Files
llm-in-text/milkdown-docs/plugin/example-block-plugin.md
ydy0615 d9ab341223 Add documentation for using Milkdown with various frameworks
- Created a new document for using components in Milkdown.
- Added a guide for using plugins in Milkdown, including toggling plugins programmatically and listing official plugins.
- Introduced a recipe for integrating Milkdown with Angular, including installation steps and component creation.
- Added a recipe for using Milkdown with Next.js, detailing installation and component setup.
- Created a guide for integrating Milkdown with NuxtJS, including installation and component creation.
- Added a comprehensive guide for using Milkdown with React, covering both Crepe and core Milkdown usage.
- Introduced a recipe for SolidJS integration with Milkdown, including installation and component creation.
- Added a guide for using Milkdown with Svelte, detailing installation and component setup.
- Created a comprehensive guide for integrating Milkdown with Vue, covering both Crepe and core Milkdown usage.
- Added a recipe for using Milkdown with Vue2, including installation and component creation.
2026-01-17 14:18:08 +08:00

126 lines
4.2 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Example: Block Plugin
The **block plugin** adds a positional hook next to every top-level node (paragraphs, headings, lists, etc.).
It is the foundation for features such as drag handles, quick-insert buttons or block toolbars.
In Milkdown this functionality lives in `@milkdown/plugin-block` and consistent with tooltip & slash consists of:
- a **BlockProvider** that deals with DOM positioning/lifecycle
- a **blockFactory** _implemented internally_ exposed as two ctx slices: `blockSpec`, `blockPlugin`
This guide covers:
- Understanding the provider/service architecture.
- Writing a **vanilla TypeScript** drag handle that lets you reorder blocks.
- Mounting custom UIs in **React** and **Vue**.
- Studying the production-ready _Block Handle_ feature inside Crepe.
---
## 1. Anatomy of a Block Plugin
Unlike tooltip/slash, `@milkdown/plugin-block` ships its factory slices directly:
```ts
import { blockSpec, blockPlugin } from "@milkdown/plugin-block";
```
You normally interact with **BlockProvider** which talks to an internal _BlockService_: the service listens to mouse / drag events, figures out which node is **active** and sends `show` / `hide` messages to the provider.
Your job is to decide how to render a UI for that active node.
Key APIs:
- `new BlockProvider({ ctx, content, ... })` similar to Tooltip/Slash.
- `provider.active` info about the currently focused block (`node`, `pos`, `el`).
- Optional callbacks: `getOffset`, `getPlacement`, `getPosition` for fine-grained positioning.
---
## 2. Minimal Vanilla Drag Handle
Below we build a small **drag handle** that appears on hover and lets you drag-n-drop any block.
```ts
import { block, blockPlugin } from "@milkdown/plugin-block";
import { BlockProvider } from "@milkdown/plugin-block/block-provider"; // path depending on bundler
import { Editor } from "@milkdown/kit/core";
import { commonmark } from "@milkdown/kit/preset/commonmark";
// 1⃣ Create DOM element for the handle
const handle = document.createElement("div");
handle.className = "drag-handle";
handle.innerHTML = "≡";
handle.style.cssText = `
width:20px;height:20px;display:flex;align-items:center;justify-content:center;
cursor:grab;border-radius:4px;background:#f2f3f5;color:#555;user-select:none;
`;
// 2⃣ Build provider show only when mouse is over a block
const provider = (ctx: Ctx) => {
const provider = new BlockProvider({
ctx,
content: handle,
getOffset: () => 8,
});
return {
update: provider.update,
destroy: provider.destroy,
};
};
// 3⃣ Wire provider to Milkdown
const blockConfig = (ctx: Ctx) => {
ctx.set(blockSpec.key, {
view: provider(ctx),
});
};
Editor.make().config(blockConfig).use(commonmark).use(block).create();
```
Drag & Drop:
The HTML element has `cursor:grab`. The internal `BlockService` automatically sets `draggable` and wires ProseMirror's drag-events so you can reorder blocks without extra code 👉 nice!
---
## 3. Framework Examples
### React
::iframe{src="https://stackblitz.com/github/Milkdown/examples/tree/main/react-block"}
The React demo renders a `<BlockHandle/>` component, keeps drag state in hooks and feeds the root element to `BlockProvider`.
### Vue
::iframe{src="https://stackblitz.com/github/Milkdown/examples/tree/main/vue-block"}
Vue's `<BlockHandle>` uses `Teleport` and reactive refs exactly like the tooltip/slash examples.
---
## 4. Real-world Feature Crepe Block Handle
Crepe brings all the pieces together to create a **block edit** experience that combines a drag handle **and** a plus-button to open the slash menu:
```text
packages/crepe/src/feature/block-edit/handle/
```
Things worth exploring:
1. **Dynamic placement** via `getPlacement` (centred vs top-aligned depending on node height).
2. Filtering nodes with `blockConfig.filterNodes` so handles do not appear inside tables / math / blockquotes.
3. Programmatically showing the _slash menu_ after pressing the "+" button.
---
## 5. Summary & Next Steps
`@milkdown/plugin-block` is the Swiss-army knife for any block-level UI: drag handles, add-buttons, side toolbars…
Combine it with tooltip/slash to build sophisticated editors.
Hack on the examples, tweak positioning callbacks, and ship your own block goodies 🚀.