gladvn 0.2.14

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 (500) hide show
  1. package/README.md +112 -0
  2. package/bin/cli.js +326 -0
  3. package/dist/components/macro/accordion-preset.d.ts +14 -0
  4. package/dist/components/macro/accordion-preset.d.ts.map +1 -0
  5. package/dist/components/macro/alert-dialog-preset.d.ts +26 -0
  6. package/dist/components/macro/alert-dialog-preset.d.ts.map +1 -0
  7. package/dist/components/macro/alert-preset.d.ts +14 -0
  8. package/dist/components/macro/alert-preset.d.ts.map +1 -0
  9. package/dist/components/macro/avatar-preset.d.ts +11 -0
  10. package/dist/components/macro/avatar-preset.d.ts.map +1 -0
  11. package/dist/components/macro/breadcrumb-preset.d.ts +12 -0
  12. package/dist/components/macro/breadcrumb-preset.d.ts.map +1 -0
  13. package/dist/components/macro/card-preset.d.ts +10 -0
  14. package/dist/components/macro/card-preset.d.ts.map +1 -0
  15. package/dist/components/macro/carousel-preset.d.ts +18 -0
  16. package/dist/components/macro/carousel-preset.d.ts.map +1 -0
  17. package/dist/components/macro/checkbox-preset.d.ts +14 -0
  18. package/dist/components/macro/checkbox-preset.d.ts.map +1 -0
  19. package/dist/components/macro/combobox-preset.d.ts +27 -0
  20. package/dist/components/macro/combobox-preset.d.ts.map +1 -0
  21. package/dist/components/macro/command-preset.d.ts +16 -0
  22. package/dist/components/macro/command-preset.d.ts.map +1 -0
  23. package/dist/components/macro/date-picker.d.ts +62 -0
  24. package/dist/components/macro/date-picker.d.ts.map +1 -0
  25. package/dist/components/macro/dialog-preset.d.ts +24 -0
  26. package/dist/components/macro/dialog-preset.d.ts.map +1 -0
  27. package/dist/components/macro/empty-preset.d.ts +15 -0
  28. package/dist/components/macro/empty-preset.d.ts.map +1 -0
  29. package/dist/components/macro/field-preset.d.ts +15 -0
  30. package/dist/components/macro/field-preset.d.ts.map +1 -0
  31. package/dist/components/macro/index.d.ts +22 -0
  32. package/dist/components/macro/index.d.ts.map +1 -0
  33. package/dist/components/macro/input-otp-preset.d.ts +13 -0
  34. package/dist/components/macro/input-otp-preset.d.ts.map +1 -0
  35. package/dist/components/macro/input-preset.d.ts +21 -0
  36. package/dist/components/macro/input-preset.d.ts.map +1 -0
  37. package/dist/components/macro/pagination-preset.d.ts +13 -0
  38. package/dist/components/macro/pagination-preset.d.ts.map +1 -0
  39. package/dist/components/macro/progress-preset.d.ts +10 -0
  40. package/dist/components/macro/progress-preset.d.ts.map +1 -0
  41. package/dist/components/macro/radio-group-preset.d.ts +22 -0
  42. package/dist/components/macro/radio-group-preset.d.ts.map +1 -0
  43. package/dist/components/macro/select-preset.d.ts +42 -0
  44. package/dist/components/macro/select-preset.d.ts.map +1 -0
  45. package/dist/components/macro/slider-preset.d.ts +12 -0
  46. package/dist/components/macro/slider-preset.d.ts.map +1 -0
  47. package/dist/components/macro/switch-preset.d.ts +12 -0
  48. package/dist/components/macro/switch-preset.d.ts.map +1 -0
  49. package/dist/components/macro/tabs-preset.d.ts +17 -0
  50. package/dist/components/macro/tabs-preset.d.ts.map +1 -0
  51. package/dist/components/macro/textarea-preset.d.ts +17 -0
  52. package/dist/components/macro/textarea-preset.d.ts.map +1 -0
  53. package/dist/components/micro/accordion.d.ts +8 -0
  54. package/dist/components/micro/accordion.d.ts.map +1 -0
  55. package/dist/components/micro/alert-dialog.d.ts +33 -0
  56. package/dist/components/micro/alert-dialog.d.ts.map +1 -0
  57. package/dist/components/micro/alert.d.ts +28 -0
  58. package/dist/components/micro/alert.d.ts.map +1 -0
  59. package/dist/components/micro/aspect-ratio.d.ts +13 -0
  60. package/dist/components/micro/aspect-ratio.d.ts.map +1 -0
  61. package/dist/components/micro/avatar.d.ts +23 -0
  62. package/dist/components/micro/avatar.d.ts.map +1 -0
  63. package/dist/components/micro/badge.d.ts +16 -0
  64. package/dist/components/micro/badge.d.ts.map +1 -0
  65. package/dist/components/micro/breadcrumb.d.ts +24 -0
  66. package/dist/components/micro/breadcrumb.d.ts.map +1 -0
  67. package/dist/components/micro/button.d.ts +39 -0
  68. package/dist/components/micro/button.d.ts.map +1 -0
  69. package/dist/components/micro/calendar.d.ts +28 -0
  70. package/dist/components/micro/calendar.d.ts.map +1 -0
  71. package/dist/components/micro/card.d.ts +28 -0
  72. package/dist/components/micro/card.d.ts.map +1 -0
  73. package/dist/components/micro/carousel.d.ts +29 -0
  74. package/dist/components/micro/carousel.d.ts.map +1 -0
  75. package/dist/components/micro/chart.d.ts +49 -0
  76. package/dist/components/micro/chart.d.ts.map +1 -0
  77. package/dist/components/micro/checkbox.d.ts +17 -0
  78. package/dist/components/micro/checkbox.d.ts.map +1 -0
  79. package/dist/components/micro/collapsible.d.ts +15 -0
  80. package/dist/components/micro/collapsible.d.ts.map +1 -0
  81. package/dist/components/micro/combobox.d.ts +33 -0
  82. package/dist/components/micro/combobox.d.ts.map +1 -0
  83. package/dist/components/micro/command.d.ts +79 -0
  84. package/dist/components/micro/command.d.ts.map +1 -0
  85. package/dist/components/micro/context-menu.d.ts +60 -0
  86. package/dist/components/micro/context-menu.d.ts.map +1 -0
  87. package/dist/components/micro/dialog.d.ts +14 -0
  88. package/dist/components/micro/dialog.d.ts.map +1 -0
  89. package/dist/components/micro/direction.d.ts +2 -0
  90. package/dist/components/micro/direction.d.ts.map +1 -0
  91. package/dist/components/micro/drawer.d.ts +17 -0
  92. package/dist/components/micro/drawer.d.ts.map +1 -0
  93. package/dist/components/micro/dropdown-menu.d.ts +37 -0
  94. package/dist/components/micro/dropdown-menu.d.ts.map +1 -0
  95. package/dist/components/micro/empty.d.ts +23 -0
  96. package/dist/components/micro/empty.d.ts.map +1 -0
  97. package/dist/components/micro/field.d.ts +29 -0
  98. package/dist/components/micro/field.d.ts.map +1 -0
  99. package/dist/components/micro/hover-card.d.ts +17 -0
  100. package/dist/components/micro/hover-card.d.ts.map +1 -0
  101. package/dist/components/micro/input-group.d.ts +17 -0
  102. package/dist/components/micro/input-group.d.ts.map +1 -0
  103. package/dist/components/micro/input-otp.d.ts +44 -0
  104. package/dist/components/micro/input-otp.d.ts.map +1 -0
  105. package/dist/components/micro/input.d.ts +22 -0
  106. package/dist/components/micro/input.d.ts.map +1 -0
  107. package/dist/components/micro/item.d.ts +31 -0
  108. package/dist/components/micro/item.d.ts.map +1 -0
  109. package/dist/components/micro/kbd.d.ts +9 -0
  110. package/dist/components/micro/kbd.d.ts.map +1 -0
  111. package/dist/components/micro/label.d.ts +15 -0
  112. package/dist/components/micro/label.d.ts.map +1 -0
  113. package/dist/components/micro/menubar.d.ts +41 -0
  114. package/dist/components/micro/menubar.d.ts.map +1 -0
  115. package/dist/components/micro/navigation-menu.d.ts +18 -0
  116. package/dist/components/micro/navigation-menu.d.ts.map +1 -0
  117. package/dist/components/micro/pagination.d.ts +33 -0
  118. package/dist/components/micro/pagination.d.ts.map +1 -0
  119. package/dist/components/micro/popover.d.ts +23 -0
  120. package/dist/components/micro/popover.d.ts.map +1 -0
  121. package/dist/components/micro/progress.d.ts +19 -0
  122. package/dist/components/micro/progress.d.ts.map +1 -0
  123. package/dist/components/micro/radio-group.d.ts +13 -0
  124. package/dist/components/micro/radio-group.d.ts.map +1 -0
  125. package/dist/components/micro/resizable.d.ts +12 -0
  126. package/dist/components/micro/resizable.d.ts.map +1 -0
  127. package/dist/components/micro/scroll-area.d.ts +12 -0
  128. package/dist/components/micro/scroll-area.d.ts.map +1 -0
  129. package/dist/components/micro/select.d.ts +21 -0
  130. package/dist/components/micro/select.d.ts.map +1 -0
  131. package/dist/components/micro/separator.d.ts +9 -0
  132. package/dist/components/micro/separator.d.ts.map +1 -0
  133. package/dist/components/micro/sheet.d.ts +17 -0
  134. package/dist/components/micro/sheet.d.ts.map +1 -0
  135. package/dist/components/micro/sidebar.d.ts +28 -0
  136. package/dist/components/micro/sidebar.d.ts.map +1 -0
  137. package/dist/components/micro/skeleton.d.ts +9 -0
  138. package/dist/components/micro/skeleton.d.ts.map +1 -0
  139. package/dist/components/micro/slider.d.ts +14 -0
  140. package/dist/components/micro/slider.d.ts.map +1 -0
  141. package/dist/components/micro/sonner.d.ts +5 -0
  142. package/dist/components/micro/sonner.d.ts.map +1 -0
  143. package/dist/components/micro/spinner.d.ts +21 -0
  144. package/dist/components/micro/spinner.d.ts.map +1 -0
  145. package/dist/components/micro/switch.d.ts +13 -0
  146. package/dist/components/micro/switch.d.ts.map +1 -0
  147. package/dist/components/micro/table.d.ts +17 -0
  148. package/dist/components/micro/table.d.ts.map +1 -0
  149. package/dist/components/micro/tabs.d.ts +13 -0
  150. package/dist/components/micro/tabs.d.ts.map +1 -0
  151. package/dist/components/micro/textarea.d.ts +17 -0
  152. package/dist/components/micro/textarea.d.ts.map +1 -0
  153. package/dist/components/micro/theme-provider.d.ts +76 -0
  154. package/dist/components/micro/theme-provider.d.ts.map +1 -0
  155. package/dist/components/micro/toggle-group.d.ts +22 -0
  156. package/dist/components/micro/toggle-group.d.ts.map +1 -0
  157. package/dist/components/micro/toggle.d.ts +16 -0
  158. package/dist/components/micro/toggle.d.ts.map +1 -0
  159. package/dist/components/micro/tooltip.d.ts +20 -0
  160. package/dist/components/micro/tooltip.d.ts.map +1 -0
  161. package/dist/dev/App.d.ts +3 -0
  162. package/dist/dev/App.d.ts.map +1 -0
  163. package/dist/dev/components/GladcnLogo.d.ts +10 -0
  164. package/dist/dev/components/GladcnLogo.d.ts.map +1 -0
  165. package/dist/dev/components/code-highlighter.d.ts +5 -0
  166. package/dist/dev/components/code-highlighter.d.ts.map +1 -0
  167. package/dist/dev/components/dev-context.d.ts +12 -0
  168. package/dist/dev/components/dev-context.d.ts.map +1 -0
  169. package/dist/dev/components/showcase.d.ts +72 -0
  170. package/dist/dev/components/showcase.d.ts.map +1 -0
  171. package/dist/dev/data.d.ts +68 -0
  172. package/dist/dev/data.d.ts.map +1 -0
  173. package/dist/dev/main.d.ts +1 -0
  174. package/dist/dev/main.d.ts.map +1 -0
  175. package/dist/dev/sections/buttons.d.ts +2 -0
  176. package/dist/dev/sections/buttons.d.ts.map +1 -0
  177. package/dist/dev/sections/display.d.ts +2 -0
  178. package/dist/dev/sections/display.d.ts.map +1 -0
  179. package/dist/dev/sections/feedback.d.ts +2 -0
  180. package/dist/dev/sections/feedback.d.ts.map +1 -0
  181. package/dist/dev/sections/forms.d.ts +2 -0
  182. package/dist/dev/sections/forms.d.ts.map +1 -0
  183. package/dist/dev/sections/interactive.d.ts +2 -0
  184. package/dist/dev/sections/interactive.d.ts.map +1 -0
  185. package/dist/dev/sections/overview.d.ts +2 -0
  186. package/dist/dev/sections/overview.d.ts.map +1 -0
  187. package/dist/dev/showcase/accordion.d.ts +2 -0
  188. package/dist/dev/showcase/accordion.d.ts.map +1 -0
  189. package/dist/dev/showcase/alert-dialog.d.ts +2 -0
  190. package/dist/dev/showcase/alert-dialog.d.ts.map +1 -0
  191. package/dist/dev/showcase/alert.d.ts +2 -0
  192. package/dist/dev/showcase/alert.d.ts.map +1 -0
  193. package/dist/dev/showcase/aspect-ratio.d.ts +2 -0
  194. package/dist/dev/showcase/aspect-ratio.d.ts.map +1 -0
  195. package/dist/dev/showcase/avatar.d.ts +2 -0
  196. package/dist/dev/showcase/avatar.d.ts.map +1 -0
  197. package/dist/dev/showcase/badge.d.ts +2 -0
  198. package/dist/dev/showcase/badge.d.ts.map +1 -0
  199. package/dist/dev/showcase/breadcrumb.d.ts +2 -0
  200. package/dist/dev/showcase/breadcrumb.d.ts.map +1 -0
  201. package/dist/dev/showcase/button.d.ts +2 -0
  202. package/dist/dev/showcase/button.d.ts.map +1 -0
  203. package/dist/dev/showcase/calendar.d.ts +2 -0
  204. package/dist/dev/showcase/calendar.d.ts.map +1 -0
  205. package/dist/dev/showcase/card.d.ts +2 -0
  206. package/dist/dev/showcase/card.d.ts.map +1 -0
  207. package/dist/dev/showcase/carousel.d.ts +2 -0
  208. package/dist/dev/showcase/carousel.d.ts.map +1 -0
  209. package/dist/dev/showcase/chart.d.ts +2 -0
  210. package/dist/dev/showcase/chart.d.ts.map +1 -0
  211. package/dist/dev/showcase/checkbox.d.ts +2 -0
  212. package/dist/dev/showcase/checkbox.d.ts.map +1 -0
  213. package/dist/dev/showcase/collapsible.d.ts +3 -0
  214. package/dist/dev/showcase/collapsible.d.ts.map +1 -0
  215. package/dist/dev/showcase/combobox.d.ts +2 -0
  216. package/dist/dev/showcase/combobox.d.ts.map +1 -0
  217. package/dist/dev/showcase/command.d.ts +2 -0
  218. package/dist/dev/showcase/command.d.ts.map +1 -0
  219. package/dist/dev/showcase/context-menu.d.ts +2 -0
  220. package/dist/dev/showcase/context-menu.d.ts.map +1 -0
  221. package/dist/dev/showcase/dialog.d.ts +2 -0
  222. package/dist/dev/showcase/dialog.d.ts.map +1 -0
  223. package/dist/dev/showcase/drawer.d.ts +2 -0
  224. package/dist/dev/showcase/drawer.d.ts.map +1 -0
  225. package/dist/dev/showcase/dropdown-menu.d.ts +2 -0
  226. package/dist/dev/showcase/dropdown-menu.d.ts.map +1 -0
  227. package/dist/dev/showcase/empty.d.ts +2 -0
  228. package/dist/dev/showcase/empty.d.ts.map +1 -0
  229. package/dist/dev/showcase/hover-card.d.ts +2 -0
  230. package/dist/dev/showcase/hover-card.d.ts.map +1 -0
  231. package/dist/dev/showcase/input-group.d.ts +2 -0
  232. package/dist/dev/showcase/input-group.d.ts.map +1 -0
  233. package/dist/dev/showcase/input-otp.d.ts +2 -0
  234. package/dist/dev/showcase/input-otp.d.ts.map +1 -0
  235. package/dist/dev/showcase/input.d.ts +2 -0
  236. package/dist/dev/showcase/input.d.ts.map +1 -0
  237. package/dist/dev/showcase/item.d.ts +2 -0
  238. package/dist/dev/showcase/item.d.ts.map +1 -0
  239. package/dist/dev/showcase/kbd.d.ts +2 -0
  240. package/dist/dev/showcase/kbd.d.ts.map +1 -0
  241. package/dist/dev/showcase/label.d.ts +2 -0
  242. package/dist/dev/showcase/label.d.ts.map +1 -0
  243. package/dist/dev/showcase/menubar.d.ts +2 -0
  244. package/dist/dev/showcase/menubar.d.ts.map +1 -0
  245. package/dist/dev/showcase/navigation-menu.d.ts +3 -0
  246. package/dist/dev/showcase/navigation-menu.d.ts.map +1 -0
  247. package/dist/dev/showcase/overview.d.ts +2 -0
  248. package/dist/dev/showcase/overview.d.ts.map +1 -0
  249. package/dist/dev/showcase/pagination.d.ts +2 -0
  250. package/dist/dev/showcase/pagination.d.ts.map +1 -0
  251. package/dist/dev/showcase/popover.d.ts +2 -0
  252. package/dist/dev/showcase/popover.d.ts.map +1 -0
  253. package/dist/dev/showcase/progress.d.ts +2 -0
  254. package/dist/dev/showcase/progress.d.ts.map +1 -0
  255. package/dist/dev/showcase/radio-group.d.ts +2 -0
  256. package/dist/dev/showcase/radio-group.d.ts.map +1 -0
  257. package/dist/dev/showcase/resizable.d.ts +2 -0
  258. package/dist/dev/showcase/resizable.d.ts.map +1 -0
  259. package/dist/dev/showcase/scroll-area.d.ts +2 -0
  260. package/dist/dev/showcase/scroll-area.d.ts.map +1 -0
  261. package/dist/dev/showcase/select.d.ts +2 -0
  262. package/dist/dev/showcase/select.d.ts.map +1 -0
  263. package/dist/dev/showcase/separator.d.ts +2 -0
  264. package/dist/dev/showcase/separator.d.ts.map +1 -0
  265. package/dist/dev/showcase/sheet.d.ts +2 -0
  266. package/dist/dev/showcase/sheet.d.ts.map +1 -0
  267. package/dist/dev/showcase/sidebar.d.ts +2 -0
  268. package/dist/dev/showcase/sidebar.d.ts.map +1 -0
  269. package/dist/dev/showcase/skeleton.d.ts +2 -0
  270. package/dist/dev/showcase/skeleton.d.ts.map +1 -0
  271. package/dist/dev/showcase/slider.d.ts +2 -0
  272. package/dist/dev/showcase/slider.d.ts.map +1 -0
  273. package/dist/dev/showcase/sonner.d.ts +2 -0
  274. package/dist/dev/showcase/sonner.d.ts.map +1 -0
  275. package/dist/dev/showcase/spinner.d.ts +2 -0
  276. package/dist/dev/showcase/spinner.d.ts.map +1 -0
  277. package/dist/dev/showcase/switch.d.ts +2 -0
  278. package/dist/dev/showcase/switch.d.ts.map +1 -0
  279. package/dist/dev/showcase/table.d.ts +2 -0
  280. package/dist/dev/showcase/table.d.ts.map +1 -0
  281. package/dist/dev/showcase/tabs.d.ts +2 -0
  282. package/dist/dev/showcase/tabs.d.ts.map +1 -0
  283. package/dist/dev/showcase/textarea.d.ts +2 -0
  284. package/dist/dev/showcase/textarea.d.ts.map +1 -0
  285. package/dist/dev/showcase/theme-provider.d.ts +2 -0
  286. package/dist/dev/showcase/theme-provider.d.ts.map +1 -0
  287. package/dist/dev/showcase/toast.d.ts +2 -0
  288. package/dist/dev/showcase/toast.d.ts.map +1 -0
  289. package/dist/dev/showcase/toggle-group.d.ts +2 -0
  290. package/dist/dev/showcase/toggle-group.d.ts.map +1 -0
  291. package/dist/dev/showcase/toggle.d.ts +2 -0
  292. package/dist/dev/showcase/toggle.d.ts.map +1 -0
  293. package/dist/dev/showcase/tooltip.d.ts +2 -0
  294. package/dist/dev/showcase/tooltip.d.ts.map +1 -0
  295. package/dist/hooks/use-controllable-state.d.ts +14 -0
  296. package/dist/hooks/use-controllable-state.d.ts.map +1 -0
  297. package/dist/hooks/use-mobile.d.ts +2 -0
  298. package/dist/hooks/use-mobile.d.ts.map +1 -0
  299. package/dist/index.cjs +5099 -0
  300. package/dist/index.cjs.map +1 -0
  301. package/dist/index.d.ts +58 -0
  302. package/dist/index.d.ts.map +1 -0
  303. package/dist/index.js +4740 -0
  304. package/dist/index.js.map +1 -0
  305. package/dist/lib/types.d.ts +4 -0
  306. package/dist/lib/types.d.ts.map +1 -0
  307. package/dist/lib/utils.d.ts +3 -0
  308. package/dist/lib/utils.d.ts.map +1 -0
  309. package/dist/preset.d.ts +18 -0
  310. package/dist/preset.d.ts.map +1 -0
  311. package/dist/test/setup.d.ts +1 -0
  312. package/dist/test/setup.d.ts.map +1 -0
  313. package/llms.txt +74 -0
  314. package/package.json +117 -0
  315. package/scripts/add-icononly-prop.ts +84 -0
  316. package/scripts/audit-best-practices.ts +114 -0
  317. package/scripts/audit-showcase.ts +262 -0
  318. package/scripts/audit-showcases.ts +83 -0
  319. package/scripts/clean-imports.ts +21 -0
  320. package/scripts/count_missing.ts +24 -0
  321. package/scripts/find-css-vars.ts +62 -0
  322. package/scripts/fix-portals.ts +90 -0
  323. package/scripts/full-audit.ts +89 -0
  324. package/scripts/merge-components.cjs +65 -0
  325. package/scripts/merge-components.js +64 -0
  326. package/scripts/optimize-barrel.ts +116 -0
  327. package/scripts/postinstall.cjs +228 -0
  328. package/scripts/refactor_divs.ts +39 -0
  329. package/scripts/remove_div_wrappers.ts +17 -0
  330. package/scripts/update-gaps.ts +83 -0
  331. package/scripts/update_showcase_codestrings.ts +87 -0
  332. package/src/components/macro/accordion-preset.test.tsx +37 -0
  333. package/src/components/macro/accordion-preset.tsx +41 -0
  334. package/src/components/macro/alert-dialog-preset.tsx +116 -0
  335. package/src/components/macro/alert-preset.tsx +94 -0
  336. package/src/components/macro/avatar-preset.tsx +54 -0
  337. package/src/components/macro/breadcrumb-preset.tsx +50 -0
  338. package/src/components/macro/card-preset.tsx +44 -0
  339. package/src/components/macro/carousel-preset.tsx +150 -0
  340. package/src/components/macro/checkbox-preset.test.tsx +49 -0
  341. package/src/components/macro/checkbox-preset.tsx +84 -0
  342. package/src/components/macro/combobox-preset.tsx +171 -0
  343. package/src/components/macro/command-preset.tsx +74 -0
  344. package/src/components/macro/date-picker.tsx +289 -0
  345. package/src/components/macro/dialog-preset.tsx +105 -0
  346. package/src/components/macro/empty-preset.tsx +46 -0
  347. package/src/components/macro/field-preset.test.tsx +69 -0
  348. package/src/components/macro/field-preset.tsx +65 -0
  349. package/src/components/macro/index.ts +21 -0
  350. package/src/components/macro/input-otp-preset.tsx +98 -0
  351. package/src/components/macro/input-preset.test.tsx +52 -0
  352. package/src/components/macro/input-preset.tsx +139 -0
  353. package/src/components/macro/pagination-preset.tsx +153 -0
  354. package/src/components/macro/progress-preset.test.tsx +65 -0
  355. package/src/components/macro/progress-preset.tsx +45 -0
  356. package/src/components/macro/radio-group-preset.tsx +115 -0
  357. package/src/components/macro/select-preset.tsx +168 -0
  358. package/src/components/macro/slider-preset.test.tsx +43 -0
  359. package/src/components/macro/slider-preset.tsx +86 -0
  360. package/src/components/macro/switch-preset.test.tsx +53 -0
  361. package/src/components/macro/switch-preset.tsx +70 -0
  362. package/src/components/macro/tabs-preset.test.tsx +31 -0
  363. package/src/components/macro/tabs-preset.tsx +51 -0
  364. package/src/components/macro/textarea-preset.test.tsx +39 -0
  365. package/src/components/macro/textarea-preset.tsx +55 -0
  366. package/src/components/micro/accordion.tsx +76 -0
  367. package/src/components/micro/alert-dialog.tsx +213 -0
  368. package/src/components/micro/alert.tsx +146 -0
  369. package/src/components/micro/aspect-ratio.tsx +38 -0
  370. package/src/components/micro/avatar.tsx +143 -0
  371. package/src/components/micro/badge.tsx +207 -0
  372. package/src/components/micro/breadcrumb.tsx +162 -0
  373. package/src/components/micro/button.test.tsx +25 -0
  374. package/src/components/micro/button.tsx +361 -0
  375. package/src/components/micro/calendar.tsx +383 -0
  376. package/src/components/micro/card.tsx +112 -0
  377. package/src/components/micro/carousel.tsx +341 -0
  378. package/src/components/micro/chart.tsx +392 -0
  379. package/src/components/micro/checkbox.tsx +74 -0
  380. package/src/components/micro/collapsible.tsx +65 -0
  381. package/src/components/micro/combobox.tsx +349 -0
  382. package/src/components/micro/command.tsx +238 -0
  383. package/src/components/micro/context-menu.tsx +285 -0
  384. package/src/components/micro/dialog.tsx +143 -0
  385. package/src/components/micro/direction.tsx +13 -0
  386. package/src/components/micro/drawer.tsx +149 -0
  387. package/src/components/micro/dropdown-menu.tsx +305 -0
  388. package/src/components/micro/empty.tsx +144 -0
  389. package/src/components/micro/field.tsx +311 -0
  390. package/src/components/micro/hover-card.tsx +86 -0
  391. package/src/components/micro/input-group.tsx +269 -0
  392. package/src/components/micro/input-otp.tsx +130 -0
  393. package/src/components/micro/input.tsx +56 -0
  394. package/src/components/micro/item.tsx +206 -0
  395. package/src/components/micro/kbd.tsx +38 -0
  396. package/src/components/micro/label.tsx +48 -0
  397. package/src/components/micro/menubar.tsx +302 -0
  398. package/src/components/micro/navigation-menu.tsx +197 -0
  399. package/src/components/micro/pagination.tsx +162 -0
  400. package/src/components/micro/popover.tsx +131 -0
  401. package/src/components/micro/progress.tsx +144 -0
  402. package/src/components/micro/radio-group.tsx +73 -0
  403. package/src/components/micro/resizable.tsx +64 -0
  404. package/src/components/micro/scroll-area.tsx +68 -0
  405. package/src/components/micro/select.tsx +234 -0
  406. package/src/components/micro/separator.tsx +37 -0
  407. package/src/components/micro/sheet.tsx +144 -0
  408. package/src/components/micro/sidebar.tsx +238 -0
  409. package/src/components/micro/skeleton.tsx +29 -0
  410. package/src/components/micro/slider.tsx +135 -0
  411. package/src/components/micro/sonner.tsx +85 -0
  412. package/src/components/micro/spinner.tsx +44 -0
  413. package/src/components/micro/switch.tsx +70 -0
  414. package/src/components/micro/table.tsx +210 -0
  415. package/src/components/micro/tabs.tsx +100 -0
  416. package/src/components/micro/textarea.tsx +53 -0
  417. package/src/components/micro/theme-provider.tsx +163 -0
  418. package/src/components/micro/toggle-group.tsx +121 -0
  419. package/src/components/micro/toggle.tsx +55 -0
  420. package/src/components/micro/tooltip.tsx +105 -0
  421. package/src/dev/App.tsx +322 -0
  422. package/src/dev/_debug_select.html +29 -0
  423. package/src/dev/components/GladcnLogo.tsx +80 -0
  424. package/src/dev/components/code-highlighter.tsx +67 -0
  425. package/src/dev/components/dev-context.tsx +31 -0
  426. package/src/dev/components/showcase.tsx +413 -0
  427. package/src/dev/data.ts +341 -0
  428. package/src/dev/index.css +55 -0
  429. package/src/dev/main.tsx +30 -0
  430. package/src/dev/sections/buttons.tsx +303 -0
  431. package/src/dev/sections/display.tsx +270 -0
  432. package/src/dev/sections/feedback.tsx +371 -0
  433. package/src/dev/sections/forms.tsx +652 -0
  434. package/src/dev/sections/interactive.tsx +51 -0
  435. package/src/dev/sections/overview.tsx +396 -0
  436. package/src/dev/showcase/accordion.tsx +947 -0
  437. package/src/dev/showcase/alert-dialog.tsx +1056 -0
  438. package/src/dev/showcase/alert.tsx +756 -0
  439. package/src/dev/showcase/aspect-ratio.tsx +339 -0
  440. package/src/dev/showcase/avatar.tsx +470 -0
  441. package/src/dev/showcase/badge.tsx +227 -0
  442. package/src/dev/showcase/breadcrumb.tsx +269 -0
  443. package/src/dev/showcase/button.tsx +309 -0
  444. package/src/dev/showcase/calendar.tsx +380 -0
  445. package/src/dev/showcase/card.tsx +448 -0
  446. package/src/dev/showcase/carousel.tsx +562 -0
  447. package/src/dev/showcase/chart.tsx +130 -0
  448. package/src/dev/showcase/checkbox.tsx +611 -0
  449. package/src/dev/showcase/collapsible.tsx +298 -0
  450. package/src/dev/showcase/combobox.tsx +777 -0
  451. package/src/dev/showcase/command.tsx +245 -0
  452. package/src/dev/showcase/context-menu.tsx +239 -0
  453. package/src/dev/showcase/dialog.tsx +1141 -0
  454. package/src/dev/showcase/drawer.tsx +392 -0
  455. package/src/dev/showcase/dropdown-menu.tsx +420 -0
  456. package/src/dev/showcase/empty.tsx +493 -0
  457. package/src/dev/showcase/hover-card.tsx +336 -0
  458. package/src/dev/showcase/input-group.tsx +511 -0
  459. package/src/dev/showcase/input-otp.tsx +404 -0
  460. package/src/dev/showcase/input.tsx +530 -0
  461. package/src/dev/showcase/item.tsx +295 -0
  462. package/src/dev/showcase/kbd.tsx +276 -0
  463. package/src/dev/showcase/label.tsx +121 -0
  464. package/src/dev/showcase/menubar.tsx +392 -0
  465. package/src/dev/showcase/navigation-menu.tsx +304 -0
  466. package/src/dev/showcase/overview.tsx +1106 -0
  467. package/src/dev/showcase/pagination.tsx +312 -0
  468. package/src/dev/showcase/popover.tsx +374 -0
  469. package/src/dev/showcase/progress.tsx +299 -0
  470. package/src/dev/showcase/radio-group.tsx +612 -0
  471. package/src/dev/showcase/resizable.tsx +237 -0
  472. package/src/dev/showcase/scroll-area.tsx +157 -0
  473. package/src/dev/showcase/select.tsx +628 -0
  474. package/src/dev/showcase/separator.tsx +126 -0
  475. package/src/dev/showcase/sheet.tsx +566 -0
  476. package/src/dev/showcase/sidebar.tsx +120 -0
  477. package/src/dev/showcase/skeleton.tsx +213 -0
  478. package/src/dev/showcase/slider.tsx +506 -0
  479. package/src/dev/showcase/sonner.tsx +376 -0
  480. package/src/dev/showcase/spinner.tsx +124 -0
  481. package/src/dev/showcase/switch.tsx +602 -0
  482. package/src/dev/showcase/table.tsx +196 -0
  483. package/src/dev/showcase/tabs.tsx +662 -0
  484. package/src/dev/showcase/textarea.tsx +498 -0
  485. package/src/dev/showcase/theme-provider.tsx +894 -0
  486. package/src/dev/showcase/toast.tsx +119 -0
  487. package/src/dev/showcase/toggle-group.tsx +223 -0
  488. package/src/dev/showcase/toggle.tsx +171 -0
  489. package/src/dev/showcase/tooltip.tsx +372 -0
  490. package/src/hooks/use-controllable-state.ts +87 -0
  491. package/src/hooks/use-mobile.ts +21 -0
  492. package/src/index.ts +62 -0
  493. package/src/lib/types.ts +14 -0
  494. package/src/lib/utils.test.ts +14 -0
  495. package/src/lib/utils.ts +6 -0
  496. package/src/preset.ts +17 -0
  497. package/src/styles/gladcn.css +116 -0
  498. package/src/styles/tokens.css +213 -0
  499. package/src/test/setup.ts +8 -0
  500. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,894 @@
