@unlayer/react-elements 0.1.17 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -0
- package/dist/index.cjs +118 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +107 -1
- package/dist/index.d.ts +107 -1
- package/dist/index.js +117 -33
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -118,6 +118,8 @@ interface MenuItem {
|
|
|
118
118
|
type RenderMode = "web" | "email" | "document";
|
|
119
119
|
interface DesignContent {
|
|
120
120
|
type: string;
|
|
121
|
+
/** Custom tool name — present when `type` is "custom" */
|
|
122
|
+
slug?: string;
|
|
121
123
|
values: Record<string, any>;
|
|
122
124
|
}
|
|
123
125
|
interface DesignColumn {
|
|
@@ -312,6 +314,10 @@ interface BaseItemComponentProps {
|
|
|
312
314
|
* string ("10px", "16px 24px"). Applied by the containing Column. */
|
|
313
315
|
containerPadding?: SizeInput;
|
|
314
316
|
index?: number;
|
|
317
|
+
/** @internal — content id allocated by the containing Column; keeps the
|
|
318
|
+
* value-level _meta.htmlID unique per instance and aligned with the
|
|
319
|
+
* wrapper id and head/json numbering. */
|
|
320
|
+
_metaHtmlId?: string;
|
|
315
321
|
colIndex?: number;
|
|
316
322
|
cells?: any[];
|
|
317
323
|
bodyValues?: any;
|
|
@@ -1001,4 +1007,104 @@ declare function renderToHtmlParts(element: React__default.ReactElement, config?
|
|
|
1001
1007
|
declare function renderRowToJson(element: React__default.ReactElement): DesignRow;
|
|
1002
1008
|
declare function renderToJson(element: React__default.ReactElement): DesignJSON;
|
|
1003
1009
|
|
|
1004
|
-
|
|
1010
|
+
/**
|
|
1011
|
+
* registerTool — render the editor's custom tools from code.
|
|
1012
|
+
*
|
|
1013
|
+
* Accepts the same tool definition object embedders already write for
|
|
1014
|
+
* `unlayer.registerTool` (see docs.unlayer.com › Custom Tools) and returns a
|
|
1015
|
+
* React component. One definition powers both runtimes:
|
|
1016
|
+
*
|
|
1017
|
+
* - The Builder uses `label`/`icon`/`options`/`renderer.Viewer`/`validator`
|
|
1018
|
+
* for its panel, canvas, and audit — Elements ignores those.
|
|
1019
|
+
* - Elements calls `renderer.exporters[mode](values)` to produce HTML and
|
|
1020
|
+
* collects `renderer.head` css/js like any built-in component, so code
|
|
1021
|
+
* output matches editor exports by construction — it is the same function.
|
|
1022
|
+
* - `renderToJson` emits `{ type: "custom", slug: <name>, values }`, the
|
|
1023
|
+
* shape the editor stores, so designs round-trip into the Builder where
|
|
1024
|
+
* the same tool is registered.
|
|
1025
|
+
*
|
|
1026
|
+
* Sanitization: like editor exports, the tool's HTML output is passed
|
|
1027
|
+
* through `toSafeHtml` when one is configured. The default config has NO
|
|
1028
|
+
* sanitizer — with untrusted values, configure `toSafeHtml` (same caveat
|
|
1029
|
+
* as the `<Html>` component).
|
|
1030
|
+
*/
|
|
1031
|
+
|
|
1032
|
+
/** Exporter: `(values) => html`. Extra args mirror built-in exporters and can be ignored. */
|
|
1033
|
+
type ToolExporter = (values: any, ...args: any[]) => string;
|
|
1034
|
+
/** One property in an options group — Elements reads only the default. */
|
|
1035
|
+
interface ToolOptionConfig {
|
|
1036
|
+
defaultValue?: unknown;
|
|
1037
|
+
/** Disabled options contribute no default */
|
|
1038
|
+
enabled?: boolean;
|
|
1039
|
+
[editorOnly: string]: unknown;
|
|
1040
|
+
}
|
|
1041
|
+
/** A property group: `options.<group>.options.<property>` */
|
|
1042
|
+
interface ToolOptionGroup {
|
|
1043
|
+
options?: Record<string, ToolOptionConfig | undefined>;
|
|
1044
|
+
[editorOnly: string]: unknown;
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* The subset of the `unlayer.registerTool` config Elements reads. Extra
|
|
1048
|
+
* editor-only fields (label, icon, Viewer, validator, transformer, ...)
|
|
1049
|
+
* are accepted and ignored, so the same object can be passed to both APIs.
|
|
1050
|
+
*/
|
|
1051
|
+
interface ElementsToolConfig {
|
|
1052
|
+
/** Unique tool name — becomes the design JSON `slug` */
|
|
1053
|
+
name: string;
|
|
1054
|
+
/** Initial values for the tool's properties (win over option defaults) */
|
|
1055
|
+
values?: Record<string, any>;
|
|
1056
|
+
/**
|
|
1057
|
+
* Property groups. Real-world tools usually define their defaults here,
|
|
1058
|
+
* as each property's `defaultValue` — those seed the initial values,
|
|
1059
|
+
* with tool-level `values` taking precedence (the editor's order).
|
|
1060
|
+
*/
|
|
1061
|
+
options?: Record<string, ToolOptionGroup | undefined>;
|
|
1062
|
+
renderer: {
|
|
1063
|
+
/** Per-mode HTML exporters. `document` falls back to `web`. */
|
|
1064
|
+
exporters: Partial<Record<RenderMode, ToolExporter>>;
|
|
1065
|
+
/** Optional head contributions collected into the document `<head>` */
|
|
1066
|
+
head?: {
|
|
1067
|
+
css?: (values: any, ...args: any[]) => string | undefined;
|
|
1068
|
+
js?: (values: any, ...args: any[]) => string | undefined;
|
|
1069
|
+
tags?: (values: any, ...args: any[]) => string[] | undefined;
|
|
1070
|
+
};
|
|
1071
|
+
/** Editor-only canvas renderer — ignored by Elements */
|
|
1072
|
+
Viewer?: unknown;
|
|
1073
|
+
};
|
|
1074
|
+
/** React DevTools name (defaults to the tool name) */
|
|
1075
|
+
displayName?: string;
|
|
1076
|
+
/** Editor-only fields (label, icon, options, validator, ...) — ignored */
|
|
1077
|
+
[editorOnly: string]: unknown;
|
|
1078
|
+
}
|
|
1079
|
+
/** Props: the tool's values as flat props, plus a `values` escape hatch. */
|
|
1080
|
+
type ElementsToolProps = BaseItemComponentProps & Record<string, any> & {
|
|
1081
|
+
values?: Record<string, any>;
|
|
1082
|
+
};
|
|
1083
|
+
/**
|
|
1084
|
+
* Create a React component from a custom tool definition — a custom tool
|
|
1085
|
+
* is the custom component you create: the same term (and the same config)
|
|
1086
|
+
* as the editor's `unlayer.registerTool`, so one definition serves both.
|
|
1087
|
+
*
|
|
1088
|
+
* @example
|
|
1089
|
+
* ```tsx
|
|
1090
|
+
* const Countdown = registerTool({
|
|
1091
|
+
* name: "countdown",
|
|
1092
|
+
* values: { endTime: "", digitColor: "#404040" },
|
|
1093
|
+
* renderer: {
|
|
1094
|
+
* exporters: {
|
|
1095
|
+
* web: (values) => `<div style="color:${values.digitColor}">…</div>`,
|
|
1096
|
+
* email: (values) => `<table role="presentation">…</table>`,
|
|
1097
|
+
* },
|
|
1098
|
+
* },
|
|
1099
|
+
* });
|
|
1100
|
+
*
|
|
1101
|
+
* <Email><Row><Column>
|
|
1102
|
+
* <Countdown endTime="2026-08-01T00:00:00Z" digitColor="#e11d48" />
|
|
1103
|
+
* </Column></Row></Email>
|
|
1104
|
+
* ```
|
|
1105
|
+
*/
|
|
1106
|
+
declare function registerTool(tool: ElementsToolConfig): React__default.FC<ElementsToolProps>;
|
|
1107
|
+
/** Alias of {@link registerTool}. */
|
|
1108
|
+
declare const registerElementsTool: typeof registerTool;
|
|
1109
|
+
|
|
1110
|
+
export { Body, type BorderInput, Button, type ButtonProps, Column, type ColumnLayout, ColumnLayouts, DEFAULT_CONFIG, type DesignBody, type DesignColumn, type DesignContent, type DesignJSON, type DesignRow, Divider, type DividerProps, Document, type DocumentProps, type ElementsToolConfig, type ElementsToolProps, Email, type EmailProps, type FontFamilyInput, type FontWeightInput, Heading, type HeadingLevel, type HeadingProps, type Href, Html, type HtmlParts, type HtmlProps, type Icons, Image, type ImageProps, type ImageSrcInput, type LinkStyle, Menu, type MenuItem, type MenuProps, Page, type PageProps, Paragraph, type ParagraphProps, type RenderToHtmlOptions, Row, type RowProps, type SizeInput, Social, type SocialIcon, type SocialProps, Table, type TableProps, type TextAlign, type TextStyleProps, type ToolExporter, type UnlayerConfig, UnlayerProvider, type UnlayerProviderProps, type ValidColumnLayout, Video, type VideoProps, type VideoSource, htmlToTextJson, registerElementsTool, registerTool, renderRowToJson, renderToHtml, renderToHtmlParts, renderToJson, renderToPlainText, useUnlayerConfig, validateColumnLayout };
|
package/dist/index.d.ts
CHANGED
|
@@ -118,6 +118,8 @@ interface MenuItem {
|
|
|
118
118
|
type RenderMode = "web" | "email" | "document";
|
|
119
119
|
interface DesignContent {
|
|
120
120
|
type: string;
|
|
121
|
+
/** Custom tool name — present when `type` is "custom" */
|
|
122
|
+
slug?: string;
|
|
121
123
|
values: Record<string, any>;
|
|
122
124
|
}
|
|
123
125
|
interface DesignColumn {
|
|
@@ -312,6 +314,10 @@ interface BaseItemComponentProps {
|
|
|
312
314
|
* string ("10px", "16px 24px"). Applied by the containing Column. */
|
|
313
315
|
containerPadding?: SizeInput;
|
|
314
316
|
index?: number;
|
|
317
|
+
/** @internal — content id allocated by the containing Column; keeps the
|
|
318
|
+
* value-level _meta.htmlID unique per instance and aligned with the
|
|
319
|
+
* wrapper id and head/json numbering. */
|
|
320
|
+
_metaHtmlId?: string;
|
|
315
321
|
colIndex?: number;
|
|
316
322
|
cells?: any[];
|
|
317
323
|
bodyValues?: any;
|
|
@@ -1001,4 +1007,104 @@ declare function renderToHtmlParts(element: React__default.ReactElement, config?
|
|
|
1001
1007
|
declare function renderRowToJson(element: React__default.ReactElement): DesignRow;
|
|
1002
1008
|
declare function renderToJson(element: React__default.ReactElement): DesignJSON;
|
|
1003
1009
|
|
|
1004
|
-
|
|
1010
|
+
/**
|
|
1011
|
+
* registerTool — render the editor's custom tools from code.
|
|
1012
|
+
*
|
|
1013
|
+
* Accepts the same tool definition object embedders already write for
|
|
1014
|
+
* `unlayer.registerTool` (see docs.unlayer.com › Custom Tools) and returns a
|
|
1015
|
+
* React component. One definition powers both runtimes:
|
|
1016
|
+
*
|
|
1017
|
+
* - The Builder uses `label`/`icon`/`options`/`renderer.Viewer`/`validator`
|
|
1018
|
+
* for its panel, canvas, and audit — Elements ignores those.
|
|
1019
|
+
* - Elements calls `renderer.exporters[mode](values)` to produce HTML and
|
|
1020
|
+
* collects `renderer.head` css/js like any built-in component, so code
|
|
1021
|
+
* output matches editor exports by construction — it is the same function.
|
|
1022
|
+
* - `renderToJson` emits `{ type: "custom", slug: <name>, values }`, the
|
|
1023
|
+
* shape the editor stores, so designs round-trip into the Builder where
|
|
1024
|
+
* the same tool is registered.
|
|
1025
|
+
*
|
|
1026
|
+
* Sanitization: like editor exports, the tool's HTML output is passed
|
|
1027
|
+
* through `toSafeHtml` when one is configured. The default config has NO
|
|
1028
|
+
* sanitizer — with untrusted values, configure `toSafeHtml` (same caveat
|
|
1029
|
+
* as the `<Html>` component).
|
|
1030
|
+
*/
|
|
1031
|
+
|
|
1032
|
+
/** Exporter: `(values) => html`. Extra args mirror built-in exporters and can be ignored. */
|
|
1033
|
+
type ToolExporter = (values: any, ...args: any[]) => string;
|
|
1034
|
+
/** One property in an options group — Elements reads only the default. */
|
|
1035
|
+
interface ToolOptionConfig {
|
|
1036
|
+
defaultValue?: unknown;
|
|
1037
|
+
/** Disabled options contribute no default */
|
|
1038
|
+
enabled?: boolean;
|
|
1039
|
+
[editorOnly: string]: unknown;
|
|
1040
|
+
}
|
|
1041
|
+
/** A property group: `options.<group>.options.<property>` */
|
|
1042
|
+
interface ToolOptionGroup {
|
|
1043
|
+
options?: Record<string, ToolOptionConfig | undefined>;
|
|
1044
|
+
[editorOnly: string]: unknown;
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* The subset of the `unlayer.registerTool` config Elements reads. Extra
|
|
1048
|
+
* editor-only fields (label, icon, Viewer, validator, transformer, ...)
|
|
1049
|
+
* are accepted and ignored, so the same object can be passed to both APIs.
|
|
1050
|
+
*/
|
|
1051
|
+
interface ElementsToolConfig {
|
|
1052
|
+
/** Unique tool name — becomes the design JSON `slug` */
|
|
1053
|
+
name: string;
|
|
1054
|
+
/** Initial values for the tool's properties (win over option defaults) */
|
|
1055
|
+
values?: Record<string, any>;
|
|
1056
|
+
/**
|
|
1057
|
+
* Property groups. Real-world tools usually define their defaults here,
|
|
1058
|
+
* as each property's `defaultValue` — those seed the initial values,
|
|
1059
|
+
* with tool-level `values` taking precedence (the editor's order).
|
|
1060
|
+
*/
|
|
1061
|
+
options?: Record<string, ToolOptionGroup | undefined>;
|
|
1062
|
+
renderer: {
|
|
1063
|
+
/** Per-mode HTML exporters. `document` falls back to `web`. */
|
|
1064
|
+
exporters: Partial<Record<RenderMode, ToolExporter>>;
|
|
1065
|
+
/** Optional head contributions collected into the document `<head>` */
|
|
1066
|
+
head?: {
|
|
1067
|
+
css?: (values: any, ...args: any[]) => string | undefined;
|
|
1068
|
+
js?: (values: any, ...args: any[]) => string | undefined;
|
|
1069
|
+
tags?: (values: any, ...args: any[]) => string[] | undefined;
|
|
1070
|
+
};
|
|
1071
|
+
/** Editor-only canvas renderer — ignored by Elements */
|
|
1072
|
+
Viewer?: unknown;
|
|
1073
|
+
};
|
|
1074
|
+
/** React DevTools name (defaults to the tool name) */
|
|
1075
|
+
displayName?: string;
|
|
1076
|
+
/** Editor-only fields (label, icon, options, validator, ...) — ignored */
|
|
1077
|
+
[editorOnly: string]: unknown;
|
|
1078
|
+
}
|
|
1079
|
+
/** Props: the tool's values as flat props, plus a `values` escape hatch. */
|
|
1080
|
+
type ElementsToolProps = BaseItemComponentProps & Record<string, any> & {
|
|
1081
|
+
values?: Record<string, any>;
|
|
1082
|
+
};
|
|
1083
|
+
/**
|
|
1084
|
+
* Create a React component from a custom tool definition — a custom tool
|
|
1085
|
+
* is the custom component you create: the same term (and the same config)
|
|
1086
|
+
* as the editor's `unlayer.registerTool`, so one definition serves both.
|
|
1087
|
+
*
|
|
1088
|
+
* @example
|
|
1089
|
+
* ```tsx
|
|
1090
|
+
* const Countdown = registerTool({
|
|
1091
|
+
* name: "countdown",
|
|
1092
|
+
* values: { endTime: "", digitColor: "#404040" },
|
|
1093
|
+
* renderer: {
|
|
1094
|
+
* exporters: {
|
|
1095
|
+
* web: (values) => `<div style="color:${values.digitColor}">…</div>`,
|
|
1096
|
+
* email: (values) => `<table role="presentation">…</table>`,
|
|
1097
|
+
* },
|
|
1098
|
+
* },
|
|
1099
|
+
* });
|
|
1100
|
+
*
|
|
1101
|
+
* <Email><Row><Column>
|
|
1102
|
+
* <Countdown endTime="2026-08-01T00:00:00Z" digitColor="#e11d48" />
|
|
1103
|
+
* </Column></Row></Email>
|
|
1104
|
+
* ```
|
|
1105
|
+
*/
|
|
1106
|
+
declare function registerTool(tool: ElementsToolConfig): React__default.FC<ElementsToolProps>;
|
|
1107
|
+
/** Alias of {@link registerTool}. */
|
|
1108
|
+
declare const registerElementsTool: typeof registerTool;
|
|
1109
|
+
|
|
1110
|
+
export { Body, type BorderInput, Button, type ButtonProps, Column, type ColumnLayout, ColumnLayouts, DEFAULT_CONFIG, type DesignBody, type DesignColumn, type DesignContent, type DesignJSON, type DesignRow, Divider, type DividerProps, Document, type DocumentProps, type ElementsToolConfig, type ElementsToolProps, Email, type EmailProps, type FontFamilyInput, type FontWeightInput, Heading, type HeadingLevel, type HeadingProps, type Href, Html, type HtmlParts, type HtmlProps, type Icons, Image, type ImageProps, type ImageSrcInput, type LinkStyle, Menu, type MenuItem, type MenuProps, Page, type PageProps, Paragraph, type ParagraphProps, type RenderToHtmlOptions, Row, type RowProps, type SizeInput, Social, type SocialIcon, type SocialProps, Table, type TableProps, type TextAlign, type TextStyleProps, type ToolExporter, type UnlayerConfig, UnlayerProvider, type UnlayerProviderProps, type ValidColumnLayout, Video, type VideoProps, type VideoSource, htmlToTextJson, registerElementsTool, registerTool, renderRowToJson, renderToHtml, renderToHtmlParts, renderToJson, renderToPlainText, useUnlayerConfig, validateColumnLayout };
|
package/dist/index.js
CHANGED
|
@@ -613,16 +613,6 @@ function nextHtmlId(config, prefix) {
|
|
|
613
613
|
ids[prefix] = (ids[prefix] || 0) + 1;
|
|
614
614
|
return `${prefix}_${ids[prefix]}`;
|
|
615
615
|
}
|
|
616
|
-
function ensureMeta(values, type, index = 0) {
|
|
617
|
-
return {
|
|
618
|
-
...values,
|
|
619
|
-
_meta: {
|
|
620
|
-
htmlID: `u_content_${type.toLowerCase()}_${index + 1}`,
|
|
621
|
-
htmlClassNames: `u_content_${type.toLowerCase()}`,
|
|
622
|
-
...values._meta || {}
|
|
623
|
-
}
|
|
624
|
-
};
|
|
625
|
-
}
|
|
626
616
|
function renderComponent(config) {
|
|
627
617
|
const { type, values, className, style, args = [], innerHTML, _config, exporter, metaContext } = config;
|
|
628
618
|
try {
|
|
@@ -675,6 +665,7 @@ function createItemComponent(config) {
|
|
|
675
665
|
style,
|
|
676
666
|
// Internal props
|
|
677
667
|
index = 0,
|
|
668
|
+
_metaHtmlId,
|
|
678
669
|
colIndex = 0,
|
|
679
670
|
cells = [],
|
|
680
671
|
bodyValues = {},
|
|
@@ -692,11 +683,15 @@ function createItemComponent(config) {
|
|
|
692
683
|
...restProps
|
|
693
684
|
});
|
|
694
685
|
const finalValues = mergeValues(config.defaultValues, mappedValues);
|
|
695
|
-
const
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
686
|
+
const metaName = config.metaName ?? config.name.toLowerCase();
|
|
687
|
+
const valuesWithMeta = {
|
|
688
|
+
...finalValues,
|
|
689
|
+
_meta: {
|
|
690
|
+
htmlID: _metaHtmlId ?? `u_content_${metaName}_${index + 1}`,
|
|
691
|
+
htmlClassNames: `u_content_${metaName}`,
|
|
692
|
+
...finalValues._meta || {}
|
|
693
|
+
}
|
|
694
|
+
};
|
|
700
695
|
const safeBodyValues = {
|
|
701
696
|
contentWidth: "500px",
|
|
702
697
|
...bodyValues
|
|
@@ -745,7 +740,11 @@ function createItemComponent(config) {
|
|
|
745
740
|
ItemComponent[UNLAYER_CONFIG_KEY] = {
|
|
746
741
|
name: config.name,
|
|
747
742
|
defaultValues: config.defaultValues,
|
|
748
|
-
propMapper: config.propMapper
|
|
743
|
+
propMapper: config.propMapper,
|
|
744
|
+
contentType: config.contentType,
|
|
745
|
+
slug: config.slug,
|
|
746
|
+
metaName: config.metaName,
|
|
747
|
+
head: config.head
|
|
749
748
|
};
|
|
750
749
|
return ItemComponent;
|
|
751
750
|
}
|
|
@@ -1367,9 +1366,12 @@ var Column = (props) => {
|
|
|
1367
1366
|
if (typeof child.type === "function") {
|
|
1368
1367
|
const ComponentType = child.type;
|
|
1369
1368
|
const renderFn = ComponentType[UNLAYER_RENDER_KEY] || ComponentType;
|
|
1369
|
+
const componentName = ComponentType?.[UNLAYER_CONFIG_KEY]?.metaName ?? (ComponentType?.displayName || ComponentType?.name || "component").toLowerCase();
|
|
1370
|
+
const contentHtmlId = nextHtmlId(_config, `u_content_${componentName}`);
|
|
1370
1371
|
const rendered = renderFn({
|
|
1371
1372
|
...child.props,
|
|
1372
1373
|
_config,
|
|
1374
|
+
_metaHtmlId: contentHtmlId,
|
|
1373
1375
|
colIndex: index,
|
|
1374
1376
|
cells,
|
|
1375
1377
|
bodyValues,
|
|
@@ -1378,15 +1380,13 @@ var Column = (props) => {
|
|
|
1378
1380
|
});
|
|
1379
1381
|
if (rendered && typeof rendered === "object" && rendered.props && rendered.props.dangerouslySetInnerHTML) {
|
|
1380
1382
|
const componentHTML = rendered.props.dangerouslySetInnerHTML.__html;
|
|
1381
|
-
const componentType = child.type;
|
|
1382
|
-
const componentName = (componentType?.displayName || componentType?.name || "component").toLowerCase();
|
|
1383
1383
|
const childProps = child.props;
|
|
1384
1384
|
const rawContainerPadding = childProps.containerPadding ?? childProps.values?.containerPadding ?? DEFAULT_CONTAINER_PADDING2;
|
|
1385
1385
|
const containerPadding = typeof rawContainerPadding === "number" ? `${rawContainerPadding}px` : rawContainerPadding;
|
|
1386
1386
|
const contentValues = {
|
|
1387
1387
|
containerPadding,
|
|
1388
1388
|
_meta: {
|
|
1389
|
-
htmlID:
|
|
1389
|
+
htmlID: contentHtmlId,
|
|
1390
1390
|
htmlClassNames: `u_content_${componentName}`
|
|
1391
1391
|
}
|
|
1392
1392
|
};
|
|
@@ -1557,7 +1557,7 @@ UnlayerProvider.displayName = "UnlayerProvider";
|
|
|
1557
1557
|
function useUnlayerConfig() {
|
|
1558
1558
|
return useContext(getUnlayerContext());
|
|
1559
1559
|
}
|
|
1560
|
-
function
|
|
1560
|
+
function ensureMeta(values, type, index = 0) {
|
|
1561
1561
|
return {
|
|
1562
1562
|
...values,
|
|
1563
1563
|
_meta: {
|
|
@@ -1638,17 +1638,17 @@ function walkItem(element, bodyValues, displayMode, headConfig, counters, styles
|
|
|
1638
1638
|
const { children, ...restProps } = element.props;
|
|
1639
1639
|
const mappedValues = propMapper({ children, ...restProps });
|
|
1640
1640
|
const finalValues = mergeValues(defaultValues, mappedValues);
|
|
1641
|
-
const contentType = name.toLowerCase();
|
|
1641
|
+
const contentType = config.metaName ?? name.toLowerCase();
|
|
1642
1642
|
const count = nextCounter(counters, `u_content_${contentType}`);
|
|
1643
|
-
const valuesWithMeta =
|
|
1644
|
-
const head = heads[name];
|
|
1643
|
+
const valuesWithMeta = ensureMeta(finalValues, contentType, count - 1);
|
|
1644
|
+
const head = config.head ?? heads[name];
|
|
1645
1645
|
callHead(head, valuesWithMeta, bodyValues, displayMode, headConfig, styles, scripts, tags);
|
|
1646
1646
|
}
|
|
1647
1647
|
function walkColumn(element, bodyValues, rowValues, displayMode, headConfig, counters, styles, scripts, tags) {
|
|
1648
1648
|
const semanticProps = extractSemanticProps(element.props);
|
|
1649
1649
|
const mapped = mapSemanticProps(semanticProps, COLUMN_DEFAULTS, "Column");
|
|
1650
1650
|
const count = nextCounter(counters, "u_column");
|
|
1651
|
-
const columnValues =
|
|
1651
|
+
const columnValues = ensureMeta(mergeValues(COLUMN_DEFAULTS, mapped), "column", count - 1);
|
|
1652
1652
|
const columnHead = heads["Column"];
|
|
1653
1653
|
callHead(columnHead, columnValues, bodyValues, displayMode, headConfig, styles, scripts, tags);
|
|
1654
1654
|
const children = collectChildren(element.props.children);
|
|
@@ -1660,7 +1660,7 @@ function walkRow(element, bodyValues, displayMode, headConfig, counters, styles,
|
|
|
1660
1660
|
const semanticProps = extractSemanticProps(element.props, ["layout"]);
|
|
1661
1661
|
const mapped = mapSemanticProps(semanticProps, ROW_DEFAULTS, "Row");
|
|
1662
1662
|
const count = nextCounter(counters, "u_row");
|
|
1663
|
-
const rowValues =
|
|
1663
|
+
const rowValues = ensureMeta(mergeValues(ROW_DEFAULTS, mapped), "row", count - 1);
|
|
1664
1664
|
const rowHead = heads["Row"];
|
|
1665
1665
|
callHead(rowHead, rowValues, bodyValues, displayMode, headConfig, styles, scripts, tags);
|
|
1666
1666
|
const children = collectChildren(element.props.children);
|
|
@@ -1683,7 +1683,7 @@ function extractHeadFromTree(element, options) {
|
|
|
1683
1683
|
const counters = {};
|
|
1684
1684
|
const semanticProps = extractSemanticProps(element.props);
|
|
1685
1685
|
const mapped = mapSemanticProps(semanticProps, BODY_DEFAULTS, "Body");
|
|
1686
|
-
const bodyValues =
|
|
1686
|
+
const bodyValues = ensureMeta(mergeValues(BODY_DEFAULTS, mapped), "body", 0);
|
|
1687
1687
|
const bodyHead = heads["Body"];
|
|
1688
1688
|
callHead(bodyHead, bodyValues, bodyValues, displayMode, headConfig, styles, scripts, tags);
|
|
1689
1689
|
const children = collectChildren(element.props.children);
|
|
@@ -1956,10 +1956,11 @@ function processItem(element, counters, layout = {}) {
|
|
|
1956
1956
|
`[Unlayer] renderToJson: <${name2}> is not a recognized Unlayer item component. Only components created with createItemComponent are supported.`
|
|
1957
1957
|
);
|
|
1958
1958
|
}
|
|
1959
|
-
const { name, defaultValues, propMapper } = config;
|
|
1960
|
-
const contentType = toContentType(name);
|
|
1961
|
-
const
|
|
1962
|
-
const
|
|
1959
|
+
const { name, defaultValues, propMapper, slug } = config;
|
|
1960
|
+
const contentType = config.contentType ?? toContentType(name);
|
|
1961
|
+
const metaBase = `u_content_${config.metaName ?? contentType}`;
|
|
1962
|
+
const count = nextCounter2(counters, metaBase);
|
|
1963
|
+
const id = makeId(metaBase, count);
|
|
1963
1964
|
const { children, ...restProps } = element.props;
|
|
1964
1965
|
const mappedValues = propMapper({ children, ...restProps });
|
|
1965
1966
|
const finalValues = mergeValues(defaultValues, mappedValues);
|
|
@@ -1972,7 +1973,7 @@ function processItem(element, counters, layout = {}) {
|
|
|
1972
1973
|
...finalValues,
|
|
1973
1974
|
_meta: {
|
|
1974
1975
|
htmlID: id,
|
|
1975
|
-
htmlClassNames:
|
|
1976
|
+
htmlClassNames: metaBase,
|
|
1976
1977
|
...finalValues._meta || {}
|
|
1977
1978
|
},
|
|
1978
1979
|
selectable: true,
|
|
@@ -1989,7 +1990,7 @@ function processItem(element, counters, layout = {}) {
|
|
|
1989
1990
|
});
|
|
1990
1991
|
values.src = pinImageSrc(itemSrc, availableWidth);
|
|
1991
1992
|
}
|
|
1992
|
-
return { type: contentType, values };
|
|
1993
|
+
return slug ? { type: contentType, slug, values } : { type: contentType, values };
|
|
1993
1994
|
}
|
|
1994
1995
|
function processColumn(element, counters, layout = {}) {
|
|
1995
1996
|
const count = nextCounter2(counters, "u_column");
|
|
@@ -2133,6 +2134,89 @@ function renderToJson(element) {
|
|
|
2133
2134
|
};
|
|
2134
2135
|
}
|
|
2135
2136
|
|
|
2136
|
-
|
|
2137
|
+
// src/utils/register-tool.tsx
|
|
2138
|
+
function initialValuesFromOptions(options) {
|
|
2139
|
+
const result = {};
|
|
2140
|
+
for (const group of Object.values(options ?? {})) {
|
|
2141
|
+
for (const [property, option] of Object.entries(group?.options ?? {})) {
|
|
2142
|
+
if (!option || option.enabled === false) continue;
|
|
2143
|
+
if (typeof option.defaultValue !== "undefined") {
|
|
2144
|
+
result[property] = option.defaultValue;
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
return result;
|
|
2149
|
+
}
|
|
2150
|
+
function withSanitizer(exporter) {
|
|
2151
|
+
return (values, ...args) => {
|
|
2152
|
+
const html = String(exporter(values, ...args) ?? "");
|
|
2153
|
+
const meta = args[args.length - 1];
|
|
2154
|
+
const toSafeHtml = meta?.exporterConfig?.toSafeHtml;
|
|
2155
|
+
return typeof toSafeHtml === "function" ? toSafeHtml(html) : html;
|
|
2156
|
+
};
|
|
2157
|
+
}
|
|
2158
|
+
function withLinkNormalization(exporter, linkKeys) {
|
|
2159
|
+
if (linkKeys.length === 0) return exporter;
|
|
2160
|
+
return (values, ...args) => {
|
|
2161
|
+
const out = { ...values };
|
|
2162
|
+
for (const key of linkKeys) {
|
|
2163
|
+
const normalized = normalizeLinkValue(out[key]);
|
|
2164
|
+
if (normalized !== void 0) out[key] = normalized;
|
|
2165
|
+
}
|
|
2166
|
+
return exporter(out, ...args);
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2169
|
+
function linkWidgetKeys(options) {
|
|
2170
|
+
const keys = [];
|
|
2171
|
+
for (const group of Object.values(options ?? {})) {
|
|
2172
|
+
for (const [key, option] of Object.entries(group?.options ?? {})) {
|
|
2173
|
+
if (option && option.widget === "link") keys.push(key);
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
return keys;
|
|
2177
|
+
}
|
|
2178
|
+
function registerTool(tool) {
|
|
2179
|
+
if (!tool?.name || typeof tool.name !== "string") {
|
|
2180
|
+
throw new Error("[Unlayer] registerTool: `name` is required");
|
|
2181
|
+
}
|
|
2182
|
+
const exporters = tool.renderer?.exporters;
|
|
2183
|
+
if (!exporters || !exporters.web && !exporters.email) {
|
|
2184
|
+
throw new Error(
|
|
2185
|
+
`[Unlayer] registerTool("${tool.name}"): \`renderer.exporters\` needs at least a \`web\` or \`email\` exporter`
|
|
2186
|
+
);
|
|
2187
|
+
}
|
|
2188
|
+
const linkKeys = linkWidgetKeys(tool.options);
|
|
2189
|
+
const sanitized = {};
|
|
2190
|
+
for (const mode of ["web", "email", "document"]) {
|
|
2191
|
+
const exporter = exporters[mode];
|
|
2192
|
+
if (exporter) {
|
|
2193
|
+
sanitized[mode] = withSanitizer(withLinkNormalization(exporter, linkKeys));
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
if (!sanitized.web && sanitized.email) sanitized.web = sanitized.email;
|
|
2197
|
+
return createItemComponent({
|
|
2198
|
+
name: tool.name,
|
|
2199
|
+
displayName: tool.displayName ?? tool.name,
|
|
2200
|
+
// Option-group widget defaults seed the values; tool-level `values`
|
|
2201
|
+
// win — the editor's precedence.
|
|
2202
|
+
defaultValues: {
|
|
2203
|
+
...initialValuesFromOptions(tool.options),
|
|
2204
|
+
...tool.values ?? {}
|
|
2205
|
+
},
|
|
2206
|
+
// Flat props are the tool's values; `values` is the escape hatch.
|
|
2207
|
+
propMapper: ({ children: _children, values, ...rest }) => ({
|
|
2208
|
+
...values ?? {},
|
|
2209
|
+
...rest
|
|
2210
|
+
}),
|
|
2211
|
+
exporters: sanitized,
|
|
2212
|
+
contentType: "custom",
|
|
2213
|
+
slug: tool.name,
|
|
2214
|
+
metaName: `custom_${tool.name}`,
|
|
2215
|
+
head: tool.renderer.head
|
|
2216
|
+
});
|
|
2217
|
+
}
|
|
2218
|
+
var registerElementsTool = registerTool;
|
|
2219
|
+
|
|
2220
|
+
export { Body_default as Body, Button_default as Button, Column_default as Column, ColumnLayouts, DEFAULT_CONFIG, Divider_default as Divider, Document, Email, Heading_default as Heading, Html_default as Html, Image_default as Image, Menu_default as Menu, Page, Paragraph_default as Paragraph, Row_default as Row, Social_default as Social, Table_default as Table, UnlayerProvider, Video_default as Video, htmlToTextJson, registerElementsTool, registerTool, renderRowToJson, renderToHtml, renderToHtmlParts, renderToJson, renderToPlainText, useUnlayerConfig, validateColumnLayout };
|
|
2137
2221
|
//# sourceMappingURL=index.js.map
|
|
2138
2222
|
//# sourceMappingURL=index.js.map
|