@furystack/shades 12.5.0 → 13.1.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 +97 -0
- package/README.md +3 -3
- package/esm/component-factory.spec.js +3 -3
- package/esm/component-factory.spec.js.map +1 -1
- package/esm/components/lazy-load.js +1 -1
- package/esm/components/lazy-load.js.map +1 -1
- package/esm/components/link-to-route.js +1 -1
- package/esm/components/link-to-route.js.map +1 -1
- package/esm/components/nested-route-link.js +1 -1
- package/esm/components/nested-route-link.js.map +1 -1
- package/esm/components/nested-router.js +1 -1
- package/esm/components/nested-router.js.map +1 -1
- package/esm/components/route-link.js +1 -1
- package/esm/components/route-link.js.map +1 -1
- package/esm/components/router.js +1 -1
- package/esm/components/router.js.map +1 -1
- package/esm/css-generator.d.ts +1 -1
- package/esm/css-generator.js +1 -1
- package/esm/models/render-options.d.ts +5 -2
- package/esm/models/render-options.d.ts.map +1 -1
- package/esm/services/index.d.ts +1 -0
- package/esm/services/index.d.ts.map +1 -1
- package/esm/services/index.js +1 -0
- package/esm/services/index.js.map +1 -1
- package/esm/services/spatial-navigation-service.d.ts +88 -0
- package/esm/services/spatial-navigation-service.d.ts.map +1 -0
- package/esm/services/spatial-navigation-service.js +523 -0
- package/esm/services/spatial-navigation-service.js.map +1 -0
- package/esm/services/spatial-navigation-service.spec.d.ts +2 -0
- package/esm/services/spatial-navigation-service.spec.d.ts.map +1 -0
- package/esm/services/spatial-navigation-service.spec.js +1133 -0
- package/esm/services/spatial-navigation-service.spec.js.map +1 -0
- package/esm/shade-host-props-ref.integration.spec.js +13 -13
- package/esm/shade-host-props-ref.integration.spec.js.map +1 -1
- package/esm/shade-resources.integration.spec.js +5 -5
- package/esm/shade-resources.integration.spec.js.map +1 -1
- package/esm/shade.d.ts +1 -1
- package/esm/shade.d.ts.map +1 -1
- package/esm/shade.js +2 -3
- package/esm/shade.js.map +1 -1
- package/esm/shade.spec.js +14 -14
- package/esm/shade.spec.js.map +1 -1
- package/esm/shades.integration.spec.js +17 -17
- package/esm/shades.integration.spec.js.map +1 -1
- package/esm/style-manager.d.ts +4 -4
- package/esm/style-manager.d.ts.map +1 -1
- package/esm/style-manager.js +9 -9
- package/esm/style-manager.js.map +1 -1
- package/esm/style-manager.spec.js +3 -3
- package/esm/style-manager.spec.js.map +1 -1
- package/esm/vnode.integration.spec.js +15 -15
- package/esm/vnode.integration.spec.js.map +1 -1
- package/package.json +3 -3
- package/src/component-factory.spec.tsx +3 -3
- package/src/components/lazy-load.tsx +1 -1
- package/src/components/link-to-route.tsx +1 -1
- package/src/components/nested-route-link.tsx +1 -1
- package/src/components/nested-router.tsx +1 -1
- package/src/components/route-link.tsx +1 -1
- package/src/components/router.tsx +1 -1
- package/src/css-generator.ts +1 -1
- package/src/models/render-options.ts +5 -2
- package/src/services/index.ts +1 -0
- package/src/services/spatial-navigation-service.spec.ts +1396 -0
- package/src/services/spatial-navigation-service.ts +597 -0
- package/src/shade-host-props-ref.integration.spec.tsx +13 -13
- package/src/shade-resources.integration.spec.tsx +5 -5
- package/src/shade.spec.tsx +14 -14
- package/src/shade.ts +3 -4
- package/src/shades.integration.spec.tsx +17 -17
- package/src/style-manager.spec.ts +3 -3
- package/src/style-manager.ts +9 -9
- package/src/vnode.integration.spec.tsx +15 -15
|
@@ -18,7 +18,7 @@ describe('useHostProps integration tests', () => {
|
|
|
18
18
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
19
19
|
|
|
20
20
|
const ExampleComponent = Shade<{ variant: string }>({
|
|
21
|
-
|
|
21
|
+
customElementName: 'host-props-data-attr-test',
|
|
22
22
|
render: ({ props, useHostProps }) => {
|
|
23
23
|
useHostProps({
|
|
24
24
|
'data-variant': props.variant,
|
|
@@ -45,7 +45,7 @@ describe('useHostProps integration tests', () => {
|
|
|
45
45
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
46
46
|
|
|
47
47
|
const ExampleComponent = Shade({
|
|
48
|
-
|
|
48
|
+
customElementName: 'host-props-aria-test',
|
|
49
49
|
render: ({ useHostProps }) => {
|
|
50
50
|
useHostProps({
|
|
51
51
|
role: 'progressbar',
|
|
@@ -77,7 +77,7 @@ describe('useHostProps integration tests', () => {
|
|
|
77
77
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
78
78
|
|
|
79
79
|
const ExampleComponent = Shade({
|
|
80
|
-
|
|
80
|
+
customElementName: 'host-props-css-vars-test',
|
|
81
81
|
render: ({ useHostProps }) => {
|
|
82
82
|
useHostProps({
|
|
83
83
|
style: {
|
|
@@ -107,7 +107,7 @@ describe('useHostProps integration tests', () => {
|
|
|
107
107
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
108
108
|
|
|
109
109
|
const ExampleComponent = Shade({
|
|
110
|
-
|
|
110
|
+
customElementName: 'host-props-inline-style-test',
|
|
111
111
|
render: ({ useHostProps }) => {
|
|
112
112
|
useHostProps({
|
|
113
113
|
style: {
|
|
@@ -137,7 +137,7 @@ describe('useHostProps integration tests', () => {
|
|
|
137
137
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
138
138
|
|
|
139
139
|
const ExampleComponent = Shade({
|
|
140
|
-
|
|
140
|
+
customElementName: 'host-props-merge-test',
|
|
141
141
|
render: ({ useHostProps }) => {
|
|
142
142
|
useHostProps({
|
|
143
143
|
'data-first': 'one',
|
|
@@ -172,7 +172,7 @@ describe('useHostProps integration tests', () => {
|
|
|
172
172
|
const showExtra = new ObservableValue(true)
|
|
173
173
|
|
|
174
174
|
const ExampleComponent = Shade({
|
|
175
|
-
|
|
175
|
+
customElementName: 'host-props-remove-attr-test',
|
|
176
176
|
render: ({ useHostProps, useObservable }) => {
|
|
177
177
|
const [show] = useObservable('showExtra', showExtra)
|
|
178
178
|
useHostProps({
|
|
@@ -208,7 +208,7 @@ describe('useHostProps integration tests', () => {
|
|
|
208
208
|
const showColor = new ObservableValue(true)
|
|
209
209
|
|
|
210
210
|
const ExampleComponent = Shade({
|
|
211
|
-
|
|
211
|
+
customElementName: 'host-props-remove-css-var-test',
|
|
212
212
|
render: ({ useHostProps, useObservable }) => {
|
|
213
213
|
const [show] = useObservable('showColor', showColor)
|
|
214
214
|
useHostProps({
|
|
@@ -246,7 +246,7 @@ describe('useHostProps integration tests', () => {
|
|
|
246
246
|
let clicked = false
|
|
247
247
|
|
|
248
248
|
const ExampleComponent = Shade({
|
|
249
|
-
|
|
249
|
+
customElementName: 'host-props-event-test',
|
|
250
250
|
render: ({ useHostProps }) => {
|
|
251
251
|
useHostProps({
|
|
252
252
|
onclick: () => {
|
|
@@ -286,7 +286,7 @@ describe('useRef integration tests', () => {
|
|
|
286
286
|
let capturedRef: { readonly current: HTMLDivElement | null } | undefined
|
|
287
287
|
|
|
288
288
|
const ExampleComponent = Shade({
|
|
289
|
-
|
|
289
|
+
customElementName: 'use-ref-basic-test',
|
|
290
290
|
render: ({ useRef }) => {
|
|
291
291
|
const divRef = useRef<HTMLDivElement>('myDiv')
|
|
292
292
|
capturedRef = divRef
|
|
@@ -320,7 +320,7 @@ describe('useRef integration tests', () => {
|
|
|
320
320
|
const capturedRefs: Array<{ readonly current: Element | null }> = []
|
|
321
321
|
|
|
322
322
|
const ExampleComponent = Shade({
|
|
323
|
-
|
|
323
|
+
customElementName: 'use-ref-stable-test',
|
|
324
324
|
render: ({ useRef, useObservable }) => {
|
|
325
325
|
const [count] = useObservable('counter', counter)
|
|
326
326
|
const divRef = useRef('myDiv')
|
|
@@ -355,7 +355,7 @@ describe('useRef integration tests', () => {
|
|
|
355
355
|
let capturedInputRef: { readonly current: HTMLInputElement | null } | undefined
|
|
356
356
|
|
|
357
357
|
const ExampleComponent = Shade({
|
|
358
|
-
|
|
358
|
+
customElementName: 'use-ref-nested-test',
|
|
359
359
|
render: ({ useRef }) => {
|
|
360
360
|
const inputRef = useRef<HTMLInputElement>('input')
|
|
361
361
|
capturedInputRef = inputRef
|
|
@@ -391,7 +391,7 @@ describe('useRef integration tests', () => {
|
|
|
391
391
|
let capturedRef: { readonly current: HTMLSpanElement | null } | undefined
|
|
392
392
|
|
|
393
393
|
const ExampleComponent = Shade({
|
|
394
|
-
|
|
394
|
+
customElementName: 'use-ref-unmount-test',
|
|
395
395
|
render: ({ useRef, useObservable }) => {
|
|
396
396
|
const [show] = useObservable('showChild', showChild)
|
|
397
397
|
const spanRef = useRef<HTMLSpanElement>('span')
|
|
@@ -423,7 +423,7 @@ describe('useRef integration tests', () => {
|
|
|
423
423
|
const counter = new ObservableValue(0)
|
|
424
424
|
|
|
425
425
|
const ExampleComponent = Shade({
|
|
426
|
-
|
|
426
|
+
customElementName: 'use-ref-onchange-test',
|
|
427
427
|
render: ({ useRef, useObservable }) => {
|
|
428
428
|
const spanRef = useRef<HTMLSpanElement>('counterSpan')
|
|
429
429
|
useObservable('counter', counter, {
|
|
@@ -35,7 +35,7 @@ describe('Shade Resources integration tests', () => {
|
|
|
35
35
|
</div>
|
|
36
36
|
)
|
|
37
37
|
},
|
|
38
|
-
|
|
38
|
+
customElementName: 'shades-example-resource',
|
|
39
39
|
})
|
|
40
40
|
|
|
41
41
|
expect(obs1.getObservers().length).toBe(0)
|
|
@@ -99,7 +99,7 @@ describe('Shade Resources integration tests', () => {
|
|
|
99
99
|
renderCounter()
|
|
100
100
|
return <div id="val">{value}</div>
|
|
101
101
|
},
|
|
102
|
-
|
|
102
|
+
customElementName: 'shades-example-custom-onchange',
|
|
103
103
|
})
|
|
104
104
|
|
|
105
105
|
initializeShadeRoot({
|
|
@@ -167,7 +167,7 @@ describe('Shade Resources integration tests', () => {
|
|
|
167
167
|
</div>
|
|
168
168
|
)
|
|
169
169
|
},
|
|
170
|
-
|
|
170
|
+
customElementName: 'shades-example-manual-dom-update',
|
|
171
171
|
})
|
|
172
172
|
|
|
173
173
|
initializeShadeRoot({
|
|
@@ -228,7 +228,7 @@ describe('Shade Resources integration tests', () => {
|
|
|
228
228
|
</div>
|
|
229
229
|
)
|
|
230
230
|
},
|
|
231
|
-
|
|
231
|
+
customElementName: 'shades-example-batching',
|
|
232
232
|
})
|
|
233
233
|
|
|
234
234
|
initializeShadeRoot({
|
|
@@ -273,7 +273,7 @@ describe('Shade Resources integration tests', () => {
|
|
|
273
273
|
renderCounter()
|
|
274
274
|
return <div>content</div>
|
|
275
275
|
},
|
|
276
|
-
|
|
276
|
+
customElementName: 'shades-example-update-batching',
|
|
277
277
|
})
|
|
278
278
|
|
|
279
279
|
initializeShadeRoot({
|
package/src/shade.spec.tsx
CHANGED
|
@@ -13,18 +13,18 @@ describe('Shade edge cases', () => {
|
|
|
13
13
|
document.body.innerHTML = ''
|
|
14
14
|
})
|
|
15
15
|
|
|
16
|
-
describe('duplicate
|
|
17
|
-
it('should throw an error when registering a duplicate
|
|
16
|
+
describe('duplicate customElementName error', () => {
|
|
17
|
+
it('should throw an error when registering a duplicate customElementName', () => {
|
|
18
18
|
// First registration should succeed
|
|
19
19
|
Shade({
|
|
20
|
-
|
|
20
|
+
customElementName: 'shade-duplicate-test',
|
|
21
21
|
render: () => <div>First</div>,
|
|
22
22
|
})
|
|
23
23
|
|
|
24
24
|
// Second registration with the same name should throw
|
|
25
25
|
expect(() => {
|
|
26
26
|
Shade({
|
|
27
|
-
|
|
27
|
+
customElementName: 'shade-duplicate-test',
|
|
28
28
|
render: () => <div>Second</div>,
|
|
29
29
|
})
|
|
30
30
|
}).toThrow("A custom shade with name 'shade-duplicate-test' has already been registered!")
|
|
@@ -34,13 +34,13 @@ describe('Shade edge cases', () => {
|
|
|
34
34
|
const uniqueName = `shade-duplicate-name-in-error-${Date.now()}`
|
|
35
35
|
|
|
36
36
|
Shade({
|
|
37
|
-
|
|
37
|
+
customElementName: uniqueName,
|
|
38
38
|
render: () => <div>First</div>,
|
|
39
39
|
})
|
|
40
40
|
|
|
41
41
|
try {
|
|
42
42
|
Shade({
|
|
43
|
-
|
|
43
|
+
customElementName: uniqueName,
|
|
44
44
|
render: () => <div>Second</div>,
|
|
45
45
|
})
|
|
46
46
|
// Should not reach here
|
|
@@ -61,7 +61,7 @@ describe('Shade edge cases', () => {
|
|
|
61
61
|
let childCapturedInjector: Injector | undefined
|
|
62
62
|
|
|
63
63
|
const ChildComponent = Shade<{ injector?: Injector }>({
|
|
64
|
-
|
|
64
|
+
customElementName: 'shade-injector-child-props-test',
|
|
65
65
|
render: ({ injector }) => {
|
|
66
66
|
childCapturedInjector = injector
|
|
67
67
|
return <div>Child</div>
|
|
@@ -69,7 +69,7 @@ describe('Shade edge cases', () => {
|
|
|
69
69
|
})
|
|
70
70
|
|
|
71
71
|
const ParentComponent = Shade({
|
|
72
|
-
|
|
72
|
+
customElementName: 'shade-injector-parent-props-test',
|
|
73
73
|
render: ({ injector, children }) => {
|
|
74
74
|
parentCapturedInjector = injector
|
|
75
75
|
return <div>{children}</div>
|
|
@@ -105,7 +105,7 @@ describe('Shade edge cases', () => {
|
|
|
105
105
|
const renderCounter = vi.fn()
|
|
106
106
|
|
|
107
107
|
const ExampleComponent = Shade({
|
|
108
|
-
|
|
108
|
+
customElementName: 'shade-no-render-after-disconnect',
|
|
109
109
|
render: () => {
|
|
110
110
|
renderCounter()
|
|
111
111
|
return <div>content</div>
|
|
@@ -140,7 +140,7 @@ describe('Shade edge cases', () => {
|
|
|
140
140
|
const renderCounter = vi.fn()
|
|
141
141
|
|
|
142
142
|
const ExampleComponent = Shade({
|
|
143
|
-
|
|
143
|
+
customElementName: 'shade-no-sync-render-after-disconnect',
|
|
144
144
|
render: () => {
|
|
145
145
|
renderCounter()
|
|
146
146
|
return <div>content</div>
|
|
@@ -174,7 +174,7 @@ describe('Shade edge cases', () => {
|
|
|
174
174
|
const obs = new ObservableValue(0)
|
|
175
175
|
|
|
176
176
|
const ExampleComponent = Shade({
|
|
177
|
-
|
|
177
|
+
customElementName: 'shade-no-render-during-disposal',
|
|
178
178
|
render: ({ useObservable, useDisposable }) => {
|
|
179
179
|
useObservable('obs', obs)
|
|
180
180
|
useDisposable('cleanup', () => ({
|
|
@@ -227,7 +227,7 @@ describe('Shade edge cases', () => {
|
|
|
227
227
|
const stateKey = 'broadcast-test-key'
|
|
228
228
|
|
|
229
229
|
const ExampleComponent = Shade({
|
|
230
|
-
|
|
230
|
+
customElementName: 'shade-broadcast-channel-test',
|
|
231
231
|
render: ({ useStoredState }) => {
|
|
232
232
|
const [value] = useStoredState(stateKey, 'initial', store)
|
|
233
233
|
return <div id="value">{value}</div>
|
|
@@ -271,7 +271,7 @@ describe('Shade edge cases', () => {
|
|
|
271
271
|
const stateKey = 'broadcast-filter-test-key'
|
|
272
272
|
|
|
273
273
|
const ExampleComponent = Shade({
|
|
274
|
-
|
|
274
|
+
customElementName: 'shade-broadcast-channel-filter-test',
|
|
275
275
|
render: ({ useStoredState }) => {
|
|
276
276
|
const [value] = useStoredState(stateKey, 'initial', store)
|
|
277
277
|
return <div id="value">{value}</div>
|
|
@@ -316,7 +316,7 @@ describe('Shade edge cases', () => {
|
|
|
316
316
|
const stateKey = 'broadcast-cleanup-test-key'
|
|
317
317
|
|
|
318
318
|
const ExampleComponent = Shade({
|
|
319
|
-
|
|
319
|
+
customElementName: 'shade-broadcast-channel-cleanup-test',
|
|
320
320
|
render: ({ useStoredState }) => {
|
|
321
321
|
const [value] = useStoredState(stateKey, 'initial', store)
|
|
322
322
|
return <div id="value">{value}</div>
|
package/src/shade.ts
CHANGED
|
@@ -17,7 +17,7 @@ export type ShadeOptions<TProps, TElementBase extends HTMLElement> = {
|
|
|
17
17
|
*
|
|
18
18
|
* @example 'my-button', 'shade-dialog', 'app-header'
|
|
19
19
|
*/
|
|
20
|
-
|
|
20
|
+
customElementName: string
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Render hook, this method will be executed on each and every render.
|
|
@@ -68,8 +68,7 @@ export type ShadeOptions<TProps, TElementBase extends HTMLElement> = {
|
|
|
68
68
|
export const Shade = <TProps, TElementBase extends HTMLElement = HTMLElement>(
|
|
69
69
|
o: ShadeOptions<TProps, TElementBase>,
|
|
70
70
|
) => {
|
|
71
|
-
|
|
72
|
-
const customElementName = o.shadowDomName
|
|
71
|
+
const { customElementName } = o
|
|
73
72
|
|
|
74
73
|
const existing = customElements.get(customElementName)
|
|
75
74
|
if (!existing) {
|
|
@@ -438,7 +437,7 @@ export const Shade = <TProps, TElementBase extends HTMLElement = HTMLElement>(
|
|
|
438
437
|
o.elementBaseName ? { extends: o.elementBaseName } : undefined,
|
|
439
438
|
)
|
|
440
439
|
} else {
|
|
441
|
-
throw Error(`A custom shade with name '${o.
|
|
440
|
+
throw Error(`A custom shade with name '${o.customElementName}' has already been registered!`)
|
|
442
441
|
}
|
|
443
442
|
|
|
444
443
|
return (props: TProps & PartialElement<TElementBase>, children?: ChildrenList) => {
|
|
@@ -18,7 +18,7 @@ describe('Shades integration tests', () => {
|
|
|
18
18
|
await usingAsync(new Injector(), async (injector) => {
|
|
19
19
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
20
20
|
|
|
21
|
-
const ExampleComponent = Shade({ render: () => <div>Hello</div>,
|
|
21
|
+
const ExampleComponent = Shade({ render: () => <div>Hello</div>, customElementName: 'shades-example' })
|
|
22
22
|
|
|
23
23
|
initializeShadeRoot({
|
|
24
24
|
injector,
|
|
@@ -34,7 +34,7 @@ describe('Shades integration tests', () => {
|
|
|
34
34
|
await usingAsync(new Injector(), async (injector) => {
|
|
35
35
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
36
36
|
|
|
37
|
-
const ExampleComponent = Shade({ render: () => 'Hello',
|
|
37
|
+
const ExampleComponent = Shade({ render: () => 'Hello', customElementName: 'shades-string-render-result' })
|
|
38
38
|
|
|
39
39
|
initializeShadeRoot({
|
|
40
40
|
injector,
|
|
@@ -52,7 +52,7 @@ describe('Shades integration tests', () => {
|
|
|
52
52
|
await usingAsync(new Injector(), async (injector) => {
|
|
53
53
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
54
54
|
|
|
55
|
-
const ExampleComponent = Shade({ render: () => null,
|
|
55
|
+
const ExampleComponent = Shade({ render: () => null, customElementName: 'shades-null-render-result' })
|
|
56
56
|
|
|
57
57
|
initializeShadeRoot({
|
|
58
58
|
injector,
|
|
@@ -77,7 +77,7 @@ describe('Shades integration tests', () => {
|
|
|
77
77
|
<p>2</p>
|
|
78
78
|
</>
|
|
79
79
|
),
|
|
80
|
-
|
|
80
|
+
customElementName: 'shades-fragment-render-result',
|
|
81
81
|
})
|
|
82
82
|
|
|
83
83
|
initializeShadeRoot({
|
|
@@ -105,7 +105,7 @@ describe('Shades integration tests', () => {
|
|
|
105
105
|
</>
|
|
106
106
|
</p>
|
|
107
107
|
),
|
|
108
|
-
|
|
108
|
+
customElementName: 'shades-fragment-render-result-nested',
|
|
109
109
|
})
|
|
110
110
|
|
|
111
111
|
initializeShadeRoot({
|
|
@@ -125,7 +125,7 @@ describe('Shades integration tests', () => {
|
|
|
125
125
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
126
126
|
|
|
127
127
|
const CustomComponent = Shade({
|
|
128
|
-
|
|
128
|
+
customElementName: 'shades-fragment-test-custom-component',
|
|
129
129
|
render: () => <p>Hello</p>,
|
|
130
130
|
})
|
|
131
131
|
|
|
@@ -136,7 +136,7 @@ describe('Shades integration tests', () => {
|
|
|
136
136
|
<CustomComponent />
|
|
137
137
|
</>
|
|
138
138
|
),
|
|
139
|
-
|
|
139
|
+
customElementName: 'shades-fragment-render-result-2',
|
|
140
140
|
})
|
|
141
141
|
|
|
142
142
|
initializeShadeRoot({
|
|
@@ -157,12 +157,12 @@ describe('Shades integration tests', () => {
|
|
|
157
157
|
|
|
158
158
|
const ExampleComponent = Shade({
|
|
159
159
|
render: ({ children }) => <div>{children}</div>,
|
|
160
|
-
|
|
160
|
+
customElementName: 'shades-example-2',
|
|
161
161
|
})
|
|
162
162
|
|
|
163
163
|
const ExampleSubs = Shade<{ no: number }>({
|
|
164
164
|
render: ({ props }) => <div>{props.no}</div>,
|
|
165
|
-
|
|
165
|
+
customElementName: 'shades-example-sub',
|
|
166
166
|
})
|
|
167
167
|
|
|
168
168
|
initializeShadeRoot({
|
|
@@ -190,7 +190,7 @@ describe('Shades integration tests', () => {
|
|
|
190
190
|
const setup = vi.fn()
|
|
191
191
|
|
|
192
192
|
const ExampleComponent = Shade({
|
|
193
|
-
|
|
193
|
+
customElementName: 'example-component-1',
|
|
194
194
|
render: ({ useDisposable }) => {
|
|
195
195
|
useDisposable('test', () => {
|
|
196
196
|
setup()
|
|
@@ -219,7 +219,7 @@ describe('Shades integration tests', () => {
|
|
|
219
219
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
220
220
|
|
|
221
221
|
const ExampleComponent = Shade({
|
|
222
|
-
|
|
222
|
+
customElementName: 'example-component-3',
|
|
223
223
|
render: ({ useState }) => {
|
|
224
224
|
const [count, setCount] = useState('count', 0)
|
|
225
225
|
return (
|
|
@@ -290,7 +290,7 @@ describe('Shades integration tests', () => {
|
|
|
290
290
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
291
291
|
|
|
292
292
|
const ExampleComponent = Shade({
|
|
293
|
-
|
|
293
|
+
customElementName: 'example-component-3-stored-state',
|
|
294
294
|
render: ({ useStoredState }) => {
|
|
295
295
|
const [count, setCount] = useStoredState('count', 0, store)
|
|
296
296
|
return (
|
|
@@ -348,7 +348,7 @@ describe('Shades integration tests', () => {
|
|
|
348
348
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
349
349
|
|
|
350
350
|
const ExampleComponent = Shade({
|
|
351
|
-
|
|
351
|
+
customElementName: 'example-component-3-search-state',
|
|
352
352
|
render: ({ useSearchState }) => {
|
|
353
353
|
const [count, setCount] = useSearchState('count', 0)
|
|
354
354
|
return (
|
|
@@ -403,7 +403,7 @@ describe('Shades integration tests', () => {
|
|
|
403
403
|
await usingAsync(new Injector(), async (injector) => {
|
|
404
404
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
405
405
|
const Parent = Shade({
|
|
406
|
-
|
|
406
|
+
customElementName: 'shade-remount-parent',
|
|
407
407
|
render: ({ children, useState }) => {
|
|
408
408
|
const [areChildrenVisible, setAreChildrenVisible] = useState('areChildrenVisible', true)
|
|
409
409
|
return (
|
|
@@ -423,7 +423,7 @@ describe('Shades integration tests', () => {
|
|
|
423
423
|
})
|
|
424
424
|
|
|
425
425
|
const Child = Shade({
|
|
426
|
-
|
|
426
|
+
customElementName: 'example-remount-child',
|
|
427
427
|
render: ({ useState }) => {
|
|
428
428
|
const [count, setCount] = useState('count', 0)
|
|
429
429
|
|
|
@@ -493,14 +493,14 @@ describe('Shades integration tests', () => {
|
|
|
493
493
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
494
494
|
|
|
495
495
|
const ChildComponent = Shade<{ isDisabled: boolean }>({
|
|
496
|
-
|
|
496
|
+
customElementName: 'shade-prop-child',
|
|
497
497
|
render: ({ props }) => {
|
|
498
498
|
return <input type="checkbox" disabled={props.isDisabled} data-testid="inner-input" />
|
|
499
499
|
},
|
|
500
500
|
})
|
|
501
501
|
|
|
502
502
|
const ParentComponent = Shade({
|
|
503
|
-
|
|
503
|
+
customElementName: 'shade-prop-parent',
|
|
504
504
|
render: ({ useState }) => {
|
|
505
505
|
const [isDisabled, setIsDisabled] = useState('isDisabled', false)
|
|
506
506
|
return (
|
|
@@ -181,7 +181,7 @@ describe('StyleManager', () => {
|
|
|
181
181
|
describe('Shade integration', () => {
|
|
182
182
|
it('should register CSS styles when Shade component is created with css property', () => {
|
|
183
183
|
Shade({
|
|
184
|
-
|
|
184
|
+
customElementName: 'shade-css-test-component',
|
|
185
185
|
css: {
|
|
186
186
|
color: 'red',
|
|
187
187
|
padding: '10px',
|
|
@@ -200,7 +200,7 @@ describe('StyleManager', () => {
|
|
|
200
200
|
|
|
201
201
|
it('should register CSS with attribute selector for customized built-in elements', () => {
|
|
202
202
|
Shade({
|
|
203
|
-
|
|
203
|
+
customElementName: 'shade-css-test-button',
|
|
204
204
|
elementBase: HTMLButtonElement,
|
|
205
205
|
elementBaseName: 'button',
|
|
206
206
|
css: {
|
|
@@ -219,7 +219,7 @@ describe('StyleManager', () => {
|
|
|
219
219
|
|
|
220
220
|
it('should not register styles when Shade component has no css property', () => {
|
|
221
221
|
Shade({
|
|
222
|
-
|
|
222
|
+
customElementName: 'shade-no-css-component',
|
|
223
223
|
render: () => null,
|
|
224
224
|
})
|
|
225
225
|
|
package/src/style-manager.ts
CHANGED
|
@@ -27,7 +27,7 @@ class StyleManagerClass {
|
|
|
27
27
|
* Registers CSS styles for a component.
|
|
28
28
|
* Styles are only injected once per component (based on the custom element name).
|
|
29
29
|
*
|
|
30
|
-
* @param
|
|
30
|
+
* @param customElementName - The custom element tag name (used as CSS selector)
|
|
31
31
|
* @param cssObject - The CSSObject containing styles and nested selectors
|
|
32
32
|
* @param elementBaseName - Optional base element name for customized built-in elements (e.g., 'a', 'button').
|
|
33
33
|
* When provided, generates selector like `a[is="component-name"]` instead of `component-name`
|
|
@@ -49,17 +49,17 @@ class StyleManagerClass {
|
|
|
49
49
|
* // Generates: a[is="my-link"] { color: blue; }
|
|
50
50
|
* ```
|
|
51
51
|
*/
|
|
52
|
-
public registerComponentStyles(
|
|
53
|
-
if (this.registeredComponents.has(
|
|
52
|
+
public registerComponentStyles(customElementName: string, cssObject: CSSObject, elementBaseName?: string): boolean {
|
|
53
|
+
if (this.registeredComponents.has(customElementName)) {
|
|
54
54
|
return false
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
const selector = elementBaseName ? `${elementBaseName}[is="${
|
|
57
|
+
const selector = elementBaseName ? `${elementBaseName}[is="${customElementName}"]` : customElementName
|
|
58
58
|
const css = generateCSS(selector, cssObject)
|
|
59
59
|
if (css) {
|
|
60
60
|
const styleElement = this.getStyleElement()
|
|
61
|
-
styleElement.textContent += `\n/* ${
|
|
62
|
-
this.registeredComponents.add(
|
|
61
|
+
styleElement.textContent += `\n/* ${customElementName} */\n${css}\n`
|
|
62
|
+
this.registeredComponents.add(customElementName)
|
|
63
63
|
return true
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -68,11 +68,11 @@ class StyleManagerClass {
|
|
|
68
68
|
|
|
69
69
|
/**
|
|
70
70
|
* Checks if a component's styles have already been registered
|
|
71
|
-
* @param
|
|
71
|
+
* @param customElementName - The component identifier to check
|
|
72
72
|
* @returns True if styles are already registered
|
|
73
73
|
*/
|
|
74
|
-
public isRegistered(
|
|
75
|
-
return this.registeredComponents.has(
|
|
74
|
+
public isRegistered(customElementName: string): boolean {
|
|
75
|
+
return this.registeredComponents.has(customElementName)
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
/**
|
|
@@ -19,7 +19,7 @@ describe('VNode reconciliation integration tests', () => {
|
|
|
19
19
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
20
20
|
|
|
21
21
|
const ExampleComponent = Shade({
|
|
22
|
-
|
|
22
|
+
customElementName: 'morph-focus-test',
|
|
23
23
|
render: ({ useState }) => {
|
|
24
24
|
const [label, setLabel] = useState('label', 'initial')
|
|
25
25
|
return (
|
|
@@ -64,7 +64,7 @@ describe('VNode reconciliation integration tests', () => {
|
|
|
64
64
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
65
65
|
|
|
66
66
|
const ExampleComponent = Shade({
|
|
67
|
-
|
|
67
|
+
customElementName: 'morph-focus-textarea-test',
|
|
68
68
|
render: ({ useState }) => {
|
|
69
69
|
const [count, setCount] = useState('count', 0)
|
|
70
70
|
return (
|
|
@@ -106,7 +106,7 @@ describe('VNode reconciliation integration tests', () => {
|
|
|
106
106
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
107
107
|
|
|
108
108
|
const ExampleComponent = Shade({
|
|
109
|
-
|
|
109
|
+
customElementName: 'morph-form-value-test',
|
|
110
110
|
render: ({ useState }) => {
|
|
111
111
|
const [title, setTitle] = useState('title', 'Title')
|
|
112
112
|
return (
|
|
@@ -151,7 +151,7 @@ describe('VNode reconciliation integration tests', () => {
|
|
|
151
151
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
152
152
|
|
|
153
153
|
const ExampleComponent = Shade({
|
|
154
|
-
|
|
154
|
+
customElementName: 'morph-checkbox-test',
|
|
155
155
|
render: ({ useState }) => {
|
|
156
156
|
const [count, setCount] = useState('count', 0)
|
|
157
157
|
return (
|
|
@@ -195,7 +195,7 @@ describe('VNode reconciliation integration tests', () => {
|
|
|
195
195
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
196
196
|
|
|
197
197
|
const ExampleComponent = Shade({
|
|
198
|
-
|
|
198
|
+
customElementName: 'morph-select-test',
|
|
199
199
|
render: ({ useState }) => {
|
|
200
200
|
const [label, setLabel] = useState('label', 'Pick one')
|
|
201
201
|
return (
|
|
@@ -245,7 +245,7 @@ describe('VNode reconciliation integration tests', () => {
|
|
|
245
245
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
246
246
|
|
|
247
247
|
const ExampleComponent = Shade({
|
|
248
|
-
|
|
248
|
+
customElementName: 'morph-identity-test',
|
|
249
249
|
render: ({ useState }) => {
|
|
250
250
|
const [count, setCount] = useState('count', 0)
|
|
251
251
|
return (
|
|
@@ -287,7 +287,7 @@ describe('VNode reconciliation integration tests', () => {
|
|
|
287
287
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
288
288
|
|
|
289
289
|
const ExampleComponent = Shade({
|
|
290
|
-
|
|
290
|
+
customElementName: 'morph-tag-change-test',
|
|
291
291
|
render: ({ useState }) => {
|
|
292
292
|
const [useDiv, setUseDiv] = useState('useDiv', true)
|
|
293
293
|
return useDiv ? (
|
|
@@ -333,7 +333,7 @@ describe('VNode reconciliation integration tests', () => {
|
|
|
333
333
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
334
334
|
|
|
335
335
|
const ExampleComponent = Shade({
|
|
336
|
-
|
|
336
|
+
customElementName: 'morph-animation-test',
|
|
337
337
|
render: ({ useState }) => {
|
|
338
338
|
const [isActive, setIsActive] =
|
|
339
339
|
// eslint-disable-next-line furystack/no-css-state-hooks -- test for re-render behavior, not CSS state
|
|
@@ -374,7 +374,7 @@ describe('VNode reconciliation integration tests', () => {
|
|
|
374
374
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
375
375
|
|
|
376
376
|
const ExampleComponent = Shade({
|
|
377
|
-
|
|
377
|
+
customElementName: 'morph-style-transition-test',
|
|
378
378
|
render: ({ useState }) => {
|
|
379
379
|
const [isExpanded, setIsExpanded] = useState('isExpanded', false)
|
|
380
380
|
return (
|
|
@@ -424,7 +424,7 @@ describe('VNode reconciliation integration tests', () => {
|
|
|
424
424
|
const clicks: number[] = []
|
|
425
425
|
|
|
426
426
|
const ExampleComponent = Shade({
|
|
427
|
-
|
|
427
|
+
customElementName: 'morph-handler-test',
|
|
428
428
|
render: ({ useState }) => {
|
|
429
429
|
const [count, setCount] = useState('count', 0)
|
|
430
430
|
return (
|
|
@@ -482,7 +482,7 @@ describe('VNode reconciliation integration tests', () => {
|
|
|
482
482
|
const obs = new ObservableValue('hello')
|
|
483
483
|
|
|
484
484
|
const ExampleComponent = Shade({
|
|
485
|
-
|
|
485
|
+
customElementName: 'morph-observable-test',
|
|
486
486
|
render: ({ useObservable }) => {
|
|
487
487
|
const [value] = useObservable('obs', obs)
|
|
488
488
|
return (
|
|
@@ -528,7 +528,7 @@ describe('VNode reconciliation integration tests', () => {
|
|
|
528
528
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
529
529
|
|
|
530
530
|
const ExampleComponent = Shade({
|
|
531
|
-
|
|
531
|
+
customElementName: 'morph-fragment-test',
|
|
532
532
|
render: ({ useState }) => {
|
|
533
533
|
const [count, setCount] = useState('count', 0)
|
|
534
534
|
return (
|
|
@@ -569,7 +569,7 @@ describe('VNode reconciliation integration tests', () => {
|
|
|
569
569
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
570
570
|
|
|
571
571
|
const ExampleComponent = Shade({
|
|
572
|
-
|
|
572
|
+
customElementName: 'morph-text-result-test',
|
|
573
573
|
render: ({ useState }) => {
|
|
574
574
|
const [text] = useState('text', 'initial')
|
|
575
575
|
return text
|
|
@@ -611,7 +611,7 @@ describe('VNode reconciliation integration tests', () => {
|
|
|
611
611
|
const childRenderSpy = vi.fn()
|
|
612
612
|
|
|
613
613
|
const ChildComponent = Shade<{ value: number }>({
|
|
614
|
-
|
|
614
|
+
customElementName: 'morph-child-component',
|
|
615
615
|
render: ({ props }) => {
|
|
616
616
|
childRenderSpy()
|
|
617
617
|
return <span id="child-value">{props.value}</span>
|
|
@@ -619,7 +619,7 @@ describe('VNode reconciliation integration tests', () => {
|
|
|
619
619
|
})
|
|
620
620
|
|
|
621
621
|
const ParentComponent = Shade({
|
|
622
|
-
|
|
622
|
+
customElementName: 'morph-parent-component',
|
|
623
623
|
render: ({ useState }) => {
|
|
624
624
|
const [count, setCount] = useState('count', 0)
|
|
625
625
|
return (
|