@neasg/design-system 0.3.0 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/README.md +37 -1
  2. package/dist/alert.d.ts +15 -0
  3. package/dist/alert.js +24 -0
  4. package/dist/avatar.d.ts +13 -4
  5. package/dist/avatar.js +24 -7
  6. package/dist/back-button.d.ts +6 -0
  7. package/dist/back-button.js +8 -0
  8. package/dist/badge.d.ts +1 -1
  9. package/dist/breadcrumb.js +2 -2
  10. package/dist/button.d.ts +2 -2
  11. package/dist/button.js +3 -4
  12. package/dist/card.d.ts +3 -0
  13. package/dist/card.js +9 -2
  14. package/dist/checkbox.d.ts +13 -0
  15. package/dist/checkbox.js +29 -0
  16. package/dist/collapsible.d.ts +19 -0
  17. package/dist/collapsible.js +20 -0
  18. package/dist/command-search.js +2 -2
  19. package/dist/command.d.ts +1 -1
  20. package/dist/copy-button.d.ts +19 -0
  21. package/dist/copy-button.js +31 -0
  22. package/dist/date-input.js +2 -2
  23. package/dist/dialog-primitive.js +1 -1
  24. package/dist/draggable-tabs.js +1 -1
  25. package/dist/drawer.js +1 -1
  26. package/dist/editable-table.js +2 -2
  27. package/dist/empty-state.js +1 -1
  28. package/dist/field.d.ts +4 -1
  29. package/dist/field.js +3 -3
  30. package/dist/file-upload.d.ts +12 -0
  31. package/dist/file-upload.js +106 -0
  32. package/dist/index.d.ts +47 -6
  33. package/dist/index.js +25 -4
  34. package/dist/input-control.d.ts +1 -1
  35. package/dist/input-control.js +1 -1
  36. package/dist/input.js +2 -2
  37. package/dist/label-value-grid.d.ts +3 -1
  38. package/dist/label-value-grid.js +15 -2
  39. package/dist/label-value.d.ts +4 -1
  40. package/dist/label-value.js +4 -3
  41. package/dist/layout.d.ts +7 -1
  42. package/dist/layout.js +3 -3
  43. package/dist/lib/date-utils.d.ts +9 -0
  44. package/dist/lib/date-utils.js +34 -0
  45. package/dist/link.d.ts +9 -0
  46. package/dist/link.js +19 -0
  47. package/dist/multi-select.d.ts +3 -1
  48. package/dist/multi-select.js +12 -11
  49. package/dist/page-section.d.ts +14 -0
  50. package/dist/page-section.js +13 -0
  51. package/dist/pagination.js +1 -1
  52. package/dist/popover-menu.d.ts +35 -0
  53. package/dist/popover-menu.js +36 -0
  54. package/dist/popover-primitive.d.ts +7 -0
  55. package/dist/popover-primitive.js +11 -0
  56. package/dist/popover.d.ts +10 -5
  57. package/dist/popover.js +6 -9
  58. package/dist/progress.d.ts +10 -0
  59. package/dist/progress.js +17 -0
  60. package/dist/search-input.js +1 -1
  61. package/dist/section-nav.d.ts +22 -0
  62. package/dist/section-nav.js +25 -0
  63. package/dist/select-primitive.js +2 -2
  64. package/dist/select.js +2 -2
  65. package/dist/separator.d.ts +6 -0
  66. package/dist/separator.js +6 -0
  67. package/dist/skeleton.d.ts +4 -0
  68. package/dist/skeleton.js +6 -0
  69. package/dist/spinner.d.ts +22 -0
  70. package/dist/spinner.js +24 -0
  71. package/dist/styles.css +42 -0
  72. package/dist/switch.d.ts +12 -0
  73. package/dist/switch.js +16 -0
  74. package/dist/table.d.ts +7 -1
  75. package/dist/table.js +26 -12
  76. package/dist/tabs.d.ts +24 -1
  77. package/dist/tabs.js +61 -3
  78. package/dist/tailwind-preset.js +9 -0
  79. package/dist/textarea.js +1 -1
  80. package/dist/toaster.js +19 -10
  81. package/dist/typography.d.ts +1 -1
  82. package/dist/use-error-shake.d.ts +7 -0
  83. package/dist/use-error-shake.js +36 -0
  84. package/dist/use-file-upload.d.ts +34 -0
  85. package/dist/use-file-upload.js +133 -0
  86. package/dist/use-sticky-sentinel.d.ts +16 -0
  87. package/dist/use-sticky-sentinel.js +62 -0
  88. package/dist/use-table-sort.d.ts +28 -0
  89. package/dist/use-table-sort.js +53 -0
  90. package/dist/use-viewport-threshold.d.ts +12 -0
  91. package/dist/use-viewport-threshold.js +43 -0
  92. package/package.json +32 -6
  93. package/dist/tab-with-dropdown.d.ts +0 -18
  94. package/dist/tab-with-dropdown.js +0 -70
