@widgetic/design-system 0.5.15 → 0.5.17

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 (155) hide show
  1. package/README.md +167 -0
  2. package/dist/components/CopyableHeader.svelte +25 -11
  3. package/dist/components/CopyableHeader.svelte.d.ts +4 -0
  4. package/dist/components/EmptyState.svelte +1 -1
  5. package/dist/components/PasswordInput.svelte +2 -2
  6. package/dist/components/WScrollArea.svelte +2 -2
  7. package/dist/components/ic/browser/BrowserInputController.svelte +2 -2
  8. package/dist/components/index.d.ts +1 -0
  9. package/dist/components/modals/ActionModal.svelte +3 -0
  10. package/dist/components/modals/ActionModal.svelte.d.ts +1 -0
  11. package/dist/components/modals/BaseModal.svelte +4 -2
  12. package/dist/components/modals/BaseModal.svelte.d.ts +1 -0
  13. package/dist/components/modals/NewItemDialog.svelte +48 -30
  14. package/dist/components/modals/NewItemDialog.svelte.d.ts +3 -0
  15. package/dist/components/modals/Portal.svelte +9 -1
  16. package/dist/components/modals/README.md +34 -1
  17. package/dist/components/modals/RenameDialog.svelte +29 -9
  18. package/dist/components/modals/RenameDialog.svelte.d.ts +3 -0
  19. package/dist/components/modals/messages.d.ts +39 -0
  20. package/dist/components/modals/messages.js +10 -0
  21. package/dist/components/navigation/Breadcrumbs.svelte +30 -17
  22. package/dist/components/navigation/Breadcrumbs.svelte.d.ts +4 -0
  23. package/dist/components/navigation/types.d.ts +8 -0
  24. package/dist/components/pdf/PDFViewer.svelte +17 -8
  25. package/dist/components/pdf/PDFViewer.svelte.d.ts +5 -0
  26. package/dist/components/search-input/SearchInput.svelte +3 -3
  27. package/dist/components/shad-editor/colorpicker.svelte +4 -1
  28. package/dist/components/shad-editor/custom/Extentions/ImageExtention.d.ts +20 -1
  29. package/dist/components/shad-editor/custom/Extentions/ImageExtention.js +4 -1
  30. package/dist/components/shad-editor/custom/code-extended.svelte +4 -2
  31. package/dist/components/shad-editor/custom/image-extended-component.svelte +54 -32
  32. package/dist/components/shad-editor/editor.css +5 -5
  33. package/dist/components/shad-editor/i18n/context.d.ts +6 -0
  34. package/dist/components/shad-editor/i18n/context.js +11 -0
  35. package/dist/components/shad-editor/i18n/index.d.ts +8 -0
  36. package/dist/components/shad-editor/i18n/index.js +11 -0
  37. package/dist/components/shad-editor/i18n/messages.d.ts +109 -0
  38. package/dist/components/shad-editor/i18n/messages.js +116 -0
  39. package/dist/components/shad-editor/icons/block-quote.svelte +3 -1
  40. package/dist/components/shad-editor/icons/bold.svelte +3 -1
  41. package/dist/components/shad-editor/icons/buttle-list.svelte +3 -1
  42. package/dist/components/shad-editor/icons/code.svelte +3 -1
  43. package/dist/components/shad-editor/icons/highlighter.svelte +5 -3
  44. package/dist/components/shad-editor/icons/image.svelte +7 -5
  45. package/dist/components/shad-editor/icons/italic.svelte +3 -1
  46. package/dist/components/shad-editor/icons/link.svelte +8 -6
  47. package/dist/components/shad-editor/icons/ordered-list.svelte +3 -1
  48. package/dist/components/shad-editor/icons/quickcolor.svelte +24 -22
  49. package/dist/components/shad-editor/icons/redo.svelte +3 -1
  50. package/dist/components/shad-editor/icons/search-replace.svelte +16 -14
  51. package/dist/components/shad-editor/icons/strikethrough.svelte +3 -1
  52. package/dist/components/shad-editor/icons/subscript.svelte +3 -1
  53. package/dist/components/shad-editor/icons/superscript.svelte +3 -1
  54. package/dist/components/shad-editor/icons/table.svelte +21 -19
  55. package/dist/components/shad-editor/icons/task-list.svelte +3 -1
  56. package/dist/components/shad-editor/icons/text.svelte +13 -11
  57. package/dist/components/shad-editor/icons/textalign.svelte +11 -9
  58. package/dist/components/shad-editor/icons/textcolor.svelte +5 -3
  59. package/dist/components/shad-editor/icons/underline.svelte +3 -1
  60. package/dist/components/shad-editor/icons/undo.svelte +3 -1
  61. package/dist/components/shad-editor/index.d.ts +1 -0
  62. package/dist/components/shad-editor/index.js +2 -0
  63. package/dist/components/shad-editor/shad-editor.svelte +32 -3
  64. package/dist/components/shad-editor/shad-editor.svelte.d.ts +12 -0
  65. package/dist/components/textDirection.d.ts +91 -0
  66. package/dist/components/textDirection.js +99 -0
  67. package/dist/components/toast/ToasterModal.svelte +49 -14
  68. package/dist/components/toast/ToasterModal.svelte.d.ts +9 -2
  69. package/dist/components/toast/toasterRoots.d.ts +23 -0
  70. package/dist/components/toast/toasterRoots.js +36 -0
  71. package/dist/components/ui/alert/index.d.ts +2 -2
  72. package/dist/components/ui/alert/index.js +1 -1
  73. package/dist/components/ui/breadcrumb/breadcrumb-separator.svelte +1 -1
  74. package/dist/components/ui/calendar/calendar-next-button.svelte +21 -4
  75. package/dist/components/ui/calendar/calendar-next-button.svelte.d.ts +9 -2
  76. package/dist/components/ui/calendar/calendar-prev-button.svelte +21 -4
  77. package/dist/components/ui/calendar/calendar-prev-button.svelte.d.ts +9 -2
  78. package/dist/components/ui/calendar/calendar.svelte +18 -6
  79. package/dist/components/ui/calendar/calendar.svelte.d.ts +6 -1
  80. package/dist/components/ui/command/command-input.svelte +1 -1
  81. package/dist/components/ui/command/command-shortcut.svelte +1 -1
  82. package/dist/components/ui/dialog/dialog-content.svelte +5 -2
  83. package/dist/components/ui/dialog/dialog-content.svelte.d.ts +1 -0
  84. package/dist/components/ui/dialog/dialog-header.svelte +1 -1
  85. package/dist/components/ui/drawer/drawer-header.svelte +1 -1
  86. package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +2 -2
  87. package/dist/components/ui/dropdown-menu/dropdown-menu-content.svelte +6 -0
  88. package/dist/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte +1 -1
  89. package/dist/components/ui/dropdown-menu/dropdown-menu-item.svelte +1 -1
  90. package/dist/components/ui/dropdown-menu/dropdown-menu-label.svelte +1 -1
  91. package/dist/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +2 -2
  92. package/dist/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte +1 -1
  93. package/dist/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte +7 -1
  94. package/dist/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +2 -2
  95. package/dist/components/ui/hover-card/hover-card-content.svelte +6 -0
  96. package/dist/components/ui/input-otp/input-otp-slot.svelte +1 -1
  97. package/dist/components/ui/popover/popover-content.svelte +6 -0
  98. package/dist/components/ui/progress/progress.svelte +2 -2
  99. package/dist/components/ui/range-calendar/range-calendar-cell.svelte +1 -1
  100. package/dist/components/ui/range-calendar/range-calendar-next-button.svelte +21 -4
  101. package/dist/components/ui/range-calendar/range-calendar-next-button.svelte.d.ts +9 -2
  102. package/dist/components/ui/range-calendar/range-calendar-prev-button.svelte +21 -4
  103. package/dist/components/ui/range-calendar/range-calendar-prev-button.svelte.d.ts +9 -2
  104. package/dist/components/ui/range-calendar/range-calendar.svelte +18 -6
  105. package/dist/components/ui/range-calendar/range-calendar.svelte.d.ts +7 -2
  106. package/dist/components/ui/scroll-area/scroll-area-scrollbar.svelte +1 -1
  107. package/dist/components/ui/scroll-area/scroll-area.svelte +6 -1
  108. package/dist/components/ui/select/select-content.svelte +6 -0
  109. package/dist/components/ui/select/select-group-heading.svelte +1 -1
  110. package/dist/components/ui/select/select-item.svelte +2 -2
  111. package/dist/components/ui/slider/slider.svelte +6 -0
  112. package/dist/components/ui/switch/switch.svelte +1 -1
  113. package/dist/components/ui/table/table-cell.svelte +1 -1
  114. package/dist/components/ui/table/table-head.svelte +1 -1
  115. package/dist/components/ui/tooltip/tooltip-content.svelte +6 -0
  116. package/dist/i18n/audit/catalogUsage.d.ts +36 -0
  117. package/dist/i18n/audit/catalogUsage.js +58 -0
  118. package/dist/i18n/audit/designSystemWords.d.ts +47 -0
  119. package/dist/i18n/audit/designSystemWords.js +137 -0
  120. package/dist/i18n/audit/hardCodedText.d.ts +40 -0
  121. package/dist/i18n/audit/hardCodedText.js +27 -0
  122. package/dist/i18n/audit/index.d.ts +19 -0
  123. package/dist/i18n/audit/index.js +19 -0
  124. package/dist/i18n/audit/messageCalls.d.ts +34 -0
  125. package/dist/i18n/audit/messageCalls.js +89 -0
  126. package/dist/i18n/audit/syntaxTree.d.ts +33 -0
  127. package/dist/i18n/audit/syntaxTree.js +90 -0
  128. package/dist/i18n/audit/visibleText.d.ts +57 -0
  129. package/dist/i18n/audit/visibleText.js +252 -0
  130. package/dist/i18n/createI18n.d.ts +63 -0
  131. package/dist/i18n/createI18n.js +72 -0
  132. package/dist/i18n/direction.d.ts +27 -0
  133. package/dist/i18n/direction.js +89 -0
  134. package/dist/i18n/index.d.ts +17 -0
  135. package/dist/i18n/index.js +16 -0
  136. package/dist/i18n/localeDate.d.ts +34 -0
  137. package/dist/i18n/localeDate.js +104 -0
  138. package/dist/i18n/splitMessages.d.ts +21 -0
  139. package/dist/i18n/splitMessages.js +33 -0
  140. package/dist/i18n/translator.d.ts +61 -0
  141. package/dist/i18n/translator.js +337 -0
  142. package/dist/rtl/audit/auditPhysicalPlacement.d.ts +36 -0
  143. package/dist/rtl/audit/auditPhysicalPlacement.js +58 -0
  144. package/dist/rtl/audit/index.d.ts +14 -0
  145. package/dist/rtl/audit/index.js +14 -0
  146. package/dist/rtl/audit/physicalPlacement.d.ts +64 -0
  147. package/dist/rtl/audit/physicalPlacement.js +444 -0
  148. package/dist/shad-editor/index.d.ts +1 -1
  149. package/dist/shad-editor/index.js +1 -1
  150. package/dist/styles/global.css +4 -4
  151. package/dist/utils/fillMessage.d.ts +8 -0
  152. package/dist/utils/fillMessage.js +10 -0
  153. package/dist/utils/itemName.d.ts +17 -0
  154. package/dist/utils/itemName.js +41 -0
  155. package/package.json +18 -1
