@sitecore-feaas/clientside 0.1.54 → 0.1.56

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.
Files changed (91) hide show
  1. package/README.md +20 -20
  2. package/build/components/FEAASComponent.js +1 -1
  3. package/build/components/FEAASComponent.js.map +1 -1
  4. package/build/components/FEAASLoader.js +1 -1
  5. package/build/components/FEAASLoader.js.map +1 -1
  6. package/build/components/FEAASStylesheet.js +1 -1
  7. package/build/components/FEAASStylesheet.js.map +1 -1
  8. package/build/dom/thumbnail.js +247 -0
  9. package/build/dom/thumbnail.js.map +1 -0
  10. package/build/fetch.js +4 -2
  11. package/build/fetch.js.map +1 -1
  12. package/build/headless.js +1 -0
  13. package/build/headless.js.map +1 -1
  14. package/build/ui/FEAASContext.js +8 -3
  15. package/build/ui/FEAASContext.js.map +1 -1
  16. package/build/ui/FEAASEditor.js +5 -5
  17. package/build/ui/FEAASEditor.js.map +1 -1
  18. package/build/ui/FEAASExternal.js +8 -9
  19. package/build/ui/FEAASExternal.js.map +1 -1
  20. package/build/ui/FEAASPicker.js +5 -3
  21. package/build/ui/FEAASPicker.js.map +1 -1
  22. package/build/utils/promise.js.map +1 -1
  23. package/dist/browser/headless.cjs +8 -5
  24. package/dist/browser/headless.cjs.map +4 -4
  25. package/dist/browser/headless.esm.js +8 -5
  26. package/dist/browser/headless.esm.js.map +1 -1
  27. package/dist/browser/index.cjs +8 -5
  28. package/dist/browser/index.cjs.map +4 -4
  29. package/dist/browser/index.esm.js +8 -5
  30. package/dist/browser/index.esm.js.map +1 -1
  31. package/dist/browser/react.cjs +8 -5
  32. package/dist/browser/react.cjs.map +4 -4
  33. package/dist/browser/react.esm.js +8 -5
  34. package/dist/browser/react.esm.js.map +1 -1
  35. package/dist/node/headless.cjs +6 -3
  36. package/dist/node/headless.cjs.map +4 -4
  37. package/dist/node/headless.esm.js +6 -3
  38. package/dist/node/headless.esm.js.map +4 -4
  39. package/dist/node/index.cjs +6 -3
  40. package/dist/node/index.cjs.map +4 -4
  41. package/dist/node/index.esm.js +6 -3
  42. package/dist/node/index.esm.js.map +4 -4
  43. package/dist/node/react.cjs +6 -3
  44. package/dist/node/react.cjs.map +4 -4
  45. package/dist/node/react.esm.js +6 -3
  46. package/dist/node/react.esm.js.map +4 -4
  47. package/headless.cjs +6 -3
  48. package/headless.cjs.map +4 -4
  49. package/headless.js +6 -3
  50. package/headless.js.map +4 -4
  51. package/package.json +2 -1
  52. package/react.cjs +8 -5
  53. package/react.cjs.map +4 -4
  54. package/react.js +8 -5
  55. package/react.js.map +4 -4
  56. package/src/components/FEAASComponent.tsx +1 -1
  57. package/src/components/FEAASLoader.tsx +1 -1
  58. package/src/components/FEAASStylesheet.tsx +1 -1
  59. package/src/dom/thumbnail.ts +293 -0
  60. package/src/fetch.ts +5 -3
  61. package/src/headless.ts +1 -0
  62. package/src/ui/FEAASContext.tsx +9 -3
  63. package/src/ui/FEAASEditor.tsx +5 -5
  64. package/src/ui/FEAASExternal.tsx +7 -8
  65. package/src/ui/FEAASPicker.tsx +7 -2
  66. package/src/utils/promise.ts +2 -2
  67. package/test/FEAASComponent.react.test.tsx +3 -6
  68. package/types/components/FEAASComponent.d.ts +1 -1
  69. package/types/components/FEAASComponent.d.ts.map +1 -1
  70. package/types/components/FEAASLoader.d.ts +1 -1
  71. package/types/components/FEAASLoader.d.ts.map +1 -1
  72. package/types/components/FEAASStylesheet.d.ts +1 -1
  73. package/types/components/FEAASStylesheet.d.ts.map +1 -1
  74. package/types/dom/thumbnail.d.ts +82 -0
  75. package/types/dom/thumbnail.d.ts.map +1 -0
  76. package/types/fetch.d.ts +6 -4
  77. package/types/fetch.d.ts.map +1 -1
  78. package/types/headless.d.ts +1 -0
  79. package/types/headless.d.ts.map +1 -1
  80. package/types/tsbuildinfo +1 -1
  81. package/types/tsbuildinfo_ui +1 -1
  82. package/types/ui/FEAASContext.d.ts +9 -5
  83. package/types/ui/FEAASContext.d.ts.map +1 -1
  84. package/types/ui/FEAASEditor.d.ts +2 -2
  85. package/types/ui/FEAASEditor.d.ts.map +1 -1
  86. package/types/ui/FEAASExternal.d.ts +1 -1
  87. package/types/ui/FEAASExternal.d.ts.map +1 -1
  88. package/types/ui/FEAASPicker.d.ts +2 -2
  89. package/types/ui/FEAASPicker.d.ts.map +1 -1
  90. package/types/utils/promise.d.ts +1 -1
  91. package/types/utils/promise.d.ts.map +1 -1
