@neasg/design-system 0.1.3 → 0.2.1

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 (189) hide show
  1. package/README.md +102 -14
  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 +84 -6
  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/govt-masthead.d.ts +9 -0
  137. package/dist/govt-masthead.js +8 -0
  138. package/dist/index.d.ts +55 -4
  139. package/dist/index.js +32 -4
  140. package/dist/input-control.d.ts +4 -0
  141. package/dist/input-control.js +7 -0
  142. package/dist/input-validation.d.ts +9 -0
  143. package/dist/input-validation.js +75 -0
  144. package/dist/input.d.ts +16 -0
  145. package/dist/input.js +22 -0
  146. package/dist/label-value-grid.d.ts +13 -0
  147. package/dist/label-value-grid.js +61 -0
  148. package/dist/label-value.d.ts +11 -0
  149. package/dist/label-value.js +19 -0
  150. package/dist/layout.d.ts +57 -0
  151. package/dist/layout.js +166 -0
  152. package/dist/otp-input.d.ts +21 -0
  153. package/dist/otp-input.js +60 -0
  154. package/dist/page-header.d.ts +9 -0
  155. package/dist/page-header.js +6 -0
  156. package/dist/pagination.d.ts +12 -0
  157. package/dist/pagination.js +24 -0
  158. package/dist/phone-input.d.ts +36 -0
  159. package/dist/phone-input.js +135 -0
  160. package/dist/right-drawer.d.ts +2 -0
  161. package/dist/right-drawer.js +1 -0
  162. package/dist/search-input-shell.d.ts +8 -0
  163. package/dist/search-input-shell.js +9 -0
  164. package/dist/search-input.d.ts +20 -0
  165. package/dist/search-input.js +73 -0
  166. package/dist/select-primitive.d.ts +17 -0
  167. package/dist/select-primitive.js +33 -0
  168. package/dist/select.d.ts +31 -0
  169. package/dist/select.js +97 -0
  170. package/dist/styles.css +111 -1
  171. package/dist/table-toolbar.d.ts +7 -0
  172. package/dist/table-toolbar.js +7 -0
  173. package/dist/table.d.ts +87 -3
  174. package/dist/table.js +102 -5
  175. package/dist/tabs.d.ts +24 -7
  176. package/dist/tabs.js +22 -26
  177. package/dist/tailwind-preset.d.ts +4 -0
  178. package/dist/tailwind-preset.js +97 -0
  179. package/dist/textarea.d.ts +16 -0
  180. package/dist/textarea.js +23 -0
  181. package/dist/theme.d.ts +259 -0
  182. package/dist/theme.js +118 -0
  183. package/dist/tooltip.d.ts +15 -3
  184. package/dist/tooltip.js +9 -4
  185. package/dist/typography.d.ts +11 -0
  186. package/dist/typography.js +41 -0
  187. package/dist/use-search-highlight.d.ts +13 -0
  188. package/dist/use-search-highlight.js +46 -0
  189. package/package.json +126 -4
package/dist/button.js CHANGED
@@ -3,7 +3,7 @@ import * as React from "react";
3
3
  import { Slot } from "@radix-ui/react-slot";
4
4
  import { cva } from "class-variance-authority";
5
5
  import { cn } from "./lib/utils";
