mirror of
https://github.com/GoogleChromeLabs/squoosh.git
synced 2025-11-13 09:17:20 +00:00
Options UI (#135)
* Move gzipped size calculations into a worker and wrap it up in a `<GzipSize />` component that will also handle showing % of original size once that info is plumbed * A couple tweaks for the app welcome (drop files) screen. We don't have mocks for this one, but this is at least a minor improvement. * Prettier "pop" effect and styling for the drop zone/indicator. * Styling for the quantization toggle to make it look like a disclosure triangle/button. * Add controls bar (zoom in/out/to, background toggle). @todo: extract into its own component. * When clicking/tapping the image area, give it focus. * Utilities used by this PR * Add a `two-up-handle` attribute to the handle for easier styling (classname gets mangled so it doesn't make for a good public API) * Add a dummy comment to test netlify deploy * Remove commented-out code. * Fix styling of vertical split (which as it turns out is slightly different in the mocks anyway) * Use a composited overlay for the dark background instead of animating background-color * Move grayscale styling into `<two-up>` by default, then set colors via custom properties * Remove commented-out svg fill * Remove dummy comment * Change `<GzipSize>` to be `<FileSize>`, add `compress` option that lets us show gzipped sizes later if we need. Defaults to `false`, and the gzip worker is only lazily instantiated the first time a compressed size calculation is requested. * Dependency updates * Remove color animations from dnd overlay * Don't use a cyclical import for EncodedImage, instead just specify the types of the properties we Options actually uses. * Pass source image through to FileSize component so it can compute delta * Stylize size display with colors based on delta amount/direction * Remove box-shadow animation. * Simplify font stack * Remove commented out code * Remove gzip compression from size component * Remove memoization bits * Use specific flattend props instead of passing large context objects around. * Remove unused packages. * Remove unreachable String case in FileSize, and omit redundant File type * Simplify calculateSize() * Fix types for FileSize! * Remove FileSize title * Make delta variable consistent. * Skip passing compareTo value for original image * Remove manual focus * Fix whitespace * remove unused keyframes * remove pointless flex-wrap property * Remove unused resetZoom() method * Remove pointless flex properties * Use `on` prefix for event handling * Remove pointless justify-self property * Use an inline SVG for TwoUp's handle icon so it can be colored from outside the component.. * Move orientation state up from `<Output>` into `<App>` and share it with `<Options>`. * Make the options panels responsive :) * Show a plus sign for size increases `(+8%)` * Use inline SVG for the zoom +/- icons, collect SVG icons into one file now that I've verified they get tree-shaken properly. * Fix top/bottom options panels being reversed * remove commented out code * lockfile * Revert quanitzation toggle styles so it's just a checkbox. * Remove minimum delta for compare size * Rename data prop to file. * scale int -> float * remove tabIndex * Remove old icon files * Add width to options panels * Add vertical scrolling when options are taller than 80% of the screen height.
This commit is contained in:
committed by
Jake Archibald
parent
54ad30a7ed
commit
32f6f8b941
@@ -1,5 +1,4 @@
|
||||
import { h, Component } from 'preact';
|
||||
import { partial } from 'filesize';
|
||||
|
||||
import { bind, linkRef, bitmapToImageData } from '../../lib/util';
|
||||
import * as style from './style.scss';
|
||||
@@ -38,6 +37,8 @@ import {
|
||||
import { decodeImage } from '../../codecs/decoders';
|
||||
import { cleanMerge, cleanSet } from '../../lib/clean-modify';
|
||||
|
||||
type Orientation = 'horizontal' | 'vertical';
|
||||
|
||||
interface SourceImage {
|
||||
file: File;
|
||||
bmp: ImageBitmap;
|
||||
@@ -65,14 +66,13 @@ interface State {
|
||||
images: [EncodedImage, EncodedImage];
|
||||
loading: boolean;
|
||||
error?: string;
|
||||
orientation: Orientation;
|
||||
}
|
||||
|
||||
interface UpdateImageOptions {
|
||||
skipPreprocessing?: boolean;
|
||||
}
|
||||
|
||||
const filesize = partial({});
|
||||
|
||||
async function preprocessImage(
|
||||
source: SourceImage,
|
||||
preprocessData: PreprocessorState,
|
||||
@@ -115,6 +115,8 @@ async function compressImage(
|
||||
}
|
||||
|
||||
export default class App extends Component<Props, State> {
|
||||
widthQuery = window.matchMedia('(min-width: 500px)');
|
||||
|
||||
state: State = {
|
||||
loading: false,
|
||||
images: [
|
||||
@@ -133,6 +135,7 @@ export default class App extends Component<Props, State> {
|
||||
loading: false,
|
||||
},
|
||||
],
|
||||
orientation: this.widthQuery.matches ? 'horizontal' : 'vertical',
|
||||
};
|
||||
|
||||
private snackbar?: SnackBarElement;
|
||||
@@ -148,6 +151,13 @@ export default class App extends Component<Props, State> {
|
||||
window.STATE = this.state;
|
||||
};
|
||||
}
|
||||
|
||||
this.widthQuery.addListener(this.onMobileWidthChange);
|
||||
}
|
||||
|
||||
@bind
|
||||
onMobileWidthChange() {
|
||||
this.setState({ orientation: this.widthQuery.matches ? 'horizontal' : 'vertical' });
|
||||
}
|
||||
|
||||
onEncoderTypeChange(index: 0 | 1, newType: EncoderType): void {
|
||||
@@ -289,33 +299,32 @@ export default class App extends Component<Props, State> {
|
||||
this.snackbar.showSnackbar({ message: error });
|
||||
}
|
||||
|
||||
render({ }: Props, { loading, images }: State) {
|
||||
render({ }: Props, { loading, images, source, orientation }: State) {
|
||||
const [leftImageBmp, rightImageBmp] = images.map(i => i.bmp);
|
||||
const anyLoading = loading || images.some(image => image.loading);
|
||||
|
||||
return (
|
||||
<file-drop accept="image/*" onfiledrop={this.onFileDrop}>
|
||||
<div id="app" class={style.app}>
|
||||
<div id="app" class={`${style.app} ${style[orientation]}`}>
|
||||
{(leftImageBmp && rightImageBmp) ? (
|
||||
<Output leftImg={leftImageBmp} rightImg={rightImageBmp} />
|
||||
<Output
|
||||
orientation={orientation}
|
||||
leftImg={leftImageBmp}
|
||||
rightImg={rightImageBmp}
|
||||
/>
|
||||
) : (
|
||||
<div class={style.welcome}>
|
||||
<h1>Select an image</h1>
|
||||
<input type="file" onChange={this.onFileChange} />
|
||||
</div>
|
||||
)}
|
||||
{images.map((image, index) => (
|
||||
<span class={index ? style.rightLabel : style.leftLabel}>
|
||||
{encoderMap[image.encoderState.type].label}
|
||||
{(image.downloadUrl && image.file) && (
|
||||
<a href={image.downloadUrl} download={image.file.name}>🔻</a>
|
||||
)}
|
||||
{image.file && ` - ${filesize(image.file.size)}`}
|
||||
</span>
|
||||
))}
|
||||
{images.map((image, index) => (
|
||||
<div class={style.welcome}>
|
||||
<h1>Drop, paste or select an image</h1>
|
||||
<input type="file" onChange={this.onFileChange} />
|
||||
</div>
|
||||
)}
|
||||
{(leftImageBmp && rightImageBmp) && images.map((image, index) => (
|
||||
<Options
|
||||
class={index ? style.rightOptions : style.leftOptions}
|
||||
orientation={orientation}
|
||||
imageIndex={index}
|
||||
imageFile={image.file}
|
||||
sourceImageFile={source && source.file}
|
||||
downloadUrl={image.downloadUrl}
|
||||
preprocessorState={image.preprocessorState}
|
||||
encoderState={image.encoderState}
|
||||
onEncoderTypeChange={this.onEncoderTypeChange.bind(this, index)}
|
||||
|
||||
Reference in New Issue
Block a user