@widgetic/editor 4.0.10 → 4.1.0

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 (84) hide show
  1. package/README.md +242 -0
  2. package/dist/components/WidgetPreview.svelte +123 -86
  3. package/dist/components/WidgetPreview.svelte.d.ts +8 -0
  4. package/dist/components/color-select/ColorSelect.svelte +4 -4
  5. package/dist/components/editors/CompositionEditorApp.svelte +562 -0
  6. package/dist/components/editors/CompositionEditorApp.svelte.d.ts +95 -0
  7. package/dist/components/editors/EmbedEditor.svelte +33 -22
  8. package/dist/components/editors/EmbedEditor.svelte.d.ts +1 -1
  9. package/dist/components/editors/PropsEditor.svelte +267 -850
  10. package/dist/components/editors/PropsEditor.svelte.d.ts +10 -3
  11. package/dist/components/ic/AngleInputController.svelte +1 -1
  12. package/dist/components/ic/BrowserInputController.svelte +58 -31
  13. package/dist/components/ic/ColorPickerInputController.svelte +55 -21
  14. package/dist/components/ic/DateFieldInputController.svelte +34 -14
  15. package/dist/components/ic/DatePickerInputController.svelte +37 -21
  16. package/dist/components/ic/DropdownInputController.svelte +21 -9
  17. package/dist/components/ic/DropdownInputController.svelte.d.ts +1 -1
  18. package/dist/components/ic/FixedPositionInputController.svelte +12 -6
  19. package/dist/components/ic/FontInputController.svelte +100 -76
  20. package/dist/components/ic/InputControllerHeader.svelte +16 -9
  21. package/dist/components/ic/NumberControls.svelte +4 -4
  22. package/dist/components/ic/NumberInputController.svelte +6 -4
  23. package/dist/components/ic/PositionInputController.svelte +23 -15
  24. package/dist/components/ic/RangeCalendarInputController.svelte +36 -20
  25. package/dist/components/ic/RoundedCornersInputController.svelte +39 -26
  26. package/dist/components/ic/SliderInputController.svelte +7 -2
  27. package/dist/components/ic/SwitchInputController.svelte +12 -3
  28. package/dist/components/ic/TagsInputController.svelte +19 -4
  29. package/dist/components/ic/TextAreaInputController.svelte +79 -51
  30. package/dist/components/ic/TextAreaPreviewInputController.svelte +27 -72
  31. package/dist/components/ic/TextInputController.svelte +17 -1
  32. package/dist/components/ic/TimeInputController.svelte +11 -4
  33. package/dist/components/ic/VectorInputController.svelte +101 -70
  34. package/dist/components/ic/shared/ItemGrid.svelte +12 -4
  35. package/dist/components/ic/shared/RecentItemsSection.svelte +16 -11
  36. package/dist/components/ic/shared/RecentItemsSection.svelte.d.ts +1 -1
  37. package/dist/composition/compositionApi.d.ts +95 -0
  38. package/dist/composition/compositionApi.js +289 -0
  39. package/dist/composition/compositionEditorController.d.ts +263 -0
  40. package/dist/composition/compositionEditorController.js +746 -0
  41. package/dist/composition/compositionHelpers.d.ts +74 -0
  42. package/dist/composition/compositionHelpers.js +405 -0
  43. package/dist/i18n/index.d.ts +4 -0
  44. package/dist/i18n/index.js +4 -0
  45. package/dist/i18n/labels.d.ts +15 -0
  46. package/dist/i18n/labels.js +57 -0
  47. package/dist/i18n/language.d.ts +228 -0
  48. package/dist/i18n/language.js +34 -0
  49. package/dist/i18n/languageKit.d.ts +54 -0
  50. package/dist/i18n/languageKit.js +102 -0
  51. package/dist/i18n/messages.d.ts +241 -0
  52. package/dist/i18n/messages.js +253 -0
  53. package/dist/i18n/translate.d.ts +6 -0
  54. package/dist/i18n/translate.js +8 -0
  55. package/dist/index.d.ts +4 -0
  56. package/dist/index.js +8 -0
  57. package/dist/mediaLibraryContext.d.ts +10 -0
  58. package/dist/mediaLibraryContext.js +30 -0
  59. package/dist/utils/FontManager.d.ts +21 -5
  60. package/dist/utils/FontManager.js +62 -67
  61. package/dist/utils/VectorManager.d.ts +0 -1
  62. package/dist/utils/VectorManager.js +10 -12
  63. package/dist/utils/dockPlacement.d.ts +12 -0
  64. package/dist/utils/dockPlacement.js +13 -0
  65. package/dist/utils/editorStorage.d.ts +23 -0
  66. package/dist/utils/editorStorage.js +95 -0
  67. package/dist/utils/fieldDirection.d.ts +13 -0
  68. package/dist/utils/fieldDirection.js +25 -0
  69. package/dist/utils/fontUtils.js +5 -3
  70. package/dist/utils/htmlSafety.d.ts +21 -0
  71. package/dist/utils/htmlSafety.js +38 -0
  72. package/dist/utils/itemReorder.d.ts +90 -0
  73. package/dist/utils/itemReorder.js +354 -0
  74. package/dist/utils/recentColors.js +4 -2
  75. package/dist/utils/richTextPreview.d.ts +38 -0
  76. package/dist/utils/richTextPreview.js +207 -0
  77. package/dist/utils/scrollActivity.d.ts +14 -0
  78. package/dist/utils/scrollActivity.js +63 -0
  79. package/dist/utils/shadowDom.d.ts +67 -0
  80. package/dist/utils/shadowDom.js +211 -0
  81. package/dist/utils/vectorUtils.js +5 -3
  82. package/dist/utils/visibilityCondition.d.ts +40 -0
  83. package/dist/utils/visibilityCondition.js +535 -0
  84. package/package.json +7 -6
