mirror of
https://github.com/GoogleChromeLabs/squoosh.git
synced 2025-11-12 08:47:31 +00:00
Use strict TypeScript, enable TSLint, update all types to work in strict mode.
This commit is contained in:
9
.editorconfig
Normal file
9
.editorconfig
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
18
global.d.ts
vendored
18
global.d.ts
vendored
@@ -0,0 +1,18 @@
|
|||||||
|
declare const __webpack_public_path__: string;
|
||||||
|
|
||||||
|
interface NodeModule {
|
||||||
|
hot: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare namespace JSX {
|
||||||
|
interface Element { }
|
||||||
|
interface IntrinsicElements { div: any; }
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'preact-i18n';
|
||||||
|
declare module 'preact-material-components-drawer';
|
||||||
|
declare module 'material-radial-progress';
|
||||||
|
|
||||||
|
declare module 'classnames' {
|
||||||
|
export default function classnames(...args: any[]): string;
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,10 @@
|
|||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": "eslint-config-developit",
|
"extends": "eslint-config-developit",
|
||||||
"rules": {
|
"rules": {
|
||||||
|
"indent": [
|
||||||
|
2,
|
||||||
|
2
|
||||||
|
],
|
||||||
"react/prefer-stateless-function": 0
|
"react/prefer-stateless-function": 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -37,6 +41,8 @@
|
|||||||
"eslint": "^4.18.2",
|
"eslint": "^4.18.2",
|
||||||
"eslint-config-developit": "^1.1.1",
|
"eslint-config-developit": "^1.1.1",
|
||||||
"extract-text-webpack-plugin": "^4.0.0-beta.0",
|
"extract-text-webpack-plugin": "^4.0.0-beta.0",
|
||||||
|
"fork-ts-checker-notifier-webpack-plugin": "^0.4.0",
|
||||||
|
"fork-ts-checker-webpack-plugin": "^0.4.1",
|
||||||
"html-webpack-plugin": "^3.0.6",
|
"html-webpack-plugin": "^3.0.6",
|
||||||
"if-env": "^1.0.4",
|
"if-env": "^1.0.4",
|
||||||
"mini-css-extract-plugin": "^0.2.0",
|
"mini-css-extract-plugin": "^0.2.0",
|
||||||
@@ -48,6 +54,9 @@
|
|||||||
"script-ext-html-webpack-plugin": "^2.0.0",
|
"script-ext-html-webpack-plugin": "^2.0.0",
|
||||||
"style-loader": "^0.20.3",
|
"style-loader": "^0.20.3",
|
||||||
"ts-loader": "^4.0.1",
|
"ts-loader": "^4.0.1",
|
||||||
|
"tslint": "^5.9.1",
|
||||||
|
"tslint-config-semistandard": "^7.0.0",
|
||||||
|
"tslint-react": "^3.5.1",
|
||||||
"typescript": "^2.7.2",
|
"typescript": "^2.7.2",
|
||||||
"typescript-loader": "^1.1.3",
|
"typescript-loader": "^1.1.3",
|
||||||
"typings-for-css-modules-loader": "^1.7.0",
|
"typings-for-css-modules-loader": "^1.7.0",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { h, Component } from 'preact';
|
import { h, Component } from 'preact';
|
||||||
import { updater, toggle, When } from '../../lib/util';
|
import { When, bind } from '../../lib/util';
|
||||||
import Fab from '../fab';
|
import Fab from '../fab';
|
||||||
import Header from '../header';
|
import Header from '../header';
|
||||||
// import Drawer from 'async!../drawer';
|
// import Drawer from 'async!../drawer';
|
||||||
@@ -8,105 +8,113 @@ import Home from '../home';
|
|||||||
import * as style from './style.scss';
|
import * as style from './style.scss';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
url?: String
|
url?: string
|
||||||
}
|
};
|
||||||
|
|
||||||
type FileObj = {
|
export type FileObj = {
|
||||||
id: any,
|
id: number,
|
||||||
data: any,
|
data?: string,
|
||||||
error: Error | DOMError | String,
|
error?: Error | DOMError | String,
|
||||||
file: File,
|
file: File,
|
||||||
loading: Boolean
|
loading: boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
showDrawer: Boolean,
|
showDrawer: boolean,
|
||||||
showFab: Boolean,
|
showFab: boolean,
|
||||||
files: FileObj[]
|
files: FileObj[]
|
||||||
};
|
};
|
||||||
|
|
||||||
let counter = 0;
|
let counter = 0;
|
||||||
|
|
||||||
export default class App extends Component<Props, State> {
|
export default class App extends Component<Props, State> {
|
||||||
state: State = {
|
state: State = {
|
||||||
showDrawer: false,
|
showDrawer: false,
|
||||||
showFab: true,
|
showFab: true,
|
||||||
files: []
|
files: []
|
||||||
};
|
};
|
||||||
|
|
||||||
loadFile = (file: File) => {
|
enableDrawer = false;
|
||||||
let fileObj = {
|
|
||||||
id: ++counter,
|
|
||||||
file,
|
|
||||||
error: null,
|
|
||||||
loading: true,
|
|
||||||
data: null
|
|
||||||
};
|
|
||||||
|
|
||||||
this.setState({
|
@bind
|
||||||
files: [fileObj]
|
openDrawer() {
|
||||||
});
|
this.setState({ showDrawer: true });
|
||||||
|
}
|
||||||
|
@bind
|
||||||
|
closeDrawer() {
|
||||||
|
this.setState({ showDrawer: false });
|
||||||
|
}
|
||||||
|
@bind
|
||||||
|
toggleDrawer() {
|
||||||
|
this.setState({ showDrawer: !this.state.showDrawer });
|
||||||
|
}
|
||||||
|
|
||||||
let fr = new FileReader();
|
@bind
|
||||||
// fr.readAsArrayBuffer();
|
openFab() {
|
||||||
fr.onerror = () => {
|
this.setState({ showFab: true });
|
||||||
let files = this.state.files.slice();
|
}
|
||||||
files.splice(0, files.indexOf(fileObj), {
|
@bind
|
||||||
...fileObj,
|
closeFab() {
|
||||||
error: fr.error,
|
this.setState({ showFab: false });
|
||||||
loading: false
|
}
|
||||||
});
|
@bind
|
||||||
this.setState({ files });
|
toggleFab() {
|
||||||
};
|
this.setState({ showFab: !this.state.showFab });
|
||||||
fr.onloadend = () => {
|
}
|
||||||
let files = this.state.files.slice();
|
|
||||||
files.splice(0, files.indexOf(fileObj), {
|
|
||||||
...fileObj,
|
|
||||||
data: fr.result,
|
|
||||||
loading: false
|
|
||||||
});
|
|
||||||
this.setState({ files });
|
|
||||||
};
|
|
||||||
fr.readAsDataURL(file);
|
|
||||||
};
|
|
||||||
|
|
||||||
enableDrawer = false;
|
@bind
|
||||||
|
loadFile(file: File) {
|
||||||
|
let fileObj: FileObj = {
|
||||||
|
id: ++counter,
|
||||||
|
file,
|
||||||
|
error: undefined,
|
||||||
|
loading: true,
|
||||||
|
data: undefined
|
||||||
|
};
|
||||||
|
|
||||||
openDrawer = updater(this, 'showDrawer', true);
|
this.setState({
|
||||||
closeDrawer = updater(this, 'showDrawer', false);
|
files: [fileObj]
|
||||||
toggleDrawer = updater(this, 'showDrawer', toggle);
|
});
|
||||||
|
|
||||||
openFab = updater(this, 'showFab', true);
|
let done = () => {
|
||||||
closeFab = updater(this, 'showFab', false);
|
let files = this.state.files.slice();
|
||||||
toggleFab = updater(this, 'showFab', toggle);
|
files[files.indexOf(fileObj)] = Object.assign({}, fileObj, {
|
||||||
|
error: fr.error,
|
||||||
|
loading: false,
|
||||||
|
data: fr.result
|
||||||
|
});
|
||||||
|
this.setState({ files });
|
||||||
|
};
|
||||||
|
|
||||||
render({ url }, { showDrawer, showFab, files }) {
|
let fr = new FileReader();
|
||||||
if (showDrawer) this.enableDrawer = true;
|
fr.onerror = fr.onloadend = done;
|
||||||
|
fr.readAsDataURL(file);
|
||||||
|
}
|
||||||
|
|
||||||
if (showFab===true) showFab = files.length>0;
|
render({ url }: Props, { showDrawer, showFab, files }: State) {
|
||||||
|
if (showDrawer) this.enableDrawer = true;
|
||||||
|
|
||||||
return (
|
if (showFab === true) showFab = files.length > 0;
|
||||||
<div id="app" class={style.app}>
|
|
||||||
<Fab showing={showFab} />
|
|
||||||
|
|
||||||
<Header toggleDrawer={this.toggleDrawer} loadFile={this.loadFile} />
|
return (
|
||||||
|
<div id="app" class={style.app}>
|
||||||
{/* Avoid loading & rendering the drawer until the first time it is shown. */}
|
<Fab showing={showFab} />
|
||||||
<When value={showDrawer}>
|
|
||||||
<Drawer showing={showDrawer} openDrawer={this.openDrawer} closeDrawer={this.closeDrawer} />
|
|
||||||
</When>
|
|
||||||
|
|
||||||
{/*
|
<Header class={style.header} toggleDrawer={this.toggleDrawer} loadFile={this.loadFile} />
|
||||||
Note: this is normally where a <Router> with auto code-splitting goes.
|
|
||||||
Since we don't seem to need one (yet?), it's omitted.
|
|
||||||
*/}
|
|
||||||
<div class={style.content}>
|
|
||||||
<Home files={files} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* This ends up in the body when prerendered, which makes it load async. */}
|
{/* Avoid loading & rendering the drawer until the first time it is shown. */}
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
|
<When value={showDrawer}>
|
||||||
</div>
|
<Drawer showing={showDrawer} openDrawer={this.openDrawer} closeDrawer={this.closeDrawer} />
|
||||||
);
|
</When>
|
||||||
}
|
|
||||||
|
{/*
|
||||||
|
Note: this is normally where a <Router> with auto code-splitting goes.
|
||||||
|
Since we don't seem to need one (yet?), it's omitted.
|
||||||
|
*/}
|
||||||
|
<div class={style.content}>
|
||||||
|
<Home files={files} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
import { h, Component } from 'preact';
|
|
||||||
import MdlDrawer from 'preact-material-components-drawer';
|
|
||||||
import 'preact-material-components/Drawer/style.css';
|
|
||||||
import List from 'preact-material-components/List';
|
|
||||||
// import 'preact-material-components/List/style.css';
|
|
||||||
import { Text } from 'preact-i18n';
|
|
||||||
import style from './style';
|
|
||||||
|
|
||||||
export default class Drawer extends Component {
|
|
||||||
state = {
|
|
||||||
rendered: false
|
|
||||||
};
|
|
||||||
|
|
||||||
setRendered = () => {
|
|
||||||
this.setState({ rendered: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
render({ showing, openDrawer, closeDrawer }, { rendered }) {
|
|
||||||
if (showing && !rendered) {
|
|
||||||
setTimeout(this.setRendered, 20);
|
|
||||||
showing = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MdlDrawer open={showing} onOpen={openDrawer} onClose={closeDrawer}>
|
|
||||||
<MdlDrawer.Header class="mdc-theme--primary-bg">
|
|
||||||
<img class={style.logo} alt="logo" src="/assets/icon.png" />
|
|
||||||
</MdlDrawer.Header>
|
|
||||||
<MdlDrawer.Content class={style.list}>
|
|
||||||
<List>
|
|
||||||
<List.LinkItem href="/">
|
|
||||||
<List.ItemIcon>verified_user</List.ItemIcon>
|
|
||||||
<Text id="SIGN_IN">Sign In</Text>
|
|
||||||
</List.LinkItem>
|
|
||||||
<List.LinkItem href="/register">
|
|
||||||
<List.ItemIcon>account_circle</List.ItemIcon>
|
|
||||||
<Text id="REGISTER">Register</Text>
|
|
||||||
</List.LinkItem>
|
|
||||||
</List>
|
|
||||||
</MdlDrawer.Content>
|
|
||||||
|
|
||||||
<div class={style.bottom}>
|
|
||||||
<List.LinkItem href="/preferences">
|
|
||||||
<List.ItemIcon>settings</List.ItemIcon>
|
|
||||||
<Text id="PREFERENCES">Preferences</Text>
|
|
||||||
</List.LinkItem>
|
|
||||||
</div>
|
|
||||||
</MdlDrawer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
63
src/components/drawer/index.tsx
Normal file
63
src/components/drawer/index.tsx
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { h, Component } from 'preact';
|
||||||
|
import MdlDrawer from 'preact-material-components-drawer';
|
||||||
|
import 'preact-material-components/Drawer/style.css';
|
||||||
|
import List from 'preact-material-components/List';
|
||||||
|
// import 'preact-material-components/List/style.css';
|
||||||
|
import { Text } from 'preact-i18n';
|
||||||
|
import * as style from './style.scss';
|
||||||
|
import { bind } from '../../lib/util';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
showing: boolean,
|
||||||
|
openDrawer(): void,
|
||||||
|
closeDrawer(): void
|
||||||
|
};
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
rendered: boolean
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class Drawer extends Component<Props, State> {
|
||||||
|
state: State = {
|
||||||
|
rendered: false
|
||||||
|
};
|
||||||
|
|
||||||
|
@bind
|
||||||
|
setRendered() {
|
||||||
|
this.setState({ rendered: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
render({ showing, openDrawer, closeDrawer }: Props, { rendered }: State) {
|
||||||
|
if (showing && !rendered) {
|
||||||
|
setTimeout(this.setRendered, 20);
|
||||||
|
showing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MdlDrawer open={showing} onOpen={openDrawer} onClose={closeDrawer}>
|
||||||
|
<MdlDrawer.Header class="mdc-theme--primary-bg">
|
||||||
|
<img class={style.logo} alt="logo" src="/assets/icon.png" />
|
||||||
|
</MdlDrawer.Header>
|
||||||
|
<MdlDrawer.Content>
|
||||||
|
<List>
|
||||||
|
<List.LinkItem href="/">
|
||||||
|
<List.ItemIcon>verified_user</List.ItemIcon>
|
||||||
|
<Text id="SIGN_IN">Sign In</Text>
|
||||||
|
</List.LinkItem>
|
||||||
|
<List.LinkItem href="/register">
|
||||||
|
<List.ItemIcon>account_circle</List.ItemIcon>
|
||||||
|
<Text id="REGISTER">Register</Text>
|
||||||
|
</List.LinkItem>
|
||||||
|
</List>
|
||||||
|
</MdlDrawer.Content>
|
||||||
|
|
||||||
|
<div class={style.bottom}>
|
||||||
|
<List.LinkItem href="/preferences">
|
||||||
|
<List.ItemIcon>settings</List.ItemIcon>
|
||||||
|
<Text id="PREFERENCES">Preferences</Text>
|
||||||
|
</List.LinkItem>
|
||||||
|
</div>
|
||||||
|
</MdlDrawer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { h, Component } from 'preact';
|
import { h, Component } from 'preact';
|
||||||
|
import { bind } from '../../lib/util';
|
||||||
import Icon from 'preact-material-components/Icon';
|
import Icon from 'preact-material-components/Icon';
|
||||||
import 'preact-material-components/Icon/style.css';
|
import 'preact-material-components/Icon/style.css';
|
||||||
import Fab from 'preact-material-components/Fab';
|
import Fab from 'preact-material-components/Fab';
|
||||||
@@ -6,34 +7,41 @@ import RadialProgress from 'material-radial-progress';
|
|||||||
import * as style from './style.scss';
|
import * as style from './style.scss';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
showing: boolean
|
showing: boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
loading: boolean
|
loading: boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class AppFab extends Component<Props, State> {
|
export default class AppFab extends Component<Props, State> {
|
||||||
state: State = {
|
state: State = {
|
||||||
loading: false
|
loading: false
|
||||||
};
|
};
|
||||||
|
|
||||||
handleClick = () => {
|
@bind
|
||||||
console.log('TODO: Save the file to disk.');
|
setLoading(loading: boolean) {
|
||||||
this.setState({ loading: true });
|
this.setState({ loading });
|
||||||
setTimeout( () => {
|
}
|
||||||
this.setState({ loading: false });
|
|
||||||
}, 1000);
|
|
||||||
};
|
|
||||||
|
|
||||||
render({ showing }, { loading }) {
|
@bind
|
||||||
return (
|
handleClick() {
|
||||||
<Fab ripple secondary exited={showing===false} class={style.fab} onClick={this.handleClick}>
|
console.log('TODO: Save the file to disk.');
|
||||||
{ loading ? (
|
this.setState({ loading: true });
|
||||||
<RadialProgress primary class={style.progress} />
|
setTimeout(() => {
|
||||||
) : (
|
this.setState({ loading: false });
|
||||||
<Icon>file_download</Icon>
|
}, 1000);
|
||||||
) }
|
}
|
||||||
</Fab>
|
|
||||||
);
|
render({ showing }: Props, { loading }: State) {
|
||||||
}
|
return (
|
||||||
}
|
<Fab ripple secondary exited={showing === false} class={style.fab} onClick={this.handleClick}>
|
||||||
|
{ loading ? (
|
||||||
|
<RadialProgress primary class={style.progress} />
|
||||||
|
) : (
|
||||||
|
<Icon>file_download</Icon>
|
||||||
|
) }
|
||||||
|
</Fab>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,50 +2,52 @@ import { h, Component } from 'preact';
|
|||||||
import Toolbar from 'preact-material-components/Toolbar';
|
import Toolbar from 'preact-material-components/Toolbar';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
import * as style from './style.scss';
|
import * as style from './style.scss';
|
||||||
|
import { bind } from '../../lib/util';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
toggleDrawer?(),
|
'class'?: string,
|
||||||
showHeader?(),
|
showHeader?: boolean,
|
||||||
showFab?(),
|
toggleDrawer?(): void,
|
||||||
loadFile?(File)
|
showFab?(): void,
|
||||||
|
loadFile(f: File): void
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {};
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class Header extends Component<Props, State> {
|
export default class Header extends Component<Props, State> {
|
||||||
input: HTMLInputElement;
|
input?: HTMLInputElement;
|
||||||
|
|
||||||
setInputRef = c => {
|
@bind
|
||||||
this.input = c;
|
setInputRef(c?: Element) {
|
||||||
};
|
this.input = c as HTMLInputElement;
|
||||||
|
}
|
||||||
|
|
||||||
upload = () => {
|
@bind
|
||||||
this.input.click();
|
upload() {
|
||||||
};
|
this.input!.click();
|
||||||
|
}
|
||||||
|
|
||||||
handleFiles = () => {
|
@bind
|
||||||
let files = this.input.files;
|
handleFiles() {
|
||||||
if (files.length) {
|
let files = this.input!.files;
|
||||||
this.props.loadFile(files[0]);
|
if (files && files.length) {
|
||||||
}
|
this.props.loadFile(files[0]);
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render({ toggleDrawer, showHeader, showFab }) {
|
render({ class: c, toggleDrawer, showHeader = false, showFab }: Props) {
|
||||||
return (
|
return (
|
||||||
<Toolbar fixed class={cx(style.toolbar, 'inert', showHeader===false && style.minimal)}>
|
<Toolbar fixed class={cx(c, style.toolbar, 'inert', !showHeader && style.minimal)}>
|
||||||
<Toolbar.Row>
|
<Toolbar.Row>
|
||||||
<Toolbar.Title class={style.title}>
|
<Toolbar.Title class={style.title}>
|
||||||
<img class={style.logo} src="/assets/icon.png" />
|
<Toolbar.Icon title="Upload" ripple onClick={this.upload}>file_upload</Toolbar.Icon>
|
||||||
<Toolbar.Icon ripple onClick={this.upload}>file_upload</Toolbar.Icon>
|
</Toolbar.Title>
|
||||||
</Toolbar.Title>
|
<Toolbar.Section align-end>
|
||||||
<Toolbar.Section align-end>
|
<Toolbar.Icon ripple onClick={toggleDrawer}>menu</Toolbar.Icon>
|
||||||
<Toolbar.Icon ripple onClick={toggleDrawer}>menu</Toolbar.Icon>
|
</Toolbar.Section>
|
||||||
</Toolbar.Section>
|
</Toolbar.Row>
|
||||||
</Toolbar.Row>
|
<input class={style.fileInput} ref={this.setInputRef} type="file" onChange={this.handleFiles} />
|
||||||
<input class={style.fileInput} ref={this.setInputRef} type="file" onChange={this.handleFiles} />
|
</Toolbar>
|
||||||
</Toolbar>
|
);
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,34 +3,34 @@ import { h, Component } from 'preact';
|
|||||||
// import Switch from 'preact-material-components/Switch';
|
// import Switch from 'preact-material-components/Switch';
|
||||||
// import 'preact-material-components/Switch/style.css';
|
// import 'preact-material-components/Switch/style.css';
|
||||||
import * as style from './style.scss';
|
import * as style from './style.scss';
|
||||||
|
import { FileObj } from '../app';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
files: {
|
files: FileObj[]
|
||||||
data: any
|
|
||||||
}[]
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
active: boolean
|
active: boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class Home extends Component<Props, State> {
|
export default class Home extends Component<Props, State> {
|
||||||
state: State = {
|
state: State = {
|
||||||
active: false
|
active: false
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
setTimeout( () => {
|
setTimeout(() => {
|
||||||
this.setState({ active: true });
|
this.setState({ active: true });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render({ files }, { active }) {
|
render({ files }: Props, { active }: State) {
|
||||||
return (
|
return (
|
||||||
<div class={style.home+' '+(active ? style.active : '')}>
|
<div class={style.home + ' ' + (active ? style.active : '')}>
|
||||||
{ files && files[0] && (
|
{ files && files[0] && (
|
||||||
<img src={files[0].data} />
|
<img src={files[0].data} />
|
||||||
) }
|
) }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
src/index.js
18
src/index.js
@@ -1,18 +0,0 @@
|
|||||||
import './style';
|
|
||||||
import './lib/fix-pmc';
|
|
||||||
import App from './components/app';
|
|
||||||
|
|
||||||
export default App;
|
|
||||||
|
|
||||||
if (typeof window!=='undefined') {
|
|
||||||
addEventListener('click', e => {
|
|
||||||
let { target } = e;
|
|
||||||
do {
|
|
||||||
if (target.nodeName === 'A') {
|
|
||||||
history.pushState(null, null, target.pathname);
|
|
||||||
e.preventDefault();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} while ((target = target.parentNode));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
32
src/index.tsx
Normal file
32
src/index.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { h, render } from 'preact';
|
||||||
|
import './lib/fix-pmc';
|
||||||
|
import './style';
|
||||||
|
import App from './components/app';
|
||||||
|
|
||||||
|
// Find the outermost Element in our server-rendered HTML structure.
|
||||||
|
let root = document.querySelector('[prerender]') || undefined;
|
||||||
|
|
||||||
|
// "attach" the client-side rendering to it, updating the DOM in-place instead of replacing:
|
||||||
|
root = render(<App />, document.body, root);
|
||||||
|
|
||||||
|
// In production, this entire condition is removed.
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
// Enable support for React DevTools and some helpful console warnings:
|
||||||
|
require('preact/debug');
|
||||||
|
|
||||||
|
// When an update to any module is received, re-import the app and trigger a full re-render:
|
||||||
|
module.hot.accept('./components/app', () => {
|
||||||
|
import('./components/app').then(({ default: App }) => {
|
||||||
|
root = render(<App />, document.body, root);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if ('serviceWorker' in navigator && location.protocol === 'https:') {
|
||||||
|
addEventListener('load', () => {
|
||||||
|
navigator.serviceWorker.register(__webpack_public_path__ + 'sw.js');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @todo SSR */
|
||||||
|
// if (typeof module==='object') {
|
||||||
|
// module.exports = app;
|
||||||
|
// }
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { Component } from 'preact';
|
|
||||||
|
|
||||||
export function updater(obj, property, value) {
|
|
||||||
return e => {
|
|
||||||
let update = {};
|
|
||||||
update[property] = typeof value === 'function' ? value(obj.state[property], e) : value;
|
|
||||||
obj.setState(update);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const toggle = value => !value;
|
|
||||||
|
|
||||||
export class When extends Component {
|
|
||||||
state = { ready: !!this.props.value };
|
|
||||||
render({ value, children: [child] }, { ready }) {
|
|
||||||
if (value && !ready) this.setState({ ready: true });
|
|
||||||
return ready ? (typeof child === 'function' ? child() : child) : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
39
src/lib/util.ts
Normal file
39
src/lib/util.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { Component, ComponentProps } from 'preact';
|
||||||
|
|
||||||
|
type WhenProps = ComponentProps<When> & {
|
||||||
|
value: boolean,
|
||||||
|
children?: (JSX.Element | (() => JSX.Element))[]
|
||||||
|
};
|
||||||
|
|
||||||
|
type WhenState = {
|
||||||
|
ready: boolean
|
||||||
|
};
|
||||||
|
|
||||||
|
export class When extends Component<WhenProps, WhenState> {
|
||||||
|
state: WhenState = {
|
||||||
|
ready: !!this.props.value
|
||||||
|
};
|
||||||
|
|
||||||
|
render({ value, children = [] }: WhenProps, { ready }: WhenState) {
|
||||||
|
let child = children[0];
|
||||||
|
if (value && !ready) this.setState({ ready: true });
|
||||||
|
return ready ? (typeof child === 'function' ? child() : child) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
descriptor.value = descriptor.value.bind(target);
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"compileOnSave": false,
|
"compileOnSave": false,
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es2017",
|
"strict": true,
|
||||||
"module": "esnext",
|
"target": "es2017",
|
||||||
"moduleResolution": "node",
|
"module": "esnext",
|
||||||
"sourceMap": true,
|
"moduleResolution": "node",
|
||||||
"jsx": "react",
|
"experimentalDecorators": true,
|
||||||
"jsxFactory": "h",
|
"noUnusedLocals": true,
|
||||||
"allowJs": true,
|
"sourceMap": true,
|
||||||
"baseUrl": ".",
|
"jsx": "react",
|
||||||
"paths": {
|
"jsxFactory": "h",
|
||||||
"async!*": ["*"]
|
"allowJs": false,
|
||||||
}
|
"baseUrl": "."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
16
tslint.json
Normal file
16
tslint.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"tslint-config-semistandard",
|
||||||
|
"tslint-react"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"quotemark": [true, "single", "jsx-double", "avoid-escape"],
|
||||||
|
"no-use-before-declare": false,
|
||||||
|
"no-floating-promises": false,
|
||||||
|
"space-before-function-paren": [true, false],
|
||||||
|
"jsx-boolean-value": [true, "never"],
|
||||||
|
"jsx-no-multiline-js": false,
|
||||||
|
"jsx-no-bind": true,
|
||||||
|
"jsx-no-lambda": true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
|
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
||||||
|
const ForkTsCheckerNotifierWebpackPlugin = require('fork-ts-checker-notifier-webpack-plugin');
|
||||||
const CleanWebpackPlugin = require('clean-webpack-plugin');
|
const CleanWebpackPlugin = require('clean-webpack-plugin');
|
||||||
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
|
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
@@ -54,6 +56,10 @@ module.exports = function(_, env) {
|
|||||||
loader: 'ts-loader',
|
loader: 'ts-loader',
|
||||||
// Don't transpile anything in node_modules:
|
// Don't transpile anything in node_modules:
|
||||||
exclude: nodeModules,
|
exclude: nodeModules,
|
||||||
|
options: {
|
||||||
|
// Offload type checking to ForkTsCheckerWebpackPlugin for better performance:
|
||||||
|
transpileOnly: true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(tsx?|jsx?)$/,
|
test: /\.(tsx?|jsx?)$/,
|
||||||
@@ -112,6 +118,15 @@ module.exports = function(_, env) {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
// Runs tslint & type checking in a worker pool
|
||||||
|
new ForkTsCheckerWebpackPlugin({
|
||||||
|
tslint: true,
|
||||||
|
// wait for type chec
|
||||||
|
async: !isProd,
|
||||||
|
formatter: 'codeframe'
|
||||||
|
}),
|
||||||
|
new ForkTsCheckerNotifierWebpackPlugin({ excludeWarnings: true }),
|
||||||
|
|
||||||
// Pretty progressbar showing build progress:
|
// Pretty progressbar showing build progress:
|
||||||
new ProgressBarPlugin({
|
new ProgressBarPlugin({
|
||||||
format: '\u001b[90m\u001b[44mBuild\u001b[49m\u001b[39m [:bar] \u001b[32m\u001b[1m:percent\u001b[22m\u001b[39m (:elapseds) \u001b[2m:msg\u001b[22m',
|
format: '\u001b[90m\u001b[44mBuild\u001b[49m\u001b[39m [:bar] \u001b[32m\u001b[1m:percent\u001b[22m\u001b[39m (:elapseds) \u001b[2m:msg\u001b[22m',
|
||||||
|
|||||||
Reference in New Issue
Block a user