@marianmeres/stuic 3.172.0 → 3.173.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.
Files changed (53) hide show
  1. package/AGENTS.md +2 -2
  2. package/API.md +56 -2
  3. package/README.md +14 -2
  4. package/dist/actions/resizable-width.svelte.d.ts +14 -28
  5. package/dist/actions/resizable-width.svelte.js +16 -171
  6. package/dist/attachments/index.d.ts +1 -0
  7. package/dist/attachments/index.js +1 -0
  8. package/dist/attachments/resizable.d.ts +113 -0
  9. package/dist/attachments/resizable.fixture.svelte +53 -0
  10. package/dist/attachments/resizable.fixture.svelte.d.ts +10 -0
  11. package/dist/attachments/resizable.js +295 -0
  12. package/dist/components/RangeSlider/README.md +291 -0
  13. package/dist/components/RangeSlider/RangeSlider.svelte +763 -0
  14. package/dist/components/RangeSlider/RangeSlider.svelte.d.ts +130 -0
  15. package/dist/components/RangeSlider/i18n-sk.d.ts +17 -0
  16. package/dist/components/RangeSlider/i18n-sk.js +19 -0
  17. package/dist/components/RangeSlider/i18n.d.ts +33 -0
  18. package/dist/components/RangeSlider/i18n.js +41 -0
  19. package/dist/components/RangeSlider/index.css +430 -0
  20. package/dist/components/RangeSlider/index.d.ts +3 -0
  21. package/dist/components/RangeSlider/index.js +3 -0
  22. package/dist/components/Rating/README.md +206 -0
  23. package/dist/components/Rating/Rating.svelte +355 -0
  24. package/dist/components/Rating/Rating.svelte.d.ts +82 -0
  25. package/dist/components/Rating/i18n-sk.d.ts +17 -0
  26. package/dist/components/Rating/i18n-sk.js +21 -0
  27. package/dist/components/Rating/i18n.d.ts +34 -0
  28. package/dist/components/Rating/i18n.js +42 -0
  29. package/dist/components/Rating/index.css +170 -0
  30. package/dist/components/Rating/index.d.ts +3 -0
  31. package/dist/components/Rating/index.js +3 -0
  32. package/dist/components/SplitPane/README.md +169 -0
  33. package/dist/components/SplitPane/SplitPane.svelte +202 -0
  34. package/dist/components/SplitPane/SplitPane.svelte.d.ts +67 -0
  35. package/dist/components/SplitPane/i18n-sk.d.ts +17 -0
  36. package/dist/components/SplitPane/i18n-sk.js +18 -0
  37. package/dist/components/SplitPane/i18n.d.ts +31 -0
  38. package/dist/components/SplitPane/i18n.js +39 -0
  39. package/dist/components/SplitPane/index.css +153 -0
  40. package/dist/components/SplitPane/index.d.ts +3 -0
  41. package/dist/components/SplitPane/index.js +3 -0
  42. package/dist/components/WithSidePanel/README.md +19 -16
  43. package/dist/icons/index.d.ts +2 -0
  44. package/dist/icons/index.js +3 -0
  45. package/dist/index.css +3 -0
  46. package/dist/index.d.ts +3 -0
  47. package/dist/index.js +3 -0
  48. package/docs/{maybe-todo.md → _archive/maybe-todo.md} +18 -7
  49. package/docs/architecture.md +1 -1
  50. package/docs/domains/actions.md +18 -18
  51. package/docs/domains/attachments.md +72 -9
  52. package/docs/domains/components.md +175 -32
  53. package/package.json +5 -5
@@ -15,10 +15,11 @@ re-exported from the package root (`src/lib/index.ts`).
15
15
 
16
16
  ## Available Attachments
17
17
 