@@ -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 min-h-10 w-full flex-wrap items-center gap-1.5 rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background md:text-sm 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 ? (selectedLabels.map((opt) => (_jsxs(Badge, { variant: "secondary", className: "gap-1 pr-1", children: [opt.label, _jsx(Button, { variant: "ghost", size: "icon", className: "h-4 w-4 p-0 hover:bg-transparent", onClick: (e) => {
68
- e.stopPropagation();
69
- removeOption(opt.value);
70
- }, "aria-label": `Remove ${opt.label}`, children: _jsx(XIcon, { size: 10 }) })] }, opt.value)))) : (_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 p-2", 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) => {
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-10 w-full cursor-pointer items-center gap-2 rounded-sm px-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("div", { className: cn("mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-sm border", isSelected
73
- ? "border-primary bg-primary text-primary-foreground"
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 };
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "./lib/utils";
4
+ import { Typography } from "./typography";
5
+ function PageSectionGroup({ className, ...props }) {
6
+ return _jsx("div", { className: cn("divide-y divide-border", className), ...props });
7
+ }
8
+ function PageSection({ title, description, headerAction, titleAdornment, className, headerClassName, contentClassName, children, ...props }) {
9
+ const renderedTitle = React.isValidElement(title) ? (title) : (_jsx(Typography, { as: "h3", variant: "subheading", children: title }));
10
+ const renderedDescription = description == null ? null : React.isValidElement(description) ? (description) : (_jsx(Typography, { as: "p", variant: "caption", children: description }));
11
+ return (_jsxs("section", { className: cn("py-6", className), ...props, children: [_jsxs("div", { className: cn("flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between", headerClassName), children: [_jsxs("div", { className: "min-w-0 space-y-1", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [renderedTitle, titleAdornment] }), renderedDescription] }), headerAction ? (_jsx("div", { className: "flex items-center gap-2 sm:shrink-0", children: headerAction })) : null] }), children ? (_jsx("div", { className: cn("pt-5", contentClassName), children: children })) : null] }));
12
+ }
13
+ export { PageSection, PageSectionGroup };
@@ -20,6 +20,6 @@ function Pagination({ currentPage, totalPages, totalItems, itemsPerPage = 5, onP
20
20
  return (_jsxs("div", { className: cn("flex items-center justify-between gap-6", className), ...props, children: [_jsxs("div", { className: "text-sm text-muted-foreground", children: ["Showing ", startItem, "-", endItem, " out of ", totalItems] }), _jsxs("div", { className: "flex items-center gap-6", children: [onItemsPerPageChange ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Typography, { as: "span", variant: "caption", children: "Rows per page" }), _jsxs(SelectRoot, { value: itemsPerPage.toString(), onValueChange: (value) => {
21
21
  onItemsPerPageChange(Number(value));
22
22
  onPageChange(1);
23
- }, children: [_jsx(SelectTrigger, { className: "h-8 w-[70px] text-sm", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: itemsPerPageOptions.map((option) => (_jsx(SelectItem, { value: option.toString(), children: option }, option))) })] })] })) : null, _jsxs("div", { className: "text-sm text-muted-foreground", children: ["Page ", currentPage, " of ", normalizedTotalPages] }), _jsx(PaginationNav, { className: "mx-0 w-auto justify-start", children: _jsxs(PaginationContent, { children: [_jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: "outline", size: "icon", className: "h-8 w-8", onClick: () => onPageChange(1), disabled: currentPage === 1, "aria-label": "Go to first page", children: _jsx(ChevronsLeft, { className: "h-4 w-4" }) }) }), _jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: "outline", size: "icon", className: "h-8 w-8", onClick: () => onPageChange(Math.max(1, currentPage - 1)), disabled: currentPage === 1, "aria-label": "Go to previous page", children: _jsx(ChevronLeftIcon, { size: 16 }) }) }), _jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: "outline", size: "icon", className: "h-8 w-8", onClick: () => onPageChange(Math.min(normalizedTotalPages, currentPage + 1)), disabled: currentPage === normalizedTotalPages || totalPages === 0, "aria-label": "Go to next page", children: _jsx(ChevronRightIcon, { size: 16 }) }) }), _jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: "outline", size: "icon", className: "h-8 w-8", onClick: () => onPageChange(normalizedTotalPages), disabled: currentPage === normalizedTotalPages || totalPages === 0, "aria-label": "Go to last page", children: _jsx(ChevronsRight, { className: "h-4 w-4" }) }) })] }) })] })] }));
23
+ }, children: [_jsx(SelectTrigger, { className: "w-[70px] text-sm", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: itemsPerPageOptions.map((option) => (_jsx(SelectItem, { value: option.toString(), children: option }, option))) })] })] })) : null, _jsxs("div", { className: "text-sm text-muted-foreground", children: ["Page ", currentPage, " of ", normalizedTotalPages] }), _jsx(PaginationNav, { className: "mx-0 w-auto justify-start", children: _jsxs(PaginationContent, { children: [_jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: "outline", size: "icon", onClick: () => onPageChange(1), disabled: currentPage === 1, "aria-label": "Go to first page", children: _jsx(ChevronsLeft, { className: "h-4 w-4" }) }) }), _jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: "outline", size: "icon", onClick: () => onPageChange(Math.max(1, currentPage - 1)), disabled: currentPage === 1, "aria-label": "Go to previous page", children: _jsx(ChevronLeftIcon, { size: 16 }) }) }), _jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: "outline", size: "icon", onClick: () => onPageChange(Math.min(normalizedTotalPages, currentPage + 1)), disabled: currentPage === normalizedTotalPages || totalPages === 0, "aria-label": "Go to next page", children: _jsx(ChevronRightIcon, { size: 16 }) }) }), _jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: "outline", size: "icon", onClick: () => onPageChange(normalizedTotalPages), disabled: currentPage === normalizedTotalPages || totalPages === 0, "aria-label": "Go to last page", children: _jsx(ChevronsRight, { className: "h-4 w-4" }) }) })] }) })] })] }));
24
24
  }
25
25
  export { Pagination };
