@neasg/design-system 0.3.0 → 0.4.3

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.
Files changed (94) hide show
  1. package/README.md +37 -1
  2. package/dist/alert.d.ts +15 -0
  3. package/dist/alert.js +24 -0
  4. package/dist/avatar.d.ts +13 -4
  5. package/dist/avatar.js +24 -7
  6. package/dist/back-button.d.ts +6 -0
  7. package/dist/back-button.js +8 -0
  8. package/dist/badge.d.ts +1 -1
  9. package/dist/breadcrumb.js +2 -2
  10. package/dist/button.d.ts +2 -2
  11. package/dist/button.js +3 -4
  12. package/dist/card.d.ts +3 -0
  13. package/dist/card.js +9 -2
  14. package/dist/checkbox.d.ts +13 -0
  15. package/dist/checkbox.js +29 -0
  16. package/dist/collapsible.d.ts +19 -0
  17. package/dist/collapsible.js +20 -0
  18. package/dist/command-search.js +2 -2
  19. package/dist/command.d.ts +1 -1
  20. package/dist/copy-button.d.ts +19 -0
  21. package/dist/copy-button.js +31 -0
  22. package/dist/date-input.js +2 -2
  23. package/dist/dialog-primitive.js +1 -1
  24. package/dist/draggable-tabs.js +1 -1
  25. package/dist/drawer.js +1 -1
  26. package/dist/editable-table.js +2 -2
  27. package/dist/empty-state.js +1 -1
  28. package/dist/field.d.ts +4 -1
  29. package/dist/field.js +3 -3
  30. package/dist/file-upload.d.ts +12 -0
  31. package/dist/file-upload.js +106 -0
  32. package/dist/index.d.ts +47 -6
  33. package/dist/index.js +25 -4
  34. package/dist/input-control.d.ts +1 -1
  35. package/dist/input-control.js +1 -1
  36. package/dist/input.js +2 -2
  37. package/dist/label-value-grid.d.ts +3 -1
  38. package/dist/label-value-grid.js +15 -2
  39. package/dist/label-value.d.ts +4 -1
  40. package/dist/label-value.js +4 -3
  41. package/dist/layout.d.ts +7 -1
  42. package/dist/layout.js +3 -3
  43. package/dist/lib/date-utils.d.ts +9 -0
  44. package/dist/lib/date-utils.js +34 -0
  45. package/dist/link.d.ts +9 -0
  46. package/dist/link.js +19 -0
  47. package/dist/multi-select.d.ts +3 -1
  48. package/dist/multi-select.js +12 -11
  49. package/dist/page-section.d.ts +14 -0
  50. package/dist/page-section.js +13 -0
  51. package/dist/pagination.js +1 -1
  52. package/dist/popover-menu.d.ts +35 -0
  53. package/dist/popover-menu.js +36 -0
  54. package/dist/popover-primitive.d.ts +7 -0
  55. package/dist/popover-primitive.js +11 -0
  56. package/dist/popover.d.ts +10 -5
  57. package/dist/popover.js +6 -9
  58. package/dist/progress.d.ts +10 -0
  59. package/dist/progress.js +17 -0
  60. package/dist/search-input.js +1 -1
  61. package/dist/section-nav.d.ts +22 -0
  62. package/dist/section-nav.js +25 -0
  63. package/dist/select-primitive.js +2 -2
  64. package/dist/select.js +2 -2
  65. package/dist/separator.d.ts +6 -0
  66. package/dist/separator.js +6 -0
  67. package/dist/skeleton.d.ts +4 -0
  68. package/dist/skeleton.js +6 -0
  69. package/dist/spinner.d.ts +22 -0
  70. package/dist/spinner.js +24 -0
  71. package/dist/styles.css +42 -0
  72. package/dist/switch.d.ts +12 -0
  73. package/dist/switch.js +16 -0
  74. package/dist/table.d.ts +7 -1
  75. package/dist/table.js +26 -12
  76. package/dist/tabs.d.ts +24 -1
  77. package/dist/tabs.js +61 -3
  78. package/dist/tailwind-preset.js +9 -0
  79. package/dist/textarea.js +1 -1
  80. package/dist/toaster.js +19 -10
  81. package/dist/typography.d.ts +1 -1
  82. package/dist/use-error-shake.d.ts +7 -0
  83. package/dist/use-error-shake.js +36 -0
  84. package/dist/use-file-upload.d.ts +34 -0
  85. package/dist/use-file-upload.js +133 -0
  86. package/dist/use-sticky-sentinel.d.ts +16 -0
  87. package/dist/use-sticky-sentinel.js +62 -0
  88. package/dist/use-table-sort.d.ts +28 -0
  89. package/dist/use-table-sort.js +53 -0
  90. package/dist/use-viewport-threshold.d.ts +12 -0
  91. package/dist/use-viewport-threshold.js +43 -0
  92. package/package.json +32 -6
  93. package/dist/tab-with-dropdown.d.ts +0 -18
  94. package/dist/tab-with-dropdown.js +0 -70
package/dist/field.js CHANGED
@@ -1,10 +1,10 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { cn } from "./lib/utils";
3
3
  function Field({ className, ...props }) {
4
4
  return (_jsx("div", { "data-slot": "field", className: cn("group/field flex w-full flex-col gap-2", className), ...props }));
5
5
  }
