@open-mercato/ui 0.6.1-develop.3291.1.6fad645fd0 → 0.6.1
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/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +2 -0
- package/dist/ai/AiAssistantLauncher.js +8 -8
- package/dist/ai/AiAssistantLauncher.js.map +2 -2
- package/dist/ai/AiChat.js +92 -93
- package/dist/ai/AiChat.js.map +2 -2
- package/dist/ai/AiIcon.js +17 -0
- package/dist/ai/AiIcon.js.map +7 -0
- package/dist/ai/LoopDisabledBanner.js +28 -0
- package/dist/ai/LoopDisabledBanner.js.map +7 -0
- package/dist/ai/LoopTracePanel.js +123 -0
- package/dist/ai/LoopTracePanel.js.map +7 -0
- package/dist/ai/ModelPicker.js.map +1 -1
- package/dist/ai/index.js +8 -0
- package/dist/ai/index.js.map +2 -2
- package/dist/ai/parts/ConfirmationCard.js +1 -5
- package/dist/ai/parts/ConfirmationCard.js.map +2 -2
- package/dist/ai/parts/FieldDiffCard.js +8 -15
- package/dist/ai/parts/FieldDiffCard.js.map +2 -2
- package/dist/ai/parts/MutationResultCard.js +1 -4
- package/dist/ai/parts/MutationResultCard.js.map +2 -2
- package/dist/ai/ui-parts/pending-phase3-placeholder.js +0 -2
- package/dist/ai/ui-parts/pending-phase3-placeholder.js.map +2 -2
- package/dist/ai/useAiChat.js +18 -4
- package/dist/ai/useAiChat.js.map +2 -2
- package/dist/backend/CrudForm.js +24 -2
- package/dist/backend/CrudForm.js.map +2 -2
- package/dist/backend/DataTable.js +2 -2
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/confirm-dialog/ConfirmDialog.js +102 -96
- package/dist/backend/confirm-dialog/ConfirmDialog.js.map +2 -2
- package/dist/backend/detail/CustomDataSection.js +22 -3
- package/dist/backend/detail/CustomDataSection.js.map +2 -2
- package/dist/backend/filters/AdvancedFilterBuilder.js +2 -2
- package/dist/backend/filters/AdvancedFilterBuilder.js.map +2 -2
- package/dist/backend/injection/ComponentOverrideProvider.js +1 -1
- package/dist/backend/injection/ComponentOverrideProvider.js.map +2 -2
- package/dist/backend/inputs/ComboboxInput.js +25 -7
- package/dist/backend/inputs/ComboboxInput.js.map +2 -2
- package/dist/backend/progress/useProgressPoll.js +58 -3
- package/dist/backend/progress/useProgressPoll.js.map +2 -2
- package/dist/backend/progress/useProgressSse.js +15 -2
- package/dist/backend/progress/useProgressSse.js.map +2 -2
- package/dist/backend/utils/bulkDelete.js +76 -0
- package/dist/backend/utils/bulkDelete.js.map +2 -2
- package/package.json +11 -6
- package/src/ai/AiAssistantLauncher.tsx +8 -8
- package/src/ai/AiChat.tsx +44 -38
- package/src/ai/AiIcon.tsx +12 -0
- package/src/ai/LoopDisabledBanner.tsx +38 -0
- package/src/ai/LoopTracePanel.tsx +184 -0
- package/src/ai/ModelPicker.tsx +1 -1
- package/src/ai/__tests__/AiChat.test.tsx +174 -5
- package/src/ai/index.ts +8 -0
- package/src/ai/parts/ConfirmationCard.tsx +1 -5
- package/src/ai/parts/FieldDiffCard.tsx +0 -3
- package/src/ai/parts/MutationResultCard.tsx +1 -4
- package/src/ai/parts/__tests__/MutationResultCard.test.tsx +5 -0
- package/src/ai/ui-parts/pending-phase3-placeholder.tsx +0 -2
- package/src/ai/useAiChat.ts +35 -5
- package/src/backend/CrudForm.tsx +30 -3
- package/src/backend/DataTable.tsx +2 -2
- package/src/backend/__tests__/CrudForm.custom-fields.test.tsx +72 -0
- package/src/backend/__tests__/CustomDataSection.test.tsx +76 -0
- package/src/backend/__tests__/DataTable.render.test.tsx +36 -0
- package/src/backend/__tests__/component-replacement.test.tsx +44 -0
- package/src/backend/confirm-dialog/ConfirmDialog.tsx +101 -91
- package/src/backend/confirm-dialog/__tests__/ConfirmDialog.test.tsx +19 -0
- package/src/backend/detail/CustomDataSection.tsx +26 -3
- package/src/backend/filters/AdvancedFilterBuilder.tsx +6 -6
- package/src/backend/injection/ComponentOverrideProvider.tsx +2 -1
- package/src/backend/inputs/ComboboxInput.tsx +29 -7
- package/src/backend/inputs/__tests__/ComboboxInput.test.tsx +33 -2
- package/src/backend/progress/useProgressPoll.ts +68 -3
- package/src/backend/progress/useProgressSse.ts +16 -2
- package/src/backend/utils/__tests__/bulkDelete.test.ts +42 -0
- package/src/backend/utils/bulkDelete.ts +93 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/filters/AdvancedFilterBuilder.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\nimport * as React from 'react'\nimport { createPortal } from 'react-dom'\nimport { Plus, Trash2, X, GripVertical, ChevronDown } from 'lucide-react'\nimport {\n DndContext,\n DragOverlay,\n PointerSensor,\n KeyboardSensor,\n useSensor,\n useSensors,\n closestCenter,\n type DragEndEvent,\n type DragStartEvent,\n} from '@dnd-kit/core'\nimport {\n SortableContext,\n sortableKeyboardCoordinates,\n useSortable,\n verticalListSortingStrategy,\n} from '@dnd-kit/sortable'\nimport { CSS } from '@dnd-kit/utilities'\nimport { Button } from '../../primitives/button'\nimport { IconButton } from '../../primitives/icon-button'\nimport { Input } from '../../primitives/input'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '../../primitives/select'\nimport { Popover, PopoverContent, PopoverTrigger } from '../../primitives/popover'\nimport { Checkbox } from '../../primitives/checkbox'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type {\n FilterFieldDef,\n FilterFieldType,\n FilterOperator,\n} from '@open-mercato/shared/lib/query/advanced-filter'\nimport {\n OPERATORS_BY_FIELD_TYPE,\n getDefaultOperator,\n isValuelessOperator,\n} from '@open-mercato/shared/lib/query/advanced-filter'\nimport {\n type AdvancedFilterTree,\n type FilterRule,\n type FilterGroup,\n type FilterCombinator,\n TREE_LIMITS,\n} from '@open-mercato/shared/lib/query/advanced-filter-tree'\nimport { treeReducer, canAddRule, canAddGroup, type TreeAction } from './treeReducer'\nimport { FilterFieldPicker } from './FilterFieldPicker'\n\nexport type AdvancedFilterBuilderProps = {\n fields: FilterFieldDef[]\n value: AdvancedFilterTree\n onChange: (state: AdvancedFilterTree) => void\n onApply: () => void\n onClear: () => void\n pendingErrors?: Array<{ ruleId: string; messageKey: string; message: string }>\n}\n\nconst OPERATOR_LABELS: Record<FilterOperator, string> = {\n is: 'is', is_not: 'is not', contains: 'contains', does_not_contain: 'does not contain',\n starts_with: 'starts with', ends_with: 'ends with', is_empty: 'is empty', is_not_empty: 'is not empty',\n equals: 'equals', not_equals: 'not equals', greater_than: 'greater than', less_than: 'less than',\n greater_or_equal: 'greater or equal', less_or_equal: 'less or equal', between: 'between',\n is_before: 'is before', is_after: 'is after', is_any_of: 'is any of', is_none_of: 'is none of',\n is_true: 'is true', is_false: 'is false', has_any_of: 'has any of', has_all_of: 'has all of', has_none_of: 'has none of',\n}\n\nfunction getFieldType(fields: FilterFieldDef[], fieldKey: string): FilterFieldType {\n return fields.find((f) => f.key === fieldKey)?.type ?? 'text'\n}\n\n// Operator value shapes. Used to decide whether changing operator preserves the\n// current value: same shape preserves (`is` \u2192 `is not` keeps \"Active\"); different\n// shape resets (`is` \u2192 `between` clears, because value goes from scalar to pair).\ntype OperatorShape = 'valueless' | 'multi' | 'range' | 'single'\n\nconst MULTI_VALUE_OPS = new Set<FilterOperator>([\n 'is_any_of', 'is_none_of', 'has_any_of', 'has_all_of', 'has_none_of',\n])\n\nfunction getOperatorShape(op: FilterOperator): OperatorShape {\n if (isValuelessOperator(op)) return 'valueless'\n if (MULTI_VALUE_OPS.has(op)) return 'multi'\n if (op === 'between') return 'range'\n return 'single'\n}\n\ntype DragHandleProps = React.HTMLAttributes<HTMLButtonElement> & {\n ref?: React.Ref<HTMLButtonElement>\n}\n\ntype Translator = ReturnType<typeof useT>\n\nexport function AdvancedFilterBuilder({\n fields, value, onChange, onApply, onClear, pendingErrors,\n}: AdvancedFilterBuilderProps) {\n const t = useT()\n\n const dispatch = React.useCallback((action: TreeAction) => {\n onChange(treeReducer(value, action))\n }, [onChange, value])\n\n React.useEffect(() => {\n function onKey(e: KeyboardEvent) {\n if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {\n e.preventDefault()\n onApply()\n }\n }\n window.addEventListener('keydown', onKey)\n return () => window.removeEventListener('keydown', onKey)\n }, [onApply])\n\n const errorByRuleId = React.useMemo(() => {\n const m = new Map<string, string>()\n for (const e of pendingErrors ?? []) m.set(e.ruleId, t(e.messageKey, e.message))\n return m\n }, [pendingErrors, t])\n\n const empty = value.root.children.length === 0\n const defaultField = fields.length > 0 ? fields[0].key : undefined\n\n // Per-group SortableContexts scope drag-and-drop to within-group ordering.\n // The DragOverlay below renders a stable preview that\n // matches the original row's footprint \u2014 without it, dnd-kit applies\n // `transform` to the source row in place, which the wrapping flex layout\n // distorted into a \"tall\" / morphed ghost.\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),\n useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),\n )\n const [activeDrag, setActiveDrag] = React.useState<{ node: FilterRule | FilterGroup; parent: FilterGroup } | null>(null)\n const findNode = React.useCallback((id: string) => locateInTree(value.root, id), [value])\n\n const handleDragStart = React.useCallback((e: DragStartEvent) => {\n const found = findNode(String(e.active.id))\n if (found) setActiveDrag(found)\n }, [findNode])\n\n const handleDragEnd = React.useCallback((e: DragEndEvent) => {\n setActiveDrag(null)\n const { active, over } = e\n if (!over || active.id === over.id) return\n const from = findNode(String(active.id))\n const to = findNode(String(over.id))\n if (!from || !to) return\n if (from.parent.id !== to.parent.id) return\n const fromIdx = from.parent.children.findIndex((c) => c.id === active.id)\n const toIdx = to.parent.children.findIndex((c) => c.id === over.id)\n if (fromIdx < 0 || toIdx < 0) return\n dispatch({ type: 'reorderChildren', groupId: from.parent.id, fromIdx, toIdx })\n }, [dispatch, findNode])\n\n const handleDragCancel = React.useCallback(() => setActiveDrag(null), [])\n\n // Wrapper has no `min-w-[640px]`: it forced horizontal scroll on a 375px viewport.\n // Rules wrap onto multiple lines on mobile; on desktop (`sm:max-w-[720px]`) they fit\n // on one line.\n return (\n <DndContext\n sensors={sensors}\n collisionDetection={closestCenter}\n onDragStart={handleDragStart}\n onDragEnd={handleDragEnd}\n onDragCancel={handleDragCancel}\n >\n <div className=\"flex flex-col gap-3 p-3\">\n {!empty ? (\n <GroupView\n group={value.root}\n level={1}\n fields={fields}\n tree={value}\n dispatch={dispatch}\n defaultField={defaultField}\n errorByRuleId={errorByRuleId}\n t={t}\n />\n ) : null}\n {!empty ? (\n <div className=\"flex items-center gap-3 pt-2 border-t border-border\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-auto px-0 py-0 text-base text-muted-foreground hover:bg-transparent hover:text-foreground\"\n onClick={onClear}\n >\n <X className=\"size-4\" />\n {t('ui.advancedFilter.clearAll', 'Clear all')}\n </Button>\n </div>\n ) : null}\n </div>\n {/* Portal DragOverlay to document.body. Radix DialogContent uses CSS `transform`\n for centering, which becomes the containing block for `position: fixed`\n descendants (dnd-kit's overlay) and shifts the ghost off-screen. Portaling\n restores viewport-relative coordinates. */}\n {typeof document !== 'undefined'\n ? createPortal(\n // dnd-kit's DragOverlay sets its own inline z-index, so a Tailwind\n // utility class would be overridden \u2014 read the DS token via CSS var.\n <DragOverlay dropAnimation={null} style={{ zIndex: 'var(--z-index-modal-elevated, 55)' }}>\n {activeDrag ? (\n <DragGhost node={activeDrag.node} parent={activeDrag.parent} fields={fields} t={t} />\n ) : null}\n </DragOverlay>,\n document.body,\n )\n : null}\n </DndContext>\n )\n}\n\n/** Find a node in the tree along with its parent group. Used by drag handlers. */\nfunction locateInTree(root: FilterGroup, id: string): { node: FilterRule | FilterGroup; parent: FilterGroup } | null {\n for (const child of root.children) {\n if (child.id === id) return { node: child, parent: root }\n if (child.type === 'group') {\n const found = locateInTree(child, id)\n if (found) return found\n }\n }\n return null\n}\n\n// Compact drag preview rendered inside DragOverlay. Cannot reuse RuleRow/GroupView \u2014\n// their popovers and form inputs conflict with the overlay.\nfunction DragGhost({\n node, parent, fields, t,\n}: {\n node: FilterRule | FilterGroup\n parent: FilterGroup\n fields: FilterFieldDef[]\n t: Translator\n}) {\n const isGroup = node.type === 'group'\n const labelParts: string[] = []\n if (isGroup) {\n const ruleCount = countRulesIn(node)\n labelParts.push(t('ui.advancedFilter.dragGhost.group', 'Group'))\n labelParts.push(`(${ruleCount})`)\n } else {\n const def = fields.find((f) => f.key === node.field)\n labelParts.push(def?.label ?? (node.field || t('ui.advancedFilter.dragGhost.unsetField', 'Unset field')))\n labelParts.push(OPERATOR_LABELS[node.operator] ?? node.operator)\n if (typeof node.value === 'string' && node.value) labelParts.push(`\"${node.value}\"`)\n }\n return (\n <div className=\"inline-flex items-center gap-2 rounded-md border border-border bg-popover px-3 py-1.5 text-sm shadow-md cursor-grabbing\">\n <GripVertical className=\"size-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">{parent.combinator === 'and' ? t('ui.advancedFilter.connector.and', 'and') : t('ui.advancedFilter.connector.or', 'or')}</span>\n <span className=\"font-medium\">{labelParts.join(' ')}</span>\n </div>\n )\n}\n\nfunction countRulesIn(group: FilterGroup): number {\n let n = 0\n for (const c of group.children) {\n if (c.type === 'rule') n += 1\n else n += countRulesIn(c)\n }\n return n\n}\n\nfunction GroupView({\n group, level, fields, tree, dispatch, defaultField, errorByRuleId, t,\n}: {\n group: FilterGroup\n level: number\n fields: FilterFieldDef[]\n tree: AdvancedFilterTree\n dispatch: (a: TreeAction) => void\n defaultField?: string\n errorByRuleId: Map<string, string>\n t: Translator\n}) {\n const isRoot = level === 1\n // Sub-groups render as a card so the nesting boundary is visible; root group stays\n // flush with the popover so it doesn't double-up the dialog's own card chrome.\n const containerClass = isRoot\n ? 'space-y-3'\n : 'space-y-3 rounded-lg border border-border bg-muted/30 p-3'\n\n // Toolbar-first layout (Kendo React Filter pattern): one [And]/[Or] toggle per group\n // is the only combinator UI. Mixing AND with OR requires `+ Add subgroup`. Matches\n // `CompositeFilterDescriptor` semantics (one logic per group).\n return (\n <div className={containerClass}>\n <GroupToolbar\n group={group}\n level={level}\n tree={tree}\n dispatch={dispatch}\n defaultField={defaultField}\n fields={fields}\n isRoot={isRoot}\n t={t}\n />\n\n <SortableChildrenList\n group={group}\n level={level}\n fields={fields}\n tree={tree}\n dispatch={dispatch}\n defaultField={defaultField}\n errorByRuleId={errorByRuleId}\n t={t}\n />\n </div>\n )\n}\n\n// Kendo-style segmented `[And] [Or]` toggle. Clicking the unselected side flips\n// the group's combinator. Selected side is brand-violet filled so the affordance\n// is unambiguous.\nfunction AndOrToggle({\n combinator, onChange, t,\n}: {\n combinator: FilterCombinator\n onChange: (c: FilterCombinator) => void\n t: Translator\n}) {\n const andLabel = t('ui.advancedFilter.combinator.and', 'And')\n const orLabel = t('ui.advancedFilter.combinator.or', 'Or')\n const baseBtn = 'h-8 px-3 text-sm font-medium outline-none focus-visible:shadow-focus disabled:cursor-not-allowed'\n const selBtn = 'bg-brand-violet text-white hover:bg-brand-violet/90'\n const unselBtn = 'bg-background text-foreground hover:bg-accent'\n return (\n <div\n role=\"group\"\n aria-label={t('ui.advancedFilter.combinator.label', 'Group combinator')}\n className=\"inline-flex rounded-md border border-input overflow-hidden shrink-0\"\n data-testid=\"advanced-filter-combinator-toggle\"\n data-combinator={combinator}\n >\n <button\n type=\"button\"\n aria-pressed={combinator === 'and'}\n onClick={() => combinator !== 'and' && onChange('and')}\n className={`${baseBtn} ${combinator === 'and' ? selBtn : unselBtn}`}\n >\n {andLabel}\n </button>\n <button\n type=\"button\"\n aria-pressed={combinator === 'or'}\n onClick={() => combinator !== 'or' && onChange('or')}\n className={`${baseBtn} border-l border-input ${combinator === 'or' ? selBtn : unselBtn}`}\n >\n {orLabel}\n </button>\n </div>\n )\n}\n\nfunction SortableChildrenList({\n group, level, fields, tree, dispatch, defaultField, errorByRuleId, t,\n}: {\n group: FilterGroup\n level: number\n fields: FilterFieldDef[]\n tree: AdvancedFilterTree\n dispatch: (a: TreeAction) => void\n defaultField?: string\n errorByRuleId: Map<string, string>\n t: Translator\n}) {\n // DndContext + handlers live one level up so a drag can move children across\n // groups. Each group owns its own SortableContext to scope sort ordering.\n return (\n <SortableContext items={group.children.map((c) => c.id)} strategy={verticalListSortingStrategy}>\n {group.children.map((child, idx) => (\n <SortableRow\n key={child.id}\n child={child}\n idx={idx}\n parent={group}\n level={level}\n fields={fields}\n tree={tree}\n dispatch={dispatch}\n defaultField={defaultField}\n errorByRuleId={errorByRuleId}\n t={t}\n />\n ))}\n </SortableContext>\n )\n}\n\nfunction SortableRow({\n child, idx, parent, level, fields, tree, dispatch, defaultField, errorByRuleId, t,\n}: {\n child: FilterRule | FilterGroup\n idx: number\n parent: FilterGroup\n level: number\n fields: FilterFieldDef[]\n tree: AdvancedFilterTree\n dispatch: (a: TreeAction) => void\n defaultField?: string\n errorByRuleId: Map<string, string>\n t: Translator\n}) {\n const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({\n id: child.id,\n })\n // Hide the source while dragging (DragOverlay renders the snapshot); keep it in\n // layout via `visibility: hidden` so siblings don't jump. Without this, the flex\n // wrapper applies both `transform` and its own resizing to the source row.\n const style: React.CSSProperties = isDragging\n ? { visibility: 'hidden' as const }\n : { transform: CSS.Transform.toString(transform), transition }\n const dragHandleProps: DragHandleProps = { ...attributes, ...listeners }\n\n return (\n <div ref={setNodeRef} style={style} data-testid=\"filter-rule-row\" data-filter-node-id={child.id}>\n {child.type === 'rule' ? (\n <RuleRow\n rule={child}\n fields={fields}\n dispatch={dispatch}\n errorByRuleId={errorByRuleId}\n dragHandleProps={dragHandleProps}\n t={t}\n />\n ) : (\n <div className=\"flex items-start gap-2\">\n {/* Raw <button> required: dnd-kit spreads `attributes`+`listeners` onto the\n activator; <Button>'s own props/ref handling breaks the keyboard sensor. */}\n <button\n type=\"button\"\n {...dragHandleProps}\n data-testid=\"filter-drag-handle\"\n className=\"mt-1 text-muted-foreground/60 hover:text-muted-foreground cursor-grab outline-none focus-visible:shadow-focus rounded-sm\"\n aria-label={t('ui.advancedFilter.dragHandle', 'Drag to reorder')}\n >\n <GripVertical className=\"size-4\" />\n </button>\n <div className=\"flex-1\">\n <GroupView\n group={child}\n level={level + 1}\n fields={fields}\n tree={tree}\n dispatch={dispatch}\n defaultField={defaultField}\n errorByRuleId={errorByRuleId}\n t={t}\n />\n </div>\n </div>\n )}\n </div>\n )\n}\n\nfunction RuleRow({\n rule, fields, dispatch, errorByRuleId, dragHandleProps, t,\n}: {\n rule: FilterRule\n fields: FilterFieldDef[]\n dispatch: (a: TreeAction) => void\n errorByRuleId: Map<string, string>\n dragHandleProps: DragHandleProps\n t: Translator\n}) {\n const fieldType = getFieldType(fields, rule.field)\n const operators = OPERATORS_BY_FIELD_TYPE[fieldType] ?? OPERATORS_BY_FIELD_TYPE.text\n const valueless = isValuelessOperator(rule.operator)\n const errorMsg = errorByRuleId.get(rule.id)\n const fieldDef = fields.find((f) => f.key === rule.field)\n const [pickerOpen, setPickerOpen] = React.useState(false)\n const fieldBtnRef = React.useRef<HTMLButtonElement>(null)\n\n return (\n <div className=\"flex flex-col gap-1\" data-testid=\"filter-rule\" data-filter-rule-id={rule.id}>\n <div className=\"flex flex-wrap items-start gap-2\">\n {/* Raw <button> required for dnd-kit activator: see SortableRow drag-handle comment. */}\n <button\n type=\"button\"\n {...dragHandleProps}\n data-testid=\"filter-drag-handle\"\n className=\"mt-1 text-muted-foreground/60 hover:text-muted-foreground cursor-grab outline-none focus-visible:shadow-focus rounded-sm\"\n aria-label={t('ui.advancedFilter.dragHandle', 'Drag to reorder')}\n >\n <GripVertical className=\"size-4\" />\n </button>\n <Button\n ref={fieldBtnRef}\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setPickerOpen(true)}\n className=\"min-w-[140px] justify-start\"\n >\n {fieldDef?.label ?? t('ui.advancedFilter.selectField', 'Select field')}\n </Button>\n <FilterFieldPicker\n fields={fields}\n open={pickerOpen}\n onOpenChange={setPickerOpen}\n onSelect={(f) => {\n const nextType = getFieldType(fields, f.key)\n dispatch({\n type: 'updateRule',\n ruleId: rule.id,\n updates: { field: f.key, operator: getDefaultOperator(nextType), value: '' },\n })\n }}\n triggerRef={fieldBtnRef as React.RefObject<HTMLElement | null>}\n />\n <Select\n value={rule.operator}\n onValueChange={(next) => {\n // Preserve the value when the operator shape doesn't change; reset\n // only when shape changes (e.g. `is` \u2192 `between` clears scalar to pair).\n const nextOp = next as FilterOperator\n const sameShape = getOperatorShape(rule.operator) === getOperatorShape(nextOp)\n const updates: Partial<Pick<FilterRule, 'operator' | 'value'>> = sameShape\n ? { operator: nextOp }\n : { operator: nextOp, value: getOperatorShape(nextOp) === 'multi' || getOperatorShape(nextOp) === 'range' ? [] : '' }\n dispatch({ type: 'updateRule', ruleId: rule.id, updates })\n }}\n >\n {/* `w-auto` overrides SelectTrigger's default `w-full` so it sits\n inline with the field/value inputs instead of breaking onto a new row. */}\n <SelectTrigger\n size=\"sm\"\n className=\"w-auto min-w-[140px]\"\n aria-label={t('ui.advancedFilter.selectOperator', 'Select operator')}\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent data-advanced-filter-portal=\"\">\n {operators.map((op) => (\n <SelectItem key={op} value={op}>\n {t(`ui.advancedFilter.operator.${op}`, OPERATOR_LABELS[op])}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {!valueless ? (\n <ValueInput\n rule={rule}\n fields={fields}\n fieldType={fieldType}\n dispatch={dispatch}\n hasError={!!errorMsg}\n t={t}\n />\n ) : null}\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n onClick={() => dispatch({ type: 'removeNode', nodeId: rule.id })}\n aria-label={t('ui.advancedFilter.removeCondition', 'Remove condition')}\n title={t('ui.advancedFilter.removeCondition', 'Remove condition')}\n >\n <Trash2 className=\"size-4 text-muted-foreground\" />\n </IconButton>\n </div>\n {errorMsg ? (\n <div className=\"ml-6 pl-12 text-xs text-status-error-text\">{errorMsg}</div>\n ) : null}\n </div>\n )\n}\n\nfunction ValueInput({\n rule, fields, fieldType, dispatch, hasError, t,\n}: {\n rule: FilterRule\n fields: FilterFieldDef[]\n fieldType: FilterFieldType\n dispatch: (a: TreeAction) => void\n hasError: boolean\n t: Translator\n}) {\n const fieldDef = fields.find((f) => f.key === rule.field)\n const value = rule.value\n const errorClass = hasError\n ? 'border-status-error-border ring-1 ring-status-error-border focus-visible:border-status-error-border focus-within:border-status-error-border'\n : ''\n\n if (fieldType === 'select' && fieldDef?.options) {\n // Multi-value operators (`is_any_of`, `is_none_of`, `has_any_of`, etc.)\n // need an array value with multiple selectable options. Radix `Select` is\n // single-select only, so the previous code silently overwrote each\n // selection (and validation kept failing because the value never became an\n // array). Render a checkbox-list popover when the operator is multi-shape.\n if (getOperatorShape(rule.operator) === 'multi') {\n return (\n <MultiValuePicker\n rule={rule}\n options={fieldDef.options}\n dispatch={dispatch}\n errorClass={errorClass}\n t={t}\n />\n )\n }\n return (\n <Select\n value={typeof value === 'string' && value.length ? value : undefined}\n onValueChange={(next) =>\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value: next ?? '' } })\n }\n >\n <SelectTrigger\n size=\"sm\"\n className={`w-auto min-w-[160px] ${errorClass}`}\n aria-label={t('ui.advancedFilter.selectValue', 'Select value')}\n >\n <SelectValue placeholder={t('ui.advancedFilter.selectValuePlaceholder', 'Select...')} />\n </SelectTrigger>\n <SelectContent data-advanced-filter-portal=\"\">\n {fieldDef.options.map((opt) => (\n <SelectItem key={opt.value} value={opt.value}>\n {opt.tone ? (\n <span className=\"inline-flex items-center gap-2\">\n <span className={toneDotClass(opt.tone)} aria-hidden=\"true\" />\n {opt.label}\n </span>\n ) : (\n opt.label\n )}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )\n }\n\n // `Input` ships `w-full` by default \u2014 use `w-auto` so inputs don't stretch\n // inside the wrapping flex row. `between` needs a `[start, end]` pair, so it\n // gets its own component below.\n if (rule.operator === 'between' && (fieldType === 'number' || fieldType === 'date')) {\n return (\n <BetweenInput\n rule={rule}\n fieldType={fieldType}\n dispatch={dispatch}\n errorClass={errorClass}\n t={t}\n />\n )\n }\n\n if (fieldType === 'date') {\n return (\n <Input\n type=\"date\"\n size=\"sm\"\n className={`w-auto min-w-[160px] ${errorClass}`}\n value={typeof value === 'string' ? value : ''}\n onChange={(e) =>\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value: e.target.value } })\n }\n aria-label={t('ui.advancedFilter.dateValue', 'Date value')}\n />\n )\n }\n\n if (fieldType === 'number') {\n return (\n <Input\n type=\"number\"\n size=\"sm\"\n className={`w-[120px] ${errorClass}`}\n value={typeof value === 'number' ? value : typeof value === 'string' ? value : ''}\n onChange={(e) =>\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value: e.target.value } })\n }\n placeholder={t('ui.advancedFilter.numberPlaceholder', 'Value')}\n aria-label={t('ui.advancedFilter.numberValue', 'Number value')}\n />\n )\n }\n\n return (\n <Input\n type=\"text\"\n size=\"sm\"\n className={`w-auto min-w-[160px] ${errorClass}`}\n value={typeof value === 'string' ? value : ''}\n onChange={(e) =>\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value: e.target.value } })\n }\n placeholder={t('ui.advancedFilter.textPlaceholder', 'Value...')}\n aria-label={t('ui.advancedFilter.textValue', 'Text value')}\n />\n )\n}\n\n/**\n * Two inputs (start / end) for the `between` operator on date and number\n * fields. Stores the value as `[start, end]`; either side can be empty while\n * the user types, but validation requires both to be set before apply.\n */\nfunction BetweenInput({\n rule, fieldType, dispatch, errorClass, t,\n}: {\n rule: FilterRule\n fieldType: FilterFieldType\n dispatch: (a: TreeAction) => void\n errorClass: string\n t: Translator\n}) {\n const pair = Array.isArray(rule.value) ? rule.value : []\n const start = typeof pair[0] === 'string' || typeof pair[0] === 'number' ? String(pair[0] ?? '') : ''\n const end = typeof pair[1] === 'string' || typeof pair[1] === 'number' ? String(pair[1] ?? '') : ''\n const update = (which: 'start' | 'end', next: string) => {\n const value = which === 'start' ? [next, end] : [start, next]\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value } })\n }\n const inputType = fieldType === 'date' ? 'date' : 'number'\n const inputWidth = fieldType === 'date' ? 'w-auto min-w-[140px]' : 'w-[110px]'\n return (\n <div className=\"inline-flex items-center gap-1\">\n <Input\n type={inputType}\n size=\"sm\"\n className={`${inputWidth} ${errorClass}`}\n value={start}\n onChange={(e) => update('start', e.target.value)}\n placeholder={fieldType === 'number' ? t('ui.advancedFilter.numberPlaceholder', 'Value') : undefined}\n aria-label={t('ui.advancedFilter.betweenStart', 'Start')}\n />\n <span className=\"text-xs text-muted-foreground\">{t('ui.advancedFilter.and', 'and')}</span>\n <Input\n type={inputType}\n size=\"sm\"\n className={`${inputWidth} ${errorClass}`}\n value={end}\n onChange={(e) => update('end', e.target.value)}\n placeholder={fieldType === 'number' ? t('ui.advancedFilter.numberPlaceholder', 'Value') : undefined}\n aria-label={t('ui.advancedFilter.betweenEnd', 'End')}\n />\n </div>\n )\n}\n\n/**\n * Trigger + popover with a checkbox list. Used when a select-typed rule has a\n * multi-shape operator (`is_any_of`, `has_all_of`, etc.). The trigger displays\n * a \"{first} +N\" summary; the popover lets the user toggle each option.\n */\nfunction MultiValuePicker({\n rule, options, dispatch, errorClass, t,\n}: {\n rule: FilterRule\n options: NonNullable<FilterFieldDef['options']>\n dispatch: (a: TreeAction) => void\n errorClass: string\n t: Translator\n}) {\n const selected: string[] = Array.isArray(rule.value)\n ? rule.value.map((v) => String(v)).filter((v) => v.length > 0)\n : []\n const selectedLabel = (() => {\n if (selected.length === 0) return t('ui.advancedFilter.selectValuePlaceholder', 'Select...')\n const firstOpt = options.find((o) => o.value === selected[0])\n const head = firstOpt?.label ?? selected[0]\n const more = selected.length - 1\n return more > 0 ? `${head} +${more}` : head\n })()\n const toggle = (val: string) => {\n const next = selected.includes(val) ? selected.filter((v) => v !== val) : [...selected, val]\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value: next } })\n }\n return (\n <Popover>\n <PopoverTrigger asChild>\n {/* Visually mirrors `SelectTrigger size=\"sm\"` (h-8, rounded-md, border)\n so the operator + value pair stays visually consistent. */}\n <button\n type=\"button\"\n className={`inline-flex h-8 items-center justify-between gap-2 rounded-md border border-input bg-background px-2.5 text-xs shadow-xs transition-colors hover:bg-muted/40 focus-visible:outline-none focus-visible:shadow-focus focus-visible:border-foreground w-auto min-w-[160px] ${errorClass}`}\n aria-label={t('ui.advancedFilter.selectValue', 'Select value')}\n data-testid=\"multi-value-trigger\"\n >\n <span className={selected.length === 0 ? 'text-muted-foreground' : ''}>{selectedLabel}</span>\n <ChevronDown className=\"size-3 text-muted-foreground\" />\n </button>\n </PopoverTrigger>\n <PopoverContent className=\"w-64 p-1\" align=\"start\" data-advanced-filter-portal=\"\">\n <div className=\"max-h-64 overflow-y-auto\" role=\"listbox\" aria-multiselectable=\"true\">\n {options.map((opt) => {\n const isOn = selected.includes(opt.value)\n return (\n // Raw <button>: needs `role=\"option\"` + `aria-selected`; <Button> would override role.\n <button\n key={opt.value}\n type=\"button\"\n role=\"option\"\n aria-selected={isOn}\n onClick={() => toggle(opt.value)}\n className=\"flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm hover:bg-accent focus-visible:bg-accent focus-visible:outline-none\"\n >\n <Checkbox checked={isOn} aria-hidden=\"true\" tabIndex={-1} className=\"pointer-events-none\" />\n {opt.tone ? (\n <span className=\"inline-flex items-center gap-2\">\n <span className={toneDotClass(opt.tone)} aria-hidden=\"true\" />\n {opt.label}\n </span>\n ) : (\n <span>{opt.label}</span>\n )}\n </button>\n )\n })}\n </div>\n </PopoverContent>\n </Popover>\n )\n}\n\nfunction toneDotClass(tone: NonNullable<FilterFieldDef['options']>[number]['tone']): string {\n // Solid filled dot using the saturated `*-icon` token (matches Tag's `dotColorMap`).\n const base = 'inline-block size-2 rounded-full shrink-0'\n switch (tone) {\n case 'success': return `${base} bg-status-success-icon`\n case 'error': return `${base} bg-status-error-icon`\n case 'warning': return `${base} bg-status-warning-icon`\n case 'info': return `${base} bg-status-info-icon`\n case 'neutral': return `${base} bg-status-neutral-icon`\n case 'pink': return `${base} bg-status-pink-icon`\n case 'brand': return `${base} bg-brand-violet`\n default: return `${base} bg-muted-foreground/40`\n }\n}\n\n// Single Kendo-style toolbar per group: combinator toggle, `+ Add condition`,\n// `+ Add subgroup` / `+ Add group`, plus a trailing delete for non-root groups.\nfunction GroupToolbar({\n group, level, tree, dispatch, defaultField, fields, isRoot, t,\n}: {\n group: FilterGroup\n level: number\n tree: AdvancedFilterTree\n dispatch: (a: TreeAction) => void\n defaultField?: string\n fields: FilterFieldDef[]\n isRoot: boolean\n t: Translator\n}) {\n const [pickerOpen, setPickerOpen] = React.useState(false)\n const addBtnRef = React.useRef<HTMLButtonElement>(null)\n const addRuleEnabled = canAddRule(tree, group.id)\n const addGroupEnabled = canAddGroup(tree, group.id)\n\n const widthCapHit = group.children.length >= TREE_LIMITS.maxChildrenPerGroup\n const depthCapHit = level >= TREE_LIMITS.maxGroupLevel\n\n const addRuleTitle = !addRuleEnabled\n ? widthCapHit\n ? t('ui.advancedFilter.limitWidthReached', 'Maximum {max} conditions per group', {\n max: TREE_LIMITS.maxChildrenPerGroup,\n })\n : t('ui.advancedFilter.limitTotalReached', 'Maximum {max} conditions reached', {\n max: TREE_LIMITS.maxTotalRules,\n })\n : undefined\n\n const addGroupTitle = !addGroupEnabled\n ? depthCapHit\n ? t('ui.advancedFilter.limitDepthReached', 'Maximum nesting depth reached')\n : widthCapHit\n ? t('ui.advancedFilter.limitWidthReached', 'Maximum {max} conditions per group', {\n max: TREE_LIMITS.maxChildrenPerGroup,\n })\n : t('ui.advancedFilter.limitTotalReached', 'Maximum {max} conditions reached', {\n max: TREE_LIMITS.maxTotalRules,\n })\n : undefined\n\n const addGroupLabel = level === 1\n ? t('ui.advancedFilter.addGroup', '+ Add group')\n : t('ui.advancedFilter.addSubgroup', '+ Add subgroup')\n\n // `text-brand-violet` overrides Button's default disabled token, so we have to\n // re-state muted disabled colors here \u2014 otherwise disabled CTAs still look clickable.\n const ctaClass = 'text-brand-violet hover:text-brand-violet/80 hover:bg-transparent px-0 disabled:text-muted-foreground/50 disabled:hover:text-muted-foreground/50'\n\n return (\n <div className=\"flex flex-col gap-1\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <AndOrToggle\n combinator={group.combinator}\n onChange={(c) => dispatch({ type: 'updateGroupCombinator', groupId: group.id, combinator: c })}\n t={t}\n />\n <Button\n ref={addBtnRef}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n disabled={!addRuleEnabled}\n title={addRuleTitle}\n onClick={() => setPickerOpen(true)}\n className={ctaClass}\n >\n <Plus className=\"size-4\" />\n {t('ui.advancedFilter.addCondition', '+ Add condition')}\n </Button>\n <FilterFieldPicker\n fields={fields}\n open={pickerOpen}\n onOpenChange={setPickerOpen}\n onSelect={(f) =>\n dispatch({\n type: 'addRule',\n groupId: group.id,\n defaultField: f.key,\n defaultOperator: getDefaultOperator(f.type),\n })\n }\n triggerRef={addBtnRef as React.RefObject<HTMLElement | null>}\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n disabled={!addGroupEnabled}\n title={addGroupTitle}\n onClick={() =>\n dispatch({ type: 'addGroup', groupId: group.id, defaultField })\n }\n className={ctaClass}\n >\n <Plus className=\"size-4\" />\n {addGroupLabel}\n </Button>\n {!isRoot ? (\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n onClick={() => dispatch({ type: 'removeNode', nodeId: group.id })}\n aria-label={t('ui.advancedFilter.deleteGroup', 'Delete group')}\n title={t('ui.advancedFilter.deleteGroup', 'Delete group')}\n className=\"ml-auto\"\n >\n <X className=\"size-4 text-muted-foreground\" />\n </IconButton>\n ) : null}\n </div>\n {!addGroupEnabled && depthCapHit ? (\n <p className=\"text-xs text-muted-foreground\" data-testid=\"filter-depth-limit-hint\">\n {t('ui.advancedFilter.limitDepthHint', 'You\\'ve reached the maximum nesting depth ({max} levels). Switch this group\\'s combinator with the toggle above instead of nesting deeper.', {\n max: TREE_LIMITS.maxGroupLevel,\n })}\n </p>\n ) : null}\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["'use client'\nimport * as React from 'react'\nimport { createPortal } from 'react-dom'\nimport { Plus, Trash2, X, GripVertical, ChevronDown } from 'lucide-react'\nimport {\n DndContext,\n DragOverlay,\n PointerSensor,\n KeyboardSensor,\n useSensor,\n useSensors,\n closestCenter,\n type DragEndEvent,\n type DragStartEvent,\n} from '@dnd-kit/core'\nimport {\n SortableContext,\n sortableKeyboardCoordinates,\n useSortable,\n verticalListSortingStrategy,\n} from '@dnd-kit/sortable'\nimport { CSS } from '@dnd-kit/utilities'\nimport { Button } from '../../primitives/button'\nimport { IconButton } from '../../primitives/icon-button'\nimport { Input } from '../../primitives/input'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '../../primitives/select'\nimport { Popover, PopoverContent, PopoverTrigger } from '../../primitives/popover'\nimport { Checkbox } from '../../primitives/checkbox'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type {\n FilterFieldDef,\n FilterFieldType,\n FilterOperator,\n} from '@open-mercato/shared/lib/query/advanced-filter'\nimport {\n OPERATORS_BY_FIELD_TYPE,\n getDefaultOperator,\n isValuelessOperator,\n} from '@open-mercato/shared/lib/query/advanced-filter'\nimport {\n type AdvancedFilterTree,\n type FilterRule,\n type FilterGroup,\n type FilterCombinator,\n TREE_LIMITS,\n} from '@open-mercato/shared/lib/query/advanced-filter-tree'\nimport { treeReducer, canAddRule, canAddGroup, type TreeAction } from './treeReducer'\nimport { FilterFieldPicker } from './FilterFieldPicker'\n\nexport type AdvancedFilterBuilderProps = {\n fields: FilterFieldDef[]\n value: AdvancedFilterTree\n onChange: (state: AdvancedFilterTree) => void\n onApply: () => void\n onClear: () => void\n pendingErrors?: Array<{ ruleId: string; messageKey: string; message: string }>\n}\n\nconst OPERATOR_LABELS: Record<FilterOperator, string> = {\n is: 'is', is_not: 'is not', contains: 'contains', does_not_contain: 'does not contain',\n starts_with: 'starts with', ends_with: 'ends with', is_empty: 'is empty', is_not_empty: 'is not empty',\n equals: 'equals', not_equals: 'not equals', greater_than: 'greater than', less_than: 'less than',\n greater_or_equal: 'greater or equal', less_or_equal: 'less or equal', between: 'between',\n is_before: 'is before', is_after: 'is after', is_any_of: 'is any of', is_none_of: 'is none of',\n is_true: 'is true', is_false: 'is false', has_any_of: 'has any of', has_all_of: 'has all of', has_none_of: 'has none of',\n}\n\nfunction getFieldType(fields: FilterFieldDef[], fieldKey: string): FilterFieldType {\n return fields.find((f) => f.key === fieldKey)?.type ?? 'text'\n}\n\n// Operator value shapes. Used to decide whether changing operator preserves the\n// current value: same shape preserves (`is` \u2192 `is not` keeps \"Active\"); different\n// shape resets (`is` \u2192 `between` clears, because value goes from scalar to pair).\ntype OperatorShape = 'valueless' | 'multi' | 'range' | 'single'\n\nconst MULTI_VALUE_OPS = new Set<FilterOperator>([\n 'is_any_of', 'is_none_of', 'has_any_of', 'has_all_of', 'has_none_of',\n])\n\nfunction getOperatorShape(op: FilterOperator): OperatorShape {\n if (isValuelessOperator(op)) return 'valueless'\n if (MULTI_VALUE_OPS.has(op)) return 'multi'\n if (op === 'between') return 'range'\n return 'single'\n}\n\ntype DragHandleProps = React.HTMLAttributes<HTMLButtonElement> & {\n ref?: React.Ref<HTMLButtonElement>\n}\n\ntype Translator = ReturnType<typeof useT>\n\nexport function AdvancedFilterBuilder({\n fields, value, onChange, onApply, onClear, pendingErrors,\n}: AdvancedFilterBuilderProps) {\n const t = useT()\n\n const dispatch = React.useCallback((action: TreeAction) => {\n onChange(treeReducer(value, action))\n }, [onChange, value])\n\n React.useEffect(() => {\n function onKey(e: KeyboardEvent) {\n if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {\n e.preventDefault()\n onApply()\n }\n }\n window.addEventListener('keydown', onKey)\n return () => window.removeEventListener('keydown', onKey)\n }, [onApply])\n\n const errorByRuleId = React.useMemo(() => {\n const m = new Map<string, string>()\n for (const e of pendingErrors ?? []) m.set(e.ruleId, t(e.messageKey, e.message))\n return m\n }, [pendingErrors, t])\n\n const empty = value.root.children.length === 0\n const defaultField = fields.length > 0 ? fields[0].key : undefined\n\n // Per-group SortableContexts scope drag-and-drop to within-group ordering.\n // The DragOverlay below renders a stable preview that\n // matches the original row's footprint \u2014 without it, dnd-kit applies\n // `transform` to the source row in place, which the wrapping flex layout\n // distorted into a \"tall\" / morphed ghost.\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),\n useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),\n )\n const [activeDrag, setActiveDrag] = React.useState<{ node: FilterRule | FilterGroup; parent: FilterGroup } | null>(null)\n const findNode = React.useCallback((id: string) => locateInTree(value.root, id), [value])\n\n const handleDragStart = React.useCallback((e: DragStartEvent) => {\n const found = findNode(String(e.active.id))\n if (found) setActiveDrag(found)\n }, [findNode])\n\n const handleDragEnd = React.useCallback((e: DragEndEvent) => {\n setActiveDrag(null)\n const { active, over } = e\n if (!over || active.id === over.id) return\n const from = findNode(String(active.id))\n const to = findNode(String(over.id))\n if (!from || !to) return\n if (from.parent.id !== to.parent.id) return\n const fromIdx = from.parent.children.findIndex((c) => c.id === active.id)\n const toIdx = to.parent.children.findIndex((c) => c.id === over.id)\n if (fromIdx < 0 || toIdx < 0) return\n dispatch({ type: 'reorderChildren', groupId: from.parent.id, fromIdx, toIdx })\n }, [dispatch, findNode])\n\n const handleDragCancel = React.useCallback(() => setActiveDrag(null), [])\n\n // Wrapper has no `min-w-[640px]`: it forced horizontal scroll on a 375px viewport.\n // Rules wrap onto multiple lines on mobile; on desktop (`sm:max-w-[720px]`) they fit\n // on one line.\n return (\n <DndContext\n sensors={sensors}\n collisionDetection={closestCenter}\n onDragStart={handleDragStart}\n onDragEnd={handleDragEnd}\n onDragCancel={handleDragCancel}\n >\n <div className=\"flex flex-col gap-3 p-3\">\n {!empty ? (\n <GroupView\n group={value.root}\n level={1}\n fields={fields}\n tree={value}\n dispatch={dispatch}\n defaultField={defaultField}\n errorByRuleId={errorByRuleId}\n t={t}\n />\n ) : null}\n {!empty ? (\n <div className=\"flex items-center gap-3 pt-2 border-t border-border\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-auto px-0 py-0 text-base text-muted-foreground hover:bg-transparent hover:text-foreground\"\n onClick={onClear}\n >\n <X className=\"size-4\" />\n {t('ui.advancedFilter.clearAll', 'Clear all')}\n </Button>\n </div>\n ) : null}\n </div>\n {/* Portal DragOverlay to document.body. Radix DialogContent uses CSS `transform`\n for centering, which becomes the containing block for `position: fixed`\n descendants (dnd-kit's overlay) and shifts the ghost off-screen. Portaling\n restores viewport-relative coordinates. */}\n {typeof document !== 'undefined'\n ? createPortal(\n // dnd-kit's DragOverlay sets its own inline z-index, so a Tailwind\n // utility class would be overridden \u2014 read the DS token via CSS var.\n <DragOverlay dropAnimation={null} style={{ zIndex: 'var(--z-index-modal-elevated, 55)' }}>\n {activeDrag ? (\n <DragGhost node={activeDrag.node} parent={activeDrag.parent} fields={fields} t={t} />\n ) : null}\n </DragOverlay>,\n document.body,\n )\n : null}\n </DndContext>\n )\n}\n\n/** Find a node in the tree along with its parent group. Used by drag handlers. */\nfunction locateInTree(root: FilterGroup, id: string): { node: FilterRule | FilterGroup; parent: FilterGroup } | null {\n for (const child of root.children) {\n if (child.id === id) return { node: child, parent: root }\n if (child.type === 'group') {\n const found = locateInTree(child, id)\n if (found) return found\n }\n }\n return null\n}\n\n// Compact drag preview rendered inside DragOverlay. Cannot reuse RuleRow/GroupView \u2014\n// their popovers and form inputs conflict with the overlay.\nfunction DragGhost({\n node, parent, fields, t,\n}: {\n node: FilterRule | FilterGroup\n parent: FilterGroup\n fields: FilterFieldDef[]\n t: Translator\n}) {\n const isGroup = node.type === 'group'\n const labelParts: string[] = []\n if (isGroup) {\n const ruleCount = countRulesIn(node)\n labelParts.push(t('ui.advancedFilter.dragGhost.group', 'Group'))\n labelParts.push(`(${ruleCount})`)\n } else {\n const def = fields.find((f) => f.key === node.field)\n labelParts.push(def?.label ?? (node.field || t('ui.advancedFilter.dragGhost.unsetField', 'Unset field')))\n labelParts.push(OPERATOR_LABELS[node.operator] ?? node.operator)\n if (typeof node.value === 'string' && node.value) labelParts.push(`\"${node.value}\"`)\n }\n return (\n <div className=\"inline-flex items-center gap-2 rounded-md border border-border bg-popover px-3 py-1.5 text-sm shadow-md cursor-grabbing\">\n <GripVertical className=\"size-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">{parent.combinator === 'and' ? t('ui.advancedFilter.connector.and', 'and') : t('ui.advancedFilter.connector.or', 'or')}</span>\n <span className=\"font-medium\">{labelParts.join(' ')}</span>\n </div>\n )\n}\n\nfunction countRulesIn(group: FilterGroup): number {\n let n = 0\n for (const c of group.children) {\n if (c.type === 'rule') n += 1\n else n += countRulesIn(c)\n }\n return n\n}\n\nfunction GroupView({\n group, level, fields, tree, dispatch, defaultField, errorByRuleId, t,\n}: {\n group: FilterGroup\n level: number\n fields: FilterFieldDef[]\n tree: AdvancedFilterTree\n dispatch: (a: TreeAction) => void\n defaultField?: string\n errorByRuleId: Map<string, string>\n t: Translator\n}) {\n const isRoot = level === 1\n // Sub-groups render as a card so the nesting boundary is visible; root group stays\n // flush with the popover so it doesn't double-up the dialog's own card chrome.\n const containerClass = isRoot\n ? 'space-y-3'\n : 'space-y-3 rounded-lg border border-border bg-muted/30 p-3'\n\n // Toolbar-first layout (Kendo React Filter pattern): one [And]/[Or] toggle per group\n // is the only combinator UI. Mixing AND with OR requires `Add subgroup`. Matches\n // `CompositeFilterDescriptor` semantics (one logic per group).\n return (\n <div className={containerClass}>\n <GroupToolbar\n group={group}\n level={level}\n tree={tree}\n dispatch={dispatch}\n defaultField={defaultField}\n fields={fields}\n isRoot={isRoot}\n t={t}\n />\n\n <SortableChildrenList\n group={group}\n level={level}\n fields={fields}\n tree={tree}\n dispatch={dispatch}\n defaultField={defaultField}\n errorByRuleId={errorByRuleId}\n t={t}\n />\n </div>\n )\n}\n\n// Kendo-style segmented `[And] [Or]` toggle. Clicking the unselected side flips\n// the group's combinator. Selected side is brand-violet filled so the affordance\n// is unambiguous.\nfunction AndOrToggle({\n combinator, onChange, t,\n}: {\n combinator: FilterCombinator\n onChange: (c: FilterCombinator) => void\n t: Translator\n}) {\n const andLabel = t('ui.advancedFilter.combinator.and', 'And')\n const orLabel = t('ui.advancedFilter.combinator.or', 'Or')\n const baseBtn = 'h-8 px-3 text-sm font-medium outline-none focus-visible:shadow-focus disabled:cursor-not-allowed'\n const selBtn = 'bg-brand-violet text-white hover:bg-brand-violet/90'\n const unselBtn = 'bg-background text-foreground hover:bg-accent'\n return (\n <div\n role=\"group\"\n aria-label={t('ui.advancedFilter.combinator.label', 'Group combinator')}\n className=\"inline-flex rounded-md border border-input overflow-hidden shrink-0\"\n data-testid=\"advanced-filter-combinator-toggle\"\n data-combinator={combinator}\n >\n <button\n type=\"button\"\n aria-pressed={combinator === 'and'}\n onClick={() => combinator !== 'and' && onChange('and')}\n className={`${baseBtn} ${combinator === 'and' ? selBtn : unselBtn}`}\n >\n {andLabel}\n </button>\n <button\n type=\"button\"\n aria-pressed={combinator === 'or'}\n onClick={() => combinator !== 'or' && onChange('or')}\n className={`${baseBtn} border-l border-input ${combinator === 'or' ? selBtn : unselBtn}`}\n >\n {orLabel}\n </button>\n </div>\n )\n}\n\nfunction SortableChildrenList({\n group, level, fields, tree, dispatch, defaultField, errorByRuleId, t,\n}: {\n group: FilterGroup\n level: number\n fields: FilterFieldDef[]\n tree: AdvancedFilterTree\n dispatch: (a: TreeAction) => void\n defaultField?: string\n errorByRuleId: Map<string, string>\n t: Translator\n}) {\n // DndContext + handlers live one level up so a drag can move children across\n // groups. Each group owns its own SortableContext to scope sort ordering.\n return (\n <SortableContext items={group.children.map((c) => c.id)} strategy={verticalListSortingStrategy}>\n {group.children.map((child, idx) => (\n <SortableRow\n key={child.id}\n child={child}\n idx={idx}\n parent={group}\n level={level}\n fields={fields}\n tree={tree}\n dispatch={dispatch}\n defaultField={defaultField}\n errorByRuleId={errorByRuleId}\n t={t}\n />\n ))}\n </SortableContext>\n )\n}\n\nfunction SortableRow({\n child, idx, parent, level, fields, tree, dispatch, defaultField, errorByRuleId, t,\n}: {\n child: FilterRule | FilterGroup\n idx: number\n parent: FilterGroup\n level: number\n fields: FilterFieldDef[]\n tree: AdvancedFilterTree\n dispatch: (a: TreeAction) => void\n defaultField?: string\n errorByRuleId: Map<string, string>\n t: Translator\n}) {\n const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({\n id: child.id,\n })\n // Hide the source while dragging (DragOverlay renders the snapshot); keep it in\n // layout via `visibility: hidden` so siblings don't jump. Without this, the flex\n // wrapper applies both `transform` and its own resizing to the source row.\n const style: React.CSSProperties = isDragging\n ? { visibility: 'hidden' as const }\n : { transform: CSS.Transform.toString(transform), transition }\n const dragHandleProps: DragHandleProps = { ...attributes, ...listeners }\n\n return (\n <div ref={setNodeRef} style={style} data-testid=\"filter-rule-row\" data-filter-node-id={child.id}>\n {child.type === 'rule' ? (\n <RuleRow\n rule={child}\n fields={fields}\n dispatch={dispatch}\n errorByRuleId={errorByRuleId}\n dragHandleProps={dragHandleProps}\n t={t}\n />\n ) : (\n <div className=\"flex items-start gap-2\">\n {/* Raw <button> required: dnd-kit spreads `attributes`+`listeners` onto the\n activator; <Button>'s own props/ref handling breaks the keyboard sensor. */}\n <button\n type=\"button\"\n {...dragHandleProps}\n data-testid=\"filter-drag-handle\"\n className=\"mt-1 text-muted-foreground/60 hover:text-muted-foreground cursor-grab outline-none focus-visible:shadow-focus rounded-sm\"\n aria-label={t('ui.advancedFilter.dragHandle', 'Drag to reorder')}\n >\n <GripVertical className=\"size-4\" />\n </button>\n <div className=\"flex-1\">\n <GroupView\n group={child}\n level={level + 1}\n fields={fields}\n tree={tree}\n dispatch={dispatch}\n defaultField={defaultField}\n errorByRuleId={errorByRuleId}\n t={t}\n />\n </div>\n </div>\n )}\n </div>\n )\n}\n\nfunction RuleRow({\n rule, fields, dispatch, errorByRuleId, dragHandleProps, t,\n}: {\n rule: FilterRule\n fields: FilterFieldDef[]\n dispatch: (a: TreeAction) => void\n errorByRuleId: Map<string, string>\n dragHandleProps: DragHandleProps\n t: Translator\n}) {\n const fieldType = getFieldType(fields, rule.field)\n const operators = OPERATORS_BY_FIELD_TYPE[fieldType] ?? OPERATORS_BY_FIELD_TYPE.text\n const valueless = isValuelessOperator(rule.operator)\n const errorMsg = errorByRuleId.get(rule.id)\n const fieldDef = fields.find((f) => f.key === rule.field)\n const [pickerOpen, setPickerOpen] = React.useState(false)\n const fieldBtnRef = React.useRef<HTMLButtonElement>(null)\n\n return (\n <div className=\"flex flex-col gap-1\" data-testid=\"filter-rule\" data-filter-rule-id={rule.id}>\n <div className=\"flex flex-wrap items-start gap-2\">\n {/* Raw <button> required for dnd-kit activator: see SortableRow drag-handle comment. */}\n <button\n type=\"button\"\n {...dragHandleProps}\n data-testid=\"filter-drag-handle\"\n className=\"mt-1 text-muted-foreground/60 hover:text-muted-foreground cursor-grab outline-none focus-visible:shadow-focus rounded-sm\"\n aria-label={t('ui.advancedFilter.dragHandle', 'Drag to reorder')}\n >\n <GripVertical className=\"size-4\" />\n </button>\n <Button\n ref={fieldBtnRef}\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setPickerOpen(true)}\n className=\"min-w-[140px] justify-start\"\n >\n {fieldDef?.label ?? t('ui.advancedFilter.selectField', 'Select field')}\n </Button>\n <FilterFieldPicker\n fields={fields}\n open={pickerOpen}\n onOpenChange={setPickerOpen}\n onSelect={(f) => {\n const nextType = getFieldType(fields, f.key)\n dispatch({\n type: 'updateRule',\n ruleId: rule.id,\n updates: { field: f.key, operator: getDefaultOperator(nextType), value: '' },\n })\n }}\n triggerRef={fieldBtnRef as React.RefObject<HTMLElement | null>}\n />\n <Select\n value={rule.operator}\n onValueChange={(next) => {\n // Preserve the value when the operator shape doesn't change; reset\n // only when shape changes (e.g. `is` \u2192 `between` clears scalar to pair).\n const nextOp = next as FilterOperator\n const sameShape = getOperatorShape(rule.operator) === getOperatorShape(nextOp)\n const updates: Partial<Pick<FilterRule, 'operator' | 'value'>> = sameShape\n ? { operator: nextOp }\n : { operator: nextOp, value: getOperatorShape(nextOp) === 'multi' || getOperatorShape(nextOp) === 'range' ? [] : '' }\n dispatch({ type: 'updateRule', ruleId: rule.id, updates })\n }}\n >\n {/* `w-auto` overrides SelectTrigger's default `w-full` so it sits\n inline with the field/value inputs instead of breaking onto a new row. */}\n <SelectTrigger\n size=\"sm\"\n className=\"w-auto min-w-[140px]\"\n aria-label={t('ui.advancedFilter.selectOperator', 'Select operator')}\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent data-advanced-filter-portal=\"\">\n {operators.map((op) => (\n <SelectItem key={op} value={op}>\n {t(`ui.advancedFilter.operator.${op}`, OPERATOR_LABELS[op])}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {!valueless ? (\n <ValueInput\n rule={rule}\n fields={fields}\n fieldType={fieldType}\n dispatch={dispatch}\n hasError={!!errorMsg}\n t={t}\n />\n ) : null}\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n onClick={() => dispatch({ type: 'removeNode', nodeId: rule.id })}\n aria-label={t('ui.advancedFilter.removeCondition', 'Remove condition')}\n title={t('ui.advancedFilter.removeCondition', 'Remove condition')}\n >\n <Trash2 className=\"size-4 text-muted-foreground\" />\n </IconButton>\n </div>\n {errorMsg ? (\n <div className=\"ml-6 pl-12 text-xs text-status-error-text\">{errorMsg}</div>\n ) : null}\n </div>\n )\n}\n\nfunction ValueInput({\n rule, fields, fieldType, dispatch, hasError, t,\n}: {\n rule: FilterRule\n fields: FilterFieldDef[]\n fieldType: FilterFieldType\n dispatch: (a: TreeAction) => void\n hasError: boolean\n t: Translator\n}) {\n const fieldDef = fields.find((f) => f.key === rule.field)\n const value = rule.value\n const errorClass = hasError\n ? 'border-status-error-border ring-1 ring-status-error-border focus-visible:border-status-error-border focus-within:border-status-error-border'\n : ''\n\n if (fieldType === 'select' && fieldDef?.options) {\n // Multi-value operators (`is_any_of`, `is_none_of`, `has_any_of`, etc.)\n // need an array value with multiple selectable options. Radix `Select` is\n // single-select only, so the previous code silently overwrote each\n // selection (and validation kept failing because the value never became an\n // array). Render a checkbox-list popover when the operator is multi-shape.\n if (getOperatorShape(rule.operator) === 'multi') {\n return (\n <MultiValuePicker\n rule={rule}\n options={fieldDef.options}\n dispatch={dispatch}\n errorClass={errorClass}\n t={t}\n />\n )\n }\n return (\n <Select\n value={typeof value === 'string' && value.length ? value : undefined}\n onValueChange={(next) =>\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value: next ?? '' } })\n }\n >\n <SelectTrigger\n size=\"sm\"\n className={`w-auto min-w-[160px] ${errorClass}`}\n aria-label={t('ui.advancedFilter.selectValue', 'Select value')}\n >\n <SelectValue placeholder={t('ui.advancedFilter.selectValuePlaceholder', 'Select...')} />\n </SelectTrigger>\n <SelectContent data-advanced-filter-portal=\"\">\n {fieldDef.options.map((opt) => (\n <SelectItem key={opt.value} value={opt.value}>\n {opt.tone ? (\n <span className=\"inline-flex items-center gap-2\">\n <span className={toneDotClass(opt.tone)} aria-hidden=\"true\" />\n {opt.label}\n </span>\n ) : (\n opt.label\n )}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )\n }\n\n // `Input` ships `w-full` by default \u2014 use `w-auto` so inputs don't stretch\n // inside the wrapping flex row. `between` needs a `[start, end]` pair, so it\n // gets its own component below.\n if (rule.operator === 'between' && (fieldType === 'number' || fieldType === 'date')) {\n return (\n <BetweenInput\n rule={rule}\n fieldType={fieldType}\n dispatch={dispatch}\n errorClass={errorClass}\n t={t}\n />\n )\n }\n\n if (fieldType === 'date') {\n return (\n <Input\n type=\"date\"\n size=\"sm\"\n className={`w-auto min-w-[160px] ${errorClass}`}\n value={typeof value === 'string' ? value : ''}\n onChange={(e) =>\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value: e.target.value } })\n }\n aria-label={t('ui.advancedFilter.dateValue', 'Date value')}\n />\n )\n }\n\n if (fieldType === 'number') {\n return (\n <Input\n type=\"number\"\n size=\"sm\"\n className={`w-[120px] ${errorClass}`}\n value={typeof value === 'number' ? value : typeof value === 'string' ? value : ''}\n onChange={(e) =>\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value: e.target.value } })\n }\n placeholder={t('ui.advancedFilter.numberPlaceholder', 'Value')}\n aria-label={t('ui.advancedFilter.numberValue', 'Number value')}\n />\n )\n }\n\n return (\n <Input\n type=\"text\"\n size=\"sm\"\n className={`w-auto min-w-[160px] ${errorClass}`}\n value={typeof value === 'string' ? value : ''}\n onChange={(e) =>\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value: e.target.value } })\n }\n placeholder={t('ui.advancedFilter.textPlaceholder', 'Value...')}\n aria-label={t('ui.advancedFilter.textValue', 'Text value')}\n />\n )\n}\n\n/**\n * Two inputs (start / end) for the `between` operator on date and number\n * fields. Stores the value as `[start, end]`; either side can be empty while\n * the user types, but validation requires both to be set before apply.\n */\nfunction BetweenInput({\n rule, fieldType, dispatch, errorClass, t,\n}: {\n rule: FilterRule\n fieldType: FilterFieldType\n dispatch: (a: TreeAction) => void\n errorClass: string\n t: Translator\n}) {\n const pair = Array.isArray(rule.value) ? rule.value : []\n const start = typeof pair[0] === 'string' || typeof pair[0] === 'number' ? String(pair[0] ?? '') : ''\n const end = typeof pair[1] === 'string' || typeof pair[1] === 'number' ? String(pair[1] ?? '') : ''\n const update = (which: 'start' | 'end', next: string) => {\n const value = which === 'start' ? [next, end] : [start, next]\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value } })\n }\n const inputType = fieldType === 'date' ? 'date' : 'number'\n const inputWidth = fieldType === 'date' ? 'w-auto min-w-[140px]' : 'w-[110px]'\n return (\n <div className=\"inline-flex items-center gap-1\">\n <Input\n type={inputType}\n size=\"sm\"\n className={`${inputWidth} ${errorClass}`}\n value={start}\n onChange={(e) => update('start', e.target.value)}\n placeholder={fieldType === 'number' ? t('ui.advancedFilter.numberPlaceholder', 'Value') : undefined}\n aria-label={t('ui.advancedFilter.betweenStart', 'Start')}\n />\n <span className=\"text-xs text-muted-foreground\">{t('ui.advancedFilter.and', 'and')}</span>\n <Input\n type={inputType}\n size=\"sm\"\n className={`${inputWidth} ${errorClass}`}\n value={end}\n onChange={(e) => update('end', e.target.value)}\n placeholder={fieldType === 'number' ? t('ui.advancedFilter.numberPlaceholder', 'Value') : undefined}\n aria-label={t('ui.advancedFilter.betweenEnd', 'End')}\n />\n </div>\n )\n}\n\n/**\n * Trigger + popover with a checkbox list. Used when a select-typed rule has a\n * multi-shape operator (`is_any_of`, `has_all_of`, etc.). The trigger displays\n * a \"{first} +N\" summary; the popover lets the user toggle each option.\n */\nfunction MultiValuePicker({\n rule, options, dispatch, errorClass, t,\n}: {\n rule: FilterRule\n options: NonNullable<FilterFieldDef['options']>\n dispatch: (a: TreeAction) => void\n errorClass: string\n t: Translator\n}) {\n const selected: string[] = Array.isArray(rule.value)\n ? rule.value.map((v) => String(v)).filter((v) => v.length > 0)\n : []\n const selectedLabel = (() => {\n if (selected.length === 0) return t('ui.advancedFilter.selectValuePlaceholder', 'Select...')\n const firstOpt = options.find((o) => o.value === selected[0])\n const head = firstOpt?.label ?? selected[0]\n const more = selected.length - 1\n return more > 0 ? `${head} +${more}` : head\n })()\n const toggle = (val: string) => {\n const next = selected.includes(val) ? selected.filter((v) => v !== val) : [...selected, val]\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value: next } })\n }\n return (\n <Popover>\n <PopoverTrigger asChild>\n {/* Visually mirrors `SelectTrigger size=\"sm\"` (h-8, rounded-md, border)\n so the operator + value pair stays visually consistent. */}\n <button\n type=\"button\"\n className={`inline-flex h-8 items-center justify-between gap-2 rounded-md border border-input bg-background px-2.5 text-xs shadow-xs transition-colors hover:bg-muted/40 focus-visible:outline-none focus-visible:shadow-focus focus-visible:border-foreground w-auto min-w-[160px] ${errorClass}`}\n aria-label={t('ui.advancedFilter.selectValue', 'Select value')}\n data-testid=\"multi-value-trigger\"\n >\n <span className={selected.length === 0 ? 'text-muted-foreground' : ''}>{selectedLabel}</span>\n <ChevronDown className=\"size-3 text-muted-foreground\" />\n </button>\n </PopoverTrigger>\n <PopoverContent className=\"w-64 p-1\" align=\"start\" data-advanced-filter-portal=\"\">\n <div className=\"max-h-64 overflow-y-auto\" role=\"listbox\" aria-multiselectable=\"true\">\n {options.map((opt) => {\n const isOn = selected.includes(opt.value)\n return (\n // Raw <button>: needs `role=\"option\"` + `aria-selected`; <Button> would override role.\n <button\n key={opt.value}\n type=\"button\"\n role=\"option\"\n aria-selected={isOn}\n onClick={() => toggle(opt.value)}\n className=\"flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm hover:bg-accent focus-visible:bg-accent focus-visible:outline-none\"\n >\n <Checkbox checked={isOn} aria-hidden=\"true\" tabIndex={-1} className=\"pointer-events-none\" />\n {opt.tone ? (\n <span className=\"inline-flex items-center gap-2\">\n <span className={toneDotClass(opt.tone)} aria-hidden=\"true\" />\n {opt.label}\n </span>\n ) : (\n <span>{opt.label}</span>\n )}\n </button>\n )\n })}\n </div>\n </PopoverContent>\n </Popover>\n )\n}\n\nfunction toneDotClass(tone: NonNullable<FilterFieldDef['options']>[number]['tone']): string {\n // Solid filled dot using the saturated `*-icon` token (matches Tag's `dotColorMap`).\n const base = 'inline-block size-2 rounded-full shrink-0'\n switch (tone) {\n case 'success': return `${base} bg-status-success-icon`\n case 'error': return `${base} bg-status-error-icon`\n case 'warning': return `${base} bg-status-warning-icon`\n case 'info': return `${base} bg-status-info-icon`\n case 'neutral': return `${base} bg-status-neutral-icon`\n case 'pink': return `${base} bg-status-pink-icon`\n case 'brand': return `${base} bg-brand-violet`\n default: return `${base} bg-muted-foreground/40`\n }\n}\n\n// Single Kendo-style toolbar per group: combinator toggle, `Add condition`,\n// `Add subgroup` / `Add group`, plus a trailing delete for non-root groups.\nfunction GroupToolbar({\n group, level, tree, dispatch, defaultField, fields, isRoot, t,\n}: {\n group: FilterGroup\n level: number\n tree: AdvancedFilterTree\n dispatch: (a: TreeAction) => void\n defaultField?: string\n fields: FilterFieldDef[]\n isRoot: boolean\n t: Translator\n}) {\n const [pickerOpen, setPickerOpen] = React.useState(false)\n const addBtnRef = React.useRef<HTMLButtonElement>(null)\n const addRuleEnabled = canAddRule(tree, group.id)\n const addGroupEnabled = canAddGroup(tree, group.id)\n\n const widthCapHit = group.children.length >= TREE_LIMITS.maxChildrenPerGroup\n const depthCapHit = level >= TREE_LIMITS.maxGroupLevel\n\n const addRuleTitle = !addRuleEnabled\n ? widthCapHit\n ? t('ui.advancedFilter.limitWidthReached', 'Maximum {max} conditions per group', {\n max: TREE_LIMITS.maxChildrenPerGroup,\n })\n : t('ui.advancedFilter.limitTotalReached', 'Maximum {max} conditions reached', {\n max: TREE_LIMITS.maxTotalRules,\n })\n : undefined\n\n const addGroupTitle = !addGroupEnabled\n ? depthCapHit\n ? t('ui.advancedFilter.limitDepthReached', 'Maximum nesting depth reached')\n : widthCapHit\n ? t('ui.advancedFilter.limitWidthReached', 'Maximum {max} conditions per group', {\n max: TREE_LIMITS.maxChildrenPerGroup,\n })\n : t('ui.advancedFilter.limitTotalReached', 'Maximum {max} conditions reached', {\n max: TREE_LIMITS.maxTotalRules,\n })\n : undefined\n\n const addGroupLabel = level === 1\n ? t('ui.advancedFilter.addGroup', 'Add group')\n : t('ui.advancedFilter.addSubgroup', 'Add subgroup')\n\n // `text-brand-violet` overrides Button's default disabled token, so we have to\n // re-state muted disabled colors here \u2014 otherwise disabled CTAs still look clickable.\n const ctaClass = 'text-brand-violet hover:text-brand-violet/80 hover:bg-transparent px-0 disabled:text-muted-foreground/50 disabled:hover:text-muted-foreground/50'\n\n return (\n <div className=\"flex flex-col gap-1\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <AndOrToggle\n combinator={group.combinator}\n onChange={(c) => dispatch({ type: 'updateGroupCombinator', groupId: group.id, combinator: c })}\n t={t}\n />\n <Button\n ref={addBtnRef}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n disabled={!addRuleEnabled}\n title={addRuleTitle}\n onClick={() => setPickerOpen(true)}\n className={ctaClass}\n >\n <Plus className=\"size-4\" />\n {t('ui.advancedFilter.addCondition', 'Add condition')}\n </Button>\n <FilterFieldPicker\n fields={fields}\n open={pickerOpen}\n onOpenChange={setPickerOpen}\n onSelect={(f) =>\n dispatch({\n type: 'addRule',\n groupId: group.id,\n defaultField: f.key,\n defaultOperator: getDefaultOperator(f.type),\n })\n }\n triggerRef={addBtnRef as React.RefObject<HTMLElement | null>}\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n disabled={!addGroupEnabled}\n title={addGroupTitle}\n onClick={() =>\n dispatch({ type: 'addGroup', groupId: group.id, defaultField })\n }\n className={ctaClass}\n >\n <Plus className=\"size-4\" />\n {addGroupLabel}\n </Button>\n {!isRoot ? (\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n onClick={() => dispatch({ type: 'removeNode', nodeId: group.id })}\n aria-label={t('ui.advancedFilter.deleteGroup', 'Delete group')}\n title={t('ui.advancedFilter.deleteGroup', 'Delete group')}\n className=\"ml-auto\"\n >\n <X className=\"size-4 text-muted-foreground\" />\n </IconButton>\n ) : null}\n </div>\n {!addGroupEnabled && depthCapHit ? (\n <p className=\"text-xs text-muted-foreground\" data-testid=\"filter-depth-limit-hint\">\n {t('ui.advancedFilter.limitDepthHint', 'You\\'ve reached the maximum nesting depth ({max} levels). Switch this group\\'s combinator with the toggle above instead of nesting deeper.', {\n max: TREE_LIMITS.maxGroupLevel,\n })}\n </p>\n ) : null}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -13,8 +13,8 @@ function ComponentOverrideProvider({
|
|
|
13
13
|
children
|
|
14
14
|
}) {
|
|
15
15
|
const [userFeatures, setUserFeatures] = React.useState([]);
|
|
16
|
+
registerComponentOverrides(overrides);
|
|
16
17
|
React.useEffect(() => {
|
|
17
|
-
registerComponentOverrides(overrides);
|
|
18
18
|
return () => {
|
|
19
19
|
registerComponentOverrides([]);
|
|
20
20
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/injection/ComponentOverrideProvider.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { createContext, useContext } from 'react'\nimport type { ComponentOverride } from '@open-mercato/shared/modules/widgets/component-registry'\nimport { registerComponentOverrides } from '@open-mercato/shared/modules/widgets/component-registry'\nimport { apiCall } from '../utils/apiCall'\n\ntype FeatureCheckResponse = { granted: string[] }\n\nconst OverrideUserFeaturesContext = createContext<readonly string[]>([])\n\nexport function useOverrideUserFeatures(): readonly string[] {\n return useContext(OverrideUserFeaturesContext)\n}\n\nexport function ComponentOverrideProvider({\n overrides,\n children,\n}: {\n overrides: ComponentOverride[]\n children: React.ReactNode\n}) {\n const [userFeatures, setUserFeatures] = React.useState<readonly string[]>([])\n\n React.useEffect(() => {\n
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { createContext, useContext } from 'react'\nimport type { ComponentOverride } from '@open-mercato/shared/modules/widgets/component-registry'\nimport { registerComponentOverrides } from '@open-mercato/shared/modules/widgets/component-registry'\nimport { apiCall } from '../utils/apiCall'\n\ntype FeatureCheckResponse = { granted: string[] }\n\nconst OverrideUserFeaturesContext = createContext<readonly string[]>([])\n\nexport function useOverrideUserFeatures(): readonly string[] {\n return useContext(OverrideUserFeaturesContext)\n}\n\nexport function ComponentOverrideProvider({\n overrides,\n children,\n}: {\n overrides: ComponentOverride[]\n children: React.ReactNode\n}) {\n const [userFeatures, setUserFeatures] = React.useState<readonly string[]>([])\n\n registerComponentOverrides(overrides)\n\n React.useEffect(() => {\n return () => {\n registerComponentOverrides([])\n }\n }, [overrides])\n\n React.useEffect(() => {\n const requiredFeatures = new Set<string>()\n for (const override of overrides) {\n for (const feature of override.features ?? []) {\n if (feature && feature.trim().length > 0) requiredFeatures.add(feature)\n }\n }\n if (requiredFeatures.size === 0) return\n let cancelled = false\n const fetchFeatures = async () => {\n const call = await apiCall<FeatureCheckResponse>('/api/auth/feature-check', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ features: Array.from(requiredFeatures) }),\n })\n if (cancelled) return\n if (call.ok && call.result?.granted) {\n setUserFeatures(call.result.granted)\n }\n }\n void fetchFeatures()\n return () => {\n cancelled = true\n }\n }, [overrides])\n\n return (\n <OverrideUserFeaturesContext.Provider value={userFeatures}>\n {children}\n </OverrideUserFeaturesContext.Provider>\n )\n}\n\nexport default ComponentOverrideProvider\n"],
|
|
5
|
+
"mappings": ";AA4DI;AA1DJ,YAAY,WAAW;AACvB,SAAS,eAAe,kBAAkB;AAE1C,SAAS,kCAAkC;AAC3C,SAAS,eAAe;AAIxB,MAAM,8BAA8B,cAAiC,CAAC,CAAC;AAEhE,SAAS,0BAA6C;AAC3D,SAAO,WAAW,2BAA2B;AAC/C;AAEO,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA;AACF,GAGG;AACD,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAA4B,CAAC,CAAC;AAE5E,6BAA2B,SAAS;AAEpC,QAAM,UAAU,MAAM;AACpB,WAAO,MAAM;AACX,iCAA2B,CAAC,CAAC;AAAA,IAC/B;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAEd,QAAM,UAAU,MAAM;AACpB,UAAM,mBAAmB,oBAAI,IAAY;AACzC,eAAW,YAAY,WAAW;AAChC,iBAAW,WAAW,SAAS,YAAY,CAAC,GAAG;AAC7C,YAAI,WAAW,QAAQ,KAAK,EAAE,SAAS,EAAG,kBAAiB,IAAI,OAAO;AAAA,MACxE;AAAA,IACF;AACA,QAAI,iBAAiB,SAAS,EAAG;AACjC,QAAI,YAAY;AAChB,UAAM,gBAAgB,YAAY;AAChC,YAAM,OAAO,MAAM,QAA8B,2BAA2B;AAAA,QAC1E,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,UAAU,MAAM,KAAK,gBAAgB,EAAE,CAAC;AAAA,MACjE,CAAC;AACD,UAAI,UAAW;AACf,UAAI,KAAK,MAAM,KAAK,QAAQ,SAAS;AACnC,wBAAgB,KAAK,OAAO,OAAO;AAAA,MACrC;AAAA,IACF;AACA,SAAK,cAAc;AACnB,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAEd,SACE,oBAAC,4BAA4B,UAA5B,EAAqC,OAAO,cAC1C,UACH;AAEJ;AAEA,IAAO,oCAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -20,6 +20,13 @@ function normalizeOptions(input) {
|
|
|
20
20
|
};
|
|
21
21
|
}).filter((option) => !!option);
|
|
22
22
|
}
|
|
23
|
+
function areOptionsEqual(a, b) {
|
|
24
|
+
if (a.length !== b.length) return false;
|
|
25
|
+
return a.every((option, index) => {
|
|
26
|
+
const next = b[index];
|
|
27
|
+
return option.value === next.value && option.label === next.label && (option.description ?? null) === (next.description ?? null);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
23
30
|
function ComboboxInput({
|
|
24
31
|
value,
|
|
25
32
|
onChange,
|
|
@@ -45,6 +52,7 @@ function ComboboxInput({
|
|
|
45
52
|
const [selectedIndex, setSelectedIndex] = React.useState(-1);
|
|
46
53
|
const inputRef = React.useRef(null);
|
|
47
54
|
const suppressOpenOnFocusRef = React.useRef(Boolean(autoFocus && !disabled));
|
|
55
|
+
const eagerFallbackLoadedValueRef = React.useRef(null);
|
|
48
56
|
const staticOptions = React.useMemo(
|
|
49
57
|
() => normalizeOptions([...seedOptions ?? [], ...suggestions ?? []]),
|
|
50
58
|
[seedOptions, suggestions]
|
|
@@ -56,6 +64,13 @@ function ComboboxInput({
|
|
|
56
64
|
}
|
|
57
65
|
return set;
|
|
58
66
|
}, [staticOptions, asyncOptions, resolvedOptions]);
|
|
67
|
+
const coveredOptionValues = React.useMemo(() => {
|
|
68
|
+
const set = /* @__PURE__ */ new Set();
|
|
69
|
+
for (const option of [...staticOptions, ...asyncOptions, ...resolvedOptions]) {
|
|
70
|
+
set.add(option.value);
|
|
71
|
+
}
|
|
72
|
+
return set;
|
|
73
|
+
}, [staticOptions, asyncOptions, resolvedOptions]);
|
|
59
74
|
const optionMap = React.useMemo(() => {
|
|
60
75
|
const map = /* @__PURE__ */ new Map();
|
|
61
76
|
const register = (option) => {
|
|
@@ -70,16 +85,15 @@ function ComboboxInput({
|
|
|
70
85
|
if (value) {
|
|
71
86
|
const existing = map.get(value);
|
|
72
87
|
if (!existing) {
|
|
73
|
-
const sync = typeof resolveLabel === "function" ? resolveLabel(value) : void 0;
|
|
74
88
|
map.set(value, {
|
|
75
89
|
value,
|
|
76
|
-
label:
|
|
90
|
+
label: value,
|
|
77
91
|
description: resolveDescription?.(value) ?? null
|
|
78
92
|
});
|
|
79
93
|
}
|
|
80
94
|
}
|
|
81
95
|
return map;
|
|
82
|
-
}, [asyncOptions, resolvedOptions, resolveDescription,
|
|
96
|
+
}, [asyncOptions, resolvedOptions, resolveDescription, staticOptions, value]);
|
|
83
97
|
const availableOptions = React.useMemo(() => {
|
|
84
98
|
return Array.from(optionMap.values());
|
|
85
99
|
}, [optionMap]);
|
|
@@ -101,7 +115,8 @@ function ComboboxInput({
|
|
|
101
115
|
try {
|
|
102
116
|
const items = await loadSuggestions(query);
|
|
103
117
|
if (!cancelled) {
|
|
104
|
-
|
|
118
|
+
const normalized = normalizeOptions(items);
|
|
119
|
+
setAsyncOptions((prev) => areOptionsEqual(prev, normalized) ? prev : normalized);
|
|
105
120
|
}
|
|
106
121
|
} finally {
|
|
107
122
|
if (!cancelled) setLoading(false);
|
|
@@ -115,7 +130,6 @@ function ComboboxInput({
|
|
|
115
130
|
const eagerResolveLabel = typeof resolveLabel === "function" ? resolveLabel : void 0;
|
|
116
131
|
React.useEffect(() => {
|
|
117
132
|
if (!value || disabled) return;
|
|
118
|
-
if (knownLabelValues.has(value)) return;
|
|
119
133
|
let cancelled = false;
|
|
120
134
|
const apply = (label, description) => {
|
|
121
135
|
const clean = typeof label === "string" ? label.trim() : "";
|
|
@@ -126,18 +140,22 @@ function ComboboxInput({
|
|
|
126
140
|
});
|
|
127
141
|
};
|
|
128
142
|
if (eagerResolveLabel) {
|
|
143
|
+
if (knownLabelValues.has(value)) return;
|
|
129
144
|
Promise.resolve().then(() => eagerResolveLabel(value)).then((label) => apply(label, resolveDescription?.(value))).catch(() => {
|
|
130
145
|
});
|
|
131
146
|
return () => {
|
|
132
147
|
cancelled = true;
|
|
133
148
|
};
|
|
134
149
|
}
|
|
150
|
+
if (coveredOptionValues.has(value)) return;
|
|
151
|
+
if (eagerFallbackLoadedValueRef.current === value) return;
|
|
152
|
+
eagerFallbackLoadedValueRef.current = value;
|
|
135
153
|
if (loadSuggestions) {
|
|
136
154
|
setLoading(true);
|
|
137
155
|
Promise.resolve().then(() => loadSuggestions()).then((items) => {
|
|
138
156
|
if (cancelled) return;
|
|
139
157
|
const normalized = normalizeOptions(items);
|
|
140
|
-
setAsyncOptions(normalized);
|
|
158
|
+
setAsyncOptions((prev) => areOptionsEqual(prev, normalized) ? prev : normalized);
|
|
141
159
|
}).catch(() => {
|
|
142
160
|
}).finally(() => {
|
|
143
161
|
if (!cancelled) setLoading(false);
|
|
@@ -146,7 +164,7 @@ function ComboboxInput({
|
|
|
146
164
|
return () => {
|
|
147
165
|
cancelled = true;
|
|
148
166
|
};
|
|
149
|
-
}, [value, disabled, knownLabelValues, eagerResolveLabel, loadSuggestions]);
|
|
167
|
+
}, [value, disabled, knownLabelValues, coveredOptionValues, eagerResolveLabel, loadSuggestions]);
|
|
150
168
|
React.useEffect(() => {
|
|
151
169
|
if (document.activeElement !== inputRef.current) {
|
|
152
170
|
const option = optionMap.get(value);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/inputs/ComboboxInput.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '../../primitives/button'\n\nexport type ComboboxOption = {\n value: string\n label: string\n description?: string | null\n}\n\nexport type ComboboxInputProps = {\n value: string\n onChange: (next: string) => void\n placeholder?: string\n suggestions?: Array<string | ComboboxOption>\n // Options to hydrate the option map up front (typically the linked entity's\n // display fields, already present in a record-detail payload). Merged with\n // `suggestions` so a pre-selected value renders its label without interaction.\n seedOptions?: ComboboxOption[]\n loadSuggestions?: (query?: string) => Promise<Array<string | ComboboxOption>>\n // Eagerly resolve a pre-selected `value` to a human label when it is not\n // covered by `suggestions`/`seedOptions`/`loadSuggestions` results. Runs once\n // per value, before any user interaction. May be sync or async.\n resolveLabel?: (value: string) => string | Promise<string>\n resolveDescription?: (value: string) => string | null | undefined\n autoFocus?: boolean\n disabled?: boolean\n allowCustomValues?: boolean\n}\n\nfunction normalizeOptions(input?: Array<string | ComboboxOption>): ComboboxOption[] {\n if (!Array.isArray(input)) return []\n return input\n .map((option) => {\n if (typeof option === 'string') {\n const trimmed = option.trim()\n if (!trimmed) return null\n return { value: trimmed, label: trimmed }\n }\n const value = typeof option.value === 'string' ? option.value.trim() : ''\n if (!value) return null\n return {\n value,\n label: option.label?.trim() || value,\n description: option.description ?? null,\n }\n })\n .filter((option): option is ComboboxOption => !!option)\n}\n\nexport function ComboboxInput({\n value,\n onChange,\n placeholder,\n suggestions,\n seedOptions,\n loadSuggestions,\n resolveLabel,\n resolveDescription,\n autoFocus,\n disabled = false,\n allowCustomValues = true,\n}: ComboboxInputProps) {\n const t = useT()\n const resolvedPlaceholder = placeholder ?? t('ui.inputs.comboboxInput.placeholder', 'Type to search...')\n const loadingLabel = t('ui.inputs.comboboxInput.loading', 'Loading suggestions\u2026')\n const [input, setInput] = React.useState('')\n const [asyncOptions, setAsyncOptions] = React.useState<ComboboxOption[]>([])\n const [resolvedOptions, setResolvedOptions] = React.useState<ComboboxOption[]>([])\n const [loading, setLoading] = React.useState(false)\n const [touched, setTouched] = React.useState(false)\n const [showSuggestions, setShowSuggestions] = React.useState(false)\n const [selectedIndex, setSelectedIndex] = React.useState(-1)\n const inputRef = React.useRef<HTMLInputElement>(null)\n const suppressOpenOnFocusRef = React.useRef(Boolean(autoFocus && !disabled))\n\n const staticOptions = React.useMemo(\n () => normalizeOptions([...(seedOptions ?? []), ...(suggestions ?? [])]),\n [seedOptions, suggestions]\n )\n\n // Options whose label is genuinely known (not a self-mapping `value === label`\n // placeholder). Used to decide whether eager resolution still needs to run.\n const knownLabelValues = React.useMemo(() => {\n const set = new Set<string>()\n for (const option of [...staticOptions, ...asyncOptions, ...resolvedOptions]) {\n if (option.label && option.label !== option.value) set.add(option.value)\n }\n return set\n }, [staticOptions, asyncOptions, resolvedOptions])\n\n const optionMap = React.useMemo(() => {\n const map = new Map<string, ComboboxOption>()\n const register = (option: ComboboxOption) => {\n const existing = map.get(option.value)\n // Prefer an entry that carries a real label over a self-mapping placeholder.\n if (!existing || (existing.label === existing.value && option.label !== option.value)) {\n map.set(option.value, option)\n }\n }\n staticOptions.forEach(register)\n asyncOptions.forEach(register)\n resolvedOptions.forEach(register)\n if (value) {\n const existing = map.get(value)\n if (!existing) {\n const sync = typeof resolveLabel === 'function' ? resolveLabel(value) : undefined\n map.set(value, {\n value,\n label: typeof sync === 'string' && sync ? sync : value,\n description: resolveDescription?.(value) ?? null,\n })\n }\n }\n return map\n }, [asyncOptions, resolvedOptions, resolveDescription, resolveLabel, staticOptions, value])\n\n const availableOptions = React.useMemo(() => {\n return Array.from(optionMap.values())\n }, [optionMap])\n\n const filteredSuggestions = React.useMemo(() => {\n const query = input.toLowerCase().trim()\n if (!query) return availableOptions\n return availableOptions.filter((option) => {\n const labelMatch = option.label.toLowerCase().includes(query)\n const descMatch = option.description?.toLowerCase().includes(query)\n return labelMatch || Boolean(descMatch)\n })\n }, [availableOptions, input])\n\n React.useEffect(() => {\n if (!loadSuggestions || !touched || disabled) return\n const query = input.trim()\n let cancelled = false\n const handle = window.setTimeout(async () => {\n setLoading(true)\n try {\n const items = await loadSuggestions(query)\n if (!cancelled) {\n setAsyncOptions(normalizeOptions(items))\n }\n } finally {\n if (!cancelled) setLoading(false)\n }\n }, 200)\n return () => {\n cancelled = true\n window.clearTimeout(handle)\n }\n }, [disabled, input, loadSuggestions, touched])\n\n // Eagerly resolve a pre-selected value to its label without requiring the user\n // to focus the field. Runs once per value when it is not already covered.\n const eagerResolveLabel = typeof resolveLabel === 'function' ? resolveLabel : undefined\n React.useEffect(() => {\n if (!value || disabled) return\n if (knownLabelValues.has(value)) return\n let cancelled = false\n const apply = (label?: string | null, description?: string | null) => {\n const clean = typeof label === 'string' ? label.trim() : ''\n if (cancelled || !clean || clean === value) return\n setResolvedOptions((prev) => {\n if (prev.some((option) => option.value === value && option.label === clean)) return prev\n return [...prev.filter((option) => option.value !== value), { value, label: clean, description: description ?? null }]\n })\n }\n if (eagerResolveLabel) {\n Promise.resolve()\n .then(() => eagerResolveLabel(value))\n .then((label) => apply(label, resolveDescription?.(value)))\n .catch(() => {})\n return () => { cancelled = true }\n }\n // Fallback: pull the first page of async suggestions so a remount that lost\n // its option cache can still recover the label without user interaction.\n if (loadSuggestions) {\n setLoading(true)\n Promise.resolve()\n .then(() => loadSuggestions())\n .then((items) => {\n if (cancelled) return\n const normalized = normalizeOptions(items)\n setAsyncOptions(normalized)\n })\n .catch(() => {})\n .finally(() => { if (!cancelled) setLoading(false) })\n }\n return () => { cancelled = true }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [value, disabled, knownLabelValues, eagerResolveLabel, loadSuggestions])\n\n // Sync input with value when value changes externally and input is not focused.\n React.useEffect(() => {\n if (document.activeElement !== inputRef.current) {\n const option = optionMap.get(value)\n setInput(option?.label ?? value ?? '')\n }\n }, [value, optionMap])\n\n const selectValue = React.useCallback(\n (nextValue: string) => {\n if (disabled) return\n const trimmed = nextValue.trim()\n onChange(trimmed)\n const option = optionMap.get(trimmed)\n setInput(option?.label ?? trimmed)\n setShowSuggestions(false)\n setSelectedIndex(-1)\n },\n [disabled, onChange, optionMap]\n )\n\n const findOptionForInput = React.useCallback(\n (raw: string): ComboboxOption | null => {\n const query = raw.trim().toLowerCase()\n if (!query) return null\n for (const option of optionMap.values()) {\n if (option.value === raw.trim()) return option\n if (option.label.toLowerCase() === query) return option\n }\n return null\n },\n [optionMap]\n )\n\n const confirmSelection = React.useCallback(\n (raw: string) => {\n if (disabled) return\n const option = findOptionForInput(raw)\n if (option) {\n selectValue(option.value)\n return\n }\n if (!allowCustomValues) {\n // Revert to the current value's label \u2014 but only if we actually know it.\n // Baking the raw value back in while eager resolution is still pending\n // would freeze a placeholder (e.g. a UUID) into the visible input.\n setShowSuggestions(false)\n const currentOption = optionMap.get(value)\n if (currentOption && currentOption.label !== currentOption.value) {\n setInput(currentOption.label)\n } else if (!value) {\n setInput('')\n }\n return\n }\n selectValue(raw)\n },\n [allowCustomValues, disabled, findOptionForInput, optionMap, selectValue, value]\n )\n\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLInputElement>) => {\n if (disabled) return\n\n if (event.key === 'ArrowDown') {\n event.preventDefault()\n if (!showSuggestions) {\n setShowSuggestions(true)\n setSelectedIndex(0)\n } else {\n setSelectedIndex((prev) => Math.min(prev + 1, filteredSuggestions.length - 1))\n }\n } else if (event.key === 'ArrowUp') {\n event.preventDefault()\n setSelectedIndex((prev) => Math.max(prev - 1, -1))\n } else if (event.key === 'Enter') {\n event.preventDefault()\n if (selectedIndex >= 0 && filteredSuggestions[selectedIndex]) {\n selectValue(filteredSuggestions[selectedIndex].value)\n } else {\n confirmSelection(input)\n }\n } else if (event.key === 'Escape') {\n event.preventDefault()\n setShowSuggestions(false)\n setSelectedIndex(-1)\n }\n },\n [confirmSelection, disabled, filteredSuggestions, input, selectValue, selectedIndex, showSuggestions]\n )\n\n return (\n <div className=\"relative w-full\">\n {/* Use raw <input> here instead of the DS Input primitive: ComboboxInput's\n focus / suggestions-popup interplay relies on the trigger being a plain\n input element. The DS wrapper introduces a <div> that desyncs autocomplete\n on this specific surface. Keeps the rest of the form on Input primitive. */}\n <input\n ref={inputRef}\n type=\"text\"\n className=\"w-full h-9 rounded-md border border-input bg-background px-3 text-sm shadow-xs transition-colors outline-none placeholder:text-muted-foreground focus-visible:shadow-focus focus-visible:border-foreground disabled:bg-bg-disabled disabled:border-border-disabled disabled:text-muted-foreground disabled:cursor-not-allowed\"\n value={input}\n placeholder={resolvedPlaceholder}\n autoFocus={autoFocus}\n data-crud-focus-target=\"\"\n disabled={disabled}\n onFocus={() => {\n setTouched(true)\n if (suppressOpenOnFocusRef.current) {\n suppressOpenOnFocusRef.current = false\n return\n }\n setShowSuggestions(true)\n }}\n onChange={(event) => {\n setTouched(true)\n setInput(event.target.value)\n setShowSuggestions(true)\n setSelectedIndex(-1)\n }}\n onKeyDown={handleKeyDown}\n onBlur={() => {\n // Delay to allow click on suggestions\n setTimeout(() => {\n if (disabled) return\n confirmSelection(input)\n }, 200)\n }}\n />\n\n {showSuggestions && !disabled && (loading || filteredSuggestions.length > 0) && (\n <div className=\"absolute z-popover w-full mt-1 rounded-md border border-input bg-popover p-2 shadow-md max-h-48 sm:max-h-60 overflow-auto\">\n {loading && touched ? (\n <div className=\"px-2 py-1.5 text-xs text-muted-foreground\">{loadingLabel}</div>\n ) : (\n <div className=\"flex flex-col gap-1\">\n {filteredSuggestions.map((option, index) => (\n <Button\n key={option.value}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className={[\n 'w-full h-auto justify-start font-normal text-left flex flex-col items-start rounded-lg p-2',\n index === selectedIndex ? 'bg-muted' : '',\n ]\n .filter(Boolean)\n .join(' ')}\n onMouseDown={(event) => event.preventDefault()}\n onClick={() => selectValue(option.value)}\n onMouseEnter={() => setSelectedIndex(index)}\n >\n <span className=\"font-medium text-foreground\">{option.label}</span>\n {option.description ? (\n <span className=\"text-xs text-muted-foreground\">{option.description}</span>\n ) : null}\n </Button>\n ))}\n </div>\n )}\n </div>\n )}\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '../../primitives/button'\n\nexport type ComboboxOption = {\n value: string\n label: string\n description?: string | null\n}\n\nexport type ComboboxInputProps = {\n value: string\n onChange: (next: string) => void\n placeholder?: string\n suggestions?: Array<string | ComboboxOption>\n // Options to hydrate the option map up front (typically the linked entity's\n // display fields, already present in a record-detail payload). Merged with\n // `suggestions` so a pre-selected value renders its label without interaction.\n seedOptions?: ComboboxOption[]\n loadSuggestions?: (query?: string) => Promise<Array<string | ComboboxOption>>\n // Eagerly resolve a pre-selected `value` to a human label when it is not\n // covered by `suggestions`/`seedOptions`/`loadSuggestions` results. Runs once\n // per value, before any user interaction. May be sync or async.\n resolveLabel?: (value: string) => string | Promise<string>\n resolveDescription?: (value: string) => string | null | undefined\n autoFocus?: boolean\n disabled?: boolean\n allowCustomValues?: boolean\n}\n\nfunction normalizeOptions(input?: Array<string | ComboboxOption>): ComboboxOption[] {\n if (!Array.isArray(input)) return []\n return input\n .map((option) => {\n if (typeof option === 'string') {\n const trimmed = option.trim()\n if (!trimmed) return null\n return { value: trimmed, label: trimmed }\n }\n const value = typeof option.value === 'string' ? option.value.trim() : ''\n if (!value) return null\n return {\n value,\n label: option.label?.trim() || value,\n description: option.description ?? null,\n }\n })\n .filter((option): option is ComboboxOption => !!option)\n}\n\nfunction areOptionsEqual(a: ComboboxOption[], b: ComboboxOption[]): boolean {\n if (a.length !== b.length) return false\n return a.every((option, index) => {\n const next = b[index]\n return option.value === next.value\n && option.label === next.label\n && (option.description ?? null) === (next.description ?? null)\n })\n}\n\nexport function ComboboxInput({\n value,\n onChange,\n placeholder,\n suggestions,\n seedOptions,\n loadSuggestions,\n resolveLabel,\n resolveDescription,\n autoFocus,\n disabled = false,\n allowCustomValues = true,\n}: ComboboxInputProps) {\n const t = useT()\n const resolvedPlaceholder = placeholder ?? t('ui.inputs.comboboxInput.placeholder', 'Type to search...')\n const loadingLabel = t('ui.inputs.comboboxInput.loading', 'Loading suggestions\u2026')\n const [input, setInput] = React.useState('')\n const [asyncOptions, setAsyncOptions] = React.useState<ComboboxOption[]>([])\n const [resolvedOptions, setResolvedOptions] = React.useState<ComboboxOption[]>([])\n const [loading, setLoading] = React.useState(false)\n const [touched, setTouched] = React.useState(false)\n const [showSuggestions, setShowSuggestions] = React.useState(false)\n const [selectedIndex, setSelectedIndex] = React.useState(-1)\n const inputRef = React.useRef<HTMLInputElement>(null)\n const suppressOpenOnFocusRef = React.useRef(Boolean(autoFocus && !disabled))\n const eagerFallbackLoadedValueRef = React.useRef<string | null>(null)\n\n const staticOptions = React.useMemo(\n () => normalizeOptions([...(seedOptions ?? []), ...(suggestions ?? [])]),\n [seedOptions, suggestions]\n )\n\n // Options whose label is genuinely known (not a self-mapping `value === label`\n // placeholder). Used to decide whether eager resolution still needs to run.\n const knownLabelValues = React.useMemo(() => {\n const set = new Set<string>()\n for (const option of [...staticOptions, ...asyncOptions, ...resolvedOptions]) {\n if (option.label && option.label !== option.value) set.add(option.value)\n }\n return set\n }, [staticOptions, asyncOptions, resolvedOptions])\n\n const coveredOptionValues = React.useMemo(() => {\n const set = new Set<string>()\n for (const option of [...staticOptions, ...asyncOptions, ...resolvedOptions]) {\n set.add(option.value)\n }\n return set\n }, [staticOptions, asyncOptions, resolvedOptions])\n\n const optionMap = React.useMemo(() => {\n const map = new Map<string, ComboboxOption>()\n const register = (option: ComboboxOption) => {\n const existing = map.get(option.value)\n // Prefer an entry that carries a real label over a self-mapping placeholder.\n if (!existing || (existing.label === existing.value && option.label !== option.value)) {\n map.set(option.value, option)\n }\n }\n staticOptions.forEach(register)\n asyncOptions.forEach(register)\n resolvedOptions.forEach(register)\n if (value) {\n const existing = map.get(value)\n if (!existing) {\n map.set(value, {\n value,\n label: value,\n description: resolveDescription?.(value) ?? null,\n })\n }\n }\n return map\n }, [asyncOptions, resolvedOptions, resolveDescription, staticOptions, value])\n\n const availableOptions = React.useMemo(() => {\n return Array.from(optionMap.values())\n }, [optionMap])\n\n const filteredSuggestions = React.useMemo(() => {\n const query = input.toLowerCase().trim()\n if (!query) return availableOptions\n return availableOptions.filter((option) => {\n const labelMatch = option.label.toLowerCase().includes(query)\n const descMatch = option.description?.toLowerCase().includes(query)\n return labelMatch || Boolean(descMatch)\n })\n }, [availableOptions, input])\n\n React.useEffect(() => {\n if (!loadSuggestions || !touched || disabled) return\n const query = input.trim()\n let cancelled = false\n const handle = window.setTimeout(async () => {\n setLoading(true)\n try {\n const items = await loadSuggestions(query)\n if (!cancelled) {\n const normalized = normalizeOptions(items)\n setAsyncOptions((prev) => areOptionsEqual(prev, normalized) ? prev : normalized)\n }\n } finally {\n if (!cancelled) setLoading(false)\n }\n }, 200)\n return () => {\n cancelled = true\n window.clearTimeout(handle)\n }\n }, [disabled, input, loadSuggestions, touched])\n\n // Eagerly resolve a pre-selected value to its label without requiring the user\n // to focus the field. Runs once per value when it is not already covered.\n const eagerResolveLabel = typeof resolveLabel === 'function' ? resolveLabel : undefined\n React.useEffect(() => {\n if (!value || disabled) return\n let cancelled = false\n const apply = (label?: string | null, description?: string | null) => {\n const clean = typeof label === 'string' ? label.trim() : ''\n if (cancelled || !clean || clean === value) return\n setResolvedOptions((prev) => {\n if (prev.some((option) => option.value === value && option.label === clean)) return prev\n return [...prev.filter((option) => option.value !== value), { value, label: clean, description: description ?? null }]\n })\n }\n if (eagerResolveLabel) {\n if (knownLabelValues.has(value)) return\n Promise.resolve()\n .then(() => eagerResolveLabel(value))\n .then((label) => apply(label, resolveDescription?.(value)))\n .catch(() => {})\n return () => { cancelled = true }\n }\n if (coveredOptionValues.has(value)) return\n if (eagerFallbackLoadedValueRef.current === value) return\n eagerFallbackLoadedValueRef.current = value\n // Fallback: pull the first page of async suggestions so a remount that lost\n // its option cache can still recover the label without user interaction.\n if (loadSuggestions) {\n setLoading(true)\n Promise.resolve()\n .then(() => loadSuggestions())\n .then((items) => {\n if (cancelled) return\n const normalized = normalizeOptions(items)\n setAsyncOptions((prev) => areOptionsEqual(prev, normalized) ? prev : normalized)\n })\n .catch(() => {})\n .finally(() => { if (!cancelled) setLoading(false) })\n }\n return () => { cancelled = true }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [value, disabled, knownLabelValues, coveredOptionValues, eagerResolveLabel, loadSuggestions])\n\n // Sync input with value when value changes externally and input is not focused.\n React.useEffect(() => {\n if (document.activeElement !== inputRef.current) {\n const option = optionMap.get(value)\n setInput(option?.label ?? value ?? '')\n }\n }, [value, optionMap])\n\n const selectValue = React.useCallback(\n (nextValue: string) => {\n if (disabled) return\n const trimmed = nextValue.trim()\n onChange(trimmed)\n const option = optionMap.get(trimmed)\n setInput(option?.label ?? trimmed)\n setShowSuggestions(false)\n setSelectedIndex(-1)\n },\n [disabled, onChange, optionMap]\n )\n\n const findOptionForInput = React.useCallback(\n (raw: string): ComboboxOption | null => {\n const query = raw.trim().toLowerCase()\n if (!query) return null\n for (const option of optionMap.values()) {\n if (option.value === raw.trim()) return option\n if (option.label.toLowerCase() === query) return option\n }\n return null\n },\n [optionMap]\n )\n\n const confirmSelection = React.useCallback(\n (raw: string) => {\n if (disabled) return\n const option = findOptionForInput(raw)\n if (option) {\n selectValue(option.value)\n return\n }\n if (!allowCustomValues) {\n // Revert to the current value's label \u2014 but only if we actually know it.\n // Baking the raw value back in while eager resolution is still pending\n // would freeze a placeholder (e.g. a UUID) into the visible input.\n setShowSuggestions(false)\n const currentOption = optionMap.get(value)\n if (currentOption && currentOption.label !== currentOption.value) {\n setInput(currentOption.label)\n } else if (!value) {\n setInput('')\n }\n return\n }\n selectValue(raw)\n },\n [allowCustomValues, disabled, findOptionForInput, optionMap, selectValue, value]\n )\n\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLInputElement>) => {\n if (disabled) return\n\n if (event.key === 'ArrowDown') {\n event.preventDefault()\n if (!showSuggestions) {\n setShowSuggestions(true)\n setSelectedIndex(0)\n } else {\n setSelectedIndex((prev) => Math.min(prev + 1, filteredSuggestions.length - 1))\n }\n } else if (event.key === 'ArrowUp') {\n event.preventDefault()\n setSelectedIndex((prev) => Math.max(prev - 1, -1))\n } else if (event.key === 'Enter') {\n event.preventDefault()\n if (selectedIndex >= 0 && filteredSuggestions[selectedIndex]) {\n selectValue(filteredSuggestions[selectedIndex].value)\n } else {\n confirmSelection(input)\n }\n } else if (event.key === 'Escape') {\n event.preventDefault()\n setShowSuggestions(false)\n setSelectedIndex(-1)\n }\n },\n [confirmSelection, disabled, filteredSuggestions, input, selectValue, selectedIndex, showSuggestions]\n )\n\n return (\n <div className=\"relative w-full\">\n {/* Use raw <input> here instead of the DS Input primitive: ComboboxInput's\n focus / suggestions-popup interplay relies on the trigger being a plain\n input element. The DS wrapper introduces a <div> that desyncs autocomplete\n on this specific surface. Keeps the rest of the form on Input primitive. */}\n <input\n ref={inputRef}\n type=\"text\"\n className=\"w-full h-9 rounded-md border border-input bg-background px-3 text-sm shadow-xs transition-colors outline-none placeholder:text-muted-foreground focus-visible:shadow-focus focus-visible:border-foreground disabled:bg-bg-disabled disabled:border-border-disabled disabled:text-muted-foreground disabled:cursor-not-allowed\"\n value={input}\n placeholder={resolvedPlaceholder}\n autoFocus={autoFocus}\n data-crud-focus-target=\"\"\n disabled={disabled}\n onFocus={() => {\n setTouched(true)\n if (suppressOpenOnFocusRef.current) {\n suppressOpenOnFocusRef.current = false\n return\n }\n setShowSuggestions(true)\n }}\n onChange={(event) => {\n setTouched(true)\n setInput(event.target.value)\n setShowSuggestions(true)\n setSelectedIndex(-1)\n }}\n onKeyDown={handleKeyDown}\n onBlur={() => {\n // Delay to allow click on suggestions\n setTimeout(() => {\n if (disabled) return\n confirmSelection(input)\n }, 200)\n }}\n />\n\n {showSuggestions && !disabled && (loading || filteredSuggestions.length > 0) && (\n <div className=\"absolute z-popover w-full mt-1 rounded-md border border-input bg-popover p-2 shadow-md max-h-48 sm:max-h-60 overflow-auto\">\n {loading && touched ? (\n <div className=\"px-2 py-1.5 text-xs text-muted-foreground\">{loadingLabel}</div>\n ) : (\n <div className=\"flex flex-col gap-1\">\n {filteredSuggestions.map((option, index) => (\n <Button\n key={option.value}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className={[\n 'w-full h-auto justify-start font-normal text-left flex flex-col items-start rounded-lg p-2',\n index === selectedIndex ? 'bg-muted' : '',\n ]\n .filter(Boolean)\n .join(' ')}\n onMouseDown={(event) => event.preventDefault()}\n onClick={() => selectValue(option.value)}\n onMouseEnter={() => setSelectedIndex(index)}\n >\n <span className=\"font-medium text-foreground\">{option.label}</span>\n {option.description ? (\n <span className=\"text-xs text-muted-foreground\">{option.description}</span>\n ) : null}\n </Button>\n ))}\n </div>\n )}\n </div>\n )}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|