@marianmeres/stuic 3.18.0 → 3.19.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 CHANGED
@@ -25,7 +25,7 @@
25
25
  src/lib/
26
26
  ├── components/ # 44 UI components
27
27
  ├── actions/ # 14 Svelte actions
28
- ├── utils/ # 42 utility functions
28
+ ├── utils/ # 42 utility modules
29
29
  ├── themes/ # 29 theme definitions (.ts) + generated CSS (css/)
30
30
  ├── icons/ # Icon re-exports
31
31
  ├── index.css # Centralized CSS imports
@@ -74,10 +74,10 @@ src/lib/
74
74
 
75
75
  ### Domain Docs
76
76
 
77
- - [Components](./docs/domains/components.md) — 44 components, Props pattern, snippets
77
+ - [Components](./docs/domains/components.md) — 44 component directories, Props pattern, snippets
78
78
  - [Theming](./docs/domains/theming.md) — CSS tokens, dark mode, themes
79
79
  - [Actions](./docs/domains/actions.md) — 14 Svelte directives
80
- - [Utils](./docs/domains/utils.md) — 42 utility functions
80
+ - [Utils](./docs/domains/utils.md) — 42 utility modules
81
81
 
82
82
  ### Reference
83
83
 
@@ -24,6 +24,9 @@
24
24
 
25
25
  // -- Progress --
26
26
 
27
+ /** Hide the built-in progress indicator (e.g. when rendered externally) */
28
+ hideProgress?: boolean;
29
+
27
30
  currentStep?: string;
28
31
  steps?: CheckoutStep[];
29
32
  onStepNavigate?: (step: CheckoutStep) => void;
@@ -76,6 +79,7 @@
76
79
  emailSent,
77
80
  isLoading = false,
78
81
  error,
82
+ hideProgress = false,
79
83
  currentStep = "complete",
80
84
  steps,
81
85
  onStepNavigate,
@@ -102,13 +106,15 @@
102
106
 
103
107
  <div bind:this={el} class={_class} {...rest}>
104
108
  <!-- Progress -->
