@dorsk/tsumikit 0.18.8 → 0.19.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.
@@ -0,0 +1,102 @@
1
+ <script lang="ts" module>
2
+ // One bar split into proportional segments (e.g. one per season of a series),
3
+ // each filled value/max with its own tone. Token-styled, thin separators.
4
+ export type ProgressSegment = {
5
+ value: number;
6
+ max: number;
7
+ // Fill colour per segment. `muted` renders a faint fill for empty parts.
8
+ tone?: 'accent' | 'success' | 'warn' | 'danger' | 'muted';
9
+ // Native tooltip / accessible name for the segment.
10
+ label?: string;
11
+ };
12
+ </script>
13
+
14
+ <script lang="ts">
15
+ let {
16
+ segments,
17
+ label,
18
+ size = 'md',
19
+ class: klass = ''
20
+ }: {
21
+ segments: ProgressSegment[];
22
+ label?: string;
23
+ // Track height. `sm` is a thin ~5px track for inline rows.
24
+ size?: 'sm' | 'md';
25
+ class?: string;
26
+ } = $props();
27
+
28
+ const totalMax = $derived(segments.reduce((s, seg) => s + Math.max(0, seg.max), 0));
29
+ const totalValue = $derived(
30
+ segments.reduce((s, seg) => s + Math.max(0, Math.min(seg.value, seg.max)), 0)
31
+ );
32
+
33
+ function pct(seg: ProgressSegment): number {
34
+ if (seg.max <= 0) return 0;
35
+ return Math.max(0, Math.min(100, (seg.value / seg.max) * 100));
36
+ }
37
+ </script>
38
+
39
+ <div
40
+ data-tsu="SegmentedProgress"
41
+ class="segmented-progress size-{size} {klass}"
42
+ role="progressbar"
43
+ aria-label={label}
44
+ aria-valuemin={0}
45
+ aria-valuemax={totalMax}
46
+ aria-valuenow={totalValue}
47
+ >
48
+ {#each segments as seg, i (i)}
49
+ <div
50
+ class="segment tone-{seg.tone ?? 'accent'}"
51
+ style="flex-grow: {Math.max(seg.max, 1)}"
52
+ title={seg.label}
53
+ >
54
+ <div class="bar" style="width: {pct(seg)}%"></div>
55
+ </div>
56
+ {/each}
57
+ </div>
58
+
59
+ <style>
60
+ .segmented-progress {
61
+ display: flex;
62
+ width: 100%;
63
+ height: 0.5rem;
64
+ gap: 2px;
65
+ border-radius: var(--r-pill);
66
+ }
67
+ .segmented-progress.size-sm {
68
+ height: 0.3125rem;
69
+ }
70
+ .segment {
71
+ flex: 0 1 0%;
72
+ min-width: 3px;
73
+ height: 100%;
74
+ background: var(--bg-elevated-2);
75
+ overflow: hidden;
76
+ }
77
+ .segment:first-child {
78
+ border-top-left-radius: var(--r-pill);
79
+ border-bottom-left-radius: var(--r-pill);
80
+ }
81
+ .segment:last-child {
82
+ border-top-right-radius: var(--r-pill);
83
+ border-bottom-right-radius: var(--r-pill);
84
+ }
85
+ .bar {
86
+ height: 100%;
87
+ background: var(--fill, var(--accent));
88
+ transition: width 0.2s var(--ease);
89
+ }
90
+ .tone-success {
91
+ --fill: var(--ok);
92
+ }
93
+ .tone-warn {
94
+ --fill: var(--warn);
95
+ }
96
+ .tone-danger {
97
+ --fill: var(--danger);
98
+ }
99
+ .tone-muted {
100
+ --fill: var(--text-faint);
101
+ }
102
+ </style>
@@ -0,0 +1,15 @@
1
+ export type ProgressSegment = {
2
+ value: number;
3
+ max: number;
4
+ tone?: 'accent' | 'success' | 'warn' | 'danger' | 'muted';
5
+ label?: string;
6
+ };
7
+ type $$ComponentProps = {
8
+ segments: ProgressSegment[];
9
+ label?: string;
10
+ size?: 'sm' | 'md';
11
+ class?: string;
12
+ };
13
+ declare const SegmentedProgress: import("svelte").Component<$$ComponentProps, {}, "">;
14
+ type SegmentedProgress = ReturnType<typeof SegmentedProgress>;
15
+ export default SegmentedProgress;
@@ -367,13 +367,18 @@
367
367
  outline: 2px solid var(--accent);
368
368
  outline-offset: -2px;
369
369
  }
