@widgetic/editor 4.0.11 → 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.
- package/README.md +124 -0
- package/dist/components/WidgetPreview.svelte +58 -59
- package/dist/components/color-select/ColorSelect.svelte +4 -4
- package/dist/components/editors/CompositionEditorApp.svelte +69 -15
- package/dist/components/editors/CompositionEditorApp.svelte.d.ts +20 -0
- package/dist/components/editors/EmbedEditor.svelte +33 -22
- package/dist/components/editors/EmbedEditor.svelte.d.ts +1 -1
- package/dist/components/editors/PropsEditor.svelte +74 -58
- package/dist/components/editors/PropsEditor.svelte.d.ts +3 -3
- package/dist/components/ic/AngleInputController.svelte +1 -1
- package/dist/components/ic/BrowserInputController.svelte +37 -29
- package/dist/components/ic/ColorPickerInputController.svelte +31 -15
- package/dist/components/ic/DateFieldInputController.svelte +27 -11
- package/dist/components/ic/DatePickerInputController.svelte +27 -17
- package/dist/components/ic/DropdownInputController.svelte +10 -5
- package/dist/components/ic/DropdownInputController.svelte.d.ts +1 -1
- package/dist/components/ic/FixedPositionInputController.svelte +12 -6
- package/dist/components/ic/FontInputController.svelte +32 -27
- package/dist/components/ic/InputControllerHeader.svelte +5 -1
- package/dist/components/ic/NumberControls.svelte +4 -4
- package/dist/components/ic/NumberInputController.svelte +2 -2
- package/dist/components/ic/PositionInputController.svelte +9 -4
- package/dist/components/ic/RangeCalendarInputController.svelte +26 -16
- package/dist/components/ic/RoundedCornersInputController.svelte +22 -17
- package/dist/components/ic/SliderInputController.svelte +7 -2
- package/dist/components/ic/SwitchInputController.svelte +12 -3
- package/dist/components/ic/TagsInputController.svelte +19 -4
- package/dist/components/ic/TextAreaInputController.svelte +59 -44
- package/dist/components/ic/TextAreaPreviewInputController.svelte +8 -3
- package/dist/components/ic/TextInputController.svelte +17 -1
- package/dist/components/ic/TimeInputController.svelte +2 -1
- package/dist/components/ic/VectorInputController.svelte +21 -16
- package/dist/components/ic/shared/ItemGrid.svelte +7 -3
- package/dist/components/ic/shared/RecentItemsSection.svelte +16 -11
- package/dist/components/ic/shared/RecentItemsSection.svelte.d.ts +1 -1
- package/dist/composition/compositionEditorController.d.ts +11 -2
- package/dist/composition/compositionEditorController.js +33 -21
- package/dist/composition/compositionHelpers.d.ts +3 -1
- package/dist/composition/compositionHelpers.js +4 -2
- package/dist/i18n/index.d.ts +4 -0
- package/dist/i18n/index.js +4 -0
- package/dist/i18n/labels.d.ts +15 -0
- package/dist/i18n/labels.js +57 -0
- package/dist/i18n/language.d.ts +228 -0
- package/dist/i18n/language.js +34 -0
- package/dist/i18n/languageKit.d.ts +54 -0
- package/dist/i18n/languageKit.js +102 -0
- package/dist/i18n/messages.d.ts +241 -0
- package/dist/i18n/messages.js +253 -0
- package/dist/i18n/translate.d.ts +6 -0
- package/dist/i18n/translate.js +8 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -0
- package/dist/utils/dockPlacement.d.ts +12 -0
- package/dist/utils/dockPlacement.js +13 -0
- package/dist/utils/fieldDirection.d.ts +13 -0
- package/dist/utils/fieldDirection.js +25 -0
- package/package.json +2 -2
|
@@ -70,6 +70,14 @@
|
|
|
70
70
|
import { getColorSuggestions, getNextSuggestion, type AutocompleteResult } from '../../utils/colorAutocomplete';
|
|
71
71
|
import { recentColorsUtil } from '../../utils/recentColors';
|
|
72
72
|
import { listenForScroll, lockPointerStyle } from '../../utils/shadowDom';
|
|
73
|
+
import { useEditorLanguage } from '../../i18n/language';
|
|
74
|
+
import { useTextDirection } from '@widgetic/design-system/components/textDirection';
|
|
75
|
+
|
|
76
|
+
// The words of the editor in the language of the page.
|
|
77
|
+
const { t } = useEditorLanguage();
|
|
78
|
+
|
|
79
|
+
// The way the screen is read: the pop-up opens from the end of the field, which is its left edge on a right to left screen.
|
|
80
|
+
const textDirection = useTextDirection();
|
|
73
81
|
|
|
74
82
|
|
|
75
83
|
|
|
@@ -1190,11 +1198,16 @@
|
|
|
1190
1198
|
const top = openAbove
|
|
1191
1199
|
? Math.round(rect.top - popoverHeight - 4)
|
|
1192
1200
|
: Math.round(rect.bottom + 4);
|
|
1193
|
-
// Align popup's
|
|
1201
|
+
// Align the popup's end edge with the end edge of the full input container: the right edge on a left to right screen,
|
|
1202
|
+
// the left edge on a right to left one (the popup itself is a drawing, drawn left to right, and opens toward the inside)
|
|
1194
1203
|
const inputContainer = triggerRef.closest('.color-input-main-container') as HTMLElement | null;
|
|
1195
|
-
const
|
|
1196
|
-
|
|
1197
|
-
|
|
1204
|
+
const containerRect = inputContainer ? inputContainer.getBoundingClientRect() : rect;
|
|
1205
|
+
if ($textDirection === 'rtl') {
|
|
1206
|
+
popoverStyle = `top: ${top}px; left: ${Math.round(containerRect.left)}px;`; // rtl-ok: the end edge of a right to left field is its left edge
|
|
1207
|
+
} else {
|
|
1208
|
+
const right = Math.round(window.innerWidth - containerRect.right);
|
|
1209
|
+
popoverStyle = `top: ${top}px; right: ${right}px;`; // rtl-ok: the end edge of a left to right field is its right edge
|
|
1210
|
+
}
|
|
1198
1211
|
}
|
|
1199
1212
|
|
|
1200
1213
|
// Position is calculated in handleToggle after tick()
|
|
@@ -1243,17 +1256,19 @@
|
|
|
1243
1256
|
</div>
|
|
1244
1257
|
|
|
1245
1258
|
<!-- Input Controller Container -->
|
|
1246
|
-
<div class="input-controller-ct absolute
|
|
1259
|
+
<div class="input-controller-ct absolute end-0">
|
|
1247
1260
|
<div class="color-input-main-container rounded-large flex items-center transition-colors duration-200"
|
|
1248
1261
|
style="height: 48px; width: 185px; background: hsl(var(--grey1)); {showRing ? 'border: 1px solid hsl(var(--grey2));' : ''}">
|
|
1249
|
-
<div class="flex items-center w-full justify-between" style="padding-
|
|
1262
|
+
<div class="flex items-center w-full justify-between" style="padding-inline-end: 12px;">
|
|
1250
1263
|
|
|
1251
1264
|
<!-- Hex Input Container -->
|
|
1252
1265
|
<div class="hex-input-container flex-1" bind:this={inputRef}>
|
|
1266
|
+
<!-- A colour code (#FF00AA) is read left to right on every screen: in a right to left line its "#" would end up at the wrong end -->
|
|
1253
1267
|
<Input
|
|
1254
1268
|
bind:this={inputComponent}
|
|
1255
1269
|
type="text"
|
|
1256
|
-
|
|
1270
|
+
dir="ltr"
|
|
1271
|
+
class="h-9 border-0 bg-transparent subtext text-grey7 placeholder:text-grey3 focus-visible:ring-0 focus-visible:ring-offset-0 ps-4"
|
|
1257
1272
|
value={hexInput}
|
|
1258
1273
|
onfocus={(e) => {
|
|
1259
1274
|
dispatch('focus', e);
|
|
@@ -1317,7 +1332,7 @@
|
|
|
1317
1332
|
z-index: 99999;
|
|
1318
1333
|
max-height: 12rem;
|
|
1319
1334
|
top: {inputRect.bottom + 4}px;
|
|
1320
|
-
left: {inputRect.left}px;
|
|
1335
|
+
left: {inputRect.left}px; /* rtl-ok: the list is as wide as the field it opens from, so its left edge is the field's */
|
|
1321
1336
|
width: {inputRect.width}px;
|
|
1322
1337
|
"
|
|
1323
1338
|
data-allow-scroll="true"
|
|
@@ -1366,7 +1381,8 @@
|
|
|
1366
1381
|
|
|
1367
1382
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
1368
1383
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
1369
|
-
<div class="color-picker-popover-content w-auto pt-2 pb-2
|
|
1384
|
+
<div class="color-picker-popover-content w-auto pt-2 pb-2 ps-2 pe-0 widgetic-colors widgetic-radius modal-shadow"
|
|
1385
|
+
dir="ltr"
|
|
1370
1386
|
style="{popoverStyle} border: 1px solid hsl(var(--grey2)); z-index: 2;"
|
|
1371
1387
|
onmousedown={(e) => e.stopPropagation()}
|
|
1372
1388
|
onclick={(e) => e.stopPropagation()}
|
|
@@ -1376,7 +1392,7 @@
|
|
|
1376
1392
|
|
|
1377
1393
|
<!-- Recent Colors Container -->
|
|
1378
1394
|
{#if saveRecentColors && (isShiftPressed || isSpacePressed)}
|
|
1379
|
-
<div class="recent-colors-container" style="width: {pickerSize + sliderWidth + 14}px; margin-
|
|
1395
|
+
<div class="recent-colors-container" style="width: {pickerSize + sliderWidth + 14}px; margin-inline-start: 10px; padding-top: 11px; padding-bottom: 8px;">
|
|
1380
1396
|
{#if recentColors.length > 0}
|
|
1381
1397
|
<div class="recent-colors-grid" style="display: grid; grid-template-columns: repeat(5, 1fr); width: 100%; gap: {swatchGap}px;">
|
|
1382
1398
|
{#each recentColors.slice(0, 5) as {color, alpha}, i}
|
|
@@ -1410,7 +1426,7 @@
|
|
|
1410
1426
|
</div>
|
|
1411
1427
|
{:else}
|
|
1412
1428
|
<div class="text-sm text-gray-500 text-center py-2">
|
|
1413
|
-
|
|
1429
|
+
{$t('editor.color.noRecent')}
|
|
1414
1430
|
</div>
|
|
1415
1431
|
{/if}
|
|
1416
1432
|
</div>
|
|
@@ -1421,7 +1437,7 @@
|
|
|
1421
1437
|
<div
|
|
1422
1438
|
class="color-picker-wrapper relative flex flex-col"
|
|
1423
1439
|
role="application"
|
|
1424
|
-
aria-label=
|
|
1440
|
+
aria-label={$t('editor.color.picker')}
|
|
1425
1441
|
onmousedown={handleDragStart}
|
|
1426
1442
|
style="padding: 0;"
|
|
1427
1443
|
>
|
|
@@ -1446,7 +1462,7 @@
|
|
|
1446
1462
|
<div
|
|
1447
1463
|
bind:this={opacitySliderRef}
|
|
1448
1464
|
class="relative cursor-pointer opacity-slider"
|
|
1449
|
-
style="width: {pickerSize}px; height: {sliderWidth}px; margin-
|
|
1465
|
+
style="width: {pickerSize}px; height: {sliderWidth}px; margin-inline-start: 10px;"
|
|
1450
1466
|
onmousedown={handleOpacityMouseDown}
|
|
1451
1467
|
>
|
|
1452
1468
|
<div class="absolute inset-0 overflow-hidden">
|
|
@@ -1467,13 +1483,13 @@
|
|
|
1467
1483
|
</div>
|
|
1468
1484
|
</div>
|
|
1469
1485
|
<!-- Opacity number input — square, matching swatch size -->
|
|
1470
|
-
<div class="opacity-number-ct flex items-center justify-center" style="width: {swatchSize + 6}px; min-width: {swatchSize + 6}px; height: {swatchSize}px; margin-
|
|
1486
|
+
<div class="opacity-number-ct flex items-center justify-center" style="width: {swatchSize + 6}px; min-width: {swatchSize + 6}px; height: {swatchSize}px; margin-inline-start: 8px;">
|
|
1471
1487
|
<Input
|
|
1472
1488
|
type="number"
|
|
1473
1489
|
min="0"
|
|
1474
1490
|
max="100"
|
|
1475
1491
|
class="w-full h-full text-center [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ring-0 focus:ring-0 shadow-none widgetic-colors subtext"
|
|
1476
|
-
style="border: 1px solid hsl(var(--forest-green)); color: hsl(var(--grey7)); padding-
|
|
1492
|
+
style="border: 1px solid hsl(var(--forest-green)); color: hsl(var(--grey7)); padding-inline-start: 8px; padding-inline-end: 0;"
|
|
1477
1493
|
value={opacityPercentage}
|
|
1478
1494
|
oninput={(e) => {
|
|
1479
1495
|
e.stopPropagation();
|
|
@@ -30,6 +30,15 @@
|
|
|
30
30
|
import { onMount, tick } from 'svelte';
|
|
31
31
|
import InputControllerHeader from './InputControllerHeader.svelte';
|
|
32
32
|
import { activeElementIn } from '../../utils/shadowDom';
|
|
33
|
+
import { useEditorLanguage } from '../../i18n/language';
|
|
34
|
+
import type { EditorMessageKey } from '../../i18n/messages';
|
|
35
|
+
import { useTextDirection } from '@widgetic/design-system/components/textDirection';
|
|
36
|
+
|
|
37
|
+
// The words of the editor in the language of the page.
|
|
38
|
+
const { t } = useEditorLanguage();
|
|
39
|
+
|
|
40
|
+
// The way the screen is read: the row of fields turns around with it, and so do the keys that move from one field to the next.
|
|
41
|
+
const textDirection = useTextDirection();
|
|
33
42
|
|
|
34
43
|
// Constants
|
|
35
44
|
const MIN_DAY = 1;
|
|
@@ -93,27 +102,30 @@
|
|
|
93
102
|
}
|
|
94
103
|
});
|
|
95
104
|
|
|
96
|
-
// Get field configuration
|
|
105
|
+
// Get field configuration. The hint in a box and its name for a screen reader are messages (`hintKey`, `labelKey`).
|
|
97
106
|
function getFieldConfig(field: 'day' | 'month' | 'year') {
|
|
98
|
-
const configs = {
|
|
107
|
+
const configs: Record<'day' | 'month' | 'year', { min: number; max: number; hintKey: EditorMessageKey; labelKey: EditorMessageKey; width: string; maxLength: number }> = {
|
|
99
108
|
day: {
|
|
100
109
|
min: MIN_DAY,
|
|
101
110
|
max: MAX_DAY,
|
|
102
|
-
|
|
111
|
+
hintKey: 'editor.dateField.dayHint',
|
|
112
|
+
labelKey: 'editor.dateField.dayLabel',
|
|
103
113
|
width: '40px',
|
|
104
114
|
maxLength: 2
|
|
105
115
|
},
|
|
106
116
|
month: {
|
|
107
117
|
min: MIN_MONTH,
|
|
108
118
|
max: MAX_MONTH,
|
|
109
|
-
|
|
119
|
+
hintKey: 'editor.dateField.monthHint',
|
|
120
|
+
labelKey: 'editor.dateField.monthLabel',
|
|
110
121
|
width: '40px',
|
|
111
122
|
maxLength: 2
|
|
112
123
|
},
|
|
113
124
|
year: {
|
|
114
125
|
min: MIN_YEAR,
|
|
115
126
|
max: MAX_YEAR,
|
|
116
|
-
|
|
127
|
+
hintKey: 'editor.dateField.yearHint',
|
|
128
|
+
labelKey: 'editor.dateField.yearLabel',
|
|
117
129
|
width: '60px',
|
|
118
130
|
maxLength: 4
|
|
119
131
|
}
|
|
@@ -337,11 +349,14 @@
|
|
|
337
349
|
return;
|
|
338
350
|
}
|
|
339
351
|
|
|
340
|
-
// Handle arrow navigation
|
|
352
|
+
// Handle arrow navigation. The digits of a field run left to right on every screen, so the key that leaves a field at the
|
|
353
|
+
// right end of its digits is ArrowRight on every screen, and what lies on that side is the next field on a left to right
|
|
354
|
+
// screen and the one before it on a right to left one (where the row of fields starts on the right).
|
|
341
355
|
if (e.key === 'ArrowRight') {
|
|
342
356
|
if (input.selectionStart === input.value.length) {
|
|
343
357
|
e.preventDefault();
|
|
344
|
-
|
|
358
|
+
if ($textDirection === 'rtl') focusPreviousField(field);
|
|
359
|
+
else focusNextField(field);
|
|
345
360
|
}
|
|
346
361
|
return;
|
|
347
362
|
}
|
|
@@ -349,7 +364,8 @@
|
|
|
349
364
|
if (e.key === 'ArrowLeft') {
|
|
350
365
|
if (input.selectionStart === 0) {
|
|
351
366
|
e.preventDefault();
|
|
352
|
-
|
|
367
|
+
if ($textDirection === 'rtl') focusNextField(field);
|
|
368
|
+
else focusPreviousField(field);
|
|
353
369
|
}
|
|
354
370
|
return;
|
|
355
371
|
}
|
|
@@ -403,7 +419,7 @@
|
|
|
403
419
|
<InputControllerHeader {label} {helpText} />
|
|
404
420
|
</div>
|
|
405
421
|
|
|
406
|
-
<div class="absolute
|
|
422
|
+
<div class="absolute end-0">
|
|
407
423
|
<div class="h-[48px] w-[185px] rounded-large border border-grey2 bg-grey1 flex items-center transition-colors duration-200 hover:border-forest-green">
|
|
408
424
|
<div class="flex items-center justify-between px-4 w-full">
|
|
409
425
|
{#each fieldOrder as field}
|
|
@@ -416,14 +432,14 @@
|
|
|
416
432
|
pattern="[0-9]*"
|
|
417
433
|
bind:this={inputRefs[field]}
|
|
418
434
|
value={field === 'day' ? day : field === 'month' ? month : year}
|
|
419
|
-
placeholder={config.
|
|
435
|
+
placeholder={$t(config.hintKey)}
|
|
420
436
|
on:input={field === 'day' ? handleDayInput : field === 'month' ? handleMonthInput : handleYearInput}
|
|
421
437
|
on:change={handleChange}
|
|
422
438
|
on:focus={() => handleFocus(field)}
|
|
423
439
|
on:blur={() => handleBlur(field)}
|
|
424
440
|
on:keydown={(e) => handleKeyDown(field, e)}
|
|
425
441
|
class={getFieldClasses(field).input}
|
|
426
|
-
aria-label={
|
|
442
|
+
aria-label={$t(config.labelKey)}
|
|
427
443
|
maxlength={config.maxLength}
|
|
428
444
|
/>
|
|
429
445
|
</div>
|
|
@@ -38,6 +38,12 @@
|
|
|
38
38
|
// Svelte imports
|
|
39
39
|
import { onMount, onDestroy } from 'svelte';
|
|
40
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();
|
|
41
47
|
|
|
42
48
|
// Shadcn UI Components
|
|
43
49
|
import { Button, Select } from '@widgetic/design-system/components';
|
|
@@ -66,12 +72,12 @@
|
|
|
66
72
|
let placeholder = today(getLocalTimeZone());
|
|
67
73
|
let currentValue: CalendarDate | undefined;
|
|
68
74
|
|
|
69
|
-
// Month and year options setup
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
+
}));
|
|
75
81
|
|
|
76
82
|
const yearOptions = Array.from({ length: 100 }, (_, i) => {
|
|
77
83
|
const year = String(new Date().getFullYear() - i);
|
|
@@ -141,15 +147,15 @@
|
|
|
141
147
|
* @param date - The date to format
|
|
142
148
|
* @returns Formatted date string
|
|
143
149
|
*/
|
|
144
|
-
function formatDate(date: CalendarDate | undefined): string {
|
|
145
|
-
if (!date || !(date instanceof CalendarDate)) return '
|
|
150
|
+
function formatDate(date: CalendarDate | undefined, languageTag: string, translate: Translate<EditorMessageKey>): string {
|
|
151
|
+
if (!date || !(date instanceof CalendarDate)) return translate('editor.date.pick');
|
|
146
152
|
try {
|
|
147
153
|
// Convert to JavaScript Date for localized formatting
|
|
148
154
|
const jsDate = new Date(date.year, date.month - 1, date.day);
|
|
149
|
-
return jsDate.toLocaleDateString(
|
|
155
|
+
return jsDate.toLocaleDateString(languageTag, { dateStyle: 'long' });
|
|
150
156
|
} catch (error) {
|
|
151
157
|
console.error('Error formatting date:', error);
|
|
152
|
-
return '
|
|
158
|
+
return translate('editor.date.pick');
|
|
153
159
|
}
|
|
154
160
|
}
|
|
155
161
|
|
|
@@ -233,7 +239,7 @@
|
|
|
233
239
|
<InputControllerHeader {label} />
|
|
234
240
|
</div>
|
|
235
241
|
|
|
236
|
-
<div class="absolute
|
|
242
|
+
<div class="absolute end-0">
|
|
237
243
|
<div
|
|
238
244
|
class="h-[48px] w-[185px] rounded-large border border-grey2 flex items-center bg-grey1 transition-colors duration-200 hover:border-forest-green"
|
|
239
245
|
on:focusin={onFocus}
|
|
@@ -244,9 +250,9 @@
|
|
|
244
250
|
<Button
|
|
245
251
|
id="date-picker"
|
|
246
252
|
variant="ghost"
|
|
247
|
-
class="w-full h-full justify-between text-
|
|
253
|
+
class="w-full h-full justify-between text-start border-0 bg-transparent hover:bg-transparent focus:bg-transparent p-0 group"
|
|
248
254
|
>
|
|
249
|
-
<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>
|
|
250
256
|
<IconCalendar class="w-[20px] h-[20px] text-grey5 transition-colors duration-200 group-hover:text-grey7" />
|
|
251
257
|
</Button>
|
|
252
258
|
</div>
|
|
@@ -280,8 +286,8 @@
|
|
|
280
286
|
}
|
|
281
287
|
}}
|
|
282
288
|
>
|
|
283
|
-
<Select.Trigger aria-label=
|
|
284
|
-
<span class="text-sm">{defaultMonth?.label ||
|
|
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>
|
|
285
291
|
</Select.Trigger>
|
|
286
292
|
<Select.Content class="max-h-[200px] overflow-y-auto month-year-dropdown-content">
|
|
287
293
|
{#each monthOptions as { value, label }}
|
|
@@ -315,8 +321,8 @@
|
|
|
315
321
|
}
|
|
316
322
|
}}
|
|
317
323
|
>
|
|
318
|
-
<Select.Trigger aria-label=
|
|
319
|
-
<span class="text-sm">{defaultYear?.label ||
|
|
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>
|
|
320
326
|
</Select.Trigger>
|
|
321
327
|
<Select.Content class="max-h-[200px] overflow-y-auto month-year-dropdown-content">
|
|
322
328
|
{#each yearOptions as { value, label }}
|
|
@@ -330,6 +336,10 @@
|
|
|
330
336
|
<div class="calendar-no-header">
|
|
331
337
|
<Calendar
|
|
332
338
|
mode="single"
|
|
339
|
+
locale={$locale}
|
|
340
|
+
calendarLabel={$t('editor.date.calendarLabel')}
|
|
341
|
+
previousLabel={$t('editor.date.previousMonth')}
|
|
342
|
+
nextLabel={$t('editor.date.nextMonth')}
|
|
333
343
|
onValueChange={(date: DateValue | undefined) => {
|
|
334
344
|
// console.log('📅 [DatePicker] Calendar onValueChange:', { date, isCalendarDate: date instanceof CalendarDate });
|
|
335
345
|
if (date instanceof CalendarDate) {
|
|
@@ -32,6 +32,10 @@
|
|
|
32
32
|
*/
|
|
33
33
|
import { onMount, onDestroy } from 'svelte';
|
|
34
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();
|
|
35
39
|
|
|
36
40
|
// Local Components imports
|
|
37
41
|
import { InputControllerHeader } from './index';
|
|
@@ -50,7 +54,8 @@
|
|
|
50
54
|
* External properties that configure the dropdown's behavior and appearance
|
|
51
55
|
*/
|
|
52
56
|
export let label: DropdownInputControllerProps['label'];
|
|
53
|
-
|
|
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;
|
|
54
59
|
export let value: DropdownInputControllerProps['value'];
|
|
55
60
|
export let onChange: DropdownInputControllerProps['onChange'];
|
|
56
61
|
export let config: DropdownInputControllerProps['config'] = {};
|
|
@@ -157,7 +162,7 @@
|
|
|
157
162
|
*/
|
|
158
163
|
$: selectedValue = typeof value === 'string' ? value : (value?.value ?? '');
|
|
159
164
|
$: triggerContent =
|
|
160
|
-
options.find((o) => o.value === selectedValue)?.label ?? defaultSelectedLabel;
|
|
165
|
+
options.find((o) => o.value === selectedValue)?.label ?? defaultSelectedLabel ?? $t('editor.dropdown.select');
|
|
161
166
|
</script>
|
|
162
167
|
|
|
163
168
|
<!-- Dropdown Container -->
|
|
@@ -167,7 +172,7 @@
|
|
|
167
172
|
<InputControllerHeader {label} />
|
|
168
173
|
</div>
|
|
169
174
|
|
|
170
|
-
<div class="absolute
|
|
175
|
+
<div class="absolute end-0">
|
|
171
176
|
<div
|
|
172
177
|
class="dropdown-main-container rounded-large flex items-center transition-colors duration-200 group"
|
|
173
178
|
style="height: 48px; width: 185px; border: 1px solid hsl(var(--grey2)); background: hsl(var(--grey1));"
|
|
@@ -182,10 +187,10 @@
|
|
|
182
187
|
>
|
|
183
188
|
<Select.Trigger
|
|
184
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"
|
|
185
|
-
style="padding-
|
|
190
|
+
style="padding-inline-start: 16px; padding-inline-end: 16px; color: hsl(var(--grey7));"
|
|
186
191
|
id={controllerId}
|
|
187
192
|
>
|
|
188
|
-
<span class="truncate flex-1 text-
|
|
193
|
+
<span class="truncate flex-1 text-start subtext" style="color: hsl(var(--grey7));">
|
|
189
194
|
{triggerContent}
|
|
190
195
|
</span>
|
|
191
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={
|
|
293
|
+
aria-label={$t('editor.fixedPosition.set', { position: fixedPositionLabel(position, $t) })}
|
|
288
294
|
aria-pressed={position === value}
|
|
289
295
|
>
|
|
290
296
|
<svelte:component
|