diff --git a/src/components/big_picture.tsx b/src/components/big_picture.tsx index d41236a..c31185d 100644 --- a/src/components/big_picture.tsx +++ b/src/components/big_picture.tsx @@ -3,7 +3,6 @@ import * as Model from "../model"; import * as React from "react"; export interface Props { - src: string; image: Model.Image; onClose: () => void; width: number; @@ -13,7 +12,7 @@ export class BigPicture extends React.PureComponent { static displayName = "BigPicture"; render() { - const src = `img/1600/${this.props.src}`; + const src = `img/1600/${this.props.image.src}`; return
{
Download + onKeyPress={this._keyPress} + tabIndex={0} > Close
; } + + private _keyPress = (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + this.props.onClose(); + } + } } diff --git a/src/components/grid.tsx b/src/components/grid.tsx index ff04223..f0d7da2 100644 --- a/src/components/grid.tsx +++ b/src/components/grid.tsx @@ -4,59 +4,66 @@ import * as Model from "../model"; import * as React from "react"; export interface Props { - images: Model.Images; - onImageSelected: (key: string) => void; + images: Model.Image[]; + onImageSelected: (image: Model.Image) => void; width: number; } export const ROW_HEIGHT = 200; export const MOBILE_ROW_HEIGHT = 100; +interface Row { + images: Model.Image[]; + width: number; +} + export class Grid extends React.PureComponent { static displayName = "Grid"; render() { - const keys = Object.keys(this.props.images); - - let row: string[] = []; - const rows: string[][] = []; - const rowWidths: number[] = []; + let row: Model.Image[] = []; + const rows: Row[] = []; let rowWidth = 0; - keys.forEach(key => { - const image = this.props.images[key]; - + this.props.images.forEach(image => { const newWidth = rowWidth + (image.width/image.height); const height = this.props.width / newWidth; if (height < this._rowHeight()) { - rows.push(row); - rowWidths.push(rowWidth); + rows.push({ + images: row, + width: rowWidth + }); row = []; rowWidth = (image.width/image.height); } else { rowWidth = newWidth; } - row.push(key); + row.push(image); + }); + rows.push({ + images: row, + width: rowWidth }); - rows.push(row); - rowWidths.push(rowWidth); - const images = rows.map((row, idx) => { - const scale = this.props.width / rowWidths[idx]; + const images = rows.map(row => { + const height = this.props.width / row.width; - const pics = row.map(key => { - const image = this.props.images[key]; + const pics = row.images.map(image => { return this.props.onImageSelected(key)} - src={key} - key={key} - width={image.width/image.height * scale} + onClick={() => this.props.onImageSelected(image)} + key={image.src} + width={image.width/image.height * height} /> }); - return
{pics}
; + return
image.src).join(",")}> + {pics} +
; }); return
{images}
; diff --git a/src/components/image_set.tsx b/src/components/image_set.tsx new file mode 100644 index 0000000..a6d8727 --- /dev/null +++ b/src/components/image_set.tsx @@ -0,0 +1,24 @@ +import { Grid } from "./grid"; +import * as Model from "../model"; + +import * as React from "react"; + +export interface Props { + imageSet: Model.ImageSet; + onImageSelected: (img: Model.Image) => void; + width: number; +} + +export class ImageSet extends React.PureComponent { + static displayName = "ImageSet"; + + render() { + return
+

{ this.props.imageSet.location } ยท { this.props.imageSet.description }

+ +
; + } +} diff --git a/src/components/picture.tsx b/src/components/picture.tsx index 03c965e..a9fed35 100644 --- a/src/components/picture.tsx +++ b/src/components/picture.tsx @@ -3,7 +3,6 @@ import * as Model from "../model"; import * as React from "react"; export interface Props { - src: string; image: Model.Image; onClick: () => void; width: number; @@ -13,7 +12,7 @@ export class Picture extends React.PureComponent { static displayName = "Picture"; render() { - const src = `img/600/${this.props.src}`; + const src = `img/600/${this.props.image.src}`; return { const scale = width / this.props.width; if (scale >= 1) { - srcs.push(`img/${size}/${this.props.src} ${scale}x`); + srcs.push(`img/${size}/${this.props.image.src} ${scale}x`); } }); diff --git a/src/components/root.tsx b/src/components/root.tsx index a9eebdd..98dd67e 100644 --- a/src/components/root.tsx +++ b/src/components/root.tsx @@ -1,5 +1,5 @@ import { BigPicture } from "./big_picture"; -import { Grid } from "./grid"; +import { ImageSet } from "./image_set"; import * as Model from "../model"; import * as React from "react"; @@ -7,8 +7,8 @@ import * as React from "react"; export interface Props {} export interface State { - images: Model.Images; - selectedImage?: string | null; + data?: Model.Data | null; + selectedImage?: Model.Image | null; width: number; } @@ -16,14 +16,14 @@ export class Root extends React.PureComponent { static displayName = "Root"; state: State = { - images: {}, width: window.innerWidth } componentDidMount() { window.fetch(Model.URL) .then(data => data.json()) - .then(json => this.setState({ images: json })) + .then(json => this.setState({ data: json })) + .then(this._loadHash) .catch(e => console.error("Error fetching data", e)); window.onresize = () => { @@ -32,45 +32,55 @@ export class Root extends React.PureComponent { window.onpopstate = this._loadHash; - this._loadHash(); } render() { + const imageSets = this.state.data + ? this.state.data.sets.map(set => + + ) + : null; + return
- { this._bigPicture() } -

Ski

-

CMH Galena

- -
; + { this._bigPicture() } +

Ski

+ { imageSets } + ; } - private _bigPicture = () => { - if (this.state.selectedImage && this.state.images[this.state.selectedImage]) { - return - } else { - return null; - } - } + private _bigPicture = () => + this.state.selectedImage + ? + : null private _loadHash = () => { - if (window.location.hash.length > 0) { - this.setState({ selectedImage: window.location.hash.slice(1) }); + if (window.location.hash.length > 0 && this.state.data) { + const src = window.location.hash.slice(1); + let selectedImage: Model.Image | null = null; + + this.state.data.sets.forEach(set => { + const image = set.images.find(image => image.src === src); + if (image) { + selectedImage = image; + } + }); + + this.setState({ selectedImage: selectedImage }); } else { this.setState({ selectedImage: null }); } } - private _onImageSelected = (key: string) => { - this.setState({ selectedImage: key }); - window.history.pushState(null, "", `#${key}`); + private _onImageSelected = (img: Model.Image) => { + this.setState({ selectedImage: img }); + window.history.pushState(null, "", `#${img.src}`); } private _showGrid = () => { diff --git a/src/model.ts b/src/model.ts index c175e01..92f61af 100644 --- a/src/model.ts +++ b/src/model.ts @@ -1,11 +1,18 @@ export const SIZES = [1600, 1200, 800, 600, 400, 200]; export const URL = "img/data.json"; -export interface Images { - [name: string]: Image +export interface Data { + sets: ImageSet[] +} + +export interface ImageSet { + location: string; + description: string; + images: Image[] } export interface Image { + src: string; height: number; width: number; }