@widgetic/design-system 0.5.22 → 0.5.23

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 (246) 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 +402 -399
  41. package/dist/components/modals/BaseModal.svelte.d.ts +1 -0
  42. package/dist/components/modals/NewItemDialog.svelte +200 -243
  43. package/dist/components/modals/Portal.svelte +107 -105
  44. package/dist/components/modals/README.md +16 -2
  45. package/dist/components/modals/RenameDialog.svelte +234 -292
  46. package/dist/components/navigation/Breadcrumbs.svelte +415 -377
  47. package/dist/components/navigation/Breadcrumbs.svelte.d.ts +3 -2
  48. package/dist/components/navigation/types.d.ts +2 -0
  49. package/dist/components/pdf/PDFViewer.svelte +1 -1
  50. package/dist/components/search-input/SearchInput.svelte +51 -104
  51. package/dist/components/search-input/SearchInput.svelte.d.ts +9 -27
  52. package/dist/components/shad-editor/editor.css +1 -1
  53. package/dist/components/shad-editor/i18n/messages.d.ts +1 -1
  54. package/dist/components/shad-editor/i18n/messages.js +1 -1
  55. package/dist/components/shell/AppShell.svelte +52 -0
  56. package/dist/components/shell/AppShell.svelte.d.ts +13 -0
  57. package/dist/components/shell/Banner.svelte +90 -0
  58. package/dist/components/shell/Banner.svelte.d.ts +61 -0
  59. package/dist/components/shell/ErrorState.svelte +60 -0
  60. package/dist/components/shell/ErrorState.svelte.d.ts +13 -0
  61. package/dist/components/shell/Link.svelte +63 -0
  62. package/dist/components/shell/Link.svelte.d.ts +53 -0
  63. package/dist/components/shell/NavDrawer.svelte +117 -0
  64. package/dist/components/shell/NavDrawer.svelte.d.ts +10 -0
  65. package/dist/components/shell/NavRow.svelte +128 -0
  66. package/dist/components/shell/NavRow.svelte.d.ts +65 -0
  67. package/dist/components/shell/NavSectionLabel.svelte +27 -0
  68. package/dist/components/shell/NavSectionLabel.svelte.d.ts +9 -0
  69. package/dist/components/shell/NotificationRow.svelte +76 -0
  70. package/dist/components/shell/NotificationRow.svelte.d.ts +16 -0
  71. package/dist/components/shell/PageHeader.svelte +42 -0
  72. package/dist/components/shell/PageHeader.svelte.d.ts +12 -0
  73. package/dist/components/shell/ProfileRow.svelte +95 -0
  74. package/dist/components/shell/ProfileRow.svelte.d.ts +54 -0
  75. package/dist/components/shell/SettingsDialog.svelte +118 -0
  76. package/dist/components/shell/SettingsDialog.svelte.d.ts +19 -0
  77. package/dist/components/shell/SettingsNav.svelte +46 -0
  78. package/dist/components/shell/SettingsNav.svelte.d.ts +11 -0
  79. package/dist/components/shell/Sidebar.svelte +82 -0
  80. package/dist/components/shell/Sidebar.svelte.d.ts +25 -0
  81. package/dist/components/shell/Stat.svelte +63 -0
  82. package/dist/components/shell/Stat.svelte.d.ts +58 -0
  83. package/dist/components/shell/TopBar.svelte +37 -0
  84. package/dist/components/shell/TopBar.svelte.d.ts +11 -0
  85. package/dist/components/shell/WidgetRow.svelte +126 -0
  86. package/dist/components/shell/WidgetRow.svelte.d.ts +78 -0
  87. package/dist/components/shell/error-state-messages.d.ts +7 -0
  88. package/dist/components/shell/error-state-messages.js +9 -0
  89. package/dist/components/shell/index.d.ts +22 -0
  90. package/dist/components/shell/index.js +25 -0
  91. package/dist/components/shell/link-external.d.ts +2 -0
  92. package/dist/components/shell/link-external.js +7 -0
  93. package/dist/components/shell/messages.d.ts +7 -0
  94. package/dist/components/shell/messages.js +5 -0
  95. package/dist/components/shell/settings-nav-types.d.ts +12 -0
  96. package/dist/components/shell/settings-nav-types.js +3 -0
  97. package/dist/components/shell/shell-utils.d.ts +7 -0
  98. package/dist/components/shell/shell-utils.js +27 -0
  99. package/dist/components/shell/stat-tone.d.ts +3 -0
  100. package/dist/components/shell/stat-tone.js +11 -0
  101. package/dist/components/toast/ToasterModal.svelte +102 -168
  102. package/dist/components/toast/ToasterModal.svelte.d.ts +2 -12
  103. package/dist/components/ui/accordion/accordion-content.svelte +3 -2
  104. package/dist/components/ui/accordion/accordion-item.svelte +10 -1
  105. package/dist/components/ui/accordion/accordion-trigger.svelte +4 -3
  106. package/dist/components/ui/alert/alert-description.svelte +5 -1
  107. package/dist/components/ui/alert/alert-title.svelte +2 -1
  108. package/dist/components/ui/alert/index.d.ts +13 -11
  109. package/dist/components/ui/alert/index.js +4 -4
  110. package/dist/components/ui/avatar/avatar-utils.d.ts +2 -0
  111. package/dist/components/ui/avatar/avatar-utils.js +9 -0
  112. package/dist/components/ui/avatar/avatar.svelte +63 -0
  113. package/dist/components/ui/avatar/avatar.svelte.d.ts +80 -0
  114. package/dist/components/ui/avatar/index.d.ts +4 -0
  115. package/dist/components/ui/avatar/index.js +4 -0
  116. package/dist/components/ui/badge/badge.svelte +22 -10
  117. package/dist/components/ui/badge/badge.svelte.d.ts +14 -15
  118. package/dist/components/ui/breadcrumb/breadcrumb-ellipsis.svelte +2 -2
  119. package/dist/components/ui/breadcrumb/breadcrumb-link.svelte +1 -1
  120. package/dist/components/ui/breadcrumb/breadcrumb-page.svelte +1 -1
  121. package/dist/components/ui/breadcrumb/breadcrumb-separator.svelte +2 -2
  122. package/dist/components/ui/button/button-padding.d.ts +9 -0
  123. package/dist/components/ui/button/button-padding.js +15 -0
  124. package/dist/components/ui/button/button.svelte +81 -31
  125. package/dist/components/ui/button/button.svelte.d.ts +48 -29
  126. package/dist/components/ui/calendar/calendar-cell.svelte +1 -1
  127. package/dist/components/ui/calendar/calendar-head-cell.svelte +1 -1
  128. package/dist/components/ui/calendar/calendar-next-button.svelte +2 -2
  129. package/dist/components/ui/calendar/calendar-prev-button.svelte +2 -2
  130. package/dist/components/ui/card/card-description.svelte +1 -1
  131. package/dist/components/ui/card/card-title.svelte +1 -1
  132. package/dist/components/ui/card/card.svelte +1 -1
  133. package/dist/components/ui/checkbox/checkbox.svelte +9 -5
  134. package/dist/components/ui/chip/chip.svelte +48 -0
  135. package/dist/components/ui/chip/chip.svelte.d.ts +13 -0
  136. package/dist/components/ui/chip/index.d.ts +2 -0
  137. package/dist/components/ui/chip/index.js +2 -0
  138. package/dist/components/ui/command/command-dialog.svelte +1 -1
  139. package/dist/components/ui/command/command-input.svelte +4 -4
  140. package/dist/components/ui/command/command-item.svelte +1 -1
  141. package/dist/components/ui/command/command.svelte +1 -1
  142. package/dist/components/ui/dialog/dialog-content.svelte +4 -4
  143. package/dist/components/ui/dialog/dialog-description.svelte +1 -1
  144. package/dist/components/ui/dialog/dialog-overlay.svelte +1 -1
  145. package/dist/components/ui/dialog/dialog-title.svelte +1 -1
  146. package/dist/components/ui/drawer/drawer-content.svelte +1 -1
  147. package/dist/components/ui/drawer/drawer-overlay.svelte +1 -1
  148. package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +5 -5
  149. package/dist/components/ui/dropdown-menu/dropdown-menu-content.svelte +1 -1
  150. package/dist/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte +1 -1
  151. package/dist/components/ui/dropdown-menu/dropdown-menu-item.svelte +20 -2
  152. package/dist/components/ui/dropdown-menu/dropdown-menu-item.svelte.d.ts +4 -0
  153. package/dist/components/ui/dropdown-menu/dropdown-menu-label.svelte +5 -1
  154. package/dist/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +3 -3
  155. package/dist/components/ui/dropdown-menu/dropdown-menu-separator.svelte +2 -1
  156. package/dist/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte +1 -1
  157. package/dist/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +3 -3
  158. package/dist/components/ui/field/field-ids.d.ts +7 -0
  159. package/dist/components/ui/field/field-ids.js +15 -0
  160. package/dist/components/ui/field/field.svelte +62 -0
  161. package/dist/components/ui/field/field.svelte.d.ts +21 -0
  162. package/dist/components/ui/field/index.d.ts +3 -0
  163. package/dist/components/ui/field/index.js +3 -0
  164. package/dist/components/ui/hover-card/hover-card-content.svelte +1 -1
  165. package/dist/components/ui/icon-button/icon-button.svelte +72 -0
  166. package/dist/components/ui/icon-button/icon-button.svelte.d.ts +90 -0
  167. package/dist/components/ui/icon-button/index.d.ts +2 -0
  168. package/dist/components/ui/icon-button/index.js +4 -0
  169. package/dist/components/ui/input/index.d.ts +1 -0
  170. package/dist/components/ui/input/input.svelte +11 -4
  171. package/dist/components/ui/input/input.svelte.d.ts +4 -1
  172. package/dist/components/ui/input-otp/input-otp-separator.svelte +2 -2
  173. package/dist/components/ui/input-otp/input-otp-slot.svelte +5 -4
  174. package/dist/components/ui/input-otp/input-otp-slot.svelte.d.ts +1 -1
  175. package/dist/components/ui/input-otp/input-otp.svelte +1 -1
  176. package/dist/components/ui/label/label.svelte +3 -2
  177. package/dist/components/ui/popover/popover-content.svelte +1 -1
  178. package/dist/components/ui/progress/progress.svelte +19 -5
  179. package/dist/components/ui/progress/progress.svelte.d.ts +4 -2
  180. package/dist/components/ui/radio-group/radio-group-item.svelte +4 -4
  181. package/dist/components/ui/range-calendar/range-calendar-head-cell.svelte +1 -1
  182. package/dist/components/ui/range-calendar/range-calendar-next-button.svelte +2 -2
  183. package/dist/components/ui/range-calendar/range-calendar-prev-button.svelte +2 -2
  184. package/dist/components/ui/scroll-area/scroll-area-scrollbar.svelte +4 -1
  185. package/dist/components/ui/scroll-area/scroll-area.svelte +1 -1
  186. package/dist/components/ui/select/select-content.svelte +1 -3
  187. package/dist/components/ui/select/select-item.svelte +5 -4
  188. package/dist/components/ui/select/select-scroll-down-button.svelte +2 -2
  189. package/dist/components/ui/select/select-scroll-up-button.svelte +2 -2
  190. package/dist/components/ui/select/select-trigger.svelte +4 -3
  191. package/dist/components/ui/skeleton/index.d.ts +2 -0
  192. package/dist/components/ui/skeleton/index.js +2 -0
  193. package/dist/components/ui/skeleton/skeleton.svelte +24 -0
  194. package/dist/components/ui/skeleton/skeleton.svelte.d.ts +8 -0
  195. package/dist/components/ui/slider/slider.svelte +5 -10
  196. package/dist/components/ui/sonner/sonner.svelte +19 -13
  197. package/dist/components/ui/stepper-button/index.d.ts +3 -0
  198. package/dist/components/ui/stepper-button/index.js +2 -0
  199. package/dist/components/ui/stepper-button/stepper-button.svelte +37 -0
  200. package/dist/components/ui/stepper-button/stepper-button.svelte.d.ts +9 -0
  201. package/dist/components/ui/switch/switch.svelte +8 -3
  202. package/dist/components/ui/table/table-cell.svelte +1 -1
  203. package/dist/components/ui/table/table-head.svelte +1 -1
  204. package/dist/components/ui/table/table-row.svelte +1 -1
  205. package/dist/components/ui/tabs/tabs-list.svelte +1 -1
  206. package/dist/components/ui/tabs/tabs-trigger.svelte +1 -1
  207. package/dist/components/ui/tag/index.d.ts +2 -0
  208. package/dist/components/ui/tag/index.js +2 -0
  209. package/dist/components/ui/tag/tag.svelte +42 -0
  210. package/dist/components/ui/tag/tag.svelte.d.ts +11 -0
  211. package/dist/components/ui/textarea/textarea.svelte +4 -3
  212. package/dist/components/ui/textarea/textarea.svelte.d.ts +2 -2
  213. package/dist/components/ui/toggle/toggle.svelte +9 -9
  214. package/dist/components/ui/toggle/toggle.svelte.d.ts +14 -21
  215. package/dist/components/ui/tooltip/tooltip-content.svelte +1 -1
  216. package/dist/fonts/Inter/Inter-Black.woff2 +0 -0
  217. package/dist/fonts/Inter/Inter-BlackItalic.woff2 +0 -0
  218. package/dist/fonts/Inter/Inter-Bold.woff2 +0 -0
  219. package/dist/fonts/Inter/Inter-BoldItalic.woff2 +0 -0
  220. package/dist/fonts/Inter/Inter-ExtraBold.woff2 +0 -0
  221. package/dist/fonts/Inter/Inter-ExtraBoldItalic.woff2 +0 -0
  222. package/dist/fonts/Inter/Inter-ExtraLight-BETA.woff2 +0 -0
  223. package/dist/fonts/Inter/Inter-ExtraLightItalic-BETA.woff2 +0 -0
  224. package/dist/fonts/Inter/Inter-Italic.woff2 +0 -0
  225. package/dist/fonts/Inter/Inter-Light-BETA.woff2 +0 -0
  226. package/dist/fonts/Inter/Inter-LightItalic-BETA.woff2 +0 -0
  227. package/dist/fonts/Inter/Inter-Medium.woff2 +0 -0
  228. package/dist/fonts/Inter/Inter-MediumItalic.woff2 +0 -0
  229. package/dist/fonts/Inter/Inter-Regular.woff2 +0 -0
  230. package/dist/fonts/Inter/Inter-SemiBold.woff2 +0 -0
  231. package/dist/fonts/Inter/Inter-SemiBoldItalic.woff2 +0 -0
  232. package/dist/fonts/Inter/Inter-Thin-BETA.woff2 +0 -0
  233. package/dist/fonts/Inter/Inter-ThinItalic-BETA.woff2 +0 -0
  234. package/dist/fonts/Inter/Inter-italic.var.woff2 +0 -0
  235. package/dist/fonts/Inter/Inter-upright.var.woff2 +0 -0
  236. package/dist/fonts/Inter/Inter.var.woff2 +0 -0
  237. package/dist/fonts/JetBrainsMono/JetBrainsMono-Regular.woff2 +0 -0
  238. package/dist/i18n/audit/designSystemWords.d.ts +1 -1
  239. package/dist/i18n/audit/designSystemWords.js +1 -1
  240. package/dist/styles/fonts.css +18 -14
  241. package/dist/styles/global.css +7 -19
  242. package/dist/styles/tokens.css +332 -0
  243. package/dist/utils.d.ts +27 -0
  244. package/dist/utils.js +33 -2
  245. package/package.json +2 -1
  246. package/tailwind.config.ts +7 -47
