noph-ui 0.34.4 → 0.35.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 (44) hide show
  1. package/dist/badge/Badge.svelte +4 -2
  2. package/dist/badge/Badge.svelte.d.ts +1 -0
  3. package/dist/button/Button.svelte +3 -5
  4. package/dist/button/IconButton.svelte +7 -7
  5. package/dist/button/SegmentedButton.svelte +22 -25
  6. package/dist/card/Card.svelte +11 -35
  7. package/dist/checkbox/Checkbox.svelte +68 -32
  8. package/dist/checkbox/types.d.ts +1 -0
  9. package/dist/chip/ChipSet.svelte +10 -2
  10. package/dist/chip/FilterChip.svelte +48 -52
  11. package/dist/chip/InputChip.svelte +32 -31
  12. package/dist/chip/InputChip.svelte.d.ts +1 -1
  13. package/dist/chip/types.d.ts +3 -1
  14. package/dist/dialog/Dialog.svelte +53 -10
  15. package/dist/index.d.ts +1 -0
  16. package/dist/index.js +1 -0
  17. package/dist/keyboard-nav.js +12 -4
  18. package/dist/list/Item.svelte +39 -66
  19. package/dist/menu/Menu.svelte +10 -1
  20. package/dist/menu/MenuItem.svelte +4 -4
  21. package/dist/navigation-drawer/NavigationDrawer.svelte +35 -1
  22. package/dist/navigation-rail/NavigationRailItem.svelte +2 -2
  23. package/dist/progress/CircularProgress.svelte +214 -117
  24. package/dist/progress/LinearProgress.svelte +382 -102
  25. package/dist/progress/types.d.ts +2 -0
  26. package/dist/ripple/Ripple.svelte +6 -7
  27. package/dist/select/Select.svelte +41 -20
  28. package/dist/slider/Slider.svelte +555 -0
  29. package/dist/slider/Slider.svelte.d.ts +4 -0
  30. package/dist/slider/index.d.ts +1 -0
  31. package/dist/slider/index.js +1 -0
  32. package/dist/slider/types.d.ts +23 -0
  33. package/dist/slider/types.js +1 -0
  34. package/dist/snackbar/Snackbar.svelte +21 -10
  35. package/dist/switch/Switch.svelte +1 -1
  36. package/dist/tabs/Tab.svelte +18 -16
  37. package/dist/tabs/Tabs.svelte +4 -2
  38. package/dist/tabs/types.d.ts +2 -0
  39. package/dist/text-field/TextField.svelte +2 -0
  40. package/dist/themes/defaultTheme.css +3 -9
  41. package/dist/tooltip/Tooltip.svelte +53 -10
  42. package/dist/types.d.ts +1 -0
  43. package/dist/types.js +1 -0
  44. package/package.json +11 -11
@@ -64,6 +64,7 @@
64
64
  let clientWidth = $state(0)
65
65
  let menuOpen = $state(false)
66
66
  let focusIndex = $state(-1)
67
+ let pendingFocus = false
67
68
  let typeBuffer = ''
68
69
  let lastTypeTime = 0
69
70
 
@@ -105,7 +106,11 @@
105
106
  return cachedRowHeight
106
107
  }
107
108
  const scrollOptionIntoView = (index: number) => {
108
- if (!useVirtualList || !menuElement) return
109
+ if (index < 0 || !menuElement) return
110
+ if (!useVirtualList) {
111
+ document.getElementById(`${uid}-opt-${index}`)?.scrollIntoView({ block: 'nearest' })
112
+ return
113
+ }
109
114
  const viewport = menuElement.querySelector('svelte-virtual-list-viewport') as HTMLElement | null
110
115
  if (!viewport) return
111
116
  const rowHeight = ensureRowHeight()
@@ -116,6 +121,20 @@
116
121
  else if (bottom > scrollTop + clientHeight) viewport.scrollTop = bottom - clientHeight
117
122
  }
118
123
 
