@marianmeres/stuic 3.172.0 → 3.174.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 (61) hide show
  1. package/AGENTS.md +19 -4
  2. package/API.md +56 -2
  3. package/README.md +21 -5
  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/Button/README.md +11 -10
  13. package/dist/components/ButtonGroupRadio/README.md +52 -23
  14. package/dist/components/ButtonGroupRadio/index.css +6 -2
  15. package/dist/components/PricingTable/PricingTable.svelte +8 -14
  16. package/dist/components/PricingTable/README.md +29 -6
  17. package/dist/components/PricingTable/index.css +54 -0
  18. package/dist/components/RangeSlider/README.md +291 -0
  19. package/dist/components/RangeSlider/RangeSlider.svelte +763 -0
  20. package/dist/components/RangeSlider/RangeSlider.svelte.d.ts +130 -0
  21. package/dist/components/RangeSlider/i18n-sk.d.ts +17 -0
  22. package/dist/components/RangeSlider/i18n-sk.js +19 -0
  23. package/dist/components/RangeSlider/i18n.d.ts +33 -0
  24. package/dist/components/RangeSlider/i18n.js +41 -0
  25. package/dist/components/RangeSlider/index.css +430 -0
  26. package/dist/components/RangeSlider/index.d.ts +3 -0
  27. package/dist/components/RangeSlider/index.js +3 -0
  28. package/dist/components/Rating/README.md +206 -0
  29. package/dist/components/Rating/Rating.svelte +355 -0
  30. package/dist/components/Rating/Rating.svelte.d.ts +82 -0
  31. package/dist/components/Rating/i18n-sk.d.ts +17 -0
  32. package/dist/components/Rating/i18n-sk.js +21 -0
  33. package/dist/components/Rating/i18n.d.ts +34 -0
  34. package/dist/components/Rating/i18n.js +42 -0
  35. package/dist/components/Rating/index.css +170 -0
  36. package/dist/components/Rating/index.d.ts +3 -0
  37. package/dist/components/Rating/index.js +3 -0
  38. package/dist/components/SplitPane/README.md +169 -0
  39. package/dist/components/SplitPane/SplitPane.svelte +202 -0
  40. package/dist/components/SplitPane/SplitPane.svelte.d.ts +67 -0
  41. package/dist/components/SplitPane/i18n-sk.d.ts +17 -0
  42. package/dist/components/SplitPane/i18n-sk.js +18 -0
  43. package/dist/components/SplitPane/i18n.d.ts +31 -0
  44. package/dist/components/SplitPane/i18n.js +39 -0
  45. package/dist/components/SplitPane/index.css +153 -0
  46. package/dist/components/SplitPane/index.d.ts +3 -0
  47. package/dist/components/SplitPane/index.js +3 -0
  48. package/dist/components/WithSidePanel/README.md +19 -16
  49. package/dist/icons/index.d.ts +2 -0
  50. package/dist/icons/index.js +3 -0
  51. package/dist/index.css +17 -0
  52. package/dist/index.d.ts +3 -0
  53. package/dist/index.js +3 -0
  54. package/docs/{maybe-todo.md → _archive/maybe-todo.md} +18 -7
  55. package/docs/architecture.md +1 -1
  56. package/docs/conventions.md +27 -7
  57. package/docs/domains/actions.md +18 -18
  58. package/docs/domains/attachments.md +72 -9
  59. package/docs/domains/components.md +175 -32
  60. package/docs/domains/theming.md +57 -13
  61. package/package.json +5 -5
