@vuer-ai/vuer-uikit 0.0.96 → 0.0.98

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 (341) hide show
  1. package/README.md +106 -7
  2. package/cli/dial-cli.js +49 -7
  3. package/dist/SyncScroll/SyncScroll.cjs +10 -10
  4. package/dist/SyncScroll/SyncScroll.mjs +3 -3
  5. package/dist/SyncScroll/index.cjs +10 -10
  6. package/dist/SyncScroll/index.mjs +3 -3
  7. package/dist/chunk-3HEZVWRW.mjs +62 -0
  8. package/dist/chunk-4KWGGESI.cjs +494 -0
  9. package/dist/{chunk-CCMKL2OA.cjs → chunk-7GWDO25E.cjs} +2 -2
  10. package/dist/chunk-A5LCX2UQ.cjs +208 -0
  11. package/dist/chunk-BEJIZ56L.mjs +300 -0
  12. package/dist/chunk-C7VGRU3O.mjs +283 -0
  13. package/dist/{chunk-LONOMMFA.cjs → chunk-LJMNHTTG.cjs} +21 -21
  14. package/dist/chunk-O66RESRR.cjs +285 -0
  15. package/dist/{chunk-RINTUFYQ.cjs → chunk-RMK6W774.cjs} +24 -19
  16. package/dist/{chunk-BFQ2WL5U.mjs → chunk-TTYSYGVE.mjs} +2 -2
  17. package/dist/chunk-VA3PEYFM.mjs +489 -0
  18. package/dist/chunk-VBBJSIY7.cjs +308 -0
  19. package/dist/{chunk-AIINOWEH.mjs → chunk-W4JCKCW7.mjs} +5 -5
  20. package/dist/chunk-WWGF6TBZ.mjs +206 -0
  21. package/dist/chunk-ZGN4UEJR.cjs +679 -0
  22. package/dist/chunk-ZQLRMOUW.mjs +661 -0
  23. package/dist/dial/DialPanel.cjs +24 -24
  24. package/dist/dial/DialPanel.mjs +23 -23
  25. package/dist/dial/DialProvider.cjs +3 -3
  26. package/dist/dial/DialProvider.d.cts +1 -0
  27. package/dist/dial/DialProvider.d.ts +1 -0
  28. package/dist/dial/DialProvider.example.cjs +72 -0
  29. package/dist/dial/DialProvider.example.d.cts +7 -0
  30. package/dist/dial/DialProvider.example.d.ts +7 -0
  31. package/dist/dial/DialProvider.example.mjs +68 -0
  32. package/dist/dial/DialProvider.mjs +1 -1
  33. package/dist/dial/index.cjs +42 -42
  34. package/dist/dial/index.mjs +23 -23
  35. package/dist/dial/wrapped-inputs/ControlledInputs.cjs +27 -27
  36. package/dist/dial/wrapped-inputs/ControlledInputs.mjs +23 -23
  37. package/dist/dial/wrapped-inputs/DialInputs.cjs +34 -34
  38. package/dist/dial/wrapped-inputs/DialInputs.mjs +23 -23
  39. package/dist/dial/wrapped-inputs/DialVectorInput.cjs +24 -24
  40. package/dist/dial/wrapped-inputs/DialVectorInput.mjs +23 -23
  41. package/dist/dial/wrapped-inputs/index.cjs +39 -39
  42. package/dist/dial/wrapped-inputs/index.mjs +23 -23
  43. package/dist/highlight-cursor/cursor-provider.cjs +3 -3
  44. package/dist/highlight-cursor/cursor-provider.mjs +2 -2
  45. package/dist/highlight-cursor/enhanced-components.cjs +10 -10
  46. package/dist/highlight-cursor/enhanced-components.mjs +5 -5
  47. package/dist/highlight-cursor/index.cjs +16 -16
  48. package/dist/highlight-cursor/index.mjs +6 -6
  49. package/dist/hooks/index.cjs +5 -5
  50. package/dist/hooks/index.mjs +1 -1
  51. package/dist/index.cjs +190 -190
  52. package/dist/index.mjs +23 -23
  53. package/dist/ui/UIKitBadge.cjs +6 -6
  54. package/dist/ui/UIKitBadge.mjs +2 -2
  55. package/dist/ui/avatar.cjs +1 -1
  56. package/dist/ui/avatar.mjs +1 -1
  57. package/dist/ui/badge.cjs +1 -1
  58. package/dist/ui/badge.d.cts +1 -1
  59. package/dist/ui/badge.d.ts +1 -1
  60. package/dist/ui/badge.mjs +1 -1
  61. package/dist/ui/button.cjs +1 -1
  62. package/dist/ui/button.mjs +1 -1
  63. package/dist/ui/card.cjs +1 -1
  64. package/dist/ui/card.mjs +1 -1
  65. package/dist/ui/checkbox.cjs +1 -1
  66. package/dist/ui/checkbox.mjs +1 -1
  67. package/dist/ui/collapsible.cjs +1 -1
  68. package/dist/ui/collapsible.mjs +1 -1
  69. package/dist/ui/drawer.cjs +1 -1
  70. package/dist/ui/drawer.mjs +1 -1
  71. package/dist/ui/dropdown.cjs +1 -1
  72. package/dist/ui/dropdown.mjs +1 -1
  73. package/dist/ui/index.cjs +107 -107
  74. package/dist/ui/index.mjs +17 -17
  75. package/dist/ui/inputs/color-input.cjs +1 -1
  76. package/dist/ui/inputs/color-input.mjs +1 -1
  77. package/dist/ui/inputs/index.cjs +11 -11
  78. package/dist/ui/inputs/index.mjs +3 -3
  79. package/dist/ui/inputs/input-numbers.cjs +1 -1
  80. package/dist/ui/inputs/input-numbers.mjs +1 -1
  81. package/dist/ui/inputs/input.cjs +1 -1
  82. package/dist/ui/inputs/input.d.cts +1 -1
  83. package/dist/ui/inputs/input.d.ts +1 -1
  84. package/dist/ui/inputs/input.mjs +1 -1
  85. package/dist/ui/inputs/number-inputs/CmInput.cjs +1 -1
  86. package/dist/ui/inputs/number-inputs/CmInput.mjs +1 -1
  87. package/dist/ui/inputs/number-inputs/DegInput.cjs +1 -1
  88. package/dist/ui/inputs/number-inputs/DegInput.mjs +1 -1
  89. package/dist/ui/inputs/number-inputs/EulerDegInput.cjs +1 -1
  90. package/dist/ui/inputs/number-inputs/EulerDegInput.mjs +1 -1
  91. package/dist/ui/inputs/number-inputs/EulerInput.cjs +1 -1
  92. package/dist/ui/inputs/number-inputs/EulerInput.mjs +1 -1
  93. package/dist/ui/inputs/number-inputs/EulerRadInput.cjs +1 -1
  94. package/dist/ui/inputs/number-inputs/EulerRadInput.mjs +1 -1
  95. package/dist/ui/inputs/number-inputs/InchInput.cjs +1 -1
  96. package/dist/ui/inputs/number-inputs/InchInput.mjs +1 -1
  97. package/dist/ui/inputs/number-inputs/IntInput.cjs +1 -1
  98. package/dist/ui/inputs/number-inputs/IntInput.mjs +1 -1
  99. package/dist/ui/inputs/number-inputs/KVectorInput.cjs +1 -1
  100. package/dist/ui/inputs/number-inputs/KVectorInput.mjs +1 -1
  101. package/dist/ui/inputs/number-inputs/QuaternionInput.cjs +1 -1
  102. package/dist/ui/inputs/number-inputs/QuaternionInput.mjs +1 -1
  103. package/dist/ui/inputs/number-inputs/RadInput.cjs +1 -1
  104. package/dist/ui/inputs/number-inputs/RadInput.mjs +1 -1
  105. package/dist/ui/inputs/number-inputs/TimeInput.cjs +1 -1
  106. package/dist/ui/inputs/number-inputs/TimeInput.mjs +1 -1
  107. package/dist/ui/inputs/number-inputs/Vec3Input.cjs +1 -1
  108. package/dist/ui/inputs/number-inputs/Vec3Input.mjs +1 -1
  109. package/dist/ui/inputs/number-inputs/VectorInput.cjs +1 -1
  110. package/dist/ui/inputs/number-inputs/VectorInput.mjs +1 -1
  111. package/dist/ui/inputs/number-inputs/index.cjs +11 -11
  112. package/dist/ui/inputs/number-inputs/index.mjs +3 -3
  113. package/dist/ui/inputs/presets-input.cjs +1 -1
  114. package/dist/ui/inputs/presets-input.mjs +1 -1
  115. package/dist/ui/label.cjs +1 -1
  116. package/dist/ui/label.mjs +1 -1
  117. package/dist/ui/layout.cjs +1 -1
  118. package/dist/ui/layout.mjs +1 -1
  119. package/dist/ui/layouts/dock-layout/DockLayoutView.cjs +1 -1
  120. package/dist/ui/layouts/dock-layout/DockLayoutView.mjs +1 -1
  121. package/dist/ui/layouts/dock-layout/LayoutSlots.cjs +1 -1
  122. package/dist/ui/layouts/dock-layout/LayoutSlots.mjs +1 -1
  123. package/dist/ui/layouts/dock-layout/index.cjs +1 -1
  124. package/dist/ui/layouts/dock-layout/index.mjs +1 -1
  125. package/dist/ui/layouts/index.cjs +2 -2
  126. package/dist/ui/layouts/index.mjs +2 -2
  127. package/dist/ui/layouts/liquid-layout/LayoutSlots.cjs +1 -1
  128. package/dist/ui/layouts/liquid-layout/LayoutSlots.mjs +1 -1
  129. package/dist/ui/layouts/liquid-layout/LiquidLayoutView.cjs +1 -1
  130. package/dist/ui/layouts/liquid-layout/LiquidLayoutView.mjs +1 -1
  131. package/dist/ui/layouts/liquid-layout/index.cjs +1 -1
  132. package/dist/ui/layouts/liquid-layout/index.mjs +1 -1
  133. package/dist/ui/modal.cjs +1 -1
  134. package/dist/ui/modal.mjs +1 -1
  135. package/dist/ui/navigation.cjs +1 -1
  136. package/dist/ui/navigation.mjs +1 -1
  137. package/dist/ui/pagination.cjs +1 -1
  138. package/dist/ui/pagination.mjs +1 -1
  139. package/dist/ui/panel.cjs +1 -1
  140. package/dist/ui/panel.mjs +1 -1
  141. package/dist/ui/popover.cjs +1 -1
  142. package/dist/ui/popover.mjs +1 -1
  143. package/dist/ui/radio-group.cjs +1 -1
  144. package/dist/ui/radio-group.mjs +1 -1
  145. package/dist/ui/resizable.cjs +1 -1
  146. package/dist/ui/resizable.mjs +1 -1
  147. package/dist/ui/select.cjs +1 -1
  148. package/dist/ui/select.d.cts +1 -1
  149. package/dist/ui/select.d.ts +1 -1
  150. package/dist/ui/select.mjs +1 -1
  151. package/dist/ui/separator.cjs +1 -1
  152. package/dist/ui/separator.mjs +1 -1
  153. package/dist/ui/sheet.cjs +1 -1
  154. package/dist/ui/sheet.mjs +1 -1
  155. package/dist/ui/sidebar.cjs +26 -26
  156. package/dist/ui/sidebar.mjs +2 -2
  157. package/dist/ui/simple-tree-view.cjs +1 -1
  158. package/dist/ui/simple-tree-view.mjs +1 -1
  159. package/dist/ui/skeleton.cjs +1 -1
  160. package/dist/ui/skeleton.mjs +1 -1
  161. package/dist/ui/slider.cjs +1 -1
  162. package/dist/ui/slider.mjs +1 -1
  163. package/dist/ui/switch.cjs +1 -1
  164. package/dist/ui/switch.mjs +1 -1
  165. package/dist/ui/table.cjs +1 -1
  166. package/dist/ui/table.mjs +1 -1
  167. package/dist/ui/tabs.cjs +1 -1
  168. package/dist/ui/tabs.mjs +1 -1
  169. package/dist/ui/textarea.cjs +1 -1
  170. package/dist/ui/textarea.d.cts +1 -1
  171. package/dist/ui/textarea.d.ts +1 -1
  172. package/dist/ui/textarea.mjs +1 -1
  173. package/dist/ui/theme/ThemeToggles.cjs +1 -1
  174. package/dist/ui/theme/ThemeToggles.mjs +1 -1
  175. package/dist/ui/theme/index.cjs +1 -1
  176. package/dist/ui/theme/index.mjs +1 -1
  177. package/dist/ui/toggle-buttons.cjs +1 -1
  178. package/dist/ui/toggle-buttons.mjs +1 -1
  179. package/dist/ui/toggle-group.cjs +1 -1
  180. package/dist/ui/toggle-group.mjs +1 -1
  181. package/dist/ui/toggle.cjs +1 -1
  182. package/dist/ui/toggle.mjs +1 -1
  183. package/dist/ui/toolbar.cjs +1 -1
  184. package/dist/ui/toolbar.mjs +1 -1
  185. package/dist/ui/tooltip.cjs +1 -1
  186. package/dist/ui/tooltip.mjs +1 -1
  187. package/dist/ui/tree-view/TreeSearchBar.cjs +1 -1
  188. package/dist/ui/tree-view/TreeSearchBar.mjs +1 -1
  189. package/dist/ui/tree-view/TreeView.cjs +1 -1
  190. package/dist/ui/tree-view/TreeView.mjs +1 -1
  191. package/dist/ui/tree-view/index.cjs +6 -6
  192. package/dist/ui/tree-view/index.mjs +2 -2
  193. package/dist/ui/tree-view-legacy.cjs +9 -9
  194. package/dist/ui/tree-view-legacy.mjs +5 -5
  195. package/dist/ui/waterfall/CursorOverlay.cjs +1 -1
  196. package/dist/ui/waterfall/CursorOverlay.mjs +1 -1
  197. package/dist/ui/waterfall/TimelineEvent.cjs +1 -1
  198. package/dist/ui/waterfall/TimelineEvent.mjs +1 -1
  199. package/dist/ui/waterfall/TimelineProcessBar.cjs +1 -1
  200. package/dist/ui/waterfall/TimelineProcessBar.mjs +1 -1
  201. package/dist/ui/waterfall/Wedges.cjs +1 -1
  202. package/dist/ui/waterfall/Wedges.mjs +1 -1
  203. package/dist/ui/waterfall/index.cjs +8 -8
  204. package/dist/ui/waterfall/index.mjs +7 -7
  205. package/package.json +28 -2
  206. package/src/SyncScroll/README.md +283 -0
  207. package/src/SyncScroll/SyncScroll.tsx +361 -0
  208. package/src/SyncScroll/index.ts +22 -0
  209. package/src/SyncScroll/useSyncScroll.tsx +226 -0
  210. package/src/cli/dial-cli.ts +1133 -0
  211. package/src/dial/DialPanel.tsx +208 -0
  212. package/src/dial/DialProvider.example.tsx +80 -0
  213. package/src/dial/DialProvider.tsx +138 -0
  214. package/src/dial/index.ts +26 -0
  215. package/src/dial/wrapped-inputs/ControlledInputs.tsx +176 -0
  216. package/src/dial/wrapped-inputs/DialInputs.tsx +401 -0
  217. package/src/dial/wrapped-inputs/DialVectorInput.tsx +125 -0
  218. package/src/dial/wrapped-inputs/index.ts +25 -0
  219. package/src/highlight-cursor/cursor-context.tsx +23 -0
  220. package/src/highlight-cursor/cursor-provider.tsx +264 -0
  221. package/src/highlight-cursor/enhanced-components.tsx +16 -0
  222. package/src/highlight-cursor/index.ts +21 -0
  223. package/src/highlight-cursor/tabs-cursor-context.tsx +121 -0
  224. package/src/highlight-cursor/types.ts +40 -0
  225. package/src/highlight-cursor/with-cursor.tsx +144 -0
  226. package/src/hooks/clientOnly.tsx +54 -0
  227. package/src/hooks/cn.ts +33 -0
  228. package/src/hooks/index.ts +9 -0
  229. package/src/hooks/useDocument.tsx +18 -0
  230. package/src/hooks/useDragSelect.ts +116 -0
  231. package/src/hooks/useIsMobile.ts +35 -0
  232. package/src/hooks/useLocalStorage.ts +122 -0
  233. package/src/hooks/useLocation.tsx +95 -0
  234. package/src/hooks/useQueryParams.tsx +31 -0
  235. package/src/hooks/useWindow.tsx +18 -0
  236. package/src/index.css +5 -0
  237. package/src/index.ts +5 -0
  238. package/src/styles/.editorconfig +0 -0
  239. package/src/styles/theme.css +152 -0
  240. package/src/styles/toast.css +67 -0
  241. package/src/styles/variables.css +229 -0
  242. package/src/ui/UIKitBadge.tsx +171 -0
  243. package/src/ui/avatar.tsx +221 -0
  244. package/src/ui/badge.tsx +74 -0
  245. package/src/ui/button.tsx +143 -0
  246. package/src/ui/card.tsx +146 -0
  247. package/src/ui/checkbox.tsx +78 -0
  248. package/src/ui/collapsible.tsx +43 -0
  249. package/src/ui/drag-selectable/DragSelectProvider.tsx +178 -0
  250. package/src/ui/drag-selectable/createSelectable.tsx +43 -0
  251. package/src/ui/drag-selectable/index.ts +2 -0
  252. package/src/ui/drawer.tsx +137 -0
  253. package/src/ui/dropdown.tsx +707 -0
  254. package/src/ui/icons/MouseCursorIcons.tsx +39 -0
  255. package/src/ui/icons/cursor.tsx +38 -0
  256. package/src/ui/icons/index.ts +2 -0
  257. package/src/ui/index.ts +45 -0
  258. package/src/ui/inputs/color-input.tsx +61 -0
  259. package/src/ui/inputs/index.tsx +5 -0
  260. package/src/ui/inputs/input-numbers.tsx +394 -0
  261. package/src/ui/inputs/input.tsx +155 -0
  262. package/src/ui/inputs/number-inputs/CmInput.tsx +26 -0
  263. package/src/ui/inputs/number-inputs/DegInput.tsx +26 -0
  264. package/src/ui/inputs/number-inputs/EulerDegInput.tsx +14 -0
  265. package/src/ui/inputs/number-inputs/EulerInput.tsx +30 -0
  266. package/src/ui/inputs/number-inputs/EulerRadInput.tsx +14 -0
  267. package/src/ui/inputs/number-inputs/InchInput.tsx +26 -0
  268. package/src/ui/inputs/number-inputs/IntInput.tsx +46 -0
  269. package/src/ui/inputs/number-inputs/KVectorInput.tsx +20 -0
  270. package/src/ui/inputs/number-inputs/QuaternionInput.tsx +27 -0
  271. package/src/ui/inputs/number-inputs/RadInput.tsx +26 -0
  272. package/src/ui/inputs/number-inputs/TimeInput.tsx +26 -0
  273. package/src/ui/inputs/number-inputs/Vec3Input.tsx +26 -0
  274. package/src/ui/inputs/number-inputs/VectorInput.tsx +38 -0
  275. package/src/ui/inputs/number-inputs/index.ts +38 -0
  276. package/src/ui/inputs/presets-input.tsx +59 -0
  277. package/src/ui/label.tsx +35 -0
  278. package/src/ui/layout.tsx +43 -0
  279. package/src/ui/layouts/dock-layout/DockLayoutView.tsx +128 -0
  280. package/src/ui/layouts/dock-layout/LayoutSlots.tsx +85 -0
  281. package/src/ui/layouts/dock-layout/index.tsx +2 -0
  282. package/src/ui/layouts/index.ts +2 -0
  283. package/src/ui/layouts/liquid-layout/LayoutSlots.tsx +82 -0
  284. package/src/ui/layouts/liquid-layout/LiquidLayoutView.tsx +76 -0
  285. package/src/ui/layouts/liquid-layout/index.ts +1 -0
  286. package/src/ui/modal.tsx +211 -0
  287. package/src/ui/navigation.tsx +86 -0
  288. package/src/ui/pagination.tsx +129 -0
  289. package/src/ui/panel.tsx +146 -0
  290. package/src/ui/popover.tsx +112 -0
  291. package/src/ui/radio-group.tsx +63 -0
  292. package/src/ui/resizable.tsx +52 -0
  293. package/src/ui/select.tsx +365 -0
  294. package/src/ui/separator.tsx +26 -0
  295. package/src/ui/sheet.tsx +257 -0
  296. package/src/ui/sidebar.tsx +695 -0
  297. package/src/ui/simple-tree-view.tsx +604 -0
  298. package/src/ui/skeleton.tsx +15 -0
  299. package/src/ui/slider.tsx +204 -0
  300. package/src/ui/switch.tsx +45 -0
  301. package/src/ui/table.tsx +99 -0
  302. package/src/ui/tabs.tsx +192 -0
  303. package/src/ui/textarea.tsx +55 -0
  304. package/src/ui/theme/ThemeProvider.tsx +319 -0
  305. package/src/ui/theme/ThemeToggles.tsx +84 -0
  306. package/src/ui/theme/index.ts +21 -0
  307. package/src/ui/theme/themeScript.tsx +179 -0
  308. package/src/ui/theme/types.ts +61 -0
  309. package/src/ui/toast.tsx +30 -0
  310. package/src/ui/toggle-buttons.tsx +290 -0
  311. package/src/ui/toggle-group.tsx +236 -0
  312. package/src/ui/toggle.tsx +94 -0
  313. package/src/ui/toolbar.tsx +54 -0
  314. package/src/ui/tooltip.tsx +171 -0
  315. package/src/ui/tree-view/TreeSearchBar.tsx +88 -0
  316. package/src/ui/tree-view/TreeView.tsx +232 -0
  317. package/src/ui/tree-view/hooks.tsx +289 -0
  318. package/src/ui/tree-view/index.ts +4 -0
  319. package/src/ui/tree-view/types.ts +23 -0
  320. package/src/ui/tree-view-legacy.tsx +644 -0
  321. package/src/ui/version-badge.tsx +0 -0
  322. package/src/ui/waterfall/CursorOverlay.tsx +96 -0
  323. package/src/ui/waterfall/NavigationControls.tsx +42 -0
  324. package/src/ui/waterfall/Tick.tsx +51 -0
  325. package/src/ui/waterfall/TimeRuleEventDot.tsx +19 -0
  326. package/src/ui/waterfall/TimelineEvent.tsx +60 -0
  327. package/src/ui/waterfall/TimelineProcessBar.tsx +207 -0
  328. package/src/ui/waterfall/Wedges.tsx +67 -0
  329. package/src/ui/waterfall/WheelZoomContext.tsx +128 -0
  330. package/src/ui/waterfall/hooks/useTimelineState.tsx +134 -0
  331. package/src/ui/waterfall/hooks/useViewport.tsx +293 -0
  332. package/src/ui/waterfall/index.tsx +326 -0
  333. package/src/ui/waterfall/types.ts +20 -0
  334. package/src/ui/waterfall/utils.ts +91 -0
  335. package/dist/chunk-W2YAQV5N.mjs +0 -57
  336. package/dist/{chunk-QLCEHV4Q.mjs → chunk-2OZK5DY5.mjs} +2 -2
  337. package/dist/{chunk-Z35DNFRZ.cjs → chunk-7IS37C3P.cjs} +2 -2
  338. package/dist/{chunk-4AOAH77D.mjs → chunk-A2PCEL5S.mjs} +2 -2
  339. package/dist/{chunk-K4VD5PPY.mjs → chunk-BIUDC66P.mjs} +1 -1
  340. package/dist/{chunk-RKJR6RZU.cjs → chunk-OYNLQTHW.cjs} +1 -1
  341. package/dist/{chunk-VE3XLQLO.cjs → chunk-QUFZWF4E.cjs} +2 -2
