asterui 0.12.52 → 0.12.55

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,120 +1,112 @@
1
- import { jsxs as z, jsx as R } from "react/jsx-runtime";
2
- import { useState as V, useMemo as P, useEffect as Y } from "react";
3
- const D = "asterui", O = 0.22, U = (t, c = O) => {
4
- const r = `rgba(0,0,0,${c})`;
5
- if (t && t.includes("var(--bc")) {
6
- if (typeof window < "u") {
7
- const a = getComputedStyle(document.documentElement).getPropertyValue("--bc").trim(), g = getComputedStyle(document.body).getPropertyValue("--bc").trim(), m = a || g;
8
- if (m)
9
- return t.replace(/var\(--bc\)/g, m);
10
- }
11
- return r;
12
- }
13
- if (t) return t;
14
- if (typeof window > "u") return r;
15
- const i = getComputedStyle(document.documentElement).getPropertyValue("--bc").trim(), d = getComputedStyle(document.body).getPropertyValue("--bc").trim(), s = i || d;
16
- return s ? `hsl(${s} / ${c})` : r;
17
- }, M = (t, c) => {
18
- const r = t?.fontSize ?? 16;
1
+ import { jsxs as R, jsx as Y } from "react/jsx-runtime";
2
+ import { useState as D, useMemo as A, useEffect as M } from "react";
3
+ import { useTheme as U } from "../hooks/useTheme.js";
4
+ const B = "asterui", X = 0.22;
5
+ function _(r, l) {
6
+ const n = Math.round(l * 255).toString(16).padStart(2, "0");
7
+ return r + n;
8
+ }
9
+ const K = (r, l) => {
10
+ const n = r?.fontSize ?? 16;
19
11
  return {
20
- color: c,
21
- fontSize: r,
22
- fontWeight: t?.fontWeight ?? 600,
23
- fontStyle: t?.fontStyle ?? "normal",
24
- fontFamily: t?.fontFamily ?? "sans-serif",
25
- lineHeight: t?.lineHeight ?? r * 1.2
12
+ color: l,
13
+ fontSize: n,
14
+ fontWeight: r?.fontWeight ?? 600,
15
+ fontStyle: r?.fontStyle ?? "normal",
16
+ fontFamily: r?.fontFamily ?? "sans-serif",
17
+ lineHeight: r?.lineHeight ?? n * 1.2
26
18
  };
27
- }, X = ({
28
- children: t,
29
- className: c = "",
30
- style: r,
31
- content: i,
32
- image: d,
33
- width: s = 120,
34
- height: a = 64,
35
- gap: g,
36
- offset: m,
37
- rotate: T = -22,
38
- zIndex: W = 1e3,
39
- font: S,
40
- ...A
19
+ }, q = ({
20
+ children: r,
21
+ className: l = "",
22
+ style: n,
23
+ content: c,
24
+ image: v,
25
+ width: a = 120,
26
+ height: i = 64,
27
+ gap: E,
28
+ offset: k,
29
+ rotate: F = -22,
30
+ zIndex: I = 1e3,
31
+ font: y,
32
+ ...b
41
33
  }) => {
42
- const [y, $] = V(null), u = g?.[0] ?? 120, f = g?.[1] ?? 120, B = m?.[0] ?? u / 2, F = m?.[1] ?? f / 2, E = P(
43
- () => typeof i == "string" ? [i] : Array.isArray(i) ? i : [D],
44
- [i]
45
- ), k = U(S?.color), l = P(
46
- () => M(S, k),
47
- [S, k]
48
- ), h = Math.PI / 180 * T, I = E.join("|");
49
- Y(() => {
34
+ const [m, L] = D(null), d = E?.[0] ?? 120, f = E?.[1] ?? 120, H = k?.[0] ?? d / 2, N = k?.[1] ?? f / 2, { colors: P } = U(), h = A(
35
+ () => typeof c == "string" ? [c] : Array.isArray(c) ? c : [B],
36
+ [c]
37
+ ), W = y?.color ?? _(P.foreground, X), s = A(
38
+ () => K(y, W),
39
+ [y, W]
40
+ ), S = Math.PI / 180 * F, j = h.join("|");
41
+ M(() => {
50
42
  if (typeof window > "u") return;
51
- let p = !1;
52
- const n = window.devicePixelRatio || 1, w = s + u, C = a + f, v = document.createElement("canvas");
53
- v.width = w * n, v.height = C * n;
54
- const e = v.getContext("2d");
43
+ let u = !1;
44
+ const o = window.devicePixelRatio || 1, $ = a + d, T = i + f, g = document.createElement("canvas");
45
+ g.width = $ * o, g.height = T * o;
46
+ const e = g.getContext("2d");
55
47
  if (!e) return;
56
- const L = () => {
57
- const o = v.toDataURL();
58
- p || $({ url: o, width: w, height: C });
59
- }, N = () => {
60
- e.save(), e.translate((u / 2 + s / 2) * n, (f / 2 + a / 2) * n), e.rotate(h), e.fillStyle = l.color, e.textAlign = "center", e.textBaseline = "middle", e.font = `${l.fontStyle} normal ${l.fontWeight} ${l.fontSize * n}px ${l.fontFamily}`;
61
- const o = l.lineHeight * n, b = -((E.length - 1) * o) / 2;
62
- E.forEach((x, j) => {
63
- e.fillText(x, 0, b + j * o);
48
+ const w = () => {
49
+ const t = g.toDataURL();
50
+ u || L({ url: t, width: $, height: T });
51
+ }, C = () => {
52
+ e.save(), e.translate((d / 2 + a / 2) * o, (f / 2 + i / 2) * o), e.rotate(S), e.fillStyle = s.color, e.textAlign = "center", e.textBaseline = "middle", e.font = `${s.fontStyle} normal ${s.fontWeight} ${s.fontSize * o}px ${s.fontFamily}`;
53
+ const t = s.lineHeight * o, p = -((h.length - 1) * t) / 2;
54
+ h.forEach((x, O) => {
55
+ e.fillText(x, 0, p + O * t);
64
56
  }), e.restore();
65
57
  };
66
- if (d) {
67
- const o = new Image();
68
- o.crossOrigin = "anonymous", o.referrerPolicy = "no-referrer";
69
- const b = () => {
70
- e.save(), e.translate((u / 2 + s / 2) * n, (f / 2 + a / 2) * n), e.rotate(h), e.drawImage(
71
- o,
72
- -s / 2 * n,
73
- -a / 2 * n,
74
- s * n,
75
- a * n
76
- ), e.restore(), L();
58
+ if (v) {
59
+ const t = new Image();
60
+ t.crossOrigin = "anonymous", t.referrerPolicy = "no-referrer";
61
+ const p = () => {
62
+ e.save(), e.translate((d / 2 + a / 2) * o, (f / 2 + i / 2) * o), e.rotate(S), e.drawImage(
63
+ t,
64
+ -a / 2 * o,
65
+ -i / 2 * o,
66
+ a * o,
67
+ i * o
68
+ ), e.restore(), w();
77
69
  }, x = () => {
78
- p || $(null);
70
+ u || L(null);
79
71
  };
80
- return o.addEventListener("load", b), o.addEventListener("error", x), o.src = d, () => {
81
- p = !0, o.removeEventListener("load", b), o.removeEventListener("error", x);
72
+ return t.addEventListener("load", p), t.addEventListener("error", x), t.src = v, () => {
73
+ u = !0, t.removeEventListener("load", p), t.removeEventListener("error", x);
82
74
  };
83
75
  } else
84
- N(), L();
76
+ C(), w();
85
77
  return () => {
86
- p = !0;
78
+ u = !0;
87
79
  };
88
80
  }, [
89
- l,
90
- u,
91
- f,
92
- a,
81
+ s,
93
82
  d,
94
- h,
95
- I,
96
- s
83
+ f,
84
+ i,
85
+ v,
86
+ S,
87
+ j,
88
+ a
97
89
  ]);
98
- const H = ["relative", c].filter(Boolean).join(" ");
99
- return /* @__PURE__ */ z(
90
+ const z = ["relative", l].filter(Boolean).join(" ");
91
+ return /* @__PURE__ */ R(
100
92
  "div",
101
93
  {
102
- className: H,
103
- style: { position: r?.position ?? "relative", ...r },
104
- ...A,
94
+ className: z,
95
+ style: { position: n?.position ?? "relative", ...n },
96
+ ...b,
105
97
  children: [
106
- t,
107
- y && /* @__PURE__ */ R(
98
+ r,
99
+ m && /* @__PURE__ */ Y(
108
100
  "div",
109
101
  {
110
102
  "aria-hidden": !0,
111
103
  className: "pointer-events-none absolute inset-0",
112
104
  style: {
113
- zIndex: W,
114
- backgroundImage: `url(${y.url})`,
105
+ zIndex: I,
106
+ backgroundImage: `url(${m.url})`,
115
107
  backgroundRepeat: "repeat",
116
- backgroundSize: `${y.width}px ${y.height}px`,
117
- backgroundPosition: `${B}px ${F}px`
108
+ backgroundSize: `${m.width}px ${m.height}px`,
109
+ backgroundPosition: `${H}px ${N}px`
118
110
  }
119
111
  }
120
112
  )
@@ -122,8 +114,8 @@ const D = "asterui", O = 0.22, U = (t, c = O) => {
122
114
  }
123
115
  );
124
116
  };
125
- X.displayName = "Watermark";
117
+ q.displayName = "Watermark";
126
118
  export {
127
- X as Watermark
119
+ q as Watermark
128
120
  };
129
121
  //# sourceMappingURL=Watermark.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Watermark.js","sources":["../../src/components/Watermark.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useState } from 'react'\n\nexport type WatermarkGap = [number, number]\nexport type WatermarkOffset = [number, number]\n\nexport interface WatermarkFontOptions {\n /** Text color for watermark content */\n color?: string\n /** Font size in pixels */\n fontSize?: number\n /** Font weight for watermark text */\n fontWeight?: number | 'normal' | 'bold' | 'bolder' | 'lighter'\n /** Font style for watermark text */\n fontStyle?: 'normal' | 'italic' | 'oblique'\n /** Font family for watermark text */\n fontFamily?: string\n /** Line height in pixels for multi-line content */\n lineHeight?: number\n}\n\nexport interface WatermarkProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'content'> {\n /** Text to render inside the watermark; falls back to \"asterui\" */\n content?: string | string[]\n /** Image source (URL or base64) to render instead of text */\n image?: string\n /** Width of a single watermark tile in pixels */\n width?: number\n /** Height of a single watermark tile in pixels */\n height?: number\n /** Horizontal/vertical gap between watermarks in pixels */\n gap?: WatermarkGap\n /** Offset for the first watermark tile from the top-left corner */\n offset?: WatermarkOffset\n /** Rotation angle in degrees */\n rotate?: number\n /** z-index for the overlay layer */\n zIndex?: number\n /** Font settings for text watermarks */\n font?: WatermarkFontOptions\n /** Content to protect with the watermark */\n children?: React.ReactNode\n}\n\ntype WatermarkImage = {\n url: string\n width: number\n height: number\n}\n\nconst DEFAULT_CONTENT = 'asterui'\nconst DEFAULT_OPACITY = 0.22\n\nconst resolveThemeColor = (requested?: string, opacity = DEFAULT_OPACITY) => {\n const fallback = `rgba(0,0,0,${opacity})`\n\n if (requested && requested.includes('var(--bc')) {\n if (typeof window !== 'undefined') {\n const docBase = getComputedStyle(document.documentElement).getPropertyValue('--bc').trim()\n const bodyBase = getComputedStyle(document.body).getPropertyValue('--bc').trim()\n const base = docBase || bodyBase\n if (base) {\n return requested.replace(/var\\(--bc\\)/g, base)\n }\n }\n return fallback\n }\n\n if (requested) return requested\n\n if (typeof window === 'undefined') return fallback\n\n const docBase = getComputedStyle(document.documentElement).getPropertyValue('--bc').trim()\n const bodyBase = getComputedStyle(document.body).getPropertyValue('--bc').trim()\n const base = docBase || bodyBase\n\n return base ? `hsl(${base} / ${opacity})` : fallback\n}\n\nconst getFontSettings = (font: WatermarkFontOptions | undefined, resolvedColor: string) => {\n const fontSize = font?.fontSize ?? 16\n\n return {\n color: resolvedColor,\n fontSize,\n fontWeight: font?.fontWeight ?? 600,\n fontStyle: font?.fontStyle ?? 'normal',\n fontFamily: font?.fontFamily ?? 'sans-serif',\n lineHeight: font?.lineHeight ?? fontSize * 1.2,\n }\n}\n\nexport const Watermark: React.FC<WatermarkProps> = ({\n children,\n className = '',\n style,\n content,\n image,\n width = 120,\n height = 64,\n gap,\n offset,\n rotate = -22,\n zIndex = 1000,\n font,\n ...rest\n}) => {\n const [watermark, setWatermark] = useState<WatermarkImage | null>(null)\n\n const gapX = gap?.[0] ?? 120\n const gapY = gap?.[1] ?? 120\n const offsetX = offset?.[0] ?? gapX / 2\n const offsetY = offset?.[1] ?? gapY / 2\n const textLines = useMemo(\n () =>\n typeof content === 'string'\n ? [content]\n : Array.isArray(content)\n ? content\n : [DEFAULT_CONTENT],\n [content]\n )\n const resolvedColor = resolveThemeColor(font?.color)\n const fontSettings = useMemo(\n () => getFontSettings(font, resolvedColor),\n [font, resolvedColor]\n )\n const rotationInRadians = (Math.PI / 180) * rotate\n const textKey = textLines.join('|')\n\n useEffect(() => {\n if (typeof window === 'undefined') return\n\n let cancelled = false\n const ratio = window.devicePixelRatio || 1\n const tileWidth = width + gapX\n const tileHeight = height + gapY\n const canvas = document.createElement('canvas')\n canvas.width = tileWidth * ratio\n canvas.height = tileHeight * ratio\n const ctx = canvas.getContext('2d')\n\n if (!ctx) return\n\n const commitWatermark = () => {\n const url = canvas.toDataURL()\n if (!cancelled) {\n setWatermark({ url, width: tileWidth, height: tileHeight })\n }\n }\n\n const drawText = () => {\n ctx.save()\n ctx.translate((gapX / 2 + width / 2) * ratio, (gapY / 2 + height / 2) * ratio)\n ctx.rotate(rotationInRadians)\n ctx.fillStyle = fontSettings.color\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n ctx.font = `${fontSettings.fontStyle} normal ${fontSettings.fontWeight} ${fontSettings.fontSize * ratio}px ${fontSettings.fontFamily}`\n\n const lineHeight = fontSettings.lineHeight * ratio\n const startY = -((textLines.length - 1) * lineHeight) / 2\n\n textLines.forEach((line, index) => {\n ctx.fillText(line, 0, startY + index * lineHeight)\n })\n\n ctx.restore()\n }\n\n if (image) {\n const img = new Image()\n img.crossOrigin = 'anonymous'\n img.referrerPolicy = 'no-referrer'\n\n const handleLoad = () => {\n ctx.save()\n ctx.translate((gapX / 2 + width / 2) * ratio, (gapY / 2 + height / 2) * ratio)\n ctx.rotate(rotationInRadians)\n ctx.drawImage(\n img,\n (-width / 2) * ratio,\n (-height / 2) * ratio,\n width * ratio,\n height * ratio\n )\n ctx.restore()\n commitWatermark()\n }\n\n const handleError = () => {\n if (!cancelled) setWatermark(null)\n }\n\n img.addEventListener('load', handleLoad)\n img.addEventListener('error', handleError)\n img.src = image\n\n return () => {\n cancelled = true\n img.removeEventListener('load', handleLoad)\n img.removeEventListener('error', handleError)\n }\n } else {\n drawText()\n commitWatermark()\n }\n\n return () => {\n cancelled = true\n }\n }, [\n fontSettings,\n gapX,\n gapY,\n height,\n image,\n rotationInRadians,\n textKey,\n width,\n ])\n\n const classes = ['relative', className].filter(Boolean).join(' ')\n\n return (\n <div\n className={classes}\n style={{ position: style?.position ?? 'relative', ...style }}\n {...rest}\n >\n {children}\n {watermark && (\n <div\n aria-hidden\n className=\"pointer-events-none absolute inset-0\"\n style={{\n zIndex,\n backgroundImage: `url(${watermark.url})`,\n backgroundRepeat: 'repeat',\n backgroundSize: `${watermark.width}px ${watermark.height}px`,\n backgroundPosition: `${offsetX}px ${offsetY}px`,\n }}\n />\n )}\n </div>\n )\n}\n\nWatermark.displayName = 'Watermark'\n"],"names":["DEFAULT_CONTENT","DEFAULT_OPACITY","resolveThemeColor","requested","opacity","fallback","docBase","bodyBase","base","getFontSettings","font","resolvedColor","fontSize","Watermark","children","className","style","content","image","width","height","gap","offset","rotate","zIndex","rest","watermark","setWatermark","useState","gapX","gapY","offsetX","offsetY","textLines","useMemo","fontSettings","rotationInRadians","textKey","useEffect","cancelled","ratio","tileWidth","tileHeight","canvas","ctx","commitWatermark","url","drawText","lineHeight","startY","line","index","img","handleLoad","handleError","classes","jsxs","jsx"],"mappings":"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"}
1
+ {"version":3,"file":"Watermark.js","sources":["../../src/components/Watermark.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useState } from 'react'\nimport { useTheme } from '../hooks/useTheme'\n\nexport type WatermarkGap = [number, number]\nexport type WatermarkOffset = [number, number]\n\nexport interface WatermarkFontOptions {\n /** Text color for watermark content */\n color?: string\n /** Font size in pixels */\n fontSize?: number\n /** Font weight for watermark text */\n fontWeight?: number | 'normal' | 'bold' | 'bolder' | 'lighter'\n /** Font style for watermark text */\n fontStyle?: 'normal' | 'italic' | 'oblique'\n /** Font family for watermark text */\n fontFamily?: string\n /** Line height in pixels for multi-line content */\n lineHeight?: number\n}\n\nexport interface WatermarkProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'content'> {\n /** Text to render inside the watermark; falls back to \"asterui\" */\n content?: string | string[]\n /** Image source (URL or base64) to render instead of text */\n image?: string\n /** Width of a single watermark tile in pixels */\n width?: number\n /** Height of a single watermark tile in pixels */\n height?: number\n /** Horizontal/vertical gap between watermarks in pixels */\n gap?: WatermarkGap\n /** Offset for the first watermark tile from the top-left corner */\n offset?: WatermarkOffset\n /** Rotation angle in degrees */\n rotate?: number\n /** z-index for the overlay layer */\n zIndex?: number\n /** Font settings for text watermarks */\n font?: WatermarkFontOptions\n /** Content to protect with the watermark */\n children?: React.ReactNode\n}\n\ntype WatermarkImage = {\n url: string\n width: number\n height: number\n}\n\nconst DEFAULT_CONTENT = 'asterui'\nconst DEFAULT_OPACITY = 0.22\n\n// Add opacity to a hex color (returns #rrggbbaa format)\nfunction hexWithOpacity(hex: string, opacity: number): string {\n const alpha = Math.round(opacity * 255).toString(16).padStart(2, '0')\n return hex + alpha\n}\n\nconst getFontSettings = (font: WatermarkFontOptions | undefined, resolvedColor: string) => {\n const fontSize = font?.fontSize ?? 16\n\n return {\n color: resolvedColor,\n fontSize,\n fontWeight: font?.fontWeight ?? 600,\n fontStyle: font?.fontStyle ?? 'normal',\n fontFamily: font?.fontFamily ?? 'sans-serif',\n lineHeight: font?.lineHeight ?? fontSize * 1.2,\n }\n}\n\nexport const Watermark: React.FC<WatermarkProps> = ({\n children,\n className = '',\n style,\n content,\n image,\n width = 120,\n height = 64,\n gap,\n offset,\n rotate = -22,\n zIndex = 1000,\n font,\n ...rest\n}) => {\n const [watermark, setWatermark] = useState<WatermarkImage | null>(null)\n\n const gapX = gap?.[0] ?? 120\n const gapY = gap?.[1] ?? 120\n const offsetX = offset?.[0] ?? gapX / 2\n const offsetY = offset?.[1] ?? gapY / 2\n const { colors } = useTheme()\n const textLines = useMemo(\n () =>\n typeof content === 'string'\n ? [content]\n : Array.isArray(content)\n ? content\n : [DEFAULT_CONTENT],\n [content]\n )\n // Use provided color or theme foreground with opacity\n const resolvedColor = font?.color ?? hexWithOpacity(colors.foreground, DEFAULT_OPACITY)\n const fontSettings = useMemo(\n () => getFontSettings(font, resolvedColor),\n [font, resolvedColor]\n )\n const rotationInRadians = (Math.PI / 180) * rotate\n const textKey = textLines.join('|')\n\n useEffect(() => {\n if (typeof window === 'undefined') return\n\n let cancelled = false\n const ratio = window.devicePixelRatio || 1\n const tileWidth = width + gapX\n const tileHeight = height + gapY\n const canvas = document.createElement('canvas')\n canvas.width = tileWidth * ratio\n canvas.height = tileHeight * ratio\n const ctx = canvas.getContext('2d')\n\n if (!ctx) return\n\n const commitWatermark = () => {\n const url = canvas.toDataURL()\n if (!cancelled) {\n setWatermark({ url, width: tileWidth, height: tileHeight })\n }\n }\n\n const drawText = () => {\n ctx.save()\n ctx.translate((gapX / 2 + width / 2) * ratio, (gapY / 2 + height / 2) * ratio)\n ctx.rotate(rotationInRadians)\n ctx.fillStyle = fontSettings.color\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n ctx.font = `${fontSettings.fontStyle} normal ${fontSettings.fontWeight} ${fontSettings.fontSize * ratio}px ${fontSettings.fontFamily}`\n\n const lineHeight = fontSettings.lineHeight * ratio\n const startY = -((textLines.length - 1) * lineHeight) / 2\n\n textLines.forEach((line, index) => {\n ctx.fillText(line, 0, startY + index * lineHeight)\n })\n\n ctx.restore()\n }\n\n if (image) {\n const img = new Image()\n img.crossOrigin = 'anonymous'\n img.referrerPolicy = 'no-referrer'\n\n const handleLoad = () => {\n ctx.save()\n ctx.translate((gapX / 2 + width / 2) * ratio, (gapY / 2 + height / 2) * ratio)\n ctx.rotate(rotationInRadians)\n ctx.drawImage(\n img,\n (-width / 2) * ratio,\n (-height / 2) * ratio,\n width * ratio,\n height * ratio\n )\n ctx.restore()\n commitWatermark()\n }\n\n const handleError = () => {\n if (!cancelled) setWatermark(null)\n }\n\n img.addEventListener('load', handleLoad)\n img.addEventListener('error', handleError)\n img.src = image\n\n return () => {\n cancelled = true\n img.removeEventListener('load', handleLoad)\n img.removeEventListener('error', handleError)\n }\n } else {\n drawText()\n commitWatermark()\n }\n\n return () => {\n cancelled = true\n }\n }, [\n fontSettings,\n gapX,\n gapY,\n height,\n image,\n rotationInRadians,\n textKey,\n width,\n ])\n\n const classes = ['relative', className].filter(Boolean).join(' ')\n\n return (\n <div\n className={classes}\n style={{ position: style?.position ?? 'relative', ...style }}\n {...rest}\n >\n {children}\n {watermark && (\n <div\n aria-hidden\n className=\"pointer-events-none absolute inset-0\"\n style={{\n zIndex,\n backgroundImage: `url(${watermark.url})`,\n backgroundRepeat: 'repeat',\n backgroundSize: `${watermark.width}px ${watermark.height}px`,\n backgroundPosition: `${offsetX}px ${offsetY}px`,\n }}\n />\n )}\n </div>\n )\n}\n\nWatermark.displayName = 'Watermark'\n"],"names":["DEFAULT_CONTENT","DEFAULT_OPACITY","hexWithOpacity","hex","opacity","alpha","getFontSettings","font","resolvedColor","fontSize","Watermark","children","className","style","content","image","width","height","gap","offset","rotate","zIndex","rest","watermark","setWatermark","useState","gapX","gapY","offsetX","offsetY","colors","useTheme","textLines","useMemo","fontSettings","rotationInRadians","textKey","useEffect","cancelled","ratio","tileWidth","tileHeight","canvas","ctx","commitWatermark","url","drawText","lineHeight","startY","line","index","img","handleLoad","handleError","classes","jsxs","jsx"],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { default as React } from 'react';
2
+ export type Size = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
3
+ export declare const SizeContext: React.Context<Size | undefined>;
4
+ export interface SizeProviderProps {
5
+ /** Size to provide to child components */
6
+ size: Size;
7
+ /** Child components */
8
+ children: React.ReactNode;
9
+ }
10
+ /**
11
+ * Provides size context to child components.
12
+ *
13
+ * Used by Button, CopyButton, and other sized components to transmit
14
+ * their size to child icons, allowing icons to automatically match
15
+ * the parent component's size.
16
+ */
17
+ export declare function SizeProvider({ size, children }: SizeProviderProps): import("react/jsx-runtime").JSX.Element;
18
+ /**
19
+ * Hook to access the current size context.
20
+ * Returns undefined if not within a SizeProvider.
21
+ */
22
+ export declare function useSize(): Size | undefined;
@@ -0,0 +1,15 @@
1
+ import { jsx as r } from "react/jsx-runtime";
2
+ import { createContext as i, useContext as n } from "react";
3
+ const e = i(void 0);
4
+ function c({ size: t, children: o }) {
5
+ return /* @__PURE__ */ r(e.Provider, { value: t, children: o });
6
+ }
7
+ function f() {
8
+ return n(e);
9
+ }
10
+ export {
11
+ e as SizeContext,
12
+ c as SizeProvider,
13
+ f as useSize
14
+ };
15
+ //# sourceMappingURL=SizeContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SizeContext.js","sources":["../../src/contexts/SizeContext.tsx"],"sourcesContent":["import React, { createContext, useContext } from 'react'\n\nexport type Size = 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n\nexport const SizeContext = createContext<Size | undefined>(undefined)\n\nexport interface SizeProviderProps {\n /** Size to provide to child components */\n size: Size\n /** Child components */\n children: React.ReactNode\n}\n\n/**\n * Provides size context to child components.\n *\n * Used by Button, CopyButton, and other sized components to transmit\n * their size to child icons, allowing icons to automatically match\n * the parent component's size.\n */\nexport function SizeProvider({ size, children }: SizeProviderProps) {\n return (\n <SizeContext.Provider value={size}>\n {children}\n </SizeContext.Provider>\n )\n}\n\n/**\n * Hook to access the current size context.\n * Returns undefined if not within a SizeProvider.\n */\nexport function useSize(): Size | undefined {\n return useContext(SizeContext)\n}\n"],"names":["SizeContext","createContext","SizeProvider","size","children","useSize","useContext"],"mappings":";;AAIO,MAAMA,IAAcC,EAAgC,MAAS;AAgB7D,SAASC,EAAa,EAAE,MAAAC,GAAM,UAAAC,KAA+B;AAClE,2BACGJ,EAAY,UAAZ,EAAqB,OAAOG,GAC1B,UAAAC,GACH;AAEJ;AAMO,SAASC,IAA4B;AAC1C,SAAOC,EAAWN,CAAW;AAC/B;"}
@@ -0,0 +1,39 @@
1
+ import { ThemeColors } from '../components/ThemeProvider';
2
+ export type { ThemeColors };
3
+ export interface UseThemeReturn {
4
+ /** The theme setting (what user selected). Only available with ThemeProvider. */
5
+ theme: string | undefined;
6
+ /** The actual applied theme. Only available with ThemeProvider. */
7
+ resolvedTheme: string | undefined;
8
+ /** Whether dark mode is active */
9
+ isDark: boolean;
10
+ /** Set the theme. Only available with ThemeProvider. */
11
+ setTheme: ((theme: string) => void) | undefined;
12
+ /** Computed theme colors as hex values */
13
+ colors: ThemeColors;
14
+ /** The system preference. Only available with ThemeProvider. */
15
+ systemTheme: 'light' | 'dark' | undefined;
16
+ }
17
+ /**
18
+ * Hook to detect current theme and get computed colors.
19
+ *
20
+ * When used within a ThemeProvider, returns full theme control including
21
+ * setTheme, theme selection, and resolved theme.
22
+ *
23
+ * When used standalone (without ThemeProvider), provides read-only access
24
+ * to isDark and colors based on the current data-theme attribute and
25
+ * system preference.
26
+ *
27
+ * @example
28
+ * // With ThemeProvider (full control)
29
+ * const { theme, setTheme, resolvedTheme, isDark, colors } = useTheme()
30
+ * setTheme('dark')
31
+ * setTheme('system')
32
+ *
33
+ * @example
34
+ * // Without ThemeProvider (read-only)
35
+ * const { isDark, colors } = useTheme()
36
+ * // colors.primary, colors.foreground, etc.
37
+ */
38
+ export declare function useTheme(): UseThemeReturn;
39
+ export default useTheme;
@@ -0,0 +1,117 @@
1
+ import { useState as i, useEffect as u, useMemo as m } from "react";
2
+ import { useHasThemeProvider as d, useThemeContext as l } from "../components/ThemeProvider.js";
3
+ const h = /* @__PURE__ */ new Set([
4
+ "dark",
5
+ "synthwave",
6
+ "halloween",
7
+ "forest",
8
+ "black",
9
+ "luxury",
10
+ "dracula",
11
+ "business",
12
+ "night",
13
+ "coffee",
14
+ "dim",
15
+ "sunset"
16
+ ]);
17
+ function g(r) {
18
+ if (typeof document > "u") return "#000000";
19
+ const e = document.createElement("canvas");
20
+ e.width = e.height = 1;
21
+ const t = e.getContext("2d");
22
+ if (!t) return "#000000";
23
+ t.fillStyle = r, t.fillRect(0, 0, 1, 1);
24
+ const [n, o, s] = t.getImageData(0, 0, 1, 1).data;
25
+ return `#${((1 << 24) + (n << 16) + (o << 8) + s).toString(16).slice(1)}`;
26
+ }
27
+ function a() {
28
+ if (typeof document > "u")
29
+ return {
30
+ background: "#ffffff",
31
+ foreground: "#000000",
32
+ primary: "#6366f1",
33
+ primaryContent: "#ffffff",
34
+ secondary: "#f000b8",
35
+ accent: "#37cdbe",
36
+ info: "#3abff8",
37
+ success: "#36d399",
38
+ warning: "#fbbd23",
39
+ error: "#f87272"
40
+ };
41
+ const r = getComputedStyle(document.documentElement), e = (t, n) => {
42
+ const o = r.getPropertyValue(t).trim();
43
+ return o ? g(o) : n;
44
+ };
45
+ return {
46
+ background: e("--color-base-100", "#ffffff"),
47
+ foreground: e("--color-base-content", "#000000"),
48
+ primary: e("--color-primary", "#6366f1"),
49
+ primaryContent: e("--color-primary-content", "#ffffff"),
50
+ secondary: e("--color-secondary", "#f000b8"),
51
+ accent: e("--color-accent", "#37cdbe"),
52
+ info: e("--color-info", "#3abff8"),
53
+ success: e("--color-success", "#36d399"),
54
+ warning: e("--color-warning", "#fbbd23"),
55
+ error: e("--color-error", "#f87272")
56
+ };
57
+ }
58
+ function y() {
59
+ return typeof window > "u" ? "light" : window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
60
+ }
61
+ function b() {
62
+ return typeof document > "u" ? null : document.documentElement.getAttribute("data-theme");
63
+ }
64
+ function k() {
65
+ if (d()) {
66
+ const e = l();
67
+ return {
68
+ theme: e.theme,
69
+ resolvedTheme: e.resolvedTheme,
70
+ isDark: e.isDark,
71
+ setTheme: e.setTheme,
72
+ colors: e.colors,
73
+ systemTheme: e.systemTheme
74
+ };
75
+ }
76
+ return T();
77
+ }
78
+ function T() {
79
+ const [r, e] = i(() => ({
80
+ isDark: !1,
81
+ colors: a()
82
+ }));
83
+ return u(() => {
84
+ const t = () => {
85
+ const s = b(), f = y();
86
+ let c = !1;
87
+ s ? c = h.has(s) : c = f === "dark", requestAnimationFrame(() => {
88
+ e({
89
+ isDark: c,
90
+ colors: a()
91
+ });
92
+ });
93
+ };
94
+ t();
95
+ const n = new MutationObserver(t);
96
+ n.observe(document.documentElement, {
97
+ attributes: !0,
98
+ attributeFilter: ["data-theme", "class"]
99
+ });
100
+ const o = window.matchMedia("(prefers-color-scheme: dark)");
101
+ return o.addEventListener("change", t), () => {
102
+ n.disconnect(), o.removeEventListener("change", t);
103
+ };
104
+ }, []), m(() => ({
105
+ theme: void 0,
106
+ resolvedTheme: void 0,
107
+ isDark: r.isDark,
108
+ setTheme: void 0,
109
+ colors: r.colors,
110
+ systemTheme: void 0
111
+ }), [r.isDark, r.colors]);
112
+ }
113
+ export {
114
+ k as default,
115
+ k as useTheme
116
+ };
117
+ //# sourceMappingURL=useTheme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTheme.js","sources":["../../src/hooks/useTheme.ts"],"sourcesContent":["import { useEffect, useState, useMemo } from 'react'\nimport { useHasThemeProvider, useThemeContext, type ThemeColors } from '../components/ThemeProvider'\n\nexport type { ThemeColors }\n\n// Common dark themes in DaisyUI\nconst DARK_THEMES = new Set([\n 'dark', 'synthwave', 'halloween', 'forest', 'black', 'luxury', 'dracula',\n 'business', 'night', 'coffee', 'dim', 'sunset'\n])\n\nexport interface UseThemeReturn {\n /** The theme setting (what user selected). Only available with ThemeProvider. */\n theme: string | undefined\n /** The actual applied theme. Only available with ThemeProvider. */\n resolvedTheme: string | undefined\n /** Whether dark mode is active */\n isDark: boolean\n /** Set the theme. Only available with ThemeProvider. */\n setTheme: ((theme: string) => void) | undefined\n /** Computed theme colors as hex values */\n colors: ThemeColors\n /** The system preference. Only available with ThemeProvider. */\n systemTheme: 'light' | 'dark' | undefined\n}\n\n// Convert any CSS color to hex\nfunction colorToHex(color: string): string {\n if (typeof document === 'undefined') return '#000000'\n const canvas = document.createElement('canvas')\n canvas.width = canvas.height = 1\n const ctx = canvas.getContext('2d')\n if (!ctx) return '#000000'\n ctx.fillStyle = color\n ctx.fillRect(0, 0, 1, 1)\n const [r, g, b] = ctx.getImageData(0, 0, 1, 1).data\n return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`\n}\n\nfunction getThemeColors(): ThemeColors {\n if (typeof document === 'undefined') {\n return {\n background: '#ffffff',\n foreground: '#000000',\n primary: '#6366f1',\n primaryContent: '#ffffff',\n secondary: '#f000b8',\n accent: '#37cdbe',\n info: '#3abff8',\n success: '#36d399',\n warning: '#fbbd23',\n error: '#f87272',\n }\n }\n\n const style = getComputedStyle(document.documentElement)\n const getColor = (varName: string, fallback: string): string => {\n const value = style.getPropertyValue(varName).trim()\n return value ? colorToHex(value) : fallback\n }\n\n return {\n background: getColor('--color-base-100', '#ffffff'),\n foreground: getColor('--color-base-content', '#000000'),\n primary: getColor('--color-primary', '#6366f1'),\n primaryContent: getColor('--color-primary-content', '#ffffff'),\n secondary: getColor('--color-secondary', '#f000b8'),\n accent: getColor('--color-accent', '#37cdbe'),\n info: getColor('--color-info', '#3abff8'),\n success: getColor('--color-success', '#36d399'),\n warning: getColor('--color-warning', '#fbbd23'),\n error: getColor('--color-error', '#f87272'),\n }\n}\n\nfunction getSystemTheme(): 'light' | 'dark' {\n if (typeof window === 'undefined') return 'light'\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'\n}\n\nfunction getCurrentTheme(): string | null {\n if (typeof document === 'undefined') return null\n return document.documentElement.getAttribute('data-theme')\n}\n\n/**\n * Hook to detect current theme and get computed colors.\n *\n * When used within a ThemeProvider, returns full theme control including\n * setTheme, theme selection, and resolved theme.\n *\n * When used standalone (without ThemeProvider), provides read-only access\n * to isDark and colors based on the current data-theme attribute and\n * system preference.\n *\n * @example\n * // With ThemeProvider (full control)\n * const { theme, setTheme, resolvedTheme, isDark, colors } = useTheme()\n * setTheme('dark')\n * setTheme('system')\n *\n * @example\n * // Without ThemeProvider (read-only)\n * const { isDark, colors } = useTheme()\n * // colors.primary, colors.foreground, etc.\n */\nexport function useTheme(): UseThemeReturn {\n const hasProvider = useHasThemeProvider()\n\n // If we have a provider, use its context\n if (hasProvider) {\n // This is safe because hasProvider is stable after initial render\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const context = useThemeContext()\n return {\n theme: context.theme,\n resolvedTheme: context.resolvedTheme,\n isDark: context.isDark,\n setTheme: context.setTheme,\n colors: context.colors,\n systemTheme: context.systemTheme,\n }\n }\n\n // Standalone mode - no provider\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return useThemeStandalone()\n}\n\n/**\n * Standalone theme detection (no ThemeProvider)\n */\nfunction useThemeStandalone(): UseThemeReturn {\n const [state, setState] = useState<{ isDark: boolean; colors: ThemeColors }>(() => ({\n isDark: false,\n colors: getThemeColors(),\n }))\n\n useEffect(() => {\n const updateTheme = () => {\n const currentTheme = getCurrentTheme()\n const systemTheme = getSystemTheme()\n\n // Determine if dark based on data-theme or system preference\n let isDark = false\n if (currentTheme) {\n isDark = DARK_THEMES.has(currentTheme)\n } else {\n isDark = systemTheme === 'dark'\n }\n\n // Update colors after a frame\n requestAnimationFrame(() => {\n setState({\n isDark,\n colors: getThemeColors(),\n })\n })\n }\n\n updateTheme()\n\n // Watch for theme changes via attribute mutation\n const observer = new MutationObserver(updateTheme)\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['data-theme', 'class']\n })\n\n // Watch for system preference changes\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n mediaQuery.addEventListener('change', updateTheme)\n\n return () => {\n observer.disconnect()\n mediaQuery.removeEventListener('change', updateTheme)\n }\n }, [])\n\n return useMemo(() => ({\n theme: undefined,\n resolvedTheme: undefined,\n isDark: state.isDark,\n setTheme: undefined,\n colors: state.colors,\n systemTheme: undefined,\n }), [state.isDark, state.colors])\n}\n\nexport default useTheme\n"],"names":["DARK_THEMES","colorToHex","color","canvas","ctx","r","g","b","getThemeColors","style","getColor","varName","fallback","value","getSystemTheme","getCurrentTheme","useTheme","useHasThemeProvider","context","useThemeContext","useThemeStandalone","state","setState","useState","useEffect","updateTheme","currentTheme","systemTheme","isDark","observer","mediaQuery","useMemo"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -14,8 +14,8 @@ export { Breadcrumb } from './components/Breadcrumb';
14
14
  export type { BreadcrumbProps, BreadcrumbItemProps } from './components/Breadcrumb';
15
15
  export { Button } from './components/Button';
16
16
  export type { ButtonProps } from './components/Button';
17
- export { IconSizeContext } from './contexts/IconSizeContext';
18
- export type { IconSize } from './contexts/IconSizeContext';
17
+ export { SizeContext, SizeProvider, useSize } from './contexts/SizeContext';
18
+ export type { Size } from './contexts/SizeContext';
19
19
  export { CopyButton } from './components/CopyButton';
20
20
  export type { CopyButtonProps, CopyButtonPosition } from './components/CopyButton';
21
21
  export { Checkbox } from './components/Checkbox';
@@ -174,6 +174,8 @@ export { Tag, CheckableTag, TagLiveRegion } from './components/Tag';
174
174
  export type { TagProps, CheckableTagProps, TagSize, TagColor, TagVariant } from './components/Tag';
175
175
  export { ThemeController } from './components/ThemeController';
176
176
  export type { ThemeControllerSwapProps, ThemeControllerDropdownProps, ThemeControllerToggleProps } from './components/ThemeController';
177
+ export { ThemeProvider, useThemeContext, useHasThemeProvider } from './components/ThemeProvider';
178
+ export type { ThemeProviderProps, ThemeContextValue } from './components/ThemeProvider';
177
179
  export { TimePicker } from './components/TimePicker';
178
180
  export type { TimePickerProps } from './components/TimePicker';
179
181
  export { Timeline } from './components/Timeline';
@@ -214,3 +216,5 @@ export { useKeyPress, useKeyPressCallback } from './hooks/useKeyPress';
214
216
  export type { UseKeyPressOptions } from './hooks/useKeyPress';
215
217
  export { useWindowSize } from './hooks/useWindowSize';
216
218
  export type { WindowSize } from './hooks/useWindowSize';
219
+ export { useTheme } from './hooks/useTheme';
220
+ export type { UseThemeReturn, ThemeColors } from './hooks/useTheme';