@furystack/shades-common-components 10.0.35 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/esm/components/animations.spec.d.ts +2 -0
  3. package/esm/components/animations.spec.d.ts.map +1 -0
  4. package/esm/components/animations.spec.js +201 -0
  5. package/esm/components/animations.spec.js.map +1 -0
  6. package/esm/components/app-bar-link.js +21 -20
  7. package/esm/components/app-bar-link.js.map +1 -1
  8. package/esm/components/app-bar-link.spec.d.ts +2 -0
  9. package/esm/components/app-bar-link.spec.d.ts.map +1 -0
  10. package/esm/components/app-bar-link.spec.js +252 -0
  11. package/esm/components/app-bar-link.spec.js.map +1 -0
  12. package/esm/components/app-bar.js +21 -21
  13. package/esm/components/app-bar.js.map +1 -1
  14. package/esm/components/app-bar.spec.d.ts +2 -0
  15. package/esm/components/app-bar.spec.d.ts.map +1 -0
  16. package/esm/components/app-bar.spec.js +117 -0
  17. package/esm/components/app-bar.spec.js.map +1 -0
  18. package/esm/components/avatar.d.ts.map +1 -1
  19. package/esm/components/avatar.js +15 -19
  20. package/esm/components/avatar.js.map +1 -1
  21. package/esm/components/avatar.spec.d.ts +2 -0
  22. package/esm/components/avatar.spec.d.ts.map +1 -0
  23. package/esm/components/avatar.spec.js +114 -0
  24. package/esm/components/avatar.spec.js.map +1 -0
  25. package/esm/components/button.d.ts.map +1 -1
  26. package/esm/components/button.js +145 -156
  27. package/esm/components/button.js.map +1 -1
  28. package/esm/components/button.spec.d.ts +2 -0
  29. package/esm/components/button.spec.d.ts.map +1 -0
  30. package/esm/components/button.spec.js +155 -0
  31. package/esm/components/button.spec.js.map +1 -0
  32. package/esm/components/command-palette/command-palette-input.d.ts.map +1 -1
  33. package/esm/components/command-palette/command-palette-input.js +18 -16
  34. package/esm/components/command-palette/command-palette-input.js.map +1 -1
  35. package/esm/components/command-palette/command-palette-input.spec.d.ts +2 -0
  36. package/esm/components/command-palette/command-palette-input.spec.d.ts.map +1 -0
  37. package/esm/components/command-palette/command-palette-input.spec.js +233 -0
  38. package/esm/components/command-palette/command-palette-input.spec.js.map +1 -0
  39. package/esm/components/command-palette/command-palette-manager.spec.d.ts +2 -0
  40. package/esm/components/command-palette/command-palette-manager.spec.d.ts.map +1 -0
  41. package/esm/components/command-palette/command-palette-manager.spec.js +362 -0
  42. package/esm/components/command-palette/command-palette-manager.spec.js.map +1 -0
  43. package/esm/components/command-palette/command-palette-suggestion-list.d.ts.map +1 -1
  44. package/esm/components/command-palette/command-palette-suggestion-list.js +42 -46
  45. package/esm/components/command-palette/command-palette-suggestion-list.js.map +1 -1
  46. package/esm/components/command-palette/command-palette-suggestion-list.spec.d.ts +2 -0
  47. package/esm/components/command-palette/command-palette-suggestion-list.spec.d.ts.map +1 -0
  48. package/esm/components/command-palette/command-palette-suggestion-list.spec.js +376 -0
  49. package/esm/components/command-palette/command-palette-suggestion-list.spec.js.map +1 -0
  50. package/esm/components/command-palette/index.d.ts.map +1 -1
  51. package/esm/components/command-palette/index.js +100 -110
  52. package/esm/components/command-palette/index.js.map +1 -1
  53. package/esm/components/command-palette/index.spec.d.ts +2 -0
  54. package/esm/components/command-palette/index.spec.d.ts.map +1 -0
  55. package/esm/components/command-palette/index.spec.js +509 -0
  56. package/esm/components/command-palette/index.spec.js.map +1 -0
  57. package/esm/components/data-grid/body.js +1 -1
  58. package/esm/components/data-grid/body.js.map +1 -1
  59. package/esm/components/data-grid/body.spec.d.ts +2 -0
  60. package/esm/components/data-grid/body.spec.d.ts.map +1 -0
  61. package/esm/components/data-grid/body.spec.js +228 -0
  62. package/esm/components/data-grid/body.spec.js.map +1 -0
  63. package/esm/components/data-grid/data-grid-row.d.ts.map +1 -1
  64. package/esm/components/data-grid/data-grid-row.js +49 -73
  65. package/esm/components/data-grid/data-grid-row.js.map +1 -1
  66. package/esm/components/data-grid/data-grid-row.spec.d.ts +2 -0
  67. package/esm/components/data-grid/data-grid-row.spec.d.ts.map +1 -0
  68. package/esm/components/data-grid/data-grid-row.spec.js +296 -0
  69. package/esm/components/data-grid/data-grid-row.spec.js.map +1 -0
  70. package/esm/components/data-grid/data-grid.d.ts.map +1 -1
  71. package/esm/components/data-grid/data-grid.js +35 -28
  72. package/esm/components/data-grid/data-grid.js.map +1 -1
  73. package/esm/components/data-grid/data-grid.spec.d.ts +2 -0
  74. package/esm/components/data-grid/data-grid.spec.d.ts.map +1 -0
  75. package/esm/components/data-grid/data-grid.spec.js +544 -0
  76. package/esm/components/data-grid/data-grid.spec.js.map +1 -0
  77. package/esm/components/data-grid/footer.js +21 -15
  78. package/esm/components/data-grid/footer.js.map +1 -1
  79. package/esm/components/data-grid/footer.spec.d.ts +2 -0
  80. package/esm/components/data-grid/footer.spec.d.ts.map +1 -0
  81. package/esm/components/data-grid/footer.spec.js +264 -0
  82. package/esm/components/data-grid/footer.spec.js.map +1 -0
  83. package/esm/components/data-grid/header.d.ts.map +1 -1
  84. package/esm/components/data-grid/header.js +55 -33
  85. package/esm/components/data-grid/header.js.map +1 -1
  86. package/esm/components/data-grid/header.spec.d.ts +2 -0
  87. package/esm/components/data-grid/header.spec.d.ts.map +1 -0
  88. package/esm/components/data-grid/header.spec.js +421 -0
  89. package/esm/components/data-grid/header.spec.js.map +1 -0
  90. package/esm/components/data-grid/selection-cell.d.ts.map +1 -1
  91. package/esm/components/data-grid/selection-cell.js +13 -6
  92. package/esm/components/data-grid/selection-cell.js.map +1 -1
  93. package/esm/components/data-grid/selection-cell.spec.d.ts +2 -0
  94. package/esm/components/data-grid/selection-cell.spec.d.ts.map +1 -0
  95. package/esm/components/data-grid/selection-cell.spec.js +118 -0
  96. package/esm/components/data-grid/selection-cell.spec.js.map +1 -0
  97. package/esm/components/fab.d.ts.map +1 -1
  98. package/esm/components/fab.js +10 -1
  99. package/esm/components/fab.js.map +1 -1
  100. package/esm/components/fab.spec.d.ts +2 -0
  101. package/esm/components/fab.spec.d.ts.map +1 -0
  102. package/esm/components/fab.spec.js +95 -0
  103. package/esm/components/fab.spec.js.map +1 -0
  104. package/esm/components/form.spec.d.ts +2 -0
  105. package/esm/components/form.spec.d.ts.map +1 -0
  106. package/esm/components/form.spec.js +314 -0
  107. package/esm/components/form.spec.js.map +1 -0
  108. package/esm/components/grid.d.ts.map +1 -1
  109. package/esm/components/grid.js +40 -37
  110. package/esm/components/grid.js.map +1 -1
  111. package/esm/components/grid.spec.d.ts +2 -0
  112. package/esm/components/grid.spec.d.ts.map +1 -0
  113. package/esm/components/grid.spec.js +316 -0
  114. package/esm/components/grid.spec.js.map +1 -0
  115. package/esm/components/inputs/autocomplete.spec.d.ts +2 -0
  116. package/esm/components/inputs/autocomplete.spec.d.ts.map +1 -0
  117. package/esm/components/inputs/autocomplete.spec.js +194 -0
  118. package/esm/components/inputs/autocomplete.spec.js.map +1 -0
  119. package/esm/components/inputs/input.d.ts.map +1 -1
  120. package/esm/components/inputs/input.js +141 -109
  121. package/esm/components/inputs/input.js.map +1 -1
  122. package/esm/components/inputs/input.spec.d.ts +2 -0
  123. package/esm/components/inputs/input.spec.d.ts.map +1 -0
  124. package/esm/components/inputs/input.spec.js +577 -0
  125. package/esm/components/inputs/input.spec.js.map +1 -0
  126. package/esm/components/inputs/text-area.d.ts.map +1 -1
  127. package/esm/components/inputs/text-area.js +54 -58
  128. package/esm/components/inputs/text-area.js.map +1 -1
  129. package/esm/components/inputs/text-area.spec.d.ts +2 -0
  130. package/esm/components/inputs/text-area.spec.d.ts.map +1 -0
  131. package/esm/components/inputs/text-area.spec.js +214 -0
  132. package/esm/components/inputs/text-area.spec.js.map +1 -0
  133. package/esm/components/loader.js +1 -1
  134. package/esm/components/loader.js.map +1 -1
  135. package/esm/components/loader.spec.d.ts +2 -0
  136. package/esm/components/loader.spec.d.ts.map +1 -0
  137. package/esm/components/loader.spec.js +251 -0
  138. package/esm/components/loader.spec.js.map +1 -0
  139. package/esm/components/modal.d.ts.map +1 -1
  140. package/esm/components/modal.js +11 -9
  141. package/esm/components/modal.js.map +1 -1
  142. package/esm/components/modal.spec.d.ts +2 -0
  143. package/esm/components/modal.spec.d.ts.map +1 -0
  144. package/esm/components/modal.spec.js +227 -0
  145. package/esm/components/modal.spec.js.map +1 -0
  146. package/esm/components/noty-list.d.ts.map +1 -1
  147. package/esm/components/noty-list.js +39 -40
  148. package/esm/components/noty-list.js.map +1 -1
  149. package/esm/components/noty-list.spec.d.ts +2 -0
  150. package/esm/components/noty-list.spec.d.ts.map +1 -0
  151. package/esm/components/noty-list.spec.js +486 -0
  152. package/esm/components/noty-list.spec.js.map +1 -0
  153. package/esm/components/paper.d.ts.map +1 -1
  154. package/esm/components/paper.js +15 -12
  155. package/esm/components/paper.js.map +1 -1
  156. package/esm/components/paper.spec.d.ts +2 -0
  157. package/esm/components/paper.spec.d.ts.map +1 -0
  158. package/esm/components/paper.spec.js +63 -0
  159. package/esm/components/paper.spec.js.map +1 -0
  160. package/esm/components/skeleton.js +1 -1
  161. package/esm/components/skeleton.js.map +1 -1
  162. package/esm/components/skeleton.spec.d.ts +2 -0
  163. package/esm/components/skeleton.spec.d.ts.map +1 -0
  164. package/esm/components/skeleton.spec.js +159 -0
  165. package/esm/components/skeleton.spec.js.map +1 -0
  166. package/esm/components/styles.spec.d.ts +2 -0
  167. package/esm/components/styles.spec.d.ts.map +1 -0
  168. package/esm/components/styles.spec.js +56 -0
  169. package/esm/components/styles.spec.js.map +1 -0
  170. package/esm/components/suggest/index.d.ts.map +1 -1
  171. package/esm/components/suggest/index.js +74 -83
  172. package/esm/components/suggest/index.js.map +1 -1
  173. package/esm/components/suggest/index.spec.d.ts +2 -0
  174. package/esm/components/suggest/index.spec.d.ts.map +1 -0
  175. package/esm/components/suggest/index.spec.js +515 -0
  176. package/esm/components/suggest/index.spec.js.map +1 -0
  177. package/esm/components/suggest/suggest-input.d.ts.map +1 -1
  178. package/esm/components/suggest/suggest-input.js +16 -17
  179. package/esm/components/suggest/suggest-input.js.map +1 -1
  180. package/esm/components/suggest/suggest-input.spec.d.ts +2 -0
  181. package/esm/components/suggest/suggest-input.spec.d.ts.map +1 -0
  182. package/esm/components/suggest/suggest-input.spec.js +138 -0
  183. package/esm/components/suggest/suggest-input.spec.js.map +1 -0
  184. package/esm/components/suggest/suggest-manager.spec.d.ts +2 -0
  185. package/esm/components/suggest/suggest-manager.spec.d.ts.map +1 -0
  186. package/esm/components/suggest/suggest-manager.spec.js +308 -0
  187. package/esm/components/suggest/suggest-manager.spec.js.map +1 -0
  188. package/esm/components/suggest/suggestion-list.d.ts.map +1 -1
  189. package/esm/components/suggest/suggestion-list.js +43 -48
  190. package/esm/components/suggest/suggestion-list.js.map +1 -1
  191. package/esm/components/suggest/suggestion-list.spec.d.ts +2 -0
  192. package/esm/components/suggest/suggestion-list.spec.d.ts.map +1 -0
  193. package/esm/components/suggest/suggestion-list.spec.js +252 -0
  194. package/esm/components/suggest/suggestion-list.spec.js.map +1 -0
  195. package/esm/components/tabs.d.ts.map +1 -1
  196. package/esm/components/tabs.js +32 -18
  197. package/esm/components/tabs.js.map +1 -1
  198. package/esm/components/tabs.spec.d.ts +2 -0
  199. package/esm/components/tabs.spec.d.ts.map +1 -0
  200. package/esm/components/tabs.spec.js +187 -0
  201. package/esm/components/tabs.spec.js.map +1 -0
  202. package/esm/components/wizard/index.d.ts.map +1 -1
  203. package/esm/components/wizard/index.js +10 -7
  204. package/esm/components/wizard/index.js.map +1 -1
  205. package/esm/components/wizard/index.spec.d.ts +2 -0
  206. package/esm/components/wizard/index.spec.d.ts.map +1 -0
  207. package/esm/components/wizard/index.spec.js +171 -0
  208. package/esm/components/wizard/index.spec.js.map +1 -0
  209. package/esm/services/collection-service.spec.js +391 -2
  210. package/esm/services/collection-service.spec.js.map +1 -1
  211. package/esm/services/css-variable-theme.d.ts.map +1 -1
  212. package/esm/services/css-variable-theme.js +21 -1
  213. package/esm/services/css-variable-theme.js.map +1 -1
  214. package/esm/services/css-variable-theme.spec.d.ts +2 -0
  215. package/esm/services/css-variable-theme.spec.d.ts.map +1 -0
  216. package/esm/services/css-variable-theme.spec.js +169 -0
  217. package/esm/services/css-variable-theme.spec.js.map +1 -0
  218. package/esm/services/default-palette.d.ts +4 -0
  219. package/esm/services/default-palette.d.ts.map +1 -1
  220. package/esm/services/default-palette.js +22 -0
  221. package/esm/services/default-palette.js.map +1 -1
  222. package/esm/services/theme-provider-service.d.ts +59 -1
  223. package/esm/services/theme-provider-service.d.ts.map +1 -1
  224. package/esm/services/theme-provider-service.js.map +1 -1
  225. package/esm/services/theme-provider-service.spec.d.ts +2 -0
  226. package/esm/services/theme-provider-service.spec.d.ts.map +1 -0
  227. package/esm/services/theme-provider-service.spec.js +166 -0
  228. package/esm/services/theme-provider-service.spec.js.map +1 -0
  229. package/package.json +2 -2
  230. package/src/components/animations.spec.ts +299 -0
  231. package/src/components/app-bar-link.spec.tsx +341 -0
  232. package/src/components/app-bar-link.tsx +21 -21
  233. package/src/components/app-bar.spec.tsx +142 -0
  234. package/src/components/app-bar.tsx +22 -22
  235. package/src/components/avatar.spec.tsx +146 -0
  236. package/src/components/avatar.tsx +17 -20
  237. package/src/components/button.spec.tsx +193 -0
  238. package/src/components/button.tsx +162 -197
  239. package/src/components/command-palette/command-palette-input.spec.tsx +320 -0
  240. package/src/components/command-palette/command-palette-input.tsx +19 -22
  241. package/src/components/command-palette/command-palette-manager.spec.ts +470 -0
  242. package/src/components/command-palette/command-palette-suggestion-list.spec.tsx +499 -0
  243. package/src/components/command-palette/command-palette-suggestion-list.tsx +42 -46
  244. package/src/components/command-palette/index.spec.tsx +684 -0
  245. package/src/components/command-palette/index.tsx +107 -136
  246. package/src/components/data-grid/body.spec.tsx +340 -0
  247. package/src/components/data-grid/body.tsx +1 -1
  248. package/src/components/data-grid/data-grid-row.spec.tsx +382 -0
  249. package/src/components/data-grid/data-grid-row.tsx +50 -82
  250. package/src/components/data-grid/data-grid.spec.tsx +939 -0
  251. package/src/components/data-grid/data-grid.tsx +38 -35
  252. package/src/components/data-grid/footer.spec.tsx +344 -0
  253. package/src/components/data-grid/footer.tsx +19 -19
  254. package/src/components/data-grid/header.spec.tsx +563 -0
  255. package/src/components/data-grid/header.tsx +53 -44
  256. package/src/components/data-grid/selection-cell.spec.tsx +150 -0
  257. package/src/components/data-grid/selection-cell.tsx +12 -6
  258. package/src/components/fab.spec.tsx +108 -0
  259. package/src/components/fab.tsx +10 -1
  260. package/src/components/form.spec.tsx +481 -0
  261. package/src/components/grid.spec.tsx +334 -0
  262. package/src/components/grid.tsx +57 -63
  263. package/src/components/inputs/autocomplete.spec.tsx +258 -0
  264. package/src/components/inputs/input.spec.tsx +808 -0
  265. package/src/components/inputs/input.tsx +153 -139
  266. package/src/components/inputs/text-area.spec.tsx +285 -0
  267. package/src/components/inputs/text-area.tsx +53 -79
  268. package/src/components/loader.spec.tsx +346 -0
  269. package/src/components/loader.tsx +1 -1
  270. package/src/components/modal.spec.tsx +304 -0
  271. package/src/components/modal.tsx +11 -9
  272. package/src/components/noty-list.spec.tsx +631 -0
  273. package/src/components/noty-list.tsx +39 -50
  274. package/src/components/paper.spec.tsx +72 -0
  275. package/src/components/paper.tsx +15 -13
  276. package/src/components/skeleton.spec.tsx +219 -0
  277. package/src/components/skeleton.tsx +1 -1
  278. package/src/components/styles.spec.ts +70 -0
  279. package/src/components/suggest/index.spec.tsx +861 -0
  280. package/src/components/suggest/index.tsx +74 -101
  281. package/src/components/suggest/suggest-input.spec.tsx +181 -0
  282. package/src/components/suggest/suggest-input.tsx +16 -24
  283. package/src/components/suggest/suggest-manager.spec.ts +409 -0
  284. package/src/components/suggest/suggestion-list.spec.tsx +334 -0
  285. package/src/components/suggest/suggestion-list.tsx +43 -48
  286. package/src/components/tabs.spec.tsx +236 -0
  287. package/src/components/tabs.tsx +33 -21
  288. package/src/components/wizard/index.spec.tsx +224 -0
  289. package/src/components/wizard/index.tsx +10 -9
  290. package/src/services/collection-service.spec.ts +492 -3
  291. package/src/services/css-variable-theme.spec.ts +204 -0
  292. package/src/services/css-variable-theme.ts +21 -1
  293. package/src/services/default-palette.ts +22 -0
  294. package/src/services/theme-provider-service.spec.ts +195 -0
  295. package/src/services/theme-provider-service.ts +60 -2
