@upbound/monarch-blocks 0.6.0 → 0.7.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.
- package/dist/cel-filter-bar.js +1 -1
- package/dist/cel-filter-bar.js.map +1 -1
- package/dist/code-block.d.ts +20 -5
- package/dist/code-block.js +70 -12
- package/dist/code-block.js.map +1 -1
- package/dist/data-table.js +5 -5
- package/dist/data-table.js.map +1 -1
- package/dist/filter-bar.js +1 -1
- package/dist/filter-bar.js.map +1 -1
- package/dist/graph.js +44 -37
- package/dist/graph.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +123 -58
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/dist/filter-bar.js
CHANGED
|
@@ -194,7 +194,7 @@ function FilterBar(_a) {
|
|
|
194
194
|
if (filter) handleAddFilter(filter.key);
|
|
195
195
|
},
|
|
196
196
|
children: [
|
|
197
|
-
/* @__PURE__ */ jsxs(ComboboxTrigger, { "aria-label": "Add filter", render: /* @__PURE__ */ jsx(Button, { variant: "outline" }), children: [
|
|
197
|
+
/* @__PURE__ */ jsxs(ComboboxTrigger, { "aria-label": "Add filter", showIndicator: false, render: /* @__PURE__ */ jsx(Button, { variant: "outline" }), children: [
|
|
198
198
|
/* @__PURE__ */ jsx(Icon, { name: "plus", "data-icon": "inline-start" }),
|
|
199
199
|
"Add filter"
|
|
200
200
|
] }),
|
package/dist/filter-bar.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/filter-bar/filter-bar.tsx","../src/lib/utils.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { type DateRange } from 'react-day-picker';\nimport { Icon } from '@upbound/monarch-core';\n\nimport { cn, sortBy } from '@/lib/utils';\nimport { Badge } from '@upbound/monarch-core';\nimport { Button } from '@upbound/monarch-core';\nimport { ButtonGroup, ButtonGroupText } from '@upbound/monarch-core';\nimport { Calendar } from '@upbound/monarch-core';\nimport {\n Combobox,\n ComboboxChip,\n ComboboxClearAll,\n ComboboxContent,\n ComboboxEmpty,\n ComboboxInput,\n ComboboxItem,\n ComboboxList,\n ComboboxSelectedChips,\n ComboboxSeparator,\n ComboboxTrigger,\n ComboboxValue,\n} from '@upbound/monarch-core';\nimport {\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuTrigger,\n} from '@upbound/monarch-core';\nimport { Input } from '@upbound/monarch-core';\nimport { Label } from '@upbound/monarch-core';\nimport { Popover, PopoverContent, PopoverTrigger } from '@upbound/monarch-core';\n\n// ── Types ─────────────────────────────────────────────\n\ntype FilterType = 'checkbox' | 'radio' | 'text' | 'multiText' | 'date';\n\ninterface FilterOption {\n value: string;\n label: string;\n icon?: React.ReactNode;\n}\n\ninterface FilterDefinitionBase {\n key: string;\n label: string;\n}\n\ninterface CheckboxFilterDefinition extends FilterDefinitionBase {\n type?: 'checkbox';\n options: FilterOption[];\n /**\n * When `true`, the chip's value popup becomes a searchable Combobox\n * (multi-select with a search input). Use for long option lists where a\n * basic dropdown becomes hard to scan. When `false` or omitted, the chip\n * uses the standard checkbox dropdown.\n */\n searchable?: boolean;\n}\n\ninterface RadioFilterDefinition extends FilterDefinitionBase {\n type: 'radio';\n options: FilterOption[];\n /** Same searchable-Combobox swap as `CheckboxFilterDefinition`, constrained to a single selection. */\n searchable?: boolean;\n}\n\ninterface TextFilterDefinition extends FilterDefinitionBase {\n type: 'text';\n placeholder?: string;\n}\n\ninterface MultiTextFilterDefinition extends FilterDefinitionBase {\n type: 'multiText';\n placeholder?: string;\n}\n\ninterface DateFilterDefinition extends FilterDefinitionBase {\n type: 'date';\n /**\n * `true` always shows Start/End time inputs, `false` never shows them,\n * and omitted auto-detects from whether the current value already carries\n * a non-midnight time component.\n */\n includeTime?: boolean;\n}\n\ntype FilterDefinition =\n | CheckboxFilterDefinition\n | RadioFilterDefinition\n | TextFilterDefinition\n | MultiTextFilterDefinition\n | DateFilterDefinition;\n\ntype CheckboxFilterValue = string[];\ntype RadioFilterValue = string;\ntype TextFilterValue = string;\ntype MultiTextFilterValue = string[];\ntype DateFilterValue = { startDate: Date | null; endDate: Date | null };\ntype FilterValue = CheckboxFilterValue | RadioFilterValue | TextFilterValue | MultiTextFilterValue | DateFilterValue;\n\ninterface ActiveFilter {\n key: string;\n value?: FilterValue;\n}\n\n// ── Value helpers ─────────────────────────────────────\n\nfunction asCheckboxValue(value: FilterValue | undefined): CheckboxFilterValue {\n return Array.isArray(value) ? value : [];\n}\n\nfunction asRadioValue(value: FilterValue | undefined): RadioFilterValue {\n return typeof value === 'string' ? value : '';\n}\n\nfunction asTextValue(value: FilterValue | undefined): TextFilterValue {\n return typeof value === 'string' ? value : '';\n}\n\nfunction asMultiTextValue(value: FilterValue | undefined): MultiTextFilterValue {\n return Array.isArray(value) ? value : [];\n}\n\nfunction asDateValue(value: FilterValue | undefined): DateFilterValue {\n if (value && typeof value === 'object' && !Array.isArray(value)) return value as DateFilterValue;\n return { startDate: null, endDate: null };\n}\n\nfunction hasExplicitTime(date: Date | null): boolean {\n if (!date) return false;\n return date.getHours() !== 0 || date.getMinutes() !== 0;\n}\n\nfunction formatTimeForInput(date: Date | null): string {\n if (!date) return '';\n return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;\n}\n\nfunction applyTimeToDate(date: Date, time: string): Date {\n const [hours, minutes] = time.split(':').map(Number);\n const next = new Date(date);\n next.setHours(hours || 0, minutes || 0, 0, 0);\n return next;\n}\n\nfunction formatDateLabel(date: Date): string {\n return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\n}\n\n/** Merges the internal ref FilterBar uses to track its own root node with\n * any ref a consumer passes to `FilterBar` itself. */\nfunction composeRefs<T>(...refs: Array<React.Ref<T> | null | undefined>) {\n return (node: T | null) => {\n for (const ref of refs) {\n if (typeof ref === 'function') ref(node);\n else if (ref) (ref as React.RefObject<T | null>).current = node;\n }\n };\n}\n\ntype PortalContainer = React.ComponentProps<typeof ComboboxContent>['container'];\n\n// ── FilterBar ─────────────────────────────────────────\n\ninterface FilterBarProps extends React.ComponentProps<'div'> {\n filters: FilterDefinition[];\n activeFilters: ActiveFilter[];\n onAddFilter: (key: string) => void;\n onRemoveFilter: (key: string) => void;\n onChangeFilter: (key: string, value: FilterValue) => void;\n}\n\nfunction hasOrder(filter: FilterDefinition): boolean {\n return typeof (filter as { order?: unknown }).order === 'number';\n}\n\nfunction FilterBar({\n filters,\n activeFilters,\n onAddFilter,\n onRemoveFilter,\n onChangeFilter,\n className,\n children,\n ref,\n ...props\n}: FilterBarProps) {\n const usedKeys = new Set(activeFilters.map(f => f.key));\n const remainingFilters = filters.filter(f => !usedKeys.has(f.key));\n const availableFilters = filters.some(hasOrder) ? remainingFilters : sortBy(remainingFilters, f => f.label);\n const shouldShowFilters = availableFilters.length > 0 || activeFilters.length > 0;\n\n // Tracks the most recently added filter so its chip can open its value\n // popup immediately, without waiting for a second click.\n const [pendingOpenKey, setPendingOpenKey] = React.useState<string | null>(null);\n\n function handleAddFilter(key: string) {\n onAddFilter(key);\n setPendingOpenKey(key);\n }\n\n // Ref for the FilterBar's own root node, used to anchor Combobox popups.\n // Helps rendering correctly the popup when rendered inside a modal Dialog/Sheet.\n const containerRef = React.useRef<HTMLDivElement>(null);\n\n return (\n <div\n data-slot=\"filter-bar\"\n data-visible-filters={shouldShowFilters}\n className={cn('flex flex-wrap items-center gap-2', className)}\n ref={composeRefs(containerRef, ref)}\n {...props}\n >\n {/* Add filter button */}\n {availableFilters.length > 0 && (\n <Combobox\n items={availableFilters}\n itemToStringValue={(item: FilterDefinition) => item.label}\n itemToStringLabel={(item: FilterDefinition) => item.label}\n onValueChange={(filter: FilterDefinition | null) => {\n if (filter) handleAddFilter(filter.key);\n }}\n >\n {/* role=\"combobox\" doesn't pick up child text as its accessible name the\n way a plain button would — an explicit aria-label is required, not\n just a nicety. */}\n <ComboboxTrigger aria-label=\"Add filter\" render={<Button variant=\"outline\" />}>\n <Icon name=\"plus\" data-icon=\"inline-start\" />\n Add filter\n </ComboboxTrigger>\n <ComboboxContent container={containerRef} align=\"start\" className=\"w-48\">\n <ComboboxInput showTrigger={false} placeholder=\"Search filters…\" />\n <ComboboxEmpty>No filters found.</ComboboxEmpty>\n <ComboboxList>\n {(filter: FilterDefinition) => (\n <ComboboxItem key={filter.key} value={filter}>\n {filter.label}\n </ComboboxItem>\n )}\n </ComboboxList>\n </ComboboxContent>\n </Combobox>\n )}\n\n {/* Active filter chips */}\n {activeFilters.map(active => {\n const definition = filters.find(f => f.key === active.key);\n if (!definition) return null;\n return (\n <FilterChip\n key={active.key}\n definition={definition}\n value={active.value}\n autoOpen={active.key === pendingOpenKey}\n container={containerRef}\n onChangeValue={value => onChangeFilter(active.key, value)}\n onRemove={() => onRemoveFilter(active.key)}\n />\n );\n })}\n\n {/* Children (count, actions) — sit immediately after the last chip\n with the same `gap-2` spacing. With the parent's `flex-wrap`, a\n long row of chips will push the action onto the next line instead\n of stretching across an empty horizontal void. */}\n {children}\n </div>\n );\n}\n\n// ── FilterChip (dispatcher) ────────────────────────────\n\ninterface FilterChipProps {\n definition: FilterDefinition;\n value: FilterValue | undefined;\n /** Opens the chip's value popup as soon as it mounts — used right after the filter is added. */\n autoOpen?: boolean;\n /** Portals a searchable checkbox/radio chip's Combobox popup here instead of the default `document.body` — see `FilterBar`'s own `containerRef` for why. */\n container?: PortalContainer;\n onChangeValue: (value: FilterValue) => void;\n onRemove: () => void;\n}\n\nfunction FilterChip({ definition, value, autoOpen, container, onChangeValue, onRemove }: FilterChipProps) {\n const type = definition.type ?? 'checkbox';\n\n switch (type) {\n case 'checkbox':\n return (\n <CheckboxFilterChip\n definition={definition as CheckboxFilterDefinition}\n value={value}\n autoOpen={autoOpen}\n container={container}\n onChangeValue={onChangeValue}\n onRemove={onRemove}\n />\n );\n case 'radio':\n return (\n <RadioFilterChip\n definition={definition as RadioFilterDefinition}\n value={value}\n autoOpen={autoOpen}\n container={container}\n onChangeValue={onChangeValue}\n onRemove={onRemove}\n />\n );\n case 'text':\n return (\n <TextFilterChip\n definition={definition as TextFilterDefinition}\n value={value}\n autoOpen={autoOpen}\n onChangeValue={onChangeValue}\n onRemove={onRemove}\n />\n );\n case 'multiText':\n return (\n <MultiTextFilterChip\n definition={definition as MultiTextFilterDefinition}\n value={value}\n autoOpen={autoOpen}\n onChangeValue={onChangeValue}\n onRemove={onRemove}\n />\n );\n case 'date':\n return (\n <DateFilterChip\n definition={definition as DateFilterDefinition}\n value={value}\n autoOpen={autoOpen}\n onChangeValue={onChangeValue}\n onRemove={onRemove}\n />\n );\n }\n}\n\n// ── Shared chip chrome ─────────────────────────────────\n\nfunction FilterChipRemoveButton({ label, onRemove }: { label: string; onRemove: () => void }) {\n return (\n <Button variant=\"outline\" size=\"icon\" aria-label={`Remove ${label} filter`} onClick={onRemove}>\n <Icon name=\"xmark\" />\n </Button>\n );\n}\n\n// ── Checkbox chip ──────────────────────────────────────\n\nfunction CheckboxFilterChip({\n definition,\n value,\n autoOpen,\n container,\n onChangeValue,\n onRemove,\n}: {\n definition: CheckboxFilterDefinition;\n value: FilterValue | undefined;\n autoOpen?: boolean;\n container?: PortalContainer;\n onChangeValue: (value: FilterValue) => void;\n onRemove: () => void;\n}) {\n const { label, searchable = false } = definition;\n const options = sortBy(definition.options, o => o.label);\n const values = asCheckboxValue(value);\n const displayLabel =\n values.length === 0\n ? 'Any'\n : values.length === 1\n ? (options.find(o => o.value === values[0])?.label ?? values[0])\n : `${values.length} selected`;\n\n function toggle(optionValue: string) {\n const next = values.includes(optionValue) ? values.filter(v => v !== optionValue) : [...values, optionValue];\n onChangeValue(next);\n }\n\n return (\n <ButtonGroup>\n <ButtonGroupText>{label}</ButtonGroupText>\n {searchable ? (\n <Combobox\n items={options}\n itemToStringValue={(item: FilterOption) => item.label}\n multiple\n defaultOpen={autoOpen}\n value={options.filter(o => values.includes(o.value))}\n onValueChange={(selected: FilterOption[]) => onChangeValue(selected.map(s => s.value))}\n >\n <ComboboxTrigger\n render={<Button variant=\"outline\" className=\"gap-1 font-normal [&>svg:last-child]:size-(--icon-sm)\" />}\n >\n <span className={values.length === 0 ? 'text-muted-foreground' : ''}>{displayLabel}</span>\n </ComboboxTrigger>\n <ComboboxContent\n container={container}\n className=\"min-w-64\"\n collisionAvoidance={{ side: 'none', align: 'shift' }}\n >\n <ComboboxInput showTrigger={false} placeholder={`Search ${label.toLowerCase()}…`} />\n {values.length > 0 && (\n <>\n <ComboboxSelectedChips>\n <ComboboxValue>\n {(selected: FilterOption[]) => (\n <>\n {selected.map(item => (\n <ComboboxChip key={item.value}>{item.label}</ComboboxChip>\n ))}\n </>\n )}\n </ComboboxValue>\n <ComboboxClearAll onClick={() => onChangeValue([])} />\n </ComboboxSelectedChips>\n <ComboboxSeparator />\n </>\n )}\n <ComboboxEmpty>No {label.toLowerCase()} found.</ComboboxEmpty>\n <ComboboxList>\n {(item: FilterOption) => (\n <ComboboxItem key={item.value} value={item}>\n {item.icon}\n {item.label}\n </ComboboxItem>\n )}\n </ComboboxList>\n </ComboboxContent>\n </Combobox>\n ) : (\n <DropdownMenu defaultOpen={autoOpen}>\n <DropdownMenuTrigger asChild>\n <Button variant=\"outline\" className=\"gap-1 font-normal\">\n <span className={values.length === 0 ? 'text-muted-foreground' : ''}>{displayLabel}</span>\n <Icon name=\"chevron-down\" size=\"sm\" className=\"text-muted-foreground\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" className=\"w-48\">\n {options.map(opt => (\n <DropdownMenuCheckboxItem\n key={opt.value}\n checked={values.includes(opt.value)}\n onCheckedChange={() => toggle(opt.value)}\n >\n {opt.icon}\n {opt.label}\n </DropdownMenuCheckboxItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n <FilterChipRemoveButton label={label} onRemove={onRemove} />\n </ButtonGroup>\n );\n}\n\n// ── Radio chip ─────────────────────────────────────────\n\nfunction RadioFilterChip({\n definition,\n value,\n autoOpen,\n container,\n onChangeValue,\n onRemove,\n}: {\n definition: RadioFilterDefinition;\n value: FilterValue | undefined;\n autoOpen?: boolean;\n container?: PortalContainer;\n onChangeValue: (value: FilterValue) => void;\n onRemove: () => void;\n}) {\n const { label, searchable = false } = definition;\n const options = sortBy(definition.options, o => o.label);\n const selectedValue = asRadioValue(value);\n const displayLabel = selectedValue ? (options.find(o => o.value === selectedValue)?.label ?? selectedValue) : 'Any';\n const selected = options.find(o => o.value === selectedValue) ?? null;\n\n return (\n <ButtonGroup>\n <ButtonGroupText>{label}</ButtonGroupText>\n {searchable ? (\n <Combobox\n items={options}\n itemToStringValue={(item: FilterOption) => item.label}\n defaultOpen={autoOpen}\n value={selected}\n onValueChange={(next: FilterOption | null) => onChangeValue(next?.value ?? '')}\n >\n <ComboboxTrigger\n render={<Button variant=\"outline\" className=\"gap-1 font-normal [&>svg:last-child]:size-(--icon-sm)\" />}\n >\n <span className={selectedValue ? '' : 'text-muted-foreground'}>{displayLabel}</span>\n </ComboboxTrigger>\n <ComboboxContent\n container={container}\n className=\"min-w-64\"\n collisionAvoidance={{ side: 'none', align: 'shift' }}\n >\n <ComboboxInput showTrigger={false} placeholder={`Search ${label.toLowerCase()}…`} />\n <ComboboxEmpty>No {label.toLowerCase()} found.</ComboboxEmpty>\n <ComboboxList>\n {(item: FilterOption) => (\n <ComboboxItem key={item.value} value={item}>\n {item.icon}\n {item.label}\n </ComboboxItem>\n )}\n </ComboboxList>\n </ComboboxContent>\n </Combobox>\n ) : (\n <DropdownMenu defaultOpen={autoOpen}>\n <DropdownMenuTrigger asChild>\n <Button variant=\"outline\" className=\"gap-1 font-normal\">\n <span className={selectedValue ? '' : 'text-muted-foreground'}>{displayLabel}</span>\n <Icon name=\"chevron-down\" size=\"sm\" className=\"text-muted-foreground\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" className=\"w-48\">\n <DropdownMenuRadioGroup value={selectedValue} onValueChange={onChangeValue}>\n {options.map(opt => (\n <DropdownMenuRadioItem key={opt.value} value={opt.value}>\n {opt.icon}\n {opt.label}\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n <FilterChipRemoveButton label={label} onRemove={onRemove} />\n </ButtonGroup>\n );\n}\n\n// ── Text chip ──────────────────────────────────────────\n\nfunction TextFilterChip({\n definition,\n value,\n autoOpen,\n onChangeValue,\n onRemove,\n}: {\n definition: TextFilterDefinition;\n value: FilterValue | undefined;\n autoOpen?: boolean;\n onChangeValue: (value: FilterValue) => void;\n onRemove: () => void;\n}) {\n const { label, placeholder } = definition;\n const text = asTextValue(value);\n const [open, setOpen] = React.useState(!!autoOpen);\n\n return (\n <ButtonGroup>\n <ButtonGroupText>{label}</ButtonGroupText>\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button variant=\"outline\" className=\"gap-1 font-normal\">\n <span className={text ? '' : 'text-muted-foreground'}>{text || 'Any'}</span>\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"start\">\n <Input\n autoFocus\n value={text}\n placeholder={placeholder ?? 'Enter a value…'}\n onChange={e => onChangeValue(e.target.value)}\n onKeyDown={e => {\n if (e.key === 'Enter') setOpen(false);\n }}\n />\n </PopoverContent>\n </Popover>\n <FilterChipRemoveButton label={label} onRemove={onRemove} />\n </ButtonGroup>\n );\n}\n\n// ── MultiText chip ─────────────────────────────────────\n\nfunction MultiTextFilterChip({\n definition,\n value,\n autoOpen,\n onChangeValue,\n onRemove,\n}: {\n definition: MultiTextFilterDefinition;\n value: FilterValue | undefined;\n autoOpen?: boolean;\n onChangeValue: (value: FilterValue) => void;\n onRemove: () => void;\n}) {\n const { label, placeholder } = definition;\n const tokens = asMultiTextValue(value);\n const [draft, setDraft] = React.useState('');\n const displayLabel = tokens.length === 0 ? 'Any' : tokens.length === 1 ? tokens[0] : `${tokens.length} values`;\n\n function addToken() {\n const token = draft.trim();\n if (!token || tokens.includes(token)) {\n setDraft('');\n return;\n }\n onChangeValue([...tokens, token]);\n setDraft('');\n }\n\n function removeToken(token: string) {\n onChangeValue(tokens.filter(t => t !== token));\n }\n\n return (\n <ButtonGroup>\n <ButtonGroupText>{label}</ButtonGroupText>\n <Popover defaultOpen={autoOpen}>\n <PopoverTrigger asChild>\n <Button variant=\"outline\" className=\"gap-1 font-normal\">\n <span className={tokens.length === 0 ? 'text-muted-foreground' : ''}>{displayLabel}</span>\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"start\">\n <div className=\"flex flex-col gap-2\">\n {tokens.length > 0 && (\n <div className=\"flex flex-wrap gap-1\">\n {tokens.map(token => (\n <Badge key={token} variant=\"secondary\" className=\"gap-1\">\n {token}\n <button\n type=\"button\"\n aria-label={`Remove ${token}`}\n onClick={() => removeToken(token)}\n className=\"cursor-pointer\"\n >\n <Icon name=\"xmark\" size=\"xs\" />\n </button>\n </Badge>\n ))}\n </div>\n )}\n <Input\n autoFocus\n value={draft}\n placeholder={placeholder ?? 'Type a value, press Enter…'}\n onChange={e => setDraft(e.target.value)}\n onKeyDown={e => {\n if (e.key === 'Enter') {\n e.preventDefault();\n addToken();\n } else if (e.key === 'Backspace' && draft === '' && tokens.length > 0) {\n removeToken(tokens[tokens.length - 1]);\n }\n }}\n />\n </div>\n </PopoverContent>\n </Popover>\n <FilterChipRemoveButton label={label} onRemove={onRemove} />\n </ButtonGroup>\n );\n}\n\n// ── Date chip ──────────────────────────────────────────\n\nfunction DateFilterChip({\n definition,\n value,\n autoOpen,\n onChangeValue,\n onRemove,\n}: {\n definition: DateFilterDefinition;\n value: FilterValue | undefined;\n autoOpen?: boolean;\n onChangeValue: (value: FilterValue) => void;\n onRemove: () => void;\n}) {\n const { label, includeTime } = definition;\n const { startDate, endDate } = asDateValue(value);\n const showTimeInputs = includeTime ?? (hasExplicitTime(startDate) || hasExplicitTime(endDate));\n\n const displayLabel = !startDate\n ? 'Any'\n : !endDate || startDate.getTime() === endDate.getTime()\n ? formatDateLabel(startDate)\n : `${formatDateLabel(startDate)} – ${formatDateLabel(endDate)}`;\n\n function pickRange(range: DateRange | undefined) {\n if (!range?.from) {\n onChangeValue({ startDate: null, endDate: null });\n return;\n }\n onChangeValue({ startDate: range.from, endDate: range.to ?? range.from });\n }\n\n function pickStartTime(time: string) {\n if (!startDate) return;\n onChangeValue({ startDate: applyTimeToDate(startDate, time), endDate });\n }\n\n function pickEndTime(time: string) {\n if (!endDate) return;\n onChangeValue({ startDate, endDate: applyTimeToDate(endDate, time) });\n }\n\n return (\n <ButtonGroup>\n <ButtonGroupText>{label}</ButtonGroupText>\n <Popover defaultOpen={autoOpen}>\n <PopoverTrigger asChild>\n <Button variant=\"outline\" className=\"gap-1 font-normal\">\n <span className={startDate ? '' : 'text-muted-foreground'}>{displayLabel}</span>\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-fit\">\n <div className=\"flex flex-col gap-2\">\n <Calendar\n mode=\"range\"\n selected={{ from: startDate ?? undefined, to: endDate ?? undefined }}\n onSelect={pickRange}\n />\n {showTimeInputs && (\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex w-full items-center gap-2\">\n <Label\n htmlFor={`${definition.key}-start-time`}\n className=\"text-body-sm text-muted-foreground w-10 shrink-0\"\n >\n Start\n </Label>\n <Input\n id={`${definition.key}-start-time`}\n type=\"time\"\n className=\"w-full\"\n value={formatTimeForInput(startDate)}\n onChange={e => pickStartTime(e.target.value)}\n disabled={!startDate}\n />\n </div>\n <div className=\"flex w-full items-center gap-2\">\n <Label\n htmlFor={`${definition.key}-end-time`}\n className=\"text-body-sm text-muted-foreground w-10 shrink-0\"\n >\n End\n </Label>\n <Input\n id={`${definition.key}-end-time`}\n type=\"time\"\n className=\"w-full\"\n value={formatTimeForInput(endDate)}\n onChange={e => pickEndTime(e.target.value)}\n disabled={!endDate}\n />\n </div>\n </div>\n )}\n </div>\n </PopoverContent>\n </Popover>\n <FilterChipRemoveButton label={label} onRemove={onRemove} />\n </ButtonGroup>\n );\n}\n\nexport {\n FilterBar,\n FilterChip,\n type FilterType,\n type FilterDefinition,\n type CheckboxFilterDefinition,\n type RadioFilterDefinition,\n type TextFilterDefinition,\n type MultiTextFilterDefinition,\n type DateFilterDefinition,\n type ActiveFilter,\n type FilterOption,\n type FilterValue,\n type CheckboxFilterValue,\n type RadioFilterValue,\n type TextFilterValue,\n type MultiTextFilterValue,\n type DateFilterValue,\n type FilterBarProps,\n};\n","import { clsx, type ClassValue } from 'clsx';\nimport { extendTailwindMerge } from 'tailwind-merge';\n\nconst twMerge = extendTailwindMerge({\n extend: {\n classGroups: {\n 'font-size': [\n {\n text: [\n 'display-hero',\n 'display-kpi-sm',\n 'display-kpi',\n 'display-kpi-lg',\n 'display-feature',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'body-lg',\n 'body',\n 'body-sm',\n 'caption',\n 'eyebrow',\n ],\n },\n ],\n },\n },\n});\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport function sortBy<T>(items: T[], getKey: (item: T) => string): T[] {\n return [...items].sort((a, b) => getKey(a).localeCompare(getKey(b)));\n}\n"],"mappings":"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","names":["_a"]}
|
|
1
|
+
{"version":3,"sources":["../src/filter-bar/filter-bar.tsx","../src/lib/utils.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { type DateRange } from 'react-day-picker';\nimport { Icon } from '@upbound/monarch-core';\n\nimport { cn, sortBy } from '@/lib/utils';\nimport { Badge } from '@upbound/monarch-core';\nimport { Button } from '@upbound/monarch-core';\nimport { ButtonGroup, ButtonGroupText } from '@upbound/monarch-core';\nimport { Calendar } from '@upbound/monarch-core';\nimport {\n Combobox,\n ComboboxChip,\n ComboboxClearAll,\n ComboboxContent,\n ComboboxEmpty,\n ComboboxInput,\n ComboboxItem,\n ComboboxList,\n ComboboxSelectedChips,\n ComboboxSeparator,\n ComboboxTrigger,\n ComboboxValue,\n} from '@upbound/monarch-core';\nimport {\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuTrigger,\n} from '@upbound/monarch-core';\nimport { Input } from '@upbound/monarch-core';\nimport { Label } from '@upbound/monarch-core';\nimport { Popover, PopoverContent, PopoverTrigger } from '@upbound/monarch-core';\n\n// ── Types ─────────────────────────────────────────────\n\ntype FilterType = 'checkbox' | 'radio' | 'text' | 'multiText' | 'date';\n\ninterface FilterOption {\n value: string;\n label: string;\n icon?: React.ReactNode;\n}\n\ninterface FilterDefinitionBase {\n key: string;\n label: string;\n}\n\ninterface CheckboxFilterDefinition extends FilterDefinitionBase {\n type?: 'checkbox';\n options: FilterOption[];\n /**\n * When `true`, the chip's value popup becomes a searchable Combobox\n * (multi-select with a search input). Use for long option lists where a\n * basic dropdown becomes hard to scan. When `false` or omitted, the chip\n * uses the standard checkbox dropdown.\n */\n searchable?: boolean;\n}\n\ninterface RadioFilterDefinition extends FilterDefinitionBase {\n type: 'radio';\n options: FilterOption[];\n /** Same searchable-Combobox swap as `CheckboxFilterDefinition`, constrained to a single selection. */\n searchable?: boolean;\n}\n\ninterface TextFilterDefinition extends FilterDefinitionBase {\n type: 'text';\n placeholder?: string;\n}\n\ninterface MultiTextFilterDefinition extends FilterDefinitionBase {\n type: 'multiText';\n placeholder?: string;\n}\n\ninterface DateFilterDefinition extends FilterDefinitionBase {\n type: 'date';\n /**\n * `true` always shows Start/End time inputs, `false` never shows them,\n * and omitted auto-detects from whether the current value already carries\n * a non-midnight time component.\n */\n includeTime?: boolean;\n}\n\ntype FilterDefinition =\n | CheckboxFilterDefinition\n | RadioFilterDefinition\n | TextFilterDefinition\n | MultiTextFilterDefinition\n | DateFilterDefinition;\n\ntype CheckboxFilterValue = string[];\ntype RadioFilterValue = string;\ntype TextFilterValue = string;\ntype MultiTextFilterValue = string[];\ntype DateFilterValue = { startDate: Date | null; endDate: Date | null };\ntype FilterValue = CheckboxFilterValue | RadioFilterValue | TextFilterValue | MultiTextFilterValue | DateFilterValue;\n\ninterface ActiveFilter {\n key: string;\n value?: FilterValue;\n}\n\n// ── Value helpers ─────────────────────────────────────\n\nfunction asCheckboxValue(value: FilterValue | undefined): CheckboxFilterValue {\n return Array.isArray(value) ? value : [];\n}\n\nfunction asRadioValue(value: FilterValue | undefined): RadioFilterValue {\n return typeof value === 'string' ? value : '';\n}\n\nfunction asTextValue(value: FilterValue | undefined): TextFilterValue {\n return typeof value === 'string' ? value : '';\n}\n\nfunction asMultiTextValue(value: FilterValue | undefined): MultiTextFilterValue {\n return Array.isArray(value) ? value : [];\n}\n\nfunction asDateValue(value: FilterValue | undefined): DateFilterValue {\n if (value && typeof value === 'object' && !Array.isArray(value)) return value as DateFilterValue;\n return { startDate: null, endDate: null };\n}\n\nfunction hasExplicitTime(date: Date | null): boolean {\n if (!date) return false;\n return date.getHours() !== 0 || date.getMinutes() !== 0;\n}\n\nfunction formatTimeForInput(date: Date | null): string {\n if (!date) return '';\n return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;\n}\n\nfunction applyTimeToDate(date: Date, time: string): Date {\n const [hours, minutes] = time.split(':').map(Number);\n const next = new Date(date);\n next.setHours(hours || 0, minutes || 0, 0, 0);\n return next;\n}\n\nfunction formatDateLabel(date: Date): string {\n return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\n}\n\n/** Merges the internal ref FilterBar uses to track its own root node with\n * any ref a consumer passes to `FilterBar` itself. */\nfunction composeRefs<T>(...refs: Array<React.Ref<T> | null | undefined>) {\n return (node: T | null) => {\n for (const ref of refs) {\n if (typeof ref === 'function') ref(node);\n else if (ref) (ref as React.RefObject<T | null>).current = node;\n }\n };\n}\n\ntype PortalContainer = React.ComponentProps<typeof ComboboxContent>['container'];\n\n// ── FilterBar ─────────────────────────────────────────\n\ninterface FilterBarProps extends React.ComponentProps<'div'> {\n filters: FilterDefinition[];\n activeFilters: ActiveFilter[];\n onAddFilter: (key: string) => void;\n onRemoveFilter: (key: string) => void;\n onChangeFilter: (key: string, value: FilterValue) => void;\n}\n\nfunction hasOrder(filter: FilterDefinition): boolean {\n return typeof (filter as { order?: unknown }).order === 'number';\n}\n\nfunction FilterBar({\n filters,\n activeFilters,\n onAddFilter,\n onRemoveFilter,\n onChangeFilter,\n className,\n children,\n ref,\n ...props\n}: FilterBarProps) {\n const usedKeys = new Set(activeFilters.map(f => f.key));\n const remainingFilters = filters.filter(f => !usedKeys.has(f.key));\n const availableFilters = filters.some(hasOrder) ? remainingFilters : sortBy(remainingFilters, f => f.label);\n const shouldShowFilters = availableFilters.length > 0 || activeFilters.length > 0;\n\n // Tracks the most recently added filter so its chip can open its value\n // popup immediately, without waiting for a second click.\n const [pendingOpenKey, setPendingOpenKey] = React.useState<string | null>(null);\n\n function handleAddFilter(key: string) {\n onAddFilter(key);\n setPendingOpenKey(key);\n }\n\n // Ref for the FilterBar's own root node, used to anchor Combobox popups.\n // Helps rendering correctly the popup when rendered inside a modal Dialog/Sheet.\n const containerRef = React.useRef<HTMLDivElement>(null);\n\n return (\n <div\n data-slot=\"filter-bar\"\n data-visible-filters={shouldShowFilters}\n className={cn('flex flex-wrap items-center gap-2', className)}\n ref={composeRefs(containerRef, ref)}\n {...props}\n >\n {/* Add filter button */}\n {availableFilters.length > 0 && (\n <Combobox\n items={availableFilters}\n itemToStringValue={(item: FilterDefinition) => item.label}\n itemToStringLabel={(item: FilterDefinition) => item.label}\n onValueChange={(filter: FilterDefinition | null) => {\n if (filter) handleAddFilter(filter.key);\n }}\n >\n {/* role=\"combobox\" doesn't pick up child text as its accessible name the\n way a plain button would — an explicit aria-label is required, not\n just a nicety. */}\n <ComboboxTrigger aria-label=\"Add filter\" showIndicator={false} render={<Button variant=\"outline\" />}>\n <Icon name=\"plus\" data-icon=\"inline-start\" />\n Add filter\n </ComboboxTrigger>\n <ComboboxContent container={containerRef} align=\"start\" className=\"w-48\">\n <ComboboxInput showTrigger={false} placeholder=\"Search filters…\" />\n <ComboboxEmpty>No filters found.</ComboboxEmpty>\n <ComboboxList>\n {(filter: FilterDefinition) => (\n <ComboboxItem key={filter.key} value={filter}>\n {filter.label}\n </ComboboxItem>\n )}\n </ComboboxList>\n </ComboboxContent>\n </Combobox>\n )}\n\n {/* Active filter chips */}\n {activeFilters.map(active => {\n const definition = filters.find(f => f.key === active.key);\n if (!definition) return null;\n return (\n <FilterChip\n key={active.key}\n definition={definition}\n value={active.value}\n autoOpen={active.key === pendingOpenKey}\n container={containerRef}\n onChangeValue={value => onChangeFilter(active.key, value)}\n onRemove={() => onRemoveFilter(active.key)}\n />\n );\n })}\n\n {/* Children (count, actions) — sit immediately after the last chip\n with the same `gap-2` spacing. With the parent's `flex-wrap`, a\n long row of chips will push the action onto the next line instead\n of stretching across an empty horizontal void. */}\n {children}\n </div>\n );\n}\n\n// ── FilterChip (dispatcher) ────────────────────────────\n\ninterface FilterChipProps {\n definition: FilterDefinition;\n value: FilterValue | undefined;\n /** Opens the chip's value popup as soon as it mounts — used right after the filter is added. */\n autoOpen?: boolean;\n /** Portals a searchable checkbox/radio chip's Combobox popup here instead of the default `document.body` — see `FilterBar`'s own `containerRef` for why. */\n container?: PortalContainer;\n onChangeValue: (value: FilterValue) => void;\n onRemove: () => void;\n}\n\nfunction FilterChip({ definition, value, autoOpen, container, onChangeValue, onRemove }: FilterChipProps) {\n const type = definition.type ?? 'checkbox';\n\n switch (type) {\n case 'checkbox':\n return (\n <CheckboxFilterChip\n definition={definition as CheckboxFilterDefinition}\n value={value}\n autoOpen={autoOpen}\n container={container}\n onChangeValue={onChangeValue}\n onRemove={onRemove}\n />\n );\n case 'radio':\n return (\n <RadioFilterChip\n definition={definition as RadioFilterDefinition}\n value={value}\n autoOpen={autoOpen}\n container={container}\n onChangeValue={onChangeValue}\n onRemove={onRemove}\n />\n );\n case 'text':\n return (\n <TextFilterChip\n definition={definition as TextFilterDefinition}\n value={value}\n autoOpen={autoOpen}\n onChangeValue={onChangeValue}\n onRemove={onRemove}\n />\n );\n case 'multiText':\n return (\n <MultiTextFilterChip\n definition={definition as MultiTextFilterDefinition}\n value={value}\n autoOpen={autoOpen}\n onChangeValue={onChangeValue}\n onRemove={onRemove}\n />\n );\n case 'date':\n return (\n <DateFilterChip\n definition={definition as DateFilterDefinition}\n value={value}\n autoOpen={autoOpen}\n onChangeValue={onChangeValue}\n onRemove={onRemove}\n />\n );\n }\n}\n\n// ── Shared chip chrome ─────────────────────────────────\n\nfunction FilterChipRemoveButton({ label, onRemove }: { label: string; onRemove: () => void }) {\n return (\n <Button variant=\"outline\" size=\"icon\" aria-label={`Remove ${label} filter`} onClick={onRemove}>\n <Icon name=\"xmark\" />\n </Button>\n );\n}\n\n// ── Checkbox chip ──────────────────────────────────────\n\nfunction CheckboxFilterChip({\n definition,\n value,\n autoOpen,\n container,\n onChangeValue,\n onRemove,\n}: {\n definition: CheckboxFilterDefinition;\n value: FilterValue | undefined;\n autoOpen?: boolean;\n container?: PortalContainer;\n onChangeValue: (value: FilterValue) => void;\n onRemove: () => void;\n}) {\n const { label, searchable = false } = definition;\n const options = sortBy(definition.options, o => o.label);\n const values = asCheckboxValue(value);\n const displayLabel =\n values.length === 0\n ? 'Any'\n : values.length === 1\n ? (options.find(o => o.value === values[0])?.label ?? values[0])\n : `${values.length} selected`;\n\n function toggle(optionValue: string) {\n const next = values.includes(optionValue) ? values.filter(v => v !== optionValue) : [...values, optionValue];\n onChangeValue(next);\n }\n\n return (\n <ButtonGroup>\n <ButtonGroupText>{label}</ButtonGroupText>\n {searchable ? (\n <Combobox\n items={options}\n itemToStringValue={(item: FilterOption) => item.label}\n multiple\n defaultOpen={autoOpen}\n value={options.filter(o => values.includes(o.value))}\n onValueChange={(selected: FilterOption[]) => onChangeValue(selected.map(s => s.value))}\n >\n <ComboboxTrigger\n render={<Button variant=\"outline\" className=\"gap-1 font-normal [&>svg:last-child]:size-(--icon-sm)\" />}\n >\n <span className={values.length === 0 ? 'text-muted-foreground' : ''}>{displayLabel}</span>\n </ComboboxTrigger>\n <ComboboxContent\n container={container}\n className=\"min-w-64\"\n collisionAvoidance={{ side: 'none', align: 'shift' }}\n >\n <ComboboxInput showTrigger={false} placeholder={`Search ${label.toLowerCase()}…`} />\n {values.length > 0 && (\n <>\n <ComboboxSelectedChips>\n <ComboboxValue>\n {(selected: FilterOption[]) => (\n <>\n {selected.map(item => (\n <ComboboxChip key={item.value}>{item.label}</ComboboxChip>\n ))}\n </>\n )}\n </ComboboxValue>\n <ComboboxClearAll onClick={() => onChangeValue([])} />\n </ComboboxSelectedChips>\n <ComboboxSeparator />\n </>\n )}\n <ComboboxEmpty>No {label.toLowerCase()} found.</ComboboxEmpty>\n <ComboboxList>\n {(item: FilterOption) => (\n <ComboboxItem key={item.value} value={item}>\n {item.icon}\n {item.label}\n </ComboboxItem>\n )}\n </ComboboxList>\n </ComboboxContent>\n </Combobox>\n ) : (\n <DropdownMenu defaultOpen={autoOpen}>\n <DropdownMenuTrigger asChild>\n <Button variant=\"outline\" className=\"gap-1 font-normal\">\n <span className={values.length === 0 ? 'text-muted-foreground' : ''}>{displayLabel}</span>\n <Icon name=\"chevron-down\" size=\"sm\" className=\"text-muted-foreground\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" className=\"w-48\">\n {options.map(opt => (\n <DropdownMenuCheckboxItem\n key={opt.value}\n checked={values.includes(opt.value)}\n onCheckedChange={() => toggle(opt.value)}\n >\n {opt.icon}\n {opt.label}\n </DropdownMenuCheckboxItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n <FilterChipRemoveButton label={label} onRemove={onRemove} />\n </ButtonGroup>\n );\n}\n\n// ── Radio chip ─────────────────────────────────────────\n\nfunction RadioFilterChip({\n definition,\n value,\n autoOpen,\n container,\n onChangeValue,\n onRemove,\n}: {\n definition: RadioFilterDefinition;\n value: FilterValue | undefined;\n autoOpen?: boolean;\n container?: PortalContainer;\n onChangeValue: (value: FilterValue) => void;\n onRemove: () => void;\n}) {\n const { label, searchable = false } = definition;\n const options = sortBy(definition.options, o => o.label);\n const selectedValue = asRadioValue(value);\n const displayLabel = selectedValue ? (options.find(o => o.value === selectedValue)?.label ?? selectedValue) : 'Any';\n const selected = options.find(o => o.value === selectedValue) ?? null;\n\n return (\n <ButtonGroup>\n <ButtonGroupText>{label}</ButtonGroupText>\n {searchable ? (\n <Combobox\n items={options}\n itemToStringValue={(item: FilterOption) => item.label}\n defaultOpen={autoOpen}\n value={selected}\n onValueChange={(next: FilterOption | null) => onChangeValue(next?.value ?? '')}\n >\n <ComboboxTrigger\n render={<Button variant=\"outline\" className=\"gap-1 font-normal [&>svg:last-child]:size-(--icon-sm)\" />}\n >\n <span className={selectedValue ? '' : 'text-muted-foreground'}>{displayLabel}</span>\n </ComboboxTrigger>\n <ComboboxContent\n container={container}\n className=\"min-w-64\"\n collisionAvoidance={{ side: 'none', align: 'shift' }}\n >\n <ComboboxInput showTrigger={false} placeholder={`Search ${label.toLowerCase()}…`} />\n <ComboboxEmpty>No {label.toLowerCase()} found.</ComboboxEmpty>\n <ComboboxList>\n {(item: FilterOption) => (\n <ComboboxItem key={item.value} value={item}>\n {item.icon}\n {item.label}\n </ComboboxItem>\n )}\n </ComboboxList>\n </ComboboxContent>\n </Combobox>\n ) : (\n <DropdownMenu defaultOpen={autoOpen}>\n <DropdownMenuTrigger asChild>\n <Button variant=\"outline\" className=\"gap-1 font-normal\">\n <span className={selectedValue ? '' : 'text-muted-foreground'}>{displayLabel}</span>\n <Icon name=\"chevron-down\" size=\"sm\" className=\"text-muted-foreground\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" className=\"w-48\">\n <DropdownMenuRadioGroup value={selectedValue} onValueChange={onChangeValue}>\n {options.map(opt => (\n <DropdownMenuRadioItem key={opt.value} value={opt.value}>\n {opt.icon}\n {opt.label}\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n <FilterChipRemoveButton label={label} onRemove={onRemove} />\n </ButtonGroup>\n );\n}\n\n// ── Text chip ──────────────────────────────────────────\n\nfunction TextFilterChip({\n definition,\n value,\n autoOpen,\n onChangeValue,\n onRemove,\n}: {\n definition: TextFilterDefinition;\n value: FilterValue | undefined;\n autoOpen?: boolean;\n onChangeValue: (value: FilterValue) => void;\n onRemove: () => void;\n}) {\n const { label, placeholder } = definition;\n const text = asTextValue(value);\n const [open, setOpen] = React.useState(!!autoOpen);\n\n return (\n <ButtonGroup>\n <ButtonGroupText>{label}</ButtonGroupText>\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button variant=\"outline\" className=\"gap-1 font-normal\">\n <span className={text ? '' : 'text-muted-foreground'}>{text || 'Any'}</span>\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"start\">\n <Input\n autoFocus\n value={text}\n placeholder={placeholder ?? 'Enter a value…'}\n onChange={e => onChangeValue(e.target.value)}\n onKeyDown={e => {\n if (e.key === 'Enter') setOpen(false);\n }}\n />\n </PopoverContent>\n </Popover>\n <FilterChipRemoveButton label={label} onRemove={onRemove} />\n </ButtonGroup>\n );\n}\n\n// ── MultiText chip ─────────────────────────────────────\n\nfunction MultiTextFilterChip({\n definition,\n value,\n autoOpen,\n onChangeValue,\n onRemove,\n}: {\n definition: MultiTextFilterDefinition;\n value: FilterValue | undefined;\n autoOpen?: boolean;\n onChangeValue: (value: FilterValue) => void;\n onRemove: () => void;\n}) {\n const { label, placeholder } = definition;\n const tokens = asMultiTextValue(value);\n const [draft, setDraft] = React.useState('');\n const displayLabel = tokens.length === 0 ? 'Any' : tokens.length === 1 ? tokens[0] : `${tokens.length} values`;\n\n function addToken() {\n const token = draft.trim();\n if (!token || tokens.includes(token)) {\n setDraft('');\n return;\n }\n onChangeValue([...tokens, token]);\n setDraft('');\n }\n\n function removeToken(token: string) {\n onChangeValue(tokens.filter(t => t !== token));\n }\n\n return (\n <ButtonGroup>\n <ButtonGroupText>{label}</ButtonGroupText>\n <Popover defaultOpen={autoOpen}>\n <PopoverTrigger asChild>\n <Button variant=\"outline\" className=\"gap-1 font-normal\">\n <span className={tokens.length === 0 ? 'text-muted-foreground' : ''}>{displayLabel}</span>\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"start\">\n <div className=\"flex flex-col gap-2\">\n {tokens.length > 0 && (\n <div className=\"flex flex-wrap gap-1\">\n {tokens.map(token => (\n <Badge key={token} variant=\"secondary\" className=\"gap-1\">\n {token}\n <button\n type=\"button\"\n aria-label={`Remove ${token}`}\n onClick={() => removeToken(token)}\n className=\"cursor-pointer\"\n >\n <Icon name=\"xmark\" size=\"xs\" />\n </button>\n </Badge>\n ))}\n </div>\n )}\n <Input\n autoFocus\n value={draft}\n placeholder={placeholder ?? 'Type a value, press Enter…'}\n onChange={e => setDraft(e.target.value)}\n onKeyDown={e => {\n if (e.key === 'Enter') {\n e.preventDefault();\n addToken();\n } else if (e.key === 'Backspace' && draft === '' && tokens.length > 0) {\n removeToken(tokens[tokens.length - 1]);\n }\n }}\n />\n </div>\n </PopoverContent>\n </Popover>\n <FilterChipRemoveButton label={label} onRemove={onRemove} />\n </ButtonGroup>\n );\n}\n\n// ── Date chip ──────────────────────────────────────────\n\nfunction DateFilterChip({\n definition,\n value,\n autoOpen,\n onChangeValue,\n onRemove,\n}: {\n definition: DateFilterDefinition;\n value: FilterValue | undefined;\n autoOpen?: boolean;\n onChangeValue: (value: FilterValue) => void;\n onRemove: () => void;\n}) {\n const { label, includeTime } = definition;\n const { startDate, endDate } = asDateValue(value);\n const showTimeInputs = includeTime ?? (hasExplicitTime(startDate) || hasExplicitTime(endDate));\n\n const displayLabel = !startDate\n ? 'Any'\n : !endDate || startDate.getTime() === endDate.getTime()\n ? formatDateLabel(startDate)\n : `${formatDateLabel(startDate)} – ${formatDateLabel(endDate)}`;\n\n function pickRange(range: DateRange | undefined) {\n if (!range?.from) {\n onChangeValue({ startDate: null, endDate: null });\n return;\n }\n onChangeValue({ startDate: range.from, endDate: range.to ?? range.from });\n }\n\n function pickStartTime(time: string) {\n if (!startDate) return;\n onChangeValue({ startDate: applyTimeToDate(startDate, time), endDate });\n }\n\n function pickEndTime(time: string) {\n if (!endDate) return;\n onChangeValue({ startDate, endDate: applyTimeToDate(endDate, time) });\n }\n\n return (\n <ButtonGroup>\n <ButtonGroupText>{label}</ButtonGroupText>\n <Popover defaultOpen={autoOpen}>\n <PopoverTrigger asChild>\n <Button variant=\"outline\" className=\"gap-1 font-normal\">\n <span className={startDate ? '' : 'text-muted-foreground'}>{displayLabel}</span>\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-fit\">\n <div className=\"flex flex-col gap-2\">\n <Calendar\n mode=\"range\"\n selected={{ from: startDate ?? undefined, to: endDate ?? undefined }}\n onSelect={pickRange}\n />\n {showTimeInputs && (\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex w-full items-center gap-2\">\n <Label\n htmlFor={`${definition.key}-start-time`}\n className=\"text-body-sm text-muted-foreground w-10 shrink-0\"\n >\n Start\n </Label>\n <Input\n id={`${definition.key}-start-time`}\n type=\"time\"\n className=\"w-full\"\n value={formatTimeForInput(startDate)}\n onChange={e => pickStartTime(e.target.value)}\n disabled={!startDate}\n />\n </div>\n <div className=\"flex w-full items-center gap-2\">\n <Label\n htmlFor={`${definition.key}-end-time`}\n className=\"text-body-sm text-muted-foreground w-10 shrink-0\"\n >\n End\n </Label>\n <Input\n id={`${definition.key}-end-time`}\n type=\"time\"\n className=\"w-full\"\n value={formatTimeForInput(endDate)}\n onChange={e => pickEndTime(e.target.value)}\n disabled={!endDate}\n />\n </div>\n </div>\n )}\n </div>\n </PopoverContent>\n </Popover>\n <FilterChipRemoveButton label={label} onRemove={onRemove} />\n </ButtonGroup>\n );\n}\n\nexport {\n FilterBar,\n FilterChip,\n type FilterType,\n type FilterDefinition,\n type CheckboxFilterDefinition,\n type RadioFilterDefinition,\n type TextFilterDefinition,\n type MultiTextFilterDefinition,\n type DateFilterDefinition,\n type ActiveFilter,\n type FilterOption,\n type FilterValue,\n type CheckboxFilterValue,\n type RadioFilterValue,\n type TextFilterValue,\n type MultiTextFilterValue,\n type DateFilterValue,\n type FilterBarProps,\n};\n","import { clsx, type ClassValue } from 'clsx';\nimport { extendTailwindMerge } from 'tailwind-merge';\n\nconst twMerge = extendTailwindMerge({\n extend: {\n classGroups: {\n 'font-size': [\n {\n text: [\n 'display-hero',\n 'display-kpi-sm',\n 'display-kpi',\n 'display-kpi-lg',\n 'display-feature',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'body-lg',\n 'body',\n 'body-sm',\n 'caption',\n 'eyebrow',\n ],\n },\n ],\n },\n },\n});\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport function sortBy<T>(items: T[], getKey: (item: T) => string): T[] {\n return [...items].sort((a, b) => getKey(a).localeCompare(getKey(b)));\n}\n"],"mappings":"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","names":["_a"]}
|
package/dist/graph.js
CHANGED
|
@@ -459,7 +459,7 @@ var GraphActionsProvider = React2.forwardRef(
|
|
|
459
459
|
GraphActionsProvider.displayName = "GraphActionsProvider";
|
|
460
460
|
|
|
461
461
|
// src/graph/graph.tsx
|
|
462
|
-
import * as
|
|
462
|
+
import * as React7 from "react";
|
|
463
463
|
import {
|
|
464
464
|
getCoreRowModel,
|
|
465
465
|
getFacetedRowModel,
|
|
@@ -700,7 +700,7 @@ function FilterBar(_a) {
|
|
|
700
700
|
if (filter) handleAddFilter(filter.key);
|
|
701
701
|
},
|
|
702
702
|
children: [
|
|
703
|
-
/* @__PURE__ */ jsxs4(ComboboxTrigger, { "aria-label": "Add filter", render: /* @__PURE__ */ jsx5(Button2, { variant: "outline" }), children: [
|
|
703
|
+
/* @__PURE__ */ jsxs4(ComboboxTrigger, { "aria-label": "Add filter", showIndicator: false, render: /* @__PURE__ */ jsx5(Button2, { variant: "outline" }), children: [
|
|
704
704
|
/* @__PURE__ */ jsx5(Icon3, { name: "plus", "data-icon": "inline-start" }),
|
|
705
705
|
"Add filter"
|
|
706
706
|
] }),
|
|
@@ -1250,15 +1250,8 @@ function SectionHeaderTitle(_a) {
|
|
|
1250
1250
|
}
|
|
1251
1251
|
|
|
1252
1252
|
// src/graph/graph-view.tsx
|
|
1253
|
-
import * as
|
|
1254
|
-
import {
|
|
1255
|
-
Background,
|
|
1256
|
-
Controls,
|
|
1257
|
-
MiniMap,
|
|
1258
|
-
ReactFlow,
|
|
1259
|
-
ReactFlowProvider,
|
|
1260
|
-
useReactFlow
|
|
1261
|
-
} from "reactflow";
|
|
1253
|
+
import * as React6 from "react";
|
|
1254
|
+
import { Background, Controls, MiniMap, ReactFlow, ReactFlowProvider } from "reactflow";
|
|
1262
1255
|
|
|
1263
1256
|
// src/graph/hooks/useAutoLayout.ts
|
|
1264
1257
|
import { useMemo as useMemo3 } from "react";
|
|
@@ -1464,25 +1457,50 @@ var useNodeVisibility = (nodes, edges) => {
|
|
|
1464
1457
|
return { nodes: visibleNodes, edges: visibleEdges };
|
|
1465
1458
|
};
|
|
1466
1459
|
|
|
1460
|
+
// src/graph/hooks/useViewportFocus.ts
|
|
1461
|
+
import * as React5 from "react";
|
|
1462
|
+
import { useReactFlow, useStore } from "reactflow";
|
|
1463
|
+
function getViewportFocusCenter(node) {
|
|
1464
|
+
var _a;
|
|
1465
|
+
const compact = node.type !== "loading" && Boolean((_a = node.data) == null ? void 0 : _a.compact);
|
|
1466
|
+
const height = compact ? GRAPH_NODE_COLLAPSED_HEIGHT : GRAPH_NODE_HEIGHT;
|
|
1467
|
+
return {
|
|
1468
|
+
x: node.position.x + GRAPH_NODE_WIDTH / 2,
|
|
1469
|
+
y: node.position.y + height / 2
|
|
1470
|
+
};
|
|
1471
|
+
}
|
|
1472
|
+
function useViewportFocus(nodeMap, viewportFocusNodeId) {
|
|
1473
|
+
const { setCenter } = useReactFlow();
|
|
1474
|
+
const viewportReady = useStore((state) => state.width > 0 && state.height > 0);
|
|
1475
|
+
const node = viewportFocusNodeId ? nodeMap.get(viewportFocusNodeId) : void 0;
|
|
1476
|
+
React5.useEffect(() => {
|
|
1477
|
+
if (!node || !viewportReady) {
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
const { x, y } = getViewportFocusCenter(node);
|
|
1481
|
+
setCenter(x, y, { zoom: 1 });
|
|
1482
|
+
}, [node == null ? void 0 : node.id, viewportReady, setCenter]);
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1467
1485
|
// src/graph/graph-view.tsx
|
|
1468
1486
|
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1469
1487
|
var GraphViewInternal = ({ nodeTypes: parentNodeTypes, edgeTypes: parentEdgeTypes, children, viewportFocusNodeId }) => {
|
|
1470
1488
|
const { nodes, edges, toggleNodeExpansion } = useGraphActionsContext();
|
|
1471
|
-
const nodeTypes =
|
|
1489
|
+
const nodeTypes = React6.useMemo(
|
|
1472
1490
|
() => __spreadValues({
|
|
1473
1491
|
default: GraphNode,
|
|
1474
1492
|
loading: GraphNodeSkeleton
|
|
1475
1493
|
}, parentNodeTypes),
|
|
1476
1494
|
[parentNodeTypes]
|
|
1477
1495
|
);
|
|
1478
|
-
const edgeTypes =
|
|
1496
|
+
const edgeTypes = React6.useMemo(
|
|
1479
1497
|
() => __spreadValues({
|
|
1480
1498
|
default: GraphEdge
|
|
1481
1499
|
}, parentEdgeTypes),
|
|
1482
1500
|
[parentEdgeTypes]
|
|
1483
1501
|
);
|
|
1484
1502
|
const { nodes: visibleNodes, edges: visibleEdges } = useNodeVisibility(nodes, edges);
|
|
1485
|
-
const nodesWithExpansion =
|
|
1503
|
+
const nodesWithExpansion = React6.useMemo(() => {
|
|
1486
1504
|
return visibleNodes.map((node) => {
|
|
1487
1505
|
if (isDefaultNode(node) && !node.data.onExpandToggle) {
|
|
1488
1506
|
return __spreadProps(__spreadValues({}, node), {
|
|
@@ -1529,21 +1547,10 @@ var GraphViewInternal = ({ nodeTypes: parentNodeTypes, edgeTypes: parentEdgeType
|
|
|
1529
1547
|
}
|
|
1530
1548
|
);
|
|
1531
1549
|
};
|
|
1532
|
-
var GraphView =
|
|
1550
|
+
var GraphView = React6.forwardRef(({ defaultNodes = [], defaultEdges = [], nodeTypes, edgeTypes, children, viewportFocusNodeId }, ref) => {
|
|
1533
1551
|
return /* @__PURE__ */ jsx8(ReactFlowProvider, { children: /* @__PURE__ */ jsx8(GraphActionsProvider, { ref, initialNodes: defaultNodes, initialEdges: defaultEdges, children: /* @__PURE__ */ jsx8(GraphViewInternal, { nodeTypes, edgeTypes, viewportFocusNodeId, children }) }) });
|
|
1534
1552
|
});
|
|
1535
1553
|
GraphView.displayName = "GraphView";
|
|
1536
|
-
function useViewportFocus(nodeMap, viewportFocusNodeId) {
|
|
1537
|
-
const flow = useReactFlow();
|
|
1538
|
-
React5.useEffect(() => {
|
|
1539
|
-
if (viewportFocusNodeId) {
|
|
1540
|
-
const focusNode = nodeMap.get(viewportFocusNodeId);
|
|
1541
|
-
if (focusNode) {
|
|
1542
|
-
flow.setCenter(focusNode.position.x, focusNode.position.y, { zoom: 1 });
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
}, [viewportFocusNodeId, flow, nodeMap]);
|
|
1546
|
-
}
|
|
1547
1554
|
|
|
1548
1555
|
// src/graph/graph.tsx
|
|
1549
1556
|
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
@@ -1582,12 +1589,12 @@ function Graph({
|
|
|
1582
1589
|
children
|
|
1583
1590
|
}) {
|
|
1584
1591
|
var _a, _b, _c;
|
|
1585
|
-
const graphRef =
|
|
1586
|
-
const [innerGlobalFilter, setInnerGlobalFilter] =
|
|
1587
|
-
const [innerColumnFilters, setInnerColumnFilters] =
|
|
1592
|
+
const graphRef = React7.useRef(null);
|
|
1593
|
+
const [innerGlobalFilter, setInnerGlobalFilter] = React7.useState("");
|
|
1594
|
+
const [innerColumnFilters, setInnerColumnFilters] = React7.useState([]);
|
|
1588
1595
|
const globalFilter = parentGlobalFilter != null ? parentGlobalFilter : innerGlobalFilter;
|
|
1589
1596
|
const setGlobalFilter = parentSetGlobalFilter != null ? parentSetGlobalFilter : setInnerGlobalFilter;
|
|
1590
|
-
|
|
1597
|
+
React7.useEffect(() => {
|
|
1591
1598
|
if (parentGlobalFilter !== void 0) {
|
|
1592
1599
|
setInnerGlobalFilter(parentGlobalFilter);
|
|
1593
1600
|
}
|
|
@@ -1608,7 +1615,7 @@ function Graph({
|
|
|
1608
1615
|
});
|
|
1609
1616
|
const flatColumns = table.getAllFlatColumns();
|
|
1610
1617
|
const filteredRows = table.getFilteredRowModel().rows;
|
|
1611
|
-
const filteredData =
|
|
1618
|
+
const filteredData = React7.useMemo(() => filteredRows.map((row) => row.original), [filteredRows]);
|
|
1612
1619
|
const filterCount = columnFilters.map(
|
|
1613
1620
|
({ id: columnFilterId, value }) => {
|
|
1614
1621
|
var _a2, _b2;
|
|
@@ -1622,10 +1629,10 @@ function Graph({
|
|
|
1622
1629
|
setColumnFilters([]);
|
|
1623
1630
|
setGlobalFilter("");
|
|
1624
1631
|
};
|
|
1625
|
-
const memoizedNodeTypes =
|
|
1626
|
-
const memoizedEdgeTypes =
|
|
1627
|
-
const memoizedConfig =
|
|
1628
|
-
|
|
1632
|
+
const memoizedNodeTypes = React7.useMemo(() => config.nodeTypes, [config.nodeTypes]);
|
|
1633
|
+
const memoizedEdgeTypes = React7.useMemo(() => config.edgeTypes, [config.edgeTypes]);
|
|
1634
|
+
const memoizedConfig = React7.useMemo(() => config, [config]);
|
|
1635
|
+
React7.useEffect(() => {
|
|
1629
1636
|
if (!graphRef.current) {
|
|
1630
1637
|
return;
|
|
1631
1638
|
}
|
|
@@ -1698,12 +1705,12 @@ function Graph({
|
|
|
1698
1705
|
|
|
1699
1706
|
// src/graph/hooks/useGetViewportNodeIds.ts
|
|
1700
1707
|
import { useEffect as useEffect3, useMemo as useMemo7, useRef as useRef3, useState as useState4 } from "react";
|
|
1701
|
-
import { useStore, useViewport } from "reactflow";
|
|
1708
|
+
import { useStore as useStore2, useViewport } from "reactflow";
|
|
1702
1709
|
var useGetDebouncedViewport = (debounceDelay = 1e3) => {
|
|
1703
1710
|
const timerRef = useRef3(null);
|
|
1704
1711
|
const [debouncedValues, setDebouncedValues] = useState4({ x: 0, y: 0, zoom: 1, width: 0, height: 0, nodes: [] });
|
|
1705
1712
|
const { x, y, zoom } = useViewport();
|
|
1706
|
-
const { width, height, nodes } =
|
|
1713
|
+
const { width, height, nodes } = useStore2((state) => ({
|
|
1707
1714
|
width: state.width,
|
|
1708
1715
|
height: state.height,
|
|
1709
1716
|
nodes: state.getNodes()
|