@marianmeres/stuic 3.179.0 → 3.181.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/AGENTS.md +2 -2
- package/dist/components/ColorPicker/ColorPicker.svelte +457 -0
- package/dist/components/ColorPicker/ColorPicker.svelte.d.ts +75 -0
- package/dist/components/ColorPicker/README.md +220 -0
- package/dist/components/ColorPicker/color-value.d.ts +32 -0
- package/dist/components/ColorPicker/color-value.js +55 -0
- package/dist/components/ColorPicker/i18n-sk.d.ts +17 -0
- package/dist/components/ColorPicker/i18n-sk.js +48 -0
- package/dist/components/ColorPicker/i18n.d.ts +64 -0
- package/dist/components/ColorPicker/i18n.js +75 -0
- package/dist/components/ColorPicker/index.css +275 -0
- package/dist/components/ColorPicker/index.d.ts +4 -0
- package/dist/components/ColorPicker/index.js +4 -0
- package/dist/components/ColorPicker/palettes.d.ts +41 -0
- package/dist/components/ColorPicker/palettes.js +49 -0
- package/dist/components/FieldsBuilder/FieldsBuilder.svelte +43 -18
- package/dist/components/FieldsBuilder/FieldsBuilder.svelte.d.ts +15 -1
- package/dist/components/FieldsBuilder/README.md +65 -30
- package/dist/components/FieldsBuilder/_internal/OptionsEditor.svelte +10 -1
- package/dist/components/FieldsBuilder/_internal/OptionsEditor.svelte.d.ts +3 -0
- package/dist/components/FieldsBuilder/types.d.ts +7 -2
- package/dist/components/FieldsBuilder/utils.d.ts +13 -3
- package/dist/components/FieldsBuilder/utils.js +9 -13
- package/dist/components/Nav/Nav.svelte +7 -2
- package/dist/components/Nav/Nav.svelte.d.ts +7 -2
- package/dist/components/Nav/README.md +2 -2
- package/dist/components/TabbedMenu/README.md +14 -13
- package/dist/components/TabbedMenu/TabbedMenu.svelte +7 -2
- package/dist/components/TabbedMenu/TabbedMenu.svelte.d.ts +7 -2
- package/dist/index.css +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/utils/tr.d.ts +26 -14
- package/dist/utils/tr.js +43 -19
- package/docs/_archive/maybe-todo.md +9 -2
- package/docs/domains/components.md +58 -1
- package/package.json +1 -1
|
@@ -187,35 +187,36 @@ not block validation. It is never silently dropped.
|
|
|
187
187
|
|
|
188
188
|
## Props
|
|
189
189
|
|
|
190
|
-
| Prop | Type | Default
|
|
191
|
-
| ------------------------------------------------------------- | ---------------------------------------------------- |
|
|
192
|
-
| `value` | `FieldDef[]` | required
|
|
193
|
-
| `name` | `string` | required
|
|
194
|
-
| `types` | `FieldTypeDef[]` | required
|
|
195
|
-
| `label` | `Snippet \| THC` | —
|
|
196
|
-
| `description` | `Snippet \| THC` | —
|
|
197
|
-
| `languages` | `string[]` | —
|
|
198
|
-
| `defaultLanguage` | `string` | `languages[0]`
|
|
199
|
-
| `
|
|
200
|
-
| `
|
|
201
|
-
| `
|
|
202
|
-
| `
|
|
203
|
-
| `
|
|
204
|
-
| `
|
|
205
|
-
| `
|
|
206
|
-
| `
|
|
207
|
-
| `
|
|
208
|
-
| `
|
|
209
|
-
| `
|
|
210
|
-
| `
|
|
211
|
-
| `
|
|
212
|
-
| `
|
|
213
|
-
| `
|
|
214
|
-
| `
|
|
215
|
-
| `
|
|
216
|
-
| `
|
|
217
|
-
| `
|
|
218
|
-
| `
|
|
190
|
+
| Prop | Type | Default | Description |
|
|
191
|
+
| ------------------------------------------------------------- | ---------------------------------------------------- | ----------------- | ------------------------------------------------------------------------------- |
|
|
192
|
+
| `value` | `FieldDef[]` | required | Bindable ordered field list |
|
|
193
|
+
| `name` | `string` | required | Hidden-input name (form participation) |
|
|
194
|
+
| `types` | `FieldTypeDef[]` | required | The type palette |
|
|
195
|
+
| `label` | `Snippet \| THC` | — | Field label |
|
|
196
|
+
| `description` | `Snippet \| THC` | — | Help text below |
|
|
197
|
+
| `languages` | `string[]` | — | Enables multi-language label/description/option labels |
|
|
198
|
+
| `defaultLanguage` | `string` | `languages[0]` | Canonical language: key derivation, required-label rule, editors' primary input |
|
|
199
|
+
| `displayLanguage` | `string` | `defaultLanguage` | Translation shown in read-only spots (row list, palette, extras); see i18n |
|
|
200
|
+
| `languageLabels` | `Record<string, string>` | — | Display names for language codes |
|
|
201
|
+
| `keyPattern` | `RegExp` | snake_case | Key validation pattern |
|
|
202
|
+
| `keyMaxLength` | `number` | `63` | Key length limit |
|
|
203
|
+
| `reservedKeys` | `string[] \| (key) => boolean` | — | Keys the user may not use |
|
|
204
|
+
| `keysImmutable` | `boolean` | `true` | Freeze keys loaded from `value` |
|
|
205
|
+
| `deriveKeyFromLabel` | `boolean \| (label) => string` | `true` | Live key derivation (custom slugifier allowed) |
|
|
206
|
+
| `maxFields` | `number` | — | Disables adding beyond the limit |
|
|
207
|
+
| `deleteMode` | `"mark" \| "immediate"` | `"mark"` | Delete UX (see above) |
|
|
208
|
+
| `onBeforeDelete` | `(field) => void \| false \| Promise<void \| false>` | — | Delete veto hook |
|
|
209
|
+
| `onBeforeTypeChange` | `(field, newType) => void \| false \| Promise<...>` | — | Type-change veto hook (pre-existing fields) |
|
|
210
|
+
| `onChange` | `(value: FieldDef[]) => void` | — | Fired after every change |
|
|
211
|
+
| `preview` | `Snippet<[{ fields: FieldDef[] }]>` | — | Preview pane content (see below) |
|
|
212
|
+
| `previewBreakpoint` | `number` | `768` | Component width for side-by-side preview; `0` = below |
|
|
213
|
+
| `required` | `boolean` | `false` | At least one field required |
|
|
214
|
+
| `validate` | `boolean \| ValidateOptions` | `true` | Validate-action options |
|
|
215
|
+
| `renderSize` | `"sm" \| "md" \| "lg"` | `"sm"` | InputWrap size |
|
|
216
|
+
| `addLabel`, `emptyMessage` | `string` | — | Text overrides |
|
|
217
|
+
| `classRow`, `classRowHeader`, `classRowBody`, `classPreview` | `string` | — | Class hooks |
|
|
218
|
+
| `t` | `TranslateFn` | built-in (en) | i18n override for all texts (see below) |
|
|
219
|
+
| `disabled`, `id`, `tabindex`, `style`, `labelLeft*`, `class*` | | | Standard `Field*`/InputWrap pass-throughs |
|
|
219
220
|
|
|
220
221
|
Imperative API (via `bind:this`), same as every `Field*`:
|
|
221
222
|
`validate()`, `clearValidation()`, `getValidation()`, `focus()`, `scrollIntoView()`.
|
|
@@ -281,7 +282,41 @@ messages alone would leave the type select in English. `FIELDS_BUILDER_DEFAULT_T
|
|
|
281
282
|
is the Slovak twin of `FIELDS_BUILDER_DEFAULT_TYPES` (identical `type` values — the
|
|
282
283
|
stored defs are unaffected by which one you pass). A palette entry's `label` /
|
|
283
284
|
`description` also accept a per-language map (`{ en: "Text", sk: "Text" }`), resolved
|
|
284
|
-
against `
|
|
285
|
+
against `displayLanguage`.
|
|
286
|
+
|
|
287
|
+
### Display language vs. default language
|
|
288
|
+
|
|
289
|
+
With `languages` set, two languages play different roles:
|
|
290
|
+
|
|
291
|
+
- **`defaultLanguage`** (default `languages[0]`) is the _canonical_ one: the key is
|
|
292
|
+
derived from its label, the "label required" rule checks its entry, and the
|
|
293
|
+
label/description/option editors open on it (the other translations sit behind the
|
|
294
|
+
toggle).
|
|
295
|
+
- **`displayLanguage`** (default `defaultLanguage`) is what the component _shows_
|
|
296
|
+
wherever it renders localized data read-only: the collapsed row titles and the
|
|
297
|
+
reorder/delete announcements, the type chips and the type picker, the palette
|
|
298
|
+
descriptions, extras labels/placeholders/descriptions, the built-in preview fallback,
|
|
299
|
+
and the extra's name in `err_extra_maxlength`. A missing translation falls back to
|
|
300
|
+
`defaultLanguage`, then to the first non-empty entry.
|
|
301
|
+
|
|
302
|
+
So a Slovak user of a schema whose canonical language is English gets a Slovak list and
|
|
303
|
+
Slovak chrome (`t`), and still edits the canonical English label first:
|
|
304
|
+
|
|
305
|
+
```svelte
|
|
306
|
+
<FieldsBuilder
|
|
307
|
+
bind:value
|
|
308
|
+
name="fields"
|
|
309
|
+
types={FIELDS_BUILDER_DEFAULT_TYPES_SK}
|
|
310
|
+
languages={["en", "sk"]}
|
|
311
|
+
displayLanguage={user.locale}
|
|
312
|
+
t={createFieldsBuilderT(FIELDS_BUILDER_MESSAGES_SK)}
|
|
313
|
+
/>
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
`getLocalizedText(text, preferred)` — exported — accepts the same fallback chain as an
|
|
317
|
+
array: `getLocalizedText(label, ["sk", "en"])`. It is the library-wide `tr()` under this
|
|
318
|
+
component's name (`LocalizedText` is an alias of `MaybeLocalized`), so a label resolved in
|
|
319
|
+
your own `preview` snippet and one resolved by `Nav` or `TabbedMenu` degrade identically.
|
|
285
320
|
|
|
286
321
|
## Accessibility
|
|
287
322
|
|
|
@@ -37,7 +37,10 @@
|
|
|
37
37
|
interface Props {
|
|
38
38
|
options?: FieldOptionDef[];
|
|
39
39
|
languages?: string[];
|
|
40
|
+
/** Canonical language: value derivation and the editors' collapsed input. */
|
|
40
41
|
defaultLanguage?: string;
|
|
42
|
+
/** Language the announcements read option labels in. Default: `defaultLanguage`. */
|
|
43
|
+
displayLanguage?: string;
|
|
41
44
|
languageLabels?: Record<string, string>;
|
|
42
45
|
disabled?: boolean;
|
|
43
46
|
/** `lock.options` — render the list read-only. */
|
|
@@ -52,6 +55,7 @@
|
|
|
52
55
|
options = $bindable(),
|
|
53
56
|
languages,
|
|
54
57
|
defaultLanguage,
|
|
58
|
+
displayLanguage,
|
|
55
59
|
languageLabels,
|
|
56
60
|
disabled = false,
|
|
57
61
|
locked = false,
|
|
@@ -61,6 +65,11 @@
|
|
|
61
65
|
}: Props = $props();
|
|
62
66
|
|
|
63
67
|
const _defaultLanguage = $derived(defaultLanguage || languages?.[0]);
|
|
68
|
+
const _displayLanguages = $derived(
|
|
69
|
+
[displayLanguage || _defaultLanguage, _defaultLanguage].filter(
|
|
70
|
+
(l): l is string => !!l
|
|
71
|
+
)
|
|
72
|
+
);
|
|
64
73
|
|
|
65
74
|
interface OptionMeta {
|
|
66
75
|
/** Stable render id (options themselves have no identity). */
|
|
@@ -92,7 +101,7 @@
|
|
|
92
101
|
) {
|
|
93
102
|
liveAnnouncement = "";
|
|
94
103
|
const msg = String(
|
|
95
|
-
t(key, { label: getLocalizedText(option.label,
|
|
104
|
+
t(key, { label: getLocalizedText(option.label, _displayLanguages), ...values })
|
|
96
105
|
);
|
|
97
106
|
tick().then(() => (liveAnnouncement = msg));
|
|
98
107
|
}
|
|
@@ -3,7 +3,10 @@ import type { FieldOptionDef } from "../types.js";
|
|
|
3
3
|
interface Props {
|
|
4
4
|
options?: FieldOptionDef[];
|
|
5
5
|
languages?: string[];
|
|
6
|
+
/** Canonical language: value derivation and the editors' collapsed input. */
|
|
6
7
|
defaultLanguage?: string;
|
|
8
|
+
/** Language the announcements read option labels in. Default: `defaultLanguage`. */
|
|
9
|
+
displayLanguage?: string;
|
|
7
10
|
languageLabels?: Record<string, string>;
|
|
8
11
|
disabled?: boolean;
|
|
9
12
|
/** `lock.options` — render the list read-only. */
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { Snippet } from "svelte";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import type { MaybeLocalized } from "../../utils/tr.js";
|
|
3
|
+
/**
|
|
4
|
+
* A localized string: either one plain string, or a per-language map. The
|
|
5
|
+
* same shape as the library-wide `MaybeLocalized` (resolved by `tr()`); the
|
|
6
|
+
* alias is kept for this component's vocabulary.
|
|
7
|
+
*/
|
|
8
|
+
export type LocalizedText = MaybeLocalized;
|
|
4
9
|
/** A single option of a choice-like field type (one declaring `supportsOptions`). */
|
|
5
10
|
export interface FieldOptionDef {
|
|
6
11
|
/** Machine value, unique within the field's options. */
|
|
@@ -5,10 +5,13 @@ export declare const DEFAULT_KEY_MAX_LENGTH = 63;
|
|
|
5
5
|
/** Minimal translate signature the pure helpers below need. */
|
|
6
6
|
export type FieldsBuilderTranslate = (key: string, values?: Record<string, string | number>) => string;
|
|
7
7
|
/**
|
|
8
|
-
* Read the display text of a `LocalizedText`: the string itself, the
|
|
9
|
-
* language
|
|
8
|
+
* Read the display text of a `LocalizedText`: the string itself, the entry of
|
|
9
|
+
* the first preferred language (a single one, or a fallback chain in order of
|
|
10
|
+
* preference) that is non-empty, or the first non-empty entry as a last
|
|
11
|
+
* resort. This is the library-wide `tr()` under this component's name — same
|
|
12
|
+
* resolution, same fallbacks (a JSON-encoded record is accepted too).
|
|
10
13
|
*/
|
|
11
|
-
export declare function getLocalizedText(text: LocalizedText | null | undefined, preferredLanguage?: string): string;
|
|
14
|
+
export declare function getLocalizedText(text: LocalizedText | null | undefined, preferredLanguage?: string | string[]): string;
|
|
12
15
|
/**
|
|
13
16
|
* Derive a machine key from a human label: transliterates diacritics
|
|
14
17
|
* (`Ročník` → `rocnik`), lowercases, collapses everything else to `_`. A slug
|
|
@@ -48,7 +51,14 @@ export interface ValidateFieldDefsOptions {
|
|
|
48
51
|
keyMaxLength?: number;
|
|
49
52
|
reservedKeys?: string[] | ((key: string) => boolean);
|
|
50
53
|
maxFields?: number;
|
|
54
|
+
/** The canonical language: the "label required" rule reads this entry. */
|
|
51
55
|
defaultLanguage?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Language the texts interpolated into messages (an extra's `label`) are
|
|
58
|
+
* read in, falling back to `defaultLanguage`. The rules themselves are not
|
|
59
|
+
* affected. Default: `defaultLanguage`.
|
|
60
|
+
*/
|
|
61
|
+
displayLanguage?: string;
|
|
52
62
|
/** Translator for the error messages; defaults to returning the message key. */
|
|
53
63
|
t?: FieldsBuilderTranslate;
|
|
54
64
|
}
|
|
@@ -1,21 +1,16 @@
|
|
|
1
|
+
import { tr } from "../../utils/tr.js";
|
|
1
2
|
/** Default machine-key policy: lowercase snake_case, starts with a letter, max 63 chars. */
|
|
2
3
|
export const DEFAULT_KEY_PATTERN = /^[a-z][a-z0-9_]{0,62}$/;
|
|
3
4
|
export const DEFAULT_KEY_MAX_LENGTH = 63;
|
|
4
5
|
/**
|
|
5
|
-
* Read the display text of a `LocalizedText`: the string itself, the
|
|
6
|
-
* language
|
|
6
|
+
* Read the display text of a `LocalizedText`: the string itself, the entry of
|
|
7
|
+
* the first preferred language (a single one, or a fallback chain in order of
|
|
8
|
+
* preference) that is non-empty, or the first non-empty entry as a last
|
|
9
|
+
* resort. This is the library-wide `tr()` under this component's name — same
|
|
10
|
+
* resolution, same fallbacks (a JSON-encoded record is accepted too).
|
|
7
11
|
*/
|
|
8
12
|
export function getLocalizedText(text, preferredLanguage) {
|
|
9
|
-
|
|
10
|
-
return "";
|
|
11
|
-
if (typeof text === "string")
|
|
12
|
-
return text;
|
|
13
|
-
if (preferredLanguage && text[preferredLanguage])
|
|
14
|
-
return text[preferredLanguage];
|
|
15
|
-
for (const v of Object.values(text))
|
|
16
|
-
if (v)
|
|
17
|
-
return v;
|
|
18
|
-
return "";
|
|
13
|
+
return tr(text, preferredLanguage);
|
|
19
14
|
}
|
|
20
15
|
/**
|
|
21
16
|
* Derive a machine key from a human label: transliterates diacritics
|
|
@@ -75,6 +70,7 @@ export function validateFieldDefs(defs, opts = {}) {
|
|
|
75
70
|
const keyPattern = opts.keyPattern ?? DEFAULT_KEY_PATTERN;
|
|
76
71
|
const keyMaxLength = opts.keyMaxLength ?? DEFAULT_KEY_MAX_LENGTH;
|
|
77
72
|
const typeMap = opts.types ? new Map(opts.types.map((td) => [td.type, td])) : null;
|
|
73
|
+
const displayLanguages = [opts.displayLanguage, opts.defaultLanguage].filter((l) => !!l);
|
|
78
74
|
const rowErrors = defs.map(() => null);
|
|
79
75
|
const put = (i, field, msg) => {
|
|
80
76
|
rowErrors[i] ??= {};
|
|
@@ -133,7 +129,7 @@ export function validateFieldDefs(defs, opts = {}) {
|
|
|
133
129
|
const v = d.extras?.[ex.key];
|
|
134
130
|
if (typeof v === "string" && v.length > ex.maxlength) {
|
|
135
131
|
put(i, "extras", t("err_extra_maxlength", {
|
|
136
|
-
label: getLocalizedText(ex.label,
|
|
132
|
+
label: getLocalizedText(ex.label, displayLanguages),
|
|
137
133
|
max: ex.maxlength,
|
|
138
134
|
}));
|
|
139
135
|
}
|
|
@@ -55,8 +55,13 @@
|
|
|
55
55
|
/** Optional section title rendered above the groups (uppercase, non-interactive) */
|
|
56
56
|
title?: MaybeLocalized;
|
|
57
57
|
|
|
58
|
-
/**
|
|
59
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Locale for `MaybeLocalized` label resolution — one locale, or a chain
|
|
60
|
+
* in order of preference (e.g. `[user.locale, "en"]`). A missing
|
|
61
|
+
* translation falls back along the chain, then to the label's first
|
|
62
|
+
* non-empty entry.
|
|
63
|
+
*/
|
|
64
|
+
locale?: string | string[];
|
|
60
65
|
|
|
61
66
|
/** Whether the sidebar is in collapsed mode (icon-only) */
|
|
62
67
|
isCollapsed?: boolean;
|
|
@@ -49,8 +49,13 @@ export interface Props extends Omit<HTMLAttributes<HTMLElement>, "children" | "t
|
|
|
49
49
|
groups: NavGroup[];
|
|
50
50
|
/** Optional section title rendered above the groups (uppercase, non-interactive) */
|
|
51
51
|
title?: MaybeLocalized;
|
|
52
|
-
/**
|
|
53
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Locale for `MaybeLocalized` label resolution — one locale, or a chain
|
|
54
|
+
* in order of preference (e.g. `[user.locale, "en"]`). A missing
|
|
55
|
+
* translation falls back along the chain, then to the label's first
|
|
56
|
+
* non-empty entry.
|
|
57
|
+
*/
|
|
58
|
+
locale?: string | string[];
|
|
54
59
|
/** Whether the sidebar is in collapsed mode (icon-only) */
|
|
55
60
|
isCollapsed?: boolean;
|
|
56
61
|
/** Whether the sidebar is expanding (transitioning from collapsed to expanded) */
|
|
@@ -8,7 +8,7 @@ A navigation component for sidebars with support for groups, nested items, expan
|
|
|
8
8
|
| ---------------------- | --------------------------------------------------- | ------------- | --------------------------------------------------------- |
|
|
9
9
|
| `groups` | `NavGroup[]` | - | Navigation groups to render |
|
|
10
10
|
| `title` | `MaybeLocalized` | - | Section title above groups (uppercase, non-interactive) |
|
|
11
|
-
| `locale` | `string`
|
|
11
|
+
| `locale` | `string \| string[]` | - | Locale (or preference chain) for localized labels |
|
|
12
12
|
| `isCollapsed` | `boolean` | `false` | Collapsed mode (icon-only) |
|
|
13
13
|
| `isExpanding` | `boolean` | `false` | Transitioning from collapsed to expanded |
|
|
14
14
|
| `activeId` | `string` | - | Active item ID for highlighting |
|
|
@@ -329,7 +329,7 @@ Note: in icon-only sidebar mode (`isCollapsed`), the section title is visually h
|
|
|
329
329
|
- **localStorage Persistence**: Expand/collapse state persists across page reloads
|
|
330
330
|
- **Collapsed Mode**: Icon-only sidebar with tooltips
|
|
331
331
|
- **Active State**: Highlight current item via `activeId` or custom callback
|
|
332
|
-
- **Localization**: Labels support `MaybeLocalized`
|
|
332
|
+
- **Localization**: Labels support `MaybeLocalized`, resolved via `tr()` against `locale` (a single locale or a preference chain); a missing translation falls back to the label's first non-empty entry, never to `[object Object]`
|
|
333
333
|
- **Touch-Friendly**: Auto-detect or manually enable larger touch targets
|
|
334
334
|
- **ARIA Compliant**: Proper roles and aria-expanded attributes
|
|
335
335
|
- **Reduced Motion**: Respects user's reduced motion preference
|
|
@@ -22,19 +22,20 @@ A horizontal tab navigation component built on semantic `ul/li` markup with ARIA
|
|
|
22
22
|
|
|
23
23
|
## Props
|
|
24
24
|
|
|
25
|
-
| Prop | Type | Default | Description
|
|
26
|
-
| --------------------- | -------------------------------- | ----------- |
|
|
27
|
-
| `items` | `TabbedMenuItem[]` | required | Array of tab items
|
|
28
|
-
| `value` | `string \| number` | `undefined` | Active tab id (bindable)
|
|
29
|
-
| `disabled` | `boolean` | `false` | Disable all tabs
|
|
30
|
-
| `onSelect` | `(item: TabbedMenuItem) => void` | `undefined` | Callback when tab is selected
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
36
|
-
| `
|
|
37
|
-
| `
|
|
25
|
+
| Prop | Type | Default | Description |
|
|
26
|
+
| --------------------- | -------------------------------- | ----------- | --------------------------------------------- |
|
|
27
|
+
| `items` | `TabbedMenuItem[]` | required | Array of tab items |
|
|
28
|
+
| `value` | `string \| number` | `undefined` | Active tab id (bindable) |
|
|
29
|
+
| `disabled` | `boolean` | `false` | Disable all tabs |
|
|
30
|
+
| `onSelect` | `(item: TabbedMenuItem) => void` | `undefined` | Callback when tab is selected |
|
|
31
|
+
| `locale` | `string \| string[]` | `undefined` | Locale (or chain) for `MaybeLocalized` labels |
|
|
32
|
+
| `class` | `string` | `undefined` | Class for the `ul` wrapper |
|
|
33
|
+
| `classItem` | `string` | `undefined` | Class for each `li` element |
|
|
34
|
+
| `classButton` | `string` | `undefined` | Class for tab buttons |
|
|
35
|
+
| `classButtonActive` | `string` | `undefined` | Additional class for active tab |
|
|
36
|
+
| `classButtonDisabled` | `string` | `undefined` | Additional class for disabled tabs |
|
|
37
|
+
| `unstyled` | `boolean` | `false` | Skip default styling |
|
|
38
|
+
| `el` | `HTMLUListElement` | `undefined` | Element reference (bindable) |
|
|
38
39
|
|
|
39
40
|
## TabbedMenuItem Interface
|
|
40
41
|
|
|
@@ -21,8 +21,13 @@
|
|
|
21
21
|
disabled?: boolean;
|
|
22
22
|
onSelect?: (item: TabbedMenuItem) => void;
|
|
23
23
|
orientation?: "horizontal" | "vertical";
|
|
24
|
-
/**
|
|
25
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Locale for `MaybeLocalized` label resolution — one locale, or a chain
|
|
26
|
+
* in order of preference (e.g. `[user.locale, "en"]`). A missing
|
|
27
|
+
* translation falls back along the chain, then to the label's first
|
|
28
|
+
* non-empty entry.
|
|
29
|
+
*/
|
|
30
|
+
locale?: string | string[];
|
|
26
31
|
//
|
|
27
32
|
class?: string;
|
|
28
33
|
classItem?: string;
|
|
@@ -16,8 +16,13 @@ export interface Props extends Omit<HTMLAttributes<HTMLUListElement>, "children"
|
|
|
16
16
|
disabled?: boolean;
|
|
17
17
|
onSelect?: (item: TabbedMenuItem) => void;
|
|
18
18
|
orientation?: "horizontal" | "vertical";
|
|
19
|
-
/**
|
|
20
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Locale for `MaybeLocalized` label resolution — one locale, or a chain
|
|
21
|
+
* in order of preference (e.g. `[user.locale, "en"]`). A missing
|
|
22
|
+
* translation falls back along the chain, then to the label's first
|
|
23
|
+
* non-empty entry.
|
|
24
|
+
*/
|
|
25
|
+
locale?: string | string[];
|
|
21
26
|
class?: string;
|
|
22
27
|
classItem?: string;
|
|
23
28
|
classButton?: string;
|
package/dist/index.css
CHANGED
|
@@ -86,6 +86,7 @@ In practice:
|
|
|
86
86
|
@import "./components/RegisterForm/index.css";
|
|
87
87
|
@import "./components/LoginOrRegisterForm/index.css";
|
|
88
88
|
@import "./components/Checkout/index.css";
|
|
89
|
+
@import "./components/ColorPicker/index.css";
|
|
89
90
|
@import "./components/CommandMenu/index.css";
|
|
90
91
|
/* NOTE: CommentInput/index.css is deliberately NOT imported here — same reason as
|
|
91
92
|
MarkdownEditor's. It is a subpath-export component and imports its own CSS
|
package/dist/index.d.ts
CHANGED
|
@@ -39,6 +39,7 @@ export * from "./components/Carousel/index.js";
|
|
|
39
39
|
export * from "./components/Checkout/index.js";
|
|
40
40
|
export * from "./components/Circle/index.js";
|
|
41
41
|
export * from "./components/Collapsible/index.js";
|
|
42
|
+
export * from "./components/ColorPicker/index.js";
|
|
42
43
|
export * from "./components/ColorScheme/index.js";
|
|
43
44
|
export * from "./components/CommandMenu/index.js";
|
|
44
45
|
export * from "./components/ContactUsForm/index.js";
|
package/dist/index.js
CHANGED
|
@@ -40,6 +40,7 @@ export * from "./components/Carousel/index.js";
|
|
|
40
40
|
export * from "./components/Checkout/index.js";
|
|
41
41
|
export * from "./components/Circle/index.js";
|
|
42
42
|
export * from "./components/Collapsible/index.js";
|
|
43
|
+
export * from "./components/ColorPicker/index.js";
|
|
43
44
|
export * from "./components/ColorScheme/index.js";
|
|
44
45
|
export * from "./components/CommandMenu/index.js";
|
|
45
46
|
// NOTE: CommentInput is deliberately NOT exported here. It embeds MarkdownEditor,
|
package/dist/utils/tr.d.ts
CHANGED
|
@@ -1,25 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* A value that may be a plain string or a locale-keyed object of translations
|
|
2
|
+
* A value that may be a plain string or a locale-keyed object of translations
|
|
3
|
+
* (`{ en: "Hello", sk: "Ahoj" }`).
|
|
3
4
|
*/
|
|
4
5
|
export type MaybeLocalized = string | Record<string, string>;
|
|
5
6
|
/**
|
|
6
|
-
*
|
|
7
|
+
* Resolves a `MaybeLocalized` value to the text to display.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* A plain string is returned as-is. A locale-keyed record (or a JSON string
|
|
10
|
+
* encoding one — how string-valued inputs such as `FieldInputLocalized` carry
|
|
11
|
+
* it) is resolved in this order:
|
|
11
12
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* 1. the first entry of `locale` (one locale, or a preference chain in order)
|
|
14
|
+
* that is non-empty — an empty entry counts as missing,
|
|
15
|
+
* 2. `fallback`, when one is given,
|
|
16
|
+
* 3. the first non-empty entry of the record,
|
|
17
|
+
* 4. `""`.
|
|
18
|
+
*
|
|
19
|
+
* So a record never renders as `[object Object]`, and a missing translation
|
|
20
|
+
* degrades to another language rather than to nothing. `null`/`undefined`
|
|
21
|
+
* yield `fallback ?? ""`.
|
|
22
|
+
*
|
|
23
|
+
* @param val - A plain string, a locale-keyed record, or a JSON string of one
|
|
24
|
+
* @param locale - The locale to look up, or a chain of locales in order of preference
|
|
25
|
+
* @param fallback - Text to use when none of `locale` has a non-empty entry
|
|
16
26
|
*
|
|
17
27
|
* @example
|
|
18
28
|
* ```ts
|
|
19
|
-
* tr(
|
|
20
|
-
* tr({ en:
|
|
21
|
-
* tr({ en:
|
|
22
|
-
* tr(
|
|
29
|
+
* tr("Hello", "sk"); // "Hello" (plain string returned as-is)
|
|
30
|
+
* tr({ en: "Hello", sk: "Ahoj" }, "sk"); // "Ahoj"
|
|
31
|
+
* tr({ en: "Hello", sk: "Ahoj" }, ["cs", "sk"]); // "Ahoj" (chain, in order)
|
|
32
|
+
* tr({ en: "Hello" }, "sk"); // "Hello" (first non-empty entry)
|
|
33
|
+
* tr({ en: "Hello" }, "sk", "—"); // "—" (explicit fallback wins)
|
|
34
|
+
* tr('{"en":"Hello","sk":"Ahoj"}', "sk"); // "Ahoj" (JSON string parsed)
|
|
23
35
|
* ```
|
|
24
36
|
*/
|
|
25
|
-
export declare function tr(val: MaybeLocalized | undefined | null, locale?: string, fallback?: string): string;
|
|
37
|
+
export declare function tr(val: MaybeLocalized | undefined | null, locale?: string | string[], fallback?: string): string;
|
package/dist/utils/tr.js
CHANGED
|
@@ -1,30 +1,54 @@
|
|
|
1
|
+
import { isPlainObject } from "./is-plain-object.js";
|
|
1
2
|
import { maybeJsonParse } from "./maybe-json-parse.js";
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
+
* Resolves a `MaybeLocalized` value to the text to display.
|
|
4
5
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* A plain string is returned as-is. A locale-keyed record (or a JSON string
|
|
7
|
+
* encoding one — how string-valued inputs such as `FieldInputLocalized` carry
|
|
8
|
+
* it) is resolved in this order:
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* 1. the first entry of `locale` (one locale, or a preference chain in order)
|
|
11
|
+
* that is non-empty — an empty entry counts as missing,
|
|
12
|
+
* 2. `fallback`, when one is given,
|
|
13
|
+
* 3. the first non-empty entry of the record,
|
|
14
|
+
* 4. `""`.
|
|
15
|
+
*
|
|
16
|
+
* So a record never renders as `[object Object]`, and a missing translation
|
|
17
|
+
* degrades to another language rather than to nothing. `null`/`undefined`
|
|
18
|
+
* yield `fallback ?? ""`.
|
|
19
|
+
*
|
|
20
|
+
* @param val - A plain string, a locale-keyed record, or a JSON string of one
|
|
21
|
+
* @param locale - The locale to look up, or a chain of locales in order of preference
|
|
22
|
+
* @param fallback - Text to use when none of `locale` has a non-empty entry
|
|
13
23
|
*
|
|
14
24
|
* @example
|
|
15
25
|
* ```ts
|
|
16
|
-
* tr(
|
|
17
|
-
* tr({ en:
|
|
18
|
-
* tr({ en:
|
|
19
|
-
* tr(
|
|
26
|
+
* tr("Hello", "sk"); // "Hello" (plain string returned as-is)
|
|
27
|
+
* tr({ en: "Hello", sk: "Ahoj" }, "sk"); // "Ahoj"
|
|
28
|
+
* tr({ en: "Hello", sk: "Ahoj" }, ["cs", "sk"]); // "Ahoj" (chain, in order)
|
|
29
|
+
* tr({ en: "Hello" }, "sk"); // "Hello" (first non-empty entry)
|
|
30
|
+
* tr({ en: "Hello" }, "sk", "—"); // "—" (explicit fallback wins)
|
|
31
|
+
* tr('{"en":"Hello","sk":"Ahoj"}', "sk"); // "Ahoj" (JSON string parsed)
|
|
20
32
|
* ```
|
|
21
33
|
*/
|
|
22
34
|
export function tr(val, locale, fallback) {
|
|
23
|
-
if (
|
|
24
|
-
return
|
|
25
|
-
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
35
|
+
if (val == null)
|
|
36
|
+
return fallback ?? "";
|
|
37
|
+
// only a plain object is a record; a string that parses to anything else
|
|
38
|
+
// ("2024", "null", "[1,2]") is just that string
|
|
39
|
+
const parsed = maybeJsonParse(val);
|
|
40
|
+
if (!isPlainObject(parsed))
|
|
41
|
+
return String(val);
|
|
42
|
+
const rec = parsed;
|
|
43
|
+
const chain = typeof locale === "string" ? [locale] : (locale ?? []);
|
|
44
|
+
for (const l of chain) {
|
|
45
|
+
if (l && Object.hasOwn(rec, l) && rec[l])
|
|
46
|
+
return String(rec[l]);
|
|
47
|
+
}
|
|
48
|
+
if (fallback !== undefined)
|
|
49
|
+
return fallback;
|
|
50
|
+
for (const v of Object.values(rec))
|
|
51
|
+
if (v)
|
|
52
|
+
return String(v);
|
|
53
|
+
return "";
|
|
30
54
|
}
|
|
@@ -83,8 +83,15 @@ Checked first, to avoid false positives:
|
|
|
83
83
|
feedback (icon + intent swap, localized label/name, sr live announcement),
|
|
84
84
|
`onCopied` / `onError` callbacks; the write is the reusable `copyToClipboard()`
|
|
85
85
|
util (async Clipboard API + `execCommand` fallback) in `utils/`.
|
|
86
|
-
13.
|
|
87
|
-
|
|
86
|
+
13. ~~**ColorPicker**~~ — ✅ shipped (see `src/lib/components/ColorPicker/`): a
|
|
87
|
+
`role="radiogroup"` swatch palette (roving tabindex, wrapping arrows, optional
|
|
88
|
+
"no color" swatch, `columns` grid) plus a custom row of the native
|
|
89
|
+
`<input type="color">` and a hex field; hidden input + `validate` with `required`,
|
|
90
|
+
`t` texts with Slovak bundled. Swatch values are never parsed — any CSS color
|
|
91
|
+
string, so `COLOR_PICKER_PALETTE_THEME` can hold `var(--stuic-color-*)` tokens and
|
|
92
|
+
the picked value keeps following the theme. Mobile needed no special path: the
|
|
93
|
+
native input opens the platform picker, so there is no popover to fight the
|
|
94
|
+
on-screen keyboard.
|
|
88
95
|
|
|
89
96
|
## Deliberately out of scope (or close enough to covered)
|
|
90
97
|
|