@@ -0,0 +1,204 @@
1
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest'
2
+ import { cssVariableTheme, getCssVariable, setCssVariable, useThemeCssVariables } from './css-variable-theme.js'
3
+
4
+ describe('css-variable-theme', () => {
5
+ describe('cssVariableTheme', () => {
6
+ it('should have a name property', () => {
7
+ expect(cssVariableTheme.name).toBe('css-variable-theme')
8
+ })
9
+
10
+ it('should have text properties with CSS variable references', () => {
11
+ expect(cssVariableTheme.text.primary).toBe('var(--shades-theme-text-primary)')
12
+ expect(cssVariableTheme.text.secondary).toBe('var(--shades-theme-text-secondary)')
13
+ expect(cssVariableTheme.text.disabled).toBe('var(--shades-theme-text-disabled)')
14
+ })
15
+
16
+ it('should have background properties with CSS variable references', () => {
17
+ expect(cssVariableTheme.background.default).toBe('var(--shades-theme-background-default)')
18
+ expect(cssVariableTheme.background.paper).toBe('var(--shades-theme-background-paper)')
19
+ })
20
+
21
+ it('should have palette with color variants', () => {
22
+ expect(cssVariableTheme.palette.primary.main).toBe('var(--shades-theme-palette-primary-main)')
23
+ expect(cssVariableTheme.palette.error.main).toBe('var(--shades-theme-palette-error-main)')
24
+ })
25
+ })
26
+
27
+ describe('setCssVariable', () => {
28
+ let testElement: HTMLElement
29
+
30
+ beforeEach(() => {
31
+ testElement = document.createElement('div')
32
+ document.body.appendChild(testElement)
33
+ })
34
+
35
+ afterEach(() => {
36
+ testElement.remove()
37
+ })
38
+
39
+ it('should set CSS variable on element', () => {
40
+ setCssVariable('--test-color', 'red', testElement)
41
+ expect(testElement.style.getPropertyValue('--test-color')).toBe('red')
42
+ })
43
+
44
+ it('should handle var() wrapper in key name', () => {
45
+ setCssVariable('var(--test-padding)', '10px', testElement)
46
+ expect(testElement.style.getPropertyValue('--test-padding')).toBe('10px')
47
+ })
48
+
49
+ it('should set multiple CSS variables on same element', () => {
50
+ setCssVariable('--color-a', 'blue', testElement)
51
+ setCssVariable('--color-b', 'green', testElement)
52
+ expect(testElement.style.getPropertyValue('--color-a')).toBe('blue')
53
+ expect(testElement.style.getPropertyValue('--color-b')).toBe('green')
54
+ })
55
+
56
+ it('should override existing CSS variable', () => {
57
+ setCssVariable('--test-value', 'first', testElement)
58
+ setCssVariable('--test-value', 'second', testElement)
59
+ expect(testElement.style.getPropertyValue('--test-value')).toBe('second')
60
+ })
61
+ })
62
+
63
+ describe('getCssVariable', () => {
64
+ let testElement: HTMLElement
65
+
66
+ beforeEach(() => {
67
+ testElement = document.createElement('div')
68
+ document.body.appendChild(testElement)
69
+ })
70
+
71
+ afterEach(() => {
72
+ testElement.remove()
73
+ })
74
+
75
+ it('should get CSS variable from element', () => {
76
+ testElement.style.setProperty('--test-color', 'red')
77
+ const result = getCssVariable('--test-color', testElement)
78
+ expect(result).toBe('red')
79
+ })
80
+
81
+ it('should handle var() wrapper in key name', () => {
82
+ testElement.style.setProperty('--test-padding', '20px')
83
+ const result = getCssVariable('var(--test-padding)', testElement)
84
+ expect(result).toBe('20px')
85
+ })
86
+
87
+ it('should return empty string for non-existent variable', () => {
88
+ const result = getCssVariable('--non-existent', testElement)
89
+ expect(result).toBe('')
90
+ })
91
+ })
92
+
93
+ describe('useThemeCssVariables', () => {
94
+ let root: HTMLElement
95
+
96
+ beforeEach(() => {
97
+ root = document.documentElement
98
+ })
99
+
100
+ afterEach(() => {
101
+ root.style.cssText = ''
102
+ })
103
+
104
+ it('should set text color CSS variables from theme', () => {
105
+ useThemeCssVariables({
106
+ text: {
107
+ primary: '#ffffff',
108
+ secondary: '#cccccc',
109
+ },
110
+ })
111
+
112
+ expect(root.style.getPropertyValue('--shades-theme-text-primary')).toBe('#ffffff')
113
+ expect(root.style.getPropertyValue('--shades-theme-text-secondary')).toBe('#cccccc')
114
+ })
115
+
116
+ it('should set background CSS variables from theme', () => {
117
+ useThemeCssVariables({
118
+ background: {
119
+ default: '#000000',
120
+ paper: '#111111',
121
+ },
122
+ })
123
+
124
+ expect(root.style.getPropertyValue('--shades-theme-background-default')).toBe('#000000')
125
+ expect(root.style.getPropertyValue('--shades-theme-background-paper')).toBe('#111111')
126
+ })
127
+
128
+ it('should set button CSS variables from theme', () => {
129
+ useThemeCssVariables({
130
+ button: {
131
+ active: '#ff0000',
132
+ hover: '#00ff00',
133
+ },
134
+ })
135
+
136
+ expect(root.style.getPropertyValue('--shades-theme-button-active')).toBe('#ff0000')
137
+ expect(root.style.getPropertyValue('--shades-theme-button-hover')).toBe('#00ff00')
138
+ })
139
+
140
+ it('should set deeply nested palette CSS variables from theme', () => {
141
+ useThemeCssVariables({
142
+ palette: {
143
+ primary: {
144
+ main: '#1976d2',
145
+ light: '#42a5f5',
146
+ dark: '#1565c0',
147
+ },
148
+ error: {
149
+ main: '#d32f2f',
150
+ },
151
+ },
152
+ })
153
+
154
+ expect(root.style.getPropertyValue('--shades-theme-palette-primary-main')).toBe('#1976d2')
155
+ expect(root.style.getPropertyValue('--shades-theme-palette-primary-light')).toBe('#42a5f5')
156
+ expect(root.style.getPropertyValue('--shades-theme-palette-primary-dark')).toBe('#1565c0')
157
+ expect(root.style.getPropertyValue('--shades-theme-palette-error-main')).toBe('#d32f2f')
158
+ })
159
+
160
+ it('should set divider CSS variable from theme', () => {
161
+ useThemeCssVariables({
162
+ divider: 'rgba(255, 255, 255, 0.12)',
163
+ })
164
+
165
+ expect(root.style.getPropertyValue('--shades-theme-divider')).toBe('rgba(255, 255, 255, 0.12)')
166
+ })
167
+
168
+ it('should handle partial theme with mixed nesting levels', () => {
169
+ useThemeCssVariables({
170
+ text: {
171
+ primary: '#fff',
172
+ },
173
+ divider: '#333',
174
+ palette: {
175
+ success: {
176
+ main: '#2e7d32',
177
+ },
178
+ },
179
+ })
180
+
181
+ expect(root.style.getPropertyValue('--shades-theme-text-primary')).toBe('#fff')
182
+ expect(root.style.getPropertyValue('--shades-theme-divider')).toBe('#333')
183
+ expect(root.style.getPropertyValue('--shades-theme-palette-success-main')).toBe('#2e7d32')
184
+ })
185
+
186
+ it('should allow overriding previously set CSS variables', () => {
187
+ useThemeCssVariables({
188
+ text: {
189
+ primary: '#aaa',
190
+ },
191
+ })
192
+
193
+ expect(root.style.getPropertyValue('--shades-theme-text-primary')).toBe('#aaa')
194
+
195
+ useThemeCssVariables({
196
+ text: {
197
+ primary: '#bbb',
198
+ },
199
+ })
200
+
201
+ expect(root.style.getPropertyValue('--shades-theme-text-primary')).toBe('#bbb')
202
+ })
203
+ })
204
+ })
@@ -21,33 +21,51 @@ export const cssVariableTheme: Theme = {
21
21
  palette: {
22
22
  primary: {
23
23
  light: 'var(--shades-theme-palette-primary-light)',
24
+ lightContrast: 'var(--shades-theme-palette-primary-light-contrast)',
24
25
  main: 'var(--shades-theme-palette-primary-main)',
26
+ mainContrast: 'var(--shades-theme-palette-primary-main-contrast)',
25
27
  dark: 'var(--shades-theme-palette-primary-dark)',
28
+ darkContrast: 'var(--shades-theme-palette-primary-dark-contrast)',
26
29
  },
27
30
  secondary: {
28
31
  light: 'var(--shades-theme-palette-secondary-light)',
32
+ lightContrast: 'var(--shades-theme-palette-secondary-light-contrast)',
29
33
  main: 'var(--shades-theme-palette-secondary-main)',
34
+ mainContrast: 'var(--shades-theme-palette-secondary-main-contrast)',
30
35
  dark: 'var(--shades-theme-palette-secondary-dark)',
36
+ darkContrast: 'var(--shades-theme-palette-secondary-dark-contrast)',
31
37
  },
32
38
  error: {
33
39
  light: 'var(--shades-theme-palette-error-light)',
40
+ lightContrast: 'var(--shades-theme-palette-error-light-contrast)',
34
41
  main: 'var(--shades-theme-palette-error-main)',
42
+ mainContrast: 'var(--shades-theme-palette-error-main-contrast)',
35
43
  dark: 'var(--shades-theme-palette-error-dark)',
44
+ darkContrast: 'var(--shades-theme-palette-error-dark-contrast)',
36
45
  },
37
46
  warning: {
38
47
  light: 'var(--shades-theme-palette-warning-light)',
48
+ lightContrast: 'var(--shades-theme-palette-warning-light-contrast)',
39
49
  main: 'var(--shades-theme-palette-warning-main)',
50
+ mainContrast: 'var(--shades-theme-palette-warning-main-contrast)',
40
51
  dark: 'var(--shades-theme-palette-warning-dark)',
52
+ darkContrast: 'var(--shades-theme-palette-warning-dark-contrast)',
41
53
  },
42
54
  info: {
43
55
  light: 'var(--shades-theme-palette-info-light)',
56
+ lightContrast: 'var(--shades-theme-palette-info-light-contrast)',
44
57
  main: 'var(--shades-theme-palette-info-main)',
58
+ mainContrast: 'var(--shades-theme-palette-info-main-contrast)',
45
59
  dark: 'var(--shades-theme-palette-info-dark)',
60
+ darkContrast: 'var(--shades-theme-palette-info-dark-contrast)',
46
61
  },
47
62
  success: {
48
63
  light: 'var(--shades-theme-palette-success-light)',
64
+ lightContrast: 'var(--shades-theme-palette-success-light-contrast)',
49
65
  main: 'var(--shades-theme-palette-success-main)',
66
+ mainContrast: 'var(--shades-theme-palette-success-main-contrast)',
50
67
  dark: 'var(--shades-theme-palette-success-dark)',
68
+ darkContrast: 'var(--shades-theme-palette-success-dark-contrast)',
51
69
  },
52
70
  },
53
71
  divider: 'var(--shades-theme-divider)',
@@ -69,9 +87,11 @@ const assignValue = <T extends object>(
69
87
  ) => {
70
88
  const keys = Object.keys(target) as Array<keyof T>
71
89
  keys.forEach((key) => {
90
+ if (source[key] === undefined) {
91
+ return
92
+ }
72
93
  if (typeof source[key] === 'object' && typeof target[key] === 'object') {
73
94
  assignValue(target[key] as object, source[key] as object, root)
74
- return
75
95
  } else {
76
96
  assignFn(target[key] as string, source[key] as string, root)
77
97
  }
@@ -1,34 +1,56 @@
1
1
  import type { Palette } from './theme-provider-service.js'
2
2
 
3
+ /**
4
+ * Default color palette with semantic colors for the application.
5
+ * Contrast colors are calculated based on WCAG contrast guidelines.
6
+ */
3
7
  export const defaultPalette: Palette = {
4
8
  primary: {
5
9
  light: '#6573c3',
10
+ lightContrast: '#ffffff',
6
11
  main: '#3f51b5',
12
+ mainContrast: '#ffffff',
7
13
  dark: '#2c387e',
14
+ darkContrast: '#ffffff',
8
15
  },
9
16
  secondary: {
10
17
  light: '#4aedc4',
18
+ lightContrast: '#000000',
11
19
  main: '#1de9b6',
20
+ mainContrast: '#000000',
12
21
  dark: '#14a37f',
22
+ darkContrast: '#ffffff',
13
23
  },
14
24
  error: {
15
25
  light: '#e57373',
26
+ lightContrast: '#000000',
16
27
  main: '#f44336',
28
+ mainContrast: '#ffffff',
17
29
  dark: '#a31f1f',
30
+ darkContrast: '#ffffff',
18
31
  },
19
32
  warning: {
20
33
  light: '#ffb74d',
34
+ lightContrast: '#000000',
21
35
  main: '#ff9800',
36
+ mainContrast: '#000000',
22
37
  dark: '#f57c00',
38
+ darkContrast: '#000000',
23
39
  },
24
40
  info: {
25
41
  light: '#64b5f6',
42
+ lightContrast: '#000000',
26
43
  main: '#2196f3',
44
+ mainContrast: '#ffffff',
27
45
  dark: '#1976d2',
46
+ darkContrast: '#ffffff',
28
47
  },
29
48
  success: {
30
49
  light: '#81c784',
50
+ lightContrast: '#000000',
31
51
  main: '#4caf50',
52
+ mainContrast: '#000000',
32
53
  dark: '#388e3c',
54
+ darkContrast: '#ffffff',
33
55
  },
34
56
  }
@@ -0,0 +1,195 @@
1
+ import { beforeEach, describe, expect, it } from 'vitest'
2
+ import { RgbColor, ThemeProviderService } from './theme-provider-service.js'
3
+
4
+ describe('theme-provider-service', () => {
5
+ describe('RgbColor', () => {
6
+ describe('constructor', () => {
7
+ it('should create RgbColor with r, g, b values', () => {
8
+ const color = new RgbColor(255, 128, 64)
9
+ expect(color.r).toBe(255)
10
+ expect(color.g).toBe(128)
11
+ expect(color.b).toBe(64)
12
+ expect(color.a).toBe(1)
13
+ })
14
+
15
+ it('should create RgbColor with alpha value', () => {
16
+ const color = new RgbColor(255, 128, 64, 0.5)
17
+ expect(color.r).toBe(255)
18
+ expect(color.g).toBe(128)
19
+ expect(color.b).toBe(64)
20
+ expect(color.a).toBe(0.5)
21
+ })
22
+
23
+ it('should default alpha to 1', () => {
24
+ const color = new RgbColor(100, 100, 100)
25
+ expect(color.a).toBe(1)
26
+ })
27
+ })
28
+
29
+ describe('update', () => {
30
+ it('should update r value and return self', () => {
31
+ const color = new RgbColor(100, 100, 100)
32
+ const result = color.update('r', 200)
33
+ expect(result).toBe(color)
34
+ expect(color.r).toBe(200)
35
+ })
36
+
37
+ it('should update g value', () => {
38
+ const color = new RgbColor(100, 100, 100)
39
+ color.update('g', 150)
40
+ expect(color.g).toBe(150)
41
+ })
42
+
43
+ it('should update b value', () => {
44
+ const color = new RgbColor(100, 100, 100)
45
+ color.update('b', 50)
46
+ expect(color.b).toBe(50)
47
+ })
48
+
49
+ it('should update a value', () => {
50
+ const color = new RgbColor(100, 100, 100, 1)
51
+ color.update('a', 0.7)
52
+ expect(color.a).toBe(0.7)
53
+ })
54
+
55
+ it('should allow chained updates', () => {
56
+ const color = new RgbColor(0, 0, 0)
57
+ color.update('r', 255).update('g', 128).update('b', 64)
58
+ expect(color.r).toBe(255)
59
+ expect(color.g).toBe(128)
60
+ expect(color.b).toBe(64)
61
+ })
62
+ })
63
+
64
+ describe('toString', () => {
65
+ it('should return rgba format string', () => {
66
+ const color = new RgbColor(255, 128, 64, 0.5)
67
+ expect(color.toString()).toBe('rgba(255,128,64,0.5)')
68
+ })
69
+
70
+ it('should handle full opacity', () => {
71
+ const color = new RgbColor(0, 0, 0, 1)
72
+ expect(color.toString()).toBe('rgba(0,0,0,1)')
73
+ })
74
+
75
+ it('should handle zero alpha', () => {
76
+ const color = new RgbColor(255, 255, 255, 0)
77
+ expect(color.toString()).toBe('rgba(255,255,255,0)')
78
+ })
79
+ })
80
+ })
81
+
82
+ describe('ThemeProviderService', () => {
83
+ let service: ThemeProviderService
84
+
85
+ beforeEach(() => {
86
+ service = new ThemeProviderService()
87
+ })
88
+
89
+ describe('getRgbFromColorString', () => {
90
+ it('should parse 6-digit hex color', () => {
91
+ const result = service.getRgbFromColorString('#ff8040')
92
+ expect(result.r).toBe(255)
93
+ expect(result.g).toBe(128)
94
+ expect(result.b).toBe(64)
95
+ })
96
+
97
+ it('should parse 6-digit hex color with lowercase', () => {
98
+ const result = service.getRgbFromColorString('#3f51b5')
99
+ expect(result.r).toBe(63)
100
+ expect(result.g).toBe(81)
101
+ expect(result.b).toBe(181)
102
+ })
103
+
104
+ it('should parse 6-digit hex color with uppercase', () => {
105
+ const result = service.getRgbFromColorString('#FF0000')
106
+ expect(result.r).toBe(255)
107
+ expect(result.g).toBe(0)
108
+ expect(result.b).toBe(0)
109
+ })
110
+
111
+ it('should parse 3-digit hex color', () => {
112
+ const result = service.getRgbFromColorString('#f80')
113
+ expect(result.r).toBe(255)
114
+ expect(result.g).toBe(136)
115
+ expect(result.b).toBe(0)
116
+ })
117
+
118
+ it('should parse 3-digit hex white', () => {
119
+ const result = service.getRgbFromColorString('#fff')
120
+ expect(result.r).toBe(255)
121
+ expect(result.g).toBe(255)
122
+ expect(result.b).toBe(255)
123
+ })
124
+
125
+ it('should parse 3-digit hex black', () => {
126
+ const result = service.getRgbFromColorString('#000')
127
+ expect(result.r).toBe(0)
128
+ expect(result.g).toBe(0)
129
+ expect(result.b).toBe(0)
130
+ })
131
+
132
+ it('should parse rgba color', () => {
133
+ const result = service.getRgbFromColorString('rgba(255,128,64,0.5)')
134
+ expect(result.r).toBe(255)
135
+ expect(result.g).toBe(128)
136
+ expect(result.b).toBe(64)
137
+ expect(result.a).toBe(0)
138
+ })
139
+
140
+ it('should parse rgba color with spaces', () => {
141
+ const result = service.getRgbFromColorString('rgba(100, 150, 200, 1)')
142
+ expect(result.r).toBe(100)
143
+ expect(result.g).toBe(150)
144
+ expect(result.b).toBe(200)
145
+ expect(result.a).toBe(1)
146
+ })
147
+
148
+ it('should throw error for unsupported color format', () => {
149
+ expect(() => service.getRgbFromColorString('red')).toThrow("Color format 'red' is not supported.'")
150
+ })
151
+
152
+ it('should throw error for rgb format without alpha', () => {
153
+ expect(() => service.getRgbFromColorString('rgb(255, 0, 0)')).toThrow()
154
+ })
155
+
156
+ it('should throw error for invalid hex length', () => {
157
+ expect(() => service.getRgbFromColorString('#12345')).toThrow()
158
+ })
159
+ })
160
+
161
+ describe('getTextColor', () => {
162
+ it('should return dark text for light background', () => {
163
+ const result = service.getTextColor('#ffffff')
164
+ expect(result).toBe('#000000')
165
+ })
166
+
167
+ it('should return light text for dark background', () => {
168
+ const result = service.getTextColor('#000000')
169
+ expect(result).toBe('#FFFFFF')
170
+ })
171
+
172
+ it('should return custom bright color for light background', () => {
173
+ const result = service.getTextColor('#ffffff', '#333333', '#eeeeee')
174
+ expect(result).toBe('#333333')
175
+ })
176
+
177
+ it('should return custom dark color for dark background', () => {
178
+ const result = service.getTextColor('#000000', '#333333', '#eeeeee')
179
+ expect(result).toBe('#eeeeee')
180
+ })
181
+ })
182
+
183
+ describe('theme management', () => {
184
+ it('should have initial theme assigned', () => {
185
+ expect(service.getAssignedTheme()).toBeDefined()
186
+ expect(service.getAssignedTheme().name).toBe('css-variable-theme')
187
+ })
188
+
189
+ it('should expose cssVariableTheme as theme property', () => {
190
+ expect(service.theme).toBeDefined()
191
+ expect(service.theme.name).toBe('css-variable-theme')
192
+ })
193
+ })
194
+ })
195
+ })
@@ -2,48 +2,106 @@ import { Injectable } from '@furystack/inject'
2
2
  import { EventHub, type DeepPartial } from '@furystack/utils'
3
3
  import { cssVariableTheme, getCssVariable, useThemeCssVariables } from './css-variable-theme.js'
4
4
 
5
- export type Color = string // `#${string}` | `rgba(${number},${number},${number},${number})` |
5
+ /**
6
+ * Represents a CSS color value.
7
+ * Can be a hex color, rgba, or CSS variable reference.
8
+ * @example '#3f51b5', 'rgba(255, 255, 255, 0.7)', 'var(--my-color)'
9
+ */
10
+ export type Color = string
6
11
 
12
+ /**
13
+ * Color variants for a palette color with their corresponding contrast text colors.
14
+ * Each variant (light, main, dark) has an associated contrast color that should be
15
+ * used for text or icons displayed on top of that variant's background.
16
+ */
7
17
  export type ColorVariants = {
8
- main: Color
18
+ /** The lighter shade of the color */
9
19
  light: Color
20
+ /** Text/icon color that contrasts well with the light variant */
21
+ lightContrast: Color
22
+ /** The primary/default shade of the color */
23
+ main: Color
24
+ /** Text/icon color that contrasts well with the main variant */
25
+ mainContrast: Color
26
+ /** The darker shade of the color */
10
27
  dark: Color
28
+ /** Text/icon color that contrasts well with the dark variant */
29
+ darkContrast: Color
11
30
  }
12
31
 
32
+ /**
33
+ * The color palette containing semantic colors for the application.
34
+ * Each color has light, main, and dark variants with corresponding contrast colors.
35
+ */
13
36
  export interface Palette {
37
+ /** Primary brand color, used for main actions and emphasis */
14
38
  primary: ColorVariants
39
+ /** Secondary brand color, used for less prominent actions */
15
40
  secondary: ColorVariants
41
+ /** Color indicating errors or destructive actions */
16
42
  error: ColorVariants
43
+ /** Color indicating warnings or caution */
17
44
  warning: ColorVariants
45
+ /** Color indicating success or positive outcomes */
18
46
  success: ColorVariants
47
+ /** Color for informational content */
19
48
  info: ColorVariants
20
49
  }
21
50
 
51
+ /**
52
+ * Text color definitions for different emphasis levels.
53
+ */
22
54
  export interface Text {
55
+ /** High-emphasis text color for important content */
23
56
  primary: Color
57
+ /** Medium-emphasis text color for secondary content */
24
58
  secondary: Color
59
+ /** Low-emphasis text color for disabled or hint text */
25
60
  disabled: Color
26
61
  }
27
62
 
63
+ /**
64
+ * Button-specific color definitions for various states.
65
+ */
28
66
  export interface ButtonColor {
67
+ /** Color when button is actively pressed */
29
68
  active: Color
69
+ /** Background color on hover */
30
70
  hover: Color
71
+ /** Background color when selected/checked */
31
72
  selected: Color
73
+ /** Text color when button is disabled */
32
74
  disabled: Color
75
+ /** Background color when button is disabled */
33
76
  disabledBackground: Color
34
77
  }
35
78
 
79
+ /**
80
+ * Background color definitions for different surface levels.
81
+ */
36
82
  export interface Background {
83
+ /** Default page/app background color */
37
84
  default: Color
85
+ /** Elevated surface background (cards, dialogs, etc.) */
38
86
  paper: Color
39
87
  }
40
88
 
89
+ /**
90
+ * Complete theme definition containing all color tokens for the application.
91
+ * Themes can be switched at runtime to support light/dark modes or custom branding.
92
+ */
41
93
  export interface Theme {
94
+ /** Unique identifier for the theme */
42
95
  name: string
96
+ /** Semantic color palette */
43
97
  palette: Palette
98
+ /** Text colors for different emphasis levels */
44
99
  text: Text
100
+ /** Button-specific colors */
45
101
  button: ButtonColor
102
+ /** Background colors */
46
103
  background: Background
104
+ /** Color for dividers and borders */
47
105
  divider: Color
48
106
  }
49
107