@sitecore-content-sdk/react 2.0.0-canary.2 → 2.0.0-canary.4

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 (108) hide show
  1. package/dist/cjs/components/Date.js +13 -11
  2. package/dist/cjs/components/DefaultEmptyFieldEditingComponents.js +2 -1
  3. package/dist/cjs/components/DesignLibrary/DesignLibrary.js +3 -7
  4. package/dist/cjs/components/DesignLibrary/DesignLibraryClientEvents.js +13 -5
  5. package/dist/cjs/components/EditingScripts.js +2 -2
  6. package/dist/cjs/components/ErrorBoundary.js +8 -3
  7. package/dist/cjs/components/File.js +3 -1
  8. package/dist/cjs/components/Form.js +12 -5
  9. package/dist/cjs/components/Image.js +12 -9
  10. package/dist/cjs/components/Link.js +19 -46
  11. package/dist/cjs/components/Placeholder/AppPlaceholder.js +57 -58
  12. package/dist/cjs/components/Placeholder/ClientComponentWrapper.js +5 -9
  13. package/dist/cjs/components/Placeholder/Placeholder.js +21 -93
  14. package/dist/cjs/components/Placeholder/index.js +1 -2
  15. package/dist/cjs/components/Placeholder/placeholder-utils.js +7 -35
  16. package/dist/cjs/components/RichText.js +18 -49
  17. package/dist/cjs/components/SitecoreProvider.js +81 -33
  18. package/dist/cjs/components/Text.js +12 -8
  19. package/dist/cjs/enhancers/withAppPlaceholder.js +6 -0
  20. package/dist/cjs/enhancers/withDatasourceCheck.js +4 -3
  21. package/dist/cjs/enhancers/withEditorChromes.js +50 -15
  22. package/dist/cjs/enhancers/withEmptyFieldEditingComponent.js +8 -38
  23. package/dist/cjs/enhancers/withFieldMetadata.js +9 -39
  24. package/dist/cjs/enhancers/withPlaceholder.js +16 -52
  25. package/dist/cjs/enhancers/withSitecore.js +5 -29
  26. package/dist/cjs/index.js +9 -7
  27. package/dist/cjs/search/utils.js +2 -2
  28. package/dist/esm/components/Date.js +13 -11
  29. package/dist/esm/components/DefaultEmptyFieldEditingComponents.js +2 -1
  30. package/dist/esm/components/DesignLibrary/DesignLibrary.js +3 -7
  31. package/dist/esm/components/DesignLibrary/DesignLibraryClientEvents.js +12 -4
  32. package/dist/esm/components/EditingScripts.js +2 -2
  33. package/dist/esm/components/ErrorBoundary.js +8 -3
  34. package/dist/esm/components/File.js +3 -1
  35. package/dist/esm/components/Form.js +11 -4
  36. package/dist/esm/components/Image.js +12 -9
  37. package/dist/esm/components/Link.js +16 -13
  38. package/dist/esm/components/Placeholder/AppPlaceholder.js +58 -59
  39. package/dist/esm/components/Placeholder/ClientComponentWrapper.js +4 -8
  40. package/dist/esm/components/Placeholder/Placeholder.js +20 -89
  41. package/dist/esm/components/Placeholder/index.js +1 -1
  42. package/dist/esm/components/Placeholder/placeholder-utils.js +5 -32
  43. package/dist/esm/components/RichText.js +15 -16
  44. package/dist/esm/components/SitecoreProvider.js +46 -32
  45. package/dist/esm/components/Text.js +12 -8
  46. package/dist/esm/enhancers/withAppPlaceholder.js +6 -0
  47. package/dist/esm/enhancers/withDatasourceCheck.js +3 -2
  48. package/dist/esm/enhancers/withEditorChromes.js +17 -12
  49. package/dist/esm/enhancers/withEmptyFieldEditingComponent.js +5 -5
  50. package/dist/esm/enhancers/withFieldMetadata.js +6 -6
  51. package/dist/esm/enhancers/withPlaceholder.js +15 -52
  52. package/dist/esm/enhancers/withSitecore.js +6 -29
  53. package/dist/esm/index.js +4 -3
  54. package/dist/esm/search/utils.js +1 -1
  55. package/package.json +5 -5
  56. package/types/components/Date.d.ts.map +1 -1
  57. package/types/components/DefaultEmptyFieldEditingComponents.d.ts.map +1 -1
  58. package/types/components/DesignLibrary/DesignLibrary.d.ts +0 -2
  59. package/types/components/DesignLibrary/DesignLibrary.d.ts.map +1 -1
  60. package/types/components/DesignLibrary/DesignLibraryClientEvents.d.ts.map +1 -1
  61. package/types/components/EditingScripts.d.ts +2 -2
  62. package/types/components/EditingScripts.d.ts.map +1 -1
  63. package/types/components/ErrorBoundary.d.ts +2 -2
  64. package/types/components/ErrorBoundary.d.ts.map +1 -1
  65. package/types/components/File.d.ts.map +1 -1
  66. package/types/components/Form.d.ts.map +1 -1
  67. package/types/components/Image.d.ts.map +1 -1
  68. package/types/components/Link.d.ts.map +1 -1
  69. package/types/components/Placeholder/AppPlaceholder.d.ts +1 -1
  70. package/types/components/Placeholder/AppPlaceholder.d.ts.map +1 -1
  71. package/types/components/Placeholder/ClientComponentWrapper.d.ts +2 -2
  72. package/types/components/Placeholder/ClientComponentWrapper.d.ts.map +1 -1
  73. package/types/components/Placeholder/Placeholder.d.ts +4 -19
  74. package/types/components/Placeholder/Placeholder.d.ts.map +1 -1
  75. package/types/components/Placeholder/index.d.ts +1 -1
  76. package/types/components/Placeholder/index.d.ts.map +1 -1
  77. package/types/components/Placeholder/models.d.ts +30 -56
  78. package/types/components/Placeholder/models.d.ts.map +1 -1
  79. package/types/components/Placeholder/placeholder-utils.d.ts +5 -13
  80. package/types/components/Placeholder/placeholder-utils.d.ts.map +1 -1
  81. package/types/components/RichText.d.ts +2 -0
  82. package/types/components/RichText.d.ts.map +1 -1
  83. package/types/components/SitecoreProvider.d.ts +43 -13
  84. package/types/components/SitecoreProvider.d.ts.map +1 -1
  85. package/types/components/Text.d.ts.map +1 -1
  86. package/types/enhancers/withAppPlaceholder.d.ts +6 -0
  87. package/types/enhancers/withAppPlaceholder.d.ts.map +1 -1
  88. package/types/enhancers/withDatasourceCheck.d.ts.map +1 -1
  89. package/types/enhancers/withEditorChromes.d.ts +4 -1
  90. package/types/enhancers/withEditorChromes.d.ts.map +1 -1
  91. package/types/enhancers/withEmptyFieldEditingComponent.d.ts +5 -3
  92. package/types/enhancers/withEmptyFieldEditingComponent.d.ts.map +1 -1
  93. package/types/enhancers/withFieldMetadata.d.ts +4 -2
  94. package/types/enhancers/withFieldMetadata.d.ts.map +1 -1
  95. package/types/enhancers/withPlaceholder.d.ts +14 -32
  96. package/types/enhancers/withPlaceholder.d.ts.map +1 -1
  97. package/types/enhancers/withSitecore.d.ts +8 -50
  98. package/types/enhancers/withSitecore.d.ts.map +1 -1
  99. package/types/index.d.ts +4 -3
  100. package/types/index.d.ts.map +1 -1
  101. package/dist/cjs/enhancers/withComponentMap.js +0 -24
  102. package/dist/cjs/enhancers/withLoadImportMap.js +0 -62
  103. package/dist/esm/enhancers/withComponentMap.js +0 -18
  104. package/dist/esm/enhancers/withLoadImportMap.js +0 -25
  105. package/types/enhancers/withComponentMap.d.ts +0 -13
  106. package/types/enhancers/withComponentMap.d.ts.map +0 -1
  107. package/types/enhancers/withLoadImportMap.d.ts +0 -28
  108. package/types/enhancers/withLoadImportMap.d.ts.map +0 -1