@@ -0,0 +1,319 @@
1
+ import {
2
+ createContext,
3
+ PropsWithChildren,
4
+ useCallback,
5
+ useContext,
6
+ useEffect,
7
+ useMemo,
8
+ useState,
9
+ } from "react";
10
+
11
+ import { ThemeScript } from "./themeScript";
12
+ import type { Attribute, ThemeProviderProps, UseThemeProps, BaseTheme, ComputedTheme } from "./types";
13
+
14
+ export const colorSchemes = ["light", "dark"] as const;
15
+ export const MEDIA = "(prefers-color-scheme: dark)";
16
+ export const isServer = typeof window === "undefined";
17
+ export const ThemeContext = createContext<UseThemeProps | undefined>(undefined);
18
+
19
+ export const saveToLS = (key: string, value: string) => {
20
+ // Save to storage
21
+ try {
22
+ localStorage.setItem(key, value);
23
+ } catch {
24
+ // Unsupported
25
+ }
26
+ };
27
+
28
+ export const getFromLS = (key: string, fallback: string): string => {
29
+ if (isServer) return fallback;
30
+ try {
31
+ return localStorage.getItem(key) || fallback;
32
+ } catch {
33
+ return fallback;
34
+ }
35
+ };
36
+
37
+ /**
38
+ * Calculate the actual theme name
39
+ */
40
+ export const computeTheme = (baseTheme: BaseTheme, isLiquid: boolean, systemIsDark: boolean): ComputedTheme => {
41
+ let resolvedBase: "light" | "dark";
42
+
43
+ if (baseTheme === "system") {
44
+ resolvedBase = systemIsDark ? "dark" : "light";
45
+ } else {
46
+ resolvedBase = baseTheme;
47
+ }
48
+
49
+ return isLiquid ? `liquid-${resolvedBase}` as ComputedTheme : resolvedBase;
50
+ };
51
+
52
+ /**
53
+ * Parse old theme string to new state structure
54
+ */
55
+ export const parseOldTheme = (oldTheme: string): { baseTheme: BaseTheme; isLiquid: boolean } => {
56
+ if (oldTheme === "system") {
57
+ return { baseTheme: "system", isLiquid: false };
58
+ }
59
+
60
+ if (oldTheme.startsWith("liquid-")) {
61
+ const base = oldTheme.replace("liquid-", "") as "light" | "dark";
62
+ return { baseTheme: base, isLiquid: true };
63
+ }
64
+
65
+ return { baseTheme: oldTheme as BaseTheme, isLiquid: false };
66
+ };
67
+
68
+
69
+
70
+ /**
71
+ * Hook to read and update theme state from `ThemeProvider`.
72
+ * Throws if used outside a provider.
73
+ */
74
+ export const useTheme = () => {
75
+ const context = useContext(ThemeContext);
76
+
77
+ if (context === undefined) throw new Error("useTheme must be used within a ThemeProvider");
78
+
79
+ return context;
80
+ };
81
+
82
+ /**
83
+ * Provides theme context and DOM attribute synchronization for theming.
84
+ * Nesting providers is supported; inner providers will passthrough.
85
+ */
86
+ export const ThemeProvider = (props: PropsWithChildren<ThemeProviderProps>) => {
87
+ const context = useContext(ThemeContext);
88
+
89
+ // Ignore nested context providers, just passthrough children
90
+ if (context) return <>{props.children}</>;
91
+ return <Theme {...props} />;
92
+ };
93
+
94
+ export const defaultThemes = ["light", "dark", "liquid-light", "liquid-dark"];
95
+
96
+ /**
97
+ * Internal component implementing theme persistence, system preference,
98
+ * and DOM attribute updates.
99
+ */
100
+ export const Theme = ({
101
+ forcedTheme,
102
+ disableTransitionOnChange = false,
103
+ enableSystem = true,
104
+ enableColorScheme = false,
105
+ storageKey = "vuer-uikit-theme",
106
+ defaultBaseTheme = enableSystem ? "system" : "light",
107
+ defaultIsLiquid = true,
108
+ attribute = "class",
109
+ value,
110
+ children,
111
+ nonce,
112
+ scriptProps,
113
+ }: PropsWithChildren<ThemeProviderProps>) => {
114
+ const themes = defaultThemes;
115
+
116
+ const initialState = useMemo(() => {
117
+ return { baseTheme: defaultBaseTheme!, isLiquid: defaultIsLiquid! };
118
+ }, [defaultBaseTheme, defaultIsLiquid]);
119
+
120
+ const [baseTheme, setBaseThemeState] = useState<BaseTheme>(() => {
121
+ const stored = getFromLS(`${storageKey}-base`, "");
122
+ if (!stored) {
123
+ return initialState.baseTheme;
124
+ }
125
+ return (["light", "dark", "system"].includes(stored) ? stored : initialState.baseTheme) as BaseTheme;
126
+ });
127
+
128
+ const [isLiquid, setIsLiquidState] = useState<boolean>(() => {
129
+ const stored = getFromLS(`${storageKey}-liquid`, "");
130
+ if (!stored) {
131
+ return initialState.isLiquid;
132
+ }
133
+ return stored === "true";
134
+ });
135
+
136
+ const [systemIsDark, setSystemIsDark] = useState(() => {
137
+ if (isServer) {
138
+ return false;
139
+ }
140
+ return window.matchMedia(MEDIA).matches;
141
+ });
142
+
143
+ const attrs = !value ? themes : Object.values(value);
144
+
145
+ const computedTheme = useMemo(() =>
146
+ computeTheme(baseTheme, isLiquid, systemIsDark),
147
+ [baseTheme, isLiquid, systemIsDark]
148
+ );
149
+
150
+ const resolvedTheme = useMemo((): ComputedTheme => {
151
+ if (forcedTheme) {
152
+ const parsed = parseOldTheme(forcedTheme);
153
+ return computeTheme(parsed.baseTheme, parsed.isLiquid, systemIsDark);
154
+ }
155
+ return computedTheme;
156
+ }, [forcedTheme, computedTheme, systemIsDark]);
157
+
158
+ const applyTheme = useCallback(
159
+ (theme: string) => {
160
+ if (!theme) return;
161
+
162
+ const name = value ? value[theme] : theme;
163
+ const enable = disableTransitionOnChange ? disableAnimation(nonce) : null;
164
+ const d = document.documentElement;
165
+
166
+ const handleAttribute = (attr: Attribute) => {
167
+ if (attr === "class") {
168
+ d.classList.remove(...attrs);
169
+ if (name) d.classList.add(name);
170
+ } else if (attr.startsWith("data-")) {
171
+ if (name) {
172
+ d.setAttribute(attr, name);
173
+ } else {
174
+ d.removeAttribute(attr);
175
+ }
176
+ }
177
+ };
178
+
179
+ if (Array.isArray(attribute)) attribute.forEach(handleAttribute);
180
+ else handleAttribute(attribute);
181
+
182
+ if (enableColorScheme) {
183
+ // Extract base color scheme from theme name
184
+ const isDark = theme.includes("dark");
185
+ d.style.colorScheme = isDark ? "dark" : "light";
186
+ }
187
+
188
+ enable?.();
189
+ },
190
+ [attribute, attrs, disableTransitionOnChange, enableColorScheme, nonce, value],
191
+ );
192
+
193
+ const setBaseTheme = useCallback((newBaseTheme: BaseTheme) => {
194
+ setBaseThemeState(newBaseTheme);
195
+ saveToLS(`${storageKey}-base`, newBaseTheme);
196
+ }, [storageKey]);
197
+
198
+ const toggleLiquid = useCallback(() => {
199
+ setIsLiquidState(prev => {
200
+ const newValue = !prev;
201
+ saveToLS(`${storageKey}-liquid`, String(newValue));
202
+ return newValue;
203
+ });
204
+ }, [storageKey]);
205
+
206
+ const handleMediaQuery = useCallback(
207
+ (e: MediaQueryListEvent | MediaQueryList) => {
208
+ const isDark = e.matches;
209
+ setSystemIsDark(isDark);
210
+ },
211
+ [],
212
+ );
213
+
214
+ // Always listen to System preference
215
+ useEffect(() => {
216
+ const media = window.matchMedia(MEDIA);
217
+
218
+ // Intentionally use deprecated listener methods to support iOS & old browsers
219
+ media.addListener(handleMediaQuery);
220
+ handleMediaQuery(media);
221
+
222
+ return () => media.removeListener(handleMediaQuery);
223
+ }, [handleMediaQuery]);
224
+
225
+ // localStorage event handling
226
+ useEffect(() => {
227
+ const handleStorage = (e: StorageEvent) => {
228
+ if (e.key === `${storageKey}-base` && e.newValue) {
229
+ setBaseThemeState(e.newValue as BaseTheme);
230
+ } else if (e.key === `${storageKey}-liquid` && e.newValue !== null) {
231
+ setIsLiquidState(e.newValue === "true");
232
+ }
233
+ };
234
+
235
+ window.addEventListener("storage", handleStorage);
236
+ return () => window.removeEventListener("storage", handleStorage);
237
+ }, [storageKey]);
238
+
239
+ // Whenever theme changes, apply it
240
+ useEffect(() => {
241
+ applyTheme(resolvedTheme);
242
+ }, [applyTheme, resolvedTheme]);
243
+
244
+ const contextValue = useMemo(
245
+ () => ({
246
+ baseTheme,
247
+ setBaseTheme,
248
+ isLiquid,
249
+ toggleLiquid,
250
+ computedTheme,
251
+ resolvedTheme,
252
+ systemTheme: (enableSystem ? (systemIsDark ? "dark" : "light") : undefined) as "light" | "dark" | undefined,
253
+ forcedTheme,
254
+ storageKey,
255
+ }),
256
+ [
257
+ baseTheme, setBaseTheme, isLiquid, toggleLiquid, computedTheme, resolvedTheme,
258
+ systemIsDark, enableSystem, forcedTheme, storageKey
259
+ ],
260
+ );
261
+
262
+ return (
263
+ <ThemeContext.Provider value={contextValue}>
264
+ <ThemeScript
265
+ {...{
266
+ forcedTheme,
267
+ storageKey,
268
+ attribute,
269
+ enableSystem,
270
+ enableColorScheme,
271
+ defaultTheme: initialState.baseTheme === "system"
272
+ ? "system"
273
+ : (initialState.isLiquid ? `liquid-${initialState.baseTheme}` : initialState.baseTheme),
274
+ value,
275
+ themes,
276
+ nonce,
277
+ scriptProps,
278
+ }}
279
+ />
280
+
281
+ {children}
282
+ </ThemeContext.Provider>
283
+ );
284
+ };
285
+
286
+ // Helpers
287
+ export const getTheme = (key: string, fallback: string) => {
288
+ return getFromLS(key, fallback);
289
+ };
290
+
291
+ export const disableAnimation = (nonce?: string) => {
292
+ const css = document.createElement("style");
293
+ if (nonce) css.setAttribute("nonce", nonce);
294
+ css.appendChild(
295
+ document.createTextNode(
296
+ `*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}`,
297
+ ),
298
+ );
299
+ document.head.appendChild(css);
300
+
301
+ return () => {
302
+ // Force restyle
303
+ (() => window.getComputedStyle(document.body))();
304
+
305
+ // Wait for next tick before removing
306
+ setTimeout(() => {
307
+ if (css && css.parentNode) {
308
+ css.parentNode.removeChild(css);
309
+ }
310
+ }, 1);
311
+ };
312
+ };
313
+
314
+ export const getSystemTheme = (e?: MediaQueryList | MediaQueryListEvent) => {
315
+ if (isServer) return "liquid-light"; // Default to liquid-light on server
316
+ if (!e) e = window.matchMedia(MEDIA);
317
+ const isDark = e.matches;
318
+ return isDark ? "liquid-dark" : "liquid-light";
319
+ };
@@ -0,0 +1,84 @@
1
+ // Liquid toggle component
2
+ import { Droplet, Moon, Sun, SunMoon } from "lucide-react";
3
+ import React, { type ComponentProps } from "react";
4
+
5
+ import { Button } from "../button";
6
+ import { Tooltip, TooltipContent, TooltipTrigger } from "../tooltip";
7
+ import { useTheme } from "./ThemeProvider";
8
+ import { cn, createClientOnlyComponent, useDocument } from "../../hooks";
9
+
10
+ type LiquidToggleProps = Omit<ComponentProps<typeof Button>, "onClick" | "children">;
11
+
12
+ export const LiquidToggle: React.FC<LiquidToggleProps> = ({ className, ...props }) => {
13
+ const { isLiquid, toggleLiquid } = useTheme();
14
+
15
+ return (
16
+ <Tooltip>
17
+ <TooltipTrigger asChild>
18
+ <Button
19
+ icon
20
+ variant="ghost"
21
+ value={isLiquid}
22
+ aria-label={`Toggle liquid theme: ${isLiquid ? "on" : "off"}`}
23
+ {...props}
24
+ onClick={toggleLiquid}
25
+ className={className}
26
+ >
27
+ <Droplet size={20} />
28
+ </Button>
29
+ </TooltipTrigger>
30
+ <TooltipContent>Toggle liquid theme</TooltipContent>
31
+ </Tooltip>
32
+ );
33
+ };
34
+
35
+ // Theme color toggle component for light/dark/system
36
+ type ThemeColorToggleProps = Omit<ComponentProps<typeof Button>, "onClick" | "children">;
37
+
38
+ export const ThemeColorToggle: React.FC<ThemeColorToggleProps> = (props) => {
39
+ const { baseTheme, setBaseTheme, resolvedTheme } = useTheme();
40
+
41
+ const handleThemeChange = () => {
42
+ if (baseTheme === "light") {
43
+ setBaseTheme("dark");
44
+ } else if (baseTheme === "dark") {
45
+ setBaseTheme("system");
46
+ } else if (baseTheme === "system") {
47
+ setBaseTheme("light");
48
+ }
49
+ };
50
+
51
+ const isDark = resolvedTheme.includes("dark");
52
+ const isSystem = baseTheme === "system";
53
+
54
+ return (
55
+ <Button
56
+ icon
57
+ variant="ghost"
58
+ aria-label={`Current theme: ${isSystem ? "system" : isDark ? "dark" : "light"}`}
59
+ {...props}
60
+ onClick={handleThemeChange}
61
+ >
62
+ {isSystem ? <SunMoon size={20} /> : isDark ? <Moon size={20} /> : <Sun size={20} />}
63
+ </Button>
64
+ );
65
+ };
66
+
67
+ type ThemeTogglesProps = {
68
+ className?: string;
69
+ liquidToggleProps?: Omit<LiquidToggleProps, "className">;
70
+ colorToggleProps?: Omit<ThemeColorToggleProps, "className">;
71
+ };
72
+
73
+ export const ThemeToggles: React.FC<ThemeTogglesProps> = ({
74
+ className,
75
+ liquidToggleProps = {},
76
+ colorToggleProps = {}
77
+ }) => {
78
+ return (
79
+ <div className={cn("flex gap-1", className)}>
80
+ <LiquidToggle {...liquidToggleProps} />
81
+ <ThemeColorToggle {...colorToggleProps} />
82
+ </div>
83
+ );
84
+ };
@@ -0,0 +1,21 @@
1
+ export {
2
+ useTheme,
3
+ ThemeProvider,
4
+ defaultThemes,
5
+ computeTheme,
6
+ parseOldTheme
7
+ } from "./ThemeProvider";
8
+ export {
9
+ LiquidToggle,
10
+ ThemeColorToggle,
11
+ ThemeToggles,
12
+ } from "./ThemeToggles";
13
+ export { ThemeScript } from "./themeScript";
14
+
15
+ export type {
16
+ Attribute,
17
+ ThemeProviderProps,
18
+ UseThemeProps,
19
+ BaseTheme,
20
+ ComputedTheme
21
+ } from "./types";
@@ -0,0 +1,179 @@
1
+ import { memo } from "react";
2
+
3
+ import { Attribute, ThemeProviderProps } from "./types";
4
+
5
+ const themeScript = (
6
+ defaultTheme: string,
7
+ storageKey: string,
8
+ attribute: Attribute | Attribute[],
9
+ themes: string[],
10
+ value?: Record<string, string> | undefined,
11
+ forcedTheme?: string | undefined,
12
+ enableSystem?: boolean | undefined,
13
+ enableColorScheme?: boolean | undefined,
14
+ ) => {
15
+ const el = document.documentElement;
16
+
17
+ function updateDOM(theme: string) {
18
+ const attributes = Array.isArray(attribute) ? attribute : [attribute];
19
+
20
+ attributes.forEach((attr) => {
21
+ const isClass = attr === "class";
22
+ const classes = isClass && value ? themes.map((t) => value[t] || t) : themes;
23
+ if (isClass) {
24
+ el.classList.remove(...classes);
25
+ el.classList.add(value && value[theme] ? value[theme] : theme);
26
+ } else {
27
+ el.setAttribute(attr, theme);
28
+ }
29
+ });
30
+
31
+ setColorScheme(theme);
32
+ }
33
+
34
+ function setColorScheme(theme: string) {
35
+ if (enableColorScheme) {
36
+ // Extract base color scheme from theme name
37
+ const isDark = theme.includes("dark");
38
+ el.style.colorScheme = isDark ? "dark" : "light";
39
+ }
40
+ }
41
+
42
+ function getSystemTheme() {
43
+ return window.matchMedia("(prefers-color-scheme: dark)").matches
44
+ ? "liquid-dark"
45
+ : "liquid-light";
46
+ }
47
+
48
+ function parseOldTheme(oldTheme: string): { baseTheme: string; isLiquid: boolean } {
49
+ if (oldTheme === "system") {
50
+ return { baseTheme: "system", isLiquid: false };
51
+ }
52
+
53
+ if (oldTheme.startsWith("liquid-")) {
54
+ const base = oldTheme.replace("liquid-", "");
55
+ return { baseTheme: base, isLiquid: true };
56
+ }
57
+
58
+ return { baseTheme: oldTheme, isLiquid: false };
59
+ }
60
+
61
+ function computeTheme(baseTheme: string, isLiquid: boolean, systemIsDark: boolean): string {
62
+ let resolvedBase: string;
63
+
64
+ if (baseTheme === "system") {
65
+ resolvedBase = systemIsDark ? "dark" : "light";
66
+ } else {
67
+ resolvedBase = baseTheme;
68
+ }
69
+
70
+ return isLiquid ? `liquid-${resolvedBase}` : resolvedBase;
71
+ }
72
+
73
+ function getStoredState(): { baseTheme: string; isLiquid: boolean } {
74
+ try {
75
+ // Try to read from new storage format
76
+ const baseTheme = localStorage.getItem(`${storageKey}-base`);
77
+ const isLiquid = localStorage.getItem(`${storageKey}-liquid`);
78
+
79
+ if (baseTheme !== null && isLiquid !== null) {
80
+ return {
81
+ baseTheme,
82
+ isLiquid: isLiquid === "true"
83
+ };
84
+ }
85
+
86
+ const oldTheme = localStorage.getItem(storageKey);
87
+ if (oldTheme) {
88
+ return parseOldTheme(oldTheme);
89
+ }
90
+
91
+ const defaultBaseTheme = enableSystem ? "system" : "light";
92
+ return {
93
+ baseTheme: defaultBaseTheme,
94
+ isLiquid: true
95
+ };
96
+ } catch {
97
+ // Default values on error
98
+ const defaultBaseTheme = enableSystem ? "system" : "light";
99
+ return {
100
+ baseTheme: defaultBaseTheme,
101
+ isLiquid: true
102
+ };
103
+ }
104
+ }
105
+
106
+ if (forcedTheme) {
107
+ updateDOM(forcedTheme);
108
+ } else {
109
+ const { baseTheme, isLiquid } = getStoredState();
110
+
111
+ const hasStoredBase = localStorage.getItem(`${storageKey}-base`) !== null;
112
+ const hasStoredLiquid = localStorage.getItem(`${storageKey}-liquid`) !== null;
113
+
114
+ if (!hasStoredBase || !hasStoredLiquid) {
115
+ try {
116
+ if (!hasStoredBase) {
117
+ localStorage.setItem(`${storageKey}-base`, baseTheme);
118
+ }
119
+ if (!hasStoredLiquid) {
120
+ localStorage.setItem(`${storageKey}-liquid`, String(isLiquid));
121
+ }
122
+ } catch {
123
+ }
124
+ }
125
+
126
+ const systemIsDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
127
+ const computedTheme = computeTheme(baseTheme, isLiquid, systemIsDark);
128
+ updateDOM(computedTheme);
129
+ }
130
+ };
131
+
132
+ interface ThemeScriptProps extends ThemeProviderProps {
133
+ themes: string[];
134
+ storageKey: string;
135
+ defaultTheme: string;
136
+ attribute: Attribute | Attribute[];
137
+ forcedTheme?: string;
138
+ enableSystem?: boolean;
139
+ enableColorScheme?: boolean;
140
+ value?: Record<string, string>;
141
+ nonce?: string;
142
+ }
143
+
144
+ const ThemeScript = memo(
145
+ ({
146
+ forcedTheme,
147
+ storageKey,
148
+ attribute,
149
+ enableSystem,
150
+ enableColorScheme,
151
+ defaultTheme,
152
+ value,
153
+ themes,
154
+ nonce,
155
+ scriptProps,
156
+ }: ThemeScriptProps) => {
157
+ const scriptArgs = JSON.stringify([
158
+ defaultTheme,
159
+ storageKey,
160
+ attribute,
161
+ themes,
162
+ value,
163
+ forcedTheme,
164
+ enableSystem,
165
+ enableColorScheme,
166
+ ]).slice(1, -1);
167
+
168
+ return (
169
+ <script
170
+ {...scriptProps}
171
+ suppressHydrationWarning
172
+ nonce={typeof window === "undefined" ? nonce : ""}
173
+ dangerouslySetInnerHTML={{ __html: `(${themeScript.toString()})(${scriptArgs})` }}
174
+ />
175
+ );
176
+ },
177
+ );
178
+
179
+ export { ThemeScript };
@@ -0,0 +1,61 @@
1
+ import { DetailedHTMLProps, Dispatch, ScriptHTMLAttributes, SetStateAction } from "react";
2
+
3
+ type DataAttribute = `data-${string}`;
4
+
5
+ interface ScriptProps
6
+ extends DetailedHTMLProps<ScriptHTMLAttributes<HTMLScriptElement>, HTMLScriptElement> {
7
+ [dataAttribute: DataAttribute]: never;
8
+ }
9
+
10
+ export type BaseTheme = "dark" | "light" | "system";
11
+
12
+ /** Computed actual theme type */
13
+ export type ComputedTheme = "light" | "dark" | "liquid-light" | "liquid-dark";
14
+
15
+ export interface UseThemeProps {
16
+ /** Base theme color */
17
+ baseTheme: BaseTheme;
18
+ /** Set base theme color */
19
+ setBaseTheme: (theme: BaseTheme) => void;
20
+ /** Whether liquid mode is enabled */
21
+ isLiquid: boolean;
22
+ /** Toggle liquid mode */
23
+ toggleLiquid: () => void;
24
+ /** Computed actual theme name */
25
+ computedTheme: ComputedTheme;
26
+ /** Resolved theme (handles system theme) */
27
+ resolvedTheme: ComputedTheme;
28
+ /** System theme preference (only valid when baseTheme is system) */
29
+ systemTheme?: "dark" | "light" | undefined;
30
+ /** Forced theme name */
31
+ forcedTheme?: string | undefined;
32
+ /** Local storage key */
33
+ storageKey: string;
34
+ }
35
+
36
+ export type Attribute = DataAttribute | "class";
37
+
38
+ export interface ThemeProviderProps {
39
+ /** Default base theme */
40
+ defaultBaseTheme?: BaseTheme | undefined;
41
+ /** Default liquid mode enabled */
42
+ defaultIsLiquid?: boolean | undefined;
43
+ /** Forced theme name */
44
+ forcedTheme?: string | undefined;
45
+ /** Enable system theme detection */
46
+ enableSystem?: boolean | undefined;
47
+ /** Disable CSS transitions when switching themes */
48
+ disableTransitionOnChange?: boolean | undefined;
49
+ /** Enable color scheme indication for built-in UI elements */
50
+ enableColorScheme?: boolean | undefined;
51
+ /** Local storage key prefix */
52
+ storageKey?: string | undefined;
53
+ /** HTML attribute name */
54
+ attribute?: Attribute | Attribute[] | undefined;
55
+ /** Theme name to attribute value mapping */
56
+ value?: Record<string, string> | undefined;
57
+ /** CSP nonce */
58
+ nonce?: string;
59
+ /** Script tag properties */
60
+ scriptProps?: ScriptProps;
61
+ }
@@ -0,0 +1,30 @@
1
+ import { useEffect, useState } from "react";
2
+ import { Toaster as Sonner, ToasterProps, toast } from "sonner";
3
+
4
+ /**
5
+ * Client-only Toaster wrapper applying default classes.
6
+ */
7
+ const Toaster = ({ ...props }: ToasterProps) => {
8
+ const [isClient, setIsClient] = useState(false);
9
+
10
+ useEffect(() => {
11
+ setIsClient(true);
12
+ }, []);
13
+
14
+ // Don't render anything during SSR
15
+ if (!isClient) {
16
+ return null;
17
+ }
18
+
19
+ return (
20
+ <Sonner
21
+ toastOptions={{
22
+ className: "vk-toast liquid:liquid-bg",
23
+ descriptionClassName: "vk-toast-description",
24
+ }}
25
+ {...props}
26
+ />
27
+ );
28
+ };
29
+
30
+ export { Toaster, toast };