@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
@@ -22,6 +22,7 @@ The `ActionModal` component is a reusable confirmation modal that can be used ac
22
22
  | `message` | string | `''` | Custom message for the modal (autogenerated if empty) |
23
23
  | `confirmLabel` | string | `'Confirm'` | Label for the confirm button |
24
24
  | `cancelLabel` | string | `'Cancel'` | Label for the cancel button |
25
+ | `closeLabel` | string | `'Close modal'` | The words a screen reader says for the close button |
25
26
  | `width` | string | `'410px'` | Width of the modal |
26
27
  | `height` | string | `'168px'` | Height of the modal |
27
28
  | `modalId` | string | `'delete-confirmation-modal'` | ID for targeting the modal |
@@ -96,4 +97,36 @@ You can provide custom title and message for specific contexts:
96
97
  confirmLabel="Delete Permanently"
97
98
  on:confirm={handleDelete}
98
99
  />
99
- ```
100
+ ```
101
+
102
+ ## Words in another language
103
+
104
+ Every word a visitor can see or hear is a prop with an English default, so a page that shows the components in
105
+ another language passes its own words and nothing else changes.
106
+
107
+ | Component | Props with words |
108
+ |-----------|------------------|
109
+ | `BaseModal`, `ActionModal`, `RenameDialog`, `NewItemDialog` | `closeLabel` (screen reader name of the close button) |
110
+ | `ActionModal`, `RenameDialog`, `NewItemDialog` | `confirmLabel`, `cancelLabel` |
111
+ | `RenameDialog` | `messages`: `invalidTitle`, `enterName`, `enterDifferentName`, `enterValidName`, `tooShort`, `tooLong` (`{max}`), `renamed` (`{name}`), `failed` |
112
+ | `NewItemDialog` | `messages`: `invalidTitle`, `required`, `invalidCharacters`, `tooLong` (`{max}`). The English defaults are built from `itemType` ("Folder name is required"). |
113
+ | `Breadcrumbs` | `backLabel`, `forwardLabel`, `rootLabel`, `breadcrumbLabel` |
114
+ | `PDFViewer` | `loadingLabel`, `failedLabel`, `pageLabel` (`{page}`, `{total}`), `previousPageLabel`, `nextPageLabel` |
115
+ | `ToasterModal` (`components/toast`) | `containerAriaLabel` (the region that holds the toasts, "Notifications"; the library adds the key that moves focus to it), `closeButtonAriaLabel` ("Close toast"). The title and description of a toast are the words of whoever calls `showToast`. |
116
+
117
+ A key of `messages` that is left out keeps its English text. A message can hold the values named next to it in
118
+ curly braces: `tooLong: 'Der Name darf höchstens {max} Zeichen haben'`.
119
+
120
+ ```svelte
121
+ <RenameDialog
122
+ bind:isOpen
123
+ itemName="Reisefotos"
124
+ confirmLabel="Umbenennen"
125
+ cancelLabel="Abbrechen"
126
+ closeLabel="Dialog schließen"
127
+ messages={{ enterName: 'Bitte einen Namen eingeben', tooLong: 'Höchstens {max} Zeichen' }}
128
+ />
129
+ ```
130
+
131
+ `NewItemDialog` accepts names made of letters, numbers and spaces **of any script** ("Ordner für Müller",
132
+ "写真", "Фото 2024"). Punctuation and symbols are still refused.
@@ -7,6 +7,9 @@
7
7
  // import { toast } from '../ui/sonner';
8
8
  import { showToast } from '../toast';
9
9
  import BaseModal from './BaseModal.svelte';
10
+ // Words of the dialog and the helper that puts values into them
11
+ import type { RenameDialogMessages } from './messages';
12
+ import { fillMessage } from '../../utils/fillMessage';
10
13
 
11
14
  // Props
12
15
  let className = '';
@@ -21,6 +24,10 @@
21
24
  export let placeholder = '';
22
25
  export let confirmLabel = 'Rename';
23
26
  export let cancelLabel = 'Cancel';
27
+ // The words a screen reader says for the close button (see BaseModal)
28
+ export let closeLabel = 'Close modal';
29
+ // The words of the errors and toasts; a key that is left out keeps its English text (see ./messages)
30
+ export let messages: Partial<RenameDialogMessages> = {};
24
31
  export let width = '410px';
25
32
  export let height = '168px';
