@portone/docx-editor 0.2.0 → 0.2.1
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 +65 -0
- package/CONTRIBUTING.md +4 -0
- package/dist/docx/commentOnlyChange.d.ts +3 -1
- package/dist/docx/commentOnlyChange.js +38 -21
- package/dist/docx/comments/constants.d.ts +3 -3
- package/dist/docx/comments/constants.js +4 -3
- package/dist/docx/comments/grammar.d.ts +80 -0
- package/dist/docx/comments/grammar.js +213 -0
- package/dist/docx/comments/people.js +12 -14
- package/dist/docx/comments/reading.d.ts +5 -3
- package/dist/docx/comments/reading.js +23 -20
- package/dist/docx/comments/verifying.d.ts +44 -0
- package/dist/docx/comments/verifying.js +206 -0
- package/dist/docx/comments/writing.js +73 -46
- package/dist/docx/exportDocx.js +2 -1
- package/dist/docx/formatting/direct.js +3 -6
- package/dist/docx/headersFooters.js +8 -10
- package/dist/docx/notes.js +3 -5
- package/dist/docx/paraProps.js +51 -46
- package/dist/docx/propsXml.d.ts +20 -2
- package/dist/docx/propsXml.js +72 -32
- package/dist/docx/relationships.js +8 -3
- package/dist/docx/runProps.js +34 -34
- package/dist/docx/sdt.js +16 -14
- package/dist/docx/serializeBlock.js +1 -2
- package/dist/docx/serializeParagraph.d.ts +2 -1
- package/dist/docx/serializeParagraph.js +25 -14
- package/dist/docx/serializeTable.js +38 -24
- package/dist/docx/storyProjection.d.ts +36 -0
- package/dist/docx/storyProjection.js +23 -0
- package/dist/docx/tableFormatting/editing.js +49 -73
- package/dist/docx/tableTemplate.js +24 -3
- package/dist/docx/theme.d.ts +0 -6
- package/dist/docx/theme.js +0 -8
- package/dist/editor/clipboard/images.js +17 -3
- package/dist/editor/clipboard/inlineFormatting.d.ts +7 -0
- package/dist/editor/clipboard/inlineFormatting.js +1 -0
- package/dist/editor/commands/breakCommands.js +4 -3
- package/dist/editor/commands/canRunCommand.d.ts +2 -2
- package/dist/editor/commands/canRunCommand.js +1 -1
- package/dist/editor/commands/comments/editing.js +3 -6
- package/dist/editor/commands/historyCommands.js +2 -1
- package/dist/editor/commands/lockCommands.js +10 -1
- package/dist/editor/commands/tabCommands.js +4 -3
- package/dist/editor/createEditor.js +3 -9
- package/dist/editor/externalClipboard.js +166 -4
- package/dist/editor/insertImage.js +4 -3
- package/dist/editor/plugins/documentProtection.d.ts +3 -3
- package/dist/editor/plugins/lockedContent.d.ts +4 -4
- package/dist/editor/plugins/lockedContent.js +1 -1
- package/dist/numbering/listTemplate.js +32 -10
- package/dist/ooxml/element.d.ts +52 -0
- package/dist/ooxml/element.js +49 -0
- package/dist/ooxml/fragment.d.ts +53 -0
- package/dist/ooxml/fragment.js +76 -0
- package/dist/ooxml/image.js +4 -3
- package/dist/ooxml/names.d.ts +27 -0
- package/dist/ooxml/names.js +29 -0
- package/dist/ooxml/precedence.d.ts +31 -0
- package/dist/ooxml/precedence.js +51 -0
- package/dist/ooxml/xml.d.ts +31 -0
- package/dist/ooxml/xml.js +27 -0
- package/dist/page/blockKinds.d.ts +42 -0
- package/dist/page/blockKinds.js +0 -0
- package/dist/page/measureBlocks.d.ts +14 -1
- package/dist/page/measureBlocks.js +31 -11
- package/dist/page/pageDecorations.d.ts +16 -0
- package/dist/page/pageDecorations.js +1 -0
- package/dist/page/pageLayout.d.ts +8 -43
- package/dist/page/pageLayout.js +28 -51
- package/dist/page/tableMeasurements.d.ts +10 -2
- package/dist/page/tableMeasurements.js +20 -16
- package/dist/page/usePageLayout.d.ts +15 -0
- package/dist/page/usePageLayout.js +30 -2
- package/dist/schema/attrRoles.d.ts +35 -0
- package/dist/schema/attrRoles.js +106 -0
- package/dist/schema/docxSchema.d.ts +6 -0
- package/dist/schema/docxSchema.js +212 -101
- package/dist/schema/editGuard.d.ts +99 -0
- package/dist/schema/editGuard.js +43 -0
- package/dist/schema/guards.d.ts +68 -0
- package/dist/schema/guards.js +82 -0
- package/dist/schema/locks.d.ts +8 -35
- package/dist/schema/locks.js +29 -64
- package/dist/schema/preservedGuards.d.ts +30 -0
- package/dist/schema/preservedGuards.js +50 -0
- package/dist/schema/protection.d.ts +10 -3
- package/dist/schema/protection.js +2 -1
- package/dist/schema/sourceEquality.d.ts +27 -0
- package/dist/schema/sourceEquality.js +65 -0
- package/dist/table/cellFormatting.js +2 -1
- package/dist/table/commands.d.ts +2 -2
- package/dist/table/commands.js +1 -1
- package/dist/table/index.d.ts +2 -2
- package/dist/table/merge.d.ts +2 -2
- package/dist/table/merge.js +1 -1
- package/package.json +4 -1
- package/dist/editor/plugins/bookmarkProtection.d.ts +0 -3
- package/dist/editor/plugins/bookmarkProtection.js +0 -28
- package/dist/editor/plugins/noteProtection.d.ts +0 -3
- package/dist/editor/plugins/noteProtection.js +0 -25
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
* document breaks at Letter's height and fits its tables to Letter's width.
|
|
11
11
|
*/
|
|
12
12
|
import { type PageGeometry } from "../docx/pageGeometry";
|
|
13
|
+
import type { MeasuredBlock, PageCut } from "./blockKinds";
|
|
14
|
+
export type { MeasuredBlock };
|
|
13
15
|
/** The paper measured in the pixels the sheet is drawn with */
|
|
14
16
|
export interface PagePixels {
|
|
15
17
|
/** The whole sheet, padding included */
|
|
@@ -45,44 +47,6 @@ export declare const A4_PAGE_PIXELS: PagePixels;
|
|
|
45
47
|
* moment before a document is opened.
|
|
46
48
|
*/
|
|
47
49
|
export declare function pageGeometryStyle(page: PagePixels): string;
|
|
48
|
-
/** One body block as drawn on screen */
|
|
49
|
-
export interface MeasuredBlock {
|
|
50
|
-
/** The position where this block starts in the document */
|
|
51
|
-
pos: number;
|
|
52
|
-
/** The height that naturally opens up between the previous block and this one */
|
|
53
|
-
gap: number;
|
|
54
|
-
height: number;
|
|
55
|
-
/** Set when the document records that a new page starts at this block */
|
|
56
|
-
breakBefore: boolean;
|
|
57
|
-
/**
|
|
58
|
-
* Every page break inside the block, in document order, each measured from the block's own top
|
|
59
|
-
* so that the block can be laid out wherever it lands.
|
|
60
|
-
* That top is the one the block would be drawn at with no space in it, so a space opened at one
|
|
61
|
-
* break shifts the ones after it.
|
|
62
|
-
*/
|
|
63
|
-
breaks: readonly number[];
|
|
64
|
-
/** Row boundaries available when this block is an editable table */
|
|
65
|
-
table?: MeasuredTable;
|
|
66
|
-
}
|
|
67
|
-
/** A row that may start the continued part of a table */
|
|
68
|
-
export interface TableBoundary {
|
|
69
|
-
/** The row's document position */
|
|
70
|
-
pos: number;
|
|
71
|
-
/** Its top measured from the table's natural top */
|
|
72
|
-
offset: number;
|
|
73
|
-
}
|
|
74
|
-
/** The measurements needed to continue a table without changing the document */
|
|
75
|
-
export interface MeasuredTable {
|
|
76
|
-
boundaries: readonly TableBoundary[];
|
|
77
|
-
/** The smallest useful first piece: headers followed by one body row group */
|
|
78
|
-
firstPageMinimum: number;
|
|
79
|
-
repeatHeaderHeight: number;
|
|
80
|
-
/** Document positions of the contiguous header rows at the start of the table */
|
|
81
|
-
headerRows: readonly number[];
|
|
82
|
-
/** Changes when any projected header content or formatting changes */
|
|
83
|
-
headerSignature: string;
|
|
84
|
-
columns: number;
|
|
85
|
-
}
|
|
86
50
|
/** The space opened up at one page break, so that what follows it starts the next page */
|
|
87
51
|
export interface BreakSpace {
|
|
88
52
|
/** The position where the block holding the break starts */
|
|
@@ -148,8 +112,8 @@ export interface PageStart {
|
|
|
148
112
|
export interface PageLayout {
|
|
149
113
|
/** Only the blocks to be moved down to the next page */
|
|
150
114
|
pushes: BlockPush[];
|
|
151
|
-
|
|
152
|
-
|
|
115
|
+
/** Where the layout parted a block, and the space it opened before the continued piece */
|
|
116
|
+
cuts: PageCut[];
|
|
153
117
|
splits: PageSplit[];
|
|
154
118
|
/** One per page, the first page included */
|
|
155
119
|
pages: PageStart[];
|
|
@@ -169,8 +133,9 @@ export interface PageLayoutInput {
|
|
|
169
133
|
* blocks below it move down by the same amount.
|
|
170
134
|
* A block taller than one page cannot be pushed, so it is left where it is and only the
|
|
171
135
|
* places it crosses are reported.
|
|
172
|
-
* A
|
|
173
|
-
*
|
|
174
|
-
* the next page while the block itself stays whole.
|
|
136
|
+
* A block is parted only where its measurer offered a candidate. A forced one - a page break
|
|
137
|
+
* written into the text - is filled out to the end of the page it falls on, which carries the rest
|
|
138
|
+
* of that block to the top of the next page while the block itself stays whole. An optional one -
|
|
139
|
+
* a row boundary in a table - is taken only where the piece after it would run off the page.
|
|
175
140
|
*/
|
|
176
141
|
export declare function pageLayout({ blocks, pageBodyHeight, pageStep, }: PageLayoutInput): PageLayout;
|
package/dist/page/pageLayout.js
CHANGED
|
@@ -47,8 +47,7 @@ function pageLayout({
|
|
|
47
47
|
pageStep
|
|
48
48
|
}) {
|
|
49
49
|
const pushes = [];
|
|
50
|
-
const
|
|
51
|
-
const tableContinuations = [];
|
|
50
|
+
const cuts = [];
|
|
52
51
|
const splits = [];
|
|
53
52
|
const firstPage = {
|
|
54
53
|
page: 1,
|
|
@@ -58,14 +57,7 @@ function pageLayout({
|
|
|
58
57
|
};
|
|
59
58
|
const pages = [firstPage];
|
|
60
59
|
if (!(pageBodyHeight > 0)) {
|
|
61
|
-
return {
|
|
62
|
-
pushes,
|
|
63
|
-
spaces,
|
|
64
|
-
tableContinuations,
|
|
65
|
-
splits,
|
|
66
|
-
pages,
|
|
67
|
-
bodyHeight: 0
|
|
68
|
-
};
|
|
60
|
+
return { pushes, cuts, splits, pages, bodyHeight: 0 };
|
|
69
61
|
}
|
|
70
62
|
let pageStart = 0;
|
|
71
63
|
let cursor = 0;
|
|
@@ -82,11 +74,12 @@ function pageLayout({
|
|
|
82
74
|
split(pageStart + pageBodyHeight, true, false);
|
|
83
75
|
}
|
|
84
76
|
};
|
|
77
|
+
let breakAfterPrevious = false;
|
|
85
78
|
for (const block of blocks) {
|
|
86
79
|
const pageEnd = pageStart + pageBodyHeight;
|
|
87
80
|
const top = cursor + block.gap;
|
|
88
|
-
const startsPage = block.breakBefore && top > pageStart + TOLERANCE_PX;
|
|
89
|
-
const first = block.
|
|
81
|
+
const startsPage = (block.breakBefore || breakAfterPrevious) && top > pageStart + TOLERANCE_PX;
|
|
82
|
+
const first = block.minFirstPiece;
|
|
90
83
|
const overflows = top + first > pageEnd + TOLERANCE_PX;
|
|
91
84
|
const fits = first <= pageBodyHeight + TOLERANCE_PX;
|
|
92
85
|
const push = startsPage || overflows && fits ? Math.max(0, pageEnd + pageStep - top) : 0;
|
|
@@ -98,51 +91,35 @@ function pageLayout({
|
|
|
98
91
|
push: round(push)
|
|
99
92
|
});
|
|
100
93
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
contentTop
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const boundary = index === 0 ? null : block.table.boundaries[index - 1];
|
|
120
|
-
const segmentHeight = segmentEnd - segmentStart;
|
|
121
|
-
if (overflows2 && boundary && segmentTop > pageStart + TOLERANCE_PX && segmentHeight <= pageBodyHeight + TOLERANCE_PX) {
|
|
122
|
-
split(pageEnd2, false, false);
|
|
123
|
-
const height = Math.max(0, pageStart - segmentTop);
|
|
124
|
-
tableContinuations.push({
|
|
125
|
-
pos: boundary.pos,
|
|
126
|
-
height: round(height),
|
|
127
|
-
headerRows: block.table.headerRows,
|
|
128
|
-
headerSignature: block.table.headerSignature,
|
|
129
|
-
columns: block.table.columns
|
|
130
|
-
});
|
|
131
|
-
tableAdded += height + block.table.repeatHeaderHeight;
|
|
132
|
-
segmentBottom = contentTop + segmentEnd + tableAdded;
|
|
94
|
+
const contentTop = top + push;
|
|
95
|
+
let added = 0;
|
|
96
|
+
let pieceStart = 0;
|
|
97
|
+
for (let index = 0; index <= block.candidates.length; index += 1) {
|
|
98
|
+
const cut = index === 0 ? null : block.candidates[index - 1];
|
|
99
|
+
const pieceEnd = block.candidates[index]?.offset ?? block.height;
|
|
100
|
+
const pieceTop = contentTop + pieceStart + added;
|
|
101
|
+
let pieceBottom = contentTop + pieceEnd + added;
|
|
102
|
+
if (cut) {
|
|
103
|
+
if (cut.forced) crossTo(pieceTop);
|
|
104
|
+
const pageBottom = pageStart + pageBodyHeight;
|
|
105
|
+
const cutHere = cut.forced || pieceBottom > pageBottom + TOLERANCE_PX && pieceTop > pageStart + TOLERANCE_PX && pieceEnd - pieceStart <= pageBodyHeight + TOLERANCE_PX;
|
|
106
|
+
if (cutHere) {
|
|
107
|
+
split(pageBottom, false, cut.forced);
|
|
108
|
+
const height = Math.max(0, pageStart - pieceTop);
|
|
109
|
+
cuts.push({ at: cut.at, height: round(height) });
|
|
110
|
+
added += height + cut.repeatHeight;
|
|
111
|
+
pieceBottom += height + cut.repeatHeight;
|
|
133
112
|
}
|
|
134
|
-
crossTo(segmentBottom);
|
|
135
|
-
segmentStart = segmentEnd;
|
|
136
113
|
}
|
|
114
|
+
crossTo(pieceBottom);
|
|
115
|
+
pieceStart = pieceEnd;
|
|
137
116
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
cursor = bottom;
|
|
117
|
+
cursor = contentTop + block.height + added;
|
|
118
|
+
breakAfterPrevious = block.breakAfter;
|
|
141
119
|
}
|
|
142
120
|
return {
|
|
143
121
|
pushes,
|
|
144
|
-
|
|
145
|
-
tableContinuations,
|
|
122
|
+
cuts,
|
|
146
123
|
splits,
|
|
147
124
|
pages,
|
|
148
125
|
bodyHeight: round(pageStart + pageBodyHeight)
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
/** Measures safe row boundaries without treating pagination decorations as document content. */
|
|
2
2
|
import type { Node as PMNode } from "prosemirror-model";
|
|
3
3
|
import type { EditorView } from "prosemirror-view";
|
|
4
|
-
import type {
|
|
4
|
+
import type { BreakCandidate } from "./blockKinds";
|
|
5
5
|
export interface TableMeasure {
|
|
6
|
-
table
|
|
6
|
+
/** Every row that may start the continued part of the table */
|
|
7
|
+
candidates: readonly BreakCandidate[];
|
|
8
|
+
/** The smallest useful first piece: headers followed by one body row group */
|
|
9
|
+
minFirstPiece: number;
|
|
7
10
|
/** Height already added by the previous pagination pass */
|
|
8
11
|
appliedHeight: number;
|
|
12
|
+
/** Document positions of the contiguous header rows at the start of the table */
|
|
13
|
+
headerRows: readonly number[];
|
|
14
|
+
/** Changes when any projected header content or formatting changes */
|
|
15
|
+
headerSignature: string;
|
|
16
|
+
columns: number;
|
|
9
17
|
}
|
|
10
18
|
export declare function measureTable(view: EditorView, tableNode: PMNode, tablePos: number, tableDom: HTMLElement, scale?: number): TableMeasure | null;
|
|
@@ -81,33 +81,37 @@ function measureTable(view, tableNode, tablePos, tableDom, scale = 1) {
|
|
|
81
81
|
if (rows.length === 0) return null;
|
|
82
82
|
const headers = headerCount(rows);
|
|
83
83
|
const unsafe = unsafeBoundaries(rows);
|
|
84
|
-
const
|
|
84
|
+
const headerRows = rows.slice(0, headers);
|
|
85
|
+
const repeatHeaderHeight = headerRows.reduce(
|
|
86
|
+
(total, row) => total + row.height,
|
|
87
|
+
0
|
|
88
|
+
);
|
|
89
|
+
const candidates = [];
|
|
85
90
|
for (let rowIndex = Math.max(1, headers); rowIndex < rows.length; rowIndex += 1) {
|
|
86
91
|
const row = rows[rowIndex];
|
|
87
92
|
if (row && !unsafe.has(rowIndex)) {
|
|
88
|
-
|
|
93
|
+
candidates.push({
|
|
94
|
+
at: row.pos,
|
|
95
|
+
offset: row.top,
|
|
96
|
+
forced: false,
|
|
97
|
+
repeatHeight: repeatHeaderHeight
|
|
98
|
+
});
|
|
89
99
|
}
|
|
90
100
|
}
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
(boundary) => boundary.offset > repeatHeaderHeight + 0.5
|
|
101
|
+
const firstBodyBoundary = candidates.find(
|
|
102
|
+
(candidate) => candidate.offset > repeatHeaderHeight + 0.5
|
|
94
103
|
);
|
|
95
104
|
const appliedHeight = appliedRows.reduce(
|
|
96
105
|
(total, row) => total + row.getBoundingClientRect().height / scale,
|
|
97
106
|
0
|
|
98
107
|
);
|
|
99
108
|
return {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
rows.slice(0, headers).map((row) => row.node.toJSON())
|
|
107
|
-
),
|
|
108
|
-
columns: columnCount(tableNode)
|
|
109
|
-
},
|
|
110
|
-
appliedHeight
|
|
109
|
+
candidates,
|
|
110
|
+
minFirstPiece: firstBodyBoundary?.offset ?? tableDom.getBoundingClientRect().height / scale - appliedHeight,
|
|
111
|
+
appliedHeight,
|
|
112
|
+
headerRows: headerRows.map((row) => row.pos),
|
|
113
|
+
headerSignature: JSON.stringify(headerRows.map((row) => row.node.toJSON())),
|
|
114
|
+
columns: columnCount(tableNode)
|
|
111
115
|
};
|
|
112
116
|
}
|
|
113
117
|
export {
|
|
@@ -8,9 +8,13 @@
|
|
|
8
8
|
* in Japanese or Chinese stays open across a whole clause while the text grows line by line. So no
|
|
9
9
|
* measurement is taken while a composition is open: the frame is taken again until it is over.
|
|
10
10
|
*/
|
|
11
|
+
import type { Node as PMNode } from "prosemirror-model";
|
|
11
12
|
import type { EditorView } from "prosemirror-view";
|
|
12
13
|
import { type RefObject } from "react";
|
|
13
14
|
import type { PageGeometry } from "../docx/pageGeometry";
|
|
15
|
+
import type { PageCut } from "./blockKinds";
|
|
16
|
+
import { type TableHeaderProjection } from "./measureBlocks";
|
|
17
|
+
import { type BreakSpace, type TableContinuation } from "./pageLayout";
|
|
14
18
|
/** One place where a page parts from the next. The position is measured on the sheet */
|
|
15
19
|
export interface PageMark {
|
|
16
20
|
page: number;
|
|
@@ -53,5 +57,16 @@ interface PageLayoutOptions {
|
|
|
53
57
|
/** The paper the open document names. A4 where a document names none */
|
|
54
58
|
geometry?: PageGeometry;
|
|
55
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* The layout's cuts as the two kinds of mark the decorations still take.
|
|
62
|
+
*
|
|
63
|
+
* A cut names the position the continued piece starts at, and which of the two it is follows from
|
|
64
|
+
* what stands there: a page `br` the block it sits in counted, or a table row. This stands in
|
|
65
|
+
* until the decorations take the cuts themselves.
|
|
66
|
+
*/
|
|
67
|
+
export declare function cutsToLegacyMarks(cuts: readonly PageCut[], tables: ReadonlyMap<number, TableHeaderProjection>, doc: PMNode): {
|
|
68
|
+
spaces: BreakSpace[];
|
|
69
|
+
tableContinuations: TableContinuation[];
|
|
70
|
+
};
|
|
56
71
|
export declare function usePageLayout({ view, layer, enabled, revision, geometry, }: PageLayoutOptions): PageOverlay | null;
|
|
57
72
|
export {};
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import { editorCssVariables } from "../styles/classNames.js";
|
|
10
10
|
import { measureSheet } from "./measureBlocks.js";
|
|
11
11
|
import {
|
|
12
|
+
pageBreaksIn,
|
|
12
13
|
setPageBreakSpaces,
|
|
13
14
|
setPagePushes,
|
|
14
15
|
setTableContinuations
|
|
@@ -52,6 +53,27 @@ function useFrameThrottle(run, hold) {
|
|
|
52
53
|
function sameOverlay(a, b) {
|
|
53
54
|
return a !== null && a.left === b.left && a.top === b.top && a.width === b.width && a.sheetHeight === b.sheetHeight && JSON.stringify(a.marks) === JSON.stringify(b.marks) && JSON.stringify(a.badges) === JSON.stringify(b.badges) && JSON.stringify(a.pages) === JSON.stringify(b.pages);
|
|
54
55
|
}
|
|
56
|
+
function cutsToLegacyMarks(cuts, tables, doc) {
|
|
57
|
+
const spaces = [];
|
|
58
|
+
const tableContinuations = [];
|
|
59
|
+
for (const cut of cuts) {
|
|
60
|
+
const pos = doc.resolve(cut.at).before(1);
|
|
61
|
+
const block = doc.nodeAt(pos);
|
|
62
|
+
if (!block) continue;
|
|
63
|
+
const index = pageBreaksIn(block, pos).findIndex(
|
|
64
|
+
(found) => found.at === cut.at
|
|
65
|
+
);
|
|
66
|
+
if (index >= 0) {
|
|
67
|
+
spaces.push({ pos, index, height: cut.height });
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const headers = tables.get(pos);
|
|
71
|
+
if (headers) {
|
|
72
|
+
tableContinuations.push({ pos: cut.at, height: cut.height, ...headers });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return { spaces, tableContinuations };
|
|
76
|
+
}
|
|
55
77
|
function usePageLayout({
|
|
56
78
|
view,
|
|
57
79
|
layer,
|
|
@@ -74,9 +96,14 @@ function usePageLayout({
|
|
|
74
96
|
pageBodyHeight: page.bodyHeight,
|
|
75
97
|
pageStep: page.pageStep
|
|
76
98
|
});
|
|
99
|
+
const marks = cutsToLegacyMarks(
|
|
100
|
+
layout.cuts,
|
|
101
|
+
measured.tables,
|
|
102
|
+
view.state.doc
|
|
103
|
+
);
|
|
77
104
|
setPagePushes(view, layout.pushes);
|
|
78
|
-
setPageBreakSpaces(view,
|
|
79
|
-
setTableContinuations(view,
|
|
105
|
+
setPageBreakSpaces(view, marks.spaces);
|
|
106
|
+
setTableContinuations(view, marks.tableContinuations);
|
|
80
107
|
const sheetHeight = measured.contentTop + layout.bodyHeight + measured.contentBottom;
|
|
81
108
|
box.style.setProperty(editorCssVariables.sheetHeight, `${sheetHeight}px`);
|
|
82
109
|
const next = {
|
|
@@ -137,5 +164,6 @@ function usePageLayout({
|
|
|
137
164
|
return enabled ? overlay : null;
|
|
138
165
|
}
|
|
139
166
|
export {
|
|
167
|
+
cutsToLegacyMarks,
|
|
140
168
|
usePageLayout
|
|
141
169
|
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What each attr of the schema is for, so that a judgement about a node can say which of them it
|
|
3
|
+
* is judging.
|
|
4
|
+
*
|
|
5
|
+
* An attr is one of three things. A `source` attr is what the exporter writes from: the XML the
|
|
6
|
+
* file arrived as, or a value the model owns that replaces part of it. A `display` attr is worked
|
|
7
|
+
* out from the source and the formatting around it, is never written, and is free to change the
|
|
8
|
+
* moment the surroundings do. A `session` attr says which block, control or link of the open
|
|
9
|
+
* document this is, and is never written either.
|
|
10
|
+
*
|
|
11
|
+
* The split exists because a display value changing is not the document changing. Opening a file
|
|
12
|
+
* in the editor re-derives a table's cell borders (`table/gridBorders`), and a comparison that
|
|
13
|
+
* counted that as an edit would rewrite a table nobody touched, losing the markup the writer does
|
|
14
|
+
* not model. `./sourceEquality` leaves display attrs out for that reason, and keeps session attrs
|
|
15
|
+
* in: two blocks that came from different places in the file are different blocks.
|
|
16
|
+
*
|
|
17
|
+
* `attrRoles.test.ts` holds this table against the schema in both directions, so an attr added
|
|
18
|
+
* without a role fails there before it can reach a judgement that does not know what to do with it.
|
|
19
|
+
*/
|
|
20
|
+
import { type MarkType, NodeType } from "prosemirror-model";
|
|
21
|
+
export type AttrRole = "source" | "display" | "session";
|
|
22
|
+
type AttrRoles = Readonly<Record<string, Readonly<Record<string, AttrRole>>>>;
|
|
23
|
+
export declare const NODE_ATTR_ROLES: AttrRoles;
|
|
24
|
+
export declare const MARK_ATTR_ROLES: AttrRoles;
|
|
25
|
+
/**
|
|
26
|
+
* What this attr of this node or mark is for.
|
|
27
|
+
*
|
|
28
|
+
* An attr with no role declared is read as `source`: a judgement then compares it, which is the
|
|
29
|
+
* answer that reports a change rather than hiding one. `attrRoles.test.ts` is what keeps the case
|
|
30
|
+
* from arising.
|
|
31
|
+
*/
|
|
32
|
+
export declare function attrRole(type: NodeType | MarkType, name: string): AttrRole;
|
|
33
|
+
/** The attrs of this node or mark that are worked out rather than written */
|
|
34
|
+
export declare function displayAttrsOf(type: NodeType | MarkType): readonly string[];
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// src/schema/attrRoles.ts
|
|
2
|
+
import { NodeType } from "prosemirror-model";
|
|
3
|
+
var NODE_ATTR_ROLES = {
|
|
4
|
+
paragraph: {
|
|
5
|
+
srcId: "session",
|
|
6
|
+
pAttrs: "source",
|
|
7
|
+
pPr: "source",
|
|
8
|
+
format: "display",
|
|
9
|
+
styleRun: "display"
|
|
10
|
+
},
|
|
11
|
+
table: {
|
|
12
|
+
srcId: "session",
|
|
13
|
+
tblAttrs: "source",
|
|
14
|
+
tblPr: "source",
|
|
15
|
+
tblW: "source",
|
|
16
|
+
gridCols: "source",
|
|
17
|
+
format: "display",
|
|
18
|
+
styleInside: "display",
|
|
19
|
+
styleCellMargins: "display"
|
|
20
|
+
},
|
|
21
|
+
tableRow: {
|
|
22
|
+
trAttrs: "source",
|
|
23
|
+
tblPrEx: "source",
|
|
24
|
+
trPr: "source",
|
|
25
|
+
format: "display"
|
|
26
|
+
},
|
|
27
|
+
tableCell: {
|
|
28
|
+
colspan: "source",
|
|
29
|
+
rowspan: "source",
|
|
30
|
+
// prosemirror-tables' own attr. Import writes null, the writer never reads it, and the table
|
|
31
|
+
// commands only carry it from one cell to another; a real column resize moves `gridCols`
|
|
32
|
+
colwidth: "display",
|
|
33
|
+
tcAttrs: "source",
|
|
34
|
+
tcPr: "source",
|
|
35
|
+
tcW: "source",
|
|
36
|
+
format: "display",
|
|
37
|
+
// Read from `sdtPrefix` rather than from the file, but a lock is not a display value: leaving
|
|
38
|
+
// it out of the comparison would let a step that unlocks a cell pass as a re-derivation
|
|
39
|
+
sdtPrefix: "source",
|
|
40
|
+
sdtContentsLocked: "source",
|
|
41
|
+
sdtDeletionLocked: "source"
|
|
42
|
+
},
|
|
43
|
+
rawBlock: { xml: "source", name: "source" },
|
|
44
|
+
docxRaw: { srcId: "session", name: "session" },
|
|
45
|
+
bookmarkBlock: { srcId: "session", name: "session" },
|
|
46
|
+
hardBreak: { brAttrs: "source" },
|
|
47
|
+
image: { src: "source", extent: "source", alt: "source", xml: "source" },
|
|
48
|
+
commentStart: { id: "source", xml: "source" },
|
|
49
|
+
commentEnd: { id: "source", xml: "source" },
|
|
50
|
+
commentReference: {
|
|
51
|
+
id: "source",
|
|
52
|
+
referenceXml: "source",
|
|
53
|
+
author: "source",
|
|
54
|
+
authorId: "source",
|
|
55
|
+
initials: "source",
|
|
56
|
+
date: "source",
|
|
57
|
+
text: "source",
|
|
58
|
+
commentXml: "source",
|
|
59
|
+
paraId: "source",
|
|
60
|
+
resolved: "source",
|
|
61
|
+
extensionXml: "source",
|
|
62
|
+
replies: "source",
|
|
63
|
+
// Read to decide whether the comment parts are rewritten at all; neither is written
|
|
64
|
+
imported: "session",
|
|
65
|
+
threadImported: "session"
|
|
66
|
+
},
|
|
67
|
+
noteReference: {
|
|
68
|
+
kind: "source",
|
|
69
|
+
id: "source",
|
|
70
|
+
customMarkFollows: "source",
|
|
71
|
+
referenceXml: "source",
|
|
72
|
+
// Both come from the notes part as the document was opened, and the writer puts back the
|
|
73
|
+
// reference alone: a note renumbered around an edit is the same reference it was
|
|
74
|
+
label: "display",
|
|
75
|
+
text: "display"
|
|
76
|
+
},
|
|
77
|
+
rawInline: { xml: "source" }
|
|
78
|
+
};
|
|
79
|
+
var MARK_ATTR_ROLES = {
|
|
80
|
+
run: { rPr: "source", rAttrs: "source", format: "display" },
|
|
81
|
+
sdt: {
|
|
82
|
+
sdtPrefix: "source",
|
|
83
|
+
// Counted through the document as it was opened, to tell one control from the next
|
|
84
|
+
sdtKey: "session",
|
|
85
|
+
contentsLocked: "source",
|
|
86
|
+
deletionLocked: "source"
|
|
87
|
+
},
|
|
88
|
+
link: { linkPrefix: "source", href: "source", linkKey: "session" },
|
|
89
|
+
tab: { tabAttrs: "source" }
|
|
90
|
+
};
|
|
91
|
+
function rolesOf(type) {
|
|
92
|
+
const roles = type instanceof NodeType ? NODE_ATTR_ROLES[type.name] : MARK_ATTR_ROLES[type.name];
|
|
93
|
+
return roles ?? {};
|
|
94
|
+
}
|
|
95
|
+
function attrRole(type, name) {
|
|
96
|
+
return rolesOf(type)[name] ?? "source";
|
|
97
|
+
}
|
|
98
|
+
function displayAttrsOf(type) {
|
|
99
|
+
return Object.entries(rolesOf(type)).filter(([, role]) => role === "display").map(([name]) => name);
|
|
100
|
+
}
|
|
101
|
+
export {
|
|
102
|
+
MARK_ATTR_ROLES,
|
|
103
|
+
NODE_ATTR_ROLES,
|
|
104
|
+
attrRole,
|
|
105
|
+
displayAttrsOf
|
|
106
|
+
};
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ProseMirror schema joining preserved DOCX fragments with validated display values. Original XML
|
|
3
3
|
* remains on nodes and marks so untouched content can round-trip verbatim.
|
|
4
|
+
*
|
|
5
|
+
* Every block sits in one of two groups beside `block`. A `modelled` block is one the editor
|
|
6
|
+
* takes apart and writes back out itself, so the writer decides what it says; a `preserved` one
|
|
7
|
+
* goes back out as the XML it arrived as. Which of the two a block is decides how it is written
|
|
8
|
+
* (`docx/serializeBlock`) and how a submitted file is compared against the original
|
|
9
|
+
* (`docx/storyProjection`), so a new block node has to name one of them.
|
|
4
10
|
*/
|
|
5
11
|
import { Schema } from "prosemirror-model";
|
|
6
12
|
export { imageNodeSpec, runMarkSpec } from "./rendering";
|