@@ -0,0 +1,35 @@
1
+ import * as React from "react";
2
+ import { InputControl } from "./input-control";
3
+ import { PopoverContent } from "./popover-primitive";
4
+ import { Typography } from "./typography";
5
+ export interface PopoverMenuHeaderProps extends Omit<React.ComponentPropsWithoutRef<typeof Typography>, "as" | "asChild" | "variant" | "color"> {
6
+ }
7
+ export interface PopoverMenuContentProps extends React.ComponentPropsWithoutRef<typeof PopoverContent> {
8
+ }
9
+ declare const PopoverMenuContent: React.ForwardRefExoticComponent<PopoverMenuContentProps & React.RefAttributes<HTMLDivElement>>;
10
+ declare function PopoverMenuHeader({ className, children, ...props }: PopoverMenuHeaderProps): import("react/jsx-runtime").JSX.Element;
11
+ export interface PopoverMenuSectionProps extends React.HTMLAttributes<HTMLDivElement> {
12
+ }
13
+ declare function PopoverMenuSection({ className, children, ...props }: PopoverMenuSectionProps): import("react/jsx-runtime").JSX.Element;
14
+ export interface PopoverMenuItemProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
15
+ active?: boolean;
16
+ }
17
+ declare function PopoverMenuItem({ className, children, active, type, ...props }: PopoverMenuItemProps): import("react/jsx-runtime").JSX.Element;
18
+ export interface PopoverMenuDividerProps extends React.HTMLAttributes<HTMLDivElement> {
19
+ }
20
+ declare function PopoverMenuDivider({ className, ...props }: PopoverMenuDividerProps): import("react/jsx-runtime").JSX.Element;
21
+ export interface PopoverMenuGroupProps extends React.HTMLAttributes<HTMLDivElement> {
22
+ }
23
+ declare function PopoverMenuGroup({ className, children, ...props }: PopoverMenuGroupProps): import("react/jsx-runtime").JSX.Element;
24
+ export interface PopoverMenuGroupLabelProps extends Omit<React.ComponentPropsWithoutRef<typeof Typography>, "as" | "asChild" | "variant" | "color"> {
25
+ }
26
+ declare function PopoverMenuGroupLabel({ className, children, ...props }: PopoverMenuGroupLabelProps): import("react/jsx-runtime").JSX.Element;
27
+ export interface PopoverMenuSearchProps extends React.ComponentPropsWithoutRef<typeof InputControl> {
28
+ containerClassName?: string;
29
+ }
30
+ declare const PopoverMenuSearch: React.ForwardRefExoticComponent<PopoverMenuSearchProps & React.RefAttributes<HTMLInputElement>>;
31
+ export interface PopoverMenuScrollAreaProps extends React.HTMLAttributes<HTMLDivElement> {
32
+ maxHeight?: string;
33
+ }
34
+ declare function PopoverMenuScrollArea({ className, children, maxHeight, ...props }: PopoverMenuScrollAreaProps): import("react/jsx-runtime").JSX.Element;
35
+ export { PopoverMenuContent, PopoverMenuHeader, PopoverMenuSection, PopoverMenuItem, PopoverMenuDivider, PopoverMenuGroup, PopoverMenuGroupLabel, PopoverMenuSearch, PopoverMenuScrollArea, };
@@ -0,0 +1,36 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Check } from "lucide-react";
5
+ import { Button } from "./button";
6
+ import { cn } from "./lib/utils";
7
+ import { InputControl } from "./input-control";
8
+ import { PopoverContent } from "./popover-primitive";
9
+ import { Typography } from "./typography";
10
+ const PopoverMenuContent = React.forwardRef(({ className, ...props }, ref) => (_jsx(PopoverContent, { ref: ref, className: cn("overflow-hidden p-1", className), ...props })));
11
+ PopoverMenuContent.displayName = "PopoverMenuContent";
12
+ function PopoverMenuHeader({ className, children, ...props }) {
13
+ return (_jsx(Typography, { as: "p", variant: "label", className: cn("px-2 py-1.5", className), ...props, children: children }));
14
+ }
15
+ function PopoverMenuSection({ className, children, ...props }) {
16
+ return (_jsx("div", { className: cn("space-y-0", className), ...props, children: children }));
17
+ }
18
+ function PopoverMenuItem({ className, children, active, type = "button", ...props }) {
19
+ return (_jsxs(Button, { type: type, variant: "ghost", className: cn("w-full justify-start rounded-sm text-left font-normal whitespace-normal", active &&
20
+ "bg-accent text-accent-foreground hover:bg-accent hover:text-accent-foreground", className), ...props, children: [children, active ? _jsx(Check, { "aria-hidden": "true", className: "ml-auto h-4 w-4 shrink-0" }) : null] }));
21
+ }
22
+ function PopoverMenuDivider({ className, ...props }) {
23
+ return _jsx("div", { className: cn("-mx-1 my-1 h-px bg-border", className), ...props });
24
+ }
25
+ function PopoverMenuGroup({ className, children, ...props }) {
26
+ return (_jsx("div", { className: cn("space-y-1", className), ...props, children: children }));
27
+ }
28
+ function PopoverMenuGroupLabel({ className, children, ...props }) {
29
+ return (_jsx(Typography, { as: "p", variant: "eyebrow", color: "muted", className: cn("px-2 py-1.5 font-normal normal-case tracking-normal", className), ...props, children: children }));
30
+ }
31
+ const PopoverMenuSearch = React.forwardRef(({ className, containerClassName, ...props }, ref) => (_jsx("div", { className: cn("-mx-1 -mt-1 mb-1 border-b px-2 py-1", containerClassName), children: _jsx(InputControl, { ref: ref, className: cn("h-8 border-0 bg-transparent px-2 py-0 text-sm shadow-none focus-visible:ring-0", className), ...props }) })));
32
+ PopoverMenuSearch.displayName = "PopoverMenuSearch";
33
+ function PopoverMenuScrollArea({ className, children, maxHeight = "max-h-64", ...props }) {
34
+ return (_jsx("div", { className: cn(maxHeight, "space-y-1 overflow-y-auto", className), ...props, children: children }));
35
+ }
36
+ export { PopoverMenuContent, PopoverMenuHeader, PopoverMenuSection, PopoverMenuItem, PopoverMenuDivider, PopoverMenuGroup, PopoverMenuGroupLabel, PopoverMenuSearch, PopoverMenuScrollArea, };
@@ -0,0 +1,7 @@
1
+ import * as React from "react";
2
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
3
+ declare const PopoverRoot: React.FC<PopoverPrimitive.PopoverProps>;
4
+ declare const PopoverTrigger: React.ForwardRefExoticComponent<PopoverPrimitive.PopoverTriggerProps & React.RefAttributes<HTMLButtonElement>>;
5
+ declare const PopoverAnchor: React.ForwardRefExoticComponent<PopoverPrimitive.PopoverAnchorProps & React.RefAttributes<HTMLDivElement>>;
6
+ declare const PopoverContent: React.ForwardRefExoticComponent<Omit<PopoverPrimitive.PopoverContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
7
+ export { PopoverAnchor, PopoverContent, PopoverRoot, PopoverTrigger };
@@ -0,0 +1,11 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
5
+ import { cn } from "./lib/utils";
6
+ const PopoverRoot = PopoverPrimitive.Root;
7
+ const PopoverTrigger = PopoverPrimitive.Trigger;
8
+ const PopoverAnchor = PopoverPrimitive.Anchor;
9
+ const PopoverContent = React.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => (_jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Content, { ref: ref, align: align, sideOffset: sideOffset, className: cn("z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none 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-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", className), ...props }) })));
10
+ PopoverContent.displayName = PopoverPrimitive.Content.displayName;
11
+ export { PopoverAnchor, PopoverContent, PopoverRoot, PopoverTrigger };
package/dist/popover.d.ts CHANGED
@@ -1,7 +1,12 @@
1
1
  import * as React from "react";