1
+ import {
2
+ Tooltip,
3
+ TooltipContent,
4
+ TooltipPortal,
5
+ TooltipProvider,
6
+ TooltipTrigger,
7
+ } from "../../components/micro/tooltip";
8
+ import { MoonIcon, SunIcon } from "lucide-react";
9
+ import { useState } from "react";
10
+
11
+ import { Button } from "../../components/micro/button";
12
+ import {
13
+ Dialog,
14
+ DialogContent,
15
+ DialogDescription,
16
+ DialogHeader,
17
+ DialogPortal,
18
+ DialogTitle,
19
+ DialogTrigger,
20
+ } from "../../components/micro/dialog";
21
+ import {
22
+ DropdownMenu,
23
+ DropdownMenuContent,
24
+ DropdownMenuGroup,
25
+ DropdownMenuItem,
26
+ DropdownMenuLabel,
27
+ DropdownMenuPortal,
28
+ DropdownMenuSeparator,
29
+ DropdownMenuTrigger,
30
+ } from "../../components/micro/dropdown-menu";
31
+ import {
32
+ Popover,
33
+ PopoverContent,
34
+ PopoverPortal,
35
+ PopoverTrigger,
36
+ } from "../../components/micro/popover";
37
+ import {
38
+ Select,
39
+ SelectContent,
40
+ SelectItem,
41
+ SelectPortal,
42
+ SelectTrigger,
43
+ SelectValue,
44
+ } from "../../components/micro/select";
45
+ import {
46
+ Sheet,
47
+ SheetContent,
48
+ SheetHeader,
49
+ SheetPortal,
50
+ SheetTitle,
51
+ SheetTrigger,
52
+ } from "../../components/micro/sheet";
53
+
54
+
55
+ import {
56
+ ThemeProvider,
57
+ ThemeWrapper,
58
+ useTheme,
59
+ type ThemeMode,
60
+ } from "../../components/micro/theme-provider";
61
+ import {
62
+ DocsCode,
63
+ DocsH3,
64
+ DocsP,
65
+ ExampleGrid,
66
+ ExampleSection,
67
+ SectionHeader,
68
+ Showcase,
69
+ ShowcaseDocs,
70
+ } from "../../dev/components/showcase";
71
+
72
+ // ──────────────────────────────────────────────────────────
73
+ // Demo helpers
74
+ // ──────────────────────────────────────────────────────────
75
+
76
+ /** Demo card that reads theme from context and shows current mode */
77
+ function ThemeAwareCard({ readOnly }: { readOnly?: boolean } = {}) {
78
+ const theme = useTheme();
79
+
80
+ return (
81
+ <div className="rounded-xl border border-border bg-card text-card-foreground p-4 space-y-3">
82
+ <div className="flex items-center justify-between">
83
+ <span className="text-sm font-medium">Thẻ nội dung</span>
84
+ <span className="text-xs px-2 py-0.5 rounded-full bg-muted text-muted-foreground">
85
+ {theme?.mode ?? "unknown"}
86
+ </span>
87
+ </div>
88
+ <p className="text-xs text-muted-foreground">
89
+ Nền, chữ và viền được điều khiển bởi CSS variable từ ThemeProvider cha.
90
+ </p>
91
+ {!readOnly && (
92
+ <Button
93
+ variant="ghost"
94
+ size="sm"
95
+ iconOnly
96
+ aria-label={theme?.mode === "dark" ? "Chuyển sang Light" : "Chuyển sang Dark"}
97
+ onClick={() => theme?.setMode(theme.mode === "dark" ? "light" : "dark")}
98
+ >
99
+ {theme?.mode === "dark" ? (
100
+ <SunIcon aria-hidden="true" />
101
+ ) : (
102
+ <MoonIcon aria-hidden="true" />
103
+ )}
104
+ </Button>
105
+ )}
106
+ </div>
107
+ );
108
+ }
109
+
110
+ // ──────────────────────────────────────────────────────────
111
+ // SECTION 1: Micro Showcase
112
+ // ──────────────────────────────────────────────────────────
113
+ function ThemeProviderMicroShowcase() {
114
+ // Controlled demo state (owned by this parent)
115
+ const [controlledMode, setControlledMode] = useState<ThemeMode>("light");
116
+
117
+ return (
118
+ <div className="space-y-10">
119
+ <SectionHeader
120
+ title="ThemeProvider"
121
+ description="Context provider điều phối dark/light mode cho toàn bộ component tree. Hỗ trợ hai chế độ: Uncontrolled (tự quản lý state) và Controlled (state do component cha sở hữu). Dùng ThemeWrapper để tunnel theme qua Portal boundary."
122
+ />
123
+
124
+ {/* ── Uncontrolled ── */}
125
+ <SectionHeader
126
+ title="Uncontrolled"
127
+ description="Dùng khi component tự quản lý theme — không cần chia sẻ state với component khác. Thích hợp cho: scoped dark sections, isolated previews, hoặc khi không cần sync với localStorage/system."
128
+ />
129
+ <ExampleSection
130
+ label="defaultMode"
131
+ description="ThemeProvider giữ state nội bộ. Bất kỳ component con nào đều có thể sử dụng hook `useTheme()` để đọc hoặc cập nhật mode."
132
+ codeString={`function ThemeAwareCard() {
133
+ const theme = useTheme();
134
+
135
+ return (
136
+ <div className="rounded-xl border border-border bg-card text-card-foreground p-4 space-y-3">
137
+ <div className="flex items-center justify-between">
138
+ <span className="text-sm font-medium">Thẻ nội dung</span>
139
+ <span className="text-xs px-2 py-0.5 rounded-full bg-muted text-muted-foreground">
140
+ {theme?.mode}
141
+ </span>
142
+ </div>
143
+ <button onClick={() => theme?.setMode(theme.mode === "dark" ? "light" : "dark")}>
144
+ Chuyển sang {theme?.mode === "dark" ? "Light" : "Dark"}
145
+ </button>
146
+ </div>
147
+ );
148
+ }
149
+
150
+ <ThemeProvider defaultMode="light">
151
+ <ThemeAwareCard />
152
+ </ThemeProvider>`}
153
+ >
154
+ <ThemeProvider defaultMode="light">
155
+ <ThemeAwareCard />
156
+ </ThemeProvider>
157
+ </ExampleSection>
158
+
159
+ {/* ── Controlled ── */}
160
+ <SectionHeader
161
+ title="Controlled"
162
+ description="Dùng khi theme cần được đồng bộ với state bên ngoài — ví dụ: Macro component đọc từ localStorage, system settings, hoặc API. Component cha hoàn toàn sở hữu state, ThemeProvider chỉ đóng vai trò hiển thị (dumb component)."
163
+ />
164
+ <ExampleSection
165
+ label="mode + onModeChange"
166
+ description="State hoàn toàn được quản lý bởi component cha. ThemeProvider chỉ nhận prop mode và cập nhật UI khi cha thay đổi state."
167
+ codeString={`const [mode, setMode] = useState<ThemeMode>("light");
168
+
169
+ <div className="space-y-3 w-full">
170
+ {/* Toggle nằm ở component cha — simulates Macro owning state */}
171
+ <div className="flex items-center gap-3 p-3 rounded-lg bg-muted/50 border border-border">
172
+ <span className="text-xs text-muted-foreground flex-1">
173
+ State ở component cha: <strong>{mode}</strong>
174
+ </span>
175
+ <Button
176
+ size="sm"
177
+ variant="outline"
178
+ onClick={() => setMode(mode === "dark" ? "light" : "dark")}
179
+ >
180
+ Toggle từ ngoài
181
+ </Button>
182
+ </div>
183
+
184
+ <ThemeProvider mode={mode} onModeChange={setMode}>
185
+ {/* ThemeAwareCard là component đọc useTheme() để hiển thị mode hiện tại */}
186
+ <ThemeAwareCard readOnly />
187
+ </ThemeProvider>
188
+ </div>`}
189
+ >
190
+ <div className="space-y-3 w-full">
191
+ {/* External toggle (simulates Macro owning state) */}
192
+ <div className="flex items-center gap-3 p-3 rounded-lg bg-muted/50 border border-border">
193
+ <span className="text-xs text-muted-foreground flex-1">
194
+ State ở component cha: <DocsCode>{controlledMode}</DocsCode>
195
+ </span>
196
+ <Button
197
+ size="sm"
198
+ variant="outline"
199
+ onClick={() =>
200
+ setControlledMode(controlledMode === "dark" ? "light" : "dark")
201
+ }
202
+ >
203
+ {controlledMode === "dark" ? (
204
+ <SunIcon aria-hidden="true" />
205
+ ) : (
206
+ <MoonIcon aria-hidden="true" />
207
+ )}
208
+ {controlledMode === "dark" ? "Light" : "Dark"}
209
+ </Button>
210
+ </div>
211
+
212
+ <ThemeProvider mode={controlledMode} onModeChange={setControlledMode}>
213
+ <ThemeAwareCard readOnly />
214
+ </ThemeProvider>
215
+ </div>
216
+ </ExampleSection>
217
+
218
+ {/* ── ThemeWrapper & Portal Tunnels ── */}
219
+ <div className="pt-16 pb-6">
220
+ <SectionHeader
221
+ title="ThemeWrapper & Portal Tunnels"
222
+ description="ThemeWrapper tunnel theme class qua Portal boundary (Dialog, Tooltip, Popover...) bằng cách re-apply CSS variable vào container mới. Dưới đây là so sánh trực tiếp giữa portal không có và có ThemeWrapper khi theme cục bộ (ThemeProvider cha) đang ở Dark Mode."
223
+ />
224
+ </div>
225
+
226
+ {/* Tooltip */}
227
+ <ExampleSection
228
+ fullWidth
229
+ label="Tooltip"
230
+ description="Tooltip được định vị gần trigger và hiển thị khi hover. Không có ThemeWrapper, tooltip của bạn sẽ trắng toát giữa một dark section — dười đây là bằng chứng trực quan."
231
+ codeString={`<ThemeProvider defaultMode="dark">
232
+ <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
233
+ <div className="flex-1 flex flex-col items-center gap-4">
234
+ <span className="text-sm font-medium text-muted-foreground">
235
+ Không dùng ThemeWrapper
236
+ </span>
237
+ <TooltipProvider>
238
+ <Tooltip>
239
+ <TooltipTrigger
240
+ render={<Button variant="solid">Hover tôi</Button>}
241
+ />
242
+ <TooltipPortal>
243
+ <TooltipContent
244
+ sideOffset={8}
245
+ className="bg-card text-card-foreground border border-border shadow-md"
246
+ >
247
+ Trắng toát! Lạc quẻ với nền đen.
248
+ </TooltipContent>
249
+ </TooltipPortal>
250
+ </Tooltip>
251
+ </TooltipProvider>
252
+ </div>
253
+
254
+ <div className="flex-1 flex flex-col items-center gap-4">
255
+ <span className="text-sm font-medium text-muted-foreground">
256
+ Có ThemeWrapper
257
+ </span>
258
+ <TooltipProvider>
259
+ <Tooltip>
260
+ <TooltipTrigger
261
+ render={<Button variant="solid">Hover tôi</Button>}
262
+ />
263
+ <TooltipPortal>
264
+ <ThemeWrapper>
265
+ <TooltipContent
266
+ sideOffset={8}
267
+ className="bg-card text-card-foreground border border-border shadow-md"
268
+ >
269
+ Màu Tối! Đồng bộ với provider cha.
270
+ </TooltipContent>
271
+ </ThemeWrapper>
272
+ </TooltipPortal>
273
+ </Tooltip>
274
+ </TooltipProvider>
275
+ </div>
276
+ </div>
277
+ </ThemeProvider>`}
278
+ >
279
+ <ThemeProvider defaultMode="dark">
280
+ <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
281
+ <div className="flex-1 flex flex-col items-center gap-4">
282
+ <span className="text-sm font-medium text-muted-foreground">
283
+ Không dùng ThemeWrapper
284
+ </span>
285
+ <TooltipProvider>
286
+ <Tooltip>
287
+ <TooltipTrigger
288
+ render={<Button variant="solid">Hover tôi</Button>}
289
+ />
290
+ <TooltipPortal>
291
+ <TooltipContent
292
+ sideOffset={8}
293
+ className="bg-card text-card-foreground border border-border shadow-md"
294
+ >
295
+ Trắng toát! Lạc quẻ với nền đen.
296
+ </TooltipContent>
297
+ </TooltipPortal>
298
+ </Tooltip>
299
+ </TooltipProvider>
300
+ </div>
301
+
302
+ <div className="flex-1 flex flex-col items-center gap-4">
303
+ <span className="text-sm font-medium text-muted-foreground">
304
+ Có ThemeWrapper
305
+ </span>
306
+ <TooltipProvider>
307
+ <Tooltip>
308
+ <TooltipTrigger
309
+ render={<Button variant="solid">Hover tôi</Button>}
310
+ />
311
+ <TooltipPortal>
312
+ <ThemeWrapper>
313
+ <TooltipContent
314
+ sideOffset={8}
315
+ className="bg-card text-card-foreground border border-border shadow-md"
316
+ >
317
+ Màu Tối! Đồng bộ với provider cha.
318
+ </TooltipContent>
319
+ </ThemeWrapper>
320
+ </TooltipPortal>
321
+ </Tooltip>
322
+ </TooltipProvider>
323
+ </div>
324
+ </div>
325
+ </ThemeProvider>
326
+ </ExampleSection>
327
+
328
+ {/* Popover */}
329
+ <ExampleSection
330
+ fullWidth
331
+ label="Popover"
332
+ description="Popover chứa content phức tạp và mở khi click. Vì portal hướng tới `document.body`, toàn bộ nền popover lấy CSS variable từ root — sẽ trắng hoàn toàn dù trigger đang nằm trong dark section."
333
+ codeString={`<ThemeProvider defaultMode="dark">
334
+ <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
335
+ <div className="flex-1 flex flex-col items-center gap-4">
336
+ <span className="text-sm font-medium text-muted-foreground">
337
+ Không dùng ThemeWrapper
338
+ </span>
339
+ <Popover>
340
+ <PopoverTrigger
341
+ render={<Button variant="outline">Click Popover</Button>}
342
+ />
343
+ <PopoverPortal>
344
+ <PopoverContent className="w-64">
345
+ <div className="space-y-2">
346
+ <h4 className="font-medium leading-none">
347
+ Nội dung Popover
348
+ </h4>
349
+ <p className="text-sm text-muted-foreground">
350
+ Lỗi: Hiển thị giao diện sáng.
351
+ </p>
352
+ </div>
353
+ </PopoverContent>
354
+ </PopoverPortal>
355
+ </Popover>
356
+ </div>
357
+
358
+ <div className="flex-1 flex flex-col items-center gap-4">
359
+ <span className="text-sm font-medium text-muted-foreground">
360
+ Có ThemeWrapper
361
+ </span>
362
+ <Popover>
363
+ <PopoverTrigger
364
+ render={<Button variant="outline">Click Popover</Button>}
365
+ />
366
+ <PopoverPortal>
367
+ <ThemeWrapper>
368
+ <PopoverContent className="w-64">
369
+ <div className="space-y-2">
370
+ <h4 className="font-medium leading-none">
371
+ Nội dung Popover
372
+ </h4>
373
+ <p className="text-sm text-muted-foreground">
374
+ Lớp Portal Tunnel đã hoạt động chính xác.
375
+ </p>
376
+ </div>
377
+ </PopoverContent>
378
+ </ThemeWrapper>
379
+ </PopoverPortal>
380
+ </Popover>
381
+ </div>
382
+ </div>
383
+ </ThemeProvider>`}
384
+ >
385
+ <ThemeProvider defaultMode="dark">
386
+ <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
387
+ <div className="flex-1 flex flex-col items-center gap-4">
388
+ <span className="text-sm font-medium text-muted-foreground">
389
+ Không dùng ThemeWrapper
390
+ </span>
391
+ <Popover>
392
+ <PopoverTrigger
393
+ render={<Button variant="outline">Click Popover</Button>}
394
+ />
395
+ <PopoverPortal>
396
+ <PopoverContent className="w-64">
397
+ <div className="space-y-2">
398
+ <h4 className="font-medium leading-none">
399
+ Nội dung Popover
400
+ </h4>
401
+ <p className="text-sm text-muted-foreground">
402
+ Lỗi: Hiển thị giao diện sáng.
403
+ </p>
404
+ </div>
405
+ </PopoverContent>
406
+ </PopoverPortal>
407
+ </Popover>
408
+ </div>
409
+
410
+ <div className="flex-1 flex flex-col items-center gap-4">
411
+ <span className="text-sm font-medium text-muted-foreground">
412
+ Có ThemeWrapper
413
+ </span>
414
+ <Popover>
415
+ <PopoverTrigger
416
+ render={<Button variant="outline">Click Popover</Button>}
417
+ />
418
+ <PopoverPortal>
419
+ <ThemeWrapper>
420
+ <PopoverContent className="w-64">
421
+ <div className="space-y-2">
422
+ <h4 className="font-medium leading-none">
423
+ Nội dung Popover
424
+ </h4>
425
+ <p className="text-sm text-muted-foreground">
426
+ Lớp Portal Tunnel đã hoạt động chính xác.
427
+ </p>
428
+ </div>
429
+ </PopoverContent>
430
+ </ThemeWrapper>
431
+ </PopoverPortal>
432
+ </Popover>
433
+ </div>
434
+ </div>
435
+ </ThemeProvider>
436
+ </ExampleSection>
437
+
438
+ {/* Select */}
439
+ <ExampleSection
440
+ fullWidth
441
+ label="Select"
442
+ description="SelectContent được render tách biệt hoàn toàn khỏi trigger. Thiếu ThemeWrapper, dropdown option sẽ trắng xuộa trong khi trigger vẫn tối — mù quáng và khó đọc."
443
+ codeString={`<ThemeProvider defaultMode="dark">
444
+ <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
445
+ <div className="flex-1 flex flex-col items-center gap-4">
446
+ <span className="text-sm font-medium text-muted-foreground">
447
+ Không dùng ThemeWrapper
448
+ </span>
449
+ <Select items={{ next: "Next.js", vite: "Vite", remix: "Remix" }}>
450
+ <SelectTrigger className="w-[140px]">
451
+ <SelectValue placeholder="Chọn framework..." />
452
+ </SelectTrigger>
453
+ <SelectPortal>
454
+ <SelectContent>
455
+ <SelectItem value="next">Next.js</SelectItem>
456
+ <SelectItem value="vite">Vite</SelectItem>
457
+ <SelectItem value="remix">Remix</SelectItem>
458
+ </SelectContent>
459
+ </SelectPortal>
460
+ </Select>
461
+ </div>
462
+
463
+ <div className="flex-1 flex flex-col items-center gap-4">
464
+ <span className="text-sm font-medium text-muted-foreground">
465
+ Có ThemeWrapper
466
+ </span>
467
+ <Select items={{ next: "Next.js", vite: "Vite", remix: "Remix" }}>
468
+ <SelectTrigger className="w-[140px]">
469
+ <SelectValue placeholder="Chọn framework..." />
470
+ </SelectTrigger>
471
+ <SelectPortal>
472
+ <ThemeWrapper>
473
+ <SelectContent>
474
+ <SelectItem value="next">Next.js</SelectItem>
475
+ <SelectItem value="vite">Vite</SelectItem>
476
+ <SelectItem value="remix">Remix</SelectItem>
477
+ </SelectContent>
478
+ </ThemeWrapper>
479
+ </SelectPortal>
480
+ </Select>
481
+ </div>
482
+ </div>
483
+ </ThemeProvider>`}
484
+ >
485
+ <ThemeProvider defaultMode="dark">
486
+ <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
487
+ <div className="flex-1 flex flex-col items-center gap-4">
488
+ <span className="text-sm font-medium text-muted-foreground">
489
+ Không dùng ThemeWrapper
490
+ </span>
491
+ <Select items={{ next: "Next.js", vite: "Vite", remix: "Remix" }}>
492
+ <SelectTrigger className="w-[140px]">
493
+ <SelectValue placeholder="Chọn framework..." />
494
+ </SelectTrigger>
495
+ <SelectPortal>
496
+ <SelectContent>
497
+ <SelectItem value="next">Next.js</SelectItem>
498
+ <SelectItem value="vite">Vite</SelectItem>
499
+ <SelectItem value="remix">Remix</SelectItem>
500
+ </SelectContent>
501
+ </SelectPortal>
502
+ </Select>
503
+ </div>
504
+
505
+ <div className="flex-1 flex flex-col items-center gap-4">
506
+ <span className="text-sm font-medium text-muted-foreground">
507
+ Có ThemeWrapper
508
+ </span>
509
+ <Select items={{ next: "Next.js", vite: "Vite", remix: "Remix" }}>
510
+ <SelectTrigger className="w-[140px]">
511
+ <SelectValue placeholder="Chọn framework..." />
512
+ </SelectTrigger>
513
+ <SelectPortal>
514
+ <ThemeWrapper>
515
+ <SelectContent>
516
+ <SelectItem value="next">Next.js</SelectItem>
517
+ <SelectItem value="vite">Vite</SelectItem>
518
+ <SelectItem value="remix">Remix</SelectItem>
519
+ </SelectContent>
520
+ </ThemeWrapper>
521
+ </SelectPortal>
522
+ </Select>
523
+ </div>
524
+ </div>
525
+ </ThemeProvider>
526
+ </ExampleSection>
527
+
528
+ {/* DropdownMenu */}
529
+ <ExampleSection
530
+ fullWidth
531
+ label="DropdownMenu"
532
+ description="DropdownMenu thường dùng cho navigation actions (profile, settings, logout). Khi hover qua từng item, màu highlight cũng lấy từ CSS variable — thiếu ThemeWrapper, cả nền menu lẫn trạng thái hover cùng lúc đều hiển thị sai màu."
533
+ codeString={`<ThemeProvider defaultMode="dark">
534
+ <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
535
+ <div className="flex-1 flex flex-col items-center gap-4">
536
+ <span className="text-sm font-medium text-muted-foreground">
537
+ Không dùng ThemeWrapper
538
+ </span>
539
+ <DropdownMenu>
540
+ <DropdownMenuTrigger
541
+ render={<Button variant="outline">Open Dropdown</Button>}
542
+ />
543
+ <DropdownMenuPortal>
544
+ <DropdownMenuContent className="w-48">
545
+ <DropdownMenuGroup>
546
+ <DropdownMenuLabel>Tài khoản</DropdownMenuLabel>
547
+ <DropdownMenuItem>Trang cá nhân</DropdownMenuItem>
548
+ <DropdownMenuItem>Cài đặt</DropdownMenuItem>
549
+ </DropdownMenuGroup>
550
+ <DropdownMenuSeparator />
551
+ <DropdownMenuItem>Đăng xuất</DropdownMenuItem>
552
+ </DropdownMenuContent>
553
+ </DropdownMenuPortal>
554
+ </DropdownMenu>
555
+ </div>
556
+
557
+ <div className="flex-1 flex flex-col items-center gap-4">
558
+ <span className="text-sm font-medium text-muted-foreground">
559
+ Có ThemeWrapper
560
+ </span>
561
+ <DropdownMenu>
562
+ <DropdownMenuTrigger
563
+ render={<Button variant="outline">Open Dropdown</Button>}
564
+ />
565
+ <DropdownMenuPortal>
566
+ <ThemeWrapper>
567
+ <DropdownMenuContent className="w-48">
568
+ <DropdownMenuGroup>
569
+ <DropdownMenuLabel>Tài khoản</DropdownMenuLabel>
570
+ <DropdownMenuItem>Trang cá nhân</DropdownMenuItem>
571
+ <DropdownMenuItem>Cài đặt</DropdownMenuItem>
572
+ </DropdownMenuGroup>
573
+ <DropdownMenuSeparator />
574
+ <DropdownMenuItem>Đăng xuất</DropdownMenuItem>
575
+ </DropdownMenuContent>
576
+ </ThemeWrapper>
577
+ </DropdownMenuPortal>
578
+ </DropdownMenu>
579
+ </div>
580
+ </div>
581
+ </ThemeProvider>`}
582
+ >
583
+ <ThemeProvider defaultMode="dark">
584
+ <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
585
+ <div className="flex-1 flex flex-col items-center gap-4">
586
+ <span className="text-sm font-medium text-muted-foreground">
587
+ Không dùng ThemeWrapper
588
+ </span>
589
+ <DropdownMenu>
590
+ <DropdownMenuTrigger
591
+ render={<Button variant="outline">Open Dropdown</Button>}
592
+ />
593
+ <DropdownMenuPortal>
594
+ <DropdownMenuContent className="w-48">
595
+ <DropdownMenuGroup>
596
+ <DropdownMenuLabel>Tài khoản</DropdownMenuLabel>
597
+ <DropdownMenuItem>Trang cá nhân</DropdownMenuItem>
598
+ <DropdownMenuItem>Cài đặt</DropdownMenuItem>
599
+ </DropdownMenuGroup>
600
+ <DropdownMenuSeparator />
601
+ <DropdownMenuItem>Đăng xuất</DropdownMenuItem>
602
+ </DropdownMenuContent>
603
+ </DropdownMenuPortal>
604
+ </DropdownMenu>
605
+ </div>
606
+
607
+ <div className="flex-1 flex flex-col items-center gap-4">
608
+ <span className="text-sm font-medium text-muted-foreground">
609
+ Có ThemeWrapper
610
+ </span>
611
+ <DropdownMenu>
612
+ <DropdownMenuTrigger
613
+ render={<Button variant="outline">Open Dropdown</Button>}
614
+ />
615
+ <DropdownMenuPortal>
616
+ <ThemeWrapper>
617
+ <DropdownMenuContent className="w-48">
618
+ <DropdownMenuGroup>
619
+ <DropdownMenuLabel>Tài khoản</DropdownMenuLabel>
620
+ <DropdownMenuItem>Trang cá nhân</DropdownMenuItem>
621
+ <DropdownMenuItem>Cài đặt</DropdownMenuItem>
622
+ </DropdownMenuGroup>
623
+ <DropdownMenuSeparator />
624
+ <DropdownMenuItem>Đăng xuất</DropdownMenuItem>
625
+ </DropdownMenuContent>
626
+ </ThemeWrapper>
627
+ </DropdownMenuPortal>
628
+ </DropdownMenu>
629
+ </div>
630
+ </div>
631
+ </ThemeProvider>
632
+ </ExampleSection>
633
+
634
+ {/* Dialog */}
635
+ <ExampleSection
636
+ fullWidth
637
+ label="Dialog"
638
+ description="Dialog là trường hợp phức tạp nhất: portal chứa cả Overlay backdrop lẫn Content panel. Cả hai layer đều cần được bọc trong ThemeWrapper để đảm bảo đồng bộ hoàn toàn."
639
+ codeString={`<ThemeProvider defaultMode="dark">
640
+ <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
641
+ <div className="flex-1 flex flex-col items-center gap-4">
642
+ <span className="text-sm font-medium text-muted-foreground">
643
+ Không dùng ThemeWrapper
644
+ </span>
645
+ <Dialog>
646
+ <DialogTrigger
647
+ render={<Button variant="outline">Open Dialog</Button>}
648
+ />
649
+ <DialogPortal>
650
+ <DialogContent className="sm:max-w-md">
651
+ <DialogHeader>
652
+ <DialogTitle>Mất Dark Mode!</DialogTitle>
653
+ <DialogDescription>
654
+ Lỗi: Hiển thị giao diện sáng mặc định.
655
+ </DialogDescription>
656
+ </DialogHeader>
657
+ </DialogContent>
658
+ </DialogPortal>
659
+ </Dialog>
660
+ </div>
661
+
662
+ <div className="flex-1 flex flex-col items-center gap-4">
663
+ <span className="text-sm font-medium text-muted-foreground">
664
+ Có ThemeWrapper
665
+ </span>
666
+ <Dialog>
667
+ <DialogTrigger
668
+ render={<Button variant="outline">Open Dialog</Button>}
669
+ />
670
+ <DialogPortal>
671
+ <ThemeWrapper>
672
+ <DialogContent className="sm:max-w-md">
673
+ <DialogHeader>
674
+ <DialogTitle>Màu tối toàn vẹn</DialogTitle>
675
+ <DialogDescription>
676
+ Ngay cả khi mở Dialog bọc ngoài toàn bộ màn hình,
677
+ Portal Tunnel vẫn bảo vệ được scope Dark Mode.
678
+ </DialogDescription>
679
+ </DialogHeader>
680
+ </DialogContent>
681
+ </ThemeWrapper>
682
+ </DialogPortal>
683
+ </Dialog>
684
+ </div>
685
+ </div>
686
+ </ThemeProvider>`}
687
+ >
688
+ <ThemeProvider defaultMode="dark">
689
+ <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
690
+ <div className="flex-1 flex flex-col items-center gap-4">
691
+ <span className="text-sm font-medium text-muted-foreground">
692
+ Không dùng ThemeWrapper
693
+ </span>
694
+ <Dialog>
695
+ <DialogTrigger
696
+ render={<Button variant="outline">Open Dialog</Button>}
697
+ />
698
+ <DialogPortal>
699
+ <DialogContent className="sm:max-w-md">
700
+ <DialogHeader>
701
+ <DialogTitle>Mất Dark Mode!</DialogTitle>
702
+ <DialogDescription>
703
+ Lỗi: Hiển thị giao diện sáng mặc định.
704
+ </DialogDescription>
705
+ </DialogHeader>
706
+ </DialogContent>
707
+ </DialogPortal>
708
+ </Dialog>
709
+ </div>
710
+
711
+ <div className="flex-1 flex flex-col items-center gap-4">
712
+ <span className="text-sm font-medium text-muted-foreground">
713
+ Có ThemeWrapper
714
+ </span>
715
+ <Dialog>
716
+ <DialogTrigger
717
+ render={<Button variant="outline">Open Dialog</Button>}
718
+ />
719
+ <DialogPortal>
720
+ <ThemeWrapper>
721
+ <DialogContent className="sm:max-w-md">
722
+ <DialogHeader>
723
+ <DialogTitle>Màu tối toàn vẹn</DialogTitle>
724
+ <DialogDescription>
725
+ Ngay cả khi mở Dialog bọc ngoài toàn bộ màn hình,
726
+ Portal Tunnel vẫn bảo vệ được scope Dark Mode.
727
+ </DialogDescription>
728
+ </DialogHeader>
729
+ </DialogContent>
730
+ </ThemeWrapper>
731
+ </DialogPortal>
732
+ </Dialog>
733
+ </div>
734
+ </div>
735
+ </ThemeProvider>
736
+ </ExampleSection>
737
+
738
+ {/* Sheet */}
739
+ <ExampleSection
740
+ fullWidth
741
+ label="Sheet"
742
+ description="Sheet trượt từ cạnh màn hình và phủ một vùng diện tích lớn — lỗi theme dễ nhận biết nhất vì panel thông thường chứa layout phức tạp hơn Popover hay Tooltip."
743
+ codeString={`<ThemeProvider defaultMode="dark">
744
+ <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
745
+ <div className="flex-1 flex flex-col items-center gap-4">
746
+ <span className="text-sm font-medium text-muted-foreground">
747
+ Không dùng ThemeWrapper
748
+ </span>
749
+ <Sheet>
750
+ <SheetTrigger
751
+ render={<Button variant="outline">Open Sheet</Button>}
752
+ />
753
+ <SheetPortal>
754
+ <SheetContent side="right">
755
+ <SheetHeader>
756
+ <SheetTitle>Mất Dark Mode!</SheetTitle>
757
+ </SheetHeader>
758
+ </SheetContent>
759
+ </SheetPortal>
760
+ </Sheet>
761
+ </div>
762
+
763
+ <div className="flex-1 flex flex-col items-center gap-4">
764
+ <span className="text-sm font-medium text-muted-foreground">
765
+ Có ThemeWrapper
766
+ </span>
767
+ <Sheet>
768
+ <SheetTrigger
769
+ render={<Button variant="outline">Open Sheet</Button>}
770
+ />
771
+ <SheetPortal>
772
+ <ThemeWrapper>
773
+ <SheetContent side="right">
774
+ <SheetHeader>
775
+ <SheetTitle>Sheet Demo</SheetTitle>
776
+ </SheetHeader>
777
+ <div className="py-4">Màu tối đồng bộ hoàn hảo!</div>
778
+ </SheetContent>
779
+ </ThemeWrapper>
780
+ </SheetPortal>
781
+ </Sheet>
782
+ </div>
783
+ </div>
784
+ </ThemeProvider>`}
785
+ >
786
+ <ThemeProvider defaultMode="dark">
787
+ <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
788
+ <div className="flex-1 flex flex-col items-center gap-4">
789
+ <span className="text-sm font-medium text-muted-foreground">
790
+ Không dùng ThemeWrapper
791
+ </span>
792
+ <Sheet>
793
+ <SheetTrigger
794
+ render={<Button variant="outline">Open Sheet</Button>}
795
+ />
796
+ <SheetPortal>
797
+ <SheetContent side="right">
798
+ <SheetHeader>
799
+ <SheetTitle>Mất Dark Mode!</SheetTitle>
800
+ </SheetHeader>
801
+ </SheetContent>
802
+ </SheetPortal>
803
+ </Sheet>
804
+ </div>
805
+
806
+ <div className="flex-1 flex flex-col items-center gap-4">
807
+ <span className="text-sm font-medium text-muted-foreground">
808
+ Có ThemeWrapper
809
+ </span>
810
+ <Sheet>
811
+ <SheetTrigger
812
+ render={<Button variant="outline">Open Sheet</Button>}
813
+ />
814
+ <SheetPortal>
815
+ <ThemeWrapper>
816
+ <SheetContent side="right">
817
+ <SheetHeader>
818
+ <SheetTitle>Sheet Demo</SheetTitle>
819
+ </SheetHeader>
820
+ <div className="py-4">Màu tối đồng bộ hoàn hảo!</div>
821
+ </SheetContent>
822
+ </ThemeWrapper>
823
+ </SheetPortal>
824
+ </Sheet>
825
+ </div>
826
+ </div>
827
+ </ThemeProvider>
828
+ </ExampleSection>
829
+ </div>
830
+ );
831
+ }
832
+
833
+ // ──────────────────────────────────────────────────────────
834
+ // Entry point
835
+ // ──────────────────────────────────────────────────────────
836
+ export default function ThemeProviderShowcase() {
837
+ return (
838
+ <Showcase
839
+ title="Theme Provider"
840
+ description="Context provider cho light/dark mode — uncontrolled (defaultMode) hoặc controlled (mode + onModeChange). Portal-safe qua ThemeWrapper."
841
+ generalConcept={
842
+ <ShowcaseDocs>
843
+ <DocsH3>ThemeProvider</DocsH3>
844
+ <DocsP>
845
+ <DocsCode>ThemeProvider</DocsCode> bọc children trong một{" "}
846
+ <DocsCode>{"div[display:contents]"}</DocsCode> và gắn class{" "}
847
+ <DocsCode>light</DocsCode> hoặc <DocsCode>dark</DocsCode> lên đó,
848
+ kích hoạt CSS variable cascade mà không ảnh hưởng layout. Hỗ trợ
849
+ hai chế độ: <strong>Uncontrolled</strong> (truyền{" "}
850
+ <DocsCode>defaultMode</DocsCode> — tự quản lý state) và{" "}
851
+ <strong>Controlled</strong> (truyền <DocsCode>mode</DocsCode> +{" "}
852
+ <DocsCode>onModeChange</DocsCode> — state do component cha sở hữu).
853
+ Việc đọc localStorage hoặc system preference thuộc về tầng Macro.
854
+ </DocsP>
855
+
856
+ <DocsH3>ThemeWrapper &amp; Portal — Hard Requirement</DocsH3>
857
+ <DocsP>
858
+ CSS variable cascade theo <strong>DOM tree</strong>, không theo
859
+ React component tree. Khi một component render qua Portal (ra{" "}
860
+ <DocsCode>document.body</DocsCode>), nó thoát khỏi hoàn toàn chuỗi
861
+ DOM ancestry — không còn tổ tiên nào mang class{" "}
862
+ <DocsCode>.dark</DocsCode>, và mọi CSS variable rơi thẳng về giá
863
+ trị <DocsCode>:root</DocsCode> (thường là light).
864
+ </DocsP>
865
+ <DocsP>
866
+ <DocsCode>ThemeWrapper</DocsCode> giải quyết vấn đề này bằng cách
867
+ đọc <DocsCode>useTheme()</DocsCode> và re-apply class theme vào một
868
+ container mới ngay bên trong Portal — tái thiết lập CSS variable
869
+ cascade cho toàn bộ nội dung bên trong.{" "}
870
+ <strong>
871
+ Đây không phải best practice tuỳ chọn — đây là yêu cầu bắt buộc
872
+ </strong>{" "}
873
+ khi dùng Portal trong bất kỳ scoped theme section nào. Bỏ qua{" "}
874
+ <DocsCode>ThemeWrapper</DocsCode> sẽ không gây lỗi compile hay
875
+ runtime warning, nhưng UI sẽ hiển thị sai màu âm thầm.
876
+ </DocsP>
877
+ <DocsP>
878
+ Trách nhiệm đặt <DocsCode>ThemeWrapper</DocsCode> không thể
879
+ delegate tuỳ tiện: Micro component không được tự ôm nó bên trong
880
+ (vi phạm nguyên tắc pure composition). Tầng nào kiểm soát Portal —
881
+ Macro component hoặc consumer trực tiếp — tầng đó phải tự đặt{" "}
882
+ <DocsCode>ThemeWrapper</DocsCode> ngay sau Portal boundary.
883
+ </DocsP>
884
+ </ShowcaseDocs>
885
+ }
886
+ tabs={[
887
+ {
888
+ label: "Micro (Primitive)",
889
+ content: <ThemeProviderMicroShowcase />,
890
+ },
891
+ ]}
892
+ />
893
+ );
894
+ }