grid mostly there
This commit is contained in:
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
|
||||
}));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user