@commonpub/editor 0.7.2 → 0.7.4
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@commonpub/editor",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "TipTap block editor with 18+ maker-focused extensions for CommonPub",
|
|
6
6
|
"license": "AGPL-3.0-or-later",
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
"remark-rehype": "^11.1.2",
|
|
51
51
|
"unified": "^11.0.5",
|
|
52
52
|
"zod": "^4.3.6",
|
|
53
|
-
"@commonpub/
|
|
54
|
-
"@commonpub/
|
|
53
|
+
"@commonpub/config": "0.9.0",
|
|
54
|
+
"@commonpub/schema": "0.9.4"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"vue": "^3.4.0",
|
|
@@ -167,6 +167,7 @@ function onSelectionChange(block: EditorBlock, hasSelection: boolean, rect: DOMR
|
|
|
167
167
|
};
|
|
168
168
|
} else {
|
|
169
169
|
floatingToolbar.value = { visible: false, top: 0, left: 0, blockId: '', activeMarks: { bold: false, italic: false, strike: false, code: false, link: false } };
|
|
170
|
+
linkPrompt.value = { visible: false, url: '' };
|
|
170
171
|
}
|
|
171
172
|
}
|
|
172
173
|
|
|
@@ -308,6 +309,7 @@ function onDrop(atIndex: number, event: DragEvent): void {
|
|
|
308
309
|
function onCanvasClick(): void {
|
|
309
310
|
props.blockEditor.selectBlock(null);
|
|
310
311
|
floatingToolbar.value = { visible: false, top: 0, left: 0, blockId: '', activeMarks: { bold: false, italic: false, strike: false, code: false, link: false } };
|
|
312
|
+
linkPrompt.value = { visible: false, url: '' };
|
|
311
313
|
}
|
|
312
314
|
|
|
313
315
|
// --- Resolve block component (injected overrides take priority) ---
|
|
@@ -24,6 +24,8 @@ const flatBlocks = computed(() => {
|
|
|
24
24
|
return props.groups.flatMap((g) => g.blocks);
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
+
const isSearching = computed(() => search.value.trim().length > 0);
|
|
28
|
+
|
|
27
29
|
const filteredBlocks = computed(() => {
|
|
28
30
|
const q = search.value.toLowerCase();
|
|
29
31
|
if (!q) return flatBlocks.value;
|
|
@@ -32,6 +34,13 @@ const filteredBlocks = computed(() => {
|
|
|
32
34
|
);
|
|
33
35
|
});
|
|
34
36
|
|
|
37
|
+
/** Flat index of a block across all groups (for keyboard nav) */
|
|
38
|
+
function globalIndex(groupIdx: number, blockIdx: number): number {
|
|
39
|
+
let idx = 0;
|
|
40
|
+
for (let g = 0; g < groupIdx; g++) idx += props.groups[g]!.blocks.length;
|
|
41
|
+
return idx + blockIdx;
|
|
42
|
+
}
|
|
43
|
+
|
|
35
44
|
watch(() => props.visible, (v) => {
|
|
36
45
|
if (v) {
|
|
37
46
|
search.value = '';
|
|
@@ -104,7 +113,29 @@ onUnmounted(() => {
|
|
|
104
113
|
/>
|
|
105
114
|
</div>
|
|
106
115
|
<div class="cpub-picker-body">
|
|
107
|
-
|
|
116
|
+
<!-- Grouped view (no search) -->
|
|
117
|
+
<template v-if="!isSearching && groups.length > 0">
|
|
118
|
+
<template v-for="(group, gi) in groups" :key="group.name">
|
|
119
|
+
<div class="cpub-picker-group-header">{{ group.name }}</div>
|
|
120
|
+
<button
|
|
121
|
+
v-for="(block, bi) in group.blocks"
|
|
122
|
+
:key="block.type + (block.attrs?.variant ?? '')"
|
|
123
|
+
:data-block="block.type"
|
|
124
|
+
class="cpub-picker-item"
|
|
125
|
+
:class="{ 'cpub-picker-item--active': globalIndex(gi, bi) === selectedIndex }"
|
|
126
|
+
@mouseenter="selectedIndex = globalIndex(gi, bi)"
|
|
127
|
+
@click="selectBlock(block)"
|
|
128
|
+
>
|
|
129
|
+
<span class="cpub-picker-icon"><i :class="['fa-solid', block.icon]"></i></span>
|
|
130
|
+
<span class="cpub-picker-text">
|
|
131
|
+
<span class="cpub-picker-label">{{ block.label }}</span>
|
|
132
|
+
<span v-if="block.description" class="cpub-picker-desc">{{ block.description }}</span>
|
|
133
|
+
</span>
|
|
134
|
+
</button>
|
|
135
|
+
</template>
|
|
136
|
+
</template>
|
|
137
|
+
<!-- Flat filtered view (searching) -->
|
|
138
|
+
<template v-else-if="filteredBlocks.length > 0">
|
|
108
139
|
<button
|
|
109
140
|
v-for="(block, i) in filteredBlocks"
|
|
110
141
|
:key="block.type + (block.attrs?.variant ?? '')"
|
|
@@ -179,6 +210,21 @@ onUnmounted(() => {
|
|
|
179
210
|
padding: 4px;
|
|
180
211
|
}
|
|
181
212
|
|
|
213
|
+
.cpub-picker-group-header {
|
|
214
|
+
font-family: var(--font-mono);
|
|
215
|
+
font-size: 9px;
|
|
216
|
+
font-weight: 700;
|
|
217
|
+
text-transform: uppercase;
|
|
218
|
+
letter-spacing: 0.08em;
|
|
219
|
+
color: var(--text-faint);
|
|
220
|
+
padding: 8px 10px 4px;
|
|
221
|
+
margin-top: 2px;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.cpub-picker-group-header:first-child {
|
|
225
|
+
margin-top: 0;
|
|
226
|
+
}
|
|
227
|
+
|
|
182
228
|
.cpub-picker-item {
|
|
183
229
|
display: flex;
|
|
184
230
|
align-items: center;
|
|
@@ -50,6 +50,15 @@ watch(() => props.selected, (sel) => {
|
|
|
50
50
|
function onDragStart(event: DragEvent): void {
|
|
51
51
|
event.dataTransfer?.setData('text/plain', props.block.id);
|
|
52
52
|
event.dataTransfer!.effectAllowed = 'move';
|
|
53
|
+
|
|
54
|
+
// Custom drag preview showing block type
|
|
55
|
+
const preview = document.createElement('div');
|
|
56
|
+
preview.textContent = props.block.type.replace(/_/g, ' ');
|
|
57
|
+
preview.style.cssText = 'position:fixed;top:-999px;left:-999px;padding:6px 12px;background:#1a1a1a;color:#eee;font-family:monospace;font-size:11px;font-weight:600;text-transform:capitalize;border:2px solid #333;pointer-events:none;white-space:nowrap;';
|
|
58
|
+
document.body.appendChild(preview);
|
|
59
|
+
event.dataTransfer!.setDragImage(preview, 0, 0);
|
|
60
|
+
requestAnimationFrame(() => document.body.removeChild(preview));
|
|
61
|
+
|
|
53
62
|
emit('drag-start', event);
|
|
54
63
|
}
|
|
55
64
|
|