@neasg/design-system 0.4.8 → 0.4.9

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 (115) hide show
  1. package/README.md +4 -4
  2. package/dist/alert.d.ts +21 -2
  3. package/dist/alert.js +10 -3
  4. package/dist/avatar.d.ts +5 -2
  5. package/dist/avatar.js +76 -8
  6. package/dist/back-button.d.ts +2 -0
  7. package/dist/back-button.js +1 -1
  8. package/dist/badge.d.ts +3 -2
  9. package/dist/badge.js +9 -3
  10. package/dist/breadcrumb.js +2 -1
  11. package/dist/button.d.ts +1 -1
  12. package/dist/button.js +12 -9
  13. package/dist/calendar.js +10 -9
  14. package/dist/card.d.ts +29 -1
  15. package/dist/card.js +61 -7
  16. package/dist/checkbox.d.ts +2 -0
  17. package/dist/checkbox.js +2 -2
  18. package/dist/close-button.d.ts +6 -0
  19. package/dist/close-button.js +8 -0
  20. package/dist/collapsible.d.ts +7 -1
  21. package/dist/collapsible.js +2 -2
  22. package/dist/command-search.d.ts +16 -1
  23. package/dist/command-search.js +42 -16
  24. package/dist/command.js +3 -3
  25. package/dist/copy-button.d.ts +3 -1
  26. package/dist/copy-button.js +4 -2
  27. package/dist/count-badge.d.ts +3 -1
  28. package/dist/count-badge.js +10 -5
  29. package/dist/date-input.d.ts +18 -3
  30. package/dist/date-input.js +94 -21
  31. package/dist/dialog-primitive.js +4 -3
  32. package/dist/draggable-tabs.d.ts +7 -0
  33. package/dist/draggable-tabs.js +6 -5
  34. package/dist/drawer.js +2 -2
  35. package/dist/dropdown-button.d.ts +18 -0
  36. package/dist/dropdown-button.js +31 -0
  37. package/dist/edit-button.d.ts +10 -0
  38. package/dist/edit-button.js +7 -0
  39. package/dist/editable-table.d.ts +3 -1
  40. package/dist/editable-table.js +25 -12
  41. package/dist/field.d.ts +2 -2
  42. package/dist/field.js +4 -3
  43. package/dist/file-upload-status-toast.d.ts +28 -0
  44. package/dist/file-upload-status-toast.js +136 -0
  45. package/dist/file-upload.js +8 -7
  46. package/dist/filter-popover.d.ts +37 -0
  47. package/dist/filter-popover.js +73 -0
  48. package/dist/filter-toggle.js +2 -2
  49. package/dist/index.d.ts +45 -14
  50. package/dist/index.js +19 -4
  51. package/dist/input-control.d.ts +3 -2
  52. package/dist/input-control.js +3 -2
  53. package/dist/label-value-grid.d.ts +4 -2
  54. package/dist/label-value-grid.js +7 -6
  55. package/dist/label-value.js +3 -1
  56. package/dist/layout-right-panel-edge.d.ts +6 -0
  57. package/dist/layout-right-panel-edge.js +6 -0
  58. package/dist/layout.d.ts +77 -5
  59. package/dist/layout.js +439 -35
  60. package/dist/lib/date-utils.d.ts +10 -2
  61. package/dist/lib/date-utils.js +91 -11
  62. package/dist/lib/utils.js +8 -1
  63. package/dist/link.js +2 -2
  64. package/dist/message-item.d.ts +62 -0
  65. package/dist/message-item.js +148 -0
  66. package/dist/multi-select.js +31 -5
  67. package/dist/notification.d.ts +82 -0
  68. package/dist/notification.js +121 -0
  69. package/dist/otp-input.js +1 -1
  70. package/dist/page-header.js +1 -1
  71. package/dist/page-section.d.ts +2 -1
  72. package/dist/page-section.js +4 -3
  73. package/dist/pagination.js +2 -2
  74. package/dist/popover-menu.d.ts +23 -1
  75. package/dist/popover-menu.js +75 -5
  76. package/dist/popover-primitive.js +1 -1
  77. package/dist/progress.d.ts +5 -2
  78. package/dist/progress.js +6 -3
  79. package/dist/radio-group.d.ts +21 -0
  80. package/dist/radio-group.js +41 -0
  81. package/dist/rich-text-editor.d.ts +56 -0
  82. package/dist/rich-text-editor.js +394 -0
  83. package/dist/routing-timeline.d.ts +22 -0
  84. package/dist/routing-timeline.js +58 -0
  85. package/dist/scroll-hint.d.ts +8 -0
  86. package/dist/scroll-hint.js +7 -0
  87. package/dist/search-input-shell.js +1 -1
  88. package/dist/search-input.js +2 -2
  89. package/dist/section-nav.js +4 -4
  90. package/dist/select-primitive.js +3 -3
  91. package/dist/select.js +1 -1
  92. package/dist/skeleton.js +1 -1
  93. package/dist/styles.css +235 -57
  94. package/dist/table-column-visibility.d.ts +21 -0
  95. package/dist/table-column-visibility.js +74 -0
  96. package/dist/table-toolbar.js +1 -1
  97. package/dist/table.d.ts +12 -1
  98. package/dist/table.js +67 -33
  99. package/dist/tabs.d.ts +14 -3
  100. package/dist/tabs.js +60 -6
  101. package/dist/tailwind-preset.js +46 -0
  102. package/dist/task-mode-shell.d.ts +24 -0
  103. package/dist/task-mode-shell.js +110 -0
  104. package/dist/textarea.js +2 -1
  105. package/dist/theme-switcher.d.ts +28 -0
  106. package/dist/theme-switcher.js +27 -0
  107. package/dist/theme.d.ts +167 -0
  108. package/dist/theme.js +112 -0
  109. package/dist/toaster.js +5 -4
  110. package/dist/tooltip.js +1 -1
  111. package/dist/typography.d.ts +1 -1
  112. package/dist/typography.js +5 -5
  113. package/dist/workspace-header.d.ts +13 -0
  114. package/dist/workspace-header.js +11 -0
  115. package/package.json +15 -1
