mirror of
https://github.com/GoogleChromeLabs/squoosh.git
synced 2025-11-15 01:59:57 +00:00
Remove decorators and bundler workarounds
This commit is contained in:
6
package-lock.json
generated
6
package-lock.json
generated
@@ -17762,9 +17762,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"pointer-tracker": {
|
"pointer-tracker": {
|
||||||
"version": "2.0.3",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/pointer-tracker/-/pointer-tracker-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/pointer-tracker/-/pointer-tracker-2.4.0.tgz",
|
||||||
"integrity": "sha512-PURBF4oc45JPECuguX6oPL3pJU5AlF0Nb/4sZdmqzPNAkV4LGL9MJMqb0smWDtmQ0F0KpbxEJn4/Lf5ugN1keQ==",
|
"integrity": "sha512-pWI2tpaM/XNtc9mUTv42Rmjf6mkHvE8LT5DDEq0G7baPNhxNM9E3CepubPplSoSLk9E5bwQrAMyDcPVmJyTW4g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"portfinder": {
|
"portfinder": {
|
||||||
|
|||||||
@@ -25,10 +25,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"//": "See https://github.com/GoogleChromeLabs/pointer-tracker/pull/10",
|
|
||||||
"alias": {
|
|
||||||
"pointer-tracker": "./node_modules/pointer-tracker/dist/PointerTracker.mjs"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/plugin-proposal-class-properties": "^7.10.4",
|
"@babel/plugin-proposal-class-properties": "^7.10.4",
|
||||||
"@babel/plugin-proposal-decorators": "^7.10.5",
|
"@babel/plugin-proposal-decorators": "^7.10.5",
|
||||||
@@ -68,7 +64,7 @@
|
|||||||
"normalize-path": "^3.0.0",
|
"normalize-path": "^3.0.0",
|
||||||
"optimize-css-assets-webpack-plugin": "5.0.1",
|
"optimize-css-assets-webpack-plugin": "5.0.1",
|
||||||
"parcel": "^2.0.0-beta.1",
|
"parcel": "^2.0.0-beta.1",
|
||||||
"pointer-tracker": "2.0.3",
|
"pointer-tracker": "^2.4.0",
|
||||||
"postcss-modules": "^3.2.0",
|
"postcss-modules": "^3.2.0",
|
||||||
"preact": "8.4.2",
|
"preact": "8.4.2",
|
||||||
"prerender-loader": "1.3.0",
|
"prerender-loader": "1.3.0",
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { h, Component } from 'preact';
|
import { h, Component } from 'preact';
|
||||||
import { bind } from '../../lib/initial-util';
|
|
||||||
import * as style from '../../components/Options/style.module.scss';
|
import * as style from '../../components/Options/style.module.scss';
|
||||||
import Range from '../../components/range';
|
import Range from '../../components/range';
|
||||||
|
|
||||||
@@ -26,8 +25,7 @@ export default function qualityOption(opts: QualityOptionArg = {}) {
|
|||||||
} = opts;
|
} = opts;
|
||||||
|
|
||||||
class QualityOptions extends Component<Props, {}> {
|
class QualityOptions extends Component<Props, {}> {
|
||||||
@bind
|
onChange = (event: Event) => {
|
||||||
onChange(event: Event) {
|
|
||||||
const el = event.currentTarget as HTMLInputElement;
|
const el = event.currentTarget as HTMLInputElement;
|
||||||
this.props.onChange({ quality: Number(el.value) });
|
this.props.onChange({ quality: Number(el.value) });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { h, Component } from 'preact';
|
import { h, Component } from 'preact';
|
||||||
import { bind } from '../../lib/initial-util';
|
|
||||||
import { inputFieldValueAsNumber, konami, preventDefault } from '../../lib/util';
|
import { inputFieldValueAsNumber, konami, preventDefault } from '../../lib/util';
|
||||||
import { QuantizeOptions } from './processor-meta';
|
import { QuantizeOptions } from './processor-meta';
|
||||||
import * as style from '../../components/Options/style.module.scss';
|
import * as style from '../../components/Options/style.module.scss';
|
||||||
@@ -27,8 +26,7 @@ export default class QuantizerOptions extends Component<Props, State> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
onChange = (event: Event) => {
|
||||||
onChange(event: Event) {
|
|
||||||
const form = (event.currentTarget as HTMLInputElement).closest('form') as HTMLFormElement;
|
const form = (event.currentTarget as HTMLInputElement).closest('form') as HTMLFormElement;
|
||||||
const { options } = this.props;
|
const { options } = this.props;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { h, Component } from 'preact';
|
import { h, Component } from 'preact';
|
||||||
import { bind } from '../../lib/initial-util';
|
|
||||||
import { inputFieldChecked, inputFieldValueAsNumber, preventDefault } from '../../lib/util';
|
import { inputFieldChecked, inputFieldValueAsNumber, preventDefault } from '../../lib/util';
|
||||||
import { EncodeOptions, MozJpegColorSpace } from './encoder-meta';
|
import { EncodeOptions, MozJpegColorSpace } from './encoder-meta';
|
||||||
import * as style from '../../components/Options/style.module.scss';
|
import * as style from '../../components/Options/style.module.scss';
|
||||||
@@ -23,8 +22,7 @@ export default class MozJPEGEncoderOptions extends Component<Props, State> {
|
|||||||
showAdvanced: false,
|
showAdvanced: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@bind
|
onChange = (event: Event) => {
|
||||||
onChange(event: Event) {
|
|
||||||
const form = (event.currentTarget as HTMLInputElement).closest('form') as HTMLFormElement;
|
const form = (event.currentTarget as HTMLInputElement).closest('form') as HTMLFormElement;
|
||||||
const { options } = this.props;
|
const { options } = this.props;
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default class OxiPNGEncoderOptions extends Component<Props, {}> {
|
export default class OxiPNGEncoderOptions extends Component<Props, {}> {
|
||||||
@bind
|
onChange = (event: Event) => {
|
||||||
onChange(event: Event) {
|
|
||||||
const form = (event.currentTarget as HTMLInputElement).closest('form') as HTMLFormElement;
|
const form = (event.currentTarget as HTMLInputElement).closest('form') as HTMLFormElement;
|
||||||
|
|
||||||
const options: EncodeOptions = {
|
const options: EncodeOptions = {
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import * as browserGIF from './browser-gif/encoder';
|
|||||||
import * as browserTIFF from './browser-tiff/encoder';
|
import * as browserTIFF from './browser-tiff/encoder';
|
||||||
import * as browserJP2 from './browser-jp2/encoder';
|
import * as browserJP2 from './browser-jp2/encoder';
|
||||||
import * as browserPDF from './browser-pdf/encoder';
|
import * as browserPDF from './browser-pdf/encoder';
|
||||||
import { bind } from '../lib/initial-util';
|
|
||||||
|
|
||||||
type ProcessorWorkerApi = import('./processor-worker').ProcessorWorkerApi;
|
type ProcessorWorkerApi = import('./processor-worker').ProcessorWorkerApi;
|
||||||
|
|
||||||
@@ -55,7 +54,7 @@ export default class Processor {
|
|||||||
private _workerTimeoutId: number = 0;
|
private _workerTimeoutId: number = 0;
|
||||||
|
|
||||||
/** @private */
|
/** @private */
|
||||||
async runProcessingJob(options: ProcessingJobOptions, processingFunc: any, ...args: any[]) {
|
async _runProcessingJob(options: ProcessingJobOptions, job: Function) {
|
||||||
const { needsWorker = false } = options;
|
const { needsWorker = false } = options;
|
||||||
|
|
||||||
this._latestJobId += 1;
|
this._latestJobId += 1;
|
||||||
@@ -69,10 +68,12 @@ export default class Processor {
|
|||||||
// @ts-ignore - Typescript doesn't know about the 2nd param to new Worker, and the
|
// @ts-ignore - Typescript doesn't know about the 2nd param to new Worker, and the
|
||||||
// definition can't be overwritten.
|
// definition can't be overwritten.
|
||||||
this._worker = new Worker(
|
this._worker = new Worker(
|
||||||
'./processor-worker',
|
// './processor-worker',
|
||||||
{ name: 'processor-worker' },
|
new URL('./processor-worker/index.ts', import.meta.url),
|
||||||
|
{ name: 'processor-worker', type: 'module' },
|
||||||
// { name: 'processor-worker', type: 'module' },
|
// { name: 'processor-worker', type: 'module' },
|
||||||
) as Worker;
|
) as Worker;
|
||||||
|
console.log(this._worker);
|
||||||
// Need to do some TypeScript trickery to make the type match.
|
// Need to do some TypeScript trickery to make the type match.
|
||||||
this._workerApi = proxy(this._worker) as any as ProcessorWorkerApi;
|
this._workerApi = proxy(this._worker) as any as ProcessorWorkerApi;
|
||||||
}
|
}
|
||||||
@@ -80,7 +81,7 @@ export default class Processor {
|
|||||||
this._busy = true;
|
this._busy = true;
|
||||||
|
|
||||||
const returnVal = Promise.race([
|
const returnVal = Promise.race([
|
||||||
processingFunc.call(this, ...args),
|
job(),
|
||||||
new Promise((_, reject) => { this._abortRejector = reject; }),
|
new Promise((_, reject) => { this._abortRejector = reject; }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -99,7 +100,7 @@ export default class Processor {
|
|||||||
if (!this._worker) return;
|
if (!this._worker) return;
|
||||||
|
|
||||||
// If the worker is unused for 10 seconds, remove it to save memory.
|
// If the worker is unused for 10 seconds, remove it to save memory.
|
||||||
this._workerTimeoutId = self.setTimeout(this.terminateWorker, workerTimeout);
|
this._workerTimeoutId = self.setTimeout(this.terminateWorker.bind(this), workerTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Abort the current job, if any */
|
/** Abort the current job, if any */
|
||||||
@@ -112,7 +113,6 @@ export default class Processor {
|
|||||||
this.terminateWorker();
|
this.terminateWorker();
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
|
||||||
terminateWorker() {
|
terminateWorker() {
|
||||||
if (!this._worker) return;
|
if (!this._worker) return;
|
||||||
this._worker.terminate();
|
this._worker.terminate();
|
||||||
@@ -120,93 +120,108 @@ export default class Processor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Off main thread jobs:
|
// Off main thread jobs:
|
||||||
@processingJob({ needsWorker: true })
|
|
||||||
imageQuant(data: ImageData, opts: QuantizeOptions): Promise<ImageData> {
|
imageQuant(data: ImageData, opts: QuantizeOptions): Promise<ImageData> {
|
||||||
return this._workerApi!.quantize(data, opts);
|
return this._runProcessingJob({ needsWorker: true }, () => {
|
||||||
|
return this._workerApi!.quantize(data, opts);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@processingJob({ needsWorker: true })
|
|
||||||
rotate(
|
rotate(
|
||||||
data: ImageData, opts: import('./rotate/processor-meta').RotateOptions,
|
data: ImageData, opts: import('./rotate/processor-meta').RotateOptions,
|
||||||
): Promise<ImageData> {
|
): Promise<ImageData> {
|
||||||
return this._workerApi!.rotate(data, opts);
|
return this._runProcessingJob({ needsWorker: true }, () => {
|
||||||
|
return this._workerApi!.rotate(data, opts);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@processingJob({ needsWorker: true })
|
|
||||||
workerResize(
|
workerResize(
|
||||||
data: ImageData, opts: import('./resize/processor-meta').WorkerResizeOptions,
|
data: ImageData, opts: import('./resize/processor-meta').WorkerResizeOptions,
|
||||||
): Promise<ImageData> {
|
): Promise<ImageData> {
|
||||||
return this._workerApi!.resize(data, opts);
|
return this._runProcessingJob({ needsWorker: true }, () => {
|
||||||
|
return this._workerApi!.resize(data, opts);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@processingJob({ needsWorker: true })
|
|
||||||
mozjpegEncode(
|
mozjpegEncode(
|
||||||
data: ImageData, opts: MozJPEGEncoderOptions,
|
data: ImageData, opts: MozJPEGEncoderOptions,
|
||||||
): Promise<ArrayBuffer> {
|
): Promise<ArrayBuffer> {
|
||||||
return this._workerApi!.mozjpegEncode(data, opts);
|
return this._runProcessingJob({ needsWorker: true }, () => {
|
||||||
|
return this._workerApi!.mozjpegEncode(data, opts);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@processingJob({ needsWorker: true })
|
|
||||||
async oxiPngEncode(
|
async oxiPngEncode(
|
||||||
data: ImageData, opts: OxiPNGEncoderOptions,
|
data: ImageData, opts: OxiPNGEncoderOptions,
|
||||||
): Promise<ArrayBuffer> {
|
): Promise<ArrayBuffer> {
|
||||||
// OxiPNG expects PNG input.
|
return this._runProcessingJob({ needsWorker: true }, async () => {
|
||||||
const pngBlob = await canvasEncode(data, 'image/png');
|
// OxiPNG expects PNG input.
|
||||||
const pngBuffer = await blobToArrayBuffer(pngBlob);
|
const pngBlob = await canvasEncode(data, 'image/png');
|
||||||
return this._workerApi!.oxiPngEncode(pngBuffer, opts);
|
const pngBuffer = await blobToArrayBuffer(pngBlob);
|
||||||
|
return this._workerApi!.oxiPngEncode(pngBuffer, opts);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@processingJob({ needsWorker: true })
|
|
||||||
webpEncode(data: ImageData, opts: WebPEncoderOptions): Promise<ArrayBuffer> {
|
webpEncode(data: ImageData, opts: WebPEncoderOptions): Promise<ArrayBuffer> {
|
||||||
return this._workerApi!.webpEncode(data, opts);
|
return this._runProcessingJob({ needsWorker: true }, () => {
|
||||||
|
return this._workerApi!.webpEncode(data, opts);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@processingJob({ needsWorker: true })
|
|
||||||
async webpDecode(blob: Blob): Promise<ImageData> {
|
async webpDecode(blob: Blob): Promise<ImageData> {
|
||||||
const data = await blobToArrayBuffer(blob);
|
return this._runProcessingJob({ needsWorker: true }, async () => {
|
||||||
return this._workerApi!.webpDecode(data);
|
const data = await blobToArrayBuffer(blob);
|
||||||
|
return this._workerApi!.webpDecode(data);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not-worker jobs:
|
// Not-worker jobs:
|
||||||
|
|
||||||
@processingJob()
|
|
||||||
browserBmpEncode(data: ImageData): Promise<Blob> {
|
browserBmpEncode(data: ImageData): Promise<Blob> {
|
||||||
return browserBMP.encode(data);
|
return this._runProcessingJob({}, () => {
|
||||||
|
return browserBMP.encode(data);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@processingJob()
|
|
||||||
browserPngEncode(data: ImageData): Promise<Blob> {
|
browserPngEncode(data: ImageData): Promise<Blob> {
|
||||||
return browserPNG.encode(data);
|
return this._runProcessingJob({}, () => {
|
||||||
|
return browserPNG.encode(data);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@processingJob()
|
|
||||||
browserJpegEncode(data: ImageData, opts: BrowserJPEGOptions): Promise<Blob> {
|
browserJpegEncode(data: ImageData, opts: BrowserJPEGOptions): Promise<Blob> {
|
||||||
return browserJPEG.encode(data, opts);
|
return this._runProcessingJob({}, () => {
|
||||||
|
return browserJPEG.encode(data, opts);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@processingJob()
|
|
||||||
browserWebpEncode(data: ImageData, opts: BrowserWebpEncodeOptions): Promise<Blob> {
|
browserWebpEncode(data: ImageData, opts: BrowserWebpEncodeOptions): Promise<Blob> {
|
||||||
return browserWebP.encode(data, opts);
|
return this._runProcessingJob({}, () => {
|
||||||
|
return browserWebP.encode(data, opts);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@processingJob()
|
|
||||||
browserGifEncode(data: ImageData): Promise<Blob> {
|
browserGifEncode(data: ImageData): Promise<Blob> {
|
||||||
return browserGIF.encode(data);
|
return this._runProcessingJob({}, () => {
|
||||||
|
return browserGIF.encode(data);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@processingJob()
|
|
||||||
browserTiffEncode(data: ImageData): Promise<Blob> {
|
browserTiffEncode(data: ImageData): Promise<Blob> {
|
||||||
return browserTIFF.encode(data);
|
return this._runProcessingJob({}, () => {
|
||||||
|
return browserTIFF.encode(data);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@processingJob()
|
|
||||||
browserJp2Encode(data: ImageData): Promise<Blob> {
|
browserJp2Encode(data: ImageData): Promise<Blob> {
|
||||||
return browserJP2.encode(data);
|
return this._runProcessingJob({}, () => {
|
||||||
|
return browserJP2.encode(data);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@processingJob()
|
|
||||||
browserPdfEncode(data: ImageData): Promise<Blob> {
|
browserPdfEncode(data: ImageData): Promise<Blob> {
|
||||||
return browserPDF.encode(data);
|
return this._runProcessingJob({}, () => {
|
||||||
|
return browserPDF.encode(data);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Synchronous jobs
|
// Synchronous jobs
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { h, Component } from 'preact';
|
import { h, Component } from 'preact';
|
||||||
import linkState from 'linkstate';
|
import linkState from 'linkstate';
|
||||||
import { bind, linkRef } from '../../lib/initial-util';
|
import { linkRef } from '../../lib/initial-util';
|
||||||
import {
|
import {
|
||||||
inputFieldValueAsNumber, inputFieldValue, preventDefault, inputFieldChecked,
|
inputFieldValueAsNumber, inputFieldValue, preventDefault, inputFieldChecked,
|
||||||
} from '../../lib/util';
|
} from '../../lib/util';
|
||||||
@@ -58,8 +58,7 @@ export default class ResizerOptions extends Component<Props, State> {
|
|||||||
this.props.onChange(newOptions);
|
this.props.onChange(newOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private onChange = () => {
|
||||||
private onChange() {
|
|
||||||
this.reportOptions();
|
this.reportOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,8 +82,7 @@ export default class ResizerOptions extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private onWidthInput = () => {
|
||||||
private onWidthInput() {
|
|
||||||
if (this.state.maintainAspect) {
|
if (this.state.maintainAspect) {
|
||||||
const width = inputFieldValueAsNumber(this.form!.width);
|
const width = inputFieldValueAsNumber(this.form!.width);
|
||||||
this.form!.height.value = Math.round(width / this.getAspect());
|
this.form!.height.value = Math.round(width / this.getAspect());
|
||||||
@@ -93,8 +91,7 @@ export default class ResizerOptions extends Component<Props, State> {
|
|||||||
this.reportOptions();
|
this.reportOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private onHeightInput = () => {
|
||||||
private onHeightInput() {
|
|
||||||
if (this.state.maintainAspect) {
|
if (this.state.maintainAspect) {
|
||||||
const height = inputFieldValueAsNumber(this.form!.height);
|
const height = inputFieldValueAsNumber(this.form!.height);
|
||||||
this.form!.width.value = Math.round(height * this.getAspect());
|
this.form!.width.value = Math.round(height * this.getAspect());
|
||||||
@@ -123,8 +120,7 @@ export default class ResizerOptions extends Component<Props, State> {
|
|||||||
return 'custom';
|
return 'custom';
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private onPresetChange = (event: Event) => {
|
||||||
private onPresetChange(event: Event) {
|
|
||||||
const select = event.target as HTMLSelectElement;
|
const select = event.target as HTMLSelectElement;
|
||||||
if (select.value === 'custom') return;
|
if (select.value === 'custom') return;
|
||||||
const multiplier = Number(select.value);
|
const multiplier = Number(select.value);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { h, Component } from 'preact';
|
import { h, Component } from 'preact';
|
||||||
import { bind } from '../../lib/initial-util';
|
|
||||||
import { inputFieldCheckedAsNumber, inputFieldValueAsNumber, preventDefault } from '../../lib/util';
|
import { inputFieldCheckedAsNumber, inputFieldValueAsNumber, preventDefault } from '../../lib/util';
|
||||||
import { EncodeOptions, WebPImageHint } from './encoder-meta';
|
import { EncodeOptions, WebPImageHint } from './encoder-meta';
|
||||||
import * as style from '../../components/Options/style.module.scss';
|
import * as style from '../../components/Options/style.module.scss';
|
||||||
@@ -41,8 +40,7 @@ export default class WebPEncoderOptions extends Component<Props, State> {
|
|||||||
showAdvanced: false,
|
showAdvanced: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@bind
|
onChange = (event: Event) => {
|
||||||
onChange(event: Event) {
|
|
||||||
const form = (event.currentTarget as HTMLInputElement).closest('form') as HTMLFormElement;
|
const form = (event.currentTarget as HTMLInputElement).closest('form') as HTMLFormElement;
|
||||||
const lossless = inputFieldCheckedAsNumber(form.lossless);
|
const lossless = inputFieldCheckedAsNumber(form.lossless);
|
||||||
const { options } = this.props;
|
const { options } = this.props;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { h, Component } from 'preact';
|
import { h, Component } from 'preact';
|
||||||
|
|
||||||
import { bind, linkRef, Fileish } from '../../lib/initial-util';
|
import { linkRef, Fileish } from '../../lib/initial-util';
|
||||||
import * as style from './style.module.scss';
|
import * as style from './style.module.scss';
|
||||||
import { FileDropEvent } from 'file-drop-element';
|
import { FileDropEvent } from 'file-drop-element';
|
||||||
import 'file-drop-element';
|
import 'file-drop-element';
|
||||||
@@ -43,6 +43,12 @@ export default class App extends Component<Props, State> {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
this.onFileDrop = this.onFileDrop.bind(this);
|
||||||
|
this.onIntroPickFile = this.onIntroPickFile.bind(this);
|
||||||
|
this.showSnack = this.showSnack.bind(this);
|
||||||
|
this.onPopState = this.onPopState.bind(this);
|
||||||
|
this.openEditor = this.openEditor.bind(this);
|
||||||
|
|
||||||
compressPromise.then((module) => {
|
compressPromise.then((module) => {
|
||||||
this.setState({ Compress: module.default });
|
this.setState({ Compress: module.default });
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
@@ -61,7 +67,8 @@ export default class App extends Component<Props, State> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// In development, persist application state across hot reloads:
|
// In development, persist application state across hot reloads:
|
||||||
if (process.env.NODE_ENV === 'development') {
|
// if (process.env.NODE_ENV === 'development') {
|
||||||
|
if (module.hot) {
|
||||||
this.setState(window.STATE);
|
this.setState(window.STATE);
|
||||||
const oldCDU = this.componentDidUpdate;
|
const oldCDU = this.componentDidUpdate;
|
||||||
this.componentDidUpdate = (props, state, prev) => {
|
this.componentDidUpdate = (props, state, prev) => {
|
||||||
@@ -81,7 +88,6 @@ export default class App extends Component<Props, State> {
|
|||||||
window.addEventListener('popstate', this.onPopState);
|
window.addEventListener('popstate', this.onPopState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
|
||||||
private onFileDrop({ files }: FileDropEvent) {
|
private onFileDrop({ files }: FileDropEvent) {
|
||||||
if (!files || files.length === 0) return;
|
if (!files || files.length === 0) return;
|
||||||
const file = files[0];
|
const file = files[0];
|
||||||
@@ -89,24 +95,20 @@ export default class App extends Component<Props, State> {
|
|||||||
this.setState({ file });
|
this.setState({ file });
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
|
||||||
private onIntroPickFile(file: File | Fileish) {
|
private onIntroPickFile(file: File | Fileish) {
|
||||||
this.openEditor();
|
this.openEditor();
|
||||||
this.setState({ file });
|
this.setState({ file });
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
|
||||||
private showSnack(message: string, options: SnackOptions = {}): Promise<string> {
|
private showSnack(message: string, options: SnackOptions = {}): Promise<string> {
|
||||||
if (!this.snackbar) throw Error('Snackbar missing');
|
if (!this.snackbar) throw Error('Snackbar missing');
|
||||||
return this.snackbar.showSnackbar(message, options);
|
return this.snackbar.showSnackbar(message, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
|
||||||
private onPopState() {
|
private onPopState() {
|
||||||
this.setState({ isEditorOpen: location.pathname === ROUTE_EDITOR });
|
this.setState({ isEditorOpen: location.pathname === ROUTE_EDITOR });
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
|
||||||
private openEditor() {
|
private openEditor() {
|
||||||
if (this.state.isEditorOpen) return;
|
if (this.state.isEditorOpen) return;
|
||||||
// Change path, but preserve query string.
|
// Change path, but preserve query string.
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { h, Component } from 'preact';
|
import { h, Component } from 'preact';
|
||||||
|
|
||||||
import * as style from './style.module.scss';
|
import * as style from './style.module.scss';
|
||||||
import { bind } from '../../lib/initial-util';
|
|
||||||
import { cleanSet, cleanMerge } from '../../lib/clean-modify';
|
import { cleanSet, cleanMerge } from '../../lib/clean-modify';
|
||||||
import OxiPNGEncoderOptions from '../../codecs/oxipng/options';
|
import OxiPNGEncoderOptions from '../../codecs/oxipng/options';
|
||||||
import MozJpegEncoderOptions from '../../codecs/mozjpeg/options';
|
import MozJpegEncoderOptions from '../../codecs/mozjpeg/options';
|
||||||
@@ -82,8 +81,7 @@ export default class Options extends Component<Props, State> {
|
|||||||
encodersSupported.then(encoderSupportMap => this.setState({ encoderSupportMap }));
|
encodersSupported.then(encoderSupportMap => this.setState({ encoderSupportMap }));
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private onEncoderTypeChange = (event: Event) => {
|
||||||
private onEncoderTypeChange(event: Event) {
|
|
||||||
const el = event.currentTarget as HTMLSelectElement;
|
const el = event.currentTarget as HTMLSelectElement;
|
||||||
|
|
||||||
// The select element only has values matching encoder types,
|
// The select element only has values matching encoder types,
|
||||||
@@ -92,8 +90,7 @@ export default class Options extends Component<Props, State> {
|
|||||||
this.props.onEncoderTypeChange(type);
|
this.props.onEncoderTypeChange(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private onPreprocessorEnabledChange = (event: Event) => {
|
||||||
private onPreprocessorEnabledChange(event: Event) {
|
|
||||||
const el = event.currentTarget as HTMLInputElement;
|
const el = event.currentTarget as HTMLInputElement;
|
||||||
const preprocessor = el.name.split('.')[0] as keyof PreprocessorState;
|
const preprocessor = el.name.split('.')[0] as keyof PreprocessorState;
|
||||||
|
|
||||||
@@ -102,15 +99,13 @@ export default class Options extends Component<Props, State> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private onQuantizerOptionsChange = (opts: QuantizeOptions) => {
|
||||||
private onQuantizerOptionsChange(opts: QuantizeOptions) {
|
|
||||||
this.props.onPreprocessorOptionsChange(
|
this.props.onPreprocessorOptionsChange(
|
||||||
cleanMerge(this.props.preprocessorState, 'quantizer', opts),
|
cleanMerge(this.props.preprocessorState, 'quantizer', opts),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private onResizeOptionsChange = (opts: ResizeOptions) => {
|
||||||
private onResizeOptionsChange(opts: ResizeOptions) {
|
|
||||||
this.props.onPreprocessorOptionsChange(
|
this.props.onPreprocessorOptionsChange(
|
||||||
cleanMerge(this.props.preprocessorState, 'resize', opts),
|
cleanMerge(this.props.preprocessorState, 'resize', opts),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import PinchZoom, { ScaleToOpts } from './custom-els/PinchZoom';
|
|||||||
import './custom-els/PinchZoom';
|
import './custom-els/PinchZoom';
|
||||||
import './custom-els/TwoUp';
|
import './custom-els/TwoUp';
|
||||||
import * as style from './style.module.scss';
|
import * as style from './style.module.scss';
|
||||||
import { bind, linkRef } from '../../lib/initial-util';
|
import { linkRef } from '../../lib/initial-util';
|
||||||
import { shallowEqual, drawDataToCanvas } from '../../lib/util';
|
import { shallowEqual, drawDataToCanvas } from '../../lib/util';
|
||||||
import {
|
import {
|
||||||
ToggleBackgroundIcon,
|
ToggleBackgroundIcon,
|
||||||
@@ -135,29 +135,25 @@ export default class Output extends Component<Props, State> {
|
|||||||
return props.rightCompressed || (props.source && props.source.processed);
|
return props.rightCompressed || (props.source && props.source.processed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private toggleBackground = () => {
|
||||||
private toggleBackground() {
|
|
||||||
this.setState({
|
this.setState({
|
||||||
altBackground: !this.state.altBackground,
|
altBackground: !this.state.altBackground,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private zoomIn = () => {
|
||||||
private zoomIn() {
|
|
||||||
if (!this.pinchZoomLeft) throw Error('Missing pinch-zoom element');
|
if (!this.pinchZoomLeft) throw Error('Missing pinch-zoom element');
|
||||||
|
|
||||||
this.pinchZoomLeft.scaleTo(this.state.scale * 1.25, scaleToOpts);
|
this.pinchZoomLeft.scaleTo(this.state.scale * 1.25, scaleToOpts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private zoomOut = () => {
|
||||||
private zoomOut() {
|
|
||||||
if (!this.pinchZoomLeft) throw Error('Missing pinch-zoom element');
|
if (!this.pinchZoomLeft) throw Error('Missing pinch-zoom element');
|
||||||
|
|
||||||
this.pinchZoomLeft.scaleTo(this.state.scale / 1.25, scaleToOpts);
|
this.pinchZoomLeft.scaleTo(this.state.scale / 1.25, scaleToOpts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private onRotateClick = () => {
|
||||||
private onRotateClick() {
|
|
||||||
const { inputProcessorState } = this.props;
|
const { inputProcessorState } = this.props;
|
||||||
if (!inputProcessorState) return;
|
if (!inputProcessorState) return;
|
||||||
|
|
||||||
@@ -170,8 +166,7 @@ export default class Output extends Component<Props, State> {
|
|||||||
this.props.onInputProcessorChange(newState);
|
this.props.onInputProcessorChange(newState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private onScaleValueFocus = () => {
|
||||||
private onScaleValueFocus() {
|
|
||||||
this.setState({ editingScale: true }, () => {
|
this.setState({ editingScale: true }, () => {
|
||||||
if (this.scaleInput) {
|
if (this.scaleInput) {
|
||||||
// Firefox unfocuses the input straight away unless I force a style calculation here. I have
|
// Firefox unfocuses the input straight away unless I force a style calculation here. I have
|
||||||
@@ -182,13 +177,11 @@ export default class Output extends Component<Props, State> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private onScaleInputBlur = () => {
|
||||||
private onScaleInputBlur() {
|
|
||||||
this.setState({ editingScale: false });
|
this.setState({ editingScale: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private onScaleInputChanged = (event: Event) => {
|
||||||
private onScaleInputChanged(event: Event) {
|
|
||||||
const target = event.target as HTMLInputElement;
|
const target = event.target as HTMLInputElement;
|
||||||
const percent = parseFloat(target.value);
|
const percent = parseFloat(target.value);
|
||||||
if (isNaN(percent)) return;
|
if (isNaN(percent)) return;
|
||||||
@@ -197,8 +190,7 @@ export default class Output extends Component<Props, State> {
|
|||||||
this.pinchZoomLeft.scaleTo(percent / 100, scaleToOpts);
|
this.pinchZoomLeft.scaleTo(percent / 100, scaleToOpts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private onPinchZoomLeftChange = (event: Event) => {
|
||||||
private onPinchZoomLeftChange(event: Event) {
|
|
||||||
if (!this.pinchZoomRight || !this.pinchZoomLeft) throw Error('Missing pinch-zoom element');
|
if (!this.pinchZoomRight || !this.pinchZoomLeft) throw Error('Missing pinch-zoom element');
|
||||||
this.setState({
|
this.setState({
|
||||||
scale: this.pinchZoomLeft.scale,
|
scale: this.pinchZoomLeft.scale,
|
||||||
@@ -218,8 +210,7 @@ export default class Output extends Component<Props, State> {
|
|||||||
*
|
*
|
||||||
* @param event Event to redirect
|
* @param event Event to redirect
|
||||||
*/
|
*/
|
||||||
@bind
|
private onRetargetableEvent = (event: Event) => {
|
||||||
private onRetargetableEvent(event: Event) {
|
|
||||||
const targetEl = event.target as HTMLElement;
|
const targetEl = event.target as HTMLElement;
|
||||||
if (!this.pinchZoomLeft) throw Error('Missing pinch-zoom element');
|
if (!this.pinchZoomLeft) throw Error('Missing pinch-zoom element');
|
||||||
// If the event is on the handle of the two-up, let it through,
|
// If the event is on the handle of the two-up, let it through,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { h, Component } from 'preact';
|
import { h, Component } from 'preact';
|
||||||
|
|
||||||
import { bind, Fileish } from '../../lib/initial-util';
|
import { Fileish } from '../../lib/initial-util';
|
||||||
import { blobToImg, drawableToImageData, blobToText } from '../../lib/util';
|
import { blobToImg, drawableToImageData, blobToText } from '../../lib/util';
|
||||||
import * as style from './style.module.scss';
|
import * as style from './style.module.scss';
|
||||||
import Output from '../Output';
|
import Output from '../Output';
|
||||||
@@ -254,8 +254,7 @@ export default class Compress extends Component<Props, State> {
|
|||||||
import('../../lib/sw-bridge').then(({ mainAppLoaded }) => mainAppLoaded());
|
import('../../lib/sw-bridge').then(({ mainAppLoaded }) => mainAppLoaded());
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private onMobileWidthChange = () => {
|
||||||
private onMobileWidthChange() {
|
|
||||||
this.setState({ mobileView: this.widthQuery.matches });
|
this.setState({ mobileView: this.widthQuery.matches });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,8 +343,7 @@ export default class Compress extends Component<Props, State> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private onInputProcessorChange = async (options: InputProcessorState): Promise<void> => {
|
||||||
private async onInputProcessorChange(options: InputProcessorState): Promise<void> {
|
|
||||||
const source = this.state.source;
|
const source = this.state.source;
|
||||||
if (!source) return;
|
if (!source) return;
|
||||||
|
|
||||||
@@ -396,8 +394,7 @@ export default class Compress extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private updateFile = async (file: File | Fileish) => {
|
||||||
private async updateFile(file: File | Fileish) {
|
|
||||||
const loadingCounter = this.state.loadingCounter + 1;
|
const loadingCounter = this.state.loadingCounter + 1;
|
||||||
// Either processor is good enough here.
|
// Either processor is good enough here.
|
||||||
const processor = this.leftProcessor;
|
const processor = this.leftProcessor;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { h, Component } from 'preact';
|
import { h, Component } from 'preact';
|
||||||
|
|
||||||
import { bind, linkRef, Fileish } from '../../lib/initial-util';
|
import { linkRef, Fileish } from '../../lib/initial-util';
|
||||||
import '../custom-els/LoadingSpinner';
|
import '../custom-els/LoadingSpinner';
|
||||||
|
|
||||||
import logo from 'url:./imgs/logo.svg';
|
import logo from 'url:./imgs/logo.svg';
|
||||||
@@ -67,13 +67,11 @@ export default class Intro extends Component<Props, State> {
|
|||||||
window.addEventListener('appinstalled', this.onAppInstalled);
|
window.addEventListener('appinstalled', this.onAppInstalled);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private resetFileInput = () => {
|
||||||
private resetFileInput() {
|
|
||||||
this.fileInput!.value = '';
|
this.fileInput!.value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private onFileChange = (event: Event) => {
|
||||||
private onFileChange(event: Event): void {
|
|
||||||
const fileInput = event.target as HTMLInputElement;
|
const fileInput = event.target as HTMLInputElement;
|
||||||
const file = fileInput.files && fileInput.files[0];
|
const file = fileInput.files && fileInput.files[0];
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
@@ -81,13 +79,11 @@ export default class Intro extends Component<Props, State> {
|
|||||||
this.props.onFile(file);
|
this.props.onFile(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private onButtonClick = () => {
|
||||||
private onButtonClick() {
|
|
||||||
this.fileInput!.click();
|
this.fileInput!.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private onDemoClick = async (index: number, event: Event) => {
|
||||||
private async onDemoClick(index: number, event: Event) {
|
|
||||||
try {
|
try {
|
||||||
this.setState({ fetchingDemoIndex: index });
|
this.setState({ fetchingDemoIndex: index });
|
||||||
const demo = demos[index];
|
const demo = demos[index];
|
||||||
@@ -104,8 +100,7 @@ export default class Intro extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private onBeforeInstallPromptEvent = (event: BeforeInstallPromptEvent) => {
|
||||||
private onBeforeInstallPromptEvent(event: BeforeInstallPromptEvent) {
|
|
||||||
// Don't show the mini-infobar on mobile
|
// Don't show the mini-infobar on mobile
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
@@ -121,8 +116,7 @@ export default class Intro extends Component<Props, State> {
|
|||||||
ga('send', 'event', gaEventInfo);
|
ga('send', 'event', gaEventInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private onInstallClick = async (event: Event) => {
|
||||||
private async onInstallClick(event: Event) {
|
|
||||||
// Get the deferred beforeinstallprompt event
|
// Get the deferred beforeinstallprompt event
|
||||||
const beforeInstallEvent = this.state.beforeInstallEvent;
|
const beforeInstallEvent = this.state.beforeInstallEvent;
|
||||||
// If there's no deferred prompt, bail.
|
// If there's no deferred prompt, bail.
|
||||||
@@ -150,8 +144,7 @@ export default class Intro extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private onAppInstalled = () => {
|
||||||
private onAppInstalled() {
|
|
||||||
// We don't need the install button, if it's shown
|
// We don't need the install button, if it's shown
|
||||||
this.setState({ beforeInstallEvent: undefined });
|
this.setState({ beforeInstallEvent: undefined });
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { h, Component } from 'preact';
|
|||||||
import * as style from './style.module.scss';
|
import * as style from './style.module.scss';
|
||||||
import RangeInputElement from '../../custom-els/RangeInput';
|
import RangeInputElement from '../../custom-els/RangeInput';
|
||||||
import '../../custom-els/RangeInput';
|
import '../../custom-els/RangeInput';
|
||||||
import { linkRef, bind } from '../../lib/initial-util';
|
import { linkRef } from '../../lib/initial-util';
|
||||||
|
|
||||||
interface Props extends JSX.HTMLAttributes {}
|
interface Props extends JSX.HTMLAttributes {}
|
||||||
interface State {}
|
interface State {}
|
||||||
@@ -10,8 +10,7 @@ interface State {}
|
|||||||
export default class Range extends Component<Props, State> {
|
export default class Range extends Component<Props, State> {
|
||||||
rangeWc?: RangeInputElement;
|
rangeWc?: RangeInputElement;
|
||||||
|
|
||||||
@bind
|
private onTextInput = (event: Event) => {
|
||||||
private onTextInput(event: Event) {
|
|
||||||
const input = event.target as HTMLInputElement;
|
const input = event.target as HTMLInputElement;
|
||||||
const value = input.value.trim();
|
const value = input.value.trim();
|
||||||
if (!value) return;
|
if (!value) return;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import FileSize from './FileSize';
|
|||||||
import { DownloadIcon, CopyAcrossIcon, CopyAcrossIconProps } from '../../lib/icons';
|
import { DownloadIcon, CopyAcrossIcon, CopyAcrossIconProps } from '../../lib/icons';
|
||||||
import '../custom-els/LoadingSpinner';
|
import '../custom-els/LoadingSpinner';
|
||||||
import { SourceImage } from '../compress';
|
import { SourceImage } from '../compress';
|
||||||
import { Fileish, bind } from '../../lib/initial-util';
|
import { Fileish } from '../../lib/initial-util';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
@@ -52,14 +52,12 @@ export default class Results extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
private onCopyToOtherClick = (event: Event) => {
|
||||||
private onCopyToOtherClick(event: Event) {
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.props.onCopyToOtherClick();
|
this.props.onCopyToOtherClick();
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
onDownload = () => {
|
||||||
onDownload() {
|
|
||||||
// GA can’t do floats. So we round to ints. We're deliberately rounding to nearest kilobyte to
|
// GA can’t do floats. So we round to ints. We're deliberately rounding to nearest kilobyte to
|
||||||
// avoid cases where exact image sizes leak something interesting about the user.
|
// avoid cases where exact image sizes leak something interesting about the user.
|
||||||
const before = Math.round(this.props.source!.file.size / 1024);
|
const before = Math.round(this.props.source!.file.size / 1024);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import PointerTracker from 'pointer-tracker';
|
import PointerTracker from 'pointer-tracker';
|
||||||
import { bind } from '../../lib/initial-util';
|
|
||||||
import * as style from './styles.module.css';
|
import * as style from './styles.module.css';
|
||||||
|
|
||||||
const RETARGETED_EVENTS = ['focus', 'blur'];
|
const RETARGETED_EVENTS = ['focus', 'blur'];
|
||||||
@@ -23,6 +22,7 @@ class RangeInputElement extends HTMLElement {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this._input = document.createElement('input');
|
this._input = document.createElement('input');
|
||||||
this._input.type = 'range';
|
this._input.type = 'range';
|
||||||
this._input.className = style.input;
|
this._input.className = style.input;
|
||||||
@@ -38,10 +38,12 @@ class RangeInputElement extends HTMLElement {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._retargetEvent = this._retargetEvent.bind(this);
|
||||||
for (const event of RETARGETED_EVENTS) {
|
for (const event of RETARGETED_EVENTS) {
|
||||||
this._input.addEventListener(event, this._retargetEvent, true);
|
this._input.addEventListener(event, this._retargetEvent, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._update = this._update.bind(this);
|
||||||
for (const event of UPDATE_EVENTS) {
|
for (const event of UPDATE_EVENTS) {
|
||||||
this._input.addEventListener(event, this._update, true);
|
this._input.addEventListener(event, this._update, true);
|
||||||
}
|
}
|
||||||
@@ -80,14 +82,12 @@ class RangeInputElement extends HTMLElement {
|
|||||||
this._update();
|
this._update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
|
||||||
private _retargetEvent(event: Event) {
|
private _retargetEvent(event: Event) {
|
||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation();
|
||||||
const retargetted = new Event(event.type, event);
|
const retargetted = new Event(event.type, event);
|
||||||
this.dispatchEvent(retargetted);
|
this.dispatchEvent(retargetted);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
|
||||||
private _update() {
|
private _update() {
|
||||||
const value = Number(this.value) || 0;
|
const value = Number(this.value) || 0;
|
||||||
const min = Number(this.min) || 0;
|
const min = Number(this.min) || 0;
|
||||||
|
|||||||
13
src/index.ts
13
src/index.ts
@@ -1,18 +1,7 @@
|
|||||||
declare module '@webcomponents/custom-elements';
|
declare module '@webcomponents/custom-elements';
|
||||||
|
|
||||||
// Patch Worker to ignore `importScripts("x.css")` generated by Parcel:
|
|
||||||
const W = self.Worker;
|
|
||||||
self.Worker = function (url: string | URL, options?: WorkerOptions) {
|
|
||||||
const code = `
|
|
||||||
importScripts=(function(){
|
|
||||||
return this.apply(self,[].slice.call(arguments).map(function(x){return !/\\.css$/i.test(x) && new URL(x,self.url).href}).filter(Boolean))
|
|
||||||
}).bind(importScripts);importScripts(self.url=${JSON.stringify(url)})
|
|
||||||
`.trim();
|
|
||||||
return new W(URL.createObjectURL(new Blob([code], { type: 'text/javascript' })), options);
|
|
||||||
} as any as (typeof Worker);
|
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
require('./init-app.tsx');
|
import('./init-app.tsx');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!('customElements' in self)) {
|
if (!('customElements' in self)) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { h, render } from 'preact';
|
import { h, render } from 'preact';
|
||||||
import './lib/fix-pmc.mjs';
|
import './lib/fix-pmc.mjs';
|
||||||
import './style/index.scss';
|
import './style/index.scss';
|
||||||
import App from './components/App';
|
import App from './components/App/index.tsx';
|
||||||
|
|
||||||
// Find the outermost Element in our server-rendered HTML structure.
|
// Find the outermost Element in our server-rendered HTML structure.
|
||||||
let root = document.getElementById('app_root') as Element;
|
let root = document.getElementById('app_root') as Element;
|
||||||
@@ -10,12 +10,7 @@ let root = document.getElementById('app_root') as Element;
|
|||||||
root = render(<App />, document.body, root);
|
root = render(<App />, document.body, root);
|
||||||
root.setAttribute('id', 'app_root');
|
root.setAttribute('id', 'app_root');
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (module.hot) {
|
||||||
// Enable support for React DevTools and some helpful console warnings:
|
// Enable support for React DevTools and some helpful console warnings:
|
||||||
require('preact/debug');
|
import('preact/debug');
|
||||||
|
|
||||||
// Full HMR may not be working due to https://github.com/parcel-bundler/parcel/issues/5016
|
|
||||||
if (module.hot) {
|
|
||||||
module.hot.accept();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,6 @@
|
|||||||
// This file contains the utils that are needed for the very first rendering of the page. They're
|
// This file contains the utils that are needed for the very first rendering of the page. They're
|
||||||
// here because WebPack isn't quite smart enough to split things in the same file.
|
// here because WebPack isn't quite smart enough to split things in the same file.
|
||||||
|
|
||||||
/**
|
|
||||||
* A decorator that binds values to their class instance.
|
|
||||||
* @example
|
|
||||||
* class C {
|
|
||||||
* @bind
|
|
||||||
* foo () {
|
|
||||||
* return this;
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* let f = new C().foo;
|
|
||||||
* f() instanceof C; // true
|
|
||||||
*/
|
|
||||||
export function bind(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
|
||||||
return {
|
|
||||||
// the first time the prototype property is accessed for an instance,
|
|
||||||
// define an instance property pointing to the bound function.
|
|
||||||
// This effectively "caches" the bound prototype method as an instance property.
|
|
||||||
get() {
|
|
||||||
const bound = descriptor.value.bind(this);
|
|
||||||
Object.defineProperty(this, propertyKey, {
|
|
||||||
value: bound,
|
|
||||||
});
|
|
||||||
return bound;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Creates a function ref that assigns its value to a given property of an object.
|
/** Creates a function ref that assigns its value to a given property of an object.
|
||||||
* @example
|
* @example
|
||||||
* // element is stored as `this.foo` when rendered.
|
* // element is stored as `this.foo` when rendered.
|
||||||
|
|||||||
Reference in New Issue
Block a user