26
33
  export let target: string = 'rename-dialog'; // target for Portal
@@ -59,6 +66,18 @@
59
66
  // Computed placeholder based on itemType
60
67
  $: computedPlaceholder = placeholder || `${itemType} name...`;
61
68
 
69
+ // The words of the errors and toasts: the page's own, or English
70
+ $: text = {
71
+ invalidTitle: messages.invalidTitle ?? 'Invalid name',
72
+ enterName: messages.enterName ?? 'Please enter a name',
73
+ enterDifferentName: messages.enterDifferentName ?? 'Please enter a different name',
74
+ enterValidName: messages.enterValidName ?? 'Please enter a valid name',
75
+ tooShort: messages.tooShort ?? 'Name must be at least 1 character long',
76
+ tooLong: fillMessage(messages.tooLong ?? 'Name must be {max} characters or less', { max: maxLength }),
77
+ renamed: messages.renamed ?? 'Successfully renamed to {name}',
78
+ failed: messages.failed ?? 'Failed to rename item'
79
+ };
80
+
62
81
  // Function to focus and select the input text
63
82
  async function focusAndSelectInput() {
64
83
  // Wait for the DOM to update
@@ -95,17 +114,17 @@
95
114
  try {
96
115
  // Validate input
97
116
  if (!inputValue.trim() || inputValue.length < 1) {
98
- showToast('error', 'Invalid name', { description: 'Name must be at least 1 character long' });
117
+ showToast('error', text.invalidTitle, { description: text.tooShort });
99
118
  return;
100
119
  }
101
120
 
102
121
  if (inputValue.length > maxLength) {
103
- showToast('error', 'Invalid name', { description: `Name must be ${maxLength} characters or less` });
122
+ showToast('error', text.invalidTitle, { description: text.tooLong });
104
123
  return;
105
124
  }
106
125
 
107
126
  if (inputValue === itemName) {
108
- showToast('error', 'Invalid name', { description: 'Please enter a different name' });
127
+ showToast('error', text.invalidTitle, { description: text.enterDifferentName });
109
128
  return;
110
129
  }
111
130
 
@@ -124,17 +143,17 @@
124
143
  // with a more generic approach
125
144
  try {
126
145
  // Simulate success
127
- showToast('success', `Successfully renamed to ${inputValue}`);
146
+ showToast('success', fillMessage(text.renamed, { name: inputValue }));
128
147
  dispatch('rename', { itemId, newName: inputValue, oldName: itemName });
129
148
  isOpen = false;
130
149
  } catch (error) {
131
150
  console.error('Error during rename:', error);
132
- showToast('error', 'Failed to rename item');
151
+ showToast('error', text.failed);
133
152
  }
134
153
  }
135
154
  } catch (error) {
136
155
  console.error('Error during rename:', error);
137
- showToast('error', 'Failed to rename item');
156
+ showToast('error', text.failed);
138
157
  }
139
158
  }
140
159
 
@@ -172,6 +191,7 @@
172
191
  width={width}
173
192
  height={height}
174
193
  titlePadding="small"
194
+ {closeLabel}
175
195
  {modalType}
176
196
  {modalData}
177
197
  on:restore={handleRestore}
@@ -224,9 +244,9 @@
224
244
  />
