@furystack/shades 6.1.5 → 7.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/dist/component-factory.spec.js +6 -6
- package/dist/component-factory.spec.js.map +1 -1
- package/dist/components/lazy-load.js +17 -14
- package/dist/components/lazy-load.js.map +1 -1
- package/dist/components/route-link.js +7 -3
- package/dist/components/route-link.js.map +1 -1
- package/dist/components/route-link.spec.js +2 -2
- package/dist/components/route-link.spec.js.map +1 -1
- package/dist/components/router.js +25 -26
- package/dist/components/router.js.map +1 -1
- package/dist/components/router.spec.js +2 -2
- package/dist/components/router.spec.js.map +1 -1
- package/dist/services/location-service.js +52 -4
- package/dist/services/location-service.js.map +1 -1
- package/dist/services/location-service.spec.js +63 -5
- package/dist/services/location-service.spec.js.map +1 -1
- package/dist/services/resource-manager.js +48 -0
- package/dist/services/resource-manager.js.map +1 -0
- package/dist/services/resource-manager.spec.js +32 -0
- package/dist/services/resource-manager.spec.js.map +1 -0
- package/dist/shade-component.js +28 -14
- package/dist/shade-component.js.map +1 -1
- package/dist/shade-resources.integration.spec.js +7 -8
- package/dist/shade-resources.integration.spec.js.map +1 -1
- package/dist/shade.js +79 -69
- package/dist/shade.js.map +1 -1
- package/dist/shades.integration.spec.js +197 -187
- package/dist/shades.integration.spec.js.map +1 -1
- package/package.json +6 -6
- package/src/component-factory.spec.tsx +7 -7
- package/src/components/lazy-load.tsx +19 -15
- package/src/components/route-link.spec.tsx +2 -2
- package/src/components/route-link.tsx +11 -10
- package/src/components/router.spec.tsx +2 -2
- package/src/components/router.tsx +32 -32
- package/src/jsx.ts +3 -2
- package/src/models/render-options.ts +59 -9
- package/src/services/location-service.spec.ts +75 -6
- package/src/services/location-service.tsx +58 -4
- package/src/services/resource-manager.spec.ts +33 -0
- package/src/services/resource-manager.ts +60 -0
- package/src/shade-component.ts +35 -15
- package/src/shade-resources.integration.spec.tsx +8 -14
- package/src/shade.ts +95 -104
- package/src/shades.integration.spec.tsx +265 -252
- package/types/components/lazy-load.d.ts +1 -1
- package/types/components/lazy-load.d.ts.map +1 -1
- package/types/components/route-link.d.ts +1 -1
- package/types/components/route-link.d.ts.map +1 -1
- package/types/components/router.d.ts +6 -8
- package/types/components/router.d.ts.map +1 -1
- package/types/jsx.d.ts +3 -2
- package/types/jsx.d.ts.map +1 -1
- package/types/models/render-options.d.ts +46 -7
- package/types/models/render-options.d.ts.map +1 -1
- package/types/services/location-service.d.ts +21 -1
- package/types/services/location-service.d.ts.map +1 -1
- package/types/services/resource-manager.d.ts +16 -0
- package/types/services/resource-manager.d.ts.map +1 -0
- package/types/services/resource-manager.spec.d.ts +2 -0
- package/types/services/resource-manager.spec.d.ts.map +1 -0
- package/types/shade-component.d.ts +16 -5
- package/types/shade-component.d.ts.map +1 -1
- package/types/shade.d.ts +8 -27
- package/types/shade.d.ts.map +1 -1
package/src/shade.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { Disposable } from '@furystack/utils'
|
|
2
|
+
import { ObservableValue } from '@furystack/utils'
|
|
2
3
|
import { Injector } from '@furystack/inject'
|
|
3
|
-
import type { ChildrenList,
|
|
4
|
+
import type { ChildrenList, RenderOptions } from './models'
|
|
5
|
+
import { ResourceManager } from './services/resource-manager'
|
|
6
|
+
import { LocationService } from './services'
|
|
4
7
|
|
|
5
|
-
export type ShadeOptions<TProps
|
|
8
|
+
export type ShadeOptions<TProps> = {
|
|
6
9
|
/**
|
|
7
10
|
* Explicit shadow dom name. Will fall back to 'shade-{guid}' if not provided
|
|
8
11
|
*/
|
|
@@ -11,48 +14,30 @@ export type ShadeOptions<TProps, TState> = {
|
|
|
11
14
|
/**
|
|
12
15
|
* Render hook, this method will be executed on each and every render.
|
|
13
16
|
*/
|
|
14
|
-
render: (options: RenderOptions<TProps
|
|
17
|
+
render: (options: RenderOptions<TProps>) => JSX.Element | string | null
|
|
15
18
|
|
|
16
19
|
/**
|
|
17
20
|
* Construct hook. Will be executed once when the element has been constructed and initialized
|
|
18
21
|
*/
|
|
19
22
|
constructed?: (
|
|
20
|
-
options: RenderOptions<TProps
|
|
23
|
+
options: RenderOptions<TProps>,
|
|
21
24
|
) => void | undefined | (() => void) | Promise<void | undefined | (() => void)>
|
|
22
25
|
|
|
23
26
|
/**
|
|
24
27
|
* Will be executed when the element is attached to the DOM.
|
|
25
28
|
*/
|
|
26
|
-
onAttach?: (options: RenderOptions<TProps
|
|
29
|
+
onAttach?: (options: RenderOptions<TProps>) => void
|
|
27
30
|
|
|
28
31
|
/**
|
|
29
32
|
* Will be executed when the element is detached from the DOM.
|
|
30
33
|
*/
|
|
31
|
-
onDetach?: (options: RenderOptions<TProps
|
|
34
|
+
onDetach?: (options: RenderOptions<TProps>) => void
|
|
32
35
|
|
|
33
36
|
/**
|
|
34
37
|
* A factory method that creates a list of disposable resources that will be disposed when the element is detached.
|
|
35
38
|
*/
|
|
36
|
-
resources?: (options: RenderOptions<TProps
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* An optional method that checks the state for changes and returns true if the element should be rerendered. This will not be called if `skipRender` is set to true in the relevant `updateState()` call.
|
|
40
|
-
*/
|
|
41
|
-
compareState?: (options: {
|
|
42
|
-
oldState: TState
|
|
43
|
-
newState: TState
|
|
44
|
-
props: TProps
|
|
45
|
-
element: HTMLElement
|
|
46
|
-
injector: Injector
|
|
47
|
-
}) => boolean
|
|
48
|
-
} & (unknown extends TState
|
|
49
|
-
? {}
|
|
50
|
-
: {
|
|
51
|
-
/**
|
|
52
|
-
* The initial state of the component
|
|
53
|
-
*/
|
|
54
|
-
getInitialState: (options: { injector: Injector; props: TProps }) => TState
|
|
55
|
-
})
|
|
39
|
+
resources?: (options: RenderOptions<TProps>) => Disposable[]
|
|
40
|
+
}
|
|
56
41
|
|
|
57
42
|
/**
|
|
58
43
|
* Factory method for creating Shade components
|
|
@@ -60,7 +45,7 @@ export type ShadeOptions<TProps, TState> = {
|
|
|
60
45
|
* @param o for component creation
|
|
61
46
|
* @returns the JSX element
|
|
62
47
|
*/
|
|
63
|
-
export const Shade = <TProps
|
|
48
|
+
export const Shade = <TProps>(o: ShadeOptions<TProps>) => {
|
|
64
49
|
// register shadow-dom element
|
|
65
50
|
const customElementName = o.shadowDomName
|
|
66
51
|
|
|
@@ -69,11 +54,7 @@ export const Shade = <TProps, TState = unknown>(o: ShadeOptions<TProps, TState>)
|
|
|
69
54
|
customElements.define(
|
|
70
55
|
customElementName,
|
|
71
56
|
class extends HTMLElement implements JSX.Element {
|
|
72
|
-
|
|
73
|
-
o.compareState ||
|
|
74
|
-
(({ oldState, newState }: { oldState: TState; newState: TState }) =>
|
|
75
|
-
Object.entries(oldState as object).some(([key, value]) => value !== newState[key as keyof TState]) ||
|
|
76
|
-
Object.entries(newState as object).some(([key, value]) => value !== oldState[key as keyof TState]))
|
|
57
|
+
public resourceManager = new ResourceManager()
|
|
77
58
|
|
|
78
59
|
public connectedCallback() {
|
|
79
60
|
o.onAttach && o.onAttach(this.getRenderOptions())
|
|
@@ -82,19 +63,14 @@ export const Shade = <TProps, TState = unknown>(o: ShadeOptions<TProps, TState>)
|
|
|
82
63
|
|
|
83
64
|
public disconnectedCallback() {
|
|
84
65
|
o.onDetach && o.onDetach(this.getRenderOptions())
|
|
85
|
-
|
|
66
|
+
this.resourceManager.dispose()
|
|
86
67
|
this.cleanup && this.cleanup()
|
|
87
68
|
}
|
|
88
69
|
|
|
89
70
|
/**
|
|
90
71
|
* Will be triggered when updating the external props object
|
|
91
72
|
*/
|
|
92
|
-
public props
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Will be triggered on state update
|
|
96
|
-
*/
|
|
97
|
-
public state!: TState
|
|
73
|
+
public props!: TProps & { children?: JSX.Element[] }
|
|
98
74
|
|
|
99
75
|
/**
|
|
100
76
|
* Will be updated when on children change
|
|
@@ -105,55 +81,88 @@ export const Shade = <TProps, TState = unknown>(o: ShadeOptions<TProps, TState>)
|
|
|
105
81
|
* @param options Options for rendering the component
|
|
106
82
|
* @returns the JSX element
|
|
107
83
|
*/
|
|
108
|
-
public render = (options: RenderOptions<TProps
|
|
84
|
+
public render = (options: RenderOptions<TProps>) => o.render(options)
|
|
109
85
|
|
|
110
86
|
/**
|
|
111
87
|
* @returns values for the current render options
|
|
112
88
|
*/
|
|
113
|
-
private getRenderOptions = (): RenderOptions<TProps
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
89
|
+
private getRenderOptions = (): RenderOptions<TProps> => {
|
|
90
|
+
const renderOptions: RenderOptions<TProps> = {
|
|
91
|
+
props: this.props,
|
|
92
|
+
injector: this.injector,
|
|
93
|
+
|
|
94
|
+
children: this.shadeChildren,
|
|
95
|
+
element: this,
|
|
96
|
+
useObservable: (key, obesrvable, callback, getLast) =>
|
|
97
|
+
this.resourceManager.useObservable(key, obesrvable, callback || (() => this.updateComponent()), getLast),
|
|
98
|
+
useState: (key, initialValue) =>
|
|
99
|
+
this.resourceManager.useState(key, initialValue, this.updateComponent.bind(this)),
|
|
100
|
+
useSearchState: (key, initialValue) =>
|
|
101
|
+
this.resourceManager.useObservable(
|
|
102
|
+
`useSearchState-${key}`,
|
|
103
|
+
this.injector.getInstance(LocationService).useSearchParam(key, initialValue),
|
|
104
|
+
() => this.updateComponent(),
|
|
105
|
+
),
|
|
106
|
+
|
|
107
|
+
useStoredState: <T>(key: string, initialValue: T, storageArea = localStorage) => {
|
|
108
|
+
const getFromStorage = () => {
|
|
109
|
+
const value = storageArea?.getItem(key)
|
|
110
|
+
return value ? JSON.parse(value) : initialValue
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const setToStorage = (value: T) => {
|
|
114
|
+
if (JSON.stringify(value) !== storageArea?.getItem(key)) {
|
|
115
|
+
const newValue = JSON.stringify(value)
|
|
116
|
+
storageArea?.setItem(key, newValue)
|
|
117
|
+
}
|
|
118
|
+
if (JSON.stringify(observable.getValue()) !== JSON.stringify(value)) {
|
|
119
|
+
observable.setValue(value)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const observable = this.resourceManager.useDisposable(
|
|
124
|
+
`useStoredState-${key}`,
|
|
125
|
+
() => new ObservableValue(getFromStorage()),
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
const updateFromStorageEvent = (e: StorageEvent) => {
|
|
129
|
+
e.key === key &&
|
|
130
|
+
e.storageArea === storageArea &&
|
|
131
|
+
setToStorage((e.newValue && JSON.parse(e.newValue)) || initialValue)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
this.resourceManager.useDisposable(`useStoredState-${key}-storage-event`, () => {
|
|
135
|
+
window.addEventListener('storage', updateFromStorageEvent)
|
|
136
|
+
const channelName = `useStoredState-broadcast-channel`
|
|
137
|
+
const messageChannel = new BroadcastChannel(channelName)
|
|
138
|
+
messageChannel.onmessage = (e) => {
|
|
139
|
+
if (e.data.key === key) {
|
|
140
|
+
setToStorage(e.data.value)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const subscription = observable.subscribe((value) => {
|
|
144
|
+
messageChannel.postMessage({ key, value })
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
dispose: () => {
|
|
149
|
+
window.removeEventListener('storage', updateFromStorageEvent)
|
|
150
|
+
subscription.dispose()
|
|
151
|
+
messageChannel.close()
|
|
152
|
+
},
|
|
153
|
+
}
|
|
130
154
|
})
|
|
131
|
-
) {
|
|
132
|
-
this.updateComponent()
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
155
|
|
|
136
|
-
|
|
137
|
-
...{
|
|
138
|
-
props,
|
|
139
|
-
injector: this.injector,
|
|
156
|
+
observable.subscribe(setToStorage)
|
|
140
157
|
|
|
141
|
-
|
|
142
|
-
|
|
158
|
+
return this.resourceManager.useObservable(`useStoredState-${key}`, observable, () =>
|
|
159
|
+
this.updateComponent(),
|
|
160
|
+
)
|
|
143
161
|
},
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
getState,
|
|
147
|
-
updateState,
|
|
148
|
-
}
|
|
149
|
-
: {}),
|
|
150
|
-
} as any as RenderOptions<TProps, TState>
|
|
151
|
-
|
|
152
|
-
return returnValue
|
|
153
|
-
}
|
|
162
|
+
useDisposable: this.resourceManager.useDisposable.bind(this.resourceManager),
|
|
163
|
+
}
|
|
154
164
|
|
|
155
|
-
|
|
156
|
-
this.resources.push(...(o.resources?.(this.getRenderOptions()) || []))
|
|
165
|
+
return renderOptions as RenderOptions<TProps>
|
|
157
166
|
}
|
|
158
167
|
|
|
159
168
|
/**
|
|
@@ -162,32 +171,27 @@ export const Shade = <TProps, TState = unknown>(o: ShadeOptions<TProps, TState>)
|
|
|
162
171
|
public updateComponent() {
|
|
163
172
|
const renderResult = this.render(this.getRenderOptions())
|
|
164
173
|
|
|
165
|
-
if (renderResult === null) {
|
|
174
|
+
if (renderResult === null || renderResult === undefined) {
|
|
166
175
|
this.innerHTML = ''
|
|
167
176
|
}
|
|
168
177
|
|
|
169
|
-
if (typeof renderResult === 'string') {
|
|
178
|
+
if (typeof renderResult === 'string' || typeof renderResult === 'number') {
|
|
170
179
|
this.innerHTML = renderResult
|
|
171
180
|
}
|
|
172
181
|
|
|
173
|
-
if (renderResult instanceof DocumentFragment) {
|
|
174
|
-
this.replaceChildren(...renderResult.children)
|
|
175
|
-
}
|
|
176
|
-
|
|
177
182
|
if (renderResult instanceof HTMLElement) {
|
|
178
183
|
this.replaceChildren(renderResult)
|
|
179
184
|
}
|
|
185
|
+
if (renderResult instanceof DocumentFragment) {
|
|
186
|
+
this.replaceChildren(renderResult)
|
|
187
|
+
}
|
|
180
188
|
}
|
|
181
189
|
|
|
182
190
|
/**
|
|
183
191
|
* Finialize the component initialization after it gets the Props. Called by the framework internally
|
|
184
192
|
*/
|
|
185
193
|
public callConstructed() {
|
|
186
|
-
;(o as any).getInitialState &&
|
|
187
|
-
(this.state = (o as any).getInitialState({ props: { ...this.props }, injector: this.injector }))
|
|
188
|
-
|
|
189
194
|
this.updateComponent()
|
|
190
|
-
this.createResources()
|
|
191
195
|
const cleanupResult = o.constructed && o.constructed(this.getRenderOptions())
|
|
192
196
|
if (cleanupResult instanceof Promise) {
|
|
193
197
|
cleanupResult.then((cleanup) => (this.cleanup = cleanup))
|
|
@@ -216,11 +220,6 @@ export const Shade = <TProps, TState = unknown>(o: ShadeOptions<TProps, TState>)
|
|
|
216
220
|
return this._injector
|
|
217
221
|
}
|
|
218
222
|
|
|
219
|
-
const fromState = (this.state as any)?.injector
|
|
220
|
-
if (fromState && fromState instanceof Injector) {
|
|
221
|
-
return fromState
|
|
222
|
-
}
|
|
223
|
-
|
|
224
223
|
const fromProps = (this.props as any)?.injector
|
|
225
224
|
if (fromProps && fromProps instanceof Injector) {
|
|
226
225
|
return fromProps
|
|
@@ -238,13 +237,6 @@ export const Shade = <TProps, TState = unknown>(o: ShadeOptions<TProps, TState>)
|
|
|
238
237
|
public set injector(i: Injector) {
|
|
239
238
|
this._injector = i
|
|
240
239
|
}
|
|
241
|
-
|
|
242
|
-
private resources: Disposable[] = []
|
|
243
|
-
|
|
244
|
-
constructor(_props: TProps & { children?: JSX.Element[] }) {
|
|
245
|
-
super()
|
|
246
|
-
this.props = _props
|
|
247
|
-
}
|
|
248
240
|
},
|
|
249
241
|
)
|
|
250
242
|
} else {
|
|
@@ -254,9 +246,8 @@ export const Shade = <TProps, TState = unknown>(o: ShadeOptions<TProps, TState>)
|
|
|
254
246
|
return (props: TProps, children: ChildrenList) => {
|
|
255
247
|
const el = document.createElement(customElementName, {
|
|
256
248
|
...(props as TProps & ElementCreationOptions),
|
|
257
|
-
}) as JSX.Element<TProps
|
|
258
|
-
el.props = props
|
|
259
|
-
|
|
249
|
+
}) as JSX.Element<TProps>
|
|
250
|
+
el.props = props || ({} as TProps)
|
|
260
251
|
el.shadeChildren = children
|
|
261
252
|
return el as JSX.Element
|
|
262
253
|
}
|