+
+type GridProvider = {
+ columns?: number
+ gap?: Gap
+ span?: number
+}
+
+type Column> = {
+ span?: number
+ offset?: number
+ as?: ComponentType
| ElementType
+} & P
+
+const GridContext = createContext(undefined as any)
+
+const gapSize = (gap: Gap): [Size, Size] => {
+ if (gap === undefined) return [undefined, undefined]
+ if (Array.isArray(gap)) return gap
+ if (typeof gap === 'number' || typeof gap === 'string') return [gap, gap]
+ if ('x' in gap && 'y' in gap) return [gap.x, gap.y]
+ if ('x' in gap) return [gap.x, gap.x]
+ return [undefined, gap.y]
+}
+
+const gridStyles = (grid: GridContext) => {
+ const [x = 0, y = 0] = gapSize(grid.gap)
+ const left = `-${rem(x)}`
+ const top = `-${rem(y)}`
+ return `
+ margin-left: ${left};
+ margin-top: ${top};
+ `
+}
+
+const GridContainer = styled.div<{
+ queries: {[key: string]: string}
+ grid: GridContext
+ align?: Align
+}>(props => {
+ return `
+ box-sizing: border-box;
+ min-width: 100%;
+ display: flex;
+ flex-wrap: wrap;
+ align-items: ${flexAlign(props.align)};
+ ${gridStyles(props.grid)}
+
+ ${Object.entries(props.queries)
+ .map(
+ ([key, query]) =>
+ `
+ @media ${query} {
+ ${gridStyles(mergeGrid(props.grid, props.grid.media![key]))};
+ }
+ `
+ )
+ .join('\n')}
+ `
+})
+
+const columnStyles = (grid: GridContext, column: Column) => {
+ const span = column.span || grid.span || 1
+ const width = grid.columns ? Math.min(span, grid.columns) : span
+ const [x = 0, y = 0] = gapSize(grid.gap)
+ const gap = rem(x)
+ const top = rem(y)
+ const grow = grid.columns ? 0 : width
+ const basis = grid.columns
+ ? `calc(${perc(width / grid.columns)} - ${gap})`
+ : 0
+ const offset =
+ grid.columns && grid.columns < width + column.offset!
+ ? grid.columns - width
+ : column.offset!
+ const left = column.offset
+ ? `calc(${gap} + ${perc(offset / grid.columns!)})`
+ : gap
+ return `
+ flex: ${grow} 0 ${basis};
+ margin-left: ${left};
+ margin-top: ${top};
+ `
+}
+
+const ColumnContainer = styled.div<{
+ queries: {[key: string]: string}
+ grid: GridContext
+ column: Column & {media: {[key: string]: Column}}
+}>(
+ props => `
+ box-sizing: border-box;
+ min-width: 0;
+ ${columnStyles(props.grid, props.column)}
+
+ ${Object.entries(props.queries)
+ .map(
+ ([key, query]) =>
+ `
+ @media ${query} {
+ ${columnStyles(mergeGrid(props.grid, props.grid.media![key]), {
+ ...props.column,
+ ...props.column.media[key]
+ })}
+ }
+ `
+ )
+ .join('\n')};
+ `
+)
+
+const mergeGap = (a: Gap, b: Gap): Gap => {
+ const [x0, y0] = gapSize(a)
+ const [x1, y1] = gapSize(b)
+ const x = x1 !== undefined ? x1 : x0
+ const y = y1 !== undefined ? y1 : y0 !== undefined ? y0 : x
+ return [x, y]
+}
+
+const mergeGrid = (a: GridContext, b: GridContext | undefined): GridContext => {
+ if (!a) return mergeGrid({media: {}}, b)
+ if (!b) return mergeGrid(a, {media: {}})
+ const grid: GridContext = {
+ columns: b.columns || a.columns,
+ gap: mergeGap(a.gap, b.gap),
+ span: b.span || a.span
+ }
+ const res: GridContext = {
+ ...grid,
+ media: {}
+ }
+ const media = Object.keys(a.media || {}).concat(Object.keys(b.media || {}))
+ media.forEach(key => {
+ res.media![key] = mergeGrid(
+ grid,
+ mergeGrid((a.media || {})[key], (b.media || {})[key])
+ )
+ })
+ return res
+}
+
+export const createGrid = <
+ T extends {
+ [key: string]: string
+ }
+>(
+ queries: T
+) => {
+ const Grid: FunctionComponent<
+ Grid & {[P in keyof T]?: number | GridBase}
+ > = ({children, span, columns, gap, align, ...props}) => {
+ const parent = useContext(GridContext)
+ const media: {[key: string]: GridContext} = {}
+ const rest: {[key: string]: any} = {}
+ Object.entries(props).forEach(([k, v]) => {
+ if (k in queries)
+ media[k] = typeof v === 'number' ? {columns: v} : (v as GridContext)
+ else rest[k] = v
+ })
+ const grid = mergeGrid(parent, {
+ span,
+ columns,
+ gap,
+ media
+ })
+ return (
+
+ {children}
+
+ )
+ }
+ const Column: FunctionComponent = ({
+ children,
+ span,
+ offset,
+ ...props
+ }) => {
+ const grid = useContext(GridContext)
+ if (!grid) throw new Error('Column used outside of grid')
+ const column = {span, offset}
+ const rest: {[key: string]: any} = {}
+ const media: {[key: string]: Column} = {}
+ const sub: {[key: string]: GridContext} = {}
+ Object.entries(props).forEach(([k, v]) => {
+ if (k in queries) {
+ const col: Column = typeof v === 'number' ? {span: v} : (v as Column)
+ media[k] = col
+ } else {
+ rest[k] = v
+ }
+ })
+ Object.entries(queries).forEach(([key, _]) => {
+ const col = {...column, ...media[key]}
+ const ctx = mergeGrid(grid, grid.media && grid.media[key])
+ sub[key] = {
+ ...col,
+ span: undefined,
+ columns:
+ (media[key] && media[key].span) || Math.min(ctx.columns!, ctx.span!)
+ }
+ })
+ return (
+
+ {grid.columns ? (
+
+ {children}
+
+ ) : (
+ children
+ )}
+
+ )
+ }
+ const GridProvider: FunctionComponent<
+ GridProvider & {[P in keyof T]?: number}
+ > = ({children, ...value}) => {
+ return {children}
+ }
+ return {
+ Grid,
+ GridProvider,
+ Column
+ }
+}
diff --git a/src/hyperscript.ts b/src/hyperscript.ts
deleted file mode 100755
index e448009..0000000
--- a/src/hyperscript.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-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 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
-)
diff --git a/src/index.ts b/src/index.ts
index ba62d7e..fa530a2 100755
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,8 +1,2 @@
-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 './grid/grid'
+export * from './carousel/carousel'
diff --git a/src/router/index.ts b/src/router/index.ts
deleted file mode 100755
index b857556..0000000
--- a/src/router/index.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import {Router} from './router'
-
-export * from './link'
-export * from './redirect'
-export * from './parseroute'
-export * from './route'
-export * from './switch'
-export const HistoryRouter = Router
-export {Location, RouterContext, Matcher} from './router'
diff --git a/src/router/link.ts b/src/router/link.ts
deleted file mode 100755
index 5b4a28a..0000000
--- a/src/router/link.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-// See: https://github.com/jorgebucaran/hyperapp-router/blob/19f95f843ae2dc5b4d83f9647edc591a6450c4e3/src/Link.js
-import {m} from '../hyperscript'
-import {View} from '../ui/view'
-import {addClasses} from '../util/classes'
-import {Location, RouterContext} from './router'
-
-export class Link extends View<{
- to: string
- target?: string
- onclick?: Function
- [key: string]: any
-}> {
- render() {
- const {to, target, 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,
- onclick: (e: MouseEvent & {redraw: boolean}) => {
- e.redraw = false
- if (onclick) onclick(e)
- const ignore =
- ignoreClick(e) ||
- target === '_blank' ||
- isExternal(location, e.currentTarget as HTMLAnchorElement)
- if (ignore) return
- e.preventDefault()
- location.push(href)
- }
- },
- {is: {active}}
- )
- return m('a', anchorAttrs, children)
- })
- }
-}
-
-const ignoreClick = (e: MouseEvent) =>
- e.defaultPrevented ||
- e.button !== 0 ||
- e.altKey ||
- e.metaKey ||
- e.ctrlKey ||
- e.shiftKey
-
-type LocationProperties = {protocol: string; hostname: string; port?: string}
-
-const isExternal = (
- location: LocationProperties,
- anchorElement: HTMLAnchorElement
-) => getOrigin(location) !== getOrigin(anchorElement)
-
-const getOrigin: (location: LocationProperties) => string = ({
- protocol,
- hostname,
- port = ''
-}) => `${protocol}//${hostname}${port && `:${port}`}`
diff --git a/src/router/parseroute.ts b/src/router/parseroute.ts
deleted file mode 100755
index 74ecb2c..0000000
--- a/src/router/parseroute.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-// Source: https://github.com/jorgebucaran/hyperapp-router/blob/19f95f843ae2dc5b4d83f9647edc591a6450c4e3/src/parseRoute.js
-
-export type Match = {
- isExact: boolean
- path: string
- url: string
- params?: {
- [key: string]: any
- }
-}
-
-function createMatch(
- isExact: boolean,
- path: undefined | string,
- url: string,
- params?: {}
-): Match {
- return {
- isExact,
- path: path || '',
- url,
- params
- }
-}
-
-function trimTrailingSlash(url: string) {
- for (var len = url.length; '/' === url[--len]; );
- return url.slice(0, len + 1)
-}
-
-function decodeParam(val: string) {
- try {
- return decodeURIComponent(val)
- } catch (e) {
- return val
- }
-}
-
-export const parseRoute = (
- url: string,
- route: {path?: string; exact?: boolean}
-) => {
- const {path, exact = false} = route
- if (path === url || !path) return createMatch(path === url, path, url)
- var paths = trimTrailingSlash(path).split('/')
- var urls = trimTrailingSlash(url).split('/')
-
- if (paths.length > urls.length || (exact && paths.length < urls.length))
- return
-
- for (
- var i = 0,
- params: {[key: string]: string} = {},
- len = paths.length,
- url = '';
- i < len;
- i++
- ) {
- if (':' === paths[i][0]) {
- params[paths[i].slice(1)] = urls[i] = decodeParam(urls[i])
- } else if (paths[i] !== urls[i]) {
- return
- }
- url += urls[i] + '/'
- }
-
- return createMatch(false, path, url.slice(0, -1), params)
-}
diff --git a/src/router/redirect.ts b/src/router/redirect.ts
deleted file mode 100755
index e7995d9..0000000
--- a/src/router/redirect.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import {m} from '../hyperscript'
-import {View} from '../ui/view'
-import {Location, RouterContext} from './router'
-
-export class Redirect extends View<{
- to: string
-}> {
- render() {
- const {to} = this.attrs
- return m(Location, (location: RouterContext) =>
- location.replace(location.formatPath(to))
- )
- }
-}
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/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/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/store/autocompletestore.ts b/src/store/autocompletestore.ts
deleted file mode 100755
index 9af151c..0000000
--- a/src/store/autocompletestore.ts
+++ /dev/null
@@ -1,105 +0,0 @@
-export enum AutocompleteChange {
- MouseUp = 'MouseUp',
- KeyArrowUp = 'KeyArrowUp',
- KeyArrowDown = 'KeyArrowDown',
- KeyEscape = 'KeyEscape',
- KeyHome = 'KeyHome',
- KeyEnd = 'KeyEnd',
- KeySpace = 'KeySpace',
- KeyEnter = 'KeyEnter',
- ItemMouseEnter = 'ItemMouseEnter',
- ItemClick = 'ItemClick',
- InputBlur = 'InputBlur',
- InputChange = 'InputChange',
- ButtonClick = 'ButtonClick',
- ButtonBlur = 'ButtonBlur'
-}
-
-export type AutocompleteAction- =
- | {type: AutocompleteChange.KeyEscape}
- | {type: AutocompleteChange.ButtonBlur}
- | {type: AutocompleteChange.ButtonClick}
- | {type: AutocompleteChange.InputBlur}
- | {type: AutocompleteChange.MouseUp}
- | {type: AutocompleteChange.KeyArrowDown; amount: number; total: number}
- | {type: AutocompleteChange.KeyArrowUp; amount: number; total: number}
- | {type: AutocompleteChange.KeyEnter; item: Item}
- | {type: AutocompleteChange.ItemClick; item: Item}
- | {type: AutocompleteChange.ItemMouseEnter; index: number}
- | {type: AutocompleteChange.InputChange; value: string}
-
-export interface AutocompleteState
- {
- highlightedIndex: number | null
- inputValue: string | null
- isOpen: boolean
- selectedItem: Item | null
-}
-
-type AutocompleteProps
- = {
- items: Array
-
- itemToString: (item: Item) => string
-}
-
-type AutocompleteReducer
- = (
- state: AutocompleteState
- ,
- action: AutocompleteAction
- ,
- props: AutocompleteProps
-
-) => AutocompleteState
-
-
-export const autocompleteReducer =
- (
- state: AutocompleteState
- ,
- action: AutocompleteAction
- ,
- props: {
- items: Array
-
- itemToString: (item: Item) => string
- }
-): AutocompleteState
- => {
- const {highlightedIndex} = state
- const reset = {highlightedIndex: null, selectedItem: null}
- switch (action.type) {
- case AutocompleteChange.ButtonClick:
- return {...state, ...reset, isOpen: !state.isOpen}
- case AutocompleteChange.KeyEscape:
- case AutocompleteChange.ButtonBlur:
- case AutocompleteChange.InputBlur:
- case AutocompleteChange.MouseUp:
- return {...state, ...reset, isOpen: false}
- case AutocompleteChange.ItemClick:
- case AutocompleteChange.KeyEnter:
- return {
- ...state,
- isOpen: false,
- inputValue: props.itemToString(action.item),
- selectedItem: action.item
- }
- case AutocompleteChange.KeyArrowDown:
- case AutocompleteChange.KeyArrowUp:
- const next =
- (highlightedIndex === null ? -1 : highlightedIndex) + action.amount
- if (next < 0 || next >= action.total) return state
- return {...state, highlightedIndex: next}
- case AutocompleteChange.ItemMouseEnter:
- return {...state, highlightedIndex: action.index}
- case AutocompleteChange.InputChange:
- return {...state, ...reset, isOpen: true, inputValue: action.value}
- }
-}
-
-export class AutocompleteStore
- implements AutocompleteState
- {
- highlightedIndex: number | null = null
- inputValue: string | null = null
- isOpen: boolean = false
- selectedItem: Item | null = null
- private reducer: AutocompleteReducer
-
-
- constructor(reducer: AutocompleteReducer
- = autocompleteReducer) {
- this.reducer = reducer
- }
-
- dispatch = (
- action: AutocompleteAction
- ,
- props: AutocompleteProps
-
- ) => {
- return Object.assign(this, this.reducer(this, action, props))
- }
-}
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
deleted file mode 100755
index f037f02..0000000
--- a/src/store/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export * from './autocompletestore'
-export * from './formstore'
-export * from './modalstore'
-export * from './sliderstore'
diff --git a/src/store/modalstore.ts b/src/store/modalstore.ts
deleted file mode 100755
index c74d285..0000000
--- a/src/store/modalstore.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import stream from 'mithril/stream'
-
-export class ModalStore {
- isOpen = false
-
- open = () =>
- this.isOpen = true
-
- close = () =>
- this.isOpen = false
-
- toggle = () =>
- this.isOpen = !this.isOpen
-}
\ No newline at end of file
diff --git a/src/store/sliderstore.ts b/src/store/sliderstore.ts
deleted file mode 100755
index d1cb370..0000000
--- a/src/store/sliderstore.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import stream from 'mithril/stream'
-
-/**
- * index: Stream
- * total: Stream
- * ?actives: Stream boolean>>
- */
-export class SliderStore {
- index = stream(0)
- total = stream(0)
- actives = stream([] as Array<() => boolean>)
- animating = stream(false)
-
- has(index: number) {
- return index >= 0 && index < this.total()
- }
-
- hasNext() {
- return this.has(this.index() + 1)
- }
-
- hasPrevious() {
- return this.has(this.index() - 1)
- }
-
- goTo(index: number) {
- return this.has(index) && (this.index(index), true)
- }
-
- goNext() {
- return this.goTo(this.index() + 1)
- }
-
- goPrevious() {
- return this.goTo(this.index() - 1)
- }
-
- isActive(childIndex: number): boolean {
- return this.actives()[childIndex] && this.actives()[childIndex]()
- }
-}
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 1ee2a49..0000000
--- a/src/styled/styled.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import createCache from '@emotion/cache'
-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'
-import {addClasses} from '../util/classes'
-
-const EmotionCacheContext = createContext(createCache())
-
-type Styled = {
- (tag: string): (...args: any[]) => StatelessView
- (tag: T): (...args: any[]) => T
-}
-
-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, ...attrs}) =>
- m(EmotionCacheContext.Consumer, (cache: EmotionCache) => {
- let className = ''
- const classInterpolations: Array = []
- if (typeof attrs.className === 'string') {
- className += getRegisteredStyles(
- cache.registered,
- classInterpolations,
- attrs.className
- )
- }
- // 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
- }
- return m(tag, addClasses(attrs, className), 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.ts
deleted file mode 100755
index 55ed740..0000000
--- a/src/ui/breakpoint.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import m, {Children} from 'mithril'
-import {View} from './view'
-
-export class Breakpoint extends View<
- {
- children?: (attrs: T) => Children
- } & {
- [breakpoint: string]: T
- }
-> {
- matchers = new Map()
-
- onInit = this.createMatchers
- onBeforeUpdate = this.createMatchers
- onRemove = this.removeMatchers
-
- createMatchers() {
- const {children, ...breakpoints} = this.attrs
- const keys = Object.keys(breakpoints)
- keys.forEach(query => {
- if (this.matchers.has(query)) return
- const matcher = matchMedia(query)
- matcher.addListener(m.redraw)
- this.matchers.set(query, matchMedia(query))
- })
- this.removeMatchers(keys)
- }
-
- removeMatchers(except: Array = []) {
- this.matchers.forEach((matcher, query) => {
- if (except.indexOf(query) > -1) return
- matcher.removeListener(m.redraw)
- this.matchers.delete(query)
- })
- }
-
- render() {
- const {children, ...breakpoints} = this.attrs
- const keys = Object.keys(breakpoints)
- const active = Array.from(this.matchers).reduce(
- (active: null | string, [key, matcher]) => {
- if (!matcher.matches) return active
- if (!active) return key
- if (keys.indexOf(key) > keys.indexOf(active)) return key
- return active
- },
- null
- )
- return active && children && children(this.attrs[active])
- }
-}
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
deleted file mode 100755
index 3c297df..0000000
--- a/src/ui/context.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-// 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
-})
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 063bc03..0000000
--- a/src/ui/form/radio.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import './radio.less'
-
-import m from 'mithril'
-import {randomKey} from '../../util/formutils'
-import {Component} from '../component'
-
-export class Radio extends Component<{
- name: string
- value: boolean
- onchange?: (checked: boolean) => void
- option: string // Really label?
- required?: boolean
- unstyled?: boolean
- className?: string
-}> {
- className =
- this.attrs.className || (this.attrs.unstyled && 'radio') || 'radio-front'
- id = randomKey('radio_')
-
- view() {
- const {
- value = false,
- onchange,
- option,
- name = this.id,
- required
- } = this.attrs
-
- return m(`div.${this.className}`, [
- m(`input.${this.className}-input`, {
- type: 'radio',
- checked: value ? true : false,
- required,
- name: name,
- onclick: onchange && (() => onchange(!value)),
- id: this.id
- }),
- 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 5855901..0000000
--- a/src/ui/form/radios.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import m from 'mithril'
-import {cleanupOptions, randomKey} from '../../util/formutils'
-import {Component} from '../component'
-import {Radio} from './radio'
-
-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}> | {[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(option =>
- m(Radio, {
- option: option.label,
- name: name,
- unstyled,
- required,
- value: value == option.key,
- onchange: onchange && (() => onchange(option.key))
- })
- )
- )
- }
-}
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.less b/src/ui/icon.less
deleted file mode 100755
index 460fb71..0000000
--- a/src/ui/icon.less
+++ /dev/null
@@ -1,5 +0,0 @@
-.icon {
- font-style: normal;
- display: inline-block;
- vertical-align: middle;
-}
diff --git a/src/ui/icon.ts b/src/ui/icon.ts
deleted file mode 100755
index fa9e10e..0000000
--- a/src/ui/icon.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import './icon.less'
-
-import {DOMAttrs, m} from '../hyperscript'
-import {addClasses} from '../util/classes'
-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}))
- }
-}
diff --git a/src/ui/image.less b/src/ui/image.less
deleted file mode 100755
index 0675d6e..0000000
--- a/src/ui/image.less
+++ /dev/null
@@ -1,12 +0,0 @@
-.image {
- display: block;
- width: auto;
- height: auto;
- max-width: 100%;
- max-height: 100%;
-
- &.mod-crop {
- width: 100%;
- height: 100%;
- }
-}
diff --git a/src/ui/image.ts b/src/ui/image.ts
deleted file mode 100755
index d9ad047..0000000
--- a/src/ui/image.ts
+++ /dev/null
@@ -1,174 +0,0 @@
-import './image.less'
-
-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'
-
-type Size = {
- width: number
- height: number
-}
-
-const supportsObjectFit = window.navigator.msMaxTouchPoints === undefined
-
-const canUpscale = (fit: 'cover' | 'contain', container: Size, image: Size) => {
- const test = fit === 'cover' ? cover : contain
- const {width, height} = test(
- container.width,
- container.height,
- image.width,
- image.height
- )
- return width < image.width && height < image.height
-}
-
-export type ImageAttrs = {
- src: string
- width?: number
- height?: number
- fit?: ImageFit
- upScale?: boolean
- position?: undefined | false | string | {x: number; y: number}
- background?: boolean
-} & DOMAttrs
-
-class ImageBase extends View {
- onCreate() {
- if (!this.shouldCheckScale()) return
- const listener = () => this.scale()
- window.addEventListener('resize', listener)
- this.onRemove = () => window.removeEventListener('resize', listener)
- this.scale()
- }
-
- onUpdate = this.scale
-
- shouldCheckScale() {
- const {width, height, fit, upScale} = this.attrs
- const crop = fit === 'cover' || fit === 'contain'
- return !upScale && width && height && crop
- }
-
- scale() {
- const {width = 0, height = 0, fit, background} = this.attrs
- if (!this.dom || !this.shouldCheckScale()) return
- const useBackground = background || !supportsObjectFit
- const container = this.dom.getBoundingClientRect()
- if (canUpscale(fit as any, container, {width, height})) return
- if (useBackground) this.dom.style.backgroundSize = 'auto'
- else this.dom.style.objectFit = 'none'
- }
-
- render() {
- const {
- src,
- children,
- fit = 'landscape',
- upScale,
- background,
- position = 'center center',
- alt = '',
- width,
- height,
- style,
- // These get passed by sizeof-loader, but we don't want them to be passed
- // to the dom
- type,
- bytes,
- ...attrs
- } = this.attrs
- const crop = background || fit === 'cover' || fit === 'contain'
- const useBackground = background || (!supportsObjectFit && crop)
- const tag = useBackground ? 'div' : 'img'
- const focus =
- typeof position === 'string'
- ? position
- : position && `${position.x * 100}% ${position.y * 100}%`
- return m(tag, {
- ...(useBackground
- ? {
- role: 'img',
- 'aria-label': alt,
- style: {
- backgroundImage: `url('${src}')`,
- backgroundSize: crop && fit,
- backgroundPosition: focus,
- backgroundRepeat: 'no-repeat',
- ...style
- }
- }
- : {
- src,
- alt,
- width,
- height,
- style: {
- objectFit: crop && fit,
- objectPosition: focus !== 'center center' && focus,
- ...style
- }
- }),
- ...addClasses(attrs, 'image', {mod: {crop}})
- })
- }
-}
-
-type Resizer = (info: ImageAttrs, container: Size) => ImageAttrs
-
-const ImageResizerContext = createContext()
-
-export class ImageResizer extends View<{
- resize: Resizer
-}> {
- render() {
- const {resize} = this.attrs
- return m(ImageResizerContext.Provider, {value: resize}, this.children)
- }
-}
-
-export class Image extends View {
- cached: undefined | string
- resolved: undefined | ImageAttrs
- render() {
- const {
- children,
- src,
- width,
- height,
- fit,
- position,
- upScale,
- background,
- alt = '',
- type,
- bytes,
- style,
- ...rest
- } = this.attrs
- const crop = background || fit === 'cover' || fit === 'contain'
- return m(ImageResizerContext.Consumer, (resize: Resizer | undefined) => {
- if (!resize) return m(ImageBase, this.attrs)
- 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()
- }
- })
- if (this.cached !== src) resolve(this.dom)
- return this.resolved && m(ImageBase, this.resolved)
- })
- }
-}
diff --git a/src/ui/index.ts b/src/ui/index.ts
deleted file mode 100755
index dcb1f85..0000000
--- a/src/ui/index.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-export * from './breakpoint'
-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 './slider'
-export * from './view'
-export * from './form'
-export * from './maps'
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.less b/src/ui/masonry.less
deleted file mode 100755
index 7ec21e9..0000000
--- a/src/ui/masonry.less
+++ /dev/null
@@ -1,8 +0,0 @@
-.masonry {
- display: flex;
- width: 100%;
-
- &-col {
- min-width: 0;
- }
-}
diff --git a/src/ui/masonry.ts b/src/ui/masonry.ts
deleted file mode 100755
index 275d311..0000000
--- a/src/ui/masonry.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import './masonry.less'
-
-import m, {Child} from 'mithril'
-import {Component} from './component'
-
-export class Masonry extends Component<{
- 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
- }
-
- view() {
- 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/mediaquery.ts b/src/ui/mediaquery.ts
deleted file mode 100755
index d5a5381..0000000
--- a/src/ui/mediaquery.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import m, {Children} from 'mithril'
-import {Breakpoint} from './breakpoint'
-import {View} from './view'
-
-export class MediaQuery extends View<{
- minWidth?: number
- maxWidth?: number
- view: () => Children
-}> {
- render() {
- const {view, minWidth, maxWidth} = this.attrs
- const rules = []
- 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())
- }
-}
diff --git a/src/ui/modal.less b/src/ui/modal.less
deleted file mode 100755
index e2a9449..0000000
--- a/src/ui/modal.less
+++ /dev/null
@@ -1,49 +0,0 @@
-.modal {
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- opacity: 0;
- transition: opacity 0.35s ease-out;
- overflow: auto;
-
- &.is-open {
- opacity: 1;
- }
-
- &-overlay {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: rgba(0, 0, 0, 0.7);
- z-index: -1;
- pointer-events: none;
- }
-
- &-container {
- position: relative;
- display: flex;
- align-items: center;
- justify-content: center;
- min-height: 100%;
- z-index: 1;
-
- &-content {
- position: relative;
- }
- }
-
- &.mod-full {
- .modal-container-content {
- position: absolute;
- top: 0;
- left: 0;
- height: 100%;
- width: 100%;
- overflow: auto;
- }
- }
-}
diff --git a/src/ui/modal.ts b/src/ui/modal.ts
deleted file mode 100755
index 12acdfd..0000000
--- a/src/ui/modal.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-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)
- )
- )
- }
-}
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/slider.less b/src/ui/slider.less
deleted file mode 100755
index 98335d7..0000000
--- a/src/ui/slider.less
+++ /dev/null
@@ -1,14 +0,0 @@
-.slider {
- height: 100%;
- //touch-action: none;
- user-select: none;
-
- .slider-content {
- height: 100%;
- white-space: nowrap;
-
- > * {
- white-space: normal;
- }
- }
-}
diff --git a/src/ui/slider.ts b/src/ui/slider.ts
deleted file mode 100755
index cf31626..0000000
--- a/src/ui/slider.ts
+++ /dev/null
@@ -1,177 +0,0 @@
-import './slider.less'
-
-import m from 'mithril'
-import {Stream} from 'mithril/stream'
-import {
- ColdSubscription,
- listen,
- pointer,
- spring,
- styler,
- tween,
- value,
- ValueReaction
-} from 'popmotion'
-import {View} from './view'
-
-export class Slider extends View<
- {
- index: Stream
- total: Stream
- actives: Stream void>>
- animating: Stream
- unstyled?: boolean
- },
- HTMLDivElement
-> {
- size = 0
- total = 0
- pos!: ValueReaction
- // scroll = scroll()
- // Todo: add proper scroll decay after
- // https://github.com/Popmotion/stylefire/pull/8 is merged
- slides: Array = []
- tween?: ColdSubscription
-
- onCreate(dom: HTMLDivElement) {
- const contentStyler = (styler as any)(dom.firstChild)
- this.pos = value(0, contentStyler.set('x'))
- const listener = this.listen(dom)
- const size = () => this.setSize(true)
- window.addEventListener('resize', size)
- size()
- // We redraw in the next frame here, because
- // active state is only now available
- setTimeout(m.redraw)
- this['onremove'] = () => {
- listener.stop && listener.stop()
- window.removeEventListener('resize', size)
- }
- }
-
- // Todo: rewrite this to properly to use popmotion's actions and reactions
- private listen(dom: HTMLDivElement): ColdSubscription {
- return listen(dom, 'mousedown touchstart').start(() => {
- const {animating} = this.attrs
- if (this.tween) this.tween.stop && this.tween.stop()
- animating(true)
- let start: {x: number; y: number},
- isHorizontal: null | boolean = null
- const track = pointer({
- x: this.pos.get() as number,
- preventDefault: false
- }).start((p: {x: number; y: number}) => {
- if (!start) return (start = {x: p.x, y: p.y})
- if (isHorizontal === null) {
- isHorizontal = Math.abs(start.x - p.x) > Math.abs(start.y - p.y)
- this.dom && (this.dom.style.pointerEvents = 'none')
- }
- if (isHorizontal) this.pos.update(p.x)
- })
- listen(document, 'mouseup touchend', {once: true}).start(() => {
- const {total, index} = this.attrs
- const velocity = this.pos.getVelocity()
- track.stop && track.stop()
- this.dom && (this.dom.style.pointerEvents = '')
- if (!isHorizontal) return
- if (Math.abs(velocity) > 0.2 * this.size) {
- const next = velocity > 0 ? index() - 1 : index() + 1
- if (next >= 0 && next < total()) {
- index(next)
- return m.redraw()
- }
- }
- this.bounce()
- })
- })
- }
-
- // Bounce back to current slide
- bounce() {
- const {index, animating} = this.attrs
- if (this.tween) this.tween.stop && this.tween.stop()
- animating(true)
- this.tween = spring({
- from: this.pos.get(),
- velocity: this.pos.getVelocity(),
- to: this.slides[index()],
- stiffness: 100,
- damping: 20
- }).start({
- update: (v: number) => this.pos.update(v),
- complete: () => animating(false)
- })
- }
-
- setSize(resized = false) {
- const {index} = this.attrs
- if (!this.dom) return
- this.size = this.dom.getBoundingClientRect().width
- this.calcSlides()
- if (resized) this.pos.update(this.slides[index()])
- }
-
- calcSlides() {
- if (!this.dom) return
- const {index, total, actives} = this.attrs
- const content = this.dom.firstChild as HTMLElement
- const children = content.children
- const activeChecks = []
- this.slides = [0]
- let curr = 0,
- prev = 0,
- last = 0
- for (let i = 0; i < children.length; i++) {
- const slide = children[i]
- const width = slide.getBoundingClientRect().width
- curr += width
- // We add a pixel to the width here to prevent rounding errors
- if (curr - last >= this.size + 1) {
- if (prev !== 0) this.slides.push(-prev)
- last = prev
- }
- const start = prev,
- end = curr
- activeChecks.push(() => {
- const now = this.slides[index()]
- return start >= -now - 1 && end <= -now + this.size + 1
- })
- prev = curr
- }
- if (curr > last && last !== 0) {
- const toLast = curr - this.size
- this.slides.pop()
- this.slides.push(-(curr - this.size))
- }
- if (total() != this.slides.length) {
- total(this.slides.length)
- if (index() > total()) index(total() - 1)
- setTimeout(m.redraw)
- }
- if (actives) actives(activeChecks)
- }
-
- onupdate() {
- const {index, animating} = this.attrs
- const x = this.pos.get()
- this.setSize()
- if (x != this.slides[index()])
- this.tween = tween({
- from: this.pos.get(),
- //velocity: this.pos.getVelocity(),
- to: this.slides[index()]
- //stiffness: 200
- }).start({
- update: (v: number) => this.pos.update(v),
- complete: () => animating(false)
- })
- }
-
- render() {
- const {unstyled = false} = this.attrs
- return m('.slider',
- {style: unstyled || {overflow: 'hidden'}},
- m('.slider-content', this.children)
- )
- }
-}
diff --git a/src/ui/view.ts b/src/ui/view.ts
deleted file mode 100755
index 6cf49ff..0000000
--- a/src/ui/view.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-import {ChildrenType} from 'hyperscript'
-import {
- Children,
- ClassComponent,
- CVnode,
- CVnodeDOM,
- Vnode,
- VnodeDOM
-} from 'mithril'
-import {extractChildren} from '../util/children'
-
-declare global {
- namespace JSX {
- interface ElementAttributesProperty {
- attrs: {}
- }
- interface ElementChildrenAttribute {
- children: {}
- }
- }
-}
-
-export type StatelessView = {
- (attr: {children?: Children} & Attr): Children
-}
-
-export abstract class View
- implements ClassComponent {
- attrs!: Readonly<{children?: Children}> & Readonly
- dom: undefined | Dom
-
- constructor(vnode: CVnode) {
- this.__update(vnode as Vnode)
- }
-
- get children(): ChildrenType {
- return this.attrs.children as ChildrenType
- }
-
- // Public API
-
- onInit() {}
- onCreate(dom: Dom) {}
- onUpdate(dom: Dom) {}
- onBeforeRemove(dom: Dom): void | Promise {}
- onRemove() {}
- onBeforeUpdate(attrs: Attrs): void | boolean {}
- abstract render(): Children | null | void
-
- // Mithril 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)
- }
-
- /** @internal */
- onbeforeremove(vnode: VnodeDOM) {
- this.__update(vnode)
- return this.onBeforeRemove(vnode.dom as Dom)
- }
-
- /** @internal */
- onremove(vnode: VnodeDOM) {
- this.__update(vnode)
- return this.onRemove()
- }
-
- /** @internal */
- onbeforeupdate(vnode: Vnode, old: CVnodeDOM) {
- this.__update(vnode)
- return this.onBeforeUpdate(old.attrs)
- }
-
- view(vnode: Vnode) {
- return this.render()
- }
-
- /** @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)
- }
- }
-}
diff --git a/src/util/children.ts b/src/util/children.ts
deleted file mode 100755
index 4b5438a..0000000
--- a/src/util/children.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import {Children} from 'mithril'
-
-export const extractChildren = (children: Children): Children =>
- Array.isArray(children) && children[0] && typeof children[0] == 'function'
- ? children[0]
- : children
diff --git a/src/util/classes.ts b/src/util/classes.ts
deleted file mode 100755
index 7b96372..0000000
--- a/src/util/classes.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import classNames from 'classnames'
-import {DOMAttrs} from '../hyperscript'
-
-interface ClassDictionary {
- [id: string]: any
-}
-
-interface ClassArray extends Array {}
-
-type ClassValue =
- | string
- | ClassDictionary
- | ClassArray
- | undefined
- | null
- | boolean
-
-function prefixClassNames(prefix: string | null, input: Array) {
- return classNames(input)
- .split(' ')
- .filter(v => v)
- .map(name => (prefix ? `${prefix}-${name}` : name))
-}
-
-export function parseClasses(
- classes: ClassValue
-): ClassValue | Array {
- if (!classes || Array.isArray(classes) || typeof classes !== 'object')
- return classes
- return Object.keys(classes).map(key =>
- typeof classes[key] === 'object' || typeof classes[key] === 'string'
- ? prefixClassNames(
- key,
- ([] as Array).concat(parseClasses(classes[key]))
- )
- : classNames({[key]: classes[key]})
- )
-}
-
-export function classes(...classes: Array) {
- const names = classNames(classes.map(parseClasses))
- return names ? {className: names} : {}
-}
-
-export const addClasses = (
- attrs: DOMAttrs,
- ...rest: Array
-): DOMAttrs => {
- const {class: c1, className: c2, ...props} = attrs
- return {...classes(c1, c2, ...rest), ...props}
-}
diff --git a/src/util/fit.ts b/src/util/fit.ts
deleted file mode 100755
index 7ba7dfa..0000000
--- a/src/util/fit.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-// Source: https://github.com/sroucheray/contain-cover/blob/master/contain-cover.js
-
-function fit(contains: boolean) {
- return function(
- containerWidth: number,
- containerHeight: number,
- width: number,
- height: number
- ) {
- let doRatio = width / height
- let cRatio = containerWidth / containerHeight
- let targetWidth = 0
- let targetHeight = 0
- let test = contains ? doRatio > cRatio : doRatio < cRatio
-
- if (test) {
- targetWidth = containerWidth
- targetHeight = targetWidth / doRatio
- } else {
- targetHeight = containerHeight
- targetWidth = targetHeight * doRatio
- }
-
- return {
- width: targetWidth,
- height: targetHeight,
- x: (containerWidth - targetWidth) / 2,
- y: (containerHeight - targetHeight) / 2
- }
- }
-}
-
-export const contain = fit(true)
-export const cover = fit(false)
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