@widgetic/editor 4.0.11 → 4.1.2

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 (61) hide show
  1. package/README.md +124 -0
  2. package/dist/components/PreviewChromeControls.svelte +84 -0
  3. package/dist/components/PreviewChromeControls.svelte.d.ts +35 -0
  4. package/dist/components/WidgetPreview.svelte +90 -129
  5. package/dist/components/WidgetPreview.svelte.d.ts +1 -0
  6. package/dist/components/color-select/ColorSelect.svelte +4 -4
  7. package/dist/components/editors/CompositionEditorApp.svelte +69 -15
  8. package/dist/components/editors/CompositionEditorApp.svelte.d.ts +20 -0
  9. package/dist/components/editors/EmbedEditor.svelte +33 -22
  10. package/dist/components/editors/EmbedEditor.svelte.d.ts +1 -1
  11. package/dist/components/editors/PropsEditor.svelte +74 -58
  12. package/dist/components/editors/PropsEditor.svelte.d.ts +3 -3
  13. package/dist/components/ic/AngleInputController.svelte +1 -1
  14. package/dist/components/ic/BrowserInputController.svelte +37 -29
  15. package/dist/components/ic/ColorPickerInputController.svelte +31 -15
  16. package/dist/components/ic/DateFieldInputController.svelte +27 -11
  17. package/dist/components/ic/DatePickerInputController.svelte +27 -17
  18. package/dist/components/ic/DropdownInputController.svelte +10 -5
  19. package/dist/components/ic/DropdownInputController.svelte.d.ts +1 -1
  20. package/dist/components/ic/FixedPositionInputController.svelte +12 -6
  21. package/dist/components/ic/FontInputController.svelte +32 -27
  22. package/dist/components/ic/InputControllerHeader.svelte +5 -1
  23. package/dist/components/ic/NumberControls.svelte +4 -4
  24. package/dist/components/ic/NumberInputController.svelte +2 -2
  25. package/dist/components/ic/PositionInputController.svelte +9 -4
  26. package/dist/components/ic/RangeCalendarInputController.svelte +26 -16
  27. package/dist/components/ic/RoundedCornersInputController.svelte +22 -17
  28. package/dist/components/ic/SliderInputController.svelte +7 -2
  29. package/dist/components/ic/SwitchInputController.svelte +12 -3
  30. package/dist/components/ic/TagsInputController.svelte +19 -4
  31. package/dist/components/ic/TextAreaInputController.svelte +59 -44
  32. package/dist/components/ic/TextAreaPreviewInputController.svelte +8 -3
  33. package/dist/components/ic/TextInputController.svelte +17 -1
  34. package/dist/components/ic/TimeInputController.svelte +2 -1
  35. package/dist/components/ic/VectorInputController.svelte +21 -16
  36. package/dist/components/ic/shared/ItemGrid.svelte +7 -3
  37. package/dist/components/ic/shared/RecentItemsSection.svelte +16 -11
  38. package/dist/components/ic/shared/RecentItemsSection.svelte.d.ts +1 -1
  39. package/dist/composition/compositionEditorController.d.ts +11 -2
  40. package/dist/composition/compositionEditorController.js +33 -21
  41. package/dist/composition/compositionHelpers.d.ts +3 -1
  42. package/dist/composition/compositionHelpers.js +4 -2
  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 +2 -0
  56. package/dist/index.js +5 -0
  57. package/dist/utils/dockPlacement.d.ts +12 -0
  58. package/dist/utils/dockPlacement.js +13 -0
  59. package/dist/utils/fieldDirection.d.ts +13 -0
  60. package/dist/utils/fieldDirection.js +25 -0
  61. package/package.json +2 -2
@@ -90,6 +90,7 @@
90
90
  } from '../../utils/shadowDom';
91
91
  import { config as appConfig } from '../../config';
92
92
  import { provideEditorMediaLibrary } from '../../mediaLibraryContext';
93
+ import { useEditorLanguage } from '../../i18n/language';
93
94
  import type { MediaLibraryProvider } from '@widgetic/file-browser';
94
95
 
