@neasg/design-system 0.2.1 → 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.
Files changed (100) 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 +5 -2
  5. package/dist/avatar.js +8 -4
  6. package/dist/back-button.d.ts +6 -0
  7. package/dist/back-button.js +8 -0
  8. package/dist/badge.d.ts +4 -2
  9. package/dist/badge.js +13 -2
  10. package/dist/breadcrumb.js +2 -2
  11. package/dist/button.d.ts +2 -2
  12. package/dist/button.js +3 -4
  13. package/dist/calendar.d.ts +8 -0
  14. package/dist/calendar.js +80 -0
  15. package/dist/card.d.ts +17 -0
  16. package/dist/card.js +16 -0
  17. package/dist/checkbox.d.ts +13 -0
  18. package/dist/checkbox.js +29 -0
  19. package/dist/collapsible.d.ts +19 -0
  20. package/dist/collapsible.js +20 -0
  21. package/dist/command-search.js +4 -3
  22. package/dist/copy-button.d.ts +19 -0
  23. package/dist/copy-button.js +31 -0
  24. package/dist/date-input.d.ts +22 -0
  25. package/dist/date-input.js +61 -0
  26. package/dist/dialog-primitive.js +1 -1
  27. package/dist/draggable-tabs.js +1 -1
  28. package/dist/drawer.js +1 -1
  29. package/dist/editable-table.js +2 -2
  30. package/dist/empty-state.js +1 -1
  31. package/dist/field.d.ts +4 -1
  32. package/dist/field.js +3 -3
  33. package/dist/file-upload.d.ts +12 -0
  34. package/dist/file-upload.js +106 -0
  35. package/dist/index.d.ts +56 -4
  36. package/dist/index.js +30 -3
  37. package/dist/input-control.d.ts +1 -1
  38. package/dist/input-control.js +1 -1
  39. package/dist/input.js +2 -2
  40. package/dist/label-value-grid.d.ts +3 -1
  41. package/dist/label-value-grid.js +15 -2
  42. package/dist/label-value.d.ts +4 -1
  43. package/dist/label-value.js +5 -3
  44. package/dist/layout.d.ts +7 -1
  45. package/dist/layout.js +3 -3
  46. package/dist/lib/date-utils.d.ts +9 -0
  47. package/dist/lib/date-utils.js +34 -0
  48. package/dist/link.d.ts +9 -0
  49. package/dist/link.js +19 -0
  50. package/dist/multi-select.d.ts +31 -0
  51. package/dist/multi-select.js +78 -0
  52. package/dist/otp-input.js +1 -1
  53. package/dist/page-section.d.ts +14 -0
  54. package/dist/page-section.js +13 -0
  55. package/dist/pagination.js +3 -2
  56. package/dist/popover-menu.d.ts +35 -0
  57. package/dist/popover-menu.js +36 -0
  58. package/dist/popover-primitive.d.ts +7 -0
  59. package/dist/popover-primitive.js +11 -0
  60. package/dist/popover.d.ts +12 -0
  61. package/dist/popover.js +8 -0
  62. package/dist/progress.d.ts +10 -0
  63. package/dist/progress.js +17 -0
  64. package/dist/search-input.js +1 -1
  65. package/dist/section-nav.d.ts +22 -0
  66. package/dist/section-nav.js +25 -0
  67. package/dist/select-primitive.d.ts +3 -1
  68. package/dist/select-primitive.js +3 -2
  69. package/dist/select.d.ts +1 -0
  70. package/dist/select.js +3 -3
  71. package/dist/separator.d.ts +6 -0
  72. package/dist/separator.js +6 -0
  73. package/dist/skeleton.d.ts +4 -0
  74. package/dist/skeleton.js +6 -0
  75. package/dist/spinner.d.ts +22 -0
  76. package/dist/spinner.js +24 -0
  77. package/dist/styles.css +42 -0
  78. package/dist/switch.d.ts +12 -0
  79. package/dist/switch.js +16 -0
  80. package/dist/table.d.ts +7 -1
  81. package/dist/table.js +26 -12
  82. package/dist/tabs.d.ts +24 -1
  83. package/dist/tabs.js +61 -3
  84. package/dist/tailwind-preset.js +9 -0
  85. package/dist/textarea.js +1 -1
  86. package/dist/toaster.d.ts +13 -0
  87. package/dist/toaster.js +27 -0
  88. package/dist/typography.d.ts +2 -0
  89. package/dist/typography.js +9 -2
  90. package/dist/use-error-shake.d.ts +7 -0
  91. package/dist/use-error-shake.js +36 -0
  92. package/dist/use-file-upload.d.ts +34 -0
  93. package/dist/use-file-upload.js +133 -0
  94. package/dist/use-sticky-sentinel.d.ts +16 -0
  95. package/dist/use-sticky-sentinel.js +62 -0
  96. package/dist/use-table-sort.d.ts +28 -0
  97. package/dist/use-table-sort.js +53 -0
  98. package/dist/use-viewport-threshold.d.ts +12 -0
  99. package/dist/use-viewport-threshold.js +43 -0
  100. package/package.json +36 -6
