UNPKG

web-ui-pack

Version:
236 lines (235 loc) 11.8 kB
import WUPPopupElement from "../popup/popupElement"; import WUPSpinElement from "../spinElement"; import WUPBaseComboControl, { MenuOpenCases } from "./baseCombo"; import WUPBaseControl, { SetValueReasons } from "./baseControl"; declare const tagName = "wup-select"; declare global { namespace WUP.Select { interface MenuItem<T> { /** Provide ordinary string for rendering as menuItem OR * Use li.innerHTML to render value & return string required for input * @example el.$options.items = [ { value: 1, text: "Item N 1"}, // this ordinary behavior { value: 2, text: (value, li, i, control) => { li.innerHTML = `<button class='delete'><button>Item N ${i+1}` // some custom HTML here // li.textContent = `Item N ${i+1}`; // or use this for fastest render const btn = li.querySelector('button')!; btn.onclick = (e) => { e.preventDefault(); control.$closeMenu(); } return `Item N ${value}`; // this is text rendered in input when related item selected }, }, ]; */ text: string | ((value: T, li: HTMLElement, i: number, control: WUPBaseControl) => string); /** Value tied with menu item */ value: T; /** Use `(e)=>e.preventDefault()` to prevent selection & closing menu and do custom action */ onClick?: (e: MouseEvent, me: MenuItem<T>) => void; } interface MenuItemElement extends HTMLLIElement { _value: any; _text: string; } interface EventMap extends WUP.BaseCombo.EventMap { } interface ValidityMap extends WUP.BaseCombo.ValidityMap { /** Count of minimal values that must be selected (only for option `multi`) */ minCount: number; /** Count of minimal values that must be selected (only for option `multi`) */ maxCount: number; } interface NewOptions<T = any> { /** Items showed in dropdown-menu. Provide promise/api-call to show pending status when control retrieves data! */ items: MenuItem<T>[] | Promise<MenuItem<T>[]> | (() => MenuItem<T>[] | Promise<MenuItem<T>[]>); /** Allow user to create new value if value not found in items * @defaultValue false */ allowNewValue: boolean; /** Allow to select multiple values; in this case $value & $initValue must contain Array<ValueType> * * @defaultValue false */ multiple: boolean; } interface Options<T = any, VM = ValidityMap> extends WUP.BaseCombo.Options<T, VM>, NewOptions<T> { /** Case when menu-popup to show * @defaultValue onPressArrowKey | onClick | onFocus | onInput */ openCase: MenuOpenCases; /** Set `true` to make input not editable but allow select items via popup-menu (ordinary dropdown mode) * @tutorial * * set number X to enable autoMode where `input.readOnly = items.length < X` */ readOnlyInput: boolean | number; } interface JSXProps<C = WUPSelectControl> extends WUP.BaseCombo.JSXProps<C>, WUP.Base.OnlyNames<NewOptions> { /** Items showed in dropdown-menu. Provide promise/api-call to show pending status when control retrieves data! * Global reference to object with array * @see {@link MenuItems} * @example * ```js * window.myItems = [...]; * <wup-select w-items="window.myItems"></wup-select> * ``` */ "w-items"?: string; "w-allowNewValue"?: boolean | ""; "w-multiple"?: boolean | ""; } } interface HTMLElementTagNameMap { [tagName]: WUPSelectControl; } } declare module "react" { namespace JSX { interface IntrinsicElements { /** Form-control with dropdown/combobox behavior * @see {@link WUPSelectControl} */ [tagName]: WUP.Base.ReactHTML<WUPSelectControl> & WUP.Select.JSXProps; } } } declare module "preact/jsx-runtime" { namespace JSX { interface HTMLAttributes<RefType> { } interface IntrinsicElements { /** Form-control with dropdown/combobox behavior * @see {@link WUPSelectControl} */ [tagName]: HTMLAttributes<WUPSelectControl> & WUP.Select.JSXProps; } } } /** Form-control with dropdown/combobox behavior * @see demo {@link https://yegorich555.github.io/web-ui-pack/control/select} * @example const el = document.createElement("wup-select"); el.$options.name = "gender"; el.$options.items = [ { value: 1, text: "Male" }, { value: 2, text: "Female" }, { value: 3, text: "Other/Skip" }, ]; el.$initValue = 3; el.$options.validations = { required: true }; const form = document.body.appendChild(document.createElement("wup-form")); form.appendChild(el); // or HTML <wup-form> <wup-select w-name="gender" w-initvalue="3" w-validations="myValidations" w-items="myDropdownItems" /> </wup-form>; * @tutorial innerHTML @example * <label> * <span> // extra span requires to use with icons via label:before, label:after without adjustments * <input/> * <strong>{$options.label}</strong> * </span> * <button clear/> * <wup-popup menu> * <ul> * <li>Item 1</li> * <li>Item 2</li> * // etc. / * </ul> * </wup-popup> * </label> */ export default class WUPSelectControl<ValueType = any | any[], ItemType = ValueType, TOptions extends WUP.Select.Options = WUP.Select.Options, EventMap extends WUP.Select.EventMap = WUP.Select.EventMap> extends WUPBaseComboControl<ValueType, TOptions, EventMap> { #private; static get $style(): string; /** Text for listbox when no items are displayed */ static $textNoItems: string | undefined; /** Text for aria-label of <ul> element */ static $ariaLabelItems: string; static $isEqual(v1: unknown, v2: unknown, c: WUPSelectControl): boolean; /** Function to filter menuItems based on inputValue * @param menuItemText textcontent in lowercase * @param menuItemValue value related to items[x].value * @param inputValue normalized input value (trimStart + lowercase) * @param inputRawValue input value without normalization * @returns true if menuItem must be visible in menu */ static $filterMenuItem(this: WUPSelectControl, menuItemText: string, menuItemValue: any, inputValue: string, inputRawValue: string): boolean; static $defaults: WUP.Select.Options; static cloneDefaults<T extends Record<string, any>>(): T; /** Returns whether control in pending state or not (shows spinner) */ get $isPending(): boolean; /** Called when need to parse attr [initValue] */ parse(attrValue: string): ValueType | undefined; parseInput(text: string): ValueType | undefined; /** Returns string for storage */ valueToStrCompare(a: WUP.Select.MenuItem<ValueType>): string | null; valueFromStorage(str: string, skipMultiple?: boolean): ValueType | undefined; /** Store value to storage; if item.text is not function then stored text, otherwise value.toString() * @see {@link valueToStrCompare} */ valueToStorage(v: ValueType, skipMultiple?: boolean): string | null; /** It's called with option allowNewValue to find value related to text */ protected findValueByText(txt: string): ValueType | undefined; /** Called on every spin-render */ renderSpin(): WUPSpinElement; /** Required to wait for fetching items to setup initValue */ _onPendingInitValue?: () => void; /** Called to get/fetch items based on $options.items */ fetchItems(): Promise<WUP.Select.MenuItem<ValueType>[]> | WUP.Select.MenuItem<ValueType>[]; protected gotChanges(propsChanged: Array<keyof WUP.Select.Options> | null): void; setupInputReadonly(): void; setupInitValue(propsChanged: Array<keyof WUP.Select.Options> | null): void; /** Change pending state */ protected changePending(v: boolean): void; /** All items of current menu */ _menuItems?: { all: WUP.Select.MenuItemElement[]; /** Index of items filtered by input */ filtered?: Array<number>; /** Index (in 'filtered' otherwise in 'all' array) of item that has virtual-focus; */ focused: number; }; /** Items resolved from options */ _cachedItems?: WUP.Select.MenuItem<ValueType>[]; /** Called when NoItems need to show */ protected renderMenuNoItems(popup: WUPPopupElement, isReset: boolean): void; protected renderMenu(popup: WUPPopupElement, menuId: string): HTMLElement; /** Method returns items defined based on $options.items */ protected getItems(): WUP.Select.MenuItem<ValueType>[]; protected valueToText(v: ItemType, items: WUP.Select.MenuItem<ItemType>[]): string; protected setInputValue(v: ValueType | undefined, reason: SetValueReasons): void; protected valueToInput(v: ValueType | undefined): string; /** Create menuItems as array of HTMLLiElement with option _text required to filtering by input (otherwise content can be html-structure) */ protected renderMenuItems(ul: HTMLUListElement): WUP.Select.MenuItemElement[]; protected gotMenuClick(e: MouseEvent): void; /** Called when need to setValue & close base on clicked item */ protected gotMenuItemClick(e: MouseEvent, li: WUP.Select.MenuItemElement): void; protected selectValue(v: ValueType, canCloseMenu?: boolean): void; canOpenMenu(openCase: MenuOpenCases, e?: MouseEvent | FocusEvent | KeyboardEvent | null): boolean; protected goOpenMenu(openCase: MenuOpenCases, e?: MouseEvent | FocusEvent | KeyboardEvent | null): Promise<WUPPopupElement | null>; /** Returns first selected visible item */ protected getFirstSelected(): WUP.Select.MenuItemElement | undefined; protected selectMenuItem(next: HTMLElement | null): void; /** Select menu item if value !== undefined and menu is opened */ protected selectMenuItemByValue(v: ValueType | undefined): void; protected focusMenuItem(next: HTMLElement | null): void; /** Focus item by index or reset is index is null (via aria-activedescendant). * If menuItems is filtered by input-text than index must point on filtered array */ protected focusMenuItemByIndex(index: number): void; protected focusMenuItemByKeydown(e: KeyboardEvent): void; protected gotKeyDown(e: KeyboardEvent): void; /** Called to filter menuItems (on input change etc.) */ protected filterMenuItems(): void; /** Called on showMenu when user opened it without input-change */ protected clearFilterMenuItems(): void; /** Called to hide/show menuItem */ protected filterMenuItem(el: HTMLElement, hide: boolean): void; /** Returns if possible to de-select menu items when input is empty */ private get canClearSelection(); protected gotBeforeInput(e: WUP.Text.GotInputEvent): void; /** Called on input change to update menu if possible */ protected tryUpdateMenu(): void; clearValue(): void; protected gotInput(e: WUP.Text.GotInputEvent): void; protected gotFocus(e: FocusEvent): Array<() => void>; protected gotFocusLost(): void; protected setValue(v: ValueType | undefined, reason: SetValueReasons, skipInput?: boolean): boolean | null; protected removePopup(): void; } export {};