@sitecore-feaas/clientside 0.0.9 → 0.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/README.md +5 -5
- package/dist/browser/basic.cjs +67 -0
- package/dist/browser/basic.js +67 -0
- package/dist/browser/index.cjs +68 -79
- package/dist/browser/index.js +68 -79
- package/dist/node/{react.cjs → basic.cjs} +436 -443
- package/dist/node/{react.js → basic.js} +432 -439
- package/dist/node/index.cjs +666 -291
- package/dist/node/index.js +666 -295
- package/examples/index.html +1 -1
- package/package.json +17 -17
- package/src/basic.ts +5 -0
- package/src/cloud.ts +9 -5
- package/src/components/FEAASBase.tsx +265 -0
- package/src/components/FEAASComponent.ts +98 -0
- package/src/components/FEAASContext.tsx +105 -0
- package/src/components/FEAASEditor.tsx +291 -0
- package/src/components/FEAASLoader.ts +113 -0
- package/src/components/FEAASPicker.tsx +98 -0
- package/src/components/FEAASStylesheet.ts +43 -0
- package/src/components/index.tsx +7 -0
- package/src/index.ts +4 -11
- package/test/components.js +11 -0
- package/test/components.ts +12 -0
- package/test/render.js +688 -0
- package/tsconfig.json +11 -6
- package/types/basic.d.ts +5 -0
- package/types/cloud.d.ts +18 -0
- package/types/components/FEAASBase.d.ts +70 -0
- package/types/components/FEAASComponent.d.ts +39 -0
- package/types/components/FEAASContext.d.ts +43 -0
- package/types/components/FEAASEditor.d.ts +70 -0
- package/types/components/FEAASLoader.d.ts +20 -0
- package/types/components/FEAASPicker.d.ts +61 -0
- package/types/components/FEAASStylesheet.d.ts +25 -0
- package/types/components/index.d.ts +7 -0
- package/types/dom.d.ts +29 -0
- package/types/fetch.d.ts +1 -0
- package/types/index.d.ts +4 -0
- package/types/tsbuildinfo +1 -0
- package/dist/browser/index.d.ts +0 -25
- package/dist/browser/react.cjs +0 -83
- package/dist/browser/react.d.ts +0 -36
- package/dist/browser/react.js +0 -83
- package/dist/browser/webcomponent-6b45b819.d.ts +0 -120
- package/dist/node/index.d.ts +0 -25
- package/dist/node/react.d.ts +0 -36
- package/dist/node/webcomponent-6b45b819.d.ts +0 -120
- package/index.cjs +0 -83
- package/index.d.ts +0 -25
- package/index.js +0 -83
- package/react.cjs +0 -83
- package/react.d.ts +0 -36
- package/react.js +0 -83
- package/src/react.ts +0 -66
- package/src/webcomponent.ts +0 -393
- package/webcomponent-6b45b819.d.ts +0 -120
package/examples/index.html
CHANGED
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sitecore-feaas/clientside",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"start": "webpack serve --open",
|
|
6
6
|
"build": "rm -rf dist; npm run build:node; npm run build:browser;",
|
|
7
|
-
"build:node": "tsup --entry.index src/index.ts --entry.
|
|
8
|
-
"build:browser": "tsup --entry.index src/index.ts --entry.
|
|
9
|
-
"watch": "tsup --entry.index src/index.ts --entry.
|
|
7
|
+
"build:node": "tsc --build; tsup --entry.index src/index.ts --entry.basic src/basic.ts --target es6 --format cjs,esm -d dist/node",
|
|
8
|
+
"build:browser": "tsc --build; tsup --entry.index src/index.ts --entry.basic src/basic.ts --target es6 --format cjs,esm -d dist/browser --minify",
|
|
9
|
+
"watch": "tsup --entry.index src/index.ts --entry.basic src/basic.ts --target es6 --format esm -d dist/node --watch",
|
|
10
10
|
"test": "npx vitest --config ../frontend/vite.config.ts $FLAGS",
|
|
11
11
|
"test:watch": "npx vitest --config ../frontend/vite.config.ts",
|
|
12
|
-
"prepublishOnly": "npm run build
|
|
13
|
-
"check": "npx tsc --noEmit",
|
|
14
|
-
"postpublish": "
|
|
12
|
+
"prepublishOnly": "npm run build",
|
|
13
|
+
"check": "npx tsc --noEmit --emitDeclarationOnly false",
|
|
14
|
+
"postpublish": "node ../scripts/upload-package-to-blob-storage.js clientside $npm_package_version"
|
|
15
15
|
},
|
|
16
16
|
"license": "ISC",
|
|
17
17
|
"dependencies": {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"tsup": {
|
|
25
25
|
"platform": "browser",
|
|
26
|
-
"dts":
|
|
26
|
+
"dts": false,
|
|
27
27
|
"splitting": false,
|
|
28
28
|
"external": [
|
|
29
29
|
"node-fetch"
|
|
@@ -38,23 +38,23 @@
|
|
|
38
38
|
"react-dom": ">=16.8.0"
|
|
39
39
|
},
|
|
40
40
|
"main": "dist/browser/index.js",
|
|
41
|
-
"types": "dist/browser/
|
|
41
|
+
"types": "dist/browser/index.d.ts",
|
|
42
42
|
"type": "module",
|
|
43
43
|
"exports": {
|
|
44
44
|
".": {
|
|
45
45
|
"typescript": "./src/index.ts",
|
|
46
|
-
"types": "./
|
|
46
|
+
"types": "./types/index.d.ts",
|
|
47
47
|
"require": "./dist/node/index.cjs",
|
|
48
48
|
"browser": "./dist/browser/index.js",
|
|
49
49
|
"default": "./dist/node/index.js"
|
|
50
50
|
},
|
|
51
|
-
"./
|
|
52
|
-
"typescript": "./src/
|
|
53
|
-
"types": "./src/
|
|
54
|
-
"require": "./dist/node/
|
|
55
|
-
"browser": "./dist/browser/
|
|
56
|
-
"development": "./src/
|
|
57
|
-
"default": "./dist/node/
|
|
51
|
+
"./basic": {
|
|
52
|
+
"typescript": "./src/basic.ts",
|
|
53
|
+
"types": "./src/basic.ts",
|
|
54
|
+
"require": "./dist/node/basic.cjs",
|
|
55
|
+
"browser": "./dist/browser/basic.js",
|
|
56
|
+
"development": "./src/basic.ts",
|
|
57
|
+
"default": "./dist/node/basic.js"
|
|
58
58
|
}
|
|
59
59
|
},
|
|
60
60
|
"typedoc": {
|
package/src/basic.ts
ADDED
package/src/cloud.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { fetchAndRevalidate } from './fetch.js'
|
|
2
2
|
|
|
3
3
|
export interface CloudTemplate {
|
|
4
|
+
cdn?: string
|
|
4
5
|
hostname?: string
|
|
5
6
|
library: string
|
|
6
7
|
component: string
|
|
@@ -8,27 +9,30 @@ export interface CloudTemplate {
|
|
|
8
9
|
revision?: string
|
|
9
10
|
}
|
|
10
11
|
export interface CloudStylesheet {
|
|
12
|
+
cdn?: string
|
|
11
13
|
hostname?: string
|
|
12
14
|
library: string
|
|
13
|
-
revision?:
|
|
15
|
+
revision?: 'staging' | 'published' | 'saved' | number
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export function getComponentURL({
|
|
17
|
-
|
|
19
|
+
cdn = 'https://feaascomponentsapidev.blob.core.windows.net',
|
|
20
|
+
hostname,
|
|
18
21
|
library,
|
|
19
22
|
component,
|
|
20
23
|
version,
|
|
21
24
|
revision = 'published'
|
|
22
25
|
}: CloudTemplate) {
|
|
23
|
-
return `${hostname}/components/${library}/${component}/${version}/${revision}.html`
|
|
26
|
+
return `${cdn || hostname}/components/${library}/${component}/${version}/${revision}.html`
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
export function getStylesheetURL({
|
|
27
|
-
|
|
30
|
+
cdn = `https://feaascomponentsapidev.blob.core.windows.net`,
|
|
31
|
+
hostname,
|
|
28
32
|
library,
|
|
29
33
|
revision = 'published'
|
|
30
34
|
}: CloudStylesheet) {
|
|
31
|
-
return `${hostname}/styles/${library}/${revision}.css`
|
|
35
|
+
return `${cdn || hostname}/styles/${library}/${revision}.css`
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
export async function fetchComponent(props: CloudTemplate, callback: (html: string) => void) {
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { JSXElementConstructor, ReactElement, ReactNode } from 'react'
|
|
2
|
+
import type ReactDOM from 'react-dom'
|
|
3
|
+
import type React from 'react'
|
|
4
|
+
|
|
5
|
+
export type OptionalExcept<T, K extends keyof T> = Pick<T, K> & Partial<Omit<T, K>>
|
|
6
|
+
|
|
7
|
+
// Shim in case it's required in node.js environment
|
|
8
|
+
var WebComponent = typeof HTMLElement == 'undefined' ? (class {} as unknown as typeof HTMLElement) : HTMLElement
|
|
9
|
+
interface ReactPayload {
|
|
10
|
+
React: typeof React
|
|
11
|
+
ReactDOM: typeof ReactDOM
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class FEAASBase<Props = any, Payload = any> extends HTMLElement {
|
|
15
|
+
static observedAttributes: string[]
|
|
16
|
+
|
|
17
|
+
/** Return attributes as parsed values */
|
|
18
|
+
getProps(context?: HTMLElement): any {
|
|
19
|
+
return {}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Properties parsed in current tick */
|
|
23
|
+
props: Props
|
|
24
|
+
overrides: Props
|
|
25
|
+
|
|
26
|
+
/** Timer until next tick */
|
|
27
|
+
nextRender: number
|
|
28
|
+
|
|
29
|
+
/** Is/was component in the dom? */
|
|
30
|
+
connected = null as Event
|
|
31
|
+
|
|
32
|
+
/** Result of lazy loading */
|
|
33
|
+
payload: Payload
|
|
34
|
+
|
|
35
|
+
refs: Record<string, FEAASBase> = {}
|
|
36
|
+
descendants: FEAASBase[] = []
|
|
37
|
+
|
|
38
|
+
/** Attaches shadow root if getShadowRootOptions any settings, or uses element itself */
|
|
39
|
+
getRoot(): FEAASBase | HTMLElement | ShadowRoot {
|
|
40
|
+
return this
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Attribute changes trigger props parsing and schedules re-rendering */
|
|
44
|
+
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
|
|
45
|
+
this.set()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Components parses its attributes when connected, and schedules rendeirng */
|
|
49
|
+
connectedCallback() {
|
|
50
|
+
console.log('FEAAS: Connected', this)
|
|
51
|
+
this.connected = new Event('feaasConnected', { bubbles: true, composed: true })
|
|
52
|
+
this.dispatchEvent(this.connected)
|
|
53
|
+
this.addEventListener('feaasConnected', this)
|
|
54
|
+
this.addEventListener('feaasDisconnected', this)
|
|
55
|
+
this.set()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Nothing special happens on disconnect, except setting a flag */
|
|
59
|
+
disconnectedCallback() {
|
|
60
|
+
console.log('FEAAS: Disconnected', this)
|
|
61
|
+
this.removeEventListener('feaasConnected', this)
|
|
62
|
+
this.removeEventListener('feaasDisconnected', this)
|
|
63
|
+
this.dispatchEvent(new Event('feaasDisconnected', { bubbles: true, composed: true }))
|
|
64
|
+
this.connected = null
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Catch-all event handler that dispatches events to callback */
|
|
68
|
+
handleEvent(event: Event) {
|
|
69
|
+
switch (event.type) {
|
|
70
|
+
case 'feaasConnected':
|
|
71
|
+
this.adoptedCallback(event)
|
|
72
|
+
break
|
|
73
|
+
case 'feaasDisconnected':
|
|
74
|
+
this.orphanCallback(event)
|
|
75
|
+
break
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Act on children element being removed */
|
|
80
|
+
orphanCallback(event: Event) {
|
|
81
|
+
const target = event.composedPath()[0] as FEAASBase
|
|
82
|
+
const index = this.descendants.indexOf(target)
|
|
83
|
+
if (index == -1) return
|
|
84
|
+
console.log('FEAAS: Orphaning', this, target)
|
|
85
|
+
this.descendants.splice(index, -1)
|
|
86
|
+
this.refs[target.getComponentName()] = null
|
|
87
|
+
if (target.refs.parent == this) target.refs.parent = null
|
|
88
|
+
if (target.refs.top == this) {
|
|
89
|
+
target.refs.top = target.refs.parent
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Act on children element being added */
|
|
94
|
+
adoptedCallback(event: Event): boolean | void {
|
|
95
|
+
const target = event.composedPath()[0] as FEAASBase
|
|
96
|
+
this.descendants.push(target)
|
|
97
|
+
console.log('FEAAS: Adopting', this, target)
|
|
98
|
+
this.refs[target.getComponentName()] = target
|
|
99
|
+
target.refs.top = this
|
|
100
|
+
target.refs.parent ||= this
|
|
101
|
+
if (target.renderReact) {
|
|
102
|
+
for (var p: FEAASBase = target.refs.parent; p; p = p.refs.parent) {
|
|
103
|
+
if (p.renderReact) {
|
|
104
|
+
target.refs.parentReact ||= p
|
|
105
|
+
target.refs.topReact = p
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
getComponentName() {
|
|
112
|
+
return this.tagName.replace('FEAAS-', '').toLowerCase()
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Check if lazy component is ready to be loaded. For example a component may stay unloaded unless certain attributes
|
|
117
|
+
* are set
|
|
118
|
+
*/
|
|
119
|
+
isReadyToLoad() {
|
|
120
|
+
return this.payload === undefined && this.getAttribute('hidden') == null
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Load lazy component & render */
|
|
124
|
+
async reify() {
|
|
125
|
+
if (this.payload !== undefined) {
|
|
126
|
+
this.render()
|
|
127
|
+
} else if (this.isReadyToLoad()) {
|
|
128
|
+
this.payload = null
|
|
129
|
+
this.load().then(this.onLoad)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Invoke preloading logic that needs to finish before component can render */
|
|
134
|
+
load() {
|
|
135
|
+
return Promise.resolve(null)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Payload is inside onLoad callback. For web components that use react, it has to provide React & ReactDOM props */
|
|
139
|
+
onLoad = (payload: Payload) => {
|
|
140
|
+
if (payload == null) return
|
|
141
|
+
|
|
142
|
+
this.payload = payload
|
|
143
|
+
this.render()
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Component-specific logic that updates the DOM. Default render implementation assume react rendering, in that case
|
|
148
|
+
* the component needs to provide React & ReactDOM references in its payload. Web component redefine its render to not
|
|
149
|
+
* use react at all
|
|
150
|
+
*/
|
|
151
|
+
render(nested = false): any {
|
|
152
|
+
console.log('FEAAS: React', this.tagName, this.payload)
|
|
153
|
+
if (this.payload == null) return
|
|
154
|
+
const { React, ReactDOM } = (this.payload as ReactPayload) || {}
|
|
155
|
+
if (!React) throw new Error(`${this.tagName}: React is not present in payload`)
|
|
156
|
+
if (!ReactDOM) throw new Error(`${this.tagName}: ReactDOM is not present in payload`)
|
|
157
|
+
|
|
158
|
+
if (!this.refs.topReact && !this.forceUpdateReact) {
|
|
159
|
+
ReactDOM.render(React.createElement(this.getReactWrapper()), this.getReactRoot())
|
|
160
|
+
} else if (this.forceUpdateReact) {
|
|
161
|
+
this.forceUpdateReact?.()
|
|
162
|
+
} else {
|
|
163
|
+
this.refs.topReact.forceUpdateReact?.()
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
invokeReactRendering(): ReactNode {
|
|
168
|
+
const { React, ReactDOM } = (this.payload as ReactPayload) || {}
|
|
169
|
+
const children = this.descendants
|
|
170
|
+
.filter((r) => {
|
|
171
|
+
return r.refs.parentReact == this && r.payload
|
|
172
|
+
})
|
|
173
|
+
.map((child: FEAASBase<any, any>, index) => {
|
|
174
|
+
return React.createElement(this.getReactWrapper(child), {
|
|
175
|
+
key: this.tagName + '-' + index,
|
|
176
|
+
slot: child.getAttribute('slot')
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
return this.renderReact({ ...this.props, children }, this.payload)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
reactWrapper: (props?: any) => ReactNode
|
|
183
|
+
getReactWrapper(target: FEAASBase = this, displayName = target.tagName) {
|
|
184
|
+
const Wrapper = (target.reactWrapper ||= (props?: any) => {
|
|
185
|
+
if (target.renderReact) {
|
|
186
|
+
const { React, ReactDOM } = (target.payload as ReactPayload) || {}
|
|
187
|
+
const [value, forceUpdateReact] = React.useReducer((r) => r + 1, 0)
|
|
188
|
+
target.forceUpdateReact = forceUpdateReact
|
|
189
|
+
return ReactDOM.createPortal(<>{target.invokeReactRendering()}</>, target.getReactRoot())
|
|
190
|
+
} else {
|
|
191
|
+
return target.render()
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
Object.assign(Wrapper, {
|
|
195
|
+
displayName: displayName
|
|
196
|
+
})
|
|
197
|
+
return Wrapper
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
uid: string
|
|
201
|
+
getUID() {
|
|
202
|
+
return (this.uid ||= String(Math.random()))
|
|
203
|
+
}
|
|
204
|
+
forceUpdateReact: (node?: ReactNode) => void
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Render method that is pretty much the same as React Functional component, but with extra second argument for
|
|
208
|
+
* payload (usually result of import, it is expected to have React in scope).
|
|
209
|
+
*/
|
|
210
|
+
renderReact?(
|
|
211
|
+
props: React.PropsWithChildren<typeof this.props>,
|
|
212
|
+
payload: typeof this.payload
|
|
213
|
+
): ReactNode | ReactElement
|
|
214
|
+
|
|
215
|
+
getReactRoot(): HTMLElement | ShadowRoot {
|
|
216
|
+
return this.getRoot()
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Set props and return values on new next tick */
|
|
220
|
+
set(overrides?: Partial<Props>) {
|
|
221
|
+
this.overrides = {
|
|
222
|
+
...this.overrides,
|
|
223
|
+
...overrides
|
|
224
|
+
}
|
|
225
|
+
this.props = {
|
|
226
|
+
...this.getProps(),
|
|
227
|
+
...this.overrides
|
|
228
|
+
}
|
|
229
|
+
for (var property in this.props) {
|
|
230
|
+
const value = this.props[property as keyof typeof this.props]
|
|
231
|
+
if (this.props[property] == null) {
|
|
232
|
+
this.removeAttribute(property)
|
|
233
|
+
} else if (typeof value == 'string' || typeof value == 'number') {
|
|
234
|
+
if (this.getAttribute(property) != String(value)) this.setAttribute(property, String(value))
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
this.scheduleRender()
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** Schedule rendering on next tick, to batch multiple attribute changes */
|
|
241
|
+
scheduleRender() {
|
|
242
|
+
cancelAnimationFrame(this.nextRender)
|
|
243
|
+
this.nextRender = requestAnimationFrame(() => {
|
|
244
|
+
this.reify()
|
|
245
|
+
})
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
formatURL(src: string, hostname: string) {
|
|
249
|
+
if (typeof src == 'string' && src.match(/\/[^\/]/)) {
|
|
250
|
+
return hostname + src
|
|
251
|
+
} else {
|
|
252
|
+
return src
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
getContextAttribute(name: string, element = this) {
|
|
257
|
+
return element.closest(`[${name}]`)?.getAttribute(name) ?? this.getAttribute(name)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
static register(tagName: string) {
|
|
261
|
+
if (!window.customElements.get(tagName)) {
|
|
262
|
+
window.customElements.define(tagName, this)
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { fetchComponent } from '../cloud.js'
|
|
2
|
+
import { renderDOMContent, renderDOMElement } from '../dom.js'
|
|
3
|
+
import { FEAASBase, OptionalExcept } from './FEAASBase.js'
|
|
4
|
+
|
|
5
|
+
export function FEAASComponentsProps(element: FEAASBase = this) {
|
|
6
|
+
let data: any
|
|
7
|
+
const dataValue = element.getAttribute('data')
|
|
8
|
+
if (dataValue != null) {
|
|
9
|
+
try {
|
|
10
|
+
data = typeof dataValue == 'string' ? JSON.parse(dataValue) : {}
|
|
11
|
+
} catch (e) {}
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
data: data ?? {},
|
|
15
|
+
library: element.getAttribute('library'),
|
|
16
|
+
component: element.getAttribute('component'),
|
|
17
|
+
version: element.getAttribute('version'),
|
|
18
|
+
revision: element.getAttribute('revision'),
|
|
19
|
+
hostname: element.getAttribute('hostname'),
|
|
20
|
+
cdn: element.getContextAttribute('cdn'),
|
|
21
|
+
template: element.getAttribute('template')
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export type FEAASComponentsProps = ReturnType<typeof FEAASComponentsProps>
|
|
25
|
+
export class FEAASComponent extends FEAASBase<FEAASComponentsProps, string> {
|
|
26
|
+
static observedAttributes = ['library', 'component', 'version', 'revision', 'hostname', 'cdn', 'template', 'data']
|
|
27
|
+
|
|
28
|
+
needsRefresh: boolean
|
|
29
|
+
setData(data: any) {
|
|
30
|
+
this.set({ data })
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
getProps() {
|
|
34
|
+
return FEAASComponentsProps(this)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
attributeChangedCallback(name: string, oldValue: string, newValue: string): void {
|
|
38
|
+
this.payload = undefined
|
|
39
|
+
this.needsRefresh = true
|
|
40
|
+
super.attributeChangedCallback(name, oldValue, newValue)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* Check if component is a repeated clone, and return the original */
|
|
44
|
+
getOriginal() {
|
|
45
|
+
const scope = this.getAttribute('data-path-scope')
|
|
46
|
+
if (!scope) return
|
|
47
|
+
for (var current = this as Element; (current = current.previousElementSibling); ) {
|
|
48
|
+
if (current.getAttribute('data-path-scope') == scope) {
|
|
49
|
+
var last = current
|
|
50
|
+
} else {
|
|
51
|
+
break
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return last as FEAASComponent
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
isReadyToLoad() {
|
|
58
|
+
return (
|
|
59
|
+
super.isReadyToLoad() && this.props.library != null && this.props.component != null && this.props.version != null
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async load() {
|
|
64
|
+
// defer loading to original element
|
|
65
|
+
if (this.getOriginal()) {
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
fetchComponent(this.props, this.onLoad)
|
|
70
|
+
} catch {
|
|
71
|
+
console.error(
|
|
72
|
+
`FEAAS: Could not fetch component: ${this.props.component}/${this.props.version}/${this.props.revision}`
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
render() {
|
|
78
|
+
if (Array.from(this.children).filter((c) => c.tagName != 'BR').length > 0 && !this.needsRefresh) {
|
|
79
|
+
renderDOMElement(this, this.props.data)
|
|
80
|
+
} else {
|
|
81
|
+
this.needsRefresh = true
|
|
82
|
+
// defer rendering to original element
|
|
83
|
+
if (this.getOriginal()) {
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
renderDOMContent(this, this.payload, this.props.data)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
declare global {
|
|
92
|
+
namespace JSX {
|
|
93
|
+
interface IntrinsicElements {
|
|
94
|
+
'feaas-component': OptionalExcept<FEAASComponentsProps, 'component' | 'version' | 'revision'>
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
FEAASComponent.register('feaas-component')
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type * as FEAASContextImports from '@sitecore-feaas/frontend/src/components/providers/ContextProvider.jsx'
|
|
2
|
+
|
|
3
|
+
import { FEAASBase, OptionalExcept } from './FEAASBase.jsx'
|
|
4
|
+
|
|
5
|
+
export function FEAASContextProps(element: HTMLElement = this) {
|
|
6
|
+
return {
|
|
7
|
+
library: element?.getAttribute('library') as string,
|
|
8
|
+
src: element?.getAttribute('src') ?? '/assets/context.js',
|
|
9
|
+
frontend: element?.getAttribute('frontend') ?? 'https://components.sitecoreclud.io',
|
|
10
|
+
backend: element?.getAttribute('backend') ?? 'https://components-api.sitecoreclud.io',
|
|
11
|
+
cdn: element?.getAttribute('cdn') ?? 'https://feaascompontsapiprod.blob.core.windows.net',
|
|
12
|
+
getToken: null as (options: any) => Promise<string>
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
type FEAASContextProps = ReturnType<typeof FEAASContextProps>
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Context provides shared parameters and react providers for its nested elements. It loads lazily once any of its
|
|
19
|
+
* nested components load. Just placing it on the page without any consumers does not impose any overhead.
|
|
20
|
+
*/
|
|
21
|
+
export class FEAASContext extends FEAASBase<FEAASContextProps, typeof FEAASContextImports> {
|
|
22
|
+
static observedAttributes = ['library', 'component', 'version', 'revision', 'hostname', 'template']
|
|
23
|
+
getProps() {
|
|
24
|
+
return FEAASContextProps(this)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
load() {
|
|
28
|
+
/* @vite-ignore */
|
|
29
|
+
return import(this.formatURL(this.props.src, this.props.frontend))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Remove global styles
|
|
33
|
+
getTheme(theme: FEAASContextImports.Chakra.Theme): FEAASContextImports.Chakra.Theme {
|
|
34
|
+
return theme
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getRoot(): HTMLElement | FEAASBase<any, any> | ShadowRoot {
|
|
38
|
+
return this.shadowRoot || this.attachShadow({ mode: 'open' })
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
reactRoot: HTMLDivElement
|
|
42
|
+
getReactRoot() {
|
|
43
|
+
if (!this.reactRoot) {
|
|
44
|
+
this.reactRoot = document.createElement('div')
|
|
45
|
+
this.getRoot().appendChild(this.reactRoot)
|
|
46
|
+
}
|
|
47
|
+
return this.reactRoot
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Pull in children FEAAS react elements into the shadow DOM so they receieve the styles */
|
|
51
|
+
adoptedCallback(event: Event) {
|
|
52
|
+
super.adoptedCallback(event)
|
|
53
|
+
|
|
54
|
+
const target = event.composedPath()[0] as FEAASBase
|
|
55
|
+
if (target.parentNode != this.getRoot()) {
|
|
56
|
+
this.getRoot().appendChild(target)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
renderReact(
|
|
61
|
+
{ children, library }: React.PropsWithChildren<FEAASContextProps>,
|
|
62
|
+
{ AppContext, Chakra: { Button, Tooltip }, React: { useRef } }: typeof this.payload
|
|
63
|
+
) {
|
|
64
|
+
Tooltip.defaultProps = {
|
|
65
|
+
hasArrow: true,
|
|
66
|
+
placement: 'top',
|
|
67
|
+
portalProps: {
|
|
68
|
+
containerRef: { current: this.getRoot() as HTMLElement }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return (
|
|
72
|
+
<AppContext
|
|
73
|
+
libraryId={this.props.library}
|
|
74
|
+
container={this.getRoot()}
|
|
75
|
+
getTheme={this.getTheme}
|
|
76
|
+
modalProps={{
|
|
77
|
+
portalProps: {
|
|
78
|
+
containerRef: { current: this.getRoot() as HTMLElement }
|
|
79
|
+
}
|
|
80
|
+
}}
|
|
81
|
+
chakraProps={{
|
|
82
|
+
cssVarsRoot: ':host',
|
|
83
|
+
resetCSS: true,
|
|
84
|
+
toastOptions: {
|
|
85
|
+
portalProps: {
|
|
86
|
+
containerRef: { current: this.getRoot() as HTMLElement }
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}}
|
|
90
|
+
>
|
|
91
|
+
{children}
|
|
92
|
+
</AppContext>
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
declare global {
|
|
98
|
+
namespace JSX {
|
|
99
|
+
interface IntrinsicElements {
|
|
100
|
+
'feaas-context': OptionalExcept<FEAASContextProps, 'library'>
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
FEAASContext.register('feaas-context')
|