@@ -0,0 +1,394 @@
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 { createPortal } from "react-dom";
5
+ import DOMPurify from "dompurify";
6
+ import { useEditor, EditorContent } from "@tiptap/react";
7
+ import StarterKit from "@tiptap/starter-kit";
8
+ import Placeholder from "@tiptap/extension-placeholder";
9
+ import Link from "@tiptap/extension-link";
10
+ import { TextStyle } from "@tiptap/extension-text-style";
11
+ import { Color } from "@tiptap/extension-color";
12
+ import Highlight from "@tiptap/extension-highlight";
13
+ import Underline from "@tiptap/extension-underline";
14
+ import { BoldIcon, ChevronDownIcon, HighlighterIcon, ItalicIcon, LinkIcon, ListIcon, ListOrderedIcon, MaximizeIcon, PaletteIcon, PaperclipIcon, PlusIcon, SendIcon, StrikethroughIcon, UnderlineIcon, XIcon, } from "./animated-icons";
15
+ import { Button } from "./button";
16
+ import { DropdownButton } from "./dropdown-button";
17
+ import { Input } from "./input";
18
+ import { inputControlPaddingClassName } from "./input-control";
19
+ import { Popover } from "./popover";
20
+ import { PopoverMenuItem, PopoverMenuSection } from "./popover-menu";
21
+ import { Tooltip } from "./tooltip";
22
+ import { cn } from "./lib/utils";
23
+ const DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
24
+ const DEFAULT_MAX_FILE_SIZE_LABEL = "10MB";
25
+ const DEFAULT_ACCEPTED_FILE_TYPES = "*";
26
+ const DEFAULT_ACCEPTED_FILE_TYPES_LABEL = "Any file type";
27
+ const DEFAULT_TEMPLATE_CATEGORY = "GENERAL";
28
+ const DEFAULT_COLOR_PALETTE = [
29
+ { name: "Default", value: "" },
30
+ { name: "Red", value: "#dc2626" },
31
+ { name: "Orange", value: "#ea580c" },
32
+ { name: "Yellow", value: "#ca8a04" },
33
+ { name: "Green", value: "#16a34a" },
34
+ { name: "Blue", value: "#2563eb" },
35
+ { name: "Purple", value: "#9333ea" },
36
+ { name: "Pink", value: "#db2777" },
37
+ { name: "Gray", value: "#6b7280" },
38
+ ];
39
+ const DOMPURIFY_ALLOWED_TAGS = [
40
+ "p",
41
+ "br",
42
+ "h1",
43
+ "h2",
44
+ "h3",
45
+ "h4",
46
+ "h5",
47
+ "h6",
48
+ "blockquote",
49
+ "pre",
50
+ "code",
51
+ "hr",
52
+ "ul",
53
+ "ol",
54
+ "li",
55
+ "strong",
56
+ "b",
57
+ "em",
58
+ "i",
59
+ "u",
60
+ "s",
61
+ "strike",
62
+ "del",
63
+ "sub",
64
+ "sup",
65
+ "mark",
66
+ "span",
67
+ "a",
68
+ ];
69
+ const DOMPURIFY_ALLOWED_ATTR = [
70
+ "href",
71
+ "target",
72
+ "rel",
73
+ "style",
74
+ "class",
75
+ "data-color",
76
+ ];
77
+ export function formatFileSize(bytes) {
78
+ if (bytes === 0)
79
+ return "0 B";
80
+ const k = 1024;
81
+ const sizes = ["B", "KB", "MB", "GB"];
82
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
83
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
84
+ }
85
+ export function sanitizeRichTextHtml(html) {
86
+ return DOMPurify.sanitize(html, {
87
+ ALLOWED_TAGS: DOMPURIFY_ALLOWED_TAGS,
88
+ ALLOWED_ATTR: DOMPURIFY_ALLOWED_ATTR,
89
+ ALLOW_DATA_ATTR: true,
90
+ });
91
+ }
92
+ function normalizeHtml(html) {
93
+ return html.replace(/>\s+</g, "><").trim();
94
+ }
95
+ function escapeHtml(text) {
96
+ const htmlEscapes = {
97
+ "&": "&amp;",
98
+ "<": "&lt;",
99
+ ">": "&gt;",
100
+ '"': "&quot;",
101
+ "'": "&#39;",
102
+ };
103
+ return text.replace(/[&<>"']/g, (char) => htmlEscapes[char]);
104
+ }
105
+ export function replaceTemplateVariables(content, context) {
106
+ const sanitizedContent = normalizeHtml(sanitizeRichTextHtml(content));
107
+ const variableMap = Object.fromEntries(Object.entries(context).map(([key, value]) => [
108
+ key.toLowerCase(),
109
+ value ? escapeHtml(value) : undefined,
110
+ ]));
111
+ const highlightStart = '<mark data-color="#fef08a" style="background-color: #fef08a;">';
112
+ const highlightEnd = "</mark>";
113
+ let result = sanitizedContent.replace(/\{\{(\w+)\}\}/gi, (match, varName) => {
114
+ const value = variableMap[varName.toLowerCase()];
115
+ return value ? value : `${highlightStart}${match}${highlightEnd}`;
116
+ });
117
+ result = result.replace(/\[([^\]]+)\]/g, (match) => {
118
+ return `${highlightStart}${match}${highlightEnd}`;
119
+ });
120
+ return result;
121
+ }
122
+ function ToolbarIconButton({ active, label, onClick, children, }) {
123
+ return (_jsx(Button, { type: "button", variant: "ghost", size: "icon", className: cn("h-8 w-8", active && "bg-muted"), onClick: onClick, "aria-label": label, title: label, children: children }));
124
+ }
125
+ function getTemplateCategory(template) {
126
+ return template.category || DEFAULT_TEMPLATE_CATEGORY;
127
+ }
128
+ export function RichTextEditor({ placeholder = "Type message here...", editorAriaLabel = "Message body", header, isExpanded = false, onExpandToggle, onSend, className, maxFileSize = DEFAULT_MAX_FILE_SIZE, maxFileSizeLabel = DEFAULT_MAX_FILE_SIZE_LABEL, acceptedFileTypes = DEFAULT_ACCEPTED_FILE_TYPES, acceptedFileTypesLabel = DEFAULT_ACCEPTED_FILE_TYPES_LABEL, isSending = false, showTemplates = true, templates = [], templateContext = {}, templateCategoryLabels = {}, templateCategoryOrder, templateCreateHref, templateCreateLabel = "Create new template", onFileRejected, colorPalette = DEFAULT_COLOR_PALETTE, sendLabel = "Send", sendingLabel = "Sending...", sendActions, }) {
129
+ const [isEmpty, setIsEmpty] = React.useState(true);
130
+ const [attachments, setAttachments] = React.useState([]);
131
+ const [isDragging, setIsDragging] = React.useState(false);
132
+ const [linkPopoverOpen, setLinkPopoverOpen] = React.useState(false);
133
+ const [linkUrl, setLinkUrl] = React.useState("");
134
+ const [linkMenuPosition, setLinkMenuPosition] = React.useState(null);
135
+ const [templatePopoverOpen, setTemplatePopoverOpen] = React.useState(false);
136
+ const fileInputRef = React.useRef(null);
137
+ const dropZoneRef = React.useRef(null);
138
+ const linkInputRef = React.useRef(null);
139
+ const linkMenuRef = React.useRef(null);
140
+ const templatesByCategory = React.useMemo(() => {
141
+ const grouped = new Map();
142
+ templates.forEach((template) => {
143
+ var _a;
144
+ const category = getTemplateCategory(template);
145
+ grouped.set(category, [...((_a = grouped.get(category)) !== null && _a !== void 0 ? _a : []), template]);
146
+ });
147
+ const categories = (templateCategoryOrder === null || templateCategoryOrder === void 0 ? void 0 : templateCategoryOrder.length)
148
+ ? [
149
+ ...templateCategoryOrder,
150
+ ...Array.from(grouped.keys()).filter((category) => !templateCategoryOrder.includes(category)),
151
+ ]
152
+ : Array.from(grouped.keys());
153
+ return categories
154
+ .map((category) => {
155
+ var _a;
156
+ return ({
157
+ category,
158
+ templates: (_a = grouped.get(category)) !== null && _a !== void 0 ? _a : [],
159
+ });
160
+ })
161
+ .filter((group) => group.templates.length > 0);
162
+ }, [templateCategoryOrder, templates]);
163
+ const extensions = React.useMemo(() => [
164
+ StarterKit.configure({
165
+ heading: false,
166
+ codeBlock: false,
167
+ code: false,
168
+ blockquote: false,
169
+ horizontalRule: false,
170
+ link: false,
171
+ underline: false,
172
+ }),
173
+ Placeholder.configure({
174
+ placeholder,
175
+ }),
176
+ Link.configure({
177
+ openOnClick: false,
178
+ }),
179
+ TextStyle,
180
+ Color,
181
+ Highlight.configure({
182
+ multicolor: true,
183
+ }),
184
+ Underline,
185
+ ], [placeholder]);
186
+ const editor = useEditor({
187
+ immediatelyRender: false,
188
+ extensions,
189
+ content: "",
190
+ editorProps: {
191
+ attributes: {
192
+ role: "textbox",
193
+ "aria-multiline": "true",
194
+ "aria-label": editorAriaLabel,
195
+ class: cn("prose prose-sm max-w-none focus:outline-none", "min-h-[120px]", inputControlPaddingClassName),
196
+ },
197
+ },
198
+ onUpdate: ({ editor }) => {
199
+ setIsEmpty(!editor.state.doc.textContent.trim());
200
+ },
201
+ });
202
+ const rejectFile = React.useCallback((message) => {
203
+ if (onFileRejected) {
204
+ onFileRejected(message);
205
+ return;
206
+ }
207
+ if (typeof window !== "undefined") {
208
+ window.alert(message);
209
+ }
210
+ }, [onFileRejected]);
211
+ const handleLinkSubmit = React.useCallback(() => {
212
+ if (linkUrl && editor) {
213
+ let url = linkUrl.trim();
214
+ if (url && !url.match(/^https?:\/\//i)) {
215
+ url = `https://${url}`;
216
+ }
217
+ editor.chain().focus().setLink({ href: url }).run();
218
+ setLinkUrl("");
219
+ setLinkPopoverOpen(false);
220
+ setLinkMenuPosition(null);
221
+ }
222
+ }, [editor, linkUrl]);
223
+ const handleLinkKeyDown = React.useCallback((event) => {
224
+ if (event.key === "Enter") {
225
+ event.preventDefault();
226
+ handleLinkSubmit();
227
+ }
228
+ else if (event.key === "Escape") {
229
+ setLinkUrl("");
230
+ setLinkPopoverOpen(false);
231
+ setLinkMenuPosition(null);
232
+ editor === null || editor === void 0 ? void 0 : editor.chain().focus().run();
233
+ }
234
+ }, [editor, handleLinkSubmit]);
235
+ const isFileTypeAccepted = React.useCallback((file) => {
236
+ if (acceptedFileTypes === "*")
237
+ return true;
238
+ const extensions = acceptedFileTypes
239
+ .split(",")
240
+ .map((extension) => extension.trim().toLowerCase());
241
+ const fileName = file.name.toLowerCase();
242
+ return extensions.some((extension) => fileName.endsWith(extension));
243
+ }, [acceptedFileTypes]);
244
+ const processFiles = React.useCallback((files) => {
245
+ const newAttachments = [];
246
+ for (const file of Array.from(files)) {
247
+ if (file.size > maxFileSize) {
248
+ rejectFile(`File "${file.name}" exceeds ${formatFileSize(maxFileSize)} limit`);
249
+ continue;
250
+ }
251
+ if (!isFileTypeAccepted(file)) {
252
+ rejectFile(`File "${file.name}" is not a supported type.\nAccepted: ${acceptedFileTypesLabel}`);
253
+ continue;
254
+ }
255
+ if (attachments.some((item) => item.name === file.name && item.size === file.size)) {
256
+ continue;
257
+ }
258
+ newAttachments.push({
259
+ name: file.name,
260
+ size: file.size,
261
+ type: file.type,
262
+ file,
263
+ });
264
+ }
265
+ if (newAttachments.length > 0) {
266
+ setAttachments((previous) => [...previous, ...newAttachments]);
267
+ }
268
+ }, [
269
+ acceptedFileTypesLabel,
270
+ attachments,
271
+ isFileTypeAccepted,
272
+ maxFileSize,
273
+ rejectFile,
274
+ ]);
275
+ const handleFileSelect = React.useCallback((event) => {
276
+ const files = event.target.files;
277
+ if (!files)
278
+ return;
279
+ processFiles(files);
280
+ event.target.value = "";
281
+ }, [processFiles]);
282
+ const handleDragEnter = React.useCallback((event) => {
283
+ event.preventDefault();
284
+ event.stopPropagation();
285
+ setIsDragging(true);
286
+ }, []);
287
+ const handleDragLeave = React.useCallback((event) => {
288
+ event.preventDefault();
289
+ event.stopPropagation();
290
+ if (dropZoneRef.current &&
291
+ !dropZoneRef.current.contains(event.relatedTarget)) {
292
+ setIsDragging(false);
293
+ }
294
+ }, []);
295
+ const handleDragOver = React.useCallback((event) => {
296
+ event.preventDefault();
297
+ event.stopPropagation();
298
+ }, []);
299
+ const handleDrop = React.useCallback((event) => {
300
+ event.preventDefault();
301
+ event.stopPropagation();
302
+ setIsDragging(false);
303
+ const files = event.dataTransfer.files;
304
+ if (files && files.length > 0) {
305
+ processFiles(files);
306
+ }
307
+ }, [processFiles]);
308
+ const removeAttachment = React.useCallback((index) => {
309
+ setAttachments((previous) => previous.filter((_, itemIndex) => itemIndex !== index));
310
+ }, []);
311
+ const hasContent = !isEmpty || attachments.length > 0;
312
+ const sendDisabled = !hasContent || isSending || !onSend;
313
+ const handleSend = async (metadata) => {
314
+ if (!editor || !onSend || sendDisabled)
315
+ return;
316
+ const result = await onSend(editor.getHTML(), attachments.length > 0 ? attachments : undefined, metadata);
317
+ if (result !== false) {
318
+ editor.commands.clearContent();
319
+ setAttachments([]);
320
+ }
321
+ };
322
+ const openLinkMenu = () => {
323
+ if (!editor || editor.state.selection.empty)
324
+ return;
325
+ const { to } = editor.state.selection;
326
+ const endCoords = editor.view.coordsAtPos(to);
327
+ const menuWidth = 320;
328
+ const viewportWidth = window.innerWidth;
329
+ let left = endCoords.left;
330
+ if (left + menuWidth > viewportWidth - 16) {
331
+ left = viewportWidth - menuWidth - 16;
332
+ }
333
+ setLinkMenuPosition({
334
+ top: endCoords.top - 8,
335
+ left,
336
+ });
337
+ setLinkPopoverOpen(true);
338
+ };
339
+ const applyTemplate = (template) => {
340
+ if (!editor)
341
+ return;
342
+ const processedContent = replaceTemplateVariables(template.content, templateContext);
343
+ editor.commands.setContent(processedContent);
344
+ setIsEmpty(false);
345
+ setTemplatePopoverOpen(false);
346
+ };
347
+ const hasSendActions = Boolean(sendActions === null || sendActions === void 0 ? void 0 : sendActions.length);
348
+ const sendAction = hasSendActions ? (_jsx(DropdownButton, { className: "w-full", align: "end", disabled: sendDisabled, label: isSending ? sendingLabel : sendLabel, leadingIcon: _jsx(SendIcon, { size: 16 }), split: true, onClick: () => {
349
+ void handleSend();
350
+ }, children: _jsx(PopoverMenuSection, { children: sendActions === null || sendActions === void 0 ? void 0 : sendActions.map((action) => (_jsx(PopoverMenuItem, { disabled: sendDisabled || action.disabled, onClick: () => {
351
+ void handleSend(action.metadata);
352
+ }, children: action.label }, action.id))) }) })) : (_jsxs(Button, { type: "button", className: "w-full", disabled: sendDisabled, onClick: () => {
353
+ void handleSend();
354
+ }, children: [_jsx(SendIcon, { size: 16 }), isSending ? sendingLabel : sendLabel] }));
355
+ return (_jsxs("div", { ref: dropZoneRef, onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDragOver: handleDragOver, onDrop: handleDrop, className: cn("overflow-hidden rounded-control border bg-background transition-all focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50", isExpanded && "shadow-md", isDragging && "border-2 border-primary bg-primary/5", className), children: [header && (_jsxs("div", { className: cn("flex items-center justify-between border-b bg-muted dark:bg-muted/30", inputControlPaddingClassName), children: [_jsx("div", { className: "flex-1", children: header }), _jsx("div", { className: "flex items-center gap-2", children: onExpandToggle && (_jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "h-6 w-6", onClick: onExpandToggle, "aria-label": "Expand editor", children: _jsx(MaximizeIcon, { size: 14 }) })) })] })), _jsxs("div", { className: cn("bg-background transition-all", isExpanded ? "min-h-[200px]" : "min-h-[120px]"), children: [_jsx(EditorContent, { editor: editor, className: cn("text-sm", "[&_.ProseMirror]:min-h-[120px] [&_.ProseMirror]:outline-none", "[&_.ProseMirror_p.is-editor-empty:first-child::before]:text-muted-foreground", "[&_.ProseMirror_p.is-editor-empty:first-child::before]:content-[attr(data-placeholder)]", "[&_.ProseMirror_p.is-editor-empty:first-child::before]:float-left", "[&_.ProseMirror_p.is-editor-empty:first-child::before]:pointer-events-none", "[&_.ProseMirror_p.is-editor-empty:first-child::before]:h-0", "[&_.ProseMirror_ul]:my-2 [&_.ProseMirror_ul]:list-disc [&_.ProseMirror_ul]:pl-6", "[&_.ProseMirror_ol]:my-2 [&_.ProseMirror_ol]:list-decimal [&_.ProseMirror_ol]:pl-6", "[&_.ProseMirror_li]:my-1", "[&_.ProseMirror_ul_ul]:list-[circle] [&_.ProseMirror_ul_ul_ul]:list-[square]") }), linkPopoverOpen &&
356
+ linkMenuPosition &&
357
+ typeof document !== "undefined" &&
358
+ createPortal(_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-[99]", onClick: () => {
359
+ setLinkPopoverOpen(false);
360
+ setLinkUrl("");
361
+ setLinkMenuPosition(null);
362
+ editor === null || editor === void 0 ? void 0 : editor.chain().focus().run();
363
+ } }), _jsx("div", { ref: linkMenuRef, className: "fixed z-[100] w-80 animate-in fade-in-0 zoom-in-95 rounded-md border bg-popover p-3 shadow-md", style: {
364
+ top: linkMenuPosition.top,
365
+ left: linkMenuPosition.left,
366
+ }, children: _jsxs("div", { className: "flex items-end gap-2", children: [_jsx(Input, { ref: linkInputRef, label: "URL", id: "link-url", type: "url", placeholder: "https://example.com", value: linkUrl, onChange: (event) => setLinkUrl(event.target.value), onKeyDown: handleLinkKeyDown, inputClassName: "h-8 text-sm", autoFocus: true }), _jsx(Button, { type: "button", className: "h-8 px-3", onClick: handleLinkSubmit, disabled: !linkUrl.trim(), children: "Add" })] }) })] }), document.body)] }), attachments.length > 0 && (_jsx("div", { className: cn("space-y-2 border-t", inputControlPaddingClassName), children: attachments.map((attachment, index) => (_jsxs("div", { className: "flex w-full items-center gap-2 rounded-md border bg-muted px-3 py-2 text-sm dark:bg-muted/50", children: [_jsx(PaperclipIcon, { size: 14, className: "shrink-0 text-muted-foreground" }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: attachment.name }), _jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: formatFileSize(attachment.size) }), _jsx("button", { type: "button", onClick: () => removeAttachment(index), className: "shrink-0 text-muted-foreground hover:text-foreground", "aria-label": `Remove ${attachment.name}`, children: _jsx(XIcon, { size: 14 }) })] }, `${attachment.name}-${attachment.size}-${index}`))) })), _jsx("input", { ref: fileInputRef, type: "file", accept: acceptedFileTypes, multiple: true, onChange: handleFileSelect, className: "hidden" }), _jsx("div", { className: cn("space-y-2 border-t bg-muted dark:bg-muted/30", inputControlPaddingClassName), children: _jsxs("div", { className: "flex flex-wrap items-center gap-1", children: [_jsx(ToolbarIconButton, { label: "Bold", active: editor === null || editor === void 0 ? void 0 : editor.isActive("bold"), onClick: () => editor === null || editor === void 0 ? void 0 : editor.chain().focus().toggleBold().run(), children: _jsx(BoldIcon, { size: 14 }) }), _jsx(ToolbarIconButton, { label: "Italic", active: editor === null || editor === void 0 ? void 0 : editor.isActive("italic"), onClick: () => editor === null || editor === void 0 ? void 0 : editor.chain().focus().toggleItalic().run(), children: _jsx(ItalicIcon, { size: 14 }) }), _jsx(ToolbarIconButton, { label: "Underline", active: editor === null || editor === void 0 ? void 0 : editor.isActive("underline"), onClick: () => editor === null || editor === void 0 ? void 0 : editor.chain().focus().toggleUnderline().run(), children: _jsx(UnderlineIcon, { size: 14 }) }), _jsx(ToolbarIconButton, { label: "Strikethrough", active: editor === null || editor === void 0 ? void 0 : editor.isActive("strike"), onClick: () => editor === null || editor === void 0 ? void 0 : editor.chain().focus().toggleStrike().run(), children: _jsx(StrikethroughIcon, { size: 14 }) }), _jsx(Popover, { align: "start", contentClassName: "w-auto p-2", trigger: _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: cn("h-8 w-8", (editor === null || editor === void 0 ? void 0 : editor.isActive("textStyle")) && "bg-muted"), "aria-label": "Text color", title: "Text color", children: _jsx(PaletteIcon, { size: 14, style: {
367
+ color: (editor === null || editor === void 0 ? void 0 : editor.getAttributes("textStyle").color) || undefined,
368
+ } }) }), children: _jsx("div", { className: "grid grid-cols-5 gap-1", children: colorPalette.map((color) => (_jsx("button", { type: "button", title: color.name, className: cn("h-6 w-6 rounded-md border border-border transition-transform hover:scale-110", color.value === "" && "bg-background", (editor === null || editor === void 0 ? void 0 : editor.getAttributes("textStyle").color) === color.value &&
369
+ "ring-2 ring-primary ring-offset-1"), style: {
370
+ backgroundColor: color.value || undefined,
371
+ }, onClick: () => {
372
+ if (color.value === "") {
373
+ editor === null || editor === void 0 ? void 0 : editor.chain().focus().unsetColor().run();
374
+ }
375
+ else {
376
+ editor === null || editor === void 0 ? void 0 : editor.chain().focus().setColor(color.value).run();
377
+ }
378
+ }, children: color.value === "" && (_jsx("span", { className: "text-xs text-muted-foreground", children: "A" })) }, color.name))) }) }), _jsx(Popover, { align: "start", contentClassName: "w-auto p-2", trigger: _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: cn("h-8 w-8", (editor === null || editor === void 0 ? void 0 : editor.isActive("highlight")) && "bg-muted"), "aria-label": "Highlight color", title: "Highlight color", children: _jsx(HighlighterIcon, { size: 14, style: {
379
+ color: (editor === null || editor === void 0 ? void 0 : editor.getAttributes("highlight").color) || undefined,
380
+ } }) }), children: _jsx("div", { className: "grid grid-cols-5 gap-1", children: colorPalette.map((color) => (_jsx("button", { type: "button", title: color.name, className: cn("h-6 w-6 rounded-md border border-border transition-transform hover:scale-110", color.value === "" && "bg-background", (editor === null || editor === void 0 ? void 0 : editor.getAttributes("highlight").color) === color.value &&
381
+ "ring-2 ring-primary ring-offset-1"), style: {
382
+ backgroundColor: color.value || undefined,
383
+ }, onClick: () => {
384
+ if (color.value === "") {
385
+ editor === null || editor === void 0 ? void 0 : editor.chain().focus().unsetHighlight().run();
386
+ }
387
+ else {
388
+ editor === null || editor === void 0 ? void 0 : editor.chain().focus().setHighlight({ color: color.value }).run();
389
+ }
390
+ }, children: color.value === "" && (_jsx("span", { className: "text-xs text-muted-foreground", children: "A" })) }, `bg-${color.name}`))) }) }), _jsx(ToolbarIconButton, { label: "Bullet list", active: editor === null || editor === void 0 ? void 0 : editor.isActive("bulletList"), onClick: () => editor === null || editor === void 0 ? void 0 : editor.chain().focus().toggleBulletList().run(), children: _jsx(ListIcon, { size: 14 }) }), _jsx(ToolbarIconButton, { label: "Ordered list", active: editor === null || editor === void 0 ? void 0 : editor.isActive("orderedList"), onClick: () => editor === null || editor === void 0 ? void 0 : editor.chain().focus().toggleOrderedList().run(), children: _jsx(ListOrderedIcon, { size: 14 }) }), _jsx(Tooltip, { content: `Attach files (max ${maxFileSizeLabel}) - ${acceptedFileTypesLabel}`, side: "top", contentClassName: "max-w-[240px] text-center", children: _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "h-8 w-8", onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, "aria-label": "Attach files", children: _jsx(PaperclipIcon, { size: 14 }) }) }), _jsx(ToolbarIconButton, { label: "Add link", active: editor === null || editor === void 0 ? void 0 : editor.isActive("link"), onClick: openLinkMenu, children: _jsx(LinkIcon, { size: 14 }) }), showTemplates && templates.length > 0 && (_jsx(_Fragment, { children: _jsxs(Popover, { open: templatePopoverOpen, onOpenChange: setTemplatePopoverOpen, align: "end", contentClassName: "w-96 p-0", trigger: _jsxs(Button, { type: "button", variant: "ghost", className: "h-8 gap-1 text-muted-foreground", children: ["Template", _jsx(ChevronDownIcon, { size: 14 })] }), children: [_jsx("div", { className: "max-h-80 overflow-y-auto", children: templatesByCategory.map(({ category, templates }) => {
391
+ var _a;
392
+ return (_jsxs("div", { children: [_jsx("div", { className: "sticky top-0 bg-muted px-3 py-2 text-sm font-semibold text-foreground", children: (_a = templateCategoryLabels[category]) !== null && _a !== void 0 ? _a : category }), templates.map((template) => (_jsxs("button", { type: "button", className: "w-full border-b border-border/50 px-3 py-2 text-left text-sm transition-colors last:border-0 hover:bg-muted", onClick: () => applyTemplate(template), children: [_jsx("div", { className: "truncate font-medium", children: template.name }), template.description && (_jsx("div", { className: "mt-0.5 truncate text-xs text-muted-foreground", children: template.description }))] }, template.id)))] }, category));
393
+ }) }), templateCreateHref && (_jsx("div", { className: "border-t p-2", children: _jsxs("a", { href: templateCreateHref, target: "_blank", rel: "noopener noreferrer", className: "flex w-full items-center justify-center gap-1.5 rounded-md px-3 py-2 text-sm text-primary transition-colors hover:bg-muted", children: [_jsx(PlusIcon, { size: 16 }), templateCreateLabel] }) }))] }) }))] }) }), _jsx("div", { className: "bg-muted px-3 pb-2 pt-1 dark:bg-background", children: sendAction })] }));
394
+ }
@@ -0,0 +1,22 @@
1
+ import * as React from "react";
2
+ export type RoutingTimelineItemKind = "assigned" | "reassigned" | "returned" | "routed" | "system";
3
+ export interface RoutingTimelineItem {
4
+ id: string;
5
+ kind?: RoutingTimelineItemKind;
6
+ title?: React.ReactNode;
7
+ timestamp?: React.ReactNode;
8
+ actor?: React.ReactNode;
9
+ remark?: React.ReactNode;
10
+ description?: React.ReactNode;
11
+ from?: React.ReactNode;
12
+ to?: React.ReactNode;
13
+ fromLabel?: React.ReactNode;
14
+ toLabel?: React.ReactNode;
15
+ badgeLabel?: React.ReactNode;
16
+ }
17
+ export interface RoutingTimelineProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
18
+ items: RoutingTimelineItem[];
19
+ emptyMessage?: React.ReactNode;
20
+ }
21
+ declare function RoutingTimeline({ items, emptyMessage, className, ...props }: RoutingTimelineProps): import("react/jsx-runtime").JSX.Element;
22
+ export { RoutingTimeline };
@@ -0,0 +1,58 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { ArrowDown } from "lucide-react";
3
+ import { Badge } from "./badge";
4
+ import { EmptyState } from "./empty-state";
5
+ import { cn } from "./lib/utils";
6
+ import { Typography } from "./typography";
7
+ const routingKindConfig = {
8
+ assigned: {
9
+ title: "Assigned Case",
10
+ label: "Assigned",
11
+ badgeVariant: "info",
12
+ markerClassName: "border-[hsl(var(--status-info-foreground)/0.35)] bg-[hsl(var(--status-info))]",
13
+ },
14
+ reassigned: {
15
+ title: "Reassigned Case",
16
+ label: "Reassigned",
17
+ badgeVariant: "accent",
18
+ markerClassName: "border-[hsl(var(--status-accent-foreground)/0.35)] bg-[hsl(var(--status-accent))]",
19
+ },
20
+ returned: {
21
+ title: "Returned Case",
22
+ label: "Returned",
23
+ badgeVariant: "warning",
24
+ markerClassName: "border-[hsl(var(--status-warning-foreground)/0.35)] bg-[hsl(var(--status-warning))]",
25
+ },
26
+ routed: {
27
+ title: "Case Transferred",
28
+ label: "Routed",
29
+ badgeVariant: "secondary",
30
+ markerClassName: "border-primary/30 bg-primary/15",
31
+ },
32
+ system: {
33
+ title: "System Update",
34
+ label: "System",
35
+ badgeVariant: "muted",
36
+ markerClassName: "border-muted-foreground/30 bg-muted",
37
+ },
38
+ };
39
+ function RoutingTimelineHandoffEndpoint({ value, }) {
40
+ const isTextValue = typeof value === "string" || typeof value === "number";
41
+ return isTextValue ? (_jsx("span", { className: "min-w-0 truncate font-medium text-card-foreground", children: value })) : (_jsx("div", { className: "min-w-0", children: value }));
42
+ }
43
+ function RoutingTimeline({ items, emptyMessage = "No routing activity yet.", className, ...props }) {
44
+ if (items.length === 0) {
45
+ return (_jsx(EmptyState, { "data-slot": "routing-timeline", message: emptyMessage, className: cn("border-0 bg-transparent px-0 py-[var(--space-6)]", className), ...props }));
46
+ }
47
+ return (_jsx("div", { "data-slot": "routing-timeline", className: cn("min-w-0", className), ...props, children: _jsx("ol", { "data-slot": "routing-timeline-list", className: "relative space-y-0", children: items.map((item) => {
48
+ var _a, _b, _c, _d;
49
+ const kind = (_a = item.kind) !== null && _a !== void 0 ? _a : "routed";
50
+ const config = routingKindConfig[kind];
51
+ const hasRouteEndpoints = Boolean(item.from || item.to);
52
+ const hasCompleteRoute = Boolean(item.from && item.to);
53
+ const title = (_b = item.title) !== null && _b !== void 0 ? _b : config.title;
54
+ const remark = (_c = item.remark) !== null && _c !== void 0 ? _c : item.description;
55
+ return (_jsxs("li", { className: "relative grid grid-cols-[var(--space-3)_minmax(0,1fr)] gap-[var(--space-3)] pb-[var(--space-5)] last:pb-0", children: [_jsx("div", { className: "relative flex justify-center", children: _jsx("span", { "aria-hidden": "true", "data-slot": "routing-timeline-marker", className: cn("relative z-10 mt-[var(--space-1)] size-[var(--space-2)] rounded-full border", config.markerClassName) }) }), _jsxs("div", { "data-slot": "routing-timeline-event-card", className: "min-w-0 rounded-surface border border-border/70 bg-card p-[var(--space-3)] text-card-foreground", children: [_jsxs("div", { className: "min-w-0 space-y-[var(--space-1)]", children: [_jsxs("div", { className: "flex min-w-0 items-start justify-between gap-[var(--space-2)]", children: [_jsx(Typography, { as: "p", variant: "label", className: "min-w-0 flex-1 font-semibold", children: title }), _jsx(Badge, { variant: config.badgeVariant, bordered: true, className: "max-w-full shrink-0 self-start", children: (_d = item.badgeLabel) !== null && _d !== void 0 ? _d : config.label })] }), item.timestamp || item.actor ? (_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-x-[var(--space-1)] gap-y-[var(--space-1)]", children: [item.timestamp ? (_jsx(Typography, { as: "time", variant: "caption", className: "shrink-0", children: item.timestamp })) : null, item.actor ? (_jsxs(Typography, { as: "p", variant: "caption", className: "inline-flex min-w-0 items-center gap-x-[var(--space-1)]", children: [item.timestamp ? (_jsx("span", { "aria-hidden": "true", children: "\u00B7" })) : null, "By ", item.actor] })) : null] })) : null] }), remark ? (_jsx(Typography, { as: "div", variant: "bodySm", "data-slot": "routing-timeline-remark", className: "mt-[var(--space-4)] font-normal", children: remark })) : null, hasRouteEndpoints ? (_jsxs("footer", { "data-slot": "routing-timeline-handoff", "data-complete-route": hasCompleteRoute ? "true" : undefined, className: "mt-[var(--space-4)] flex flex-col space-y-[var(--space-2)] text-xs text-muted-foreground", children: [item.from ? (_jsx(RoutingTimelineHandoffEndpoint, { value: item.from })) : null, item.from && item.to ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "sr-only", children: "to" }), _jsx(ArrowDown, { "aria-hidden": "true", "data-slot": "routing-timeline-handoff-arrow", className: "ml-1 size-4 shrink-0 text-muted-foreground" })] })) : null, item.to ? (_jsx(RoutingTimelineHandoffEndpoint, { value: item.to })) : null] })) : null] })] }, item.id));
56
+ }) }) }));
57
+ }
58
+ export { RoutingTimeline };
@@ -0,0 +1,8 @@
1
+ import * as React from "react";
2
+ export interface ScrollHintProps extends React.HTMLAttributes<HTMLDivElement> {
3
+ visible?: boolean;
4
+ indicatorClassName?: string;
5
+ iconClassName?: string;
6
+ }
7
+ declare function ScrollHint({ visible, className, indicatorClassName, iconClassName, ...props }: ScrollHintProps): import("react/jsx-runtime").JSX.Element;
8
+ export { ScrollHint };
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { ChevronDown } from "lucide-react";
3
+ import { cn } from "./lib/utils";
4
+ function ScrollHint({ visible = true, className, indicatorClassName, iconClassName, ...props }) {
5
+ return (_jsx("div", { "aria-hidden": "true", "data-slot": "scroll-hint", className: cn("pointer-events-none absolute inset-x-0 bottom-2 z-10 flex justify-center transition-opacity duration-150 motion-reduce:transition-none", visible ? "opacity-100" : "opacity-0", className), ...props, children: _jsx("div", { "data-slot": "scroll-hint-indicator", className: cn("shadow-overlay-strong flex h-8 w-8 items-center justify-center rounded-full border border-border/80 bg-popover/95 text-muted-foreground ring-1 ring-border/40 backdrop-blur-sm", indicatorClassName), children: _jsx(ChevronDown, { "aria-hidden": "true", className: cn("h-4 w-4 stroke-[2.25]", iconClassName) }) }) }));
6
+ }
7
+ export { ScrollHint };
@@ -4,6 +4,6 @@ function SearchInputIcon({ className, size = 16, }) {
4
4
  return (_jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: className, style: { width: size, height: size }, "aria-hidden": "true", children: [_jsx("circle", { cx: "11", cy: "11", r: "8" }), _jsx("path", { d: "m21 21-4.3-4.3" })] }));
5
5
  }
6
6
  function SearchInputShell({ children, className, iconClassName, iconSize = 14, ...props }) {
7
- return (_jsxs("div", { className: cn("relative w-full", className), ...props, children: [_jsx(SearchInputIcon, { className: cn("pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", iconClassName), size: iconSize }), children] }));
7
+ return (_jsxs("div", { className: cn("relative w-full", className), ...props, children: [_jsx(SearchInputIcon, { className: cn("pointer-events-none absolute left-2.5 top-1/2 -translate-y-1/2 text-muted-foreground", iconClassName), size: iconSize }), children] }));
8
8
  }
9
9
  export { SearchInputShell };
@@ -64,10 +64,10 @@ const SearchInput = React.forwardRef(({ value, onValueChange, label = "Search",
64
64
  }
65
65
  onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event);
66
66
  }, [onKeyDown, runSearch, searchOnEnter]);
67
- return (_jsxs(Field, { className: containerClassName, "data-disabled": disabled ? "true" : undefined, "data-invalid": isInvalid ? "true" : undefined, children: [_jsx(FieldLabel, { htmlFor: inputId, className: cn("sr-only", labelClassName), children: label }), _jsxs(SearchInputShell, { children: [_jsx(InputControl, { ref: ref, id: inputId, type: "text", placeholder: placeholder, value: value, onChange: handleChange, onKeyDown: handleKeyDown, disabled: disabled, "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": describedBy, className: cn("bg-background pl-9 pr-8", className), ...props }), value ? (_jsx("button", { type: "button", onClick: () => {
67
+ return (_jsxs(Field, { className: containerClassName, "data-disabled": disabled ? "true" : undefined, "data-invalid": isInvalid ? "true" : undefined, children: [_jsx(FieldLabel, { htmlFor: inputId, className: cn("sr-only", labelClassName), children: label }), _jsxs(SearchInputShell, { children: [_jsx(InputControl, { ref: ref, id: inputId, type: "text", placeholder: placeholder, value: value, onChange: handleChange, onKeyDown: handleKeyDown, disabled: disabled, "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": describedBy, className: cn("bg-background pl-8 pr-7", className), ...props }), value ? (_jsx("button", { type: "button", onClick: () => {
68
68
  onValueChange("");
69
69
  runSearch("");
70
- }, disabled: disabled, className: "absolute right-2 top-1/2 -translate-y-1/2 cursor-pointer rounded-sm p-0.5 text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:pointer-events-none", "aria-label": "Clear search", children: _jsx(XIcon, { size: 14 }) })) : null] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
70
+ }, disabled: disabled, className: "absolute right-1.5 top-1/2 -translate-y-1/2 cursor-pointer rounded-sm p-0.5 text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:pointer-events-none", "aria-label": "Clear search", children: _jsx(XIcon, { size: 14 }) })) : null] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
71
71
  });
72
72
  SearchInput.displayName = "SearchInput";
73
73
  export { SearchInput };
@@ -27,13 +27,13 @@ function renderSectionNavItemContent({ item, active, surface, hasChildren, }) {
27
27
  const SectionNavRow = React.forwardRef(({ item, active, surface, hasChildren = false, onSelect, ariaExpanded, className: triggerClassName, onClick: externalOnClick, ...buttonProps }, ref) => {
28
28
  const isLink = Boolean(item.href) && !item.disabled && !hasChildren;
29
29
  const className = cn(surface === "list"
30
- ? "w-full justify-start gap-3 rounded-lg px-3 text-sm font-medium shadow-none"
31
- : "h-auto min-h-control w-full justify-start gap-3 rounded-sm px-2 py-2 text-left text-sm font-normal shadow-none whitespace-normal", active && !item.disabled
30
+ ? "w-full justify-start gap-2 rounded-control px-2.5 text-sm font-medium shadow-none"
31
+ : "h-auto min-h-8 w-full justify-start gap-2 rounded-sm px-2 py-1.5 text-left text-sm font-normal shadow-none whitespace-normal", active && !item.disabled
32
32
  ? surface === "list"
33
- ? "bg-white hover:bg-white"
33
+ ? "bg-background hover:bg-background"
34
34
  : "bg-accent text-accent-foreground hover:bg-accent hover:text-accent-foreground"
35
35
  : surface === "list"
36
- ? "hover:bg-white"
36
+ ? "hover:bg-background"
37
37
  : "hover:bg-accent hover:text-accent-foreground");
38
38
  const content = renderSectionNavItemContent({
39
39
  item,
@@ -14,20 +14,20 @@ function SelectGroup(props) {
14
14
  function SelectValue(props) {
15
15
  return _jsx(SelectPrimitive.Value, { "data-slot": "select-value", ...props });
16
16
  }
17
- const SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => (_jsxs(SelectPrimitive.Trigger, { ref: ref, "data-slot": "select-trigger", className: cn("flex h-control w-full cursor-pointer items-center justify-between gap-2 whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-[invalid=true]:border-destructive aria-[invalid=true]:ring-destructive/20 data-[placeholder]:text-muted-foreground [&>span]:line-clamp-1 [&_svg]:pointer-events-none [&_svg]:shrink-0", className), ...props, children: [children, _jsx(SelectPrimitive.Icon, { asChild: true, children: _jsx(ChevronDown, { className: "h-4 w-4 shrink-0 opacity-50" }) })] })));
17
+ const SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => (_jsxs(SelectPrimitive.Trigger, { ref: ref, "data-slot": "select-trigger", className: cn("flex h-control w-full cursor-pointer items-center justify-between gap-1.5 whitespace-nowrap rounded-control border border-input bg-background px-2.5 py-1.5 text-sm transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-[invalid=true]:border-destructive aria-[invalid=true]:ring-destructive/20 data-[placeholder]:text-muted-foreground [&>span]:line-clamp-1 [&_svg]:pointer-events-none [&_svg]:shrink-0", className), ...props, children: [children, _jsx(SelectPrimitive.Icon, { asChild: true, children: _jsx(ChevronDown, { className: "h-4 w-4 shrink-0 opacity-50" }) })] })));
18
18
  SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
19
19
  const SelectScrollUpButton = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.ScrollUpButton, { ref: ref, "data-slot": "select-scroll-up-button", className: cn("flex cursor-pointer items-center justify-center py-1", className), ...props, children: _jsx(ChevronUp, { className: "h-4 w-4" }) })));
20
20
  SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
21
21
  const SelectScrollDownButton = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.ScrollDownButton, { ref: ref, "data-slot": "select-scroll-down-button", className: cn("flex cursor-pointer items-center justify-center py-1", className), ...props, children: _jsx(ChevronDown, { className: "h-4 w-4" }) })));
22
22
  SelectScrollDownButton.displayName =
23
23
  SelectPrimitive.ScrollDownButton.displayName;
24
- const SelectContent = React.forwardRef(({ className, children, header, position = "popper", viewportClassName, ...props }, ref) => (_jsx(SelectPrimitive.Portal, { children: _jsxs(SelectPrimitive.Content, { ref: ref, "data-slot": "select-content", className: cn("relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", position === "popper" &&
24
+ const SelectContent = React.forwardRef(({ className, children, header, position = "popper", viewportClassName, ...props }, ref) => (_jsx(SelectPrimitive.Portal, { children: _jsxs(SelectPrimitive.Content, { ref: ref, "data-slot": "select-content", className: cn("relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-overlay border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", position === "popper" &&
25
25
  "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className), position: position, ...props, children: [header ? _jsx("div", { className: "border-b p-1", children: header }) : null, _jsx(SelectScrollUpButton, {}), _jsx(SelectPrimitive.Viewport, { className: cn("p-1", position === "popper" &&
26
26
  "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]", viewportClassName), children: children }), _jsx(SelectScrollDownButton, {})] }) })));
27
27
  SelectContent.displayName = SelectPrimitive.Content.displayName;
28
28
  const SelectLabel = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.Label, { ref: ref, "data-slot": "select-label", className: cn("px-2 py-1.5 text-xs text-muted-foreground", className), ...props })));
29
29
  SelectLabel.displayName = SelectPrimitive.Label.displayName;
30
- const SelectItem = React.forwardRef(({ className, children, description, ...props }, ref) => (_jsxs(SelectPrimitive.Item, { ref: ref, "data-slot": "select-item", className: cn("relative flex min-h-control w-full cursor-pointer select-none items-center gap-2 rounded-sm py-2 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className), ...props, children: [_jsx("span", { "data-slot": "select-item-indicator", className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: _jsx(SelectPrimitive.ItemIndicator, { children: _jsx(Check, { className: "h-4 w-4" }) }) }), _jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx(SelectPrimitive.ItemText, { className: "break-words font-normal", children: children }), description ? (_jsx(Typography, { as: "span", variant: "caption", children: description })) : null] })] })));
30
+ const SelectItem = React.forwardRef(({ className, children, description, ...props }, ref) => (_jsxs(SelectPrimitive.Item, { ref: ref, "data-slot": "select-item", className: cn("relative flex min-h-8 w-full cursor-pointer select-none items-center gap-1.5 rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className), ...props, children: [_jsx("span", { "data-slot": "select-item-indicator", className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: _jsx(SelectPrimitive.ItemIndicator, { children: _jsx(Check, { className: "h-4 w-4" }) }) }), _jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx(SelectPrimitive.ItemText, { className: "break-words font-normal", children: children }), description ? (_jsx(Typography, { as: "span", variant: "caption", children: description })) : null] })] })));
31
31
  SelectItem.displayName = SelectPrimitive.Item.displayName;
32
32
  const SelectSeparator = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.Separator, { ref: ref, "data-slot": "select-separator", className: cn("-mx-1 my-1 h-px bg-border", className), ...props })));
33
33
  SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
package/dist/select.js CHANGED
@@ -115,6 +115,6 @@ function Select({ label, options, placeholder = "Select an option", description,
115
115
  } })) : undefined, viewportClassName: searchable ? "max-h-64" : undefined, children: hasVisibleOptions ? (optionGroups.map((group, groupIndex) => {
116
116
  var _a;
117
117
  return (_jsxs(React.Fragment, { children: [_jsxs(SelectGroup, { children: [group.group ? (_jsx(SelectLabel, { children: _jsx(SearchHighlight, { text: group.group, query: searchQuery }) })) : null, group.options.map((option) => (_jsx(SelectItem, { value: option.value, disabled: option.disabled, textValue: getOptionTextValue(option), description: option.description, children: renderHighlightedLabel(option.label, searchQuery) }, option.value)))] }), groupIndex < optionGroups.length - 1 ? _jsx(SelectSeparator, {}) : null] }, (_a = group.group) !== null && _a !== void 0 ? _a : `group-${groupIndex}`));
118
- })) : (_jsx("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: searchEmptyMessage })) })] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
118
+ })) : (_jsx("div", { className: "px-2.5 py-1.5 text-sm text-muted-foreground", children: searchEmptyMessage })) })] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
119
119
  }
120
120
  export { Select };
package/dist/skeleton.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { cn } from "./lib/utils";
3
3
  function Skeleton({ className, ...props }) {
4
- return (_jsx("div", { "aria-hidden": "true", className: cn("animate-pulse rounded-md bg-muted/80", className), ...props }));
4
+ return (_jsx("div", { "aria-hidden": "true", className: cn("animate-pulse rounded-control bg-muted/80", className), ...props }));
5
5
  }
6
6
  export { Skeleton };