6
- function FieldLabel({ className, ...props }) {
7
- return (_jsx("label", { "data-slot": "field-label", className: cn("text-sm font-medium leading-none group-data-[disabled=true]/field:cursor-not-allowed group-data-[disabled=true]/field:opacity-50 group-data-[invalid=true]/field:text-destructive", className), ...props }));
6
+ function FieldLabel({ className, required, children, ...props }) {
7
+ return (_jsxs("label", { "data-slot": "field-label", className: cn("text-sm font-medium leading-none group-data-[disabled=true]/field:cursor-not-allowed group-data-[disabled=true]/field:opacity-50 group-data-[invalid=true]/field:text-destructive", className), ...props, children: [children, required ? (_jsx("span", { "aria-hidden": "true", className: "ml-0.5 text-destructive", children: "*" })) : null] }));
8
8
  }
9
9
  function FieldDescription({ className, ...props }) {
10
10
  return (_jsx("p", { "data-slot": "field-description", className: cn("text-sm leading-normal text-muted-foreground", className), ...props }));
@@ -0,0 +1,12 @@
1
+ import type { FileUploadProgress } from "./use-file-upload";
2
+ export interface FileUploadProps {
3
+ accept?: string;
4
+ maxSize?: number;
5
+ maxFiles?: number;
6
+ onFilesChange: (files: File[]) => void;
7
+ value?: File[];
8
+ disabled?: boolean;
9
+ className?: string;
10
+ uploadProgress?: Map<string, FileUploadProgress>;
11
+ }
12
+ export declare function FileUpload({ accept, maxSize, maxFiles, onFilesChange, value, disabled, className, uploadProgress, }: FileUploadProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,106 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { useCallback, useState } from "react";
5
+ import { File, ImageIcon } from "lucide-react";
6
+ import { CheckIcon } from "./animated-icons/check";
7
+ import { FileTextIcon } from "./animated-icons/file-text";
8
+ import { UploadIcon } from "./animated-icons/upload";
9
+ import { XIcon } from "./animated-icons/x";
10
+ import { Button } from "./button";
11
+ import { cn } from "./lib/utils";
12
+ import { Progress } from "./progress";
13
+ const DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
14
+ function formatFileSize(bytes) {
15
+ if (bytes === 0)
16
+ return "0 B";
17
+ const k = 1024;
18
+ const sizes = ["B", "KB", "MB", "GB"];
19
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
20
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i];
21
+ }
22
+ function getFileIcon(file) {
23
+ if (file.type.startsWith("image/")) {
24
+ return _jsx(ImageIcon, { className: "h-4 w-4 text-blue-500" });
25
+ }
26
+ if (file.type === "application/pdf") {
27
+ return _jsx(FileTextIcon, { className: "text-red-500", size: 16 });
28
+ }
29
+ return _jsx(File, { className: "h-4 w-4 text-muted-foreground" });
30
+ }
31
+ export function FileUpload({ accept, maxSize = DEFAULT_MAX_FILE_SIZE, maxFiles = 10, onFilesChange, value = [], disabled = false, className, uploadProgress, }) {
32
+ const [isDragOver, setIsDragOver] = useState(false);
33
+ const [error, setError] = useState(null);
34
+ const inputRef = React.useRef(null);
35
+ const validateFiles = useCallback((files) => {
36
+ const validFiles = [];
37
+ const fileArray = Array.from(files);
38
+ for (const file of fileArray) {
39
+ if (value.length + validFiles.length >= maxFiles) {
40
+ setError(`Maximum ${maxFiles} files allowed`);
41
+ break;
42
+ }
43
+ if (file.size > maxSize) {
44
+ setError(`File "${file.name}" exceeds ${formatFileSize(maxSize)} limit`);
45
+ continue;
46
+ }
47
+ if (value.some((f) => f.name === file.name && f.size === file.size)) {
48
+ continue;
49
+ }
50
+ validFiles.push(file);
51
+ }
52
+ return validFiles;
53
+ }, [maxFiles, maxSize, value]);
54
+ const handleFiles = useCallback((files) => {
55
+ setError(null);
56
+ const validFiles = validateFiles(files);
57
+ if (validFiles.length > 0) {
58
+ onFilesChange([...value, ...validFiles]);
59
+ }
60
+ }, [validateFiles, value, onFilesChange]);
61
+ const handleDrop = useCallback((e) => {
62
+ e.preventDefault();
63
+ setIsDragOver(false);
64
+ if (disabled)
65
+ return;
66
+ handleFiles(e.dataTransfer.files);
67
+ }, [disabled, handleFiles]);
68
+ const handleDragOver = useCallback((e) => {
69
+ e.preventDefault();
70
+ setIsDragOver(true);
71
+ }, []);
72
+ const handleDragLeave = useCallback((e) => {
73
+ e.preventDefault();
74
+ setIsDragOver(false);
75
+ }, []);
76
+ const handleInputChange = useCallback((e) => {
77
+ if (e.target.files) {
78
+ handleFiles(e.target.files);
79
+ }
80
+ e.target.value = "";
81
+ }, [handleFiles]);
82
+ const removeFile = useCallback((index) => {
83
+ const newFiles = value.filter((_, i) => i !== index);
84
+ onFilesChange(newFiles);
85
+ setError(null);
86
+ }, [value, onFilesChange]);
87
+ const openFilePicker = useCallback(() => {
88
+ var _a;
89
+ (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.click();
90
+ }, []);
91
+ const fileInput = (_jsx("input", { ref: inputRef, type: "file", accept: accept, multiple: maxFiles > 1, onChange: handleInputChange, disabled: disabled, className: "hidden" }));
92
+ return (_jsxs("div", { className: cn("space-y-3", className), children: [fileInput, _jsxs("div", { onDrop: handleDrop, onDragOver: handleDragOver, onDragLeave: handleDragLeave, onClick: openFilePicker, className: cn("relative flex cursor-pointer flex-col items-center justify-center gap-2 rounded-lg border border-dashed p-6 transition-colors", isDragOver && !disabled
93
+ ? "border-primary bg-primary/5"
94
+ : "border-muted-foreground/25 hover:border-muted-foreground/50", disabled && "cursor-not-allowed opacity-50"), children: [_jsx(UploadIcon, { className: cn(isDragOver ? "text-primary" : "text-muted-foreground"), size: 32 }), _jsxs("div", { className: "text-center", children: [_jsx("p", { className: "text-sm font-medium", children: isDragOver ? "Drop files here" : "Drag & drop files here" }), _jsxs("p", { className: "mt-1 text-xs text-muted-foreground", children: ["or click to browse (max ", formatFileSize(maxSize), " each)"] })] })] }), error ? _jsx("p", { className: "text-sm text-destructive", children: error }) : null, value.length > 0 ? (_jsx("div", { className: "space-y-2", children: value.map((file, index) => {
95
+ var _a;
96
+ const progress = uploadProgress === null || uploadProgress === void 0 ? void 0 : uploadProgress.get(file.name);
97
+ const isUploading = (progress === null || progress === void 0 ? void 0 : progress.status) === "uploading";
98
+ const isComplete = (progress === null || progress === void 0 ? void 0 : progress.status) === "complete";
99
+ const isError = (progress === null || progress === void 0 ? void 0 : progress.status) === "error";
100
+ return (_jsxs("div", { className: "rounded-md border bg-muted/30 px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-3", children: [getFileIcon(file), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate text-sm font-medium", children: file.name }), _jsxs("p", { className: "text-xs text-muted-foreground", children: [formatFileSize(file.size), isUploading && ` • ${progress === null || progress === void 0 ? void 0 : progress.progress}%`, isComplete && " • Complete", isError && ` • ${(progress === null || progress === void 0 ? void 0 : progress.error) || "Error"}`] })] }), isComplete ? (_jsx("div", { className: "flex h-6 w-6 shrink-0 items-center justify-center text-green-500", children: _jsx(CheckIcon, { size: 16 }) })) : (_jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "h-6 w-6 shrink-0", onClick: (e) => {
101
+ e.stopPropagation();
102
+ removeFile(index);
103
+ }, disabled: disabled || isUploading, "aria-label": `Remove ${file.name}`, children: _jsx(XIcon, { size: 16 }) }))] }), (isUploading ||
104
+ (progress && progress.status === "pending")) && (_jsx("div", { className: "mt-2", children: _jsx(Progress, { value: (_a = progress === null || progress === void 0 ? void 0 : progress.progress) !== null && _a !== void 0 ? _a : 0, size: "sm", variant: isError ? "error" : "default", label: `Uploading ${file.name}` }) }))] }, `${file.name}-${file.size}-${index}`));
105
+ }) })) : null] }));
106
+ }
package/dist/index.d.ts CHANGED
@@ -1,11 +1,17 @@
1
- export { Avatar, AvatarProfile, AvatarRoot, AvatarImage, AvatarFallback, } from "./avatar";
1
+ export { Avatar, AvatarProfile, AvatarRoot, AvatarImage, } from "./avatar";
2
2
  export type { AvatarProps, AvatarProfileProps, AvatarRootProps, } from "./avatar";