@@ -37,6 +37,13 @@
37
37
  <script lang="ts">
38
38
  // Svelte imports
39
39
  import { onMount, onDestroy } from 'svelte';
40
+ import { listenForScroll } from '../../utils/shadowDom';
41
+ import { useEditorLanguage } from '../../i18n/language';
42
+ import type { EditorMessageKey } from '../../i18n/messages';
43
+ import type { Translate } from '@widgetic/design-system/i18n';
44
+
45
+ // The words of the editor in the language of the page, and the language itself (how a month and a date are written).
46
+ const { t, locale } = useEditorLanguage();
40
47
 
41
48
  // Shadcn UI Components
42
49
  import { Button, Select } from '@widgetic/design-system/components';
@@ -65,12 +72,12 @@
65
72
  let placeholder = today(getLocalTimeZone());
66
73
  let currentValue: CalendarDate | undefined;
67
74
 
68
- // Month and year options setup
69
- const monthFmt = new DateFormatter('en-US', { month: 'long' });
70
- const monthOptions = [
71
- "January", "February", "March", "April", "May", "June",
72
- "July", "August", "September", "October", "November", "December"
73
- ].map((month, i) => ({ value: String(i + 1), label: month }));
75
+ // Month and year options setup: the names of the months are written by the language of the page
76
+ $: monthFmt = new DateFormatter($locale, { month: 'long' });
77
+ $: monthOptions = Array.from({ length: 12 }, (_, monthIndex) => ({
78
+ value: String(monthIndex + 1),
79
+ label: monthFmt.format(new Date(2021, monthIndex, 1))
80
+ }));
74
81
 
