@unlayer/react-elements 0.1.17 → 0.1.19
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 +32 -1
- package/dist/index.cjs +122 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +127 -2
- package/dist/index.d.ts +127 -2
- package/dist/index.js +121 -34
- 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;
|
|
@@ -896,7 +902,11 @@ declare function renderToPlainText(element: React__default.ReactElement, config?
|
|
|
896
902
|
* Result of renderToHtmlParts — head and body as separate strings.
|
|
897
903
|
*/
|
|
898
904
|
interface HtmlParts {
|
|
899
|
-
/**
|
|
905
|
+
/**
|
|
906
|
+
* Pre-assembled `<head>` content: the css in a `<style>` tag, the js in a
|
|
907
|
+
* `<script>` tag, and the tags — joined and ready to drop into a document.
|
|
908
|
+
* Equivalent to composing the granular fields below yourself.
|
|
909
|
+
*/
|
|
900
910
|
head: string;
|
|
901
911
|
/**
|
|
902
912
|
* The rendered body markup, wrapped in the renderer's host `<div>`. In
|
|
@@ -905,6 +915,21 @@ interface HtmlParts {
|
|
|
905
915
|
* `<body>` tag when composing a full document.
|
|
906
916
|
*/
|
|
907
917
|
body: string;
|
|
918
|
+
/** Raw component CSS (no `<style>` wrapper) — e.g. for inlining pipelines */
|
|
919
|
+
css: string;
|
|
920
|
+
/** Raw component JS (no `<script>` wrapper); empty for most email designs */
|
|
921
|
+
js: string;
|
|
922
|
+
/**
|
|
923
|
+
* Individual head tags (`<meta>`, `<link>`, ...) contributed by components.
|
|
924
|
+
* Not part of the editor's documented chunk parameters, but exposed here
|
|
925
|
+
* because `head` can contain them — without this field, reassembling the
|
|
926
|
+
* head from `css` + `js` would silently drop them. Usually empty.
|
|
927
|
+
*
|
|
928
|
+
* There is deliberately no `fonts` chunk: Elements has no font registry to
|
|
929
|
+
* resolve font URLs from — pass stylesheet URLs via renderToHtml's `fonts`
|
|
930
|
+
* option instead.
|
|
931
|
+
*/
|
|
932
|
+
tags: string[];
|
|
908
933
|
}
|
|
909
934
|
/**
|
|
910
935
|
* Renders an Unlayer element tree to separate head and body HTML strings.
|
|
@@ -1001,4 +1026,104 @@ declare function renderToHtmlParts(element: React__default.ReactElement, config?
|
|
|
1001
1026
|
declare function renderRowToJson(element: React__default.ReactElement): DesignRow;
|
|
1002
1027
|
declare function renderToJson(element: React__default.ReactElement): DesignJSON;
|
|
1003
1028
|
|
|
1004
|
-
|
|
1029
|
+
/**
|
|
1030
|
+
* registerTool — render the editor's custom tools from code.
|
|
1031
|
+
*
|
|
1032
|
+
* Accepts the same tool definition object embedders already write for
|
|
1033
|
+
* `unlayer.registerTool` (see docs.unlayer.com › Custom Tools) and returns a
|
|
1034
|
+
* React component. One definition powers both runtimes:
|
|
1035
|
+
*
|
|
1036
|
+
* - The Builder uses `label`/`icon`/`options`/`renderer.Viewer`/`validator`
|
|
1037
|
+
* for its panel, canvas, and audit — Elements ignores those.
|
|
1038
|
+
* - Elements calls `renderer.exporters[mode](values)` to produce HTML and
|
|
1039
|
+
* collects `renderer.head` css/js like any built-in component, so code
|
|
1040
|
+
* output matches editor exports by construction — it is the same function.
|
|
1041
|
+
* - `renderToJson` emits `{ type: "custom", slug: <name>, values }`, the
|
|
1042
|
+
* shape the editor stores, so designs round-trip into the Builder where
|
|
1043
|
+
* the same tool is registered.
|
|
1044
|
+
*
|
|
1045
|
+
* Sanitization: like editor exports, the tool's HTML output is passed
|
|
1046
|
+
* through `toSafeHtml` when one is configured. The default config has NO
|
|
1047
|
+
* sanitizer — with untrusted values, configure `toSafeHtml` (same caveat
|
|
1048
|
+
* as the `<Html>` component).
|
|
1049
|
+
*/
|
|
1050
|
+
|
|
1051
|
+
/** Exporter: `(values) => html`. Extra args mirror built-in exporters and can be ignored. */
|
|
1052
|
+
type ToolExporter = (values: any, ...args: any[]) => string;
|
|
1053
|
+
/** One property in an options group — Elements reads only the default. */
|
|
1054
|
+
interface ToolOptionConfig {
|
|
1055
|
+
defaultValue?: unknown;
|
|
1056
|
+
/** Disabled options contribute no default */
|
|
1057
|
+
enabled?: boolean;
|
|
1058
|
+
[editorOnly: string]: unknown;
|
|
1059
|
+
}
|
|
1060
|
+
/** A property group: `options.<group>.options.<property>` */
|
|
1061
|
+
interface ToolOptionGroup {
|
|
1062
|
+
options?: Record<string, ToolOptionConfig | undefined>;
|
|
1063
|
+
[editorOnly: string]: unknown;
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* The subset of the `unlayer.registerTool` config Elements reads. Extra
|
|
1067
|
+
* editor-only fields (label, icon, Viewer, validator, transformer, ...)
|
|
1068
|
+
* are accepted and ignored, so the same object can be passed to both APIs.
|
|
1069
|
+
*/
|
|
1070
|
+
interface ElementsToolConfig {
|
|
1071
|
+
/** Unique tool name — becomes the design JSON `slug` */
|
|
1072
|
+
name: string;
|
|
1073
|
+
/** Initial values for the tool's properties (win over option defaults) */
|
|
1074
|
+
values?: Record<string, any>;
|
|
1075
|
+
/**
|
|
1076
|
+
* Property groups. Real-world tools usually define their defaults here,
|
|
1077
|
+
* as each property's `defaultValue` — those seed the initial values,
|
|
1078
|
+
* with tool-level `values` taking precedence (the editor's order).
|
|
1079
|
+
*/
|
|
1080
|
+
options?: Record<string, ToolOptionGroup | undefined>;
|
|
1081
|
+
renderer: {
|
|
1082
|
+
/** Per-mode HTML exporters. `document` falls back to `web`. */
|
|
1083
|
+
exporters: Partial<Record<RenderMode, ToolExporter>>;
|
|
1084
|
+
/** Optional head contributions collected into the document `<head>` */
|
|
1085
|
+
head?: {
|
|
1086
|
+
css?: (values: any, ...args: any[]) => string | undefined;
|
|
1087
|
+
js?: (values: any, ...args: any[]) => string | undefined;
|
|
1088
|
+
tags?: (values: any, ...args: any[]) => string[] | undefined;
|
|
1089
|
+
};
|
|
1090
|
+
/** Editor-only canvas renderer — ignored by Elements */
|
|
1091
|
+
Viewer?: unknown;
|
|
1092
|
+
};
|
|
1093
|
+
/** React DevTools name (defaults to the tool name) */
|
|
1094
|
+
displayName?: string;
|
|
1095
|
+
/** Editor-only fields (label, icon, options, validator, ...) — ignored */
|
|
1096
|
+
[editorOnly: string]: unknown;
|
|
1097
|
+
}
|
|
1098
|
+
/** Props: the tool's values as flat props, plus a `values` escape hatch. */
|
|
1099
|
+
type ElementsToolProps = BaseItemComponentProps & Record<string, any> & {
|
|
1100
|
+
values?: Record<string, any>;
|
|
1101
|
+
};
|
|
1102
|
+
/**
|
|
1103
|
+
* Create a React component from a custom tool definition — a custom tool
|
|
1104
|
+
* is the custom component you create: the same term (and the same config)
|
|
1105
|
+
* as the editor's `unlayer.registerTool`, so one definition serves both.
|
|
1106
|
+
*
|
|
1107
|
+
* @example
|
|
1108
|
+
* ```tsx
|
|
1109
|
+
* const Countdown = registerTool({
|
|
1110
|
+
* name: "countdown",
|
|
1111
|
+
* values: { endTime: "", digitColor: "#404040" },
|
|
1112
|
+
* renderer: {
|
|
1113
|
+
* exporters: {
|
|
1114
|
+
* web: (values) => `<div style="color:${values.digitColor}">…</div>`,
|
|
1115
|
+
* email: (values) => `<table role="presentation">…</table>`,
|
|
1116
|
+
* },
|
|
1117
|
+
* },
|
|
1118
|
+
* });
|
|
1119
|
+
*
|
|
1120
|
+
* <Email><Row><Column>
|
|
1121
|
+
* <Countdown endTime="2026-08-01T00:00:00Z" digitColor="#e11d48" />
|
|
1122
|
+
* </Column></Row></Email>
|
|
1123
|
+
* ```
|
|
1124
|
+
*/
|
|
1125
|
+
declare function registerTool(tool: ElementsToolConfig): React__default.FC<ElementsToolProps>;
|
|
1126
|
+
/** Alias of {@link registerTool}. */
|
|
1127
|
+
declare const registerElementsTool: typeof registerTool;
|
|
1128
|
+
|
|
1129
|
+
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;
|
|
@@ -896,7 +902,11 @@ declare function renderToPlainText(element: React__default.ReactElement, config?
|
|
|
896
902
|
* Result of renderToHtmlParts — head and body as separate strings.
|
|
897
903
|
*/
|
|
898
904
|
interface HtmlParts {
|
|
899
|
-
/**
|
|
905
|
+
/**
|
|
906
|
+
* Pre-assembled `<head>` content: the css in a `<style>` tag, the js in a
|
|
907
|
+
* `<script>` tag, and the tags — joined and ready to drop into a document.
|
|
908
|
+
* Equivalent to composing the granular fields below yourself.
|
|
909
|
+
*/
|
|
900
910
|
head: string;
|
|
901
911
|
/**
|
|
902
912
|
* The rendered body markup, wrapped in the renderer's host `<div>`. In
|
|
@@ -905,6 +915,21 @@ interface HtmlParts {
|
|
|
905
915
|
* `<body>` tag when composing a full document.
|
|
906
916
|
*/
|
|
907
917
|
body: string;
|
|
918
|
+
/** Raw component CSS (no `<style>` wrapper) — e.g. for inlining pipelines */
|
|
919
|
+
css: string;
|
|
920
|
+
/** Raw component JS (no `<script>` wrapper); empty for most email designs */
|
|
921
|
+
js: string;
|
|
922
|
+
/**
|
|
923
|
+
* Individual head tags (`<meta>`, `<link>`, ...) contributed by components.
|
|
924
|
+
* Not part of the editor's documented chunk parameters, but exposed here
|
|
925
|
+
* because `head` can contain them — without this field, reassembling the
|
|
926
|
+
* head from `css` + `js` would silently drop them. Usually empty.
|
|
927
|
+
*
|
|
928
|
+
* There is deliberately no `fonts` chunk: Elements has no font registry to
|
|
929
|
+
* resolve font URLs from — pass stylesheet URLs via renderToHtml's `fonts`
|
|
930
|
+
* option instead.
|
|
931
|
+
*/
|
|
932
|
+
tags: string[];
|
|
908
933
|
}
|
|
909
934
|
/**
|
|
910
935
|
* Renders an Unlayer element tree to separate head and body HTML strings.
|
|
@@ -1001,4 +1026,104 @@ declare function renderToHtmlParts(element: React__default.ReactElement, config?
|
|
|
1001
1026
|
declare function renderRowToJson(element: React__default.ReactElement): DesignRow;
|
|
1002
1027
|
declare function renderToJson(element: React__default.ReactElement): DesignJSON;
|
|
1003
1028
|
|
|
1004
|
-
|
|
1029
|
+
/**
|
|
1030
|
+
* registerTool — render the editor's custom tools from code.
|
|
1031
|
+
*
|
|
1032
|
+
* Accepts the same tool definition object embedders already write for
|
|
1033
|
+
* `unlayer.registerTool` (see docs.unlayer.com › Custom Tools) and returns a
|
|
1034
|
+
* React component. One definition powers both runtimes:
|
|
1035
|
+
*
|
|
1036
|
+
* - The Builder uses `label`/`icon`/`options`/`renderer.Viewer`/`validator`
|
|
1037
|
+
* for its panel, canvas, and audit — Elements ignores those.
|
|
1038
|
+
* - Elements calls `renderer.exporters[mode](values)` to produce HTML and
|
|
1039
|
+
* collects `renderer.head` css/js like any built-in component, so code
|
|
1040
|
+
* output matches editor exports by construction — it is the same function.
|
|
1041
|
+
* - `renderToJson` emits `{ type: "custom", slug: <name>, values }`, the
|
|
1042
|
+
* shape the editor stores, so designs round-trip into the Builder where
|
|
1043
|
+
* the same tool is registered.
|
|
1044
|
+
*
|
|
1045
|
+
* Sanitization: like editor exports, the tool's HTML output is passed
|
|
1046
|
+
* through `toSafeHtml` when one is configured. The default config has NO
|
|
1047
|
+
* sanitizer — with untrusted values, configure `toSafeHtml` (same caveat
|
|
1048
|
+
* as the `<Html>` component).
|
|
1049
|
+
*/
|
|
1050
|
+
|
|
1051
|
+
/** Exporter: `(values) => html`. Extra args mirror built-in exporters and can be ignored. */
|
|
1052
|
+
type ToolExporter = (values: any, ...args: any[]) => string;
|
|
1053
|
+
/** One property in an options group — Elements reads only the default. */
|
|
1054
|
+
interface ToolOptionConfig {
|
|
1055
|
+
defaultValue?: unknown;
|
|
1056
|
+
/** Disabled options contribute no default */
|
|
1057
|
+
enabled?: boolean;
|
|
1058
|
+
[editorOnly: string]: unknown;
|
|
1059
|
+
}
|
|
1060
|
+
/** A property group: `options.<group>.options.<property>` */
|
|
1061
|
+
interface ToolOptionGroup {
|
|
1062
|
+
options?: Record<string, ToolOptionConfig | undefined>;
|
|
1063
|
+
[editorOnly: string]: unknown;
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* The subset of the `unlayer.registerTool` config Elements reads. Extra
|
|
1067
|
+
* editor-only fields (label, icon, Viewer, validator, transformer, ...)
|
|
1068
|
+
* are accepted and ignored, so the same object can be passed to both APIs.
|
|
1069
|
+
*/
|
|
1070
|
+
interface ElementsToolConfig {
|
|
1071
|
+
/** Unique tool name — becomes the design JSON `slug` */
|
|
1072
|
+
name: string;
|
|
1073
|
+
/** Initial values for the tool's properties (win over option defaults) */
|
|
1074
|
+
values?: Record<string, any>;
|
|
1075
|
+
/**
|
|
1076
|
+
* Property groups. Real-world tools usually define their defaults here,
|
|
1077
|
+
* as each property's `defaultValue` — those seed the initial values,
|
|
1078
|
+
* with tool-level `values` taking precedence (the editor's order).
|
|
1079
|
+
*/
|
|
1080
|
+
options?: Record<string, ToolOptionGroup | undefined>;
|
|
1081
|
+
renderer: {
|
|
1082
|
+
/** Per-mode HTML exporters. `document` falls back to `web`. */
|
|
1083
|
+
exporters: Partial<Record<RenderMode, ToolExporter>>;
|
|
1084
|
+
/** Optional head contributions collected into the document `<head>` */
|
|
1085
|
+
head?: {
|
|
1086
|
+
css?: (values: any, ...args: any[]) => string | undefined;
|
|
1087
|
+
js?: (values: any, ...args: any[]) => string | undefined;
|
|
1088
|
+
tags?: (values: any, ...args: any[]) => string[] | undefined;
|
|
1089
|
+
};
|
|
1090
|
+
/** Editor-only canvas renderer — ignored by Elements */
|
|
1091
|
+
Viewer?: unknown;
|
|
1092
|
+
};
|
|
1093
|
+
/** React DevTools name (defaults to the tool name) */
|
|
1094
|
+
displayName?: string;
|
|
1095
|
+
/** Editor-only fields (label, icon, options, validator, ...) — ignored */
|
|
1096
|
+
[editorOnly: string]: unknown;
|
|
1097
|
+
}
|
|
1098
|
+
/** Props: the tool's values as flat props, plus a `values` escape hatch. */
|
|
1099
|
+
type ElementsToolProps = BaseItemComponentProps & Record<string, any> & {
|
|
1100
|
+
values?: Record<string, any>;
|
|
1101
|
+
};
|
|
1102
|
+
/**
|
|
1103
|
+
* Create a React component from a custom tool definition — a custom tool
|
|
1104
|
+
* is the custom component you create: the same term (and the same config)
|
|
1105
|
+
* as the editor's `unlayer.registerTool`, so one definition serves both.
|
|
1106
|
+
*
|
|
1107
|
+
* @example
|
|
1108
|
+
* ```tsx
|
|
1109
|
+
* const Countdown = registerTool({
|
|
1110
|
+
* name: "countdown",
|
|
1111
|
+
* values: { endTime: "", digitColor: "#404040" },
|
|
1112
|
+
* renderer: {
|
|
1113
|
+
* exporters: {
|
|
1114
|
+
* web: (values) => `<div style="color:${values.digitColor}">…</div>`,
|
|
1115
|
+
* email: (values) => `<table role="presentation">…</table>`,
|
|
1116
|
+
* },
|
|
1117
|
+
* },
|
|
1118
|
+
* });
|
|
1119
|
+
*
|
|
1120
|
+
* <Email><Row><Column>
|
|
1121
|
+
* <Countdown endTime="2026-08-01T00:00:00Z" digitColor="#e11d48" />
|
|
1122
|
+
* </Column></Row></Email>
|
|
1123
|
+
* ```
|
|
1124
|
+
*/
|
|
1125
|
+
declare function registerTool(tool: ElementsToolConfig): React__default.FC<ElementsToolProps>;
|
|
1126
|
+
/** Alias of {@link registerTool}. */
|
|
1127
|
+
declare const registerElementsTool: typeof registerTool;
|
|
1128
|
+
|
|
1129
|
+
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);
|
|
@@ -1850,7 +1850,10 @@ function renderToHtmlParts(element, config) {
|
|
|
1850
1850
|
}
|
|
1851
1851
|
return {
|
|
1852
1852
|
head: headParts.join("\n"),
|
|
1853
|
-
body
|
|
1853
|
+
body,
|
|
1854
|
+
css,
|
|
1855
|
+
js,
|
|
1856
|
+
tags
|
|
1854
1857
|
};
|
|
1855
1858
|
}
|
|
1856
1859
|
var schemaVersion = schemaVersion$1 ?? 24;
|
|
@@ -1956,10 +1959,11 @@ function processItem(element, counters, layout = {}) {
|
|
|
1956
1959
|
`[Unlayer] renderToJson: <${name2}> is not a recognized Unlayer item component. Only components created with createItemComponent are supported.`
|
|
1957
1960
|
);
|
|
1958
1961
|
}
|
|
1959
|
-
const { name, defaultValues, propMapper } = config;
|
|
1960
|
-
const contentType = toContentType(name);
|
|
1961
|
-
const
|
|
1962
|
-
const
|
|
1962
|
+
const { name, defaultValues, propMapper, slug } = config;
|
|
1963
|
+
const contentType = config.contentType ?? toContentType(name);
|
|
1964
|
+
const metaBase = `u_content_${config.metaName ?? contentType}`;
|
|
1965
|
+
const count = nextCounter2(counters, metaBase);
|
|
1966
|
+
const id = makeId(metaBase, count);
|
|
1963
1967
|
const { children, ...restProps } = element.props;
|
|
1964
1968
|
const mappedValues = propMapper({ children, ...restProps });
|
|
1965
1969
|
const finalValues = mergeValues(defaultValues, mappedValues);
|
|
@@ -1972,7 +1976,7 @@ function processItem(element, counters, layout = {}) {
|
|
|
1972
1976
|
...finalValues,
|
|
1973
1977
|
_meta: {
|
|
1974
1978
|
htmlID: id,
|
|
1975
|
-
htmlClassNames:
|
|
1979
|
+
htmlClassNames: metaBase,
|
|
1976
1980
|
...finalValues._meta || {}
|
|
1977
1981
|
},
|
|
1978
1982
|
selectable: true,
|
|
@@ -1989,7 +1993,7 @@ function processItem(element, counters, layout = {}) {
|
|
|
1989
1993
|
});
|
|
1990
1994
|
values.src = pinImageSrc(itemSrc, availableWidth);
|
|
1991
1995
|
}
|
|
1992
|
-
return { type: contentType, values };
|
|
1996
|
+
return slug ? { type: contentType, slug, values } : { type: contentType, values };
|
|
1993
1997
|
}
|
|
1994
1998
|
function processColumn(element, counters, layout = {}) {
|
|
1995
1999
|
const count = nextCounter2(counters, "u_column");
|
|
@@ -2133,6 +2137,89 @@ function renderToJson(element) {
|
|
|
2133
2137
|
};
|
|
2134
2138
|
}
|
|
2135
2139
|
|
|
2136
|
-
|
|
2140
|
+
// src/utils/register-tool.tsx
|
|
2141
|
+
function initialValuesFromOptions(options) {
|
|
2142
|
+
const result = {};
|
|
2143
|
+
for (const group of Object.values(options ?? {})) {
|
|
2144
|
+
for (const [property, option] of Object.entries(group?.options ?? {})) {
|
|
2145
|
+
if (!option || option.enabled === false) continue;
|
|
2146
|
+
if (typeof option.defaultValue !== "undefined") {
|
|
2147
|
+
result[property] = option.defaultValue;
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
return result;
|
|
2152
|
+
}
|
|
2153
|
+
function withSanitizer(exporter) {
|
|
2154
|
+
return (values, ...args) => {
|
|
2155
|
+
const html = String(exporter(values, ...args) ?? "");
|
|
2156
|
+
const meta = args[args.length - 1];
|
|
2157
|
+
const toSafeHtml = meta?.exporterConfig?.toSafeHtml;
|
|
2158
|
+
return typeof toSafeHtml === "function" ? toSafeHtml(html) : html;
|
|
2159
|
+
};
|
|
2160
|
+
}
|
|
2161
|
+
function withLinkNormalization(exporter, linkKeys) {
|
|
2162
|
+
if (linkKeys.length === 0) return exporter;
|
|
2163
|
+
return (values, ...args) => {
|
|
2164
|
+
const out = { ...values };
|
|
2165
|
+
for (const key of linkKeys) {
|
|
2166
|
+
const normalized = normalizeLinkValue(out[key]);
|
|
2167
|
+
if (normalized !== void 0) out[key] = normalized;
|
|
2168
|
+
}
|
|
2169
|
+
return exporter(out, ...args);
|
|
2170
|
+
};
|
|
2171
|
+
}
|
|
2172
|
+
function linkWidgetKeys(options) {
|
|
2173
|
+
const keys = [];
|
|
2174
|
+
for (const group of Object.values(options ?? {})) {
|
|
2175
|
+
for (const [key, option] of Object.entries(group?.options ?? {})) {
|
|
2176
|
+
if (option && option.widget === "link") keys.push(key);
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
return keys;
|
|
2180
|
+
}
|
|
2181
|
+
function registerTool(tool) {
|
|
2182
|
+
if (!tool?.name || typeof tool.name !== "string") {
|
|
2183
|
+
throw new Error("[Unlayer] registerTool: `name` is required");
|
|
2184
|
+
}
|
|
2185
|
+
const exporters = tool.renderer?.exporters;
|
|
2186
|
+
if (!exporters || !exporters.web && !exporters.email) {
|
|
2187
|
+
throw new Error(
|
|
2188
|
+
`[Unlayer] registerTool("${tool.name}"): \`renderer.exporters\` needs at least a \`web\` or \`email\` exporter`
|
|
2189
|
+
);
|
|
2190
|
+
}
|
|
2191
|
+
const linkKeys = linkWidgetKeys(tool.options);
|
|
2192
|
+
const sanitized = {};
|
|
2193
|
+
for (const mode of ["web", "email", "document"]) {
|
|
2194
|
+
const exporter = exporters[mode];
|
|
2195
|
+
if (exporter) {
|
|
2196
|
+
sanitized[mode] = withSanitizer(withLinkNormalization(exporter, linkKeys));
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
if (!sanitized.web && sanitized.email) sanitized.web = sanitized.email;
|
|
2200
|
+
return createItemComponent({
|
|
2201
|
+
name: tool.name,
|
|
2202
|
+
displayName: tool.displayName ?? tool.name,
|
|
2203
|
+
// Option-group widget defaults seed the values; tool-level `values`
|
|
2204
|
+
// win — the editor's precedence.
|
|
2205
|
+
defaultValues: {
|
|
2206
|
+
...initialValuesFromOptions(tool.options),
|
|
2207
|
+
...tool.values ?? {}
|
|
2208
|
+
},
|
|
2209
|
+
// Flat props are the tool's values; `values` is the escape hatch.
|
|
2210
|
+
propMapper: ({ children: _children, values, ...rest }) => ({
|
|
2211
|
+
...values ?? {},
|
|
2212
|
+
...rest
|
|
2213
|
+
}),
|
|
2214
|
+
exporters: sanitized,
|
|
2215
|
+
contentType: "custom",
|
|
2216
|
+
slug: tool.name,
|
|
2217
|
+
metaName: `custom_${tool.name}`,
|
|
2218
|
+
head: tool.renderer.head
|
|
2219
|
+
});
|
|
2220
|
+
}
|
|
2221
|
+
var registerElementsTool = registerTool;
|
|
2222
|
+
|
|
2223
|
+
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
2224
|
//# sourceMappingURL=index.js.map
|
|
2138
2225
|
//# sourceMappingURL=index.js.map
|