@marianmeres/stuic 3.37.0 → 3.38.1

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
@@ -23,8 +23,8 @@
23
23
 
24
24
  ```
25
25
  src/lib/
26
- ├── components/ # 45 UI components
27
- ├── actions/ # 14 Svelte actions
26
+ ├── components/ # 46 UI components
27
+ ├── actions/ # 15 Svelte actions
28
28
  ├── utils/ # 43 utility modules
29
29
  ├── themes/ # 29 theme definitions (.ts) + generated CSS (css/)
30
30
  ├── icons/ # Icon re-exports
@@ -74,10 +74,10 @@ src/lib/
74
74
 
75
75
  ### Domain Docs
76
76
 
77
- - [Components](./docs/domains/components.md) — 45 component directories, Props pattern, snippets
77
+ - [Components](./docs/domains/components.md) — 46 component directories, Props pattern, snippets
78
78
  - [Theming](./docs/domains/theming.md) — CSS tokens, dark mode, themes
79
- - [Actions](./docs/domains/actions.md) — 14 Svelte directives
80
- - [Utils](./docs/domains/utils.md) — 42 utility modules
79
+ - [Actions](./docs/domains/actions.md) — 15 Svelte directives
80
+ - [Utils](./docs/domains/utils.md) — 43 utility modules
81
81
 
82
82
  ### Reference
83
83
 
package/API.md CHANGED
@@ -280,6 +280,25 @@ Multi-language input field.
280
280
 
281
281
  Toggle button styled as a "like" action.
282
282
 
283
+ #### `FieldPhoneNumber`
284
+
285
+ International phone number input with country dial code picker. Parses and composes full phone numbers (e.g. `+421905123456`), handles paste with international prefix detection, supports country filtering and preferred countries.
286
+
287
+ | Prop | Type | Default | Description |
288
+ | -------------------- | ----------------------------------- | ----------- | -------------------------------------------------- |
289
+ | `value` | `string` | `""` | Bindable full phone number (e.g. `"+421905123456"`) |
290
+ | `country` | `string` | `""` | Bindable selected country ISO code (e.g. `"SK"`) |
291
+ | `dialCode` | `string` | `""` | Bindable dial code with `+` (e.g. `"+421"`) |
292
+ | `localNumber` | `string` | `""` | Bindable local number part |
293
+ | `defaultCountry` | `string` | — | ISO code for initial country selection |
294
+ | `flags` | `boolean` | `true` | Show country flag emoji |
295
+ | `countries` | `string[]` | all | Filtered list of country ISO codes to show |
296
+ | `preferredCountries` | `string[]` | — | ISO codes pinned at top of dropdown |
297
+ | `name` | `string` | — | Hidden input name for form submission |
298
+ | `validate` | `boolean \| ValidateOptions` | — | Enable phone validation |
299
+
300
+ Exports: `FieldPhoneNumber`, `FieldPhoneNumberProps`, `validatePhoneNumber`, `Country`.
301
+
283
302
  #### `FieldObject`
284
303
 
285
304
  Dual-mode JSON object editor with pretty-print and raw edit modes. Validates JSON syntax, supports recursive depth display, auto-grow textarea, and form submission via hidden input.
@@ -405,7 +424,43 @@ Progress bar.
405
424
 
406
425
  #### `Spinner`
407
426
 
408
- Loading spinner indicator.
427
+ Loading spinner indicator (default SVG spinner).
428
+
429
+ #### `SpinnerCircle`
430
+
431
+ CSS-only circular spinner.
432
+
433
+ | Prop | Type | Default | Description |
434
+ | ----------- | --------------------------------- | ---------- | -------------------------- |
435
+ | `duration` | `number` | `750` | One loop duration in ms |
436
+ | `thickness` | `"normal" \| "thin" \| "thick"` | `"normal"` | Border thickness preset |
437
+ | `direction` | `"cw" \| "ccw"` | `"cw"` | Rotation direction |
438
+
439
+ #### `SpinnerCircleOscillate`
440
+
441
+ Animated Circle-based spinner with oscillating arc completeness.
442
+
443
+ | Prop | Type | Default | Description |
444
+ | ---------------- | --------- | ------- | ----------------------------- |
445
+ | `bgStrokeColor` | `string` | — | Background circle stroke |
446
+ | `strokeWidth` | `number` | — | SVG stroke width |
447
+ | `noOscillate` | `boolean` | — | Fixed completeness (no anim) |
448
+ | `rotateDuration` | `string` | — | CSS animation duration |
449
+
450
+ #### `SpinnerUnicode`
451
+
452
+ Unicode character frame animation with 17 built-in variants.
453
+
454
+ | Prop | Type | Default | Description |
455
+ | ---------- | ------------------------ | ------------------- | ---------------------------- |
456
+ | `speed` | `number` | `100` | Frame interval in ms |
457
+ | `variant` | `SpinnerUnicodeVariant` | `"braille_bar_dot"` | Built-in animation variant |
458
+ | `reversed` | `boolean` | `false` | Reverse frame order |
459
+ | `frames` | `string[]` | — | Custom animation frames |
460
+
461
+ Variants: `braille_bar`, `braille_bar_dot`, `braille_dot_circle`, `braille_dot_bounce`, `half_circle`, `quarter_circle`, `ascii`, `bar_v`, `bar_h`, `shade`, `arrows`, `arrows2`, `asterix`, `asterix2`, `asterix3`, `asterix4`, `asterix5`.
462
+
463
+ Helper: `spinnerCreateBackAndForthCharFrames(width, hiChar, loChar, glue?)` — create custom back-and-forth animation frames.
409
464
 
410
465
  #### `Skeleton`
411
466
 
@@ -829,6 +884,30 @@ Responsive data table with paging, row selection, batch actions, and mobile card
829
884
  />
830
885
  ```