18
- | Attachment | Purpose | File |
19
- | ------------ | ------------------------------------------------------------ | ---------------- |
20
- | `autoHeight` | Animate a host's height to its single child's natural height | `auto-height.ts` |
21
- | `longPress` | Detect a touch/pen long-press on the host (factory) | `long-press.ts` |
18
+ | Attachment | Purpose | File |
19
+ | ------------ | ------------------------------------------------------------------------- | ---------------- |
20
+ | `autoHeight` | Animate a host's height to its single child's natural height | `auto-height.ts` |
21
+ | `longPress` | Detect a touch/pen long-press on the host (factory) | `long-press.ts` |
22
+ | `resizable` | Drag / keyboard resizable width or height, ARIA window splitter (factory) | `resizable.ts` |
22
23
 
23
24
  ---
24
25
 
@@ -141,6 +142,67 @@ Options: `onLongPress` (required), `duration`, `moveTolerance`, `pointerTypes`,
141
142
 
142
143
  ---
143
144
 
145
+ ## `resizable`
146
+
147
+ Factory returning an attachment that makes the host's **width** (`axis: "x"`, default) or
148
+ **height** (`axis: "y"`) resizable by dragging its edge. The generalised core of the older
149
+ `resizableWidth` action (now a thin wrapper over it) and of the `SplitPane` component.
150
+
151
+ ```svelte
152
+ <script>
153
+ import { resizable } from "@marianmeres/stuic";
154
+ </script>
155
+
156
+ <!-- a draggable sidebar width -->
157
+ <div class="flex">
158
+ <aside {@attach resizable({ initial: 300, min: 200, max: 600, key: "sidebar" })}>
159
+ …
160
+ </aside>
161
+ <main class="flex-1">…</main>
162
+ </div>
163
+
164
+ <!-- a draggable height, in % of the (definite-height) parent -->
165
+ <div class="flex flex-col h-96">
166
+ <div {@attach resizable({ axis: "y", units: "%", initial: 40, max: 80 })}>…</div>
167
+ <div class="flex-1">…</div>
168
+ </div>
169
+ ```
170
+
171
+ Semantics worth knowing:
172
+
173
+ - **The handle is an ARIA window splitter** — focusable `role="separator"` with
174
+ `aria-orientation`, `aria-label` (`label`, default "Resize"), `aria-valuenow` / `min` /
175
+ `max` / `valuetext` in `units` and `aria-controls` → the host (which gets an id if it has
176
+ none). Arrow keys along the axis move it by `step` (default `10` px / `1` %; Shift ×10),
177
+ Home / End go to `min` / `max` (or 0 / the container), Enter — like double-click — resets.
178
+ Pointer Events with capture cover mouse, touch and pen; `data-resizing` sits on the handle
179
+ while a drag is in progress, and the body gets a resize cursor + `user-select: none`.
180
+ - **Created or provided handle.** By default a thin strip with a grip is appended inside the
181
+ host on the resized edge (`reverse` moves it to the start edge — left / top — and inverts
182
+ the drag, for a host sitting at the end of its container) and the host becomes
183
+ `position: relative`. Pass `handle` to drive an element of your own instead (a sibling
184
+ separator, say — what `SplitPane` does): it receives the semantics, the listeners and
185
+ `touch-action: none`, no styling; everything is restored on cleanup.
186
+ - **Sizes are in `units`**: `px`, or `%` of the parent element along the axis (the parent
187
+ needs a definite height for `axis: "y"` + `%`). `min` / `max` clamp every path — drag,
188
+ keys, `api.set()`.
189
+ - **`initial` vs stored.** `initial` (`0` = leave the CSS size alone) applies on setup unless
190
+ a size is stored under `key` (`resizable-width-<key>` / `resizable-height-<key>`;
191
+ `sessionStorage` by default, `storage: "local"` for localStorage). Reset restores
192
+ `resetTo`, which defaults to `initial`.
193
+ - **Imperative api** through `onInit(api)`: `api.set(size)` (clamped, persisted, announced,
194
+ reported), `api.reset()`, `api.current`.
195
+ - **Reactivity.** Options read in the template expression re-run the attachment, as do
196
+ reactive reads inside an options _function_ (`resizable(() => ({ … }))`). A re-run tears
197
+ the handle down and re-applies the (stored or initial) size, so don't feed it state that the
198
+ drag itself writes — read that with `untrack`, or pass it as `initial` once.
199
+
200
+ Options: `enabled`, `axis`, `initial`, `min`, `max`, `units`, `reverse`, `key`, `storage`,
201
+ `step`, `label`, `resetTo`, `handle`, `handleClass`, `handleDragClass`, `onResize`, `onInit`,
202
+ `debug`.
203
+
204
+ ---
205
+
144
206
  ## Attachment File Pattern
