@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.
- package/AGENTS.md +19 -4
- package/API.md +56 -2
- package/README.md +21 -5
- package/dist/actions/resizable-width.svelte.d.ts +14 -28
- package/dist/actions/resizable-width.svelte.js +16 -171
- package/dist/attachments/index.d.ts +1 -0
- package/dist/attachments/index.js +1 -0
- package/dist/attachments/resizable.d.ts +113 -0
- package/dist/attachments/resizable.fixture.svelte +53 -0
- package/dist/attachments/resizable.fixture.svelte.d.ts +10 -0
- package/dist/attachments/resizable.js +295 -0
- package/dist/components/Button/README.md +11 -10
- package/dist/components/ButtonGroupRadio/README.md +52 -23
- package/dist/components/ButtonGroupRadio/index.css +6 -2
- package/dist/components/PricingTable/PricingTable.svelte +8 -14
- package/dist/components/PricingTable/README.md +29 -6
- package/dist/components/PricingTable/index.css +54 -0
- package/dist/components/RangeSlider/README.md +291 -0
- package/dist/components/RangeSlider/RangeSlider.svelte +763 -0
- package/dist/components/RangeSlider/RangeSlider.svelte.d.ts +130 -0
- package/dist/components/RangeSlider/i18n-sk.d.ts +17 -0
- package/dist/components/RangeSlider/i18n-sk.js +19 -0
- package/dist/components/RangeSlider/i18n.d.ts +33 -0
- package/dist/components/RangeSlider/i18n.js +41 -0
- package/dist/components/RangeSlider/index.css +430 -0
- package/dist/components/RangeSlider/index.d.ts +3 -0
- package/dist/components/RangeSlider/index.js +3 -0
- package/dist/components/Rating/README.md +206 -0
- package/dist/components/Rating/Rating.svelte +355 -0
- package/dist/components/Rating/Rating.svelte.d.ts +82 -0
- package/dist/components/Rating/i18n-sk.d.ts +17 -0
- package/dist/components/Rating/i18n-sk.js +21 -0
- package/dist/components/Rating/i18n.d.ts +34 -0
- package/dist/components/Rating/i18n.js +42 -0
- package/dist/components/Rating/index.css +170 -0
- package/dist/components/Rating/index.d.ts +3 -0
- package/dist/components/Rating/index.js +3 -0
- package/dist/components/SplitPane/README.md +169 -0
- package/dist/components/SplitPane/SplitPane.svelte +202 -0
- package/dist/components/SplitPane/SplitPane.svelte.d.ts +67 -0
- package/dist/components/SplitPane/i18n-sk.d.ts +17 -0
- package/dist/components/SplitPane/i18n-sk.js +18 -0
- package/dist/components/SplitPane/i18n.d.ts +31 -0
- package/dist/components/SplitPane/i18n.js +39 -0
- package/dist/components/SplitPane/index.css +153 -0
- package/dist/components/SplitPane/index.d.ts +3 -0
- package/dist/components/SplitPane/index.js +3 -0
- package/dist/components/WithSidePanel/README.md +19 -16
- package/dist/icons/index.d.ts +2 -0
- package/dist/icons/index.js +3 -0
- package/dist/index.css +17 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/docs/{maybe-todo.md → _archive/maybe-todo.md} +18 -7
- package/docs/architecture.md +1 -1
- package/docs/conventions.md +27 -7
- package/docs/domains/actions.md +18 -18
- package/docs/domains/attachments.md +72 -9
- package/docs/domains/components.md +175 -32
- package/docs/domains/theming.md +57 -13
- 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";
|