@neasg/design-system 0.1.3 → 0.2.0

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 (187) hide show
  1. package/README.md +84 -9
  2. package/dist/animated-icons/arrow-down.d.ts +10 -0
  3. package/dist/animated-icons/arrow-down.js +54 -0
  4. package/dist/animated-icons/arrow-up.d.ts +10 -0
  5. package/dist/animated-icons/arrow-up.js +54 -0
  6. package/dist/animated-icons/bell.d.ts +10 -0
  7. package/dist/animated-icons/bell.js +42 -0
  8. package/dist/animated-icons/bold.d.ts +10 -0
  9. package/dist/animated-icons/bold.js +45 -0
  10. package/dist/animated-icons/book-open.d.ts +10 -0
  11. package/dist/animated-icons/book-open.js +55 -0
  12. package/dist/animated-icons/building.d.ts +10 -0
  13. package/dist/animated-icons/building.js +46 -0
  14. package/dist/animated-icons/check.d.ts +10 -0
  15. package/dist/animated-icons/check.js +55 -0
  16. package/dist/animated-icons/chevron-down.d.ts +10 -0
  17. package/dist/animated-icons/chevron-down.js +42 -0
  18. package/dist/animated-icons/chevron-left.d.ts +10 -0
  19. package/dist/animated-icons/chevron-left.js +42 -0
  20. package/dist/animated-icons/chevron-right.d.ts +10 -0
  21. package/dist/animated-icons/chevron-right.js +42 -0
  22. package/dist/animated-icons/chevron-up.d.ts +10 -0
  23. package/dist/animated-icons/chevron-up.js +42 -0
  24. package/dist/animated-icons/clipboard-check.d.ts +10 -0
  25. package/dist/animated-icons/clipboard-check.js +56 -0
  26. package/dist/animated-icons/download.d.ts +10 -0
  27. package/dist/animated-icons/download.js +47 -0
  28. package/dist/animated-icons/external-link.d.ts +10 -0
  29. package/dist/animated-icons/external-link.js +59 -0
  30. package/dist/animated-icons/eye.d.ts +10 -0
  31. package/dist/animated-icons/eye.js +41 -0
  32. package/dist/animated-icons/file-text.d.ts +11 -0
  33. package/dist/animated-icons/file-text.js +89 -0
  34. package/dist/animated-icons/filter.d.ts +10 -0
  35. package/dist/animated-icons/filter.js +78 -0
  36. package/dist/animated-icons/folder-open.d.ts +10 -0
  37. package/dist/animated-icons/folder-open.js +46 -0
  38. package/dist/animated-icons/form.d.ts +10 -0
  39. package/dist/animated-icons/form.js +56 -0
  40. package/dist/animated-icons/git-branch.d.ts +10 -0
  41. package/dist/animated-icons/git-branch.js +56 -0
  42. package/dist/animated-icons/grip-horizontal.d.ts +10 -0
  43. package/dist/animated-icons/grip-horizontal.js +70 -0
  44. package/dist/animated-icons/grip-vertical.d.ts +10 -0
  45. package/dist/animated-icons/grip-vertical.js +67 -0
  46. package/dist/animated-icons/grip.d.ts +10 -0
  47. package/dist/animated-icons/grip.js +72 -0
  48. package/dist/animated-icons/highlighter.d.ts +10 -0
  49. package/dist/animated-icons/highlighter.js +46 -0
  50. package/dist/animated-icons/history.d.ts +10 -0
  51. package/dist/animated-icons/history.js +80 -0
  52. package/dist/animated-icons/index.d.ts +100 -0
  53. package/dist/animated-icons/index.js +50 -0
  54. package/dist/animated-icons/italic.d.ts +10 -0
  55. package/dist/animated-icons/italic.js +45 -0
  56. package/dist/animated-icons/link.d.ts +10 -0
  57. package/dist/animated-icons/link.js +51 -0
  58. package/dist/animated-icons/list-ordered.d.ts +10 -0
  59. package/dist/animated-icons/list-ordered.js +46 -0
  60. package/dist/animated-icons/list.d.ts +10 -0
  61. package/dist/animated-icons/list.js +56 -0
  62. package/dist/animated-icons/loader-pinwheel.d.ts +10 -0
  63. package/dist/animated-icons/loader-pinwheel.js +51 -0
  64. package/dist/animated-icons/logout.d.ts +10 -0
  65. package/dist/animated-icons/logout.js +44 -0
  66. package/dist/animated-icons/maximize.d.ts +10 -0
  67. package/dist/animated-icons/maximize.js +45 -0
  68. package/dist/animated-icons/message-square.d.ts +10 -0
  69. package/dist/animated-icons/message-square.js +56 -0
  70. package/dist/animated-icons/palette.d.ts +10 -0
  71. package/dist/animated-icons/palette.js +45 -0
  72. package/dist/animated-icons/paperclip.d.ts +10 -0
  73. package/dist/animated-icons/paperclip.js +45 -0
  74. package/dist/animated-icons/pencil.d.ts +10 -0
  75. package/dist/animated-icons/pencil.js +46 -0
  76. package/dist/animated-icons/plus.d.ts +10 -0
  77. package/dist/animated-icons/plus.js +42 -0
  78. package/dist/animated-icons/search.d.ts +10 -0
  79. package/dist/animated-icons/search.js +44 -0
  80. package/dist/animated-icons/send.d.ts +10 -0
  81. package/dist/animated-icons/send.js +46 -0
  82. package/dist/animated-icons/settings.d.ts +10 -0
  83. package/dist/animated-icons/settings.js +42 -0
  84. package/dist/animated-icons/shield.d.ts +10 -0
  85. package/dist/animated-icons/shield.js +45 -0
  86. package/dist/animated-icons/strikethrough.d.ts +10 -0
  87. package/dist/animated-icons/strikethrough.js +45 -0
  88. package/dist/animated-icons/timer.d.ts +10 -0
  89. package/dist/animated-icons/timer.js +70 -0
  90. package/dist/animated-icons/trash.d.ts +10 -0
  91. package/dist/animated-icons/trash.js +56 -0
  92. package/dist/animated-icons/trending-down.d.ts +10 -0
  93. package/dist/animated-icons/trending-down.js +86 -0
  94. package/dist/animated-icons/trending-up.d.ts +10 -0
  95. package/dist/animated-icons/trending-up.js +86 -0
  96. package/dist/animated-icons/underline.d.ts +10 -0
  97. package/dist/animated-icons/underline.js +45 -0
  98. package/dist/animated-icons/upload.d.ts +10 -0
  99. package/dist/animated-icons/upload.js +47 -0
  100. package/dist/animated-icons/users.d.ts +10 -0
  101. package/dist/animated-icons/users.js +45 -0
  102. package/dist/animated-icons/x.d.ts +10 -0
  103. package/dist/animated-icons/x.js +45 -0
  104. package/dist/avatar.d.ts +32 -0
  105. package/dist/avatar.js +21 -0
  106. package/dist/badge.d.ts +4 -3
  107. package/dist/badge.js +7 -12
  108. package/dist/breadcrumb.d.ts +30 -0
  109. package/dist/breadcrumb.js +32 -0
  110. package/dist/button.d.ts +2 -2
  111. package/dist/button.js +86 -8
  112. package/dist/command-search.d.ts +44 -0
  113. package/dist/command-search.js +73 -0
  114. package/dist/command.d.ts +77 -0
  115. package/dist/command.js +24 -0
  116. package/dist/count-badge.d.ts +10 -0
  117. package/dist/count-badge.js +34 -0
  118. package/dist/dialog-primitive.d.ts +22 -0
  119. package/dist/dialog-primitive.js +23 -0
  120. package/dist/dialog.d.ts +28 -0
  121. package/dist/dialog.js +60 -0
  122. package/dist/draggable-tabs.d.ts +27 -0
  123. package/dist/draggable-tabs.js +61 -0
  124. package/dist/drawer.d.ts +30 -0
  125. package/dist/drawer.js +77 -0
  126. package/dist/editable-table.d.ts +31 -0
  127. package/dist/editable-table.js +122 -0
  128. package/dist/email-input.d.ts +16 -0
  129. package/dist/email-input.js +55 -0
  130. package/dist/empty-state.d.ts +11 -0
  131. package/dist/empty-state.js +7 -0
  132. package/dist/field.d.ts +6 -0
  133. package/dist/field.js +18 -0
  134. package/dist/filter-toggle.d.ts +12 -0
  135. package/dist/filter-toggle.js +23 -0
  136. package/dist/index.d.ts +53 -4
  137. package/dist/index.js +31 -4
  138. package/dist/input-control.d.ts +4 -0
  139. package/dist/input-control.js +7 -0
  140. package/dist/input-validation.d.ts +9 -0
  141. package/dist/input-validation.js +75 -0
  142. package/dist/input.d.ts +16 -0
  143. package/dist/input.js +22 -0
  144. package/dist/label-value-grid.d.ts +13 -0
  145. package/dist/label-value-grid.js +61 -0
  146. package/dist/label-value.d.ts +11 -0
  147. package/dist/label-value.js +19 -0
  148. package/dist/layout.d.ts +55 -0
  149. package/dist/layout.js +140 -0
  150. package/dist/otp-input.d.ts +21 -0
  151. package/dist/otp-input.js +60 -0
  152. package/dist/page-header.d.ts +9 -0
  153. package/dist/page-header.js +6 -0
  154. package/dist/pagination.d.ts +12 -0
  155. package/dist/pagination.js +24 -0
  156. package/dist/phone-input.d.ts +36 -0
  157. package/dist/phone-input.js +135 -0
  158. package/dist/right-drawer.d.ts +2 -0
  159. package/dist/right-drawer.js +1 -0
  160. package/dist/search-input-shell.d.ts +8 -0
  161. package/dist/search-input-shell.js +9 -0
  162. package/dist/search-input.d.ts +20 -0
  163. package/dist/search-input.js +73 -0
  164. package/dist/select-primitive.d.ts +17 -0
  165. package/dist/select-primitive.js +33 -0
  166. package/dist/select.d.ts +31 -0
  167. package/dist/select.js +97 -0
  168. package/dist/styles.css +111 -1
  169. package/dist/table-toolbar.d.ts +7 -0
  170. package/dist/table-toolbar.js +7 -0
  171. package/dist/table.d.ts +87 -3
  172. package/dist/table.js +102 -5
  173. package/dist/tabs.d.ts +24 -7
  174. package/dist/tabs.js +22 -26
  175. package/dist/tailwind-preset.d.ts +4 -0
  176. package/dist/tailwind-preset.js +97 -0
  177. package/dist/textarea.d.ts +16 -0
  178. package/dist/textarea.js +23 -0
  179. package/dist/theme.d.ts +259 -0
  180. package/dist/theme.js +118 -0
  181. package/dist/tooltip.d.ts +15 -3
  182. package/dist/tooltip.js +9 -4
  183. package/dist/typography.d.ts +11 -0
  184. package/dist/typography.js +41 -0
  185. package/dist/use-search-highlight.d.ts +13 -0
  186. package/dist/use-search-highlight.js +46 -0
  187. package/package.json +124 -3
