@widgetic/design-system 0.5.22 → 0.5.24

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 (258) hide show
  1. package/README.md +2 -0
  2. package/dist/components/CopyableHeader.svelte +260 -448
  3. package/dist/components/CopyableHeader.svelte.d.ts +8 -5
  4. package/dist/components/EmptyState.svelte +100 -128
  5. package/dist/components/EmptyState.svelte.d.ts +16 -19
  6. package/dist/components/GenerateLoader.svelte +17 -10
  7. package/dist/components/PasswordInput.svelte +41 -44
  8. package/dist/components/PasswordInput.svelte.d.ts +2 -0
  9. package/dist/components/ThumbHashImage.svelte +117 -119
  10. package/dist/components/WScrollArea.svelte +47 -41
  11. package/dist/components/chat/ActivityRow.svelte +115 -0
  12. package/dist/components/chat/ActivityRow.svelte.d.ts +18 -0
  13. package/dist/components/chat/ChatHeader.svelte +114 -0
  14. package/dist/components/chat/ChatHeader.svelte.d.ts +26 -0
  15. package/dist/components/chat/Composer.svelte +172 -0
  16. package/dist/components/chat/Composer.svelte.d.ts +27 -0
  17. package/dist/components/chat/Message.svelte +158 -0
  18. package/dist/components/chat/Message.svelte.d.ts +28 -0
  19. package/dist/components/chat/OptionRow.svelte +59 -0
  20. package/dist/components/chat/OptionRow.svelte.d.ts +14 -0
  21. package/dist/components/chat/PlanCard.svelte +220 -0
  22. package/dist/components/chat/PlanCard.svelte.d.ts +34 -0
  23. package/dist/components/chat/QuestionCard.svelte +265 -0
  24. package/dist/components/chat/QuestionCard.svelte.d.ts +38 -0
  25. package/dist/components/chat/ScrollToBottom.svelte +51 -0
  26. package/dist/components/chat/ScrollToBottom.svelte.d.ts +11 -0
  27. package/dist/components/chat/SuggestionCard.svelte +79 -0
  28. package/dist/components/chat/SuggestionCard.svelte.d.ts +20 -0
  29. package/dist/components/chat/TaskRow.svelte +59 -0
  30. package/dist/components/chat/TaskRow.svelte.d.ts +11 -0
  31. package/dist/components/chat/composerLogic.d.ts +16 -0
  32. package/dist/components/chat/composerLogic.js +29 -0
  33. package/dist/components/chat/index.d.ts +16 -0
  34. package/dist/components/chat/index.js +16 -0
  35. package/dist/components/chat/questionCardLogic.d.ts +11 -0
  36. package/dist/components/chat/questionCardLogic.js +39 -0
  37. package/dist/components/index.d.ts +10 -1
  38. package/dist/components/index.js +10 -0
  39. package/dist/components/modals/ActionModal.svelte +186 -180
  40. package/dist/components/modals/BaseModal.svelte +495 -401
  41. package/dist/components/modals/BaseModal.svelte.d.ts +4 -0
  42. package/dist/components/modals/NewItemDialog.svelte +198 -243
  43. package/dist/components/modals/Portal.svelte +109 -105
  44. package/dist/components/modals/README.md +16 -2
  45. package/dist/components/modals/RenameDialog.svelte +232 -292
  46. package/dist/components/modals/modalStore.d.ts +9 -1
  47. package/dist/components/modals/modalStore.js +44 -11
  48. package/dist/components/navigation/Breadcrumbs.svelte +421 -377
  49. package/dist/components/navigation/Breadcrumbs.svelte.d.ts +3 -2
  50. package/dist/components/navigation/breadcrumbsAvailableWidth.d.ts +10 -0
  51. package/dist/components/navigation/breadcrumbsAvailableWidth.js +10 -0
  52. package/dist/components/navigation/types.d.ts +2 -0
  53. package/dist/components/pdf/PDFViewer.svelte +1 -1
  54. package/dist/components/search-input/SearchInput.svelte +63 -104
  55. package/dist/components/search-input/SearchInput.svelte.d.ts +13 -27
  56. package/dist/components/search-input/index2.d.ts +14 -9
  57. package/dist/components/shad-editor/editor.css +1 -1
  58. package/dist/components/shad-editor/i18n/messages.d.ts +1 -1
  59. package/dist/components/shad-editor/i18n/messages.js +1 -1
  60. package/dist/components/shell/AppShell.svelte +52 -0
  61. package/dist/components/shell/AppShell.svelte.d.ts +13 -0
  62. package/dist/components/shell/Banner.svelte +90 -0
  63. package/dist/components/shell/Banner.svelte.d.ts +61 -0
  64. package/dist/components/shell/ErrorState.svelte +60 -0
  65. package/dist/components/shell/ErrorState.svelte.d.ts +13 -0
  66. package/dist/components/shell/Link.svelte +63 -0
  67. package/dist/components/shell/Link.svelte.d.ts +53 -0
  68. package/dist/components/shell/NavDrawer.svelte +117 -0
  69. package/dist/components/shell/NavDrawer.svelte.d.ts +10 -0
  70. package/dist/components/shell/NavRow.svelte +128 -0
  71. package/dist/components/shell/NavRow.svelte.d.ts +65 -0
  72. package/dist/components/shell/NavSectionLabel.svelte +27 -0
  73. package/dist/components/shell/NavSectionLabel.svelte.d.ts +9 -0
  74. package/dist/components/shell/NotificationRow.svelte +76 -0
  75. package/dist/components/shell/NotificationRow.svelte.d.ts +16 -0
  76. package/dist/components/shell/PageHeader.svelte +42 -0
  77. package/dist/components/shell/PageHeader.svelte.d.ts +12 -0
  78. package/dist/components/shell/ProfileRow.svelte +95 -0
  79. package/dist/components/shell/ProfileRow.svelte.d.ts +54 -0
  80. package/dist/components/shell/SettingsDialog.svelte +163 -0
  81. package/dist/components/shell/SettingsDialog.svelte.d.ts +19 -0
  82. package/dist/components/shell/SettingsNav.svelte +46 -0
  83. package/dist/components/shell/SettingsNav.svelte.d.ts +11 -0
  84. package/dist/components/shell/Sidebar.svelte +82 -0
  85. package/dist/components/shell/Sidebar.svelte.d.ts +25 -0
  86. package/dist/components/shell/Stat.svelte +63 -0
  87. package/dist/components/shell/Stat.svelte.d.ts +58 -0
  88. package/dist/components/shell/TopBar.svelte +37 -0
  89. package/dist/components/shell/TopBar.svelte.d.ts +11 -0
  90. package/dist/components/shell/WidgetRow.svelte +126 -0
  91. package/dist/components/shell/WidgetRow.svelte.d.ts +78 -0
  92. package/dist/components/shell/error-state-messages.d.ts +7 -0
  93. package/dist/components/shell/error-state-messages.js +9 -0
  94. package/dist/components/shell/index.d.ts +22 -0
  95. package/dist/components/shell/index.js +25 -0
  96. package/dist/components/shell/link-external.d.ts +2 -0
  97. package/dist/components/shell/link-external.js +7 -0
  98. package/dist/components/shell/messages.d.ts +7 -0
  99. package/dist/components/shell/messages.js +5 -0
  100. package/dist/components/shell/nested-modal-guard.d.ts +25 -0
  101. package/dist/components/shell/nested-modal-guard.js +33 -0
  102. package/dist/components/shell/settings-nav-types.d.ts +12 -0
  103. package/dist/components/shell/settings-nav-types.js +3 -0
  104. package/dist/components/shell/shell-utils.d.ts +7 -0
  105. package/dist/components/shell/shell-utils.js +27 -0
  106. package/dist/components/shell/stat-tone.d.ts +3 -0
  107. package/dist/components/shell/stat-tone.js +11 -0
  108. package/dist/components/toast/ToasterModal.svelte +102 -168
  109. package/dist/components/toast/ToasterModal.svelte.d.ts +2 -12
  110. package/dist/components/ui/accordion/accordion-content.svelte +3 -2
  111. package/dist/components/ui/accordion/accordion-item.svelte +10 -1
  112. package/dist/components/ui/accordion/accordion-trigger.svelte +4 -3
  113. package/dist/components/ui/alert/alert-description.svelte +5 -1
  114. package/dist/components/ui/alert/alert-title.svelte +2 -1
  115. package/dist/components/ui/alert/index.d.ts +13 -11
  116. package/dist/components/ui/alert/index.js +4 -4
  117. package/dist/components/ui/avatar/avatar-utils.d.ts +2 -0
  118. package/dist/components/ui/avatar/avatar-utils.js +9 -0
  119. package/dist/components/ui/avatar/avatar.svelte +63 -0
  120. package/dist/components/ui/avatar/avatar.svelte.d.ts +80 -0
  121. package/dist/components/ui/avatar/index.d.ts +4 -0
  122. package/dist/components/ui/avatar/index.js +4 -0
  123. package/dist/components/ui/badge/badge.svelte +22 -10
  124. package/dist/components/ui/badge/badge.svelte.d.ts +14 -15
  125. package/dist/components/ui/breadcrumb/breadcrumb-ellipsis.svelte +2 -2
  126. package/dist/components/ui/breadcrumb/breadcrumb-link.svelte +1 -1
  127. package/dist/components/ui/breadcrumb/breadcrumb-page.svelte +1 -1
  128. package/dist/components/ui/breadcrumb/breadcrumb-separator.svelte +2 -2
  129. package/dist/components/ui/button/button-padding.d.ts +9 -0
  130. package/dist/components/ui/button/button-padding.js +15 -0
  131. package/dist/components/ui/button/button.svelte +81 -31
  132. package/dist/components/ui/button/button.svelte.d.ts +48 -29
  133. package/dist/components/ui/calendar/calendar-cell.svelte +1 -1
  134. package/dist/components/ui/calendar/calendar-head-cell.svelte +1 -1
  135. package/dist/components/ui/calendar/calendar-next-button.svelte +2 -2
  136. package/dist/components/ui/calendar/calendar-prev-button.svelte +2 -2
  137. package/dist/components/ui/calendar/calendar.svelte.d.ts +1 -1
  138. package/dist/components/ui/card/card-description.svelte +1 -1
  139. package/dist/components/ui/card/card-title.svelte +1 -1
  140. package/dist/components/ui/card/card.svelte +1 -1
  141. package/dist/components/ui/checkbox/checkbox.svelte +9 -5
  142. package/dist/components/ui/chip/chip.svelte +48 -0
  143. package/dist/components/ui/chip/chip.svelte.d.ts +13 -0
  144. package/dist/components/ui/chip/index.d.ts +2 -0
  145. package/dist/components/ui/chip/index.js +2 -0
  146. package/dist/components/ui/command/command-dialog.svelte +1 -1
  147. package/dist/components/ui/command/command-input.svelte +4 -4
  148. package/dist/components/ui/command/command-item.svelte +1 -1
  149. package/dist/components/ui/command/command-link-item.svelte +1 -1
  150. package/dist/components/ui/command/command.svelte +1 -1
  151. package/dist/components/ui/dialog/dialog-content.svelte +11 -4
  152. package/dist/components/ui/dialog/dialog-description.svelte +1 -1
  153. package/dist/components/ui/dialog/dialog-overlay.svelte +8 -1
  154. package/dist/components/ui/dialog/dialog-title.svelte +1 -1
  155. package/dist/components/ui/drawer/drawer-content.svelte +1 -1
  156. package/dist/components/ui/drawer/drawer-overlay.svelte +1 -1
  157. package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +5 -5
  158. package/dist/components/ui/dropdown-menu/dropdown-menu-content.svelte +1 -1
  159. package/dist/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte +1 -1
  160. package/dist/components/ui/dropdown-menu/dropdown-menu-item.svelte +20 -2
  161. package/dist/components/ui/dropdown-menu/dropdown-menu-item.svelte.d.ts +4 -0
  162. package/dist/components/ui/dropdown-menu/dropdown-menu-label.svelte +5 -1
  163. package/dist/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +3 -3
  164. package/dist/components/ui/dropdown-menu/dropdown-menu-separator.svelte +2 -1
  165. package/dist/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte +1 -1
  166. package/dist/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +3 -3
  167. package/dist/components/ui/field/field-ids.d.ts +7 -0
  168. package/dist/components/ui/field/field-ids.js +15 -0
  169. package/dist/components/ui/field/field.svelte +62 -0
  170. package/dist/components/ui/field/field.svelte.d.ts +21 -0
  171. package/dist/components/ui/field/index.d.ts +3 -0
  172. package/dist/components/ui/field/index.js +3 -0
  173. package/dist/components/ui/hover-card/hover-card-content.svelte +1 -1
  174. package/dist/components/ui/icon-button/icon-button.svelte +72 -0
  175. package/dist/components/ui/icon-button/icon-button.svelte.d.ts +90 -0
  176. package/dist/components/ui/icon-button/index.d.ts +2 -0
  177. package/dist/components/ui/icon-button/index.js +4 -0
  178. package/dist/components/ui/input/index.d.ts +1 -0
  179. package/dist/components/ui/input/input.svelte +11 -4
  180. package/dist/components/ui/input/input.svelte.d.ts +4 -1
  181. package/dist/components/ui/input-otp/input-otp-separator.svelte +2 -2
  182. package/dist/components/ui/input-otp/input-otp-slot.svelte +5 -4
  183. package/dist/components/ui/input-otp/input-otp-slot.svelte.d.ts +1 -1
  184. package/dist/components/ui/input-otp/input-otp.svelte +1 -1
  185. package/dist/components/ui/label/label.svelte +3 -2
  186. package/dist/components/ui/popover/popover-content.svelte +1 -1
  187. package/dist/components/ui/progress/progress.svelte +19 -5
  188. package/dist/components/ui/progress/progress.svelte.d.ts +4 -2
  189. package/dist/components/ui/radio-group/radio-group-item.svelte +4 -4
  190. package/dist/components/ui/range-calendar/range-calendar-head-cell.svelte +1 -1
  191. package/dist/components/ui/range-calendar/range-calendar-next-button.svelte +2 -2
  192. package/dist/components/ui/range-calendar/range-calendar-prev-button.svelte +2 -2
  193. package/dist/components/ui/range-calendar/range-calendar.svelte.d.ts +1 -1
  194. package/dist/components/ui/scroll-area/scroll-area-scrollbar.svelte +4 -1
  195. package/dist/components/ui/scroll-area/scroll-area.svelte +1 -1
  196. package/dist/components/ui/select/select-content.svelte +1 -3
  197. package/dist/components/ui/select/select-item.svelte +5 -4
  198. package/dist/components/ui/select/select-scroll-down-button.svelte +2 -2
  199. package/dist/components/ui/select/select-scroll-up-button.svelte +2 -2
  200. package/dist/components/ui/select/select-trigger.svelte +4 -3
  201. package/dist/components/ui/skeleton/index.d.ts +2 -0
  202. package/dist/components/ui/skeleton/index.js +2 -0
  203. package/dist/components/ui/skeleton/skeleton.svelte +24 -0
  204. package/dist/components/ui/skeleton/skeleton.svelte.d.ts +8 -0
  205. package/dist/components/ui/slider/slider.svelte +5 -10
  206. package/dist/components/ui/sonner/sonner.svelte +19 -13
  207. package/dist/components/ui/stepper-button/index.d.ts +3 -0
  208. package/dist/components/ui/stepper-button/index.js +2 -0
  209. package/dist/components/ui/stepper-button/stepper-button.svelte +37 -0
  210. package/dist/components/ui/stepper-button/stepper-button.svelte.d.ts +9 -0
  211. package/dist/components/ui/switch/switch.svelte +8 -3
  212. package/dist/components/ui/table/table-cell.svelte +1 -1
  213. package/dist/components/ui/table/table-head.svelte +1 -1
  214. package/dist/components/ui/table/table-row.svelte +1 -1
  215. package/dist/components/ui/tabs/tabs-list.svelte +1 -1
  216. package/dist/components/ui/tabs/tabs-trigger.svelte +1 -1
  217. package/dist/components/ui/tag/index.d.ts +2 -0
  218. package/dist/components/ui/tag/index.js +2 -0
  219. package/dist/components/ui/tag/tag.svelte +42 -0
  220. package/dist/components/ui/tag/tag.svelte.d.ts +11 -0
  221. package/dist/components/ui/textarea/textarea.svelte +4 -3
  222. package/dist/components/ui/textarea/textarea.svelte.d.ts +2 -2
  223. package/dist/components/ui/toggle/toggle.svelte +9 -9
  224. package/dist/components/ui/toggle/toggle.svelte.d.ts +14 -21
  225. package/dist/components/ui/tooltip/tooltip-content.svelte +1 -1
  226. package/dist/fonts/Inter/Inter-Black.woff2 +0 -0
  227. package/dist/fonts/Inter/Inter-BlackItalic.woff2 +0 -0
  228. package/dist/fonts/Inter/Inter-Bold.woff2 +0 -0
  229. package/dist/fonts/Inter/Inter-BoldItalic.woff2 +0 -0
  230. package/dist/fonts/Inter/Inter-ExtraBold.woff2 +0 -0
  231. package/dist/fonts/Inter/Inter-ExtraBoldItalic.woff2 +0 -0
  232. package/dist/fonts/Inter/Inter-ExtraLight-BETA.woff2 +0 -0
  233. package/dist/fonts/Inter/Inter-ExtraLightItalic-BETA.woff2 +0 -0
  234. package/dist/fonts/Inter/Inter-Italic.woff2 +0 -0
  235. package/dist/fonts/Inter/Inter-Light-BETA.woff2 +0 -0
  236. package/dist/fonts/Inter/Inter-LightItalic-BETA.woff2 +0 -0
  237. package/dist/fonts/Inter/Inter-Medium.woff2 +0 -0
  238. package/dist/fonts/Inter/Inter-MediumItalic.woff2 +0 -0
  239. package/dist/fonts/Inter/Inter-Regular.woff2 +0 -0
  240. package/dist/fonts/Inter/Inter-SemiBold.woff2 +0 -0
  241. package/dist/fonts/Inter/Inter-SemiBoldItalic.woff2 +0 -0
  242. package/dist/fonts/Inter/Inter-Thin-BETA.woff2 +0 -0
  243. package/dist/fonts/Inter/Inter-ThinItalic-BETA.woff2 +0 -0
  244. package/dist/fonts/Inter/Inter-italic.var.woff2 +0 -0
  245. package/dist/fonts/Inter/Inter-upright.var.woff2 +0 -0
  246. package/dist/fonts/Inter/Inter.var.woff2 +0 -0
  247. package/dist/fonts/JetBrainsMono/JetBrainsMono-Regular.woff2 +0 -0
  248. package/dist/i18n/audit/designSystemWords.d.ts +1 -1
  249. package/dist/i18n/audit/designSystemWords.js +1 -1
  250. package/dist/styles/fonts.css +18 -14
  251. package/dist/styles/global.css +10 -21
  252. package/dist/styles/tokens.css +332 -0
  253. package/dist/utils/overlayMotion.d.ts +49 -0
  254. package/dist/utils/overlayMotion.js +71 -0
  255. package/dist/utils.d.ts +27 -0
  256. package/dist/utils.js +33 -2
  257. package/package.json +2 -1
  258. package/tailwind.config.ts +7 -47