@@ -10,22 +10,16 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  }
11
11
  return t;
12
12
  };
13
- import React, { forwardRef } from 'react';
13
+ import React from 'react';
14
14
  import { isFieldValueEmpty } from '@sitecore-content-sdk/content/layout';
15
15
  import { withFieldMetadata } from '../enhancers/withFieldMetadata';
16
16
  import { withEmptyFieldEditingComponent } from '../enhancers/withEmptyFieldEditingComponent';
17
17
  import { DefaultEmptyFieldEditingComponentText } from './DefaultEmptyFieldEditingComponents';
18
- /**
19
- * The Link component.
20
- * @param {LinkProps} props component props
21
- * @public
22
- */
23
- export const Link = withFieldMetadata(withEmptyFieldEditingComponent(forwardRef(
24
- // eslint-disable-next-line no-unused-vars
25
- (_a, ref) => {
26
- var { field, editable = true, showLinkTextWithChildrenPresent } = _a, otherProps = __rest(_a, ["field", "editable", "showLinkTextWithChildrenPresent"]);
18
+ const LinkComponent = (_a) => {
19
+ var { field, showLinkTextWithChildrenPresent, ref } = _a, otherProps = __rest(_a, ["field", "showLinkTextWithChildrenPresent", "ref"]);
27
20
  const children = otherProps.children;
28
21
  const dynamicField = field;
22
+ delete otherProps.editable; // prevent editable from being passed to the DOM
29
23
  if (isFieldValueEmpty(dynamicField)) {
30
24
  return null;
31
25
  }
@@ -49,7 +43,16 @@ export const Link = withFieldMetadata(withEmptyFieldEditingComponent(forwardRef(
49
43
  anchorAttrs.rel = 'noopener noreferrer';
50
44
  }
51
45
  const linkText = showLinkTextWithChildrenPresent || !children ? link.text || link.href : null;
52
- const element = React.createElement('a', Object.assign(Object.assign(Object.assign({}, anchorAttrs), otherProps), { key: 'link', ref }), linkText, children);
53
- return React.createElement(React.Fragment, null, element);
54
- }), { defaultEmptyFieldEditingComponent: DefaultEmptyFieldEditingComponentText, isForwardRef: true }), true);
46
+ return (React.createElement("a", Object.assign({}, anchorAttrs, otherProps, { key: "link", ref: ref }),
47
+ linkText,
48
+ children));
49
+ };
50
+ /**
51
+ * The Link component.
52
+ * @param {LinkProps} props component props
53
+ * @public
54
+ */
55
+ export const Link = withFieldMetadata(withEmptyFieldEditingComponent(LinkComponent, {
56
+ defaultEmptyFieldEditingComponent: DefaultEmptyFieldEditingComponentText,
57
+ }));
55
58
  Link.displayName = 'Link';
@@ -1,72 +1,71 @@
1
- import { getAppComponentProps, getComponentForRendering, getPlaceholderRenderings, renderEmptyPlaceholder, } from './placeholder-utils';
1
+ import { getChildComponentProps, getComponentForRendering, getPlaceholderRenderings, renderEmptyPlaceholder, } from './placeholder-utils';
2
2
  import React from 'react';
3
- import { PlaceholderMetadata } from './PlaceholderMetadata';
4
3
  import ErrorBoundary from '../ErrorBoundary';
5
4
  import { ClientComponentWrapper } from './ClientComponentWrapper';
6
5
  import { rsc } from '#rsc-env';
7
- /**
8
- * The implemention of placeholder compatible with React Server Components.
9
- * Renders components from the layout data for the given placeholder name, with consideration for page edit mode.
10
- * Pulls components from the provided component map.
11
- * @param {AppPlaceholderProps} props Placeholder props
12
- * @returns {React.ReactNode | React.ReactElement[]} rendered component(s)
13
- * @public
14
- */
15
- export const AppPlaceholder = (props) => {
16
- const { rendering: parentRendering, componentMap, page } = props;
17
- const placeholderRenderings = getPlaceholderRenderings(parentRendering, props.name, page.mode.isEditing);
18
- const components = placeholderRenderings
19
- .map((rendering, index) => {
20
- const { component: Component, isEmpty, componentType, dynamic, } = getComponentForRendering(rendering, props.name, componentMap, props.hiddenRenderingComponent, props.missingComponentComponent);
21
- const isClient = componentType === 'client';
22
- const key = rendering.uid || `component-${index}`;
23
- // Use rsc context to determine the current runtime
24
- const componentRuntime = rsc ? 'server' : 'client';
25
- const renderedProps = getAppComponentProps(props, rendering);
26
- const finalRenderedProps = props.modifyComponentProps
27
- ? props.modifyComponentProps(renderedProps)
28
- : renderedProps;
6
+ import { PlaceholderMetadata } from './PlaceholderMetadata';
7
+ const AppPlaceholderComponent = (props) => {
8
+ const renderingData = props.rendering;
9
+ const isEditing = props.page.mode.isEditing;
10
+ const placeholderRenderings = getPlaceholderRenderings(renderingData, props.name, isEditing);
11
+ const isEmpty = !placeholderRenderings.length;
12
+ const components = getPlaceholderComponents(props, placeholderRenderings);
13
+ if (isEmpty) {
14
+ let renderedOutput = components;
15
+ if (props.renderEmpty) {
16
+ renderedOutput = props.renderEmpty(components);
17
+ }
18
+ return isEditing ? renderEmptyPlaceholder(renderedOutput) : renderedOutput;
19
+ }
20
+ else if (props.render) {
21
+ return props.render(components, placeholderRenderings, props);
22
+ }
23
+ return components;
24
+ };
25
+ const getPlaceholderComponents = (placeholderProps, placeholderRenderings) => {
26
+ const { name, rendering, passThroughComponentProps, missingComponentComponent, hiddenRenderingComponent, errorComponent, componentLoadingMessage, renderEach, modifyComponentProps, componentMap, page, } = placeholderProps;
27
+ const isEditing = page.mode.isEditing;
28
+ const componentRuntime = rsc ? 'server' : 'client';
29
+ const transformedComponents = placeholderRenderings
30
+ .map((componentRendering, index) => {
31
+ const { component: ChildComponent, isEmpty: componentEmpty, componentType, dynamic, } = getComponentForRendering(componentRendering, name, componentMap, hiddenRenderingComponent, missingComponentComponent);
32
+ const key = componentRendering.uid || `component-${index}`;
33
+ const renderedProps = modifyComponentProps
34
+ ? modifyComponentProps(getChildComponentProps(placeholderProps, componentRendering))
35
+ : getChildComponentProps(placeholderProps, componentRendering);
29
36
  // Client wrapper is required only when component crosses boundary from server to client.
30
37
  // It happens when component is marker as client and rendered in RSC context.
31
38
  // Also, it is not required when component is hidden or empty, as it will be rendered whthout boundary crossing.
32
- const useClientWrapper = isClient && rsc && !isEmpty;
33
- let rendered = useClientWrapper ? (React.createElement(ClientComponentWrapper, { rendering: rendering, componentProps: finalRenderedProps, placeholderName: props.name, key: key })) : (React.createElement(Component, Object.assign({ key: key }, finalRenderedProps, { rendering: rendering, page: page, componentMap: componentMap })));
34
- if (!isEmpty) {
39
+ const useClientWrapper = componentType === 'client' && rsc && !componentEmpty;
40
+ let rendered = useClientWrapper ? (React.createElement(ClientComponentWrapper, { rendering: renderedProps.rendering, componentProps: Object.assign(Object.assign({}, renderedProps), passThroughComponentProps), placeholderName: name, key: key })) : (React.createElement(ChildComponent, Object.assign({ key: key }, renderedProps, passThroughComponentProps, { page: page, componentMap: componentMap })));
41
+ if (renderEach) {
42
+ rendered = renderEach(rendered, index);
43
+ }
44
+ if (!componentEmpty) {
35
45
  const errorBoundaryKey = rendered.type + '-' + index;
36
- const disableSuspense = props.disableSuspense || false;
37
- rendered = (React.createElement(ErrorBoundary, { "data-testid": "error-boundary", key: errorBoundaryKey, errorComponent: props.errorComponent, componentLoadingMessage: props.componentLoadingMessage, isDynamic: dynamic, disableSuspense: disableSuspense, rendering: rendered.props.rendering }, rendered));
46
+ const disableSuspense = placeholderProps.disableSuspense || false;
47
+ rendered = (React.createElement(ErrorBoundary, { "data-testid": "error-boundary", key: errorBoundaryKey, errorComponent: errorComponent, componentLoadingMessage: componentLoadingMessage, isDynamic: dynamic, disableSuspense: disableSuspense, rendering: rendered.props.rendering }, rendered));
38
48
  }
39
49
  // if in edit mode then emit shallow chromes for hydration in Pages
40
- if (page.mode.isEditing) {
41
- const key = rendering.uid || `component-${index}`;
42
- return (React.createElement(PlaceholderMetadata, { key: key, rendering: rendering, componentRuntime: componentRuntime }, rendered));
43
- }
44
- return rendered;
50
+ return isEditing ? (React.createElement(PlaceholderMetadata, { key: key, rendering: componentRendering, componentRuntime: componentRuntime }, rendered)) : (rendered);
45
51
  })
46
- .filter((element) => element);
47
- const finalRendering = page.mode.isEditing
48
- ? [
49
- React.createElement(PlaceholderMetadata, { key: parentRendering.uid || 'placeholder-metadata-root', placeholderName: props.name, rendering: parentRendering }, components),
50
- ]
51
- : components;
52
- const placeholderEmpty = !placeholderRenderings.length;
53
- if (placeholderEmpty) {
54
- const rendered = props.renderEmpty ? props.renderEmpty(finalRendering) : finalRendering;
55
- return page.mode.isEditing ? renderEmptyPlaceholder(rendered) : rendered;
56
- }
57
- if (props.render) {
58
- return props.render(components, placeholderRenderings, props);
59
- }
60
- else if (props.renderEach) {
61
- const renderEach = props.renderEach;
62
- return finalRendering.map((component, index) => {
63
- if (component && component.props && component.props.type === 'text/sitecore') {
64
- return component;
65
- }
66
- return renderEach(component, index);
67
- });
68
- }
69
- else {
70
- return finalRendering;
52
+ .filter((element) => element); // remove nulls
53
+ if (!isEditing) {
54
+ return transformedComponents;
71
55
  }
56
+ return [
57
+ React.createElement(PlaceholderMetadata, { key: rendering.uid, placeholderName: name, rendering: rendering }, transformedComponents),
58
+ ];
72
59
  };
60
+ /**
61
+ * The implemention of placeholder compatible with React Server Components.
62
+ * Renders components from the layout data for the given placeholder name, with consideration for page edit mode.
63
+ * Pulls components from the provided component map.
64
+ * @param {AppPlaceholderProps} props Placeholder props
65
+ * @returns {React.ReactNode | React.ReactElement[]} rendered component(s)
66
+ * @public
67
+ */
68
+ export const AppPlaceholder = (props) => (
69
+ // Using error boundary for errors that may happen within Placeholder itself
70
+ React.createElement(ErrorBoundary, { errorComponent: props.errorComponent },
71
+ React.createElement(AppPlaceholderComponent, Object.assign({}, props))));
@@ -1,14 +1,10 @@
1
1
  'use client';
2
- import { ComponentMapReactContext } from '../SitecoreProvider';
3
- import { useContext } from 'react';
2
+ import { useSitecore } from '../SitecoreProvider';
4
3
  import React from 'react';
5
- import { useSitecore } from '../../enhancers/withSitecore';
6
4
  import { getComponentForRendering } from './placeholder-utils';
7
5
  export const ClientComponentWrapper = (props) => {
8
- const { page } = useSitecore();
9
- const componentMap = useContext(ComponentMapReactContext);
10
- const componentPropsWithContext = Object.assign(Object.assign({}, props.componentProps), { rendering: props.rendering, componentMap,
11
- page });
12
- const { component: Component } = getComponentForRendering(props.rendering, props.placeholderName, componentMap);
6
+ const { page, componentMap: clientComponentMap } = useSitecore();
7
+ const componentPropsWithContext = Object.assign(Object.assign({}, props.componentProps), { rendering: props.rendering, componentMap: clientComponentMap, page });
8
+ const { component: Component } = getComponentForRendering(props.rendering, props.placeholderName, clientComponentMap);
13
9
  return React.createElement(Component, Object.assign({}, componentPropsWithContext));
14
10
  };
@@ -1,95 +1,26 @@
1
1
  'use client';
2
- import React from 'react';
3
- import { withComponentMap } from '../../enhancers/withComponentMap';
2
+ import React, { useEffect } from 'react';
4
3
  import { PagesEditor } from '@sitecore-content-sdk/content/editing';
5
- import { withSitecore } from '../../enhancers/withSitecore';
6
- import { getComponentForRendering, getPlaceholderRenderings, getRenderedComponentProps, renderEmptyPlaceholder, } from './placeholder-utils';
7
- import { PlaceholderMetadata } from './PlaceholderMetadata';
8
- import ErrorBoundary, { ErrorComponent } from '../ErrorBoundary';
9
- export class PlaceholderComponent extends React.Component {
10
- constructor(props) {
11
- super(props);
12
- this.isEmpty = false;
13
- this.state = {};
14
- }
15
- componentDidMount() {
16
- if (this.isEmpty && PagesEditor.isActive()) {
17
- PagesEditor.resetChromes();
18
- }
19
- }
20
- componentDidCatch(error) {
21
- this.setState({ error });
22
- }
23
- render() {
24
- const childProps = Object.assign({}, this.props);
25
- delete childProps.componentMap;
26
- if (this.state.error) {
27
- if (childProps.errorComponent) {
28
- return React.createElement(childProps.errorComponent, { error: this.state.error });
29
- }
30
- return (React.createElement(ErrorComponent, { message: `A rendering error occurred: ${this.state.error.message}.` }));
31
- }
32
- const renderingData = childProps.rendering;
33
- const placeholderRenderings = getPlaceholderRenderings(renderingData, this.props.name, this.props.page.mode.isEditing);
34
- this.isEmpty = !placeholderRenderings.length;
35
- const components = PlaceholderComponent.getRenderedComponents(this.props, placeholderRenderings);
36
- if (this.isEmpty) {
37
- const rendered = this.props.renderEmpty ? this.props.renderEmpty(components) : components;
38
- return this.props.page.mode.isEditing ? renderEmptyPlaceholder(rendered) : rendered;
39
- }
40
- else if (this.props.render) {
41
- return this.props.render(components, placeholderRenderings, childProps);
42
- }
43
- else if (this.props.renderEach) {
44
- const renderEach = this.props.renderEach;
45
- return components.map((component, index) => {
46
- if (component && component.props && component.props.type === 'text/sitecore') {
47
- return component;
48
- }
49
- return renderEach(component, index);
50
- });
51
- }
52
- else {
53
- return components;
54
- }
55
- }
56
- }
57
- /**
58
- * Renders the components for the placeholder based on the provided rendering data.
59
- * @param {PlaceholderProps} props placeholder component props
60
- * @param {ComponentRendering[]} placeholderRenderings renderings within placeholder
61
- * @returns {React.ReactNode | React.ReactElement[]} rendered components
62
- */
63
- PlaceholderComponent.getRenderedComponents = (props, placeholderRenderings) => {
64
- const { name, missingComponentComponent, hiddenRenderingComponent } = props;
65
- const transformedComponents = placeholderRenderings
66
- .map((componentRendering, index) => {
67
- const key = componentRendering.uid || `component-${index}`;
68
- const renderedProps = getRenderedComponentProps(props, componentRendering, key);
69
- const component = getComponentForRendering(componentRendering, name, props.componentMap, hiddenRenderingComponent, missingComponentComponent);
70
- let rendered = React.createElement(component.component, props.modifyComponentProps ? props.modifyComponentProps(renderedProps) : renderedProps);
71
- if (!component.isEmpty) {
72
- const errorBoundaryKey = rendered.type + '-' + index;
73
- const disableSuspense = props.disableSuspense || false;
74
- rendered = (React.createElement(ErrorBoundary, { "data-testid": "error-boundary", key: errorBoundaryKey, errorComponent: props.errorComponent, componentLoadingMessage: props.componentLoadingMessage, isDynamic: component.dynamic, disableSuspense: disableSuspense, rendering: rendered.props.rendering }, rendered));
75
- }
76
- // if in edit mode then emit shallow chromes for hydration in Pages
77
- if (props.page.mode.isEditing) {
78
- return (React.createElement(PlaceholderMetadata, { key: key, rendering: componentRendering }, rendered));
79
- }
80
- return rendered;
81
- })
82
- .filter((element) => element); // remove nulls
83
- if (props.page.mode.isEditing) {
84
- return [
85
- React.createElement(PlaceholderMetadata, { key: props.rendering.uid, placeholderName: name, rendering: props.rendering }, transformedComponents),
86
- ];
87
- }
88
- return transformedComponents;
89
- };
90
- const PlaceholderWithComponentMap = withComponentMap(PlaceholderComponent);
4
+ import { getPlaceholderRenderings } from './placeholder-utils';
5
+ import { useSitecore } from '../SitecoreProvider';
6
+ import { AppPlaceholder } from './AppPlaceholder';
91
7
  /**
92
8
  * The Placeholder component.
9
+ * Renders the components assigned to a placeholder in Sitecore. It also supports custom rendering and empty state.
10
+ * @param {PlaceholderProps} props - The props for the Placeholder component.
11
+ * @returns The rendered Placeholder component.
93
12
  * @public
94
13
  */
95
- export const Placeholder = withSitecore()(PlaceholderWithComponentMap);
14
+ export const Placeholder = (props) => {
15
+ const { page, componentMap } = useSitecore();
16
+ const placeholderRenderings = getPlaceholderRenderings(props.rendering, props.name, page.mode.isEditing);
17
+ const isEmpty = !placeholderRenderings.length;
18
+ useEffect(() => {
19
+ if (isEmpty && PagesEditor.isActive()) {
20
+ PagesEditor.resetChromes();
21
+ }
22
+ // eslint-disable-next-line react-hooks/exhaustive-deps
23
+ }, []); // Empty array so it runs only once on mount
24
+ const appProps = Object.assign(Object.assign({}, props), { page, componentMap });
25
+ return React.createElement(AppPlaceholder, Object.assign({}, appProps));
26
+ };
@@ -1,4 +1,4 @@
1
- export { Placeholder, PlaceholderComponent } from './Placeholder';
1
+ export { Placeholder } from './Placeholder';
2
2
  export { PlaceholderMetadata } from './PlaceholderMetadata';
3
3
  export { AppPlaceholder } from './AppPlaceholder';
4
4
  export * from './placeholder-utils';
@@ -1,14 +1,3 @@
1
- var __rest = (this && this.__rest) || function (s, e) {
2
- var t = {};
3
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
- t[p] = s[p];
5
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
- t[p[i]] = s[p[i]];
9
- }
10
- return t;
11
- };
12
1
  import React from 'react';
13
2
  import { MissingComponent } from '../MissingComponent';
14
3
  import { DEFAULT_EXPORT_NAME } from '../sharedTypes';
@@ -17,7 +6,7 @@ import { HIDDEN_RENDERING_NAME } from '@sitecore-content-sdk/content';
17
6
  import { HiddenRendering } from '../HiddenRendering';
18
7
  import { FEaaSComponent, FEaaSWrapper, BYOCComponent, BYOCWrapper, BYOC_COMPONENT_RENDERING_NAME, BYOC_WRAPPER_RENDERING_NAME, FEAAS_COMPONENT_RENDERING_NAME, FEAAS_WRAPPER_RENDERING_NAME, } from '../FEaaS';
19
8
  /**
20
- * Get the renderings for the specified placeholder from the rendering data.
9
+ * Get the renderings for the specified placeholder from the rendering layout data.
21
10
  * @param {ComponentRendering | RouteData } rendering rendering data
22
11
  * @param {string} name placeholder name
23
12
  * @param {boolean} isEditing whether components should be rendered in editing mode
@@ -82,28 +71,12 @@ export const renderEmptyPlaceholder = (node) => {
82
71
  return React.createElement("div", { className: "sc-jss-empty-placeholder" }, node);
83
72
  };
84
73
  /**
85
- * Get component props to be passed to the rendered component.
86
- * @param {PlaceholderProps} placeholderProps current placeholder props
87
- * @param {ComponentRendering} componentRendering rendering to be rendered
88
- * @param {string} renderingKey unique key to pass over to rendering props
89
- * @returns {RenderedProps} props to be passed to the rendered component
90
- */
91
- export const getRenderedComponentProps = (placeholderProps, componentRendering, renderingKey) => {
92
- // eslint-disable-next-line no-unused-vars
93
- const { fields, params: placeholderParams } = placeholderProps, passThroughProps = __rest(placeholderProps, ["fields", "params"]);
94
- delete passThroughProps.missingComponentComponent;
95
- delete passThroughProps.hiddenRenderingComponent;
96
- delete passThroughProps.name;
97
- const mergedContentProps = getAppComponentProps(placeholderProps, componentRendering);
98
- return Object.assign(Object.assign(Object.assign({ key: renderingKey }, passThroughProps), mergedContentProps), { rendering: componentRendering });
99
- };
100
- /**
101
- * Merge placeholder and component field and params content props.
102
- * @param {BasePlaceholderProps} placeholderProps placeholder props
74
+ * Merge specific placeholder props with component field and params content props.
75
+ * @param {PlaceholderProps} placeholderProps placeholder props
103
76
  * @param {ComponentRendering} componentRendering component rendering
104
77
  * @returns {ComponentProps} merged props
105
78
  */
106
- export function getAppComponentProps(placeholderProps, componentRendering) {
79
+ export function getChildComponentProps(placeholderProps, componentRendering) {
107
80
  const fields = Object.assign(Object.assign({}, (placeholderProps.fields || {})), (componentRendering.fields || {}));
108
81
  const params = Object.assign(Object.assign({}, (placeholderProps.params || {})), (componentRendering.params || {}));
109
82
  return {
@@ -171,7 +144,7 @@ export const getComponentForRendering = (renderingDefinition, placeholderName, c
171
144
  };
172
145
  }
173
146
  else if (renderingDefinition.componentName === BYOC_WRAPPER_RENDERING_NAME) {
174
- // wrapping with error boundary could cause problems in case where parent component uses withPlaceholder HOC and tries to access its children props
147
+ // wrapping with error boundary could cause problems in case where parent component uses withPlaceholder HOCs and tries to access its children props
175
148
  // that's why we need to mark BYOC wrapper dynamic
176
149
  return {
177
150
  component: BYOCWrapper,
@@ -10,30 +10,29 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  }
11
11
  return t;
12
12
  };
13
- import React, { forwardRef } from 'react';
13
+ import React from 'react';
14
14
  import { isFieldValueEmpty } from '@sitecore-content-sdk/content/layout';
15
15
  import { withFieldMetadata } from '../enhancers/withFieldMetadata';
16
16
  import { withEmptyFieldEditingComponent } from '../enhancers/withEmptyFieldEditingComponent';
17
17
  import { DefaultEmptyFieldEditingComponentText } from './DefaultEmptyFieldEditingComponents';
18
- /**
19
- * The RichText component.
20
- * @param {RichTextProps} props component props
21
- * @public
22
- */
23
- export const RichText = withFieldMetadata(withEmptyFieldEditingComponent(forwardRef((
24
- // eslint-disable-next-line no-unused-vars
25
- _a, ref) => {
26
- var
27
- // eslint-disable-next-line no-unused-vars
28
- { field, tag = 'div', editable = true } = _a, otherProps = __rest(_a,
29
- // eslint-disable-next-line no-unused-vars
30
- ["field", "tag", "editable"]);
18
+ const RichTextComponent = (_a) => {
19
+ var { field, tag = 'div', ref } = _a, otherProps = __rest(_a, ["field", "tag", "ref"]);
31
20
  if (isFieldValueEmpty(field)) {
32
21
  return null;
33
22
  }
23
+ delete otherProps.editable; // prevent editable from being passed to the DOM
34
24
  const htmlProps = Object.assign({ dangerouslySetInnerHTML: {
35
25
  __html: field.value,
36
26
  }, ref, suppressHydrationWarning: field.metadata ? true : undefined }, otherProps);
37
- return React.createElement(tag || 'div', htmlProps);
38
- }), { defaultEmptyFieldEditingComponent: DefaultEmptyFieldEditingComponentText, isForwardRef: true }), true);
27
+ const Tag = (tag || 'div');
28
+ return React.createElement(Tag, Object.assign({}, htmlProps));
29
+ };
30
+ /**
31
+ * The RichText component.
32
+ * @param {RichTextProps} props component props
33
+ * @public
34
+ */
35
+ export const RichText = withFieldMetadata(withEmptyFieldEditingComponent(RichTextComponent, {
36
+ defaultEmptyFieldEditingComponent: DefaultEmptyFieldEditingComponentText,
37
+ }));
39
38
  RichText.displayName = 'RichText';
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import React from 'react';
2
+ import React, { useState, useEffect, useCallback, useMemo, useContext } from 'react';
3
3
  import fastDeepEqual from 'fast-deep-equal/es6/react';
4
4
  /**
5
5
  * The context for the SitecoreProvider component.
@@ -10,38 +10,52 @@ export const ComponentMapReactContext = React.createContext(new Map());
10
10
  export const ImportMapReactContext = React.createContext(undefined);
11
11
  /**
12
12
  * The SitecoreProvider component.
13
+ * @param {SitecoreProviderProps} props - The props for the SitecoreProvider component.
14
+ * @param {SitecoreProviderProps['api']} props.api - The API configuration.
15
+ * @param {SitecoreProviderProps['page']} props.page - The page data.
16
+ * @param {SitecoreProviderProps['componentMap']} props.componentMap - The component map.
17
+ * @param {SitecoreProviderProps['loadImportMap']} props.loadImportMap - The function to load the import map.
18
+ * @param {React.ReactNode} props.children - The children to render.
19
+ * @returns {React.ReactNode} The SitecoreProvider component.
13
20
  * @public
14
21
  */
15
- export class SitecoreProvider extends React.Component {
16
- constructor(props) {
17
- super(props);
18
- /**
19
- * Update page state.
20
- * @param {Page} value New page value
21
- */
22
- this.setPage = (value) => {
23
- this.setState({
24
- page: value,
25
- });
26
- };
27
- this.state = {
28
- page: props.page,
29
- setPage: this.setPage,
30
- api: props.api,
31
- };
32
- }
33
- componentDidUpdate(prevProps) {
34
- // In case if somebody will manage SitecoreProvider state by passing fresh `page` prop
35
- // instead of using `updateContext`
36
- if (!fastDeepEqual(prevProps.page, this.props.page)) {
37
- this.setPage(this.props.page);
38
- return;
22
+ export const SitecoreProvider = (props) => {
23
+ const { api, page: propsPage, componentMap, loadImportMap, children } = props;
24
+ const [page, setPageInternal] = useState(propsPage);
25
+ // Memoize setPage callback
26
+ const setPage = useCallback((value) => {
27
+ setPageInternal(value);
28
+ }, []);
29
+ // Handle page prop changes using useEffect instead of componentDidUpdate
30
+ useEffect(() => {
31
+ if (!fastDeepEqual(propsPage, page)) {
32
+ setPage(propsPage);
39
33
  }
40
- }
41
- render() {
42
- return (React.createElement(ImportMapReactContext.Provider, { value: this.props.loadImportMap },
43
- React.createElement(ComponentMapReactContext.Provider, { value: this.props.componentMap },
44
- React.createElement(SitecoreProviderReactContext.Provider, { value: this.state }, this.props.children))));
45
- }
46
- }
34
+ }, [propsPage, page, setPage]);
35
+ // Memoize the context value to prevent unnecessary re-renders
36
+ const contextValue = useMemo(() => ({
37
+ page,
38
+ setPage,
39
+ api,
40
+ componentMap,
41
+ loadImportMap,
42
+ }), [page, setPage, api, componentMap, loadImportMap]);
43
+ return (React.createElement(SitecoreProviderReactContext.Provider, { value: contextValue }, children));
44
+ };
47
45
  SitecoreProvider.displayName = 'SitecoreProvider';
46
+ /**
47
+ * This hook grants acсess to the current Sitecore page and api.
48
+ * @param {UseSitecoreOptions} [options] hook options
49
+ * @example
50
+ * const EditMode = () => {
51
+ * const { page } = useSitecore();
52
+ * return <span>Edit Mode is {page.mode.isEditing ? 'active' : 'inactive'}</span>
53
+ * }
54
+ * @returns {SitecoreProviderState} The current Sitecore context, including the page and api.
55
+ * @public
56
+ */
57
+ export function useSitecore(options) {
58
+ const scContext = useContext(SitecoreProviderReactContext);
59
+ const updatable = options === null || options === void 0 ? void 0 : options.updatable;
60
+ return Object.assign(Object.assign({}, scContext), { setPage: updatable ? scContext.setPage : undefined });
61
+ }
@@ -15,11 +15,7 @@ import { isFieldValueEmpty } from '@sitecore-content-sdk/content/layout';
15
15
  import { withFieldMetadata } from '../enhancers/withFieldMetadata';
16
16
  import { withEmptyFieldEditingComponent } from '../enhancers/withEmptyFieldEditingComponent';
17
17
  import { DefaultEmptyFieldEditingComponentText } from './DefaultEmptyFieldEditingComponents';
18
- /**
19
- * The Text component.
20
- * @public
21
- */
22
- export const Text = withFieldMetadata(withEmptyFieldEditingComponent((_a) => {
18
+ const TextComponent = (_a) => {
23
19
  var { field, tag, editable = true, encode = true } = _a, otherProps = __rest(_a, ["field", "tag", "editable", "encode"]);
24
20
  if (isFieldValueEmpty(field)) {
25
21
  return null;
@@ -53,14 +49,22 @@ export const Text = withFieldMetadata(withEmptyFieldEditingComponent((_a) => {
53
49
  else {
54
50
  children = output;
55
51
  }
52
+ const Tag = (tag || 'span');
56
53
  if (field.metadata) {
57
- return React.createElement(tag || 'span', Object.assign(Object.assign({}, htmlProps), { suppressHydrationWarning: true }), children);
54
+ return (React.createElement(Tag, Object.assign({}, htmlProps, { suppressHydrationWarning: true }), children));
58
55
  }
59
56
  else if (tag || !encode) {
60
- return React.createElement(tag || 'span', htmlProps, children);
57
+ return React.createElement(Tag, Object.assign({}, htmlProps), children);
61
58
  }
62
59
  else {
63
60
  return React.createElement(React.Fragment, null, children);
64
61
  }
65
- }, { defaultEmptyFieldEditingComponent: DefaultEmptyFieldEditingComponentText }));
62
+ };
63
+ /**
64
+ * The Text component.
65
+ * @public
66
+ */
67
+ export const Text = withFieldMetadata(withEmptyFieldEditingComponent(TextComponent, {
68
+ defaultEmptyFieldEditingComponent: DefaultEmptyFieldEditingComponentText,
69
+ }));
66
70
  Text.displayName = 'Text';
@@ -1,5 +1,11 @@
1
1
  import React from 'react';
2
2
  import { AppPlaceholder } from '../components/Placeholder/AppPlaceholder';
3
+ /**
4
+ * Provides a slot-like functionality by wrapping a component and rendering placeholders defined in the layout data.
5
+ * @param {ComponentType<T>} Component - The component to be wrapped around placeholders.
6
+ * @returns {React.ReactNode} A new component that renders the original component with placeholders.
7
+ * @public
8
+ */
3
9
  export const withAppPlaceholder = (Component) => {
4
10
  return (props) => {
5
11
  const placeholders = props.rendering.placeholders || {};
@@ -1,5 +1,6 @@
1
+ 'use client';
1
2
  import React from 'react';
2
- import { useSitecore } from './withSitecore';
3
+ import { useSitecore } from './../components/SitecoreProvider';
3
4
  export const DefaultEditingError = () => (React.createElement("div", { className: "sc-jss-editing-error", role: "alert" }, "Datasource is required. Please choose a content item for this component."));
4
5
  /**
5
6
  * Checks whether a Sitecore datasource is present and renders appropriately depending on page mode (normal vs editing).
@@ -11,7 +12,7 @@ export const DefaultEditingError = () => (React.createElement("div", { className
11
12
  */
12
13
  export function withDatasourceCheck(options) {
13
14
  return function withDatasourceCheckHoc(Component) {
14
- return function WithDatasourceCheck(props) {
15
+ return (props) => {
15
16
  var _a, _b;
16
17
  const { page } = useSitecore();
17
18
  const EditingError = (_a = options === null || options === void 0 ? void 0 : options.editingErrorComponent) !== null && _a !== void 0 ? _a : DefaultEditingError;
@@ -1,4 +1,5 @@
1
- import React from 'react';
1
+ 'use client';
2
+ import React, { useEffect, useRef } from 'react';
2
3
  import { resetEditorChromes } from '..';
3
4
  /**
4
5
  * HOC to inject editor chromes reset on component update.
@@ -6,17 +7,21 @@ import { resetEditorChromes } from '..';
6
7
  * @public
7
8
  */
8
9
  export const withEditorChromes = (WrappedComponent) => {
9
- class Enhancer extends React.Component {
10
- constructor() {
11
- super(...arguments);
12
- this.displayName = WrappedComponent.displayName || WrappedComponent.name || 'Component';
13
- }
14
- componentDidUpdate() {
10
+ const Enhancer = (props) => {
11
+ const isFirstRender = useRef(true);
12
+ useEffect(() => {
13
+ if (isFirstRender.current) {
14
+ isFirstRender.current = false;
15
+ return;
16
+ }
17
+ // only reset chromes on subsequent re-renders
15
18
  resetEditorChromes();
16
- }
17
- render() {
18
- return React.createElement(WrappedComponent, Object.assign({}, this.props));
19
- }
20
- }
19
+ });
20
+ return React.createElement(WrappedComponent, Object.assign({}, props));
21
+ };
22
+ Enhancer.displayName =
23
+ WrappedComponent.displayName ||
24
+ WrappedComponent.name ||
25
+ 'Component';
21
26
  return Enhancer;
22
27
  };