3
3
  export * from "./animated-icons";
4
+ export { Alert, alertVariants } from "./alert";
5
+ export type { AlertProps } from "./alert";
4
6
  export { Badge, badgeVariants } from "./badge";
5
7
  export { Card } from "./card";
6
8
  export type { CardProps } from "./card";
7
9
  export type { BadgeProps } from "./badge";
8
10
  export { Calendar, CalendarDayButton } from "./calendar";
11
+ export { Checkbox } from "./checkbox";
12
+ export type { CheckboxProps } from "./checkbox";
13
+ export { Switch } from "./switch";
14
+ export type { SwitchProps } from "./switch";
9
15
  export { DateInput } from "./date-input";
10
16
  export type { DateInputProps } from "./date-input";
11
17
  export { CountBadge } from "./count-badge";
@@ -32,26 +38,50 @@ export { LabelValueGrid } from "./label-value-grid";
32
38
  export type { LabelValueGridItem, LabelValueGridProps, } from "./label-value-grid";
33
39
  export { Breadcrumb, BreadcrumbNav, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis, } from "./breadcrumb";
34
40
  export type { BreadcrumbEntry, BreadcrumbProps } from "./breadcrumb";
41
+ export { BackButton } from "./back-button";
42
+ export type { BackButtonProps } from "./back-button";
35
43
  export { Button, buttonVariants } from "./button";
36
44
  export type { ButtonProps } from "./button";
45
+ export { Link, linkVariants } from "./link";
46
+ export type { LinkProps } from "./link";
37
47
  export { Dialog } from "./dialog";
38
48
  export type { DialogAction, DialogProps } from "./dialog";
39
49
  export { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator, CommandShortcut, } from "./command";
40
50
  export { CommandSearch, type CommandSearchFilter, type CommandSearchItem, type CommandSearchProps, type CommandSearchSection, } from "./command-search";
51
+ export { FileUpload } from "./file-upload";
52
+ export type { FileUploadProps } from "./file-upload";
53
+ export { Progress } from "./progress";
54
+ export type { ProgressProps } from "./progress";
55
+ export { useFileUpload } from "./use-file-upload";
56
+ export type { FileUploadProgress, FileUploadResult, FileUploadStatus, UseFileUploadOptions, UseFileUploadReturn, } from "./use-file-upload";
57
+ export { Popover } from "./popover";
58
+ export type { PopoverProps } from "./popover";
59
+ export { PopoverMenuContent, PopoverMenuHeader, PopoverMenuSection, PopoverMenuItem, PopoverMenuDivider, PopoverMenuGroup, PopoverMenuGroupLabel, PopoverMenuSearch, PopoverMenuScrollArea, } from "./popover-menu";
60
+ export type { PopoverMenuContentProps, PopoverMenuHeaderProps, PopoverMenuSectionProps, PopoverMenuItemProps, PopoverMenuDividerProps, PopoverMenuGroupProps, PopoverMenuGroupLabelProps, PopoverMenuSearchProps, PopoverMenuScrollAreaProps, } from "./popover-menu";
41
61
  export { SearchInput } from "./search-input";