370
+ /* The handle overhangs the panel edge by 6px, but never past the host
371
+ container: when the panel fills it (edge at the window border), the
372
+ overhang would land off-screen and be ungrabbable, so the shift clamps to
373
+ the leftover space and the lost overhang is added back inside instead. */
370
374
  .resize-handle {
375
+ --handle-shift: max(-6px, calc(var(--panel-current-width) - 100cqw));
371
376
  position: absolute;
372
377
  top: 0;
373
- right: -6px;
378
+ right: var(--handle-shift);
374
379
  bottom: 0;
375
380
  z-index: 2;
376
- width: 12px;
381
+ width: calc(18px + var(--handle-shift));
377
382
  padding: 0;
378
383
  background: transparent;
379
384
  border: 0;
@@ -382,7 +387,7 @@
382
387
  }
383
388
  .right .resize-handle {
384
389
  right: auto;
385
- left: -6px;
390
+ left: var(--handle-shift);
386
391
  }
387
392
  .resize-handle::after {
388
393
  content: '';
@@ -428,6 +433,11 @@
428
433
  .collapsed .panel {
429
434
  box-shadow: none;
430
435
  }
436
+ /* The 85cqw cap guarantees free space beside the panel, but the clamp
437
+ above reads --panel-current-width, which the cap may exceed. */
438
+ .resize-handle {
439
+ --handle-shift: -6px;
440
+ }
431
441
  .main {
432
442
  height: 100%;
433
443
  }
package/dist/index.d.ts CHANGED
@@ -11,6 +11,7 @@ export { default as Icon } from './components/atoms/Icon.svelte';
11
11
  export { default as Input } from './components/atoms/Input.svelte';
12
12
  export { default as Link } from './components/atoms/Link.svelte';
13
13
  export { default as Progress } from './components/atoms/Progress.svelte';
14
+ export { default as SegmentedProgress, type ProgressSegment, } from './components/atoms/SegmentedProgress.svelte';
14
15
  export { default as Select } from './components/atoms/Select.svelte';
15
16
  export { default as Slider } from './components/atoms/Slider.svelte';
16
17
  export { default as Spinner } from './components/atoms/Spinner.svelte';
package/dist/index.js CHANGED
@@ -16,6 +16,7 @@ export { default as Icon } from './components/atoms/Icon.svelte';
16
16
  export { default as Input } from './components/atoms/Input.svelte';
17
17
  export { default as Link } from './components/atoms/Link.svelte';
18
18
  export { default as Progress } from './components/atoms/Progress.svelte';
19
+ export { default as SegmentedProgress, } from './components/atoms/SegmentedProgress.svelte';
19
20
  export { default as Select } from './components/atoms/Select.svelte';
20
21
  export { default as Slider } from './components/atoms/Slider.svelte';
21
22
  export { default as Spinner } from './components/atoms/Spinner.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dorsk/tsumikit",
3
- "version": "0.18.8",
3
+ "version": "0.19.0",
4
4
  "description": "Minimal, dependency-free Svelte 5 + pure-CSS UI kit. Token-driven atoms, molecules & layouts with theming out of the box.",
5
5
  "type": "module",
6
6
  "license": "MIT",