225
245
  {#if !isValid && hasAttemptedSubmit}
226
246
  <div class="text-xs text-[hsl(var(--error))] mt-1">
227
- {inputValue.trim().length === 0 ? 'Please enter a name' :
228
- inputValue === itemName ? 'Please enter a different name' :
229
- 'Please enter a valid name'}
247
+ {inputValue.trim().length === 0 ? text.enterName :
248
+ inputValue === itemName ? text.enterDifferentName :
249
+ text.enterValidName}
230
250
  </div>
231
251
  {/if}
232
252
  </div>
@@ -1,3 +1,4 @@
1
+ import type { RenameDialogMessages } from './messages';
1
2
  interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
3
  new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
4
  $$bindings?: Bindings;
@@ -22,6 +23,8 @@ declare const RenameDialog: $$__sveltets_2_IsomorphicComponent<{
22
23
  placeholder?: string;
23
24
  confirmLabel?: string;
24
25
  cancelLabel?: string;
26
+ closeLabel?: string;
27
+ messages?: Partial<RenameDialogMessages>;
25
28
  width?: string;
26
29
  height?: string;
27
30
  target?: string;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The words the dialogs say besides their title, placeholder and button labels.
3
+ *
4
+ * Every key is optional: a key that is left out keeps its English text. A message can hold named values in curly
5
+ * braces, for example `Name must be {max} characters or less`; the dialog says which ones it fills in (see
6
+ * `fillMessage` in utils).
7
+ * A page that shows the dialogs in another language passes its own words, for example
8
+ * `<RenameDialog messages={{ enterName: 'Bitte einen Namen eingeben' }} />`.
9
+ */
10
+ /** Words of RenameDialog. */
11
+ export interface RenameDialogMessages {
12
+ /** Heading of the toast shown for a name that cannot be used. */
13
+ invalidTitle: string;
14
+ /** Under the field when it is empty. */
15
+ enterName: string;
16
+ /** Under the field and in the toast when the name has not changed. */
17
+ enterDifferentName: string;
18
+ /** Under the field when the name cannot be used for another reason. */
19
+ enterValidName: string;
20
+ /** Toast text for an empty name. */
21
+ tooShort: string;
22
+ /** Toast text for a name that is too long. Values: `{max}`, the longest name allowed. */
23
+ tooLong: string;
24
+ /** Toast when the dialog did the rename itself. Values: `{name}`, the new name. */
25
+ renamed: string;
26
+ /** Toast when the dialog did the rename itself and it failed. */
27
+ failed: string;
28
+ }
29
+ /** Words of NewItemDialog. The English defaults are built from the dialog's `itemType` ("Folder name is required"). */
30
+ export interface NewItemDialogMessages {
31
+ /** Heading of the toast shown for a name that cannot be used. */
32
+ invalidTitle: string;
33
+ /** Under the field when it is empty. */
34
+ required: string;
35
+ /** Under the field when the name has a character the dialog does not accept. */
36
+ invalidCharacters: string;
37
+ /** Under the field and in the toast when the name is too long. Values: `{max}`, the longest name allowed. */
38
+ tooLong: string;
39
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The words the dialogs say besides their title, placeholder and button labels.
3
+ *
4
+ * Every key is optional: a key that is left out keeps its English text. A message can hold named values in curly
5
+ * braces, for example `Name must be {max} characters or less`; the dialog says which ones it fills in (see
6
+ * `fillMessage` in utils).
7
+ * A page that shows the dialogs in another language passes its own words, for example
8
+ * `<RenameDialog messages={{ enterName: 'Bitte einen Namen eingeben' }} />`.
9
+ */
10
+ export {};
@@ -12,7 +12,8 @@
12
12
  import { IconArrowLeft, IconArrowRight } from '@tabler/icons-svelte';
13
13
 
14
14
  // Types Import
15
- import type { BreadcrumbsProps } from './types.ts';
15
+ import type { BreadcrumbsProps } from './types.js';
16
+ import { dirForUserText, useTextDirection } from '../textDirection.js';
16
17
 
17
18
  // Environment Import
18
19
  // import { browser } from '$app/environment';
@@ -36,6 +37,11 @@
36
37
  export let showNavigationButtons: BreadcrumbsProps['showNavigationButtons'] = true;
37
38
  export let canGoBack: BreadcrumbsProps['canGoBack'] = false;
38
39
  export let canGoForward: BreadcrumbsProps['canGoForward'] = false;
40
+ // words (tooltips and screen readers): a page that shows this in another language passes its own
41
+ export let backLabel: string = 'Back';
42
+ export let forwardLabel: string = 'Forward';
43
+ export let rootLabel: string = 'File browser';
44
+ export let breadcrumbLabel: string = 'breadcrumb';
39
45
  // event handlers
40
46
  export let onNavigate: BreadcrumbsProps['onNavigate'] = () => {};
41
47
  export let onBack: BreadcrumbsProps['onBack'] = () => {};
@@ -43,6 +49,11 @@
43
49
  // debug settings
44
50
  export let showDebugVisualizer: boolean = false;
45
51
 
52
+ // A name a visitor wrote keeps a direction of its own on a right to left screen (a Latin folder name reads left to
53
+ // right inside it); on a left to right screen no attribute is written and nothing changes.
54
+ const textDirection = useTextDirection();
55
+ $: nameDirection = dirForUserText($textDirection);
56
+
46
57
  /***
47
58
  * COMPONENT STATE VARIABLES
48
59
  **/
@@ -99,7 +110,7 @@
99
110
  navButtons: showNavigationButtons
100
111
  ? navElement.querySelector('.nav-buttons')?.getBoundingClientRect()
101
112
  : null,
102
- rightContent: containerElement.querySelector('.pr-5, .right-content')?.getBoundingClientRect(),
113
+ rightContent: containerElement.querySelector('.pe-5, .pr-5, .right-content')?.getBoundingClientRect(),
103
114
  breadcrumbs: breadcrumbsContainer.getBoundingClientRect()
104
115
  };
105
116
 
@@ -179,7 +190,7 @@
179
190
  <nav
180
191
  bind:this={navElement}
181
192
  bind:clientHeight
182
- aria-label="breadcrumb"
193
+ aria-label={breadcrumbLabel}
183
194
  class="{className ? className + ' ' : ''}breadcrumbs-component flex items-center widgetic-colors h-[56px] bg-white border-b relative w-full"
184
195
  style={style}
185
196
  >
@@ -192,13 +203,14 @@
192
203
  type="button"
193
204
  class="breadcrumbs-nav-button inline-flex items-center justify-center w-6 h-6 border-0 bg-transparent p-0 focus:outline-none"
194
205
  style="cursor: {canGoBack && !isLoading ? 'pointer' : 'default'}; opacity: {canGoBack && !isLoading ? 1 : 0.35};"
206
+ aria-label={backLabel}
195
207
  aria-disabled={!canGoBack || isLoading}
196
208
  on:mouseenter={() => { showBackTooltip = true; }}
197
209
  on:mouseleave={() => { showBackTooltip = false; }}
198
210
  on:click={() => { if (!isLoading) onBack(); }}
199
211
  >
200
212
  <IconArrowLeft
201
- class="w-6 h-6 text-[hsl(var(--grey6))]"
213
+ class="w-6 h-6 text-[hsl(var(--grey6))] rtl:-scale-x-100"
202
214
  style="min-width: 24px; min-height: 24px;"
203
215
  />
204
216
  </button>
@@ -207,7 +219,7 @@
207
219
  otherwise clips the tooltip at the header's bottom edge. -->
208
220
  <Tooltip.Portal>
209
221
  <Tooltip.Content side="bottom" sideOffset={6} class="bg-[hsl(var(--dark))] text-white caption px-2 py-1 rounded-md z-[200000]">
210
- Back
222
+ {backLabel}
211
223
  </Tooltip.Content>
212
224
  </Tooltip.Portal>
213
225
  {/if}
@@ -216,22 +228,23 @@
216
228
  <Tooltip.Root open={showForwardTooltip}>
217
229
  <button
218
230
  type="button"
219
- class="breadcrumbs-nav-button ml-4 inline-flex items-center justify-center w-6 h-6 border-0 bg-transparent p-0 focus:outline-none"
231
+ class="breadcrumbs-nav-button ms-4 inline-flex items-center justify-center w-6 h-6 border-0 bg-transparent p-0 focus:outline-none"
220
232
  style="cursor: {canGoForward && !isLoading ? 'pointer' : 'default'}; opacity: {canGoForward && !isLoading ? 1 : 0.35};"
233
+ aria-label={forwardLabel}
221
234
  aria-disabled={!canGoForward || isLoading}
222
235
  on:mouseenter={() => { showForwardTooltip = true; }}
223
236
  on:mouseleave={() => { showForwardTooltip = false; }}
224
237
  on:click={() => { if (!isLoading) onForward(); }}
225
238
  >
226
239
  <IconArrowRight
227
- class="w-6 h-6 text-[hsl(var(--grey6))]"
240
+ class="w-6 h-6 text-[hsl(var(--grey6))] rtl:-scale-x-100"
228
241
  style="min-width: 24px; min-height: 24px;"
229
242
  />
230
243
  </button>
231
244
  {#if showForwardTooltip}
232
245
  <Tooltip.Portal>
233
246
  <Tooltip.Content side="bottom" sideOffset={6} class="bg-[hsl(var(--dark))] text-white caption px-2 py-1 rounded-md z-[200000]">
234
- Forward
247
+ {forwardLabel}
235
248
  </Tooltip.Content>
236
249
  </Tooltip.Portal>
237
250
  {/if}
@@ -241,8 +254,8 @@
241
254
  {/if}
242
255
 
243
256
  <!-- BREADCRUMBS CONTAINER -->
244
- <div bind:this={breadcrumbsContainer} class="flex-1 pl-[18px] overflow-hidden min-w-0">
245
- <Breadcrumb.Root>
257
+ <div bind:this={breadcrumbsContainer} class="flex-1 ps-[18px] overflow-hidden min-w-0">
258
+ <Breadcrumb.Root aria-label={breadcrumbLabel}>
246
259
  <Breadcrumb.List class="inline-flex items-center min-w-0 breadcrumbs-list">
247
260
  <div class="inline-flex items-center min-w-0 min-h-[24px]">
248
261
  <!-- Root Breadcrumb Item -->
@@ -257,7 +270,7 @@
257
270
  on:click={(e) => handleClick(e, -1)}
258
271
  on:keydown={(e) => handleKeyDown(e, -1)}
259
272
  >
260
- File browser
273
+ {rootLabel}
261
274
  </span>
262
275
  </Breadcrumb.Item>
263
276
 
@@ -277,7 +290,7 @@
277
290
  {#each currentPath.slice(0, -itemsToShow + 1) as _, index}
278
291
  <DropdownMenu.Item>
279
292
  <!-- svelte-ignore a11y-click-events-have-key-events -->
280
- <span
293
+ <span dir={nameDirection}
281
294
  role="link"
282
295
  tabindex="0"
283
296
  class="block w-full cursor-pointer"
@@ -296,12 +309,12 @@
296
309
  <Breadcrumb.Separator class="h5 text-[hsl(var(--grey6))] shrink-0">/</Breadcrumb.Separator>
297
310
  <Breadcrumb.Item class="shrink-0">
298
311
  {#if index === itemsToShow - 2}
299
- <span class="h5 text-[hsl(var(--grey3))]">
312
+ <span dir={nameDirection} class="h5 text-[hsl(var(--grey3))]">
300
313
  {currentPathNames[currentPath.length - 1]}
301
314
  </span>
302
315
  {:else}
303
316
  <!-- svelte-ignore a11y-click-events-have-key-events -->
304
- <span
317
+ <span dir={nameDirection}
305
318
  role="link"
306
319
  tabindex="0"
307
320
  class="h5 text-[hsl(var(--grey6))] transition-colors hover:text-[hsl(var(--grey7))] hover:underline hover:decoration-[hsl(var(--grey7))] cursor-pointer"
@@ -320,12 +333,12 @@
320
333
  <Breadcrumb.Separator class="h5 text-[hsl(var(--grey6))] shrink-0">/</Breadcrumb.Separator>
321
334
  <Breadcrumb.Item class="shrink-0">
322
335
  {#if index === currentPath.length - 1}
323
- <span class="h5 text-[hsl(var(--grey3))]">
336
+ <span dir={nameDirection} class="h5 text-[hsl(var(--grey3))]">
324
337
  {currentPathNames[index]}
325
338
  </span>
326
339
  {:else}
327
340
  <!-- svelte-ignore a11y-click-events-have-key-events -->
328
- <span
341
+ <span dir={nameDirection}
329
342
  role="link"
330
343
  tabindex="0"
331
344
  class="h5 text-[hsl(var(--grey6))] transition-colors hover:text-[hsl(var(--grey7))] hover:underline hover:decoration-[hsl(var(--grey7))] cursor-pointer"
@@ -347,7 +360,7 @@
347
360
  </div>
348
361
 
349
362
  {#if showDebugVisualizer}
350
- <div class="absolute bottom-1 right-1 text-xs text-blue-600 bg-white px-1 rounded shadow-2xs">
363
+ <div class="absolute bottom-1 end-1 text-xs text-blue-600 bg-white px-1 rounded shadow-2xs">
351
364
  Available width: {Math.round(availableWidth)}px
352
365
  <br>
353
366
  Items to show: {itemsToShow}
@@ -24,6 +24,10 @@ declare const Breadcrumbs: $$__sveltets_2_IsomorphicComponent<{
24
24
  showNavigationButtons?: BreadcrumbsProps["showNavigationButtons"];
25
25
  canGoBack?: BreadcrumbsProps["canGoBack"];
26
26
  canGoForward?: BreadcrumbsProps["canGoForward"];
27
+ backLabel?: string;
28
+ forwardLabel?: string;
29
+ rootLabel?: string;
30
+ breadcrumbLabel?: string;
27
31
  onNavigate?: BreadcrumbsProps["onNavigate"];
28
32
  onBack?: BreadcrumbsProps["onBack"];
29
33
  onForward?: BreadcrumbsProps["onForward"];
@@ -9,4 +9,12 @@ export interface BreadcrumbsProps {
9
9
  onForward: () => void;
10
10
  showNavigationButtons?: boolean;
11
11
  showDebugVisualizer?: boolean;
12
+ /** Tooltip and screen reader name of the back button. Default: "Back". */
13
+ backLabel?: string;
14
+ /** Tooltip and screen reader name of the forward button. Default: "Forward". */
15
+ forwardLabel?: string;
16
+ /** Name of the first item of the path. Default: "File browser". */
17
+ rootLabel?: string;
18
+ /** Screen reader name of the navigation. Default: "breadcrumb". */
19
+ breadcrumbLabel?: string;
12
20
  }
@@ -43,6 +43,9 @@
43
43
 
44
44
  // Import PDF cache store
45
45
  import { pdfCache } from '../../stores/pdfCache';
46
+
47
+ // Puts the page numbers into the words of the page counter
48
+ import { fillMessage } from '../../utils/fillMessage';
46
49
 
47
50
  // Component props
48
51
  // class and style props
@@ -52,6 +55,12 @@
52
55
  // data props
53
56
  export let url = '';
54
57
  export let enableCacheClearing: boolean = true; // Flag to enable/disable cache clearing shortcut
58
+ // words: a page that shows the viewer in another language passes its own
59
+ export let loadingLabel: string = 'Loading PDF...';
60
+ export let failedLabel: string = 'Failed to load PDF';
61
+ export let pageLabel: string = 'Page {page} of {total}'; // {total} is "?" until the page count is known
62
+ export let previousPageLabel: string = 'Previous page';
63
+ export let nextPageLabel: string = 'Next page';
55
64
 
56
65
  // Consts
57
66
  const initialContainerWidth = 500; // Default width during loading
@@ -531,11 +540,11 @@
531
540
  <!-- Always render loading or error state on top of content -->
532
541
  {#if !mounted || loading || (!isCanvasReady && !error)}
533
542
  <div class="text-[hsl(var(--grey5))] p-6 absolute z-10 widgetic-colors flex items-center justify-center inset-0">
534
- Loading PDF...
543
+ {loadingLabel}
535
544
  </div>
536
545
  {:else if error}
537
546
  <div class="pdf-error-overlay bg-[hsl(var(--grey1))] text-[hsl(var(--grey2))] inset-4 px-4 absolute z-10 flex flex-col items-center justify-center">
538
- <p class="text-[hsl(var(--error))] mb-2">Failed to load PDF</p>
547
+ <p class="text-[hsl(var(--error))] mb-2">{failedLabel}</p>
539
548
  <p class="text-[hsl(var(--grey5))] text-sm">{error}</p>
540
549
  </div>
541
550
  {/if}
@@ -546,18 +555,18 @@
546
555
  <!-- Page Navigation Bar -->
547
556
  <div class="page-navigation-bar flex items-center justify-center h-[54px] widgetic-colors border-b border-[hsl(var(--grey2))] w-full">
548
557
  <div class="flex items-center gap-3 w-full max-w-[200px] justify-between px-0">
549
- <button class="pdf-nav-button h-9 w-9 flex items-center justify-center rounded-small bg-white border-[hsl(var(--grey2))] transition-colors group disabled:cursor-not-allowed" on:click={onPrevPage} disabled={pageNum <= 1}>
558
+ <button class="pdf-nav-button h-9 w-9 flex items-center justify-center rounded-small bg-white border-[hsl(var(--grey2))] transition-colors group disabled:cursor-not-allowed" on:click={onPrevPage} disabled={pageNum <= 1} aria-label={previousPageLabel}>
550
559
  <IconChevronLeft
551
- class="!text-[hsl(var(--grey5))] group-hover:!text-[hsl(var(--grey7))]"
560
+ class="!text-[hsl(var(--grey5))] group-hover:!text-[hsl(var(--grey7))] rtl:-scale-x-100"
552
561
  style="height: 20px !important; min-width: 20px !important; max-width: 20px !important; max-height: 20px !important;"
553
562
  />
554
563
  </button>
555
564
 
556
- <span class="subtext text-[hsl(var(--grey7))]">Page {pageNum} of {pageCount || '?'}</span>
565
+ <span class="subtext text-[hsl(var(--grey7))]">{fillMessage(pageLabel, { page: pageNum, total: pageCount || '?' })}</span>
557
566
 
558
- <button class="pdf-nav-button h-9 w-9 flex items-center justify-center rounded-small bg-white border-[hsl(var(--grey2))] transition-colors group disabled:cursor-not-allowed" on:click={onNextPage} disabled={pageNum >= pageCount}>
567
+ <button class="pdf-nav-button h-9 w-9 flex items-center justify-center rounded-small bg-white border-[hsl(var(--grey2))] transition-colors group disabled:cursor-not-allowed" on:click={onNextPage} disabled={pageNum >= pageCount} aria-label={nextPageLabel}>
559
568
  <IconChevronRight
560
- class="!text-[hsl(var(--grey5))] group-hover:!text-[hsl(var(--grey7))]"
569
+ class="!text-[hsl(var(--grey5))] group-hover:!text-[hsl(var(--grey7))] rtl:-scale-x-100"
561
570
  style="height: 20px !important; min-width: 20px !important; max-width: 20px !important; max-height: 20px !important;"
562
571
  />
563
572
  </button>
@@ -591,7 +600,7 @@
591
600
  content: '';
592
601
  position: absolute;
593
602
  top: 0;
594
- left: 0;
603
+ inset-inline-start: 0;
595
604
  width: 100%;
596
605
  height: 100%;
597
606
  border: 1px solid hsl(var(--grey2));
@@ -16,6 +16,11 @@ declare const PDFViewer: $$__sveltets_2_IsomorphicComponent<{
16
16
  style?: string;
17
17
  url?: string;
18
18
  enableCacheClearing?: boolean;
19
+ loadingLabel?: string;
20
+ failedLabel?: string;
21
+ pageLabel?: string;
22
+ previousPageLabel?: string;
23
+ nextPageLabel?: string;
19
24
  }, {
20
25
  error: CustomEvent<{
21
26
  message: string;
@@ -62,7 +62,7 @@
62
62
  <div class="group relative w-full">
63
63
  <div class="relative overflow-hidden rounded-small border border-[hsl(var(--grey2))] bg-[hsl(var(--grey1))] transition-colors duration-200 hover:border-[hsl(var(--grey2))]">
64
64
  <div class="relative w-full flex items-center">
65
- <div class="absolute left-2 flex items-center pointer-events-none">
65
+ <div class="absolute start-2 flex items-center pointer-events-none">
66
66
  <IconSearch class="h-5 w-5 text-[hsl(var(--grey5))] transition-colors duration-300 {value ? 'text-[hsl(var(--grey7))]' : 'group-hover:text-[hsl(var(--grey7))] group-focus-within:text-[hsl(var(--grey7))]'}" />
67
67
  </div>
68
68
  <input
@@ -79,7 +79,7 @@
79
79
  onKeydown(e);
80
80
  }}
81
81
  onpaste={onPaste}
82
- class="w-full h-[38px] border-0 bg-transparent focus:outline-hidden focus:ring-0 focus-visible:ring-0 pl-9 pr-8 subtext
82
+ class="w-full h-[38px] border-0 bg-transparent focus:outline-hidden focus:ring-0 focus-visible:ring-0 ps-9 pe-8 subtext
83
83
  transition-all duration-300
84
84
  {value ? 'text-[hsl(var(--grey7))]' : 'text-[hsl(var(--grey3))] placeholder:text-[hsl(var(--grey3))] placeholder:transition-colors placeholder:duration-300 group-hover:text-[hsl(var(--grey7))] group-hover:placeholder:text-[hsl(var(--grey7))] group-focus-within:text-[hsl(var(--grey7))] group-focus-within:placeholder:text-[hsl(var(--grey7)))'}
85
85
  {inputClass}"
@@ -87,7 +87,7 @@
87
87
  {#if value}
88
88
  <button
89
89
  type="button"
90
- class="absolute right-2 h-6 w-6 inline-flex items-center justify-center rounded-full hover:bg-[hsl(var(--grey2))] transition-all duration-300 widgetic-colors"
90
+ class="absolute end-2 h-6 w-6 inline-flex items-center justify-center rounded-full hover:bg-[hsl(var(--grey2))] transition-all duration-300 widgetic-colors"
91
91
  onclick={onClear}
92
92
  >
93
93
  <X class="h-4 w-4 text-[hsl(var(--grey7))] transition-colors duration-300 hover:text-[hsl(var(--grey7))] widgetic-colors" />
@@ -7,13 +7,16 @@
7
7
  import { mode } from 'mode-watcher';
8
8
  let { hex = $bindable() }: ColorpickerProps = $props();
9
9
  import ColorPicker from 'svelte-awesome-color-picker';
10
+ import { useI18n } from './i18n/context.js';
11
+
12
+ const { t } = useI18n();
10
13
  </script>
11
14
 
12
15
  <div class:dark={$mode === 'dark'}>
13
16
  <ColorPicker
14
17
  bind:hex
15
18
  sliderDirection="vertical"
16
- label="Pick a color"
19
+ label={$t('shadEditor.colorPicker.label')}
17
20
  isTextInput={false}
18
21
  isAlpha
19
22
  --picker-indicator-size="1rem"
@@ -1 +1,20 @@
1
- export declare const ImageExtension: import("@tiptap/core").Node<import("@tiptap/extension-image").ImageOptions, any>;
1
+ import { type ImageOptions } from '@tiptap/extension-image';
2
+ import type { Readable } from 'svelte/store';
3
+ import type { Translate } from '../../../../i18n/index.js';
4
+ import type { ShadEditorMessageKey } from '../../i18n/messages.js';
5
+ import type { TextDirection } from '../../../textDirection.js';
6
+ export interface ImageExtensionOptions {
7
+ /**
8
+ * The words of the editor (`language.t` of the editor component). A node view is mounted by TipTap outside the
9
+ * editor's component tree, where Svelte's context does not reach, so the words travel in the options of the extension.
10
+ * English when left out.
11
+ */
12
+ translate: Readable<Translate<ShadEditorMessageKey>> | null;
13
+ /**
14
+ * The way the screen around the editor is read (`textDirection` of the editor component). It travels in the options for
15
+ * the same reason as `translate`: the menu of the image bar is drawn by a component TipTap mounts outside the editor's
16
+ * tree, so it would not find the direction in Svelte's context. Left to right when left out.
17
+ */
18
+ textDirection: Readable<TextDirection> | null;
19
+ }
20
+ export declare const ImageExtension: import("@tiptap/core").Node<ImageOptions & ImageExtensionOptions, any>;
@@ -1,7 +1,10 @@
1
1
  import { SvelteNodeViewRenderer } from 'svelte-tiptap';
2
2
  import ImageExtendedComponent from '../image-extended-component.svelte';
3
- import Image from '@tiptap/extension-image';
3
+ import Image, {} from '@tiptap/extension-image';
4
4
  export const ImageExtension = Image.extend({
5
+ addOptions() {
6
+ return { ...this.parent?.(), translate: null, textDirection: null };
7
+ },
5
8
  addAttributes() {
6
9
  return {
7
10
  src: {
@@ -33,8 +33,10 @@
33
33
  }
34
34
  </script>
35
35
 
36
+ <!-- Code is read from left to right in every language, so a code block is a left to right island: its language and its copy button stay in the corners they are drawn in. -->
36
37
  <NodeViewWrapper
37
38
  class="code-wrapper group relative rounded bg-muted p-6 dark:bg-muted/20"
39
+ dir="ltr"
38
40
  draggable
39
41
  >
40
42
  <DropdownMenu.Root>
@@ -44,7 +46,7 @@
44
46
  variant: 'ghost',
45
47
  size: 'sm',
46
48
  class:
47
- 'absolute left-2 top-2 h-4 rounded px-1 py-2 text-xs capitalize text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100'
49
+ 'absolute start-2 top-2 h-4 rounded px-1 py-2 text-xs capitalize text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100'
48
50
  })}
49
51
  >{defaultLanguage}
50
52
  <ChevronDown class="!size-3" />
@@ -67,7 +69,7 @@
67
69
  </DropdownMenu.Root>
68
70
  <Button
69
71
  variant="ghost"
70
- class="absolute right-2 top-2 size-4 p-0 text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100"
72
+ class="absolute end-2 top-2 size-4 p-0 text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100"
71
73
  onclick={copyCode}
72
74
  >
73
75
  {#if isCopying}