105
- <CheckoutProgress
106
- {steps}
107
- {currentStep}
108
- onNavigate={onStepNavigate}
109
- t={tProp}
110
- {unstyled}
111
- />
109
+ {#if !hideProgress}
110
+ <CheckoutProgress
111
+ {steps}
112
+ {currentStep}
113
+ onNavigate={onStepNavigate}
114
+ t={tProp}
115
+ {unstyled}
116
+ />
117
+ {/if}
112
118
 
113
119
  {#if isLoading}
114
120
  <div class={unstyled ? undefined : "stuic-checkout-complete-step-loading"}>
@@ -13,6 +13,8 @@ export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children">
13
13
  isLoading?: boolean;
14
14
  /** Error message */
15
15
  error?: string | null;
16
+ /** Hide the built-in progress indicator (e.g. when rendered externally) */
17
+ hideProgress?: boolean;
16
18
  currentStep?: string;
17
19
  steps?: CheckoutStep[];
18
20
  onStepNavigate?: (step: CheckoutStep) => void;
@@ -34,6 +34,9 @@
34
34
 
35
35
  // -- Progress --
36
36
 
37
+ /** Hide the built-in progress indicator (e.g. when rendered externally) */
38
+ hideProgress?: boolean;
39
+
37
40
  currentStep?: string;
38
41
  steps?: CheckoutStep[];
39
42
  onStepNavigate?: (step: CheckoutStep) => void;
@@ -99,6 +102,7 @@
99
102
  validationErrors,
100
103
  isValid = true,
101
104
  isSubmitting = false,
105
+ hideProgress = false,
102
106
  currentStep = "confirm",
103
107
  steps,
104
108
  onStepNavigate,
@@ -132,13 +136,15 @@
132
136
 
133
137
  <div bind:this={el} class={_class} {...rest}>
134
138
  <!-- Progress -->
135
- <CheckoutProgress
136
- {steps}
137
- {currentStep}
138
- onNavigate={onStepNavigate}
139
- t={tProp}
140
- {unstyled}
141
- />
139
+ {#if !hideProgress}
140
+ <CheckoutProgress
141
+ {steps}
142
+ {currentStep}
143
+ onNavigate={onStepNavigate}
144
+ t={tProp}
145
+ {unstyled}
146
+ />
147
+ {/if}
142
148
 
143
149
  {#if isLoading}
144
150
  <!-- Skeleton grid -->
@@ -18,6 +18,8 @@ export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children">
18
18
  isValid?: boolean;
19
19
  /** Whether payment is being initiated */
20
20
  isSubmitting?: boolean;
21
+ /** Hide the built-in progress indicator (e.g. when rendered externally) */
22
+ hideProgress?: boolean;
21
23
  currentStep?: string;
22
24
  steps?: CheckoutStep[];
23
25
  onStepNavigate?: (step: CheckoutStep) => void;
@@ -22,6 +22,9 @@
22
22
 
23
23
  // -- Progress --
24
24
 
25
+ /** Hide the built-in progress indicator (e.g. when rendered externally) */
26
+ hideProgress?: boolean;
27
+
25
28
  /** Current step ID for the progress indicator. Default: "review" */
26
29
  currentStep?: string;
27
30
 
@@ -111,6 +114,7 @@
111
114
  items,
112
115
  isLoading = false,
113
116
  error,
117
+ hideProgress = false,
114
118
  currentStep = "review",
115
119
  steps,
116
120
  onStepNavigate,
@@ -140,13 +144,15 @@
140
144
 
141
145
  <div bind:this={el} class={_class} {...rest}>
142
146
  <!-- Progress -->
143
- <CheckoutProgress
144
- {steps}
145
- {currentStep}
146
- onNavigate={onStepNavigate}
147
- t={tProp}
148
- {unstyled}
149
- />
147
+ {#if !hideProgress}
148
+ <CheckoutProgress
149
+ {steps}
150
+ {currentStep}
151
+ onNavigate={onStepNavigate}
152
+ t={tProp}
153
+ {unstyled}
154
+ />
155
+ {/if}
150
156
 
151
157
  {#if isLoading}
152
158
  <!-- Skeleton grid -->
@@ -10,6 +10,8 @@ export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children">
10
10
  isLoading?: boolean;
11
11
  /** Error message to display */
12
12
  error?: string | null;
13
+ /** Hide the built-in progress indicator (e.g. when rendered externally) */
14
+ hideProgress?: boolean;
13
15
  /** Current step ID for the progress indicator. Default: "review" */
14
16
  currentStep?: string;
15
17
  /** Step definitions for progress. Default: standard 4-step flow */
@@ -30,6 +30,9 @@
30
30
 
31
31
  // -- Progress --
32
32
 
33
+ /** Hide the built-in progress indicator (e.g. when rendered externally) */
34
+ hideProgress?: boolean;
35
+
33
36
  currentStep?: string;
34
37
  steps?: CheckoutStep[];
35
38
  onStepNavigate?: (step: CheckoutStep) => void;
@@ -127,6 +130,7 @@
127
130
  isLoading = false,
128
131
  error,
129
132
  isSubmitting = false,
133
+ hideProgress = false,
130
134
  currentStep = "shipping",
131
135
  steps,
132
136
  onStepNavigate,
@@ -164,13 +168,15 @@
164
168
 
165
169
  <div bind:this={el} class={_class} {...rest}>
166
170
  <!-- Progress -->
167
- <CheckoutProgress
168
- {steps}
169
- {currentStep}
170
- onNavigate={onStepNavigate}
171
- t={tProp}
172
- {unstyled}
173
- />
171
+ {#if !hideProgress}
172
+ <CheckoutProgress
173
+ {steps}
174
+ {currentStep}
175
+ onNavigate={onStepNavigate}
176
+ t={tProp}
177
+ {unstyled}
178
+ />
179
+ {/if}
174
180
 
175
181
  {#if isLoading}
176
182
  <!-- Skeleton grid -->
@@ -13,6 +13,8 @@ export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children">
13
13
  error?: string | null;
14
14
  /** Whether the continue action is in progress */
15
15
  isSubmitting?: boolean;
16
+ /** Hide the built-in progress indicator (e.g. when rendered externally) */
17
+ hideProgress?: boolean;
16
18
  currentStep?: string;
17
19
  steps?: CheckoutStep[];
18
20
  onStepNavigate?: (step: CheckoutStep) => void;
@@ -128,6 +128,26 @@
128
128
  // Note, that this will also reset if nested... (which is not desired, but ignoring)
129
129
  onDestroy(BodyScroll.unlock);
130
130
 
131
+ async function _onEscapeKeydown(e: KeyboardEvent) {
132
+ if (e.key === "Escape" && visible) {
133
+ // do not allow built-in close on escape
134
+ e.preventDefault();
135
+ // do not bubble
136
+ e.stopPropagation();
137
+ // do not allow additional onkeydown listeners on this dialog
138
+ e.stopImmediatePropagation();
139
+
140
+ if (!noEscapeClose) {
141
+ // explicit false prevents close
142
+ let allowed = await preEscapeClose?.();
143
+ if (allowed !== false) {
144
+ // `preClose` will be handled next
145
+ close();
146
+ }
147
+ }
148
+ }
149
+ }
150
+
131
151
  // $inspect("Modal dialog mounted, is visible:", visible).with(clog);
132
152
  </script>
133
153
 
@@ -156,27 +176,7 @@
156
176
  close();
157
177
  }
158
178
  }}
159
- onkeydown={async (e) => {
160
- if (e.key === "Escape" && visible) {
161
- // clog("on Escape keydown, preventing default and stopping propagation");
162
-
163
- // do not allow built-in close on escape
164
- e.preventDefault();
165
- // do not bubble
166
- e.stopPropagation();
167
- // ???: do not allow additional onkeydown listeners on this dialog (should there be any...)
168
- e.stopImmediatePropagation();
169
-
170
- if (!noEscapeClose) {
171
- // explicit false prevents close
172
- let allowed = await preEscapeClose?.();
173
- if (allowed !== false) {
174
- // `preClose` will be handled next
175
- close();
176
- }
177
- }
178
- }
179
- }}
179
+ onkeydown={_onEscapeKeydown}
180
180
  >
181
181
  <!-- svelte-ignore a11y_click_events_have_key_events -->
182
182
  <!-- svelte-ignore a11y_no_static_element_interactions -->
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- 44 Svelte 5 components with consistent API patterns. All use runes-based reactivity.
5
+ 44 Svelte 5 component directories with consistent API patterns. All use runes-based reactivity.
6
6
 
7
7
  ## Component Categories
8
8
 
@@ -79,45 +79,66 @@
79
79
 
80
80
  | Component | Purpose |
81
81
  | --------- | ----------------------------------------------------------------------------------------- |
82
- | Cart | Shopping cart with quantity controls, pricing, summary; default/compact/readonly variants |
83
- | Checkout | Multi-step checkout flow (13 sub-components: atomic + composite steps) |
82
+ | Cart | Shopping cart with quantity controls, pricing, summary; default/compact/summary variants |
83
+ | Checkout | Multi-step checkout flow (14 exported sub-components: atomic + composite steps) |
84
84
 
85
85
  ---
86
86
 
87
87
  ## Checkout Components
88
88
 
89
- 13 sub-components organized as atomic building blocks + composite step pages.
89
+ 14 exported sub-components organized as atomic building blocks + composite step pages.
90
+ CSS is split into modular partials (`_*.css`) imported by `index.css`.
90
91
 
91
92
  ### Atomic Components
92
93
 
93
- | Component | Purpose | Key Props |
94
- | ------------------------- | --------------------------------------------------------------- | ---------------------------------------------------- |
95
- | CheckoutProgress | Step indicator with navigation | `currentStep`, `steps`, `onNavigate`, `separator` |
96
- | CheckoutOrderSummary | Price totals display (subtotal, shipping, tax, discount, total) | `totals`, `formatPrice`, `row`, `extraRows` |
97
- | CheckoutCartReview | Readonly cart display with summary | `items`, `onEditCart`, `thumbnail`, `title` |
98
- | CheckoutGuestForm | Guest checkout form (email, name, phone, B2B fields) | `formData`, `onSubmit`, `showB2bFields`, `fields` |
99
- | CheckoutLoginForm | Login form (email + password) | `formData`, `onSubmit`, `onForgotPassword`, `footer` |
100
- | CheckoutAddressForm | Address input fieldset | `address`, `label`, `requiredFields`, `countryField` |
101
- | CheckoutDeliveryOptions | Delivery method radio selection with free shipping logic | `options`, `selectedId`, `onSelect`, `subtotal` |
102
- | CheckoutOrderReview | Full order review with edit callbacks per section | `order`, `onEditItems`, `onEditShippingAddress` |
103
- | CheckoutOrderConfirmation | Order success screen with details | `order`, `orderId`, `onContinueShopping` |
94
+ | Component | Purpose | Key Props |
95
+ | -------------------------- | --------------------------------------------------------------- | ---------------------------------------------------------- |
96
+ | CheckoutProgress | Step indicator with navigation | `currentStep`, `steps`, `onNavigate`, `separator` |
97
+ | CheckoutOrderSummary | Price totals display (subtotal, shipping, tax, discount, total) | `totals`, `formatPrice`, `row`, `extraRows` |
98
+ | CheckoutCartReview | Readonly cart display with summary | `items`, `onEditCart`, `thumbnail`, `title` |
99
+ | CheckoutGuestForm | Guest checkout form (email, name, phone, B2B fields) | `formData`, `onSubmit`, `showB2bFields`, `fields` |
100
+ | CheckoutLoginForm | Login form (email + password) | `formData`, `onSubmit`, `onForgotPassword`, `footer` |
101
+ | CheckoutGuestOrLoginForm | Composite guest/login with tabbed/stacked/single modes | `guestForm`, `loginForm`, `formMode`, `activeTab` |
102
+ | CheckoutAddressForm | Address input fieldset | `address`, `label`, `requiredFields`, `countryField` |
103
+ | CheckoutDeliveryOptions | Delivery method radio selection with free shipping logic | `options`, `selectedId`, `onSelect`, `subtotal` |
104
+ | CheckoutOrderReview | Full order review with edit callbacks per section | `order`, `onEditItems`, `onEditShippingAddress` |
105
+ | CheckoutOrderConfirmation | Order success screen with details | `order`, `orderId`, `onContinueShopping` |
106
+
107
+ **Internal (not exported):** `CheckoutSectionHeader` — reusable section header with left/right layout.
104
108
 
105
109
  ### Composite Step Components
106
110
 
111
+ All step components support `hideProgress?: boolean` to hide the built-in progress indicator (e.g. when rendered externally).
112
+
107
113
  | Component | Purpose | Combines |
108
114
  | -------------------- | ------------------------------------------------- | ------------------------------------------------------------- |
109
- | CheckoutReviewStep | Cart review + guest/login forms (2-column layout) | CartReview + GuestForm/LoginForm (tabbed/stacked/single mode) |
115
+ | CheckoutReviewStep | Cart review + guest/login forms (2-column layout) | CartReview + GuestOrLoginForm (tabbed/stacked/single mode) |
110
116
  | CheckoutShippingStep | Shipping + billing addresses + delivery selection | AddressForm (×2) + DeliveryOptions + OrderSummary sidebar |
111
117
  | CheckoutConfirmStep | Order review + place order CTA | OrderReview + OrderSummary sidebar + validation errors |
112
118
  | CheckoutCompleteStep | Order confirmation with loading/error states | Progress + OrderConfirmation (or error/loading fallback) |
113
119
 
120
+ ### CheckoutGuestOrLoginForm
121
+
122
+ Composite component combining guest and login forms with multiple display modes:
123
+
124
+ | FormMode | Behavior |
125
+ | -------------- | ------------------------------------------------------------ |
126
+ | `"tabbed"` | Pill-style tab switcher between guest/login (default) |
127
+ | `"stacked"` | Both forms vertically stacked with "or" divider |
128
+ | `"guest-only"` | Only guest form |
129
+ | `"login-only"` | Only login form |
130
+
131
+ Key props: `guestForm`, `loginForm` (pass-through config objects), `activeTab` (bindable: `"guest"` | `"login"`), `formMode`, `heading`, `hLevel`.
132
+
133
+ Exported type: `CheckoutFormMode` (`"guest-only" | "login-only" | "tabbed" | "stacked"`).
134
+
114
135
  ### Checkout Architecture
115
136
 
116
137
  - **i18n**: Full translation support via `t?: TranslateFn` on every component; 100+ default English keys
117
138
  - **Validation**: Built-in validators (`validateEmail`, `validateAddress`, `validateCustomerForm`, `validateLoginForm`)
118
139
  - **Factory helpers**: `createEmptyAddress()`, `createEmptyCustomerFormData()`, `createEmptyLoginFormData()`
119
140
  - **Price formatting**: `defaultFormatPrice(cents)` — all prices in smallest currency unit (cents)
120
- - **CSS tokens**: `--stuic-checkout-*` prefix; extensive design tokens in `Checkout/index.css`
141
+ - **CSS tokens**: `--stuic-checkout-*` prefix; modular CSS partials (`_shared.css`, `_*.css`) in `Checkout/`
121
142
  - **Data types**: `CheckoutOrderData`, `CheckoutAddressData`, `CheckoutDeliveryOption`, `CheckoutOrderTotals`, etc.
122
143
  - **Snippet overrides**: Every component offers snippet props to replace default sections
123
144
 
@@ -270,5 +291,5 @@ Components use private CSS vars (`--_*`) set by intent/variant:
270
291
  | src/lib/components/Button/ | Reference implementation |
271
292
  | src/lib/components/Modal/ | Complex component example |
272
293
  | src/lib/components/Input/ | Form field patterns |
273
- | src/lib/components/Checkout/ | E-commerce checkout flow (13 sub-components) |
294
+ | src/lib/components/Checkout/ | E-commerce checkout flow (14 exported sub-components) |
274
295
  | src/lib/index.ts | All component exports |
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- 42 utility functions for common tasks. Organized by category.
5
+ 42 utility modules for common tasks. Organized by category.
6
6
 
7
7
  ---
8
8
 
@@ -35,12 +35,11 @@ theme.value = "dark";
35
35
 
36
36
  ## DOM Utilities
37
37
 
38
- | Util | Purpose |
39
- | ------------------ | ------------------------------------- |
40
- | `qsa` | querySelectorAll wrapper |
41
- | `bodyScrollLocker` | Lock/unlock body scroll |
42
- | `anchorName` | Generate CSS anchor-positioning names |
43
- | `getId` | Generate unique IDs |
38
+ | Util | Purpose |
39
+ | ------------------ | ------------------------ |
40
+ | `qsa` | querySelectorAll wrapper |
41
+ | `bodyScrollLocker` | Lock/unlock body scroll |
42
+ | `getId` | Generate unique IDs |
44
43
 
45
44
  ---
46
45
 
@@ -83,26 +82,23 @@ const search = debounce((query: string) => {
83
82
 
84
83
  ## Type Checks
85
84
 
86
- | Util | Purpose |
87
- | --------------- | ------------------------- |
88
- | `isNullish` | Check null/undefined |
89
- | `isPlainObject` | Check plain object |
90
- | `isImage` | Check if file is image |
91
- | `isBrowser` | Check browser environment |
92
- | `isMac` | Check macOS |
85
+ | Util | Purpose |
86
+ | ----------- | ------------------------- |
87
+ | `isNullish` | Check null/undefined |
88
+ | `isImage` | Check if file is image |
89
+ | `isBrowser` | Check browser environment |
90
+ | `isMac` | Check macOS |
93
91
 
94
92
  ---
95
93
 
96
94
  ## Data Handling
97
95
 
98
- | Util | Purpose |
99
- | -------------------- | ----------------------- |
100
- | `maybeJsonParse` | Safe JSON.parse |
101
- | `maybeJsonStringify` | Safe JSON.stringify |
102
- | `toInteger` | Safe integer conversion |
103
- | `omit` | Omit object keys |
104
- | `pick` | Pick object keys |
105
- | `moveArrayItem` | Reorder array items |
96
+ | Util | Purpose |
97
+ | -------------------- | ------------------- |
98
+ | `maybeJsonParse` | Safe JSON.parse |
99
+ | `maybeJsonStringify` | Safe JSON.stringify |
100
+ | `omit` | Omit object keys |
101
+ | `pick` | Pick object keys |
106
102
 
107
103
  ---
108
104
 
@@ -139,6 +135,28 @@ twMerge("px-4 py-2", "px-6"); // => "py-2 px-6"
139
135
 
140
136
  ---
141
137
 
138
+ ## Storage
139
+
140
+ | Util | Purpose |
141
+ | --------------------- | ---------------------------------------------------------- |
142
+ | `StorageAbstraction` | Unified class over localStorage/sessionStorage/memory |
143
+ | `MemoryStorage` | In-memory storage fallback (SSR-safe) |
144
+ | `localStorageValue` | Non-reactive value backed by localStorage (get/set/remove) |
145
+ | `sessionStorageValue` | Non-reactive value backed by sessionStorage |
146
+ | `memoryStorageValue` | Non-reactive value backed by in-memory storage |
147
+
148
+ ### Example: Storage Abstraction
149
+
150
+ ```ts
151
+ import { StorageAbstraction } from "@marianmeres/stuic";
152
+
153
+ const storage = new StorageAbstraction("local");
154
+ storage.set("user", { name: "John" });
155
+ storage.get("user"); // { name: 'John' }
156
+ ```
157
+
158
+ ---
159
+
142
160
  ## Design Tokens
143
161
 
144
162
  | Util | Purpose |
@@ -151,9 +169,10 @@ twMerge("px-4 py-2", "px-6"); // => "py-2 px-6"
151
169
 
152
170
  ## Key Files
153
171
 
154
- | File | Purpose |
155
- | ---------------------------------------- | -------------------------- |
156
- | src/lib/utils/index.ts | All utility exports |
157
- | src/lib/utils/tw-merge.ts | Critical for class merging |
158
- | src/lib/utils/persistent-state.svelte.ts | Reactive storage pattern |
159
- | src/lib/utils/design-tokens.ts | Theme token types |
172
+ | File | Purpose |
173
+ | ---------------------------------------- | ----------------------------------------- |
174
+ | src/lib/utils/index.ts | All utility exports |
175
+ | src/lib/utils/tw-merge.ts | Critical for class merging |
176
+ | src/lib/utils/persistent-state.svelte.ts | Reactive storage pattern (runes-based) |
177
+ | src/lib/utils/storage-abstraction.ts | Non-reactive storage (localStorage, etc.) |
178
+ | src/lib/utils/design-tokens.ts | Theme token types |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/stuic",
3
- "version": "3.18.0",
3
+ "version": "3.19.0",
4
4
  "files": [
5
5
  "dist",
6
6
  "!dist/**/*.test.*",
@@ -39,7 +39,7 @@
39
39
  "@marianmeres/icons-fns": "^5.0.0",
40
40
  "@marianmeres/random-human-readable": "^1.6.1",
41
41
  "@sveltejs/adapter-auto": "^4.0.0",
42
- "@sveltejs/kit": "^2.51.0",
42
+ "@sveltejs/kit": "^2.52.0",
43
43
  "@sveltejs/package": "^2.5.7",
44
44
  "@sveltejs/vite-plugin-svelte": "^6.2.4",
45
45
  "@tailwindcss/cli": "^4.1.18",
@@ -53,8 +53,8 @@
53
53
  "prettier": "^3.8.1",
54
54
  "prettier-plugin-svelte": "^3.4.1",
55
55
  "publint": "^0.3.17",
56
- "svelte": "^5.50.3",
57
- "svelte-check": "^4.3.6",
56
+ "svelte": "^5.51.1",
57
+ "svelte-check": "^4.4.0",
58
58
  "tailwindcss": "^4.1.18",
59
59
  "tsx": "^4.21.0",
60
60
  "typescript": "^5.9.3",