@marianmeres/stuic 3.168.0 → 3.170.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/README.md +1 -1
- package/dist/components/Input/FieldOptions.svelte +89 -54
- package/dist/components/Input/FieldOptions.svelte.d.ts +13 -0
- package/dist/components/Input/README.md +144 -31
- package/dist/components/Input/_internal/FieldLikeChips.svelte +286 -0
- package/dist/components/Input/_internal/FieldLikeChips.svelte.d.ts +65 -0
- package/dist/components/Input/field-options-i18n-sk.d.ts +21 -0
- package/dist/components/Input/field-options-i18n-sk.js +61 -0
- package/dist/components/Input/field-options-i18n.d.ts +82 -0
- package/dist/components/Input/field-options-i18n.js +90 -0
- package/dist/components/Input/index.css +58 -0
- package/dist/components/Input/index.d.ts +2 -0
- package/dist/components/Input/index.js +2 -0
- package/dist/components/Pill/Pill.svelte +8 -2
- package/dist/components/Pill/Pill.svelte.d.ts +2 -0
- package/dist/components/Pill/README.md +35 -22
- package/dist/components/Pill/index.css +57 -18
- package/dist/components/Timeline/README.md +204 -0
- package/dist/components/Timeline/Timeline.svelte +246 -0
- package/dist/components/Timeline/Timeline.svelte.d.ts +87 -0
- package/dist/components/Timeline/index.css +311 -0
- package/dist/components/Timeline/index.d.ts +1 -0
- package/dist/components/Timeline/index.js +1 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/docs/domains/components.md +39 -1
- package/docs/maybe-todo.md +37 -13
- package/docs/upgrading.md +6 -0
- package/package.json +1 -1
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import type { ValidateOptions } from "../../../actions/validate.svelte.js";
|
|
4
|
+
import type { IntentColorKey } from "../../../utils/design-tokens.js";
|
|
5
|
+
import type { THC } from "../../Thc/Thc.svelte";
|
|
6
|
+
import type { InputWrapClassProps } from "../types.js";
|
|
7
|
+
|
|
8
|
+
type SnippetWithId = Snippet<[{ id: string }]>;
|
|
9
|
+
|
|
10
|
+
/** One rendered chip. `key` must be unique within the row. */
|
|
11
|
+
export interface FieldLikeChip {
|
|
12
|
+
key: string | number;
|
|
13
|
+
label: string;
|
|
14
|
+
/** Accessible name of the chip's remove (×) button, e.g. "Remove Foo" */
|
|
15
|
+
removeLabel: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Internal: the `chips` trigger of `FieldOptions`. The FieldLikeButton counterpart for a
|
|
20
|
+
* selection shown as inline, individually removable Pills — same InputWrap shell, same
|
|
21
|
+
* hidden input + validate wiring, same imperative API.
|
|
22
|
+
*/
|
|
23
|
+
export interface Props extends InputWrapClassProps {
|
|
24
|
+
/** The hidden input carrying `value` + `name` (bindable) */
|
|
25
|
+
input?: HTMLInputElement;
|
|
26
|
+
value: string;
|
|
27
|
+
/** The chips to render (the parent derives them from `value`) */
|
|
28
|
+
chips: FieldLikeChip[];
|
|
29
|
+
label?: SnippetWithId | THC;
|
|
30
|
+
description?: SnippetWithId | THC;
|
|
31
|
+
class?: string;
|
|
32
|
+
id?: string;
|
|
33
|
+
tabindex?: number;
|
|
34
|
+
renderSize?: "sm" | "md" | "lg" | string;
|
|
35
|
+
name?: string;
|
|
36
|
+
required?: boolean;
|
|
37
|
+
disabled?: boolean;
|
|
38
|
+
validate?: boolean | Omit<ValidateOptions, "setValidationResult">;
|
|
39
|
+
labelAfter?: SnippetWithId | THC;
|
|
40
|
+
below?: SnippetWithId | THC;
|
|
41
|
+
labelLeft?: boolean;
|
|
42
|
+
labelLeftWidth?: "normal" | "wide";
|
|
43
|
+
labelLeftBreakpoint?: number;
|
|
44
|
+
style?: string;
|
|
45
|
+
/** Shown in the row while there are no chips */
|
|
46
|
+
placeholder?: string;
|
|
47
|
+
/** Accessible name (and tooltip) of the trailing "open" button */
|
|
48
|
+
openLabel: string;
|
|
49
|
+
/** Classes for each chip (Pill) */
|
|
50
|
+
classChip?: string;
|
|
51
|
+
/** Pill intent of the chips */
|
|
52
|
+
chipIntent?: IntentColorKey;
|
|
53
|
+
/** Open the picker (trailing button, or a click on the empty part of the row) */
|
|
54
|
+
onOpen: () => void;
|
|
55
|
+
/** A chip's × was pressed */
|
|
56
|
+
onRemove: (chip: FieldLikeChip, index: number) => void;
|
|
57
|
+
}
|
|
58
|
+
</script>
|
|
59
|
+
|
|
60
|
+
<script lang="ts">
|
|
61
|
+
import { tick } from "svelte";
|
|
62
|
+
import { iconSearch } from "../../../icons/index.js";
|
|
63
|
+
import {
|
|
64
|
+
validate as validateAction,
|
|
65
|
+
type ValidationResult,
|
|
66
|
+
} from "../../../actions/validate.svelte.js";
|
|
67
|
+
import { getId } from "../../../utils/get-id.js";
|
|
68
|
+
import Button from "../../Button/Button.svelte";
|
|
69
|
+
import Pill from "../../Pill/Pill.svelte";
|
|
70
|
+
import InputWrap from "./InputWrap.svelte";
|
|
71
|
+
|
|
72
|
+
let {
|
|
73
|
+
input = $bindable(),
|
|
74
|
+
value = $bindable(),
|
|
75
|
+
chips,
|
|
76
|
+
label = "",
|
|
77
|
+
id = getId(),
|
|
78
|
+
tabindex = 0,
|
|
79
|
+
description,
|
|
80
|
+
class: classProp,
|
|
81
|
+
renderSize = "md",
|
|
82
|
+
name,
|
|
83
|
+
//
|
|
84
|
+
required = false,
|
|
85
|
+
disabled = false,
|
|
86
|
+
//
|
|
87
|
+
// Renamed local binding to avoid collision with `export function validate()` below.
|
|
88
|
+
validate: validateProp,
|
|
89
|
+
//
|
|
90
|
+
labelAfter,
|
|
91
|
+
below,
|
|
92
|
+
//
|
|
93
|
+
labelLeft = false,
|
|
94
|
+
labelLeftWidth = "normal",
|
|
95
|
+
labelLeftBreakpoint = 480,
|
|
96
|
+
//
|
|
97
|
+
classLabel,
|
|
98
|
+
classLabelBox,
|
|
99
|
+
classInputBox,
|
|
100
|
+
classInputBoxWrap,
|
|
101
|
+
classInputBoxWrapInvalid,
|
|
102
|
+
classDescBox,
|
|
103
|
+
classDescBoxToggle,
|
|
104
|
+
classBelowBox,
|
|
105
|
+
classValidationBox,
|
|
106
|
+
style = "",
|
|
107
|
+
//
|
|
108
|
+
placeholder,
|
|
109
|
+
openLabel,
|
|
110
|
+
classChip,
|
|
111
|
+
chipIntent,
|
|
112
|
+
onOpen,
|
|
113
|
+
onRemove,
|
|
114
|
+
}: Props = $props();
|
|
115
|
+
|
|
116
|
+
// chips sit one step smaller than the field they live in
|
|
117
|
+
const PILL_SIZE: Record<string, string> = { sm: "sm", md: "sm", lg: "md" };
|
|
118
|
+
let pillSize = $derived(PILL_SIZE[renderSize] ?? "sm");
|
|
119
|
+
|
|
120
|
+
let validation: ValidationResult | undefined = $state();
|
|
121
|
+
const setValidationResult = (res: ValidationResult) => (validation = res);
|
|
122
|
+
|
|
123
|
+
let _doValidate: (() => void) | undefined = $state();
|
|
124
|
+
let rowEl: HTMLDivElement | undefined = $state();
|
|
125
|
+
let openBtnEl: HTMLElement | undefined = $state();
|
|
126
|
+
|
|
127
|
+
/** Trigger validation now. Renders the inline message if invalid. */
|
|
128
|
+
export function validate(): ValidationResult | undefined {
|
|
129
|
+
_doValidate?.();
|
|
130
|
+
return validation;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Clear the inline validation message and reset `setCustomValidity`. */
|
|
134
|
+
export function clearValidation(): void {
|
|
135
|
+
validation = undefined;
|
|
136
|
+
input?.setCustomValidity?.("");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Current validation state, or undefined if validator has never run. */
|
|
140
|
+
export function getValidation(): ValidationResult | undefined {
|
|
141
|
+
return validation;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Focus the trailing "open" button (the hidden input cannot be focused). */
|
|
145
|
+
export function focus(): void {
|
|
146
|
+
openBtnEl?.focus?.();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Scroll the field into view. Defaults to smooth + center. */
|
|
150
|
+
export function scrollIntoView(opts?: ScrollIntoViewOptions): void {
|
|
151
|
+
rowEl?.scrollIntoView?.({
|
|
152
|
+
behavior: "smooth",
|
|
153
|
+
block: "center",
|
|
154
|
+
...opts,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// the empty part of the row is a pointer convenience: chips own their own clicks and
|
|
159
|
+
// the trailing button is the keyboard-reachable opener
|
|
160
|
+
function onRowClick(e: MouseEvent) {
|
|
161
|
+
if (disabled) return;
|
|
162
|
+
if ((e.target as Element | null)?.closest?.(".stuic-pill")) return;
|
|
163
|
+
onOpen();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// keep keyboard focus inside the field after a removal: the × now sitting at the same
|
|
167
|
+
// index, else the trailing button
|
|
168
|
+
function remove(chip: FieldLikeChip, index: number) {
|
|
169
|
+
onRemove(chip, index);
|
|
170
|
+
tick().then(() => {
|
|
171
|
+
const btns =
|
|
172
|
+
rowEl?.querySelectorAll<HTMLButtonElement>(".stuic-pill-dismiss") ?? [];
|
|
173
|
+
const next = btns[Math.min(index, btns.length - 1)];
|
|
174
|
+
(next ?? openBtnEl)?.focus?.();
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
</script>
|
|
178
|
+
|
|
179
|
+
<InputWrap
|
|
180
|
+
{description}
|
|
181
|
+
class={classProp}
|
|
182
|
+
size={renderSize}
|
|
183
|
+
{id}
|
|
184
|
+
{label}
|
|
185
|
+
{labelAfter}
|
|
186
|
+
{below}
|
|
187
|
+
{required}
|
|
188
|
+
{disabled}
|
|
189
|
+
{labelLeft}
|
|
190
|
+
{labelLeftWidth}
|
|
191
|
+
{labelLeftBreakpoint}
|
|
192
|
+
{classLabel}
|
|
193
|
+
{classLabelBox}
|
|
194
|
+
{classInputBox}
|
|
195
|
+
{classInputBoxWrap}
|
|
196
|
+
{classInputBoxWrapInvalid}
|
|
197
|
+
{classDescBox}
|
|
198
|
+
{classDescBoxToggle}
|
|
199
|
+
{classBelowBox}
|
|
200
|
+
{classValidationBox}
|
|
201
|
+
{validation}
|
|
202
|
+
{style}
|
|
203
|
+
>
|
|
204
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
205
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
206
|
+
<div
|
|
207
|
+
bind:this={rowEl}
|
|
208
|
+
class="stuic-field-options-chips"
|
|
209
|
+
data-size={renderSize}
|
|
210
|
+
data-empty={chips.length ? undefined : "true"}
|
|
211
|
+
onclick={onRowClick}
|
|
212
|
+
>
|
|
213
|
+
{#each chips as chip, i (chip.key)}
|
|
214
|
+
<Pill
|
|
215
|
+
dismissible
|
|
216
|
+
size={pillSize}
|
|
217
|
+
intent={chipIntent}
|
|
218
|
+
class={classChip}
|
|
219
|
+
dismissLabel={chip.removeLabel}
|
|
220
|
+
ondismiss={() => remove(chip, i)}
|
|
221
|
+
{disabled}
|
|
222
|
+
title={chip.label}
|
|
223
|
+
>
|
|
224
|
+
<!-- own span so an over-long label truncates (the Pill body is a flex row) -->
|
|
225
|
+
<span class="stuic-field-options-chip-label">{chip.label}</span>
|
|
226
|
+
</Pill>
|
|
227
|
+
{/each}
|
|
228
|
+
{#if !chips.length && placeholder}
|
|
229
|
+
<span class="stuic-field-options-chips-placeholder">{placeholder}</span>
|
|
230
|
+
{/if}
|
|
231
|
+
</div>
|
|
232
|
+
|
|
233
|
+
{#snippet inputAfter()}
|
|
234
|
+
<div class="flex items-center pr-1">
|
|
235
|
+
<Button
|
|
236
|
+
bind:el={openBtnEl}
|
|
237
|
+
iconButton
|
|
238
|
+
variant="ghost"
|
|
239
|
+
roundedFull
|
|
240
|
+
type="button"
|
|
241
|
+
size={renderSize === "lg" ? "md" : "sm"}
|
|
242
|
+
aria-label={openLabel}
|
|
243
|
+
tooltip={openLabel}
|
|
244
|
+
{disabled}
|
|
245
|
+
{tabindex}
|
|
246
|
+
onclick={() => onOpen()}
|
|
247
|
+
>
|
|
248
|
+
{@html iconSearch({ size: 18 })}
|
|
249
|
+
</Button>
|
|
250
|
+
</div>
|
|
251
|
+
{/snippet}
|
|
252
|
+
|
|
253
|
+
<input
|
|
254
|
+
bind:value
|
|
255
|
+
bind:this={input}
|
|
256
|
+
type="hidden"
|
|
257
|
+
{id}
|
|
258
|
+
{name}
|
|
259
|
+
use:validateAction={() => ({
|
|
260
|
+
enabled: validateProp !== false,
|
|
261
|
+
...(typeof validateProp === "boolean"
|
|
262
|
+
? {
|
|
263
|
+
// Return actual messages (not reason names) because hidden inputs
|
|
264
|
+
// don't support el.validationMessage - the validate action preserves
|
|
265
|
+
// our return value and uses it directly as the error message.
|
|
266
|
+
customValidator(val, ctx, el) {
|
|
267
|
+
if (required && !val)
|
|
268
|
+
return "This field requires attention. Please review and try again.";
|
|
269
|
+
|
|
270
|
+
// also, by default, JSON validation is built in
|
|
271
|
+
try {
|
|
272
|
+
JSON.parse(val as string);
|
|
273
|
+
return "";
|
|
274
|
+
} catch (e) {
|
|
275
|
+
return "This field is invalid. Please review and try again.";
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
}
|
|
279
|
+
: validateProp),
|
|
280
|
+
setValidationResult,
|
|
281
|
+
setDoValidate: (fn) => (_doValidate = fn),
|
|
282
|
+
})}
|
|
283
|
+
{required}
|
|
284
|
+
{disabled}
|
|
285
|
+
/>
|
|
286
|
+
</InputWrap>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { ValidateOptions } from "../../../actions/validate.svelte.js";
|
|
3
|
+
import type { IntentColorKey } from "../../../utils/design-tokens.js";
|
|
4
|
+
import type { THC } from "../../Thc/Thc.svelte";
|
|
5
|
+
import type { InputWrapClassProps } from "../types.js";
|
|
6
|
+
type SnippetWithId = Snippet<[{
|
|
7
|
+
id: string;
|
|
8
|
+
}]>;
|
|
9
|
+
/** One rendered chip. `key` must be unique within the row. */
|
|
10
|
+
export interface FieldLikeChip {
|
|
11
|
+
key: string | number;
|
|
12
|
+
label: string;
|
|
13
|
+
/** Accessible name of the chip's remove (×) button, e.g. "Remove Foo" */
|
|
14
|
+
removeLabel: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Internal: the `chips` trigger of `FieldOptions`. The FieldLikeButton counterpart for a
|
|
18
|
+
* selection shown as inline, individually removable Pills — same InputWrap shell, same
|
|
19
|
+
* hidden input + validate wiring, same imperative API.
|
|
20
|
+
*/
|
|
21
|
+
export interface Props extends InputWrapClassProps {
|
|
22
|
+
/** The hidden input carrying `value` + `name` (bindable) */
|
|
23
|
+
input?: HTMLInputElement;
|
|
24
|
+
value: string;
|
|
25
|
+
/** The chips to render (the parent derives them from `value`) */
|
|
26
|
+
chips: FieldLikeChip[];
|
|
27
|
+
label?: SnippetWithId | THC;
|
|
28
|
+
description?: SnippetWithId | THC;
|
|
29
|
+
class?: string;
|
|
30
|
+
id?: string;
|
|
31
|
+
tabindex?: number;
|
|
32
|
+
renderSize?: "sm" | "md" | "lg" | string;
|
|
33
|
+
name?: string;
|
|
34
|
+
required?: boolean;
|
|
35
|
+
disabled?: boolean;
|
|
36
|
+
validate?: boolean | Omit<ValidateOptions, "setValidationResult">;
|
|
37
|
+
labelAfter?: SnippetWithId | THC;
|
|
38
|
+
below?: SnippetWithId | THC;
|
|
39
|
+
labelLeft?: boolean;
|
|
40
|
+
labelLeftWidth?: "normal" | "wide";
|
|
41
|
+
labelLeftBreakpoint?: number;
|
|
42
|
+
style?: string;
|
|
43
|
+
/** Shown in the row while there are no chips */
|
|
44
|
+
placeholder?: string;
|
|
45
|
+
/** Accessible name (and tooltip) of the trailing "open" button */
|
|
46
|
+
openLabel: string;
|
|
47
|
+
/** Classes for each chip (Pill) */
|
|
48
|
+
classChip?: string;
|
|
49
|
+
/** Pill intent of the chips */
|
|
50
|
+
chipIntent?: IntentColorKey;
|
|
51
|
+
/** Open the picker (trailing button, or a click on the empty part of the row) */
|
|
52
|
+
onOpen: () => void;
|
|
53
|
+
/** A chip's × was pressed */
|
|
54
|
+
onRemove: (chip: FieldLikeChip, index: number) => void;
|
|
55
|
+
}
|
|
56
|
+
import { type ValidationResult } from "../../../actions/validate.svelte.js";
|
|
57
|
+
declare const FieldLikeChips: import("svelte").Component<Props, {
|
|
58
|
+
validate: () => ValidationResult | undefined;
|
|
59
|
+
clearValidation: () => void;
|
|
60
|
+
getValidation: () => ValidationResult | undefined;
|
|
61
|
+
focus: () => void;
|
|
62
|
+
scrollIntoView: (opts?: ScrollIntoViewOptions) => void;
|
|
63
|
+
}, "value" | "input">;
|
|
64
|
+
type FieldLikeChips = ReturnType<typeof FieldLikeChips>;
|
|
65
|
+
export default FieldLikeChips;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { FieldOptionsMessages } from "./field-options-i18n.js";
|
|
2
|
+
/**
|
|
3
|
+
* Slovak message catalog for `FieldOptions`. Opt-in — English stays the built-in
|
|
4
|
+
* default, and this module is only pulled into a bundle when it is actually imported
|
|
5
|
+
* (the component itself never references it).
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```svelte
|
|
9
|
+
* <script>
|
|
10
|
+
* import {
|
|
11
|
+
* FieldOptions,
|
|
12
|
+
* createFieldOptionsT,
|
|
13
|
+
* FIELD_OPTIONS_MESSAGES_SK,
|
|
14
|
+
* } from "@marianmeres/stuic";
|
|
15
|
+
* const t = createFieldOptionsT(FIELD_OPTIONS_MESSAGES_SK);
|
|
16
|
+
* </script>
|
|
17
|
+
*
|
|
18
|
+
* <FieldOptions name="tags" bind:value {getOptions} {t} />
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare const FIELD_OPTIONS_MESSAGES_SK: FieldOptionsMessages;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slovak message catalog for `FieldOptions`. Opt-in — English stays the built-in
|
|
3
|
+
* default, and this module is only pulled into a bundle when it is actually imported
|
|
4
|
+
* (the component itself never references it).
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```svelte
|
|
8
|
+
* <script>
|
|
9
|
+
* import {
|
|
10
|
+
* FieldOptions,
|
|
11
|
+
* createFieldOptionsT,
|
|
12
|
+
* FIELD_OPTIONS_MESSAGES_SK,
|
|
13
|
+
* } from "@marianmeres/stuic";
|
|
14
|
+
* const t = createFieldOptionsT(FIELD_OPTIONS_MESSAGES_SK);
|
|
15
|
+
* </script>
|
|
16
|
+
*
|
|
17
|
+
* <FieldOptions name="tags" bind:value {getOptions} {t} />
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export const FIELD_OPTIONS_MESSAGES_SK = {
|
|
21
|
+
field_req_att: "Toto pole vyžaduje pozornosť. Skontrolujte ho a skúste to znova.",
|
|
22
|
+
cardinality_of: "z max.",
|
|
23
|
+
cardinality_selected: "vybraných",
|
|
24
|
+
submit: "Potvrdiť",
|
|
25
|
+
select_all: "Vybrať všetko",
|
|
26
|
+
clear_all: "Zrušiť výber",
|
|
27
|
+
clear: "Zrušiť výber",
|
|
28
|
+
search_placeholder: "Píšte pre vyhľadávanie...",
|
|
29
|
+
search_submit_placeholder: "Píšte pre vyhľadanie a potvrdenie...",
|
|
30
|
+
cardinality_full: "Dosiahnutý maximálny počet výberov",
|
|
31
|
+
select_from_list: "Vyberte, prosím, iba zo zoznamu",
|
|
32
|
+
x_close: "Vymazať vstup alebo zavrieť [esc]",
|
|
33
|
+
close: "Zavrieť [esc]",
|
|
34
|
+
unknown_allowed: "Vyberte alebo napíšte a potvrďte",
|
|
35
|
+
unknown_not_allowed: "Vyberte zo zoznamu",
|
|
36
|
+
no_results: "Nenašli sa žiadne výsledky.",
|
|
37
|
+
add_new: 'Pridať "{{value}}"...',
|
|
38
|
+
click_add_new: "Ak chcete pokračovať, musíte hodnotu pridať",
|
|
39
|
+
// chips display mode
|
|
40
|
+
chips_placeholder: "Nič nie je vybrané",
|
|
41
|
+
chips_open: "Vybrať...",
|
|
42
|
+
chips_remove: "Odstrániť {{value}}",
|
|
43
|
+
//
|
|
44
|
+
pick_tab: "Výber",
|
|
45
|
+
arrange_tab: "Poradie ({{value}})",
|
|
46
|
+
arrange_help: "Zmeňte poradie vybraných položiek. Na presun použite tlačidlá.",
|
|
47
|
+
sort_az: "Zoradiť A–Z",
|
|
48
|
+
reverse: "Obrátiť",
|
|
49
|
+
shuffle: "Zamiešať",
|
|
50
|
+
move_up: "Posunúť vyššie",
|
|
51
|
+
move_down: "Posunúť nižšie",
|
|
52
|
+
move_to_top: "Presunúť na začiatok",
|
|
53
|
+
move_to_bottom: "Presunúť na koniec",
|
|
54
|
+
remove_item: "Odstrániť",
|
|
55
|
+
moved_up: "Posunuté vyššie: {{value}}",
|
|
56
|
+
moved_down: "Posunuté nižšie: {{value}}",
|
|
57
|
+
removed_item: "Odstránené: {{value}}",
|
|
58
|
+
sorted_az: "Zoradené od A po Z",
|
|
59
|
+
reversed: "Poradie obrátené",
|
|
60
|
+
shuffled: "Poradie zamiešané",
|
|
61
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { TranslateFn } from "../../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* The built-in (English) message catalog of `FieldOptions`. Also the fallback of every
|
|
4
|
+
* other bundled locale, so a locale missing a key still renders text.
|
|
5
|
+
*
|
|
6
|
+
* Placeholders are mustache-style (`{{value}}`).
|
|
7
|
+
*/
|
|
8
|
+
export declare const FIELD_OPTIONS_MESSAGES_EN: {
|
|
9
|
+
field_req_att: string;
|
|
10
|
+
cardinality_of: string;
|
|
11
|
+
cardinality_selected: string;
|
|
12
|
+
submit: string;
|
|
13
|
+
select_all: string;
|
|
14
|
+
clear_all: string;
|
|
15
|
+
clear: string;
|
|
16
|
+
search_placeholder: string;
|
|
17
|
+
search_submit_placeholder: string;
|
|
18
|
+
cardinality_full: string;
|
|
19
|
+
select_from_list: string;
|
|
20
|
+
x_close: string;
|
|
21
|
+
close: string;
|
|
22
|
+
unknown_allowed: string;
|
|
23
|
+
unknown_not_allowed: string;
|
|
24
|
+
no_results: string;
|
|
25
|
+
add_new: string;
|
|
26
|
+
click_add_new: string;
|
|
27
|
+
chips_placeholder: string;
|
|
28
|
+
chips_open: string;
|
|
29
|
+
chips_remove: string;
|
|
30
|
+
pick_tab: string;
|
|
31
|
+
arrange_tab: string;
|
|
32
|
+
arrange_help: string;
|
|
33
|
+
sort_az: string;
|
|
34
|
+
reverse: string;
|
|
35
|
+
shuffle: string;
|
|
36
|
+
move_up: string;
|
|
37
|
+
move_down: string;
|
|
38
|
+
move_to_top: string;
|
|
39
|
+
move_to_bottom: string;
|
|
40
|
+
remove_item: string;
|
|
41
|
+
moved_up: string;
|
|
42
|
+
moved_down: string;
|
|
43
|
+
removed_item: string;
|
|
44
|
+
sorted_az: string;
|
|
45
|
+
reversed: string;
|
|
46
|
+
shuffled: string;
|
|
47
|
+
};
|
|
48
|
+
/** Every message key `FieldOptions` may look up. */
|
|
49
|
+
export type FieldOptionsMessageKey = keyof typeof FIELD_OPTIONS_MESSAGES_EN;
|
|
50
|
+
/** A (possibly partial) catalog for one locale. */
|
|
51
|
+
export type FieldOptionsMessages = Record<FieldOptionsMessageKey, string>;
|
|
52
|
+
/**
|
|
53
|
+
* Builds the `t` prop of `FieldOptions` from a message catalog. Unknown or untranslated
|
|
54
|
+
* keys fall back to `fallbackMessages` (English by default), so a catalog may safely be
|
|
55
|
+
* partial and never renders a raw key.
|
|
56
|
+
*
|
|
57
|
+
* Handy with the `chips` display mode, where a few of the texts (`chips_placeholder`,
|
|
58
|
+
* `chips_open`, `chips_remove`) end up in the closed field, on the page, next to your own
|
|
59
|
+
* copy — reworded per field, without restating the whole catalog.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```svelte
|
|
63
|
+
* <script>
|
|
64
|
+
* import {
|
|
65
|
+
* FieldOptions,
|
|
66
|
+
* createFieldOptionsT,
|
|
67
|
+
* FIELD_OPTIONS_MESSAGES_SK,
|
|
68
|
+
* } from "@marianmeres/stuic";
|
|
69
|
+
*
|
|
70
|
+
* // full locale, plus a field specific tweak
|
|
71
|
+
* const t = createFieldOptionsT({
|
|
72
|
+
* ...FIELD_OPTIONS_MESSAGES_SK,
|
|
73
|
+
* chips_placeholder: "Bez štítkov",
|
|
74
|
+
* });
|
|
75
|
+
* </script>
|
|
76
|
+
*
|
|
77
|
+
* <FieldOptions chips name="tags" bind:value {getOptions} {t} />
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export declare function createFieldOptionsT(messages: Partial<FieldOptionsMessages> | Record<string, string>, fallbackMessages?: Partial<FieldOptionsMessages> | Record<string, string>): TranslateFn;
|
|
81
|
+
/** The component's built-in English `t`. */
|
|
82
|
+
export declare const t_default: TranslateFn;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { isPlainObject } from "../../utils/is-plain-object.js";
|
|
2
|
+
import { replaceMap } from "../../utils/replace-map.js";
|
|
3
|
+
/**
|
|
4
|
+
* The built-in (English) message catalog of `FieldOptions`. Also the fallback of every
|
|
5
|
+
* other bundled locale, so a locale missing a key still renders text.
|
|
6
|
+
*
|
|
7
|
+
* Placeholders are mustache-style (`{{value}}`).
|
|
8
|
+
*/
|
|
9
|
+
export const FIELD_OPTIONS_MESSAGES_EN = {
|
|
10
|
+
field_req_att: "This field requires attention. Please review and try again.",
|
|
11
|
+
cardinality_of: "of max",
|
|
12
|
+
cardinality_selected: "selected",
|
|
13
|
+
submit: "Submit",
|
|
14
|
+
select_all: "Select all",
|
|
15
|
+
clear_all: "Clear selected",
|
|
16
|
+
clear: "Clear",
|
|
17
|
+
search_placeholder: "Type to search...",
|
|
18
|
+
search_submit_placeholder: "Type to search and submit...",
|
|
19
|
+
cardinality_full: "Max selection reached",
|
|
20
|
+
select_from_list: "Please select from the list only",
|
|
21
|
+
x_close: "Clear input or close [esc]",
|
|
22
|
+
close: "Close [esc]",
|
|
23
|
+
unknown_allowed: "Select or type and submit",
|
|
24
|
+
unknown_not_allowed: "Select from the list",
|
|
25
|
+
no_results: "No results found.",
|
|
26
|
+
add_new: 'Add "{{value}}"...',
|
|
27
|
+
click_add_new: "You must add the value to continue",
|
|
28
|
+
// chips display mode
|
|
29
|
+
chips_placeholder: "Nothing selected",
|
|
30
|
+
chips_open: "Choose...",
|
|
31
|
+
chips_remove: "Remove {{value}}",
|
|
32
|
+
//
|
|
33
|
+
pick_tab: "Pick",
|
|
34
|
+
arrange_tab: "Arrange ({{value}})",
|
|
35
|
+
arrange_help: "Reorder the selected items. Use the buttons to move them.",
|
|
36
|
+
sort_az: "Sort A–Z",
|
|
37
|
+
reverse: "Reverse",
|
|
38
|
+
shuffle: "Shuffle",
|
|
39
|
+
move_up: "Move up",
|
|
40
|
+
move_down: "Move down",
|
|
41
|
+
move_to_top: "Move to top",
|
|
42
|
+
move_to_bottom: "Move to bottom",
|
|
43
|
+
remove_item: "Remove",
|
|
44
|
+
moved_up: "Moved {{value}} up",
|
|
45
|
+
moved_down: "Moved {{value}} down",
|
|
46
|
+
removed_item: "Removed {{value}}",
|
|
47
|
+
sorted_az: "Sorted A to Z",
|
|
48
|
+
reversed: "Order reversed",
|
|
49
|
+
shuffled: "Order shuffled",
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Builds the `t` prop of `FieldOptions` from a message catalog. Unknown or untranslated
|
|
53
|
+
* keys fall back to `fallbackMessages` (English by default), so a catalog may safely be
|
|
54
|
+
* partial and never renders a raw key.
|
|
55
|
+
*
|
|
56
|
+
* Handy with the `chips` display mode, where a few of the texts (`chips_placeholder`,
|
|
57
|
+
* `chips_open`, `chips_remove`) end up in the closed field, on the page, next to your own
|
|
58
|
+
* copy — reworded per field, without restating the whole catalog.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```svelte
|
|
62
|
+
* <script>
|
|
63
|
+
* import {
|
|
64
|
+
* FieldOptions,
|
|
65
|
+
* createFieldOptionsT,
|
|
66
|
+
* FIELD_OPTIONS_MESSAGES_SK,
|
|
67
|
+
* } from "@marianmeres/stuic";
|
|
68
|
+
*
|
|
69
|
+
* // full locale, plus a field specific tweak
|
|
70
|
+
* const t = createFieldOptionsT({
|
|
71
|
+
* ...FIELD_OPTIONS_MESSAGES_SK,
|
|
72
|
+
* chips_placeholder: "Bez štítkov",
|
|
73
|
+
* });
|
|
74
|
+
* </script>
|
|
75
|
+
*
|
|
76
|
+
* <FieldOptions chips name="tags" bind:value {getOptions} {t} />
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export function createFieldOptionsT(messages, fallbackMessages = FIELD_OPTIONS_MESSAGES_EN) {
|
|
80
|
+
return (k, values = null, fallback = "") => {
|
|
81
|
+
const out = messages[k] ??
|
|
82
|
+
fallbackMessages[k] ??
|
|
83
|
+
(typeof fallback === "string" ? fallback : k);
|
|
84
|
+
return isPlainObject(values)
|
|
85
|
+
? replaceMap(out, values)
|
|
86
|
+
: out;
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/** The component's built-in English `t`. */
|
|
90
|
+
export const t_default = createFieldOptionsT(FIELD_OPTIONS_MESSAGES_EN);
|
|
@@ -81,6 +81,8 @@
|
|
|
81
81
|
--stuic-field-options-control-ring: var(--stuic-color-ring);
|
|
82
82
|
--stuic-field-options-muted-text: var(--stuic-color-muted-foreground);
|
|
83
83
|
--stuic-field-options-optgroup-text: var(--stuic-color-muted-foreground);
|
|
84
|
+
/* FieldOptions `chips` display mode */
|
|
85
|
+
--stuic-field-options-chips-gap: 0.25rem;
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
@layer components {
|
|
@@ -561,6 +563,62 @@
|
|
|
561
563
|
opacity: 1;
|
|
562
564
|
}
|
|
563
565
|
|
|
566
|
+
/* `chips` display mode: the selection as inline, removable Pills (FieldLikeChips) */
|
|
567
|
+
.stuic-field-options-chips {
|
|
568
|
+
display: flex;
|
|
569
|
+
flex: 1 1 0%;
|
|
570
|
+
min-width: 0;
|
|
571
|
+
flex-wrap: wrap;
|
|
572
|
+
align-items: center;
|
|
573
|
+
gap: var(--stuic-field-options-chips-gap);
|
|
574
|
+
cursor: pointer;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.stuic-input.disabled .stuic-field-options-chips {
|
|
578
|
+
cursor: not-allowed;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/* An over-long label must not push the chip out of the field: cap the chip at the row
|
|
582
|
+
width and let the label span truncate (the × keeps its size) */
|
|
583
|
+
.stuic-field-options-chips .stuic-pill {
|
|
584
|
+
max-width: 100%;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
.stuic-field-options-chip-label {
|
|
588
|
+
min-width: 0;
|
|
589
|
+
overflow: hidden;
|
|
590
|
+
text-overflow: ellipsis;
|
|
591
|
+
white-space: nowrap;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/* Row metrics follow the input size tokens so the field lines up with its siblings */
|
|
595
|
+
.stuic-field-options-chips[data-size="sm"] {
|
|
596
|
+
padding: calc(var(--stuic-input-padding-y-sm) / 2) var(--stuic-input-padding-x-sm);
|
|
597
|
+
font-size: var(--stuic-input-font-size-sm);
|
|
598
|
+
min-height: var(--stuic-input-min-height-sm);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
.stuic-field-options-chips[data-size="md"],
|
|
602
|
+
.stuic-field-options-chips:not([data-size]) {
|
|
603
|
+
padding: calc(var(--stuic-input-padding-y-md) / 2) var(--stuic-input-padding-x-md);
|
|
604
|
+
font-size: var(--stuic-input-font-size-md);
|
|
605
|
+
min-height: var(--stuic-input-min-height-md);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
.stuic-field-options-chips[data-size="lg"] {
|
|
609
|
+
padding: calc(var(--stuic-input-padding-y-lg) / 2) var(--stuic-input-padding-x-lg);
|
|
610
|
+
font-size: var(--stuic-input-font-size-lg);
|
|
611
|
+
min-height: var(--stuic-input-min-height-lg);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
.stuic-field-options-chips-placeholder {
|
|
615
|
+
color: var(
|
|
616
|
+
--stuic-field-options-chips-placeholder-text,
|
|
617
|
+
var(--stuic-input-placeholder)
|
|
618
|
+
);
|
|
619
|
+
user-select: none;
|
|
620
|
+
}
|
|
621
|
+
|
|
564
622
|
/* ============================================================================
|
|
565
623
|
FIELD INPUT LOCALIZED
|
|
566
624
|
============================================================================ */
|
|
@@ -8,6 +8,8 @@ export { default as FieldInput, type Props as FieldInputProps, } from "./FieldIn
|
|
|
8
8
|
export { default as FieldMoney, type Props as FieldMoneyProps, } from "./FieldMoney.svelte";
|
|
9
9
|
export { default as FieldLikeButton, type Props as FieldLikeButtonProps, } from "./FieldLikeButton.svelte";
|
|
10
10
|
export { default as FieldOptions, type Props as FieldOptionsProps, type Option as FieldOption, } from "./FieldOptions.svelte";
|
|
11
|
+
export { createFieldOptionsT, FIELD_OPTIONS_MESSAGES_EN, type FieldOptionsMessageKey, type FieldOptionsMessages, } from "./field-options-i18n.js";
|
|
12
|
+
export { FIELD_OPTIONS_MESSAGES_SK } from "./field-options-i18n-sk.js";
|
|
11
13
|
export { default as FieldRadios, type Props as FieldRadiosProps, } from "./FieldRadios.svelte";
|
|
12
14
|
export { default as FieldSelect, type Props as FieldSelectProps, } from "./FieldSelect.svelte";
|
|
13
15
|
export { default as Fieldset, type Props as FieldsetProps } from "./Fieldset.svelte";
|