@@ -0,0 +1,130 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ import type { ValidateOptions, ValidationResult } from "../../actions/validate.svelte.js";
4
+ import type { TranslateFn } from "../../types.js";
5
+ export type RangeSliderIntent = "primary" | "accent" | "success" | "warning" | "destructive";
6
+ export type RangeSliderOrientation = "horizontal" | "vertical";
7
+ /** One of the two thumbs: `start` carries the lower value, `end` the upper one. */
8
+ export type RangeSliderThumb = "start" | "end";
9
+ /** The value pair — payload of `oninput` / `onchange` and of `validate.customValidator`. */
10
+ export interface RangeSliderValue {
11
+ start: number;
12
+ end: number;
13
+ }
14
+ /** Context passed to the `thumb` and `valueLabel` snippets (rendered once per thumb). */
15
+ export interface RangeSliderRenderCtx {
16
+ /** The thumb this render is for */
17
+ thumb: RangeSliderThumb;
18
+ /** This thumb's value */
19
+ value: number;
20
+ /** Normalized position of this thumb, 0..1 */
21
+ ratio: number;
22
+ /** Normalized position of this thumb, 0..100 */
23
+ percent: number;
24
+ /** Whether this thumb is being dragged */
25
+ dragging: boolean;
26
+ /** Current lower value */
27
+ start: number;
28
+ /** Current upper value */
29
+ end: number;
30
+ }
31
+ export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children" | "oninput" | "onchange" | "onpointerdown" | "onpointermove" | "onpointerup" | "onpointercancel"> {
32
+ /**
33
+ * Lower value (bindable). Defaults to `min`. Non-finite, out-of-range and
34
+ * off-step-grid writes are normalized back into the binding, a reversed pair
35
+ * is reordered and `minRange` is enforced — so the bound values always equal
36
+ * what is rendered and submitted.
37
+ */
38
+ start?: number;
39
+ /** Upper value (bindable). Defaults to `max` (or the last step-grid point below it). */
40
+ end?: number;
41
+ min?: number;
42
+ max?: number;
43
+ /**
44
+ * Snap increment. Use "any" for continuous (no snapping). Non-positive
45
+ * numbers are treated as "any".
46
+ */
47
+ step?: number | "any";
48
+ /**
49
+ * Minimum distance between the two values — the thumbs can never get closer
50
+ * than this. Rounded up onto the step grid and capped at the reachable span;
51
+ * `0` (default) lets the thumbs coincide.
52
+ */
53
+ minRange?: number;
54
+ orientation?: RangeSliderOrientation;
55
+ /** Cross-axis thickness preset (main-axis length via `--stuic-range-slider-length` or class) */
56
+ size?: "sm" | "md" | "lg" | string;
57
+ /** Semantic color intent (colors the fill) */
58
+ intent?: RangeSliderIntent;
59
+ disabled?: boolean;
60
+ /** Accessible name of the whole control (the root is a labelled `group`) */
61
+ label?: string;
62
+ /** Accessible name of the start (lower) thumb. Defaults to `t("minimum")` — "Minimum". */
63
+ labelStart?: string;
64
+ /** Accessible name of the end (upper) thumb. Defaults to `t("maximum")` — "Maximum". */
65
+ labelEnd?: string;
66
+ /** Form field name of the hidden range input carrying `start` */
67
+ nameStart?: string;
68
+ /** Form field name of the hidden range input carrying `end` */
69
+ nameEnd?: string;
70
+ /** Skip all default styling, use only custom classes */
71
+ unstyled?: boolean;
72
+ class?: string;
73
+ /** Classes for the track (pill background) element */
74
+ trackClass?: string;
75
+ /** Classes for the fill (selected range) element */
76
+ fillClass?: string;
77
+ /** Classes for both thumb elements */
78
+ thumbClass?: string;
79
+ /** Classes for each tick mark element */
80
+ tickClass?: string;
81
+ /** Classes for both floating value label wrappers */
82
+ valueClass?: string;
83
+ /**
84
+ * Thumb rendering: `true` (default) renders two empty thumbs, `false` hides
85
+ * them (fill-only look), a snippet renders custom content inside each thumb
86
+ * (its context says which one).
87
+ */
88
+ thumb?: boolean | Snippet<[RangeSliderRenderCtx]>;
89
+ /**
90
+ * Round the fill's edges (instead of flat cuts), giving the "pill inside a
91
+ * pill" look.
92
+ */
93
+ fillRounded?: boolean;
94
+ /**
95
+ * Tick marks along the track: `true` renders one at every `step` (requires a
96
+ * positive numeric step; auto ticks are skipped above 101 — pass an explicit
97
+ * array instead), an array renders them at the given (in-range) values.
98
+ */
99
+ ticks?: boolean | number[];
100
+ /** Floating value label per thumb (tracks the thumb along the track, outside it) */
101
+ valueLabel?: Snippet<[RangeSliderRenderCtx]>;
102
+ /** Bindable root element reference */
103
+ el?: HTMLDivElement;
104
+ /** Bindable reference of the hidden range input carrying `start` */
105
+ inputStartEl?: HTMLInputElement;
106
+ /** Bindable reference of the hidden range input carrying `end` */
107
+ inputEndEl?: HTMLInputElement;
108
+ /** Fires on every actual value change (dragging, keyboard), with the thumb that moved */
109
+ oninput?: (value: RangeSliderValue, thumb: RangeSliderThumb) => void;
110
+ /** Fires when a changed value is committed (drag released, keyboard), with the thumb that moved */
111
+ onchange?: (value: RangeSliderValue, thumb: RangeSliderThumb) => void;
112
+ /**
113
+ * Validation (the stuic `validate` action, attached to the start input and
114
+ * re-run on every commit of either thumb). NOTE: `customValidator` receives
115
+ * the `{ start, end }` pair as its value, not a DOM string.
116
+ */
117
+ validate?: boolean | Omit<ValidateOptions, "setValidationResult">;
118
+ setValidationResult?: (res: ValidationResult) => void;
119
+ /** i18n of the default thumb names (see `createRangeSliderT`) */
120
+ t?: TranslateFn;
121
+ }
122
+ declare const RangeSlider: import("svelte").Component<Props, {
123
+ validate: () => ValidationResult | undefined;
124
+ clearValidation: () => void;
125
+ getValidation: () => ValidationResult | undefined;
126
+ focus: () => void;
127
+ scrollIntoView: (opts?: ScrollIntoViewOptions) => void;
128
+ }, "el" | "end" | "start" | "inputStartEl" | "inputEndEl">;
129
+ type RangeSlider = ReturnType<typeof RangeSlider>;
130
+ export default RangeSlider;
@@ -0,0 +1,17 @@
1
+ import type { RangeSliderMessages } from "./i18n.js";
2
+ /**
3
+ * Slovak message catalog for `RangeSlider`. Opt-in — English stays the built-in
4
+ * default, and this module is only pulled into a bundle when it is actually
5
+ * imported (the component itself never references it).
6
+ *
7
+ * @example
8
+ * ```svelte
9
+ * <script>
10
+ * import { RangeSlider, createRangeSliderT, RANGE_SLIDER_MESSAGES_SK } from "@marianmeres/stuic";
11
+ * const t = createRangeSliderT(RANGE_SLIDER_MESSAGES_SK);
12
+ * </script>
13
+ *
14
+ * <RangeSlider bind:start bind:end {t} />
15
+ * ```
16
+ */
17
+ export declare const RANGE_SLIDER_MESSAGES_SK: RangeSliderMessages;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Slovak message catalog for `RangeSlider`. Opt-in — English stays the built-in
3
+ * default, and this module is only pulled into a bundle when it is actually
4
+ * imported (the component itself never references it).
5
+ *
6
+ * @example
7
+ * ```svelte
8
+ * <script>
9
+ * import { RangeSlider, createRangeSliderT, RANGE_SLIDER_MESSAGES_SK } from "@marianmeres/stuic";
10
+ * const t = createRangeSliderT(RANGE_SLIDER_MESSAGES_SK);
11
+ * </script>
12
+ *
13
+ * <RangeSlider bind:start bind:end {t} />
14
+ * ```
15
+ */
16
+ export const RANGE_SLIDER_MESSAGES_SK = {
17
+ minimum: "Minimálna hodnota",
18
+ maximum: "Maximálna hodnota",
19
+ };
@@ -0,0 +1,33 @@
1
+ import type { TranslateFn } from "../../types.js";
2
+ /**
3
+ * The built-in (English) message catalog of `RangeSlider`. Also the fallback of
4
+ * every other bundled locale, so a locale missing a key still renders text.
5
+ */
6
+ export declare const RANGE_SLIDER_MESSAGES_EN: {
7
+ /** Default accessible name of the start (lower) thumb */
8
+ minimum: string;
9
+ /** Default accessible name of the end (upper) thumb */
10
+ maximum: string;
11
+ };
12
+ /** Every message key `RangeSlider` may look up. */
13
+ export type RangeSliderMessageKey = keyof typeof RANGE_SLIDER_MESSAGES_EN;
14
+ /** A (possibly partial) catalog for one locale. */
15
+ export type RangeSliderMessages = Record<RangeSliderMessageKey, string>;
16
+ /**
17
+ * Builds the `t` prop of `RangeSlider` from a message catalog. Unknown or
18
+ * untranslated keys fall back to `fallbackMessages` (English by default), so a
19
+ * catalog may safely be partial and never renders a raw key.
20
+ *
21
+ * @example
22
+ * ```svelte
23
+ * <script>
24
+ * import { RangeSlider, createRangeSliderT, RANGE_SLIDER_MESSAGES_SK } from "@marianmeres/stuic";
25
+ * const t = createRangeSliderT(RANGE_SLIDER_MESSAGES_SK);
26
+ * </script>
27
+ *
28
+ * <RangeSlider bind:start bind:end {t} />
29
+ * ```
30
+ */
31
+ export declare function createRangeSliderT(messages: Partial<RangeSliderMessages> | Record<string, string>, fallbackMessages?: Partial<RangeSliderMessages> | Record<string, string>): TranslateFn;
32
+ /** The component's built-in English `t`. */
33
+ export declare const t_default: TranslateFn;
@@ -0,0 +1,41 @@
1
+ import { isPlainObject } from "../../utils/is-plain-object.js";
2
+ import { replaceMap } from "../../utils/replace-map.js";
3
+ /**
4
+ * The built-in (English) message catalog of `RangeSlider`. Also the fallback of
5
+ * every other bundled locale, so a locale missing a key still renders text.
6
+ */
7
+ export const RANGE_SLIDER_MESSAGES_EN = {
8
+ /** Default accessible name of the start (lower) thumb */
9
+ minimum: "Minimum",
10
+ /** Default accessible name of the end (upper) thumb */
11
+ maximum: "Maximum",
12
+ };
13
+ /**
14
+ * Builds the `t` prop of `RangeSlider` from a message catalog. Unknown or
15
+ * untranslated keys fall back to `fallbackMessages` (English by default), so a
16
+ * catalog may safely be partial and never renders a raw key.
17
+ *
18
+ * @example
19
+ * ```svelte
20
+ * <script>
21
+ * import { RangeSlider, createRangeSliderT, RANGE_SLIDER_MESSAGES_SK } from "@marianmeres/stuic";
22
+ * const t = createRangeSliderT(RANGE_SLIDER_MESSAGES_SK);
23
+ * </script>
24
+ *
25
+ * <RangeSlider bind:start bind:end {t} />
26
+ * ```
27
+ */
28
+ export function createRangeSliderT(messages, fallbackMessages = RANGE_SLIDER_MESSAGES_EN) {
29
+ return (k, values = null, fallback = "") => {
30
+ const out = messages[k] ??
31
+ fallbackMessages[k] ??
32
+ (typeof fallback === "string" ? fallback : k);
33
+ return isPlainObject(values)
34
+ ? replaceMap(out, values, {
35
+ preSearchKeyTransform: (k) => `{${k}}`,
36
+ })
37
+ : out;
38
+ };
39
+ }
40
+ /** The component's built-in English `t`. */
41
+ export const t_default = createRangeSliderT(RANGE_SLIDER_MESSAGES_EN);
@@ -0,0 +1,430 @@
1
+ /* ============================================================================
2
+ RANGE SLIDER COMPONENT TOKENS
3
+ Override globally: :root { --stuic-range-slider-fill: var(--color-green-500); }
4
+ Override locally: <RangeSlider style="--stuic-range-slider-fill: var(--color-green-500);">
5
+
6
+ Sizing (structural, override at any scope — resolved at the element):
7
+ --stuic-range-slider-thickness cross-axis size (also set by size="sm|md|lg")
8
+ --stuic-range-slider-length main-axis size (or use a width/height class)
9
+ --stuic-range-slider-thumb-inset gap between thumb and track edge
10
+
11
+ Deliberately its own token set (not derived from --stuic-slider-*): a theme
12
+ that tunes one slider look does so explicitly for the other, and scoped
13
+ overrides never leak between the two components.
14
+ ============================================================================ */
15
+
16
+ :root {
17
+ /* Track (pill background) */
18
+ --stuic-range-slider-track: var(--stuic-color-muted);
19
+
20
+ /* Fill (the selected range) */
21
+ --stuic-range-slider-fill: var(--stuic-color-primary);
22
+
23
+ /* Thumbs */
24
+ --stuic-range-slider-thumb: var(--color-white);
25
+ --stuic-range-slider-thumb-foreground: var(--stuic-color-foreground);
26
+
27
+ /* Tick marks (a second, fill-clipped layer keeps them readable over the fill) */
28
+ --stuic-range-slider-tick: color-mix(
29
+ in srgb,
30
+ var(--stuic-color-foreground) 25%,
31
+ transparent
32
+ );
33
+ --stuic-range-slider-tick-on-fill: color-mix(
34
+ in srgb,
35
+ var(--stuic-color-background) 55%,
36
+ transparent
37
+ );
38
+
39
+ /* Focus ring (drawn around the focused thumb) */
40
+ --stuic-range-slider-ring-width: 4px;
41
+ --stuic-range-slider-ring-color: var(--stuic-color-ring);
42
+ }
43
+
44
+ @layer components {
45
+ /* ============================================================================
46
+ BASE / ROOT
47
+ ============================================================================ */
48
+
49
+ .stuic-range-slider {
50
+ /* Internal vars (intent may override the fill) */
51
+ --_track: var(--stuic-range-slider-track);
52
+ --_fill: var(--stuic-range-slider-fill);
53
+ --_thumb-bg: var(--stuic-range-slider-thumb);
54
+ --_thumb-fg: var(--stuic-range-slider-thumb-foreground);
55
+ --_tick: var(--stuic-range-slider-tick);
56
+ --_tick-on-fill: var(--stuic-range-slider-tick-on-fill);
57
+ --_tick-size: var(--stuic-range-slider-tick-size, 4px);
58
+
59
+ /* Structural (fallbacks resolved here so scoped overrides work) */
60
+ --_thickness: var(--stuic-range-slider-thickness, 2rem);
61
+ --_length: var(--stuic-range-slider-length, 10rem);
62
+ --_thumb-inset: var(--stuic-range-slider-thumb-inset, 3px);
63
+ /* Main-axis offset + extent of the fill (percentages resolve against the
64
+ track, which is the containing block of both the fill and the tick layers) */
65
+ --_fill-start: calc(var(--_ratio-start) * 100%);
66
+ --_fill-len: calc((var(--_ratio-end) - var(--_ratio-start)) * 100%);
67
+
68
+ display: inline-block;
69
+ vertical-align: middle;
70
+ flex-shrink: 0;
71
+ cursor: pointer;
72
+ }
73
+
74
+ /* FUNCTIONAL, not cosmetic — keyed on an attribute the component always emits
75
+ so it survives `unstyled`: `touch-action: none` keeps the browser from
76
+ stealing touch drags for scrolling, `position: relative` anchors the
77
+ absolutely-positioned track/thumbs/hidden inputs, and the hidden inputs are
78
+ laid out over their halves of the track. Attribute specificity (0,1,0) leaves
79
+ it overridable by a consumer utility class (Tailwind's utilities layer beats
80
+ this components layer). */
81
+ [data-stuic-range-slider] {
82
+ position: relative;
83
+ touch-action: none;
84
+ user-select: none;
85
+ -webkit-user-select: none;
86
+ -webkit-tap-highlight-color: transparent;
87
+ /* Where the two hidden inputs split the track: the midpoint between the
88
+ thumbs, in the same coordinates the fill uses (linear without thumbs) */
89
+ --_split: calc(var(--_mid) * 100%);
90
+ }
91
+ [data-stuic-range-slider][data-thumbs="true"] {
92
+ --_split: calc(
93
+ var(--_thickness, 0px) / 2 + (100% - var(--_thickness, 0px)) * var(--_mid)
94
+ );
95
+ }
96
+ /* Visually hidden, full-thickness, always the topmost hit targets (z-index above
97
+ the raised active thumb) so touch-based assistive tech finds a real slider
98
+ role anywhere on the track. */
99
+ [data-stuic-range-slider] > input {
100
+ position: absolute;
101
+ z-index: 2;
102
+ margin: 0;
103
+ opacity: 0;
104
+ cursor: inherit;
105
+ }
106
+ [data-stuic-range-slider][data-orientation="horizontal"] > input {
107
+ inset-block: 0;
108
+ }
109
+ [data-stuic-range-slider][data-orientation="horizontal"] > input[data-thumb="start"] {
110
+ inset-inline-start: 0;
111
+ width: var(--_split);
112
+ }
113
+ [data-stuic-range-slider][data-orientation="horizontal"] > input[data-thumb="end"] {
114
+ inset-inline-end: 0;
115
+ width: calc(100% - var(--_split));
116
+ }
117
+ [data-stuic-range-slider][data-orientation="vertical"] > input {
118
+ inset-inline: 0;
119
+ }
120
+ [data-stuic-range-slider][data-orientation="vertical"] > input[data-thumb="start"] {
121
+ bottom: 0;
122
+ height: var(--_split);
123
+ }
124
+ [data-stuic-range-slider][data-orientation="vertical"] > input[data-thumb="end"] {
125
+ top: 0;
126
+ height: calc(100% - var(--_split));
127
+ }
128
+
129
+ .stuic-range-slider[data-size="sm"] {
130
+ --_thickness: var(--stuic-range-slider-thickness, 1.25rem);
131
+ }
132
+ /* md is the base default (2rem) */
133
+ .stuic-range-slider[data-size="lg"] {
134
+ --_thickness: var(--stuic-range-slider-thickness, 3rem);
135
+ }
136
+
137
+ .stuic-range-slider[data-orientation="horizontal"] {
138
+ width: var(--_length);
139
+ height: var(--_thickness);
140
+ }
141
+ .stuic-range-slider[data-orientation="vertical"] {
142
+ width: var(--_thickness);
143
+ height: var(--_length);
144
+ }
145
+
146
+ /* Disabled */
147
+ .stuic-range-slider[data-disabled="true"] {
148
+ cursor: not-allowed;
149
+ opacity: 0.6;
150
+ }
151
+
152
+ /* Per-thumb parts (thumbs, value labels) pick their own ratio, so the
153
+ positioning rules below are written once. */
154
+ .stuic-range-slider > [data-thumb="start"] {
155
+ --_ratio: var(--_ratio-start);
156
+ }
157
+ .stuic-range-slider > [data-thumb="end"] {
158
+ --_ratio: var(--_ratio-end);
159
+ }
160
+
161
+ /* ============================================================================
162
+ TRACK + FILL
163
+ ============================================================================ */
164
+
165
+ .stuic-range-slider > .track {
166
+ position: absolute;
167
+ inset: 0;
168
+ overflow: hidden;
169
+ border-radius: var(--stuic-range-slider-radius, 9999px);
170
+ background: var(--_track);
171
+ transition: box-shadow var(--stuic-range-slider-transition, var(--stuic-transition));
172
+ }
173
+
174
+ .stuic-range-slider .fill {
175
+ position: absolute;
176
+ background: var(--_fill);
177
+ }
178
+ /* With thumbs, the fill spans the thumb-center travel between the two values
179
+ plus one thumb: it reaches the outer edge of both thumbs and, when they
180
+ coincide, is exactly one thickness — a thumb-sized nub it can never shrink
181
+ below. No thumbs falls back to the plain linear mapping above, which
182
+ collapses to zero. */
183
+ .stuic-range-slider[data-thumbs="true"] {
184
+ --_fill-start: calc(var(--_ratio-start) * (100% - var(--_thickness)));
185
+ --_fill-len: calc(
186
+ (var(--_ratio-end) - var(--_ratio-start)) * (100% - var(--_thickness)) +
187
+ var(--_thickness)
188
+ );
189
+ }
190
+ .stuic-range-slider[data-orientation="horizontal"] .fill {
191
+ inset-block: 0;
192
+ inset-inline-start: var(--_fill-start);
193
+ width: var(--_fill-len);
194
+ }
195
+ .stuic-range-slider[data-orientation="vertical"] .fill {
196
+ inset-inline: 0;
197
+ bottom: var(--_fill-start);
198
+ height: var(--_fill-len);
199
+ }
200
+
201
+ /* Rounded edges ("pill inside a pill") */
202
+ .stuic-range-slider[data-fill-rounded="true"] .fill {
203
+ border-radius: var(
204
+ --stuic-range-slider-fill-radius,
205
+ var(--stuic-range-slider-radius, 9999px)
206
+ );
207
+ }
208
+
209
+ /* ============================================================================
210
+ THUMBS
211
+ ============================================================================ */
212
+
213
+ .stuic-range-slider > .thumb {
214
+ position: absolute;
215
+ display: flex;
216
+ align-items: center;
217
+ justify-content: center;
218
+ width: calc(var(--_thickness) - 2 * var(--_thumb-inset));
219
+ height: calc(var(--_thickness) - 2 * var(--_thumb-inset));
220
+ border-radius: var(--stuic-range-slider-thumb-radius, 9999px);
221
+ background: var(--_thumb-bg);
222
+ color: var(--_thumb-fg);
223
+ box-shadow: var(--stuic-range-slider-thumb-shadow, var(--stuic-shadow));
224
+ /* No pointer-events rules needed: the hidden inputs are always the topmost
225
+ hit targets, so a thumb never receives pointer events. Grab detection is
226
+ geometric, in _onpointerdown. */
227
+ }
228
+ .stuic-range-slider[data-orientation="horizontal"] > .thumb {
229
+ inset-block-start: var(--_thumb-inset);
230
+ inset-inline-start: calc(
231
+ var(--_thumb-inset) + (100% - var(--_thickness)) * var(--_ratio)
232
+ );
233
+ }
234
+ .stuic-range-slider[data-orientation="vertical"] > .thumb {
235
+ inset-inline-start: var(--_thumb-inset);
236
+ bottom: calc(var(--_thumb-inset) + (100% - var(--_thickness)) * var(--_ratio));
237
+ }
238
+ /* The thumb last dragged or focused renders above the other one (they overlap
239
+ when the values coincide) */
240
+ .stuic-range-slider[data-active-thumb="start"] > .thumb[data-thumb="start"],
241
+ .stuic-range-slider[data-active-thumb="end"] > .thumb[data-thumb="end"] {
242
+ z-index: 1;
243
+ }
244
+ /* Smooth value movement (keyboard steps); instant while dragging */
245
+ .stuic-range-slider .fill,
246
+ .stuic-range-slider > .thumb,
247
+ .stuic-range-slider > .value {
248
+ transition:
249
+ width var(--stuic-range-slider-transition, var(--stuic-transition)),
250
+ height var(--stuic-range-slider-transition, var(--stuic-transition)),
251
+ inset-inline-start var(--stuic-range-slider-transition, var(--stuic-transition)),
252
+ bottom var(--stuic-range-slider-transition, var(--stuic-transition));
253
+ }
254
+ .stuic-range-slider[data-dragging] .fill,
255
+ .stuic-range-slider[data-dragging] > .thumb,
256
+ .stuic-range-slider[data-dragging] > .value {
257
+ transition: none;
258
+ }
259
+
260
+ /* ============================================================================
261
+ TICKS
262
+ ============================================================================ */
263
+
264
+ /* Three identical tick layers clipped to complementary parts of the track: the
265
+ `before` and `after` ones cover the unfilled ends, the `on-fill` one the
266
+ selected range, each with its own color. A single layer would vanish into
267
+ whichever it overlaps, and overlapping layers would composite (both tick
268
+ colors are translucent). */
269
+ .stuic-range-slider .ticks {
270
+ position: absolute;
271
+ inset: 0;
272
+ transition: clip-path var(--stuic-range-slider-transition, var(--stuic-transition));
273
+ }
274
+ .stuic-range-slider[data-dragging] .ticks {
275
+ transition: none;
276
+ }
277
+ .stuic-range-slider .ticks[data-layer="on-fill"] .tick {
278
+ background: var(--_tick-on-fill);
279
+ }
280
+ .stuic-range-slider[data-orientation="horizontal"] .ticks[data-layer="before"] {
281
+ clip-path: inset(0 calc(100% - var(--_fill-start)) 0 0);
282
+ }
283
+ .stuic-range-slider[data-orientation="horizontal"] .ticks[data-layer="on-fill"] {
284
+ clip-path: inset(
285
+ 0 calc(100% - var(--_fill-start) - var(--_fill-len)) 0 var(--_fill-start)
286
+ );
287
+ }
288
+ .stuic-range-slider[data-orientation="horizontal"] .ticks[data-layer="after"] {
289
+ clip-path: inset(0 0 0 calc(var(--_fill-start) + var(--_fill-len)));
290
+ }
291
+ .stuic-range-slider[data-orientation="horizontal"]:dir(rtl)
292
+ .ticks[data-layer="before"] {
293
+ clip-path: inset(0 0 0 calc(100% - var(--_fill-start)));
294
+ }
295
+ .stuic-range-slider[data-orientation="horizontal"]:dir(rtl)
296
+ .ticks[data-layer="on-fill"] {
297
+ clip-path: inset(
298
+ 0 var(--_fill-start) 0 calc(100% - var(--_fill-start) - var(--_fill-len))
299
+ );
300
+ }
301
+ .stuic-range-slider[data-orientation="horizontal"]:dir(rtl) .ticks[data-layer="after"] {
302
+ clip-path: inset(0 calc(var(--_fill-start) + var(--_fill-len)) 0 0);
303
+ }
304
+ .stuic-range-slider[data-orientation="vertical"] .ticks[data-layer="before"] {
305
+ clip-path: inset(calc(100% - var(--_fill-start)) 0 0 0);
306
+ }
307
+ .stuic-range-slider[data-orientation="vertical"] .ticks[data-layer="on-fill"] {
308
+ clip-path: inset(
309
+ calc(100% - var(--_fill-start) - var(--_fill-len)) 0 var(--_fill-start) 0
310
+ );
311
+ }
312
+ .stuic-range-slider[data-orientation="vertical"] .ticks[data-layer="after"] {
313
+ clip-path: inset(0 0 calc(var(--_fill-start) + var(--_fill-len)) 0);
314
+ }
315
+
316
+ .stuic-range-slider .tick {
317
+ position: absolute;
318
+ width: var(--_tick-size);
319
+ height: var(--_tick-size);
320
+ border-radius: 9999px;
321
+ background: var(--_tick);
322
+ }
323
+ /* Without thumbs there is no thickness inset to hide behind, so the travel is
324
+ inset by half a tick — otherwise the ticks at ratio 0 and 1 sit centered on
325
+ the track edges and the overflow:hidden track halves them. */
326
+ .stuic-range-slider[data-orientation="horizontal"] .tick {
327
+ top: 50%;
328
+ inset-inline-start: calc(
329
+ var(--_tick-size) / 2 + (100% - var(--_tick-size)) * var(--_tick-ratio)
330
+ );
331
+ transform: translate(-50%, -50%);
332
+ }
333
+ .stuic-range-slider[data-orientation="vertical"] .tick {
334
+ left: 50%;
335
+ bottom: calc(var(--_tick-size) / 2 + (100% - var(--_tick-size)) * var(--_tick-ratio));
336
+ transform: translate(-50%, 50%);
337
+ }
338
+ /* With thumbs, ticks align to the thumb-center travel */
339
+ .stuic-range-slider[data-thumbs="true"][data-orientation="horizontal"] .tick {
340
+ inset-inline-start: calc(
341
+ var(--_thickness) / 2 + (100% - var(--_thickness)) * var(--_tick-ratio)
342
+ );
343
+ }
344
+ .stuic-range-slider[data-thumbs="true"][data-orientation="vertical"] .tick {
345
+ bottom: calc(var(--_thickness) / 2 + (100% - var(--_thickness)) * var(--_tick-ratio));
346
+ }
347
+ /* Logical-property positioning + physical translate: flip centering in RTL */
348
+ .stuic-range-slider[data-orientation="horizontal"]:dir(rtl) .tick {
349
+ transform: translate(50%, -50%);
350
+ }
351
+
352
+ /* ============================================================================
353
+ VALUE LABELS (one per thumb)
354
+ ============================================================================ */
355
+
356
+ .stuic-range-slider > .value {
357
+ position: absolute;
358
+ pointer-events: none;
359
+ white-space: nowrap;
360
+ font-size: var(--text-xs);
361
+ color: var(--stuic-color-muted-foreground);
362
+ }
363
+ .stuic-range-slider[data-orientation="horizontal"] > .value {
364
+ bottom: calc(100% + var(--stuic-range-slider-value-gap, 0.375rem));
365
+ inset-inline-start: calc(var(--_ratio) * 100%);
366
+ transform: translateX(-50%);
367
+ }
368
+ .stuic-range-slider[data-orientation="vertical"] > .value {
369
+ inset-inline-start: calc(100% + var(--stuic-range-slider-value-gap, 0.375rem));
370
+ bottom: calc(var(--_ratio) * 100%);
371
+ transform: translateY(50%);
372
+ }
373
+ .stuic-range-slider[data-thumbs="true"][data-orientation="horizontal"] > .value {
374
+ inset-inline-start: calc(
375
+ var(--_thickness) / 2 + (100% - var(--_thickness)) * var(--_ratio)
376
+ );
377
+ }
378
+ .stuic-range-slider[data-thumbs="true"][data-orientation="vertical"] > .value {
379
+ bottom: calc(var(--_thickness) / 2 + (100% - var(--_thickness)) * var(--_ratio));
380
+ }
381
+ .stuic-range-slider[data-orientation="horizontal"]:dir(rtl) > .value {
382
+ transform: translateX(50%);
383
+ }
384
+
385
+ /* ============================================================================
386
+ FOCUS RING (keyboard-initiated focus; data-ring names the focused thumb —
387
+ :focus-visible is unreliable for pointer-initiated programmatic focus)
388
+ ============================================================================ */
389
+
390
+ .stuic-range-slider[data-ring="start"] > .thumb[data-thumb="start"],
391
+ .stuic-range-slider[data-ring="end"] > .thumb[data-thumb="end"] {
392
+ box-shadow:
393
+ var(--stuic-range-slider-thumb-shadow, var(--stuic-shadow)),
394
+ 0 0 0 var(--stuic-range-slider-ring-width)
395
+ color-mix(
396
+ in srgb,
397
+ var(--stuic-range-slider-ring-color) 30%,
398
+ var(--stuic-color-background)
399
+ );
400
+ }
401
+ /* No thumbs to ring: ring the track instead */
402
+ .stuic-range-slider[data-thumbs="false"][data-ring] > .track {
403
+ box-shadow: 0 0 0 var(--stuic-range-slider-ring-width)
404
+ color-mix(
405
+ in srgb,
406
+ var(--stuic-range-slider-ring-color) 30%,
407
+ var(--stuic-color-background)
408
+ );
409
+ }
410
+
411
+ /* ============================================================================
412
+ INTENT COLOR MAPPING
413
+ ============================================================================ */
414
+
415
+ .stuic-range-slider[data-intent="primary"] {
416
+ --_fill: var(--stuic-color-primary);
417
+ }
418
+ .stuic-range-slider[data-intent="accent"] {
419
+ --_fill: var(--stuic-color-accent);
420
+ }
421
+ .stuic-range-slider[data-intent="success"] {
422
+ --_fill: var(--stuic-color-success);
423
+ }
424
+ .stuic-range-slider[data-intent="warning"] {
425
+ --_fill: var(--stuic-color-warning);
426
+ }
427
+ .stuic-range-slider[data-intent="destructive"] {
428
+ --_fill: var(--stuic-color-destructive);
429
+ }
430
+ }
@@ -0,0 +1,3 @@
1
+ export { default as RangeSlider, type Props as RangeSliderProps, type RangeSliderIntent, type RangeSliderOrientation, type RangeSliderThumb, type RangeSliderValue, type RangeSliderRenderCtx, } from "./RangeSlider.svelte";
2
+ export { createRangeSliderT, RANGE_SLIDER_MESSAGES_EN, type RangeSliderMessageKey, type RangeSliderMessages, } from "./i18n.js";
3
+ export { RANGE_SLIDER_MESSAGES_SK } from "./i18n-sk.js";
@@ -0,0 +1,3 @@
1
+ export { default as RangeSlider, } from "./RangeSlider.svelte";
2
+ export { createRangeSliderT, RANGE_SLIDER_MESSAGES_EN, } from "./i18n.js";
3
+ export { RANGE_SLIDER_MESSAGES_SK } from "./i18n-sk.js";