2
2
  import * as PopoverPrimitive from "@radix-ui/react-popover";
3
- declare const Popover: React.FC<PopoverPrimitive.PopoverProps>;
4
- declare const PopoverTrigger: React.ForwardRefExoticComponent<PopoverPrimitive.PopoverTriggerProps & React.RefAttributes<HTMLButtonElement>>;
5
- declare const PopoverAnchor: React.ForwardRefExoticComponent<PopoverPrimitive.PopoverAnchorProps & React.RefAttributes<HTMLDivElement>>;
6
- declare const PopoverContent: React.ForwardRefExoticComponent<Omit<PopoverPrimitive.PopoverContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
7
- export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };
3
+ type PopoverRootProps = React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Root>;
4
+ type PopoverContentProps = React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>;
5
+ export interface PopoverProps extends Pick<PopoverRootProps, "defaultOpen" | "modal" | "onOpenChange" | "open">, Omit<PopoverContentProps, "children" | "className"> {
6
+ trigger: React.ReactElement;
7
+ children: React.ReactNode;
8
+ surface?: "panel" | "menu";
9
+ contentClassName?: string;
10
+ }
11
+ declare function Popover({ defaultOpen, modal, onOpenChange, open, trigger, children, surface, contentClassName, align, sideOffset, ...contentProps }: PopoverProps): import("react/jsx-runtime").JSX.Element;
12
+ export { Popover };
package/dist/popover.js CHANGED
@@ -1,11 +1,8 @@
1
1
  "use client";
2
- import { jsx as _jsx } from "react/jsx-runtime";
3
- import * as React from "react";
4
- import * as PopoverPrimitive from "@radix-ui/react-popover";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
3
  import { cn } from "./lib/utils";