@@ -3,7 +3,7 @@ import { renderDOMContent, renderDOMElement } from '../dom/rendering.js'
3
3
  import { loadStylesheetAllowStale } from '../headless.js'
4
4
  import { FEAASBase } from './FEAASBase.js'
5
5
 
6
- export function FEAASComponentsProps(element: FEAASBase = this) {
6
+ export function FEAASComponentsProps(element: FEAASBase) {
7
7
  let data: any
8
8
  const src = element.getAttribute('src')
9
9
  if (src) {
@@ -1,6 +1,6 @@
1
1
  import { FEAASBase } from './FEAASBase.js'
2
2
 
3
- export function FEAASLoaderProps(element: HTMLElement = this) {
3
+ export function FEAASLoaderProps(element: HTMLElement) {
4
4
  return {
5
5
  opacity: element?.getAttribute('opacity') ? parseFloat(element?.getAttribute('opacity')) : 1,
6
6
  hidden: element?.getAttribute('hidden') != null
@@ -2,7 +2,7 @@ import { DEFAULT_CDN_URL, FEAASCDNStylesheetParams, parseStylesheetSource } from
2
2
  import { loadStylesheet } from '../dom/stylesheet.js'
3
3
  import { FEAASBase } from './FEAASBase.js'
4
4
 
5
- export function FEAASStylesheetProps(element: FEAASBase = this) {
5
+ export function FEAASStylesheetProps(element: FEAASBase) {
6
6
  const src = element.getAttribute('src')
7
7
  if (src) {
8
8
  var { cdn, library, revision } = parseStylesheetSource(src)
@@ -0,0 +1,293 @@
1
+ import type { SDK, Style, VersionModel, ComponentModel } from '@sitecore-feaas/sdk'
2
+ import { fetchAndRevalidate } from '../fetch.js'
3
+ import { renderComponent } from './component.js'
4
+
5
+ export type Callback = (image: HTMLImageElement, isCached: boolean) => void
6
+
7
+ export const convertBase64toBlob = (base64: string) => fetch(base64).then((res) => res.blob())
8
+
9
+ /** Parse fetch response as blob and turn it into HTMLImageElement off thread */
10
+ export const responseToImage = (response: Response): Promise<HTMLImageElement> =>
11
+ response.blob().then((blob) => {
12
+ return new Promise((resolve, reject) => {
13
+ const img = new Image()
14
+ img.onerror = reject
15
+ img.onload = () => {
16
+ resolve(img)
17
+ }
18
+ img.src = URL.createObjectURL(blob)
19
+ })
20
+ })
21
+
22
+ /**
23
+ * Retrieves the version of a given component that best fits a specific display width.
24
+ *
25
+ * The function ensures that the version data for the component is available and selects the one that is most suitable
26
+ * for a display width of 800 pixels.
27
+ *
28
+ * @param {ComponentModel} component - The component for which the version needs to be obtained.
29
+ * @returns {Promise<VersionModel>} - A promise that resolves to the most suitable version for a display width of 800
30
+ * pixels.
31
+ */
32
+ export async function getVersionForThumbnail(component: ComponentModel): Promise<VersionModel> {
33
+ if (!component.versions.length) await component.versions.fetch()
34
+ return component.getVersionForWidth(800)
35
+ }
36
+
37
+ /**
38
+ * Ensures an up-to-date thumbnail for a specific component and version.
39
+ *
40
+ * This operation initiates thumbnail load and, if that fails or if file is out of date, a new thumbnail is generated.
41
+ * It can work with a callback, which is designed to potentially fire twice for enhancing user experience. This dual
42
+ * firing allows for an immediate display of a possibly stale cached image while downloading or generating new version
43
+ * in the background.
44
+ *
45
+ * 1. The first call can happen with the stale cached or outdated image.
46
+ * 2. The second call occurs with the up-to-date loaded or freshly generated generated image.
47
+ *
48
+ * @param {ComponentModel} component - Relevant component.
49
+ * @param {VersionModel} [version] - Relevant version (optional, falls back to responsive).
50
+ * @param {Callback} [callback] - Callback to handle the loaded or generated image (optional).
51
+ * @returns {Promise<HTMLImageElement | null>} - The final thumbnail image (either loaded or freshly generated).
52
+ */
53
+ export async function get(component: ComponentModel, version?: VersionModel, callback?: Callback) {
54
+ try {
55
+ if (!component.isNew) {
56
+ return await load(component, version, callback)
57
+ }
58
+ } catch (e) {
59
+ component.sdk.log(
60
+ `FEAAS: Thumbnail ${component.id}, version: ${
61
+ version?.id || 'responsive'
62
+ }, status: generating ${status}, reason: ${e}`
63
+ )
64
+ }
65
+ // generate the image
66
+ const img = await generate(component, version)
67
+ callback?.(img, false)
68
+
69
+ // upload base64 image as blob off-thread
70
+ if (img && !component.isNew) {
71
+ convertBase64toBlob(img.src).then((blob) => upload(component, version, blob))
72
+ }
73
+ return img
74
+ }
75
+
76
+ /**
77
+ * Retrieves the thumbnail image for a given component/version combination, with a responsive default if version is not
78
+ * provided.
79
+ *
80
+ * The resulting HTML <img> element will have `width`, `height`, and `src` properties ready for use. Although it can
81
+ * operate as a regular promise that resolves when the thumbnail is loaded, it also supports a callback for an enhanced
82
+ * user experience. This callback may be invoked twice, depending on the state of the cache:
83
+ *
84
+ * 1. The first call occurs if the image was previously cached by the browser, but the cache is stale.
85
+ * 2. The second call happens when the image is updated from a fresh download. By this design, the user gets to see a
86
+ * cached image immediately while the updated image is being downloaded in the background.
87
+ *
88
+ * If the version of a component is known to be newer than remote file, the function will throw `InvalidResponse` error.
89
+ * As a way to handle that error, the `generate` function may be used to produce new thumbnail (@see render).
90
+ *
91
+ * @param {ComponentModel} component - The component to load a thumbnail for.
92
+ * @param {VersionModel} [version] - The version of the component to load a thumbnail for. If not provided, a version
93
+ * suitable for a responsive component is selected.
94
+ * @param {Callback} [callback] - A callback to handle the loaded image. This callback may be invoked twice for the
95
+ * reasons described above.
96
+ * @returns {Promise<HTMLImageElement>} - The thumbnail image for the component/version combination.
97
+ */
98
+ export async function load(
99
+ component: ComponentModel,
100
+ version?: VersionModel,
101
+ callback?: Callback
102
+ ): Promise<HTMLImageElement> {
103
+ const url = version ? version.getThumbnailURL() : component.getThumbnailURL()
104
+ const stylesheet = component.library.stylesheet
105
+ const lastModifiedDate = new Date(Math.max(Number(component.modifiedAt), Number(stylesheet.modifiedAt)))
106
+ component.sdk.log(`FEAAS: Thumbnail ${component.id}/${version?.id || 'responsive'}, loading`)
107
+ // Otherwise use a logic that will invoke callback for cached request and then try to revalidate it
108
+ var img: HTMLImageElement
109
+ await fetchAndRevalidate(
110
+ url,
111
+ {},
112
+ (response, isCached) => {
113
+ // <2 second difference is not considered outdated
114
+ // If thumbnail is detected as outdated it will throw error and promise will be rejected
115
+ const isUpToDate =
116
+ Math.abs(Number(new Date(response.headers.get('x-ms-meta-componentRenderedAt'))) - Number(lastModifiedDate)) <
117
+ 2000
118
+
119
+ if (isCached && !isUpToDate) {
120
+ responseToImage(response).then((image) => {
121
+ img = image
122
+ callback(image, isCached)
123
+ })
124
+ }
125
+ return isUpToDate
126
+ },
127
+ (response, state, isCached) => {
128
+ component.sdk.log(`FEAAS: Thumbnail ${component.id}/${version?.id || 'responsive'}, status: ${state}`)
129
+ responseToImage(response).then((image) => {
130
+ img = image
131
+ callback(image, isCached)
132
+ })
133
+ },
134
+ null
135
+ )
136
+ return img
137
+ }
138
+
139
+ /**
140
+ * Creates a thumbnail image for a given component and version.
141
+ *
142
+ * First, it determines the appropriate version to use. If not provided, it attempts to fetch one suitable for
143
+ * thumbnail. It then leverages `render` to generate an image with provided parameters and finally, if the component
144
+ * isn't new, uploads the generated as a Blob onto cdn for future retrieval.
145
+ *
146
+ * @param {ComponentModel} component - Component instance.
147
+ * @param {VersionModel} [version] - Version instance.
148
+ * @returns {Promise<HTMLImageElement | null>} Thumbnail image or null if generation isn't possible.
149
+ */
150
+ export async function generate(component: ComponentModel, version?: VersionModel) {
151
+ const sourceVersion = version || (component.isNew ? null : await getVersionForThumbnail(component))
152
+ if (!sourceVersion || sourceVersion.isViewEmpty()) {
153
+ return null
154
+ }
155
+ const { stylesheet, datasources } = component.library
156
+ const img = await render(
157
+ sourceVersion.classList,
158
+ stylesheet.getBreakpointForWidth(800, sourceVersion.getBreakpoints()),
159
+ sourceVersion.view,
160
+ datasources.reduce((object, datasource) => {
161
+ return Object.assign(object, { [datasource.id]: datasource.sample })
162
+ }, {}),
163
+ stylesheet.css,
164
+ component.sdk
165
+ )
166
+
167
+ return img
168
+ }
169
+
170
+ /**
171
+ * Transforms a specific component version into an image.
172
+ *
173
+ * Generates an isolated environment (iframe) to render the component with supplied parameters. 'html2canvas.js' is then
174
+ * injected into the iframe to capture the rendered component as an image. The image is created respecting the provided
175
+ * design breakpoint, ensuring an accurate visual representation.
176
+ *
177
+ * @param {string[]} classList - Array of class names applied to the rendered component.
178
+ * @param {Style.Rule<'breakpoint'>} breakpoint - Breakpoint style rule that dictates the viewport dimensions for
179
+ * rendering.
180
+ * @param {string} template - String template of the component to render.
181
+ * @param {any} data - Data object to be used when rendering the component.
182
+ * @param {string} cssText - CSS rules to be applied during component rendering.
183
+ * @param {EnvironmentVariables} env - Environment variables used to set frontend and backend endpoint URLs.
184
+ * @returns {Promise<BasicImage>} - Promise that resolves to an object containing the src, width, and height of the
185
+ * created image.
186
+ */
187
+ function render(
188
+ classList: string[],
189
+ breakpoint: Style.Rule<'breakpoint'>,
190
+ template: string,
191
+ data: any,
192
+ cssText: string,
193
+ sdk: SDK
194
+ ) {
195
+ const iframe = document.createElement('iframe')
196
+ iframe.style.position = 'absolute'
197
+ iframe.style.top = '-2000px'
198
+ iframe.style.left = '-2000px'
199
+ iframe.style.width = breakpoint.props.minWidth + 'px'
200
+ iframe.style.height = '720px'
201
+ iframe.style.zIndex = '100011'
202
+ document.body.appendChild(iframe)
203
+
204
+ return new Promise<HTMLImageElement>((resolve, reject) => {
205
+ const script = document.createElement('script')
206
+ script.onerror = reject
207
+ script.src = sdk.frontend + '/assets/html2canvas.js'
208
+ script.onload = () => {
209
+ ;(iframe.contentWindow.document.fonts?.ready || Promise.resolve()).then(function () {
210
+ return (
211
+ iframe.contentWindow
212
+ //@ts-ignore
213
+ .html2canvas(root, {
214
+ // 1.1 scale is to remove shadows that are drawn incorrectly
215
+ scale: Math.max(1.1, 1000 / Math.min(1401, Math.max(320, breakpoint.props.minWidth))),
216
+ width: Math.min(1401, Math.max(320, breakpoint.props.minWidth)),
217
+ height: Math.max(1, Math.min(2000, root.offsetHeight)),
218
+ windowWidth: breakpoint.props.minWidth + 'px',
219
+ windowHeight: 1000,
220
+ proxy: sdk.backend + '/proxy/media',
221
+ backgroundColor: '#ffffff'
222
+ })
223
+ .then((canvas: HTMLCanvasElement) => {
224
+ const src = canvas.toDataURL('image/jpeg', 0.6)
225
+ iframe.parentElement.removeChild(iframe)
226
+ const img = new Image()
227
+ img.onload = () => {
228
+ resolve(img)
229
+ }
230
+ img.onerror = reject
231
+ img.src = src
232
+ })
233
+ )
234
+ })
235
+ }
236
+ iframe.contentWindow.document.head.appendChild(script)
237
+ const stylesheet = iframe.contentWindow.document.createElement('style')
238
+ stylesheet.textContent = `
239
+ ${cssText}
240
+ body, html { padding: 0; margin: 0; overflow: hidden }
241
+ `
242
+
243
+ iframe.contentWindow.document.head.appendChild(stylesheet)
244
+ const root = renderComponent({ template, data })
245
+ root.classList.add(...classList, '-feaas--preview', '-emulate--' + breakpoint.details.slug)
246
+ iframe.contentWindow.document.body.appendChild(root)
247
+ })
248
+ }
249
+
250
+ /**
251
+ * Uploads a thumbnail Blob to a CDN, enabling it for future retrieval.
252
+ *
253
+ * It constructs a URL based on component and version information, if available. The thumbnail's blob is uploaded with
254
+ * several metadata points, including identifiers and timestamps. Upon successful upload, it sends a no-cache fetch
255
+ * request to the constructed URL to bust the cache.
256
+ *
257
+ * @param {ComponentModel} component - Component instance.
258
+ * @param {VersionModel} version - Version instance.
259
+ * @param {Blob} blob - Blob instance to upload.
260
+ * @returns {Promise<string>} URL of the uploaded Blob.
261
+ */
262
+ export async function upload(component: ComponentModel, version: VersionModel, blob: Blob): Promise<string> {
263
+ const url = version ? version.getThumbnailURL() : component.getThumbnailURL()
264
+ const lastModifiedDate = new Date(
265
+ Math.max(Number(component.modifiedAt), Number(component.library.stylesheet.modifiedAt))
266
+ )
267
+
268
+ try {
269
+ await component.sdk.uploadBlob('thumbnails', url.split(/\/thumbnails\//)[1], blob, {
270
+ headers: {
271
+ 'x-ms-blob-content-type': 'image/jpg',
272
+ 'x-ms-blob-cache-control': 'public,max-age=31536000,immutable',
273
+ 'x-ms-meta-versionId': version ? version.id : null,
274
+ 'x-ms-meta-versionRevision': version ? version.revision.toString() : null,
275
+ 'x-ms-meta-componentId': component.id,
276
+ 'x-ms-meta-componentRenderedAt': lastModifiedDate.toUTCString(),
277
+ 'x-ms-meta-componentModifiedAt': component.modifiedAt.toUTCString(),
278
+ 'x-ms-meta-componentCreatedAt': component.createdAt.toUTCString(),
279
+ 'x-ms-meta-modifiedAt': component.modifiedAt.toUTCString()
280
+ }
281
+ })
282
+ // bust thumbnail cache for the future, off-thread
283
+ fetch(url, {
284
+ cache: 'no-cache'
285
+ }).then(() => {
286
+ component.sdk.log('FEAAS: Thumbnail uploaded', url)
287
+ })
288
+ } catch (e) {
289
+ component.sdk.log('FEAAS: Thumbnail failed to upload', url, e)
290
+ }
291
+
292
+ return url
293
+ }
package/src/fetch.ts CHANGED
@@ -13,12 +13,12 @@ import { ControlledPromise } from './utils/promise.js'
13
13
  * errors will not be caught. Function throws when unable to fetch valid response.
14
14
  */
15
15
  const phases = ['initial', 'check', 'updated'] as const
16
- export type ValidatableRequestPhase = typeof phases[number]
16
+ export type ValidatableRequestPhase = (typeof phases)[number]
17
17
  export type FetchCache = Record<string, FetchCacheRequest>
18
18
  export interface ValidatableRequest {
19
19
  url: Parameters<typeof fetch>[0]
20
20
  options?: Parameters<typeof fetch>[1]
21
- validator?: (resposne: Response) => boolean
21
+ validator?: (resposne: Response, isCached: boolean) => boolean
22
22
  callback?: (response: Response, status: ValidatableRequestPhase, isCached: boolean) => any
23
23
  isCached?: boolean
24
24
  response?: Response
@@ -66,6 +66,8 @@ export function getFetchCache(url: Parameters<typeof fetch>[0], dontCreate = fal
66
66
  return cache
67
67
  }
68
68
 
69
+ export class InvalidResponse extends Error {}
70
+
69
71
  export async function fetchWithCacheAwareness(
70
72
  { url, options, validator, callback, cache }: ValidatableRequest,
71
73
  phase: ValidatableRequestPhase = 'initial'
@@ -88,11 +90,11 @@ export async function fetchWithCacheAwareness(
88
90
  }
89
91
  }
90
92
  if (!response.ok) throw new Error('Fetch failed')
91
- if (validator?.(response) === false) throw new Error('Response is not valid')
92
93
  // see if response was pulled from cache
93
94
  const isCached =
94
95
  new Date(response.headers.get('date')) < date ||
95
96
  (promise && phase == 'initial' && (cachedRequest.phase == 'updated' || cachedRequest.phase == 'check'))
97
+ if (validator?.(response, isCached) === false) throw new InvalidResponse('Response is not valid')
96
98
  await callback?.(response, phase, isCached)
97
99
  return {
98
100
  url,
package/src/headless.ts CHANGED
@@ -18,6 +18,7 @@ export * from './components/FEAASComponent.js'
18
18
  export * from './components/FEAASLoader.js'
19
19
  export * from './components/FEAASStylesheet.js'
20
20
  export * from './utils/datapath.js'
21
+ export * as Thumbnail from './dom/thumbnail.js'
21
22
 
22
23
  declare global {
23
24
  interface Window {
@@ -1,8 +1,9 @@
1
1
  import type * as FEAASContextImports from '@sitecore-feaas/frontend/context'
2
2
  import type { AuthModel } from '@sitecore-feaas/sdk'
3
3
  import { FEAASBase, OptionalExcept } from '../components/FEAASBase.js'
4
+ import { ControlledPromise } from '../utils/promise.js'
4
5
 
5
- export function FEAASContextProps(element: HTMLElement = this) {
6
+ export function FEAASContextProps(element: HTMLElement) {
6
7
  return {
7
8
  library: element?.getAttribute('library') as string,
8
9
  src: element?.getAttribute('src') ?? '/context.js',
@@ -66,11 +67,16 @@ export class FEAASContext extends FEAASBase<FEAASContextProps, typeof FEAASConte
66
67
  super.connectedCallback()
67
68
  }
68
69
 
69
- onStatusChange = (status: string) => {
70
+ sdk: FEAASContextImports.SDK
71
+ onStatusChange: FEAASContextImports.SDKStatusCallback = (status, sdk) => {
70
72
  if (status == 'ready') {
71
- this.dispatchEvent(new Event('feaasReady', { bubbles: true, composed: true }))
73
+ this.whenSDKReady.resolve(sdk)
74
+ this.dispatchEvent(new CustomEvent('feaasReady', { bubbles: true, composed: true, detail: sdk }))
72
75
  }
73
76
  }
77
+ whenSDKReady = ControlledPromise<FEAASContextImports.SDK>((sdk) => {
78
+ this.sdk = sdk
79
+ })
74
80
 
75
81
  render(
76
82
  { children, library }: React.PropsWithChildren<FEAASContextProps>,
@@ -38,7 +38,7 @@ export function getFramePosition(element: HTMLElement) {
38
38
  { left: 0, top: 0 }
39
39
  )
40
40
  }
41
- export function FEAASEditorProps(element: FEAASBase = this) {
41
+ export function FEAASEditorProps(element: FEAASBase) {
42
42
  return {
43
43
  context: HTMLElement,
44
44
  frontend: element?.getContextAttribute('frontend'),
@@ -447,6 +447,7 @@ export class FEAASEditor extends FEAASBase<FEAASEditorProps, typeof FEAASEditorI
447
447
  Chrome,
448
448
  ReactDOM,
449
449
  useSDK,
450
+ useLibrary,
450
451
  useSlots,
451
452
  useModelObserver,
452
453
  ConfirmationContext,
@@ -455,12 +456,11 @@ export class FEAASEditor extends FEAASBase<FEAASEditorProps, typeof FEAASEditorI
455
456
  ) {
456
457
  if (!this.payload) return
457
458
  const { React } = this.payload
458
- const { sdk } = useSDK()
459
+ const sdk = useSDK()
460
+ const library = useLibrary()
459
461
  const [component, setComponent] = useState<FEAASEditorImports.SDK.ComponentModel>()
460
462
  const { setConfirm } = useContext(ConfirmationContext)
461
-
462
463
  const usedId = instanceId || versionId
463
- const library = sdk.library
464
464
 
465
465
  if (component) this.component = component
466
466
 
@@ -600,7 +600,7 @@ export class FEAASEditor extends FEAASBase<FEAASEditorProps, typeof FEAASEditorI
600
600
  <Editor
601
601
  datasources={sdk.datasources}
602
602
  sdk={sdk}
603
- library={sdk.library}
603
+ library={library}
604
604
  component={component}
605
605
  versions={versions}
606
606
  context={{
@@ -15,13 +15,12 @@ const convertSchemaPropertiesToKebabCase = (schema: any) => {
15
15
  return acc
16
16
  }, {})
17
17
 
18
- kebabRequiredProperties = schema.required.map((item: string) => kebabCase(item))
18
+ kebabRequiredProperties = (schema.required || []).map((item: string) => kebabCase(item))
19
19
  }
20
20
  return { ...schema, properties: kebabSchemaProperties, required: kebabRequiredProperties }
21
21
  }
22
22
 
23
23
  export const parseProps = (propName: string, propValue: string, properties: any) => {
24
- debugger
25
24
  switch (properties?.[propName]?.type) {
26
25
  case 'string':
27
26
  return propValue
@@ -40,7 +39,7 @@ export const parseProps = (propName: string, propValue: string, properties: any)
40
39
  }
41
40
  }
42
41
 
43
- export function FEAASExternalProps(element: FEAASBase = this) {
42
+ export function FEAASExternalProps(element: FEAASBase) {
44
43
  const attributes = element.getAttributeNames()
45
44
  const id = element.getAttribute('data-external-id')
46
45
  const schema: any = FEAASExternal.registered[id]?.schema
@@ -83,9 +82,9 @@ export default class FEAASExternal extends FEAASBase<FEAASExternalProps, ReactMo
83
82
  }
84
83
 
85
84
  static setRegistrationCallback() {
86
- // if registerCallback has not already been triggered by another component, set it
87
- if (!FEAASExternal.registrationCallback && typeof window !== 'undefined') {
88
- FEAASExternal.registrationCallback = setTimeout(() => {
85
+ clearTimeout(FEAASExternal.registrationCallback)
86
+ FEAASExternal.registrationCallback = setTimeout(() => {
87
+ if (typeof window !== 'undefined') {
89
88
  // create components array from object
90
89
  const components = Object.keys(FEAASExternal.registered).map((id) => ({
91
90
  ...FEAASExternal.registered[id],
@@ -102,8 +101,8 @@ export default class FEAASExternal extends FEAASBase<FEAASExternalProps, ReactMo
102
101
  }),
103
102
  '*'
104
103
  )
105
- }, 500)
106
- }
104
+ }
105
+ }, 30)
107
106
  }
108
107
 
109
108
  /*static loadComponents() {
@@ -1,7 +1,7 @@
1
1
  import type * as FEAASPickerImports from '@sitecore-feaas/frontend/picker'
2
2
  import { FEAASBase } from '../components/FEAASBase.js'
3
3
 
4
- export function FEAASPickerProps(element: FEAASBase<any, any> = this) {
4
+ export function FEAASPickerProps(element: FEAASBase<any, any>) {
5
5
  return {
6
6
  part: element?.getAttribute('part') || 'picker',
7
7
  slot: element?.getAttribute('part') || 'picker',
@@ -35,10 +35,15 @@ export class FEAASPicker extends FEAASBase<FEAASPickerProps, typeof FEAASPickerI
35
35
 
36
36
  render(
37
37
  { placeholder, context, mode, children, variant }: React.PropsWithChildren<FEAASPickerProps>,
38
- { Picker, React, ReactDOM }: typeof FEAASPickerImports
38
+ { Picker, React, useLibrary }: typeof FEAASPickerImports
39
39
  ) {
40
+ const collections = useLibrary('collections')
41
+ const rules = useLibrary('stylesheet.styles')
40
42
  return (
41
43
  <Picker
44
+ rules={rules}
45
+ collections={collections}
46
+ themeContext={context}
42
47
  placeholder={placeholder}
43
48
  mode={mode || undefined}
44
49
  onModeChange={(mode) => {
@@ -1,5 +1,5 @@
1
1
  export type ControlledPromise<T> = Promise<T> & {
2
- resolve: (...args: any) => void
2
+ resolve: (result?: T) => void
3
3
  reject: (e: Error) => void
4
4
  restart: () => ControlledPromise<T>
5
5
  first: ControlledPromise<T>
@@ -35,7 +35,7 @@ export type ControlledPromise<T> = Promise<T> & {
35
35
  export function ControlledPromise<T>(onSuccess?: (result: T) => void, onFailure = (e: Error) => {}) {
36
36
  var first: ControlledPromise<T>
37
37
  const restart = () => {
38
- var resolve: (...args: any) => void
38
+ var resolve: (result?: T) => void
39
39
  var reject: (e: Error) => void
40
40
  const promise = new Promise<T>((success, failure) => {
41
41
  resolve = (value: T) => {
@@ -1,10 +1,7 @@
1
- import { beforeEach, afterAll, beforeAll, describe, expect, it, vi } from 'vitest'
2
- import { FEAASComponent } from '../src/components/FEAASComponent.js'
3
- import { renderDOMContent } from '../src/dom/rendering.js'
4
- import { clearFetchCache } from '../src/fetch.js'
5
- import { Component } from '../src/ui/react.js'
6
- import ReactDOM from 'react-dom'
7
1
  import React from 'react'
2
+ import ReactDOM from 'react-dom'
3
+ import { describe, expect, it } from 'vitest'
4
+ import { Component } from '../src/ui/react.js'
8
5
 
9
6
  async function test(react: any, html: string) {
10
7
  const div = document.createElement('div')
@@ -1,5 +1,5 @@
1
1
  import { FEAASBase } from './FEAASBase.js';
2
- export declare function FEAASComponentsProps(element?: FEAASBase): {
2
+ export declare function FEAASComponentsProps(element: FEAASBase): {
3
3
  data: any;
4
4
  cdn: string;
5
5
  library: string;
@@ -1 +1 @@
1
- {"version":3,"file":"FEAASComponent.d.ts","sourceRoot":"","sources":["../../src/components/FEAASComponent.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE1C,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,SAAgB;;;;;;;;;;;;;;EA6B7D;AACD,MAAM,MAAM,oBAAoB,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;AAC1E,qBAAa,cAAe,SAAQ,SAAS,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACzE,MAAM,CAAC,kBAAkB,WAYxB;IAED,YAAY;;;;;;;;;;;;;;OAOsB;IAElC,YAAY,EAAE,OAAO,CAAA;IACrB,OAAO,CAAC,IAAI,EAAE,GAAG;IAIjB,QAAQ;;;;;;;;;;;;;;;IAIR,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAUhF,SAAS,IAAI,GAAG;;IAahB,IAAI;IASJ,WAAW;IAaX,aAAa;IAMb,MAAM,SAAU,MAAM,UAGrB;IAEK,IAAI;IAsBV,MAAM;CAYP;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,GAAG,CAAC;QACZ,UAAU,iBAAiB;YACzB,iBAAiB,EAAE;gBACjB,IAAI,CAAC,EAAE,GAAG,CAAA;gBACV,QAAQ,CAAC,EAAE,MAAM,CAAA;gBACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;gBACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;gBAClB,QAAQ,CAAC,EAAE,GAAG,CAAA;gBACd,uBAAuB,CAAC,EAAE;oBAAE,MAAM,EAAE,MAAM,CAAA;iBAAE,CAAA;gBAC5C,eAAe,CAAC,EAAE,MAAM,CAAA;gBACxB,iBAAiB,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,EAAE,CAAA;aAC1C,GAAG,CACA;gBACE,kCAAkC;gBAClC,QAAQ,CAAC,EAAE,MAAM,CAAA;gBACjB,GAAG,CAAC,EAAE,MAAM,CAAA;gBACZ,OAAO,EAAE,MAAM,CAAA;gBACf,SAAS,EAAE,MAAM,CAAA;gBACjB,OAAO,CAAC,EAAE,MAAM,CAAA;gBAChB,QAAQ,CAAC,EAAE,oBAAoB,CAAC,UAAU,CAAC,CAAA;aAC5C,GACD;gBAAE,GAAG,EAAE,MAAM,CAAA;aAAE,GACf;gBAAE,QAAQ,EAAE,MAAM,CAAA;aAAE,CACvB,CAAA;SACF;KACF;CACF;AAGD,OAAO,EAAE,cAAc,IAAI,SAAS,EAAE,oBAAoB,IAAI,cAAc,EAAE,CAAA"}
1
+ {"version":3,"file":"FEAASComponent.d.ts","sourceRoot":"","sources":["../../src/components/FEAASComponent.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE1C,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS;;;;;;;;;;;;;;EA6BtD;AACD,MAAM,MAAM,oBAAoB,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;AAC1E,qBAAa,cAAe,SAAQ,SAAS,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACzE,MAAM,CAAC,kBAAkB,WAYxB;IAED,YAAY;;;;;;;;;;;;;;OAOsB;IAElC,YAAY,EAAE,OAAO,CAAA;IACrB,OAAO,CAAC,IAAI,EAAE,GAAG;IAIjB,QAAQ;;;;;;;;;;;;;;;IAIR,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAUhF,SAAS,IAAI,GAAG;;IAahB,IAAI;IASJ,WAAW;IAaX,aAAa;IAMb,MAAM,SAAU,MAAM,UAGrB;IAEK,IAAI;IAsBV,MAAM;CAYP;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,GAAG,CAAC;QACZ,UAAU,iBAAiB;YACzB,iBAAiB,EAAE;gBACjB,IAAI,CAAC,EAAE,GAAG,CAAA;gBACV,QAAQ,CAAC,EAAE,MAAM,CAAA;gBACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;gBACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;gBAClB,QAAQ,CAAC,EAAE,GAAG,CAAA;gBACd,uBAAuB,CAAC,EAAE;oBAAE,MAAM,EAAE,MAAM,CAAA;iBAAE,CAAA;gBAC5C,eAAe,CAAC,EAAE,MAAM,CAAA;gBACxB,iBAAiB,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,EAAE,CAAA;aAC1C,GAAG,CACA;gBACE,kCAAkC;gBAClC,QAAQ,CAAC,EAAE,MAAM,CAAA;gBACjB,GAAG,CAAC,EAAE,MAAM,CAAA;gBACZ,OAAO,EAAE,MAAM,CAAA;gBACf,SAAS,EAAE,MAAM,CAAA;gBACjB,OAAO,CAAC,EAAE,MAAM,CAAA;gBAChB,QAAQ,CAAC,EAAE,oBAAoB,CAAC,UAAU,CAAC,CAAA;aAC5C,GACD;gBAAE,GAAG,EAAE,MAAM,CAAA;aAAE,GACf;gBAAE,QAAQ,EAAE,MAAM,CAAA;aAAE,CACvB,CAAA;SACF;KACF;CACF;AAGD,OAAO,EAAE,cAAc,IAAI,SAAS,EAAE,oBAAoB,IAAI,cAAc,EAAE,CAAA"}
@@ -1,5 +1,5 @@
1
1
  import { FEAASBase } from './FEAASBase.js';
2
- export declare function FEAASLoaderProps(element?: HTMLElement): {
2
+ export declare function FEAASLoaderProps(element: HTMLElement): {
3
3
  opacity: number;
4
4
  hidden: boolean;
5
5
  };
@@ -1 +1 @@
1
- {"version":3,"file":"FEAASLoader.d.ts","sourceRoot":"","sources":["../../src/components/FEAASLoader.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE1C,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,WAAkB;;;EAK3D;AACD,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAA;AAE3D,qBAAa,WAAY,SAAQ,SAAS,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClE,UAAU,EAAE,gBAAgB,CAAA;IAC5B,MAAM,EAAE,cAAc,CAAA;IAEtB,QAAQ;;;;IAIR,YAAY;;;OAEkB;IAE9B,MAAM,CAAC,kBAAkB,WAAwB;IAEjD,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAgBhF,iBAAiB;IA6EjB,MAAM;CAGP;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,GAAG,CAAC;QACZ,UAAU,iBAAiB;YACzB,cAAc,EAAE,GAAG,CAAA;SACpB;KACF;CACF;AAGD,OAAO,EAAE,WAAW,IAAI,MAAM,EAAE,CAAA"}
1
+ {"version":3,"file":"FEAASLoader.d.ts","sourceRoot":"","sources":["../../src/components/FEAASLoader.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE1C,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW;;;EAKpD;AACD,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAA;AAE3D,qBAAa,WAAY,SAAQ,SAAS,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClE,UAAU,EAAE,gBAAgB,CAAA;IAC5B,MAAM,EAAE,cAAc,CAAA;IAEtB,QAAQ;;;;IAIR,YAAY;;;OAEkB;IAE9B,MAAM,CAAC,kBAAkB,WAAwB;IAEjD,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAgBhF,iBAAiB;IA6EjB,MAAM;CAGP;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,GAAG,CAAC;QACZ,UAAU,iBAAiB;YACzB,cAAc,EAAE,GAAG,CAAA;SACpB;KACF;CACF;AAGD,OAAO,EAAE,WAAW,IAAI,MAAM,EAAE,CAAA"}
@@ -1,6 +1,6 @@
1
1
  import { FEAASCDNStylesheetParams } from '../cdn.js';
2
2
  import { FEAASBase } from './FEAASBase.js';
3
- export declare function FEAASStylesheetProps(element?: FEAASBase): FEAASCDNStylesheetParams;
3
+ export declare function FEAASStylesheetProps(element: FEAASBase): FEAASCDNStylesheetParams;
4
4
  export type FEAASStylesheetProps = ReturnType<typeof FEAASStylesheetProps>;
5
5
  export declare class FEAASStylesheet extends FEAASBase<FEAASStylesheetProps, string> {
6
6
  getProps(): FEAASCDNStylesheetParams;
@@ -1 +1 @@
1
- {"version":3,"file":"FEAASStylesheet.d.ts","sourceRoot":"","sources":["../../src/components/FEAASStylesheet.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAmB,wBAAwB,EAAyB,MAAM,WAAW,CAAA;AAE5F,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE1C,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,SAAgB,4BAU7D;AACD,MAAM,MAAM,oBAAoB,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;AAE1E,qBAAa,eAAgB,SAAQ,SAAS,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAC1E,QAAQ;IAIR,aAAa,IAAI,OAAO;IAIxB,MAAM,SAAU,MAAM,UAGrB;IAED,IAAI;CAGL;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,GAAG,CAAC;QACZ,UAAU,iBAAiB;YACzB,kBAAkB,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG;gBAAE,QAAQ,CAAC,EAAE,GAAG,CAAA;aAAE,CAAA;SACvE;KACF;CACF;AAED,OAAO,EAAE,eAAe,IAAI,UAAU,EAAE,CAAA"}
1
+ {"version":3,"file":"FEAASStylesheet.d.ts","sourceRoot":"","sources":["../../src/components/FEAASStylesheet.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAmB,wBAAwB,EAAyB,MAAM,WAAW,CAAA;AAE5F,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE1C,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,4BAUtD;AACD,MAAM,MAAM,oBAAoB,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;AAE1E,qBAAa,eAAgB,SAAQ,SAAS,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAC1E,QAAQ;IAIR,aAAa,IAAI,OAAO;IAIxB,MAAM,SAAU,MAAM,UAGrB;IAED,IAAI;CAGL;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,GAAG,CAAC;QACZ,UAAU,iBAAiB;YACzB,kBAAkB,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG;gBAAE,QAAQ,CAAC,EAAE,GAAG,CAAA;aAAE,CAAA;SACvE;KACF;CACF;AAED,OAAO,EAAE,eAAe,IAAI,UAAU,EAAE,CAAA"}