42
62
  export type { SearchInputProps } from "./search-input";
63
+ export { SectionNav } from "./section-nav";
64
+ export type { SectionNavItem, SectionNavProps } from "./section-nav";
43
65
  export { GovtMasthead } from "./govt-masthead";
44
66
  export type { GovtMastheadProps } from "./govt-masthead";
45
67
  export { SearchHighlight, useSearchHighlight, } from "./use-search-highlight";
46
68
  export type { SearchHighlightProps, SearchHighlightSegment, } from "./use-search-highlight";
69
+ export { useErrorShake } from "./use-error-shake";
70
+ export type { UseErrorShakeOptions } from "./use-error-shake";
71
+ export { useStickySentinel } from "./use-sticky-sentinel";
72
+ export type { UseStickySentinelOptions, UseStickySentinelReturn, } from "./use-sticky-sentinel";
73
+ export { useViewportThreshold } from "./use-viewport-threshold";
74
+ export type { UseViewportThresholdOptions, UseViewportThresholdReturn, } from "./use-viewport-threshold";
47
75
  export { Textarea } from "./textarea";
48
76
  export type { TextareaProps } from "./textarea";
49
77
  export { DraggableTabs, type DraggableTabItem, type DraggableTabsProps, } from "./draggable-tabs";
50
78
  export { Drawer, } from "./drawer";
51
79
  export type { DrawerAction, DrawerProps, DrawerSide } from "./drawer";
52
- export { DashboardLayout, DashboardSidebar, DashboardContent, SidebarProvider, useSidebar, } from "./layout";
53
- export type { DashboardSidebarNavGroup, DashboardSidebarNavItem, } from "./layout";
80
+ export { DashboardLayout, DashboardSidebar, useSidebar, } from "./layout";
81
+ export type { DashboardLayoutProps, DashboardSidebarNavGroup, DashboardSidebarNavItem, } from "./layout";
54
82
  export { PageHeader } from "./page-header";
83
+ export { PageSection, PageSectionGroup } from "./page-section";
84
+ export type { PageSectionGroupProps, PageSectionProps } from "./page-section";
55
85
  export { Typography, typographyVariants } from "./typography";
56
86
  export type { TypographyProps } from "./typography";
57
87
  export { neaPalette, semanticColorTokens, typographyTokens, radiusTokens, spacingTokens, layoutTokens, } from "./theme";
@@ -60,12 +90,23 @@ export { Pagination } from "./pagination";
60
90
  export type { PaginationProps } from "./pagination";
61
91
  export { TableToolbar } from "./table-toolbar";
62
92
  export type { TableToolbarProps } from "./table-toolbar";
93
+ export { useTableSort } from "./use-table-sort";
94
+ export type { UseTableSortOptions, UseTableSortReturn, } from "./use-table-sort";
63
95
  export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, TableRowSkeleton, TABLE_SKELETON_PRESETS, } from "./table";
64
96
  export type { TableColumn, TableProps, TableSortDirection, TableSortingProps, TableSkeletonConfig, TableRowSkeletonProps, } from "./table";
65
- export { Tabs, TabsRoot, type TabsItem, type TabsProps, TabsList, TabsTrigger, TabsContent, useTabsContext, } from "./tabs";
66
- export { TabWithDropdown } from "./tab-with-dropdown";
67
- export type { TabWithDropdownProps, TabDropdownSection } from "./tab-with-dropdown";
97
+ export { Tabs, TabsRoot, TabWithDropdown, type TabsItem, type TabsProps, TabsList, TabsTrigger, TabsContent, type TabWithDropdownProps, useTabsContext, type TabDropdownSection, } from "./tabs";
68
98
  export { Tooltip, TooltipRoot, TooltipTrigger, TooltipContent, TooltipProvider, type TooltipProps, } from "./tooltip";
69
99
  export { Toaster, toast } from "./toaster";
70
100
  export type { ToasterProps } from "./toaster";
101
+ export { Separator } from "./separator";
102
+ export type { SeparatorProps } from "./separator";
103
+ export { Skeleton } from "./skeleton";
104
+ export type { SkeletonProps } from "./skeleton";
105
+ export { CopyButton } from "./copy-button";
106
+ export type { CopyButtonProps } from "./copy-button";
107
+ export { Spinner, PageLoader, FullPageLoader } from "./spinner";
108
+ export type { SpinnerProps, PageLoaderProps, FullPageLoaderProps, } from "./spinner";
109
+ export { Collapsible, CollapsibleSection } from "./collapsible";
110
+ export type { CollapsibleProps } from "./collapsible";
71
111
  export { cn } from "./lib/utils";
112
+ export { parseDateTime, formatDateTime } from "./lib/date-utils";
package/dist/index.js CHANGED
@@ -1,8 +1,11 @@
1
- export { Avatar, AvatarProfile, AvatarRoot, AvatarImage, AvatarFallback, } from "./avatar";
1
+ export { Avatar, AvatarProfile, AvatarRoot, AvatarImage, } from "./avatar";
2
2
  export * from "./animated-icons";
3
+ export { Alert, alertVariants } from "./alert";
3
4
  export { Badge, badgeVariants } from "./badge";
4
5
  export { Card } from "./card";