@@ -0,0 +1,61 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { format, parse } from "date-fns";
5
+ import { Calendar } from "./calendar";
6
+ import { inputControlClassName } from "./input-control";
7
+ import { Field, FieldDescription, FieldError, FieldLabel } from "./field";
8
+ import { PopoverContent, PopoverRoot, PopoverTrigger } from "./popover-primitive";
9
+ import { cn } from "./lib/utils";
10
+ function parseDateString(dateStr) {
11
+ if (!(dateStr === null || dateStr === void 0 ? void 0 : dateStr.trim()))
12
+ return undefined;
13
+ try {
14
+ const nativeDate = new Date(dateStr);
15
+ if (!isNaN(nativeDate.getTime()))
16
+ return nativeDate;
17
+ const ddmmyyyyMatch = dateStr.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})/);
18
+ if (ddmmyyyyMatch) {
19
+ const [, day, month, year] = ddmmyyyyMatch;
20
+ const date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
21
+ if (!isNaN(date.getTime()))
22
+ return date;
23
+ }
24
+ const date = parse(dateStr, "yyyy-MM-dd", new Date());
25
+ if (!isNaN(date.getTime()))
26
+ return date;
27
+ return undefined;
28
+ }
29
+ catch {
30
+ return undefined;
31
+ }
32
+ }
33
+ function formatISODate(date) {
34
+ return format(date, "yyyy-MM-dd");
35
+ }
36
+ const DateInput = React.forwardRef(({ label, value, onValueChange, description, error, invalid = false, hideLabel = false, placeholder = "Pick a date", disabled = false, minDate, maxDate, className, triggerClassName, labelClassName, descriptionClassName, errorClassName, id, }, ref) => {
37
+ const [open, setOpen] = React.useState(false);
38
+ const generatedId = React.useId();
39
+ const inputId = id !== null && id !== void 0 ? id : `date-input-${generatedId}`;
40
+ const errorId = error ? `${inputId}-error` : undefined;
41
+ const showDescription = Boolean(description) && !error;
42
+ const descriptionId = showDescription ? `${inputId}-description` : undefined;
43
+ const isInvalid = invalid || Boolean(error);
44
+ const selectedDate = React.useMemo(() => parseDateString(value), [value]);
45
+ const displayValue = selectedDate
46
+ ? format(selectedDate, "d MMM yyyy")
47
+ : undefined;
48
+ const handleSelect = (date) => {
49
+ onValueChange(date ? formatISODate(date) : "");
50
+ setOpen(false);
51
+ };
52
+ 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 }), _jsxs(PopoverRoot, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("div", { className: "relative w-full", children: [_jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", style: { width: 14, height: 14 }, "aria-hidden": "true", children: [_jsx("rect", { width: "18", height: "18", x: "3", y: "4", rx: "2", ry: "2" }), _jsx("line", { x1: "16", x2: "16", y1: "2", y2: "6" }), _jsx("line", { x1: "8", x2: "8", y1: "2", y2: "6" }), _jsx("line", { x1: "3", x2: "21", y1: "10", y2: "10" })] }), _jsx("button", { ref: ref, type: "button", id: inputId, disabled: disabled, "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": [descriptionId, errorId].filter(Boolean).join(" ") || undefined, className: cn(inputControlClassName, "cursor-pointer pl-9 text-left", !selectedDate && "text-muted-foreground", isInvalid && "border-destructive focus-visible:ring-destructive", triggerClassName), children: displayValue !== null && displayValue !== void 0 ? displayValue : _jsx("span", { children: placeholder }) })] }) }), _jsx(PopoverContent, { className: "w-auto p-0", align: "start", children: _jsx(Calendar, { mode: "single", selected: selectedDate, onSelect: handleSelect, disabled: (date) => {
53
+ if (minDate && date < minDate)
54
+ return true;
55
+ if (maxDate && date > maxDate)
56
+ return true;
57
+ return false;
58
+ }, defaultMonth: selectedDate, autoFocus: true }) })] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
59
+ });
60
+ DateInput.displayName = "DateInput";
61
+ export { DateInput };
@@ -10,7 +10,7 @@ const DialogPortal = DialogPrimitive.Portal;
10
10
  const DialogClose = DialogPrimitive.Close;
