@templatical/editor 0.19.0 → 0.20.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/{AiChatSidebar-DAQTFuvi.js → AiChatSidebar-ChrE6pUq.js} +7 -7
- package/dist/{AiFeatureMenu-aFSO20PR.js → AiFeatureMenu-DkQ9HhQ6.js} +4 -4
- package/dist/{BlockIssueBadge-BQmq1bxn.js → BlockIssueBadge-B4QjePax.js} +2 -2
- package/dist/{CloudEditor-D0K-m1GA.js → CloudEditor-CfX-aMiV.js} +15 -15
- package/dist/{CollaboratorBar-CJO3l6Dt.js → CollaboratorBar-DTibhQ1j.js} +2 -2
- package/dist/{ColorPicker-BsgpTJqK.js → ColorPicker-4vDYhdGe.js} +1 -1
- package/dist/{CommentsSidebar-BYMVucZh.js → CommentsSidebar-D-o5Stlg.js} +8 -8
- package/dist/{CountdownToolbar-PJI3ffi1.js → CountdownToolbar-D7yQqnA2.js} +2 -2
- package/dist/{DesignReferenceSidebar-QfAV8XT9.js → DesignReferenceSidebar-DnO6ZdwS.js} +5 -5
- package/dist/{IssuesPanel-CTjRgz8w.js → IssuesPanel-X_vkNGrs.js} +5 -5
- package/dist/{ModuleBrowserModal-BNalLx0k.js → ModuleBrowserModal-AJt--JPh.js} +6 -6
- package/dist/{ModulePreviewCanvas-BwObwisa.js → ModulePreviewCanvas-BKYOcVCh.js} +1 -1
- package/dist/{NumberWithSuffix-D3BKz66W.js → NumberWithSuffix-Bf3rNxza.js} +1 -1
- package/dist/{ParagraphEditor-Cf75UfSo.js → ParagraphEditor-C5ixOIYb.js} +7 -7
- package/dist/{RichTextEditorContent-MdaLz6Q3.js → RichTextEditorContent-DwhDnMsG.js} +3 -3
- package/dist/{SaveModuleDialog-MTjwiF9D.js → SaveModuleDialog-B4QX9eiv.js} +3 -3
- package/dist/{SnapshotHistory-C_Y1LzAY.js → SnapshotHistory-DaVueFv6.js} +5 -5
- package/dist/{TemplateScoringPanel-DikuKc6i.js → TemplateScoringPanel-B0UHoTLx.js} +10 -10
- package/dist/{TestEmailModal-BW8aTtzf.js → TestEmailModal-BkauxF7j.js} +3 -3
- package/dist/{TitleEditor-CCUkbRN0.js → TitleEditor-n6WDVDjW.js} +4 -4
- package/dist/{TplModal-DoOwUKlT.js → TplModal-BDDyvT-C.js} +1 -1
- package/dist/{blockTypeIcons-BlPKjD7M.js → blockTypeIcons-4D0aFXNg.js} +2 -2
- package/dist/bundle-stats.json +5 -5
- package/dist/cdn/chunks/{AiFeatureMenu-RwJiaPXZ.js → AiFeatureMenu-CIBHAGSz.js} +2 -2
- package/dist/cdn/chunks/{AiFeatureMenu-RwJiaPXZ.js.map → AiFeatureMenu-CIBHAGSz.js.map} +1 -1
- package/dist/cdn/chunks/{BlockIssueBadge-DM-TDKbr.js → BlockIssueBadge-CxMiLo3p.js} +4 -4
- package/dist/cdn/chunks/{BlockIssueBadge-DM-TDKbr.js.map → BlockIssueBadge-CxMiLo3p.js.map} +1 -1
- package/dist/cdn/chunks/{CloudEditor-Dufl354p.js → CloudEditor-CEa3Xgqh.js} +131 -131
- package/dist/cdn/chunks/{CloudEditor-Dufl354p.js.map → CloudEditor-CEa3Xgqh.js.map} +1 -1
- package/dist/cdn/chunks/{CollaboratorBar-xsU20iPl.js → CollaboratorBar-Bk1gD02G.js} +3 -3
- package/dist/cdn/chunks/{CollaboratorBar-xsU20iPl.js.map → CollaboratorBar-Bk1gD02G.js.map} +1 -1
- package/dist/cdn/chunks/{ColorPicker-y_obnw4o.js → ColorPicker-ll8pynws.js} +179 -179
- package/dist/cdn/chunks/ColorPicker-ll8pynws.js.map +1 -0
- package/dist/cdn/chunks/{CountdownBlock-sHsRMe1q.js → CountdownBlock-eGUE7hhw.js} +4 -4
- package/dist/cdn/chunks/{CountdownBlock-sHsRMe1q.js.map → CountdownBlock-eGUE7hhw.js.map} +1 -1
- package/dist/cdn/chunks/{CountdownToolbar-Ckc_jEi-.js → CountdownToolbar-C1bkSjUR.js} +4 -4
- package/dist/cdn/chunks/{CountdownToolbar-Ckc_jEi-.js.map → CountdownToolbar-C1bkSjUR.js.map} +1 -1
- package/dist/cdn/chunks/{IssuesPanel-DF5Ajuna.js → IssuesPanel-Csasylf_.js} +5 -5
- package/dist/cdn/chunks/{IssuesPanel-DF5Ajuna.js.map → IssuesPanel-Csasylf_.js.map} +1 -1
- package/dist/cdn/chunks/{ModuleBrowserModal-DG4AVFjH.js → ModuleBrowserModal-DznWWtBI.js} +7 -7
- package/dist/cdn/chunks/{ModuleBrowserModal-DG4AVFjH.js.map → ModuleBrowserModal-DznWWtBI.js.map} +1 -1
- package/dist/cdn/chunks/{ModulePreviewCanvas-Bc_PcJuc.js → ModulePreviewCanvas-D-h7fr87.js} +20 -20
- package/dist/cdn/chunks/{ModulePreviewCanvas-Bc_PcJuc.js.map → ModulePreviewCanvas-D-h7fr87.js.map} +1 -1
- package/dist/cdn/chunks/{NumberWithSuffix-8fOO-LC_.js → NumberWithSuffix-Dt-0odUN.js} +2 -2
- package/dist/cdn/chunks/{NumberWithSuffix-8fOO-LC_.js.map → NumberWithSuffix-Dt-0odUN.js.map} +1 -1
- package/dist/cdn/chunks/{ParagraphEditor-BkafKSju.js → ParagraphEditor-v0_XEL-v.js} +18 -18
- package/dist/cdn/chunks/{ParagraphEditor-BkafKSju.js.map → ParagraphEditor-v0_XEL-v.js.map} +1 -1
- package/dist/cdn/chunks/{RichTextEditorContent-BthhzOZS.js → RichTextEditorContent-NQExNsaV.js} +4 -4
- package/dist/cdn/chunks/{RichTextEditorContent-BthhzOZS.js.map → RichTextEditorContent-NQExNsaV.js.map} +1 -1
- package/dist/cdn/chunks/{SaveModuleDialog-DhsfYf2n.js → SaveModuleDialog-BpFhTi-k.js} +5 -5
- package/dist/cdn/chunks/{SaveModuleDialog-DhsfYf2n.js.map → SaveModuleDialog-BpFhTi-k.js.map} +1 -1
- package/dist/cdn/chunks/{TitleEditor-DLv9cXba.js → TitleEditor-CS-_MzYs.js} +8 -8
- package/dist/cdn/chunks/{TitleEditor-DLv9cXba.js.map → TitleEditor-CS-_MzYs.js.map} +1 -1
- package/dist/cdn/chunks/{blockTypeIcons-B0uibGX7.js → blockTypeIcons-B0Hjh42a.js} +3 -3
- package/dist/cdn/chunks/{blockTypeIcons-B0uibGX7.js.map → blockTypeIcons-B0Hjh42a.js.map} +1 -1
- package/dist/cdn/chunks/{extensions-DLHvZ0UH.js → extensions-Di3hdIRT.js} +22 -22
- package/dist/cdn/chunks/{extensions-DLHvZ0UH.js.map → extensions-Di3hdIRT.js.map} +1 -1
- package/dist/cdn/chunks/{features-Cnfv4QAY.js → features-CLIZ7Q-Y.js} +1490 -1447
- package/dist/cdn/chunks/features-CLIZ7Q-Y.js.map +1 -0
- package/dist/cdn/chunks/{icons-DbcM69bb.js → icons-GX40Qm2W.js} +2 -2
- package/dist/cdn/chunks/{icons-DbcM69bb.js.map → icons-GX40Qm2W.js.map} +1 -1
- package/dist/cdn/chunks/{media-library-DpSMwHd4.js → media-library-yYAkAagU.js} +80 -80
- package/dist/cdn/chunks/{media-library-DpSMwHd4.js.map → media-library-yYAkAagU.js.map} +1 -1
- package/dist/cdn/chunks/{pusher-CIwiKF8H.js → pusher-R0v5p99W.js} +27 -22
- package/dist/cdn/chunks/pusher-R0v5p99W.js.map +1 -0
- package/dist/cdn/chunks/quality-DffYv9j5.js +2180 -0
- package/dist/cdn/chunks/{quality-D9QqkRcU.js.map → quality-DffYv9j5.js.map} +1 -1
- package/dist/cdn/chunks/{renderer-BsLQZNXU.js → renderer-DpBGiXOg.js} +13 -13
- package/dist/cdn/chunks/{renderer-BsLQZNXU.js.map → renderer-DpBGiXOg.js.map} +1 -1
- package/dist/cdn/chunks/{src-zu1ehVNN.js → src-9K6uFiuK.js} +4 -4
- package/dist/cdn/chunks/{src-zu1ehVNN.js.map → src-9K6uFiuK.js.map} +1 -1
- package/dist/cdn/chunks/{styles-Dx-jXeCd.js → styles-DHSmjlpX.js} +837 -833
- package/dist/cdn/chunks/styles-DHSmjlpX.js.map +1 -0
- package/dist/cdn/editor.js +11 -11
- package/dist/{check-Cg6Mc2pP.js → check-Bl4J_tYV.js} +1 -1
- package/dist/{chevron-down-dClfxKrJ.js → chevron-down-COEcl2iP.js} +1 -1
- package/dist/{circle-alert-Bhzm-fNA.js → circle-alert-CvHi5etc.js} +1 -1
- package/dist/{clock-m7TwQw4b.js → clock-CZZw62e8.js} +1 -1
- package/dist/{createLucideIcon-FSF4peHQ.js → createLucideIcon-_peTZQtY.js} +2 -2
- package/dist/{image-up-CDEgJqwA.js → image-up-D26rNdfX.js} +1 -1
- package/dist/{info-BNP8yGsK.js → info-CE3JSTqk.js} +1 -1
- package/dist/{list-checks-jepi4iYr.js → list-checks-BQsLrtBB.js} +1 -1
- package/dist/{loader-circle-BT38qSm3.js → loader-circle-s9Rh5lLd.js} +1 -1
- package/dist/{message-circle-BAHcL9S-.js → message-circle-CI62d690.js} +1 -1
- package/dist/{refresh-cw-as5Bmg4X.js → refresh-cw-DlYfl65Y.js} +1 -1
- package/dist/{scan-line-IAMjPtRw.js → scan-line-CHadrYVb.js} +1 -1
- package/dist/{send-DvMay6bt.js → send-CbP40otC.js} +1 -1
- package/dist/{shield-check-YYDhGroE.js → shield-check-Bnj9aRoJ.js} +1 -1
- package/dist/{sparkles-CCwQMMMZ.js → sparkles-CncWT57a.js} +1 -1
- package/dist/{styles-64uLkPJV.js → styles-DkF_-uX8.js} +658 -654
- package/dist/templatical-editor.js +4 -4
- package/dist/{text-align-start-BwsA6QzC.js → text-align-start-B458vzYw.js} +1 -1
- package/dist/{trash-2-CRBDolnR.js → trash-2-DO0yjSU1.js} +1 -1
- package/dist/{triangle-alert-B2g0ALvq.js → triangle-alert-C18_ima6.js} +1 -1
- package/dist/{upload-y4gxiFp5.js → upload-DsCd2zTR.js} +1 -1
- package/dist/{useEditorCore-ea9OrrH3.js → useEditorCore-kKUemYY6.js} +910 -867
- package/dist/{x-Ctg_250T.js → x-BCFQuE_q.js} +1 -1
- package/package.json +10 -10
- package/dist/cdn/chunks/ColorPicker-y_obnw4o.js.map +0 -1
- package/dist/cdn/chunks/features-Cnfv4QAY.js.map +0 -1
- package/dist/cdn/chunks/pusher-CIwiKF8H.js.map +0 -1
- package/dist/cdn/chunks/quality-D9QqkRcU.js +0 -2180
- package/dist/cdn/chunks/styles-Dx-jXeCd.js.map +0 -1
package/dist/cdn/chunks/{ModuleBrowserModal-DG4AVFjH.js.map → ModuleBrowserModal-DznWWtBI.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ModuleBrowserModal-DG4AVFjH.js","names":[],"sources":["../../../src/cloud/components/ModuleBrowserModal.vue","../../../src/cloud/components/ModuleBrowserModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport TplModal from \"../../components/TplModal.vue\";\nimport { useI18n } from \"../../composables\";\nimport { useCloudI18nStrict } 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 { t: cloudT } = useCloudI18nStrict();\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: cloudT.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: cloudT.modules.insertAfterBlock.replace(\n \"{block}\",\n `${label} ${i + 1}`,\n ),\n });\n }\n options.push({ value: \"end\", label: cloudT.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 {{ cloudT.modules.browse }}\n </h3>\n <button\n :aria-label=\"cloudT.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=\"cloudT.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 cloudT.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=\"cloudT.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 {{ cloudT.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=\"cloudT.modules.delete\"\n :title=\"cloudT.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\n ? cloudT.modules.noModules\n : cloudT.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 {{ cloudT.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 {{ cloudT.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 {{ cloudT.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 {{ cloudT.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 \"../../components/TplModal.vue\";\nimport { useI18n } from \"../../composables\";\nimport { useCloudI18nStrict } 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 { t: cloudT } = useCloudI18nStrict();\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: cloudT.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: cloudT.modules.insertAfterBlock.replace(\n \"{block}\",\n `${label} ${i + 1}`,\n ),\n });\n }\n options.push({ value: \"end\", label: cloudT.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 {{ cloudT.modules.browse }}\n </h3>\n <button\n :aria-label=\"cloudT.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=\"cloudT.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 cloudT.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=\"cloudT.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 {{ cloudT.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=\"cloudT.modules.delete\"\n :title=\"cloudT.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\n ? cloudT.modules.noModules\n : cloudT.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 {{ cloudT.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 {{ cloudT.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 {{ cloudT.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 {{ cloudT.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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"}
|
|
1
|
+
{"version":3,"file":"ModuleBrowserModal-DznWWtBI.js","names":[],"sources":["../../../src/cloud/components/ModuleBrowserModal.vue","../../../src/cloud/components/ModuleBrowserModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport TplModal from \"../../components/TplModal.vue\";\nimport { useI18n } from \"../../composables\";\nimport { useCloudI18nStrict } 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 { t: cloudT } = useCloudI18nStrict();\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: cloudT.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: cloudT.modules.insertAfterBlock.replace(\n \"{block}\",\n `${label} ${i + 1}`,\n ),\n });\n }\n options.push({ value: \"end\", label: cloudT.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 {{ cloudT.modules.browse }}\n </h3>\n <button\n :aria-label=\"cloudT.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=\"cloudT.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 cloudT.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=\"cloudT.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 {{ cloudT.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=\"cloudT.modules.delete\"\n :title=\"cloudT.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\n ? cloudT.modules.noModules\n : cloudT.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 {{ cloudT.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 {{ cloudT.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 {{ cloudT.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 {{ cloudT.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 \"../../components/TplModal.vue\";\nimport { useI18n } from \"../../composables\";\nimport { useCloudI18nStrict } 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 { t: cloudT } = useCloudI18nStrict();\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: cloudT.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: cloudT.modules.insertAfterBlock.replace(\n \"{block}\",\n `${label} ${i + 1}`,\n ),\n });\n }\n options.push({ value: \"end\", label: cloudT.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 {{ cloudT.modules.browse }}\n </h3>\n <button\n :aria-label=\"cloudT.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=\"cloudT.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 cloudT.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=\"cloudT.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 {{ cloudT.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=\"cloudT.modules.delete\"\n :title=\"cloudT.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\n ? cloudT.modules.noModules\n : cloudT.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 {{ cloudT.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 {{ cloudT.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 {{ cloudT.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 {{ cloudT.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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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { I as e, L as t, M as n, P as r, T as i, f as a, g as o, it as s, m as c, p as l, st as u, u as d, x as f } from "./draggable-D840DIZB.js";
|
|
2
|
-
import { A as p, B as m, I as h, It as g, J as _,
|
|
2
|
+
import { A as p, B as m, I as h, It as g, J as _, Jt as v, M as y, N as b, R as x, U as S, W as C, Y as w, j as T, q as E, z as D } from "./features-CLIZ7Q-Y.js";
|
|
3
3
|
//#region src/components/blocks/PreviewSectionBlock.vue?vue&type=script&setup=true&lang.ts
|
|
4
4
|
var O = { class: "tpl:w-full" }, k = { class: "tpl:flex tpl:gap-0" }, A = /* @__PURE__ */ f({
|
|
5
5
|
name: "PreviewSectionBlock",
|
|
@@ -12,17 +12,17 @@ var O = { class: "tpl:w-full" }, k = { class: "tpl:flex tpl:gap-0" }, A = /* @__
|
|
|
12
12
|
let A = {
|
|
13
13
|
title: h,
|
|
14
14
|
paragraph: m,
|
|
15
|
-
image:
|
|
15
|
+
image: C,
|
|
16
16
|
video: p,
|
|
17
17
|
button: g,
|
|
18
18
|
divider: _,
|
|
19
|
-
social:
|
|
20
|
-
menu:
|
|
21
|
-
table:
|
|
22
|
-
spacer:
|
|
23
|
-
html:
|
|
24
|
-
custom:
|
|
25
|
-
}, j = f, M = i(
|
|
19
|
+
social: b,
|
|
20
|
+
menu: S,
|
|
21
|
+
table: T,
|
|
22
|
+
spacer: y,
|
|
23
|
+
html: E,
|
|
24
|
+
custom: w
|
|
25
|
+
}, j = f, M = i(v, null), N = a(() => {
|
|
26
26
|
switch (j.block.columns) {
|
|
27
27
|
case "2": return ["50%", "50%"];
|
|
28
28
|
case "3": return [
|
|
@@ -52,7 +52,7 @@ var O = { class: "tpl:w-full" }, k = { class: "tpl:flex tpl:gap-0" }, A = /* @__
|
|
|
52
52
|
style: u({ width: N.value[i] })
|
|
53
53
|
}, [(n(!0), o(d, null, r(F(i), (e) => (n(), o("div", {
|
|
54
54
|
key: e.id,
|
|
55
|
-
style: u(s(
|
|
55
|
+
style: u(s(x)(e))
|
|
56
56
|
}, [e.type === "section" ? (n(), c(f, {
|
|
57
57
|
key: 0,
|
|
58
58
|
block: e,
|
|
@@ -74,27 +74,27 @@ var O = { class: "tpl:w-full" }, k = { class: "tpl:flex tpl:gap-0" }, A = /* @__
|
|
|
74
74
|
__name: "ModulePreviewCanvas",
|
|
75
75
|
props: { blocks: {} },
|
|
76
76
|
setup(e) {
|
|
77
|
-
let a = i(
|
|
77
|
+
let a = i(v), l = {
|
|
78
78
|
section: A,
|
|
79
79
|
title: h,
|
|
80
80
|
paragraph: m,
|
|
81
|
-
image:
|
|
81
|
+
image: C,
|
|
82
82
|
video: p,
|
|
83
83
|
button: g,
|
|
84
84
|
divider: _,
|
|
85
|
-
social:
|
|
86
|
-
menu:
|
|
87
|
-
table:
|
|
88
|
-
spacer:
|
|
89
|
-
html:
|
|
90
|
-
custom:
|
|
85
|
+
social: b,
|
|
86
|
+
menu: S,
|
|
87
|
+
table: T,
|
|
88
|
+
spacer: y,
|
|
89
|
+
html: E,
|
|
90
|
+
custom: w
|
|
91
91
|
};
|
|
92
92
|
function f(e) {
|
|
93
93
|
return D(e, a, l);
|
|
94
94
|
}
|
|
95
95
|
return (i, a) => (n(), o("div", j, [(n(!0), o(d, null, r(e.blocks, (e) => (n(), o("div", {
|
|
96
96
|
key: e.id,
|
|
97
|
-
style: u(s(
|
|
97
|
+
style: u(s(x)(e))
|
|
98
98
|
}, [(n(), c(t(f(e)), {
|
|
99
99
|
block: e,
|
|
100
100
|
viewport: "desktop"
|
|
@@ -104,4 +104,4 @@ var O = { class: "tpl:w-full" }, k = { class: "tpl:flex tpl:gap-0" }, A = /* @__
|
|
|
104
104
|
//#endregion
|
|
105
105
|
export { M as default };
|
|
106
106
|
|
|
107
|
-
//# sourceMappingURL=ModulePreviewCanvas-
|
|
107
|
+
//# sourceMappingURL=ModulePreviewCanvas-D-h7fr87.js.map
|
package/dist/cdn/chunks/{ModulePreviewCanvas-Bc_PcJuc.js.map → ModulePreviewCanvas-D-h7fr87.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ModulePreviewCanvas-Bc_PcJuc.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;EACV,GAEM,IAAQ,GAOR,IAAgB,EAAO,GAAoB,IAAI,GAE/C,IAAe,QAAe;GAClC,QAAQ,EAAM,MAAM,SAApB;IACE,KAAK,KACH,OAAO,CAAC,OAAO,KAAK;IACtB,KAAK,KACH,OAAO;KAAC;KAAU;KAAU;IAAQ;IACtC,KAAK,OACH,OAAO,CAAC,UAAU,QAAQ;IAC5B,KAAK,OACH,OAAO,CAAC,UAAU,QAAQ;IAC5B,SACE,OAAO,CAAC,MAAM;GAClB;EACF,CAAC,GAEK,IAAU,QAAe;GAC7B,IAAM,IAAQ,EAAa,MAAM,QAC3B,IAAW,CAAC,GAAG,EAAM,MAAM,QAAQ;GACzC,OAAO,EAAS,SAAS,IACvB,EAAS,KAAK,CAAC,CAAC;GAElB,OAAO,EAAS,MAAM,GAAG,CAAK;EAChC,CAAC;EAED,SAAS,EAAgB,GAA2B;GAClD,OAAO,EAAQ,MAAM,MAAa,CAAC;EACrC;EAEA,SAAS,EAAkB,GAAgC;GACzD,OAAO,EAAsB,GAAO,GAAe,CAAwB;EAC7E;;;eAIE,EA2BM,OA3BN,GA2BM,CA1BJ,EAyBM,OAzBN,GAyBM,EAAA,EAAA,EAAA,GAxBJ,EAuBM,GAAA,MAAA,EAtBoB,EAAA,QAAhB,GAAG,YADb,EAuBM,OAAA;IArBH,KAAK;IACL,OAAK,EAAA,EAAA,OAAW,EAAA,MAAa,GAAQ,CAAA;eAEtC,EAiBM,GAAA,MAAA,EAhBiB,EAAgB,CAAQ,IAAtC,YADT,EAiBM,OAAA;IAfH,KAAK,EAAW;IAChB,OAAK,EAAE,EAAA,CAAA,CAAoB,CAAC,CAAU,CAAA;OAI/B,EAAW,SAAI,aAAA,EAAA,GADvB,EAIE,GAAA;;IAFC,OAAO;IACR,UAAS;mCAEX,EAKE,EAJK,EAAkB,CAAU,CAAA,GAAA;;IAEhC,OAAO;IACR,UAAS;;;;;;;;;;;;;;EE9ErB,IAAM,IAAgB,EAAO,CAAkB,GAEzC,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;EACV;EAEA,SAAS,EAAkB,GAAgC;GACzD,OAAO,EAAsB,GAAO,GAAe,CAAyB;EAC9E;yBAIE,EAkBM,OAlBN,GAkBM,EAAA,EAAA,EAAA,GAXJ,EAUM,GAAA,MAAA,EATY,EAAA,SAAT,YADT,EAUM,OAAA;GARH,KAAK,EAAM;GACX,OAAK,EAAE,EAAA,CAAA,CAAoB,CAAC,CAAK,CAAA;YAElC,EAIE,EAHK,EAAkB,CAAK,CAAA,GAAA;GACpB;GACR,UAAS"}
|
|
1
|
+
{"version":3,"file":"ModulePreviewCanvas-D-h7fr87.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;EACV,GAEM,IAAQ,GAOR,IAAgB,EAAO,GAAoB,IAAI,GAE/C,IAAe,QAAe;GAClC,QAAQ,EAAM,MAAM,SAApB;IACE,KAAK,KACH,OAAO,CAAC,OAAO,KAAK;IACtB,KAAK,KACH,OAAO;KAAC;KAAU;KAAU;IAAQ;IACtC,KAAK,OACH,OAAO,CAAC,UAAU,QAAQ;IAC5B,KAAK,OACH,OAAO,CAAC,UAAU,QAAQ;IAC5B,SACE,OAAO,CAAC,MAAM;GAClB;EACF,CAAC,GAEK,IAAU,QAAe;GAC7B,IAAM,IAAQ,EAAa,MAAM,QAC3B,IAAW,CAAC,GAAG,EAAM,MAAM,QAAQ;GACzC,OAAO,EAAS,SAAS,IACvB,EAAS,KAAK,CAAC,CAAC;GAElB,OAAO,EAAS,MAAM,GAAG,CAAK;EAChC,CAAC;EAED,SAAS,EAAgB,GAA2B;GAClD,OAAO,EAAQ,MAAM,MAAa,CAAC;EACrC;EAEA,SAAS,EAAkB,GAAgC;GACzD,OAAO,EAAsB,GAAO,GAAe,CAAwB;EAC7E;;;eAIE,EA2BM,OA3BN,GA2BM,CA1BJ,EAyBM,OAzBN,GAyBM,EAAA,EAAA,EAAA,GAxBJ,EAuBM,GAAA,MAAA,EAtBoB,EAAA,QAAhB,GAAG,YADb,EAuBM,OAAA;IArBH,KAAK;IACL,OAAK,EAAA,EAAA,OAAW,EAAA,MAAa,GAAQ,CAAA;eAEtC,EAiBM,GAAA,MAAA,EAhBiB,EAAgB,CAAQ,IAAtC,YADT,EAiBM,OAAA;IAfH,KAAK,EAAW;IAChB,OAAK,EAAE,EAAA,CAAA,CAAoB,CAAC,CAAU,CAAA;OAI/B,EAAW,SAAI,aAAA,EAAA,GADvB,EAIE,GAAA;;IAFC,OAAO;IACR,UAAS;mCAEX,EAKE,EAJK,EAAkB,CAAU,CAAA,GAAA;;IAEhC,OAAO;IACR,UAAS;;;;;;;;;;;;;;EE9ErB,IAAM,IAAgB,EAAO,CAAkB,GAEzC,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;EACV;EAEA,SAAS,EAAkB,GAAgC;GACzD,OAAO,EAAsB,GAAO,GAAe,CAAyB;EAC9E;yBAIE,EAkBM,OAlBN,GAkBM,EAAA,EAAA,EAAA,GAXJ,EAUM,GAAA,MAAA,EATY,EAAA,SAAT,YADT,EAUM,OAAA;GARH,KAAK,EAAM;GACX,OAAK,EAAE,EAAA,CAAA,CAAoB,CAAC,CAAK,CAAA;YAElC,EAIE,EAHK,EAAkB,CAAK,CAAA,GAAA;GACpB;GACR,UAAS"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { F as e, L as t, M as n, P as r, ct as i, f as a, g as o, h as s, it as c, m as l, ot as u, p as d, st as f, u as p, x as m } from "./draggable-D840DIZB.js";
|
|
2
|
-
import { h, m as g, p as _ } from "./ColorPicker-
|
|
2
|
+
import { h, m as g, p as _ } from "./ColorPicker-ll8pynws.js";
|
|
3
3
|
//#region src/components/SlidingPillSelect.vue?vue&type=script&setup=true&lang.ts
|
|
4
4
|
var v = [
|
|
5
5
|
"aria-checked",
|
|
@@ -88,4 +88,4 @@ var v = [
|
|
|
88
88
|
//#endregion
|
|
89
89
|
export { S as n, b as r, T as t };
|
|
90
90
|
|
|
91
|
-
//# sourceMappingURL=NumberWithSuffix-
|
|
91
|
+
//# sourceMappingURL=NumberWithSuffix-Dt-0odUN.js.map
|
package/dist/cdn/chunks/{NumberWithSuffix-8fOO-LC_.js.map → NumberWithSuffix-Dt-0odUN.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NumberWithSuffix-
|
|
1
|
+
{"version":3,"file":"NumberWithSuffix-Dt-0odUN.js","names":[],"sources":["../../../src/components/SlidingPillSelect.vue","../../../src/components/SlidingPillSelect.vue","../../../src/components/toolbar/FieldRow.vue","../../../src/components/toolbar/FieldRow.vue","../../../src/components/toolbar/NumberWithSuffix.vue","../../../src/components/toolbar/NumberWithSuffix.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Component } from \"vue\";\nimport { computed } from \"vue\";\n\nexport interface PillOption {\n value: string;\n label: string;\n icon?: Component;\n}\n\nconst props = defineProps<{\n options: PillOption[];\n modelValue: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst selectedIndex = computed(() =>\n props.options.findIndex((o) => o.value === props.modelValue),\n);\n\nconst pillOffset = computed(\n () => `translateX(${Math.max(selectedIndex.value, 0) * 100}%)`,\n);\n</script>\n\n<template>\n <div\n role=\"radiogroup\"\n class=\"tpl:relative tpl:grid tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1\"\n :style=\"{\n gridTemplateColumns: `repeat(${options.length}, 1fr)`,\n backgroundColor: 'var(--tpl-bg-hover)',\n }\"\n >\n <!-- Sliding pill — hidden when modelValue matches no option,\n otherwise it would silently sit on the first option and create\n a visual mismatch with aria-checked. -->\n <div\n v-if=\"selectedIndex >= 0\"\n class=\"tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n left: '4px',\n width: `calc((100% - 8px) / ${options.length})`,\n transform: pillOffset,\n backgroundColor: 'var(--tpl-bg)',\n boxShadow: 'var(--tpl-shadow)',\n transition: 'transform 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n ></div>\n\n <button\n v-for=\"option in options\"\n :key=\"option.value\"\n role=\"radio\"\n :aria-checked=\"modelValue === option.value\"\n :aria-label=\"option.label\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:py-[5px] tpl:text-xs tpl:font-medium\"\n :style=\"{\n color:\n modelValue === option.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n transition: 'color 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n :title=\"option.label\"\n @click=\"emit('update:modelValue', option.value)\"\n >\n <component\n :is=\"option.icon\"\n v-if=\"option.icon\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span v-else>{{ option.label }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport type { Component } from \"vue\";\nimport { computed } from \"vue\";\n\nexport interface PillOption {\n value: string;\n label: string;\n icon?: Component;\n}\n\nconst props = defineProps<{\n options: PillOption[];\n modelValue: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst selectedIndex = computed(() =>\n props.options.findIndex((o) => o.value === props.modelValue),\n);\n\nconst pillOffset = computed(\n () => `translateX(${Math.max(selectedIndex.value, 0) * 100}%)`,\n);\n</script>\n\n<template>\n <div\n role=\"radiogroup\"\n class=\"tpl:relative tpl:grid tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1\"\n :style=\"{\n gridTemplateColumns: `repeat(${options.length}, 1fr)`,\n backgroundColor: 'var(--tpl-bg-hover)',\n }\"\n >\n <!-- Sliding pill — hidden when modelValue matches no option,\n otherwise it would silently sit on the first option and create\n a visual mismatch with aria-checked. -->\n <div\n v-if=\"selectedIndex >= 0\"\n class=\"tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n left: '4px',\n width: `calc((100% - 8px) / ${options.length})`,\n transform: pillOffset,\n backgroundColor: 'var(--tpl-bg)',\n boxShadow: 'var(--tpl-shadow)',\n transition: 'transform 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n ></div>\n\n <button\n v-for=\"option in options\"\n :key=\"option.value\"\n role=\"radio\"\n :aria-checked=\"modelValue === option.value\"\n :aria-label=\"option.label\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:py-[5px] tpl:text-xs tpl:font-medium\"\n :style=\"{\n color:\n modelValue === option.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n transition: 'color 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n :title=\"option.label\"\n @click=\"emit('update:modelValue', option.value)\"\n >\n <component\n :is=\"option.icon\"\n v-if=\"option.icon\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span v-else>{{ option.label }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { labelClass } from \"../../constants/styleConstants\";\n\ndefineProps<{\n label?: string;\n}>();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label v-if=\"label\" :class=\"labelClass\">{{ label }}</label>\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { labelClass } from \"../../constants/styleConstants\";\n\ndefineProps<{\n label?: string;\n}>();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label v-if=\"label\" :class=\"labelClass\">{{ label }}</label>\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n} from \"../../constants/styleConstants\";\n\ndefineProps<{\n modelValue: number;\n min?: number;\n max?: number;\n suffix: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: number): void;\n}>();\n</script>\n\n<template>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"modelValue\"\n :min=\"min\"\n :max=\"max\"\n @input=\"\n emit(\n 'update:modelValue',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">{{ suffix }}</span>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n} from \"../../constants/styleConstants\";\n\ndefineProps<{\n modelValue: number;\n min?: number;\n max?: number;\n suffix: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: number): void;\n}>();\n</script>\n\n<template>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"modelValue\"\n :min=\"min\"\n :max=\"max\"\n @input=\"\n emit(\n 'update:modelValue',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">{{ suffix }}</span>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;EAUA,IAAM,IAAQ,GAKR,IAAO,GAIP,IAAgB,QACpB,EAAM,QAAQ,WAAW,MAAM,EAAE,UAAU,EAAM,UAAU,CAC7D,GAEM,IAAa,QACX,cAAc,KAAK,IAAI,EAAc,OAAO,CAAC,IAAI,IAAI,GAC7D;yBAIE,EAiDM,OAAA;GAhDJ,MAAK;GACL,OAAM;GACL,OAAK,EAAA;mCAAyC,EAAA,QAAQ,OAAM;;;MASrD,EAAA,SAAa,KAAA,EAAA,GADrB,EAWO,OAAA;;GATL,OAAM;GACL,OAAK,EAAA;;kCAA+D,EAAA,QAAQ,OAAM;eAAwB,EAAA;;;;;oCAU7G,EAwBS,GAAA,MAAA,EAvBU,EAAA,UAAV,YADT,EAwBS,UAAA;GAtBN,KAAK,EAAO;GACb,MAAK;GACJ,gBAAc,EAAA,eAAe,EAAO;GACpC,cAAY,EAAO;GACpB,OAAM;GACL,OAAK,EAAA;WAA6B,EAAA,eAAe,EAAO,QAAA,uBAAA;;;GAOxD,OAAO,EAAO;GACd,UAAK,MAAE,EAAI,qBAAsB,EAAO,KAAK;MAItC,EAAO,QAAA,EAAA,GAFf,EAKE,EAJK,EAAO,IAAI,GAAA;;GAEf,MAAM;GACN,gBAAc;cAEjB,EAAsC,QAAA,GAAA,EAAtB,EAAO,KAAK,GAAA,CAAA,EAAA,GAAA,IAAA,CAAA;;;;;;yBEnEhC,EAGM,OAHN,GAGM,CAFS,EAAA,SAAA,EAAA,GAAb,EAA2D,SAAA;;GAAtC,OAAK,EAAE,EAAA,CAAA,CAAU;OAAK,EAAA,KAAK,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA,GAChD,EAAQ,EAAA,QAAA,SAAA,CAAA,CAAA;;;;;;;;;;;;;;;;EEEZ,IAAM,IAAO;yBAMX,EAeM,OAfN,GAeM,CAdJ,EAYE,SAAA;GAXA,MAAK;GACJ,OAAK,EAAE,EAAA,CAAA,CAAoB;GAC3B,OAAO,EAAA;GACP,KAAK,EAAA;GACL,KAAK,EAAA;GACL,SAAK,AAAA,EAAA,QAAA,MAAW,EAAA,qBAA+C,OAAQ,EAAO,OAA4B,KAAK,CAAA;mBAOlH,EAAmD,QAAA,EAA5C,OAAK,EAAE,EAAA,CAAA,CAAgB,EAAA,GAAA,EAAK,EAAA,MAAM,GAAA,CAAA,CAAA,CAAA"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { K as e, L as t, M as n, P as r, T as i, Z as a, c as o, ct as s, d as c, f as l, g as u, h as d, it as f, l as p, m, ot as h, p as g, st as _, u as v, v as y, x as b, y as x } from "./draggable-D840DIZB.js";
|
|
2
|
-
import { H as S,
|
|
3
|
-
import { B as ie, C as ae, H as oe, L as se, R as ce, X as le, Z as ue, b as de, c as fe, d as pe, f as me, g as P, i as he, l as ge, o as _e, s as ve } from "./icons-
|
|
4
|
-
import { c as ye, o as be, r as xe, s as Se, t as F } from "./ColorPicker-
|
|
5
|
-
import { n as I, t as L } from "./RichTextEditorContent-
|
|
2
|
+
import { H as S, Q as C, St as w, Ut as T, Z as E, _n as D, an as O, gn as k, hn as A, in as ee, nt as j, on as te, ot as M, pn as ne, rn as re, st as N } from "./features-CLIZ7Q-Y.js";
|
|
3
|
+
import { B as ie, C as ae, H as oe, L as se, R as ce, X as le, Z as ue, b as de, c as fe, d as pe, f as me, g as P, i as he, l as ge, o as _e, s as ve } from "./icons-GX40Qm2W.js";
|
|
4
|
+
import { c as ye, o as be, r as xe, s as Se, t as F } from "./ColorPicker-ll8pynws.js";
|
|
5
|
+
import { n as I, t as L } from "./RichTextEditorContent-NQExNsaV.js";
|
|
6
6
|
//#region src/components/blocks/EmojiPickerDropdown.vue?vue&type=script&setup=true&lang.ts
|
|
7
7
|
var R = [
|
|
8
8
|
"aria-label",
|
|
@@ -12,8 +12,8 @@ var R = [
|
|
|
12
12
|
__name: "EmojiPickerDropdown",
|
|
13
13
|
emits: ["insert"],
|
|
14
14
|
setup(e, { emit: t }) {
|
|
15
|
-
let i = t, { categories: c, isOpen: m, toggle: _, close: y } = j(), { t: b, format: S } =
|
|
16
|
-
M(
|
|
15
|
+
let i = t, { categories: c, isOpen: m, toggle: _, close: y } = j(), { t: b, format: S } = T(), C = a(null), w = a(null);
|
|
16
|
+
M(C, l(() => m.value)), D(w, () => {
|
|
17
17
|
m.value && y();
|
|
18
18
|
});
|
|
19
19
|
function E(e) {
|
|
@@ -21,7 +21,7 @@ var R = [
|
|
|
21
21
|
}
|
|
22
22
|
return (e, t) => (n(), u("div", {
|
|
23
23
|
ref_key: "rootRef",
|
|
24
|
-
ref:
|
|
24
|
+
ref: w,
|
|
25
25
|
class: "tpl:relative"
|
|
26
26
|
}, [g("button", {
|
|
27
27
|
type: "button",
|
|
@@ -39,7 +39,7 @@ var R = [
|
|
|
39
39
|
key: 0,
|
|
40
40
|
id: "tpl-emoji-picker",
|
|
41
41
|
ref_key: "pickerRef",
|
|
42
|
-
ref:
|
|
42
|
+
ref: C,
|
|
43
43
|
role: "dialog",
|
|
44
44
|
"aria-modal": "false",
|
|
45
45
|
"aria-label": f(b).paragraphEditor.insertEmoji,
|
|
@@ -151,7 +151,7 @@ var $ = ["data-tpl-theme", "aria-label"], Te = { class: "tpl:flex tpl:items-cent
|
|
|
151
151
|
"add-logic-tag"
|
|
152
152
|
],
|
|
153
153
|
setup(e, { emit: t }) {
|
|
154
|
-
let r = e, a = t, o = i(
|
|
154
|
+
let r = e, a = t, o = i(ne, null), p = i(A, null), h = k(ee, "ParagraphToolbar"), b = S(), C = i(te, null), E = i(O, null), D = i(re, null), j = l(() => (C?.isOpen.value ?? !1) || (E?.isOpen.value ?? !1)), { t: M } = T(), N = h.fonts;
|
|
155
155
|
function P(e) {
|
|
156
156
|
r.editor?.chain().focus().insertContent(e).run();
|
|
157
157
|
}
|
|
@@ -177,7 +177,7 @@ var $ = ["data-tpl-theme", "aria-label"], Te = { class: "tpl:flex tpl:items-cent
|
|
|
177
177
|
t && (z() ? (t.extendMarkRange("link").updateAttributes("link", { color: e || null }), e && t.unsetColor(), t.run()) : e ? t.setColor(e).updateAttributes("link", { color: e }).run() : t.unsetColor().run());
|
|
178
178
|
}
|
|
179
179
|
function H() {
|
|
180
|
-
let e =
|
|
180
|
+
let e = D?.content.value.settings;
|
|
181
181
|
return z() ? Q(B(), e?.linkColor || e?.textColor) : Q(I("color"), e?.textColor);
|
|
182
182
|
}
|
|
183
183
|
function U() {
|
|
@@ -450,7 +450,7 @@ var $ = ["data-tpl-theme", "aria-label"], Te = { class: "tpl:flex tpl:items-cent
|
|
|
450
450
|
size: 16,
|
|
451
451
|
"stroke-width": 2
|
|
452
452
|
}), y(" " + s(f(M).logicTag.insertShort), 1)], 8, Oe)], 64)) : d("", !0)
|
|
453
|
-
])], 64)) : (n(), u("div", ke, [x(f(
|
|
453
|
+
])], 64)) : (n(), u("div", ke, [x(f(w), {
|
|
454
454
|
class: "tpl-spinner",
|
|
455
455
|
size: 14,
|
|
456
456
|
"stroke-width": 2
|
|
@@ -464,13 +464,13 @@ var $ = ["data-tpl-theme", "aria-label"], Te = { class: "tpl:flex tpl:items-cent
|
|
|
464
464
|
},
|
|
465
465
|
emits: ["done"],
|
|
466
466
|
setup(t, { emit: r }) {
|
|
467
|
-
let i = t, a = r, o = S(), { editor: s, EditorContent: c, isLoading: l, initError: d, retry: p, showLinkDialog: m, linkUrl: h, linkColor: g, linkDialogRef: _, canRequestMergeTag: v, canInsertLogicTag: y, openLinkDialog: b, insertLink:
|
|
467
|
+
let i = t, a = r, o = S(), { editor: s, EditorContent: c, isLoading: l, initError: d, retry: p, showLinkDialog: m, linkUrl: h, linkColor: g, linkDialogRef: _, canRequestMergeTag: v, canInsertLogicTag: y, openLinkDialog: b, insertLink: w, removeLink: T, closeLinkDialog: D, handleLinkKeydown: O, handleAddMergeTag: k, handleAddLogicTag: A } = E({
|
|
468
468
|
blockId: () => i.block.id,
|
|
469
469
|
blockContent: () => i.block.content,
|
|
470
470
|
onDone: () => a("done"),
|
|
471
471
|
editorName: "ParagraphEditor",
|
|
472
472
|
async loadExtensions({ mergeTags: e, syntax: t, triggerChar: n, autocompleteEnabled: r, suggestionEmptyText: i }) {
|
|
473
|
-
let [{ Editor: a, EditorContent: s }, { default: c }, { default: l }, { default: u }, { default: d }, { default: f }, { default: p }, { TextStyle: m }, { default: h }, { default: g }, { default: _ }, { MergeTagNode: v, MergeTagSuggestion: y, LogicMergeTagNode: b, FontSize: x, LineHeight: S, LetterSpacing:
|
|
473
|
+
let [{ Editor: a, EditorContent: s }, { default: c }, { default: l }, { default: u }, { default: d }, { default: f }, { default: p }, { TextStyle: m }, { default: h }, { default: g }, { default: _ }, { MergeTagNode: v, MergeTagSuggestion: y, LogicMergeTagNode: b, FontSize: x, LineHeight: S, LetterSpacing: w }] = await Promise.all([
|
|
474
474
|
import("./tiptap-C2indasq.js").then((e) => e.r),
|
|
475
475
|
import("./tiptap-C2indasq.js").then((e) => e.i),
|
|
476
476
|
import("./tiptap-C2indasq.js").then((e) => e.l),
|
|
@@ -482,7 +482,7 @@ var $ = ["data-tpl-theme", "aria-label"], Te = { class: "tpl:flex tpl:items-cent
|
|
|
482
482
|
import("./tiptap-C2indasq.js").then((e) => e.f),
|
|
483
483
|
import("./tiptap-C2indasq.js").then((e) => e.d),
|
|
484
484
|
import("./tiptap-C2indasq.js").then((e) => e.u),
|
|
485
|
-
import("./extensions-
|
|
485
|
+
import("./extensions-Di3hdIRT.js")
|
|
486
486
|
]);
|
|
487
487
|
return {
|
|
488
488
|
TiptapEditor: a,
|
|
@@ -499,7 +499,7 @@ var $ = ["data-tpl-theme", "aria-label"], Te = { class: "tpl:flex tpl:items-cent
|
|
|
499
499
|
u,
|
|
500
500
|
d,
|
|
501
501
|
f,
|
|
502
|
-
|
|
502
|
+
C(l).configure({
|
|
503
503
|
openOnClick: !1,
|
|
504
504
|
HTMLAttributes: {
|
|
505
505
|
target: "_blank",
|
|
@@ -513,7 +513,7 @@ var $ = ["data-tpl-theme", "aria-label"], Te = { class: "tpl:flex tpl:items-cent
|
|
|
513
513
|
_.configure({ multicolor: !0 }),
|
|
514
514
|
x,
|
|
515
515
|
S,
|
|
516
|
-
|
|
516
|
+
w,
|
|
517
517
|
v.configure({
|
|
518
518
|
mergeTags: e,
|
|
519
519
|
syntax: t
|
|
@@ -572,7 +572,7 @@ var $ = ["data-tpl-theme", "aria-label"], Te = { class: "tpl:flex tpl:items-cent
|
|
|
572
572
|
"link-color": f(g),
|
|
573
573
|
"onUpdate:linkColor": i[2] ||= (t) => e(g) ? g.value = t : null,
|
|
574
574
|
onClose: f(D),
|
|
575
|
-
onInsert: f(
|
|
575
|
+
onInsert: f(w),
|
|
576
576
|
onRemove: f(T),
|
|
577
577
|
onKeydown: f(O)
|
|
578
578
|
}, null, 8, [
|
|
@@ -592,4 +592,4 @@ var $ = ["data-tpl-theme", "aria-label"], Te = { class: "tpl:flex tpl:items-cent
|
|
|
592
592
|
//#endregion
|
|
593
593
|
export { Me as default };
|
|
594
594
|
|
|
595
|
-
//# sourceMappingURL=ParagraphEditor-
|
|
595
|
+
//# sourceMappingURL=ParagraphEditor-v0_XEL-v.js.map
|