@furystack/shades 10.0.6 → 11.0.1
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/esm/compile-route.d.ts +2 -0
- package/esm/compile-route.d.ts.map +1 -0
- package/esm/compile-route.js +4 -0
- package/esm/compile-route.js.map +1 -0
- package/esm/components/lazy-load.d.ts +2 -2
- package/esm/components/lazy-load.d.ts.map +1 -1
- package/esm/components/lazy-load.spec.js +2 -2
- package/esm/components/lazy-load.spec.js.map +1 -1
- package/esm/components/link-to-route.d.ts +1 -1
- package/esm/components/link-to-route.d.ts.map +1 -1
- package/esm/components/link-to-route.js +3 -3
- package/esm/components/link-to-route.js.map +1 -1
- package/esm/components/route-link.d.ts +2 -2
- package/esm/components/route-link.d.ts.map +1 -1
- package/esm/components/router.d.ts +4 -4
- package/esm/components/router.d.ts.map +1 -1
- package/esm/components/router.js +4 -4
- package/esm/components/router.js.map +1 -1
- package/esm/components/router.spec.js +1 -1
- package/esm/components/router.spec.js.map +1 -1
- package/esm/index.d.ts +5 -4
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +5 -4
- package/esm/index.js.map +1 -1
- package/esm/models/render-options.d.ts +2 -2
- package/esm/models/render-options.d.ts.map +1 -1
- package/esm/models/shade-component.d.ts.map +1 -1
- package/esm/services/location-service.d.ts +6 -7
- package/esm/services/location-service.d.ts.map +1 -1
- package/esm/services/location-service.js +24 -26
- package/esm/services/location-service.js.map +1 -1
- package/esm/services/location-service.spec.js +21 -21
- package/esm/services/location-service.spec.js.map +1 -1
- package/esm/services/resource-manager.d.ts +4 -5
- package/esm/services/resource-manager.d.ts.map +1 -1
- package/esm/services/resource-manager.js +18 -5
- package/esm/services/resource-manager.js.map +1 -1
- package/esm/services/resource-manager.spec.js +55 -7
- package/esm/services/resource-manager.spec.js.map +1 -1
- package/esm/services/screen-service.d.ts +1 -2
- package/esm/services/screen-service.d.ts.map +1 -1
- package/esm/services/screen-service.js +1 -1
- package/esm/services/screen-service.js.map +1 -1
- package/esm/shade-component.d.ts +6 -6
- package/esm/shade-component.d.ts.map +1 -1
- package/esm/shade-component.js +8 -5
- package/esm/shade-component.js.map +1 -1
- package/esm/shade-resources.integration.spec.js +6 -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 +5 -5
- package/esm/shade.js.map +1 -1
- package/esm/shades.integration.spec.js +79 -4
- package/esm/shades.integration.spec.js.map +1 -1
- package/esm/styled-element.d.ts.map +1 -1
- package/esm/styled-shade.d.ts.map +1 -1
- package/esm/styled-shade.js +13 -7
- package/esm/styled-shade.js.map +1 -1
- package/package.json +8 -8
- package/src/compile-route.ts +6 -0
- package/src/components/lazy-load.spec.tsx +3 -3
- package/src/components/link-to-route.tsx +4 -4
- package/src/components/router.spec.tsx +1 -1
- package/src/components/router.tsx +7 -7
- package/src/index.ts +5 -4
- package/src/models/render-options.ts +2 -2
- package/src/services/location-service.spec.ts +23 -23
- package/src/services/location-service.tsx +29 -31
- package/src/services/resource-manager.spec.ts +74 -7
- package/src/services/resource-manager.ts +29 -10
- package/src/services/screen-service.ts +1 -2
- package/src/shade-component.ts +16 -12
- package/src/shade-resources.integration.spec.tsx +7 -5
- package/src/shade.ts +6 -6
- package/src/shades.integration.spec.tsx +107 -4
- package/src/styled-shade.ts +13 -7
package/src/shade-component.ts
CHANGED
|
@@ -20,8 +20,10 @@ export const appendChild = (el: HTMLElement | DocumentFragment, children: Childr
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export const hasStyle = (props:
|
|
24
|
-
return
|
|
23
|
+
export const hasStyle = (props: unknown): props is { style: Partial<CSSStyleDeclaration> } => {
|
|
24
|
+
return (
|
|
25
|
+
!!props && typeof props === 'object' && typeof (props as { style: Partial<CSSStyleDeclaration> }).style === 'object'
|
|
26
|
+
)
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
/**
|
|
@@ -37,7 +39,7 @@ export const attachStyles = (el: HTMLElement, props: any) => {
|
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
41
|
|
|
40
|
-
export const attachDataAttributes = (el: HTMLElement, props:
|
|
42
|
+
export const attachDataAttributes = <TProps extends object>(el: HTMLElement, props: TProps) => {
|
|
41
43
|
props &&
|
|
42
44
|
Object.entries(props)
|
|
43
45
|
.filter(([key]) => key.startsWith('data-'))
|
|
@@ -49,17 +51,17 @@ export const attachDataAttributes = (el: HTMLElement, props: any) => {
|
|
|
49
51
|
* @param el The Target HTML Element
|
|
50
52
|
* @param props The Props to attach
|
|
51
53
|
*/
|
|
52
|
-
export const attachProps = (el: HTMLElement, props:
|
|
54
|
+
export const attachProps = <TProps extends object>(el: HTMLElement, props: TProps) => {
|
|
53
55
|
if (!props) {
|
|
54
56
|
return
|
|
55
57
|
}
|
|
58
|
+
attachStyles(el, props)
|
|
56
59
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
attachStyles(el, props)
|
|
60
|
+
if (hasStyle(props)) {
|
|
61
|
+
const { style, ...rest } = props
|
|
62
|
+
Object.assign(el, rest)
|
|
63
|
+
} else {
|
|
64
|
+
Object.assign(el, props)
|
|
63
65
|
}
|
|
64
66
|
attachDataAttributes(el, props)
|
|
65
67
|
}
|
|
@@ -74,7 +76,9 @@ type CreateComponentArgs<TProps> = [
|
|
|
74
76
|
* Factory method that creates a component. This should be configured as a default JSX Factory in tsconfig.
|
|
75
77
|
* @returns the created JSX element
|
|
76
78
|
*/
|
|
77
|
-
export const createComponentInner = <TProps
|
|
79
|
+
export const createComponentInner = <TProps extends object>(
|
|
80
|
+
...[elementType, props, ...children]: CreateComponentArgs<TProps>
|
|
81
|
+
) => {
|
|
78
82
|
if (typeof elementType === 'string') {
|
|
79
83
|
const el = document.createElement(elementType)
|
|
80
84
|
|
|
@@ -100,7 +104,7 @@ export const createFragmentInner = (...[_props, ...children]: CreateFragmentArgs
|
|
|
100
104
|
return fragment
|
|
101
105
|
}
|
|
102
106
|
|
|
103
|
-
export const createComponent = <TProps>(...args: CreateComponentArgs<TProps> | CreateFragmentArgs) => {
|
|
107
|
+
export const createComponent = <TProps extends object>(...args: CreateComponentArgs<TProps> | CreateFragmentArgs) => {
|
|
104
108
|
if (args[0] === null) {
|
|
105
109
|
return createFragmentInner(...args)
|
|
106
110
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { Injector } from '@furystack/inject'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { TextDecoder, TextEncoder } from 'util'
|
|
4
4
|
|
|
5
5
|
global.TextEncoder = TextEncoder
|
|
6
6
|
global.TextDecoder = TextDecoder as any
|
|
7
7
|
|
|
8
|
+
import { ObservableValue, sleepAsync } from '@furystack/utils'
|
|
9
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
8
10
|
import { initializeShadeRoot } from './initialize.js'
|
|
9
|
-
import { Shade } from './shade.js'
|
|
10
11
|
import { createComponent } from './shade-component.js'
|
|
11
|
-
import {
|
|
12
|
-
import { describe, it, expect, afterEach, beforeEach, vi } from 'vitest'
|
|
12
|
+
import { Shade } from './shade.js'
|
|
13
13
|
|
|
14
14
|
describe('Shade Resources integration tests', () => {
|
|
15
15
|
beforeEach(() => {
|
|
@@ -19,7 +19,7 @@ describe('Shade Resources integration tests', () => {
|
|
|
19
19
|
document.body.innerHTML = ''
|
|
20
20
|
})
|
|
21
21
|
|
|
22
|
-
it('Should update the component based on a custom observable value change', () => {
|
|
22
|
+
it('Should update the component based on a custom observable value change', async () => {
|
|
23
23
|
const injector = new Injector()
|
|
24
24
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
25
25
|
|
|
@@ -77,6 +77,8 @@ describe('Shade Resources integration tests', () => {
|
|
|
77
77
|
|
|
78
78
|
document.body.innerHTML = ''
|
|
79
79
|
|
|
80
|
+
await sleepAsync(10) // Cleanup can be async
|
|
81
|
+
|
|
80
82
|
expect(obs1.getObservers().length).toBe(0)
|
|
81
83
|
expect(obs2.getObservers().length).toBe(0)
|
|
82
84
|
|
package/src/shade.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { ObservableValue } from '@furystack/utils'
|
|
2
1
|
import type { Constructable } from '@furystack/inject'
|
|
3
2
|
import { Injector } from '@furystack/inject'
|
|
3
|
+
import { ObservableValue } from '@furystack/utils'
|
|
4
4
|
import type { ChildrenList, PartialElement, RenderOptions } from './models/index.js'
|
|
5
|
-
import { ResourceManager } from './services/resource-manager.js'
|
|
6
5
|
import { LocationService } from './services/location-service.js'
|
|
6
|
+
import { ResourceManager } from './services/resource-manager.js'
|
|
7
7
|
import { attachProps, attachStyles } from './shade-component.js'
|
|
8
8
|
|
|
9
9
|
export type ShadeOptions<TProps, TElementBase extends HTMLElement> = {
|
|
@@ -86,7 +86,7 @@ export const Shade = <TProps, TElementBase extends HTMLElement = HTMLElement>(
|
|
|
86
86
|
|
|
87
87
|
public disconnectedCallback() {
|
|
88
88
|
o.onDetach?.(this.getRenderOptions())
|
|
89
|
-
this.resourceManager.
|
|
89
|
+
this.resourceManager[Symbol.asyncDispose]()
|
|
90
90
|
this.cleanup?.()
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -173,9 +173,9 @@ export const Shade = <TProps, TElementBase extends HTMLElement = HTMLElement>(
|
|
|
173
173
|
})
|
|
174
174
|
|
|
175
175
|
return {
|
|
176
|
-
dispose: () => {
|
|
176
|
+
[Symbol.dispose]: () => {
|
|
177
177
|
window.removeEventListener('storage', updateFromStorageEvent)
|
|
178
|
-
subscription.dispose()
|
|
178
|
+
subscription[Symbol.dispose]()
|
|
179
179
|
messageChannel.close()
|
|
180
180
|
},
|
|
181
181
|
}
|
|
@@ -190,7 +190,7 @@ export const Shade = <TProps, TElementBase extends HTMLElement = HTMLElement>(
|
|
|
190
190
|
useDisposable: this.resourceManager.useDisposable.bind(this.resourceManager),
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
return renderOptions
|
|
193
|
+
return renderOptions
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
/**
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { Injector } from '@furystack/inject'
|
|
2
|
-
import { usingAsync } from '@furystack/utils'
|
|
2
|
+
import { sleepAsync, usingAsync } from '@furystack/utils'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { TextDecoder, TextEncoder } from 'util'
|
|
5
5
|
|
|
6
6
|
global.TextEncoder = TextEncoder
|
|
7
7
|
global.TextDecoder = TextDecoder as any
|
|
8
8
|
|
|
9
|
+
import { serializeToQueryString } from '@furystack/rest'
|
|
10
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
9
11
|
import { initializeShadeRoot } from './initialize.js'
|
|
10
|
-
import { Shade } from './shade.js'
|
|
11
12
|
import { createComponent } from './shade-component.js'
|
|
12
|
-
import {
|
|
13
|
+
import { Shade } from './shade.js'
|
|
13
14
|
|
|
14
15
|
describe('Shades integration tests', () => {
|
|
15
16
|
beforeEach(() => {
|
|
@@ -199,6 +200,7 @@ describe('Shades integration tests', () => {
|
|
|
199
200
|
expect(constructed).toBeCalled()
|
|
200
201
|
expect(cleanup).not.toBeCalled()
|
|
201
202
|
document.body.innerHTML = ''
|
|
203
|
+
await sleepAsync(10) // Dispose can be async
|
|
202
204
|
expect(cleanup).toBeCalled()
|
|
203
205
|
})
|
|
204
206
|
})
|
|
@@ -271,6 +273,104 @@ describe('Shades integration tests', () => {
|
|
|
271
273
|
})
|
|
272
274
|
})
|
|
273
275
|
|
|
276
|
+
it('Should update the stored state', async () => {
|
|
277
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
278
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
279
|
+
|
|
280
|
+
const ExampleComponent = Shade({
|
|
281
|
+
shadowDomName: 'example-component-3-stored-state',
|
|
282
|
+
render: ({ useStoredState }) => {
|
|
283
|
+
const [count, setCount] = useStoredState('count', 0)
|
|
284
|
+
return (
|
|
285
|
+
<div>
|
|
286
|
+
Count is {count}
|
|
287
|
+
<button id="plus" onclick={() => setCount(count + 1)}>
|
|
288
|
+
+
|
|
289
|
+
</button>
|
|
290
|
+
<button id="minus" onclick={() => setCount(count - 1)}>
|
|
291
|
+
-
|
|
292
|
+
</button>
|
|
293
|
+
</div>
|
|
294
|
+
)
|
|
295
|
+
},
|
|
296
|
+
})
|
|
297
|
+
initializeShadeRoot({
|
|
298
|
+
injector,
|
|
299
|
+
rootElement,
|
|
300
|
+
jsxElement: <ExampleComponent />,
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
const plus = () => document.getElementById('plus')?.click()
|
|
304
|
+
const minus = () => document.getElementById('minus')?.click()
|
|
305
|
+
const expectCount = (count: number) => expect(document.body.innerHTML).toContain(`Count is ${count}`)
|
|
306
|
+
|
|
307
|
+
expectCount(0)
|
|
308
|
+
|
|
309
|
+
await sleepAsync(100)
|
|
310
|
+
plus()
|
|
311
|
+
expectCount(1)
|
|
312
|
+
expect(localStorage.getItem('count')).toBe('1')
|
|
313
|
+
|
|
314
|
+
plus()
|
|
315
|
+
expectCount(2)
|
|
316
|
+
expect(localStorage.getItem('count')).toBe('2')
|
|
317
|
+
|
|
318
|
+
minus()
|
|
319
|
+
minus()
|
|
320
|
+
expectCount(0)
|
|
321
|
+
expect(localStorage.getItem('count')).toBe('0')
|
|
322
|
+
})
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
it('Should update the search state', async () => {
|
|
326
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
327
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
328
|
+
|
|
329
|
+
const ExampleComponent = Shade({
|
|
330
|
+
shadowDomName: 'example-component-3-search-state',
|
|
331
|
+
render: ({ useSearchState }) => {
|
|
332
|
+
const [count, setCount] = useSearchState('count', 0)
|
|
333
|
+
return (
|
|
334
|
+
<div>
|
|
335
|
+
Count is {count}
|
|
336
|
+
<button id="plus" onclick={() => setCount(count + 1)}>
|
|
337
|
+
+
|
|
338
|
+
</button>
|
|
339
|
+
<button id="minus" onclick={() => setCount(count - 1)}>
|
|
340
|
+
-
|
|
341
|
+
</button>
|
|
342
|
+
</div>
|
|
343
|
+
)
|
|
344
|
+
},
|
|
345
|
+
})
|
|
346
|
+
initializeShadeRoot({
|
|
347
|
+
injector,
|
|
348
|
+
rootElement,
|
|
349
|
+
jsxElement: <ExampleComponent />,
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
const plus = () => document.getElementById('plus')?.click()
|
|
353
|
+
const minus = () => document.getElementById('minus')?.click()
|
|
354
|
+
const expectCount = (count: number) => expect(document.body.innerHTML).toContain(`Count is ${count}`)
|
|
355
|
+
|
|
356
|
+
expectCount(0)
|
|
357
|
+
|
|
358
|
+
await sleepAsync(100)
|
|
359
|
+
plus()
|
|
360
|
+
expectCount(1)
|
|
361
|
+
expect(location.search).toBe(`?${serializeToQueryString({ count: 1 })}`)
|
|
362
|
+
|
|
363
|
+
plus()
|
|
364
|
+
expectCount(2)
|
|
365
|
+
expect(location.search).toBe(`?${serializeToQueryString({ count: 2 })}`)
|
|
366
|
+
|
|
367
|
+
minus()
|
|
368
|
+
minus()
|
|
369
|
+
expectCount(0)
|
|
370
|
+
expect(location.search).toBe(`?${serializeToQueryString({ count: 0 })}`)
|
|
371
|
+
})
|
|
372
|
+
})
|
|
373
|
+
|
|
274
374
|
it('Should allow children update after unmount and remount', async () => {
|
|
275
375
|
await usingAsync(new Injector(), async (injector) => {
|
|
276
376
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
@@ -325,6 +425,7 @@ describe('Shades integration tests', () => {
|
|
|
325
425
|
|
|
326
426
|
const plus = () => document.getElementById('plus')?.click()
|
|
327
427
|
const minus = () => document.getElementById('minus')?.click()
|
|
428
|
+
|
|
328
429
|
const expectCount = (count: number) => expect(document.body.innerHTML).toContain(`Count is ${count}`)
|
|
329
430
|
const toggleChildren = () => document.getElementById('showHideChildren')?.click()
|
|
330
431
|
|
|
@@ -336,6 +437,8 @@ describe('Shades integration tests', () => {
|
|
|
336
437
|
|
|
337
438
|
expect(document.getElementById('plus')).toBeNull()
|
|
338
439
|
|
|
440
|
+
await sleepAsync(10) // Dispose can be async
|
|
441
|
+
|
|
339
442
|
toggleChildren()
|
|
340
443
|
expect(document.getElementById('plus')).toBeDefined()
|
|
341
444
|
|
package/src/styled-shade.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ChildrenList } from './models/children-list.js'
|
|
2
|
+
import { hasStyle } from './shade-component.js'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Creates a shortcut for a specific custom Shade element with additional styles
|
|
@@ -11,13 +12,18 @@ export const styledShade = <T extends (props: any, children?: ChildrenList) => J
|
|
|
11
12
|
styles: Partial<CSSStyleDeclaration>,
|
|
12
13
|
) => {
|
|
13
14
|
return ((props: any, childrenList?: ChildrenList) => {
|
|
14
|
-
const mergedProps =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
const mergedProps = hasStyle(props)
|
|
16
|
+
? {
|
|
17
|
+
...props,
|
|
18
|
+
style: {
|
|
19
|
+
...props.style,
|
|
20
|
+
...styles,
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
: {
|
|
24
|
+
...props,
|
|
25
|
+
style: styles,
|
|
26
|
+
}
|
|
21
27
|
return element(mergedProps, childrenList)
|
|
22
28
|
}) as T
|
|
23
29
|
}
|