831
886
 
887
+ #### `ImageCycler`
888
+
889
+ Auto-cycling image carousel with fade transitions. Preloads next image before displaying. Supports custom title/description snippets.
890
+
891
+ | Prop | Type | Default | Description |
892
+ | -------------------- | ------------------- | --------- | ---------------------------------------- |
893
+ | `images` | `ImageCyclerImage[]`| required | Array of images to cycle through |
894
+ | `fit` | `ImageCyclerFit` | `"cover"` | CSS object-fit: `"cover"`, `"contain"`, `"fill"` |
895
+ | `minWait` | `number` | `3000` | Minimum wait (ms) before next image |
896
+ | `transitionDuration` | `number` | `500` | Fade transition duration (ms) |
897
+ | `onclick` | `(image, index) => void` | — | Click handler |
898
+ | `title` | `Snippet` | — | Custom title snippet `({ image, index, onclick })` |
899
+ | `description` | `Snippet` | — | Custom description snippet |
900
+
901
+ ```ts
902
+ interface ImageCyclerImage {
903
+ src: string;
904
+ alt?: string;
905
+ title?: string;
906
+ description?: string;
907
+ [key: string]: unknown;
908
+ }
909
+ ```
910
+
832
911
  #### `ThemePreview`
833
912
 
834
913
  Theme color swatch preview.
@@ -1215,6 +1294,63 @@ Tooltip display from `aria-label`.
1215
1294
  <button use:tooltip aria-label="Save changes"> Save </button>