@@ -28,8 +28,12 @@ declare const BaseModal: $$__sveltets_2_IsomorphicComponent<{
28
28
  shown?: boolean;
29
29
  animateSize?: boolean;
30
30
  closeLabel?: string;
31
+ bare?: boolean;
32
+ /** When false, clicks and taps on the scrim do not close (destructive confirms can set this). Default matches Figma: scrim dismisses. */ closeOnBackdropClick?: boolean;
33
+ /** When false, Escape does not close. Default: Escape dismisses unless a nested control already handled the key. */ closeOnEscape?: boolean;
31
34
  children: any;
32
35
  }, {
36
+ pointerdown: PointerEvent;
33
37
  click: PointerEvent;
34
38
  restore: CustomEvent<any>;
35
39
  spaceKey: CustomEvent<any>;
@@ -1,248 +1,203 @@
1
1
  <script lang="ts">
2
- // Svelte imports
3
- import { createEventDispatcher } from 'svelte';
4
- const dispatch = createEventDispatcher();
5
-
6
- // Design system imports
7
- import { Button } from '../ui/button';
8
- import { Input } from '../ui/input';
9
- import { toast } from '../ui/sonner';
10
- // Import BaseModal as a default import using relative path, similar to RenameDialog
11
- import BaseModal from './BaseModal.svelte';
12
-
13
- // Words of the dialog and the helper that puts values into them
14
- import type { NewItemDialogMessages } from './messages';
15
- import { fillMessage } from '../../utils/fillMessage';
16
-
17
- // What a name may be made of: letters, numbers and white space of any script (see utils/itemName)
18
- import { findItemNameProblem, isValidItemName, keepAllowedNameCharacters } from '../../utils/itemName';
19
-
20
- // Props
21
- let className = '';
22
- export { className as class };
23
- export let style: string = '';
24
- //
25
- export let isOpen = false;
26
- export let title = ''; // Make title optional
27
- export let showTitle = true; // Whether to show a title at all
28
- export let itemType = "item"; // "folder", "file", "project", etc.
29
- export let placeholder = `${itemType} name...`;
30
- export let target: string = 'new-item-dialog'; // target for Portal
31
- export let currentPath: string[] = [];
32
- export let currentPathNames: string[] = [];
33
- export let confirmLabel = "Create";
34
- export let cancelLabel = "Cancel";
35
- // The words a screen reader says for the close button (see BaseModal)
36
- export let closeLabel = "Close modal";
37
- // The words of the validation errors; a key that is left out keeps its English text (see ./messages)
38
- export let messages: Partial<NewItemDialogMessages> = {};
39
- export let modalType: string = "newItem";
40
- export let modalWidth = "410px";
41
- export let modalHeight = "168px";
42
- export let maxLength = 200;
43
- export let modalData: Record<string, any> = {};
44
-
45
- // Local state
46
- let itemName = '';
47
- let isValid = false;
48
- let hasAttemptedSubmit = false; // Track if user has attempted to submit
49
- let currentError = ''; // Real-time validation error
50
-
51
- // Compute title based on itemType if not provided
52
- $: displayTitle = title || `New ${itemType.charAt(0).toUpperCase() + itemType.slice(1)}`;
53
-
54
- // The words of the validation errors: the page's own, or English built from the item type ("Folder name is required")
55
- $: itemTypeCapitalized = itemType.charAt(0).toUpperCase() + itemType.slice(1);
56
- $: text = {
57
- invalidTitle: messages.invalidTitle ?? `Invalid ${itemType} name`,
58
- required: messages.required ?? `${itemTypeCapitalized} name is required`,
59
- invalidCharacters:
60
- messages.invalidCharacters ?? `${itemTypeCapitalized} name can only contain letters, numbers, and spaces`,
61
- tooLong: fillMessage(
62
- messages.tooLong ?? `${itemTypeCapitalized} name cannot exceed {max} characters`,
63
- { max: maxLength }
64
- )
65
- };
66
-
67
- $: if (isOpen) {
68
- itemName = '';
69
- hasAttemptedSubmit = false;
70
- currentError = '';
71
- }
72
-
73
- $: {
74
- const trimmedName = itemName.trim();
75
- isValid = trimmedName.length > 0 && itemName.length <= maxLength && isValidItemName(trimmedName);
76
-
77
- // Update real-time error message
78
- if (itemName.length > 0) {
79
- if (itemName.length > maxLength) {
80
- currentError = text.tooLong;
81
- } else {
82
- const nameProblem = findItemNameProblem(trimmedName);
83
- currentError = nameProblem ? text[nameProblem] : '';
84
- }
85
- } else {
86
- currentError = '';
87
- }
88
- }
89
-
90
- function handleSubmit() {
91
- hasAttemptedSubmit = true;
92
-
93
- // Validate name format
94
- const nameProblem = findItemNameProblem(itemName);
95
- if (nameProblem) {
96
- toast.error(text.invalidTitle, {
97
- description: text[nameProblem]
98
- });
99
- return;
100
- }
101
-
102
- if (itemName.length > maxLength) {
103
- // Show error toast for name too long
104
- toast.error(text.invalidTitle, {
105
- description: text.tooLong
106
- });
107
- return;
108
- }
109
-
110
- // console.log(`Creating ${itemType}:`, { itemName, path: currentPath });
111
- dispatch('create', { itemName: itemName.trim(), path: currentPath });
112
- handleClose();
113
- }
114
-
115
- // Only allow letters, numbers, and spaces (of any script): anything else typed or pasted is dropped
116
- function keepAllowedCharacters(inputElement: HTMLInputElement) {
117
- inputElement.value = keepAllowedNameCharacters(inputElement.value);
118
- itemName = inputElement.value;
119
- }
120
-
121
- function handleClose() {
122
- isOpen = false;
123
- itemName = '';
124
- isValid = false;
125
- hasAttemptedSubmit = false;
126
- currentError = '';
127
- dispatch('cancel');
128
- }
129
-
130
- // Combine the custom modal data with our required path info
131
- $: combinedModalData = {
132
- ...modalData,
133
- currentPath,
134
- currentPathNames
135
- };
2
+ // Svelte imports
3
+ import { createEventDispatcher } from 'svelte';
4
+ const dispatch = createEventDispatcher();
5
+
6
+ // Design system imports
7
+ import { Button } from '../ui/button';
8
+ import { Input } from '../ui/input';
9
+ let inputElement: HTMLInputElement | null = null;
10
+ import { toast } from '../ui/sonner';
11
+ // Import BaseModal as a default import using relative path, similar to RenameDialog
12
+ import BaseModal from './BaseModal.svelte';
13
+
14
+ // Words of the dialog and the helper that puts values into them
15
+ import type { NewItemDialogMessages } from './messages';
16
+ import { fillMessage } from '../../utils/fillMessage';
17
+
18
+ // What a name may be made of: letters, numbers and white space of any script (see utils/itemName)
19
+ import {
20
+ findItemNameProblem,
21
+ isValidItemName,
22
+ keepAllowedNameCharacters
23
+ } from '../../utils/itemName';
24
+
25
+ // Props
26
+ let className = '';
27
+ export { className as class };
28
+ export let style: string = '';
29
+ //
30
+ export let isOpen = false;
31
+ export let title = ''; // Make title optional
32
+ export let showTitle = true; // Whether to show a title at all
33
+ export let itemType = 'item'; // "folder", "file", "project", etc.
34
+ export let placeholder = `${itemType} name…`;
35
+ export let target: string = 'new-item-dialog'; // target for Portal
36
+ export let currentPath: string[] = [];
37
+ export let currentPathNames: string[] = [];
38
+ export let confirmLabel = 'Create';
39
+ export let cancelLabel = 'Cancel';
40
+ // The words a screen reader says for the close button (see BaseModal)
41
+ export let closeLabel = 'Close modal';
42
+ // The words of the validation errors; a key that is left out keeps its English text (see ./messages)
43
+ export let messages: Partial<NewItemDialogMessages> = {};
44
+ export let modalType: string = 'newItem';
45
+ export let modalWidth = '400px';
46
+ export let modalHeight = 'auto';
47
+ export let maxLength = 200;
48
+ export let modalData: Record<string, any> = {};
49
+
50
+ // Local state
51
+ let itemName = '';
52
+ let isValid = false;
53
+ let hasAttemptedSubmit = false; // Track if user has attempted to submit
54
+ let currentError = ''; // Real-time validation error
55
+
56
+ // Compute title based on itemType if not provided
57
+ $: displayTitle = title || `New ${itemType.charAt(0).toUpperCase() + itemType.slice(1)}`;
58
+
59
+ // The words of the validation errors: the page's own, or English built from the item type ("Folder name is required")
60
+ $: itemTypeCapitalized = itemType.charAt(0).toUpperCase() + itemType.slice(1);
61
+ $: text = {
62
+ invalidTitle: messages.invalidTitle ?? `Invalid ${itemType} name`,
63
+ required: messages.required ?? `${itemTypeCapitalized} name is required`,
64
+ invalidCharacters:
65
+ messages.invalidCharacters ??
66
+ `${itemTypeCapitalized} name can only contain letters, numbers, and spaces`,
67
+ tooLong: fillMessage(
68
+ messages.tooLong ?? `${itemTypeCapitalized} name cannot exceed {max} characters`,
69
+ { max: maxLength }
70
+ )
71
+ };
72
+
73
+ $: if (isOpen) {
74
+ itemName = '';
75
+ hasAttemptedSubmit = false;
76
+ currentError = '';
77
+ }
78
+
79
+ $: {
80
+ const trimmedName = itemName.trim();
81
+ isValid =
82
+ trimmedName.length > 0 && itemName.length <= maxLength && isValidItemName(trimmedName);
83
+
84
+ // Update real-time error message
85
+ if (itemName.length > 0) {
86
+ if (itemName.length > maxLength) {
87
+ currentError = text.tooLong;
88
+ } else {
89
+ const nameProblem = findItemNameProblem(trimmedName);
90
+ currentError = nameProblem ? text[nameProblem] : '';
91
+ }
92
+ } else {
93
+ currentError = '';
94
+ }
95
+ }
96
+
97
+ function handleSubmit() {
98
+ hasAttemptedSubmit = true;
99
+
100
+ // Validate name format
101
+ const nameProblem = findItemNameProblem(itemName);
102
+ if (nameProblem) {
103
+ toast.error(text.invalidTitle, {
104
+ description: text[nameProblem]
105
+ });
106
+ return;
107
+ }
108
+
109
+ if (itemName.length > maxLength) {
110
+ // Show error toast for name too long
111
+ toast.error(text.invalidTitle, {
112
+ description: text.tooLong
113
+ });
114
+ return;
115
+ }
116
+
117
+ // console.log(`Creating ${itemType}:`, { itemName, path: currentPath });
118
+ dispatch('create', { itemName: itemName.trim(), path: currentPath });
119
+ handleClose();
120
+ }
121
+
122
+ // Only allow letters, numbers, and spaces (of any script): anything else typed or pasted is dropped
123
+ function keepAllowedCharacters(inputElement: HTMLInputElement) {
124
+ inputElement.value = keepAllowedNameCharacters(inputElement.value);
125
+ itemName = inputElement.value;
126
+ }
127
+
128
+ function handleClose() {
129
+ isOpen = false;
130
+ itemName = '';
131
+ isValid = false;
132
+ hasAttemptedSubmit = false;
133
+ currentError = '';
134
+ dispatch('cancel');
135
+ }
136
+
137
+ // Combine the custom modal data with our required path info
138
+ $: combinedModalData = {
139
+ ...modalData,
140
+ currentPath,
141
+ currentPathNames
142
+ };
136
143
  </script>
137
144
 
138
- <BaseModal
139
- bind:open={isOpen}
140
- on:close={handleClose}
141
- width={modalWidth}
142
- height={modalHeight}
143
- {modalType}
144
- modalData={combinedModalData}
145
- title={showTitle ? displayTitle : undefined}
146
- {closeLabel}
147
- {target}
148
- on:restore={(event: CustomEvent<any>) => {
149
- if (event.detail) {
150
- if (event.detail.currentPath) currentPath = event.detail.currentPath;
151
- if (event.detail.currentPathNames) currentPathNames = event.detail.currentPathNames;
152
- }
153
- }}
145
+ <BaseModal
146
+ bind:open={isOpen}
147
+ on:close={handleClose}
148
+ width={modalWidth}
149
+ height={modalHeight}
150
+ {modalType}
151
+ modalData={combinedModalData}
152
+ title={showTitle ? displayTitle : undefined}
153
+ {closeLabel}
154
+ {target}
155
+ on:restore={(event: CustomEvent<any>) => {
156
+ if (event.detail) {
157
+ if (event.detail.currentPath) currentPath = event.detail.currentPath;
158
+ if (event.detail.currentPathNames) currentPathNames = event.detail.currentPathNames;
159
+ }
160
+ }}
154
161
  >
155
- <div class="{className ? `${className} ` : ''}new-item-dialog-container h-full flex flex-col min-h-[136px] justify-between" style={style}>
156
- <div class="flex flex-col items-center pt-6">
157
- <!-- Replace Input component with native input to match RenameDialog styling -->
158
- <input
159
- type="text"
160
- bind:value={itemName}
161
- onkeydown={(e) => {
162
- if (e.key === 'Escape') {
163
- handleClose();
164
- } else {
165
- e.stopPropagation(); // Stop all keyboard events except Escape from bubbling up
166
- if (e.key === 'Enter') {
167
- e.preventDefault();
168
- handleSubmit();
169
- }
170
- }
171
- }}
172
- oninput={(e) => {
173
- const target = e.target as HTMLInputElement;
174
- // An input method (Japanese, Chinese, Korean...) is still building a word: changing the field now would
175
- // cancel the word. It is checked when the word is committed, in oncompositionend.
176
- if ('isComposing' in e && e.isComposing) {
177
- itemName = target.value;
178
- return;
179
- }
180
- keepAllowedCharacters(target);
181
- }}
182
- oncompositionend={(e) => keepAllowedCharacters(e.target as HTMLInputElement)}
183
- onfocus={(e) => {
184
- const target = e.target as HTMLInputElement;
185
- target.placeholder = "";
186
- }}
187
- onblur={(e) => {
188
- const target = e.target as HTMLInputElement;
189
- target.placeholder = placeholder;
190
- }}
191
- required
192
- class="w-full h5 text-[24px] text-[hsl(var(--grey7))] text-center
193
- bg-transparent border-none focus:outline-hidden focus:ring-0
194
- placeholder:text-[24px] placeholder:text-[hsl(var(--grey3))]
195
- {currentError ? 'input-error' : ''}"
196
- placeholder={placeholder}
197
- maxlength={maxLength}
198
- data-min-length="1"
199
- />
200
- {#if currentError}
201
- <div class="text-xs text-[hsl(var(--error))] mt-1 text-center">
202
- {currentError}
203
- </div>
204
- {/if}
205
- </div>
206
-
207
- <div class="buttons-container flex justify-center gap-4">
208
- <Button
209
- type="button"
210
- variant="secondary"
211
- class="modal-btn-secondary !rounded-[100px] !h-[34px]"
212
- onclick={handleClose}
213
- >
214
- {cancelLabel}
215
- </Button>
216
- <Button
217
- type="submit"
218
- class="modal-btn-primary !rounded-[100px] !h-[34px]"
219
- onclick={handleSubmit}
220
- disabled={!isValid}
221
- >
222
- {confirmLabel}
223
- </Button>
224
- </div>
225
- </div>
162
+ <div
163
+ class="{className ? `${className} ` : ''}new-item-dialog-container flex flex-col gap-4"
164
+ {style}
165
+ >
166
+ <div class="flex flex-col gap-1.5">
167
+ <Input
168
+ bind:ref={inputElement}
169
+ bind:value={itemName}
170
+ {placeholder}
171
+ maxlength={maxLength}
172
+ aria-invalid={Boolean(currentError)}
173
+ class={currentError ? 'border-destructive' : ''}
174
+ onkeydown={(e) => {
175
+ if (e.key !== 'Escape') {
176
+ e.stopPropagation();
177
+ }
178
+ if (e.key === 'Enter') {
179
+ e.preventDefault();
180
+ handleSubmit();
181
+ }
182
+ }}
183
+ oninput={(e) => {
184
+ const target = e.currentTarget;
185
+ if ('isComposing' in e && e.isComposing) {
186
+ itemName = target.value;
187
+ return;
188
+ }
189
+ keepAllowedCharacters(target);
190
+ }}
191
+ oncompositionend={(e) => keepAllowedCharacters(e.currentTarget)}
192
+ />
193
+ {#if currentError}
194
+ <p class="text-caption text-destructive">{currentError}</p>
195
+ {/if}
196
+ </div>
197
+
198
+ <div class="flex justify-end gap-2">
199
+ <Button type="button" variant="outline" onclick={handleClose}>{cancelLabel}</Button>
200
+ <Button type="submit" onclick={handleSubmit} disabled={!isValid}>{confirmLabel}</Button>
201
+ </div>
202
+ </div>
226
203
  </BaseModal>
227
-
228
- <style>
229
- @keyframes pulse-subtle {
230
- 0%, 100% { opacity: 0; }
231
- 50% { opacity: 0.1; }
232
- }
233
-
234
- :global(.animate-pulse-subtle) {
235
- animation: pulse-subtle 2s ease-in-out infinite;
236
- }
237
-
238
- :global(.input-error) {
239
- animation: shake 0.4s ease-in-out;
240
- border-bottom: 1px solid hsl(var(--error)) !important;
241
- }
242
-
243
- @keyframes shake {
244
- 0%, 100% { transform: translateX(0); }
245
- 25% { transform: translateX(-5px); }
246
- 75% { transform: translateX(5px); }
247
- }
248
- </style>