@@ -0,0 +1,30 @@
1
+ import * as React from "react";
2
+ import { type ButtonProps } from "./button";
3
+ interface DrawerRenderHelpers {
4
+ close: () => void;
5
+ }
6
+ export type DrawerSide = "left" | "right";
7
+ export interface DrawerAction extends Omit<ButtonProps, "children"> {
8
+ label: React.ReactNode;
9
+ closeOnClick?: boolean;
10
+ }
11
+ type DrawerRenderable = React.ReactNode | ((helpers: DrawerRenderHelpers) => React.ReactNode);
12
+ export interface DrawerProps {
13
+ open?: boolean;
14
+ defaultOpen?: boolean;
15
+ onOpenChange?: (open: boolean) => void;
16
+ modal?: boolean;
17
+ trigger?: React.ReactElement;
18
+ title: React.ReactNode;
19
+ description?: React.ReactNode;
20
+ children?: DrawerRenderable;
21
+ footer?: DrawerRenderable;
22
+ primaryAction?: DrawerAction;
23
+ secondaryAction?: DrawerAction;
24
+ side?: DrawerSide;
25
+ contentClassName?: string;
26
+ bodyClassName?: string;
27
+ hideCloseButton?: boolean;
28
+ }
29
+ declare function Drawer({ open, defaultOpen, onOpenChange, modal, trigger, title, description, children, footer, primaryAction, secondaryAction, side, contentClassName, bodyClassName, hideCloseButton, }: DrawerProps): import("react/jsx-runtime").JSX.Element;
30
+ export { Drawer };
package/dist/drawer.js ADDED
@@ -0,0 +1,77 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
5
+ import { cva } from "class-variance-authority";
6
+ import { XIcon } from "./animated-icons/x";
7
+ import { Button } from "./button";
8
+ import { DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogRoot, DialogTitle, DialogTrigger, } from "./dialog-primitive";
9
+ import { cn } from "./lib/utils";
10
+ function useControllableOpen({ open, defaultOpen, onOpenChange, }) {
11
+ const [internalOpen, setInternalOpen] = React.useState(defaultOpen !== null && defaultOpen !== void 0 ? defaultOpen : false);
12
+ const isControlled = open !== undefined;
13
+ const currentOpen = isControlled ? open : internalOpen;
14
+ const setOpen = React.useCallback((next) => {
15
+ if (!isControlled) {
16
+ setInternalOpen(next);
17
+ }
18
+ onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(next);
19
+ }, [isControlled, onOpenChange]);
20
+ return [currentOpen, setOpen];
21
+ }
22
+ function resolveDrawerRenderable(value, helpers) {
23
+ if (typeof value === "function") {
24
+ return value(helpers);
25
+ }
26
+ return value;
27
+ }
28
+ function renderDrawerAction({ action, close, defaultVariant, }) {
29
+ const { label, onClick, closeOnClick = true, type = "button", variant = defaultVariant, ...buttonProps } = action;
30
+ return (_jsx(Button, { ...buttonProps, type: type, variant: variant, onClick: (event) => {
31
+ onClick === null || onClick === void 0 ? void 0 : onClick(event);
32
+ if (!event.defaultPrevented && closeOnClick) {
33
+ close();
34
+ }
35
+ }, children: label }));
36
+ }
37
+ const drawerSurfaceVariants = cva("fixed z-50 flex flex-col gap-4 overflow-y-auto bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out", {
38
+ variants: {
39
+ side: {
40
+ left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-lg",
41
+ right: "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-lg",
42
+ },
43
+ },
44
+ defaultVariants: {
45
+ side: "right",
46
+ },
47
+ });
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] })] }));
50
+ }
51
+ function Drawer({ open, defaultOpen, onOpenChange, modal, trigger, title, description, children, footer, primaryAction, secondaryAction, side = "right", contentClassName, bodyClassName, hideCloseButton, }) {
52
+ const [isOpen, setIsOpen] = useControllableOpen({
53
+ open,
54
+ defaultOpen,
55
+ onOpenChange,
56
+ });
57
+ const helpers = React.useMemo(() => ({
58
+ close: () => setIsOpen(false),
59
+ }), [setIsOpen]);
60
+ const body = resolveDrawerRenderable(children, helpers);
61
+ const customFooter = resolveDrawerRenderable(footer, helpers);
62
+ const hasFooter = Boolean(customFooter || primaryAction || secondaryAction);
63
+ return (_jsxs(DialogRoot, { open: isOpen, onOpenChange: setIsOpen, modal: modal, children: [trigger ? _jsx(DialogTrigger, { asChild: true, children: trigger }) : null, _jsxs(DrawerSurface, { side: side, className: contentClassName, hideCloseButton: hideCloseButton, children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: title }), description ? (_jsx(DialogDescription, { children: description })) : null] }), body ? _jsx("div", { className: cn("grid gap-4", bodyClassName), children: body }) : null, hasFooter ? (_jsx(DialogFooter, { className: "mt-auto", children: customFooter !== null && customFooter !== void 0 ? customFooter : (_jsxs(_Fragment, { children: [secondaryAction
64
+ ? renderDrawerAction({
65
+ action: secondaryAction,
66
+ close: helpers.close,
67
+ defaultVariant: "outline",
68
+ })
69
+ : null, primaryAction
70
+ ? renderDrawerAction({
71
+ action: primaryAction,
72
+ close: helpers.close,
73
+ defaultVariant: "default",
74
+ })
75
+ : null] })) })) : null] })] }));
76
+ }
77
+ export { Drawer };
@@ -0,0 +1,31 @@
1
+ import * as React from "react";
2
+ export interface EditableTableColumn {
3
+ header: string;
4
+ path: string;
5
+ width?: string;
6
+ minWidth?: string;
7
+ maxWidth?: string;
8
+ }
9
+ export interface EditableTableRowActionContext {
10
+ row: Record<string, unknown>;
11
+ rowIndex: number;
12
+ rows: Record<string, unknown>[];
13
+ isEditing: boolean;
14
+ updateRow: (nextRow: Record<string, unknown>) => void;
15
+ insertRow: (nextRow: Record<string, unknown>, index?: number) => void;
16
+ removeRow: () => void;
17
+ }
18
+ export interface EditableTableProps {
19
+ value: Record<string, unknown>[];
20
+ onChange: (value: Record<string, unknown>[]) => void;
21
+ columns: EditableTableColumn[];
22
+ isEditing?: boolean;
23
+ className?: string;
24
+ emptyPlaceholder?: string;
25
+ renderRowActions?: (context: EditableTableRowActionContext) => React.ReactNode;
26
+ }
27
+ declare function EditableTable({ value, onChange, columns, isEditing, className, emptyPlaceholder, renderRowActions, }: EditableTableProps): import("react/jsx-runtime").JSX.Element;
28
+ declare namespace EditableTable {
29
+ var displayName: string;
30
+ }
31
+ export { EditableTable };
@@ -0,0 +1,122 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { Button } from "./button";
4
+ import { PlusIcon } from "./animated-icons/plus";
5
+ import { TrashIcon } from "./animated-icons/trash";
6
+ import { InputControl } from "./input-control";
7
+ import { TableRoot as Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "./table";
8
+ import { cn } from "./lib/utils";
9
+ function getNestedValue(obj, path) {
10
+ return path.split(".").reduce((acc, part) => {
11
+ if (acc && typeof acc === "object" && part in acc) {
12
+ return acc[part];
13
+ }
14
+ return undefined;
15
+ }, obj);
16
+ }
17
+ function setNestedValue(obj, path, value) {
18
+ const result = { ...obj };
19
+ const parts = path.split(".");
20
+ let current = result;
21
+ for (let index = 0; index < parts.length - 1; index += 1) {
22
+ const part = parts[index];
23
+ if (!(part in current) || typeof current[part] !== "object") {
24
+ current[part] = {};
25
+ }
26
+ else {
27
+ current[part] = { ...current[part] };
28
+ }
29
+ current = current[part];
30
+ }
31
+ current[parts[parts.length - 1]] = value;
32
+ return result;
33
+ }
34
+ function EditableTable({ value, onChange, columns, isEditing = false, className, emptyPlaceholder = "-", renderRowActions, }) {
35
+ const [editingCell, setEditingCell] = React.useState(null);
36
+ const [editValue, setEditValue] = React.useState("");
37
+ const inputRef = React.useRef(null);
38
+ const getColumnMaxWidth = React.useCallback((column) => { var _a; return (_a = column.maxWidth) !== null && _a !== void 0 ? _a : "320px"; }, []);
39
+ const getColumnMinWidth = React.useCallback((column) => { var _a; return (_a = column.minWidth) !== null && _a !== void 0 ? _a : `${Math.max(column.header.length + 4, 12)}ch`; }, []);
40
+ React.useEffect(() => {
41
+ if (editingCell && inputRef.current) {
42
+ inputRef.current.focus();
43
+ inputRef.current.select();
44
+ }
45
+ }, [editingCell]);
46
+ const handleCellClick = (rowIndex, colPath) => {
47
+ if (!isEditing) {
48
+ return;
49
+ }
50
+ const cellValue = getNestedValue(value[rowIndex], colPath);
51
+ setEditValue(cellValue !== undefined && cellValue !== null ? String(cellValue) : "");
52
+ setEditingCell({ rowIndex, colPath });
53
+ };
54
+ const commitEdit = () => {
55
+ if (!editingCell) {
56
+ return;
57
+ }
58
+ const { rowIndex, colPath } = editingCell;
59
+ const nextRows = [...value];
60
+ nextRows[rowIndex] = setNestedValue(nextRows[rowIndex], colPath, editValue);
61
+ onChange(nextRows);
62
+ setEditingCell(null);
63
+ };
64
+ const handleReplaceRow = React.useCallback((rowIndex, nextRow) => {
65
+ const nextRows = [...value];
66
+ nextRows[rowIndex] = nextRow;
67
+ onChange(nextRows);
68
+ }, [onChange, value]);
69
+ const handleInsertRow = React.useCallback((nextRow, index = value.length) => {
70
+ const nextRows = [...value];
71
+ nextRows.splice(index, 0, nextRow);
72
+ onChange(nextRows);
73
+ }, [onChange, value]);
74
+ const handleKeyDown = (event) => {
75
+ if (event.key === "Enter") {
76
+ commitEdit();
77
+ return;
78
+ }
79
+ if (event.key === "Escape") {
80
+ setEditingCell(null);
81
+ }
82
+ };
83
+ const handleAddRow = () => {
84
+ let nextRow = {};
85
+ columns.forEach((column) => {
86
+ nextRow = setNestedValue(nextRow, column.path, "");
87
+ });
88
+ handleInsertRow(nextRow);
89
+ };
90
+ const handleDeleteRow = React.useCallback((rowIndex) => {
91
+ onChange(value.filter((_, index) => index !== rowIndex));
92
+ }, [onChange, value]);
93
+ const formatCellValue = (cellValue) => {
94
+ if (cellValue === null || cellValue === undefined || cellValue === "") {
95
+ return emptyPlaceholder;
96
+ }
97
+ return String(cellValue);
98
+ };
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) => {
101
+ const cellValue = getNestedValue(row, column.path);
102
+ const isEditingCell = (editingCell === null || editingCell === void 0 ? void 0 : editingCell.rowIndex) === rowIndex &&
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-9 w-full text-sm" })) : (_jsx("span", { className: cn("block whitespace-normal break-words", cellValue === null ||
105
+ cellValue === undefined ||
106
+ cellValue === ""
107
+ ? "text-muted-foreground"
108
+ : undefined), style: { maxWidth: getColumnMaxWidth(column) }, children: formatCellValue(cellValue) })) }, `cell-${rowIndex}-${columnIndex}`));
109
+ }), hasActionsColumn ? (_jsx(TableCell, { children: _jsxs("div", { className: "flex items-center justify-end gap-1", children: [renderRowActions
110
+ ? renderRowActions({
111
+ row,
112
+ rowIndex,
113
+ rows: value,
114
+ isEditing,
115
+ updateRow: (nextRow) => handleReplaceRow(rowIndex, nextRow),
116
+ insertRow: (nextRow, index = rowIndex + 1) => handleInsertRow(nextRow, index),
117
+ removeRow: () => handleDeleteRow(rowIndex),
118
+ })
119
+ : null, isEditing ? (_jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "h-8 w-8 text-destructive hover:bg-destructive/10 hover:text-destructive", onClick: () => handleDeleteRow(rowIndex), "aria-label": `Delete row ${rowIndex + 1}`, children: _jsx(TrashIcon, { size: 16 }) })) : null] }) })) : null] }, `row-${rowIndex}`)))) })] }) }), isEditing ? (_jsxs(Button, { type: "button", variant: "outline", onClick: handleAddRow, className: "w-full justify-center", children: [_jsx(PlusIcon, { size: 16 }), "Add Row"] })) : null] }));
120
+ }
121
+ EditableTable.displayName = "EditableTable";
122
+ export { EditableTable };
@@ -0,0 +1,16 @@
1
+ import * as React from "react";
2
+ import { type InputProps } from "./input";
3
+ export interface EmailInputValidatorHelpers {
4
+ super: () => string | undefined;
5
+ }
6
+ export type EmailInputValidator = (value: string, helpers: EmailInputValidatorHelpers) => string | undefined;
7
+ export interface EmailInputProps extends Omit<InputProps, "error" | "invalid" | "type"> {
8
+ error?: string;
9
+ invalid?: boolean;
10
+ onClearError?: () => void;
11
+ fieldName?: string;
12
+ validateOnChange?: boolean;
13
+ validator?: EmailInputValidator;
14
+ }
15
+ declare const EmailInput: React.ForwardRefExoticComponent<Omit<EmailInputProps, "ref"> & React.RefAttributes<HTMLInputElement>>;
16
+ export { EmailInput };
@@ -0,0 +1,55 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { Input } from "./input";
4
+ import { combineValidators, validateEmail, validateRequired, } from "./input-validation";
5
+ const EmailInput = React.forwardRef(({ onChange, error: externalError, invalid = false, onClearError, required = false, fieldName = "Email address", validateOnChange = false, validator, onBlur, inputClassName, autoComplete = "email", placeholder = "name@example.com", ...props }, ref) => {
6
+ const [internalError, setInternalError] = React.useState();
7
+ const [touched, setTouched] = React.useState(false);
8
+ const validateBase = React.useMemo(() => {
9
+ const rules = [];
10
+ if (required) {
11
+ rules.push(validateRequired(fieldName));
12
+ }
13
+ rules.push(validateEmail);
14
+ return combineValidators(...rules);
15
+ }, [fieldName, required]);
16
+ const validate = React.useCallback((nextValue) => {
17
+ const runSuper = () => validateBase(nextValue);
18
+ if (validator) {
19
+ return validator(nextValue, { super: runSuper });
20
+ }
21
+ return runSuper();
22
+ }, [validateBase, validator]);
23
+ const handleChange = React.useCallback((event) => {
24
+ const nextValue = event.target.value;
25
+ onChange === null || onChange === void 0 ? void 0 : onChange(event);
26
+ if (externalError) {
27
+ onClearError === null || onClearError === void 0 ? void 0 : onClearError();
28
+ }
29
+ if (validateOnChange && touched) {
30
+ setInternalError(validate(nextValue));
31
+ return;
32
+ }
33
+ if (internalError) {
34
+ setInternalError(undefined);
35
+ }
36
+ }, [
37
+ externalError,
38
+ internalError,
39
+ onChange,
40
+ onClearError,
41
+ touched,
42
+ validate,
43
+ validateOnChange,
44
+ ]);
45
+ const handleBlur = React.useCallback((event) => {
46
+ const nextValue = event.currentTarget.value;
47
+ setTouched(true);
48
+ setInternalError(validate(nextValue));
49
+ onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
50
+ }, [onBlur, validate]);
51
+ const displayError = externalError !== null && externalError !== void 0 ? externalError : internalError;
52
+ return (_jsx(Input, { ref: ref, type: "email", onChange: handleChange, onBlur: handleBlur, placeholder: placeholder, autoComplete: autoComplete, required: required, error: displayError, invalid: invalid || Boolean(displayError), inputClassName: inputClassName, ...props }));
53
+ });
54
+ EmailInput.displayName = "EmailInput";
55
+ export { EmailInput };
@@ -0,0 +1,11 @@
1
+ import * as React from "react";
2
+ export interface EmptyStateProps extends React.HTMLAttributes<HTMLDivElement> {
3
+ message: React.ReactNode;
4
+ description?: React.ReactNode;
5
+ icon?: React.ComponentType<{
6
+ className?: string;
7
+ }>;
8
+ action?: React.ReactNode;
9
+ }
10
+ declare function EmptyState({ message, description, icon: Icon, action, className, ...props }: EmptyStateProps): import("react/jsx-runtime").JSX.Element;
11
+ export { EmptyState };
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "./lib/utils";
3
+ import { Typography } from "./typography";
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] }));
6
+ }
7
+ export { EmptyState };
@@ -0,0 +1,6 @@
1
+ import * as React from "react";
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;
4
+ declare function FieldDescription({ className, ...props }: React.ComponentProps<"p">): import("react/jsx-runtime").JSX.Element;
5
+ declare function FieldError({ className, ...props }: React.ComponentProps<"p">): import("react/jsx-runtime").JSX.Element | null;
6
+ export { Field, FieldDescription, FieldError, FieldLabel };
package/dist/field.js ADDED
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from "./lib/utils";
3
+ function Field({ className, ...props }) {
4
+ return (_jsx("div", { "data-slot": "field", className: cn("group/field flex w-full flex-col gap-2", className), ...props }));
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 }));
8
+ }
9
+ function FieldDescription({ className, ...props }) {
10
+ return (_jsx("p", { "data-slot": "field-description", className: cn("text-sm leading-normal text-muted-foreground", className), ...props }));
11
+ }
12
+ function FieldError({ className, ...props }) {
13
+ if (!props.children) {
14
+ return null;
15
+ }
16
+ return (_jsx("p", { role: "alert", "data-slot": "field-error", className: cn("text-sm leading-normal text-destructive", className), ...props }));
17
+ }
18
+ export { Field, FieldDescription, FieldError, FieldLabel };
@@ -0,0 +1,12 @@
1
+ import * as React from "react";
2
+ import { type VariantProps } from "class-variance-authority";
3
+ declare const filterToggleVariants: (props?: ({
4
+ variant?: "muted" | "default" | null | undefined;
5
+ selected?: boolean | null | undefined;
6
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
+ export interface FilterToggleProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof filterToggleVariants> {
8
+ selected?: boolean;
9
+ icon?: React.ReactNode;
10
+ }
11
+ declare const FilterToggle: React.ForwardRefExoticComponent<FilterToggleProps & React.RefAttributes<HTMLButtonElement>>;
12
+ export { FilterToggle, filterToggleVariants };
@@ -0,0 +1,23 @@
1
+ import { jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cva } from "class-variance-authority";
4
+ import { cn } from "./lib/utils";
5
+ const filterToggleVariants = cva("inline-flex cursor-pointer select-none items-center justify-center gap-1.5 rounded-md border px-2.5 py-1 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", {
6
+ variants: {
7
+ variant: {
8
+ default: "",
9
+ muted: "",
10
+ },
11
+ selected: {
12
+ true: "border-transparent bg-primary/10 text-primary",
13
+ false: "border-transparent bg-muted text-muted-foreground hover:bg-muted/80",
14
+ },
15
+ },
16
+ defaultVariants: {
17
+ variant: "default",
18
+ selected: false,
19
+ },
20
+ });
21
+ const FilterToggle = React.forwardRef(({ className, variant, selected = false, icon, children, ...props }, ref) => (_jsxs("button", { ref: ref, className: cn(filterToggleVariants({ variant, selected, className })), ...props, children: [icon, children] })));
22
+ FilterToggle.displayName = "FilterToggle";
23
+ export { FilterToggle, filterToggleVariants };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,58 @@
1
+ export { Avatar, AvatarProfile, AvatarRoot, AvatarImage, AvatarFallback, } from "./avatar";
2
+ export type { AvatarProps, AvatarProfileProps, AvatarRootProps, } from "./avatar";
3
+ export * from "./animated-icons";
1
4
  export { Badge, badgeVariants } from "./badge";
2
5
  export type { BadgeProps } from "./badge";
6
+ export { CountBadge } from "./count-badge";
7
+ export type { CountBadgeProps } from "./count-badge";
8
+ export { EmptyState } from "./empty-state";
9
+ export type { EmptyStateProps } from "./empty-state";
10
+ export { EditableTable } from "./editable-table";
11
+ export type { EditableTableColumn, EditableTableProps, EditableTableRowActionContext, } from "./editable-table";
12
+ export { EmailInput } from "./email-input";
13
+ export type { EmailInputProps, EmailInputValidator, EmailInputValidatorHelpers, } from "./email-input";
14
+ export { Input } from "./input";
15
+ export type { InputProps } from "./input";
16
+ export { Select } from "./select";
17
+ export type { SelectOption, SelectProps } from "./select";
18
+ export { DEFAULT_COUNTRY_CODES, PhoneInput, } from "./phone-input";
19
+ export type { CountryCode, PhoneInputProps, } from "./phone-input";
20
+ export { OtpInput } from "./otp-input";
21
+ export type { OtpInputProps } from "./otp-input";
22
+ export { LabelValue, isEmptyValue, } from "./label-value";
23
+ export type { LabelValueProps, } from "./label-value";
24
+ export { LabelValueGrid } from "./label-value-grid";
25
+ export type { LabelValueGridItem, LabelValueGridProps, } from "./label-value-grid";
26
+ export { Breadcrumb, BreadcrumbNav, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis, } from "./breadcrumb";
27
+ export type { BreadcrumbEntry, BreadcrumbProps } from "./breadcrumb";
3
28
  export { Button, buttonVariants } from "./button";
4
29
  export type { ButtonProps } from "./button";
5
- export { neaPalette, semanticColorTokens } from "./theme";
6
- export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, } from "./table";
7
- export { Tabs, TabsList, TabsTrigger, TabsContent, useTabsContext, } from "./tabs";
8
- export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "./tooltip";
30
+ export { Dialog } from "./dialog";
31
+ export type { DialogAction, DialogProps } from "./dialog";
32
+ export { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator, CommandShortcut, } from "./command";
33
+ export { CommandSearch, type CommandSearchFilter, type CommandSearchItem, type CommandSearchProps, type CommandSearchSection, } from "./command-search";
34
+ export { SearchInput } from "./search-input";
35
+ export type { SearchInputProps } from "./search-input";
36
+ export { SearchHighlight, useSearchHighlight, } from "./use-search-highlight";
37
+ export type { SearchHighlightProps, SearchHighlightSegment, } from "./use-search-highlight";
38
+ export { Textarea } from "./textarea";
39
+ export type { TextareaProps } from "./textarea";
40
+ export { DraggableTabs, type DraggableTabItem, type DraggableTabsProps, } from "./draggable-tabs";
41
+ export { Drawer, } from "./drawer";
42
+ export type { DrawerAction, DrawerProps, DrawerSide } from "./drawer";
43
+ export { DashboardLayout, DashboardSidebar, DashboardContent, SidebarProvider, useSidebar, } from "./layout";
44
+ export type { DashboardSidebarNavGroup, DashboardSidebarNavItem, } from "./layout";
45
+ export { PageHeader } from "./page-header";
46
+ export { Typography, typographyVariants } from "./typography";
47
+ export type { TypographyProps } from "./typography";
48
+ export { neaPalette, semanticColorTokens, typographyTokens, radiusTokens, spacingTokens, layoutTokens, } from "./theme";
49
+ export { neaDesignSystemContent, neaTailwindPreset, } from "./tailwind-preset";
50
+ export { Pagination } from "./pagination";
51
+ export type { PaginationProps } from "./pagination";
52
+ export { TableToolbar } from "./table-toolbar";
53
+ export type { TableToolbarProps } from "./table-toolbar";
54
+ export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, TableRowSkeleton, TABLE_SKELETON_PRESETS, } from "./table";
55
+ export type { TableColumn, TableProps, TableSortDirection, TableSortingProps, TableSkeletonConfig, TableRowSkeletonProps, } from "./table";
56
+ export { Tabs, TabsRoot, type TabsItem, type TabsProps, TabsList, TabsTrigger, TabsContent, useTabsContext, } from "./tabs";
57
+ export { Tooltip, TooltipRoot, TooltipTrigger, TooltipContent, TooltipProvider, type TooltipProps, } from "./tooltip";
9
58
  export { cn } from "./lib/utils";
package/dist/index.js CHANGED
@@ -1,7 +1,34 @@
1
+ export { Avatar, AvatarProfile, AvatarRoot, AvatarImage, AvatarFallback, } from "./avatar";
2
+ export * from "./animated-icons";
1
3
  export { Badge, badgeVariants } from "./badge";
4
+ export { CountBadge } from "./count-badge";
5
+ export { EmptyState } from "./empty-state";
6
+ export { EditableTable } from "./editable-table";
7
+ export { EmailInput } from "./email-input";
8
+ export { Input } from "./input";
9
+ export { Select } from "./select";
10
+ export { DEFAULT_COUNTRY_CODES, PhoneInput, } from "./phone-input";
11
+ export { OtpInput } from "./otp-input";
12
+ export { LabelValue, isEmptyValue, } from "./label-value";
13
+ export { LabelValueGrid } from "./label-value-grid";
14
+ export { Breadcrumb, BreadcrumbNav, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis, } from "./breadcrumb";
2
15
  export { Button, buttonVariants } from "./button";
3
- export { neaPalette, semanticColorTokens } from "./theme";
4
- export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, } from "./table";
5
- export { Tabs, TabsList, TabsTrigger, TabsContent, useTabsContext, } from "./tabs";
6
- export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "./tooltip";
16
+ export { Dialog } from "./dialog";
17
+ export { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator, CommandShortcut, } from "./command";
18
+ export { CommandSearch, } from "./command-search";
19
+ export { SearchInput } from "./search-input";
20
+ export { SearchHighlight, useSearchHighlight, } from "./use-search-highlight";
21
+ export { Textarea } from "./textarea";
22
+ export { DraggableTabs, } from "./draggable-tabs";
23
+ export { Drawer, } from "./drawer";
24
+ export { DashboardLayout, DashboardSidebar, DashboardContent, SidebarProvider, useSidebar, } from "./layout";
25
+ export { PageHeader } from "./page-header";
26
+ export { Typography, typographyVariants } from "./typography";
27
+ export { neaPalette, semanticColorTokens, typographyTokens, radiusTokens, spacingTokens, layoutTokens, } from "./theme";
28
+ export { neaDesignSystemContent, neaTailwindPreset, } from "./tailwind-preset";
29
+ export { Pagination } from "./pagination";
30
+ export { TableToolbar } from "./table-toolbar";
31
+ export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, TableRowSkeleton, TABLE_SKELETON_PRESETS, } from "./table";
32
+ export { Tabs, TabsRoot, TabsList, TabsTrigger, TabsContent, useTabsContext, } from "./tabs";
33
+ export { Tooltip, TooltipRoot, TooltipTrigger, TooltipContent, TooltipProvider, } from "./tooltip";
7
34
  export { cn } from "./lib/utils";