5
6
  export { Calendar, CalendarDayButton } from "./calendar";
7
+ export { Checkbox } from "./checkbox";
8
+ export { Switch } from "./switch";
6
9
  export { DateInput } from "./date-input";
7
10
  export { CountBadge } from "./count-badge";
8
11
  export { EmptyState } from "./empty-state";
@@ -16,26 +19,44 @@ export { OtpInput } from "./otp-input";
16
19
  export { LabelValue, isEmptyValue, } from "./label-value";
17
20
  export { LabelValueGrid } from "./label-value-grid";
18
21
  export { Breadcrumb, BreadcrumbNav, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis, } from "./breadcrumb";
22
+ export { BackButton } from "./back-button";
19
23
  export { Button, buttonVariants } from "./button";
24
+ export { Link, linkVariants } from "./link";
20
25
  export { Dialog } from "./dialog";
21
26
  export { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator, CommandShortcut, } from "./command";
22
27
  export { CommandSearch, } from "./command-search";
28
+ export { FileUpload } from "./file-upload";
29
+ export { Progress } from "./progress";
30
+ export { useFileUpload } from "./use-file-upload";
31
+ export { Popover } from "./popover";
32
+ export { PopoverMenuContent, PopoverMenuHeader, PopoverMenuSection, PopoverMenuItem, PopoverMenuDivider, PopoverMenuGroup, PopoverMenuGroupLabel, PopoverMenuSearch, PopoverMenuScrollArea, } from "./popover-menu";
23
33
  export { SearchInput } from "./search-input";
34
+ export { SectionNav } from "./section-nav";
24
35
  export { GovtMasthead } from "./govt-masthead";
25
36
  export { SearchHighlight, useSearchHighlight, } from "./use-search-highlight";
37
+ export { useErrorShake } from "./use-error-shake";
38
+ export { useStickySentinel } from "./use-sticky-sentinel";
39
+ export { useViewportThreshold } from "./use-viewport-threshold";
26
40
  export { Textarea } from "./textarea";
27
41
  export { DraggableTabs, } from "./draggable-tabs";
28
42
  export { Drawer, } from "./drawer";
29
- export { DashboardLayout, DashboardSidebar, DashboardContent, SidebarProvider, useSidebar, } from "./layout";
43
+ export { DashboardLayout, DashboardSidebar, useSidebar, } from "./layout";
30
44
  export { PageHeader } from "./page-header";
45
+ export { PageSection, PageSectionGroup } from "./page-section";
31
46
  export { Typography, typographyVariants } from "./typography";
32
47
  export { neaPalette, semanticColorTokens, typographyTokens, radiusTokens, spacingTokens, layoutTokens, } from "./theme";
33
48
  export { neaDesignSystemContent, neaTailwindPreset, } from "./tailwind-preset";
34
49
  export { Pagination } from "./pagination";
35
50
  export { TableToolbar } from "./table-toolbar";
51
+ export { useTableSort } from "./use-table-sort";
36
52
  export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, TableRowSkeleton, TABLE_SKELETON_PRESETS, } from "./table";
37
- export { Tabs, TabsRoot, TabsList, TabsTrigger, TabsContent, useTabsContext, } from "./tabs";
38
- export { TabWithDropdown } from "./tab-with-dropdown";
53
+ export { Tabs, TabsRoot, TabWithDropdown, TabsList, TabsTrigger, TabsContent, useTabsContext, } from "./tabs";
39
54
  export { Tooltip, TooltipRoot, TooltipTrigger, TooltipContent, TooltipProvider, } from "./tooltip";
40
55
  export { Toaster, toast } from "./toaster";
56
+ export { Separator } from "./separator";
57
+ export { Skeleton } from "./skeleton";
58
+ export { CopyButton } from "./copy-button";
59
+ export { Spinner, PageLoader, FullPageLoader } from "./spinner";
60
+ export { Collapsible, CollapsibleSection } from "./collapsible";
41
61
  export { cn } from "./lib/utils";
62
+ export { parseDateTime, formatDateTime } from "./lib/date-utils";
@@ -1,4 +1,4 @@
1
1
  import * as React from "react";
2
- declare const inputControlClassName = "h-10 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-8 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-[invalid=true]:border-destructive aria-[invalid=true]:ring-destructive/20 md:text-sm";
2
+ declare const inputControlClassName = "h-control w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-sm transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-8 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-[invalid=true]:border-destructive aria-[invalid=true]:ring-destructive/20";
3
3
  declare const InputControl: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref"> & React.RefAttributes<HTMLInputElement>>;
4
4
  export { InputControl, inputControlClassName };
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { cn } from "./lib/utils";
4
- const inputControlClassName = "h-10 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-8 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-[invalid=true]:border-destructive aria-[invalid=true]:ring-destructive/20 md:text-sm";
4
+ const inputControlClassName = "h-control w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-sm transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-8 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-[invalid=true]:border-destructive aria-[invalid=true]:ring-destructive/20";
5
5
  const InputControl = React.forwardRef(({ className, type = "text", ...props }, ref) => (_jsx("input", { ref: ref, type: type, "data-slot": "input", className: cn(inputControlClassName, className), ...props })));
6
6
  InputControl.displayName = "InputControl";
7
7
  export { InputControl, inputControlClassName };
package/dist/input.js CHANGED
@@ -3,7 +3,7 @@ import * as React from "react";
3
3
  import { Field, FieldDescription, FieldError, FieldLabel } from "./field";
4
4
  import { InputControl } from "./input-control";
5
5
  import { cn } from "./lib/utils";
