@furystack/shades 11.0.35 → 12.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 (215) hide show
  1. package/CHANGELOG.md +337 -0
  2. package/README.md +99 -13
  3. package/esm/compile-route.spec.d.ts +2 -0
  4. package/esm/compile-route.spec.d.ts.map +1 -0
  5. package/esm/compile-route.spec.js +34 -0
  6. package/esm/compile-route.spec.js.map +1 -0
  7. package/esm/component-factory.spec.js +13 -5
  8. package/esm/component-factory.spec.js.map +1 -1
  9. package/esm/components/index.d.ts +4 -1
  10. package/esm/components/index.d.ts.map +1 -1
  11. package/esm/components/index.js +4 -1
  12. package/esm/components/index.js.map +1 -1
  13. package/esm/components/lazy-load.d.ts +2 -4
  14. package/esm/components/lazy-load.d.ts.map +1 -1
  15. package/esm/components/lazy-load.js +40 -24
  16. package/esm/components/lazy-load.js.map +1 -1
  17. package/esm/components/lazy-load.spec.js +57 -50
  18. package/esm/components/lazy-load.spec.js.map +1 -1
  19. package/esm/components/link-to-route.d.ts +2 -0
  20. package/esm/components/link-to-route.d.ts.map +1 -1
  21. package/esm/components/link-to-route.js +3 -2
  22. package/esm/components/link-to-route.js.map +1 -1
  23. package/esm/components/link-to-route.spec.js +13 -9
  24. package/esm/components/link-to-route.spec.js.map +1 -1
  25. package/esm/components/nested-route-link.d.ts +62 -0
  26. package/esm/components/nested-route-link.d.ts.map +1 -0
  27. package/esm/components/nested-route-link.js +66 -0
  28. package/esm/components/nested-route-link.js.map +1 -0
  29. package/esm/components/nested-route-link.spec.d.ts +2 -0
  30. package/esm/components/nested-route-link.spec.d.ts.map +1 -0
  31. package/esm/components/nested-route-link.spec.js +179 -0
  32. package/esm/components/nested-route-link.spec.js.map +1 -0
  33. package/esm/components/nested-route-types.d.ts +37 -0
  34. package/esm/components/nested-route-types.d.ts.map +1 -0
  35. package/esm/components/nested-route-types.js +2 -0
  36. package/esm/components/nested-route-types.js.map +1 -0
  37. package/esm/components/nested-router.d.ts +103 -0
  38. package/esm/components/nested-router.d.ts.map +1 -0
  39. package/esm/components/nested-router.js +178 -0
  40. package/esm/components/nested-router.js.map +1 -0
  41. package/esm/components/nested-router.spec.d.ts +2 -0
  42. package/esm/components/nested-router.spec.d.ts.map +1 -0
  43. package/esm/components/nested-router.spec.js +659 -0
  44. package/esm/components/nested-router.spec.js.map +1 -0
  45. package/esm/components/route-link.d.ts +4 -0
  46. package/esm/components/route-link.d.ts.map +1 -1
  47. package/esm/components/route-link.js +9 -10
  48. package/esm/components/route-link.js.map +1 -1
  49. package/esm/components/route-link.spec.js +16 -12
  50. package/esm/components/route-link.spec.js.map +1 -1
  51. package/esm/components/router.d.ts +20 -2
  52. package/esm/components/router.d.ts.map +1 -1
  53. package/esm/components/router.js +3 -0
  54. package/esm/components/router.js.map +1 -1
  55. package/esm/components/router.spec.js +75 -74
  56. package/esm/components/router.spec.js.map +1 -1
  57. package/esm/css-generator.d.ts +50 -0
  58. package/esm/css-generator.d.ts.map +1 -0
  59. package/esm/css-generator.js +107 -0
  60. package/esm/css-generator.js.map +1 -0
  61. package/esm/css-generator.spec.d.ts +2 -0
  62. package/esm/css-generator.spec.d.ts.map +1 -0
  63. package/esm/css-generator.spec.js +162 -0
  64. package/esm/css-generator.spec.js.map +1 -0
  65. package/esm/index.d.ts +2 -0
  66. package/esm/index.d.ts.map +1 -1
  67. package/esm/index.js +2 -0
  68. package/esm/index.js.map +1 -1
  69. package/esm/initialize.d.ts +11 -0
  70. package/esm/initialize.d.ts.map +1 -1
  71. package/esm/initialize.js +5 -0
  72. package/esm/initialize.js.map +1 -1
  73. package/esm/jsx.d.ts +83 -2
  74. package/esm/jsx.d.ts.map +1 -1
  75. package/esm/models/children-list.d.ts +5 -1
  76. package/esm/models/children-list.d.ts.map +1 -1
  77. package/esm/models/css-object.d.ts +33 -0
  78. package/esm/models/css-object.d.ts.map +1 -0
  79. package/esm/models/css-object.js +2 -0
  80. package/esm/models/css-object.js.map +1 -0
  81. package/esm/models/index.d.ts +1 -0
  82. package/esm/models/index.d.ts.map +1 -1
  83. package/esm/models/index.js +1 -0
  84. package/esm/models/index.js.map +1 -1
  85. package/esm/models/partial-element.d.ts +12 -2
  86. package/esm/models/partial-element.d.ts.map +1 -1
  87. package/esm/models/render-options.d.ts +89 -3
  88. package/esm/models/render-options.d.ts.map +1 -1
  89. package/esm/models/selection-state.d.ts +4 -0
  90. package/esm/models/selection-state.d.ts.map +1 -1
  91. package/esm/services/location-service.d.ts +11 -0
  92. package/esm/services/location-service.d.ts.map +1 -1
  93. package/esm/services/location-service.js +11 -0
  94. package/esm/services/location-service.js.map +1 -1
  95. package/esm/services/resource-manager.d.ts +24 -0
  96. package/esm/services/resource-manager.d.ts.map +1 -1
  97. package/esm/services/resource-manager.js +30 -0
  98. package/esm/services/resource-manager.js.map +1 -1
  99. package/esm/services/resource-manager.spec.js +93 -0
  100. package/esm/services/resource-manager.spec.js.map +1 -1
  101. package/esm/services/screen-service.d.ts +81 -4
  102. package/esm/services/screen-service.d.ts.map +1 -1
  103. package/esm/services/screen-service.js +75 -4
  104. package/esm/services/screen-service.js.map +1 -1
  105. package/esm/services/screen-service.spec.js +91 -7
  106. package/esm/services/screen-service.spec.js.map +1 -1
  107. package/esm/shade-component.d.ts +17 -4
  108. package/esm/shade-component.d.ts.map +1 -1
  109. package/esm/shade-component.js +67 -5
  110. package/esm/shade-component.js.map +1 -1
  111. package/esm/shade-host-props-ref.integration.spec.d.ts +2 -0
  112. package/esm/shade-host-props-ref.integration.spec.d.ts.map +1 -0
  113. package/esm/shade-host-props-ref.integration.spec.js +381 -0
  114. package/esm/shade-host-props-ref.integration.spec.js.map +1 -0
  115. package/esm/shade-resources.integration.spec.js +208 -39
  116. package/esm/shade-resources.integration.spec.js.map +1 -1
  117. package/esm/shade.d.ts +34 -15
  118. package/esm/shade.d.ts.map +1 -1
  119. package/esm/shade.js +180 -33
  120. package/esm/shade.js.map +1 -1
  121. package/esm/shade.spec.d.ts +2 -0
  122. package/esm/shade.spec.d.ts.map +1 -0
  123. package/esm/shade.spec.js +198 -0
  124. package/esm/shade.spec.js.map +1 -0
  125. package/esm/shades.integration.spec.js +135 -72
  126. package/esm/shades.integration.spec.js.map +1 -1
  127. package/esm/style-manager.d.ts +65 -0
  128. package/esm/style-manager.d.ts.map +1 -0
  129. package/esm/style-manager.js +95 -0
  130. package/esm/style-manager.js.map +1 -0
  131. package/esm/style-manager.spec.d.ts +2 -0
  132. package/esm/style-manager.spec.d.ts.map +1 -0
  133. package/esm/style-manager.spec.js +179 -0
  134. package/esm/style-manager.spec.js.map +1 -0
  135. package/esm/styled-element.spec.d.ts +2 -0
  136. package/esm/styled-element.spec.d.ts.map +1 -0
  137. package/esm/styled-element.spec.js +86 -0
  138. package/esm/styled-element.spec.js.map +1 -0
  139. package/esm/styled-shade.spec.d.ts +2 -0
  140. package/esm/styled-shade.spec.d.ts.map +1 -0
  141. package/esm/styled-shade.spec.js +66 -0
  142. package/esm/styled-shade.spec.js.map +1 -0
  143. package/esm/svg-types.d.ts +389 -0
  144. package/esm/svg-types.d.ts.map +1 -0
  145. package/esm/svg-types.js +9 -0
  146. package/esm/svg-types.js.map +1 -0
  147. package/esm/svg.d.ts +15 -0
  148. package/esm/svg.d.ts.map +1 -0
  149. package/esm/svg.js +76 -0
  150. package/esm/svg.js.map +1 -0
  151. package/esm/svg.spec.d.ts +2 -0
  152. package/esm/svg.spec.d.ts.map +1 -0
  153. package/esm/svg.spec.js +80 -0
  154. package/esm/svg.spec.js.map +1 -0
  155. package/esm/vnode.d.ts +103 -0
  156. package/esm/vnode.d.ts.map +1 -0
  157. package/esm/vnode.integration.spec.d.ts +2 -0
  158. package/esm/vnode.integration.spec.d.ts.map +1 -0
  159. package/esm/vnode.integration.spec.js +494 -0
  160. package/esm/vnode.integration.spec.js.map +1 -0
  161. package/esm/vnode.js +453 -0
  162. package/esm/vnode.js.map +1 -0
  163. package/esm/vnode.spec.d.ts +2 -0
  164. package/esm/vnode.spec.d.ts.map +1 -0
  165. package/esm/vnode.spec.js +473 -0
  166. package/esm/vnode.spec.js.map +1 -0
  167. package/package.json +3 -3
  168. package/src/compile-route.spec.ts +39 -0
  169. package/src/component-factory.spec.tsx +18 -5
  170. package/src/components/index.ts +4 -1
  171. package/src/components/lazy-load.spec.tsx +82 -75
  172. package/src/components/lazy-load.tsx +49 -27
  173. package/src/components/link-to-route.spec.tsx +25 -21
  174. package/src/components/link-to-route.tsx +4 -2
  175. package/src/components/nested-route-link.spec.tsx +303 -0
  176. package/src/components/nested-route-link.tsx +100 -0
  177. package/src/components/nested-route-types.ts +42 -0
  178. package/src/components/nested-router.spec.tsx +817 -0
  179. package/src/components/nested-router.tsx +256 -0
  180. package/src/components/route-link.spec.tsx +22 -18
  181. package/src/components/route-link.tsx +10 -10
  182. package/src/components/router.spec.tsx +109 -108
  183. package/src/components/router.tsx +15 -2
  184. package/src/css-generator.spec.ts +183 -0
  185. package/src/css-generator.ts +117 -0
  186. package/src/index.ts +2 -0
  187. package/src/initialize.ts +12 -0
  188. package/src/jsx.ts +129 -2
  189. package/src/models/children-list.ts +7 -1
  190. package/src/models/css-object.ts +34 -0
  191. package/src/models/index.ts +1 -0
  192. package/src/models/partial-element.ts +13 -2
  193. package/src/models/render-options.ts +90 -3
  194. package/src/models/selection-state.ts +4 -0
  195. package/src/services/location-service.tsx +11 -0
  196. package/src/services/resource-manager.spec.ts +116 -0
  197. package/src/services/resource-manager.ts +30 -0
  198. package/src/services/screen-service.spec.ts +109 -7
  199. package/src/services/screen-service.ts +81 -4
  200. package/src/shade-component.ts +72 -6
  201. package/src/shade-host-props-ref.integration.spec.tsx +460 -0
  202. package/src/shade-resources.integration.spec.tsx +276 -52
  203. package/src/shade.spec.tsx +239 -0
  204. package/src/shade.ts +211 -56
  205. package/src/shades.integration.spec.tsx +154 -80
  206. package/src/style-manager.spec.ts +229 -0
  207. package/src/style-manager.ts +104 -0
  208. package/src/styled-element.spec.tsx +117 -0
  209. package/src/styled-shade.spec.ts +86 -0
  210. package/src/svg-types.ts +437 -0
  211. package/src/svg.spec.ts +89 -0
  212. package/src/svg.ts +78 -0
  213. package/src/vnode.integration.spec.tsx +657 -0
  214. package/src/vnode.spec.ts +579 -0
  215. package/src/vnode.ts +508 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,342 @@
