UNPKG

vira

Version:

A simple and highly versatile design system using element-vir.

180 lines (170 loc) 6.32 kB
import { check } from '@augment-vir/assert'; import { filterMap } from '@augment-vir/common'; import { classMap, css, defineElementEvent, html, ifDefined, listen, nothing, testId, } from 'element-vir'; import { ChevronUp24Icon } from '../icons/index.js'; import { viraBorders } from '../styles/border.js'; import { viraFormCssVars } from '../styles/form-styles.js'; import { noUserSelect, viraAnimationDurations } from '../styles/index.js'; import { defineViraElement } from './define-vira-element.js'; import { ViraMenuTrigger } from './pop-up/vira-menu-trigger.element.js'; import { HorizontalAnchor } from './pop-up/vira-pop-up-trigger.element.js'; import { ViraIcon } from './vira-icon.element.js'; /** * Test ids for {@link ViraDropdown}. * * @category Internal */ export const viraDropdownTestIds = { trigger: 'dropdown-trigger', icon: 'dropdown-icon', prefix: 'dropdown-prefix', }; /** * A dropdown element that uses pop-up menus. * * @category Dropdown * @category Elements * @see https://electrovir.github.io/element-vir/vira/book/elements/dropdown/vira-dropdown */ export const ViraDropdown = defineViraElement()({ tagName: 'vira-dropdown', styles: css ` :host { display: inline-flex; vertical-align: middle; width: 256px; position: relative; max-width: 100%; } ${ViraMenuTrigger} { width: 100%; } .selection-display { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .trigger-icon { align-self: flex-start; will-change: transform; transform: rotate(180deg); transition: transform ${viraAnimationDurations['vira-interaction-animation-duration'].value} linear; } .trigger-icon-wrapper { flex-grow: 1; display: flex; justify-content: flex-end; } .open { & .trigger-icon { transform: rotate(0); } &:not(.open-upwards).dropdown-trigger { border-bottom-left-radius: 0; } &.open-upwards.dropdown-trigger { border-top-left-radius: 0; } } .dropdown-trigger { ${noUserSelect}; border: 1px solid ${viraFormCssVars['vira-form-border-color'].value}; height: 100%; width: 100%; box-sizing: border-box; display: flex; gap: 8px; text-align: left; align-items: center; padding: 3px; padding-left: 10px; border-radius: ${viraBorders['vira-form-input-radius'].value}; background-color: ${viraFormCssVars['vira-form-background-color'].value}; color: ${viraFormCssVars['vira-form-foreground-color'].value}; } .using-placeholder { opacity: 0.4; } `, events: { selectedChange: defineElementEvent(), openChange: defineElementEvent(), }, state() { return { /** `undefined` means the pop up is not currently showing. */ showPopUpResult: undefined, }; }, render({ state, inputs, dispatch, events, updateState }) { const selectedOptions = filterMap(inputs.selected, (selectedId) => inputs.options.find((option) => option.id === selectedId), check.isTruthy); const leadingIconTemplate = inputs.icon ? html ` <${ViraIcon.assign({ icon: inputs.icon, })} ${testId(viraDropdownTestIds.icon)} ></${ViraIcon}> ` : nothing; const shouldUsePlaceholder = !selectedOptions.length; const prefixTemplate = inputs.selectionPrefix && !shouldUsePlaceholder ? html ` <span class="selected-label-prefix" ${testId(viraDropdownTestIds.prefix)}> ${inputs.selectionPrefix} </span> ` : nothing; const selectionDisplay = shouldUsePlaceholder ? inputs.placeholder || '' : inputs.isMultiSelect && selectedOptions.length > 1 ? `${selectedOptions.length} Selected` : selectedOptions[0]?.label || ''; return html ` <${ViraMenuTrigger.assign({ items: inputs.options, selected: inputs.selected, isDisabled: inputs.isDisabled, isMultiSelect: inputs.isMultiSelect, z_debug_forceOpenState: inputs.z_debug_forceOpenState, popUpOffset: { vertical: -1, right: 24, }, horizontalAnchor: inputs.horizontalAnchor || HorizontalAnchor.Both, })} ${listen(ViraMenuTrigger.events.openChange, (event) => { updateState({ showPopUpResult: event.detail }); dispatch(new events.openChange(event.detail)); })} ${listen(ViraMenuTrigger.events.itemActivate, (event) => { dispatch(new events.selectedChange(event.detail)); })} > <div class="dropdown-trigger ${classMap({ open: !!state.showPopUpResult, 'open-upwards': !state.showPopUpResult?.popDown, })}" ${testId(viraDropdownTestIds.trigger)} > ${leadingIconTemplate} <span class="selection-display ${classMap({ 'using-placeholder': shouldUsePlaceholder, })}" title=${ifDefined(shouldUsePlaceholder ? undefined : selectionDisplay)} > ${prefixTemplate} ${selectionDisplay} </span> <span class="trigger-icon-wrapper"> <${ViraIcon.assign({ icon: ChevronUp24Icon })} class="trigger-icon" ></${ViraIcon}> </span> </div> </${ViraMenuTrigger}> `; }, });