95
96
  /**
@@ -135,9 +136,9 @@
135
136
  export let designSchema: DesignSchemaView | null = null;
136
137
  export let contentSchema: ContentSchemaView | undefined = undefined;
137
138
  export let itemTemplate: any = null; // Store template for new items
138
- // Tab titles
139
- export let designTabTitle: string = 'Design';
140
- export let contentTabTitle: string = 'Content';
139
+ // Tab titles. Without one, the editor's own word, in the language of the page.
140
+ export let designTabTitle: string | undefined = undefined;
141
+ export let contentTabTitle: string | undefined = undefined;
141
142
  // Default tab selection
142
143
  export let defaultTab: 'design' | 'content' = 'content';
143
144
 
@@ -163,7 +164,7 @@
163
164
  export let headerContent = undefined;
164
165
  export let headerClass = '';
165
166
  export let dragHandleClass = 'text-grey2';
166
- export let title = 'Edit Properties';
167
+ export let title: string | undefined = undefined;
167
168
  /** Hide the default missing-schema message for header-only editors. */
168
169
  export let showSchemaError = true;
169
170
  /** Optional layout wrapper moved by this editor's built-in drag and position API. */
@@ -183,6 +184,11 @@
183
184
  // The file controls are created from the schema several components down: give them the library through context.
184
185
  provideEditorMediaLibrary(() => mediaLibrary);
185
186
 
187
+ // The words of the editor in the language of the page: the editor above this one chose it (see `provideEditorLanguage`).
188
+ const { t } = useEditorLanguage();
189
+ // The title of the bar on top: the host's own, or the editor's word; an empty title hides the bar's title.
190
+ $: titleLabel = title ?? $t('editor.props.title');
191
+
186
192
  /**
187
193
  * CONSTANTS
188
194
  */
@@ -1988,6 +1994,7 @@
1988
1994
  export function setPosition(left: number, top: number) {
1989
1995
  const target = positionTarget || rootElement;
1990
1996
  if (!target) return;
1997
+ // rtl-ok: the panel is placed in pixels from the left edge of the editor; the dock mirrors the pair on a right to left screen
1991
1998
  target.style.left = `${left}px`;
1992
1999
  target.style.top = `${top}px`;
1993
2000
  }
@@ -2037,7 +2044,7 @@
2037
2044
  <!-- A single toolbar owns drag and Save for all editor variants. -->
