@marianmeres/stuic 3.165.0 → 3.166.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/API.md CHANGED
@@ -582,11 +582,13 @@ Closeable message banner with intent styling.
582
582
 
583
583
  #### `Progress`
584
584
 
585
- Progress bar.
585
+ Progress bar or circle.
586
586
 
587
- | Prop | Type | Default | Description |
588
- | ------- | -------- | ------- | ------------------------ |
589
- | `value` | `number` | `0` | Current progress (0-100) |
587
+ | Prop | Type | Default | Description |
588
+ | ---------- | ------------------- | ------- | --------------------------------- |
589
+ | `progress` | `number` | `0` | Current progress (0-100, clamped) |
590
+ | `type` | `"bar" \| "circle"` | `"bar"` | Rendering variant |
591
+ | `classBar` | `string` | - | Classes for the inner fill (bar) |
590
592
 
591
593
  #### `Spinner`
592
594
 
@@ -1206,12 +1208,26 @@ Responsive wrapper around Book that intelligently switches between book mode (du
1206
1208
 
1207
1209
  #### `Circle`
1208
1210
 
1209
- SVG-based circular progress indicator with configurable stroke width and rotation.
1211
+ SVG-based circular progress indicator (a Svelte wrapper around the [`svgCircle`](#svgcircleoptions)
1212
+ utility). The svg fills its container over a fixed `100x100` viewBox, so the container
1213
+ sets the size and `strokeWidth` is in viewBox units. The stroke is `currentColor`.
1214
+
1215
+ | Prop | Type | Default | Description |
1216
+ | ----------------------- | --------- | ------- | ---------------------------------------------------------------- |
1217
+ | `completeness` | `number` | `1` | Progress from 0 to 1 (clamped) |
1218
+ | `strokeWidth` | `number` | `10` | Stroke width in viewBox units (radius = `50 - strokeWidth / 2`) |
1219
+ | `strokeWidthRatio` | `number` | `0` | Caps `strokeWidth` at this fraction of the radius (`0` = no cap) |
1220
+ | `bgStrokeColor` | `string` | - | CSS color of the background ring behind the arc |
1221
+ | `roundedEdges` | `boolean` | `true` | Rounded (vs butt) stroke line caps |
1222
+ | `rotate` | `number` | `0` | Rotation in degrees (arc starts at 3 o'clock; `-90` = top) |
1223
+ | `animateCompletenessMs` | `number` | `0` | CSS transition duration on `stroke-dashoffset` (ms) |
1224
+ | `class` / `style` | `string` | - | Container div class / inline style |
1225
+ | `circleClass` | `string` | - | Classes for the `<svg>` element |
1226
+ | `circleStyle` | `string` | - | Inline styles for the `<circle>` element |
1210
1227
 
1211
- | Prop | Type | Default | Description |
1212
- | ------------- | -------- | ------- | ---------------------- |
1213
- | `value` | `number` | `0` | Progress value (0-100) |
1214
- | `strokeWidth` | `number` | `8` | Stroke width |
1228
+ ```svelte
1229
+ <Circle completeness={0.75} rotate={-90} bgStrokeColor="#e5e5e5" class="size-16" />
1230
+ ```
1215
1231
 
1216
1232
  #### `H`
1217
1233
 
@@ -1935,9 +1951,48 @@ Generate deterministic avatar colors from a name string.
1935
1951
 
1936
1952
  HSL color generation.
1937
1953
 
1938
- #### `svgCircle(radius, strokeWidth)`
1954
+ #### `svgCircle(options?)`
1955
+
1956
+ Build an SVG ring as a plain DOM node, with setters to update it in place. Framework
1957
+ agnostic - it returns the element, mounting it is up to the caller. Used by `Circle` and
1958
+ `Progress` internally.
1959
+
1960
+ The svg is `width/height: 100%` over a fixed `100x100` viewBox, so the container decides
1961
+ the rendered size; `strokeWidth` is in viewBox units and the radius is derived from it
1962
+ (`50 - strokeWidth / 2`).
1963
+
1964
+ **Options** (all optional):
1965
+
1966
+ | Option | Type | Default | Description |
1967
+ | ------------------ | --------- | ------- | ---------------------------------------------------------------- |
1968
+ | `completeness` | `number` | `1` | Arc length from 0 to 1 (clamped) |
1969
+ | `strokeWidth` | `number` | `10` | Stroke width in viewBox units |
1970
+ | `strokeWidthRatio` | `number` | `0` | Caps `strokeWidth` at this fraction of the radius (`0` = no cap) |
1971
+ | `rotate` | `number` | `0` | Rotation in degrees, modulo 360 (arc starts at 3 o'clock) |
1972
+ | `roundedEdges` | `boolean` | `true` | Rounded (vs butt) line caps |
1973
+ | `bgStrokeColor` | `string` | - | CSS color of a full background ring behind the arc |
1974
+ | `class` | `string` | - | Classes for the `<svg>` element |
1975
+ | `circleStyle` | `string` | - | Inline styles for the `<circle>` element |
1976
+ | `radius` | `number` | - | Currently ignored (derived from the viewBox and stroke width) |
1939
1977
 
1940
- Generate SVG circle path data.
1978
+ **Returns:** `{ svg: SVGSVGElement, setCompleteness(v: number): void, setRotate(deg: number): void }`
1979
+
1980
+ The setters mutate the existing element - reach for them instead of rebuilding when only
1981
+ the progress or rotation changes.
1982
+
1983
+ ```ts
1984
+ import { svgCircle } from "@marianmeres/stuic";
1985
+
1986
+ const { svg, setCompleteness } = svgCircle({
1987
+ completeness: 0.75,
1988
+ strokeWidth: 8,
1989
+ rotate: -90,
1990
+ bgStrokeColor: "#e5e5e5",
1991
+ });
1992
+ container.appendChild(svg);
1993
+
1994
+ setCompleteness(0.9); // no rebuild
1995
+ ```
1941
1996
 
1942
1997
  #### `oscillate(min, max, step)`
1943
1998
 
@@ -1,5 +1,24 @@
1
+ <script lang="ts" module>
2
+ import type { SvgCircleOptions } from "../../utils/svg-circle.js";
3
+
4
+ export interface Props extends Partial<SvgCircleOptions> {
5
+ /** Inline styles for the container element */
6
+ style?: string;
7
+ /**
8
+ * CSS classes for the `<svg>` element (forwarded to the helper's `class`
9
+ * option). Since the stroke is `currentColor`, a text color utility here
10
+ * colors the ring.
11
+ */
12
+ circleClass?: string;
13
+ /** Inline styles for the `<circle>` element */
14
+ circleStyle?: string;
15
+ /** Transition duration in ms on the stroke-dashoffset */
16
+ animateCompletenessMs?: number;
17
+ }
18
+ </script>
19
+
1
20
  <script lang="ts">
2
- import { svgCircle, type SvgCircleOptions } from "../../utils/svg-circle.js";
21
+ import { svgCircle } from "../../utils/svg-circle.js";
3
22
  import { twMerge } from "../../utils/tw-merge.js";
4
23
 
5
24
  let {
@@ -14,13 +33,7 @@
14
33
  circleClass,
15
34
  circleStyle = "",
16
35
  animateCompletenessMs = 0,
17
- }: Partial<SvgCircleOptions> & {
18
- style?: string;
19
- circleClass?: string;
20
- circleStyle?: string;
21
- // transition duration in ms on the stroke-dashoffset
22
- animateCompletenessMs?: number;
23
- } = $props();
36
+ }: Props = $props();
24
37
 
25
38
  let container: HTMLDivElement = $state()!;
26
39
 
@@ -42,10 +55,12 @@
42
55
  );
43
56
 
44
57
  $effect(() => {
45
- container.appendChild(circle.svg);
46
- return () => {
47
- circle.svg.remove();
48
- };
58
+ // Capture the current instance: `circle` is a derived, so reading it again in the
59
+ // teardown would resolve to the freshly rebuilt svg and leave the previous node
60
+ // behind (stacking one <svg> per structural prop change).
61
+ const { svg } = circle;
62
+ container.appendChild(svg);
63
+ return () => svg.remove();
49
64
  });
50
65
 
51
66
  $effect(() => {
@@ -1,10 +1,18 @@
1
- import { type SvgCircleOptions } from "../../utils/svg-circle.js";
2
- type $$ComponentProps = Partial<SvgCircleOptions> & {
1
+ import type { SvgCircleOptions } from "../../utils/svg-circle.js";
2
+ export interface Props extends Partial<SvgCircleOptions> {
3
+ /** Inline styles for the container element */
3
4
  style?: string;
5
+ /**
6
+ * CSS classes for the `<svg>` element (forwarded to the helper's `class`
7
+ * option). Since the stroke is `currentColor`, a text color utility here
8
+ * colors the ring.
9
+ */
4
10
  circleClass?: string;
11
+ /** Inline styles for the `<circle>` element */
5
12
  circleStyle?: string;
13
+ /** Transition duration in ms on the stroke-dashoffset */
6
14
  animateCompletenessMs?: number;
7
- };
8
- declare const Circle: import("svelte").Component<$$ComponentProps, {}, "">;
15
+ }
16
+ declare const Circle: import("svelte").Component<Props, {}, "">;
9
17
  type Circle = ReturnType<typeof Circle>;
10
18
  export default Circle;
@@ -1,22 +1,31 @@
1
1
  # Circle
2
2
 
3
- An SVG circle progress indicator with configurable stroke, rotation, and animated transitions.
3
+ An SVG circle progress indicator with configurable stroke, rotation, and animated
4
+ transitions. A thin Svelte wrapper around the [`svgCircle`](../../utils/svg-circle.ts)
5
+ utility.
6
+
7
+ The svg is rendered at `width/height: 100%` over a fixed `100x100` viewBox, so **the
8
+ container decides the size** (default `size-6`) and `strokeWidth` is expressed in viewBox
9
+ units - i.e. it scales with the box. The stroke defaults to `currentColor`.
4
10
 
5
11
  ## Props
6
12
 
7
- | Prop | Type | Default | Description |
8
- | ----------------------- | --------- | ------- | --------------------------------------------- |
9
- | `completeness` | `number` | `1` | Progress value from 0 to 1 |
10
- | `strokeWidth` | `number` | `10` | Stroke width in SVG units |
11
- | `bgStrokeColor` | `string` | - | Background circle stroke color |
12
- | `roundedEdges` | `boolean` | `true` | Use rounded stroke line caps |
13
- | `rotate` | `number` | `0` | Rotation in degrees |
14
- | `strokeWidthRatio` | `number` | `0` | Ratio for background stroke width |
15
- | `animateCompletenessMs` | `number` | `0` | Transition duration for progress changes (ms) |
16
- | `class` | `string` | - | CSS for container div |
17
- | `style` | `string` | - | Inline styles for container |
18
- | `circleClass` | `string` | - | CSS for SVG circle element |
19
- | `circleStyle` | `string` | - | Inline styles for circle |
13
+ | Prop | Type | Default | Description |
14
+ | ----------------------- | --------- | ------- | ---------------------------------------------------------------------- |
15
+ | `completeness` | `number` | `1` | Progress from 0 to 1 (clamped) |
16
+ | `strokeWidth` | `number` | `10` | Stroke width in viewBox units (radius = `50 - strokeWidth / 2`) |
17
+ | `strokeWidthRatio` | `number` | `0` | Caps `strokeWidth` at this fraction of the radius; `0` means no cap |
18
+ | `bgStrokeColor` | `string` | - | Any CSS color; adds a full background ring behind the arc |
19
+ | `roundedEdges` | `boolean` | `true` | Rounded (vs butt) stroke line caps |
20
+ | `rotate` | `number` | `0` | Rotation in degrees; the arc starts at 3 o'clock, so use `-90` for top |
21
+ | `animateCompletenessMs` | `number` | `0` | CSS transition duration on `stroke-dashoffset` (ms) |
22
+ | `class` | `string` | - | CSS classes for the container div |
23
+ | `style` | `string` | - | Inline styles for the container div |
24
+ | `circleClass` | `string` | - | CSS classes for the `<svg>` element |
25
+ | `circleStyle` | `string` | - | Inline styles for the `<circle>` element |
26
+
27
+ `completeness` and `rotate` are applied through the helper's setters, so changing them
28
+ only rewrites two attributes. Every other prop rebuilds the svg.
20
29
 
21
30
  ## Usage
22
31
 
@@ -24,17 +33,17 @@ An SVG circle progress indicator with configurable stroke, rotation, and animate
24
33
 
25
34
  ```svelte
26
35
  <script lang="ts">
27
- import { Circle } from "stuic";
36
+ import { Circle } from "@marianmeres/stuic";
28
37
  </script>
29
38
 
30
- <Circle completeness={0.75} class="size-16" />
39
+ <Circle completeness={0.75} rotate={-90} class="size-16" />
31
40
  ```
32
41
 
33
42
  ### Animated Progress
34
43
 
35
44
  ```svelte
36
45
  <script lang="ts">
37
- import { Circle } from "stuic";
46
+ import { Circle } from "@marianmeres/stuic";
38
47
 
39
48
  let progress = $state(0);
40
49
 
@@ -54,13 +63,40 @@ An SVG circle progress indicator with configurable stroke, rotation, and animate
54
63
 
55
64
  ### Custom Styling
56
65
 
66
+ The ring's stroke is `currentColor`, so a text color on the container (or on the svg via
67
+ `circleClass`) colors it. To set the stroke directly, use `circleStyle` - it lands on the
68
+ `<circle>` element, which is the only place that beats the `stroke="currentColor"`
69
+ presentation attribute.
70
+
57
71
  ```svelte
72
+ <!-- via currentColor -->
58
73
  <Circle
59
74
  completeness={0.5}
60
75
  strokeWidth={8}
61
76
  rotate={-90}
62
77
  bgStrokeColor="rgba(0,0,0,0.1)"
78
+ class="size-24 text-blue-500"
79
+ />
80
+
81
+ <!-- or explicitly on the circle element -->
82
+ <Circle
83
+ completeness={0.5}
84
+ rotate={-90}
63
85
  class="size-24"
64
- circleClass="stroke-blue-500"
86
+ circleStyle="stroke: var(--stuic-color-primary);"
65
87
  />
66
88
  ```
89
+
90
+ ### Content Inside the Ring
91
+
92
+ ```svelte
93
+ <div class="relative size-24">
94
+ <Circle
95
+ completeness={0.62}
96
+ rotate={-90}
97
+ bgStrokeColor="#e5e5e5"
98
+ class="absolute inset-0"
99
+ />
100
+ <div class="absolute inset-0 flex items-center justify-center">62%</div>
101
+ </div>
102
+ ```
@@ -0,0 +1 @@
1
+ export { default as Circle, type Props as CircleProps } from "./Circle.svelte";
@@ -0,0 +1 @@
1
+ export { default as Circle } from "./Circle.svelte";
package/dist/index.d.ts CHANGED
@@ -35,6 +35,7 @@ export * from "./components/Cart/index.js";
35
35
  export * from "./components/Card/index.js";
36
36
  export * from "./components/Carousel/index.js";
37
37
  export * from "./components/Checkout/index.js";
38
+ export * from "./components/Circle/index.js";
38
39
  export * from "./components/Collapsible/index.js";
39
40
  export * from "./components/ColorScheme/index.js";
40
41
  export * from "./components/CommandMenu/index.js";
package/dist/index.js CHANGED
@@ -36,6 +36,7 @@ export * from "./components/Cart/index.js";
36
36
  export * from "./components/Card/index.js";
37
37
  export * from "./components/Carousel/index.js";
38
38
  export * from "./components/Checkout/index.js";
39
+ export * from "./components/Circle/index.js";
39
40
  export * from "./components/Collapsible/index.js";
40
41
  export * from "./components/ColorScheme/index.js";
41
42
  export * from "./components/CommandMenu/index.js";
@@ -110,7 +110,7 @@ const search = debounce((query: string) => {
110
110
  | `colors` | Color manipulation |
111
111
  | `avatarColors` | Deterministic avatar colors |
112
112
  | `paint` | HSL color generation |
113
- | `svgCircle` | SVG circle path |
113
+ | `svgCircle` | SVG progress ring (DOM node) |
114
114
  | `oscillate` | Value oscillation for animation |
115
115
 
116
116
  ### Example: Class Merging
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/stuic",
3
- "version": "3.165.0",
3
+ "version": "3.166.0",
4
4
  "packageManager": "pnpm@11.5.0",
5
5
  "scripts": {
6
6
  "dev": "vite dev",