1
1
  # Changelog
2
2
 
3
+ ## [12.0.0] - 2026-02-09
4
+
5
+ ### 📝 Documentation
6
+
7
+ ### ScreenService API Documentation
8
+
9
+ Improved JSDoc documentation for `ScreenService` with usage examples for responsive UI development.
10
+
11
+ **Documented APIs:**
12
+
13
+ - `screenSize.atLeast[size]` - Observable breakpoint detection
14
+ - `orientation` - Observable screen orientation tracking
15
+ - `breakpoints` - Breakpoint threshold definitions
16
+
17
+ **Breakpoint Thresholds:**
18
+
19
+ - `xs`: 0px+ (all sizes)
20
+ - `sm`: 600px+ (small tablets and up)
21
+ - `md`: 960px+ (tablets and up)
22
+ - `lg`: 1280px+ (desktops and up)
23
+ - `xl`: 1920px+ (large desktops)
24
+
25
+ ### useObservable Documentation
26
+
27
+ Enhanced `useObservable` JSDoc with examples for the `onChange` callback option.
28
+
29
+ ### 🧪 Tests
30
+
31
+ - Added integration tests for Shade resource management (`useObservable`, `useDisposable`)
32
+ - Added tests for `ScreenService` breakpoints, observables, and disposal
33
+ - Updated unit and integration tests to use `updateComponent()` instead of the removed `callConstructed()`
34
+ - Replaced `constructed` callback test with `useDisposable` cleanup test
35
+ - Updated integration tests to use `flushUpdates()` for asserting DOM state after microtask-based rendering
36
+ - Added test for batching multiple synchronous observable changes into a single render
37
+ - Added test for coalescing multiple `updateComponent()` calls into a single render pass
38
+ - Added tests for `NestedRouter` covering route matching, nested layouts, lifecycle hooks, `notFound` fallback, and URL parameter extraction
39
+ - Added tests for `NestedRouteLink` covering SPA navigation, parameterized route compilation, and `createNestedRouteLink` type constraints
40
+ - Refactored existing `Router`, `LazyLoad`, `LinkToRoute`, `RouteLink`, and integration tests to use `usingAsync` for proper `Injector` disposal
41
+ - Added `vnode.spec.ts` with unit tests for VNode creation, flattening, mounting, patching, prop diffing, and unmounting
42
+ - Added `vnode.integration.spec.tsx` with integration tests covering VNode reconciliation within Shade components
43
+ - Added `shade-host-props-ref.integration.spec.tsx` with tests for `useHostProps` and `useRef` behaviors
44
+ - Added tests for `ResourceManager.useObservable` observable switching behavior when a different observable reference is passed for the same key
45
+ - Updated existing integration tests to use `flushUpdates()` and the new API
46
+
47
+ ### 💥 Breaking Changes
48
+
49
+ ### Removed `constructed` callback from `Shade()`
50
+
51
+ The `constructed` option has been removed from the `Shade()` component definition. The `callConstructed()` method has also been removed from the `JSX.Element` interface. Any cleanup function returned by `constructed` is no longer supported.
52
+
53
+ **Migration:** Move initialization logic into `render` using `useDisposable()` for one-time setup that needs cleanup.
54
+
55
+ ```typescript
56
+ // ❌ Before
57
+ const MyComponent = Shade({
58
+ shadowDomName: 'my-component',
59
+ constructed: ({ element }) => {
60
+ const listener = () => { /* ... */ }
61
+ window.addEventListener('click', listener)
62
+ return () => window.removeEventListener('click', listener)
63
+ },
64
+ render: () => <div>Hello</div>,
65
+ })
66
+
67
+ // ✅ After
68
+ const MyComponent = Shade({
69
+ shadowDomName: 'my-component',
70
+ render: ({ element, useDisposable }) => {
71
+ useDisposable('click-handler', () => {
72
+ const listener = () => { /* ... */ }
73
+ window.addEventListener('click', listener)
74
+ return { [Symbol.dispose]: () => window.removeEventListener('click', listener) }
75
+ })
76
+ return <div>Hello</div>
77
+ },
78
+ })
79
+ ```
80
+
81
+ **Impact:** All components using the `constructed` callback must be updated.
82
+
83
+ ### Rendering Engine Replaced with VNode-Based Reconciliation
84
+
85
+ The rendering engine has been replaced with a lightweight VNode-based reconciler. Instead of creating real DOM elements during each render and diffing them, the JSX factory now produces VNode descriptors during render mode. A reconciler diffs the previous VNode tree against the new one and applies surgical DOM updates using tracked element references.
86
+
87
+ **Impact:** All components using the `element` parameter from `RenderOptions` need to be updated. Components using `onAttach` or `onDetach` lifecycle hooks need to migrate to `useDisposable`.
88
+
89
+ ### `element` Removed from `RenderOptions`
90
+
91
+ The `element` property (direct reference to the host custom element) has been removed from `RenderOptions`. Components should no longer imperatively mutate the host element.
92
+
93
+ **Migration:** Use the new `useHostProps` hook to declaratively set attributes and styles on the host element.
94
+
95
+ ```typescript
96
+ // ❌ Before
97
+ render: ({ element, props }) => {
98
+ element.setAttribute('data-variant', props.variant)
99
+ element.style.setProperty('--color', colors.main)
100
+ // ...
101
+ }
102
+
103
+ // ✅ After
104
+ render: ({ useHostProps, props }) => {
105
+ useHostProps({
106
+ 'data-variant': props.variant,
107
+ style: { '--color': colors.main },
108
+ })
109
+ // ...
110
+ }
111
+ ```
112
+
113
+ ### `onAttach` and `onDetach` Lifecycle Hooks Removed
114
+
115
+ The `onAttach` and `onDetach` component lifecycle hooks have been removed. Use `useDisposable` or `connectedCallback`/`disconnectedCallback` for setup and teardown logic.
116
+
117
+ ```typescript
118
+ // ❌ Before
119
+ Shade({
120
+ shadowDomName: 'my-component',
121
+ onAttach: ({ element }) => {
122
+ /* setup */
123
+ },
124
+ onDetach: ({ element }) => {
125
+ /* cleanup */
126
+ },
127
+ render: ({ props }) => {
128
+ /* ... */
129
+ },
130
+ })
131
+
132
+ // ✅ After
133
+ Shade({
134
+ shadowDomName: 'my-component',
135
+ render: ({ props, useDisposable }) => {
136
+ useDisposable('setup', () => {
137
+ /* setup */
138
+ return {
139
+ [Symbol.dispose]: () => {
140
+ /* cleanup */
141
+ },
142
+ }
143
+ })
144
+ // ...
145
+ },
146
+ })
147
+ ```
148
+
149
+ ### 📚 Documentation
150
+
151
+ - Updated README to remove references to `constructed` and `initialState`, and to recommend `useDisposable` for one-time setup with cleanup
152
+ - Removed outdated note from README about DOM morphing behavior
153
+
154
+ ### ⚠️ Changed
155
+
156
+ ### Behavioral change: `updateComponent()` is now asynchronous
157
+
158
+ `updateComponent()` no longer renders synchronously. Any code that calls `updateComponent()` (or triggers it via observable changes) and immediately inspects the DOM will now see stale state. Use `await flushUpdates()` to wait for pending renders to complete before reading the DOM.
159
+
160
+ ### ⚡ Performance
161
+
162
+ ### Microtask-based batched component updates
163
+
164
+ `updateComponent()` now schedules renders via `queueMicrotask()` instead of executing them synchronously. Multiple calls to `updateComponent()` within the same synchronous block (e.g. several observable changes) are coalesced into a single render pass, reducing unnecessary DOM updates.
165
+
166
+ - Component updates are batched via `queueMicrotask`, coalescing multiple `updateComponent()` calls into a single render pass
167
+ - VNode props are shallow-compared to skip unnecessary DOM updates
168
+ - Style diffing patches only changed properties instead of replacing the entire style
169
+
170
+ ### ✨ Features
171
+
172
+ ### New `NestedRouter` component
173
+
174
+ Added a `NestedRouter` component that supports hierarchical route definitions with parent/child relationships. Parent routes receive an `outlet` prop containing the rendered child route, enabling layout composition patterns (e.g. a shared layout wrapping page-specific content).
175
+
176
+ Routes are defined as a nested `Record` where keys are URL patterns (using `path-to-regexp`). The matching algorithm builds a chain from outermost to innermost route, then renders inside-out so each parent wraps its child.
177
+
178
+ **Usage:**
179
+
180
+ ```typescript
181
+ import { NestedRouter, createComponent } from '@furystack/shades'
182
+
183
+ const routes = {
184
+ '/': {
185
+ component: ({ outlet }) => (
186
+ <div>
187
+ <nav>Shared Navigation</nav>
188
+ {outlet}
189
+ </div>
190
+ ),
191
+ children: {
192
+ '/': { component: () => <div>Home</div> },
193
+ '/about': { component: () => <div>About</div> },
194
+ },
195
+ },
196
+ }
197
+
198
+ <NestedRouter routes={routes} notFound={<div>404</div>} />
199
+ ```
200
+
201
+ Key features:
202
+
203
+ - Hierarchical route matching with `buildMatchChain()` - matches from outermost to innermost route
204
+ - Lifecycle hooks (`onVisit`/`onLeave`) scoped per route level, only triggered for routes that actually change
205
+ - `findDivergenceIndex()` for efficient diffing - sibling navigation only triggers leave/visit for the changed subtree
206
+ - `notFound` fallback when no routes match
207
+
208
+ ### New `NestedRouteLink` and `createNestedRouteLink` components
209
+
210
+ Added `NestedRouteLink` for SPA navigation with type-safe parameterized routes. It intercepts clicks to use `history.pushState` and compiles URL parameters (e.g. `/users/:id`) automatically.
211
+
212
+ `createNestedRouteLink()` creates a narrowed version of `NestedRouteLink` constrained to a specific route tree, so TypeScript only accepts valid paths and requires `params` when the route has parameters.
213
+
214
+ **Usage:**
215
+
216
+ ```typescript
217
+ import { NestedRouteLink, createNestedRouteLink } from '@furystack/shades'
218
+
219
+ // Basic usage — params are inferred from the href pattern
220
+ <NestedRouteLink href="/users/:id" params={{ id: '123' }}>User</NestedRouteLink>
221
+
222
+ // Type-safe usage — constrained to a route tree
223
+ const AppLink = createNestedRouteLink<typeof appRoutes>()
224
+ <AppLink href="/buttons">Buttons</AppLink>
225
+ ```
226
+
227
+ ### Route type utilities
228
+
229
+ Added type-level utilities for working with nested route trees:
230
+
231
+ - `ExtractRoutePaths<T>` - recursively extracts all valid full URL paths from a nested route tree
232
+ - `ExtractRouteParams<T>` - extracts parameter names from a URL pattern into a typed record
233
+ - `ConcatPaths<Parent, Child>` - concatenates parent and child paths handling the `/` root
234
+ - `UrlTree<TPaths>` - validates URL constant objects against a set of valid paths
235
+
236
+ ### VNode-Based Reconciliation Engine
237
+
238
+ Introduced a new `vnode.ts` module that implements a VNode-based virtual DOM reconciler. The JSX factory produces lightweight VNode descriptors during component renders, which are then diffed against the previous tree to apply minimal DOM updates. This eliminates the overhead of creating and diffing real DOM elements on every render cycle.
239
+
240
+ ### `useHostProps` Hook
241
+
242
+ Added `useHostProps` to `RenderOptions`, enabling components to declaratively set attributes, data attributes, ARIA attributes, event handlers, and styles (including CSS custom properties) on the host custom element. It can be called multiple times per render; calls are merged and diffed against the previous render.
243
+
244
+ ### `useRef` Hook
245
+
246
+ Added `useRef` to `RenderOptions`, allowing components to create mutable ref objects that capture references to child DOM elements. Refs are cached by key and persist across renders. The ref's `current` property is set to the DOM element after mount and `null` on unmount.
247
+
248
+ ```typescript
249
+ const inputRef = useRef<HTMLInputElement>('input')
250
+ // In JSX:
251
+ <input ref={inputRef} />
252
+ // Later:
253
+ inputRef.current?.focus()
254
+ ```
255
+
256
+ ### `ref` Prop Support on Intrinsic Elements
257
+
258
+ Added a `ref` property to `PartialElement<T>`, enabling `ref` objects to be passed to any intrinsic JSX element (e.g., `<div ref={myRef} />`). The VNode reconciler handles mounting and unmounting refs automatically.
259
+
260
+ ### Native SVG Element Support
261
+
262
+ Added first-class SVG support with proper namespace handling. SVG elements are now created with `createElementNS` using the correct SVG namespace, and attributes are applied via `setAttribute` instead of property assignment. This includes:
263
+
264
+ - A new `svg.ts` module with SVG tag detection and namespace constants
265
+ - A new `svg-types.ts` module with typed SVG attribute interfaces for all standard SVG elements (shapes, gradients, filters, animations, etc.)
266
+ - Updated `IntrinsicElements` with proper typed SVG element definitions
267
+
268
+ ### `flushUpdates` Utility
269
+
270
+ Added `flushUpdates()` — a test utility that returns a promise resolving after the current microtask queue has been processed, enabling tests to await batched component updates before asserting DOM state.
271
+
272
+ - Exported `flushUpdates()` utility that returns a promise resolving after the current microtask queue is processed, allowing tests to reliably wait for batched renders to complete
273
+ - Extended `attachDataAttributes` to forward `aria-*` attributes from component props to the DOM element, enabling accessible components built with Shades
274
+
275
+ ### 🗑️ Deprecated
276
+
277
+ - Deprecated `Router`, `Route`, `RouterProps`, and `RouterState` in favor of `NestedRouter` and its types
278
+ - Deprecated `RouteLink` and `RouteLinkProps` in favor of `NestedRouteLink`
279
+ - Deprecated `LinkToRoute` and `LinkToRouteProps` in favor of `NestedRouteLink`
280
+
281
+ ### 🐛 Bug Fixes
282
+
283
+ - Fixed `onLeave` lifecycle hooks not firing correctly when navigating between nested routes
284
+
285
+ ### ♻️ Refactoring
286
+
287
+ - `appendChild` in `shade-component.ts` now accepts `Element | DocumentFragment` instead of `HTMLElement | DocumentFragment` for broader compatibility
288
+ - `createComponent` now acts as a render-mode switch: when in render mode it produces VNode descriptors, otherwise it creates real DOM elements as before
289
+
290
+ ### ⬆️ Dependencies
291
+
292
+ - Peer dependency on `@furystack/shades` updated to the new major version for downstream packages
293
+
294
+ ## [11.1.0] - 2026-02-01
295
+
296
+ ### ✨ Features
297
+
298
+ ### CSS Property for Component-Level Styling
299
+
300
+ Added a new `css` property to `Shade()` components that enables defining component-level styles with support for pseudo-selectors and nested selectors. This provides a cleaner alternative to using `useState` for hover/focus states.
301
+
302
+ **Key benefits:**
303
+
304
+ - Define `:hover`, `:active`, `:focus`, and `:disabled` states declaratively
305
+ - Support for nested selectors (e.g., `& .className`, `& > div`)
306
+ - Styles are injected as a shared stylesheet, reducing DOM overhead
307
+ - Type-safe with the new `CSSObject` type
308
+
309
+ **Usage:**
310
+
311
+ ```typescript
312
+ const Button = Shade({
313
+ shadowDomName: 'my-button',
314
+ css: {
315
+ padding: '12px 24px',
316
+ backgroundColor: 'blue',
317
+ cursor: 'pointer',
318
+ '&:hover': { backgroundColor: 'darkblue' },
319
+ '&:disabled': { opacity: '0.5', cursor: 'not-allowed' },
320
+ '& .icon': { marginRight: '8px' }
321
+ },
322
+ render: ({ children }) => <button>{children}</button>
323
+ })
324
+ ```
325
+
326
+ - Added `CSSObject` type - type definition for CSS styles with nested selector support
327
+ - Added `StyleManager` - singleton that manages CSS injection and deduplication for components
328
+ - Added `generateCSS()` - utility function to convert `CSSObject` to CSS strings
329
+ - Extended support for customized built-in elements (e.g., `a[is="my-link"]` selectors)
330
+
331
+ ### 📚 Documentation
332
+
333
+ - Updated README with documentation for the new `css` property and styling patterns
334
+
335
+ ### 🧪 Tests
336
+
337
+ - Added tests for `generateCSS()` covering camelCase conversion, pseudo-selectors, and nested selectors
338
+ - Added tests for `StyleManager` covering component registration, deduplication, and customized built-in elements
339
+
3
340
  ## [11.0.35] - 2026-01-26