2038
2045
  {#if showDragHandle && (designSchema || contentSchema || headerContent)}
2039
2046
  <!-- svelte-ignore a11y_no_static_element_interactions -->
2040
- <div class="props-editor-drag-bar top-0 left-0 w-full flex h-7 shrink-0 items-center justify-center z-999 px-3 {headerContent ? 'relative ' + headerClass : 'absolute'}">
2047
+ <div class="props-editor-drag-bar top-0 start-0 w-full flex h-7 shrink-0 items-center justify-center z-999 px-3 {headerContent ? 'relative ' + headerClass : 'absolute'}">
2041
2048
  <!-- svelte-ignore a11y_no_static_element_interactions -->
2042
2049
  <div
2043
2050
  class="flex items-center justify-center h-7 cursor-move transition-all duration-200 widgetic-colors opacity-0 hover:opacity-100 drag-handle-container"
@@ -2046,10 +2053,10 @@
2046
2053
  <DragHandle class="w-16.5 h-6 {dragHandleClass} hover:text-grey3 transition-colors" />
2047
2054
  </div>
2048
2055
  {#if showSaveButton}
2049
- {#if title}<span class="props-editor-title-label absolute left-6 top-1.75 pointer-events-none select-none text-[13px] font-bold leading-none text-[hsl(var(--grey5))]">{title}</span>{/if}
2056
+ {#if titleLabel}<span class="props-editor-title-label absolute start-6 top-1.75 pointer-events-none select-none text-[13px] font-bold leading-none text-[hsl(var(--grey5))]">{titleLabel}</span>{/if}
2050
2057
  <button
2051
2058
  type="button"
2052
- class="props-editor-save-btn absolute right-6 top-1 inline-flex h-5 w-19 items-center justify-center rounded-md text-[10px] font-semibold leading-none transition-colors focus:outline-none focus-visible:outline-none
2059
+ class="props-editor-save-btn absolute end-6 top-1 inline-flex h-5 w-19 items-center justify-center rounded-md text-[10px] font-semibold leading-none transition-colors focus:outline-none focus-visible:outline-none
2053
2060
  {saveSaving
2054
2061
  ? 'text-[hsl(var(--deep-forest-green))] bg-[hsl(var(--light-green))]'
2055
2062
  : saveDirty
@@ -2062,12 +2069,12 @@
2062
2069
  }}
2063
2070
  onmousedown={(e) => e.stopPropagation()}
2064
2071
  >
2065
- <svg class="props-editor-save-icon absolute left-2 h-3 w-3 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
2072
+ <svg class="props-editor-save-icon absolute start-2 h-3 w-3 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
2066
2073
  <path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" />
2067
2074
  <polyline points="17 21 17 13 7 13 7 21" />
2068
2075
  <polyline points="7 3 7 8 15 8" />
2069
2076
  </svg>
2070
- <span class="props-editor-save-label">{saveSaving ? 'Saving' : 'Save'}</span>
2077
+ <span class="props-editor-save-label">{saveSaving ? $t('editor.props.saving') : $t('editor.props.save')}</span>
2071
2078
  </button>
2072
2079
  {/if}
2073
2080
  </div>
@@ -2080,8 +2087,8 @@
2080
2087
  {#if !designSchema && !contentSchema}
2081
2088
  {#if showSchemaError}
2082
2089
  <div class="props-editor-schema-error flex flex-col items-center justify-center h-full">
2083
- <div class="props-editor-error-title text-error text-lg font-medium">No schema configuration available</div>
2084
- <div class="props-editor-error-code text-grey5 subtext mt-1">Error code: CONFIG_NOT_FOUND</div>
2090
+ <div class="props-editor-error-title text-error text-lg font-medium">{$t('editor.props.noConfiguration')}</div>
2091
+ <div class="props-editor-error-code text-grey5 subtext mt-1">{$t('editor.failure.errorCode', { code: 'CONFIG_NOT_FOUND' })}</div>
2085
2092
  </div>
2086
2093
  {/if}
2087
2094
  {:else}
@@ -2097,14 +2104,14 @@
2097
2104
  value="design"
2098
2105
  class="flex-1 h-full rounded-[11px] transition-colors duration-250 text-grey5 hover:text-grey7 hover:underline data-[state=active]:bg-white data-[state=active]:text-grey7 data-[state=active]:hover:text-grey7"
2099
2106
  >
2100
- <span class="body">{designTabTitle}</span>
2107
+ <span class="body">{designTabTitle ?? $t('editor.props.designTab')}</span>
2101
2108
  </Tabs.Trigger>
2102
2109
  {#if contentSchema}
2103
2110
  <Tabs.Trigger
2104
2111
  value="content"
2105
2112
  class="flex-1 h-full rounded-[11px] transition-colors duration-250 text-grey5 hover:text-grey7 hover:underline data-[state=active]:bg-white data-[state=active]:text-grey7 data-[state=active]:hover:text-grey7"
2106
2113
  >
2107
- <span class="body">{contentTabTitle}</span>
2114
+ <span class="body">{contentTabTitle ?? $t('editor.props.contentTab')}</span>
2108
2115
  </Tabs.Trigger>
2109
2116
  {/if}
2110
2117
  </Tabs.List>
@@ -2190,7 +2197,7 @@
2190
2197
  </div>
2191
2198
  {/if}
2192
2199
 
2193
- <div class="ic-input-ct {isFontIc ? 'w-full' : 'shrink-0 ml-auto'}" style={isFontIc ? '' : 'width: 185px;'}>
2200
+ <div class="ic-input-ct {isFontIc ? 'w-full' : 'shrink-0 ms-auto'}" style={isFontIc ? '' : 'width: 185px;'}>
2194
2201
  <InputControllerRenderer
2195
2202
  controller={describeInputController(InputComponent, {
2196
2203
  ...props,
@@ -2210,13 +2217,13 @@
2210
2217
  {:else}
2211
2218
  <div class="p-4 text-gray-500">
2212
2219
  {#if !designSchema}
2213
- No design schema available
2220
+ {$t('editor.props.noDesignSchema')}
2214
2221
  {:else if !designSchema[0]}
2215
- No schema data available
2222
+ {$t('editor.props.noSchemaData')}
2216
2223
  {:else if !designSchema[0].components}
2217
- No components defined in schema
2224
+ {$t('editor.props.noComponents')}
2218
2225
  {:else}
2219
- No properties available
2226
+ {$t('editor.props.noProperties')}
2220
2227
  {/if}
2221
2228
  </div>
2222
2229
  {/if}
@@ -2256,8 +2263,8 @@
2256
2263
  onOpenChange={() => isGenericContentOpen = !isGenericContentOpen}
2257
2264
  >
2258
2265
  <Collapsible.Trigger class="generic-content-trigger w-full">
2259
- <div class="flex items-center h-13" style="padding-left: {CONTENT_INNER_PADDING}px; padding-right: {CONTENT_INNER_PADDING}px;">
2260
- <h3 class="body-medium text-grey7 flex-1 content-title">{contentSchema?.genericContent?.title || 'Content'}</h3>
2266
+ <div class="flex items-center h-13" style="padding-inline-start: {CONTENT_INNER_PADDING}px; padding-inline-end: {CONTENT_INNER_PADDING}px;">
2267
+ <h3 class="body-medium text-grey7 flex-1 content-title">{contentSchema?.genericContent?.title || $t('editor.props.contentCard')}</h3>
2261
2268
  <ChevronDown class="h-6 w-6 text-grey5 transition-transform chevron" />
2262
2269
  </div>
2263
2270
  </Collapsible.Trigger>
@@ -2294,7 +2301,7 @@
2294
2301
  helpText={header.helpText}
2295
2302
  />
2296
2303
  {:else if !isFontIc}
2297
- <div class="flex-1 min-w-0 pr-4">
2304
+ <div class="flex-1 min-w-0 pe-4">
2298
2305
  <InputControllerHeader
2299
2306
  label={header.label}
2300
2307
  helpText={header.helpText}
@@ -2353,7 +2360,7 @@
2353
2360
  </h4>
2354
2361
 
2355
2362
  <!-- Arrow Icon -->
2356
- <div class="pr-4">
2363
+ <div class="pe-4">
2357
2364
  <ChevronDown class="h-6 w-6 text-grey7 transition-transform chevron" />
2358
2365
  </div>
2359
2366
  </div>
@@ -2392,7 +2399,7 @@
2392
2399
  helpText={header.helpText}
2393
2400
  />
2394
2401
  {:else if !isFontIc}
2395
- <div class="flex-1 min-w-0 pr-4">
2402
+ <div class="flex-1 min-w-0 pe-4">
2396
2403
  <InputControllerHeader
2397
2404
  label={header.label}
2398
2405
  helpText={header.helpText}
@@ -2435,7 +2442,7 @@
2435
2442
  }}
2436
2443
  >
2437
2444
  <span class="subtext text-white transition-colors duration-200">
2438
- Delete item
2445
+ {$t('editor.props.deleteItem')}
2439
2446
  </span>
2440
2447
  </button>
2441
2448
  </div>
@@ -2459,7 +2466,7 @@
2459
2466
  >
2460
2467
  <div class="flex items-center justify-center gap-2">
2461
2468
  <AddNew class="w-5 h-5 text-grey5" />
2462
- <span class="body-medium text-grey7 group-hover:underline">Add new</span>
2469
+ <span class="body-medium text-grey7 group-hover:underline">{$t('editor.props.addItem')}</span>
2463
2470
  </div>
2464
2471
  </Button>
2465
2472
  </div>
@@ -2540,7 +2547,7 @@
2540
2547
  /* Icon stays put next to the text; label is centered in the remaining space
2541
2548
  with a small left bias so "Save" and "Saving" never shift the icon. */
2542
2549
  .props-editor-save-label {
2543
- padding-left: 14px;
2550
+ padding-inline-start: 14px;
2544
2551
  line-height: 1;
2545
2552
  }
2546
2553
  .props-editor-save-btn .props-editor-save-icon {
@@ -2623,20 +2630,20 @@
2623
2630
 
2624
2631
  /* First item - rounded at top only */
2625
2632
  :global(.accordion-item[data-position="first"]) {
2626
- border-top-left-radius: var(--radius-small);
2627
- border-top-right-radius: var(--radius-small);
2628
- border-bottom-left-radius: 0;
2629
- border-bottom-right-radius: 0;
2633
+ border-start-start-radius: var(--radius-small);
2634
+ border-start-end-radius: var(--radius-small);
2635
+ border-end-start-radius: 0;
2636
+ border-end-end-radius: 0;
2630
2637
  margin-top: 0; /* Explicitly remove top margin */
2631
2638
  }
2632
2639
 
2633
2640
  /* Last item - rounded at bottom only */
2634
2641
  :global(.accordion-item[data-position="last"]) {
2635
2642
  margin-bottom: 0;
2636
- border-top-left-radius: 0;
2637
- border-top-right-radius: 0;
2638
- border-bottom-left-radius: var(--radius-small);
2639
- border-bottom-right-radius: var(--radius-small);
2643
+ border-start-start-radius: 0;
2644
+ border-start-end-radius: 0;
2645
+ border-end-start-radius: var(--radius-small);
2646
+ border-end-end-radius: var(--radius-small);
2640
2647
  }
2641
2648
 
2642
2649
  /* Middle items - no rounded corners */
@@ -2738,13 +2745,13 @@
2738
2745
  /* Update scrollbar styling to match */
2739
2746
  :global([data-scrollable="true"] .scrollbar) {
2740
2747
  width: 8px !important; /* Thinner scrollbar */
2741
- margin-right: 2px !important; /* Add padding from the right */
2748
+ margin-inline-end: 2px !important; /* Add padding from the right */
2742
2749
  }
2743
2750
 
2744
2751
  :global([data-scrollable="true"] .thumb) {
2745
2752
  background-color: hsl(var(--grey1)) !important;
2746
2753
  border-radius: 3px !important;
2747
- margin-right: 2px !important;
2754
+ margin-inline-end: 2px !important;
2748
2755
  }
2749
2756
 
2750
2757
  :global([data-scrollable="true"] .thumb:hover) {
@@ -2753,11 +2760,11 @@
2753
2760
 
2754
2761
  :global([data-scrollable="true"] .track) {
2755
2762
  background-color: transparent !important;
2756
- margin-right: 2px !important;
2763
+ margin-inline-end: 2px !important;
2757
2764
  }
2758
2765
 
2759
2766
  :global([data-scrollable="true"]) {
2760
- padding-right: 2px !important; /* Add padding for scrollbar */
2767
+ padding-inline-end: 2px !important; /* Add padding for scrollbar */
2761
2768
  }
2762
2769
 
2763
2770
  /* Add these styles to ensure proper z-index layering */
@@ -2881,7 +2888,7 @@
2881
2888
  /* Add hover effect for title text */
2882
2889
  :global(.content-item-card [data-radix-collapsible-trigger] h4),
2883
2890
  :global(.generic-content-card [data-radix-collapsible-trigger] h3) {
2884
- text-align: left !important;
2891
+ text-align: start !important;
2885
2892
  }
2886
2893
 
2887
2894
  /* Add hover overlay for the entire trigger area */
@@ -2953,7 +2960,7 @@
2953
2960
  /* Keep text left-aligned */
2954
2961
  :global(.content-item-card h4),
2955
2962
  :global(.generic-content-card h3) {
2956
- text-align: left !important;
2963
+ text-align: start !important;
2957
2964
  }
2958
2965
 
2959
2966
  /* Add this new rule to remove focus borders */
@@ -3023,7 +3030,7 @@
3023
3030
  :global(.input-controller > div:first-child) {
3024
3031
  flex: 1 !important;
3025
3032
  min-width: 0 !important;
3026
- margin-right: 16px !important;
3033
+ margin-inline-end: 16px !important;
3027
3034
  }
3028
3035
 
3029
3036
  :global(.input-controller > div:last-child) {
@@ -3045,7 +3052,7 @@
3045
3052
  width: 100% !important;
3046
3053
  max-width: 100% !important;
3047
3054
  flex: none !important;
3048
- margin-right: 0 !important;
3055
+ margin-inline-end: 0 !important;
3049
3056
  }
3050
3057
 
3051
3058
  /* New animation system */
@@ -3225,17 +3232,17 @@
3225
3232
 
3226
3233
  /* Modify existing position rules */
3227
3234
  :global(.accordion-item[data-position="first"]) {
3228
- border-top-left-radius: var(--radius-small);
3229
- border-top-right-radius: var(--radius-small);
3230
- border-bottom-left-radius: 0;
3231
- border-bottom-right-radius: 0;
3235
+ border-start-start-radius: var(--radius-small);
3236
+ border-start-end-radius: var(--radius-small);
3237
+ border-end-start-radius: 0;
3238
+ border-end-end-radius: 0;
3232
3239
  }
3233
3240
 
3234
3241
  :global(.accordion-item[data-position="last"]) {
3235
- border-top-left-radius: 0;
3236
- border-top-right-radius: 0;
3237
- border-bottom-left-radius: var(--radius-small);
3238
- border-bottom-right-radius: var(--radius-small);
3242
+ border-start-start-radius: 0;
3243
+ border-start-end-radius: 0;
3244
+ border-end-start-radius: var(--radius-small);
3245
+ border-end-end-radius: var(--radius-small);
3239
3246
  }
3240
3247
 
3241
3248
  :global(.accordion-item[data-position="middle"]) {
@@ -3270,26 +3277,26 @@
3270
3277
 
3271
3278
  /* Ensure scroll area maintains border radius */
3272
3279
  :global([data-radix-scroll-area-viewport]) {
3273
- border-bottom-right-radius: var(--radius-large) !important;
3280
+ border-end-end-radius: var(--radius-large) !important;
3274
3281
  }
3275
3282
 
3276
3283
  :global(.scroll-area) {
3277
- border-bottom-right-radius: var(--radius-large) !important;
3284
+ border-end-end-radius: var(--radius-large) !important;
3278
3285
  }
3279
3286
 
3280
3287
  :global(.scroll-viewport) {
3281
- border-bottom-right-radius: var(--radius-large) !important;
3288
+ border-end-end-radius: var(--radius-large) !important;
3282
3289
  }
3283
3290
 
3284
3291
  /* Ensure scrollbar doesn't affect border radius */
3285
3292
  :global([data-radix-scroll-area-scrollbar]) {
3286
- border-bottom-right-radius: var(--radius-large) !important;
3287
- padding-right: 2px !important;
3293
+ border-end-end-radius: var(--radius-large) !important;
3294
+ padding-inline-end: 2px !important;
3288
3295
  }
3289
3296
 
3290
3297
  :global([data-radix-scroll-area-thumb]) {
3291
3298
  border-radius: var(--radius-small) !important;
3292
- margin-right: 2px !important;
3299
+ margin-inline-end: 2px !important;
3293
3300
  }
3294
3301
 
3295
3302
  /* Add z-index management for tooltips */
@@ -3329,7 +3336,7 @@
3329
3336
  /* Content Item Hover Styles */
3330
3337
  :global(.content-item-card [data-radix-collapsible-trigger] h4),
3331
3338
  :global(.generic-content-card [data-radix-collapsible-trigger] h3) {
3332
- text-align: left !important;
3339
+ text-align: start !important;
3333
3340
  }
3334
3341
 
3335
3342
  /* Add new specific hover styles for content titles */
@@ -3477,7 +3484,7 @@
3477
3484
 
3478
3485
  /* Remove any previous left padding from the flex container */
3479
3486
  :global(.content-item-trigger .flex) {
3480
- padding-left: 0 !important;
3487
+ padding-inline-start: 0 !important;
3481
3488
  }
3482
3489
 
3483
3490
  :global(.content-item) {
@@ -3507,6 +3514,15 @@
3507
3514
  transform: none;
3508
3515
  }
3509
3516
 
3517
+ /* The two tabs trade places on a right to left screen, so each panel slides in from the side that its tab is on. */
3518
+ :global(.design-tab-content.animate-slide-left[data-state="active"]:dir(rtl)) {
3519
+ animation-name: slideFromRight;
3520
+ }
3521
+
3522
+ :global(.content-tab-content.animate-slide-right[data-state="active"]:dir(rtl)) {
3523
+ animation-name: slideFromLeft;
3524
+ }
3525
+
3510
3526
  @keyframes slideFromLeft {
3511
3527
  from {
3512
3528
  opacity: 0;
@@ -29,8 +29,8 @@ declare const PropsEditor: $$__sveltets_2_IsomorphicComponent<{
29
29
  designSchema?: DesignSchemaView | null;
30
30
  contentSchema?: ContentSchemaView | undefined;
31
31
  itemTemplate?: any;
32
- designTabTitle?: string;
33
- contentTabTitle?: string;
32
+ designTabTitle?: string | undefined;
33
+ contentTabTitle?: string | undefined;
34
34
  defaultTab?: "design" | "content";
35
35
  defaultOpen?: boolean;
36
36
  showDragHandle?: boolean;
@@ -41,7 +41,7 @@ declare const PropsEditor: $$__sveltets_2_IsomorphicComponent<{
41
41
  /** Optional content below the shared toolbar, before property fields. */ headerContent?: any;
42
42
  headerClass?: string;
43
43
  dragHandleClass?: string;
44
- title?: string;
44
+ title?: string | undefined;
45
45
  /** Hide the default missing-schema message for header-only editors. */ showSchemaError?: boolean;
46
46
  /** Optional layout wrapper moved by this editor's built-in drag and position API. */ positionTarget?: any;
47
47
  initialDesignValues?: Record<string, any>;
@@ -234,7 +234,7 @@
234
234
  <InputControllerHeader {label} {helpText} />
235
235
  </div>
236
236
 
237
- <div class="absolute right-0 h-[48px] flex items-center">
237
+ <div class="absolute end-0 h-[48px] flex items-center">
238
238
  <div class="flex items-center justify-end gap-2">
239
239
  <!-- Circle Indicator -->
240
240
  <!-- svelte-ignore a11y_no_static_element_interactions -->
@@ -36,6 +36,9 @@
36
36
  import { ThumbHashImage } from "@widgetic/design-system/components";
37
37
  import { FileBrowser, setFileBrowserUploadConfig, persistentSelectStore } from '@widgetic/file-browser';
38
38
  import { useEditorMediaLibrary } from '../../mediaLibraryContext';
39
+ import { useEditorLanguage } from '../../i18n/language';
40
+ import type { EditorMessageKey } from '../../i18n/messages';
41
+ import type { Translate } from '@widgetic/design-system/i18n';
39
42
  import X from '../icons/x.svelte';
40
43
  // console.log("BrowserInputController - Tooltip:", Tooltip);
41
44
 
@@ -71,6 +74,9 @@
71
74
  // The media library the editor above was given (a partner's own storage, for instance), read through a getter
72
75
  // because it reaches this component through context.
73
76
  const getEditorMediaLibrary = useEditorMediaLibrary();
77
+
78
+ // The words of the editor in the language of the page, and the share of them that belongs to the File Browser inside it.
79
+ const { t, fileBrowser: fileBrowserLanguage } = useEditorLanguage();
74
80
 
75
81
  // Extract allowed file types from config or default to all files
76
82
  const fileTypes = config.fileTypes || ['*'];
@@ -391,15 +397,15 @@
391
397
  $: if (videoElement) setupMediaElement(videoElement);
392
398
 
393
399
  // Add placeholder text logic
394
- function getAddToEditorLabelSingular(fileTypes: string[]): string {
395
- if (!fileTypes || fileTypes.includes('*')) return 'Use file';
400
+ function getAddToEditorLabelSingular(fileTypes: string[], translate: Translate<EditorMessageKey>): string {
401
+ if (!fileTypes || fileTypes.includes('*')) return translate('editor.file.useFile');
396
402
  const type = fileTypes[0].split('/')[0];
397
403
  switch (type) {
398
- case 'image': return 'Use image';
399
- case 'video': return 'Use video';
400
- case 'audio': return 'Use audio';
401
- case 'document': return 'Use document';
402
- default: return 'Use file';
404
+ case 'image': return translate('editor.file.useImage');
405
+ case 'video': return translate('editor.file.useVideo');
406
+ case 'audio': return translate('editor.file.useAudio');
407
+ case 'document': return translate('editor.file.useDocument');
408
+ default: return translate('editor.file.useFile');
403
409
  }
404
410
  }
405
411
 
@@ -419,15 +425,15 @@
419
425
  }
420
426
  }
421
427
 
422
- function getPlaceholderText(fileTypes: string[]): string {
423
- if (!fileTypes || fileTypes.includes('*')) return 'Add file...';
428
+ function getPlaceholderText(fileTypes: string[], translate: Translate<EditorMessageKey>): string {
429
+ if (!fileTypes || fileTypes.includes('*')) return translate('editor.file.addFile');
424
430
  const type = fileTypes[0].split('/')[0];
425
431
  switch (type) {
426
- case 'image': return 'Add image...';
427
- case 'video': return 'Add video...';
428
- case 'audio': return 'Add audio...';
429
- case 'document': return 'Add docs...';
430
- default: return 'Add file...';
432
+ case 'image': return translate('editor.file.addImage');
433
+ case 'video': return translate('editor.file.addVideo');
434
+ case 'audio': return translate('editor.file.addAudio');
435
+ case 'document': return translate('editor.file.addDocument');
436
+ default: return translate('editor.file.addFile');
431
437
  }
432
438
  }
433
439
 
@@ -443,7 +449,7 @@
443
449
 
444
450
  $: fileBrowserFilter = getInitialFileBrowserFilter(fileTypes);
445
451
  $: fileBrowserRestrictUpload = fileBrowserFilter !== 'all';
446
- $: fileBrowserAddToEditorLabelSingular = getAddToEditorLabelSingular(fileTypes);
452
+ $: fileBrowserAddToEditorLabelSingular = getAddToEditorLabelSingular(fileTypes, $t);
447
453
 
448
454
  function handleContentRemoval(e: MouseEvent) {
449
455
  e.stopPropagation();
@@ -481,7 +487,7 @@
481
487
  <InputControllerHeader label={header?.label || label} helpText={header?.helpText} />
482
488
  </div>
483
489
 
484
- <div class="absolute right-0">
490
+ <div class="absolute end-0">
485
491
  <!-- svelte-ignore a11y_click_events_have_key_events -->
486
492
  <!-- svelte-ignore a11y_no_static_element_interactions -->
487
493
  <!-- svelte-ignore a11y_click_events_have_key_events -->
@@ -496,12 +502,12 @@
496
502
  <!-- File name or placeholder -->
497
503
  <div class="browser-input-controller-label flex-1 min-w-0 px-4 flex items-center h-full">
498
504
  <span class="subtext {value ? 'text-grey7' : 'text-grey3'} truncate inline-block">
499
- {value ? getFileName(value) : getPlaceholderText(fileTypes)}
505
+ {value ? getFileName(value) : getPlaceholderText(fileTypes, $t)}
500
506
  </span>
501
507
  </div>
502
508
 
503
509
  <!-- Preview area -->
504
- <div class="flex-none h-full flex items-center pr-[16px]">
510
+ <div class="flex-none h-full flex items-center pe-[16px]">
505
511
  {#if isChecking}
506
512
  <!-- Loading state -->
507
513
  <div class="w-8 h-7 bg-gray-100 rounded-lg"></div>
@@ -578,7 +584,7 @@
578
584
  >
579
585
  <img
580
586
  src={isPlaying ? icPlayerPause : icPlayerPlay}
581
- alt={isPlaying ? "Pause" : "Play"}
587
+ alt={isPlaying ? $t('editor.file.pause') : $t('editor.file.play')}
582
588
  class="w-5 h-5 {isPlaying ? 'opacity-0 hover:opacity-100' : 'opacity-100'} transition-opacity"
583
589
  />
584
590
  </div>
@@ -614,7 +620,7 @@
614
620
  >
615
621
  <img
616
622
  src={isPlaying ? icPlayerPause : icPlayerPlay}
617
- alt={isPlaying ? "Pause" : "Play"}
623
+ alt={isPlaying ? $t('editor.file.pause') : $t('editor.file.play')}
618
624
  class="w-5 h-5"
619
625
  />
620
626
  </div>
@@ -643,14 +649,14 @@
643
649
  <!-- Error state -->
644
650
  <img
645
651
  src={icError}
646
- alt="File not found"
652
+ alt={$t('editor.file.notFound')}
647
653
  class="w-8 h-7 object-contain"
648
654
  />
649
655
  {:else}
650
656
  <!-- Empty state -->
651
657
  <img
652
658
  src={icFolder}
653
- alt="Empty folder"
659
+ alt={$t('editor.file.emptyFolder')}
654
660
  class="w-8 h-7 object-contain"
655
661
  />
656
662
  {/if}
@@ -659,9 +665,9 @@
659
665
  {#if value}
660
666
  <button
661
667
  type="button"
662
- class="browser-ic-clear-btn absolute top-[-4px] right-[-4px] w-4 h-4 rounded-full hover:bg-grey2 transition-colors flex items-center justify-center"
668
+ class="browser-ic-clear-btn absolute top-[-4px] end-[-4px] w-4 h-4 rounded-full hover:bg-grey2 transition-colors flex items-center justify-center"
663
669
  on:click={handleContentRemoval}
664
- aria-label="Clear file"
670
+ aria-label={$t('editor.file.clear')}
665
671
  >
666
672
  <div class="w-4 h-4 flex items-center justify-center">
667
673
  <X class="w-4! h-4!" />
@@ -699,14 +705,14 @@
699
705
  class:rounded-large={!fillEditorPanel}
700
706
  class:shadow-2xl={!fillEditorPanel}
701
707
  >
702
- <div class="browser-input-file-browser-header relative flex h-[48px] shrink-0 items-center border-b border-grey2 pl-4 pr-8">
703
- <span class="body-medium text-grey7">Select file</span>
708
+ <div class="browser-input-file-browser-header relative flex h-[48px] shrink-0 items-center border-b border-grey2 ps-4 pe-8">
709
+ <span class="body-medium text-grey7">{$t('editor.file.dialogTitle')}</span>
704
710
  <button
705
711
  type="button"
706
712
  class="browser-input-file-browser-close inline-flex h-6 w-6 items-center justify-center p-0"
707
- style="position:absolute;top:4px;right:4px;"
713
+ style="position:absolute;top:4px;inset-inline-end:4px;"
708
714
  on:click={closeFileBrowser}
709
- aria-label="Close file browser"
715
+ aria-label={$t('editor.file.closeDialog')}
710
716
  >
711
717
  <X class="h-6! w-6!" />
712
718
  </button>
@@ -729,6 +735,8 @@
729
735
  maxSelections={1}
730
736
  activeFileId={getCurrentFileId()}
731
737
  addToEditorLabel={fileBrowserAddToEditorLabelSingular}
738
+ locale={$fileBrowserLanguage.locale ?? undefined}
739
+ messages={$fileBrowserLanguage.messages ?? null}
732
740
  on:select={handleFileBrowserSelect}
733
741
  />
734
742
  </div>
@@ -741,7 +749,7 @@
741
749
  .browser-input-file-browser-close {
742
750
  position: absolute;
743
751
  top: 4px;
744
- right: 4px;
752
+ inset-inline-end: 4px;
745
753
  }
746
754
 
747
755
  .browser-input-file-browser-dialog-animate {