@marianmeres/stuic 3.115.0 → 3.116.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/API.md +297 -304
- package/dist/actions/dim-behind/index.css +4 -1
- package/dist/actions/focus-trap.js +3 -1
- package/dist/components/Accordion/README.md +17 -17
- package/dist/components/Accordion/index.css +4 -2
- package/dist/components/AssetsPreview/README.md +7 -7
- package/dist/components/AssetsPreview/_internal/assets-preview-types.d.ts +1 -2
- package/dist/components/AssetsPreview/_internal/assets-preview-utils.d.ts +1 -1
- package/dist/components/AssetsPreview/_internal/assets-preview-utils.js +9 -3
- package/dist/components/Avatar/Avatar.svelte +1 -3
- package/dist/components/Avatar/README.md +33 -27
- package/dist/components/Book/Book.svelte +6 -1
- package/dist/components/Book/README.md +22 -20
- package/dist/components/Book/index.css +4 -2
- package/dist/components/Button/README.md +17 -17
- package/dist/components/Card/Card.svelte +25 -8
- package/dist/components/Card/README.md +52 -56
- package/dist/components/Card/index.css +2 -1
- package/dist/components/Carousel/Carousel.svelte +1 -3
- package/dist/components/Carousel/README.md +28 -28
- package/dist/components/Cart/Cart.svelte +2 -1
- package/dist/components/Cart/README.md +25 -25
- package/dist/components/Checkout/CheckoutGuestOrLoginForm.svelte +8 -3
- package/dist/components/Checkout/CheckoutShippingStep.svelte +1 -2
- package/dist/components/Checkout/README.md +143 -130
- package/dist/components/CronInput/CronInput.svelte +64 -60
- package/dist/components/CronInput/README.md +46 -46
- package/dist/components/DataTable/DataTable.svelte +5 -1
- package/dist/components/DataTable/README.md +78 -63
- package/dist/components/DropdownMenu/DropdownMenu.svelte +6 -2
- package/dist/components/DropdownMenu/README.md +33 -27
- package/dist/components/EmailVerifyForm/EmailVerifyForm.svelte +2 -9
- package/dist/components/EmailVerifyForm/README.md +30 -30
- package/dist/components/Header/Header.svelte +161 -165
- package/dist/components/Header/README.md +7 -7
- package/dist/components/IconSwap/README.md +20 -15
- package/dist/components/IconSwap/index.css +2 -1
- package/dist/components/ImageCycler/ImageCycler.svelte +19 -5
- package/dist/components/ImageCycler/ImageCycler.svelte.d.ts +14 -10
- package/dist/components/ImageCycler/README.md +15 -15
- package/dist/components/ImageCycler/index.css +26 -20
- package/dist/components/Input/FieldFile.svelte +1 -3
- package/dist/components/Input/FieldInput.svelte +1 -3
- package/dist/components/Input/FieldKeyValues.svelte +2 -6
- package/dist/components/Input/FieldObject.svelte +2 -1
- package/dist/components/Input/README.md +11 -11
- package/dist/components/KbdShortcut/index.css +2 -1
- package/dist/components/LoginForm/LoginForm.svelte +1 -7
- package/dist/components/LoginForm/README.md +46 -46
- package/dist/components/ModalDialog/index.css +2 -1
- package/dist/components/Notifications/index.css +24 -6
- package/dist/components/OtpInput/OtpInput.svelte +0 -0
- package/dist/components/OtpInput/README.md +15 -19
- package/dist/components/OtpInput/index.css +1 -4
- package/dist/components/OtpInput/index.d.ts +1 -1
- package/dist/components/OtpInput/index.js +1 -1
- package/dist/components/Pill/README.md +41 -40
- package/dist/components/Pill/index.css +3 -6
- package/dist/components/PricingTable/README.md +86 -86
- package/dist/components/PricingTable/index.css +20 -35
- package/dist/components/RegisterForm/README.md +60 -60
- package/dist/components/RegisterForm/RegisterForm.svelte +1 -7
- package/dist/components/Separator/README.md +7 -7
- package/dist/components/TabbedMenu/index.css +6 -3
- package/dist/components/Tree/README.md +67 -67
- package/dist/components/UserAvatarMenu/UserAvatarMenu.svelte +1 -5
- package/dist/components/WithSidePanel/index.css +4 -4
- package/dist/index.css +12 -8
- package/dist/utils/design-tokens.d.ts +1 -1
- package/dist/utils/design-tokens.js +1 -1
- package/docs/architecture.md +7 -7
- package/docs/component-testing/00-overview-and-roadmap.md +19 -19
- package/docs/component-testing/01-framework-setup.md +6 -6
- package/docs/component-testing/02-test-conventions.md +6 -5
- package/docs/component-testing/03-component-coverage-roadmap.md +27 -27
- package/docs/component-testing/04-hard-cases-and-e2e.md +8 -8
- package/docs/component-testing/05-ci.md +3 -3
- package/docs/component-testing/PROGRESS.md +30 -26
- package/docs/component-testing/README.md +8 -8
- package/docs/conventions.md +25 -25
- package/docs/domains/components.md +386 -385
- package/docs/domains/theming.md +24 -24
- package/docs/domains/utils.md +22 -25
- package/docs/testing.md +2 -2
- package/docs/upgrading.md +32 -28
- package/package.json +2 -1
|
@@ -6,80 +6,80 @@ Standalone registration form. Same conventions as [`LoginForm`](../LoginForm/REA
|
|
|
6
6
|
|
|
7
7
|
## Exports
|
|
8
8
|
|
|
9
|
-
| Export
|
|
10
|
-
|
|
|
11
|
-
| `RegisterForm`
|
|
12
|
-
| `RegisterFormModal`
|
|
13
|
-
| `RegisterFormProps`
|
|
14
|
-
| `RegisterFormModalProps`
|
|
15
|
-
| `RegisterFormData`
|
|
16
|
-
| `RegisterFormValidationError`
|
|
17
|
-
| `RegisterFieldConfig`
|
|
18
|
-
| `createEmptyRegisterFormData`
|
|
19
|
-
| `validateRegisterForm`
|
|
9
|
+
| Export | Kind | Description |
|
|
10
|
+
| ----------------------------- | --------- | ------------------------------------------------ |
|
|
11
|
+
| `RegisterForm` | component | Form component |
|
|
12
|
+
| `RegisterFormModal` | component | Modal-wrapped form with optional trigger snippet |
|
|
13
|
+
| `RegisterFormProps` | type | Props for `RegisterForm` |
|
|
14
|
+
| `RegisterFormModalProps` | type | Props for `RegisterFormModal` |
|
|
15
|
+
| `RegisterFormData` | type | `{ email, password, passwordConfirm, extra }` |
|
|
16
|
+
| `RegisterFormValidationError` | type | `{ field, message }` |
|
|
17
|
+
| `RegisterFieldConfig` | type | Declarative extra-field descriptor |
|
|
18
|
+
| `createEmptyRegisterFormData` | function | Factory for an empty `RegisterFormData` |
|
|
19
|
+
| `validateRegisterForm` | function | `(data, t, extraFields, opts) => Error[]` |
|
|
20
20
|
|
|
21
21
|
## `RegisterFieldConfig`
|
|
22
22
|
|
|
23
23
|
```ts
|
|
24
24
|
interface RegisterFieldConfig {
|
|
25
|
-
name: string;
|
|
26
|
-
label: string;
|
|
25
|
+
name: string; // unique key under formData.extra
|
|
26
|
+
label: string; // already-translated
|
|
27
27
|
type?: "text" | "email" | "tel" | "url" | "password" | "number";
|
|
28
28
|
placeholder?: string;
|
|
29
29
|
required?: boolean;
|
|
30
30
|
autocomplete?: HTMLInputAttributes["autocomplete"];
|
|
31
31
|
initialValue?: unknown;
|
|
32
32
|
validate?: (value: unknown, data: RegisterFormData) => string | undefined;
|
|
33
|
-
position?: "top" | "bottom";
|
|
34
|
-
props?: Record<string, unknown>;
|
|
33
|
+
position?: "top" | "bottom"; // default "bottom"
|
|
34
|
+
props?: Record<string, unknown>; // passthrough to FieldInput
|
|
35
35
|
}
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
## RegisterForm — Props
|
|
39
39
|
|
|
40
|
-
| Prop | Type
|
|
41
|
-
| --------------------- |
|
|
42
|
-
| `formData` | `RegisterFormData`
|
|
43
|
-
| `onSubmit` | `(data: RegisterFormData) => void`
|
|
44
|
-
| `isSubmitting` | `boolean`
|
|
45
|
-
| `errors` | `RegisterFormValidationError[]`
|
|
46
|
-
| `error` | `string`
|
|
47
|
-
| `showPasswordConfirm` | `boolean`
|
|
48
|
-
| `passwordMinLength` | `number`
|
|
49
|
-
| `extraFields` | `RegisterFieldConfig[]`
|
|
50
|
-
| `extraFieldsSlot` | `Snippet<[{ formData, fieldError }]>`
|
|
51
|
-
| `submitLabel` | `string`
|
|
52
|
-
| `submittingLabel` | `string`
|
|
53
|
-
| `submitButton` | `Snippet<[{ isSubmitting, disabled }]>`
|
|
54
|
-
| `socialLogins` | `Snippet`
|
|
55
|
-
| `socialDividerLabel` | `string \| false`
|
|
56
|
-
| `footer` | `Snippet`
|
|
57
|
-
| `notifications` | `NotificationsStack`
|
|
58
|
-
| `t` | `TranslateFn`
|
|
59
|
-
| `unstyled` / `class` | -
|
|
60
|
-
| `el` | `HTMLFormElement`
|
|
40
|
+
| Prop | Type | Default | Description |
|
|
41
|
+
| --------------------- | --------------------------------------- | -------- | ----------------------------------------------------------------------------------- |
|
|
42
|
+
| `formData` | `RegisterFormData` | empty | Bindable form data. |
|
|
43
|
+
| `onSubmit` | `(data: RegisterFormData) => void` | required | Called after client-side validation passes. |
|
|
44
|
+
| `isSubmitting` | `boolean` | `false` | Disables the CTA during submission. |
|
|
45
|
+
| `errors` | `RegisterFormValidationError[]` | `[]` | Field-specific server errors (merged with internal validation). |
|
|
46
|
+
| `error` | `string` | - | General error rendered as a `DismissibleMessage` above the form. |
|
|
47
|
+
| `showPasswordConfirm` | `boolean` | `true` | Render the password-confirm field. |
|
|
48
|
+
| `passwordMinLength` | `number` | `8` | Minimum password length (fed into both the FieldInput attribute and the validator). |
|
|
49
|
+
| `extraFields` | `RegisterFieldConfig[]` | `[]` | Declarative extra fields. Rendered as `FieldInput`s positioned top or bottom. |
|
|
50
|
+
| `extraFieldsSlot` | `Snippet<[{ formData, fieldError }]>` | - | Escape hatch for non-FieldInput extras. Rendered after declarative bottom fields. |
|
|
51
|
+
| `submitLabel` | `string` | i18n | Override the CTA label. |
|
|
52
|
+
| `submittingLabel` | `string` | i18n | Override the CTA label while submitting. |
|
|
53
|
+
| `submitButton` | `Snippet<[{ isSubmitting, disabled }]>` | - | Override the entire CTA section. |
|
|
54
|
+
| `socialLogins` | `Snippet` | - | Social/OAuth buttons. A divider is shown above when set. |
|
|
55
|
+
| `socialDividerLabel` | `string \| false` | i18n | Override (or hide with `false`) the divider above social buttons. |
|
|
56
|
+
| `footer` | `Snippet` | - | Content below the form (e.g., "Already have an account? Log in"). |
|
|
57
|
+
| `notifications` | `NotificationsStack` | - | When set, general errors are also pushed via `notifications.error()`. |
|
|
58
|
+
| `t` | `TranslateFn` | English | i18n function. |
|
|
59
|
+
| `unstyled` / `class` | - | - | Standard styling escape hatches. |
|
|
60
|
+
| `el` | `HTMLFormElement` | - | Bindable form element. |
|
|
61
61
|
|
|
62
62
|
### Imperative methods (via `bind:this`)
|
|
63
63
|
|
|
64
|
-
| Method
|
|
65
|
-
|
|
|
66
|
-
| `validate()`
|
|
67
|
-
| `scrollToFirstError(opts?)`
|
|
64
|
+
| Method | Returns | Purpose |
|
|
65
|
+
| --------------------------- | --------- | ------------------------------------------------------------------- |
|
|
66
|
+
| `validate()` | `boolean` | Forces every inner field's validator to run. `true` if all valid. |
|
|
67
|
+
| `scrollToFirstError(opts?)` | `boolean` | Scrolls + focuses the first invalid field. Call after `validate()`. |
|
|
68
68
|
|
|
69
69
|
## RegisterFormModal — extra props
|
|
70
70
|
|
|
71
71
|
Inherits all `RegisterForm` props, plus:
|
|
72
72
|
|
|
73
|
-
| Prop
|
|
74
|
-
|
|
|
75
|
-
| `title`
|
|
76
|
-
| `visible`
|
|
77
|
-
| `trigger`
|
|
78
|
-
| `classModal`
|
|
79
|
-
| `classInner`
|
|
80
|
-
| `classForm`
|
|
81
|
-
| `noXClose`
|
|
82
|
-
| `onClose`
|
|
73
|
+
| Prop | Type | Default | Description |
|
|
74
|
+
| ------------ | --------------------- | ------------------------- | ---------------------------------------------------- |
|
|
75
|
+
| `title` | `string` | `"Create account"` (i18n) | Modal title. |
|
|
76
|
+
| `visible` | `boolean` | `false` | Bindable modal visibility. |
|
|
77
|
+
| `trigger` | `Snippet<[{ open }]>` | - | Optional trigger element rendered outside the modal. |
|
|
78
|
+
| `classModal` | `string` | - | Class for the Modal box. |
|
|
79
|
+
| `classInner` | `string` | - | Class for the Modal inner width container. |
|
|
80
|
+
| `classForm` | `string` | - | Class forwarded to the inner `RegisterForm`. |
|
|
81
|
+
| `noXClose` | `boolean` | `false` | Hide the close (X) button. |
|
|
82
|
+
| `onClose` | `() => false \| void` | - | Pre-close hook. Return `false` to prevent close. |
|
|
83
83
|
|
|
84
84
|
**Methods:** `open(openerOrEvent?)`, `close()` — exposed via `bind:this`.
|
|
85
85
|
|
|
@@ -161,16 +161,16 @@ Inherits all `RegisterForm` props, plus:
|
|
|
161
161
|
|
|
162
162
|
Prefix: `--stuic-register-form-*`
|
|
163
163
|
|
|
164
|
-
| Variable
|
|
165
|
-
|
|
|
166
|
-
| `--stuic-register-form-gap`
|
|
167
|
-
| `--stuic-register-form-gap-row`
|
|
168
|
-
| `--stuic-register-form-social-margin-top`
|
|
169
|
-
| `--stuic-register-form-social-gap`
|
|
170
|
-
| `--stuic-register-form-social-divider-color`
|
|
171
|
-
| `--stuic-register-form-social-divider-line-color`
|
|
172
|
-
| `--stuic-register-form-social-divider-font-size`
|
|
173
|
-
| `--stuic-register-form-social-divider-margin-bottom`
|
|
164
|
+
| Variable | Purpose |
|
|
165
|
+
| ---------------------------------------------------- | ----------------------------- |
|
|
166
|
+
| `--stuic-register-form-gap` | Vertical gap between sections |
|
|
167
|
+
| `--stuic-register-form-gap-row` | Gap inside multi-column rows |
|
|
168
|
+
| `--stuic-register-form-social-margin-top` | Margin above social block |
|
|
169
|
+
| `--stuic-register-form-social-gap` | Gap between social buttons |
|
|
170
|
+
| `--stuic-register-form-social-divider-color` | Divider text color |
|
|
171
|
+
| `--stuic-register-form-social-divider-line-color` | Divider line color |
|
|
172
|
+
| `--stuic-register-form-social-divider-font-size` | Divider text size |
|
|
173
|
+
| `--stuic-register-form-social-divider-margin-bottom` | Divider bottom margin |
|
|
174
174
|
|
|
175
175
|
## i18n keys
|
|
176
176
|
|
|
@@ -257,13 +257,7 @@
|
|
|
257
257
|
}
|
|
258
258
|
</script>
|
|
259
259
|
|
|
260
|
-
<form
|
|
261
|
-
bind:this={formEl}
|
|
262
|
-
class={_class}
|
|
263
|
-
use:onSubmitValidityCheck
|
|
264
|
-
novalidate
|
|
265
|
-
{...rest}
|
|
266
|
-
>
|
|
260
|
+
<form bind:this={formEl} class={_class} use:onSubmitValidityCheck novalidate {...rest}>
|
|
267
261
|
<!-- General error alert -->
|
|
268
262
|
<DismissibleMessage message={error} intent="destructive" />
|
|
269
263
|
|
|
@@ -4,14 +4,14 @@ A horizontal or vertical separator line. Renders an accessible `role="separator"
|
|
|
4
4
|
|
|
5
5
|
## Props
|
|
6
6
|
|
|
7
|
-
| Prop | Type | Default | Description
|
|
8
|
-
| ------------- | ---------------------------- | -------------- |
|
|
9
|
-
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Layout direction. Also published as `data-orientation`.
|
|
7
|
+
| Prop | Type | Default | Description |
|
|
8
|
+
| ------------- | ---------------------------- | -------------- | ----------------------------------------------------------- |
|
|
9
|
+
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Layout direction. Also published as `data-orientation`. |
|
|
10
10
|
| `decorative` | `boolean` | `false` | Drop `role` + `aria-orientation`, set `aria-hidden="true"`. |
|
|
11
|
-
| `unstyled` | `boolean` | `false` | Skip all default styling.
|
|
12
|
-
| `class` | `string` | - | Additional CSS classes.
|
|
13
|
-
| `style` | `string` | - | Inline style.
|
|
14
|
-
| `el` | `HTMLDivElement` | - | Bindable root element.
|
|
11
|
+
| `unstyled` | `boolean` | `false` | Skip all default styling. |
|
|
12
|
+
| `class` | `string` | - | Additional CSS classes. |
|
|
13
|
+
| `style` | `string` | - | Inline style. |
|
|
14
|
+
| `el` | `HTMLDivElement` | - | Bindable root element. |
|
|
15
15
|
|
|
16
16
|
## Usage
|
|
17
17
|
|
|
@@ -46,7 +46,8 @@
|
|
|
46
46
|
display: block;
|
|
47
47
|
width: 100%;
|
|
48
48
|
padding: var(--stuic-tabbed-menu-padding-y) var(--stuic-tabbed-menu-padding-x);
|
|
49
|
-
border-radius: var(--stuic-tabbed-menu-radius, var(--stuic-radius))
|
|
49
|
+
border-radius: var(--stuic-tabbed-menu-radius, var(--stuic-radius))
|
|
50
|
+
var(--stuic-tabbed-menu-radius, var(--stuic-radius)) 0 0;
|
|
50
51
|
border: 1px solid var(--stuic-tabbed-menu-border);
|
|
51
52
|
border-bottom: 0;
|
|
52
53
|
background: var(--stuic-tabbed-menu-tab-bg);
|
|
@@ -102,13 +103,15 @@
|
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
.stuic-tabbed-menu[data-orientation="vertical"] .stuic-tabbed-menu-tab {
|
|
105
|
-
border-radius: var(--stuic-tabbed-menu-radius, var(--stuic-radius)) 0 0
|
|
106
|
+
border-radius: var(--stuic-tabbed-menu-radius, var(--stuic-radius)) 0 0
|
|
107
|
+
var(--stuic-tabbed-menu-radius, var(--stuic-radius));
|
|
106
108
|
border: 1px solid var(--stuic-tabbed-menu-border);
|
|
107
109
|
border-right: 0;
|
|
108
110
|
text-align: left;
|
|
109
111
|
}
|
|
110
112
|
|
|
111
|
-
.stuic-tabbed-menu[data-orientation="vertical"]
|
|
113
|
+
.stuic-tabbed-menu[data-orientation="vertical"]
|
|
114
|
+
.stuic-tabbed-menu-tab[aria-selected="true"] {
|
|
112
115
|
border-color: var(--stuic-tabbed-menu-border-active);
|
|
113
116
|
}
|
|
114
117
|
}
|
|
@@ -84,49 +84,49 @@ Collapsing a branch also collapses all of its descendants.
|
|
|
84
84
|
|
|
85
85
|
## Props
|
|
86
86
|
|
|
87
|
-
| Prop
|
|
88
|
-
|
|
|
89
|
-
| `items`
|
|
90
|
-
| `activeId`
|
|
91
|
-
| `isActive`
|
|
92
|
-
| `onSelect`
|
|
93
|
-
| `onToggle`
|
|
94
|
-
| `sort`
|
|
95
|
-
| `defaultExpanded`
|
|
96
|
-
| `expandedIds`
|
|
97
|
-
| `persistState`
|
|
98
|
-
| `storageKeyPrefix`
|
|
99
|
-
| `draggable`
|
|
100
|
-
| `isDraggable`
|
|
101
|
-
| `isDropTarget`
|
|
102
|
-
| `onMove`
|
|
103
|
-
| `onError`
|
|
104
|
-
| `dragExpandDelay`
|
|
105
|
-
| `t`
|
|
106
|
-
| `getNodeLabel`
|
|
107
|
-
| `renderItem`
|
|
108
|
-
| `renderIcon`
|
|
109
|
-
| `unstyled`
|
|
110
|
-
| `class`
|
|
111
|
-
| `classItem`
|
|
112
|
-
| `classItemActive`
|
|
113
|
-
| `classIcon`
|
|
114
|
-
| `classLabel`
|
|
115
|
-
| `classChevron`
|
|
116
|
-
| `classChildren`
|
|
117
|
-
| `el`
|
|
87
|
+
| Prop | Type | Default | Description |
|
|
88
|
+
| ------------------ | ---------------------------------------------------- | ----------------- | --------------------------------------------------------------------- |
|
|
89
|
+
| `items` | `TreeNodeDTO<T>[]` | required | Tree data (e.g. from `tree.toJSON().children`) |
|
|
90
|
+
| `activeId` | `string` | - | ID of the currently active/selected node |
|
|
91
|
+
| `isActive` | `(item) => boolean` | - | Alternative to `activeId` for custom active detection |
|
|
92
|
+
| `onSelect` | `(item) => void` | - | Called when a node is selected (click or Enter/Space) |
|
|
93
|
+
| `onToggle` | `(item, expanded) => void` | - | Called when a branch is expanded/collapsed |
|
|
94
|
+
| `sort` | `(a, b) => number` | - | Per-level sort comparator |
|
|
95
|
+
| `defaultExpanded` | `boolean` | `false` | Default expansion state for branches |
|
|
96
|
+
| `expandedIds` | `Set<string>` | - | Initially-expanded branch IDs |
|
|
97
|
+
| `persistState` | `boolean` | `false` | Persist expand/collapse to `localStorage` |
|
|
98
|
+
| `storageKeyPrefix` | `string` | `"stuic-tree"` | Prefix used for `localStorage` keys |
|
|
99
|
+
| `draggable` | `boolean` | `false` | Enable drag-and-drop reordering |
|
|
100
|
+
| `isDraggable` | `(item) => boolean` | - | Return `false` to block dragging a specific node |
|
|
101
|
+
| `isDropTarget` | `(item) => boolean` | - | Return `false` to block dropping onto a specific node |
|
|
102
|
+
| `onMove` | `(event) => void \| false \| Promise<void \| false>` | - | Called when a valid drop happens; return `false` to reject |
|
|
103
|
+
| `onError` | `(err) => void` | - | Called when `onMove` throws |
|
|
104
|
+
| `dragExpandDelay` | `number` | `800` | ms before auto-expanding a collapsed branch hovered during drag |
|
|
105
|
+
| `t` | `TranslateFn` | built-in | Optional translation function (used for drag-drop a11y announcements) |
|
|
106
|
+
| `getNodeLabel` | `(item) => string` | `String(v.value)` | String used in a11y announcements when a node is moved |
|
|
107
|
+
| `renderItem` | `Snippet<[item, depth, isExpanded]>` | - | Custom node label renderer |
|
|
108
|
+
| `renderIcon` | `Snippet<[item, depth, isExpanded]>` | - | Custom node icon renderer |
|
|
109
|
+
| `unstyled` | `boolean` | `false` | Skip default styling |
|
|
110
|
+
| `class` | `string` | - | Classes for wrapper |
|
|
111
|
+
| `classItem` | `string` | - | Classes for each item button |
|
|
112
|
+
| `classItemActive` | `string` | - | Extra classes when an item is active |
|
|
113
|
+
| `classIcon` | `string` | - | Classes for the icon wrapper |
|
|
114
|
+
| `classLabel` | `string` | - | Classes for the label wrapper |
|
|
115
|
+
| `classChevron` | `string` | - | Classes for the expand/collapse chevron |
|
|
116
|
+
| `classChildren` | `string` | - | Classes for the children container |
|
|
117
|
+
| `el` | `HTMLElement` | - | Bindable wrapper reference |
|
|
118
118
|
|
|
119
119
|
## Keyboard Navigation
|
|
120
120
|
|
|
121
|
-
| Key
|
|
122
|
-
|
|
|
123
|
-
| `ArrowDown`
|
|
124
|
-
| `ArrowUp`
|
|
125
|
-
| `ArrowRight`
|
|
126
|
-
| `ArrowLeft`
|
|
127
|
-
| `Home`
|
|
128
|
-
| `End`
|
|
129
|
-
| `Enter` / `Space`
|
|
121
|
+
| Key | Action |
|
|
122
|
+
| ----------------- | -------------------------------------------------------------------- |
|
|
123
|
+
| `ArrowDown` | Focus next visible node |
|
|
124
|
+
| `ArrowUp` | Focus previous visible node |
|
|
125
|
+
| `ArrowRight` | Expand a collapsed branch, or move to first child of an expanded one |
|
|
126
|
+
| `ArrowLeft` | Collapse an expanded branch, or move to parent |
|
|
127
|
+
| `Home` | Focus first visible node |
|
|
128
|
+
| `End` | Focus last visible node |
|
|
129
|
+
| `Enter` / `Space` | Toggle expansion (if branch) and fire `onSelect` |
|
|
130
130
|
|
|
131
131
|
Focus follows the **roving tabindex** pattern: only the currently-focused node has `tabindex=0`, all others are `-1`.
|
|
132
132
|
|
|
@@ -147,39 +147,39 @@ Override globally in `:root` or locally via `style=""`. Radius / border-width /
|
|
|
147
147
|
|
|
148
148
|
### Structure
|
|
149
149
|
|
|
150
|
-
| Variable
|
|
151
|
-
|
|
|
152
|
-
| `--stuic-tree-indent`
|
|
153
|
-
| `--stuic-tree-item-padding-x`
|
|
154
|
-
| `--stuic-tree-item-padding-y`
|
|
155
|
-
| `--stuic-tree-item-height`
|
|
156
|
-
| `--stuic-tree-item-font-size`
|
|
157
|
-
| `--stuic-tree-item-gap`
|
|
158
|
-
| `--stuic-tree-chevron-size`
|
|
159
|
-
| `--stuic-tree-chevron-opacity`
|
|
160
|
-
| `--stuic-tree-icon-opacity`
|
|
161
|
-
| `--stuic-tree-item-radius`
|
|
162
|
-
| `--stuic-tree-transition`
|
|
150
|
+
| Variable | Default | Description |
|
|
151
|
+
| ------------------------------ | ------------------------- | -------------------------------- |
|
|
152
|
+
| `--stuic-tree-indent` | `1.25rem` | Indentation per depth level |
|
|
153
|
+
| `--stuic-tree-item-padding-x` | `0.375rem` | Item horizontal padding |
|
|
154
|
+
| `--stuic-tree-item-padding-y` | `0.125rem` | Item vertical padding |
|
|
155
|
+
| `--stuic-tree-item-height` | `1.75rem` | Item row height |
|
|
156
|
+
| `--stuic-tree-item-font-size` | `var(--text-sm)` | Item font size |
|
|
157
|
+
| `--stuic-tree-item-gap` | `0.25rem` | Gap between chevron, icon, label |
|
|
158
|
+
| `--stuic-tree-chevron-size` | `14px` | Chevron icon size |
|
|
159
|
+
| `--stuic-tree-chevron-opacity` | `0.5` | Chevron opacity |
|
|
160
|
+
| `--stuic-tree-icon-opacity` | `0.7` | Icon opacity |
|
|
161
|
+
| `--stuic-tree-item-radius` | `var(--stuic-radius)` | Item border radius |
|
|
162
|
+
| `--stuic-tree-transition` | `var(--stuic-transition)` | Transition duration |
|
|
163
163
|
|
|
164
164
|
### Colors
|
|
165
165
|
|
|
166
|
-
| Variable
|
|
167
|
-
|
|
|
168
|
-
| `--stuic-tree-item-bg`
|
|
169
|
-
| `--stuic-tree-item-text`
|
|
170
|
-
| `--stuic-tree-item-bg-hover`
|
|
171
|
-
| `--stuic-tree-item-bg-focus`
|
|
172
|
-
| `--stuic-tree-item-bg-active`
|
|
173
|
-
| `--stuic-tree-item-text-active`
|
|
166
|
+
| Variable | Default | Description |
|
|
167
|
+
| ------------------------------- | --------------------------------------- | ------------------------- |
|
|
168
|
+
| `--stuic-tree-item-bg` | `transparent` | Item background |
|
|
169
|
+
| `--stuic-tree-item-text` | `inherit` | Item text color |
|
|
170
|
+
| `--stuic-tree-item-bg-hover` | `rgb(0 0 0 / 0.06)` | Hover background |
|
|
171
|
+
| `--stuic-tree-item-bg-focus` | `rgb(0 0 0 / 0.06)` | Keyboard-focus background |
|
|
172
|
+
| `--stuic-tree-item-bg-active` | `var(--stuic-color-primary)` | Active/selected bg |
|
|
173
|
+
| `--stuic-tree-item-text-active` | `var(--stuic-color-primary-foreground)` | Active/selected text |
|
|
174
174
|
|
|
175
175
|
### Drag & drop
|
|
176
176
|
|
|
177
|
-
| Variable
|
|
178
|
-
|
|
|
179
|
-
| `--stuic-tree-item-opacity-dragging`
|
|
180
|
-
| `--stuic-tree-drop-indicator-color`
|
|
181
|
-
| `--stuic-tree-drop-indicator-height`
|
|
182
|
-
| `--stuic-tree-item-bg-dragover`
|
|
177
|
+
| Variable | Default | Description |
|
|
178
|
+
| ------------------------------------ | ---------------------------- | ----------------------------- |
|
|
179
|
+
| `--stuic-tree-item-opacity-dragging` | `0.4` | Opacity of the dragged item |
|
|
180
|
+
| `--stuic-tree-drop-indicator-color` | `var(--stuic-color-primary)` | Before/after drop line color |
|
|
181
|
+
| `--stuic-tree-drop-indicator-height` | `2px` | Before/after drop line height |
|
|
182
|
+
| `--stuic-tree-item-bg-dragover` | `rgb(0 0 0 / 0.04)` | "Inside"-drop highlight |
|
|
183
183
|
|
|
184
184
|
## Limitations
|
|
185
185
|
|
|
@@ -219,11 +219,7 @@
|
|
|
219
219
|
);
|
|
220
220
|
|
|
221
221
|
const isDark = $derived(
|
|
222
|
-
csEnabled
|
|
223
|
-
? cs.isDark
|
|
224
|
-
? cs.isDark()
|
|
225
|
-
: ColorScheme.current === "dark"
|
|
226
|
-
: false
|
|
222
|
+
csEnabled ? (cs.isDark ? cs.isDark() : ColorScheme.current === "dark") : false
|
|
227
223
|
);
|
|
228
224
|
|
|
229
225
|
function toggleColorScheme() {
|
|
@@ -34,14 +34,14 @@
|
|
|
34
34
|
|
|
35
35
|
/* border between panels: left position */
|
|
36
36
|
.stuic-with-side-panel[data-position="left"] .stuic-with-side-panel-side {
|
|
37
|
-
border-right: var(--stuic-with-side-panel-border-width, var(--stuic-border-width))
|
|
38
|
-
var(--stuic-with-side-panel-border-color);
|
|
37
|
+
border-right: var(--stuic-with-side-panel-border-width, var(--stuic-border-width))
|
|
38
|
+
solid var(--stuic-with-side-panel-border-color);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/* border between panels: right position */
|
|
42
42
|
.stuic-with-side-panel[data-position="right"] .stuic-with-side-panel-side {
|
|
43
|
-
border-left: var(--stuic-with-side-panel-border-width, var(--stuic-border-width))
|
|
44
|
-
var(--stuic-with-side-panel-border-color);
|
|
43
|
+
border-left: var(--stuic-with-side-panel-border-width, var(--stuic-border-width))
|
|
44
|
+
solid var(--stuic-with-side-panel-border-color);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
.stuic-with-side-panel-main {
|
package/dist/index.css
CHANGED
|
@@ -32,19 +32,23 @@ In practice:
|
|
|
32
32
|
============================================================================ */
|
|
33
33
|
:root {
|
|
34
34
|
/* Radius: three tiers */
|
|
35
|
-
--stuic-radius: var(--radius-md);
|
|
36
|
-
--stuic-radius-button: var(
|
|
37
|
-
|
|
35
|
+
--stuic-radius: var(--radius-md); /* elements: inputs, badges, list items */
|
|
36
|
+
--stuic-radius-button: var(
|
|
37
|
+
--radius-md
|
|
38
|
+
); /* buttons: independent from general elements */
|
|
39
|
+
--stuic-radius-container: var(
|
|
40
|
+
--radius-lg
|
|
41
|
+
); /* containers: cards, modals, dropdowns, notifications */
|
|
38
42
|
|
|
39
43
|
/* Shadows */
|
|
40
|
-
--stuic-shadow: var(--shadow-sm);
|
|
41
|
-
--stuic-shadow-hover: var(--shadow-md);
|
|
42
|
-
--stuic-shadow-overlay: var(--shadow-lg);
|
|
43
|
-
--stuic-shadow-dialog: var(--shadow-xl);
|
|
44
|
+
--stuic-shadow: var(--shadow-sm); /* default resting shadow */
|
|
45
|
+
--stuic-shadow-hover: var(--shadow-md); /* hover/elevated shadow */
|
|
46
|
+
--stuic-shadow-overlay: var(--shadow-lg); /* overlays: dropdowns, notifications */
|
|
47
|
+
--stuic-shadow-dialog: var(--shadow-xl); /* dialogs/modals */
|
|
44
48
|
|
|
45
49
|
/* Border */
|
|
46
50
|
--stuic-border-width: 1px;
|
|
47
|
-
--stuic-border-width-button: 1px;
|
|
51
|
+
--stuic-border-width-button: 1px; /* buttons: independent from general elements */
|
|
48
52
|
|
|
49
53
|
/* Transition */
|
|
50
54
|
--stuic-transition: 150ms;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { generateCssTokens, generateThemeCss, toCssString } from "@marianmeres/design-tokens";
|
|
1
|
+
export { generateCssTokens, generateThemeCss, toCssString, } from "@marianmeres/design-tokens";
|
|
2
2
|
export type { ColorPair, ColorValue, GeneratedTokens, IntentColorKey, RolePairedKey, RoleSingleKey, SingleColor, ThemeSchema, TokenSchema, WithOptional, WithRequired, } from "@marianmeres/design-tokens";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Re-export core API from @marianmeres/design-tokens
|
|
2
|
-
export { generateCssTokens, generateThemeCss, toCssString } from "@marianmeres/design-tokens";
|
|
2
|
+
export { generateCssTokens, generateThemeCss, toCssString, } from "@marianmeres/design-tokens";
|
package/docs/architecture.md
CHANGED
|
@@ -124,13 +124,13 @@ Props → Component → Data Attributes → CSS Selectors
|
|
|
124
124
|
|
|
125
125
|
## Key Files
|
|
126
126
|
|
|
127
|
-
| File
|
|
128
|
-
|
|
|
129
|
-
| `src/lib/index.css`
|
|
130
|
-
| `src/lib/index.ts`
|
|
131
|
-
| `@marianmeres/design-tokens/css/stone.css` | Default theme (42 themes available)
|
|
132
|
-
| `src/lib/utils/tw-merge.ts`
|
|
133
|
-
| `src/lib/utils/design-tokens.ts`
|
|
127
|
+
| File | Purpose |
|
|
128
|
+
| ------------------------------------------ | --------------------------------------------------------------------------- |
|
|
129
|
+
| `src/lib/index.css` | CSS entry point (import this) |
|
|
130
|
+
| `src/lib/index.ts` | JS entry point (barrel export) |
|
|
131
|
+
| `@marianmeres/design-tokens/css/stone.css` | Default theme (42 themes available) |
|
|
132
|
+
| `src/lib/utils/tw-merge.ts` | Tailwind class merging |
|
|
133
|
+
| `src/lib/utils/design-tokens.ts` | Theme types (`ThemeSchema`, `ColorPair`, etc.) and CSS generation functions |
|
|
134
134
|
|
|
135
135
|
---
|
|
136
136
|
|
|
@@ -8,9 +8,9 @@ vitest-browser-svelte docs. Planning artifact; no code was changed.
|
|
|
8
8
|
# @marianmeres/stuic — Component Testing: Overview & Roadmap
|
|
9
9
|
|
|
10
10
|
> **Verdict:** the proposed stack — Vitest Browser Mode + `vitest-browser-svelte` + Playwright/Chromium
|
|
11
|
-
> — is the right default for
|
|
11
|
+
> — is the right default for _this_ library, where the value being shipped is precisely the DOM/layout/
|
|
12
12
|
> focus/positioning behavior that the current node/server-build test setup **cannot exercise at all**.
|
|
13
|
-
> The claim is
|
|
13
|
+
> The claim is _mostly_ correct rather than gospel: it's not an officially-mandated singular standard
|
|
14
14
|
> (svelte.dev still nominally leads with jsdom + @testing-library), and one term was dated — modern
|
|
15
15
|
> spelling is `vitest-browser-svelte` + **`@vitest/browser-playwright`** + `playwright` on **Vitest 4**.
|
|
16
16
|
>
|
|
@@ -32,28 +32,28 @@ vitest-browser-svelte docs. Planning artifact; no code was changed.
|
|
|
32
32
|
|
|
33
33
|
[`docs/testing.md`](../testing.md) records a deliberate decision **not** to test component rendering
|
|
34
34
|
("50+ components × prop combos = slow suite, tiny yield; rendering is gated by svelte-check + publint +
|
|
35
|
-
build"). That reasoning holds for
|
|
36
|
-
us test
|
|
35
|
+
build"). That reasoning holds for _"does it render"_ and we keep it. What changes: browser mode lets
|
|
36
|
+
us test _"does it **behave**"_ — events, two-way binding, aria/disabled/active state, focus traps,
|
|
37
37
|
viewport-clamped positioning (cf. the `9d8c974` annotation regression) — which the build does **not**
|
|
38
38
|
cover and which was **previously impossible**. Updating `docs/testing.md` to add this layer is an
|
|
39
39
|
explicit sprint task so the docs don't contradict each other.
|
|
40
40
|
|
|
41
41
|
## Top recommendations across all dimensions (ranked)
|
|
42
42
|
|
|
43
|
-
| Rank | Recommendation
|
|
44
|
-
|
|
45
|
-
| 1
|
|
46
|
-
| 2
|
|
47
|
-
| 3
|
|
48
|
-
| 4
|
|
49
|
-
| 5
|
|
50
|
-
| 6
|
|
51
|
-
| 7
|
|
52
|
-
| 8
|
|
53
|
-
| 9
|
|
54
|
-
| 10
|
|
55
|
-
| 11
|
|
56
|
-
| 12
|
|
43
|
+
| Rank | Recommendation | Dimension | Value | Effort | Risk | Why now |
|
|
44
|
+
| ---- | ------------------------------------------------------------------- | ---------------------------------------- | ----- | ------ | ---- | ------------------------------------------------------------ |
|
|
45
|
+
| 1 | Upgrade vitest 3→4, verify 9 suites green | [01](./01-framework-setup.md) | high | S | med | Gating prerequisite; nothing installs without it |
|
|
46
|
+
| 2 | Add `projects` split (node `server` + browser `client`) + Chromium | [01](./01-framework-setup.md) | high | S | med | The harness; routes by `*.svelte.test.ts` filename |
|
|
47
|
+
| 3 | Separator smoke test — prove client build + `$effect` actually run | [01](./01-framework-setup.md) | high | S | med | Disproves/confirms the documented server-build blocker |
|
|
48
|
+
| 4 | Reconcile `docs/testing.md` (behavior ✅, rendering still ❌) | [02](./02-test-conventions.md) | med | S | low | Keep docs internally consistent before scaling |
|
|
49
|
+
| 5 | Button — flagship; sets every assertion pattern | [03](./03-component-coverage-roadmap.md) | high | S | low | Most-used primitive; template for the rest |
|
|
50
|
+
| 6 | Pill, Switch — events + binding patterns | [03](./03-component-coverage-roadmap.md) | high | S | low | Cover dismiss/toggle/bind once, reuse everywhere |
|
|
51
|
+
| 7 | Spinner, Skeleton, DismissibleMessage, Avatar, Progress | [03](./03-component-coverage-roadmap.md) | high | S | low | Deterministic, high-traffic; quick wins |
|
|
52
|
+
| 8 | **One hard proof** — anchor-position viewport clamp (or focus trap) | [04](./04-hard-cases-and-e2e.md) | high | M | med | Guards a real recent regression; proves browser mode's worth |
|
|
53
|
+
| 9 | Minimal GitHub Actions workflow | [05](./05-ci.md) | high | S | low | Stops broken tests reaching npm; once a few tests pass |
|
|
54
|
+
| 10 | Tier-2 form fields (`FieldInput` first, then the family) | [03](./03-component-coverage-roadmap.md) | med | M | low | Largest component group; one pattern unlocks many |
|
|
55
|
+
| 11 | Portals/focus-traps in browser mode (Modal/Drawer/Backdrop) | [04](./04-hard-cases-and-e2e.md) | med | M | med | High-value a11y contracts; after patterns settle |
|
|
56
|
+
| 12 | Standalone Playwright E2E layer (drag, Milkdown, checkout flows) | [04](./04-hard-cases-and-e2e.md) | med | L | med | Separate later initiative; explicitly out of sprint 1 |
|
|
57
57
|
|
|
58
58
|
> **Deliberately deferred as low-yield:** visual-regression / `toMatchScreenshot`, multi-browser
|
|
59
59
|
> (Firefox/WebKit) matrix, and exhaustive prop-matrix coverage. Revisit only if motivated by a real bug.
|
|
@@ -65,7 +65,7 @@ Branch: `feat/component-testing`. One commit per task.
|
|
|
65
65
|
1. **Vitest 4 upgrade (#1)** — `pnpm add -D vitest@^4`, run `pnpm test`, confirm 9 suites green. Why
|
|
66
66
|
first: everything else peer-depends on it; isolating it makes the one risky bump reversible.
|
|
67
67
|
2. **Browser harness (#2, #3)** — add browser deps, the `projects` config, `playwright install
|
|
68
|
-
|
|
68
|
+
chromium`, fix the test scripts, and land the Separator smoke test. Unblocks all component tests
|
|
69
69
|
and proves the server-build blocker is gone. Detail in [01](./01-framework-setup.md).
|
|
70
70
|
3. **Reconcile `docs/testing.md` (#4)** — small doc edit so the philosophy matches reality.
|
|
71
71
|
4. **Button (#5)** — establishes the assertion vocabulary ([02](./02-test-conventions.md)) every later
|