@marianmeres/stuic 2.1.6 → 2.1.8

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.
@@ -1,5 +1,31 @@
1
1
  <script lang="ts" module>
2
- const _instances = [];
2
+ interface BodyStyles {
3
+ position: string | null;
4
+ top: string | null;
5
+ width: string | null;
6
+ overflow: string | null;
7
+ }
8
+
9
+ function get_body_style(): BodyStyles {
10
+ // const style = window.getComputedStyle(document.body);
11
+ const style = document.body.style; // we want only explicitly defined, not computed
12
+ return {
13
+ position: style.position || null,
14
+ top: style.top || null,
15
+ width: style.width || null,
16
+ overflow: style.overflow || null,
17
+ };
18
+ }
19
+
20
+ function restore_body_styles(original: BodyStyles) {
21
+ (["position", "top", "width", "overflow"] as (keyof BodyStyles)[]).forEach((k) => {
22
+ if (original[k] !== null) {
23
+ document.body.style[k] = original[k];
24
+ } else {
25
+ document.body.style.removeProperty(k);
26
+ }
27
+ });
28
+ }
3
29
  </script>
4
30
 
5
31
  <script lang="ts">
@@ -11,7 +37,7 @@
11
37
  } from "../../index.js";
12
38
  import { createClog } from "@marianmeres/clog";
13
39
  import { PressedKeys, watch } from "runed";
14
- import { type Snippet } from "svelte";
40
+ import { onDestroy, tick, type Snippet } from "svelte";
15
41
  import { fade } from "svelte/transition";
16
42
 
17
43
  const clog = createClog("Backdrop").debug;
@@ -95,29 +121,33 @@
95
121
  );
96
122
 
97
123
  // lock body scroll when open and restore back
98
- let _original: any = {};
124
+ let _original: BodyStyles = {
125
+ position: null,
126
+ top: null,
127
+ width: null,
128
+ overflow: null,
129
+ };
130
+
131
+ function _restore() {
132
+ const scrollY = document.body.style.top;
133
+ restore_body_styles(_original);
134
+ // Restore scroll position
135
+ window.scrollTo(0, parseInt(scrollY || "0") * -1);
136
+ }
137
+
99
138
  $effect(() => {
100
139
  if (noScrollLock) return;
101
140
  if (visible) {
102
- _original = window.getComputedStyle(document.body);
141
+ _original = get_body_style();
103
142
  const scrollY = window.scrollY;
104
-
105
143
  document.body.style.position = "fixed";
106
144
  document.body.style.top = `-${scrollY}px`;
107
145
  document.body.style.width = "100%";
108
146
  document.body.style.overflow = "hidden";
109
147
  } else {
110
- const scrollY = document.body.style.top;
111
-
112
- document.body.style.position = _original.position;
113
- document.body.style.position = "";
114
- document.body.style.top = "";
115
- document.body.style.width = "";
116
- document.body.style.overflow = "";
117
-
118
- // Restore scroll position
119
- window.scrollTo(0, parseInt(scrollY || "0") * -1);
148
+ _restore();
120
149
  }
150
+ return _restore; // onDestroy as well
121
151
  });
122
152
 
