@templatical/editor 0.0.3 → 0.0.5
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/{AiChatSidebar-busJk9hm.js → AiChatSidebar-DwME3f-a.js} +84 -70
- package/dist/{AiFeatureMenu-DLGv_-pj.js → AiFeatureMenu-DJvWL1GZ.js} +23 -23
- package/dist/CloudEditor-Fe0ssRgi.js +1082 -0
- package/dist/{CollaboratorBar-Dv3l52vC.js → CollaboratorBar-DTT0EkZn.js} +25 -21
- package/dist/{CommentsSidebar-4tjp0VU5.js → CommentsSidebar-DrJhQRXK.js} +131 -131
- package/dist/{DesignReferenceSidebar-CmwXvltV.js → DesignReferenceSidebar-DdOht5zn.js} +49 -49
- package/dist/ModuleBrowserModal-CiV_jOEM.js +205 -0
- package/dist/{ModulePreviewCanvas-BcBJLnwL.js → ModulePreviewCanvas-Bmy6Y1WE.js} +2 -2
- package/dist/ParagraphEditor-CoQ3NlS7.js +688 -0
- package/dist/{RichTextEditorContent-CQqodi7p.js → RichTextEditorContent-CHJlh7HJ.js} +8 -4
- package/dist/{SaveModuleDialog-Bmzi72td.js → SaveModuleDialog-CD2ZYq1o.js} +25 -25
- package/dist/{SnapshotHistory-AEgi9Xsn.js → SnapshotHistory-DltsKvhP.js} +2 -2
- package/dist/TemplateScoringPanel-DmnmUE3y.js +254 -0
- package/dist/{TestEmailModal-Dpq1is9S.js → TestEmailModal-Dl633j9o.js} +3 -3
- package/dist/{TitleEditor-CLcDdcWI.js → TitleEditor-C7fds2Nc.js} +7 -7
- package/dist/{TplModal-CGzRjR96.js → TplModal-C5_CF-qn.js} +2 -2
- package/dist/{blockTypeIcons-BpPTqcok.js → blockTypeIcons-BrKZB10B.js} +1 -1
- package/dist/cdn/chunks/AiChatSidebar-X_Bv3Qys.js +2 -0
- package/dist/cdn/chunks/AiFeatureMenu-C5UQmEgV.js +59 -0
- package/dist/cdn/chunks/AiFeatureMenu-C5UQmEgV.js.map +1 -0
- package/dist/cdn/chunks/CloudEditor-DeTolKnf.js +1056 -0
- package/dist/cdn/chunks/CloudEditor-DeTolKnf.js.map +1 -0
- package/dist/cdn/chunks/CollaboratorBar-DO1nxSrr.js +51 -0
- package/dist/cdn/chunks/CollaboratorBar-DO1nxSrr.js.map +1 -0
- package/dist/cdn/chunks/CommentsSidebar-4MTw_hue.js +2 -0
- package/dist/cdn/chunks/DesignReferenceSidebar-Bswh4Yx4.js +2 -0
- package/dist/{ModuleBrowserModal-lrk3Fr0H.js → cdn/chunks/ModuleBrowserModal-ChBr3aXj.js} +52 -62
- package/dist/cdn/chunks/ModuleBrowserModal-ChBr3aXj.js.map +1 -0
- package/dist/cdn/chunks/ModulePreviewCanvas-DkSvri9H.js +107 -0
- package/dist/cdn/chunks/ModulePreviewCanvas-DkSvri9H.js.map +1 -0
- package/dist/cdn/chunks/ParagraphEditor-DU3oUKA7.js +539 -0
- package/dist/cdn/chunks/ParagraphEditor-DU3oUKA7.js.map +1 -0
- package/dist/cdn/chunks/RichTextEditorContent-BrsW1p9s.js +106 -0
- package/dist/cdn/chunks/RichTextEditorContent-BrsW1p9s.js.map +1 -0
- package/dist/cdn/chunks/SaveModuleDialog-CjqKkTEc.js +119 -0
- package/dist/cdn/chunks/SaveModuleDialog-CjqKkTEc.js.map +1 -0
- package/dist/cdn/chunks/SnapshotHistory-KME4xmn_.js +2 -0
- package/dist/cdn/chunks/TemplateScoringPanel-DgB3xDN6.js +2 -0
- package/dist/cdn/chunks/TestEmailModal-DdpvRbYf.js +2 -0
- package/dist/cdn/chunks/TitleEditor-C8FYbadT.js +166 -0
- package/dist/cdn/chunks/TitleEditor-C8FYbadT.js.map +1 -0
- package/dist/cdn/chunks/_rolldown_dynamic_import_helper-DMEI4TQ3.js +9 -0
- package/dist/cdn/chunks/blockTypeIcons-5QwYklNq.js +22 -0
- package/dist/cdn/chunks/blockTypeIcons-5QwYklNq.js.map +1 -0
- package/dist/cdn/chunks/de-BB3dgVOc.js +700 -0
- package/dist/cdn/chunks/de-BB3dgVOc.js.map +1 -0
- package/dist/cdn/chunks/de-BvYD17KT.js +89 -0
- package/dist/cdn/chunks/de-BvYD17KT.js.map +1 -0
- package/dist/cdn/chunks/dist-BF5c3Dr-.js +2 -0
- package/dist/cdn/chunks/dist-BGzvIxcJ.js +2 -0
- package/dist/cdn/chunks/dist-CFemF8rI.js +2 -0
- package/dist/cdn/chunks/dist-Co6uFhFK.js +2 -0
- package/dist/cdn/chunks/dist-DCikBY9K.js +2 -0
- package/dist/cdn/chunks/dist-DUILafAC.js +2 -0
- package/dist/cdn/chunks/dist-DghiKH0A.js +2 -0
- package/dist/cdn/chunks/dist-Dw8ckvfK.js +2 -0
- package/dist/cdn/chunks/dist-H07p0KAw.js +2 -0
- package/dist/cdn/chunks/dist-KYv9v_1z2.js +457 -0
- package/dist/cdn/chunks/dist-KYv9v_1z2.js.map +1 -0
- package/dist/cdn/chunks/dist-MjnKIc0W.js +2 -0
- package/dist/cdn/chunks/dist-odp0vGRv.js +2 -0
- package/dist/cdn/chunks/draggable-BQNU47zu.js +11544 -0
- package/dist/cdn/chunks/{draggable-ClUwYCFL.js.map → draggable-BQNU47zu.js.map} +1 -1
- package/dist/cdn/chunks/emojiData-BVEJHcNH.js +19 -0
- package/dist/cdn/chunks/{emojiData-6fVLNqeH.js.map → emojiData-BVEJHcNH.js.map} +1 -1
- package/dist/cdn/chunks/en-CpotcOPr.js +89 -0
- package/dist/cdn/chunks/en-CpotcOPr.js.map +1 -0
- package/dist/cdn/chunks/en-DeDcpnoS.js +700 -0
- package/dist/cdn/chunks/en-DeDcpnoS.js.map +1 -0
- package/dist/cdn/chunks/extensions-Bj7USRLr.js +419 -0
- package/dist/cdn/chunks/{extensions-ea_ewKUl.js.map → extensions-Bj7USRLr.js.map} +1 -1
- package/dist/cdn/chunks/features-Ds0XUfte.js +6737 -0
- package/dist/cdn/chunks/features-Ds0XUfte.js.map +1 -0
- package/dist/cdn/chunks/icons-fWsuSvgd.js +653 -0
- package/dist/cdn/chunks/icons-fWsuSvgd.js.map +1 -0
- package/dist/cdn/chunks/liquid.browser-C1VIYISn.js +3272 -0
- package/dist/cdn/chunks/liquid.browser-C1VIYISn.js.map +1 -0
- package/dist/cdn/chunks/media-library-BGQm_OyC.js +6005 -0
- package/dist/cdn/chunks/media-library-BGQm_OyC.js.map +1 -0
- package/dist/cdn/chunks/pusher-DJPhQnE8.js +2505 -0
- package/dist/cdn/chunks/pusher-DJPhQnE8.js.map +1 -0
- package/dist/cdn/chunks/readableTextColor-Cd_cgWO_.js +32 -0
- package/dist/cdn/chunks/readableTextColor-Cd_cgWO_.js.map +1 -0
- package/dist/cdn/chunks/rolldown-runtime-DPITmOBR.js +20 -0
- package/dist/cdn/chunks/src-3i8rPuqd.js +494 -0
- package/dist/cdn/chunks/src-3i8rPuqd.js.map +1 -0
- package/dist/cdn/chunks/styleConstants-DFe3I4Op.js +57 -0
- package/dist/cdn/chunks/{styleConstants-CNejCb-L.js.map → styleConstants-DFe3I4Op.js.map} +1 -1
- package/dist/cdn/chunks/styles-Dgijy53u.js +3350 -0
- package/dist/cdn/chunks/styles-Dgijy53u.js.map +1 -0
- package/dist/cdn/chunks/tiptap-BhxaWR8R.js +14208 -0
- package/dist/cdn/chunks/{tiptap-Cya4P9CN.js.map → tiptap-BhxaWR8R.js.map} +1 -1
- package/dist/cdn/editor.css +2 -1
- package/dist/cdn/editor.js +231 -1
- package/dist/cdn/editor.js.map +1 -1
- package/dist/{de-B4Ob4vCo.js → de-D7TLGIPA.js} +20 -4
- package/dist/{dist-DmpMJbmZ.js → dist-Ci5lFuUy.js} +1 -1
- package/dist/{en-YXsspZJG.js → en-DvtiEMwP.js} +20 -4
- package/dist/{extensions-CKM99njP.js → extensions-DWx_jj8v.js} +3 -3
- package/dist/{keys-Dwa2PmdD.js → keys-C0MQRs8d.js} +3 -3
- package/dist/readableTextColor-LDlmVEUv.js +30 -0
- package/dist/{styleConstants-D4SOZGBV.js → styleConstants-Cxw88naD.js} +5 -5
- package/dist/styles-fdXNRqI3.js +3556 -0
- package/dist/templatical-editor.css +1 -1
- package/dist/templatical-editor.js +122 -128
- package/dist/templatical-editor.umd.cjs +62 -71
- package/dist/{useEditorCore-Cc4RCwWq.js → useEditorCore-DUGD6pq_.js} +1247 -1088
- package/dist/{useI18n-DUirdXEX.js → useI18n-D6m7ZUgY.js} +1 -1
- package/dist/{useMergeTag-DVnlvPYJ.js → useMergeTag-BZ3X0bNr.js} +1 -1
- package/package.json +4 -2
- package/dist/CloudEditor-BDnHd6Um.js +0 -919
- package/dist/ParagraphEditor-DH8cSC6m.js +0 -625
- package/dist/TemplateScoringPanel-CTgMtc0-.js +0 -249
- package/dist/cdn/chunks/ParagraphEditor-CCtWbGDv.js +0 -3
- package/dist/cdn/chunks/ParagraphEditor-CCtWbGDv.js.map +0 -1
- package/dist/cdn/chunks/RichTextEditorContent-BUD9veXd.js +0 -2
- package/dist/cdn/chunks/RichTextEditorContent-BUD9veXd.js.map +0 -1
- package/dist/cdn/chunks/TitleEditor-e_UTyxjd.js +0 -3
- package/dist/cdn/chunks/TitleEditor-e_UTyxjd.js.map +0 -1
- package/dist/cdn/chunks/dist-0UheN8rK.js +0 -1
- package/dist/cdn/chunks/dist-55mmbGQ9.js +0 -1
- package/dist/cdn/chunks/dist-B31mxKyP.js +0 -1
- package/dist/cdn/chunks/dist-B5JI9nIg.js +0 -1
- package/dist/cdn/chunks/dist-B93vLKhU.js +0 -1
- package/dist/cdn/chunks/dist-BDt3FJvj.js +0 -1
- package/dist/cdn/chunks/dist-BJRuFHmi.js +0 -1
- package/dist/cdn/chunks/dist-BKSzrf0L.js +0 -1
- package/dist/cdn/chunks/dist-BL8c5gYQ.js +0 -1
- package/dist/cdn/chunks/dist-CYThWMP5.js +0 -1
- package/dist/cdn/chunks/dist-DxZbPJYt.js +0 -1
- package/dist/cdn/chunks/draggable-ClUwYCFL.js +0 -17
- package/dist/cdn/chunks/emojiData-6fVLNqeH.js +0 -2
- package/dist/cdn/chunks/extensions-ea_ewKUl.js +0 -2
- package/dist/cdn/chunks/icons-vmLJTaJk.js +0 -2
- package/dist/cdn/chunks/icons-vmLJTaJk.js.map +0 -1
- package/dist/cdn/chunks/rolldown-runtime-BakkzWXw.js +0 -1
- package/dist/cdn/chunks/styleConstants-CNejCb-L.js +0 -2
- package/dist/cdn/chunks/tiptap-Cya4P9CN.js +0 -145
- package/dist/cdn/chunks/useEditorCore-CwuxQuvh.js +0 -2
- package/dist/cdn/chunks/useEditorCore-CwuxQuvh.js.map +0 -1
- package/dist/cdn/chunks/useMergeTag-DVOz1v9p.js +0 -2
- package/dist/cdn/chunks/useMergeTag-DVOz1v9p.js.map +0 -1
- package/dist/i18n-CJsFtdbZ.js +0 -23
- package/dist/styles-DSw1VNU3.js +0 -3406
|
@@ -1,21 +1,9 @@
|
|
|
1
|
-
import "./
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import { t as te } from "./x-CGlq2XQe.js";
|
|
8
|
-
import { t as ne } from "./TplModal-CGzRjR96.js";
|
|
9
|
-
import { Fragment as o, computed as s, createBlock as c, createCommentVNode as re, createElementBlock as l, createElementVNode as u, createVNode as d, defineAsyncComponent as ie, defineComponent as f, inject as p, normalizeStyle as ae, openBlock as m, ref as h, renderList as g, resolveDynamicComponent as oe, toDisplayString as _, unref as v, vModelSelect as se, vModelText as ce, watch as le, withCtx as ue, withDirectives as y, withModifiers as b } from "vue";
|
|
10
|
-
var de = r("search", [["path", {
|
|
11
|
-
d: "m21 21-4.34-4.34",
|
|
12
|
-
key: "14j7rj"
|
|
13
|
-
}], ["circle", {
|
|
14
|
-
cx: "11",
|
|
15
|
-
cy: "11",
|
|
16
|
-
r: "8",
|
|
17
|
-
key: "4ej97u"
|
|
18
|
-
}]]), fe = {
|
|
1
|
+
import { $ as e, B as t, F as n, H as r, N as i, R as a, S as o, U as s, _ as c, at as l, b as u, ct as ee, d, g as te, h as f, lt as p, m, o as ne, p as h, s as re, u as g, x as ie } from "./draggable-BQNU47zu.js";
|
|
2
|
+
import { Ht as ae, Kt as oe, Xt as _, i as se, jt as ce, rt as le, tt as ue } from "./features-Ds0XUfte.js";
|
|
3
|
+
import { A as v, y as de } from "./icons-fWsuSvgd.js";
|
|
4
|
+
import { t as y } from "./blockTypeIcons-5QwYklNq.js";
|
|
5
|
+
//#region src/cloud/components/ModuleBrowserModal.vue?vue&type=script&setup=true&lang.ts
|
|
6
|
+
var fe = {
|
|
19
7
|
role: "dialog",
|
|
20
8
|
"aria-modal": "true",
|
|
21
9
|
"aria-labelledby": "tpl-module-browser-title",
|
|
@@ -28,7 +16,7 @@ var de = r("search", [["path", {
|
|
|
28
16
|
}, pe = { class: "tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:px-5 tpl:py-4 tpl:border-[var(--tpl-border)]" }, me = {
|
|
29
17
|
id: "tpl-module-browser-title",
|
|
30
18
|
class: "tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]"
|
|
31
|
-
},
|
|
19
|
+
}, b = ["aria-label"], x = { class: "tpl:flex tpl:min-h-0 tpl:flex-1 tpl:overflow-hidden" }, S = { class: "tpl:flex tpl:w-[300px] tpl:shrink-0 tpl:flex-col tpl:overflow-hidden" }, C = { class: "tpl:px-4 tpl:pt-4 tpl:pb-3" }, w = { class: "tpl:relative" }, T = ["placeholder"], E = { class: "tpl:flex-1 tpl:overflow-y-auto tpl:px-4 tpl:pb-4" }, D = {
|
|
32
20
|
key: 0,
|
|
33
21
|
class: "tpl:flex tpl:flex-col tpl:gap-1"
|
|
34
22
|
}, O = ["aria-pressed", "onClick"], k = { class: "tpl:flex tpl:items-center tpl:gap-2" }, A = { class: "tpl:flex-1 tpl:truncate tpl:text-xs tpl:font-semibold tpl:text-[var(--tpl-text)]" }, j = { class: "tpl:shrink-0 tpl:rounded-full tpl:px-1.5 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]" }, M = { class: "tpl:mt-1 tpl:flex tpl:items-center tpl:gap-1" }, N = {
|
|
@@ -41,45 +29,45 @@ var de = r("search", [["path", {
|
|
|
41
29
|
], I = {
|
|
42
30
|
key: 1,
|
|
43
31
|
class: "tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:py-12"
|
|
44
|
-
}, L = { class: "tpl:mt-2 tpl:text-xs tpl:text-[var(--tpl-text-dim)]" }, R = { class: "tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-hidden tpl:border-l tpl:border-[var(--tpl-border)]" },
|
|
32
|
+
}, L = { class: "tpl:mt-2 tpl:text-xs tpl:text-[var(--tpl-text-dim)]" }, R = { class: "tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-hidden tpl:border-l tpl:border-[var(--tpl-border)]" }, z = {
|
|
45
33
|
key: 0,
|
|
46
34
|
class: "tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-hidden"
|
|
47
|
-
},
|
|
35
|
+
}, he = { class: "tpl:flex-1 tpl:overflow-y-auto tpl:p-4" }, ge = {
|
|
48
36
|
key: 1,
|
|
49
37
|
class: "tpl:flex tpl:flex-1 tpl:flex-col tpl:items-center tpl:justify-center tpl:px-4"
|
|
50
|
-
},
|
|
38
|
+
}, _e = { class: "tpl:mt-2 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]" }, ve = { class: "tpl:flex tpl:items-center tpl:justify-between tpl:border-t tpl:px-5 tpl:py-3 tpl:border-[var(--tpl-border)]" }, ye = { class: "tpl:flex tpl:items-center tpl:gap-2" }, be = { class: "tpl:shrink-0 tpl:text-xs tpl:text-[var(--tpl-text-dim)]" }, xe = ["value"], Se = { class: "tpl:flex tpl:gap-2" }, Ce = ["disabled"], B = /* @__PURE__ */ o({
|
|
51
39
|
__name: "ModuleBrowserModal",
|
|
52
40
|
props: { visible: { type: Boolean } },
|
|
53
41
|
emits: ["close", "insert"],
|
|
54
|
-
setup(
|
|
55
|
-
let
|
|
42
|
+
setup(o, { emit: B }) {
|
|
43
|
+
let we = o, V = B, Te = ie(() => import("./ModulePreviewCanvas-DkSvri9H.js")), { t: H } = ce(), U = _(oe, "ModuleBrowserModal"), W = _(ae, "ModuleBrowserModal"), G = e(""), K = e(null), q = e(null), J = e("end"), Y = h(() => {
|
|
56
44
|
let e = U.modules.value;
|
|
57
45
|
if (!G.value) return e;
|
|
58
46
|
let t = G.value.toLowerCase();
|
|
59
47
|
return e.filter((e) => e.name.toLowerCase().includes(t));
|
|
60
|
-
}), X =
|
|
48
|
+
}), X = h(() => K.value ? U.modules.value.find((e) => e.id === K.value) ?? null : null), Ee = h(() => {
|
|
61
49
|
let e = [{
|
|
62
50
|
value: "beginning",
|
|
63
51
|
label: H.modules.insertAtBeginning
|
|
64
52
|
}], t = W.content.value.blocks;
|
|
65
53
|
for (let n = 0; n < t.length; n++) {
|
|
66
|
-
let r = t[n]
|
|
54
|
+
let r = t[n];
|
|
67
55
|
e.push({
|
|
68
56
|
value: r.id,
|
|
69
|
-
label: H.modules.insertAfterBlock.replace("{block}", `${
|
|
57
|
+
label: H.modules.insertAfterBlock.replace("{block}", `${H.blocks[r.type] ?? r.type} ${n + 1}`)
|
|
70
58
|
});
|
|
71
59
|
}
|
|
72
60
|
return e.push({
|
|
73
61
|
value: "end",
|
|
74
62
|
label: H.modules.insertAtEnd
|
|
75
63
|
}), e;
|
|
76
|
-
}), De =
|
|
64
|
+
}), De = h(() => {
|
|
77
65
|
if (J.value === "end") return;
|
|
78
66
|
if (J.value === "beginning") return 0;
|
|
79
67
|
let e = W.content.value.blocks.findIndex((e) => e.id === J.value);
|
|
80
68
|
if (e !== -1) return e + 1;
|
|
81
69
|
});
|
|
82
|
-
|
|
70
|
+
t(() => we.visible, (e) => {
|
|
83
71
|
if (e) {
|
|
84
72
|
G.value = "", K.value = null, q.value = null;
|
|
85
73
|
let e = W.state.selectedBlockId;
|
|
@@ -88,9 +76,9 @@ var de = r("search", [["path", {
|
|
|
88
76
|
});
|
|
89
77
|
function Oe(e) {
|
|
90
78
|
let t = [], n = /* @__PURE__ */ new Set();
|
|
91
|
-
for (let r of e.content) if (!n.has(r.type) &&
|
|
79
|
+
for (let r of e.content) if (!n.has(r.type) && y[r.type] && (n.add(r.type), t.push({
|
|
92
80
|
type: r.type,
|
|
93
|
-
icon:
|
|
81
|
+
icon: y[r.type]
|
|
94
82
|
})), t.length >= 5) break;
|
|
95
83
|
return t;
|
|
96
84
|
}
|
|
@@ -114,88 +102,88 @@ var de = r("search", [["path", {
|
|
|
114
102
|
function Ae(e) {
|
|
115
103
|
e.key === "Escape" && $(), e.key === "Enter" && X.value && (e.preventDefault(), Q());
|
|
116
104
|
}
|
|
117
|
-
return (e, t) => (
|
|
118
|
-
visible:
|
|
105
|
+
return (e, t) => (i(), f(se, {
|
|
106
|
+
visible: o.visible,
|
|
119
107
|
onClose: $,
|
|
120
108
|
onKeydown: Ae
|
|
121
109
|
}, {
|
|
122
|
-
default:
|
|
123
|
-
|
|
124
|
-
"aria-label":
|
|
110
|
+
default: r(() => [m("div", fe, [
|
|
111
|
+
m("div", pe, [m("h3", me, p(l(H).modules.browse), 1), m("button", {
|
|
112
|
+
"aria-label": l(H).modules.close,
|
|
125
113
|
class: "tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:p-1 tpl:transition-colors tpl:duration-100 tpl:text-[var(--tpl-text-dim)]",
|
|
126
114
|
onClick: $
|
|
127
|
-
}, [
|
|
115
|
+
}, [u(l(ue), {
|
|
128
116
|
size: 16,
|
|
129
117
|
"stroke-width": 2
|
|
130
|
-
})], 8,
|
|
131
|
-
|
|
118
|
+
})], 8, b)]),
|
|
119
|
+
m("div", x, [m("div", S, [m("div", C, [m("div", w, [u(l(de), {
|
|
132
120
|
size: 14,
|
|
133
121
|
"stroke-width": 2,
|
|
134
122
|
class: "tpl:pointer-events-none tpl:absolute tpl:left-3 tpl:top-1/2 tpl:-translate-y-1/2 tpl:text-[var(--tpl-text-dim)]"
|
|
135
|
-
}),
|
|
123
|
+
}), s(m("input", {
|
|
136
124
|
"onUpdate:modelValue": t[0] ||= (e) => G.value = e,
|
|
137
125
|
type: "text",
|
|
138
|
-
placeholder:
|
|
126
|
+
placeholder: l(H).modules.search,
|
|
139
127
|
class: "tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:pl-9 tpl:pr-3 tpl:text-sm tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]"
|
|
140
|
-
}, null, 8, T), [[
|
|
128
|
+
}, null, 8, T), [[re, G.value]])])]), m("div", E, [Y.value.length > 0 ? (i(), c("div", D, [(i(!0), c(d, null, n(Y.value, (e) => (i(), c("button", {
|
|
141
129
|
key: e.id,
|
|
142
130
|
type: "button",
|
|
143
131
|
"aria-pressed": K.value === e.id,
|
|
144
132
|
class: "tpl:group/card tpl:w-full tpl:cursor-pointer tpl:rounded-[var(--tpl-radius-md)] tpl:border tpl:bg-transparent tpl:px-3 tpl:py-2 tpl:text-left tpl:transition-all tpl:duration-[120ms]",
|
|
145
|
-
style:
|
|
133
|
+
style: ee({
|
|
146
134
|
borderColor: K.value === e.id ? "var(--tpl-primary)" : "var(--tpl-border)",
|
|
147
135
|
backgroundColor: K.value === e.id ? "var(--tpl-primary-light)" : "transparent"
|
|
148
136
|
}),
|
|
149
137
|
onClick: (t) => K.value = e.id
|
|
150
|
-
}, [
|
|
151
|
-
(
|
|
138
|
+
}, [m("div", k, [m("span", A, p(e.name), 1), m("span", j, p(l(H).modules.blockCount.replace("{count}", String(e.content.length))), 1)]), m("div", M, [
|
|
139
|
+
(i(!0), c(d, null, n(Oe(e), (e) => (i(), f(a(e.icon), {
|
|
152
140
|
key: e.type,
|
|
153
141
|
size: 14,
|
|
154
142
|
"stroke-width": 1.5,
|
|
155
143
|
class: "tpl:text-[var(--tpl-text-dim)]"
|
|
156
144
|
}))), 128)),
|
|
157
|
-
Z(e) > 0 ? (
|
|
158
|
-
q.value === e.id ? (
|
|
145
|
+
Z(e) > 0 ? (i(), c("span", N, " +" + p(Z(e)), 1)) : te("", !0),
|
|
146
|
+
q.value === e.id ? (i(), c("button", {
|
|
159
147
|
key: 1,
|
|
160
|
-
"aria-label":
|
|
148
|
+
"aria-label": l(H).modules.deleteConfirm,
|
|
161
149
|
class: "tpl:ml-auto tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-2 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:transition-colors tpl:duration-100 tpl:border-[var(--tpl-danger)] tpl:text-[var(--tpl-danger)]",
|
|
162
150
|
style: { "background-color": "transparent" },
|
|
163
|
-
onClick:
|
|
164
|
-
},
|
|
151
|
+
onClick: g((t) => ke(e.id), ["stop"])
|
|
152
|
+
}, p(l(H).modules.deleteConfirm), 9, P)) : (i(), c("button", {
|
|
165
153
|
key: 2,
|
|
166
154
|
class: "tpl-module-delete-btn tpl:ml-auto tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:p-0.5 tpl:transition-colors tpl:duration-100 tpl:text-[var(--tpl-text-dim)]",
|
|
167
|
-
"aria-label":
|
|
168
|
-
title:
|
|
169
|
-
onClick:
|
|
170
|
-
}, [
|
|
155
|
+
"aria-label": l(H).modules.delete,
|
|
156
|
+
title: l(H).modules.delete,
|
|
157
|
+
onClick: g((t) => q.value = e.id, ["stop"])
|
|
158
|
+
}, [u(l(le), {
|
|
171
159
|
size: 12,
|
|
172
160
|
"stroke-width": 1.5
|
|
173
161
|
})], 8, F))
|
|
174
|
-
])], 12, O))), 128))])) : (
|
|
162
|
+
])], 12, O))), 128))])) : (i(), c("div", I, [u(l(v), {
|
|
175
163
|
size: 32,
|
|
176
164
|
"stroke-width": 1,
|
|
177
165
|
class: "tpl:text-[var(--tpl-text-dim)]"
|
|
178
|
-
}),
|
|
166
|
+
}), m("p", L, p(G.value ? l(H).modules.noModules : l(H).modules.noModulesHint), 1)]))])]), m("div", R, [X.value ? (i(), c("div", z, [m("div", he, [u(l(Te), { blocks: X.value.content }, null, 8, ["blocks"])])])) : (i(), c("div", ge, [u(l(v), {
|
|
179
167
|
size: 32,
|
|
180
168
|
"stroke-width": 1,
|
|
181
169
|
class: "tpl:text-[var(--tpl-text-dim)]"
|
|
182
|
-
}),
|
|
183
|
-
|
|
170
|
+
}), m("p", _e, p(l(H).modules.selectToPreview), 1)]))])]),
|
|
171
|
+
m("div", ve, [m("div", ye, [m("label", be, p(l(H).modules.insertPosition), 1), s(m("select", {
|
|
184
172
|
"onUpdate:modelValue": t[1] ||= (e) => J.value = e,
|
|
185
173
|
class: "tpl:h-7 tpl:max-w-[220px] tpl:rounded-md tpl:border tpl:px-2 tpl:text-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]"
|
|
186
|
-
}, [(
|
|
174
|
+
}, [(i(!0), c(d, null, n(Ee.value, (e) => (i(), c("option", {
|
|
187
175
|
key: e.value,
|
|
188
176
|
value: e.value
|
|
189
|
-
},
|
|
177
|
+
}, p(e.label), 9, xe))), 128))], 512), [[ne, J.value]])]), m("div", Se, [m("button", {
|
|
190
178
|
type: "button",
|
|
191
179
|
class: "tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]",
|
|
192
180
|
onClick: $
|
|
193
|
-
},
|
|
181
|
+
}, p(l(H).modules.close), 1), m("button", {
|
|
194
182
|
type: "button",
|
|
195
183
|
class: "tpl:cursor-pointer tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:cursor-not-allowed tpl:disabled:opacity-50 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]",
|
|
196
184
|
disabled: !X.value,
|
|
197
185
|
onClick: Q
|
|
198
|
-
},
|
|
186
|
+
}, p(l(H).modules.insert), 9, Ce)])])
|
|
199
187
|
])]),
|
|
200
188
|
_: 1
|
|
201
189
|
}, 8, ["visible"]));
|
|
@@ -203,3 +191,5 @@ var de = r("search", [["path", {
|
|
|
203
191
|
});
|
|
204
192
|
//#endregion
|
|
205
193
|
export { B as default };
|
|
194
|
+
|
|
195
|
+
//# sourceMappingURL=ModuleBrowserModal-ChBr3aXj.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ModuleBrowserModal-ChBr3aXj.js","names":[],"sources":["../../../src/cloud/components/ModuleBrowserModal.vue","../../../src/cloud/components/ModuleBrowserModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport TplModal from \"./TplModal.vue\";\nimport { useI18n } from \"../../composables\";\nimport { blockTypeIcons } from \"../../utils/blockTypeIcons\";\nimport {\n SAVED_MODULES_HEADLESS_KEY,\n EDITOR_KEY,\n requireInject,\n} from \"../../keys\";\nimport type { SavedModule } from \"@templatical/types\";\nimport { Package, Search, Trash2, X } from \"@lucide/vue\";\nimport { computed, defineAsyncComponent, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"insert\", module: SavedModule, insertIndex: number | undefined): void;\n}>();\n\nconst ModulePreviewCanvas = defineAsyncComponent(\n () => import(\"./ModulePreviewCanvas.vue\"),\n);\n\nconst { t } = useI18n();\nconst savedModules = requireInject(\n SAVED_MODULES_HEADLESS_KEY,\n \"ModuleBrowserModal\",\n);\nconst editor = requireInject(EDITOR_KEY, \"ModuleBrowserModal\");\n\nconst searchQuery = ref(\"\");\nconst selectedModuleId = ref<string | null>(null);\nconst confirmDeleteId = ref<string | null>(null);\n// 'end' = append, 'beginning' = index 0, or block id = after that block\nconst insertPosition = ref<string>(\"end\");\n\nconst filteredModules = computed(() => {\n const modules = savedModules.modules.value;\n if (!searchQuery.value) return modules;\n const query = searchQuery.value.toLowerCase();\n return modules.filter((m) => m.name.toLowerCase().includes(query));\n});\n\nconst selectedModule = computed(() => {\n if (!selectedModuleId.value) return null;\n return (\n savedModules.modules.value.find((m) => m.id === selectedModuleId.value) ??\n null\n );\n});\n\ninterface PositionOption {\n value: string;\n label: string;\n}\n\nconst positionOptions = computed<PositionOption[]>(() => {\n const options: PositionOption[] = [\n { value: \"beginning\", label: t.modules.insertAtBeginning },\n ];\n const blocks = editor.content.value.blocks;\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i];\n const typeKey = block.type as keyof typeof t.blocks;\n const label = t.blocks[typeKey] ?? block.type;\n options.push({\n value: block.id,\n label: t.modules.insertAfterBlock.replace(\"{block}\", `${label} ${i + 1}`),\n });\n }\n options.push({ value: \"end\", label: t.modules.insertAtEnd });\n return options;\n});\n\nconst resolvedInsertIndex = computed<number | undefined>(() => {\n if (insertPosition.value === \"end\") return undefined;\n if (insertPosition.value === \"beginning\") return 0;\n const blocks = editor.content.value.blocks;\n const idx = blocks.findIndex((b) => b.id === insertPosition.value);\n if (idx !== -1) return idx + 1;\n return undefined;\n});\n\nwatch(\n () => props.visible,\n (visible) => {\n if (visible) {\n searchQuery.value = \"\";\n selectedModuleId.value = null;\n confirmDeleteId.value = null;\n // Default to after selected block, or end\n const selectedId = editor.state.selectedBlockId;\n if (selectedId) {\n const idx = editor.content.value.blocks.findIndex(\n (b) => b.id === selectedId,\n );\n insertPosition.value = idx !== -1 ? selectedId : \"end\";\n } else {\n insertPosition.value = \"end\";\n }\n }\n },\n);\n\nfunction getModuleBlockTypeIcons(\n module: SavedModule,\n): { type: string; icon: unknown }[] {\n const icons: { type: string; icon: unknown }[] = [];\n const seen = new Set<string>();\n for (const block of module.content) {\n if (!seen.has(block.type) && blockTypeIcons[block.type]) {\n seen.add(block.type);\n icons.push({ type: block.type, icon: blockTypeIcons[block.type] });\n }\n if (icons.length >= 5) break;\n }\n return icons;\n}\n\nfunction getRemainingTypeCount(module: SavedModule): number {\n const types = new Set(module.content.map((b) => b.type));\n return Math.max(0, types.size - 5);\n}\n\nasync function handleDelete(moduleId: string): Promise<void> {\n try {\n await savedModules.deleteModule(moduleId);\n if (selectedModuleId.value === moduleId) {\n selectedModuleId.value = null;\n }\n } finally {\n confirmDeleteId.value = null;\n }\n}\n\nfunction handleInsert(): void {\n if (selectedModule.value) {\n emit(\"insert\", selectedModule.value, resolvedInsertIndex.value);\n }\n}\n\nfunction handleClose(): void {\n emit(\"close\");\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Escape\") {\n handleClose();\n }\n if (event.key === \"Enter\" && selectedModule.value) {\n event.preventDefault();\n handleInsert();\n }\n}\n</script>\n\n<template>\n <TplModal :visible=\"visible\" @close=\"handleClose\" @keydown=\"handleKeydown\">\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"tpl-module-browser-title\"\n class=\"tpl-scale-in tpl:mx-4 tpl:flex tpl:w-full tpl:max-w-[1000px] tpl:flex-col tpl:rounded-[var(--tpl-radius-lg)]\"\n style=\"\n background-color: var(--tpl-bg-elevated);\n box-shadow: var(--tpl-shadow-xl);\n max-height: 90vh;\n \"\n >\n <!-- Header -->\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:px-5 tpl:py-4 tpl:border-[var(--tpl-border)]\"\n >\n <h3\n id=\"tpl-module-browser-title\"\n class=\"tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.modules.browse }}\n </h3>\n <button\n :aria-label=\"t.modules.close\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:p-1 tpl:transition-colors tpl:duration-100 tpl:text-[var(--tpl-text-dim)]\"\n @click=\"handleClose\"\n >\n <X :size=\"16\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Body -->\n <div class=\"tpl:flex tpl:min-h-0 tpl:flex-1 tpl:overflow-hidden\">\n <!-- Left panel: module grid -->\n <div\n class=\"tpl:flex tpl:w-[300px] tpl:shrink-0 tpl:flex-col tpl:overflow-hidden\"\n >\n <!-- Search -->\n <div class=\"tpl:px-4 tpl:pt-4 tpl:pb-3\">\n <div class=\"tpl:relative\">\n <Search\n :size=\"14\"\n :stroke-width=\"2\"\n class=\"tpl:pointer-events-none tpl:absolute tpl:left-3 tpl:top-1/2 tpl:-translate-y-1/2 tpl:text-[var(--tpl-text-dim)]\"\n />\n <input\n v-model=\"searchQuery\"\n type=\"text\"\n :placeholder=\"t.modules.search\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:pl-9 tpl:pr-3 tpl:text-sm tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n />\n </div>\n </div>\n\n <!-- Grid -->\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:px-4 tpl:pb-4\">\n <div\n v-if=\"filteredModules.length > 0\"\n class=\"tpl:flex tpl:flex-col tpl:gap-1\"\n >\n <button\n v-for=\"mod in filteredModules\"\n :key=\"mod.id\"\n type=\"button\"\n :aria-pressed=\"selectedModuleId === mod.id\"\n class=\"tpl:group/card tpl:w-full tpl:cursor-pointer tpl:rounded-[var(--tpl-radius-md)] tpl:border tpl:bg-transparent tpl:px-3 tpl:py-2 tpl:text-left tpl:transition-all tpl:duration-[120ms]\"\n :style=\"{\n borderColor:\n selectedModuleId === mod.id\n ? 'var(--tpl-primary)'\n : 'var(--tpl-border)',\n backgroundColor:\n selectedModuleId === mod.id\n ? 'var(--tpl-primary-light)'\n : 'transparent',\n }\"\n @click=\"selectedModuleId = mod.id\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <span\n class=\"tpl:flex-1 tpl:truncate tpl:text-xs tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ mod.name }}\n </span>\n <span\n class=\"tpl:shrink-0 tpl:rounded-full tpl:px-1.5 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]\"\n >\n {{\n t.modules.blockCount.replace(\n \"{count}\",\n String(mod.content.length),\n )\n }}\n </span>\n </div>\n <div class=\"tpl:mt-1 tpl:flex tpl:items-center tpl:gap-1\">\n <component\n :is=\"icon.icon\"\n v-for=\"icon in getModuleBlockTypeIcons(mod)\"\n :key=\"icon.type\"\n :size=\"14\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <span\n v-if=\"getRemainingTypeCount(mod) > 0\"\n class=\"tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n +{{ getRemainingTypeCount(mod) }}\n </span>\n <button\n v-if=\"confirmDeleteId === mod.id\"\n :aria-label=\"t.modules.deleteConfirm\"\n class=\"tpl:ml-auto tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-2 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:transition-colors tpl:duration-100 tpl:border-[var(--tpl-danger)] tpl:text-[var(--tpl-danger)]\"\n style=\"background-color: transparent\"\n @click.stop=\"handleDelete(mod.id)\"\n >\n {{ t.modules.deleteConfirm }}\n </button>\n <button\n v-else\n class=\"tpl-module-delete-btn tpl:ml-auto tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:p-0.5 tpl:transition-colors tpl:duration-100 tpl:text-[var(--tpl-text-dim)]\"\n :aria-label=\"t.modules.delete\"\n :title=\"t.modules.delete\"\n @click.stop=\"confirmDeleteId = mod.id\"\n >\n <Trash2 :size=\"12\" :stroke-width=\"1.5\" />\n </button>\n </div>\n </button>\n </div>\n\n <!-- Empty state -->\n <div\n v-else\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:py-12\"\n >\n <Package\n :size=\"32\"\n :stroke-width=\"1\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <p class=\"tpl:mt-2 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\">\n {{\n searchQuery ? t.modules.noModules : t.modules.noModulesHint\n }}\n </p>\n </div>\n </div>\n </div>\n\n <!-- Right panel: preview -->\n <div\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-hidden tpl:border-l tpl:border-[var(--tpl-border)]\"\n >\n <div\n v-if=\"selectedModule\"\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-hidden\"\n >\n <!-- Visual preview -->\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:p-4\">\n <ModulePreviewCanvas :blocks=\"selectedModule.content\" />\n </div>\n </div>\n\n <!-- Empty preview state -->\n <div\n v-else\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:items-center tpl:justify-center tpl:px-4\"\n >\n <Package\n :size=\"32\"\n :stroke-width=\"1\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <p\n class=\"tpl:mt-2 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.modules.selectToPreview }}\n </p>\n </div>\n </div>\n </div>\n\n <!-- Footer -->\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-t tpl:px-5 tpl:py-3 tpl:border-[var(--tpl-border)]\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <label\n class=\"tpl:shrink-0 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.modules.insertPosition }}\n </label>\n <select\n v-model=\"insertPosition\"\n class=\"tpl:h-7 tpl:max-w-[220px] tpl:rounded-md tpl:border tpl:px-2 tpl:text-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n >\n <option\n v-for=\"opt in positionOptions\"\n :key=\"opt.value\"\n :value=\"opt.value\"\n >\n {{ opt.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:flex tpl:gap-2\">\n <button\n type=\"button\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]\"\n @click=\"handleClose\"\n >\n {{ t.modules.close }}\n </button>\n <button\n type=\"button\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:cursor-not-allowed tpl:disabled:opacity-50 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n :disabled=\"!selectedModule\"\n @click=\"handleInsert\"\n >\n {{ t.modules.insert }}\n </button>\n </div>\n </div>\n </div>\n </TplModal>\n</template>\n\n<style>\n.tpl-module-delete-btn:hover {\n color: var(--tpl-danger) !important;\n}\n</style>\n","<script setup lang=\"ts\">\nimport TplModal from \"./TplModal.vue\";\nimport { useI18n } from \"../../composables\";\nimport { blockTypeIcons } from \"../../utils/blockTypeIcons\";\nimport {\n SAVED_MODULES_HEADLESS_KEY,\n EDITOR_KEY,\n requireInject,\n} from \"../../keys\";\nimport type { SavedModule } from \"@templatical/types\";\nimport { Package, Search, Trash2, X } from \"@lucide/vue\";\nimport { computed, defineAsyncComponent, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"insert\", module: SavedModule, insertIndex: number | undefined): void;\n}>();\n\nconst ModulePreviewCanvas = defineAsyncComponent(\n () => import(\"./ModulePreviewCanvas.vue\"),\n);\n\nconst { t } = useI18n();\nconst savedModules = requireInject(\n SAVED_MODULES_HEADLESS_KEY,\n \"ModuleBrowserModal\",\n);\nconst editor = requireInject(EDITOR_KEY, \"ModuleBrowserModal\");\n\nconst searchQuery = ref(\"\");\nconst selectedModuleId = ref<string | null>(null);\nconst confirmDeleteId = ref<string | null>(null);\n// 'end' = append, 'beginning' = index 0, or block id = after that block\nconst insertPosition = ref<string>(\"end\");\n\nconst filteredModules = computed(() => {\n const modules = savedModules.modules.value;\n if (!searchQuery.value) return modules;\n const query = searchQuery.value.toLowerCase();\n return modules.filter((m) => m.name.toLowerCase().includes(query));\n});\n\nconst selectedModule = computed(() => {\n if (!selectedModuleId.value) return null;\n return (\n savedModules.modules.value.find((m) => m.id === selectedModuleId.value) ??\n null\n );\n});\n\ninterface PositionOption {\n value: string;\n label: string;\n}\n\nconst positionOptions = computed<PositionOption[]>(() => {\n const options: PositionOption[] = [\n { value: \"beginning\", label: t.modules.insertAtBeginning },\n ];\n const blocks = editor.content.value.blocks;\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i];\n const typeKey = block.type as keyof typeof t.blocks;\n const label = t.blocks[typeKey] ?? block.type;\n options.push({\n value: block.id,\n label: t.modules.insertAfterBlock.replace(\"{block}\", `${label} ${i + 1}`),\n });\n }\n options.push({ value: \"end\", label: t.modules.insertAtEnd });\n return options;\n});\n\nconst resolvedInsertIndex = computed<number | undefined>(() => {\n if (insertPosition.value === \"end\") return undefined;\n if (insertPosition.value === \"beginning\") return 0;\n const blocks = editor.content.value.blocks;\n const idx = blocks.findIndex((b) => b.id === insertPosition.value);\n if (idx !== -1) return idx + 1;\n return undefined;\n});\n\nwatch(\n () => props.visible,\n (visible) => {\n if (visible) {\n searchQuery.value = \"\";\n selectedModuleId.value = null;\n confirmDeleteId.value = null;\n // Default to after selected block, or end\n const selectedId = editor.state.selectedBlockId;\n if (selectedId) {\n const idx = editor.content.value.blocks.findIndex(\n (b) => b.id === selectedId,\n );\n insertPosition.value = idx !== -1 ? selectedId : \"end\";\n } else {\n insertPosition.value = \"end\";\n }\n }\n },\n);\n\nfunction getModuleBlockTypeIcons(\n module: SavedModule,\n): { type: string; icon: unknown }[] {\n const icons: { type: string; icon: unknown }[] = [];\n const seen = new Set<string>();\n for (const block of module.content) {\n if (!seen.has(block.type) && blockTypeIcons[block.type]) {\n seen.add(block.type);\n icons.push({ type: block.type, icon: blockTypeIcons[block.type] });\n }\n if (icons.length >= 5) break;\n }\n return icons;\n}\n\nfunction getRemainingTypeCount(module: SavedModule): number {\n const types = new Set(module.content.map((b) => b.type));\n return Math.max(0, types.size - 5);\n}\n\nasync function handleDelete(moduleId: string): Promise<void> {\n try {\n await savedModules.deleteModule(moduleId);\n if (selectedModuleId.value === moduleId) {\n selectedModuleId.value = null;\n }\n } finally {\n confirmDeleteId.value = null;\n }\n}\n\nfunction handleInsert(): void {\n if (selectedModule.value) {\n emit(\"insert\", selectedModule.value, resolvedInsertIndex.value);\n }\n}\n\nfunction handleClose(): void {\n emit(\"close\");\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Escape\") {\n handleClose();\n }\n if (event.key === \"Enter\" && selectedModule.value) {\n event.preventDefault();\n handleInsert();\n }\n}\n</script>\n\n<template>\n <TplModal :visible=\"visible\" @close=\"handleClose\" @keydown=\"handleKeydown\">\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"tpl-module-browser-title\"\n class=\"tpl-scale-in tpl:mx-4 tpl:flex tpl:w-full tpl:max-w-[1000px] tpl:flex-col tpl:rounded-[var(--tpl-radius-lg)]\"\n style=\"\n background-color: var(--tpl-bg-elevated);\n box-shadow: var(--tpl-shadow-xl);\n max-height: 90vh;\n \"\n >\n <!-- Header -->\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:px-5 tpl:py-4 tpl:border-[var(--tpl-border)]\"\n >\n <h3\n id=\"tpl-module-browser-title\"\n class=\"tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.modules.browse }}\n </h3>\n <button\n :aria-label=\"t.modules.close\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:p-1 tpl:transition-colors tpl:duration-100 tpl:text-[var(--tpl-text-dim)]\"\n @click=\"handleClose\"\n >\n <X :size=\"16\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Body -->\n <div class=\"tpl:flex tpl:min-h-0 tpl:flex-1 tpl:overflow-hidden\">\n <!-- Left panel: module grid -->\n <div\n class=\"tpl:flex tpl:w-[300px] tpl:shrink-0 tpl:flex-col tpl:overflow-hidden\"\n >\n <!-- Search -->\n <div class=\"tpl:px-4 tpl:pt-4 tpl:pb-3\">\n <div class=\"tpl:relative\">\n <Search\n :size=\"14\"\n :stroke-width=\"2\"\n class=\"tpl:pointer-events-none tpl:absolute tpl:left-3 tpl:top-1/2 tpl:-translate-y-1/2 tpl:text-[var(--tpl-text-dim)]\"\n />\n <input\n v-model=\"searchQuery\"\n type=\"text\"\n :placeholder=\"t.modules.search\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:pl-9 tpl:pr-3 tpl:text-sm tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n />\n </div>\n </div>\n\n <!-- Grid -->\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:px-4 tpl:pb-4\">\n <div\n v-if=\"filteredModules.length > 0\"\n class=\"tpl:flex tpl:flex-col tpl:gap-1\"\n >\n <button\n v-for=\"mod in filteredModules\"\n :key=\"mod.id\"\n type=\"button\"\n :aria-pressed=\"selectedModuleId === mod.id\"\n class=\"tpl:group/card tpl:w-full tpl:cursor-pointer tpl:rounded-[var(--tpl-radius-md)] tpl:border tpl:bg-transparent tpl:px-3 tpl:py-2 tpl:text-left tpl:transition-all tpl:duration-[120ms]\"\n :style=\"{\n borderColor:\n selectedModuleId === mod.id\n ? 'var(--tpl-primary)'\n : 'var(--tpl-border)',\n backgroundColor:\n selectedModuleId === mod.id\n ? 'var(--tpl-primary-light)'\n : 'transparent',\n }\"\n @click=\"selectedModuleId = mod.id\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <span\n class=\"tpl:flex-1 tpl:truncate tpl:text-xs tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ mod.name }}\n </span>\n <span\n class=\"tpl:shrink-0 tpl:rounded-full tpl:px-1.5 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]\"\n >\n {{\n t.modules.blockCount.replace(\n \"{count}\",\n String(mod.content.length),\n )\n }}\n </span>\n </div>\n <div class=\"tpl:mt-1 tpl:flex tpl:items-center tpl:gap-1\">\n <component\n :is=\"icon.icon\"\n v-for=\"icon in getModuleBlockTypeIcons(mod)\"\n :key=\"icon.type\"\n :size=\"14\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <span\n v-if=\"getRemainingTypeCount(mod) > 0\"\n class=\"tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n +{{ getRemainingTypeCount(mod) }}\n </span>\n <button\n v-if=\"confirmDeleteId === mod.id\"\n :aria-label=\"t.modules.deleteConfirm\"\n class=\"tpl:ml-auto tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-2 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:transition-colors tpl:duration-100 tpl:border-[var(--tpl-danger)] tpl:text-[var(--tpl-danger)]\"\n style=\"background-color: transparent\"\n @click.stop=\"handleDelete(mod.id)\"\n >\n {{ t.modules.deleteConfirm }}\n </button>\n <button\n v-else\n class=\"tpl-module-delete-btn tpl:ml-auto tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:p-0.5 tpl:transition-colors tpl:duration-100 tpl:text-[var(--tpl-text-dim)]\"\n :aria-label=\"t.modules.delete\"\n :title=\"t.modules.delete\"\n @click.stop=\"confirmDeleteId = mod.id\"\n >\n <Trash2 :size=\"12\" :stroke-width=\"1.5\" />\n </button>\n </div>\n </button>\n </div>\n\n <!-- Empty state -->\n <div\n v-else\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:py-12\"\n >\n <Package\n :size=\"32\"\n :stroke-width=\"1\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <p class=\"tpl:mt-2 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\">\n {{\n searchQuery ? t.modules.noModules : t.modules.noModulesHint\n }}\n </p>\n </div>\n </div>\n </div>\n\n <!-- Right panel: preview -->\n <div\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-hidden tpl:border-l tpl:border-[var(--tpl-border)]\"\n >\n <div\n v-if=\"selectedModule\"\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-hidden\"\n >\n <!-- Visual preview -->\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:p-4\">\n <ModulePreviewCanvas :blocks=\"selectedModule.content\" />\n </div>\n </div>\n\n <!-- Empty preview state -->\n <div\n v-else\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:items-center tpl:justify-center tpl:px-4\"\n >\n <Package\n :size=\"32\"\n :stroke-width=\"1\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <p\n class=\"tpl:mt-2 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.modules.selectToPreview }}\n </p>\n </div>\n </div>\n </div>\n\n <!-- Footer -->\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-t tpl:px-5 tpl:py-3 tpl:border-[var(--tpl-border)]\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <label\n class=\"tpl:shrink-0 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.modules.insertPosition }}\n </label>\n <select\n v-model=\"insertPosition\"\n class=\"tpl:h-7 tpl:max-w-[220px] tpl:rounded-md tpl:border tpl:px-2 tpl:text-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n >\n <option\n v-for=\"opt in positionOptions\"\n :key=\"opt.value\"\n :value=\"opt.value\"\n >\n {{ opt.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:flex tpl:gap-2\">\n <button\n type=\"button\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]\"\n @click=\"handleClose\"\n >\n {{ t.modules.close }}\n </button>\n <button\n type=\"button\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:cursor-not-allowed tpl:disabled:opacity-50 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n :disabled=\"!selectedModule\"\n @click=\"handleInsert\"\n >\n {{ t.modules.insert }}\n </button>\n </div>\n </div>\n </div>\n </TplModal>\n</template>\n\n<style>\n.tpl-module-delete-btn:hover {\n color: var(--tpl-danger) !important;\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { E as e, F as t, L as n, N as r, R as i, S as a, _ as o, at as s, ct as c, d as l, h as u, m as d, p as f } from "./draggable-BQNU47zu.js";
|
|
2
|
+
import { A as p, B as m, G as h, H as g, It as _, K as v, L as y, M as b, N as x, Ot as S, U as C, V as w, W as T, j as E, z as D } from "./features-Ds0XUfte.js";
|
|
3
|
+
//#region src/components/blocks/PreviewSectionBlock.vue?vue&type=script&setup=true&lang.ts
|
|
4
|
+
var O = { class: "tpl:w-full" }, k = { class: "tpl:flex tpl:gap-0" }, A = /* @__PURE__ */ a({
|
|
5
|
+
name: "PreviewSectionBlock",
|
|
6
|
+
__name: "PreviewSectionBlock",
|
|
7
|
+
props: {
|
|
8
|
+
block: {},
|
|
9
|
+
viewport: {}
|
|
10
|
+
},
|
|
11
|
+
setup(a) {
|
|
12
|
+
let A = {
|
|
13
|
+
title: y,
|
|
14
|
+
paragraph: w,
|
|
15
|
+
image: C,
|
|
16
|
+
video: p,
|
|
17
|
+
button: S,
|
|
18
|
+
divider: h,
|
|
19
|
+
social: x,
|
|
20
|
+
menu: g,
|
|
21
|
+
table: E,
|
|
22
|
+
spacer: b,
|
|
23
|
+
html: T,
|
|
24
|
+
custom: v
|
|
25
|
+
}, j = a, M = e(_, null), N = f(() => {
|
|
26
|
+
switch (j.block.columns) {
|
|
27
|
+
case "2": return ["50%", "50%"];
|
|
28
|
+
case "3": return [
|
|
29
|
+
"33.33%",
|
|
30
|
+
"33.33%",
|
|
31
|
+
"33.33%"
|
|
32
|
+
];
|
|
33
|
+
case "1-2": return ["33.33%", "66.67%"];
|
|
34
|
+
case "2-1": return ["66.67%", "33.33%"];
|
|
35
|
+
default: return ["100%"];
|
|
36
|
+
}
|
|
37
|
+
}), P = f(() => {
|
|
38
|
+
let e = N.value.length, t = [...j.block.children];
|
|
39
|
+
for (; t.length < e;) t.push([]);
|
|
40
|
+
return t.slice(0, e);
|
|
41
|
+
});
|
|
42
|
+
function F(e) {
|
|
43
|
+
return P.value[e] || [];
|
|
44
|
+
}
|
|
45
|
+
function I(e) {
|
|
46
|
+
return m(e, M, A);
|
|
47
|
+
}
|
|
48
|
+
return (e, a) => {
|
|
49
|
+
let f = n("PreviewSectionBlock", !0);
|
|
50
|
+
return r(), o("div", O, [d("div", k, [(r(!0), o(l, null, t(P.value, (e, n) => (r(), o("div", {
|
|
51
|
+
key: n,
|
|
52
|
+
style: c({ width: N.value[n] })
|
|
53
|
+
}, [(r(!0), o(l, null, t(F(n), (e) => (r(), o("div", {
|
|
54
|
+
key: e.id,
|
|
55
|
+
style: c(s(D)(e))
|
|
56
|
+
}, [e.type === "section" ? (r(), u(f, {
|
|
57
|
+
key: 0,
|
|
58
|
+
block: e,
|
|
59
|
+
viewport: "desktop"
|
|
60
|
+
}, null, 8, ["block"])) : (r(), u(i(I(e)), {
|
|
61
|
+
key: 1,
|
|
62
|
+
block: e,
|
|
63
|
+
viewport: "desktop"
|
|
64
|
+
}, null, 8, ["block"]))], 4))), 128))], 4))), 128))])]);
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}), j = {
|
|
68
|
+
class: "tpl:pointer-events-none tpl:mx-auto tpl:w-[600px] tpl:select-none tpl:rounded-lg",
|
|
69
|
+
style: {
|
|
70
|
+
"background-color": "var(--tpl-canvas-bg)",
|
|
71
|
+
"box-shadow": "var(--tpl-shadow-sm)"
|
|
72
|
+
}
|
|
73
|
+
}, M = /* @__PURE__ */ a({
|
|
74
|
+
__name: "ModulePreviewCanvas",
|
|
75
|
+
props: { blocks: {} },
|
|
76
|
+
setup(n) {
|
|
77
|
+
let a = e(_), d = {
|
|
78
|
+
section: A,
|
|
79
|
+
title: y,
|
|
80
|
+
paragraph: w,
|
|
81
|
+
image: C,
|
|
82
|
+
video: p,
|
|
83
|
+
button: S,
|
|
84
|
+
divider: h,
|
|
85
|
+
social: x,
|
|
86
|
+
menu: g,
|
|
87
|
+
table: E,
|
|
88
|
+
spacer: b,
|
|
89
|
+
html: T,
|
|
90
|
+
custom: v
|
|
91
|
+
};
|
|
92
|
+
function f(e) {
|
|
93
|
+
return m(e, a, d);
|
|
94
|
+
}
|
|
95
|
+
return (e, a) => (r(), o("div", j, [(r(!0), o(l, null, t(n.blocks, (e) => (r(), o("div", {
|
|
96
|
+
key: e.id,
|
|
97
|
+
style: c(s(D)(e))
|
|
98
|
+
}, [(r(), u(i(f(e)), {
|
|
99
|
+
block: e,
|
|
100
|
+
viewport: "desktop"
|
|
101
|
+
}, null, 8, ["block"]))], 4))), 128))]));
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
//#endregion
|
|
105
|
+
export { M as default };
|
|
106
|
+
|
|
107
|
+
//# sourceMappingURL=ModulePreviewCanvas-DkSvri9H.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ModulePreviewCanvas-DkSvri9H.js","names":[],"sources":["../../../src/components/blocks/PreviewSectionBlock.vue","../../../src/components/blocks/PreviewSectionBlock.vue","../../../src/cloud/components/ModulePreviewCanvas.vue","../../../src/cloud/components/ModulePreviewCanvas.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport ButtonBlock from \"./ButtonBlock.vue\";\nimport CustomBlock from \"./CustomBlock.vue\";\nimport DividerBlock from \"./DividerBlock.vue\";\nimport HtmlBlock from \"./HtmlBlock.vue\";\nimport ImageBlock from \"./ImageBlock.vue\";\nimport MenuBlock from \"./MenuBlock.vue\";\nimport SocialIconsBlock from \"./SocialIconsBlock.vue\";\nimport SpacerBlock from \"./SpacerBlock.vue\";\nimport TableBlock from \"./TableBlock.vue\";\nimport TitleBlock from \"./TitleBlock.vue\";\nimport ParagraphBlock from \"./ParagraphBlock.vue\";\nimport VideoBlock from \"./VideoBlock.vue\";\nimport {\n resolveBlockComponent,\n getBlockWrapperStyle,\n} from \"../../utils/blockComponentResolver\";\nimport type {\n Block,\n SectionBlock as SectionBlockType,\n} from \"@templatical/types\";\nimport { computed, inject, type Component } from \"vue\";\nimport { BLOCK_REGISTRY_KEY } from \"../../keys\";\n\nconst previewBlockComponentMap: Record<string, Component> = {\n title: TitleBlock,\n paragraph: ParagraphBlock,\n image: ImageBlock,\n video: VideoBlock,\n button: ButtonBlock,\n divider: DividerBlock,\n social: SocialIconsBlock,\n menu: MenuBlock,\n table: TableBlock,\n spacer: SpacerBlock,\n html: HtmlBlock,\n custom: CustomBlock,\n};\n\nconst props = defineProps<{\n block: SectionBlockType;\n viewport: \"desktop\";\n}>();\n\ndefineOptions({ name: \"PreviewSectionBlock\" });\n\nconst blockRegistry = inject(BLOCK_REGISTRY_KEY, null);\n\nconst columnWidths = computed(() => {\n switch (props.block.columns) {\n case \"2\":\n return [\"50%\", \"50%\"];\n case \"3\":\n return [\"33.33%\", \"33.33%\", \"33.33%\"];\n case \"1-2\":\n return [\"33.33%\", \"66.67%\"];\n case \"2-1\":\n return [\"66.67%\", \"33.33%\"];\n default:\n return [\"100%\"];\n }\n});\n\nconst columns = computed(() => {\n const count = columnWidths.value.length;\n const children = [...props.block.children];\n while (children.length < count) {\n children.push([]);\n }\n return children.slice(0, count);\n});\n\nfunction getColumnBlocks(colIndex: number): Block[] {\n return columns.value[colIndex] || [];\n}\n\nfunction getBlockComponent(block: Block): Component | null {\n return resolveBlockComponent(block, blockRegistry, previewBlockComponentMap);\n}\n</script>\n\n<template>\n <div class=\"tpl:w-full\">\n <div class=\"tpl:flex tpl:gap-0\">\n <div\n v-for=\"(_, colIndex) in columns\"\n :key=\"colIndex\"\n :style=\"{ width: columnWidths[colIndex] }\"\n >\n <div\n v-for=\"childBlock in getColumnBlocks(colIndex)\"\n :key=\"childBlock.id\"\n :style=\"getBlockWrapperStyle(childBlock)\"\n >\n <!-- Recursive self-reference for nested sections -->\n <PreviewSectionBlock\n v-if=\"childBlock.type === 'section'\"\n :block=\"childBlock\"\n viewport=\"desktop\"\n />\n <component\n :is=\"getBlockComponent(childBlock)\"\n v-else\n :block=\"childBlock\"\n viewport=\"desktop\"\n />\n </div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ButtonBlock from \"./ButtonBlock.vue\";\nimport CustomBlock from \"./CustomBlock.vue\";\nimport DividerBlock from \"./DividerBlock.vue\";\nimport HtmlBlock from \"./HtmlBlock.vue\";\nimport ImageBlock from \"./ImageBlock.vue\";\nimport MenuBlock from \"./MenuBlock.vue\";\nimport SocialIconsBlock from \"./SocialIconsBlock.vue\";\nimport SpacerBlock from \"./SpacerBlock.vue\";\nimport TableBlock from \"./TableBlock.vue\";\nimport TitleBlock from \"./TitleBlock.vue\";\nimport ParagraphBlock from \"./ParagraphBlock.vue\";\nimport VideoBlock from \"./VideoBlock.vue\";\nimport {\n resolveBlockComponent,\n getBlockWrapperStyle,\n} from \"../../utils/blockComponentResolver\";\nimport type {\n Block,\n SectionBlock as SectionBlockType,\n} from \"@templatical/types\";\nimport { computed, inject, type Component } from \"vue\";\nimport { BLOCK_REGISTRY_KEY } from \"../../keys\";\n\nconst previewBlockComponentMap: Record<string, Component> = {\n title: TitleBlock,\n paragraph: ParagraphBlock,\n image: ImageBlock,\n video: VideoBlock,\n button: ButtonBlock,\n divider: DividerBlock,\n social: SocialIconsBlock,\n menu: MenuBlock,\n table: TableBlock,\n spacer: SpacerBlock,\n html: HtmlBlock,\n custom: CustomBlock,\n};\n\nconst props = defineProps<{\n block: SectionBlockType;\n viewport: \"desktop\";\n}>();\n\ndefineOptions({ name: \"PreviewSectionBlock\" });\n\nconst blockRegistry = inject(BLOCK_REGISTRY_KEY, null);\n\nconst columnWidths = computed(() => {\n switch (props.block.columns) {\n case \"2\":\n return [\"50%\", \"50%\"];\n case \"3\":\n return [\"33.33%\", \"33.33%\", \"33.33%\"];\n case \"1-2\":\n return [\"33.33%\", \"66.67%\"];\n case \"2-1\":\n return [\"66.67%\", \"33.33%\"];\n default:\n return [\"100%\"];\n }\n});\n\nconst columns = computed(() => {\n const count = columnWidths.value.length;\n const children = [...props.block.children];\n while (children.length < count) {\n children.push([]);\n }\n return children.slice(0, count);\n});\n\nfunction getColumnBlocks(colIndex: number): Block[] {\n return columns.value[colIndex] || [];\n}\n\nfunction getBlockComponent(block: Block): Component | null {\n return resolveBlockComponent(block, blockRegistry, previewBlockComponentMap);\n}\n</script>\n\n<template>\n <div class=\"tpl:w-full\">\n <div class=\"tpl:flex tpl:gap-0\">\n <div\n v-for=\"(_, colIndex) in columns\"\n :key=\"colIndex\"\n :style=\"{ width: columnWidths[colIndex] }\"\n >\n <div\n v-for=\"childBlock in getColumnBlocks(colIndex)\"\n :key=\"childBlock.id\"\n :style=\"getBlockWrapperStyle(childBlock)\"\n >\n <!-- Recursive self-reference for nested sections -->\n <PreviewSectionBlock\n v-if=\"childBlock.type === 'section'\"\n :block=\"childBlock\"\n viewport=\"desktop\"\n />\n <component\n :is=\"getBlockComponent(childBlock)\"\n v-else\n :block=\"childBlock\"\n viewport=\"desktop\"\n />\n </div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ButtonBlock from \"../../components/blocks/ButtonBlock.vue\";\nimport CustomBlock from \"../../components/blocks/CustomBlock.vue\";\nimport DividerBlock from \"../../components/blocks/DividerBlock.vue\";\nimport HtmlBlock from \"../../components/blocks/HtmlBlock.vue\";\nimport ImageBlock from \"../../components/blocks/ImageBlock.vue\";\nimport MenuBlock from \"../../components/blocks/MenuBlock.vue\";\nimport PreviewSectionBlock from \"../../components/blocks/PreviewSectionBlock.vue\";\nimport SocialIconsBlock from \"../../components/blocks/SocialIconsBlock.vue\";\nimport SpacerBlock from \"../../components/blocks/SpacerBlock.vue\";\nimport TableBlock from \"../../components/blocks/TableBlock.vue\";\nimport TitleBlock from \"../../components/blocks/TitleBlock.vue\";\nimport ParagraphBlock from \"../../components/blocks/ParagraphBlock.vue\";\nimport VideoBlock from \"../../components/blocks/VideoBlock.vue\";\nimport { BLOCK_REGISTRY_KEY } from \"../../keys\";\nimport {\n resolveBlockComponent,\n getBlockWrapperStyle,\n} from \"../../utils/blockComponentResolver\";\nimport type { Block } from \"@templatical/types\";\nimport { inject, type Component } from \"vue\";\n\ndefineProps<{\n blocks: Block[];\n}>();\n\nconst blockRegistry = inject(BLOCK_REGISTRY_KEY);\n\nconst modulePreviewComponentMap: Record<string, Component> = {\n section: PreviewSectionBlock,\n title: TitleBlock,\n paragraph: ParagraphBlock,\n image: ImageBlock,\n video: VideoBlock,\n button: ButtonBlock,\n divider: DividerBlock,\n social: SocialIconsBlock,\n menu: MenuBlock,\n table: TableBlock,\n spacer: SpacerBlock,\n html: HtmlBlock,\n custom: CustomBlock,\n};\n\nfunction getBlockComponent(block: Block): Component | null {\n return resolveBlockComponent(block, blockRegistry, modulePreviewComponentMap);\n}\n</script>\n\n<template>\n <div\n class=\"tpl:pointer-events-none tpl:mx-auto tpl:w-[600px] tpl:select-none tpl:rounded-lg\"\n style=\"\n background-color: var(--tpl-canvas-bg);\n box-shadow: var(--tpl-shadow-sm);\n \"\n >\n <div\n v-for=\"block in blocks\"\n :key=\"block.id\"\n :style=\"getBlockWrapperStyle(block)\"\n >\n <component\n :is=\"getBlockComponent(block)\"\n :block=\"block\"\n viewport=\"desktop\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ButtonBlock from \"../../components/blocks/ButtonBlock.vue\";\nimport CustomBlock from \"../../components/blocks/CustomBlock.vue\";\nimport DividerBlock from \"../../components/blocks/DividerBlock.vue\";\nimport HtmlBlock from \"../../components/blocks/HtmlBlock.vue\";\nimport ImageBlock from \"../../components/blocks/ImageBlock.vue\";\nimport MenuBlock from \"../../components/blocks/MenuBlock.vue\";\nimport PreviewSectionBlock from \"../../components/blocks/PreviewSectionBlock.vue\";\nimport SocialIconsBlock from \"../../components/blocks/SocialIconsBlock.vue\";\nimport SpacerBlock from \"../../components/blocks/SpacerBlock.vue\";\nimport TableBlock from \"../../components/blocks/TableBlock.vue\";\nimport TitleBlock from \"../../components/blocks/TitleBlock.vue\";\nimport ParagraphBlock from \"../../components/blocks/ParagraphBlock.vue\";\nimport VideoBlock from \"../../components/blocks/VideoBlock.vue\";\nimport { BLOCK_REGISTRY_KEY } from \"../../keys\";\nimport {\n resolveBlockComponent,\n getBlockWrapperStyle,\n} from \"../../utils/blockComponentResolver\";\nimport type { Block } from \"@templatical/types\";\nimport { inject, type Component } from \"vue\";\n\ndefineProps<{\n blocks: Block[];\n}>();\n\nconst blockRegistry = inject(BLOCK_REGISTRY_KEY);\n\nconst modulePreviewComponentMap: Record<string, Component> = {\n section: PreviewSectionBlock,\n title: TitleBlock,\n paragraph: ParagraphBlock,\n image: ImageBlock,\n video: VideoBlock,\n button: ButtonBlock,\n divider: DividerBlock,\n social: SocialIconsBlock,\n menu: MenuBlock,\n table: TableBlock,\n spacer: SpacerBlock,\n html: HtmlBlock,\n custom: CustomBlock,\n};\n\nfunction getBlockComponent(block: Block): Component | null {\n return resolveBlockComponent(block, blockRegistry, modulePreviewComponentMap);\n}\n</script>\n\n<template>\n <div\n class=\"tpl:pointer-events-none tpl:mx-auto tpl:w-[600px] tpl:select-none tpl:rounded-lg\"\n style=\"\n background-color: var(--tpl-canvas-bg);\n box-shadow: var(--tpl-shadow-sm);\n \"\n >\n <div\n v-for=\"block in blocks\"\n :key=\"block.id\"\n :style=\"getBlockWrapperStyle(block)\"\n >\n <component\n :is=\"getBlockComponent(block)\"\n :block=\"block\"\n viewport=\"desktop\"\n />\n </div>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;EAwBA,IAAM,IAAsD;GAC1D,OAAO;GACP,WAAW;GACX,OAAO;GACP,OAAO;GACP,QAAQ;GACR,SAAS;GACT,QAAQ;GACR,MAAM;GACN,OAAO;GACP,QAAQ;GACR,MAAM;GACN,QAAQ;GACT,EAEK,IAAQ,GAOR,IAAgB,EAAO,GAAoB,KAAK,EAEhD,IAAe,QAAe;AAClC,WAAQ,EAAM,MAAM,SAApB;IACE,KAAK,IACH,QAAO,CAAC,OAAO,MAAM;IACvB,KAAK,IACH,QAAO;KAAC;KAAU;KAAU;KAAS;IACvC,KAAK,MACH,QAAO,CAAC,UAAU,SAAS;IAC7B,KAAK,MACH,QAAO,CAAC,UAAU,SAAS;IAC7B,QACE,QAAO,CAAC,OAAO;;IAEnB,EAEI,IAAU,QAAe;GAC7B,IAAM,IAAQ,EAAa,MAAM,QAC3B,IAAW,CAAC,GAAG,EAAM,MAAM,SAAS;AAC1C,UAAO,EAAS,SAAS,GACvB,GAAS,KAAK,EAAE,CAAC;AAEnB,UAAO,EAAS,MAAM,GAAG,EAAM;IAC/B;EAEF,SAAS,EAAgB,GAA2B;AAClD,UAAO,EAAQ,MAAM,MAAa,EAAE;;EAGtC,SAAS,EAAkB,GAAgC;AACzD,UAAO,EAAsB,GAAO,GAAe,EAAyB;;;;eAK5E,EA2BM,OA3BN,GA2BM,CA1BJ,EAyBM,OAzBN,GAyBM,EAAA,EAAA,GAAA,EAxBJ,EAuBM,GAAA,MAAA,EAtBoB,EAAA,QAAhB,GAAG,YADb,EAuBM,OAAA;IArBH,KAAK;IACL,OAAK,EAAA,EAAA,OAAW,EAAA,MAAa,IAAQ,CAAA;eAEtC,EAiBM,GAAA,MAAA,EAhBiB,EAAgB,EAAQ,GAAtC,YADT,EAiBM,OAAA;IAfH,KAAK,EAAW;IAChB,OAAK,EAAE,EAAA,EAAoB,CAAC,EAAU,CAAA;OAI/B,EAAW,SAAI,aAAA,GAAA,EADvB,EAIE,GAAA;;IAFC,OAAO;IACR,UAAS;mCAEX,EAKE,EAJK,EAAkB,EAAU,CAAA,EAAA;;IAEhC,OAAO;IACR,UAAS;;;;;;;;;;;;;;EE9ErB,IAAM,IAAgB,EAAO,EAAmB,EAE1C,IAAuD;GAC3D,SAAS;GACT,OAAO;GACP,WAAW;GACX,OAAO;GACP,OAAO;GACP,QAAQ;GACR,SAAS;GACT,QAAQ;GACR,MAAM;GACN,OAAO;GACP,QAAQ;GACR,MAAM;GACN,QAAQ;GACT;EAED,SAAS,EAAkB,GAAgC;AACzD,UAAO,EAAsB,GAAO,GAAe,EAA0B;;yBAK7E,EAkBM,OAlBN,GAkBM,EAAA,EAAA,GAAA,EAXJ,EAUM,GAAA,MAAA,EATY,EAAA,SAAT,YADT,EAUM,OAAA;GARH,KAAK,EAAM;GACX,OAAK,EAAE,EAAA,EAAoB,CAAC,EAAK,CAAA;YAElC,EAIE,EAHK,EAAkB,EAAK,CAAA,EAAA;GACpB;GACR,UAAS"}
|