feat(writekit): rename @robonen/editor to @robonen/writekit

Rename the rich-text editor package and all Editor* exports to Writekit*;
remove the old vue/editor tree.
This commit is contained in:
2026-06-15 16:54:06 +07:00
parent 55e78786d6
commit 263c32002f
149 changed files with 1563 additions and 1748 deletions
@@ -0,0 +1,88 @@
<script setup lang="ts">
import { onBeforeUnmount, ref } from 'vue';
import { DismissableLayer, PopperContent, PopperRoot, Portal } from '@robonen/primitives';
import { isCollapsed } from '../../model';
import { isMarkActive, toggleMark } from '../../commands';
import { useWritekitContext } from '../context';
import { useEventListener } from '../composables';
export interface WritekitBubbleMenuProps {
/** Marks shown in the default toolbar (ignored when the default slot is used). */
marks?: string[];
}
const { marks = ['bold', 'italic', 'underline', 'strike', 'code'] } = defineProps<WritekitBubbleMenuProps>();
const ctx = useWritekitContext();
// Virtual reference (a `Measurable`) anchored to the selection rect — Popper
// positions against it with no trigger element. Reassigned on every refresh so
// PopperContent re-resolves position as the selection moves.
const reference = ref<{ getBoundingClientRect: () => DOMRect } | undefined>();
const open = ref(false);
const rev = ref(0);
function selectionRect(): DOMRect | null {
const selection = globalThis.window === undefined ? null : globalThis.getSelection();
if (!selection || selection.rangeCount === 0)
return null;
const rect = selection.getRangeAt(0).getBoundingClientRect();
return rect.width || rect.height ? rect : null;
}
function refresh(): void {
rev.value += 1;
const sel = ctx.writekit.state.selection;
const rect = selectionRect();
open.value = sel.kind === 'text' && !isCollapsed(sel) && !ctx.composing.value && rect !== null;
if (open.value)
reference.value = { getBoundingClientRect: () => selectionRect() ?? new DOMRect() };
}
ctx.writekit.on('transaction', refresh);
useEventListener(() => (typeof document === 'undefined' ? undefined : document), 'selectionchange', refresh);
onBeforeUnmount(() => ctx.writekit.off('transaction', refresh));
function active(type: string): boolean {
return Boolean(rev.value >= 0 && isMarkActive(ctx.writekit.state, type));
}
function toggle(type: string): void {
ctx.writekit.command(toggleMark(type));
}
</script>
<template>
<Portal to="body">
<PopperRoot>
<PopperContent
v-if="open && reference"
:reference="reference"
side="top"
:side-offset="8"
:collision-padding="8"
>
<DismissableLayer
class="writekit-bubble-menu"
role="toolbar"
data-writekit-bubble-menu=""
@dismiss="open = false"
>
<slot :active="active" :toggle="toggle" :writekit="ctx.writekit">
<button
v-for="mark in marks"
:key="mark"
type="button"
:data-mark="mark"
:data-active="active(mark) || undefined"
@mousedown.prevent="toggle(mark)"
>
{{ mark }}
</button>
</slot>
</DismissableLayer>
</PopperContent>
</PopperRoot>
</Portal>
</template>