UNPKG

@freshworks/crayons

Version:
1,029 lines (1,019 loc) 48.7 kB
import { r as registerInstance, c as createEvent, h, i as getElement, F as Fragment } from './index-25bc21e4.js'; import { d as debounce, b as cyclicDecrement, e as cyclicIncrement, i as isEqual, h as hasSlot, r as renderHiddenField, a as handleKeyDown } from './index-04e9b0fa.js'; import { i as i18n } from './Translation-f65767b0.js'; import { F as FieldControl } from './field-control-d467cf63.js'; const listOptionsCss = ":host{font-family:var(--fw-font-family, -apple-system, blinkmacsystemfont, \"Segoe UI\", roboto, oxygen, ubuntu, cantarell, \"Open Sans\", \"Helvetica Neue\", sans-serif);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-box-sizing:border-box;box-sizing:border-box}.container{margin:0px;padding:12px 8px 8px;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}"; var __decorate$1 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; let ListOptions = class { constructor(hostRef) { registerInstance(this, hostRef); this.fwChange = createEvent(this, "fwChange", 7); this.fwLoading = createEvent(this, "fwLoading", 7); this.isInternalValueChange = false; this.arrowKeyCounter = 0; this.optionRefs = []; this.defaultSearchFunction = (text, dataSource) => { return new Promise((resolve) => { const value = text.toLowerCase(); const filteredValue = value !== '' ? dataSource.filter((option) => option.text.toLowerCase().includes(value.toLowerCase())) : dataSource; resolve(filteredValue); }); }; this.filteredOptions = []; this.selectOptions = []; this.selectedOptionsState = []; this.isLoading = false; /** * Value corresponding to the option, that is saved when the form data is saved. */ this.options = []; /** * Value of the option that is displayed as the default selection, in the list box. Must be a valid value corresponding to the fw-select-option components used in Select. */ this.value = ''; /** * Works with `multiple` enabled. Configures the maximum number of options that can be selected with a multi-select component. */ this.max = Number.MAX_VALUE; /** * Enables selection of multiple options. If the attribute’s value is undefined, the value is set to false. */ this.multiple = false; /** * Enables the input with in the popup for filtering the options. */ this.searchable = false; /** * Disables the component on the interface. If the attribute’s value is undefined, the value is set to false. */ this.disabled = false; /** * Standard is the default option without any graphics other options are icon and avatar which places either the icon or avatar at the beginning of the row. * The props for the icon or avatar are passed as an object via the graphicsProps. */ this.variant = 'standard'; /** * Place a checkbox. */ this.checkbox = false; /** * Default option to be shown if the option doesn't match the filterText. */ this.notFoundText = ''; /** * Filter function which takes in filterText and dataSource and return a Promise. * Where filter text is the text to filter the value in dataSource array. * The returned promise should contain the array of options to be displayed. */ this.search = this.defaultSearchFunction; /** * Placeholder to placed on the search text box. */ this.searchText = ''; /** * Text to be displayed when there is no data available in the select. */ this.noDataText = ''; /** * Debounce timer for the search promise function. */ this.debounceTimer = 300; /** * The option that is displayed as the default selection, in the list box. Must be a valid value corresponding to the fw-select-option components used in Select. */ this.selectedOptions = []; /** * Whether clicking on the already selected option disables it. */ this.allowDeselect = true; this.handleSearchWithDebounce = debounce((filterText) => { var _a; this.isLoading = true; this.fwLoading.emit({ isLoading: this.isLoading }); if (filterText) { this.search(filterText, this.selectOptions).then((options) => { this.filteredOptions = (options === null || options === void 0 ? void 0 : options.length) > 0 ? this.serializeData(options) : [{ text: this.notFoundText, disabled: true }]; this.isLoading = false; this.fwLoading.emit({ isLoading: this.isLoading }); }); } else { this.filteredOptions = ((_a = this.selectOptions) === null || _a === void 0 ? void 0 : _a.length) > 0 ? this.selectOptions : [{ text: this.noDataText, disabled: true }]; this.isLoading = false; this.fwLoading.emit({ isLoading: this.isLoading }); } }, this, this.debounceTimer); } fwSelectedHandler(selectedItem) { const { value, selected } = selectedItem.detail; if (selected) { const selectedObj = this.filteredOptions.filter((option) => option.value === value)[0]; this.selectedOptionsState = this.multiple ? [...this.selectedOptionsState, selectedObj] : [selectedObj]; } else { this.selectedOptionsState = this.multiple ? this.selectedOptionsState.filter((option) => option.value !== value) : []; } this.isInternalValueChange = true; this.setValue(this.selectedOptionsState); } onKeyDown(ev) { switch (ev.key) { case 'ArrowDown': // If focus is on the last option, moves focus to the first option. // Ref - https://www.w3.org/TR/wai-aria-practices/examples/combobox/aria1.1pattern/listbox-combo.html this.arrowKeyCounter = cyclicIncrement(this.arrowKeyCounter, this.optionRefs.length - 1); this.optionRefs[this.arrowKeyCounter].setFocus(); ev.preventDefault(); ev.stopPropagation(); break; case 'ArrowUp': // If focus is on the first option, moves focus to the last option. // Ref - https://www.w3.org/TR/wai-aria-practices/examples/combobox/aria1.1pattern/listbox-combo.html this.arrowKeyCounter = cyclicDecrement(this.arrowKeyCounter, this.optionRefs.length - 1); this.optionRefs[this.arrowKeyCounter].setFocus(); ev.preventDefault(); ev.stopPropagation(); break; } } async clearFilter() { this.filteredOptions = this.selectOptions; if (this.searchable) { this.searchInput.value = ''; } } async scrollToLastSelected() { var _a; if (this.filteredOptions.length > 0 && this.valueExists()) { (_a = this.container .querySelector(`fw-select-option[id='${this.host.id}-option-${this.getLastSelectedValue()}']`)) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ block: 'nearest' }); } } async getSelectedOptions() { return this.selectedOptionsState; } /** * Pass an array of string in case of multi-select or string for single-select. */ async setSelectedValues(values) { if (this.options) { this.selectedOptionsState = this.options.filter((option) => this.isValueEqual(values, option)); this.isInternalValueChange = true; this.setValue(this.selectedOptionsState); } } async setSelectedOptions(options) { this.selectedOptionsState = options; this.isInternalValueChange = true; this.setValue(options); } async setFocus() { this.optionRefs = [ ...this.container.getElementsByTagName('fw-select-option'), ]; const lastValue = this.getLastSelectedValue(); if (lastValue && this.optionRefs.length > 0) { const lastValueIndex = this.optionRefs.findIndex((option) => option.value === lastValue); this.arrowKeyCounter = lastValueIndex === -1 ? 0 : lastValueIndex; } this.optionRefs[this.arrowKeyCounter].setFocus(); } onOptionsChange(newValue) { this.setDataSource(newValue); } disabledWatcher() { const options = this.options; // updating the object to retrigger this.options = [...options]; } onValueChange(newValue, oldValue) { if (!isEqual(newValue, oldValue)) { if (newValue) { this.validateValue(newValue); } else { newValue = this.multiple ? [] : ''; } this.selectOptions = this.selectOptions.map((option) => { option.selected = this.isValueEqual(newValue, option); return option; }); // Warning: Before mutating this.value inside this file set the isInternalValueChange to true. // This is to prevent triggering the below code which is executed whenever there is a change in the prop this.value if (!this.isInternalValueChange) { // source might change during dynamic select const source = this.options.length > 0 ? this.options : this.selectedOptionsState; this.selectedOptionsState = source.filter((option) => this.isValueEqual(newValue, option)); } this.fwChange.emit({ value: newValue, meta: { selectedOptions: this.selectedOptionsState }, }); this.isInternalValueChange = false; } } onFilterTextChange(newValue) { this.handleSearchWithDebounce(newValue); } valueExists() { return this.multiple ? this.value.length > 0 : !!this.value; } validateValue(value) { if (this.multiple && !Array.isArray(value)) { throw new Error('Value must be a array for multi-select'); } if (!this.multiple && typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'bigint') { throw new Error('Value must be a string or number or bigint for single-select'); } } getLastSelectedValue() { if (this.valueExists()) { return this.multiple ? this.value.slice(-1)[0] : this.value; } } setSelectedOptionsByValue(values) { if (this.options) { this.selectedOptionsState = this.options.filter((option) => this.isValueEqual(values, option)); } else { throw new Error('Options must be passed if value is set'); } } serializeData(dataSource) { return dataSource.map((option) => { var _a; return Object.assign(Object.assign({}, option), { checkbox: option.checkbox || this.checkbox, variant: option.variant || this.variant, selected: this.isValueEqual(this.value, option) || option.selected, disabled: option.disabled || this.disabled || (this.multiple && ((_a = this.value) === null || _a === void 0 ? void 0 : _a.length) >= this.max), allowDeselect: this.allowDeselect, }); }); } isValueEqual(value, option) { return this.multiple ? value.includes(option.value) : value === option.value; } setValue(options) { if ((options === null || options === void 0 ? void 0 : options.length) > 0) { this.value = this.multiple ? options.map((option) => option.value) : options[0].value; } else { this.value = this.multiple ? [] : ''; } } setDataSource(dataSource) { if (dataSource.length > 0) { this.selectOptions = this.serializeData(dataSource); } else { this.selectOptions = [{ text: this.noDataText, disabled: true }]; } this.filteredOptions = this.selectOptions; } renderSelectOptions(options) { return options.map((option) => (h("fw-select-option", Object.assign({ id: `${this.host.id}-option-${option.value}`, key: option.value }, option)))); } renderSearchInput() { return (h("fw-input", { ref: (searchInput) => (this.searchInput = searchInput), placeholder: this.searchText, onInput: () => this.handleSearchWithDebounce(this.searchInput.value) })); } componentWillLoad() { this.validateValue(this.value); if (this.selectedOptions.length > 0) { this.selectedOptionsState = this.selectedOptions; this.value = this.multiple ? this.selectedOptionsState.map((option) => option.value) : this.selectedOptionsState[0].value; } else if (this.valueExists()) { this.setSelectedOptionsByValue(this.value); } else { this.setValue([]); } if (this.multiple && typeof this.value === 'string') { throw Error('value must be a array of string when multiple is true'); } this.setDataSource(this.options); } render() { return (h("div", { class: 'container', ref: (container) => { this.container = container; } }, this.searchable && this.renderSearchInput(), this.renderSelectOptions(this.filteredOptions))); } get host() { return getElement(this); } static get watchers() { return { "options": ["onOptionsChange"], "disabled": ["disabledWatcher"], "value": ["onValueChange"], "filterText": ["onFilterTextChange"] }; } }; __decorate$1([ i18n({ keyName: 'search.noItemsFound' }) ], ListOptions.prototype, "notFoundText", void 0); __decorate$1([ i18n({ keyName: 'search.search' }) ], ListOptions.prototype, "searchText", void 0); __decorate$1([ i18n({ keyName: 'search.noDataAvailable' }) ], ListOptions.prototype, "noDataText", void 0); ListOptions.style = listOptionsCss; const selectCss = ":host{font-family:var(--fw-font-family, -apple-system, blinkmacsystemfont, \"Segoe UI\", roboto, oxygen, ubuntu, cantarell, \"Open Sans\", \"Helvetica Neue\", sans-serif);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-box-sizing:border-box;box-sizing:border-box}.field-control{position:relative}.field-control-label{display:block;font-size:12px;color:var(--fw-label-color, #475867);font-weight:600;margin-bottom:4px;padding-left:2px;line-height:20px}.field-control-label.required::after{content:\"*\";position:relative;display:inline-block;top:2px;font-size:14px;color:#d72d30;padding-left:2px;font-weight:700}.field-control-hint-text{font-family:-apple-system, blinkmacsystemfont, \"Segoe UI\", \"Roboto\", \"Helvetica Neue\", arial, sans-serif;font-size:12px;line-height:20px;margin-top:4px;margin-bottom:0;color:var(--fw-hint-color, #acb6be);position:inherit;display:block;padding-left:2px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.field-control-error-text{font-family:-apple-system, blinkmacsystemfont, \"Segoe UI\", \"Roboto\", \"Helvetica Neue\", arial, sans-serif;font-size:12px;line-height:20px;margin-top:4px;margin-bottom:0;color:var(--fw-error-color, #d72d30);position:inherit;display:block;padding-left:2px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.field-control-warning-text{font-family:-apple-system, blinkmacsystemfont, \"Segoe UI\", \"Roboto\", \"Helvetica Neue\", arial, sans-serif;font-size:12px;line-height:20px;margin-top:4px;margin-bottom:0;color:var(--fw-warning-color, #f8ab59);position:inherit;display:block;padding-left:2px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}:host{display:block}.input-container{width:calc(100% - 10px);border:var(--fw-select-border, 1px solid #cfd7df);border-radius:var(--fw-select-border-radius, 4px);padding-left:10px;background-color:#fff;-webkit-box-shadow:none;box-shadow:none;min-height:22px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;}.input-container .input-container-inner{display:-ms-flexbox;display:flex;-ms-flex-positive:1;flex-grow:1;-ms-flex-wrap:wrap;flex-wrap:wrap}.input-container .input-container-inner .tag-container{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.input-container .input-container-inner .tag-container fw-tag{margin:4px 4px 4px 0px}.input-container .input-container-inner input{-ms-flex-positive:1;flex-grow:1;width:100%;border:none;font-family:inherit;font-size:14px;font-weight:500;letter-spacing:0;line-height:1.4;background-color:#fff;min-width:20px;min-height:22px;-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;margin:4px 0px}.input-container .input-container-inner input:focus{border:none;outline:none}.input-container .input-container-inner input.multi-select{width:auto}.input-container .input-container-inner input:disabled{background-color:#f5f7f9 !important;border-color:transparent;cursor:not-allowed}.input-container fw-spinner{margin-right:8px}@media (prefers-reduced-motion){.input-container:hover,.input-container:focus{-webkit-transition:none;transition:none}}.input-container:hover,.input-container:focus{border:1px #475867 solid;-webkit-transition:0.2s linear;transition:0.2s linear}.input-container.error{border-color:#d72d30}.input-container.error>span.help-block{color:#d72d30}.input-container.warning{border-color:#f8ab59}.input-container.warning>span.help-block{color:#f8ab59}.input-container.select-disabled{color:#ebeff3;background-color:#f5f7f9 !important;border-style:solid;cursor:not-allowed}.input-container.select-disabled:hover{border:1px solid #cfd7df}.has-focus .input-container{outline:none;background:#fff;border:1px solid transparent;-webkit-box-shadow:0 0 0 2px #2c5cc5;box-shadow:0 0 0 2px #2c5cc5}.has-focus .input-container.error{border-color:#d72d30;-webkit-box-shadow:none;box-shadow:none}.has-focus .input-container.error>span.help-block{color:#d72d30}.has-focus .input-container.warning{border-color:#f8ab59;-webkit-box-shadow:0 0 0 1px #f8ab59;box-shadow:0 0 0 1px #f8ab59}.has-focus .input-container.warning>span.help-block{color:#f8ab59}.select-container{margin-bottom:var(--fw-select-margin-bottom, 0px);width:inherit;height:inherit;position:relative}.select-container .dropdown{z-index:99}.select-container .dropdown-status-icon{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-right:8px;min-width:20px;min-height:20px;-webkit-transition:all 0.15s;transition:all 0.15s;--icon-color:$color-elephant-800}.select-container .help-block{font-size:12px;margin-top:4px;line-height:20px;color:#acb6be;position:inherit;margin-bottom:0;display:block;padding-left:2px}.select-container .dropdown-status-icon.expanded{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.select-container.error{border-color:#d72d30}.select-container.error>span.help-block{color:#d72d30}.select-container.warning{border-color:#f8ab59}.select-container.warning>span.help-block{color:#f8ab59}::-webkit-input-placeholder{color:#92a2b1;opacity:1}::-moz-placeholder{color:#92a2b1;opacity:1}:-ms-input-placeholder{color:#92a2b1;opacity:1}::-ms-input-placeholder{color:#92a2b1;opacity:1}::placeholder{color:#92a2b1;opacity:1}:-ms-input-placeholder{color:#92a2b1}::-ms-input-placeholder{color:#92a2b1}"; var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; let Select = class { constructor(hostRef) { registerInstance(this, hostRef); this.fwChange = createEvent(this, "fwChange", 7); this.fwFocus = createEvent(this, "fwFocus", 7); this.fwBlur = createEvent(this, "fwBlur", 7); this.tagRefs = []; this.tagArrowKeyCounter = 0; this.changeEmittable = () => !this.disabled; this.innerOnFocus = (e) => { if (this.changeEmittable()) { this.hasFocus = true; this.fwFocus.emit(e); } }; this.innerOnClick = () => { var _a, _b; this.setFocus(); // Select the whole text in case of single select this.multiple || ((_b = (_a = this.selectInput) === null || _a === void 0 ? void 0 : _a.select) === null || _b === void 0 ? void 0 : _b.call(_a)); if (this.variant !== 'mail') { this.openDropdown(); } }; this.innerOnBlur = (e) => { if (this.changeEmittable()) { this.hasFocus = false; this.fwBlur.emit({ event: e, name: this.name, }); } }; this.openDropdown = () => { if (!this.isExpanded && this.changeEmittable()) { this.popover.show(); } }; this.closeDropdown = () => { if (this.isExpanded && this.changeEmittable()) { this.popover.hide(); } }; /** * If the dropdown is shown or not */ this.isExpanded = false; this.hasFocus = false; this.didInit = false; this.selectedOptionsState = []; this.isLoading = false; this.focusedOptionId = ''; this.hasHintTextSlot = false; this.hasWarningTextSlot = false; this.hasErrorTextSlot = false; /** * Label displayed on the interface, for the component. */ this.label = ''; /** * Name of the component, saved as part of form data. */ this.name = ''; /** * Type of option accepted as the input value. If a user tries to enter an option other than the specified type, the list is not populated. */ this.type = 'text'; /** * Theme based on which the list box is styled. */ this.state = 'normal'; /** * If true, the user cannot modify the default value selected. If the attribute's value is undefined, the value is set to true. */ this.readonly = false; /** * Specifies the select field as a mandatory field and displays an asterisk next to the label. If the attribute’s value is undefined, the value is set to false. */ this.required = false; /** * If true, the user must select a value. The default value is not displayed. */ this.forceSelect = true; /** * Disables the component on the interface. If the attribute’s value is undefined, the value is set to false. */ this.disabled = false; /** * Enables selection of multiple options. If the attribute’s value is undefined, the value is set to false. */ this.multiple = false; /** * Works with `multiple` enabled. Configures the maximum number of options that can be selected with a multi-select component. */ this.max = Number.MAX_VALUE; /** * The UI variant of the select to be used. */ this.variant = 'standard'; /** * Standard is the default option without any graphics other options are icon and avatar which places either the icon or avatar at the beginning of the row. * The props for the icon or avatar are passed as an object via the graphicsProps. */ this.optionsVariant = 'standard'; /** * Allow to search for value. Default is true. */ this.searchable = true; /** * Place a checkbox. */ this.checkbox = false; /** * Default option to be shown if the option doesn't match the filterText. */ this.notFoundText = ''; /** * Text to be displayed when there is no data available in the select. */ this.noDataText = ''; /** * Debounce timer for the search promise function. */ this.debounceTimer = 300; /** * Array of the options that is displayed as the default selection, in the list box. Must be a valid option corresponding to the fw-select-option components used in Select. */ this.selectedOptions = []; /** * Whether the select width to be same as that of the options. */ this.sameWidth = true; /** * Placement of the options list with respect to select. */ this.optionsPlacement = 'bottom'; /** * The variant of tag to be used. */ this.tagVariant = 'standard'; /** * Whether the arrow/caret should be shown in the select. */ this.caret = true; /** * If the default label prop is not used, then use this prop to pass the id of the label. */ this.labelledBy = ''; /** * Whether clicking on the already selected option disables it. */ this.allowDeselect = true; /** * Hint text displayed below the text box. */ this.hintText = ''; /** * Warning text displayed below the text box. */ this.warningText = ''; /** * Error text displayed below the text box. */ this.errorText = ''; this.tagContainerKeyDown = (ev) => { switch (ev.key) { case 'Escape': this.innerOnBlur(ev); this.closeDropdown(); break; case 'ArrowLeft': this.tagArrowKeyCounter--; if (this.tagArrowKeyCounter >= 0) { this.focusOnTag(this.tagArrowKeyCounter); } else { this.tagArrowKeyCounter = 0; } ev.stopImmediatePropagation(); break; case 'ArrowRight': this.tagArrowKeyCounter++; if (this.tagArrowKeyCounter >= this.value.length) { this.selectInput.focus(); } else { this.focusOnTag(this.tagArrowKeyCounter); } ev.stopImmediatePropagation(); break; } }; } onDropdownClose() { this.clearInput(); this.isExpanded = false; this.multiple && this.selectInput.focus(); } onDropdownOpen() { this.isExpanded = true; } onLoading(event) { this.isLoading = event.detail.isLoading; if (this.variant === 'mail' && !this.isLoading) { this.selectInput.value && this.openDropdown(); } } fwSelectedHandler(selectedItem) { var _a, _b; if (selectedItem.composedPath()[0].tagName === 'FW-LIST-OPTIONS') { this.selectedOptionsState = (_b = (_a = selectedItem.detail) === null || _a === void 0 ? void 0 : _a.meta) === null || _b === void 0 ? void 0 : _b.selectedOptions; this.value = selectedItem.detail.value; this.renderInput(); if (!this.multiple || this.variant === 'mail') { this.closeDropdown(); } selectedItem.stopImmediatePropagation(); selectedItem.stopPropagation(); selectedItem.preventDefault(); if (this.selectedOptionsState.length > 0) { this.fwChange.emit({ name: this.name, value: this.value, meta: { selectedOptions: this.selectedOptionsState }, }); } else { this.fwChange.emit({ name: this.name, value: this.value, meta: { shouldValidate: false, selectedOptions: this.selectedOptionsState, }, }); } } } // Listen to Tag close in case of multi-select fwCloseHandler(ev) { this.value = this.value.filter((value) => value !== ev.detail.value); } onKeyDown(ev) { switch (ev.key) { case 'ArrowDown': this.innerOnClick(); this.fwListOptions.setFocus(); ev.preventDefault(); ev.stopPropagation(); break; case 'ArrowLeft': case 'Backspace': if (this.multiple && this.selectInput.value === '') { this.focusOnTagContainer(); } break; case 'Escape': this.innerOnBlur(ev); this.closeDropdown(); break; case 'Tab': this.closeDropdown(); break; } } onOptionFocus(event) { if (event.composedPath()[0].tagName === 'FW-SELECT-OPTION') { this.focusedOptionId = event.detail.id; } } onOptionBlur(event) { if (event.composedPath()[0].tagName === 'FW-SELECT-OPTION') { this.focusedOptionId = ''; } } optionsChangedHandler() { this.renderInput(); } onOptionsChange(newValue) { var _a; const selectedValues = newValue === null || newValue === void 0 ? void 0 : newValue.filter((option) => option.selected); // If selected key is available in options schema use it // Or check for the value if (selectedValues.length > 0) { this.dataSource = newValue; this.selectedOptionsState = selectedValues; this.value = this.multiple ? this.selectedOptionsState.map((x) => x.value) : (_a = this.selectedOptionsState[0]) === null || _a === void 0 ? void 0 : _a.value; } else if (this.valueExists()) { this.dataSource = newValue.map((option) => { return Object.assign(Object.assign({}, option), { selected: this.isValueEqual(this.value, option) }); }); } else { this.dataSource = newValue; this.value = this.multiple ? [] : ''; this.selectedOptionsState = []; } } async getSelectedItem() { return this.fwListOptions.getSelectedOptions(); } async setSelectedValues(values) { this.fwListOptions.setSelectedValues(values); this.renderInput(); } async setSelectedOptions(options) { this.fwListOptions.setSelectedOptions(options); this.renderInput(); } async setFocus() { var _a; this.hasFocus = true; (_a = this.selectInput) === null || _a === void 0 ? void 0 : _a.focus(); } focusOnTagContainer() { this.tagRefs = [...this.tagContainer.getElementsByTagName('fw-tag')]; this.tagArrowKeyCounter = this.value.length - 1; this.focusOnTag(this.tagArrowKeyCounter); } focusOnTag(index) { var _a; (_a = this.tagRefs[index]) === null || _a === void 0 ? void 0 : _a.setFocus(); } clearInput() { if (!this.multiple && this.value) { this.renderInput(); return; } this.selectInput.value = ''; } isValueEqual(value, option) { return this.multiple ? value.includes(option.value) : value === option.value; } valueExists() { return this.value && (this.multiple ? this.value.length > 0 : !!this.value); } onInput() { this.searchValue = this.selectInput.value; if (this.selectInput.value) { this.variant !== 'mail' && this.openDropdown(); } else { // Clear selected value in case of single select. this.multiple || this.setSelectedValues(''); this.variant === 'mail' && this.closeDropdown(); } } renderTags() { if (this.multiple && Array.isArray(this.value)) { return this.selectedOptionsState.map((option) => { if (this.isValueEqual(this.value, option)) { return (h("fw-tag", { variant: this.tagVariant, graphicsProps: option.graphicsProps, text: option.text, disabled: option.disabled, value: option.value, focusable: false })); } }); } } renderInput() { if (this.selectedOptionsState.length > 0) { if (this.selectInput) { this.selectInput.value = ''; this.selectInput.value = this.multiple ? this.selectInput.value : this.selectedOptionsState[0].text || ''; } } else { if (this.selectInput) { this.selectInput.value = ''; } } } componentWillLoad() { var _a, _b; this.boundary || (this.boundary = this.host.parentElement); this.checkSlotContent(); if (this.variant === 'mail') { this.caret = false; this.multiple = true; } //TODO: The below is a rough draft and needs to be optimized for better performance. const selectOptions = Array.from(this.host.querySelectorAll('fw-select-option')); // Set value if the selectedOptions is provided if (this.selectedOptions.length > 0) { this.selectedOptionsState = this.selectedOptions; this.value = this.multiple ? this.selectedOptions.map((option) => option.value) : this.selectedOptions[0].value; } if (this.multiple) { if (this.multiple && typeof this.value === 'string') { throw Error('value must be a array of string when multiple is true'); } this.value = ((_a = this.value) === null || _a === void 0 ? void 0 : _a.length) > 0 ? this.value : []; } else { this.value = this.value ? this.value : ''; } const options = selectOptions.map((option) => { return { html: option.html, text: option.html ? option.optionText : option.textContent, value: option.value, selected: this.isValueEqual(this.value, option) || option.selected, disabled: option.disabled, htmlContent: option.html ? option.innerHTML : '', }; }); this.dataSource = options.length === 0 ? this.options : options; // Set selectedOptions if the value is provided if (!this.multiple && this.value && this.selectedOptions.length === 0) { this.selectedOptionsState = this.dataSource.filter((option) => this.value === option.value); } else if (this.multiple && this.value.length !== this.selectedOptions.length) { this.selectedOptionsState = this.dataSource.filter((option) => this.isValueEqual(this.value, option)); } if (((_b = this.dataSource) === null || _b === void 0 ? void 0 : _b.length) > 0) { // Check whether the selected data in the this.dataSource matches the value const selectedDataSource = this.dataSource.filter((option) => option.selected); const selectedDataSourceValues = selectedDataSource.map((option) => option.value); const selected = this.multiple ? selectedDataSourceValues : selectedDataSourceValues[0]; if (selectedDataSourceValues.length > 0 && JSON.stringify(this.value) !== JSON.stringify(selected)) { this.value = selected; this.selectedOptionsState = selectedDataSource; } } this.host.addEventListener('focus', this.setFocus); //this.host.innerHTML = ''; //Get id this.hostId = this.host.id || ''; } componentDidLoad() { this.renderInput(); this.didInit = true; } disconnectedCallback() { this.host.removeEventListener('focus', this.setFocus); } expandWatcher(expanded) { var _a, _b, _c, _d; if (this.variant === 'button') { const icon = (_d = (_c = (_b = (_a = this.host.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.select-container')) === null || _b === void 0 ? void 0 : _b.querySelector('fw-button')) === null || _c === void 0 ? void 0 : _c.shadowRoot) === null || _d === void 0 ? void 0 : _d.querySelector('fw-icon'); icon && (icon.name = expanded ? 'chevron-up' : 'chevron-down'); } } checkSlotContent() { this.hasHintTextSlot = hasSlot(this.host, 'hint-text'); this.hasWarningTextSlot = hasSlot(this.host, 'warning-text'); this.hasErrorTextSlot = hasSlot(this.host, 'error-text'); } getAriaDescribedBy() { if (this.state === 'normal') return `hint-${this.name}`; else if (this.state === 'error') return `error-${this.name}`; else if (this.state === 'warning') return `warning-${this.name}`; return null; } render() { const { host, name, value } = this; renderHiddenField(host, name, value); return (h(FieldControl, { inputId: this.name, label: this.label, labelId: `${this.label}-${this.name}`, state: this.state, hintTextId: `hint-${this.name}`, hintText: this.hintText, hasHintTextSlot: this.hasHintTextSlot, errorTextId: `error-${this.name}`, errorText: this.errorText, hasErrorTextSlot: this.hasErrorTextSlot, warningTextId: `warning-${this.name}`, warningText: this.warningText, hasWarningTextSlot: this.hasWarningTextSlot, required: this.required }, h("div", { "aria-disabled": this.disabled, class: { 'has-focus': this.hasFocus, } }, h("div", { class: { 'select-container': true, [this.state]: true }, role: 'combobox', "aria-controls": `${this.hostId}-listbox`, "aria-haspopup": 'listbox', "aria-expanded": this.isExpanded, "aria-owns": `${this.hostId}-listbox` }, h("fw-popover", { distance: '8', trigger: 'manual', ref: (popover) => (this.popover = popover), "same-width": this.sameWidth, placement: this.optionsPlacement, boundary: this.boundary }, h("div", { slot: 'popover-trigger', class: { 'input-container': this.variant !== 'button', [this.state]: true, 'select-disabled': this.disabled, }, onClick: () => this.innerOnClick(), onKeyDown: handleKeyDown(this.innerOnClick, true) }, this.variant === 'button' ? (h("fw-button", { style: { '--fw-button-label-vertical-padding': '7px' }, "show-caret-icon": true, id: `${this.hostId}-btn`, color: 'secondary', class: this.host.classList.value.includes('first') ? 'fw-button-group__button--first' : 'fw-button-group__button--last' }, this.value)) : (h(Fragment, null, h("div", { class: 'input-container-inner' }, this.multiple && (h("div", { class: 'tag-container', onFocus: this.focusOnTagContainer, ref: (tagContainer) => (this.tagContainer = tagContainer), onKeyDown: this.tagContainerKeyDown }, this.renderTags())), h("input", { ref: (selectInput) => (this.selectInput = selectInput), class: { 'multiple-select': this.multiple, }, autoComplete: 'off', disabled: this.disabled, name: this.name, id: this.name, placeholder: this.valueExists() ? '' : this.placeholder || '', readOnly: this.readonly, required: this.required, type: this.type, value: '', "aria-autocomplete": 'list', "aria-activedescendant": this.focusedOptionId, onInput: () => this.onInput(), onFocus: (e) => this.innerOnFocus(e), onBlur: (e) => this.innerOnBlur(e), "aria-invalid": this.state === 'error', "aria-describedby": `hint-${this.name} error-${this.name}` })), this.isLoading ? (h("fw-spinner", { size: 'small' })) : (this.caret && (h("span", { class: { 'dropdown-status-icon': true, 'expanded': this.isExpanded, } }, h("fw-icon", { name: 'chevron-down', size: 8, library: 'system' }))))))), h("fw-list-options", { ref: (fwListOptions) => (this.fwListOptions = fwListOptions), id: `${this.hostId}-listbox`, role: 'listbox', "aria-labelledby": this.labelledBy || `${this.hostId}-label`, notFoundText: this.notFoundText, debounceTimer: this.debounceTimer, noDataText: this.noDataText, search: this.search, selectedOptions: this.selectedOptions, variant: this.optionsVariant, "filter-text": this.searchValue, options: this.dataSource, value: this.value, multiple: this.multiple, max: this.max, disabled: this.disabled, checkbox: this.checkbox, allowDeselect: this.allowDeselect, slot: 'popover-content' })))))); } get host() { return getElement(this); } static get watchers() { return { "dataSource": ["optionsChangedHandler"], "options": ["onOptionsChange"], "isExpanded": ["expandWatcher"] }; } }; __decorate([ i18n({ keyName: 'search.noItemsFound' }) ], Select.prototype, "notFoundText", void 0); __decorate([ i18n({ keyName: 'search.noDataAvailable' }) ], Select.prototype, "noDataText", void 0); Select.style = selectCss; const selectOptionCss = ":host{font-family:var(--fw-font-family, -apple-system, blinkmacsystemfont, \"Segoe UI\", roboto, oxygen, ubuntu, cantarell, \"Open Sans\", \"Helvetica Neue\", sans-serif);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-box-sizing:border-box;box-sizing:border-box}.select-center{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.select-option{cursor:pointer;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;color:#12344d;font-size:14px;border-radius:4px;list-style:none;line-height:1.45;word-break:break-word;word-wrap:break-word;position:relative;margin-bottom:4px;padding-right:8px}.select-option.single-line{padding-top:6px;padding-bottom:6px}.select-option.multi-line{padding-top:8px;padding-bottom:8px}.select-option.standard{padding-left:0px}.select-option.icon{padding-left:10px}.select-option.avatar{padding-left:4px}.select-option.selected{font-weight:600;background-color:#e5f2fd}.select-option.selected:hover,.select-option.selected:focus{background-color:#e5f2fd}.select-option:hover{background-color:#ebeff3}.select-option:focus{background-color:#ebeff3;outline:#2c5cc5 auto 1px}.select-option.disabled{color:#92a2b1;position:relative;cursor:not-allowed;background-color:#f5f7f9}.select-option .description{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.select-option .description-text{font-weight:600}.select-option .description-subText{font-weight:400;font-size:12px;color:#475867}.select-option .description.icon-margin{margin-left:18px}.select-option .description.standard-margin{margin-left:12px}.select-option .selected-icon{min-width:24px;min-height:24px;margin-left:auto;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;-ms-flex-align:center;align-items:center}.select-option fw-icon{-ms-flex-align:center;align-items:center;display:-ms-flexbox;display:flex}"; let SelectOption = class { constructor(hostRef) { registerInstance(this, hostRef); this.fwSelected = createEvent(this, "fwSelected", 7); this.fwFocus = createEvent(this, "fwFocus", 7); this.fwBlur = createEvent(this, "fwBlur", 7); /** * Sets the state of the option to selected. The selected option is highlighted and a check mark is displayed next to it. If the attribute’s value is undefined, the value is set to false. */ this.selected = false; /** * Sets the state of the option to disabled. The selected option is disabled and greyed out. If the attribute’s value is undefined, the value is set to false. */ this.disabled = false; /** * States that the option is an HTML value. If the attribute's value is undefined, the value is set to true. */ this.html = false; /** * Standard is the default option without any graphics other options are icon and avatar which places either the icon or avatar at the beginning of the row. * The props for the icon or avatar are passed as an object via the graphicsProps. */ this.variant = 'standard'; /** * Place a checkbox. */ this.checkbox = false; /** * Whether clicking on the already selected option disables it. */ this.allowDeselect = true; } async setFocus() { this.rowContainer.focus(); } onKeyDown(ev) { switch (ev.key) { case 'Enter': this.onOptionSelected(); break; } } onOptionSelected() { if (this.disabled) { return; } if (this.selected && !this.allowDeselect) { return; } this.selected = !this.selected; const { value, selected } = this; this.fwSelected.emit({ value, selected }); } renderInnerHtml() { const description = this.createDescription(); const checkbox = this.checkbox ? this.createCheckbox() : ''; const selectedIconContainer = (h("span", { class: 'selected-icon' }, this.selected && (h("fw-icon", { name: 'check', size: 12, color: '#2C5CC5', library: 'system' })))); switch (this.variant) { case 'standard': return (h(Fragment, null, checkbox, description, selectedIconContainer)); case 'icon': return (h(Fragment, null, checkbox, this.createIcon(), description, selectedIconContainer)); case 'avatar': return (h(Fragment, null, checkbox, this.createAvatar(), description, selectedIconContainer)); } } createDescription() { return this.subText ? (h("div", { class: 'description ' + (this.variant === 'icon' ? 'icon-margin ' : 'standard-margin ') }, h("span", { class: 'description-text' }, this.text), h("span", { class: 'description-subText' }, this.subText))) : (h("span", { class: 'description ' + (this.variant === 'icon' ? 'icon-margin ' : 'standard-margin ') }, this.text)); } createIcon() { return h("fw-icon", Object.assign({}, this.graphicsProps)); } createCheckbox() { return h("fw-checkbox", { checked: this.selected }); } createAvatar() { return h("fw-avatar", Object.assign({ size: 'small' }, this.graphicsProps)); } render() { return (h("div", { role: 'option', tabindex: '-1', "aria-selected": this.selected, ref: (el) => (this.rowContainer = el), class: 'select-option ' + (this.selected && !this.checkbox ? 'selected ' : '') + (this.disabled ? 'disabled ' : '') + (this.html ? '' : (this.subText ? 'multi-line ' : 'single-line ') + (this.variant + ' ' + 'select-center')), onMouseDown: () => this.onOptionSelected(), onFocus: () => this.fwFocus.emit({ id: this.host.id }), onBlur: (e) => this.fwBlur.emit(e) }, this.html ? '' : this.text ? this.renderInnerHtml() : h("slot", null))); } componentDidLoad() { if (this.html) { this.rowContainer.innerHTML = this.htmlContent; } } get host() { return getElement(this); } }; SelectOption.style = selectOptionCss; const tagCss = ":host{font-family:var(--fw-font-family, -apple-system, blinkmacsystemfont, \"Segoe UI\", roboto, oxygen, ubuntu, cantarell, \"Open Sans\", \"Helvetica Neue\", sans-serif);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-box-sizing:border-box;box-sizing:border-box}.tag{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;color:#12344d;font-size:14px;word-break:break-all}.tag .content{line-height:20px;vertical-align:middle}.tag-standard{padding:2px 2px 2px 8px;border-radius:4px;font-weight:600;background-color:#ebeff3}.tag-avatar{padding:4px;border-radius:24px;font-weight:400;background:-webkit-gradient(linear, left top, left bottom, color-stop(2.56%, #f5f7f9), color-stop(95.75%, #f3f5f7));background:linear-gradient(180deg, #f5f7f9 2.56%, #f3f5f7 95.75%)}.tag-avatar fw-avatar{margin-right:8px}.tag .remove-btn{margin-left:8px;display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;color:#264966;font-size:16px}.tag .remove-btn.standard{min-height:20px;min-width:20px;border-radius:4px}.tag .remove-btn.avatar{min-height:24px;min-width:24px;border-radius:16px}.tag .remove-btn.avatar:hover,.tag .remove-btn.avatar:focus{background-color:#fff;cursor:pointer}.tag .remove-btn:hover,.tag .remove-btn:focus{background-color:#cfd7df;cursor:pointer}.tag .remove-btn.disabled{color:#447093;background-color:#ebeff3;cursor:not-allowed}"; let Tag = class { constructor(hostRef) { registerInstance(this, hostRef); this.fwClosed = createEvent(this, "fwClosed", 7); /** * The variant of tag to be displayed. */ this.variant = 'standard'; /** * The props need to be passed for the variant. If the variant is avatar then use this prop to send the props for the fw-avatar component. */ this.graphicsProps = {}; /** * Whether the Tag can be closed. */ this.closable = true; /** * Whether the Tag is focusable. */ this.focusable = true; this.removeTag = (e) => { if (this.disabled || !this.closable) { return; } const { value, text } = this; this.fwClosed.emit({ value, text }); e.stopPropagation(); }; } onKeyDown(event) { switch (event.key) { case 'Backspace': this.removeTag(event); event.preventDefault(); break; } } async setFocus() { this.tagContainer.focus(); } renderContent() { switch (this.variant) { case 'standard': return h("span", { class: 'content' }, this.text); case 'avatar': { return [ h("fw-avatar", Object.assign({ size: 'xsmall' }, this.graphicsProps)), h("span", { class: 'content' }, this.text), ]; } } } render() { return (h("div", { role: 'button', tabindex: '-1', class: `tag tag-${this.variant}`, ref: (tagContainer) => (this.tagContainer = tagContainer) }, this.renderContent(), this.closable && (h("span", { role: 'button', tabIndex: this.focusable ? 0 : -1, class: `remove-btn ${this.variant} ${this.disabled ? 'disabled' : ''}`, onClick: (e) => this.removeTag(e), onKeyDown: handleKeyDown(this.removeTag) }, h("fw-icon", { name: 'cross', size: 8, library: 'system' }))))); } get host() { return getElement(this); } }; Tag.style = tagCss; export { ListOptions as fw_list_options, Select as fw_select, SelectOption as fw_select_option, Tag as fw_tag };