@zuilib/text-editor 0.0.2 → 0.2.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/README.md +56 -5
- package/dist/index.d.ts +97 -4
- package/dist/index.js +2283 -57
- package/dist/styles.css +420 -0
- package/package.json +33 -23
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @zuilib/text-editor
|
|
2
2
|
|
|
3
|
-
Markdown editor for ZUI built on [Lexical](https://lexical.dev/): rich editing with shortcuts, raw markdown mode, read-only view, checklists,
|
|
3
|
+
Markdown editor for ZUI built on [Lexical](https://lexical.dev/): rich editing with shortcuts, raw markdown mode, read-only view, checklists, fenced code highlighting, GFM tables, and an embedded drawing canvas for diagrams.
|
|
4
4
|
|
|
5
5
|
## When to use
|
|
6
6
|
|
|
@@ -22,6 +22,8 @@ Peer dependencies (install in the consuming app):
|
|
|
22
22
|
| `@lexical/code` | `^0.35.0` |
|
|
23
23
|
| `@lexical/list` | `^0.35.0` |
|
|
24
24
|
| `@lexical/link` | `^0.35.0` |
|
|
25
|
+
| `@lexical/table` | `^0.35.0` |
|
|
26
|
+
| `@lexical/utils` | `^0.35.0` |
|
|
25
27
|
| `@zuilib/core` | workspace / published — import core styles |
|
|
26
28
|
|
|
27
29
|
## Installation
|
|
@@ -29,7 +31,7 @@ Peer dependencies (install in the consuming app):
|
|
|
29
31
|
```bash
|
|
30
32
|
pnpm add @zuilib/text-editor @zuilib/core \
|
|
31
33
|
lexical @lexical/react @lexical/markdown @lexical/rich-text \
|
|
32
|
-
@lexical/code @lexical/list @lexical/link
|
|
34
|
+
@lexical/code @lexical/list @lexical/link @lexical/table @lexical/utils
|
|
33
35
|
```
|
|
34
36
|
|
|
35
37
|
Monorepo:
|
|
@@ -45,7 +47,9 @@ Monorepo:
|
|
|
45
47
|
"@lexical/rich-text": "^0.35.0",
|
|
46
48
|
"@lexical/code": "^0.35.0",
|
|
47
49
|
"@lexical/list": "^0.35.0",
|
|
48
|
-
"@lexical/link": "^0.35.0"
|
|
50
|
+
"@lexical/link": "^0.35.0",
|
|
51
|
+
"@lexical/table": "^0.35.0",
|
|
52
|
+
"@lexical/utils": "^0.35.0"
|
|
49
53
|
}
|
|
50
54
|
}
|
|
51
55
|
```
|
|
@@ -115,15 +119,62 @@ Toggle modes in parent state; the editor preserves content via an internal ref w
|
|
|
115
119
|
| `readOnly` | `boolean` | `false` | Disables editing in Lexical modes |
|
|
116
120
|
| `autoFocus` | `boolean` | `false` | Focus on mount |
|
|
117
121
|
| `className` | `string` | — | Root wrapper class |
|
|
122
|
+
| `toolbar` | `boolean` | `true` | Formatting/insert toolbar in `edit-md` mode |
|
|
123
|
+
| `outline` | `boolean` | `false` | Table-of-contents sidebar listing headings |
|
|
124
|
+
| `foldable` | `boolean` | `true` | Collapse sections under their headings |
|
|
118
125
|
|
|
119
126
|
## Markdown features
|
|
120
127
|
|
|
121
|
-
**Shortcuts (edit-md):** headings (`#`), lists, blockquote, links, fenced code (via `CodeBlockShortcutPlugin`), checklists (`- [ ]` via `ChecklistShortcutPlugin`).
|
|
128
|
+
**Shortcuts (edit-md):** headings (`#`), lists, blockquote, links, fenced code (via `CodeBlockShortcutPlugin`), checklists (`- [ ]` via `ChecklistShortcutPlugin`), tables (`| a | b |`).
|
|
122
129
|
|
|
123
|
-
**Built-in plugins:** history (undo/redo), lists, checklists, links, markdown sync (`MarkdownSyncPlugin`), code highlighting (`CodeHighlightPlugin`).
|
|
130
|
+
**Built-in plugins:** history (undo/redo), lists, checklists, links, tables (`TablePlugin`), markdown sync (`MarkdownSyncPlugin`), code highlighting (`CodeHighlightPlugin`), toolbar (`ToolbarPlugin`).
|
|
124
131
|
|
|
125
132
|
**Not included:** file uploads, collaborative editing, or custom Lexical node registration — extend by forking or wrapping `MarkdownEditor`.
|
|
126
133
|
|
|
134
|
+
## Tables
|
|
135
|
+
|
|
136
|
+
GFM pipe tables round-trip through markdown and render artifact-style (rounded outer border, shaded header row, per-cell rules):
|
|
137
|
+
|
|
138
|
+
```md
|
|
139
|
+
| Metric | Q1 | Q2 |
|
|
140
|
+
| --- | --- | --- |
|
|
141
|
+
| Revenue | $1.2M | $1.8M |
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Insert a 3×3 table from the toolbar, or type a `| a | b |` row. Tab/arrow navigation, cell selection, and row/column operations come from Lexical's `TablePlugin`. Inline formatting (`**bold**`, `*italic*`, `` `code` ``) works inside cells.
|
|
145
|
+
|
|
146
|
+
## Drawing canvas
|
|
147
|
+
|
|
148
|
+
The toolbar's "Insert drawing" button embeds an Excalidraw-style canvas for simple diagrams: rectangles, ellipses, triangles, pentagons, arrows, lines, and text labels, with a stroke and fill color palette.
|
|
149
|
+
|
|
150
|
+
- Draw a shape by picking a tool and dragging; the tool returns to **Select** after each shape
|
|
151
|
+
- Select to move, resize (corner handles), or re-point arrows/lines (endpoint handles)
|
|
152
|
+
- Color swatches restyle the selected shape (or set defaults for the next one)
|
|
153
|
+
- `Delete`/`Backspace` removes the selected shape; drag the bottom pill to resize the canvas
|
|
154
|
+
- Boxes (rects/ellipses) have three built-in text slots that move with the shape: a bold **label** at the top, main **content** in the center, and a dimmer **footer** at the bottom. Double-click the top strip of a box to edit the label, the middle for content, the bottom strip for the footer (or click a selected box again, or press Enter for content). Clearing a slot removes it; the box stays.
|
|
155
|
+
- **Arrows bind to boxes**: draw an arrow starting or ending on a box and it attaches to the box border — moving or resizing the box drags the arrow along. Endpoints re-anchor toward the other end automatically. Drag an endpoint off a box to detach it; drop it on another box to re-attach. Deleting a box releases its arrows.
|
|
156
|
+
- A selected arrow shows a **direction toggle** in the toolbar: one-way (head at the end) or two-way (heads on both ends).
|
|
157
|
+
- A selected connector also shows a **routing toggle**: straight (freely diagonal) or **elbow** — an orthogonal path with right-angle bends. The elbow's middle segment has a drag handle to reposition the bend; its position is stored as a fraction of the span so it stays put while bound boxes move.
|
|
158
|
+
- **Waypoints** for arbitrary multi-segment paths: a selected connector shows dashed "+" handles at each segment midpoint — drag one to insert a bend point there. Waypoints snap to their neighbors' axes near-alignment so right angles are easy; drag freely for diagonals. Double-click a waypoint to remove it. Adding a point to an elbow connector converts its corners into editable waypoints. Selecting a routing mode from the toolbar clears waypoints.
|
|
159
|
+
- **Arrows and lines can carry a label**: double-click the connector (or select it and press Enter) to edit text that rides the midpoint on a small backing plate, and moves as the connector moves.
|
|
160
|
+
- The standalone **Text** tool is for free-floating annotations (e.g. labeling an arrow); double-click to edit
|
|
161
|
+
|
|
162
|
+
Drawings persist inside the markdown as a fenced block, so the document stays a plain string:
|
|
163
|
+
|
|
164
|
+
````md
|
|
165
|
+
```drawing
|
|
166
|
+
{"version":1,"height":320,"shapes":[...]}
|
|
167
|
+
```
|
|
168
|
+
````
|
|
169
|
+
|
|
170
|
+
In `view` mode (or `readOnly`) the canvas renders the shapes without any editing chrome.
|
|
171
|
+
|
|
172
|
+
## Outline & section folding
|
|
173
|
+
|
|
174
|
+
`outline` docks a collapsible table-of-contents sidebar on the right: it lists the document's headings live (indented by level), scrolls to a heading on click, and highlights the section currently in view. Pure UI — nothing is added to the document. Works in `edit-md` and `view` modes (not `edit-raw`).
|
|
175
|
+
|
|
176
|
+
`foldable` (on by default) adds a chevron in the left gutter of every heading (visible on hover). Clicking it collapses the section — everything up to the next heading of the same or higher level — marked by a trailing `…` on the heading. Folding is view-layer only: the markdown string is unaffected, and fold state resets on remount. If the cursor enters a folded section (e.g. via arrow keys), it auto-expands so content can never be edited invisibly.
|
|
177
|
+
|
|
127
178
|
## Form integration
|
|
128
179
|
|
|
129
180
|
With `@zuilib/form`:
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { ElementNode, NodeKey, EditorConfig, SerializedElementNode, LexicalNode } from 'lexical';
|
|
3
|
-
import { MultilineElementTransformer } from '@lexical/markdown';
|
|
2
|
+
import { ElementNode, NodeKey, EditorConfig, SerializedElementNode, LexicalNode, DecoratorNode, Spread, SerializedLexicalNode, DOMExportOutput, DOMConversionMap, LexicalEditor } from 'lexical';
|
|
3
|
+
import { MultilineElementTransformer, ElementTransformer } from '@lexical/markdown';
|
|
4
|
+
import { ReactElement } from 'react';
|
|
4
5
|
|
|
5
6
|
type Props = Readonly<{
|
|
6
7
|
value?: string;
|
|
@@ -10,8 +11,14 @@ type Props = Readonly<{
|
|
|
10
11
|
className?: string;
|
|
11
12
|
mode?: 'edit-raw' | 'edit-md' | 'view';
|
|
12
13
|
autoFocus?: boolean;
|
|
14
|
+
/** Show the formatting/insert toolbar in edit-md mode (default true) */
|
|
15
|
+
toolbar?: boolean;
|
|
16
|
+
/** Show a table-of-contents sidebar listing the document's headings */
|
|
17
|
+
outline?: boolean;
|
|
18
|
+
/** Allow collapsing sections under their headings (default true) */
|
|
19
|
+
foldable?: boolean;
|
|
13
20
|
}>;
|
|
14
|
-
declare function MarkdownEditor({ value, onChange, placeholder, readOnly, className, mode, autoFocus, }: Props): react_jsx_runtime.JSX.Element;
|
|
21
|
+
declare function MarkdownEditor({ value, onChange, placeholder, readOnly, className, mode, autoFocus, toolbar, outline, foldable, }: Props): react_jsx_runtime.JSX.Element;
|
|
15
22
|
|
|
16
23
|
type SerializedFrontmatterNode = SerializedElementNode;
|
|
17
24
|
/**
|
|
@@ -54,4 +61,90 @@ declare function $isFrontmatterNode(node: LexicalNode | null | undefined): node
|
|
|
54
61
|
*/
|
|
55
62
|
declare const FRONTMATTER: MultilineElementTransformer;
|
|
56
63
|
|
|
57
|
-
|
|
64
|
+
type DrawingShapeType = 'rect' | 'ellipse' | 'triangle' | 'pentagon' | 'arrow' | 'line' | 'text';
|
|
65
|
+
type DrawingShape = Readonly<{
|
|
66
|
+
id: string;
|
|
67
|
+
type: DrawingShapeType;
|
|
68
|
+
/** Top-left corner (rect/ellipse/text) or start point (arrow/line) */
|
|
69
|
+
x: number;
|
|
70
|
+
y: number;
|
|
71
|
+
/** Size (rect/ellipse) or delta to the end point (arrow/line) */
|
|
72
|
+
w: number;
|
|
73
|
+
h: number;
|
|
74
|
+
stroke: string;
|
|
75
|
+
fill: string;
|
|
76
|
+
strokeWidth: number;
|
|
77
|
+
/** Standalone text, main (center) content of a box, or connector label */
|
|
78
|
+
text?: string;
|
|
79
|
+
/** Small heading rendered at the top of a box */
|
|
80
|
+
label?: string;
|
|
81
|
+
/** Small line rendered at the bottom of a box */
|
|
82
|
+
footer?: string;
|
|
83
|
+
/** Arrow/line: id of the box the start point is attached to */
|
|
84
|
+
startBinding?: string;
|
|
85
|
+
/** Arrow/line: id of the box the end point is attached to */
|
|
86
|
+
endBinding?: string;
|
|
87
|
+
/** Arrow: render arrowheads on both ends */
|
|
88
|
+
bidirectional?: boolean;
|
|
89
|
+
/** Connector routing: right-angled elbow path instead of a straight line */
|
|
90
|
+
routing?: 'elbow';
|
|
91
|
+
/** Elbow middle-segment position as a fraction of the span (default 0.5) */
|
|
92
|
+
elbow?: number;
|
|
93
|
+
/**
|
|
94
|
+
* Connector: intermediate path points between start and end (absolute
|
|
95
|
+
* canvas coordinates). Takes precedence over `routing`.
|
|
96
|
+
*/
|
|
97
|
+
waypoints?: ReadonlyArray<{
|
|
98
|
+
x: number;
|
|
99
|
+
y: number;
|
|
100
|
+
}>;
|
|
101
|
+
}>;
|
|
102
|
+
type DrawingData = Readonly<{
|
|
103
|
+
version: 1;
|
|
104
|
+
height: number;
|
|
105
|
+
shapes: readonly DrawingShape[];
|
|
106
|
+
}>;
|
|
107
|
+
|
|
108
|
+
type SerializedDrawingNode = Spread<{
|
|
109
|
+
data: string;
|
|
110
|
+
}, SerializedLexicalNode>;
|
|
111
|
+
/**
|
|
112
|
+
* A block-level decorator node embedding a vector drawing (rectangles,
|
|
113
|
+
* ellipses, arrows, lines, text) edited on an Excalidraw-style canvas.
|
|
114
|
+
*
|
|
115
|
+
* The drawing is stored as a JSON string and round-trips through markdown as
|
|
116
|
+
* a ```drawing fenced code block, so documents remain plain markdown.
|
|
117
|
+
*/
|
|
118
|
+
declare class DrawingNode extends DecoratorNode<ReactElement> {
|
|
119
|
+
__data: string;
|
|
120
|
+
static getType(): string;
|
|
121
|
+
static clone(node: DrawingNode): DrawingNode;
|
|
122
|
+
constructor(data: string, key?: NodeKey);
|
|
123
|
+
static importJSON(serializedNode: SerializedDrawingNode): DrawingNode;
|
|
124
|
+
exportJSON(): SerializedDrawingNode;
|
|
125
|
+
createDOM(config: EditorConfig): HTMLElement;
|
|
126
|
+
updateDOM(): false;
|
|
127
|
+
exportDOM(): DOMExportOutput;
|
|
128
|
+
static importDOM(): DOMConversionMap | null;
|
|
129
|
+
getData(): DrawingData;
|
|
130
|
+
setData(data: DrawingData): void;
|
|
131
|
+
isInline(): false;
|
|
132
|
+
decorate(_editor: LexicalEditor, _config: EditorConfig): ReactElement;
|
|
133
|
+
}
|
|
134
|
+
declare function $createDrawingNode(data?: string): DrawingNode;
|
|
135
|
+
declare function $isDrawingNode(node: LexicalNode | null | undefined): node is DrawingNode;
|
|
136
|
+
/**
|
|
137
|
+
* Markdown transformer persisting drawings as fenced ```drawing blocks with a
|
|
138
|
+
* JSON payload. Must be ordered before the built-in CODE transformer so it
|
|
139
|
+
* claims the fence first on import.
|
|
140
|
+
*/
|
|
141
|
+
declare const DRAWING: MultilineElementTransformer;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Markdown transformer for GFM tables. Adapted from the Lexical playground.
|
|
145
|
+
* Rows are matched line-by-line on import and stitched into a single
|
|
146
|
+
* TableNode; the divider row promotes the row above it to a header row.
|
|
147
|
+
*/
|
|
148
|
+
declare const TABLE: ElementTransformer;
|
|
149
|
+
|
|
150
|
+
export { $createDrawingNode, $createFrontmatterNode, $isDrawingNode, $isFrontmatterNode, DRAWING, type DrawingData, DrawingNode, type DrawingShape, type DrawingShapeType, FRONTMATTER, FrontmatterNode, MarkdownEditor, type Props as MarkdownEditorProps, type SerializedDrawingNode, type SerializedFrontmatterNode, TABLE };
|