11
11
  const DialogOverlay = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Overlay, { ref: ref, className: cn("fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", className), ...props })));
12
12
  DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
13
- const DialogSurface = React.forwardRef(({ className, children, hideCloseButton, ...props }, ref) => (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, {}), _jsxs(DialogPrimitive.Content, { ref: ref, className: cn("fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", className), ...props, children: [children, !hideCloseButton ? (_jsxs(DialogPrimitive.Close, { className: "absolute right-4 top-4 cursor-pointer rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground", children: [_jsx(XIcon, { size: 16 }), _jsx("span", { className: "sr-only", children: "Close" })] })) : null] })] })));
13
+ const DialogSurface = React.forwardRef(({ className, children, hideCloseButton, ...props }, ref) => (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, {}), _jsxs(DialogPrimitive.Content, { ref: ref, className: cn("fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", className), ...props, children: [children, !hideCloseButton ? (_jsxs(DialogPrimitive.Close, { className: "absolute right-2 top-2 flex h-6 w-6 cursor-pointer items-center justify-center rounded-md text-muted-foreground ring-offset-background transition-colors hover:bg-accent hover:text-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none", children: [_jsx(XIcon, { size: 16 }), _jsx("span", { className: "sr-only", children: "Close" })] })) : null] })] })));
14
14
  DialogSurface.displayName = DialogPrimitive.Content.displayName;
15
15
  const DialogHeader = ({ className, ...props }) => (_jsx("div", { className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className), ...props }));
16
16
  DialogHeader.displayName = "DialogHeader";
@@ -19,7 +19,7 @@ function TabButton({ item, dragHandleProps, }) {
19
19
  : isDraggable
20
20
  ? "cursor-grab active:cursor-grabbing"
21
21
  : "cursor-pointer", item.active
22
- ? (_a = item.activeClassName) !== null && _a !== void 0 ? _a : "border-green-500"
22
+ ? (_a = item.activeClassName) !== null && _a !== void 0 ? _a : "border-green-500 hover:bg-muted"
23
23
  : (_b = item.inactiveClassName) !== null && _b !== void 0 ? _b : "border-transparent hover:bg-muted", item.className), ...dragHandleProps, children: [_jsxs("button", { type: "button", onClick: item.disabled ? undefined : item.onSelect, disabled: item.disabled, className: cn("flex cursor-pointer items-center gap-2 py-3 pr-1 text-sm font-medium whitespace-nowrap disabled:cursor-not-allowed", labelClassName), children: [item.leading, item.label, item.trailing] }), item.onClose ? (_jsx("button", { type: "button", onClick: (event) => {
24
24
  var _a;
25
25
  event.stopPropagation();
package/dist/drawer.js CHANGED
@@ -46,7 +46,7 @@ const drawerSurfaceVariants = cva("fixed z-50 flex flex-col gap-4 overflow-y-aut
46
46
  },
47
47
  });
48
48
  function DrawerSurface({ side, className, children, hideCloseButton, ...props }) {
49
- return (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, {}), _jsxs(DialogPrimitive.Content, { className: cn(drawerSurfaceVariants({ side }), className), ...props, children: [children, !hideCloseButton ? (_jsxs(DialogPrimitive.Close, { className: "absolute right-4 top-4 cursor-pointer rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground", children: [_jsx(XIcon, { size: 16 }), _jsx("span", { className: "sr-only", children: "Close" })] })) : null] })] }));
49
+ return (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, {}), _jsxs(DialogPrimitive.Content, { className: cn(drawerSurfaceVariants({ side }), className), ...props, children: [children, !hideCloseButton ? (_jsxs(DialogPrimitive.Close, { className: "absolute right-2 top-2 flex h-6 w-6 cursor-pointer items-center justify-center rounded-md text-muted-foreground ring-offset-background transition-colors hover:bg-accent hover:text-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none", children: [_jsx(XIcon, { size: 16 }), _jsx("span", { className: "sr-only", children: "Close" })] })) : null] })] }));
50
50
  }
