@zuilib/text-editor 0.0.2 → 0.1.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 +48 -5
- package/dist/index.d.ts +93 -4
- package/dist/index.js +2010 -42
- package/dist/styles.css +279 -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,54 @@ 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 |
|
|
118
123
|
|
|
119
124
|
## Markdown features
|
|
120
125
|
|
|
121
|
-
**Shortcuts (edit-md):** headings (`#`), lists, blockquote, links, fenced code (via `CodeBlockShortcutPlugin`), checklists (`- [ ]` via `ChecklistShortcutPlugin`).
|
|
126
|
+
**Shortcuts (edit-md):** headings (`#`), lists, blockquote, links, fenced code (via `CodeBlockShortcutPlugin`), checklists (`- [ ]` via `ChecklistShortcutPlugin`), tables (`| a | b |`).
|
|
122
127
|
|
|
123
|
-
**Built-in plugins:** history (undo/redo), lists, checklists, links, markdown sync (`MarkdownSyncPlugin`), code highlighting (`CodeHighlightPlugin`).
|
|
128
|
+
**Built-in plugins:** history (undo/redo), lists, checklists, links, tables (`TablePlugin`), markdown sync (`MarkdownSyncPlugin`), code highlighting (`CodeHighlightPlugin`), toolbar (`ToolbarPlugin`).
|
|
124
129
|
|
|
125
130
|
**Not included:** file uploads, collaborative editing, or custom Lexical node registration — extend by forking or wrapping `MarkdownEditor`.
|
|
126
131
|
|
|
132
|
+
## Tables
|
|
133
|
+
|
|
134
|
+
GFM pipe tables round-trip through markdown and render artifact-style (rounded outer border, shaded header row, per-cell rules):
|
|
135
|
+
|
|
136
|
+
```md
|
|
137
|
+
| Metric | Q1 | Q2 |
|
|
138
|
+
| --- | --- | --- |
|
|
139
|
+
| Revenue | $1.2M | $1.8M |
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
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.
|
|
143
|
+
|
|
144
|
+
## Drawing canvas
|
|
145
|
+
|
|
146
|
+
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.
|
|
147
|
+
|
|
148
|
+
- Draw a shape by picking a tool and dragging; the tool returns to **Select** after each shape
|
|
149
|
+
- Select to move, resize (corner handles), or re-point arrows/lines (endpoint handles)
|
|
150
|
+
- Color swatches restyle the selected shape (or set defaults for the next one)
|
|
151
|
+
- `Delete`/`Backspace` removes the selected shape; drag the bottom pill to resize the canvas
|
|
152
|
+
- 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.
|
|
153
|
+
- **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.
|
|
154
|
+
- A selected arrow shows a **direction toggle** in the toolbar: one-way (head at the end) or two-way (heads on both ends).
|
|
155
|
+
- 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.
|
|
156
|
+
- **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.
|
|
157
|
+
- **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.
|
|
158
|
+
- The standalone **Text** tool is for free-floating annotations (e.g. labeling an arrow); double-click to edit
|
|
159
|
+
|
|
160
|
+
Drawings persist inside the markdown as a fenced block, so the document stays a plain string:
|
|
161
|
+
|
|
162
|
+
````md
|
|
163
|
+
```drawing
|
|
164
|
+
{"version":1,"height":320,"shapes":[...]}
|
|
165
|
+
```
|
|
166
|
+
````
|
|
167
|
+
|
|
168
|
+
In `view` mode (or `readOnly`) the canvas renders the shapes without any editing chrome.
|
|
169
|
+
|
|
127
170
|
## Form integration
|
|
128
171
|
|
|
129
172
|
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,10 @@ 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;
|
|
13
16
|
}>;
|
|
14
|
-
declare function MarkdownEditor({ value, onChange, placeholder, readOnly, className, mode, autoFocus, }: Props): react_jsx_runtime.JSX.Element;
|
|
17
|
+
declare function MarkdownEditor({ value, onChange, placeholder, readOnly, className, mode, autoFocus, toolbar, }: Props): react_jsx_runtime.JSX.Element;
|
|
15
18
|
|
|
16
19
|
type SerializedFrontmatterNode = SerializedElementNode;
|
|
17
20
|
/**
|
|
@@ -54,4 +57,90 @@ declare function $isFrontmatterNode(node: LexicalNode | null | undefined): node
|
|
|
54
57
|
*/
|
|
55
58
|
declare const FRONTMATTER: MultilineElementTransformer;
|
|
56
59
|
|
|
57
|
-
|
|
60
|
+
type DrawingShapeType = 'rect' | 'ellipse' | 'triangle' | 'pentagon' | 'arrow' | 'line' | 'text';
|
|
61
|
+
type DrawingShape = Readonly<{
|
|
62
|
+
id: string;
|
|
63
|
+
type: DrawingShapeType;
|
|
64
|
+
/** Top-left corner (rect/ellipse/text) or start point (arrow/line) */
|
|
65
|
+
x: number;
|
|
66
|
+
y: number;
|
|
67
|
+
/** Size (rect/ellipse) or delta to the end point (arrow/line) */
|
|
68
|
+
w: number;
|
|
69
|
+
h: number;
|
|
70
|
+
stroke: string;
|
|
71
|
+
fill: string;
|
|
72
|
+
strokeWidth: number;
|
|
73
|
+
/** Standalone text, main (center) content of a box, or connector label */
|
|
74
|
+
text?: string;
|
|
75
|
+
/** Small heading rendered at the top of a box */
|
|
76
|
+
label?: string;
|
|
77
|
+
/** Small line rendered at the bottom of a box */
|
|
78
|
+
footer?: string;
|
|
79
|
+
/** Arrow/line: id of the box the start point is attached to */
|
|
80
|
+
startBinding?: string;
|
|
81
|
+
/** Arrow/line: id of the box the end point is attached to */
|
|
82
|
+
endBinding?: string;
|
|
83
|
+
/** Arrow: render arrowheads on both ends */
|
|
84
|
+
bidirectional?: boolean;
|
|
85
|
+
/** Connector routing: right-angled elbow path instead of a straight line */
|
|
86
|
+
routing?: 'elbow';
|
|
87
|
+
/** Elbow middle-segment position as a fraction of the span (default 0.5) */
|
|
88
|
+
elbow?: number;
|
|
89
|
+
/**
|
|
90
|
+
* Connector: intermediate path points between start and end (absolute
|
|
91
|
+
* canvas coordinates). Takes precedence over `routing`.
|
|
92
|
+
*/
|
|
93
|
+
waypoints?: ReadonlyArray<{
|
|
94
|
+
x: number;
|
|
95
|
+
y: number;
|
|
96
|
+
}>;
|
|
97
|
+
}>;
|
|
98
|
+
type DrawingData = Readonly<{
|
|
99
|
+
version: 1;
|
|
100
|
+
height: number;
|
|
101
|
+
shapes: readonly DrawingShape[];
|
|
102
|
+
}>;
|
|
103
|
+
|
|
104
|
+
type SerializedDrawingNode = Spread<{
|
|
105
|
+
data: string;
|
|
106
|
+
}, SerializedLexicalNode>;
|
|
107
|
+
/**
|
|
108
|
+
* A block-level decorator node embedding a vector drawing (rectangles,
|
|
109
|
+
* ellipses, arrows, lines, text) edited on an Excalidraw-style canvas.
|
|
110
|
+
*
|
|
111
|
+
* The drawing is stored as a JSON string and round-trips through markdown as
|
|
112
|
+
* a ```drawing fenced code block, so documents remain plain markdown.
|
|
113
|
+
*/
|
|
114
|
+
declare class DrawingNode extends DecoratorNode<ReactElement> {
|
|
115
|
+
__data: string;
|
|
116
|
+
static getType(): string;
|
|
117
|
+
static clone(node: DrawingNode): DrawingNode;
|
|
118
|
+
constructor(data: string, key?: NodeKey);
|
|
119
|
+
static importJSON(serializedNode: SerializedDrawingNode): DrawingNode;
|
|
120
|
+
exportJSON(): SerializedDrawingNode;
|
|
121
|
+
createDOM(config: EditorConfig): HTMLElement;
|
|
122
|
+
updateDOM(): false;
|
|
123
|
+
exportDOM(): DOMExportOutput;
|
|
124
|
+
static importDOM(): DOMConversionMap | null;
|
|
125
|
+
getData(): DrawingData;
|
|
126
|
+
setData(data: DrawingData): void;
|
|
127
|
+
isInline(): false;
|
|
128
|
+
decorate(_editor: LexicalEditor, _config: EditorConfig): ReactElement;
|
|
129
|
+
}
|
|
130
|
+
declare function $createDrawingNode(data?: string): DrawingNode;
|
|
131
|
+
declare function $isDrawingNode(node: LexicalNode | null | undefined): node is DrawingNode;
|
|
132
|
+
/**
|
|
133
|
+
* Markdown transformer persisting drawings as fenced ```drawing blocks with a
|
|
134
|
+
* JSON payload. Must be ordered before the built-in CODE transformer so it
|
|
135
|
+
* claims the fence first on import.
|
|
136
|
+
*/
|
|
137
|
+
declare const DRAWING: MultilineElementTransformer;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Markdown transformer for GFM tables. Adapted from the Lexical playground.
|
|
141
|
+
* Rows are matched line-by-line on import and stitched into a single
|
|
142
|
+
* TableNode; the divider row promotes the row above it to a header row.
|
|
143
|
+
*/
|
|
144
|
+
declare const TABLE: ElementTransformer;
|
|
145
|
+
|
|
146
|
+
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 };
|