@@ -2,467 +2,279 @@
2
2
  /**
3
3
  * CopyableHeader Component
4
4
  *
5
- * A reusable header component for displaying content that can be copied to clipboard:
6
- * - One-click copy functionality with visual feedback
7
- * - Optional help icon with tooltip
8
- * - Configurable header text and content
9
- * - Consistent styling with the design system
5
+ * Figma: Embed Code Header (title row, code card, help row — no drag handle).
6
+ * Displays copyable content with icon or text copy control, loading/error card states, optional help link row.
10
7
  *
11
8
  * Usage:
12
9
  * ```svelte
13
10
  * <CopyableHeader
14
11
  * headerText="Embed code"
15
12
  * content="<widgetic id='123' width='350' height='50'></widgetic>"
16
- * helpText="Read more about embedding"
13
+ * helpPrompt="Not sure how embedding works?"
14
+ * helpText="Learn more"
17
15
  * helpUrl="/embed"
18
16
  * />
19
17
  * ```
20
18
  *
21
- * The words of the pop-up that says whether the copy worked are props too (`copiedTitle`, `copiedDescription`,
22
- * `copyFailedTitle`, `copyFailedDescription`), so a page in another language can give them. A page that shows its own pop-up
23
- * on the `copy` and `copyError` events passes `showInternalToast={false}` instead.
19
+ * Toast copy strings are props (`copiedTitle`, `copiedDescription`, …). Pass `showInternalToast={false}` when the host shows toasts on `copy` / `copyError`.
24
20
  */
