@stackone/malachite 2.53.0 → 2.54.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FilterSearch.js","sources":["../../src/components/FilterSearch.tsx"],"sourcesContent":["import React, {\n useCallback,\n useEffect,\n useId,\n useInsertionEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport CaretDownIcon from './CustomIcons/CaretDownIcon';\nimport CheckmarkIcon from './CustomIcons/CheckmarkIcon';\nimport DismissIcon from './CustomIcons/DismissIcon';\nimport SearchIcon from './CustomIcons/SearchIcon';\nimport Spinner from './Spinner';\nimport { CONTROL_TEXT_SIZE, CONTROL_TEXT_SIZE_CLASS, type ControlSize } from './controlTextSize';\nimport { type ControlWidth, controlWidthStyle } from './controlWidth';\nimport { DROPDOWN_TRIGGER_ATTR, getTriggerVisual } from './dropdownTriggerStyle';\nimport { pushEscapeLayer } from './escapeLayers';\nimport {\n type FilterSearchChange,\n type FilterSearchFilter,\n type FilterSearchItemRow,\n type FilterSearchSelection,\n type FilterSearchToggle,\n type FilterSearchUpdate,\n chipText,\n clearAll,\n deselectAll,\n filterRows,\n orderSelections,\n removeFilter,\n resolveMode,\n searchRows,\n selectAll,\n toggleValue,\n} from './filterSearchModel';\nimport { FILTER_SEARCH_CLASS, ensureFilterSearchStyles } from './filterSearchStyles';\nimport {\n MENU_MAX_HEIGHT_PX,\n MENU_SPECS,\n type MenuGeometry,\n measureMenuGeometry,\n} from './floatingMenuGeometry';\nimport { FOCUS_RING } from './focusRing';\nimport { usePopupLayer } from './overlayLayers';\nimport { useTopLayer } from './topLayer';\nimport { useLatestRef } from './useLatestRef';\n\nexport type FilterSearchSize = ControlSize;\n\nexport interface FilterSearchProps {\n filters: FilterSearchFilter[];\n /** Controlled: the bar always shows `value`, so a URL-backed selection needs no syncing. */\n value: FilterSearchSelection[];\n /** `changes` lists only the filters that moved; an empty `items` means that filter was removed. */\n onChange: (next: FilterSearchSelection[], changes: FilterSearchChange[]) => void;\n /** Called when the clear control empties the bar, after `onChange`. */\n onClear?: () => void;\n /** The filter whose values are open, or `null` on the filter list — for loading values lazily. */\n onActiveFilterChange?: (filter: string | null) => void;\n placeholder?: string;\n size?: FilterSearchSize;\n width?: ControlWidth;\n loading?: boolean;\n disabled?: boolean;\n clearable?: boolean;\n /** Chips past this count collapse into `+N`. */\n maxVisibleChips?: number;\n className?: string;\n style?: React.CSSProperties;\n id?: string;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n}\n\nconst FIELD_SIZE_CLASSES: Record<FilterSearchSize, string> = {\n small: `min-h-[var(--malachite-control-height-small,28px)] gap-1 px-2.5 py-[2px] ${CONTROL_TEXT_SIZE_CLASS.small} rounded-[var(--malachite-radius-10,10px)]`,\n medium: `min-h-[var(--malachite-control-height-medium,32px)] gap-1 px-3 py-[3px] ${CONTROL_TEXT_SIZE_CLASS.medium} rounded-xl`,\n large: `min-h-[var(--malachite-control-height-large,42px)] gap-1.5 px-4 py-[6px] ${CONTROL_TEXT_SIZE_CLASS.large} rounded-[var(--malachite-radius-xxl,14px)]`,\n};\n\nconst CHIP_GAP: Record<FilterSearchSize, string> = {\n small: 'gap-1',\n medium: 'gap-1',\n large: 'gap-1.5',\n};\n\nconst CHIP_CLASSES = {\n resting:\n 'bg-[var(--malachite-tag-default-background,rgba(123,123,123,0.1))] text-[var(--malachite-text-primary,rgb(10,10,10))]',\n active: 'bg-[var(--malachite-accent-background,rgb(0,175,102))] text-[var(--malachite-card-background,rgb(255,255,255))]',\n};\n\nconst LOADING_ROWS = 5;\n\ntype Row =\n | { kind: 'filter'; filter: FilterSearchFilter }\n | { kind: 'toggle'; toggle: FilterSearchToggle }\n | { kind: 'item'; row: FilterSearchItemRow };\n\ninterface Chip {\n filter: string;\n text: string;\n}\n\ntype LabelMap = Map<string, { label: string; items: Map<string, string> }>;\n\nconst isEnabled = (row: Row | undefined) =>\n !!row && !(row.kind === 'item' && row.row.item.disabled);\n\nconst firstEnabledFrom = (rows: Row[], start: number): number => {\n for (let index = Math.max(start, 0); index < rows.length; index++) {\n if (isEnabled(rows[index])) return index;\n }\n return -1;\n};\n\nconst rowStyle: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n width: '100%',\n flexShrink: 0,\n border: 'none',\n textAlign: 'left',\n borderRadius: 8,\n padding: '6px 10px',\n font: 'inherit',\n cursor: 'pointer',\n minWidth: 0,\n};\n\nconst pillStyle: React.CSSProperties = {\n borderRadius: 6,\n padding: '2px 6px',\n flexShrink: 0,\n whiteSpace: 'nowrap',\n};\n\nconst ellipsis: React.CSSProperties = {\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n minWidth: 0,\n};\n\nconst trailing: React.CSSProperties = { ...ellipsis, marginLeft: 'auto', flexShrink: 1 };\n\nconst keepFocus = (event: React.MouseEvent) => event.preventDefault();\n\n// A chip's own buttons decide whether the menu opens; the field's mousedown must not.\nconst keepChipClick = (event: React.MouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n};\n\nconst RowContent: React.FC<{ row: Row; showFilter: boolean }> = ({ row, showFilter }) => {\n if (row.kind === 'filter') {\n return (\n <>\n <span className={FILTER_SEARCH_CLASS.pill} style={pillStyle}>\n {row.filter.label}\n </span>\n {row.filter.info && <span style={ellipsis}>{row.filter.info}</span>}\n {row.filter.example && (\n <span className={FILTER_SEARCH_CLASS.hint} style={trailing}>\n {row.filter.example}\n </span>\n )}\n </>\n );\n }\n\n if (row.kind === 'toggle') {\n return (\n <span style={{ color: 'var(--malachite-text-primary, rgb(10, 10, 10))' }}>\n {row.toggle === 'select' ? 'Select All' : 'Deselect All'}\n </span>\n );\n }\n\n const { item, filter, selected } = row.row;\n return (\n <>\n {showFilter && (\n <span className={FILTER_SEARCH_CLASS.pill} style={pillStyle}>\n {filter.label}\n </span>\n )}\n <span style={ellipsis}>{item.label}</span>\n {selected ? (\n <CheckmarkIcon\n size={16}\n style={{\n marginLeft: 'auto',\n flexShrink: 0,\n color: 'var(--malachite-accent-background, rgb(0, 175, 102))',\n }}\n />\n ) : (\n showFilter &&\n filter.info && (\n <span className={FILTER_SEARCH_CLASS.hint} style={trailing}>\n {filter.info}\n </span>\n )\n )}\n </>\n );\n};\n\nconst FilterSearch: React.FC<FilterSearchProps> = ({\n filters,\n value,\n onChange,\n onClear,\n onActiveFilterChange,\n placeholder = 'Search',\n size = 'medium',\n width,\n loading = false,\n disabled = false,\n clearable = false,\n maxVisibleChips = 4,\n className = '',\n style,\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n}) => {\n const instanceId = useId();\n const listboxId = `${instanceId}-listbox`;\n const optionId = (index: number) => `${instanceId}-option-${index}`;\n\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState('');\n const [activeFilter, setActiveFilterState] = useState<string | null>(null);\n const [highlighted, setHighlighted] = useState(0);\n const [position, setPosition] = useState<MenuGeometry | null>(null);\n\n const fieldRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const popoverRef = useRef<HTMLDivElement>(null);\n const listRef = useRef<HTMLDivElement>(null);\n const optionRefs = useRef<(HTMLButtonElement | null)[]>([]);\n\n useTopLayer(popoverRef, open);\n const popupIdentity = usePopupLayer('multiselect', open, popoverRef);\n\n // Labels outlive a reload of `filters`, so applied chips keep their names while it is in flight.\n const previousLabels = useRef<LabelMap>(new Map());\n const labels = useMemo(() => {\n const next: LabelMap = new Map(previousLabels.current);\n for (const filter of filters) {\n // Merged, not replaced: a filter can reload with its values still in flight.\n const known = next.get(filter.value)?.items ?? new Map<string, string>();\n next.set(filter.value, {\n label: filter.label,\n items: new Map([\n ...known,\n ...filter.items.map((item) => [item.value, item.label] as const),\n ]),\n });\n }\n return next;\n }, [filters]);\n useEffect(() => {\n previousLabels.current = labels;\n }, [labels]);\n\n const previousOrder = useRef<string[]>([]);\n const order = useMemo(\n () => orderSelections(value, previousOrder.current, activeFilter),\n [value, activeFilter],\n );\n useEffect(() => {\n previousOrder.current = order;\n }, [order]);\n\n const mode = resolveMode(activeFilter, query);\n const activeFilterDef = filters.find((filter) => filter.value === activeFilter);\n\n const activeFilterRef = useRef(activeFilter);\n // Through a ref so an inline callback does not re-bind the open menu's listeners every render.\n const onActiveFilterChangeRef = useLatestRef(onActiveFilterChange);\n const setActiveFilter = useCallback(\n (next: string | null) => {\n if (activeFilterRef.current === next) return;\n activeFilterRef.current = next;\n setActiveFilterState(next);\n onActiveFilterChangeRef.current?.(next);\n },\n [onActiveFilterChangeRef],\n );\n\n const rows = useMemo<Row[]>(() => {\n if (loading) return [];\n if (mode === 'filters') return filters.map((filter) => ({ kind: 'filter', filter }));\n const { toggle, items } =\n mode === 'items'\n ? filterRows(activeFilterDef, value, query)\n : searchRows(filters, value, query);\n return [\n ...(toggle ? [{ kind: 'toggle' as const, toggle }] : []),\n ...items.map((row) => ({ kind: 'item' as const, row })),\n ];\n }, [loading, mode, filters, activeFilterDef, value, query]);\n\n // In `order`, so a filter emptied while it is being edited keeps its place in the row.\n const chips = useMemo<Chip[]>(() => {\n const applied = new Map(value.map((selection) => [selection.filter, selection.items]));\n const chipFor = (filter: string): Chip[] => {\n const entry = labels.get(filter);\n const items = applied.get(filter);\n if (!items) {\n return filter === activeFilter ? [{ filter, text: entry?.label ?? filter }] : [];\n }\n if (!entry && loading) return [];\n const itemLabels = items.map((item) => entry?.items.get(item) ?? item);\n return [{ filter, text: chipText(entry?.label ?? filter, itemLabels) }];\n };\n const pendingIsNew = activeFilter !== null && !order.includes(activeFilter);\n return [...order, ...(pendingIsNew ? [activeFilter] : [])].flatMap(chipFor);\n }, [order, value, labels, loading, activeFilter]);\n\n const visibleChips = chips.slice(0, maxVisibleChips);\n const hiddenChipCount = chips.length - visibleChips.length;\n\n const emit = useCallback(\n ({ next, changes }: FilterSearchUpdate) => {\n if (changes.length > 0) onChange(next, changes);\n },\n [onChange],\n );\n\n const close = useCallback(() => {\n setOpen(false);\n setQuery('');\n setActiveFilter(null);\n setPosition(null);\n }, [setActiveFilter]);\n\n const openMenu = () => {\n if (disabled || open) return;\n setHighlighted(0);\n setOpen(true);\n };\n\n const updatePosition = useCallback(() => {\n const field = fieldRef.current;\n if (!field) return;\n setPosition(\n measureMenuGeometry(popoverRef.current, {\n triggerRect: field.getBoundingClientRect(),\n preset: 'standard',\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n }),\n );\n }, []);\n\n useEffect(() => {\n if (disabled && open) close();\n }, [disabled, open, close]);\n\n useEffect(() => {\n if (!open) return;\n const handleClickOutside = (event: MouseEvent) => {\n const path = event.composedPath();\n const field = fieldRef.current;\n const popover = popoverRef.current;\n if ((!field || !path.includes(field)) && (!popover || !path.includes(popover))) {\n close();\n }\n };\n let frame: number | null = null;\n const handleScrollOrResize = () => {\n if (frame !== null) return;\n frame = requestAnimationFrame(() => {\n frame = null;\n updatePosition();\n });\n };\n const popEscapeLayer = pushEscapeLayer(close);\n document.addEventListener('mousedown', handleClickOutside);\n window.addEventListener('scroll', handleScrollOrResize, true);\n window.addEventListener('resize', handleScrollOrResize);\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n window.removeEventListener('scroll', handleScrollOrResize, true);\n window.removeEventListener('resize', handleScrollOrResize);\n if (frame !== null) cancelAnimationFrame(frame);\n popEscapeLayer();\n };\n }, [open, close, updatePosition]);\n\n // Chips wrap onto a second line as they are added, which moves the menu's anchor.\n useLayoutEffect(() => {\n if (!open) return;\n updatePosition();\n if (typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(() => updatePosition());\n if (popoverRef.current) observer.observe(popoverRef.current);\n if (fieldRef.current) observer.observe(fieldRef.current);\n return () => observer.disconnect();\n }, [open, updatePosition]);\n\n // Every mode reuses one list element, so a new list would otherwise open at the old offset.\n // biome-ignore lint/correctness/useExhaustiveDependencies: the reset is keyed on what the list shows\n useLayoutEffect(() => {\n if (listRef.current) listRef.current.scrollTop = 0;\n }, [mode, activeFilter, query]);\n\n useInsertionEffect(() => {\n if (open) ensureFilterSearchStyles();\n }, [open]);\n\n const scrollToRow = (index: number) => {\n requestAnimationFrame(() => {\n const row = optionRefs.current[index];\n const list = listRef.current;\n if (!row || !list) return;\n const top = row.offsetTop;\n const bottom = top + row.offsetHeight;\n if (bottom > list.scrollTop + list.clientHeight) {\n list.scrollTop = bottom - list.clientHeight;\n } else if (top < list.scrollTop) {\n list.scrollTop = top;\n }\n });\n };\n\n const choose = (row: Row) => {\n if (row.kind === 'filter') {\n setActiveFilter(row.filter.value);\n setQuery('');\n setHighlighted(0);\n return;\n }\n\n if (row.kind === 'toggle') {\n const scope = mode === 'items' && activeFilterDef ? [activeFilterDef] : filters;\n emit(\n row.toggle === 'select'\n ? selectAll(scope, value, query)\n : deselectAll(scope, value, query),\n );\n setQuery('');\n setHighlighted(0);\n return;\n }\n\n const { item, filter } = row.row;\n if (item.disabled) return;\n const update = toggleValue(value, filter.value, item.value);\n emit(update);\n setQuery('');\n\n if (mode !== 'items') {\n setHighlighted(0);\n return;\n }\n // The query is cleared, so the list grows back: keep the cursor on the value just chosen.\n const { toggle, items } = filterRows(filter, update.next, '');\n const index = items.findIndex((candidate) => candidate.item.value === item.value);\n const nextIndex = index < 0 ? 0 : index + (toggle ? 1 : 0);\n setHighlighted(nextIndex);\n scrollToRow(nextIndex);\n };\n\n const moveHighlight = (direction: 1 | -1) => {\n if (rows.length === 0) return;\n let next = activeRow < 0 ? highlighted : activeRow;\n for (let step = 0; step < rows.length; step++) {\n next = (next + direction + rows.length) % rows.length;\n if (isEnabled(rows[next])) {\n setHighlighted(next);\n scrollToRow(next);\n return;\n }\n }\n };\n\n const removeChip = (filter: string) => {\n if (filter === activeFilter) setActiveFilter(null);\n emit(removeFilter(value, filter));\n };\n\n const editChip = (filter: string) => {\n if (disabled || loading) return;\n setActiveFilter(filter);\n setQuery('');\n setHighlighted(0);\n setOpen(true);\n inputRef.current?.focus();\n };\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {\n // Enter and the arrows belong to the input method while a composition is in progress;\n // Safari reports the committing Enter as keyCode 229 with `isComposing` already false.\n if (event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229) return;\n switch (event.key) {\n case 'ArrowDown':\n case 'ArrowUp': {\n event.preventDefault();\n if (!open) {\n openMenu();\n return;\n }\n moveHighlight(event.key === 'ArrowDown' ? 1 : -1);\n return;\n }\n case 'Enter': {\n event.preventDefault();\n if (!open) {\n openMenu();\n return;\n }\n const row = rows[activeRow];\n if (isEnabled(row)) choose(row as Row);\n return;\n }\n case 'Backspace': {\n if (query !== '') return;\n const pending =\n activeFilter !== null && !value.some((s) => s.filter === activeFilter);\n if (pending) {\n event.preventDefault();\n setActiveFilter(null);\n setHighlighted(0);\n return;\n }\n // The last chip on screen: one hidden while its labels load cannot be removed unseen.\n const shown = chips.filter((chip) => value.some((s) => s.filter === chip.filter));\n const last = shown[shown.length - 1];\n if (!last) return;\n event.preventDefault();\n removeChip(last.filter);\n return;\n }\n }\n };\n\n const handleClear = (event: React.MouseEvent) => {\n event.stopPropagation();\n emit(clearAll(value));\n setActiveFilter(null);\n setQuery('');\n onClear?.();\n };\n\n const backToFilters = () => {\n setActiveFilter(null);\n setQuery('');\n setHighlighted(0);\n };\n\n const showsClear = clearable && value.length > 0 && !disabled && !loading;\n // A disabled row cannot hold the cursor: it falls through to the next enabled one.\n const activeRow = open ? firstEnabledFrom(rows, highlighted) : -1;\n\n const triggerVisual = getTriggerVisual({ disabled, open });\n\n const emptyMessage = mode === 'filters' ? 'No filters available' : 'No matching values';\n\n const popoverStyle: React.CSSProperties = {\n position: 'fixed',\n top: position?.top ?? 0,\n left: position?.left ?? 0,\n width: position?.width,\n background: MENU_SPECS.standard.background,\n border: MENU_SPECS.standard.border,\n borderRadius: MENU_SPECS.standard.radius,\n boxShadow: MENU_SPECS.standard.shadow,\n padding: MENU_SPECS.standard.padding,\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n maxHeight: position?.maxHeight ?? MENU_MAX_HEIGHT_PX,\n fontSize: CONTROL_TEXT_SIZE[size],\n visibility: position ? 'visible' : 'hidden',\n opacity: position ? 1 : 0,\n };\n\n return (\n <>\n <div\n className={`relative min-w-0 ${className}`}\n style={{ ...controlWidthStyle(width), ...style }}\n >\n <div\n ref={fieldRef}\n {...{ [DROPDOWN_TRIGGER_ATTR]: '' }}\n data-open={open || undefined}\n // On the field, not the input: focus can move on to a chip's own buttons.\n onBlur={(event) => {\n const next = event.relatedTarget as Node | null;\n if (\n next &&\n (fieldRef.current?.contains(next) || popoverRef.current?.contains(next))\n ) {\n return;\n }\n close();\n }}\n onMouseDown={(event) => {\n if (disabled) return;\n if (event.target !== inputRef.current) event.preventDefault();\n inputRef.current?.focus();\n openMenu();\n }}\n style={{\n transition: 'border-color 0.15s ease, box-shadow 0.15s ease',\n ...triggerVisual,\n }}\n className={`w-full min-w-0 flex items-center border border-solid font-normal ${FIELD_SIZE_CLASSES[size]} ${disabled ? 'cursor-not-allowed' : 'cursor-text'}`}\n >\n <SearchIcon\n size={13}\n className=\"shrink-0 mr-1\"\n style={{ color: 'var(--malachite-text-placeholder, rgb(187, 187, 187))' }}\n />\n {/* Chips wrap here, never into the icon or caret; the reserve keeps the clear button off them. */}\n <div\n className={`flex flex-1 min-w-0 flex-wrap items-center ${CHIP_GAP[size]} ${showsClear ? 'pr-[18px]' : ''}`}\n >\n {visibleChips.map((chip) => {\n const active = open && chip.filter === activeFilter;\n return (\n <span\n key={chip.filter}\n onMouseDown={keepChipClick}\n className={`inline-flex items-center gap-1 max-w-[max(60px,calc(100%-64px))] min-w-0 h-[22px] pl-1.5 pr-1 rounded-md text-xs ${active ? CHIP_CLASSES.active : CHIP_CLASSES.resting}`}\n >\n <button\n type=\"button\"\n disabled={disabled}\n title={chip.text}\n aria-label={`Edit filter ${chip.text}`}\n onClick={() => editChip(chip.filter)}\n className={`truncate min-w-0 bg-transparent border-none p-0 text-[length:inherit] text-current ${disabled || loading ? 'cursor-default' : 'cursor-pointer'} ${FOCUS_RING}`}\n >\n {chip.text}\n </button>\n {!disabled && (\n <button\n type=\"button\"\n aria-label={`Remove filter ${chip.text}`}\n onClick={(event) => {\n event.stopPropagation();\n removeChip(chip.filter);\n // The button unmounts with its chip; keep focus in the field.\n inputRef.current?.focus();\n }}\n className={`flex items-center justify-center shrink-0 bg-transparent border-none p-0 text-current opacity-70 hover:opacity-100 cursor-pointer ${FOCUS_RING}`}\n >\n <DismissIcon size={10} />\n </button>\n )}\n </span>\n );\n })}\n {hiddenChipCount > 0 && (\n <span\n className={`inline-flex items-center h-[22px] px-1.5 rounded-md text-xs ${CHIP_CLASSES.resting}`}\n >\n +{hiddenChipCount}\n </span>\n )}\n <input\n ref={inputRef}\n id={id}\n type=\"text\"\n role=\"combobox\"\n value={query}\n disabled={disabled}\n placeholder={chips.length === 0 ? placeholder : ''}\n autoComplete=\"off\"\n spellCheck={false}\n aria-label={ariaLabelledBy ? undefined : (ariaLabel ?? placeholder)}\n aria-labelledby={ariaLabelledBy}\n aria-haspopup=\"listbox\"\n aria-autocomplete=\"list\"\n aria-expanded={open}\n aria-controls={open ? listboxId : undefined}\n aria-activedescendant={activeRow >= 0 ? optionId(activeRow) : undefined}\n aria-busy={loading || undefined}\n onChange={(event) => {\n setQuery(event.target.value);\n setHighlighted(0);\n openMenu();\n }}\n onKeyDown={handleKeyDown}\n className=\"flex-1 min-w-[60px] border-none outline-none bg-transparent p-0 text-[length:inherit] text-[var(--malachite-text-primary,rgb(10,10,10))] placeholder:text-[var(--malachite-text-placeholder,rgb(187,187,187))] disabled:cursor-not-allowed\"\n />\n </div>\n <span className=\"flex items-center justify-center shrink-0 size-[14px] ml-1\">\n {loading ? (\n <Spinner size=\"xxxsmall\" />\n ) : (\n <CaretDownIcon\n size={13}\n style={{\n color: open\n ? 'var(--malachite-text-primary, rgb(10, 10, 10))'\n : 'var(--malachite-text-placeholder, rgb(187, 187, 187))',\n transition: 'transform 0.2s ease, color 0.2s ease',\n transform: open ? 'rotate(180deg)' : 'rotate(0deg)',\n }}\n />\n )}\n </span>\n </div>\n {showsClear && (\n <button\n type=\"button\"\n aria-label=\"Clear filters\"\n onMouseDown={keepFocus}\n onClick={handleClear}\n className={`absolute top-0 bottom-0 right-[30px] flex items-center justify-center px-[2px] bg-transparent border-none text-[var(--malachite-text-placeholder)] hover:text-[var(--malachite-text-primary)] cursor-pointer ${FOCUS_RING}`}\n >\n <DismissIcon size={10} />\n </button>\n )}\n </div>\n {open &&\n createPortal(\n <div\n ref={popoverRef}\n {...popupIdentity}\n style={popoverStyle}\n onMouseDown={keepFocus}\n >\n <div\n style={{\n padding: '4px 10px 2px',\n color: 'var(--malachite-text-placeholder, rgb(187, 187, 187))',\n display: 'flex',\n gap: 4,\n minWidth: 0,\n }}\n >\n {mode === 'items' ? (\n <>\n <button\n type=\"button\"\n tabIndex={-1}\n onClick={backToFilters}\n className={FILTER_SEARCH_CLASS.crumb}\n style={{\n background: 'none',\n border: 'none',\n padding: 0,\n font: 'inherit',\n cursor: 'pointer',\n }}\n >\n Filters\n </button>\n <span style={ellipsis}>\n {`> ${activeFilterDef?.label ?? labels.get(activeFilter ?? '')?.label ?? ''}`}\n </span>\n </>\n ) : (\n 'Filters'\n )}\n </div>\n <div\n ref={listRef}\n id={listboxId}\n role=\"listbox\"\n aria-multiselectable={mode !== 'filters'}\n aria-label={ariaLabel ?? placeholder}\n aria-busy={loading || undefined}\n style={{\n display: 'flex',\n flexDirection: 'column',\n gap: 2,\n overflowY: 'auto',\n overscrollBehavior: 'contain',\n minHeight: 0,\n flex: 1,\n scrollbarWidth: 'thin',\n }}\n >\n {loading &&\n Array.from({ length: LOADING_ROWS }, (_, index) => (\n <div\n key={index}\n role=\"presentation\"\n className={FILTER_SEARCH_CLASS.loading}\n style={{\n height: 24,\n margin: '4px 10px',\n borderRadius: 6,\n background:\n 'var(--malachite-tag-default-background, rgba(123, 123, 123, 0.1))',\n }}\n />\n ))}\n {!loading && rows.length === 0 && (\n <div\n role=\"presentation\"\n style={{\n padding: '6px 10px',\n color: 'var(--malachite-text-secondary, rgb(123, 123, 123))',\n }}\n >\n {emptyMessage}\n </div>\n )}\n {rows.map((row, index) => {\n const key =\n row.kind === 'filter'\n ? `filter:${row.filter.value}`\n : row.kind === 'toggle'\n ? 'toggle'\n : `item:${row.row.filter.value}:${row.row.item.value}`;\n const itemDisabled = row.kind === 'item' && !!row.row.item.disabled;\n return (\n <button\n key={key}\n id={optionId(index)}\n ref={(element) => {\n optionRefs.current[index] = element;\n }}\n type=\"button\"\n tabIndex={-1}\n role=\"option\"\n aria-selected={\n row.kind === 'item' ? row.row.selected : false\n }\n aria-disabled={itemDisabled || undefined}\n data-highlighted={index === activeRow}\n data-disabled={itemDisabled}\n className={FILTER_SEARCH_CLASS.row}\n // Exception to \"no JS for styling\": the highlight IS the\n // keyboard cursor, so a bare :hover would light a second row.\n onMouseEnter={() => {\n if (!itemDisabled) setHighlighted(index);\n }}\n onClick={() => choose(row)}\n style={rowStyle}\n >\n <RowContent row={row} showFilter={mode === 'search'} />\n </button>\n );\n })}\n </div>\n </div>,\n document.body,\n )}\n </>\n );\n};\n\nexport default FilterSearch;\nexport { FilterSearch };\nexport type {\n FilterSearchChange,\n FilterSearchFilter,\n FilterSearchItem,\n FilterSearchSelection,\n} from './filterSearchModel';\n"],"names":["FIELD_SIZE_CLASSES","CONTROL_TEXT_SIZE_CLASS","CHIP_GAP","CHIP_CLASSES","LOADING_ROWS","isEnabled","row","firstEnabledFrom","rows","start","index","rowStyle","pillStyle","ellipsis","trailing","keepFocus","event","keepChipClick","RowContent","showFilter","jsxs","Fragment","jsx","FILTER_SEARCH_CLASS","item","filter","selected","CheckmarkIcon","FilterSearch","filters","value","onChange","onClear","onActiveFilterChange","placeholder","size","width","loading","disabled","clearable","maxVisibleChips","className","style","id","ariaLabel","ariaLabelledBy","instanceId","useId","listboxId","optionId","open","setOpen","useState","query","setQuery","activeFilter","setActiveFilterState","highlighted","setHighlighted","position","setPosition","fieldRef","useRef","inputRef","popoverRef","listRef","optionRefs","useTopLayer","popupIdentity","usePopupLayer","previousLabels","labels","useMemo","next","known","_a","useEffect","previousOrder","order","orderSelections","mode","resolveMode","activeFilterDef","activeFilterRef","onActiveFilterChangeRef","useLatestRef","setActiveFilter","useCallback","toggle","items","filterRows","searchRows","chips","applied","selection","chipFor","entry","itemLabels","chipText","pendingIsNew","visibleChips","hiddenChipCount","emit","changes","close","openMenu","updatePosition","field","measureMenuGeometry","handleClickOutside","path","popover","frame","handleScrollOrResize","popEscapeLayer","pushEscapeLayer","useLayoutEffect","observer","useInsertionEffect","ensureFilterSearchStyles","scrollToRow","list","top","bottom","choose","scope","selectAll","deselectAll","update","toggleValue","candidate","nextIndex","moveHighlight","direction","activeRow","step","removeChip","removeFilter","editChip","handleKeyDown","s","shown","chip","last","handleClear","clearAll","backToFilters","showsClear","triggerVisual","getTriggerVisual","emptyMessage","popoverStyle","MENU_SPECS","MENU_MAX_HEIGHT_PX","CONTROL_TEXT_SIZE","controlWidthStyle","DROPDOWN_TRIGGER_ATTR","_b","SearchIcon","active","FOCUS_RING","DismissIcon","Spinner","CaretDownIcon","createPortal","_","key","itemDisabled","element"],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ const h = (e, t) => e !== null ? "items" : t.trim().length > 0 ? "search" : "filters", a = (e, t) => {
2
+ const s = t.trim().toLowerCase();
3
+ return e.label.toLowerCase().includes(s) || e.value.toLowerCase().includes(s);
4
+ }, c = (e, t) => {
5
+ var s;
6
+ return new Set(((s = e.find((l) => l.filter === t)) == null ? void 0 : s.items) ?? []);
7
+ }, d = (e, t) => e.flatMap((s) => {
8
+ const l = c(t, s.value);
9
+ return s.items.map((o) => ({ item: o, filter: s, selected: l.has(o.value) }));
10
+ }), f = (e, t) => {
11
+ const s = e.filter((r) => a(r.item, t));
12
+ if (!s.some((r) => !r.item.disabled)) return null;
13
+ const l = e.every((r) => r.item.disabled || r.selected), o = t.trim().length > 0 && s.filter((r) => !r.item.disabled).every((r) => r.selected);
14
+ return l || o ? "deselect" : "select";
15
+ }, m = (e, t) => ({
16
+ toggle: f(e, t),
17
+ items: e.filter((s) => a(s.item, t))
18
+ }), g = (e, t, s) => e ? m(d([e], t), s) : { toggle: null, items: [] }, v = (e, t, s) => m(d(e, t), s), p = (e, t) => {
19
+ const s = new Set(t);
20
+ return e.length === s.size && e.every((l) => s.has(l));
21
+ }, i = (e, t) => {
22
+ const s = t.filter(
23
+ (n) => !p(n.items, [...c(e, n.filter)])
24
+ ), l = new Map(s.map((n) => [n.filter, n.items])), o = e.map((n) => ({
25
+ filter: n.filter,
26
+ items: l.get(n.filter) ?? n.items
27
+ })).filter((n) => n.items.length > 0), r = s.filter(
28
+ (n) => n.items.length > 0 && !e.some((u) => u.filter === n.filter)
29
+ );
30
+ return { next: [...o, ...r], changes: s };
31
+ }, w = (e, t, s) => {
32
+ const l = [...c(e, t)], o = l.includes(s) ? l.filter((r) => r !== s) : [...l, s];
33
+ return i(e, [{ filter: t, items: o }]);
34
+ }, S = (e, t, s) => i(
35
+ t,
36
+ e.map((l) => {
37
+ const o = c(t, l.value), r = l.items.filter(
38
+ (n) => !o.has(n.value) && !n.disabled && a(n, s)
39
+ ).map((n) => n.value);
40
+ return { filter: l.value, items: [...o, ...r] };
41
+ })
42
+ ), y = (e, t, s) => i(
43
+ t,
44
+ e.map((l) => {
45
+ const o = c(t, l.value), r = new Set(
46
+ l.items.filter((n) => !n.disabled && a(n, s)).map((n) => n.value)
47
+ );
48
+ return {
49
+ filter: l.value,
50
+ items: [...o].filter((n) => !r.has(n))
51
+ };
52
+ })
53
+ ), C = (e, t) => i(e, [{ filter: t, items: [] }]), F = (e) => i(
54
+ e,
55
+ e.map((t) => ({ filter: t.filter, items: [] }))
56
+ ), M = (e, t) => t.length === 0 ? e : t.length === 1 ? `${e}: ${t[0]}` : `${e}: (${t.join(", ")})`, $ = (e, t, s = null) => {
57
+ const l = new Set(e.map((r) => r.filter)), o = t.filter((r) => l.has(r) || r === s);
58
+ return [
59
+ ...o,
60
+ ...e.map((r) => r.filter).filter((r) => !o.includes(r))
61
+ ];
62
+ };
63
+ export {
64
+ M as chipText,
65
+ F as clearAll,
66
+ y as deselectAll,
67
+ g as filterRows,
68
+ a as matchesQuery,
69
+ $ as orderSelections,
70
+ C as removeFilter,
71
+ h as resolveMode,
72
+ v as searchRows,
73
+ S as selectAll,
74
+ w as toggleValue
75
+ };
76
+ //# sourceMappingURL=filterSearchModel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filterSearchModel.js","sources":["../../src/components/filterSearchModel.ts"],"sourcesContent":["export interface FilterSearchItem {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface FilterSearchFilter {\n value: string;\n label: string;\n /** Shown beside the filter's name in the menu, and beside its values in a cross-filter search. */\n info?: string;\n /** Right-aligned hint on the filter's row, e.g. `e.g. Attio, Ashby`. */\n example?: string;\n items: FilterSearchItem[];\n}\n\nexport interface FilterSearchSelection {\n filter: string;\n items: string[];\n}\n\n/** One filter's new values. An empty `items` means the filter was removed. */\nexport interface FilterSearchChange {\n filter: string;\n items: string[];\n}\n\nexport interface FilterSearchUpdate {\n next: FilterSearchSelection[];\n changes: FilterSearchChange[];\n}\n\nexport type FilterSearchMode = 'filters' | 'items' | 'search';\n\nexport type FilterSearchToggle = 'select' | 'deselect';\n\nexport interface FilterSearchItemRow {\n item: FilterSearchItem;\n filter: FilterSearchFilter;\n selected: boolean;\n}\n\nexport interface FilterSearchRows {\n /** The Select All / Deselect All row, absent when no enabled value matches. */\n toggle: FilterSearchToggle | null;\n items: FilterSearchItemRow[];\n}\n\nexport const resolveMode = (activeFilter: string | null, query: string): FilterSearchMode => {\n if (activeFilter !== null) return 'items';\n return query.trim().length > 0 ? 'search' : 'filters';\n};\n\nexport const matchesQuery = (item: FilterSearchItem, query: string): boolean => {\n const needle = query.trim().toLowerCase();\n return item.label.toLowerCase().includes(needle) || item.value.toLowerCase().includes(needle);\n};\n\nconst selectedValues = (selections: FilterSearchSelection[], filter: string): Set<string> =>\n new Set(selections.find((selection) => selection.filter === filter)?.items ?? []);\n\nconst toRows = (\n filters: FilterSearchFilter[],\n selections: FilterSearchSelection[],\n): FilterSearchItemRow[] =>\n filters.flatMap((filter) => {\n const selected = selectedValues(selections, filter.value);\n return filter.items.map((item) => ({ item, filter, selected: selected.has(item.value) }));\n });\n\n/**\n * \"Deselect All\" replaces \"Select All\" once every enabled value is chosen, or once every value\n * the query matches is.\n */\nconst toggleFor = (all: FilterSearchItemRow[], query: string): FilterSearchToggle | null => {\n const matched = all.filter((row) => matchesQuery(row.item, query));\n if (!matched.some((row) => !row.item.disabled)) return null;\n\n const everyEnabledSelected = all.every((row) => row.item.disabled || row.selected);\n const everyMatchSelected =\n query.trim().length > 0 &&\n matched.filter((row) => !row.item.disabled).every((row) => row.selected);\n\n return everyEnabledSelected || everyMatchSelected ? 'deselect' : 'select';\n};\n\nconst rowsFor = (all: FilterSearchItemRow[], query: string): FilterSearchRows => ({\n toggle: toggleFor(all, query),\n items: all.filter((row) => matchesQuery(row.item, query)),\n});\n\n/** The values of one filter, as the menu lists them once that filter is picked. */\nexport const filterRows = (\n filter: FilterSearchFilter | undefined,\n selections: FilterSearchSelection[],\n query: string,\n): FilterSearchRows =>\n filter ? rowsFor(toRows([filter], selections), query) : { toggle: null, items: [] };\n\n/** The values of every filter that match a query typed before any filter was picked. */\nexport const searchRows = (\n filters: FilterSearchFilter[],\n selections: FilterSearchSelection[],\n query: string,\n): FilterSearchRows => rowsFor(toRows(filters, selections), query);\n\n// As sets: a Select All that adds nothing returns the values in item order, not the order chosen.\nconst sameItems = (a: string[], b: string[]) => {\n const set = new Set(b);\n return a.length === set.size && a.every((value) => set.has(value));\n};\n\n/** Applies per-filter changes, keeping each filter where it already was and dropping the emptied. */\nconst applyChanges = (\n selections: FilterSearchSelection[],\n proposed: FilterSearchChange[],\n): FilterSearchUpdate => {\n const changes = proposed.filter(\n (change) => !sameItems(change.items, [...selectedValues(selections, change.filter)]),\n );\n const byFilter = new Map(changes.map((change) => [change.filter, change.items]));\n const kept = selections\n .map((selection) => ({\n filter: selection.filter,\n items: byFilter.get(selection.filter) ?? selection.items,\n }))\n .filter((selection) => selection.items.length > 0);\n const added = changes.filter(\n (change) =>\n change.items.length > 0 &&\n !selections.some((selection) => selection.filter === change.filter),\n );\n\n return { next: [...kept, ...added], changes };\n};\n\nexport const toggleValue = (\n selections: FilterSearchSelection[],\n filter: string,\n value: string,\n): FilterSearchUpdate => {\n const current = [...selectedValues(selections, filter)];\n const items = current.includes(value)\n ? current.filter((item) => item !== value)\n : [...current, value];\n\n return applyChanges(selections, [{ filter, items }]);\n};\n\n/** Select All keeps what is already chosen and adds every enabled value the query matches. */\nexport const selectAll = (\n filters: FilterSearchFilter[],\n selections: FilterSearchSelection[],\n query: string,\n): FilterSearchUpdate =>\n applyChanges(\n selections,\n filters.map((filter) => {\n const selected = selectedValues(selections, filter.value);\n const added = filter.items\n .filter(\n (item) =>\n !selected.has(item.value) && !item.disabled && matchesQuery(item, query),\n )\n .map((item) => item.value);\n // Chosen values stay even when `items` no longer lists them (a stale or lazy list).\n return { filter: filter.value, items: [...selected, ...added] };\n }),\n );\n\n/** Deselect All drops every enabled value the query matches and keeps the rest. */\nexport const deselectAll = (\n filters: FilterSearchFilter[],\n selections: FilterSearchSelection[],\n query: string,\n): FilterSearchUpdate =>\n applyChanges(\n selections,\n filters.map((filter) => {\n const selected = selectedValues(selections, filter.value);\n const dropped = new Set(\n filter.items\n .filter((item) => !item.disabled && matchesQuery(item, query))\n .map((item) => item.value),\n );\n return {\n filter: filter.value,\n items: [...selected].filter((value) => !dropped.has(value)),\n };\n }),\n );\n\nexport const removeFilter = (\n selections: FilterSearchSelection[],\n filter: string,\n): FilterSearchUpdate => applyChanges(selections, [{ filter, items: [] }]);\n\nexport const clearAll = (selections: FilterSearchSelection[]): FilterSearchUpdate =>\n applyChanges(\n selections,\n selections.map((selection) => ({ filter: selection.filter, items: [] })),\n );\n\n/** `Connector: Notion`, or `Connector: (Notion, Attio)` once there is more than one value. */\nexport const chipText = (filterLabel: string, itemLabels: string[]): string => {\n if (itemLabels.length === 0) return filterLabel;\n if (itemLabels.length === 1) return `${filterLabel}: ${itemLabels[0]}`;\n return `${filterLabel}: (${itemLabels.join(', ')})`;\n};\n\n/**\n * The order chips show filters in: when each was first added. A consumer reading its selection back\n * from the URL gets it in a fixed key order, which would reshuffle the chips on every change.\n */\nexport const orderSelections = (\n selections: FilterSearchSelection[],\n order: string[],\n /** A filter to hold its place in `order` while it has no values, e.g. the one being edited. */\n keep: string | null = null,\n): string[] => {\n const present = new Set(selections.map((selection) => selection.filter));\n const kept = order.filter((filter) => present.has(filter) || filter === keep);\n return [\n ...kept,\n ...selections.map((selection) => selection.filter).filter((f) => !kept.includes(f)),\n ];\n};\n"],"names":["resolveMode","activeFilter","query","matchesQuery","item","needle","selectedValues","selections","filter","_a","selection","toRows","filters","selected","toggleFor","all","matched","row","everyEnabledSelected","everyMatchSelected","rowsFor","filterRows","searchRows","sameItems","a","b","set","value","applyChanges","proposed","changes","change","byFilter","kept","added","toggleValue","current","items","selectAll","deselectAll","dropped","removeFilter","clearAll","chipText","filterLabel","itemLabels","orderSelections","order","keep","present","f"],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import { injectStyleSheet as n } from "./injectStyleSheet.js";
2
+ import { ensureOverlayStyles as s } from "./portalOverlay.js";
3
+ const e = ".malachite-filtersearch-row", t = ".malachite-filtersearch-pill", c = ".malachite-filtersearch-hint", o = ".malachite-filtersearch-loading", a = ".malachite-filtersearch-crumb", r = "var(--malachite-accent-foreground, rgb(2, 122, 66))", h = "var(--malachite-accent-background, rgb(0, 175, 102))", d = "var(--malachite-accent-background-glass, rgba(0, 175, 102, 0.08))", l = "var(--malachite-text-secondary, rgb(123, 123, 123))", i = "var(--malachite-text-placeholder, rgb(187, 187, 187))", m = "var(--malachite-tag-default-background, rgba(123, 123, 123, 0.1))", u = "var(--malachite-card-background, rgb(255, 255, 255))", g = [
4
+ `${e} { background: transparent; color: ${l}; }`,
5
+ `${e}[data-highlighted='true'] { background: ${d}; color: ${r}; }`,
6
+ `${e}[data-disabled='true'] { color: ${i}; cursor: not-allowed; }`,
7
+ `${t} { background: ${m}; color: ${l}; }`,
8
+ `${e}[data-highlighted='true'] ${t} { background: ${h}; color: ${u}; }`,
9
+ `${c} { color: ${i}; }`,
10
+ `${e}[data-highlighted='true'] ${c} { color: ${r}; }`,
11
+ `${a} { color: inherit; }`,
12
+ `${a}:hover { color: ${r}; }`,
13
+ `${a}:focus-visible { outline: 2px solid var(--malachite-text-primary, rgb(10, 10, 10)); outline-offset: 2px; }`,
14
+ "@keyframes malachite-filtersearch-pulse { 50% { opacity: 0.5; } }",
15
+ `${o} { animation: malachite-filtersearch-pulse 1.6s ease-in-out infinite; }`,
16
+ `@media (prefers-reduced-motion: reduce) { ${o} { animation: none; } }`
17
+ ], f = {
18
+ row: e.slice(1),
19
+ pill: t.slice(1),
20
+ hint: c.slice(1),
21
+ loading: o.slice(1),
22
+ crumb: a.slice(1)
23
+ }, p = () => (s(), n("malachite-filtersearch-styles", g));
24
+ export {
25
+ f as FILTER_SEARCH_CLASS,
26
+ p as ensureFilterSearchStyles
27
+ };
28
+ //# sourceMappingURL=filterSearchStyles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filterSearchStyles.js","sources":["../../src/components/filterSearchStyles.ts"],"sourcesContent":["// The menu portals into document.body, outside the shadow root the Tailwind sheet is adopted into,\n// so its rows are styled here. Never inline a row's background: it would outrank these rules.\n\nimport { injectStyleSheet } from './injectStyleSheet';\nimport { ensureOverlayStyles } from './portalOverlay';\n\nconst ROW = '.malachite-filtersearch-row';\nconst PILL = '.malachite-filtersearch-pill';\nconst HINT = '.malachite-filtersearch-hint';\nconst LOADING = '.malachite-filtersearch-loading';\nconst CRUMB = '.malachite-filtersearch-crumb';\n\nconst ACCENT = 'var(--malachite-accent-foreground, rgb(2, 122, 66))';\nconst ACCENT_FILL = 'var(--malachite-accent-background, rgb(0, 175, 102))';\nconst ACCENT_GLASS = 'var(--malachite-accent-background-glass, rgba(0, 175, 102, 0.08))';\nconst SECONDARY = 'var(--malachite-text-secondary, rgb(123, 123, 123))';\nconst PLACEHOLDER = 'var(--malachite-text-placeholder, rgb(187, 187, 187))';\nconst PILL_BACKGROUND = 'var(--malachite-tag-default-background, rgba(123, 123, 123, 0.1))';\n// No palette entry exists for text on a solid accent fill; the card surface is its contrast.\nconst ON_ACCENT = 'var(--malachite-card-background, rgb(255, 255, 255))';\n\n// Highlight is the keyboard cursor, so it paints the accent tint the old antd menu did.\nconst RULES = [\n `${ROW} { background: transparent; color: ${SECONDARY}; }`,\n `${ROW}[data-highlighted='true'] { background: ${ACCENT_GLASS}; color: ${ACCENT}; }`,\n `${ROW}[data-disabled='true'] { color: ${PLACEHOLDER}; cursor: not-allowed; }`,\n `${PILL} { background: ${PILL_BACKGROUND}; color: ${SECONDARY}; }`,\n `${ROW}[data-highlighted='true'] ${PILL} { background: ${ACCENT_FILL}; color: ${ON_ACCENT}; }`,\n `${HINT} { color: ${PLACEHOLDER}; }`,\n `${ROW}[data-highlighted='true'] ${HINT} { color: ${ACCENT}; }`,\n `${CRUMB} { color: inherit; }`,\n `${CRUMB}:hover { color: ${ACCENT}; }`,\n `${CRUMB}:focus-visible { outline: 2px solid var(--malachite-text-primary, rgb(10, 10, 10)); outline-offset: 2px; }`,\n `@keyframes malachite-filtersearch-pulse { 50% { opacity: 0.5; } }`,\n `${LOADING} { animation: malachite-filtersearch-pulse 1.6s ease-in-out infinite; }`,\n `@media (prefers-reduced-motion: reduce) { ${LOADING} { animation: none; } }`,\n];\n\nexport const FILTER_SEARCH_CLASS = {\n row: ROW.slice(1),\n pill: PILL.slice(1),\n hint: HINT.slice(1),\n loading: LOADING.slice(1),\n crumb: CRUMB.slice(1),\n} as const;\n\nexport const ensureFilterSearchStyles = (): CSSStyleSheet | null => {\n ensureOverlayStyles();\n return injectStyleSheet('malachite-filtersearch-styles', RULES);\n};\n"],"names":["ROW","PILL","HINT","LOADING","CRUMB","ACCENT","ACCENT_FILL","ACCENT_GLASS","SECONDARY","PLACEHOLDER","PILL_BACKGROUND","ON_ACCENT","RULES","FILTER_SEARCH_CLASS","ensureFilterSearchStyles","ensureOverlayStyles","injectStyleSheet"],"mappings":";;AAMA,MAAMA,IAAM,+BACNC,IAAO,gCACPC,IAAO,gCACPC,IAAU,mCACVC,IAAQ,iCAERC,IAAS,uDACTC,IAAc,wDACdC,IAAe,qEACfC,IAAY,uDACZC,IAAc,yDACdC,IAAkB,qEAElBC,IAAY,wDAGZC,IAAQ;AAAA,EACV,GAAGZ,CAAG,sCAAsCQ,CAAS;AAAA,EACrD,GAAGR,CAAG,2CAA2CO,CAAY,YAAYF,CAAM;AAAA,EAC/E,GAAGL,CAAG,mCAAmCS,CAAW;AAAA,EACpD,GAAGR,CAAI,kBAAkBS,CAAe,YAAYF,CAAS;AAAA,EAC7D,GAAGR,CAAG,6BAA6BC,CAAI,kBAAkBK,CAAW,YAAYK,CAAS;AAAA,EACzF,GAAGT,CAAI,aAAaO,CAAW;AAAA,EAC/B,GAAGT,CAAG,6BAA6BE,CAAI,aAAaG,CAAM;AAAA,EAC1D,GAAGD,CAAK;AAAA,EACR,GAAGA,CAAK,mBAAmBC,CAAM;AAAA,EACjC,GAAGD,CAAK;AAAA,EACR;AAAA,EACA,GAAGD,CAAO;AAAA,EACV,6CAA6CA,CAAO;AACxD,GAEaU,IAAsB;AAAA,EAC/B,KAAKb,EAAI,MAAM,CAAC;AAAA,EAChB,MAAMC,EAAK,MAAM,CAAC;AAAA,EAClB,MAAMC,EAAK,MAAM,CAAC;AAAA,EAClB,SAASC,EAAQ,MAAM,CAAC;AAAA,EACxB,OAAOC,EAAM,MAAM,CAAC;AACxB,GAEaU,IAA2B,OACpCC,EAAA,GACOC,EAAiB,iCAAiCJ,CAAK;"}
package/dist/index.d.ts CHANGED
@@ -1167,6 +1167,15 @@ export declare interface ConnectionHeaderProps {
1167
1167
 
1168
1168
  export declare const ConnectorIcon: React.FC<IconProps_29>;
1169
1169
 
1170
+ /**
1171
+ * The text half of the 28/32/42 control ladder. Keys are control sizes, which deliberately do
1172
+ * NOT match the type scale's names: control `medium` is 13px (`font-size-small`, not `-medium`
1173
+ * at 14px) and control `large` is the off-ladder 15px rung, not `font-size-large` at 16px.
1174
+ */
1175
+ declare type ControlSize = 'small' | 'medium' | 'large';
1176
+
1177
+ declare type ControlWidth = string | number;
1178
+
1170
1179
  export declare const CopyIcon: React.FC<IconProps_30>;
1171
1180
 
1172
1181
  export declare const CsvIcon: React.FC<IconProps_31>;
@@ -1589,6 +1598,62 @@ export declare const FilledCaretRightIcon: React.FC<IconProps_52>;
1589
1598
 
1590
1599
  export declare const FilterIcon: React.FC<IconProps_53>;
1591
1600
 
1601
+ export declare const FilterSearch: default_2.FC<FilterSearchProps>;
1602
+
1603
+ /** One filter's new values. An empty `items` means the filter was removed. */
1604
+ export declare interface FilterSearchChange {
1605
+ filter: string;
1606
+ items: string[];
1607
+ }
1608
+
1609
+ export declare interface FilterSearchFilter {
1610
+ value: string;
1611
+ label: string;
1612
+ /** Shown beside the filter's name in the menu, and beside its values in a cross-filter search. */
1613
+ info?: string;
1614
+ /** Right-aligned hint on the filter's row, e.g. `e.g. Attio, Ashby`. */
1615
+ example?: string;
1616
+ items: FilterSearchItem[];
1617
+ }
1618
+
1619
+ export declare interface FilterSearchItem {
1620
+ value: string;
1621
+ label: string;
1622
+ disabled?: boolean;
1623
+ }
1624
+
1625
+ export declare interface FilterSearchProps {
1626
+ filters: FilterSearchFilter[];
1627
+ /** Controlled: the bar always shows `value`, so a URL-backed selection needs no syncing. */
1628
+ value: FilterSearchSelection[];
1629
+ /** `changes` lists only the filters that moved; an empty `items` means that filter was removed. */
1630
+ onChange: (next: FilterSearchSelection[], changes: FilterSearchChange[]) => void;
1631
+ /** Called when the clear control empties the bar, after `onChange`. */
1632
+ onClear?: () => void;
1633
+ /** The filter whose values are open, or `null` on the filter list — for loading values lazily. */
1634
+ onActiveFilterChange?: (filter: string | null) => void;
1635
+ placeholder?: string;
1636
+ size?: FilterSearchSize;
1637
+ width?: ControlWidth;
1638
+ loading?: boolean;
1639
+ disabled?: boolean;
1640
+ clearable?: boolean;
1641
+ /** Chips past this count collapse into `+N`. */
1642
+ maxVisibleChips?: number;
1643
+ className?: string;
1644
+ style?: default_2.CSSProperties;
1645
+ id?: string;
1646
+ 'aria-label'?: string;
1647
+ 'aria-labelledby'?: string;
1648
+ }
1649
+
1650
+ export declare interface FilterSearchSelection {
1651
+ filter: string;
1652
+ items: string[];
1653
+ }
1654
+
1655
+ export declare type FilterSearchSize = ControlSize;
1656
+
1592
1657
  export declare const Flex: default_2.FC<FlexProps>;
1593
1658
 
1594
1659
  export declare enum FlexAlign {