6
- const buttonVariants = cva("inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium 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 disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0", {
6
+ const buttonVariants = cva("inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium 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 disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0", {
7
7
  variants: {
8
8
  variant: {
9
9
  default: "bg-primary text-primary-foreground hover:bg-primary/90",
@@ -15,9 +15,9 @@ const buttonVariants = cva("inline-flex items-center justify-center whitespace-n
15
15
  },
16
16
  size: {
17
17
  default: "h-10 px-4 py-2 [&_svg]:h-4 [&_svg]:w-4",
18
- sm: "h-9 rounded-md px-3 [&_svg]:h-4 [&_svg]:w-4",
18
+ sm: "h-9 rounded-md px-3 text-xs [&_svg]:h-4 [&_svg]:w-4",
19
19
  lg: "h-11 rounded-md px-8 [&_svg]:h-5 [&_svg]:w-5",
20
- icon: "h-10 w-10 [&_svg]:h-5 [&_svg]:w-5",
20
+ icon: "h-10 w-10 [&_svg]:h-4 [&_svg]:w-4",
21
21
  },
22
22
  },
23
23
  defaultVariants: {
@@ -49,14 +49,72 @@ function hasTextLikeContent(node) {
49
49
  }
50
50
  return false;
51
51
  }
52
- const Button = React.forwardRef(({ className, variant, size, asChild = false, loading = false, loadingText, loadingIndicator, disabled, children, onClick, tabIndex, ...props }, ref) => {
52
+ function mergeRefs(...refs) {
53
+ return (value) => {
54
+ refs.forEach((ref) => {
55
+ if (!ref)
56
+ return;
57
+ if (typeof ref === "function") {
58
+ ref(value);
59
+ return;
60
+ }
61
+ ref.current = value;
62
+ });
63
+ };
64
+ }
65
+ function isAnimatedIconElement(node) {
66
+ var _a, _b;
67
+ if (typeof node.type === "string" || node.type === React.Fragment) {
68
+ return false;
69
+ }
70
+ const component = node.type;
71
+ const componentName = (_b = (_a = component.displayName) !== null && _a !== void 0 ? _a : component.name) !== null && _b !== void 0 ? _b : "";
72
+ return componentName.endsWith("Icon");
73
+ }
74
+ function attachAnimatedIconRefs(node, refs) {
75
+ if (node == null || typeof node === "boolean") {
76
+ return node;
77
+ }
78
+ if (Array.isArray(node)) {
79
+ return node.map((child) => attachAnimatedIconRefs(child, refs));
80
+ }
81
+ if (!React.isValidElement(node)) {
82
+ return node;
83
+ }
84
+ if (node.type === React.Fragment) {
85
+ return React.cloneElement(node, undefined, attachAnimatedIconRefs(node.props.children, refs));
86
+ }
87
+ if (isAnimatedIconElement(node)) {
88
+ const refCallback = (value) => {
89
+ if (!value)
90
+ return;
91
+ refs.current.push(value);
92
+ };
93
+ return React.cloneElement(node, {
94
+ ref: mergeRefs(node.ref, refCallback),
95
+ });
96
+ }
97
+ if ("children" in node.props && node.props.children !== undefined) {
98
+ return React.cloneElement(node, undefined, attachAnimatedIconRefs(node.props.children, refs));
99
+ }
100
+ return node;
101
+ }
102
+ const Button = React.forwardRef(({ className, variant, size, asChild = false, loading = false, loadingText, loadingIndicator, disabled, children, onClick, onMouseEnter, onMouseLeave, onFocus, onBlur, tabIndex, ...props }, ref) => {
53
103
  const Comp = asChild ? Slot : "button";
54
104
  const isDisabled = disabled || loading;
55
105
  const loadingLabel = loadingText !== null && loadingText !== void 0 ? loadingText : children;
106
+ const animatedIconRefs = React.useRef([]);
107
+ animatedIconRefs.current = [];
56
108
  if (size === "icon" && hasTextLikeContent(children)) {
57
109
  throw new Error('NEA Button `size="icon"` only accepts icon components or SVG content. Do not pass text or emoji.');
58
110
  }
59
- const content = loading ? (loadingLabel ? (_jsxs("span", { className: "inline-flex items-center gap-2", children: [loadingIndicator !== null && loadingIndicator !== void 0 ? loadingIndicator : _jsx(DefaultLoadingIndicator, {}), _jsx("span", { children: loadingLabel })] })) : (loadingIndicator !== null && loadingIndicator !== void 0 ? loadingIndicator : _jsx(DefaultLoadingIndicator, {}))) : (children);
111
+ const content = loading ? (loadingLabel ? (_jsxs("span", { className: "inline-flex items-center gap-2", children: [loadingIndicator !== null && loadingIndicator !== void 0 ? loadingIndicator : _jsx(DefaultLoadingIndicator, {}), _jsx("span", { children: attachAnimatedIconRefs(loadingLabel, animatedIconRefs) })] })) : (loadingIndicator !== null && loadingIndicator !== void 0 ? loadingIndicator : _jsx(DefaultLoadingIndicator, {}))) : (attachAnimatedIconRefs(children, animatedIconRefs));
112
+ const startAnimatedIcons = () => {
113
+ animatedIconRefs.current.forEach((icon) => { var _a; return (_a = icon.startAnimation) === null || _a === void 0 ? void 0 : _a.call(icon); });
114
+ };
115
+ const stopAnimatedIcons = () => {
116
+ animatedIconRefs.current.forEach((icon) => { var _a; return (_a = icon.stopAnimation) === null || _a === void 0 ? void 0 : _a.call(icon); });
117
+ };
60
118
  const handleClick = (event) => {
61
119
  if (isDisabled && asChild) {
62
120
  event.preventDefault();
@@ -65,7 +123,27 @@ const Button = React.forwardRef(({ className, variant, size, asChild = false, lo
65
123
  }
66
124
  onClick === null || onClick === void 0 ? void 0 : onClick(event);
67
125
  };
68
- return (_jsx(Comp, { className: cn(buttonVariants({ variant, size, className })), ref: ref, "aria-busy": loading || undefined, "aria-disabled": isDisabled || undefined, "data-loading": loading ? "" : undefined, "data-disabled": isDisabled ? "" : undefined, onClick: handleClick, tabIndex: isDisabled && asChild ? -1 : tabIndex, disabled: !asChild ? isDisabled : undefined, ...props, children: content }));
126
+ const handleMouseEnter = (event) => {
127
+ if (!isDisabled) {
128
+ startAnimatedIcons();
129
+ }
130
+ onMouseEnter === null || onMouseEnter === void 0 ? void 0 : onMouseEnter(event);
131
+ };
132
+ const handleMouseLeave = (event) => {
133
+ stopAnimatedIcons();
134
+ onMouseLeave === null || onMouseLeave === void 0 ? void 0 : onMouseLeave(event);
135
+ };
136
+ const handleFocus = (event) => {
137
+ if (!isDisabled) {
138
+ startAnimatedIcons();
139
+ }
140
+ onFocus === null || onFocus === void 0 ? void 0 : onFocus(event);
141
+ };
142
+ const handleBlur = (event) => {
143
+ stopAnimatedIcons();
144
+ onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
145
+ };
146
+ return (_jsx(Comp, { className: cn(buttonVariants({ variant, size, className })), ref: ref, "aria-busy": loading || undefined, "aria-disabled": isDisabled || undefined, "data-loading": loading ? "" : undefined, "data-disabled": isDisabled ? "" : undefined, onClick: handleClick, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onFocus: handleFocus, onBlur: handleBlur, tabIndex: isDisabled && asChild ? -1 : tabIndex, disabled: !asChild ? isDisabled : undefined, ...props, children: content }));
69
147
  });
70
148
  Button.displayName = "Button";
71
149
  export { Button, buttonVariants };
@@ -0,0 +1,44 @@
1
+ import * as React from "react";
2
+ export interface CommandSearchFilter {
3
+ value: string;
4
+ label: string;
5
+ selected?: boolean;
6
+ onSelect?: () => void;
7
+ }
8
+ export interface CommandSearchItem {
9
+ id: string;
10
+ title: string;
11
+ subtitle?: string;
12
+ value?: string;
13
+ keywords?: string[];
14
+ disabled?: boolean;
15
+ closeOnSelect?: boolean;
16
+ onSelect?: () => void;
17
+ }
18
+ export interface CommandSearchSection {
19
+ key: string;
20
+ heading: string;
21
+ items: CommandSearchItem[];
22
+ }
23
+ export interface CommandSearchProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
24
+ value: string;
25
+ onValueChange: (value: string) => void;
26
+ sections?: CommandSearchSection[];
27
+ children?: React.ReactNode;
28
+ isCollapsed?: boolean;
29
+ open?: boolean;
30
+ defaultOpen?: boolean;
31
+ onOpenChange?: (open: boolean) => void;
32
+ triggerPlaceholder?: string;
33
+ inputPlaceholder?: string;
34
+ /** The key that opens the dialog when pressed with Cmd/Ctrl (e.g. "k", "j"). */
35
+ shortcutKey?: string;
36
+ /** Override the label shown on the trigger. Defaults to "⌘{shortcutKey.toUpperCase()}". */
37
+ shortcutLabel?: string;
38
+ allFilter?: CommandSearchFilter;
39
+ filters?: CommandSearchFilter[];
40
+ dialogClassName?: string;
41
+ resultsClassName?: string;
42
+ }
43
+ declare function CommandSearch({ value, onValueChange, sections, children, className, isCollapsed, open, defaultOpen, onOpenChange, triggerPlaceholder, inputPlaceholder, shortcutKey, shortcutLabel, allFilter, filters, dialogClassName, resultsClassName, ...props }: CommandSearchProps): import("react/jsx-runtime").JSX.Element;
44
+ export { CommandSearch };
@@ -0,0 +1,73 @@
1
+ "use client";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Button } from "./button";
5
+ import { Command, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, } from "./command";
6
+ import { EmptyState } from "./empty-state";
7
+ import { SearchIcon } from "./animated-icons/search";
8
+ import { DialogRoot, DialogSurface } from "./dialog-primitive";
9
+ import { FilterToggle } from "./filter-toggle";
10
+ import { cn } from "./lib/utils";
11
+ import { SearchHighlight } from "./use-search-highlight";
12
+ function CommandSearchEmptyIcon({ className }) {
13
+ return _jsx(SearchIcon, { className: className, size: 20 });
14
+ }
15
+ function getCommandSearchItemValue(item) {
16
+ var _a, _b;
17
+ const fallbackValue = [item.title, item.subtitle, ...((_a = item.keywords) !== null && _a !== void 0 ? _a : [])]
18
+ .filter(Boolean)
19
+ .join(" ");
20
+ return (_b = item.value) !== null && _b !== void 0 ? _b : fallbackValue;
21
+ }
22
+ function useControllableOpen({ open, defaultOpen, onOpenChange, }) {
23
+ const [internalOpen, setInternalOpen] = React.useState(defaultOpen !== null && defaultOpen !== void 0 ? defaultOpen : false);
24
+ const isControlled = open !== undefined;
25
+ const currentOpen = isControlled ? open : internalOpen;
26
+ const setOpen = React.useCallback((next) => {
27
+ if (!isControlled) {
28
+ setInternalOpen(next);
29
+ }
30
+ onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(next);
31
+ }, [isControlled, onOpenChange]);
32
+ return [currentOpen, setOpen];
33
+ }
34
+ function CommandSearch({ value, onValueChange, sections, children, className, isCollapsed = false, open, defaultOpen, onOpenChange, triggerPlaceholder = "Search...", inputPlaceholder = "Search...", shortcutKey = "k", shortcutLabel, allFilter, filters = [], dialogClassName, resultsClassName, ...props }) {
35
+ const [isOpen, setIsOpen] = useControllableOpen({
36
+ open,
37
+ defaultOpen,
38
+ onOpenChange,
39
+ });
40
+ const inputRef = React.useRef(null);
41
+ const normalizedValue = value.trim();
42
+ const visibleSections = React.useMemo(() => (sections !== null && sections !== void 0 ? sections : []).filter((section) => section.items.length > 0), [sections]);
43
+ const resolvedLabel = shortcutLabel !== null && shortcutLabel !== void 0 ? shortcutLabel : `⌘${shortcutKey.toUpperCase()}`;
44
+ React.useEffect(() => {
45
+ const handleKeyDown = (event) => {
46
+ if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === shortcutKey.toLowerCase()) {
47
+ event.preventDefault();
48
+ setIsOpen(true);
49
+ }
50
+ };
51
+ document.addEventListener("keydown", handleKeyDown);
52
+ return () => document.removeEventListener("keydown", handleKeyDown);
53
+ }, [setIsOpen, shortcutKey]);
54
+ React.useEffect(() => {
55
+ if (isOpen) {
56
+ window.setTimeout(() => { var _a; return (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); }, 0);
57
+ }
58
+ }, [isOpen]);
59
+ return (_jsxs("div", { className: className, ...props, children: [_jsxs(Button, { type: "button", onClick: () => setIsOpen(true), variant: "ghost", className: cn(isCollapsed
60
+ ? "h-10 w-full justify-center rounded-lg px-3 !font-normal text-muted-foreground hover:bg-white hover:text-foreground"
61
+ : "h-10 w-full min-w-[14rem] justify-start gap-3 rounded-lg border border-gray-200 bg-white px-3 !font-normal text-muted-foreground shadow-sm hover:bg-white hover:text-foreground"), children: [_jsx(SearchIcon, { className: "shrink-0", size: 16 }), !isCollapsed ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "flex-1 text-left text-sm", children: triggerPlaceholder }), _jsx("kbd", { className: "hidden h-5 items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] text-muted-foreground sm:inline-flex", children: resolvedLabel })] })) : null] }), _jsx(DialogRoot, { open: isOpen, onOpenChange: setIsOpen, children: _jsx(DialogSurface, { className: cn("max-w-xl gap-0 overflow-hidden p-0 sm:max-w-xl", dialogClassName), hideCloseButton: true, children: _jsxs(Command, { shouldFilter: false, className: "rounded-none border-0", children: [_jsx(CommandInput, { ref: inputRef, value: value, onValueChange: onValueChange, placeholder: inputPlaceholder, className: "text-sm" }), allFilter || filters.length ? (_jsxs("div", { className: "flex items-center gap-1 border-b px-3 py-2", children: [allFilter ? (_jsx(FilterToggle, { selected: allFilter.selected, onClick: allFilter.onSelect, children: allFilter.label })) : null, filters.map((filter) => (_jsx(FilterToggle, { selected: filter.selected, onClick: filter.onSelect, children: filter.label }, filter.value)))] })) : null, _jsx(CommandList, { className: cn("max-h-[400px] p-1", resultsClassName), children: children !== undefined ? children : sections ? (visibleSections.length ? (visibleSections.map((section, index) => (_jsxs(React.Fragment, { children: [index > 0 ? _jsx(CommandSeparator, { alwaysRender: true }) : null, _jsx(CommandGroup, { heading: section.heading, children: section.items.map((item) => (_jsx(CommandItem, { value: getCommandSearchItemValue(item), disabled: item.disabled, onSelect: () => {
62
+ var _a;
63
+ (_a = item.onSelect) === null || _a === void 0 ? void 0 : _a.call(item);
64
+ if (item.closeOnSelect !== false) {
65
+ setIsOpen(false);
66
+ }
67
+ }, children: _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "truncate text-sm font-medium", children: _jsx(SearchHighlight, { text: item.title, query: value }) }), item.subtitle ? (_jsx("p", { className: "truncate text-xs text-muted-foreground", children: _jsx(SearchHighlight, { text: item.subtitle, query: value }) })) : null] }) }, item.id))) })] }, section.key)))) : (_jsx(EmptyState, { icon: CommandSearchEmptyIcon, message: normalizedValue ? "No results found" : "Start typing to search", description: normalizedValue
68
+ ? `No results found for "${value}".`
69
+ : "Results will appear here as you type." }))) : (_jsx(EmptyState, { icon: CommandSearchEmptyIcon, message: normalizedValue ? "No results found" : "Start typing to search", description: normalizedValue
70
+ ? `No results found for "${value}".`
71
+ : "Results will appear here as you type." })) })] }) }) })] }));
72
+ }
73
+ export { CommandSearch };
@@ -0,0 +1,77 @@
1
+ import * as React from "react";
2
+ import { Command as CommandPrimitive } from "cmdk";
3
+ declare const Command: React.ForwardRefExoticComponent<Omit<{
4
+ children?: React.ReactNode;
5
+ } & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
6
+ ref?: React.Ref<HTMLDivElement>;
7
+ } & {
8
+ asChild?: boolean;
9
+ }, "key" | "asChild" | keyof React.HTMLAttributes<HTMLDivElement>> & {
10
+ label?: string;
11
+ shouldFilter?: boolean;
12
+ filter?: (value: string, search: string, keywords?: string[]) => number;
13
+ defaultValue?: string;
14
+ value?: string;
15
+ onValueChange?: (value: string) => void;
16
+ loop?: boolean;
17
+ disablePointerSelection?: boolean;
18
+ vimBindings?: boolean;
19
+ } & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
20
+ interface CommandInputProps extends React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input> {
21
+ wrapperClassName?: string;
22
+ iconClassName?: string;
23
+ iconSize?: number;
24
+ }
25
+ declare const CommandInput: React.ForwardRefExoticComponent<CommandInputProps & React.RefAttributes<HTMLInputElement>>;
26
+ declare const CommandList: React.ForwardRefExoticComponent<Omit<{
27
+ children?: React.ReactNode;
28
+ } & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
29
+ ref?: React.Ref<HTMLDivElement>;
30
+ } & {
31
+ asChild?: boolean;
32
+ }, "key" | "asChild" | keyof React.HTMLAttributes<HTMLDivElement>> & {
33
+ label?: string;
34
+ } & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
35
+ declare const CommandEmpty: React.ForwardRefExoticComponent<Omit<{
36
+ children?: React.ReactNode;
37
+ } & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
38
+ ref?: React.Ref<HTMLDivElement>;
39
+ } & {
40
+ asChild?: boolean;
41
+ }, "key" | "asChild" | keyof React.HTMLAttributes<HTMLDivElement>> & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
42
+ declare const CommandGroup: React.ForwardRefExoticComponent<Omit<{
43
+ children?: React.ReactNode;
44
+ } & Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
45
+ ref?: React.Ref<HTMLDivElement>;
46
+ } & {
47
+ asChild?: boolean;
48
+ }, "key" | "asChild" | keyof React.HTMLAttributes<HTMLDivElement>>, "value" | "heading"> & {
49
+ heading?: React.ReactNode;
50
+ value?: string;
51
+ forceMount?: boolean;
52
+ } & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
53
+ declare const CommandSeparator: React.ForwardRefExoticComponent<Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
54
+ ref?: React.Ref<HTMLDivElement>;
55
+ } & {
56
+ asChild?: boolean;
57
+ }, "key" | "asChild" | keyof React.HTMLAttributes<HTMLDivElement>> & {
58
+ alwaysRender?: boolean;
59
+ } & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
60
+ declare const CommandItem: React.ForwardRefExoticComponent<Omit<{
61
+ children?: React.ReactNode;
62
+ } & Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
63
+ ref?: React.Ref<HTMLDivElement>;
64
+ } & {
65
+ asChild?: boolean;
66
+ }, "key" | "asChild" | keyof React.HTMLAttributes<HTMLDivElement>>, "onSelect" | "disabled" | "value"> & {
67
+ disabled?: boolean;
68
+ onSelect?: (value: string) => void;
69
+ value?: string;
70
+ keywords?: string[];
71
+ forceMount?: boolean;
72
+ } & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
73
+ declare const CommandShortcut: {
74
+ ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>): import("react/jsx-runtime").JSX.Element;
75
+ displayName: string;
76
+ };
77
+ export { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator, CommandShortcut, };
@@ -0,0 +1,24 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Command as CommandPrimitive } from "cmdk";
5
+ import { inputControlClassName } from "./input-control";
6
+ import { cn } from "./lib/utils";
7
+ import { SearchInputShell } from "./search-input-shell";
8
+ const Command = React.forwardRef(({ className, ...props }, ref) => (_jsx(CommandPrimitive, { ref: ref, className: cn("flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground", className), ...props })));
9
+ Command.displayName = CommandPrimitive.displayName;
10
+ const CommandInput = React.forwardRef(({ className, wrapperClassName, iconClassName, iconSize = 16, ...props }, ref) => (_jsx("div", { "cmdk-input-wrapper": "", className: cn("border-b p-4", wrapperClassName), children: _jsx(SearchInputShell, { iconClassName: iconClassName, iconSize: iconSize, children: _jsx(CommandPrimitive.Input, { ref: ref, className: cn(inputControlClassName, "bg-background pl-9 pr-3", className), ...props }) }) })));
11
+ CommandInput.displayName = CommandPrimitive.Input.displayName;
12
+ const CommandList = React.forwardRef(({ className, ...props }, ref) => (_jsx(CommandPrimitive.List, { ref: ref, className: cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className), ...props })));
13
+ CommandList.displayName = CommandPrimitive.List.displayName;
14
+ const CommandEmpty = React.forwardRef(({ className, ...props }, ref) => (_jsx(CommandPrimitive.Empty, { ref: ref, className: cn("py-6 text-center text-sm text-muted-foreground", className), ...props })));
15
+ CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
16
+ const CommandGroup = React.forwardRef(({ className, ...props }, ref) => (_jsx(CommandPrimitive.Group, { ref: ref, className: cn("overflow-hidden py-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:text-muted-foreground", className), ...props })));
17
+ CommandGroup.displayName = CommandPrimitive.Group.displayName;
18
+ const CommandSeparator = React.forwardRef(({ className, ...props }, ref) => (_jsx(CommandPrimitive.Separator, { ref: ref, className: cn("-mx-1 my-1 h-px bg-border", className), ...props })));
19
+ CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
20
+ const CommandItem = React.forwardRef(({ className, ...props }, ref) => (_jsx(CommandPrimitive.Item, { ref: ref, className: cn("relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-[selected=true]:bg-muted data-[selected=true]:text-foreground", className), ...props })));
21
+ CommandItem.displayName = CommandPrimitive.Item.displayName;
22
+ const CommandShortcut = ({ className, ...props }) => (_jsx("span", { className: cn("ml-auto text-xs tracking-widest text-muted-foreground", className), ...props }));
23
+ CommandShortcut.displayName = "CommandShortcut";
24
+ export { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator, CommandShortcut, };
@@ -0,0 +1,10 @@
1
+ import * as React from "react";
2
+ export interface CountBadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
3
+ count: number;
4
+ variant?: "default" | "danger" | "muted";
5
+ size?: "sm" | "md";
6
+ pulse?: boolean;
7
+ hideWhenZero?: boolean;
8
+ }
9
+ declare function CountBadge({ count, variant, size, pulse, className, hideWhenZero, ...props }: CountBadgeProps): import("react/jsx-runtime").JSX.Element | null;
10
+ export { CountBadge };
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Badge } from "./badge";
3
+ import { cn } from "./lib/utils";
4
+ function CountBadge({ count, variant = "default", size = "md", pulse = false, className, hideWhenZero = false, ...props }) {
5
+ if (count < 0 || (count === 0 && hideWhenZero))
6
+ return null;
7
+ const effectiveVariant = count === 0 ? "muted" : variant;
8
+ const sizeClasses = {
9
+ sm: "min-w-[18px] h-[18px] text-[10px] px-1",
10
+ md: "min-w-[20px] h-5 text-[11px] px-1.5",
11
+ };
12
+ const variantClasses = {
13
+ default: {
14
+ badgeVariant: "secondary",
15
+ className: "border-transparent bg-primary text-primary-foreground hover:bg-primary/90",
16
+ },
17
+ danger: {
18
+ badgeVariant: "destructive",
19
+ className: "border-transparent",
20
+ },
21
+ muted: {
22
+ badgeVariant: "muted",
23
+ className: "border-transparent",
24
+ },
25
+ };
26
+ const pulseColor = {
27
+ default: "bg-primary/75",
28
+ danger: "bg-[hsl(var(--status-destructive))]/70",
29
+ muted: "bg-muted-foreground/50",
30
+ };
31
+ const shouldPulse = pulse && count > 0;
32
+ return (_jsxs("span", { className: cn("relative inline-flex", className), ...props, children: [shouldPulse ? (_jsx("span", { className: cn("absolute inline-flex h-full w-full animate-ping rounded-full opacity-75", pulseColor[effectiveVariant]) })) : null, _jsx("span", { className: "relative inline-flex", children: _jsx(Badge, { variant: variantClasses[effectiveVariant].badgeVariant, className: cn("max-w-none rounded-full font-medium leading-none", sizeClasses[size], variantClasses[effectiveVariant].className), children: count }) })] }));
33
+ }
34
+ export { CountBadge };
@@ -0,0 +1,22 @@
1
+ import * as React from "react";
2
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
3
+ declare const DialogRoot: React.FC<DialogPrimitive.DialogProps>;
4
+ declare const DialogTrigger: React.ForwardRefExoticComponent<DialogPrimitive.DialogTriggerProps & React.RefAttributes<HTMLButtonElement>>;
5
+ declare const DialogPortal: React.FC<DialogPrimitive.DialogPortalProps>;
6
+ declare const DialogClose: React.ForwardRefExoticComponent<DialogPrimitive.DialogCloseProps & React.RefAttributes<HTMLButtonElement>>;
7
+ declare const DialogOverlay: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogOverlayProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
8
+ export interface DialogSurfaceProps extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> {
9
+ hideCloseButton?: boolean;
10
+ }
11
+ declare const DialogSurface: React.ForwardRefExoticComponent<DialogSurfaceProps & React.RefAttributes<HTMLDivElement>>;
12
+ declare const DialogHeader: {
13
+ ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): import("react/jsx-runtime").JSX.Element;
14
+ displayName: string;
15
+ };
16
+ declare const DialogFooter: {
17
+ ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): import("react/jsx-runtime").JSX.Element;
18
+ displayName: string;
19
+ };
20
+ declare const DialogTitle: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogTitleProps & React.RefAttributes<HTMLHeadingElement>, "ref"> & React.RefAttributes<HTMLHeadingElement>>;
21
+ declare const DialogDescription: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogDescriptionProps & React.RefAttributes<HTMLParagraphElement>, "ref"> & React.RefAttributes<HTMLParagraphElement>>;
22
+ export { DialogRoot, DialogPortal, DialogOverlay, DialogTrigger, DialogClose, DialogSurface, DialogHeader, DialogFooter, DialogTitle, DialogDescription, };
@@ -0,0 +1,23 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
5
+ import { XIcon } from "./animated-icons/x";
6
+ import { cn } from "./lib/utils";
7
+ const DialogRoot = DialogPrimitive.Root;
8
+ const DialogTrigger = DialogPrimitive.Trigger;
9
+ const DialogPortal = DialogPrimitive.Portal;
10
+ const DialogClose = DialogPrimitive.Close;
11
+ const DialogOverlay = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Overlay, { ref: ref, className: cn("fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", className), ...props })));
12
+ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
13
+ const DialogSurface = React.forwardRef(({ className, children, hideCloseButton, ...props }, ref) => (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, {}), _jsxs(DialogPrimitive.Content, { ref: ref, className: cn("fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", className), ...props, children: [children, !hideCloseButton ? (_jsxs(DialogPrimitive.Close, { className: "absolute right-4 top-4 cursor-pointer rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground", children: [_jsx(XIcon, { size: 16 }), _jsx("span", { className: "sr-only", children: "Close" })] })) : null] })] })));
14
+ DialogSurface.displayName = DialogPrimitive.Content.displayName;
15
+ const DialogHeader = ({ className, ...props }) => (_jsx("div", { className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className), ...props }));
16
+ DialogHeader.displayName = "DialogHeader";
17
+ const DialogFooter = ({ className, ...props }) => (_jsx("div", { className: cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className), ...props }));
18
+ DialogFooter.displayName = "DialogFooter";
19
+ const DialogTitle = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Title, { ref: ref, className: cn("text-lg font-semibold leading-none tracking-tight", className), ...props })));
20
+ DialogTitle.displayName = DialogPrimitive.Title.displayName;
21
+ const DialogDescription = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Description, { ref: ref, className: cn("text-sm text-muted-foreground", className), ...props })));
22
+ DialogDescription.displayName = DialogPrimitive.Description.displayName;
23
+ export { DialogRoot, DialogPortal, DialogOverlay, DialogTrigger, DialogClose, DialogSurface, DialogHeader, DialogFooter, DialogTitle, DialogDescription, };
@@ -0,0 +1,28 @@
1
+ import * as React from "react";
2
+ import { type ButtonProps } from "./button";
3
+ interface DialogRenderHelpers {
4
+ close: () => void;
5
+ }
6
+ export interface DialogAction extends Omit<ButtonProps, "children"> {
7
+ label: React.ReactNode;
8
+ closeOnClick?: boolean;
9
+ }
10
+ type DialogRenderable = React.ReactNode | ((helpers: DialogRenderHelpers) => React.ReactNode);
11
+ export interface DialogProps {
12
+ open?: boolean;
13
+ defaultOpen?: boolean;
14
+ onOpenChange?: (open: boolean) => void;
15
+ modal?: boolean;
16
+ trigger?: React.ReactElement;
17
+ title: React.ReactNode;
18
+ description?: React.ReactNode;
19
+ children?: DialogRenderable;
20
+ footer?: DialogRenderable;
21
+ primaryAction?: DialogAction;
22
+ secondaryAction?: DialogAction;
23
+ contentClassName?: string;
24
+ bodyClassName?: string;
25
+ hideCloseButton?: boolean;
26
+ }
27
+ declare function Dialog({ open, defaultOpen, onOpenChange, modal, trigger, title, description, children, footer, primaryAction, secondaryAction, contentClassName, bodyClassName, hideCloseButton, }: DialogProps): import("react/jsx-runtime").JSX.Element;
28
+ export { Dialog };
package/dist/dialog.js ADDED
@@ -0,0 +1,60 @@
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 { Button } from "./button";
5
+ import { DialogDescription, DialogFooter, DialogHeader, DialogRoot, DialogSurface, DialogTitle, DialogTrigger, } from "./dialog-primitive";
6
+ import { cn } from "./lib/utils";
7
+ function useControllableOpen({ open, defaultOpen, onOpenChange, }) {
8
+ const [internalOpen, setInternalOpen] = React.useState(defaultOpen !== null && defaultOpen !== void 0 ? defaultOpen : false);
9
+ const isControlled = open !== undefined;
10
+ const currentOpen = isControlled ? open : internalOpen;
11
+ const setOpen = React.useCallback((next) => {
12
+ if (!isControlled) {
13
+ setInternalOpen(next);
14
+ }
15
+ onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(next);
16
+ }, [isControlled, onOpenChange]);
17
+ return [currentOpen, setOpen];
18
+ }
19
+ function resolveDialogRenderable(value, helpers) {
20
+ if (typeof value === "function") {
21
+ return value(helpers);
22
+ }
23
+ return value;
24
+ }
25
+ function renderDialogAction({ action, close, defaultVariant, }) {
26
+ const { label, onClick, closeOnClick = true, type = "button", variant = defaultVariant, ...buttonProps } = action;
27
+ return (_jsx(Button, { ...buttonProps, type: type, variant: variant, onClick: (event) => {
28
+ onClick === null || onClick === void 0 ? void 0 : onClick(event);
29
+ if (!event.defaultPrevented && closeOnClick) {
30
+ close();
31
+ }
32
+ }, children: label }));
33
+ }
34
+ function Dialog({ open, defaultOpen, onOpenChange, modal, trigger, title, description, children, footer, primaryAction, secondaryAction, contentClassName, bodyClassName, hideCloseButton, }) {
35
+ const [isOpen, setIsOpen] = useControllableOpen({
36
+ open,
37
+ defaultOpen,
38
+ onOpenChange,
39
+ });
40
+ const helpers = React.useMemo(() => ({
41
+ close: () => setIsOpen(false),
42
+ }), [setIsOpen]);
43
+ const body = resolveDialogRenderable(children, helpers);
44
+ const customFooter = resolveDialogRenderable(footer, helpers);
45
+ const hasFooter = Boolean(customFooter || primaryAction || secondaryAction);
46
+ return (_jsxs(DialogRoot, { open: isOpen, onOpenChange: setIsOpen, modal: modal, children: [trigger ? _jsx(DialogTrigger, { asChild: true, children: trigger }) : null, _jsxs(DialogSurface, { 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, { children: customFooter !== null && customFooter !== void 0 ? customFooter : (_jsxs(_Fragment, { children: [secondaryAction
47
+ ? renderDialogAction({
48
+ action: secondaryAction,
49
+ close: helpers.close,
50
+ defaultVariant: "outline",
51
+ })
52
+ : null, primaryAction
53
+ ? renderDialogAction({
54
+ action: primaryAction,
55
+ close: helpers.close,
56
+ defaultVariant: "default",
57
+ })
58
+ : null] })) })) : null] })] }));
59
+ }
60
+ export { Dialog };
@@ -0,0 +1,27 @@
1
+ import * as React from "react";
2
+ import { type TooltipProps } from "./tooltip";
3
+ export interface DraggableTabItem {
4
+ id: string;
5
+ label: React.ReactNode;
6
+ active?: boolean;
7
+ disabled?: boolean;
8
+ leading?: React.ReactNode;
9
+ trailing?: React.ReactNode;
10
+ tooltip?: React.ReactNode;
11
+ tooltipContentClassName?: string;
12
+ tooltipSide?: TooltipProps["side"];
13
+ onSelect?: () => void;
14
+ onClose?: () => void;
15
+ className?: string;
16
+ activeClassName?: string;
17
+ inactiveClassName?: string;
18
+ labelClassName?: string;
19
+ closeButtonClassName?: string;
20
+ }
21
+ export interface DraggableTabsProps extends React.HTMLAttributes<HTMLDivElement> {
22
+ staticTabs?: DraggableTabItem[];
23
+ tabs: DraggableTabItem[];
24
+ onReorder?: (oldIndex: number, newIndex: number) => void;
25
+ }
26
+ declare function DraggableTabs({ staticTabs, tabs, onReorder, className, ...props }: DraggableTabsProps): import("react/jsx-runtime").JSX.Element;
27
+ export { DraggableTabs };
@@ -0,0 +1,61 @@
1
+ "use client";
2
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
3
+ import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors, } from "@dnd-kit/core";
4
+ import { SortableContext, horizontalListSortingStrategy, sortableKeyboardCoordinates, useSortable, } from "@dnd-kit/sortable";
5
+ import { CSS } from "@dnd-kit/utilities";
6
+ import { XIcon } from "./animated-icons/x";
7
+ import { cn } from "./lib/utils";
8
+ import { Tooltip } from "./tooltip";
9
+ function TabButton({ item, dragHandleProps, }) {
10
+ var _a, _b, _c;
11
+ const isDraggable = Boolean(dragHandleProps);
12
+ const labelClassName = item.labelClassName
13
+ ? item.labelClassName
14
+ : item.active
15
+ ? "text-foreground"
16
+ : "text-muted-foreground hover:text-foreground";
17
+ const content = (_jsxs("div", { className: cn("group flex items-center gap-2 rounded-t-md border-b-2 px-3 transition-colors select-none", item.disabled
18
+ ? "cursor-not-allowed opacity-50"
19
+ : isDraggable
20
+ ? "cursor-grab active:cursor-grabbing"
21
+ : "cursor-pointer", item.active
22
+ ? (_a = item.activeClassName) !== null && _a !== void 0 ? _a : "border-green-500"
23
+ : (_b = item.inactiveClassName) !== null && _b !== void 0 ? _b : "border-transparent hover:bg-muted", item.className), ...dragHandleProps, children: [_jsxs("button", { type: "button", onClick: item.disabled ? undefined : item.onSelect, disabled: item.disabled, className: cn("flex cursor-pointer items-center gap-2 py-3 pr-1 text-sm font-medium whitespace-nowrap disabled:cursor-not-allowed", labelClassName), children: [item.leading, item.label, item.trailing] }), item.onClose ? (_jsx("button", { type: "button", onClick: (event) => {
24
+ var _a;
25
+ event.stopPropagation();
26
+ (_a = item.onClose) === null || _a === void 0 ? void 0 : _a.call(item);
27
+ }, className: cn("cursor-pointer rounded p-1 text-muted-foreground hover:text-foreground", item.closeButtonClassName), "aria-label": "Close tab", children: _jsx(XIcon, { size: 12 }) })) : null] }));
28
+ if (!item.tooltip) {
29
+ return content;
30
+ }
31
+ return (_jsx(Tooltip, { content: item.tooltip, side: (_c = item.tooltipSide) !== null && _c !== void 0 ? _c : "bottom", contentClassName: item.tooltipContentClassName, children: content }));
32
+ }
33
+ function SortableTab({ item }) {
34
+ const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: item.id });
35
+ return (_jsx("div", { ref: setNodeRef, style: {
36
+ transform: CSS.Transform.toString(transform),
37
+ transition,
38
+ zIndex: isDragging ? 50 : undefined,
39
+ }, className: cn(isDragging && "opacity-50 shadow-lg"), children: _jsx(TabButton, { item: item, dragHandleProps: { ...attributes, ...listeners } }) }));
40
+ }
41
+ function DraggableTabs({ staticTabs = [], tabs, onReorder, className, ...props }) {
42
+ const sensors = useSensors(useSensor(PointerSensor, {
43
+ activationConstraint: {
44
+ distance: 8,
45
+ },
46
+ }), useSensor(KeyboardSensor, {
47
+ coordinateGetter: sortableKeyboardCoordinates,
48
+ }));
49
+ const handleDragEnd = (event) => {
50
+ const { active, over } = event;
51
+ if (!over || active.id === over.id || !onReorder)
52
+ return;
53
+ const oldIndex = tabs.findIndex((tab) => tab.id === active.id);
54
+ const newIndex = tabs.findIndex((tab) => tab.id === over.id);
55
+ if (oldIndex >= 0 && newIndex >= 0) {
56
+ onReorder(oldIndex, newIndex);
57
+ }
58
+ };
59
+ return (_jsxs("div", { className: cn("flex items-end gap-1 border-b overflow-x-auto overflow-y-hidden bg-background", className), ...props, children: [staticTabs.map((tab) => (_jsx(TabButton, { item: tab }, tab.id))), _jsx(DndContext, { sensors: sensors, collisionDetection: closestCenter, onDragEnd: handleDragEnd, children: _jsx(SortableContext, { items: tabs.map((tab) => tab.id), strategy: horizontalListSortingStrategy, children: tabs.map((tab) => (_jsx(SortableTab, { item: tab }, tab.id))) }) })] }));
60
+ }
61
+ export { DraggableTabs };