6
- const Popover = PopoverPrimitive.Root;
7
- const PopoverTrigger = PopoverPrimitive.Trigger;
8
- const PopoverAnchor = PopoverPrimitive.Anchor;
9
- const PopoverContent = React.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => (_jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Content, { ref: ref, align: align, sideOffset: sideOffset, className: cn("z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none 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-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", className), ...props }) })));
10
- PopoverContent.displayName = PopoverPrimitive.Content.displayName;
11
- export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };
4
+ import { PopoverContent, PopoverRoot, PopoverTrigger, } from "./popover-primitive";
5
+ function Popover({ defaultOpen, modal, onOpenChange, open, trigger, children, surface = "panel", contentClassName, align = "center", sideOffset = 4, ...contentProps }) {
6
+ return (_jsxs(PopoverRoot, { defaultOpen: defaultOpen, modal: modal, onOpenChange: onOpenChange, open: open, children: [_jsx(PopoverTrigger, { asChild: true, children: trigger }), _jsx(PopoverContent, { align: align, sideOffset: sideOffset, className: cn(surface === "menu" && "overflow-hidden p-1", contentClassName), ...contentProps, children: children })] }));
7
+ }
8
+ export { Popover };
@@ -0,0 +1,10 @@
1
+ import * as React from "react";
2
+ export interface ProgressProps extends React.HTMLAttributes<HTMLDivElement> {
3
+ value?: number;
4
+ max?: number;
5
+ showValue?: boolean;
6
+ size?: "sm" | "md" | "lg";
7
+ variant?: "default" | "success" | "error";
8
+ label?: string;
9
+ }
10
+ export declare function Progress({ value, max, showValue, size, variant, label, className, ...props }: ProgressProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,17 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { cn } from "./lib/utils";
4
+ const sizeClasses = {
5
+ sm: "h-1",
6
+ md: "h-2",
7
+ lg: "h-3",
8
+ };
9
+ const variantClasses = {
10
+ default: "bg-primary",
11
+ success: "bg-green-500",
12
+ error: "bg-destructive",
13
+ };
14
+ export function Progress({ value = 0, max = 100, showValue = false, size = "md", variant = "default", label = "Progress", className, ...props }) {
15
+ const percentage = Math.min(Math.max((value / max) * 100, 0), 100);
16
+ return (_jsxs("div", { className: cn("w-full", className), ...props, children: [_jsx("div", { className: cn("w-full overflow-hidden rounded-full bg-secondary", sizeClasses[size]), role: "progressbar", "aria-valuenow": value, "aria-valuemin": 0, "aria-valuemax": max, "aria-label": label, children: _jsx("div", { className: cn("h-full rounded-full transition-all duration-300 ease-out", variantClasses[variant]), style: { width: `${percentage}%` } }) }), showValue && (_jsxs("p", { className: "mt-1 text-right text-xs text-muted-foreground", children: [Math.round(percentage), "%"] }))] }));
17
+ }
@@ -67,7 +67,7 @@ const SearchInput = React.forwardRef(({ value, onValueChange, label = "Search",
67
67
  return (_jsxs(Field, { className: containerClassName, "data-disabled": disabled ? "true" : undefined, "data-invalid": isInvalid ? "true" : undefined, children: [_jsx(FieldLabel, { htmlFor: inputId, className: cn("sr-only", labelClassName), children: label }), _jsxs(SearchInputShell, { children: [_jsx(InputControl, { ref: ref, id: inputId, type: "text", placeholder: placeholder, value: value, onChange: handleChange, onKeyDown: handleKeyDown, disabled: disabled, "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": describedBy, className: cn("bg-background pl-9 pr-8", className), ...props }), value ? (_jsx("button", { type: "button", onClick: () => {
68
68
  onValueChange("");
69
69
  runSearch("");
70
- }, disabled: disabled, className: "absolute right-2 top-1/2 -translate-y-1/2 rounded-sm p-0.5 text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none", "aria-label": "Clear search", children: _jsx(XIcon, { size: 14 }) })) : null] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
70
+ }, disabled: disabled, className: "absolute right-2 top-1/2 -translate-y-1/2 cursor-pointer rounded-sm p-0.5 text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:pointer-events-none", "aria-label": "Clear search", children: _jsx(XIcon, { size: 14 }) })) : null] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
71
71
  });
72
72
  SearchInput.displayName = "SearchInput";
73
73
  export { SearchInput };
@@ -0,0 +1,22 @@
1
+ import * as React from "react";
2
+ type SectionNavIcon = React.ComponentType<{
3
+ className?: string;
4
+ }>;
5
+ export interface SectionNavItem {
6
+ id: string;
7
+ label: React.ReactNode;
8
+ icon?: SectionNavIcon;
9
+ href?: string;
10
+ target?: string;
11
+ rel?: string;
12
+ active?: boolean;
13
+ disabled?: boolean;
14
+ trailing?: React.ReactNode;
15
+ onClick?: () => void;
16
+ }
17
+ export interface SectionNavProps extends Omit<React.HTMLAttributes<HTMLElement>, "title"> {
18
+ title?: React.ReactNode;
19
+ items: SectionNavItem[];
20
+ }
21
+ declare function SectionNav({ title, items, className, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...props }: SectionNavProps): import("react/jsx-runtime").JSX.Element;
22
+ export { SectionNav };
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { Button } from "./button";
4
+ import { cn } from "./lib/utils";
5
+ import { Link } from "./link";
6
+ import { Typography } from "./typography";
7
+ function SectionNav({ title, items, className, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...props }) {
8
+ const titleId = React.useId();
9
+ const resolvedLabelledBy = ariaLabelledBy !== null && ariaLabelledBy !== void 0 ? ariaLabelledBy : (!ariaLabel && title != null && !React.isValidElement(title)
10
+ ? titleId
11
+ : undefined);
12
+ const renderedTitle = title == null ? null : React.isValidElement(title) ? (title) : (_jsx(Typography, { id: titleId, as: "h2", variant: "bodySm", className: "font-medium text-muted-foreground", children: title }));
13
+ return (_jsxs("nav", { className: cn("w-full space-y-3 rounded-xl bg-muted/40 p-3", className), "aria-label": ariaLabel, "aria-labelledby": resolvedLabelledBy, ...props, children: [renderedTitle, _jsx("ul", { className: "space-y-1.5", children: items.map((item) => {
14
+ const Icon = item.icon;
15
+ const itemClasses = cn("w-full justify-start", item.active &&
16
+ !item.disabled &&
17
+ "bg-white hover:bg-white");
18
+ const content = (_jsxs(_Fragment, { children: [Icon ? _jsx(Icon, {}) : null, _jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: item.label }), item.trailing ? (_jsx("span", { className: "shrink-0", children: item.trailing })) : null] }));
19
+ const isLink = Boolean(item.href) && !item.disabled;
20
+ return (_jsx("li", { children: _jsx(Button, { asChild: isLink, variant: "ghost", disabled: item.disabled, className: itemClasses, "aria-current": item.active ? "page" : undefined, ...(isLink
21
+ ? {}
22
+ : { type: "button", onClick: item.onClick }), children: isLink ? (_jsx(Link, { variant: "inherit", href: item.href, target: item.target, rel: item.rel, onClick: item.onClick, children: content })) : (content) }) }, item.id));
23
+ }) })] }));
24
+ }
25
+ export { SectionNav };
@@ -14,7 +14,7 @@ function SelectGroup(props) {
14
14
  function SelectValue(props) {
15
15
  return _jsx(SelectPrimitive.Value, { "data-slot": "select-value", ...props });
16
16
  }
17
- const SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => (_jsxs(SelectPrimitive.Trigger, { ref: ref, "data-slot": "select-trigger", className: cn("flex h-10 w-full cursor-pointer items-center justify-between gap-2 whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-base transition-[color,box-shadow] md:text-sm outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-[invalid=true]:border-destructive aria-[invalid=true]:ring-destructive/20 data-[placeholder]:text-muted-foreground [&>span]:line-clamp-1 [&_svg]:pointer-events-none [&_svg]:shrink-0", className), ...props, children: [children, _jsx(SelectPrimitive.Icon, { asChild: true, children: _jsx(ChevronDown, { className: "h-4 w-4 shrink-0 opacity-50" }) })] })));
17
+ const SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => (_jsxs(SelectPrimitive.Trigger, { ref: ref, "data-slot": "select-trigger", className: cn("flex h-control w-full cursor-pointer items-center justify-between gap-2 whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-[invalid=true]:border-destructive aria-[invalid=true]:ring-destructive/20 data-[placeholder]:text-muted-foreground [&>span]:line-clamp-1 [&_svg]:pointer-events-none [&_svg]:shrink-0", className), ...props, children: [children, _jsx(SelectPrimitive.Icon, { asChild: true, children: _jsx(ChevronDown, { className: "h-4 w-4 shrink-0 opacity-50" }) })] })));
18
18
  SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