145
207
 
146
208
  An attachment is a function `(node) => cleanup?`, typed `Attachment<T>` from `svelte/attachments`.
@@ -171,8 +233,9 @@ it needs `$state` / `$derived` / a nested `$effect`.
171
233
 
172
234
  ## Key Files
173
235
 
174
- | File | Purpose |
175
- | ---------------------------------- | ------------------------------ |
176
- | src/lib/attachments/index.ts | All attachment exports |
177
- | src/lib/attachments/auto-height.ts | Height-animation attachment |
178
- | src/lib/attachments/long-press.ts | Long-press detection (factory) |
236
+ | File | Purpose |
237
+ | ---------------------------------- | ---------------------------------- |
238
+ | src/lib/attachments/index.ts | All attachment exports |
239
+ | src/lib/attachments/auto-height.ts | Height-animation attachment |
240
+ | src/lib/attachments/long-press.ts | Long-press detection (factory) |
241
+ | src/lib/attachments/resizable.ts | Resizable width / height (factory) |
@@ -2,45 +2,48 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- 75 Svelte 5 component directories with consistent API patterns. All use runes-based reactivity.
5
+ 78 Svelte 5 component directories with consistent API patterns. All use runes-based reactivity.
6
6
 
7
7
  ## Component Categories
8
8
 
9
9
  ### Layout
10
10
 
11
- | Component | Purpose |
12
- | ------------------------ | -------------------------------------------------- |
13
- | AppShell, AppShellSimple | Page layouts with header/sidebar/content |
14
- | Header | Responsive nav header with hamburger collapse |
15
- | Modal, ModalDialog | Overlay containers |
16
- | Drawer | Side panel overlay |
17
- | Backdrop | Semi-transparent overlay with escape/focus trap |
18
- | Collapsible | Expandable sections |
19
- | Accordion | Exclusive/multi-open expandable sections |
20
- | SlidingPanels | Panel transitions |
21
- | TabbedMenu | Tab navigation |
22
- | Nav | Navigation wrapper |
23
- | Breadcrumbs | Breadcrumb trail (collapse + JSON-LD helpers) |
24
- | WithSidePanel | Two-column layout with collapsible/resizable panel |
11
+ | Component | Purpose |
12
+ | ------------------------ | -------------------------------------------------------------------------- |
13
+ | AppShell, AppShellSimple | Page layouts with header/sidebar/content |
14
+ | Header | Responsive nav header with hamburger collapse |
15
+ | Modal, ModalDialog | Overlay containers |
16
+ | Drawer | Side panel overlay |
17
+ | Backdrop | Semi-transparent overlay with escape/focus trap |
18
+ | Collapsible | Expandable sections |
19
+ | Accordion | Exclusive/multi-open expandable sections |
20
+ | SlidingPanels | Panel transitions |
21
+ | TabbedMenu | Tab navigation |
22
+ | Nav | Navigation wrapper |
23
+ | Breadcrumbs | Breadcrumb trail (collapse + JSON-LD helpers) |
24
+ | WithSidePanel | Two-column layout with collapsible/resizable panel |
25
+ | SplitPane | Two panes, a draggable keyboard-operable separator (horizontal / vertical) |
25
26
 
26
27
  ### Interactive
27
28
 
