@marianmeres/stuic 2.1.18 → 2.1.20

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,41 @@
1
+ <script lang="ts">
2
+ import { onMount } from "svelte";
3
+ import { svgCircle, type SvgCircleOptions } from "../../utils/svg-circle.js";
4
+ import { twMerge } from "../../utils/tw-merge.js";
5
+
6
+ let {
7
+ strokeWidth = 10,
8
+ completeness = 1,
9
+ bgStrokeColor,
10
+ class: classProp = "",
11
+ roundedEdges = true,
12
+ rotate = 0,
13
+ strokeWidthRatio = 0,
14
+ style,
15
+ }: Partial<SvgCircleOptions> & { style?: string } = $props();
16
+
17
+ let container: HTMLDivElement = $state()!;
18
+
19
+ const circle = svgCircle({
20
+ strokeWidth,
21
+ completeness,
22
+ bgStrokeColor,
23
+ roundedEdges,
24
+ rotate,
25
+ strokeWidthRatio,
26
+ });
27
+
28
+ $effect(() => {
29
+ container.appendChild(circle.svg);
30
+ });
31
+
32
+ $effect(() => {
33
+ circle.setCompleteness(completeness);
34
+ });
35
+
36
+ $effect(() => {
37
+ circle.setRotate(rotate);
38
+ });
39
+ </script>
40
+
41
+ <div bind:this={container} class={twMerge("size-6", classProp)} {style}></div>
@@ -0,0 +1,7 @@
1
+ import { type SvgCircleOptions } from "../../utils/svg-circle.js";
2
+ type $$ComponentProps = Partial<SvgCircleOptions> & {
3
+ style?: string;
4
+ };
5
+ declare const Circle: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type Circle = ReturnType<typeof Circle>;
7
+ export default Circle;
@@ -56,7 +56,7 @@
56
56
  --color-dismiss-border: var(--color-${theme}-500);
57
57
  --color-dismiss-border-dark: var(--color-${theme}-500);
58
58
  `
59
- : ""}
59
+ : ``}
60
60
  transition:slide={{ duration }}
61
61
  >
62
62
  <div class={twMerge("content", "flex-1 px-4 py-3", classContent)}>
@@ -133,7 +133,11 @@
133
133
 
134
134
  let _iconFns = $derived({ ...notificationsDefaultIcons, ...iconFns });
135
135
 
136
- const maybeIcon = (n: Notification) => (n.iconFn ?? _iconFns?.[n.type])?.();
136
+ const maybeIcon = (n: Notification) => {
137
+ if (n.iconFn === false) return "";
138
+ if (typeof n.iconFn === "function") return n.iconFn();
139
+ return (_iconFns?.[n.type] as any)?.();
140
+ };
137
141
 
138
142
  const _classWrapX = `
139
143
  fixed z-50 flex flex-row inset-0
@@ -0,0 +1,17 @@
1
+ <script lang="ts">
2
+ import { createTickerRAF } from "@marianmeres/ticker";
3
+ import Circle from "../Circle/Circle.svelte";
4
+ import { oscillate } from "../../utils/oscillate.js";
5
+ import { twMerge } from "../../utils/tw-merge.js";
6
+
7
+ let {
8
+ class: classProp = "",
9
+ bgStrokeColor = "rgba(0 0 0 / .1)",
10
+ }: { class?: string; bgStrokeColor?: string } = $props();
11
+
12
+ const ticker = createTickerRAF(50, true);
13
+
14
+ let completeness = $derived(oscillate($ticker / 1000, 0.15, 0.85, 1));
15
+ </script>
16
+
17
+ <Circle {completeness} class={twMerge("animate-spin", classProp)} {bgStrokeColor} />
@@ -0,0 +1,7 @@
1
+ type $$ComponentProps = {
2
+ class?: string;
3
+ bgStrokeColor?: string;
4
+ };
5
+ declare const SpinnerCircle: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type SpinnerCircle = ReturnType<typeof SpinnerCircle>;
7
+ export default SpinnerCircle;
@@ -14,6 +14,7 @@ export * from "./maybe-json-parse.js";
14
14
  export * from "./maybe-json-stringify.js";
15
15
  export * from "./nl2br.js";
16
16
  export * from "./omit-pick.js";
17
+ export * from "./oscillate.js";
17
18
  export * from "./paint.js";
18
19
  export * from "./persistent-state.svelte.js";
19
20
  export * from "./prefers-reduced-motion.svelte.js";
@@ -23,6 +24,7 @@ export * from "./seconds.js";
23
24
  export * from "./sleep.js";
24
25
  export * from "./storage-abstraction.js";
25
26
  export * from "./str-hash.js";
27
+ export * from "./svg-circle.js";
26
28
  export * from "./switch.svelte.js";
27
29
  export * from "./throttle.js";
28
30
  export * from "./tr.js";
@@ -14,6 +14,7 @@ export * from "./maybe-json-parse.js";
14
14
  export * from "./maybe-json-stringify.js";
15
15
  export * from "./nl2br.js";
16
16
  export * from "./omit-pick.js";
17
+ export * from "./oscillate.js";
17
18
  export * from "./paint.js";
18
19
  export * from "./persistent-state.svelte.js";
19
20
  export * from "./prefers-reduced-motion.svelte.js";
@@ -23,6 +24,7 @@ export * from "./seconds.js";
23
24
  export * from "./sleep.js";
24
25
  export * from "./storage-abstraction.js";
25
26
  export * from "./str-hash.js";
27
+ export * from "./svg-circle.js";
26
28
  export * from "./switch.svelte.js";
27
29
  export * from "./throttle.js";