123
153
  $effect(() => {
@@ -9,10 +9,11 @@
9
9
  rounded-md
10
10
  inline-flex items-center justify-center gap-x-2
11
11
  px-3 py-2
12
+ select-none
12
13
 
13
14
  hover:brightness-105
14
15
  active:brightness-95
15
- disabled:hover:brightness-100
16
+ disabled:hover:brightness-100 disabled:opacity-50
16
17
 
17
18
  focus:brightness-105
18
19
  focus:border-button-border-focus focus:dark:border-button-border-focus-dark
@@ -1,4 +1,4 @@
1
- export declare const BUTTON_STUIC_BASE_CLASSES = "\n\t\tbg-button-bg text-button-text \n\t\tdark:bg-button-bg-dark dark:text-button-text-dark\n\t\tfont-mono text-sm text-center \n\t\tleading-none\n\t\tborder-1\n\t\tborder-button-border dark:border-button-border-dark\n\t\trounded-md\n\t\tinline-flex items-center justify-center gap-x-2\n\t\tpx-3 py-2\n\n\t\thover:brightness-105\n\t\tactive:brightness-95\n\t\tdisabled:hover:brightness-100\n\n\t\tfocus:brightness-105\n\t\tfocus:border-button-border-focus focus:dark:border-button-border-focus-dark\n\n\t\t focus:outline-4 focus:outline-black/10 focus:dark:outline-white/20\n\t\tfocus-visible:outline-4 focus-visible:outline-black/10 focus-visible:dark:outline-white/20\n\t";
1
+ export declare const BUTTON_STUIC_BASE_CLASSES = "\n\t\tbg-button-bg text-button-text \n\t\tdark:bg-button-bg-dark dark:text-button-text-dark\n\t\tfont-mono text-sm text-center \n\t\tleading-none\n\t\tborder-1\n\t\tborder-button-border dark:border-button-border-dark\n\t\trounded-md\n\t\tinline-flex items-center justify-center gap-x-2\n\t\tpx-3 py-2\n\t\tselect-none\n\n\t\thover:brightness-105\n\t\tactive:brightness-95\n\t\tdisabled:hover:brightness-100 disabled:opacity-50\n\n\t\tfocus:brightness-105\n\t\tfocus:border-button-border-focus focus:dark:border-button-border-focus-dark\n\n\t\t focus:outline-4 focus:outline-black/10 focus:dark:outline-white/20\n\t\tfocus-visible:outline-4 focus-visible:outline-black/10 focus-visible:dark:outline-white/20\n\t";
2
2
  export declare const BUTTON_STUIC_PRESET_CLASSES: any;
3
3
  import type { Snippet } from "svelte";
4
4
  import type { HTMLButtonAttributes } from "svelte/elements";
@@ -1,6 +1,36 @@
1
+ <script lang="ts" module>
2
+ interface BodyStyles {
3
+ position: string | null;
4
+ top: string | null;
5
+ width: string | null;
6
+ overflow: string | null;
7
+ }
8
+
9
+ function get_body_style(): BodyStyles {
10
+ // const style = window.getComputedStyle(document.body);
11
+ const style = document.body.style; // we want only explicitly defined, not computed
12
+ return {
13
+ position: style.position || null,
14
+ top: style.top || null,
15
+ width: style.width || null,
16
+ overflow: style.overflow || null,
17
+ };
18
+ }
19
+
20
+ function restore_body_styles(original: BodyStyles) {
21
+ (["position", "top", "width", "overflow"] as (keyof BodyStyles)[]).forEach((k) => {
22
+ if (original[k] !== null) {
23
+ document.body.style[k] = original[k];
24
+ } else {
25
+ document.body.style.removeProperty(k);
26
+ }
27
+ });
28
+ }
29
+ </script>
30
+
1
31
  <script lang="ts">
2
32
  import { onClickOutside } from "runed";
3
- import { onMount, tick, type Snippet } from "svelte";
33
+ import { onDestroy, onMount, tick, type Snippet } from "svelte";
4
34
  import { focusTrap } from "../../actions/focus-trap.js";
5
35
  import { stopPropagation } from "../../utils/event-modifiers.js";
6
36
  import { twMerge } from "../../utils/tw-merge.js";
@@ -76,29 +106,34 @@
76
106
  () => !noClickOutsideClose && close()
77
107
  );
78
108
 
79
- let _original: any = {};
109
+ // lock body scroll when open and restore back
110
+ let _original: BodyStyles = {
111
+ position: null,
112
+ top: null,
113
+ width: null,
114
+ overflow: null,
115
+ };
116
+
117
+ function _restore() {
118
+ const scrollY = document.body.style.top;
119
+ restore_body_styles(_original);
120
+ // Restore scroll position
121
+ window.scrollTo(0, parseInt(scrollY || "0") * -1);
122
+ }
123
+
80
124
  $effect(() => {
81
- // if (noScrollLock) return;
82
125
  if (visible) {
83
- _original = window.getComputedStyle(document.body);
126
+ _original = get_body_style();
84
127
  const scrollY = window.scrollY;
85
-
86
128
  document.body.style.position = "fixed";
87
129
  document.body.style.top = `-${scrollY}px`;
88
130
  document.body.style.width = "100%";
89
131
  document.body.style.overflow = "hidden";
90
132
  } else {
91
- const scrollY = document.body.style.top;
92
-
93
- document.body.style.position = _original.position;
94
- document.body.style.position = "";
95
- document.body.style.top = "";
96
- document.body.style.width = "";
97
- document.body.style.overflow = "";
98
-
99
- // Restore scroll position
100
- window.scrollTo(0, parseInt(scrollY || "0") * -1);
133
+ _restore();
101
134
  }
135
+ // also onDestroy (will also reset if nested... which is not desired, but ignoring currently)
136
+ return _restore;
102
137
  });
103
138
 
104
139
  // $inspect("Modal dialog mounted, is visible:", visible).with(clog);
package/dist/index.css CHANGED
@@ -6,7 +6,7 @@
6
6
  @source "./";
7
7
 
8
8
  /* "components" looks like a better fit here, but forms plugin uses "utilities"
9
- so, since we need to override, sticking with that*/
9
+ so, since we need to override, sticking with that */
10
10
  @layer utilities {
11
11
  @import "./actions/tooltip/index.css";
12
12
  @import "./components/Button/index.css";
@@ -29,6 +29,6 @@ so, since we need to override, sticking with that*/
29
29
  [role="button"]:disabled,
30
30
  input:disabled {
31
31
  cursor: not-allowed !important;
32
- opacity: 0.5 !important;
32
+ /* opacity: 0.5 !important; moved to Button itself, so it can be overridden */
33
33
  }
34
34
  }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Converts a CSS HEX color string to an Oklch color string.
3
+ */
4
+ export declare function hexToOklch(hex: string): string;
5
+ /**
6
+ * Converts a sRGB (0-255) to an Oklch color string.
7
+ */
8
+ export declare function rgbToOklch(rgb: {
9
+ r: number;
10
+ g: number;
11
+ b: number;
12
+ }): string;
13
+ /**
14
+ * Converts a HEX string to an RGB object.
15
+ */
16
+ export declare function hexToRgb(hex: string): {
17
+ r: number;
18
+ g: number;
19
+ b: number;
20
+ } | null;
21
+ /**
22
+ * Converts sRGB (0-255) to linear RGB (0.0-1.0).
23
+ */
24
+ export declare function srgbToLinearRgb({ r, g, b }: {
25
+ r: number;
26
+ g: number;
27
+ b: number;
28
+ }): {
29
+ r: number;
30
+ g: number;
31
+ b: number;
32
+ };
33
+ /**
34
+ * Converts linear RGB (0.0-1.0) to Oklab.
35
+ */
36
+ export declare function linearRgbToOklab({ r, g, b }: {
37
+ r: number;
38
+ g: number;
39
+ b: number;
40
+ }): {
41
+ l: number;
42
+ a: number;
43
+ b: number;
44
+ };
45
+ /**
46
+ * Converts Oklab to Oklch.
47
+ */
48
+ export declare function oklabToOklch({ l, a, b }: {
49
+ l: number;
50
+ a: number;
51
+ b: number;
52
+ }): {
53
+ l: number;
54
+ c: number;
55
+ h: number;
56
+ };
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Converts a CSS HEX color string to an Oklch color string.
3
+ */
4
+ export function hexToOklch(hex) {
5
+ // 1. Parse HEX to RGB
6
+ const rgb = hexToRgb(hex);
7
+ if (!rgb) {
8
+ throw new Error("Invalid HEX color format.");
9
+ }
10
+ // 2. Convert sRGB (0-255) to oklab
11
+ return rgbToOklch(rgb);
12
+ }
13
+ /**
14
+ * Converts a sRGB (0-255) to an Oklch color string.
15
+ */
16
+ export function rgbToOklch(rgb) {
17
+ // 1. Convert sRGB (0-255) to Linear RGB (0.0-1.0)
18
+ const linearRgb = srgbToLinearRgb(rgb);
19
+ // 2. Convert Linear RGB to Oklab
20
+ const oklab = linearRgbToOklab(linearRgb);
21
+ // 3. Convert Oklab to Oklch
22
+ const oklch = oklabToOklch(oklab);
23
+ // 4. Format as CSS string
24
+ // L is 0-1, formatted as 0-100%
25
+ // C is 0-0.4 (approx), formatted as a number
26
+ // h is 0-360, formatted as a number (degrees)
27
+ const l = (oklch.l * 100).toFixed(1);
28
+ const c = oklch.c.toFixed(3);
29
+ // Handle hue: NaN becomes 0
30
+ const h = isNaN(oklch.h) ? "0" : oklch.h.toFixed(1);
31
+ return `oklch(${l}% ${c} ${h})`;
32
+ }
33
+ /**
34
+ * Converts a HEX string to an RGB object.
35
+ */
36
+ export function hexToRgb(hex) {
37
+ let shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
38
+ hex = hex.replace(shorthandRegex, (m, r, g, b) => {
39
+ return r + r + g + g + b + b;
40
+ });
41
+ let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
42
+ return result
43
+ ? {
44
+ r: parseInt(result[1], 16),
45
+ g: parseInt(result[2], 16),
46
+ b: parseInt(result[3], 16),
47
+ }
48
+ : null;
49
+ }
50
+ /**
51
+ * Converts sRGB (0-255) to linear RGB (0.0-1.0).
52
+ */
53
+ export function srgbToLinearRgb({ r, g, b }) {
54
+ const convert = (val) => {
55
+ let v = val / 255;
56
+ return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
57
+ };
58
+ return {
59
+ r: convert(r),
60
+ g: convert(g),
61
+ b: convert(b),
62
+ };
63
+ }
64
+ /**
65
+ * Converts linear RGB (0.0-1.0) to Oklab.
66
+ */
67
+ export function linearRgbToOklab({ r, g, b }) {
68
+ // Based on the conversion matrices from the Oklab color space specification
69
+ const l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;
70
+ const m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;
71
+ const s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b;
72
+ const l_ = Math.cbrt(l);
73
+ const m_ = Math.cbrt(m);
74
+ const s_ = Math.cbrt(s);
75
+ return {
76
+ l: 0.2104542553 * l_ + 0.793617785 * m_ - 0.0040720468 * s_,
77
+ a: 1.9779984951 * l_ - 2.428592205 * m_ + 0.4505937099 * s_,
78
+ b: 0.0259040371 * l_ + 0.7827717662 * m_ - 0.808675766 * s_,
79
+ };
80
+ }
81
+ /**
82
+ * Converts Oklab to Oklch.
83
+ */
84
+ export function oklabToOklch({ l, a, b }) {
85
+ const c = Math.sqrt(a * a + b * b);
86
+ let h = Math.atan2(b, a) * (180 / Math.PI);
87
+ // Normalize hue to be between 0 and 360
88
+ if (h < 0) {
89
+ h += 360;
90
+ }
91
+ return { l, c, h };
92
+ }
@@ -1,12 +1,11 @@
1
1
  export * from "./breakpoint.svelte.js";
2
+ export * from "./colors.js";
2
3
  export * from "./debounce.js";
3
4
  export * from "./device-pointer.svelte.js";
4
5
  export * from "./escape-regex.js";
5
6
  export * from "./event-emitter.js";
6
7
  export * from "./event-modifiers.js";
7
8
  export * from "./get-id.js";
8
- export * from "./hex-to-oklch.js";
9
- export * from "./hex-to-rgb.js";
10
9
  export * from "./is-browser.js";
11
10
  export * from "./is-mac.js";
12
11
  export * from "./is-nullish.js";
@@ -1,12 +1,11 @@
1
1
  export * from "./breakpoint.svelte.js";
2
+ export * from "./colors.js";
2
3
  export * from "./debounce.js";
3
4
  export * from "./device-pointer.svelte.js";
4
5
  export * from "./escape-regex.js";
5
6
  export * from "./event-emitter.js";
6
7
  export * from "./event-modifiers.js";
7
8
  export * from "./get-id.js";
8
- export * from "./hex-to-oklch.js";
9
- export * from "./hex-to-rgb.js";
10
9
  export * from "./is-browser.js";
11
10
  export * from "./is-mac.js";
12
11
  export * from "./is-nullish.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/stuic",
3
- "version": "2.1.6",
3
+ "version": "2.1.8",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",
@@ -1 +0,0 @@
1
- export {};
@@ -1,53 +0,0 @@
1
- "use strict";
2
- /**
3
- * Converts a hex color string to OKLCH
4
- * @param {string} hex - The hex color string (with or without leading #)
5
- * @returns {Object} An object with l (lightness), c (chroma), and h (hue) properties
6
- */
7
- function hexToOklch(hex) {
8
- // Remove the hash if it exists
9
- hex = hex.replace(/^#/, "");
10
- // Handle both 3-digit and 6-digit formats
11
- if (hex.length === 3) {
12
- hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
13
- }
14
- // Parse the hex values to RGB (0-1)
15
- const r = parseInt(hex.substring(0, 2), 16) / 255;
16
- const g = parseInt(hex.substring(2, 4), 16) / 255;
17
- const b = parseInt(hex.substring(4, 6), 16) / 255;
18
- // Convert RGB to linear RGB (removing gamma correction)
19
- const linearR = r <= 0.04045 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4);
20
- const linearG = g <= 0.04045 ? g / 12.92 : Math.pow((g + 0.055) / 1.055, 2.4);
21
- const linearB = b <= 0.04045 ? b / 12.92 : Math.pow((b + 0.055) / 1.055, 2.4);
22
- // Convert to XYZ
23
- const x = 0.4124 * linearR + 0.3576 * linearG + 0.1805 * linearB;
24
- const y = 0.2126 * linearR + 0.7152 * linearG + 0.0722 * linearB;
25
- const z = 0.0193 * linearR + 0.1192 * linearG + 0.9505 * linearB;
26
- // Convert XYZ to LMS (cone response)
27
- const l = 0.819 * x + 0.3619 * y - 0.1289 * z;
28
- const m = 0.0329 * x + 0.9293 * y + 0.0361 * z;
29
- const s = 0.0482 * x + 0.2645 * y + 0.6886 * z;
30
- // Apply non-linearity to LMS
31
- const lp = Math.cbrt(l);
32
- const mp = Math.cbrt(m);
33
- const sp = Math.cbrt(s);
34
- // Convert to Oklab
35
- const L = 0.2104 * lp + 0.7936 * mp - 0.004 * sp;
36
- const a = 1.9779 * lp - 2.4285 * mp + 0.4505 * sp;
37
- const bb = 0.0259 * lp + 0.7827 * mp - 0.8086 * sp;
38
- // Convert Oklab to Oklch
39
- const C = Math.sqrt(a * a + bb * bb);
40
- let h = (Math.atan2(bb, a) * 180) / Math.PI;
41
- // Ensure hue is positive
42
- if (h < 0) {
43
- h += 360;
44
- }
45
- return {
46
- l: parseFloat(L.toFixed(4)),
47
- c: parseFloat(C.toFixed(4)),
48
- h: parseFloat(h.toFixed(2)),
49
- };
50
- }
51
- // Example usage:
52
- // const oklch = hexToOklch("#ff5733");
53
- // console.log(oklch); // Example output: { l: 0.6321, c: 0.1549, h: 27.23 }
@@ -1,6 +0,0 @@
1
- /** Will convert #fff or #ffffff to {r: 255, g: 255, b: 255} */
2
- export declare function hexToRgb(hex: string): {
3
- r: number;
4
- g: number;
5
- b: number;
6
- };
@@ -1,12 +0,0 @@
1
- /** Will convert #fff or #ffffff to {r: 255, g: 255, b: 255} */
2
- export function hexToRgb(hex) {
3
- hex = hex.replace(/^#/, "");
4
- // both 3-digit and 6-digit formats
5
- if (hex.length === 3) {
6
- hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
7
- }
8
- const r = parseInt(hex.substring(0, 2), 16);
9
- const g = parseInt(hex.substring(2, 4), 16);
10
- const b = parseInt(hex.substring(4, 6), 16);
11
- return { r, g, b };
12
- }