@hellboy/ds 0.1.2 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (298) hide show
  1. package/README.md +568 -71
  2. package/{src/style/components/badge → dist/components}/badge.css +9 -25
  3. package/dist/components/badge.d.mts +12 -0
  4. package/dist/components/badge.d.ts +12 -0
  5. package/dist/components/badge.js +42 -0
  6. package/dist/components/badge.mjs +15 -0
  7. package/dist/components/banner.css +280 -0
  8. package/dist/components/banner.d.mts +12 -0
  9. package/dist/components/banner.d.ts +12 -0
  10. package/dist/components/banner.js +184 -0
  11. package/dist/components/banner.mjs +147 -0
  12. package/dist/components/button-group.css +289 -0
  13. package/dist/components/button-group.d.mts +81 -0
  14. package/dist/components/button-group.d.ts +81 -0
  15. package/dist/components/button-group.js +180 -0
  16. package/dist/components/button-group.mjs +143 -0
  17. package/{src/style/components/button → dist/components}/button.css +59 -62
  18. package/dist/components/button.d.mts +57 -0
  19. package/dist/components/button.d.ts +57 -0
  20. package/dist/components/button.js +129 -0
  21. package/dist/components/button.mjs +92 -0
  22. package/{src/style/components/card → dist/components}/card.css +9 -30
  23. package/dist/components/card.d.mts +31 -0
  24. package/dist/components/card.d.ts +31 -0
  25. package/dist/components/card.js +59 -0
  26. package/dist/components/card.mjs +32 -0
  27. package/{src/style → dist}/components/checkbox.css +51 -43
  28. package/dist/components/checkbox.d.mts +31 -0
  29. package/dist/components/checkbox.d.ts +31 -0
  30. package/dist/components/checkbox.js +130 -0
  31. package/dist/components/checkbox.mjs +93 -0
  32. package/{src/style/components/code-block → dist/components}/code-block.css +3 -7
  33. package/dist/components/code-block.d.mts +24 -0
  34. package/dist/components/code-block.d.ts +24 -0
  35. package/dist/components/code-block.js +43 -0
  36. package/dist/components/code-block.mjs +16 -0
  37. package/dist/components/color-control.css +285 -0
  38. package/dist/components/color-control.d.mts +5 -0
  39. package/dist/components/color-control.d.ts +5 -0
  40. package/dist/components/color-control.js +534 -0
  41. package/dist/components/color-control.mjs +497 -0
  42. package/dist/components/dialog.css +930 -0
  43. package/dist/components/dialog.d.mts +32 -0
  44. package/dist/components/dialog.d.ts +32 -0
  45. package/dist/components/dialog.js +1111 -0
  46. package/dist/components/dialog.mjs +1074 -0
  47. package/dist/components/divider.css +356 -0
  48. package/dist/components/divider.d.mts +32 -0
  49. package/dist/components/divider.d.ts +32 -0
  50. package/dist/components/divider.js +344 -0
  51. package/dist/components/divider.mjs +307 -0
  52. package/{src/style/components/drag-handle → dist/components}/drag-handle.css +3 -18
  53. package/dist/components/drag-handle.d.mts +11 -0
  54. package/dist/components/drag-handle.d.ts +11 -0
  55. package/dist/components/drag-handle.js +103 -0
  56. package/dist/components/drag-handle.mjs +66 -0
  57. package/dist/components/drawer.css +1027 -0
  58. package/dist/components/drawer.d.mts +14 -0
  59. package/dist/components/drawer.d.ts +14 -0
  60. package/dist/components/drawer.js +1072 -0
  61. package/dist/components/drawer.mjs +1035 -0
  62. package/dist/components/floating-bar.css +17 -0
  63. package/dist/components/floating-bar.d.mts +25 -0
  64. package/dist/components/floating-bar.d.ts +25 -0
  65. package/dist/components/floating-bar.js +52 -0
  66. package/dist/components/floating-bar.mjs +25 -0
  67. package/dist/components/footer.css +40 -0
  68. package/dist/components/footer.d.mts +8 -0
  69. package/dist/components/footer.d.ts +8 -0
  70. package/dist/components/footer.js +44 -0
  71. package/dist/components/footer.mjs +17 -0
  72. package/dist/components/grid.css +47 -0
  73. package/dist/components/grid.d.mts +27 -0
  74. package/dist/components/grid.d.ts +27 -0
  75. package/dist/components/grid.js +52 -0
  76. package/dist/components/grid.mjs +25 -0
  77. package/dist/components/header.css +1075 -0
  78. package/dist/components/header.d.mts +35 -0
  79. package/dist/components/header.d.ts +35 -0
  80. package/dist/components/header.js +1402 -0
  81. package/dist/components/header.mjs +1365 -0
  82. package/dist/components/hero.css +121 -0
  83. package/dist/components/hero.d.mts +111 -0
  84. package/dist/components/hero.d.ts +111 -0
  85. package/dist/components/hero.js +285 -0
  86. package/dist/components/hero.mjs +248 -0
  87. package/{src/style/components/icons → dist/components}/icons.css +14 -15
  88. package/dist/components/icons.d.mts +104 -0
  89. package/dist/components/icons.d.ts +104 -0
  90. package/dist/components/icons.js +239 -0
  91. package/dist/components/icons.mjs +203 -0
  92. package/{src/style/components/input → dist/components}/input.css +189 -102
  93. package/dist/components/input.d.mts +114 -0
  94. package/dist/components/input.d.ts +114 -0
  95. package/dist/components/input.js +926 -0
  96. package/dist/components/input.mjs +879 -0
  97. package/dist/components/layout.css +551 -0
  98. package/dist/components/layout.d.mts +16 -0
  99. package/dist/components/layout.d.ts +16 -0
  100. package/dist/components/layout.js +387 -0
  101. package/dist/components/layout.mjs +352 -0
  102. package/{src/style/components/list → dist/components}/list.css +47 -41
  103. package/dist/components/list.d.mts +46 -0
  104. package/dist/components/list.d.ts +46 -0
  105. package/dist/components/list.js +124 -0
  106. package/dist/components/list.mjs +96 -0
  107. package/dist/components/navbar.css +706 -0
  108. package/dist/components/navbar.d.mts +56 -0
  109. package/dist/components/navbar.d.ts +56 -0
  110. package/dist/components/navbar.js +994 -0
  111. package/dist/components/navbar.mjs +952 -0
  112. package/{src/style/components/page-index → dist/components}/page-index.css +2 -47
  113. package/dist/components/page-index.d.mts +25 -0
  114. package/dist/components/page-index.d.ts +25 -0
  115. package/dist/components/page-index.js +239 -0
  116. package/dist/components/page-index.mjs +202 -0
  117. package/{src/style/components/page → dist/components}/page.css +4 -15
  118. package/dist/components/page.d.mts +30 -0
  119. package/dist/components/page.d.ts +30 -0
  120. package/dist/components/page.js +40 -0
  121. package/dist/components/page.mjs +13 -0
  122. package/dist/components/popover.css +87 -0
  123. package/dist/components/popover.d.mts +22 -0
  124. package/dist/components/popover.d.ts +22 -0
  125. package/dist/components/popover.js +243 -0
  126. package/dist/components/popover.mjs +206 -0
  127. package/{src/style → dist}/components/radio.css +8 -51
  128. package/dist/components/radio.d.mts +59 -0
  129. package/dist/components/radio.d.ts +59 -0
  130. package/dist/components/radio.js +133 -0
  131. package/dist/components/radio.mjs +95 -0
  132. package/dist/components/section.css +993 -0
  133. package/dist/components/section.d.mts +33 -0
  134. package/dist/components/section.d.ts +33 -0
  135. package/dist/components/section.js +1401 -0
  136. package/dist/components/section.mjs +1364 -0
  137. package/dist/components/select.css +391 -0
  138. package/dist/components/select.d.mts +63 -0
  139. package/dist/components/select.d.ts +63 -0
  140. package/dist/components/select.js +452 -0
  141. package/dist/components/select.mjs +415 -0
  142. package/{src/style/components/slider → dist/components}/slider.css +55 -33
  143. package/dist/components/slider.d.mts +69 -0
  144. package/dist/components/slider.d.ts +69 -0
  145. package/dist/components/slider.js +254 -0
  146. package/dist/components/slider.mjs +217 -0
  147. package/dist/components/switch.css +1081 -0
  148. package/dist/components/switch.d.mts +33 -0
  149. package/dist/components/switch.d.ts +33 -0
  150. package/dist/components/switch.js +1092 -0
  151. package/dist/components/switch.mjs +1055 -0
  152. package/{src/style/components/table → dist/components}/table.css +3 -28
  153. package/dist/components/table.d.mts +42 -0
  154. package/dist/components/table.d.ts +42 -0
  155. package/dist/components/table.js +108 -0
  156. package/dist/components/table.mjs +76 -0
  157. package/dist/components/tag.css +97 -0
  158. package/dist/components/tag.d.mts +12 -0
  159. package/dist/components/tag.d.ts +12 -0
  160. package/dist/components/tag.js +42 -0
  161. package/dist/components/tag.mjs +15 -0
  162. package/dist/components/textarea.css +1359 -0
  163. package/dist/components/textarea.d.mts +84 -0
  164. package/dist/components/textarea.d.ts +84 -0
  165. package/dist/components/textarea.js +1962 -0
  166. package/dist/components/textarea.mjs +1924 -0
  167. package/{src/style/components/theme-control → dist/components}/theme-control.css +3 -7
  168. package/dist/components/theme-control.d.mts +9 -0
  169. package/dist/components/theme-control.d.ts +9 -0
  170. package/dist/components/theme-control.js +235 -0
  171. package/dist/components/theme-control.mjs +200 -0
  172. package/{src/style/components/tooltip → dist/components}/tooltip.css +5 -13
  173. package/dist/components/tooltip.d.mts +12 -0
  174. package/dist/components/tooltip.d.ts +12 -0
  175. package/dist/components/tooltip.js +200 -0
  176. package/dist/components/tooltip.mjs +163 -0
  177. package/dist/icons-Czahnf-r.d.mts +15 -0
  178. package/dist/icons-Czahnf-r.d.ts +15 -0
  179. package/dist/index.css +2915 -2068
  180. package/dist/index.d.mts +144 -721
  181. package/dist/index.d.ts +144 -721
  182. package/dist/index.js +4076 -2282
  183. package/dist/index.mjs +4132 -2366
  184. package/dist/theme.css +34 -34
  185. package/package.json +27 -8
  186. package/dist/index.css.map +0 -1
  187. package/dist/index.js.map +0 -1
  188. package/dist/index.mjs.map +0 -1
  189. package/hellboy-ds-0.1.2.tgz +0 -0
  190. package/src/components/badge/Badge.tsx +0 -29
  191. package/src/components/badge/index.ts +0 -1
  192. package/src/components/banner/Banner.tsx +0 -48
  193. package/src/components/banner/banner.css +0 -44
  194. package/src/components/banner/index.ts +0 -1
  195. package/src/components/button/button.tsx +0 -127
  196. package/src/components/button/index.ts +0 -1
  197. package/src/components/card/card.tsx +0 -57
  198. package/src/components/card/index.ts +0 -1
  199. package/src/components/checkbox/Checkbox.tsx +0 -98
  200. package/src/components/checkbox/index.ts +0 -1
  201. package/src/components/code-block/code-block.tsx +0 -44
  202. package/src/components/code-block/index.ts +0 -1
  203. package/src/components/color-control/color-control.tsx +0 -322
  204. package/src/components/color-control/index.ts +0 -1
  205. package/src/components/drag-handle/DragHandle.tsx +0 -78
  206. package/src/components/drag-handle/index.ts +0 -1
  207. package/src/components/drawer/drawer.tsx +0 -82
  208. package/src/components/drawer/index.ts +0 -1
  209. package/src/components/floating-bar/floating-bar.tsx +0 -52
  210. package/src/components/floating-bar/index.ts +0 -2
  211. package/src/components/footer/footer.tsx +0 -28
  212. package/src/components/footer/index.ts +0 -1
  213. package/src/components/grid/Grid.tsx +0 -53
  214. package/src/components/grid/index.ts +0 -1
  215. package/src/components/header/header.tsx +0 -57
  216. package/src/components/header/index.ts +0 -1
  217. package/src/components/icons/icons.tsx +0 -44
  218. package/src/components/icons/index.ts +0 -1
  219. package/src/components/index.ts +0 -29
  220. package/src/components/input/DatePicker.tsx +0 -133
  221. package/src/components/input/Input.tsx +0 -220
  222. package/src/components/input/InputDate.tsx +0 -10
  223. package/src/components/input/InputDateTime.tsx +0 -10
  224. package/src/components/input/InputEmail.tsx +0 -10
  225. package/src/components/input/InputField.tsx +0 -137
  226. package/src/components/input/InputNumber.tsx +0 -10
  227. package/src/components/input/InputPassword.tsx +0 -10
  228. package/src/components/input/InputSearch.tsx +0 -10
  229. package/src/components/input/InputTel.tsx +0 -10
  230. package/src/components/input/InputText.tsx +0 -10
  231. package/src/components/input/InputTime.tsx +0 -10
  232. package/src/components/input/InputUrl.tsx +0 -10
  233. package/src/components/input/TimePicker.tsx +0 -151
  234. package/src/components/input/index.ts +0 -11
  235. package/src/components/layout/Layout.tsx +0 -244
  236. package/src/components/layout/index.ts +0 -1
  237. package/src/components/list/List.tsx +0 -159
  238. package/src/components/list/index.ts +0 -1
  239. package/src/components/navbar/MenuCategory.tsx +0 -20
  240. package/src/components/navbar/MenuGroup.tsx +0 -288
  241. package/src/components/navbar/MenuItem.tsx +0 -65
  242. package/src/components/navbar/Navbar.tsx +0 -23
  243. package/src/components/navbar/index.ts +0 -4
  244. package/src/components/page/index.ts +0 -1
  245. package/src/components/page/page.tsx +0 -46
  246. package/src/components/page-index/PageIndex.tsx +0 -275
  247. package/src/components/page-index/index.ts +0 -1
  248. package/src/components/popover/index.ts +0 -1
  249. package/src/components/popover/popover.tsx +0 -199
  250. package/src/components/radio/Radio.tsx +0 -176
  251. package/src/components/radio/index.ts +0 -1
  252. package/src/components/section/index.ts +0 -1
  253. package/src/components/section/section.tsx +0 -66
  254. package/src/components/select/Select.tsx +0 -212
  255. package/src/components/select/index.ts +0 -1
  256. package/src/components/slider/Slider.tsx +0 -267
  257. package/src/components/slider/index.ts +0 -1
  258. package/src/components/switch/index.ts +0 -1
  259. package/src/components/switch/switch.tsx +0 -99
  260. package/src/components/table/Table.tsx +0 -147
  261. package/src/components/table/index.ts +0 -1
  262. package/src/components/theme-control/index.ts +0 -1
  263. package/src/components/theme-control/theme-control.tsx +0 -78
  264. package/src/components/tooltip/index.ts +0 -1
  265. package/src/components/tooltip/tooltip.tsx +0 -207
  266. package/src/contexts/NavbarTooltipContext.tsx +0 -48
  267. package/src/contexts/index.ts +0 -1
  268. package/src/foundations/motion.md +0 -136
  269. package/src/index.ts +0 -40
  270. package/src/style/_shared/field.css +0 -69
  271. package/src/style/components/color-control/color-control.css +0 -126
  272. package/src/style/components/drawer/drawer.css +0 -210
  273. package/src/style/components/floating-bar/floating-bar.css +0 -39
  274. package/src/style/components/footer/footer.css +0 -108
  275. package/src/style/components/grid/grid.css +0 -33
  276. package/src/style/components/header/header.css +0 -44
  277. package/src/style/components/layout/layout.css +0 -205
  278. package/src/style/components/navbar/navbar.css +0 -342
  279. package/src/style/components/popover/popover.css +0 -44
  280. package/src/style/components/section/section.css +0 -67
  281. package/src/style/components/select/select.css +0 -143
  282. package/src/style/components/switch/switch.css +0 -267
  283. package/src/style/foundations/global.css +0 -316
  284. package/src/style/foundations/motion.css +0 -164
  285. package/src/style/foundations/spacing.css +0 -51
  286. package/src/style/foundations/typography.css +0 -39
  287. package/src/style/foundations/z-index.css +0 -81
  288. package/src/style/modes/dark.css +0 -146
  289. package/src/style/modes/light.css +0 -147
  290. package/src/style/semantic.css +0 -52
  291. package/src/style/styles.css +0 -51
  292. package/src/style/themes/theme.json +0 -37
  293. package/src/utils/README.md +0 -305
  294. package/src/utils/USER_PREFERENCES.md +0 -558
  295. package/src/utils/theme.ts +0 -127
  296. package/src/utils/user-preferences.ts +0 -577
  297. package/tsconfig.json +0 -25
  298. package/tsup.config.ts +0 -52
