@nocoo/basalt 2.1.0 → 2.1.7

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.
Files changed (80) hide show
  1. package/ai/COMPATIBILITY.md +1 -1
  2. package/ai/INTEGRATION.md +17 -1
  3. package/ai/USAGE.md +1 -1
  4. package/ai/registry.json +80 -80
  5. package/dist/assets/brand-mark.d.ts +1 -1
  6. package/dist/assets/brand-mark.js +1 -1
  7. package/dist/assets/brand-mark.js.map +1 -1
  8. package/dist/charts/charts.d.ts +1 -1
  9. package/dist/charts/charts.js +3 -16
  10. package/dist/charts/charts.js.map +1 -1
  11. package/dist/charts/custom-chart.d.ts +1 -1
  12. package/dist/charts/custom-chart.js +3 -16
  13. package/dist/charts/custom-chart.js.map +1 -1
  14. package/dist/charts/grouped-bar.d.ts +1 -1
  15. package/dist/charts/grouped-bar.js +11 -48
  16. package/dist/charts/grouped-bar.js.map +1 -1
  17. package/dist/charts/heatmap-calendar.js +80 -136
  18. package/dist/charts/heatmap-calendar.js.map +1 -1
  19. package/dist/charts/slot-bar.js +2 -12
  20. package/dist/charts/slot-bar.js.map +1 -1
  21. package/dist/charts/stat-card.js +2 -7
  22. package/dist/charts/stat-card.js.map +1 -1
  23. package/dist/charts/timeseries.d.ts +1 -1
  24. package/dist/charts/timeseries.js +3 -16
  25. package/dist/charts/timeseries.js.map +1 -1
  26. package/dist/charts/tooltip.js +13 -51
  27. package/dist/charts/tooltip.js.map +1 -1
  28. package/dist/components/app-shell.js +2 -2
  29. package/dist/components/app-shell.js.map +1 -1
  30. package/dist/components/autocomplete.d.ts +1 -1
  31. package/dist/components/autocomplete.js +3 -16
  32. package/dist/components/autocomplete.js.map +1 -1
  33. package/dist/components/badge.js +6 -6
  34. package/dist/components/badge.js.map +1 -1
  35. package/dist/components/banner.js +28 -28
  36. package/dist/components/banner.js.map +1 -1
  37. package/dist/components/button.js +6 -7
  38. package/dist/components/button.js.map +1 -1
  39. package/dist/components/chat-composer.js +1 -2
  40. package/dist/components/chat-composer.js.map +1 -1
  41. package/dist/components/checkbox.js +50 -82
  42. package/dist/components/checkbox.js.map +1 -1
  43. package/dist/components/code.js +1 -1
  44. package/dist/components/code.js.map +1 -1
  45. package/dist/components/combobox.d.ts +1 -1
  46. package/dist/components/combobox.js +3 -16
  47. package/dist/components/combobox.js.map +1 -1
  48. package/dist/components/context-menu.d.ts +1 -67
  49. package/dist/components/context-menu.js.map +1 -1
  50. package/dist/components/data-table.js +111 -123
  51. package/dist/components/data-table.js.map +1 -1
  52. package/dist/components/date-picker.js +236 -245
  53. package/dist/components/date-picker.js.map +1 -1
  54. package/dist/components/dock.js +15 -16
  55. package/dist/components/dock.js.map +1 -1
  56. package/dist/components/field.js +20 -23
  57. package/dist/components/field.js.map +1 -1
  58. package/dist/components/layer-card.d.ts +1 -1
  59. package/dist/components/layer-card.js +44 -51
  60. package/dist/components/layer-card.js.map +1 -1
  61. package/dist/components/sidebar.js +3 -3
  62. package/dist/components/sidebar.js.map +1 -1
  63. package/dist/components/skeleton-line.d.ts +2 -2
  64. package/dist/components/skeleton-line.js +1 -1
  65. package/dist/components/skeleton-line.js.map +1 -1
  66. package/dist/components/switch.js +46 -78
  67. package/dist/components/switch.js.map +1 -1
  68. package/dist/providers/accent.js +89 -198
  69. package/dist/providers/accent.js.map +1 -1
  70. package/dist/providers/theme.js +46 -144
  71. package/dist/providers/theme.js.map +1 -1
  72. package/dist/styles/standalone.css +12 -24
  73. package/dist/styles/tokens.css +6 -6
  74. package/dist/utils/preference-store.d.ts +16 -0
  75. package/dist/utils/preference-store.js +106 -0
  76. package/dist/utils/preference-store.js.map +1 -0
  77. package/dist/utils/use-checkable-group.d.ts +19 -0
  78. package/dist/utils/use-checkable-group.js +61 -0
  79. package/dist/utils/use-checkable-group.js.map +1 -0
  80. package/package.json +1 -2
@@ -1,9 +1,10 @@
1
1
  "use client";
2
- import { createContext as e, useCallback as t, useContext as n, useEffect as r, useMemo as i, useState as a, useSyncExternalStore as o } from "react";
3
- import { jsx as s } from "react/jsx-runtime";
2
+ import { createPreferenceStore as e } from "../utils/preference-store.js";
3
+ import { createContext as t, useCallback as n, useContext as r, useEffect as i, useMemo as a, useState as o, useSyncExternalStore as s } from "react";
4
+ import { jsx as c } from "react/jsx-runtime";
4
5
  //#region src/providers/theme.tsx
