@marianmeres/stuic 3.104.0 → 3.106.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.
|
@@ -173,6 +173,15 @@ export function validate(el, fn) {
|
|
|
173
173
|
const _doValidate = () => {
|
|
174
174
|
if (!enabled)
|
|
175
175
|
return;
|
|
176
|
+
// A focused, dirty field torn down by a route change fires a final
|
|
177
|
+
// synchronous `change`/`blur` while being removed from the DOM. That
|
|
178
|
+
// removal runs inside Svelte's flush, so writing `validation` state here
|
|
179
|
+
// would throw `state_unsafe_mutation`. By then the node is already
|
|
180
|
+
// detached (`isConnected === false`) and the field is going away — skip.
|
|
181
|
+
// No-op for normal interactive validation and for the synchronous
|
|
182
|
+
// imperative `validate()` path, both of which run while connected.
|
|
183
|
+
if (!el.isConnected)
|
|
184
|
+
return;
|
|
176
185
|
el.checkValidity();
|
|
177
186
|
// Store customValidator message directly - hidden inputs (type="hidden")
|
|
178
187
|
// don't populate el.validationMessage even when setCustomValidity() is called.
|
|
@@ -210,9 +219,6 @@ export function validate(el, fn) {
|
|
|
210
219
|
return m;
|
|
211
220
|
}, []);
|
|
212
221
|
// console.log(1111, validityState, el);
|
|
213
|
-
// hm... Uncaught Svelte error: state_unsafe_mutation...
|
|
214
|
-
// the `tick` await helps, but I'm not really sure I understand the internals...
|
|
215
|
-
// tick().then(() => {
|
|
216
222
|
setValidationResult?.({
|
|
217
223
|
validity: validityState,
|
|
218
224
|
reasons,
|
|
@@ -224,7 +230,6 @@ export function validate(el, fn) {
|
|
|
224
230
|
el.validationMessage ||
|
|
225
231
|
"This field is invalid. Please review and try again."),
|
|
226
232
|
});
|
|
227
|
-
// });
|
|
228
233
|
};
|
|
229
234
|
// Expose the current validator to the host so it can trigger validation
|
|
230
235
|
// imperatively (e.g., on submit). The closure captures the current
|
|
@@ -32,6 +32,14 @@
|
|
|
32
32
|
unstyled?: boolean;
|
|
33
33
|
/** Render as rounded-full */
|
|
34
34
|
roundedFull?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Trim the icon-side horizontal padding down to the vertical padding, so a
|
|
37
|
+
* leading/trailing icon sits the same distance from the edge as a rounded icon
|
|
38
|
+
* (aspect1) button. Pair with `roundedFull` for the "rounded icon button with
|
|
39
|
+
* label" pill look (e.g. a "Back" button: prev arrow + label). Size-aware and
|
|
40
|
+
* RTL-aware (`leading` = start side, `trailing` = end side).
|
|
41
|
+
*/
|
|
42
|
+
iconEdge?: "leading" | "trailing";
|
|
35
43
|
/** Render as aspect ratio 1 */
|
|
36
44
|
aspect1?: boolean;
|
|
37
45
|
/** Icon-only button (implies aspect1, adds data-icon-button for global CSS targeting) */
|
|
@@ -104,6 +112,7 @@
|
|
|
104
112
|
raised = false,
|
|
105
113
|
unstyled = false,
|
|
106
114
|
roundedFull = false,
|
|
115
|
+
iconEdge,
|
|
107
116
|
aspect1 = false,
|
|
108
117
|
iconButton = false,
|
|
109
118
|
tooltip: _tooltip,
|
|
@@ -168,6 +177,7 @@
|
|
|
168
177
|
data-raised={!unstyled && raised ? "true" : undefined}
|
|
169
178
|
data-checked={roleSwitch && checked ? "true" : undefined}
|
|
170
179
|
data-rounded-full={!unstyled && roundedFull ? "true" : undefined}
|
|
180
|
+
data-icon-edge={!unstyled && iconEdge ? iconEdge : undefined}
|
|
171
181
|
data-aspect1={!unstyled && _isAspect1 ? "true" : undefined}
|
|
172
182
|
data-icon-button={!unstyled && _isIconButton ? "true" : undefined}
|
|
173
183
|
data-x={!unstyled && !!_xProps ? "true" : undefined}
|
|
@@ -213,6 +223,7 @@
|
|
|
213
223
|
data-raised={!unstyled && raised ? "true" : undefined}
|
|
214
224
|
data-checked={roleSwitch && checked ? "true" : undefined}
|
|
215
225
|
data-rounded-full={!unstyled && roundedFull ? "true" : undefined}
|
|
226
|
+
data-icon-edge={!unstyled && iconEdge ? iconEdge : undefined}
|
|
216
227
|
data-aspect1={!unstyled && _isAspect1 ? "true" : undefined}
|
|
217
228
|
data-icon-button={!unstyled && _isIconButton ? "true" : undefined}
|
|
218
229
|
data-x={!unstyled && !!_xProps ? "true" : undefined}
|
|
@@ -27,6 +27,14 @@ export interface Props extends Omit<HTMLButtonAttributes, "children"> {
|
|
|
27
27
|
unstyled?: boolean;
|
|
28
28
|
/** Render as rounded-full */
|
|
29
29
|
roundedFull?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Trim the icon-side horizontal padding down to the vertical padding, so a
|
|
32
|
+
* leading/trailing icon sits the same distance from the edge as a rounded icon
|
|
33
|
+
* (aspect1) button. Pair with `roundedFull` for the "rounded icon button with
|
|
34
|
+
* label" pill look (e.g. a "Back" button: prev arrow + label). Size-aware and
|
|
35
|
+
* RTL-aware (`leading` = start side, `trailing` = end side).
|
|
36
|
+
*/
|
|
37
|
+
iconEdge?: "leading" | "trailing";
|
|
30
38
|
/** Render as aspect ratio 1 */
|
|
31
39
|
aspect1?: boolean;
|
|
32
40
|
/** Icon-only button (implies aspect1, adds data-icon-button for global CSS targeting) */
|
|
@@ -20,6 +20,7 @@ A flexible button component with semantic intents, visual variants, sizes, and o
|
|
|
20
20
|
| `iconSwap` | `[string \| Snippet, string \| Snippet]` | - | Two icon states with swap animation (implies iconButton) |
|
|
21
21
|
| `x` | `boolean \| XProps` | - | Normalized "X" icon button shortcut (close/dismiss) |
|
|
22
22
|
| `nav` | `"prev" \| "next" \| ButtonNavProps` | - | Normalized prev/next icon button shortcut (arrow by default; `x` wins on conflict) |
|
|
23
|
+
| `iconEdge` | `"leading" \| "trailing"` | - | Trim icon-side padding to the y-padding (pill + edge-flush icon; pair with `roundedFull`) |
|
|
23
24
|
| `class` | `string` | - | Additional CSS classes |
|
|
24
25
|
|
|
25
26
|
## Snippet Props
|
|
@@ -134,6 +135,26 @@ Global CSS targeting for all icon buttons:
|
|
|
134
135
|
}
|
|
135
136
|
```
|
|
136
137
|
|
|
138
|
+
### Pill with edge-flush icon (`iconEdge`)
|
|
139
|
+
|
|
140
|
+
The "rounded icon button with label" look: a pill-shaped button whose leading (or
|
|
141
|
+
trailing) icon sits the same distance from the edge as a rounded icon (nav) button.
|
|
142
|
+
`iconEdge` trims the icon-side horizontal padding down to the vertical padding — it's
|
|
143
|
+
size-aware and uses logical properties, so `leading`/`trailing` follow text direction
|
|
144
|
+
(RTL-safe). It does NOT round the button on its own; pair it with `roundedFull`.
|
|
145
|
+
|
|
146
|
+
```svelte
|
|
147
|
+
<Button roundedFull iconEdge="leading">{@html iconArrowLeft({ size: 24 })} Back</Button>
|
|
148
|
+
<Button roundedFull iconEdge="trailing">Next {@html iconArrowRight({ size: 24 })}</Button>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Notes:
|
|
152
|
+
|
|
153
|
+
- `iconEdge` is composable — it only trims padding, so it also works on a
|
|
154
|
+
default-radius (non-pill) button if you want the icon flush without the pill shape.
|
|
155
|
+
- You compose the icon + label yourself in `children` (no default icon, unlike `nav`),
|
|
156
|
+
so it works with any icon. Use `size: 24` to match the nav button's arrow.
|
|
157
|
+
|
|
137
158
|
### Custom Styling
|
|
138
159
|
|
|
139
160
|
```svelte
|
|
@@ -199,6 +220,7 @@ The component uses data attributes for styling:
|
|
|
199
220
|
- `data-raised` - Present when raised
|
|
200
221
|
- `data-checked` - Present when roleSwitch is enabled and checked
|
|
201
222
|
- `data-rounded-full` - Present when roundedFull
|
|
223
|
+
- `data-icon-edge` - Set to `"leading"` or `"trailing"` when iconEdge is set
|
|
202
224
|
- `data-aspect1` - Present when aspect1 (or iconButton, or x, or nav)
|
|
203
225
|
- `data-icon-button` - Present when iconButton (or x, or nav)
|
|
204
226
|
- `data-x` - Present when x is set
|
|
@@ -422,6 +422,38 @@
|
|
|
422
422
|
padding: var(--stuic-button-padding-y-xl);
|
|
423
423
|
}
|
|
424
424
|
|
|
425
|
+
/* ============================================================================
|
|
426
|
+
ICON EDGE
|
|
427
|
+
Trim the icon-side horizontal padding down to the vertical padding, so a
|
|
428
|
+
leading/trailing icon sits the same distance from the edge as a rounded icon
|
|
429
|
+
(aspect1) button. Pair with [data-rounded-full] for the "rounded icon button
|
|
430
|
+
with label" pill look. Logical props => leading/trailing follow text direction.
|
|
431
|
+
============================================================================ */
|
|
432
|
+
.stuic-button[data-icon-edge="leading"][data-size="sm"] {
|
|
433
|
+
padding-inline-start: var(--stuic-button-padding-y-sm);
|
|
434
|
+
}
|
|
435
|
+
.stuic-button[data-icon-edge="leading"][data-size="md"] {
|
|
436
|
+
padding-inline-start: var(--stuic-button-padding-y-md);
|
|
437
|
+
}
|
|
438
|
+
.stuic-button[data-icon-edge="leading"][data-size="lg"] {
|
|
439
|
+
padding-inline-start: var(--stuic-button-padding-y-lg);
|
|
440
|
+
}
|
|
441
|
+
.stuic-button[data-icon-edge="leading"][data-size="xl"] {
|
|
442
|
+
padding-inline-start: var(--stuic-button-padding-y-xl);
|
|
443
|
+
}
|
|
444
|
+
.stuic-button[data-icon-edge="trailing"][data-size="sm"] {
|
|
445
|
+
padding-inline-end: var(--stuic-button-padding-y-sm);
|
|
446
|
+
}
|
|
447
|
+
.stuic-button[data-icon-edge="trailing"][data-size="md"] {
|
|
448
|
+
padding-inline-end: var(--stuic-button-padding-y-md);
|
|
449
|
+
}
|
|
450
|
+
.stuic-button[data-icon-edge="trailing"][data-size="lg"] {
|
|
451
|
+
padding-inline-end: var(--stuic-button-padding-y-lg);
|
|
452
|
+
}
|
|
453
|
+
.stuic-button[data-icon-edge="trailing"][data-size="xl"] {
|
|
454
|
+
padding-inline-end: var(--stuic-button-padding-y-xl);
|
|
455
|
+
}
|
|
456
|
+
|
|
425
457
|
/* ============================================================================
|
|
426
458
|
ICON BUTTON
|
|
427
459
|
Semantic marker for icon-only buttons. Layout is handled by [data-aspect1].
|