@just-web/toolkits 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/dist/attributes/get-attribute.cjs +1 -1
  2. package/dist/attributes/get-attribute.cjs.map +1 -1
  3. package/dist/attributes/get-attribute.d.cts +2 -2
  4. package/dist/attributes/get-attribute.d.cts.map +1 -1
  5. package/dist/attributes/get-attribute.d.mts +2 -2
  6. package/dist/attributes/get-attribute.d.mts.map +1 -1
  7. package/dist/attributes/get-attribute.mjs +1 -1
  8. package/dist/attributes/get-attribute.mjs.map +1 -1
  9. package/dist/attributes/get-data-attribute.cjs +1 -1
  10. package/dist/attributes/get-data-attribute.cjs.map +1 -1
  11. package/dist/attributes/get-data-attribute.d.cts +2 -2
  12. package/dist/attributes/get-data-attribute.d.cts.map +1 -1
  13. package/dist/attributes/get-data-attribute.d.mts +2 -2
  14. package/dist/attributes/get-data-attribute.d.mts.map +1 -1
  15. package/dist/attributes/get-data-attribute.mjs +1 -1
  16. package/dist/attributes/get-data-attribute.mjs.map +1 -1
  17. package/dist/attributes/observe-attribute.cjs +1 -1
  18. package/dist/attributes/observe-attribute.cjs.map +1 -1
  19. package/dist/attributes/observe-attribute.d.cts +2 -2
  20. package/dist/attributes/observe-attribute.d.cts.map +1 -1
  21. package/dist/attributes/observe-attribute.d.mts +2 -2
  22. package/dist/attributes/observe-attribute.d.mts.map +1 -1
  23. package/dist/attributes/observe-attribute.mjs +1 -1
  24. package/dist/attributes/observe-attribute.mjs.map +1 -1
  25. package/dist/attributes/observe-data-attribute.cjs +1 -1
  26. package/dist/attributes/observe-data-attribute.cjs.map +1 -1
  27. package/dist/attributes/observe-data-attribute.d.cts +2 -2
  28. package/dist/attributes/observe-data-attribute.d.cts.map +1 -1
  29. package/dist/attributes/observe-data-attribute.d.mts +2 -2
  30. package/dist/attributes/observe-data-attribute.d.mts.map +1 -1
  31. package/dist/attributes/observe-data-attribute.mjs +1 -1
  32. package/dist/attributes/observe-data-attribute.mjs.map +1 -1
  33. package/dist/color-scheme/color-scheme.types.d.cts +11 -0
  34. package/dist/color-scheme/color-scheme.types.d.cts.map +1 -0
  35. package/dist/color-scheme/color-scheme.types.d.mts +11 -0
  36. package/dist/color-scheme/color-scheme.types.d.mts.map +1 -0
  37. package/dist/color-scheme/get-prefers-color-scheme.cjs +3 -1
  38. package/dist/color-scheme/get-prefers-color-scheme.cjs.map +1 -1
  39. package/dist/color-scheme/get-prefers-color-scheme.d.cts +7 -2
  40. package/dist/color-scheme/get-prefers-color-scheme.d.cts.map +1 -1
  41. package/dist/color-scheme/get-prefers-color-scheme.d.mts +7 -2
  42. package/dist/color-scheme/get-prefers-color-scheme.d.mts.map +1 -1
  43. package/dist/color-scheme/get-prefers-color-scheme.mjs +3 -1
  44. package/dist/color-scheme/get-prefers-color-scheme.mjs.map +1 -1
  45. package/dist/color-scheme/observe-prefers-color-scheme.cjs.map +1 -1
  46. package/dist/color-scheme/observe-prefers-color-scheme.d.cts +4 -1
  47. package/dist/color-scheme/observe-prefers-color-scheme.d.cts.map +1 -1
  48. package/dist/color-scheme/observe-prefers-color-scheme.d.mts +4 -1
  49. package/dist/color-scheme/observe-prefers-color-scheme.d.mts.map +1 -1
  50. package/dist/color-scheme/observe-prefers-color-scheme.mjs.map +1 -1
  51. package/dist/index.d.cts +2 -1
  52. package/dist/index.d.mts +2 -1
  53. package/dist/react/hooks/use-attribute.cjs +7 -7
  54. package/dist/react/hooks/use-attribute.cjs.map +1 -1
  55. package/dist/react/hooks/use-attribute.d.cts +4 -4
  56. package/dist/react/hooks/use-attribute.d.mts +4 -4
  57. package/dist/react/hooks/use-attribute.mjs +7 -7
  58. package/dist/react/hooks/use-attribute.mjs.map +1 -1
  59. package/dist/react/hooks/use-theme-by-class-name.cjs +1 -1
  60. package/dist/react/hooks/use-theme-by-class-name.cjs.map +1 -1
  61. package/dist/react/hooks/use-theme-by-class-name.d.cts +2 -2
  62. package/dist/react/hooks/use-theme-by-class-name.d.mts +2 -2
  63. package/dist/react/hooks/use-theme-by-class-name.mjs +1 -1
  64. package/dist/react/hooks/use-theme-by-class-name.mjs.map +1 -1
  65. package/dist/react/hooks/use-theme-by-data-attribute.cjs +1 -1
  66. package/dist/react/hooks/use-theme-by-data-attribute.cjs.map +1 -1
  67. package/dist/react/hooks/use-theme-by-data-attribute.d.cts +2 -2
  68. package/dist/react/hooks/use-theme-by-data-attribute.d.mts +2 -2
  69. package/dist/react/hooks/use-theme-by-data-attribute.mjs +1 -1
  70. package/dist/react/hooks/use-theme-by-data-attribute.mjs.map +1 -1
  71. package/dist/react/theme/create-theme-hook.cjs.map +1 -1
  72. package/dist/react/theme/create-theme-hook.mjs.map +1 -1
  73. package/dist/theme/_utils/parse-stored-theme.cjs +1 -1
  74. package/dist/theme/_utils/parse-stored-theme.cjs.map +1 -1
  75. package/dist/theme/_utils/parse-stored-theme.mjs +1 -1
  76. package/dist/theme/_utils/parse-stored-theme.mjs.map +1 -1
  77. package/dist/theme/_utils/set-theme-to-stores.cjs +1 -1
  78. package/dist/theme/_utils/set-theme-to-stores.cjs.map +1 -1
  79. package/dist/theme/_utils/set-theme-to-stores.mjs +1 -1
  80. package/dist/theme/_utils/set-theme-to-stores.mjs.map +1 -1
  81. package/dist/theme/class-name/parse-class-name.cjs +1 -1
  82. package/dist/theme/class-name/parse-class-name.cjs.map +1 -1
  83. package/dist/theme/class-name/parse-class-name.d.cts +2 -2
  84. package/dist/theme/class-name/parse-class-name.d.cts.map +1 -1
  85. package/dist/theme/class-name/parse-class-name.d.mts +2 -2
  86. package/dist/theme/class-name/parse-class-name.d.mts.map +1 -1
  87. package/dist/theme/class-name/parse-class-name.mjs +1 -1
  88. package/dist/theme/class-name/parse-class-name.mjs.map +1 -1
  89. package/dist/theme/class-name/read-class-name.cjs +1 -1
  90. package/dist/theme/class-name/read-class-name.cjs.map +1 -1
  91. package/dist/theme/class-name/read-class-name.d.cts +2 -2
  92. package/dist/theme/class-name/read-class-name.d.cts.map +1 -1
  93. package/dist/theme/class-name/read-class-name.d.mts +2 -2
  94. package/dist/theme/class-name/read-class-name.d.mts.map +1 -1
  95. package/dist/theme/class-name/read-class-name.mjs +1 -1
  96. package/dist/theme/class-name/read-class-name.mjs.map +1 -1
  97. package/dist/theme/class-name/stringify-class-name.cjs +4 -4
  98. package/dist/theme/class-name/stringify-class-name.cjs.map +1 -1
  99. package/dist/theme/class-name/stringify-class-name.d.cts +3 -3
  100. package/dist/theme/class-name/stringify-class-name.d.cts.map +1 -1
  101. package/dist/theme/class-name/stringify-class-name.d.mts +3 -3
  102. package/dist/theme/class-name/stringify-class-name.d.mts.map +1 -1
  103. package/dist/theme/class-name/stringify-class-name.mjs +4 -4
  104. package/dist/theme/class-name/stringify-class-name.mjs.map +1 -1
  105. package/dist/theme/class-name/subscribe-class-name.cjs +1 -1
  106. package/dist/theme/class-name/subscribe-class-name.cjs.map +1 -1
  107. package/dist/theme/class-name/subscribe-class-name.d.cts +2 -2
  108. package/dist/theme/class-name/subscribe-class-name.d.cts.map +1 -1
  109. package/dist/theme/class-name/subscribe-class-name.d.mts +2 -2
  110. package/dist/theme/class-name/subscribe-class-name.d.mts.map +1 -1
  111. package/dist/theme/class-name/subscribe-class-name.mjs +1 -1
  112. package/dist/theme/class-name/subscribe-class-name.mjs.map +1 -1
  113. package/dist/theme/class-name/write-class-name.cjs +2 -2
  114. package/dist/theme/class-name/write-class-name.cjs.map +1 -1
  115. package/dist/theme/class-name/write-class-name.d.cts +4 -4
  116. package/dist/theme/class-name/write-class-name.d.cts.map +1 -1
  117. package/dist/theme/class-name/write-class-name.d.mts +4 -4
  118. package/dist/theme/class-name/write-class-name.d.mts.map +1 -1
  119. package/dist/theme/class-name/write-class-name.mjs +2 -2
  120. package/dist/theme/class-name/write-class-name.mjs.map +1 -1
  121. package/dist/theme/compose-theme-stores.cjs.map +1 -1
  122. package/dist/theme/compose-theme-stores.mjs.map +1 -1
  123. package/dist/theme/cookie/write-cookie-theme.cjs +2 -2
  124. package/dist/theme/cookie/write-cookie-theme.cjs.map +1 -1
  125. package/dist/theme/cookie/write-cookie-theme.d.cts +2 -2
  126. package/dist/theme/cookie/write-cookie-theme.d.cts.map +1 -1
  127. package/dist/theme/cookie/write-cookie-theme.d.mts +2 -2
  128. package/dist/theme/cookie/write-cookie-theme.d.mts.map +1 -1
  129. package/dist/theme/cookie/write-cookie-theme.mjs +2 -2
  130. package/dist/theme/cookie/write-cookie-theme.mjs.map +1 -1
  131. package/dist/theme/data-attribute/parse-data-attribute.cjs +1 -1
  132. package/dist/theme/data-attribute/parse-data-attribute.cjs.map +1 -1
  133. package/dist/theme/data-attribute/parse-data-attribute.d.cts +2 -2
  134. package/dist/theme/data-attribute/parse-data-attribute.d.mts +2 -2
  135. package/dist/theme/data-attribute/parse-data-attribute.mjs +1 -1
  136. package/dist/theme/data-attribute/parse-data-attribute.mjs.map +1 -1
  137. package/dist/theme/data-attribute/read-data-attribute.cjs +1 -1
  138. package/dist/theme/data-attribute/read-data-attribute.cjs.map +1 -1
  139. package/dist/theme/data-attribute/read-data-attribute.d.cts +2 -2
  140. package/dist/theme/data-attribute/read-data-attribute.d.cts.map +1 -1
  141. package/dist/theme/data-attribute/read-data-attribute.d.mts +2 -2
  142. package/dist/theme/data-attribute/read-data-attribute.d.mts.map +1 -1
  143. package/dist/theme/data-attribute/read-data-attribute.mjs +1 -1
  144. package/dist/theme/data-attribute/read-data-attribute.mjs.map +1 -1
  145. package/dist/theme/data-attribute/stringify-data-attribute.cjs +4 -4
  146. package/dist/theme/data-attribute/stringify-data-attribute.cjs.map +1 -1
  147. package/dist/theme/data-attribute/stringify-data-attribute.d.cts +3 -3
  148. package/dist/theme/data-attribute/stringify-data-attribute.d.cts.map +1 -1
  149. package/dist/theme/data-attribute/stringify-data-attribute.d.mts +3 -3
  150. package/dist/theme/data-attribute/stringify-data-attribute.d.mts.map +1 -1
  151. package/dist/theme/data-attribute/stringify-data-attribute.mjs +4 -4
  152. package/dist/theme/data-attribute/stringify-data-attribute.mjs.map +1 -1
  153. package/dist/theme/data-attribute/subscribe-data-attribute.cjs +1 -1
  154. package/dist/theme/data-attribute/subscribe-data-attribute.cjs.map +1 -1
  155. package/dist/theme/data-attribute/subscribe-data-attribute.d.cts +2 -2
  156. package/dist/theme/data-attribute/subscribe-data-attribute.d.cts.map +1 -1
  157. package/dist/theme/data-attribute/subscribe-data-attribute.d.mts +2 -2
  158. package/dist/theme/data-attribute/subscribe-data-attribute.d.mts.map +1 -1
  159. package/dist/theme/data-attribute/subscribe-data-attribute.mjs +1 -1
  160. package/dist/theme/data-attribute/subscribe-data-attribute.mjs.map +1 -1
  161. package/dist/theme/data-attribute/write-data-attribute.cjs +3 -3
  162. package/dist/theme/data-attribute/write-data-attribute.cjs.map +1 -1
  163. package/dist/theme/data-attribute/write-data-attribute.d.cts +4 -4
  164. package/dist/theme/data-attribute/write-data-attribute.d.cts.map +1 -1
  165. package/dist/theme/data-attribute/write-data-attribute.d.mts +4 -4
  166. package/dist/theme/data-attribute/write-data-attribute.d.mts.map +1 -1
  167. package/dist/theme/data-attribute/write-data-attribute.mjs +3 -3
  168. package/dist/theme/data-attribute/write-data-attribute.mjs.map +1 -1
  169. package/dist/theme/local-storage/write-local-storage.cjs +1 -1
  170. package/dist/theme/local-storage/write-local-storage.cjs.map +1 -1
  171. package/dist/theme/local-storage/write-local-storage.d.cts +2 -2
  172. package/dist/theme/local-storage/write-local-storage.d.mts +2 -2
  173. package/dist/theme/local-storage/write-local-storage.mjs +1 -1
  174. package/dist/theme/local-storage/write-local-storage.mjs.map +1 -1
  175. package/dist/theme/session-storage/write-session-storage.cjs +1 -1
  176. package/dist/theme/session-storage/write-session-storage.cjs.map +1 -1
  177. package/dist/theme/session-storage/write-session-storage.d.cts +2 -2
  178. package/dist/theme/session-storage/write-session-storage.d.mts +2 -2
  179. package/dist/theme/session-storage/write-session-storage.mjs +1 -1
  180. package/dist/theme/session-storage/write-session-storage.mjs.map +1 -1
  181. package/dist/theme/theme-entry.types.d.cts +4 -2
  182. package/dist/theme/theme-entry.types.d.cts.map +1 -1
  183. package/dist/theme/theme-entry.types.d.mts +4 -2
  184. package/dist/theme/theme-entry.types.d.mts.map +1 -1
  185. package/dist/theme/theme-store/async-theme-store.types.d.cts +1 -1
  186. package/dist/theme/theme-store/async-theme-store.types.d.cts.map +1 -1
  187. package/dist/theme/theme-store/async-theme-store.types.d.mts +1 -1
  188. package/dist/theme/theme-store/async-theme-store.types.d.mts.map +1 -1
  189. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.cjs +1 -1
  190. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.cjs.map +1 -1
  191. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.d.cts +2 -2
  192. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.d.mts +2 -2
  193. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.mjs +1 -1
  194. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.mjs.map +1 -1
  195. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.cjs +1 -1
  196. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.cjs.map +1 -1
  197. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.d.cts +3 -3
  198. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.d.mts +3 -3
  199. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.mjs +1 -1
  200. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.mjs.map +1 -1
  201. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.cjs +1 -1
  202. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.cjs.map +1 -1
  203. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.d.cts +1 -1
  204. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.d.mts +1 -1
  205. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.mjs +1 -1
  206. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.mjs.map +1 -1
  207. package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.d.cts +1 -1
  208. package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.d.mts +1 -1
  209. package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.d.cts +1 -1
  210. package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.d.mts +1 -1
  211. package/dist/theme/theme-store/theme-store.types.d.cts +1 -1
  212. package/dist/theme/theme-store/theme-store.types.d.cts.map +1 -1
  213. package/dist/theme/theme-store/theme-store.types.d.mts +1 -1
  214. package/dist/theme/theme-store/theme-store.types.d.mts.map +1 -1
  215. package/dist/theme/web-storage/write-web-storage.cjs +3 -3
  216. package/dist/theme/web-storage/write-web-storage.cjs.map +1 -1
  217. package/dist/theme/web-storage/write-web-storage.d.cts +2 -2
  218. package/dist/theme/web-storage/write-web-storage.d.mts +2 -2
  219. package/dist/theme/web-storage/write-web-storage.mjs +3 -3
  220. package/dist/theme/web-storage/write-web-storage.mjs.map +1 -1
  221. package/dist/utils/append-id.cjs +2 -2
  222. package/dist/utils/append-id.cjs.map +1 -1
  223. package/dist/utils/append-id.d.cts +3 -3
  224. package/dist/utils/append-id.d.mts +3 -3
  225. package/dist/utils/append-id.mjs +2 -2
  226. package/dist/utils/append-id.mjs.map +1 -1
  227. package/package.json +1 -1
  228. package/src/attributes/get-attribute.ts +5 -2
  229. package/src/attributes/get-data-attribute.ts +5 -2
  230. package/src/attributes/observe-attribute.ts +2 -2
  231. package/src/attributes/observe-data-attribute.ts +2 -2
  232. package/src/color-scheme/color-scheme.types.ts +7 -0
  233. package/src/color-scheme/get-prefers-color-scheme.ts +6 -4
  234. package/src/color-scheme/observe-prefers-color-scheme.ts +3 -1
  235. package/src/index.ts +1 -0
  236. package/src/react/hooks/use-attribute.ts +11 -11
  237. package/src/react/hooks/use-theme-by-class-name.ts +2 -2
  238. package/src/react/hooks/use-theme-by-data-attribute.ts +2 -2
  239. package/src/react/theme/create-theme-hook.ts +4 -6
  240. package/src/theme/_utils/parse-stored-theme.ts +2 -2
  241. package/src/theme/_utils/set-theme-to-stores.ts +3 -3
  242. package/src/theme/class-name/parse-class-name.ts +2 -2
  243. package/src/theme/class-name/read-class-name.ts +2 -2
  244. package/src/theme/class-name/stringify-class-name.ts +6 -6
  245. package/src/theme/class-name/subscribe-class-name.ts +2 -2
  246. package/src/theme/class-name/write-class-name.ts +4 -4
  247. package/src/theme/compose-theme-stores.ts +1 -1
  248. package/src/theme/cookie/write-cookie-theme.ts +3 -3
  249. package/src/theme/data-attribute/parse-data-attribute.ts +2 -2
  250. package/src/theme/data-attribute/read-data-attribute.ts +2 -2
  251. package/src/theme/data-attribute/stringify-data-attribute.ts +6 -6
  252. package/src/theme/data-attribute/subscribe-data-attribute.ts +2 -2
  253. package/src/theme/data-attribute/write-data-attribute.ts +5 -5
  254. package/src/theme/local-storage/write-local-storage.ts +2 -2
  255. package/src/theme/session-storage/write-session-storage.ts +2 -2
  256. package/src/theme/theme-entry.types.ts +5 -3
  257. package/src/theme/theme-store/async-theme-store.types.ts +1 -3
  258. package/src/theme/theme-store/class-name-theme-store/class-name-theme-store.ts +2 -2
  259. package/src/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.ts +2 -2
  260. package/src/theme/theme-store/in-memory-theme-store/in-memory-theme-store.ts +1 -1
  261. package/src/theme/theme-store/theme-store.types.ts +1 -3
  262. package/src/theme/web-storage/write-web-storage.ts +5 -5
  263. package/src/utils/append-id.ts +3 -3
@@ -8,30 +8,30 @@ let react = require("react");
8
8
  * and a setter to update it. Stays in sync when the attribute changes (e.g. from elsewhere).
9
9
  *
10
10
  * @param attributeName - The attribute to observe (e.g. `'class'`, `'data-theme'`).
11
- * @param element - The element to observe. Defaults to `document.documentElement` when omitted.
12
- * @returns Tuple of [value, setValue]. Pass null to setValue to remove the attribute.
11
+ * @param element - The element to observe (accepts null e.g. from refs). Defaults to `document.documentElement` when omitted.
12
+ * @returns Tuple of [value, setValue]. Pass null or undefined to setValue to remove the attribute.
13
13
  *
14
14
  * @example
