From bad96c50e53ebb8a97613486fde727bc48f51e96 Mon Sep 17 00:00:00 2001 From: Ben Merckx Date: Tue, 14 May 2019 18:06:43 -0400 Subject: [PATCH 01/23] Small success, continue after next preact release --- .vscode/settings.json | 41 +- examples/src/index.ts | 219 ------ examples/src/index.tsx | 178 +++++ examples/src/views/sliderpage.ts | 69 -- examples/src/views/sliderpage.tsx | 95 +++ examples/tsconfig.json | 6 +- examples/webpack.config.js | 8 +- package.json | 16 +- src/container/autocomplete.ts | 20 +- src/container/fetcher.ts | 26 +- src/container/toggle.ts | 4 +- src/deprecated/action.ts | 60 -- src/deprecated/background.ts | 52 -- src/deprecated/form.ts | 128 ---- src/deprecated/index.ts | 7 - src/deprecated/page.ts | 34 - src/deprecated/picture.ts | 62 -- src/deprecated/router.ts | 81 --- src/deprecated/slider.less | 13 - src/deprecated/slider.ts | 26 - src/hyperscript.ts | 79 +-- src/index.ts | 4 +- src/router/{link.ts => link.tsx} | 60 +- src/router/{redirect.ts => redirect.tsx} | 9 +- src/router/route.ts | 22 - src/router/route.tsx | 30 + src/router/router.ts | 103 --- src/router/router.tsx | 103 +++ src/router/switch.ts | 27 - src/router/switch.tsx | 30 + src/styled/index.ts | 1 - src/styled/styled.ts | 96 --- src/ui/{breakpoint.ts => breakpoint.tsx} | 9 +- src/ui/{carousel.ts => carousel.tsx} | 29 +- src/ui/component.ts | 60 -- src/ui/context.ts | 49 +- src/ui/form/boxes.less | 15 - src/ui/form/boxes.ts | 64 -- src/ui/form/checkbox.less | 63 -- src/ui/form/checkbox.ts | 41 -- src/ui/form/field.less | 15 - src/ui/form/field.ts | 47 -- src/ui/form/fields.ts | 90 --- src/ui/form/index.ts | 9 - src/ui/form/input.less | 29 - src/ui/form/input.ts | 60 -- src/ui/form/radio.less | 60 -- src/ui/form/radio.ts | 53 -- src/ui/form/radios.ts | 51 -- src/ui/form/select.less | 20 - src/ui/form/select.ts | 55 -- src/ui/form/textarea.less | 34 - src/ui/form/textarea.ts | 49 -- src/ui/{icon.ts => icon.tsx} | 2 +- src/ui/{image.ts => image.tsx} | 92 ++- src/ui/index.ts | 4 - src/ui/maps/eventmanager.ts | 40 -- src/ui/maps/index.ts | 2 - src/ui/maps/mappool.ts | 21 - src/ui/maps/maps.less | 3 - src/ui/maps/maps.ts | 111 --- src/ui/maps/marker.ts | 61 -- src/ui/masonry.ts | 45 -- src/ui/masonry.tsx | 43 ++ src/ui/{mediaquery.ts => mediaquery.tsx} | 15 +- src/ui/{modal.ts => modal.tsx} | 169 ++--- src/ui/portal.ts | 20 - src/ui/view.ts | 106 +-- src/util/children.ts | 8 - src/util/formutils.ts | 32 - src/util/index.ts | 6 +- src/util/lazy.ts | 23 +- src/util/listenercache.ts | 39 -- src/util/style.ts | 34 - src/util/wrap.ts | 17 - src/util/wrap.tsx | 18 + tsconfig.json | 4 +- yarn.lock | 825 +++++++++++++++++++++-- 78 files changed, 1622 insertions(+), 2729 deletions(-) delete mode 100755 examples/src/index.ts create mode 100644 examples/src/index.tsx delete mode 100755 examples/src/views/sliderpage.ts create mode 100644 examples/src/views/sliderpage.tsx delete mode 100755 src/deprecated/action.ts delete mode 100755 src/deprecated/background.ts delete mode 100755 src/deprecated/form.ts delete mode 100755 src/deprecated/index.ts delete mode 100755 src/deprecated/page.ts delete mode 100755 src/deprecated/picture.ts delete mode 100755 src/deprecated/router.ts delete mode 100644 src/deprecated/slider.less delete mode 100644 src/deprecated/slider.ts rename src/router/{link.ts => link.tsx} (52%) mode change 100755 => 100644 rename src/router/{redirect.ts => redirect.tsx} (59%) mode change 100755 => 100644 delete mode 100755 src/router/route.ts create mode 100644 src/router/route.tsx delete mode 100755 src/router/router.ts create mode 100644 src/router/router.tsx delete mode 100755 src/router/switch.ts create mode 100644 src/router/switch.tsx delete mode 100755 src/styled/index.ts delete mode 100755 src/styled/styled.ts rename src/ui/{breakpoint.ts => breakpoint.tsx} (85%) mode change 100755 => 100644 rename src/ui/{carousel.ts => carousel.tsx} (96%) delete mode 100755 src/ui/component.ts delete mode 100644 src/ui/form/boxes.less delete mode 100755 src/ui/form/boxes.ts delete mode 100644 src/ui/form/checkbox.less delete mode 100755 src/ui/form/checkbox.ts delete mode 100644 src/ui/form/field.less delete mode 100755 src/ui/form/field.ts delete mode 100755 src/ui/form/fields.ts delete mode 100755 src/ui/form/index.ts delete mode 100644 src/ui/form/input.less delete mode 100755 src/ui/form/input.ts delete mode 100644 src/ui/form/radio.less delete mode 100755 src/ui/form/radio.ts delete mode 100755 src/ui/form/radios.ts delete mode 100644 src/ui/form/select.less delete mode 100755 src/ui/form/select.ts delete mode 100644 src/ui/form/textarea.less delete mode 100755 src/ui/form/textarea.ts rename src/ui/{icon.ts => icon.tsx} (81%) mode change 100755 => 100644 rename src/ui/{image.ts => image.tsx} (72%) mode change 100755 => 100644 delete mode 100755 src/ui/maps/eventmanager.ts delete mode 100755 src/ui/maps/index.ts delete mode 100755 src/ui/maps/mappool.ts delete mode 100755 src/ui/maps/maps.less delete mode 100755 src/ui/maps/maps.ts delete mode 100755 src/ui/maps/marker.ts delete mode 100755 src/ui/masonry.ts create mode 100644 src/ui/masonry.tsx rename src/ui/{mediaquery.ts => mediaquery.tsx} (61%) mode change 100755 => 100644 rename src/ui/{modal.ts => modal.tsx} (77%) mode change 100755 => 100644 delete mode 100755 src/ui/portal.ts delete mode 100755 src/util/children.ts delete mode 100755 src/util/formutils.ts delete mode 100755 src/util/listenercache.ts delete mode 100755 src/util/style.ts delete mode 100755 src/util/wrap.ts create mode 100644 src/util/wrap.tsx diff --git a/.vscode/settings.json b/.vscode/settings.json index 3e8e557..ad33ab6 100755 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,10 +1,33 @@ { - "editor.tabSize": 2, - "editor.insertSpaces": false, - "editor.detectIndentation": false, - "prettier.tabWidth": 2, - "prettier.useTabs": true, - "prettier.semi": false, - "prettier.bracketSpacing": false, - "prettier.singleQuote": true -} \ No newline at end of file + "editor.tabSize": 2, + "editor.insertSpaces": false, + "editor.detectIndentation": false, + "editor.formatOnSave": true, + "editor.rulers": [80], + "less.lint.emptyRules": "ignore", + "prettier.tabWidth": 2, + "prettier.useTabs": true, + "prettier.semi": false, + "prettier.bracketSpacing": false, + "prettier.singleQuote": true, + "importSorter.generalConfiguration.sortOnBeforeSave": true, + "importSorter.importStringConfiguration.hasSemicolon": false, + "importSorter.importStringConfiguration.spacingPerImportExpression.afterStartingBracket": 0, + "importSorter.importStringConfiguration.spacingPerImportExpression.beforeEndingBracket": 0, + "importSorter.sortConfiguration.customOrderingRules.rules": [ + { + "type": "importMember", + "regex": "^$", + "orderLevel": 10, + "disableSort": true + }, + { + "regex": "^[@]", + "orderLevel": 20 + } + ], + "importSorter.importStringConfiguration.maximumNumberOfImportExpressionsPerLine.count": 80, + "importSorter.importStringConfiguration.tabSize": 2, + "importSorter.importStringConfiguration.tabType": "tab", + "importSorter.importStringConfiguration.maximumNumberOfImportExpressionsPerLine.type": "newLineEachExpressionAfterCountLimit" +} diff --git a/examples/src/index.ts b/examples/src/index.ts deleted file mode 100755 index fa1b4f8..0000000 --- a/examples/src/index.ts +++ /dev/null @@ -1,219 +0,0 @@ -import './index.less' - -import { - Breakpoint, createContext, HistoryRouter, Image, ImageResizer, lazy, Link, m, Modal, - ModalOverlay, ModalStore, parseRoute, Portal, Redirect, Route, styled, Switch, View -} from '@codeurs/front' -import testImage from 'assets/test.jpg' -import svgImage from 'assets/test.svg' -import Autocomplete from 'views/autocomplete' - -const Map = lazy(() => import('./views/map')) - -const Theme = createContext('green') - -const hashMatcher = (location, route) => - parseRoute(location.hash.substr(1), route) - -const hashFormatter = path => path && `/#${path}` - -const HashRouter = ({children}) => - m(HistoryRouter, {matcher: hashMatcher, formatPath: hashFormatter}, children) - -const LanguageLink = ({lang, children}) => - m(Link, {to: `/${lang}`}, children) - -const LanguagesNav = () => - m(HashRouter, [ - m(LanguageLink, {lang: 'nl'}, 'Nederlands'), - m(LanguageLink, {lang: 'en'}, 'English'), - m(LanguageLink, {lang: 'fr'}, 'Français'), - ]) - -class ModalExample extends View { - modal = new ModalStore() - render() { - return [ - m('button', {onclick: this.modal.open}, 'Open modal'), - m(Portal, [ - m(Modal, this.modal, [ - m(ModalOverlay), - m('.modalexample', [ - 'Popup content', - m('button', {onclick: () => this.modal.close()}, 'Close popup') - ]) - ]) - ]) - ] - } -} - -const Images = ({children}) => - m('.images', - m('h3', 'Onload'), - m(Image, { - class: 'images-bg', - background: true, - onclick: () => {}, - src: `https://picsum.photos/200/${Math.random() > .5 ? '200' : '300'}`, - onload: e => console.log(e) - }), - m('h3', 'Server resize'), - m('.images-landscape', {style: {width: '400px', height: 'auto'}}, - m(ImageResizer, { - resize: (attrs, container) => { - return {...attrs} - } - }, - m(Image, svgImage) - ) - ), - m('h3', 'No crop'), - m('.images-landscape', {style: {width: '400px', height: 'auto'}}, - m(Image, svgImage) - ), - m('.images-portrait', - m(Image, testImage) - ), - m('h3', 'Cover'), - m('.images-landscape', - m(Image, { - alt: 'ok', - fit: 'cover', - src: 'https://picsum.photos/600/200' - }) - ), - m('.images-portrait', - m(Image, { - fit: 'cover', - width: 200, - height: 100, - src: 'https://picsum.photos/200/100' - }) - ), - m('.images-portrait', - m(Image, { - fit: 'cover', - position: {x: .8, y: .8}, - src: 'https://picsum.photos/800/400' - }) - ), - m('h3', 'Contain'), - m('.images-landscape', - m(Image, { - fit: 'contain', - src: 'https://picsum.photos/600/200' - }) - ), - m('.images-portrait', - m(Image, { - fit: 'contain', - src: 'https://picsum.photos/600/200' - }) - ), - m('h3', 'Fixed height'), - m('.images-fixedheight', [ - m(Image, { - fit: 'portrait', - src: 'https://picsum.photos/600/200' - }), - m(Image, { - fit: 'portrait', - src: 'https://picsum.photos/300/300' - }), - m(Image, { - fit: 'portrait', - src: 'https://picsum.photos/100/200' - }) - ]) - ) - -const Nav = styled('nav')` - background: red; -` - -const Nav2 = styled(Nav)` - color: white; -` - -const Nav3 = styled(Nav2)` - color: blue; -` - -const A = styled(Link)` - color: inherit; - &:hover { - color: yellow; - } - &.is-active { - color: gold; - } -` -const breakpoints = { - ipadPort: '(min-width: 800px)' -} - -const Slider = lazy(() => import('./views/sliderpage')) - -class Examples extends View { - render() { - return m(HashRouter, - m(Switch, [ - m(Route, { - path: '/:language', - }, ({match}) => { - const {params: {language}} = match - return m('', [ - m(HistoryRouter, { - matcher: (location, route) => - hashMatcher(location, { - ...route, - path: route.path && `/${language}${route.path}` - }), - formatPath: path => hashFormatter(`/${language}${path}`) - }, [ - m('', m(LanguagesNav)), - `Language: ${language}`, - m(Nav3, {as: 'div'}, [ - m(A, {class: 'home', to: '/', exact: true}, 'Home'), - m(A, {to: '/slider'}, 'Slider'), - m(A, {to: '/autocomplete'}, 'Autocomplete'), - m(A, {to: '/modal'}, 'Modal'), - m(A, {to: '/images'}, 'Images'), - m(A, {to: '/maps'}, 'Maps'), - m(A, {to: '/back'}, 'Back home') - ]), - m(Switch, [ - m(Route, {path: '/slider'}, Slider), - m(Route, {path: '/autocomplete'}, Autocomplete), - m(Route, {path: '/images'}, Images), - m(Route, {path: '/back'}, - () => m('', [ - 'Back home', - m(Redirect, {to: '/'}) - ]) - ), - m(Route, {path: '/modal'}, ModalExample), - m(Route, {path: '/maps'}, Map), - m(Route, () => [ - m(Breakpoint, { - '(max-width: 800px)': {cols: 3}, - '(min-width: 800px)': {cols: 5}, - '(min-width: 1000px)': {cols: 10} - }, ({cols}) => m('', 'cols: '+cols)) - ]) - ]) - ]) - ]) - } - ), - m(Route, () => m('', - 'Choose a language:', - m(LanguagesNav) - )) - ]) - ) - } -} - -m.mount(document.getElementById('root'), Examples) \ No newline at end of file diff --git a/examples/src/index.tsx b/examples/src/index.tsx new file mode 100644 index 0000000..af2c2c8 --- /dev/null +++ b/examples/src/index.tsx @@ -0,0 +1,178 @@ +import './index.less' + +import { + Breakpoint, + createContext, + HistoryRouter, + Image, + ImageResizer, + Link, + m, + Modal, + ModalStore, + parseRoute, + Redirect, + Route, + Switch, + View +} from '@codeurs/front' +import testImage from 'assets/test.jpg' +import svgImage from 'assets/test.svg' +import {render} from 'preact' +import Slider from './views/sliderpage' + +//import Autocomplete from 'views/autocomplete' +//const Map = lazy(() => import('./views/map')) + +const Theme = createContext('green') + +const hashMatcher = (location, route) => + parseRoute(location.hash.substr(1), route) + +const hashFormatter = path => path && `/#${path}` + +const HashRouter = ({children}) => ( + + {children} + +) + +const LanguageLink = ({lang, children}) => ( + {children} +) + +const LanguagesNav = () => ( + + Nederlands + English + Français + +) + +class ModalExample extends View { + modal = new ModalStore() + render() { + return ( +
+ + +
+ Popup contant + +
+
+
+ ) + } +} + +const Images = ({children}) => ( +
+

Onload

+ {}} + src={`https://picsum.photos/200/${Math.random() > 0.5 ? '200' : '300'}`} + onload={console.log} + /> + +

Server resize