124
+ const defaultActiveIndex = () => {
125
+ let idx = -1
126
+ if (multiple) {
127
+ if (Array.isArray(value) && value.length) {
128
+ idx = options.findIndex((o) => value.includes(o.value) && !o.disabled)
129
+ }
130
+ } else {
131
+ idx = options.findIndex((o) => o.value === value && !o.disabled)
132
+ }
133
+ if (idx < 0) idx = options.findIndex((o) => !o.disabled)
134
+ if (idx < 0) idx = 0
135
+ return idx
136
+ }
137
+
119
138
  const toggleValue = (option: SelectOption) => {
120
139
  if (multiple) {
121
140
  let arr = Array.isArray(value) ? [...value] : []
@@ -139,13 +158,22 @@
139
158
  selectElement?.dispatchEvent(new Event('change', { bubbles: true }))
140
159
  }
141
160
 
161
+ const focusActiveOption = () => {
162
+ const el = document.getElementById(`${uid}-opt-${focusIndex}`)
163
+ if (el) {
164
+ el.focus()
165
+ pendingFocus = false
166
+ } else {
167
+ scrollOptionIntoView(focusIndex)
168
+ }
169
+ }
170
+
142
171
  const openMenuAndFocus = async (index: number) => {
143
172
  if (!menuOpen) menuElement?.showPopover()
144
173
  focusIndex = Math.min(Math.max(index, 0), options.length - 1)
174
+ pendingFocus = true
145
175
  await tick()
146
- const el = document.getElementById(`${uid}-opt-${focusIndex}`)
147
- if (el) el.focus()
148
- else scrollOptionIntoView(focusIndex)
176
+ focusActiveOption()
149
177
  }
150
178
 
151
179
  const moveFocus = (delta: number) => {
@@ -512,21 +540,12 @@
512
540
  bind:open={menuOpen}
513
541
  ontoggle={async ({ newState }) => {
514
542
  if (newState === 'open') {
515
- let idx = -1
516
- if (multiple) {
517
- if (Array.isArray(value) && value.length) {
518
- idx = options.findIndex((o) => value.includes(o.value) && !o.disabled)
519
- }
520
- } else {
521
- idx = options.findIndex((o) => o.value === value && !o.disabled)
522
- }
523
- if (idx < 0) {
524
- idx = options.findIndex((o) => !o.disabled)
525
- }
526
- if (idx < 0) idx = 0
527
- focusIndex = idx
543
+ if (focusIndex < 0) focusIndex = defaultActiveIndex()
544
+ await tick()
545
+ scrollOptionIntoView(focusIndex)
528
546
  } else {
529
547
  focusIndex = -1
548
+ pendingFocus = false
530
549
  }
531
550
  }}
532
551
  bind:element={menuElement}
@@ -537,10 +556,12 @@
537
556
  itemHeight={48}
538
557
  items={options}
539
558
  rendered={({ start, end }) => {
540
- if (focusIndex >= start && focusIndex < end) {
559
+ if (pendingFocus && focusIndex >= start && focusIndex < end) {
541
560
  const el = document.getElementById(`${uid}-opt-${focusIndex}`)
542
- if (el) el.focus()
543
- else scrollOptionIntoView(focusIndex)
561
+ if (el) {
562
+ el.focus()
563
+ pendingFocus = false
564
+ }
544
565
  }
545
566
  }}
546
567
  >
@@ -0,0 +1,555 @@
1
+ <script lang="ts">
2
+ import type { SliderProps } from './types.ts'
3
+
4
+ let {
5
+ value = $bindable(0),
6
+ endValue = $bindable(),
7
+ min = 0,
8
+ max = 100,
9
+ step = 0,
10
+ size = 'xs',
11
+ orientation = 'horizontal',
12
+ range = false,
13
+ centered = false,
14
+ labeled = false,
15
+ ticks = false,
16
+ disabled = false,
17
+ format,
18
+ icon,
19
+ endInputAttributes,
20
+ style,
21
+ element = $bindable(),
22
+ inputElement = $bindable(),
23
+ endInputElement = $bindable(),
24
+ ...attributes
25
+ }: SliderProps = $props()
26
+
27
+ const clamp = (v: number, lo: number, hi: number) => Math.min(hi, Math.max(lo, v))
28
+
29
+ let span = $derived(max - min || 1)
30
+
31
+ const quantize = (v: number) => {
32
+ const c = clamp(v, min, max)
33
+ if (!(step > 0)) return c
34
+ return clamp(Number((min + Math.round((c - min) / step) * step).toFixed(10)), min, max)
35
+ }
36
+
37
+ let lo = $derived(clamp(value ?? min, min, max))
38
+ let hi = $derived(clamp(endValue ?? max, min, max))
39
+ let f1 = $derived(((range ? Math.min(lo, hi) : lo) - min) / span)
40
+ let f2 = $derived(((range ? Math.max(lo, hi) : lo) - min) / span)
41
+
42
+ const positionOf = (f: number) =>
43
+ step > 0 && f > 0 && f < 1
44
+ ? `calc(var(--_corner) + ${f} * (100% - 2 * var(--_corner)))`
45
+ : `${f * 100}%`
46
+
47
+ let p1 = $derived(positionOf(f1))
48
+ let p2 = $derived(positionOf(f2))
49
+
50
+ let edges = $derived.by(() => {
51
+ const gap = 'var(--_gap)'
52
+ if (range) {
53
+ return {
54
+ a1: `calc(${p1} + ${gap})`,
55
+ a2: `calc(${p2} - ${gap})`,
56
+ i1: `calc(${p1} - ${gap})`,
57
+ i2: `calc(${p2} + ${gap})`,
58
+ }
59
+ }
60
+ if (centered) {
61
+ return {
62
+ a1: `min(calc(${p1} + ${gap}), 50%)`,
63
+ a2: `max(calc(${p1} - ${gap}), 50%)`,
64
+ i1: `calc(min(${p1}, 50%) - ${gap})`,
65
+ i2: `calc(max(${p1}, 50%) + ${gap})`,
66
+ }
67
+ }
68
+ return { a1: '0%', a2: `calc(${p1} - ${gap})`, i1: '0%', i2: `calc(${p1} + ${gap})` }
69
+ })
70
+
71
+ let hostStyle = $derived(
72
+ `--_p1:${p1};--_p2:${p2};--_a1:${edges.a1};--_a2:${edges.a2};--_i1:${edges.i1};--_i2:${edges.i2};` +
73
+ (style ?? ''),
74
+ )
75
+
76
+ let hasStartStop = $derived(centered || range)
77
+
78
+ let tickList = $derived.by(() => {
79
+ if (!ticks || !(step > 0)) return []
80
+ const count = Math.floor(span / step)
81
+ if (count < 1 || count > 250) return []
82
+ return Array.from({ length: count + 1 }, (_, i) => Math.min(1, (i * step) / span))
83
+ })
84
+
85
+ let visibleTicks = $derived(
86
+ tickList
87
+ .map((t, i) => ({ t, i }))
88
+ .filter(({ t, i }) => {
89
+ if (i === tickList.length - 1) return false
90
+ if (hasStartStop && i === 0) return false
91
+ if (Math.abs(t - f1) < 1e-9) return false
92
+ if (range && Math.abs(t - f2) < 1e-9) return false
93
+ return true
94
+ }),
95
+ )
96
+
97
+ let activeFrom = $derived(centered ? Math.min(f1, 0.5) : range ? f1 : 0)
98
+ let activeTo = $derived(centered ? Math.max(f1, 0.5) : f2)
99
+ const isActive = (t: number) => t >= activeFrom && t <= activeTo
100
+
101
+ let dragging = $state<'start' | 'end' | null>(null)
102
+ let trackElement = $state<HTMLDivElement>()
103
+ let pointerFocused = $state(false)
104
+
105
+ const fractionFromEvent = (e: PointerEvent) => {
106
+ if (!trackElement) return 0
107
+ const r = trackElement.getBoundingClientRect()
108
+ if (orientation === 'vertical') {
109
+ return r.height ? (r.bottom - e.clientY) / r.height : 0
110
+ }
111
+ const rtl = getComputedStyle(trackElement).direction === 'rtl'
112
+ const offset = rtl ? r.right - e.clientX : e.clientX - r.left
113
+ return r.width ? offset / r.width : 0
114
+ }
115
+
116
+ const commit = (input: HTMLInputElement | undefined, v: number) => {
117
+ if (!input) return
118
+ const next = quantize(v)
119
+ if (Number(input.value) === next) return
120
+ input.value = String(next)
121
+ input.dispatchEvent(new Event('input', { bubbles: true }))
122
+ }
123
+
124
+ const moveTo = (e: PointerEvent) => {
125
+ if (!dragging) return
126
+ const v = min + clamp(fractionFromEvent(e), 0, 1) * span
127
+ if (dragging === 'start') {
128
+ commit(inputElement, range ? Math.min(quantize(v), hi) : v)
129
+ } else {
130
+ commit(endInputElement, Math.max(quantize(v), lo))
131
+ }
132
+ }
133
+
134
+ const onpointerdown = (e: PointerEvent) => {
135
+ if (disabled || e.button !== 0) return
136
+ const v = min + clamp(fractionFromEvent(e), 0, 1) * span
137
+ dragging = !range
138
+ ? 'start'
139
+ : lo === hi
140
+ ? v < lo
141
+ ? 'start'
142
+ : 'end'
143
+ : Math.abs(v - lo) <= Math.abs(v - hi)
144
+ ? 'start'
145
+ : 'end'
146
+ element?.setPointerCapture(e.pointerId)
147
+ e.preventDefault()
148
+ ;(dragging === 'start' ? inputElement : endInputElement)?.focus()
149
+ pointerFocused = true
150
+ moveTo(e)
151
+ }
152
+
153
+ const endDrag = (e: PointerEvent) => {
154
+ if (!dragging) return
155
+ const input = dragging === 'start' ? inputElement : endInputElement
156
+ dragging = null
157
+ element?.releasePointerCapture(e.pointerId)
158
+ input?.dispatchEvent(new Event('change', { bubbles: true }))
159
+ }
160
+
161
+ const labelFor = (v: number) => format?.(v) ?? String(v)
162
+ </script>
163
+
164
+ <div
165
+ bind:this={element}
166
+ style={hostStyle}
167
+ class={[
168
+ 'np-slider',
169
+ `np-size-${size}`,
170
+ orientation === 'vertical' && 'np-vertical',
171
+ range && 'np-range',
172
+ centered && 'np-centered',
173
+ labeled && 'np-labeled',
174
+ disabled && 'np-disabled',
175
+ dragging && 'np-dragging',
176
+ pointerFocused && 'np-pointer-focused',
177
+ attributes.class,
178
+ ]}
179
+ role="presentation"
180
+ {onpointerdown}
181
+ onpointermove={moveTo}
182
+ onpointerup={endDrag}
183
+ onpointercancel={endDrag}
184
+ onkeydown={() => (pointerFocused = false)}
185
+ onfocusout={() => (pointerFocused = false)}
186
+ >
187
+ <div class="np-slider-track" bind:this={trackElement}>
188
+ {#if hasStartStop}
189
+ <div class="np-slider-lane np-slider-inactive-start">
190
+ <span class="np-slider-stop np-slider-stop-start"></span>
191
+ </div>
192
+ {/if}
193
+ <div class="np-slider-lane np-slider-active">
194
+ {#if icon}
195
+ <span class="np-slider-icon">{@render icon()}</span>
196
+ {/if}
197
+ </div>
198
+ <div class="np-slider-lane np-slider-inactive-end">
199
+ <span class="np-slider-stop np-slider-stop-end"></span>
200
+ </div>
201
+
202
+ {#each visibleTicks as { t, i } (i)}
203
+ <span class={['np-slider-tick', isActive(t) && 'np-on-active']} style="--_t:{t}"></span>
204
+ {/each}
205
+
206
+ <div class="np-slider-handle np-slider-handle-start">
207
+ {#if labeled}
208
+ <div class="np-slider-label-anchor">
209
+ <div class="np-slider-label">{labelFor(range ? Math.min(lo, hi) : lo)}</div>
210
+ </div>
211
+ {/if}
212
+ </div>
213
+ {#if range}
214
+ <div class="np-slider-handle np-slider-handle-end">
215
+ {#if labeled}
216
+ <div class="np-slider-label-anchor">
217
+ <div class="np-slider-label">{labelFor(Math.max(lo, hi))}</div>
218
+ </div>
219
+ {/if}
220
+ </div>
221
+ {/if}
222
+ </div>
223
+
224
+ <input
225
+ {...attributes}
226
+ bind:this={inputElement}
227
+ bind:value
228
+ type="range"
229
+ class="np-slider-input np-slider-input-start"
230
+ {min}
231
+ max={range ? Math.max(min, hi) : max}
232
+ step={step > 0 ? step : 'any'}
233
+ {disabled}
234
+ aria-orientation={orientation}
235
+ aria-valuetext={format ? labelFor(lo) : attributes['aria-valuetext']}
236
+ />
237
+ {#if range}
238
+ <input
239
+ {...endInputAttributes}
240
+ bind:this={endInputElement}
241
+ bind:value={endValue}
242
+ type="range"
243
+ class="np-slider-input np-slider-input-end"
244
+ min={Math.min(max, lo)}
245
+ {max}
246
+ step={step > 0 ? step : 'any'}
247
+ {disabled}
248
+ aria-orientation={orientation}
249
+ aria-valuetext={format ? labelFor(hi) : endInputAttributes?.['aria-valuetext']}
250
+ />
251
+ {/if}
252
+ </div>
253
+
254
+ <style>
255
+ .np-slider {
256
+ --_size-track-height: 1rem;
257
+ --_size-handle-height: 2.75rem;
258
+ --_size-corner: 0.5rem;
259
+ --_size-icon-size: 1.5rem;
260
+
261
+ --_track-height: var(--np-slider-track-height, var(--_size-track-height));
262
+ --_handle-height: var(--np-slider-handle-height, var(--_size-handle-height));
263
+ --_corner: var(--np-slider-track-shape, var(--_size-corner));
264
+ --_icon-size: var(--np-slider-icon-size, var(--_size-icon-size));
265
+ --_hw: var(--np-slider-handle-width, 0.25rem);
266
+ --_inside: var(--np-slider-track-inside-shape, 0.125rem);
267
+ --_stop: var(--np-slider-stop-indicator-size, 0.25rem);
268
+ --_gap: calc(var(--_hw) / 2 + 0.375rem);
269
+
270
+ position: relative;
271
+ display: block;
272
+ inline-size: 100%;
273
+ block-size: var(--_handle-height);
274
+ touch-action: none;
275
+ -webkit-tap-highlight-color: transparent;
276
+ cursor: pointer;
277
+ }
278
+
279
+ .np-slider.np-size-s {
280
+ --_size-track-height: 1.5rem;
281
+ }
282
+ .np-slider.np-size-m {
283
+ --_size-track-height: 2.5rem;
284
+ --_size-corner: 0.75rem;
285
+ }
286
+ .np-slider.np-size-l {
287
+ --_size-track-height: 3.5rem;
288
+ --_size-handle-height: 4.25rem;
289
+ --_size-corner: 1rem;
290
+ }
291
+ .np-slider.np-size-xl {
292
+ --_size-track-height: 6rem;
293
+ --_size-handle-height: 6.75rem;
294
+ --_size-corner: 1.75rem;
295
+ --_size-icon-size: 2rem;
296
+ }
297
+
298
+ .np-slider.np-vertical {
299
+ writing-mode: vertical-lr;
300
+ direction: rtl;
301
+ inline-size: var(--np-slider-length, 12rem);
302
+ }
303
+
304
+ .np-slider.np-disabled {
305
+ cursor: default;
306
+ }
307
+
308
+ .np-slider-track {
309
+ position: absolute;
310
+ inset-inline: calc(var(--_hw) / 2);
311
+ inset-block: calc((var(--_handle-height) - var(--_track-height)) / 2);
312
+ }
313
+
314
+ .np-slider-lane {
315
+ position: absolute;
316
+ inset-block: 0;
317
+ border-radius: var(--_inside);
318
+ transition:
319
+ inset-inline-start var(--np-motion-expressive-fast-effects),
320
+ inset-inline-end var(--np-motion-expressive-fast-effects);
321
+ }
322
+
323
+ .np-dragging .np-slider-lane,
324
+ .np-dragging .np-slider-handle {
325
+ transition: none;
326
+ }
327
+
328
+ .np-slider-active {
329
+ inset-inline-start: var(--_a1);
330
+ inset-inline-end: calc(100% - var(--_a2));
331
+ overflow: hidden;
332
+ background: var(--np-slider-active-track-color, var(--np-color-primary));
333
+ }
334
+
335
+ .np-slider-inactive-start {
336
+ inset-inline-start: 0;
337
+ inset-inline-end: calc(100% - var(--_i1));
338
+ }
339
+
340
+ .np-slider-inactive-end {
341
+ inset-inline-start: var(--_i2);
342
+ inset-inline-end: 0;
343
+ }
344
+
345
+ .np-slider-inactive-start,
346
+ .np-slider-inactive-end {
347
+ background: var(--np-slider-inactive-track-color, var(--np-color-secondary-container));
348
+ overflow: hidden;
349
+ }
350
+
351
+ .np-slider-inactive-start,
352
+ .np-slider:not(.np-centered):not(.np-range) .np-slider-active {
353
+ border-start-start-radius: var(--_corner);
354
+ border-end-start-radius: var(--_corner);
355
+ }
356
+
357
+ .np-slider-inactive-end {
358
+ border-start-end-radius: var(--_corner);
359
+ border-end-end-radius: var(--_corner);
360
+ }
361
+
362
+ .np-slider-icon {
363
+ position: absolute;
364
+ inset-inline-start: var(--np-slider-icon-padding, 0.625rem);
365
+ inset-block: 0;
366
+ display: flex;
367
+ align-items: center;
368
+ justify-content: center;
369
+ inline-size: var(--_icon-size);
370
+ font-size: var(--_icon-size);
371
+ color: var(--np-slider-icon-color, var(--np-color-on-primary));
372
+ writing-mode: horizontal-tb;
373
+ }
374
+
375
+ .np-slider-icon :global(svg) {
376
+ inline-size: var(--_icon-size);
377
+ block-size: var(--_icon-size);
378
+ fill: currentColor;
379
+ }
380
+
381
+ .np-slider-stop,
382
+ .np-slider-tick {
383
+ position: absolute;
384
+ inline-size: var(--_stop);
385
+ block-size: var(--_stop);
386
+ inset-block-start: calc((var(--_track-height) - var(--_stop)) / 2);
387
+ border-radius: var(--np-shape-corner-full);
388
+ background: var(--np-slider-inactive-stop-color, var(--np-color-on-secondary-container));
389
+ pointer-events: none;
390
+ }
391
+
392
+ .np-slider-stop-end {
393
+ inset-inline-end: calc(var(--_corner) - var(--_stop) / 2);
394
+ }
395
+
396
+ .np-slider-stop-start {
397
+ inset-inline-start: calc(var(--_corner) - var(--_stop) / 2);
398
+ }
399
+
400
+ .np-slider-tick {
401
+ inset-inline-start: calc(
402
+ var(--_corner) + var(--_t) * (100% - 2 * var(--_corner)) - var(--_stop) / 2
403
+ );
404
+ }
405
+
406
+ .np-slider-tick.np-on-active {
407
+ background: var(--np-slider-active-stop-color, var(--np-color-on-primary));
408
+ }
409
+
410
+ .np-slider-handle {
411
+ position: absolute;
412
+ inset-inline-start: calc(var(--_p) - var(--_hw) / 2);
413
+ inset-block-start: calc((var(--_track-height) - var(--_handle-height)) / 2);
414
+ inline-size: var(--_hw);
415
+ block-size: var(--_handle-height);
416
+ border-radius: var(--np-slider-handle-shape, var(--np-shape-corner-full));
417
+ background: var(--np-slider-handle-color, var(--np-color-primary));
418
+ transition:
419
+ inset-inline-start var(--np-motion-expressive-fast-effects),
420
+ inline-size var(--np-motion-expressive-fast-effects),
421
+ block-size var(--np-motion-expressive-fast-effects);
422
+ }
423
+
424
+ .np-slider-handle-start {
425
+ --_p: var(--_p1);
426
+ }
427
+
428
+ .np-slider-handle-end {
429
+ --_p: var(--_p2);
430
+ }
431
+
432
+ .np-slider:not(.np-disabled, .np-pointer-focused):has(.np-slider-input-start:focus-visible)
433
+ .np-slider-handle-start,
434
+ .np-slider:not(.np-disabled, .np-pointer-focused):has(.np-slider-input-end:focus-visible)
435
+ .np-slider-handle-end {
436
+ inline-size: var(--np-slider-handle-width-focus, 0.125rem);
437
+ block-size: calc(var(--_handle-height) - 0.375rem);
438
+ outline: 3px solid var(--np-color-secondary);
439
+ outline-offset: 0.125rem;
440
+ }
441
+
442
+ .np-dragging .np-slider-handle {
443
+ inline-size: var(--np-slider-handle-width-focus, 0.125rem);
444
+ }
445
+
446
+ .np-slider-label-anchor {
447
+ position: absolute;
448
+ inset-block-end: calc(100% + 0.375rem);
449
+ inset-inline: 0;
450
+ display: flex;
451
+ justify-content: center;
452
+ pointer-events: none;
453
+ }
454
+
455
+ .np-slider-label {
456
+ flex: none;
457
+ writing-mode: horizontal-tb;
458
+ direction: ltr;
459
+ min-inline-size: 2rem;
460
+ box-sizing: border-box;
461
+ padding: 0.375rem 0.625rem;
462
+ border-radius: var(--np-shape-corner-full);
463
+ background: var(--np-slider-label-container-color, var(--np-color-inverse-surface));
464
+ color: var(--np-slider-label-text-color, var(--np-color-inverse-on-surface));
465
+ font-size: 0.875rem;
466
+ line-height: 1.25rem;
467
+ font-weight: 500;
468
+ text-align: center;
469
+ white-space: nowrap;
470
+ opacity: 0;
471
+ transition: opacity var(--np-motion-expressive-fast-effects);
472
+ }
473
+
474
+ .np-slider.np-labeled:hover .np-slider-label,
475
+ .np-slider.np-labeled.np-dragging .np-slider-label,
476
+ .np-slider.np-labeled:has(.np-slider-input:focus-visible) .np-slider-label {
477
+ opacity: 1;
478
+ }
479
+
480
+ .np-slider.np-disabled .np-slider-label {
481
+ opacity: 0;
482
+ }
483
+
484
+ .np-slider-input {
485
+ position: absolute;
486
+ inset: 0;
487
+ inline-size: 100%;
488
+ block-size: 100%;
489
+ margin: 0;
490
+ opacity: 0;
491
+ pointer-events: none;
492
+ }
493
+
494
+ .np-slider.np-disabled .np-slider-active {
495
+ background: color-mix(
496
+ in srgb,
497
+ var(--np-slider-disabled-active-track-color, var(--np-color-on-surface)) 38%,
498
+ transparent
499
+ );
500
+ }
501
+
502
+ .np-slider.np-disabled .np-slider-inactive-start,
503
+ .np-slider.np-disabled .np-slider-inactive-end {
504
+ background: color-mix(
505
+ in srgb,
506
+ var(--np-slider-disabled-inactive-track-color, var(--np-color-on-surface)) 12%,
507
+ transparent
508
+ );
509
+ }
510
+
511
+ .np-slider.np-disabled .np-slider-handle {
512
+ background: color-mix(
513
+ in srgb,
514
+ var(--np-slider-disabled-handle-color, var(--np-color-on-surface)) 38%,
515
+ transparent
516
+ );
517
+ }
518
+
519
+ .np-slider.np-disabled .np-slider-stop,
520
+ .np-slider.np-disabled .np-slider-tick {
521
+ background: color-mix(in srgb, var(--np-color-on-surface) 38%, transparent);
522
+ }
523
+
524
+ .np-slider.np-disabled .np-slider-stop.np-on-active,
525
+ .np-slider.np-disabled .np-slider-tick.np-on-active {
526
+ background: var(--np-color-surface);
527
+ }
528
+
529
+ .np-slider.np-disabled .np-slider-icon {
530
+ color: var(--np-color-surface);
531
+ }
532
+
533
+ @media (prefers-reduced-motion: reduce) {
534
+ .np-slider-lane,
535
+ .np-slider-handle,
536
+ .np-slider-label {
537
+ transition: none;
538
+ }
539
+ }
540
+
541
+ @media (forced-colors: active) {
542
+ .np-slider-active,
543
+ .np-slider-handle {
544
+ background: CanvasText;
545
+ }
546
+ .np-slider-inactive-start,
547
+ .np-slider-inactive-end {
548
+ outline: 1px solid CanvasText;
549
+ }
550
+ .np-slider.np-disabled .np-slider-active,
551
+ .np-slider.np-disabled .np-slider-handle {
552
+ background: GrayText;
553
+ }
554
+ }
555
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { SliderProps } from './types.ts';
2
+ declare const Slider: import("svelte").Component<SliderProps, {}, "element" | "value" | "inputElement" | "endValue" | "endInputElement">;
3
+ type Slider = ReturnType<typeof Slider>;
4
+ export default Slider;
@@ -0,0 +1 @@
1
+ export { default as Slider } from './Slider.svelte';
@@ -0,0 +1 @@
1
+ export { default as Slider } from './Slider.svelte';
@@ -0,0 +1,23 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLInputAttributes } from 'svelte/elements';
3
+ export type SliderSize = 'xs' | 's' | 'm' | 'l' | 'xl';
4
+ export interface SliderProps extends Omit<HTMLInputAttributes, 'type' | 'value' | 'size' | 'min' | 'max' | 'step' | 'orientation' | 'children'> {
5
+ value?: number;
6
+ endValue?: number;
7
+ min?: number;
8
+ max?: number;
9
+ step?: number;
10
+ size?: SliderSize;
11
+ orientation?: 'horizontal' | 'vertical';
12
+ range?: boolean;
13
+ centered?: boolean;
14
+ labeled?: boolean;
15
+ ticks?: boolean;
16
+ disabled?: boolean;
17
+ format?: (value: number) => string;
18
+ icon?: Snippet;
19
+ endInputAttributes?: HTMLInputAttributes;
20
+ element?: HTMLDivElement;
21
+ inputElement?: HTMLInputElement;
22
+ endInputElement?: HTMLInputElement;
23
+ }
@@ -0,0 +1 @@
1
+ export {};