@@ -0,0 +1,4 @@
1
+ import * as React from "react";
2
+ declare const inputControlClassName = "h-9 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-7 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";
3
+ declare const InputControl: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref"> & React.RefAttributes<HTMLInputElement>>;
4
+ export { InputControl, inputControlClassName };
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "./lib/utils";
4
+ const inputControlClassName = "h-9 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-7 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";
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
+ InputControl.displayName = "InputControl";
7
+ export { InputControl, inputControlClassName };
@@ -0,0 +1,9 @@
1
+ export type Validator = (value: string) => string | undefined;
2
+ declare function validateRequired(fieldName?: string): Validator;
3
+ declare const validateEmail: Validator;
4
+ declare const validateGovernmentEmail: Validator;
5
+ declare const validatePhone: Validator;
6
+ declare const validatePhoneForCountry: (countryCode: string, fieldName?: string) => Validator;
7
+ declare const validateOtp: (length: number, fieldName?: string) => Validator;
8
+ declare function combineValidators(...validators: Validator[]): Validator;
9
+ export { combineValidators, validateEmail, validateGovernmentEmail, validateOtp, validatePhone, validatePhoneForCountry, validateRequired, };
@@ -0,0 +1,75 @@
1
+ import { parsePhoneNumberFromString, } from "libphonenumber-js";
2
+ function validateRequired(fieldName = "This field") {
3
+ return (value) => {
4
+ if (!value.trim()) {
5
+ return `${fieldName} is required`;
6
+ }
7
+ return undefined;
8
+ };
9
+ }
10
+ const validateEmail = (value) => {
11
+ if (!value.trim()) {
12
+ return undefined;
13
+ }
14
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
15
+ if (!emailRegex.test(value.trim())) {
16
+ return "Please enter a valid email address";
17
+ }
18
+ return undefined;
19
+ };
20
+ const validateGovernmentEmail = (value) => {
21
+ const emailError = validateEmail(value);
22
+ if (emailError) {
23
+ return emailError;
24
+ }
25
+ if (value && !value.trim().toLowerCase().endsWith(".gov.sg")) {
26
+ return "Only government emails (*.gov.sg) are allowed";
27
+ }
28
+ return undefined;
29
+ };
30
+ const validatePhone = (value) => {
31
+ if (!value.trim()) {
32
+ return undefined;
33
+ }
34
+ const cleaned = value.replace(/\s/g, "");
35
+ const phoneRegex = /^[0-9]{6,15}$/;
36
+ if (!phoneRegex.test(cleaned)) {
37
+ return "Please enter a valid phone number (digits only, 6-15 digits)";
38
+ }
39
+ return undefined;
40
+ };
41
+ const validatePhoneForCountry = (countryCode, fieldName = "Phone number") => {
42
+ return (value) => {
43
+ if (!value.trim()) {
44
+ return undefined;
45
+ }
46
+ const phoneNumber = parsePhoneNumberFromString(value, countryCode);
47
+ if (!phoneNumber || !phoneNumber.isValid()) {
48
+ return `Please enter a valid ${fieldName.toLowerCase()}`;
49
+ }
50
+ return undefined;
51
+ };
52
+ };
53
+ const validateOtp = (length, fieldName = "Verification code") => {
54
+ return (value) => {
55
+ if (!value.trim()) {
56
+ return undefined;
57
+ }
58
+ if (value.trim().length !== length) {
59
+ return `Please enter the complete ${fieldName.toLowerCase()}`;
60
+ }
61
+ return undefined;
62
+ };
63
+ };
64
+ function combineValidators(...validators) {
65
+ return (value) => {
66
+ for (const validator of validators) {
67
+ const error = validator(value);
68
+ if (error) {
69
+ return error;
70
+ }
71
+ }
72
+ return undefined;
73
+ };
74
+ }
75
+ export { combineValidators, validateEmail, validateGovernmentEmail, validateOtp, validatePhone, validatePhoneForCountry, validateRequired, };