@widgetic/design-system 0.5.14 → 0.5.16

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 +7 -2
  10. package/dist/components/modals/ActionModal.svelte.d.ts +2 -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 +55 -50
  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 +19 -2
package/README.md CHANGED
@@ -172,6 +172,173 @@ The global styles load the necessary `@font-face` rules. You can apply typograph
172
172
  <span class="subtext">Some smaller text.</span>
173
173
  ```
174
174
 
175
+ ### Words of a package (`@widgetic/design-system/i18n`)
176
+
177
+ A package that has words of its own (the File Browser, the editor, the canvas, the chat, the widget creator) keeps them in one catalog of English messages and lets the page that embeds it change them and choose the language. This is the translator and the Svelte context they all share, so every package reads a message, a plural and a number the same way.
178
+
179
+ ```ts
180
+ // my-package/src/lib/i18n.ts: the catalog, once
181
+ import { createI18n } from '@widgetic/design-system/i18n';
182
+
183
+ export const MY_MESSAGES = {
184
+ 'upload.clear': 'Clear',
185
+ 'toast.deleted': '{count, plural, one {Deleted # file} other {Deleted # files}}'
186
+ } as const;
187
+
188
+ export const { provideI18n, useI18n, translateEnglish } = createI18n({ catalog: MY_MESSAGES, label: 'MyPackage' });
189
+ ```
190
+
191
+ ```svelte
192
+ <!-- the root component of the package: the page's language, set once and again whenever the props change -->
193
+ <script lang="ts">
194
+ import { provideI18n } from './i18n';
195
+ export let locale: string | undefined = undefined;
196
+ export let messages: Record<string, string> | undefined = undefined;
197
+ const language = provideI18n({ locale, messages });
198
+ $: language.update({ locale, messages });
199
+ </script>
200
+ ```
201
+
202
+ ```svelte
203
+ <!-- any part below it -->
204
+ <script lang="ts">
205
+ import { useI18n } from './i18n';
206
+ const { t } = useI18n();
207
+ </script>
208
+ <button>{$t('upload.clear')}</button>
209
+ <p>{$t('toast.deleted', { count: 3 })}</p>
210
+ ```
211
+
212
+ - A message is text with `{name}` where a value goes and `{count, plural, =0 {…} one {…} other {…}}` for plurals (`#` is the number, written the way the locale writes numbers). `\{`, `\}`, `\#` and `\\` write the character itself.
213
+ - `messages` from the page replace the English text of any key of the catalog; a key it leaves out keeps its English. A key that does not exist, a value that is not text, a template that does not read, or one that asks for a value the message is never given is said once in the console, as `[MyPackage] …` with the nearest real key, and the English stays: a mistake of the page never stops the package.
214
+ - `locale` is a BCP 47 tag (`de`, `pt-BR`): it sets the plural rules and how numbers, dates and lists are written. A tag the browser does not know is said once and English is used.
215
+ - For a language written right to left (`ar`, `he`, `fa`, `ur` …) the translator puts every text value it inserts (a `{name}`) between two invisible marks, the Unicode isolates U+2068 and U+2069, so that the words around a name cannot turn the name around: a file name that starts with a digit, or has a hyphen, would be put in the wrong order otherwise. Numbers, the `#` of a plural and empty values get none, and a left to right language gets none. A screen reader does not say the marks; a test that compares what a screen says in such a language has to leave them out. `createTranslator` takes `isolateValues: true | false` to decide for itself; left out, it follows `locale`.
216
+ - `findMessageProblems(MY_MESSAGES, messages)` lists the same problems without showing anything (for a test or a build step of the page); `createTranslator` is the translator on its own, for code that has no component around it.
217
+ - Each package makes its own with `createI18n`, so two packages on one page (the File Browser inside the editor) each have their own words and never hear each other's.
218
+ - A package with others inside it (an editor that has the File Browser and a rich text editor) takes one flat `messages` from the page and gives each inner package its own keys: `const { slices, unclaimed } = splitMessages(messages, { editor: EDITOR_MESSAGES, fileBrowser: FILE_BROWSER_MESSAGES })`. `slices.editor` holds the entries whose key is in that catalog, and `unclaimed` the entries whose key is in none (a misspelt key): check those with `findMessageProblems` against the catalogs put together, so the page is told once and by the nearest real key.
219
+
220
+ #### Keeping every word in the catalog (`@widgetic/design-system/i18n/audit`)
221
+
222
+ For a package's tests only: it reads the source with the Svelte parser and runs in Node, so it must never be imported by code that runs in a page. Three audits run over a package's own source, so a new English sentence cannot slip into a component without somebody noticing.
223
+
224
+ ```ts
225
+ // my-package/tests/i18n.test.ts (node:test; the same calls work in vitest)
226
+ import assert from 'node:assert/strict';
227
+ import { test } from 'node:test';
228
+ import { auditCatalogUsage, auditDesignSystemWords, auditHardCodedText } from '@widgetic/design-system/i18n/audit';
229
+ import { MY_MESSAGES } from '../src/lib/i18n.ts';
230
+
231
+ const folder = new URL('../src/lib', import.meta.url).pathname;
232
+ const catalogFile = new URL('../src/lib/i18n.ts', import.meta.url).pathname;
233
+
234
+ test('no sentence is written into a component', () => {
235
+ const report = auditHardCodedText({
236
+ folder,
237
+ exclude: [catalogFile],
238
+ allowed: [{ file: 'Specimen.svelte', text: 'aA', reason: 'A type specimen, not a word.' }]
239
+ });
240
+ assert.deepEqual(report.loose, []); // one line for each text that is not in the catalog
241
+ assert.deepEqual(report.unusedAllowances, []); // an allowance whose text is gone: take it off
242
+ assert.deepEqual(report.allowancesWithoutReason, []); // a reason is at least twelve characters
243
+ });
244
+
245
+ test('the code and the catalog agree', () => {
246
+ const report = auditCatalogUsage({ folder, catalog: MY_MESSAGES, exclude: [catalogFile] });
247
+ assert.deepEqual(report.unknownKeys, []); // asked for, but not in the catalog
248
+ assert.deepEqual(report.wrongValues, []); // a value the message needs and is not given, or one it never uses
249
+ assert.deepEqual(report.unusedKeys, []); // in the catalog, and nothing asks for it
250
+ assert.deepEqual(report.messageProblems, []); // a message that does not parse
251
+ assert.deepEqual(report.oddKeys, []); // not `group.name` in camelCase
252
+ });
253
+
254
+ test('every design system component that shows words is given all of them', () => {
255
+ const report = auditDesignSystemWords({
256
+ folder,
257
+ componentsWithWords: { ActionModal: ['closeLabel'], Breadcrumbs: ['backLabel', 'rootLabel'] },
258
+ withoutWords: ['Button', 'Tooltip']
259
+ });
260
+ assert.deepEqual(report.missing, []); // a component used without one of its words
261
+ assert.deepEqual(report.unknown, []); // a component the package imports that is in neither list
262
+ assert.deepEqual(report.stale, []); // a name in a list that the package no longer imports
263
+ });
264
+ ```
265
+
266
+ - What counts as a word: text in the markup, the attributes that carry words (`title`, `aria-label`, `alt`, `placeholder`, `label`, …), the properties and variables with a word's name (`label`, `message`, `errorMessage`, …) and the strings a listed function returns. Pass `textAttributes`, `textProperties`, `textFunctions` and `messageLookups` to add what a package calls things; they add to the defaults, they never replace them. Pass `messageKeys` (the keys of the catalog) when a table of keys sits in a property that is also a word's name (`{ text: 'chat.contextType.text' }`): a string that is exactly a key is the name of a message, not words.
267
+ - A text a visitor never reads as a word (a CSS unit, a type specimen, English meant for the host page's code) goes into `allowed` with the reason; the audit fails when the text is gone from its file, so the list never outlives the code.
268
+ - A message is asked for with `$t('group.name')`, `t(…)`, `translate(…)` or `translateEnglish(…)`; a package that has its own function says where the key and the values are with `lookups: { messageForVisitor: { key: 2, values: null } }`. A list of descriptors that carry a key writes it as `messageKey` (and the values as `messageValues`), and the audit checks it like a call. A key built in code (`` $t(`filter.${kind}`) ``) is checked as a prefix: at least one key of the catalog must start with `filter.`.
269
+
270
+ ### Right to left (`@widgetic/design-system/i18n` and `components/textDirection`)
271
+
272
+ A screen that can be read from right to left says so once, and the components below it follow. Nothing changes for a screen that says nothing: a component with no provider above it is left to right, as every component here has always been.
273
+
274
+ - `resolveTextDirection(locale)` answers `'rtl'` or `'ltr'` for a language tag. It reads the script the language is written in (`ar` and `ar-EG` are Arabic, `he` is Hebrew, `pa-PK` is Arabic script but `pa-IN` is Gurmukhi, `ar-Latn` is Latin and so left to right), and a short list of languages is only the fallback for a browser without `Intl.Locale`. A blank, missing or unreadable tag is `'ltr'`, and it never throws. `resolveLanguageTag(locale)` gives the `lang` of the same tag in its canonical spelling (`pt_BR` becomes `pt-BR`, a blank one is `en`, one that cannot be read is `undefined`: leave `lang` off).
275
+ - The root component of a screen puts the direction in the context, from a store that follows its page's language, and a component that wraps a library reads it:
276
+
277
+ ```svelte
278
+ <!-- the root component of the package -->
279
+ <script lang="ts">
280
+ import { writable } from 'svelte/store';
281
+ import { provideTextDirection, readTextDirection, type TextDirection } from '@widgetic/design-system/components/textDirection';
282
+
283
+ export let dir: TextDirection = 'ltr';
284
+ const textDirection = writable<TextDirection>(readTextDirection(dir)); // anything but 'rtl' is left to right
285
+ provideTextDirection(textDirection);
286
+ $: textDirection.set(readTextDirection(dir));
287
+ </script>
288
+
289
+ <div {dir}>…</div>
290
+ ```
291
+
292
+ - Why a context and not only the `dir` attribute: the scroll area and the menus of `bits-ui` write `dir="ltr"` on their own element unless they are given the direction, and that one attribute turns a whole screen back from the inside. The scroll area, tooltip, popover, select, dropdown menu, hover card, breadcrumbs and toaster of this package read the context, so a screen that provides it turns around as a whole, and the toaster opens in the bottom corner where a line starts.
293
+ - `dirForUserText(direction)` is the `dir` for text a visitor wrote (the name of a file): `'auto'` on a right to left screen, so a Latin name is not put in the wrong order in a row of Arabic ones, and `undefined` on a left to right one.
294
+ - `shareTextDirection(givenDirection)` is for the root of a package that takes a `dir` prop which may be left out (`givenDirection` is a store that holds the prop): a `dir` that is given is the direction of that screen whatever the screen above it says, one that is left out follows the screen above it (and is left to right when there is none), and everything below reads the answer from the context. It returns the store of the answer, for the component's own decisions (a position worked out in pixels, an arrow key). `useTextDirection()` is what a component reads it with (left to right when nothing above it provides one) and `useOptionalTextDirection()` tells "left to right because the screen says so" from "nobody said anything".
295
+ - `sideAtStart(direction)` and `sideAtEnd(direction)` give the `side` (`'left'` or `'right'`) of a pop-up that opens at the start or the end of the line: the library names the sides of a pop-up by places on the screen, which do not turn around.
296
+ - Write placement in logical terms: `ms-`/`me-`, `ps-`/`pe-`, `start-`/`end-`, `text-start`/`text-end`, `border-s`/`border-e`, `rounded-s`/`rounded-e`/`rounded-ss`, `float-start` in Tailwind; `margin-inline-start`, `inset-inline-end`, `border-start-start-radius` in CSS. Mirror an arrow that means "forward" with `rtl:-scale-x-100`.
297
+
298
+ #### Keeping it so (`@widgetic/design-system/rtl/audit`)
299
+
300
+ For a package's tests only (it reads files with `node:fs`, so it must never be imported by code that runs in a page). `auditPhysicalPlacement` reads every `.svelte`, `.ts`, `.js` and `.css` file under a folder (not `node_modules`, `dist`, `.d.ts` files or tests) and reports each class, style and CSS rule that puts something on a physical side, with the logical one to write instead:
301
+
302
+ ```ts
303
+ // my-package/tests/rtl.test.ts
304
+ import { auditPhysicalPlacement } from '@widgetic/design-system/rtl/audit';
305
+
306
+ const report = auditPhysicalPlacement({
307
+ folder: new URL('../src', import.meta.url).pathname,
308
+ skipped: [{ path: 'lib/board/', reason: 'A drawing board: its left and right are the picture, not the language.' }]
309
+ });
310
+ expect(report.filesRead).toBeGreaterThan(0); // it looked at something
311
+ expect(report.lines).toEqual([]); // one line for each physical side
312
+ expect(report.unusedSkips).toEqual([]); // a part that was named and is gone: take it off
313
+ ```
314
+
315
+ A physical side that is meant (a slider's track, a pixel offset that is the same in both directions) says why with the note `rtl-ok` on the same line or the line above.
316
+
317
+ What the audit reads besides classes and properties: a **shorthand that names the sides by their place** (`margin`, `padding`, `inset`, `scroll-margin`, `scroll-padding`, `border-width`, `border-color`, `border-style` and `border-radius`: `padding: 4px 6px 4px 0` is top, right, bottom, left) is reported when its right and left (or its corners) differ, with the logical property to write (`padding-inline: 0 6px`); one that is the same at both ends is not. A comment hides a line only where it is a comment: a `/*` inside a quoted attribute, an image path or a regular expression does not hide what follows it.
318
+
319
+ What it cannot see (found by looking at a screen in Arabic, not by the audit, so look at one): a box that is put in place **in pixels** from the left (a dock, a pop-up, a divider) has to be mirrored where it is placed; an element that says `position: relative` itself is moved from where it sits in the flow, which on a right to left screen is its right edge, so a box placed with `left:` says `position: absolute` inline; and `dir="auto"` on a field with **nothing in it yet** reads left to right (there is no first letter), so write it only once there is a value and let an empty field take the direction of the screen.
320
+
321
+ ### Rich text editor (`@widgetic/design-system/shad-editor`)
322
+
323
+ `ShadEditor` is a TipTap editor with a toolbar. It shows English until the page gives it words in another language:
324
+
325
+ ```svelte
326
+ <script lang="ts">
327
+ import { ShadEditor, checkShadEditorMessages } from '@widgetic/design-system/shad-editor';
328
+
329
+ let html = $state('<p>Hello</p>');
330
+ const messages = { 'shadEditor.toolbar.bold': 'Fett (⌘B)', 'shadEditor.table.insert': 'Tabelle einfügen' };
331
+ console.log(checkShadEditorMessages(messages)); // [] when every key exists and every message reads
332
+ </script>
333
+
334
+ <ShadEditor bind:content={html} locale="de" {messages} />
335
+ ```
336
+
337
+ - `SHAD_EDITOR_MESSAGES` is the catalog, 85 messages: the tooltip of every toolbar button (the keyboard shortcut is part of the message, so a page that wants `Ctrl` for its visitors writes it there), the menus of text styles and alignment, the link, image, colour and highlight pop-ups, the table menu, the names of the quick colours, search and replace, and the bar over an image in the text. A key the page leaves out keeps its English.
338
+ - `checkShadEditorMessages(messages)` lists the keys that do not exist (with the nearest real one), the values that are not text and the templates that do not read, without showing anything.
339
+ - A change of `locale` or `messages` reaches the toolbar, the menus and the images that are already in the text, without making the editor again.
340
+ - Two words are not the editor's to change: the button of the file chooser in the image pop-up is the browser's own, and the colour picker names its window `color picker` in English.
341
+
175
342
  ## Troubleshooting
176
343
 
177
344
  ### File Case Sensitivity Issues (macOS/Windows Teams)
@@ -17,6 +17,10 @@
17
17
  * helpUrl="/embed"
18
18
  * />
19
19
  * ```
20
+ *
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.
20
24
  */
21
25
  -->
22
26
  <script lang="ts">
@@ -33,6 +37,10 @@ const dispatch = createEventDispatcher();
33
37
  // import { toast, Button, Tooltip, Portal } from '@widgetic/design-system/components';
34
38
  import { toast, Button, Tooltip, Portal } from './';
35
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();
36
44
 
37
45
  /**
38
46
  * COMPONENT PROPS
@@ -48,6 +56,12 @@ export let showHelp = true;
48
56
  export let monospaceContent = true;
49
57
  export let showStandaloneCopyButton = false;
50
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';
51
65
  export let showButtonText = true;
52
66
  export let buttonClass = '';
53
67
  export let showInternalToast = true;
@@ -127,15 +141,15 @@ async function handleCopyButtonClick(e: MouseEvent) {
127
141
  try {
128
142
  await navigator.clipboard.writeText(content);
129
143
  if (showInternalToast) {
130
- showToast('success', 'Copied to clipboard', {
131
- description: 'Content copied successfully.',
144
+ showToast('success', copiedTitle, {
145
+ description: copiedDescription,
132
146
  });
133
147
  }
134
148
  dispatch('copy', { content });
135
149
  } catch (err) {
136
150
  if (showInternalToast) {
137
- showToast('error', 'Copy failed', {
138
- description: 'Please try manual selection',
151
+ showToast('error', copyFailedTitle, {
152
+ description: copyFailedDescription,
139
153
  });
140
154
  }
141
155
  dispatch('copyError', { error: err });
@@ -217,7 +231,7 @@ function showToast(type: 'success' | 'error', message: string, options = {}) {
217
231
  {#if showHelpTooltip}
218
232
  <Portal target="editor-tooltips">
219
233
  <Tooltip.Content
220
- side="right"
234
+ side={sideAtEnd($textDirection)}
221
235
  sideOffset={5}
222
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"
223
237
  >
@@ -247,7 +261,7 @@ function showToast(type: 'success' | 'error', message: string, options = {}) {
247
261
  >
248
262
  <IconCopy class="w-5 h-5" style="width: 20px !important; height: 20px !important;" />
249
263
  {#if showButtonText}
250
- <span class="ml-2">{standaloneCopyButtonText}</span>
264
+ <span class="ms-2">{standaloneCopyButtonText}</span>
251
265
  {/if}
252
266
  </Button>
253
267
  </Tooltip.Trigger>
@@ -255,7 +269,7 @@ function showToast(type: 'success' | 'error', message: string, options = {}) {
255
269
  {#if showCopyTooltip}
256
270
  <Portal target="editor-tooltips">
257
271
  <Tooltip.Content
258
- side="left"
272
+ side={sideAtStart($textDirection)}
259
273
  sideOffset={5}
260
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"
261
275
  >
@@ -272,7 +286,7 @@ function showToast(type: 'success' | 'error', message: string, options = {}) {
272
286
  <Button
273
287
  variant="ghost"
274
288
  size="icon"
275
- class="h-10 w-10 {headerBackgroundColor ? headerBackgroundColor : 'hover:bg-white'} active:scale-95 transition-transform duration-150 mr-[-4px]"
289
+ class="h-10 w-10 {headerBackgroundColor ? headerBackgroundColor : 'hover:bg-white'} active:scale-95 transition-transform duration-150 me-[-4px]"
276
290
  onclick={handleCopyButtonClick}
277
291
  onmouseenter={handleCopyMouseEnter}
278
292
  onmousemove={handleMouseMove}
@@ -293,7 +307,7 @@ function showToast(type: 'success' | 'error', message: string, options = {}) {
293
307
  {#if showCopyTooltip}
294
308
  <Portal target="editor-tooltips">
295
309
  <Tooltip.Content
296
- side="left"
310
+ side={sideAtStart($textDirection)}
297
311
  sideOffset={5}
298
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"
299
313
  >
@@ -312,7 +326,7 @@ function showToast(type: 'success' | 'error', message: string, options = {}) {
312
326
  <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
313
327
  <div
314
328
  bind:this={contentElement}
315
- class="copyable-content-area text-[14px] w-full text-left bg-white relative {monospaceContent ? 'font-mono' : ''}"
329
+ class="copyable-content-area text-[14px] w-full text-start bg-white relative {monospaceContent ? 'font-mono' : ''}"
316
330
  style="
317
331
  padding: 16px;
318
332
  min-height: 80px;
@@ -396,7 +410,7 @@ function showToast(type: 'success' | 'error', message: string, options = {}) {
396
410
  :global(.copyable-header-toast) {
397
411
  position: fixed !important;
398
412
  bottom: 20px !important;
399
- right: 20px !important;
413
+ inset-inline-end: 20px !important;
400
414
  top: auto !important;
401
415
  display: flex !important;
402
416
  align-items: center !important;
@@ -24,6 +24,10 @@ declare const CopyableHeader: $$__sveltets_2_IsomorphicComponent<{
24
24
  monospaceContent?: boolean;
25
25
  showStandaloneCopyButton?: boolean;
26
26
  standaloneCopyButtonText?: string;
27
+ /** The pop-up after a copy that worked: its title and the line under it. */ copiedTitle?: string;
28
+ copiedDescription?: string;
29
+ /** The pop-up after a copy that failed: its title and the line under it. */ copyFailedTitle?: string;
30
+ copyFailedDescription?: string;
27
31
  showButtonText?: boolean;
28
32
  buttonClass?: string;
29
33
  showInternalToast?: boolean;
@@ -83,7 +83,7 @@
83
83
  <p class="subtext" style="color: {textColor}">{message}</p>
84
84
 
85
85
  {#if $$slots.action}
86
- <div class="ml-2">
86
+ <div class="ms-2">
87
87
  <slot name="action" />
88
88
  </div>
89
89
  {/if}
@@ -23,7 +23,7 @@
23
23
  <div class="relative widgetic-colors">
24
24
  <div class="relative">
25
25
  <Input
26
- class="pr-[32px]"
26
+ class="pe-[32px]"
27
27
  bind:value
28
28
  {name}
29
29
  type={showPassword ? 'text' : 'password'}
@@ -36,7 +36,7 @@
36
36
  <!-- Toggle Password Visibility -->
37
37
  <button
38
38
  type="button"
39
- class="absolute top-1/2 -translate-y-1/2 right-3 cursor-pointer text-[hsl(var(--sage-green))] hover:text-[hsl(var(--dark))]"
39
+ class="absolute top-1/2 -translate-y-1/2 end-3 cursor-pointer text-[hsl(var(--sage-green))] hover:text-[hsl(var(--dark))]"
40
40
  tabindex="-1"
41
41
  on:click={() => showPassword = !showPassword}
42
42
  >
@@ -26,7 +26,7 @@ export let element: HTMLElement | null = null;
26
26
  <ScrollArea bind:ref={element}
27
27
  class={(className ? className + ' ' : '') + "scroll-area-container w-full h-full " +
28
28
  "widgetic-radius widgetic-colors select-text " +
29
- "**:!data-scroll-area-viewport:pr-(--scrollbar-width)"}
29
+ "**:!data-scroll-area-viewport:pe-(--scrollbar-width)"}
30
30
  style="{style} {scrollOutside ? `width: calc(100% + ${scrollbarWidth}px) !important;` : ''}
31
31
  --scrollbar-width: {scrollbarWidth}px; --scrollbar-padding: {scrollbarWidth/4}px; "
32
32
  {orientation}
@@ -38,7 +38,7 @@ export let element: HTMLElement | null = null;
38
38
  "[&>div]:bg-[hsl(var(--grey1))] hover:[&>div]:bg-[hsl(var(--grey2))] " +
39
39
  "[&>div]:transition-colors [&>div]:duration-250"}
40
40
  >
41
- <div class="actual-viewport {viewPortClass ? viewPortClass + ' ' : ''} absolute inset-0 z-[-1]" style="margin-right: {scrollbarWidth}px;">
41
+ <div class="actual-viewport {viewPortClass ? viewPortClass + ' ' : ''} absolute inset-0 z-[-1]" style="margin-inline-end: {scrollbarWidth}px;">
42
42
  <!-- <div class="viewport-inner absolute border border-red-500" style="inset: {contentPadding}px;"></div> -->
43
43
  </div>
44
44
  <slot/>
@@ -285,10 +285,10 @@
285
285
  </span>
286
286
  </Tooltip.Trigger>
287
287
  <Tooltip.Content
288
- side="right"
288
+ side={/* rtl-ok: the help text is put where the pointer is, by the style below */ 'right'}
289
289
  sideOffset={5}
290
290
  class="bg-gray-900 text-white z-99999 overflow-hidden rounded-md px-3 py-1.5 text-sm shadow-md"
291
- style="position: fixed; left: {mouseX + 20}px; top: {mouseY}px; z-index: 999999;"
291
+ style="position: fixed; left: {mouseX + 20}px; top: {mouseY}px; z-index: 999999; /* rtl-ok: the help text follows the pointer, measured from the left edge of the page */"
292
292
  >
293
293
  <p class="text-sm">{header.helpText}</p>
294
294
  </Tooltip.Content>
@@ -9,6 +9,7 @@ export { default as BrowserInputController } from './ic/browser/BrowserInputCont
9
9
  export { default as ActionModal } from './modals/ActionModal.svelte';
10
10
  export { default as BaseModal } from './modals/BaseModal.svelte';
11
11
  export { default as NewItemDialog } from './modals/NewItemDialog.svelte';
12
+ export type { NewItemDialogMessages, RenameDialogMessages } from './modals/messages';
12
13
  export { default as Portal } from './modals/Portal.svelte';
13
14
  export { default as RenameDialog } from './modals/RenameDialog.svelte';
14
15
  export { Breadcrumbs, type BreadcrumbsProps } from './navigation';
@@ -23,11 +23,15 @@
23
23
  export let confirmLabel: string = 'Confirm';
24
24
  export let onConfirm: () => Promise<any> = async () => {};
25
25
  export let cancelLabel: string = 'Cancel';
26
+ // The words a screen reader says for the close button (see BaseModal)
27
+ export let closeLabel: string = 'Close modal';
26
28
  export let onCancel: () => void = () => {};
27
29
  export let width: string = '410px';
28
30
  export let height: string = '168px';
29
31
  export let target: string = 'action-confirmation-modal'; // target for Portal
30
32
  export let isConfirmDisabled: boolean = false;
33
+ /** When false, a failed confirm can leave the dialog open. Callers close it themselves. */
34
+ export let closeOnConfirm: boolean = true;
31
35
  export let confirmVariant: 'primary' | 'attention' = 'primary';
32
36
  export let actionType: string = 'confirm';
33
37
 
@@ -75,8 +79,8 @@
75
79
  // Dispatch a confirmation event with all items
76
80
  dispatch('confirm', { items });
77
81
 
78
- // Close the modal immediately to allow user to continue working
79
- isOpen = false;
82
+ // Callers that show an error inside the dialog set closeOnConfirm to false.
83
+ if (closeOnConfirm) isOpen = false;
80
84
  }
81
85
 
82
86
  // Support for restoring modal state
@@ -120,6 +124,7 @@
120
124
  width={width}
121
125
  height={height}
122
126
  titlePadding="small"
127
+ {closeLabel}
123
128
  {modalType}
124
129
  {modalData}
125
130
  on:restore={handleRestore}
@@ -26,11 +26,13 @@ declare const ActionModal: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_Pro
26
26
  confirmLabel?: string;
27
27
  onConfirm?: () => Promise<any>;
28
28
  cancelLabel?: string;
29
+ closeLabel?: string;
29
30
  onCancel?: () => void;
30
31
  width?: string;
31
32
  height?: string;
32
33
  target?: string;
33
34
  isConfirmDisabled?: boolean;
35
+ /** When false, a failed confirm can leave the dialog open. Callers close it themselves. */ closeOnConfirm?: boolean;
34
36
  confirmVariant?: "primary" | "attention";
35
37
  actionType?: string;
36
38
  modalType?: string;
@@ -33,6 +33,8 @@
33
33
  export let enableSpaceKey = false;
34
34
  export let isSelected = false;
35
35
  export let showScroll = false;
36
+ // The words a screen reader says for the close button. A page that shows the modal in another language passes its own.
37
+ export let closeLabel: string = 'Close modal';
36
38
 
37
39
  // children prop(svelte 5) to replace the traditional slot(svelte 4)
38
40
  export let children: any;
@@ -286,13 +288,13 @@
286
288
  ></div>
287
289
 
288
290
  <!-- Close button -->
289
- <div class="modal-close-bt-container absolute top-4 right-4 z-1004" transition:fade={{ duration: transitionDuration, easing }}>
291
+ <div class="modal-close-bt-container absolute top-4 end-4 z-1004" transition:fade={{ duration: transitionDuration, easing }}>
290
292
  <!-- svelte-ignore a11y-click-events-have-key-events -->
291
293
  <!-- svelte-ignore a11y-no-static-element-interactions -->
292
294
  <div
293
295
  class="close-button widgetic-colors p-0 w-10 h-10 bg-[hsl(var(--dark))] hover:bg-[hsl(var(--grey6))] transition-colors duration-200 rounded-full flex items-center justify-center cursor-pointer"
294
296
  on:click|stopPropagation|preventDefault={handleClose}
295
- aria-label="Close modal"
297
+ aria-label={closeLabel}
296
298
  role="button"
297
299
  tabindex="0"
298
300
  >
@@ -25,6 +25,7 @@ declare const BaseModal: $$__sveltets_2_IsomorphicComponent<{
25
25
  enableSpaceKey?: boolean;
26
26
  isSelected?: boolean;
27
27
  showScroll?: boolean;
28
+ closeLabel?: string;
28
29
  children: any;
29
30
  }, {
30
31
  click: PointerEvent;
@@ -10,23 +10,12 @@
10
10
  // Import BaseModal as a default import using relative path, similar to RenameDialog
11
11
  import BaseModal from './BaseModal.svelte';
12
12
 
13
- // Name validation function
14
- function isValidName(name: string): boolean {
15
- if (!name || typeof name !== 'string') return false;
16
- const trimmed = name.trim();
17
- if (trimmed.length === 0) return false;
18
- return /^[a-zA-Z0-9\s]+$/.test(trimmed);
19
- }
20
-
21
- function getNameValidationError(name: string, itemType: string = 'name'): string | null {
22
- if (!name || typeof name !== 'string') return `${itemType} is required`;
23
- const trimmed = name.trim();
24
- if (trimmed.length === 0) return `${itemType} is required`;
25
- if (!isValidName(name)) {
26
- return `${itemType} can only contain letters, numbers, and spaces`;
27
- }
28
- return null;
29
- }
13
+ // Words of the dialog and the helper that puts values into them
14
+ import type { NewItemDialogMessages } from './messages';
15
+ import { fillMessage } from '../../utils/fillMessage';
16
+
17
+ // What a name may be made of: letters, numbers and white space of any script (see utils/itemName)
18
+ import { findItemNameProblem, isValidItemName, keepAllowedNameCharacters } from '../../utils/itemName';
30
19
 
31
20
  // Props
32
21
  let className = '';
@@ -43,6 +32,10 @@
43
32
  export let currentPathNames: string[] = [];
44
33
  export let confirmLabel = "Create";
45
34
  export let cancelLabel = "Cancel";
35
+ // The words a screen reader says for the close button (see BaseModal)
36
+ export let closeLabel = "Close modal";
37
+ // The words of the validation errors; a key that is left out keeps its English text (see ./messages)
38
+ export let messages: Partial<NewItemDialogMessages> = {};
46
39
  export let modalType: string = "newItem";
47
40
  export let modalWidth = "410px";
48
41
  export let modalHeight = "168px";
@@ -58,6 +51,19 @@
58
51
  // Compute title based on itemType if not provided
59
52
  $: displayTitle = title || `New ${itemType.charAt(0).toUpperCase() + itemType.slice(1)}`;
60
53
 
54
+ // The words of the validation errors: the page's own, or English built from the item type ("Folder name is required")
55
+ $: itemTypeCapitalized = itemType.charAt(0).toUpperCase() + itemType.slice(1);
56
+ $: text = {
57
+ invalidTitle: messages.invalidTitle ?? `Invalid ${itemType} name`,
58
+ required: messages.required ?? `${itemTypeCapitalized} name is required`,
59
+ invalidCharacters:
60
+ messages.invalidCharacters ?? `${itemTypeCapitalized} name can only contain letters, numbers, and spaces`,
61
+ tooLong: fillMessage(
62
+ messages.tooLong ?? `${itemTypeCapitalized} name cannot exceed {max} characters`,
63
+ { max: maxLength }
64
+ )
65
+ };
66
+
61
67
  $: if (isOpen) {
62
68
  itemName = '';
63
69
  hasAttemptedSubmit = false;
@@ -66,15 +72,15 @@
66
72
 
67
73
  $: {
68
74
  const trimmedName = itemName.trim();
69
- isValid = trimmedName.length > 0 && itemName.length <= maxLength && isValidName(trimmedName);
75
+ isValid = trimmedName.length > 0 && itemName.length <= maxLength && isValidItemName(trimmedName);
70
76
 
71
77
  // Update real-time error message
72
78
  if (itemName.length > 0) {
73
79
  if (itemName.length > maxLength) {
74
- currentError = `${itemType.charAt(0).toUpperCase() + itemType.slice(1)} name cannot exceed ${maxLength} characters`;
80
+ currentError = text.tooLong;
75
81
  } else {
76
- const nameError = getNameValidationError(trimmedName, itemType.charAt(0).toUpperCase() + itemType.slice(1) + ' name');
77
- currentError = nameError || '';
82
+ const nameProblem = findItemNameProblem(trimmedName);
83
+ currentError = nameProblem ? text[nameProblem] : '';
78
84
  }
79
85
  } else {
80
86
  currentError = '';
@@ -85,18 +91,18 @@
85
91
  hasAttemptedSubmit = true;
86
92
 
87
93
  // Validate name format
88
- const nameError = getNameValidationError(itemName, itemType.charAt(0).toUpperCase() + itemType.slice(1) + ' name');
89
- if (nameError) {
90
- toast.error(`Invalid ${itemType} name`, {
91
- description: nameError
94
+ const nameProblem = findItemNameProblem(itemName);
95
+ if (nameProblem) {
96
+ toast.error(text.invalidTitle, {
97
+ description: text[nameProblem]
92
98
  });
93
99
  return;
94
100
  }
95
101
 
96
102
  if (itemName.length > maxLength) {
97
103
  // Show error toast for name too long
98
- toast.error(`Invalid ${itemType} name`, {
99
- description: `${itemType} name cannot exceed ${maxLength} characters`
104
+ toast.error(text.invalidTitle, {
105
+ description: text.tooLong
100
106
  });
101
107
  return;
102
108
  }
@@ -106,6 +112,12 @@
106
112
  handleClose();
107
113
  }
108
114
 
115
+ // Only allow letters, numbers, and spaces (of any script): anything else typed or pasted is dropped
116
+ function keepAllowedCharacters(inputElement: HTMLInputElement) {
117
+ inputElement.value = keepAllowedNameCharacters(inputElement.value);
118
+ itemName = inputElement.value;
119
+ }
120
+
109
121
  function handleClose() {
110
122
  isOpen = false;
111
123
  itemName = '';
@@ -131,6 +143,7 @@
131
143
  {modalType}
132
144
  modalData={combinedModalData}
133
145
  title={showTitle ? displayTitle : undefined}
146
+ {closeLabel}
134
147
  {target}
135
148
  on:restore={(event: CustomEvent<any>) => {
136
149
  if (event.detail) {
@@ -158,10 +171,15 @@
158
171
  }}
159
172
  oninput={(e) => {
160
173
  const target = e.target as HTMLInputElement;
161
- // Only allow letters, numbers, and spaces
162
- target.value = target.value.replace(/[^a-zA-Z0-9\s]/g, '');
163
- itemName = target.value;
174
+ // An input method (Japanese, Chinese, Korean...) is still building a word: changing the field now would
175
+ // cancel the word. It is checked when the word is committed, in oncompositionend.
176
+ if ('isComposing' in e && e.isComposing) {
177
+ itemName = target.value;
178
+ return;
179
+ }
180
+ keepAllowedCharacters(target);
164
181
  }}
182
+ oncompositionend={(e) => keepAllowedCharacters(e.target as HTMLInputElement)}
165
183
  onfocus={(e) => {
166
184
  const target = e.target as HTMLInputElement;
167
185
  target.placeholder = "";
@@ -1,3 +1,4 @@
1
+ import type { NewItemDialogMessages } 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;
@@ -24,6 +25,8 @@ declare const NewItemDialog: $$__sveltets_2_IsomorphicComponent<{
24
25
  currentPathNames?: string[];
25
26
  confirmLabel?: string;
26
27
  cancelLabel?: string;
28
+ closeLabel?: string;
29
+ messages?: Partial<NewItemDialogMessages>;
27
30
  modalType?: string;
28
31
  modalWidth?: string;
29
32
  modalHeight?: string;