5
- var c = "theme", l = e(null);
6
- function u(e) {
6
+ var l = "theme", u = t(null);
7
+ function d(e) {
7
8
  if (typeof document > "u") return;
8
9
  let t = document.documentElement, n = !1;
9
10
  try {
@@ -14,163 +15,64 @@ function u(e) {
14
15
  let r = e === "dark" || e === "system" && n;
15
16
  t.classList.toggle("dark", r), t.classList.toggle("light", !r), t.dataset.mode = r ? "dark" : "light";
16
17
  }
17
- function d(e) {
18
- return e === "light" || e === "dark" || e === "system";
19
- }
20
18
  function f(e) {
21
- try {
22
- return typeof window > "u" || !window.localStorage ? { status: "error" } : {
23
- status: "success",
24
- value: window.localStorage.getItem(e)
25
- };
26
- } catch {
27
- return { status: "error" };
28
- }
29
- }
30
- function p(e, t) {
31
- try {
32
- return typeof window > "u" || !window.localStorage ? !1 : (window.localStorage.setItem(e, t), !0);
33
- } catch {
34
- return !1;
35
- }
36
- }
37
- function m(e) {
38
- if (!e) return !0;
39
- try {
40
- if (typeof window < "u" && window.localStorage) return e === window.localStorage;
41
- } catch {
42
- return !1;
43
- }
44
- return !1;
45
- }
46
- var h = "basalt:theme-change";
47
- function g(e, t, n) {
48
- let r = e, i = t, a = n, o = i, s = !1, c = !1;
49
- if (a) {
50
- let e = f(r);
51
- e.status === "success" && d(e.value) && (o = e.value, s = !0);
52
- }
53
- let l = /* @__PURE__ */ new Set(), u = () => {
54
- for (let e of l) e();
55
- }, g = (e) => {
56
- if (!a) return;
57
- let t = e;
58
- if (!t.detail || t.detail.key !== r) return;
59
- let { value: n, writeSucceeded: i } = t.detail;
60
- c = !i, o = n, s = !0, u();
61
- }, _ = (e) => {
62
- if (!a) return;
63
- let t = e;
64
- if ("key" in t && t.key !== void 0) {
65
- if (!m(t.storageArea) || t.key !== null && t.key !== r) return;
66
- c = !1;
67
- let e = t.newValue;
68
- if (e == null || !d(e)) {
69
- o = i, s = !1, u();
70
- return;
71
- }
72
- o = e, s = !0, u();
73
- return;
74
- }
75
- if (c) return;
76
- let n = f(r);
77
- n.status !== "error" && (d(n.value) ? (o = n.value, s = !0, u()) : (o = i, s = !1, u()));
78
- }, v = null, y = () => {
79
- typeof window > "u" || l.size === 1 && !v && (window.addEventListener("storage", _), window.addEventListener(h, g), v = () => {
80
- window.removeEventListener("storage", _), window.removeEventListener(h, g), v = null;
81
- });
82
- }, b = () => {
83
- l.size === 0 && v && v();
84
- };
85
- return {
86
- getSnapshot: () => o,
87
- subscribe: (e) => (l.add(e), y(), () => {
88
- l.delete(e), b();
89
- }),
90
- setTheme: (e) => {
91
- o = e, s = !0;
92
- let t = !0;
93
- if (a) {
94
- let n = p(r, e);
95
- if (c = !n, t = n, typeof window < "u") try {
96
- window.dispatchEvent(new CustomEvent(h, { detail: {
97
- key: r,
98
- value: e,
99
- writeSucceeded: t
100
- } }));
101
- } catch {}
102
- }
103
- u();
104
- },
105
- updateConfig: (e) => {
106
- let t = r !== e.storageKey, n = a !== e.persist, l = i !== e.defaultTheme, p = a;
107
- if (r = e.storageKey, i = e.defaultTheme, a = e.persist, t) {
108
- if (a) {
109
- let e = f(r);
110
- e.status === "success" && (c = !1, d(e.value) ? (o = e.value, s = !0) : (o = i, s = !1)), u();
111
- }
112
- } else if (n) {
113
- if (!p && a) {
114
- let e = f(r);
115
- e.status === "success" && (c = !1, d(e.value) && (o = e.value, s = !0, u()));
116
- }
117
- } else if (l && !s) {
118
- if (a) {
119
- let e = f(r);
120
- e.status === "success" && !d(e.value) && (o = i, u());
121
- } else o = i, u();
122
- }
123
- }
124
- };
19
+ return e === "light" || e === "dark" || e === "system";
125
20
  }
126
- function _({ children: e, storageKey: n = c, defaultTheme: d = "system", persist: f = !0, theme: p, onThemeChange: m, applyToDocument: h = !0 }) {
127
- let _ = p !== void 0, [v] = a(() => g(n, d, f));
128
- r(() => {
129
- v.updateConfig({
130
- storageKey: n,
131
- defaultTheme: d,
132
- persist: f
21
+ function p({ children: t, storageKey: r = l, defaultTheme: p = "system", persist: m = !0, theme: h, onThemeChange: g, applyToDocument: _ = !0 }) {
22
+ let v = h !== void 0, [y] = o(() => e({
23
+ storageKey: r,
24
+ defaultValue: p,
25
+ persist: m,
26
+ isValid: f,
27
+ normalize: (e) => e,
28
+ eventName: "basalt:theme-change"
29
+ }));
30
+ i(() => {
31
+ y.updateConfig({
32
+ storageKey: r,
33
+ defaultValue: p,
34
+ persist: m
133
35
  });
134
36
  }, [
135
- v,
136
- n,
137
- d,
138
- f
37
+ y,
38
+ r,
39
+ p,
40
+ m
139
41
  ]);
140
- let y = t(() => d, [d]), b = o(v.subscribe, v.getSnapshot, y), x = _ ? p : b;
141
- r(() => {
142
- if (!(!h || typeof window > "u") && (u(x), x === "system")) try {
143
- let e = window.matchMedia("(prefers-color-scheme: dark)"), t = () => u("system");
42
+ let b = n(() => p, [p]), x = s(y.subscribe, y.getSnapshot, b), S = v ? h : x;
43
+ i(() => {
44
+ if (!(!_ || typeof window > "u") && (d(S), S === "system")) try {
45
+ let e = window.matchMedia("(prefers-color-scheme: dark)"), t = () => d("system");
144
46
  return e.addEventListener("change", t), () => e.removeEventListener("change", t);
145
47
  } catch {
146
48
  return;
147
49
  }
148
- }, [x, h]);
149
- let S = t((e) => {
150
- if (_) {
151
- m?.(e);
50
+ }, [S, _]);
51
+ let C = n((e) => {
52
+ if (v) {
53
+ g?.(e);
152
54
  return;
153
55
  }
154
- v.setTheme(e), m?.(e);
56
+ y.setValue(e), g?.(e);
155
57
  }, [
156
- _,
157
- m,
158
- v
159
- ]), C = i(() => ({
160
- theme: x,
161
- setTheme: S
162
- }), [x, S]);
163
- return /* @__PURE__ */ s(l.Provider, {
164
- value: C,
165
- children: e
58
+ v,
59
+ g,
60
+ y
61
+ ]), w = a(() => ({
62
+ theme: S,
63
+ setTheme: C
64
+ }), [S, C]);
65
+ return /* @__PURE__ */ c(u.Provider, {
66
+ value: w,
67
+ children: t
166
68
  });
167
69
  }
168
- function v() {
169
- let e = n(l);
70
+ function m() {
71
+ let e = r(u);
170
72
  if (!e) throw Error("useTheme must be used within ThemeProvider");
171
73
  return e;
172
74
  }
173
75
  //#endregion
174
- export { _ as ThemeProvider, v as useTheme };
76
+ export { p as ThemeProvider, m as useTheme };
175
77
 
176
78
  //# sourceMappingURL=theme.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"theme.js","names":[],"sources":["../../src/providers/theme.tsx"],"sourcesContent":["import {\n\tcreateContext,\n\ttype ReactNode,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseMemo,\n\tuseState,\n\tuseSyncExternalStore,\n} from \"react\";\n\nexport type BasaltTheme = \"light\" | \"dark\" | \"system\";\n\nconst STORAGE_KEY = \"theme\";\n\ntype ThemeContextValue = {\n\ttheme: BasaltTheme;\n\tsetTheme: (theme: BasaltTheme) => void;\n};\n\nconst ThemeContext = createContext<ThemeContextValue | null>(null);\n\nfunction applyTheme(theme: BasaltTheme) {\n\tif (typeof document === \"undefined\") {\n\t\treturn;\n\t}\n\tconst root = document.documentElement;\n\tlet systemDark = false;\n\ttry {\n\t\tif (typeof window !== \"undefined\" && typeof window.matchMedia === \"function\") {\n\t\t\tsystemDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n\t\t}\n\t} catch {\n\t\tsystemDark = false;\n\t}\n\tconst dark = theme === \"dark\" || (theme === \"system\" && systemDark);\n\troot.classList.toggle(\"dark\", dark);\n\troot.classList.toggle(\"light\", !dark);\n\troot.dataset.mode = dark ? \"dark\" : \"light\";\n}\n\nexport interface ThemeProviderProps {\n\t/**\n\t * Application components wrapped by the theme context.\n\t */\n\tchildren: ReactNode;\n\t/**\n\t * Storage key used for theme persistence.\n\t * @default \"theme\"\n\t */\n\tstorageKey?: string;\n\t/**\n\t * Initial theme used when no stored preference exists or during SSR.\n\t * @default \"system\"\n\t */\n\tdefaultTheme?: BasaltTheme;\n\t/**\n\t * Whether to persist theme changes to localStorage.\n\t * If false, localStorage is never read or written.\n\t * @default true\n\t */\n\tpersist?: boolean;\n\t/**\n\t * Controlled theme value. When provided, the provider acts as a controlled component\n\t * and internal state is driven by this prop.\n\t */\n\ttheme?: BasaltTheme;\n\t/**\n\t * Callback fired when theme change is requested.\n\t * In controlled mode, callers are responsible for updating `theme`.\n\t */\n\tonThemeChange?: (theme: BasaltTheme) => void;\n\t/**\n\t * Whether to apply mode classes and data-mode attribute to document.documentElement.\n\t * Set to false when a host theme system manages the document root.\n\t * @default true\n\t */\n\tapplyToDocument?: boolean;\n}\n\ninterface ThemeStore {\n\tgetSnapshot: () => BasaltTheme;\n\tsubscribe: (listener: () => void) => () => void;\n\tsetTheme: (next: BasaltTheme) => void;\n\tupdateConfig: (config: {\n\t\tstorageKey: string;\n\t\tdefaultTheme: BasaltTheme;\n\t\tpersist: boolean;\n\t}) => void;\n}\n\nfunction isValidTheme(value: unknown): value is BasaltTheme {\n\treturn value === \"light\" || value === \"dark\" || value === \"system\";\n}\n\ntype StorageReadResult = { status: \"success\"; value: string | null } | { status: \"error\" };\n\nfunction safeGetStorageItem(key: string): StorageReadResult {\n\ttry {\n\t\tif (typeof window === \"undefined\" || !window.localStorage) {\n\t\t\treturn { status: \"error\" };\n\t\t}\n\t\tconst value = window.localStorage.getItem(key);\n\t\treturn { status: \"success\", value };\n\t} catch {\n\t\treturn { status: \"error\" };\n\t}\n}\n\nfunction safeSetStorageItem(key: string, value: string): boolean {\n\ttry {\n\t\tif (typeof window === \"undefined\" || !window.localStorage) {\n\t\t\treturn false;\n\t\t}\n\t\twindow.localStorage.setItem(key, value);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction isLocalStorageArea(area: Storage | null | undefined): boolean {\n\tif (!area) {\n\t\treturn true;\n\t}\n\ttry {\n\t\tif (typeof window !== \"undefined\" && window.localStorage) {\n\t\t\treturn area === window.localStorage;\n\t\t}\n\t} catch {\n\t\t// localStorage getter threw\n\t\treturn false;\n\t}\n\treturn false;\n}\n\nconst THEME_CHANGE_EVENT = \"basalt:theme-change\";\n\ninterface ThemeChangeEventDetail {\n\tkey: string;\n\tvalue: BasaltTheme;\n\twriteSucceeded: boolean;\n}\n\nfunction createThemeStore(\n\tinitialStorageKey: string,\n\tinitialDefaultTheme: BasaltTheme,\n\tinitialPersist: boolean,\n): ThemeStore {\n\tlet storageKey = initialStorageKey;\n\tlet defaultTheme = initialDefaultTheme;\n\tlet persist = initialPersist;\n\n\t// In-memory fallback / cache value\n\tlet memoryTheme: BasaltTheme = defaultTheme;\n\tlet hasExplicitSelection = false;\n\tlet lastSetFailed = false;\n\n\tif (persist) {\n\t\tconst res = safeGetStorageItem(storageKey);\n\t\tif (res.status === \"success\" && isValidTheme(res.value)) {\n\t\t\tmemoryTheme = res.value;\n\t\t\thasExplicitSelection = true;\n\t\t}\n\t}\n\n\tconst listeners = new Set<() => void>();\n\n\tconst notify = () => {\n\t\tfor (const listener of listeners) {\n\t\t\tlistener();\n\t\t}\n\t};\n\n\tconst onInternalChange = (event: Event) => {\n\t\tif (!persist) {\n\t\t\treturn;\n\t\t}\n\t\tconst customEv = event as CustomEvent<ThemeChangeEventDetail>;\n\t\tif (!customEv.detail || customEv.detail.key !== storageKey) {\n\t\t\treturn;\n\t\t}\n\t\tconst { value, writeSucceeded } = customEv.detail;\n\t\tif (writeSucceeded) {\n\t\t\tlastSetFailed = false;\n\t\t} else {\n\t\t\tlastSetFailed = true;\n\t\t}\n\t\tmemoryTheme = value;\n\t\thasExplicitSelection = true;\n\t\tnotify();\n\t};\n\n\tconst onStorage = (event: StorageEvent | Event) => {\n\t\tif (!persist) {\n\t\t\treturn;\n\t\t}\n\t\tconst storageEv = event as Partial<StorageEvent>;\n\t\tif (\"key\" in storageEv && storageEv.key !== undefined) {\n\t\t\tif (!isLocalStorageArea(storageEv.storageArea)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (storageEv.key !== null && storageEv.key !== storageKey) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlastSetFailed = false;\n\t\t\tconst rawVal = storageEv.newValue;\n\t\t\tif (rawVal === null || rawVal === undefined || !isValidTheme(rawVal)) {\n\t\t\t\t// Key removed or invalid value: revert to configured default\n\t\t\t\tmemoryTheme = defaultTheme;\n\t\t\t\thasExplicitSelection = false;\n\t\t\t\tnotify();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tmemoryTheme = rawVal;\n\t\t\thasExplicitSelection = true;\n\t\t\tnotify();\n\t\t\treturn;\n\t\t}\n\t\t// Untyped/bare storage event: e.g. dispatchEvent(new Event(\"storage\"))\n\t\t// If last set failed, ignore so stale storage cannot overwrite in-memory switch\n\t\tif (lastSetFailed) {\n\t\t\treturn;\n\t\t}\n\t\tconst res = safeGetStorageItem(storageKey);\n\t\tif (res.status === \"error\") {\n\t\t\t// Read failed: preserve current cached memory theme, do not revert to default\n\t\t\treturn;\n\t\t}\n\t\tif (isValidTheme(res.value)) {\n\t\t\tmemoryTheme = res.value;\n\t\t\thasExplicitSelection = true;\n\t\t\tnotify();\n\t\t} else {\n\t\t\t// Key removed or invalid value stored: revert to default\n\t\t\tmemoryTheme = defaultTheme;\n\t\t\thasExplicitSelection = false;\n\t\t\tnotify();\n\t\t}\n\t};\n\n\tlet cleanupStorageListener: (() => void) | null = null;\n\tconst attachStorageListener = () => {\n\t\tif (typeof window === \"undefined\") {\n\t\t\treturn;\n\t\t}\n\t\tif (listeners.size === 1 && !cleanupStorageListener) {\n\t\t\twindow.addEventListener(\"storage\", onStorage);\n\t\t\twindow.addEventListener(THEME_CHANGE_EVENT, onInternalChange);\n\t\t\tcleanupStorageListener = () => {\n\t\t\t\twindow.removeEventListener(\"storage\", onStorage);\n\t\t\t\twindow.removeEventListener(THEME_CHANGE_EVENT, onInternalChange);\n\t\t\t\tcleanupStorageListener = null;\n\t\t\t};\n\t\t}\n\t};\n\n\tconst detachStorageListener = () => {\n\t\tif (listeners.size === 0 && cleanupStorageListener) {\n\t\t\tcleanupStorageListener();\n\t\t}\n\t};\n\n\treturn {\n\t\tgetSnapshot: () => memoryTheme,\n\t\tsubscribe: (listener: () => void) => {\n\t\t\tlisteners.add(listener);\n\t\t\tattachStorageListener();\n\t\t\treturn () => {\n\t\t\t\tlisteners.delete(listener);\n\t\t\t\tdetachStorageListener();\n\t\t\t};\n\t\t},\n\t\tsetTheme: (next: BasaltTheme) => {\n\t\t\tmemoryTheme = next;\n\t\t\thasExplicitSelection = true;\n\t\t\tlet writeSucceeded = true;\n\t\t\tif (persist) {\n\t\t\t\tconst success = safeSetStorageItem(storageKey, next);\n\t\t\t\tlastSetFailed = !success;\n\t\t\t\twriteSucceeded = success;\n\t\t\t\tif (typeof window !== \"undefined\") {\n\t\t\t\t\ttry {\n\t\t\t\t\t\twindow.dispatchEvent(\n\t\t\t\t\t\t\tnew CustomEvent<ThemeChangeEventDetail>(THEME_CHANGE_EVENT, {\n\t\t\t\t\t\t\t\tdetail: { key: storageKey, value: next, writeSucceeded },\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// ignore\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tnotify();\n\t\t},\n\t\tupdateConfig: (config) => {\n\t\t\tconst keyChanged = storageKey !== config.storageKey;\n\t\t\tconst persistChanged = persist !== config.persist;\n\t\t\tconst defaultChanged = defaultTheme !== config.defaultTheme;\n\n\t\t\tconst oldPersist = persist;\n\t\t\tstorageKey = config.storageKey;\n\t\t\tdefaultTheme = config.defaultTheme;\n\t\t\tpersist = config.persist;\n\n\t\t\tif (keyChanged) {\n\t\t\t\tif (persist) {\n\t\t\t\t\tconst res = safeGetStorageItem(storageKey);\n\t\t\t\t\tif (res.status === \"success\") {\n\t\t\t\t\t\tlastSetFailed = false;\n\t\t\t\t\t\tif (isValidTheme(res.value)) {\n\t\t\t\t\t\t\tmemoryTheme = res.value;\n\t\t\t\t\t\t\thasExplicitSelection = true;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tmemoryTheme = defaultTheme;\n\t\t\t\t\t\t\thasExplicitSelection = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tnotify();\n\t\t\t\t}\n\t\t\t\t// persist=false: changing storageKey does not reset current memoryTheme\n\t\t\t} else if (persistChanged) {\n\t\t\t\tif (!oldPersist && persist) {\n\t\t\t\t\t// persist flipped false -> true: read from storage or keep current\n\t\t\t\t\tconst res = safeGetStorageItem(storageKey);\n\t\t\t\t\tif (res.status === \"success\") {\n\t\t\t\t\t\tlastSetFailed = false;\n\t\t\t\t\t\tif (isValidTheme(res.value)) {\n\t\t\t\t\t\t\tmemoryTheme = res.value;\n\t\t\t\t\t\t\thasExplicitSelection = true;\n\t\t\t\t\t\t\tnotify();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// persist flipped true -> false: only disable persistence, keep current memoryTheme, no storage read/write\n\t\t\t} else if (defaultChanged) {\n\t\t\t\t// defaultTheme changed: only update if no explicit selection was made\n\t\t\t\tif (!hasExplicitSelection) {\n\t\t\t\t\tif (persist) {\n\t\t\t\t\t\tconst res = safeGetStorageItem(storageKey);\n\t\t\t\t\t\tif (res.status === \"success\" && !isValidTheme(res.value)) {\n\t\t\t\t\t\t\tmemoryTheme = defaultTheme;\n\t\t\t\t\t\t\tnotify();\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmemoryTheme = defaultTheme;\n\t\t\t\t\t\tnotify();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t};\n}\n\nexport function ThemeProvider({\n\tchildren,\n\tstorageKey = STORAGE_KEY,\n\tdefaultTheme = \"system\",\n\tpersist = true,\n\ttheme: controlledTheme,\n\tonThemeChange,\n\tapplyToDocument = true,\n}: ThemeProviderProps) {\n\tconst isControlled = controlledTheme !== undefined;\n\n\tconst [store] = useState(() => createThemeStore(storageKey, defaultTheme, persist));\n\n\tuseEffect(() => {\n\t\tstore.updateConfig({ storageKey, defaultTheme, persist });\n\t}, [store, storageKey, defaultTheme, persist]);\n\n\tconst getServerSnapshot = useCallback(() => defaultTheme, [defaultTheme]);\n\n\tconst storeTheme = useSyncExternalStore(store.subscribe, store.getSnapshot, getServerSnapshot);\n\n\tconst currentTheme = isControlled ? controlledTheme : storeTheme;\n\n\tuseEffect(() => {\n\t\tif (!applyToDocument || typeof window === \"undefined\") {\n\t\t\treturn;\n\t\t}\n\t\tapplyTheme(currentTheme);\n\t\tif (currentTheme !== \"system\") {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tconst mq = window.matchMedia(\"(prefers-color-scheme: dark)\");\n\t\t\tconst onChange = () => applyTheme(\"system\");\n\t\t\tmq.addEventListener(\"change\", onChange);\n\t\t\treturn () => mq.removeEventListener(\"change\", onChange);\n\t\t} catch {\n\t\t\t// matchMedia not supported or restricted\n\t\t\treturn;\n\t\t}\n\t}, [currentTheme, applyToDocument]);\n\n\tconst setTheme = useCallback(\n\t\t(next: BasaltTheme) => {\n\t\t\tif (isControlled) {\n\t\t\t\tonThemeChange?.(next);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstore.setTheme(next);\n\t\t\tonThemeChange?.(next);\n\t\t},\n\t\t[isControlled, onThemeChange, store],\n\t);\n\n\tconst value = useMemo(() => ({ theme: currentTheme, setTheme }), [currentTheme, setTheme]);\n\n\treturn <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\n}\n\nexport function useTheme() {\n\tconst ctx = useContext(ThemeContext);\n\tif (!ctx) {\n\t\tthrow new Error(\"useTheme must be used within ThemeProvider\");\n\t}\n\treturn ctx;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"theme.js","names":[],"sources":["../../src/providers/theme.tsx"],"sourcesContent":["import {\n\tcreateContext,\n\ttype ReactNode,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseMemo,\n\tuseState,\n\tuseSyncExternalStore,\n} from \"react\";\nimport { createPreferenceStore } from \"../utils/preference-store\";\n\nexport type BasaltTheme = \"light\" | \"dark\" | \"system\";\n\nconst STORAGE_KEY = \"theme\";\n\ntype ThemeContextValue = {\n\ttheme: BasaltTheme;\n\tsetTheme: (theme: BasaltTheme) => void;\n};\n\nconst ThemeContext = createContext<ThemeContextValue | null>(null);\n\nfunction applyTheme(theme: BasaltTheme) {\n\tif (typeof document === \"undefined\") {\n\t\treturn;\n\t}\n\tconst root = document.documentElement;\n\tlet systemDark = false;\n\ttry {\n\t\tif (typeof window !== \"undefined\" && typeof window.matchMedia === \"function\") {\n\t\t\tsystemDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n\t\t}\n\t} catch {\n\t\tsystemDark = false;\n\t}\n\tconst dark = theme === \"dark\" || (theme === \"system\" && systemDark);\n\troot.classList.toggle(\"dark\", dark);\n\troot.classList.toggle(\"light\", !dark);\n\troot.dataset.mode = dark ? \"dark\" : \"light\";\n}\n\nexport interface ThemeProviderProps {\n\t/**\n\t * Application components wrapped by the theme context.\n\t */\n\tchildren: ReactNode;\n\t/**\n\t * Storage key used for theme persistence.\n\t * @default \"theme\"\n\t */\n\tstorageKey?: string;\n\t/**\n\t * Initial theme used when no stored preference exists or during SSR.\n\t * @default \"system\"\n\t */\n\tdefaultTheme?: BasaltTheme;\n\t/**\n\t * Whether to persist theme changes to localStorage.\n\t * If false, localStorage is never read or written.\n\t * @default true\n\t */\n\tpersist?: boolean;\n\t/**\n\t * Controlled theme value. When provided, the provider acts as a controlled component\n\t * and internal state is driven by this prop.\n\t */\n\ttheme?: BasaltTheme;\n\t/**\n\t * Callback fired when theme change is requested.\n\t * In controlled mode, callers are responsible for updating `theme`.\n\t */\n\tonThemeChange?: (theme: BasaltTheme) => void;\n\t/**\n\t * Whether to apply mode classes and data-mode attribute to document.documentElement.\n\t * Set to false when a host theme system manages the document root.\n\t * @default true\n\t */\n\tapplyToDocument?: boolean;\n}\n\nfunction isValidTheme(value: unknown): value is BasaltTheme {\n\treturn value === \"light\" || value === \"dark\" || value === \"system\";\n}\n\nexport function ThemeProvider({\n\tchildren,\n\tstorageKey = STORAGE_KEY,\n\tdefaultTheme = \"system\",\n\tpersist = true,\n\ttheme: controlledTheme,\n\tonThemeChange,\n\tapplyToDocument = true,\n}: ThemeProviderProps) {\n\tconst isControlled = controlledTheme !== undefined;\n\n\tconst [store] = useState(() =>\n\t\tcreatePreferenceStore({\n\t\t\tstorageKey,\n\t\t\tdefaultValue: defaultTheme,\n\t\t\tpersist,\n\t\t\tisValid: isValidTheme,\n\t\t\tnormalize: (value) => value,\n\t\t\teventName: \"basalt:theme-change\",\n\t\t}),\n\t);\n\n\tuseEffect(() => {\n\t\tstore.updateConfig({ storageKey, defaultValue: defaultTheme, persist });\n\t}, [store, storageKey, defaultTheme, persist]);\n\n\tconst getServerSnapshot = useCallback(() => defaultTheme, [defaultTheme]);\n\n\tconst storeTheme = useSyncExternalStore(store.subscribe, store.getSnapshot, getServerSnapshot);\n\n\tconst currentTheme = isControlled ? controlledTheme : storeTheme;\n\n\tuseEffect(() => {\n\t\tif (!applyToDocument || typeof window === \"undefined\") {\n\t\t\treturn;\n\t\t}\n\t\tapplyTheme(currentTheme);\n\t\tif (currentTheme !== \"system\") {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tconst mq = window.matchMedia(\"(prefers-color-scheme: dark)\");\n\t\t\tconst onChange = () => applyTheme(\"system\");\n\t\t\tmq.addEventListener(\"change\", onChange);\n\t\t\treturn () => mq.removeEventListener(\"change\", onChange);\n\t\t} catch {\n\t\t\t// matchMedia not supported or restricted\n\t\t\treturn;\n\t\t}\n\t}, [currentTheme, applyToDocument]);\n\n\tconst setTheme = useCallback(\n\t\t(next: BasaltTheme) => {\n\t\t\tif (isControlled) {\n\t\t\t\tonThemeChange?.(next);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstore.setValue(next);\n\t\t\tonThemeChange?.(next);\n\t\t},\n\t\t[isControlled, onThemeChange, store],\n\t);\n\n\tconst value = useMemo(() => ({ theme: currentTheme, setTheme }), [currentTheme, setTheme]);\n\n\treturn <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\n}\n\nexport function useTheme() {\n\tconst ctx = useContext(ThemeContext);\n\tif (!ctx) {\n\t\tthrow new Error(\"useTheme must be used within ThemeProvider\");\n\t}\n\treturn ctx;\n}\n"],"mappings":";;;;;AAcA,IAAM,IAAc,SAOd,IAAe,EAAwC,IAAI;AAEjE,SAAS,EAAW,GAAoB;CACvC,IAAI,OAAO,WAAa,KACvB;CAED,IAAM,IAAO,SAAS,iBAClB,IAAa;CACjB,IAAI;EACH,AAAI,OAAO,SAAW,OAAe,OAAO,OAAO,cAAe,eACjE,IAAa,OAAO,WAAW,8BAA8B,CAAC,CAAC;CAEjE,QAAQ;EACP,IAAa;CACd;CACA,IAAM,IAAO,MAAU,UAAW,MAAU,YAAY;CAGxD,AAFA,EAAK,UAAU,OAAO,QAAQ,CAAI,GAClC,EAAK,UAAU,OAAO,SAAS,CAAC,CAAI,GACpC,EAAK,QAAQ,OAAO,IAAO,SAAS;AACrC;AAyCA,SAAS,EAAa,GAAsC;CAC3D,OAAO,MAAU,WAAW,MAAU,UAAU,MAAU;AAC3D;AAEA,SAAgB,EAAc,EAC7B,aACA,gBAAa,GACb,kBAAe,UACf,aAAU,IACV,OAAO,GACP,kBACA,qBAAkB,MACI;CACtB,IAAM,IAAe,MAAoB,KAAA,GAEnC,CAAC,KAAS,QACf,EAAsB;EACrB;EACA,cAAc;EACd;EACA,SAAS;EACT,YAAY,MAAU;EACtB,WAAW;CACZ,CAAC,CACF;CAEA,QAAgB;EACf,EAAM,aAAa;GAAE;GAAY,cAAc;GAAc;EAAQ,CAAC;CACvE,GAAG;EAAC;EAAO;EAAY;EAAc;CAAO,CAAC;CAE7C,IAAM,IAAoB,QAAkB,GAAc,CAAC,CAAY,CAAC,GAElE,IAAa,EAAqB,EAAM,WAAW,EAAM,aAAa,CAAiB,GAEvF,IAAe,IAAe,IAAkB;CAEtD,QAAgB;EACX,OAAC,KAAmB,OAAO,SAAW,SAG1C,EAAW,CAAY,GACnB,MAAiB,WAGrB,IAAI;GACH,IAAM,IAAK,OAAO,WAAW,8BAA8B,GACrD,UAAiB,EAAW,QAAQ;GAE1C,OADA,EAAG,iBAAiB,UAAU,CAAQ,SACzB,EAAG,oBAAoB,UAAU,CAAQ;EACvD,QAAQ;GAEP;EACD;CACD,GAAG,CAAC,GAAc,CAAe,CAAC;CAElC,IAAM,IAAW,GACf,MAAsB;EACtB,IAAI,GAAc;GACjB,IAAgB,CAAI;GACpB;EACD;EAEA,AADA,EAAM,SAAS,CAAI,GACnB,IAAgB,CAAI;CACrB,GACA;EAAC;EAAc;EAAe;CAAK,CACpC,GAEM,IAAQ,SAAe;EAAE,OAAO;EAAc;CAAS,IAAI,CAAC,GAAc,CAAQ,CAAC;CAEzF,OAAO,kBAAC,EAAa,UAAd;EAA8B;EAAQ;CAAgC,CAAA;AAC9E;AAEA,SAAgB,IAAW;CAC1B,IAAM,IAAM,EAAW,CAAY;CACnC,IAAI,CAAC,GACJ,MAAU,MAAM,4CAA4C;CAE7D,OAAO;AACR"}
@@ -172,11 +172,11 @@
172
172
  --basalt-accent-10: 280 53% 71%;
173
173
  --basalt-accent-11: 237 66% 69%;
174
174
  --basalt-accent-12: 210 20% 87%;
175
- --basalt-badge-green-foreground: 0 0% 10%;
175
+ --basalt-badge-green-foreground: 0 0% 100%;
176
176
  --basalt-badge-teal: var(--basalt-accent-3);
177
177
  --basalt-badge-purple: var(--basalt-accent-10);
178
- --basalt-badge-teal-foreground: 0 0% 10%;
179
- --basalt-badge-purple-foreground: 0 0% 10%;
178
+ --basalt-badge-teal-foreground: 0 0% 100%;
179
+ --basalt-badge-purple-foreground: 0 0% 100%;
180
180
  --basalt-chart-axis: 0 0% 40%;
181
181
  --basalt-chart-muted: 220 12% 86%;
182
182
  --basalt-heatmap-green-1: 113 50% 85%;
@@ -223,11 +223,11 @@
223
223
  --basalt-danger-tint: 0 40% 16%;
224
224
  --basalt-info-foreground: 0 0% 10%;
225
225
  --basalt-warning-foreground: 0 0% 10%;
226
- --basalt-badge-green-foreground: 0 0% 10%;
226
+ --basalt-badge-green-foreground: 0 0% 100%;
227
227
  --basalt-badge-teal: var(--basalt-accent-3);
228
228
  --basalt-badge-purple: var(--basalt-accent-10);
229
- --basalt-badge-teal-foreground: 0 0% 10%;
230
- --basalt-badge-purple-foreground: 0 0% 10%;
229
+ --basalt-badge-teal-foreground: 0 0% 100%;
230
+ --basalt-badge-purple-foreground: 0 0% 100%;
231
231
  --basalt-border: 0 0% 16%;
232
232
  --basalt-input: 0 0% 18%;
233
233
  --basalt-ring: var(--basalt-primary);
@@ -1193,6 +1193,12 @@
1193
1193
  .overflow-y-hidden\! {
1194
1194
  overflow-y: hidden !important;
1195
1195
  }
1196
+ .overscroll-none {
1197
+ overscroll-behavior: none;
1198
+ }
1199
+ .overscroll-y-contain {
1200
+ overscroll-behavior-y: contain;
1201
+ }
1196
1202
  .rounded {
1197
1203
  border-radius: 0.25rem;
1198
1204
  }
@@ -1735,15 +1741,6 @@
1735
1741
  .text-amber-900 {
1736
1742
  color: var(--color-amber-900);
1737
1743
  }
1738
- .text-basalt-badge-green-foreground {
1739
- color: hsl(var(--basalt-badge-green-foreground));
1740
- }
1741
- .text-basalt-badge-purple-foreground {
1742
- color: hsl(var(--basalt-badge-purple-foreground));
1743
- }
1744
- .text-basalt-badge-teal-foreground {
1745
- color: hsl(var(--basalt-badge-teal-foreground));
1746
- }
1747
1744
  .text-basalt-card-foreground {
1748
1745
  color: hsl(var(--basalt-card-foreground));
1749
1746
  }
@@ -1756,9 +1753,6 @@
1756
1753
  .text-basalt-danger {
1757
1754
  color: hsl(var(--basalt-danger));
1758
1755
  }
1759
- .text-basalt-danger-foreground {
1760
- color: hsl(var(--basalt-danger-foreground));
1761
- }
1762
1756
  .text-basalt-destructive {
1763
1757
  color: hsl(var(--basalt-destructive));
1764
1758
  }
@@ -1774,9 +1768,6 @@
1774
1768
  .text-basalt-info {
1775
1769
  color: hsl(var(--basalt-info));
1776
1770
  }
1777
- .text-basalt-info-foreground {
1778
- color: hsl(var(--basalt-info-foreground));
1779
- }
1780
1771
  .text-basalt-muted-foreground {
1781
1772
  color: hsl(var(--basalt-muted-foreground));
1782
1773
  }
@@ -1792,9 +1783,6 @@
1792
1783
  .text-basalt-warning {
1793
1784
  color: hsl(var(--basalt-warning));
1794
1785
  }
1795
- .text-basalt-warning-foreground {
1796
- color: hsl(var(--basalt-warning-foreground));
1797
- }
1798
1786
  .text-blue-800 {
1799
1787
  color: var(--color-blue-800);
1800
1788
  }
@@ -81,11 +81,11 @@
81
81
  --basalt-accent-10: 280 53% 71%;
82
82
  --basalt-accent-11: 237 66% 69%;
83
83
  --basalt-accent-12: 210 20% 87%;
84
- --basalt-badge-green-foreground: 0 0% 10%;
84
+ --basalt-badge-green-foreground: 0 0% 100%;
85
85
  --basalt-badge-teal: var(--basalt-accent-3);
86
86
  --basalt-badge-purple: var(--basalt-accent-10);
87
- --basalt-badge-teal-foreground: 0 0% 10%;
88
- --basalt-badge-purple-foreground: 0 0% 10%;
87
+ --basalt-badge-teal-foreground: 0 0% 100%;
88
+ --basalt-badge-purple-foreground: 0 0% 100%;
89
89
  --basalt-chart-axis: 0 0% 40%;
90
90
  --basalt-chart-muted: 220 12% 86%;
91
91
  --basalt-heatmap-green-1: 113 50% 85%;
@@ -134,11 +134,11 @@
134
134
  --basalt-danger-tint: 0 40% 16%;
135
135
  --basalt-info-foreground: 0 0% 10%;
136
136
  --basalt-warning-foreground: 0 0% 10%;
137
- --basalt-badge-green-foreground: 0 0% 10%;
137
+ --basalt-badge-green-foreground: 0 0% 100%;
138
138
  --basalt-badge-teal: var(--basalt-accent-3);
139
139
  --basalt-badge-purple: var(--basalt-accent-10);
140
- --basalt-badge-teal-foreground: 0 0% 10%;
141
- --basalt-badge-purple-foreground: 0 0% 10%;
140
+ --basalt-badge-teal-foreground: 0 0% 100%;
141
+ --basalt-badge-purple-foreground: 0 0% 100%;
142
142
  --basalt-border: 0 0% 16%;
143
143
  --basalt-input: 0 0% 18%;
144
144
  --basalt-ring: var(--basalt-primary);
@@ -0,0 +1,16 @@
1
+ type PreferenceConfig<T> = {
2
+ storageKey: string;
3
+ defaultValue: T;
4
+ persist: boolean;
5
+ };
6
+ export declare function createPreferenceStore<T extends string>({ storageKey, defaultValue: initialDefaultValue, persist, isValid, normalize, eventName, }: PreferenceConfig<T> & {
7
+ isValid: (value: unknown) => value is T;
8
+ normalize: (value: T) => T;
9
+ eventName: string;
10
+ }): {
11
+ getSnapshot: () => T;
12
+ subscribe: (listener: () => void) => () => void;
13
+ setValue: (next: T) => void;
14
+ updateConfig: (config: PreferenceConfig<T>) => void;
15
+ };
16
+ export {};
@@ -0,0 +1,106 @@
1
+ "use client";
2
+ //#region src/utils/preference-store.ts
3
+ function e(e) {
4
+ try {
5
+ return typeof window > "u" || !window.localStorage ? { status: "error" } : {
6
+ status: "success",
7
+ value: window.localStorage.getItem(e)
8
+ };
9
+ } catch {
10
+ return { status: "error" };
11
+ }
12
+ }
13
+ function t(e, t) {
14
+ try {
15
+ return typeof window > "u" || !window.localStorage ? !1 : (window.localStorage.setItem(e, t), !0);
16
+ } catch {
17
+ return !1;
18
+ }
19
+ }
20
+ function n(e) {
21
+ if (!e) return !0;
22
+ try {
23
+ if (typeof window < "u" && window.localStorage) return e === window.localStorage;
24
+ } catch {
25
+ return !1;
26
+ }
27
+ return !1;
28
+ }
29
+ function r({ storageKey: r, defaultValue: i, persist: a, isValid: o, normalize: s, eventName: c }) {
30
+ let l = s(i), u = l, d = !1, f = !1;
31
+ if (a) {
32
+ let t = e(r);
33
+ t.status === "success" && o(t.value) && (u = s(t.value), d = !0);
34
+ }
35
+ let p = /* @__PURE__ */ new Set(), m = () => {
36
+ for (let e of p) e();
37
+ }, h = (e) => {
38
+ if (!a) return;
39
+ let t = e;
40
+ if (!t.detail || t.detail.key !== r) return;
41
+ let { value: n, writeSucceeded: i } = t.detail;
42
+ f = !i, u = n, d = !0, m();
43
+ }, g = (t) => {
44
+ if (!a) return;
45
+ let i = t;
46
+ if ("key" in i && i.key !== void 0) {
47
+ if (!n(i.storageArea) || i.key !== null && i.key !== r) return;
48
+ f = !1;
49
+ let e = i.newValue;
50
+ e == null || !o(e) ? (u = l, d = !1) : (u = s(e), d = !0), m();
51
+ return;
52
+ }
53
+ if (f) return;
54
+ let c = e(r);
55
+ c.status !== "error" && (o(c.value) ? (u = s(c.value), d = !0) : (u = l, d = !1), m());
56
+ }, _ = null, v = () => {
57
+ typeof window > "u" || p.size === 1 && !_ && (window.addEventListener("storage", g), window.addEventListener(c, h), _ = () => {
58
+ window.removeEventListener("storage", g), window.removeEventListener(c, h), _ = null;
59
+ });
60
+ }, y = () => {
61
+ p.size === 0 && _ && _();
62
+ };
63
+ return {
64
+ getSnapshot: () => u,
65
+ subscribe: (e) => (p.add(e), v(), () => {
66
+ p.delete(e), y();
67
+ }),
68
+ setValue: (e) => {
69
+ let n = s(e);
70
+ if (u = n, d = !0, a) {
71
+ let e = t(r, n);
72
+ if (f = !e, typeof window < "u") try {
73
+ window.dispatchEvent(new CustomEvent(c, { detail: {
74
+ key: r,
75
+ value: n,
76
+ writeSucceeded: e
77
+ } }));
78
+ } catch {}
79
+ }
80
+ m();
81
+ },
82
+ updateConfig: (t) => {
83
+ let n = r !== t.storageKey, i = a !== t.persist, c = s(t.defaultValue), p = l !== c, h = a;
84
+ if (r = t.storageKey, l = c, a = t.persist, n) {
85
+ if (a) {
86
+ let t = e(r);
87
+ t.status === "success" && (f = !1, o(t.value) ? (u = s(t.value), d = !0) : (u = l, d = !1)), m();
88
+ }
89
+ } else if (i) {
90
+ if (!h && a) {
91
+ let t = e(r);
92
+ t.status === "success" && (f = !1, o(t.value) && (u = s(t.value), d = !0, m()));
93
+ }
94
+ } else if (p && !d) {
95
+ if (a) {
96
+ let t = e(r);
97
+ t.status === "success" && !o(t.value) && (u = l, m());
98
+ } else u = l, m();
99
+ }
100
+ }
101
+ };
102
+ }
103
+ //#endregion
104
+ export { r as createPreferenceStore };
105
+
106
+ //# sourceMappingURL=preference-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preference-store.js","names":[],"sources":["../../src/utils/preference-store.ts"],"sourcesContent":["type PreferenceConfig<T> = {\n\tstorageKey: string;\n\tdefaultValue: T;\n\tpersist: boolean;\n};\n\ntype StorageReadResult = { status: \"success\"; value: string | null } | { status: \"error\" };\n\nfunction safeGetStorageItem(key: string): StorageReadResult {\n\ttry {\n\t\tif (typeof window === \"undefined\" || !window.localStorage) {\n\t\t\treturn { status: \"error\" };\n\t\t}\n\t\tconst value = window.localStorage.getItem(key);\n\t\treturn { status: \"success\", value };\n\t} catch {\n\t\treturn { status: \"error\" };\n\t}\n}\n\nfunction safeSetStorageItem(key: string, value: string): boolean {\n\ttry {\n\t\tif (typeof window === \"undefined\" || !window.localStorage) {\n\t\t\treturn false;\n\t\t}\n\t\twindow.localStorage.setItem(key, value);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction isLocalStorageArea(area: Storage | null | undefined): boolean {\n\tif (!area) {\n\t\treturn true;\n\t}\n\ttry {\n\t\tif (typeof window !== \"undefined\" && window.localStorage) {\n\t\t\treturn area === window.localStorage;\n\t\t}\n\t} catch {\n\t\treturn false;\n\t}\n\treturn false;\n}\n\ninterface PreferenceChangeEventDetail<T> {\n\tkey: string;\n\tvalue: T;\n\twriteSucceeded: boolean;\n}\n\nexport function createPreferenceStore<T extends string>({\n\tstorageKey,\n\tdefaultValue: initialDefaultValue,\n\tpersist,\n\tisValid,\n\tnormalize,\n\teventName,\n}: PreferenceConfig<T> & {\n\tisValid: (value: unknown) => value is T;\n\tnormalize: (value: T) => T;\n\teventName: string;\n}) {\n\tlet defaultValue = normalize(initialDefaultValue);\n\tlet memoryValue = defaultValue;\n\tlet hasExplicitSelection = false;\n\tlet lastSetFailed = false;\n\n\tif (persist) {\n\t\tconst res = safeGetStorageItem(storageKey);\n\t\tif (res.status === \"success\" && isValid(res.value)) {\n\t\t\tmemoryValue = normalize(res.value);\n\t\t\thasExplicitSelection = true;\n\t\t}\n\t}\n\n\tconst listeners = new Set<() => void>();\n\tconst notify = () => {\n\t\tfor (const listener of listeners) {\n\t\t\tlistener();\n\t\t}\n\t};\n\n\tconst onInternalChange = (event: Event) => {\n\t\tif (!persist) {\n\t\t\treturn;\n\t\t}\n\t\tconst customEv = event as CustomEvent<PreferenceChangeEventDetail<T>>;\n\t\tif (!customEv.detail || customEv.detail.key !== storageKey) {\n\t\t\treturn;\n\t\t}\n\t\tconst { value, writeSucceeded } = customEv.detail;\n\t\tlastSetFailed = !writeSucceeded;\n\t\tmemoryValue = value;\n\t\thasExplicitSelection = true;\n\t\tnotify();\n\t};\n\n\tconst onStorage = (event: StorageEvent | Event) => {\n\t\tif (!persist) {\n\t\t\treturn;\n\t\t}\n\t\tconst storageEv = event as Partial<StorageEvent>;\n\t\tif (\"key\" in storageEv && storageEv.key !== undefined) {\n\t\t\tif (!isLocalStorageArea(storageEv.storageArea)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (storageEv.key !== null && storageEv.key !== storageKey) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlastSetFailed = false;\n\t\t\tconst rawValue = storageEv.newValue;\n\t\t\tif (rawValue === null || rawValue === undefined || !isValid(rawValue)) {\n\t\t\t\tmemoryValue = defaultValue;\n\t\t\t\thasExplicitSelection = false;\n\t\t\t} else {\n\t\t\t\tmemoryValue = normalize(rawValue);\n\t\t\t\thasExplicitSelection = true;\n\t\t\t}\n\t\t\tnotify();\n\t\t\treturn;\n\t\t}\n\t\t// A bare storage event must not replace a selection whose write failed.\n\t\tif (lastSetFailed) {\n\t\t\treturn;\n\t\t}\n\t\tconst res = safeGetStorageItem(storageKey);\n\t\tif (res.status === \"error\") {\n\t\t\treturn;\n\t\t}\n\t\tif (isValid(res.value)) {\n\t\t\tmemoryValue = normalize(res.value);\n\t\t\thasExplicitSelection = true;\n\t\t} else {\n\t\t\tmemoryValue = defaultValue;\n\t\t\thasExplicitSelection = false;\n\t\t}\n\t\tnotify();\n\t};\n\n\tlet cleanupStorageListener: (() => void) | null = null;\n\tconst attachStorageListener = () => {\n\t\tif (typeof window === \"undefined\") {\n\t\t\treturn;\n\t\t}\n\t\tif (listeners.size === 1 && !cleanupStorageListener) {\n\t\t\twindow.addEventListener(\"storage\", onStorage);\n\t\t\twindow.addEventListener(eventName, onInternalChange);\n\t\t\tcleanupStorageListener = () => {\n\t\t\t\twindow.removeEventListener(\"storage\", onStorage);\n\t\t\t\twindow.removeEventListener(eventName, onInternalChange);\n\t\t\t\tcleanupStorageListener = null;\n\t\t\t};\n\t\t}\n\t};\n\n\tconst detachStorageListener = () => {\n\t\tif (listeners.size === 0 && cleanupStorageListener) {\n\t\t\tcleanupStorageListener();\n\t\t}\n\t};\n\n\treturn {\n\t\tgetSnapshot: () => memoryValue,\n\t\tsubscribe: (listener: () => void) => {\n\t\t\tlisteners.add(listener);\n\t\t\tattachStorageListener();\n\t\t\treturn () => {\n\t\t\t\tlisteners.delete(listener);\n\t\t\t\tdetachStorageListener();\n\t\t\t};\n\t\t},\n\t\tsetValue: (next: T) => {\n\t\t\tconst value = normalize(next);\n\t\t\tmemoryValue = value;\n\t\t\thasExplicitSelection = true;\n\t\t\tif (persist) {\n\t\t\t\tconst writeSucceeded = safeSetStorageItem(storageKey, value);\n\t\t\t\tlastSetFailed = !writeSucceeded;\n\t\t\t\tif (typeof window !== \"undefined\") {\n\t\t\t\t\ttry {\n\t\t\t\t\t\twindow.dispatchEvent(\n\t\t\t\t\t\t\tnew CustomEvent<PreferenceChangeEventDetail<T>>(eventName, {\n\t\t\t\t\t\t\t\tdetail: { key: storageKey, value, writeSucceeded },\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// The local selection still applies when dispatch is unavailable.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tnotify();\n\t\t},\n\t\tupdateConfig: (config: PreferenceConfig<T>) => {\n\t\t\tconst keyChanged = storageKey !== config.storageKey;\n\t\t\tconst persistChanged = persist !== config.persist;\n\t\t\tconst normalizedDefault = normalize(config.defaultValue);\n\t\t\tconst defaultChanged = defaultValue !== normalizedDefault;\n\n\t\t\tconst oldPersist = persist;\n\t\t\tstorageKey = config.storageKey;\n\t\t\tdefaultValue = normalizedDefault;\n\t\t\tpersist = config.persist;\n\n\t\t\tif (keyChanged) {\n\t\t\t\tif (persist) {\n\t\t\t\t\tconst res = safeGetStorageItem(storageKey);\n\t\t\t\t\tif (res.status === \"success\") {\n\t\t\t\t\t\tlastSetFailed = false;\n\t\t\t\t\t\tif (isValid(res.value)) {\n\t\t\t\t\t\t\tmemoryValue = normalize(res.value);\n\t\t\t\t\t\t\thasExplicitSelection = true;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tmemoryValue = defaultValue;\n\t\t\t\t\t\t\thasExplicitSelection = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tnotify();\n\t\t\t\t}\n\t\t\t} else if (persistChanged) {\n\t\t\t\tif (!oldPersist && persist) {\n\t\t\t\t\tconst res = safeGetStorageItem(storageKey);\n\t\t\t\t\tif (res.status === \"success\") {\n\t\t\t\t\t\tlastSetFailed = false;\n\t\t\t\t\t\tif (isValid(res.value)) {\n\t\t\t\t\t\t\tmemoryValue = normalize(res.value);\n\t\t\t\t\t\t\thasExplicitSelection = true;\n\t\t\t\t\t\t\tnotify();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (defaultChanged) {\n\t\t\t\tif (!hasExplicitSelection) {\n\t\t\t\t\tif (persist) {\n\t\t\t\t\t\tconst res = safeGetStorageItem(storageKey);\n\t\t\t\t\t\tif (res.status === \"success\" && !isValid(res.value)) {\n\t\t\t\t\t\t\tmemoryValue = defaultValue;\n\t\t\t\t\t\t\tnotify();\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmemoryValue = defaultValue;\n\t\t\t\t\t\tnotify();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t};\n}\n"],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import * as React from "react";
2
+ export declare function useCheckableGroup({ value, defaultValue, onValueChange, error, describedBy, ariaInvalid, form, ref, }: {
3
+ value?: string[];
4
+ defaultValue?: string[];
5
+ onValueChange?: (value: string[]) => void;
6
+ error?: React.ReactNode;
7
+ describedBy?: string;
8
+ ariaInvalid?: React.AriaAttributes["aria-invalid"];
9
+ form?: string;
10
+ ref: React.ForwardedRef<HTMLFieldSetElement>;
11
+ }): {
12
+ current: string[];
13
+ setValue: (next: string[]) => void;
14
+ invalid: boolean;
15
+ errorId: string;
16
+ mergedDescribedBy: string | undefined;
17
+ setRefs: (node: HTMLFieldSetElement | null) => (() => void) | undefined;
18
+ ariaInvalid: "false" | "grammar" | "spelling" | "true" | boolean | undefined;
19
+ };
@@ -0,0 +1,61 @@
1
+ "use client";
2
+ import * as e from "react";
3
+ //#region src/utils/use-checkable-group.ts
4
+ function t({ value: t, defaultValue: n, onValueChange: r, error: i, describedBy: a, ariaInvalid: o, form: s, ref: c }) {
5
+ let l = e.useId(), u = e.useRef(null), [d, f] = e.useState(n ?? []), p = t ?? d, m = !!i, h = `${l}-error`, g = [m ? h : null, a].filter(Boolean).join(" ") || void 0, _ = e.useRef(null), v = e.useCallback((e) => {
6
+ let n = _.current;
7
+ n && n.eventPhase !== Event.NONE || (t === void 0 && f(e), r?.(e));
8
+ }, [r, t]), y = e.useCallback((e) => {
9
+ if (u.current = e, c) {
10
+ if (typeof c == "function") {
11
+ let t = c(e);
12
+ return typeof t == "function" ? () => {
13
+ u.current = null, t();
14
+ } : () => {
15
+ u.current = null, c(null);
16
+ };
17
+ }
18
+ return c.current = e, () => {
19
+ u.current = null, c.current = null;
20
+ };
21
+ }
22
+ }, [c]), b = e.useRef({
23
+ defaultValue: n,
24
+ value: t
25
+ });
26
+ return e.useEffect(() => {
27
+ b.current = {
28
+ defaultValue: n,
29
+ value: t
30
+ };
31
+ }), e.useEffect(() => {
32
+ let e = u.current?.form;
33
+ if (!e || s && e.id !== s) return;
34
+ let t = !1, n = /* @__PURE__ */ new Set(), r = (e) => {
35
+ _.current = e;
36
+ let r = setTimeout(() => {
37
+ if (n.delete(r), t || e.defaultPrevented) return;
38
+ let i = b.current;
39
+ i.value === void 0 && f(i.defaultValue ?? []);
40
+ }, 0);
41
+ n.add(r);
42
+ };
43
+ return e.addEventListener("reset", r, !0), () => {
44
+ t = !0, _.current = null;
45
+ for (let e of n) clearTimeout(e);
46
+ n.clear(), e.removeEventListener("reset", r, !0);
47
+ };
48
+ }, [s]), {
49
+ current: p,
50
+ setValue: v,
51
+ invalid: m,
52
+ errorId: h,
53
+ mergedDescribedBy: g,
54
+ setRefs: y,
55
+ ariaInvalid: m ? !0 : o
56
+ };
57
+ }
58
+ //#endregion
59
+ export { t as useCheckableGroup };
60
+
61
+ //# sourceMappingURL=use-checkable-group.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-checkable-group.js","names":[],"sources":["../../src/utils/use-checkable-group.ts"],"sourcesContent":["import * as React from \"react\";\n\nexport function useCheckableGroup({\n\tvalue,\n\tdefaultValue,\n\tonValueChange,\n\terror,\n\tdescribedBy,\n\tariaInvalid,\n\tform,\n\tref,\n}: {\n\tvalue?: string[];\n\tdefaultValue?: string[];\n\tonValueChange?: (value: string[]) => void;\n\terror?: React.ReactNode;\n\tdescribedBy?: string;\n\tariaInvalid?: React.AriaAttributes[\"aria-invalid\"];\n\tform?: string;\n\tref: React.ForwardedRef<HTMLFieldSetElement>;\n}) {\n\tconst generatedId = React.useId();\n\tconst nodeRef = React.useRef<HTMLFieldSetElement | null>(null);\n\tconst [uncontrolled, setUncontrolled] = React.useState(defaultValue ?? []);\n\tconst current = value ?? uncontrolled;\n\tconst invalid = Boolean(error);\n\tconst errorId = `${generatedId}-error`;\n\tconst mergedDescribedBy =\n\t\t[invalid ? errorId : null, describedBy].filter(Boolean).join(\" \") || undefined;\n\tconst resetEventRef = React.useRef<Event | null>(null);\n\tconst setValue = React.useCallback(\n\t\t(next: string[]) => {\n\t\t\tconst activeReset = resetEventRef.current;\n\t\t\tif (activeReset && activeReset.eventPhase !== Event.NONE) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (value === undefined) {\n\t\t\t\tsetUncontrolled(next);\n\t\t\t}\n\t\t\tonValueChange?.(next);\n\t\t},\n\t\t[onValueChange, value],\n\t);\n\tconst setRefs = React.useCallback(\n\t\t(node: HTMLFieldSetElement | null) => {\n\t\t\tnodeRef.current = node;\n\t\t\tif (!ref) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (typeof ref === \"function\") {\n\t\t\t\tconst cleanup = (ref as React.RefCallback<HTMLFieldSetElement>)(node);\n\t\t\t\tif (typeof cleanup === \"function\") {\n\t\t\t\t\treturn () => {\n\t\t\t\t\t\tnodeRef.current = null;\n\t\t\t\t\t\tcleanup();\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn () => {\n\t\t\t\t\tnodeRef.current = null;\n\t\t\t\t\tref(null);\n\t\t\t\t};\n\t\t\t}\n\t\t\t(ref as React.RefObject<HTMLFieldSetElement | null>).current = node;\n\t\t\treturn () => {\n\t\t\t\tnodeRef.current = null;\n\t\t\t\t(ref as React.RefObject<HTMLFieldSetElement | null>).current = null;\n\t\t\t};\n\t\t},\n\t\t[ref],\n\t);\n\tconst latestConfigRef = React.useRef({\n\t\tdefaultValue,\n\t\tvalue,\n\t});\n\tReact.useEffect(() => {\n\t\tlatestConfigRef.current = {\n\t\t\tdefaultValue,\n\t\t\tvalue,\n\t\t};\n\t});\n\n\tReact.useEffect(() => {\n\t\tconst formEl = nodeRef.current?.form;\n\t\tif (!formEl || (form && formEl.id !== form)) {\n\t\t\treturn;\n\t\t}\n\t\tlet disposed = false;\n\t\tconst timers = new Set<ReturnType<typeof setTimeout>>();\n\t\tconst onReset = (event: Event) => {\n\t\t\tresetEventRef.current = event;\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\ttimers.delete(timer);\n\t\t\t\tif (disposed || event.defaultPrevented) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst cfg = latestConfigRef.current;\n\t\t\t\tif (cfg.value === undefined) {\n\t\t\t\t\tsetUncontrolled(cfg.defaultValue ?? []);\n\t\t\t\t}\n\t\t\t}, 0);\n\t\t\ttimers.add(timer);\n\t\t};\n\t\tformEl.addEventListener(\"reset\", onReset, true);\n\t\treturn () => {\n\t\t\tdisposed = true;\n\t\t\tresetEventRef.current = null;\n\t\t\tfor (const timer of timers) {\n\t\t\t\tclearTimeout(timer);\n\t\t\t}\n\t\t\ttimers.clear();\n\t\t\tformEl.removeEventListener(\"reset\", onReset, true);\n\t\t};\n\t}, [form]);\n\n\treturn {\n\t\tcurrent,\n\t\tsetValue,\n\t\tinvalid,\n\t\terrorId,\n\t\tmergedDescribedBy,\n\t\tsetRefs,\n\t\tariaInvalid: invalid ? true : ariaInvalid,\n\t};\n}\n"],"mappings":";;;AAEA,SAAgB,EAAkB,EACjC,UACA,iBACA,kBACA,UACA,gBACA,gBACA,SACA,UAUE;CACF,IAAM,IAAc,EAAM,MAAM,GAC1B,IAAU,EAAM,OAAmC,IAAI,GACvD,CAAC,GAAc,KAAmB,EAAM,SAAS,KAAgB,CAAC,CAAC,GACnE,IAAU,KAAS,GACnB,IAAU,EAAQ,GAClB,IAAU,GAAG,EAAY,SACzB,IACL,CAAC,IAAU,IAAU,MAAM,CAAW,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,KAAK,KAAA,GAChE,IAAgB,EAAM,OAAqB,IAAI,GAC/C,IAAW,EAAM,aACrB,MAAmB;EACnB,IAAM,IAAc,EAAc;EAC9B,KAAe,EAAY,eAAe,MAAM,SAGhD,MAAU,KAAA,KACb,EAAgB,CAAI,GAErB,IAAgB,CAAI;CACrB,GACA,CAAC,GAAe,CAAK,CACtB,GACM,IAAU,EAAM,aACpB,MAAqC;EACrC,MAAQ,UAAU,GACb,GAGL;OAAI,OAAO,KAAQ,YAAY;IAC9B,IAAM,IAAW,EAA+C,CAAI;IAOpE,OANI,OAAO,KAAY,mBACT;KAEZ,AADA,EAAQ,UAAU,MAClB,EAAQ;IACT,UAEY;KAEZ,AADA,EAAQ,UAAU,MAClB,EAAI,IAAI;IACT;GACD;GAEA,OADA,EAAqD,UAAU,SAClD;IAEZ,AADA,EAAQ,UAAU,MAClB,EAAqD,UAAU;GAChE;EALA;CAMD,GACA,CAAC,CAAG,CACL,GACM,IAAkB,EAAM,OAAO;EACpC;EACA;CACD,CAAC;CAyCD,OAxCA,EAAM,gBAAgB;EACrB,EAAgB,UAAU;GACzB;GACA;EACD;CACD,CAAC,GAED,EAAM,gBAAgB;EACrB,IAAM,IAAS,EAAQ,SAAS;EAChC,IAAI,CAAC,KAAW,KAAQ,EAAO,OAAO,GACrC;EAED,IAAI,IAAW,IACT,oBAAS,IAAI,IAAmC,GAChD,KAAW,MAAiB;GACjC,EAAc,UAAU;GACxB,IAAM,IAAQ,iBAAiB;IAE9B,IADA,EAAO,OAAO,CAAK,GACf,KAAY,EAAM,kBACrB;IAED,IAAM,IAAM,EAAgB;IAC5B,AAAI,EAAI,UAAU,KAAA,KACjB,EAAgB,EAAI,gBAAgB,CAAC,CAAC;GAExC,GAAG,CAAC;GACJ,EAAO,IAAI,CAAK;EACjB;EAEA,OADA,EAAO,iBAAiB,SAAS,GAAS,EAAI,SACjC;GAEZ,AADA,IAAW,IACX,EAAc,UAAU;GACxB,KAAK,IAAM,KAAS,GACnB,aAAa,CAAK;GAGnB,AADA,EAAO,MAAM,GACb,EAAO,oBAAoB,SAAS,GAAS,EAAI;EAClD;CACD,GAAG,CAAC,CAAI,CAAC,GAEF;EACN;EACA;EACA;EACA;EACA;EACA;EACA,aAAa,IAAU,KAAO;CAC/B;AACD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocoo/basalt",
3
- "version": "2.1.0",
3
+ "version": "2.1.7",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -74,7 +74,6 @@
74
74
  "@radix-ui/react-slot": "1.3.3",
75
75
  "@radix-ui/react-switch": "1.3.7",
76
76
  "@radix-ui/react-tabs": "1.1.21",
77
- "@radix-ui/react-toast": "1.2.23",
78
77
  "@radix-ui/react-toggle": "1.1.18",
79
78
  "@radix-ui/react-toggle-group": "1.1.19",
80
79
  "@radix-ui/react-tooltip": "1.2.16",