@zuilib/text-editor 0.3.1 → 0.4.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/CHANGELOG.md +15 -0
- package/README.md +27 -17
- package/dist/index.d.ts +31 -16
- package/dist/index.js +209 -61
- package/dist/styles.css +8 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## 0.4.0
|
|
4
4
|
|
|
5
|
+
- **Table density**, Slab-style: the floating table toolbar (caret in a
|
|
6
|
+
table) gains *compact* / *comfortable* (default) / *spacious* row padding
|
|
7
|
+
and font size, next to the width toggle
|
|
8
|
+
- The settings comment above a GFM table now lists every non-default
|
|
9
|
+
setting, e.g. `<!-- width: content; density: compact -->`; the 0.3.1
|
|
10
|
+
`<!-- width: content -->` form is unchanged for width-only tables
|
|
11
|
+
- `useMarkdownEditor()` gains `tableDensity` / `setTableDensity`
|
|
12
|
+
- New exports: `TableDensity`, `TableSettings`, `DEFAULT_TABLE_SETTINGS`,
|
|
13
|
+
`$getTableSettings` / `$setTableSettings`, `$getTableDensity` /
|
|
14
|
+
`$setTableDensity`, `parseTableSettingsMarker` /
|
|
15
|
+
`formatTableSettingsMarker`. `TABLE_WIDTH_MARKER` is deprecated in favour
|
|
16
|
+
of `formatTableSettingsMarker`
|
|
17
|
+
|
|
18
|
+
## 0.3.1
|
|
19
|
+
|
|
5
20
|
- **Block width** for tables and drawings: switch any table or drawing
|
|
6
21
|
between *full width* (spans the editor, the previous behaviour) and
|
|
7
22
|
*content width* (shrinks to fit its columns / shapes, left-aligned with
|
package/README.md
CHANGED
|
@@ -13,8 +13,8 @@ document outline, and collapsible sections.
|
|
|
13
13
|
blockquotes, links, fenced code (with syntax highlighting), tables
|
|
14
14
|
- **Tables**: GFM pipe tables, edited in place (tab between cells, ranges,
|
|
15
15
|
inline formatting), styled like Claude artifacts
|
|
16
|
-
- **
|
|
17
|
-
content width
|
|
16
|
+
- **Table settings**: full/content width and row density per table;
|
|
17
|
+
diagrams switch between full and content width too — Slab-style
|
|
18
18
|
- **Diagrams**: a drawing canvas embedded in the document — cards with
|
|
19
19
|
attached text slots, bound arrows that follow their cards, elbow and
|
|
20
20
|
multi-waypoint routing, labels, color palettes, dark mode
|
|
@@ -98,26 +98,33 @@ Editing is in place: Tab/arrows between cells, cell range selection, inline
|
|
|
98
98
|
formatting inside cells. Cell content is single-line in markdown; newlines
|
|
99
99
|
are escaped as `\n`.
|
|
100
100
|
|
|
101
|
-
###
|
|
101
|
+
### Table settings: width and density
|
|
102
102
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
103
|
+
Place the caret in a table and a small toolbar floats at its top-right
|
|
104
|
+
corner:
|
|
105
|
+
|
|
106
|
+
- **Width** — *full* (default: columns share the editor width) or
|
|
107
|
+
*content* (the table shrinks to fit its columns, left-aligned with the
|
|
108
|
+
text, never wider than the editor).
|
|
109
|
+
- **Density** — *compact*, *comfortable* (default), or *spacious* cell
|
|
110
|
+
padding and font size.
|
|
111
|
+
|
|
112
|
+
Non-default settings are persisted as one HTML comment on the line directly
|
|
113
|
+
above the table — other markdown renderers hide it:
|
|
109
114
|
|
|
110
115
|
```md
|
|
111
|
-
<!-- width: content -->
|
|
116
|
+
<!-- width: content; density: compact -->
|
|
112
117
|
| Key | Value |
|
|
113
118
|
| --- | --- |
|
|
114
119
|
| Region | eu-west-1 |
|
|
115
120
|
```
|
|
116
121
|
|
|
117
|
-
Programmatic access: `useMarkdownEditor().tableWidth` / `setTableWidth`
|
|
118
|
-
(for custom toolbars), or
|
|
119
|
-
|
|
120
|
-
`
|
|
122
|
+
Programmatic access: `useMarkdownEditor().tableWidth` / `setTableWidth` and
|
|
123
|
+
`tableDensity` / `setTableDensity` (for custom toolbars), or
|
|
124
|
+
`$getTableSettings(node)` / `$setTableSettings(node, { width, density })`
|
|
125
|
+
inside `editor.update`. `parseTableSettingsMarker` /
|
|
126
|
+
`formatTableSettingsMarker` convert between the comment line and a
|
|
127
|
+
`TableSettings` object.
|
|
121
128
|
|
|
122
129
|
## Toolbar & custom toolbars
|
|
123
130
|
|
|
@@ -203,8 +210,9 @@ function BoldButton() {
|
|
|
203
210
|
|
|
204
211
|
It returns `editor` (the Lexical instance), `activeFormats`,
|
|
205
212
|
`toggleFormat`, `insertTable`, `insertDrawing`, `tableWidth` /
|
|
206
|
-
`setTableWidth`
|
|
207
|
-
outside tables), `canUndo`,
|
|
213
|
+
`setTableWidth` and `tableDensity` / `setTableDensity` (settings of the
|
|
214
|
+
table containing the selection, `null` outside tables), `canUndo`,
|
|
215
|
+
`canRedo`, `undo`, `redo`.
|
|
208
216
|
|
|
209
217
|
## Diagrams (drawing canvas)
|
|
210
218
|
|
|
@@ -269,7 +277,9 @@ diagrams:
|
|
|
269
277
|
| `Toolbar`, `ToolbarButton`, `ToolbarDivider`, `FormatButtons`, `InsertButtons`, `HistoryButtons` | Toolbar primitives |
|
|
270
278
|
| `@zuilib/text-editor/styles.css` | Editor chrome + theme styles (required) |
|
|
271
279
|
| `TABLE` | GFM table markdown transformer |
|
|
272
|
-
| `BlockWidth`,
|
|
280
|
+
| `BlockWidth`, `TableDensity`, `TableSettings`, `DEFAULT_TABLE_SETTINGS` | Table setting types |
|
|
281
|
+
| `$getTableSettings`, `$setTableSettings`, `$getTableWidth`, `$setTableWidth`, `$getTableDensity`, `$setTableDensity`, `$getSelectedTable` | Table setting helpers (inside `editor.update`/`read`) |
|
|
282
|
+
| `parseTableSettingsMarker`, `formatTableSettingsMarker` | Marker comment ⇄ `TableSettings` |
|
|
273
283
|
| `DRAWING`, `DrawingNode`, `$createDrawingNode`, `$isDrawingNode` | Drawing node + markdown transformer |
|
|
274
284
|
| `FRONTMATTER`, `FrontmatterNode`, `$createFrontmatterNode`, `$isFrontmatterNode` | Frontmatter node + transformer |
|
|
275
285
|
| `DrawingData`, `DrawingShape`, `DrawingShapeType` | Drawing payload types |
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode, ReactElement } from 'react';
|
|
3
3
|
import { LexicalEditor, TextFormatType, ElementNode, NodeKey, EditorConfig, SerializedElementNode, LexicalNode, DecoratorNode, Spread, SerializedLexicalNode, DOMExportOutput, DOMConversionMap } from 'lexical';
|
|
4
|
-
import { MultilineElementTransformer, ElementTransformer } from '@lexical/markdown';
|
|
5
4
|
import { TableNode } from '@lexical/table';
|
|
5
|
+
import { MultilineElementTransformer, ElementTransformer } from '@lexical/markdown';
|
|
6
6
|
|
|
7
7
|
type EditorMode = 'edit-raw' | 'edit-md' | 'view';
|
|
8
8
|
type EditorRootProps = Readonly<{
|
|
@@ -48,6 +48,28 @@ declare function OutlinePlugin(): ReactElement;
|
|
|
48
48
|
*/
|
|
49
49
|
type BlockWidth = 'full' | 'content';
|
|
50
50
|
|
|
51
|
+
/** Cell padding / font size preset */
|
|
52
|
+
type TableDensity = 'compact' | 'comfortable' | 'spacious';
|
|
53
|
+
type TableSettings = Readonly<{
|
|
54
|
+
width: BlockWidth;
|
|
55
|
+
density: TableDensity;
|
|
56
|
+
}>;
|
|
57
|
+
declare const DEFAULT_TABLE_SETTINGS: TableSettings;
|
|
58
|
+
declare function $getTableSettings(table: TableNode): TableSettings;
|
|
59
|
+
declare function $setTableSettings(table: TableNode, settings: Partial<TableSettings>): void;
|
|
60
|
+
declare function $getTableWidth(table: TableNode): BlockWidth;
|
|
61
|
+
declare function $setTableWidth(table: TableNode, width: BlockWidth): void;
|
|
62
|
+
declare function $getTableDensity(table: TableNode): TableDensity;
|
|
63
|
+
declare function $setTableDensity(table: TableNode, density: TableDensity): void;
|
|
64
|
+
/** The table containing the current selection (caret or cell range), if any */
|
|
65
|
+
declare function $getSelectedTable(): TableNode | null;
|
|
66
|
+
/** @deprecated Use `formatTableSettingsMarker({ ...DEFAULT_TABLE_SETTINGS, width: 'content' })` */
|
|
67
|
+
declare const TABLE_WIDTH_MARKER = "<!-- width: content -->";
|
|
68
|
+
/** Settings encoded in a marker line, or null if the text is not a marker */
|
|
69
|
+
declare function parseTableSettingsMarker(text: string): Partial<TableSettings> | null;
|
|
70
|
+
/** Marker line for the given settings, or null when everything is default */
|
|
71
|
+
declare function formatTableSettingsMarker(settings: TableSettings): string | null;
|
|
72
|
+
|
|
51
73
|
type MarkdownEditorApi = Readonly<{
|
|
52
74
|
/** The underlying Lexical editor, for dispatching your own commands */
|
|
53
75
|
editor: LexicalEditor;
|
|
@@ -63,6 +85,10 @@ type MarkdownEditorApi = Readonly<{
|
|
|
63
85
|
tableWidth: BlockWidth | null;
|
|
64
86
|
/** Resize the table containing the selection; no-op outside tables */
|
|
65
87
|
setTableWidth: (width: BlockWidth) => void;
|
|
88
|
+
/** Density of the table containing the selection; `null` outside tables */
|
|
89
|
+
tableDensity: TableDensity | null;
|
|
90
|
+
/** Change the density of the table containing the selection; no-op outside tables */
|
|
91
|
+
setTableDensity: (density: TableDensity) => void;
|
|
66
92
|
canUndo: boolean;
|
|
67
93
|
canRedo: boolean;
|
|
68
94
|
undo: () => void;
|
|
@@ -274,23 +300,12 @@ declare const DRAWING: MultilineElementTransformer;
|
|
|
274
300
|
* Markdown transformer for GFM tables. Adapted from the Lexical playground.
|
|
275
301
|
* Rows are matched line-by-line on import and stitched into a single
|
|
276
302
|
* TableNode; the divider row promotes the row above it to a header row.
|
|
277
|
-
* A `<!-- width: content -->` line directly
|
|
278
|
-
*
|
|
279
|
-
* on export.
|
|
303
|
+
* A `<!-- width: content; density: compact -->` settings line directly
|
|
304
|
+
* above the table (see tableSettings.ts) is consumed on import and
|
|
305
|
+
* re-emitted on export.
|
|
280
306
|
*/
|
|
281
307
|
declare const TABLE: ElementTransformer;
|
|
282
308
|
|
|
283
|
-
/**
|
|
284
|
-
* A content-width table is persisted in markdown as a GFM table preceded by
|
|
285
|
-
* this HTML comment on its own line. Renderers that don't know the marker
|
|
286
|
-
* simply hide the comment.
|
|
287
|
-
*/
|
|
288
|
-
declare const TABLE_WIDTH_MARKER = "<!-- width: content -->";
|
|
289
|
-
declare function $getTableWidth(table: TableNode): BlockWidth;
|
|
290
|
-
declare function $setTableWidth(table: TableNode, width: BlockWidth): void;
|
|
291
|
-
/** The table containing the current selection (caret or cell range), if any */
|
|
292
|
-
declare function $getSelectedTable(): TableNode | null;
|
|
293
|
-
|
|
294
309
|
/**
|
|
295
310
|
* JSON Schema (draft-07) for the payload of a ```drawing fenced block.
|
|
296
311
|
*
|
|
@@ -427,4 +442,4 @@ declare const DRAWING_DATA_JSON_SCHEMA: {
|
|
|
427
442
|
};
|
|
428
443
|
};
|
|
429
444
|
|
|
430
|
-
export { $createDrawingNode, $createFrontmatterNode, $getSelectedTable, $getTableWidth, $isDrawingNode, $isFrontmatterNode, $setTableWidth, type BlockWidth, DRAWING, DRAWING_DATA_JSON_SCHEMA, type DrawingData, DrawingNode, type DrawingShape, type DrawingShapeType, type EditorContentProps, type EditorMode, type EditorRootProps, FRONTMATTER, FormatButtons, FrontmatterNode, HistoryButtons, InsertButtons, MarkdownEditor, type MarkdownEditorApi, type Props as MarkdownEditorProps, type SerializedDrawingNode, type SerializedFrontmatterNode, TABLE, TABLE_WIDTH_MARKER, Toolbar, ToolbarButton, ToolbarDivider, type ToolbarItems, parseDrawingData, serializeDrawingData, useMarkdownEditor };
|
|
445
|
+
export { $createDrawingNode, $createFrontmatterNode, $getSelectedTable, $getTableDensity, $getTableSettings, $getTableWidth, $isDrawingNode, $isFrontmatterNode, $setTableDensity, $setTableSettings, $setTableWidth, type BlockWidth, DEFAULT_TABLE_SETTINGS, DRAWING, DRAWING_DATA_JSON_SCHEMA, type DrawingData, DrawingNode, type DrawingShape, type DrawingShapeType, type EditorContentProps, type EditorMode, type EditorRootProps, FRONTMATTER, FormatButtons, FrontmatterNode, HistoryButtons, InsertButtons, MarkdownEditor, type MarkdownEditorApi, type Props as MarkdownEditorProps, type SerializedDrawingNode, type SerializedFrontmatterNode, TABLE, TABLE_WIDTH_MARKER, type TableDensity, type TableSettings, Toolbar, ToolbarButton, ToolbarDivider, type ToolbarItems, formatTableSettingsMarker, parseDrawingData, parseTableSettingsMarker, serializeDrawingData, useMarkdownEditor };
|
package/dist/index.js
CHANGED
|
@@ -2046,7 +2046,7 @@ import {
|
|
|
2046
2046
|
TableRowNode
|
|
2047
2047
|
} from "@lexical/table";
|
|
2048
2048
|
|
|
2049
|
-
// src/transformers/
|
|
2049
|
+
// src/transformers/tableSettings.ts
|
|
2050
2050
|
import {
|
|
2051
2051
|
$getNodeByKey as $getNodeByKey3,
|
|
2052
2052
|
$getSelection as $getSelection2,
|
|
@@ -2054,15 +2054,89 @@ import {
|
|
|
2054
2054
|
} from "lexical";
|
|
2055
2055
|
import { $findMatchingParent } from "@lexical/utils";
|
|
2056
2056
|
import { $isTableNode, $isTableSelection } from "@lexical/table";
|
|
2057
|
-
var
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2057
|
+
var DEFAULT_TABLE_SETTINGS = {
|
|
2058
|
+
width: "full",
|
|
2059
|
+
density: "comfortable"
|
|
2060
|
+
};
|
|
2061
|
+
var OPTION_DECLARATIONS = {
|
|
2062
|
+
width: {
|
|
2063
|
+
full: {},
|
|
2064
|
+
content: { width: "auto", "table-layout": "auto" }
|
|
2065
|
+
},
|
|
2066
|
+
density: {
|
|
2067
|
+
comfortable: {},
|
|
2068
|
+
compact: {
|
|
2069
|
+
"--zui-table-cell-padding": "0.25rem 0.5rem",
|
|
2070
|
+
"--zui-table-font-size": "0.8125rem"
|
|
2071
|
+
},
|
|
2072
|
+
spacious: {
|
|
2073
|
+
"--zui-table-cell-padding": "0.875rem 1rem",
|
|
2074
|
+
"--zui-table-font-size": "0.9375rem"
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
};
|
|
2078
|
+
var SETTING_KEYS = Object.keys(OPTION_DECLARATIONS);
|
|
2079
|
+
function parseStyle(style) {
|
|
2080
|
+
const declarations = /* @__PURE__ */ new Map();
|
|
2081
|
+
for (const part of style.split(";")) {
|
|
2082
|
+
const index = part.indexOf(":");
|
|
2083
|
+
if (index === -1) continue;
|
|
2084
|
+
const key = part.slice(0, index).trim();
|
|
2085
|
+
const value = part.slice(index + 1).trim();
|
|
2086
|
+
if (key && value) declarations.set(key, value);
|
|
2087
|
+
}
|
|
2088
|
+
return declarations;
|
|
2089
|
+
}
|
|
2090
|
+
function serializeStyle(declarations) {
|
|
2091
|
+
return [...declarations].map(([key, value]) => `${key}: ${value}`).join("; ");
|
|
2092
|
+
}
|
|
2093
|
+
function optionsOf(setting) {
|
|
2094
|
+
return Object.entries(OPTION_DECLARATIONS[setting]);
|
|
2095
|
+
}
|
|
2096
|
+
function readSetting(declarations, setting) {
|
|
2097
|
+
for (const [option, decls] of optionsOf(setting)) {
|
|
2098
|
+
const entries = Object.entries(decls);
|
|
2099
|
+
if (entries.length === 0) continue;
|
|
2100
|
+
if (entries.every(([key, value]) => declarations.get(key) === value)) {
|
|
2101
|
+
return option;
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
return DEFAULT_TABLE_SETTINGS[setting];
|
|
2105
|
+
}
|
|
2106
|
+
function writeSetting(declarations, setting, option) {
|
|
2107
|
+
for (const [, decls] of optionsOf(setting)) {
|
|
2108
|
+
for (const key of Object.keys(decls)) declarations.delete(key);
|
|
2109
|
+
}
|
|
2110
|
+
for (const [key, value] of Object.entries(OPTION_DECLARATIONS[setting][option])) {
|
|
2111
|
+
declarations.set(key, value);
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
function $getTableSettings(table) {
|
|
2115
|
+
const declarations = parseStyle(table.getStyle());
|
|
2116
|
+
return {
|
|
2117
|
+
width: readSetting(declarations, "width"),
|
|
2118
|
+
density: readSetting(declarations, "density")
|
|
2119
|
+
};
|
|
2120
|
+
}
|
|
2121
|
+
function $setTableSettings(table, settings) {
|
|
2122
|
+
const declarations = parseStyle(table.getStyle());
|
|
2123
|
+
for (const key of SETTING_KEYS) {
|
|
2124
|
+
const option = settings[key];
|
|
2125
|
+
if (option !== void 0) writeSetting(declarations, key, option);
|
|
2126
|
+
}
|
|
2127
|
+
table.setStyle(serializeStyle(declarations));
|
|
2128
|
+
}
|
|
2061
2129
|
function $getTableWidth(table) {
|
|
2062
|
-
return
|
|
2130
|
+
return $getTableSettings(table).width;
|
|
2063
2131
|
}
|
|
2064
2132
|
function $setTableWidth(table, width) {
|
|
2065
|
-
table
|
|
2133
|
+
$setTableSettings(table, { width });
|
|
2134
|
+
}
|
|
2135
|
+
function $getTableDensity(table) {
|
|
2136
|
+
return $getTableSettings(table).density;
|
|
2137
|
+
}
|
|
2138
|
+
function $setTableDensity(table, density) {
|
|
2139
|
+
$setTableSettings(table, { density });
|
|
2066
2140
|
}
|
|
2067
2141
|
function $getSelectedTable() {
|
|
2068
2142
|
const selection = $getSelection2();
|
|
@@ -2077,14 +2151,36 @@ function $getSelectedTable() {
|
|
|
2077
2151
|
);
|
|
2078
2152
|
return $isTableNode(table) ? table : null;
|
|
2079
2153
|
}
|
|
2154
|
+
var MARKER_REG_EXP = /^<!--\s*([a-z]+\s*:\s*[a-z]+(?:\s*;\s*[a-z]+\s*:\s*[a-z]+)*)\s*;?\s*-->\s*$/;
|
|
2155
|
+
function isOption(setting, value) {
|
|
2156
|
+
return value in OPTION_DECLARATIONS[setting];
|
|
2157
|
+
}
|
|
2158
|
+
var TABLE_WIDTH_MARKER = "<!-- width: content -->";
|
|
2159
|
+
function parseTableSettingsMarker(text) {
|
|
2160
|
+
const match = MARKER_REG_EXP.exec(text);
|
|
2161
|
+
if (!match) return null;
|
|
2162
|
+
const settings = {};
|
|
2163
|
+
for (const pair of match[1].split(";")) {
|
|
2164
|
+
const [key, value] = pair.split(":").map((s) => s.trim());
|
|
2165
|
+
if (key === "width" && isOption("width", value)) settings.width = value;
|
|
2166
|
+
if (key === "density" && isOption("density", value)) settings.density = value;
|
|
2167
|
+
}
|
|
2168
|
+
return Object.keys(settings).length > 0 ? settings : null;
|
|
2169
|
+
}
|
|
2170
|
+
function formatTableSettingsMarker(settings) {
|
|
2171
|
+
const pairs = SETTING_KEYS.filter(
|
|
2172
|
+
(key) => settings[key] !== DEFAULT_TABLE_SETTINGS[key]
|
|
2173
|
+
).map((key) => `${key}: ${settings[key]}`);
|
|
2174
|
+
return pairs.length > 0 ? `<!-- ${pairs.join("; ")} -->` : null;
|
|
2175
|
+
}
|
|
2080
2176
|
|
|
2081
2177
|
// src/transformers/tableTransformer.ts
|
|
2082
2178
|
var TABLE_ROW_REG_EXP = /^\|(.+)\|\s?$/;
|
|
2083
2179
|
var TABLE_ROW_DIVIDER_REG_EXP = /^(\| ?:?-*:? ?)+\|\s?$/;
|
|
2084
|
-
function $
|
|
2085
|
-
if (!$isParagraphNode2(node) || node.getChildrenSize() !== 1) return
|
|
2180
|
+
function $getMarkerSettings(node) {
|
|
2181
|
+
if (!$isParagraphNode2(node) || node.getChildrenSize() !== 1) return null;
|
|
2086
2182
|
const child = node.getFirstChild();
|
|
2087
|
-
return $isTextNode(child)
|
|
2183
|
+
return $isTextNode(child) ? parseTableSettingsMarker(child.getTextContent()) : null;
|
|
2088
2184
|
}
|
|
2089
2185
|
function getTableColumnsSize(table) {
|
|
2090
2186
|
const row = table.getFirstChild();
|
|
@@ -2106,7 +2202,8 @@ var TABLE = {
|
|
|
2106
2202
|
export: (node) => {
|
|
2107
2203
|
if (!$isTableNode2(node)) return null;
|
|
2108
2204
|
const output = [];
|
|
2109
|
-
|
|
2205
|
+
const marker = formatTableSettingsMarker($getTableSettings(node));
|
|
2206
|
+
if (marker) output.push(marker);
|
|
2110
2207
|
for (const row of node.getChildren()) {
|
|
2111
2208
|
if (!$isTableRowNode(row)) continue;
|
|
2112
2209
|
const rowOutput = [];
|
|
@@ -2172,8 +2269,9 @@ var TABLE = {
|
|
|
2172
2269
|
previousSibling.append(...table.getChildren());
|
|
2173
2270
|
parentNode.remove();
|
|
2174
2271
|
} else {
|
|
2175
|
-
|
|
2176
|
-
|
|
2272
|
+
const markerSettings = $getMarkerSettings(previousSibling);
|
|
2273
|
+
if (markerSettings) {
|
|
2274
|
+
$setTableSettings(table, markerSettings);
|
|
2177
2275
|
previousSibling?.remove();
|
|
2178
2276
|
}
|
|
2179
2277
|
parentNode.replace(table);
|
|
@@ -2521,7 +2619,7 @@ function FoldingPlugin() {
|
|
|
2521
2619
|
)) });
|
|
2522
2620
|
}
|
|
2523
2621
|
|
|
2524
|
-
// src/plugins/
|
|
2622
|
+
// src/plugins/TableSettingsPlugin.tsx
|
|
2525
2623
|
import { useCallback as useCallback5, useEffect as useEffect9, useState as useState5 } from "react";
|
|
2526
2624
|
import { useLexicalComposerContext as useLexicalComposerContext9 } from "@lexical/react/LexicalComposerContext";
|
|
2527
2625
|
import { useLexicalEditable as useLexicalEditable2 } from "@lexical/react/useLexicalEditable";
|
|
@@ -2555,13 +2653,13 @@ function useMarkdownEditor() {
|
|
|
2555
2653
|
);
|
|
2556
2654
|
const [canUndo, setCanUndo] = useState4(false);
|
|
2557
2655
|
const [canRedo, setCanRedo] = useState4(false);
|
|
2558
|
-
const [
|
|
2656
|
+
const [tableSettings, setTableSettingsState] = useState4(null);
|
|
2559
2657
|
useEffect8(
|
|
2560
2658
|
() => mergeRegister(
|
|
2561
2659
|
editor.registerUpdateListener(({ editorState }) => {
|
|
2562
2660
|
editorState.read(() => {
|
|
2563
2661
|
const table = $getSelectedTable();
|
|
2564
|
-
|
|
2662
|
+
setTableSettingsState(table ? $getTableSettings(table) : null);
|
|
2565
2663
|
const selection = $getSelection4();
|
|
2566
2664
|
if (!$isRangeSelection4(selection)) {
|
|
2567
2665
|
setActiveFormats(/* @__PURE__ */ new Set());
|
|
@@ -2614,15 +2712,23 @@ function useMarkdownEditor() {
|
|
|
2614
2712
|
$insertNodeToNearestRoot($createDrawingNode());
|
|
2615
2713
|
});
|
|
2616
2714
|
}, [editor]);
|
|
2617
|
-
const
|
|
2618
|
-
(
|
|
2715
|
+
const updateTableSettings = useCallback4(
|
|
2716
|
+
(settings) => {
|
|
2619
2717
|
editor.update(() => {
|
|
2620
2718
|
const table = $getSelectedTable();
|
|
2621
|
-
if (table) $
|
|
2719
|
+
if (table) $setTableSettings(table, settings);
|
|
2622
2720
|
});
|
|
2623
2721
|
},
|
|
2624
2722
|
[editor]
|
|
2625
2723
|
);
|
|
2724
|
+
const setTableWidth = useCallback4(
|
|
2725
|
+
(width) => updateTableSettings({ width }),
|
|
2726
|
+
[updateTableSettings]
|
|
2727
|
+
);
|
|
2728
|
+
const setTableDensity = useCallback4(
|
|
2729
|
+
(density) => updateTableSettings({ density }),
|
|
2730
|
+
[updateTableSettings]
|
|
2731
|
+
);
|
|
2626
2732
|
const undo = useCallback4(() => {
|
|
2627
2733
|
editor.dispatchCommand(UNDO_COMMAND, void 0);
|
|
2628
2734
|
}, [editor]);
|
|
@@ -2635,8 +2741,10 @@ function useMarkdownEditor() {
|
|
|
2635
2741
|
toggleFormat,
|
|
2636
2742
|
insertTable,
|
|
2637
2743
|
insertDrawing,
|
|
2638
|
-
tableWidth,
|
|
2744
|
+
tableWidth: tableSettings?.width ?? null,
|
|
2639
2745
|
setTableWidth,
|
|
2746
|
+
tableDensity: tableSettings?.density ?? null,
|
|
2747
|
+
setTableDensity,
|
|
2640
2748
|
canUndo,
|
|
2641
2749
|
canRedo,
|
|
2642
2750
|
undo,
|
|
@@ -2765,12 +2873,45 @@ function HistoryButtons() {
|
|
|
2765
2873
|
] });
|
|
2766
2874
|
}
|
|
2767
2875
|
|
|
2768
|
-
// src/plugins/
|
|
2769
|
-
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
2770
|
-
|
|
2876
|
+
// src/plugins/TableSettingsPlugin.tsx
|
|
2877
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2878
|
+
var DENSITY_OPTIONS = [
|
|
2879
|
+
{
|
|
2880
|
+
density: "compact",
|
|
2881
|
+
label: "Compact rows",
|
|
2882
|
+
icon: /* @__PURE__ */ jsx7("path", { d: "M3 4.5h14M3 8.17h14M3 11.83h14M3 15.5h14" })
|
|
2883
|
+
},
|
|
2884
|
+
{
|
|
2885
|
+
density: "comfortable",
|
|
2886
|
+
label: "Comfortable rows",
|
|
2887
|
+
icon: /* @__PURE__ */ jsx7("path", { d: "M3 4.5h14M3 10h14M3 15.5h14" })
|
|
2888
|
+
},
|
|
2889
|
+
{
|
|
2890
|
+
density: "spacious",
|
|
2891
|
+
label: "Spacious rows",
|
|
2892
|
+
icon: /* @__PURE__ */ jsx7("path", { d: "M3 5h14M3 15h14" })
|
|
2893
|
+
}
|
|
2894
|
+
];
|
|
2895
|
+
function Icon2({ children }) {
|
|
2896
|
+
return /* @__PURE__ */ jsx7(
|
|
2897
|
+
"svg",
|
|
2898
|
+
{
|
|
2899
|
+
viewBox: "0 0 20 20",
|
|
2900
|
+
width: "16",
|
|
2901
|
+
height: "16",
|
|
2902
|
+
fill: "none",
|
|
2903
|
+
stroke: "currentColor",
|
|
2904
|
+
strokeWidth: "1.6",
|
|
2905
|
+
strokeLinecap: "round",
|
|
2906
|
+
strokeLinejoin: "round",
|
|
2907
|
+
children
|
|
2908
|
+
}
|
|
2909
|
+
);
|
|
2910
|
+
}
|
|
2911
|
+
function TableSettingsPlugin() {
|
|
2771
2912
|
const [editor] = useLexicalComposerContext9();
|
|
2772
2913
|
const isEditable = useLexicalEditable2();
|
|
2773
|
-
const { tableWidth, setTableWidth } = useMarkdownEditor();
|
|
2914
|
+
const { tableWidth, setTableWidth, tableDensity, setTableDensity } = useMarkdownEditor();
|
|
2774
2915
|
const [anchor, setAnchor] = useState5(null);
|
|
2775
2916
|
const sync = useCallback5(() => {
|
|
2776
2917
|
const tableKey = editor.getEditorState().read(() => $getSelectedTable()?.getKey());
|
|
@@ -2798,43 +2939,43 @@ function TableWidthPlugin() {
|
|
|
2798
2939
|
observer?.disconnect();
|
|
2799
2940
|
};
|
|
2800
2941
|
}, [editor, sync]);
|
|
2801
|
-
if (!isEditable || !anchor || !tableWidth) return null;
|
|
2802
|
-
return /* @__PURE__ */
|
|
2942
|
+
if (!isEditable || !anchor || !tableWidth || !tableDensity) return null;
|
|
2943
|
+
return /* @__PURE__ */ jsxs5(
|
|
2803
2944
|
"div",
|
|
2804
2945
|
{
|
|
2805
|
-
className: "zui-table-
|
|
2806
|
-
role: "
|
|
2807
|
-
"aria-label": "Table
|
|
2946
|
+
className: "zui-table-settings",
|
|
2947
|
+
role: "toolbar",
|
|
2948
|
+
"aria-label": "Table settings",
|
|
2808
2949
|
style: { top: anchor.top, right: anchor.right },
|
|
2809
|
-
children:
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
}
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2950
|
+
children: [
|
|
2951
|
+
BLOCK_WIDTH_OPTIONS.map(({ width, label, icon }) => /* @__PURE__ */ jsx7(
|
|
2952
|
+
ToolbarButton,
|
|
2953
|
+
{
|
|
2954
|
+
label,
|
|
2955
|
+
active: tableWidth === width,
|
|
2956
|
+
onClick: () => setTableWidth(width),
|
|
2957
|
+
children: /* @__PURE__ */ jsx7(Icon2, { children: icon })
|
|
2958
|
+
},
|
|
2959
|
+
width
|
|
2960
|
+
)),
|
|
2961
|
+
/* @__PURE__ */ jsx7(ToolbarDivider, {}),
|
|
2962
|
+
DENSITY_OPTIONS.map(({ density, label, icon }) => /* @__PURE__ */ jsx7(
|
|
2963
|
+
ToolbarButton,
|
|
2964
|
+
{
|
|
2965
|
+
label,
|
|
2966
|
+
active: tableDensity === density,
|
|
2967
|
+
onClick: () => setTableDensity(density),
|
|
2968
|
+
children: /* @__PURE__ */ jsx7(Icon2, { children: icon })
|
|
2969
|
+
},
|
|
2970
|
+
density
|
|
2971
|
+
))
|
|
2972
|
+
]
|
|
2832
2973
|
}
|
|
2833
2974
|
);
|
|
2834
2975
|
}
|
|
2835
2976
|
|
|
2836
2977
|
// src/EditorContent.tsx
|
|
2837
|
-
import { jsx as jsx8, jsxs as
|
|
2978
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2838
2979
|
function EditorContent({
|
|
2839
2980
|
placeholder = "Start writing...",
|
|
2840
2981
|
foldable = true,
|
|
@@ -2855,8 +2996,8 @@ function EditorContent({
|
|
|
2855
2996
|
}
|
|
2856
2997
|
);
|
|
2857
2998
|
}
|
|
2858
|
-
return /* @__PURE__ */
|
|
2859
|
-
/* @__PURE__ */
|
|
2999
|
+
return /* @__PURE__ */ jsxs6("div", { className: "zui-text-editor-body", children: [
|
|
3000
|
+
/* @__PURE__ */ jsxs6("div", { className: "zui-text-editor-main", children: [
|
|
2860
3001
|
/* @__PURE__ */ jsx8(
|
|
2861
3002
|
RichTextPlugin,
|
|
2862
3003
|
{
|
|
@@ -2872,7 +3013,7 @@ function EditorContent({
|
|
|
2872
3013
|
}
|
|
2873
3014
|
),
|
|
2874
3015
|
foldable && /* @__PURE__ */ jsx8(FoldingPlugin, {}),
|
|
2875
|
-
/* @__PURE__ */ jsx8(
|
|
3016
|
+
/* @__PURE__ */ jsx8(TableSettingsPlugin, {})
|
|
2876
3017
|
] }),
|
|
2877
3018
|
children
|
|
2878
3019
|
] });
|
|
@@ -2884,7 +3025,7 @@ import { useLexicalComposerContext as useLexicalComposerContext10 } from "@lexic
|
|
|
2884
3025
|
import {
|
|
2885
3026
|
TableOfContentsPlugin
|
|
2886
3027
|
} from "@lexical/react/LexicalTableOfContentsPlugin";
|
|
2887
|
-
import { jsx as jsx9, jsxs as
|
|
3028
|
+
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2888
3029
|
var INDENT_PER_LEVEL = {
|
|
2889
3030
|
h1: 0,
|
|
2890
3031
|
h2: 1,
|
|
@@ -2940,7 +3081,7 @@ function OutlinePlugin() {
|
|
|
2940
3081
|
for (const [key] of entries) {
|
|
2941
3082
|
editor.getElementByKey(key)?.setAttribute("data-outline-key", key);
|
|
2942
3083
|
}
|
|
2943
|
-
return /* @__PURE__ */
|
|
3084
|
+
return /* @__PURE__ */ jsxs7(
|
|
2944
3085
|
"div",
|
|
2945
3086
|
{
|
|
2946
3087
|
className: `zui-text-editor-outline ${collapsed ? "is-collapsed" : ""}`,
|
|
@@ -2969,7 +3110,7 @@ function OutlinePlugin() {
|
|
|
2969
3110
|
)
|
|
2970
3111
|
}
|
|
2971
3112
|
),
|
|
2972
|
-
!collapsed && /* @__PURE__ */
|
|
3113
|
+
!collapsed && /* @__PURE__ */ jsxs7("nav", { className: "zui-text-editor-outline-list", "aria-label": "Table of contents", children: [
|
|
2973
3114
|
entries.length === 0 && /* @__PURE__ */ jsx9("div", { className: "zui-text-editor-outline-empty", children: "No headings" }),
|
|
2974
3115
|
entries.map(([key, text, tag]) => /* @__PURE__ */ jsx9(
|
|
2975
3116
|
"button",
|
|
@@ -2992,7 +3133,7 @@ function OutlinePlugin() {
|
|
|
2992
3133
|
}
|
|
2993
3134
|
|
|
2994
3135
|
// src/MarkdownEditor.tsx
|
|
2995
|
-
import { jsx as jsx10, jsxs as
|
|
3136
|
+
import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2996
3137
|
var DEFAULT_ITEMS = {
|
|
2997
3138
|
format: /* @__PURE__ */ jsx10(FormatButtons, {}),
|
|
2998
3139
|
insert: /* @__PURE__ */ jsx10(InsertButtons, {}),
|
|
@@ -3013,7 +3154,7 @@ function MarkdownEditor({
|
|
|
3013
3154
|
foldable = true,
|
|
3014
3155
|
...rootProps
|
|
3015
3156
|
}) {
|
|
3016
|
-
return /* @__PURE__ */
|
|
3157
|
+
return /* @__PURE__ */ jsxs8(EditorRoot, { ...rootProps, children: [
|
|
3017
3158
|
toolbar === true && /* @__PURE__ */ jsx10(EditorToolbar, {}),
|
|
3018
3159
|
typeof toolbar === "function" && toolbar(DEFAULT_ITEMS),
|
|
3019
3160
|
/* @__PURE__ */ jsx10(EditorContent, { placeholder, foldable, children: outline && /* @__PURE__ */ jsx10(OutlinePlugin, {}) })
|
|
@@ -3147,10 +3288,15 @@ export {
|
|
|
3147
3288
|
$createDrawingNode,
|
|
3148
3289
|
$createFrontmatterNode,
|
|
3149
3290
|
$getSelectedTable,
|
|
3291
|
+
$getTableDensity,
|
|
3292
|
+
$getTableSettings,
|
|
3150
3293
|
$getTableWidth,
|
|
3151
3294
|
$isDrawingNode,
|
|
3152
3295
|
$isFrontmatterNode,
|
|
3296
|
+
$setTableDensity,
|
|
3297
|
+
$setTableSettings,
|
|
3153
3298
|
$setTableWidth,
|
|
3299
|
+
DEFAULT_TABLE_SETTINGS,
|
|
3154
3300
|
DRAWING,
|
|
3155
3301
|
DRAWING_DATA_JSON_SCHEMA,
|
|
3156
3302
|
DrawingNode,
|
|
@@ -3165,7 +3311,9 @@ export {
|
|
|
3165
3311
|
Toolbar,
|
|
3166
3312
|
ToolbarButton,
|
|
3167
3313
|
ToolbarDivider,
|
|
3314
|
+
formatTableSettingsMarker,
|
|
3168
3315
|
parseDrawingData,
|
|
3316
|
+
parseTableSettingsMarker,
|
|
3169
3317
|
serializeDrawingData,
|
|
3170
3318
|
useMarkdownEditor
|
|
3171
3319
|
};
|
package/dist/styles.css
CHANGED
|
@@ -282,12 +282,13 @@
|
|
|
282
282
|
border: 1px solid color-mix(in srgb, currentColor 15%, transparent);
|
|
283
283
|
border-radius: 0.625rem;
|
|
284
284
|
overflow: hidden;
|
|
285
|
-
|
|
285
|
+
/* Density presets set these custom properties inline (tableSettings.ts) */
|
|
286
|
+
font-size: var(--zui-table-font-size, 0.875rem);
|
|
286
287
|
table-layout: fixed;
|
|
287
288
|
}
|
|
288
289
|
|
|
289
290
|
.zui-table-cell {
|
|
290
|
-
padding: 0.5rem 0.75rem;
|
|
291
|
+
padding: var(--zui-table-cell-padding, 0.5rem 0.75rem);
|
|
291
292
|
border-bottom: 1px solid color-mix(in srgb, currentColor 10%, transparent);
|
|
292
293
|
border-right: 1px solid color-mix(in srgb, currentColor 10%, transparent);
|
|
293
294
|
vertical-align: top;
|
|
@@ -319,13 +320,13 @@
|
|
|
319
320
|
}
|
|
320
321
|
|
|
321
322
|
/*
|
|
322
|
-
* Floating
|
|
323
|
+
* Floating table settings (width, density), straddling the top-right corner
|
|
323
324
|
* of the table the caret is in (positioned within .zui-text-editor-main). It
|
|
324
325
|
* stays inside the table's own top margin so it never covers the previous
|
|
325
|
-
* paragraph.
|
|
326
|
-
*
|
|
326
|
+
* paragraph. The settings themselves are an inline style on the <table>
|
|
327
|
+
* (see tableSettings.ts).
|
|
327
328
|
*/
|
|
328
|
-
.zui-table-
|
|
329
|
+
.zui-table-settings {
|
|
329
330
|
position: absolute;
|
|
330
331
|
z-index: 5;
|
|
331
332
|
display: flex;
|
|
@@ -338,7 +339,7 @@
|
|
|
338
339
|
transform: translateY(-70%);
|
|
339
340
|
}
|
|
340
341
|
|
|
341
|
-
.dark .zui-table-
|
|
342
|
+
.dark .zui-table-settings {
|
|
342
343
|
background: #232329;
|
|
343
344
|
border-color: rgba(255, 255, 255, 0.1);
|
|
344
345
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
|