@ahrowe/ui 0.47.2 → 0.47.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"multiSelect.mjs","names":[],"sources":["../../../../package/common/multiSelect/multiSelect.tsx"],"sourcesContent":["import { useEffect, useId, useRef, useState } from 'react';\nimport type { ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faChevronDown, faCheck, faXmark } from '@fortawesome/free-solid-svg-icons';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\n\nimport Chip from 'components/common/chip';\nimport SearchInput from 'components/common/searchInput';\nimport Tooltip from 'components/common/tooltip';\nimport Popover from 'components/common/popover';\nimport { PopoverAlign, PopoverPlacement, PopoverTrigger } from '../popover/popover.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { edgeIndex, nextIndex, scrollOptionIntoView } from '../utils/listNavigation';\nimport type { MultiSelectItem, MultiSelectLabels, MultiSelectProps, MultiSelectValue } from './multiSelect.types';\n\nimport styles from './multiSelect.module.pcss';\n\nconst EMPTY: MultiSelectValue[] = [];\n\nconst DEFAULT_LABELS: Required<MultiSelectLabels> = {\n clearSelection: 'Clear selection',\n create: (value) => `Add \"${value}\"`,\n searchPlaceholder: 'Search…',\n empty: 'No matches',\n};\n\nfunction MultiSelect(props: MultiSelectProps): ReactElement {\n const {\n items,\n value,\n defaultValue = [],\n onChange = () => {},\n label = '',\n alwaysFloatLabel = false,\n placeholder = '',\n searchable = false,\n allowCustomValues = false,\n maxSelected,\n clearable = true,\n disabled = false,\n readOnly = false,\n presentation,\n formValidator = null,\n errorMessage = '',\n isValid = true,\n className,\n style,\n classNames,\n styles: slotStyles,\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n labels,\n ...rest\n } = useComponentDefaults('MultiSelect', props);\n const text = useLabels('MultiSelect', labels, DEFAULT_LABELS);\n\n const rootRef = useRef<HTMLDivElement>(null);\n const controlRef = useRef<HTMLDivElement>(null);\n const searchRef = useRef<HTMLDivElement>(null);\n const optionsRef = useRef<HTMLDivElement>(null);\n const listboxId = useId();\n\n const [internalValue, setInternalValue] = useState<MultiSelectValue[]>(defaultValue);\n const [isOpen, setIsOpen] = useState(false);\n const [search, setSearch] = useState('');\n const [activeIndex, setActiveIndex] = useState(0);\n const [isFocused, setIsFocused] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n const [panelWidth, setPanelWidth] = useState<number>();\n\n // A ref as well as the prop: react-hooks/immutability refuses a mutation of the prop itself in\n // an event handler, and marking the validator touched happens in one.\n const fvRef = useRef(formValidator);\n const [, forceUpdate] = useState(0);\n useEffect(() => {\n fvRef.current = formValidator;\n const fv = fvRef.current;\n if (!fv) return;\n fv.label = label;\n fv.validate();\n const listener = () => forceUpdate((n) => n + 1);\n fv.registerOnUpdateListener(listener);\n return () => {\n fv.removeOnUpdateListener(listener);\n };\n }, [formValidator, label]);\n\n const isControlled = value !== undefined && !formValidator;\n const rawValue = formValidator ? formValidator.value : isControlled ? value : internalValue;\n const selected = Array.isArray(rawValue) ? (rawValue as MultiSelectValue[]) : EMPTY;\n\n const hasError = formValidator ? formValidator.touched && formValidator.hasError() : !isValid || !!errorMessage;\n const errorText = formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || '';\n\n const isInteractive = !disabled && !readOnly;\n const atMax = maxSelected !== undefined && selected.length >= maxSelected;\n\n const shouldFloatLabel = alwaysFloatLabel || isOpen || isFocused || selected.length > 0;\n\n const isSearchable = searchable || allowCustomValues;\n const typed = search.trim();\n const needle = isSearchable ? typed.toLowerCase() : '';\n const matches = needle ? items.filter((item) => item.label.toLowerCase().includes(needle)) : items;\n\n const canCreate =\n allowCustomValues &&\n typed.length > 0 &&\n !atMax &&\n !items.some((item) => item.label.toLowerCase() === needle || String(item.key).toLowerCase() === needle) &&\n !selected.some((key) => String(key).toLowerCase() === needle);\n\n const visibleItems: MultiSelectItem[] = canCreate ? [...matches, { key: typed, label: text.create(typed) }] : matches;\n\n // Mapped from `selected`, not filtered from `items`, to keep the chips in pick order. A custom\n // value has no entry in `items`, so it stands in for itself.\n const selectedItems = selected\n .map<MultiSelectItem | undefined>(\n (key) => items.find((item) => item.key === key) ?? (allowCustomValues ? { key, label: String(key) } : undefined),\n )\n .filter((item): item is MultiSelectItem => item !== undefined);\n\n function commit(next: MultiSelectValue[]) {\n if (formValidator) formValidator.set(next);\n else if (!isControlled) setInternalValue(next);\n onChange(next);\n }\n\n function toggle(item: MultiSelectItem) {\n if (!isInteractive || item.disabled) return;\n if (selected.includes(item.key)) commit(selected.filter((key) => key !== item.key));\n else if (!atMax) commit([...selected, item.key]);\n // The typed text has become a chip, so leaving it in the box would offer to add it again.\n if (canCreate && item.key === typed) setSearch('');\n }\n\n /**\n * A focused element that unmounts fires no blur, so removing the chip or clear button that was\n * just clicked would leave `isFocused` stuck true. Hand focus back so there is still something\n * left to blur.\n */\n function keepFocusInControl() {\n if (rootRef.current?.contains(document.activeElement)) controlRef.current?.focus();\n }\n\n function handleOpenChange(next: boolean) {\n if (next && !isInteractive) return;\n setIsOpen(next);\n if (next) {\n setPanelWidth(controlRef.current?.offsetWidth);\n setActiveIndex(0);\n } else {\n setSearch('');\n if (fvRef.current) fvRef.current.touched = true;\n }\n }\n\n useEffect(() => {\n if (!isOpen || !isSearchable) return;\n searchRef.current?.querySelector('input')?.focus();\n }, [isOpen, isSearchable]);\n\n useEffect(() => {\n if (!isOpen) return;\n scrollOptionIntoView(optionsRef.current, document.getElementById(`${listboxId}-${activeIndex}`));\n }, [isOpen, activeIndex, listboxId]);\n\n // Matches what the options render as `aria-disabled`, so the arrows cannot land on one.\n const navigation = {\n isDisabled: (index: number) => {\n const item = visibleItems[index];\n return !!item.disabled || (atMax && !selected.includes(item.key));\n },\n };\n\n function handleKeyDown(event: React.KeyboardEvent) {\n if (!isInteractive) return;\n\n if (!isOpen) {\n if (event.key === 'ArrowDown' || event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n handleOpenChange(true);\n } else if (event.key === 'Backspace' && selected.length > 0) {\n commit(selected.slice(0, -1));\n }\n return;\n }\n\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n setActiveIndex(nextIndex(activeIndex, event.key === 'ArrowDown' ? 1 : -1, visibleItems.length, navigation) ?? 0);\n } else if (event.key === 'Home' || event.key === 'End') {\n event.preventDefault();\n setActiveIndex(edgeIndex(event.key === 'Home' ? 'first' : 'last', visibleItems.length, navigation) ?? 0);\n } else if (event.key === 'Enter') {\n event.preventDefault();\n const item = visibleItems[activeIndex];\n if (item) toggle(item);\n } else if (event.key === 'Backspace' && !search && selected.length > 0) {\n commit(selected.slice(0, -1));\n }\n }\n\n const panel = (\n <div className={cx(styles.multiSelectPanel, classNames?.panel)} style={slotStyles?.panel}>\n {isSearchable && (\n <div ref={searchRef} className={cx(styles.multiSelectSearch, classNames?.search)} style={slotStyles?.search}>\n <SearchInput\n value={search}\n onChange={(value) => {\n setSearch(value);\n setActiveIndex(0);\n }}\n placeholder={text.searchPlaceholder}\n onKeyDown={handleKeyDown}\n />\n </div>\n )}\n <div\n ref={optionsRef}\n id={listboxId}\n role='listbox'\n aria-multiselectable='true'\n aria-label={label || undefined}\n className={cx(styles.multiSelectOptions, classNames?.options)}\n style={slotStyles?.options}\n >\n {visibleItems.length === 0 ? (\n <div className={cx(styles.multiSelectEmpty, classNames?.empty)} style={slotStyles?.empty}>\n {text.empty}\n </div>\n ) : (\n visibleItems.map((item, index) => {\n const isSelected = selected.includes(item.key);\n const isDisabled = item.disabled || (atMax && !isSelected);\n\n return (\n <div\n key={item.key}\n id={`${listboxId}-${index}`}\n role='option'\n aria-selected={isSelected}\n aria-disabled={isDisabled || undefined}\n className={cx(\n styles.multiSelectOption,\n index === activeIndex && styles.multiSelectOptionActive,\n isSelected && styles.multiSelectOptionSelected,\n isDisabled && styles.multiSelectOptionDisabled,\n classNames?.option,\n )}\n style={slotStyles?.option}\n onMouseDown={(event) => event.preventDefault()}\n onMouseEnter={() => setActiveIndex(index)}\n onClick={() => toggle(item)}\n >\n <span\n className={cx(styles.multiSelectOptionCheck, classNames?.optionCheck)}\n style={slotStyles?.optionCheck}\n >\n {isSelected && <FontAwesomeIcon icon={faCheck} />}\n </span>\n <span\n className={cx(styles.multiSelectOptionLabel, classNames?.optionLabel)}\n style={slotStyles?.optionLabel}\n >\n {item.label}\n </span>\n </div>\n );\n })\n )}\n </div>\n </div>\n );\n\n return (\n <div\n {...rest}\n ref={rootRef}\n className={cx(styles.multiSelect, disabled && styles.multiSelectDisabled, classNames?.root, className)}\n style={{ ...style, ...slotStyles?.root }}\n onMouseEnter={(event) => {\n setIsHovered(true);\n onMouseEnter?.(event);\n }}\n onMouseLeave={(event) => {\n setIsHovered(false);\n onMouseLeave?.(event);\n }}\n onFocus={(event) => {\n setIsFocused(true);\n onFocus?.(event);\n }}\n onBlur={(event) => {\n if (event.currentTarget.contains(event.relatedTarget as Node | null)) return;\n setIsFocused(false);\n onBlur?.(event);\n }}\n >\n <Popover\n isOpen={isOpen}\n onOpenChange={handleOpenChange}\n trigger={PopoverTrigger.Click}\n placement={PopoverPlacement.Bottom}\n align={PopoverAlign.Start}\n offset={4}\n withArrow={false}\n // Neither half of this is the keyboard-only default. A search field takes\n // the caret however the panel was opened, and without one the control\n // keeps the focus and points at the active option with\n // `aria-activedescendant`, so nothing may take it away.\n focusOnOpen={isSearchable}\n constrainWidth={false}\n width='trigger'\n disabled={!isInteractive}\n presentation={presentation}\n content={panel}\n styles={{ panel: panelWidth ? { minWidth: panelWidth } : undefined }}\n >\n <div\n ref={controlRef}\n role='combobox'\n tabIndex={disabled ? -1 : 0}\n aria-expanded={isOpen}\n aria-haspopup='listbox'\n aria-controls={listboxId}\n aria-activedescendant={isOpen && visibleItems.length > 0 ? `${listboxId}-${activeIndex}` : undefined}\n aria-disabled={disabled || undefined}\n aria-readonly={readOnly || undefined}\n className={cx(styles.multiSelectControl, classNames?.control)}\n style={slotStyles?.control}\n onKeyDown={handleKeyDown}\n >\n {placeholder && selectedItems.length === 0 && (\n <span\n className={cx(\n styles.multiSelectPlaceholder,\n (!label || shouldFloatLabel) && styles.multiSelectPlaceholderShown,\n !label && styles.multiSelectPlaceholderInstant,\n classNames?.placeholder,\n )}\n style={slotStyles?.placeholder}\n >\n {placeholder}\n </span>\n )}\n\n <div className={cx(styles.multiSelectValues, classNames?.values)} style={slotStyles?.values}>\n {selectedItems.map((item) => (\n <Chip\n key={item.key}\n text={item.label}\n showRemoveIcon={isInteractive}\n labels={{ remove: `Remove ${item.label}` }}\n onMouseDown={(event) => event.preventDefault()}\n onRemove={() => {\n toggle(item);\n keepFocusInControl();\n }}\n className={cx(styles.multiSelectChip, classNames?.chip)}\n style={slotStyles?.chip}\n />\n ))}\n </div>\n\n {clearable && isInteractive && selected.length > 0 && (\n <button\n type='button'\n tabIndex={-1}\n aria-label={text.clearSelection}\n className={cx(styles.multiSelectClearButton, classNames?.clearButton)}\n style={slotStyles?.clearButton}\n onMouseDown={(event) => event.preventDefault()}\n onClick={(event) => {\n event.stopPropagation();\n commit([]);\n keepFocusInControl();\n }}\n >\n <FontAwesomeIcon icon={faXmark} />\n </button>\n )}\n\n <FontAwesomeIcon\n icon={faChevronDown}\n className={cx(styles.multiSelectIcon, isOpen && styles.multiSelectIconOpen, classNames?.icon)}\n style={slotStyles?.icon as React.CSSProperties & Record<`--fa-font-${string}`, string>}\n />\n\n {label && (\n <div\n className={cx(\n styles.multiSelectLabel,\n shouldFloatLabel && styles.multiSelectLabelFloating,\n classNames?.label,\n )}\n style={slotStyles?.label}\n >\n {label}\n </div>\n )}\n\n {/* The visible border lives here rather than on the control, because the legend is what\n cuts the notch the floating label sits in. Same construction as Input. */}\n <fieldset\n className={cx(\n styles.multiSelectFieldset,\n (isFocused || isOpen) && styles.multiSelectFieldsetFocused,\n hasError && styles.multiSelectFieldsetError,\n classNames?.fieldset,\n )}\n style={slotStyles?.fieldset}\n >\n <legend\n className={cx(\n styles.multiSelectFieldsetLegend,\n shouldFloatLabel && styles.multiSelectFieldsetLegendActive,\n )}\n >\n <span>{label}</span>\n </legend>\n </fieldset>\n </div>\n </Popover>\n\n <Tooltip variant='error' message={errorText} isVisible={hasError && (isFocused || isHovered)} />\n </div>\n );\n}\n\nexport default MultiSelect;\n"],"mappings":"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"}
1
+ {"version":3,"file":"multiSelect.mjs","names":[],"sources":["../../../../package/common/multiSelect/multiSelect.tsx"],"sourcesContent":["import { useEffect, useId, useRef, useState } from 'react';\nimport type { ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faChevronDown, faCheck, faXmark } from '@fortawesome/free-solid-svg-icons';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\n\nimport Chip from 'components/common/chip';\nimport SearchInput from 'components/common/searchInput';\nimport Tooltip from 'components/common/tooltip';\nimport Popover from 'components/common/popover';\nimport { PopoverAlign, PopoverPlacement, PopoverTrigger } from '../popover/popover.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { edgeIndex, nextIndex, scrollOptionIntoView } from '../utils/listNavigation';\nimport type { MultiSelectItem, MultiSelectLabels, MultiSelectProps, MultiSelectValue } from './multiSelect.types';\n\nimport styles from './multiSelect.module.pcss';\n\nconst EMPTY: MultiSelectValue[] = [];\n\nconst DEFAULT_LABELS: Required<MultiSelectLabels> = {\n clearSelection: 'Clear selection',\n create: (value) => `Add \"${value}\"`,\n searchPlaceholder: 'Search…',\n empty: 'No matches',\n};\n\nfunction MultiSelect(props: MultiSelectProps): ReactElement {\n const {\n items,\n value,\n defaultValue = [],\n onChange = () => {},\n label = '',\n alwaysFloatLabel = false,\n placeholder = '',\n searchable = false,\n allowCustomValues = false,\n maxSelected,\n clearable = true,\n disabled = false,\n readOnly = false,\n presentation,\n formValidator = null,\n errorMessage = '',\n isValid = true,\n className,\n style,\n classNames,\n styles: slotStyles,\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n labels,\n ...rest\n } = useComponentDefaults('MultiSelect', props);\n const text = useLabels('MultiSelect', labels, DEFAULT_LABELS);\n\n const rootRef = useRef<HTMLDivElement>(null);\n const controlRef = useRef<HTMLDivElement>(null);\n const searchRef = useRef<HTMLDivElement>(null);\n const optionsRef = useRef<HTMLDivElement>(null);\n const listboxId = useId();\n\n const [internalValue, setInternalValue] = useState<MultiSelectValue[]>(defaultValue);\n const [isOpen, setIsOpen] = useState(false);\n const [search, setSearch] = useState('');\n const [activeIndex, setActiveIndex] = useState(0);\n const [isFocused, setIsFocused] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n const [panelWidth, setPanelWidth] = useState<number>();\n\n // A ref as well as the prop: react-hooks/immutability refuses a mutation of the prop itself in\n // an event handler, and marking the validator touched happens in one.\n const fvRef = useRef(formValidator);\n const [, forceUpdate] = useState(0);\n useEffect(() => {\n fvRef.current = formValidator;\n const fv = fvRef.current;\n if (!fv) return;\n fv.label = label;\n fv.validate();\n const listener = () => forceUpdate((n) => n + 1);\n fv.registerOnUpdateListener(listener);\n return () => {\n fv.removeOnUpdateListener(listener);\n };\n }, [formValidator, label]);\n\n const isControlled = value !== undefined && !formValidator;\n const rawValue = formValidator ? formValidator.value : isControlled ? value : internalValue;\n const selected = Array.isArray(rawValue) ? (rawValue as MultiSelectValue[]) : EMPTY;\n\n const hasError = formValidator ? formValidator.touched && formValidator.hasError() : !isValid || !!errorMessage;\n const errorText = formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || '';\n\n const isInteractive = !disabled && !readOnly;\n const atMax = maxSelected !== undefined && selected.length >= maxSelected;\n\n const shouldFloatLabel = alwaysFloatLabel || isOpen || isFocused || selected.length > 0;\n\n const isSearchable = searchable || allowCustomValues;\n const typed = search.trim();\n const needle = isSearchable ? typed.toLowerCase() : '';\n const matches = needle ? items.filter((item) => item.label.toLowerCase().includes(needle)) : items;\n\n const canCreate =\n allowCustomValues &&\n typed.length > 0 &&\n !atMax &&\n !items.some((item) => item.label.toLowerCase() === needle || String(item.key).toLowerCase() === needle) &&\n !selected.some((key) => String(key).toLowerCase() === needle);\n\n const visibleItems: MultiSelectItem[] = canCreate ? [...matches, { key: typed, label: text.create(typed) }] : matches;\n\n // Mapped from `selected`, not filtered from `items`, to keep the chips in pick order. A custom\n // value has no entry in `items`, so it stands in for itself.\n const selectedItems = selected\n .map<MultiSelectItem | undefined>(\n (key) => items.find((item) => item.key === key) ?? (allowCustomValues ? { key, label: String(key) } : undefined),\n )\n .filter((item): item is MultiSelectItem => item !== undefined);\n\n function commit(next: MultiSelectValue[]) {\n if (formValidator) formValidator.set(next);\n else if (!isControlled) setInternalValue(next);\n onChange(next);\n }\n\n function toggle(item: MultiSelectItem) {\n if (!isInteractive || item.disabled) return;\n if (selected.includes(item.key)) commit(selected.filter((key) => key !== item.key));\n else if (!atMax) commit([...selected, item.key]);\n // The typed text has become a chip, so leaving it in the box would offer to add it again.\n if (canCreate && item.key === typed) setSearch('');\n }\n\n /**\n * A focused element that unmounts fires no blur, so removing the chip or clear button that was\n * just clicked would leave `isFocused` stuck true. Hand focus back so there is still something\n * left to blur.\n */\n function keepFocusInControl() {\n if (rootRef.current?.contains(document.activeElement)) controlRef.current?.focus();\n }\n\n function handleOpenChange(next: boolean) {\n if (next && !isInteractive) return;\n setIsOpen(next);\n if (next) {\n setPanelWidth(controlRef.current?.offsetWidth);\n setActiveIndex(0);\n } else {\n setSearch('');\n if (fvRef.current) fvRef.current.touched = true;\n }\n }\n\n useEffect(() => {\n if (!isOpen || !isSearchable) return;\n searchRef.current?.querySelector('input')?.focus();\n }, [isOpen, isSearchable]);\n\n useEffect(() => {\n if (!isOpen) return;\n scrollOptionIntoView(optionsRef.current, document.getElementById(`${listboxId}-${activeIndex}`));\n }, [isOpen, activeIndex, listboxId]);\n\n // Matches what the options render as `aria-disabled`, so the arrows cannot land on one.\n const navigation = {\n isDisabled: (index: number) => {\n const item = visibleItems[index];\n return !!item.disabled || (atMax && !selected.includes(item.key));\n },\n };\n\n function handleKeyDown(event: React.KeyboardEvent) {\n if (!isInteractive) return;\n\n if (!isOpen) {\n if (event.key === 'ArrowDown' || event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n handleOpenChange(true);\n } else if (event.key === 'Backspace' && selected.length > 0) {\n commit(selected.slice(0, -1));\n }\n return;\n }\n\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n setActiveIndex(nextIndex(activeIndex, event.key === 'ArrowDown' ? 1 : -1, visibleItems.length, navigation) ?? 0);\n } else if (event.key === 'Home' || event.key === 'End') {\n event.preventDefault();\n setActiveIndex(edgeIndex(event.key === 'Home' ? 'first' : 'last', visibleItems.length, navigation) ?? 0);\n } else if (event.key === 'Enter') {\n event.preventDefault();\n const item = visibleItems[activeIndex];\n if (item) toggle(item);\n } else if (event.key === 'Backspace' && !search && selected.length > 0) {\n commit(selected.slice(0, -1));\n }\n }\n\n const panel = (\n <div className={cx(styles.multiSelectPanel, classNames?.panel)} style={slotStyles?.panel}>\n {isSearchable && (\n <div ref={searchRef} className={cx(styles.multiSelectSearch, classNames?.search)} style={slotStyles?.search}>\n <SearchInput\n value={search}\n onChange={(value) => {\n setSearch(value);\n setActiveIndex(0);\n }}\n placeholder={text.searchPlaceholder}\n onKeyDown={handleKeyDown}\n />\n </div>\n )}\n <div\n ref={optionsRef}\n id={listboxId}\n role='listbox'\n aria-multiselectable='true'\n aria-label={label || undefined}\n className={cx(styles.multiSelectOptions, classNames?.options)}\n style={slotStyles?.options}\n >\n {visibleItems.length === 0 ? (\n <div className={cx(styles.multiSelectEmpty, classNames?.empty)} style={slotStyles?.empty}>\n {text.empty}\n </div>\n ) : (\n visibleItems.map((item, index) => {\n const isSelected = selected.includes(item.key);\n const isDisabled = item.disabled || (atMax && !isSelected);\n\n return (\n <div\n key={item.key}\n id={`${listboxId}-${index}`}\n role='option'\n aria-selected={isSelected}\n aria-disabled={isDisabled || undefined}\n className={cx(\n styles.multiSelectOption,\n index === activeIndex && styles.multiSelectOptionActive,\n isSelected && styles.multiSelectOptionSelected,\n isDisabled && styles.multiSelectOptionDisabled,\n classNames?.option,\n )}\n style={slotStyles?.option}\n onMouseDown={(event) => event.preventDefault()}\n onMouseEnter={() => setActiveIndex(index)}\n onClick={() => toggle(item)}\n >\n <span\n className={cx(styles.multiSelectOptionCheck, classNames?.optionCheck)}\n style={slotStyles?.optionCheck}\n >\n {isSelected && <FontAwesomeIcon icon={faCheck} />}\n </span>\n <span\n className={cx(styles.multiSelectOptionLabel, classNames?.optionLabel)}\n style={slotStyles?.optionLabel}\n >\n {item.label}\n </span>\n </div>\n );\n })\n )}\n </div>\n </div>\n );\n\n return (\n <div\n {...rest}\n ref={rootRef}\n className={cx(\n styles.multiSelect,\n disabled && styles.multiSelectDisabled,\n !!label && styles.multiSelectWithLabel,\n classNames?.root,\n className,\n )}\n style={{ ...style, ...slotStyles?.root }}\n onMouseEnter={(event) => {\n setIsHovered(true);\n onMouseEnter?.(event);\n }}\n onMouseLeave={(event) => {\n setIsHovered(false);\n onMouseLeave?.(event);\n }}\n onFocus={(event) => {\n setIsFocused(true);\n onFocus?.(event);\n }}\n onBlur={(event) => {\n if (event.currentTarget.contains(event.relatedTarget as Node | null)) return;\n setIsFocused(false);\n onBlur?.(event);\n }}\n >\n <Popover\n isOpen={isOpen}\n onOpenChange={handleOpenChange}\n trigger={PopoverTrigger.Click}\n placement={PopoverPlacement.Bottom}\n align={PopoverAlign.Start}\n offset={4}\n withArrow={false}\n // Neither half of this is the keyboard-only default. A search field takes\n // the caret however the panel was opened, and without one the control\n // keeps the focus and points at the active option with\n // `aria-activedescendant`, so nothing may take it away.\n focusOnOpen={isSearchable}\n constrainWidth={false}\n width='trigger'\n disabled={!isInteractive}\n presentation={presentation}\n content={panel}\n styles={{ panel: panelWidth ? { minWidth: panelWidth } : undefined }}\n >\n <div\n ref={controlRef}\n role='combobox'\n tabIndex={disabled ? -1 : 0}\n aria-expanded={isOpen}\n aria-haspopup='listbox'\n aria-controls={listboxId}\n aria-activedescendant={isOpen && visibleItems.length > 0 ? `${listboxId}-${activeIndex}` : undefined}\n aria-disabled={disabled || undefined}\n aria-readonly={readOnly || undefined}\n className={cx(styles.multiSelectControl, classNames?.control)}\n style={slotStyles?.control}\n onKeyDown={handleKeyDown}\n >\n {placeholder && selectedItems.length === 0 && (\n <span\n className={cx(\n styles.multiSelectPlaceholder,\n (!label || shouldFloatLabel) && styles.multiSelectPlaceholderShown,\n !label && styles.multiSelectPlaceholderInstant,\n classNames?.placeholder,\n )}\n style={slotStyles?.placeholder}\n >\n {placeholder}\n </span>\n )}\n\n <div className={cx(styles.multiSelectValues, classNames?.values)} style={slotStyles?.values}>\n {selectedItems.map((item) => (\n <Chip\n key={item.key}\n text={item.label}\n showRemoveIcon={isInteractive}\n labels={{ remove: `Remove ${item.label}` }}\n onMouseDown={(event) => event.preventDefault()}\n onRemove={() => {\n toggle(item);\n keepFocusInControl();\n }}\n className={cx(styles.multiSelectChip, classNames?.chip)}\n style={slotStyles?.chip}\n />\n ))}\n </div>\n\n {clearable && isInteractive && selected.length > 0 && (\n <button\n type='button'\n tabIndex={-1}\n aria-label={text.clearSelection}\n className={cx(styles.multiSelectClearButton, classNames?.clearButton)}\n style={slotStyles?.clearButton}\n onMouseDown={(event) => event.preventDefault()}\n onClick={(event) => {\n event.stopPropagation();\n commit([]);\n keepFocusInControl();\n }}\n >\n <FontAwesomeIcon icon={faXmark} />\n </button>\n )}\n\n <FontAwesomeIcon\n icon={faChevronDown}\n className={cx(styles.multiSelectIcon, isOpen && styles.multiSelectIconOpen, classNames?.icon)}\n style={slotStyles?.icon as React.CSSProperties & Record<`--fa-font-${string}`, string>}\n />\n\n {label && (\n <div\n className={cx(\n styles.multiSelectLabel,\n shouldFloatLabel && styles.multiSelectLabelFloating,\n classNames?.label,\n )}\n style={slotStyles?.label}\n >\n {label}\n </div>\n )}\n\n {/* The visible border lives here rather than on the control, because the legend is what\n cuts the notch the floating label sits in. Same construction as Input. */}\n <fieldset\n className={cx(\n styles.multiSelectFieldset,\n (isFocused || isOpen) && styles.multiSelectFieldsetFocused,\n hasError && styles.multiSelectFieldsetError,\n classNames?.fieldset,\n )}\n style={slotStyles?.fieldset}\n >\n <legend\n className={cx(\n styles.multiSelectFieldsetLegend,\n shouldFloatLabel && styles.multiSelectFieldsetLegendActive,\n )}\n >\n <span>{label}</span>\n </legend>\n </fieldset>\n </div>\n </Popover>\n\n <Tooltip variant='error' message={errorText} isVisible={hasError && (isFocused || isHovered)} />\n </div>\n );\n}\n\nexport default MultiSelect;\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use client";var e=`multiSelect_fmViY`,t=`multiSelect-label_asHZ1`,n=`multiSelect-label-floating_d4-Om`,r=`multiSelect-fieldset_Eo29l`,i=`multiSelect-fieldset-focused_ZC2P6`,a=`multiSelect-fieldset-error_CWzEo`,o=`multiSelect-fieldset-legend_p-6nh`,s=`multiSelect-fieldset-legend-active_M0L7g`,c=`multiSelect-control_zLb7t`,l=`multiSelect-values_k1Q8x`,u=`multiSelect-chip_xqe8Q`,d=`multiSelect-placeholder_Tjrmh`,f=`multiSelect-placeholder-shown_8v36t`,p=`multiSelectFadeIn_rOplX`,m=`multiSelect-placeholder-instant_r9-TD`,h=`multiSelect-clearButton_FKs0m`,g=`multiSelect-icon_ovLu-`,_=`multiSelect-icon-open_h8VAu`,v=`multiSelect-disabled_Hm8mv`,y=`multiSelect-panel_VX7gU`,b=`multiSelect-search_g4OHX`,x=`multiSelect-options_MGZCv`,S=`multiSelect-option_hbvPY`,C=`multiSelect-option-active_mSWFG`,w=`multiSelect-option-selected_mjPq-`,T=`multiSelect-option-disabled_qSgYz`,E=`multiSelect-optionCheck_-YKoj`,D=`multiSelect-optionLabel_CFZke`,O=`multiSelect-empty_MjABY`,k={multiSelect:e,"multiSelect-label":`multiSelect-label_asHZ1`,multiSelectLabel:t,"multiSelect-label-floating":`multiSelect-label-floating_d4-Om`,multiSelectLabelFloating:n,"multiSelect-fieldset":`multiSelect-fieldset_Eo29l`,multiSelectFieldset:r,"multiSelect-fieldset-focused":`multiSelect-fieldset-focused_ZC2P6`,multiSelectFieldsetFocused:i,"multiSelect-fieldset-error":`multiSelect-fieldset-error_CWzEo`,multiSelectFieldsetError:a,"multiSelect-fieldset-legend":`multiSelect-fieldset-legend_p-6nh`,multiSelectFieldsetLegend:o,"multiSelect-fieldset-legend-active":`multiSelect-fieldset-legend-active_M0L7g`,multiSelectFieldsetLegendActive:s,"multiSelect-control":`multiSelect-control_zLb7t`,multiSelectControl:c,"multiSelect-values":`multiSelect-values_k1Q8x`,multiSelectValues:l,"multiSelect-chip":`multiSelect-chip_xqe8Q`,multiSelectChip:u,"multiSelect-placeholder":`multiSelect-placeholder_Tjrmh`,multiSelectPlaceholder:d,"multiSelect-placeholder-shown":`multiSelect-placeholder-shown_8v36t`,multiSelectPlaceholderShown:f,multiSelectFadeIn:p,"multiSelect-placeholder-instant":`multiSelect-placeholder-instant_r9-TD`,multiSelectPlaceholderInstant:m,"multiSelect-clearButton":`multiSelect-clearButton_FKs0m`,multiSelectClearButton:h,"multiSelect-icon":`multiSelect-icon_ovLu-`,multiSelectIcon:g,"multiSelect-icon-open":`multiSelect-icon-open_h8VAu`,multiSelectIconOpen:_,"multiSelect-disabled":`multiSelect-disabled_Hm8mv`,multiSelectDisabled:v,"multiSelect-panel":`multiSelect-panel_VX7gU`,multiSelectPanel:y,"multiSelect-search":`multiSelect-search_g4OHX`,multiSelectSearch:b,"multiSelect-options":`multiSelect-options_MGZCv`,multiSelectOptions:x,"multiSelect-option":`multiSelect-option_hbvPY`,multiSelectOption:S,"multiSelect-option-active":`multiSelect-option-active_mSWFG`,multiSelectOptionActive:C,"multiSelect-option-selected":`multiSelect-option-selected_mjPq-`,multiSelectOptionSelected:w,"multiSelect-option-disabled":`multiSelect-option-disabled_qSgYz`,multiSelectOptionDisabled:T,"multiSelect-optionCheck":`multiSelect-optionCheck_-YKoj`,multiSelectOptionCheck:E,"multiSelect-optionLabel":`multiSelect-optionLabel_CFZke`,multiSelectOptionLabel:D,"multiSelect-empty":`multiSelect-empty_MjABY`,multiSelectEmpty:O};export{k as default,e as multiSelect,u as multiSelectChip,h as multiSelectClearButton,c as multiSelectControl,v as multiSelectDisabled,O as multiSelectEmpty,p as multiSelectFadeIn,r as multiSelectFieldset,a as multiSelectFieldsetError,i as multiSelectFieldsetFocused,o as multiSelectFieldsetLegend,s as multiSelectFieldsetLegendActive,g as multiSelectIcon,_ as multiSelectIconOpen,t as multiSelectLabel,n as multiSelectLabelFloating,S as multiSelectOption,C as multiSelectOptionActive,E as multiSelectOptionCheck,T as multiSelectOptionDisabled,D as multiSelectOptionLabel,w as multiSelectOptionSelected,x as multiSelectOptions,y as multiSelectPanel,d as multiSelectPlaceholder,m as multiSelectPlaceholderInstant,f as multiSelectPlaceholderShown,b as multiSelectSearch,l as multiSelectValues};
1
+ "use client";var e=`multiSelect_fmViY`,t=`multiSelect-withLabel_s3N7A`,n=`multiSelect-label_asHZ1`,r=`multiSelect-label-floating_d4-Om`,i=`multiSelect-fieldset_Eo29l`,a=`multiSelect-fieldset-focused_ZC2P6`,o=`multiSelect-fieldset-error_CWzEo`,s=`multiSelect-fieldset-legend_p-6nh`,c=`multiSelect-fieldset-legend-active_M0L7g`,l=`multiSelect-control_zLb7t`,u=`multiSelect-values_k1Q8x`,d=`multiSelect-chip_xqe8Q`,f=`multiSelect-placeholder_Tjrmh`,p=`multiSelect-placeholder-shown_8v36t`,m=`multiSelectFadeIn_rOplX`,h=`multiSelect-placeholder-instant_r9-TD`,g=`multiSelect-clearButton_FKs0m`,_=`multiSelect-icon_ovLu-`,v=`multiSelect-icon-open_h8VAu`,y=`multiSelect-disabled_Hm8mv`,b=`multiSelect-panel_VX7gU`,x=`multiSelect-search_g4OHX`,S=`multiSelect-options_MGZCv`,C=`multiSelect-option_hbvPY`,w=`multiSelect-option-active_mSWFG`,T=`multiSelect-option-selected_mjPq-`,E=`multiSelect-option-disabled_qSgYz`,D=`multiSelect-optionCheck_-YKoj`,O=`multiSelect-optionLabel_CFZke`,k=`multiSelect-empty_MjABY`,A={multiSelect:e,"multiSelect-withLabel":`multiSelect-withLabel_s3N7A`,multiSelectWithLabel:t,"multiSelect-label":`multiSelect-label_asHZ1`,multiSelectLabel:n,"multiSelect-label-floating":`multiSelect-label-floating_d4-Om`,multiSelectLabelFloating:r,"multiSelect-fieldset":`multiSelect-fieldset_Eo29l`,multiSelectFieldset:i,"multiSelect-fieldset-focused":`multiSelect-fieldset-focused_ZC2P6`,multiSelectFieldsetFocused:a,"multiSelect-fieldset-error":`multiSelect-fieldset-error_CWzEo`,multiSelectFieldsetError:o,"multiSelect-fieldset-legend":`multiSelect-fieldset-legend_p-6nh`,multiSelectFieldsetLegend:s,"multiSelect-fieldset-legend-active":`multiSelect-fieldset-legend-active_M0L7g`,multiSelectFieldsetLegendActive:c,"multiSelect-control":`multiSelect-control_zLb7t`,multiSelectControl:l,"multiSelect-values":`multiSelect-values_k1Q8x`,multiSelectValues:u,"multiSelect-chip":`multiSelect-chip_xqe8Q`,multiSelectChip:d,"multiSelect-placeholder":`multiSelect-placeholder_Tjrmh`,multiSelectPlaceholder:f,"multiSelect-placeholder-shown":`multiSelect-placeholder-shown_8v36t`,multiSelectPlaceholderShown:p,multiSelectFadeIn:m,"multiSelect-placeholder-instant":`multiSelect-placeholder-instant_r9-TD`,multiSelectPlaceholderInstant:h,"multiSelect-clearButton":`multiSelect-clearButton_FKs0m`,multiSelectClearButton:g,"multiSelect-icon":`multiSelect-icon_ovLu-`,multiSelectIcon:_,"multiSelect-icon-open":`multiSelect-icon-open_h8VAu`,multiSelectIconOpen:v,"multiSelect-disabled":`multiSelect-disabled_Hm8mv`,multiSelectDisabled:y,"multiSelect-panel":`multiSelect-panel_VX7gU`,multiSelectPanel:b,"multiSelect-search":`multiSelect-search_g4OHX`,multiSelectSearch:x,"multiSelect-options":`multiSelect-options_MGZCv`,multiSelectOptions:S,"multiSelect-option":`multiSelect-option_hbvPY`,multiSelectOption:C,"multiSelect-option-active":`multiSelect-option-active_mSWFG`,multiSelectOptionActive:w,"multiSelect-option-selected":`multiSelect-option-selected_mjPq-`,multiSelectOptionSelected:T,"multiSelect-option-disabled":`multiSelect-option-disabled_qSgYz`,multiSelectOptionDisabled:E,"multiSelect-optionCheck":`multiSelect-optionCheck_-YKoj`,multiSelectOptionCheck:D,"multiSelect-optionLabel":`multiSelect-optionLabel_CFZke`,multiSelectOptionLabel:O,"multiSelect-empty":`multiSelect-empty_MjABY`,multiSelectEmpty:k};export{A as default,e as multiSelect,d as multiSelectChip,g as multiSelectClearButton,l as multiSelectControl,y as multiSelectDisabled,k as multiSelectEmpty,m as multiSelectFadeIn,i as multiSelectFieldset,o as multiSelectFieldsetError,a as multiSelectFieldsetFocused,s as multiSelectFieldsetLegend,c as multiSelectFieldsetLegendActive,_ as multiSelectIcon,v as multiSelectIconOpen,n as multiSelectLabel,r as multiSelectLabelFloating,C as multiSelectOption,w as multiSelectOptionActive,D as multiSelectOptionCheck,E as multiSelectOptionDisabled,O as multiSelectOptionLabel,T as multiSelectOptionSelected,S as multiSelectOptions,b as multiSelectPanel,f as multiSelectPlaceholder,h as multiSelectPlaceholderInstant,p as multiSelectPlaceholderShown,x as multiSelectSearch,u as multiSelectValues,t as multiSelectWithLabel};
2
2
  //# sourceMappingURL=multiSelect.module.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"multiSelect.module.mjs","names":[],"sources":["../../../../package/common/multiSelect/multiSelect.module.pcss"],"sourcesContent":[".multiSelect {\n position: relative;\n width: 100%;\n\n /* Input's notch geometry, so both labels float to the same height in a form row. */\n --multi-select-notch-height: 1.75em;\n\n &-label {\n position: absolute;\n left: 7px;\n right: 0;\n top: 50%;\n transform: translateY(-50%);\n font-size: 1em;\n color: var(--input-label-resting-color, var(--text-light));\n pointer-events: none;\n user-select: none;\n transition: all 0.3s;\n\n &-floating {\n top: 0;\n bottom: auto;\n left: 14px;\n font-size: 0.8em;\n color: var(--input-label-floating-color, var(--text-color));\n }\n }\n\n /* The field's border lives here, not on the control: the legend has to cut the notch out of it. */\n &-fieldset {\n position: absolute;\n inset: calc(var(--multi-select-notch-height) / -2) 0 0;\n width: 100%;\n margin: 0;\n padding: 0 0 0 8px;\n min-width: 0;\n box-sizing: border-box;\n border: 1px solid var(--border-color);\n border-radius: var(--default-border-radius);\n font-size: 0.8em;\n pointer-events: none;\n user-select: none;\n transition: border-color 0.15s;\n\n &-focused {\n border-color: var(--focus-border-color, var(--primary-color));\n }\n\n &-error {\n border-color: var(--error-border-color, var(--error-color));\n }\n\n &-legend {\n display: block;\n\n /* Not taken from the text: an empty span gets no line box, which raised a label-less\n field's border half a notch. */\n height: var(--multi-select-notch-height);\n max-width: 0;\n padding: 0;\n white-space: nowrap;\n transition: max-width 0.2s;\n\n &-active {\n max-width: 300px;\n }\n\n span {\n line-height: var(--multi-select-notch-height);\n padding: 0 5px;\n opacity: 0;\n }\n\n span:empty {\n padding: 0;\n }\n }\n }\n\n &-control {\n position: relative;\n display: flex;\n align-items: center;\n gap: 6px;\n box-sizing: border-box;\n width: 100%;\n\n /* Input and Dropdown both resolve to this: a 1.25 line-height plus 14px of padding. Kept as\n the formula rather than a flat 34px so it still matches them at another font size. */\n min-height: var(--multi-select-min-height, calc(1.25em + 14px));\n padding: 2px 8px;\n border-radius: var(--default-border-radius);\n background: var(--input-background, var(--background));\n color: var(--text-color);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n /* An outline here would draw a second, un-notched rectangle over the fieldset's border. */\n &:focus-visible {\n outline: none;\n\n .multiSelect-fieldset {\n border-color: var(--focus-border-color, var(--primary-color));\n }\n }\n }\n\n &-values {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px;\n flex: 1;\n min-width: 0;\n }\n\n /* At full size a Chip is nearly as tall as the field, so one row would set the height instead\n of fitting inside it. Shrinking the font shrinks the text and the 1em remove icon together. */\n &-chip {\n max-width: 100%;\n min-width: 0;\n font-size: 0.8em;\n }\n\n /* Faded in on a delay rather than swapped in: the label takes 0.3s to float clear, and showing\n the placeholder as it starts moving puts the two on top of each other. As Input does it. */\n &-placeholder {\n position: absolute;\n left: 8px;\n right: 1.75em;\n top: 50%;\n transform: translateY(-50%);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n opacity: 0;\n color: var(--input-label-resting-color, var(--text-light));\n pointer-events: none;\n user-select: none;\n\n &-shown {\n animation: 0.2s multiSelectFadeIn 0.3s forwards;\n }\n\n /* Nothing to wait for without a label. Must stay after `-shown` to win the specificity tie. */\n &-instant {\n animation-delay: 0s;\n }\n }\n\n @keyframes multiSelectFadeIn {\n from {\n opacity: 0;\n }\n\n to {\n opacity: 1;\n }\n }\n\n &-clearButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: 22px;\n height: 22px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--text-dark);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n &:hover {\n background: var(--background-accent);\n color: var(--text-color);\n }\n }\n\n &-icon {\n flex: none;\n color: var(--input-icon-color, var(--text-dark));\n transition: transform 0.2s;\n\n &-open {\n transform: rotate(180deg);\n }\n }\n\n &-disabled {\n opacity: 0.5;\n\n .multiSelect-control {\n cursor: default;\n }\n }\n}\n\n/* Portalled, so a sibling of the field rather than a descendant, and not nestable above. */\n.multiSelect-panel {\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 180px;\n}\n\n.multiSelect-search {\n flex: none;\n}\n\n.multiSelect-options {\n display: flex;\n flex-direction: column;\n max-height: var(--multi-select-panel-height, 260px);\n overflow-y: auto;\n}\n\n.multiSelect-option {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n border-radius: var(--default-border-radius);\n color: var(--text-color);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n &-active {\n background: var(--background-accent);\n }\n\n &-selected {\n color: var(--primary-color);\n font-weight: 600;\n }\n\n &-disabled {\n opacity: 0.45;\n cursor: default;\n pointer-events: none;\n }\n}\n\n.multiSelect-optionCheck {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: 1em;\n color: var(--primary-color);\n}\n\n.multiSelect-optionLabel {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.multiSelect-empty {\n padding: 12px 10px;\n color: var(--text-dark);\n text-align: center;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .multiSelect-label,\n .multiSelect-icon {\n transition: none;\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"multiSelect.module.mjs","names":[],"sources":["../../../../package/common/multiSelect/multiSelect.module.pcss"],"sourcesContent":[".multiSelect {\n position: relative;\n width: 100%;\n\n /* Input's notch geometry, so both labels float to the same height in a form row. */\n --multi-select-notch-height: 1.75em;\n --field-label-offset: 0px;\n\n /* Input's --field-label-offset, for the text only: the resting label and the placeholder. */\n &-withLabel {\n --field-label-offset: 1px;\n }\n\n &-label {\n position: absolute;\n left: 7px;\n right: 0;\n top: calc(50% + var(--field-label-offset));\n transform: translateY(-50%);\n font-size: 1em;\n color: var(--input-label-resting-color, var(--text-light));\n pointer-events: none;\n user-select: none;\n transition: all 0.3s;\n\n &-floating {\n top: 0;\n bottom: auto;\n left: 14px;\n font-size: 0.8em;\n color: var(--input-label-floating-color, var(--text-color));\n }\n }\n\n /* The field's border lives here, not on the control: the legend has to cut the notch out of it. */\n &-fieldset {\n position: absolute;\n inset: calc(var(--multi-select-notch-height) / -2) 0 0;\n width: 100%;\n margin: 0;\n padding: 0 0 0 8px;\n min-width: 0;\n box-sizing: border-box;\n border: 1px solid var(--border-color);\n border-radius: var(--default-border-radius);\n font-size: 0.8em;\n pointer-events: none;\n user-select: none;\n transition: border-color 0.15s;\n\n &-focused {\n border-color: var(--focus-border-color, var(--primary-color));\n }\n\n &-error {\n border-color: var(--error-border-color, var(--error-color));\n }\n\n &-legend {\n display: block;\n\n /* Not taken from the text: an empty span gets no line box, which raised a label-less\n field's border half a notch. */\n height: var(--multi-select-notch-height);\n max-width: 0;\n padding: 0;\n white-space: nowrap;\n transition: max-width 0.2s;\n\n &-active {\n max-width: 300px;\n }\n\n span {\n line-height: var(--multi-select-notch-height);\n padding: 0 5px;\n opacity: 0;\n }\n\n span:empty {\n padding: 0;\n }\n }\n }\n\n &-control {\n position: relative;\n display: flex;\n align-items: center;\n gap: 6px;\n box-sizing: border-box;\n width: 100%;\n\n /* Input's container sets the same, and Dropdown's button reaches it through a 1.25 line-height\n plus 14px of padding. Kept as the formula rather than a flat 34px so it still matches them at\n another font size. */\n min-height: var(--multi-select-min-height, calc(1.25em + 14px));\n\n /* No --field-label-offset: unlike text, a chip is a filled box nearly as tall as the field, and\n the eye measures it against the border, not the label. */\n padding: 2px 8px;\n border-radius: var(--default-border-radius);\n background: var(--input-background, var(--background));\n color: var(--text-color);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n /* An outline here would draw a second, un-notched rectangle over the fieldset's border. */\n &:focus-visible {\n outline: none;\n\n .multiSelect-fieldset {\n border-color: var(--focus-border-color, var(--primary-color));\n }\n }\n }\n\n &-values {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px;\n flex: 1;\n min-width: 0;\n }\n\n /* At full size a Chip is nearly as tall as the field, so one row would set the height instead\n of fitting inside it. Shrinking the font shrinks the text and the 1em remove icon together. */\n &-chip {\n max-width: 100%;\n min-width: 0;\n font-size: 0.8em;\n }\n\n /* Faded in on a delay rather than swapped in: the label takes 0.3s to float clear, and showing\n the placeholder as it starts moving puts the two on top of each other. As Input does it. */\n &-placeholder {\n position: absolute;\n left: 8px;\n right: 1.75em;\n top: calc(50% + var(--field-label-offset));\n transform: translateY(-50%);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n opacity: 0;\n color: var(--input-label-resting-color, var(--text-light));\n pointer-events: none;\n user-select: none;\n\n &-shown {\n animation: 0.2s multiSelectFadeIn 0.3s forwards;\n }\n\n /* Nothing to wait for without a label. Must stay after `-shown` to win the specificity tie. */\n &-instant {\n animation-delay: 0s;\n }\n }\n\n @keyframes multiSelectFadeIn {\n from {\n opacity: 0;\n }\n\n to {\n opacity: 1;\n }\n }\n\n &-clearButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: 22px;\n height: 22px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--text-dark);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n &:hover {\n background: var(--background-accent);\n color: var(--text-color);\n }\n }\n\n &-icon {\n flex: none;\n color: var(--input-icon-color, var(--text-dark));\n transition: transform 0.2s;\n\n &-open {\n transform: rotate(180deg);\n }\n }\n\n &-disabled {\n opacity: 0.5;\n\n .multiSelect-control {\n cursor: default;\n }\n }\n}\n\n/* Portalled, so a sibling of the field rather than a descendant, and not nestable above. */\n.multiSelect-panel {\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 180px;\n}\n\n.multiSelect-search {\n flex: none;\n}\n\n.multiSelect-options {\n display: flex;\n flex-direction: column;\n max-height: var(--multi-select-panel-height, 260px);\n overflow-y: auto;\n}\n\n.multiSelect-option {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n border-radius: var(--default-border-radius);\n color: var(--text-color);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n &-active {\n background: var(--background-accent);\n }\n\n &-selected {\n color: var(--primary-color);\n font-weight: 600;\n }\n\n &-disabled {\n opacity: 0.45;\n cursor: default;\n pointer-events: none;\n }\n}\n\n.multiSelect-optionCheck {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: 1em;\n color: var(--primary-color);\n}\n\n.multiSelect-optionLabel {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.multiSelect-empty {\n padding: 12px 10px;\n color: var(--text-dark);\n text-align: center;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .multiSelect-label,\n .multiSelect-icon {\n transition: none;\n }\n}\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"numberInput.module.mjs","names":[],"sources":["../../../../package/common/numberInput/numberInput.module.pcss"],"sourcesContent":[".numberInput {\n /* Only set when the split layout wraps this input in a flex row. */\n &-split {\n flex: 1;\n min-width: 0;\n }\n}\n\n/* ── Stacked layout: chevrons inside the field's right edge (mouse-oriented) ───────── */\n\n/* Input's `suffix` slot: stretch to the field so the chevrons get its full height. Two classes, to\n outrank Input's one-class suffix rules whichever stylesheet loads first. */\n.numberInput .arrowSuffix {\n align-self: stretch;\n display: flex;\n padding-right: 0;\n margin-top: 0;\n}\n\n.arrows {\n display: flex;\n flex-direction: column;\n align-self: stretch;\n border-left: 1px solid var(--border-color);\n\n /* Sitting flush against the field's edge, the chevrons' background would square off its\n rounded corner, so it is clipped. Read from --group-radius-* like Input's own border, so it\n still flattens inside a ButtonGroup. */\n overflow: hidden;\n border-top-right-radius: var(--group-radius-tr, var(--default-border-radius));\n border-bottom-right-radius: var(--group-radius-br, var(--default-border-radius));\n}\n\n.arrowBtn {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n background: none;\n border: none;\n padding: 0 0.5em;\n\n /* No min-height: the two halves divide the field's own height, so a stepper never makes a\n NumberInput taller than a plain Input. In em, so it tracks the field's text size. */\n min-width: 1.75em;\n font-size: 0.75em;\n line-height: 1;\n color: var(--text-dark);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n /* So a long press reads as a hold, not a text gesture. */\n touch-action: manipulation;\n user-select: none;\n -webkit-user-select: none;\n -webkit-touch-callout: none;\n transition:\n color 0.15s,\n opacity 0.15s;\n\n &:disabled {\n cursor: default;\n opacity: 0.35;\n }\n\n &:focus-visible {\n outline: 2px solid var(--focus-border-color, var(--primary-color));\n outline-offset: -2px;\n }\n\n /* (hover: hover) only — on touch a :hover state latches after a tap until you tap elsewhere.\n\n Colour-only, unlike the --background-accent-light hover surface used elsewhere in the\n library: this button sits flush against .arrows' 1px --border-color divider, and in dark\n palettes those two tokens are close enough in luminance that painting the surface right up\n to the line eats most of the divider's already-low contrast, under the pointer. Tuning the\n hover colour per theme would not hold — the collision is between three tokens (divider,\n hover surface, field background), so any new theme can reintroduce it. The chevron turning\n --primary-color is affordance enough on a target this small. */\n @media (hover: hover) {\n &:hover:not(:disabled) {\n color: var(--primary-color);\n }\n }\n\n &:active:not(:disabled) {\n color: var(--primary-color);\n }\n}\n\n/* ── Split layout: full-height − / + flanking the field (touch-oriented) ───────────── */\n\n/* ButtonGroup is inline-flex; a form field fills its row instead, with the Input taking the\n slack (see .numberInput-split). The class is repeated to beat `.buttonGroup`'s own `display`\n without depending on which CSS module the bundler emits first. */\n.split.split {\n display: flex;\n width: 100%;\n}\n\n/* Two classes, to outrank Button's one-class rules whichever stylesheet loads first. Resting state\n only: Button's hover and press layer tints over it. */\n.split .splitBtn {\n background: var(--input-background);\n color: var(--text-dark);\n border-color: var(--border-color);\n}\n\n.splitBtn {\n flex: 0 0 auto;\n\n /* No fixed height — ButtonGroup stretches these to the field.\n\n The width repeats Input's own height formula (`line-height: 1.25` plus the 8px/6px padding\n of `.input-field-default`) to come out square at any text size, and must follow it if that\n padding changes. `aspect-ratio: 1` cannot do this: a flex item's main size is resolved from\n its content before the stretched cross size exists. */\n align-self: stretch;\n width: calc(1.25em + 14px);\n touch-action: manipulation;\n user-select: none;\n -webkit-user-select: none;\n -webkit-touch-callout: none;\n transition: opacity 0.15s;\n\n &:disabled {\n opacity: 0.35;\n }\n\n &:focus-visible {\n outline: 2px solid var(--focus-border-color, var(--primary-color));\n outline-offset: 2px;\n }\n}\n\n/* One continuous border, so the buttons follow the field's focus and error colours — left grey,\n the group reads as a box inside a box. */\n.split:focus-within .splitBtn {\n border-color: var(--focus-border-color, var(--primary-color));\n}\n\n.split-invalid .splitBtn {\n border-color: var(--error-border-color);\n}\n"],"mappings":""}
1
+ {"version":3,"file":"numberInput.module.mjs","names":[],"sources":["../../../../package/common/numberInput/numberInput.module.pcss"],"sourcesContent":[".numberInput {\n /* Only set when the split layout wraps this input in a flex row. */\n &-split {\n flex: 1;\n min-width: 0;\n }\n}\n\n/* ── Stacked layout: chevrons inside the field's right edge (mouse-oriented) ───────── */\n\n/* Input's `suffix` slot: stretch to the field so the chevrons get its full height. Two classes, to\n outrank Input's one-class suffix rules whichever stylesheet loads first. */\n.numberInput .arrowSuffix {\n align-self: stretch;\n display: flex;\n padding-right: 0;\n}\n\n.arrows {\n display: flex;\n flex-direction: column;\n align-self: stretch;\n border-left: 1px solid var(--border-color);\n\n /* Sitting flush against the field's edge, the chevrons' background would square off its\n rounded corner, so it is clipped. Read from --group-radius-* like Input's own border, so it\n still flattens inside a ButtonGroup. */\n overflow: hidden;\n border-top-right-radius: var(--group-radius-tr, var(--default-border-radius));\n border-bottom-right-radius: var(--group-radius-br, var(--default-border-radius));\n}\n\n.arrowBtn {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n background: none;\n border: none;\n padding: 0 0.5em;\n\n /* No min-height: the two halves divide the field's own height, so a stepper never makes a\n NumberInput taller than a plain Input. In em, so it tracks the field's text size. */\n min-width: 1.75em;\n font-size: 0.75em;\n line-height: 1;\n color: var(--text-dark);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n /* So a long press reads as a hold, not a text gesture. */\n touch-action: manipulation;\n user-select: none;\n -webkit-user-select: none;\n -webkit-touch-callout: none;\n transition:\n color 0.15s,\n opacity 0.15s;\n\n &:disabled {\n cursor: default;\n opacity: 0.35;\n }\n\n &:focus-visible {\n outline: 2px solid var(--focus-border-color, var(--primary-color));\n outline-offset: -2px;\n }\n\n /* (hover: hover) only — on touch a :hover state latches after a tap until you tap elsewhere.\n\n Colour-only, unlike the --background-accent-light hover surface used elsewhere in the\n library: this button sits flush against .arrows' 1px --border-color divider, and in dark\n palettes those two tokens are close enough in luminance that painting the surface right up\n to the line eats most of the divider's already-low contrast, under the pointer. Tuning the\n hover colour per theme would not hold — the collision is between three tokens (divider,\n hover surface, field background), so any new theme can reintroduce it. The chevron turning\n --primary-color is affordance enough on a target this small. */\n @media (hover: hover) {\n &:hover:not(:disabled) {\n color: var(--primary-color);\n }\n }\n\n &:active:not(:disabled) {\n color: var(--primary-color);\n }\n}\n\n/* ── Split layout: full-height − / + flanking the field (touch-oriented) ───────────── */\n\n/* ButtonGroup is inline-flex; a form field fills its row instead, with the Input taking the\n slack (see .numberInput-split). The class is repeated to beat `.buttonGroup`'s own `display`\n without depending on which CSS module the bundler emits first. */\n.split.split {\n display: flex;\n width: 100%;\n}\n\n/* Two classes, to outrank Button's one-class rules whichever stylesheet loads first. Resting state\n only: Button's hover and press layer tints over it. */\n.split .splitBtn {\n background: var(--input-background);\n color: var(--text-dark);\n border-color: var(--border-color);\n}\n\n.splitBtn {\n flex: 0 0 auto;\n\n /* No fixed height — ButtonGroup stretches these to the field.\n\n The width repeats Input's own height, `calc(1.25em + 14px)` on `.input-container`, to come\n out square at any text size, and must follow it if that changes. `aspect-ratio: 1` cannot do this: a flex item's main size is resolved from\n its content before the stretched cross size exists. */\n align-self: stretch;\n width: calc(1.25em + 14px);\n touch-action: manipulation;\n user-select: none;\n -webkit-user-select: none;\n -webkit-touch-callout: none;\n transition: opacity 0.15s;\n\n &:disabled {\n opacity: 0.35;\n }\n\n &:focus-visible {\n outline: 2px solid var(--focus-border-color, var(--primary-color));\n outline-offset: 2px;\n }\n}\n\n/* One continuous border, so the buttons follow the field's focus and error colours — left grey,\n the group reads as a box inside a box. */\n.split:focus-within .splitBtn {\n border-color: var(--focus-border-color, var(--primary-color));\n}\n\n.split-invalid .splitBtn {\n border-color: var(--error-border-color);\n}\n"],"mappings":""}