@standhigher/react-email-editor 0.1.0 → 0.2.0
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/CHANGELOG.md +38 -0
- package/README.md +76 -189
- package/README.zh-CN.md +76 -189
- package/dist/index.d.ts +25 -7
- package/dist/index.js +1983 -1059
- package/dist/styles.css +677 -390
- package/package.json +21 -4
package/dist/index.js
CHANGED
|
@@ -18,15 +18,27 @@ var __spreadValues = (a, b) => {
|
|
|
18
18
|
return a;
|
|
19
19
|
};
|
|
20
20
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
21
|
+
var __objRest = (source, exclude) => {
|
|
22
|
+
var target = {};
|
|
23
|
+
for (var prop in source)
|
|
24
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
25
|
+
target[prop] = source[prop];
|
|
26
|
+
if (source != null && __getOwnPropSymbols)
|
|
27
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
28
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
29
|
+
target[prop] = source[prop];
|
|
30
|
+
}
|
|
31
|
+
return target;
|
|
32
|
+
};
|
|
21
33
|
|
|
22
34
|
// src/EmailEditor.tsx
|
|
23
|
-
import { forwardRef as
|
|
35
|
+
import { forwardRef as forwardRef3 } from "react";
|
|
24
36
|
|
|
25
37
|
// src/shell/EmailEditorShell.tsx
|
|
26
|
-
import { forwardRef
|
|
38
|
+
import { forwardRef as forwardRef2, useState as useState15 } from "react";
|
|
27
39
|
|
|
28
40
|
// src/editor-dnd/EditorDnd.tsx
|
|
29
|
-
import { useMemo, useRef, useState } from "react";
|
|
41
|
+
import { useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
30
42
|
import {
|
|
31
43
|
DndContext,
|
|
32
44
|
DragOverlay,
|
|
@@ -61,6 +73,51 @@ function BlockIcon({ icon, className }) {
|
|
|
61
73
|
return /* @__PURE__ */ jsx("span", { className, children: icon });
|
|
62
74
|
}
|
|
63
75
|
|
|
76
|
+
// src/blocks/inspector-sections.ts
|
|
77
|
+
var SPACING = {
|
|
78
|
+
title: "Spacing",
|
|
79
|
+
styleIds: ["dimension.padding", "dimension.margin"]
|
|
80
|
+
};
|
|
81
|
+
var BLOCK_BORDERS = ["decorations.borderWidth", "decorations.borderStyle", "decorations.borderColor", "decorations.borderRadius"];
|
|
82
|
+
var BLOCK_APPEARANCE = {
|
|
83
|
+
title: "Block appearance",
|
|
84
|
+
styleIds: ["decorations.backgroundColor", ...BLOCK_BORDERS]
|
|
85
|
+
};
|
|
86
|
+
var ADVANCED_BLOCK_APPEARANCE = {
|
|
87
|
+
title: "Block appearance",
|
|
88
|
+
styleIds: ["decorations.backgroundColor"],
|
|
89
|
+
advancedStyleIds: BLOCK_BORDERS
|
|
90
|
+
};
|
|
91
|
+
var ELEMENT_BORDERS = ["element.borderWidth", "element.borderStyle", "element.borderColor", "element.borderRadius"];
|
|
92
|
+
var HEADING_SECTIONS = [SPACING, BLOCK_APPEARANCE];
|
|
93
|
+
var TEXT_SECTIONS = [SPACING, BLOCK_APPEARANCE];
|
|
94
|
+
var HTML_SECTIONS = [
|
|
95
|
+
{ title: "HTML", fieldNames: ["html"] },
|
|
96
|
+
SPACING,
|
|
97
|
+
BLOCK_APPEARANCE
|
|
98
|
+
];
|
|
99
|
+
var BUTTON_SECTIONS = [
|
|
100
|
+
{ title: "Link", fieldNames: ["href"] },
|
|
101
|
+
{ title: "Button", styleIds: ["element.align", "element.width", "element.padding", "element.backgroundColor", ...ELEMENT_BORDERS] },
|
|
102
|
+
SPACING,
|
|
103
|
+
ADVANCED_BLOCK_APPEARANCE
|
|
104
|
+
];
|
|
105
|
+
var IMAGE_SECTIONS = [
|
|
106
|
+
{ title: "Image", fieldNames: ["src", "alt", "link"] },
|
|
107
|
+
{ title: "Size & position", styleIds: ["element.width", "element.height", "element.align"] },
|
|
108
|
+
{ title: "Image appearance", styleIds: ELEMENT_BORDERS },
|
|
109
|
+
SPACING,
|
|
110
|
+
ADVANCED_BLOCK_APPEARANCE
|
|
111
|
+
];
|
|
112
|
+
var DIVIDER_SECTIONS = [
|
|
113
|
+
{ title: "Line", styleIds: ["element.borderColor", "element.borderWidth", "element.borderStyle", "element.width", "element.align"] },
|
|
114
|
+
SPACING,
|
|
115
|
+
{ title: "Block appearance", styleIds: ["decorations.backgroundColor"] }
|
|
116
|
+
];
|
|
117
|
+
var SPACER_SECTIONS = [
|
|
118
|
+
{ title: "Spacer", fieldNames: ["height"], styleIds: ["decorations.backgroundColor"] }
|
|
119
|
+
];
|
|
120
|
+
|
|
64
121
|
// src/constants/block-types.ts
|
|
65
122
|
var BLOCK_TYPES = {
|
|
66
123
|
heading: "heading",
|
|
@@ -76,6 +133,16 @@ var BLOCK_TYPES = {
|
|
|
76
133
|
function esc(value) {
|
|
77
134
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
78
135
|
}
|
|
136
|
+
var TEXT_ENTITIES = { amp: "&", lt: "<", gt: ">", quot: '"', apos: "'", nbsp: "\xA0" };
|
|
137
|
+
function decodeHtmlText(value) {
|
|
138
|
+
return value.replace(/&(#x[\da-f]+|#\d+|amp|lt|gt|quot|apos|nbsp);/gi, (entity, name) => {
|
|
139
|
+
var _a;
|
|
140
|
+
if (!name.startsWith("#")) return (_a = TEXT_ENTITIES[name.toLowerCase()]) != null ? _a : entity;
|
|
141
|
+
const hex = name[1].toLowerCase() === "x";
|
|
142
|
+
const code = Number.parseInt(name.slice(hex ? 2 : 1), hex ? 16 : 10);
|
|
143
|
+
return code > 0 && code <= 1114111 && !(code >= 55296 && code <= 57343) ? String.fromCodePoint(code) : entity;
|
|
144
|
+
});
|
|
145
|
+
}
|
|
79
146
|
|
|
80
147
|
// src/constants/style-values.ts
|
|
81
148
|
var TEXT_ALIGNS = {
|
|
@@ -106,6 +173,7 @@ var LINK_DECORATIONS = {
|
|
|
106
173
|
function isStyleValue(values, value) {
|
|
107
174
|
return typeof value === "string" && Object.values(values).includes(value);
|
|
108
175
|
}
|
|
176
|
+
var HEX_COLOR = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
|
|
109
177
|
|
|
110
178
|
// src/utils/css-values.ts
|
|
111
179
|
function cssLength(value, fallback = "0px", allowAuto = false) {
|
|
@@ -182,7 +250,7 @@ function cssBorderStyle(value) {
|
|
|
182
250
|
}
|
|
183
251
|
|
|
184
252
|
// src/document/constants.ts
|
|
185
|
-
var DOCUMENT_VERSION =
|
|
253
|
+
var DOCUMENT_VERSION = 2;
|
|
186
254
|
function fontOption(label, value) {
|
|
187
255
|
return Object.freeze({ label, value });
|
|
188
256
|
}
|
|
@@ -221,7 +289,7 @@ var DEFAULT_EMAIL_GLOBAL = {
|
|
|
221
289
|
listIndent: "22px",
|
|
222
290
|
borderWidth: "0px",
|
|
223
291
|
borderStyle: BORDER_STYLES.solid,
|
|
224
|
-
borderColor: "
|
|
292
|
+
borderColor: "transparent",
|
|
225
293
|
borderRadius: "0px",
|
|
226
294
|
fontFamily: "Arial, Helvetica, sans-serif",
|
|
227
295
|
fontWeight: FONT_WEIGHTS.normal,
|
|
@@ -277,6 +345,9 @@ function createEmptyDocument() {
|
|
|
277
345
|
return { version: DOCUMENT_VERSION, subject: "", global: resolveEmailGlobal(), blocks: [] };
|
|
278
346
|
}
|
|
279
347
|
function ensureEmailDocument(document) {
|
|
348
|
+
if (document.version !== DOCUMENT_VERSION) {
|
|
349
|
+
throw new Error(`Unsupported email document version: ${document.version}. Expected ${DOCUMENT_VERSION}.`);
|
|
350
|
+
}
|
|
280
351
|
return __spreadProps(__spreadValues({}, document), { global: resolveEmailGlobal(document.global) });
|
|
281
352
|
}
|
|
282
353
|
|
|
@@ -329,7 +400,7 @@ function decorationStyle(decoration = {}) {
|
|
|
329
400
|
backgroundColor: cssColor(decoration.backgroundColor),
|
|
330
401
|
borderWidth: cssLength(decoration.borderWidth),
|
|
331
402
|
borderStyle: cssBorderStyle(decoration.borderStyle),
|
|
332
|
-
borderColor: cssColor(decoration.borderColor, "
|
|
403
|
+
borderColor: cssColor(decoration.borderColor, "transparent")
|
|
333
404
|
}, radiusStyle(decoration.borderRadius));
|
|
334
405
|
}
|
|
335
406
|
function styleToCss(style) {
|
|
@@ -403,15 +474,6 @@ function renderAligned(target, options) {
|
|
|
403
474
|
return renderShrinkElement(align, width, options.content);
|
|
404
475
|
}
|
|
405
476
|
|
|
406
|
-
// src/blocks/icons.ts
|
|
407
|
-
var ICON_TEXT = '<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M5 4h14v3H5V4Zm0 13h9v-3H5v3Z"/></svg>';
|
|
408
|
-
var ICON_HEADING = '<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M4 4h3v7h10V4h3v16h-3v-6H7v6H4V4Z"/></svg>';
|
|
409
|
-
var ICON_BUTTON = '<svg viewBox="0 0 24 24" width="18" height="18"><rect x="3" y="7" width="18" height="10" rx="2" fill="currentColor"/></svg>';
|
|
410
|
-
var ICON_IMAGE = '<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M4 5h16a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1Zm1.5 11 4-4 3 3 2.5-2.5 4 4V6.5h-13V16Z"/></svg>';
|
|
411
|
-
var ICON_DIVIDER = '<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M3 11h18v2H3v-2Z"/></svg>';
|
|
412
|
-
var ICON_SPACER = '<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M4 8h16v2H4V8Zm0 6h16v2H4v-2Z"/></svg>';
|
|
413
|
-
var ICON_HTML = '<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M5 3l-3 9 3 9h3l-3-9 3-9H5Zm6 0 3 9-3 9h3l3-9-3-9h-3Zm6 0-3 9 3 9h3l-3-9 3-9h-3Z"/></svg>';
|
|
414
|
-
|
|
415
477
|
// src/blocks/style-fields.ts
|
|
416
478
|
var BORDER_STYLE_LABELS = {
|
|
417
479
|
none: "None",
|
|
@@ -431,7 +493,7 @@ var TEXT_ALIGN = { id: "typography.textAlign", group: "typography", label: "Text
|
|
|
431
493
|
var BG_COLOR = { id: "decorations.backgroundColor", group: "decorations", label: "Background", control: "color", defaultValue: "transparent" };
|
|
432
494
|
var BORDER_RADIUS = { id: "decorations.borderRadius", group: "decorations", label: "Block corner radius", control: "corners", max: 48, defaultValue: "0px" };
|
|
433
495
|
var BORDER_WIDTH = { id: "decorations.borderWidth", group: "decorations", label: "Block border width", control: "range", unit: "px", min: 0, max: 12, defaultValue: "0px" };
|
|
434
|
-
var BORDER_COLOR = { id: "decorations.borderColor", group: "decorations", label: "Border", control: "color", defaultValue: "
|
|
496
|
+
var BORDER_COLOR = { id: "decorations.borderColor", group: "decorations", label: "Border", control: "color", defaultValue: "transparent" };
|
|
435
497
|
var BORDER_STYLE = {
|
|
436
498
|
id: "decorations.borderStyle",
|
|
437
499
|
group: "decorations",
|
|
@@ -470,18 +532,14 @@ var TYPOGRAPHY_FIELD_MAP = {
|
|
|
470
532
|
linkDecoration: LINK_DECORATION
|
|
471
533
|
};
|
|
472
534
|
var COMMON_STYLE_FIELDS = [PADDING, MARGIN, BLOCK_HEIGHT, BG_COLOR, BORDER_WIDTH, BORDER_STYLE, BORDER_COLOR, BORDER_RADIUS];
|
|
473
|
-
var
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
LIST_SPACING,
|
|
482
|
-
LIST_INDENT,
|
|
483
|
-
LINK_COLOR,
|
|
484
|
-
LINK_DECORATION
|
|
535
|
+
var WRAPPER_STYLE_FIELDS = [
|
|
536
|
+
__spreadProps(__spreadValues({}, PADDING), { label: "Block padding" }),
|
|
537
|
+
MARGIN,
|
|
538
|
+
__spreadProps(__spreadValues({}, BG_COLOR), { label: "Block background" }),
|
|
539
|
+
BORDER_WIDTH,
|
|
540
|
+
BORDER_STYLE,
|
|
541
|
+
__spreadProps(__spreadValues({}, BORDER_COLOR), { label: "Block border" }),
|
|
542
|
+
BORDER_RADIUS
|
|
485
543
|
];
|
|
486
544
|
function copyStyleField(field, overrides) {
|
|
487
545
|
const next = __spreadValues(__spreadValues({}, field), overrides);
|
|
@@ -527,19 +585,12 @@ var elementDecoration = createDecorationFields({
|
|
|
527
585
|
borderColor: { id: "element.borderColor", label: "Outline" },
|
|
528
586
|
borderRadius: { id: "element.borderRadius", label: "Corner radius" }
|
|
529
587
|
});
|
|
530
|
-
var buttonTypography = createTypographyFields({
|
|
531
|
-
fontFamily: { id: "element.fontFamily" },
|
|
532
|
-
fontSize: { id: "element.fontSize" },
|
|
533
|
-
fontWeight: { id: "element.fontWeight" },
|
|
534
|
-
lineHeight: { id: "element.lineHeight" },
|
|
535
|
-
color: { id: "element.color" }
|
|
536
|
-
});
|
|
537
588
|
var BUTTON_BOX_DEFAULTS = {
|
|
538
589
|
align: HORIZONTAL_ALIGNS.center,
|
|
539
|
-
backgroundColor: "#
|
|
590
|
+
backgroundColor: "#000000",
|
|
540
591
|
borderRadius: "8px",
|
|
541
592
|
borderWidth: "1px",
|
|
542
|
-
borderColor: "
|
|
593
|
+
borderColor: "transparent",
|
|
543
594
|
padding: "12px 32px"
|
|
544
595
|
};
|
|
545
596
|
var ELEMENT_BORDER_FIELDS = [
|
|
@@ -554,12 +605,7 @@ var BUTTON_STYLE_FIELDS = [
|
|
|
554
605
|
ELEMENT_ALIGN,
|
|
555
606
|
ELEMENT_WIDTH,
|
|
556
607
|
createDimensionFields({ padding: { id: "element.padding", label: "Button padding", defaultValue: BUTTON_BOX_DEFAULTS.padding } }).padding,
|
|
557
|
-
|
|
558
|
-
buttonTypography.fontSize,
|
|
559
|
-
buttonTypography.fontWeight,
|
|
560
|
-
buttonTypography.lineHeight,
|
|
561
|
-
buttonTypography.color,
|
|
562
|
-
createDecorationFields({ backgroundColor: { id: "element.backgroundColor", label: "Button", defaultValue: BUTTON_BOX_DEFAULTS.backgroundColor } }).backgroundColor,
|
|
608
|
+
createDecorationFields({ backgroundColor: { id: "element.backgroundColor", label: "Button fill", defaultValue: BUTTON_BOX_DEFAULTS.backgroundColor } }).backgroundColor,
|
|
563
609
|
...ELEMENT_BORDER_FIELDS.map((field) => __spreadProps(__spreadValues({}, field), {
|
|
564
610
|
defaultValue: field.id === "element.borderWidth" ? BUTTON_BOX_DEFAULTS.borderWidth : field.id === "element.borderColor" ? BUTTON_BOX_DEFAULTS.borderColor : field.id === "element.borderRadius" ? BUTTON_BOX_DEFAULTS.borderRadius : field.defaultValue
|
|
565
611
|
}))
|
|
@@ -576,44 +622,188 @@ var DIVIDER_STYLE_FIELDS = [
|
|
|
576
622
|
__spreadProps(__spreadValues({}, ELEMENT_BORDER_FIELDS[1]), { label: "Line style" })
|
|
577
623
|
];
|
|
578
624
|
|
|
625
|
+
// src/render/button.ts
|
|
626
|
+
function buttonContent(block) {
|
|
627
|
+
var _a;
|
|
628
|
+
return String((_a = block.fields.html) != null ? _a : "");
|
|
629
|
+
}
|
|
630
|
+
function buttonStyles(block, global) {
|
|
631
|
+
var _a;
|
|
632
|
+
const element = __spreadValues(__spreadValues({}, BUTTON_BOX_DEFAULTS), (_a = block.style) == null ? void 0 : _a.element);
|
|
633
|
+
const text = __spreadProps(__spreadValues({}, typographyStyle(void 0, global)), { textAlign: "center" });
|
|
634
|
+
const box = __spreadProps(__spreadValues({}, decorationStyle(element)), {
|
|
635
|
+
padding: cssSpacing(element.padding, BUTTON_BOX_DEFAULTS.padding),
|
|
636
|
+
textAlign: "center"
|
|
637
|
+
});
|
|
638
|
+
return { element, width: cssLength(element.width, "auto", true), text, box };
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// src/render/button-content.ts
|
|
642
|
+
import { parser } from "@lezer/html";
|
|
643
|
+
|
|
644
|
+
// src/render/template-tokens.ts
|
|
645
|
+
function protectTemplateTokens(html) {
|
|
646
|
+
var _a;
|
|
647
|
+
let prefix = "REE_TEMPLATE_TOKEN_";
|
|
648
|
+
while (html.includes(prefix)) prefix += "_";
|
|
649
|
+
const tokens = [];
|
|
650
|
+
let protectedHtml = "";
|
|
651
|
+
let cursor = 0;
|
|
652
|
+
while (cursor < html.length) {
|
|
653
|
+
const start = html.indexOf("{{", cursor);
|
|
654
|
+
if (start < 0) break;
|
|
655
|
+
let quote = "";
|
|
656
|
+
let comment = false;
|
|
657
|
+
let escaped = false;
|
|
658
|
+
let end = start + 2;
|
|
659
|
+
for (; end < html.length - 1; end += 1) {
|
|
660
|
+
const entity = html[end] === "&" ? (_a = /^&(?:#x[\da-f]+|#\d+|quot|apos);/i.exec(html.slice(end))) == null ? void 0 : _a[0] : void 0;
|
|
661
|
+
const char = decodeHtmlText(entity != null ? entity : html[end]);
|
|
662
|
+
if (comment) {
|
|
663
|
+
if (html.startsWith("*/", end)) {
|
|
664
|
+
comment = false;
|
|
665
|
+
end += 1;
|
|
666
|
+
}
|
|
667
|
+
} else if (quote) {
|
|
668
|
+
if (escaped) escaped = false;
|
|
669
|
+
else if (char === "\\" && quote !== "`") escaped = true;
|
|
670
|
+
else if (char === quote) quote = "";
|
|
671
|
+
} else if (html.startsWith("/*", end)) {
|
|
672
|
+
comment = true;
|
|
673
|
+
end += 1;
|
|
674
|
+
} else if (char === '"' || char === "'" || char === "`") {
|
|
675
|
+
quote = char;
|
|
676
|
+
} else if (html.startsWith("}}", end)) {
|
|
677
|
+
break;
|
|
678
|
+
}
|
|
679
|
+
if (entity) end += entity.length - 1;
|
|
680
|
+
}
|
|
681
|
+
if (end >= html.length - 1) break;
|
|
682
|
+
protectedHtml += html.slice(cursor, start) + `${prefix}${tokens.length}_END`;
|
|
683
|
+
tokens.push(decodeHtmlText(html.slice(start, end + 2)));
|
|
684
|
+
cursor = end + 2;
|
|
685
|
+
}
|
|
686
|
+
protectedHtml += html.slice(cursor);
|
|
687
|
+
return {
|
|
688
|
+
html: protectedHtml,
|
|
689
|
+
restore: (value) => value.replace(new RegExp(`${prefix}(\\d+)_END`, "g"), (token, index) => {
|
|
690
|
+
var _a2;
|
|
691
|
+
return (_a2 = tokens[Number(index)]) != null ? _a2 : token;
|
|
692
|
+
})
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// src/render/button-content.ts
|
|
697
|
+
var INLINE_TAGS = { strong: "strong", b: "strong", em: "em", i: "em", u: "u", span: "span" };
|
|
698
|
+
var OMIT_CONTENT = /* @__PURE__ */ new Set(["script", "style", "iframe", "object", "template"]);
|
|
699
|
+
var BLOCK_TAGS = /* @__PURE__ */ new Set(["p", "div", "li", "br", "h1", "h2", "h3", "h4", "h5", "h6"]);
|
|
700
|
+
function inlineStyle(value) {
|
|
701
|
+
return value.split(";").flatMap((declaration) => {
|
|
702
|
+
const separator = declaration.indexOf(":");
|
|
703
|
+
const name = declaration.slice(0, separator).trim().toLowerCase();
|
|
704
|
+
const raw = declaration.slice(separator + 1).trim();
|
|
705
|
+
let normalized = "";
|
|
706
|
+
if (name === "font-family") normalized = cssFont(raw, "");
|
|
707
|
+
if (name === "font-size") normalized = cssLength(raw, "");
|
|
708
|
+
if (name === "color") normalized = cssColor(raw, "");
|
|
709
|
+
if (name === "font-weight" && /^(normal|bold|[1-9]00)$/.test(raw)) normalized = raw;
|
|
710
|
+
if (name === "font-style" && /^(normal|italic)$/.test(raw)) normalized = raw;
|
|
711
|
+
if (name === "text-decoration" && /^(none|underline)$/.test(raw)) normalized = raw;
|
|
712
|
+
return normalized ? [`${name}:${normalized}`] : [];
|
|
713
|
+
}).join(";");
|
|
714
|
+
}
|
|
715
|
+
function buttonInlineHtml(html) {
|
|
716
|
+
const templates = protectTemplateTokens(html);
|
|
717
|
+
html = templates.html;
|
|
718
|
+
const source = (node) => html.slice(node.from, node.to);
|
|
719
|
+
const tagName = (node) => {
|
|
720
|
+
var _a, _b;
|
|
721
|
+
const tag = (_b = (_a = node.getChild("OpenTag")) != null ? _a : node.getChild("SelfClosingTag")) == null ? void 0 : _b.getChild("TagName");
|
|
722
|
+
return tag ? source(tag).toLowerCase() : "";
|
|
723
|
+
};
|
|
724
|
+
const read = (node) => {
|
|
725
|
+
var _a, _b;
|
|
726
|
+
if (node.name === "Text") return esc(source(node));
|
|
727
|
+
if (node.name === "EntityReference" || node.name === "CharacterReference") return source(node);
|
|
728
|
+
if (node.name !== "Element" && node.name !== "Document") return "";
|
|
729
|
+
const open = (_a = node.getChild("OpenTag")) != null ? _a : node.getChild("SelfClosingTag");
|
|
730
|
+
const tag = tagName(node);
|
|
731
|
+
if (OMIT_CONTENT.has(tag)) return "";
|
|
732
|
+
let content = "";
|
|
733
|
+
let previousBlock = false;
|
|
734
|
+
for (let child = node.firstChild; child; child = child.nextSibling) {
|
|
735
|
+
const next = read(child);
|
|
736
|
+
const block = BLOCK_TAGS.has(tagName(child));
|
|
737
|
+
if (next && content && (previousBlock || block)) content += " ";
|
|
738
|
+
content += next;
|
|
739
|
+
if (next) previousBlock = block;
|
|
740
|
+
}
|
|
741
|
+
const inline = INLINE_TAGS[tag];
|
|
742
|
+
let style = "";
|
|
743
|
+
for (const attribute of (_b = open == null ? void 0 : open.getChildren("Attribute")) != null ? _b : []) {
|
|
744
|
+
const name = attribute.getChild("AttributeName");
|
|
745
|
+
const value = attribute.getChild("AttributeValue");
|
|
746
|
+
if (name && value && source(name).toLowerCase() === "style") {
|
|
747
|
+
style = inlineStyle(source(value).replace(/^(["'])([\s\S]*)\1$/, "$2").replace(/"/gi, '"').replace(/'|'/gi, "'"));
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
const styled = style ? ` style="${esc(style)}"` : "";
|
|
751
|
+
if (inline) return `<${inline}${styled}>${content}</${inline}>`;
|
|
752
|
+
if (style) content = `<span${styled}>${content}</span>`;
|
|
753
|
+
return tag === "br" ? " " : content;
|
|
754
|
+
};
|
|
755
|
+
return templates.restore(read(parser.parse(html).topNode));
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// src/blocks/icons.ts
|
|
759
|
+
var ICON_TEXT = '<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M5 4h14v3H5V4Zm0 13h9v-3H5v3Z"/></svg>';
|
|
760
|
+
var ICON_HEADING = '<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M4 4h3v7h10V4h3v16h-3v-6H7v6H4V4Z"/></svg>';
|
|
761
|
+
var ICON_BUTTON = '<svg viewBox="0 0 24 24" width="18" height="18"><rect x="3" y="7" width="18" height="10" rx="2" fill="currentColor"/></svg>';
|
|
762
|
+
var ICON_IMAGE = '<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M4 5h16a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1Zm1.5 11 4-4 3 3 2.5-2.5 4 4V6.5h-13V16Z"/></svg>';
|
|
763
|
+
var ICON_DIVIDER = '<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M3 11h18v2H3v-2Z"/></svg>';
|
|
764
|
+
var ICON_SPACER = '<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M4 8h16v2H4V8Zm0 6h16v2H4v-2Z"/></svg>';
|
|
765
|
+
var ICON_HTML = '<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M5 3l-3 9 3 9h3l-3-9 3-9H5Zm6 0 3 9-3 9h3l3-9-3-9h-3Zm6 0-3 9 3 9h3l-3-9 3-9h-3Z"/></svg>';
|
|
766
|
+
|
|
579
767
|
// src/blocks/button.ts
|
|
580
768
|
var buttonBlock = {
|
|
769
|
+
exactStyleFields: true,
|
|
770
|
+
inspectorSections: BUTTON_SECTIONS,
|
|
581
771
|
type: BLOCK_TYPES.button,
|
|
582
772
|
title: "Button",
|
|
583
773
|
icon: ICON_BUTTON,
|
|
584
774
|
description: "Clickable CTA button",
|
|
775
|
+
inlineEdit: true,
|
|
776
|
+
inlineEditProfile: "button",
|
|
777
|
+
defaultFields: { html: '<p><span style="font-size:16px;color:#ffffff">Buy now</span></p>' },
|
|
585
778
|
fields: [
|
|
586
|
-
{ type: "
|
|
587
|
-
{ type: "url", name: "href", label: "Link", defaultValue: "https://example.com" }
|
|
779
|
+
{ type: "url", name: "href", label: "Link", defaultValue: "", placeholder: "https://example.com" }
|
|
588
780
|
],
|
|
589
781
|
defaultStyle: {
|
|
590
|
-
element: __spreadValues({
|
|
782
|
+
element: __spreadValues({}, BUTTON_BOX_DEFAULTS)
|
|
591
783
|
},
|
|
592
|
-
styleFields: [...
|
|
593
|
-
render: ({
|
|
594
|
-
var _a
|
|
595
|
-
const element =
|
|
596
|
-
const width = cssLength(element.width, "auto", true);
|
|
784
|
+
styleFields: [...WRAPPER_STYLE_FIELDS, ...BUTTON_STYLE_FIELDS],
|
|
785
|
+
render: ({ block, global }) => {
|
|
786
|
+
var _a;
|
|
787
|
+
const { element, width, text, box } = buttonStyles(block, global);
|
|
597
788
|
const px = pixelLength(width);
|
|
598
|
-
const
|
|
599
|
-
const
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
}));
|
|
603
|
-
const anchor = `<a href="${esc(String((_a = fields.href) != null ? _a : "#"))}" style="display:block;text-decoration:none;${text};">${esc(String((_b = fields.label) != null ? _b : "Button"))}</a>`;
|
|
604
|
-
const cell = `<td align="center"${px ? ` width="${px}"` : ""} style="${box};">${anchor}</td>`;
|
|
789
|
+
const href = String((_a = block.fields.href) != null ? _a : "").trim();
|
|
790
|
+
const tag = href ? "a" : "span";
|
|
791
|
+
const anchor = `<${tag}${href ? ` href="${esc(href)}"` : ""} style="display:block;text-decoration:none;${styleToCss(text)};">${buttonInlineHtml(buttonContent(block))}</${tag}>`;
|
|
792
|
+
const cell = `<td align="center"${px ? ` width="${px}"` : ""} style="${styleToCss(box)};">${anchor}</td>`;
|
|
605
793
|
return renderAligned("button", { align: element.align, width, content: cell });
|
|
606
794
|
}
|
|
607
795
|
};
|
|
608
796
|
|
|
609
797
|
// src/blocks/divider.ts
|
|
610
798
|
var dividerBlock = {
|
|
799
|
+
exactStyleFields: true,
|
|
800
|
+
inspectorSections: DIVIDER_SECTIONS,
|
|
611
801
|
type: BLOCK_TYPES.divider,
|
|
612
802
|
title: "Divider",
|
|
613
803
|
icon: ICON_DIVIDER,
|
|
614
804
|
description: "Horizontal divider line",
|
|
615
805
|
defaultStyle: { dimension: { padding: "12px" }, element: { borderColor: "#e3e3e5" } },
|
|
616
|
-
styleFields: [...
|
|
806
|
+
styleFields: [...WRAPPER_STYLE_FIELDS.filter((field) => !field.id.startsWith("decorations.border")), ...DIVIDER_STYLE_FIELDS, { id: "element.borderColor", group: "decorations", label: "Line", control: "color", defaultValue: "#e3e3e5" }],
|
|
617
807
|
render: ({ style }) => {
|
|
618
808
|
var _a;
|
|
619
809
|
const element = (_a = style.element) != null ? _a : {};
|
|
@@ -723,45 +913,45 @@ function styleRichText(html, style, global) {
|
|
|
723
913
|
|
|
724
914
|
// src/blocks/heading.ts
|
|
725
915
|
var headingBlock = {
|
|
916
|
+
exactStyleFields: true,
|
|
917
|
+
inspectorSections: HEADING_SECTIONS,
|
|
726
918
|
type: BLOCK_TYPES.heading,
|
|
727
919
|
title: "Heading",
|
|
728
920
|
icon: ICON_HEADING,
|
|
729
921
|
description: "Large section heading",
|
|
730
922
|
inlineEdit: true,
|
|
731
|
-
|
|
923
|
+
inlineEditProfile: "heading",
|
|
924
|
+
defaultFields: { html: '<p style="line-height:1.25"><span style="font-size:26px"><strong>Heading</strong></span></p>' },
|
|
732
925
|
defaultStyle: {
|
|
733
|
-
dimension: { padding: "16px" }
|
|
734
|
-
typography: {
|
|
735
|
-
fontSize: "26px",
|
|
736
|
-
fontWeight: "bold",
|
|
737
|
-
lineHeight: "1.25"
|
|
738
|
-
}
|
|
926
|
+
dimension: { padding: "16px" }
|
|
739
927
|
},
|
|
740
|
-
styleFields:
|
|
741
|
-
render: ({ fields,
|
|
928
|
+
styleFields: WRAPPER_STYLE_FIELDS,
|
|
929
|
+
render: ({ fields, global }) => {
|
|
742
930
|
var _a;
|
|
743
|
-
return `<div style="display:flow-root;${typographyCss(
|
|
931
|
+
return `<div style="display:flow-root;${typographyCss(void 0, global)}">${styleRichText(String((_a = fields.html) != null ? _a : ""), void 0, global)}</div>`;
|
|
744
932
|
}
|
|
745
933
|
};
|
|
746
934
|
|
|
747
935
|
// src/render/html-content.ts
|
|
748
|
-
function htmlContent(html,
|
|
936
|
+
function htmlContent(html, global, id) {
|
|
749
937
|
var _a, _b, _c, _d;
|
|
750
938
|
const scope = `ree-html-${id.replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
751
939
|
const body = html.match(/<body\b([^>]*)>([\s\S]*?)<\/body\s*>/i);
|
|
752
940
|
let marker = "";
|
|
753
|
-
let inner =
|
|
941
|
+
let inner = html;
|
|
754
942
|
if (body) {
|
|
755
943
|
const head = (_b = (_a = html.match(/<head\b[^>]*>([\s\S]*?)<\/head\s*>/i)) == null ? void 0 : _a[1]) != null ? _b : "";
|
|
756
944
|
const styles = (_d = (_c = head.match(/<style\b[^>]*>[\s\S]*?<\/style\s*>/gi)) == null ? void 0 : _c.join("\n")) != null ? _d : "";
|
|
757
945
|
marker = styles ? `<!--ree-head-->${styles}<!--/ree-head-->` : "";
|
|
758
|
-
inner = `<div${body[1]}>${
|
|
946
|
+
inner = `<div${body[1]}>${body[2]}</div>`;
|
|
759
947
|
}
|
|
760
|
-
return `${marker}<div class="${scope}" style="display:flow-root;${typographyCss(
|
|
948
|
+
return `${marker}<div class="${scope}" style="display:flow-root;${typographyCss(void 0, global)};">${inner}</div>`;
|
|
761
949
|
}
|
|
762
950
|
|
|
763
951
|
// src/blocks/html.ts
|
|
764
952
|
var htmlBlock = {
|
|
953
|
+
exactStyleFields: true,
|
|
954
|
+
inspectorSections: HTML_SECTIONS,
|
|
765
955
|
type: BLOCK_TYPES.html,
|
|
766
956
|
title: "HTML",
|
|
767
957
|
icon: ICON_HTML,
|
|
@@ -777,15 +967,17 @@ var htmlBlock = {
|
|
|
777
967
|
}
|
|
778
968
|
],
|
|
779
969
|
defaultStyle: { dimension: { padding: "8px" } },
|
|
780
|
-
styleFields:
|
|
781
|
-
render: ({ fields,
|
|
970
|
+
styleFields: WRAPPER_STYLE_FIELDS,
|
|
971
|
+
render: ({ fields, global, block }) => {
|
|
782
972
|
var _a;
|
|
783
|
-
return htmlContent(String((_a = fields.html) != null ? _a : ""),
|
|
973
|
+
return htmlContent(String((_a = fields.html) != null ? _a : ""), global, block.id);
|
|
784
974
|
}
|
|
785
975
|
};
|
|
786
976
|
|
|
787
977
|
// src/blocks/image.ts
|
|
788
978
|
var imageBlock = {
|
|
979
|
+
exactStyleFields: true,
|
|
980
|
+
inspectorSections: IMAGE_SECTIONS,
|
|
789
981
|
type: BLOCK_TYPES.image,
|
|
790
982
|
title: "Image",
|
|
791
983
|
icon: ICON_IMAGE,
|
|
@@ -793,10 +985,10 @@ var imageBlock = {
|
|
|
793
985
|
fields: [
|
|
794
986
|
{ type: "url", name: "src", label: "Image URL", defaultValue: "https://placehold.co/560x200?text=Image" },
|
|
795
987
|
{ type: "text", name: "alt", label: "Alt text", defaultValue: "Image" },
|
|
796
|
-
{ type: "url", name: "link", label: "Link", defaultValue: "" }
|
|
988
|
+
{ type: "url", name: "link", label: "Link", defaultValue: "", placeholder: "https://example.com" }
|
|
797
989
|
],
|
|
798
990
|
defaultStyle: { dimension: { padding: "8px" } },
|
|
799
|
-
styleFields: [...
|
|
991
|
+
styleFields: [...WRAPPER_STYLE_FIELDS, ELEMENT_ALIGN, ...IMAGE_STYLE_FIELDS],
|
|
800
992
|
render: ({ fields, style }) => {
|
|
801
993
|
var _a, _b, _c, _d;
|
|
802
994
|
const element = (_a = style.element) != null ? _a : {};
|
|
@@ -813,7 +1005,7 @@ var imageBlock = {
|
|
|
813
1005
|
const widthAttr = pixelLength(width);
|
|
814
1006
|
const heightAttr = pixelLength(height);
|
|
815
1007
|
const image = `<img src="${esc(String((_b = fields.src) != null ? _b : ""))}" alt="${esc(String((_c = fields.alt) != null ? _c : ""))}"${widthAttr ? ` width="${widthAttr}"` : ""}${heightAttr ? ` height="${heightAttr}"` : ""} border="0" style="outline:none;text-decoration:none;${css};" />`;
|
|
816
|
-
const link = String((_d = fields.link) != null ? _d : "");
|
|
1008
|
+
const link = String((_d = fields.link) != null ? _d : "").trim();
|
|
817
1009
|
const content = link ? `<a href="${esc(link)}" style="text-decoration:none;${box.display === "block" ? "display:block;" : ""}">${image}</a>` : image;
|
|
818
1010
|
return renderAligned("image", { align: element.align, width, content });
|
|
819
1011
|
}
|
|
@@ -829,30 +1021,35 @@ function spacerHtml(height, fallback = "0px") {
|
|
|
829
1021
|
|
|
830
1022
|
// src/blocks/spacer.ts
|
|
831
1023
|
var spacerBlock = {
|
|
1024
|
+
exactStyleFields: true,
|
|
1025
|
+
inspectorSections: SPACER_SECTIONS,
|
|
832
1026
|
type: BLOCK_TYPES.spacer,
|
|
833
1027
|
title: "Spacer",
|
|
834
1028
|
icon: ICON_SPACER,
|
|
835
1029
|
description: "Vertical whitespace",
|
|
836
1030
|
fields: [{ type: "number", name: "height", label: "Height", defaultValue: 16, min: 0, max: 240, step: 1 }],
|
|
837
|
-
styleFields:
|
|
1031
|
+
styleFields: [BG_COLOR],
|
|
838
1032
|
render: ({ fields }) => spacerHtml(fields.height, "16px")
|
|
839
1033
|
};
|
|
840
1034
|
|
|
841
1035
|
// src/blocks/text.ts
|
|
842
1036
|
var textBlock = {
|
|
1037
|
+
exactStyleFields: true,
|
|
1038
|
+
inspectorSections: TEXT_SECTIONS,
|
|
843
1039
|
type: BLOCK_TYPES.text,
|
|
844
1040
|
title: "Text",
|
|
845
1041
|
icon: ICON_TEXT,
|
|
846
1042
|
description: "Paragraph of body text",
|
|
847
1043
|
inlineEdit: true,
|
|
1044
|
+
inlineEditProfile: "text",
|
|
848
1045
|
defaultFields: { html: "<p>Type your paragraph here\u2026</p>" },
|
|
849
1046
|
defaultStyle: {
|
|
850
1047
|
dimension: { padding: "16px" }
|
|
851
1048
|
},
|
|
852
|
-
styleFields:
|
|
853
|
-
render: ({ fields,
|
|
1049
|
+
styleFields: WRAPPER_STYLE_FIELDS,
|
|
1050
|
+
render: ({ fields, global }) => {
|
|
854
1051
|
var _a;
|
|
855
|
-
return `<div style="display:flow-root;${typographyCss(
|
|
1052
|
+
return `<div style="display:flow-root;${typographyCss(void 0, global)}">${styleRichText(String((_a = fields.html) != null ? _a : ""), void 0, global)}</div>`;
|
|
856
1053
|
}
|
|
857
1054
|
};
|
|
858
1055
|
|
|
@@ -1049,7 +1246,9 @@ function EditorDnd({
|
|
|
1049
1246
|
children
|
|
1050
1247
|
}) {
|
|
1051
1248
|
const blocksRef = useRef(blocks);
|
|
1052
|
-
|
|
1249
|
+
useLayoutEffect(() => {
|
|
1250
|
+
blocksRef.current = blocks;
|
|
1251
|
+
}, [blocks]);
|
|
1053
1252
|
const [activeDrag, setActiveDrag] = useState(null);
|
|
1054
1253
|
const [indicator, setIndicator] = useState(null);
|
|
1055
1254
|
const sensors = useSensors(
|
|
@@ -1241,10 +1440,9 @@ function StyleFieldStack({ fields, renderField }) {
|
|
|
1241
1440
|
}
|
|
1242
1441
|
|
|
1243
1442
|
// src/inspector/StyleControl.tsx
|
|
1244
|
-
import { useId } from "react";
|
|
1443
|
+
import { useId as useId2 } from "react";
|
|
1245
1444
|
|
|
1246
1445
|
// src/inspector/constants.ts
|
|
1247
|
-
var HEX_COLOR = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
|
|
1248
1446
|
var TEXT_ALIGN_OPTIONS = [
|
|
1249
1447
|
{ value: HORIZONTAL_ALIGNS.left, label: "Left", glyph: "\u21E4" },
|
|
1250
1448
|
{ value: HORIZONTAL_ALIGNS.center, label: "Center", glyph: "\u21D4" },
|
|
@@ -1268,108 +1466,19 @@ function AlignControl({ value, onChange }) {
|
|
|
1268
1466
|
className: `ree-align-btn${value === option.value ? " is-active" : ""}`,
|
|
1269
1467
|
onClick: () => onChange(option.value),
|
|
1270
1468
|
title: option.label,
|
|
1469
|
+
"aria-label": option.label,
|
|
1470
|
+
"aria-pressed": value === option.value,
|
|
1271
1471
|
children: option.glyph
|
|
1272
1472
|
},
|
|
1273
1473
|
option.value
|
|
1274
1474
|
)) });
|
|
1275
1475
|
}
|
|
1276
1476
|
|
|
1277
|
-
// src/
|
|
1278
|
-
|
|
1279
|
-
return HEX_COLOR.test(value) ? value : fallback;
|
|
1280
|
-
}
|
|
1281
|
-
function styleReset(stored, restore, inherit) {
|
|
1282
|
-
if (inherit) return { resetTo: "", show: stored !== "" };
|
|
1283
|
-
return { resetTo: restore, show: stored !== "" && stored !== restore };
|
|
1284
|
-
}
|
|
1285
|
-
function inheritedStyleValue(id, global) {
|
|
1286
|
-
if (!id.startsWith("typography.") && !id.startsWith("element.")) return;
|
|
1287
|
-
switch (id.split(".")[1]) {
|
|
1288
|
-
case "fontFamily":
|
|
1289
|
-
return global.fontFamily;
|
|
1290
|
-
case "fontSize":
|
|
1291
|
-
return global.defaultFontSize;
|
|
1292
|
-
case "fontWeight":
|
|
1293
|
-
return global.fontWeight;
|
|
1294
|
-
case "lineHeight":
|
|
1295
|
-
return global.defaultLineHeight;
|
|
1296
|
-
case "color":
|
|
1297
|
-
return global.textColor;
|
|
1298
|
-
case "textAlign":
|
|
1299
|
-
return global.textAlign;
|
|
1300
|
-
case "paragraphSpacing":
|
|
1301
|
-
return global.paragraphSpacing;
|
|
1302
|
-
case "listSpacing":
|
|
1303
|
-
return global.listSpacing;
|
|
1304
|
-
case "listIndent":
|
|
1305
|
-
return global.listIndent;
|
|
1306
|
-
case "linkColor":
|
|
1307
|
-
return global.linkColor;
|
|
1308
|
-
case "linkDecoration":
|
|
1309
|
-
return global.linkDecoration;
|
|
1310
|
-
}
|
|
1311
|
-
}
|
|
1312
|
-
|
|
1313
|
-
// src/inspector/components/ColorControl.tsx
|
|
1314
|
-
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1315
|
-
function ColorControl({ value, fallback = "#000000", label = "Color", onChange }) {
|
|
1316
|
-
const actual = value || fallback;
|
|
1317
|
-
const empty = !actual || actual === "transparent" || !HEX_COLOR.test(actual);
|
|
1318
|
-
return /* @__PURE__ */ jsxs6("label", { className: "ree-color-chip", children: [
|
|
1319
|
-
/* @__PURE__ */ jsxs6("span", { className: "ree-color-swatch", children: [
|
|
1320
|
-
/* @__PURE__ */ jsx8(
|
|
1321
|
-
"span",
|
|
1322
|
-
{
|
|
1323
|
-
className: `ree-color-swatch-face${empty ? " is-empty" : ""}`,
|
|
1324
|
-
style: empty ? void 0 : { background: actual }
|
|
1325
|
-
}
|
|
1326
|
-
),
|
|
1327
|
-
/* @__PURE__ */ jsx8(
|
|
1328
|
-
"input",
|
|
1329
|
-
{
|
|
1330
|
-
type: "color",
|
|
1331
|
-
className: "ree-color-native",
|
|
1332
|
-
"aria-label": label,
|
|
1333
|
-
value: colorInputValue(actual),
|
|
1334
|
-
onChange: (event) => onChange(event.target.value)
|
|
1335
|
-
}
|
|
1336
|
-
)
|
|
1337
|
-
] }),
|
|
1338
|
-
/* @__PURE__ */ jsx8("span", { className: "ree-color-chip-label", children: label })
|
|
1339
|
-
] });
|
|
1340
|
-
}
|
|
1341
|
-
|
|
1342
|
-
// src/inspector/components/CssValueInput.tsx
|
|
1343
|
-
import { useEffect, useState as useState3 } from "react";
|
|
1344
|
-
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
1345
|
-
function CssValueInput({ label, value, placeholder: placeholder2, onChange }) {
|
|
1346
|
-
const [draft, setDraft] = useState3(value);
|
|
1347
|
-
useEffect(() => setDraft(value), [value]);
|
|
1348
|
-
return /* @__PURE__ */ jsx9(
|
|
1349
|
-
"input",
|
|
1350
|
-
{
|
|
1351
|
-
className: "ree-input",
|
|
1352
|
-
"aria-label": label,
|
|
1353
|
-
value: draft,
|
|
1354
|
-
placeholder: placeholder2 != null ? placeholder2 : "0px",
|
|
1355
|
-
onChange: (event) => setDraft(event.target.value),
|
|
1356
|
-
onBlur: () => {
|
|
1357
|
-
onChange(draft.trim());
|
|
1358
|
-
setDraft(value);
|
|
1359
|
-
},
|
|
1360
|
-
onKeyDown: (event) => {
|
|
1361
|
-
if (event.key === "Enter") event.currentTarget.blur();
|
|
1362
|
-
if (event.key === "Escape") {
|
|
1363
|
-
setDraft(value);
|
|
1364
|
-
event.stopPropagation();
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
);
|
|
1369
|
-
}
|
|
1477
|
+
// src/components/ColorPicker.tsx
|
|
1478
|
+
import { useState as useState3 } from "react";
|
|
1370
1479
|
|
|
1371
|
-
// src/
|
|
1372
|
-
import { jsx as
|
|
1480
|
+
// src/components/RangeControl.tsx
|
|
1481
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1373
1482
|
function RangeControl({
|
|
1374
1483
|
id,
|
|
1375
1484
|
label,
|
|
@@ -1380,7 +1489,7 @@ function RangeControl({
|
|
|
1380
1489
|
onChange
|
|
1381
1490
|
}) {
|
|
1382
1491
|
const progress = max > min ? Math.min(100, Math.max(0, (value - min) / (max - min) * 100)) : 0;
|
|
1383
|
-
return /* @__PURE__ */
|
|
1492
|
+
return /* @__PURE__ */ jsx8(
|
|
1384
1493
|
"input",
|
|
1385
1494
|
{
|
|
1386
1495
|
id,
|
|
@@ -1397,83 +1506,362 @@ function RangeControl({
|
|
|
1397
1506
|
);
|
|
1398
1507
|
}
|
|
1399
1508
|
|
|
1400
|
-
// src/
|
|
1401
|
-
import {
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1509
|
+
// src/components/TextInput.tsx
|
|
1510
|
+
import { forwardRef } from "react";
|
|
1511
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
1512
|
+
var TextInput = forwardRef(function TextInput2(_a, ref) {
|
|
1513
|
+
var _b = _a, { className = "", type = "text" } = _b, props = __objRest(_b, ["className", "type"]);
|
|
1514
|
+
return /* @__PURE__ */ jsx9("input", __spreadProps(__spreadValues({}, props), { ref, type, className: `ree-input${className ? ` ${className}` : ""}` }));
|
|
1515
|
+
});
|
|
1516
|
+
|
|
1517
|
+
// src/components/ResetButton.tsx
|
|
1518
|
+
import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1519
|
+
function ResetButton({ label, disabled = false, onClick }) {
|
|
1520
|
+
return /* @__PURE__ */ jsx10("button", { type: "button", className: "ree-reset", "aria-label": label, title: label, disabled, onClick, children: /* @__PURE__ */ jsxs6("svg", { viewBox: "0 0 16 16", width: "14", height: "14", "aria-hidden": "true", children: [
|
|
1521
|
+
/* @__PURE__ */ jsx10("path", { d: "M13 8a5 5 0 1 1-1.4-3.45", fill: "none", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round" }),
|
|
1522
|
+
/* @__PURE__ */ jsx10("path", { d: "M12.2 2.4v2.7h-2.7", fill: "none", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
1523
|
+
] }) });
|
|
1405
1524
|
}
|
|
1406
|
-
|
|
1525
|
+
|
|
1526
|
+
// src/components/color-picker/ColorSaturation.tsx
|
|
1527
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
1528
|
+
function ColorSaturation({ color, onChange }) {
|
|
1529
|
+
const update = (event) => {
|
|
1530
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
1531
|
+
if (!rect.width || !rect.height) return;
|
|
1532
|
+
onChange(__spreadProps(__spreadValues({}, color), {
|
|
1533
|
+
saturation: Math.min(100, Math.max(0, (event.clientX - rect.left) / rect.width * 100)),
|
|
1534
|
+
brightness: Math.min(100, Math.max(0, (1 - (event.clientY - rect.top) / rect.height) * 100))
|
|
1535
|
+
}));
|
|
1536
|
+
};
|
|
1537
|
+
return /* @__PURE__ */ jsx11(
|
|
1538
|
+
"div",
|
|
1539
|
+
{
|
|
1540
|
+
className: "ree-color-saturation",
|
|
1541
|
+
role: "slider",
|
|
1542
|
+
tabIndex: 0,
|
|
1543
|
+
"aria-label": "Saturation and brightness",
|
|
1544
|
+
"aria-valuemin": 0,
|
|
1545
|
+
"aria-valuemax": 100,
|
|
1546
|
+
"aria-valuenow": Math.round(color.saturation),
|
|
1547
|
+
"aria-valuetext": `Saturation ${Math.round(color.saturation)}%, brightness ${Math.round(color.brightness)}%. Use left/right for saturation and up/down for brightness.`,
|
|
1548
|
+
style: { "--ree-color-hue": `hsl(${color.hue} 100% 50%)` },
|
|
1549
|
+
onPointerDown: (event) => {
|
|
1550
|
+
if (event.button !== 0) return;
|
|
1551
|
+
event.currentTarget.focus({ preventScroll: true });
|
|
1552
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
1553
|
+
update(event);
|
|
1554
|
+
},
|
|
1555
|
+
onPointerMove: (event) => {
|
|
1556
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) update(event);
|
|
1557
|
+
},
|
|
1558
|
+
onPointerUp: (event) => {
|
|
1559
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
|
|
1560
|
+
},
|
|
1561
|
+
onKeyDown: (event) => {
|
|
1562
|
+
const step = event.shiftKey ? 10 : 1;
|
|
1563
|
+
let { saturation, brightness } = color;
|
|
1564
|
+
switch (event.key) {
|
|
1565
|
+
case "ArrowLeft":
|
|
1566
|
+
saturation -= step;
|
|
1567
|
+
break;
|
|
1568
|
+
case "ArrowRight":
|
|
1569
|
+
saturation += step;
|
|
1570
|
+
break;
|
|
1571
|
+
case "ArrowUp":
|
|
1572
|
+
brightness += step;
|
|
1573
|
+
break;
|
|
1574
|
+
case "ArrowDown":
|
|
1575
|
+
brightness -= step;
|
|
1576
|
+
break;
|
|
1577
|
+
case "Home":
|
|
1578
|
+
saturation = 0;
|
|
1579
|
+
break;
|
|
1580
|
+
case "End":
|
|
1581
|
+
saturation = 100;
|
|
1582
|
+
break;
|
|
1583
|
+
default:
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
event.preventDefault();
|
|
1587
|
+
onChange(__spreadProps(__spreadValues({}, color), {
|
|
1588
|
+
saturation: Math.min(100, Math.max(0, saturation)),
|
|
1589
|
+
brightness: Math.min(100, Math.max(0, brightness))
|
|
1590
|
+
}));
|
|
1591
|
+
},
|
|
1592
|
+
children: /* @__PURE__ */ jsx11("span", { className: "ree-color-cursor", style: { left: `${color.saturation}%`, top: `${100 - color.brightness}%` } })
|
|
1593
|
+
}
|
|
1594
|
+
);
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
// src/components/color-picker/colors.ts
|
|
1598
|
+
function normalizeHex(value) {
|
|
1599
|
+
const input = value.trim();
|
|
1600
|
+
const hex = input.startsWith("#") ? input : `#${input}`;
|
|
1601
|
+
if (!HEX_COLOR.test(hex)) return null;
|
|
1602
|
+
const digits = hex.slice(1).toLowerCase();
|
|
1603
|
+
return `#${digits.length === 3 ? [...digits].map((digit) => digit + digit).join("") : digits}`;
|
|
1604
|
+
}
|
|
1605
|
+
function colorToHex(value) {
|
|
1606
|
+
const hex = normalizeHex(value);
|
|
1607
|
+
if (hex) return hex;
|
|
1608
|
+
const rgb = /^rgb\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*\)$/i.exec(value.trim());
|
|
1609
|
+
if (!rgb) return null;
|
|
1610
|
+
const channels = rgb.slice(1).map(Number);
|
|
1611
|
+
if (channels.some((channel) => !Number.isFinite(channel) || channel < 0 || channel > 255)) return null;
|
|
1612
|
+
return `#${channels.map((channel) => Math.round(channel).toString(16).padStart(2, "0")).join("")}`;
|
|
1613
|
+
}
|
|
1614
|
+
function hexToHsv(hex) {
|
|
1407
1615
|
var _a;
|
|
1408
|
-
const
|
|
1409
|
-
|
|
1616
|
+
const normalized = (_a = normalizeHex(hex)) != null ? _a : "#000000";
|
|
1617
|
+
const [red, green, blue] = [1, 3, 5].map((offset) => Number.parseInt(normalized.slice(offset, offset + 2), 16) / 255);
|
|
1618
|
+
const max = Math.max(red, green, blue);
|
|
1619
|
+
const min = Math.min(red, green, blue);
|
|
1620
|
+
const delta = max - min;
|
|
1621
|
+
let hue = 0;
|
|
1622
|
+
if (delta) {
|
|
1623
|
+
if (max === red) hue = (green - blue) / delta % 6;
|
|
1624
|
+
else if (max === green) hue = (blue - red) / delta + 2;
|
|
1625
|
+
else hue = (red - green) / delta + 4;
|
|
1626
|
+
}
|
|
1627
|
+
return { hue: (hue * 60 + 360) % 360, saturation: max ? delta / max * 100 : 0, brightness: max * 100 };
|
|
1410
1628
|
}
|
|
1411
|
-
function
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
}
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
const
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
const
|
|
1430
|
-
|
|
1431
|
-
|
|
1629
|
+
function hsvToHex({ hue, saturation, brightness }) {
|
|
1630
|
+
const sector = (hue % 360 + 360) % 360 / 60;
|
|
1631
|
+
const value = Math.min(100, Math.max(0, brightness)) / 100;
|
|
1632
|
+
const chroma = value * Math.min(100, Math.max(0, saturation)) / 100;
|
|
1633
|
+
const secondary = chroma * (1 - Math.abs(sector % 2 - 1));
|
|
1634
|
+
const channels = sector < 1 ? [chroma, secondary, 0] : sector < 2 ? [secondary, chroma, 0] : sector < 3 ? [0, chroma, secondary] : sector < 4 ? [0, secondary, chroma] : sector < 5 ? [secondary, 0, chroma] : [chroma, 0, secondary];
|
|
1635
|
+
return `#${channels.map((channel) => Math.round((channel + value - chroma) * 255).toString(16).padStart(2, "0")).join("")}`;
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
// src/components/ColorPicker.tsx
|
|
1639
|
+
import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1640
|
+
function ColorPicker({ id, label, value, fallback = "#000000", panelRef, onChange, onReset, onClose }) {
|
|
1641
|
+
var _a, _b;
|
|
1642
|
+
const initial = (_b = (_a = colorToHex(value)) != null ? _a : colorToHex(fallback)) != null ? _b : "#000000";
|
|
1643
|
+
const [color, setColor] = useState3(() => hexToHsv(initial));
|
|
1644
|
+
const [draft, setDraft] = useState3(initial);
|
|
1645
|
+
const invalid = !normalizeHex(draft);
|
|
1646
|
+
const selectColor = (next) => {
|
|
1647
|
+
const hex = hsvToHex(next);
|
|
1648
|
+
setColor(next);
|
|
1649
|
+
setDraft(hex);
|
|
1650
|
+
onChange(hex);
|
|
1432
1651
|
};
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1652
|
+
const commitHex = () => {
|
|
1653
|
+
const hex = normalizeHex(draft);
|
|
1654
|
+
if (!hex) return;
|
|
1655
|
+
setDraft(hex);
|
|
1656
|
+
};
|
|
1657
|
+
return /* @__PURE__ */ jsxs7(
|
|
1658
|
+
"div",
|
|
1659
|
+
{
|
|
1660
|
+
ref: panelRef,
|
|
1661
|
+
id,
|
|
1662
|
+
className: "ree-color-picker",
|
|
1663
|
+
role: "dialog",
|
|
1664
|
+
"aria-label": `${label} picker`,
|
|
1665
|
+
onMouseDown: (event) => event.stopPropagation(),
|
|
1666
|
+
onPointerDown: (event) => event.stopPropagation(),
|
|
1667
|
+
onKeyDown: (event) => {
|
|
1668
|
+
if (event.key === "Escape") {
|
|
1669
|
+
event.preventDefault();
|
|
1670
|
+
event.stopPropagation();
|
|
1671
|
+
onClose();
|
|
1672
|
+
}
|
|
1673
|
+
},
|
|
1674
|
+
children: [
|
|
1675
|
+
/* @__PURE__ */ jsx12(ColorSaturation, { color, onChange: selectColor }),
|
|
1676
|
+
/* @__PURE__ */ jsx12("div", { className: "ree-color-hue", children: /* @__PURE__ */ jsx12(RangeControl, { label: "Hue", value: color.hue, min: 0, max: 360, onChange: (hue) => selectColor(__spreadProps(__spreadValues({}, color), { hue })) }) }),
|
|
1677
|
+
/* @__PURE__ */ jsxs7("div", { className: "ree-color-hex-row", children: [
|
|
1678
|
+
/* @__PURE__ */ jsx12("span", { className: "ree-color-preview", style: { backgroundColor: hsvToHex(color) } }),
|
|
1679
|
+
/* @__PURE__ */ jsxs7("label", { className: "ree-color-hex-field", children: [
|
|
1680
|
+
/* @__PURE__ */ jsx12("span", { children: "HEX" }),
|
|
1681
|
+
/* @__PURE__ */ jsx12(
|
|
1682
|
+
TextInput,
|
|
1683
|
+
{
|
|
1684
|
+
"aria-label": "HEX",
|
|
1685
|
+
"aria-invalid": invalid,
|
|
1686
|
+
"aria-describedby": invalid ? `${id}-error` : void 0,
|
|
1687
|
+
value: draft,
|
|
1688
|
+
autoComplete: "off",
|
|
1689
|
+
spellCheck: false,
|
|
1690
|
+
onChange: (event) => {
|
|
1691
|
+
const next = event.target.value;
|
|
1692
|
+
setDraft(next);
|
|
1693
|
+
const hex = normalizeHex(next);
|
|
1694
|
+
if (hex) {
|
|
1695
|
+
setColor(hexToHsv(hex));
|
|
1696
|
+
onChange(hex);
|
|
1697
|
+
}
|
|
1698
|
+
},
|
|
1699
|
+
onBlur: commitHex,
|
|
1700
|
+
onKeyDown: (event) => {
|
|
1701
|
+
if (event.key !== "Enter") return;
|
|
1702
|
+
event.preventDefault();
|
|
1703
|
+
event.stopPropagation();
|
|
1704
|
+
commitHex();
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
)
|
|
1708
|
+
] }),
|
|
1709
|
+
/* @__PURE__ */ jsx12(ResetButton, { label: `Reset ${label.toLowerCase()}`, onClick: () => {
|
|
1710
|
+
onReset();
|
|
1711
|
+
onClose();
|
|
1712
|
+
} })
|
|
1713
|
+
] }),
|
|
1714
|
+
invalid ? /* @__PURE__ */ jsx12("p", { id: `${id}-error`, className: "ree-color-error", role: "status", children: "Enter a 3 or 6 digit HEX color." }) : null
|
|
1715
|
+
]
|
|
1716
|
+
}
|
|
1717
|
+
);
|
|
1451
1718
|
}
|
|
1452
1719
|
|
|
1453
|
-
// src/
|
|
1454
|
-
import {
|
|
1455
|
-
function
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
]
|
|
1720
|
+
// src/components/color-picker/useColorPicker.ts
|
|
1721
|
+
import { useId, useLayoutEffect as useLayoutEffect2, useRef as useRef2, useState as useState4 } from "react";
|
|
1722
|
+
function useColorPicker(onOpen) {
|
|
1723
|
+
const id = useId();
|
|
1724
|
+
const anchorRef = useRef2(null);
|
|
1725
|
+
const panelRef = useRef2(null);
|
|
1726
|
+
const [open, setOpen] = useState4(false);
|
|
1727
|
+
useLayoutEffect2(() => {
|
|
1728
|
+
var _a;
|
|
1729
|
+
const panel = panelRef.current;
|
|
1730
|
+
const anchor = anchorRef.current;
|
|
1731
|
+
if (!open || !panel || !anchor) return;
|
|
1732
|
+
const ownerDocument = anchor.ownerDocument;
|
|
1733
|
+
const ownerWindow = ownerDocument.defaultView;
|
|
1734
|
+
if (!ownerWindow) return;
|
|
1735
|
+
const place = () => {
|
|
1736
|
+
const rect = anchor.getBoundingClientRect();
|
|
1737
|
+
const width = panel.offsetWidth;
|
|
1738
|
+
const height = panel.offsetHeight;
|
|
1739
|
+
const left = Math.max(8, Math.min(rect.left, ownerWindow.innerWidth - width - 8));
|
|
1740
|
+
const below = rect.bottom + 6;
|
|
1741
|
+
const top = below + height <= ownerWindow.innerHeight - 8 ? below : Math.max(8, rect.top - height - 6);
|
|
1742
|
+
panel.style.left = `${left}px`;
|
|
1743
|
+
panel.style.top = `${Math.min(top, Math.max(8, ownerWindow.innerHeight - height - 8))}px`;
|
|
1744
|
+
};
|
|
1745
|
+
const onToggle = () => {
|
|
1746
|
+
if (!panel.matches(":popover-open")) setOpen(false);
|
|
1747
|
+
};
|
|
1748
|
+
panel.setAttribute("popover", "auto");
|
|
1749
|
+
panel.addEventListener("toggle", onToggle);
|
|
1750
|
+
panel.showPopover();
|
|
1751
|
+
place();
|
|
1752
|
+
(_a = panel.querySelector('input[type="text"]')) == null ? void 0 : _a.focus({ preventScroll: true });
|
|
1753
|
+
const observer = new ResizeObserver(place);
|
|
1754
|
+
observer.observe(panel);
|
|
1755
|
+
ownerDocument.addEventListener("scroll", place, true);
|
|
1756
|
+
ownerWindow.addEventListener("resize", place);
|
|
1757
|
+
return () => {
|
|
1758
|
+
panel.removeEventListener("toggle", onToggle);
|
|
1759
|
+
panel.hidePopover();
|
|
1760
|
+
observer.disconnect();
|
|
1761
|
+
ownerDocument.removeEventListener("scroll", place, true);
|
|
1762
|
+
ownerWindow.removeEventListener("resize", place);
|
|
1763
|
+
};
|
|
1764
|
+
}, [open]);
|
|
1765
|
+
return {
|
|
1766
|
+
id,
|
|
1767
|
+
open,
|
|
1768
|
+
anchorRef,
|
|
1769
|
+
panelRef,
|
|
1770
|
+
toggle: () => {
|
|
1771
|
+
if (!open) onOpen == null ? void 0 : onOpen();
|
|
1772
|
+
setOpen(!open);
|
|
1773
|
+
},
|
|
1774
|
+
close: () => {
|
|
1775
|
+
var _a, _b;
|
|
1776
|
+
setOpen(false);
|
|
1777
|
+
(_b = (_a = anchorRef.current) == null ? void 0 : _a.querySelector("button")) == null ? void 0 : _b.focus({ preventScroll: true });
|
|
1778
|
+
}
|
|
1779
|
+
};
|
|
1460
1780
|
}
|
|
1461
1781
|
|
|
1462
|
-
// src/
|
|
1463
|
-
import { jsx as jsx13, jsxs as
|
|
1464
|
-
function
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1782
|
+
// src/components/ColorControl.tsx
|
|
1783
|
+
import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1784
|
+
function ColorControl({ value, fallback = "#000000", label = "Color", resetTo = fallback, onChange }) {
|
|
1785
|
+
const { id, open, anchorRef, panelRef, toggle, close } = useColorPicker();
|
|
1786
|
+
const actual = value || fallback;
|
|
1787
|
+
const normalized = colorToHex(actual);
|
|
1788
|
+
const empty = normalized === null;
|
|
1789
|
+
return /* @__PURE__ */ jsxs8("div", { className: "ree-color-control", ref: anchorRef, children: [
|
|
1790
|
+
/* @__PURE__ */ jsxs8(
|
|
1791
|
+
"button",
|
|
1468
1792
|
{
|
|
1793
|
+
type: "button",
|
|
1794
|
+
className: "ree-color-chip",
|
|
1469
1795
|
"aria-label": label,
|
|
1470
|
-
|
|
1471
|
-
|
|
1796
|
+
"aria-haspopup": "dialog",
|
|
1797
|
+
"aria-expanded": open,
|
|
1798
|
+
"aria-controls": open ? id : void 0,
|
|
1799
|
+
onClick: toggle,
|
|
1800
|
+
children: [
|
|
1801
|
+
/* @__PURE__ */ jsx13("span", { className: "ree-color-swatch", children: /* @__PURE__ */ jsx13(
|
|
1802
|
+
"span",
|
|
1803
|
+
{
|
|
1804
|
+
className: `ree-color-swatch-face${empty ? " is-empty" : ""}`,
|
|
1805
|
+
style: normalized ? { background: normalized } : void 0
|
|
1806
|
+
}
|
|
1807
|
+
) }),
|
|
1808
|
+
/* @__PURE__ */ jsx13("span", { className: "ree-color-chip-label", children: label })
|
|
1809
|
+
]
|
|
1810
|
+
}
|
|
1811
|
+
),
|
|
1812
|
+
open ? /* @__PURE__ */ jsx13(ColorPicker, { id, label, value: actual, fallback, panelRef, onChange, onReset: () => onChange(resetTo), onClose: close }) : null
|
|
1813
|
+
] });
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
// src/inspector/components/CssValueInput.tsx
|
|
1817
|
+
import { useState as useState5 } from "react";
|
|
1818
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
1819
|
+
function CssValueInput({ label, value, placeholder: placeholder2, onChange }) {
|
|
1820
|
+
const [draft, setDraft] = useState5(value);
|
|
1821
|
+
const [previousValue, setPreviousValue] = useState5(value);
|
|
1822
|
+
if (previousValue !== value) {
|
|
1823
|
+
setPreviousValue(value);
|
|
1824
|
+
setDraft(value);
|
|
1825
|
+
}
|
|
1826
|
+
return /* @__PURE__ */ jsx14(
|
|
1827
|
+
TextInput,
|
|
1828
|
+
{
|
|
1829
|
+
"aria-label": label,
|
|
1830
|
+
value: draft,
|
|
1831
|
+
placeholder: placeholder2 != null ? placeholder2 : "0px",
|
|
1832
|
+
onChange: (event) => setDraft(event.target.value),
|
|
1833
|
+
onBlur: () => {
|
|
1834
|
+
onChange(draft.trim());
|
|
1835
|
+
setDraft(value);
|
|
1836
|
+
},
|
|
1837
|
+
onKeyDown: (event) => {
|
|
1838
|
+
if (event.key === "Enter") event.currentTarget.blur();
|
|
1839
|
+
if (event.key === "Escape") {
|
|
1840
|
+
setDraft(value);
|
|
1841
|
+
event.stopPropagation();
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
);
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
// src/components/SelectControl.tsx
|
|
1849
|
+
import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1850
|
+
function SelectControl({ id, label, value, options, compact = false, disabled, onChange }) {
|
|
1851
|
+
return /* @__PURE__ */ jsxs9("span", { className: `ree-select${compact ? " is-compact" : ""}`, children: [
|
|
1852
|
+
/* @__PURE__ */ jsx15(
|
|
1853
|
+
"select",
|
|
1854
|
+
{
|
|
1855
|
+
id,
|
|
1856
|
+
"aria-label": label,
|
|
1857
|
+
disabled,
|
|
1858
|
+
className: "ree-input",
|
|
1859
|
+
value,
|
|
1472
1860
|
onChange: (event) => onChange(event.target.value),
|
|
1473
|
-
children: options.map((option) => /* @__PURE__ */
|
|
1861
|
+
children: options.map((option) => /* @__PURE__ */ jsx15("option", { value: option.value, children: option.label }, option.value))
|
|
1474
1862
|
}
|
|
1475
1863
|
),
|
|
1476
|
-
/* @__PURE__ */
|
|
1864
|
+
/* @__PURE__ */ jsx15("span", { className: "ree-select-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx15("svg", { viewBox: "0 0 16 16", width: "16", height: "16", fill: "none", children: /* @__PURE__ */ jsx15(
|
|
1477
1865
|
"path",
|
|
1478
1866
|
{
|
|
1479
1867
|
d: "M4 6.25 8 10.25 12 6.25",
|
|
@@ -1486,9 +1874,72 @@ function SelectControl({ label, value, options, onChange }) {
|
|
|
1486
1874
|
] });
|
|
1487
1875
|
}
|
|
1488
1876
|
|
|
1877
|
+
// src/inspector/components/LengthControl.tsx
|
|
1878
|
+
import { jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1879
|
+
function bounds(unit, min, max, step) {
|
|
1880
|
+
if (unit === "%") return { min: 0, max: 100, step: 1 };
|
|
1881
|
+
return { min, max, step };
|
|
1882
|
+
}
|
|
1883
|
+
function snap(value, step) {
|
|
1884
|
+
var _a;
|
|
1885
|
+
const places = ((_a = String(step).split(".")[1]) != null ? _a : "").length;
|
|
1886
|
+
return Number((Math.round(value / step) * step).toFixed(places));
|
|
1887
|
+
}
|
|
1888
|
+
function LengthControl({
|
|
1889
|
+
id,
|
|
1890
|
+
label,
|
|
1891
|
+
amount,
|
|
1892
|
+
unit,
|
|
1893
|
+
units,
|
|
1894
|
+
min = 0,
|
|
1895
|
+
max = 200,
|
|
1896
|
+
step = 1,
|
|
1897
|
+
onChange
|
|
1898
|
+
}) {
|
|
1899
|
+
const range = bounds(unit, min, max, step);
|
|
1900
|
+
const shown = Math.min(range.max, Math.max(range.min, amount));
|
|
1901
|
+
const commit = (nextAmount, nextUnit) => {
|
|
1902
|
+
if (nextUnit === "auto") {
|
|
1903
|
+
onChange("auto");
|
|
1904
|
+
return;
|
|
1905
|
+
}
|
|
1906
|
+
const nextRange = bounds(nextUnit, min, max, step);
|
|
1907
|
+
const snapped = snap(Math.min(nextRange.max, Math.max(nextRange.min, nextAmount)), nextRange.step);
|
|
1908
|
+
onChange(formatCssLength(snapped, nextUnit));
|
|
1909
|
+
};
|
|
1910
|
+
return /* @__PURE__ */ jsxs10("div", { className: "ree-range-row", children: [
|
|
1911
|
+
unit === "auto" ? null : /* @__PURE__ */ jsx16(RangeControl, { id, label, value: shown, min: range.min, max: range.max, step: range.step, onChange: (next) => commit(next, unit) }),
|
|
1912
|
+
units.length > 1 ? /* @__PURE__ */ jsx16(
|
|
1913
|
+
SelectControl,
|
|
1914
|
+
{
|
|
1915
|
+
compact: true,
|
|
1916
|
+
label: `${label} unit`,
|
|
1917
|
+
value: unit,
|
|
1918
|
+
options: units.map((option) => ({ value: option, label: option })),
|
|
1919
|
+
onChange: (nextUnit) => {
|
|
1920
|
+
if (nextUnit === unit) return;
|
|
1921
|
+
const seed = unit === "auto" ? nextUnit === "%" ? 100 : Math.min(max, 160) : amount;
|
|
1922
|
+
commit(seed, nextUnit);
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
) : null
|
|
1926
|
+
] });
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
// src/inspector/components/SpacingControl.tsx
|
|
1930
|
+
import { useState as useState6 } from "react";
|
|
1931
|
+
|
|
1932
|
+
// src/components/CheckboxControl.tsx
|
|
1933
|
+
import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1934
|
+
function CheckboxControl({ label, checked, disabled, className = "", onChange }) {
|
|
1935
|
+
return /* @__PURE__ */ jsxs11("label", { className: `ree-checkbox-control${className ? ` ${className}` : ""}`, children: [
|
|
1936
|
+
/* @__PURE__ */ jsx17("input", { type: "checkbox", checked, disabled, onChange: (event) => onChange(event.target.checked) }),
|
|
1937
|
+
/* @__PURE__ */ jsx17("span", { children: label })
|
|
1938
|
+
] });
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1489
1941
|
// src/inspector/components/SpacingControl.tsx
|
|
1490
|
-
import {
|
|
1491
|
-
import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1942
|
+
import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1492
1943
|
function sideAmount(value, max) {
|
|
1493
1944
|
return Math.min(max, Math.max(0, readCssLength(value, ["px"]).amount));
|
|
1494
1945
|
}
|
|
@@ -1505,37 +1956,35 @@ function SpacingControl({
|
|
|
1505
1956
|
var _a;
|
|
1506
1957
|
const sides = expand(value);
|
|
1507
1958
|
const equal = sides.every((side) => sideAmount(side, max) === sideAmount(sides[0], max));
|
|
1508
|
-
const [independent, setIndependent] =
|
|
1959
|
+
const [independent, setIndependent] = useState6(!equal);
|
|
1509
1960
|
const slider = (sideLabel, sideValue, commit) => {
|
|
1510
1961
|
const amount = sideAmount(sideValue, max);
|
|
1511
|
-
return /* @__PURE__ */
|
|
1512
|
-
/* @__PURE__ */
|
|
1513
|
-
/* @__PURE__ */
|
|
1514
|
-
/* @__PURE__ */
|
|
1962
|
+
return /* @__PURE__ */ jsxs12("div", { className: "ree-field", children: [
|
|
1963
|
+
/* @__PURE__ */ jsxs12("div", { className: "ree-field-label", children: [
|
|
1964
|
+
/* @__PURE__ */ jsx18("span", { children: sideLabel }),
|
|
1965
|
+
/* @__PURE__ */ jsxs12("span", { className: "ree-field-value", children: [
|
|
1515
1966
|
amount,
|
|
1516
1967
|
"px"
|
|
1517
1968
|
] })
|
|
1518
1969
|
] }),
|
|
1519
|
-
/* @__PURE__ */
|
|
1970
|
+
/* @__PURE__ */ jsx18(RangeControl, { label: sideLabel, value: amount, min: 0, max, step: 1, onChange: (next) => commit(`${next}px`) })
|
|
1520
1971
|
] }, sideLabel);
|
|
1521
1972
|
};
|
|
1522
|
-
return /* @__PURE__ */
|
|
1523
|
-
/* @__PURE__ */
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
}
|
|
1973
|
+
return /* @__PURE__ */ jsxs12("div", { className: "ree-spacing-control", children: [
|
|
1974
|
+
/* @__PURE__ */ jsx18(
|
|
1975
|
+
CheckboxControl,
|
|
1976
|
+
{
|
|
1977
|
+
className: "ree-spacing-link",
|
|
1978
|
+
label: linkLabel,
|
|
1979
|
+
checked: !independent && equal,
|
|
1980
|
+
onChange: (checked) => {
|
|
1981
|
+
var _a2;
|
|
1982
|
+
setIndependent(!checked);
|
|
1983
|
+
if (checked) onChange(`${sideAmount((_a2 = sides[0]) != null ? _a2 : "0px", max)}px`);
|
|
1534
1984
|
}
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
!independent && equal ? slider(uniformLabel != null ? uniformLabel : `${label} all sides`, (_a = sides[0]) != null ? _a : "0px", onChange) : /* @__PURE__ */ jsx14("div", { className: "ree-spacing-grid", children: sideLabels.map((side, index) => {
|
|
1985
|
+
}
|
|
1986
|
+
),
|
|
1987
|
+
!independent && equal ? slider(uniformLabel != null ? uniformLabel : `${label} all sides`, (_a = sides[0]) != null ? _a : "0px", onChange) : /* @__PURE__ */ jsx18("div", { className: "ree-spacing-grid", children: sideLabels.map((side, index) => {
|
|
1539
1988
|
var _a2;
|
|
1540
1989
|
return slider(side, (_a2 = sides[index]) != null ? _a2 : "0px", (next) => {
|
|
1541
1990
|
const updated = [...sides];
|
|
@@ -1547,7 +1996,7 @@ function SpacingControl({
|
|
|
1547
1996
|
}
|
|
1548
1997
|
|
|
1549
1998
|
// src/inspector/StyleControl.tsx
|
|
1550
|
-
import { jsx as
|
|
1999
|
+
import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1551
2000
|
function lengthUnits(config) {
|
|
1552
2001
|
var _a;
|
|
1553
2002
|
if ((_a = config.units) == null ? void 0 : _a.length) return config.units;
|
|
@@ -1556,7 +2005,7 @@ function lengthUnits(config) {
|
|
|
1556
2005
|
}
|
|
1557
2006
|
function StyleControl({ config, value, fallback, resetLabel = "Reset", resetTo, showReset, onChange }) {
|
|
1558
2007
|
var _a, _b;
|
|
1559
|
-
const id =
|
|
2008
|
+
const id = useId2();
|
|
1560
2009
|
const units = lengthUnits(config);
|
|
1561
2010
|
const stored = value === "auto" && units.length > 0 && !units.includes("auto") ? "" : value;
|
|
1562
2011
|
const actual = stored || fallback || config.defaultValue || "";
|
|
@@ -1564,24 +2013,24 @@ function StyleControl({ config, value, fallback, resetLabel = "Reset", resetTo,
|
|
|
1564
2013
|
const parsed = numeric ? readCssLength(actual, units) : null;
|
|
1565
2014
|
const options = (_a = config.options) != null ? _a : [];
|
|
1566
2015
|
const selectOptions = actual && !options.some((option) => option.value === actual) ? [{ label: actual, value: actual }, ...options] : options;
|
|
1567
|
-
const reset = (showReset != null ? showReset : Boolean(value)) ? /* @__PURE__ */
|
|
2016
|
+
const reset = (showReset != null ? showReset : Boolean(value)) ? /* @__PURE__ */ jsx19(ResetButton, { label: resetLabel, onClick: () => onChange(resetTo != null ? resetTo : "") }) : null;
|
|
1568
2017
|
if (config.control === "color") {
|
|
1569
|
-
return /* @__PURE__ */
|
|
1570
|
-
/* @__PURE__ */
|
|
2018
|
+
return /* @__PURE__ */ jsxs13("div", { className: "ree-color-chip-row", children: [
|
|
2019
|
+
/* @__PURE__ */ jsx19(ColorControl, { label: config.label, value: actual, resetTo: resetTo != null ? resetTo : "", onChange }),
|
|
1571
2020
|
reset
|
|
1572
2021
|
] });
|
|
1573
2022
|
}
|
|
1574
|
-
return /* @__PURE__ */
|
|
1575
|
-
/* @__PURE__ */
|
|
1576
|
-
/* @__PURE__ */
|
|
1577
|
-
/* @__PURE__ */
|
|
1578
|
-
parsed ? /* @__PURE__ */
|
|
2023
|
+
return /* @__PURE__ */ jsxs13("div", { className: "ree-field", children: [
|
|
2024
|
+
/* @__PURE__ */ jsxs13("div", { className: "ree-field-label", children: [
|
|
2025
|
+
/* @__PURE__ */ jsx19("label", { htmlFor: numeric ? id : void 0, children: config.label }),
|
|
2026
|
+
/* @__PURE__ */ jsxs13("span", { className: "ree-field-meta", children: [
|
|
2027
|
+
parsed ? /* @__PURE__ */ jsx19("span", { className: "ree-field-value", children: formatCssLength(parsed.amount, parsed.unit) }) : null,
|
|
1579
2028
|
reset
|
|
1580
2029
|
] })
|
|
1581
2030
|
] }),
|
|
1582
|
-
config.control === "alignment" ? /* @__PURE__ */
|
|
1583
|
-
config.control === "select" ? /* @__PURE__ */
|
|
1584
|
-
numeric && parsed ? /* @__PURE__ */
|
|
2031
|
+
config.control === "alignment" ? /* @__PURE__ */ jsx19(AlignControl, { value: actual, onChange }) : null,
|
|
2032
|
+
config.control === "select" ? /* @__PURE__ */ jsx19(SelectControl, { label: config.label, value: actual, options: selectOptions, onChange }) : null,
|
|
2033
|
+
numeric && parsed ? /* @__PURE__ */ jsx19(
|
|
1585
2034
|
LengthControl,
|
|
1586
2035
|
{
|
|
1587
2036
|
id,
|
|
@@ -1595,7 +2044,7 @@ function StyleControl({ config, value, fallback, resetLabel = "Reset", resetTo,
|
|
|
1595
2044
|
onChange
|
|
1596
2045
|
}
|
|
1597
2046
|
) : null,
|
|
1598
|
-
config.control === "text" ? /* @__PURE__ */
|
|
2047
|
+
config.control === "text" ? /* @__PURE__ */ jsx19(
|
|
1599
2048
|
CssValueInput,
|
|
1600
2049
|
{
|
|
1601
2050
|
label: config.label,
|
|
@@ -1606,8 +2055,8 @@ function StyleControl({ config, value, fallback, resetLabel = "Reset", resetTo,
|
|
|
1606
2055
|
}
|
|
1607
2056
|
}
|
|
1608
2057
|
) : null,
|
|
1609
|
-
config.control === "spacing" ? /* @__PURE__ */
|
|
1610
|
-
config.control === "corners" ? /* @__PURE__ */
|
|
2058
|
+
config.control === "spacing" ? /* @__PURE__ */ jsx19(SpacingControl, { label: config.label, value: actual, onChange }) : null,
|
|
2059
|
+
config.control === "corners" ? /* @__PURE__ */ jsx19(
|
|
1611
2060
|
SpacingControl,
|
|
1612
2061
|
{
|
|
1613
2062
|
label: config.label,
|
|
@@ -1624,14 +2073,14 @@ function StyleControl({ config, value, fallback, resetLabel = "Reset", resetTo,
|
|
|
1624
2073
|
}
|
|
1625
2074
|
|
|
1626
2075
|
// src/inspector/GlobalFields.tsx
|
|
1627
|
-
import { jsx as
|
|
2076
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
1628
2077
|
function GlobalFields({ global, onChange }) {
|
|
1629
2078
|
const settings = resolveEmailGlobal(global);
|
|
1630
2079
|
const renderField = (field) => {
|
|
1631
2080
|
const key = field.id;
|
|
1632
2081
|
const current = String(settings[key]);
|
|
1633
2082
|
const initial = String(DEFAULT_EMAIL_GLOBAL[key]);
|
|
1634
|
-
return /* @__PURE__ */
|
|
2083
|
+
return /* @__PURE__ */ jsx20(
|
|
1635
2084
|
StyleControl,
|
|
1636
2085
|
{
|
|
1637
2086
|
config: field,
|
|
@@ -1645,7 +2094,7 @@ function GlobalFields({ global, onChange }) {
|
|
|
1645
2094
|
key
|
|
1646
2095
|
);
|
|
1647
2096
|
};
|
|
1648
|
-
return /* @__PURE__ */
|
|
2097
|
+
return /* @__PURE__ */ jsx20("div", { className: "ree-inspector ree-inspector-global", children: GLOBAL_FIELD_SECTIONS.map((section) => /* @__PURE__ */ jsx20(InspectorSection, { title: section.title, children: /* @__PURE__ */ jsx20(StyleFieldStack, { fields: section.fields, renderField }) }, section.title)) });
|
|
1649
2098
|
}
|
|
1650
2099
|
|
|
1651
2100
|
// src/constants/style-groups.ts
|
|
@@ -1656,11 +2105,44 @@ var STYLE_GROUPS = [
|
|
|
1656
2105
|
{ group: "decorations", label: "Decoration" }
|
|
1657
2106
|
];
|
|
1658
2107
|
|
|
2108
|
+
// src/inspector/utils.ts
|
|
2109
|
+
function styleReset(stored, restore, inherit) {
|
|
2110
|
+
if (inherit) return { resetTo: "", show: stored !== "" };
|
|
2111
|
+
return { resetTo: restore, show: stored !== "" && stored !== restore };
|
|
2112
|
+
}
|
|
2113
|
+
function inheritedStyleValue(id, global) {
|
|
2114
|
+
if (!id.startsWith("typography.") && !id.startsWith("element.")) return;
|
|
2115
|
+
switch (id.split(".")[1]) {
|
|
2116
|
+
case "fontFamily":
|
|
2117
|
+
return global.fontFamily;
|
|
2118
|
+
case "fontSize":
|
|
2119
|
+
return global.defaultFontSize;
|
|
2120
|
+
case "fontWeight":
|
|
2121
|
+
return global.fontWeight;
|
|
2122
|
+
case "lineHeight":
|
|
2123
|
+
return global.defaultLineHeight;
|
|
2124
|
+
case "color":
|
|
2125
|
+
return global.textColor;
|
|
2126
|
+
case "textAlign":
|
|
2127
|
+
return global.textAlign;
|
|
2128
|
+
case "paragraphSpacing":
|
|
2129
|
+
return global.paragraphSpacing;
|
|
2130
|
+
case "listSpacing":
|
|
2131
|
+
return global.listSpacing;
|
|
2132
|
+
case "listIndent":
|
|
2133
|
+
return global.listIndent;
|
|
2134
|
+
case "linkColor":
|
|
2135
|
+
return global.linkColor;
|
|
2136
|
+
case "linkDecoration":
|
|
2137
|
+
return global.linkDecoration;
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
|
|
1659
2141
|
// src/inspector/FieldControl.tsx
|
|
1660
|
-
import { useRef as
|
|
2142
|
+
import { useRef as useRef4 } from "react";
|
|
1661
2143
|
|
|
1662
2144
|
// src/inspector/HtmlCodeEditor.tsx
|
|
1663
|
-
import { useEffect as
|
|
2145
|
+
import { useEffect, useLayoutEffect as useLayoutEffect3, useRef as useRef3 } from "react";
|
|
1664
2146
|
import { EditorState as EditorState2 } from "@codemirror/state";
|
|
1665
2147
|
import { EditorView as EditorView2 } from "@codemirror/view";
|
|
1666
2148
|
|
|
@@ -1799,7 +2281,7 @@ function createHtmlEditorExtensions(placeholderText, onChange) {
|
|
|
1799
2281
|
}
|
|
1800
2282
|
|
|
1801
2283
|
// src/inspector/HtmlCodeEditor.tsx
|
|
1802
|
-
import { jsx as
|
|
2284
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
1803
2285
|
var LINE_HEIGHT2 = 22;
|
|
1804
2286
|
function HtmlCodeEditor({
|
|
1805
2287
|
value,
|
|
@@ -1807,11 +2289,13 @@ function HtmlCodeEditor({
|
|
|
1807
2289
|
rows = 10,
|
|
1808
2290
|
onChange
|
|
1809
2291
|
}) {
|
|
1810
|
-
const hostRef =
|
|
1811
|
-
const viewRef =
|
|
1812
|
-
const onChangeRef =
|
|
1813
|
-
|
|
1814
|
-
|
|
2292
|
+
const hostRef = useRef3(null);
|
|
2293
|
+
const viewRef = useRef3(null);
|
|
2294
|
+
const onChangeRef = useRef3(onChange);
|
|
2295
|
+
useLayoutEffect3(() => {
|
|
2296
|
+
onChangeRef.current = onChange;
|
|
2297
|
+
}, [onChange]);
|
|
2298
|
+
useEffect(() => {
|
|
1815
2299
|
const parent = hostRef.current;
|
|
1816
2300
|
if (!parent) return;
|
|
1817
2301
|
const view = new EditorView2({
|
|
@@ -1829,7 +2313,7 @@ function HtmlCodeEditor({
|
|
|
1829
2313
|
viewRef.current = null;
|
|
1830
2314
|
};
|
|
1831
2315
|
}, []);
|
|
1832
|
-
|
|
2316
|
+
useEffect(() => {
|
|
1833
2317
|
const view = viewRef.current;
|
|
1834
2318
|
if (!view) return;
|
|
1835
2319
|
const current = view.state.doc.toString();
|
|
@@ -1839,7 +2323,7 @@ function HtmlCodeEditor({
|
|
|
1839
2323
|
});
|
|
1840
2324
|
}, [value]);
|
|
1841
2325
|
const minHeight = Math.max(rows, 8) * LINE_HEIGHT2;
|
|
1842
|
-
return /* @__PURE__ */
|
|
2326
|
+
return /* @__PURE__ */ jsx21(
|
|
1843
2327
|
"div",
|
|
1844
2328
|
{
|
|
1845
2329
|
ref: hostRef,
|
|
@@ -1850,23 +2334,25 @@ function HtmlCodeEditor({
|
|
|
1850
2334
|
}
|
|
1851
2335
|
|
|
1852
2336
|
// src/inspector/VariablesDropdown.tsx
|
|
1853
|
-
import { useState as
|
|
1854
|
-
import { jsx as
|
|
2337
|
+
import { useState as useState7 } from "react";
|
|
2338
|
+
import { jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1855
2339
|
function VariablesDropdown({ variables, onInsert }) {
|
|
1856
|
-
const [open, setOpen] =
|
|
2340
|
+
const [open, setOpen] = useState7(false);
|
|
1857
2341
|
if (variables.length === 0) return null;
|
|
1858
|
-
return /* @__PURE__ */
|
|
1859
|
-
/* @__PURE__ */
|
|
2342
|
+
return /* @__PURE__ */ jsxs14("div", { className: "ree-var-wrap", children: [
|
|
2343
|
+
/* @__PURE__ */ jsx22(
|
|
1860
2344
|
"button",
|
|
1861
2345
|
{
|
|
1862
2346
|
type: "button",
|
|
1863
2347
|
className: "ree-var-btn",
|
|
1864
2348
|
onClick: () => setOpen((current) => !current),
|
|
1865
2349
|
title: "Insert variable",
|
|
2350
|
+
"aria-label": "Insert variable",
|
|
2351
|
+
"aria-expanded": open,
|
|
1866
2352
|
children: "{ }"
|
|
1867
2353
|
}
|
|
1868
2354
|
),
|
|
1869
|
-
open ? /* @__PURE__ */
|
|
2355
|
+
open ? /* @__PURE__ */ jsx22("div", { className: "ree-var-menu", children: variables.map((variable) => /* @__PURE__ */ jsxs14(
|
|
1870
2356
|
"button",
|
|
1871
2357
|
{
|
|
1872
2358
|
type: "button",
|
|
@@ -1876,8 +2362,8 @@ function VariablesDropdown({ variables, onInsert }) {
|
|
|
1876
2362
|
setOpen(false);
|
|
1877
2363
|
},
|
|
1878
2364
|
children: [
|
|
1879
|
-
/* @__PURE__ */
|
|
1880
|
-
variable.description ? /* @__PURE__ */
|
|
2365
|
+
/* @__PURE__ */ jsx22("span", { className: "ree-var-name", children: variable.name }),
|
|
2366
|
+
variable.description ? /* @__PURE__ */ jsx22("span", { className: "ree-var-label", children: variable.description }) : null
|
|
1881
2367
|
]
|
|
1882
2368
|
},
|
|
1883
2369
|
variable.name
|
|
@@ -1886,15 +2372,15 @@ function VariablesDropdown({ variables, onInsert }) {
|
|
|
1886
2372
|
}
|
|
1887
2373
|
|
|
1888
2374
|
// src/inspector/FieldControl.tsx
|
|
1889
|
-
import { jsx as
|
|
2375
|
+
import { jsx as jsx23, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1890
2376
|
function FieldControl({
|
|
1891
2377
|
field,
|
|
1892
2378
|
value,
|
|
1893
2379
|
variables,
|
|
1894
2380
|
onChange
|
|
1895
2381
|
}) {
|
|
1896
|
-
var _a, _b, _c, _d, _e;
|
|
1897
|
-
const ref =
|
|
2382
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2383
|
+
const ref = useRef4(null);
|
|
1898
2384
|
const stringValue = value == null ? "" : String(value);
|
|
1899
2385
|
const insertVariable = (token) => {
|
|
1900
2386
|
var _a2, _b2;
|
|
@@ -1908,14 +2394,14 @@ function FieldControl({
|
|
|
1908
2394
|
onChange(stringValue + token);
|
|
1909
2395
|
};
|
|
1910
2396
|
const handleChange = (event) => onChange(event.target.value);
|
|
1911
|
-
const label = /* @__PURE__ */
|
|
1912
|
-
/* @__PURE__ */
|
|
1913
|
-
field.type === "text" || field.type === "textarea" || field.type === "url" ? /* @__PURE__ */
|
|
2397
|
+
const label = /* @__PURE__ */ jsxs15("div", { className: "ree-field-label", children: [
|
|
2398
|
+
/* @__PURE__ */ jsx23("span", { children: field.label }),
|
|
2399
|
+
field.type === "text" || field.type === "textarea" || field.type === "url" ? /* @__PURE__ */ jsx23(VariablesDropdown, { variables, onInsert: insertVariable }) : null
|
|
1914
2400
|
] });
|
|
1915
2401
|
if (field.type === "html") {
|
|
1916
|
-
return /* @__PURE__ */
|
|
1917
|
-
/* @__PURE__ */
|
|
1918
|
-
/* @__PURE__ */
|
|
2402
|
+
return /* @__PURE__ */ jsxs15("div", { className: "ree-field", children: [
|
|
2403
|
+
/* @__PURE__ */ jsx23("div", { className: "ree-field-label", children: /* @__PURE__ */ jsx23("span", { children: field.label }) }),
|
|
2404
|
+
/* @__PURE__ */ jsx23(
|
|
1919
2405
|
HtmlCodeEditor,
|
|
1920
2406
|
{
|
|
1921
2407
|
value: stringValue,
|
|
@@ -1927,9 +2413,9 @@ function FieldControl({
|
|
|
1927
2413
|
] });
|
|
1928
2414
|
}
|
|
1929
2415
|
if (field.type === "textarea") {
|
|
1930
|
-
return /* @__PURE__ */
|
|
2416
|
+
return /* @__PURE__ */ jsxs15("div", { className: "ree-field", children: [
|
|
1931
2417
|
label,
|
|
1932
|
-
/* @__PURE__ */
|
|
2418
|
+
/* @__PURE__ */ jsx23(
|
|
1933
2419
|
"textarea",
|
|
1934
2420
|
{
|
|
1935
2421
|
ref: (element) => {
|
|
@@ -1945,11 +2431,12 @@ function FieldControl({
|
|
|
1945
2431
|
] });
|
|
1946
2432
|
}
|
|
1947
2433
|
if (field.type === "select") {
|
|
1948
|
-
return /* @__PURE__ */
|
|
1949
|
-
/* @__PURE__ */
|
|
1950
|
-
/* @__PURE__ */
|
|
2434
|
+
return /* @__PURE__ */ jsxs15("div", { className: "ree-field", children: [
|
|
2435
|
+
/* @__PURE__ */ jsx23("label", { className: "ree-field-label", children: field.label }),
|
|
2436
|
+
/* @__PURE__ */ jsx23(
|
|
1951
2437
|
SelectControl,
|
|
1952
2438
|
{
|
|
2439
|
+
label: field.label,
|
|
1953
2440
|
value: stringValue,
|
|
1954
2441
|
options: (_b = field.options) != null ? _b : [],
|
|
1955
2442
|
onChange
|
|
@@ -1958,32 +2445,31 @@ function FieldControl({
|
|
|
1958
2445
|
] });
|
|
1959
2446
|
}
|
|
1960
2447
|
if (field.type === "color") {
|
|
1961
|
-
return /* @__PURE__ */
|
|
2448
|
+
return /* @__PURE__ */ jsx23("div", { className: "ree-color-chip-row", children: /* @__PURE__ */ jsx23(ColorControl, { label: field.label, value: stringValue, resetTo: String((_c = field.defaultValue) != null ? _c : ""), onChange: (next) => onChange(next) }) });
|
|
1962
2449
|
}
|
|
1963
2450
|
if (field.type === "number") {
|
|
1964
2451
|
const amount = Number(value);
|
|
1965
|
-
const max = (
|
|
1966
|
-
const shown = Math.min(max, Math.max((
|
|
1967
|
-
return /* @__PURE__ */
|
|
1968
|
-
/* @__PURE__ */
|
|
1969
|
-
/* @__PURE__ */
|
|
1970
|
-
/* @__PURE__ */
|
|
2452
|
+
const max = (_d = field.max) != null ? _d : 200;
|
|
2453
|
+
const shown = Math.min(max, Math.max((_e = field.min) != null ? _e : 0, Number.isFinite(amount) ? amount : (_f = field.min) != null ? _f : 0));
|
|
2454
|
+
return /* @__PURE__ */ jsxs15("div", { className: "ree-field", children: [
|
|
2455
|
+
/* @__PURE__ */ jsxs15("div", { className: "ree-field-label", children: [
|
|
2456
|
+
/* @__PURE__ */ jsx23("span", { children: field.label }),
|
|
2457
|
+
/* @__PURE__ */ jsxs15("span", { className: "ree-field-value", children: [
|
|
1971
2458
|
shown,
|
|
1972
2459
|
"px"
|
|
1973
2460
|
] })
|
|
1974
2461
|
] }),
|
|
1975
|
-
/* @__PURE__ */
|
|
2462
|
+
/* @__PURE__ */ jsx23(RangeControl, { label: field.label, value: shown, min: field.min, max, step: field.step, onChange })
|
|
1976
2463
|
] });
|
|
1977
2464
|
}
|
|
1978
|
-
return /* @__PURE__ */
|
|
2465
|
+
return /* @__PURE__ */ jsxs15("div", { className: "ree-field", children: [
|
|
1979
2466
|
label,
|
|
1980
|
-
/* @__PURE__ */
|
|
1981
|
-
|
|
2467
|
+
/* @__PURE__ */ jsx23(
|
|
2468
|
+
TextInput,
|
|
1982
2469
|
{
|
|
1983
2470
|
ref: (element) => {
|
|
1984
2471
|
ref.current = element;
|
|
1985
2472
|
},
|
|
1986
|
-
className: "ree-input",
|
|
1987
2473
|
"aria-label": field.label,
|
|
1988
2474
|
min: field.min,
|
|
1989
2475
|
max: field.max,
|
|
@@ -1998,7 +2484,7 @@ function FieldControl({
|
|
|
1998
2484
|
}
|
|
1999
2485
|
|
|
2000
2486
|
// src/inspector/InspectorFields.tsx
|
|
2001
|
-
import { jsx as
|
|
2487
|
+
import { jsx as jsx24, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2002
2488
|
function InspectorFields({
|
|
2003
2489
|
block,
|
|
2004
2490
|
definition,
|
|
@@ -2015,7 +2501,7 @@ function InspectorFields({
|
|
|
2015
2501
|
const inherited = inheritedStyleValue(field.id, global);
|
|
2016
2502
|
const restore = readStyleValue(definition.defaultStyle, field.id) || field.defaultValue || "";
|
|
2017
2503
|
const reset = styleReset(stored, restore, inherited !== void 0);
|
|
2018
|
-
return /* @__PURE__ */
|
|
2504
|
+
return /* @__PURE__ */ jsx24(
|
|
2019
2505
|
StyleControl,
|
|
2020
2506
|
{
|
|
2021
2507
|
config: field,
|
|
@@ -2029,35 +2515,52 @@ function InspectorFields({
|
|
|
2029
2515
|
field.id
|
|
2030
2516
|
);
|
|
2031
2517
|
};
|
|
2032
|
-
const sections = STYLE_GROUPS.map((group) =>
|
|
2033
|
-
|
|
2518
|
+
const sections = (_c = definition.inspectorSections) != null ? _c : STYLE_GROUPS.map((group) => ({
|
|
2519
|
+
title: group.label,
|
|
2520
|
+
fieldNames: group.group === "general" ? contentFields.map((field) => field.name) : [],
|
|
2521
|
+
styleIds: styleFields.filter((field) => field.group === group.group).map((field) => field.id)
|
|
2034
2522
|
}));
|
|
2035
|
-
const
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2523
|
+
const resolveStyles = (ids = []) => ids.flatMap((id) => {
|
|
2524
|
+
const field = styleFields.find((item) => item.id === id);
|
|
2525
|
+
return field ? [field] : [];
|
|
2526
|
+
});
|
|
2527
|
+
return /* @__PURE__ */ jsxs16("div", { className: "ree-inspector", children: [
|
|
2528
|
+
/* @__PURE__ */ jsxs16("div", { className: "ree-inspector-head", children: [
|
|
2529
|
+
/* @__PURE__ */ jsx24(BlockIcon, { icon: definition.icon, className: "ree-inspector-icon" }),
|
|
2530
|
+
/* @__PURE__ */ jsx24("span", { className: "ree-inspector-title", children: definition.title })
|
|
2041
2531
|
] }),
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2532
|
+
sections.map((section) => {
|
|
2533
|
+
var _a2;
|
|
2534
|
+
const fields = ((_a2 = section.fieldNames) != null ? _a2 : []).flatMap((name) => {
|
|
2535
|
+
const field = contentFields.find((item) => item.name === name);
|
|
2536
|
+
return field ? [field] : [];
|
|
2537
|
+
});
|
|
2538
|
+
const styles = resolveStyles(section.styleIds);
|
|
2539
|
+
const advanced = resolveStyles(section.advancedStyleIds);
|
|
2540
|
+
if (!fields.length && !styles.length && !advanced.length) return null;
|
|
2541
|
+
return /* @__PURE__ */ jsxs16(InspectorSection, { title: section.title, children: [
|
|
2542
|
+
fields.map((field) => /* @__PURE__ */ jsx24(
|
|
2543
|
+
FieldControl,
|
|
2544
|
+
{
|
|
2545
|
+
field,
|
|
2546
|
+
value: block.fields[field.name],
|
|
2547
|
+
variables,
|
|
2548
|
+
onChange: (value) => onCommitField(block.id, field.name, value)
|
|
2549
|
+
},
|
|
2550
|
+
field.name
|
|
2551
|
+
)),
|
|
2552
|
+
/* @__PURE__ */ jsx24(StyleFieldStack, { fields: styles, renderField: renderStyle }),
|
|
2553
|
+
advanced.length ? /* @__PURE__ */ jsxs16("details", { className: "ree-inspector-advanced", children: [
|
|
2554
|
+
/* @__PURE__ */ jsx24("summary", { children: "Advanced block border" }),
|
|
2555
|
+
/* @__PURE__ */ jsx24("div", { className: "ree-inspector-section-body", children: /* @__PURE__ */ jsx24(StyleFieldStack, { fields: advanced, renderField: renderStyle }) })
|
|
2556
|
+
] }) : null
|
|
2557
|
+
] }, `${block.type}-${section.title}`);
|
|
2558
|
+
})
|
|
2056
2559
|
] });
|
|
2057
2560
|
}
|
|
2058
2561
|
|
|
2059
2562
|
// src/inspector/Inspector.tsx
|
|
2060
|
-
import { jsx as
|
|
2563
|
+
import { jsx as jsx25, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2061
2564
|
function Inspector({
|
|
2062
2565
|
panel,
|
|
2063
2566
|
global,
|
|
@@ -2070,9 +2573,9 @@ function Inspector({
|
|
|
2070
2573
|
onCommitStyle
|
|
2071
2574
|
}) {
|
|
2072
2575
|
const showBlock = panel === "block" && block && definition;
|
|
2073
|
-
return /* @__PURE__ */
|
|
2074
|
-
/* @__PURE__ */
|
|
2075
|
-
/* @__PURE__ */
|
|
2576
|
+
return /* @__PURE__ */ jsxs17("aside", { className: "ree-col ree-col-right", children: [
|
|
2577
|
+
/* @__PURE__ */ jsx25("div", { className: "ree-col-head ree-inspector-bar", children: /* @__PURE__ */ jsxs17("div", { className: "ree-inspector-tabs", role: "group", "aria-label": "Inspector panel", children: [
|
|
2578
|
+
/* @__PURE__ */ jsx25(
|
|
2076
2579
|
"button",
|
|
2077
2580
|
{
|
|
2078
2581
|
type: "button",
|
|
@@ -2082,19 +2585,18 @@ function Inspector({
|
|
|
2082
2585
|
children: "GLOBAL"
|
|
2083
2586
|
}
|
|
2084
2587
|
),
|
|
2085
|
-
/* @__PURE__ */
|
|
2588
|
+
block && definition ? /* @__PURE__ */ jsx25(
|
|
2086
2589
|
"button",
|
|
2087
2590
|
{
|
|
2088
2591
|
type: "button",
|
|
2089
2592
|
className: `ree-inspector-tab${showBlock ? " is-active" : ""}`,
|
|
2090
2593
|
"aria-pressed": Boolean(showBlock),
|
|
2091
|
-
disabled: !block || !definition,
|
|
2092
2594
|
onClick: () => onPanelChange("block"),
|
|
2093
2595
|
children: "BLOCK"
|
|
2094
2596
|
}
|
|
2095
|
-
)
|
|
2597
|
+
) : null
|
|
2096
2598
|
] }) }),
|
|
2097
|
-
/* @__PURE__ */
|
|
2599
|
+
/* @__PURE__ */ jsx25("div", { className: "ree-inspector-scroll", children: showBlock ? /* @__PURE__ */ jsx25(
|
|
2098
2600
|
InspectorFields,
|
|
2099
2601
|
{
|
|
2100
2602
|
block,
|
|
@@ -2104,7 +2606,7 @@ function Inspector({
|
|
|
2104
2606
|
onCommitField,
|
|
2105
2607
|
onCommitStyle
|
|
2106
2608
|
}
|
|
2107
|
-
) : /* @__PURE__ */
|
|
2609
|
+
) : /* @__PURE__ */ jsx25(GlobalFields, { global, onChange: onCommitGlobal }) })
|
|
2108
2610
|
] });
|
|
2109
2611
|
}
|
|
2110
2612
|
|
|
@@ -2139,16 +2641,29 @@ function viewportWidth(device, global) {
|
|
|
2139
2641
|
}
|
|
2140
2642
|
|
|
2141
2643
|
// src/stage/canvas/CanvasBlock.tsx
|
|
2644
|
+
import { useId as useId5, useState as useState12 } from "react";
|
|
2142
2645
|
import { useSortable } from "@dnd-kit/sortable";
|
|
2143
2646
|
import { CSS } from "@dnd-kit/utilities";
|
|
2144
2647
|
|
|
2648
|
+
// src/document/block-style.ts
|
|
2649
|
+
function resolveBlockStyle(style, definition) {
|
|
2650
|
+
let result = {};
|
|
2651
|
+
if (!definition) return result;
|
|
2652
|
+
for (const field of resolveBlockStyleFields(definition.styleFields, definition.exactStyleFields)) {
|
|
2653
|
+
const value = readStyleValue(style, field.id);
|
|
2654
|
+
if (value) result = writeStyleValue(result, field.id, value);
|
|
2655
|
+
}
|
|
2656
|
+
return result;
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2145
2659
|
// src/render/context.ts
|
|
2146
|
-
function buildRenderContext(node, mode, global = DEFAULT_EMAIL_GLOBAL) {
|
|
2147
|
-
var _a
|
|
2660
|
+
function buildRenderContext(node, mode, global = DEFAULT_EMAIL_GLOBAL, definition = getBlockDefinition(node.type)) {
|
|
2661
|
+
var _a;
|
|
2662
|
+
const block = __spreadProps(__spreadValues({}, node), { style: resolveBlockStyle(node.style, definition) });
|
|
2148
2663
|
return {
|
|
2149
|
-
block
|
|
2664
|
+
block,
|
|
2150
2665
|
fields: (_a = node.fields) != null ? _a : {},
|
|
2151
|
-
style:
|
|
2666
|
+
style: block.style,
|
|
2152
2667
|
mode,
|
|
2153
2668
|
global
|
|
2154
2669
|
};
|
|
@@ -2363,31 +2878,38 @@ function renderBlockContent(node, definitions = DEFAULT_BLOCKS, mode = "email",
|
|
|
2363
2878
|
}
|
|
2364
2879
|
function renderBlock(node, definitions = DEFAULT_BLOCKS, mode = "email", global = DEFAULT_EMAIL_GLOBAL) {
|
|
2365
2880
|
const definition = getBlockDefinition(node.type, definitions);
|
|
2366
|
-
return definition ? definition.render(buildRenderContext(node, mode, global)) : `<!-- unknown block type: ${node.type} -->`;
|
|
2881
|
+
return definition ? definition.render(buildRenderContext(node, mode, global, definition)) : `<!-- unknown block type: ${node.type} -->`;
|
|
2367
2882
|
}
|
|
2368
2883
|
function blockToHtml(node, definitions = DEFAULT_BLOCKS, mode = "email", options = {}) {
|
|
2369
|
-
var _a, _b, _c
|
|
2884
|
+
var _a, _b, _c;
|
|
2370
2885
|
const normalizedOptions = normalizeBlockRenderOptions(options);
|
|
2371
2886
|
const global = (_a = normalizedOptions.global) != null ? _a : DEFAULT_EMAIL_GLOBAL;
|
|
2887
|
+
const style = resolveBlockStyle(node.style, getBlockDefinition(node.type, definitions));
|
|
2372
2888
|
const inner = renderBlockContent(node, definitions, mode, __spreadProps(__spreadValues({}, normalizedOptions), {
|
|
2373
2889
|
global
|
|
2374
2890
|
}));
|
|
2375
|
-
const height = cssPx((
|
|
2891
|
+
const height = cssPx((_b = style.dimension) == null ? void 0 : _b.height);
|
|
2376
2892
|
const cellAttrs = height ? ` height="${esc(height)}" valign="top"` : "";
|
|
2377
|
-
return `<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="table-layout:fixed;"><tr><td style="padding:${esc(cssSpacing((
|
|
2893
|
+
return `<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="table-layout:fixed;"><tr><td style="padding:${esc(cssSpacing((_c = style.dimension) == null ? void 0 : _c.margin))};"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="table-layout:fixed;"><tr><td${cellAttrs} style="${blockCellCss(style, global)}">${inner}</td></tr></table></td></tr></table>`;
|
|
2378
2894
|
}
|
|
2379
2895
|
|
|
2380
2896
|
// src/stage/rich-text/RichText.tsx
|
|
2381
|
-
import { useEffect as
|
|
2897
|
+
import { useEffect as useEffect4, useLayoutEffect as useLayoutEffect6, useRef as useRef9, useState as useState11 } from "react";
|
|
2382
2898
|
import { createPortal } from "react-dom";
|
|
2383
2899
|
import { EditorContent, useEditor } from "@tiptap/react";
|
|
2384
2900
|
|
|
2385
2901
|
// src/stage/rich-text/components/RichTextToolbar.tsx
|
|
2386
|
-
import { useEffect as
|
|
2902
|
+
import { useEffect as useEffect3, useRef as useRef8, useState as useState10 } from "react";
|
|
2903
|
+
import { isAllowedUri as isAllowedUri2 } from "@tiptap/extension-link";
|
|
2387
2904
|
import { useEditorState } from "@tiptap/react";
|
|
2388
2905
|
|
|
2389
2906
|
// src/stage/rich-text/constants.ts
|
|
2390
2907
|
var PARAGRAPH_SPACING_MAX = 48;
|
|
2908
|
+
var DEFAULT_LINK_TARGET = "_blank";
|
|
2909
|
+
var LINK_TARGETS = [
|
|
2910
|
+
{ value: "_self", label: "Current page" },
|
|
2911
|
+
{ value: "_blank", label: "New page" }
|
|
2912
|
+
];
|
|
2391
2913
|
var LINE_HEIGHT_RANGE = { min: 1, max: 2.5, step: 0.05 };
|
|
2392
2914
|
var FONT_SIZES = [
|
|
2393
2915
|
"12px",
|
|
@@ -2403,17 +2925,6 @@ var FONT_SIZES = [
|
|
|
2403
2925
|
"36px",
|
|
2404
2926
|
"48px"
|
|
2405
2927
|
];
|
|
2406
|
-
var TEXT_COLORS = [
|
|
2407
|
-
"#202223",
|
|
2408
|
-
"#303030",
|
|
2409
|
-
"#6d7175",
|
|
2410
|
-
"#d72c0d",
|
|
2411
|
-
"#b98900",
|
|
2412
|
-
"#008060",
|
|
2413
|
-
"#005bd3",
|
|
2414
|
-
"#6d28d9",
|
|
2415
|
-
"#ffffff"
|
|
2416
|
-
];
|
|
2417
2928
|
var EMOJIS = [
|
|
2418
2929
|
"\u{1F600}",
|
|
2419
2930
|
"\u{1F601}",
|
|
@@ -2456,10 +2967,11 @@ var TEXT_ALIGNS2 = [
|
|
|
2456
2967
|
];
|
|
2457
2968
|
|
|
2458
2969
|
// src/stage/rich-text/useAnchoredToolbar.ts
|
|
2459
|
-
import { useLayoutEffect, useRef as
|
|
2460
|
-
function useAnchoredToolbar(anchorRef, active) {
|
|
2461
|
-
const toolbarRef =
|
|
2462
|
-
|
|
2970
|
+
import { useLayoutEffect as useLayoutEffect4, useRef as useRef5 } from "react";
|
|
2971
|
+
function useAnchoredToolbar(anchorRef, active, compact = false) {
|
|
2972
|
+
const toolbarRef = useRef5(null);
|
|
2973
|
+
useLayoutEffect4(() => {
|
|
2974
|
+
var _a;
|
|
2463
2975
|
if (!active) return;
|
|
2464
2976
|
const anchor = anchorRef.current;
|
|
2465
2977
|
const toolbar = toolbarRef.current;
|
|
@@ -2468,8 +2980,8 @@ function useAnchoredToolbar(anchorRef, active) {
|
|
|
2468
2980
|
const ownerWindow = ownerDocument.defaultView;
|
|
2469
2981
|
if (!ownerWindow) return;
|
|
2470
2982
|
const place = () => {
|
|
2471
|
-
var
|
|
2472
|
-
const block = (
|
|
2983
|
+
var _a2;
|
|
2984
|
+
const block = (_a2 = anchor.closest(".ree-block")) != null ? _a2 : anchor;
|
|
2473
2985
|
const rect = block.getBoundingClientRect();
|
|
2474
2986
|
const offscreen = rect.width === 0 || rect.height === 0 || rect.bottom < 0 || rect.top > ownerWindow.innerHeight;
|
|
2475
2987
|
if (offscreen) {
|
|
@@ -2477,7 +2989,13 @@ function useAnchoredToolbar(anchorRef, active) {
|
|
|
2477
2989
|
return;
|
|
2478
2990
|
}
|
|
2479
2991
|
const width = Math.min(rect.width, ownerWindow.innerWidth - 16);
|
|
2480
|
-
toolbar.style.width = `${width}px`;
|
|
2992
|
+
toolbar.style.width = compact ? "max-content" : `${width}px`;
|
|
2993
|
+
toolbar.style.maxWidth = `${width}px`;
|
|
2994
|
+
let previousBottom = null;
|
|
2995
|
+
for (const group of toolbar.querySelectorAll(":scope > .ree-rte-group")) {
|
|
2996
|
+
group.classList.toggle("is-row-start", previousBottom === null || group.offsetTop >= previousBottom);
|
|
2997
|
+
previousBottom = group.offsetTop + group.offsetHeight;
|
|
2998
|
+
}
|
|
2481
2999
|
const height = toolbar.offsetHeight;
|
|
2482
3000
|
let top = rect.top - height - 4;
|
|
2483
3001
|
if (top < 8) {
|
|
@@ -2485,7 +3003,7 @@ function useAnchoredToolbar(anchorRef, active) {
|
|
|
2485
3003
|
top = below + height <= ownerWindow.innerHeight - 8 ? below : 8;
|
|
2486
3004
|
}
|
|
2487
3005
|
let left = rect.left;
|
|
2488
|
-
const maxLeft = Math.max(8, ownerWindow.innerWidth -
|
|
3006
|
+
const maxLeft = Math.max(8, ownerWindow.innerWidth - toolbar.offsetWidth - 8);
|
|
2489
3007
|
if (left > maxLeft) left = maxLeft;
|
|
2490
3008
|
if (left < 8) left = 8;
|
|
2491
3009
|
toolbar.style.top = `${top}px`;
|
|
@@ -2495,6 +3013,8 @@ function useAnchoredToolbar(anchorRef, active) {
|
|
|
2495
3013
|
place();
|
|
2496
3014
|
const observer = new ResizeObserver(place);
|
|
2497
3015
|
observer.observe(toolbar);
|
|
3016
|
+
for (const group of toolbar.querySelectorAll(".ree-rte-group")) observer.observe(group);
|
|
3017
|
+
observer.observe((_a = anchor.closest(".ree-block")) != null ? _a : anchor);
|
|
2498
3018
|
ownerDocument.addEventListener("scroll", place, true);
|
|
2499
3019
|
ownerWindow.addEventListener("resize", place);
|
|
2500
3020
|
return () => {
|
|
@@ -2502,7 +3022,7 @@ function useAnchoredToolbar(anchorRef, active) {
|
|
|
2502
3022
|
ownerDocument.removeEventListener("scroll", place, true);
|
|
2503
3023
|
ownerWindow.removeEventListener("resize", place);
|
|
2504
3024
|
};
|
|
2505
|
-
}, [active, anchorRef]);
|
|
3025
|
+
}, [active, anchorRef, compact]);
|
|
2506
3026
|
return toolbarRef;
|
|
2507
3027
|
}
|
|
2508
3028
|
|
|
@@ -2514,15 +3034,11 @@ function textAttribute(value) {
|
|
|
2514
3034
|
function markWeight(value) {
|
|
2515
3035
|
return value === FONT_WEIGHTS.normal || value === FONT_WEIGHTS.bold ? value : "";
|
|
2516
3036
|
}
|
|
2517
|
-
function isVisuallyBold(markWeightValue, strong, inherited) {
|
|
2518
|
-
if (strong || markWeightValue === FONT_WEIGHTS.bold) return true;
|
|
2519
|
-
if (markWeightValue === FONT_WEIGHTS.normal) return false;
|
|
2520
|
-
return inherited === FONT_WEIGHTS.bold;
|
|
2521
|
-
}
|
|
2522
3037
|
function readToolbarState(editor) {
|
|
2523
3038
|
var _a;
|
|
2524
3039
|
const textStyle = editor.getAttributes("textStyle");
|
|
2525
3040
|
const link = editor.getAttributes("link");
|
|
3041
|
+
const list = editor.getAttributes(editor.isActive("orderedList") ? "orderedList" : "bulletList");
|
|
2526
3042
|
return {
|
|
2527
3043
|
bold: editor.isActive("bold"),
|
|
2528
3044
|
italic: editor.isActive("italic"),
|
|
@@ -2536,87 +3052,85 @@ function readToolbarState(editor) {
|
|
|
2536
3052
|
fontWeight: markWeight(textStyle.fontWeight),
|
|
2537
3053
|
color: textAttribute(textStyle.color),
|
|
2538
3054
|
linkHref: textAttribute(link.href),
|
|
3055
|
+
linkTarget: textAttribute(link.target),
|
|
3056
|
+
linkColor: textAttribute(link.color),
|
|
3057
|
+
linkDecoration: textAttribute(link.decoration),
|
|
3058
|
+
listSpacing: textAttribute(list.listSpacing),
|
|
3059
|
+
listIndent: textAttribute(list.listIndent),
|
|
2539
3060
|
spacing: textAttribute(editor.getAttributes("paragraph").spacing),
|
|
2540
3061
|
lineHeight: textAttribute(editor.getAttributes("paragraph").lineHeight)
|
|
2541
3062
|
};
|
|
2542
3063
|
}
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
chain.setMark("textStyle", { fontWeight: FONT_WEIGHTS.normal }).run();
|
|
2550
|
-
return;
|
|
2551
|
-
}
|
|
2552
|
-
chain.setMark("textStyle", { fontWeight: null }).removeEmptyTextStyle().run();
|
|
2553
|
-
return;
|
|
2554
|
-
}
|
|
2555
|
-
if (inherited === FONT_WEIGHTS.bold) {
|
|
2556
|
-
chain.setMark("textStyle", { fontWeight: null }).removeEmptyTextStyle().run();
|
|
2557
|
-
return;
|
|
2558
|
-
}
|
|
2559
|
-
chain.setMark("textStyle", { fontWeight: FONT_WEIGHTS.bold }).run();
|
|
2560
|
-
}
|
|
2561
|
-
function toColorInputValue(value, fallback) {
|
|
2562
|
-
return /^#[0-9a-fA-F]{6}$/.test(value) ? value : fallback;
|
|
3064
|
+
|
|
3065
|
+
// src/stage/rich-text/formatting.ts
|
|
3066
|
+
import { getAttributes, isMarkActive } from "@tiptap/react";
|
|
3067
|
+
function currentSelection(editor) {
|
|
3068
|
+
const { selection, storedMarks } = editor.state;
|
|
3069
|
+
return { anchor: selection.anchor, head: selection.head, storedMarks };
|
|
2563
3070
|
}
|
|
2564
|
-
function focusSelection(editor, range) {
|
|
2565
|
-
const chain = editor.chain().focus();
|
|
3071
|
+
function focusSelection(editor, range, focus = true) {
|
|
3072
|
+
const chain = focus ? editor.chain().focus() : editor.chain();
|
|
2566
3073
|
if (!range) return chain;
|
|
2567
|
-
return chain.
|
|
3074
|
+
return chain.command(({ tr, commands }) => {
|
|
3075
|
+
if (tr.selection.anchor !== range.anchor || tr.selection.head !== range.head) {
|
|
3076
|
+
commands.setTextSelection({ from: range.anchor, to: range.head });
|
|
3077
|
+
}
|
|
3078
|
+
if (tr.selection.empty) tr.setStoredMarks(range.storedMarks);
|
|
3079
|
+
return true;
|
|
3080
|
+
});
|
|
2568
3081
|
}
|
|
2569
|
-
function
|
|
2570
|
-
const
|
|
2571
|
-
return {
|
|
3082
|
+
function formatText(editor, command, range, scope = "text", focus = true) {
|
|
3083
|
+
const chain = focusSelection(editor, range, focus);
|
|
3084
|
+
return chain.command(({ tr, chain: scopedChain }) => {
|
|
3085
|
+
const selection = tr.selection;
|
|
3086
|
+
const pendingMarks = tr.storedMarks;
|
|
3087
|
+
const bookmark = selection.getBookmark();
|
|
3088
|
+
const scoped = scopedChain();
|
|
3089
|
+
if (selection.empty && (scope === "paragraph" || tr.doc.textContent)) scoped.selectAll();
|
|
3090
|
+
return command(scoped).command(({ tr: formatted }) => {
|
|
3091
|
+
const marks = formatted.storedMarks;
|
|
3092
|
+
formatted.setSelection(bookmark.map(formatted.mapping).resolve(formatted.doc));
|
|
3093
|
+
if (!formatted.doc.textContent) formatted.setStoredMarks(scope === "paragraph" ? pendingMarks : marks);
|
|
3094
|
+
return true;
|
|
3095
|
+
}).run();
|
|
3096
|
+
}).run();
|
|
2572
3097
|
}
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
function ColorMenu({ colors, value, fallback, onSelect, onClear }) {
|
|
2578
|
-
const inputRef = useRef5(null);
|
|
2579
|
-
return /* @__PURE__ */ jsxs16("div", { children: [
|
|
2580
|
-
/* @__PURE__ */ jsx22("div", { className: "ree-rte-swatches", children: colors.map((color) => /* @__PURE__ */ jsx22(
|
|
2581
|
-
"button",
|
|
2582
|
-
{
|
|
2583
|
-
type: "button",
|
|
2584
|
-
className: `ree-rte-swatch${sameColor(color, value) ? " is-active" : ""}`,
|
|
2585
|
-
style: { backgroundColor: color },
|
|
2586
|
-
title: color,
|
|
2587
|
-
"aria-label": color,
|
|
2588
|
-
onClick: () => onSelect(color)
|
|
2589
|
-
},
|
|
2590
|
-
color
|
|
2591
|
-
)) }),
|
|
2592
|
-
/* @__PURE__ */ jsxs16("div", { className: "ree-rte-color-actions", children: [
|
|
2593
|
-
/* @__PURE__ */ jsx22("button", { type: "button", className: "ree-btn", onClick: onClear, children: "Default" }),
|
|
2594
|
-
/* @__PURE__ */ jsx22("button", { type: "button", className: "ree-btn", onClick: () => {
|
|
2595
|
-
var _a;
|
|
2596
|
-
return (_a = inputRef.current) == null ? void 0 : _a.click();
|
|
2597
|
-
}, children: "Custom" }),
|
|
2598
|
-
/* @__PURE__ */ jsx22(
|
|
2599
|
-
"input",
|
|
2600
|
-
{
|
|
2601
|
-
ref: inputRef,
|
|
2602
|
-
className: "ree-rte-color-input",
|
|
2603
|
-
type: "color",
|
|
2604
|
-
tabIndex: -1,
|
|
2605
|
-
value: toColorInputValue(value, fallback),
|
|
2606
|
-
onChange: (event) => onSelect(event.target.value)
|
|
2607
|
-
}
|
|
2608
|
-
)
|
|
2609
|
-
] })
|
|
2610
|
-
] });
|
|
3098
|
+
function isVisuallyBold(markWeight2, strong, inherited) {
|
|
3099
|
+
if (strong || markWeight2 === "bold") return true;
|
|
3100
|
+
if (markWeight2 === "normal") return false;
|
|
3101
|
+
return inherited === "bold";
|
|
2611
3102
|
}
|
|
2612
|
-
function
|
|
2613
|
-
return
|
|
3103
|
+
function toggleVisualBold(editor, inherited) {
|
|
3104
|
+
return formatText(editor, (chain) => chain.command(({ state, chain: scopedChain }) => {
|
|
3105
|
+
const weight = getAttributes(state, "textStyle").fontWeight;
|
|
3106
|
+
let visuallyBold = isVisuallyBold(
|
|
3107
|
+
weight === "normal" || weight === "bold" ? weight : "",
|
|
3108
|
+
isMarkActive(state, "bold"),
|
|
3109
|
+
inherited
|
|
3110
|
+
);
|
|
3111
|
+
if (!state.selection.empty) {
|
|
3112
|
+
visuallyBold = true;
|
|
3113
|
+
state.doc.nodesBetween(state.selection.from, state.selection.to, (node) => {
|
|
3114
|
+
var _a;
|
|
3115
|
+
if (!node.isText) return;
|
|
3116
|
+
const textWeight = (_a = node.marks.find((mark) => mark.type.name === "textStyle")) == null ? void 0 : _a.attrs.fontWeight;
|
|
3117
|
+
if (!isVisuallyBold(
|
|
3118
|
+
textWeight === "normal" || textWeight === "bold" ? textWeight : "",
|
|
3119
|
+
node.marks.some((mark) => mark.type.name === "bold"),
|
|
3120
|
+
inherited
|
|
3121
|
+
)) visuallyBold = false;
|
|
3122
|
+
});
|
|
3123
|
+
}
|
|
3124
|
+
const next = scopedChain().unsetBold();
|
|
3125
|
+
const fontWeight = visuallyBold ? inherited === "bold" ? "normal" : null : inherited === "bold" ? null : "bold";
|
|
3126
|
+
return next.setMark("textStyle", { fontWeight }).removeEmptyTextStyle().run();
|
|
3127
|
+
}));
|
|
2614
3128
|
}
|
|
2615
3129
|
|
|
2616
3130
|
// src/stage/rich-text/components/EmojiMenu.tsx
|
|
2617
|
-
import { jsx as
|
|
3131
|
+
import { jsx as jsx26 } from "react/jsx-runtime";
|
|
2618
3132
|
function EmojiMenu({ onPick }) {
|
|
2619
|
-
return /* @__PURE__ */
|
|
3133
|
+
return /* @__PURE__ */ jsx26("div", { className: "ree-rte-emoji-grid", children: EMOJIS.map((emoji) => /* @__PURE__ */ jsx26(
|
|
2620
3134
|
"button",
|
|
2621
3135
|
{
|
|
2622
3136
|
type: "button",
|
|
@@ -2629,65 +3143,27 @@ function EmojiMenu({ onPick }) {
|
|
|
2629
3143
|
)) });
|
|
2630
3144
|
}
|
|
2631
3145
|
|
|
2632
|
-
// src/stage/rich-text/components/LinkPopover.tsx
|
|
2633
|
-
import { useEffect as useEffect3, useRef as useRef6 } from "react";
|
|
2634
|
-
import { jsx as jsx24, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2635
|
-
function LinkPopover({
|
|
2636
|
-
url,
|
|
2637
|
-
canUnlink,
|
|
2638
|
-
onUrlChange,
|
|
2639
|
-
onApply,
|
|
2640
|
-
onUnlink
|
|
2641
|
-
}) {
|
|
2642
|
-
const inputRef = useRef6(null);
|
|
2643
|
-
useEffect3(() => {
|
|
2644
|
-
var _a, _b;
|
|
2645
|
-
(_a = inputRef.current) == null ? void 0 : _a.focus();
|
|
2646
|
-
(_b = inputRef.current) == null ? void 0 : _b.select();
|
|
2647
|
-
}, []);
|
|
2648
|
-
return /* @__PURE__ */ jsxs17("div", { className: "ree-rte-link", children: [
|
|
2649
|
-
/* @__PURE__ */ jsxs17("label", { className: "ree-field", children: [
|
|
2650
|
-
/* @__PURE__ */ jsx24("span", { className: "ree-field-label", children: "Link URL" }),
|
|
2651
|
-
/* @__PURE__ */ jsx24(
|
|
2652
|
-
"input",
|
|
2653
|
-
{
|
|
2654
|
-
ref: inputRef,
|
|
2655
|
-
className: "ree-input",
|
|
2656
|
-
value: url,
|
|
2657
|
-
placeholder: "https://",
|
|
2658
|
-
onChange: (event) => onUrlChange(event.target.value),
|
|
2659
|
-
onKeyDown: (event) => {
|
|
2660
|
-
if (event.key === "Enter") {
|
|
2661
|
-
event.preventDefault();
|
|
2662
|
-
onApply();
|
|
2663
|
-
}
|
|
2664
|
-
}
|
|
2665
|
-
}
|
|
2666
|
-
)
|
|
2667
|
-
] }),
|
|
2668
|
-
/* @__PURE__ */ jsxs17("div", { className: "ree-rte-link-actions", children: [
|
|
2669
|
-
/* @__PURE__ */ jsx24("button", { type: "button", className: "ree-btn", disabled: !canUnlink, onClick: onUnlink, children: "Unlink" }),
|
|
2670
|
-
/* @__PURE__ */ jsx24("button", { type: "button", className: "ree-btn ree-btn-primary", onClick: onApply, children: "Apply" })
|
|
2671
|
-
] })
|
|
2672
|
-
] });
|
|
2673
|
-
}
|
|
2674
|
-
|
|
2675
3146
|
// src/stage/rich-text/components/ToolbarButton.tsx
|
|
2676
|
-
import { jsx as
|
|
3147
|
+
import { jsx as jsx27 } from "react/jsx-runtime";
|
|
2677
3148
|
function ToolbarButton({
|
|
2678
3149
|
title,
|
|
2679
3150
|
active = false,
|
|
2680
3151
|
disabled = false,
|
|
3152
|
+
expanded,
|
|
3153
|
+
controls,
|
|
2681
3154
|
onClick,
|
|
2682
3155
|
children
|
|
2683
3156
|
}) {
|
|
2684
|
-
return /* @__PURE__ */
|
|
3157
|
+
return /* @__PURE__ */ jsx27(
|
|
2685
3158
|
"button",
|
|
2686
3159
|
{
|
|
2687
3160
|
type: "button",
|
|
2688
3161
|
className: `ree-tool-btn${active ? " is-active" : ""}`,
|
|
2689
3162
|
title,
|
|
2690
|
-
"aria-pressed": active,
|
|
3163
|
+
"aria-pressed": expanded === void 0 ? active : void 0,
|
|
3164
|
+
"aria-haspopup": expanded === void 0 ? void 0 : "dialog",
|
|
3165
|
+
"aria-expanded": expanded,
|
|
3166
|
+
"aria-controls": controls,
|
|
2691
3167
|
disabled,
|
|
2692
3168
|
onClick,
|
|
2693
3169
|
children
|
|
@@ -2695,10 +3171,32 @@ function ToolbarButton({
|
|
|
2695
3171
|
);
|
|
2696
3172
|
}
|
|
2697
3173
|
|
|
3174
|
+
// src/stage/rich-text/components/TextColorControl.tsx
|
|
3175
|
+
import { jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
3176
|
+
function TextColorControl({ value, fallback, onOpen, onChange, onReset }) {
|
|
3177
|
+
const { id, open, anchorRef, panelRef, toggle, close } = useColorPicker(onOpen);
|
|
3178
|
+
return /* @__PURE__ */ jsxs18("div", { className: "ree-rte-pop", ref: anchorRef, children: [
|
|
3179
|
+
/* @__PURE__ */ jsx28(
|
|
3180
|
+
ToolbarButton,
|
|
3181
|
+
{
|
|
3182
|
+
title: "Text color",
|
|
3183
|
+
expanded: open,
|
|
3184
|
+
controls: open ? id : void 0,
|
|
3185
|
+
onClick: toggle,
|
|
3186
|
+
children: /* @__PURE__ */ jsx28("span", { className: "ree-tool-color", style: { borderBottomColor: value || fallback }, children: "A" })
|
|
3187
|
+
}
|
|
3188
|
+
),
|
|
3189
|
+
open ? /* @__PURE__ */ jsx28(ColorPicker, { id, label: "Text color", value, fallback, panelRef, onChange, onReset, onClose: close }) : null
|
|
3190
|
+
] });
|
|
3191
|
+
}
|
|
3192
|
+
|
|
3193
|
+
// src/stage/rich-text/components/ToolbarMenu.tsx
|
|
3194
|
+
import { useLayoutEffect as useLayoutEffect5, useRef as useRef6, useState as useState8 } from "react";
|
|
3195
|
+
|
|
2698
3196
|
// src/stage/rich-text/components/ToolbarIcon.tsx
|
|
2699
|
-
import { jsx as
|
|
3197
|
+
import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2700
3198
|
function ToolbarIcon({ name }) {
|
|
2701
|
-
return /* @__PURE__ */
|
|
3199
|
+
return /* @__PURE__ */ jsx29("svg", { viewBox: "0 0 24 24", width: "16", height: "16", "aria-hidden": "true", fill: "none", children: iconPaths(name) });
|
|
2702
3200
|
}
|
|
2703
3201
|
function iconPaths(name) {
|
|
2704
3202
|
const stroke = {
|
|
@@ -2709,73 +3207,83 @@ function iconPaths(name) {
|
|
|
2709
3207
|
strokeLinejoin: "round"
|
|
2710
3208
|
};
|
|
2711
3209
|
switch (name) {
|
|
3210
|
+
case "space-before":
|
|
3211
|
+
return /* @__PURE__ */ jsxs19("g", __spreadProps(__spreadValues({}, stroke), { children: [
|
|
3212
|
+
/* @__PURE__ */ jsx29("path", { d: "M4 4h16M4 15h16M4 20h10" }),
|
|
3213
|
+
/* @__PURE__ */ jsx29("path", { d: "M12 7v5m-3-3 3 3 3-3" })
|
|
3214
|
+
] }));
|
|
3215
|
+
case "line-height":
|
|
3216
|
+
return /* @__PURE__ */ jsxs19("g", __spreadProps(__spreadValues({}, stroke), { children: [
|
|
3217
|
+
/* @__PURE__ */ jsx29("path", { d: "M11 5h9M11 12h9M11 19h9" }),
|
|
3218
|
+
/* @__PURE__ */ jsx29("path", { d: "M5 4v16M2 7l3-3 3 3M2 17l3 3 3-3" })
|
|
3219
|
+
] }));
|
|
2712
3220
|
case "align-left":
|
|
2713
|
-
return /* @__PURE__ */
|
|
2714
|
-
/* @__PURE__ */
|
|
2715
|
-
/* @__PURE__ */
|
|
2716
|
-
/* @__PURE__ */
|
|
2717
|
-
/* @__PURE__ */
|
|
3221
|
+
return /* @__PURE__ */ jsxs19("g", __spreadProps(__spreadValues({}, stroke), { children: [
|
|
3222
|
+
/* @__PURE__ */ jsx29("path", { d: "M4 6h16" }),
|
|
3223
|
+
/* @__PURE__ */ jsx29("path", { d: "M4 10h10" }),
|
|
3224
|
+
/* @__PURE__ */ jsx29("path", { d: "M4 14h16" }),
|
|
3225
|
+
/* @__PURE__ */ jsx29("path", { d: "M4 18h10" })
|
|
2718
3226
|
] }));
|
|
2719
3227
|
case "align-center":
|
|
2720
|
-
return /* @__PURE__ */
|
|
2721
|
-
/* @__PURE__ */
|
|
2722
|
-
/* @__PURE__ */
|
|
2723
|
-
/* @__PURE__ */
|
|
2724
|
-
/* @__PURE__ */
|
|
3228
|
+
return /* @__PURE__ */ jsxs19("g", __spreadProps(__spreadValues({}, stroke), { children: [
|
|
3229
|
+
/* @__PURE__ */ jsx29("path", { d: "M4 6h16" }),
|
|
3230
|
+
/* @__PURE__ */ jsx29("path", { d: "M7 10h10" }),
|
|
3231
|
+
/* @__PURE__ */ jsx29("path", { d: "M4 14h16" }),
|
|
3232
|
+
/* @__PURE__ */ jsx29("path", { d: "M7 18h10" })
|
|
2725
3233
|
] }));
|
|
2726
3234
|
case "align-right":
|
|
2727
|
-
return /* @__PURE__ */
|
|
2728
|
-
/* @__PURE__ */
|
|
2729
|
-
/* @__PURE__ */
|
|
2730
|
-
/* @__PURE__ */
|
|
2731
|
-
/* @__PURE__ */
|
|
3235
|
+
return /* @__PURE__ */ jsxs19("g", __spreadProps(__spreadValues({}, stroke), { children: [
|
|
3236
|
+
/* @__PURE__ */ jsx29("path", { d: "M4 6h16" }),
|
|
3237
|
+
/* @__PURE__ */ jsx29("path", { d: "M10 10h10" }),
|
|
3238
|
+
/* @__PURE__ */ jsx29("path", { d: "M4 14h16" }),
|
|
3239
|
+
/* @__PURE__ */ jsx29("path", { d: "M10 18h10" })
|
|
2732
3240
|
] }));
|
|
2733
3241
|
case "align-justify":
|
|
2734
|
-
return /* @__PURE__ */
|
|
2735
|
-
/* @__PURE__ */
|
|
2736
|
-
/* @__PURE__ */
|
|
2737
|
-
/* @__PURE__ */
|
|
2738
|
-
/* @__PURE__ */
|
|
3242
|
+
return /* @__PURE__ */ jsxs19("g", __spreadProps(__spreadValues({}, stroke), { children: [
|
|
3243
|
+
/* @__PURE__ */ jsx29("path", { d: "M4 6h16" }),
|
|
3244
|
+
/* @__PURE__ */ jsx29("path", { d: "M4 10h16" }),
|
|
3245
|
+
/* @__PURE__ */ jsx29("path", { d: "M4 14h16" }),
|
|
3246
|
+
/* @__PURE__ */ jsx29("path", { d: "M4 18h16" })
|
|
2739
3247
|
] }));
|
|
2740
3248
|
case "list-bullet":
|
|
2741
|
-
return /* @__PURE__ */
|
|
2742
|
-
/* @__PURE__ */
|
|
2743
|
-
/* @__PURE__ */
|
|
2744
|
-
/* @__PURE__ */
|
|
2745
|
-
/* @__PURE__ */
|
|
2746
|
-
/* @__PURE__ */
|
|
2747
|
-
/* @__PURE__ */
|
|
3249
|
+
return /* @__PURE__ */ jsxs19("g", __spreadProps(__spreadValues({}, stroke), { children: [
|
|
3250
|
+
/* @__PURE__ */ jsx29("path", { d: "M10 7h10" }),
|
|
3251
|
+
/* @__PURE__ */ jsx29("path", { d: "M10 12h10" }),
|
|
3252
|
+
/* @__PURE__ */ jsx29("path", { d: "M10 17h10" }),
|
|
3253
|
+
/* @__PURE__ */ jsx29("circle", { cx: "5", cy: "7", r: "1.2", fill: "currentColor", stroke: "none" }),
|
|
3254
|
+
/* @__PURE__ */ jsx29("circle", { cx: "5", cy: "12", r: "1.2", fill: "currentColor", stroke: "none" }),
|
|
3255
|
+
/* @__PURE__ */ jsx29("circle", { cx: "5", cy: "17", r: "1.2", fill: "currentColor", stroke: "none" })
|
|
2748
3256
|
] }));
|
|
2749
3257
|
case "list-ordered":
|
|
2750
|
-
return /* @__PURE__ */
|
|
2751
|
-
/* @__PURE__ */
|
|
2752
|
-
/* @__PURE__ */
|
|
2753
|
-
/* @__PURE__ */
|
|
2754
|
-
/* @__PURE__ */
|
|
2755
|
-
/* @__PURE__ */
|
|
2756
|
-
/* @__PURE__ */
|
|
3258
|
+
return /* @__PURE__ */ jsxs19("g", __spreadProps(__spreadValues({}, stroke), { children: [
|
|
3259
|
+
/* @__PURE__ */ jsx29("path", { d: "M11 7h9" }),
|
|
3260
|
+
/* @__PURE__ */ jsx29("path", { d: "M11 12h9" }),
|
|
3261
|
+
/* @__PURE__ */ jsx29("path", { d: "M11 17h9" }),
|
|
3262
|
+
/* @__PURE__ */ jsx29("text", { x: "3", y: "9", fill: "currentColor", stroke: "none", fontSize: "7", fontFamily: "sans-serif", children: "1" }),
|
|
3263
|
+
/* @__PURE__ */ jsx29("text", { x: "3", y: "14", fill: "currentColor", stroke: "none", fontSize: "7", fontFamily: "sans-serif", children: "2" }),
|
|
3264
|
+
/* @__PURE__ */ jsx29("text", { x: "3", y: "19", fill: "currentColor", stroke: "none", fontSize: "7", fontFamily: "sans-serif", children: "3" })
|
|
2757
3265
|
] }));
|
|
2758
3266
|
case "emoji":
|
|
2759
|
-
return /* @__PURE__ */
|
|
2760
|
-
/* @__PURE__ */
|
|
2761
|
-
/* @__PURE__ */
|
|
2762
|
-
/* @__PURE__ */
|
|
2763
|
-
/* @__PURE__ */
|
|
3267
|
+
return /* @__PURE__ */ jsxs19("g", __spreadProps(__spreadValues({}, stroke), { children: [
|
|
3268
|
+
/* @__PURE__ */ jsx29("circle", { cx: "12", cy: "12", r: "8" }),
|
|
3269
|
+
/* @__PURE__ */ jsx29("path", { d: "M8.5 14.2a4.2 4.2 0 0 0 7 0" }),
|
|
3270
|
+
/* @__PURE__ */ jsx29("path", { d: "M9 10h.01" }),
|
|
3271
|
+
/* @__PURE__ */ jsx29("path", { d: "M15 10h.01" })
|
|
2764
3272
|
] }));
|
|
2765
3273
|
case "link":
|
|
2766
|
-
return /* @__PURE__ */
|
|
2767
|
-
/* @__PURE__ */
|
|
2768
|
-
/* @__PURE__ */
|
|
3274
|
+
return /* @__PURE__ */ jsxs19("g", __spreadProps(__spreadValues({}, stroke), { children: [
|
|
3275
|
+
/* @__PURE__ */ jsx29("path", { d: "M10 13a5 5 0 0 0 7.1.1l1.8-1.8a5 5 0 0 0-7.1-7.1L10.2 5.8" }),
|
|
3276
|
+
/* @__PURE__ */ jsx29("path", { d: "M14 11a5 5 0 0 0-7.1-.1L5.1 12.7a5 5 0 0 0 7.1 7.1l1.6-1.6" })
|
|
2769
3277
|
] }));
|
|
2770
3278
|
case "unlink":
|
|
2771
|
-
return /* @__PURE__ */
|
|
2772
|
-
/* @__PURE__ */
|
|
2773
|
-
/* @__PURE__ */
|
|
2774
|
-
/* @__PURE__ */
|
|
2775
|
-
/* @__PURE__ */
|
|
3279
|
+
return /* @__PURE__ */ jsxs19("g", __spreadProps(__spreadValues({}, stroke), { children: [
|
|
3280
|
+
/* @__PURE__ */ jsx29("path", { d: "M9 17H7a5 5 0 0 1 0-10h2" }),
|
|
3281
|
+
/* @__PURE__ */ jsx29("path", { d: "M15 7h2a5 5 0 0 1 0 10h-2" }),
|
|
3282
|
+
/* @__PURE__ */ jsx29("path", { d: "M8 12h4" }),
|
|
3283
|
+
/* @__PURE__ */ jsx29("path", { d: "m4 4 16 16" })
|
|
2776
3284
|
] }));
|
|
2777
3285
|
case "chevron":
|
|
2778
|
-
return /* @__PURE__ */
|
|
3286
|
+
return /* @__PURE__ */ jsx29(
|
|
2779
3287
|
"path",
|
|
2780
3288
|
{
|
|
2781
3289
|
d: "M7 10.2 12 15l5-4.8",
|
|
@@ -2790,8 +3298,7 @@ function iconPaths(name) {
|
|
|
2790
3298
|
}
|
|
2791
3299
|
|
|
2792
3300
|
// src/stage/rich-text/components/ToolbarMenu.tsx
|
|
2793
|
-
import {
|
|
2794
|
-
import { jsx as jsx27, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
3301
|
+
import { jsx as jsx30, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
2795
3302
|
function ToolbarMenu({
|
|
2796
3303
|
label,
|
|
2797
3304
|
title,
|
|
@@ -2804,9 +3311,9 @@ function ToolbarMenu({
|
|
|
2804
3311
|
panelClassName,
|
|
2805
3312
|
children
|
|
2806
3313
|
}) {
|
|
2807
|
-
const rootRef =
|
|
2808
|
-
const [up, setUp] =
|
|
2809
|
-
|
|
3314
|
+
const rootRef = useRef6(null);
|
|
3315
|
+
const [up, setUp] = useState8(false);
|
|
3316
|
+
useLayoutEffect5(() => {
|
|
2810
3317
|
if (!open || !rootRef.current) return;
|
|
2811
3318
|
const ownerWindow = rootRef.current.ownerDocument.defaultView;
|
|
2812
3319
|
if (!ownerWindow) return;
|
|
@@ -2814,22 +3321,23 @@ function ToolbarMenu({
|
|
|
2814
3321
|
setUp(rect.bottom + 260 > ownerWindow.innerHeight && rect.top > 260);
|
|
2815
3322
|
}, [open]);
|
|
2816
3323
|
const buttonClass = variant === "icon" ? `ree-tool-btn${active ? " is-active" : ""}${open ? " is-open" : ""}` : `ree-rte-select${open ? " is-open" : ""}`;
|
|
2817
|
-
return /* @__PURE__ */
|
|
2818
|
-
/* @__PURE__ */
|
|
3324
|
+
return /* @__PURE__ */ jsxs20("div", { className: `ree-rte-pop${up ? " is-up" : ""}`, ref: rootRef, children: [
|
|
3325
|
+
/* @__PURE__ */ jsxs20(
|
|
2819
3326
|
"button",
|
|
2820
3327
|
{
|
|
2821
3328
|
type: "button",
|
|
2822
3329
|
className: buttonClass,
|
|
2823
3330
|
title,
|
|
3331
|
+
"aria-label": variant === "icon" ? title : void 0,
|
|
2824
3332
|
"aria-expanded": open,
|
|
2825
3333
|
onClick: onToggle,
|
|
2826
3334
|
children: [
|
|
2827
|
-
variant === "select" ? /* @__PURE__ */
|
|
2828
|
-
caret ? /* @__PURE__ */
|
|
3335
|
+
variant === "select" ? /* @__PURE__ */ jsx30("span", { className: "ree-rte-select-label", children: label }) : label,
|
|
3336
|
+
caret ? /* @__PURE__ */ jsx30(ToolbarIcon, { name: "chevron" }) : null
|
|
2829
3337
|
]
|
|
2830
3338
|
}
|
|
2831
3339
|
),
|
|
2832
|
-
open ? /* @__PURE__ */
|
|
3340
|
+
open ? /* @__PURE__ */ jsx30(
|
|
2833
3341
|
"div",
|
|
2834
3342
|
{
|
|
2835
3343
|
className: [
|
|
@@ -2843,8 +3351,151 @@ function ToolbarMenu({
|
|
|
2843
3351
|
] });
|
|
2844
3352
|
}
|
|
2845
3353
|
|
|
3354
|
+
// src/stage/rich-text/components/TextFormatControls.tsx
|
|
3355
|
+
import { Fragment as Fragment2, jsx as jsx31, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
3356
|
+
function TextFormatControls({ editor, profile, state, inherited, menu, range, bold, toggleMenu, run, children }) {
|
|
3357
|
+
const fontFamily = state.fontFamily || inherited.fontFamily;
|
|
3358
|
+
const fontSize = state.fontSize || inherited.fontSize;
|
|
3359
|
+
return /* @__PURE__ */ jsxs21(Fragment2, { children: [
|
|
3360
|
+
/* @__PURE__ */ jsxs21("div", { className: "ree-rte-group", role: "group", "aria-label": "Font", children: [
|
|
3361
|
+
/* @__PURE__ */ jsx31(
|
|
3362
|
+
ToolbarMenu,
|
|
3363
|
+
{
|
|
3364
|
+
title: "Font",
|
|
3365
|
+
open: menu === "font",
|
|
3366
|
+
onToggle: () => toggleMenu("font"),
|
|
3367
|
+
label: fontFamilyLabel(fontFamily),
|
|
3368
|
+
children: FONT_FAMILIES.map((font) => /* @__PURE__ */ jsx31(
|
|
3369
|
+
"button",
|
|
3370
|
+
{
|
|
3371
|
+
type: "button",
|
|
3372
|
+
className: `ree-rte-option${fontFamily === font.value ? " is-active" : ""}`,
|
|
3373
|
+
style: { fontFamily: font.value },
|
|
3374
|
+
onClick: () => run(() => formatText(editor, (chain) => chain.setFontFamily(font.value), range)),
|
|
3375
|
+
children: font.label
|
|
3376
|
+
},
|
|
3377
|
+
font.value
|
|
3378
|
+
))
|
|
3379
|
+
}
|
|
3380
|
+
),
|
|
3381
|
+
/* @__PURE__ */ jsx31(
|
|
3382
|
+
ToolbarMenu,
|
|
3383
|
+
{
|
|
3384
|
+
title: "Font size",
|
|
3385
|
+
open: menu === "size",
|
|
3386
|
+
onToggle: () => toggleMenu("size"),
|
|
3387
|
+
label: fontSize || "15px",
|
|
3388
|
+
children: FONT_SIZES.map((size) => /* @__PURE__ */ jsx31(
|
|
3389
|
+
"button",
|
|
3390
|
+
{
|
|
3391
|
+
type: "button",
|
|
3392
|
+
className: `ree-rte-option${fontSize === size ? " is-active" : ""}`,
|
|
3393
|
+
onClick: () => run(() => formatText(editor, (chain) => chain.setFontSize(size), range)),
|
|
3394
|
+
children: size
|
|
3395
|
+
},
|
|
3396
|
+
size
|
|
3397
|
+
))
|
|
3398
|
+
}
|
|
3399
|
+
)
|
|
3400
|
+
] }),
|
|
3401
|
+
/* @__PURE__ */ jsxs21("div", { className: "ree-rte-group", role: "group", "aria-label": "Text style", children: [
|
|
3402
|
+
/* @__PURE__ */ jsx31(
|
|
3403
|
+
ToolbarButton,
|
|
3404
|
+
{
|
|
3405
|
+
title: "Bold",
|
|
3406
|
+
active: bold,
|
|
3407
|
+
onClick: () => toggleVisualBold(editor, inherited.fontWeight),
|
|
3408
|
+
children: /* @__PURE__ */ jsx31("span", { className: "ree-tool-bold", children: "B" })
|
|
3409
|
+
}
|
|
3410
|
+
),
|
|
3411
|
+
/* @__PURE__ */ jsx31(
|
|
3412
|
+
ToolbarButton,
|
|
3413
|
+
{
|
|
3414
|
+
title: "Italic",
|
|
3415
|
+
active: state.italic,
|
|
3416
|
+
onClick: () => formatText(editor, (chain) => chain.toggleItalic()),
|
|
3417
|
+
children: /* @__PURE__ */ jsx31("span", { className: "ree-tool-italic", children: "I" })
|
|
3418
|
+
}
|
|
3419
|
+
),
|
|
3420
|
+
/* @__PURE__ */ jsx31(
|
|
3421
|
+
ToolbarButton,
|
|
3422
|
+
{
|
|
3423
|
+
title: "Underline",
|
|
3424
|
+
active: state.underline,
|
|
3425
|
+
onClick: () => formatText(editor, (chain) => chain.toggleUnderline()),
|
|
3426
|
+
children: /* @__PURE__ */ jsx31("span", { className: "ree-tool-underline", children: "U" })
|
|
3427
|
+
}
|
|
3428
|
+
),
|
|
3429
|
+
profile === "text" ? /* @__PURE__ */ jsx31(
|
|
3430
|
+
ToolbarButton,
|
|
3431
|
+
{
|
|
3432
|
+
title: "Strikethrough",
|
|
3433
|
+
active: state.strike,
|
|
3434
|
+
onClick: () => formatText(editor, (chain) => chain.toggleStrike()),
|
|
3435
|
+
children: /* @__PURE__ */ jsx31("span", { className: "ree-tool-strike", children: "S" })
|
|
3436
|
+
}
|
|
3437
|
+
) : null,
|
|
3438
|
+
children
|
|
3439
|
+
] })
|
|
3440
|
+
] });
|
|
3441
|
+
}
|
|
3442
|
+
|
|
3443
|
+
// src/stage/rich-text/list-formatting.ts
|
|
3444
|
+
import { TextSelection } from "@tiptap/pm/state";
|
|
3445
|
+
function isList(node) {
|
|
3446
|
+
return node.type.name === "bulletList" || node.type.name === "orderedList";
|
|
3447
|
+
}
|
|
3448
|
+
function toggleTextList(editor, type) {
|
|
3449
|
+
return editor.chain().focus().command(({ tr, chain }) => {
|
|
3450
|
+
const selection = tr.selection;
|
|
3451
|
+
const bookmark = selection.getBookmark();
|
|
3452
|
+
const storedMarks = tr.storedMarks;
|
|
3453
|
+
const from = selection.empty ? 0 : selection.from;
|
|
3454
|
+
let to = selection.empty ? tr.doc.content.size : selection.to;
|
|
3455
|
+
const last = tr.doc.lastChild;
|
|
3456
|
+
if (selection.empty && tr.doc.childCount > 1 && (last == null ? void 0 : last.type.name) === "paragraph" && last.content.size === 0) {
|
|
3457
|
+
const previous = tr.doc.child(tr.doc.childCount - 2);
|
|
3458
|
+
if (isList(previous)) to -= last.nodeSize;
|
|
3459
|
+
}
|
|
3460
|
+
const lists = [];
|
|
3461
|
+
const paragraphs = [];
|
|
3462
|
+
tr.doc.nodesBetween(from, to, (node, pos) => {
|
|
3463
|
+
if (isList(node)) {
|
|
3464
|
+
lists.push({ node, from: pos, to: pos + node.nodeSize });
|
|
3465
|
+
return false;
|
|
3466
|
+
}
|
|
3467
|
+
if (node.type.name === "paragraph") {
|
|
3468
|
+
const previous = paragraphs[paragraphs.length - 1];
|
|
3469
|
+
if ((previous == null ? void 0 : previous.to) === pos) previous.to = pos + node.nodeSize;
|
|
3470
|
+
else paragraphs.push({ node, from: pos, to: pos + node.nodeSize });
|
|
3471
|
+
return false;
|
|
3472
|
+
}
|
|
3473
|
+
});
|
|
3474
|
+
const remove = lists.length > 0 && paragraphs.length === 0 && lists.every((item) => item.node.type.name === type);
|
|
3475
|
+
if (remove) {
|
|
3476
|
+
for (const list of lists.reverse()) {
|
|
3477
|
+
const start = selection.empty ? list.from + 1 : Math.max(selection.from, list.from + 1);
|
|
3478
|
+
const end = selection.empty ? list.to - 1 : Math.min(selection.to, list.to - 1);
|
|
3479
|
+
tr.setSelection(TextSelection.between(tr.doc.resolve(start), tr.doc.resolve(end)));
|
|
3480
|
+
chain().liftListItem("listItem").run();
|
|
3481
|
+
}
|
|
3482
|
+
} else {
|
|
3483
|
+
const target = editor.schema.nodes[type];
|
|
3484
|
+
for (const list of lists) {
|
|
3485
|
+
if (list.node.type !== target) tr.setNodeMarkup(list.from, target, list.node.attrs);
|
|
3486
|
+
}
|
|
3487
|
+
for (const paragraph of paragraphs.reverse()) {
|
|
3488
|
+
chain().setTextSelection({ from: paragraph.from + 1, to: paragraph.to - 1 }).wrapInList(type).run();
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3491
|
+
tr.setSelection(bookmark.map(tr.mapping).resolve(tr.doc));
|
|
3492
|
+
if (tr.selection.empty) tr.setStoredMarks(storedMarks);
|
|
3493
|
+
return true;
|
|
3494
|
+
}).run();
|
|
3495
|
+
}
|
|
3496
|
+
|
|
2846
3497
|
// src/stage/rich-text/components/MetricSlider.tsx
|
|
2847
|
-
import { jsx as
|
|
3498
|
+
import { jsx as jsx32, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
2848
3499
|
function MetricSlider({
|
|
2849
3500
|
label,
|
|
2850
3501
|
value,
|
|
@@ -2856,32 +3507,193 @@ function MetricSlider({
|
|
|
2856
3507
|
onChange,
|
|
2857
3508
|
onReset
|
|
2858
3509
|
}) {
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
/* @__PURE__ */
|
|
2863
|
-
|
|
3510
|
+
return /* @__PURE__ */ jsxs22("div", { className: "ree-rte-metric", children: [
|
|
3511
|
+
/* @__PURE__ */ jsxs22("div", { className: "ree-rte-metric-label", children: [
|
|
3512
|
+
/* @__PURE__ */ jsx32("span", { children: label }),
|
|
3513
|
+
/* @__PURE__ */ jsxs22("span", { className: "ree-rte-metric-meta", children: [
|
|
3514
|
+
/* @__PURE__ */ jsx32("span", { children: display }),
|
|
3515
|
+
/* @__PURE__ */ jsx32(ResetButton, { label: `Reset ${label.toLowerCase()}`, disabled: !overridden, onClick: onReset })
|
|
3516
|
+
] })
|
|
2864
3517
|
] }),
|
|
2865
|
-
/* @__PURE__ */
|
|
2866
|
-
|
|
3518
|
+
/* @__PURE__ */ jsx32(
|
|
3519
|
+
RangeControl,
|
|
2867
3520
|
{
|
|
2868
|
-
|
|
2869
|
-
type: "range",
|
|
2870
|
-
className: "ree-range",
|
|
2871
|
-
style: { "--ree-range-progress": `${progress}%` },
|
|
3521
|
+
label,
|
|
2872
3522
|
value,
|
|
2873
3523
|
min,
|
|
2874
3524
|
max,
|
|
2875
3525
|
step,
|
|
2876
|
-
onChange
|
|
3526
|
+
onChange
|
|
3527
|
+
}
|
|
3528
|
+
)
|
|
3529
|
+
] });
|
|
3530
|
+
}
|
|
3531
|
+
|
|
3532
|
+
// src/stage/rich-text/components/ListControls.tsx
|
|
3533
|
+
import { jsx as jsx33, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
3534
|
+
function ListControls({ editor, state, menu, range, inherited, onToggle }) {
|
|
3535
|
+
const listType = state.orderedList ? "orderedList" : "bulletList";
|
|
3536
|
+
const spacing = state.listSpacing || inherited.listSpacing || "4px";
|
|
3537
|
+
const indent = state.listIndent || inherited.listIndent || "22px";
|
|
3538
|
+
const update = (key, value) => {
|
|
3539
|
+
focusSelection(editor, range).updateAttributes(listType, { [key]: value }).run();
|
|
3540
|
+
};
|
|
3541
|
+
return /* @__PURE__ */ jsxs23("div", { className: "ree-rte-group", role: "group", "aria-label": "Lists", children: [
|
|
3542
|
+
/* @__PURE__ */ jsx33(
|
|
3543
|
+
ToolbarButton,
|
|
3544
|
+
{
|
|
3545
|
+
title: "Bulleted list",
|
|
3546
|
+
active: state.bulletList,
|
|
3547
|
+
onClick: () => toggleTextList(editor, "bulletList"),
|
|
3548
|
+
children: /* @__PURE__ */ jsx33(ToolbarIcon, { name: "list-bullet" })
|
|
3549
|
+
}
|
|
3550
|
+
),
|
|
3551
|
+
/* @__PURE__ */ jsx33(
|
|
3552
|
+
ToolbarButton,
|
|
3553
|
+
{
|
|
3554
|
+
title: "Numbered list",
|
|
3555
|
+
active: state.orderedList,
|
|
3556
|
+
onClick: () => toggleTextList(editor, "orderedList"),
|
|
3557
|
+
children: /* @__PURE__ */ jsx33(ToolbarIcon, { name: "list-ordered" })
|
|
2877
3558
|
}
|
|
2878
3559
|
),
|
|
2879
|
-
/* @__PURE__ */
|
|
3560
|
+
state.bulletList || state.orderedList ? /* @__PURE__ */ jsxs23(ToolbarMenu, { title: "List settings", label: "List", open: menu === "list", onToggle: () => onToggle("list"), children: [
|
|
3561
|
+
/* @__PURE__ */ jsx33(
|
|
3562
|
+
MetricSlider,
|
|
3563
|
+
{
|
|
3564
|
+
label: "List spacing",
|
|
3565
|
+
value: readCssLength(spacing).amount,
|
|
3566
|
+
min: 0,
|
|
3567
|
+
max: 48,
|
|
3568
|
+
step: 1,
|
|
3569
|
+
display: spacing,
|
|
3570
|
+
overridden: Boolean(state.listSpacing),
|
|
3571
|
+
onChange: (value) => update("listSpacing", `${value}px`),
|
|
3572
|
+
onReset: () => update("listSpacing", null)
|
|
3573
|
+
}
|
|
3574
|
+
),
|
|
3575
|
+
/* @__PURE__ */ jsx33(
|
|
3576
|
+
MetricSlider,
|
|
3577
|
+
{
|
|
3578
|
+
label: "List indent",
|
|
3579
|
+
value: readCssLength(indent).amount,
|
|
3580
|
+
min: 0,
|
|
3581
|
+
max: 64,
|
|
3582
|
+
step: 1,
|
|
3583
|
+
display: indent,
|
|
3584
|
+
overridden: Boolean(state.listIndent),
|
|
3585
|
+
onChange: (value) => update("listIndent", `${value}px`),
|
|
3586
|
+
onReset: () => update("listIndent", null)
|
|
3587
|
+
}
|
|
3588
|
+
)
|
|
3589
|
+
] }) : null
|
|
2880
3590
|
] });
|
|
2881
3591
|
}
|
|
2882
3592
|
|
|
3593
|
+
// src/stage/rich-text/components/LinkPopover.tsx
|
|
3594
|
+
import { useEffect as useEffect2, useId as useId3, useRef as useRef7, useState as useState9 } from "react";
|
|
3595
|
+
import { isAllowedUri } from "@tiptap/extension-link";
|
|
3596
|
+
|
|
3597
|
+
// src/components/Button.tsx
|
|
3598
|
+
import { jsx as jsx34 } from "react/jsx-runtime";
|
|
3599
|
+
function Button(_a) {
|
|
3600
|
+
var _b = _a, { type = "button", variant = "default", className = "" } = _b, props = __objRest(_b, ["type", "variant", "className"]);
|
|
3601
|
+
return /* @__PURE__ */ jsx34("button", __spreadProps(__spreadValues({}, props), { type, className: `ree-btn${variant === "default" ? "" : ` ree-btn-${variant}`}${className ? ` ${className}` : ""}` }));
|
|
3602
|
+
}
|
|
3603
|
+
|
|
3604
|
+
// src/stage/rich-text/components/LinkPopover.tsx
|
|
3605
|
+
import { jsx as jsx35, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
3606
|
+
function LinkPopover({
|
|
3607
|
+
url,
|
|
3608
|
+
color,
|
|
3609
|
+
decoration,
|
|
3610
|
+
target,
|
|
3611
|
+
onColorChange,
|
|
3612
|
+
onDecorationChange,
|
|
3613
|
+
onTargetChange,
|
|
3614
|
+
canUnlink,
|
|
3615
|
+
onUrlChange,
|
|
3616
|
+
onApply,
|
|
3617
|
+
onClose
|
|
3618
|
+
}) {
|
|
3619
|
+
const dialogRef = useRef7(null);
|
|
3620
|
+
const titleId = useId3();
|
|
3621
|
+
const errorId = useId3();
|
|
3622
|
+
const [submitted, setSubmitted] = useState9(false);
|
|
3623
|
+
const invalidUrl = Boolean(url.trim()) && !isAllowedUri(url.trim());
|
|
3624
|
+
const save = () => {
|
|
3625
|
+
setSubmitted(true);
|
|
3626
|
+
if (!url.trim() && !canUnlink || invalidUrl) return;
|
|
3627
|
+
onApply();
|
|
3628
|
+
};
|
|
3629
|
+
useEffect2(() => {
|
|
3630
|
+
var _a;
|
|
3631
|
+
const dialog = dialogRef.current;
|
|
3632
|
+
dialog == null ? void 0 : dialog.showModal();
|
|
3633
|
+
(_a = dialog == null ? void 0 : dialog.querySelector("input")) == null ? void 0 : _a.select();
|
|
3634
|
+
return () => dialog == null ? void 0 : dialog.close();
|
|
3635
|
+
}, []);
|
|
3636
|
+
return /* @__PURE__ */ jsxs24(
|
|
3637
|
+
"dialog",
|
|
3638
|
+
{
|
|
3639
|
+
ref: dialogRef,
|
|
3640
|
+
className: "ree-rte-link",
|
|
3641
|
+
"aria-labelledby": titleId,
|
|
3642
|
+
"aria-modal": "true",
|
|
3643
|
+
onCancel: (event) => {
|
|
3644
|
+
event.preventDefault();
|
|
3645
|
+
onClose();
|
|
3646
|
+
},
|
|
3647
|
+
onMouseDown: (event) => event.stopPropagation(),
|
|
3648
|
+
onPointerDown: (event) => event.stopPropagation(),
|
|
3649
|
+
onClick: (event) => {
|
|
3650
|
+
event.stopPropagation();
|
|
3651
|
+
if (event.target !== event.currentTarget) return;
|
|
3652
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
3653
|
+
if (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom) onClose();
|
|
3654
|
+
},
|
|
3655
|
+
children: [
|
|
3656
|
+
/* @__PURE__ */ jsx35("h2", { id: titleId, className: "ree-rte-link-title", children: "Edit link" }),
|
|
3657
|
+
/* @__PURE__ */ jsxs24("div", { className: "ree-rte-link-fields", children: [
|
|
3658
|
+
/* @__PURE__ */ jsxs24("label", { className: "ree-field", children: [
|
|
3659
|
+
/* @__PURE__ */ jsx35("span", { className: "ree-field-label", children: "Link URL" }),
|
|
3660
|
+
/* @__PURE__ */ jsx35(
|
|
3661
|
+
TextInput,
|
|
3662
|
+
{
|
|
3663
|
+
value: url,
|
|
3664
|
+
placeholder: "https://",
|
|
3665
|
+
autoComplete: "off",
|
|
3666
|
+
"aria-invalid": submitted && invalidUrl,
|
|
3667
|
+
"aria-describedby": submitted && invalidUrl ? errorId : void 0,
|
|
3668
|
+
onChange: (event) => onUrlChange(event.target.value),
|
|
3669
|
+
onKeyDown: (event) => {
|
|
3670
|
+
if (event.key !== "Enter" || event.nativeEvent.isComposing) return;
|
|
3671
|
+
event.preventDefault();
|
|
3672
|
+
event.stopPropagation();
|
|
3673
|
+
save();
|
|
3674
|
+
}
|
|
3675
|
+
}
|
|
3676
|
+
)
|
|
3677
|
+
] }),
|
|
3678
|
+
submitted && invalidUrl ? /* @__PURE__ */ jsx35("p", { id: errorId, className: "ree-rte-link-error", role: "alert", children: "Enter a valid link using an allowed protocol, such as https://." }) : null,
|
|
3679
|
+
/* @__PURE__ */ jsx35(ColorControl, { value: color, label: "Link color", resetTo: DEFAULT_EMAIL_GLOBAL.linkColor, onChange: onColorChange }),
|
|
3680
|
+
/* @__PURE__ */ jsx35(CheckboxControl, { label: "Underline", checked: decoration === "underline", onChange: (checked) => onDecorationChange(checked ? "underline" : "none") }),
|
|
3681
|
+
/* @__PURE__ */ jsxs24("label", { className: "ree-field", children: [
|
|
3682
|
+
/* @__PURE__ */ jsx35("span", { className: "ree-field-label", children: "Target" }),
|
|
3683
|
+
/* @__PURE__ */ jsx35(SelectControl, { label: "Target", value: target, options: LINK_TARGETS, onChange: onTargetChange })
|
|
3684
|
+
] })
|
|
3685
|
+
] }),
|
|
3686
|
+
/* @__PURE__ */ jsxs24("div", { className: "ree-rte-link-actions", children: [
|
|
3687
|
+
/* @__PURE__ */ jsx35(Button, { onClick: onClose, children: "Cancel" }),
|
|
3688
|
+
/* @__PURE__ */ jsx35(Button, { variant: "solid", disabled: !url.trim() && !canUnlink, onClick: save, children: "Save" })
|
|
3689
|
+
] })
|
|
3690
|
+
]
|
|
3691
|
+
}
|
|
3692
|
+
);
|
|
3693
|
+
}
|
|
3694
|
+
|
|
2883
3695
|
// src/stage/rich-text/components/ParagraphMetricMenus.tsx
|
|
2884
|
-
import { Fragment as
|
|
3696
|
+
import { Fragment as Fragment3, jsx as jsx36, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
2885
3697
|
function clampAmount(value, fallback, min, max) {
|
|
2886
3698
|
const parsed = readCssLength(value || fallback);
|
|
2887
3699
|
return Math.min(max, Math.max(min, parsed.amount));
|
|
@@ -2893,6 +3705,7 @@ function formatStep(value, step) {
|
|
|
2893
3705
|
}
|
|
2894
3706
|
function ParagraphMetricMenus({
|
|
2895
3707
|
menu,
|
|
3708
|
+
showSpacing = true,
|
|
2896
3709
|
spacing,
|
|
2897
3710
|
lineHeight,
|
|
2898
3711
|
inheritedSpacing,
|
|
@@ -2904,15 +3717,17 @@ function ParagraphMetricMenus({
|
|
|
2904
3717
|
const spacingValue = clampAmount(spacing, inheritedSpacing, 0, PARAGRAPH_SPACING_MAX);
|
|
2905
3718
|
const lineHeightValue = clampAmount(lineHeight, inheritedLineHeight, LINE_HEIGHT_RANGE.min, LINE_HEIGHT_RANGE.max);
|
|
2906
3719
|
const lineHeightLabel = formatStep(lineHeightValue, LINE_HEIGHT_RANGE.step);
|
|
2907
|
-
return /* @__PURE__ */
|
|
2908
|
-
/* @__PURE__ */
|
|
3720
|
+
return /* @__PURE__ */ jsxs25(Fragment3, { children: [
|
|
3721
|
+
showSpacing ? /* @__PURE__ */ jsx36(
|
|
2909
3722
|
ToolbarMenu,
|
|
2910
3723
|
{
|
|
2911
|
-
title: "
|
|
3724
|
+
title: "Space before",
|
|
3725
|
+
variant: "icon",
|
|
3726
|
+
caret: false,
|
|
2912
3727
|
open: menu === "spacing",
|
|
2913
3728
|
onToggle: () => onToggle("spacing"),
|
|
2914
|
-
label:
|
|
2915
|
-
children: /* @__PURE__ */
|
|
3729
|
+
label: /* @__PURE__ */ jsx36(ToolbarIcon, { name: "space-before" }),
|
|
3730
|
+
children: /* @__PURE__ */ jsx36(
|
|
2916
3731
|
MetricSlider,
|
|
2917
3732
|
{
|
|
2918
3733
|
label: "Space before",
|
|
@@ -2927,15 +3742,17 @@ function ParagraphMetricMenus({
|
|
|
2927
3742
|
}
|
|
2928
3743
|
)
|
|
2929
3744
|
}
|
|
2930
|
-
),
|
|
2931
|
-
/* @__PURE__ */
|
|
3745
|
+
) : null,
|
|
3746
|
+
/* @__PURE__ */ jsx36(
|
|
2932
3747
|
ToolbarMenu,
|
|
2933
3748
|
{
|
|
2934
3749
|
title: "Line height",
|
|
3750
|
+
variant: "icon",
|
|
3751
|
+
caret: false,
|
|
2935
3752
|
open: menu === "lineHeight",
|
|
2936
3753
|
onToggle: () => onToggle("lineHeight"),
|
|
2937
|
-
label:
|
|
2938
|
-
children: /* @__PURE__ */
|
|
3754
|
+
label: /* @__PURE__ */ jsx36(ToolbarIcon, { name: "line-height" }),
|
|
3755
|
+
children: /* @__PURE__ */ jsx36(
|
|
2939
3756
|
MetricSlider,
|
|
2940
3757
|
{
|
|
2941
3758
|
label: "Line height",
|
|
@@ -2965,20 +3782,20 @@ function restoreTemplateSpaces(value) {
|
|
|
2965
3782
|
}
|
|
2966
3783
|
|
|
2967
3784
|
// src/stage/rich-text/components/VariableMenu.tsx
|
|
2968
|
-
import { jsx as
|
|
3785
|
+
import { jsx as jsx37, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2969
3786
|
function VariableMenu({ variables, onInsert }) {
|
|
2970
3787
|
if (variables.length === 0) {
|
|
2971
|
-
return
|
|
3788
|
+
return null;
|
|
2972
3789
|
}
|
|
2973
|
-
return /* @__PURE__ */
|
|
3790
|
+
return /* @__PURE__ */ jsx37("div", { className: "ree-rte-variables", children: variables.map((variable) => /* @__PURE__ */ jsxs26(
|
|
2974
3791
|
"button",
|
|
2975
3792
|
{
|
|
2976
3793
|
type: "button",
|
|
2977
3794
|
className: "ree-var-item",
|
|
2978
3795
|
onClick: () => onInsert(variable.name),
|
|
2979
3796
|
children: [
|
|
2980
|
-
/* @__PURE__ */
|
|
2981
|
-
variable.description ? /* @__PURE__ */
|
|
3797
|
+
/* @__PURE__ */ jsx37("span", { className: "ree-var-name", children: variable.name }),
|
|
3798
|
+
variable.description ? /* @__PURE__ */ jsx37("span", { className: "ree-var-label", children: variable.description }) : null
|
|
2982
3799
|
]
|
|
2983
3800
|
},
|
|
2984
3801
|
variable.name
|
|
@@ -2986,8 +3803,10 @@ function VariableMenu({ variables, onInsert }) {
|
|
|
2986
3803
|
}
|
|
2987
3804
|
|
|
2988
3805
|
// src/stage/rich-text/components/RichTextToolbar.tsx
|
|
2989
|
-
import { jsx as
|
|
3806
|
+
import { jsx as jsx38, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
2990
3807
|
function RichTextToolbar({
|
|
3808
|
+
id,
|
|
3809
|
+
profile,
|
|
2991
3810
|
editor,
|
|
2992
3811
|
variables,
|
|
2993
3812
|
anchorRef,
|
|
@@ -2997,14 +3816,18 @@ function RichTextToolbar({
|
|
|
2997
3816
|
editor,
|
|
2998
3817
|
selector: ({ editor: current }) => current ? readToolbarState(current) : null
|
|
2999
3818
|
});
|
|
3000
|
-
const rootRef = useAnchoredToolbar(anchorRef, Boolean(state));
|
|
3819
|
+
const rootRef = useAnchoredToolbar(anchorRef, Boolean(state), profile === "button");
|
|
3001
3820
|
const linkRange = useRef8(null);
|
|
3002
|
-
const
|
|
3003
|
-
const [
|
|
3004
|
-
const [
|
|
3005
|
-
|
|
3821
|
+
const colorRange = useRef8(null);
|
|
3822
|
+
const [markRange, setMarkRange] = useState10(null);
|
|
3823
|
+
const [menu, setMenu] = useState10(null);
|
|
3824
|
+
const [linkUrl, setLinkUrl] = useState10("");
|
|
3825
|
+
const [linkColor, setLinkColor] = useState10("");
|
|
3826
|
+
const [linkDecoration, setLinkDecoration] = useState10("");
|
|
3827
|
+
const [linkTarget, setLinkTarget] = useState10(DEFAULT_LINK_TARGET);
|
|
3828
|
+
useEffect3(() => {
|
|
3006
3829
|
const root = rootRef.current;
|
|
3007
|
-
if (!menu || !root) return;
|
|
3830
|
+
if (!menu || menu === "link" || !root) return;
|
|
3008
3831
|
const ownerDocument = root.ownerDocument;
|
|
3009
3832
|
const onPointerDown = (event) => {
|
|
3010
3833
|
if (!root.contains(event.target)) setMenu(null);
|
|
@@ -3018,36 +3841,41 @@ function RichTextToolbar({
|
|
|
3018
3841
|
ownerDocument.removeEventListener("pointerdown", onPointerDown, true);
|
|
3019
3842
|
ownerDocument.removeEventListener("keydown", onKeyDown);
|
|
3020
3843
|
};
|
|
3021
|
-
}, [menu]);
|
|
3844
|
+
}, [menu, rootRef]);
|
|
3022
3845
|
if (!state) return null;
|
|
3023
|
-
const fontFamily = state.fontFamily || inherited.fontFamily;
|
|
3024
|
-
const fontSize = state.fontSize || inherited.fontSize;
|
|
3025
|
-
const color = state.color || inherited.color;
|
|
3026
3846
|
const bold = isVisuallyBold(state.fontWeight, state.bold, inherited.fontWeight);
|
|
3027
3847
|
const toggleMenu = (next) => {
|
|
3028
3848
|
if (menu !== next) {
|
|
3029
3849
|
const range = currentSelection(editor);
|
|
3030
3850
|
if (next === "link") {
|
|
3031
|
-
setLinkUrl(state.linkHref
|
|
3851
|
+
setLinkUrl(state.linkHref);
|
|
3852
|
+
setLinkColor(state.linkColor || (state.linkHref ? inherited.linkColor : "") || DEFAULT_EMAIL_GLOBAL.linkColor);
|
|
3853
|
+
setLinkDecoration(state.linkDecoration || (state.linkHref ? inherited.linkDecoration : "") || "underline");
|
|
3854
|
+
setLinkTarget(state.linkTarget === "_self" ? "_self" : DEFAULT_LINK_TARGET);
|
|
3032
3855
|
linkRange.current = range;
|
|
3033
3856
|
}
|
|
3034
|
-
|
|
3857
|
+
setMarkRange(range);
|
|
3035
3858
|
}
|
|
3036
3859
|
setMenu((current) => current === next ? null : next);
|
|
3037
3860
|
};
|
|
3038
3861
|
const applyLink = () => {
|
|
3039
3862
|
const href = linkUrl.trim();
|
|
3040
|
-
if (!href)
|
|
3863
|
+
if (!href) {
|
|
3864
|
+
if (state.linkHref) unlinkSaved();
|
|
3865
|
+
return;
|
|
3866
|
+
}
|
|
3867
|
+
if (!isAllowedUri2(href)) return;
|
|
3868
|
+
const attrs = { href, color: linkColor, decoration: linkDecoration, target: linkTarget };
|
|
3041
3869
|
const range = linkRange.current;
|
|
3042
3870
|
const chain = focusSelection(editor, range);
|
|
3043
|
-
if (range && range.
|
|
3871
|
+
if (range && range.anchor === range.head && !state.linkHref) {
|
|
3044
3872
|
chain.insertContent({
|
|
3045
3873
|
type: "text",
|
|
3046
3874
|
text: href,
|
|
3047
|
-
marks: [{ type: "link", attrs
|
|
3875
|
+
marks: [{ type: "link", attrs }]
|
|
3048
3876
|
}).run();
|
|
3049
3877
|
} else {
|
|
3050
|
-
chain.extendMarkRange("link").setLink({ href }).run();
|
|
3878
|
+
chain.extendMarkRange("link").setLink({ href }).updateAttributes("link", attrs).run();
|
|
3051
3879
|
}
|
|
3052
3880
|
setMenu(null);
|
|
3053
3881
|
};
|
|
@@ -3063,217 +3891,135 @@ function RichTextToolbar({
|
|
|
3063
3891
|
command();
|
|
3064
3892
|
setMenu(null);
|
|
3065
3893
|
};
|
|
3066
|
-
return /* @__PURE__ */
|
|
3894
|
+
return /* @__PURE__ */ jsxs27(
|
|
3067
3895
|
"div",
|
|
3068
3896
|
{
|
|
3897
|
+
id,
|
|
3069
3898
|
className: "ree-rte-toolbar",
|
|
3070
3899
|
ref: rootRef,
|
|
3071
3900
|
onMouseDown: (event) => {
|
|
3072
3901
|
const ownerWindow = event.currentTarget.ownerDocument.defaultView;
|
|
3073
3902
|
const target = event.target;
|
|
3074
|
-
if (ownerWindow && target instanceof ownerWindow.Element && target.closest("input, textarea")) return;
|
|
3903
|
+
if (ownerWindow && target instanceof ownerWindow.Element && target.closest("input, textarea, select")) return;
|
|
3075
3904
|
event.preventDefault();
|
|
3076
3905
|
},
|
|
3077
3906
|
children: [
|
|
3078
|
-
/* @__PURE__ */
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
),
|
|
3099
|
-
/* @__PURE__ */ jsx31(
|
|
3100
|
-
ToolbarMenu,
|
|
3101
|
-
{
|
|
3102
|
-
title: "Font size",
|
|
3103
|
-
open: menu === "size",
|
|
3104
|
-
onToggle: () => toggleMenu("size"),
|
|
3105
|
-
label: fontSize || "15px",
|
|
3106
|
-
children: FONT_SIZES.map((size) => /* @__PURE__ */ jsx31(
|
|
3107
|
-
"button",
|
|
3108
|
-
{
|
|
3109
|
-
type: "button",
|
|
3110
|
-
className: `ree-rte-option${fontSize === size ? " is-active" : ""}`,
|
|
3111
|
-
onClick: () => run(() => editor.chain().focus().setFontSize(size).run()),
|
|
3112
|
-
children: size
|
|
3907
|
+
/* @__PURE__ */ jsx38(
|
|
3908
|
+
TextFormatControls,
|
|
3909
|
+
{
|
|
3910
|
+
editor,
|
|
3911
|
+
profile,
|
|
3912
|
+
state,
|
|
3913
|
+
inherited,
|
|
3914
|
+
menu,
|
|
3915
|
+
range: markRange,
|
|
3916
|
+
bold,
|
|
3917
|
+
toggleMenu,
|
|
3918
|
+
run,
|
|
3919
|
+
children: /* @__PURE__ */ jsx38(
|
|
3920
|
+
TextColorControl,
|
|
3921
|
+
{
|
|
3922
|
+
value: state.color,
|
|
3923
|
+
fallback: inherited.color,
|
|
3924
|
+
onOpen: () => {
|
|
3925
|
+
colorRange.current = currentSelection(editor);
|
|
3926
|
+
setMenu(null);
|
|
3113
3927
|
},
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
title: "Bold",
|
|
3123
|
-
active: bold,
|
|
3124
|
-
onClick: () => toggleVisualBold(editor, inherited.fontWeight),
|
|
3125
|
-
children: /* @__PURE__ */ jsx31("span", { className: "ree-tool-bold", children: "B" })
|
|
3126
|
-
}
|
|
3127
|
-
),
|
|
3128
|
-
/* @__PURE__ */ jsx31(
|
|
3129
|
-
ToolbarButton,
|
|
3130
|
-
{
|
|
3131
|
-
title: "Italic",
|
|
3132
|
-
active: state.italic,
|
|
3133
|
-
onClick: () => editor.chain().focus().toggleItalic().run(),
|
|
3134
|
-
children: /* @__PURE__ */ jsx31("span", { className: "ree-tool-italic", children: "I" })
|
|
3135
|
-
}
|
|
3136
|
-
),
|
|
3137
|
-
/* @__PURE__ */ jsx31(
|
|
3138
|
-
ToolbarButton,
|
|
3139
|
-
{
|
|
3140
|
-
title: "Underline",
|
|
3141
|
-
active: state.underline,
|
|
3142
|
-
onClick: () => editor.chain().focus().toggleUnderline().run(),
|
|
3143
|
-
children: /* @__PURE__ */ jsx31("span", { className: "ree-tool-underline", children: "U" })
|
|
3144
|
-
}
|
|
3145
|
-
),
|
|
3146
|
-
/* @__PURE__ */ jsx31(
|
|
3147
|
-
ToolbarButton,
|
|
3148
|
-
{
|
|
3149
|
-
title: "Strikethrough",
|
|
3150
|
-
active: state.strike,
|
|
3151
|
-
onClick: () => editor.chain().focus().toggleStrike().run(),
|
|
3152
|
-
children: /* @__PURE__ */ jsx31("span", { className: "ree-tool-strike", children: "S" })
|
|
3153
|
-
}
|
|
3154
|
-
),
|
|
3155
|
-
/* @__PURE__ */ jsx31("span", { className: "ree-rte-sep" }),
|
|
3156
|
-
/* @__PURE__ */ jsx31(
|
|
3157
|
-
ToolbarMenu,
|
|
3158
|
-
{
|
|
3159
|
-
title: "Emoji",
|
|
3160
|
-
variant: "icon",
|
|
3161
|
-
caret: false,
|
|
3162
|
-
align: "end",
|
|
3163
|
-
open: menu === "emoji",
|
|
3164
|
-
onToggle: () => toggleMenu("emoji"),
|
|
3165
|
-
label: /* @__PURE__ */ jsx31(ToolbarIcon, { name: "emoji" }),
|
|
3166
|
-
children: /* @__PURE__ */ jsx31(
|
|
3167
|
-
EmojiMenu,
|
|
3168
|
-
{
|
|
3169
|
-
onPick: (emoji) => run(() => editor.chain().focus().insertContent(emoji).run())
|
|
3170
|
-
}
|
|
3171
|
-
)
|
|
3172
|
-
}
|
|
3173
|
-
)
|
|
3174
|
-
] }),
|
|
3175
|
-
/* @__PURE__ */ jsxs23("div", { className: "ree-rte-row", children: [
|
|
3176
|
-
TEXT_ALIGNS2.map((align) => /* @__PURE__ */ jsx31(
|
|
3928
|
+
onChange: (color) => formatText(editor, (chain) => chain.setColor(color), colorRange.current, "text", false),
|
|
3929
|
+
onReset: () => formatText(editor, (chain) => chain.unsetColor(), colorRange.current, "text", false)
|
|
3930
|
+
}
|
|
3931
|
+
)
|
|
3932
|
+
}
|
|
3933
|
+
),
|
|
3934
|
+
profile !== "button" ? /* @__PURE__ */ jsxs27("div", { className: "ree-rte-group", role: "group", "aria-label": "Paragraph", children: [
|
|
3935
|
+
TEXT_ALIGNS2.filter((align) => profile === "text" || align.value !== "justify").map((align) => /* @__PURE__ */ jsx38(
|
|
3177
3936
|
ToolbarButton,
|
|
3178
3937
|
{
|
|
3179
3938
|
title: align.title,
|
|
3180
3939
|
active: state.align === align.value,
|
|
3181
|
-
onClick: () => editor
|
|
3182
|
-
children: /* @__PURE__ */
|
|
3940
|
+
onClick: () => formatText(editor, (chain) => chain.toggleTextAlign(align.value), null, "paragraph"),
|
|
3941
|
+
children: /* @__PURE__ */ jsx38(ToolbarIcon, { name: align.icon })
|
|
3183
3942
|
},
|
|
3184
3943
|
align.value
|
|
3185
3944
|
)),
|
|
3186
|
-
/* @__PURE__ */
|
|
3187
|
-
/* @__PURE__ */ jsx31(
|
|
3945
|
+
/* @__PURE__ */ jsx38(
|
|
3188
3946
|
ParagraphMetricMenus,
|
|
3189
3947
|
{
|
|
3190
3948
|
menu,
|
|
3949
|
+
showSpacing: profile === "text",
|
|
3191
3950
|
spacing: state.spacing,
|
|
3192
3951
|
lineHeight: state.lineHeight,
|
|
3193
3952
|
inheritedSpacing: inherited.paragraphSpacing,
|
|
3194
3953
|
inheritedLineHeight: inherited.lineHeight,
|
|
3195
3954
|
onToggle: toggleMenu,
|
|
3196
3955
|
onSpacing: (value) => {
|
|
3197
|
-
|
|
3198
|
-
if (value) chain.updateAttributes("paragraph", { spacing: value }).run();
|
|
3199
|
-
else chain.resetAttributes("paragraph", "spacing").run();
|
|
3956
|
+
formatText(editor, (chain) => value ? chain.updateAttributes("paragraph", { spacing: value }) : chain.resetAttributes("paragraph", "spacing"), markRange, "paragraph");
|
|
3200
3957
|
},
|
|
3201
3958
|
onLineHeight: (value) => {
|
|
3202
|
-
|
|
3203
|
-
if (value) chain.updateAttributes("paragraph", { lineHeight: value }).run();
|
|
3204
|
-
else chain.resetAttributes("paragraph", "lineHeight").run();
|
|
3959
|
+
formatText(editor, (chain) => value ? chain.updateAttributes("paragraph", { lineHeight: value }) : chain.resetAttributes("paragraph", "lineHeight"), markRange, "paragraph");
|
|
3205
3960
|
}
|
|
3206
3961
|
}
|
|
3207
|
-
)
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3962
|
+
)
|
|
3963
|
+
] }) : null,
|
|
3964
|
+
profile === "text" ? /* @__PURE__ */ jsx38(
|
|
3965
|
+
ListControls,
|
|
3966
|
+
{
|
|
3967
|
+
editor,
|
|
3968
|
+
state,
|
|
3969
|
+
menu,
|
|
3970
|
+
range: markRange,
|
|
3971
|
+
inherited,
|
|
3972
|
+
onToggle: toggleMenu
|
|
3973
|
+
}
|
|
3974
|
+
) : null,
|
|
3975
|
+
profile !== "button" ? /* @__PURE__ */ jsxs27("div", { className: "ree-rte-group", role: "group", "aria-label": "Links", children: [
|
|
3976
|
+
/* @__PURE__ */ jsx38(
|
|
3219
3977
|
ToolbarButton,
|
|
3220
3978
|
{
|
|
3221
|
-
title: "
|
|
3222
|
-
active: state.
|
|
3223
|
-
onClick: () =>
|
|
3224
|
-
children: /* @__PURE__ */
|
|
3979
|
+
title: "Link",
|
|
3980
|
+
active: Boolean(state.linkHref),
|
|
3981
|
+
onClick: () => toggleMenu("link"),
|
|
3982
|
+
children: /* @__PURE__ */ jsx38(ToolbarIcon, { name: "link" })
|
|
3225
3983
|
}
|
|
3226
3984
|
),
|
|
3227
|
-
/* @__PURE__ */
|
|
3228
|
-
|
|
3229
|
-
ToolbarMenu,
|
|
3985
|
+
menu === "link" ? /* @__PURE__ */ jsx38(
|
|
3986
|
+
LinkPopover,
|
|
3230
3987
|
{
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
fallback: inherited.color,
|
|
3243
|
-
onSelect: (color2) => run(() => focusSelection(editor, markRange.current).setColor(color2).run()),
|
|
3244
|
-
onClear: () => run(() => focusSelection(editor, markRange.current).unsetColor().run())
|
|
3245
|
-
}
|
|
3246
|
-
)
|
|
3988
|
+
url: linkUrl,
|
|
3989
|
+
color: linkColor,
|
|
3990
|
+
decoration: linkDecoration,
|
|
3991
|
+
target: linkTarget,
|
|
3992
|
+
onColorChange: setLinkColor,
|
|
3993
|
+
onDecorationChange: setLinkDecoration,
|
|
3994
|
+
onTargetChange: setLinkTarget,
|
|
3995
|
+
canUnlink: Boolean(state.linkHref),
|
|
3996
|
+
onUrlChange: setLinkUrl,
|
|
3997
|
+
onApply: applyLink,
|
|
3998
|
+
onClose: () => setMenu(null)
|
|
3247
3999
|
}
|
|
3248
|
-
),
|
|
3249
|
-
/* @__PURE__ */
|
|
3250
|
-
|
|
4000
|
+
) : null,
|
|
4001
|
+
/* @__PURE__ */ jsx38(ToolbarButton, { title: "Unlink", disabled: !state.linkHref, onClick: unlinkSelection, children: /* @__PURE__ */ jsx38(ToolbarIcon, { name: "unlink" }) })
|
|
4002
|
+
] }) : null,
|
|
4003
|
+
/* @__PURE__ */ jsxs27("div", { className: "ree-rte-group", role: "group", "aria-label": "Insert", children: [
|
|
4004
|
+
/* @__PURE__ */ jsx38(
|
|
3251
4005
|
ToolbarMenu,
|
|
3252
4006
|
{
|
|
3253
|
-
title: "
|
|
4007
|
+
title: "Emoji",
|
|
3254
4008
|
variant: "icon",
|
|
3255
4009
|
caret: false,
|
|
3256
|
-
active: Boolean(state.linkHref),
|
|
3257
4010
|
align: "end",
|
|
3258
|
-
open: menu === "
|
|
3259
|
-
onToggle: () => toggleMenu("
|
|
3260
|
-
label: /* @__PURE__ */
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
LinkPopover,
|
|
4011
|
+
open: menu === "emoji",
|
|
4012
|
+
onToggle: () => toggleMenu("emoji"),
|
|
4013
|
+
label: /* @__PURE__ */ jsx38(ToolbarIcon, { name: "emoji" }),
|
|
4014
|
+
children: /* @__PURE__ */ jsx38(
|
|
4015
|
+
EmojiMenu,
|
|
3264
4016
|
{
|
|
3265
|
-
|
|
3266
|
-
canUnlink: Boolean(state.linkHref),
|
|
3267
|
-
onUrlChange: setLinkUrl,
|
|
3268
|
-
onApply: applyLink,
|
|
3269
|
-
onUnlink: unlinkSaved
|
|
4017
|
+
onPick: (emoji) => run(() => editor.chain().focus().insertContent(emoji).run())
|
|
3270
4018
|
}
|
|
3271
4019
|
)
|
|
3272
4020
|
}
|
|
3273
4021
|
),
|
|
3274
|
-
|
|
3275
|
-
/* @__PURE__ */ jsx31("span", { className: "ree-rte-sep" }),
|
|
3276
|
-
/* @__PURE__ */ jsx31(
|
|
4022
|
+
variables.length > 0 ? /* @__PURE__ */ jsx38(
|
|
3277
4023
|
ToolbarMenu,
|
|
3278
4024
|
{
|
|
3279
4025
|
title: "Personalization",
|
|
@@ -3281,7 +4027,7 @@ function RichTextToolbar({
|
|
|
3281
4027
|
open: menu === "variable",
|
|
3282
4028
|
onToggle: () => toggleMenu("variable"),
|
|
3283
4029
|
label: "Personalization",
|
|
3284
|
-
children: /* @__PURE__ */
|
|
4030
|
+
children: /* @__PURE__ */ jsx38(
|
|
3285
4031
|
VariableMenu,
|
|
3286
4032
|
{
|
|
3287
4033
|
variables,
|
|
@@ -3289,7 +4035,7 @@ function RichTextToolbar({
|
|
|
3289
4035
|
}
|
|
3290
4036
|
)
|
|
3291
4037
|
}
|
|
3292
|
-
)
|
|
4038
|
+
) : null
|
|
3293
4039
|
] })
|
|
3294
4040
|
]
|
|
3295
4041
|
}
|
|
@@ -3331,8 +4077,76 @@ var FontWeight = Extension.create({
|
|
|
3331
4077
|
}
|
|
3332
4078
|
});
|
|
3333
4079
|
|
|
3334
|
-
// src/stage/rich-text/
|
|
4080
|
+
// src/stage/rich-text/button-document.ts
|
|
4081
|
+
import { Node } from "@tiptap/react";
|
|
4082
|
+
import { Fragment as Fragment4, Slice } from "@tiptap/pm/model";
|
|
4083
|
+
import { Plugin } from "@tiptap/pm/state";
|
|
4084
|
+
var ButtonDocument = Node.create({
|
|
4085
|
+
name: "doc",
|
|
4086
|
+
topNode: true,
|
|
4087
|
+
content: "paragraph",
|
|
4088
|
+
addKeyboardShortcuts() {
|
|
4089
|
+
return { Enter: () => true, "Shift-Enter": () => true };
|
|
4090
|
+
},
|
|
4091
|
+
addProseMirrorPlugins() {
|
|
4092
|
+
return [new Plugin({
|
|
4093
|
+
props: {
|
|
4094
|
+
transformPasted(slice) {
|
|
4095
|
+
const nodes = [];
|
|
4096
|
+
slice.content.descendants((node) => {
|
|
4097
|
+
if (node.isTextblock && nodes.length) nodes.push(node.type.schema.text(" "));
|
|
4098
|
+
if (node.isText) nodes.push(node);
|
|
4099
|
+
});
|
|
4100
|
+
return new Slice(Fragment4.from(nodes), 0, 0);
|
|
4101
|
+
}
|
|
4102
|
+
}
|
|
4103
|
+
})];
|
|
4104
|
+
}
|
|
4105
|
+
});
|
|
4106
|
+
|
|
4107
|
+
// src/stage/rich-text/content-attributes.ts
|
|
4108
|
+
import Link from "@tiptap/extension-link";
|
|
3335
4109
|
import { Extension as Extension2 } from "@tiptap/react";
|
|
4110
|
+
var StyledLink = Link.extend({
|
|
4111
|
+
addAttributes() {
|
|
4112
|
+
var _a;
|
|
4113
|
+
return __spreadProps(__spreadValues({}, (_a = this.parent) == null ? void 0 : _a.call(this)), {
|
|
4114
|
+
color: {
|
|
4115
|
+
default: null,
|
|
4116
|
+
parseHTML: (element) => cssColor(element.style.color, "") || null,
|
|
4117
|
+
renderHTML: (attrs) => attrs.color ? { style: `color:${cssColor(attrs.color)}` } : {}
|
|
4118
|
+
},
|
|
4119
|
+
decoration: {
|
|
4120
|
+
default: null,
|
|
4121
|
+
parseHTML: (element) => element.style.textDecoration === "none" ? "none" : element.style.textDecoration === "underline" ? "underline" : null,
|
|
4122
|
+
renderHTML: (attrs) => attrs.decoration ? { style: `text-decoration:${attrs.decoration === "none" ? "none" : "underline"}` } : {}
|
|
4123
|
+
}
|
|
4124
|
+
});
|
|
4125
|
+
}
|
|
4126
|
+
}).configure({ openOnClick: false, autolink: true });
|
|
4127
|
+
var ListStyle = Extension2.create({
|
|
4128
|
+
name: "listStyle",
|
|
4129
|
+
addGlobalAttributes() {
|
|
4130
|
+
return [{
|
|
4131
|
+
types: ["bulletList", "orderedList"],
|
|
4132
|
+
attributes: {
|
|
4133
|
+
listSpacing: {
|
|
4134
|
+
default: null,
|
|
4135
|
+
parseHTML: (element) => cssLength(element.style.marginTop, "") || null,
|
|
4136
|
+
renderHTML: (attrs) => attrs.listSpacing ? { style: `margin:${cssLength(attrs.listSpacing)} 0` } : {}
|
|
4137
|
+
},
|
|
4138
|
+
listIndent: {
|
|
4139
|
+
default: null,
|
|
4140
|
+
parseHTML: (element) => cssLength(element.style.paddingLeft, "") || null,
|
|
4141
|
+
renderHTML: (attrs) => attrs.listIndent ? { style: `padding-left:${cssLength(attrs.listIndent)}` } : {}
|
|
4142
|
+
}
|
|
4143
|
+
}
|
|
4144
|
+
}];
|
|
4145
|
+
}
|
|
4146
|
+
});
|
|
4147
|
+
|
|
4148
|
+
// src/stage/rich-text/paragraph-style.ts
|
|
4149
|
+
import { Extension as Extension3 } from "@tiptap/react";
|
|
3336
4150
|
function lengthAttribute(value) {
|
|
3337
4151
|
const length = cssLength(value != null ? value : "", "");
|
|
3338
4152
|
return length || null;
|
|
@@ -3341,7 +4155,7 @@ function lineHeightAttribute(value) {
|
|
|
3341
4155
|
const lineHeight = cssLineHeight(value != null ? value : "", "");
|
|
3342
4156
|
return lineHeight || null;
|
|
3343
4157
|
}
|
|
3344
|
-
var ParagraphStyle =
|
|
4158
|
+
var ParagraphStyle = Extension3.create({
|
|
3345
4159
|
name: "paragraphStyle",
|
|
3346
4160
|
addGlobalAttributes() {
|
|
3347
4161
|
return [
|
|
@@ -3379,10 +4193,20 @@ var ParagraphStyle = Extension2.create({
|
|
|
3379
4193
|
});
|
|
3380
4194
|
|
|
3381
4195
|
// src/stage/rich-text/extensions.ts
|
|
3382
|
-
function createRichTextExtensions(placeholder2) {
|
|
4196
|
+
function createRichTextExtensions(placeholder2, profile = "text") {
|
|
4197
|
+
const button = profile === "button";
|
|
4198
|
+
const text = profile === "text";
|
|
3383
4199
|
return [
|
|
3384
4200
|
StarterKit.configure({
|
|
3385
|
-
|
|
4201
|
+
document: button ? false : void 0,
|
|
4202
|
+
trailingNode: button ? false : void 0,
|
|
4203
|
+
link: false,
|
|
4204
|
+
bulletList: text ? void 0 : false,
|
|
4205
|
+
orderedList: text ? void 0 : false,
|
|
4206
|
+
listItem: text ? void 0 : false,
|
|
4207
|
+
listKeymap: text ? void 0 : false,
|
|
4208
|
+
strike: text ? void 0 : false,
|
|
4209
|
+
hardBreak: button ? false : void 0,
|
|
3386
4210
|
blockquote: false,
|
|
3387
4211
|
code: false,
|
|
3388
4212
|
codeBlock: false,
|
|
@@ -3391,17 +4215,17 @@ function createRichTextExtensions(placeholder2) {
|
|
|
3391
4215
|
}),
|
|
3392
4216
|
TextStyleKit.configure({ backgroundColor: false, lineHeight: false }),
|
|
3393
4217
|
FontWeight,
|
|
3394
|
-
ParagraphStyle,
|
|
3395
|
-
TextAlign.configure({
|
|
4218
|
+
...button ? [ButtonDocument] : [ParagraphStyle, StyledLink, TextAlign.configure({
|
|
3396
4219
|
types: ["paragraph"],
|
|
3397
|
-
alignments: Object.values(TEXT_ALIGNS)
|
|
3398
|
-
}),
|
|
4220
|
+
alignments: text ? Object.values(TEXT_ALIGNS) : [TEXT_ALIGNS.left, TEXT_ALIGNS.center, TEXT_ALIGNS.right]
|
|
4221
|
+
})],
|
|
4222
|
+
...text ? [ListStyle] : [],
|
|
3399
4223
|
Placeholder.configure({ placeholder: placeholder2 })
|
|
3400
4224
|
];
|
|
3401
4225
|
}
|
|
3402
4226
|
|
|
3403
4227
|
// src/stage/rich-text/RichText.tsx
|
|
3404
|
-
import { jsx as
|
|
4228
|
+
import { jsx as jsx39, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
3405
4229
|
var DEFAULT_INHERITED = {
|
|
3406
4230
|
fontFamily: DEFAULT_EMAIL_GLOBAL.fontFamily,
|
|
3407
4231
|
fontSize: "15px",
|
|
@@ -3412,36 +4236,41 @@ var DEFAULT_INHERITED = {
|
|
|
3412
4236
|
};
|
|
3413
4237
|
function RichText({
|
|
3414
4238
|
initialHtml,
|
|
4239
|
+
profile = "text",
|
|
3415
4240
|
placeholder: placeholder2,
|
|
3416
4241
|
variables,
|
|
3417
4242
|
selected,
|
|
4243
|
+
toolbarId,
|
|
4244
|
+
toolbarCollapsed,
|
|
3418
4245
|
inherited = DEFAULT_INHERITED,
|
|
3419
4246
|
onCommit,
|
|
3420
4247
|
onSelect
|
|
3421
4248
|
}) {
|
|
3422
4249
|
const rootRef = useRef9(null);
|
|
3423
|
-
const [host, setHost] =
|
|
4250
|
+
const [host, setHost] = useState11(null);
|
|
3424
4251
|
const editor = useEditor({
|
|
3425
|
-
extensions: createRichTextExtensions(placeholder2 != null ? placeholder2 : "Type something\u2026"),
|
|
4252
|
+
extensions: createRichTextExtensions(placeholder2 != null ? placeholder2 : "Type something\u2026", profile),
|
|
3426
4253
|
content: preserveTemplateSpaces(initialHtml),
|
|
3427
4254
|
immediatelyRender: false,
|
|
3428
4255
|
onUpdate: ({ editor: current }) => onCommit(restoreTemplateSpaces(current.getHTML()))
|
|
3429
|
-
});
|
|
3430
|
-
|
|
4256
|
+
}, [profile]);
|
|
4257
|
+
useLayoutEffect6(() => {
|
|
3431
4258
|
var _a;
|
|
3432
4259
|
const node = rootRef.current;
|
|
3433
4260
|
const themeRoot = node == null ? void 0 : node.closest(".ree-root");
|
|
3434
4261
|
setHost(themeRoot instanceof HTMLElement ? themeRoot : (_a = node == null ? void 0 : node.ownerDocument.body) != null ? _a : null);
|
|
3435
4262
|
}, []);
|
|
3436
|
-
|
|
3437
|
-
if (!editor || restoreTemplateSpaces(editor.getHTML()) === initialHtml) return;
|
|
4263
|
+
useEffect4(() => {
|
|
4264
|
+
if (!editor || editor.isDestroyed || restoreTemplateSpaces(editor.getHTML()) === initialHtml) return;
|
|
3438
4265
|
editor.commands.setContent(preserveTemplateSpaces(initialHtml), { emitUpdate: false });
|
|
3439
4266
|
}, [editor, initialHtml]);
|
|
3440
|
-
return /* @__PURE__ */
|
|
3441
|
-
selected && editor && host ? createPortal(
|
|
3442
|
-
/* @__PURE__ */
|
|
4267
|
+
return /* @__PURE__ */ jsxs28("div", { className: `ree-rte ree-rte-${profile}`, ref: rootRef, onClick: onSelect, children: [
|
|
4268
|
+
selected && !toolbarCollapsed && editor && host ? createPortal(
|
|
4269
|
+
/* @__PURE__ */ jsx39(
|
|
3443
4270
|
RichTextToolbar,
|
|
3444
4271
|
{
|
|
4272
|
+
id: toolbarId,
|
|
4273
|
+
profile,
|
|
3445
4274
|
editor,
|
|
3446
4275
|
variables,
|
|
3447
4276
|
anchorRef: rootRef,
|
|
@@ -3450,12 +4279,44 @@ function RichText({
|
|
|
3450
4279
|
),
|
|
3451
4280
|
host
|
|
3452
4281
|
) : null,
|
|
3453
|
-
editor ? /* @__PURE__ */
|
|
4282
|
+
editor ? /* @__PURE__ */ jsx39(EditorContent, { editor }) : /* @__PURE__ */ jsx39("div", { className: "ree-rte-loading", children: "Loading editor\u2026" })
|
|
3454
4283
|
] });
|
|
3455
4284
|
}
|
|
3456
4285
|
|
|
4286
|
+
// src/stage/canvas/components/InlineCanvas.tsx
|
|
4287
|
+
import { jsx as jsx40 } from "react/jsx-runtime";
|
|
4288
|
+
function InlineCanvas(_a) {
|
|
4289
|
+
var _b = _a, { block, global } = _b, props = __objRest(_b, ["block", "global"]);
|
|
4290
|
+
var _a2, _b2, _c, _d;
|
|
4291
|
+
const button = props.profile === "button";
|
|
4292
|
+
const styles = buttonStyles(block, global);
|
|
4293
|
+
const typography = button ? styles.text : typographyStyle(block.style, global);
|
|
4294
|
+
const defaults = contentDefaults(block.style, global);
|
|
4295
|
+
const editor = /* @__PURE__ */ jsx40(
|
|
4296
|
+
RichText,
|
|
4297
|
+
__spreadProps(__spreadValues({}, props), {
|
|
4298
|
+
initialHtml: button ? buttonContent(block) : String((_a2 = block.fields.html) != null ? _a2 : ""),
|
|
4299
|
+
inherited: {
|
|
4300
|
+
fontFamily: String(typography.fontFamily),
|
|
4301
|
+
fontSize: String(typography.fontSize),
|
|
4302
|
+
color: String(typography.color),
|
|
4303
|
+
fontWeight: resolvedFontWeight(button ? void 0 : (_c = (_b2 = block.style) == null ? void 0 : _b2.typography) == null ? void 0 : _c.fontWeight, global.fontWeight),
|
|
4304
|
+
lineHeight: String((_d = typography.lineHeight) != null ? _d : ""),
|
|
4305
|
+
paragraphSpacing: defaults.paragraph,
|
|
4306
|
+
listSpacing: defaults.listSpacing,
|
|
4307
|
+
listIndent: defaults.listIndent,
|
|
4308
|
+
linkColor: defaults.linkColor,
|
|
4309
|
+
linkDecoration: defaults.linkDecoration
|
|
4310
|
+
}
|
|
4311
|
+
})
|
|
4312
|
+
);
|
|
4313
|
+
if (!button) return editor;
|
|
4314
|
+
const align = readAlign(styles.element.align);
|
|
4315
|
+
return /* @__PURE__ */ jsx40("table", { role: "presentation", className: "ree-button-canvas-row", cellPadding: 0, cellSpacing: 0, children: /* @__PURE__ */ jsx40("tbody", { children: /* @__PURE__ */ jsx40("tr", { children: /* @__PURE__ */ jsx40("td", { align, children: /* @__PURE__ */ jsx40("table", { role: "presentation", align, cellPadding: 0, cellSpacing: 0, style: { width: styles.width, maxWidth: "100%" }, children: /* @__PURE__ */ jsx40("tbody", { children: /* @__PURE__ */ jsx40("tr", { children: /* @__PURE__ */ jsx40("td", { style: __spreadValues(__spreadValues({}, styles.box), styles.text), children: editor }) }) }) }) }) }) }) });
|
|
4316
|
+
}
|
|
4317
|
+
|
|
3457
4318
|
// src/stage/canvas/components/HtmlCanvas.tsx
|
|
3458
|
-
import { useCallback, useEffect as
|
|
4319
|
+
import { useCallback, useEffect as useEffect5, useRef as useRef10 } from "react";
|
|
3459
4320
|
|
|
3460
4321
|
// src/render/head-styles.ts
|
|
3461
4322
|
function extractHeadStyles(html) {
|
|
@@ -3469,7 +4330,7 @@ function extractHeadStyles(html) {
|
|
|
3469
4330
|
}
|
|
3470
4331
|
|
|
3471
4332
|
// src/stage/canvas/components/HtmlCanvas.tsx
|
|
3472
|
-
import { jsx as
|
|
4333
|
+
import { jsx as jsx41, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
3473
4334
|
function frameHtml(html) {
|
|
3474
4335
|
const { html: body, styles } = extractHeadStyles(html);
|
|
3475
4336
|
return `<!DOCTYPE html><html><head><style>html,body{margin:0;padding:0;}body{display:flow-root;}</style>${styles}</head><body>${body}</body></html>`;
|
|
@@ -3520,12 +4381,12 @@ function HtmlCanvas({ html, onSelect }) {
|
|
|
3520
4381
|
window.cancelAnimationFrame(animationFrame);
|
|
3521
4382
|
};
|
|
3522
4383
|
}, []);
|
|
3523
|
-
|
|
4384
|
+
useEffect5(() => () => {
|
|
3524
4385
|
var _a;
|
|
3525
4386
|
return (_a = cleanupRef.current) == null ? void 0 : _a.call(cleanupRef);
|
|
3526
4387
|
}, [hasHtmlTag, html]);
|
|
3527
4388
|
if (!hasHtmlTag) {
|
|
3528
|
-
return /* @__PURE__ */
|
|
4389
|
+
return /* @__PURE__ */ jsx41(
|
|
3529
4390
|
"div",
|
|
3530
4391
|
{
|
|
3531
4392
|
className: "ree-html-canvas ree-html-canvas-text",
|
|
@@ -3534,14 +4395,14 @@ function HtmlCanvas({ html, onSelect }) {
|
|
|
3534
4395
|
}
|
|
3535
4396
|
);
|
|
3536
4397
|
}
|
|
3537
|
-
return /* @__PURE__ */
|
|
4398
|
+
return /* @__PURE__ */ jsxs29(
|
|
3538
4399
|
"div",
|
|
3539
4400
|
{
|
|
3540
4401
|
ref: containerRef,
|
|
3541
4402
|
className: "ree-html-canvas",
|
|
3542
4403
|
onClick: () => onSelect(),
|
|
3543
4404
|
children: [
|
|
3544
|
-
/* @__PURE__ */
|
|
4405
|
+
/* @__PURE__ */ jsx41(
|
|
3545
4406
|
"button",
|
|
3546
4407
|
{
|
|
3547
4408
|
type: "button",
|
|
@@ -3552,7 +4413,7 @@ function HtmlCanvas({ html, onSelect }) {
|
|
|
3552
4413
|
onClick: () => onSelect()
|
|
3553
4414
|
}
|
|
3554
4415
|
),
|
|
3555
|
-
/* @__PURE__ */
|
|
4416
|
+
/* @__PURE__ */ jsx41(
|
|
3556
4417
|
"iframe",
|
|
3557
4418
|
{
|
|
3558
4419
|
ref: frameRef,
|
|
@@ -3569,6 +4430,88 @@ function HtmlCanvas({ html, onSelect }) {
|
|
|
3569
4430
|
);
|
|
3570
4431
|
}
|
|
3571
4432
|
|
|
4433
|
+
// src/stage/canvas/components/BlockActions.tsx
|
|
4434
|
+
import { useId as useId4 } from "react";
|
|
4435
|
+
import { jsx as jsx42, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
4436
|
+
function BlockActions({
|
|
4437
|
+
inlineEdit,
|
|
4438
|
+
toolbarId,
|
|
4439
|
+
toolbarCollapsed,
|
|
4440
|
+
onToggleToolbar,
|
|
4441
|
+
onDuplicate,
|
|
4442
|
+
onRemove
|
|
4443
|
+
}) {
|
|
4444
|
+
const tooltipId = useId4();
|
|
4445
|
+
const toggleLabel = toolbarCollapsed ? "Expand Toolbar" : "Collapse Toolbar";
|
|
4446
|
+
return /* @__PURE__ */ jsxs30(
|
|
4447
|
+
"div",
|
|
4448
|
+
{
|
|
4449
|
+
className: "ree-block-toolbar",
|
|
4450
|
+
role: "group",
|
|
4451
|
+
"aria-label": "Block actions",
|
|
4452
|
+
onClick: (event) => event.stopPropagation(),
|
|
4453
|
+
onPointerDown: (event) => event.stopPropagation(),
|
|
4454
|
+
onMouseDown: (event) => {
|
|
4455
|
+
event.stopPropagation();
|
|
4456
|
+
event.preventDefault();
|
|
4457
|
+
},
|
|
4458
|
+
children: [
|
|
4459
|
+
/* @__PURE__ */ jsxs30(
|
|
4460
|
+
"button",
|
|
4461
|
+
{
|
|
4462
|
+
type: "button",
|
|
4463
|
+
className: "ree-block-btn",
|
|
4464
|
+
"aria-label": "Duplicate",
|
|
4465
|
+
"aria-describedby": `${tooltipId}-duplicate`,
|
|
4466
|
+
onClick: onDuplicate,
|
|
4467
|
+
children: [
|
|
4468
|
+
/* @__PURE__ */ jsxs30("svg", { viewBox: "0 0 24 24", width: "16", height: "16", "aria-hidden": "true", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
4469
|
+
/* @__PURE__ */ jsx42("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }),
|
|
4470
|
+
/* @__PURE__ */ jsx42("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
|
|
4471
|
+
] }),
|
|
4472
|
+
/* @__PURE__ */ jsx42("span", { id: `${tooltipId}-duplicate`, className: "ree-block-tooltip", role: "tooltip", children: "Duplicate" })
|
|
4473
|
+
]
|
|
4474
|
+
}
|
|
4475
|
+
),
|
|
4476
|
+
/* @__PURE__ */ jsxs30(
|
|
4477
|
+
"button",
|
|
4478
|
+
{
|
|
4479
|
+
type: "button",
|
|
4480
|
+
className: "ree-block-btn ree-danger",
|
|
4481
|
+
"aria-label": "Delete",
|
|
4482
|
+
"aria-describedby": `${tooltipId}-delete`,
|
|
4483
|
+
onClick: onRemove,
|
|
4484
|
+
children: [
|
|
4485
|
+
/* @__PURE__ */ jsxs30("svg", { viewBox: "0 0 24 24", width: "16", height: "16", "aria-hidden": "true", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
4486
|
+
/* @__PURE__ */ jsx42("path", { d: "M3 6h18" }),
|
|
4487
|
+
/* @__PURE__ */ jsx42("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" }),
|
|
4488
|
+
/* @__PURE__ */ jsx42("path", { d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
|
|
4489
|
+
] }),
|
|
4490
|
+
/* @__PURE__ */ jsx42("span", { id: `${tooltipId}-delete`, className: "ree-block-tooltip", role: "tooltip", children: "Delete" })
|
|
4491
|
+
]
|
|
4492
|
+
}
|
|
4493
|
+
),
|
|
4494
|
+
inlineEdit ? /* @__PURE__ */ jsxs30(
|
|
4495
|
+
"button",
|
|
4496
|
+
{
|
|
4497
|
+
type: "button",
|
|
4498
|
+
className: "ree-block-btn",
|
|
4499
|
+
"aria-label": toggleLabel,
|
|
4500
|
+
"aria-describedby": `${tooltipId}-toolbar`,
|
|
4501
|
+
"aria-controls": toolbarId,
|
|
4502
|
+
"aria-expanded": !toolbarCollapsed,
|
|
4503
|
+
onClick: onToggleToolbar,
|
|
4504
|
+
children: [
|
|
4505
|
+
/* @__PURE__ */ jsx42("svg", { viewBox: "0 0 24 24", width: "16", height: "16", "aria-hidden": "true", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx42("path", { d: toolbarCollapsed ? "M8 3H3v5M3 3l7 7M16 21h5v-5m0 5-7-7" : "M3 8h5V3M8 8 2 2m19 14h-5v5m0-5 6 6" }) }),
|
|
4506
|
+
/* @__PURE__ */ jsx42("span", { id: `${tooltipId}-toolbar`, className: "ree-block-tooltip", role: "tooltip", children: toggleLabel })
|
|
4507
|
+
]
|
|
4508
|
+
}
|
|
4509
|
+
) : null
|
|
4510
|
+
]
|
|
4511
|
+
}
|
|
4512
|
+
);
|
|
4513
|
+
}
|
|
4514
|
+
|
|
3572
4515
|
// src/stage/canvas/block-style.ts
|
|
3573
4516
|
function canvasBlockStyle(block, global) {
|
|
3574
4517
|
var _a, _b;
|
|
@@ -3577,9 +4520,9 @@ function canvasBlockStyle(block, global) {
|
|
|
3577
4520
|
}
|
|
3578
4521
|
|
|
3579
4522
|
// src/stage/canvas/CanvasBlock.tsx
|
|
3580
|
-
import { jsx as
|
|
4523
|
+
import { jsx as jsx43, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
3581
4524
|
function CanvasBlock({
|
|
3582
|
-
block,
|
|
4525
|
+
block: sourceBlock,
|
|
3583
4526
|
global,
|
|
3584
4527
|
definition,
|
|
3585
4528
|
selected,
|
|
@@ -3587,14 +4530,15 @@ function CanvasBlock({
|
|
|
3587
4530
|
variables,
|
|
3588
4531
|
onSelect,
|
|
3589
4532
|
onCommitField,
|
|
3590
|
-
onMove,
|
|
3591
4533
|
onDuplicate,
|
|
3592
4534
|
onRemove
|
|
3593
4535
|
}) {
|
|
3594
|
-
var _a, _b
|
|
4536
|
+
var _a, _b;
|
|
4537
|
+
const block = __spreadProps(__spreadValues({}, sourceBlock), { style: resolveBlockStyle(sourceBlock.style, definition) });
|
|
4538
|
+
const toolbarId = useId5();
|
|
4539
|
+
const [toolbarCollapsed, setToolbarCollapsed] = useState12(false);
|
|
3595
4540
|
const dragData = { kind: "canvas" };
|
|
3596
4541
|
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: block.id, data: dragData });
|
|
3597
|
-
const stop = (event) => event.stopPropagation();
|
|
3598
4542
|
const select = (event) => {
|
|
3599
4543
|
event == null ? void 0 : event.stopPropagation();
|
|
3600
4544
|
onSelect();
|
|
@@ -3605,7 +4549,7 @@ function CanvasBlock({
|
|
|
3605
4549
|
global
|
|
3606
4550
|
}) : "";
|
|
3607
4551
|
const usesTemplatePreview = block.type === "html";
|
|
3608
|
-
return /* @__PURE__ */
|
|
4552
|
+
return /* @__PURE__ */ jsxs31(
|
|
3609
4553
|
"div",
|
|
3610
4554
|
{
|
|
3611
4555
|
ref: setNodeRef,
|
|
@@ -3623,7 +4567,8 @@ function CanvasBlock({
|
|
|
3623
4567
|
].filter(Boolean).join(" "),
|
|
3624
4568
|
onClick: select,
|
|
3625
4569
|
children: [
|
|
3626
|
-
/* @__PURE__ */
|
|
4570
|
+
/* @__PURE__ */ jsx43("span", { className: "ree-block-highlight", "aria-hidden": "true" }),
|
|
4571
|
+
/* @__PURE__ */ jsx43(
|
|
3627
4572
|
"button",
|
|
3628
4573
|
__spreadProps(__spreadValues(__spreadValues({
|
|
3629
4574
|
type: "button",
|
|
@@ -3631,124 +4576,70 @@ function CanvasBlock({
|
|
|
3631
4576
|
}, attributes), listeners), {
|
|
3632
4577
|
onClick: select,
|
|
3633
4578
|
title: "Drag to reorder",
|
|
3634
|
-
children: /* @__PURE__ */
|
|
3635
|
-
/* @__PURE__ */
|
|
3636
|
-
/* @__PURE__ */
|
|
3637
|
-
/* @__PURE__ */
|
|
3638
|
-
/* @__PURE__ */
|
|
3639
|
-
/* @__PURE__ */
|
|
3640
|
-
/* @__PURE__ */
|
|
4579
|
+
children: /* @__PURE__ */ jsxs31("svg", { viewBox: "0 0 24 24", width: "16", height: "16", fill: "currentColor", children: [
|
|
4580
|
+
/* @__PURE__ */ jsx43("circle", { cx: "9", cy: "5", r: "1.6" }),
|
|
4581
|
+
/* @__PURE__ */ jsx43("circle", { cx: "15", cy: "5", r: "1.6" }),
|
|
4582
|
+
/* @__PURE__ */ jsx43("circle", { cx: "9", cy: "12", r: "1.6" }),
|
|
4583
|
+
/* @__PURE__ */ jsx43("circle", { cx: "15", cy: "12", r: "1.6" }),
|
|
4584
|
+
/* @__PURE__ */ jsx43("circle", { cx: "9", cy: "19", r: "1.6" }),
|
|
4585
|
+
/* @__PURE__ */ jsx43("circle", { cx: "15", cy: "19", r: "1.6" })
|
|
3641
4586
|
] })
|
|
3642
4587
|
})
|
|
3643
4588
|
),
|
|
3644
|
-
selected ? /* @__PURE__ */
|
|
3645
|
-
|
|
4589
|
+
selected ? /* @__PURE__ */ jsx43(
|
|
4590
|
+
BlockActions,
|
|
3646
4591
|
{
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
"button",
|
|
3654
|
-
{
|
|
3655
|
-
type: "button",
|
|
3656
|
-
className: "ree-block-btn",
|
|
3657
|
-
title: "Move up",
|
|
3658
|
-
onClick: () => onMove(-1),
|
|
3659
|
-
children: /* @__PURE__ */ jsx34("svg", { viewBox: "0 0 24 24", width: "16", height: "16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx34("path", { d: "m18 15-6-6-6 6" }) })
|
|
3660
|
-
}
|
|
3661
|
-
),
|
|
3662
|
-
/* @__PURE__ */ jsx34(
|
|
3663
|
-
"button",
|
|
3664
|
-
{
|
|
3665
|
-
type: "button",
|
|
3666
|
-
className: "ree-block-btn",
|
|
3667
|
-
title: "Move down",
|
|
3668
|
-
onClick: () => onMove(1),
|
|
3669
|
-
children: /* @__PURE__ */ jsx34("svg", { viewBox: "0 0 24 24", width: "16", height: "16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx34("path", { d: "m6 9 6 6 6-6" }) })
|
|
3670
|
-
}
|
|
3671
|
-
),
|
|
3672
|
-
/* @__PURE__ */ jsx34(
|
|
3673
|
-
"button",
|
|
3674
|
-
{
|
|
3675
|
-
type: "button",
|
|
3676
|
-
className: "ree-block-btn",
|
|
3677
|
-
title: "Duplicate",
|
|
3678
|
-
onClick: onDuplicate,
|
|
3679
|
-
children: /* @__PURE__ */ jsxs26("svg", { viewBox: "0 0 24 24", width: "16", height: "16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
3680
|
-
/* @__PURE__ */ jsx34("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }),
|
|
3681
|
-
/* @__PURE__ */ jsx34("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
|
|
3682
|
-
] })
|
|
3683
|
-
}
|
|
3684
|
-
),
|
|
3685
|
-
/* @__PURE__ */ jsx34(
|
|
3686
|
-
"button",
|
|
3687
|
-
{
|
|
3688
|
-
type: "button",
|
|
3689
|
-
className: "ree-block-btn ree-danger",
|
|
3690
|
-
title: "Delete",
|
|
3691
|
-
onClick: onRemove,
|
|
3692
|
-
children: /* @__PURE__ */ jsxs26("svg", { viewBox: "0 0 24 24", width: "16", height: "16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
3693
|
-
/* @__PURE__ */ jsx34("path", { d: "M3 6h18" }),
|
|
3694
|
-
/* @__PURE__ */ jsx34("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" }),
|
|
3695
|
-
/* @__PURE__ */ jsx34("path", { d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
|
|
3696
|
-
] })
|
|
3697
|
-
}
|
|
3698
|
-
)
|
|
3699
|
-
]
|
|
4592
|
+
inlineEdit: Boolean(definition == null ? void 0 : definition.inlineEdit),
|
|
4593
|
+
toolbarId,
|
|
4594
|
+
toolbarCollapsed,
|
|
4595
|
+
onToggleToolbar: () => setToolbarCollapsed((collapsed) => !collapsed),
|
|
4596
|
+
onDuplicate,
|
|
4597
|
+
onRemove
|
|
3700
4598
|
}
|
|
3701
4599
|
) : null,
|
|
3702
|
-
/* @__PURE__ */
|
|
4600
|
+
/* @__PURE__ */ jsx43(
|
|
3703
4601
|
"div",
|
|
3704
4602
|
{
|
|
3705
4603
|
className: `ree-block-content${(definition == null ? void 0 : definition.inlineEdit) ? " is-inline" : ""}`,
|
|
3706
4604
|
style: canvasBlockStyle(block, global),
|
|
3707
|
-
children: (definition == null ? void 0 : definition.inlineEdit) ? /* @__PURE__ */
|
|
3708
|
-
|
|
4605
|
+
children: (definition == null ? void 0 : definition.inlineEdit) ? /* @__PURE__ */ jsx43(
|
|
4606
|
+
InlineCanvas,
|
|
3709
4607
|
{
|
|
3710
|
-
|
|
4608
|
+
block,
|
|
4609
|
+
global,
|
|
4610
|
+
profile: definition.inlineEditProfile,
|
|
3711
4611
|
variables,
|
|
3712
4612
|
selected,
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
fontSize: String(typographyStyle(block.style, global).fontSize),
|
|
3716
|
-
color: ((_e = (_d = block.style) == null ? void 0 : _d.typography) == null ? void 0 : _e.color) || global.textColor,
|
|
3717
|
-
fontWeight: resolvedFontWeight(
|
|
3718
|
-
(_g = (_f = block.style) == null ? void 0 : _f.typography) == null ? void 0 : _g.fontWeight,
|
|
3719
|
-
global.fontWeight
|
|
3720
|
-
),
|
|
3721
|
-
lineHeight: String((_h = typographyStyle(block.style, global).lineHeight) != null ? _h : ""),
|
|
3722
|
-
paragraphSpacing: contentDefaults(block.style, global).paragraph
|
|
3723
|
-
},
|
|
4613
|
+
toolbarId,
|
|
4614
|
+
toolbarCollapsed,
|
|
3724
4615
|
onSelect,
|
|
3725
4616
|
onCommit: (html) => onCommitField(block.id, "html", html)
|
|
3726
4617
|
}
|
|
3727
|
-
) : usesTemplatePreview ? /* @__PURE__ */
|
|
4618
|
+
) : usesTemplatePreview ? /* @__PURE__ */ jsx43(
|
|
3728
4619
|
HtmlCanvas,
|
|
3729
4620
|
{
|
|
3730
4621
|
html: renderedHtml,
|
|
3731
4622
|
onSelect: select
|
|
3732
4623
|
}
|
|
3733
|
-
) : /* @__PURE__ */
|
|
4624
|
+
) : /* @__PURE__ */ jsx43(
|
|
3734
4625
|
"div",
|
|
3735
4626
|
{
|
|
3736
4627
|
className: "ree-block-static",
|
|
3737
4628
|
dangerouslySetInnerHTML: {
|
|
3738
|
-
__html: definition ? definition.render(buildRenderContext(block, "canvas", global)) : `<!-- ${block.type} -->`
|
|
4629
|
+
__html: definition ? definition.render(buildRenderContext(block, "canvas", global, definition)) : `<!-- ${block.type} -->`
|
|
3739
4630
|
}
|
|
3740
4631
|
}
|
|
3741
4632
|
)
|
|
3742
4633
|
}
|
|
3743
4634
|
),
|
|
3744
|
-
definition ? /* @__PURE__ */
|
|
4635
|
+
definition ? /* @__PURE__ */ jsx43("span", { className: "ree-block-badge", children: definition.title }) : null
|
|
3745
4636
|
]
|
|
3746
4637
|
}
|
|
3747
4638
|
);
|
|
3748
4639
|
}
|
|
3749
4640
|
|
|
3750
4641
|
// src/stage/canvas/Canvas.tsx
|
|
3751
|
-
import { jsx as
|
|
4642
|
+
import { jsx as jsx44 } from "react/jsx-runtime";
|
|
3752
4643
|
function Canvas({
|
|
3753
4644
|
blocks,
|
|
3754
4645
|
global,
|
|
@@ -3760,7 +4651,6 @@ function Canvas({
|
|
|
3760
4651
|
device,
|
|
3761
4652
|
onSelect,
|
|
3762
4653
|
onCommitField,
|
|
3763
|
-
onMove,
|
|
3764
4654
|
onDuplicate,
|
|
3765
4655
|
onRemove
|
|
3766
4656
|
}) {
|
|
@@ -3768,7 +4658,7 @@ function Canvas({
|
|
|
3768
4658
|
id: CANVAS_DROP_ID,
|
|
3769
4659
|
data: { kind: "canvas" }
|
|
3770
4660
|
});
|
|
3771
|
-
return /* @__PURE__ */
|
|
4661
|
+
return /* @__PURE__ */ jsx44(
|
|
3772
4662
|
"div",
|
|
3773
4663
|
{
|
|
3774
4664
|
ref: setNodeRef,
|
|
@@ -3785,7 +4675,7 @@ function Canvas({
|
|
|
3785
4675
|
active.blur();
|
|
3786
4676
|
}
|
|
3787
4677
|
},
|
|
3788
|
-
children: /* @__PURE__ */
|
|
4678
|
+
children: /* @__PURE__ */ jsx44(
|
|
3789
4679
|
"div",
|
|
3790
4680
|
{
|
|
3791
4681
|
className: "ree-email-viewport",
|
|
@@ -3793,52 +4683,57 @@ function Canvas({
|
|
|
3793
4683
|
width: viewportWidth(device, global),
|
|
3794
4684
|
fontSize: global.defaultFontSize,
|
|
3795
4685
|
fontFamily: global.fontFamily,
|
|
3796
|
-
backgroundColor: global.backgroundColor,
|
|
3797
4686
|
marginLeft: global.contentAlign === "left" ? 0 : "auto",
|
|
3798
4687
|
marginRight: global.contentAlign === "right" ? 0 : "auto"
|
|
3799
4688
|
},
|
|
3800
|
-
children: /* @__PURE__ */
|
|
4689
|
+
children: /* @__PURE__ */ jsx44(
|
|
3801
4690
|
"div",
|
|
3802
4691
|
{
|
|
3803
|
-
className: "ree-email-
|
|
3804
|
-
style:
|
|
3805
|
-
|
|
3806
|
-
marginLeft: global.contentAlign === "left" ? 0 : "auto",
|
|
3807
|
-
marginRight: global.contentAlign === "right" ? 0 : "auto"
|
|
3808
|
-
}),
|
|
3809
|
-
children: /* @__PURE__ */ jsx35(
|
|
4692
|
+
className: "ree-email-outer",
|
|
4693
|
+
style: { padding: global.outerPadding, backgroundColor: global.backgroundColor },
|
|
4694
|
+
children: /* @__PURE__ */ jsx44(
|
|
3810
4695
|
"div",
|
|
3811
4696
|
{
|
|
3812
|
-
className: "ree-
|
|
3813
|
-
style: {
|
|
3814
|
-
|
|
3815
|
-
|
|
4697
|
+
className: "ree-email-surface",
|
|
4698
|
+
style: __spreadProps(__spreadValues({}, surfaceStyle(global)), {
|
|
4699
|
+
maxWidth: global.contentWidth,
|
|
4700
|
+
marginLeft: global.contentAlign === "left" ? 0 : "auto",
|
|
4701
|
+
marginRight: global.contentAlign === "right" ? 0 : "auto"
|
|
4702
|
+
}),
|
|
4703
|
+
children: /* @__PURE__ */ jsx44(
|
|
4704
|
+
"div",
|
|
3816
4705
|
{
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
children: blocks.
|
|
3820
|
-
|
|
4706
|
+
className: "ree-canvas-inner",
|
|
4707
|
+
style: { "--ree-block-gap": global.blockGap },
|
|
4708
|
+
children: blocks.length === 0 ? /* @__PURE__ */ jsx44("div", { className: "ree-canvas-empty", children: "Drag a block here or click one to get started" }) : /* @__PURE__ */ jsx44(
|
|
4709
|
+
SortableContext,
|
|
3821
4710
|
{
|
|
3822
|
-
block,
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
4711
|
+
items: blocks.map((block) => block.id),
|
|
4712
|
+
strategy: verticalListSortingStrategy,
|
|
4713
|
+
children: blocks.map((block) => /* @__PURE__ */ jsx44(
|
|
4714
|
+
CanvasBlock,
|
|
4715
|
+
{
|
|
4716
|
+
block,
|
|
4717
|
+
global,
|
|
4718
|
+
definition: getBlockDefinition(block.type, definitions),
|
|
4719
|
+
selected: block.id === selectedId,
|
|
4720
|
+
dropEdge: dropTargetId === block.id ? dropEdge : null,
|
|
4721
|
+
variables,
|
|
4722
|
+
onSelect: () => onSelect(block.id),
|
|
4723
|
+
onCommitField,
|
|
4724
|
+
onDuplicate: () => onDuplicate(block.id),
|
|
4725
|
+
onRemove: () => onRemove(block.id)
|
|
4726
|
+
},
|
|
4727
|
+
block.id
|
|
4728
|
+
))
|
|
4729
|
+
}
|
|
4730
|
+
)
|
|
3836
4731
|
}
|
|
3837
4732
|
)
|
|
3838
4733
|
}
|
|
3839
4734
|
)
|
|
3840
4735
|
}
|
|
3841
|
-
)
|
|
4736
|
+
)
|
|
3842
4737
|
}
|
|
3843
4738
|
)
|
|
3844
4739
|
}
|
|
@@ -3846,7 +4741,7 @@ function Canvas({
|
|
|
3846
4741
|
}
|
|
3847
4742
|
|
|
3848
4743
|
// src/stage/preview/Preview.tsx
|
|
3849
|
-
import { useMemo as useMemo2 } from "react";
|
|
4744
|
+
import { useCallback as useCallback2, useEffect as useEffect6, useMemo as useMemo2, useRef as useRef11 } from "react";
|
|
3850
4745
|
|
|
3851
4746
|
// src/render/document-html.ts
|
|
3852
4747
|
function documentToHtml(document, definitions = DEFAULT_BLOCKS, options = {}) {
|
|
@@ -3854,14 +4749,14 @@ function documentToHtml(document, definitions = DEFAULT_BLOCKS, options = {}) {
|
|
|
3854
4749
|
const global = source.global;
|
|
3855
4750
|
const baseCss = emailBaseCss(global);
|
|
3856
4751
|
const rendered = source.blocks.map((node, index) => {
|
|
3857
|
-
var _a
|
|
3858
|
-
const blockData = __spreadValues(__spreadValues({},
|
|
4752
|
+
var _a;
|
|
4753
|
+
const blockData = __spreadValues(__spreadValues({}, options.templateData), node.defaultData);
|
|
3859
4754
|
const block = blockToHtml(node, definitions, "email", {
|
|
3860
4755
|
resolveHtmlTemplate: options.resolveHtmlTemplates,
|
|
3861
4756
|
global,
|
|
3862
4757
|
variables: options.variables,
|
|
3863
4758
|
templateData: mergeTemplateData(
|
|
3864
|
-
(
|
|
4759
|
+
(_a = options.variables) != null ? _a : [],
|
|
3865
4760
|
Object.keys(blockData).length > 0 ? blockData : void 0
|
|
3866
4761
|
)
|
|
3867
4762
|
});
|
|
@@ -3898,8 +4793,10 @@ function documentToHtml(document, definitions = DEFAULT_BLOCKS, options = {}) {
|
|
|
3898
4793
|
}
|
|
3899
4794
|
|
|
3900
4795
|
// src/stage/preview/Preview.tsx
|
|
3901
|
-
import { jsx as
|
|
4796
|
+
import { jsx as jsx45 } from "react/jsx-runtime";
|
|
3902
4797
|
function Preview({ document, definitions, variables, device }) {
|
|
4798
|
+
const frameRef = useRef11(null);
|
|
4799
|
+
const cleanupRef = useRef11();
|
|
3903
4800
|
const html = useMemo2(
|
|
3904
4801
|
() => documentToHtml(document, definitions, {
|
|
3905
4802
|
resolveHtmlTemplates: true,
|
|
@@ -3907,34 +4804,73 @@ function Preview({ document, definitions, variables, device }) {
|
|
|
3907
4804
|
}),
|
|
3908
4805
|
[document, definitions, variables]
|
|
3909
4806
|
);
|
|
3910
|
-
|
|
3911
|
-
|
|
4807
|
+
const observeContent = useCallback2(() => {
|
|
4808
|
+
var _a;
|
|
4809
|
+
(_a = cleanupRef.current) == null ? void 0 : _a.call(cleanupRef);
|
|
4810
|
+
const frame = frameRef.current;
|
|
4811
|
+
const frameDocument = frame == null ? void 0 : frame.contentDocument;
|
|
4812
|
+
const frameWindow = frameDocument == null ? void 0 : frameDocument.defaultView;
|
|
4813
|
+
if (!frame || !(frameDocument == null ? void 0 : frameDocument.body) || !frameWindow) return;
|
|
4814
|
+
let animationFrame = 0;
|
|
4815
|
+
const fit = () => {
|
|
4816
|
+
frame.style.height = `${frameDocument.body.getBoundingClientRect().height}px`;
|
|
4817
|
+
};
|
|
4818
|
+
const scheduleFit = () => {
|
|
4819
|
+
frameWindow.cancelAnimationFrame(animationFrame);
|
|
4820
|
+
animationFrame = frameWindow.requestAnimationFrame(fit);
|
|
4821
|
+
};
|
|
4822
|
+
const observer = new frameWindow.ResizeObserver(scheduleFit);
|
|
4823
|
+
observer.observe(frameDocument.body);
|
|
4824
|
+
frameDocument.addEventListener("load", scheduleFit, true);
|
|
4825
|
+
fit();
|
|
4826
|
+
cleanupRef.current = () => {
|
|
4827
|
+
observer.disconnect();
|
|
4828
|
+
frameDocument.removeEventListener("load", scheduleFit, true);
|
|
4829
|
+
frameWindow.cancelAnimationFrame(animationFrame);
|
|
4830
|
+
};
|
|
4831
|
+
}, []);
|
|
4832
|
+
useEffect6(() => () => {
|
|
4833
|
+
var _a;
|
|
4834
|
+
return (_a = cleanupRef.current) == null ? void 0 : _a.call(cleanupRef);
|
|
4835
|
+
}, [html]);
|
|
4836
|
+
return /* @__PURE__ */ jsx45("div", { className: "ree-preview-stage", children: /* @__PURE__ */ jsx45(
|
|
4837
|
+
"div",
|
|
3912
4838
|
{
|
|
3913
|
-
className: "ree-
|
|
3914
|
-
title: "Email preview",
|
|
3915
|
-
srcDoc: html,
|
|
4839
|
+
className: "ree-email-viewport",
|
|
3916
4840
|
style: {
|
|
3917
4841
|
width: viewportWidth(device, document.global),
|
|
3918
|
-
fontSize: document.global.defaultFontSize,
|
|
3919
4842
|
marginLeft: document.global.contentAlign === "left" ? 0 : "auto",
|
|
3920
|
-
marginRight: document.global.contentAlign === "right" ? 0 : "auto"
|
|
3921
|
-
|
|
3922
|
-
|
|
4843
|
+
marginRight: document.global.contentAlign === "right" ? 0 : "auto"
|
|
4844
|
+
},
|
|
4845
|
+
children: /* @__PURE__ */ jsx45(
|
|
4846
|
+
"iframe",
|
|
4847
|
+
{
|
|
4848
|
+
ref: frameRef,
|
|
4849
|
+
className: "ree-preview-frame",
|
|
4850
|
+
title: "Email preview",
|
|
4851
|
+
srcDoc: html,
|
|
4852
|
+
onLoad: observeContent,
|
|
4853
|
+
style: {
|
|
4854
|
+
fontSize: document.global.defaultFontSize,
|
|
4855
|
+
backgroundColor: document.global.backgroundColor || "#ffffff"
|
|
4856
|
+
}
|
|
4857
|
+
}
|
|
4858
|
+
)
|
|
3923
4859
|
}
|
|
3924
4860
|
) });
|
|
3925
4861
|
}
|
|
3926
4862
|
|
|
3927
4863
|
// src/stage/StageHeader.tsx
|
|
3928
|
-
import { jsx as
|
|
4864
|
+
import { jsx as jsx46, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
3929
4865
|
function StageHeader({
|
|
3930
4866
|
viewMode,
|
|
3931
4867
|
device,
|
|
3932
4868
|
onViewModeChange,
|
|
3933
4869
|
onDeviceChange
|
|
3934
4870
|
}) {
|
|
3935
|
-
return /* @__PURE__ */
|
|
3936
|
-
/* @__PURE__ */
|
|
3937
|
-
/* @__PURE__ */
|
|
4871
|
+
return /* @__PURE__ */ jsxs32("div", { className: "ree-col-head ree-stage-head", children: [
|
|
4872
|
+
/* @__PURE__ */ jsxs32("div", { className: "ree-segment", role: "group", "aria-label": "Editor mode", children: [
|
|
4873
|
+
/* @__PURE__ */ jsx46(
|
|
3938
4874
|
"button",
|
|
3939
4875
|
{
|
|
3940
4876
|
type: "button",
|
|
@@ -3943,14 +4879,14 @@ function StageHeader({
|
|
|
3943
4879
|
"aria-label": "Edit",
|
|
3944
4880
|
title: "Edit",
|
|
3945
4881
|
onClick: () => onViewModeChange("edit"),
|
|
3946
|
-
children: /* @__PURE__ */
|
|
3947
|
-
/* @__PURE__ */
|
|
3948
|
-
/* @__PURE__ */
|
|
4882
|
+
children: /* @__PURE__ */ jsxs32("svg", { viewBox: "0 0 24 24", width: "20", height: "20", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
4883
|
+
/* @__PURE__ */ jsx46("path", { d: "M12 20h9" }),
|
|
4884
|
+
/* @__PURE__ */ jsx46("path", { d: "M16.5 3.5a2.1 2.1 0 0 1 3 3L8 18l-4 1 1-4 11.5-11.5Z" })
|
|
3949
4885
|
] })
|
|
3950
4886
|
}
|
|
3951
4887
|
),
|
|
3952
|
-
/* @__PURE__ */
|
|
3953
|
-
/* @__PURE__ */
|
|
4888
|
+
/* @__PURE__ */ jsx46("span", { className: "ree-segment-divider", "aria-hidden": "true" }),
|
|
4889
|
+
/* @__PURE__ */ jsx46(
|
|
3954
4890
|
"button",
|
|
3955
4891
|
{
|
|
3956
4892
|
type: "button",
|
|
@@ -3959,15 +4895,15 @@ function StageHeader({
|
|
|
3959
4895
|
"aria-label": "Preview",
|
|
3960
4896
|
title: "Preview",
|
|
3961
4897
|
onClick: () => onViewModeChange("preview"),
|
|
3962
|
-
children: /* @__PURE__ */
|
|
3963
|
-
/* @__PURE__ */
|
|
3964
|
-
/* @__PURE__ */
|
|
4898
|
+
children: /* @__PURE__ */ jsxs32("svg", { viewBox: "0 0 24 24", width: "20", height: "20", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
4899
|
+
/* @__PURE__ */ jsx46("path", { d: "M2.5 12s3.5-6 9.5-6 9.5 6 9.5 6-3.5 6-9.5 6-9.5-6-9.5-6Z" }),
|
|
4900
|
+
/* @__PURE__ */ jsx46("circle", { cx: "12", cy: "12", r: "2.5" })
|
|
3965
4901
|
] })
|
|
3966
4902
|
}
|
|
3967
4903
|
)
|
|
3968
4904
|
] }),
|
|
3969
|
-
/* @__PURE__ */
|
|
3970
|
-
/* @__PURE__ */
|
|
4905
|
+
/* @__PURE__ */ jsxs32("div", { className: "ree-segment", role: "group", "aria-label": "Canvas device", children: [
|
|
4906
|
+
/* @__PURE__ */ jsx46(
|
|
3971
4907
|
"button",
|
|
3972
4908
|
{
|
|
3973
4909
|
type: "button",
|
|
@@ -3976,15 +4912,15 @@ function StageHeader({
|
|
|
3976
4912
|
"aria-label": "Desktop",
|
|
3977
4913
|
title: "Desktop",
|
|
3978
4914
|
onClick: () => onDeviceChange("desktop"),
|
|
3979
|
-
children: /* @__PURE__ */
|
|
3980
|
-
/* @__PURE__ */
|
|
3981
|
-
/* @__PURE__ */
|
|
3982
|
-
/* @__PURE__ */
|
|
4915
|
+
children: /* @__PURE__ */ jsxs32("svg", { viewBox: "0 0 24 24", width: "20", height: "20", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
4916
|
+
/* @__PURE__ */ jsx46("rect", { x: "3", y: "4", width: "18", height: "12", rx: "1.5" }),
|
|
4917
|
+
/* @__PURE__ */ jsx46("path", { d: "M8 20h8" }),
|
|
4918
|
+
/* @__PURE__ */ jsx46("path", { d: "M12 16v4" })
|
|
3983
4919
|
] })
|
|
3984
4920
|
}
|
|
3985
4921
|
),
|
|
3986
|
-
/* @__PURE__ */
|
|
3987
|
-
/* @__PURE__ */
|
|
4922
|
+
/* @__PURE__ */ jsx46("span", { className: "ree-segment-divider", "aria-hidden": "true" }),
|
|
4923
|
+
/* @__PURE__ */ jsx46(
|
|
3988
4924
|
"button",
|
|
3989
4925
|
{
|
|
3990
4926
|
type: "button",
|
|
@@ -3993,9 +4929,9 @@ function StageHeader({
|
|
|
3993
4929
|
"aria-label": "Mobile",
|
|
3994
4930
|
title: "Mobile",
|
|
3995
4931
|
onClick: () => onDeviceChange("mobile"),
|
|
3996
|
-
children: /* @__PURE__ */
|
|
3997
|
-
/* @__PURE__ */
|
|
3998
|
-
/* @__PURE__ */
|
|
4932
|
+
children: /* @__PURE__ */ jsxs32("svg", { viewBox: "0 0 24 24", width: "20", height: "20", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
4933
|
+
/* @__PURE__ */ jsx46("rect", { x: "7", y: "2.5", width: "10", height: "19", rx: "2" }),
|
|
4934
|
+
/* @__PURE__ */ jsx46("path", { d: "M11 18.5h2" })
|
|
3999
4935
|
] })
|
|
4000
4936
|
}
|
|
4001
4937
|
)
|
|
@@ -4004,7 +4940,7 @@ function StageHeader({
|
|
|
4004
4940
|
}
|
|
4005
4941
|
|
|
4006
4942
|
// src/stage/Stage.tsx
|
|
4007
|
-
import { jsx as
|
|
4943
|
+
import { jsx as jsx47, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
4008
4944
|
function Stage({
|
|
4009
4945
|
session,
|
|
4010
4946
|
viewMode,
|
|
@@ -4014,8 +4950,8 @@ function Stage({
|
|
|
4014
4950
|
onSelectBlock
|
|
4015
4951
|
}) {
|
|
4016
4952
|
const { targetId, dropEdge } = useEditorDragState();
|
|
4017
|
-
return /* @__PURE__ */
|
|
4018
|
-
/* @__PURE__ */
|
|
4953
|
+
return /* @__PURE__ */ jsxs33("div", { className: "ree-col ree-col-center", children: [
|
|
4954
|
+
/* @__PURE__ */ jsx47(
|
|
4019
4955
|
StageHeader,
|
|
4020
4956
|
{
|
|
4021
4957
|
viewMode,
|
|
@@ -4024,7 +4960,7 @@ function Stage({
|
|
|
4024
4960
|
onDeviceChange
|
|
4025
4961
|
}
|
|
4026
4962
|
),
|
|
4027
|
-
viewMode === "edit" ? /* @__PURE__ */
|
|
4963
|
+
viewMode === "edit" ? /* @__PURE__ */ jsx47("div", { className: "ree-stage-view", children: /* @__PURE__ */ jsx47(
|
|
4028
4964
|
Canvas,
|
|
4029
4965
|
{
|
|
4030
4966
|
blocks: session.document.blocks,
|
|
@@ -4037,11 +4973,10 @@ function Stage({
|
|
|
4037
4973
|
device,
|
|
4038
4974
|
onSelect: onSelectBlock,
|
|
4039
4975
|
onCommitField: session.updateField,
|
|
4040
|
-
onMove: session.moveBlock,
|
|
4041
4976
|
onDuplicate: session.duplicateBlock,
|
|
4042
4977
|
onRemove: session.removeBlock
|
|
4043
4978
|
}
|
|
4044
|
-
) }) : /* @__PURE__ */
|
|
4979
|
+
) }) : /* @__PURE__ */ jsx47("div", { className: "ree-stage-view", children: /* @__PURE__ */ jsx47(
|
|
4045
4980
|
Preview,
|
|
4046
4981
|
{
|
|
4047
4982
|
document: session.document,
|
|
@@ -4059,17 +4994,17 @@ var EMPTY_VARIABLES = [];
|
|
|
4059
4994
|
var PERSONALIZATION_HINT = "Personalization fields will appear blank if no data is available.";
|
|
4060
4995
|
|
|
4061
4996
|
// src/shell/SubjectBar.tsx
|
|
4062
|
-
import { useRef as
|
|
4997
|
+
import { useRef as useRef13 } from "react";
|
|
4063
4998
|
|
|
4064
4999
|
// src/shell/PersonalizationMenu.tsx
|
|
4065
|
-
import { useEffect as useEffect7, useRef as
|
|
4066
|
-
import { jsx as
|
|
5000
|
+
import { useEffect as useEffect7, useRef as useRef12, useState as useState13 } from "react";
|
|
5001
|
+
import { jsx as jsx48, jsxs as jsxs34 } from "react/jsx-runtime";
|
|
4067
5002
|
function PersonalizationMenu({
|
|
4068
5003
|
variables,
|
|
4069
5004
|
onInsert
|
|
4070
5005
|
}) {
|
|
4071
|
-
const [open, setOpen] =
|
|
4072
|
-
const rootRef =
|
|
5006
|
+
const [open, setOpen] = useState13(false);
|
|
5007
|
+
const rootRef = useRef12(null);
|
|
4073
5008
|
useEffect7(() => {
|
|
4074
5009
|
const root = rootRef.current;
|
|
4075
5010
|
if (!open || !root) return;
|
|
@@ -4080,8 +5015,9 @@ function PersonalizationMenu({
|
|
|
4080
5015
|
ownerDocument.addEventListener("pointerdown", onPointerDown, true);
|
|
4081
5016
|
return () => ownerDocument.removeEventListener("pointerdown", onPointerDown, true);
|
|
4082
5017
|
}, [open]);
|
|
4083
|
-
|
|
4084
|
-
|
|
5018
|
+
if (variables.length === 0) return null;
|
|
5019
|
+
return /* @__PURE__ */ jsxs34("div", { className: "ree-personalization", ref: rootRef, children: [
|
|
5020
|
+
/* @__PURE__ */ jsxs34(
|
|
4085
5021
|
"button",
|
|
4086
5022
|
{
|
|
4087
5023
|
type: "button",
|
|
@@ -4090,11 +5026,11 @@ function PersonalizationMenu({
|
|
|
4090
5026
|
onClick: () => setOpen((current) => !current),
|
|
4091
5027
|
children: [
|
|
4092
5028
|
"Personalization",
|
|
4093
|
-
/* @__PURE__ */
|
|
5029
|
+
/* @__PURE__ */ jsx48("svg", { viewBox: "0 0 20 20", width: "16", height: "16", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsx48("path", { d: "M5.2 7.5 10 12.3l4.8-4.8 1.2 1.2L10 14.7 4 8.7l1.2-1.2Z" }) })
|
|
4094
5030
|
]
|
|
4095
5031
|
}
|
|
4096
5032
|
),
|
|
4097
|
-
open ? /* @__PURE__ */
|
|
5033
|
+
open ? /* @__PURE__ */ jsx48("div", { className: "ree-var-menu ree-personalization-menu", children: variables.map((variable) => /* @__PURE__ */ jsxs34(
|
|
4098
5034
|
"button",
|
|
4099
5035
|
{
|
|
4100
5036
|
type: "button",
|
|
@@ -4104,8 +5040,8 @@ function PersonalizationMenu({
|
|
|
4104
5040
|
setOpen(false);
|
|
4105
5041
|
},
|
|
4106
5042
|
children: [
|
|
4107
|
-
/* @__PURE__ */
|
|
4108
|
-
variable.description ? /* @__PURE__ */
|
|
5043
|
+
/* @__PURE__ */ jsx48("span", { className: "ree-var-name", children: variable.name }),
|
|
5044
|
+
variable.description ? /* @__PURE__ */ jsx48("span", { className: "ree-var-label", children: variable.description }) : null
|
|
4109
5045
|
]
|
|
4110
5046
|
},
|
|
4111
5047
|
variable.name
|
|
@@ -4114,14 +5050,13 @@ function PersonalizationMenu({
|
|
|
4114
5050
|
}
|
|
4115
5051
|
|
|
4116
5052
|
// src/shell/SubjectBar.tsx
|
|
4117
|
-
import { Fragment as
|
|
5053
|
+
import { Fragment as Fragment5, jsx as jsx49, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
4118
5054
|
function SubjectBar({
|
|
4119
5055
|
subject,
|
|
4120
5056
|
variables,
|
|
4121
|
-
onSubjectChange
|
|
4122
|
-
onReset
|
|
5057
|
+
onSubjectChange
|
|
4123
5058
|
}) {
|
|
4124
|
-
const inputRef =
|
|
5059
|
+
const inputRef = useRef13(null);
|
|
4125
5060
|
const insertVariable = (token) => {
|
|
4126
5061
|
var _a, _b;
|
|
4127
5062
|
const input = inputRef.current;
|
|
@@ -4139,10 +5074,10 @@ function SubjectBar({
|
|
|
4139
5074
|
(_b2 = inputRef.current) == null ? void 0 : _b2.setSelectionRange(cursor, cursor);
|
|
4140
5075
|
});
|
|
4141
5076
|
};
|
|
4142
|
-
return /* @__PURE__ */
|
|
4143
|
-
/* @__PURE__ */
|
|
4144
|
-
/* @__PURE__ */
|
|
4145
|
-
|
|
5077
|
+
return /* @__PURE__ */ jsxs35("div", { className: "ree-subject-bar", children: [
|
|
5078
|
+
/* @__PURE__ */ jsx49("label", { className: "ree-subject-label", htmlFor: "ree-email-subject", children: "Email subject" }),
|
|
5079
|
+
/* @__PURE__ */ jsx49(
|
|
5080
|
+
TextInput,
|
|
4146
5081
|
{
|
|
4147
5082
|
id: "ree-email-subject",
|
|
4148
5083
|
ref: inputRef,
|
|
@@ -4152,21 +5087,20 @@ function SubjectBar({
|
|
|
4152
5087
|
onChange: (event) => onSubjectChange(event.target.value)
|
|
4153
5088
|
}
|
|
4154
5089
|
),
|
|
4155
|
-
/* @__PURE__ */
|
|
5090
|
+
/* @__PURE__ */ jsxs35("span", { className: "ree-subject-count", children: [
|
|
4156
5091
|
subject.length,
|
|
4157
5092
|
"/",
|
|
4158
5093
|
SUBJECT_MAX_LENGTH
|
|
4159
5094
|
] }),
|
|
4160
|
-
variables.length > 0 ? /* @__PURE__ */
|
|
4161
|
-
/* @__PURE__ */
|
|
4162
|
-
/* @__PURE__ */
|
|
4163
|
-
] }) : null
|
|
4164
|
-
/* @__PURE__ */ jsx40("button", { type: "button", className: "ree-btn ree-subject-reset", onClick: onReset, children: "Reset" })
|
|
5095
|
+
variables.length > 0 ? /* @__PURE__ */ jsxs35(Fragment5, { children: [
|
|
5096
|
+
/* @__PURE__ */ jsx49(PersonalizationMenu, { variables, onInsert: insertVariable }),
|
|
5097
|
+
/* @__PURE__ */ jsx49("p", { className: "ree-subject-hint", children: PERSONALIZATION_HINT })
|
|
5098
|
+
] }) : null
|
|
4165
5099
|
] });
|
|
4166
5100
|
}
|
|
4167
5101
|
|
|
4168
5102
|
// src/shell/useEditorSession.ts
|
|
4169
|
-
import { useCallback as
|
|
5103
|
+
import { useCallback as useCallback3, useEffect as useEffect8, useLayoutEffect as useLayoutEffect7, useMemo as useMemo3, useRef as useRef14, useState as useState14 } from "react";
|
|
4170
5104
|
|
|
4171
5105
|
// src/utils/id.ts
|
|
4172
5106
|
function generateId(prefix = "block") {
|
|
@@ -4178,11 +5112,11 @@ function generateId(prefix = "block") {
|
|
|
4178
5112
|
|
|
4179
5113
|
// src/document/create-block.ts
|
|
4180
5114
|
function createBlockNode(type, definitions) {
|
|
4181
|
-
var _a
|
|
5115
|
+
var _a;
|
|
4182
5116
|
const definition = getBlockDefinition(type, definitions);
|
|
4183
5117
|
if (!definition) return null;
|
|
4184
|
-
const fields = __spreadValues({},
|
|
4185
|
-
for (const field of (
|
|
5118
|
+
const fields = __spreadValues({}, definition.defaultFields);
|
|
5119
|
+
for (const field of (_a = definition.fields) != null ? _a : []) {
|
|
4186
5120
|
if (!(field.name in fields) && field.defaultValue !== void 0) {
|
|
4187
5121
|
fields[field.name] = field.defaultValue;
|
|
4188
5122
|
}
|
|
@@ -4194,7 +5128,7 @@ function createBlockNode(type, definitions) {
|
|
|
4194
5128
|
id: generateId(type),
|
|
4195
5129
|
type,
|
|
4196
5130
|
fields,
|
|
4197
|
-
style: definition.defaultStyle ?
|
|
5131
|
+
style: definition.defaultStyle ? resolveBlockStyle(definition.defaultStyle, definition) : void 0,
|
|
4198
5132
|
defaultData: definition.defaultData ? structuredClone(definition.defaultData) : void 0
|
|
4199
5133
|
};
|
|
4200
5134
|
}
|
|
@@ -4276,22 +5210,19 @@ function useEditorSession({
|
|
|
4276
5210
|
}) {
|
|
4277
5211
|
var _a;
|
|
4278
5212
|
const definitions = useMemo3(() => mergeBlockDefinitions(blocks), [blocks]);
|
|
4279
|
-
const [
|
|
5213
|
+
const [document, setDocument] = useState14(() => {
|
|
4280
5214
|
const base = ensureEmailDocument(
|
|
4281
5215
|
structuredClone(initialDocument != null ? initialDocument : createEmptyDocument())
|
|
4282
5216
|
);
|
|
4283
5217
|
if (global === void 0) return base;
|
|
4284
5218
|
return __spreadProps(__spreadValues({}, base), { global: mergeEmailGlobal(base.global, global) });
|
|
4285
5219
|
});
|
|
4286
|
-
const [
|
|
4287
|
-
|
|
4288
|
-
)
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
const globalPropRef = useRef13(global);
|
|
4293
|
-
globalPropRef.current = global;
|
|
4294
|
-
const commit = useCallback2(
|
|
5220
|
+
const [selectedId, setSelectedId] = useState14(null);
|
|
5221
|
+
const documentRef = useRef14(document);
|
|
5222
|
+
useLayoutEffect7(() => {
|
|
5223
|
+
documentRef.current = document;
|
|
5224
|
+
}, [document]);
|
|
5225
|
+
const commit = useCallback3(
|
|
4295
5226
|
(next) => {
|
|
4296
5227
|
documentRef.current = next;
|
|
4297
5228
|
setDocument(next);
|
|
@@ -4306,14 +5237,14 @@ function useEditorSession({
|
|
|
4306
5237
|
if (sameEmailGlobal(resolveEmailGlobal(current.global), nextGlobal)) return;
|
|
4307
5238
|
commit(__spreadProps(__spreadValues({}, current), { global: nextGlobal }));
|
|
4308
5239
|
}, [global, commit]);
|
|
4309
|
-
const getDocument =
|
|
5240
|
+
const getDocument = useCallback3(
|
|
4310
5241
|
() => ensureEmailDocument(structuredClone(documentRef.current)),
|
|
4311
5242
|
[]
|
|
4312
5243
|
);
|
|
4313
|
-
const selectBlock =
|
|
5244
|
+
const selectBlock = useCallback3((id) => {
|
|
4314
5245
|
setSelectedId(id);
|
|
4315
5246
|
}, []);
|
|
4316
|
-
const insertBlock =
|
|
5247
|
+
const insertBlock = useCallback3(
|
|
4317
5248
|
(type, index) => {
|
|
4318
5249
|
const node = createBlockNode(type, definitions);
|
|
4319
5250
|
if (!node) return false;
|
|
@@ -4323,19 +5254,19 @@ function useEditorSession({
|
|
|
4323
5254
|
},
|
|
4324
5255
|
[commit, definitions]
|
|
4325
5256
|
);
|
|
4326
|
-
const updateField =
|
|
5257
|
+
const updateField = useCallback3(
|
|
4327
5258
|
(id, name, value) => {
|
|
4328
5259
|
commit(updateBlockField(documentRef.current, id, name, value));
|
|
4329
5260
|
},
|
|
4330
5261
|
[commit]
|
|
4331
5262
|
);
|
|
4332
|
-
const updateStyle =
|
|
5263
|
+
const updateStyle = useCallback3(
|
|
4333
5264
|
(id, styleId, value) => {
|
|
4334
5265
|
commit(updateBlockStyle(documentRef.current, id, styleId, value));
|
|
4335
5266
|
},
|
|
4336
5267
|
[commit]
|
|
4337
5268
|
);
|
|
4338
|
-
const updateGlobal =
|
|
5269
|
+
const updateGlobal = useCallback3(
|
|
4339
5270
|
(patch) => {
|
|
4340
5271
|
const next = updateGlobalSettings(documentRef.current, patch);
|
|
4341
5272
|
commit(next);
|
|
@@ -4343,44 +5274,37 @@ function useEditorSession({
|
|
|
4343
5274
|
},
|
|
4344
5275
|
[commit, onGlobalChange]
|
|
4345
5276
|
);
|
|
4346
|
-
const moveBlock =
|
|
5277
|
+
const moveBlock = useCallback3(
|
|
4347
5278
|
(id, direction) => {
|
|
4348
5279
|
commit(moveBlockBy(documentRef.current, id, direction));
|
|
4349
5280
|
},
|
|
4350
5281
|
[commit]
|
|
4351
5282
|
);
|
|
4352
|
-
const duplicateBlock =
|
|
5283
|
+
const duplicateBlock = useCallback3(
|
|
4353
5284
|
(id) => {
|
|
4354
5285
|
commit(duplicateBlockNode(documentRef.current, id));
|
|
4355
5286
|
},
|
|
4356
5287
|
[commit]
|
|
4357
5288
|
);
|
|
4358
|
-
const removeBlock =
|
|
5289
|
+
const removeBlock = useCallback3(
|
|
4359
5290
|
(id) => {
|
|
4360
5291
|
commit(removeBlockNode(documentRef.current, id));
|
|
4361
5292
|
setSelectedId((current) => current === id ? null : current);
|
|
4362
5293
|
},
|
|
4363
5294
|
[commit]
|
|
4364
5295
|
);
|
|
4365
|
-
const reorderBlocks =
|
|
5296
|
+
const reorderBlocks = useCallback3(
|
|
4366
5297
|
(activeId, overId) => {
|
|
4367
5298
|
commit(reorderBlockNodes(documentRef.current, activeId, overId));
|
|
4368
5299
|
},
|
|
4369
5300
|
[commit]
|
|
4370
5301
|
);
|
|
4371
|
-
const setSubject =
|
|
5302
|
+
const setSubject = useCallback3(
|
|
4372
5303
|
(subject) => {
|
|
4373
5304
|
commit(withSubject(documentRef.current, subject.slice(0, SUBJECT_MAX_LENGTH)));
|
|
4374
5305
|
},
|
|
4375
5306
|
[commit]
|
|
4376
5307
|
);
|
|
4377
|
-
const reset = useCallback2(() => {
|
|
4378
|
-
const restored = structuredClone(baseline);
|
|
4379
|
-
const incoming = globalPropRef.current;
|
|
4380
|
-
const next = incoming === void 0 ? restored : __spreadProps(__spreadValues({}, restored), { global: mergeEmailGlobal(restored.global, incoming) });
|
|
4381
|
-
commit(next);
|
|
4382
|
-
setSelectedId(null);
|
|
4383
|
-
}, [baseline, commit]);
|
|
4384
5308
|
const resolvedDocument = useMemo3(() => ensureEmailDocument(document), [document]);
|
|
4385
5309
|
const selectedBlock = (_a = resolvedDocument.blocks.find((block) => block.id === selectedId)) != null ? _a : null;
|
|
4386
5310
|
const selectedDefinition = selectedBlock ? getBlockDefinition(selectedBlock.type, definitions) : void 0;
|
|
@@ -4401,8 +5325,7 @@ function useEditorSession({
|
|
|
4401
5325
|
duplicateBlock,
|
|
4402
5326
|
removeBlock,
|
|
4403
5327
|
reorderBlocks,
|
|
4404
|
-
setSubject
|
|
4405
|
-
reset
|
|
5328
|
+
setSubject
|
|
4406
5329
|
};
|
|
4407
5330
|
}
|
|
4408
5331
|
|
|
@@ -4433,8 +5356,8 @@ function useEditorHandle(ref, { getDocument, definitions, variables }) {
|
|
|
4433
5356
|
}
|
|
4434
5357
|
|
|
4435
5358
|
// src/shell/EmailEditorShell.tsx
|
|
4436
|
-
import { jsx as
|
|
4437
|
-
var EmailEditorShell =
|
|
5359
|
+
import { jsx as jsx50, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
5360
|
+
var EmailEditorShell = forwardRef2(
|
|
4438
5361
|
function EmailEditorShell2({
|
|
4439
5362
|
initialDocument,
|
|
4440
5363
|
global,
|
|
@@ -4454,12 +5377,14 @@ var EmailEditorShell = forwardRef(
|
|
|
4454
5377
|
onGlobalChange
|
|
4455
5378
|
});
|
|
4456
5379
|
useEditorHandle(ref, session);
|
|
4457
|
-
const [viewMode, setViewMode] =
|
|
4458
|
-
const [device, setDevice] =
|
|
4459
|
-
const [inspectorPanel, setInspectorPanel] =
|
|
4460
|
-
|
|
5380
|
+
const [viewMode, setViewMode] = useState15("edit");
|
|
5381
|
+
const [device, setDevice] = useState15("desktop");
|
|
5382
|
+
const [inspectorPanel, setInspectorPanel] = useState15("global");
|
|
5383
|
+
const [previousSelectedId, setPreviousSelectedId] = useState15(session.selectedId);
|
|
5384
|
+
if (previousSelectedId !== session.selectedId) {
|
|
5385
|
+
setPreviousSelectedId(session.selectedId);
|
|
4461
5386
|
if (!session.selectedId) setInspectorPanel("global");
|
|
4462
|
-
}
|
|
5387
|
+
}
|
|
4463
5388
|
const selectBlock = (id) => {
|
|
4464
5389
|
const hadSelection = session.selectedId !== null;
|
|
4465
5390
|
session.selectBlock(id);
|
|
@@ -4479,18 +5404,17 @@ var EmailEditorShell = forwardRef(
|
|
|
4479
5404
|
selectBlock(null);
|
|
4480
5405
|
};
|
|
4481
5406
|
const showCanvas = () => setViewMode("edit");
|
|
4482
|
-
return /* @__PURE__ */
|
|
4483
|
-
/* @__PURE__ */
|
|
5407
|
+
return /* @__PURE__ */ jsxs36("div", { className: "ree-root", style: { height }, onKeyDown: handleKeyDown, children: [
|
|
5408
|
+
/* @__PURE__ */ jsx50(
|
|
4484
5409
|
SubjectBar,
|
|
4485
5410
|
{
|
|
4486
5411
|
subject: (_a = session.document.subject) != null ? _a : "",
|
|
4487
5412
|
variables: session.variables,
|
|
4488
|
-
onSubjectChange: session.setSubject
|
|
4489
|
-
onReset: session.reset
|
|
5413
|
+
onSubjectChange: session.setSubject
|
|
4490
5414
|
}
|
|
4491
5415
|
),
|
|
4492
|
-
/* @__PURE__ */
|
|
4493
|
-
/* @__PURE__ */
|
|
5416
|
+
/* @__PURE__ */ jsxs36("div", { className: "ree-body", children: [
|
|
5417
|
+
/* @__PURE__ */ jsxs36(
|
|
4494
5418
|
EditorDnd,
|
|
4495
5419
|
{
|
|
4496
5420
|
blocks: session.document.blocks,
|
|
@@ -4502,7 +5426,7 @@ var EmailEditorShell = forwardRef(
|
|
|
4502
5426
|
onReorder: session.reorderBlocks,
|
|
4503
5427
|
onLibraryDragStart: showCanvas,
|
|
4504
5428
|
children: [
|
|
4505
|
-
/* @__PURE__ */
|
|
5429
|
+
/* @__PURE__ */ jsx50(
|
|
4506
5430
|
BlockLibrary,
|
|
4507
5431
|
{
|
|
4508
5432
|
definitions: session.definitions,
|
|
@@ -4512,7 +5436,7 @@ var EmailEditorShell = forwardRef(
|
|
|
4512
5436
|
}
|
|
4513
5437
|
}
|
|
4514
5438
|
),
|
|
4515
|
-
/* @__PURE__ */
|
|
5439
|
+
/* @__PURE__ */ jsx50(
|
|
4516
5440
|
Stage,
|
|
4517
5441
|
{
|
|
4518
5442
|
session,
|
|
@@ -4526,7 +5450,7 @@ var EmailEditorShell = forwardRef(
|
|
|
4526
5450
|
]
|
|
4527
5451
|
}
|
|
4528
5452
|
),
|
|
4529
|
-
/* @__PURE__ */
|
|
5453
|
+
/* @__PURE__ */ jsx50(
|
|
4530
5454
|
Inspector,
|
|
4531
5455
|
{
|
|
4532
5456
|
panel: inspectorPanel,
|
|
@@ -4546,10 +5470,10 @@ var EmailEditorShell = forwardRef(
|
|
|
4546
5470
|
);
|
|
4547
5471
|
|
|
4548
5472
|
// src/EmailEditor.tsx
|
|
4549
|
-
import { jsx as
|
|
4550
|
-
var EmailEditor =
|
|
5473
|
+
import { jsx as jsx51 } from "react/jsx-runtime";
|
|
5474
|
+
var EmailEditor = forwardRef3(
|
|
4551
5475
|
function EmailEditor2(props, ref) {
|
|
4552
|
-
return /* @__PURE__ */
|
|
5476
|
+
return /* @__PURE__ */ jsx51(EmailEditorShell, __spreadProps(__spreadValues({}, props), { ref }));
|
|
4553
5477
|
}
|
|
4554
5478
|
);
|
|
4555
5479
|
export {
|