@furystack/shades 11.1.0 → 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.
- package/CHANGELOG.md +291 -0
- package/README.md +13 -13
- package/esm/component-factory.spec.js +13 -5
- package/esm/component-factory.spec.js.map +1 -1
- package/esm/components/index.d.ts +4 -1
- package/esm/components/index.d.ts.map +1 -1
- package/esm/components/index.js +4 -1
- package/esm/components/index.js.map +1 -1
- package/esm/components/lazy-load.d.ts +2 -4
- package/esm/components/lazy-load.d.ts.map +1 -1
- package/esm/components/lazy-load.js +40 -24
- package/esm/components/lazy-load.js.map +1 -1
- package/esm/components/lazy-load.spec.js +57 -50
- package/esm/components/lazy-load.spec.js.map +1 -1
- package/esm/components/link-to-route.d.ts +2 -0
- package/esm/components/link-to-route.d.ts.map +1 -1
- package/esm/components/link-to-route.js +3 -2
- package/esm/components/link-to-route.js.map +1 -1
- package/esm/components/link-to-route.spec.js +13 -9
- package/esm/components/link-to-route.spec.js.map +1 -1
- package/esm/components/nested-route-link.d.ts +62 -0
- package/esm/components/nested-route-link.d.ts.map +1 -0
- package/esm/components/nested-route-link.js +66 -0
- package/esm/components/nested-route-link.js.map +1 -0
- package/esm/components/nested-route-link.spec.d.ts +2 -0
- package/esm/components/nested-route-link.spec.d.ts.map +1 -0
- package/esm/components/nested-route-link.spec.js +179 -0
- package/esm/components/nested-route-link.spec.js.map +1 -0
- package/esm/components/nested-route-types.d.ts +37 -0
- package/esm/components/nested-route-types.d.ts.map +1 -0
- package/esm/components/nested-route-types.js +2 -0
- package/esm/components/nested-route-types.js.map +1 -0
- package/esm/components/nested-router.d.ts +103 -0
- package/esm/components/nested-router.d.ts.map +1 -0
- package/esm/components/nested-router.js +178 -0
- package/esm/components/nested-router.js.map +1 -0
- package/esm/components/nested-router.spec.d.ts +2 -0
- package/esm/components/nested-router.spec.d.ts.map +1 -0
- package/esm/components/nested-router.spec.js +659 -0
- package/esm/components/nested-router.spec.js.map +1 -0
- package/esm/components/route-link.d.ts +4 -0
- package/esm/components/route-link.d.ts.map +1 -1
- package/esm/components/route-link.js +5 -5
- package/esm/components/route-link.js.map +1 -1
- package/esm/components/route-link.spec.js +16 -12
- package/esm/components/route-link.spec.js.map +1 -1
- package/esm/components/router.d.ts +20 -2
- package/esm/components/router.d.ts.map +1 -1
- package/esm/components/router.js +3 -0
- package/esm/components/router.js.map +1 -1
- package/esm/components/router.spec.js +75 -74
- package/esm/components/router.spec.js.map +1 -1
- package/esm/initialize.d.ts +11 -0
- package/esm/initialize.d.ts.map +1 -1
- package/esm/initialize.js +5 -0
- package/esm/initialize.js.map +1 -1
- package/esm/jsx.d.ts +83 -2
- package/esm/jsx.d.ts.map +1 -1
- package/esm/models/children-list.d.ts +5 -1
- package/esm/models/children-list.d.ts.map +1 -1
- package/esm/models/partial-element.d.ts +12 -2
- package/esm/models/partial-element.d.ts.map +1 -1
- package/esm/models/render-options.d.ts +89 -3
- package/esm/models/render-options.d.ts.map +1 -1
- package/esm/models/selection-state.d.ts +4 -0
- package/esm/models/selection-state.d.ts.map +1 -1
- package/esm/services/location-service.d.ts +11 -0
- package/esm/services/location-service.d.ts.map +1 -1
- package/esm/services/location-service.js +11 -0
- package/esm/services/location-service.js.map +1 -1
- package/esm/services/resource-manager.d.ts +24 -0
- package/esm/services/resource-manager.d.ts.map +1 -1
- package/esm/services/resource-manager.js +30 -0
- package/esm/services/resource-manager.js.map +1 -1
- package/esm/services/resource-manager.spec.js +93 -0
- package/esm/services/resource-manager.spec.js.map +1 -1
- package/esm/services/screen-service.d.ts +81 -4
- package/esm/services/screen-service.d.ts.map +1 -1
- package/esm/services/screen-service.js +75 -4
- package/esm/services/screen-service.js.map +1 -1
- package/esm/services/screen-service.spec.js +91 -7
- package/esm/services/screen-service.spec.js.map +1 -1
- package/esm/shade-component.d.ts +17 -4
- package/esm/shade-component.d.ts.map +1 -1
- package/esm/shade-component.js +67 -5
- package/esm/shade-component.js.map +1 -1
- package/esm/shade-host-props-ref.integration.spec.d.ts +2 -0
- package/esm/shade-host-props-ref.integration.spec.d.ts.map +1 -0
- package/esm/shade-host-props-ref.integration.spec.js +381 -0
- package/esm/shade-host-props-ref.integration.spec.js.map +1 -0
- package/esm/shade-resources.integration.spec.js +208 -39
- package/esm/shade-resources.integration.spec.js.map +1 -1
- package/esm/shade.d.ts +20 -17
- package/esm/shade.d.ts.map +1 -1
- package/esm/shade.js +172 -33
- package/esm/shade.js.map +1 -1
- package/esm/shade.spec.js +31 -30
- package/esm/shade.spec.js.map +1 -1
- package/esm/shades.integration.spec.js +135 -72
- package/esm/shades.integration.spec.js.map +1 -1
- package/esm/style-manager.d.ts +2 -2
- package/esm/style-manager.js +2 -2
- package/esm/svg-types.d.ts +389 -0
- package/esm/svg-types.d.ts.map +1 -0
- package/esm/svg-types.js +9 -0
- package/esm/svg-types.js.map +1 -0
- package/esm/svg.d.ts +15 -0
- package/esm/svg.d.ts.map +1 -0
- package/esm/svg.js +76 -0
- package/esm/svg.js.map +1 -0
- package/esm/svg.spec.d.ts +2 -0
- package/esm/svg.spec.d.ts.map +1 -0
- package/esm/svg.spec.js +80 -0
- package/esm/svg.spec.js.map +1 -0
- package/esm/vnode.d.ts +103 -0
- package/esm/vnode.d.ts.map +1 -0
- package/esm/vnode.integration.spec.d.ts +2 -0
- package/esm/vnode.integration.spec.d.ts.map +1 -0
- package/esm/vnode.integration.spec.js +494 -0
- package/esm/vnode.integration.spec.js.map +1 -0
- package/esm/vnode.js +453 -0
- package/esm/vnode.js.map +1 -0
- package/esm/vnode.spec.d.ts +2 -0
- package/esm/vnode.spec.d.ts.map +1 -0
- package/esm/vnode.spec.js +473 -0
- package/esm/vnode.spec.js.map +1 -0
- package/package.json +3 -3
- package/src/component-factory.spec.tsx +18 -5
- package/src/components/index.ts +4 -1
- package/src/components/lazy-load.spec.tsx +82 -75
- package/src/components/lazy-load.tsx +49 -27
- package/src/components/link-to-route.spec.tsx +25 -21
- package/src/components/link-to-route.tsx +4 -2
- package/src/components/nested-route-link.spec.tsx +303 -0
- package/src/components/nested-route-link.tsx +100 -0
- package/src/components/nested-route-types.ts +42 -0
- package/src/components/nested-router.spec.tsx +817 -0
- package/src/components/nested-router.tsx +256 -0
- package/src/components/route-link.spec.tsx +22 -18
- package/src/components/route-link.tsx +6 -5
- package/src/components/router.spec.tsx +109 -108
- package/src/components/router.tsx +15 -2
- package/src/initialize.ts +12 -0
- package/src/jsx.ts +129 -2
- package/src/models/children-list.ts +7 -1
- package/src/models/partial-element.ts +13 -2
- package/src/models/render-options.ts +90 -3
- package/src/models/selection-state.ts +4 -0
- package/src/services/location-service.tsx +11 -0
- package/src/services/resource-manager.spec.ts +116 -0
- package/src/services/resource-manager.ts +30 -0
- package/src/services/screen-service.spec.ts +109 -7
- package/src/services/screen-service.ts +81 -4
- package/src/shade-component.ts +72 -6
- package/src/shade-host-props-ref.integration.spec.tsx +460 -0
- package/src/shade-resources.integration.spec.tsx +276 -52
- package/src/shade.spec.tsx +40 -39
- package/src/shade.ts +186 -58
- package/src/shades.integration.spec.tsx +154 -80
- package/src/style-manager.ts +2 -2
- package/src/svg-types.ts +437 -0
- package/src/svg.spec.ts +89 -0
- package/src/svg.ts +78 -0
- package/src/vnode.integration.spec.tsx +657 -0
- package/src/vnode.spec.ts +579 -0
- package/src/vnode.ts +508 -0
package/src/shade.ts
CHANGED
|
@@ -2,14 +2,20 @@ import type { Constructable } from '@furystack/inject'
|
|
|
2
2
|
import { hasInjectorReference, Injector } from '@furystack/inject'
|
|
3
3
|
import { ObservableValue } from '@furystack/utils'
|
|
4
4
|
import type { ChildrenList, CSSObject, PartialElement, RenderOptions } from './models/index.js'
|
|
5
|
+
import type { RefObject } from './models/render-options.js'
|
|
5
6
|
import { LocationService } from './services/location-service.js'
|
|
6
7
|
import { ResourceManager } from './services/resource-manager.js'
|
|
7
|
-
import { attachProps, attachStyles } from './shade-component.js'
|
|
8
|
+
import { attachProps, attachStyles, setRenderMode } from './shade-component.js'
|
|
8
9
|
import { StyleManager } from './style-manager.js'
|
|
10
|
+
import type { VChild } from './vnode.js'
|
|
11
|
+
import { patchChildren, toVChildArray } from './vnode.js'
|
|
9
12
|
|
|
10
13
|
export type ShadeOptions<TProps, TElementBase extends HTMLElement> = {
|
|
11
14
|
/**
|
|
12
|
-
*
|
|
15
|
+
* The custom element tag name used to register the component.
|
|
16
|
+
* Must follow the Custom Elements naming convention (lowercase, must contain a hyphen).
|
|
17
|
+
*
|
|
18
|
+
* @example 'my-button', 'shade-dialog', 'app-header'
|
|
13
19
|
*/
|
|
14
20
|
shadowDomName: string
|
|
15
21
|
|
|
@@ -18,23 +24,6 @@ export type ShadeOptions<TProps, TElementBase extends HTMLElement> = {
|
|
|
18
24
|
*/
|
|
19
25
|
render: (options: RenderOptions<TProps, TElementBase>) => JSX.Element | string | null
|
|
20
26
|
|
|
21
|
-
/**
|
|
22
|
-
* Construct hook. Will be executed once when the element has been constructed and initialized
|
|
23
|
-
*/
|
|
24
|
-
constructed?: (
|
|
25
|
-
options: RenderOptions<TProps, TElementBase>,
|
|
26
|
-
) => void | undefined | (() => void) | Promise<void | undefined | (() => void)>
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Will be executed when the element is attached to the DOM.
|
|
30
|
-
*/
|
|
31
|
-
onAttach?: (options: RenderOptions<TProps, TElementBase>) => void
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Will be executed when the element is detached from the DOM.
|
|
35
|
-
*/
|
|
36
|
-
onDetach?: (options: RenderOptions<TProps, TElementBase>) => void
|
|
37
|
-
|
|
38
27
|
/**
|
|
39
28
|
* Name of the HTML Element's base class. Needs to be defined if the elementBase is set. E.g.: 'div', 'button', 'input'
|
|
40
29
|
*/
|
|
@@ -46,19 +35,22 @@ export type ShadeOptions<TProps, TElementBase extends HTMLElement> = {
|
|
|
46
35
|
elementBase?: Constructable<TElementBase>
|
|
47
36
|
|
|
48
37
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
38
|
+
* Inline styles applied to each component instance.
|
|
39
|
+
* Use for per-instance dynamic overrides. Prefer `css` for component-level defaults.
|
|
51
40
|
*/
|
|
52
41
|
style?: Partial<CSSStyleDeclaration>
|
|
53
42
|
|
|
54
43
|
/**
|
|
55
44
|
* CSS styles injected as a stylesheet during component registration.
|
|
56
|
-
* Supports pseudo-selectors (
|
|
57
|
-
*
|
|
45
|
+
* Supports pseudo-selectors (`&:hover`, `&:active`) and nested selectors (`& .class`).
|
|
46
|
+
*
|
|
47
|
+
* **Best practice:** Prefer `css` over `style` for component defaults -- styles are injected
|
|
48
|
+
* once per component type (better performance), and support pseudo-selectors and nesting.
|
|
58
49
|
*
|
|
59
50
|
* @example
|
|
60
51
|
* ```typescript
|
|
61
52
|
* css: {
|
|
53
|
+
* display: 'flex',
|
|
62
54
|
* padding: '16px',
|
|
63
55
|
* '&:hover': { backgroundColor: '#f0f0f0' },
|
|
64
56
|
* '& .title': { fontWeight: 'bold' }
|
|
@@ -76,7 +68,7 @@ export type ShadeOptions<TProps, TElementBase extends HTMLElement> = {
|
|
|
76
68
|
export const Shade = <TProps, TElementBase extends HTMLElement = HTMLElement>(
|
|
77
69
|
o: ShadeOptions<TProps, TElementBase>,
|
|
78
70
|
) => {
|
|
79
|
-
//
|
|
71
|
+
// Register custom element
|
|
80
72
|
const customElementName = o.shadowDomName
|
|
81
73
|
|
|
82
74
|
const existing = customElements.get(customElementName)
|
|
@@ -102,15 +94,31 @@ export const Shade = <TProps, TElementBase extends HTMLElement = HTMLElement>(
|
|
|
102
94
|
|
|
103
95
|
public resourceManager = new ResourceManager()
|
|
104
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Host props collected during the current render pass via `useHostProps`.
|
|
99
|
+
* Applied to the host element after each render.
|
|
100
|
+
*/
|
|
101
|
+
private _pendingHostProps: Array<Record<string, unknown> & { style?: Record<string, string> }> = []
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The host props that were applied in the previous render, used for diffing.
|
|
105
|
+
*/
|
|
106
|
+
private _prevHostProps: Record<string, unknown> | null = null
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Cached ref objects keyed by the user-provided key string.
|
|
110
|
+
*/
|
|
111
|
+
private _refs = new Map<string, RefObject<Element>>()
|
|
112
|
+
|
|
105
113
|
public connectedCallback() {
|
|
106
|
-
|
|
107
|
-
this.callConstructed()
|
|
114
|
+
this.updateComponent()
|
|
108
115
|
}
|
|
109
116
|
|
|
110
117
|
public async disconnectedCallback() {
|
|
111
|
-
|
|
118
|
+
this._refs.clear()
|
|
119
|
+
this._prevVTree = null
|
|
120
|
+
this._prevHostProps = null
|
|
112
121
|
await this.resourceManager[Symbol.asyncDispose]()
|
|
113
|
-
this.cleanup?.()
|
|
114
122
|
}
|
|
115
123
|
|
|
116
124
|
/**
|
|
@@ -140,8 +148,17 @@ export const Shade = <TProps, TElementBase extends HTMLElement = HTMLElement>(
|
|
|
140
148
|
props: this.props,
|
|
141
149
|
injector: this.injector,
|
|
142
150
|
children: this.shadeChildren,
|
|
143
|
-
element: this,
|
|
144
151
|
renderCount: this._renderCount,
|
|
152
|
+
useHostProps: (hostProps) => {
|
|
153
|
+
this._pendingHostProps.push(hostProps)
|
|
154
|
+
},
|
|
155
|
+
useRef: <T extends Element = HTMLElement>(key: string): RefObject<T> => {
|
|
156
|
+
const existingRef = this._refs.get(key) as RefObject<T> | undefined
|
|
157
|
+
if (existingRef) return existingRef
|
|
158
|
+
const refObject = { current: null } as { current: T | null }
|
|
159
|
+
this._refs.set(key, refObject as unknown as RefObject<Element>)
|
|
160
|
+
return refObject as RefObject<T>
|
|
161
|
+
},
|
|
145
162
|
useObservable: (key, obesrvable, options) => {
|
|
146
163
|
const onChange = options?.onChange || (() => this.updateComponent())
|
|
147
164
|
return this.resourceManager.useObservable(key, obesrvable, onChange, options)
|
|
@@ -216,47 +233,148 @@ export const Shade = <TProps, TElementBase extends HTMLElement = HTMLElement>(
|
|
|
216
233
|
return renderOptions
|
|
217
234
|
}
|
|
218
235
|
|
|
236
|
+
private _updateScheduled = false
|
|
237
|
+
|
|
219
238
|
/**
|
|
220
|
-
*
|
|
239
|
+
* The VChild array from the previous render, with `_el` references
|
|
240
|
+
* pointing to the real DOM nodes. Used to diff against the next render.
|
|
221
241
|
*/
|
|
222
|
-
|
|
223
|
-
const renderResult = this.render(this.getRenderOptions())
|
|
242
|
+
private _prevVTree: VChild[] | null = null
|
|
224
243
|
|
|
225
|
-
|
|
226
|
-
|
|
244
|
+
/**
|
|
245
|
+
* Schedules a component update via microtask. Multiple calls before the microtask
|
|
246
|
+
* runs are coalesced into a single render pass.
|
|
247
|
+
*/
|
|
248
|
+
public updateComponent() {
|
|
249
|
+
if (!this._updateScheduled) {
|
|
250
|
+
this._updateScheduled = true
|
|
251
|
+
queueMicrotask(() => {
|
|
252
|
+
this._updateScheduled = false
|
|
253
|
+
this._performUpdate()
|
|
254
|
+
})
|
|
227
255
|
}
|
|
256
|
+
}
|
|
228
257
|
|
|
229
|
-
|
|
230
|
-
|
|
258
|
+
private _performUpdate() {
|
|
259
|
+
this._pendingHostProps = []
|
|
260
|
+
let renderResult: unknown
|
|
261
|
+
setRenderMode(true)
|
|
262
|
+
try {
|
|
263
|
+
renderResult = this.render(this.getRenderOptions())
|
|
264
|
+
} finally {
|
|
265
|
+
setRenderMode(false)
|
|
231
266
|
}
|
|
232
267
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
268
|
+
const newVTree = toVChildArray(renderResult)
|
|
269
|
+
patchChildren(this, this._prevVTree || [], newVTree)
|
|
270
|
+
this._prevVTree = newVTree
|
|
271
|
+
|
|
272
|
+
this._applyHostProps()
|
|
239
273
|
}
|
|
240
274
|
|
|
241
275
|
/**
|
|
242
|
-
*
|
|
276
|
+
* Merges all pending host props from the render pass and applies them
|
|
277
|
+
* to the host element, diffing against the previously applied host props.
|
|
243
278
|
*/
|
|
244
|
-
|
|
245
|
-
this.
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
279
|
+
private _applyHostProps() {
|
|
280
|
+
if (this._pendingHostProps.length === 0) {
|
|
281
|
+
if (this._prevHostProps) {
|
|
282
|
+
// All host props were removed — clean up
|
|
283
|
+
for (const key of Object.keys(this._prevHostProps)) {
|
|
284
|
+
if (key === 'style') continue
|
|
285
|
+
this.removeAttribute(key)
|
|
286
|
+
}
|
|
287
|
+
if (this._prevHostProps.style) {
|
|
288
|
+
for (const sk of Object.keys(this._prevHostProps.style as Record<string, string>)) {
|
|
289
|
+
if (sk.startsWith('--')) {
|
|
290
|
+
this.style.removeProperty(sk)
|
|
291
|
+
} else {
|
|
292
|
+
;(this.style as unknown as Record<string, string>)[sk] = ''
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
this._prevHostProps = null
|
|
297
|
+
}
|
|
298
|
+
return
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Merge all pending host prop calls into a single object
|
|
302
|
+
const merged: Record<string, unknown> = {}
|
|
303
|
+
let mergedStyle: Record<string, string> | undefined
|
|
304
|
+
|
|
305
|
+
for (const hp of this._pendingHostProps) {
|
|
306
|
+
for (const [key, value] of Object.entries(hp)) {
|
|
307
|
+
if (key === 'style' && typeof value === 'object' && value !== null) {
|
|
308
|
+
mergedStyle = { ...mergedStyle, ...(value as Record<string, string>) }
|
|
309
|
+
} else {
|
|
310
|
+
merged[key] = value
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (mergedStyle) {
|
|
316
|
+
merged.style = mergedStyle
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const oldHP = this._prevHostProps || {}
|
|
320
|
+
const newHP = merged
|
|
321
|
+
|
|
322
|
+
// Remove attributes no longer present
|
|
323
|
+
for (const key of Object.keys(oldHP)) {
|
|
324
|
+
if (key === 'style') continue
|
|
325
|
+
if (!(key in newHP)) {
|
|
326
|
+
if (key.startsWith('on') && typeof oldHP[key] === 'function') {
|
|
327
|
+
;(this as unknown as Record<string, unknown>)[key] = null
|
|
328
|
+
} else {
|
|
329
|
+
this.removeAttribute(key)
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Apply new/changed attributes
|
|
335
|
+
for (const [key, value] of Object.entries(newHP)) {
|
|
336
|
+
if (key === 'style') continue
|
|
337
|
+
if (oldHP[key] !== value) {
|
|
338
|
+
if (typeof value === 'function' || (typeof value === 'object' && value !== null)) {
|
|
339
|
+
;(this as unknown as Record<string, unknown>)[key] = value
|
|
340
|
+
} else if (value === null || value === undefined || value === false) {
|
|
341
|
+
if (key in this) {
|
|
342
|
+
;(this as unknown as Record<string, unknown>)[key] = undefined
|
|
343
|
+
}
|
|
344
|
+
this.removeAttribute(key)
|
|
345
|
+
} else {
|
|
346
|
+
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
347
|
+
this.setAttribute(key, String(value))
|
|
348
|
+
}
|
|
349
|
+
}
|
|
256
350
|
}
|
|
257
|
-
}
|
|
258
351
|
|
|
259
|
-
|
|
352
|
+
// Diff styles
|
|
353
|
+
const oldStyle = (oldHP.style as Record<string, string>) || {}
|
|
354
|
+
const newStyle = (mergedStyle as Record<string, string>) || {}
|
|
355
|
+
|
|
356
|
+
for (const sk of Object.keys(oldStyle)) {
|
|
357
|
+
if (!(sk in newStyle)) {
|
|
358
|
+
if (sk.startsWith('--')) {
|
|
359
|
+
this.style.removeProperty(sk)
|
|
360
|
+
} else {
|
|
361
|
+
;(this.style as unknown as Record<string, string>)[sk] = ''
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
for (const [sk, sv] of Object.entries(newStyle)) {
|
|
367
|
+
if (oldStyle[sk] !== sv) {
|
|
368
|
+
if (sk.startsWith('--')) {
|
|
369
|
+
this.style.setProperty(sk, sv)
|
|
370
|
+
} else {
|
|
371
|
+
;(this.style as unknown as Record<string, string>)[sk] = sv
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
this._prevHostProps = merged
|
|
377
|
+
}
|
|
260
378
|
|
|
261
379
|
private _injector?: Injector
|
|
262
380
|
|
|
@@ -296,7 +414,7 @@ export const Shade = <TProps, TElementBase extends HTMLElement = HTMLElement>(
|
|
|
296
414
|
o.elementBaseName ? { extends: o.elementBaseName } : undefined,
|
|
297
415
|
)
|
|
298
416
|
} else {
|
|
299
|
-
throw Error(`A custom shade with
|
|
417
|
+
throw Error(`A custom shade with name '${o.shadowDomName}' has already been registered!`)
|
|
300
418
|
}
|
|
301
419
|
|
|
302
420
|
return (props: TProps & PartialElement<TElementBase>, children?: ChildrenList) => {
|
|
@@ -318,3 +436,13 @@ export const Shade = <TProps, TElementBase extends HTMLElement = HTMLElement>(
|
|
|
318
436
|
return el as JSX.Element
|
|
319
437
|
}
|
|
320
438
|
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Flushes any pending microtask-based component updates.
|
|
442
|
+
* Useful in tests to wait for batched renders to complete before asserting DOM state.
|
|
443
|
+
*
|
|
444
|
+
* Note: this flushes one level of pending updates. If a render itself triggers new
|
|
445
|
+
* `updateComponent()` calls, an additional `await flushUpdates()` may be needed.
|
|
446
|
+
* @returns a promise that resolves after the current microtask queue has been processed
|
|
447
|
+
*/
|
|
448
|
+
export const flushUpdates = (): Promise<void> => new Promise<void>((resolve) => queueMicrotask(resolve))
|