@unlayer/react-elements 0.1.8 → 0.1.10
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 +13 -10
- package/dist/index.cjs +173 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +108 -14
- package/dist/index.d.ts +108 -14
- package/dist/index.js +174 -21
- package/dist/index.js.map +1 -1
- package/package.json +10 -2
package/README.md
CHANGED
|
@@ -51,14 +51,17 @@ function WelcomeEmail() {
|
|
|
51
51
|
|
|
52
52
|
These props have non-obvious shapes that **must** be followed exactly:
|
|
53
53
|
|
|
54
|
-
- **fontFamily**:
|
|
54
|
+
- **fontFamily**: Accepts a plain family-name string (`fontFamily="Georgia"`) or, for a full stack, `{ label, value }` (recommended).
|
|
55
55
|
```tsx
|
|
56
56
|
fontFamily={{ label: "Arial", value: "arial, sans-serif" }}
|
|
57
57
|
```
|
|
58
|
-
- **fontWeight**:
|
|
58
|
+
- **fontWeight**: Accepts a number (`700`), a numeric string (`"700"`), or a CSS keyword (`"bold"`).
|
|
59
|
+
- **fontSize / padding**: Accept a CSS string (`"28px"`, `"20px 40px"`) or a bare number (treated as px: `fontSize={28}` → `28px`).
|
|
60
|
+
- **lineHeight**: Accepts a CSS string (`"1.4"`, `"140%"`) or a bare number (kept **unitless**: `lineHeight={1.4}` → `"1.4"`).
|
|
59
61
|
- **Wrapper component**: Use `<Email>`, `<Page>`, or `<Document>` as root — they set the rendering mode automatically.
|
|
60
62
|
- **href**: Can be a plain string URL (auto-wrapped) or `{ name: "web", values: { href, target } }`.
|
|
61
|
-
- **Image
|
|
63
|
+
- **Image sizing**: `src` is a plain URL string or `{ url, width?, height?, ... }`, where `width`/`height` are the image's **natural** size. By default an image is **responsive** — it fills its container, capped at its natural size. For a **fixed** display size, use a **percent**: `width="50%"` or `maxWidth="50%"`. A px/number `width` is treated as the natural size, so `width="300px"` shows the image at up to 300px (responsive).
|
|
64
|
+
- **Heading level**: `headingType` (or its alias `level`) accepts `h1`–`h6`.
|
|
62
65
|
- **children**: Text components accept children as shorthand. `<Heading>Hello</Heading>` sets the heading text. `<Paragraph>` supports children for plain text.
|
|
63
66
|
- **Paragraph text**: Use `html` prop for text content (supports inline formatting like `<b>`, `<a>`). Use children for plain text.
|
|
64
67
|
|
|
@@ -154,6 +157,7 @@ Must be child of Row. Count must match layout.
|
|
|
154
157
|
- `fontFamily?: { label: string, value: string }`
|
|
155
158
|
- `padding?: string` — `"10px 20px"`
|
|
156
159
|
- `borderRadius?: string` — `"4px"`
|
|
160
|
+
- `width?: number | string` — display width; `width="100%"` makes the button full-width, `width="200px"` pins it
|
|
157
161
|
- `textAlign?: "left" | "center" | "right"` — `"center"`
|
|
158
162
|
|
|
159
163
|
### Paragraph
|
|
@@ -439,13 +443,12 @@ const monoFont = { label: "Monospace", value: "'SF Mono', 'Fira Code', 'Roboto M
|
|
|
439
443
|
|
|
440
444
|
## Common Mistakes
|
|
441
445
|
|
|
442
|
-
1. **
|
|
443
|
-
2. **
|
|
444
|
-
3. **
|
|
445
|
-
4. **
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
7. **padding without units** — Use `padding="0px"` not `padding="0"` — the type requires the `px` suffix for consistency
|
|
446
|
+
1. **Column count mismatch** — `TwoEqual` layout requires exactly 2 `<Column>` children
|
|
447
|
+
2. **Missing Column** — Items must be inside `<Column>`, never directly in `<Row>`
|
|
448
|
+
3. **Missing Row** — Columns must be inside `<Row>`, never directly in `<Email>`/`<Page>`/`<Document>`
|
|
449
|
+
4. **JSX formatting in children** — `<Heading>Hi <b>x</b></Heading>` is flattened to plain text (the formatting is **not** preserved). For inline formatting use `<Paragraph html="Hi <b>x</b>" />`.
|
|
450
|
+
|
|
451
|
+
> Note: the CSS-idiom forms that used to be mistakes now work — a string `fontFamily`, a string/number `fontWeight`, a numeric `fontSize`, `padding="0"`, and `<Paragraph text="..." />` are all accepted and normalized. The object/numeric forms above are still recommended for clarity.
|
|
449
452
|
|
|
450
453
|
## Development
|
|
451
454
|
|
package/dist/index.cjs
CHANGED
|
@@ -208,16 +208,53 @@ function analyzeNestedStructure(defaultValues) {
|
|
|
208
208
|
nestedStructureCache.set(defaultValues, nestedGroups);
|
|
209
209
|
return nestedGroups;
|
|
210
210
|
}
|
|
211
|
+
var PX_SIZE_KEYS = [
|
|
212
|
+
"fontSize",
|
|
213
|
+
"padding",
|
|
214
|
+
"containerPadding",
|
|
215
|
+
"borderRadius",
|
|
216
|
+
"letterSpacing"
|
|
217
|
+
];
|
|
218
|
+
function normalizeCssProps(props) {
|
|
219
|
+
if (typeof props.fontFamily === "string") {
|
|
220
|
+
const v = props.fontFamily;
|
|
221
|
+
props.fontFamily = { label: v, value: v };
|
|
222
|
+
}
|
|
223
|
+
if (typeof props.fontWeight === "string" && /^\d+$/.test(props.fontWeight.trim())) {
|
|
224
|
+
props.fontWeight = Number(props.fontWeight.trim());
|
|
225
|
+
}
|
|
226
|
+
if (typeof props.lineHeight === "number") {
|
|
227
|
+
props.lineHeight = String(props.lineHeight);
|
|
228
|
+
}
|
|
229
|
+
for (const key of PX_SIZE_KEYS) {
|
|
230
|
+
const v = props[key];
|
|
231
|
+
if (typeof v === "number") {
|
|
232
|
+
props[key] = `${v}px`;
|
|
233
|
+
} else if (typeof v === "string" && /^\d+$/.test(v.trim())) {
|
|
234
|
+
props[key] = `${v.trim()}px`;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function flattenChildrenText(node) {
|
|
239
|
+
if (node == null || typeof node === "boolean") return "";
|
|
240
|
+
if (typeof node === "string") return node;
|
|
241
|
+
if (typeof node === "number") return String(node);
|
|
242
|
+
if (Array.isArray(node)) return node.map(flattenChildrenText).join("");
|
|
243
|
+
if (typeof node === "object" && "props" in node) {
|
|
244
|
+
return flattenChildrenText(node.props?.children);
|
|
245
|
+
}
|
|
246
|
+
return "";
|
|
247
|
+
}
|
|
211
248
|
function mapSemanticProps(props, defaultValues, componentType) {
|
|
212
249
|
const { children, values, ...restProps } = props;
|
|
213
250
|
const userProps = { ...restProps };
|
|
214
251
|
const result = values ? { ...values } : {};
|
|
215
252
|
if (children !== void 0 && !result.text && !result.textJson) {
|
|
253
|
+
const textContent = typeof children === "string" ? children : flattenChildrenText(children);
|
|
216
254
|
if (componentType === "Paragraph") {
|
|
217
|
-
const textContent = typeof children === "string" ? children : String(children);
|
|
218
255
|
result.textJson = textToTextJson(textContent);
|
|
219
256
|
} else {
|
|
220
|
-
result.text =
|
|
257
|
+
result.text = textContent;
|
|
221
258
|
}
|
|
222
259
|
}
|
|
223
260
|
const textFromEscapeHatch = result.text;
|
|
@@ -243,6 +280,7 @@ function mapSemanticProps(props, defaultValues, componentType) {
|
|
|
243
280
|
};
|
|
244
281
|
}
|
|
245
282
|
}
|
|
283
|
+
normalizeCssProps(userProps);
|
|
246
284
|
const nestedGroups = analyzeNestedStructure(defaultValues);
|
|
247
285
|
const nested = {};
|
|
248
286
|
const flat = {};
|
|
@@ -277,6 +315,19 @@ function mapSemanticProps(props, defaultValues, componentType) {
|
|
|
277
315
|
};
|
|
278
316
|
}
|
|
279
317
|
}
|
|
318
|
+
if (defaultValues && typeof defaultValues === "object" && "border" in defaultValues) {
|
|
319
|
+
const borderSideRe = /^border(Top|Right|Bottom|Left)(Width|Style|Color)$/;
|
|
320
|
+
const collected = {};
|
|
321
|
+
for (const key of Object.keys(final)) {
|
|
322
|
+
if (borderSideRe.test(key)) {
|
|
323
|
+
collected[key] = final[key];
|
|
324
|
+
delete final[key];
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (Object.keys(collected).length > 0) {
|
|
328
|
+
final.border = { ...final.border || {}, ...collected };
|
|
329
|
+
}
|
|
330
|
+
}
|
|
280
331
|
return final;
|
|
281
332
|
}
|
|
282
333
|
function normalizeLinkValue(value) {
|
|
@@ -545,7 +596,26 @@ var DEFAULT_VALUES = {
|
|
|
545
596
|
var Button = createItemComponent({
|
|
546
597
|
name: "Button",
|
|
547
598
|
defaultValues: DEFAULT_VALUES,
|
|
548
|
-
propMapper: (props) =>
|
|
599
|
+
propMapper: (props) => {
|
|
600
|
+
const mapped = mapSemanticProps(
|
|
601
|
+
props,
|
|
602
|
+
DEFAULT_VALUES,
|
|
603
|
+
"Button"
|
|
604
|
+
);
|
|
605
|
+
const size = mapped.size;
|
|
606
|
+
if (size && typeof size === "object" && !Array.isArray(size)) {
|
|
607
|
+
const s = size;
|
|
608
|
+
if (typeof s.width === "number") {
|
|
609
|
+
s.width = `${s.width}px`;
|
|
610
|
+
} else if (typeof s.width === "string" && /^\d+(?:\.\d+)?$/.test(s.width.trim())) {
|
|
611
|
+
s.width = `${s.width.trim()}px`;
|
|
612
|
+
}
|
|
613
|
+
if (s.width !== void 0 && s.autoWidth === void 0) {
|
|
614
|
+
s.autoWidth = false;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
return mapped;
|
|
618
|
+
},
|
|
549
619
|
displayName: "Button",
|
|
550
620
|
exporters: exporters.ButtonExporters
|
|
551
621
|
});
|
|
@@ -610,18 +680,46 @@ var Image = createItemComponent({
|
|
|
610
680
|
defaultValues: DEFAULT_VALUES5,
|
|
611
681
|
propMapper: (props) => {
|
|
612
682
|
const { alt, src, ...rest } = props;
|
|
683
|
+
const restValues = rest.values;
|
|
684
|
+
const normalizedRest = restValues && typeof restValues.src === "string" ? { ...rest, values: { ...restValues, src: { url: restValues.src } } } : rest;
|
|
613
685
|
const base = mapSemanticProps(
|
|
614
|
-
|
|
686
|
+
normalizedRest,
|
|
615
687
|
DEFAULT_VALUES5,
|
|
616
688
|
"Image"
|
|
617
689
|
);
|
|
618
690
|
if (alt !== void 0) {
|
|
619
691
|
base.altText = alt;
|
|
620
692
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
693
|
+
const baseSrc = base.src;
|
|
694
|
+
const fromValues = baseSrc && typeof baseSrc === "object" && !Array.isArray(baseSrc) ? baseSrc : typeof baseSrc === "string" ? { url: baseSrc } : {};
|
|
695
|
+
const fromProp = typeof src === "string" ? { url: src } : src ?? {};
|
|
696
|
+
const userSrc = { ...fromValues, ...fromProp };
|
|
697
|
+
if (src !== void 0 || baseSrc !== void 0) {
|
|
698
|
+
const isStringUrl = typeof src === "string" || src === void 0 && typeof baseSrc === "string";
|
|
699
|
+
const start = isStringUrl ? { autoWidth: true, maxWidth: "100%" } : { ...DEFAULT_VALUES5.src };
|
|
700
|
+
const merged = { ...start, ...userSrc };
|
|
701
|
+
const pctRe = /^\d+(?:\.\d+)?%$/;
|
|
702
|
+
if (typeof merged.width === "string") {
|
|
703
|
+
const t = merged.width.trim();
|
|
704
|
+
if (pctRe.test(t)) {
|
|
705
|
+
if (userSrc.maxWidth === void 0) merged.maxWidth = t;
|
|
706
|
+
delete merged.width;
|
|
707
|
+
} else {
|
|
708
|
+
const px = /^(\d+(?:\.\d+)?)(?:px)?$/.exec(t);
|
|
709
|
+
if (px) merged.width = parseFloat(px[1]);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
const displayPct = typeof merged.maxWidth === "string" && pctRe.test(merged.maxWidth.trim()) ? merged.maxWidth.trim() : void 0;
|
|
713
|
+
if (userSrc.autoWidth === void 0) {
|
|
714
|
+
if (displayPct && displayPct !== "100%") {
|
|
715
|
+
merged.autoWidth = false;
|
|
716
|
+
merged.maxWidth = displayPct;
|
|
717
|
+
} else {
|
|
718
|
+
merged.autoWidth = true;
|
|
719
|
+
merged.maxWidth = "100%";
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
base.src = merged;
|
|
625
723
|
}
|
|
626
724
|
return base;
|
|
627
725
|
},
|
|
@@ -732,25 +830,37 @@ var Table = createItemComponent({
|
|
|
732
830
|
name: "Table",
|
|
733
831
|
defaultValues: DEFAULT_VALUES9,
|
|
734
832
|
propMapper: (props) => {
|
|
735
|
-
const { headers, data, ...rest } = props;
|
|
736
|
-
if (headers || data) {
|
|
833
|
+
const { headers, data, columns, rows, ...rest } = props;
|
|
834
|
+
if (headers || data || typeof columns === "number" || typeof rows === "number") {
|
|
737
835
|
const base = mapSemanticProps(
|
|
738
836
|
rest,
|
|
739
837
|
DEFAULT_VALUES9,
|
|
740
838
|
"Table"
|
|
741
839
|
);
|
|
840
|
+
const colCount = headers ? headers.length : typeof columns === "number" ? columns : data?.[0]?.length ?? 0;
|
|
841
|
+
const blankCells = (n) => Array.from({ length: n }, () => ({ text: "", width: 0 }));
|
|
742
842
|
const tableHeaders = headers ? [{ cells: headers.map((text) => ({ text, width: 0 })), height: 0 }] : [];
|
|
743
843
|
const tableRows = data ? data.map((row) => ({
|
|
744
844
|
cells: row.map((text) => ({ text, width: 0 })),
|
|
745
845
|
height: 0
|
|
746
|
-
})) :
|
|
846
|
+
})) : typeof rows === "number" ? (
|
|
847
|
+
// No data: build an empty grid sized by `columns` × `rows`.
|
|
848
|
+
Array.from({ length: rows }, () => ({
|
|
849
|
+
cells: blankCells(colCount),
|
|
850
|
+
height: 0
|
|
851
|
+
}))
|
|
852
|
+
) : [];
|
|
747
853
|
base.table = { headers: tableHeaders, rows: tableRows, footers: [] };
|
|
854
|
+
if (headers || typeof columns === "number") {
|
|
855
|
+
base.columns = colCount;
|
|
856
|
+
}
|
|
748
857
|
if (headers) {
|
|
749
|
-
base.columns = headers.length;
|
|
750
858
|
base.enableHeader = true;
|
|
751
859
|
}
|
|
752
860
|
if (data) {
|
|
753
861
|
base.rows = data.length;
|
|
862
|
+
} else if (typeof rows === "number") {
|
|
863
|
+
base.rows = rows;
|
|
754
864
|
}
|
|
755
865
|
return base;
|
|
756
866
|
}
|
|
@@ -911,13 +1021,23 @@ ${widths.map(({ value, className }) => ` .no-stack .u-col-${className} { width:
|
|
|
911
1021
|
}`;
|
|
912
1022
|
return baseCSS + "\n" + columnCSS + "\n" + responsiveCSS;
|
|
913
1023
|
}
|
|
1024
|
+
function toContentWidthPx(bodyValues, fallback = 500) {
|
|
1025
|
+
const raw = bodyValues?.contentWidth;
|
|
1026
|
+
if (typeof raw === "number" && Number.isFinite(raw)) return raw;
|
|
1027
|
+
if (typeof raw === "string") {
|
|
1028
|
+
const n = parseInt(raw, 10);
|
|
1029
|
+
if (Number.isFinite(n)) return n;
|
|
1030
|
+
}
|
|
1031
|
+
return fallback;
|
|
1032
|
+
}
|
|
914
1033
|
function renderRowToHtml(innerHTML, values, bodyValues, mode, cells, collection = "rows") {
|
|
915
1034
|
const rowExporter = exporters.RowExporters[mode] || exporters.RowExporters.web;
|
|
916
1035
|
const html = rowExporter(innerHTML, values, bodyValues, {
|
|
917
1036
|
collection,
|
|
918
1037
|
variant: mode
|
|
919
1038
|
});
|
|
920
|
-
const
|
|
1039
|
+
const contentWidth = toContentWidthPx(bodyValues);
|
|
1040
|
+
const css = generateGridCSS(cells, mode, contentWidth);
|
|
921
1041
|
return css ? `<style>${css}</style>${html}` : html;
|
|
922
1042
|
}
|
|
923
1043
|
function processChildren(children, cells, bodyValues, rowValues, mode, _config) {
|
|
@@ -1015,11 +1135,16 @@ var Row = (props) => {
|
|
|
1015
1135
|
};
|
|
1016
1136
|
Row.displayName = "Row";
|
|
1017
1137
|
var Row_default = Row;
|
|
1138
|
+
var DEFAULT_CONTAINER_PADDING = "10px";
|
|
1018
1139
|
var DEFAULT_VALUES12 = COLUMN_DEFAULTS;
|
|
1019
1140
|
function renderColumnToHtml(innerHTML, values, index, cells, bodyValues, rowValues, mode) {
|
|
1020
1141
|
const columnExporter = exporters.ColumnExporters[mode] || exporters.ColumnExporters.web;
|
|
1021
1142
|
return columnExporter(innerHTML, values, index, cells, bodyValues, rowValues);
|
|
1022
1143
|
}
|
|
1144
|
+
function renderContentToHtml(innerHTML, values, bodyValues, mode) {
|
|
1145
|
+
const contentExporter = exporters.ContentExporters[mode] || exporters.ContentExporters.web;
|
|
1146
|
+
return contentExporter(innerHTML, values, bodyValues, {});
|
|
1147
|
+
}
|
|
1023
1148
|
var Column = (props) => {
|
|
1024
1149
|
const {
|
|
1025
1150
|
children,
|
|
@@ -1062,10 +1187,22 @@ var Column = (props) => {
|
|
|
1062
1187
|
if (rendered && typeof rendered === "object" && rendered.props && rendered.props.dangerouslySetInnerHTML) {
|
|
1063
1188
|
const componentHTML = rendered.props.dangerouslySetInnerHTML.__html;
|
|
1064
1189
|
const componentType = child.type;
|
|
1065
|
-
const componentName = componentType?.displayName || componentType?.name || "component";
|
|
1066
|
-
const
|
|
1067
|
-
const containerPadding =
|
|
1068
|
-
|
|
1190
|
+
const componentName = (componentType?.displayName || componentType?.name || "component").toLowerCase();
|
|
1191
|
+
const childProps = child.props;
|
|
1192
|
+
const containerPadding = childProps.containerPadding ?? childProps.values?.containerPadding ?? DEFAULT_CONTAINER_PADDING;
|
|
1193
|
+
const contentValues = {
|
|
1194
|
+
containerPadding,
|
|
1195
|
+
_meta: {
|
|
1196
|
+
htmlID: `u_content_${componentName}_${childIndex + 1}`,
|
|
1197
|
+
htmlClassNames: `u_content_${componentName}`
|
|
1198
|
+
}
|
|
1199
|
+
};
|
|
1200
|
+
innerHTML += renderContentToHtml(
|
|
1201
|
+
componentHTML,
|
|
1202
|
+
contentValues,
|
|
1203
|
+
bodyValues,
|
|
1204
|
+
mode
|
|
1205
|
+
);
|
|
1069
1206
|
} else if (rendered) {
|
|
1070
1207
|
const name = child.type?.displayName || child.type?.name || "Unknown";
|
|
1071
1208
|
console.warn(
|
|
@@ -1127,7 +1264,13 @@ function renderBodyToHtml(innerHTML, values, mode, previewText) {
|
|
|
1127
1264
|
}
|
|
1128
1265
|
}
|
|
1129
1266
|
const bodyExporter = exporters.BodyExporters[mode] || exporters.BodyExporters.web;
|
|
1130
|
-
const raw = mode === "document" ? bodyExporter(finalInnerHtml, values, { type: "" }) :
|
|
1267
|
+
const raw = mode === "document" ? bodyExporter(finalInnerHtml, values, { type: "" }) : mode === "email" ? (
|
|
1268
|
+
// The email body exporter reads body context (contentWidth,
|
|
1269
|
+
// contentAlign) from the `bodyValues` field of its 3rd argument.
|
|
1270
|
+
// Passing `values` directly left it undefined, so the Outlook (MSO)
|
|
1271
|
+
// table fell back to 600px regardless of contentWidth.
|
|
1272
|
+
bodyExporter(finalInnerHtml, values, { bodyValues: values })
|
|
1273
|
+
) : bodyExporter(finalInnerHtml, values, values);
|
|
1131
1274
|
return raw.replace("min-height: 100vh; ", "").replace("min-height: 100vh;", "");
|
|
1132
1275
|
}
|
|
1133
1276
|
var Body = (props) => {
|
|
@@ -1135,7 +1278,10 @@ var Body = (props) => {
|
|
|
1135
1278
|
const resolvedConfig = { ...DEFAULT_CONFIG, ...configProp };
|
|
1136
1279
|
const mode = modeProp ?? resolvedConfig.mode ?? "web";
|
|
1137
1280
|
const _config = { ...resolvedConfig, mode };
|
|
1138
|
-
const values =
|
|
1281
|
+
const values = mergeValues(
|
|
1282
|
+
DEFAULT_VALUES13,
|
|
1283
|
+
mapSemanticProps(semanticProps, DEFAULT_VALUES13, "Body")
|
|
1284
|
+
);
|
|
1139
1285
|
const valuesWithMeta = {
|
|
1140
1286
|
...values,
|
|
1141
1287
|
_meta: {
|
|
@@ -1148,7 +1294,10 @@ var Body = (props) => {
|
|
|
1148
1294
|
if (children) {
|
|
1149
1295
|
enrichedChildren = React__default.default.Children.map(children, (child) => {
|
|
1150
1296
|
if (React__default.default.isValidElement(child)) {
|
|
1151
|
-
return React__default.default.cloneElement(child, {
|
|
1297
|
+
return React__default.default.cloneElement(child, {
|
|
1298
|
+
_config,
|
|
1299
|
+
bodyValues: values
|
|
1300
|
+
});
|
|
1152
1301
|
}
|
|
1153
1302
|
return child;
|
|
1154
1303
|
});
|
|
@@ -1580,6 +1729,10 @@ function processBody(element, counters) {
|
|
|
1580
1729
|
const semanticProps = extractSemanticProps2(element.props);
|
|
1581
1730
|
const mapped = mapSemanticProps(semanticProps, BODY_DEFAULTS, "Body");
|
|
1582
1731
|
const values = mergeValues(BODY_DEFAULTS, mapped);
|
|
1732
|
+
const previewText = element.props.previewText;
|
|
1733
|
+
if (previewText !== void 0) {
|
|
1734
|
+
values.preheaderText = previewText;
|
|
1735
|
+
}
|
|
1583
1736
|
const valuesWithMeta = {
|
|
1584
1737
|
...values,
|
|
1585
1738
|
_meta: {
|