@neasg/design-system 0.3.0 → 0.4.2
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/README.md +37 -1
- package/dist/alert.d.ts +15 -0
- package/dist/alert.js +24 -0
- package/dist/avatar.d.ts +5 -2
- package/dist/avatar.js +8 -4
- package/dist/back-button.d.ts +6 -0
- package/dist/back-button.js +8 -0
- package/dist/badge.d.ts +1 -1
- package/dist/breadcrumb.js +2 -2
- package/dist/button.d.ts +2 -2
- package/dist/button.js +3 -4
- package/dist/card.d.ts +3 -0
- package/dist/card.js +9 -2
- package/dist/checkbox.d.ts +13 -0
- package/dist/checkbox.js +29 -0
- package/dist/collapsible.d.ts +19 -0
- package/dist/collapsible.js +20 -0
- package/dist/command-search.js +2 -2
- package/dist/copy-button.d.ts +19 -0
- package/dist/copy-button.js +31 -0
- package/dist/date-input.js +2 -2
- package/dist/dialog-primitive.js +1 -1
- package/dist/draggable-tabs.js +1 -1
- package/dist/drawer.js +1 -1
- package/dist/editable-table.js +2 -2
- package/dist/empty-state.js +1 -1
- package/dist/field.d.ts +4 -1
- package/dist/field.js +3 -3
- package/dist/file-upload.d.ts +12 -0
- package/dist/file-upload.js +106 -0
- package/dist/index.d.ts +47 -6
- package/dist/index.js +25 -4
- package/dist/input-control.d.ts +1 -1
- package/dist/input-control.js +1 -1
- package/dist/input.js +2 -2
- package/dist/label-value-grid.d.ts +3 -1
- package/dist/label-value-grid.js +15 -2
- package/dist/label-value.d.ts +4 -1
- package/dist/label-value.js +4 -3
- package/dist/layout.d.ts +7 -1
- package/dist/layout.js +3 -3
- package/dist/lib/date-utils.d.ts +9 -0
- package/dist/lib/date-utils.js +34 -0
- package/dist/link.d.ts +9 -0
- package/dist/link.js +19 -0
- package/dist/multi-select.d.ts +3 -1
- package/dist/multi-select.js +12 -11
- package/dist/page-section.d.ts +14 -0
- package/dist/page-section.js +13 -0
- package/dist/pagination.js +1 -1
- package/dist/popover-menu.d.ts +35 -0
- package/dist/popover-menu.js +36 -0
- package/dist/popover-primitive.d.ts +7 -0
- package/dist/popover-primitive.js +11 -0
- package/dist/popover.d.ts +10 -5
- package/dist/popover.js +6 -9
- package/dist/progress.d.ts +10 -0
- package/dist/progress.js +17 -0
- package/dist/search-input.js +1 -1
- package/dist/section-nav.d.ts +22 -0
- package/dist/section-nav.js +25 -0
- package/dist/select-primitive.js +2 -2
- package/dist/select.js +2 -2
- package/dist/separator.d.ts +6 -0
- package/dist/separator.js +6 -0
- package/dist/skeleton.d.ts +4 -0
- package/dist/skeleton.js +6 -0
- package/dist/spinner.d.ts +22 -0
- package/dist/spinner.js +24 -0
- package/dist/styles.css +42 -0
- package/dist/switch.d.ts +12 -0
- package/dist/switch.js +16 -0
- package/dist/table.d.ts +7 -1
- package/dist/table.js +26 -12
- package/dist/tabs.d.ts +24 -1
- package/dist/tabs.js +61 -3
- package/dist/tailwind-preset.js +9 -0
- package/dist/textarea.js +1 -1
- package/dist/toaster.js +19 -10
- package/dist/use-error-shake.d.ts +7 -0
- package/dist/use-error-shake.js +36 -0
- package/dist/use-file-upload.d.ts +34 -0
- package/dist/use-file-upload.js +133 -0
- package/dist/use-sticky-sentinel.d.ts +16 -0
- package/dist/use-sticky-sentinel.js +62 -0
- package/dist/use-table-sort.d.ts +28 -0
- package/dist/use-table-sort.js +53 -0
- package/dist/use-viewport-threshold.d.ts +12 -0
- package/dist/use-viewport-threshold.js +43 -0
- package/package.json +32 -6
- package/dist/tab-with-dropdown.d.ts +0 -18
- package/dist/tab-with-dropdown.js +0 -70
|
@@ -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,
|
|
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,
|
|
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,
|
|
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,
|
|
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";
|
package/dist/input-control.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
declare const inputControlClassName = "h-
|
|
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 };
|
package/dist/input-control.js
CHANGED
|
@@ -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-
|
|
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 };
|
package/dist/label-value-grid.js
CHANGED
|
@@ -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:
|
|
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
|
}) }));
|
package/dist/label-value.d.ts
CHANGED
|
@@ -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 };
|
package/dist/label-value.js
CHANGED
|
@@ -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: 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,
|
|
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-
|
|
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?: "default" | "inherit" | "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 };
|
package/dist/multi-select.d.ts
CHANGED
|
@@ -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 };
|
package/dist/multi-select.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { XIcon } from "./animated-icons/x";
|
|
5
5
|
import { Badge } from "./badge";
|
|
6
|
-
import { Button } from "./button";
|
|
7
6
|
import { Field, FieldDescription, FieldError, FieldLabel } from "./field";
|
|
7
|
+
import { Checkbox } from "./checkbox";
|
|
8
8
|
import { InputControl } from "./input-control";
|
|
9
9
|
import { cn } from "./lib/utils";
|
|
10
10
|
import { Typography } from "./typography";
|
|
11
|
-
function MultiSelect({ label, options, value, onValueChange, placeholder = "Select options", description, error, invalid = false, disabled = false, hideLabel = false, searchable = false, searchPlaceholder = "Search...", className, triggerClassName, labelClassName, descriptionClassName, errorClassName, id, }) {
|
|
11
|
+
function MultiSelect({ label, options, value, onValueChange, placeholder = "Select options", description, error, invalid = false, disabled = false, hideLabel = false, searchable = false, searchPlaceholder = "Search...", maxVisibleTags = 2, className, triggerClassName, labelClassName, descriptionClassName, errorClassName, id, }) {
|
|
12
12
|
const generatedId = React.useId();
|
|
13
13
|
const selectId = id !== null && id !== void 0 ? id : `multi-select-${generatedId}`;
|
|
14
14
|
const errorId = error ? `${selectId}-error` : undefined;
|
|
@@ -64,14 +64,15 @@ function MultiSelect({ label, options, value, onValueChange, placeholder = "Sele
|
|
|
64
64
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
65
65
|
}
|
|
66
66
|
}, [open]);
|
|
67
|
-
return (_jsxs(Field, { className: className, "data-disabled": disabled ? "true" : undefined, "data-invalid": isInvalid ? "true" : undefined, children: [_jsx(FieldLabel, { htmlFor: selectId, className: cn(hideLabel && "sr-only", labelClassName), children: label }), _jsxs("div", { ref: containerRef, className: "relative", children: [_jsx("button", { type: "button", id: selectId, disabled: disabled, onClick: () => setOpen((prev) => !prev), "aria-expanded": open, "aria-haspopup": "listbox", "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": [descriptionId, errorId].filter(Boolean).join(" ") || undefined, className: cn("flex
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
return (_jsxs(Field, { className: className, "data-disabled": disabled ? "true" : undefined, "data-invalid": isInvalid ? "true" : undefined, children: [_jsx(FieldLabel, { htmlFor: selectId, className: cn(hideLabel && "sr-only", labelClassName), children: label }), _jsxs("div", { ref: containerRef, className: "relative", children: [_jsx("button", { type: "button", id: selectId, disabled: disabled, onClick: () => setOpen((prev) => !prev), "aria-expanded": open, "aria-haspopup": "listbox", "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": [descriptionId, errorId].filter(Boolean).join(" ") || undefined, className: cn("flex h-control w-full items-center gap-1.5 overflow-hidden rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", isInvalid ? "border-destructive" : "border-input", triggerClassName), children: selectedLabels.length > 0 ? (_jsxs(_Fragment, { children: [selectedLabels.slice(0, maxVisibleTags).map((opt) => (_jsxs(Badge, { variant: "secondary", className: "max-w-[10rem] shrink-0 justify-start gap-1 overflow-visible text-sm font-medium normal-case", children: [_jsx("span", { className: "min-w-0 truncate", children: opt.label }), _jsx("button", { type: "button", className: "inline-flex cursor-pointer items-center justify-center rounded-sm text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: (e) => {
|
|
68
|
+
e.stopPropagation();
|
|
69
|
+
removeOption(opt.value);
|
|
70
|
+
}, "aria-label": `Remove ${opt.label}`, children: _jsx(XIcon, { size: 12 }) })] }, opt.value))), selectedLabels.length > maxVisibleTags ? (_jsxs(Badge, { variant: "secondary", className: "shrink-0 text-sm font-medium normal-case", title: selectedLabels
|
|
71
|
+
.slice(maxVisibleTags)
|
|
72
|
+
.map((o) => o.label)
|
|
73
|
+
.join(", "), children: ["+", selectedLabels.length - maxVisibleTags] })) : null] })) : (_jsx("span", { className: "text-muted-foreground", children: placeholder })) }), open ? (_jsxs("div", { className: "absolute z-50 mt-1 w-full rounded-md border bg-popover shadow-md", children: [searchable ? (_jsx("div", { className: "border-b px-2 py-1", children: _jsx(InputControl, { ref: searchInputRef, value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: searchPlaceholder, className: "h-8 border-0 bg-transparent px-2 py-0 text-sm shadow-none focus-visible:ring-0", onKeyDown: (e) => e.stopPropagation() }) })) : null, _jsx("div", { role: "listbox", "aria-label": typeof label === "string" ? label : "Options", "aria-multiselectable": "true", className: "max-h-64 overflow-y-auto p-1", children: filteredOptions.length > 0 ? (filteredOptions.map((option) => {
|
|
71
74
|
const isSelected = value.includes(option.value);
|
|
72
|
-
return (_jsxs("button", { type: "button", role: "option", "aria-selected": isSelected, disabled: option.disabled, onClick: () => toggleOption(option.value), className: cn("flex h-
|
|
73
|
-
|
|
74
|
-
: "border-input"), children: isSelected ? (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round", strokeLinejoin: "round", className: "h-3 w-3", children: _jsx("polyline", { points: "20 6 9 17 4 12" }) })) : null }), _jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { children: option.label }), option.description ? (_jsx(Typography, { as: "span", variant: "caption", children: option.description })) : null] })] }, option.value));
|
|
75
|
-
})) : (_jsx("div", { className: "px-3 py-6 text-center text-sm text-muted-foreground", children: "No options found." })) })] })) : null] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
|
|
75
|
+
return (_jsxs("button", { type: "button", role: "option", "aria-selected": isSelected, disabled: option.disabled, onClick: () => toggleOption(option.value), className: cn("flex min-h-control w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-2 text-left text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50", isSelected && "bg-accent"), children: [_jsx(Checkbox, { checked: isSelected, disabled: option.disabled, tabIndex: -1, readOnly: true, "aria-hidden": "true", className: "mt-0.5" }), _jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { children: option.label }), option.description ? (_jsx(Typography, { as: "span", variant: "caption", children: option.description })) : null] })] }, option.value));
|
|
76
|
+
})) : (_jsx("div", { className: "px-3 py-2 text-center text-sm text-muted-foreground", children: "No options found." })) })] })) : null] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
|
|
76
77
|
}
|
|
77
78
|
export { MultiSelect };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface PageSectionGroupProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
3
|
+
}
|
|
4
|
+
declare function PageSectionGroup({ className, ...props }: PageSectionGroupProps): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export interface PageSectionProps extends Omit<React.HTMLAttributes<HTMLElement>, "title"> {
|
|
6
|
+
title: React.ReactNode;
|
|
7
|
+
description?: React.ReactNode;
|
|
8
|
+
headerAction?: React.ReactNode;
|
|
9
|
+
titleAdornment?: React.ReactNode;
|
|
10
|
+
headerClassName?: string;
|
|
11
|
+
contentClassName?: string;
|
|
12
|
+
}
|
|
13
|
+
declare function PageSection({ title, description, headerAction, titleAdornment, className, headerClassName, contentClassName, children, ...props }: PageSectionProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export { PageSection, PageSectionGroup };
|