@@ -16,8 +16,10 @@
16
16
  import * as Tooltip from '../../ui/tooltip/index.js';
17
17
  import * as DropdownMenu from '../../ui/dropdown-menu/index.js';
18
18
  import Button from '../../ui/button/button.svelte';
19
+ import { useI18n } from '../i18n/context.js';
19
20
 
20
21
  let { editor }: TextalignProps = $props();
22
+ const { t } = useI18n();
21
23
  </script>
22
24
 
23
25
  <Tooltip.Provider>
@@ -45,43 +47,43 @@
45
47
  onclick={() => editor.chain().focus().setTextAlign('left').run()}
46
48
  closeOnSelect={false}
47
49
  >
48
- <AlignLeft /> Align Left
50
+ <AlignLeft /> {$t('shadEditor.align.left')}
49
51
  {#if editor.isActive({ textAlign: 'left' })}
50
- <Check class="absolute right-2 !size-3 text-muted-foreground" />
52
+ <Check class="absolute end-2 !size-3 text-muted-foreground" />
51
53
  {/if}
52
54
  </DropdownMenu.Item>
53
55
  <DropdownMenu.Item
54
56
  onclick={() => editor.chain().focus().setTextAlign('center').run()}
55
57
  closeOnSelect={false}
56
58
  >
57
- <AlignCenter /> Align Center
59
+ <AlignCenter /> {$t('shadEditor.align.center')}
58
60
  {#if editor.isActive({ textAlign: 'center' })}
59
- <Check class="absolute right-2 !size-3 text-muted-foreground" />
61
+ <Check class="absolute end-2 !size-3 text-muted-foreground" />
60
62
  {/if}
61
63
  </DropdownMenu.Item>
62
64
  <DropdownMenu.Item
63
65
  onclick={() => editor.chain().focus().setTextAlign('right').run()}
64
66
  closeOnSelect={false}
65
67
  >
66
- <AlignRight /> Align Right
68
+ <AlignRight /> {$t('shadEditor.align.right')}
67
69
  {#if editor.isActive({ textAlign: 'right' })}
68
- <Check class="absolute right-2 !size-3 text-muted-foreground" />
70
+ <Check class="absolute end-2 !size-3 text-muted-foreground" />
69
71
  {/if}
70
72
  </DropdownMenu.Item>
71
73
  <DropdownMenu.Item
72
74
  onclick={() => editor.chain().focus().setTextAlign('justify').run()}
73
75
  closeOnSelect={false}
74
76
  >
75
- <AlignJustify /> Align Justify
77
+ <AlignJustify /> {$t('shadEditor.align.justify')}
76
78
  {#if editor.isActive({ textAlign: 'justify' })}
77
- <Check class="absolute right-2 !size-3 text-muted-foreground" />
79
+ <Check class="absolute end-2 !size-3 text-muted-foreground" />
78
80
  {/if}
79
81
  </DropdownMenu.Item>
80
82
  </DropdownMenu.Content>
81
83
  </DropdownMenu.Root>
82
84
  </Tooltip.Trigger>
83
85
  <Tooltip.Content>
84
- <p>Text Alignment</p>
86
+ <p>{$t('shadEditor.align.tooltip')}</p>
85
87
  </Tooltip.Content>
86
88
  </Tooltip.Root>
87
89
  </Tooltip.Provider>
@@ -17,8 +17,10 @@
17
17
  import * as Popover from '../../ui/popover/index.js';
18
18
  import { mode } from 'mode-watcher';
19
19
  import ColorPicker from 'svelte-awesome-color-picker';
20
+ import { useI18n } from '../i18n/context.js';
20
21
 
21
22
  let { editor, color = $bindable('') }: TextcolorProps = $props();
23
+ const { t } = useI18n();
22
24
  </script>
23
25
 
24
26
  <Tooltip.Provider>
@@ -38,7 +40,7 @@
38
40
  </Popover.Trigger>
39
41
  <Popover.Content class="bg-popover shadow-lg *:my-2">
40
42
  <div class="flex items-center justify-between">
41
- <h1 class="text-[1.2rem] font-bold">Pick a text color</h1>
43
+ <h1 class="text-[1.2rem] font-bold">{$t('shadEditor.textColor.pick')}</h1>
42
44
  <Popover.Close>
43
45
  <X class="size-4 text-muted-foreground" />
44
46
  </Popover.Close>
@@ -65,14 +67,14 @@
65
67
  size="sm"
66
68
  class="border-destructive text-destructive hover:bg-destructive hover:text-foreground"
67
69
  onclick={() => editor.chain().focus().unsetColor().run()}
68
- >Remove Color
70
+ >{$t('shadEditor.textColor.remove')}
69
71
  </Button>
70
72
  </div>
71
73
  </Popover.Content>
72
74
  </Popover.Root>
73
75
  </Tooltip.Trigger>
74
76
  <Tooltip.Content>
75
- <p>Text Color</p>
77
+ <p>{$t('shadEditor.textColor.tooltip')}</p>
76
78
  </Tooltip.Content>
77
79
  </Tooltip.Root>
78
80
  </Tooltip.Provider>
@@ -9,8 +9,10 @@
9
9
  import * as Tooltip from '../../ui/tooltip/index.js';
10
10
  import { Button } from '../../ui/button/index.js';
11
11
  import { cn } from '../../../utils.js';
12
+ import { useI18n } from '../i18n/context.js';
12
13
 
13
14
  let { editor }: UnderlineProps = $props();
15
+ const { t } = useI18n();
14
16
  </script>
15
17
 
16
18
  <Tooltip.Provider>
@@ -25,7 +27,7 @@
25
27
  </Button>
26
28
  </Tooltip.Trigger>
27
29
  <Tooltip.Content>
28
- <p>Underline (⌘U)</p>
30
+ <p>{$t('shadEditor.toolbar.underline')}</p>
29
31
  </Tooltip.Content>
30
32
  </Tooltip.Root>
31
33
  </Tooltip.Provider>
@@ -8,8 +8,10 @@
8
8
  import { type Editor } from '@tiptap/core';
9
9
  import * as Tooltip from '../../ui/tooltip/index.js';
10
10
  import { Button } from '../../ui/button/index.js';
11
+ import { useI18n } from '../i18n/context.js';
11
12
 
12
13
  let { editor }: UndoProps = $props();
14
+ const { t } = useI18n();
13
15
  </script>
14
16
 
15
17
  <Tooltip.Provider>
@@ -25,7 +27,7 @@
25
27
  </Button>
26
28
  </Tooltip.Trigger>
27
29
  <Tooltip.Content>
28
- <p>Undo (⌘Z)</p>
30
+ <p>{$t('shadEditor.toolbar.undo')}</p>
29
31
  </Tooltip.Content>
30
32
  </Tooltip.Root>
31
33
  </Tooltip.Provider>
@@ -1,4 +1,5 @@
1
1
  import ShadEditor from './shad-editor.svelte';
2
2
  export { ShadEditor };
3
+ export { SHAD_EDITOR_MESSAGES, checkShadEditorMessages, type ShadEditorMessageKey } from './i18n/index.js';
3
4
  export { type Editor as TipTapEditor } from '@tiptap/core';
4
5
  export { type Level as TipTapEditorLevel } from '@tiptap/extension-heading';
@@ -1,5 +1,7 @@
1
1
  import ShadEditor from './shad-editor.svelte';
2
2
  export { ShadEditor };
3
+ // The words of the editor, so a page can translate them: the English catalog and a check of the page's own messages.
4
+ export { SHAD_EDITOR_MESSAGES, checkShadEditorMessages } from './i18n/index.js';
3
5
  // export Editor and Level from '@tiptap';
4
6
  export {} from '@tiptap/core';
5
7
  export {} from '@tiptap/extension-heading';
@@ -1,11 +1,23 @@
1
1
  <script lang="ts" module>
2
2
  import type { Content } from '@tiptap/core';
3
+ import type { MessageOverrides } from '../../i18n/index.js';
3
4
 
4
5
  export interface ShadEditorProps {
5
6
  class?: string;
6
7
  style?: string;
7
8
  content?: Content;
8
9
  showToolbar?: boolean;
10
+ /**
11
+ * The language of the page: a BCP 47 tag (`de`, `pt-BR`) that sets how numbers are written. The words of the
12
+ * editor are English until `messages` gives them in the page's language.
13
+ */
14
+ locale?: string;
15
+ /**
16
+ * The page's own words for the toolbar, the menus and the image bar: any key of `SHAD_EDITOR_MESSAGES`
17
+ * (`'shadEditor.toolbar.bold': 'Fett (⌘B)'`). A key that is left out keeps its English text; `checkShadEditorMessages`
18
+ * lists the keys that do not exist.
19
+ */
20
+ messages?: MessageOverrides;
9
21
  }
10
22
  </script>
11
23
 
@@ -39,6 +51,8 @@
39
51
  import { ImageExtension } from './custom/Extentions/ImageExtention.js';
40
52
  import { SvelteNodeViewRenderer } from 'svelte-tiptap';
41
53
  import CodeExtended from './custom/code-extended.svelte';
54
+ import { provideI18n } from './i18n/context.js';
55
+ import { dirForUserText, useTextDirection } from '../textDirection.js';
42
56
 
43
57
  // Lowlight
44
58
  import { CodeBlockLowlight } from '@tiptap/extension-code-block-lowlight';
@@ -52,7 +66,21 @@
52
66
  update: { html: string };
53
67
  }>();
54
68
 
55
- let { class: className = '', style = '', content = $bindable(null), showToolbar = true }: ShadEditorProps = $props();
69
+ let { class: className = '', style = '', content = $bindable(null), showToolbar = true, locale, messages }: ShadEditorProps = $props();
70
+
71
+ // The words of the toolbar, the menus and the image bar, in the page's language. The parts below read them from
72
+ // Svelte's context; an image in the text gets them through the options of its extension, because TipTap mounts it
73
+ // outside this component's tree. A change of `locale` or `messages` reaches both without making the editor again.
74
+ // svelte-ignore state_referenced_locally
75
+ const language = provideI18n({ locale, messages });
76
+ $effect(() => {
77
+ language.update({ locale, messages });
78
+ });
79
+
80
+ // The way the screen around the editor is read. The words a visitor types are read by their own first letter on a right to
81
+ // left screen (`dir="auto"` on the text, see below), and the menu of an image in the text gets the direction through the
82
+ // options of its extension, like its words.
83
+ const textDirection = useTextDirection();
56
84
 
57
85
  const lowlight = createLowlight(common);
58
86
 
@@ -131,7 +159,7 @@
131
159
  TableRow,
132
160
  TableHeader,
133
161
  TableCell,
134
- ImageExtension
162
+ ImageExtension.configure({ translate: language.t, textDirection })
135
163
  ],
136
164
  autofocus: true,
137
165
  onUpdate: ({ editor }) => {
@@ -153,5 +181,6 @@
153
181
  {#if editor && showToolbar}
154
182
  <EditorToolbar {editor} class='shad-editor-toolbar' />
155
183
  {/if}
156
- <div bind:this={element} spellcheck="false" class="shad-editor-content h-full w-full flex-1 overflow-auto"></div>
184
+ <!-- On a right to left screen the text reads in the direction of its own first letter: an Arabic paragraph runs from the right, an English one from the left, and an English one does not get its full stop in front. On a left to right screen no attribute is written. -->
185
+ <div bind:this={element} spellcheck="false" dir={dirForUserText($textDirection)} class="shad-editor-content h-full w-full flex-1 overflow-auto"></div>
157
186
  </div>
@@ -1,9 +1,21 @@
1
1
  import type { Content } from '@tiptap/core';
2
+ import type { MessageOverrides } from '../../i18n/index.js';
2
3
  export interface ShadEditorProps {
3
4
  class?: string;
4
5
  style?: string;
5
6
  content?: Content;
6
7
  showToolbar?: boolean;
8
+ /**
9
+ * The language of the page: a BCP 47 tag (`de`, `pt-BR`) that sets how numbers are written. The words of the
10
+ * editor are English until `messages` gives them in the page's language.
11
+ */
12
+ locale?: string;
13
+ /**
14
+ * The page's own words for the toolbar, the menus and the image bar: any key of `SHAD_EDITOR_MESSAGES`
15
+ * (`'shadEditor.toolbar.bold': 'Fett (⌘B)'`). A key that is left out keeps its English text; `checkShadEditorMessages`
16
+ * lists the keys that do not exist.
17
+ */
18
+ messages?: MessageOverrides;
7
19
  }
8
20
  import './editor.css';
9
21
  import { Editor } from '@tiptap/core';
@@ -0,0 +1,91 @@
1
+ import { type Readable } from 'svelte/store';
2
+ import type { TextDirection } from '../i18n/direction.ts';
3
+ /**
4
+ * Which way the text of a screen runs, shared with the components below it through Svelte's context.
5
+ *
6
+ * Most of what a right to left screen needs is done by the browser and by the `dir` attribute on an element around the
7
+ * screen: a row of buttons turns around, a paragraph starts on the right, and the logical classes of the components
8
+ * (`ms-2`, `pe-4`, `start-0`, `text-start`) follow. What the browser cannot do is reach into a library that wants to be
9
+ * told: the scroll area and the menus of `bits-ui` write `dir="ltr"` on their own element unless they are given the
10
+ * direction, and that one attribute turns a whole screen left to right again from the inside. So the root component of a
11
+ * right to left screen says its direction once, and the components that wrap such a library read it here:
12
+ *
13
+ * // in the root component of a package, with `direction` a store that follows the page's language
14
+ * provideTextDirection(direction);
15
+ * // in a component that wraps a library
16
+ * const textDirection = useTextDirection(); then <ScrollAreaPrimitive.Root dir={$textDirection} />
17
+ *
18
+ * A component with no provider above it is left to right, which is what every component here has always been, so a page
19
+ * that never says anything keeps the layout it has.
20
+ *
21
+ * A package whose root component has a `dir` prop that may be left out (the File Browser, the composition editor, the
22
+ * chat, the canvas) does not call `provideTextDirection` itself but `shareTextDirection`: a `dir` that is given is the
23
+ * direction of that screen, and without one the screen is drawn the way the screen around it is.
24
+ */
25
+ /** The two ways text runs: the values of the `dir` attribute. Defined with the language helpers, where a language tag is turned into one. */
26
+ export type { TextDirection };
27
+ /**
28
+ * The two functions of Svelte that the context is made of. They only work while a component is being created, so a test
29
+ * hands in stand-ins; the components use the real ones by leaving this out.
30
+ */
31
+ export interface TextDirectionContextApi {
32
+ getContext<Value>(key: unknown): Value | undefined;
33
+ setContext<Value>(key: unknown, value: Value): Value;
34
+ }
35
+ /** Anything that is not exactly `'rtl'` is left to right: a missing, blank or misspelled direction must not mirror a screen. */
36
+ export declare function readTextDirection(value: unknown): TextDirection;
37
+ /**
38
+ * The `dir` for text a visitor wrote: the name of a file, of a folder.
39
+ *
40
+ * On a right to left screen it is "auto": the browser reads the direction from the first letter that has one, so a Latin
41
+ * name ("2024 report.pdf") reads left to right inside the screen, with its ellipsis at the end of the name, and an
42
+ * Arabic one reads right to left. Without it a Latin name that starts with a digit or ends with a full stop is put in
43
+ * the wrong order, because the line around it runs right to left.
44
+ *
45
+ * On a left to right screen it is `undefined` (Svelte then writes no attribute), and that screen looks exactly as it
46
+ * always has.
47
+ */
48
+ export declare function dirForUserText(direction: TextDirection): 'auto' | undefined;
49
+ /**
50
+ * The `side` of a pop-up that opens at the start of the line: on the left of a left to right screen and on the right of a
51
+ * right to left one. The library names the sides of a pop-up by places on the screen, which do not turn around, so a tooltip
52
+ * that must open beside a control, on the side where the line begins, asks for it here.
53
+ *
54
+ * <Tooltip.Content side={sideAtStart($textDirection)}>
55
+ */
56
+ export declare function sideAtStart(direction: TextDirection): 'left' | 'right';
57
+ /** The `side` of a pop-up that opens at the end of the line: on the right of a left to right screen, on the left of a right to left one. */
58
+ export declare function sideAtEnd(direction: TextDirection): 'left' | 'right';
59
+ /**
60
+ * Called by the root component of a screen while it is created: everything below it reads `direction`. It is a store, so
61
+ * a screen that is already open turns around when the page sets another language. Returns the store it was given.
62
+ */
63
+ export declare function provideTextDirection(direction: Readable<TextDirection>, contextApi?: TextDirectionContextApi): Readable<TextDirection>;
64
+ /**
65
+ * Called by the root component of a package that takes a `dir` prop which may be left out. `given` is a store that holds
66
+ * the prop. A `dir` that is given ('rtl' or 'ltr') is the direction of this screen, whatever the screen above it says
67
+ * (a left to right island inside a right to left screen is possible); with no `dir` (undefined or null) the screen is
68
+ * drawn the way the screen around it is, and left to right when there is none. A `dir` that is given but is not exactly
69
+ * 'rtl' is left to right, like everywhere else: a mistake never mirrors a screen. Everything below the component reads
70
+ * the answer from the context, and it follows when the prop changes or the screen above turns around. Returns the store
71
+ * of the answer, for the component's own decisions (a position worked out in pixels, an arrow key).
72
+ *
73
+ * export let dir: TextDirection | undefined = undefined;
74
+ * const givenDirection = writable(dir);
75
+ * const textDirection = shareTextDirection(givenDirection);
76
+ * $: givenDirection.set(dir);
77
+ */
78
+ export declare function shareTextDirection(given: Readable<TextDirection | null | undefined>, contextApi?: TextDirectionContextApi): Readable<TextDirection>;
79
+ /**
80
+ * Called by a component while it is created: the store of the direction a screen above it gave, or `undefined` when no
81
+ * screen did. For the few parts that must tell "left to right because the screen says so" from "left to right because
82
+ * nobody said anything": a part that draws outside the element of its screen (a pop-up in a layer of its own) writes the
83
+ * screen's direction onto itself, and writes nothing on a page that never asked for one, so the page's own `dir` stays.
84
+ * Never throws; outside a component it is `undefined`.
85
+ */
86
+ export declare function useOptionalTextDirection(contextApi?: TextDirectionContextApi): Readable<TextDirection> | undefined;
87
+ /**
88
+ * Called by a component while it is created: the store of the direction it is drawn in. Never throws; with no provider
89
+ * above it, or outside a component, it is left to right.
90
+ */
91
+ export declare function useTextDirection(contextApi?: TextDirectionContextApi): Readable<TextDirection>;
@@ -0,0 +1,99 @@
1
+ import { getContext, setContext } from 'svelte';
2
+ import { derived, readable } from 'svelte/store';
3
+ /**
4
+ * The key of the context. `Symbol.for` gives the same key to every copy of this file on a page, so a package that was
5
+ * built with its own copy of the design system still reads what the page's root component said.
6
+ */
7
+ const TEXT_DIRECTION_CONTEXT_KEY = Symbol.for('@widgetic/design-system/text-direction');
8
+ /** What a component gets when nothing above it said otherwise. */
9
+ const LEFT_TO_RIGHT = readable('ltr');
10
+ /** Anything that is not exactly `'rtl'` is left to right: a missing, blank or misspelled direction must not mirror a screen. */
11
+ export function readTextDirection(value) {
12
+ return value === 'rtl' ? 'rtl' : 'ltr';
13
+ }
14
+ /**
15
+ * The `dir` for text a visitor wrote: the name of a file, of a folder.
16
+ *
17
+ * On a right to left screen it is "auto": the browser reads the direction from the first letter that has one, so a Latin
18
+ * name ("2024 report.pdf") reads left to right inside the screen, with its ellipsis at the end of the name, and an
19
+ * Arabic one reads right to left. Without it a Latin name that starts with a digit or ends with a full stop is put in
20
+ * the wrong order, because the line around it runs right to left.
21
+ *
22
+ * On a left to right screen it is `undefined` (Svelte then writes no attribute), and that screen looks exactly as it
23
+ * always has.
24
+ */
25
+ export function dirForUserText(direction) {
26
+ return direction === 'rtl' ? 'auto' : undefined;
27
+ }
28
+ /**
29
+ * The `side` of a pop-up that opens at the start of the line: on the left of a left to right screen and on the right of a
30
+ * right to left one. The library names the sides of a pop-up by places on the screen, which do not turn around, so a tooltip
31
+ * that must open beside a control, on the side where the line begins, asks for it here.
32
+ *
33
+ * <Tooltip.Content side={sideAtStart($textDirection)}>
34
+ */
35
+ export function sideAtStart(direction) {
36
+ return direction === 'rtl' ? 'right' : 'left';
37
+ }
38
+ /** The `side` of a pop-up that opens at the end of the line: on the right of a left to right screen, on the left of a right to left one. */
39
+ export function sideAtEnd(direction) {
40
+ return direction === 'rtl' ? 'left' : 'right';
41
+ }
42
+ /**
43
+ * Called by the root component of a screen while it is created: everything below it reads `direction`. It is a store, so
44
+ * a screen that is already open turns around when the page sets another language. Returns the store it was given.
45
+ */
46
+ export function provideTextDirection(direction, contextApi = { getContext, setContext }) {
47
+ try {
48
+ contextApi.setContext(TEXT_DIRECTION_CONTEXT_KEY, direction);
49
+ }
50
+ catch (error) {
51
+ // Called outside a component (a helper, a test): nothing below can read it, and a screen must not fail for that.
52
+ console.warn('[widgetic-ui] The text direction could not be shared with the components below:', error);
53
+ }
54
+ return direction;
55
+ }
56
+ /**
57
+ * Called by the root component of a package that takes a `dir` prop which may be left out. `given` is a store that holds
58
+ * the prop. A `dir` that is given ('rtl' or 'ltr') is the direction of this screen, whatever the screen above it says
59
+ * (a left to right island inside a right to left screen is possible); with no `dir` (undefined or null) the screen is
60
+ * drawn the way the screen around it is, and left to right when there is none. A `dir` that is given but is not exactly
61
+ * 'rtl' is left to right, like everywhere else: a mistake never mirrors a screen. Everything below the component reads
62
+ * the answer from the context, and it follows when the prop changes or the screen above turns around. Returns the store
63
+ * of the answer, for the component's own decisions (a position worked out in pixels, an arrow key).
64
+ *
65
+ * export let dir: TextDirection | undefined = undefined;
66
+ * const givenDirection = writable(dir);
67
+ * const textDirection = shareTextDirection(givenDirection);
68
+ * $: givenDirection.set(dir);
69
+ */
70
+ export function shareTextDirection(given, contextApi) {
71
+ const aboveThisScreen = useTextDirection(contextApi);
72
+ const direction = derived([aboveThisScreen, given], ([fromAbove, fromProp]) => fromProp === undefined || fromProp === null ? fromAbove : readTextDirection(fromProp));
73
+ return provideTextDirection(direction, contextApi);
74
+ }
75
+ /**
76
+ * Called by a component while it is created: the store of the direction a screen above it gave, or `undefined` when no
77
+ * screen did. For the few parts that must tell "left to right because the screen says so" from "left to right because
78
+ * nobody said anything": a part that draws outside the element of its screen (a pop-up in a layer of its own) writes the
79
+ * screen's direction onto itself, and writes nothing on a page that never asked for one, so the page's own `dir` stays.
80
+ * Never throws; outside a component it is `undefined`.
81
+ */
82
+ export function useOptionalTextDirection(contextApi = { getContext, setContext }) {
83
+ try {
84
+ const provided = contextApi.getContext(TEXT_DIRECTION_CONTEXT_KEY);
85
+ if (provided !== undefined && provided !== null && typeof provided.subscribe === 'function')
86
+ return provided;
87
+ }
88
+ catch {
89
+ // Outside a component Svelte refuses to hand out a context: nothing above, then.
90
+ }
91
+ return undefined;
92
+ }
93
+ /**
94
+ * Called by a component while it is created: the store of the direction it is drawn in. Never throws; with no provider
95
+ * above it, or outside a component, it is left to right.
96
+ */
97
+ export function useTextDirection(contextApi = { getContext, setContext }) {
98
+ return useOptionalTextDirection(contextApi) ?? LEFT_TO_RIGHT;
99
+ }
@@ -6,6 +6,9 @@ import { toast, Toaster } from '../ui/sonner';
6
6
  import Exclamation from '../../icons/Exclamation.svelte';
7
7
  import Checked from '../../icons/Checked.svelte';
8
8
 
9
+ // Where the toasters are drawn (the page's document, or a shadow root): see ./toasterRoots.ts
10
+ import { toastLookupRoots } from './toasterRoots';
11
+
9
12
  // Import Toast Types directly from the svelte-sonner module
10
13
  // import type { ToastTypes } from 'svelte-sonner';
11
14
  // Define the ToastTypes type
@@ -67,17 +70,21 @@ export function showToast(type: string = 'success', title: string, options: any
67
70
 
68
71
  // set style of the data-icon element
69
72
  setTimeout(() => {
70
- // set the close button color
71
- let closeButtonElement = document.querySelector('.' + toastMainClass + '-close-button') as HTMLElement; //console.log('closeButtonElement:', closeButtonElement);
72
- // if(closeButtonElement) closeButtonElement.style.backgroundColor = 'hsl(var(' + options.closeBgFillColor + '))';
73
-
74
- // set icon background fill color
75
- let iconElement = document.querySelector('.' + toastMainClass + '> [data-icon] > svg') as HTMLElement; //console.log('iconElement:', iconElement);
76
- if(iconElement) iconElement.style.color = 'hsl(var(' + options.iconBgFillColor + '))';
77
-
78
- // set the title color
79
- let titleElement = document.querySelector('.' + toastMainClass + '-title') as HTMLElement; //console.log('titleElement:', titleElement);
80
- if(titleElement) titleElement.style.color = 'hsl(var(' + titleColor + '))';
73
+ // The elements are looked for in the root the toaster is drawn in: `document.querySelector` does not look inside
74
+ // a shadow root, so for a toaster in a module's shadow root the icon and the title kept the library's colours.
75
+ for (const lookupRoot of toastLookupRoots(document)) {
76
+ // set the close button color
77
+ let closeButtonElement = lookupRoot.querySelector('.' + toastMainClass + '-close-button') as HTMLElement; //console.log('closeButtonElement:', closeButtonElement);
78
+ // if(closeButtonElement) closeButtonElement.style.backgroundColor = 'hsl(var(' + options.closeBgFillColor + '))';
79
+
80
+ // set icon background fill color
81
+ let iconElement = lookupRoot.querySelector('.' + toastMainClass + '> [data-icon] > svg') as HTMLElement; //console.log('iconElement:', iconElement);
82
+ if(iconElement) iconElement.style.color = 'hsl(var(' + options.iconBgFillColor + '))';
83
+
84
+ // set the title color
85
+ let titleElement = lookupRoot.querySelector('.' + toastMainClass + '-title') as HTMLElement; //console.log('titleElement:', titleElement);
86
+ if(titleElement) titleElement.style.color = 'hsl(var(' + titleColor + '))';
87
+ }
81
88
  }, 20);
82
89
 
83
90
  // Call toast with custom styling
@@ -96,10 +103,38 @@ export function showToast(type: string = 'success', title: string, options: any
96
103
 
97
104
  </script>
98
105
 
106
+ <script lang="ts">
107
+ import { onMount } from 'svelte';
108
+ import { registerToasterRoot } from './toasterRoots';
109
+ import { useTextDirection } from '../textDirection.js';
110
+
111
+ /**
112
+ * The words a screen reader hears: the region that holds the toasts ("Notifications", read with the key that moves
113
+ * focus to it) and the button that closes one ("Close toast"). A page in another language gives its own words;
114
+ * left out, the English ones of the library are used.
115
+ */
116
+ export let containerAriaLabel: string | undefined = undefined;
117
+ export let closeButtonAriaLabel: string | undefined = undefined;
118
+
119
+ // An element with no size that is only here to ask which root (the page's document, or a shadow root) this toaster is
120
+ // drawn in, so that `showToast` looks for the toast's icon and title there.
121
+ let rootMarker: HTMLElement;
122
+ onMount(() => registerToasterRoot(rootMarker.getRootNode() as Document | ShadowRoot));
123
+
124
+ // The toaster reads the direction of the PAGE's <html> unless it is told, and a module on a left to right page that
125
+ // speaks Arabic (or the other way around) must follow its own screen. The toasts sit in the corner at the end of the
126
+ // line: the right in a left to right screen, the left in a right to left one.
127
+ const textDirection = useTextDirection();
128
+ </script>
129
+
99
130
  <!-- HTML Template -->
131
+ <span hidden bind:this={rootMarker}></span>
100
132
  <Toaster
101
133
  theme="light"
102
- position="bottom-right"
134
+ dir={$textDirection}
135
+ position={$textDirection === 'rtl' ? 'bottom-left' : 'bottom-right'}
136
+ {containerAriaLabel}
137
+ {closeButtonAriaLabel}
103
138
  richColors
104
139
  closeButton
105
140
  toastOptions={{
@@ -107,7 +142,7 @@ export function showToast(type: string = 'success', title: string, options: any
107
142
  classes: {
108
143
  toast: toastMainClass + " widgetic-colors widgetic-radius !bg-white !rounded-small !border !border-[hsl(var(--forest-green))] " +
109
144
  "!min-w-[334px] !max-w-[450px] " +
110
- "!fixed !top-auto !bottom-[20px] !right-[20px] " +
145
+ "!fixed !top-auto !bottom-[20px] !end-[20px] " +
111
146
  "!flex !items-center !gap-[24px] !p-[16px] !px-[24px] " +
112
147
  // Icon Styling
113
148
  "*:data-icon:!w-[24px] *:data-icon:!h-[24px] *:data-icon:!m-0 " +
@@ -123,7 +158,7 @@ export function showToast(type: string = 'success', title: string, options: any
123
158
  // Cancel Button Styling
124
159
  cancelButton: "",
125
160
  // Close Button Styling
126
- closeButton: toastMainClass + '-close-button !w-[16px] !h-[16px] !absolute !top-[-4px] !left-[-4px] z-10 ' +
161
+ closeButton: toastMainClass + '-close-button !w-[16px] !h-[16px] !absolute !top-[-4px] !start-[-4px] z-10 ' +
127
162
  '!flex !items-center !justify-center ' +
128
163
  '!bg-[hsl(var(--grey7))] !border !border-[hsl(var(--grey5))] !rounded-full !text-white ' +
129
164
  '[&_svg]:!text-white [&_svg]:!fill-white [&_svg]:!stroke-white'
@@ -14,7 +14,7 @@ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> =
14
14
  new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
15
15
  $$bindings?: Bindings;
16
16
  } & Exports;
17
- (internal: unknown, props: {
17
+ (internal: unknown, props: Props & {
18
18
  $$events?: Events;
19
19
  $$slots?: Slots;
20
20
  }): Exports & {
@@ -23,7 +23,14 @@ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> =
23
23
  };
24
24
  z_$$bindings?: Bindings;
25
25
  }
26
- declare const ToasterModal: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
26
+ declare const ToasterModal: $$__sveltets_2_IsomorphicComponent<{
27
+ /**
28
+ * The words a screen reader hears: the region that holds the toasts ("Notifications", read with the key that moves
29
+ * focus to it) and the button that closes one ("Close toast"). A page in another language gives its own words;
30
+ * left out, the English ones of the library are used.
31
+ */ containerAriaLabel?: string | undefined;
32
+ closeButtonAriaLabel?: string | undefined;
33
+ }, {
27
34
  [evt: string]: CustomEvent<any>;
28
35
  }, {}, {}, string>;
29
36
  type ToasterModal = InstanceType<typeof ToasterModal>;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Where the toasters are drawn.
3
+ *
4
+ * `showToast` colours the icon and the title of a toast after the toast has been drawn, by looking for them in the
5
+ * tree. `document.querySelector` never looks inside a shadow root, so for a toaster that a module draws inside its
6
+ * shadow root that lookup found nothing and the toast kept the library's grey. A toaster says which root it is drawn
7
+ * in (the page's document, or a shadow root), and the lookup looks there.
8
+ */
9
+ /** What a lookup needs of a root: the `document` of a page and a `ShadowRoot` both have it. */
10
+ export interface ToastLookupRoot {
11
+ querySelector(selectors: string): Element | null;
12
+ }
13
+ /**
14
+ * Says that a toaster is drawn in `root`. Returns the function that says it is gone: call it when the toaster is
15
+ * removed. Calling it twice is the same as calling it once, so a toaster that is destroyed twice does not take another
16
+ * toaster's place in the count.
17
+ */
18
+ export declare function registerToasterRoot(root: ToastLookupRoot): () => void;
19
+ /**
20
+ * The roots to look for the elements of a toast in: every root a toaster is mounted in, or `fallbackRoot` (the page's
21
+ * `document`) when no toaster has said where it is, as before a toaster is mounted.
22
+ */
23
+ export declare function toastLookupRoots(fallbackRoot: ToastLookupRoot): ToastLookupRoot[];
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Where the toasters are drawn.
3
+ *
4
+ * `showToast` colours the icon and the title of a toast after the toast has been drawn, by looking for them in the
5
+ * tree. `document.querySelector` never looks inside a shadow root, so for a toaster that a module draws inside its
6
+ * shadow root that lookup found nothing and the toast kept the library's grey. A toaster says which root it is drawn
7
+ * in (the page's document, or a shadow root), and the lookup looks there.
8
+ */
9
+ /** The roots toasters are mounted in, with how many toasters each has (two in one root are allowed, if odd). */
10
+ const mountedRoots = new Map();
11
+ /**
12
+ * Says that a toaster is drawn in `root`. Returns the function that says it is gone: call it when the toaster is
13
+ * removed. Calling it twice is the same as calling it once, so a toaster that is destroyed twice does not take another
14
+ * toaster's place in the count.
15
+ */
16
+ export function registerToasterRoot(root) {
17
+ mountedRoots.set(root, (mountedRoots.get(root) ?? 0) + 1);
18
+ let isRegistered = true;
19
+ return () => {
20
+ if (!isRegistered)
21
+ return;
22
+ isRegistered = false;
23
+ const remaining = (mountedRoots.get(root) ?? 1) - 1;
24
+ if (remaining <= 0)
25
+ mountedRoots.delete(root);
26
+ else
27
+ mountedRoots.set(root, remaining);
28
+ };
29
+ }
30
+ /**
31
+ * The roots to look for the elements of a toast in: every root a toaster is mounted in, or `fallbackRoot` (the page's
32
+ * `document`) when no toaster has said where it is, as before a toaster is mounted.
33
+ */
34
+ export function toastLookupRoots(fallbackRoot) {
35
+ return mountedRoots.size > 0 ? [...mountedRoots.keys()] : [fallbackRoot];
36
+ }