@commonpub/editor 0.7.3 → 0.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@commonpub/editor",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.5",
|
|
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",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"unified": "^11.0.5",
|
|
52
52
|
"zod": "^4.3.6",
|
|
53
53
|
"@commonpub/config": "0.9.0",
|
|
54
|
-
"@commonpub/schema": "0.9.
|
|
54
|
+
"@commonpub/schema": "0.9.5"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"vue": "^3.4.0",
|
|
@@ -394,6 +394,13 @@ function onKeydown(event: KeyboardEvent): void {
|
|
|
394
394
|
|
|
395
395
|
onMounted(() => { document.addEventListener('keydown', onKeydown); });
|
|
396
396
|
onUnmounted(() => { document.removeEventListener('keydown', onKeydown); });
|
|
397
|
+
|
|
398
|
+
/** Expose formatting commands for external toolbar integration */
|
|
399
|
+
defineExpose({
|
|
400
|
+
toggleMark,
|
|
401
|
+
toggleLink,
|
|
402
|
+
getActiveMarks: () => floatingToolbar.value.activeMarks,
|
|
403
|
+
});
|
|
397
404
|
</script>
|
|
398
405
|
|
|
399
406
|
<template>
|
|
@@ -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
|
|