@socprime/react-ui 0.0.12 → 0.0.14
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/index.d.ts +36 -3
- package/package.json +5 -1
- package/preset/DocsTemplate.js +22 -0
- package/preset/createPreview.js +3 -1
- package/ui/Editor/Editor.js +5 -4
- package/ui/MultiSelect/BaseMultiSelect.js +147 -0
- package/ui/MultiSelect/MultiSelect.js +65 -12
- package/ui/MultiSelect/index.js +2 -0
package/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as AccordionPrimitive from '@radix-ui/react-accordion';
|
|
2
2
|
import * as React$1 from 'react';
|
|
3
|
-
import React__default, { ComponentProps, FC } from 'react';
|
|
3
|
+
import React__default, { ComponentProps, ReactNode, FC } from 'react';
|
|
4
4
|
import { LucideIcon, LucideProps } from 'lucide-react';
|
|
5
5
|
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
6
6
|
import { VariantProps } from 'class-variance-authority';
|
|
@@ -409,7 +409,7 @@ interface MultiSelectOption {
|
|
|
409
409
|
interface MultiSelectProps {
|
|
410
410
|
loading?: boolean;
|
|
411
411
|
options: MultiSelectOption[];
|
|
412
|
-
value
|
|
412
|
+
value?: string[];
|
|
413
413
|
placeholder?: string;
|
|
414
414
|
className?: string;
|
|
415
415
|
disabled?: boolean;
|
|
@@ -427,6 +427,39 @@ interface MultiSelectProps {
|
|
|
427
427
|
}
|
|
428
428
|
declare function MultiSelect({ loading, options, value, placeholder, className, disabled, maxDisplay, maxSelection, searchable, size, dropdownClassName, align, variant, badgeClassNames, id, name, onChange, }: MultiSelectProps): React$1.JSX.Element;
|
|
429
429
|
|
|
430
|
+
interface BaseMultiSelectOptionState {
|
|
431
|
+
selected: boolean;
|
|
432
|
+
disabled: boolean;
|
|
433
|
+
toggle: () => void;
|
|
434
|
+
}
|
|
435
|
+
interface BaseMultiSelectValueHelpers {
|
|
436
|
+
remove: (value: string) => void;
|
|
437
|
+
clear: () => void;
|
|
438
|
+
}
|
|
439
|
+
interface BaseMultiSelectProps<T> {
|
|
440
|
+
options: T[];
|
|
441
|
+
value?: string[];
|
|
442
|
+
onChange: (value: string[]) => void;
|
|
443
|
+
getOptionValue: (option: T) => string;
|
|
444
|
+
isOptionDisabled?: (option: T) => boolean;
|
|
445
|
+
renderOption?: (option: T, state: BaseMultiSelectOptionState) => ReactNode;
|
|
446
|
+
renderValue?: (selectedOptions: T[], helpers: BaseMultiSelectValueHelpers) => ReactNode;
|
|
447
|
+
renderEmpty?: () => ReactNode;
|
|
448
|
+
placeholder?: ReactNode;
|
|
449
|
+
loading?: boolean;
|
|
450
|
+
disabled?: boolean;
|
|
451
|
+
open?: boolean;
|
|
452
|
+
onOpenChange?: (open: boolean) => void;
|
|
453
|
+
className?: string;
|
|
454
|
+
dropdownClassName?: string;
|
|
455
|
+
align?: 'start' | 'center' | 'end';
|
|
456
|
+
sideOffset?: number;
|
|
457
|
+
size?: 'sm' | 'default';
|
|
458
|
+
id?: string;
|
|
459
|
+
name?: string;
|
|
460
|
+
}
|
|
461
|
+
declare function BaseMultiSelect<T>({ options, value, onChange, getOptionValue, isOptionDisabled, renderOption, renderValue, renderEmpty, placeholder, loading, disabled, open, onOpenChange, className, dropdownClassName, align, sideOffset, size, id, name, }: BaseMultiSelectProps<T>): React$1.JSX.Element;
|
|
462
|
+
|
|
430
463
|
interface PageHeaderProps {
|
|
431
464
|
loading?: boolean;
|
|
432
465
|
title?: string;
|
|
@@ -735,4 +768,4 @@ declare function TreeRow({ className, children, ...props }: ComponentProps<'div'
|
|
|
735
768
|
type TreeGuideProps = ComponentProps<typeof TreeGuide>;
|
|
736
769
|
type TreeRowProps = ComponentProps<typeof TreeRow>;
|
|
737
770
|
|
|
738
|
-
export { Accordion, type AccordionChevronVariant, AccordionContent, type AccordionContentProps, AccordionItem, type AccordionItemProps, type AccordionProps, AccordionSection, type AccordionSectionProps, AccordionTrigger, type AccordionTriggerProps, AnimatedDots, type AnimatedDotsProps, Aside, AsideHorizontal, type AsideHorizontalProps, type AsideProps, type AsideTab, Badge, type BadgeProps, Button, type ButtonProps, Checkbox, type CheckboxProps, ConditionalContent, type ConditionalContentProps, ConfirmDeleteDialog, type ConfirmDeleteDialogProps, ConfirmDeleteDialog as ConfirmDialog, DefaultDialog, type DefaultDialogProps, DeleteButtonConfirm, type DeleteButtonConfirmProps, Dialog, DialogClose, type DialogCloseProps, DialogContent, type DialogContentProps, DialogDescription, type DialogDescriptionProps, DialogFooter, type DialogFooterProps, DialogHeader, type DialogHeaderProps, DialogOverlay, type DialogOverlayProps, DialogPortal, type DialogPortalProps, type DialogProps, DialogTitle, type DialogTitleProps, DialogTrigger, type DialogTriggerProps, DropdownMenu, DropdownMenuCheckboxItem, type DropdownMenuCheckboxItemProps, DropdownMenuContent, type DropdownMenuContentProps, DropdownMenuGroup, type DropdownMenuGroupProps, DropdownMenuItem, type DropdownMenuItemProps, DropdownMenuLabel, type DropdownMenuLabelProps, DropdownMenuPortal, type DropdownMenuPortalProps, type DropdownMenuProps, DropdownMenuRadioGroup, type DropdownMenuRadioGroupProps, DropdownMenuRadioItem, type DropdownMenuRadioItemProps, DropdownMenuSeparator, type DropdownMenuSeparatorProps, DropdownMenuShortcut, type DropdownMenuShortcutProps, DropdownMenuSub, DropdownMenuSubContent, type DropdownMenuSubContentProps, type DropdownMenuSubProps, DropdownMenuSubTrigger, type DropdownMenuSubTriggerProps, DropdownMenuTrigger, type DropdownMenuTriggerProps, DropdownsCheckbox, type DropdownsCheckboxOption, type DropdownsCheckboxProps, DropdownsSelect, type DropdownsSelectOption, type DropdownsSelectProps, EmptyState, type EmptyStateProps, Field, FieldContent, type FieldContentProps, FieldDescription, type FieldDescriptionProps, FieldError, type FieldErrorProps, FieldGroup, type FieldGroupProps, FieldLabel, type FieldLabelProps, FieldLegend, type FieldLegendProps, type FieldProps, FieldSeparator, type FieldSeparatorProps, FieldSet, type FieldSetProps, FieldTitle, type FieldTitleProps, FolderTreeExpandTrigger, type FolderTreeExpandTriggerProps, GroupHeaderRow, type GroupHeaderRowProps, HelperText, type HelperTextProps, Input, InputNumber, type InputNumberProps, InputPassword, type InputPasswordProps, type InputProps, Label, LabelInfo, type LabelInfoProps, type LabelProps, MobileNav, type MobileNavItem, type MobileNavProps, MultiSelect, type MultiSelectOption, type MultiSelectProps, PageHeader, type PageHeaderProps, Pagination, PaginationContent, type PaginationContentProps, PaginationEllipsis, type PaginationEllipsisProps, PaginationItem, type PaginationItemProps, PaginationLink, type PaginationLinkProps, PaginationNext, type PaginationNextProps, PaginationPrevious, type PaginationPreviousProps, type PaginationProps, PaginationWrap, type PaginationWrapProps, Progress, type ProgressProps, RadioGroup, RadioGroupItem, type RadioGroupItemProps, type RadioGroupProps, ScrollArea, type ScrollAreaOrientation, type ScrollAreaProps, ScrollBar, SearchInput, SearchInputFields, type SearchInputFieldsProps, type SearchInputProps, Select, SelectContent, type SelectContentProps, SelectDefault, type SelectDefaultProps, SelectGroup, type SelectGroupProps, SelectItem, type SelectItemProps, SelectLabel, type SelectLabelProps, type SelectProps, SelectScrollDownButton, type SelectScrollDownButtonProps, SelectScrollUpButton, type SelectScrollUpButtonProps, SelectSeparator, type SelectSeparatorProps, SelectTrigger, type SelectTriggerProps, SelectValue, type SelectValueProps, Separator, type SeparatorProps, ServerGroupedTableBody, type ServerGroupedTableBodyProps, Skeleton, type SkeletonProps, SkeletonTable, type SkeletonTableProps, Slider, type SliderProps, SortableHeader, type SortableHeaderProps, Spinner, SpinnerCircle, type SpinnerCircleProps, SpinnerCustom, type SpinnerProps, SpinnerSquare, type SpinnerSquareProps, type SuggestionOption, Suggestions, type SuggestionsProps, Switch, type SwitchProps, type TabItem, Table, TableBody, type TableBodyProps, TableCaption, type TableCaptionProps, TableCell, type TableCellProps, TableFooter, type TableFooterProps, TableHead, type TableHeadProps, TableHeader, type TableHeaderProps, type TableProps, TableRow, type TableRowProps, TableWrap, type TableWrapProps, Tabs, type TabsProps, TextTruncate, type TextTruncateProps, Textarea, type TextareaProps, ThemeToggle, type ThemeToggleProps, Toaster, Tooltip, TooltipContent, type TooltipContentProps, TooltipParent, type TooltipParentProps, type TooltipProps, TooltipProvider, type TooltipProviderProps, TooltipTrigger, type TooltipTriggerProps, TreeGuide, type TreeGuideProps, TreeRow, type TreeRowProps, badgeVariants, buttonVariants };
|
|
771
|
+
export { Accordion, type AccordionChevronVariant, AccordionContent, type AccordionContentProps, AccordionItem, type AccordionItemProps, type AccordionProps, AccordionSection, type AccordionSectionProps, AccordionTrigger, type AccordionTriggerProps, AnimatedDots, type AnimatedDotsProps, Aside, AsideHorizontal, type AsideHorizontalProps, type AsideProps, type AsideTab, Badge, type BadgeProps, BaseMultiSelect, type BaseMultiSelectOptionState, type BaseMultiSelectProps, type BaseMultiSelectValueHelpers, Button, type ButtonProps, Checkbox, type CheckboxProps, ConditionalContent, type ConditionalContentProps, ConfirmDeleteDialog, type ConfirmDeleteDialogProps, ConfirmDeleteDialog as ConfirmDialog, DefaultDialog, type DefaultDialogProps, DeleteButtonConfirm, type DeleteButtonConfirmProps, Dialog, DialogClose, type DialogCloseProps, DialogContent, type DialogContentProps, DialogDescription, type DialogDescriptionProps, DialogFooter, type DialogFooterProps, DialogHeader, type DialogHeaderProps, DialogOverlay, type DialogOverlayProps, DialogPortal, type DialogPortalProps, type DialogProps, DialogTitle, type DialogTitleProps, DialogTrigger, type DialogTriggerProps, DropdownMenu, DropdownMenuCheckboxItem, type DropdownMenuCheckboxItemProps, DropdownMenuContent, type DropdownMenuContentProps, DropdownMenuGroup, type DropdownMenuGroupProps, DropdownMenuItem, type DropdownMenuItemProps, DropdownMenuLabel, type DropdownMenuLabelProps, DropdownMenuPortal, type DropdownMenuPortalProps, type DropdownMenuProps, DropdownMenuRadioGroup, type DropdownMenuRadioGroupProps, DropdownMenuRadioItem, type DropdownMenuRadioItemProps, DropdownMenuSeparator, type DropdownMenuSeparatorProps, DropdownMenuShortcut, type DropdownMenuShortcutProps, DropdownMenuSub, DropdownMenuSubContent, type DropdownMenuSubContentProps, type DropdownMenuSubProps, DropdownMenuSubTrigger, type DropdownMenuSubTriggerProps, DropdownMenuTrigger, type DropdownMenuTriggerProps, DropdownsCheckbox, type DropdownsCheckboxOption, type DropdownsCheckboxProps, DropdownsSelect, type DropdownsSelectOption, type DropdownsSelectProps, EmptyState, type EmptyStateProps, Field, FieldContent, type FieldContentProps, FieldDescription, type FieldDescriptionProps, FieldError, type FieldErrorProps, FieldGroup, type FieldGroupProps, FieldLabel, type FieldLabelProps, FieldLegend, type FieldLegendProps, type FieldProps, FieldSeparator, type FieldSeparatorProps, FieldSet, type FieldSetProps, FieldTitle, type FieldTitleProps, FolderTreeExpandTrigger, type FolderTreeExpandTriggerProps, GroupHeaderRow, type GroupHeaderRowProps, HelperText, type HelperTextProps, Input, InputNumber, type InputNumberProps, InputPassword, type InputPasswordProps, type InputProps, Label, LabelInfo, type LabelInfoProps, type LabelProps, MobileNav, type MobileNavItem, type MobileNavProps, MultiSelect, type MultiSelectOption, type MultiSelectProps, PageHeader, type PageHeaderProps, Pagination, PaginationContent, type PaginationContentProps, PaginationEllipsis, type PaginationEllipsisProps, PaginationItem, type PaginationItemProps, PaginationLink, type PaginationLinkProps, PaginationNext, type PaginationNextProps, PaginationPrevious, type PaginationPreviousProps, type PaginationProps, PaginationWrap, type PaginationWrapProps, Progress, type ProgressProps, RadioGroup, RadioGroupItem, type RadioGroupItemProps, type RadioGroupProps, ScrollArea, type ScrollAreaOrientation, type ScrollAreaProps, ScrollBar, SearchInput, SearchInputFields, type SearchInputFieldsProps, type SearchInputProps, Select, SelectContent, type SelectContentProps, SelectDefault, type SelectDefaultProps, SelectGroup, type SelectGroupProps, SelectItem, type SelectItemProps, SelectLabel, type SelectLabelProps, type SelectProps, SelectScrollDownButton, type SelectScrollDownButtonProps, SelectScrollUpButton, type SelectScrollUpButtonProps, SelectSeparator, type SelectSeparatorProps, SelectTrigger, type SelectTriggerProps, SelectValue, type SelectValueProps, Separator, type SeparatorProps, ServerGroupedTableBody, type ServerGroupedTableBodyProps, Skeleton, type SkeletonProps, SkeletonTable, type SkeletonTableProps, Slider, type SliderProps, SortableHeader, type SortableHeaderProps, Spinner, SpinnerCircle, type SpinnerCircleProps, SpinnerCustom, type SpinnerProps, SpinnerSquare, type SpinnerSquareProps, type SuggestionOption, Suggestions, type SuggestionsProps, Switch, type SwitchProps, type TabItem, Table, TableBody, type TableBodyProps, TableCaption, type TableCaptionProps, TableCell, type TableCellProps, TableFooter, type TableFooterProps, TableHead, type TableHeadProps, TableHeader, type TableHeaderProps, type TableProps, TableRow, type TableRowProps, TableWrap, type TableWrapProps, Tabs, type TabsProps, TextTruncate, type TextTruncateProps, Textarea, type TextareaProps, ThemeToggle, type ThemeToggleProps, Toaster, Tooltip, TooltipContent, type TooltipContentProps, TooltipParent, type TooltipParentProps, type TooltipProps, TooltipProvider, type TooltipProviderProps, TooltipTrigger, type TooltipTriggerProps, TreeGuide, type TreeGuideProps, TreeRow, type TreeRowProps, badgeVariants, buttonVariants };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@socprime/react-ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"description": "SOC Prime React UI component library and Storybook design system.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"engines": {
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"@monaco-editor/react": "^4.7.0",
|
|
49
|
+
"@storybook/addon-docs": "^10.0.0",
|
|
49
50
|
"@storybook/react-vite": "^10.0.0",
|
|
50
51
|
"@tanstack/react-table": "^8.21.0",
|
|
51
52
|
"@xyflow/react": "^12.9.2",
|
|
@@ -62,6 +63,9 @@
|
|
|
62
63
|
"@monaco-editor/react": {
|
|
63
64
|
"optional": true
|
|
64
65
|
},
|
|
66
|
+
"@storybook/addon-docs": {
|
|
67
|
+
"optional": true
|
|
68
|
+
},
|
|
65
69
|
"@storybook/react-vite": {
|
|
66
70
|
"optional": true
|
|
67
71
|
},
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import {
|
|
3
|
+
Controls,
|
|
4
|
+
Description,
|
|
5
|
+
Primary,
|
|
6
|
+
Stories,
|
|
7
|
+
Subtitle,
|
|
8
|
+
Title
|
|
9
|
+
} from "@storybook/addon-docs/blocks";
|
|
10
|
+
const DocsTemplate = () => {
|
|
11
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
12
|
+
/* @__PURE__ */ jsx(Title, {}),
|
|
13
|
+
/* @__PURE__ */ jsx(Subtitle, {}),
|
|
14
|
+
/* @__PURE__ */ jsx(Description, {}),
|
|
15
|
+
/* @__PURE__ */ jsx(Primary, {}),
|
|
16
|
+
/* @__PURE__ */ jsx(Controls, {}),
|
|
17
|
+
/* @__PURE__ */ jsx(Stories, {})
|
|
18
|
+
] });
|
|
19
|
+
};
|
|
20
|
+
export {
|
|
21
|
+
DocsTemplate
|
|
22
|
+
};
|
package/preset/createPreview.js
CHANGED
|
@@ -2,6 +2,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { withRouter, withTheme } from "../decorators/index.js";
|
|
3
3
|
import { Toaster } from "../ui/index.js";
|
|
4
4
|
import { restoreNativeFocus } from "./restoreNativeFocus.js";
|
|
5
|
+
import { DocsTemplate } from "./DocsTemplate.js";
|
|
5
6
|
function createStorybookPreview() {
|
|
6
7
|
return {
|
|
7
8
|
globalTypes: {
|
|
@@ -30,7 +31,8 @@ function createStorybookPreview() {
|
|
|
30
31
|
}
|
|
31
32
|
},
|
|
32
33
|
docs: {
|
|
33
|
-
codePanel: true
|
|
34
|
+
codePanel: true,
|
|
35
|
+
page: DocsTemplate
|
|
34
36
|
},
|
|
35
37
|
backgrounds: { disable: true },
|
|
36
38
|
a11y: {
|
package/ui/Editor/Editor.js
CHANGED
|
@@ -58,8 +58,10 @@ const Editor = ({
|
|
|
58
58
|
});
|
|
59
59
|
}
|
|
60
60
|
};
|
|
61
|
-
const
|
|
62
|
-
|
|
61
|
+
const handleContainerClick = (e) => {
|
|
62
|
+
if (e.target === e.currentTarget) {
|
|
63
|
+
editorRef.current?.focus();
|
|
64
|
+
}
|
|
63
65
|
};
|
|
64
66
|
return /* @__PURE__ */ jsxs(
|
|
65
67
|
"div",
|
|
@@ -76,8 +78,7 @@ const Editor = ({
|
|
|
76
78
|
className: "h-full",
|
|
77
79
|
id,
|
|
78
80
|
"data-name": name,
|
|
79
|
-
|
|
80
|
-
onClick: handleFocus,
|
|
81
|
+
onClick: handleContainerClick,
|
|
81
82
|
children: /* @__PURE__ */ jsx(
|
|
82
83
|
MonacoEditor,
|
|
83
84
|
{
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ChevronDownIcon, Loader2 } from "lucide-react";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
5
|
+
import { Checkbox } from "../Checkbox/Checkbox.js";
|
|
6
|
+
import { ScrollArea } from "../ScrollArea/ScrollArea.js";
|
|
7
|
+
import { cn } from "../../lib/cn.js";
|
|
8
|
+
function BaseMultiSelect({
|
|
9
|
+
options,
|
|
10
|
+
value = [],
|
|
11
|
+
onChange,
|
|
12
|
+
getOptionValue,
|
|
13
|
+
isOptionDisabled,
|
|
14
|
+
renderOption,
|
|
15
|
+
renderValue,
|
|
16
|
+
renderEmpty,
|
|
17
|
+
placeholder = "Select...",
|
|
18
|
+
loading = false,
|
|
19
|
+
disabled = false,
|
|
20
|
+
open,
|
|
21
|
+
onOpenChange,
|
|
22
|
+
className,
|
|
23
|
+
dropdownClassName,
|
|
24
|
+
align = "start",
|
|
25
|
+
sideOffset = 4,
|
|
26
|
+
size = "default",
|
|
27
|
+
id,
|
|
28
|
+
name
|
|
29
|
+
}) {
|
|
30
|
+
const [internalOpen, setInternalOpen] = useState(false);
|
|
31
|
+
const isOpen = open ?? internalOpen;
|
|
32
|
+
const handleOpenChange = (nextOpen) => {
|
|
33
|
+
setInternalOpen(nextOpen);
|
|
34
|
+
onOpenChange?.(nextOpen);
|
|
35
|
+
};
|
|
36
|
+
const toggle = (optionValue) => {
|
|
37
|
+
onChange(
|
|
38
|
+
value.includes(optionValue) ? value.filter((v) => v !== optionValue) : [...value, optionValue]
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
const helpers = {
|
|
42
|
+
remove: (optionValue) => onChange(value.filter((v) => v !== optionValue)),
|
|
43
|
+
clear: () => onChange([])
|
|
44
|
+
};
|
|
45
|
+
const selectedOptions = options.filter((option) => value.includes(getOptionValue(option)));
|
|
46
|
+
const renderOptionItem = (option) => {
|
|
47
|
+
const optionValue = getOptionValue(option);
|
|
48
|
+
const selected = value.includes(optionValue);
|
|
49
|
+
const optionDisabled = isOptionDisabled?.(option) ?? false;
|
|
50
|
+
const state = {
|
|
51
|
+
selected,
|
|
52
|
+
disabled: optionDisabled,
|
|
53
|
+
toggle: () => {
|
|
54
|
+
if (!optionDisabled) toggle(optionValue);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
return /* @__PURE__ */ jsx(
|
|
58
|
+
"div",
|
|
59
|
+
{
|
|
60
|
+
role: "option",
|
|
61
|
+
"aria-selected": selected,
|
|
62
|
+
"aria-disabled": optionDisabled,
|
|
63
|
+
tabIndex: optionDisabled ? -1 : 0,
|
|
64
|
+
onClick: state.toggle,
|
|
65
|
+
onKeyDown: (e) => {
|
|
66
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
67
|
+
e.preventDefault();
|
|
68
|
+
state.toggle();
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
className: cn(
|
|
72
|
+
"flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-xs transition-colors outline-none",
|
|
73
|
+
"hover:bg-hover focus-visible:bg-hover focus-visible:text-default",
|
|
74
|
+
selected && "bg-hover",
|
|
75
|
+
optionDisabled && "cursor-not-allowed opacity-50"
|
|
76
|
+
),
|
|
77
|
+
children: renderOption ? renderOption(option, state) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
78
|
+
/* @__PURE__ */ jsx(
|
|
79
|
+
Checkbox,
|
|
80
|
+
{
|
|
81
|
+
checked: selected,
|
|
82
|
+
disabled: optionDisabled,
|
|
83
|
+
tabIndex: -1,
|
|
84
|
+
className: "pointer-events-none"
|
|
85
|
+
}
|
|
86
|
+
),
|
|
87
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1", children: optionValue })
|
|
88
|
+
] })
|
|
89
|
+
},
|
|
90
|
+
optionValue
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
return /* @__PURE__ */ jsxs(PopoverPrimitive.Root, { open: isOpen, onOpenChange: handleOpenChange, children: [
|
|
94
|
+
/* @__PURE__ */ jsx(PopoverPrimitive.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
95
|
+
"button",
|
|
96
|
+
{
|
|
97
|
+
type: "button",
|
|
98
|
+
id,
|
|
99
|
+
name,
|
|
100
|
+
disabled,
|
|
101
|
+
"data-size": size,
|
|
102
|
+
"data-state": isOpen ? "open" : "closed",
|
|
103
|
+
className: cn(
|
|
104
|
+
'border-border bg-secondary data-[placeholder]:text-gray-chateau [&_svg:not([class*="text-"])]:text-gray-chateau flex min-h-9 w-full items-center justify-between gap-2 rounded-md border px-3 py-1 text-xs font-normal transition-[color,box-shadow] outline-none',
|
|
105
|
+
"data-[size=sm]:min-h-8 data-[size=sm]:px-2",
|
|
106
|
+
"focus-visible:border-success focus-visible:ring-success/40 focus-visible:ring-[2px]",
|
|
107
|
+
"data-[state=open]:border-success data-[state=open]:ring-success/40 data-[state=open]:ring-[2px]",
|
|
108
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
109
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
110
|
+
className
|
|
111
|
+
),
|
|
112
|
+
children: [
|
|
113
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-wrap items-center gap-1", children: selectedOptions.length === 0 ? /* @__PURE__ */ jsx("span", { className: "text-gray-chateau", children: placeholder }) : renderValue ? renderValue(selectedOptions, helpers) : /* @__PURE__ */ jsx("span", { children: selectedOptions.map(getOptionValue).join(", ") }) }),
|
|
114
|
+
loading ? /* @__PURE__ */ jsx(Loader2, { className: "text-gray-chateau size-4 animate-spin" }) : /* @__PURE__ */ jsx(ChevronDownIcon, { className: "ml-2 size-4 shrink-0 opacity-50" })
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
) }),
|
|
118
|
+
/* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx(
|
|
119
|
+
PopoverPrimitive.Content,
|
|
120
|
+
{
|
|
121
|
+
align,
|
|
122
|
+
sideOffset,
|
|
123
|
+
className: cn(
|
|
124
|
+
"bg-secondary border-border text-foreground z-50 w-[var(--radix-popover-trigger-width)] rounded-md border shadow-md outline-none",
|
|
125
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
|
126
|
+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
127
|
+
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
128
|
+
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
|
|
129
|
+
"data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
130
|
+
dropdownClassName
|
|
131
|
+
),
|
|
132
|
+
children: /* @__PURE__ */ jsx(ScrollArea, { children: /* @__PURE__ */ jsx(
|
|
133
|
+
"div",
|
|
134
|
+
{
|
|
135
|
+
role: "listbox",
|
|
136
|
+
"aria-multiselectable": "true",
|
|
137
|
+
className: "flex max-h-64 flex-col gap-2 p-3",
|
|
138
|
+
children: options.length === 0 ? renderEmpty?.() ?? /* @__PURE__ */ jsx("div", { className: "text-gray-chateau py-6 text-center text-xs", children: "Nothing found" }) : options.map(renderOptionItem)
|
|
139
|
+
}
|
|
140
|
+
) })
|
|
141
|
+
}
|
|
142
|
+
) })
|
|
143
|
+
] });
|
|
144
|
+
}
|
|
145
|
+
export {
|
|
146
|
+
BaseMultiSelect
|
|
147
|
+
};
|
|
@@ -37,9 +37,22 @@ function MultiSelect({
|
|
|
37
37
|
() => options.filter((option) => value.includes(option.value)),
|
|
38
38
|
[options, value]
|
|
39
39
|
);
|
|
40
|
-
const
|
|
41
|
-
|
|
40
|
+
const orphanValues = useMemo(
|
|
41
|
+
() => value.filter((v) => !options.some((option) => option.value === v)),
|
|
42
|
+
[options, value]
|
|
43
|
+
);
|
|
44
|
+
const allSelected = useMemo(
|
|
45
|
+
() => [...selectedOptions, ...orphanValues.map((v) => ({ label: v, value: v }))],
|
|
46
|
+
[selectedOptions, orphanValues]
|
|
47
|
+
);
|
|
48
|
+
const displayedBadges = allSelected.slice(0, maxDisplay);
|
|
49
|
+
const remainingCount = allSelected.length - maxDisplay;
|
|
50
|
+
const hiddenLabels = allSelected.slice(maxDisplay).map((option) => option.label).join(", ");
|
|
42
51
|
const isMaxReached = maxSelection !== void 0 && value.length >= maxSelection;
|
|
52
|
+
const handleOpenChange = (nextOpen) => {
|
|
53
|
+
setOpen(nextOpen);
|
|
54
|
+
if (!nextOpen) setSearchQuery("");
|
|
55
|
+
};
|
|
43
56
|
const handleToggle = (optionValue) => {
|
|
44
57
|
const isSelected = value.includes(optionValue);
|
|
45
58
|
if (isSelected) {
|
|
@@ -53,7 +66,7 @@ function MultiSelect({
|
|
|
53
66
|
onChange(value.filter((v) => v !== optionValue));
|
|
54
67
|
};
|
|
55
68
|
const renderSelectedBadges = () => {
|
|
56
|
-
if (
|
|
69
|
+
if (allSelected.length === 0) {
|
|
57
70
|
return /* @__PURE__ */ jsx("span", { className: "text-gray-chateau", children: placeholder });
|
|
58
71
|
}
|
|
59
72
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -67,7 +80,7 @@ function MultiSelect({
|
|
|
67
80
|
},
|
|
68
81
|
option.value
|
|
69
82
|
)),
|
|
70
|
-
remainingCount > 0 && /* @__PURE__ */ jsxs(Badge, { variant: "outline", children: [
|
|
83
|
+
remainingCount > 0 && /* @__PURE__ */ jsxs(Badge, { variant: "outline", title: hiddenLabels, children: [
|
|
71
84
|
"+",
|
|
72
85
|
remainingCount
|
|
73
86
|
] })
|
|
@@ -76,12 +89,26 @@ function MultiSelect({
|
|
|
76
89
|
const renderOptionItem = (option) => {
|
|
77
90
|
const isSelected = value.includes(option.value);
|
|
78
91
|
const isDisabled = option.disabled || !isSelected && isMaxReached;
|
|
92
|
+
const handleSelect = () => {
|
|
93
|
+
if (!isDisabled) handleToggle(option.value);
|
|
94
|
+
};
|
|
79
95
|
return /* @__PURE__ */ jsxs(
|
|
80
|
-
"
|
|
96
|
+
"div",
|
|
81
97
|
{
|
|
98
|
+
role: "option",
|
|
99
|
+
"aria-selected": isSelected,
|
|
100
|
+
"aria-disabled": isDisabled,
|
|
101
|
+
tabIndex: isDisabled ? -1 : 0,
|
|
102
|
+
onClick: handleSelect,
|
|
103
|
+
onKeyDown: (e) => {
|
|
104
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
105
|
+
e.preventDefault();
|
|
106
|
+
handleSelect();
|
|
107
|
+
}
|
|
108
|
+
},
|
|
82
109
|
className: cn(
|
|
83
|
-
"flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-xs transition-colors",
|
|
84
|
-
"hover:bg-hover focus:bg-hover focus:text-default",
|
|
110
|
+
"flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-xs transition-colors outline-none",
|
|
111
|
+
"hover:bg-hover focus-visible:bg-hover focus-visible:text-default",
|
|
85
112
|
isSelected && "bg-hover",
|
|
86
113
|
isDisabled && "cursor-not-allowed opacity-50"
|
|
87
114
|
),
|
|
@@ -90,8 +117,9 @@ function MultiSelect({
|
|
|
90
117
|
Checkbox,
|
|
91
118
|
{
|
|
92
119
|
checked: isSelected,
|
|
93
|
-
|
|
94
|
-
|
|
120
|
+
disabled: isDisabled,
|
|
121
|
+
tabIndex: -1,
|
|
122
|
+
className: "pointer-events-none"
|
|
95
123
|
}
|
|
96
124
|
),
|
|
97
125
|
/* @__PURE__ */ jsxs("span", { className: "flex flex-1 items-center gap-2", children: [
|
|
@@ -103,7 +131,7 @@ function MultiSelect({
|
|
|
103
131
|
option.value
|
|
104
132
|
);
|
|
105
133
|
};
|
|
106
|
-
return /* @__PURE__ */ jsxs(PopoverPrimitive.Root, { open, onOpenChange:
|
|
134
|
+
return /* @__PURE__ */ jsxs(PopoverPrimitive.Root, { open, onOpenChange: handleOpenChange, children: [
|
|
107
135
|
/* @__PURE__ */ jsx(PopoverPrimitive.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
108
136
|
"button",
|
|
109
137
|
{
|
|
@@ -115,6 +143,7 @@ function MultiSelect({
|
|
|
115
143
|
"data-state": open ? "open" : "closed",
|
|
116
144
|
className: cn(
|
|
117
145
|
'border-border bg-secondary data-[placeholder]:text-gray-chateau [&_svg:not([class*="text-"])]:text-gray-chateau flex min-h-9 w-full items-center justify-between gap-2 rounded-md border px-3 py-1 text-xs font-normal transition-[color,box-shadow] outline-none',
|
|
146
|
+
"data-[size=sm]:min-h-8 data-[size=sm]:px-2",
|
|
118
147
|
"focus-visible:border-success focus-visible:ring-success/40 focus-visible:ring-[2px]",
|
|
119
148
|
"data-[state=open]:border-success data-[state=open]:ring-success/40 data-[state=open]:ring-[2px]",
|
|
120
149
|
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
@@ -133,7 +162,7 @@ function MultiSelect({
|
|
|
133
162
|
align,
|
|
134
163
|
sideOffset: 4,
|
|
135
164
|
className: cn(
|
|
136
|
-
"bg-secondary border-border text-foreground z-50 rounded-md border shadow-md outline-none",
|
|
165
|
+
"bg-secondary border-border text-foreground z-50 w-[var(--radix-popover-trigger-width)] rounded-md border shadow-md outline-none",
|
|
137
166
|
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
|
138
167
|
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
139
168
|
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
@@ -152,7 +181,31 @@ function MultiSelect({
|
|
|
152
181
|
placeholder: "Search..."
|
|
153
182
|
}
|
|
154
183
|
),
|
|
155
|
-
|
|
184
|
+
value.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
185
|
+
/* @__PURE__ */ jsxs("span", { className: "text-2xs text-gray-chateau", children: [
|
|
186
|
+
value.length,
|
|
187
|
+
" selected",
|
|
188
|
+
maxSelection !== void 0 && ` / ${maxSelection}`
|
|
189
|
+
] }),
|
|
190
|
+
/* @__PURE__ */ jsx(
|
|
191
|
+
"button",
|
|
192
|
+
{
|
|
193
|
+
type: "button",
|
|
194
|
+
onClick: () => onChange([]),
|
|
195
|
+
className: "text-2xs text-gray-chateau hover:text-default cursor-pointer transition-colors",
|
|
196
|
+
children: "Clear all"
|
|
197
|
+
}
|
|
198
|
+
)
|
|
199
|
+
] }),
|
|
200
|
+
/* @__PURE__ */ jsx(ScrollArea, { children: /* @__PURE__ */ jsx(
|
|
201
|
+
"div",
|
|
202
|
+
{
|
|
203
|
+
role: "listbox",
|
|
204
|
+
"aria-multiselectable": "true",
|
|
205
|
+
className: "flex max-h-64 flex-col gap-2",
|
|
206
|
+
children: filteredOptions.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-gray-chateau py-6 text-center text-xs", children: "Nothing found" }) : filteredOptions.map(renderOptionItem)
|
|
207
|
+
}
|
|
208
|
+
) })
|
|
156
209
|
] })
|
|
157
210
|
}
|
|
158
211
|
) })
|