@sitecore-content-sdk/react 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +202 -0
- package/README.md +10 -0
- package/dist/cjs/ComponentBuilder.js +25 -0
- package/dist/cjs/components/BYOCComponent.js +132 -0
- package/dist/cjs/components/BYOCWrapper.js +21 -0
- package/dist/cjs/components/ComponentLibraryLayout.js +80 -0
- package/dist/cjs/components/Date.js +57 -0
- package/dist/cjs/components/DefaultEmptyFieldEditingComponents.js +22 -0
- package/dist/cjs/components/EditingScripts.js +27 -0
- package/dist/cjs/components/ErrorBoundary.js +95 -0
- package/dist/cjs/components/FEaaSComponent.js +161 -0
- package/dist/cjs/components/FEaaSWrapper.js +18 -0
- package/dist/cjs/components/FieldMetadata.js +34 -0
- package/dist/cjs/components/File.js +51 -0
- package/dist/cjs/components/HiddenRendering.js +18 -0
- package/dist/cjs/components/Image.js +83 -0
- package/dist/cjs/components/Link.js +106 -0
- package/dist/cjs/components/MissingComponent.js +34 -0
- package/dist/cjs/components/Placeholder.js +72 -0
- package/dist/cjs/components/PlaceholderCommon.js +191 -0
- package/dist/cjs/components/PlaceholderMetadata.js +63 -0
- package/dist/cjs/components/RichText.js +82 -0
- package/dist/cjs/components/SitecoreContext.js +67 -0
- package/dist/cjs/components/Text.js +78 -0
- package/dist/cjs/components/sharedTypes.js +2 -0
- package/dist/cjs/enhancers/withComponentFactory.js +26 -0
- package/dist/cjs/enhancers/withDatasourceCheck.js +28 -0
- package/dist/cjs/enhancers/withEditorChromes.js +24 -0
- package/dist/cjs/enhancers/withEmptyFieldEditingComponent.js +65 -0
- package/dist/cjs/enhancers/withFieldMetadata.js +69 -0
- package/dist/cjs/enhancers/withPlaceholder.js +63 -0
- package/dist/cjs/enhancers/withSitecoreContext.js +48 -0
- package/dist/cjs/index.js +78 -0
- package/dist/cjs/utils.js +38 -0
- package/dist/esm/ComponentBuilder.js +21 -0
- package/dist/esm/components/BYOCComponent.js +91 -0
- package/dist/esm/components/BYOCWrapper.js +14 -0
- package/dist/esm/components/ComponentLibraryLayout.js +43 -0
- package/dist/esm/components/Date.js +51 -0
- package/dist/esm/components/DefaultEmptyFieldEditingComponents.js +14 -0
- package/dist/esm/components/EditingScripts.js +20 -0
- package/dist/esm/components/ErrorBoundary.js +60 -0
- package/dist/esm/components/FEaaSComponent.js +119 -0
- package/dist/esm/components/FEaaSWrapper.js +11 -0
- package/dist/esm/components/FieldMetadata.js +27 -0
- package/dist/esm/components/File.js +44 -0
- package/dist/esm/components/HiddenRendering.js +11 -0
- package/dist/esm/components/Image.js +77 -0
- package/dist/esm/components/Link.js +67 -0
- package/dist/esm/components/MissingComponent.js +27 -0
- package/dist/esm/components/Placeholder.js +66 -0
- package/dist/esm/components/PlaceholderCommon.js +184 -0
- package/dist/esm/components/PlaceholderMetadata.js +56 -0
- package/dist/esm/components/RichText.js +43 -0
- package/dist/esm/components/SitecoreContext.js +60 -0
- package/dist/esm/components/Text.js +72 -0
- package/dist/esm/components/sharedTypes.js +1 -0
- package/dist/esm/enhancers/withComponentFactory.js +20 -0
- package/dist/esm/enhancers/withDatasourceCheck.js +20 -0
- package/dist/esm/enhancers/withEditorChromes.js +17 -0
- package/dist/esm/enhancers/withEmptyFieldEditingComponent.js +29 -0
- package/dist/esm/enhancers/withFieldMetadata.js +33 -0
- package/dist/esm/enhancers/withPlaceholder.js +57 -0
- package/dist/esm/enhancers/withSitecoreContext.js +41 -0
- package/dist/esm/index.js +28 -0
- package/dist/esm/utils.js +33 -0
- package/package.json +78 -0
- package/types/ComponentBuilder.d.ts +27 -0
- package/types/components/BYOCComponent.d.ts +93 -0
- package/types/components/BYOCWrapper.d.ts +6 -0
- package/types/components/ComponentLibraryLayout.d.ts +2 -0
- package/types/components/Date.d.ts +16 -0
- package/types/components/DefaultEmptyFieldEditingComponents.d.ts +3 -0
- package/types/components/EditingScripts.d.ts +4 -0
- package/types/components/ErrorBoundary.d.ts +17 -0
- package/types/components/FEaaSComponent.d.ts +71 -0
- package/types/components/FEaaSWrapper.d.ts +3 -0
- package/types/components/FieldMetadata.d.ts +22 -0
- package/types/components/File.d.ts +18 -0
- package/types/components/HiddenRendering.d.ts +2 -0
- package/types/components/Image.d.ts +48 -0
- package/types/components/Link.d.ts +38 -0
- package/types/components/MissingComponent.d.ts +8 -0
- package/types/components/Placeholder.d.ts +20 -0
- package/types/components/PlaceholderCommon.d.ts +98 -0
- package/types/components/PlaceholderMetadata.d.ts +28 -0
- package/types/components/RichText.d.ts +29 -0
- package/types/components/SitecoreContext.d.ts +42 -0
- package/types/components/Text.d.ts +20 -0
- package/types/components/sharedTypes.d.ts +28 -0
- package/types/enhancers/withComponentFactory.d.ts +12 -0
- package/types/enhancers/withDatasourceCheck.d.ts +21 -0
- package/types/enhancers/withEditorChromes.d.ts +2 -0
- package/types/enhancers/withEmptyFieldEditingComponent.d.ts +27 -0
- package/types/enhancers/withFieldMetadata.d.ts +16 -0
- package/types/enhancers/withPlaceholder.d.ts +34 -0
- package/types/enhancers/withSitecoreContext.d.ts +40 -0
- package/types/index.d.ts +30 -0
- package/types/utils.d.ts +17 -0
|
@@ -0,0 +1,184 @@
|
|
|
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
|
+
import React from 'react';
|
|
13
|
+
import PropTypes from 'prop-types';
|
|
14
|
+
import { MissingComponent } from './MissingComponent';
|
|
15
|
+
import { isDynamicPlaceholder, getDynamicPlaceholderPattern, } from '@sitecore-content-sdk/core/layout';
|
|
16
|
+
import { constants } from '@sitecore-content-sdk/core';
|
|
17
|
+
import { HiddenRendering } from './HiddenRendering';
|
|
18
|
+
import { FEaaSComponent, FEAAS_COMPONENT_RENDERING_NAME } from './FEaaSComponent';
|
|
19
|
+
import { FEaaSWrapper, FEAAS_WRAPPER_RENDERING_NAME } from './FEaaSWrapper';
|
|
20
|
+
import { BYOCComponent, BYOC_COMPONENT_RENDERING_NAME } from './BYOCComponent';
|
|
21
|
+
import { BYOCWrapper, BYOC_WRAPPER_RENDERING_NAME } from './BYOCWrapper';
|
|
22
|
+
import { PlaceholderMetadata } from './PlaceholderMetadata';
|
|
23
|
+
import ErrorBoundary from './ErrorBoundary';
|
|
24
|
+
export class PlaceholderCommon extends React.Component {
|
|
25
|
+
constructor(props) {
|
|
26
|
+
super(props);
|
|
27
|
+
this.state = {};
|
|
28
|
+
}
|
|
29
|
+
static getPlaceholderDataFromRenderingData(rendering, name, isEditing) {
|
|
30
|
+
let result;
|
|
31
|
+
let phName = name.slice();
|
|
32
|
+
/**
|
|
33
|
+
* Process (SXA) dynamic placeholders
|
|
34
|
+
* Find and replace the matching dynamic placeholder e.g 'nameOfContainer-{*}' with the requested e.g. 'nameOfContainer-1'.
|
|
35
|
+
* For Metadata EditMode, we need to keep the raw placeholder name in place.
|
|
36
|
+
*/
|
|
37
|
+
if (rendering === null || rendering === void 0 ? void 0 : rendering.placeholders) {
|
|
38
|
+
Object.keys(rendering.placeholders).forEach((placeholder) => {
|
|
39
|
+
const patternPlaceholder = isDynamicPlaceholder(placeholder)
|
|
40
|
+
? getDynamicPlaceholderPattern(placeholder)
|
|
41
|
+
: null;
|
|
42
|
+
if (patternPlaceholder && patternPlaceholder.test(phName)) {
|
|
43
|
+
if (isEditing) {
|
|
44
|
+
phName = placeholder;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
rendering.placeholders[phName] = rendering.placeholders[placeholder];
|
|
48
|
+
delete rendering.placeholders[placeholder];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (rendering && rendering.placeholders && Object.keys(rendering.placeholders).length > 0) {
|
|
54
|
+
result = rendering.placeholders[phName];
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
result = null;
|
|
58
|
+
}
|
|
59
|
+
if (!result) {
|
|
60
|
+
console.warn(`Placeholder '${phName}' was not found in the current rendering data`, JSON.stringify(rendering, null, 2));
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
componentDidCatch(error) {
|
|
66
|
+
this.setState({ error });
|
|
67
|
+
}
|
|
68
|
+
getSXAParams(rendering) {
|
|
69
|
+
if (!rendering.params)
|
|
70
|
+
return {};
|
|
71
|
+
return (rendering.params.FieldNames && {
|
|
72
|
+
styles: `${rendering.params.GridParameters || ''} ${rendering.params.Styles || ''}`,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
getComponentsForRenderingData(placeholderData) {
|
|
76
|
+
var _a;
|
|
77
|
+
const _b = this.props, { name, fields: placeholderFields, params: placeholderParams, missingComponentComponent, hiddenRenderingComponent } = _b, placeholderProps = __rest(_b, ["name", "fields", "params", "missingComponentComponent", "hiddenRenderingComponent"]);
|
|
78
|
+
const transformedComponents = placeholderData
|
|
79
|
+
.map((rendering, index) => {
|
|
80
|
+
var _a, _b;
|
|
81
|
+
const key = rendering.uid
|
|
82
|
+
? rendering.uid
|
|
83
|
+
: `component-${index}`;
|
|
84
|
+
const commonProps = { key };
|
|
85
|
+
let isEmpty = false;
|
|
86
|
+
const componentRendering = rendering;
|
|
87
|
+
let component;
|
|
88
|
+
if (componentRendering.componentName === constants.HIDDEN_RENDERING_NAME) {
|
|
89
|
+
component = hiddenRenderingComponent !== null && hiddenRenderingComponent !== void 0 ? hiddenRenderingComponent : HiddenRendering;
|
|
90
|
+
isEmpty = true;
|
|
91
|
+
}
|
|
92
|
+
else if (!componentRendering.componentName) {
|
|
93
|
+
component = () => React.createElement(React.Fragment, null);
|
|
94
|
+
isEmpty = true;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
component = this.getComponentForRendering(componentRendering);
|
|
98
|
+
}
|
|
99
|
+
// Fallback/defaults for Sitecore Component renderings (in case not defined in component factory)
|
|
100
|
+
if (!component) {
|
|
101
|
+
if (componentRendering.componentName === FEAAS_COMPONENT_RENDERING_NAME) {
|
|
102
|
+
component = FEaaSComponent;
|
|
103
|
+
}
|
|
104
|
+
else if (componentRendering.componentName === FEAAS_WRAPPER_RENDERING_NAME) {
|
|
105
|
+
component = FEaaSWrapper;
|
|
106
|
+
}
|
|
107
|
+
else if (componentRendering.componentName === BYOC_COMPONENT_RENDERING_NAME) {
|
|
108
|
+
component = BYOCComponent;
|
|
109
|
+
}
|
|
110
|
+
else if (componentRendering.componentName === BYOC_WRAPPER_RENDERING_NAME) {
|
|
111
|
+
component = BYOCWrapper;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (!component) {
|
|
115
|
+
console.error(`Placeholder ${name} contains unknown component ${componentRendering.componentName}. Ensure that a React component exists for it, and that it is registered in your componentFactory.js.`);
|
|
116
|
+
component = missingComponentComponent !== null && missingComponentComponent !== void 0 ? missingComponentComponent : MissingComponent;
|
|
117
|
+
isEmpty = true;
|
|
118
|
+
}
|
|
119
|
+
const finalProps = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, commonProps), placeholderProps), ((placeholderFields || componentRendering.fields) && {
|
|
120
|
+
fields: Object.assign(Object.assign({}, placeholderFields), componentRendering.fields),
|
|
121
|
+
})), ((placeholderParams || componentRendering.params) && {
|
|
122
|
+
params: Object.assign(Object.assign(Object.assign({}, placeholderParams), componentRendering.params), this.getSXAParams(componentRendering)),
|
|
123
|
+
})), { rendering: componentRendering });
|
|
124
|
+
let rendered = React.createElement(component, this.props.modifyComponentProps ? this.props.modifyComponentProps(finalProps) : finalProps);
|
|
125
|
+
if (!isEmpty) {
|
|
126
|
+
// assign type based on passed element - type='text/sitecore' should be ignored when renderEach Placeholder prop function is being used
|
|
127
|
+
const type = rendered.props.type === 'text/sitecore' ? rendered.props.type : '';
|
|
128
|
+
// wrapping with error boundary could cause problems in case where parent component uses withPlaceholder HOC and tries to access its children props
|
|
129
|
+
// that's why we need to expose element's props here
|
|
130
|
+
rendered = (React.createElement(ErrorBoundary, Object.assign({ key: rendered.type + '-' + index, errorComponent: this.props.errorComponent, componentLoadingMessage: this.props.componentLoadingMessage, type: type, isDynamic: ((_a = component.render) === null || _a === void 0 ? void 0 : _a.preload) ? true : false }, rendered.props), rendered));
|
|
131
|
+
}
|
|
132
|
+
// if in edit mode then emit shallow chromes for hydration in Pages
|
|
133
|
+
if ((_b = this.props.sitecoreContext) === null || _b === void 0 ? void 0 : _b.pageEditing) {
|
|
134
|
+
return (React.createElement(PlaceholderMetadata, { key: key, rendering: rendering }, rendered));
|
|
135
|
+
}
|
|
136
|
+
return rendered;
|
|
137
|
+
})
|
|
138
|
+
.filter((element) => element); // remove nulls
|
|
139
|
+
if ((_a = this.props.sitecoreContext) === null || _a === void 0 ? void 0 : _a.pageEditing) {
|
|
140
|
+
return [
|
|
141
|
+
React.createElement(PlaceholderMetadata, { key: this.props.rendering.uid, placeholderName: name, rendering: this.props.rendering }, transformedComponents),
|
|
142
|
+
];
|
|
143
|
+
}
|
|
144
|
+
return transformedComponents;
|
|
145
|
+
}
|
|
146
|
+
getComponentForRendering(renderingDefinition) {
|
|
147
|
+
var _a;
|
|
148
|
+
const componentFactory = this.props.componentFactory;
|
|
149
|
+
if (!componentFactory || typeof componentFactory !== 'function') {
|
|
150
|
+
console.warn(`No componentFactory was available to service request for component ${renderingDefinition}`);
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
// Render SXA Rendering Variant
|
|
154
|
+
if ((_a = renderingDefinition.params) === null || _a === void 0 ? void 0 : _a.FieldNames) {
|
|
155
|
+
return componentFactory(renderingDefinition.componentName, renderingDefinition.params.FieldNames);
|
|
156
|
+
}
|
|
157
|
+
return componentFactory(renderingDefinition.componentName);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
PlaceholderCommon.propTypes = {
|
|
161
|
+
rendering: PropTypes.oneOfType([
|
|
162
|
+
PropTypes.object,
|
|
163
|
+
PropTypes.object,
|
|
164
|
+
]).isRequired,
|
|
165
|
+
fields: PropTypes.objectOf(PropTypes.oneOfType([
|
|
166
|
+
PropTypes.object,
|
|
167
|
+
PropTypes.object,
|
|
168
|
+
]).isRequired),
|
|
169
|
+
params: PropTypes.objectOf(PropTypes.string.isRequired),
|
|
170
|
+
missingComponentComponent: PropTypes.oneOfType([
|
|
171
|
+
PropTypes.object,
|
|
172
|
+
PropTypes.func,
|
|
173
|
+
]),
|
|
174
|
+
hiddenRenderingComponent: PropTypes.oneOfType([
|
|
175
|
+
PropTypes.object,
|
|
176
|
+
PropTypes.func,
|
|
177
|
+
]),
|
|
178
|
+
errorComponent: PropTypes.oneOfType([
|
|
179
|
+
PropTypes.object,
|
|
180
|
+
PropTypes.func,
|
|
181
|
+
]),
|
|
182
|
+
modifyComponentProps: PropTypes.func,
|
|
183
|
+
sitecoreContext: PropTypes.object,
|
|
184
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { getDynamicPlaceholderPattern, isDynamicPlaceholder, } from '@sitecore-content-sdk/core/layout';
|
|
3
|
+
import { MetadataKind, DEFAULT_PLACEHOLDER_UID } from '@sitecore-content-sdk/core/editing';
|
|
4
|
+
/**
|
|
5
|
+
* A React component to generate metadata blocks for a placeholder or rendering.
|
|
6
|
+
* It utilizes dynamic attributes based on whether the component acts as a placeholder
|
|
7
|
+
* or as a rendering to properly render the surrounding code blocks.
|
|
8
|
+
* @param {object} props The properties passed to the component.
|
|
9
|
+
* @param {ComponentRendering} props.rendering The rendering data.
|
|
10
|
+
* @param {string} [props.placeholderName] The name of the placeholder.
|
|
11
|
+
* @param {JSX.Element} props.children The child components or elements to be wrapped by the metadata code blocks.
|
|
12
|
+
* @returns {JSX.Element} A React fragment containing open and close code blocks surrounding the children elements.
|
|
13
|
+
*/
|
|
14
|
+
export const PlaceholderMetadata = ({ rendering, placeholderName, children, }) => {
|
|
15
|
+
const getCodeBlockAttributes = (kind, id, placeholderName) => {
|
|
16
|
+
const chrometype = placeholderName ? 'placeholder' : 'rendering';
|
|
17
|
+
const attributes = {
|
|
18
|
+
type: 'text/sitecore',
|
|
19
|
+
chrometype: chrometype,
|
|
20
|
+
className: 'scpm',
|
|
21
|
+
kind: kind,
|
|
22
|
+
};
|
|
23
|
+
if (kind === MetadataKind.Open) {
|
|
24
|
+
if (chrometype === 'placeholder' && placeholderName) {
|
|
25
|
+
let phId = '';
|
|
26
|
+
for (const placeholder of Object.keys(rendering.placeholders)) {
|
|
27
|
+
if (placeholderName === placeholder) {
|
|
28
|
+
phId = id
|
|
29
|
+
? `${placeholderName}_${id}`
|
|
30
|
+
: `${placeholderName}_${DEFAULT_PLACEHOLDER_UID}`;
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
// Check if the placeholder is a dynamic placeholder
|
|
34
|
+
if (isDynamicPlaceholder(placeholder)) {
|
|
35
|
+
const pattern = getDynamicPlaceholderPattern(placeholder);
|
|
36
|
+
// Check if the placeholder matches the dynamic placeholder pattern
|
|
37
|
+
if (pattern.test(placeholderName)) {
|
|
38
|
+
phId = id ? `${placeholder}_${id}` : `${placeholder}_${DEFAULT_PLACEHOLDER_UID}`;
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
attributes.id = phId;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
attributes.id = id;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return attributes;
|
|
50
|
+
};
|
|
51
|
+
const renderComponent = (uid, placeholderName) => (React.createElement(React.Fragment, null,
|
|
52
|
+
React.createElement("code", Object.assign({}, getCodeBlockAttributes(MetadataKind.Open, uid, placeholderName))),
|
|
53
|
+
children,
|
|
54
|
+
React.createElement("code", Object.assign({}, getCodeBlockAttributes(MetadataKind.Close, uid, placeholderName)))));
|
|
55
|
+
return React.createElement(React.Fragment, null, renderComponent(rendering.uid, placeholderName));
|
|
56
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
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
|
+
import React, { forwardRef } from 'react';
|
|
13
|
+
import PropTypes from 'prop-types';
|
|
14
|
+
import { withFieldMetadata } from '../enhancers/withFieldMetadata';
|
|
15
|
+
import { withEmptyFieldEditingComponent } from '../enhancers/withEmptyFieldEditingComponent';
|
|
16
|
+
import { DefaultEmptyFieldEditingComponentText } from './DefaultEmptyFieldEditingComponents';
|
|
17
|
+
import { isFieldValueEmpty } from '@sitecore-content-sdk/core/layout';
|
|
18
|
+
export const RichText = withFieldMetadata(withEmptyFieldEditingComponent(
|
|
19
|
+
// eslint-disable-next-line react/display-name
|
|
20
|
+
forwardRef((_a, ref) => {
|
|
21
|
+
var { field, tag = 'div', editable = true } = _a, otherProps = __rest(_a, ["field", "tag", "editable"]);
|
|
22
|
+
if (isFieldValueEmpty(field)) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const htmlProps = Object.assign({ dangerouslySetInnerHTML: {
|
|
26
|
+
__html: field.value,
|
|
27
|
+
}, ref }, otherProps);
|
|
28
|
+
return React.createElement(tag || 'div', htmlProps);
|
|
29
|
+
}), { defaultEmptyFieldEditingComponent: DefaultEmptyFieldEditingComponentText, isForwardRef: true }), true);
|
|
30
|
+
export const RichTextPropTypes = {
|
|
31
|
+
field: PropTypes.shape({
|
|
32
|
+
value: PropTypes.string,
|
|
33
|
+
metadata: PropTypes.objectOf(PropTypes.any),
|
|
34
|
+
}),
|
|
35
|
+
tag: PropTypes.string,
|
|
36
|
+
editable: PropTypes.bool,
|
|
37
|
+
emptyFieldEditingComponent: PropTypes.oneOfType([
|
|
38
|
+
PropTypes.object,
|
|
39
|
+
PropTypes.func,
|
|
40
|
+
]),
|
|
41
|
+
};
|
|
42
|
+
RichText.propTypes = RichTextPropTypes;
|
|
43
|
+
RichText.displayName = 'RichText';
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import fastDeepEqual from 'fast-deep-equal/es6/react';
|
|
5
|
+
export const SitecoreContextReactContext = React.createContext({});
|
|
6
|
+
export const ComponentFactoryReactContext = React.createContext({});
|
|
7
|
+
export class SitecoreContext extends React.Component {
|
|
8
|
+
constructor(props) {
|
|
9
|
+
super(props);
|
|
10
|
+
/**
|
|
11
|
+
* Update context state. Value can be @type {LayoutServiceData} which will be automatically transformed
|
|
12
|
+
* or you can provide exact @type {SitecoreContextValue}
|
|
13
|
+
* @param {SitecoreContextValue | LayoutServiceData} value New context value
|
|
14
|
+
*/
|
|
15
|
+
this.setContext = (value) => {
|
|
16
|
+
this.setState({
|
|
17
|
+
context: value.sitecore
|
|
18
|
+
? this.constructContext(value)
|
|
19
|
+
: Object.assign({}, value),
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
const context = this.constructContext(props.layoutData);
|
|
23
|
+
this.state = {
|
|
24
|
+
context,
|
|
25
|
+
setContext: this.setContext,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
constructContext(layoutData) {
|
|
29
|
+
var _a;
|
|
30
|
+
if (!layoutData) {
|
|
31
|
+
return {
|
|
32
|
+
pageEditing: false,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return Object.assign({ route: layoutData.sitecore.route, itemId: (_a = layoutData.sitecore.route) === null || _a === void 0 ? void 0 : _a.itemId }, layoutData.sitecore.context);
|
|
36
|
+
}
|
|
37
|
+
componentDidUpdate(prevProps) {
|
|
38
|
+
// In case if somebody will manage SitecoreContext state by passing fresh `layoutData` prop
|
|
39
|
+
// instead of using `updateSitecoreContext`
|
|
40
|
+
if (!fastDeepEqual(prevProps.layoutData, this.props.layoutData)) {
|
|
41
|
+
this.setContext(this.props.layoutData);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
render() {
|
|
46
|
+
return (React.createElement(ComponentFactoryReactContext.Provider, { value: this.props.componentFactory },
|
|
47
|
+
React.createElement(SitecoreContextReactContext.Provider, { value: this.state }, this.props.children)));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
SitecoreContext.propTypes = {
|
|
51
|
+
children: PropTypes.any.isRequired,
|
|
52
|
+
componentFactory: PropTypes.func,
|
|
53
|
+
layoutData: PropTypes.shape({
|
|
54
|
+
sitecore: PropTypes.shape({
|
|
55
|
+
context: PropTypes.any,
|
|
56
|
+
route: PropTypes.any,
|
|
57
|
+
}),
|
|
58
|
+
}),
|
|
59
|
+
};
|
|
60
|
+
SitecoreContext.displayName = 'SitecoreContext';
|
|
@@ -0,0 +1,72 @@
|
|
|
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
|
+
import React from 'react';
|
|
13
|
+
import { withFieldMetadata } from '../enhancers/withFieldMetadata';
|
|
14
|
+
import { withEmptyFieldEditingComponent } from '../enhancers/withEmptyFieldEditingComponent';
|
|
15
|
+
import { DefaultEmptyFieldEditingComponentText } from './DefaultEmptyFieldEditingComponents';
|
|
16
|
+
import PropTypes from 'prop-types';
|
|
17
|
+
import { isFieldValueEmpty } from '@sitecore-content-sdk/core/layout';
|
|
18
|
+
export const Text = withFieldMetadata(withEmptyFieldEditingComponent((_a) => {
|
|
19
|
+
var { field, tag, editable = true, encode = true } = _a, otherProps = __rest(_a, ["field", "tag", "editable", "encode"]);
|
|
20
|
+
if (isFieldValueEmpty(field)) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
// can't use editable value if we want to output unencoded
|
|
24
|
+
if (!encode) {
|
|
25
|
+
// eslint-disable-next-line no-param-reassign
|
|
26
|
+
editable = false;
|
|
27
|
+
}
|
|
28
|
+
let output = field.value === undefined ? '' : field.value;
|
|
29
|
+
// when string value isn't formatted, we should format line breaks
|
|
30
|
+
const splitted = String(output).split('\n');
|
|
31
|
+
if (splitted.length) {
|
|
32
|
+
const formatted = [];
|
|
33
|
+
splitted.forEach((str, i) => {
|
|
34
|
+
const isLast = i === splitted.length - 1;
|
|
35
|
+
formatted.push(str);
|
|
36
|
+
if (!isLast) {
|
|
37
|
+
formatted.push(React.createElement("br", { key: i }));
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
output = formatted;
|
|
41
|
+
}
|
|
42
|
+
let children = null;
|
|
43
|
+
const htmlProps = Object.assign({}, otherProps);
|
|
44
|
+
if (!encode) {
|
|
45
|
+
htmlProps.dangerouslySetInnerHTML = {
|
|
46
|
+
__html: output,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
children = output;
|
|
51
|
+
}
|
|
52
|
+
if (tag || !encode) {
|
|
53
|
+
return React.createElement(tag || 'span', htmlProps, children);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
return React.createElement(React.Fragment, null, children);
|
|
57
|
+
}
|
|
58
|
+
}, { defaultEmptyFieldEditingComponent: DefaultEmptyFieldEditingComponentText }));
|
|
59
|
+
Text.propTypes = {
|
|
60
|
+
field: PropTypes.shape({
|
|
61
|
+
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
62
|
+
metadata: PropTypes.objectOf(PropTypes.any),
|
|
63
|
+
}),
|
|
64
|
+
tag: PropTypes.string,
|
|
65
|
+
editable: PropTypes.bool,
|
|
66
|
+
encode: PropTypes.bool,
|
|
67
|
+
emptyFieldEditingComponent: PropTypes.oneOfType([
|
|
68
|
+
PropTypes.object,
|
|
69
|
+
PropTypes.func,
|
|
70
|
+
]),
|
|
71
|
+
};
|
|
72
|
+
Text.displayName = 'Text';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ComponentFactoryReactContext } from '../components/SitecoreContext';
|
|
3
|
+
import { useContext } from 'react';
|
|
4
|
+
/**
|
|
5
|
+
* @param {React.ComponentClass<T> | React.FC<T>} Component
|
|
6
|
+
*/
|
|
7
|
+
export function withComponentFactory(Component) {
|
|
8
|
+
/**
|
|
9
|
+
* @param {T} props - props to pass to the wrapped component
|
|
10
|
+
* @returns {JSX.Element} - the rendered component
|
|
11
|
+
*/
|
|
12
|
+
function WithComponentFactory(props) {
|
|
13
|
+
const context = useContext(ComponentFactoryReactContext);
|
|
14
|
+
return React.createElement(Component, Object.assign({}, props, { componentFactory: props.componentFactory || context }));
|
|
15
|
+
}
|
|
16
|
+
WithComponentFactory.displayName = `withComponentFactory(${Component.displayName ||
|
|
17
|
+
Component.name ||
|
|
18
|
+
'Anonymous'})`;
|
|
19
|
+
return WithComponentFactory;
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useSitecoreContext } from './withSitecoreContext';
|
|
3
|
+
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
|
+
* Checks whether a Sitecore datasource is present and renders appropriately depending on page mode (normal vs editing).
|
|
6
|
+
* @param {WithDatasourceCheckOptions} [options]
|
|
7
|
+
* @returns
|
|
8
|
+
* The wrapped component, if a datasource is present.
|
|
9
|
+
* A null component (in normal mode) or an error component (in editing mode), if a datasource is not present.
|
|
10
|
+
*/
|
|
11
|
+
export function withDatasourceCheck(options) {
|
|
12
|
+
return function withDatasourceCheckHoc(Component) {
|
|
13
|
+
return function WithDatasourceCheck(props) {
|
|
14
|
+
var _a, _b;
|
|
15
|
+
const { sitecoreContext } = useSitecoreContext();
|
|
16
|
+
const EditingError = (_a = options === null || options === void 0 ? void 0 : options.editingErrorComponent) !== null && _a !== void 0 ? _a : DefaultEditingError;
|
|
17
|
+
return ((_b = props.rendering) === null || _b === void 0 ? void 0 : _b.dataSource) ? (React.createElement(Component, Object.assign({}, props))) : sitecoreContext.pageEditing ? (React.createElement(EditingError, null)) : null;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { resetEditorChromes } from '..';
|
|
3
|
+
export const withEditorChromes = (WrappedComponent) => {
|
|
4
|
+
class Enhancer extends React.Component {
|
|
5
|
+
constructor() {
|
|
6
|
+
super(...arguments);
|
|
7
|
+
this.displayName = WrappedComponent.displayName || WrappedComponent.name || 'Component';
|
|
8
|
+
}
|
|
9
|
+
componentDidUpdate() {
|
|
10
|
+
resetEditorChromes();
|
|
11
|
+
}
|
|
12
|
+
render() {
|
|
13
|
+
return React.createElement(WrappedComponent, Object.assign({}, this.props));
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return Enhancer;
|
|
17
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
import { isFieldValueEmpty, } from '@sitecore-content-sdk/core/layout';
|
|
3
|
+
/**
|
|
4
|
+
* Returns the passed field component or default component in case field value is empty and edit mode is 'metadata'
|
|
5
|
+
* @param {ComponentType<FieldComponentProps>} FieldComponent the field component
|
|
6
|
+
* @param {WithEmptyFieldEditingComponentProps} options the options of the HOC;
|
|
7
|
+
*/
|
|
8
|
+
export function withEmptyFieldEditingComponent(FieldComponent, options) {
|
|
9
|
+
const getEmptyFieldEditingComponent = (props) => {
|
|
10
|
+
var _a;
|
|
11
|
+
const { editable = true } = props;
|
|
12
|
+
if (((_a = props.field) === null || _a === void 0 ? void 0 : _a.metadata) && editable && isFieldValueEmpty(props.field)) {
|
|
13
|
+
return props.emptyFieldEditingComponent || options.defaultEmptyFieldEditingComponent;
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
};
|
|
17
|
+
if (options.isForwardRef) {
|
|
18
|
+
// eslint-disable-next-line react/display-name
|
|
19
|
+
return forwardRef((props, ref) => {
|
|
20
|
+
const EmptyFieldEditingComponent = getEmptyFieldEditingComponent(props);
|
|
21
|
+
return (React.createElement(React.Fragment, null, (EmptyFieldEditingComponent && React.createElement(EmptyFieldEditingComponent, null)) || (React.createElement(FieldComponent, Object.assign({}, props, { ref: ref })))));
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
// eslint-disable-next-line react/display-name
|
|
25
|
+
return (props) => {
|
|
26
|
+
const EmptyFieldEditingComponent = getEmptyFieldEditingComponent(props);
|
|
27
|
+
return (React.createElement(React.Fragment, null, (EmptyFieldEditingComponent && React.createElement(EmptyFieldEditingComponent, null)) || (React.createElement(FieldComponent, Object.assign({}, props)))));
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
import { FieldMetadata } from '../components/FieldMetadata';
|
|
3
|
+
/**
|
|
4
|
+
* Wraps the field component with metadata markup intended to be used for chromes hydration in Pages
|
|
5
|
+
* @param {ComponentType<FieldComponentProps>} FieldComponent the field component
|
|
6
|
+
* @param {boolean} isForwardRef set to 'true' if forward reference is needed
|
|
7
|
+
*/
|
|
8
|
+
export function withFieldMetadata(FieldComponent, isForwardRef = false) {
|
|
9
|
+
if (isForwardRef) {
|
|
10
|
+
// eslint-disable-next-line react/display-name
|
|
11
|
+
return forwardRef((props, ref) => {
|
|
12
|
+
var _a;
|
|
13
|
+
const { editable = true } = props;
|
|
14
|
+
const metadata = (_a = props.field) === null || _a === void 0 ? void 0 : _a.metadata;
|
|
15
|
+
if (!metadata || !editable) {
|
|
16
|
+
return React.createElement(FieldComponent, Object.assign({}, props, { ref: ref }));
|
|
17
|
+
}
|
|
18
|
+
return (React.createElement(FieldMetadata, { metadata: metadata },
|
|
19
|
+
React.createElement(FieldComponent, Object.assign({}, props, { ref: ref }))));
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
// eslint-disable-next-line react/display-name
|
|
23
|
+
return (props) => {
|
|
24
|
+
var _a;
|
|
25
|
+
const { editable = true } = props;
|
|
26
|
+
const metadata = (_a = props.field) === null || _a === void 0 ? void 0 : _a.metadata;
|
|
27
|
+
if (!metadata || !editable) {
|
|
28
|
+
return React.createElement(FieldComponent, Object.assign({}, props));
|
|
29
|
+
}
|
|
30
|
+
return (React.createElement(FieldMetadata, { metadata: metadata },
|
|
31
|
+
React.createElement(FieldComponent, Object.assign({}, props))));
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { PlaceholderCommon } from '../components/PlaceholderCommon';
|
|
3
|
+
import { withComponentFactory } from './withComponentFactory';
|
|
4
|
+
import { withSitecoreContext } from './withSitecoreContext';
|
|
5
|
+
/**
|
|
6
|
+
* @param {WithPlaceholderSpec} placeholders
|
|
7
|
+
* @param {WithPlaceholderOptions} [options]
|
|
8
|
+
*/
|
|
9
|
+
export function withPlaceholder(placeholders, options) {
|
|
10
|
+
return (WrappedComponent) => {
|
|
11
|
+
class WithPlaceholder extends PlaceholderCommon {
|
|
12
|
+
constructor(props) {
|
|
13
|
+
super(props);
|
|
14
|
+
}
|
|
15
|
+
render() {
|
|
16
|
+
let childProps = Object.assign({}, this.props);
|
|
17
|
+
delete childProps.componentFactory;
|
|
18
|
+
if (options && options.propsTransformer) {
|
|
19
|
+
childProps = options.propsTransformer(childProps);
|
|
20
|
+
}
|
|
21
|
+
if (this.state.error) {
|
|
22
|
+
if (childProps.errorComponent) {
|
|
23
|
+
return React.createElement(childProps.errorComponent, { error: this.state.error });
|
|
24
|
+
}
|
|
25
|
+
return (React.createElement("div", { className: "sc-jss-placeholder-error" },
|
|
26
|
+
"A rendering error occurred: ",
|
|
27
|
+
this.state.error.message,
|
|
28
|
+
"."));
|
|
29
|
+
}
|
|
30
|
+
const renderingData = options && options.resolvePlaceholderDataFromProps
|
|
31
|
+
? options.resolvePlaceholderDataFromProps(childProps)
|
|
32
|
+
: childProps.rendering;
|
|
33
|
+
const definitelyArrayPlacholders = !Array.isArray(placeholders)
|
|
34
|
+
? [placeholders]
|
|
35
|
+
: placeholders;
|
|
36
|
+
definitelyArrayPlacholders.forEach((placeholder) => {
|
|
37
|
+
let placeholderData;
|
|
38
|
+
if (typeof placeholder !== 'string' && placeholder.placeholder && placeholder.prop) {
|
|
39
|
+
placeholderData = PlaceholderCommon.getPlaceholderDataFromRenderingData(renderingData, placeholder.placeholder, childProps.sitecoreContext.pageEditing);
|
|
40
|
+
if (placeholderData) {
|
|
41
|
+
childProps[placeholder.prop] = this.getComponentsForRenderingData(placeholderData);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
placeholderData = PlaceholderCommon.getPlaceholderDataFromRenderingData(renderingData, placeholder, childProps.sitecoreContext.pageEditing);
|
|
46
|
+
if (placeholderData) {
|
|
47
|
+
childProps[placeholder] = this.getComponentsForRenderingData(placeholderData);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
return React.createElement(WrappedComponent, Object.assign({}, childProps));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
WithPlaceholder.propTypes = PlaceholderCommon.propTypes;
|
|
55
|
+
return withSitecoreContext()(withComponentFactory(WithPlaceholder));
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { SitecoreContextReactContext } from '../components/SitecoreContext';
|
|
3
|
+
/**
|
|
4
|
+
* @param {WithSitecoreContextOptions} [options]
|
|
5
|
+
*/
|
|
6
|
+
export function withSitecoreContext(options) {
|
|
7
|
+
return function withSitecoreContextHoc(Component) {
|
|
8
|
+
return function WithSitecoreContext(props) {
|
|
9
|
+
return (React.createElement(SitecoreContextReactContext.Consumer, null, (context) => (React.createElement(Component, Object.assign({}, props, { sitecoreContext: context.context, updateSitecoreContext: options && options.updatable && context.setContext })))));
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* This hook grants acсess to the current Sitecore page context
|
|
15
|
+
* by default JSS includes the following properties in this context:
|
|
16
|
+
* - pageEditing - Provided by Layout Service, a boolean indicating whether the route is being accessed via the Sitecore Editor.
|
|
17
|
+
* - pageState - Like pageEditing, but a string: normal, preview or edit.
|
|
18
|
+
* - site - Provided by Layout Service, an object containing the name of the current Sitecore site context.
|
|
19
|
+
* @see https://jss.sitecore.com/docs/techniques/extending-layout-service/layoutservice-extending-context
|
|
20
|
+
* @param {WithSitecoreContextOptions} [options] hook options
|
|
21
|
+
* @example
|
|
22
|
+
* const EditMode = () => {
|
|
23
|
+
* const { sitecoreContext } = useSitecoreContext();
|
|
24
|
+
* return <span>Edit Mode is {sitecoreContext.pageEditing ? 'active' : 'inactive'}</span>
|
|
25
|
+
* }
|
|
26
|
+
* @example
|
|
27
|
+
* const EditMode = () => {
|
|
28
|
+
* const { sitecoreContext, updateSitecoreContext } = useSitecoreContext({ updatable: true });
|
|
29
|
+
* const onClick = () => updateSitecoreContext({ pageEditing: true });
|
|
30
|
+
* return <span onClick={onClick}>Edit Mode is {sitecoreContext.pageEditing ? 'active' : 'inactive'}</span>
|
|
31
|
+
* }
|
|
32
|
+
* @returns {object} { sitecoreContext, updateSitecoreContext }
|
|
33
|
+
*/
|
|
34
|
+
export function useSitecoreContext(options) {
|
|
35
|
+
const reactContext = React.useContext(SitecoreContextReactContext);
|
|
36
|
+
const updatable = options === null || options === void 0 ? void 0 : options.updatable;
|
|
37
|
+
return {
|
|
38
|
+
sitecoreContext: reactContext.context,
|
|
39
|
+
updateSitecoreContext: updatable ? reactContext.setContext : undefined,
|
|
40
|
+
};
|
|
41
|
+
}
|