@fonderie/react-courier-admin-screens 0.8.0 → 0.10.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/dist/index.cjs +281 -83
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -5
- package/dist/index.d.ts +19 -5
- package/dist/index.js +287 -86
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -41,11 +41,13 @@ function TemplateEditorScreen({
|
|
|
41
41
|
system = false,
|
|
42
42
|
onDeleted,
|
|
43
43
|
onAddLocale,
|
|
44
|
+
onSelectLocale,
|
|
44
45
|
uiLocale
|
|
45
46
|
}) {
|
|
46
47
|
const t = (0, import_react.useMemo)(() => (0, import_client.createAdminT)(uiLocale), [uiLocale]);
|
|
47
48
|
const { template, isLoading, error, refresh } = (0, import_react_courier_admin.useTemplate)(client, type, locale);
|
|
48
|
-
const { saveTemplate, removeTemplate } = (0, import_react_courier_admin.useTemplates)(client);
|
|
49
|
+
const { templates, saveTemplate, removeTemplate } = (0, import_react_courier_admin.useTemplates)(client);
|
|
50
|
+
const siblings = (0, import_client.groupTemplatesByType)(templates).find((g) => g.type === type)?.entries ?? [];
|
|
49
51
|
const [isDeleting, setIsDeleting] = (0, import_react.useState)(false);
|
|
50
52
|
const { revisions, rollback } = (0, import_react_courier_admin.useTemplateRevisions)(client, type, locale);
|
|
51
53
|
const [isSaving, setIsSaving] = (0, import_react.useState)(false);
|
|
@@ -116,6 +118,10 @@ function TemplateEditorScreen({
|
|
|
116
118
|
setIsSaving(false);
|
|
117
119
|
}
|
|
118
120
|
};
|
|
121
|
+
const switchTo = (entry) => {
|
|
122
|
+
if (dirty && !window.confirm(t("templates.editor.discardChanges"))) return;
|
|
123
|
+
onSelectLocale?.(entry);
|
|
124
|
+
};
|
|
119
125
|
const handleDelete = async () => {
|
|
120
126
|
const message = locale ? t("templates.editor.confirmDeleteLocale", { type, locale }) : t("templates.editor.confirmDelete", { type });
|
|
121
127
|
if (!window.confirm(message)) return;
|
|
@@ -134,18 +140,40 @@ function TemplateEditorScreen({
|
|
|
134
140
|
if (error)
|
|
135
141
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.error, role: "alert", children: error.explanation });
|
|
136
142
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.container, children: [
|
|
137
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
138
|
-
|
|
139
|
-
|
|
143
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { style: styles.title, children: type }),
|
|
144
|
+
onSelectLocale || onAddLocale ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.tabBar, children: [
|
|
145
|
+
onSelectLocale ? (
|
|
146
|
+
// Each tab opens another stored row, so this is navigation, not an
|
|
147
|
+
// ARIA tablist: plain buttons, the open one marked aria-current.
|
|
148
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("nav", { "aria-label": t("templates.editor.locales"), style: styles.tabs, children: siblings.map((entry) => {
|
|
149
|
+
const selected = entry.locale === (locale ?? null);
|
|
150
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
151
|
+
"button",
|
|
152
|
+
{
|
|
153
|
+
type: "button",
|
|
154
|
+
"aria-current": selected ? "true" : void 0,
|
|
155
|
+
onClick: () => selected ? void 0 : switchTo(entry),
|
|
156
|
+
style: selected ? styles.tabSelected : entry.active ? styles.tab : styles.tabInactive,
|
|
157
|
+
title: entry.active ? void 0 : t("common.status.inactive"),
|
|
158
|
+
children: entry.locale ?? t("templates.defaultChip")
|
|
159
|
+
},
|
|
160
|
+
entry.locale ?? ""
|
|
161
|
+
);
|
|
162
|
+
}) })
|
|
163
|
+
) : null,
|
|
164
|
+
onAddLocale ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
140
165
|
"button",
|
|
141
166
|
{
|
|
142
167
|
type: "button",
|
|
143
168
|
style: { ...styles.rollbackButton, marginLeft: "auto" },
|
|
144
|
-
onClick: () =>
|
|
169
|
+
onClick: () => {
|
|
170
|
+
if (dirty && !window.confirm(t("templates.editor.discardChanges"))) return;
|
|
171
|
+
onAddLocale(type, { locales: (0, import_client.missingTemplateLocales)(templates, type) });
|
|
172
|
+
},
|
|
145
173
|
children: t("templates.editor.addLocale")
|
|
146
174
|
}
|
|
147
175
|
) : null
|
|
148
|
-
] }),
|
|
176
|
+
] }) : null,
|
|
149
177
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.meta, children: [
|
|
150
178
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: locale ?? t("templates.defaultLocale") }),
|
|
151
179
|
" \xB7 v",
|
|
@@ -306,8 +334,38 @@ function TemplateEditorScreen({
|
|
|
306
334
|
] })
|
|
307
335
|
] });
|
|
308
336
|
}
|
|
337
|
+
var tab = {
|
|
338
|
+
height: 32,
|
|
339
|
+
padding: "0 10px",
|
|
340
|
+
background: "none",
|
|
341
|
+
border: "none",
|
|
342
|
+
borderBottom: "2px solid transparent",
|
|
343
|
+
color: "var(--fonderie-text-muted,#5c5c5c)",
|
|
344
|
+
fontSize: 13,
|
|
345
|
+
fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)',
|
|
346
|
+
cursor: "pointer"
|
|
347
|
+
};
|
|
309
348
|
var styles = {
|
|
310
349
|
container: { padding: "8px 40px 64px", maxWidth: 1400, boxSizing: "border-box" },
|
|
350
|
+
// One tab per locale of this email, underlined like a document's tabs.
|
|
351
|
+
tabBar: {
|
|
352
|
+
display: "flex",
|
|
353
|
+
alignItems: "flex-end",
|
|
354
|
+
gap: 12,
|
|
355
|
+
borderBottom: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
356
|
+
margin: "4px 0 12px",
|
|
357
|
+
paddingBottom: 6
|
|
358
|
+
},
|
|
359
|
+
tabs: { display: "flex", flexWrap: "wrap", gap: 4, marginBottom: -7 },
|
|
360
|
+
tab,
|
|
361
|
+
tabSelected: {
|
|
362
|
+
...tab,
|
|
363
|
+
color: "var(--fonderie-text,#171717)",
|
|
364
|
+
fontWeight: 600,
|
|
365
|
+
borderBottomColor: "var(--fonderie-text,#171717)",
|
|
366
|
+
cursor: "default"
|
|
367
|
+
},
|
|
368
|
+
tabInactive: { ...tab, opacity: 0.55, textDecoration: "line-through" },
|
|
311
369
|
// Two columns, always: the editor on the left, the rendered result on the
|
|
312
370
|
// right. Stacking the preview under a long form hid it below the fold.
|
|
313
371
|
split: {
|
|
@@ -486,6 +544,7 @@ function TemplateListScreen({
|
|
|
486
544
|
}) {
|
|
487
545
|
const t = (0, import_client2.createAdminT)(locale);
|
|
488
546
|
const { templates, isLoading, error } = (0, import_react_courier_admin2.useTemplates)(client);
|
|
547
|
+
const groups = (0, import_client2.groupTemplatesByType)(templates);
|
|
489
548
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles2.container, children: [
|
|
490
549
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
491
550
|
"div",
|
|
@@ -510,23 +569,39 @@ function TemplateListScreen({
|
|
|
510
569
|
}
|
|
511
570
|
),
|
|
512
571
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles2.hint, children: t("templates.list.hint") }),
|
|
513
|
-
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles2.status, children: t("templates.list.loading") }) : error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles2.error, role: "alert", children: error.explanation }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { style: styles2.list, children:
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
572
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles2.status, children: t("templates.list.loading") }) : error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles2.error, role: "alert", children: error.explanation }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { style: styles2.list, children: groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { style: styles2.row, children: [
|
|
573
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
574
|
+
"button",
|
|
575
|
+
{
|
|
576
|
+
type: "button",
|
|
577
|
+
onClick: () => onSelectTemplate?.(group.primary),
|
|
578
|
+
style: styles2.rowButton,
|
|
579
|
+
children: [
|
|
580
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: styles2.type, children: group.type }),
|
|
581
|
+
group.system ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: styles2.builtIn, children: t("templates.builtInBadge") }) : null,
|
|
582
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: group.primary.active ? styles2.active : styles2.inactive, children: [
|
|
583
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: styles2.dot }),
|
|
584
|
+
group.primary.active ? t("common.status.active") : t("common.status.inactive")
|
|
585
|
+
] })
|
|
586
|
+
]
|
|
587
|
+
}
|
|
588
|
+
),
|
|
589
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: styles2.chips, children: group.entries.map((entry) => {
|
|
590
|
+
const name = entry.locale ?? t("templates.defaultChip");
|
|
591
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
592
|
+
"button",
|
|
593
|
+
{
|
|
594
|
+
type: "button",
|
|
595
|
+
onClick: () => onSelectTemplate?.(entry),
|
|
596
|
+
style: entry.active ? styles2.chip : styles2.chipInactive,
|
|
597
|
+
"aria-label": t("templates.list.openLocale", { type: group.type, locale: name }),
|
|
598
|
+
title: entry.active ? void 0 : t("common.status.inactive"),
|
|
599
|
+
children: name
|
|
600
|
+
},
|
|
601
|
+
entry.locale ?? ""
|
|
602
|
+
);
|
|
603
|
+
}) })
|
|
604
|
+
] }, group.type)) })
|
|
530
605
|
] });
|
|
531
606
|
}
|
|
532
607
|
var pill = {
|
|
@@ -540,6 +615,18 @@ var pill = {
|
|
|
540
615
|
lineHeight: 1.5,
|
|
541
616
|
minWidth: 64
|
|
542
617
|
};
|
|
618
|
+
var chip = {
|
|
619
|
+
height: 24,
|
|
620
|
+
boxSizing: "border-box",
|
|
621
|
+
padding: "0 8px",
|
|
622
|
+
borderRadius: 6,
|
|
623
|
+
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
624
|
+
background: "var(--fonderie-surface,#fff)",
|
|
625
|
+
color: "var(--fonderie-text,#171717)",
|
|
626
|
+
fontSize: 12,
|
|
627
|
+
fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)',
|
|
628
|
+
cursor: "pointer"
|
|
629
|
+
};
|
|
543
630
|
var styles2 = {
|
|
544
631
|
container: { padding: "32px 40px 64px", maxWidth: 1160, boxSizing: "border-box" },
|
|
545
632
|
title: {
|
|
@@ -562,9 +649,16 @@ var styles2 = {
|
|
|
562
649
|
overflow: "hidden",
|
|
563
650
|
boxShadow: "var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))"
|
|
564
651
|
},
|
|
565
|
-
row: {
|
|
652
|
+
row: {
|
|
653
|
+
display: "flex",
|
|
654
|
+
alignItems: "center",
|
|
655
|
+
gap: 8,
|
|
656
|
+
paddingRight: 16,
|
|
657
|
+
borderBottom: "1px solid var(--fonderie-border-light,#f5f5f5)"
|
|
658
|
+
},
|
|
566
659
|
rowButton: {
|
|
567
|
-
|
|
660
|
+
flex: 1,
|
|
661
|
+
minWidth: 0,
|
|
568
662
|
display: "flex",
|
|
569
663
|
justifyContent: "space-between",
|
|
570
664
|
alignItems: "center",
|
|
@@ -583,7 +677,9 @@ var styles2 = {
|
|
|
583
677
|
flex: 1,
|
|
584
678
|
fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)'
|
|
585
679
|
},
|
|
586
|
-
|
|
680
|
+
chips: { display: "flex", flexWrap: "wrap", justifyContent: "flex-end", gap: 6 },
|
|
681
|
+
chip,
|
|
682
|
+
chipInactive: { ...chip, opacity: 0.5, textDecoration: "line-through" },
|
|
587
683
|
dot: { width: 6, height: 6, borderRadius: 999, background: "currentColor" },
|
|
588
684
|
builtIn: {
|
|
589
685
|
fontSize: 11,
|
|
@@ -622,10 +718,13 @@ var styles2 = {
|
|
|
622
718
|
|
|
623
719
|
// src/screens/TemplateCreateScreen.tsx
|
|
624
720
|
var import_client3 = require("@fonderie/client");
|
|
721
|
+
var import_react_courier_admin3 = require("@fonderie/react-courier-admin");
|
|
625
722
|
var import_react2 = require("react");
|
|
626
723
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
627
724
|
var TYPE_RE = /^[a-z0-9][a-z0-9_-]{0,80}$/;
|
|
628
725
|
var LOCALE_RE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;
|
|
726
|
+
var IMPLICIT2 = /* @__PURE__ */ new Set(["subject", "preheader", "brandName"]);
|
|
727
|
+
var PREVIEW_DEBOUNCE_MS2 = 500;
|
|
629
728
|
function TemplateCreateScreen({
|
|
630
729
|
client,
|
|
631
730
|
type,
|
|
@@ -642,14 +741,48 @@ function TemplateCreateScreen({
|
|
|
642
741
|
const [text, setText] = (0, import_react2.useState)("");
|
|
643
742
|
const [error, setError] = (0, import_react2.useState)(null);
|
|
644
743
|
const [busy, setBusy] = (0, import_react2.useState)(false);
|
|
744
|
+
const [source, setSource] = (0, import_react2.useState)(
|
|
745
|
+
null
|
|
746
|
+
);
|
|
747
|
+
const { preview, isPreviewing, error: previewError, renderPreview } = (0, import_react_courier_admin3.useTemplatePreview)(client);
|
|
748
|
+
const variables = (0, import_react2.useRef)([]);
|
|
645
749
|
(0, import_react2.useEffect)(() => {
|
|
646
750
|
if (!type) return;
|
|
647
751
|
client.getTemplate(type, null).then(({ result }) => {
|
|
648
752
|
setSubject(result.subject ?? "");
|
|
649
753
|
setHtml(result.html ?? "");
|
|
650
754
|
setText(result.text);
|
|
755
|
+
setSource({ subject: result.subject ?? "", html: result.html ?? "", text: result.text });
|
|
651
756
|
}).catch(() => void 0);
|
|
652
757
|
}, [client, type]);
|
|
758
|
+
const previewType = addingLocale ? type : newType.trim();
|
|
759
|
+
const previewLocale = LOCALE_RE.test(targetLocale.trim()) ? targetLocale.trim() : null;
|
|
760
|
+
(0, import_react2.useEffect)(() => {
|
|
761
|
+
if (!TYPE_RE.test(previewType) || !text.trim()) return;
|
|
762
|
+
const timer = setTimeout(async () => {
|
|
763
|
+
const input = { text, ...subject ? { subject } : {}, ...html ? { html } : {} };
|
|
764
|
+
const render = () => renderPreview(
|
|
765
|
+
previewType,
|
|
766
|
+
{ ...input, data: Object.fromEntries(variables.current.map((v) => [v, v])) },
|
|
767
|
+
previewLocale
|
|
768
|
+
);
|
|
769
|
+
try {
|
|
770
|
+
const result = await render();
|
|
771
|
+
const used = result.variables.filter((v) => !IMPLICIT2.has(v));
|
|
772
|
+
if (used.some((v) => !variables.current.includes(v))) {
|
|
773
|
+
variables.current = used;
|
|
774
|
+
await render();
|
|
775
|
+
}
|
|
776
|
+
} catch {
|
|
777
|
+
}
|
|
778
|
+
}, PREVIEW_DEBOUNCE_MS2);
|
|
779
|
+
return () => clearTimeout(timer);
|
|
780
|
+
}, [renderPreview, previewType, previewLocale, subject, html, text]);
|
|
781
|
+
const untranslated = source ? [
|
|
782
|
+
subject && subject === source.subject ? tr("templates.subject") : null,
|
|
783
|
+
html && html === source.html ? tr("templates.htmlBody") : null,
|
|
784
|
+
text === source.text ? tr("templates.textBody") : null
|
|
785
|
+
].filter((f) => f !== null) : [];
|
|
653
786
|
const submit = async (e) => {
|
|
654
787
|
e.preventDefault();
|
|
655
788
|
setError(null);
|
|
@@ -685,75 +818,140 @@ function TemplateCreateScreen({
|
|
|
685
818
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles3.container, children: [
|
|
686
819
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { style: styles3.title, children: addingLocale ? tr("templates.create.titleLocale", { type }) : tr("templates.create.titleNew") }),
|
|
687
820
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.meta, children: addingLocale ? tr("templates.create.hintLocale") : tr("templates.create.hintNew") }),
|
|
688
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("
|
|
689
|
-
|
|
690
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.
|
|
821
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles3.split, children: [
|
|
822
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { style: styles3.form, onSubmit: (e) => void submit(e), children: [
|
|
823
|
+
!addingLocale ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
824
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-type", children: tr("templates.create.type") }),
|
|
825
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
826
|
+
"input",
|
|
827
|
+
{
|
|
828
|
+
id: "new-template-type",
|
|
829
|
+
style: styles3.input,
|
|
830
|
+
value: newType,
|
|
831
|
+
placeholder: "weekly-digest",
|
|
832
|
+
autoComplete: "off",
|
|
833
|
+
spellCheck: false,
|
|
834
|
+
onChange: (e) => setNewType(e.target.value)
|
|
835
|
+
}
|
|
836
|
+
)
|
|
837
|
+
] }) : null,
|
|
838
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-locale", children: addingLocale ? tr("templates.create.locale") : tr("templates.create.localeOptional") }),
|
|
691
839
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
692
840
|
"input",
|
|
693
841
|
{
|
|
694
|
-
id: "new-template-
|
|
842
|
+
id: "new-template-locale",
|
|
695
843
|
style: styles3.input,
|
|
696
|
-
value:
|
|
697
|
-
placeholder: "
|
|
844
|
+
value: targetLocale,
|
|
845
|
+
placeholder: "fr",
|
|
846
|
+
list: "new-template-locales",
|
|
698
847
|
autoComplete: "off",
|
|
699
848
|
spellCheck: false,
|
|
700
|
-
onChange: (e) =>
|
|
849
|
+
onChange: (e) => setTargetLocale(e.target.value)
|
|
701
850
|
}
|
|
702
|
-
)
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
),
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
851
|
+
),
|
|
852
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("datalist", { id: "new-template-locales", children: [...new Set(locales)].map((l) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: l }, l)) }),
|
|
853
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-subject", children: tr("templates.subject") }),
|
|
854
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
855
|
+
"input",
|
|
856
|
+
{
|
|
857
|
+
id: "new-template-subject",
|
|
858
|
+
style: styles3.input,
|
|
859
|
+
value: subject,
|
|
860
|
+
onChange: (e) => setSubject(e.target.value)
|
|
861
|
+
}
|
|
862
|
+
),
|
|
863
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-html", children: tr("templates.htmlBody") }),
|
|
864
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
865
|
+
"textarea",
|
|
866
|
+
{
|
|
867
|
+
id: "new-template-html",
|
|
868
|
+
style: { ...styles3.textarea, minHeight: 160 },
|
|
869
|
+
value: html,
|
|
870
|
+
onChange: (e) => setHtml(e.target.value)
|
|
871
|
+
}
|
|
872
|
+
),
|
|
873
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-text", children: tr("templates.textBody") }),
|
|
874
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
875
|
+
"textarea",
|
|
876
|
+
{
|
|
877
|
+
id: "new-template-text",
|
|
878
|
+
style: { ...styles3.textarea, minHeight: 100 },
|
|
879
|
+
value: text,
|
|
880
|
+
onChange: (e) => setText(e.target.value)
|
|
881
|
+
}
|
|
882
|
+
),
|
|
883
|
+
untranslated.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.notice, children: tr("templates.create.untranslated", { fields: untranslated.join(", ") }) }) : null,
|
|
884
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.error, role: "alert", children: error }) : null,
|
|
885
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "submit", disabled: busy, style: styles3.button, children: busy ? tr("templates.create.creating") : addingLocale ? tr("templates.create.submitLocale") : tr("templates.create.submitNew") })
|
|
886
|
+
] }),
|
|
887
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles3.previewColumn, children: [
|
|
888
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles3.previewHeader, children: [
|
|
889
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: styles3.label, children: tr("templates.editor.preview") }),
|
|
890
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: styles3.meta, children: isPreviewing ? tr("templates.editor.rendering") : tr("templates.editor.live") })
|
|
891
|
+
] }),
|
|
892
|
+
previewError ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.error, role: "alert", children: previewError.explanation }) : null,
|
|
893
|
+
preview?.subject ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.previewSubject, children: preview.subject }) : null,
|
|
894
|
+
preview?.html ? (
|
|
895
|
+
// sandbox="" — no scripts, no same-origin: operator-authored HTML must
|
|
896
|
+
// never execute in the console's origin.
|
|
897
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
898
|
+
"iframe",
|
|
899
|
+
{
|
|
900
|
+
title: tr("templates.editor.previewTitle"),
|
|
901
|
+
style: styles3.previewFrame,
|
|
902
|
+
sandbox: "",
|
|
903
|
+
srcDoc: preview.html
|
|
904
|
+
}
|
|
905
|
+
)
|
|
906
|
+
) : preview ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("pre", { style: styles3.previewText, children: preview.text }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.meta, children: tr("templates.editor.nothingRendered") })
|
|
907
|
+
] })
|
|
751
908
|
] })
|
|
752
909
|
] });
|
|
753
910
|
}
|
|
754
911
|
var MONO = 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)';
|
|
755
912
|
var styles3 = {
|
|
756
|
-
container: { padding: "8px 40px 64px", maxWidth:
|
|
913
|
+
container: { padding: "8px 40px 64px", maxWidth: 1400, boxSizing: "border-box" },
|
|
914
|
+
// The editor's layout: the form on the left, the render pinned on the right.
|
|
915
|
+
split: {
|
|
916
|
+
display: "grid",
|
|
917
|
+
gridTemplateColumns: "minmax(0, 1fr) minmax(0, 1fr)",
|
|
918
|
+
gap: 24,
|
|
919
|
+
alignItems: "start"
|
|
920
|
+
},
|
|
921
|
+
previewColumn: {
|
|
922
|
+
minWidth: 0,
|
|
923
|
+
display: "flex",
|
|
924
|
+
flexDirection: "column",
|
|
925
|
+
position: "sticky",
|
|
926
|
+
top: 16
|
|
927
|
+
},
|
|
928
|
+
previewHeader: { display: "flex", justifyContent: "space-between", alignItems: "center" },
|
|
929
|
+
previewSubject: { fontSize: 14, fontWeight: 600, margin: "8px 0" },
|
|
930
|
+
previewFrame: {
|
|
931
|
+
boxShadow: "var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))",
|
|
932
|
+
width: "100%",
|
|
933
|
+
height: 520,
|
|
934
|
+
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
935
|
+
borderRadius: 8,
|
|
936
|
+
background: "var(--fonderie-surface,#fff)"
|
|
937
|
+
},
|
|
938
|
+
previewText: {
|
|
939
|
+
whiteSpace: "pre-wrap",
|
|
940
|
+
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
941
|
+
borderRadius: 8,
|
|
942
|
+
padding: 12,
|
|
943
|
+
fontSize: 13,
|
|
944
|
+
fontFamily: MONO
|
|
945
|
+
},
|
|
946
|
+
// The console's warning token (the shell's amber), readable in both themes.
|
|
947
|
+
notice: {
|
|
948
|
+
fontSize: 13,
|
|
949
|
+
color: "var(--fonderie-text,#171717)",
|
|
950
|
+
background: "color-mix(in srgb, var(--fonderie-warning,#f5a623) 16%, transparent)",
|
|
951
|
+
borderRadius: 6,
|
|
952
|
+
padding: "8px 12px",
|
|
953
|
+
margin: "12px 0 0"
|
|
954
|
+
},
|
|
757
955
|
title: {
|
|
758
956
|
fontSize: 22,
|
|
759
957
|
fontWeight: 600,
|