4
341
 
5
342
  ### 🔧 Chores
package/README.md CHANGED
@@ -18,24 +18,110 @@ You can check the [@furystack/boilerplate](https://github.com/furystack/boilerpl
18
18
 
19
19
  A shade (component) can be constructed from the following properties:
20
20
 
21
- - `render:(options: RenderOptions)=>JSX.Element` – A required method that will be executed on each render.
22
- - `initialState` – A default state that can be updated during the component lifecycle.
23
- - `shadowDomName` – Can be specified as the custom element's name in the DOM.
24
- - `constructed: (options: RenderOptions)=>void` – Optional callback executed after component construction. It can return a cleanup method (e.g., free up resources, dispose value observers, etc.).
25
- - `onAttach: (options: RenderOptions)=>void` – Executed when the component is attached to the DOM.
26
- - `onDetach: (options: RenderOptions)=>void` – Executed when the component is detached from the DOM.
21
+ - `render:(options: RenderOptions)=>JSX.Element` – A required method that will be executed on each render. Use `useDisposable` within render for one-time setup that needs cleanup.
22
+ - `shadowDomName` – The custom element tag name. Must follow Custom Elements naming convention (lowercase, must contain a hyphen).
23
+ - `style` – Optional inline styles applied to each component instance. Use for per-instance overrides.
24
+ - `css` – Optional CSS styles injected as a stylesheet during component registration. Supports pseudo-selectors and nested selectors.
25
+
26
+ ### Styling
27
+
28
+ Shades provides two complementary approaches to styling components:
29
+
30
+ #### `style` Property (Inline Styles)
31
+
32
+ The `style` property applies inline styles to each component instance. Use this for:
33
+
34
+ - Per-instance style overrides
35
+ - Dynamic styles that change based on props/state
36
+ - Quick prototyping
37
+
38
+ ```typescript
39
+ const MyComponent = Shade({
40
+ shadowDomName: 'my-component',
41
+ style: {
42
+ display: 'flex',
43
+ padding: '16px',
44
+ },
45
+ render: () => <div>Content</div>,
46
+ })
47
+
48
+ // Override styles on specific instances
49
+ <MyComponent style={{ marginTop: '20px' }} />
50
+ ```
51
+
52
+ #### `css` Property (Stylesheet Injection)
53
+
54
+ The `css` property injects CSS rules into a stylesheet once per component type. Use this for:
55
+
56
+ - Component-level default styles
57
+ - Pseudo-selectors (`:hover`, `:active`, `:focus`, `:disabled`, etc.)
58
+ - Nested selectors (child elements, class names)
59
+ - Better performance (styles injected once, not per-instance)
60
+
61
+ ```typescript
62
+ const Button = Shade({
63
+ shadowDomName: 'my-button',
64
+ css: {
65
+ padding: '12px 24px',
66
+ backgroundColor: 'blue',
67
+ color: 'white',
68
+ border: 'none',
69
+ cursor: 'pointer',
70
+ transition: 'all 0.2s ease',
71
+
72
+ '&:hover': {
73
+ backgroundColor: 'darkblue',
74
+ },
75
+
76
+ '&:active': {
77
+ transform: 'scale(0.98)',
78
+ },
79
+
80
+ '&:disabled': {
81
+ opacity: '0.5',
82
+ cursor: 'not-allowed',
83
+ },
84
+
85
+ '& .icon': {
86
+ marginRight: '8px',
87
+ },
88
+ },
89
+ render: ({ props }) => (
90
+ <button disabled={props.disabled}>
91
+ {props.icon && <span className="icon">{props.icon}</span>}
92
+ {props.children}
93
+ </button>
94
+ ),
95
+ })
96
+ ```
97
+
98
+ #### When to Use Which
99
+
100
+ | Use Case | `style` | `css` |
101
+ | ------------------------- | ------- | ----- |
102
+ | Hover/focus/active states | ❌ | ✅ |
103
+ | Per-instance overrides | ✅ | ❌ |
104
+ | Nested element styling | ❌ | ✅ |
105
+ | Dynamic values from props | ✅ | ❌ |
106
+ | Component defaults | ⚠️ | ✅ |
107
+
108
+ Both properties can be used together. Inline `style` will override `css` due to CSS specificity rules.
27
109
 
28
110
  ### Render Options
29
111
 
30
- The lifecycle methods receive the following options as a parameter:
112
+ The `render` function receives a `RenderOptions` object with these hooks:
31
113
 
32
- - `props` – The current readonly props object for the element. As props are passed from the parent, it is read-only.
33
- - `getState()` – Returns the current state. The state object is also read-only and immutable and can be updated only with a corresponding method.
34
- - `updateState(newState: TState, skipRender?: boolean)` – Updates (patches) the component state. An optional flag can indicate that this state change shouldn't trigger a re-render (e.g., form input fields change, etc.).
35
- - `injector` – An injector instance. It can be retrieved from the closest parent or specified on the state or props.
114
+ - `props` – The current readonly props object. Passed from the parent, treat as immutable.
115
+ - `injector` – The injector instance, inherited from the closest parent or set explicitly.
36
116
  - `children` – The children element(s) of the component.
37
- - `element` – A reference to the current component's custom element (root).
38
- - `logger` – A specified logger instance with a pre-defined scope.
117
+ - `renderCount` – How many times this component has rendered.
118
+ - `useState(key, initialValue)` – Local state that triggers re-renders on change.
119
+ - `useObservable(key, observable, options?)` – Subscribes to an `ObservableValue`; re-renders on change by default. Provide a custom `onChange` to skip re-renders.
120
+ - `useSearchState(key, initialValue)` – State synced with URL search parameters.
121
+ - `useStoredState(key, initialValue, storageArea?)` – State persisted to `localStorage` or `sessionStorage`.
122
+ - `useDisposable(key, factory)` – Creates a resource that is automatically disposed when the component unmounts.
123
+ - `useHostProps(hostProps)` – Declaratively sets attributes, styles, and CSS variables on the host element. Prefer this over direct DOM manipulation.
124
+ - `useRef(key)` – Creates a ref object for imperative access to child DOM elements.
39
125
 
40
126
  ### Bundled Goodies
41
127
 
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=compile-route.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compile-route.spec.d.ts","sourceRoot":"","sources":["../src/compile-route.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,34 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { compileRoute } from './compile-route.js';
3
+ describe('compile-route', () => {
4
+ describe('compileRoute', () => {
5
+ it('should compile route with string params', () => {
6
+ const result = compileRoute('/users/:id', { id: 'abc123' });
7
+ expect(result).toBe('/users/abc123');
8
+ });
9
+ it('should compile route with numeric params (stringified)', () => {
10
+ const result = compileRoute('/users/:id', { id: 42 });
11
+ expect(result).toBe('/users/42');
12
+ });
13
+ it('should compile route with multiple params', () => {
14
+ const result = compileRoute('/users/:userId/posts/:postId', {
15
+ userId: 'user1',
16
+ postId: 'post2',
17
+ });
18
+ expect(result).toBe('/users/user1/posts/post2');
19
+ });
20
+ it('should handle empty params object for static routes', () => {
21
+ const result = compileRoute('/home', {});
22
+ expect(result).toBe('/home');
23
+ });
24
+ it('should handle params with special characters', () => {
25
+ const result = compileRoute('/search/:query', { query: 'hello-world' });
26
+ expect(result).toBe('/search/hello-world');
27
+ });
28
+ it('should handle boolean params (stringified)', () => {
29
+ const result = compileRoute('/feature/:enabled', { enabled: true });
30
+ expect(result).toBe('/feature/true');
31
+ });
32
+ });
33
+ });
34
+ //# sourceMappingURL=compile-route.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compile-route.spec.js","sourceRoot":"","sources":["../src/compile-route.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;QAC5B,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;YACjD,MAAM,MAAM,GAAG,YAAY,CAAC,YAAY,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAA;YAC3D,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;QACtC,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;YAChE,MAAM,MAAM,GAAG,YAAY,CAAC,YAAY,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA;YACrD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;QAClC,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;YACnD,MAAM,MAAM,GAAG,YAAY,CAAC,8BAA8B,EAAE;gBAC1D,MAAM,EAAE,OAAO;gBACf,MAAM,EAAE,OAAO;aAChB,CAAC,CAAA;YACF,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAA;QACjD,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;YAC7D,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;YACxC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAC9B,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;YACtD,MAAM,MAAM,GAAG,YAAY,CAAC,gBAAgB,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAA;YACvE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAA;QAC5C,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;YACpD,MAAM,MAAM,GAAG,YAAY,CAAC,mBAAmB,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;YACnE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;QACtC,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -2,7 +2,7 @@ import { Injector } from '@furystack/inject';
2
2
  import { describe, expect, it, vi } from 'vitest';
3
3
  import './jsx';
4
4
  import { createComponent } from './shade-component.js';
5
- import { Shade } from './shade.js';
5
+ import { flushUpdates, Shade } from './shade.js';
6
6
  describe('Shades Component Factory', () => {
7
7
  describe('HTML Elements', () => {
8
8
  it('Should create a simple component', () => {
@@ -21,6 +21,12 @@ describe('Shades Component Factory', () => {
21
21
  expect(component).toBeInstanceOf(HTMLDivElement);
22
22
  expect(component.getAttribute('data-testid')).toBe('asd-123');
23
23
  });
24
+ it('Should apply aria attributes', () => {
25
+ const component = (createComponent("div", { "aria-label": "My label", "aria-hidden": "true" }, "a"));
26
+ expect(component).toBeInstanceOf(HTMLDivElement);
27
+ expect(component.getAttribute('aria-label')).toBe('My label');
28
+ expect(component.getAttribute('aria-hidden')).toBe('true');
29
+ });
24
30
  it('Should create a nested component', () => {
25
31
  const component = (createComponent("div", { style: { display: 'flex' } },
26
32
  createComponent("h1", null, "Hi, I'm a header"),
@@ -47,7 +53,7 @@ describe('Shades Component Factory', () => {
47
53
  expect(shade.props).toEqual({});
48
54
  expect(shade.shadeChildren).toEqual([]);
49
55
  });
50
- it('Should render a component with props', () => {
56
+ it('Should render a component with props', async () => {
51
57
  const Example = Shade({
52
58
  shadowDomName: 'example-with-props',
53
59
  render: ({ props }) => createComponent("div", null, props.foo),
@@ -55,12 +61,13 @@ describe('Shades Component Factory', () => {
55
61
  const component = (createComponent("div", null,
56
62
  createComponent(Example, { foo: "example", injector: new Injector() })));
57
63
  const shade = component.firstElementChild;
58
- shade.callConstructed();
64
+ shade.updateComponent();
65
+ await flushUpdates();
59
66
  expect(shade.props.foo).toEqual('example');
60
67
  expect(shade.shadeChildren).toEqual([]);
61
68
  expect(shade.innerHTML).toBe('<div>example</div>');
62
69
  });
63
- it('Should render a component with state', () => {
70
+ it('Should render a component with state', async () => {
64
71
  const Example = Shade({
65
72
  shadowDomName: 'example-with-state',
66
73
  render: ({ useState }) => {
@@ -71,7 +78,8 @@ describe('Shades Component Factory', () => {
71
78
  const component = (createComponent("div", null,
72
79
  createComponent(Example, null)));
73
80
  const shade = component.firstElementChild;
74
- shade.callConstructed();
81
+ shade.updateComponent();
82
+ await flushUpdates();
75
83
  expect(shade.resourceManager.stateObservers.get('foo')?.getValue()).toEqual('example');
76
84
  expect(shade.shadeChildren).toEqual([]);
77
85
  });
@@ -1 +1 @@
1
- {"version":3,"file":"component-factory.spec.js","sourceRoot":"","sources":["../src/component-factory.spec.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"component-factory.spec.js","sourceRoot":"","sources":["../src/component-factory.spec.tsx"],"names":[],"mappings":"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"}
@@ -1,5 +1,8 @@
1
1
  export * from './lazy-load.js';
2
2
  export * from './link-to-route.js';
3
- export * from './router.js';
3
+ export * from './nested-route-link.js';
4
+ export * from './nested-route-types.js';
5
+ export * from './nested-router.js';
4
6
  export * from './route-link.js';
7
+ export * from './router.js';
5
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,oBAAoB,CAAA;AAClC,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,oBAAoB,CAAA;AAClC,cAAc,wBAAwB,CAAA;AACtC,cAAc,yBAAyB,CAAA;AACvC,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA"}
@@ -1,5 +1,8 @@
1
1
  export * from './lazy-load.js';
2
2
  export * from './link-to-route.js';
3
- export * from './router.js';
3
+ export * from './nested-route-link.js';
4
+ export * from './nested-route-types.js';
5
+ export * from './nested-router.js';
4
6
  export * from './route-link.js';
7
+ export * from './router.js';
5
8
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,oBAAoB,CAAA;AAClC,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,oBAAoB,CAAA;AAClC,cAAc,wBAAwB,CAAA;AACtC,cAAc,yBAAyB,CAAA;AACvC,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA"}
@@ -3,11 +3,9 @@ export interface LazyLoadProps {
3
3
  error?: (error: unknown, retry: () => Promise<void>) => JSX.Element;
4
4
  component: () => Promise<JSX.Element>;
5
5
  }
6
- export interface LazyLoadState {
7
- component?: JSX.Element;
8
- error?: unknown;
9
- }
10
6
  export declare const LazyLoad: (props: LazyLoadProps & Omit<Partial<HTMLElement>, "style"> & {
11
7
  style?: Partial<CSSStyleDeclaration>;
8
+ } & {
9
+ ref?: import("../index.js").RefObject<Element>;
12
10
  }, children?: import("../index.js").ChildrenList) => JSX.Element;
13
11
  //# sourceMappingURL=lazy-load.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"lazy-load.d.ts","sourceRoot":"","sources":["../../src/components/lazy-load.tsx"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,GAAG,CAAC,OAAO,CAAA;IACnB,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,OAAO,CAAA;IACnE,SAAS,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;CACtC;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACvB,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,eAAO,MAAM,QAAQ;;gEAyCnB,CAAA"}
1
+ {"version":3,"file":"lazy-load.d.ts","sourceRoot":"","sources":["../../src/components/lazy-load.tsx"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,GAAG,CAAC,OAAO,CAAA;IACnB,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,OAAO,CAAA;IACnE,SAAS,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;CACtC;AAED,eAAO,MAAM,QAAQ;;;;gEAoEnB,CAAA"}