28
- | Component | Purpose |
29
- | -------------------- | ----------------------------------------------------------------------------------------- |
30
- | Button | Actions with intent/variant/size |
31
- | CopyButton | Click-to-copy Button with copied/error feedback; `copyToClipboard()` util for code |
32
- | ButtonGroupRadio | Toggle group (single selection) |
33
- | Switch | Boolean toggle |
34
- | Slider | Fancy range input (fill + optional thumb) |
35
- | TwCheck | Styled checkbox/radio |
36
- | DropdownMenu | Popover menu |
37
- | ContextMenu | Right-click / long-press menu at the cursor (the DropdownMenu engine + trigger semantics) |
38
- | CommandMenu | Command palette (keyboard-driven) |
39
- | Pagination | Standalone pager: compact prev/next or windowed page numbers; DataTable-compatible |
40
- | Stepper | Numbered step indicator for wizard/checkout flows; optional click navigation |
41
- | TypeaheadInput | Autocomplete input |
42
- | ColorScheme | Dark/light mode management with persistence |
43
- | HoverExpandableWidth | Width-expanding container on hover |
29
+ | Component | Purpose |
30
+ | -------------------- | ----------------------------------------------------------------------------------------------- |
31
+ | Button | Actions with intent/variant/size |
32
+ | CopyButton | Click-to-copy Button with copied/error feedback; `copyToClipboard()` util for code |
33
+ | ButtonGroupRadio | Toggle group (single selection) |
34
+ | Switch | Boolean toggle |
35
+ | Slider | Fancy range input (fill + optional thumb) |
36
+ | RangeSlider | Dual-thumb Slider: `start` ≤ `end` on one track, no crossing, `minRange`, two hidden inputs |
37
+ | Rating | Star rating: radiogroup input (half steps, hover preview, form + validate) or read-only display |
38
+ | TwCheck | Styled checkbox/radio |
39
+ | DropdownMenu | Popover menu |
40
+ | ContextMenu | Right-click / long-press menu at the cursor (the DropdownMenu engine + trigger semantics) |
41
+ | CommandMenu | Command palette (keyboard-driven) |
42
+ | Pagination | Standalone pager: compact prev/next or windowed page numbers; DataTable-compatible |
43
+ | Stepper | Numbered step indicator for wizard/checkout flows; optional click navigation |
44
+ | TypeaheadInput | Autocomplete input |
45
+ | ColorScheme | Dark/light mode management with persistence |
46
+ | HoverExpandableWidth | Width-expanding container on hover |
44
47
 
45
48
  ### Feedback
46
49
 
@@ -152,7 +155,7 @@ Use `validate={false}` to bypass stuic's validation entirely.
152
155
 
153
156
  > **Why default-on?** Hidden-input field components (`FieldPhoneNumber`,
154
157
  > `FieldCountry`, `FieldObject`, `FieldAssets`, `FieldInputLocalized`,
155
- > `FieldKeyValues`, `FieldLikeButton`, `FieldDate`, `FieldDateRange`) _must_ be default-on because hidden
158
+ > `FieldKeyValues`, `FieldLikeButton`, `FieldDate`, `FieldDateRange`, `Rating`) _must_ be default-on because hidden
156
159
  > inputs are excluded from native browser constraint validation — without the
157
160
  > stuic action enforcing `required` in a `customValidator`, the attribute is a
158
161
  > silent no-op. Plain-input field components were harmonized to the same
@@ -1051,6 +1054,146 @@ Prefix: `--stuic-pagination-*`
1051
1054
 
1052
1055
  ---
1053
1056
 