+
+ { + return {...attrs} + }} + > + + +
+
+) + +const breakpoints = { + ipadPort: '(min-width: 800px)' +} + +class Examples extends View { + render() { + return ( + + + + {({match}) => { + const { + params: {language} + } = match + return ( +
+ + hashMatcher(location, { + ...route, + path: route.path && `/${language}${route.path}` + }) + } + formatPath={path => hashFormatter(`/${language}${path}`)} + > + + {`Language: ${language}`} +
+ + Home + + Slider + Autocomplete + Modal + Images + Maps + Back home +
+ + {Slider} + {() => 'autocomplete'} + {Images} + + {() => ( +
+ Back home + +
+ )} +
+ {ModalExample} + {Map} + + {() => ( + + {({cols}) => 'cols: ' + cols} + + )} + +
+
+
+ ) + }} +
+ + {() => ( +
+ Choose a language: + +
+ )} +
+
+
+ ) + } +} + +render(, document.getElementById('root')) diff --git a/examples/src/views/sliderpage.ts b/examples/src/views/sliderpage.ts deleted file mode 100755 index 81bdaea..0000000 --- a/examples/src/views/sliderpage.ts +++ /dev/null @@ -1,69 +0,0 @@ -import './sliderpage.less' - -import { - Carousel, CarouselStore, m, Slider, SliderStore, View -} from '@codeurs/front' - -export default class SliderPage extends View { - snap: 'pages' | 'elements' = 'pages' - width = 1 - slider = new SliderStore() - carousel = new CarouselStore() - - render() { - return m('div', [ - m('h1', '@codeurs/front'), - m('input[type=range]', { - value: this.width, - oninput: e => this.width = parseFloat(e.target.value), - min: 0, - max: 1, - step: .01 - }), - m('button', { - onclick: () => this.snap = this.snap == 'pages' ? 'elements': 'pages' - }, `Snap to ${this.snap == 'pages' ? 'elements': 'pages'}`), - m('.sliderpage', - m(Carousel, {...this.carousel, snapTo: this.snap}, - [1, 2, 3, 4, 5, 6, 7].map((slide, i) => - m('.sliderpage-slide', { - //onclick: () => alert('ok'), - className: this.carousel.isActive(i) && 'is-active', - style: {'min-width': this.width * 100 + '%'} - }, [ - slide, - m('a.sliderpage-slide-link', { - onclick: () => { - this.carousel.goNext() - } - }, 'next >>'), - m('a.sliderpage-slide-link', { - href: 'https://codeurs.be', - target: '_blank' - }, 'external') - ]) - ) - ), - m(Carousel, - [1, 2, 3, 4, 5, 6, 7].map((slide, i) => - m('.sliderpage-slide', { - className: 'is-active', - style: {'min-width': '33.33333%'} - }, [ - slide, - m('a.sliderpage-slide-link', { - onclick: () => { - this.carousel.goNext() - } - }, 'next >>'), - m('a.sliderpage-slide-link', { - href: 'https://codeurs.be', - target: '_blank' - }, 'external') - ]) - ) - ) - ) - ]) - } -} \ No newline at end of file diff --git a/examples/src/views/sliderpage.tsx b/examples/src/views/sliderpage.tsx new file mode 100644 index 0000000..a819264 --- /dev/null +++ b/examples/src/views/sliderpage.tsx @@ -0,0 +1,95 @@ +import './sliderpage.less' + +import {Carousel, CarouselStore, m, View} from '@codeurs/front' + +export default class SliderPage extends View { + carousel = new CarouselStore() + state = { + snap: 'pages' as const, + width: 1 + } + + render() { + return ( +
+

@codeurs/front

+ + this.setState({ + width: parseFloat((e.target as HTMLInputElement).value) + }) + } + min={0} + max={1} + step={0.01} + /> + +
+ + {[1, 2, 3, 4, 5, 6, 7].map((slide, i) => ( + + ))} + + + {...[1, 2, 3, 4, 5, 6, 7].map((slide, i) => ( + + ))} + +
+
+ ) + } +} diff --git a/examples/tsconfig.json b/examples/tsconfig.json index 30da950..f6d62a4 100755 --- a/examples/tsconfig.json +++ b/examples/tsconfig.json @@ -16,11 +16,11 @@ "outDir": "./dist/", "emitDecoratorMetadata": true, "experimentalDecorators": true, - "resolveJsonModule": true, + "resolveJsonModule": true, "downlevelIteration": true, "jsx": "react", "jsxFactory": "m", - "lib": ["es2017", "dom"] + "lib": ["es2017", "dom"] }, - "include": ["./src/**/*"] + "files": ["./src/custom.d.ts", "./src/index.tsx"] } diff --git a/examples/webpack.config.js b/examples/webpack.config.js index 1d689ae..dbedb95 100755 --- a/examples/webpack.config.js +++ b/examples/webpack.config.js @@ -1,7 +1,5 @@ const path = require('path') -module.exports = require('@codeurs/packer')( - 'src/index.ts', 'dist/bundle.js', { - include: [path.resolve('../node_modules/@codeurs/front/dist')] - } -) \ No newline at end of file +module.exports = require('@codeurs/packer')('src/index.tsx', 'dist/bundle.js', { + include: [path.resolve('../node_modules/@codeurs/front/dist')] +}) diff --git a/package.json b/package.json index 9f71b88..c1a8d6a 100755 --- a/package.json +++ b/package.json @@ -17,6 +17,8 @@ "license": "MIT", "scripts": { "dev": "run-s build less && run-p less watch examples:watch", + "serve": "run-p dev client:serve", + "client:serve": "cd examples/dist && wright index.html", "build": "tsc", "watch": "tsc --pretty --preserveWatchOutput -w", "examples:watch": "cd examples && webpack --mode development -w", @@ -27,7 +29,8 @@ "pretty:format": "prettier --print-width 80 --single-quote --write --no-semi --no-bracket-spacing --parser typescript --tab-width 2 --use-tabs", "pretty:hyperscript": "perl -i -0777pe 's/\\bm\\(\\s+/m(/gs'", "prepare": "symlink-dir . node_modules/@codeurs/front", - "test": "jest" + "test": "jest", + "tojsx": "codemod --plugin ./node_modules/babel-plugin-hyperscript-to-jsx/src/index.js" }, "husky": { "hooks": { @@ -71,16 +74,17 @@ "@types/googlemaps": "^3.30.16", "@types/jump.js": "^1.0.2", "@types/load-google-maps-api": "^1.3.0", - "@types/mithril": "https://codeload.github.com/codeurs/mithril.d.ts/tar.gz/911e3bb343b77dc6839debc911e9829de2cefbcf", + "@types/mithril": "^1.1.16", "@types/throttle-debounce": "^1.1.1", "classnames": "^2.2.5", "core-js": "^3.0.1", "deep-equal": "^1.0.1", "jump.js": "codeurs/jump.js", "load-google-maps-api": "^1.3.2", - "mithril": "2.0.0-rc.4", + "mithril": "^1.1.6", "object-to-formdata": "codeurs/object-to-formdata", "popmotion": "^8.6.4", + "preact": "^10.0.0-beta.1", "scroll-into-view-if-needed": "^2.2.20", "throttle-debounce": "^2.1.0", "tslib": "^1.9.3" @@ -88,6 +92,9 @@ "devDependencies": { "@codeurs/packer": "=0.14.1", "@types/jest": "^24.0.9", + "babel-codemod": "^2.1.2", + "babel-core": "^6.26.3", + "babel-plugin-hyperscript-to-jsx": "codeurs/babel-plugin-hyperscript-to-jsx", "copyfiles": "^2.0.0", "cross-env": "^5.2.0", "husky": "^1.2.0", @@ -99,6 +106,7 @@ "serve": "^10.1.2", "style-loader": "^0.23.1", "symlink-dir": "^2.0.2", - "ts-jest": "^24.0.0" + "ts-jest": "^24.0.0", + "wright": "^2.0.0-beta35" } } diff --git a/src/container/autocomplete.ts b/src/container/autocomplete.ts index dd8b22e..011bfd0 100755 --- a/src/container/autocomplete.ts +++ b/src/container/autocomplete.ts @@ -1,6 +1,5 @@ -import {Children, VnodeDOM} from 'mithril' -import scrollIntoView from 'scroll-into-view-if-needed' -import {DOMAttrs, m} from '../hyperscript' +import {ComponentChildren} from 'preact' +import {DOMAttrs} from '../hyperscript' import { AutocompleteAction, AutocompleteChange, @@ -19,7 +18,7 @@ export type AutocompleteApi = AutocompleteState & { export type AutocompleteRenderApi = ( api: AutocompleteApi -) => Children +) => ComponentChildren type ItemAttrs = { item: Item @@ -56,7 +55,7 @@ export class Autocomplete extends View< > { static instanceCount = 0 - state: AutocompleteStore = new AutocompleteStore() + defaultStore: AutocompleteStore = new AutocompleteStore() private items: Array = [] private id = `autocomplete-${Autocomplete.instanceCount++}` @@ -70,7 +69,7 @@ export class Autocomplete extends View< private interacting = false get store() { - return this.attrs.store || this.state + return this.attrs.store || this.defaultStore } onCreate(dom: HTMLElement) { @@ -112,8 +111,7 @@ export class Autocomplete extends View< }) const {selectedItem} = this.store if (wasSelected != selectedItem) if (onselect) onselect(selectedItem) - if (async) m.redraw() - else (m.redraw as any).sync() + this.redraw() } // -- Input ------------------- @@ -173,12 +171,14 @@ export class Autocomplete extends View< onkeydown: callHandlers(onkeydown, this.keyDown) } const selected = highlightedIndex === index - const scrollTo = selected && { + const scrollTo = {} + /*const scrollTo = selected && { onupdate: (vnode: VnodeDOM) => { if (this.avoidScrolling || !this.state.isOpen) return scrollIntoView(vnode.dom, {behavior: 'smooth', scrollMode: 'if-needed'}) } - } + }*/ + // todo: fix scrollto return { id: this.getItemId(index), role: 'option', diff --git a/src/container/fetcher.ts b/src/container/fetcher.ts index 2ca22ff..99c0473 100755 --- a/src/container/fetcher.ts +++ b/src/container/fetcher.ts @@ -1,5 +1,7 @@ import deepEqual from 'deep-equal' -import m, {Children, RequestOptions} from 'mithril' +import {RequestOptions} from 'mithril' +import requestService from 'mithril/request' +import {ComponentChildren} from 'preact' import {View} from '../ui/view' export type FetcherState = { @@ -8,7 +10,7 @@ export type FetcherState = { error?: Error } -export type FetcherRender = (state: FetcherState) => Children +export type FetcherRender = (state: FetcherState) => ComponentChildren type RequestData = { url: string @@ -36,24 +38,26 @@ export class Fetcher extends View< setCache = (cache: any) => (this.cache = cache), children, hydrate, - ...request + ...req } = this.attrs if (hydrate && !this.hydrated) { this.data = hydrate this.hydrated = true - setCache(request) + setCache(req) } - if (deepEqual(cache, request)) return - setCache(request) + if (deepEqual(cache, req)) return + setCache(req) this.isLoading = true this.onRemove() - m.request({ - ...request, - config: xhr => (this.transport = xhr) - }) - .then(data => (this.data = data)) + requestService + .request({ + ...req, + config: xhr => (this.transport = xhr) + }) + .then(data => (this.data = data as T)) .catch(e => console.error((this.error = e))) .then(() => (this.isLoading = false)) + .then(this.redraw) } onRemove() { diff --git a/src/container/toggle.ts b/src/container/toggle.ts index 80e9866..006f4bd 100755 --- a/src/container/toggle.ts +++ b/src/container/toggle.ts @@ -1,4 +1,4 @@ -import {Children} from 'mithril' +import {ComponentChildren} from 'preact' import {View} from '../ui/view' export class Toggle extends View<{ @@ -7,7 +7,7 @@ export class Toggle extends View<{ value: boolean toggle: () => boolean set: (value: boolean) => boolean - }) => Children + }) => ComponentChildren }> { value = !!this.attrs.defaultValue render() { diff --git a/src/deprecated/action.ts b/src/deprecated/action.ts deleted file mode 100755 index 4b5ab76..0000000 --- a/src/deprecated/action.ts +++ /dev/null @@ -1,60 +0,0 @@ -function trimSlashes(str: string) { - return str.replace(/^\/|\/$/g, '') -} - -function startsWith(a: string, b: string) { - return a.substr(0, b.length) == b -} - -export const action = Object.assign( - ( - data: string | {url: string; target?: string}, - cb: null | Function = null, - replace: boolean = false - ): any => { - if (typeof data == 'string') return action({url: data}, cb, replace) - if (!data || !data.url) return {} - const {url, target} = data - if (url.indexOf('mailto:') === 0) return {href: url} - if (url.indexOf('@') > -1) return {href: 'mailto:' + url} - if (url.indexOf('.') > -1 || url.indexOf('://') > -1) - return { - href: url, - target: target || '_blank', - rel: 'external noopener', - onclick: cb - } - else - return { - href: url, - target, - onclick: (e: MouseEvent) => { - if (cb) cb() - action.anchorClick(e, url, replace) - } - } - }, - { - isActive(data: any, exact = false): boolean { - if (typeof data == 'string') return action.isActive({url: data}, exact) - const {pathname} = window.location - const path = trimSlashes(pathname) - const url = trimSlashes(data.url) - if (exact) return path == url - return startsWith(path, url) - }, - anchorClick(e: MouseEvent, href: string, replace = false) { - if (e.which == 2) return - e.preventDefault() - action.navigate(href, replace) - }, - navigate(url: string | {url: string}, replace = false): void { - if (!url) return - if (typeof url != 'string' && 'url' in url) - return action.navigate(url.url, replace) - if (replace) history.replaceState(null, '', url) - else history.pushState(null, '', url) - window.onpopstate && window.onpopstate({state: null} as any) - } - } -) diff --git a/src/deprecated/background.ts b/src/deprecated/background.ts deleted file mode 100755 index f444e6d..0000000 --- a/src/deprecated/background.ts +++ /dev/null @@ -1,52 +0,0 @@ -import {DOMAttrs} from 'hyperscript' -import m from 'mithril' -import {Image, ImageResizer} from '../ui/image' -import {View} from '../ui/view' -import {classes} from '../util/classes' -import {getResizedUrl} from './picture' - -export type Img = { - src: string - empty?: boolean - focus?: {x: number; y: number} -} - -export class Background extends View< - { - img: string | Img - class?: string - } & DOMAttrs, - HTMLDivElement -> { - render() { - const {children, img, class: className, ...rest} = this.attrs - if (img && typeof img == 'object' && img.empty) return - const src = typeof img === 'string' ? img : img.src - const position = (typeof img == 'object' && img.focus) || 'center center' - return m(ImageResizer, - { - resize: ( - attrs: {src: string} & DOMAttrs, - container: {width: number; height: number} - ) => { - const {src} = attrs - if (src.indexOf('://') > -1) return attrs - const url = getResizedUrl(src, container.width, container.height) - return {...attrs, src: url} - } - }, - m(Image, - { - src, - background: true, - fit: 'cover', - position, - upScale: true, - ...classes('background', className), - ...rest - }, - children - ) - ) - } -} diff --git a/src/deprecated/form.ts b/src/deprecated/form.ts deleted file mode 100755 index 7d66857..0000000 --- a/src/deprecated/form.ts +++ /dev/null @@ -1,128 +0,0 @@ -import m, {RequestOptions} from 'mithril' -import objectToFormData from 'object-to-formdata' -import {FormStatus, FormStore} from '../store/formstore' -import { - Boxes, - Checkbox, - Fields, - Input, - Radios, - Select, - Textarea -} from '../ui/form/fields' - -export class FormBase { - constructor(protected store: FormStore, protected fields: Fields) {} - - status() { - return this.store.status.type - } - - isCompleted() { - return this.status() == FormStatus.Success - } - - formSubmit(e: Event, options = {}) { - e.preventDefault() - const form = e.target as HTMLFormElement - const {action: url, method} = form - const type = form.getAttribute('enctype') || 'application/json' - return this.submit(type, {url, method, ...options}) - } - - submit(type: string, options: {url: string} & RequestOptions) { - const {url, method, headers = {}, ...rest} = options - switch (this.store.status.type) { - case 'sending': - case 'success': - return Promise.resolve() - default: - return this.transfer({ - url, - method, - data: this.formatData(type), - headers: - type.indexOf('multipart') === 0 - ? headers - : {'Content-Type': type, ...headers}, - serialize: v => v, - ...rest - }).then( - response => Promise.resolve(this.store.success(response)), - errors => Promise.reject(this.store.fail(errors)) - ) - } - } - - formatData(type: string) { - switch (type) { - case 'application/x-www-form-urlencoded': - return m.buildQueryString(this.store.data) - case 'multipart/form-data': - return objectToFormData(this.store.data) - case 'application/json': - return JSON.stringify(this.store.data) - } - } - - transfer(request: RequestOptions & {url: string}) { - return m.request({ - ...request, - config: (xhr: XMLHttpRequest) => { - this.store.send(xhr) - } - }) - } - - text = (config: any) => this.fields.asField(Input, {...config}) - email = (config: any) => this.text({...config, type: 'email'}) - password = (config: any) => this.text({...config, type: 'password'}) - textarea = (config: any) => this.fields.asField(Textarea, {...config}) - select = (config: any) => this.fields.asField(Select, config) - radio = (config: any) => this.fields.asField(Radios, config) - checkbox = (config: any) => this.fields.asField(Checkbox, config) - boxes = (config: any) => this.fields.asField(Boxes, config) -} - -/** - * Als instance klasse van de view waar je de form wilt gebruiken maak je een nieuw Form aan. - * Het onsubmit event van je html formulier koppel je door aan de submit methode van dit form object. - * Velden toevoegen aan je html formulier is gemakkelijk via de shortcurts die je vind op dit form object. - * Wanneer het onsucces event wordt aangeroepen weet je dat het formulier succesvol verwerkt is door de server. - * - * Eigen custom velden maken en toevoegen kan door de attrs methode te gebruiken (zie implementatie shortcuts). - * Een eigen veld moet ten minste de attributes 'value' en 'onchange' ondersteunen om te functioneren. - * - * Langs de serverkant moet je een route voorzien die de data verwerkt. - * Deze stuurt een json bericht terug van volgende type: - * { - * "succes": true | false - * "errors": { ... } // Alleen als succes false is - * "data": { ... } // Alleen als succes true is (wordt meegegeven als argument aan de onsucces) - * } - * - * TODO: - * - Gebruik de setCustomValidity methode om de in-browser validatie toe te passen op elementen met een error - * - Select vervangen door geheel eigen implementatie (die makkelijker te stylen is als de standaard html select) - * - Een flexibel en veilig wysiwyg-editor veld toevoegen (bv. tinymce) - * - Basis styling van bestaande elementen robuuster en flexibeler maken - */ -export class Form extends FormBase { - constructor({data = {}, ...config} = {}) { - const store = new FormStore(data) - const fields = new Fields(store, { - defaultUnstyled: true, - labelInFields: false, - defaultRequired: false, - ...config - }) - super(store, fields) - } - - formSubmit(e: Event, options?: any) { - return super.formSubmit(e, options).catch(errors => { - const [firstKey] = Object.keys(errors) - if (firstKey) this.fields.focusField(firstKey) - }) - } -} diff --git a/src/deprecated/index.ts b/src/deprecated/index.ts deleted file mode 100755 index a0e820d..0000000 --- a/src/deprecated/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export * from './action' -export * from './background' -export * from './form' -export * from './page' -export * from './picture' -export * from './router' -export * from './slider' diff --git a/src/deprecated/page.ts b/src/deprecated/page.ts deleted file mode 100755 index 0d378f3..0000000 --- a/src/deprecated/page.ts +++ /dev/null @@ -1,34 +0,0 @@ -import m, {CVnode} from 'mithril' -import {Component} from '../ui/component' - -type Route = { - href: string - path: string - params: {} -} - -export class Page extends Component<{route: Route} & T> { - currentRoute: Route - - constructor(vnode: CVnode<{route: Route} & T>) { - super(vnode) - this.currentRoute = this.attrs.route - } - - oninit(vnode: CVnode<{route: Route} & T>) { - super.oninit(vnode) - this.onroutechange() - } - - onafterupdate() { - const last = this.currentRoute.href - this.currentRoute = this.attrs.route - if (last !== this.currentRoute.href) this.onroutechange() - } - - onroutechange() {} - - static render(attrs: any) { - return m(this, attrs) - } -} diff --git a/src/deprecated/picture.ts b/src/deprecated/picture.ts deleted file mode 100755 index 7ef681b..0000000 --- a/src/deprecated/picture.ts +++ /dev/null @@ -1,62 +0,0 @@ -import {DOMAttrs} from 'hyperscript' -import m from 'mithril' -import {Image, ImageResizer} from '../ui/image' -import {View} from '../ui/view' -import {classes} from '../util/classes' - -const WIDTHS = [100, 200, 400, 600, 800] -const HEIGHTS = [100, 200, 400, 600, 800] - -export function getResizedUrl(url: string, width: number, height: number) { - let w = WIDTHS.find(w => w > width) - let h = HEIGHTS.find(h => h > height) - if (!w) w = WIDTHS[WIDTHS.length - 1] - if (!h) h = HEIGHTS[HEIGHTS.length - 1] - return `/cache/${w}/${h}${url}` -} - -export class Picture extends View< - { - src: string - empty?: boolean - width?: number - height?: number - inline?: boolean - max?: number - [key: string]: any - }, - HTMLDivElement -> { - render() { - const { - children, - inline, - class: className, - max, - empty, - focus: position, - ...rest - } = this.attrs - if (empty) return - return m(ImageResizer, - { - resize: ( - attrs: {src: string} & DOMAttrs, - container: {width: number; height: number} - ) => { - const {src} = attrs - if (src.indexOf('://') > -1) return attrs - const url = getResizedUrl(src, container.width, Infinity) - return {...attrs, src: url} - } - }, - m(Image, { - upScale: true, - position, - style: {display: inline && 'inline'}, - ...classes('picture', className), - ...rest - }) - ) - } -} diff --git a/src/deprecated/router.ts b/src/deprecated/router.ts deleted file mode 100755 index 1813e57..0000000 --- a/src/deprecated/router.ts +++ /dev/null @@ -1,81 +0,0 @@ -import jump from 'jump.js' -import m from 'mithril' - -export class Router { - transport: null | XMLHttpRequest = null - data: any = {} - - constructor(initialState: any) { - this.data = initialState - } - - url() { - return this.data.url - } - - view(vnode: any) { - const {href} = window.location - const params = - href.indexOf('?') > -1 ? m.parseQueryString(href.split('?')[1]) : {} - const route = {href, path: window.location.pathname, params} - return this.resolve(this.data, route) - } - - mount(element: HTMLElement) { - window.onpopstate = e => { - if (e.state) { - this.setData(e.state.data) - m.redraw() - } else { - this.navigate(window.location.pathname) - } - } - m.mount(element, this) - } - - getPageTitle(data: any) { - return data.title - } - - setData(data: any) { - this.data = data - const {hash, href} = window.location - const queryIndex = href.indexOf('?') - const query = queryIndex > -1 ? href.substr(queryIndex) : '' - window.history.replaceState({data}, this.getPageTitle(data)) - document.title = this.getPageTitle(data) - } - - clear() { - if (!this.transport) return - this.transport.abort() - this.transport = null - } - - scroll(hash?: string) { - if (hash) jump(hash) - else window.scrollTo(0, 0) - } - - fetch(path: string) { - return Promise.reject('implement') - } - - resolve(data: any, route: any) { - throw 'implement' - } - - navigate(path: string) { - const {hash} = window.location - if (path == this.url()) { - if (hash) this.scroll(hash) - } else { - this.clear() - return this.fetch(path).then(data => { - this.setData(data) - setTimeout(() => this.scroll(hash)) - return data - }) - } - } -} diff --git a/src/deprecated/slider.less b/src/deprecated/slider.less deleted file mode 100644 index b4a80c3..0000000 --- a/src/deprecated/slider.less +++ /dev/null @@ -1,13 +0,0 @@ -.slider { - height: 100%; - - .carousel-content { - display: block; - height: 100%; - white-space: nowrap; - - > * { - white-space: normal; - } - } -} \ No newline at end of file diff --git a/src/deprecated/slider.ts b/src/deprecated/slider.ts deleted file mode 100644 index bb9dd45..0000000 --- a/src/deprecated/slider.ts +++ /dev/null @@ -1,26 +0,0 @@ -import './slider.less' - -import {CarouselStore} from '../store/carouselstore' - -export {Carousel as Slider} from '../ui/carousel' - -// Backwards compatibility -export class SliderStore extends CarouselStore { - className = 'slider' - - index() { - if (!this.carousel) return this.initialPage - return this.activePage - } - - total() { - return this.totalPages - } - - actives() { - if (!this.carousel) return [] - return this.carousel.snaps.elements.map((_, i) => () => this.isActive(i)) - } - - //animating = stream(false) -} diff --git a/src/hyperscript.ts b/src/hyperscript.ts index e163a47..88f0718 100755 --- a/src/hyperscript.ts +++ b/src/hyperscript.ts @@ -1,78 +1,9 @@ -import 'core-js/features/object/assign' +import {ComponentChildren, h} from 'preact' -import hyperscript, {Attributes, Child, Children, CVnode} from 'mithril' -import {Component, View} from 'ui' -import {extractChildren} from './util/children' - -export type ChildAttr = {children: T} | {children?: T} - -const mithrilStatic = {...hyperscript} - -export type ClassComponents = - | {new (vnode: CVnode): View} - | {new (vnode: CVnode): Component} - -export type ComponentConstructors = - | {(attrs: Attrs): Children} - | ClassComponents - -type Without = Pick> -type AttributesArgument = Without & {key?: any} -type OptionalAttrs = {} extends Attrs - ? {} - : {children: any} extends Attrs - ? {} - : never -export type ChildrenType = ({children?: Children} & Attrs)['children'] - -type ExtendedHyperscript = { - (selector: string, ...children: Children[]): Children - (selector: string, attributes: Attributes, ...children: Children[]): Children - >( - component: ComponentConstructors, - ...args: Array> - ): Children - ( - component: ComponentConstructors, - attributes: AttributesArgument, - ...args: Array> - ): Children -} & typeof mithrilStatic - -const isPlainFunction = (input: Function) => - typeof input === 'function' && typeof input.prototype.view !== 'function' - -export type Attrs = Partial & { - [key: string]: any -} +export {h as m} from 'preact' export type DOMAttrs = {[key: string]: any} -export const m: ExtendedHyperscript = Object.assign( - (selector: any, attrs: any, ...children: any) => { - const makeChildren = (children: any) => - !children || children.length === 0 - ? {} - : {children: extractChildren(children)} - if (isPlainFunction(selector)) { - if ( - attrs && - (typeof attrs !== 'object' || attrs.tag != null || Array.isArray(attrs)) - ) - return selector(makeChildren([].concat(attrs).concat(children))) - return selector({...attrs, ...makeChildren(children)}) - } - if ( - typeof selector === 'string' && - attrs && - !Array.isArray(attrs) && - typeof attrs === 'object' && - !attrs.tag - ) { - const {children: childrenAttr, ...rest} = attrs - return hyperscript(selector, rest, childrenAttr, ...children) - } - return hyperscript(selector, attrs, ...children) - }, - hyperscript -) +export type ChildrenType = A extends {children?: any} + ? A['children'] + : ComponentChildren diff --git a/src/index.ts b/src/index.ts index ba62d7e..cc6357a 100755 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,6 @@ export * from './container' -export * from './hyperscript' export * from './router' export * from './store' export * from './ui' -export * from './styled' export * from './util' -export * from './deprecated' +export * from './hyperscript' diff --git a/src/router/link.ts b/src/router/link.tsx old mode 100755 new mode 100644 similarity index 52% rename from src/router/link.ts rename to src/router/link.tsx index 4630c80..6c75769 --- a/src/router/link.ts +++ b/src/router/link.tsx @@ -7,37 +7,41 @@ import {Location, RouterContext} from './router' export class Link extends View<{ to: string target?: string - onclick?: Function + onClick?: Function [key: string]: any }> { render() { - const {to, target, download, onclick, children, ...attrs} = this.attrs - return m(Location, (location: RouterContext) => { - const href = location.formatPath(to) - const active = !!location.match({path: to, exact: attrs.exact}) - const anchorAttrs = addClasses( - { - ...attrs, - href, - target, - download, - onclick: (e: MouseEvent & {redraw: boolean}) => { - e.redraw = false - if (onclick) onclick(e) - const ignore = - download || - target === '_blank' || - ignoreClick(e) || - isExternal(location, e.currentTarget as HTMLAnchorElement) - if (ignore) return - e.preventDefault() - location.push(href) - } - }, - {is: {active}} - ) - return m('a', anchorAttrs, children) - }) + const {to, target, download, onClick, children, ...attrs} = this.attrs + + return ( + + {(location: RouterContext) => { + const href = location.formatPath(to) + const active = !!location.match({path: to, exact: attrs.exact}) + const anchorAttrs = addClasses( + { + ...attrs, + href, + target, + download, + onClick: (e: MouseEvent) => { + if (onClick) onClick(e) + const ignore = + download || + target === '_blank' || + ignoreClick(e) || + isExternal(location, e.currentTarget as HTMLAnchorElement) + if (ignore) return + e.preventDefault() + location.push(href) + } + }, + {is: {active}} + ) + return {children} + }} + + ) } } diff --git a/src/router/redirect.ts b/src/router/redirect.tsx old mode 100755 new mode 100644 similarity index 59% rename from src/router/redirect.ts rename to src/router/redirect.tsx index e7995d9..61ad192 --- a/src/router/redirect.ts +++ b/src/router/redirect.tsx @@ -7,8 +7,13 @@ export class Redirect extends View<{ }> { render() { const {to} = this.attrs - return m(Location, (location: RouterContext) => - location.replace(location.formatPath(to)) + return ( + + {(location: RouterContext) => { + location.replace(location.formatPath(to)) + return null + }} + ) } } diff --git a/src/router/route.ts b/src/router/route.ts deleted file mode 100755 index de9db57..0000000 --- a/src/router/route.ts +++ /dev/null @@ -1,22 +0,0 @@ -import {ComponentConstructors, m} from '../hyperscript' -import {View} from '../ui/view' -import {Match} from './parseroute' -import {Location, RouteInfo, RouterContext} from './router' - -export type RouteAttrs = RouteInfo & { - children: ComponentConstructors<{ - match?: Match - location?: RouterContext - [key: string]: any - }> -} - -export class Route extends View { - render() { - const {children} = this.attrs - return m(Location, (location: RouterContext) => { - const match = location.match(this.attrs) - return match && children && m(children, {match, location}) - }) - } -} diff --git a/src/router/route.tsx b/src/router/route.tsx new file mode 100644 index 0000000..63ac722 --- /dev/null +++ b/src/router/route.tsx @@ -0,0 +1,30 @@ +import {AnyComponent, ComponentChildren} from 'preact' +import {m} from '../hyperscript' +import {View} from '../ui/view' +import {Match} from './parseroute' +import {Location, RouteInfo, RouterContext} from './router' + +type RouteProps = { + match?: Match + location?: RouterContext + [key: string]: any +} + +export type RouteAttrs = RouteInfo & { + children: any // Todo: type as component constructor and pass jsx +} + +export class Route extends View { + render() { + const {children} = this.attrs + return ( + + {(location: RouterContext) => { + const match = location.match(this.attrs) + const View = children as any + return match && View && + }} + + ) + } +} diff --git a/src/router/router.ts b/src/router/router.ts deleted file mode 100755 index b090a85..0000000 --- a/src/router/router.ts +++ /dev/null @@ -1,103 +0,0 @@ -import {Children} from 'mithril' -import {m} from '../hyperscript' -import {createContext} from '../ui/context' -import {StatelessView, View} from '../ui/view' -import {Match, parseRoute} from './parseroute' -import {RouteAttrs} from './route' - -export type LocationData = { - protocol: string - hostname: string - port: string - search: string - hash: string - pathname: string -} - -export type History = { - replace: (to: string) => void - push: (href: string) => void -} - -export type RouteInfo = { - path?: string - exact?: boolean -} - -export type Matcher = ( - location: LocationData, - route: RouteInfo -) => undefined | Match - -export type RouterContext = LocationData & - History & - RouterAttrs & { - match: (route: RouteInfo) => Match - formatPath: (path: string) => string - } - -const {Provider, Consumer} = createContext() - -export const Location: StatelessView<{ - children: (location: RouterContext) => Children -}> = ({children}) => - m(Consumer, - (location: undefined | RouterContext) => location && children(location) - ) - -const pathnameMatcher: Matcher = (location, route) => - parseRoute(location.pathname, route) - -type RouterAttrs = { - matcher?: Matcher - formatPath?: (path: string) => string -} - -export class Router extends View { - static instances = 0 - - onCreate() { - if (!Router.instances++) { - window.addEventListener('popstate', m.redraw) - window.addEventListener('hashchange', m.redraw) - } - this.onRemove = () => { - if (!--Router.instances) { - window.removeEventListener('popstate', m.redraw) - window.removeEventListener('hashchange', m.redraw) - } - } - } - - render() { - const { - matcher = pathnameMatcher, - formatPath = (v: string) => v - } = this.attrs - const {location, history} = window - const {protocol, hostname, port, search, hash, pathname} = location - return m(Provider, - { - value: { - protocol, - hostname, - port, - search, - hash, - pathname, - replace: (to: string) => { - history.replaceState(pathname, '', to) - m.redraw() - }, - push: (href: string) => { - history.pushState(href, '', href) - m.redraw() - }, - match: (route: RouteAttrs) => matcher(location, route), - formatPath - } as RouterContext - }, - this.children - ) - } -} diff --git a/src/router/router.tsx b/src/router/router.tsx new file mode 100644 index 0000000..c4dcec5 --- /dev/null +++ b/src/router/router.tsx @@ -0,0 +1,103 @@ +import {ComponentChildren, FunctionComponent} from 'preact' +import {m} from '../hyperscript' +import {createContext} from '../ui/context' +import {View} from '../ui/view' +import {Match, parseRoute} from './parseroute' +import {RouteAttrs} from './route' + +export type LocationData = { + protocol: string + hostname: string + port: string + search: string + hash: string + pathname: string +} + +export type History = { + replace: (to: string) => void + push: (href: string) => void +} + +export type RouteInfo = { + path?: string + exact?: boolean +} + +export type Matcher = ( + location: LocationData, + route: RouteInfo +) => undefined | Match + +export type RouterContext = LocationData & + History & + RouterAttrs & { + match: (route: RouteInfo) => Match + formatPath: (path: string) => string + } + +const {Provider, Consumer} = createContext(undefined) + +export const Location: FunctionComponent<{ + children?: (location: RouterContext) => ComponentChildren +}> = ({children}) => ( + + {(location: undefined | RouterContext) => + location && children && children(location) + } + +) + +const pathnameMatcher: Matcher = (location, route) => + parseRoute(location.pathname, route) + +type RouterAttrs = { + matcher?: Matcher + formatPath?: (path: string) => string +} + +export class Router extends View { + onCreate() { + window.addEventListener('popstate', this.redraw) + window.addEventListener('hashchange', this.redraw) + this.onRemove = () => { + window.removeEventListener('popstate', this.redraw) + window.removeEventListener('hashchange', this.redraw) + } + } + + render() { + const { + matcher = pathnameMatcher, + formatPath = (v: string) => v + } = this.attrs + const {location, history} = window + const {protocol, hostname, port, search, hash, pathname} = location + return ( + { + history.replaceState(pathname, '', to) + dispatchEvent(new PopStateEvent('popstate', {state: pathname})) + }, + push: (href: string) => { + history.pushState(href, '', href) + dispatchEvent(new PopStateEvent('popstate', {state: href})) + }, + match: (route: RouteAttrs) => matcher(location, route), + formatPath + } as RouterContext + } + > + {this.children} + + ) + } +} diff --git a/src/router/switch.ts b/src/router/switch.ts deleted file mode 100755 index 29eff0a..0000000 --- a/src/router/switch.ts +++ /dev/null @@ -1,27 +0,0 @@ -import {Children, Vnode} from 'mithril' -import {ComponentConstructors, m} from '../hyperscript' -import {View} from '../ui/view' -import {extractChildren} from '../util/children' -import {Match} from './parseroute' -import {Route, RouteAttrs} from './route' -import {Location, RouterContext} from './router' - -export class Switch extends View<{ - children: Array> -}> { - render() { - const {children} = this.attrs - return m(Location, (location: RouterContext) => { - for (const child of children) { - if (child.tag !== Route) - throw `Unexpected child type "${child.tag}", expected "Route"` - const render = extractChildren( - child.children - ) as ComponentConstructors<{match?: Match; location?: RouterContext}> - const match = location.match(child.attrs) - if (!match) continue - return render && m(render, {match, location}) - } - }) - } -} diff --git a/src/router/switch.tsx b/src/router/switch.tsx new file mode 100644 index 0000000..37354f4 --- /dev/null +++ b/src/router/switch.tsx @@ -0,0 +1,30 @@ +import {ComponentChildren, VNode} from 'preact' +import {m} from '../hyperscript' +import {View} from '../ui/view' +import {Route, RouteAttrs} from './route' +import {Location, RouterContext} from './router' + +export class Switch extends View<{ + children: Array> +}> { + render() { + const {children} = this.attrs + return ( + + {(location: RouterContext) => { + for (const child of children) { + const props = child!.props! as RouteAttrs & { + children: ComponentChildren + } + if (child.type !== Route) + throw `Unexpected child type "${child.type}", expected "Route"` + const Render = props.children as any + const match = location.match(props) + if (!match) continue + return Render && + } + }} + + ) + } +} diff --git a/src/styled/index.ts b/src/styled/index.ts deleted file mode 100755 index 9bc65a5..0000000 --- a/src/styled/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './styled' diff --git a/src/styled/styled.ts b/src/styled/styled.ts deleted file mode 100755 index f2c5bbe..0000000 --- a/src/styled/styled.ts +++ /dev/null @@ -1,96 +0,0 @@ -import createCache from '@emotion/cache' -import isPropValid from '@emotion/is-prop-valid' -import {serializeStyles} from '@emotion/serialize' -import {EmotionCache, getRegisteredStyles, insertStyles} from '@emotion/utils' -import {StatelessView} from 'ui' -import {DOMAttrs, m} from '../hyperscript' -import {createContext} from '../ui/context' - -const EmotionCacheContext = createContext(createCache()) - -type Styled = { - (tag: string): (...args: any[]) => StatelessView - (tag: T): (...args: any[]) => T -} - -const concatClasses = (classes: Array) => - classes.filter(v => v).join(' ') - -const addClasses = (attrs: DOMAttrs, ...rest: Array): DOMAttrs => { - const {class: c1, className: c2, ...props} = attrs - return {...props, class: concatClasses([c1, c2, ...rest])} -} - -const filterProps = (props: DOMAttrs) => { - const res: DOMAttrs = {} - for (const key in props) if (isPropValid(key)) res[key] = props[key] - return res -} - -export const styled: Styled = (tag: any): any => { - const isReal = tag.__emotion_real === tag - const baseTag = (isReal && tag.__emotion_base) || tag - - return function(): StatelessView { - const args = arguments - const styles: any = /*isReal && tag.__emotion_styles !== undefined - ? tag.__emotion_styles.slice(0) - :*/ [] - const name = - (typeof tag === 'string' && tag) || - (tag.name !== 'component' && tag.name) || - tag.displayName - if (name) styles.push(`label:${name};`) - if (args[0] == null || args[0].raw === undefined) { - styles.push.apply(styles, args) - } else { - styles.push(args[0][0]) - const len = args.length - for (let i = 1; i < len; i++) { - styles.push(args[i], args[0][i]) - } - } - - const component: StatelessView = ({ - children, - className: extra, - ...attrs - }) => - m(EmotionCacheContext.Consumer, (cache: EmotionCache) => { - let className = '' - const classInterpolations: Array = [] - if (typeof extra === 'string') { - className += getRegisteredStyles( - cache.registered, - classInterpolations, - extra - ) - } - // Todo: typescript typings are wrong, first two arguments are switched - const serialized = (serializeStyles as any)( - styles.concat(classInterpolations), - cache.registered, - attrs - ) - const rules = insertStyles(cache, serialized, typeof tag === 'string') - className += `${cache.key}-${serialized.name}` - if (typeof tag === 'string' && attrs.as) { - tag = attrs.as - delete attrs.as - } - const props = - typeof tag === 'string' - ? filterProps(addClasses(attrs, className)) - : addClasses(attrs, className) - return m(tag, props, children) - }) - - const Styled: any = component - if (name) Styled.displayName = name - Styled.__emotion_real = Styled - Styled.__emotion_base = baseTag - Styled.__emotion_styles = styles - - return Styled - } -} diff --git a/src/ui/breakpoint.ts b/src/ui/breakpoint.tsx old mode 100755 new mode 100644 similarity index 85% rename from src/ui/breakpoint.ts rename to src/ui/breakpoint.tsx index 55ed740..f87ca93 --- a/src/ui/breakpoint.ts +++ b/src/ui/breakpoint.tsx @@ -1,9 +1,10 @@ -import m, {Children} from 'mithril' +import {ComponentChildren} from 'preact' +import {m} from '../hyperscript' import {View} from './view' export class Breakpoint extends View< { - children?: (attrs: T) => Children + children: (attrs: T) => ComponentChildren } & { [breakpoint: string]: T } @@ -20,7 +21,7 @@ export class Breakpoint extends View< keys.forEach(query => { if (this.matchers.has(query)) return const matcher = matchMedia(query) - matcher.addListener(m.redraw) + matcher.addListener(this.redraw) this.matchers.set(query, matchMedia(query)) }) this.removeMatchers(keys) @@ -29,7 +30,7 @@ export class Breakpoint extends View< removeMatchers(except: Array = []) { this.matchers.forEach((matcher, query) => { if (except.indexOf(query) > -1) return - matcher.removeListener(m.redraw) + matcher.removeListener(this.redraw) this.matchers.delete(query) }) } diff --git a/src/ui/carousel.ts b/src/ui/carousel.tsx similarity index 96% rename from src/ui/carousel.ts rename to src/ui/carousel.tsx index a1bcf3b..7d994f9 100644 --- a/src/ui/carousel.ts +++ b/src/ui/carousel.tsx @@ -1,5 +1,4 @@ import './carousel.less' - // We polyfill these as they're used in popmotion. Babel runtime transform // doesn't pick these up if they're in in an external lib like this one // (I think). @@ -68,10 +67,6 @@ export class Carousel extends View< return this.offset.get() as number } - redraw() { - m.redraw() - } - onCreate() { this.onUpdate() const contentStyler = styler(this.content) @@ -257,17 +252,19 @@ export class Carousel extends View< render() { const {overflow, unstyled, className} = this.attrs - return m('.carousel', - { - style: {overflow: !(overflow || unstyled) && 'hidden'}, - className - }, - m('.carousel-content', - { - ondragstart: (e: Event) => e.preventDefault() - }, - this.children - ) + return ( +
+ +
) } } diff --git a/src/ui/component.ts b/src/ui/component.ts deleted file mode 100755 index 2431ab5..0000000 --- a/src/ui/component.ts +++ /dev/null @@ -1,60 +0,0 @@ -import {Children, ClassComponent, CVnode, CVnodeDOM} from 'mithril' -import {extractChildren} from '../util/children' - -declare var process: {env: {NODE_ENV: string}} - -export class Component - implements ClassComponent { - // Following are references to properties on mithril's vnode - attrs: Attr - dom!: El - children: Children - - constructor(vnode: CVnode) { - this.attrs = vnode.attrs - } - - oninit(vnode: CVnode) { - this.patch(this.update, 'oncreate', 'onupdate', 'view') - } - - onbeforeupdate(vnode: CVnode, old: CVnodeDOM) { - return true - } - - onafterupdate() {} - onremove() {} - - patch(to: any, ...methods: Array) { - const comp: any = this - methods.forEach(method => { - const original = comp[method] && comp[method].bind(comp) - comp[method] = (...args: Array) => { - return to.apply(comp, args.concat(original)) - } - }) - } - - update(vnode: CVnodeDOM, original?: any) { - this.attrs = vnode.attrs - if (vnode.dom) this.dom = vnode.dom as any - this.children = extractChildren(vnode.children) - this.onafterupdate() - if (original) { - if (process.env.NODE_ENV === 'production') { - try { - return original(vnode) - } catch (e) { - console.error(e) - return null - } - } else { - return original(vnode) - } - } - } - - view(): Children { - throw 'assert' - } -} diff --git a/src/ui/context.ts b/src/ui/context.ts index 3c297df..bd673fc 100755 --- a/src/ui/context.ts +++ b/src/ui/context.ts @@ -1,48 +1 @@ -// See https://github.com/MithrilJS/mithril.js/issues/2148#issuecomment-452023800 -import m, {Children, CVnode} from 'mithril' -import {View} from './view' - -export type ProviderAttrs = {value: T} -export type Provider = { - new (vnode: CVnode>): View> -} -export type ConsumerAttrs = {children: (value: T) => Children} -export type Consumer = { - new (vnode: CVnode>): View> -} - -export type Context = { - Provider: Provider - Consumer: Consumer - __get: () => T -} - -type ContextCreator = { - (): Context - (context: T): Context -} - -export const createContext: ContextCreator = (context?: T): Context => ({ - Provider: class Provider extends View> { - render() { - const received = context - const {value, children} = this.attrs - context = value - return [ - children, - m({ - view: () => { - context = received - } - }) - ] - } - }, - Consumer: class Consumer extends View> { - render() { - const {children} = this.attrs - return children(context as T) - } - }, - __get: () => context as T -}) +export {createContext} from 'preact' \ No newline at end of file diff --git a/src/ui/form/boxes.less b/src/ui/form/boxes.less deleted file mode 100644 index 41d56e8..0000000 --- a/src/ui/form/boxes.less +++ /dev/null @@ -1,15 +0,0 @@ -.boxes-front { - &-left, &-right { - @media (min-width: 480px) { - display: inline-block; - vertical-align: top; - width: ~"calc(50% - 12.5px)"; - } - } - - &-col + &-col { - @media (min-width: 480px) { - margin-left: 25px; - } - } -} \ No newline at end of file diff --git a/src/ui/form/boxes.ts b/src/ui/form/boxes.ts deleted file mode 100755 index ebebe27..0000000 --- a/src/ui/form/boxes.ts +++ /dev/null @@ -1,64 +0,0 @@ -import './boxes.less' - -import m from 'mithril' -import {cleanupOptions, Options} from '../../util/formutils' -import {Component} from '../component' -import {Checkbox} from './checkbox' - -export class Boxes extends Component<{ - name: string - unstyled?: boolean - className?: string - value: Array - label?: string - modifier?: any - required?: boolean - options: Options - onchange?: (v: Array) => void - onfocus?: (e: Event) => void -}> { - className = - this.attrs.className || (this.attrs.unstyled && 'boxes') || 'boxes-front' - - setValue(key: string, active: boolean) { - const {value = [], onchange = () => {}} = this.attrs - - if (active) { - if (!value.find(v => v == key)) onchange([...value, key]) - else onchange(value) - } else { - onchange(value.filter(v => v != key)) - } - } - - view() { - const {value = [], options, unstyled, required} = this.attrs - const cleanOptions = cleanupOptions(options) - const half = Math.ceil(cleanOptions.length / 2) - - return m(`div.${this.className}`, [ - m(`div.${this.className}-left`, [ - cleanOptions.slice(0, half).map(o => - m(Checkbox, { - unstyled, - required: required && value.length == 0, - value: value.find(v => v == o.key), - onchange: (d: boolean) => this.setValue(o.key, d), - label: o.label - }) - ) - ]), - m(`.${this.className}-right`, [ - cleanOptions.slice(half).map(o => - m(Checkbox, { - unstyled, - required: required && value.length == 0, - value: value.find(v => v == o.key), - onchange: (d: boolean) => this.setValue(o.key, d), - label: o.label - }) - ) - ]) - ]) - } -} diff --git a/src/ui/form/checkbox.less b/src/ui/form/checkbox.less deleted file mode 100644 index cc68861..0000000 --- a/src/ui/form/checkbox.less +++ /dev/null @@ -1,63 +0,0 @@ -.checkbox-front { - transform: translate3d(0, 0, 0); - - &-input { - //Do not use display: none because the object has to be positioned - position: absolute; - opacity: 0; - } - - &-label { - cursor: pointer; - position: relative; - - &-square, - &-text { - display: inline-block; - vertical-align: top; - } - - &-square { - position: relative; - top: 4px; - width: 14px; - height: 14px; - border: 1px solid #dedede; - border-radius: 2px; - margin-right: 15px; - - .has-error & { - border: 1px solid #f44336; - } - - &:after { - color: #000000; - content: "✔"; - font-size: 16px; - line-height: 0.9; - position: absolute; - top: 0; - left: 0; - width: 14px; - height: 14px; - opacity: 0; - transform: scale(0); - transition: all 0.3s ease; - } - } - - &-text { - font-size: 18px; - line-height: 22px; - max-width: ~"calc(100% - 30px)"; - user-select: none; - } - } - - &-input:checked + .checkbox-front-label { - .checkbox-front-label-square:after { - opacity: 1; - transform: scale(1); - } - } -} diff --git a/src/ui/form/checkbox.ts b/src/ui/form/checkbox.ts deleted file mode 100755 index a3532fd..0000000 --- a/src/ui/form/checkbox.ts +++ /dev/null @@ -1,41 +0,0 @@ -import './checkbox.less' - -import m from 'mithril' -import {randomKey} from '../../util/formutils' -import {Component} from '../component' - -export class Checkbox extends Component<{ - name?: string - unstyled?: boolean - className?: string - value?: null | string | boolean - label?: string - required?: boolean - onchange?: (v: boolean) => void -}> { - className = - this.attrs.className || - (this.attrs.unstyled && 'checkbox') || - 'checkbox-front' - id = randomKey('check_') - - view() { - const {value, onchange, label, name = this.id, required} = this.attrs - - return m(`div.${this.className}`, [ - m(`input.${this.className}-input`, { - type: 'checkbox', - name, - id: this.id, - checked: value ? true : false, - onclick: onchange && (() => onchange(!value)), - required - }), - m(`label.${this.className}-label`, - {for: this.id}, - m(`span.${this.className}-label-square`), - m(`span.${this.className}-label-text`, label) - ) - ]) - } -} diff --git a/src/ui/form/field.less b/src/ui/form/field.less deleted file mode 100644 index 927e40a..0000000 --- a/src/ui/form/field.less +++ /dev/null @@ -1,15 +0,0 @@ -.field-front{ - - position: relative; - display: inline-block; - padding: 10px 0; - text-align: left; - vertical-align: top; - - &-errormsg { - padding-top: 5px; - padding-right: 10px; - font-size: 11px; - color: red; - } -} \ No newline at end of file diff --git a/src/ui/form/field.ts b/src/ui/form/field.ts deleted file mode 100755 index 49d9b32..0000000 --- a/src/ui/form/field.ts +++ /dev/null @@ -1,47 +0,0 @@ -import './field.less' - -import m from 'mithril' -import classnames from 'classnames' -import {Component} from '../component' -import {getErrorMessage} from '../../util/formutils' - -export class Field extends Component<{ - unstyled?: boolean - errors?: undefined | string | Array - label?: string - id?: string - required?: boolean - width?: number -}> { - className = this.attrs.unstyled ? 'field' : 'field-front' - - view() { - const {errors, id, required, width = 1.0} = this.attrs - - const style = {width: `${width * 100}%`} - const hasErrors = errors !== undefined - const classes = [hasErrors && 'has-error', required && 'is-required'] - - return m(`div.${this.className}`, {class: classnames(classes), style, id}, [ - this.viewLabel(), - this.children, - this.viewErrors() - ]) - } - - viewLabel() { - const {label} = this.attrs - if (!label) return - - return m(`div.${this.className}-label`, label) - } - - viewErrors() { - const {errors} = this.attrs - const hasErrors = errors !== undefined - - if (!hasErrors) return - - return m(`div.${this.className}-errormsg`, getErrorMessage(errors)) - } -} diff --git a/src/ui/form/fields.ts b/src/ui/form/fields.ts deleted file mode 100755 index 7372417..0000000 --- a/src/ui/form/fields.ts +++ /dev/null @@ -1,90 +0,0 @@ -import jump from 'jump.js' -import m from 'mithril' -import {FormStatus, FormStore} from '../../store/formstore' -import {randomKey} from '../../util/formutils' -import {Field} from './field' - -export {Input} from './input' -export {Select} from './select' -export {Textarea} from './textarea' -export {Radio} from './radio' -export {Radios} from './radios' -export {Checkbox} from './checkbox' -export {Boxes} from './boxes' - -export class Fields { - store: FormStore - key = randomKey() - config = { - fieldClass: Field, - defaultUnstyled: false, - defaultRequired: true, - labelInFields: false - } - - constructor(store: FormStore, config: any) { - this.store = store - this.config = {...this.config, ...config} - } - - status() { - return this.store.status - } - - asField(viewClass: any, config: any, children?: any) { - return m(this.config.fieldClass, - this.fieldAttrs(config), - m(viewClass, this.viewAttrs(config), children) - ) - } - - defaultFieldAttrs(key: string, rest: any) { - return { - required: this.config.defaultRequired, - unstyled: this.config.defaultUnstyled, - name: key, - ...rest, - id: 'field_' + key + '_' + this.key, - value: this.store.data[key], - onchange: (value: any) => this.store.setData(key, value), - label: this.config.labelInFields ? undefined : rest.label - } - } - - /** - * Can be used to initialize custom formfields - also used internally - */ - fieldAttrs(input: any) { - const {key, ...rest} = input - const attrs = this.defaultFieldAttrs(rest.name || key, rest) - const status = this.status() - switch (status.type) { - case 'error': - return { - ...attrs, - errors: status.errors[key], - onfocus: () => { - if (status.type == 'error') delete status.errors[key] - } - } - default: - return attrs - } - } - - /** - * This method can be overridden and used to filter certain attributes from passing on to the child element inside. - * Example: Use this to filter out the label attribute. It can now be drawn in the field view itself. - */ - viewAttrs(attrs: any) { - return { - ...this.fieldAttrs(attrs), - id: undefined, - label: this.config.labelInFields ? attrs.label : undefined - } - } - - focusField(field: string) { - jump(`#field_${field}_${this.key}`) - } -} diff --git a/src/ui/form/index.ts b/src/ui/form/index.ts deleted file mode 100755 index 19e3f26..0000000 --- a/src/ui/form/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -export * from './boxes' -export * from './checkbox' -export * from './field' -export * from './fields' -export * from './input' -export * from './radio' -export * from './radios' -export * from './select' -export * from './textarea' diff --git a/src/ui/form/input.less b/src/ui/form/input.less deleted file mode 100644 index 3ca7d0d..0000000 --- a/src/ui/form/input.less +++ /dev/null @@ -1,29 +0,0 @@ -.input-front { - position: relative; - - //INPUT - &-input { - width: 100%; - background-color: #fff; - border: 1px solid #dedede; - padding: 10px; - color: #171717; - font: inherit; - font-size: 14px; - line-height: 14px; - - .has-error & { - border: 1px solid #f44336; - } - } - - //LABEL - &-label { - color: #000000; - pointer-events: none; - user-select: none; - position: absolute; - left: 10px; - top: 10px; - } -} \ No newline at end of file diff --git a/src/ui/form/input.ts b/src/ui/form/input.ts deleted file mode 100755 index 96a7561..0000000 --- a/src/ui/form/input.ts +++ /dev/null @@ -1,60 +0,0 @@ -import './input.less' - -import classnames from 'classnames' -import m from 'mithril' -import {getErrorMessage} from '../../util/formutils' -import {Component} from '../component' - -export class Input extends Component<{ - name: string - type?: string - unstyled?: boolean - className?: string - value: string - label?: string - modifier?: any - required?: boolean - disabled?: boolean - options: Array<{key: string; label: string}> | {[key: string]: string} - onchange?: (v: string) => void - onfocus?: (e: Event) => void - placeholder?: string -}> { - className = - this.attrs.className || (this.attrs.unstyled && 'input') || 'input-front' - inputDom: null | HTMLElement = null - - view() { - const { - value, - onchange, - label, - name, - modifier, - onfocus, - type = 'text', - required, - disabled, - placeholder - } = this.attrs - - return m(`.${this.className}`, - {class: classnames([modifier, value && 'has-value'])}, - [ - m(`input.${this.className}-input`, { - type, - required, - disabled, - name, - value, - placeholder, - onfocus, - oncreate: vnode => (this.inputDom = vnode.dom as HTMLElement), - oninput: onchange && ((e: any) => onchange(e.target.value)), - onchange: onchange && ((e: any) => onchange(e.target.value)) - }), - label && m(`label.${this.className}-label`, label) - ] - ) - } -} diff --git a/src/ui/form/radio.less b/src/ui/form/radio.less deleted file mode 100644 index f44b965..0000000 --- a/src/ui/form/radio.less +++ /dev/null @@ -1,60 +0,0 @@ -.radio-front { - & + .radio-front { - margin-top: 10px; - } - - &-input { - //Do not use display: none because the object has to be positioned - position: absolute; - opacity: 0; - } - - &-label { - cursor: pointer; - position: relative; - - &-bullet, - &-text { - display: inline-block; - vertical-align: top; - } - - &-bullet { - position: relative; - top: 4px; - width: 14px; - height: 14px; - border: 1px solid #dedede; - border-radius: 100%; - margin-right: 15px; - - &:after { - content: ""; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%) scale(0); - width: 10px; - height: 10px; - border-radius: 100%; - background-color: #3f98bd; - opacity: 0; - transition: all 0.3s ease; - } - } - - &-text { - font-size: 18px; - line-height: 22px; - max-width: ~"calc(100% - 30px)"; - user-select: none; - } - } - - &-input:checked + .radio-front-label { - .radio-front-label-bullet:after { - opacity: 1; - transform: translate(-50%, -50%) scale(1); - } - } -} diff --git a/src/ui/form/radio.ts b/src/ui/form/radio.ts deleted file mode 100755 index 436a77c..0000000 --- a/src/ui/form/radio.ts +++ /dev/null @@ -1,53 +0,0 @@ -import './radio.less' - -import m from 'mithril' -import {randomKey} from '../../util/formutils' -import {Component} from '../component' -import {classes} from '../../util/classes' -import {DOMAttrs} from '../../hyperscript' - -export class Radio extends Component< - { - name: string - value: boolean - onchange?: (checked: boolean) => void - option: string // Really label? - required?: boolean - unstyled?: boolean - className?: string - disabled?: boolean - } & DOMAttrs -> { - className = - this.attrs.className || (this.attrs.unstyled && 'radio') || 'radio-front' - id = randomKey('radio_') - - view() { - const { - value = false, - onchange, - option, - name = this.id, - required, - disabled, - ...rest - } = this.attrs - - return m(`div.${this.className}`, classes({is: {disabled}}), [ - m(`input.${this.className}-input`, { - type: 'radio', - checked: !!value, - required, - name: name, - onclick: onchange && (() => onchange(!value)), - id: this.id, - disabled, - ...rest - }), - m(`label.${this.className}-label`, {for: this.id}, [ - m(`span.${this.className}-label-bullet`), - m(`span.${this.className}-label-text`, option) - ]) - ]) - } -} diff --git a/src/ui/form/radios.ts b/src/ui/form/radios.ts deleted file mode 100755 index b620e7b..0000000 --- a/src/ui/form/radios.ts +++ /dev/null @@ -1,51 +0,0 @@ -import m from 'mithril' -import {cleanupOptions, randomKey} from '../../util/formutils' -import {Component} from '../component' -import {Radio} from './radio' -import {DOMAttrs} from '../../hyperscript' - -export class Radios extends Component<{ - name: string - unstyled?: boolean - className?: string - value: string - label?: string - modifier?: any - required?: boolean - options: - | Array<{key: string; label: string} & DOMAttrs> - | {[key: string]: string} - onchange?: (v: string) => void - onfocus?: (e: Event) => void -}> { - className = - this.attrs.className || (this.attrs.unstyled && 'radios') || 'radios-front' - defaultKey = randomKey('radios_') - - view() { - const { - value, - onchange, - options, - unstyled, - name = this.defaultKey, - required - } = this.attrs - - const cleanOptions = cleanupOptions(options) - - return m(`div.${this.className}`, - cleanOptions.map(({key, label, ...rest}) => - m(Radio, { - option: label, - name: name, - unstyled, - required, - value: value == key, - onchange: onchange && (() => onchange(key)), - ...rest - }) - ) - ) - } -} diff --git a/src/ui/form/select.less b/src/ui/form/select.less deleted file mode 100644 index 6498f4d..0000000 --- a/src/ui/form/select.less +++ /dev/null @@ -1,20 +0,0 @@ -.select-front { - width: 100%; - background-color: #fff; - border: 1px solid #dedede; - border-radius: 4px; - padding: 18.5px 20px; - color: #000000; - font: inherit; - font-size: 14px; - line-height: 14px; - appearance: none; - - &:focus { - outline: 0; - } - - .has-error & { - border: 1px solid #f44336; - } -} diff --git a/src/ui/form/select.ts b/src/ui/form/select.ts deleted file mode 100755 index b88bdb6..0000000 --- a/src/ui/form/select.ts +++ /dev/null @@ -1,55 +0,0 @@ -import './select.less' - -import classnames from 'classnames' -import m from 'mithril' -import {cleanupOptions} from '../../util/formutils' -import {Component} from '../component' - -export class Select extends Component<{ - name: string - unstyled?: boolean - className?: string - value: string - label?: string - modifier?: any - required?: boolean - options: Array<{key: string; label: string}> | {[key: string]: string} - onchange?: (v: string) => void - onfocus?: (e: Event) => void -}> { - className = - this.attrs.className || (this.attrs.unstyled && 'select') || 'select-front' - - view() { - const { - value, - onchange, - label, - name, - options, - modifier, - onfocus, - required = true - } = this.attrs - - const cleanOptions = cleanupOptions(options) - const fullLabel = required ? label + ' *' : label - - return m(`select.${this.className}`, - { - class: classnames([modifier, value && 'has-value']), - name, - required, - onfocus, - onchange: onchange && ((e: any) => onchange(e.target.value)), - oninput: onchange && ((e: any) => onchange(e.target.value)) - }, - [ - label && m('option[disabled]', {selected: !value}, fullLabel), - cleanOptions.map(o => - m('option', {value: o.key, selected: o.key == value}, o.label) - ) - ] - ) - } -} diff --git a/src/ui/form/textarea.less b/src/ui/form/textarea.less deleted file mode 100644 index 47538db..0000000 --- a/src/ui/form/textarea.less +++ /dev/null @@ -1,34 +0,0 @@ -.textarea-front { - position: relative; - - //TEXTAREA - &-textarea { - width: 100%; - background-color: #fff; - border: 1px solid #dedede; - border-radius: 4px; - padding: 25px 20px 10px 20px; - color: #000000; - font: inherit; - font-size: 14px; - line-height: 14px; - display: inline-block; - resize: vertical; - min-height: 100px; - - .error & { - border: 1px solid #f44336; - } - } - - //LABEL - &-label { - color: #000000; - pointer-events: none; - user-select: none; - position: absolute; - left: 10px; - top: 10px; - backface-visibility: hidden; - } -} diff --git a/src/ui/form/textarea.ts b/src/ui/form/textarea.ts deleted file mode 100755 index 5274d81..0000000 --- a/src/ui/form/textarea.ts +++ /dev/null @@ -1,49 +0,0 @@ -import './textarea.less' - -import classnames from 'classnames' -import m from 'mithril' -import {Component} from '../component' - -export class Textarea extends Component<{ - name: string - unstyled?: boolean - className?: string - value: string - label?: string - modifier?: any - required?: boolean - onchange?: (v: string) => void - onfocus?: (e: Event) => void -}> { - className = - this.attrs.className || - (this.attrs.unstyled && 'textarea') || - 'textarea-front' - - view() { - const { - value, - onchange, - label, - modifier, - name, - required, - onfocus - } = this.attrs - - return m(`div.${this.className}`, - {class: classnames([modifier, value && 'has-value'])}, - [ - m(`textarea.${this.className}-textarea`, { - required, - name, - onfocus, - value, - oninput: onchange && ((e: any) => onchange(e.target.value)), - onchange: onchange && ((e: any) => onchange(e.target.value)) - }), - label && m(`label.${this.className}-label`, label) - ] - ) - } -} diff --git a/src/ui/icon.ts b/src/ui/icon.tsx old mode 100755 new mode 100644 similarity index 81% rename from src/ui/icon.ts rename to src/ui/icon.tsx index fa9e10e..10459ae --- a/src/ui/icon.ts +++ b/src/ui/icon.tsx @@ -7,6 +7,6 @@ import {View} from './view' export class Icon extends View<{icon: string} & DOMAttrs> { render() { const {children, icon, ...attrs} = this.attrs - return m('i.icon', addClasses(attrs, {icon})) + return } } diff --git a/src/ui/image.ts b/src/ui/image.tsx old mode 100755 new mode 100644 similarity index 72% rename from src/ui/image.ts rename to src/ui/image.tsx index 2777c7b..3f6521c --- a/src/ui/image.ts +++ b/src/ui/image.tsx @@ -1,9 +1,9 @@ import './image.less' +import {createContext, h} from 'preact' import {DOMAttrs, m} from '../hyperscript' import {addClasses} from '../util/classes' import {contain, cover} from '../util/fit' -import {createContext} from './context' import {View} from './view' export type ImageFit = 'portrait' | 'landscape' | 'cover' | 'contain' @@ -69,12 +69,13 @@ class ImageBase extends View { } scale() { + const dom = this.dom as HTMLElement const {width = 0, height = 0, fit, background} = this.attrs if (!this.dom || !this.shouldCheckScale) return - const container = this.dom.getBoundingClientRect() + const container = dom.getBoundingClientRect() if (canUpscale(fit as any, container, {width, height})) return - if (this.useBackground) this.dom.style.backgroundSize = 'auto' - else this.dom.style.objectFit = 'none' + if (this.useBackground) dom.style.backgroundSize = 'auto' + else dom.style.objectFit = 'none' } get shouldCheckScale() { @@ -114,14 +115,14 @@ class ImageBase extends View { ...attrs } = this.attrs const crop = this.shouldCrop - const tag = this.useBackground ? 'div' : 'img' + const Tag = this.useBackground ? 'div' : 'img' const focus = typeof position === 'string' ? position : position && `${position.x * 100}% ${position.y * 100}%` - return m(tag, - { - ...(this.useBackground + return ( + { objectPosition: focus !== 'center center' && focus, ...style } - }), - ...addClasses(attrs, 'image', {mod: {crop}}) - }, - children + })} + {...addClasses(attrs, 'image', {mod: {crop}})} + > + {children} + ) } } type Resizer = (info: ImageAttrs, container: Size) => ImageAttrs -const ImageResizerContext = createContext() +const ImageResizerContext = createContext(undefined) export class ImageResizer extends View<{ resize: Resizer }> { render() { const {resize} = this.attrs - return m(ImageResizerContext.Provider, {value: resize}, this.children) + return ( + + {this.children} + + ) } } @@ -184,28 +190,40 @@ export class Image extends View { ...rest } = this.attrs const crop = background || fit === 'cover' || fit === 'contain' - return m(ImageResizerContext.Consumer, (resize: Resizer | undefined) => { - if (!resize) return m(ImageBase, this.attrs, children) - const resolve = (dom: HTMLElement) => { - this.resolved = resize(this.attrs, { - width: dom.offsetWidth, - height: dom.offsetHeight - }) - this.cached = src - } - if (!this.dom) - return m('div', - { - ...addClasses(rest, 'image', {mod: {crop}}), - oncreate: vnode => { - resolve(vnode.dom as HTMLElement) - m.redraw() - } - }, - children - ) - if (this.cached !== src) resolve(this.dom) - return this.resolved && m(ImageBase, this.resolved, children) - }) + return ( + + {(resize: Resizer | undefined) => { + if (!resize) return {children} + const resolve = (dom: HTMLElement) => { + this.resolved = resize(this.attrs, { + width: dom.offsetWidth, + height: dom.offsetHeight + }) + this.cached = src + } + if (!this.dom) + return ( +
{ + if (!dom) return + resolve(dom as HTMLElement) + this.redraw() + } + }} + > + {children} +
+ ) + if (this.cached !== src) resolve(this.dom as HTMLElement) + return ( + this.resolved && ( + {children} + ) + ) + }} +
+ ) } } diff --git a/src/ui/index.ts b/src/ui/index.ts index 6b1ed00..fa65c8b 100755 --- a/src/ui/index.ts +++ b/src/ui/index.ts @@ -1,13 +1,9 @@ -export * from './form' -export * from './maps' export * from './breakpoint' export * from './carousel' -export * from './component' export * from './context' export * from './icon' export * from './image' export * from './masonry' export * from './mediaquery' export * from './modal' -export * from './portal' export * from './view' diff --git a/src/ui/maps/eventmanager.ts b/src/ui/maps/eventmanager.ts deleted file mode 100755 index c8ce159..0000000 --- a/src/ui/maps/eventmanager.ts +++ /dev/null @@ -1,40 +0,0 @@ -import {ListenerCache} from '../../util/listenercache' -import {View} from '../view' - -export class EventManager extends View<{ - object: google.maps.MVCObject - on?: {[key: string]: any} -}> { - events!: ListenerCache - onInit = this.createCache - onBeforeUpdate = this.attachEvents - - createCache() { - const {object} = this.attrs - this.events = new ListenerCache({ - add: (key, listener) => { - const dissolve = object.addListener(key, listener) - return () => google.maps.event.removeListener(dissolve) - }, - hit: (key, event) => { - const {on = {}} = this.attrs - event.target = object - if (on && key in on) on[key](event) - } - }) - this.attachEvents() - } - - attachEvents() { - const {on = {}} = this.attrs - this.events.attach(Object.keys(on)) - } - - onRemove() { - this.events.remove() - } - - render() { - return this.children - } -} diff --git a/src/ui/maps/index.ts b/src/ui/maps/index.ts deleted file mode 100755 index 43ec588..0000000 --- a/src/ui/maps/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './maps' -export * from './marker' diff --git a/src/ui/maps/mappool.ts b/src/ui/maps/mappool.ts deleted file mode 100755 index cf7aa92..0000000 --- a/src/ui/maps/mappool.ts +++ /dev/null @@ -1,21 +0,0 @@ -export class MapPool { - pool: Array = [] - - create(construct: (dom: Element) => google.maps.Map) { - const container = () => { - const dom = document.createElement('div') - dom.style.height = '100%' - return dom - } - const instance = this.pool.length - ? (this.pool.pop() as google.maps.Map) - : construct(container()) - return instance - } - - release(map: google.maps.Map) { - const dom = map.getDiv() - if (dom.parentNode) dom.parentNode.removeChild(dom) - this.pool.push(map) - } -} diff --git a/src/ui/maps/maps.less b/src/ui/maps/maps.less deleted file mode 100755 index 94a1160..0000000 --- a/src/ui/maps/maps.less +++ /dev/null @@ -1,3 +0,0 @@ -.maps { - height: 100%; -} diff --git a/src/ui/maps/maps.ts b/src/ui/maps/maps.ts deleted file mode 100755 index aa84b10..0000000 --- a/src/ui/maps/maps.ts +++ /dev/null @@ -1,111 +0,0 @@ -import './maps.less' - -import loadGoogleMapsApi from 'load-google-maps-api' -import {m} from '../../hyperscript' -import {createContext} from '../context' -import {View} from '../view' -import {EventManager} from './eventmanager' -import {MapPool} from './mappool' - -export const MapsContext = createContext() - -export type MapsEvent = { - target: T - redraw: boolean -} - -export type MouseEvents = { - click?: (event?: google.maps.MouseEvent & MapsEvent) => void - dblclick?: (event?: google.maps.MouseEvent & MapsEvent) => void - rightclick?: (event?: google.maps.MouseEvent & MapsEvent) => void - mousemove?: (event?: google.maps.MouseEvent & MapsEvent) => void - mouseout?: (event?: google.maps.MouseEvent & MapsEvent) => void - mouseover?: (event?: google.maps.MouseEvent & MapsEvent) => void -} - -export type MapsEvents = MouseEvents & { - idle?: (event?: MapsEvent) => void - tilesloaded?: (event?: MapsEvent) => void - - drag?: (event?: MapsEvent) => void - dragend?: (event?: MapsEvent) => void - dragstart?: (event?: MapsEvent) => void - - bounds_changed?: (event?: MapsEvent) => void - center_changed?: (event?: MapsEvent) => void - heading_changed?: (event?: MapsEvent) => void - maptypeid_changed?: (event?: MapsEvent) => void - projection_changed?: (event?: MapsEvent) => void - tilt_changed?: (event?: MapsEvent) => void - zoom_changed?: (event?: MapsEvent) => void -} - -export class Maps extends View< - { - apiKey: string - region?: string - language?: string - class?: string - on?: MapsEvents - initial?: { - zoom?: number - center?: google.maps.LatLng | google.maps.LatLngLiteral - } - } & google.maps.MapOptions -> { - static pool = new MapPool() - map?: google.maps.Map - - onCreate() { - const { - children, - on, - apiKey, - region, - language, - class: className, - initial = {}, - ...options - } = this.attrs - loadGoogleMapsApi({key: apiKey, region, language}) - .then(maps => { - const map = Maps.pool.create(dom => new maps.Map(dom)) - const { - zoom = 7, - center = { - lat: 51.0030477, - lng: 4.5000955 - } - } = initial - map.setOptions({zoom, center, ...options}) - if (this.dom) this.dom.appendChild(map.getDiv()) - this.onRemove = () => { - Maps.pool.release(map) - } - this.map = map - }) - .then(m.redraw) - .catch(console.error) - } - - render() { - const {class: className, on, ...options} = this.attrs - if (this.map) { - if (options) this.map.setOptions(options) - } - return m('.maps', - {className}, - this.map && - m(MapsContext.Provider, - {value: this.map}, - m(EventManager, - { - on, - object: this.map - }, - this.children - ) - ) - ) - } -} diff --git a/src/ui/maps/marker.ts b/src/ui/maps/marker.ts deleted file mode 100755 index 538eb99..0000000 --- a/src/ui/maps/marker.ts +++ /dev/null @@ -1,61 +0,0 @@ -import {m} from '../../hyperscript' -import {View} from '../view' -import {EventManager} from './eventmanager' -import {MapsContext, MapsEvent, MouseEvents} from './maps' - -export type MarkerEvents = MouseEvents & { - mousedown?: ( - event?: google.maps.MouseEvent & MapsEvent - ) => void - mouseup?: ( - event?: google.maps.MouseEvent & MapsEvent - ) => void - - drag?: ( - event?: google.maps.MouseEvent & MapsEvent - ) => void - dragend?: ( - event?: google.maps.MouseEvent & MapsEvent - ) => void - dragstart?: ( - event?: google.maps.MouseEvent & MapsEvent - ) => void - - draggable_changed?: (event?: MapsEvent) => void - animation_changed?: (event?: MapsEvent) => void - clickable_changed?: (event?: MapsEvent) => void - cursor_changed?: (event?: MapsEvent) => void - flat_changed?: (event?: MapsEvent) => void - icon_changed?: (event?: MapsEvent) => void - position_changed?: (event?: MapsEvent) => void - shape_changed?: (event?: MapsEvent) => void - title_changed?: (event?: MapsEvent) => void - visible_changed?: (event?: MapsEvent) => void - zindex_changed?: (event?: MapsEvent) => void -} - -export class Marker extends View< - google.maps.MarkerOptions & { - on?: MarkerEvents - } -> { - marker?: google.maps.Marker - onRemove() { - if (this.marker) this.marker.setMap(null) - } - render() { - const {children, on, ...options} = this.attrs - return m(MapsContext.Consumer, (map?: google.maps.Map) => { - if (!map) throw 'No Maps parent detected' - if (this.marker) this.marker.setOptions(options) - else this.marker = new google.maps.Marker({map, ...options}) - return m(EventManager, - { - on, - object: this.marker - }, - this.children - ) - }) - } -} diff --git a/src/ui/masonry.ts b/src/ui/masonry.ts deleted file mode 100755 index 268a55f..0000000 --- a/src/ui/masonry.ts +++ /dev/null @@ -1,45 +0,0 @@ -import './masonry.less' - -import m, {Child} from 'mithril' -import {View} from './view' - -export class Masonry extends View<{ - cols: number - addClass?: (i: number, j: number) => string - children: Array -}> { - divide(items: Array, colsCount: number) { - const cols: Array> = Array(colsCount) - .fill(null) - .map(_ => []) - items.forEach((item, i) => { - const col = i % colsCount - cols[col].push(item) - }) - return cols - } - - render() { - const {children, cols: colsCount, addClass} = this.attrs - const cols = this.divide(children, colsCount) - return m('.masonry', - cols.map((children, i) => - m('.masonry-col', - { - style: { - 'flex-basis': 100 / cols.length + '%' - } - }, - children.map((item, j) => - m('.masonry-item', - { - class: addClass ? addClass(i, j) : '' - }, - item - ) - ) - ) - ) - ) - } -} diff --git a/src/ui/masonry.tsx b/src/ui/masonry.tsx new file mode 100644 index 0000000..a330533 --- /dev/null +++ b/src/ui/masonry.tsx @@ -0,0 +1,43 @@ +import './masonry.less' + +import {ComponentChild} from 'preact' +import {m} from '../hyperscript' +import {View} from './view' + +export class Masonry extends View<{ + cols: number + children: Array +}> { + divide(items: Array, colsCount: number) { + const cols: Array> = Array(colsCount) + .fill(null) + .map(_ => []) + items.forEach((item, i) => { + const col = i % colsCount + cols[col].push(item) + }) + return cols + } + + render() { + const {children, cols: colsCount} = this.attrs + const cols = this.divide(children, colsCount) + return ( +
( +
+ {children.map((item, j) => ( +
{item}
+ ))} +
+ ))} + /> + ) + } +} diff --git a/src/ui/mediaquery.ts b/src/ui/mediaquery.tsx old mode 100755 new mode 100644 similarity index 61% rename from src/ui/mediaquery.ts rename to src/ui/mediaquery.tsx index d5a5381..b2e319d --- a/src/ui/mediaquery.ts +++ b/src/ui/mediaquery.tsx @@ -1,11 +1,12 @@ -import m, {Children} from 'mithril' +import {ComponentChildren} from 'preact' +import {m} from '../hyperscript' import {Breakpoint} from './breakpoint' import {View} from './view' export class MediaQuery extends View<{ minWidth?: number maxWidth?: number - view: () => Children + view: () => ComponentChildren }> { render() { const {view, minWidth, maxWidth} = this.attrs @@ -13,6 +14,14 @@ export class MediaQuery extends View<{ if (minWidth) rules.push(`(min-width: ${minWidth}px)`) if (maxWidth) rules.push(`(max-width: ${maxWidth}px)`) const query = rules.join(' and ') - return m(Breakpoint, {[query]: true}, (match: boolean) => match && view()) + return ( + + {((match: boolean) => match && view()) as any} + + ) } } diff --git a/src/ui/modal.ts b/src/ui/modal.tsx old mode 100755 new mode 100644 similarity index 77% rename from src/ui/modal.ts rename to src/ui/modal.tsx index 12acdfd..ce5f0e9 --- a/src/ui/modal.ts +++ b/src/ui/modal.tsx @@ -1,82 +1,87 @@ -import './modal.less' - -import {DOMAttrs} from 'hyperscript' -import m from 'mithril' -import {classes} from '../util/classes' -import lockScroll from '../util/lockscroll' -import {Component} from './component' - -export class ModalOverlay extends Component { - view() { - return m('.modal-overlay', this.attrs, this.children) - } -} - -export class Modal extends Component<{ - isOpen: boolean - close: () => void - zIndex?: number - mod?: any -}> { - opened = false - oncreate = this.lock - onupdate = this.lock - - lock() { - const {isOpen, close} = this.attrs - if (this.opened === isOpen) return - if (isOpen) window.addEventListener('keydown', this.closeByKey) - else window.removeEventListener('keydown', this.closeByKey) - this.opened = isOpen - } - - onremove() { - window.removeEventListener('keydown', this.closeByKey) - } - - closeByKey = (e: KeyboardEvent) => { - const {close} = this.attrs - if (e.keyCode !== 27) return - close() - m.redraw() - } - - view() { - const {isOpen, close, zIndex = 1000, mod} = this.attrs - if (!isOpen) return null - return m('.modal', - { - oncreate: ({dom}) => - setTimeout(() => { - lockScroll(true) - dom.classList.add('is-open') - }, 25), - onbeforeremove: ({dom}) => - new Promise(done => { - ;(dom as any).addEventListener( - 'transitionend', - () => { - lockScroll(false) - done() - }, - false, - {once: true} - ) - dom.classList.remove('is-open') - }), - onremove: () => lockScroll(false), - ...classes({mod}), - style: {zIndex} - }, - m('.modal-container', - { - onclick: (e: MouseEvent) => { - const target = e.target as HTMLElement - if (target && target.classList.contains('modal-container')) close() - } - }, - m('.modal-container-content', this.children) - ) - ) - } -} +import './modal.less' + +import {DOMAttrs, m} from '../hyperscript' +import {classes} from '../util/classes' +import lockScroll from '../util/lockscroll' +import {View} from './view' + +export class ModalOverlay extends View { + render() { + return ( +
+ {this.children} +
+ ) + } +} + +export class Modal extends View<{ + isOpen: boolean + close: () => void + zIndex?: number + mod?: any +}> { + render() { + return null + } + /*opened = false + oncreate = this.lock + onupdate = this.lock + + lock() { + const {isOpen, close} = this.attrs + if (this.opened === isOpen) return + if (isOpen) window.addEventListener('keydown', this.closeByKey) + else window.removeEventListener('keydown', this.closeByKey) + this.opened = isOpen + } + + onRemove() { + window.removeEventListener('keydown', this.closeByKey) + } + + closeByKey = (e: KeyboardEvent) => { + const {close} = this.attrs + if (e.keyCode !== 27) return + close() + } + + render() { + const {isOpen, close, zIndex = 1000, mod} = this.attrs + if (!isOpen) return null + return + { + oncreate: ({dom}) => + setTimeout(() => { + lockScroll(true) + dom.classList.add('is-open') + }, 25), + onbeforeremove: ({dom}) => + new Promise(done => { + ;(dom as any).addEventListener( + 'transitionend', + () => { + lockScroll(false) + done() + }, + false, + {once: true} + ) + dom.classList.remove('is-open') + }), + onremove: () => lockScroll(false), + ...classes({mod}), + style: {zIndex} + }, + m('.modal-container', + { + onclick: (e: MouseEvent) => { + const target = e.target as HTMLElement + if (target && target.classList.contains('modal-container')) close() + } + }, + m('.modal-container-content', this.children) + ) + ) + }*/ +} diff --git a/src/ui/portal.ts b/src/ui/portal.ts deleted file mode 100755 index b452141..0000000 --- a/src/ui/portal.ts +++ /dev/null @@ -1,20 +0,0 @@ -import m from 'mithril' -import {View} from './view' - -export class Portal extends View { - node = document.createElement('div') - - onCreate() { - document.body.appendChild(this.node) - m.mount(this.node, {view: () => this.children}) - } - - onRemove() { - m.mount(this.node, {view: () => null}) - document.body.removeChild(this.node) - } - - render() { - return null - } -} diff --git a/src/ui/view.ts b/src/ui/view.ts index 6cf49ff..569cfa7 100755 --- a/src/ui/view.ts +++ b/src/ui/view.ts @@ -1,40 +1,21 @@ import {ChildrenType} from 'hyperscript' -import { - Children, - ClassComponent, - CVnode, - CVnodeDOM, - Vnode, - VnodeDOM -} from 'mithril' -import {extractChildren} from '../util/children' +import {Component, FunctionComponent} from 'preact' -declare global { - namespace JSX { - interface ElementAttributesProperty { - attrs: {} - } - interface ElementChildrenAttribute { - children: {} - } - } -} +export type StatelessView

= FunctionComponent

-export type StatelessView = { - (attr: {children?: Children} & Attr): Children -} +type Dom = Element | Text | undefined -export abstract class View - implements ClassComponent { - attrs!: Readonly<{children?: Children}> & Readonly - dom: undefined | Dom +export abstract class View extends Component { + get dom() { + return this.base + } - constructor(vnode: CVnode) { - this.__update(vnode as Vnode) + get attrs(): A { + return this.props } - get children(): ChildrenType { - return this.attrs.children as ChildrenType + get children(): ChildrenType { + return this.props.children as any } // Public API @@ -42,59 +23,40 @@ export abstract class View onInit() {} onCreate(dom: Dom) {} onUpdate(dom: Dom) {} - onBeforeRemove(dom: Dom): void | Promise {} onRemove() {} - onBeforeUpdate(attrs: Attrs): void | boolean {} - abstract render(): Children | null | void + onBeforeUpdate( + nextAttrs: Readonly, + nextState: Readonly + ): void | boolean {} - // Mithril connection + // Preact connection - /** @internal */ - oninit(vnode: Vnode) { - this.__update(vnode) - return this.onInit() - } - - /** @internal */ - oncreate(vnode: VnodeDOM) { - this.__update(vnode) - return this.onCreate(vnode.dom as Dom) - } - - /** @internal */ - onupdate(vnode: VnodeDOM) { - this.__update(vnode) - return this.onUpdate(vnode.dom as Dom) - } + redraw = () => this.setState({}) - /** @internal */ - onbeforeremove(vnode: VnodeDOM) { - this.__update(vnode) - return this.onBeforeRemove(vnode.dom as Dom) + constructor(attrs?: A, context?: any) { + super(attrs, context) + this.onInit() } - /** @internal */ - onremove(vnode: VnodeDOM) { - this.__update(vnode) - return this.onRemove() + componentDidMount() { + this.onCreate(this.dom) } - /** @internal */ - onbeforeupdate(vnode: Vnode, old: CVnodeDOM) { - this.__update(vnode) - return this.onBeforeUpdate(old.attrs) + componentDidUpdate() { + this.onUpdate(this.dom) } - view(vnode: Vnode) { - return this.render() + componentWillUnmount() { + this.onRemove() } - /** @internal */ - __update(vnode: Vnode | VnodeDOM) { - if ('dom' in vnode && vnode.dom) this.dom = vnode.dom as Dom - this.attrs = { - ...vnode.attrs, - children: extractChildren(vnode.children) - } + shouldComponentUpdate( + nextAttrs: Readonly, + nextState: Readonly, + nextContext: any + ): boolean { + const res = this.onBeforeUpdate(nextAttrs, nextState) + if (res === false) return false + return true } } diff --git a/src/util/children.ts b/src/util/children.ts deleted file mode 100755 index a1dd885..0000000 --- a/src/util/children.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {Children, ChildArray} from 'mithril' - -export const extractChildren = (children: Children): Children => { - if (!Array.isArray(children)) return children - if (children[0] && typeof children[0] == 'function') - return children[0] - return ([] as ChildArray).concat(...children) -} \ No newline at end of file diff --git a/src/util/formutils.ts b/src/util/formutils.ts deleted file mode 100755 index 342ed9a..0000000 --- a/src/util/formutils.ts +++ /dev/null @@ -1,32 +0,0 @@ -export type Option = {key: string; label: string} - -export type OptionsArray = Array - -export type Options = OptionsArray | {[key: string]: string} - -export function cleanupOptions(options: Options): Array = A extends {children?: any} ? A['children'] : ComponentChildren + +export type Children = ComponentChildren \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index cc6357a..fde47fa 100755 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,8 @@ +export * from './deprecated' export * from './container' export * from './router' export * from './store' +export * from './styled' export * from './ui' export * from './util' export * from './hyperscript' diff --git a/src/router/route.tsx b/src/router/route.tsx index 63ac722..61f4849 100644 --- a/src/router/route.tsx +++ b/src/router/route.tsx @@ -1,4 +1,3 @@ -import {AnyComponent, ComponentChildren} from 'preact' import {m} from '../hyperscript' import {View} from '../ui/view' import {Match} from './parseroute' diff --git a/src/router/router.tsx b/src/router/router.tsx index c4dcec5..aacfe0b 100644 --- a/src/router/router.tsx +++ b/src/router/router.tsx @@ -1,7 +1,6 @@ -import {ComponentChildren, FunctionComponent} from 'preact' -import {m} from '../hyperscript' +import {Children, m} from '../hyperscript' import {createContext} from '../ui/context' -import {View} from '../ui/view' +import {StatelessView, View} from '../ui/view' import {Match, parseRoute} from './parseroute' import {RouteAttrs} from './route' @@ -38,8 +37,8 @@ export type RouterContext = LocationData & const {Provider, Consumer} = createContext(undefined) -export const Location: FunctionComponent<{ - children?: (location: RouterContext) => ComponentChildren +export const Location: StatelessView<{ + children?: (location: RouterContext) => Children }> = ({children}) => ( {(location: undefined | RouterContext) => diff --git a/src/router/switch.tsx b/src/router/switch.tsx index 37354f4..2235702 100644 --- a/src/router/switch.tsx +++ b/src/router/switch.tsx @@ -1,5 +1,5 @@ -import {ComponentChildren, VNode} from 'preact' -import {m} from '../hyperscript' +import {VNode} from 'preact' +import {Children, m} from '../hyperscript' import {View} from '../ui/view' import {Route, RouteAttrs} from './route' import {Location, RouterContext} from './router' @@ -14,7 +14,7 @@ export class Switch extends View<{ {(location: RouterContext) => { for (const child of children) { const props = child!.props! as RouteAttrs & { - children: ComponentChildren + children: Children } if (child.type !== Route) throw `Unexpected child type "${child.type}", expected "Route"` diff --git a/src/store/formstore.ts b/src/store/formstore.ts deleted file mode 100755 index c748082..0000000 --- a/src/store/formstore.ts +++ /dev/null @@ -1,55 +0,0 @@ -import m from 'mithril' - -export const FormStatus = { - Reset: 'reset', - Sending: 'sending', // {xhr} - Failure: 'error', // {errors} - Success: 'success' // {response} -} - -export type FormState = - | {type: 'reset'} - | {type: 'sending'; xhr: XMLHttpRequest} - | {type: 'error'; errors: {[key: string]: any}} - | {type: 'success'; response: {[key: string]: any}} - -export class FormStore { - data: {[key: string]: any} - status: FormState - - constructor(data = {}) { - this.data = data - this.status = {type: 'reset'} - } - - send(xhr: XMLHttpRequest) { - this.status = {type: 'sending', xhr} - } - - success(response: any) { - this.status = {type: 'success', response} - return response - } - - fail(errors: any) { - this.status = {type: 'error', errors} - return errors - } - - reset() { - switch (this.status.type) { - case 'sending': - this.status.xhr.abort() - default: - this.status = {type: 'reset'} - } - } - - setData(key: string, value: any) { - this.data[key] = value - } - - toString() { - return this.status.type - } -} diff --git a/src/store/index.ts b/src/store/index.ts index 37514f1..d79e8b1 100755 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,4 +1,3 @@ export * from './autocompletestore' export * from './carouselstore' -export * from './formstore' export * from './modalstore' diff --git a/src/styled/index.ts b/src/styled/index.ts new file mode 100644 index 0000000..9bc65a5 --- /dev/null +++ b/src/styled/index.ts @@ -0,0 +1 @@ +export * from './styled' diff --git a/src/styled/styled.tsx b/src/styled/styled.tsx new file mode 100644 index 0000000..8042ede --- /dev/null +++ b/src/styled/styled.tsx @@ -0,0 +1,96 @@ +import createCache from '@emotion/cache' +import isPropValid from '@emotion/is-prop-valid' +import {serializeStyles} from '@emotion/serialize' +import {EmotionCache, getRegisteredStyles, insertStyles} from '@emotion/utils' +import {DOMAttrs, m} from '../hyperscript' +import {createContext} from '../ui/context' +import {StatelessView} from '../ui/view' + +const EmotionCacheContext = createContext(createCache()) + +type Styled = { + (tag: string): (...args: any[]) => StatelessView + (tag: T): (...args: any[]) => T +} + +const concatClasses = (classes: Array) => + classes.filter(v => v).join(' ') + +const addClasses = (attrs: DOMAttrs, ...rest: Array): DOMAttrs => { + const {class: c1, className: c2, ...props} = attrs + return {...props, class: concatClasses([c1, c2, ...rest])} +} + +const filterProps = (props: DOMAttrs) => { + const res: DOMAttrs = {} + for (const key in props) if (isPropValid(key)) res[key] = props[key] + return res +} + +export const styled: Styled = (tag: any): any => { + const isReal = tag.__emotion_real === tag + const baseTag = (isReal && tag.__emotion_base) || tag + + return function(): StatelessView { + const args = arguments + const styles: any = /*isReal && tag.__emotion_styles !== undefined + ? tag.__emotion_styles.slice(0) + :*/ [] + const name = + (typeof tag === 'string' && tag) || + (tag.name !== 'component' && tag.name) || + tag.displayName + if (name) styles.push(`label:${name};`) + if (args[0] == null || args[0].raw === undefined) { + styles.push.apply(styles, args) + } else { + styles.push(args[0][0]) + const len = args.length + for (let i = 1; i < len; i++) { + styles.push(args[i], args[0][i]) + } + } + + const component: StatelessView = ({ + children, + className: extra, + ...attrs + }) => ( + + {(cache: EmotionCache) => { + let className = '' + const classInterpolations: Array = [] + if (typeof extra === 'string') { + className += getRegisteredStyles( + cache.registered, + classInterpolations, + extra + ) + } + // Todo: typescript typings are wrong, first two arguments are switched + const serialized = (serializeStyles as any)( + styles.concat(classInterpolations), + cache.registered, + attrs + ) + const rules = insertStyles(cache, serialized, typeof tag === 'string') + className += `${cache.key}-${serialized.name}` + const props = + typeof tag === 'string' + ? filterProps(addClasses(attrs, className)) + : addClasses(attrs, className) + const View = typeof tag === 'string' && attrs.as ? attrs.as : tag + return {children} + }} + + ) + + const Styled: any = component + if (name) Styled.displayName = name + Styled.__emotion_real = Styled + Styled.__emotion_base = baseTag + Styled.__emotion_styles = styles + + return Styled + } +} diff --git a/src/ui/breakpoint.tsx b/src/ui/breakpoint.tsx index f87ca93..d2115e9 100644 --- a/src/ui/breakpoint.tsx +++ b/src/ui/breakpoint.tsx @@ -1,10 +1,9 @@ -import {ComponentChildren} from 'preact' -import {m} from '../hyperscript' +import {Children} from '../hyperscript' import {View} from './view' export class Breakpoint extends View< { - children: (attrs: T) => ComponentChildren + children: (attrs: T) => Children } & { [breakpoint: string]: T } diff --git a/src/ui/icon.tsx b/src/ui/icon.tsx index 10459ae..4443709 100644 --- a/src/ui/icon.tsx +++ b/src/ui/icon.tsx @@ -7,6 +7,6 @@ import {View} from './view' export class Icon extends View<{icon: string} & DOMAttrs> { render() { const {children, icon, ...attrs} = this.attrs - return + return } } diff --git a/src/ui/mediaquery.tsx b/src/ui/mediaquery.tsx index b2e319d..8025174 100644 --- a/src/ui/mediaquery.tsx +++ b/src/ui/mediaquery.tsx @@ -1,12 +1,11 @@ -import {ComponentChildren} from 'preact' -import {m} from '../hyperscript' +import {Children, m} from '../hyperscript' import {Breakpoint} from './breakpoint' import {View} from './view' export class MediaQuery extends View<{ minWidth?: number maxWidth?: number - view: () => ComponentChildren + view: () => Children }> { render() { const {view, minWidth, maxWidth} = this.attrs diff --git a/src/ui/view.ts b/src/ui/view.ts index 569cfa7..276d7bf 100755 --- a/src/ui/view.ts +++ b/src/ui/view.ts @@ -1,5 +1,5 @@ -import {ChildrenType} from 'hyperscript' import {Component, FunctionComponent} from 'preact' +import {ChildrenType} from '../hyperscript' export type StatelessView

= FunctionComponent

@@ -33,8 +33,7 @@ export abstract class View extends Component { redraw = () => this.setState({}) - constructor(attrs?: A, context?: any) { - super(attrs, context) + componentWillMount() { this.onInit() } diff --git a/src/util/lazy.ts b/src/util/lazy.ts index d961186..53f5361 100755 --- a/src/util/lazy.ts +++ b/src/util/lazy.ts @@ -1,3 +1,3 @@ -// Wait for release of developit/react#1619 +// Wait for release of developit/preact#1619 //export {lazy} from 'preact' export default null diff --git a/src/util/wrap.tsx b/src/util/wrap.tsx index 0e19db5..024bdd1 100644 --- a/src/util/wrap.tsx +++ b/src/util/wrap.tsx @@ -2,17 +2,19 @@ import {Component} from 'preact' import {m} from '../hyperscript' import {StatelessView} from '../ui/view' -export function wrap>( +/*export function wrap>( layout: Component>, options: Partial = {} -): (component: T) => T { - return (component: T): T => { - const View = layout as any +): (component: T) => T {*/ +export function wrap(layout: any, options?: any): (component: any) => any { + return (component: any): any => { + const Layout = layout + const Component = component const comp: StatelessView<{}> = ({children, ...attrs}) => ( - - {children} - + + {children} + ) - return (comp as any) as T + return comp as any } } diff --git a/tsconfig.json b/tsconfig.json index 171b9f3..b028973 100755 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,6 @@ "importHelpers": true, "module": "es6", "moduleResolution": "node", - "baseUrl": "./src/", "outDir": "./dist/", "allowSyntheticDefaultImports": true, "esModuleInterop": true, From d41455cf472d846acc43b63cca70858e27f97d8c Mon Sep 17 00:00:00 2001 From: Ben Merckx Date: Thu, 23 May 2019 19:29:51 -0400 Subject: [PATCH 03/23] Alpha --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c1a8d6a..fe15bb1 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@codeurs/front", - "version": "0.14.7", + "version": "1.0.0-alpha.0", "description": "Collection of mithril utilities and components", "sideEffects": false, "module": "dist/index.js", From 2ad7d2a554049bfe400574713545f292dbb55d10 Mon Sep 17 00:00:00 2001 From: Ben Merckx Date: Wed, 29 May 2019 12:36:46 -0400 Subject: [PATCH 04/23] Upgrade typings so createElement accepts React elements --- examples/webpack.config.js | 11 +++++++- package.json | 3 ++- src/hyperscript.ts | 51 +++++++++++++++++++++++++++++++++++--- yarn.lock | 18 ++++++++++++++ 4 files changed, 78 insertions(+), 5 deletions(-) diff --git a/examples/webpack.config.js b/examples/webpack.config.js index dbedb95..780803d 100755 --- a/examples/webpack.config.js +++ b/examples/webpack.config.js @@ -1,5 +1,14 @@ const path = require('path') -module.exports = require('@codeurs/packer')('src/index.tsx', 'dist/bundle.js', { +const pack = require('@codeurs/packer')('src/index.tsx', 'dist/bundle.js', { include: [path.resolve('../node_modules/@codeurs/front/dist')] }) + +module.exports = (...args) => { + const config = pack(...args) + config.resolve.alias = { + react: 'preact/compat', + 'react-dom': 'preact/compat' + } + return config +} diff --git a/package.json b/package.json index fe15bb1..327f369 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@codeurs/front", - "version": "1.0.0-alpha.0", + "version": "1.0.0-alpha.1", "description": "Collection of mithril utilities and components", "sideEffects": false, "module": "dist/index.js", @@ -75,6 +75,7 @@ "@types/jump.js": "^1.0.2", "@types/load-google-maps-api": "^1.3.0", "@types/mithril": "^1.1.16", + "@types/react": "^16.8.19", "@types/throttle-debounce": "^1.1.1", "classnames": "^2.2.5", "core-js": "^3.0.1", diff --git a/src/hyperscript.ts b/src/hyperscript.ts index 9ae2eaa..0ce50a8 100755 --- a/src/hyperscript.ts +++ b/src/hyperscript.ts @@ -1,6 +1,35 @@ -import {ComponentChildren, h} from 'preact' +import { + ComponentChildren, + ComponentType as ComponentTypePreact, + createElement, + JSX as JSXPreact +} from 'preact' +import {ComponentType as ComponentTypeReact} from 'react' -export {h as m} from 'preact' +type Key = string | number | any + +interface Attributes { + key?: Key + jsx?: boolean +} + +type Hyperscript = { + ( + type: string, + props: Record | null, + ...children: Array + ): ComponentChildren +

( + type: ComponentTypePreact

, + props: Attributes & P | null, + ...children: Array + ): ComponentChildren +

( + type: ComponentTypeReact

, + props: Attributes & P | null, + ...children: Array + ): ComponentChildren +} export type DOMAttrs = {[key: string]: any} @@ -8,4 +37,20 @@ export type ChildrenType = A extends {children?: any} ? A['children'] : ComponentChildren -export type Children = ComponentChildren \ No newline at end of file +export type Children = ComponentChildren + +export const m: Hyperscript = createElement as any + +export namespace m { + export namespace JSX { + export interface ElementAttributesProperty { + props: {} + } + + export interface ElementChildrenAttribute { + children: {} + } + + export interface IntrinsicElements extends JSXPreact.IntrinsicElements {} + } +} diff --git a/yarn.lock b/yarn.lock index 5a89670..b9aa1b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1074,11 +1074,24 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.27.tgz#eb3843f15d0ba0986cc7e4d734d2ee8b50709ef8" integrity sha512-e9wgeY6gaY21on3ve0xAjgBVjGDWq/xUteK0ujsE53bUoxycMkqfnkUgMt6ffZtykZ5X12Mg3T7Pw4TRCObDKg== +"@types/prop-types@*": + version "15.7.1" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.1.tgz#f1a11e7babb0c3cad68100be381d1e064c68f1f6" + integrity sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg== + "@types/q@^1.5.1": version "1.5.1" resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.1.tgz#48fd98c1561fe718b61733daed46ff115b496e18" integrity sha512-eqz8c/0kwNi/OEHQfvIuJVLTst3in0e7uTKeuY+WL/zfKn0xVujOTp42bS/vUUokhK5P2BppLd9JXMOMHcgbjA== +"@types/react@^16.8.19": + version "16.8.19" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.19.tgz#629154ef05e2e1985cdde94477deefd823ad9be3" + integrity sha512-QzEzjrd1zFzY9cDlbIiFvdr+YUmefuuRYrPxmkwG0UQv5XF35gFIi7a95m1bNVcFU0VimxSZ5QVGSiBmlggQXQ== + dependencies: + "@types/prop-types" "*" + csstype "^2.2.0" + "@types/tapable@*", "@types/tapable@^1.0.4": version "1.0.4" resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.4.tgz#b4ffc7dc97b498c969b360a41eee247f82616370" @@ -2921,6 +2934,11 @@ cssstyle@^1.0.0: dependencies: cssom "0.3.x" +csstype@^2.2.0: + version "2.6.5" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.5.tgz#1cd1dff742ebf4d7c991470ae71e12bb6751e034" + integrity sha512-JsTaiksRsel5n7XwqPAfB0l3TFKdpjW/kgAELf9vrb5adGA7UCPLajKK5s3nFrcFm3Rkyp/Qkgl73ENc1UY3cA== + csstype@^2.5.7: version "2.6.2" resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.2.tgz#3043d5e065454579afc7478a18de41909c8a2f01" From 99797cc8b2de94ff3bc9878059949959fe77b418 Mon Sep 17 00:00:00 2001 From: Ben Merckx Date: Mon, 3 Jun 2019 10:29:19 +0200 Subject: [PATCH 05/23] Re-add lazy for now --- examples/src/index.tsx | 6 +++--- package.json | 2 +- src/hyperscript.ts | 17 +++++------------ src/ui/view.ts | 4 ++-- src/util/lazy.ts | 3 --- src/util/lazy.tsx | 27 +++++++++++++++++++++++++++ yarn.lock | 14 +++++++------- 7 files changed, 45 insertions(+), 28 deletions(-) delete mode 100755 src/util/lazy.ts create mode 100644 src/util/lazy.tsx diff --git a/examples/src/index.tsx b/examples/src/index.tsx index af2c2c8..82503ce 100644 --- a/examples/src/index.tsx +++ b/examples/src/index.tsx @@ -6,6 +6,7 @@ import { HistoryRouter, Image, ImageResizer, + lazy, Link, m, Modal, @@ -19,7 +20,8 @@ import { import testImage from 'assets/test.jpg' import svgImage from 'assets/test.svg' import {render} from 'preact' -import Slider from './views/sliderpage' + +const Slider = lazy(() => import('./views/sliderpage')) //import Autocomplete from 'views/autocomplete' //const Map = lazy(() => import('./views/map')) @@ -125,7 +127,6 @@ class Examples extends View { Autocomplete Modal Images - Maps Back home

@@ -141,7 +142,6 @@ class Examples extends View { )} {ModalExample} - {Map} {() => ( = + | ComponentTypeReact

+ | ComponentTypePreact

type Hyperscript = { ( @@ -20,12 +18,7 @@ type Hyperscript = { ...children: Array ): ComponentChildren

( - type: ComponentTypePreact

, - props: Attributes & P | null, - ...children: Array - ): ComponentChildren -

( - type: ComponentTypeReact

, + type: ComponentType

, props: Attributes & P | null, ...children: Array ): ComponentChildren diff --git a/src/ui/view.ts b/src/ui/view.ts index 276d7bf..5f137f1 100755 --- a/src/ui/view.ts +++ b/src/ui/view.ts @@ -1,4 +1,4 @@ -import {Component, FunctionComponent} from 'preact' +import {Component, FunctionComponent, RenderableProps} from 'preact' import {ChildrenType} from '../hyperscript' export type StatelessView

= FunctionComponent

@@ -10,7 +10,7 @@ export abstract class View extends Component { return this.base } - get attrs(): A { + get attrs(): RenderableProps { return this.props } diff --git a/src/util/lazy.ts b/src/util/lazy.ts deleted file mode 100755 index 53f5361..0000000 --- a/src/util/lazy.ts +++ /dev/null @@ -1,3 +0,0 @@ -// Wait for release of developit/preact#1619 -//export {lazy} from 'preact' -export default null diff --git a/src/util/lazy.tsx b/src/util/lazy.tsx new file mode 100644 index 0000000..4b13004 --- /dev/null +++ b/src/util/lazy.tsx @@ -0,0 +1,27 @@ +// Preact's lazy is not yet ready for prime time... +import {ComponentType, m} from '../hyperscript' +import {View} from '../ui/view' + +export const lazy = ( + fetch: () => Promise<{default: ComponentType}> +) => { + return class LazyLoad extends View { + state: { + component?: ComponentType + } = {} + + onInit() { + fetch() + .then(component => this.setState({component: component.default})) + .catch(console.error) + } + + render() { + const {children, ...attrs} = this.attrs + const {component} = this.state + const Component = component as any + if (!Component) return null + return {children} + } + } +} diff --git a/yarn.lock b/yarn.lock index b9aa1b4..db75525 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3903,9 +3903,9 @@ fsevents@^1.2.3, fsevents@^1.2.7: node-pre-gyp "^0.10.0" fstream@^1.0.0, fstream@^1.0.2: - version "1.0.11" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" - integrity sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE= + version "1.0.12" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" + integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== dependencies: graceful-fs "^4.1.2" inherits "~2.0.0" @@ -7242,10 +7242,10 @@ postcss@^7.0.6: source-map "^0.6.1" supports-color "^6.1.0" -preact@^10.0.0-beta.1: - version "10.0.0-beta.1" - resolved "https://registry.yarnpkg.com/preact/-/preact-10.0.0-beta.1.tgz#f7d524668db72ed74e99d447f09600fa4db0b186" - integrity sha512-1uhj3JXOEzEPUof5t/iQW5heEvTOpexwS0FLAjJQfgfT8OCXICyzI2TwkYyFJ+W9q9GsDmhZgFagH2G0akFveQ== +preact@^10.0.0-beta.2: + version "10.0.0-beta.2" + resolved "https://registry.yarnpkg.com/preact/-/preact-10.0.0-beta.2.tgz#659b6520eb41d5a3d178a61b3311e6ae79d8cf56" + integrity sha512-sKu2tdECRcmG8B2Q0GIAhTR95PhaWy15RkYFgpzfkEQLo7qqnE5lYQO2ccHNTfwuzj0LVPRdG71s5QjhnRyVbQ== prelude-ls@~1.1.2: version "1.1.2" From 843a03415b0950ba527207d87e1f6eb69d2935dc Mon Sep 17 00:00:00 2001 From: Ben Merckx Date: Mon, 3 Jun 2019 10:29:42 +0200 Subject: [PATCH 06/23] Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8445aba..99cee3f 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@codeurs/front", - "version": "1.0.0-alpha.1", + "version": "1.0.0-alpha.2", "description": "Collection of mithril utilities and components", "sideEffects": false, "module": "dist/index.js", From 10c2de37b6d4cd1c56dd6ea41109c72d61857061 Mon Sep 17 00:00:00 2001 From: Ben Merckx Date: Mon, 3 Jun 2019 10:39:41 +0200 Subject: [PATCH 07/23] Cleanup dependencies - todo: check why core-js is not detected as external dependency (via packer) --- examples/src/views/map.less | 5 - examples/src/views/map.ts | 82 --------- package.json | 12 +- types/object-to-formdata/index.d.ts | 4 - yarn.lock | 260 ++-------------------------- 5 files changed, 18 insertions(+), 345 deletions(-) delete mode 100755 examples/src/views/map.less delete mode 100755 examples/src/views/map.ts delete mode 100755 types/object-to-formdata/index.d.ts diff --git a/examples/src/views/map.less b/examples/src/views/map.less deleted file mode 100755 index 7fb4827..0000000 --- a/examples/src/views/map.less +++ /dev/null @@ -1,5 +0,0 @@ -.map { - &-maps { - height: 500px; - } -} \ No newline at end of file diff --git a/examples/src/views/map.ts b/examples/src/views/map.ts deleted file mode 100755 index 1f99058..0000000 --- a/examples/src/views/map.ts +++ /dev/null @@ -1,82 +0,0 @@ -import './map.less' - -import {m, Maps, Marker, View} from '@codeurs/front' -import {debounce} from 'throttle-debounce' - -export default class Map extends View { - markers = 1 - zoom = 4.5 - center = {lat: 50.3115184, lng: 6.3301855} - - setMapProperties = debounce(300, e => { - let redraw = false - const map = e.target - const zoomLevel = map.getZoom() - if (zoomLevel !== this.zoom) { - redraw = true - this.zoom = zoomLevel - } - const center = map.getCenter() - if (!center.equals(new google.maps.LatLng( - this.center.lat, this.center.lng - ))) { - redraw = true - this.center = center.toJSON() - } - if (redraw) m.redraw() - }) - - changeMapProperty = e => { - e.redraw = false - this.setMapProperties(e) - } - - render() { - return m('.map', - m('label', - 'Add markers: ', - m('input[type=range]', { - value: this.markers, - min: 1, max: 50, - step: 1, - oninput: e => this.markers = parseInt(e.target.value, 10) - }) - ), - m('label', - 'Zoom: ', - m('input[type=range]', { - value: this.zoom, - min: 1, max: 15, - step: 1, - oninput: e => this.zoom = parseFloat(e.target.value) - }) - ), - m('.map-maps', - m(Maps, - { - apiKey: 'AIzaSyDSOLxTZy_56sksxVCD970SpqJPFxXe5Ok', - center: this.center, - zoom: this.zoom, - disableDefaultUI: true, - on: { - center_changed: this.changeMapProperty, - zoom_changed: this.changeMapProperty - } - }, - Array.from(Array(this.markers)) - .map((_, i) => - m(Marker, { - position: { - lat: this.center.lat, - lng: this.center.lng + i * 1.5 - }, - on: { - click: () => console.log(i) - } - }) - ) - ) - ) - ) - } -} \ No newline at end of file diff --git a/package.json b/package.json index 99cee3f..b4e806a 100755 --- a/package.json +++ b/package.json @@ -71,19 +71,13 @@ "@emotion/utils": "^0.11.1", "@types/classnames": "^2.2.6", "@types/deep-equal": "^1.0.1", - "@types/googlemaps": "^3.30.16", - "@types/jump.js": "^1.0.2", - "@types/load-google-maps-api": "^1.3.0", "@types/mithril": "^1.1.16", "@types/react": "^16.8.19", "@types/throttle-debounce": "^1.1.1", "classnames": "^2.2.5", - "core-js": "^3.0.1", + "core-js": "^3.1.3", "deep-equal": "^1.0.1", - "jump.js": "codeurs/jump.js", - "load-google-maps-api": "^1.3.2", "mithril": "^1.1.6", - "object-to-formdata": "codeurs/object-to-formdata", "popmotion": "^8.6.4", "preact": "^10.0.0-beta.2", "scroll-into-view-if-needed": "^2.2.20", @@ -91,7 +85,7 @@ "tslib": "^1.9.3" }, "devDependencies": { - "@codeurs/packer": "=0.14.1", + "@codeurs/packer": "^0.14.3", "@types/jest": "^24.0.9", "babel-codemod": "^2.1.2", "babel-core": "^6.26.3", @@ -104,8 +98,6 @@ "npm-run-all": "^4.1.3", "prettier": "^1.15.3", "rimraf": "^2.6.3", - "serve": "^10.1.2", - "style-loader": "^0.23.1", "symlink-dir": "^2.0.2", "ts-jest": "^24.0.0", "wright": "^2.0.0-beta35" diff --git a/types/object-to-formdata/index.d.ts b/types/object-to-formdata/index.d.ts deleted file mode 100755 index 67aff8c..0000000 --- a/types/object-to-formdata/index.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module 'object-to-formdata' { - const objectToFormData: (data: Object) => string - export = objectToFormData -} diff --git a/yarn.lock b/yarn.lock index db75525..155bc90 100644 --- a/yarn.lock +++ b/yarn.lock @@ -852,10 +852,10 @@ lodash "^4.17.11" to-fast-properties "^2.0.0" -"@codeurs/packer@=0.14.1": - version "0.14.1" - resolved "https://registry.yarnpkg.com/@codeurs/packer/-/packer-0.14.1.tgz#1d382a99727108f3060148dbffe856aa830914fc" - integrity sha512-vkceHNpVFJCVwo0qdKAf4PLmP8yaWKfo9Su/+bOe8GOOVUw1i4WAY7udiiHz9MPaxJz+RX2dT/rdmJESohQmYg== +"@codeurs/packer@^0.14.3": + version "0.14.3" + resolved "https://registry.yarnpkg.com/@codeurs/packer/-/packer-0.14.3.tgz#0e5ede87a8de7f5750bfbb37a5d9f34ced9a4281" + integrity sha512-zNlwJhiPn5442wS3ye8ao8fhLDccFoWQ6UCmrGDz+qFdZ1AC85BZ/hFPVCWEL5B8bPluJF7/DjRGuudKAaB0og== dependencies: "@babel/core" "^7.4.3" "@babel/plugin-proposal-class-properties" "^7.4.0" @@ -1023,11 +1023,6 @@ resolved "https://registry.yarnpkg.com/@types/deep-equal/-/deep-equal-1.0.1.tgz#71cfabb247c22bcc16d536111f50c0ed12476b03" integrity sha512-mMUu4nWHLBlHtxXY17Fg6+ucS/MnndyOWyOe7MmwkoMYxvfQU2ajtRaEvqSUv+aVkMqH/C0NCI8UoVfRNQ10yg== -"@types/googlemaps@*", "@types/googlemaps@^3.30.16": - version "3.30.16" - resolved "https://registry.yarnpkg.com/@types/googlemaps/-/googlemaps-3.30.16.tgz#3264d5ef7c3a92fab2a9f00e09e3247866ff3532" - integrity sha512-6OZ64ahLzYfzuSr71y4jAHZXiwxjvvEM2bfF2tzjIc9KUZVbO30SkYa8WewTsR8aM5BFz/uBiNlZ14eRLXYS0g== - "@types/jest-diff@*": version "20.0.1" resolved "https://registry.yarnpkg.com/@types/jest-diff/-/jest-diff-20.0.1.tgz#35cc15b9c4f30a18ef21852e255fdb02f6d59b89" @@ -1040,18 +1035,6 @@ dependencies: "@types/jest-diff" "*" -"@types/jump.js@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@types/jump.js/-/jump.js-1.0.2.tgz#9eb1cf571e406bb3514a062008b6889ab9984160" - integrity sha1-nrHPVx5Aa7NRSgYgCLaImrmYQWA= - -"@types/load-google-maps-api@^1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@types/load-google-maps-api/-/load-google-maps-api-1.3.0.tgz#40a610653fb5381579c921136186d27b77946c37" - integrity sha512-rUvxFoheh57ast18Cn8haBnonEAPxS/WZIcUcg6Vhgh33rjONVD9oBFqiV/JO7j0IgXPgu54BeKft/nm3UYMsg== - dependencies: - "@types/googlemaps" "*" - "@types/mithril@^1.1.16": version "1.1.16" resolved "https://registry.yarnpkg.com/@types/mithril/-/mithril-1.1.16.tgz#97647157a4d6dacee8fc86414578c56723dfd715" @@ -1276,11 +1259,6 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== -"@zeit/schemas@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@zeit/schemas/-/schemas-2.6.0.tgz#004e8e553b4cd53d538bd38eac7bcbf58a867fe3" - integrity sha512-uUrgZ8AxS+Lio0fZKAipJjAh415JyrOZowliZAzmnJSsf7piVL5w+G0+gFJ0KSu3QRhvui/7zuvpLz03YjXAhg== - abab@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.0.tgz#aba0ab4c5eee2d4c79d3487d85450fb2376ebb0f" @@ -1342,16 +1320,6 @@ ajv-keywords@^3.1.0: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.0.tgz#4b831e7b531415a7cc518cd404e73f6193c6349d" integrity sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw== -ajv@6.5.3: - version "6.5.3" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.3.tgz#71a569d189ecf4f4f321224fecb166f071dd90f9" - integrity sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg== - dependencies: - fast-deep-equal "^2.0.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - ajv@^5.0.0: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" @@ -1377,13 +1345,6 @@ alphanum-sort@^1.0.0: resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= -ansi-align@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" - integrity sha1-w2rsy6VjuJzrVW82kPCx2eNUf38= - dependencies: - string-width "^2.0.0" - ansi-escapes@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" @@ -1446,11 +1407,6 @@ aproba@^1.0.3, aproba@^1.1.1: resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== -arch@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/arch/-/arch-2.1.1.tgz#8f5c2731aa35a30929221bb0640eed65175ec84e" - integrity sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg== - are-we-there-yet@~1.1.2: version "1.1.5" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" @@ -1459,11 +1415,6 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" -arg@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/arg/-/arg-2.0.0.tgz#c06e7ff69ab05b3a4a03ebe0407fac4cba657545" - integrity sha512-XxNTUzKnz1ctK3ZIcI2XUPlD96wbHP2nGqkPKpvk/HNRlPveYrXIVSTk9m3LcqOgDPg3B1nMvdV/K8wZd7PG4w== - argparse@^1.0.6, argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -1942,19 +1893,6 @@ boolbase@^1.0.0, boolbase@~1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= -boxen@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" - integrity sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw== - dependencies: - ansi-align "^2.0.0" - camelcase "^4.0.0" - chalk "^2.0.1" - cli-boxes "^1.0.0" - string-width "^2.0.0" - term-size "^1.2.0" - widest-line "^2.0.0" - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -2205,7 +2143,7 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.0.0.tgz#fb7eb569b72ad7a45812f93fd9430a3e410b3dd3" integrity sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw== -camelcase@^4.0.0, camelcase@^4.1.0: +camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= @@ -2252,15 +2190,6 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chalk@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" - integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - chalk@^1.0.0, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -2370,11 +2299,6 @@ classnames@^2.2.5: resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== -cli-boxes@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" - integrity sha1-T6kXw+WclKAEzWH47lCdplFocUM= - cli-cursor@^2.0.0, cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -2395,14 +2319,6 @@ cli-width@^2.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= -clipboardy@1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-1.2.3.tgz#0526361bf78724c1f20be248d428e365433c07ef" - integrity sha512-2WNImOvCRe6r63Gk9pShfkwXsVtKCroMAevIbiae021mS850UkWPbevxsBz3tnvjZIEGvlwaqCPsw+4ulzNgJA== - dependencies: - arch "^2.1.0" - execa "^0.8.0" - cliui@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" @@ -2521,26 +2437,6 @@ component-emitter@^1.2.1: resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= -compressible@~2.0.14: - version "2.0.16" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.16.tgz#a49bf9858f3821b64ce1be0296afc7380466a77f" - integrity sha512-JQfEOdnI7dASwCuSPWIeVYwc/zMsu/+tRhoUvEfXz2gxOA2DNjmG5vhtFdBlhWPPGo+RdT9S3tgc/uH5qgDiiA== - dependencies: - mime-db ">= 1.38.0 < 2" - -compression@1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.3.tgz#27e0e176aaf260f7f2c2813c3e440adb9f1993db" - integrity sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg== - dependencies: - accepts "~1.3.5" - bytes "3.0.0" - compressible "~2.0.14" - debug "2.6.9" - on-headers "~1.0.1" - safe-buffer "5.1.2" - vary "~1.1.2" - compute-scroll-into-view@1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.11.tgz#7ff0a57f9aeda6314132d8994cce7aeca794fecf" @@ -2659,6 +2555,11 @@ core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.7: resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.5.tgz#44bc8d249e7fb2ff5d00e0341a7ffb94fbf67895" integrity sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A== +core-js@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.1.3.tgz#95700bca5f248f5f78c0ec63e784eca663ec4138" + integrity sha512-PWZ+ZfuaKf178BIAg+CRsljwjIMRV8MY00CbZczkR6Zk5LfkSkjGoaab3+bqRQWVITNZxQB7TFYz+CFcyuamvA== + core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -3440,19 +3341,6 @@ execa@^0.7.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" - integrity sha1-2NdrvBtVIX7RkP1t1J08d07PyNo= - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - execa@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" @@ -3648,13 +3536,6 @@ fast-levenshtein@~2.0.4: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= -fast-url-parser@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" - integrity sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0= - dependencies: - punycode "^1.3.2" - fb-watchman@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.0.tgz#54e9abf7dfa2f26cd9b1636c588c1afc05de5d58" @@ -5420,10 +5301,6 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -jump.js@codeurs/jump.js: - version "1.0.3" - resolved "https://codeload.github.com/codeurs/jump.js/tar.gz/6e735f934a373a346bb892093839330c3b212d8c" - keyv@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" @@ -5592,11 +5469,6 @@ listr@^0.14.2: p-map "^2.0.0" rxjs "^6.3.3" -load-google-maps-api@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/load-google-maps-api/-/load-google-maps-api-1.3.2.tgz#ae1b0052b9ddf8f5a93486836a9d2b145b9d2205" - integrity sha512-uc0kW24xpTtw2g1g3qIXGgFF+u2Rlp+7a1Hze7Esuz24tHkbblJH0LZGjRVZuL7mqIxEWp+lP7ANt1/OiZRJbA== - load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" @@ -5913,23 +5785,11 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -"mime-db@>= 1.38.0 < 2", mime-db@~1.38.0: +mime-db@~1.38.0: version "1.38.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.38.0.tgz#1a2aab16da9eb167b49c6e4df2d9c68d63d8e2ad" integrity sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg== -mime-db@~1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" - integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== - -mime-types@2.1.18: - version "2.1.18" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" - integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== - dependencies: - mime-db "~1.33.0" - mime-types@^2.1.12, mime-types@~2.1.18, mime-types@~2.1.19: version "2.1.22" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.22.tgz#fe6b355a190926ab7698c9a0556a11199b2199bd" @@ -5977,7 +5837,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= -minimatch@3.0.4, minimatch@^3.0.3, minimatch@^3.0.4: +minimatch@^3.0.3, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -6451,10 +6311,6 @@ object-path@0.x.x: resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.4.tgz#370ae752fbf37de3ea70a861c23bba8915691949" integrity sha1-NwrnUvvzfePqcKhhwju6iRVpGUk= -object-to-formdata@codeurs/object-to-formdata: - version "1.2.2" - resolved "https://codeload.github.com/codeurs/object-to-formdata/tar.gz/0247b316cdabb54553a66ffee03b9ae9506111d6" - object-visit@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" @@ -6494,11 +6350,6 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" -on-headers@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== - once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -6757,7 +6608,7 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-is-inside@1.0.2, path-is-inside@^1.0.1, path-is-inside@^1.0.2: +path-is-inside@^1.0.1, path-is-inside@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= @@ -6777,11 +6628,6 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= -path-to-regexp@2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45" - integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ== - path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" @@ -7385,7 +7231,7 @@ punycode@1.3.2: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= -punycode@^1.2.4, punycode@^1.3.2, punycode@^1.4.1: +punycode@^1.2.4, punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= @@ -7444,7 +7290,7 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" -range-parser@1.2.0, range-parser@~1.2.0: +range-parser@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= @@ -7467,7 +7313,7 @@ raw-loader@^2.0.0: loader-utils "^1.1.0" schema-utils "^1.0.0" -rc@^1.0.1, rc@^1.1.6, rc@^1.2.7: +rc@^1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -7633,21 +7479,6 @@ regexpu-core@^4.5.4: unicode-match-property-ecmascript "^1.0.4" unicode-match-property-value-ecmascript "^1.1.0" -registry-auth-token@3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20" - integrity sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ== - dependencies: - rc "^1.1.6" - safe-buffer "^5.0.1" - -registry-url@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" - integrity sha1-PU74cPc93h138M+aOBQyRE4XSUI= - dependencies: - rc "^1.0.1" - regjsgen@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.0.tgz#a7634dc08f89209c2049adda3525711fb97265dd" @@ -7999,20 +7830,6 @@ serialize-javascript@^1.4.0: resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.6.1.tgz#4d1f697ec49429a847ca6f442a2a755126c4d879" integrity sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw== -serve-handler@5.0.8: - version "5.0.8" - resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-5.0.8.tgz#790dbe340dabf1d61bdbaa02ea37dcab372377a8" - integrity sha512-pqk0SChbBLLHfMIxQ55czjdiW7tj2cFy53svvP8e5VqEN/uB/QpfiTJ8k1uIYeFTDVoi+FGi5aqXScuu88bymg== - dependencies: - bytes "3.0.0" - content-disposition "0.5.2" - fast-url-parser "1.1.3" - mime-types "2.1.18" - minimatch "3.0.4" - path-is-inside "1.0.2" - path-to-regexp "2.2.1" - range-parser "1.2.0" - serve-static@1.13.2: version "1.13.2" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" @@ -8023,21 +7840,6 @@ serve-static@1.13.2: parseurl "~1.3.2" send "0.16.2" -serve@^10.1.2: - version "10.1.2" - resolved "https://registry.yarnpkg.com/serve/-/serve-10.1.2.tgz#805917f2692ed5d8720bbd1981ac3974d38b7d8d" - integrity sha512-TVH35uwndRlCqSeX3grR3Ntrjx2aBTeu6sx+zTD2CzN2N/rHuEDTvxiBwWbrellJNyWiQFz2xZmoW+UxV+Zahg== - dependencies: - "@zeit/schemas" "2.6.0" - ajv "6.5.3" - arg "2.0.0" - boxen "1.3.0" - chalk "2.4.1" - clipboardy "1.2.3" - compression "1.7.3" - serve-handler "5.0.8" - update-check "1.5.2" - set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -8502,14 +8304,6 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -style-loader@^0.23.1: - version "0.23.1" - resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.23.1.tgz#cb9154606f3e771ab6c4ab637026a1049174d925" - integrity sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg== - dependencies: - loader-utils "^1.1.0" - schema-utils "^1.0.0" - style-value-types@^3.0.6: version "3.0.7" resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-3.0.7.tgz#6e7a22cc8b1a4465193268ed66ad5f2a82579054" @@ -8676,13 +8470,6 @@ tar@^4: safe-buffer "^5.1.2" yallist "^3.0.2" -term-size@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" - integrity sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk= - dependencies: - execa "^0.7.0" - terser-webpack-plugin@^1.1.0: version "1.2.3" resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.2.3.tgz#3f98bc902fac3e5d0de730869f50668561262ec8" @@ -9093,14 +8880,6 @@ upath@^1.1.0: resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd" integrity sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw== -update-check@1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/update-check/-/update-check-1.5.2.tgz#2fe09f725c543440b3d7dabe8971f2d5caaedc28" - integrity sha512-1TrmYLuLj/5ZovwUS7fFd1jMH3NnFDN1y1A8dboedIDt7zs/zJMo6TwwlhYKkSeEwzleeiSBV5/3c9ufAQWDaQ== - dependencies: - registry-auth-token "3.3.2" - registry-url "3.1.0" - uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" @@ -9425,13 +9204,6 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2 || 2" -widest-line@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc" - integrity sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA== - dependencies: - string-width "^2.1.1" - wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" From 53a35fbab17f536ac401b720baff53c66fab8ef4 Mon Sep 17 00:00:00 2001 From: Ben Merckx Date: Fri, 7 Jun 2019 10:59:56 +0200 Subject: [PATCH 08/23] Fix image resizing --- examples/src/index.tsx | 3 ++- examples/src/views/index.ts | 4 ---- src/deprecated/picture.tsx | 2 +- src/ui/image.tsx | 27 +++++++++++++-------------- 4 files changed, 16 insertions(+), 20 deletions(-) delete mode 100755 examples/src/views/index.ts diff --git a/examples/src/index.tsx b/examples/src/index.tsx index 82503ce..412db62 100644 --- a/examples/src/index.tsx +++ b/examples/src/index.tsx @@ -19,7 +19,7 @@ import { } from '@codeurs/front' import testImage from 'assets/test.jpg' import svgImage from 'assets/test.svg' -import {render} from 'preact' +import {h, render} from 'preact' const Slider = lazy(() => import('./views/sliderpage')) @@ -83,6 +83,7 @@ const Images = ({children}) => (

{ + console.log(attrs, container) return {...attrs} }} > diff --git a/examples/src/views/index.ts b/examples/src/views/index.ts deleted file mode 100755 index 883be09..0000000 --- a/examples/src/views/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { default as autocomplete } from './autocomplete'; -export { default as map } from './map'; -export { default as sliderpage } from './sliderpage'; -export { default as countries } from './countries'; diff --git a/src/deprecated/picture.tsx b/src/deprecated/picture.tsx index ca026e6..879f781 100644 --- a/src/deprecated/picture.tsx +++ b/src/deprecated/picture.tsx @@ -50,7 +50,7 @@ export class Picture extends View< }} > { +class ImageBase extends View { loader?: typeof BrowserImage onCreate() { @@ -170,9 +170,17 @@ export class ImageResizer extends View<{ } } -export class Image extends View { +export class Image extends View { cached: undefined | string resolved: undefined | ImageAttrs + resolve?: (dom: HTMLElement) => void + + onCreate(dom: HTMLElement) { + if (!dom || !this.resolve) return + this.resolve(dom) + this.redraw() + } + render() { const { children, @@ -194,7 +202,7 @@ export class Image extends View { {(resize: Resizer | undefined) => { if (!resize) return {children} - const resolve = (dom: HTMLElement) => { + this.resolve = (dom: HTMLElement) => { this.resolved = resize(this.attrs, { width: dom.offsetWidth, height: dom.offsetHeight @@ -203,20 +211,11 @@ export class Image extends View { } if (!this.dom) return ( -
{ - if (!dom) return - resolve(dom as HTMLElement) - this.redraw() - } - }} - > +
{children}
) - if (this.cached !== src) resolve(this.dom as HTMLElement) + if (this.cached !== src) this.resolve(this.dom as HTMLElement) return ( this.resolved && ( {children} From 0093b30689a9514b9455a8944d9f1b1b7e723078 Mon Sep 17 00:00:00 2001 From: Ben Merckx Date: Fri, 7 Jun 2019 11:00:34 +0200 Subject: [PATCH 09/23] 1.0.0-alpha.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b4e806a..639dc90 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@codeurs/front", - "version": "1.0.0-alpha.2", + "version": "1.0.0-alpha.3", "description": "Collection of mithril utilities and components", "sideEffects": false, "module": "dist/index.js", From 69f149cb1daccf8a2fdf536e101261b6a235eb8d Mon Sep 17 00:00:00 2001 From: Ben Merckx Date: Fri, 7 Jun 2019 11:39:54 +0200 Subject: [PATCH 10/23] Pin dependencies --- package.json | 2 +- src/ui/image.tsx | 2 +- yarn.lock | 20 ++++++++++---------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 639dc90..f155d7e 100755 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "@types/react": "^16.8.19", "@types/throttle-debounce": "^1.1.1", "classnames": "^2.2.5", - "core-js": "^3.1.3", + "core-js": "^3.0.1", "deep-equal": "^1.0.1", "mithril": "^1.1.6", "popmotion": "^8.6.4", diff --git a/src/ui/image.tsx b/src/ui/image.tsx index fabcef4..6b7539b 100644 --- a/src/ui/image.tsx +++ b/src/ui/image.tsx @@ -1,6 +1,6 @@ import './image.less' -import {createContext, h} from 'preact' +import {createContext} from 'preact' import {DOMAttrs, m} from '../hyperscript' import {addClasses} from '../util/classes' import {contain, cover} from '../util/fit' diff --git a/yarn.lock b/yarn.lock index 155bc90..6eb8f41 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1557,7 +1557,7 @@ async@^1.5.2: resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= -async@^2.4.1, async@^2.5.0, async@^2.6.1: +async@^2.4.1, async@^2.6.1: version "2.6.2" resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" integrity sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg== @@ -2555,11 +2555,6 @@ core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.7: resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.5.tgz#44bc8d249e7fb2ff5d00e0341a7ffb94fbf67895" integrity sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A== -core-js@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.1.3.tgz#95700bca5f248f5f78c0ec63e784eca663ec4138" - integrity sha512-PWZ+ZfuaKf178BIAg+CRsljwjIMRV8MY00CbZczkR6Zk5LfkSkjGoaab3+bqRQWVITNZxQB7TFYz+CFcyuamvA== - core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -3989,11 +3984,11 @@ gzip-size@^5.0.0: pify "^3.0.0" handlebars@^4.0.5, handlebars@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.0.tgz#0d6a6f34ff1f63cecec8423aa4169827bf787c3a" - integrity sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w== + version "4.1.2" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.2.tgz#b6b37c1ced0306b221e094fc7aca3ec23b131b67" + integrity sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw== dependencies: - async "^2.5.0" + neo-async "^2.6.0" optimist "^0.6.1" source-map "^0.6.1" optionalDependencies: @@ -6016,6 +6011,11 @@ neo-async@^2.5.0: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.0.tgz#b9d15e4d71c6762908654b5183ed38b753340835" integrity sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA== +neo-async@^2.6.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" + integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" From 420ca2a9fb27c6189e12c630981e45c029378eb9 Mon Sep 17 00:00:00 2001 From: Ben Merckx Date: Fri, 7 Jun 2019 11:40:21 +0200 Subject: [PATCH 11/23] 1.0.0-alpha.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f155d7e..eeb5266 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@codeurs/front", - "version": "1.0.0-alpha.3", + "version": "1.0.0-alpha.4", "description": "Collection of mithril utilities and components", "sideEffects": false, "module": "dist/index.js", From f2c02ec13e8c09ad6ea1e7a127e087b17f256e5a Mon Sep 17 00:00:00 2001 From: Ben Merckx Date: Tue, 11 Jun 2019 15:33:39 +0200 Subject: [PATCH 12/23] Use lazy cache --- src/util/lazy.tsx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/util/lazy.tsx b/src/util/lazy.tsx index 4b13004..10fe720 100644 --- a/src/util/lazy.tsx +++ b/src/util/lazy.tsx @@ -2,24 +2,23 @@ import {ComponentType, m} from '../hyperscript' import {View} from '../ui/view' +const cache = new Map>() + export const lazy = ( fetch: () => Promise<{default: ComponentType}> ) => { return class LazyLoad extends View { - state: { - component?: ComponentType - } = {} - onInit() { + if (cache.has(fetch)) return fetch() - .then(component => this.setState({component: component.default})) + .then(component => cache.set(fetch, component.default)) + .then(this.redraw) .catch(console.error) } render() { const {children, ...attrs} = this.attrs - const {component} = this.state - const Component = component as any + const Component = cache.get(fetch) if (!Component) return null return {children} } From 2bbf34c62bc5e9ca52d9777db5cc726aad1465d3 Mon Sep 17 00:00:00 2001 From: Ben Merckx Date: Wed, 19 Jun 2019 17:37:46 +0200 Subject: [PATCH 13/23] Use react typings exclusively --- examples/src/index.tsx | 2 +- examples/src/views/sliderpage.tsx | 4 +-- package.json | 4 +-- src/hyperscript.ts | 54 +++++++------------------------ src/router/router.tsx | 4 +-- src/router/switch.tsx | 4 +-- src/styled/styled.tsx | 3 +- src/ui/carousel.tsx | 12 ++----- src/ui/masonry.tsx | 2 +- src/ui/view.ts | 10 +++--- src/util/wrap.tsx | 3 +- yarn.lock | 11 +++---- 12 files changed, 35 insertions(+), 78 deletions(-) diff --git a/examples/src/index.tsx b/examples/src/index.tsx index 412db62..0bdc679 100644 --- a/examples/src/index.tsx +++ b/examples/src/index.tsx @@ -19,7 +19,7 @@ import { } from '@codeurs/front' import testImage from 'assets/test.jpg' import svgImage from 'assets/test.svg' -import {h, render} from 'preact' +import {render} from 'preact' const Slider = lazy(() => import('./views/sliderpage')) diff --git a/examples/src/views/sliderpage.tsx b/examples/src/views/sliderpage.tsx index a819264..35de4b9 100644 --- a/examples/src/views/sliderpage.tsx +++ b/examples/src/views/sliderpage.tsx @@ -42,7 +42,7 @@ export default class SliderPage extends View { 'sliderpage-slide ' + (this.carousel.isActive(i) ? 'is-active' : '') } - style={{'min-width': this.state.width * 100 + '%'}} + style={{minWidth: this.state.width * 100 + '%'}} > {slide}
(
{slide} = - | ComponentTypeReact

- | ComponentTypePreact

+import {ReactNode} from 'react' -type Hyperscript = { - ( - type: string, - props: Record | null, - ...children: Array - ): ComponentChildren -

( - type: ComponentType

, - props: Attributes & P | null, - ...children: Array - ): ComponentChildren -} +export { + createElement as m, + Attributes, + ComponentType, + FunctionComponent, + FunctionComponent as StatelessView +} from 'react' export type DOMAttrs = {[key: string]: any} +export type Children = Array | ReactNode + export type ChildrenType = A extends {children?: any} ? A['children'] - : ComponentChildren - -export type Children = ComponentChildren - -export const m: Hyperscript = createElement as any - -export namespace m { - export namespace JSX { - export interface ElementAttributesProperty { - props: {} - } - - export interface ElementChildrenAttribute { - children: {} - } - - export interface IntrinsicElements extends JSXPreact.IntrinsicElements {} - } -} + : Children diff --git a/src/router/router.tsx b/src/router/router.tsx index aacfe0b..148703d 100644 --- a/src/router/router.tsx +++ b/src/router/router.tsx @@ -1,6 +1,6 @@ -import {Children, m} from '../hyperscript' +import {Children, m, StatelessView} from '../hyperscript' import {createContext} from '../ui/context' -import {StatelessView, View} from '../ui/view' +import {View} from '../ui/view' import {Match, parseRoute} from './parseroute' import {RouteAttrs} from './route' diff --git a/src/router/switch.tsx b/src/router/switch.tsx index 2235702..2863be1 100644 --- a/src/router/switch.tsx +++ b/src/router/switch.tsx @@ -1,11 +1,11 @@ -import {VNode} from 'preact' +import {ReactElement} from 'react' import {Children, m} from '../hyperscript' import {View} from '../ui/view' import {Route, RouteAttrs} from './route' import {Location, RouterContext} from './router' export class Switch extends View<{ - children: Array> + children: Array> }> { render() { const {children} = this.attrs diff --git a/src/styled/styled.tsx b/src/styled/styled.tsx index 8042ede..a2ef043 100644 --- a/src/styled/styled.tsx +++ b/src/styled/styled.tsx @@ -2,9 +2,8 @@ import createCache from '@emotion/cache' import isPropValid from '@emotion/is-prop-valid' import {serializeStyles} from '@emotion/serialize' import {EmotionCache, getRegisteredStyles, insertStyles} from '@emotion/utils' -import {DOMAttrs, m} from '../hyperscript' +import {DOMAttrs, m, StatelessView} from '../hyperscript' import {createContext} from '../ui/context' -import {StatelessView} from '../ui/view' const EmotionCacheContext = createContext(createCache()) diff --git a/src/ui/carousel.tsx b/src/ui/carousel.tsx index 7d994f9..7457d7d 100644 --- a/src/ui/carousel.tsx +++ b/src/ui/carousel.tsx @@ -252,16 +252,10 @@ export class Carousel extends View< render() { const {overflow, unstyled, className} = this.attrs + const style = !(overflow || unstyled) ? {overflow: 'hidden'} : {} return ( -

-