grid mostly there
This commit is contained in:
1
dist/index.html
vendored
Symbolic link
1
dist/index.html
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../index.html
|
||||||
1
dist/site.css
vendored
Symbolic link
1
dist/site.css
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../site.css
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<title>Skiing - Aaron Gutierrez</title>
|
<title>Skiing - Aaron Gutierrez</title>
|
||||||
<link rel="stylesheet" href="site.css">
|
<link rel="stylesheet" href="site.css">
|
||||||
<meta charshet="utf-8">
|
<meta charshet="utf-8">
|
||||||
<meta viewport="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="mount">
|
<div id="mount">
|
||||||
@@ -13,6 +13,6 @@
|
|||||||
<script src="./node_modules/react/umd/react.development.js"></script>
|
<script src="./node_modules/react/umd/react.development.js"></script>
|
||||||
<script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
|
<script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
|
||||||
|
|
||||||
<script type="text/javascript" src="./dist/bundle.js"></script>
|
<script type="text/javascript" src="bundle.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
1191
package-lock.json
generated
1191
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -23,6 +23,7 @@
|
|||||||
"source-map-loader": "^0.2.4",
|
"source-map-loader": "^0.2.4",
|
||||||
"typescript": "^3.2.2",
|
"typescript": "^3.2.2",
|
||||||
"webpack": "^4.28.2",
|
"webpack": "^4.28.2",
|
||||||
"webpack-cli": "^3.1.2"
|
"webpack-cli": "^3.1.2",
|
||||||
|
"webpack-dev-server": "^3.1.14"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
32
site.css
32
site.css
@@ -0,0 +1,32 @@
|
|||||||
|
html,
|
||||||
|
body {
|
||||||
|
background-color: #fff;
|
||||||
|
color: #335;
|
||||||
|
font-family: sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: 1px solid #eef;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #69c;
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Grid-row {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Grid img {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|||||||
69
src/components/grid.tsx
Normal file
69
src/components/grid.tsx
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import { Picture } from "./picture";
|
||||||
|
import * as Model from "../model";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
images: Model.Images;
|
||||||
|
onImageSelected: (key: string) => void;
|
||||||
|
selectedImage: string | null;
|
||||||
|
width: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ROW_HEIGHT = 200;
|
||||||
|
export const MOBILE_ROW_HEIGHT = 100;
|
||||||
|
|
||||||
|
export class Grid extends React.PureComponent<Props, {}> {
|
||||||
|
static displayName = "Grid";
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const keys = Object.keys(this.props.images);
|
||||||
|
|
||||||
|
let row: string[] = [];
|
||||||
|
const rows: string[][] = [];
|
||||||
|
const rowWidths: number[] = [];
|
||||||
|
let rowWidth = 0;
|
||||||
|
|
||||||
|
keys.forEach(key => {
|
||||||
|
const image = this.props.images[key];
|
||||||
|
|
||||||
|
const newWidth = rowWidth + (image.width/image.height);
|
||||||
|
const height = this.props.width / newWidth;
|
||||||
|
|
||||||
|
if (height < this._rowHeight()) {
|
||||||
|
rows.push(row);
|
||||||
|
rowWidths.push(rowWidth);
|
||||||
|
|
||||||
|
row = [];
|
||||||
|
rowWidth = (image.width/image.height);
|
||||||
|
} else {
|
||||||
|
rowWidth = newWidth;
|
||||||
|
}
|
||||||
|
row.push(key);
|
||||||
|
});
|
||||||
|
rows.push(row);
|
||||||
|
rowWidths.push(rowWidth);
|
||||||
|
|
||||||
|
const images = rows.map((row, idx) => {
|
||||||
|
const scale = this.props.width / rowWidths[idx];
|
||||||
|
|
||||||
|
const pics = row.map(key => {
|
||||||
|
const image = this.props.images[key];
|
||||||
|
return <Picture
|
||||||
|
image={image}
|
||||||
|
onClick={() => this.props.onImageSelected(key)}
|
||||||
|
selected={this.props.selectedImage === key}
|
||||||
|
src={key}
|
||||||
|
key={key}
|
||||||
|
width={image.width/image.height * scale}
|
||||||
|
/>
|
||||||
|
});
|
||||||
|
return <div className="Grid-row" style={{height: scale + "px"}} key={row.join(",")}>{pics}</div>;
|
||||||
|
});
|
||||||
|
|
||||||
|
return <div className="Grid">{images}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _rowHeight = (): number =>
|
||||||
|
this.props.width > 500 ? ROW_HEIGHT : MOBILE_ROW_HEIGHT;
|
||||||
|
}
|
||||||
44
src/components/picture.tsx
Normal file
44
src/components/picture.tsx
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import * as Model from "../model";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
src: string;
|
||||||
|
image: Model.Image;
|
||||||
|
onClick: () => void;
|
||||||
|
selected?: boolean;
|
||||||
|
width: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Picture extends React.PureComponent<Props, {}> {
|
||||||
|
static displayName = "Picture";
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const src = `img/600/${this.props.src}`;
|
||||||
|
return <img
|
||||||
|
className={ this.props.selected ? "Picture-selected" : ""}
|
||||||
|
onClick={this.props.onClick}
|
||||||
|
srcSet={this._srcset()}
|
||||||
|
src={src}
|
||||||
|
width={ this.props.selected ? "100%" : this.props.width + "px" }
|
||||||
|
/>;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _srcset = (): string => {
|
||||||
|
const srcs: string[] = [];
|
||||||
|
|
||||||
|
Model.SIZES.forEach(size => {
|
||||||
|
const width = this.props.image.width > this.props.image.height
|
||||||
|
? size
|
||||||
|
: this.props.image.width / this.props.image.height * size;
|
||||||
|
|
||||||
|
const scale = width / this.props.width;
|
||||||
|
|
||||||
|
if (scale >= 1) {
|
||||||
|
srcs.push(`img/${size}/${this.props.src} ${scale}x`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return srcs.join(",");
|
||||||
|
}
|
||||||
|
}
|
||||||
52
src/components/root.tsx
Normal file
52
src/components/root.tsx
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { Grid } from "./grid";
|
||||||
|
import * as Model from "../model";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
export interface Props {}
|
||||||
|
|
||||||
|
export interface State {
|
||||||
|
images: Model.Images;
|
||||||
|
selectedIamge?: string | null;
|
||||||
|
width: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Root extends React.PureComponent<Props, State> {
|
||||||
|
static displayName = "Root";
|
||||||
|
|
||||||
|
state = {
|
||||||
|
images: {},
|
||||||
|
selectedImage: null,
|
||||||
|
width: window.innerWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
window.fetch(Model.URL)
|
||||||
|
.then(data => data.json())
|
||||||
|
.then(json => this.setState({ images: json }))
|
||||||
|
.catch(e => console.error("Error fetching data", e));
|
||||||
|
|
||||||
|
window.onresize = () => {
|
||||||
|
this.setState({ width: window.innerWidth });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <div className="Root">
|
||||||
|
<h1>Ski</h1>
|
||||||
|
<hr />
|
||||||
|
<Grid
|
||||||
|
images={ this.state.images }
|
||||||
|
onImageSelected={ this._onImageSelected }
|
||||||
|
selectedImage={ this.state.selectedImage }
|
||||||
|
width={ this.state.width } />
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onImageSelected = (key: string) => {
|
||||||
|
this.setState(state => ({
|
||||||
|
...state,
|
||||||
|
selectedImage: key
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
import { Root } from "./root";
|
import { Root } from "./components/root";
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as ReactDOM from "react-dom";
|
import * as ReactDOM from "react-dom";
|
||||||
|
|
||||||
window.onload = () => {
|
const body = document.getElementById("mount");
|
||||||
const body = document.getElementById("mount");
|
|
||||||
|
|
||||||
const root = <Root name="Friend" />;
|
ReactDOM.render(<Root />, body);
|
||||||
ReactDOM.render(root, body);
|
|
||||||
};
|
|
||||||
|
|||||||
11
src/model.ts
Normal file
11
src/model.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export const SIZES = [1600, 1200, 800, 600, 400, 200];
|
||||||
|
export const URL = "img/data.json";
|
||||||
|
|
||||||
|
export interface Images {
|
||||||
|
[name: string]: Image
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Image {
|
||||||
|
height: number;
|
||||||
|
width: number;
|
||||||
|
}
|
||||||
15
src/root.tsx
15
src/root.tsx
@@ -1,15 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
|
|
||||||
export interface Props {
|
|
||||||
name: String;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface State {}
|
|
||||||
|
|
||||||
export class Root extends React.PureComponent<Props, State> {
|
|
||||||
static displayName = "Root";
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return <h1>Hello, {this.props.name}!</h1>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./dist/",
|
"outDir": "./dist/",
|
||||||
|
"baseUrl": ".",
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
"target": "es6",
|
"target": "es6",
|
||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
var path = require("path");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: "./src/index.tsx",
|
entry: "./src/index.tsx",
|
||||||
output: {
|
output: {
|
||||||
@@ -5,9 +7,15 @@ module.exports = {
|
|||||||
path: __dirname + "/dist"
|
path: __dirname + "/dist"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
mode: "development",
|
||||||
|
|
||||||
// Enable sourcemaps for debugging webpack's output.
|
// Enable sourcemaps for debugging webpack's output.
|
||||||
devtool: "source-map",
|
devtool: "source-map",
|
||||||
|
|
||||||
|
devServer: {
|
||||||
|
port: 8080
|
||||||
|
},
|
||||||
|
|
||||||
resolve: {
|
resolve: {
|
||||||
// Add '.ts' and '.tsx' as resolvable extensions.
|
// Add '.ts' and '.tsx' as resolvable extensions.
|
||||||
extensions: [".ts", ".tsx", ".js", ".json"]
|
extensions: [".ts", ".tsx", ".js", ".json"]
|
||||||
|
|||||||
Reference in New Issue
Block a user