This commit is contained in:
2022-12-24 15:22:23 -07:00
parent 882709fcea
commit c1f33f9df5
7 changed files with 189 additions and 44 deletions

View File

@@ -4,16 +4,24 @@ body {
color: #335;
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Helvetica,Arial,sans-serif;
margin: 0;
overflow-x: hidden;
padding: 0;
}
body {
overflow-x: hidden;
}
body.no-scroll {
overflow: hidden;
}
div {
box-sizing: border-box;
}
h1 {
color: #69c;
cursor: pointer;
font-size: 45px;
font-weight: lighter;
line-height: 60px;
@@ -32,15 +40,35 @@ h2 {
margin-left: 30px;
margin-right: 30px;
margin-top: 0px;
}
.ImageSet h2 {
border-bottom: 1px solid #eef;
}
.Root-setCovers {
display: grid;
gap: 30px;
}
.SetCover {
align-items: center;
cursor: pointer;
display: flex;
flex-direction: column;
justify-content: end;
overflow: hidden;
}
.ImageSet-location,
.ImageSet-description {
.ImageSet-description,
.SetCover-location,
.SetCover-description {
white-space: nowrap;
}
.ImageSet-location:after {
.ImageSet-location:after,
.SetCover-location:after {
content: " · ";
white-space: break-spaces;
}
@@ -100,9 +128,20 @@ h2 {
color: #ccf;
}
h2 {
.ImageSet h2 {
border-color: #335;
color: #bbb;
}
}
@media (min-width: 900px) {
.Root-setCovers {
grid-template-columns: 1fr 1fr;
}
}
@media (min-width: 1400px) {
.Root-setCovers {
grid-template-columns: 1fr 1fr 1fr;
}
}

View File

@@ -14,10 +14,12 @@ export class BigPicture extends React.PureComponent<Props, {}> {
componentDidMount() {
window.addEventListener("keyup", this._onEscape as any);
document.body.classList.add("no-scroll");
}
componentWillUnmount() {
window.removeEventListener("keyup", this._onEscape as any);
document.body.classList.remove("no-scroll");
}
render() {

View File

@@ -31,14 +31,14 @@ export class Grid extends React.PureComponent<Props, {}> {
const rows: Row[] = [];
let rowWidth = 0;
this.props.images.forEach(image => {
this.props.images.forEach((image) => {
const newWidth = rowWidth + image.width / image.height;
const height = this.props.width / newWidth;
if (height < this._rowHeight()) {
rows.push({
images: row,
width: rowWidth
width: rowWidth,
});
row = [];
@@ -50,13 +50,13 @@ export class Grid extends React.PureComponent<Props, {}> {
});
rows.push({
images: row,
width: rowWidth
width: rowWidth,
});
const images = rows.map(row => {
const images = rows.map((row) => {
const height = Math.min(this.props.height, this.props.width / row.width);
const pics = row.images.map(image => {
const pics = row.images.map((image) => {
return (
<Picture
image={image}
@@ -75,7 +75,7 @@ export class Grid extends React.PureComponent<Props, {}> {
<div
className="Grid-row"
style={{ height: height + "px" }}
key={row.images.map(image => image.src).join(",")}
key={row.images.map((image) => image.src).join(",")}
>
{pics}
</div>

View File

@@ -21,8 +21,12 @@ export class ImageSet extends React.PureComponent<Props, {}> {
return (
<div className="ImageSet" ref={this.divRef}>
<h2>
<span className="ImageSet-location">{this.props.imageSet.location}</span>
<span className="ImageSet-description">{this.props.imageSet.description}</span>
<span className="ImageSet-location">
{this.props.imageSet.location}
</span>
<span className="ImageSet-description">
{this.props.imageSet.description}
</span>
</h2>
<Grid
images={this.props.imageSet.images}

View File

@@ -24,7 +24,7 @@ export class Picture extends React.PureComponent<Props, State> {
static displayName = "Picture";
state: State = {
isMounted: false
isMounted: false,
};
componentDidMount() {
@@ -52,7 +52,7 @@ export class Picture extends React.PureComponent<Props, State> {
onClick={this.props.onClick}
src={srcSet.bestSrc}
height={this.props.height + "px"}
width={this.props.width + "px"}
width={Math.floor(this.props.width) + "px"}
/>
</picture>
);
@@ -64,7 +64,7 @@ export class Picture extends React.PureComponent<Props, State> {
let bestSize = 800;
let bestScale = Infinity;
Model.SIZES.forEach(size => {
Model.SIZES.forEach((size) => {
const width =
this.props.image.width > this.props.image.height
? size
@@ -87,7 +87,7 @@ export class Picture extends React.PureComponent<Props, State> {
return {
jpeg: jpegSrcSet.join(","),
webp: webpSrcSet.join(","),
bestSrc: `img/${bestSize}/${this.props.image.src}`
bestSrc: `img/${bestSize}/${this.props.image.src}`,
};
};
}

View File

@@ -1,5 +1,6 @@
import { BigPicture } from "./big_picture";
import { ImageSet } from "./image_set";
import { SetCover } from "./set_cover";
import * as Model from "../model";
import * as React from "react";
@@ -9,6 +10,7 @@ export interface Props {}
export interface State {
data?: Model.Data | null;
selectedImage?: Model.Image | null;
selectedSet?: Model.ImageSet | null;
gridHeights: number[];
pageBottom: number;
width: number;
@@ -39,17 +41,17 @@ export class Root extends React.PureComponent<Props, State> {
gridHeights: [],
pageBottom: this._viewHeight() + window.pageYOffset,
width: this._viewWidth(),
height: this._viewHeight()
height: this._viewHeight(),
};
componentDidMount() {
window
.fetch(Model.dataUrl)
.then(data => data.json())
.then(json => this.setState({ data: json }))
.then((data) => data.json())
.then((json) => this.setState({ data: json }))
.then(this._loadHash)
.then(this._onViewChange)
.catch(e => console.error("Error fetching data", e));
.catch((e) => console.error("Error fetching data", e));
window.onresize = this._onViewChange;
window.onscroll = this._onViewChange;
@@ -65,27 +67,49 @@ export class Root extends React.PureComponent<Props, State> {
window.onpopstate = this._loadHash;
}
render() {
const imageSets = this.state.data
? this.state.data.sets.map((set, idx) => (
private _renderSet(set: Model.ImageSet) {
return (
<ImageSet
key={set.location + set.description}
imageSet={set}
pageBottom={
this.state.pageBottom - this._getPreviousGridHeights(idx)
}
setGridHeight={this._setGridHeight(idx)}
pageBottom={this.state.pageBottom}
setGridHeight={this._setGridHeight(0)}
onImageSelected={this._onImageSelected}
width={this.state.width}
height={this.state.height}
/>
))
);
}
private _renderSetCovers(sets: Model.ImageSet[]) {
return (
<div className="Root-setCovers">
{sets.map((set) => (
<SetCover
key={set.location + set.description}
imageSet={set}
onClick={() => {
this._onSetSelected(set);
scrollTo(0, 0);
}}
width={Math.min(this.state.width, 400)}
/>
))}
</div>
);
}
render() {
const imageSets = this.state.data
? this.state.selectedSet
? this._renderSet(this.state.selectedSet)
: this._renderSetCovers(this.state.data.sets)
: null;
return (
<div className="Root">
{this._bigPicture()}
<h1>Aaron's Ski Pictures</h1>
<h1 onClick={this._onHomeSelected}>Aaron's Ski Pictures</h1>
{imageSets}
</div>
);
@@ -102,19 +126,26 @@ export class Root extends React.PureComponent<Props, State> {
private _loadHash = () => {
if (window.location.hash.length > 0 && this.state.data) {
const src = window.location.hash.slice(1);
let selectedImage: Model.Image | null = null;
const hash = window.location.hash.slice(1);
this.state.data.sets.forEach(set => {
const image = set.images.find(image => image.src === src);
let selectedImage: Model.Image | null = null;
let selectedSet: Model.ImageSet | null = null;
this.state.data.sets.forEach((set) => {
if (this._setToHash(set) === hash) {
selectedSet = set;
}
const image = set.images.find((image) => image.src === hash);
if (image) {
selectedImage = image;
selectedSet = set;
}
});
this.setState({ selectedImage: selectedImage });
this.setState({ selectedImage, selectedSet });
} else {
this.setState({ selectedImage: null });
this.setState({ selectedImage: null, selectedSet: null });
}
};
@@ -131,16 +162,37 @@ export class Root extends React.PureComponent<Props, State> {
window.history.pushState(null, "", `#${img.src}`);
};
private _onSetSelected = (set: Model.ImageSet) => {
this.setState({ selectedSet: set });
document.title =
set.location + " " + set.description + " Skiing - Aaron Gutierrez";
window.history.pushState(null, "", `#${this._setToHash(set)}`);
};
private _onHomeSelected = () => {
this.setState({
selectedSet: null,
selectedImage: null,
});
window.history.pushState(null, "", "#");
document.title = "Skiing - Aaron Gutierrez";
};
private _setToHash = (set: Model.ImageSet) =>
set.location.replace(/[^a-zA-Z0-9-_]/g, "-") +
"-" +
set.description.replace(/[^a-zA-Z0-9-_]/g, "-");
private _showGrid = () => {
this.setState({ selectedImage: null });
window.history.pushState(null, "", "#");
this._onSetSelected(this.state.selectedSet as Model.ImageSet);
};
private _setGridHeight = (grid: number) => (height: number) => {
if (this.state.gridHeights[grid] === height) {
return;
}
this.setState(state => {
this.setState((state) => {
const newGridHeights = [...state.gridHeights];
newGridHeights[grid] = height;

View File

@@ -0,0 +1,48 @@
import { Picture } from "./picture";
import * as Model from "../model";
import * as React from "react";
export interface Props {
imageSet: Model.ImageSet;
onClick: () => void;
width: number;
}
export interface State {}
export class SetCover extends React.PureComponent<Props, State> {
static displayName = "SetCover";
render() {
const image = this.props.imageSet.images[0];
const isTall = image.height > image.width;
const height = isTall
? this.props.width
: (image.height / image.width) * this.props.width;
const width = isTall
? (image.width / image.height) * this.props.width
: this.props.width;
return (
<div className="SetCover" onClick={this.props.onClick}>
<Picture
image={image}
onClick={() => {}}
height={height}
width={width}
/>
<h2>
<span className="SetCover-location">
{this.props.imageSet.location}
</span>
<span className="SetCover-description">
{this.props.imageSet.description}
</span>
</h2>
</div>
);
}
}