From 27f2926d80aecaaef49a2e793363b54b38664e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eryk=20Napiera=C5=82a?= Date: Wed, 10 Jun 2015 23:06:21 +0000 Subject: [PATCH] finish all required features --- demo/demo.index.css | 30 ++++-- demo/index.js | 6 +- package.json | 2 +- .../css/_components/files-list-item.scss | 21 +---- src/file-manager/css/_vars.scss | 6 +- src/file-manager/css/index.scss | 36 ++++++- .../js/components/file-rename-form/index.js | 16 +++- .../js/components/file-rename-form/model.js | 7 -- .../js/components/files-list-item/index.js | 5 +- .../js/components/selectable-list/model.js | 4 +- src/file-manager/js/index.js | 93 ++++++++++++++----- src/file-manager/js/model.js | 33 +++++-- src/test/js/index.js | 68 ++++++++++++++ 13 files changed, 247 insertions(+), 80 deletions(-) delete mode 100644 src/file-manager/js/components/file-rename-form/model.js create mode 100644 src/test/js/index.js diff --git a/demo/demo.index.css b/demo/demo.index.css index 9f2e47b..98c5422 100644 --- a/demo/demo.index.css +++ b/demo/demo.index.css @@ -14,7 +14,8 @@ vertical-align: middle; } .files-list-item { - height: 2em; } + height: 2em; + line-height: 2em; } .files-list-item:before, .files-list-item__rename-form, .files-list-item__label, @@ -26,12 +27,6 @@ height: 100%; } .files-list-item__label { margin-left: 4px; } - .files-list-item__rename-form .file-rename-form__input { - margin-left: 0; - border-width: 2px; - padding-left: 2px; - margin-right: 0.5em; - max-width: 8em; } .files-list-item__button--rename-cancel { margin-left: 0.5em; margin-top: 1.5px; } @@ -74,10 +69,29 @@ body { margin-bottom: -1.2em; margin-left: 1.2em; } +.files .add-new { + height: 2em; + line-height: 2em; } + .files .add-new > * { + display: inline-block; + vertical-align: middle; } + .files .add-new .file-rename-form__input { + margin-right: 0.5em; } + .files .add-new__cancel { + margin-left: 0.5em; + margin-top: 1.5px; } + +.files .file-rename-form__input { + margin-left: 0; + border-width: 2px; + padding-left: 2px; + margin-right: 0.5em; + max-width: 8em; } + .removal-confirmation { background-color: #FFF; } .removal-confirmation__message { margin-top: 0; white-space: pre-wrap; } -/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIl9jb21wb25lbnRzL3NlbGVjdGFibGUtbGlzdC5zY3NzIiwiX3ZhcnMuc2NzcyIsIl9jb21wb25lbnRzL2ZpbGVzLWxpc3QtaXRlbS5zY3NzIiwiX2NvbXBvbmVudHMvZmlsZS1yZW5hbWUtZm9ybS5zY3NzIiwiX2NvbXBvbmVudHMvY29uZmlybS5zY3NzIiwiaW5kZXguc2NzcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtFQUNJLHNCQUFzQjtFQUN0QixXQUFXO0VBQ1gsVUFBVSxFQUhJO0VBUU07O0lBQ1osdUJDSE0sRURFUztFQUlKOztJQUNYLHVCQ05LLEVES1M7RUFJZDs7SUFDQSxzQkFBc0I7SUFDdEIsdUJBQXVCLEVBRnBCOztBRWhCZjtFQUlJLFlBQVksRUFKRTtFQVNiOzs7O0lBQ0csc0JBQXNCO0lBQ3RCLHVCQUF1QixFQUZEO0VBS3pCO0lBQ0csWUFBWTtJQUNaLGFBQWEsRUFGUDtFQUtUO0lBRUcsaUJBbkJjLEVBaUJSO0VBTU47SUFJSSxlQUFlO0lBQ2Ysa0JBSmtCO0lBS2xCLGtCQUEyQjtJQUUzQixvQkFoQ1k7SUFrQ1osZUFBZSxFQVZPO0VBYzdCO0lBQ0csbUJBdkNnQjtJQXdDaEIsa0JBQWtCLEVBRkk7O0FDckN6QjtFQUNHLG1CQUFtQixFQURiOztBQ0ZkO0VBRUksdUJBQXVCO0VBQ3ZCLCtDQUFrQztFQUdsQyxnQkFBZ0I7RUFDaEIsVUFBVTtFQUNWLFNBQVM7RUFDVCxxREFBd0M7RUFBeEMsaURBQXdDO0VBQXhDLDZDQUF3QztFQUV4Qyx1QkFBdUI7RUFDdkIsV0FBVztFQUNYLGlCQUFpQjtFQUNqQixhQUFhO0VBRWIsY0hmVyxFR0RNO0VBbUJoQjtJQUNHLGtCQUFrQixFQURWO0VBSVg7SUFDRyxtQkFBbUIsRUFEZjtFQUtQOztJQUNHLHNCQUFzQjtJQUN0Qix1QkFBdUIsRUFGUjs7QUN0QnZCO0VBQ0ksYUFBYSxFQURYOztBQUlOO0VBQ0ksd0JBQ3VCO0VBRXZCLFVBQVU7RUFDVixpQkFBaUIsRUFMZjs7QUFRTjtFQUNJLHNCQUNtQjtFQURuQixtQkFFZ0IsRUFIZDs7QUFZTjtFQUNJLHVCSjFCZ0IsRUl5Qkc7RUFHbEI7SUFDRyxjQUFjO0lBQ2Qsc0JBQXNCLEVBRmQiLCJmaWxlIjoiZGVtby5pbmRleC5jc3MiLCJzb3VyY2VzQ29udGVudCI6W10sInNvdXJjZVJvb3QiOiIvc291cmNlLyJ9 */ \ No newline at end of file +/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIl9jb21wb25lbnRzL3NlbGVjdGFibGUtbGlzdC5zY3NzIiwiX3ZhcnMuc2NzcyIsIl9jb21wb25lbnRzL2ZpbGVzLWxpc3QtaXRlbS5zY3NzIiwiX2NvbXBvbmVudHMvZmlsZS1yZW5hbWUtZm9ybS5zY3NzIiwiX2NvbXBvbmVudHMvY29uZmlybS5zY3NzIiwiaW5kZXguc2NzcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtFQUNJLHNCQUFzQjtFQUN0QixXQUFXO0VBQ1gsVUFBVSxFQUhJO0VBUU07O0lBQ1osdUJDSE0sRURFUztFQUlKOztJQUNYLHVCQ05LLEVES1M7RUFJZDs7SUFDQSxzQkFBc0I7SUFDdEIsdUJBQXVCLEVBRnBCOztBRWhCZjtFQUNJLFlEU3VCO0VDUnZCLGlCRFF1QixFQ1ZUO0VBT2I7Ozs7SUFDRyxzQkFBc0I7SUFDdEIsdUJBQXVCLEVBRkQ7RUFLekI7SUFDRyxZQUFZO0lBQ1osYUFBYSxFQUZQO0VBS1Q7SUFFRyxpQkRQVSxFQ0tKO0VBS1Q7SUFDRyxtQkRaWTtJQ2FaLGtCQUFrQixFQUZJOztBQ3BCekI7RUFDRyxtQkFBbUIsRUFEYjs7QUNGZDtFQUVJLHVCQUF1QjtFQUN2QiwrQ0FBa0M7RUFHbEMsZ0JBQWdCO0VBQ2hCLFVBQVU7RUFDVixTQUFTO0VBQ1QscURBQXdDO0VBQXhDLGlEQUF3QztFQUF4Qyw2Q0FBd0M7RUFFeEMsdUJBQXVCO0VBQ3ZCLFdBQVc7RUFDWCxpQkFBaUI7RUFDakIsYUFBYTtFQUViLGNIZlcsRUdETTtFQW1CaEI7SUFDRyxrQkFBa0IsRUFEVjtFQUlYO0lBQ0csbUJBQW1CLEVBRGY7RUFLUDs7SUFDRyxzQkFBc0I7SUFDdEIsdUJBQXVCLEVBRlI7O0FDdEJ2QjtFQUNJLGFBQWEsRUFEWDs7QUFJTjtFQUNJLHdCQUN1QjtFQUV2QixVQUFVO0VBQ1YsaUJBQWlCLEVBTGY7O0FBUU47RUFDSSxzQkFDbUI7RUFEbkIsbUJBRWdCLEVBSGQ7O0FBU0Y7RUFDSSxZSmxCbUI7RUltQm5CLGlCSm5CbUIsRUlpQmI7RUFJRjtJQUNBLHNCQUFzQjtJQUN0Qix1QkFBdUIsRUFGcEI7RUFLUDtJQUNJLG9CSjFCUSxFSXlCYztFQUl6QjtJQUNHLG1CSjlCUTtJSStCUixrQkFBa0IsRUFGWDs7QUFNZjtFQUlJLGVBQWU7RUFDZixrQkFKa0I7RUFLbEIsa0JBQTJCO0VBRTNCLG9CSjNDWTtFSTZDWixlQUFlLEVBVk87O0FBYzlCO0VBQ0ksdUJKeERnQixFSXVERztFQUdsQjtJQUNHLGNBQWM7SUFDZCxzQkFBc0IsRUFGZCIsImZpbGUiOiJkZW1vLmluZGV4LmNzcyIsInNvdXJjZXNDb250ZW50IjpbXSwic291cmNlUm9vdCI6Ii9zb3VyY2UvIn0= */ \ No newline at end of file diff --git a/demo/index.js b/demo/index.js index f8ba198..1c14190 100644 --- a/demo/index.js +++ b/demo/index.js @@ -1,3 +1,7 @@ import fileManager from '../src/file-manager/js'; -fileManager(document.body); \ No newline at end of file +fileManager(document.body); + +// import test from '../src/test/js'; + +// test(document.body); \ No newline at end of file diff --git a/package.json b/package.json index 57f7a8b..7c4ef88 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "bem-cn": "^1.0.0", "core-js": "^0.9.11", "cyclejs": "^0.22.0", - "cyclejs-group": "^0.5.0", + "cyclejs-group": "^0.6.0", "cyclejs-stream": "^0.3.2", "dedent": "^0.4.0", "ramda": "~0.14", diff --git a/src/file-manager/css/_components/files-list-item.scss b/src/file-manager/css/_components/files-list-item.scss index f16bf99..658d378 100644 --- a/src/file-manager/css/_components/files-list-item.scss +++ b/src/file-manager/css/_components/files-list-item.scss @@ -1,8 +1,6 @@ .files-list-item { - $buttons-space: .5em; - $input-offset: 4px; // to make text indent equal for label end rename form - - height: 2em; + height: $file-list-item-height; + line-height: $file-list-item-height; &:before, &__rename-form, @@ -22,21 +20,6 @@ margin-left: $input-offset; } - &__rename-form { - .file-rename-form__input { - $border-width: 2px; - - // make text indent equal for label end rename form - margin-left: 0; - border-width: $border-width; - padding-left: $input-offset - $border-width; - - margin-right: $buttons-space; // pull buttons - - max-width: 8em; - } - } - &__button--rename-cancel { margin-left: $buttons-space; margin-top: 1.5px; // accidental offset diff --git a/src/file-manager/css/_vars.scss b/src/file-manager/css/_vars.scss index d5ead30..4849244 100644 --- a/src/file-manager/css/_vars.scss +++ b/src/file-manager/css/_vars.scss @@ -6,4 +6,8 @@ $color: ( background: #FFF, row-even: #EEE, row-odd: #FFF -); \ No newline at end of file +); + +$file-list-item-height: 2em; +$buttons-space: .5em; +$input-offset: 4px; // to make text indent equal for label end rename form \ No newline at end of file diff --git a/src/file-manager/css/index.scss b/src/file-manager/css/index.scss index ec0bbfb..a263f6d 100644 --- a/src/file-manager/css/index.scss +++ b/src/file-manager/css/index.scss @@ -24,13 +24,43 @@ body { } .files { - - + + .add-new { + height: $file-list-item-height; + line-height: $file-list-item-height; + + & > * { + display: inline-block; + vertical-align: middle; + } + + .file-rename-form__input { + margin-right: $buttons-space; // pull buttons + } + + &__cancel { + margin-left: $buttons-space; + margin-top: 1.5px; // accidental offset + } + } + + .file-rename-form__input { + $border-width: 2px; + + // make text indent equal for label end rename form + margin-left: 0; + border-width: $border-width; + padding-left: $input-offset - $border-width; + + margin-right: $buttons-space; // pull buttons + + max-width: 8em; + } } .removal-confirmation { background-color: map-get($color, 'background'); - + &__message { margin-top: 0; white-space: pre-wrap; diff --git a/src/file-manager/js/components/file-rename-form/index.js b/src/file-manager/js/components/file-rename-form/index.js index 36c589d..2a92ec2 100644 --- a/src/file-manager/js/components/file-rename-form/index.js +++ b/src/file-manager/js/components/file-rename-form/index.js @@ -1,8 +1,7 @@ import { registerCustomElement, h, Rx } from 'cyclejs'; +import FocusHook from 'cyclejs/node_modules/virtual-dom/virtual-hyperscript/hooks/focus-hook'; import createGroup from 'cyclejs-group'; -import modelDefinition from './model'; - export default function createFileRenameFormElement(tagName) { let formClass = tagName; @@ -11,10 +10,16 @@ export default function createFileRenameFormElement(tagName) { let applyButtonClass = buttonClass + '--apply'; registerCustomElement(tagName, (interactions, properties) => { - let model = createGroup(modelDefinition); + let model = createGroup({ + value$: (initialValue$, apply$, input$) => + apply$.withLatestFrom( + input$.merge(initialValue$), + (apply, input) => input + ).merge(initialValue$) + }); model.inject({ - initialValue$: properties.get('value'), + initialValue$: properties.get('value').take(1), apply$: interactions.get(`.${formClass}`, 'submit') .tap((e) => e.preventDefault()), input$: interactions.get(`.${inputClass}`, 'input') @@ -30,7 +35,8 @@ export default function createFileRenameFormElement(tagName) { h('input', { className: `${inputClass}`, type: 'text', - value: value + value: value, + 'focus-hook': new FocusHook() }), h('button', { type: 'submit', diff --git a/src/file-manager/js/components/file-rename-form/model.js b/src/file-manager/js/components/file-rename-form/model.js deleted file mode 100644 index f50ed36..0000000 --- a/src/file-manager/js/components/file-rename-form/model.js +++ /dev/null @@ -1,7 +0,0 @@ - - -export default { - value$: (initialValue$, apply$, input$) => - apply$.withLatestFrom(input$, (apply, input) => input) - .merge(initialValue$) -}; \ No newline at end of file diff --git a/src/file-manager/js/components/files-list-item/index.js b/src/file-manager/js/components/files-list-item/index.js index 0a265cd..52674c7 100644 --- a/src/file-manager/js/components/files-list-item/index.js +++ b/src/file-manager/js/components/files-list-item/index.js @@ -1,11 +1,8 @@ import { registerCustomElement, h, Rx } from 'cyclejs'; import createGroup from 'cyclejs-group'; -import fileRenameForm from '../file-rename-form'; import modelDefinition from './model'; -fileRenameForm('file-rename-form'); - export default function createFilesListItemElement(tagName) { let itemClass = tagName; @@ -49,7 +46,7 @@ export default function createFilesListItemElement(tagName) { return { vtree$, name$: interactions.get(`.${renameFormClass}`, 'value') - .map(({ detail}) => detail) + .map(({ detail }) => detail) }; }); diff --git a/src/file-manager/js/components/selectable-list/model.js b/src/file-manager/js/components/selectable-list/model.js index 5bce3f7..6ae06ce 100644 --- a/src/file-manager/js/components/selectable-list/model.js +++ b/src/file-manager/js/components/selectable-list/model.js @@ -3,7 +3,7 @@ export default { options$: (children$, change$, selectAll$, options$) => change$.withLatestFrom( - options$, + options$.delay(1), (change, options) => options.map(({ element, id, selected }) => ({ id, @@ -15,7 +15,7 @@ export default { ) .merge( selectAll$.withLatestFrom( - options$, + options$.delay(1), (selected, options) => options.map(({ element, id }) => ({ id, diff --git a/src/file-manager/js/index.js b/src/file-manager/js/index.js index da21da6..50b9972 100644 --- a/src/file-manager/js/index.js +++ b/src/file-manager/js/index.js @@ -8,10 +8,12 @@ import modelDefinition from './model'; import confirm from './components/confirm'; import selectableListComponent from './components/selectable-list'; import filesListItem from './components/files-list-item'; +import fileRenameForm from './components/file-rename-form'; selectableListComponent('selectable-list'); filesListItem('files-list-item'); confirm('confirmation-popup'); +fileRenameForm('file-rename-form'); // functional style console.log @@ -24,13 +26,32 @@ function computer(interactions) { let listId = uuid(); let listClass = 'files'; let listItemClass = listClass + '__item'; + let addNewClass = 'add-new'; + let addNewFormClass = addNewClass + '__form'; + let cancelAddingNewButtonClass = addNewClass + '__cancel'; let navClass = 'nav'; let buttonClass = navClass + '__button'; let renameButtonClass = buttonClass + '--rename'; let removeButtonClass = buttonClass + '--remove'; + let addNewButtonClass = buttonClass + '--add-new'; let removalConfirmationClass = 'removal-confirmation'; let removalConfirmationMessageClass = removalConfirmationClass + '__message'; + let newFolderRegex = /^New folder(?: \((\d+)\))?$/; + function getNewFolderName(files) { + let lastNewFolder = files.map(({ fileName }) => + fileName.match(newFolderRegex) + ).filter((result) => + result + ).map((result) => + 'undefined' !== typeof result[1] ? + parseInt(result[1], 10) : + 0 + ).sort((a, b) => b - a)[0]; + + return 'New folder' + ('undefined' !== typeof lastNewFolder ? ` (${lastNewFolder + 1})` : ''); + } + let model = createGroup(modelDefinition); model.inject({ initialFiles$: Rx.Observable.just( @@ -40,9 +61,9 @@ function computer(interactions) { .map((fileName) => ({ fileName, uuid: uuid(), - selected: !!Math.round(Math.random()) + selected: false })) - ).shareReplay(1), + ), selectedOptions$: interactions.get(`.${listClass}`, 'selectedOptions') .map(({ detail }) => detail) .map((options) => @@ -52,9 +73,13 @@ function computer(interactions) { ), renameButtonClick$: interactions.get(`.${renameButtonClass}`, 'click'), removeButtonClick$: interactions.get(`.${removeButtonClass}`, 'click'), + addNewButtonClick$: interactions.get(`.${addNewButtonClass}`, 'click'), + addingNewConfirmed$: interactions.get(`.${addNewFormClass}`, 'value') + .map(({ detail }) => detail), + addingNewCanceled$: interactions.get(`.${cancelAddingNewButtonClass}`, 'click'), removalConfirmed$: interactions.get(`.${removalConfirmationClass}`, 'confirm') .map(({ detail }) => detail) - .shareReplay(1), + .share(), removalCanceled$: interactions.get(`.${removalConfirmationClass}`, 'cancel') .map(({ detail }) => detail), fileNameChange$: interactions.get(`.${listItemClass}`, 'name') @@ -69,42 +94,64 @@ function computer(interactions) { return Rx.Observable.combineLatest( model.files$, model.renameMode$, + model.addingNewMode$, model.anyFileSelected$, model.removalConfirmationVisible$, - (files, renameMode, anyFileSelected, removalConfirmationVisible) => + (files, renameMode, addingNewMode, anyFileSelected, removalConfirmationVisible) => h('div', [ h('div', { className: `${navClass}` }, [ h('button', { className: `${renameButtonClass}`, - disabled: !anyFileSelected + disabled: !anyFileSelected || addingNewMode || removalConfirmationVisible }, renameMode ? 'Finish renaming' : 'Rename'), h('button', { className: `${removeButtonClass}`, - disabled: renameMode || !anyFileSelected - }, 'Remove') + disabled: renameMode || !anyFileSelected || addingNewMode || removalConfirmationVisible + }, 'Remove'), + h('button', { + className: `${addNewButtonClass}`, + disabled: renameMode || addingNewMode || removalConfirmationVisible + }, 'New folder') ]), h('selectable-list', { - disabled: renameMode, + disabled: renameMode || addingNewMode || removalConfirmationVisible, key: listId, className: `${listClass}` - }, files.map(({ fileName, uuid, selected }) => - h('div', { - // custom elements can't be embedded in other custom elements directly - // until we fix it in the core, use plain DIV to wrap - id: uuid, - selected: selected - }, - h('files-list-item', { - key: uuid, + }, (addingNewMode ? [ + h('div', { + selected: true + }, h('div', { + className: `${addNewClass}` + }, [ + h('file-rename-form', { + className: `${addNewFormClass}`, + value: getNewFolderName(files), + key: 'adding-new-file-form' + }), + h('button', { + className: `${cancelAddingNewButtonClass}` + }, 'Cancel') + ])) + ] : [ ]).concat( + files.map(({ fileName, uuid, selected }) => + h('div', { + // custom elements can't be embedded in other custom elements directly + // until we fix it in the core, use plain DIV to wrap id: uuid, - name: fileName, - renameMode: selected && renameMode, - className: `${listItemClass}` - }) - ) - )) + selected: selected + }, + h('files-list-item', { + key: uuid, + id: uuid, + name: fileName, + renameMode: selected && renameMode, + className: `${listItemClass}` + }) + ) + )) + ) ].concat( removalConfirmationVisible ? [ h('confirmation-popup', { className: `${removalConfirmationClass}`, diff --git a/src/file-manager/js/model.js b/src/file-manager/js/model.js index 78ebefe..041154f 100644 --- a/src/file-manager/js/model.js +++ b/src/file-manager/js/model.js @@ -1,8 +1,9 @@ import { Rx } from 'cyclejs'; +import { v1 as uuid } from 'uuid'; export default { - files$: (selectedOptions$, initialFiles$, files$, fileNameChange$, removalConfirmed$) => + files$: (selectedOptions$, initialFiles$, files$, fileNameChange$, removalConfirmed$, addingNewConfirmed$) => Rx.Observable.merge( selectedOptions$ .withLatestFrom( @@ -34,10 +35,25 @@ export default { !selected ) ), + addingNewConfirmed$ + .withLatestFrom( + files$, + (newName, files) => + [{ + fileName: newName, + uuid: uuid(), + selected: true + }].concat(files) + ), initialFiles$ ).distinctUntilChanged((files) => JSON.stringify(files) ), + anyFileSelected$: (files$) => + files$ + .map((files) => !!files.filter(({ selected }) => selected).length) + .startWith(false) + .distinctUntilChanged(), renameMode$: (renameButtonClick$) => renameButtonClick$ .scan(false, (previous) => @@ -45,11 +61,16 @@ export default { ) .startWith(false) .distinctUntilChanged(), - anyFileSelected$: (files$) => - files$ - .map((files) => !!files.filter(({ selected }) => selected).length) - .startWith(false) - .distinctUntilChanged(), + // mode can be created by factory function, but what about this cyclejs-group DI based on function parameters names? + addingNewMode$: (addNewButtonClick$, addingNewConfirmed$, addingNewCanceled$) => + Rx.Observable.merge( + Rx.Observable.merge( + addingNewConfirmed$, + addingNewCanceled$ + ).map(() => false), + addNewButtonClick$.map(() => true) + ).startWith(false) + .distinctUntilChanged(), removalConfirmationVisible$: (removeButtonClick$, removalConfirmed$, removalCanceled$) => Rx.Observable.merge( Rx.Observable.merge( diff --git a/src/test/js/index.js b/src/test/js/index.js new file mode 100644 index 0000000..b435f6b --- /dev/null +++ b/src/test/js/index.js @@ -0,0 +1,68 @@ +import { Rx, h, applyToDOM } from 'cyclejs'; +import createGroup from 'cyclejs-group'; + + +// functional style console.log +global.log = function log(...args) { + return console.log.bind(console, ...args); +}; + + +function computer(interactions) { + + + let model = createGroup({ + list$: (initialList$, addItem$, newItemName$, list$) => + Rx.Observable.merge( + addItem$.tap(log('addItem$')).withLatestFrom(newItemName$, list$, (add, newItem, list) => + [ newItem ].concat(list) + ), + initialList$ + ).tap(log('list$')), + addingMode$: (turnOnAddingMode$, addItem$) => + Rx.Observable.merge( + turnOnAddingMode$.map(() => true), + addItem$.map(() => false) + ).startWith(false) + .tap(log('addingMode$')) + }); + model.inject({ + initialList$: Rx.Observable.just([ 'abc', 'def', 'xyz' ]), + turnOnAddingMode$: interactions.get('.add', 'click'), + addItem$: interactions.get('.confirm-add', 'click'), + newItemName$: interactions.get('.new-item-name', 'input') + .map(({ target }) => target.value) + }, model + ); + + return Rx.Observable.combineLatest( + model.list$, + model.addingMode$, + (list, addingMode) => + h('div', [ + h('div', addingMode ? [ + h('input', { + type: 'text', + className: 'new-item-name', + value: Math.floor(Math.random() * 10e10).toString(31) + }), + h('button', { + className: 'confirm-add' + }, 'Add') + ] : h('button', { + className: 'add' + }, 'Add item') + ), + h('ul', { + }, list.map((item) => + h('li', { }, item) + ) + ) + ] + ) + ); +} + +export default function fileManagerApp(dom) { + applyToDOM(dom, computer); +} \ No newline at end of file