19
19
  const SelectScrollUpButton = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.ScrollUpButton, { ref: ref, "data-slot": "select-scroll-up-button", className: cn("flex cursor-pointer items-center justify-center py-1", className), ...props, children: _jsx(ChevronUp, { className: "h-4 w-4" }) })));
20
20
  SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
@@ -27,7 +27,7 @@ const SelectContent = React.forwardRef(({ className, children, header, position
27
27
  SelectContent.displayName = SelectPrimitive.Content.displayName;
28
28
  const SelectLabel = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.Label, { ref: ref, "data-slot": "select-label", className: cn("px-2 py-1.5 text-xs text-muted-foreground", className), ...props })));
29
29
  SelectLabel.displayName = SelectPrimitive.Label.displayName;
30
- const SelectItem = React.forwardRef(({ className, children, description, ...props }, ref) => (_jsxs(SelectPrimitive.Item, { ref: ref, "data-slot": "select-item", className: cn("relative flex h-10 w-full cursor-pointer select-none items-center gap-2 rounded-sm pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className), ...props, children: [_jsx("span", { "data-slot": "select-item-indicator", className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: _jsx(SelectPrimitive.ItemIndicator, { children: _jsx(Check, { className: "h-4 w-4" }) }) }), _jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx(SelectPrimitive.ItemText, { className: "break-words font-normal", children: children }), description ? (_jsx(Typography, { as: "span", variant: "caption", children: description })) : null] })] })));
30
+ const SelectItem = React.forwardRef(({ className, children, description, ...props }, ref) => (_jsxs(SelectPrimitive.Item, { ref: ref, "data-slot": "select-item", className: cn("relative flex min-h-control w-full cursor-pointer select-none items-center gap-2 rounded-sm py-2 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className), ...props, children: [_jsx("span", { "data-slot": "select-item-indicator", className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: _jsx(SelectPrimitive.ItemIndicator, { children: _jsx(Check, { className: "h-4 w-4" }) }) }), _jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx(SelectPrimitive.ItemText, { className: "break-words font-normal", children: children }), description ? (_jsx(Typography, { as: "span", variant: "caption", children: description })) : null] })] })));
31
31
  SelectItem.displayName = SelectPrimitive.Item.displayName;
32
32
  const SelectSeparator = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.Separator, { ref: ref, "data-slot": "select-separator", className: cn("-mx-1 my-1 h-px bg-border", className), ...props })));
33
33
  SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
package/dist/select.js CHANGED
@@ -87,11 +87,11 @@ function Select({ label, options, placeholder = "Select an option", description,
87
87
  }
88
88
  onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(nextOpen);
89
89
  }, [onOpenChange, open]);
90
- 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(SelectRoot, { disabled: disabled, open: open, onOpenChange: handleOpenChange, ...props, children: [_jsx(SelectTrigger, { id: selectId, className: triggerClassName, "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": [descriptionId, errorId].filter(Boolean).join(" ") || undefined, children: _jsx(SelectValue, { placeholder: placeholder }) }), _jsx(SelectContent, { className: contentClassName, header: searchable ? (_jsx(InputControl, { ref: searchInputRef, value: searchQuery, onChange: (event) => setSearchQuery(event.target.value), placeholder: searchPlaceholder, "aria-label": searchInputAriaLabel, autoComplete: "off", className: "h-8 border-0 bg-transparent px-2 py-0 text-sm shadow-none focus-visible:border-0 focus-visible:ring-0", onKeyDown: (event) => {
90
+ 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(SelectRoot, { disabled: disabled, open: open, onOpenChange: handleOpenChange, ...props, children: [_jsx(SelectTrigger, { id: selectId, className: triggerClassName, "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": [descriptionId, errorId].filter(Boolean).join(" ") || undefined, children: _jsx(SelectValue, { placeholder: placeholder }) }), _jsx(SelectContent, { className: contentClassName, header: searchable ? (_jsx(InputControl, { ref: searchInputRef, value: searchQuery, onChange: (event) => setSearchQuery(event.target.value), placeholder: searchPlaceholder, "aria-label": searchInputAriaLabel, autoComplete: "off", className: "h-control border-0 bg-transparent px-2 py-0 text-sm shadow-none focus-visible:border-0 focus-visible:ring-0", onKeyDown: (event) => {
91
91
  event.stopPropagation();
92
92
  } })) : undefined, viewportClassName: searchable ? "max-h-64" : undefined, children: hasVisibleOptions ? (optionGroups.map((group, groupIndex) => {
93
93
  var _a;
94
94
  return (_jsxs(React.Fragment, { children: [_jsxs(SelectGroup, { children: [group.group ? (_jsx(SelectLabel, { children: _jsx(SearchHighlight, { text: group.group, query: searchQuery }) })) : null, group.options.map((option) => (_jsx(SelectItem, { value: option.value, disabled: option.disabled, textValue: getOptionTextValue(option), description: option.description, children: renderHighlightedLabel(option.label, searchQuery) }, option.value)))] }), groupIndex < optionGroups.length - 1 ? _jsx(SelectSeparator, {}) : null] }, (_a = group.group) !== null && _a !== void 0 ? _a : `group-${groupIndex}`));
95
- })) : (_jsx("div", { className: "px-3 py-6 text-sm text-muted-foreground", children: searchEmptyMessage })) })] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
95
+ })) : (_jsx("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: searchEmptyMessage })) })] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
96
96
  }
97
97
  export { Select };
@@ -0,0 +1,6 @@
1
+ import * as React from "react";
2
+ export interface SeparatorProps extends React.HTMLAttributes<HTMLDivElement> {
3
+ orientation?: "horizontal" | "vertical";
4
+ }
5
+ declare const Separator: React.ForwardRefExoticComponent<SeparatorProps & React.RefAttributes<HTMLDivElement>>;
6
+ export { Separator };
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "./lib/utils";
4
+ const Separator = React.forwardRef(({ className, orientation = "horizontal", ...props }, ref) => (_jsx("div", { ref: ref, role: "separator", "aria-orientation": orientation, className: cn("shrink-0 bg-border", orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]", className), ...props })));
5
+ Separator.displayName = "Separator";
6
+ export { Separator };
@@ -0,0 +1,4 @@
1
+ import * as React from "react";
2
+ export type SkeletonProps = React.HTMLAttributes<HTMLDivElement>;
3
+ declare function Skeleton({ className, ...props }: SkeletonProps): import("react/jsx-runtime").JSX.Element;
4
+ export { Skeleton };
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from "./lib/utils";
3
+ function Skeleton({ className, ...props }) {
4
+ return (_jsx("div", { "aria-hidden": "true", className: cn("animate-pulse rounded-md bg-muted/80", className), ...props }));
5
+ }
6
+ export { Skeleton };
@@ -0,0 +1,22 @@
1
+ import * as React from "react";
2
+ type SpinnerSize = "sm" | "md" | "lg";
3
+ export interface SpinnerProps extends React.HTMLAttributes<HTMLDivElement> {
4
+ size?: SpinnerSize;
5
+ /** Accessible label announced to screen readers. */
6
+ label?: string;
7
+ }
8
+ declare function Spinner({ size, label, className, ...props }: SpinnerProps): import("react/jsx-runtime").JSX.Element;
9
+ export interface PageLoaderProps extends Omit<SpinnerProps, "size"> {
10
+ size?: SpinnerSize;
11
+ /** Optional visible label shown beneath the spinner. */
12
+ message?: React.ReactNode;
13
+ /** Optional supporting description below the message. */
14
+ description?: React.ReactNode;
15
+ }
16
+ declare function PageLoader({ size, label, message, description, className, ...props }: PageLoaderProps): import("react/jsx-runtime").JSX.Element;
17
+ export interface FullPageLoaderProps extends PageLoaderProps {
18
+ /** Background overlay. `"solid"` blocks the page; `"blur"` keeps content visible. */
19
+ surface?: "solid" | "blur";
20
+ }
21
+ declare function FullPageLoader({ size, label, message, description, surface, className, ...props }: FullPageLoaderProps): import("react/jsx-runtime").JSX.Element;
22
+ export { Spinner, PageLoader, FullPageLoader };
@@ -0,0 +1,24 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { cn } from "./lib/utils";
4
+ import { Typography } from "./typography";
5
+ const SIZE_CLASS = {
6
+ sm: "h-4 w-4 border-2",
7
+ md: "h-6 w-6 border-2",
8
+ lg: "h-10 w-10 border-[3px]",
9
+ };
10
+ function Spinner({ size = "md", label = "Loading", className, ...props }) {
11
+ return (_jsxs("div", { role: "status", "aria-live": "polite", className: cn("inline-flex items-center justify-center", className), ...props, children: [_jsx("span", { "aria-hidden": "true", className: cn("shrink-0 animate-spin rounded-full border-current border-t-transparent text-primary", SIZE_CLASS[size]) }), _jsx("span", { className: "sr-only", children: label })] }));
12
+ }
13
+ function LoaderStack({ size, label, message, description, }) {
14
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: "mb-4", children: _jsx(Spinner, { size: size, label: label }) }), message || description ? (_jsxs("div", { className: "space-y-1.5", children: [message ? (_jsx(Typography, { as: "p", variant: "bodySm", className: "font-semibold", children: message })) : null, description ? (_jsx(Typography, { as: "p", variant: "caption", className: "max-w-md", children: description })) : null] })) : null] }));
15
+ }
16
+ function PageLoader({ size = "lg", label = "Loading", message, description, className, ...props }) {
17
+ return (_jsx("div", { className: cn("flex flex-col items-center justify-center px-6 py-10 text-center", className), ...props, children: _jsx(LoaderStack, { size: size, label: label, message: message, description: description }) }));
18
+ }
19
+ function FullPageLoader({ size = "lg", label = "Loading", message, description, surface = "solid", className, ...props }) {
20
+ return (_jsx("div", { className: cn("fixed inset-0 z-50 flex flex-col items-center justify-center px-6 py-10 text-center", surface === "solid"
21
+ ? "bg-background"
22
+ : "bg-background/60 backdrop-blur-sm", className), ...props, children: _jsx(LoaderStack, { size: size, label: label, message: message, description: description }) }));
23
+ }
24
+ export { Spinner, PageLoader, FullPageLoader };
package/dist/styles.css CHANGED
@@ -2,6 +2,43 @@
2
2
  @tailwind components;
3
3
  @tailwind utilities;
4
4
 
5
+ @keyframes collapsible-down {
6
+ from { height: 0; }
7
+ to { height: var(--radix-collapsible-content-height); }
8
+ }
9
+
10
+ @keyframes collapsible-up {
11
+ from { height: var(--radix-collapsible-content-height); }
12
+ to { height: 0; }
13
+ }
14
+
15
+ [data-slot="collapsible-content"][data-state="open"] {
16
+ animation: collapsible-down 200ms ease-out;
17
+ }
18
+
19
+ [data-slot="collapsible-content"][data-state="closed"] {
20
+ animation: collapsible-up 200ms ease-out;
21
+ }
22
+
23
+ @keyframes shake {
24
+ 0%,
25
+ 100% {
26
+ transform: translateX(0);
27
+ }
28
+ 20%,
29
+ 60% {
30
+ transform: translateX(-3px);
31
+ }
32
+ 40%,
33
+ 80% {
34
+ transform: translateX(3px);
35
+ }
36
+ }
37
+
38
+ .animate-shake {
39
+ animation: shake 0.4s cubic-bezier(0.36, 0.07, 0.19, 0.97);
40
+ }
41
+
5
42
  @layer base {
6
43
  :root {
7
44
  --space-0: 0rem;
@@ -15,6 +52,8 @@
15
52
  --space-10: 2.5rem;
16
53
  --space-12: 3rem;
17
54
 
55
+ --size-control: 2.5rem;
56
+
18
57
  --nea-green-50: 140 57% 96%;
19
58
  --nea-green-100: 141 50% 90%;
20
59
  --nea-green-200: 142 45% 80%;
@@ -208,5 +247,8 @@
208
247
  @apply bg-background text-foreground;
209
248
  font-family: var(--font-sans);
210
249
  font-weight: var(--font-weight-regular);
250
+ -webkit-font-smoothing: antialiased;
251
+ -moz-osx-font-smoothing: grayscale;
252
+ font-smooth: never;
211
253
  }
212
254
  }
@@ -0,0 +1,12 @@
1
+ import * as React from "react";
2
+ import * as SwitchPrimitives from "@radix-ui/react-switch";
3
+ export interface SwitchProps extends React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root> {
4
+ /** Label rendered next to the switch. When provided, wraps the control in a clickable `<label>`. */
5
+ label?: React.ReactNode;
6
+ /** Description shown below the label in smaller muted text. */
7
+ description?: React.ReactNode;
8
+ /** Optional class for the outer wrapping `<label>` (only used when `label` is set). */
9
+ wrapperClassName?: string;
10
+ }
11
+ declare const Switch: React.ForwardRefExoticComponent<SwitchProps & React.RefAttributes<HTMLButtonElement>>;
12
+ export { Switch };
package/dist/switch.js ADDED
@@ -0,0 +1,16 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import * as SwitchPrimitives from "@radix-ui/react-switch";
5
+ import { cn } from "./lib/utils";
6
+ import { Typography } from "./typography";
7
+ const Switch = React.forwardRef(({ className, label, description, wrapperClassName, id, ...props }, ref) => {
8
+ const generatedId = React.useId();
9
+ const switchId = id !== null && id !== void 0 ? id : `switch-${generatedId}`;
10
+ const control = (_jsx(SwitchPrimitives.Root, { id: switchId, ref: ref, className: cn("peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input", !label && className), ...props, children: _jsx(SwitchPrimitives.Thumb, { className: "pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0" }) }));
11
+ if (!label)
12
+ return control;
13
+ return (_jsxs("label", { htmlFor: switchId, className: cn("inline-flex cursor-pointer items-center gap-2 text-sm", props.disabled && "cursor-not-allowed opacity-50", wrapperClassName, className), children: [control, _jsxs("div", { className: "select-none", children: [_jsx(Typography, { as: "span", variant: "bodySm", children: label }), description ? (_jsx(Typography, { as: "p", variant: "caption", className: "mt-1", children: description })) : null] })] }));
14
+ });
15
+ Switch.displayName = "Switch";
16
+ export { Switch };
package/dist/table.d.ts CHANGED
@@ -67,6 +67,8 @@ export interface TableColumn<Row> {
67
67
  skeleton?: TableSkeletonConfig;
68
68
  headerClassName?: string;
69
69
  cellClassName?: string;
70
+ /** Pins the column to the left or right edge while the table scrolls horizontally. */
71
+ sticky?: "left" | "right";
70
72
  }
71
73
  export interface TableSortingProps {
72
74
  field: string | null;
@@ -102,6 +104,10 @@ export interface TableProps<Row> extends Omit<React.HTMLAttributes<HTMLDivElemen
102
104
  pagination?: Omit<PaginationProps, "className">;
103
105
  loading?: boolean;
104
106
  loadingRows?: number;
107
+ /** When true, applies alternating row backgrounds for easier scanning. */
108
+ striped?: boolean;
109
+ /** When set, rows become interactive: cursor-pointer, hover highlight, and click handling. */
110
+ onRowClick?: (row: Row, index: number) => void;
105
111
  }
106
- declare function Table<Row>({ columns, rows, getRowKey, emptyState, toolbar, caption, className, containerClassName, tableClassName, rowClassName, sorting, pagination, loading, loadingRows, ...props }: TableProps<Row>): import("react/jsx-runtime").JSX.Element;
112
+ declare function Table<Row>({ columns, rows, getRowKey, emptyState, toolbar, caption, className, containerClassName, tableClassName, rowClassName, sorting, pagination, loading, loadingRows, striped, onRowClick, ...props }: TableProps<Row>): import("react/jsx-runtime").JSX.Element;
107
113
  export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, SortableTableHead, TableRowSkeleton, };