25
21
  -->
26
22
  <script lang="ts">
27
- /**
28
- * SVELTE IMPORTS
29
- */
30
- import { createEventDispatcher } from 'svelte';
31
-
32
- const dispatch = createEventDispatcher();
33
-
34
- /**
35
- * COMPONENT IMPORTS
36
- */
37
- // import { toast, Button, Tooltip, Portal } from '@widgetic/design-system/components';
38
- import { toast, Button, Tooltip, Portal } from './';
39
- import { IconCopy, IconHelp } from '@tabler/icons-svelte';
40
- import { sideAtEnd, sideAtStart, useTextDirection } from './textDirection.js';
41
-
42
- // The pop-ups open on a side of the screen, and the sides at the start and at the end of the line trade places on a right to left one.
43
- const textDirection = useTextDirection();
44
-
45
- /**
46
- * COMPONENT PROPS
47
- */
48
- let className = '';
49
- export { className as class };
50
- export let style = '';
51
- export let headerText = 'Copy content';
52
- export let content = '';
53
- export let helpText = 'Learn more';
54
- export let helpUrl = '';
55
- export let showHelp = true;
56
- export let monospaceContent = true;
57
- export let showStandaloneCopyButton = false;
58
- export let standaloneCopyButtonText = '';
59
- /** The pop-up after a copy that worked: its title and the line under it. */
60
- export let copiedTitle = 'Copied to clipboard';
61
- export let copiedDescription = 'Content copied successfully.';
62
- /** The pop-up after a copy that failed: its title and the line under it. */
63
- export let copyFailedTitle = 'Copy failed';
64
- export let copyFailedDescription = 'Please try manual selection';
65
- export let showButtonText = true;
66
- export let buttonClass = '';
67
- export let showInternalToast = true;
68
- export let headerTextClass = '';
69
- export let headerBackgroundColor = '';
70
- export let contentBorderColor = '';
71
-
72
- /**
73
- * LOCAL VARIABLES
74
- */
75
- let mouseX = 0;
76
- let mouseY = 0;
77
- let showHelpTooltip = false;
78
- let showCopyTooltip = false;
79
- let tooltipDebounce = false;
80
- let contentElement: HTMLDivElement | null = null;
81
-
82
- /**
83
- * MOUSE EVENTS HANDLERS
84
- */
85
- function handleMouseEnter(event: MouseEvent) {
86
- if (tooltipDebounce) return;
87
- mouseX = event.clientX;
88
- mouseY = event.clientY;
89
- showHelpTooltip = true;
90
- }
91
-
92
- function handleCopyMouseEnter(event: MouseEvent) {
93
- if (tooltipDebounce) return;
94
- mouseX = event.clientX;
95
- mouseY = event.clientY;
96
- showCopyTooltip = true;
97
- }
98
-
99
- function handleMouseMove(event: MouseEvent) {
100
- mouseX = event.clientX;
101
- mouseY = event.clientY;
102
- }
103
-
104
- function handleMouseLeave() {
105
- showHelpTooltip = false;
106
- }
107
-
108
- function handleCopyMouseLeave() {
109
- showCopyTooltip = false;
110
- }
111
-
112
- function setTooltipDebounce() {
113
- tooltipDebounce = true;
114
- setTimeout(() => {
115
- tooltipDebounce = false;
116
- }, 500); // Prevent tooltips from showing for 500ms after clicking
117
- }
118
-
119
- /**
120
- * Clipboard Interaction Handlers
121
- * Provides copy functionality via the copy button
122
- * with visual feedback via toast notifications
123
- */
124
- async function handleCopyButtonClick(e: MouseEvent) {
125
- e.preventDefault();
126
- e.stopPropagation();
127
-
128
- // Immediately hide the tooltip to prevent flickering
129
- showCopyTooltip = false;
130
- setTooltipDebounce();
131
-
132
- // Select all text in content element
133
- if (contentElement) {
134
- const selection = window.getSelection();
135
- const range = document.createRange();
136
- range.selectNodeContents(contentElement);
137
- selection?.removeAllRanges();
138
- selection?.addRange(range);
139
- }
140
-
141
- try {
142
- await navigator.clipboard.writeText(content);
143
- if (showInternalToast) {
144
- showToast('success', copiedTitle, {
145
- description: copiedDescription,
146
- });
147
- }
148
- dispatch('copy', { content });
149
- } catch (err) {
150
- if (showInternalToast) {
151
- showToast('error', copyFailedTitle, {
152
- description: copyFailedDescription,
153
- });
154
- }
155
- dispatch('copyError', { error: err });
156
- }
157
- }
158
-
159
- // Toast helper function
160
- let isToastVisible = false;
161
-
162
- function showToast(type: 'success' | 'error', message: string, options = {}) {
163
- if (isToastVisible) return;
164
-
165
- isToastVisible = true;
166
- toast[type](message, {
167
- ...options,
168
- duration: 5000,
169
- unstyled: true,
170
- classes: {
171
- toast: 'copyable-header-toast flex items-center gap-3',
172
- title: 'copyable-header-toast-title',
173
- description: 'copyable-header-toast-description',
174
- closeButton: 'copyable-header-toast-close-button'
175
- },
176
- onAutoClose: () => {
177
- isToastVisible = false;
178
- },
179
- onDismiss: () => {
180
- isToastVisible = false;
181
- }
182
- });
183
- }
23
+ import { createEventDispatcher, onDestroy } from 'svelte';
24
+ import { toast, Button, Tooltip, Portal, IconButton, Link, Skeleton } from './';
25
+ import { cn } from '../utils.js';
26
+ import { IconCopy, IconCheck, IconAlertTriangle } from '@tabler/icons-svelte';
27
+ import { sideAtStart, useTextDirection } from './textDirection.js';
28
+
29
+ const dispatch = createEventDispatcher();
30
+ const textDirection = useTextDirection();
31
+ type TriggerChildProps = { props: Record<string, unknown> & { class?: string } };
32
+
33
+ let className = '';
34
+ export { className as class };
35
+ export let style = '';
36
+ export let headerText = 'Copy content';
37
+ export let content = '';
38
+ export let helpText = 'Learn more';
39
+ export let helpUrl = '';
40
+ export let helpPrompt = '';
41
+ export let helpLinkAria = '';
42
+ export let showHelp = true;
43
+ export let monospaceContent = true;
44
+ export let showStandaloneCopyButton = false;
45
+ export let standaloneCopyButtonText = '';
46
+ export let copiedTitle = 'Copied to clipboard';
47
+ export let copiedDescription = 'Content copied successfully.';
48
+ export let copyFailedTitle = 'Copy failed';
49
+ export let copyFailedDescription = 'Please try manual selection';
50
+ export let showButtonText = true;
51
+ export let buttonClass = '';
52
+ export let showInternalToast = true;
53
+ export let headerTextClass = '';
54
+ export let headerBackgroundColor = '';
55
+ export let contentBorderColor = '';
56
+ export let loading = false;
57
+ export let errorTitle = '';
58
+ export let errorDetail = '';
59
+
60
+ let showCopyTooltip = false;
61
+ let tooltipDebounce = false;
62
+ let contentElement: HTMLDivElement | null = null;
63
+ let copied = false;
64
+ let copiedTimeout: ReturnType<typeof setTimeout> | undefined;
65
+ let isToastVisible = false;
66
+
67
+ $: showHelpRow = showHelp && !!helpUrl && (!!helpPrompt || !!helpText);
68
+
69
+ onDestroy(() => {
70
+ if (copiedTimeout) clearTimeout(copiedTimeout);
71
+ });
72
+
73
+ function setTooltipDebounce() {
74
+ tooltipDebounce = true;
75
+ setTimeout(() => {
76
+ tooltipDebounce = false;
77
+ }, 500);
78
+ }
79
+
80
+ async function handleCopyButtonClick(e: MouseEvent) {
81
+ e.preventDefault();
82
+ e.stopPropagation();
83
+ showCopyTooltip = false;
84
+ setTooltipDebounce();
85
+
86
+ if (contentElement) {
87
+ const selection = window.getSelection();
88
+ const range = document.createRange();
89
+ range.selectNodeContents(contentElement);
90
+ selection?.removeAllRanges();
91
+ selection?.addRange(range);
92
+ }
93
+
94
+ try {
95
+ await navigator.clipboard.writeText(content);
96
+ copied = true;
97
+ if (copiedTimeout) clearTimeout(copiedTimeout);
98
+ copiedTimeout = setTimeout(() => {
99
+ copied = false;
100
+ }, 2000);
101
+ if (showInternalToast) {
102
+ showToast('success', copiedTitle, {
103
+ description: copiedDescription
104
+ });
105
+ }
106
+ dispatch('copy', { content });
107
+ } catch (err) {
108
+ if (showInternalToast) {
109
+ showToast('error', copyFailedTitle, {
110
+ description: copyFailedDescription
111
+ });
112
+ }
113
+ dispatch('copyError', { error: err });
114
+ }
115
+ }
116
+
117
+ function showToast(type: 'success' | 'error', message: string, options = {}) {
118
+ if (isToastVisible) return;
119
+ isToastVisible = true;
120
+ toast[type](message, {
121
+ ...options,
122
+ duration: 5000,
123
+ onAutoClose: () => {
124
+ isToastVisible = false;
125
+ },
126
+ onDismiss: () => {
127
+ isToastVisible = false;
128
+ }
129
+ });
130
+ }
184
131
  </script>