75
82
  const yearOptions = Array.from({ length: 100 }, (_, i) => {
76
83
  const year = String(new Date().getFullYear() - i);
@@ -140,15 +147,15 @@
140
147
  * @param date - The date to format
141
148
  * @returns Formatted date string
142
149
  */
143
- function formatDate(date: CalendarDate | undefined): string {
144
- if (!date || !(date instanceof CalendarDate)) return 'Pick a date';
150
+ function formatDate(date: CalendarDate | undefined, languageTag: string, translate: Translate<EditorMessageKey>): string {
151
+ if (!date || !(date instanceof CalendarDate)) return translate('editor.date.pick');
145
152
  try {
146
153
  // Convert to JavaScript Date for localized formatting
147
154
  const jsDate = new Date(date.year, date.month - 1, date.day);
148
- return jsDate.toLocaleDateString('en-US', { dateStyle: 'long' });
155
+ return jsDate.toLocaleDateString(languageTag, { dateStyle: 'long' });
149
156
  } catch (error) {
150
157
  console.error('Error formatting date:', error);
151
- return 'Pick a date';
158
+ return translate('editor.date.pick');
152
159
  }
153
160
  }
154
161
 
@@ -209,25 +216,30 @@
209
216
  }
210
217
  }
211
218
 
212
- // Setup scroll event listener on mount
219
+ // The control's own element, which says which tree (the page or a module's shadow root) to listen for scrolling in
220
+ let controllerElement: HTMLDivElement | undefined;
221
+ let stopListeningForScroll: () => void = () => {};
222
+
223
+ // Setup scroll event listener on mount (the page and, inside a module, the module's own panels: a scroll never
224
+ // leaves a shadow root, so a listener on the window alone does not hear about those)
213
225
  onMount(() => {
214
- window.addEventListener('scroll', handleScroll, { capture: true });
226
+ stopListeningForScroll = listenForScroll(controllerElement, handleScroll);
215
227
  });
216
228
 
217
229
  // Cleanup scroll event listener on destroy
218
230
  onDestroy(() => {
219
- window.removeEventListener('scroll', handleScroll, true);
231
+ stopListeningForScroll();
220
232
  });
221
233
  </script>
222
234
 
223
235
  <!-- Date Picker Container -->
224
- <div class="flex flex-col gap-2 widgetic-colors">
236
+ <div bind:this={controllerElement} class="flex flex-col gap-2 widgetic-colors">
225
237
  <div class="flex items-center w-full relative">
226
238
  <div class="flex-1 min-w-0">
227
239
  <InputControllerHeader {label} />
228
240
  </div>
229
241
 
230
- <div class="absolute right-0">
242
+ <div class="absolute end-0">
231
243
  <div
232
244
  class="h-[48px] w-[185px] rounded-large border border-grey2 flex items-center bg-grey1 transition-colors duration-200 hover:border-forest-green"
233
245
  on:focusin={onFocus}
@@ -238,9 +250,9 @@
238
250
  <Button
239
251
  id="date-picker"
240
252
  variant="ghost"
241
- class="w-full h-full justify-between text-left border-0 bg-transparent hover:bg-transparent focus:bg-transparent p-0 group"
253
+ class="w-full h-full justify-between text-start border-0 bg-transparent hover:bg-transparent focus:bg-transparent p-0 group"
242
254
  >
243
- <span class="subtext text-grey7 {!currentValue ? 'text-grey3' : ''}">{formatDate(currentValue)}</span>
255
+ <span class="subtext text-grey7 {!currentValue ? 'text-grey3' : ''}">{formatDate(currentValue, $locale, $t)}</span>
244
256
  <IconCalendar class="w-[20px] h-[20px] text-grey5 transition-colors duration-200 group-hover:text-grey7" />
245
257
  </Button>
246
258
  </div>
@@ -274,8 +286,8 @@
274
286
  }
275
287
  }}
276
288
  >
277
- <Select.Trigger aria-label="Select month" class="w-[60%] month-year-dropdown-content">
278
- <span class="text-sm">{defaultMonth?.label || "Select month"}</span>
289
+ <Select.Trigger aria-label={$t('editor.date.selectMonth')} class="w-[60%] month-year-dropdown-content">
290
+ <span class="text-sm">{defaultMonth?.label || $t('editor.date.selectMonth')}</span>
279
291
  </Select.Trigger>
280
292
  <Select.Content class="max-h-[200px] overflow-y-auto month-year-dropdown-content">
281
293
  {#each monthOptions as { value, label }}
@@ -309,8 +321,8 @@
309
321
  }
310
322
  }}
311
323
  >
312
- <Select.Trigger aria-label="Select year" class="w-[40%] month-year-dropdown-content">
313
- <span class="text-sm">{defaultYear?.label || "Select year"}</span>
324
+ <Select.Trigger aria-label={$t('editor.date.selectYear')} class="w-[40%] month-year-dropdown-content">
325
+ <span class="text-sm">{defaultYear?.label || $t('editor.date.selectYear')}</span>
314
326
  </Select.Trigger>
315
327
  <Select.Content class="max-h-[200px] overflow-y-auto month-year-dropdown-content">
