From be02a5bd4f2ccae08388650837f6c8e620861a3d Mon Sep 17 00:00:00 2001 From: arlair Date: Fri, 22 Apr 2016 13:21:29 +0800 Subject: [PATCH] feat(component): Start of Drawer component --- package.json | 2 +- src/drawer/Drawer.ts | 128 ++++++++++++++++++++++++++++++++ src/drawer/index.ts | 1 + src/drawer/style.scss | 30 ++++++++ src/helpers/componentFactory.ts | 8 +- src/index.ts | 1 + src/overlay/Overlay.ts | 9 ++- 7 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 src/drawer/Drawer.ts create mode 100644 src/drawer/index.ts create mode 100644 src/drawer/style.scss diff --git a/package.json b/package.json index 3f27668..2afab1a 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "ghooks": "1.0.3", "mocha": "^2.4.5", "semantic-release": "^4.3.5", - "typescript": "^1.8.7", + "typescript": "^1.8.10", "typings": "0.7.9" }, "keywords": [ diff --git a/src/drawer/Drawer.ts b/src/drawer/Drawer.ts new file mode 100644 index 0000000..aa624d0 --- /dev/null +++ b/src/drawer/Drawer.ts @@ -0,0 +1,128 @@ +import { Observable as $, Subject } from 'rx'; +const { h, aside } = require('cycle-snabbdom'); +const dialogPolyfill = require('dialog-polyfill/dialog-polyfill.js'); +import * as classNames from 'classnames'; +const style = require('./style'); +//const style = require('react-toolbox/lib/drawer/style'); +import { componentFactory } from '../helpers/componentFactory'; +import { CycleDomComponent, CycleComponent, CycleUiComponentProps } + from '../helpers/cycleDomInterfaces'; + +export interface DrawerProps extends CycleUiComponentProps { + active?: boolean; + // TODO Enum? oneOf(['left', 'right']) + type?: string; +} + +export const DrawerDefaultProps: DrawerProps = { + isolate: true, + className: '', + active: false, + type: 'left', +}; + +export function Drawer(sources: any, props?: DrawerProps, children?: CycleComponent[]): + CycleDomComponent; +export function Drawer(sources: any, children?: CycleComponent[]): CycleDomComponent; + +export function Drawer(sources: any, propsOrChildren: any, children?: any) { + return componentFactory(DrawerFactory, DrawerDefaultProps, + sources, propsOrChildren, children); +} + +export function DrawerFactory(sources: any, props$: $, + children?: CycleComponent[]): CycleDomComponent { + + let closeEvent$: any = new Subject(); + + let className: string; + props$.map( (props) => { + className = classNames([ style.root, style[props.type] ], { + [style.active]: props.active + }, props.className); + }); + + // const drawerClose$ = sources.DOM.select(`.${className}`).events('close'). + // startWith(false). + // map((x: any) => false). + // do((x: any) => console.log('mydrawerClose$: ' + x)); + // + // const drawerCancel$ = sources.DOM.select('.drawer').events('cancel').map(() => false). + // startWith(false). + // do((x: any) => console.log('drawerCancel: ' + x)); + + //const vtree$ = $.combineLatest(props$, drawerClose$, (props, drawerClose) => { + const vtree$ = props$.map( (props) => { + console.log('DOM'); + const className = classNames([ style.root, style[props.type] ], { + [style.active]: props.active + }, props.className); + + // const isOpen$ = $.merge( + // props.active, + // sources.DOM.select(`.${className}`).events('close').map(() => false), + // ).startWith(false) + + const insert = (vnode: any) => { + console.log('insert hook'); + const dialog: any = document.querySelector('dialog'); + dialogPolyfill.registerDialog(dialog); + if (props.active) { + console.log('showing modal'); + dialog.showModal(); + dialog.oncancel = (event: any) => { + console.log('cancel'); + }; + + dialog.addEventListener('close', (event: any) => { + console.log('theonclose'); + closeEvent$.onNext(false); + }); + + // closeEvent$ = ($).fromEvent(dialog, 'close'). + // startWith(false); + dialog.onclick = (event: any) => { + const rect = dialog.getBoundingClientRect(); + const isInDialog = (rect.top <= event.clientY && event.clientY <= rect.top + rect.height + && rect.left <= event.clientX && event.clientX <= rect.left + rect.width); + if (!isInDialog) { + console.log('clicked outside dialog and about to close'); + dialog.close(); + } + }; + // const backdrop: any = document.querySelector('.backdrop'); + // console.log(backdrop); + // if (backdrop != null) { + // console.log('adding backdrop listener'); + // backdrop.onclick = () => { + // console.log('click on backdrop'); + // dialog.close(); + // }; + // } + } else { + const overlay: any = document.querySelector('._dialog_overlay'); + if (overlay != null) { + console.log('removing dialog overlay'); + overlay.parentNode.removeChild(overlay); + } + } + }; + + return ( +// Overlay( { active: props.active }, [ + // TODO only render when required: props.active &&, but errors if nothing is returned + h('dialog', {hook: {insert}, props: { className }, + attrs: { 'data-cycle-ui': 'drawer' } }, [ + aside( { props: { className: style.content } }, + children + ), + ]) +// ]).DOM + ); + }); + + return { + DOM: vtree$, + closeEvent$ + }; +} diff --git a/src/drawer/index.ts b/src/drawer/index.ts new file mode 100644 index 0000000..777c377 --- /dev/null +++ b/src/drawer/index.ts @@ -0,0 +1 @@ +export { Drawer } from './Drawer'; diff --git a/src/drawer/style.scss b/src/drawer/style.scss new file mode 100644 index 0000000..ede03a7 --- /dev/null +++ b/src/drawer/style.scss @@ -0,0 +1,30 @@ +@import "../../node_modules/react-toolbox/lib/base"; +@import "../../node_modules/react-toolbox/lib/drawer/config"; +@import "../../node_modules/react-toolbox/lib/drawer/style"; +//@import "../../node_modules/dialog-polyfill/dialog-polyfill.css"; + +dialog[open] { + display: block; + position: fixed; + z-index: 10; +} + +dialog + :global(.backdrop) { + position: fixed; + top: 0; right: 0; bottom: 0; left: 0; + background: rgba(0,0,0,0.1); +} + +/* for small devices, modal dialogs go full-screen */ +@media screen and (max-width: 540px) { + dialog[_polyfill_modal] { /* TODO: implement */ + top: 0; + width: auto; + margin: 1em; + } +} + +:global(._dialog_overlay) { + position: fixed; + top: 0; right: 0; bottom: 0; left: 0; +} diff --git a/src/helpers/componentFactory.ts b/src/helpers/componentFactory.ts index 20cbd5a..b013aeb 100644 --- a/src/helpers/componentFactory.ts +++ b/src/helpers/componentFactory.ts @@ -1,4 +1,4 @@ -const isolate = require('@cycle/isolate'); +const isolateLocal = require('@cycle/isolate'); import { Observable } from 'rx'; import { getComponentParams } from './componentParams'; import { CycleUiComponentProps, CycleComponent } from './cycleDomInterfaces'; @@ -9,7 +9,11 @@ export function componentFactory(factory: any, const params = getComponentParams(defaults, propsOrChildren, children); if (params.props.isolate) { - factory = isolate(factory); + // if (typeof sources === 'undefined') { + factory = isolateLocal(factory); + // } else { + // factory = sources.isolate(factory); + // } } const props$ = Observable.just(params.props); diff --git a/src/index.ts b/src/index.ts index d16b124..c6f504f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ export { Button, ButtonFactory } from './button/index'; export { Card, CardTitle, CardText, CardActions } from './card/index'; export { Dialog } from './dialog/index'; +export { Drawer } from './drawer/index'; export { Input } from './input/index'; export { RadioButton, RadioGroup } from './radio/index'; export { Overlay } from './overlay/index'; diff --git a/src/overlay/Overlay.ts b/src/overlay/Overlay.ts index 298fc12..91a308a 100644 --- a/src/overlay/Overlay.ts +++ b/src/overlay/Overlay.ts @@ -4,7 +4,6 @@ import * as classNames from 'classnames'; const style = require('react-toolbox/lib/overlay/style'); import { componentFactory } from '../helpers/componentFactory'; import { CycleDomComponent, CycleUiComponentProps } from '../helpers/cycleDomInterfaces'; -const { concat } = require('lodash'); export interface OverlayProps extends CycleUiComponentProps { active?: boolean; @@ -41,6 +40,14 @@ export function OverlayFactory(props$: $, children ) ]); + // return ( + // h('dialog', {hook: {insert}, props: { className } }, [ + // div( { props: { className } }, [ + // div( { props: { className: style.overlay } }, + // children + // ) + // ]); + }); return {