1216
1295
  ```
1217
1296
 
1297
+ ### `createTour` / `tourStep` (onboarding)
1298
+
1299
+ Multi-step onboarding tour built on the spotlight primitive. Define steps centrally, attach targets via `use:tourStep`.
1300
+
1301
+ **`createTour(options)`**
1302
+
1303
+ | Option | Type | Default | Description |
1304
+ | ----------------- | ----------------------------- | --------- | ---------------------------------------------------- |
1305
+ | `steps` | `TourStepDef[]` | required | Tour step definitions |
1306
+ | `waitForElement` | `number` | `500` | Max wait (ms) for step element to appear |
1307
+ | `labels` | `TourLabels` | defaults | Default button labels (Next, Back, Skip, Finish) |
1308
+ | `shell` | `Snippet<[TourShellContext]>` | — | Custom shell snippet replacing default UI |
1309
+ | `closeOnEscape` | `boolean` | `true` | Press Escape to skip |
1310
+ | `confirmSkip` | `() => boolean \| Promise<boolean>` | — | Guard before skipping (return `false` to cancel) |
1311
+ | `storageKey` | `string` | — | Persist tour completion (skips on re-run) |
1312
+ | `storage` | `"local" \| "session"` | `"local"` | Storage backend for persistence |
1313
+ | `onStart` | `() => void` | — | Called when tour starts |
1314
+ | `onEnd` | `() => void` | — | Called when tour completes |
1315
+ | `onSkip` | `() => void` | — | Called when tour is skipped |
1316
+ | `onStepChange` | `(step, index) => void` | — | Called on every step change |
1317
+
1318
+ **Returns:** `{ start(), stop(), next(), prev(), skip(), reset(), active, currentStep, currentIndex }`
1319
+
1320
+ **`TourStepDef`:**
1321
+
1322
+ | Field | Type | Description |
1323
+ | ------------- | ------------------- | ----------------------------------------- |
1324
+ | `id` | `string` | Unique ID (must match `use:tourStep`) |
1325
+ | `title` | `string` | Step title |
1326
+ | `content` | `THC` | Step description (string/html/component) |
1327
+ | `position` | `SpotlightPosition` | Annotation placement |
1328
+ | `padding` | `number` | Cutout padding (px) |
1329
+ | `borderRadius`| `number` | Cutout border radius (px) |
1330
+ | `onEnter` | `() => void` | Called when entering step |
1331
+ | `onLeave` | `() => void` | Called when leaving step |
1332
+
1333
+ **`tourStep` action:** `use:tourStep={[tour, stepId]}`
1334
+
1335
+ ```svelte
1336
+ <script>
1337
+ import { createTour, tourStep } from "@marianmeres/stuic";
1338
+
1339
+ const tour = createTour({
1340
+ steps: [
1341
+ { id: "header", title: "Welcome", content: "This is the top." },
1342
+ { id: "save-btn", title: "Save", content: "Click here to save." },
1343
+ ],
1344
+ storageKey: "intro-tour",
1345
+ onEnd: () => console.log("Tour complete!"),
1346
+ });
1347
+ </script>
1348
+
1349
+ <header use:tourStep={[tour, "header"]}>...</header>
1350
+ <button use:tourStep={[tour, "save-btn"]}>Save</button>
1351
+ <button onclick={tour.start}>Start Tour</button>
1352
+ ```
1353
+
1218
1354
  ---
1219
1355
 
1220
1356
  ## Utilities
package/README.md CHANGED
@@ -127,7 +127,7 @@ AppShell, Accordion, Backdrop, Modal, ModalDialog, Drawer, Collapsible, SlidingP
127
127
 
128
128
  ### Forms & Inputs
129
129
 
130
- FieldInput, FieldTextarea, FieldSelect, FieldCheckbox, FieldRadios, FieldFile, FieldAssets, FieldOptions, FieldKeyValues, FieldObject, FieldSwitch, FieldInputLocalized, FieldLikeButton, Fieldset, LoginForm, LoginFormModal
130
+ FieldInput, FieldTextarea, FieldSelect, FieldCheckbox, FieldRadios, FieldFile, FieldAssets, FieldOptions, FieldKeyValues, FieldObject, FieldSwitch, FieldInputLocalized, FieldLikeButton, FieldPhoneNumber, Fieldset, LoginForm, LoginFormModal
131
131
 
132
132
  ### Buttons & Controls
133
133
 
@@ -135,7 +135,7 @@ Button, ButtonGroupRadio, Switch, TwCheck, ListItemButton, X
135
135
 
136
136
  ### Feedback & Notifications
137
137
 
138
- Notifications, AlertConfirmPrompt, DismissibleMessage, Progress, Spinner, Skeleton
138
+ Notifications, AlertConfirmPrompt, DismissibleMessage, Progress, Spinner (SpinnerCircle, SpinnerCircleOscillate, SpinnerUnicode), Skeleton
139
139
 
140
140
  ### Navigation & Menus
141
141
 
@@ -143,7 +143,7 @@ CommandMenu, DropdownMenu, TabbedMenu, TypeaheadInput, KbdShortcut
143
143
 
144
144
  ### Display & Utility
145
145
 
146
- Avatar, Book, BookResponsive, Carousel, Circle, AnimatedElipsis, H, IconSwap, Separator, ThemePreview, ColorScheme, Thc, HoverExpandableWidth, AssetsPreview, AssetsPreviewInline, DataTable
146
+ Avatar, Book, BookResponsive, Carousel, Circle, AnimatedElipsis, H, IconSwap, ImageCycler, Separator, ThemePreview, ColorScheme, Thc, HoverExpandableWidth, AssetsPreview, AssetsPreviewInline, DataTable
147
147
 
148
148
  ### E-commerce
149
149
 
@@ -160,7 +160,7 @@ Cart, Checkout (CheckoutProgress, CheckoutOrderSummary, CheckoutCartReview, Chec
160
160
  <div use:fileDropzone={() => ({ onDrop: handleFiles })}>Drop here</div>
161
161
  ```
