/** jsx:pragma h */ import { Component, h, render } from 'preact'; import { DataItem, DataItemRenderer, QueryFunction } from '../../control/src/abstract-select'; import { Dictionary } from '../../control/src/dictionary'; import { MultiSelect } from '../../control/src/multi-select'; import '../../control/src/select25.scss'; import { SingleSelect } from '../../control/src/single-select'; import { extend } from '../../control/src/util'; import { Ajax, createQueryFromAjax } from './ajax'; import { createQueryFromData, DataFunction } from './data'; import { Store } from './store'; const forceImportOfH = h; enum StoreKeys { targetElement = 'te' } interface BaseSelectOptions { containerStyle?: string; containerClass?: string; placeholder?: string; tabIndex?: number; valueContent?: DataItemRenderer; resultContent?: DataItemRenderer; query?: QueryFunction; data?: DataFunction; ajax?: Ajax; quiet?: number; minimumCharacters?: number; openOnFocus?: boolean; dictionary?: string | Dictionary; } interface MultiSelectOptions extends BaseSelectOptions { hiddenValue?: (values: DataItem[], options: MultiSelectOptions) => string; valuesLabel?: string; comboboxLabel?: string; allowDuplicates: boolean; values: DataItem[]; } interface SingleSelectOptions extends BaseSelectOptions { hiddenValue?: (value: DataItem, options: SingleSelectOptions) => string; label?: string; value: DataItem; allowClear?: boolean; } const BASE_DEFAULT_OPTIONS = { dictionary: 'en_us', minimumCharacters: 0, openOnFocus: false }; const DEFAULT_MULTI_SELECT_OPTIONS = extend({}, BASE_DEFAULT_OPTIONS, { hiddenValue: (values: DataItem[], options: MultiSelectOptions) => { if (values && values.length > 0) { return values.map(item => item.id).join(','); } else { return ''; } } }); const DEFAULT_SINGLE_SELECT_OPTIONS = extend({}, BASE_DEFAULT_OPTIONS, { allowClear: false, hiddenValue: (value: DataItem, options: SingleSelectOptions) => { if (value) { return value.id; } else { return ''; } } }); function triggerOnChange(element: HTMLElement, data: any) { const event = document.createEvent('HTMLEvents'); event.initEvent('change', false, true); event[data] = data; element.dispatchEvent(event); } class MultiSelectWrapper extends Component< { element: HTMLInputElement; options: MultiSelectOptions; }, { values?: DataItem[] } > { constructor(props) { super(props); this.state = { values: props.options.values || [] }; } public componentDidUpdate() { this.setHiddenValue(this.state.values); } public componentDidMount() { this.setHiddenValue(this.state.values); } public render(props, state, context) { const opts = this.props.options; return ( ); } public onChange = (values: any[]) => { this.setState({ values }); this.setHiddenValue(values); triggerOnChange(this.props.element, values); }; private setHiddenValue(values: any) { const { element, options } = this.props; element.value = options.hiddenValue(values, options); } } class SingleSelectWrapper extends Component< { options: SingleSelectOptions; element: HTMLInputElement; }, { value?: DataItem } > { constructor(props) { super(props); this.state = { value: props.options.value }; } public componentDidMount() { this.setHiddenValue(this.state.value); } public componentDidUpdate() { this.setHiddenValue(this.state.value); } public render(props, state, context) { const opts = this.props.options; return ( ); } public onChange = (value: DataItem) => { this.setState({ value }); this.setHiddenValue(value); triggerOnChange(this.props.element, value); }; private setHiddenValue(value: DataItem) { const { element, options } = this.props; element.value = options.hiddenValue(value, options); } } function createSingleSelect(element: HTMLInputElement, options: SingleSelectOptions) { options = extend({}, DEFAULT_SINGLE_SELECT_OPTIONS, options); fillBaseOptions(element, options); const store = Store.getStore(element); // create placeholder element into which the control will be rendered const parentElement = element.parentElement; const targetElement = document.createElement('div'); parentElement.insertBefore(targetElement, element); store.set(StoreKeys.targetElement, targetElement); render(, parentElement, targetElement); } function createMultiSelect(element: HTMLInputElement, options: SingleSelectOptions) { options = extend({}, DEFAULT_MULTI_SELECT_OPTIONS, options); fillBaseOptions(element, options); const store = Store.getStore(element); // create placeholder element into which the control will be rendered const parentElement = element.parentElement; const targetElement = document.createElement('div'); parentElement.insertBefore(targetElement, element); store.set(StoreKeys.targetElement, targetElement); render(, parentElement, targetElement); } function fillBaseOptions(element: HTMLInputElement, options: BaseSelectOptions) { if (!options.query) { if (options.ajax) { options.query = createQueryFromAjax(options.ajax); } else if (options.data) { options.query = createQueryFromData(options.data); } } if (!options.tabIndex && element.tabIndex) { options.tabIndex = element.tabIndex; } if (element.getAttribute('data-s25-container-style')) { let style = options.containerStyle || ''; if (style.length > 0) { style += ';'; } style += element.getAttribute('data-s25-container-style'); options.containerStyle = style; } if (element.getAttribute('data-s25-container-class')) { let clazz = options.containerClass || ''; if (clazz.length > 0) { clazz += ' '; } clazz += element.getAttribute('data-s25-container-class'); options.containerClass = clazz; } } function destroy(element: HTMLElement) { if (!Store.hasStore(element)) { return; } const store = Store.getStore(element); const targetElement = store.get(StoreKeys.targetElement); const parentElement = element.parentElement; render(null, parentElement, targetElement); parentElement.removeChild(targetElement); Store.removeStore(element); } const select25 = { createMultiSelect, createSingleSelect, destroy }; export { select25 }; declare global { interface Window { select25: typeof select25; } } window.select25 = select25;