15
15
  * ```tsx
16
16
  * const [className, setClassName] = useAttribute('class')
17
17
  * const [theme, setTheme] = useAttribute('data-theme', myElement)
18
18
  * setTheme('dark')
19
- * setClassName(null) // removes class attribute
19
+ * setClassName(undefined) // removes class attribute
20
20
  * ```
21
21
  */
22
22
  function useAttribute(attributeName, element = typeof document !== "undefined" ? document.documentElement : void 0) {
23
- const [value, setValueState] = (0, react.useState)(() => element?.getAttribute(attributeName) ?? null);
23
+ const [value, setValueState] = (0, react.useState)(() => element?.getAttribute(attributeName) ?? void 0);
24
24
  (0, react.useEffect)(() => {
25
25
  if (!element) return;
26
- setValueState(element.getAttribute(attributeName));
26
+ setValueState(element.getAttribute(attributeName) ?? void 0);
27
27
  const observer = require_observe_attribute.observeAttributes({ [attributeName]: (next) => {
28
- setValueState(next);
28
+ setValueState(next ?? void 0);
29
29
  } }, element);
30
30
  return () => observer.disconnect();
31
31
  }, [element, attributeName]);
32
32
  return [value, (0, react.useCallback)((next) => {
33
33
  if (!element) return;
34
- if (next === null) element.removeAttribute(attributeName);
34
+ if (next == null) element.removeAttribute(attributeName);
35
35
  else element.setAttribute(attributeName, next);
36
36
  }, [element, attributeName])];
37
37
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-attribute.cjs","names":["observeAttributes"],"sources":["../../../src/react/hooks/use-attribute.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react'\nimport { observeAttributes } from '../../attributes/observe-attribute.ts'\n\n/**\n * React hook that returns the current value of an attribute on a target element\n * and a setter to update it. Stays in sync when the attribute changes (e.g. from elsewhere).\n *\n * @param attributeName - The attribute to observe (e.g. `'class'`, `'data-theme'`).\n * @param element - The element to observe. Defaults to `document.documentElement` when omitted.\n * @returns Tuple of [value, setValue]. Pass null to setValue to remove the attribute.\n *\n * @example\n * ```tsx\n * const [className, setClassName] = useAttribute('class')\n * const [theme, setTheme] = useAttribute('data-theme', myElement)\n * setTheme('dark')\n * setClassName(null) // removes class attribute\n * ```\n */\nexport function useAttribute(\n\tattributeName: string,\n\telement: Element | undefined = typeof document !== 'undefined'\n\t\t? document.documentElement\n\t\t: undefined\n): [string | null, (value: string | null) => void] {\n\tconst [value, setValueState] = useState<string | null>(\n\t\t() => element?.getAttribute(attributeName) ?? null\n\t)\n\n\tuseEffect(() => {\n\t\tif (!element) return\n\n\t\tsetValueState(element.getAttribute(attributeName))\n\n\t\tconst observer = observeAttributes(\n\t\t\t{\n\t\t\t\t[attributeName]: (next) => {\n\t\t\t\t\tsetValueState(next)\n\t\t\t\t}\n\t\t\t},\n\t\t\telement\n\t\t)\n\t\treturn () => observer.disconnect()\n\t}, [element, attributeName])\n\n\tconst setValue = useCallback(\n\t\t(next: string | null) => {\n\t\t\tif (!element) return\n\t\t\tif (next === null) {\n\t\t\t\telement.removeAttribute(attributeName)\n\t\t\t} else {\n\t\t\t\telement.setAttribute(attributeName, next)\n\t\t\t}\n\t\t},\n\t\t[element, attributeName]\n\t)\n\n\treturn [value, setValue]\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,aACf,eACA,UAA+B,OAAO,aAAa,cAChD,SAAS,kBACT,QAC+C;CAClD,MAAM,CAAC,OAAO,2CACP,SAAS,aAAa,cAAc,IAAI,KAC9C;AAED,4BAAgB;AACf,MAAI,CAAC,QAAS;AAEd,gBAAc,QAAQ,aAAa,cAAc,CAAC;EAElD,MAAM,WAAWA,4CAChB,GACE,iBAAiB,SAAS;AAC1B,iBAAc,KAAK;KAEpB,EACD,QACA;AACD,eAAa,SAAS,YAAY;IAChC,CAAC,SAAS,cAAc,CAAC;AAc5B,QAAO,CAAC,+BAXN,SAAwB;AACxB,MAAI,CAAC,QAAS;AACd,MAAI,SAAS,KACZ,SAAQ,gBAAgB,cAAc;MAEtC,SAAQ,aAAa,eAAe,KAAK;IAG3C,CAAC,SAAS,cAAc,CACxB,CAEuB"}
1
+ {"version":3,"file":"use-attribute.cjs","names":["observeAttributes"],"sources":["../../../src/react/hooks/use-attribute.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react'\nimport { observeAttributes } from '../../attributes/observe-attribute.ts'\n\n/**\n * React hook that returns the current value of an attribute on a target element\n * and a setter to update it. Stays in sync when the attribute changes (e.g. from elsewhere).\n *\n * @param attributeName - The attribute to observe (e.g. `'class'`, `'data-theme'`).\n * @param element - The element to observe (accepts null e.g. from refs). Defaults to `document.documentElement` when omitted.\n * @returns Tuple of [value, setValue]. Pass null or undefined to setValue to remove the attribute.\n *\n * @example\n * ```tsx\n * const [className, setClassName] = useAttribute('class')\n * const [theme, setTheme] = useAttribute('data-theme', myElement)\n * setTheme('dark')\n * setClassName(undefined) // removes class attribute\n * ```\n */\nexport function useAttribute(\n\tattributeName: string,\n\telement: Element | null | undefined = typeof document !== 'undefined'\n\t\t? document.documentElement\n\t\t: undefined\n): [string | undefined, (value: string | null | undefined) => void] {\n\tconst [value, setValueState] = useState<string | undefined>(\n\t\t() => element?.getAttribute(attributeName) ?? undefined\n\t)\n\n\tuseEffect(() => {\n\t\tif (!element) return\n\n\t\tsetValueState(element.getAttribute(attributeName) ?? undefined)\n\n\t\tconst observer = observeAttributes(\n\t\t\t{\n\t\t\t\t[attributeName]: (next) => {\n\t\t\t\t\tsetValueState(next ?? undefined)\n\t\t\t\t}\n\t\t\t},\n\t\t\telement\n\t\t)\n\t\treturn () => observer.disconnect()\n\t}, [element, attributeName])\n\n\tconst setValue = useCallback(\n\t\t(next: string | null | undefined) => {\n\t\t\tif (!element) return\n\t\t\tif (next == null) {\n\t\t\t\telement.removeAttribute(attributeName)\n\t\t\t} else {\n\t\t\t\telement.setAttribute(attributeName, next)\n\t\t\t}\n\t\t},\n\t\t[element, attributeName]\n\t)\n\n\treturn [value, setValue]\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,aACf,eACA,UAAsC,OAAO,aAAa,cACvD,SAAS,kBACT,QACgE;CACnE,MAAM,CAAC,OAAO,2CACP,SAAS,aAAa,cAAc,IAAI,OAC9C;AAED,4BAAgB;AACf,MAAI,CAAC,QAAS;AAEd,gBAAc,QAAQ,aAAa,cAAc,IAAI,OAAU;EAE/D,MAAM,WAAWA,4CAChB,GACE,iBAAiB,SAAS;AAC1B,iBAAc,QAAQ,OAAU;KAEjC,EACD,QACA;AACD,eAAa,SAAS,YAAY;IAChC,CAAC,SAAS,cAAc,CAAC;AAc5B,QAAO,CAAC,+BAXN,SAAoC;AACpC,MAAI,CAAC,QAAS;AACd,MAAI,QAAQ,KACX,SAAQ,gBAAgB,cAAc;MAEtC,SAAQ,aAAa,eAAe,KAAK;IAG3C,CAAC,SAAS,cAAc,CACxB,CAEuB"}
@@ -4,18 +4,18 @@
4
4
  * and a setter to update it. Stays in sync when the attribute changes (e.g. from elsewhere).
5
5
  *
6
6
  * @param attributeName - The attribute to observe (e.g. `'class'`, `'data-theme'`).
7
- * @param element - The element to observe. Defaults to `document.documentElement` when omitted.
8
- * @returns Tuple of [value, setValue]. Pass null to setValue to remove the attribute.
7
+ * @param element - The element to observe (accepts null e.g. from refs). Defaults to `document.documentElement` when omitted.
8
+ * @returns Tuple of [value, setValue]. Pass null or undefined to setValue to remove the attribute.
9
9
  *
10
10
  * @example
11
11
  * ```tsx
12
12
  * const [className, setClassName] = useAttribute('class')
13
13
  * const [theme, setTheme] = useAttribute('data-theme', myElement)
14
14
  * setTheme('dark')
15
- * setClassName(null) // removes class attribute
15
+ * setClassName(undefined) // removes class attribute
16
16
  * ```
17
17
  */
18
- declare function useAttribute(attributeName: string, element?: Element | undefined): [string | null, (value: string | null) => void];
18
+ declare function useAttribute(attributeName: string, element?: Element | null | undefined): [string | undefined, (value: string | null | undefined) => void];
19
19
  //#endregion
20
20
  export { useAttribute };
21
21
  //# sourceMappingURL=use-attribute.d.cts.map
@@ -4,18 +4,18 @@
4
4
  * and a setter to update it. Stays in sync when the attribute changes (e.g. from elsewhere).
5
5
  *
6
6
  * @param attributeName - The attribute to observe (e.g. `'class'`, `'data-theme'`).
7
- * @param element - The element to observe. Defaults to `document.documentElement` when omitted.
8
- * @returns Tuple of [value, setValue]. Pass null to setValue to remove the attribute.
7
+ * @param element - The element to observe (accepts null e.g. from refs). Defaults to `document.documentElement` when omitted.
8
+ * @returns Tuple of [value, setValue]. Pass null or undefined to setValue to remove the attribute.
9
9
  *
10
10
  * @example
11
11
  * ```tsx
12
12
  * const [className, setClassName] = useAttribute('class')
13
13
  * const [theme, setTheme] = useAttribute('data-theme', myElement)
14
14
  * setTheme('dark')
15
- * setClassName(null) // removes class attribute
15
+ * setClassName(undefined) // removes class attribute
16
16
  * ```
17
17
  */
18
- declare function useAttribute(attributeName: string, element?: Element | undefined): [string | null, (value: string | null) => void];
18
+ declare function useAttribute(attributeName: string, element?: Element | null | undefined): [string | undefined, (value: string | null | undefined) => void];
19
19
  //#endregion
20
20
  export { useAttribute };
21
21
  //# sourceMappingURL=use-attribute.d.mts.map
@@ -7,30 +7,30 @@ import { useCallback, useEffect, useState } from "react";
7
7
  * and a setter to update it. Stays in sync when the attribute changes (e.g. from elsewhere).
8
8
  *
9
9
  * @param attributeName - The attribute to observe (e.g. `'class'`, `'data-theme'`).
10
- * @param element - The element to observe. Defaults to `document.documentElement` when omitted.
11
- * @returns Tuple of [value, setValue]. Pass null to setValue to remove the attribute.
10
+ * @param element - The element to observe (accepts null e.g. from refs). Defaults to `document.documentElement` when omitted.
11
+ * @returns Tuple of [value, setValue]. Pass null or undefined to setValue to remove the attribute.
12
12
  *
13
13
  * @example
14
14
  * ```tsx
15
15
  * const [className, setClassName] = useAttribute('class')
16
16
  * const [theme, setTheme] = useAttribute('data-theme', myElement)
17
17
  * setTheme('dark')
18
- * setClassName(null) // removes class attribute
18
+ * setClassName(undefined) // removes class attribute
19
19
  * ```
20
20
  */
21
21
  function useAttribute(attributeName, element = typeof document !== "undefined" ? document.documentElement : void 0) {
22
- const [value, setValueState] = useState(() => element?.getAttribute(attributeName) ?? null);
22
+ const [value, setValueState] = useState(() => element?.getAttribute(attributeName) ?? void 0);
23
23
  useEffect(() => {
24
24
  if (!element) return;
25
- setValueState(element.getAttribute(attributeName));
25
+ setValueState(element.getAttribute(attributeName) ?? void 0);
26
26
  const observer = observeAttributes({ [attributeName]: (next) => {
27
- setValueState(next);
27
+ setValueState(next ?? void 0);
28
28
  } }, element);
29
29
  return () => observer.disconnect();
30
30
  }, [element, attributeName]);
31
31
  return [value, useCallback((next) => {
32
32
  if (!element) return;
33
- if (next === null) element.removeAttribute(attributeName);
33
+ if (next == null) element.removeAttribute(attributeName);
34
34
  else element.setAttribute(attributeName, next);
35
35
  }, [element, attributeName])];
36
36
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-attribute.mjs","names":[],"sources":["../../../src/react/hooks/use-attribute.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react'\nimport { observeAttributes } from '../../attributes/observe-attribute.ts'\n\n/**\n * React hook that returns the current value of an attribute on a target element\n * and a setter to update it. Stays in sync when the attribute changes (e.g. from elsewhere).\n *\n * @param attributeName - The attribute to observe (e.g. `'class'`, `'data-theme'`).\n * @param element - The element to observe. Defaults to `document.documentElement` when omitted.\n * @returns Tuple of [value, setValue]. Pass null to setValue to remove the attribute.\n *\n * @example\n * ```tsx\n * const [className, setClassName] = useAttribute('class')\n * const [theme, setTheme] = useAttribute('data-theme', myElement)\n * setTheme('dark')\n * setClassName(null) // removes class attribute\n * ```\n */\nexport function useAttribute(\n\tattributeName: string,\n\telement: Element | undefined = typeof document !== 'undefined'\n\t\t? document.documentElement\n\t\t: undefined\n): [string | null, (value: string | null) => void] {\n\tconst [value, setValueState] = useState<string | null>(\n\t\t() => element?.getAttribute(attributeName) ?? null\n\t)\n\n\tuseEffect(() => {\n\t\tif (!element) return\n\n\t\tsetValueState(element.getAttribute(attributeName))\n\n\t\tconst observer = observeAttributes(\n\t\t\t{\n\t\t\t\t[attributeName]: (next) => {\n\t\t\t\t\tsetValueState(next)\n\t\t\t\t}\n\t\t\t},\n\t\t\telement\n\t\t)\n\t\treturn () => observer.disconnect()\n\t}, [element, attributeName])\n\n\tconst setValue = useCallback(\n\t\t(next: string | null) => {\n\t\t\tif (!element) return\n\t\t\tif (next === null) {\n\t\t\t\telement.removeAttribute(attributeName)\n\t\t\t} else {\n\t\t\t\telement.setAttribute(attributeName, next)\n\t\t\t}\n\t\t},\n\t\t[element, attributeName]\n\t)\n\n\treturn [value, setValue]\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,aACf,eACA,UAA+B,OAAO,aAAa,cAChD,SAAS,kBACT,QAC+C;CAClD,MAAM,CAAC,OAAO,iBAAiB,eACxB,SAAS,aAAa,cAAc,IAAI,KAC9C;AAED,iBAAgB;AACf,MAAI,CAAC,QAAS;AAEd,gBAAc,QAAQ,aAAa,cAAc,CAAC;EAElD,MAAM,WAAW,kBAChB,GACE,iBAAiB,SAAS;AAC1B,iBAAc,KAAK;KAEpB,EACD,QACA;AACD,eAAa,SAAS,YAAY;IAChC,CAAC,SAAS,cAAc,CAAC;AAc5B,QAAO,CAAC,OAZS,aACf,SAAwB;AACxB,MAAI,CAAC,QAAS;AACd,MAAI,SAAS,KACZ,SAAQ,gBAAgB,cAAc;MAEtC,SAAQ,aAAa,eAAe,KAAK;IAG3C,CAAC,SAAS,cAAc,CACxB,CAEuB"}
1
+ {"version":3,"file":"use-attribute.mjs","names":[],"sources":["../../../src/react/hooks/use-attribute.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react'\nimport { observeAttributes } from '../../attributes/observe-attribute.ts'\n\n/**\n * React hook that returns the current value of an attribute on a target element\n * and a setter to update it. Stays in sync when the attribute changes (e.g. from elsewhere).\n *\n * @param attributeName - The attribute to observe (e.g. `'class'`, `'data-theme'`).\n * @param element - The element to observe (accepts null e.g. from refs). Defaults to `document.documentElement` when omitted.\n * @returns Tuple of [value, setValue]. Pass null or undefined to setValue to remove the attribute.\n *\n * @example\n * ```tsx\n * const [className, setClassName] = useAttribute('class')\n * const [theme, setTheme] = useAttribute('data-theme', myElement)\n * setTheme('dark')\n * setClassName(undefined) // removes class attribute\n * ```\n */\nexport function useAttribute(\n\tattributeName: string,\n\telement: Element | null | undefined = typeof document !== 'undefined'\n\t\t? document.documentElement\n\t\t: undefined\n): [string | undefined, (value: string | null | undefined) => void] {\n\tconst [value, setValueState] = useState<string | undefined>(\n\t\t() => element?.getAttribute(attributeName) ?? undefined\n\t)\n\n\tuseEffect(() => {\n\t\tif (!element) return\n\n\t\tsetValueState(element.getAttribute(attributeName) ?? undefined)\n\n\t\tconst observer = observeAttributes(\n\t\t\t{\n\t\t\t\t[attributeName]: (next) => {\n\t\t\t\t\tsetValueState(next ?? undefined)\n\t\t\t\t}\n\t\t\t},\n\t\t\telement\n\t\t)\n\t\treturn () => observer.disconnect()\n\t}, [element, attributeName])\n\n\tconst setValue = useCallback(\n\t\t(next: string | null | undefined) => {\n\t\t\tif (!element) return\n\t\t\tif (next == null) {\n\t\t\t\telement.removeAttribute(attributeName)\n\t\t\t} else {\n\t\t\t\telement.setAttribute(attributeName, next)\n\t\t\t}\n\t\t},\n\t\t[element, attributeName]\n\t)\n\n\treturn [value, setValue]\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,aACf,eACA,UAAsC,OAAO,aAAa,cACvD,SAAS,kBACT,QACgE;CACnE,MAAM,CAAC,OAAO,iBAAiB,eACxB,SAAS,aAAa,cAAc,IAAI,OAC9C;AAED,iBAAgB;AACf,MAAI,CAAC,QAAS;AAEd,gBAAc,QAAQ,aAAa,cAAc,IAAI,OAAU;EAE/D,MAAM,WAAW,kBAChB,GACE,iBAAiB,SAAS;AAC1B,iBAAc,QAAQ,OAAU;KAEjC,EACD,QACA;AACD,eAAa,SAAS,YAAY;IAChC,CAAC,SAAS,cAAc,CAAC;AAc5B,QAAO,CAAC,OAZS,aACf,SAAoC;AACpC,MAAI,CAAC,QAAS;AACd,MAAI,QAAQ,KACX,SAAQ,gBAAgB,cAAc;MAEtC,SAAQ,aAAa,eAAe,KAAK;IAG3C,CAAC,SAAS,cAAc,CACxB,CAEuB"}
@@ -12,7 +12,7 @@ let react = require("react");
12
12
  *
13
13
  * @param themes - Record mapping theme keys to their class name values
14
14
  * @param options.theme - Fallback theme key when no matching class is found
15
- * @param options.element - Element to read/set theme on (defaults to document.documentElement)
15
+ * @param options.element - Element to read/set theme on (accepts null e.g. from refs). Defaults to document.documentElement.
16
16
  * @returns Tuple of [currentTheme, setTheme]
17
17
  *
18
18
  * @example
@@ -1 +1 @@
1
- {"version":3,"file":"use-theme-by-class-name.cjs","names":["classNameThemeStore","observeThemeFromStores","themeEntry"],"sources":["../../../src/react/hooks/use-theme-by-class-name.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from 'react'\nimport { observeThemeFromStores } from '../../theme/_utils/observe-theme-from-stores.ts'\nimport { setThemeToStores } from '../../theme/_utils/set-theme-to-stores.ts'\nimport { themeEntry } from '../../theme/theme-entry.ts'\nimport type { ThemeMap } from '../../theme/theme-map.types.ts'\nimport { classNameThemeStore } from '../../theme/theme-store/class-name-theme-store/class-name-theme-store.ts'\n\n/**\n * React hook that returns the current theme (from element class) and a setter.\n * Subscribes to class changes on the element so the returned theme stays in sync.\n *\n * @param themes - Record mapping theme keys to their class name values\n * @param options.theme - Fallback theme key when no matching class is found\n * @param options.element - Element to read/set theme on (defaults to document.documentElement)\n * @returns Tuple of [currentTheme, setTheme]\n *\n * @example\n * ```tsx\n * const themes = { light: 'theme-light', dark: 'theme-dark' }\n * const [theme, setTheme] = useThemeByClassName(themes, { theme: 'light' })\n *\n * return (\n * <>\n * <span>Current: {theme}</span>\n * <button onClick={() => setTheme('dark')}>Dark</button>\n * <button onClick={() => setTheme('light')}>Light</button>\n * </>\n * )\n * ```\n */\nexport function useThemeByClassName<Themes extends ThemeMap>(\n\tthemes: Themes,\n\toptions?: {\n\t\tdefaultTheme?: keyof Themes | undefined\n\t\telement?: Element | undefined\n\t}\n): [keyof Themes | undefined, (theme: keyof Themes) => void] {\n\tconst element =\n\t\toptions?.element ?? (typeof document !== 'undefined' ? document.documentElement : undefined)\n\tconst defaultTheme = options?.defaultTheme\n\n\tconst store = useMemo(() => classNameThemeStore(themes, { element }), [element, themes])\n\n\tconst [theme, setThemeState] = useState<keyof Themes | undefined>(\n\t\t() => store.read()?.theme ?? defaultTheme\n\t)\n\n\tuseEffect(() => {\n\t\tif (!element) return\n\t\tconst unobserve = observeThemeFromStores([store], defaultTheme, setThemeState)\n\t\treturn unobserve\n\t}, [element, store, defaultTheme])\n\n\tconst setTheme = useCallback(\n\t\t(themeKey: keyof Themes) => {\n\t\t\tif (element) {\n\t\t\t\tsetThemeToStores([store], themeEntry(themes, themeKey))\n\t\t\t}\n\t\t},\n\t\t[element, store, themes]\n\t)\n\n\treturn [theme, setTheme]\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,SAAgB,oBACf,QACA,SAI4D;CAC5D,MAAM,UACL,SAAS,YAAY,OAAO,aAAa,cAAc,SAAS,kBAAkB;CACnF,MAAM,eAAe,SAAS;CAE9B,MAAM,iCAAsBA,mDAAoB,QAAQ,EAAE,SAAS,CAAC,EAAE,CAAC,SAAS,OAAO,CAAC;CAExF,MAAM,CAAC,OAAO,2CACP,MAAM,MAAM,EAAE,SAAS,aAC7B;AAED,4BAAgB;AACf,MAAI,CAAC,QAAS;AAEd,SADkBC,yDAAuB,CAAC,MAAM,EAAE,cAAc,cAAc;IAE5E;EAAC;EAAS;EAAO;EAAa,CAAC;AAWlC,QAAO,CAAC,+BARN,aAA2B;AAC3B,MAAI,QACH,8CAAiB,CAAC,MAAM,EAAEC,+BAAW,QAAQ,SAAS,CAAC;IAGzD;EAAC;EAAS;EAAO;EAAO,CACxB,CAEuB"}
1
+ {"version":3,"file":"use-theme-by-class-name.cjs","names":["classNameThemeStore","observeThemeFromStores","themeEntry"],"sources":["../../../src/react/hooks/use-theme-by-class-name.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from 'react'\nimport { observeThemeFromStores } from '../../theme/_utils/observe-theme-from-stores.ts'\nimport { setThemeToStores } from '../../theme/_utils/set-theme-to-stores.ts'\nimport { themeEntry } from '../../theme/theme-entry.ts'\nimport type { ThemeMap } from '../../theme/theme-map.types.ts'\nimport { classNameThemeStore } from '../../theme/theme-store/class-name-theme-store/class-name-theme-store.ts'\n\n/**\n * React hook that returns the current theme (from element class) and a setter.\n * Subscribes to class changes on the element so the returned theme stays in sync.\n *\n * @param themes - Record mapping theme keys to their class name values\n * @param options.theme - Fallback theme key when no matching class is found\n * @param options.element - Element to read/set theme on (accepts null e.g. from refs). Defaults to document.documentElement.\n * @returns Tuple of [currentTheme, setTheme]\n *\n * @example\n * ```tsx\n * const themes = { light: 'theme-light', dark: 'theme-dark' }\n * const [theme, setTheme] = useThemeByClassName(themes, { theme: 'light' })\n *\n * return (\n * <>\n * <span>Current: {theme}</span>\n * <button onClick={() => setTheme('dark')}>Dark</button>\n * <button onClick={() => setTheme('light')}>Light</button>\n * </>\n * )\n * ```\n */\nexport function useThemeByClassName<Themes extends ThemeMap>(\n\tthemes: Themes,\n\toptions?: {\n\t\tdefaultTheme?: keyof Themes | undefined\n\t\telement?: Element | null | undefined\n\t}\n): [keyof Themes | undefined, (theme: keyof Themes) => void] {\n\tconst element =\n\t\toptions?.element ?? (typeof document !== 'undefined' ? document.documentElement : undefined)\n\tconst defaultTheme = options?.defaultTheme\n\n\tconst store = useMemo(() => classNameThemeStore(themes, { element }), [element, themes])\n\n\tconst [theme, setThemeState] = useState<keyof Themes | undefined>(\n\t\t() => store.read()?.theme ?? defaultTheme\n\t)\n\n\tuseEffect(() => {\n\t\tif (!element) return\n\t\tconst unobserve = observeThemeFromStores([store], defaultTheme, setThemeState)\n\t\treturn unobserve\n\t}, [element, store, defaultTheme])\n\n\tconst setTheme = useCallback(\n\t\t(themeKey: keyof Themes) => {\n\t\t\tif (element) {\n\t\t\t\tsetThemeToStores([store], themeEntry(themes, themeKey))\n\t\t\t}\n\t\t},\n\t\t[element, store, themes]\n\t)\n\n\treturn [theme, setTheme]\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,SAAgB,oBACf,QACA,SAI4D;CAC5D,MAAM,UACL,SAAS,YAAY,OAAO,aAAa,cAAc,SAAS,kBAAkB;CACnF,MAAM,eAAe,SAAS;CAE9B,MAAM,iCAAsBA,mDAAoB,QAAQ,EAAE,SAAS,CAAC,EAAE,CAAC,SAAS,OAAO,CAAC;CAExF,MAAM,CAAC,OAAO,2CACP,MAAM,MAAM,EAAE,SAAS,aAC7B;AAED,4BAAgB;AACf,MAAI,CAAC,QAAS;AAEd,SADkBC,yDAAuB,CAAC,MAAM,EAAE,cAAc,cAAc;IAE5E;EAAC;EAAS;EAAO;EAAa,CAAC;AAWlC,QAAO,CAAC,+BARN,aAA2B;AAC3B,MAAI,QACH,8CAAiB,CAAC,MAAM,EAAEC,+BAAW,QAAQ,SAAS,CAAC;IAGzD;EAAC;EAAS;EAAO;EAAO,CACxB,CAEuB"}
@@ -8,7 +8,7 @@ import { ThemeMap } from "../../theme/theme-map.types.cjs";
8
8
  *
9
9
  * @param themes - Record mapping theme keys to their class name values
10
10
  * @param options.theme - Fallback theme key when no matching class is found
11
- * @param options.element - Element to read/set theme on (defaults to document.documentElement)
11
+ * @param options.element - Element to read/set theme on (accepts null e.g. from refs). Defaults to document.documentElement.
12
12
  * @returns Tuple of [currentTheme, setTheme]
13
13
  *
14
14
  * @example
@@ -27,7 +27,7 @@ import { ThemeMap } from "../../theme/theme-map.types.cjs";
27
27
  */
28
28
  declare function useThemeByClassName<Themes extends ThemeMap>(themes: Themes, options?: {
29
29
  defaultTheme?: keyof Themes | undefined;
30
- element?: Element | undefined;
30
+ element?: Element | null | undefined;
31
31
  }): [keyof Themes | undefined, (theme: keyof Themes) => void];
32
32
  //#endregion
33
33
  export { useThemeByClassName };
@@ -8,7 +8,7 @@ import { ThemeMap } from "../../theme/theme-map.types.mjs";
8
8
  *
9
9
  * @param themes - Record mapping theme keys to their class name values
10
10
  * @param options.theme - Fallback theme key when no matching class is found
11
- * @param options.element - Element to read/set theme on (defaults to document.documentElement)
11
+ * @param options.element - Element to read/set theme on (accepts null e.g. from refs). Defaults to document.documentElement.
12
12
  * @returns Tuple of [currentTheme, setTheme]
13
13
  *
14
14
  * @example
@@ -27,7 +27,7 @@ import { ThemeMap } from "../../theme/theme-map.types.mjs";
27
27
  */
28
28
  declare function useThemeByClassName<Themes extends ThemeMap>(themes: Themes, options?: {
29
29
  defaultTheme?: keyof Themes | undefined;
30
- element?: Element | undefined;
30
+ element?: Element | null | undefined;
31
31
  }): [keyof Themes | undefined, (theme: keyof Themes) => void];
32
32
  //#endregion
33
33
  export { useThemeByClassName };
@@ -11,7 +11,7 @@ import { useCallback, useEffect, useMemo, useState } from "react";
11
11
  *
12
12
  * @param themes - Record mapping theme keys to their class name values
13
13
  * @param options.theme - Fallback theme key when no matching class is found
14
- * @param options.element - Element to read/set theme on (defaults to document.documentElement)
14
+ * @param options.element - Element to read/set theme on (accepts null e.g. from refs). Defaults to document.documentElement.
15
15
  * @returns Tuple of [currentTheme, setTheme]
16
16
  *
17
17
  * @example
@@ -1 +1 @@
1
- {"version":3,"file":"use-theme-by-class-name.mjs","names":[],"sources":["../../../src/react/hooks/use-theme-by-class-name.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from 'react'\nimport { observeThemeFromStores } from '../../theme/_utils/observe-theme-from-stores.ts'\nimport { setThemeToStores } from '../../theme/_utils/set-theme-to-stores.ts'\nimport { themeEntry } from '../../theme/theme-entry.ts'\nimport type { ThemeMap } from '../../theme/theme-map.types.ts'\nimport { classNameThemeStore } from '../../theme/theme-store/class-name-theme-store/class-name-theme-store.ts'\n\n/**\n * React hook that returns the current theme (from element class) and a setter.\n * Subscribes to class changes on the element so the returned theme stays in sync.\n *\n * @param themes - Record mapping theme keys to their class name values\n * @param options.theme - Fallback theme key when no matching class is found\n * @param options.element - Element to read/set theme on (defaults to document.documentElement)\n * @returns Tuple of [currentTheme, setTheme]\n *\n * @example\n * ```tsx\n * const themes = { light: 'theme-light', dark: 'theme-dark' }\n * const [theme, setTheme] = useThemeByClassName(themes, { theme: 'light' })\n *\n * return (\n * <>\n * <span>Current: {theme}</span>\n * <button onClick={() => setTheme('dark')}>Dark</button>\n * <button onClick={() => setTheme('light')}>Light</button>\n * </>\n * )\n * ```\n */\nexport function useThemeByClassName<Themes extends ThemeMap>(\n\tthemes: Themes,\n\toptions?: {\n\t\tdefaultTheme?: keyof Themes | undefined\n\t\telement?: Element | undefined\n\t}\n): [keyof Themes | undefined, (theme: keyof Themes) => void] {\n\tconst element =\n\t\toptions?.element ?? (typeof document !== 'undefined' ? document.documentElement : undefined)\n\tconst defaultTheme = options?.defaultTheme\n\n\tconst store = useMemo(() => classNameThemeStore(themes, { element }), [element, themes])\n\n\tconst [theme, setThemeState] = useState<keyof Themes | undefined>(\n\t\t() => store.read()?.theme ?? defaultTheme\n\t)\n\n\tuseEffect(() => {\n\t\tif (!element) return\n\t\tconst unobserve = observeThemeFromStores([store], defaultTheme, setThemeState)\n\t\treturn unobserve\n\t}, [element, store, defaultTheme])\n\n\tconst setTheme = useCallback(\n\t\t(themeKey: keyof Themes) => {\n\t\t\tif (element) {\n\t\t\t\tsetThemeToStores([store], themeEntry(themes, themeKey))\n\t\t\t}\n\t\t},\n\t\t[element, store, themes]\n\t)\n\n\treturn [theme, setTheme]\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,SAAgB,oBACf,QACA,SAI4D;CAC5D,MAAM,UACL,SAAS,YAAY,OAAO,aAAa,cAAc,SAAS,kBAAkB;CACnF,MAAM,eAAe,SAAS;CAE9B,MAAM,QAAQ,cAAc,oBAAoB,QAAQ,EAAE,SAAS,CAAC,EAAE,CAAC,SAAS,OAAO,CAAC;CAExF,MAAM,CAAC,OAAO,iBAAiB,eACxB,MAAM,MAAM,EAAE,SAAS,aAC7B;AAED,iBAAgB;AACf,MAAI,CAAC,QAAS;AAEd,SADkB,uBAAuB,CAAC,MAAM,EAAE,cAAc,cAAc;IAE5E;EAAC;EAAS;EAAO;EAAa,CAAC;AAWlC,QAAO,CAAC,OATS,aACf,aAA2B;AAC3B,MAAI,QACH,kBAAiB,CAAC,MAAM,EAAE,WAAW,QAAQ,SAAS,CAAC;IAGzD;EAAC;EAAS;EAAO;EAAO,CACxB,CAEuB"}
1
+ {"version":3,"file":"use-theme-by-class-name.mjs","names":[],"sources":["../../../src/react/hooks/use-theme-by-class-name.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from 'react'\nimport { observeThemeFromStores } from '../../theme/_utils/observe-theme-from-stores.ts'\nimport { setThemeToStores } from '../../theme/_utils/set-theme-to-stores.ts'\nimport { themeEntry } from '../../theme/theme-entry.ts'\nimport type { ThemeMap } from '../../theme/theme-map.types.ts'\nimport { classNameThemeStore } from '../../theme/theme-store/class-name-theme-store/class-name-theme-store.ts'\n\n/**\n * React hook that returns the current theme (from element class) and a setter.\n * Subscribes to class changes on the element so the returned theme stays in sync.\n *\n * @param themes - Record mapping theme keys to their class name values\n * @param options.theme - Fallback theme key when no matching class is found\n * @param options.element - Element to read/set theme on (accepts null e.g. from refs). Defaults to document.documentElement.\n * @returns Tuple of [currentTheme, setTheme]\n *\n * @example\n * ```tsx\n * const themes = { light: 'theme-light', dark: 'theme-dark' }\n * const [theme, setTheme] = useThemeByClassName(themes, { theme: 'light' })\n *\n * return (\n * <>\n * <span>Current: {theme}</span>\n * <button onClick={() => setTheme('dark')}>Dark</button>\n * <button onClick={() => setTheme('light')}>Light</button>\n * </>\n * )\n * ```\n */\nexport function useThemeByClassName<Themes extends ThemeMap>(\n\tthemes: Themes,\n\toptions?: {\n\t\tdefaultTheme?: keyof Themes | undefined\n\t\telement?: Element | null | undefined\n\t}\n): [keyof Themes | undefined, (theme: keyof Themes) => void] {\n\tconst element =\n\t\toptions?.element ?? (typeof document !== 'undefined' ? document.documentElement : undefined)\n\tconst defaultTheme = options?.defaultTheme\n\n\tconst store = useMemo(() => classNameThemeStore(themes, { element }), [element, themes])\n\n\tconst [theme, setThemeState] = useState<keyof Themes | undefined>(\n\t\t() => store.read()?.theme ?? defaultTheme\n\t)\n\n\tuseEffect(() => {\n\t\tif (!element) return\n\t\tconst unobserve = observeThemeFromStores([store], defaultTheme, setThemeState)\n\t\treturn unobserve\n\t}, [element, store, defaultTheme])\n\n\tconst setTheme = useCallback(\n\t\t(themeKey: keyof Themes) => {\n\t\t\tif (element) {\n\t\t\t\tsetThemeToStores([store], themeEntry(themes, themeKey))\n\t\t\t}\n\t\t},\n\t\t[element, store, themes]\n\t)\n\n\treturn [theme, setTheme]\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,SAAgB,oBACf,QACA,SAI4D;CAC5D,MAAM,UACL,SAAS,YAAY,OAAO,aAAa,cAAc,SAAS,kBAAkB;CACnF,MAAM,eAAe,SAAS;CAE9B,MAAM,QAAQ,cAAc,oBAAoB,QAAQ,EAAE,SAAS,CAAC,EAAE,CAAC,SAAS,OAAO,CAAC;CAExF,MAAM,CAAC,OAAO,iBAAiB,eACxB,MAAM,MAAM,EAAE,SAAS,aAC7B;AAED,iBAAgB;AACf,MAAI,CAAC,QAAS;AAEd,SADkB,uBAAuB,CAAC,MAAM,EAAE,cAAc,cAAc;IAE5E;EAAC;EAAS;EAAO;EAAa,CAAC;AAWlC,QAAO,CAAC,OATS,aACf,aAA2B;AAC3B,MAAI,QACH,kBAAiB,CAAC,MAAM,EAAE,WAAW,QAAQ,SAAS,CAAC;IAGzD;EAAC;EAAS;EAAO;EAAO,CACxB,CAEuB"}
@@ -13,7 +13,7 @@ let react = require("react");
13
13
  * @param themes - Record mapping theme keys to their data attribute values
14
14
  * @param options.attributeName - Data attribute name (e.g. `data-theme`)
15
15
  * @param options.defaultTheme - Fallback theme key when no matching attribute value is found
16
- * @param options.element - Element to read/set theme on (defaults to document.documentElement)
16
+ * @param options.element - Element to read/set theme on (accepts null e.g. from refs). Defaults to document.documentElement.
17
17
  * @returns Tuple of [currentTheme, setTheme]
18
18
  *
19
19
  * @example
@@ -1 +1 @@
1
- {"version":3,"file":"use-theme-by-data-attribute.cjs","names":["dataAttributeThemeStore","observeThemeFromStores","themeEntry"],"sources":["../../../src/react/hooks/use-theme-by-data-attribute.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from 'react'\nimport { observeThemeFromStores } from '../../theme/_utils/observe-theme-from-stores.ts'\nimport { setThemeToStores } from '../../theme/_utils/set-theme-to-stores.ts'\nimport { themeEntry } from '../../theme/theme-entry.ts'\nimport type { ThemeMap } from '../../theme/theme-map.types.ts'\nimport { dataAttributeThemeStore } from '../../theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.ts'\n\n/**\n * React hook that returns the current theme (from element data attribute) and a setter.\n * Subscribes to data attribute changes on the element so the returned theme stays in sync.\n *\n * @param themes - Record mapping theme keys to their data attribute values\n * @param options.attributeName - Data attribute name (e.g. `data-theme`)\n * @param options.defaultTheme - Fallback theme key when no matching attribute value is found\n * @param options.element - Element to read/set theme on (defaults to document.documentElement)\n * @returns Tuple of [currentTheme, setTheme]\n *\n * @example\n * ```tsx\n * const themes = { light: 'theme-light', dark: 'theme-dark' }\n * const [theme, setTheme] = useThemeByDataAttribute(themes, {\n * attributeName: 'data-theme',\n * defaultTheme: 'light'\n * })\n *\n * return (\n * <>\n * <span>Current: {theme}</span>\n * <button onClick={() => setTheme('dark')}>Dark</button>\n * <button onClick={() => setTheme('light')}>Light</button>\n * </>\n * )\n * ```\n */\nexport function useThemeByDataAttribute<Themes extends ThemeMap>(\n\tthemes: Themes,\n\toptions: {\n\t\tattributeName: `data-${string}`\n\t\tdefaultTheme?: keyof Themes | undefined\n\t\telement?: Element | undefined\n\t}\n): [keyof Themes | undefined, (theme: keyof Themes) => void] {\n\tconst element =\n\t\toptions.element ?? (typeof document !== 'undefined' ? document.documentElement : undefined)\n\tconst defaultTheme = options.defaultTheme\n\tconst attributeName = options.attributeName\n\n\tconst store = useMemo(\n\t\t() =>\n\t\t\tdataAttributeThemeStore(themes, {\n\t\t\t\tattributeName,\n\t\t\t\telement\n\t\t\t}),\n\t\t[element, themes, attributeName]\n\t)\n\n\tconst [theme, setThemeState] = useState<keyof Themes | undefined>(\n\t\t() => store.read()?.theme ?? defaultTheme\n\t)\n\n\tuseEffect(() => {\n\t\tif (!element) return\n\t\tconst unobserve = observeThemeFromStores([store], defaultTheme, setThemeState)\n\t\treturn unobserve\n\t}, [element, store, defaultTheme])\n\n\tconst setTheme = useCallback(\n\t\t(themeKey: keyof Themes) => {\n\t\t\tif (element) {\n\t\t\t\tsetThemeToStores([store], themeEntry(themes, themeKey))\n\t\t\t}\n\t\t},\n\t\t[element, store, themes]\n\t)\n\n\treturn [theme, setTheme]\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAgB,wBACf,QACA,SAK4D;CAC5D,MAAM,UACL,QAAQ,YAAY,OAAO,aAAa,cAAc,SAAS,kBAAkB;CAClF,MAAM,eAAe,QAAQ;CAC7B,MAAM,gBAAgB,QAAQ;CAE9B,MAAM,iCAEJA,2DAAwB,QAAQ;EAC/B;EACA;EACA,CAAC,EACH;EAAC;EAAS;EAAQ;EAAc,CAChC;CAED,MAAM,CAAC,OAAO,2CACP,MAAM,MAAM,EAAE,SAAS,aAC7B;AAED,4BAAgB;AACf,MAAI,CAAC,QAAS;AAEd,SADkBC,yDAAuB,CAAC,MAAM,EAAE,cAAc,cAAc;IAE5E;EAAC;EAAS;EAAO;EAAa,CAAC;AAWlC,QAAO,CAAC,+BARN,aAA2B;AAC3B,MAAI,QACH,8CAAiB,CAAC,MAAM,EAAEC,+BAAW,QAAQ,SAAS,CAAC;IAGzD;EAAC;EAAS;EAAO;EAAO,CACxB,CAEuB"}
1
+ {"version":3,"file":"use-theme-by-data-attribute.cjs","names":["dataAttributeThemeStore","observeThemeFromStores","themeEntry"],"sources":["../../../src/react/hooks/use-theme-by-data-attribute.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from 'react'\nimport { observeThemeFromStores } from '../../theme/_utils/observe-theme-from-stores.ts'\nimport { setThemeToStores } from '../../theme/_utils/set-theme-to-stores.ts'\nimport { themeEntry } from '../../theme/theme-entry.ts'\nimport type { ThemeMap } from '../../theme/theme-map.types.ts'\nimport { dataAttributeThemeStore } from '../../theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.ts'\n\n/**\n * React hook that returns the current theme (from element data attribute) and a setter.\n * Subscribes to data attribute changes on the element so the returned theme stays in sync.\n *\n * @param themes - Record mapping theme keys to their data attribute values\n * @param options.attributeName - Data attribute name (e.g. `data-theme`)\n * @param options.defaultTheme - Fallback theme key when no matching attribute value is found\n * @param options.element - Element to read/set theme on (accepts null e.g. from refs). Defaults to document.documentElement.\n * @returns Tuple of [currentTheme, setTheme]\n *\n * @example\n * ```tsx\n * const themes = { light: 'theme-light', dark: 'theme-dark' }\n * const [theme, setTheme] = useThemeByDataAttribute(themes, {\n * attributeName: 'data-theme',\n * defaultTheme: 'light'\n * })\n *\n * return (\n * <>\n * <span>Current: {theme}</span>\n * <button onClick={() => setTheme('dark')}>Dark</button>\n * <button onClick={() => setTheme('light')}>Light</button>\n * </>\n * )\n * ```\n */\nexport function useThemeByDataAttribute<Themes extends ThemeMap>(\n\tthemes: Themes,\n\toptions: {\n\t\tattributeName: `data-${string}`\n\t\tdefaultTheme?: keyof Themes | undefined\n\t\telement?: Element | null | undefined\n\t}\n): [keyof Themes | undefined, (theme: keyof Themes) => void] {\n\tconst element =\n\t\toptions.element ?? (typeof document !== 'undefined' ? document.documentElement : undefined)\n\tconst defaultTheme = options.defaultTheme\n\tconst attributeName = options.attributeName\n\n\tconst store = useMemo(\n\t\t() =>\n\t\t\tdataAttributeThemeStore(themes, {\n\t\t\t\tattributeName,\n\t\t\t\telement\n\t\t\t}),\n\t\t[element, themes, attributeName]\n\t)\n\n\tconst [theme, setThemeState] = useState<keyof Themes | undefined>(\n\t\t() => store.read()?.theme ?? defaultTheme\n\t)\n\n\tuseEffect(() => {\n\t\tif (!element) return\n\t\tconst unobserve = observeThemeFromStores([store], defaultTheme, setThemeState)\n\t\treturn unobserve\n\t}, [element, store, defaultTheme])\n\n\tconst setTheme = useCallback(\n\t\t(themeKey: keyof Themes) => {\n\t\t\tif (element) {\n\t\t\t\tsetThemeToStores([store], themeEntry(themes, themeKey))\n\t\t\t}\n\t\t},\n\t\t[element, store, themes]\n\t)\n\n\treturn [theme, setTheme]\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAgB,wBACf,QACA,SAK4D;CAC5D,MAAM,UACL,QAAQ,YAAY,OAAO,aAAa,cAAc,SAAS,kBAAkB;CAClF,MAAM,eAAe,QAAQ;CAC7B,MAAM,gBAAgB,QAAQ;CAE9B,MAAM,iCAEJA,2DAAwB,QAAQ;EAC/B;EACA;EACA,CAAC,EACH;EAAC;EAAS;EAAQ;EAAc,CAChC;CAED,MAAM,CAAC,OAAO,2CACP,MAAM,MAAM,EAAE,SAAS,aAC7B;AAED,4BAAgB;AACf,MAAI,CAAC,QAAS;AAEd,SADkBC,yDAAuB,CAAC,MAAM,EAAE,cAAc,cAAc;IAE5E;EAAC;EAAS;EAAO;EAAa,CAAC;AAWlC,QAAO,CAAC,+BARN,aAA2B;AAC3B,MAAI,QACH,8CAAiB,CAAC,MAAM,EAAEC,+BAAW,QAAQ,SAAS,CAAC;IAGzD;EAAC;EAAS;EAAO;EAAO,CACxB,CAEuB"}
@@ -9,7 +9,7 @@ import { ThemeMap } from "../../theme/theme-map.types.cjs";
9
9
  * @param themes - Record mapping theme keys to their data attribute values
10
10
  * @param options.attributeName - Data attribute name (e.g. `data-theme`)
11
11
  * @param options.defaultTheme - Fallback theme key when no matching attribute value is found
12
- * @param options.element - Element to read/set theme on (defaults to document.documentElement)
12
+ * @param options.element - Element to read/set theme on (accepts null e.g. from refs). Defaults to document.documentElement.
13
13
  * @returns Tuple of [currentTheme, setTheme]
14
14
  *
15
15
  * @example
@@ -32,7 +32,7 @@ import { ThemeMap } from "../../theme/theme-map.types.cjs";
32
32
  declare function useThemeByDataAttribute<Themes extends ThemeMap>(themes: Themes, options: {
33
33
  attributeName: `data-${string}`;
34
34
  defaultTheme?: keyof Themes | undefined;
35
- element?: Element | undefined;
35
+ element?: Element | null | undefined;
36
36
  }): [keyof Themes | undefined, (theme: keyof Themes) => void];
37
37
  //#endregion
38
38
  export { useThemeByDataAttribute };
@@ -9,7 +9,7 @@ import { ThemeMap } from "../../theme/theme-map.types.mjs";
9
9
  * @param themes - Record mapping theme keys to their data attribute values
10
10
  * @param options.attributeName - Data attribute name (e.g. `data-theme`)
11
11
  * @param options.defaultTheme - Fallback theme key when no matching attribute value is found
12
- * @param options.element - Element to read/set theme on (defaults to document.documentElement)
12
+ * @param options.element - Element to read/set theme on (accepts null e.g. from refs). Defaults to document.documentElement.
13
13
  * @returns Tuple of [currentTheme, setTheme]
14
14
  *
15
15
  * @example
@@ -32,7 +32,7 @@ import { ThemeMap } from "../../theme/theme-map.types.mjs";
32
32
  declare function useThemeByDataAttribute<Themes extends ThemeMap>(themes: Themes, options: {
33
33
  attributeName: `data-${string}`;
34
34
  defaultTheme?: keyof Themes | undefined;
35
- element?: Element | undefined;
35
+ element?: Element | null | undefined;
36
36
  }): [keyof Themes | undefined, (theme: keyof Themes) => void];
37
37
  //#endregion
38
38
  export { useThemeByDataAttribute };
@@ -12,7 +12,7 @@ import { useCallback, useEffect, useMemo, useState } from "react";
12
12
  * @param themes - Record mapping theme keys to their data attribute values
13
13
  * @param options.attributeName - Data attribute name (e.g. `data-theme`)
14
14
  * @param options.defaultTheme - Fallback theme key when no matching attribute value is found
15
- * @param options.element - Element to read/set theme on (defaults to document.documentElement)
15
+ * @param options.element - Element to read/set theme on (accepts null e.g. from refs). Defaults to document.documentElement.
16
16
  * @returns Tuple of [currentTheme, setTheme]
17
17
  *
18
18
  * @example
@@ -1 +1 @@
1
- {"version":3,"file":"use-theme-by-data-attribute.mjs","names":[],"sources":["../../../src/react/hooks/use-theme-by-data-attribute.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from 'react'\nimport { observeThemeFromStores } from '../../theme/_utils/observe-theme-from-stores.ts'\nimport { setThemeToStores } from '../../theme/_utils/set-theme-to-stores.ts'\nimport { themeEntry } from '../../theme/theme-entry.ts'\nimport type { ThemeMap } from '../../theme/theme-map.types.ts'\nimport { dataAttributeThemeStore } from '../../theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.ts'\n\n/**\n * React hook that returns the current theme (from element data attribute) and a setter.\n * Subscribes to data attribute changes on the element so the returned theme stays in sync.\n *\n * @param themes - Record mapping theme keys to their data attribute values\n * @param options.attributeName - Data attribute name (e.g. `data-theme`)\n * @param options.defaultTheme - Fallback theme key when no matching attribute value is found\n * @param options.element - Element to read/set theme on (defaults to document.documentElement)\n * @returns Tuple of [currentTheme, setTheme]\n *\n * @example\n * ```tsx\n * const themes = { light: 'theme-light', dark: 'theme-dark' }\n * const [theme, setTheme] = useThemeByDataAttribute(themes, {\n * attributeName: 'data-theme',\n * defaultTheme: 'light'\n * })\n *\n * return (\n * <>\n * <span>Current: {theme}</span>\n * <button onClick={() => setTheme('dark')}>Dark</button>\n * <button onClick={() => setTheme('light')}>Light</button>\n * </>\n * )\n * ```\n */\nexport function useThemeByDataAttribute<Themes extends ThemeMap>(\n\tthemes: Themes,\n\toptions: {\n\t\tattributeName: `data-${string}`\n\t\tdefaultTheme?: keyof Themes | undefined\n\t\telement?: Element | undefined\n\t}\n): [keyof Themes | undefined, (theme: keyof Themes) => void] {\n\tconst element =\n\t\toptions.element ?? (typeof document !== 'undefined' ? document.documentElement : undefined)\n\tconst defaultTheme = options.defaultTheme\n\tconst attributeName = options.attributeName\n\n\tconst store = useMemo(\n\t\t() =>\n\t\t\tdataAttributeThemeStore(themes, {\n\t\t\t\tattributeName,\n\t\t\t\telement\n\t\t\t}),\n\t\t[element, themes, attributeName]\n\t)\n\n\tconst [theme, setThemeState] = useState<keyof Themes | undefined>(\n\t\t() => store.read()?.theme ?? defaultTheme\n\t)\n\n\tuseEffect(() => {\n\t\tif (!element) return\n\t\tconst unobserve = observeThemeFromStores([store], defaultTheme, setThemeState)\n\t\treturn unobserve\n\t}, [element, store, defaultTheme])\n\n\tconst setTheme = useCallback(\n\t\t(themeKey: keyof Themes) => {\n\t\t\tif (element) {\n\t\t\t\tsetThemeToStores([store], themeEntry(themes, themeKey))\n\t\t\t}\n\t\t},\n\t\t[element, store, themes]\n\t)\n\n\treturn [theme, setTheme]\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAgB,wBACf,QACA,SAK4D;CAC5D,MAAM,UACL,QAAQ,YAAY,OAAO,aAAa,cAAc,SAAS,kBAAkB;CAClF,MAAM,eAAe,QAAQ;CAC7B,MAAM,gBAAgB,QAAQ;CAE9B,MAAM,QAAQ,cAEZ,wBAAwB,QAAQ;EAC/B;EACA;EACA,CAAC,EACH;EAAC;EAAS;EAAQ;EAAc,CAChC;CAED,MAAM,CAAC,OAAO,iBAAiB,eACxB,MAAM,MAAM,EAAE,SAAS,aAC7B;AAED,iBAAgB;AACf,MAAI,CAAC,QAAS;AAEd,SADkB,uBAAuB,CAAC,MAAM,EAAE,cAAc,cAAc;IAE5E;EAAC;EAAS;EAAO;EAAa,CAAC;AAWlC,QAAO,CAAC,OATS,aACf,aAA2B;AAC3B,MAAI,QACH,kBAAiB,CAAC,MAAM,EAAE,WAAW,QAAQ,SAAS,CAAC;IAGzD;EAAC;EAAS;EAAO;EAAO,CACxB,CAEuB"}
1
+ {"version":3,"file":"use-theme-by-data-attribute.mjs","names":[],"sources":["../../../src/react/hooks/use-theme-by-data-attribute.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from 'react'\nimport { observeThemeFromStores } from '../../theme/_utils/observe-theme-from-stores.ts'\nimport { setThemeToStores } from '../../theme/_utils/set-theme-to-stores.ts'\nimport { themeEntry } from '../../theme/theme-entry.ts'\nimport type { ThemeMap } from '../../theme/theme-map.types.ts'\nimport { dataAttributeThemeStore } from '../../theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.ts'\n\n/**\n * React hook that returns the current theme (from element data attribute) and a setter.\n * Subscribes to data attribute changes on the element so the returned theme stays in sync.\n *\n * @param themes - Record mapping theme keys to their data attribute values\n * @param options.attributeName - Data attribute name (e.g. `data-theme`)\n * @param options.defaultTheme - Fallback theme key when no matching attribute value is found\n * @param options.element - Element to read/set theme on (accepts null e.g. from refs). Defaults to document.documentElement.\n * @returns Tuple of [currentTheme, setTheme]\n *\n * @example\n * ```tsx\n * const themes = { light: 'theme-light', dark: 'theme-dark' }\n * const [theme, setTheme] = useThemeByDataAttribute(themes, {\n * attributeName: 'data-theme',\n * defaultTheme: 'light'\n * })\n *\n * return (\n * <>\n * <span>Current: {theme}</span>\n * <button onClick={() => setTheme('dark')}>Dark</button>\n * <button onClick={() => setTheme('light')}>Light</button>\n * </>\n * )\n * ```\n */\nexport function useThemeByDataAttribute<Themes extends ThemeMap>(\n\tthemes: Themes,\n\toptions: {\n\t\tattributeName: `data-${string}`\n\t\tdefaultTheme?: keyof Themes | undefined\n\t\telement?: Element | null | undefined\n\t}\n): [keyof Themes | undefined, (theme: keyof Themes) => void] {\n\tconst element =\n\t\toptions.element ?? (typeof document !== 'undefined' ? document.documentElement : undefined)\n\tconst defaultTheme = options.defaultTheme\n\tconst attributeName = options.attributeName\n\n\tconst store = useMemo(\n\t\t() =>\n\t\t\tdataAttributeThemeStore(themes, {\n\t\t\t\tattributeName,\n\t\t\t\telement\n\t\t\t}),\n\t\t[element, themes, attributeName]\n\t)\n\n\tconst [theme, setThemeState] = useState<keyof Themes | undefined>(\n\t\t() => store.read()?.theme ?? defaultTheme\n\t)\n\n\tuseEffect(() => {\n\t\tif (!element) return\n\t\tconst unobserve = observeThemeFromStores([store], defaultTheme, setThemeState)\n\t\treturn unobserve\n\t}, [element, store, defaultTheme])\n\n\tconst setTheme = useCallback(\n\t\t(themeKey: keyof Themes) => {\n\t\t\tif (element) {\n\t\t\t\tsetThemeToStores([store], themeEntry(themes, themeKey))\n\t\t\t}\n\t\t},\n\t\t[element, store, themes]\n\t)\n\n\treturn [theme, setTheme]\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAgB,wBACf,QACA,SAK4D;CAC5D,MAAM,UACL,QAAQ,YAAY,OAAO,aAAa,cAAc,SAAS,kBAAkB;CAClF,MAAM,eAAe,QAAQ;CAC7B,MAAM,gBAAgB,QAAQ;CAE9B,MAAM,QAAQ,cAEZ,wBAAwB,QAAQ;EAC/B;EACA;EACA,CAAC,EACH;EAAC;EAAS;EAAQ;EAAc,CAChC;CAED,MAAM,CAAC,OAAO,iBAAiB,eACxB,MAAM,MAAM,EAAE,SAAS,aAC7B;AAED,iBAAgB;AACf,MAAI,CAAC,QAAS;AAEd,SADkB,uBAAuB,CAAC,MAAM,EAAE,cAAc,cAAc;IAE5E;EAAC;EAAS;EAAO;EAAa,CAAC;AAWlC,QAAO,CAAC,OATS,aACf,aAA2B;AAC3B,MAAI,QACH,kBAAiB,CAAC,MAAM,EAAE,WAAW,QAAQ,SAAS,CAAC;IAGzD;EAAC;EAAS;EAAO;EAAO,CACxB,CAEuB"}
@@ -1 +1 @@
1
- {"version":3,"file":"create-theme-hook.cjs","names":["lastTheme: keyof Themes | undefined","unobserve: () => void","themeEntry","composeThemeStores"],"sources":["../../../src/react/theme/create-theme-hook.ts"],"sourcesContent":["import { useCallback, useSyncExternalStore } from 'react'\nimport type { Required } from 'type-plus'\nimport {\n\ttype ComposeThemeStoreEntry,\n\ttype ComposeThemeStoresOptions,\n\tcomposeThemeStores\n} from '../../theme/compose-theme-stores.ts'\nimport { themeEntry } from '../../theme/theme-entry.ts'\nimport type { ThemeEntry } from '../../theme/theme-entry.types.ts'\nimport type { ThemeMap } from '../../theme/theme-map.types.ts'\nimport type { AsyncThemeStore } from '../../theme/theme-store/async-theme-store.types.ts'\nimport type { ThemeStoreFactory } from '../../theme/theme-store/theme-store-factory.types.ts'\n\n/**\n * Creates a React hook for theme selection that reads from and writes to composed theme stores.\n *\n * The returned hook subscribes to store changes via `useSyncExternalStore`, supports SSR\n * (uses `defaultTheme` for server snapshot), and returns a `[theme, setTheme]` tuple.\n * Channels are cached per store configuration and default theme for efficient reuse.\n *\n * @param themes - ThemeMap mapping theme keys to their values (e.g. CSS class names)\n * @param stores - Array of 1–8 theme stores or factory configs (see ComposeThemeStoreEntry)\n * @param options.defaultTheme - Fallback theme key when stores return empty; also used for SSR\n * @returns A `useTheme` hook that returns `[currentTheme, setTheme]` tuple\n *\n * @example\n * ```ts\n * const useTheme = createThemeHook(themes, [localStorageStore], { defaultTheme: 'light' })\n * const [theme, setTheme] = useTheme()\n * setTheme('dark')\n * ```\n */\nexport function createThemeHook<\n\tThemes extends ThemeMap,\n\tA extends ThemeStoreFactory<Themes> = never,\n\tB extends ThemeStoreFactory<Themes> = never,\n\tC extends ThemeStoreFactory<Themes> = never,\n\tD extends ThemeStoreFactory<Themes> = never,\n\tE extends ThemeStoreFactory<Themes> = never,\n\tF extends ThemeStoreFactory<Themes> = never,\n\tG extends ThemeStoreFactory<Themes> = never,\n\tH extends ThemeStoreFactory<Themes> = never\n>(\n\tthemes: Themes,\n\tstores: readonly [\n\t\tstore1: ComposeThemeStoreEntry<Themes, A>,\n\t\tstore2?: ComposeThemeStoreEntry<Themes, B>,\n\t\tstore3?: ComposeThemeStoreEntry<Themes, C>,\n\t\tstore4?: ComposeThemeStoreEntry<Themes, D>,\n\t\tstore5?: ComposeThemeStoreEntry<Themes, E>,\n\t\tstore6?: ComposeThemeStoreEntry<Themes, F>,\n\t\tstore7?: ComposeThemeStoreEntry<Themes, G>,\n\t\tstore8?: ComposeThemeStoreEntry<Themes, H>\n\t],\n\toptions?: ComposeThemeStoresOptions<Themes>\n): (\n\toverrideDefaultTheme?: keyof Themes | undefined\n) => [keyof Themes | undefined, (theme: keyof Themes) => void] {\n\tconst { defaultTheme } = options ?? {}\n\treturn function useTheme(overrideDefaultTheme?: keyof Themes | undefined) {\n\t\tconst effectiveDefault = overrideDefaultTheme ?? defaultTheme\n\t\tconst channel = getOrCreateChannel<Themes, A, B, C, D, E, F, G, H>(themes, stores, {\n\t\t\tdefaultTheme: effectiveDefault\n\t\t})\n\n\t\tconst theme = useSyncExternalStore<keyof Themes | undefined>(\n\t\t\tchannel.subscribe,\n\t\t\tchannel.getSnapshot,\n\t\t\tchannel.getServerSnapshot\n\t\t)\n\n\t\tconst setTheme = useCallback(\n\t\t\tasync (newTheme: keyof Themes) => {\n\t\t\t\tawait channel.setTheme(newTheme)\n\t\t\t},\n\t\t\t[channel]\n\t\t)\n\n\t\treturn [theme, setTheme]\n\t}\n}\n\n/**\n * Creates a subscription channel bridging a composed theme store to React's useSyncExternalStore.\n * Manages listeners, initial async read from store, and delegates setTheme to store.write.\n *\n * @internal\n */\nfunction createSharedChannel<Themes extends ThemeMap>(\n\tthemes: Themes,\n\tcomposedStore: Required<AsyncThemeStore<Themes>>,\n\tdefaultTheme: keyof Themes | undefined\n) {\n\tlet lastTheme: keyof Themes | undefined = defaultTheme\n\tconst listeners = new Set<(theme: keyof Themes | undefined) => void>()\n\n\tconst notify = (theme: keyof Themes | undefined) => {\n\t\tlastTheme = theme\n\t\tfor (const fn of listeners) {\n\t\t\tfn(theme)\n\t\t}\n\t}\n\n\tconst handleStoreUpdate = (entry: ThemeEntry<Themes> | undefined | null) => {\n\t\tnotify(entry?.theme ?? defaultTheme)\n\t}\n\n\t// Initial read to populate lastTheme (compose store subscribe has no initial notify)\n\tvoid Promise.resolve(composedStore.read()).then(\n\t\t(entry: ThemeEntry<Themes> | undefined | null) => {\n\t\t\tnotify(entry?.theme ?? defaultTheme)\n\t\t}\n\t)\n\n\tlet unobserve: () => void = composedStore.subscribe(handleStoreUpdate)\n\tlet isSubscribedToStore = true\n\n\tconst subscribe = (listener: (theme: keyof Themes | undefined) => void) => {\n\t\tif (!isSubscribedToStore) {\n\t\t\tunobserve = composedStore.subscribe(handleStoreUpdate)\n\t\t\tisSubscribedToStore = true\n\t\t}\n\t\tlisteners.add(listener)\n\t\tlistener(lastTheme)\n\t\treturn () => {\n\t\t\tlisteners.delete(listener)\n\t\t\tif (listeners.size === 0) {\n\t\t\t\tunobserve()\n\t\t\t\tisSubscribedToStore = false\n\t\t\t}\n\t\t}\n\t}\n\n\tconst getSnapshot = (): keyof Themes | undefined => lastTheme\n\tconst getServerSnapshot = (): keyof Themes | undefined => defaultTheme\n\n\treturn {\n\t\tsubscribe,\n\t\tgetSnapshot,\n\t\tgetServerSnapshot,\n\t\tsetTheme: (theme: keyof Themes) => composedStore.write(themeEntry(themes, theme))\n\t}\n}\n\nconst channelsByStores = new WeakMap<\n\tobject,\n\tMap<string | undefined, ReturnType<typeof createSharedChannel<any>>>\n>()\n\n/**\n * Returns a cached shared channel for the given themes, stores, and defaultTheme.\n * Channels are keyed by stores (WeakMap) and defaultTheme to avoid duplicate subscriptions.\n *\n * @internal\n */\nfunction getOrCreateChannel<\n\tThemes extends ThemeMap,\n\tA extends ThemeStoreFactory<Themes> = never,\n\tB extends ThemeStoreFactory<Themes> = never,\n\tC extends ThemeStoreFactory<Themes> = never,\n\tD extends ThemeStoreFactory<Themes> = never,\n\tE extends ThemeStoreFactory<Themes> = never,\n\tF extends ThemeStoreFactory<Themes> = never,\n\tG extends ThemeStoreFactory<Themes> = never,\n\tH extends ThemeStoreFactory<Themes> = never\n>(\n\tthemes: Themes,\n\tstores: readonly [\n\t\tstore1: ComposeThemeStoreEntry<Themes, A>,\n\t\tstore2?: ComposeThemeStoreEntry<Themes, B>,\n\t\tstore3?: ComposeThemeStoreEntry<Themes, C>,\n\t\tstore4?: ComposeThemeStoreEntry<Themes, D>,\n\t\tstore5?: ComposeThemeStoreEntry<Themes, E>,\n\t\tstore6?: ComposeThemeStoreEntry<Themes, F>,\n\t\tstore7?: ComposeThemeStoreEntry<Themes, G>,\n\t\tstore8?: ComposeThemeStoreEntry<Themes, H>\n\t],\n\toptions?: ComposeThemeStoresOptions<Themes> | undefined\n) {\n\tconst { defaultTheme } = options ?? {}\n\tconst storesKey = stores as unknown as object\n\tlet byDefault = channelsByStores.get(storesKey) as Map<\n\t\tkeyof Themes | undefined,\n\t\tReturnType<typeof createSharedChannel<Themes>>\n\t>\n\tif (!byDefault) {\n\t\tbyDefault = new Map<keyof Themes | undefined, ReturnType<typeof createSharedChannel<Themes>>>()\n\t\tchannelsByStores.set(storesKey, byDefault as any)\n\t}\n\tlet channel = byDefault.get(defaultTheme) as ReturnType<typeof createSharedChannel<Themes>>\n\tif (!channel) {\n\t\tconst composedStore = composeThemeStores(themes, stores, { defaultTheme })\n\t\tchannel = createSharedChannel<Themes>(themes, composedStore, defaultTheme)\n\t\tbyDefault.set(defaultTheme, channel)\n\t}\n\treturn channel\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,SAAgB,gBAWf,QACA,QAUA,SAG8D;CAC9D,MAAM,EAAE,iBAAiB,WAAW,EAAE;AACtC,QAAO,SAAS,SAAS,sBAAiD;EAEzE,MAAM,UAAU,mBAAmD,QAAQ,QAAQ,EAClF,cAFwB,wBAAwB,cAGhD,CAAC;AAeF,SAAO,iCAZN,QAAQ,WACR,QAAQ,aACR,QAAQ,kBACR,yBAGA,OAAO,aAA2B;AACjC,SAAM,QAAQ,SAAS,SAAS;KAEjC,CAAC,QAAQ,CACT,CAEuB;;;;;;;;;AAU1B,SAAS,oBACR,QACA,eACA,cACC;CACD,IAAIA,YAAsC;CAC1C,MAAM,4BAAY,IAAI,KAAgD;CAEtE,MAAM,UAAU,UAAoC;AACnD,cAAY;AACZ,OAAK,MAAM,MAAM,UAChB,IAAG,MAAM;;CAIX,MAAM,qBAAqB,UAAiD;AAC3E,SAAO,OAAO,SAAS,aAAa;;AAIrC,CAAK,QAAQ,QAAQ,cAAc,MAAM,CAAC,CAAC,MACzC,UAAiD;AACjD,SAAO,OAAO,SAAS,aAAa;GAErC;CAED,IAAIC,YAAwB,cAAc,UAAU,kBAAkB;CACtE,IAAI,sBAAsB;CAE1B,MAAM,aAAa,aAAwD;AAC1E,MAAI,CAAC,qBAAqB;AACzB,eAAY,cAAc,UAAU,kBAAkB;AACtD,yBAAsB;;AAEvB,YAAU,IAAI,SAAS;AACvB,WAAS,UAAU;AACnB,eAAa;AACZ,aAAU,OAAO,SAAS;AAC1B,OAAI,UAAU,SAAS,GAAG;AACzB,eAAW;AACX,0BAAsB;;;;CAKzB,MAAM,oBAA8C;CACpD,MAAM,0BAAoD;AAE1D,QAAO;EACN;EACA;EACA;EACA,WAAW,UAAwB,cAAc,MAAMC,+BAAW,QAAQ,MAAM,CAAC;EACjF;;AAGF,MAAM,mCAAmB,IAAI,SAG1B;;;;;;;AAQH,SAAS,mBAWR,QACA,QAUA,SACC;CACD,MAAM,EAAE,iBAAiB,WAAW,EAAE;CACtC,MAAM,YAAY;CAClB,IAAI,YAAY,iBAAiB,IAAI,UAAU;AAI/C,KAAI,CAAC,WAAW;AACf,8BAAY,IAAI,KAA+E;AAC/F,mBAAiB,IAAI,WAAW,UAAiB;;CAElD,IAAI,UAAU,UAAU,IAAI,aAAa;AACzC,KAAI,CAAC,SAAS;AAEb,YAAU,oBAA4B,QADhBC,gDAAmB,QAAQ,QAAQ,EAAE,cAAc,CAAC,EACb,aAAa;AAC1E,YAAU,IAAI,cAAc,QAAQ;;AAErC,QAAO"}
1
+ {"version":3,"file":"create-theme-hook.cjs","names":["lastTheme: keyof Themes | undefined","unobserve: () => void","themeEntry","composeThemeStores"],"sources":["../../../src/react/theme/create-theme-hook.ts"],"sourcesContent":["import { useCallback, useSyncExternalStore } from 'react'\nimport type { Required } from 'type-plus'\nimport {\n\ttype ComposeThemeStoreEntry,\n\ttype ComposeThemeStoresOptions,\n\tcomposeThemeStores\n} from '../../theme/compose-theme-stores.ts'\nimport { themeEntry } from '../../theme/theme-entry.ts'\nimport type { ThemeEntry } from '../../theme/theme-entry.types.ts'\nimport type { ThemeMap } from '../../theme/theme-map.types.ts'\nimport type { AsyncThemeStore } from '../../theme/theme-store/async-theme-store.types.ts'\nimport type { ThemeStoreFactory } from '../../theme/theme-store/theme-store-factory.types.ts'\n\n/**\n * Creates a React hook for theme selection that reads from and writes to composed theme stores.\n *\n * The returned hook subscribes to store changes via `useSyncExternalStore`, supports SSR\n * (uses `defaultTheme` for server snapshot), and returns a `[theme, setTheme]` tuple.\n * Channels are cached per store configuration and default theme for efficient reuse.\n *\n * @param themes - ThemeMap mapping theme keys to their values (e.g. CSS class names)\n * @param stores - Array of 1–8 theme stores or factory configs (see ComposeThemeStoreEntry)\n * @param options.defaultTheme - Fallback theme key when stores return empty; also used for SSR\n * @returns A `useTheme` hook that returns `[currentTheme, setTheme]` tuple\n *\n * @example\n * ```ts\n * const useTheme = createThemeHook(themes, [localStorageStore], { defaultTheme: 'light' })\n * const [theme, setTheme] = useTheme()\n * setTheme('dark')\n * ```\n */\nexport function createThemeHook<\n\tThemes extends ThemeMap,\n\tA extends ThemeStoreFactory<Themes> = never,\n\tB extends ThemeStoreFactory<Themes> = never,\n\tC extends ThemeStoreFactory<Themes> = never,\n\tD extends ThemeStoreFactory<Themes> = never,\n\tE extends ThemeStoreFactory<Themes> = never,\n\tF extends ThemeStoreFactory<Themes> = never,\n\tG extends ThemeStoreFactory<Themes> = never,\n\tH extends ThemeStoreFactory<Themes> = never\n>(\n\tthemes: Themes,\n\tstores: readonly [\n\t\tstore1: ComposeThemeStoreEntry<Themes, A>,\n\t\tstore2?: ComposeThemeStoreEntry<Themes, B>,\n\t\tstore3?: ComposeThemeStoreEntry<Themes, C>,\n\t\tstore4?: ComposeThemeStoreEntry<Themes, D>,\n\t\tstore5?: ComposeThemeStoreEntry<Themes, E>,\n\t\tstore6?: ComposeThemeStoreEntry<Themes, F>,\n\t\tstore7?: ComposeThemeStoreEntry<Themes, G>,\n\t\tstore8?: ComposeThemeStoreEntry<Themes, H>\n\t],\n\toptions?: ComposeThemeStoresOptions<Themes>\n): (\n\toverrideDefaultTheme?: keyof Themes | undefined\n) => [keyof Themes | undefined, (theme: keyof Themes) => void] {\n\tconst { defaultTheme } = options ?? {}\n\treturn function useTheme(overrideDefaultTheme?: keyof Themes | undefined) {\n\t\tconst effectiveDefault = overrideDefaultTheme ?? defaultTheme\n\t\tconst channel = getOrCreateChannel<Themes, A, B, C, D, E, F, G, H>(themes, stores, {\n\t\t\tdefaultTheme: effectiveDefault\n\t\t})\n\n\t\tconst theme = useSyncExternalStore<keyof Themes | undefined>(\n\t\t\tchannel.subscribe,\n\t\t\tchannel.getSnapshot,\n\t\t\tchannel.getServerSnapshot\n\t\t)\n\n\t\tconst setTheme = useCallback(\n\t\t\tasync (newTheme: keyof Themes) => {\n\t\t\t\tawait channel.setTheme(newTheme)\n\t\t\t},\n\t\t\t[channel]\n\t\t)\n\n\t\treturn [theme, setTheme]\n\t}\n}\n\n/**\n * Creates a subscription channel bridging a composed theme store to React's useSyncExternalStore.\n * Manages listeners, initial async read from store, and delegates setTheme to store.write.\n *\n * @internal\n */\nfunction createSharedChannel<Themes extends ThemeMap>(\n\tthemes: Themes,\n\tcomposedStore: Required<AsyncThemeStore<Themes>>,\n\tdefaultTheme: keyof Themes | undefined\n) {\n\tlet lastTheme: keyof Themes | undefined = defaultTheme\n\tconst listeners = new Set<(theme: keyof Themes | undefined) => void>()\n\n\tconst notify = (theme: keyof Themes | undefined) => {\n\t\tlastTheme = theme\n\t\tfor (const fn of listeners) {\n\t\t\tfn(theme)\n\t\t}\n\t}\n\n\tconst handleStoreUpdate = (entry: ThemeEntry<Themes> | undefined) => {\n\t\tnotify(entry?.theme ?? defaultTheme)\n\t}\n\n\t// Initial read to populate lastTheme (compose store subscribe has no initial notify)\n\tvoid Promise.resolve(composedStore.read()).then((entry: ThemeEntry<Themes> | undefined) => {\n\t\tnotify(entry?.theme ?? defaultTheme)\n\t})\n\n\tlet unobserve: () => void = composedStore.subscribe(handleStoreUpdate)\n\tlet isSubscribedToStore = true\n\n\tconst subscribe = (listener: (theme: keyof Themes | undefined) => void) => {\n\t\tif (!isSubscribedToStore) {\n\t\t\tunobserve = composedStore.subscribe(handleStoreUpdate)\n\t\t\tisSubscribedToStore = true\n\t\t}\n\t\tlisteners.add(listener)\n\t\tlistener(lastTheme)\n\t\treturn () => {\n\t\t\tlisteners.delete(listener)\n\t\t\tif (listeners.size === 0) {\n\t\t\t\tunobserve()\n\t\t\t\tisSubscribedToStore = false\n\t\t\t}\n\t\t}\n\t}\n\n\tconst getSnapshot = (): keyof Themes | undefined => lastTheme\n\tconst getServerSnapshot = (): keyof Themes | undefined => defaultTheme\n\n\treturn {\n\t\tsubscribe,\n\t\tgetSnapshot,\n\t\tgetServerSnapshot,\n\t\tsetTheme: (theme: keyof Themes) => composedStore.write(themeEntry(themes, theme))\n\t}\n}\n\nconst channelsByStores = new WeakMap<\n\tobject,\n\tMap<string | undefined, ReturnType<typeof createSharedChannel<any>>>\n>()\n\n/**\n * Returns a cached shared channel for the given themes, stores, and defaultTheme.\n * Channels are keyed by stores (WeakMap) and defaultTheme to avoid duplicate subscriptions.\n *\n * @internal\n */\nfunction getOrCreateChannel<\n\tThemes extends ThemeMap,\n\tA extends ThemeStoreFactory<Themes> = never,\n\tB extends ThemeStoreFactory<Themes> = never,\n\tC extends ThemeStoreFactory<Themes> = never,\n\tD extends ThemeStoreFactory<Themes> = never,\n\tE extends ThemeStoreFactory<Themes> = never,\n\tF extends ThemeStoreFactory<Themes> = never,\n\tG extends ThemeStoreFactory<Themes> = never,\n\tH extends ThemeStoreFactory<Themes> = never\n>(\n\tthemes: Themes,\n\tstores: readonly [\n\t\tstore1: ComposeThemeStoreEntry<Themes, A>,\n\t\tstore2?: ComposeThemeStoreEntry<Themes, B>,\n\t\tstore3?: ComposeThemeStoreEntry<Themes, C>,\n\t\tstore4?: ComposeThemeStoreEntry<Themes, D>,\n\t\tstore5?: ComposeThemeStoreEntry<Themes, E>,\n\t\tstore6?: ComposeThemeStoreEntry<Themes, F>,\n\t\tstore7?: ComposeThemeStoreEntry<Themes, G>,\n\t\tstore8?: ComposeThemeStoreEntry<Themes, H>\n\t],\n\toptions?: ComposeThemeStoresOptions<Themes> | undefined\n) {\n\tconst { defaultTheme } = options ?? {}\n\tconst storesKey = stores as unknown as object\n\tlet byDefault = channelsByStores.get(storesKey) as Map<\n\t\tkeyof Themes | undefined,\n\t\tReturnType<typeof createSharedChannel<Themes>>\n\t>\n\tif (!byDefault) {\n\t\tbyDefault = new Map<keyof Themes | undefined, ReturnType<typeof createSharedChannel<Themes>>>()\n\t\tchannelsByStores.set(storesKey, byDefault as any)\n\t}\n\tlet channel = byDefault.get(defaultTheme) as ReturnType<typeof createSharedChannel<Themes>>\n\tif (!channel) {\n\t\tconst composedStore = composeThemeStores(themes, stores, { defaultTheme })\n\t\tchannel = createSharedChannel<Themes>(themes, composedStore, defaultTheme)\n\t\tbyDefault.set(defaultTheme, channel)\n\t}\n\treturn channel\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,SAAgB,gBAWf,QACA,QAUA,SAG8D;CAC9D,MAAM,EAAE,iBAAiB,WAAW,EAAE;AACtC,QAAO,SAAS,SAAS,sBAAiD;EAEzE,MAAM,UAAU,mBAAmD,QAAQ,QAAQ,EAClF,cAFwB,wBAAwB,cAGhD,CAAC;AAeF,SAAO,iCAZN,QAAQ,WACR,QAAQ,aACR,QAAQ,kBACR,yBAGA,OAAO,aAA2B;AACjC,SAAM,QAAQ,SAAS,SAAS;KAEjC,CAAC,QAAQ,CACT,CAEuB;;;;;;;;;AAU1B,SAAS,oBACR,QACA,eACA,cACC;CACD,IAAIA,YAAsC;CAC1C,MAAM,4BAAY,IAAI,KAAgD;CAEtE,MAAM,UAAU,UAAoC;AACnD,cAAY;AACZ,OAAK,MAAM,MAAM,UAChB,IAAG,MAAM;;CAIX,MAAM,qBAAqB,UAA0C;AACpE,SAAO,OAAO,SAAS,aAAa;;AAIrC,CAAK,QAAQ,QAAQ,cAAc,MAAM,CAAC,CAAC,MAAM,UAA0C;AAC1F,SAAO,OAAO,SAAS,aAAa;GACnC;CAEF,IAAIC,YAAwB,cAAc,UAAU,kBAAkB;CACtE,IAAI,sBAAsB;CAE1B,MAAM,aAAa,aAAwD;AAC1E,MAAI,CAAC,qBAAqB;AACzB,eAAY,cAAc,UAAU,kBAAkB;AACtD,yBAAsB;;AAEvB,YAAU,IAAI,SAAS;AACvB,WAAS,UAAU;AACnB,eAAa;AACZ,aAAU,OAAO,SAAS;AAC1B,OAAI,UAAU,SAAS,GAAG;AACzB,eAAW;AACX,0BAAsB;;;;CAKzB,MAAM,oBAA8C;CACpD,MAAM,0BAAoD;AAE1D,QAAO;EACN;EACA;EACA;EACA,WAAW,UAAwB,cAAc,MAAMC,+BAAW,QAAQ,MAAM,CAAC;EACjF;;AAGF,MAAM,mCAAmB,IAAI,SAG1B;;;;;;;AAQH,SAAS,mBAWR,QACA,QAUA,SACC;CACD,MAAM,EAAE,iBAAiB,WAAW,EAAE;CACtC,MAAM,YAAY;CAClB,IAAI,YAAY,iBAAiB,IAAI,UAAU;AAI/C,KAAI,CAAC,WAAW;AACf,8BAAY,IAAI,KAA+E;AAC/F,mBAAiB,IAAI,WAAW,UAAiB;;CAElD,IAAI,UAAU,UAAU,IAAI,aAAa;AACzC,KAAI,CAAC,SAAS;AAEb,YAAU,oBAA4B,QADhBC,gDAAmB,QAAQ,QAAQ,EAAE,cAAc,CAAC,EACb,aAAa;AAC1E,YAAU,IAAI,cAAc,QAAQ;;AAErC,QAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"create-theme-hook.mjs","names":["lastTheme: keyof Themes | undefined","unobserve: () => void"],"sources":["../../../src/react/theme/create-theme-hook.ts"],"sourcesContent":["import { useCallback, useSyncExternalStore } from 'react'\nimport type { Required } from 'type-plus'\nimport {\n\ttype ComposeThemeStoreEntry,\n\ttype ComposeThemeStoresOptions,\n\tcomposeThemeStores\n} from '../../theme/compose-theme-stores.ts'\nimport { themeEntry } from '../../theme/theme-entry.ts'\nimport type { ThemeEntry } from '../../theme/theme-entry.types.ts'\nimport type { ThemeMap } from '../../theme/theme-map.types.ts'\nimport type { AsyncThemeStore } from '../../theme/theme-store/async-theme-store.types.ts'\nimport type { ThemeStoreFactory } from '../../theme/theme-store/theme-store-factory.types.ts'\n\n/**\n * Creates a React hook for theme selection that reads from and writes to composed theme stores.\n *\n * The returned hook subscribes to store changes via `useSyncExternalStore`, supports SSR\n * (uses `defaultTheme` for server snapshot), and returns a `[theme, setTheme]` tuple.\n * Channels are cached per store configuration and default theme for efficient reuse.\n *\n * @param themes - ThemeMap mapping theme keys to their values (e.g. CSS class names)\n * @param stores - Array of 1–8 theme stores or factory configs (see ComposeThemeStoreEntry)\n * @param options.defaultTheme - Fallback theme key when stores return empty; also used for SSR\n * @returns A `useTheme` hook that returns `[currentTheme, setTheme]` tuple\n *\n * @example\n * ```ts\n * const useTheme = createThemeHook(themes, [localStorageStore], { defaultTheme: 'light' })\n * const [theme, setTheme] = useTheme()\n * setTheme('dark')\n * ```\n */\nexport function createThemeHook<\n\tThemes extends ThemeMap,\n\tA extends ThemeStoreFactory<Themes> = never,\n\tB extends ThemeStoreFactory<Themes> = never,\n\tC extends ThemeStoreFactory<Themes> = never,\n\tD extends ThemeStoreFactory<Themes> = never,\n\tE extends ThemeStoreFactory<Themes> = never,\n\tF extends ThemeStoreFactory<Themes> = never,\n\tG extends ThemeStoreFactory<Themes> = never,\n\tH extends ThemeStoreFactory<Themes> = never\n>(\n\tthemes: Themes,\n\tstores: readonly [\n\t\tstore1: ComposeThemeStoreEntry<Themes, A>,\n\t\tstore2?: ComposeThemeStoreEntry<Themes, B>,\n\t\tstore3?: ComposeThemeStoreEntry<Themes, C>,\n\t\tstore4?: ComposeThemeStoreEntry<Themes, D>,\n\t\tstore5?: ComposeThemeStoreEntry<Themes, E>,\n\t\tstore6?: ComposeThemeStoreEntry<Themes, F>,\n\t\tstore7?: ComposeThemeStoreEntry<Themes, G>,\n\t\tstore8?: ComposeThemeStoreEntry<Themes, H>\n\t],\n\toptions?: ComposeThemeStoresOptions<Themes>\n): (\n\toverrideDefaultTheme?: keyof Themes | undefined\n) => [keyof Themes | undefined, (theme: keyof Themes) => void] {\n\tconst { defaultTheme } = options ?? {}\n\treturn function useTheme(overrideDefaultTheme?: keyof Themes | undefined) {\n\t\tconst effectiveDefault = overrideDefaultTheme ?? defaultTheme\n\t\tconst channel = getOrCreateChannel<Themes, A, B, C, D, E, F, G, H>(themes, stores, {\n\t\t\tdefaultTheme: effectiveDefault\n\t\t})\n\n\t\tconst theme = useSyncExternalStore<keyof Themes | undefined>(\n\t\t\tchannel.subscribe,\n\t\t\tchannel.getSnapshot,\n\t\t\tchannel.getServerSnapshot\n\t\t)\n\n\t\tconst setTheme = useCallback(\n\t\t\tasync (newTheme: keyof Themes) => {\n\t\t\t\tawait channel.setTheme(newTheme)\n\t\t\t},\n\t\t\t[channel]\n\t\t)\n\n\t\treturn [theme, setTheme]\n\t}\n}\n\n/**\n * Creates a subscription channel bridging a composed theme store to React's useSyncExternalStore.\n * Manages listeners, initial async read from store, and delegates setTheme to store.write.\n *\n * @internal\n */\nfunction createSharedChannel<Themes extends ThemeMap>(\n\tthemes: Themes,\n\tcomposedStore: Required<AsyncThemeStore<Themes>>,\n\tdefaultTheme: keyof Themes | undefined\n) {\n\tlet lastTheme: keyof Themes | undefined = defaultTheme\n\tconst listeners = new Set<(theme: keyof Themes | undefined) => void>()\n\n\tconst notify = (theme: keyof Themes | undefined) => {\n\t\tlastTheme = theme\n\t\tfor (const fn of listeners) {\n\t\t\tfn(theme)\n\t\t}\n\t}\n\n\tconst handleStoreUpdate = (entry: ThemeEntry<Themes> | undefined | null) => {\n\t\tnotify(entry?.theme ?? defaultTheme)\n\t}\n\n\t// Initial read to populate lastTheme (compose store subscribe has no initial notify)\n\tvoid Promise.resolve(composedStore.read()).then(\n\t\t(entry: ThemeEntry<Themes> | undefined | null) => {\n\t\t\tnotify(entry?.theme ?? defaultTheme)\n\t\t}\n\t)\n\n\tlet unobserve: () => void = composedStore.subscribe(handleStoreUpdate)\n\tlet isSubscribedToStore = true\n\n\tconst subscribe = (listener: (theme: keyof Themes | undefined) => void) => {\n\t\tif (!isSubscribedToStore) {\n\t\t\tunobserve = composedStore.subscribe(handleStoreUpdate)\n\t\t\tisSubscribedToStore = true\n\t\t}\n\t\tlisteners.add(listener)\n\t\tlistener(lastTheme)\n\t\treturn () => {\n\t\t\tlisteners.delete(listener)\n\t\t\tif (listeners.size === 0) {\n\t\t\t\tunobserve()\n\t\t\t\tisSubscribedToStore = false\n\t\t\t}\n\t\t}\n\t}\n\n\tconst getSnapshot = (): keyof Themes | undefined => lastTheme\n\tconst getServerSnapshot = (): keyof Themes | undefined => defaultTheme\n\n\treturn {\n\t\tsubscribe,\n\t\tgetSnapshot,\n\t\tgetServerSnapshot,\n\t\tsetTheme: (theme: keyof Themes) => composedStore.write(themeEntry(themes, theme))\n\t}\n}\n\nconst channelsByStores = new WeakMap<\n\tobject,\n\tMap<string | undefined, ReturnType<typeof createSharedChannel<any>>>\n>()\n\n/**\n * Returns a cached shared channel for the given themes, stores, and defaultTheme.\n * Channels are keyed by stores (WeakMap) and defaultTheme to avoid duplicate subscriptions.\n *\n * @internal\n */\nfunction getOrCreateChannel<\n\tThemes extends ThemeMap,\n\tA extends ThemeStoreFactory<Themes> = never,\n\tB extends ThemeStoreFactory<Themes> = never,\n\tC extends ThemeStoreFactory<Themes> = never,\n\tD extends ThemeStoreFactory<Themes> = never,\n\tE extends ThemeStoreFactory<Themes> = never,\n\tF extends ThemeStoreFactory<Themes> = never,\n\tG extends ThemeStoreFactory<Themes> = never,\n\tH extends ThemeStoreFactory<Themes> = never\n>(\n\tthemes: Themes,\n\tstores: readonly [\n\t\tstore1: ComposeThemeStoreEntry<Themes, A>,\n\t\tstore2?: ComposeThemeStoreEntry<Themes, B>,\n\t\tstore3?: ComposeThemeStoreEntry<Themes, C>,\n\t\tstore4?: ComposeThemeStoreEntry<Themes, D>,\n\t\tstore5?: ComposeThemeStoreEntry<Themes, E>,\n\t\tstore6?: ComposeThemeStoreEntry<Themes, F>,\n\t\tstore7?: ComposeThemeStoreEntry<Themes, G>,\n\t\tstore8?: ComposeThemeStoreEntry<Themes, H>\n\t],\n\toptions?: ComposeThemeStoresOptions<Themes> | undefined\n) {\n\tconst { defaultTheme } = options ?? {}\n\tconst storesKey = stores as unknown as object\n\tlet byDefault = channelsByStores.get(storesKey) as Map<\n\t\tkeyof Themes | undefined,\n\t\tReturnType<typeof createSharedChannel<Themes>>\n\t>\n\tif (!byDefault) {\n\t\tbyDefault = new Map<keyof Themes | undefined, ReturnType<typeof createSharedChannel<Themes>>>()\n\t\tchannelsByStores.set(storesKey, byDefault as any)\n\t}\n\tlet channel = byDefault.get(defaultTheme) as ReturnType<typeof createSharedChannel<Themes>>\n\tif (!channel) {\n\t\tconst composedStore = composeThemeStores(themes, stores, { defaultTheme })\n\t\tchannel = createSharedChannel<Themes>(themes, composedStore, defaultTheme)\n\t\tbyDefault.set(defaultTheme, channel)\n\t}\n\treturn channel\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAgCA,SAAgB,gBAWf,QACA,QAUA,SAG8D;CAC9D,MAAM,EAAE,iBAAiB,WAAW,EAAE;AACtC,QAAO,SAAS,SAAS,sBAAiD;EAEzE,MAAM,UAAU,mBAAmD,QAAQ,QAAQ,EAClF,cAFwB,wBAAwB,cAGhD,CAAC;AAeF,SAAO,CAbO,qBACb,QAAQ,WACR,QAAQ,aACR,QAAQ,kBACR,EAEgB,YAChB,OAAO,aAA2B;AACjC,SAAM,QAAQ,SAAS,SAAS;KAEjC,CAAC,QAAQ,CACT,CAEuB;;;;;;;;;AAU1B,SAAS,oBACR,QACA,eACA,cACC;CACD,IAAIA,YAAsC;CAC1C,MAAM,4BAAY,IAAI,KAAgD;CAEtE,MAAM,UAAU,UAAoC;AACnD,cAAY;AACZ,OAAK,MAAM,MAAM,UAChB,IAAG,MAAM;;CAIX,MAAM,qBAAqB,UAAiD;AAC3E,SAAO,OAAO,SAAS,aAAa;;AAIrC,CAAK,QAAQ,QAAQ,cAAc,MAAM,CAAC,CAAC,MACzC,UAAiD;AACjD,SAAO,OAAO,SAAS,aAAa;GAErC;CAED,IAAIC,YAAwB,cAAc,UAAU,kBAAkB;CACtE,IAAI,sBAAsB;CAE1B,MAAM,aAAa,aAAwD;AAC1E,MAAI,CAAC,qBAAqB;AACzB,eAAY,cAAc,UAAU,kBAAkB;AACtD,yBAAsB;;AAEvB,YAAU,IAAI,SAAS;AACvB,WAAS,UAAU;AACnB,eAAa;AACZ,aAAU,OAAO,SAAS;AAC1B,OAAI,UAAU,SAAS,GAAG;AACzB,eAAW;AACX,0BAAsB;;;;CAKzB,MAAM,oBAA8C;CACpD,MAAM,0BAAoD;AAE1D,QAAO;EACN;EACA;EACA;EACA,WAAW,UAAwB,cAAc,MAAM,WAAW,QAAQ,MAAM,CAAC;EACjF;;AAGF,MAAM,mCAAmB,IAAI,SAG1B;;;;;;;AAQH,SAAS,mBAWR,QACA,QAUA,SACC;CACD,MAAM,EAAE,iBAAiB,WAAW,EAAE;CACtC,MAAM,YAAY;CAClB,IAAI,YAAY,iBAAiB,IAAI,UAAU;AAI/C,KAAI,CAAC,WAAW;AACf,8BAAY,IAAI,KAA+E;AAC/F,mBAAiB,IAAI,WAAW,UAAiB;;CAElD,IAAI,UAAU,UAAU,IAAI,aAAa;AACzC,KAAI,CAAC,SAAS;AAEb,YAAU,oBAA4B,QADhB,mBAAmB,QAAQ,QAAQ,EAAE,cAAc,CAAC,EACb,aAAa;AAC1E,YAAU,IAAI,cAAc,QAAQ;;AAErC,QAAO"}
1
+ {"version":3,"file":"create-theme-hook.mjs","names":["lastTheme: keyof Themes | undefined","unobserve: () => void"],"sources":["../../../src/react/theme/create-theme-hook.ts"],"sourcesContent":["import { useCallback, useSyncExternalStore } from 'react'\nimport type { Required } from 'type-plus'\nimport {\n\ttype ComposeThemeStoreEntry,\n\ttype ComposeThemeStoresOptions,\n\tcomposeThemeStores\n} from '../../theme/compose-theme-stores.ts'\nimport { themeEntry } from '../../theme/theme-entry.ts'\nimport type { ThemeEntry } from '../../theme/theme-entry.types.ts'\nimport type { ThemeMap } from '../../theme/theme-map.types.ts'\nimport type { AsyncThemeStore } from '../../theme/theme-store/async-theme-store.types.ts'\nimport type { ThemeStoreFactory } from '../../theme/theme-store/theme-store-factory.types.ts'\n\n/**\n * Creates a React hook for theme selection that reads from and writes to composed theme stores.\n *\n * The returned hook subscribes to store changes via `useSyncExternalStore`, supports SSR\n * (uses `defaultTheme` for server snapshot), and returns a `[theme, setTheme]` tuple.\n * Channels are cached per store configuration and default theme for efficient reuse.\n *\n * @param themes - ThemeMap mapping theme keys to their values (e.g. CSS class names)\n * @param stores - Array of 1–8 theme stores or factory configs (see ComposeThemeStoreEntry)\n * @param options.defaultTheme - Fallback theme key when stores return empty; also used for SSR\n * @returns A `useTheme` hook that returns `[currentTheme, setTheme]` tuple\n *\n * @example\n * ```ts\n * const useTheme = createThemeHook(themes, [localStorageStore], { defaultTheme: 'light' })\n * const [theme, setTheme] = useTheme()\n * setTheme('dark')\n * ```\n */\nexport function createThemeHook<\n\tThemes extends ThemeMap,\n\tA extends ThemeStoreFactory<Themes> = never,\n\tB extends ThemeStoreFactory<Themes> = never,\n\tC extends ThemeStoreFactory<Themes> = never,\n\tD extends ThemeStoreFactory<Themes> = never,\n\tE extends ThemeStoreFactory<Themes> = never,\n\tF extends ThemeStoreFactory<Themes> = never,\n\tG extends ThemeStoreFactory<Themes> = never,\n\tH extends ThemeStoreFactory<Themes> = never\n>(\n\tthemes: Themes,\n\tstores: readonly [\n\t\tstore1: ComposeThemeStoreEntry<Themes, A>,\n\t\tstore2?: ComposeThemeStoreEntry<Themes, B>,\n\t\tstore3?: ComposeThemeStoreEntry<Themes, C>,\n\t\tstore4?: ComposeThemeStoreEntry<Themes, D>,\n\t\tstore5?: ComposeThemeStoreEntry<Themes, E>,\n\t\tstore6?: ComposeThemeStoreEntry<Themes, F>,\n\t\tstore7?: ComposeThemeStoreEntry<Themes, G>,\n\t\tstore8?: ComposeThemeStoreEntry<Themes, H>\n\t],\n\toptions?: ComposeThemeStoresOptions<Themes>\n): (\n\toverrideDefaultTheme?: keyof Themes | undefined\n) => [keyof Themes | undefined, (theme: keyof Themes) => void] {\n\tconst { defaultTheme } = options ?? {}\n\treturn function useTheme(overrideDefaultTheme?: keyof Themes | undefined) {\n\t\tconst effectiveDefault = overrideDefaultTheme ?? defaultTheme\n\t\tconst channel = getOrCreateChannel<Themes, A, B, C, D, E, F, G, H>(themes, stores, {\n\t\t\tdefaultTheme: effectiveDefault\n\t\t})\n\n\t\tconst theme = useSyncExternalStore<keyof Themes | undefined>(\n\t\t\tchannel.subscribe,\n\t\t\tchannel.getSnapshot,\n\t\t\tchannel.getServerSnapshot\n\t\t)\n\n\t\tconst setTheme = useCallback(\n\t\t\tasync (newTheme: keyof Themes) => {\n\t\t\t\tawait channel.setTheme(newTheme)\n\t\t\t},\n\t\t\t[channel]\n\t\t)\n\n\t\treturn [theme, setTheme]\n\t}\n}\n\n/**\n * Creates a subscription channel bridging a composed theme store to React's useSyncExternalStore.\n * Manages listeners, initial async read from store, and delegates setTheme to store.write.\n *\n * @internal\n */\nfunction createSharedChannel<Themes extends ThemeMap>(\n\tthemes: Themes,\n\tcomposedStore: Required<AsyncThemeStore<Themes>>,\n\tdefaultTheme: keyof Themes | undefined\n) {\n\tlet lastTheme: keyof Themes | undefined = defaultTheme\n\tconst listeners = new Set<(theme: keyof Themes | undefined) => void>()\n\n\tconst notify = (theme: keyof Themes | undefined) => {\n\t\tlastTheme = theme\n\t\tfor (const fn of listeners) {\n\t\t\tfn(theme)\n\t\t}\n\t}\n\n\tconst handleStoreUpdate = (entry: ThemeEntry<Themes> | undefined) => {\n\t\tnotify(entry?.theme ?? defaultTheme)\n\t}\n\n\t// Initial read to populate lastTheme (compose store subscribe has no initial notify)\n\tvoid Promise.resolve(composedStore.read()).then((entry: ThemeEntry<Themes> | undefined) => {\n\t\tnotify(entry?.theme ?? defaultTheme)\n\t})\n\n\tlet unobserve: () => void = composedStore.subscribe(handleStoreUpdate)\n\tlet isSubscribedToStore = true\n\n\tconst subscribe = (listener: (theme: keyof Themes | undefined) => void) => {\n\t\tif (!isSubscribedToStore) {\n\t\t\tunobserve = composedStore.subscribe(handleStoreUpdate)\n\t\t\tisSubscribedToStore = true\n\t\t}\n\t\tlisteners.add(listener)\n\t\tlistener(lastTheme)\n\t\treturn () => {\n\t\t\tlisteners.delete(listener)\n\t\t\tif (listeners.size === 0) {\n\t\t\t\tunobserve()\n\t\t\t\tisSubscribedToStore = false\n\t\t\t}\n\t\t}\n\t}\n\n\tconst getSnapshot = (): keyof Themes | undefined => lastTheme\n\tconst getServerSnapshot = (): keyof Themes | undefined => defaultTheme\n\n\treturn {\n\t\tsubscribe,\n\t\tgetSnapshot,\n\t\tgetServerSnapshot,\n\t\tsetTheme: (theme: keyof Themes) => composedStore.write(themeEntry(themes, theme))\n\t}\n}\n\nconst channelsByStores = new WeakMap<\n\tobject,\n\tMap<string | undefined, ReturnType<typeof createSharedChannel<any>>>\n>()\n\n/**\n * Returns a cached shared channel for the given themes, stores, and defaultTheme.\n * Channels are keyed by stores (WeakMap) and defaultTheme to avoid duplicate subscriptions.\n *\n * @internal\n */\nfunction getOrCreateChannel<\n\tThemes extends ThemeMap,\n\tA extends ThemeStoreFactory<Themes> = never,\n\tB extends ThemeStoreFactory<Themes> = never,\n\tC extends ThemeStoreFactory<Themes> = never,\n\tD extends ThemeStoreFactory<Themes> = never,\n\tE extends ThemeStoreFactory<Themes> = never,\n\tF extends ThemeStoreFactory<Themes> = never,\n\tG extends ThemeStoreFactory<Themes> = never,\n\tH extends ThemeStoreFactory<Themes> = never\n>(\n\tthemes: Themes,\n\tstores: readonly [\n\t\tstore1: ComposeThemeStoreEntry<Themes, A>,\n\t\tstore2?: ComposeThemeStoreEntry<Themes, B>,\n\t\tstore3?: ComposeThemeStoreEntry<Themes, C>,\n\t\tstore4?: ComposeThemeStoreEntry<Themes, D>,\n\t\tstore5?: ComposeThemeStoreEntry<Themes, E>,\n\t\tstore6?: ComposeThemeStoreEntry<Themes, F>,\n\t\tstore7?: ComposeThemeStoreEntry<Themes, G>,\n\t\tstore8?: ComposeThemeStoreEntry<Themes, H>\n\t],\n\toptions?: ComposeThemeStoresOptions<Themes> | undefined\n) {\n\tconst { defaultTheme } = options ?? {}\n\tconst storesKey = stores as unknown as object\n\tlet byDefault = channelsByStores.get(storesKey) as Map<\n\t\tkeyof Themes | undefined,\n\t\tReturnType<typeof createSharedChannel<Themes>>\n\t>\n\tif (!byDefault) {\n\t\tbyDefault = new Map<keyof Themes | undefined, ReturnType<typeof createSharedChannel<Themes>>>()\n\t\tchannelsByStores.set(storesKey, byDefault as any)\n\t}\n\tlet channel = byDefault.get(defaultTheme) as ReturnType<typeof createSharedChannel<Themes>>\n\tif (!channel) {\n\t\tconst composedStore = composeThemeStores(themes, stores, { defaultTheme })\n\t\tchannel = createSharedChannel<Themes>(themes, composedStore, defaultTheme)\n\t\tbyDefault.set(defaultTheme, channel)\n\t}\n\treturn channel\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAgCA,SAAgB,gBAWf,QACA,QAUA,SAG8D;CAC9D,MAAM,EAAE,iBAAiB,WAAW,EAAE;AACtC,QAAO,SAAS,SAAS,sBAAiD;EAEzE,MAAM,UAAU,mBAAmD,QAAQ,QAAQ,EAClF,cAFwB,wBAAwB,cAGhD,CAAC;AAeF,SAAO,CAbO,qBACb,QAAQ,WACR,QAAQ,aACR,QAAQ,kBACR,EAEgB,YAChB,OAAO,aAA2B;AACjC,SAAM,QAAQ,SAAS,SAAS;KAEjC,CAAC,QAAQ,CACT,CAEuB;;;;;;;;;AAU1B,SAAS,oBACR,QACA,eACA,cACC;CACD,IAAIA,YAAsC;CAC1C,MAAM,4BAAY,IAAI,KAAgD;CAEtE,MAAM,UAAU,UAAoC;AACnD,cAAY;AACZ,OAAK,MAAM,MAAM,UAChB,IAAG,MAAM;;CAIX,MAAM,qBAAqB,UAA0C;AACpE,SAAO,OAAO,SAAS,aAAa;;AAIrC,CAAK,QAAQ,QAAQ,cAAc,MAAM,CAAC,CAAC,MAAM,UAA0C;AAC1F,SAAO,OAAO,SAAS,aAAa;GACnC;CAEF,IAAIC,YAAwB,cAAc,UAAU,kBAAkB;CACtE,IAAI,sBAAsB;CAE1B,MAAM,aAAa,aAAwD;AAC1E,MAAI,CAAC,qBAAqB;AACzB,eAAY,cAAc,UAAU,kBAAkB;AACtD,yBAAsB;;AAEvB,YAAU,IAAI,SAAS;AACvB,WAAS,UAAU;AACnB,eAAa;AACZ,aAAU,OAAO,SAAS;AAC1B,OAAI,UAAU,SAAS,GAAG;AACzB,eAAW;AACX,0BAAsB;;;;CAKzB,MAAM,oBAA8C;CACpD,MAAM,0BAAoD;AAE1D,QAAO;EACN;EACA;EACA;EACA,WAAW,UAAwB,cAAc,MAAM,WAAW,QAAQ,MAAM,CAAC;EACjF;;AAGF,MAAM,mCAAmB,IAAI,SAG1B;;;;;;;AAQH,SAAS,mBAWR,QACA,QAUA,SACC;CACD,MAAM,EAAE,iBAAiB,WAAW,EAAE;CACtC,MAAM,YAAY;CAClB,IAAI,YAAY,iBAAiB,IAAI,UAAU;AAI/C,KAAI,CAAC,WAAW;AACf,8BAAY,IAAI,KAA+E;AAC/F,mBAAiB,IAAI,WAAW,UAAiB;;CAElD,IAAI,UAAU,UAAU,IAAI,aAAa;AACzC,KAAI,CAAC,SAAS;AAEb,YAAU,oBAA4B,QADhB,mBAAmB,QAAQ,QAAQ,EAAE,cAAc,CAAC,EACb,aAAa;AAC1E,YAAU,IAAI,cAAc,QAAQ;;AAErC,QAAO"}
@@ -52,7 +52,7 @@ function getCanonicalShapeAndComparable(v) {
52
52
  * returns { theme, value: stored.value }. Else returns undefined.
53
53
  *
54
54
  * @param themes - Record of valid theme keys and values (required for validation)
55
- * @param value - Raw string from localStorage/sessionStorage/cookie
55
+ * @param value - Raw string from localStorage/sessionStorage/cookie (accepts null)
56
56
  * @returns ThemeEntry when valid, otherwise undefined
57
57
  */
58
58
  function parseStoredTheme(themes, value) {
@@ -1 +1 @@
1
- {"version":3,"file":"parse-stored-theme.cjs","names":["isReadonlyStringArray","tryParseJSON"],"sources":["../../../src/theme/_utils/parse-stored-theme.ts"],"sourcesContent":["import { tryParseJSON } from '../../_internal/utils/try-parse-json.ts'\nimport type { ThemeEntry } from '../theme-entry.types.ts'\nimport type { ThemeMap, ThemeMapValue } from '../theme-map.types.ts'\nimport { isReadonlyStringArray } from './resolve-theme-map-value.ts'\n\ntype Shape = 'string' | 'array' | 'object-string' | 'object-array'\n\nfunction getShapeAndComparable(\n\tv: unknown\n): { shape: Shape; comparable: string | undefined } | null {\n\tif (typeof v === 'string') return { shape: 'string', comparable: v }\n\tif (Array.isArray(v)) return { shape: 'array', comparable: v[0] }\n\tif (v !== null && typeof v === 'object' && 'themeValue' in v) {\n\t\tconst tv = (v as { themeValue: string | string[] }).themeValue\n\t\tif (typeof tv === 'string') return { shape: 'object-string', comparable: tv }\n\t\tif (Array.isArray(tv)) return { shape: 'object-array', comparable: tv[0] }\n\t}\n\treturn null\n}\n\nfunction getCanonicalShapeAndComparable(v: ThemeMapValue): {\n\tshape: Shape\n\tcomparable: string | undefined\n} {\n\tif (typeof v === 'string') return { shape: 'string', comparable: v }\n\tif (isReadonlyStringArray(v)) return { shape: 'array', comparable: v[0] }\n\tconst tv = v.themeValue\n\tif (typeof tv === 'string') return { shape: 'object-string', comparable: tv }\n\treturn { shape: 'object-array', comparable: tv[0] }\n}\n\n/**\n * Parses stored JSON theme and validates against theme map with strict shape and comparable matching.\n *\n * Expects stored shape: { theme: string, value?: unknown }\n *\n * When shape matches AND comparable value (string or [0]) matches themes[theme]:\n * returns { theme, value: stored.value }. Else returns undefined.\n *\n * @param themes - Record of valid theme keys and values (required for validation)\n * @param value - Raw string from localStorage/sessionStorage/cookie\n * @returns ThemeEntry when valid, otherwise undefined\n */\nexport function parseStoredTheme<Themes extends ThemeMap>(\n\tthemes: Themes | undefined,\n\tvalue: string | undefined\n): ThemeEntry<Themes> | undefined {\n\tconst parsed = tryParseJSON<{ theme: string; value?: unknown }>(value ?? null)\n\tif (!parsed?.theme || typeof parsed.theme !== 'string') return undefined\n\tif (!themes || !(parsed.theme in themes)) return undefined\n\tif (parsed.value === undefined) return undefined\n\n\tconst storedInfo = getShapeAndComparable(parsed.value)\n\tif (!storedInfo) return undefined\n\n\tconst canonical = getCanonicalShapeAndComparable(themes[parsed.theme] as ThemeMapValue)\n\tif (storedInfo.shape !== canonical.shape) return undefined\n\tif (storedInfo.comparable !== canonical.comparable) return undefined\n\n\treturn { theme: parsed.theme as keyof Themes, value: parsed.value as Themes[keyof Themes] }\n}\n"],"mappings":";;;;AAOA,SAAS,sBACR,GAC0D;AAC1D,KAAI,OAAO,MAAM,SAAU,QAAO;EAAE,OAAO;EAAU,YAAY;EAAG;AACpE,KAAI,MAAM,QAAQ,EAAE,CAAE,QAAO;EAAE,OAAO;EAAS,YAAY,EAAE;EAAI;AACjE,KAAI,MAAM,QAAQ,OAAO,MAAM,YAAY,gBAAgB,GAAG;EAC7D,MAAM,KAAM,EAAwC;AACpD,MAAI,OAAO,OAAO,SAAU,QAAO;GAAE,OAAO;GAAiB,YAAY;GAAI;AAC7E,MAAI,MAAM,QAAQ,GAAG,CAAE,QAAO;GAAE,OAAO;GAAgB,YAAY,GAAG;GAAI;;AAE3E,QAAO;;AAGR,SAAS,+BAA+B,GAGtC;AACD,KAAI,OAAO,MAAM,SAAU,QAAO;EAAE,OAAO;EAAU,YAAY;EAAG;AACpE,KAAIA,sDAAsB,EAAE,CAAE,QAAO;EAAE,OAAO;EAAS,YAAY,EAAE;EAAI;CACzE,MAAM,KAAK,EAAE;AACb,KAAI,OAAO,OAAO,SAAU,QAAO;EAAE,OAAO;EAAiB,YAAY;EAAI;AAC7E,QAAO;EAAE,OAAO;EAAgB,YAAY,GAAG;EAAI;;;;;;;;;;;;;;AAepD,SAAgB,iBACf,QACA,OACiC;CACjC,MAAM,SAASC,oCAAiD,SAAS,KAAK;AAC9E,KAAI,CAAC,QAAQ,SAAS,OAAO,OAAO,UAAU,SAAU,QAAO;AAC/D,KAAI,CAAC,UAAU,EAAE,OAAO,SAAS,QAAS,QAAO;AACjD,KAAI,OAAO,UAAU,OAAW,QAAO;CAEvC,MAAM,aAAa,sBAAsB,OAAO,MAAM;AACtD,KAAI,CAAC,WAAY,QAAO;CAExB,MAAM,YAAY,+BAA+B,OAAO,OAAO,OAAwB;AACvF,KAAI,WAAW,UAAU,UAAU,MAAO,QAAO;AACjD,KAAI,WAAW,eAAe,UAAU,WAAY,QAAO;AAE3D,QAAO;EAAE,OAAO,OAAO;EAAuB,OAAO,OAAO;EAA+B"}
1
+ {"version":3,"file":"parse-stored-theme.cjs","names":["isReadonlyStringArray","tryParseJSON"],"sources":["../../../src/theme/_utils/parse-stored-theme.ts"],"sourcesContent":["import { tryParseJSON } from '../../_internal/utils/try-parse-json.ts'\nimport type { ThemeEntry } from '../theme-entry.types.ts'\nimport type { ThemeMap, ThemeMapValue } from '../theme-map.types.ts'\nimport { isReadonlyStringArray } from './resolve-theme-map-value.ts'\n\ntype Shape = 'string' | 'array' | 'object-string' | 'object-array'\n\nfunction getShapeAndComparable(\n\tv: unknown\n): { shape: Shape; comparable: string | undefined } | null {\n\tif (typeof v === 'string') return { shape: 'string', comparable: v }\n\tif (Array.isArray(v)) return { shape: 'array', comparable: v[0] }\n\tif (v !== null && typeof v === 'object' && 'themeValue' in v) {\n\t\tconst tv = (v as { themeValue: string | string[] }).themeValue\n\t\tif (typeof tv === 'string') return { shape: 'object-string', comparable: tv }\n\t\tif (Array.isArray(tv)) return { shape: 'object-array', comparable: tv[0] }\n\t}\n\treturn null\n}\n\nfunction getCanonicalShapeAndComparable(v: ThemeMapValue): {\n\tshape: Shape\n\tcomparable: string | undefined\n} {\n\tif (typeof v === 'string') return { shape: 'string', comparable: v }\n\tif (isReadonlyStringArray(v)) return { shape: 'array', comparable: v[0] }\n\tconst tv = v.themeValue\n\tif (typeof tv === 'string') return { shape: 'object-string', comparable: tv }\n\treturn { shape: 'object-array', comparable: tv[0] }\n}\n\n/**\n * Parses stored JSON theme and validates against theme map with strict shape and comparable matching.\n *\n * Expects stored shape: { theme: string, value?: unknown }\n *\n * When shape matches AND comparable value (string or [0]) matches themes[theme]:\n * returns { theme, value: stored.value }. Else returns undefined.\n *\n * @param themes - Record of valid theme keys and values (required for validation)\n * @param value - Raw string from localStorage/sessionStorage/cookie (accepts null)\n * @returns ThemeEntry when valid, otherwise undefined\n */\nexport function parseStoredTheme<Themes extends ThemeMap>(\n\tthemes: Themes | undefined,\n\tvalue: string | null | undefined\n): ThemeEntry<Themes> | undefined {\n\tconst parsed = tryParseJSON<{ theme: string; value?: unknown }>(value ?? null)\n\tif (!parsed?.theme || typeof parsed.theme !== 'string') return undefined\n\tif (!themes || !(parsed.theme in themes)) return undefined\n\tif (parsed.value === undefined) return undefined\n\n\tconst storedInfo = getShapeAndComparable(parsed.value)\n\tif (!storedInfo) return undefined\n\n\tconst canonical = getCanonicalShapeAndComparable(themes[parsed.theme] as ThemeMapValue)\n\tif (storedInfo.shape !== canonical.shape) return undefined\n\tif (storedInfo.comparable !== canonical.comparable) return undefined\n\n\treturn { theme: parsed.theme as keyof Themes, value: parsed.value as Themes[keyof Themes] }\n}\n"],"mappings":";;;;AAOA,SAAS,sBACR,GAC0D;AAC1D,KAAI,OAAO,MAAM,SAAU,QAAO;EAAE,OAAO;EAAU,YAAY;EAAG;AACpE,KAAI,MAAM,QAAQ,EAAE,CAAE,QAAO;EAAE,OAAO;EAAS,YAAY,EAAE;EAAI;AACjE,KAAI,MAAM,QAAQ,OAAO,MAAM,YAAY,gBAAgB,GAAG;EAC7D,MAAM,KAAM,EAAwC;AACpD,MAAI,OAAO,OAAO,SAAU,QAAO;GAAE,OAAO;GAAiB,YAAY;GAAI;AAC7E,MAAI,MAAM,QAAQ,GAAG,CAAE,QAAO;GAAE,OAAO;GAAgB,YAAY,GAAG;GAAI;;AAE3E,QAAO;;AAGR,SAAS,+BAA+B,GAGtC;AACD,KAAI,OAAO,MAAM,SAAU,QAAO;EAAE,OAAO;EAAU,YAAY;EAAG;AACpE,KAAIA,sDAAsB,EAAE,CAAE,QAAO;EAAE,OAAO;EAAS,YAAY,EAAE;EAAI;CACzE,MAAM,KAAK,EAAE;AACb,KAAI,OAAO,OAAO,SAAU,QAAO;EAAE,OAAO;EAAiB,YAAY;EAAI;AAC7E,QAAO;EAAE,OAAO;EAAgB,YAAY,GAAG;EAAI;;;;;;;;;;;;;;AAepD,SAAgB,iBACf,QACA,OACiC;CACjC,MAAM,SAASC,oCAAiD,SAAS,KAAK;AAC9E,KAAI,CAAC,QAAQ,SAAS,OAAO,OAAO,UAAU,SAAU,QAAO;AAC/D,KAAI,CAAC,UAAU,EAAE,OAAO,SAAS,QAAS,QAAO;AACjD,KAAI,OAAO,UAAU,OAAW,QAAO;CAEvC,MAAM,aAAa,sBAAsB,OAAO,MAAM;AACtD,KAAI,CAAC,WAAY,QAAO;CAExB,MAAM,YAAY,+BAA+B,OAAO,OAAO,OAAwB;AACvF,KAAI,WAAW,UAAU,UAAU,MAAO,QAAO;AACjD,KAAI,WAAW,eAAe,UAAU,WAAY,QAAO;AAE3D,QAAO;EAAE,OAAO,OAAO;EAAuB,OAAO,OAAO;EAA+B"}
@@ -52,7 +52,7 @@ function getCanonicalShapeAndComparable(v) {
52
52
  * returns { theme, value: stored.value }. Else returns undefined.
53
53
  *
54
54
  * @param themes - Record of valid theme keys and values (required for validation)
55
- * @param value - Raw string from localStorage/sessionStorage/cookie
55
+ * @param value - Raw string from localStorage/sessionStorage/cookie (accepts null)
56
56
  * @returns ThemeEntry when valid, otherwise undefined
57
57
  */
58
58
  function parseStoredTheme(themes, value) {
@@ -1 +1 @@
1
- {"version":3,"file":"parse-stored-theme.mjs","names":[],"sources":["../../../src/theme/_utils/parse-stored-theme.ts"],"sourcesContent":["import { tryParseJSON } from '../../_internal/utils/try-parse-json.ts'\nimport type { ThemeEntry } from '../theme-entry.types.ts'\nimport type { ThemeMap, ThemeMapValue } from '../theme-map.types.ts'\nimport { isReadonlyStringArray } from './resolve-theme-map-value.ts'\n\ntype Shape = 'string' | 'array' | 'object-string' | 'object-array'\n\nfunction getShapeAndComparable(\n\tv: unknown\n): { shape: Shape; comparable: string | undefined } | null {\n\tif (typeof v === 'string') return { shape: 'string', comparable: v }\n\tif (Array.isArray(v)) return { shape: 'array', comparable: v[0] }\n\tif (v !== null && typeof v === 'object' && 'themeValue' in v) {\n\t\tconst tv = (v as { themeValue: string | string[] }).themeValue\n\t\tif (typeof tv === 'string') return { shape: 'object-string', comparable: tv }\n\t\tif (Array.isArray(tv)) return { shape: 'object-array', comparable: tv[0] }\n\t}\n\treturn null\n}\n\nfunction getCanonicalShapeAndComparable(v: ThemeMapValue): {\n\tshape: Shape\n\tcomparable: string | undefined\n} {\n\tif (typeof v === 'string') return { shape: 'string', comparable: v }\n\tif (isReadonlyStringArray(v)) return { shape: 'array', comparable: v[0] }\n\tconst tv = v.themeValue\n\tif (typeof tv === 'string') return { shape: 'object-string', comparable: tv }\n\treturn { shape: 'object-array', comparable: tv[0] }\n}\n\n/**\n * Parses stored JSON theme and validates against theme map with strict shape and comparable matching.\n *\n * Expects stored shape: { theme: string, value?: unknown }\n *\n * When shape matches AND comparable value (string or [0]) matches themes[theme]:\n * returns { theme, value: stored.value }. Else returns undefined.\n *\n * @param themes - Record of valid theme keys and values (required for validation)\n * @param value - Raw string from localStorage/sessionStorage/cookie\n * @returns ThemeEntry when valid, otherwise undefined\n */\nexport function parseStoredTheme<Themes extends ThemeMap>(\n\tthemes: Themes | undefined,\n\tvalue: string | undefined\n): ThemeEntry<Themes> | undefined {\n\tconst parsed = tryParseJSON<{ theme: string; value?: unknown }>(value ?? null)\n\tif (!parsed?.theme || typeof parsed.theme !== 'string') return undefined\n\tif (!themes || !(parsed.theme in themes)) return undefined\n\tif (parsed.value === undefined) return undefined\n\n\tconst storedInfo = getShapeAndComparable(parsed.value)\n\tif (!storedInfo) return undefined\n\n\tconst canonical = getCanonicalShapeAndComparable(themes[parsed.theme] as ThemeMapValue)\n\tif (storedInfo.shape !== canonical.shape) return undefined\n\tif (storedInfo.comparable !== canonical.comparable) return undefined\n\n\treturn { theme: parsed.theme as keyof Themes, value: parsed.value as Themes[keyof Themes] }\n}\n"],"mappings":";;;;AAOA,SAAS,sBACR,GAC0D;AAC1D,KAAI,OAAO,MAAM,SAAU,QAAO;EAAE,OAAO;EAAU,YAAY;EAAG;AACpE,KAAI,MAAM,QAAQ,EAAE,CAAE,QAAO;EAAE,OAAO;EAAS,YAAY,EAAE;EAAI;AACjE,KAAI,MAAM,QAAQ,OAAO,MAAM,YAAY,gBAAgB,GAAG;EAC7D,MAAM,KAAM,EAAwC;AACpD,MAAI,OAAO,OAAO,SAAU,QAAO;GAAE,OAAO;GAAiB,YAAY;GAAI;AAC7E,MAAI,MAAM,QAAQ,GAAG,CAAE,QAAO;GAAE,OAAO;GAAgB,YAAY,GAAG;GAAI;;AAE3E,QAAO;;AAGR,SAAS,+BAA+B,GAGtC;AACD,KAAI,OAAO,MAAM,SAAU,QAAO;EAAE,OAAO;EAAU,YAAY;EAAG;AACpE,KAAI,sBAAsB,EAAE,CAAE,QAAO;EAAE,OAAO;EAAS,YAAY,EAAE;EAAI;CACzE,MAAM,KAAK,EAAE;AACb,KAAI,OAAO,OAAO,SAAU,QAAO;EAAE,OAAO;EAAiB,YAAY;EAAI;AAC7E,QAAO;EAAE,OAAO;EAAgB,YAAY,GAAG;EAAI;;;;;;;;;;;;;;AAepD,SAAgB,iBACf,QACA,OACiC;CACjC,MAAM,SAAS,aAAiD,SAAS,KAAK;AAC9E,KAAI,CAAC,QAAQ,SAAS,OAAO,OAAO,UAAU,SAAU,QAAO;AAC/D,KAAI,CAAC,UAAU,EAAE,OAAO,SAAS,QAAS,QAAO;AACjD,KAAI,OAAO,UAAU,OAAW,QAAO;CAEvC,MAAM,aAAa,sBAAsB,OAAO,MAAM;AACtD,KAAI,CAAC,WAAY,QAAO;CAExB,MAAM,YAAY,+BAA+B,OAAO,OAAO,OAAwB;AACvF,KAAI,WAAW,UAAU,UAAU,MAAO,QAAO;AACjD,KAAI,WAAW,eAAe,UAAU,WAAY,QAAO;AAE3D,QAAO;EAAE,OAAO,OAAO;EAAuB,OAAO,OAAO;EAA+B"}
1
+ {"version":3,"file":"parse-stored-theme.mjs","names":[],"sources":["../../../src/theme/_utils/parse-stored-theme.ts"],"sourcesContent":["import { tryParseJSON } from '../../_internal/utils/try-parse-json.ts'\nimport type { ThemeEntry } from '../theme-entry.types.ts'\nimport type { ThemeMap, ThemeMapValue } from '../theme-map.types.ts'\nimport { isReadonlyStringArray } from './resolve-theme-map-value.ts'\n\ntype Shape = 'string' | 'array' | 'object-string' | 'object-array'\n\nfunction getShapeAndComparable(\n\tv: unknown\n): { shape: Shape; comparable: string | undefined } | null {\n\tif (typeof v === 'string') return { shape: 'string', comparable: v }\n\tif (Array.isArray(v)) return { shape: 'array', comparable: v[0] }\n\tif (v !== null && typeof v === 'object' && 'themeValue' in v) {\n\t\tconst tv = (v as { themeValue: string | string[] }).themeValue\n\t\tif (typeof tv === 'string') return { shape: 'object-string', comparable: tv }\n\t\tif (Array.isArray(tv)) return { shape: 'object-array', comparable: tv[0] }\n\t}\n\treturn null\n}\n\nfunction getCanonicalShapeAndComparable(v: ThemeMapValue): {\n\tshape: Shape\n\tcomparable: string | undefined\n} {\n\tif (typeof v === 'string') return { shape: 'string', comparable: v }\n\tif (isReadonlyStringArray(v)) return { shape: 'array', comparable: v[0] }\n\tconst tv = v.themeValue\n\tif (typeof tv === 'string') return { shape: 'object-string', comparable: tv }\n\treturn { shape: 'object-array', comparable: tv[0] }\n}\n\n/**\n * Parses stored JSON theme and validates against theme map with strict shape and comparable matching.\n *\n * Expects stored shape: { theme: string, value?: unknown }\n *\n * When shape matches AND comparable value (string or [0]) matches themes[theme]:\n * returns { theme, value: stored.value }. Else returns undefined.\n *\n * @param themes - Record of valid theme keys and values (required for validation)\n * @param value - Raw string from localStorage/sessionStorage/cookie (accepts null)\n * @returns ThemeEntry when valid, otherwise undefined\n */\nexport function parseStoredTheme<Themes extends ThemeMap>(\n\tthemes: Themes | undefined,\n\tvalue: string | null | undefined\n): ThemeEntry<Themes> | undefined {\n\tconst parsed = tryParseJSON<{ theme: string; value?: unknown }>(value ?? null)\n\tif (!parsed?.theme || typeof parsed.theme !== 'string') return undefined\n\tif (!themes || !(parsed.theme in themes)) return undefined\n\tif (parsed.value === undefined) return undefined\n\n\tconst storedInfo = getShapeAndComparable(parsed.value)\n\tif (!storedInfo) return undefined\n\n\tconst canonical = getCanonicalShapeAndComparable(themes[parsed.theme] as ThemeMapValue)\n\tif (storedInfo.shape !== canonical.shape) return undefined\n\tif (storedInfo.comparable !== canonical.comparable) return undefined\n\n\treturn { theme: parsed.theme as keyof Themes, value: parsed.value as Themes[keyof Themes] }\n}\n"],"mappings":";;;;AAOA,SAAS,sBACR,GAC0D;AAC1D,KAAI,OAAO,MAAM,SAAU,QAAO;EAAE,OAAO;EAAU,YAAY;EAAG;AACpE,KAAI,MAAM,QAAQ,EAAE,CAAE,QAAO;EAAE,OAAO;EAAS,YAAY,EAAE;EAAI;AACjE,KAAI,MAAM,QAAQ,OAAO,MAAM,YAAY,gBAAgB,GAAG;EAC7D,MAAM,KAAM,EAAwC;AACpD,MAAI,OAAO,OAAO,SAAU,QAAO;GAAE,OAAO;GAAiB,YAAY;GAAI;AAC7E,MAAI,MAAM,QAAQ,GAAG,CAAE,QAAO;GAAE,OAAO;GAAgB,YAAY,GAAG;GAAI;;AAE3E,QAAO;;AAGR,SAAS,+BAA+B,GAGtC;AACD,KAAI,OAAO,MAAM,SAAU,QAAO;EAAE,OAAO;EAAU,YAAY;EAAG;AACpE,KAAI,sBAAsB,EAAE,CAAE,QAAO;EAAE,OAAO;EAAS,YAAY,EAAE;EAAI;CACzE,MAAM,KAAK,EAAE;AACb,KAAI,OAAO,OAAO,SAAU,QAAO;EAAE,OAAO;EAAiB,YAAY;EAAI;AAC7E,QAAO;EAAE,OAAO;EAAgB,YAAY,GAAG;EAAI;;;;;;;;;;;;;;AAepD,SAAgB,iBACf,QACA,OACiC;CACjC,MAAM,SAAS,aAAiD,SAAS,KAAK;AAC9E,KAAI,CAAC,QAAQ,SAAS,OAAO,OAAO,UAAU,SAAU,QAAO;AAC/D,KAAI,CAAC,UAAU,EAAE,OAAO,SAAS,QAAS,QAAO;AACjD,KAAI,OAAO,UAAU,OAAW,QAAO;CAEvC,MAAM,aAAa,sBAAsB,OAAO,MAAM;AACtD,KAAI,CAAC,WAAY,QAAO;CAExB,MAAM,YAAY,+BAA+B,OAAO,OAAO,OAAwB;AACvF,KAAI,WAAW,UAAU,UAAU,MAAO,QAAO;AACjD,KAAI,WAAW,eAAe,UAAU,WAAY,QAAO;AAE3D,QAAO;EAAE,OAAO,OAAO;EAAuB,OAAO,OAAO;EAA+B"}
@@ -4,7 +4,7 @@
4
4
  * Writes theme entry to all stores that have a write method.
5
5
  *
6
6
  * @param stores - Array of theme stores
7
- * @param entry - Theme entry to write, or undefined to clear
7
+ * @param entry - Theme entry to write, or null/undefined to clear
8
8
  */
9
9
  async function setThemeToStores(stores, entry) {
10
10
  const withWrite = stores.filter((s) => typeof s.write === "function");
@@ -1 +1 @@
1
- {"version":3,"file":"set-theme-to-stores.cjs","names":[],"sources":["../../../src/theme/_utils/set-theme-to-stores.ts"],"sourcesContent":["import type { ThemeEntry } from '../theme-entry.types.ts'\nimport type { ThemeMap } from '../theme-map.types.ts'\nimport type { AsyncThemeStore } from '../theme-store/async-theme-store.types.ts'\nimport type { ThemeStore } from '../theme-store/theme-store.types.ts'\n\ntype StoreWithWrite<Themes extends ThemeMap> = (ThemeStore<Themes> | AsyncThemeStore<Themes>) & {\n\twrite: (entry: ThemeEntry<Themes> | undefined) => void | Promise<void>\n}\n\n/**\n * Writes theme entry to all stores that have a write method.\n *\n * @param stores - Array of theme stores\n * @param entry - Theme entry to write, or undefined to clear\n */\nexport async function setThemeToStores<Themes extends ThemeMap>(\n\tstores: (ThemeStore<Themes> | AsyncThemeStore<Themes>)[],\n\tentry: ThemeEntry<Themes> | undefined\n): Promise<void> {\n\tconst withWrite = stores.filter((s): s is StoreWithWrite<Themes> => typeof s.write === 'function')\n\n\tawait Promise.all(withWrite.map((store) => Promise.resolve(store.write!(entry))))\n}\n"],"mappings":";;;;;;;;AAeA,eAAsB,iBACrB,QACA,OACgB;CAChB,MAAM,YAAY,OAAO,QAAQ,MAAmC,OAAO,EAAE,UAAU,WAAW;AAElG,OAAM,QAAQ,IAAI,UAAU,KAAK,UAAU,QAAQ,QAAQ,MAAM,MAAO,MAAM,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"set-theme-to-stores.cjs","names":[],"sources":["../../../src/theme/_utils/set-theme-to-stores.ts"],"sourcesContent":["import type { ThemeEntry } from '../theme-entry.types.ts'\nimport type { ThemeMap } from '../theme-map.types.ts'\nimport type { AsyncThemeStore } from '../theme-store/async-theme-store.types.ts'\nimport type { ThemeStore } from '../theme-store/theme-store.types.ts'\n\ntype StoreWithWrite<Themes extends ThemeMap> = (ThemeStore<Themes> | AsyncThemeStore<Themes>) & {\n\twrite: (entry: ThemeEntry<Themes> | null | undefined) => void | Promise<void>\n}\n\n/**\n * Writes theme entry to all stores that have a write method.\n *\n * @param stores - Array of theme stores\n * @param entry - Theme entry to write, or null/undefined to clear\n */\nexport async function setThemeToStores<Themes extends ThemeMap>(\n\tstores: (ThemeStore<Themes> | AsyncThemeStore<Themes>)[],\n\tentry: ThemeEntry<Themes> | null | undefined\n): Promise<void> {\n\tconst withWrite = stores.filter((s): s is StoreWithWrite<Themes> => typeof s.write === 'function')\n\n\tawait Promise.all(withWrite.map((store) => Promise.resolve(store.write!(entry))))\n}\n"],"mappings":";;;;;;;;AAeA,eAAsB,iBACrB,QACA,OACgB;CAChB,MAAM,YAAY,OAAO,QAAQ,MAAmC,OAAO,EAAE,UAAU,WAAW;AAElG,OAAM,QAAQ,IAAI,UAAU,KAAK,UAAU,QAAQ,QAAQ,MAAM,MAAO,MAAM,CAAC,CAAC,CAAC"}
@@ -3,7 +3,7 @@
3
3
  * Writes theme entry to all stores that have a write method.
4
4
  *
5
5
  * @param stores - Array of theme stores
6
- * @param entry - Theme entry to write, or undefined to clear
6
+ * @param entry - Theme entry to write, or null/undefined to clear
7
7
  */
8
8
  async function setThemeToStores(stores, entry) {
9
9
  const withWrite = stores.filter((s) => typeof s.write === "function");
@@ -1 +1 @@
1
- {"version":3,"file":"set-theme-to-stores.mjs","names":[],"sources":["../../../src/theme/_utils/set-theme-to-stores.ts"],"sourcesContent":["import type { ThemeEntry } from '../theme-entry.types.ts'\nimport type { ThemeMap } from '../theme-map.types.ts'\nimport type { AsyncThemeStore } from '../theme-store/async-theme-store.types.ts'\nimport type { ThemeStore } from '../theme-store/theme-store.types.ts'\n\ntype StoreWithWrite<Themes extends ThemeMap> = (ThemeStore<Themes> | AsyncThemeStore<Themes>) & {\n\twrite: (entry: ThemeEntry<Themes> | undefined) => void | Promise<void>\n}\n\n/**\n * Writes theme entry to all stores that have a write method.\n *\n * @param stores - Array of theme stores\n * @param entry - Theme entry to write, or undefined to clear\n */\nexport async function setThemeToStores<Themes extends ThemeMap>(\n\tstores: (ThemeStore<Themes> | AsyncThemeStore<Themes>)[],\n\tentry: ThemeEntry<Themes> | undefined\n): Promise<void> {\n\tconst withWrite = stores.filter((s): s is StoreWithWrite<Themes> => typeof s.write === 'function')\n\n\tawait Promise.all(withWrite.map((store) => Promise.resolve(store.write!(entry))))\n}\n"],"mappings":";;;;;;;AAeA,eAAsB,iBACrB,QACA,OACgB;CAChB,MAAM,YAAY,OAAO,QAAQ,MAAmC,OAAO,EAAE,UAAU,WAAW;AAElG,OAAM,QAAQ,IAAI,UAAU,KAAK,UAAU,QAAQ,QAAQ,MAAM,MAAO,MAAM,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"set-theme-to-stores.mjs","names":[],"sources":["../../../src/theme/_utils/set-theme-to-stores.ts"],"sourcesContent":["import type { ThemeEntry } from '../theme-entry.types.ts'\nimport type { ThemeMap } from '../theme-map.types.ts'\nimport type { AsyncThemeStore } from '../theme-store/async-theme-store.types.ts'\nimport type { ThemeStore } from '../theme-store/theme-store.types.ts'\n\ntype StoreWithWrite<Themes extends ThemeMap> = (ThemeStore<Themes> | AsyncThemeStore<Themes>) & {\n\twrite: (entry: ThemeEntry<Themes> | null | undefined) => void | Promise<void>\n}\n\n/**\n * Writes theme entry to all stores that have a write method.\n *\n * @param stores - Array of theme stores\n * @param entry - Theme entry to write, or null/undefined to clear\n */\nexport async function setThemeToStores<Themes extends ThemeMap>(\n\tstores: (ThemeStore<Themes> | AsyncThemeStore<Themes>)[],\n\tentry: ThemeEntry<Themes> | null | undefined\n): Promise<void> {\n\tconst withWrite = stores.filter((s): s is StoreWithWrite<Themes> => typeof s.write === 'function')\n\n\tawait Promise.all(withWrite.map((store) => Promise.resolve(store.write!(entry))))\n}\n"],"mappings":";;;;;;;AAeA,eAAsB,iBACrB,QACA,OACgB;CAChB,MAAM,YAAY,OAAO,QAAQ,MAAmC,OAAO,EAAE,UAAU,WAAW;AAElG,OAAM,QAAQ,IAAI,UAAU,KAAK,UAAU,QAAQ,QAAQ,MAAM,MAAO,MAAM,CAAC,CAAC,CAAC"}
@@ -12,7 +12,7 @@ let type_plus = require("type-plus");
12
12
  * Arrays in theme map use first value for matching.
13
13
  *
14
14
  * @param themes - Record mapping theme keys to class name(s)
15
- * @param className - Raw class attribute value (e.g. from element.className)
15
+ * @param className - Raw class attribute value (e.g. from element.className; accepts null)
16
16
  * @returns ThemeEntry if a match is found, otherwise undefined
17
17
  */
18
18
  function parseClassName(themes, className) {
@@ -1 +1 @@
1
- {"version":3,"file":"parse-class-name.cjs","names":["resolveThemeMapValue","themeEntry"],"sources":["../../../src/theme/class-name/parse-class-name.ts"],"sourcesContent":["import { findKey } from 'type-plus'\nimport { resolveThemeMapValue } from '../_utils/resolve-theme-map-value.ts'\nimport { themeEntry } from '../theme-entry.ts'\nimport type { ThemeEntry } from '../theme-entry.types.ts'\nimport type { ThemeMap } from '../theme-map.types.ts'\n\n/**\n * Parses a class name string into a ThemeEntry.\n *\n * Pure function: no DOM access. Matches class strings by checking if any theme\n * class is included in the className (e.g. `className.includes(themeClass)`).\n * Arrays in theme map use first value for matching.\n *\n * @param themes - Record mapping theme keys to class name(s)\n * @param className - Raw class attribute value (e.g. from element.className)\n * @returns ThemeEntry if a match is found, otherwise undefined\n */\nexport function parseClassName<Themes extends ThemeMap>(\n\tthemes: Themes,\n\tclassName: string | undefined\n): ThemeEntry<Themes> | undefined {\n\tconst cls = className ?? ''\n\tconst theme = findKey(themes, (key) => {\n\t\tconst value = themes[key]\n\t\tif (value === undefined) return false\n\t\tconst resolved = resolveThemeMapValue(value)\n\t\tconst v = Array.isArray(resolved) ? resolved[0] : resolved\n\t\treturn !!v && cls.includes(v)\n\t})\n\treturn theme !== undefined ? themeEntry(themes, theme) : undefined\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAiBA,SAAgB,eACf,QACA,WACiC;CACjC,MAAM,MAAM,aAAa;CACzB,MAAM,+BAAgB,SAAS,QAAQ;EACtC,MAAM,QAAQ,OAAO;AACrB,MAAI,UAAU,OAAW,QAAO;EAChC,MAAM,WAAWA,qDAAqB,MAAM;EAC5C,MAAM,IAAI,MAAM,QAAQ,SAAS,GAAG,SAAS,KAAK;AAClD,SAAO,CAAC,CAAC,KAAK,IAAI,SAAS,EAAE;GAC5B;AACF,QAAO,UAAU,SAAYC,+BAAW,QAAQ,MAAM,GAAG"}
1
+ {"version":3,"file":"parse-class-name.cjs","names":["resolveThemeMapValue","themeEntry"],"sources":["../../../src/theme/class-name/parse-class-name.ts"],"sourcesContent":["import { findKey } from 'type-plus'\nimport { resolveThemeMapValue } from '../_utils/resolve-theme-map-value.ts'\nimport { themeEntry } from '../theme-entry.ts'\nimport type { ThemeEntry } from '../theme-entry.types.ts'\nimport type { ThemeMap } from '../theme-map.types.ts'\n\n/**\n * Parses a class name string into a ThemeEntry.\n *\n * Pure function: no DOM access. Matches class strings by checking if any theme\n * class is included in the className (e.g. `className.includes(themeClass)`).\n * Arrays in theme map use first value for matching.\n *\n * @param themes - Record mapping theme keys to class name(s)\n * @param className - Raw class attribute value (e.g. from element.className; accepts null)\n * @returns ThemeEntry if a match is found, otherwise undefined\n */\nexport function parseClassName<Themes extends ThemeMap>(\n\tthemes: Themes,\n\tclassName: string | null | undefined\n): ThemeEntry<Themes> | undefined {\n\tconst cls = className ?? ''\n\tconst theme = findKey(themes, (key) => {\n\t\tconst value = themes[key]\n\t\tif (value === undefined) return false\n\t\tconst resolved = resolveThemeMapValue(value)\n\t\tconst v = Array.isArray(resolved) ? resolved[0] : resolved\n\t\treturn !!v && cls.includes(v)\n\t})\n\treturn theme !== undefined ? themeEntry(themes, theme) : undefined\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAiBA,SAAgB,eACf,QACA,WACiC;CACjC,MAAM,MAAM,aAAa;CACzB,MAAM,+BAAgB,SAAS,QAAQ;EACtC,MAAM,QAAQ,OAAO;AACrB,MAAI,UAAU,OAAW,QAAO;EAChC,MAAM,WAAWA,qDAAqB,MAAM;EAC5C,MAAM,IAAI,MAAM,QAAQ,SAAS,GAAG,SAAS,KAAK;AAClD,SAAO,CAAC,CAAC,KAAK,IAAI,SAAS,EAAE;GAC5B;AACF,QAAO,UAAU,SAAYC,+BAAW,QAAQ,MAAM,GAAG"}