51
51
  function Drawer({ open, defaultOpen, onOpenChange, modal, trigger, title, description, children, footer, primaryAction, secondaryAction, side = "right", contentClassName, bodyClassName, hideCloseButton, }) {
52
52
  const [isOpen, setIsOpen] = useControllableOpen({
@@ -97,11 +97,11 @@ function EditableTable({ value, onChange, columns, isEditing = false, className,
97
97
  return String(cellValue);
98
98
  };
99
99
  const hasActionsColumn = isEditing || Boolean(renderRowActions);
100
- return (_jsxs("div", { className: cn("space-y-2", className), children: [_jsx("div", { className: "rounded-md border", children: _jsxs(Table, { className: "table-fixed", children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [columns.map((column, index) => (_jsx(TableHead, { width: column.width, minWidth: getColumnMinWidth(column), children: _jsx("div", { className: "whitespace-nowrap", children: column.header }) }, `header-${index}`))), hasActionsColumn ? (_jsx(TableHead, { width: renderRowActions ? undefined : "56px", minWidth: renderRowActions ? "120px" : "56px" })) : null] }) }), _jsx(TableBody, { children: value.length === 0 ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: columns.length + (hasActionsColumn ? 1 : 0), className: "py-8 text-center text-muted-foreground", children: "No data available" }) })) : (value.map((row, rowIndex) => (_jsxs(TableRow, { className: rowIndex % 2 === 0 ? "bg-muted/30" : undefined, children: [columns.map((column, columnIndex) => {
100
+ return (_jsxs("div", { className: cn("space-y-2", className), children: [_jsx("div", { className: "rounded-md border", children: _jsxs(Table, { className: "table-fixed", children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [columns.map((column, index) => (_jsx(TableHead, { width: column.width, minWidth: getColumnMinWidth(column), children: _jsx("div", { className: "whitespace-nowrap", children: column.header }) }, `header-${index}`))), hasActionsColumn ? (_jsx(TableHead, { width: renderRowActions ? undefined : "72px", minWidth: renderRowActions ? "120px" : "72px" })) : null] }) }), _jsx(TableBody, { children: value.length === 0 ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: columns.length + (hasActionsColumn ? 1 : 0), className: "py-8 text-center text-muted-foreground", children: "No data available" }) })) : (value.map((row, rowIndex) => (_jsxs(TableRow, { className: rowIndex % 2 === 0 ? "bg-muted/30" : undefined, children: [columns.map((column, columnIndex) => {
101
101
  const cellValue = getNestedValue(row, column.path);
102
102
  const isEditingCell = (editingCell === null || editingCell === void 0 ? void 0 : editingCell.rowIndex) === rowIndex &&
103
103
  (editingCell === null || editingCell === void 0 ? void 0 : editingCell.colPath) === column.path;
104
- return (_jsx(TableCell, { className: cn("whitespace-normal", isEditing && "cursor-pointer hover:bg-muted/50"), minWidth: getColumnMinWidth(column), onClick: () => handleCellClick(rowIndex, column.path), children: isEditingCell ? (_jsx(InputControl, { ref: inputRef, value: editValue, onChange: (event) => setEditValue(event.target.value), onBlur: commitEdit, onKeyDown: handleKeyDown, className: "h-10 w-full text-sm" })) : (_jsx("span", { className: cn("block whitespace-normal break-words", cellValue === null ||
104
+ return (_jsx(TableCell, { className: cn("whitespace-normal", isEditing && "cursor-pointer hover:bg-muted/50"), minWidth: getColumnMinWidth(column), onClick: () => handleCellClick(rowIndex, column.path), children: isEditingCell ? (_jsx(InputControl, { ref: inputRef, value: editValue, onChange: (event) => setEditValue(event.target.value), onBlur: commitEdit, onKeyDown: handleKeyDown, className: "h-control w-full text-sm" })) : (_jsx("span", { className: cn("block whitespace-normal break-words", cellValue === null ||
105
105
  cellValue === undefined ||
106
106
  cellValue === ""
107
107
  ? "text-muted-foreground"
@@ -2,6 +2,6 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { cn } from "./lib/utils";
3
3
  import { Typography } from "./typography";
4
4
  function EmptyState({ message, description, icon: Icon, action, className, ...props }) {
5
- return (_jsxs("div", { className: cn("flex flex-col items-center justify-center px-6 py-10 text-center", className), ...props, children: [Icon ? (_jsx(Icon, { className: "mb-4 h-5 w-5 text-muted-foreground/70" })) : null, _jsxs("div", { className: "space-y-1.5", children: [_jsx(Typography, { as: "p", variant: "bodySm", className: "font-semibold", children: message }), description ? (_jsx(Typography, { as: "p", variant: "caption", className: "max-w-md", children: description })) : null] }), action ? _jsx("div", { className: "pt-4", children: action }) : null] }));
5
+ return (_jsxs("div", { className: cn("flex flex-col items-center justify-center px-6 py-10 text-center", className), ...props, children: [Icon ? (_jsx(Icon, { className: "mb-4 h-10 w-10 stroke-[1.5] text-muted-foreground/70" })) : null, _jsxs("div", { className: "space-y-1.5", children: [_jsx(Typography, { as: "p", variant: "bodySm", className: "font-semibold", children: message }), description ? (_jsx(Typography, { as: "p", variant: "caption", className: "max-w-md", children: description })) : null] }), action ? _jsx("div", { className: "pt-6", children: action }) : null] }));
6
6
  }
7
7
  export { EmptyState };
package/dist/field.d.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  import * as React from "react";
2
2
  declare function Field({ className, ...props }: React.ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
3
- declare function FieldLabel({ className, ...props }: React.ComponentProps<"label">): import("react/jsx-runtime").JSX.Element;
3
+ interface FieldLabelProps extends React.ComponentProps<"label"> {
4
+ required?: boolean;
5
+ }
6
+ declare function FieldLabel({ className, required, children, ...props }: FieldLabelProps): import("react/jsx-runtime").JSX.Element;
4
7
  declare function FieldDescription({ className, ...props }: React.ComponentProps<"p">): import("react/jsx-runtime").JSX.Element;
5
8
  declare function FieldError({ className, ...props }: React.ComponentProps<"p">): import("react/jsx-runtime").JSX.Element | null;
6
9
  export { Field, FieldDescription, FieldError, FieldLabel };
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,8 +1,19 @@
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";
7
+ export { Card } from "./card";
8
+ export type { CardProps } from "./card";
5
9
  export type { BadgeProps } from "./badge";
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";
15
+ export { DateInput } from "./date-input";
16
+ export type { DateInputProps } from "./date-input";
6
17
  export { CountBadge } from "./count-badge";
7
18
  export type { CountBadgeProps } from "./count-badge";
8
19
  export { EmptyState } from "./empty-state";
@@ -15,6 +26,8 @@ export { Input } from "./input";
15
26
  export type { InputProps } from "./input";
16
27
  export { Select } from "./select";
17
28
  export type { SelectOption, SelectProps } from "./select";
29
+ export { MultiSelect } from "./multi-select";
30
+ export type { MultiSelectOption, MultiSelectProps } from "./multi-select";
18
31
  export { DEFAULT_COUNTRY_CODES, PhoneInput, } from "./phone-input";
19
32
  export type { CountryCode, PhoneInputProps, } from "./phone-input";
20
33
  export { OtpInput } from "./otp-input";
@@ -25,26 +38,50 @@ export { LabelValueGrid } from "./label-value-grid";
25
38
  export type { LabelValueGridItem, LabelValueGridProps, } from "./label-value-grid";
26
39
  export { Breadcrumb, BreadcrumbNav, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis, } from "./breadcrumb";
27
40
  export type { BreadcrumbEntry, BreadcrumbProps } from "./breadcrumb";
41
+ export { BackButton } from "./back-button";
42
+ export type { BackButtonProps } from "./back-button";
28
43
  export { Button, buttonVariants } from "./button";
29
44
  export type { ButtonProps } from "./button";
45
+ export { Link, linkVariants } from "./link";
46
+ export type { LinkProps } from "./link";
30
47
  export { Dialog } from "./dialog";
31
48
  export type { DialogAction, DialogProps } from "./dialog";
32
49
  export { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator, CommandShortcut, } from "./command";
33
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";
34
61
  export { SearchInput } from "./search-input";
35
62
  export type { SearchInputProps } from "./search-input";
63
+ export { SectionNav } from "./section-nav";
64
+ export type { SectionNavItem, SectionNavProps } from "./section-nav";
36
65
  export { GovtMasthead } from "./govt-masthead";
37
66
  export type { GovtMastheadProps } from "./govt-masthead";
38
67
  export { SearchHighlight, useSearchHighlight, } from "./use-search-highlight";
39
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";
40
75
  export { Textarea } from "./textarea";
41
76
  export type { TextareaProps } from "./textarea";
42
77
  export { DraggableTabs, type DraggableTabItem, type DraggableTabsProps, } from "./draggable-tabs";
43
78
  export { Drawer, } from "./drawer";
44
79
  export type { DrawerAction, DrawerProps, DrawerSide } from "./drawer";
45
- export { DashboardLayout, DashboardSidebar, DashboardContent, SidebarProvider, useSidebar, } from "./layout";
46
- export type { DashboardSidebarNavGroup, DashboardSidebarNavItem, } from "./layout";
80
+ export { DashboardLayout, DashboardSidebar, useSidebar, } from "./layout";
81
+ export type { DashboardLayoutProps, DashboardSidebarNavGroup, DashboardSidebarNavItem, } from "./layout";
47
82
  export { PageHeader } from "./page-header";
83
+ export { PageSection, PageSectionGroup } from "./page-section";
84
+ export type { PageSectionGroupProps, PageSectionProps } from "./page-section";
48
85
  export { Typography, typographyVariants } from "./typography";
49
86
  export type { TypographyProps } from "./typography";
50
87
  export { neaPalette, semanticColorTokens, typographyTokens, radiusTokens, spacingTokens, layoutTokens, } from "./theme";
@@ -53,8 +90,23 @@ export { Pagination } from "./pagination";
53
90
  export type { PaginationProps } from "./pagination";
54
91
  export { TableToolbar } from "./table-toolbar";
55
92
  export type { TableToolbarProps } from "./table-toolbar";
93
+ export { useTableSort } from "./use-table-sort";
94
+ export type { UseTableSortOptions, UseTableSortReturn, } from "./use-table-sort";
56
95
  export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, TableRowSkeleton, TABLE_SKELETON_PRESETS, } from "./table";
57
96
  export type { TableColumn, TableProps, TableSortDirection, TableSortingProps, TableSkeletonConfig, TableRowSkeletonProps, } from "./table";
58
- export { Tabs, TabsRoot, type TabsItem, type TabsProps, TabsList, TabsTrigger, TabsContent, useTabsContext, } from "./tabs";
97
+ export { Tabs, TabsRoot, TabWithDropdown, type TabsItem, type TabsProps, TabsList, TabsTrigger, TabsContent, type TabWithDropdownProps, useTabsContext, type TabDropdownSection, } from "./tabs";
59
98
  export { Tooltip, TooltipRoot, TooltipTrigger, TooltipContent, TooltipProvider, type TooltipProps, } from "./tooltip";
99
+ export { Toaster, toast } from "./toaster";
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";
60
111
  export { cn } from "./lib/utils";
112
+ export { parseDateTime, formatDateTime } from "./lib/date-utils";
package/dist/index.js CHANGED
@@ -1,35 +1,62 @@
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";
5
+ export { Card } from "./card";
6
+ export { Calendar, CalendarDayButton } from "./calendar";
7
+ export { Checkbox } from "./checkbox";
8
+ export { Switch } from "./switch";
9
+ export { DateInput } from "./date-input";
4
10
  export { CountBadge } from "./count-badge";
5
11
  export { EmptyState } from "./empty-state";
6
12
  export { EditableTable } from "./editable-table";
7
13
  export { EmailInput } from "./email-input";
8
14
  export { Input } from "./input";
9
15
  export { Select } from "./select";
16
+ export { MultiSelect } from "./multi-select";
10
17
  export { DEFAULT_COUNTRY_CODES, PhoneInput, } from "./phone-input";
11
18
  export { OtpInput } from "./otp-input";
12
19
  export { LabelValue, isEmptyValue, } from "./label-value";
13
20
  export { LabelValueGrid } from "./label-value-grid";
14
21
  export { Breadcrumb, BreadcrumbNav, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis, } from "./breadcrumb";
22
+ export { BackButton } from "./back-button";
15
23
  export { Button, buttonVariants } from "./button";
24
+ export { Link, linkVariants } from "./link";
16
25
  export { Dialog } from "./dialog";
17
26
  export { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator, CommandShortcut, } from "./command";
18
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";
19
33
  export { SearchInput } from "./search-input";
34
+ export { SectionNav } from "./section-nav";
20
35
  export { GovtMasthead } from "./govt-masthead";
21
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";
22
40
  export { Textarea } from "./textarea";
23
41
  export { DraggableTabs, } from "./draggable-tabs";
24
42
  export { Drawer, } from "./drawer";
25
- export { DashboardLayout, DashboardSidebar, DashboardContent, SidebarProvider, useSidebar, } from "./layout";
43
+ export { DashboardLayout, DashboardSidebar, useSidebar, } from "./layout";
26
44
  export { PageHeader } from "./page-header";
45
+ export { PageSection, PageSectionGroup } from "./page-section";
27
46
  export { Typography, typographyVariants } from "./typography";
28
47
  export { neaPalette, semanticColorTokens, typographyTokens, radiusTokens, spacingTokens, layoutTokens, } from "./theme";
29
48
  export { neaDesignSystemContent, neaTailwindPreset, } from "./tailwind-preset";
30
49
  export { Pagination } from "./pagination";
31
50
  export { TableToolbar } from "./table-toolbar";
51
+ export { useTableSort } from "./use-table-sort";
32
52
  export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, TableRowSkeleton, TABLE_SKELETON_PRESETS, } from "./table";
33
- export { Tabs, TabsRoot, TabsList, TabsTrigger, TabsContent, useTabsContext, } from "./tabs";
53
+ export { Tabs, TabsRoot, TabWithDropdown, TabsList, TabsTrigger, TabsContent, useTabsContext, } from "./tabs";
34
54
  export { Tooltip, TooltipRoot, TooltipTrigger, TooltipContent, TooltipProvider, } from "./tooltip";
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";
35
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 shadow-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 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 shadow-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 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,7 @@
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";
4
+ import { Typography } from "./typography";
3
5
  export function isEmptyValue(value) {
4
6
  if (value === null || value === undefined || value === "")
5
7
  return true;
@@ -9,11 +11,11 @@ export function isEmptyValue(value) {
9
11
  }
10
12
  return false;
11
13
  }
12
- function LabelValue({ label, value, className, labelClassName, hideIfEmpty = false, }) {
14
+ function LabelValue({ label, value, className, labelClassName, hideIfEmpty = false, hideLabel = false, loading = false, required = false, }) {
13
15
  const empty = isEmptyValue(value);
14
- if (hideIfEmpty && empty) {
16
+ if (!loading && hideIfEmpty && empty) {
15
17
  return null;
16
18
  }
17
- return (_jsxs("div", { className: cn("min-w-0 h-full space-y-1", className), children: [_jsx("p", { className: cn("text-sm font-semibold text-foreground", 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 }))] }));
18
20
  }
19
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;