1057
+ ## RangeSlider
1058
+
1059
+ The dual-thumb sibling of `Slider`: two values (`start` ≤ `end`) on one track, the fill spanning the selected range — price filters, "between" queries, min/max limits. Same construction: the root owns the pointer machinery (the nearest thumb jumps to a track press, grabbing a thumb drags it from the grab point, thumbs never cross — `minRange` apart when set; two stacked thumbs are told apart by the side that is pressed or, when pressed on, by the first move's direction), while two visually hidden native `<input type="range">` — one per thumb, each covering the track from its end up to the midpoint between the thumbs — provide per-thumb keyboard interaction, the slider roles for assistive tech (explore-by-touch lands on the slider of the side touched) and form participation (`nameStart` / `nameEnd`). The bound ends are two plain numbers (`bind:start` / `bind:end`, the `FieldDateRange` shape); non-finite / out-of-range / off-grid writes are normalized back into the bindings and a reversed pair is reordered.
1060
+
1061
+ ### Exports
1062
+
1063
+ | Export | Kind | Description |
1064
+ | -------------------------- | --------- | ----------------------------------------------------------------- |
1065
+ | `RangeSlider` | component | Main component |
1066
+ | `RangeSliderProps` | type | Props type |
1067
+ | `RangeSliderIntent` | type | Fill intent union |
1068
+ | `RangeSliderOrientation` | type | `"horizontal" \| "vertical"` |
1069
+ | `RangeSliderThumb` | type | `"start" \| "end"` |
1070
+ | `RangeSliderValue` | type | `{ start: number; end: number }` — callback and validator payload |
1071
+ | `RangeSliderRenderCtx` | type | Snippet context (one render per thumb) |
1072
+ | `createRangeSliderT` | function | Builds the `t` prop from a (partial) message catalog |
1073
+ | `RANGE_SLIDER_MESSAGES_EN` | constant | Built-in English catalog (also the fallback) |
1074
+ | `RANGE_SLIDER_MESSAGES_SK` | constant | Bundled Slovak catalog (opt-in) |
1075
+ | `RangeSliderMessageKey` | type | Message key union |
1076
+ | `RangeSliderMessages` | type | One locale's catalog |
1077
+
1078
+ ### Key Props
1079
+
1080
+ | Prop | Type | Default | Description |
1081
+ | -------------------------------------- | ------------------------------------------------------------ | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
1082
+ | `start` / `end` | `number` | `min` / `max` | Bindable ends — always finite, in range, on grid, ordered and `minRange` apart (writes are normalized back) |
1083
+ | `min` / `max` / `step` | `number` / `number` / `number \| "any"` | `0` / `100` / `1` | Bounds and snap increment (`"any"` = continuous) |
1084
+ | `minRange` | `number` | `0` | Minimum distance between the values; rounded up onto the step grid, capped at the span |
1085
+ | `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Vertical fills bottom-up |
1086
+ | `size` / `intent` | `"sm" \| "md" \| "lg"` / intent | `"md"` / — | Thickness preset; fill color |
1087
+ | `thumb` | `boolean \| Snippet<[RangeSliderRenderCtx]>` | `true` | `false` = fill-only (linear mapping); a snippet renders inside each thumb, `ctx.thumb` says which |
1088
+ | `fillRounded` / `ticks` / `valueLabel` | | | As on `Slider`; value labels render once per thumb (they overlap when the thumbs are close) |
1089
+ | `label` / `labelStart` / `labelEnd` | `string` | — / `t("minimum")` / `t("maximum")` | Group name (`role="group"`); accessible names of the two inputs |
1090
+ | `nameStart` / `nameEnd` | `string` | — | Form field names of the hidden inputs |
1091
+ | `oninput` / `onchange` | `(value: RangeSliderValue, thumb: RangeSliderThumb) => void` | — | Every change / every commit, with the pair and the thumb that moved (native-faithful: an unchanged commit fires nothing) |
1092
+ | `validate` / `setValidationResult` | | | Validate action on the start input, re-run on either thumb's commit; **`customValidator` receives the `{ start, end }` pair** |
1093
+ | `t` | `TranslateFn` | English | Default thumb names ("Minimum" / "Maximum") |
1094
+
1095
+ Class slots: `class`, `trackClass`, `fillClass`, `thumbClass`, `tickClass`, `valueClass`. Reserved root handlers: `onpointerdown/move/up/cancel`. Imperative `validate()` / `clearValidation()` / `getValidation()` / `focus()` / `scrollIntoView()` via `bind:this`. Root data attributes: `data-thumbs` (`"true"` / `"false"`), `data-dragging` / `data-ring` / `data-active-thumb` (each naming a thumb), plus `data-orientation`, `data-size`, `data-intent`, `data-disabled`, `data-fill-rounded`; thumbs, value labels and inputs carry `data-thumb="start|end"`, tick layers `data-layer="before|on-fill|after"`. The per-thumb ratios and their midpoint are written inline as `--_ratio-start` / `--_ratio-end` / `--_mid` (survive `unstyled`; the functional CSS — `touch-action`, the inputs' hit areas — hangs off `data-stuic-range-slider`).
1096
+
1097
+ ### CSS Tokens
1098
+
1099
+ Prefix: `--stuic-range-slider-*`
1100
+
1101
+ `track`, `fill`, `thumb`, `thumb-foreground`, `tick`, `tick-on-fill`, `tick-size`, `ring-width`, `ring-color`, `thickness` (`size` presets 1.25 / 2 / 3rem), `length`, `thumb-inset`, `radius`, `fill-radius`, `thumb-radius`, `thumb-shadow`, `value-gap`, `transition`
1102
+
1103
+ Deliberately its own token set, not derived from `--stuic-slider-*` (a theme restyles both explicitly). Falls back to shared `--stuic-shadow` and `--stuic-transition`.
1104
+
1105
+ ---
1106
+
1107
+ ## Rating
1108
+
1109
+ Star rating — input and display in one. Input mode is a `role="radiogroup"` of `role="radio"` buttons (one per symbol, two with `allowHalf`), each labeled "N of M stars": hover preview, roving tabindex + arrow/Home/End/Delete keys, click-the-selected-again-to-clear (`allowClear`), a hidden input for `name`/value and the `validate` action with `required` enforced (hidden inputs skip native constraint validation). `readonly` is the display variant: `role="img"` with the value announcement, any fraction rendered (4.2 → a 20% fifth star). Symbols are two stacked copies of an svg (empty muted below, filled clipped to the value on top), so any `currentColor` icon works; the filled color is a fixed star amber by design (theme-independent — monochrome themes grey out the warning intent), `intent` colors by theme instead.
1110
+
1111
+ ### Exports
1112
+
1113
+ | Export | Kind | Description |
1114
+ | -------------------- | --------- | ------------------------------------------------------ |
1115
+ | `Rating` | component | Main component |
1116
+ | `RatingProps` | type | Props type |
1117
+ | `RatingSize` | type | `"sm" \| "md" \| "lg"` |
1118
+ | `RatingItemState` | type | `"full" \| "partial" \| "empty"` (symbol `data-state`) |
1119
+ | `createRatingT` | function | Builds the `t` prop from a (partial) message catalog |
1120
+ | `RATING_MESSAGES_EN` | constant | Built-in English catalog (also the fallback) |
1121
+ | `RATING_MESSAGES_SK` | constant | Bundled Slovak catalog (opt-in) |
1122
+ | `RatingMessageKey` | type | Message key union |
1123
+ | `RatingMessages` | type | One locale's catalog |
1124
+
1125
+ `iconStar` / `iconStarFill` (Bootstrap) are on the icons barrel — the filled one is the default symbol, the outline one the usual `iconEmpty`.
1126
+
1127
+ ### Key Props
1128
+
1129
+ | Prop | Type | Default | Description |
1130
+ | ----------------------------------------------------- | ------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
1131
+ | `value` | `number` | `0` | Bindable; 0 = none. Rendered clamped to `[0, max]`, snapped to whole/half in input mode |
1132
+ | `max` | `number` | `5` | Number of symbols |
1133
+ | `allowHalf` | `boolean` | `false` | Half-symbol values (two hit zones per symbol, arrows step 0.5) |
1134
+ | `allowClear` | `boolean` | `true` | Re-click the selected symbol / Delete / Backspace → 0 |
1135
+ | `readonly` | `boolean` | `false` | Display variant (`role="img"`, fractional fill, no input) |
1136
+ | `intent` | `IntentColorKey` | — | Theme color of the filled symbols (unset = fixed amber `--stuic-rating-icon-color`) |
1137
+ | `icon` / `iconEmpty` | `string` | star | Raw svg/html of the filled / empty symbol (empty defaults to the same icon, muted) |
1138
+ | `name`, `required`, `validate`, `setValidationResult` | | | Form integration — same contract as the `Field*` components; imperative `validate()` / `clearValidation()` / `getValidation()` via `bind:this` |
1139
+ | `onchange` | `(value: number) => void` | — | User-driven value changes |
1140
+ | `t` | `TranslateFn` | English | i18n: group label, "{value} of {max} stars" announcements, required message |
1141
+
1142
+ Class slots: `class`, `classItem`, `classIcon`. Reserved root handlers: `onkeydown`, `onpointerleave`. The component writes `--stuic-rating-fill` (0–100%) inline on every symbol wrapper (survives `unstyled`).
1143
+
1144
+ ### CSS Tokens
1145
+
1146
+ Prefix: `--stuic-rating-*`
1147
+
1148
+ `size` (unset; `size` presets 1.125 / 1.5 / 2rem), `gap`, `icon-color`, `icon-color-empty`, `icon-scale-hover`, `ring-width`, `ring-color`, `radius`, `transition`, `opacity-disabled`
1149
+
1150
+ ---
1151
+
1152
+ ## SplitPane
1153
+
1154
+ Two panes with one draggable separator between them — `horizontal` puts them side by side and resizes the primary pane's width, `vertical` stacks them and resizes its height; the other pane flexes into the rest (`min-width/height: 0`, `overflow: auto`, so long content scrolls inside a pane). Nestable. Built on the `resizable` attachment with the separator as its `handle`, so the separator is an ARIA window splitter: focusable `role="separator"`, `aria-orientation`, `aria-valuenow` / `min` / `max` in `units`, `aria-controls` → the primary pane; arrows along the axis (Shift ×10), Home / End to the bounds, Enter (or double-click) resets; Pointer Events with capture for mouse / touch / pen. `bind:size` mirrors every resize and writing it resizes (clamped, persisted, announced); a size stored under `key` wins on mount and is written back; `reset()` (and Enter) restore the size the component mounted with. `disabled` keeps the layout and renders the separator inert.
1155
+
1156
+ ### Exports
1157
+
1158
+ | Export | Kind | Description |
1159
+ | ------------------------ | --------- | ---------------------------------------------------- |
1160
+ | `SplitPane` | component | Main component |
1161
+ | `SplitPaneProps` | type | Props type |
1162
+ | `SplitPaneOrientation` | type | `"horizontal" \| "vertical"` |
1163
+ | `SplitPanePrimary` | type | `"start" \| "end"` |
1164
+ | `createSplitPaneT` | function | Builds the `t` prop from a (partial) message catalog |
1165
+ | `SPLIT_PANE_MESSAGES_EN` | constant | Built-in English catalog (also the fallback) |
1166
+ | `SPLIT_PANE_MESSAGES_SK` | constant | Bundled Slovak catalog (opt-in) |
1167
+ | `SplitPaneMessageKey` | type | Message key union |
1168
+ | `SplitPaneMessages` | type | One locale's catalog |
1169
+
1170
+ ### Key Props
1171
+
1172
+ | Prop | Type | Default | Description |
1173
+ | ----------------- | ------------------------------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------- |
1174
+ | `start` / `end` | `Snippet` | — | The two panes' content (left / right, or top / bottom when vertical) |
1175
+ | `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Side by side (a width) or stacked (a height — the container then needs a definite height) |
1176
+ | `primary` | `"start" \| "end"` | `"start"` | Which pane carries the size; the other one fills the rest (`end` puts the separator on the second pane's start edge) |
1177
+ | `size` | `number` | `50` | Bindable size of the primary pane in `units`; a stored size wins on mount; writes are clamped and written back |
1178
+ | `units` | `"%" \| "px"` | `"%"` | Of the container, or px |
1179
+ | `min` / `max` | `number` | `0` / `0` | Bounds in `units` (`0` = none; `%` caps at 100) |
1180
+ | `step` | `number` | `1` (%) / `10` (px) | Keyboard step; Shift ×10 |
1181
+ | `key` / `storage` | `string \| number` / `"local" \| "session"` | — / `"session"` | Persist the size (`resizable-width-<key>` / `resizable-height-<key>`) |
1182
+ | `disabled` | `boolean` | `false` | Inert separator (no drag / keys / focus, no grip); the layout stays and `size` writes still land |
1183
+ | `label` | `string` | `t("resize")` | Accessible name of the separator |
1184
+ | `onResize` | `(info: ResizableInfo) => void` | — | Every applied size: mount, drag, keys, reset, `size` writes |
1185
+ | `t` | `TranslateFn` | English | The separator's default name ("Resize") |
1186
+
1187
+ Class slots: `class`, `startClass`, `endClass`, `separatorClass`. Imperative `reset()` via `bind:this`. Bindable `el` / `separatorEl`. Root data attributes: `data-orientation`, `data-disabled`; the panes carry `data-pane="start|end"` and the sized one `data-primary`; the separator `data-separator`, plus `data-resizing` while dragged. The size lands as an inline `width` / `height` on the primary pane (survives `unstyled`).
1188
+
1189
+ ### CSS Tokens
1190
+
1191
+ Prefix: `--stuic-split-pane-*`
1192
+
1193
+ `separator-color`, `separator-color-hover` (hover / focus / drag), `separator-thickness`, `separator-hit-area` (invisible grab zone on each side), `grip-color`, `grip-color-hover`, `grip-border-color`, `grip-length`, `grip-thickness`, `grip-radius`, `ring-color`, `transition` (falls back to `--stuic-transition`). The defaults reproduce the `resizable` attachment's built-in handle (a faint 1px line, a small rounded grip; `:root.dark` variants), palette-based rather than theme-based
1194
+
1195
+ ---
1196
+
1054
1197
  ## Stepper
1055
1198
 
1056
1199
  Numbered multi-step progress header for wizard/checkout flows. Accessible `<nav>` landmark with an ordered list: completed steps get a check mark and filled connector, the current step is highlighted (`aria-current="step"`), upcoming steps stay muted; per-step `error` renders a destructive "×". Display-only by default; `onSelect` makes steps clickable (completed-only by default, `clickable="all"` for free navigation). `current` is a zero-based index — exactly `@marianmeres/wizard`'s `step.index` (`steps.length` = all done).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/stuic",
3
- "version": "3.172.0",
3
+ "version": "3.173.0",
4
4
  "packageManager": "pnpm@11.5.0",
5
5
  "scripts": {
6
6
  "dev": "vite dev",
@@ -141,8 +141,8 @@
141
141
  "@codemirror/lang-markdown": "^6.5.2",
142
142
  "@codemirror/language": "^6.12.4",
143
143
  "@codemirror/language-data": "^6.5.2",
144
- "@codemirror/state": "^6.7.1",
145
- "@codemirror/view": "^6.43.9",
144
+ "@codemirror/state": "^6.7.2",
145
+ "@codemirror/view": "^6.43.10",
146
146
  "@eslint/js": "^9.39.5",
147
147
  "@marianmeres/random-human-readable": "^1.10.2",
148
148
  "@marianmeres/trend-chart": "^0.5.0",
@@ -177,7 +177,7 @@
177
177
  "tailwindcss": "^4.3.3",
178
178
  "tsx": "^4.23.13",
179
179
  "typescript": "^5.9.3",
180
- "typescript-eslint": "^8.68.0",
180
+ "typescript-eslint": "^8.69.0",
181
181
  "vite": "^7.3.6",
182
182
  "vitest": "^4.1.11",
183
183
  "vitest-browser-svelte": "^2.2.1"
@@ -187,7 +187,7 @@
187
187
  "@marianmeres/clog": "^3.21.0",
188
188
  "@marianmeres/countries": "^1.1.0",
189
189
  "@marianmeres/cron-parser": "^1.0.1",
190
- "@marianmeres/design-tokens": "^1.17.0",
190
+ "@marianmeres/design-tokens": "^1.18.0",
191
191
  "@marianmeres/icons-fns": "^6.0.0",
192
192
  "@marianmeres/item-collection": "^1.4.2",
193
193
  "@marianmeres/paging-store": "^2.1.1",