@marianmeres/stuic 3.143.0 → 3.145.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 +5 -2
- package/API.md +1 -0
- package/README.md +24 -1
- package/dist/components/CommentInput/CommentInput.svelte +1 -0
- package/dist/components/CommentInput/CommentInput.svelte.d.ts +1 -0
- package/dist/components/CommentInput/README.md +11 -1
- package/dist/components/CommentInput/index.css +13 -0
- package/dist/components/Slider/README.md +205 -0
- package/dist/components/Slider/Slider.svelte +521 -0
- package/dist/components/Slider/Slider.svelte.d.ts +104 -0
- package/dist/components/Slider/index.css +337 -0
- package/dist/components/Slider/index.d.ts +1 -0
- package/dist/components/Slider/index.js +1 -0
- package/dist/index.css +4 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +6 -1
- package/docs/architecture.md +23 -1
- package/docs/domains/components.md +2 -1
- package/package.json +9 -13
package/AGENTS.md
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
```
|
|
25
25
|
src/lib/
|
|
26
|
-
├── components/ #
|
|
26
|
+
├── components/ # 63 component directories
|
|
27
27
|
├── actions/ # 15 Svelte actions (use: directives)
|
|
28
28
|
├── attachments/ # Svelte attachments ({@attach} — preferred for new DOM helpers)
|
|
29
29
|
├── utils/ # 44 utility modules
|
|
@@ -53,6 +53,9 @@ Theme CSS files are not bundled in this package — they're provided by `@marian
|
|
|
53
53
|
5. Create components without `unstyled`, `class`, `el` props
|
|
54
54
|
6. Use `dark:` Tailwind prefix when CSS vars handle dark mode
|
|
55
55
|
7. Import CSS inside components — centralize in `src/lib/index.css`
|
|
56
|
+
(**exception:** subpath-export components with optional peer deps — `MarkdownEditor`,
|
|
57
|
+
`CommentInput` — import their own `index.css` locally so their styles don't ship to
|
|
58
|
+
barrel-only consumers. Enforced by `src/lib/barrel-optional-peers.test.ts`.)
|
|
56
59
|
8. Declare component tokens at `:root` that reference shared structural tokens (use fallback pattern instead)
|
|
57
60
|
|
|
58
61
|
### CSS Variable Pattern
|
|
@@ -125,7 +128,7 @@ Global tokens that control cross-component visual properties. Defined in `src/li
|
|
|
125
128
|
|
|
126
129
|
### Domain Docs
|
|
127
130
|
|
|
128
|
-
- [Components](./docs/domains/components.md) —
|
|
131
|
+
- [Components](./docs/domains/components.md) — 63 component directories, Props pattern, snippets
|
|
129
132
|
- [Theming](./docs/domains/theming.md) — CSS tokens, dark mode, themes
|
|
130
133
|
- [Actions](./docs/domains/actions.md) — 15 Svelte directives
|
|
131
134
|
- [Attachments](./docs/domains/attachments.md) — `{@attach}` DOM helpers (preferred for new ones)
|
package/API.md
CHANGED
|
@@ -2178,6 +2178,7 @@ Each component defines customization tokens. Override globally in `:root {}` or
|
|
|
2178
2178
|
| ------------------ | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
2179
2179
|
| Button | `--stuic-button-*` | `bg`, `text`, `border`, `ring-color`, `radius`, `padding-x-{size}` |
|
|
2180
2180
|
| Switch | `--stuic-switch-*` | `accent` |
|
|
2181
|
+
| Slider | `--stuic-slider-*` | `track`, `fill`, `thumb`, `tick`, `tick-on-fill`, `thickness`, `length`, `radius`, `fill-radius` |
|
|
2181
2182
|
| Input | `--stuic-input-*` | `accent`, `accent-error` |
|
|
2182
2183
|
| Progress | `--stuic-progress-*` | `bg`, `accent` |
|
|
2183
2184
|
| ListItemButton | `--stuic-list-item-button-*` | `bg`, `text`, `border`, `bg-hover`, `text-hover` |
|
package/README.md
CHANGED
|
@@ -27,6 +27,29 @@ npm install @marianmeres/stuic
|
|
|
27
27
|
</Modal>
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
## Subpath exports
|
|
31
|
+
|
|
32
|
+
Most of the library is on the main entry. A few things live behind subpaths:
|
|
33
|
+
|
|
34
|
+
| Subpath | Contents |
|
|
35
|
+
| ------------------------------------- | ------------------------------------------------------------- |
|
|
36
|
+
| `@marianmeres/stuic` | Components, actions, icons, utils — everything below excepted |
|
|
37
|
+
| `@marianmeres/stuic/utils` | Utilities only, without pulling in components |
|
|
38
|
+
| `@marianmeres/stuic/phone-validation` | Phone validation helpers |
|
|
39
|
+
| `@marianmeres/stuic/markdown-editor` | `MarkdownEditor` — requires optional peer deps |
|
|
40
|
+
| `@marianmeres/stuic/comment-input` | `CommentInput` — requires optional peer deps |
|
|
41
|
+
|
|
42
|
+
The last two are **not** on the main entry by design: they depend on Milkdown and
|
|
43
|
+
CodeMirror, which are declared as _optional_ peer dependencies. Keeping them off the
|
|
44
|
+
barrel means consumers who don't use them never have to install that stack — and,
|
|
45
|
+
more importantly, their builds don't fail for want of it.
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { CommentInput } from "@marianmeres/stuic/comment-input";
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
See each component's README for the peer set to install.
|
|
52
|
+
|
|
30
53
|
## Theming System
|
|
31
54
|
|
|
32
55
|
STUIC uses a 4-layer CSS variable token system:
|
|
@@ -152,7 +175,7 @@ FieldInput, FieldMoney, FieldTextarea, FieldSelect, FieldCheckbox, FieldRadios,
|
|
|
152
175
|
|
|
153
176
|
### Buttons & Controls
|
|
154
177
|
|
|
155
|
-
Button, ButtonGroupRadio, Switch, TwCheck, ListItemButton, X
|
|
178
|
+
Button, ButtonGroupRadio, Switch, Slider, TwCheck, ListItemButton, X
|
|
156
179
|
|
|
157
180
|
### Feedback & Notifications
|
|
158
181
|
|
|
@@ -110,6 +110,7 @@ export interface Props extends InputWrapClassProps {
|
|
|
110
110
|
classFooter?: string;
|
|
111
111
|
style?: string;
|
|
112
112
|
}
|
|
113
|
+
import "./index.css";
|
|
113
114
|
declare const CommentInput: import("svelte").Component<Props, {
|
|
114
115
|
validate: () => ValidationResult | undefined;
|
|
115
116
|
clearValidation: () => void;
|
|
@@ -12,9 +12,14 @@ lightweight, opinionated wrapper for the common "leave a comment / reply" use
|
|
|
12
12
|
case: a clean minimal toolbar, an avatar, a submit button, and
|
|
13
13
|
⌘/Ctrl+Enter-to-send — without you having to wire any of it up.
|
|
14
14
|
|
|
15
|
+
Because it embeds `MarkdownEditor`, it is shipped as an **optional subpath export**
|
|
16
|
+
(`@marianmeres/stuic/comment-input`), separate from the main barrel — so the heavy
|
|
17
|
+
editor dependencies never reach consumers who don't use it. Importing it from
|
|
18
|
+
`@marianmeres/stuic` will not work.
|
|
19
|
+
|
|
15
20
|
```svelte
|
|
16
21
|
<script>
|
|
17
|
-
import { CommentInput } from "@marianmeres/stuic";
|
|
22
|
+
import { CommentInput } from "@marianmeres/stuic/comment-input";
|
|
18
23
|
|
|
19
24
|
let value = $state("");
|
|
20
25
|
|
|
@@ -49,6 +54,11 @@ This is the same peer set as `MarkdownEditor` — see its README for the
|
|
|
49
54
|
authoritative list. If they aren't installed, the surface stays empty and a
|
|
50
55
|
console error explains why.
|
|
51
56
|
|
|
57
|
+
> **CSS is imported locally** by this component, not via the central
|
|
58
|
+
> `src/lib/index.css`. Deliberate deviation from the usual STUIC convention,
|
|
59
|
+
> required so these styles ship only to subpath users. Same as `MarkdownEditor`;
|
|
60
|
+
> see `index.css` for the rationale.
|
|
61
|
+
|
|
52
62
|
## Toolbar
|
|
53
63
|
|
|
54
64
|
`toolbar` accepts `true` (the default), `false` (hidden), or an ordered
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
/******************************************************************************
|
|
2
2
|
CommentInput
|
|
3
3
|
|
|
4
|
+
NOTE — intentional deviation from the usual STUIC CSS convention.
|
|
5
|
+
Every other component's `index.css` is `@import`-ed into the central
|
|
6
|
+
`src/lib/index.css`. This component is NOT: because it embeds `MarkdownEditor`
|
|
7
|
+
it is an OPTIONAL subpath export (`@marianmeres/stuic/comment-input`) kept off
|
|
8
|
+
the main barrel, so the heavy Milkdown/CodeMirror peers never reach consumers
|
|
9
|
+
who don't use it. Centralizing its CSS would ship these styles to every
|
|
10
|
+
consumer anyway. Instead `CommentInput.svelte` imports THIS file locally; the
|
|
11
|
+
package's `sideEffects` glob (all css files) keeps the import for real users.
|
|
12
|
+
|
|
13
|
+
`MarkdownEditor/index.css` arrives on its own — `MarkdownEditor.svelte`
|
|
14
|
+
imports it locally too — so do NOT `@import` it here (it would double-inject
|
|
15
|
+
for anyone using both components).
|
|
16
|
+
|
|
4
17
|
A GitHub-style comment composer built on top of `MarkdownEditor` (a rich
|
|
5
18
|
WYSIWYG / source surface) with an avatar gutter and a submit/cancel footer.
|
|
6
19
|
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# Slider
|
|
2
|
+
|
|
3
|
+
A fancy `input[type="range"]` wrap — a pill-shaped track that fills with the value, with
|
|
4
|
+
an optional icon-capable thumb riding the fill edge (think iOS volume control). Supports
|
|
5
|
+
horizontal and vertical orientation, pointer dragging with step snapping, native keyboard
|
|
6
|
+
interaction, tick marks, a floating value label, form participation, and validation.
|
|
7
|
+
|
|
8
|
+
Not a replacement for `FieldInput type="range"` — this is the special-case "fancy"
|
|
9
|
+
variant for custom UI (volume/brightness controls, dashboards, media players).
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Default | Description |
|
|
14
|
+
| --------------- | ------------------------------------------------------------------ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
15
|
+
| `value` | `number` | `min` | Current value (bindable; non-finite / out-of-range / off-grid writes are normalized back) |
|
|
16
|
+
| `min` | `number` | `0` | Minimum value |
|
|
17
|
+
| `max` | `number` | `100` | Maximum value |
|
|
18
|
+
| `step` | `number \| "any"` | `1` | Snap increment (`"any"` or non-positive = continuous) |
|
|
19
|
+
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Slider direction (vertical fills bottom-up) |
|
|
20
|
+
| `size` | `"sm" \| "md" \| "lg" \| string` | `"md"` | Cross-axis thickness preset |
|
|
21
|
+
| `intent` | `"primary" \| "accent" \| "success" \| "warning" \| "destructive"` | - | Semantic fill color |
|
|
22
|
+
| `thumb` | `boolean \| Snippet<[SliderRenderCtx]>` | `true` | `false` hides the thumb (fill-only look), snippet renders inside thumb |
|
|
23
|
+
| `thumbPosition` | `"value" \| "start"` | `"value"` | `"value"` rides the fill edge; `"start"` pins it to the left/bottom so only the bar moves (true iOS volume look) |
|
|
24
|
+
| `fillRounded` | `boolean` | `false` | Round the fill's leading edge ("pill inside a pill") |
|
|
25
|
+
| `ticks` | `boolean \| number[]` | - | `true` = tick at every `step` (positive numeric step only; skipped above 101 auto ticks — pass an array), array = ticks at given in-range values |
|
|
26
|
+
| `valueLabel` | `Snippet<[SliderRenderCtx]>` | - | Floating label at the current value along the track |
|
|
27
|
+
| `disabled` | `boolean` | `false` | Disable interaction |
|
|
28
|
+
| `label` | `string` | - | Screen reader label for the underlying input |
|
|
29
|
+
| `id` | `string` | - | Id for the underlying input (enables `<label for>` association) |
|
|
30
|
+
| `name` | `string` | - | Form field name for the hidden range input |
|
|
31
|
+
| `required` | `boolean` | `false` | Forwarded to the input; per HTML spec inert on range inputs (use `validate.customValidator` for custom rules) |
|
|
32
|
+
| `oninput` | `(value: number) => void` | - | Fires on every value change (drag, keyboard) |
|
|
33
|
+
| `onchange` | `(value: number) => void` | - | Fires when a change is committed (drag release, keyboard) |
|
|
34
|
+
| `validate` | `boolean \| ValidateOptions` | - | Enable validation (stuic validate action) |
|
|
35
|
+
| `unstyled` | `boolean` | `false` | Skip all default styling |
|
|
36
|
+
| `class` | `string` | - | Classes for the root element |
|
|
37
|
+
| `trackClass` | `string` | - | Classes for the track (pill background) |
|
|
38
|
+
| `fillClass` | `string` | - | Classes for the fill (value indicator) |
|
|
39
|
+
| `thumbClass` | `string` | - | Classes for the thumb |
|
|
40
|
+
| `tickClass` | `string` | - | Classes for each tick mark |
|
|
41
|
+
| `valueClass` | `string` | - | Classes for the value label wrapper |
|
|
42
|
+
| `el` | `HTMLDivElement` | - | Root element reference (bindable) |
|
|
43
|
+
| `inputEl` | `HTMLInputElement` | - | Hidden range input reference (bindable) |
|
|
44
|
+
|
|
45
|
+
`SliderRenderCtx` (passed to the `thumb` and `valueLabel` snippets):
|
|
46
|
+
`{ value: number; ratio: number /* 0..1 */; percent: number /* 0..100 */; dragging: boolean }`
|
|
47
|
+
|
|
48
|
+
Remaining props are spread onto the root `<div>`.
|
|
49
|
+
|
|
50
|
+
### Exported methods (via component instance binding)
|
|
51
|
+
|
|
52
|
+
| Method | Description |
|
|
53
|
+
| ----------------------- | ----------------------------------- |
|
|
54
|
+
| `validate()` | Trigger validation now |
|
|
55
|
+
| `clearValidation()` | Clear the current validation result |
|
|
56
|
+
| `getValidation()` | Read the current validation result |
|
|
57
|
+
| `focus()` | Focus the underlying range input |
|
|
58
|
+
| `scrollIntoView(opts?)` | Scroll the slider into view |
|
|
59
|
+
|
|
60
|
+
## Usage
|
|
61
|
+
|
|
62
|
+
### Basic
|
|
63
|
+
|
|
64
|
+
```svelte
|
|
65
|
+
<script lang="ts">
|
|
66
|
+
import { Slider } from "@marianmeres/stuic";
|
|
67
|
+
|
|
68
|
+
let volume = $state(30);
|
|
69
|
+
</script>
|
|
70
|
+
|
|
71
|
+
<Slider bind:value={volume} />
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Vertical volume control (thumb rides the fill edge)
|
|
75
|
+
|
|
76
|
+
```svelte
|
|
77
|
+
<Slider
|
|
78
|
+
orientation="vertical"
|
|
79
|
+
size="lg"
|
|
80
|
+
bind:value={volume}
|
|
81
|
+
style="--stuic-slider-length: 10rem; --stuic-slider-fill: white; --stuic-slider-track: rgb(255 255 255 / .25);"
|
|
82
|
+
>
|
|
83
|
+
{#snippet thumb()}
|
|
84
|
+
{@html iconVolume({ size: 16 })}
|
|
85
|
+
{/snippet}
|
|
86
|
+
</Slider>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Fill-only (no thumb)
|
|
90
|
+
|
|
91
|
+
```svelte
|
|
92
|
+
<Slider thumb={false} bind:value={brightness} />
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### True iOS volume: fixed icon at the start, only the bar moves
|
|
96
|
+
|
|
97
|
+
```svelte
|
|
98
|
+
<!-- bare icon (no knob): drop the thumb's own background/shadow -->
|
|
99
|
+
<Slider
|
|
100
|
+
orientation="vertical"
|
|
101
|
+
thumbPosition="start"
|
|
102
|
+
fillRounded
|
|
103
|
+
thumbClass="bg-transparent shadow-none"
|
|
104
|
+
bind:value={volume}
|
|
105
|
+
>
|
|
106
|
+
{#snippet thumb()}
|
|
107
|
+
{@html iconVolume({ size: 18 })}
|
|
108
|
+
{/snippet}
|
|
109
|
+
</Slider>
|
|
110
|
+
|
|
111
|
+
<!-- or keep the white knob pinned at the bottom -->
|
|
112
|
+
<Slider orientation="vertical" thumbPosition="start" bind:value={volume}>
|
|
113
|
+
{#snippet thumb()}
|
|
114
|
+
{@html iconVolume({ size: 16 })}
|
|
115
|
+
{/snippet}
|
|
116
|
+
</Slider>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
With `thumbPosition="start"` the thumb is decorative (`pointer-events: none`) and the
|
|
120
|
+
value maps linearly across the full track — there is no thumb-travel inset.
|
|
121
|
+
|
|
122
|
+
### Steps and ticks
|
|
123
|
+
|
|
124
|
+
```svelte
|
|
125
|
+
<Slider min={0} max={10} step={1} ticks bind:value={rating} />
|
|
126
|
+
<Slider min={0} max={100} step={0.5} ticks={[0, 25, 50, 75, 100]} />
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Value label
|
|
130
|
+
|
|
131
|
+
```svelte
|
|
132
|
+
<Slider bind:value={percent}>
|
|
133
|
+
{#snippet valueLabel({ value })}
|
|
134
|
+
{value}%
|
|
135
|
+
{/snippet}
|
|
136
|
+
</Slider>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Continuous (no snapping)
|
|
140
|
+
|
|
141
|
+
```svelte
|
|
142
|
+
<Slider step="any" bind:value={gain} />
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### In a form
|
|
146
|
+
|
|
147
|
+
```svelte
|
|
148
|
+
<form onsubmit={...}>
|
|
149
|
+
<Slider name="volume" bind:value={volume} />
|
|
150
|
+
</form>
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Interaction
|
|
154
|
+
|
|
155
|
+
- **Pointer**: press anywhere on the track to jump the value there, then drag. Grabbing
|
|
156
|
+
the thumb itself does not jump (drag continues from the grab point).
|
|
157
|
+
- **Keyboard**: focus and use Arrow keys / PageUp / PageDown / Home / End — native
|
|
158
|
+
`input[type=range]` behavior (the real input is visually hidden but focusable).
|
|
159
|
+
- **Vertical**: bottom is `min`, top is `max`; ArrowUp increases.
|
|
160
|
+
- **RTL**: horizontal sliders flip automatically (logical CSS properties + pointer math).
|
|
161
|
+
- **Commit semantics**: `oninput` fires only on actual value changes; `onchange` only
|
|
162
|
+
when a drag/keypress committed a _different_ value (native-faithful — a no-move tap
|
|
163
|
+
fires neither).
|
|
164
|
+
- **Off-grid max**: when `max` is not on the step grid (e.g. `min=0 max=95 step=10`),
|
|
165
|
+
the largest reachable value is the last grid point (`90`), matching native range
|
|
166
|
+
sanitization.
|
|
167
|
+
|
|
168
|
+
## Caveats
|
|
169
|
+
|
|
170
|
+
- **Cross-axis sizing**: size the thickness via `size` presets or
|
|
171
|
+
`--stuic-slider-thickness` — not via `h-*`/`w-*` utility classes. The pointer math
|
|
172
|
+
and the CSS thumb positioning both derive from the thickness; a utility class
|
|
173
|
+
resizes the box without updating `--_thickness`, misaligning them. (Main-axis
|
|
174
|
+
length via a class — e.g. `class="h-40"` on a vertical slider — is fine.)
|
|
175
|
+
- **Root pointer handlers are reserved**: `onpointerdown/move/up/cancel` are excluded
|
|
176
|
+
from `Props` (the drag machinery owns them). Wrap the slider if you need them.
|
|
177
|
+
- **Touch**: the slider claims the whole touch gesture (`touch-action: none`) —
|
|
178
|
+
a touch starting on it adjusts the value and never scrolls the page.
|
|
179
|
+
- **The wrapper is not the control**: props spread onto the root `<div>` — including
|
|
180
|
+
`aria-*` and `onfocus`/`onblur` — never reach the underlying `<input type="range">`
|
|
181
|
+
and are inert for assistive tech. Use `label` (→ `aria-label`) and `id` (→ `<label for>`);
|
|
182
|
+
for anything else, bind `inputEl` and wire it imperatively.
|
|
183
|
+
|
|
184
|
+
## CSS Variables
|
|
185
|
+
|
|
186
|
+
| Variable | Default | Description |
|
|
187
|
+
| --------------------------------- | -------------------------- | --------------------------------- |
|
|
188
|
+
| `--stuic-slider-track` | `--stuic-color-muted` | Track (pill background) color |
|
|
189
|
+
| `--stuic-slider-fill` | `--stuic-color-primary` | Fill color |
|
|
190
|
+
| `--stuic-slider-thumb` | `--color-white` | Thumb background |
|
|
191
|
+
| `--stuic-slider-thumb-foreground` | `--stuic-color-foreground` | Thumb content color |
|
|
192
|
+
| `--stuic-slider-tick` | foreground 25% mix | Tick mark color (over the track) |
|
|
193
|
+
| `--stuic-slider-tick-on-fill` | background 55% mix | Tick mark color (over the fill) |
|
|
194
|
+
| `--stuic-slider-ring-width` | `4px` | Focus ring width |
|
|
195
|
+
| `--stuic-slider-ring-color` | `--stuic-color-ring` | Focus ring color |
|
|
196
|
+
| `--stuic-slider-thickness` | `2rem` (`sm` 1.25, `lg` 3) | Cross-axis size |
|
|
197
|
+
| `--stuic-slider-length` | `10rem` | Main-axis size |
|
|
198
|
+
| `--stuic-slider-thumb-inset` | `3px` | Gap between thumb and track edge |
|
|
199
|
+
| `--stuic-slider-radius` | `9999px` | Track corner radius |
|
|
200
|
+
| `--stuic-slider-fill-radius` | `--stuic-slider-radius` | Fill radius (when `fillRounded`) |
|
|
201
|
+
| `--stuic-slider-thumb-radius` | `9999px` | Thumb corner radius |
|
|
202
|
+
| `--stuic-slider-thumb-shadow` | `--stuic-shadow` | Thumb shadow |
|
|
203
|
+
| `--stuic-slider-tick-size` | `4px` | Tick mark diameter |
|
|
204
|
+
| `--stuic-slider-value-gap` | `0.375rem` | Gap between track and value label |
|
|
205
|
+
| `--stuic-slider-transition` | `--stuic-transition` | Fill/thumb movement transition |
|