import m from 'mithril'; import Stream from 'mithril/stream'; import { List, ListTile, Search, IconButton, Button, Shadow, Toolbar, ToolbarTitle, } from 'polythene-mithril'; import infinite from 'mithril-infinite'; import { debounce } from '../utils'; const iconSearchSVG = "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path d=\"M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z\"/></svg>"; const iconBackSVG = "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path d=\"M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z\"/></svg>"; const iconClearSVG = "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"/></svg>"; const createSearchField = () => { const BackButton = { view: ({ attrs }) => m(IconButton, { icon: { svg: m.trust(iconBackSVG) }, ink: false, events: { onclick: attrs.leave }, }), }; const ClearButton = { view: ({ attrs }) => m(IconButton, { icon: { svg: m.trust(iconClearSVG) }, ink: false, events: { onclick: attrs.clear }, }), }; const SearchIcon = { view: () => m(IconButton, { icon: { svg: m.trust(iconSearchSVG) }, inactive: true, }), }; return { oninit: vnode => { const value = Stream(""); const setInputState = Stream(); //const clear = () => setInputState()({ value: '', focus: false}); const clear = () => value(''); const leave = () => value(''); vnode.state = { value, setInputState, clear, leave }; }, view: ({ state, attrs }) => { // incoming value and focus added for result list example: const value = attrs.value !== undefined ? attrs.value : state.value(); return m(Search, Object.assign( {}, { textfield: { label: "Search", onChange: (newState) => { state.value(newState.value); state.setInputState(newState.setInputState); // onChange callback added for result list example: attrs.onChange && attrs.onChange(newState, state.setInputState); }, value, // incoming label and defaultValue added for result list example: label: attrs.label || "Search", defaultValue: attrs.defaultValue, }, buttons: { none: { before: m(SearchIcon), }, focus: { before: m(BackButton, { leave: state.leave }), }, focus_dirty: { before: m(BackButton, { leave: state.leave }), after: m(ClearButton, { clear: state.clear }), }, dirty: { before: m(BackButton, { leave: state.leave }), after: m(ClearButton, { clear: state.clear }), }, }, before: m(Shadow), }, attrs, )); }, }; }; const SearchField = createSearchField(); export default class SelectList { constructor({ attrs: { listTileAttrs } }) { this.selected = null; this.showList = false; this.searchValue = ''; this.listTileAttrs = listTileAttrs; } item() { return (data) => { const attrs = { compactFront: true, hoverable: true, className: 'themed-list-tile', events: { onclick: () => { this.selected = data; this.showList = false; }, }, }; // Overwrite default attrs Object.assign(attrs, this.listTileAttrs(data)); return m(ListTile, attrs); }; } view({ attrs: { controller, onSubmit = () => {} } }) { return m('div', [ this.selected ? m(Toolbar, { compact: true }, [ m(IconButton, { icon: { svg: m.trust(iconClearSVG) }, ink: false, events: { onclick: () => { this.selected = null; } }, }), m(ToolbarTitle, { text: this.selected.name }), m(Button, { label: 'Submit', className: 'blue-button', events: { onclick: () => { onSubmit(this.selected); this.selected = null; controller.setSearch(''); controller.refresh(); }, }, }), ]) : m(SearchField, Object.assign({}, { label: 'type here', onChange: ({ value, focus }) => { if (focus) { this.showList = true; } if (value !== this.searchValue) { // if we always update the search value, this would also happen // immidiately in the moment where we click on the listitem. // Then, the list get's updated before the click is registered. // So, we make sure this state change is due to value change and // not due to focus change. this.searchValue = value; controller.setSearch(value); debounce(() => { controller.refresh(); }, 500); } }, defaultValue: '', })), this.showList ? m(List, { className: 'scrollTable', tiles: m(infinite, controller.infiniteScrollParams(this.item())), }) : null, ]); } }