316
328
  {#each yearOptions as { value, label }}
@@ -324,6 +336,10 @@
324
336
  <div class="calendar-no-header">
325
337
  <Calendar
326
338
  mode="single"
339
+ locale={$locale}
340
+ calendarLabel={$t('editor.date.calendarLabel')}
341
+ previousLabel={$t('editor.date.previousMonth')}
342
+ nextLabel={$t('editor.date.nextMonth')}
327
343
  onValueChange={(date: DateValue | undefined) => {
328
344
  // console.log('📅 [DatePicker] Calendar onValueChange:', { date, isCalendarDate: date instanceof CalendarDate });
329
345
  if (date instanceof CalendarDate) {
@@ -31,6 +31,11 @@
31
31
  * Required for component setup and cleanup
32
32
  */
33
33
  import { onMount, onDestroy } from 'svelte';
34
+ import { listenForScroll } from '../../utils/shadowDom';
35
+ import { useEditorLanguage } from '../../i18n/language';
36
+
37
+ // The words of the editor in the language of the page.
38
+ const { t } = useEditorLanguage();
34
39
 
35
40
  // Local Components imports
36
41
  import { InputControllerHeader } from './index';
@@ -49,7 +54,8 @@
49
54
  * External properties that configure the dropdown's behavior and appearance
50
55
  */
51
56
  export let label: DropdownInputControllerProps['label'];
52
- export let defaultSelectedLabel: DropdownInputControllerProps['defaultSelectedLabel'] = "Select an option";
57
+ /** What the button says while nothing is chosen. Without one, the editor's own word, in the language of the page. */
58
+ export let defaultSelectedLabel: DropdownInputControllerProps['defaultSelectedLabel'] = undefined;
53
59
  export let value: DropdownInputControllerProps['value'];
54
60
  export let onChange: DropdownInputControllerProps['onChange'];
55
61
  export let config: DropdownInputControllerProps['config'] = {};
@@ -113,13 +119,18 @@
113
119
  * Lifecycle Hooks
114
120
  * Setup and cleanup of scroll event listeners
115
121
  */
122
+ // The control's own element, which says which tree (the page or a module's shadow root) to listen for scrolling in
123
+ // (a scroll never leaves a shadow root, so a listener on the window alone only hears about the page scrolling)
124
+ let controllerElement: HTMLDivElement | undefined;
125
+ let stopListeningForScroll: () => void = () => {};
126
+
116
127
  onMount(() => {
117
128
  // console.log('DropdownInputController: onMount:', value);
118
- window.addEventListener('scroll', handleScroll, true);
129
+ stopListeningForScroll = listenForScroll(controllerElement, handleScroll);
119
130
  });
120
131
 
121
132
  onDestroy(() => {
122
- window.removeEventListener('scroll', handleScroll, true);
133
+ stopListeningForScroll();
123
134
  });
124
135
 
125
136
  /**
@@ -147,20 +158,21 @@
147
158
  /**
148
159
  * The option label for the current selection. `value` accepts a bare string
149
160
  * or an option object; normalize once instead of re-narrowing at each read.
161
+ * A missing selection is the empty string, as the select expects.
150
162
  */
151
- $: selectedValue = typeof value === 'string' ? value : value?.value;
163
+ $: selectedValue = typeof value === 'string' ? value : (value?.value ?? '');
152
164
  $: triggerContent =
153
- options.find((o) => o.value === selectedValue)?.label ?? defaultSelectedLabel;
165
+ options.find((o) => o.value === selectedValue)?.label ?? defaultSelectedLabel ?? $t('editor.dropdown.select');
154
166
  </script>
155
167
 
156
168
  <!-- Dropdown Container -->
157
- <div class="dropdown-ic flex flex-col gap-2 widgetic-colors">
169
+ <div bind:this={controllerElement} class="dropdown-ic flex flex-col gap-2 widgetic-colors">
158
170
  <div class="flex items-center w-full relative">
159
171
  <div class="flex-1 min-w-0">
160
172
  <InputControllerHeader {label} />
161
173
  </div>
162
174
 
163
- <div class="absolute right-0">
175
+ <div class="absolute end-0">
164
176
  <div
165
177
  class="dropdown-main-container rounded-large flex items-center transition-colors duration-200 group"
166
178
  style="height: 48px; width: 185px; border: 1px solid hsl(var(--grey2)); background: hsl(var(--grey1));"
@@ -175,10 +187,10 @@
175
187
  >
176
188
  <Select.Trigger
177
189
  class="dropdown-ic-trigger h-full w-full border-0 bg-transparent subtext hover:bg-transparent focus:bg-transparent data-[state=open]:bg-transparent"
178
- style="padding-left: 16px; padding-right: 16px; color: hsl(var(--grey7));"
190
+ style="padding-inline-start: 16px; padding-inline-end: 16px; color: hsl(var(--grey7));"
179
191
  id={controllerId}
180
192
  >
181
- <span class="truncate flex-1 text-left subtext" style="color: hsl(var(--grey7));">
193
+ <span class="truncate flex-1 text-start subtext" style="color: hsl(var(--grey7));">
182
194
  {triggerContent}
183
195
  </span>
184
196
  <IconCaretDownFilled class="dropdown-ic-trigger-icon" style="width: 20px; height: 20px; color: hsl(var(--grey5));"/>
@@ -39,7 +39,7 @@ declare const DropdownInputController: $$__sveltets_2_IsomorphicComponent<{
39
39
  * Component Props
40
40
  * External properties that configure the dropdown's behavior and appearance
41
41
  */ label: DropdownInputControllerProps["label"];
42
- defaultSelectedLabel?: DropdownInputControllerProps["defaultSelectedLabel"];
42
+ /** What the button says while nothing is chosen. Without one, the editor's own word, in the language of the page. */ defaultSelectedLabel?: DropdownInputControllerProps["defaultSelectedLabel"];
43
43
  value: DropdownInputControllerProps["value"];
44
44
  onChange: DropdownInputControllerProps["onChange"];
45
45
  config?: DropdownInputControllerProps["config"];
@@ -40,6 +40,11 @@
40
40
  <script lang="ts">
41
41
  // Svelte imports
42
42
  import { onDestroy } from 'svelte';
43
+ import { useEditorLanguage } from '../../i18n/language';
44
+ import { fixedPositionLabel } from '../../i18n/labels';
45
+
46
+ // The words of the editor in the language of the page.
47
+ const { t } = useEditorLanguage();
43
48
 
44
49
  // Import icons from Tabler
45
50
  import {
@@ -259,10 +264,10 @@
259
264
 
260
265
  function getCornerRounding(pos: Position): string {
261
266
  switch (pos) {
262
- case 'top-left': return 'rounded-tl-[15px]';
263
- case 'top-right': return 'rounded-tr-[15px]';
264
- case 'bottom-left': return 'rounded-bl-[15px]';
265
- case 'bottom-right': return 'rounded-br-[15px]';
267
+ case 'top-left': return 'rounded-tl-[15px]'; // rtl-ok: the corner of the picture of a screen
268
+ case 'top-right': return 'rounded-tr-[15px]'; // rtl-ok: the corner of the picture of a screen
269
+ case 'bottom-left': return 'rounded-bl-[15px]'; // rtl-ok: the corner of the picture of a screen
270
+ case 'bottom-right': return 'rounded-br-[15px]'; // rtl-ok: the corner of the picture of a screen
266
271
  default: return '';
267
272
  }
268
273
  }
@@ -270,9 +275,10 @@
270
275
 
271
276
  <!-- Container with relative positioning for overlay -->
272
277
  <div class="relative widgetic-colors">
273
- <!-- Grid component -->
278
+ <!-- Grid component: the nine places of a screen, top left to bottom right, are a picture of the screen, so it is a left to right island -->
274
279
  <div
275
280
  class="fixed-position-grid grid grid-cols-3 gap-[1px] w-fit bg-white rounded-large relative"
281
+ dir="ltr"
276
282
  {id}
277
283
  >
278
284
  {#each positions as position}
@@ -284,7 +290,7 @@
284
290
  }"
285
291
  on:click={() => handlePositionClick(position)}
286
292
  on:focus={onFocus}
287
- aria-label={`Set position to ${position}`}
293
+ aria-label={$t('editor.fixedPosition.set', { position: fixedPositionLabel(position, $t) })}
288
294
  aria-pressed={position === value}
289
295
  >
290
296
  <svelte:component