185
132
 
186
- <!-- Header Container -->
187
- <div
188
- class="{className ? className + ' ' : ''}copyable-header-container
189
- w-full {headerBackgroundColor || 'bg-[hsl(var(--grey2))]'} widgetic-colors widgetic-radius"
190
- style="z-index: 102; position: relative; {style}"
191
- >
192
- <!-- Header with Title and Copy Button -->
193
- <div
194
- class="copyable-header w-full flex flex-col justify-center gap-[16px] relative"
195
- style="
196
- padding: 8px 24px 0px 24px;
197
- z-index: 102;
198
- "
199
- >
200
- <!-- Header Text and Copy Button -->
201
- <div class="flex justify-between items-center">
202
- <div class="flex items-center gap-2">
203
- <div class="{headerTextClass || 'body-medium text-[hsl(var(--grey7))]'}">{headerText}</div>
204
- {#if showHelp && helpUrl}
205
- <Tooltip.Provider>
206
- <Tooltip.Root open={showHelpTooltip}>
207
- <Tooltip.Trigger class="focus:outline-hidden">
208
- <a
209
- href={helpUrl}
210
- target="_blank"
211
- rel="noopener noreferrer"
212
- role="button"
213
- tabindex="0"
214
- class="inline-flex items-center justify-center w-6 h-6 cursor-pointer"
215
- onmouseenter={handleMouseEnter}
216
- onmousemove={handleMouseMove}
217
- onmouseleave={handleMouseLeave}
218
- onmousedown={() => {
219
- showHelpTooltip = false;
220
- setTooltipDebounce();
221
- }}
222
- onclick={(e) => {
223
- e.preventDefault();
224
- window.open(helpUrl, '_blank', 'noopener,noreferrer');
225
- }}
226
- >
227
- <IconHelp class="w-5 h-5 text-[hsl(var(--grey5))] hover:text-[hsl(var(--deep-forest-green))] transition-colors duration-200" />
228
- </a>
229
- </Tooltip.Trigger>
230
-
231
- {#if showHelpTooltip}
232
- <Portal target="editor-tooltips">
233
- <Tooltip.Content
234
- side={sideAtEnd($textDirection)}
235
- sideOffset={5}
236
- class="bg-[hsl(var(--dark))] text-white z-200000 overflow-hidden rounded-md px-2 py-1.5 shadow-md widgetic-colors widgetic-radius"
237
- >
238
- <p class="caption text-white">{helpText}</p>
239
- </Tooltip.Content>
240
- </Portal>
241
- {/if}
242
- </Tooltip.Root>
243
- </Tooltip.Provider>
244
- {/if}
245
- </div>
246
- {#if showStandaloneCopyButton}
247
- <Tooltip.Provider>
248
- <Tooltip.Root open={showCopyTooltip}>
249
- <Tooltip.Trigger class="focus:outline-hidden">
250
- <Button
251
- variant="default"
252
- class="h-9 w-6 {headerBackgroundColor ? headerBackgroundColor + ' text-[hsl(var(--grey7))]' : buttonClass || 'bg-[hsl(var(--dark))] hover:bg-white text-white rounded-full'} flex items-center justify-center active:scale-95 transition-transform duration-150"
253
- onclick={handleCopyButtonClick}
254
- onmouseenter={handleCopyMouseEnter}
255
- onmousemove={handleMouseMove}
256
- onmouseleave={handleCopyMouseLeave}
257
- onmousedown={() => {
258
- showCopyTooltip = false;
259
- setTooltipDebounce();
260
- }}
261
- >
262
- <IconCopy class="w-5 h-5" style="width: 20px !important; height: 20px !important;" />
263
- {#if showButtonText}
264
- <span class="ms-2">{standaloneCopyButtonText}</span>
265
- {/if}
266
- </Button>
267
- </Tooltip.Trigger>
268
-
269
- {#if showCopyTooltip}
270
- <Portal target="editor-tooltips">
271
- <Tooltip.Content
272
- side={sideAtStart($textDirection)}
273
- sideOffset={5}
274
- class="bg-[hsl(var(--dark))] text-white z-200000 overflow-hidden rounded-md px-3 py-1.5 shadow-md widgetic-colors widgetic-radius"
275
- >
276
- <p class="caption text-white">{standaloneCopyButtonText}</p>
277
- </Tooltip.Content>
278
- </Portal>
279
- {/if}
280
- </Tooltip.Root>
281
- </Tooltip.Provider>
282
- {:else}
283
- <Tooltip.Provider>
284
- <Tooltip.Root open={showCopyTooltip}>
285
- <Tooltip.Trigger class="focus:outline-hidden">
286
- <Button
287
- variant="ghost"
288
- size="icon"
289
- class="h-10 w-10 {headerBackgroundColor ? headerBackgroundColor : 'hover:bg-white'} active:scale-95 transition-transform duration-150 me-[-4px]"
290
- onclick={handleCopyButtonClick}
291
- onmouseenter={handleCopyMouseEnter}
292
- onmousemove={handleMouseMove}
293
- onmouseleave={handleCopyMouseLeave}
294
- onmousedown={() => {
295
- showCopyTooltip = false;
296
- setTooltipDebounce();
297
- }}
298
- aria-label={standaloneCopyButtonText}
299
- >
300
- <IconCopy
301
- class="w-[20px] h-[20px] transition-colors duration-300 text-[hsl(var(--grey5))] hover:text-[hsl(var(--grey7))]"
302
- style="width: 20px !important; height: 20px !important;"
303
- />
304
- </Button>
305
- </Tooltip.Trigger>
306
-
307
- {#if showCopyTooltip}
308
- <Portal target="editor-tooltips">
309
- <Tooltip.Content
310
- side={sideAtStart($textDirection)}
311
- sideOffset={5}
312
- class="bg-[hsl(var(--dark))] text-white z-200000 overflow-hidden rounded-md px-3 py-1.5 shadow-md widgetic-colors widgetic-radius"
313
- >
314
- <p class="caption text-white">{standaloneCopyButtonText}</p>
315
- </Tooltip.Content>
316
- </Portal>
317
- {/if}
318
- </Tooltip.Root>
319
- </Tooltip.Provider>
320
- {/if}
321
- </div>
322
- </div>
323
-
324
- <!-- Content Area -->
325
- <div class="px-6 pb-6">
326
- <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
327
- <div
328
- bind:this={contentElement}
329
- class="copyable-content-area text-[14px] w-full text-start bg-white relative {monospaceContent ? 'font-mono' : ''}"
330
- style="
331
- padding: 16px;
332
- min-height: 80px;
333
- max-height: 141px;
334
- z-index: 101;
335
- line-height: 1.5;
336
- border: 1px solid {contentBorderColor || 'hsl(var(--grey2))'};
337
- border-radius: var(--radius-small);
338
- margin: 0 -2px -2px -2px;
339
- width: calc(100% + 4px);
340
- cursor: text;
341
- white-space: pre-wrap;
342
- word-break: break-all;
343
- overflow-wrap: anywhere;
344
- overflow-y: auto;
345
- "
346
- tabindex="0"
347
- >
348
- {content}
349
- </div>
350
- </div>
133
+ <div class={cn('copyable-header flex w-full flex-col gap-3', className)} {style}>
134
+ <!-- Title row: section label + copy control (Figma 32px). -->
135
+ <div class={cn('flex h-8 items-center justify-between gap-3', headerBackgroundColor)}>
136
+ <div class={cn('text-heading-section text-foreground min-w-0 truncate', headerTextClass)}>
137
+ {headerText}
138
+ </div>
139
+
140
+ {#if showStandaloneCopyButton}
141
+ <Tooltip.Provider>
142
+ <Tooltip.Root open={showCopyTooltip}>
143
+ <Tooltip.Trigger>
144
+ {#snippet child({ props }: TriggerChildProps)}
145
+ <Button
146
+ {...props}
147
+ variant="default"
148
+ class={cn(buttonClass, props.class)}
149
+ onclick={handleCopyButtonClick}
150
+ onmouseenter={() => {
151
+ if (!tooltipDebounce) showCopyTooltip = true;
152
+ }}
153
+ onmouseleave={() => {
154
+ showCopyTooltip = false;
155
+ }}
156
+ onmousedown={() => {
157
+ showCopyTooltip = false;
158
+ setTooltipDebounce();
159
+ }}
160
+ >
161
+ {#if copied}
162
+ <IconCheck class="size-4 stroke-[1.333]" />
163
+ {:else}
164
+ <IconCopy class="size-4 stroke-[1.333]" />
165
+ {/if}
166
+ {#if showButtonText}
167
+ <span class="ms-2">{standaloneCopyButtonText}</span>
168
+ {/if}
169
+ </Button>
170
+ {/snippet}
171
+ </Tooltip.Trigger>
172
+ {#if showCopyTooltip}
173
+ <Portal target="editor-tooltips">
174
+ <Tooltip.Content side={sideAtStart($textDirection)} sideOffset={5}>
175
+ <p class="text-caption">{standaloneCopyButtonText}</p>
176
+ </Tooltip.Content>
177
+ </Portal>
178
+ {/if}
179
+ </Tooltip.Root>
180
+ </Tooltip.Provider>
181
+ {:else}
182
+ <Tooltip.Provider>
183
+ <Tooltip.Root open={showCopyTooltip}>
184
+ <Tooltip.Trigger>
185
+ {#snippet child({ props }: TriggerChildProps)}
186
+ <IconButton
187
+ {...props}
188
+ variant={copied ? 'accent' : 'ghost'}
189
+ label={standaloneCopyButtonText || 'Copy'}
190
+ class={cn(headerBackgroundColor, props.class)}
191
+ onclick={handleCopyButtonClick}
192
+ onmouseenter={() => {
193
+ if (!tooltipDebounce) showCopyTooltip = true;
194
+ }}
195
+ onmouseleave={() => {
196
+ showCopyTooltip = false;
197
+ }}
198
+ onmousedown={() => {
199
+ showCopyTooltip = false;
200
+ setTooltipDebounce();
201
+ }}
202
+ >
203
+ {#if copied}
204
+ <IconCheck class="size-4 stroke-[1.333]" />
205
+ {:else}
206
+ <IconCopy class="text-muted-foreground size-4 stroke-[1.333]" />
207
+ {/if}
208
+ </IconButton>
209
+ {/snippet}
210
+ </Tooltip.Trigger>
211
+ {#if showCopyTooltip && standaloneCopyButtonText}
212
+ <Portal target="editor-tooltips">
213
+ <Tooltip.Content side={sideAtStart($textDirection)} sideOffset={5}>
214
+ <p class="text-caption">{standaloneCopyButtonText}</p>
215
+ </Tooltip.Content>
216
+ </Portal>
217
+ {/if}
218
+ </Tooltip.Root>
219
+ </Tooltip.Provider>
220
+ {/if}
221
+ </div>
222
+
223
+ <!-- Code card: accent surface; loading skeleton, error, or selectable snippet. -->
224
+ <div
225
+ class={cn(
226
+ 'bg-accent flex flex-col gap-2 rounded-xl p-3',
227
+ contentBorderColor && 'border-border border'
228
+ )}
229
+ style={contentBorderColor && !contentBorderColor.startsWith('border')
230
+ ? `border-color: ${contentBorderColor};`
231
+ : undefined}
232
+ >
233
+ {#if loading}
234
+ <div class="flex flex-col gap-2" aria-busy="true">
235
+ <Skeleton class="bg-border h-4 w-full" />
236
+ <Skeleton class="bg-border h-4 w-full" />
237
+ <Skeleton class="bg-border h-4 w-40" />
238
+ </div>
239
+ {:else if errorTitle}
240
+ <div class="flex flex-col items-center gap-2 py-2 text-center">
241
+ <div class="bg-destructive/10 flex size-10 items-center justify-center rounded-lg">
242
+ <IconAlertTriangle class="text-destructive size-4 stroke-[1.333]" aria-hidden="true" />
243
+ </div>
244
+ <p class="text-body-medium text-foreground">{errorTitle}</p>
245
+ {#if errorDetail}
246
+ <p class="text-caption text-muted-foreground font-mono">{errorDetail}</p>
247
+ {/if}
248
+ </div>
249
+ {:else}
250
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
251
+ <div
252
+ bind:this={contentElement}
253
+ class={cn(
254
+ 'text-code text-foreground max-h-36 w-full cursor-text overflow-y-auto text-start break-all whitespace-pre-wrap',
255
+ monospaceContent ? '' : 'text-body'
256
+ )}
257
+ tabindex="0"
258
+ >
259
+ {content}
260
+ </div>
261
+ {/if}
262
+ </div>
263
+
264
+ {#if showHelpRow}
265
+ <div class="flex flex-wrap items-center gap-1">
266
+ {#if helpPrompt}
267
+ <span class="text-caption text-muted-foreground">{helpPrompt}</span>
268
+ {/if}
269
+ <Link
270
+ href={helpUrl}
271
+ label={helpText}
272
+ size="small"
273
+ class="underline"
274
+ aria-label={helpLinkAria || helpText}
275
+ target="_blank"
276
+ rel="noopener noreferrer"
277
+ />
278
+ </div>
279
+ {/if}
351
280
  </div>
352
-
353
- <style>
354
- /* Style for the content area */
355
- .copyable-content-area {
356
- -webkit-user-select: text !important;
357
- -moz-user-select: text !important;
358
- -ms-user-select: text !important;
359
- user-select: text !important;
360
- overflow-y: auto !important;
361
- }
362
-
363
- .copyable-content-area::selection {
364
- background-color: rgba(59, 130, 246, 0.2) !important;
365
- color: inherit !important;
366
- }
367
-
368
- .copyable-content-area::-moz-selection {
369
- background-color: rgba(59, 130, 246, 0.2) !important;
370
- color: inherit !important;
371
- }
372
-
373
- /* Help icon hover styles */
374
- :global(.copyable-header-container .w-6.h-6) {
375
- display: flex !important;
376
- align-items: center !important;
377
- justify-content: center !important;
378
- border-radius: 50% !important;
379
- transition: background-color 0.2s ease !important;
380
- }
381
-
382
- /* Removed the hover background effect for the help icon */
383
- /* :global(.copyable-header-container .w-6.h-6:hover) {
384
- background-color: rgba(255, 255, 255, 0.5) !important;
385
- } */
386
-
387
- /* Button Press Effect */
388
- :global(.copyable-header button) {
389
- transition: transform 0.15s ease, background-color 0.1s ease;
390
- }
391
-
392
- /* Add hover styles for the standalone copy button */
393
- :global(.copyable-header-container button[variant="default"]:hover) {
394
- background-color: white !important;
395
- color: hsl(var(--grey7)) !important;
396
- }
397
-
398
- /* Prevent hover background change for buttons with custom header background */
399
- :global(.copyable-header-container button[class*="bg-"]:hover) {
400
- background-color: inherit !important;
401
- }
402
-
403
- /* Add active scale effect for all buttons */
404
- :global(.copyable-header button:active) {
405
- transform: scale(0.95);
406
- }
407
-
408
- /** TOASTER STYLES **/
409
- /* Toaster Positioning */
410
- :global(.copyable-header-toast) {
411
- position: fixed !important;
412
- bottom: 20px !important;
413
- inset-inline-end: 20px !important;
414
- top: auto !important;
415
- display: flex !important;
416
- align-items: center !important;
417
- gap: 12px !important;
418
- padding: 16px 24px !important;
419
- background: white !important;
420
- border-radius: var(--radius-small) !important;
421
- box-shadow: 0px 0px 15px 3px rgba(0, 0, 0, 0.1) !important;
422
- }
423
-
424
- /* Icon container */
425
- :global(.copyable-header-toast [data-icon]) {
426
- order: -1 !important;
427
- display: flex !important;
428
- align-items: center !important;
429
- justify-content: center !important;
430
- margin: 0 !important;
431
- }
432
-
433
- /* Text container */
434
- :global(.copyable-header-toast [data-content]) {
435
- display: flex !important;
436
- flex-direction: column !important;
437
- gap: 4px !important;
438
- margin: 0 !important;
439
- }
440
-
441
- /* Toaster title */
442
- .copyable-header-toast-title {
443
- font-size: var(--h6) !important;
444
- color: hsl(var(--forest)) !important;
445
- font-weight: 600 !important;
446
- }
447
-
448
- /* Toaster description */
449
- .copyable-header-toast-description {
450
- font-size: var(--caption) !important;
451
- color: hsl(var(--grey3)) !important;
452
- }
453
-
454
- /* Toaster close button */
455
- :global(.copyable-header-toast-close-button) {
456
- background: hsl(var(--grey7)) !important;
457
- border: 1px solid hsl(var(--grey5)) !important;
458
- }
459
-
460
- :global(.copyable-header-toast-close-button:hover) {
461
- background: hsl(var(--dark)) !important;
462
- border: 1px solid hsl(var(--grey5)) !important;
463
- }
464
-
465
- :global(.copyable-header-toast-close-button svg) {
466
- color: white !important;
467
- }
468
- </style>