162
162
 
163
- `autogrow` · `validate` · `focusTrap` · `autoscroll` · `dimBehind` · `fileDropzone` · `highlightDragover` · `resizableWidth` · `spotlight` · `trim` · `typeahead` · `onSubmitValidityCheck` · `popover` · `tooltip`
163
+ `autogrow` · `validate` · `focusTrap` · `autoscroll` · `dimBehind` · `fileDropzone` · `highlightDragover` · `resizableWidth` · `spotlight` · `trim` · `typeahead` · `onSubmitValidityCheck` · `popover` · `tooltip` · `createTour` / `tourStep` (onboarding)
164
164
 
165
165
  ## TypeScript
166
166
 
@@ -0,0 +1,98 @@
1
+ <script lang="ts" module>
2
+ import type { Snippet } from "svelte";
3
+
4
+ export interface ImageCyclerImage {
5
+ src: string;
6
+ alt?: string;
7
+ title?: string;
8
+ description?: string;
9
+ [key: string]: unknown;
10
+ }
11
+
12
+ export type ImageCyclerFit = "cover" | "contain" | "fill";
13
+
14
+ export interface Props {
15
+ images: ImageCyclerImage[];
16
+ fit?: ImageCyclerFit;
17
+ minWait?: number;
18
+ transitionDuration?: number;
19
+ onclick?: (image: ImageCyclerImage, index: number) => void;
20
+ title?: Snippet<[{ image: ImageCyclerImage; index: number; onclick: (() => void) | undefined }]>;
21
+ description?: Snippet<[{ image: ImageCyclerImage; index: number; onclick: (() => void) | undefined }]>;
22
+ unstyled?: boolean;
23
+ class?: string;
24
+ el?: HTMLElement;
25
+ }
26
+ </script>
27
+
28
+ <script lang="ts">
29
+ import { fade } from "svelte/transition";
30
+ import { preloadImg } from "../../utils/preload-img.js";
31
+ import { twMerge } from "../../utils/tw-merge.js";
32
+
33
+ let {
34
+ images,
35
+ fit = "cover",
36
+ minWait = 3000,
37
+ transitionDuration = 500,
38
+ onclick,
39
+ title,
40
+ description,
41
+ unstyled = false,
42
+ class: classProp,
43
+ el = $bindable(),
44
+ }: Props = $props();
45
+
46
+ let currentIndex = $state(0);
47
+
48
+ let _class = $derived(unstyled ? classProp : twMerge("stuic-image-cycler", classProp));
49
+
50
+ let _onclick = $derived(onclick ? () => onclick(images[currentIndex], currentIndex) : undefined);
51
+
52
+ $effect(() => {
53
+ const idx = currentIndex;
54
+ if (images.length <= 1) return;
55
+ let cancelled = false;
56
+ const nextIndex = (idx + 1) % images.length;
57
+ Promise.all([
58
+ preloadImg({ src: images[nextIndex].src }),
59
+ new Promise<void>((resolve) => setTimeout(resolve, minWait)),
60
+ ]).then(() => {
61
+ if (!cancelled) currentIndex = nextIndex;
62
+ });
63
+ return () => {
64
+ cancelled = true;
65
+ };
66
+ });
67
+ </script>
68
+
69
+ <div bind:this={el} class={_class}>
70
+ {#key currentIndex}
71
+ <div
72
+ class="stuic-image-cycler-bg"
73
+ data-fit={!unstyled ? fit : undefined}
74
+ style:background-image="url({images[currentIndex].src})"
75
+ role="img"
76
+ aria-label={images[currentIndex].alt ?? images[currentIndex].title ?? ""}
77
+ in:fade={{ duration: transitionDuration }}
78
+ out:fade={{ duration: transitionDuration }}
79
+ ></div>
80
+ {/key}
81
+
82
+ {#if title || description}
83
+ {#key currentIndex}
84
+ <div
85
+ class="stuic-image-cycler-meta"
86
+ in:fade={{ duration: transitionDuration }}
87
+ out:fade={{ duration: transitionDuration }}
88
+ >
89
+ {#if title}
90
+ {@render title({ image: images[currentIndex], index: currentIndex, onclick: _onclick })}
91
+ {/if}
92
+ {#if description}
93
+ {@render description({ image: images[currentIndex], index: currentIndex, onclick: _onclick })}
94
+ {/if}
95
+ </div>
96
+ {/key}
97
+ {/if}
98
+ </div>
@@ -0,0 +1,32 @@
1
+ import type { Snippet } from "svelte";
2
+ export interface ImageCyclerImage {
3
+ src: string;
4
+ alt?: string;
5
+ title?: string;
6
+ description?: string;
7
+ [key: string]: unknown;
8
+ }
9
+ export type ImageCyclerFit = "cover" | "contain" | "fill";
10
+ export interface Props {
11
+ images: ImageCyclerImage[];
12
+ fit?: ImageCyclerFit;
13
+ minWait?: number;
14
+ transitionDuration?: number;
15
+ onclick?: (image: ImageCyclerImage, index: number) => void;
16
+ title?: Snippet<[{
17
+ image: ImageCyclerImage;
18
+ index: number;
19
+ onclick: (() => void) | undefined;
20
+ }]>;
21
+ description?: Snippet<[{
22
+ image: ImageCyclerImage;
23
+ index: number;
24
+ onclick: (() => void) | undefined;
25
+ }]>;
26
+ unstyled?: boolean;
27
+ class?: string;
28
+ el?: HTMLElement;
29
+ }
30
+ declare const ImageCycler: import("svelte").Component<Props, {}, "el">;
31
+ type ImageCycler = ReturnType<typeof ImageCycler>;
32
+ export default ImageCycler;
@@ -0,0 +1,28 @@
1
+ :root {
2
+ --stuic-image-cycler-transition-duration: 500ms;
3
+ }
4
+
5
+ @layer components {
6
+ .stuic-image-cycler {
7
+ position: relative;
8
+ width: 100%;
9
+ height: 100%;
10
+ overflow: hidden;
11
+ }
12
+
13
+ .stuic-image-cycler-bg {
14
+ position: absolute;
15
+ inset: 0;
16
+ background-position: center;
17
+ background-repeat: no-repeat;
18
+ }
19
+
20
+ .stuic-image-cycler-bg[data-fit="cover"] { background-size: cover; }
21
+ .stuic-image-cycler-bg[data-fit="contain"] { background-size: contain; }
22
+ .stuic-image-cycler-bg[data-fit="fill"] { background-size: 100% 100%; }
23
+
24
+ .stuic-image-cycler-meta {
25
+ position: absolute;
26
+ inset: 0;
27
+ }
28
+ }
@@ -0,0 +1 @@
1
+ export { default as ImageCycler, type Props as ImageCyclerProps, type ImageCyclerImage, type ImageCyclerFit, } from "./ImageCycler.svelte";
@@ -0,0 +1 @@
1
+ export { default as ImageCycler, } from "./ImageCycler.svelte";
package/dist/index.css CHANGED
@@ -41,6 +41,7 @@ In practice:
41
41
  @import "./components/DismissibleMessage/index.css";
42
42
  @import "./components/DropdownMenu/index.css";
43
43
  @import "./components/H/index.css";
44
+ @import "./components/ImageCycler/index.css";
44
45
  @import "./components/IconSwap/index.css";
45
46
  @import "./components/Input/index.css";
46
47
  @import "./components/KbdShortcut/index.css";
package/dist/index.d.ts CHANGED
@@ -42,6 +42,7 @@ export * from "./components/DismissibleMessage/index.js";
42
42
  export * from "./components/Drawer/index.js";
43
43
  export * from "./components/DropdownMenu/index.js";
44
44
  export * from "./components/H/index.js";
45
+ export * from "./components/ImageCycler/index.js";
45
46
  export * from "./components/HoverExpandableWidth/index.js";
46
47
  export * from "./components/IconSwap/index.js";
47
48
  export * from "./components/Input/index.js";
package/dist/index.js CHANGED
@@ -43,6 +43,7 @@ export * from "./components/DismissibleMessage/index.js";
43
43
  export * from "./components/Drawer/index.js";
44
44
  export * from "./components/DropdownMenu/index.js";
45
45
  export * from "./components/H/index.js";
46
+ export * from "./components/ImageCycler/index.js";
46
47
  export * from "./components/HoverExpandableWidth/index.js";
47
48
  export * from "./components/IconSwap/index.js";
48
49
  export * from "./components/Input/index.js";
@@ -32,19 +32,19 @@ Layer 3: Internal Vars (--_bg, --_text, --_border)
32
32
 
33
33
  ```
34
34
  src/lib/
35
- ├── components/ # 45 UI components
35
+ ├── components/ # 46 UI components
36
36
  │ └── {Name}/
37
37
  │ ├── {Name}.svelte # Main component
38
38
  │ ├── index.ts # Exports
39
39
  │ ├── index.css # CSS tokens (if styled)
40
40
  │ └── README.md # Documentation
41
41
 
42
- ├── actions/ # 14 Svelte actions
42
+ ├── actions/ # 15 Svelte actions
43
43
  │ ├── *.svelte.ts # Reactive actions
44
44
  │ ├── *.ts # Traditional actions
45
45
  │ └── index.ts # Barrel export
46
46
 
47
- ├── utils/ # 42 utility modules
47
+ ├── utils/ # 43 utility modules
48
48
  │ ├── *.svelte.ts # Reactive utilities
49
49
  │ ├── *.ts # Pure functions
50
50
  │ └── index.ts # Barrel export
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- 14 Svelte actions (directives) for reusable DOM behavior.
5
+ 15 Svelte actions (directives) for reusable DOM behavior.
6
6
 
7
7
  ---
8
8
 
@@ -24,6 +24,7 @@
24
24
  | `popover` | Popover positioning | `popover/` |
25
25
  | `spotlight` | Spotlight/coach mark overlay with cutout hole | `spotlight/` |
26
26
  | `tooltip` | Tooltip positioning and display | `tooltip/` |
27
+ | `createTour` / `tourStep` | Multi-step onboarding tour (built on spotlight) | `onboarding/` |
27
28
 
28
29
  ---
29
30
 
@@ -115,6 +116,28 @@ Actions using `$effect()` accept a function returning options:
115
116
  <button use:tooltip={{ content: "Save changes", position: "top" }}> Save </button>
116
117
  ```
117
118
 
119
+ ### Onboarding Tour
120
+
121
+ ```svelte
122
+ <script>
123
+ import { createTour, tourStep } from "@marianmeres/stuic";
124
+
125
+ const tour = createTour({
126
+ steps: [
127
+ { id: "header", title: "Welcome", content: "This is the top." },
128
+ { id: "save-btn", title: "Save", content: "Click here to save." },
129
+ ],
130
+ onEnd: () => console.log("Tour complete!"),
131
+ });
132
+ </script>
133
+
134
+ <header use:tourStep={[tour, "header"]}>...</header>
135
+ <button use:tourStep={[tour, "save-btn"]}>Save</button>
136
+ <button onclick={tour.start}>Start Tour</button>
137
+ ```
138
+
139
+ Features: step navigation (next/prev/skip), persistent state via `storageKey`, custom shell snippets, `confirmSkip` callback, wait-for-element mechanism, Escape key support, step lifecycle callbacks (`onEnter`/`onLeave`).
140
+
118
141
  ---
119
142
 
120
143
  ## Action File Patterns
@@ -163,3 +186,4 @@ export function focusTrap(el: HTMLElement, options?: Options) {
163
186
  | src/lib/actions/dim-behind/ | Simplified spotlight alternative |
164
187
  | src/lib/actions/spotlight/ | Spotlight/coach mark action |
165
188
  | src/lib/actions/tooltip/ | Multi-file action example |
189
+ | src/lib/actions/onboarding/ | Multi-step onboarding tour |
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- 45 Svelte 5 component directories with consistent API patterns. All use runes-based reactivity.
5
+ 46 Svelte 5 component directories with consistent API patterns. All use runes-based reactivity.
6
6
 
7
7
  ## Component Categories
8
8
 
@@ -39,18 +39,22 @@
39
39
 
40
40
  | Component | Purpose |
41
41
  | ------------------ | ------------------------------------- |
42
- | Notifications | Toast notification system |
43
- | AlertConfirmPrompt | Dialog factory (alert/confirm/prompt) |
44
- | DismissibleMessage | Closeable message banner |
45
- | Progress | Progress bar |
46
- | Spinner | Loading indicator |
47
- | Skeleton | Loading placeholder |
42
+ | Notifications | Toast notification system |
43
+ | AlertConfirmPrompt | Dialog factory (alert/confirm/prompt) |
44
+ | DismissibleMessage | Closeable message banner |
45
+ | Progress | Progress bar |
46
+ | Spinner | Loading indicator (default SVG spinner) |
47
+ | SpinnerCircle | CSS-only circular spinner with thickness/direction options |
48
+ | SpinnerCircleOscillate | Animated Circle-based spinner with oscillating completeness |
49
+ | SpinnerUnicode | Unicode character frame animation (17 variants) |
50
+ | Skeleton | Loading placeholder |
48
51
 
49
52
  ### Form
50
53
 
51
54
  | Component | Purpose |
52
55
  | ------------------------------------- | ------------------------------------------------- |
53
56
  | Input (FieldInput, FieldSelect, etc.) | Form fields |
57
+ | FieldPhoneNumber | International phone input with country picker |
54
58
  | FieldObject | Dual-mode JSON object editor (pretty-print/raw) |
55
59
  | Fieldset | Field grouping with legend |
56
60
  | FieldKeyValues | Key-value pair editor |
@@ -78,6 +82,7 @@
78
82
  | Separator | Horizontal/vertical separator line |
79
83
  | Thc | Flexible renderer for text, HTML, components, or snippets |
80
84
  | X | Styled close/multiply SVG icon |
85
+ | ImageCycler | Auto-cycling image carousel with fade transitions and preloading |
81
86
 
82
87
  ### E-commerce
83
88
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/stuic",
3
- "version": "3.37.0",
3
+ "version": "3.38.1",
4
4
  "files": [
5
5
  "dist",
6
6
  "!dist/**/*.test.*",