@beyondwork/docx-react-component 1.0.2 → 1.0.4
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 +11 -7
- package/package.json +21 -29
- package/src/compare/export-redlines.ts +0 -2
- package/src/core/commands/index.ts +116 -8
- package/src/core/state/text-transaction.ts +226 -0
- package/src/formats/xlsx/io/xlsx-session.ts +1 -1
- package/src/io/docx-session.ts +1 -1
- package/src/io/export/serialize-tables.ts +27 -0
- package/src/io/ooxml/parse-tables.ts +50 -0
- package/src/io/opc/package-reader.ts +32 -27
- package/src/runtime/table-commands.ts +10 -3
- package/src/runtime/table-schema.ts +120 -44
- package/src/ui/WordReviewEditor.tsx +2 -0
- package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +249 -16
- package/src/ui-tailwind/theme/editor-theme.css +1 -1
|
@@ -59,12 +59,23 @@ export interface ParsedCellMargins {
|
|
|
59
59
|
right?: number;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
export interface ParsedTableLook {
|
|
63
|
+
val?: string;
|
|
64
|
+
firstRow?: boolean;
|
|
65
|
+
lastRow?: boolean;
|
|
66
|
+
firstColumn?: boolean;
|
|
67
|
+
lastColumn?: boolean;
|
|
68
|
+
noHBand?: boolean;
|
|
69
|
+
noVBand?: boolean;
|
|
70
|
+
}
|
|
71
|
+
|
|
62
72
|
export interface ParsedTableDocument {
|
|
63
73
|
tables: ParsedTable[];
|
|
64
74
|
}
|
|
65
75
|
|
|
66
76
|
export interface ParsedTable {
|
|
67
77
|
type: "table";
|
|
78
|
+
styleId?: string;
|
|
68
79
|
propertiesXml?: string;
|
|
69
80
|
gridColumns: number[];
|
|
70
81
|
rows: ParsedTableRow[];
|
|
@@ -73,6 +84,7 @@ export interface ParsedTable {
|
|
|
73
84
|
alignment?: "left" | "center" | "right";
|
|
74
85
|
borders?: ParsedTableBorders;
|
|
75
86
|
cellMargins?: ParsedCellMargins;
|
|
87
|
+
tblLook?: ParsedTableLook;
|
|
76
88
|
}
|
|
77
89
|
|
|
78
90
|
export interface ParsedTableRow {
|
|
@@ -115,13 +127,16 @@ function parseTable(node: XmlElementNode, sourceXml: string): ParsedTable {
|
|
|
115
127
|
.filter((child): child is XmlElementNode => child.type === "element" && localName(child.name) === "tr")
|
|
116
128
|
.map((rowNode) => parseRow(rowNode, sourceXml));
|
|
117
129
|
|
|
130
|
+
const styleId = propertiesNode ? readTableStyleId(propertiesNode) : undefined;
|
|
118
131
|
const width = propertiesNode ? readTableWidth(propertiesNode) : undefined;
|
|
119
132
|
const alignment = propertiesNode ? readTableAlignment(propertiesNode) : undefined;
|
|
120
133
|
const borders = propertiesNode ? readTableBorders(propertiesNode) : undefined;
|
|
121
134
|
const cellMargins = propertiesNode ? readTableCellMargins(propertiesNode) : undefined;
|
|
135
|
+
const tblLook = propertiesNode ? readTableLook(propertiesNode) : undefined;
|
|
122
136
|
|
|
123
137
|
return {
|
|
124
138
|
type: "table",
|
|
139
|
+
...(styleId ? { styleId } : {}),
|
|
125
140
|
...(propertiesNode ? { propertiesXml: sourceXml.slice(propertiesNode.start, propertiesNode.end) } : {}),
|
|
126
141
|
gridColumns: gridNode ? readGridColumns(gridNode) : [],
|
|
127
142
|
rows,
|
|
@@ -130,6 +145,7 @@ function parseTable(node: XmlElementNode, sourceXml: string): ParsedTable {
|
|
|
130
145
|
...(alignment ? { alignment } : {}),
|
|
131
146
|
...(borders ? { borders } : {}),
|
|
132
147
|
...(cellMargins ? { cellMargins } : {}),
|
|
148
|
+
...(tblLook ? { tblLook } : {}),
|
|
133
149
|
};
|
|
134
150
|
}
|
|
135
151
|
|
|
@@ -493,6 +509,40 @@ function readTableAlignment(propertiesNode: XmlElementNode): "left" | "center" |
|
|
|
493
509
|
return undefined;
|
|
494
510
|
}
|
|
495
511
|
|
|
512
|
+
function readTableStyleId(propertiesNode: XmlElementNode): string | undefined {
|
|
513
|
+
const styleNode = findFirstChild(propertiesNode, "tblStyle");
|
|
514
|
+
if (!styleNode) return undefined;
|
|
515
|
+
return styleNode.attributes["w:val"] ?? styleNode.attributes.val;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function readTableLook(propertiesNode: XmlElementNode): ParsedTableLook | undefined {
|
|
519
|
+
const tblLookNode = findFirstChild(propertiesNode, "tblLook");
|
|
520
|
+
if (!tblLookNode) return undefined;
|
|
521
|
+
|
|
522
|
+
const tableLook: ParsedTableLook = {};
|
|
523
|
+
const val = tblLookNode.attributes["w:val"] ?? tblLookNode.attributes.val;
|
|
524
|
+
if (val) {
|
|
525
|
+
tableLook.val = val;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
for (const [attribute, key] of [
|
|
529
|
+
["w:firstRow", "firstRow"],
|
|
530
|
+
["w:lastRow", "lastRow"],
|
|
531
|
+
["w:firstColumn", "firstColumn"],
|
|
532
|
+
["w:lastColumn", "lastColumn"],
|
|
533
|
+
["w:noHBand", "noHBand"],
|
|
534
|
+
["w:noVBand", "noVBand"],
|
|
535
|
+
] as const) {
|
|
536
|
+
const fallback = attribute.replace("w:", "");
|
|
537
|
+
const raw = tblLookNode.attributes[attribute] ?? tblLookNode.attributes[fallback];
|
|
538
|
+
if (raw !== undefined) {
|
|
539
|
+
tableLook[key] = raw !== "0" && raw !== "false" && raw !== "off";
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
return Object.keys(tableLook).length > 0 ? tableLook : undefined;
|
|
544
|
+
}
|
|
545
|
+
|
|
496
546
|
function readTableBorders(propertiesNode: XmlElementNode): ParsedTableBorders | undefined {
|
|
497
547
|
const bordersNode = findFirstChild(propertiesNode, "tblBorders");
|
|
498
548
|
if (!bordersNode) return undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { inflateSync } from "fflate";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
CONTENT_TYPES_PATH,
|
|
@@ -35,8 +35,7 @@ const LOCAL_FILE_HEADER_SIGNATURE = 0x04034b50;
|
|
|
35
35
|
|
|
36
36
|
export function readOpcPackage(source: Uint8Array | ArrayBuffer): OpcPackage {
|
|
37
37
|
const bytes = source instanceof Uint8Array ? source : new Uint8Array(source);
|
|
38
|
-
const
|
|
39
|
-
const centralDirectory = readCentralDirectory(archive);
|
|
38
|
+
const centralDirectory = readCentralDirectory(bytes);
|
|
40
39
|
const parts = new Map<string, OpcPackagePart>();
|
|
41
40
|
|
|
42
41
|
for (const entry of centralDirectory) {
|
|
@@ -44,7 +43,7 @@ export function readOpcPackage(source: Uint8Array | ArrayBuffer): OpcPackage {
|
|
|
44
43
|
continue;
|
|
45
44
|
}
|
|
46
45
|
|
|
47
|
-
const storedBytes = readZipEntry(
|
|
46
|
+
const storedBytes = readZipEntry(bytes, entry);
|
|
48
47
|
const normalizedPath = normalizePartPath(entry.path);
|
|
49
48
|
const surfaceKind = getSurfaceKind(normalizedPath);
|
|
50
49
|
|
|
@@ -112,30 +111,31 @@ export function readOpcPackage(source: Uint8Array | ArrayBuffer): OpcPackage {
|
|
|
112
111
|
};
|
|
113
112
|
}
|
|
114
113
|
|
|
115
|
-
function readCentralDirectory(archive:
|
|
116
|
-
const
|
|
117
|
-
const
|
|
118
|
-
const
|
|
114
|
+
function readCentralDirectory(archive: Uint8Array): ZipCentralDirectoryEntry[] {
|
|
115
|
+
const view = new DataView(archive.buffer, archive.byteOffset, archive.byteLength);
|
|
116
|
+
const eocdOffset = findEndOfCentralDirectory(archive, view);
|
|
117
|
+
const entryCount = view.getUint16(eocdOffset + 10, true);
|
|
118
|
+
const centralDirectoryOffset = view.getUint32(eocdOffset + 16, true);
|
|
119
119
|
const entries: ZipCentralDirectoryEntry[] = [];
|
|
120
120
|
let offset = centralDirectoryOffset;
|
|
121
121
|
|
|
122
122
|
for (let index = 0; index < entryCount; index += 1) {
|
|
123
|
-
const signature =
|
|
123
|
+
const signature = view.getUint32(offset, true);
|
|
124
124
|
if (signature !== CENTRAL_DIRECTORY_SIGNATURE) {
|
|
125
125
|
throw new Error(`Invalid ZIP central directory signature at offset ${offset}.`);
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
const generalPurposeBitFlag =
|
|
129
|
-
const compressionMethod =
|
|
130
|
-
const crc32 =
|
|
131
|
-
const compressedSize =
|
|
132
|
-
const uncompressedSize =
|
|
133
|
-
const fileNameLength =
|
|
134
|
-
const extraFieldLength =
|
|
135
|
-
const fileCommentLength =
|
|
136
|
-
const localHeaderOffset =
|
|
128
|
+
const generalPurposeBitFlag = view.getUint16(offset + 8, true);
|
|
129
|
+
const compressionMethod = view.getUint16(offset + 10, true);
|
|
130
|
+
const crc32 = view.getUint32(offset + 16, true);
|
|
131
|
+
const compressedSize = view.getUint32(offset + 20, true);
|
|
132
|
+
const uncompressedSize = view.getUint32(offset + 24, true);
|
|
133
|
+
const fileNameLength = view.getUint16(offset + 28, true);
|
|
134
|
+
const extraFieldLength = view.getUint16(offset + 30, true);
|
|
135
|
+
const fileCommentLength = view.getUint16(offset + 32, true);
|
|
136
|
+
const localHeaderOffset = view.getUint32(offset + 42, true);
|
|
137
137
|
const fileNameOffset = offset + 46;
|
|
138
|
-
const fileName = archive.subarray(fileNameOffset, fileNameOffset + fileNameLength)
|
|
138
|
+
const fileName = decodeUtf8(archive.subarray(fileNameOffset, fileNameOffset + fileNameLength));
|
|
139
139
|
|
|
140
140
|
entries.push({
|
|
141
141
|
path: fileName,
|
|
@@ -153,13 +153,13 @@ function readCentralDirectory(archive: Buffer): ZipCentralDirectoryEntry[] {
|
|
|
153
153
|
return entries;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
function findEndOfCentralDirectory(archive:
|
|
156
|
+
function findEndOfCentralDirectory(archive: Uint8Array, view: DataView): number {
|
|
157
157
|
const minimumEocdSize = 22;
|
|
158
158
|
const maximumCommentSize = 0xffff;
|
|
159
159
|
const searchStart = Math.max(0, archive.length - minimumEocdSize - maximumCommentSize);
|
|
160
160
|
|
|
161
161
|
for (let offset = archive.length - minimumEocdSize; offset >= searchStart; offset -= 1) {
|
|
162
|
-
if (
|
|
162
|
+
if (view.getUint32(offset, true) === EOCD_SIGNATURE) {
|
|
163
163
|
return offset;
|
|
164
164
|
}
|
|
165
165
|
}
|
|
@@ -167,15 +167,16 @@ function findEndOfCentralDirectory(archive: Buffer): number {
|
|
|
167
167
|
throw new Error("Invalid ZIP archive: end of central directory not found.");
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
function readZipEntry(archive:
|
|
170
|
+
function readZipEntry(archive: Uint8Array, entry: ZipCentralDirectoryEntry): Uint8Array {
|
|
171
|
+
const view = new DataView(archive.buffer, archive.byteOffset, archive.byteLength);
|
|
171
172
|
const headerOffset = entry.localHeaderOffset;
|
|
172
|
-
const signature =
|
|
173
|
+
const signature = view.getUint32(headerOffset, true);
|
|
173
174
|
if (signature !== LOCAL_FILE_HEADER_SIGNATURE) {
|
|
174
175
|
throw new Error(`Invalid ZIP local file header signature at offset ${headerOffset}.`);
|
|
175
176
|
}
|
|
176
177
|
|
|
177
|
-
const fileNameLength =
|
|
178
|
-
const extraFieldLength =
|
|
178
|
+
const fileNameLength = view.getUint16(headerOffset + 26, true);
|
|
179
|
+
const extraFieldLength = view.getUint16(headerOffset + 28, true);
|
|
179
180
|
const dataOffset = headerOffset + 30 + fileNameLength + extraFieldLength;
|
|
180
181
|
const compressed = archive.subarray(dataOffset, dataOffset + entry.compressedSize);
|
|
181
182
|
|
|
@@ -183,7 +184,7 @@ function readZipEntry(archive: Buffer, entry: ZipCentralDirectoryEntry): Uint8Ar
|
|
|
183
184
|
case "store":
|
|
184
185
|
return new Uint8Array(compressed);
|
|
185
186
|
case "deflate":
|
|
186
|
-
return
|
|
187
|
+
return inflateSync(compressed);
|
|
187
188
|
default:
|
|
188
189
|
throw new Error(`Unsupported ZIP compression for ${entry.path}.`);
|
|
189
190
|
}
|
|
@@ -213,7 +214,11 @@ function getSurfaceKind(path: string): OpcPartManifestEntry["surfaceKind"] {
|
|
|
213
214
|
}
|
|
214
215
|
|
|
215
216
|
function decodeXml(bytes: Uint8Array): string {
|
|
216
|
-
return
|
|
217
|
+
return new TextDecoder("utf-8").decode(bytes);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function decodeUtf8(bytes: Uint8Array): string {
|
|
221
|
+
return new TextDecoder("utf-8").decode(bytes);
|
|
217
222
|
}
|
|
218
223
|
|
|
219
224
|
function parseContentTypesXml(xml: string): OpcPackageManifest["contentTypes"] {
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import type { Command as PMCommand, EditorState } from "prosemirror-state";
|
|
13
13
|
import {
|
|
14
|
+
TableMap,
|
|
14
15
|
addColumnAfter as pmAddColumnAfter,
|
|
15
16
|
addColumnBefore as pmAddColumnBefore,
|
|
16
17
|
addRowAfter as pmAddRowAfter,
|
|
@@ -48,8 +49,14 @@ function withTableGuard(
|
|
|
48
49
|
command: PMCommand,
|
|
49
50
|
): PMCommand {
|
|
50
51
|
return (state, dispatch, view) => {
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
try {
|
|
53
|
+
if (!canRun(state)) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return command(state, dispatch, view);
|
|
57
|
+
} catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
53
60
|
};
|
|
54
61
|
}
|
|
55
62
|
|
|
@@ -72,7 +79,7 @@ export const addColumnAfter: PMCommand = (state, dispatch, view) =>
|
|
|
72
79
|
|
|
73
80
|
export const deleteColumn: PMCommand = withTableGuard((state) => {
|
|
74
81
|
const table = tableAtSelection(state);
|
|
75
|
-
return table !== null &&
|
|
82
|
+
return table !== null && TableMap.get(table).width > 1;
|
|
76
83
|
}, pmDeleteColumn);
|
|
77
84
|
|
|
78
85
|
export {
|
|
@@ -11,6 +11,14 @@
|
|
|
11
11
|
|
|
12
12
|
import type { NodeSpec } from "prosemirror-model";
|
|
13
13
|
|
|
14
|
+
type TableCellAttrs = {
|
|
15
|
+
colspan?: number | null;
|
|
16
|
+
rowspan?: number | null;
|
|
17
|
+
colwidth?: number[] | null;
|
|
18
|
+
gridSpan?: number | null;
|
|
19
|
+
verticalMerge?: "restart" | "continue" | null;
|
|
20
|
+
};
|
|
21
|
+
|
|
14
22
|
function resolveRenderedColspan(attrs: {
|
|
15
23
|
colspan?: number | null;
|
|
16
24
|
gridSpan?: number | null;
|
|
@@ -24,6 +32,93 @@ function resolveRenderedColspan(attrs: {
|
|
|
24
32
|
return 1;
|
|
25
33
|
}
|
|
26
34
|
|
|
35
|
+
function resolveRenderedRowspan(attrs: { rowspan?: number | null }): number {
|
|
36
|
+
return typeof attrs.rowspan === "number" && attrs.rowspan > 1 ? attrs.rowspan : 1;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function parseColwidthAttr(dom: HTMLElement, colspan: number): number[] | null {
|
|
40
|
+
const widthAttr = dom.getAttribute("data-colwidth");
|
|
41
|
+
if (!widthAttr || !/^\d+(,\d+)*$/.test(widthAttr)) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const widths = widthAttr.split(",").map((value) => Number.parseInt(value, 10));
|
|
46
|
+
return widths.length === colspan ? widths : null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getCellAttrs(dom: HTMLElement): TableCellAttrs {
|
|
50
|
+
const colspan = Number(dom.getAttribute("colspan") || 1);
|
|
51
|
+
const rowspan = Number(dom.getAttribute("rowspan") || 1);
|
|
52
|
+
const gridSpanAttr = dom.getAttribute("data-grid-span");
|
|
53
|
+
const verticalMergeAttr = dom.getAttribute("data-vertical-merge");
|
|
54
|
+
const gridSpan = gridSpanAttr ? Number.parseInt(gridSpanAttr, 10) : colspan;
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
colspan,
|
|
58
|
+
rowspan,
|
|
59
|
+
colwidth: parseColwidthAttr(dom, colspan),
|
|
60
|
+
gridSpan: Number.isFinite(gridSpan) && gridSpan > 0 ? gridSpan : colspan,
|
|
61
|
+
verticalMerge:
|
|
62
|
+
verticalMergeAttr === "restart" || verticalMergeAttr === "continue"
|
|
63
|
+
? verticalMergeAttr
|
|
64
|
+
: null,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function setCellDomAttrs(nodeAttrs: TableCellAttrs, className: string): Record<string, string> {
|
|
69
|
+
const attrs: Record<string, string> = { class: className };
|
|
70
|
+
const colspan = resolveRenderedColspan(nodeAttrs);
|
|
71
|
+
const rowspan = resolveRenderedRowspan(nodeAttrs);
|
|
72
|
+
|
|
73
|
+
if (colspan > 1) {
|
|
74
|
+
attrs.colspan = String(colspan);
|
|
75
|
+
}
|
|
76
|
+
if (rowspan > 1) {
|
|
77
|
+
attrs.rowspan = String(rowspan);
|
|
78
|
+
}
|
|
79
|
+
if (nodeAttrs.colwidth && nodeAttrs.colwidth.length > 0) {
|
|
80
|
+
attrs["data-colwidth"] = nodeAttrs.colwidth.join(",");
|
|
81
|
+
}
|
|
82
|
+
if (typeof nodeAttrs.gridSpan === "number" && nodeAttrs.gridSpan > 1) {
|
|
83
|
+
attrs["data-grid-span"] = String(nodeAttrs.gridSpan);
|
|
84
|
+
}
|
|
85
|
+
if (nodeAttrs.verticalMerge) {
|
|
86
|
+
attrs["data-vertical-merge"] = nodeAttrs.verticalMerge;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return attrs;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function validateColwidth(value: unknown): void {
|
|
93
|
+
if (value === null) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (!Array.isArray(value)) {
|
|
97
|
+
throw new TypeError("colwidth must be null or an array");
|
|
98
|
+
}
|
|
99
|
+
for (const item of value) {
|
|
100
|
+
if (typeof item !== "number") {
|
|
101
|
+
throw new TypeError("colwidth must be null or an array of numbers");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function validateVerticalMerge(value: unknown): void {
|
|
107
|
+
if (value === null || value === "restart" || value === "continue") {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
throw new TypeError("verticalMerge must be null, 'restart', or 'continue'");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const tableCellSpecAttrs = {
|
|
114
|
+
propertiesXml: { default: null },
|
|
115
|
+
gridSpan: { default: 1, validate: "number" },
|
|
116
|
+
verticalMerge: { default: null, validate: validateVerticalMerge },
|
|
117
|
+
colspan: { default: 1, validate: "number" },
|
|
118
|
+
rowspan: { default: 1, validate: "number" },
|
|
119
|
+
colwidth: { default: null, validate: validateColwidth },
|
|
120
|
+
} as const;
|
|
121
|
+
|
|
27
122
|
export const tableNodeSpec: NodeSpec = {
|
|
28
123
|
content: "table_row+",
|
|
29
124
|
tableRole: "table",
|
|
@@ -56,35 +151,24 @@ export const tableCellNodeSpec: NodeSpec = {
|
|
|
56
151
|
content: "paragraph+",
|
|
57
152
|
tableRole: "cell",
|
|
58
153
|
isolating: true,
|
|
59
|
-
attrs:
|
|
60
|
-
propertiesXml: { default: null },
|
|
61
|
-
gridSpan: { default: 1 },
|
|
62
|
-
verticalMerge: { default: null },
|
|
63
|
-
colspan: { default: 1 },
|
|
64
|
-
rowspan: { default: 1 },
|
|
65
|
-
colwidth: { default: null },
|
|
66
|
-
},
|
|
154
|
+
attrs: tableCellSpecAttrs,
|
|
67
155
|
parseDOM: [
|
|
68
156
|
{
|
|
69
157
|
tag: "td",
|
|
70
|
-
getAttrs(dom
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
rowspan: rowspan ? Number.parseInt(rowspan, 10) : 1,
|
|
76
|
-
};
|
|
158
|
+
getAttrs(dom) {
|
|
159
|
+
if (!(dom instanceof HTMLElement)) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
return getCellAttrs(dom);
|
|
77
163
|
},
|
|
78
164
|
},
|
|
79
165
|
],
|
|
80
166
|
toDOM(node) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (node.attrs.rowspan > 1) attrs.rowspan = String(node.attrs.rowspan);
|
|
87
|
-
return ["td", attrs, 0];
|
|
167
|
+
return [
|
|
168
|
+
"td",
|
|
169
|
+
setCellDomAttrs(node.attrs as TableCellAttrs, "border border-primary/20 p-2 align-top"),
|
|
170
|
+
0,
|
|
171
|
+
];
|
|
88
172
|
},
|
|
89
173
|
};
|
|
90
174
|
|
|
@@ -92,35 +176,27 @@ export const tableHeaderCellNodeSpec: NodeSpec = {
|
|
|
92
176
|
content: "paragraph+",
|
|
93
177
|
tableRole: "header_cell",
|
|
94
178
|
isolating: true,
|
|
95
|
-
attrs:
|
|
96
|
-
propertiesXml: { default: null },
|
|
97
|
-
gridSpan: { default: 1 },
|
|
98
|
-
verticalMerge: { default: null },
|
|
99
|
-
colspan: { default: 1 },
|
|
100
|
-
rowspan: { default: 1 },
|
|
101
|
-
colwidth: { default: null },
|
|
102
|
-
},
|
|
179
|
+
attrs: tableCellSpecAttrs,
|
|
103
180
|
parseDOM: [
|
|
104
181
|
{
|
|
105
182
|
tag: "th",
|
|
106
|
-
getAttrs(dom
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
rowspan: rowspan ? Number.parseInt(rowspan, 10) : 1,
|
|
112
|
-
};
|
|
183
|
+
getAttrs(dom) {
|
|
184
|
+
if (!(dom instanceof HTMLElement)) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
return getCellAttrs(dom);
|
|
113
188
|
},
|
|
114
189
|
},
|
|
115
190
|
],
|
|
116
191
|
toDOM(node) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
192
|
+
return [
|
|
193
|
+
"th",
|
|
194
|
+
setCellDomAttrs(
|
|
195
|
+
node.attrs as TableCellAttrs,
|
|
196
|
+
"border border-primary/20 p-2 align-top font-semibold bg-surface-raised",
|
|
197
|
+
),
|
|
198
|
+
0,
|
|
199
|
+
];
|
|
124
200
|
},
|
|
125
201
|
};
|
|
126
202
|
|
|
@@ -880,6 +880,8 @@ function findRegionFocusTarget(
|
|
|
880
880
|
if (regionId === "document" || regionId === "toolbar" || regionId === "review-rail" || regionId === "status") {
|
|
881
881
|
return region;
|
|
882
882
|
}
|
|
883
|
+
|
|
884
|
+
return null;
|
|
883
885
|
}
|
|
884
886
|
|
|
885
887
|
function isAccessibleRegionId(value: string | undefined): value is AccessibleRegionId {
|