@optilogic/core 1.2.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +582 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +128 -3
- package/dist/index.d.ts +128 -3
- package/dist/index.js +578 -28
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/combobox.tsx +340 -0
- package/src/components/multi-select.tsx +375 -0
- package/src/components/select.tsx +18 -2
- package/src/components/tabs.tsx +92 -28
- package/src/index.ts +16 -0
package/dist/index.cjs
CHANGED
|
@@ -388,17 +388,26 @@ var SelectItem = React20__namespace.forwardRef(({ className, children, ...props
|
|
|
388
388
|
{
|
|
389
389
|
ref,
|
|
390
390
|
className: cn(
|
|
391
|
-
"relative flex w-full cursor-default select-none items-
|
|
391
|
+
"relative flex w-full cursor-default select-none items-start rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
392
392
|
className
|
|
393
393
|
),
|
|
394
394
|
...props,
|
|
395
395
|
children: [
|
|
396
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.ItemIndicator, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-4 w-4" }) }) }),
|
|
396
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center mt-0.5", children: /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.ItemIndicator, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-4 w-4" }) }) }),
|
|
397
397
|
/* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.ItemText, { children })
|
|
398
398
|
]
|
|
399
399
|
}
|
|
400
400
|
));
|
|
401
401
|
SelectItem.displayName = SelectPrimitive__namespace.Item.displayName;
|
|
402
|
+
var SelectItemDescription = React20__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
403
|
+
"span",
|
|
404
|
+
{
|
|
405
|
+
ref,
|
|
406
|
+
className: cn("text-xs text-muted-foreground", className),
|
|
407
|
+
...props
|
|
408
|
+
}
|
|
409
|
+
));
|
|
410
|
+
SelectItemDescription.displayName = "SelectItemDescription";
|
|
402
411
|
var SelectSeparator = React20__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
403
412
|
SelectPrimitive__namespace.Separator,
|
|
404
413
|
{
|
|
@@ -409,41 +418,85 @@ var SelectSeparator = React20__namespace.forwardRef(({ className, ...props }, re
|
|
|
409
418
|
));
|
|
410
419
|
SelectSeparator.displayName = SelectPrimitive__namespace.Separator.displayName;
|
|
411
420
|
var Tabs = TabsPrimitive__namespace.Root;
|
|
412
|
-
var
|
|
421
|
+
var tabsListVariants = classVarianceAuthority.cva(
|
|
422
|
+
"inline-flex h-10 items-center justify-start bg-transparent",
|
|
423
|
+
{
|
|
424
|
+
variants: {
|
|
425
|
+
variant: {
|
|
426
|
+
default: "border-b border-border",
|
|
427
|
+
pill: "gap-1 rounded-lg bg-muted p-1",
|
|
428
|
+
unstyled: ""
|
|
429
|
+
}
|
|
430
|
+
},
|
|
431
|
+
defaultVariants: {
|
|
432
|
+
variant: "default"
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
);
|
|
436
|
+
var tabsTriggerVariants = classVarianceAuthority.cva(
|
|
437
|
+
[
|
|
438
|
+
"inline-flex items-center justify-center whitespace-nowrap",
|
|
439
|
+
"px-4 py-2.5 text-sm font-medium",
|
|
440
|
+
"transition-colors",
|
|
441
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
442
|
+
"disabled:pointer-events-none disabled:opacity-50"
|
|
443
|
+
],
|
|
444
|
+
{
|
|
445
|
+
variants: {
|
|
446
|
+
variant: {
|
|
447
|
+
default: [
|
|
448
|
+
"-mb-px",
|
|
449
|
+
"border-transparent text-muted-foreground",
|
|
450
|
+
"hover:text-foreground hover:border-muted-foreground/50",
|
|
451
|
+
"data-[state=active]:border-foreground data-[state=active]:text-foreground"
|
|
452
|
+
],
|
|
453
|
+
pill: [
|
|
454
|
+
"rounded-md",
|
|
455
|
+
"text-muted-foreground",
|
|
456
|
+
"hover:text-foreground",
|
|
457
|
+
"data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm"
|
|
458
|
+
],
|
|
459
|
+
unstyled: [
|
|
460
|
+
"text-muted-foreground",
|
|
461
|
+
"hover:text-foreground",
|
|
462
|
+
"data-[state=active]:text-foreground"
|
|
463
|
+
]
|
|
464
|
+
},
|
|
465
|
+
indicatorSize: {
|
|
466
|
+
sm: "border-b",
|
|
467
|
+
default: "border-b-2",
|
|
468
|
+
lg: "border-b-[3px]"
|
|
469
|
+
}
|
|
470
|
+
},
|
|
471
|
+
compoundVariants: [
|
|
472
|
+
{ variant: "pill", indicatorSize: "sm", className: "border-b-0" },
|
|
473
|
+
{ variant: "pill", indicatorSize: "default", className: "border-b-0" },
|
|
474
|
+
{ variant: "pill", indicatorSize: "lg", className: "border-b-0" },
|
|
475
|
+
{ variant: "unstyled", indicatorSize: "sm", className: "border-b-0" },
|
|
476
|
+
{ variant: "unstyled", indicatorSize: "default", className: "border-b-0" },
|
|
477
|
+
{ variant: "unstyled", indicatorSize: "lg", className: "border-b-0" }
|
|
478
|
+
],
|
|
479
|
+
defaultVariants: {
|
|
480
|
+
variant: "default",
|
|
481
|
+
indicatorSize: "default"
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
);
|
|
485
|
+
var TabsList = React20__namespace.forwardRef(({ className, variant, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
413
486
|
TabsPrimitive__namespace.List,
|
|
414
487
|
{
|
|
415
488
|
ref,
|
|
416
|
-
className: cn(
|
|
417
|
-
"inline-flex h-10 items-center justify-start",
|
|
418
|
-
"border-b border-border",
|
|
419
|
-
"bg-transparent",
|
|
420
|
-
className
|
|
421
|
-
),
|
|
489
|
+
className: cn(tabsListVariants({ variant }), className),
|
|
422
490
|
...props
|
|
423
491
|
}
|
|
424
492
|
));
|
|
425
493
|
TabsList.displayName = TabsPrimitive__namespace.List.displayName;
|
|
426
|
-
var TabsTrigger = React20__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
494
|
+
var TabsTrigger = React20__namespace.forwardRef(({ className, variant, indicatorSize, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
427
495
|
TabsPrimitive__namespace.Trigger,
|
|
428
496
|
{
|
|
429
497
|
ref,
|
|
430
498
|
className: cn(
|
|
431
|
-
|
|
432
|
-
"inline-flex items-center justify-center whitespace-nowrap",
|
|
433
|
-
"px-4 py-2.5 text-sm font-medium",
|
|
434
|
-
"transition-colors",
|
|
435
|
-
// Border-bottom indicator style
|
|
436
|
-
"border-b-2 -mb-px",
|
|
437
|
-
// Default state
|
|
438
|
-
"border-transparent text-muted-foreground",
|
|
439
|
-
// Hover state
|
|
440
|
-
"hover:text-foreground hover:border-muted-foreground/50",
|
|
441
|
-
// Active/selected state
|
|
442
|
-
"data-[state=active]:border-foreground data-[state=active]:text-foreground",
|
|
443
|
-
// Focus styles
|
|
444
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
445
|
-
// Disabled styles
|
|
446
|
-
"disabled:pointer-events-none disabled:opacity-50",
|
|
499
|
+
tabsTriggerVariants({ variant, indicatorSize }),
|
|
447
500
|
className
|
|
448
501
|
),
|
|
449
502
|
...props
|
|
@@ -456,7 +509,6 @@ var TabsContent = React20__namespace.forwardRef(({ className, ...props }, ref) =
|
|
|
456
509
|
ref,
|
|
457
510
|
className: cn(
|
|
458
511
|
"mt-2",
|
|
459
|
-
// Focus styles
|
|
460
512
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
461
513
|
className
|
|
462
514
|
),
|
|
@@ -5650,6 +5702,504 @@ function Autocomplete({
|
|
|
5650
5702
|
)
|
|
5651
5703
|
] });
|
|
5652
5704
|
}
|
|
5705
|
+
function MultiSelect({
|
|
5706
|
+
options,
|
|
5707
|
+
value,
|
|
5708
|
+
onChange,
|
|
5709
|
+
placeholder = "Select items...",
|
|
5710
|
+
searchPlaceholder = "Search...",
|
|
5711
|
+
emptyText = "No options found.",
|
|
5712
|
+
disabled = false,
|
|
5713
|
+
className,
|
|
5714
|
+
clearable = true,
|
|
5715
|
+
maxDisplayItems = 3,
|
|
5716
|
+
showSelectAll = false,
|
|
5717
|
+
selectAllLabel = "Select all"
|
|
5718
|
+
}) {
|
|
5719
|
+
const [open, setOpen] = React20__namespace.useState(false);
|
|
5720
|
+
const [search, setSearch] = React20__namespace.useState("");
|
|
5721
|
+
const inputRef = React20__namespace.useRef(null);
|
|
5722
|
+
const safeOptions = options ?? [];
|
|
5723
|
+
const safeValue = value ?? [];
|
|
5724
|
+
const selectedSet = React20__namespace.useMemo(() => new Set(safeValue), [safeValue]);
|
|
5725
|
+
const filteredOptions = React20__namespace.useMemo(() => {
|
|
5726
|
+
if (!search.trim()) return safeOptions;
|
|
5727
|
+
const searchLower = search.toLowerCase();
|
|
5728
|
+
return safeOptions.filter(
|
|
5729
|
+
(opt) => opt.label.toLowerCase().includes(searchLower) || opt.description?.toLowerCase().includes(searchLower)
|
|
5730
|
+
);
|
|
5731
|
+
}, [safeOptions, search]);
|
|
5732
|
+
const groupedOptions = React20__namespace.useMemo(() => {
|
|
5733
|
+
const groups = {};
|
|
5734
|
+
const ungrouped = [];
|
|
5735
|
+
filteredOptions.forEach((opt) => {
|
|
5736
|
+
if (opt.group) {
|
|
5737
|
+
if (!groups[opt.group]) groups[opt.group] = [];
|
|
5738
|
+
groups[opt.group].push(opt);
|
|
5739
|
+
} else {
|
|
5740
|
+
ungrouped.push(opt);
|
|
5741
|
+
}
|
|
5742
|
+
});
|
|
5743
|
+
return { groups, ungrouped };
|
|
5744
|
+
}, [filteredOptions]);
|
|
5745
|
+
const hasGroups = Object.keys(groupedOptions.groups).length > 0;
|
|
5746
|
+
const selectableFiltered = React20__namespace.useMemo(
|
|
5747
|
+
() => filteredOptions.filter((opt) => !opt.disabled),
|
|
5748
|
+
[filteredOptions]
|
|
5749
|
+
);
|
|
5750
|
+
const allFilteredSelected = React20__namespace.useMemo(
|
|
5751
|
+
() => selectableFiltered.length > 0 && selectableFiltered.every((opt) => selectedSet.has(opt.value)),
|
|
5752
|
+
[selectableFiltered, selectedSet]
|
|
5753
|
+
);
|
|
5754
|
+
const someFilteredSelected = React20__namespace.useMemo(
|
|
5755
|
+
() => !allFilteredSelected && selectableFiltered.some((opt) => selectedSet.has(opt.value)),
|
|
5756
|
+
[selectableFiltered, selectedSet, allFilteredSelected]
|
|
5757
|
+
);
|
|
5758
|
+
const handleToggle = React20__namespace.useCallback(
|
|
5759
|
+
(optionValue) => {
|
|
5760
|
+
const next = selectedSet.has(optionValue) ? safeValue.filter((v) => v !== optionValue) : [...safeValue, optionValue];
|
|
5761
|
+
onChange?.(next);
|
|
5762
|
+
},
|
|
5763
|
+
[onChange, safeValue, selectedSet]
|
|
5764
|
+
);
|
|
5765
|
+
const handleRemove = React20__namespace.useCallback(
|
|
5766
|
+
(optionValue, e) => {
|
|
5767
|
+
e.stopPropagation();
|
|
5768
|
+
onChange?.(safeValue.filter((v) => v !== optionValue));
|
|
5769
|
+
},
|
|
5770
|
+
[onChange, safeValue]
|
|
5771
|
+
);
|
|
5772
|
+
const handleClearAll = React20__namespace.useCallback(
|
|
5773
|
+
(e) => {
|
|
5774
|
+
e.stopPropagation();
|
|
5775
|
+
onChange?.([]);
|
|
5776
|
+
},
|
|
5777
|
+
[onChange]
|
|
5778
|
+
);
|
|
5779
|
+
const handleSelectAll = React20__namespace.useCallback(() => {
|
|
5780
|
+
if (allFilteredSelected) {
|
|
5781
|
+
const filteredValues = new Set(selectableFiltered.map((o) => o.value));
|
|
5782
|
+
onChange?.(safeValue.filter((v) => !filteredValues.has(v)));
|
|
5783
|
+
} else {
|
|
5784
|
+
const existing = new Set(safeValue);
|
|
5785
|
+
const next = [...safeValue];
|
|
5786
|
+
for (const opt of selectableFiltered) {
|
|
5787
|
+
if (!existing.has(opt.value)) {
|
|
5788
|
+
next.push(opt.value);
|
|
5789
|
+
}
|
|
5790
|
+
}
|
|
5791
|
+
onChange?.(next);
|
|
5792
|
+
}
|
|
5793
|
+
}, [allFilteredSelected, selectableFiltered, safeValue, onChange]);
|
|
5794
|
+
React20__namespace.useEffect(() => {
|
|
5795
|
+
if (open) {
|
|
5796
|
+
const timeout = setTimeout(() => inputRef.current?.focus(), 0);
|
|
5797
|
+
return () => clearTimeout(timeout);
|
|
5798
|
+
} else {
|
|
5799
|
+
setSearch("");
|
|
5800
|
+
}
|
|
5801
|
+
}, [open]);
|
|
5802
|
+
const handleKeyDown = React20__namespace.useCallback((e) => {
|
|
5803
|
+
if (e.key === "Escape") setOpen(false);
|
|
5804
|
+
}, []);
|
|
5805
|
+
const selectedLabels = React20__namespace.useMemo(
|
|
5806
|
+
() => safeValue.map((v) => safeOptions.find((o) => o.value === v)?.label ?? v).slice(0, maxDisplayItems),
|
|
5807
|
+
[safeValue, safeOptions, maxDisplayItems]
|
|
5808
|
+
);
|
|
5809
|
+
const overflow = safeValue.length - maxDisplayItems;
|
|
5810
|
+
const isSearching = search.trim().length > 0;
|
|
5811
|
+
const renderOption = (option) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5812
|
+
"button",
|
|
5813
|
+
{
|
|
5814
|
+
type: "button",
|
|
5815
|
+
disabled: option.disabled,
|
|
5816
|
+
onClick: () => handleToggle(option.value),
|
|
5817
|
+
className: cn(
|
|
5818
|
+
"relative flex w-full cursor-pointer select-none items-start gap-2 rounded-sm px-2 py-1.5 text-sm outline-none",
|
|
5819
|
+
"hover:bg-accent hover:text-accent-foreground",
|
|
5820
|
+
option.disabled && "pointer-events-none opacity-50"
|
|
5821
|
+
),
|
|
5822
|
+
children: [
|
|
5823
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5824
|
+
Checkbox,
|
|
5825
|
+
{
|
|
5826
|
+
checked: selectedSet.has(option.value),
|
|
5827
|
+
tabIndex: -1,
|
|
5828
|
+
className: "mt-0.5 pointer-events-none"
|
|
5829
|
+
}
|
|
5830
|
+
),
|
|
5831
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
5832
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate", children: option.label }),
|
|
5833
|
+
option.description && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground truncate", children: option.description })
|
|
5834
|
+
] })
|
|
5835
|
+
]
|
|
5836
|
+
},
|
|
5837
|
+
option.value
|
|
5838
|
+
);
|
|
5839
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open, onOpenChange: setOpen, children: [
|
|
5840
|
+
/* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, disabled, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5841
|
+
"button",
|
|
5842
|
+
{
|
|
5843
|
+
type: "button",
|
|
5844
|
+
role: "combobox",
|
|
5845
|
+
"aria-expanded": open,
|
|
5846
|
+
"aria-haspopup": "listbox",
|
|
5847
|
+
disabled,
|
|
5848
|
+
className: cn(
|
|
5849
|
+
"flex min-h-9 w-full items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-1.5 text-sm shadow-sm ring-offset-background",
|
|
5850
|
+
"hover:border-input-hover",
|
|
5851
|
+
"focus:outline-none focus:ring-1 focus:ring-ring",
|
|
5852
|
+
"disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:border-input",
|
|
5853
|
+
className
|
|
5854
|
+
),
|
|
5855
|
+
children: [
|
|
5856
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 flex-wrap gap-1 items-center min-w-0", children: safeValue.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: placeholder }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5857
|
+
selectedLabels.map((label, i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5858
|
+
"span",
|
|
5859
|
+
{
|
|
5860
|
+
className: "inline-flex items-center gap-0.5 rounded-sm bg-accent px-1.5 py-0.5 text-xs font-medium text-accent-foreground",
|
|
5861
|
+
children: [
|
|
5862
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate max-w-[100px]", children: label }),
|
|
5863
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5864
|
+
"span",
|
|
5865
|
+
{
|
|
5866
|
+
role: "button",
|
|
5867
|
+
tabIndex: -1,
|
|
5868
|
+
onClick: (e) => handleRemove(safeValue[i], e),
|
|
5869
|
+
className: "rounded-sm hover:bg-foreground/10 p-0.5",
|
|
5870
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3 w-3" })
|
|
5871
|
+
}
|
|
5872
|
+
)
|
|
5873
|
+
]
|
|
5874
|
+
},
|
|
5875
|
+
safeValue[i]
|
|
5876
|
+
)),
|
|
5877
|
+
overflow > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
5878
|
+
"+",
|
|
5879
|
+
overflow
|
|
5880
|
+
] })
|
|
5881
|
+
] }) }),
|
|
5882
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 flex-shrink-0", children: [
|
|
5883
|
+
clearable && safeValue.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5884
|
+
"span",
|
|
5885
|
+
{
|
|
5886
|
+
role: "button",
|
|
5887
|
+
tabIndex: -1,
|
|
5888
|
+
onClick: handleClearAll,
|
|
5889
|
+
className: "rounded-sm hover:bg-muted p-0.5",
|
|
5890
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3.5 w-3.5 text-muted-foreground" })
|
|
5891
|
+
}
|
|
5892
|
+
),
|
|
5893
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-4 w-4 opacity-50" })
|
|
5894
|
+
] })
|
|
5895
|
+
]
|
|
5896
|
+
}
|
|
5897
|
+
) }),
|
|
5898
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
5899
|
+
PopoverContent,
|
|
5900
|
+
{
|
|
5901
|
+
className: "w-[--radix-popover-trigger-width] p-0",
|
|
5902
|
+
align: "start",
|
|
5903
|
+
sideOffset: 4,
|
|
5904
|
+
onKeyDown: handleKeyDown,
|
|
5905
|
+
children: [
|
|
5906
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center border-b border-border px-3", children: [
|
|
5907
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5908
|
+
"input",
|
|
5909
|
+
{
|
|
5910
|
+
ref: inputRef,
|
|
5911
|
+
type: "text",
|
|
5912
|
+
value: search,
|
|
5913
|
+
onChange: (e) => setSearch(e.target.value),
|
|
5914
|
+
placeholder: searchPlaceholder,
|
|
5915
|
+
className: "flex h-9 w-full bg-transparent py-2 text-sm outline-none placeholder:text-muted-foreground"
|
|
5916
|
+
}
|
|
5917
|
+
),
|
|
5918
|
+
search && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5919
|
+
"button",
|
|
5920
|
+
{
|
|
5921
|
+
type: "button",
|
|
5922
|
+
onClick: () => setSearch(""),
|
|
5923
|
+
className: "p-1 hover:bg-muted rounded-sm",
|
|
5924
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3.5 w-3.5 text-muted-foreground" })
|
|
5925
|
+
}
|
|
5926
|
+
)
|
|
5927
|
+
] }),
|
|
5928
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-h-[300px] overflow-y-auto p-1", children: [
|
|
5929
|
+
showSelectAll && selectableFiltered.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5930
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
5931
|
+
"button",
|
|
5932
|
+
{
|
|
5933
|
+
type: "button",
|
|
5934
|
+
onClick: handleSelectAll,
|
|
5935
|
+
className: cn(
|
|
5936
|
+
"relative flex w-full cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none",
|
|
5937
|
+
"hover:bg-accent hover:text-accent-foreground"
|
|
5938
|
+
),
|
|
5939
|
+
children: [
|
|
5940
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5941
|
+
Checkbox,
|
|
5942
|
+
{
|
|
5943
|
+
checked: allFilteredSelected ? true : someFilteredSelected ? "indeterminate" : false,
|
|
5944
|
+
tabIndex: -1,
|
|
5945
|
+
className: "pointer-events-none"
|
|
5946
|
+
}
|
|
5947
|
+
),
|
|
5948
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: isSearching ? `${selectAllLabel} (filtered)` : selectAllLabel })
|
|
5949
|
+
]
|
|
5950
|
+
}
|
|
5951
|
+
),
|
|
5952
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "-mx-1 my-1 h-px bg-border" })
|
|
5953
|
+
] }),
|
|
5954
|
+
filteredOptions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-6 text-center text-sm text-muted-foreground", children: emptyText }) : hasGroups ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5955
|
+
groupedOptions.ungrouped.map(renderOption),
|
|
5956
|
+
Object.entries(groupedOptions.groups).map(([group, opts]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
5957
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 py-1.5 text-xs font-semibold text-muted-foreground", children: group }),
|
|
5958
|
+
opts.map(renderOption)
|
|
5959
|
+
] }, group))
|
|
5960
|
+
] }) : filteredOptions.map(renderOption)
|
|
5961
|
+
] })
|
|
5962
|
+
]
|
|
5963
|
+
}
|
|
5964
|
+
)
|
|
5965
|
+
] });
|
|
5966
|
+
}
|
|
5967
|
+
MultiSelect.displayName = "MultiSelect";
|
|
5968
|
+
function Combobox({
|
|
5969
|
+
options,
|
|
5970
|
+
value,
|
|
5971
|
+
onChange,
|
|
5972
|
+
onInputChange,
|
|
5973
|
+
placeholder = "Type or select...",
|
|
5974
|
+
emptyText = "No options found.",
|
|
5975
|
+
disabled = false,
|
|
5976
|
+
className,
|
|
5977
|
+
clearable = false,
|
|
5978
|
+
allowCustomValue = true
|
|
5979
|
+
}) {
|
|
5980
|
+
const [open, setOpen] = React20__namespace.useState(false);
|
|
5981
|
+
const [inputValue, setInputValue] = React20__namespace.useState("");
|
|
5982
|
+
const inputRef = React20__namespace.useRef(null);
|
|
5983
|
+
const wrapperRef = React20__namespace.useRef(null);
|
|
5984
|
+
const selectedOption = React20__namespace.useMemo(
|
|
5985
|
+
() => options.find((opt) => opt.value === value),
|
|
5986
|
+
[options, value]
|
|
5987
|
+
);
|
|
5988
|
+
React20__namespace.useEffect(() => {
|
|
5989
|
+
if (!open) {
|
|
5990
|
+
setInputValue(selectedOption?.label ?? value ?? "");
|
|
5991
|
+
}
|
|
5992
|
+
}, [value, selectedOption, open]);
|
|
5993
|
+
const filteredOptions = React20__namespace.useMemo(() => {
|
|
5994
|
+
if (!inputValue.trim()) return options;
|
|
5995
|
+
const searchLower = inputValue.toLowerCase();
|
|
5996
|
+
return options.filter(
|
|
5997
|
+
(opt) => opt.label.toLowerCase().includes(searchLower) || opt.description?.toLowerCase().includes(searchLower)
|
|
5998
|
+
);
|
|
5999
|
+
}, [options, inputValue]);
|
|
6000
|
+
const groupedOptions = React20__namespace.useMemo(() => {
|
|
6001
|
+
const groups = {};
|
|
6002
|
+
const ungrouped = [];
|
|
6003
|
+
filteredOptions.forEach((opt) => {
|
|
6004
|
+
if (opt.group) {
|
|
6005
|
+
if (!groups[opt.group]) groups[opt.group] = [];
|
|
6006
|
+
groups[opt.group].push(opt);
|
|
6007
|
+
} else {
|
|
6008
|
+
ungrouped.push(opt);
|
|
6009
|
+
}
|
|
6010
|
+
});
|
|
6011
|
+
return { groups, ungrouped };
|
|
6012
|
+
}, [filteredOptions]);
|
|
6013
|
+
const hasGroups = Object.keys(groupedOptions.groups).length > 0;
|
|
6014
|
+
const handleInputChange = React20__namespace.useCallback(
|
|
6015
|
+
(e) => {
|
|
6016
|
+
const newValue = e.target.value;
|
|
6017
|
+
setInputValue(newValue);
|
|
6018
|
+
onInputChange?.(newValue);
|
|
6019
|
+
if (!open) setOpen(true);
|
|
6020
|
+
},
|
|
6021
|
+
[onInputChange, open]
|
|
6022
|
+
);
|
|
6023
|
+
const handleSelect = React20__namespace.useCallback(
|
|
6024
|
+
(optionValue) => {
|
|
6025
|
+
const option = options.find((o) => o.value === optionValue);
|
|
6026
|
+
onChange?.(optionValue);
|
|
6027
|
+
setInputValue(option?.label ?? optionValue);
|
|
6028
|
+
setOpen(false);
|
|
6029
|
+
},
|
|
6030
|
+
[onChange, options]
|
|
6031
|
+
);
|
|
6032
|
+
const handleClear = React20__namespace.useCallback(
|
|
6033
|
+
(e) => {
|
|
6034
|
+
e.stopPropagation();
|
|
6035
|
+
e.preventDefault();
|
|
6036
|
+
onChange?.(void 0);
|
|
6037
|
+
setInputValue("");
|
|
6038
|
+
inputRef.current?.focus();
|
|
6039
|
+
},
|
|
6040
|
+
[onChange]
|
|
6041
|
+
);
|
|
6042
|
+
const handleFocus = React20__namespace.useCallback(() => {
|
|
6043
|
+
setOpen(true);
|
|
6044
|
+
inputRef.current?.select();
|
|
6045
|
+
}, []);
|
|
6046
|
+
const handleBlur = React20__namespace.useCallback(() => {
|
|
6047
|
+
setTimeout(() => {
|
|
6048
|
+
if (!wrapperRef.current?.contains(document.activeElement)) {
|
|
6049
|
+
setOpen(false);
|
|
6050
|
+
}
|
|
6051
|
+
if (allowCustomValue && inputValue.trim()) {
|
|
6052
|
+
const matchingOption = options.find(
|
|
6053
|
+
(opt) => opt.label.toLowerCase() === inputValue.toLowerCase()
|
|
6054
|
+
);
|
|
6055
|
+
if (matchingOption) {
|
|
6056
|
+
onChange?.(matchingOption.value);
|
|
6057
|
+
setInputValue(matchingOption.label);
|
|
6058
|
+
} else {
|
|
6059
|
+
onChange?.(inputValue.trim());
|
|
6060
|
+
}
|
|
6061
|
+
} else if (!allowCustomValue) {
|
|
6062
|
+
setInputValue(selectedOption?.label ?? "");
|
|
6063
|
+
}
|
|
6064
|
+
}, 200);
|
|
6065
|
+
}, [allowCustomValue, inputValue, options, onChange, selectedOption]);
|
|
6066
|
+
const handleKeyDown = React20__namespace.useCallback(
|
|
6067
|
+
(e) => {
|
|
6068
|
+
if (e.key === "Escape") {
|
|
6069
|
+
setOpen(false);
|
|
6070
|
+
setInputValue(selectedOption?.label ?? value ?? "");
|
|
6071
|
+
inputRef.current?.blur();
|
|
6072
|
+
} else if (e.key === "Enter" && open) {
|
|
6073
|
+
e.preventDefault();
|
|
6074
|
+
if (filteredOptions.length === 1) {
|
|
6075
|
+
handleSelect(filteredOptions[0].value);
|
|
6076
|
+
} else if (allowCustomValue && inputValue.trim()) {
|
|
6077
|
+
const exactMatch = filteredOptions.find(
|
|
6078
|
+
(opt) => opt.label.toLowerCase() === inputValue.toLowerCase()
|
|
6079
|
+
);
|
|
6080
|
+
if (exactMatch) {
|
|
6081
|
+
handleSelect(exactMatch.value);
|
|
6082
|
+
} else {
|
|
6083
|
+
onChange?.(inputValue.trim());
|
|
6084
|
+
setOpen(false);
|
|
6085
|
+
}
|
|
6086
|
+
}
|
|
6087
|
+
}
|
|
6088
|
+
},
|
|
6089
|
+
[
|
|
6090
|
+
open,
|
|
6091
|
+
filteredOptions,
|
|
6092
|
+
handleSelect,
|
|
6093
|
+
allowCustomValue,
|
|
6094
|
+
inputValue,
|
|
6095
|
+
onChange,
|
|
6096
|
+
selectedOption,
|
|
6097
|
+
value
|
|
6098
|
+
]
|
|
6099
|
+
);
|
|
6100
|
+
const renderOption = (option) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6101
|
+
"button",
|
|
6102
|
+
{
|
|
6103
|
+
type: "button",
|
|
6104
|
+
disabled: option.disabled,
|
|
6105
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
6106
|
+
onClick: () => handleSelect(option.value),
|
|
6107
|
+
className: cn(
|
|
6108
|
+
"relative flex w-full cursor-pointer select-none items-start gap-2 rounded-sm px-2 py-1.5 text-sm outline-none",
|
|
6109
|
+
"hover:bg-accent hover:text-accent-foreground",
|
|
6110
|
+
"focus:bg-accent focus:text-accent-foreground",
|
|
6111
|
+
option.disabled && "pointer-events-none opacity-50",
|
|
6112
|
+
value === option.value && "bg-accent/50"
|
|
6113
|
+
),
|
|
6114
|
+
children: [
|
|
6115
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-4 w-4 items-center justify-center flex-shrink-0 mt-0.5", children: value === option.value && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-4 w-4" }) }),
|
|
6116
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
6117
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate", children: option.label }),
|
|
6118
|
+
option.description && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground truncate", children: option.description })
|
|
6119
|
+
] })
|
|
6120
|
+
]
|
|
6121
|
+
},
|
|
6122
|
+
option.value
|
|
6123
|
+
);
|
|
6124
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open, onOpenChange: setOpen, children: [
|
|
6125
|
+
/* @__PURE__ */ jsxRuntime.jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6126
|
+
"div",
|
|
6127
|
+
{
|
|
6128
|
+
ref: wrapperRef,
|
|
6129
|
+
className: cn(
|
|
6130
|
+
"flex h-9 w-full items-center gap-2 rounded-md border border-input bg-transparent px-3 text-sm shadow-sm ring-offset-background",
|
|
6131
|
+
"hover:border-input-hover",
|
|
6132
|
+
"focus-within:outline-none focus-within:ring-1 focus-within:ring-ring",
|
|
6133
|
+
disabled && "cursor-not-allowed opacity-50 hover:border-input",
|
|
6134
|
+
className
|
|
6135
|
+
),
|
|
6136
|
+
children: [
|
|
6137
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6138
|
+
"input",
|
|
6139
|
+
{
|
|
6140
|
+
ref: inputRef,
|
|
6141
|
+
type: "text",
|
|
6142
|
+
value: inputValue,
|
|
6143
|
+
onChange: handleInputChange,
|
|
6144
|
+
onFocus: handleFocus,
|
|
6145
|
+
onBlur: handleBlur,
|
|
6146
|
+
onKeyDown: handleKeyDown,
|
|
6147
|
+
placeholder,
|
|
6148
|
+
disabled,
|
|
6149
|
+
className: "flex-1 min-w-0 bg-transparent py-2 outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed",
|
|
6150
|
+
role: "combobox",
|
|
6151
|
+
"aria-expanded": open,
|
|
6152
|
+
"aria-haspopup": "listbox",
|
|
6153
|
+
"aria-autocomplete": "list"
|
|
6154
|
+
}
|
|
6155
|
+
),
|
|
6156
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 flex-shrink-0", children: [
|
|
6157
|
+
clearable && value && /* @__PURE__ */ jsxRuntime.jsx(
|
|
6158
|
+
"span",
|
|
6159
|
+
{
|
|
6160
|
+
role: "button",
|
|
6161
|
+
tabIndex: -1,
|
|
6162
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
6163
|
+
onClick: handleClear,
|
|
6164
|
+
className: "rounded-sm hover:bg-muted p-0.5",
|
|
6165
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3.5 w-3.5 text-muted-foreground" })
|
|
6166
|
+
}
|
|
6167
|
+
),
|
|
6168
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-4 w-4 opacity-50" })
|
|
6169
|
+
] })
|
|
6170
|
+
]
|
|
6171
|
+
}
|
|
6172
|
+
) }),
|
|
6173
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6174
|
+
PopoverContent,
|
|
6175
|
+
{
|
|
6176
|
+
className: "p-0",
|
|
6177
|
+
style: { width: wrapperRef.current?.offsetWidth },
|
|
6178
|
+
align: "start",
|
|
6179
|
+
sideOffset: 4,
|
|
6180
|
+
onOpenAutoFocus: (e) => e.preventDefault(),
|
|
6181
|
+
onFocusOutside: (e) => {
|
|
6182
|
+
if (wrapperRef.current?.contains(e.target)) {
|
|
6183
|
+
e.preventDefault();
|
|
6184
|
+
}
|
|
6185
|
+
},
|
|
6186
|
+
onInteractOutside: (e) => {
|
|
6187
|
+
if (wrapperRef.current?.contains(e.target)) {
|
|
6188
|
+
e.preventDefault();
|
|
6189
|
+
}
|
|
6190
|
+
},
|
|
6191
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-[300px] overflow-y-auto p-1", children: filteredOptions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-6 text-center text-sm text-muted-foreground", children: emptyText }) : hasGroups ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
6192
|
+
groupedOptions.ungrouped.map(renderOption),
|
|
6193
|
+
Object.entries(groupedOptions.groups).map(([group, opts]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
6194
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 py-1.5 text-xs font-semibold text-muted-foreground", children: group }),
|
|
6195
|
+
opts.map(renderOption)
|
|
6196
|
+
] }, group))
|
|
6197
|
+
] }) : filteredOptions.map(renderOption) })
|
|
6198
|
+
}
|
|
6199
|
+
)
|
|
6200
|
+
] });
|
|
6201
|
+
}
|
|
6202
|
+
Combobox.displayName = "Combobox";
|
|
5653
6203
|
var iconButtonVariants = classVarianceAuthority.cva(
|
|
5654
6204
|
// Base styles
|
|
5655
6205
|
[
|
|
@@ -7963,6 +8513,7 @@ exports.CellEditor = CellEditor;
|
|
|
7963
8513
|
exports.Checkbox = Checkbox;
|
|
7964
8514
|
exports.Chip = Chip;
|
|
7965
8515
|
exports.CodeRenderer = CodeRenderer;
|
|
8516
|
+
exports.Combobox = Combobox;
|
|
7966
8517
|
exports.ConfirmationModal = ConfirmationModal;
|
|
7967
8518
|
exports.ContextMenu = ContextMenu;
|
|
7968
8519
|
exports.CopyButton = CopyButton;
|
|
@@ -8005,6 +8556,7 @@ exports.MODERN_LIGHT_THEME = MODERN_LIGHT_THEME;
|
|
|
8005
8556
|
exports.MarkdownRenderer = MarkdownRenderer;
|
|
8006
8557
|
exports.Modal = Modal;
|
|
8007
8558
|
exports.ModalButton = ModalButton;
|
|
8559
|
+
exports.MultiSelect = MultiSelect;
|
|
8008
8560
|
exports.OPTILOGIC_DARK_THEME = OPTILOGIC_DARK_THEME;
|
|
8009
8561
|
exports.OPTILOGIC_LEGACY_THEME = OPTILOGIC_LEGACY_THEME;
|
|
8010
8562
|
exports.OptilogicLogo = OptilogicLogo;
|
|
@@ -8022,6 +8574,7 @@ exports.Select = Select;
|
|
|
8022
8574
|
exports.SelectContent = SelectContent;
|
|
8023
8575
|
exports.SelectGroup = SelectGroup;
|
|
8024
8576
|
exports.SelectItem = SelectItem;
|
|
8577
|
+
exports.SelectItemDescription = SelectItemDescription;
|
|
8025
8578
|
exports.SelectLabel = SelectLabel;
|
|
8026
8579
|
exports.SelectScrollDownButton = SelectScrollDownButton;
|
|
8027
8580
|
exports.SelectScrollUpButton = SelectScrollUpButton;
|
|
@@ -8089,6 +8642,8 @@ exports.labelVariants = labelVariants;
|
|
|
8089
8642
|
exports.loadingSpinnerVariants = loadingSpinnerVariants;
|
|
8090
8643
|
exports.mergeRenderers = mergeRenderers;
|
|
8091
8644
|
exports.resolveRenderer = resolveRenderer;
|
|
8645
|
+
exports.tabsListVariants = tabsListVariants;
|
|
8646
|
+
exports.tabsTriggerVariants = tabsTriggerVariants;
|
|
8092
8647
|
exports.themeToHsl = themeToHsl;
|
|
8093
8648
|
exports.useColumnResize = useColumnResize;
|
|
8094
8649
|
exports.useColumnResizeManager = useColumnResizeManager;
|