@furystack/shades 12.5.0 → 13.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 +45 -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/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/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('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 (
|