@@ -1,558 +0,0 @@
1
- # User Preferences Utility
2
-
3
- ## Overview
4
-
5
- The **User Preferences** utility is a comprehensive preference management system for the Hellboy Design System. It provides a practical, type-safe solution for storing and retrieving user preferences across sessions using localStorage with optional IndexedDB support.
6
-
7
- ## Philosophy
8
-
9
- The Hellboy Design System aims to **maximize development velocity** by providing complete, practical solutions rather than staying purely agnostic. While many design systems focus solely on visual components, we believe in delivering utilities that solve common real-world problems developers face when building modern web applications.
10
-
11
- This preference system is designed to be:
12
-
13
- - ✅ **Easy to use** with minimal configuration
14
- - ✅ **Type-safe** with TypeScript
15
- - ✅ **Persistent** across browser sessions
16
- - ✅ **Extensible** for custom preferences
17
- - ✅ **Framework-agnostic** (works with React, Vue, Svelte, vanilla JS, etc.)
18
-
19
- ## Why This Utility Exists
20
-
21
- When building applications with the Hellboy Design System, you'll commonly need to:
22
-
23
- - Remember user's theme preference (light/dark mode)
24
- - Persist custom color configurations
25
- - Save layout preferences (sidebar widths, panel sizes)
26
- - Store component states (drawer positions, table column preferences)
27
- - Maintain user-specific settings across sessions
28
-
29
- Instead of implementing this functionality from scratch in every project, the User Preferences utility provides a **ready-to-use, production-ready solution** that integrates seamlessly with the design system.
30
-
31
- ## Installation
32
-
33
- The utility is included in the core `@hellboy/ds` package:
34
-
35
- ```bash
36
- npm install @hellboy/ds
37
- ```
38
-
39
- ## Basic Usage
40
-
41
- ### Importing
42
-
43
- ```typescript
44
- import {
45
- getPreference,
46
- setPreference,
47
- removePreference,
48
- clearPreferences
49
- } from '@hellboy/ds';
50
- ```
51
-
52
- ### Simple Get/Set
53
-
54
- ```typescript
55
- // Set a preference
56
- setPreference('theme', 'dark');
57
- setPreference('sidebarWidth', 320);
58
-
59
- // Get a preference with optional default
60
- const theme = getPreference('theme', 'light'); // 'dark'
61
- const width = getPreference('sidebarWidth', 280); // 320
62
-
63
- // Remove a preference
64
- removePreference('sidebarWidth');
65
-
66
- // Clear all preferences
67
- clearPreferences();
68
- ```
69
-
70
- ## Supported Preference Types
71
-
72
- The utility comes with built-in TypeScript interfaces for common preference categories:
73
-
74
- ### Theme Preferences
75
-
76
- ```typescript
77
- import { setPreference, getPreference } from '@hellboy/ds';
78
-
79
- // Theme mode
80
- setPreference('theme', 'dark');
81
-
82
- // Custom color configuration
83
- setPreference('colorConfig', {
84
- primaryHue: 220,
85
- primarySaturation: 80,
86
- primaryLightness: 50,
87
- // ... other color properties
88
- });
89
- ```
90
-
91
- ### Layout Preferences
92
-
93
- Perfect for remembering resizable panel dimensions:
94
-
95
- ```typescript
96
- // Save sidebar widths
97
- setPreference('layout.leftSidebarWidth', 280);
98
- setPreference('layout.rightSidebarWidth', 320);
99
- setPreference('layout.navbarCollapsed', false);
100
-
101
- // Restore on load
102
- const leftWidth = getPreference('layout.leftSidebarWidth', 280);
103
- const rightWidth = getPreference('layout.rightSidebarWidth', 300);
104
- ```
105
-
106
- ### Component-Specific Preferences
107
-
108
- Store state for individual components:
109
-
110
- ```typescript
111
- // Save drawer state
112
- setPreference('components.drawer.position', 'right');
113
- setPreference('components.drawer.width', 400);
114
-
115
- // Save table preferences
116
- setPreference('components.userTable.sortColumn', 'name');
117
- setPreference('components.userTable.sortDirection', 'asc');
118
- setPreference('components.userTable.pageSize', 50);
119
- ```
120
-
121
- ### Custom Preferences
122
-
123
- Store any custom data:
124
-
125
- ```typescript
126
- setPreference('custom.userLanguage', 'pt-BR');
127
- setPreference('custom.timezone', 'America/Sao_Paulo');
128
- setPreference('custom.notifications', {
129
- email: true,
130
- push: false,
131
- sms: true
132
- });
133
- ```
134
-
135
- ## Advanced Features
136
-
137
- ### Namespaces
138
-
139
- Organize preferences with namespaces:
140
-
141
- ```typescript
142
- import { createPreferenceNamespace } from '@hellboy/ds';
143
-
144
- // Create a namespace for layout preferences
145
- const layoutPrefs = createPreferenceNamespace('layout');
146
-
147
- // Use scoped methods
148
- layoutPrefs.set('sidebarWidth', 300);
149
- layoutPrefs.set('navbarHeight', 64);
150
-
151
- const width = layoutPrefs.get('sidebarWidth', 280); // 300
152
-
153
- // Clear only layout preferences
154
- layoutPrefs.clear();
155
- ```
156
-
157
- ### Export/Import
158
-
159
- Backup and restore preferences:
160
-
161
- ```typescript
162
- import { exportPreferences, importPreferences } from '@hellboy/ds';
163
-
164
- // Export all preferences as JSON
165
- const json = exportPreferences();
166
-
167
- // Download as file
168
- const blob = new Blob([json], { type: 'application/json' });
169
- const url = URL.createObjectURL(blob);
170
- const a = document.createElement('a');
171
- a.href = url;
172
- a.download = 'user-preferences.json';
173
- a.click();
174
-
175
- // Import preferences (merge or replace)
176
- importPreferences(json, true); // Merge with existing
177
- importPreferences(json, false); // Replace all
178
- ```
179
-
180
- ### Get All Preferences
181
-
182
- ```typescript
183
- import { getAllPreferences } from '@hellboy/ds';
184
-
185
- const allPrefs = getAllPreferences();
186
- console.log(allPrefs);
187
- // {
188
- // 'theme': 'dark',
189
- // 'layout.leftSidebarWidth': 320,
190
- // 'custom.language': 'pt-BR',
191
- // ...
192
- // }
193
- ```
194
-
195
- ### Configuration
196
-
197
- Customize the storage backend and key prefix:
198
-
199
- ```typescript
200
- import { configurePreferences } from '@hellboy/ds';
201
-
202
- // Configure at app initialization
203
- configurePreferences({
204
- backend: 'localStorage', // or 'indexedDB' (future)
205
- prefix: 'my-app',
206
- dbName: 'my-app-prefs',
207
- storeName: 'preferences'
208
- });
209
- ```
210
-
211
- ## Framework Integration
212
-
213
- ### React Hook Pattern
214
-
215
- Here's a recommended pattern for React applications:
216
-
217
- ```tsx
218
- // hooks/usePreference.ts
219
- import { useState, useEffect } from 'react';
220
- import { getPreference, setPreference } from '@hellboy/ds';
221
-
222
- export function usePreference<T>(key: string, defaultValue: T) {
223
- const [value, setValue] = useState<T>(() =>
224
- getPreference(key, defaultValue) ?? defaultValue
225
- );
226
-
227
- const updateValue = (newValue: T | ((prev: T) => T)) => {
228
- const resolvedValue = typeof newValue === 'function'
229
- ? (newValue as (prev: T) => T)(value)
230
- : newValue;
231
-
232
- setValue(resolvedValue);
233
- setPreference(key, resolvedValue);
234
- };
235
-
236
- return [value, updateValue] as const;
237
- }
238
-
239
- // Usage in components
240
- function MyComponent() {
241
- const [theme, setTheme] = usePreference('theme', 'light');
242
- const [sidebarWidth, setSidebarWidth] = usePreference('layout.sidebarWidth', 280);
243
-
244
- return (
245
- <div>
246
- <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
247
- Toggle Theme
248
- </button>
249
- <button onClick={() => setSidebarWidth(w => w + 20)}>
250
- Increase Sidebar
251
- </button>
252
- </div>
253
- );
254
- }
255
- ```
256
-
257
- ### Vue Composition API
258
-
259
- ```typescript
260
- // composables/usePreference.ts
261
- import { ref, watch } from 'vue';
262
- import { getPreference, setPreference } from '@hellboy/ds';
263
-
264
- export function usePreference<T>(key: string, defaultValue: T) {
265
- const value = ref<T>(getPreference(key, defaultValue) ?? defaultValue);
266
-
267
- watch(value, (newValue) => {
268
- setPreference(key, newValue);
269
- });
270
-
271
- return value;
272
- }
273
-
274
- // Usage in components
275
- import { usePreference } from '@/composables/usePreference';
276
-
277
- export default {
278
- setup() {
279
- const theme = usePreference('theme', 'light');
280
- const sidebarWidth = usePreference('layout.sidebarWidth', 280);
281
-
282
- return { theme, sidebarWidth };
283
- }
284
- }
285
- ```
286
-
287
- ### Vanilla JavaScript
288
-
289
- ```javascript
290
- import { getPreference, setPreference } from '@hellboy/ds';
291
-
292
- // On page load
293
- document.addEventListener('DOMContentLoaded', () => {
294
- const theme = getPreference('theme', 'light');
295
- document.documentElement.setAttribute('data-theme', theme);
296
-
297
- const sidebarWidth = getPreference('layout.sidebarWidth', 280);
298
- document.querySelector('.sidebar').style.width = `${sidebarWidth}px`;
299
- });
300
-
301
- // On user interaction
302
- document.querySelector('#theme-toggle').addEventListener('click', () => {
303
- const current = getPreference('theme', 'light');
304
- const next = current === 'light' ? 'dark' : 'light';
305
- setPreference('theme', next);
306
- document.documentElement.setAttribute('data-theme', next);
307
- });
308
- ```
309
-
310
- ## Real-World Examples
311
-
312
- ### Example 1: Persistent Resizable Sidebar
313
-
314
- ```typescript
315
- import { useState, useCallback } from 'react';
316
- import { getPreference, setPreference } from '@hellboy/ds';
317
- import { Layout } from '@hellboy/ds';
318
-
319
- function App() {
320
- const [leftWidth, setLeftWidth] = useState(() =>
321
- getPreference('layout.leftSidebarWidth', 280)
322
- );
323
-
324
- const handleResize = useCallback((delta: number) => {
325
- setLeftWidth(prev => {
326
- const newWidth = Math.max(200, Math.min(600, prev + delta));
327
- setPreference('layout.leftSidebarWidth', newWidth);
328
- return newWidth;
329
- });
330
- }, []);
331
-
332
- return (
333
- <Layout
334
- variant="sidebar-main-sidebar"
335
- resizable
336
- sidebarLeft={<Sidebar width={leftWidth} />}
337
- onLeftResize={handleResize}
338
- >
339
- <MainContent />
340
- </Layout>
341
- );
342
- }
343
- ```
344
-
345
- ### Example 2: Theme Persistence with Color Customization
346
-
347
- ```typescript
348
- import { useEffect } from 'react';
349
- import {
350
- getPreference,
351
- setPreference,
352
- applyColorConfig,
353
- setTheme
354
- } from '@hellboy/ds';
355
-
356
- function ThemeProvider({ children }) {
357
- useEffect(() => {
358
- // Restore theme mode
359
- const theme = getPreference('theme', 'light');
360
- setTheme(theme);
361
-
362
- // Restore custom colors
363
- const colorConfig = getPreference('colorConfig');
364
- if (colorConfig) {
365
- applyColorConfig(colorConfig);
366
- }
367
- }, []);
368
-
369
- const updateTheme = (newTheme) => {
370
- setPreference('theme', newTheme);
371
- setTheme(newTheme);
372
- };
373
-
374
- const updateColors = (colorConfig) => {
375
- setPreference('colorConfig', colorConfig);
376
- applyColorConfig(colorConfig);
377
- };
378
-
379
- return children;
380
- }
381
- ```
382
-
383
- ### Example 3: Table Preferences
384
-
385
- ```typescript
386
- function DataTable() {
387
- const [sortColumn, setSortColumn] = usePreference('table.sortColumn', 'name');
388
- const [sortDirection, setSortDirection] = usePreference('table.sortDirection', 'asc');
389
- const [pageSize, setPageSize] = usePreference('table.pageSize', 25);
390
- const [visibleColumns, setVisibleColumns] = usePreference('table.visibleColumns', [
391
- 'name', 'email', 'role', 'status'
392
- ]);
393
-
394
- return (
395
- <Table
396
- sortColumn={sortColumn}
397
- sortDirection={sortDirection}
398
- pageSize={pageSize}
399
- visibleColumns={visibleColumns}
400
- onSortChange={(col, dir) => {
401
- setSortColumn(col);
402
- setSortDirection(dir);
403
- }}
404
- onPageSizeChange={setPageSize}
405
- onVisibleColumnsChange={setVisibleColumns}
406
- />
407
- );
408
- }
409
- ```
410
-
411
- ## API Reference
412
-
413
- ### Core Functions
414
-
415
- | Function | Description |
416
- |----------|-------------|
417
- | `getPreference<T>(key, defaultValue?)` | Get a preference value |
418
- | `setPreference<T>(key, value)` | Set a preference value |
419
- | `removePreference(key)` | Remove a preference |
420
- | `clearPreferences()` | Clear all preferences |
421
- | `getAllPreferences()` | Get all preferences as object |
422
-
423
- ### Async Functions (IndexedDB)
424
-
425
- | Function | Description |
426
- |----------|-------------|
427
- | `getPreferenceAsync<T>(key, defaultValue?)` | Async get for IndexedDB |
428
- | `setPreferenceAsync<T>(key, value)` | Async set for IndexedDB |
429
- | `removePreferenceAsync(key)` | Async remove for IndexedDB |
430
- | `clearPreferencesAsync()` | Async clear for IndexedDB |
431
-
432
- ### Utility Functions
433
-
434
- | Function | Description |
435
- |----------|-------------|
436
- | `configurePreferences(config)` | Configure storage backend |
437
- | `createPreferenceNamespace(namespace)` | Create scoped preferences |
438
- | `exportPreferences()` | Export preferences as JSON |
439
- | `importPreferences(json, merge?)` | Import preferences from JSON |
440
-
441
- ## TypeScript Support
442
-
443
- The utility is fully typed with TypeScript:
444
-
445
- ```typescript
446
- import type { UserPreferences } from '@hellboy/ds';
447
-
448
- // Type-safe preference access
449
- const theme = getPreference<'light' | 'dark'>('theme', 'light');
450
- const width = getPreference<number>('layout.sidebarWidth', 280);
451
-
452
- // Extend UserPreferences interface for custom types
453
- declare module '@hellboy/ds' {
454
- interface UserPreferences {
455
- custom?: {
456
- myCustomSetting?: string;
457
- myFeatureFlags?: {
458
- newUI: boolean;
459
- betaFeatures: boolean;
460
- };
461
- };
462
- }
463
- }
464
- ```
465
-
466
- ## Storage Details
467
-
468
- ### localStorage (Default)
469
-
470
- - **Prefix**: `hellboy-ds:` (configurable)
471
- - **Format**: JSON serialization
472
- - **Capacity**: ~5-10MB (browser-dependent)
473
- - **Sync**: Synchronous operations
474
- - **Browser Support**: All modern browsers
475
-
476
- ### IndexedDB (Future)
477
-
478
- - **Database**: `hellboy-ds-prefs` (configurable)
479
- - **Store**: `preferences` (configurable)
480
- - **Format**: Native JSON
481
- - **Capacity**: Much larger than localStorage
482
- - **Sync**: Asynchronous operations
483
- - **Browser Support**: All modern browsers
484
-
485
- ## Best Practices
486
-
487
- 1. **Use meaningful keys**: Use dot notation for hierarchical organization
488
- ```typescript
489
- setPreference('components.drawer.width', 400);
490
- setPreference('layout.sidebar.collapsed', true);
491
- ```
492
-
493
- 2. **Always provide defaults**: Ensures app works on first load
494
- ```typescript
495
- const width = getPreference('sidebarWidth', 280);
496
- ```
497
-
498
- 3. **Namespace related preferences**: Keep things organized
499
- ```typescript
500
- const layoutPrefs = createPreferenceNamespace('layout');
501
- ```
502
-
503
- 4. **Export for backups**: Allow users to backup their settings
504
- ```typescript
505
- const json = exportPreferences();
506
- ```
507
-
508
- 5. **Type your preferences**: Use TypeScript for type safety
509
- ```typescript
510
- const theme = getPreference<'light' | 'dark'>('theme', 'light');
511
- ```
512
-
513
- ## Browser Compatibility
514
-
515
- - ✅ Chrome 4+
516
- - ✅ Firefox 3.5+
517
- - ✅ Safari 4+
518
- - ✅ Edge (all versions)
519
- - ✅ Opera 10.50+
520
- - ✅ iOS Safari 3.2+
521
- - ✅ Android Browser 2.1+
522
-
523
- ## Performance Considerations
524
-
525
- - localStorage is **synchronous** - operations block the main thread
526
- - For **large datasets** (>1MB), consider using IndexedDB backend (when available)
527
- - **Batch updates** when possible to minimize localStorage writes
528
- - Use **namespaces** to organize and clear related preferences efficiently
529
-
530
- ## Security Notes
531
-
532
- - localStorage is **not encrypted** - don't store sensitive data
533
- - Data is **domain-specific** - accessible to all scripts on the same origin
534
- - Data persists **indefinitely** until explicitly cleared or browser storage is cleared
535
- - Consider **user privacy** - allow users to export/clear their data
536
-
537
- ## Migration from Legacy Storage
538
-
539
- If you're migrating from direct localStorage usage:
540
-
541
- ```typescript
542
- // Before
543
- localStorage.setItem('myTheme', 'dark');
544
- const theme = localStorage.getItem('myTheme') || 'light';
545
-
546
- // After
547
- import { setPreference, getPreference } from '@hellboy/ds';
548
- setPreference('theme', 'dark');
549
- const theme = getPreference('theme', 'light');
550
- ```
551
-
552
- ## Support & Contributing
553
-
554
- This utility is part of the Hellboy Design System. For issues, feature requests, or contributions, please visit the [GitHub repository](#).
555
-
556
- ---
557
-
558
- **Design System Philosophy**: We believe in providing **practical, opinionated solutions** that accelerate development. While staying framework-agnostic where possible, we're not afraid to be opinionated when it delivers real value to developers. The User Preferences utility embodies this philosophy by solving a common problem with a production-ready, batteries-included solution.
@@ -1,127 +0,0 @@
1
- /**
2
- * Theme Utility
3
- * Manages data-theme attribute on <html>, localStorage persistence, and prefers-color-scheme fallback
4
- *
5
- * Priority: data-theme (explicit) > prefers-color-scheme (OS preference) > light (default)
6
- *
7
- * This utility now uses the User Preferences system for storage.
8
- * See USER_PREFERENCES.md for more details on the underlying storage system.
9
- */
10
-
11
- import { getPreference, setPreference } from './user-preferences';
12
-
13
- type Theme = 'light' | 'dark';
14
-
15
- // Preference key for storing the chosen mode only
16
- const THEME_KEY = 'theme';
17
-
18
- const ATTR_NAME = 'data-theme';
19
-
20
- /**
21
- * Apply theme to document element
22
- */
23
- export const applyTheme = (theme: Theme) => {
24
- document.documentElement.setAttribute(ATTR_NAME, theme);
25
- };
26
-
27
- /**
28
- * Get current theme.
29
- * Sources of truth (simplified):
30
- * - `data-theme` attribute on <html>
31
- * - OS `prefers-color-scheme`
32
- * - default: 'light'
33
- */
34
- export const getTheme = (): Theme => {
35
- // 1. If `data-theme` is explicitly set on <html>, that wins
36
- const dataTheme = document.documentElement.getAttribute(ATTR_NAME);
37
- if (dataTheme === 'light' || dataTheme === 'dark') return dataTheme;
38
-
39
- // 2. If the user previously selected a mode, use stored preference
40
- try {
41
- const stored = getPreference<Theme>(THEME_KEY);
42
- if (stored === 'light' || stored === 'dark') return stored;
43
- } catch (e) {
44
- // ignore storage errors and fallback to system/default
45
- }
46
-
47
- // 3. Fall back to OS preference
48
- if (typeof window !== 'undefined' && window.matchMedia('(prefers-color-scheme: dark)').matches) {
49
- return 'dark';
50
- }
51
-
52
- // 4. Default
53
- return 'light';
54
- };
55
-
56
- /**
57
- * Backwards-compatible alias
58
- */
59
- export const getSavedTheme = (): Theme => getTheme();
60
-
61
- /**
62
- * Set theme on document (no persistence)
63
- */
64
- export const setTheme = (theme: Theme) => {
65
- if (!['light', 'dark'].includes(theme)) {
66
- console.warn(`Invalid theme: ${theme}. Using 'light' as fallback.`);
67
- theme = 'light';
68
- }
69
-
70
- // Persist only the mode (light/dark)
71
- try {
72
- setPreference(THEME_KEY, theme);
73
- } catch (e) {
74
- // ignore storage errors
75
- }
76
-
77
- applyTheme(theme);
78
- };
79
-
80
- /**
81
- * Initialize theme on startup (mode-only)
82
- */
83
- export const initializeTheme = () => {
84
- const theme = getTheme();
85
- applyTheme(theme);
86
- };
87
-
88
- /**
89
- * Listen to system theme changes. Only calls callback when OS preference
90
- * changes. Consumers decide whether to apply it.
91
- */
92
- export const onSystemThemeChange = (callback: (theme: Theme) => void): (() => void) => {
93
- if (typeof window === 'undefined') return () => {};
94
-
95
- const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
96
- const listener = (e: MediaQueryListEvent | MediaQueryList) => {
97
- const theme = e.matches ? 'dark' : 'light';
98
- // Only notify consumers if the user has NOT explicitly chosen a mode
99
- const hasStored = (() => {
100
- try {
101
- const stored = getPreference<Theme>(THEME_KEY);
102
- return stored === 'light' || stored === 'dark';
103
- } catch { return false; }
104
- })();
105
-
106
- const dataTheme = document.documentElement.getAttribute(ATTR_NAME);
107
- if (!hasStored && !dataTheme) callback(theme);
108
- };
109
-
110
- if (mediaQuery.addEventListener) {
111
- mediaQuery.addEventListener('change', listener);
112
- return () => mediaQuery.removeEventListener('change', listener);
113
- }
114
-
115
- mediaQuery.addListener(listener as any);
116
- return () => mediaQuery.removeListener(listener as any);
117
- };
118
-
119
- /**
120
- * Toggle between light and dark themes (no persistence)
121
- */
122
- export const toggleTheme = (): Theme => {
123
- const current = getTheme();
124
- const next = current === 'light' ? 'dark' : 'light';
125
- setTheme(next);
126
- return next;
127
- };