28
30
  export * from "./tr.js";
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Returns an oscillating value (sine wave) between a min and max.
3
+ */
4
+ export declare function oscillate(time: number, min?: number, max?: number, speed?: number): number;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Returns an oscillating value (sine wave) between a min and max.
3
+ */
4
+ export function oscillate(time, min = 0, max = 1, speed = 1) {
5
+ // Calculate the midpoint (the center of the oscillation)
6
+ // e.g., if min=10, max=50, midpoint is (50+10)/2 = 30
7
+ const midpoint = (max + min) / 2;
8
+ // Calculate the amplitude (half the distance between min and max)
9
+ // e.g., if min=10, max=50, amplitude is (50-10)/2 = 20
10
+ const amplitude = (max - min) / 2;
11
+ // Calculate the oscillation
12
+ // 1. Math.sin(time * speed) gives a value between -1 and 1
13
+ // 2. Multiplying by amplitude scales it to [-amplitude, amplitude] (e.g., [-20, 20])
14
+ // 3. Adding midpoint shifts the range to [midpoint - amplitude, midpoint + amplitude]
15
+ // (e.g., [30 - 20, 30 + 20] which is [10, 50])
16
+ return midpoint + Math.sin(time * speed) * amplitude;
17
+ }
@@ -0,0 +1,16 @@
1
+ export interface SvgCircleOptions {
2
+ radius: number;
3
+ strokeWidth: number;
4
+ completeness: number;
5
+ class: string;
6
+ bgStrokeColor: string;
7
+ roundedEdges: boolean;
8
+ rotate: number;
9
+ strokeWidthRatio: number;
10
+ }
11
+ /** Will construct and return svg DOM element based on input options */
12
+ export declare function svgCircle(options?: Partial<SvgCircleOptions>): {
13
+ svg: SVGSVGElement;
14
+ setCompleteness(completeness: number): void;
15
+ setRotate(rotate: number): void;
16
+ };
@@ -0,0 +1,76 @@
1
+ function _normalize_completness(v) {
2
+ return Math.max(0, Math.min(1, v));
3
+ }
4
+ function _normalize_rotate(v) {
5
+ return `${v % 360}`;
6
+ }
7
+ function _normalize_cls(v) {
8
+ return [
9
+ ...new Set(v
10
+ .split(" ")
11
+ .map((v) => v.trim())
12
+ .filter(Boolean)),
13
+ ];
14
+ }
15
+ /** Will construct and return svg DOM element based on input options */
16
+ export function svgCircle(options = {}) {
17
+ let { strokeWidth = 10, completeness = 1, bgStrokeColor = "", class: classProp = "", roundedEdges = true, rotate = 0, strokeWidthRatio = 0, } = options ?? {};
18
+ completeness = _normalize_completness(completeness);
19
+ // calculate radius based on viewBox, leaving room for stroke
20
+ let actualStrokeWidth = strokeWidth;
21
+ if (strokeWidthRatio) {
22
+ const maxStrokeWidth = strokeWidthRatio * 50; // percentage of radius
23
+ actualStrokeWidth = Math.min(strokeWidth, maxStrokeWidth);
24
+ }
25
+ const radius = 50 - actualStrokeWidth / 2;
26
+ const center = 50;
27
+ const circumference = 2 * Math.PI * radius;
28
+ const dashArray = circumference;
29
+ const dashOffset = circumference * (1 - completeness);
30
+ const linecap = roundedEdges ? "round" : "butt";
31
+ // the svg element
32
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
33
+ svg.setAttribute("width", "100%");
34
+ svg.setAttribute("height", "100%");
35
+ svg.setAttribute("viewBox", "0 0 100 100");
36
+ svg.setAttribute("preserveAspectRatio", "xMidYMid meet");
37
+ if (classProp)
38
+ svg.classList.add(..._normalize_cls(classProp));
39
+ // optional background
40
+ if (bgStrokeColor) {
41
+ const bgCircle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
42
+ bgCircle.setAttribute("cx", `${center}`);
43
+ bgCircle.setAttribute("cy", `${center}`);
44
+ bgCircle.setAttribute("r", `${radius}`);
45
+ bgCircle.setAttribute("fill", "none");
46
+ bgCircle.setAttribute("stroke", bgStrokeColor);
47
+ bgCircle.setAttribute("stroke-width", `${actualStrokeWidth}`);
48
+ svg.appendChild(bgCircle);
49
+ }
50
+ // the circle
51
+ const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
52
+ circle.setAttribute("cx", `${center}`);
53
+ circle.setAttribute("cy", `${center}`);
54
+ circle.setAttribute("r", `${radius}`);
55
+ circle.setAttribute("fill", "none");
56
+ circle.setAttribute("stroke", "currentColor");
57
+ circle.setAttribute("stroke-width", `${actualStrokeWidth}`);
58
+ circle.setAttribute("stroke-dasharray", `${dashArray}`);
59
+ circle.setAttribute("stroke-dashoffset", `${dashOffset}`);
60
+ circle.setAttribute("stroke-linecap", linecap);
61
+ circle.setAttribute("transform-origin", "center");
62
+ circle.setAttribute("transform", `rotate(${_normalize_rotate(rotate)})`);
63
+ //
64
+ svg.appendChild(circle);
65
+ return {
66
+ svg,
67
+ setCompleteness(completeness) {
68
+ completeness = _normalize_completness(completeness);
69
+ const dashOffset = circumference * (1 - completeness);
70
+ circle.setAttribute("stroke-dashoffset", `${dashOffset}`);
71
+ },
72
+ setRotate(rotate) {
73
+ circle.setAttribute("transform", `rotate(${_normalize_rotate(rotate)})`);
74
+ },
75
+ };
76
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/stuic",
3
- "version": "2.1.18",
3
+ "version": "2.1.20",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",