@openleaf-editor/plugins-table 0.1.0-beta.1 → 0.1.0-beta.3

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 ADDED
@@ -0,0 +1,98 @@
1
+ # @openleaf-editor/plugins-table
2
+
3
+ Opt-in table editing: cell selection, column resizing, property dialogs,
4
+ captions, an insert grid, a context menu, and the row and column commands.
5
+
6
+ > **Editor output is untrusted input.** Whatever the editor produces — and
7
+ > whatever a user pasted into it — must be sanitized **on your server** before it
8
+ > is stored or rendered as HTML. Client-side sanitization is a user-experience
9
+ > feature, not a security control: anything the editor strips can be put back
10
+ > with developer tools, because the editor runs under the user's control.
11
+ >
12
+ > [`@openleaf-editor/sanitize`](https://github.com/PeytonNowlin/openleaf/tree/main/packages/sanitize) ships the
13
+ > canonical allowlist as data and generates configuration for DOMPurify, Python
14
+ > `bleach` and PHP HTMLPurifier from it, so client and server enforce the same
15
+ > rules. Read [SECURITY.md](https://github.com/PeytonNowlin/openleaf/blob/main/SECURITY.md) before you ship.
16
+
17
+ ## Install
18
+
19
+ ```sh
20
+ npm install @openleaf-editor/plugins-table@beta
21
+ ```
22
+
23
+ Keep every `@openleaf-editor/*` package on the same version. They pin each other
24
+ exactly, so mixing versions installs two copies of the schema and the toolbar
25
+ registry -- and a node built by one is not a node type the other accepts.
26
+
27
+ ## Use it
28
+
29
+ With a bundler:
30
+
31
+ ```ts
32
+ import { installTableEditing } from '@openleaf-editor/plugins-table'
33
+ installTableEditing()
34
+ ```
35
+
36
+ With a script tag, load it after the core bundle -- it borrows the first one's
37
+ ProseMirror runtime rather than shipping a second copy:
38
+
39
+ ```html
40
+ <script src="/js/openleaf.min.js"></script>
41
+ <script src="/js/openleaf-tables.min.js"></script>
42
+ ```
43
+
44
+ Then name the controls you want in the `toolbar` attribute. Installing the
45
+ plugin registers capability; it does not rearrange your toolbar.
46
+
47
+ ## The schema is not in here
48
+
49
+ Table node types live in [`@openleaf-editor/core`](../core), and that was not the
50
+ original plan. The fidelity harness changed it: without them a `<table>` in stored
51
+ content is claimed by the preservation layer and becomes an opaque, uneditable
52
+ card. Faithful, and useless.
53
+
54
+ So the split falls at the editing machinery instead. Every deployment reads and
55
+ writes tables; only the ones that want table *editing* download this.
56
+
57
+ ## Captions and colgroup
58
+
59
+ Both round-trip byte-for-byte, as furniture attributes on the table node rather
60
+ than child nodes. They render but are not editable in place: a caption has to be a
61
+ child node for that, and `prosemirror-tables` derives its cell map from
62
+ `table.childCount`, so it needs an upstream fix first. The caption dialog is how
63
+ you change one meanwhile.
64
+
65
+ Inserting or deleting a column reindexes a stored `<colgroup>` with the cells.
66
+ Without that, every remaining column inherited the previous `<col>`'s width and
67
+ class. Column resize still patches widths onto the same elements, so the two
68
+ paths do not fight.
69
+
70
+ ## License
71
+
72
+ Apache-2.0.
73
+
74
+ ## Toolbar item ids
75
+
76
+ Installing registers the controls; it does not rearrange a custom toolbar. Name
77
+ the ones you want in the element's `toolbar` attribute:
78
+
79
+ | Id | Control |
80
+ | --- | --- |
81
+ | `insertTable` | Insert-table grid |
82
+ | `tableProperties` | Table properties dialog |
83
+ | `rowProperties` | Row properties dialog |
84
+ | `cellProperties` | Cell properties dialog |
85
+ | `tableCaption` | Caption dialog |
86
+ | `addRowBefore`, `addRowAfter`, `deleteRow` | Row commands. They keep `headerRows` / `footerRows` in step so `<thead>` and `<tfoot>` stay on the header and footer rows the author actually has. |
87
+ | `addColumnBefore`, `addColumnAfter`, `deleteColumn` | Column commands |
88
+ | `mergeCells`, `splitCell` | Cell merge and split |
89
+ | `toggleHeaderRow` | Promote or demote the header row |
90
+ | `deleteTable` | Delete the whole table |
91
+
92
+ ```html
93
+ <openleaf-editor for="body"
94
+ toolbar="bold italic | insertTable tableProperties | source">
95
+ </openleaf-editor>
96
+ ```
97
+
98
+ An id nothing has registered logs a warning rather than being skipped silently.
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Table commands that are not already in `prosemirror-tables`.
3
+ *
4
+ * Property edits, caption/colgroup, nested insert and vertical alignment live
5
+ * here so a test can apply them without standing up a toolbar. Column insert and
6
+ * delete also reindex the stored colgroup here: the upstream commands only move
7
+ * cells, and `colgroupFromCellWidths` never runs unless a cell already has
8
+ * `colwidth`.
9
+ */
10
+ import type { Node, ResolvedPos } from 'prosemirror-model';
11
+ import type { Command, EditorState } from 'prosemirror-state';
12
+ import { Plugin } from 'prosemirror-state';
13
+ export declare function inTable(state: EditorState): boolean;
14
+ export declare function findRole($pos: ResolvedPos, role: 'table' | 'row' | 'cell' | 'header_cell'): {
15
+ node: Node;
16
+ pos: number;
17
+ depth: number;
18
+ } | null;
19
+ export declare function findTable($pos: ResolvedPos): {
20
+ node: Node;
21
+ pos: number;
22
+ depth: number;
23
+ } | null;
24
+ export declare function findRow($pos: ResolvedPos): {
25
+ node: Node;
26
+ pos: number;
27
+ depth: number;
28
+ } | null;
29
+ export declare function findCell($pos: ResolvedPos): {
30
+ node: Node;
31
+ pos: number;
32
+ depth: number;
33
+ } | null;
34
+ export declare function insertTable(rows?: number, cols?: number): Command;
35
+ export declare function withCellScope(command: Command): Command;
36
+ export declare const addColumnAfter: Command;
37
+ export declare const addColumnBefore: Command;
38
+ export declare const deleteColumn: Command;
39
+ export declare const addRowAfter: Command;
40
+ export declare const addRowBefore: Command;
41
+ export declare const deleteRow: Command;
42
+ export declare const toggleHeaderRow: Command;
43
+ export declare function selectedCellPositions(state: EditorState): number[];
44
+ /**
45
+ * Patch declarations onto a style attribute, validating every value written.
46
+ *
47
+ * The validation is here rather than in each caller because this is the choke
48
+ * point: a value reaches the stored style attribute only through this function,
49
+ * and `serializeDeclarations` joins on `;`, so an unchecked value carrying one
50
+ * becomes extra declarations. `padding: 0;position:fixed;inset:0` is a
51
+ * page-covering overlay, written from a property dialog and saved.
52
+ *
53
+ * `safeTableStyleValue` is core's own parse-path validator, so a dialog cannot
54
+ * disagree with the schema about what an acceptable value is.
55
+ */
56
+ export declare function mergeStyle(existing: string | null | undefined, patch: Record<string, string | null>): string | null;
57
+ /** A style value the schema will keep, or null. For a dialog's commit step. */
58
+ export declare function styleValueOrNull(property: string, value: string | undefined): string | null;
59
+ export declare function setTableAttrs(attrs: Record<string, unknown>): Command;
60
+ export declare function setRowAttrs(attrs: Record<string, unknown>): Command;
61
+ export declare function setCellAttrs(attrs: Record<string, unknown>): Command;
62
+ export declare function setCellVerticalAlign(value: string | null): Command;
63
+ export declare function captionTextFromHtml(html: string | null | undefined): string;
64
+ export declare function captionHtmlFromText(text: string, previous: string | null | undefined): string | null;
65
+ export declare function setTableCaption(text: string): Command;
66
+ export declare function colgroupHtmlFromWidths(widths: Array<string | null | undefined>): string | null;
67
+ /**
68
+ * Column widths, one per column.
69
+ *
70
+ * `span` is honoured: `<col span="2" width="120">` sets two columns to 120, not
71
+ * one. Reading the elements positionally would report the second column as
72
+ * having no width, and then saving would write that back.
73
+ */
74
+ export declare function widthsFromColgroup(html: string | null | undefined, columns: number): string[];
75
+ /**
76
+ * Write widths into an existing colgroup rather than replacing it.
77
+ *
78
+ * Inherited markup carries more than widths -- `<colgroup class="layout">`,
79
+ * `<col span="2">`, whatever else a previous CMS wrote -- and the table
80
+ * properties dialog saves the whole table, so rebuilding the colgroup from
81
+ * widths alone dropped all of it on a save that changed nothing else.
82
+ *
83
+ * An unchanged set of widths returns the stored markup untouched, so saving the
84
+ * dialog is genuinely a no-op. When a width does change, the existing elements
85
+ * are patched: a spanned `<col>` keeps its span while its columns still agree,
86
+ * and is split into one `<col>` per column -- carrying its other attributes --
87
+ * only when they no longer do.
88
+ */
89
+ export declare function colgroupHtmlWithWidths(existing: string | null | undefined, widths: Array<string | null | undefined>): string | null;
90
+ /**
91
+ * Insert a bare `<col>` so columns after `at` keep the `<col>` they already had.
92
+ *
93
+ * A spanned element that covers `at` is split around the insertion rather than
94
+ * widened: the new column must not inherit the neighbour's class or width, which
95
+ * is the same shift insert-without-a-patch produced for unspanned columns.
96
+ */
97
+ export declare function colgroupHtmlInsertColumn(existing: string | null | undefined, at: number): string | null;
98
+ /**
99
+ * Drop the `<col>` covering column `at`, or decrement its `span` when it covers
100
+ * more than one column.
101
+ */
102
+ export declare function colgroupHtmlDeleteColumn(existing: string | null | undefined, at: number): string | null;
103
+ export declare function setTableColgroup(widths: Array<string | null | undefined>): Command;
104
+ /**
105
+ * Keep `<colgroup>` in lockstep with column resizing.
106
+ *
107
+ * `prosemirror-tables` writes `colwidth` onto cells. That is enough for the
108
+ * editor; it is not enough for stored HTML, whose column widths live on
109
+ * `<col>`. Updating the furniture attribute after a resize is what makes
110
+ * colgroup a first-class editing feature rather than a round-trip souvenir.
111
+ *
112
+ * Authored colgroups that are not the product of a resize are left alone:
113
+ * inventing `<col>` elements for a table that never had widths would change
114
+ * markup we had no reason to touch.
115
+ */
116
+ export declare function colgroupSyncPlugin(): Plugin;
117
+ export declare function emptyToNull(value: string | undefined): string | null;
118
+ export declare function colorOrNull(value: string | undefined): string | null;
119
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"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"}