@marianmeres/stuic 3.180.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/README.md +3 -1
- package/dist/components/FieldsBuilder/types.d.ts +7 -2
- package/dist/components/FieldsBuilder/utils.d.ts +2 -1
- package/dist/components/FieldsBuilder/utils.js +4 -15
- 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
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
COLOR PICKER COMPONENT TOKENS
|
|
3
|
+
Override globally: :root { --stuic-color-picker-swatch-size: 2rem; }
|
|
4
|
+
Override locally: <ColorPicker style="--stuic-color-picker-gap: 0.5rem;">
|
|
5
|
+
============================================================================ */
|
|
6
|
+
|
|
7
|
+
/* prettier-ignore */
|
|
8
|
+
:root {
|
|
9
|
+
/* Layout */
|
|
10
|
+
--stuic-color-picker-gap: 0.375rem;
|
|
11
|
+
--stuic-color-picker-custom-gap: 0.5rem;
|
|
12
|
+
--stuic-color-picker-swatch-size: 1.75rem;
|
|
13
|
+
/* Touch bump: the swatches are the whole hit target, so on a coarse pointer
|
|
14
|
+
they must reach the ~44px recommendation on their own. */
|
|
15
|
+
--stuic-color-picker-swatch-size-touch: 2.75rem;
|
|
16
|
+
|
|
17
|
+
/* Swatch: a hairline keeps white / transparent / near-background swatches
|
|
18
|
+
visible on any theme. */
|
|
19
|
+
--stuic-color-picker-swatch-border: color-mix(in oklab, var(--stuic-color-foreground) 20%, transparent);
|
|
20
|
+
--stuic-color-picker-swatch-scale-hover: 1.1;
|
|
21
|
+
|
|
22
|
+
/* Selected ring — drawn OUTSIDE the swatch (a gap of page background, then
|
|
23
|
+
the ring) so it reads on any color without measuring its luminance. */
|
|
24
|
+
--stuic-color-picker-swatch-ring-width: 2px;
|
|
25
|
+
--stuic-color-picker-swatch-ring-gap: 2px;
|
|
26
|
+
--stuic-color-picker-swatch-ring-color: var(--stuic-color-foreground);
|
|
27
|
+
--stuic-color-picker-swatch-ring-gap-color: var(--stuic-color-background);
|
|
28
|
+
|
|
29
|
+
/* Focus ring */
|
|
30
|
+
--stuic-color-picker-ring-width: 2px;
|
|
31
|
+
--stuic-color-picker-ring-color: var(--stuic-color-ring);
|
|
32
|
+
|
|
33
|
+
/* The "no color" swatch (diagonal slash) */
|
|
34
|
+
--stuic-color-picker-clear-color: var(--stuic-color-destructive);
|
|
35
|
+
--stuic-color-picker-clear-bg: transparent;
|
|
36
|
+
|
|
37
|
+
/* Hex text field */
|
|
38
|
+
--stuic-color-picker-text-width: 10ch;
|
|
39
|
+
--stuic-color-picker-text-bg: var(--stuic-color-input);
|
|
40
|
+
--stuic-color-picker-text-border: var(--stuic-color-border);
|
|
41
|
+
--stuic-color-picker-text-color: var(--stuic-color-foreground);
|
|
42
|
+
--stuic-color-picker-text-placeholder: color-mix(in oklab, var(--stuic-color-muted-foreground) 75%, transparent);
|
|
43
|
+
--stuic-color-picker-text-font-family: var(--font-mono);
|
|
44
|
+
--stuic-color-picker-text-font-size: var(--text-sm);
|
|
45
|
+
/* See the "IOS ZOOM GUARD" section at the bottom of this file. */
|
|
46
|
+
--stuic-color-picker-text-font-size-touch-min: 16px;
|
|
47
|
+
|
|
48
|
+
/* Disabled */
|
|
49
|
+
--stuic-color-picker-opacity-disabled: 0.5;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@layer components {
|
|
53
|
+
/* ============================================================================
|
|
54
|
+
BASE
|
|
55
|
+
============================================================================ */
|
|
56
|
+
|
|
57
|
+
.stuic-color-picker {
|
|
58
|
+
--_size: var(--stuic-color-picker-swatch-size);
|
|
59
|
+
display: flex;
|
|
60
|
+
flex-direction: column;
|
|
61
|
+
align-items: flex-start;
|
|
62
|
+
gap: var(--stuic-color-picker-custom-gap);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.stuic-color-picker[data-disabled] {
|
|
66
|
+
opacity: var(--stuic-color-picker-opacity-disabled);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* ============================================================================
|
|
70
|
+
SWATCHES (the radiogroup)
|
|
71
|
+
============================================================================ */
|
|
72
|
+
|
|
73
|
+
.stuic-color-picker-swatches {
|
|
74
|
+
display: flex;
|
|
75
|
+
flex-wrap: wrap;
|
|
76
|
+
gap: var(--stuic-color-picker-gap);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* `columns` is a cap, not a grid: the row is exactly wide enough for N
|
|
80
|
+
swatches, so it holds N — and on a screen too narrow for that it wraps to
|
|
81
|
+
fewer instead of pushing the page sideways. (A real N-column grid would
|
|
82
|
+
overflow: 8 x 44px + gaps does not fit a phone.) The keyboard follows the
|
|
83
|
+
rendered rows, not this number — see `rowLength()`. */
|
|
84
|
+
.stuic-color-picker-swatches[data-columns] {
|
|
85
|
+
max-inline-size: calc(
|
|
86
|
+
var(--stuic-color-picker-columns, 6) * var(--_size) +
|
|
87
|
+
(var(--stuic-color-picker-columns, 6) - 1) * var(--stuic-color-picker-gap)
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* ============================================================================
|
|
92
|
+
SWATCH
|
|
93
|
+
============================================================================ */
|
|
94
|
+
|
|
95
|
+
.stuic-color-picker-swatch {
|
|
96
|
+
box-sizing: border-box;
|
|
97
|
+
inline-size: var(--_size);
|
|
98
|
+
block-size: var(--_size);
|
|
99
|
+
margin: 0;
|
|
100
|
+
padding: 0;
|
|
101
|
+
appearance: none;
|
|
102
|
+
cursor: pointer;
|
|
103
|
+
-webkit-tap-highlight-color: transparent;
|
|
104
|
+
background-color: var(--stuic-color-picker-swatch-color, transparent);
|
|
105
|
+
/* the hairline must not be tinted by the swatch color underneath it */
|
|
106
|
+
background-clip: padding-box;
|
|
107
|
+
border: var(--stuic-color-picker-swatch-border-width, var(--stuic-border-width)) solid
|
|
108
|
+
var(--stuic-color-picker-swatch-border);
|
|
109
|
+
border-radius: var(--stuic-color-picker-swatch-radius, var(--stuic-radius));
|
|
110
|
+
transition:
|
|
111
|
+
transform var(--stuic-color-picker-transition, var(--stuic-transition)),
|
|
112
|
+
box-shadow var(--stuic-color-picker-transition, var(--stuic-transition));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@media (hover: hover) {
|
|
116
|
+
.stuic-color-picker-swatch:hover:not(:disabled) {
|
|
117
|
+
transform: scale(var(--stuic-color-picker-swatch-scale-hover));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.stuic-color-picker-swatch:disabled {
|
|
122
|
+
cursor: default;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* The selected marker: a gap of background, then the ring. Two stacked
|
|
126
|
+
shadows rather than an outline, so the focus outline stays available. */
|
|
127
|
+
.stuic-color-picker-swatch[data-selected] {
|
|
128
|
+
box-shadow:
|
|
129
|
+
0 0 0 var(--stuic-color-picker-swatch-ring-gap)
|
|
130
|
+
var(--stuic-color-picker-swatch-ring-gap-color),
|
|
131
|
+
0 0 0
|
|
132
|
+
calc(
|
|
133
|
+
var(--stuic-color-picker-swatch-ring-gap) +
|
|
134
|
+
var(--stuic-color-picker-swatch-ring-width)
|
|
135
|
+
)
|
|
136
|
+
var(--stuic-color-picker-swatch-ring-color);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.stuic-color-picker-swatch:focus-visible {
|
|
140
|
+
outline: var(--stuic-color-picker-ring-width) solid
|
|
141
|
+
var(--stuic-color-picker-ring-color);
|
|
142
|
+
outline-offset: calc(
|
|
143
|
+
var(--stuic-color-picker-swatch-ring-gap) +
|
|
144
|
+
var(--stuic-color-picker-swatch-ring-width)
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* The "no color" swatch: a diagonal slash across an empty chip */
|
|
149
|
+
.stuic-color-picker-swatch[data-clear] {
|
|
150
|
+
background-color: var(--stuic-color-picker-clear-bg);
|
|
151
|
+
background-image: linear-gradient(
|
|
152
|
+
to top right,
|
|
153
|
+
transparent calc(50% - 0.5px),
|
|
154
|
+
var(--stuic-color-picker-clear-color) calc(50% - 0.5px),
|
|
155
|
+
var(--stuic-color-picker-clear-color) calc(50% + 0.5px),
|
|
156
|
+
transparent calc(50% + 0.5px)
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* ============================================================================
|
|
161
|
+
CUSTOM ROW (native picker + hex field)
|
|
162
|
+
============================================================================ */
|
|
163
|
+
|
|
164
|
+
.stuic-color-picker-custom {
|
|
165
|
+
display: flex;
|
|
166
|
+
flex-wrap: wrap;
|
|
167
|
+
align-items: center;
|
|
168
|
+
gap: var(--stuic-color-picker-gap);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* The native `<input type="color">`, shaped like one more swatch. */
|
|
172
|
+
.stuic-color-picker-native {
|
|
173
|
+
box-sizing: border-box;
|
|
174
|
+
inline-size: var(--_size);
|
|
175
|
+
block-size: var(--_size);
|
|
176
|
+
margin: 0;
|
|
177
|
+
padding: 0;
|
|
178
|
+
appearance: none;
|
|
179
|
+
-webkit-appearance: none;
|
|
180
|
+
background: none;
|
|
181
|
+
cursor: pointer;
|
|
182
|
+
border: var(--stuic-color-picker-swatch-border-width, var(--stuic-border-width)) solid
|
|
183
|
+
var(--stuic-color-picker-swatch-border);
|
|
184
|
+
border-radius: var(--stuic-color-picker-swatch-radius, var(--stuic-radius));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.stuic-color-picker-native:disabled {
|
|
188
|
+
cursor: default;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.stuic-color-picker-native::-webkit-color-swatch-wrapper {
|
|
192
|
+
padding: 0;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.stuic-color-picker-native::-webkit-color-swatch {
|
|
196
|
+
border: none;
|
|
197
|
+
border-radius: calc(
|
|
198
|
+
var(--stuic-color-picker-swatch-radius, var(--stuic-radius)) - 1px
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.stuic-color-picker-native::-moz-color-swatch {
|
|
203
|
+
border: none;
|
|
204
|
+
border-radius: calc(
|
|
205
|
+
var(--stuic-color-picker-swatch-radius, var(--stuic-radius)) - 1px
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.stuic-color-picker-native:focus-visible {
|
|
210
|
+
outline: var(--stuic-color-picker-ring-width) solid
|
|
211
|
+
var(--stuic-color-picker-ring-color);
|
|
212
|
+
outline-offset: 2px;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/* The hex field. Deliberately not `.stuic-input` — that is a whole field
|
|
216
|
+
wrapper (label, description, error box); this is one bare control. */
|
|
217
|
+
.stuic-color-picker-text {
|
|
218
|
+
box-sizing: border-box;
|
|
219
|
+
inline-size: var(--stuic-color-picker-text-width);
|
|
220
|
+
min-inline-size: 0;
|
|
221
|
+
min-block-size: var(--_size);
|
|
222
|
+
padding-inline: 0.5rem;
|
|
223
|
+
padding-block: 0.25rem;
|
|
224
|
+
font-family: var(--stuic-color-picker-text-font-family);
|
|
225
|
+
font-size: var(--stuic-color-picker-text-font-size);
|
|
226
|
+
color: var(--stuic-color-picker-text-color);
|
|
227
|
+
background-color: var(--stuic-color-picker-text-bg);
|
|
228
|
+
border: var(--stuic-color-picker-text-border-width, var(--stuic-border-width)) solid
|
|
229
|
+
var(--stuic-color-picker-text-border);
|
|
230
|
+
border-radius: var(--stuic-color-picker-text-radius, var(--stuic-radius));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.stuic-color-picker-text::placeholder {
|
|
234
|
+
color: var(--stuic-color-picker-text-placeholder);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.stuic-color-picker-text:focus-visible {
|
|
238
|
+
outline: var(--stuic-color-picker-ring-width) solid
|
|
239
|
+
var(--stuic-color-picker-ring-color);
|
|
240
|
+
outline-offset: 1px;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/* ============================================================================
|
|
244
|
+
TOUCH — the swatches are the entire hit target, so they carry the size
|
|
245
|
+
============================================================================ */
|
|
246
|
+
|
|
247
|
+
@media (pointer: coarse) {
|
|
248
|
+
.stuic-color-picker {
|
|
249
|
+
--_size: var(--stuic-color-picker-swatch-size-touch);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/* ============================================================================
|
|
255
|
+
IOS ZOOM GUARD
|
|
256
|
+
|
|
257
|
+
Mobile WebKit auto-zooms the viewport when a text control whose computed
|
|
258
|
+
font-size is below 16px receives focus, and the hex field defaults to 14px.
|
|
259
|
+
Same two conditions (and the same reasoning) as the guard in `Input/index.css`
|
|
260
|
+
— an Apple/WebKit engine AND a touch-capable device — and likewise unlayered,
|
|
261
|
+
so it also wins over a Tailwind text-size utility put on the field.
|
|
262
|
+
|
|
263
|
+
Opt out with `--stuic-color-picker-text-font-size-touch-min: 0px`.
|
|
264
|
+
============================================================================ */
|
|
265
|
+
|
|
266
|
+
@supports (font: -apple-system-body) or (-webkit-touch-callout: none) {
|
|
267
|
+
@media (any-pointer: coarse) {
|
|
268
|
+
.stuic-color-picker-text {
|
|
269
|
+
font-size: max(
|
|
270
|
+
var(--stuic-color-picker-text-font-size-touch-min),
|
|
271
|
+
var(--stuic-color-picker-text-font-size)
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { default as ColorPicker, type Props as ColorPickerProps, type ColorPickerCustom, } from "./ColorPicker.svelte";
|
|
2
|
+
export { COLOR_PICKER_PALETTE, COLOR_PICKER_PALETTE_THEME, type ColorPickerSwatch, type ColorPickerSwatchObject, } from "./palettes.js";
|
|
3
|
+
export { createColorPickerT, COLOR_PICKER_MESSAGES_EN, type ColorPickerMessageKey, type ColorPickerMessages, } from "./i18n.js";
|
|
4
|
+
export { COLOR_PICKER_MESSAGES_SK } from "./i18n-sk.js";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { default as ColorPicker, } from "./ColorPicker.svelte";
|
|
2
|
+
export { COLOR_PICKER_PALETTE, COLOR_PICKER_PALETTE_THEME, } from "./palettes.js";
|
|
3
|
+
export { createColorPickerT, COLOR_PICKER_MESSAGES_EN, } from "./i18n.js";
|
|
4
|
+
export { COLOR_PICKER_MESSAGES_SK } from "./i18n-sk.js";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** One palette entry: a bare CSS color string, or a color with a label. */
|
|
2
|
+
export type ColorPickerSwatch = string | ColorPickerSwatchObject;
|
|
3
|
+
/** The object form of a palette entry. */
|
|
4
|
+
export interface ColorPickerSwatchObject {
|
|
5
|
+
/**
|
|
6
|
+
* Any CSS color string — `#3b82f6`, `oklch(62.8% 0.258 29.2)`, `rgb(0 0 0)`,
|
|
7
|
+
* `transparent`, or a custom property reference like
|
|
8
|
+
* `var(--stuic-color-primary)`. Rendered as-is; never parsed.
|
|
9
|
+
*/
|
|
10
|
+
value: string;
|
|
11
|
+
/**
|
|
12
|
+
* Accessible name (and `title`) of the swatch. Passed through the component's
|
|
13
|
+
* `t` with itself as the fallback, so plain labels ("Brand blue") render
|
|
14
|
+
* verbatim while short keys ("red") get translated by a bundled catalog.
|
|
15
|
+
* Defaults to `value`.
|
|
16
|
+
*/
|
|
17
|
+
label?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The default `palette` of `ColorPicker`: 12 fixed hues plus white / grey / black.
|
|
21
|
+
*
|
|
22
|
+
* Deliberately literal hex, not theme tokens — a swatch palette must read the
|
|
23
|
+
* same on every theme (several of the bundled ones are monochrome). Use
|
|
24
|
+
* {@link COLOR_PICKER_PALETTE_THEME} when theme-following colors are what you want.
|
|
25
|
+
*/
|
|
26
|
+
export declare const COLOR_PICKER_PALETTE: ColorPickerSwatch[];
|
|
27
|
+
/**
|
|
28
|
+
* An opt-in palette of design-token colors — pass it as `palette` when the picked
|
|
29
|
+
* color should follow the active theme:
|
|
30
|
+
*
|
|
31
|
+
* ```svelte
|
|
32
|
+
* <ColorPicker bind:value palette={COLOR_PICKER_PALETTE_THEME} />
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* The stored value is the `var(--stuic-color-*)` reference itself, so it keeps
|
|
36
|
+
* following the theme wherever it is later applied. Two consequences worth
|
|
37
|
+
* knowing: the native color input cannot display such a value (it falls back to
|
|
38
|
+
* black until the user picks a hex), and on the monochrome themes several of
|
|
39
|
+
* these resolve to near-identical greys.
|
|
40
|
+
*/
|
|
41
|
+
export declare const COLOR_PICKER_PALETTE_THEME: ColorPickerSwatch[];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The default `palette` of `ColorPicker`: 12 fixed hues plus white / grey / black.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately literal hex, not theme tokens — a swatch palette must read the
|
|
5
|
+
* same on every theme (several of the bundled ones are monochrome). Use
|
|
6
|
+
* {@link COLOR_PICKER_PALETTE_THEME} when theme-following colors are what you want.
|
|
7
|
+
*/
|
|
8
|
+
export const COLOR_PICKER_PALETTE = [
|
|
9
|
+
{ value: "#ef4444", label: "red" },
|
|
10
|
+
{ value: "#f97316", label: "orange" },
|
|
11
|
+
{ value: "#f59e0b", label: "amber" },
|
|
12
|
+
{ value: "#eab308", label: "yellow" },
|
|
13
|
+
{ value: "#84cc16", label: "lime" },
|
|
14
|
+
{ value: "#22c55e", label: "green" },
|
|
15
|
+
{ value: "#14b8a6", label: "teal" },
|
|
16
|
+
{ value: "#06b6d4", label: "cyan" },
|
|
17
|
+
{ value: "#3b82f6", label: "blue" },
|
|
18
|
+
{ value: "#6366f1", label: "indigo" },
|
|
19
|
+
{ value: "#8b5cf6", label: "violet" },
|
|
20
|
+
{ value: "#ec4899", label: "pink" },
|
|
21
|
+
{ value: "#ffffff", label: "white" },
|
|
22
|
+
{ value: "#6b7280", label: "gray" },
|
|
23
|
+
{ value: "#000000", label: "black" },
|
|
24
|
+
];
|
|
25
|
+
/**
|
|
26
|
+
* An opt-in palette of design-token colors — pass it as `palette` when the picked
|
|
27
|
+
* color should follow the active theme:
|
|
28
|
+
*
|
|
29
|
+
* ```svelte
|
|
30
|
+
* <ColorPicker bind:value palette={COLOR_PICKER_PALETTE_THEME} />
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* The stored value is the `var(--stuic-color-*)` reference itself, so it keeps
|
|
34
|
+
* following the theme wherever it is later applied. Two consequences worth
|
|
35
|
+
* knowing: the native color input cannot display such a value (it falls back to
|
|
36
|
+
* black until the user picks a hex), and on the monochrome themes several of
|
|
37
|
+
* these resolve to near-identical greys.
|
|
38
|
+
*/
|
|
39
|
+
export const COLOR_PICKER_PALETTE_THEME = [
|
|
40
|
+
{ value: "var(--stuic-color-primary)", label: "primary" },
|
|
41
|
+
{ value: "var(--stuic-color-accent)", label: "accent" },
|
|
42
|
+
{ value: "var(--stuic-color-success)", label: "success" },
|
|
43
|
+
{ value: "var(--stuic-color-warning)", label: "warning" },
|
|
44
|
+
{ value: "var(--stuic-color-destructive)", label: "destructive" },
|
|
45
|
+
{ value: "var(--stuic-color-foreground)", label: "foreground" },
|
|
46
|
+
{ value: "var(--stuic-color-muted-foreground)", label: "muted_foreground" },
|
|
47
|
+
{ value: "var(--stuic-color-muted)", label: "muted" },
|
|
48
|
+
{ value: "var(--stuic-color-background)", label: "background" },
|
|
49
|
+
];
|
|
@@ -314,7 +314,9 @@ Slovak chrome (`t`), and still edits the canonical English label first:
|
|
|
314
314
|
```
|
|
315
315
|
|
|
316
316
|
`getLocalizedText(text, preferred)` — exported — accepts the same fallback chain as an
|
|
317
|
-
array: `getLocalizedText(label, ["sk", "en"])`.
|
|
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.
|
|
318
320
|
|
|
319
321
|
## Accessibility
|
|
320
322
|
|
|
@@ -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. */
|
|
@@ -8,7 +8,8 @@ export type FieldsBuilderTranslate = (key: string, values?: Record<string, strin
|
|
|
8
8
|
* Read the display text of a `LocalizedText`: the string itself, the entry of
|
|
9
9
|
* the first preferred language (a single one, or a fallback chain in order of
|
|
10
10
|
* preference) that is non-empty, or the first non-empty entry as a last
|
|
11
|
-
* resort.
|
|
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).
|
|
12
13
|
*/
|
|
13
14
|
export declare function getLocalizedText(text: LocalizedText | null | undefined, preferredLanguage?: string | string[]): string;
|
|
14
15
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
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;
|
|
@@ -5,23 +6,11 @@ export const DEFAULT_KEY_MAX_LENGTH = 63;
|
|
|
5
6
|
* Read the display text of a `LocalizedText`: the string itself, the entry of
|
|
6
7
|
* the first preferred language (a single one, or a fallback chain in order of
|
|
7
8
|
* preference) that is non-empty, or the first non-empty entry as a last
|
|
8
|
-
* resort.
|
|
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).
|
|
9
11
|
*/
|
|
10
12
|
export function getLocalizedText(text, preferredLanguage) {
|
|
11
|
-
|
|
12
|
-
return "";
|
|
13
|
-
if (typeof text === "string")
|
|
14
|
-
return text;
|
|
15
|
-
const preferred = typeof preferredLanguage === "string"
|
|
16
|
-
? [preferredLanguage]
|
|
17
|
-
: (preferredLanguage ?? []);
|
|
18
|
-
for (const lang of preferred)
|
|
19
|
-
if (lang && text[lang])
|
|
20
|
-
return text[lang];
|
|
21
|
-
for (const v of Object.values(text))
|
|
22
|
-
if (v)
|
|
23
|
-
return v;
|
|
24
|
-
return "";
|
|
13
|
+
return tr(text, preferredLanguage);
|
|
25
14
|
}
|
|
26
15
|
/**
|
|
27
16
|
* Derive a machine key from a human label: transliterates diacritics
|
|
@@ -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,
|