6
- const Input = React.forwardRef(({ label, description, error, invalid = false, hideLabel = false, className, inputClassName, labelClassName, descriptionClassName, errorClassName, id, disabled, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, ...props }, ref) => {
6
+ const Input = React.forwardRef(({ label, description, error, invalid = false, hideLabel = false, className, inputClassName, labelClassName, descriptionClassName, errorClassName, id, disabled, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, required, ...props }, ref) => {
7
7
  const generatedId = React.useId();
8
8
  const inputId = id !== null && id !== void 0 ? id : `input-${generatedId}`;
9
9
  const errorId = error ? `${inputId}-error` : undefined;
@@ -16,7 +16,7 @@ const Input = React.forwardRef(({ label, description, error, invalid = false, hi
16
16
  const describedBy = [ariaDescribedBy, descriptionId, errorId]
17
17
  .filter(Boolean)
18
18
  .join(" ") || undefined;
19
- return (_jsxs(Field, { className: className, "data-disabled": disabled ? "true" : undefined, "data-invalid": isInvalid ? "true" : undefined, children: [_jsx(FieldLabel, { htmlFor: inputId, className: cn(hideLabel && "sr-only", labelClassName), children: label }), _jsx(InputControl, { ref: ref, id: inputId, disabled: disabled, "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": describedBy, className: inputClassName, ...props }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
19
+ return (_jsxs(Field, { className: className, "data-disabled": disabled ? "true" : undefined, "data-invalid": isInvalid ? "true" : undefined, children: [_jsx(FieldLabel, { htmlFor: inputId, required: required, className: cn(hideLabel && "sr-only", labelClassName), children: label }), _jsx(InputControl, { ref: ref, id: inputId, disabled: disabled, required: required, "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": describedBy, className: inputClassName, ...props }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
20
20
  });
21
21
  Input.displayName = "Input";
22
22
  export { Input };
@@ -8,6 +8,8 @@ export interface LabelValueGridProps extends React.HTMLAttributes<HTMLDivElement
8
8
  columns?: 1 | 2 | 3 | 4;
9
9
  itemClassName?: string;
10
10
  minColumnWidth?: number;
11
+ loading?: boolean;
12
+ loadingItems?: number;
11
13
  }
12
- declare function LabelValueGrid({ items, columns, className, itemClassName, minColumnWidth, style, ...props }: LabelValueGridProps): import("react/jsx-runtime").JSX.Element;
14
+ declare function LabelValueGrid({ items, columns, className, itemClassName, minColumnWidth, loading, loadingItems, style, ...props }: LabelValueGridProps): import("react/jsx-runtime").JSX.Element;
13
15
  export { LabelValueGrid };
@@ -11,7 +11,20 @@ function getAvailableWidth(element) {
11
11
  var _a, _b, _c, _d, _e, _f;
12
12
  return Math.max(element.getBoundingClientRect().width, element.clientWidth, element.offsetWidth, (_b = (_a = element.parentElement) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().width) !== null && _b !== void 0 ? _b : 0, (_d = (_c = element.parentElement) === null || _c === void 0 ? void 0 : _c.clientWidth) !== null && _d !== void 0 ? _d : 0, (_f = (_e = element.parentElement) === null || _e === void 0 ? void 0 : _e.offsetWidth) !== null && _f !== void 0 ? _f : 0);
13
13
  }
14
- function LabelValueGrid({ items, columns = 4, className, itemClassName, minColumnWidth = 192, style, ...props }) {
14
+ function LabelValueGrid({ items, columns = 4, className, itemClassName, minColumnWidth = 192, loading = false, loadingItems = 4, style, ...props }) {
15
+ const resolvedItems = React.useMemo(() => {
16
+ if (!loading)
17
+ return items;
18
+ if (items.length > 0) {
19
+ return items.map((item) => ({ ...item, loading: true }));
20
+ }
21
+ return Array.from({ length: loadingItems }).map((_, index) => ({
22
+ id: `skeleton-${index}`,
23
+ label: "",
24
+ value: null,
25
+ loading: true,
26
+ }));
27
+ }, [items, loading, loadingItems]);
15
28
  const containerRef = React.useRef(null);
16
29
  const [resolvedColumns, setResolvedColumns] = React.useState(columns);
17
30
  React.useLayoutEffect(() => {
@@ -53,7 +66,7 @@ function LabelValueGrid({ items, columns = 4, className, itemClassName, minColum
53
66
  return (_jsx("div", { ref: containerRef, className: cn("grid auto-rows-fr items-start gap-x-6 gap-y-3", className), style: {
54
67
  ...style,
55
68
  gridTemplateColumns: `repeat(${resolvedColumns}, minmax(0, 1fr))`,
56
- }, ...props, children: items.map((item, index) => {
69
+ }, ...props, children: resolvedItems.map((item, index) => {
57
70
  var _a;
58
71
  return (_jsx(LabelValue, { ...item, className: cn("min-w-0", itemClassName, item.className) }, (_a = item.id) !== null && _a !== void 0 ? _a : `${item.label}-${index}`));
59
72
  }) }));
@@ -5,7 +5,10 @@ export interface LabelValueProps {
5
5
  className?: string;
6
6
  labelClassName?: string;
7
7
  hideIfEmpty?: boolean;
8
+ hideLabel?: boolean;
9
+ loading?: boolean;
10
+ required?: boolean;
8
11
  }
9
12
  export declare function isEmptyValue(value: React.ReactNode): boolean;
10
- declare function LabelValue({ label, value, className, labelClassName, hideIfEmpty, }: LabelValueProps): import("react/jsx-runtime").JSX.Element | null;
13
+ declare function LabelValue({ label, value, className, labelClassName, hideIfEmpty, hideLabel, loading, required, }: LabelValueProps): import("react/jsx-runtime").JSX.Element | null;
11
14
  export { LabelValue };
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { cn } from "./lib/utils";
3
+ import { Skeleton } from "./skeleton";
3
4
  import { Typography } from "./typography";
4
5
  export function isEmptyValue(value) {
5
6
  if (value === null || value === undefined || value === "")
@@ -10,11 +11,11 @@ export function isEmptyValue(value) {
10
11
  }
11
12
  return false;
12
13
  }
13
- function LabelValue({ label, value, className, labelClassName, hideIfEmpty = false, }) {
14
+ function LabelValue({ label, value, className, labelClassName, hideIfEmpty = false, hideLabel = false, loading = false, required = false, }) {
14
15
  const empty = isEmptyValue(value);
15
- if (hideIfEmpty && empty) {
16
+ if (!loading && hideIfEmpty && empty) {
16
17
  return null;
17
18
  }
18
- return (_jsxs("div", { className: cn("min-w-0 h-full space-y-1", className), children: [_jsx(Typography, { as: "p", variant: "label", className: labelClassName, children: label }), _jsx("div", { className: cn("min-w-0 break-words text-sm text-foreground", empty && "italic text-muted-foreground/60"), children: empty ? "-" : value })] }));
19
+ return (_jsxs("div", { className: cn("min-w-0 h-full space-y-1", className), children: [!hideLabel && (loading ? (_jsx(Skeleton, { className: "h-3.5 w-20" })) : (_jsxs(Typography, { as: "p", variant: "label", className: cn("leading-tight", labelClassName), children: [label, required ? (_jsx("span", { "aria-hidden": "true", className: "ml-0.5 text-destructive", children: "*" })) : null] }))), loading ? (_jsx(Skeleton, { className: "h-4 w-32" })) : (_jsx("div", { className: cn("min-w-0 break-words text-sm text-foreground", empty && "italic text-muted-foreground/60"), children: empty ? "-" : value }))] }));
19
20
  }
20
21
  export { LabelValue };
package/dist/layout.d.ts CHANGED
@@ -11,8 +11,14 @@ export declare function useSidebar(): SidebarContextType;
11
11
  export interface DashboardLayoutProps extends React.HTMLAttributes<HTMLDivElement> {
12
12
  masthead?: React.ReactNode;
13
13
  prototypeBanner?: React.ReactNode;
14
+ /** Sidebar element, typically a <DashboardSidebar />. */
15
+ sidebar?: React.ReactNode;
16
+ /** Class names applied to the inner content surface (the rounded card around children). */
17
+ surfaceClassName?: string;
18
+ /** Class names applied to the content `<main>` wrapper. */
19
+ contentClassName?: string;
14
20
  }
15
- declare function DashboardLayout({ className, masthead, prototypeBanner, children, style, ...props }: DashboardLayoutProps): import("react/jsx-runtime").JSX.Element;
21
+ declare function DashboardLayout({ className, masthead, prototypeBanner, sidebar, surfaceClassName, contentClassName, children, style, ...props }: DashboardLayoutProps): import("react/jsx-runtime").JSX.Element;
16
22
  export interface DashboardSidebarProps extends React.HTMLAttributes<HTMLElement> {
17
23
  logo?: React.ReactNode | ((context: {
18
24
  isCollapsed: boolean;
package/dist/layout.js CHANGED
@@ -44,7 +44,7 @@ export function useSidebar() {
44
44
  }
45
45
  return context;
46
46
  }
47
- function DashboardLayout({ className, masthead, prototypeBanner, children, style, ...props }) {
47
+ function DashboardLayout({ className, masthead, prototypeBanner, sidebar, surfaceClassName, contentClassName, children, style, ...props }) {
48
48
  const chromeRef = React.useRef(null);
49
49
  const [topOffset, setTopOffset] = React.useState(0);
50
50
  const layoutStyle = React.useMemo(() => ({
@@ -69,7 +69,7 @@ function DashboardLayout({ className, masthead, prototypeBanner, children, style
69
69
  observer.observe(node);
70
70
  return () => observer.disconnect();
71
71
  }, [masthead, prototypeBanner]);
72
- return (_jsxs("div", { className: cn("flex min-h-screen flex-col bg-[hsl(var(--layout-shell-background))]", className), style: layoutStyle, ...props, children: [masthead || prototypeBanner ? (_jsxs("div", { ref: chromeRef, className: "sticky top-0 z-40 shrink-0", children: [masthead, prototypeBanner] })) : null, _jsx("div", { className: "flex min-h-0 flex-1", children: children })] }));
72
+ return (_jsx(SidebarProvider, { children: _jsxs("div", { className: cn("flex min-h-screen flex-col bg-[hsl(var(--layout-shell-background))]", className), style: layoutStyle, ...props, children: [masthead || prototypeBanner ? (_jsxs("div", { ref: chromeRef, className: "sticky top-0 z-40 shrink-0", children: [masthead, prototypeBanner] })) : null, _jsxs("div", { className: "flex min-h-0 flex-1", children: [sidebar, _jsx(DashboardContent, { className: contentClassName, surfaceClassName: surfaceClassName, children: children })] })] }) }));
73
73
  }
74
74
  function SidebarShell({ isCollapsed, className, children, ...props }) {
75
75
  return (_jsx("aside", { className: cn("fixed left-0 top-[var(--layout-top-offset)] z-30 flex h-[calc(100dvh-var(--layout-top-offset))] shrink-0 flex-col overflow-hidden bg-[hsl(var(--layout-shell-background))] pb-2 pl-[var(--layout-sidebar-padding-inline)] pt-[var(--layout-sidebar-padding-top)] transition-all duration-300 ease-in-out", isCollapsed
@@ -98,7 +98,7 @@ function SidebarMenuItem({ className, children, }) {
98
98
  return _jsx("li", { className: className, children: children });
99
99
  }
100
100
  function SidebarMenuButton({ active = false, collapsed = false, disabled = false, className, asChild = false, children, ...props }) {
101
- return (_jsx(Button, { asChild: asChild, variant: "ghost", disabled: disabled, className: cn("h-10 w-full justify-start gap-3 rounded-lg text-sm font-medium shadow-none", collapsed ? "justify-center px-3" : "px-3", disabled
101
+ return (_jsx(Button, { asChild: asChild, variant: "ghost", disabled: disabled, className: cn("h-control w-full justify-start gap-3 rounded-lg text-sm font-medium shadow-none", collapsed ? "justify-center px-3" : "px-3", disabled
102
102
  ? "text-muted-foreground/50 hover:bg-transparent hover:text-muted-foreground/50"
103
103
  : active
104
104
  ? "bg-white text-foreground hover:bg-white hover:text-foreground"
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Parse a date string in DD/MM/YYYY HH:MM AM/PM format to a timestamp (ms).
3
+ */
4
+ export declare function parseDateTime(dateStr: string): number;
5
+ /**
6
+ * Format a Date object or ISO string to DD/MM/YYYY HH:MM AM/PM format.
7
+ * Returns `"-"` for null/undefined/invalid input.
8
+ */
9
+ export declare function formatDateTime(date: Date | string | null | undefined): string;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Parse a date string in DD/MM/YYYY HH:MM AM/PM format to a timestamp (ms).
3
+ */
4
+ export function parseDateTime(dateStr) {
5
+ const [datePart, timePart, ampm] = dateStr.split(" ");
6
+ const [day, month, year] = datePart.split("/");
7
+ const [hours, minutes] = timePart.split(":");
8
+ let hour = parseInt(hours);
9
+ if (ampm === "PM" && hour !== 12)
10
+ hour += 12;
11
+ if (ampm === "AM" && hour === 12)
12
+ hour = 0;
13
+ return new Date(parseInt(year), parseInt(month) - 1, parseInt(day), hour, parseInt(minutes)).getTime();
14
+ }
15
+ /**
16
+ * Format a Date object or ISO string to DD/MM/YYYY HH:MM AM/PM format.
17
+ * Returns `"-"` for null/undefined/invalid input.
18
+ */
19
+ export function formatDateTime(date) {
20
+ if (!date)
21
+ return "-";
22
+ const dateObj = typeof date === "string" ? new Date(date) : date;
23
+ if (isNaN(dateObj.getTime()))
24
+ return "-";
25
+ const day = dateObj.getDate().toString().padStart(2, "0");
26
+ const month = (dateObj.getMonth() + 1).toString().padStart(2, "0");
27
+ const year = dateObj.getFullYear();
28
+ let hours = dateObj.getHours();
29
+ const minutes = dateObj.getMinutes().toString().padStart(2, "0");
30
+ const ampm = hours >= 12 ? "PM" : "AM";
31
+ hours = hours % 12;
32
+ hours = hours ? hours : 12;
33
+ return `${day}/${month}/${year} ${hours}:${minutes} ${ampm}`;
34
+ }
package/dist/link.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ import * as React from "react";
2
+ import { type VariantProps } from "class-variance-authority";
3
+ declare const linkVariants: (props?: ({
4
+ variant?: "inherit" | "default" | "subtle" | null | undefined;
5
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
+ export interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement>, VariantProps<typeof linkVariants> {
7
+ }
8
+ declare const Link: React.ForwardRefExoticComponent<LinkProps & React.RefAttributes<HTMLAnchorElement>>;
9
+ export { Link, linkVariants };
package/dist/link.js ADDED
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cva } from "class-variance-authority";
4
+ import { cn } from "./lib/utils";
5
+ const linkVariants = cva("rounded-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", {
6
+ variants: {
7
+ variant: {
8
+ default: "text-primary underline-offset-4 hover:underline",
9
+ subtle: "text-foreground underline-offset-4 hover:text-primary hover:underline",
10
+ inherit: "text-inherit no-underline hover:no-underline",
11
+ },
12
+ },
13
+ defaultVariants: {
14
+ variant: "default",
15
+ },
16
+ });
17
+ const Link = React.forwardRef(({ className, variant, ...props }, ref) => (_jsx("a", { ref: ref, className: cn(linkVariants({ variant }), className), ...props })));
18
+ Link.displayName = "Link";
19
+ export { Link, linkVariants };
@@ -18,6 +18,8 @@ export interface MultiSelectProps {
18
18
  hideLabel?: boolean;
19
19
  searchable?: boolean;
20
20
  searchPlaceholder?: string;
21
+ /** Cap how many selected tags render in the trigger; the rest collapse into a `+N` badge. */
22
+ maxVisibleTags?: number;
21
23
  className?: string;
22
24
  triggerClassName?: string;
23
25
  labelClassName?: string;
@@ -25,5 +27,5 @@ export interface MultiSelectProps {
25
27
  errorClassName?: string;
26
28
  id?: string;
27
29
  }
28
- declare function MultiSelect({ label, options, value, onValueChange, placeholder, description, error, invalid, disabled, hideLabel, searchable, searchPlaceholder, className, triggerClassName, labelClassName, descriptionClassName, errorClassName, id, }: MultiSelectProps): import("react/jsx-runtime").JSX.Element;
30
+ declare function MultiSelect({ label, options, value, onValueChange, placeholder, description, error, invalid, disabled, hideLabel, searchable, searchPlaceholder, maxVisibleTags, className, triggerClassName, labelClassName, descriptionClassName, errorClassName, id, }: MultiSelectProps): import("react/jsx-runtime").JSX.Element;
29
31
  export { MultiSelect };