@univerjs/docs 0.25.0 → 1.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/cjs/facade.js +1979 -0
- package/lib/cjs/index.js +372 -35
- package/lib/es/facade.js +1965 -0
- package/lib/es/index.js +360 -34
- package/lib/facade.js +1965 -0
- package/lib/index.js +360 -34
- package/lib/types/commands/commands/core-editing.command.d.ts +57 -0
- package/lib/types/facade/doc-element-registry.d.ts +84 -0
- package/lib/types/facade/f-doc-block-range.d.ts +139 -0
- package/lib/types/facade/f-doc-body.d.ts +535 -0
- package/lib/types/facade/f-doc-custom-block.d.ts +94 -0
- package/lib/types/facade/f-doc-element.d.ts +177 -0
- package/lib/types/facade/f-doc-paragraph.d.ts +194 -0
- package/lib/types/facade/f-doc-table.d.ts +94 -0
- package/lib/types/facade/f-document.d.ts +205 -0
- package/lib/types/facade/f-univer.d.ts +64 -0
- package/lib/types/facade/index.d.ts +25 -0
- package/lib/types/facade/utils.d.ts +26 -0
- package/lib/types/index.d.ts +10 -3
- package/lib/types/services/doc-content-insert.service.d.ts +28 -0
- package/lib/types/services/doc-state-change-manager.service.d.ts +53 -0
- package/lib/types/utils/custom-range-factory.d.ts +1 -5
- package/lib/umd/facade.js +5 -0
- package/lib/umd/index.js +1 -1
- package/package.json +15 -5
package/lib/facade.js
ADDED
|
@@ -0,0 +1,1965 @@
|
|
|
1
|
+
import { ICommandService, IResourceLoaderService, IUniverInstanceService, Inject, Injector, JSONX, PresetListType, RedoCommand, TextX, TextXActionType, UndoCommand, UniverInstanceType, UpdateDocsAttributeType, createParagraphId, getRichTextEditPath } from "@univerjs/core";
|
|
2
|
+
import { FBaseInitialable, FUniver } from "@univerjs/core/facade";
|
|
3
|
+
import { InsertTextCommand, RichTextEditingMutation } from "@univerjs/docs";
|
|
4
|
+
|
|
5
|
+
//#region src/facade/doc-element-registry.ts
|
|
6
|
+
const PARAGRAPH_REGISTRY_MIGRATION_ERROR = "DocElementRegistry no longer tracks paragraph identity; use paragraphId.";
|
|
7
|
+
/**
|
|
8
|
+
* Error thrown when a facade element key can no longer be resolved uniquely.
|
|
9
|
+
*
|
|
10
|
+
* A stale paragraph usually means the paragraph was deleted, or the document no
|
|
11
|
+
* longer contains exactly one paragraph with the requested `paragraphId`.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const doc = univerAPI.getActiveDocument();
|
|
16
|
+
* if (!doc) throw new Error('No active document');
|
|
17
|
+
*
|
|
18
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
19
|
+
* paragraph.removeFromParent();
|
|
20
|
+
*
|
|
21
|
+
* try {
|
|
22
|
+
* paragraph.getText();
|
|
23
|
+
* } catch (error) {
|
|
24
|
+
* if (error instanceof DocElementStaleError) {
|
|
25
|
+
* console.log('The paragraph handle is stale.');
|
|
26
|
+
* }
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
var DocElementStaleError = class extends Error {
|
|
31
|
+
/**
|
|
32
|
+
* Create a stale element error.
|
|
33
|
+
* @param {string} message The error message.
|
|
34
|
+
*/
|
|
35
|
+
constructor(message = "Doc element is stale") {
|
|
36
|
+
super(message);
|
|
37
|
+
this.name = "DocElementStaleError";
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* @deprecated Paragraph facade identity is now resolved directly from persisted
|
|
42
|
+
* `paragraphId` values. This class remains as a compatibility shell for callers
|
|
43
|
+
* that constructed document facade internals directly.
|
|
44
|
+
*
|
|
45
|
+
* @hideconstructor
|
|
46
|
+
*/
|
|
47
|
+
var DocElementRegistry = class {
|
|
48
|
+
/**
|
|
49
|
+
* @deprecated Paragraph facade identity is now the persisted `paragraphId`.
|
|
50
|
+
* @throws {Error} Always throws; use `paragraph.paragraphId` instead.
|
|
51
|
+
*/
|
|
52
|
+
getParagraphKey(_segmentId, _body, _paragraphIndex) {
|
|
53
|
+
throw new Error(PARAGRAPH_REGISTRY_MIGRATION_ERROR);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* @deprecated Paragraph facade identity is now the persisted `paragraphId`.
|
|
57
|
+
* @throws {Error} Always throws; resolve paragraph handles by `paragraphId` instead.
|
|
58
|
+
*/
|
|
59
|
+
resolveParagraphStartIndex(_segmentId, _key) {
|
|
60
|
+
throw new Error(PARAGRAPH_REGISTRY_MIGRATION_ERROR);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* @deprecated Paragraph facade identity is now the persisted `paragraphId`.
|
|
64
|
+
* @throws {Error} Always throws; resolve paragraph handles by `paragraphId` instead.
|
|
65
|
+
*/
|
|
66
|
+
syncParagraph(_segmentId, _key, _body) {
|
|
67
|
+
throw new Error(PARAGRAPH_REGISTRY_MIGRATION_ERROR);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* @deprecated Paragraph facade identity is now the persisted `paragraphId`.
|
|
71
|
+
* @throws {Error} Always throws; stale state is detected from live `paragraphId` lookups.
|
|
72
|
+
*/
|
|
73
|
+
markStale(_segmentId, _key) {
|
|
74
|
+
throw new Error(PARAGRAPH_REGISTRY_MIGRATION_ERROR);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* @deprecated Paragraph facade identity is now the persisted `paragraphId`.
|
|
78
|
+
* @throws {Error} Always throws; text edits no longer need registry offset tracking.
|
|
79
|
+
*/
|
|
80
|
+
beforeTextEdit(_segmentId, _startOffset, _endOffset, _insertLength) {
|
|
81
|
+
throw new Error(PARAGRAPH_REGISTRY_MIGRATION_ERROR);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/facade/utils.ts
|
|
87
|
+
function cloneParagraphStyle(paragraphStyle) {
|
|
88
|
+
return paragraphStyle == null ? paragraphStyle : JSON.parse(JSON.stringify(paragraphStyle));
|
|
89
|
+
}
|
|
90
|
+
function normalizePlainTextDataStream(dataStream) {
|
|
91
|
+
return dataStream.replace(/\r\n/g, "\r").replace(/\n/g, "\r");
|
|
92
|
+
}
|
|
93
|
+
function getRemovedLeadingParagraphBreakLength(dataStream, removeLeadingParagraphBreak) {
|
|
94
|
+
const normalized = normalizePlainTextDataStream(dataStream);
|
|
95
|
+
if (removeLeadingParagraphBreak && normalized.length > 1 && normalized.startsWith("\r")) return 1;
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
98
|
+
function getNormalizedPlainTextCursorOffset(dataStream, cursorOffset, removeLeadingParagraphBreak) {
|
|
99
|
+
const normalizedPrefixLength = normalizePlainTextDataStream(dataStream.slice(0, cursorOffset)).length;
|
|
100
|
+
return Math.max(0, normalizedPrefixLength - getRemovedLeadingParagraphBreakLength(dataStream, removeLeadingParagraphBreak));
|
|
101
|
+
}
|
|
102
|
+
function getParagraphStyleAtOffset(body, offset) {
|
|
103
|
+
var _body$paragraphs, _paragraphs$find;
|
|
104
|
+
const paragraphs = (_body$paragraphs = body.paragraphs) !== null && _body$paragraphs !== void 0 ? _body$paragraphs : [];
|
|
105
|
+
const paragraph = (_paragraphs$find = paragraphs.find((item) => item.startIndex >= offset)) !== null && _paragraphs$find !== void 0 ? _paragraphs$find : paragraphs[paragraphs.length - 1];
|
|
106
|
+
return paragraph === null || paragraph === void 0 ? void 0 : paragraph.paragraphStyle;
|
|
107
|
+
}
|
|
108
|
+
function buildPlainTextInsertBody(dataStream, options = {}) {
|
|
109
|
+
const normalizedDataStream = normalizePlainTextDataStream(dataStream).slice(getRemovedLeadingParagraphBreakLength(dataStream, options.removeLeadingParagraphBreak));
|
|
110
|
+
const body = {
|
|
111
|
+
dataStream: normalizedDataStream,
|
|
112
|
+
customDecorations: [],
|
|
113
|
+
customRanges: [],
|
|
114
|
+
textRuns: []
|
|
115
|
+
};
|
|
116
|
+
const paragraphs = [];
|
|
117
|
+
const existingParagraphIds = /* @__PURE__ */ new Set();
|
|
118
|
+
for (let index = 0; index < normalizedDataStream.length; index++) if (normalizedDataStream[index] === "\r") paragraphs.push({
|
|
119
|
+
startIndex: index,
|
|
120
|
+
paragraphId: createParagraphId(existingParagraphIds),
|
|
121
|
+
...options.paragraphStyle == null ? {} : { paragraphStyle: cloneParagraphStyle(options.paragraphStyle) }
|
|
122
|
+
});
|
|
123
|
+
if (paragraphs.length > 0) body.paragraphs = paragraphs;
|
|
124
|
+
return body;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region src/facade/f-doc-block-range.ts
|
|
129
|
+
/**
|
|
130
|
+
* A facade wrapper for document block ranges, such as callout, quote, and code blocks.
|
|
131
|
+
*
|
|
132
|
+
* Block range identity is backed by the persisted `IDocumentBlockRange.blockId`,
|
|
133
|
+
* so wrappers can be re-resolved after text is inserted before the block range.
|
|
134
|
+
*
|
|
135
|
+
* @hideconstructor
|
|
136
|
+
*/
|
|
137
|
+
var FDocBlockRange = class {
|
|
138
|
+
constructor(_body, _key) {
|
|
139
|
+
this._body = _body;
|
|
140
|
+
this._key = _key;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get the document element type.
|
|
144
|
+
* @returns {'blockRange'} The literal block range element type.
|
|
145
|
+
* @example
|
|
146
|
+
* ```ts
|
|
147
|
+
* const doc = univerAPI.getActiveDocument();
|
|
148
|
+
* if (!doc) throw new Error('No active document');
|
|
149
|
+
*
|
|
150
|
+
* const blockRange = doc.getBody().getChild(0).asBlockRange();
|
|
151
|
+
* console.log(blockRange.getType());
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
getType() {
|
|
155
|
+
return "blockRange";
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get the block range key.
|
|
159
|
+
* @returns {string} The persisted `blockId` for this block range.
|
|
160
|
+
* @example
|
|
161
|
+
* ```ts
|
|
162
|
+
* const doc = univerAPI.getActiveDocument();
|
|
163
|
+
* if (!doc) throw new Error('No active document');
|
|
164
|
+
*
|
|
165
|
+
* const blockRange = doc.getBody().getChild(0).asBlockRange();
|
|
166
|
+
* console.log(blockRange.getKey());
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
getKey() {
|
|
170
|
+
return this._key;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Get the parent body facade that owns this block range.
|
|
174
|
+
* @returns {FDocBody} The document body facade.
|
|
175
|
+
* @example
|
|
176
|
+
* ```ts
|
|
177
|
+
* const doc = univerAPI.getActiveDocument();
|
|
178
|
+
* if (!doc) throw new Error('No active document');
|
|
179
|
+
*
|
|
180
|
+
* const blockRange = doc.getBody().getChild(0).asBlockRange();
|
|
181
|
+
* console.log(blockRange.getParent().getChildIndex(blockRange));
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
getParent() {
|
|
185
|
+
return this._body;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Remove this block range and its content from the parent body.
|
|
189
|
+
* @returns {boolean} `true` if the block range content was removed.
|
|
190
|
+
* @example
|
|
191
|
+
* ```ts
|
|
192
|
+
* const doc = univerAPI.getActiveDocument();
|
|
193
|
+
* if (!doc) throw new Error('No active document');
|
|
194
|
+
*
|
|
195
|
+
* const blockRange = doc.getBody().getChild(0).asBlockRange();
|
|
196
|
+
* blockRange.removeFromParent();
|
|
197
|
+
* ```
|
|
198
|
+
*/
|
|
199
|
+
removeFromParent() {
|
|
200
|
+
return this._body.removeBlockRange(this._key);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Get the block range type.
|
|
204
|
+
* @returns {DocumentBlockRangeType} The block type, such as callout, quote, or code.
|
|
205
|
+
* @example
|
|
206
|
+
* ```ts
|
|
207
|
+
* const doc = univerAPI.getActiveDocument();
|
|
208
|
+
* if (!doc) throw new Error('No active document');
|
|
209
|
+
*
|
|
210
|
+
* const blockRange = doc.getBody().getChild(0).asBlockRange();
|
|
211
|
+
* console.log(blockRange.getBlockType());
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
214
|
+
getBlockType() {
|
|
215
|
+
return this._body.getBlockRange(this._key).blockType;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Get the plain text inside this block range.
|
|
219
|
+
* @returns {string} The block range text.
|
|
220
|
+
* @example
|
|
221
|
+
* ```ts
|
|
222
|
+
* const doc = univerAPI.getActiveDocument();
|
|
223
|
+
* if (!doc) throw new Error('No active document');
|
|
224
|
+
*
|
|
225
|
+
* const blockRange = doc.getBody().getChild(0).asBlockRange();
|
|
226
|
+
* console.log(blockRange.getText());
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
getText() {
|
|
230
|
+
return this._body.getBlockRangeText(this._key);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Replace the plain text inside this block range.
|
|
234
|
+
* @param {string} text The replacement text.
|
|
235
|
+
* @returns {boolean} `true` if the block range text was replaced.
|
|
236
|
+
* @example
|
|
237
|
+
* ```ts
|
|
238
|
+
* const doc = univerAPI.getActiveDocument();
|
|
239
|
+
* if (!doc) throw new Error('No active document');
|
|
240
|
+
*
|
|
241
|
+
* const blockRange = doc.getBody().getChild(0).asBlockRange();
|
|
242
|
+
* blockRange.setText('Updated block text');
|
|
243
|
+
* ```
|
|
244
|
+
*/
|
|
245
|
+
setText(text) {
|
|
246
|
+
return this._body.setBlockRangeText(this._key, text);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Remove this block range wrapper from the body.
|
|
250
|
+
*
|
|
251
|
+
* This currently removes the block range and its content, matching
|
|
252
|
+
* `removeFromParent()`.
|
|
253
|
+
*
|
|
254
|
+
* @returns {boolean} `true` if the block range content was removed.
|
|
255
|
+
* @example
|
|
256
|
+
* ```ts
|
|
257
|
+
* const doc = univerAPI.getActiveDocument();
|
|
258
|
+
* if (!doc) throw new Error('No active document');
|
|
259
|
+
*
|
|
260
|
+
* const blockRange = doc.getBody().getChild(0).asBlockRange();
|
|
261
|
+
* blockRange.unwrap();
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
unwrap() {
|
|
265
|
+
return this.removeFromParent();
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
//#endregion
|
|
270
|
+
//#region src/facade/f-doc-custom-block.ts
|
|
271
|
+
/**
|
|
272
|
+
* A facade wrapper for a document custom block, such as an embedded drawing or widget.
|
|
273
|
+
*
|
|
274
|
+
* Custom block identity is backed by the persisted `ICustomBlock.blockId`, so the
|
|
275
|
+
* wrapper can be re-resolved after text is inserted before the custom block.
|
|
276
|
+
*
|
|
277
|
+
* @hideconstructor
|
|
278
|
+
*/
|
|
279
|
+
var FDocCustomBlock = class {
|
|
280
|
+
constructor(_body, _key) {
|
|
281
|
+
this._body = _body;
|
|
282
|
+
this._key = _key;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Get the document element type.
|
|
286
|
+
* @returns {'customBlock'} The literal custom block element type.
|
|
287
|
+
* @example
|
|
288
|
+
* ```ts
|
|
289
|
+
* const doc = univerAPI.getActiveDocument();
|
|
290
|
+
* if (!doc) throw new Error('No active document');
|
|
291
|
+
*
|
|
292
|
+
* const customBlock = doc.getBody().getChild(0).asCustomBlock();
|
|
293
|
+
* console.log(customBlock.getType());
|
|
294
|
+
* ```
|
|
295
|
+
*/
|
|
296
|
+
getType() {
|
|
297
|
+
return "customBlock";
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Get the custom block key.
|
|
301
|
+
* @returns {string} The persisted `blockId` for this custom block.
|
|
302
|
+
* @example
|
|
303
|
+
* ```ts
|
|
304
|
+
* const doc = univerAPI.getActiveDocument();
|
|
305
|
+
* if (!doc) throw new Error('No active document');
|
|
306
|
+
*
|
|
307
|
+
* const customBlock = doc.getBody().getChild(0).asCustomBlock();
|
|
308
|
+
* console.log(customBlock.getKey());
|
|
309
|
+
* ```
|
|
310
|
+
*/
|
|
311
|
+
getKey() {
|
|
312
|
+
return this._key;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Get the parent body facade that owns this custom block.
|
|
316
|
+
* @returns {FDocBody} The document body facade.
|
|
317
|
+
* @example
|
|
318
|
+
* ```ts
|
|
319
|
+
* const doc = univerAPI.getActiveDocument();
|
|
320
|
+
* if (!doc) throw new Error('No active document');
|
|
321
|
+
*
|
|
322
|
+
* const customBlock = doc.getBody().getChild(0).asCustomBlock();
|
|
323
|
+
* console.log(customBlock.getParent().getChildIndex(customBlock));
|
|
324
|
+
* ```
|
|
325
|
+
*/
|
|
326
|
+
getParent() {
|
|
327
|
+
return this._body;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Remove this custom block from the parent body.
|
|
331
|
+
* @returns {boolean} `true` if the custom block placeholder was removed.
|
|
332
|
+
* @example
|
|
333
|
+
* ```ts
|
|
334
|
+
* const doc = univerAPI.getActiveDocument();
|
|
335
|
+
* if (!doc) throw new Error('No active document');
|
|
336
|
+
*
|
|
337
|
+
* const customBlock = doc.getBody().getChild(0).asCustomBlock();
|
|
338
|
+
* customBlock.removeFromParent();
|
|
339
|
+
* ```
|
|
340
|
+
*/
|
|
341
|
+
removeFromParent() {
|
|
342
|
+
return this._body.removeCustomBlock(this._key);
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Get the persisted custom block id.
|
|
346
|
+
* @returns {string} The `ICustomBlock.blockId` value.
|
|
347
|
+
* @example
|
|
348
|
+
* ```ts
|
|
349
|
+
* const doc = univerAPI.getActiveDocument();
|
|
350
|
+
* if (!doc) throw new Error('No active document');
|
|
351
|
+
*
|
|
352
|
+
* const customBlock = doc.getBody().getChild(0).asCustomBlock();
|
|
353
|
+
* console.log(customBlock.getBlockId());
|
|
354
|
+
* ```
|
|
355
|
+
*/
|
|
356
|
+
getBlockId() {
|
|
357
|
+
return this._body.getCustomBlock(this._key).blockId;
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
//#endregion
|
|
362
|
+
//#region src/facade/f-doc-paragraph.ts
|
|
363
|
+
/**
|
|
364
|
+
* A paragraph facade wrapper.
|
|
365
|
+
*
|
|
366
|
+
* Paragraph identity is backed by the persisted `paragraphId`. The id is
|
|
367
|
+
* re-resolved before each method call, so insertions before this paragraph do
|
|
368
|
+
* not break the wrapper.
|
|
369
|
+
*
|
|
370
|
+
* @hideconstructor
|
|
371
|
+
*/
|
|
372
|
+
var FDocParagraph = class {
|
|
373
|
+
constructor(_body, _key) {
|
|
374
|
+
this._body = _body;
|
|
375
|
+
this._key = _key;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Get the document element type.
|
|
379
|
+
* @returns {'paragraph'} The literal paragraph element type.
|
|
380
|
+
* @example
|
|
381
|
+
* ```ts
|
|
382
|
+
* const doc = univerAPI.getActiveDocument();
|
|
383
|
+
* if (!doc) throw new Error('No active document');
|
|
384
|
+
*
|
|
385
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
386
|
+
* console.log(paragraph.getType());
|
|
387
|
+
* ```
|
|
388
|
+
*/
|
|
389
|
+
getType() {
|
|
390
|
+
return "paragraph";
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Get the persisted paragraph id.
|
|
394
|
+
* @returns {string} The paragraph id.
|
|
395
|
+
* @example
|
|
396
|
+
* ```ts
|
|
397
|
+
* const doc = univerAPI.getActiveDocument();
|
|
398
|
+
* if (!doc) throw new Error('No active document');
|
|
399
|
+
*
|
|
400
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
401
|
+
* console.log(paragraph.getKey());
|
|
402
|
+
* ```
|
|
403
|
+
*/
|
|
404
|
+
getKey() {
|
|
405
|
+
return this._key;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Get the persisted paragraph id.
|
|
409
|
+
* @returns {string} The paragraph id.
|
|
410
|
+
* @example
|
|
411
|
+
* ```ts
|
|
412
|
+
* const doc = univerAPI.getActiveDocument();
|
|
413
|
+
* if (!doc) throw new Error('No active document');
|
|
414
|
+
*
|
|
415
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
416
|
+
* console.log(paragraph.getId());
|
|
417
|
+
* ```
|
|
418
|
+
*/
|
|
419
|
+
getId() {
|
|
420
|
+
return this._key;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Get the parent body facade that owns this paragraph.
|
|
424
|
+
* @returns {FDocBody} The document body facade.
|
|
425
|
+
* @example
|
|
426
|
+
* ```ts
|
|
427
|
+
* const doc = univerAPI.getActiveDocument();
|
|
428
|
+
* if (!doc) throw new Error('No active document');
|
|
429
|
+
*
|
|
430
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
431
|
+
* console.log(paragraph.getParent().getChildIndex(paragraph));
|
|
432
|
+
* ```
|
|
433
|
+
*/
|
|
434
|
+
getParent() {
|
|
435
|
+
return this._body;
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Remove this paragraph from its parent body.
|
|
439
|
+
* @returns {boolean} `true` if the paragraph was removed.
|
|
440
|
+
* @example
|
|
441
|
+
* ```ts
|
|
442
|
+
* const doc = univerAPI.getActiveDocument();
|
|
443
|
+
* if (!doc) throw new Error('No active document');
|
|
444
|
+
*
|
|
445
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
446
|
+
* paragraph.removeFromParent();
|
|
447
|
+
* ```
|
|
448
|
+
*/
|
|
449
|
+
removeFromParent() {
|
|
450
|
+
return this._body.removeParagraph(this._key);
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Get this paragraph's plain text.
|
|
454
|
+
* @returns {string} The paragraph text without the trailing paragraph break.
|
|
455
|
+
* @example
|
|
456
|
+
* ```ts
|
|
457
|
+
* const doc = univerAPI.getActiveDocument();
|
|
458
|
+
* if (!doc) throw new Error('No active document');
|
|
459
|
+
*
|
|
460
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
461
|
+
* console.log(paragraph.getText());
|
|
462
|
+
* ```
|
|
463
|
+
*/
|
|
464
|
+
getText() {
|
|
465
|
+
return this._body.getParagraphText(this._key);
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Replace this paragraph's plain text.
|
|
469
|
+
* @param {string} text The replacement text. Do not include the paragraph break.
|
|
470
|
+
* @returns {boolean} `true` if the paragraph text was replaced.
|
|
471
|
+
* @example
|
|
472
|
+
* ```ts
|
|
473
|
+
* const doc = univerAPI.getActiveDocument();
|
|
474
|
+
* if (!doc) throw new Error('No active document');
|
|
475
|
+
*
|
|
476
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
477
|
+
* paragraph.setText('Updated title');
|
|
478
|
+
* ```
|
|
479
|
+
*/
|
|
480
|
+
setText(text) {
|
|
481
|
+
return this._body.setParagraphText(this._key, text);
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Append plain text before this paragraph's trailing paragraph break.
|
|
485
|
+
* @param {string} text The plain text to append.
|
|
486
|
+
* @returns {boolean} `true` if the text was appended.
|
|
487
|
+
* @example
|
|
488
|
+
* ```ts
|
|
489
|
+
* const doc = univerAPI.getActiveDocument();
|
|
490
|
+
* if (!doc) throw new Error('No active document');
|
|
491
|
+
*
|
|
492
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
493
|
+
* paragraph.appendText(' suffix');
|
|
494
|
+
* ```
|
|
495
|
+
*/
|
|
496
|
+
appendText(text) {
|
|
497
|
+
return this._body.appendParagraphText(this._key, text);
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Get the current text range occupied by this paragraph.
|
|
501
|
+
* @returns {IFDocTextRange} The paragraph text range, excluding the trailing paragraph break.
|
|
502
|
+
* @example
|
|
503
|
+
* ```ts
|
|
504
|
+
* const doc = univerAPI.getActiveDocument();
|
|
505
|
+
* if (!doc) throw new Error('No active document');
|
|
506
|
+
*
|
|
507
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
508
|
+
* const range = paragraph.getRange();
|
|
509
|
+
* doc.getBody().setTextStyle(range, { bl: 1 });
|
|
510
|
+
* ```
|
|
511
|
+
*/
|
|
512
|
+
getRange() {
|
|
513
|
+
return this._body.getParagraphRange(this._key);
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Check whether this paragraph is a bullet, ordered, or checklist item.
|
|
517
|
+
* @returns {boolean} `true` if the paragraph has list metadata.
|
|
518
|
+
* @example
|
|
519
|
+
* ```ts
|
|
520
|
+
* const doc = univerAPI.getActiveDocument();
|
|
521
|
+
* if (!doc) throw new Error('No active document');
|
|
522
|
+
*
|
|
523
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
524
|
+
* console.log(paragraph.isListItem());
|
|
525
|
+
* ```
|
|
526
|
+
*/
|
|
527
|
+
isListItem() {
|
|
528
|
+
return this._body.isListParagraph(this._key);
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Check whether this paragraph is a task/checklist item.
|
|
532
|
+
* @returns {boolean} `true` if this paragraph is an unchecked or checked task item.
|
|
533
|
+
* @example
|
|
534
|
+
* ```ts
|
|
535
|
+
* const doc = univerAPI.getActiveDocument();
|
|
536
|
+
* if (!doc) throw new Error('No active document');
|
|
537
|
+
*
|
|
538
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
539
|
+
* if (paragraph.isTask()) {
|
|
540
|
+
* paragraph.setTaskChecked(true);
|
|
541
|
+
* }
|
|
542
|
+
* ```
|
|
543
|
+
*/
|
|
544
|
+
isTask() {
|
|
545
|
+
return this._body.isTaskParagraph(this._key);
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Set the checked state of this task/checklist paragraph.
|
|
549
|
+
* @param {boolean} checked Whether the task item should be checked.
|
|
550
|
+
* @returns {boolean} `true` if the task state was updated, or `false` if this paragraph is not a task item.
|
|
551
|
+
* @example
|
|
552
|
+
* ```ts
|
|
553
|
+
* const doc = univerAPI.getActiveDocument();
|
|
554
|
+
* if (!doc) throw new Error('No active document');
|
|
555
|
+
*
|
|
556
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
557
|
+
* if (paragraph.isTask()) {
|
|
558
|
+
* paragraph.setTaskChecked(false);
|
|
559
|
+
* }
|
|
560
|
+
* ```
|
|
561
|
+
*/
|
|
562
|
+
setTaskChecked(checked) {
|
|
563
|
+
return this._body.setTaskChecked(this._key, checked);
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
//#endregion
|
|
568
|
+
//#region src/facade/f-doc-table.ts
|
|
569
|
+
/**
|
|
570
|
+
* A facade wrapper for a document table marker.
|
|
571
|
+
*
|
|
572
|
+
* Table identity is backed by the persisted `ICustomTable.tableId`, so the
|
|
573
|
+
* wrapper can be re-resolved after text is inserted before the table.
|
|
574
|
+
*
|
|
575
|
+
* @hideconstructor
|
|
576
|
+
*/
|
|
577
|
+
var FDocTable = class {
|
|
578
|
+
constructor(_body, _key) {
|
|
579
|
+
this._body = _body;
|
|
580
|
+
this._key = _key;
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Get the document element type.
|
|
584
|
+
* @returns {'table'} The literal table element type.
|
|
585
|
+
* @example
|
|
586
|
+
* ```ts
|
|
587
|
+
* const doc = univerAPI.getActiveDocument();
|
|
588
|
+
* if (!doc) throw new Error('No active document');
|
|
589
|
+
*
|
|
590
|
+
* const table = doc.getBody().getChild(0).asTable();
|
|
591
|
+
* console.log(table.getType());
|
|
592
|
+
* ```
|
|
593
|
+
*/
|
|
594
|
+
getType() {
|
|
595
|
+
return "table";
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Get the table key.
|
|
599
|
+
* @returns {string} The persisted `tableId` for this table.
|
|
600
|
+
* @example
|
|
601
|
+
* ```ts
|
|
602
|
+
* const doc = univerAPI.getActiveDocument();
|
|
603
|
+
* if (!doc) throw new Error('No active document');
|
|
604
|
+
*
|
|
605
|
+
* const table = doc.getBody().getChild(0).asTable();
|
|
606
|
+
* console.log(table.getKey());
|
|
607
|
+
* ```
|
|
608
|
+
*/
|
|
609
|
+
getKey() {
|
|
610
|
+
return this._key;
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Get the parent body facade that owns this table.
|
|
614
|
+
* @returns {FDocBody} The document body facade.
|
|
615
|
+
* @example
|
|
616
|
+
* ```ts
|
|
617
|
+
* const doc = univerAPI.getActiveDocument();
|
|
618
|
+
* if (!doc) throw new Error('No active document');
|
|
619
|
+
*
|
|
620
|
+
* const table = doc.getBody().getChild(0).asTable();
|
|
621
|
+
* console.log(table.getParent().getChildIndex(table));
|
|
622
|
+
* ```
|
|
623
|
+
*/
|
|
624
|
+
getParent() {
|
|
625
|
+
return this._body;
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Remove this table from the parent body.
|
|
629
|
+
* @returns {boolean} `true` if the table range was removed.
|
|
630
|
+
* @example
|
|
631
|
+
* ```ts
|
|
632
|
+
* const doc = univerAPI.getActiveDocument();
|
|
633
|
+
* if (!doc) throw new Error('No active document');
|
|
634
|
+
*
|
|
635
|
+
* const table = doc.getBody().getChild(0).asTable();
|
|
636
|
+
* table.removeFromParent();
|
|
637
|
+
* ```
|
|
638
|
+
*/
|
|
639
|
+
removeFromParent() {
|
|
640
|
+
return this._body.removeTable(this._key);
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Get the persisted table id.
|
|
644
|
+
* @returns {string} The `ICustomTable.tableId` value.
|
|
645
|
+
* @example
|
|
646
|
+
* ```ts
|
|
647
|
+
* const doc = univerAPI.getActiveDocument();
|
|
648
|
+
* if (!doc) throw new Error('No active document');
|
|
649
|
+
*
|
|
650
|
+
* const table = doc.getBody().getChild(0).asTable();
|
|
651
|
+
* console.log(table.getTableId());
|
|
652
|
+
* ```
|
|
653
|
+
*/
|
|
654
|
+
getTableId() {
|
|
655
|
+
return this._body.getTable(this._key).tableId;
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
//#endregion
|
|
660
|
+
//#region src/facade/f-doc-element.ts
|
|
661
|
+
/**
|
|
662
|
+
* A generic top-level document body element.
|
|
663
|
+
*
|
|
664
|
+
* Use this wrapper when you need to inspect an element type first, navigate to
|
|
665
|
+
* neighboring elements, or cast the element to a more specific facade wrapper.
|
|
666
|
+
*
|
|
667
|
+
* Paragraph keys are persisted `paragraphId` values. Tables, block ranges, and
|
|
668
|
+
* custom blocks use their persisted ids.
|
|
669
|
+
*
|
|
670
|
+
* @hideconstructor
|
|
671
|
+
*/
|
|
672
|
+
var FDocElement = class {
|
|
673
|
+
constructor(_body, _type, _key) {
|
|
674
|
+
this._body = _body;
|
|
675
|
+
this._type = _type;
|
|
676
|
+
this._key = _key;
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Get the document element type.
|
|
680
|
+
* @returns {FDocElementType} The element type, such as `paragraph`, `table`, `blockRange`, or `customBlock`.
|
|
681
|
+
* @example
|
|
682
|
+
* ```ts
|
|
683
|
+
* const doc = univerAPI.getActiveDocument();
|
|
684
|
+
* if (!doc) throw new Error('No active document');
|
|
685
|
+
*
|
|
686
|
+
* const element = doc.getBody().getChild(0);
|
|
687
|
+
* console.log(element.getType());
|
|
688
|
+
* ```
|
|
689
|
+
*/
|
|
690
|
+
getType() {
|
|
691
|
+
return this._type;
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Get the facade key used to resolve this element.
|
|
695
|
+
* @returns {string} The paragraph `paragraphId` or persisted table/block/custom block id.
|
|
696
|
+
* @example
|
|
697
|
+
* ```ts
|
|
698
|
+
* const doc = univerAPI.getActiveDocument();
|
|
699
|
+
* if (!doc) throw new Error('No active document');
|
|
700
|
+
*
|
|
701
|
+
* const body = doc.getBody();
|
|
702
|
+
* const element = body.getChild(0);
|
|
703
|
+
* console.log(element.getKey());
|
|
704
|
+
* ```
|
|
705
|
+
*/
|
|
706
|
+
getKey() {
|
|
707
|
+
return this._key;
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Get the parent body facade that owns this element.
|
|
711
|
+
* @returns {FDocBody} The document body facade.
|
|
712
|
+
* @example
|
|
713
|
+
* ```ts
|
|
714
|
+
* const doc = univerAPI.getActiveDocument();
|
|
715
|
+
* if (!doc) throw new Error('No active document');
|
|
716
|
+
*
|
|
717
|
+
* const element = doc.getBody().getChild(0);
|
|
718
|
+
* console.log(element.getParent().getNumChildren());
|
|
719
|
+
* ```
|
|
720
|
+
*/
|
|
721
|
+
getParent() {
|
|
722
|
+
return this._body;
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* Get the next sibling element in the current body order.
|
|
726
|
+
* @returns {FDocElement | null} The next sibling wrapper, or `null` when this is the last child.
|
|
727
|
+
* @example
|
|
728
|
+
* ```ts
|
|
729
|
+
* const doc = univerAPI.getActiveDocument();
|
|
730
|
+
* if (!doc) throw new Error('No active document');
|
|
731
|
+
*
|
|
732
|
+
* const first = doc.getBody().getChild(0);
|
|
733
|
+
* const next = first.getNextSibling();
|
|
734
|
+
* console.log(next?.getType());
|
|
735
|
+
* ```
|
|
736
|
+
*/
|
|
737
|
+
getNextSibling() {
|
|
738
|
+
return this._body.createSibling(this._type, this._key, 1);
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* Get the previous sibling element in the current body order.
|
|
742
|
+
* @returns {FDocElement | null} The previous sibling wrapper, or `null` when this is the first child.
|
|
743
|
+
* @example
|
|
744
|
+
* ```ts
|
|
745
|
+
* const doc = univerAPI.getActiveDocument();
|
|
746
|
+
* if (!doc) throw new Error('No active document');
|
|
747
|
+
*
|
|
748
|
+
* const second = doc.getBody().getChild(1);
|
|
749
|
+
* const previous = second.getPreviousSibling();
|
|
750
|
+
* console.log(previous?.getType());
|
|
751
|
+
* ```
|
|
752
|
+
*/
|
|
753
|
+
getPreviousSibling() {
|
|
754
|
+
return this._body.createSibling(this._type, this._key, -1);
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Remove this element from its parent body.
|
|
758
|
+
* @returns {boolean} `true` if the element content was removed.
|
|
759
|
+
* @example
|
|
760
|
+
* ```ts
|
|
761
|
+
* const doc = univerAPI.getActiveDocument();
|
|
762
|
+
* if (!doc) throw new Error('No active document');
|
|
763
|
+
*
|
|
764
|
+
* const body = doc.getBody();
|
|
765
|
+
* const element = body.getChild(0);
|
|
766
|
+
* element.removeFromParent();
|
|
767
|
+
* ```
|
|
768
|
+
*/
|
|
769
|
+
removeFromParent() {
|
|
770
|
+
if (this._type === "paragraph") return this._body.removeParagraph(this._key);
|
|
771
|
+
if (this._type === "blockRange") return this._body.removeBlockRange(this._key);
|
|
772
|
+
if (this._type === "table") return this._body.removeTable(this._key);
|
|
773
|
+
return this._body.removeCustomBlock(this._key);
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Cast this generic element to a paragraph facade.
|
|
777
|
+
* @returns {FDocParagraph} The paragraph facade wrapper.
|
|
778
|
+
* @throws {TypeError} If the element is not a paragraph.
|
|
779
|
+
* @example
|
|
780
|
+
* ```ts
|
|
781
|
+
* const doc = univerAPI.getActiveDocument();
|
|
782
|
+
* if (!doc) throw new Error('No active document');
|
|
783
|
+
*
|
|
784
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
785
|
+
* console.log(paragraph.getText());
|
|
786
|
+
* ```
|
|
787
|
+
*/
|
|
788
|
+
asParagraph() {
|
|
789
|
+
this._assertType("paragraph");
|
|
790
|
+
return new FDocParagraph(this._body, this._key);
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Cast this generic element to a table facade.
|
|
794
|
+
* @returns {FDocTable} The table facade wrapper.
|
|
795
|
+
* @throws {TypeError} If the element is not a table.
|
|
796
|
+
* @example
|
|
797
|
+
* ```ts
|
|
798
|
+
* const doc = univerAPI.getActiveDocument();
|
|
799
|
+
* if (!doc) throw new Error('No active document');
|
|
800
|
+
*
|
|
801
|
+
* const table = doc.getBody().getChild(0).asTable();
|
|
802
|
+
* console.log(table.getTableId());
|
|
803
|
+
* ```
|
|
804
|
+
*/
|
|
805
|
+
asTable() {
|
|
806
|
+
this._assertType("table");
|
|
807
|
+
return new FDocTable(this._body, this._key);
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Cast this generic element to a callout, quote, or code block range facade.
|
|
811
|
+
* @returns {FDocBlockRange} The block range facade wrapper.
|
|
812
|
+
* @throws {TypeError} If the element is not a block range.
|
|
813
|
+
* @example
|
|
814
|
+
* ```ts
|
|
815
|
+
* const doc = univerAPI.getActiveDocument();
|
|
816
|
+
* if (!doc) throw new Error('No active document');
|
|
817
|
+
*
|
|
818
|
+
* const blockRange = doc.getBody().getChild(0).asBlockRange();
|
|
819
|
+
* console.log(blockRange.getBlockType());
|
|
820
|
+
* ```
|
|
821
|
+
*/
|
|
822
|
+
asBlockRange() {
|
|
823
|
+
this._assertType("blockRange");
|
|
824
|
+
return new FDocBlockRange(this._body, this._key);
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Cast this generic element to a custom block facade.
|
|
828
|
+
* @returns {FDocCustomBlock} The custom block facade wrapper.
|
|
829
|
+
* @throws {TypeError} If the element is not a custom block.
|
|
830
|
+
* @example
|
|
831
|
+
* ```ts
|
|
832
|
+
* const doc = univerAPI.getActiveDocument();
|
|
833
|
+
* if (!doc) throw new Error('No active document');
|
|
834
|
+
*
|
|
835
|
+
* const customBlock = doc.getBody().getChild(0).asCustomBlock();
|
|
836
|
+
* console.log(customBlock.getBlockId());
|
|
837
|
+
* ```
|
|
838
|
+
*/
|
|
839
|
+
asCustomBlock() {
|
|
840
|
+
this._assertType("customBlock");
|
|
841
|
+
return new FDocCustomBlock(this._body, this._key);
|
|
842
|
+
}
|
|
843
|
+
_assertType(type) {
|
|
844
|
+
if (this._type !== type) throw new TypeError(`Cannot cast ${this._type} to ${type}.`);
|
|
845
|
+
}
|
|
846
|
+
};
|
|
847
|
+
|
|
848
|
+
//#endregion
|
|
849
|
+
//#region src/facade/f-doc-body.ts
|
|
850
|
+
function isRichTextLike(value) {
|
|
851
|
+
return typeof value === "object" && value !== null && "getData" in value && typeof value.getData === "function";
|
|
852
|
+
}
|
|
853
|
+
const FACADE_TRIGGER = "doc-facade";
|
|
854
|
+
const RESTORE_INSERTED_PARAGRAPH_IDS = "__textXRestoreParagraphIds";
|
|
855
|
+
/**
|
|
856
|
+
* A Facade API object bounded to a document body or header/footer segment.
|
|
857
|
+
* It provides Google Docs-like element access and range editing methods.
|
|
858
|
+
*
|
|
859
|
+
* Paragraph elements use their persisted `paragraphId`. Tables, block ranges, and
|
|
860
|
+
* custom blocks use their persisted ids.
|
|
861
|
+
*
|
|
862
|
+
* @hideconstructor
|
|
863
|
+
*/
|
|
864
|
+
var FDocBody = class {
|
|
865
|
+
constructor(_documentDataModel, _commandService, _registry, _segmentId = "") {
|
|
866
|
+
this._documentDataModel = _documentDataModel;
|
|
867
|
+
this._commandService = _commandService;
|
|
868
|
+
this._segmentId = _segmentId;
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Get the number of top-level child elements in the body.
|
|
872
|
+
* @returns {number} The number of child elements.
|
|
873
|
+
* @example
|
|
874
|
+
* ```ts
|
|
875
|
+
* const doc = univerAPI.getActiveDocument();
|
|
876
|
+
* if (!doc) throw new Error('No active document');
|
|
877
|
+
*
|
|
878
|
+
* const body = doc.getBody();
|
|
879
|
+
* console.log(body.getNumChildren());
|
|
880
|
+
* ```
|
|
881
|
+
*/
|
|
882
|
+
getNumChildren() {
|
|
883
|
+
return this._getChildren().length;
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* Get a top-level child element by child index.
|
|
887
|
+
* @param {number} index The zero-based child index.
|
|
888
|
+
* @returns {FDocElement} The child element wrapper.
|
|
889
|
+
* @example
|
|
890
|
+
* ```ts
|
|
891
|
+
* const doc = univerAPI.getActiveDocument();
|
|
892
|
+
* if (!doc) throw new Error('No active document');
|
|
893
|
+
*
|
|
894
|
+
* const body = doc.getBody();
|
|
895
|
+
* const firstChild = body.getChild(0);
|
|
896
|
+
* console.log(firstChild.getType());
|
|
897
|
+
* ```
|
|
898
|
+
*/
|
|
899
|
+
getChild(index) {
|
|
900
|
+
const child = this._getChildren()[index];
|
|
901
|
+
if (!child) throw new RangeError(`Child index ${index} is out of range.`);
|
|
902
|
+
return this._createElement(child.type, child.key);
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Get the current child index of an element handle.
|
|
906
|
+
* The index is resolved from the element key, so a paragraph handle keeps pointing
|
|
907
|
+
* to the same paragraph after facade edits insert content before it.
|
|
908
|
+
* @param {IFDocElementHandle} element The element handle to locate.
|
|
909
|
+
* @returns {number} The current zero-based child index.
|
|
910
|
+
* @example
|
|
911
|
+
* ```ts
|
|
912
|
+
* const doc = univerAPI.getActiveDocument();
|
|
913
|
+
* if (!doc) throw new Error('No active document');
|
|
914
|
+
*
|
|
915
|
+
* const body = doc.getBody();
|
|
916
|
+
* const paragraph = body.getChild(1).asParagraph();
|
|
917
|
+
* body.insertParagraph(0, 'Intro');
|
|
918
|
+
* console.log(body.getChildIndex(paragraph));
|
|
919
|
+
* ```
|
|
920
|
+
*/
|
|
921
|
+
getChildIndex(element) {
|
|
922
|
+
const resolved = this.resolveElement(element.getType(), element.getKey());
|
|
923
|
+
const index = this._getChildren().findIndex((child) => child.type === resolved.type && child.key === resolved.key);
|
|
924
|
+
if (index < 0) throw new Error("Doc element is stale");
|
|
925
|
+
return index;
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* Insert plain text at a document body offset.
|
|
929
|
+
* @param {number} index The zero-based insertion offset.
|
|
930
|
+
* @param {string} text The plain text to insert.
|
|
931
|
+
* @returns {boolean} `true` if the edit was applied.
|
|
932
|
+
* @example
|
|
933
|
+
* ```ts
|
|
934
|
+
* const doc = univerAPI.getActiveDocument();
|
|
935
|
+
* if (!doc) throw new Error('No active document');
|
|
936
|
+
*
|
|
937
|
+
* const body = doc.getBody();
|
|
938
|
+
* body.insertText(0, 'Hello ');
|
|
939
|
+
* ```
|
|
940
|
+
*/
|
|
941
|
+
insertText(index, text) {
|
|
942
|
+
return this._replaceBodyRange({
|
|
943
|
+
startOffset: index,
|
|
944
|
+
endOffset: index
|
|
945
|
+
}, buildPlainTextInsertBody(text));
|
|
946
|
+
}
|
|
947
|
+
/**
|
|
948
|
+
* Insert a plain-text paragraph before the paragraph at the given paragraph index.
|
|
949
|
+
* @param {number} index The zero-based paragraph insertion index.
|
|
950
|
+
* @param {string} text The paragraph text. Defaults to an empty paragraph.
|
|
951
|
+
* @returns {FDocParagraph} The inserted paragraph wrapper.
|
|
952
|
+
* @example
|
|
953
|
+
* ```ts
|
|
954
|
+
* const doc = univerAPI.getActiveDocument();
|
|
955
|
+
* if (!doc) throw new Error('No active document');
|
|
956
|
+
*
|
|
957
|
+
* const body = doc.getBody();
|
|
958
|
+
* const paragraph = body.insertParagraph(0, 'Document title');
|
|
959
|
+
* paragraph.appendText(' suffix');
|
|
960
|
+
* ```
|
|
961
|
+
*/
|
|
962
|
+
insertParagraph(index, text = "") {
|
|
963
|
+
var _this$_getBody$paragr;
|
|
964
|
+
const offset = this._getParagraphInsertOffset(index);
|
|
965
|
+
if (!this._replaceBodyRange({
|
|
966
|
+
startOffset: offset,
|
|
967
|
+
endOffset: offset
|
|
968
|
+
}, buildPlainTextInsertBody(`${text}\r`))) throw new Error("Failed to insert paragraph.");
|
|
969
|
+
const paragraphIndex = this._normalizeInsertedParagraphIndex(index);
|
|
970
|
+
const paragraph = (_this$_getBody$paragr = this._getBody().paragraphs) === null || _this$_getBody$paragr === void 0 ? void 0 : _this$_getBody$paragr[paragraphIndex];
|
|
971
|
+
return new FDocParagraph(this, this._getParagraphId(paragraph, paragraphIndex));
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* Append a plain-text paragraph at the end of the body.
|
|
975
|
+
* @param {string} text The paragraph text. Defaults to an empty paragraph.
|
|
976
|
+
* @returns {FDocParagraph} The appended paragraph wrapper.
|
|
977
|
+
* @example
|
|
978
|
+
* ```ts
|
|
979
|
+
* const doc = univerAPI.getActiveDocument();
|
|
980
|
+
* if (!doc) throw new Error('No active document');
|
|
981
|
+
*
|
|
982
|
+
* const body = doc.getBody();
|
|
983
|
+
* const paragraph = body.appendParagraph('Summary');
|
|
984
|
+
* console.log(paragraph.getText());
|
|
985
|
+
* ```
|
|
986
|
+
*/
|
|
987
|
+
appendParagraph(text = "") {
|
|
988
|
+
var _this$_getBody$paragr2, _this$_getBody$paragr3;
|
|
989
|
+
return this.insertParagraph((_this$_getBody$paragr2 = (_this$_getBody$paragr3 = this._getBody().paragraphs) === null || _this$_getBody$paragr3 === void 0 ? void 0 : _this$_getBody$paragr3.length) !== null && _this$_getBody$paragr2 !== void 0 ? _this$_getBody$paragr2 : 0, text);
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* Delete a range from the body.
|
|
993
|
+
* @param {IFDocTextRange} range The text range to delete.
|
|
994
|
+
* @returns {boolean} `true` if the range was deleted.
|
|
995
|
+
* @example
|
|
996
|
+
* ```ts
|
|
997
|
+
* const doc = univerAPI.getActiveDocument();
|
|
998
|
+
* if (!doc) throw new Error('No active document');
|
|
999
|
+
*
|
|
1000
|
+
* const body = doc.getBody();
|
|
1001
|
+
* body.deleteRange({ startOffset: 0, endOffset: 5 });
|
|
1002
|
+
* ```
|
|
1003
|
+
*/
|
|
1004
|
+
deleteRange(range) {
|
|
1005
|
+
return this._replaceBodyRange(range, { dataStream: "" });
|
|
1006
|
+
}
|
|
1007
|
+
/**
|
|
1008
|
+
* Replace a range with plain text or rich text body data.
|
|
1009
|
+
* @param {IFDocTextRange} range The text range to replace.
|
|
1010
|
+
* @param {string | IFDocRichTextLike | { body?: IDocumentBody }} value The replacement text or rich-text-like value.
|
|
1011
|
+
* @returns {boolean} `true` if the replacement was applied.
|
|
1012
|
+
* @example
|
|
1013
|
+
* ```ts
|
|
1014
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1015
|
+
* if (!doc) throw new Error('No active document');
|
|
1016
|
+
*
|
|
1017
|
+
* const body = doc.getBody();
|
|
1018
|
+
* body.replaceRange({ startOffset: 0, endOffset: 5 }, 'Hello');
|
|
1019
|
+
* ```
|
|
1020
|
+
*/
|
|
1021
|
+
replaceRange(range, value) {
|
|
1022
|
+
let body;
|
|
1023
|
+
if (typeof value === "string") body = buildPlainTextInsertBody(value);
|
|
1024
|
+
else if (isRichTextLike(value)) {
|
|
1025
|
+
var _value$getData$body;
|
|
1026
|
+
body = (_value$getData$body = value.getData().body) !== null && _value$getData$body !== void 0 ? _value$getData$body : { dataStream: "" };
|
|
1027
|
+
} else {
|
|
1028
|
+
var _value$body;
|
|
1029
|
+
body = (_value$body = value.body) !== null && _value$body !== void 0 ? _value$body : { dataStream: "" };
|
|
1030
|
+
}
|
|
1031
|
+
return this._replaceBodyRange(range, body);
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
1034
|
+
* Apply text style to a body range.
|
|
1035
|
+
* @param {IFDocTextRange} range The range to style.
|
|
1036
|
+
* @param {ITextStyle} style The Univer text style patch.
|
|
1037
|
+
* @returns {boolean} `true` if the style was applied.
|
|
1038
|
+
* @example
|
|
1039
|
+
* ```ts
|
|
1040
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1041
|
+
* if (!doc) throw new Error('No active document');
|
|
1042
|
+
*
|
|
1043
|
+
* const body = doc.getBody();
|
|
1044
|
+
* body.setTextStyle({ startOffset: 0, endOffset: 5 }, { bl: 1 });
|
|
1045
|
+
* ```
|
|
1046
|
+
*/
|
|
1047
|
+
setTextStyle(range, style) {
|
|
1048
|
+
const updateBody = {
|
|
1049
|
+
dataStream: "",
|
|
1050
|
+
textRuns: [{
|
|
1051
|
+
st: 0,
|
|
1052
|
+
ed: range.endOffset - range.startOffset,
|
|
1053
|
+
ts: style
|
|
1054
|
+
}]
|
|
1055
|
+
};
|
|
1056
|
+
return this._retainBodyRange(range, updateBody, UpdateDocsAttributeType.COVER);
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Apply paragraph style to a paragraph handle or text range.
|
|
1060
|
+
* @param {FDocElement | FDocParagraph | IFDocTextRange} paragraph The paragraph handle or a range inside the paragraph.
|
|
1061
|
+
* @param {IParagraphStyle} style The Univer paragraph style patch.
|
|
1062
|
+
* @returns {boolean} `true` if the style was applied.
|
|
1063
|
+
* @example
|
|
1064
|
+
* ```ts
|
|
1065
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1066
|
+
* if (!doc) throw new Error('No active document');
|
|
1067
|
+
*
|
|
1068
|
+
* const body = doc.getBody();
|
|
1069
|
+
* const paragraph = body.getChild(0).asParagraph();
|
|
1070
|
+
* body.setParagraphStyle(paragraph, { horizontalAlign: 2 });
|
|
1071
|
+
* ```
|
|
1072
|
+
*/
|
|
1073
|
+
setParagraphStyle(paragraph, style) {
|
|
1074
|
+
const resolved = paragraph instanceof FDocElement || paragraph instanceof FDocParagraph ? this.resolveParagraph(paragraph.getKey()) : this._findParagraphByRange(paragraph);
|
|
1075
|
+
const updateBody = {
|
|
1076
|
+
dataStream: "",
|
|
1077
|
+
paragraphs: [{
|
|
1078
|
+
...resolved.paragraph,
|
|
1079
|
+
startIndex: 0,
|
|
1080
|
+
paragraphStyle: {
|
|
1081
|
+
...resolved.paragraph.paragraphStyle,
|
|
1082
|
+
...style
|
|
1083
|
+
}
|
|
1084
|
+
}]
|
|
1085
|
+
};
|
|
1086
|
+
this._preserveExplicitParagraphIds(updateBody);
|
|
1087
|
+
return this._retainBodyRange({
|
|
1088
|
+
startOffset: resolved.endOffset,
|
|
1089
|
+
endOffset: resolved.endOffset + 1
|
|
1090
|
+
}, updateBody, UpdateDocsAttributeType.REPLACE);
|
|
1091
|
+
}
|
|
1092
|
+
/**
|
|
1093
|
+
* Get the text content of a paragraph by paragraph id.
|
|
1094
|
+
* @param {string} key The paragraph id.
|
|
1095
|
+
* @returns {string} The paragraph text without the trailing paragraph break.
|
|
1096
|
+
* @example
|
|
1097
|
+
* ```ts
|
|
1098
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1099
|
+
* if (!doc) throw new Error('No active document');
|
|
1100
|
+
*
|
|
1101
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
1102
|
+
* console.log(doc.getBody().getParagraphText(paragraph.getKey()));
|
|
1103
|
+
* ```
|
|
1104
|
+
*/
|
|
1105
|
+
getParagraphText(key) {
|
|
1106
|
+
const resolved = this.resolveParagraph(key);
|
|
1107
|
+
return this._getBody().dataStream.slice(resolved.startOffset, resolved.endOffset);
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Replace the text content of a paragraph by paragraph id.
|
|
1111
|
+
* @param {string} key The paragraph id.
|
|
1112
|
+
* @param {string} text The replacement paragraph text.
|
|
1113
|
+
* @returns {boolean} `true` if the paragraph text was replaced.
|
|
1114
|
+
* @example
|
|
1115
|
+
* ```ts
|
|
1116
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1117
|
+
* if (!doc) throw new Error('No active document');
|
|
1118
|
+
*
|
|
1119
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
1120
|
+
* doc.getBody().setParagraphText(paragraph.getKey(), 'Updated text');
|
|
1121
|
+
* ```
|
|
1122
|
+
*/
|
|
1123
|
+
setParagraphText(key, text) {
|
|
1124
|
+
const resolved = this.resolveParagraph(key);
|
|
1125
|
+
return this.replaceRange({
|
|
1126
|
+
startOffset: resolved.startOffset,
|
|
1127
|
+
endOffset: resolved.endOffset
|
|
1128
|
+
}, text);
|
|
1129
|
+
}
|
|
1130
|
+
/**
|
|
1131
|
+
* Append text to a paragraph by paragraph id.
|
|
1132
|
+
* @param {string} key The paragraph id.
|
|
1133
|
+
* @param {string} text The text to append before the paragraph break.
|
|
1134
|
+
* @returns {boolean} `true` if the text was appended.
|
|
1135
|
+
* @example
|
|
1136
|
+
* ```ts
|
|
1137
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1138
|
+
* if (!doc) throw new Error('No active document');
|
|
1139
|
+
*
|
|
1140
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
1141
|
+
* doc.getBody().appendParagraphText(paragraph.getKey(), ' suffix');
|
|
1142
|
+
* ```
|
|
1143
|
+
*/
|
|
1144
|
+
appendParagraphText(key, text) {
|
|
1145
|
+
const resolved = this.resolveParagraph(key);
|
|
1146
|
+
return this.insertText(resolved.endOffset, text);
|
|
1147
|
+
}
|
|
1148
|
+
/**
|
|
1149
|
+
* Remove a paragraph by paragraph id.
|
|
1150
|
+
* @param {string} key The paragraph id.
|
|
1151
|
+
* @returns {boolean} `true` if the paragraph was removed.
|
|
1152
|
+
* @example
|
|
1153
|
+
* ```ts
|
|
1154
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1155
|
+
* if (!doc) throw new Error('No active document');
|
|
1156
|
+
*
|
|
1157
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
1158
|
+
* doc.getBody().removeParagraph(paragraph.getKey());
|
|
1159
|
+
* ```
|
|
1160
|
+
*/
|
|
1161
|
+
removeParagraph(key) {
|
|
1162
|
+
const resolved = this.resolveParagraph(key);
|
|
1163
|
+
return this.deleteRange({
|
|
1164
|
+
startOffset: resolved.startOffset,
|
|
1165
|
+
endOffset: resolved.endOffset + 1
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
/**
|
|
1169
|
+
* Get a paragraph text range by paragraph id.
|
|
1170
|
+
* @param {string} key The paragraph id.
|
|
1171
|
+
* @returns {IFDocTextRange} The paragraph range excluding the trailing paragraph break.
|
|
1172
|
+
* @example
|
|
1173
|
+
* ```ts
|
|
1174
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1175
|
+
* if (!doc) throw new Error('No active document');
|
|
1176
|
+
*
|
|
1177
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
1178
|
+
* const range = doc.getBody().getParagraphRange(paragraph.getKey());
|
|
1179
|
+
* console.log(range.startOffset, range.endOffset);
|
|
1180
|
+
* ```
|
|
1181
|
+
*/
|
|
1182
|
+
getParagraphRange(key) {
|
|
1183
|
+
const resolved = this.resolveParagraph(key);
|
|
1184
|
+
return {
|
|
1185
|
+
startOffset: resolved.startOffset,
|
|
1186
|
+
endOffset: resolved.endOffset,
|
|
1187
|
+
segmentId: this._segmentId
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Check whether a paragraph has list metadata.
|
|
1192
|
+
* @param {string} key The paragraph id.
|
|
1193
|
+
* @returns {boolean} `true` if the paragraph is a list item.
|
|
1194
|
+
* @example
|
|
1195
|
+
* ```ts
|
|
1196
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1197
|
+
* if (!doc) throw new Error('No active document');
|
|
1198
|
+
*
|
|
1199
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
1200
|
+
* console.log(doc.getBody().isListParagraph(paragraph.getKey()));
|
|
1201
|
+
* ```
|
|
1202
|
+
*/
|
|
1203
|
+
isListParagraph(key) {
|
|
1204
|
+
return Boolean(this.resolveParagraph(key).paragraph.bullet);
|
|
1205
|
+
}
|
|
1206
|
+
/**
|
|
1207
|
+
* Check whether a paragraph is a task/checklist item.
|
|
1208
|
+
* @param {string} key The paragraph id.
|
|
1209
|
+
* @returns {boolean} `true` if the paragraph is an unchecked or checked task item.
|
|
1210
|
+
* @example
|
|
1211
|
+
* ```ts
|
|
1212
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1213
|
+
* if (!doc) throw new Error('No active document');
|
|
1214
|
+
*
|
|
1215
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
1216
|
+
* console.log(doc.getBody().isTaskParagraph(paragraph.getKey()));
|
|
1217
|
+
* ```
|
|
1218
|
+
*/
|
|
1219
|
+
isTaskParagraph(key) {
|
|
1220
|
+
var _this$resolveParagrap;
|
|
1221
|
+
const listType = (_this$resolveParagrap = this.resolveParagraph(key).paragraph.bullet) === null || _this$resolveParagrap === void 0 ? void 0 : _this$resolveParagrap.listType;
|
|
1222
|
+
return listType === PresetListType.CHECK_LIST || listType === PresetListType.CHECK_LIST_CHECKED;
|
|
1223
|
+
}
|
|
1224
|
+
/**
|
|
1225
|
+
* Set the checked state of a task/checklist paragraph.
|
|
1226
|
+
* @param {string} key The paragraph id.
|
|
1227
|
+
* @param {boolean} checked Whether the task should be checked.
|
|
1228
|
+
* @returns {boolean} `true` if the task state was updated, or `false` if the paragraph is not a task item.
|
|
1229
|
+
* @example
|
|
1230
|
+
* ```ts
|
|
1231
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1232
|
+
* if (!doc) throw new Error('No active document');
|
|
1233
|
+
*
|
|
1234
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
1235
|
+
* doc.getBody().setTaskChecked(paragraph.getKey(), true);
|
|
1236
|
+
* ```
|
|
1237
|
+
*/
|
|
1238
|
+
setTaskChecked(key, checked) {
|
|
1239
|
+
const resolved = this.resolveParagraph(key);
|
|
1240
|
+
const bullet = resolved.paragraph.bullet;
|
|
1241
|
+
if (!bullet || !this.isTaskParagraph(key)) return false;
|
|
1242
|
+
const updateBody = {
|
|
1243
|
+
dataStream: "",
|
|
1244
|
+
paragraphs: [{
|
|
1245
|
+
...resolved.paragraph,
|
|
1246
|
+
startIndex: 0,
|
|
1247
|
+
bullet: {
|
|
1248
|
+
...bullet,
|
|
1249
|
+
listType: checked ? PresetListType.CHECK_LIST_CHECKED : PresetListType.CHECK_LIST
|
|
1250
|
+
}
|
|
1251
|
+
}]
|
|
1252
|
+
};
|
|
1253
|
+
this._preserveExplicitParagraphIds(updateBody);
|
|
1254
|
+
return this._retainBodyRange({
|
|
1255
|
+
startOffset: resolved.endOffset,
|
|
1256
|
+
endOffset: resolved.endOffset + 1
|
|
1257
|
+
}, updateBody, UpdateDocsAttributeType.REPLACE);
|
|
1258
|
+
}
|
|
1259
|
+
/**
|
|
1260
|
+
* Resolve a paragraph id to its current paragraph metadata.
|
|
1261
|
+
* @param {string} key The paragraph id.
|
|
1262
|
+
* @returns {IFDocResolvedParagraph} The current paragraph metadata.
|
|
1263
|
+
* @example
|
|
1264
|
+
* ```ts
|
|
1265
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1266
|
+
* if (!doc) throw new Error('No active document');
|
|
1267
|
+
*
|
|
1268
|
+
* const paragraph = doc.getBody().getChild(0).asParagraph();
|
|
1269
|
+
* const resolved = doc.getBody().resolveParagraph(paragraph.getKey());
|
|
1270
|
+
* console.log(resolved.paragraphIndex);
|
|
1271
|
+
* ```
|
|
1272
|
+
*/
|
|
1273
|
+
resolveParagraph(key) {
|
|
1274
|
+
var _body$paragraphs;
|
|
1275
|
+
const body = this._getBody();
|
|
1276
|
+
const matches = ((_body$paragraphs = body.paragraphs) !== null && _body$paragraphs !== void 0 ? _body$paragraphs : []).map((paragraph, paragraphIndex) => ({
|
|
1277
|
+
paragraph,
|
|
1278
|
+
paragraphIndex
|
|
1279
|
+
})).filter(({ paragraph }) => paragraph.paragraphId === key);
|
|
1280
|
+
if (matches.length !== 1) throw new DocElementStaleError(matches.length > 1 ? `Doc paragraph id "${key}" is duplicated.` : `Doc paragraph id "${key}" is stale.`);
|
|
1281
|
+
const { paragraph, paragraphIndex } = matches[0];
|
|
1282
|
+
return {
|
|
1283
|
+
paragraph,
|
|
1284
|
+
paragraphIndex,
|
|
1285
|
+
startOffset: paragraphIndex > 0 ? body.paragraphs[paragraphIndex - 1].startIndex + 1 : 0,
|
|
1286
|
+
endOffset: paragraph.startIndex
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
/**
|
|
1290
|
+
* Resolve an element key to its current child metadata.
|
|
1291
|
+
* @param {FDocElementType} type The element type.
|
|
1292
|
+
* @param {string} key The persisted element key.
|
|
1293
|
+
* @returns {object} The current child metadata used by the facade.
|
|
1294
|
+
* @example
|
|
1295
|
+
* ```ts
|
|
1296
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1297
|
+
* if (!doc) throw new Error('No active document');
|
|
1298
|
+
*
|
|
1299
|
+
* const element = doc.getBody().getChild(0);
|
|
1300
|
+
* const resolved = doc.getBody().resolveElement(element.getType(), element.getKey());
|
|
1301
|
+
* console.log(resolved.position);
|
|
1302
|
+
* ```
|
|
1303
|
+
*/
|
|
1304
|
+
resolveElement(type, key) {
|
|
1305
|
+
if (type === "paragraph") return {
|
|
1306
|
+
type,
|
|
1307
|
+
key,
|
|
1308
|
+
position: this.resolveParagraph(key).startOffset,
|
|
1309
|
+
priority: 3
|
|
1310
|
+
};
|
|
1311
|
+
const child = this._getChildren().find((item) => item.type === type && item.key === key);
|
|
1312
|
+
if (!child) throw new Error("Doc element is stale");
|
|
1313
|
+
return child;
|
|
1314
|
+
}
|
|
1315
|
+
/**
|
|
1316
|
+
* Get a callout, quote, or code block range by block id.
|
|
1317
|
+
* @param {string} key The persisted block range id.
|
|
1318
|
+
* @returns {IDocumentBlockRange} The matching block range snapshot.
|
|
1319
|
+
* @example
|
|
1320
|
+
* ```ts
|
|
1321
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1322
|
+
* if (!doc) throw new Error('No active document');
|
|
1323
|
+
*
|
|
1324
|
+
* const block = doc.getBody().getChild(0).asBlockRange();
|
|
1325
|
+
* console.log(doc.getBody().getBlockRange(block.getKey()).blockType);
|
|
1326
|
+
* ```
|
|
1327
|
+
*/
|
|
1328
|
+
getBlockRange(key) {
|
|
1329
|
+
var _this$_getBody$blockR;
|
|
1330
|
+
const blockRange = (_this$_getBody$blockR = this._getBody().blockRanges) === null || _this$_getBody$blockR === void 0 ? void 0 : _this$_getBody$blockR.find((item) => item.blockId === key);
|
|
1331
|
+
if (!blockRange) throw new Error("Doc element is stale");
|
|
1332
|
+
return blockRange;
|
|
1333
|
+
}
|
|
1334
|
+
/**
|
|
1335
|
+
* Get the text inside a callout, quote, or code block range.
|
|
1336
|
+
* @param {string} key The persisted block range id.
|
|
1337
|
+
* @returns {string} The block range text.
|
|
1338
|
+
* @example
|
|
1339
|
+
* ```ts
|
|
1340
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1341
|
+
* if (!doc) throw new Error('No active document');
|
|
1342
|
+
*
|
|
1343
|
+
* const block = doc.getBody().getChild(0).asBlockRange();
|
|
1344
|
+
* console.log(doc.getBody().getBlockRangeText(block.getKey()));
|
|
1345
|
+
* ```
|
|
1346
|
+
*/
|
|
1347
|
+
getBlockRangeText(key) {
|
|
1348
|
+
const blockRange = this.getBlockRange(key);
|
|
1349
|
+
return this._getBody().dataStream.slice(blockRange.startIndex, blockRange.endIndex);
|
|
1350
|
+
}
|
|
1351
|
+
/**
|
|
1352
|
+
* Replace the text inside a callout, quote, or code block range.
|
|
1353
|
+
* @param {string} key The persisted block range id.
|
|
1354
|
+
* @param {string} text The replacement text.
|
|
1355
|
+
* @returns {boolean} `true` if the text was replaced.
|
|
1356
|
+
* @example
|
|
1357
|
+
* ```ts
|
|
1358
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1359
|
+
* if (!doc) throw new Error('No active document');
|
|
1360
|
+
*
|
|
1361
|
+
* const block = doc.getBody().getChild(0).asBlockRange();
|
|
1362
|
+
* doc.getBody().setBlockRangeText(block.getKey(), 'Updated block');
|
|
1363
|
+
* ```
|
|
1364
|
+
*/
|
|
1365
|
+
setBlockRangeText(key, text) {
|
|
1366
|
+
const blockRange = this.getBlockRange(key);
|
|
1367
|
+
const body = buildPlainTextInsertBody(`${text}\r`);
|
|
1368
|
+
body.blockRanges = [{
|
|
1369
|
+
...blockRange,
|
|
1370
|
+
startIndex: 0,
|
|
1371
|
+
endIndex: text.length
|
|
1372
|
+
}];
|
|
1373
|
+
return this.replaceRange({
|
|
1374
|
+
startOffset: blockRange.startIndex,
|
|
1375
|
+
endOffset: blockRange.endIndex + 1
|
|
1376
|
+
}, { body });
|
|
1377
|
+
}
|
|
1378
|
+
/**
|
|
1379
|
+
* Remove a callout, quote, or code block range and its content.
|
|
1380
|
+
* @param {string} key The persisted block range id.
|
|
1381
|
+
* @returns {boolean} `true` if the block range content was removed.
|
|
1382
|
+
* @example
|
|
1383
|
+
* ```ts
|
|
1384
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1385
|
+
* if (!doc) throw new Error('No active document');
|
|
1386
|
+
*
|
|
1387
|
+
* const block = doc.getBody().getChild(0).asBlockRange();
|
|
1388
|
+
* doc.getBody().removeBlockRange(block.getKey());
|
|
1389
|
+
* ```
|
|
1390
|
+
*/
|
|
1391
|
+
removeBlockRange(key) {
|
|
1392
|
+
const blockRange = this.getBlockRange(key);
|
|
1393
|
+
return this.deleteRange({
|
|
1394
|
+
startOffset: blockRange.startIndex,
|
|
1395
|
+
endOffset: blockRange.endIndex + 1
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1398
|
+
/**
|
|
1399
|
+
* Get a table marker by table id.
|
|
1400
|
+
* @param {string} key The persisted table id.
|
|
1401
|
+
* @returns {ICustomTable} The matching table marker.
|
|
1402
|
+
* @example
|
|
1403
|
+
* ```ts
|
|
1404
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1405
|
+
* if (!doc) throw new Error('No active document');
|
|
1406
|
+
*
|
|
1407
|
+
* const table = doc.getBody().getChild(0).asTable();
|
|
1408
|
+
* console.log(doc.getBody().getTable(table.getTableId()));
|
|
1409
|
+
* ```
|
|
1410
|
+
*/
|
|
1411
|
+
getTable(key) {
|
|
1412
|
+
var _this$_getBody$tables;
|
|
1413
|
+
const table = (_this$_getBody$tables = this._getBody().tables) === null || _this$_getBody$tables === void 0 ? void 0 : _this$_getBody$tables.find((item) => item.tableId === key);
|
|
1414
|
+
if (!table) throw new Error("Doc element is stale");
|
|
1415
|
+
return table;
|
|
1416
|
+
}
|
|
1417
|
+
/**
|
|
1418
|
+
* Remove a table marker and its content range.
|
|
1419
|
+
* @param {string} key The persisted table id.
|
|
1420
|
+
* @returns {boolean} `true` if the table range was removed.
|
|
1421
|
+
* @example
|
|
1422
|
+
* ```ts
|
|
1423
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1424
|
+
* if (!doc) throw new Error('No active document');
|
|
1425
|
+
*
|
|
1426
|
+
* const table = doc.getBody().getChild(0).asTable();
|
|
1427
|
+
* doc.getBody().removeTable(table.getTableId());
|
|
1428
|
+
* ```
|
|
1429
|
+
*/
|
|
1430
|
+
removeTable(key) {
|
|
1431
|
+
const table = this.getTable(key);
|
|
1432
|
+
return this.deleteRange({
|
|
1433
|
+
startOffset: table.startIndex,
|
|
1434
|
+
endOffset: table.endIndex + 1
|
|
1435
|
+
});
|
|
1436
|
+
}
|
|
1437
|
+
/**
|
|
1438
|
+
* Get a custom block marker by block id.
|
|
1439
|
+
* @param {string} key The persisted custom block id.
|
|
1440
|
+
* @returns {ICustomBlock} The matching custom block marker.
|
|
1441
|
+
* @example
|
|
1442
|
+
* ```ts
|
|
1443
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1444
|
+
* if (!doc) throw new Error('No active document');
|
|
1445
|
+
*
|
|
1446
|
+
* const customBlock = doc.getBody().getChild(0).asCustomBlock();
|
|
1447
|
+
* console.log(doc.getBody().getCustomBlock(customBlock.getBlockId()));
|
|
1448
|
+
* ```
|
|
1449
|
+
*/
|
|
1450
|
+
getCustomBlock(key) {
|
|
1451
|
+
var _this$_getBody$custom;
|
|
1452
|
+
const block = (_this$_getBody$custom = this._getBody().customBlocks) === null || _this$_getBody$custom === void 0 ? void 0 : _this$_getBody$custom.find((item) => item.blockId === key);
|
|
1453
|
+
if (!block) throw new Error("Doc element is stale");
|
|
1454
|
+
return block;
|
|
1455
|
+
}
|
|
1456
|
+
/**
|
|
1457
|
+
* Remove a custom block marker and its placeholder character.
|
|
1458
|
+
* @param {string} key The persisted custom block id.
|
|
1459
|
+
* @returns {boolean} `true` if the custom block placeholder was removed.
|
|
1460
|
+
* @example
|
|
1461
|
+
* ```ts
|
|
1462
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1463
|
+
* if (!doc) throw new Error('No active document');
|
|
1464
|
+
*
|
|
1465
|
+
* const customBlock = doc.getBody().getChild(0).asCustomBlock();
|
|
1466
|
+
* doc.getBody().removeCustomBlock(customBlock.getBlockId());
|
|
1467
|
+
* ```
|
|
1468
|
+
*/
|
|
1469
|
+
removeCustomBlock(key) {
|
|
1470
|
+
const block = this.getCustomBlock(key);
|
|
1471
|
+
return this.deleteRange({
|
|
1472
|
+
startOffset: block.startIndex,
|
|
1473
|
+
endOffset: block.startIndex + 1
|
|
1474
|
+
});
|
|
1475
|
+
}
|
|
1476
|
+
/**
|
|
1477
|
+
* Create a sibling element wrapper relative to the current element key.
|
|
1478
|
+
* @param {FDocElementType} type The current element type.
|
|
1479
|
+
* @param {string} key The current element key.
|
|
1480
|
+
* @param {-1 | 1} direction `-1` for previous sibling, `1` for next sibling.
|
|
1481
|
+
* @returns {FDocElement | null} The sibling wrapper, or `null` if none exists.
|
|
1482
|
+
* @example
|
|
1483
|
+
* ```ts
|
|
1484
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1485
|
+
* if (!doc) throw new Error('No active document');
|
|
1486
|
+
*
|
|
1487
|
+
* const element = doc.getBody().getChild(0);
|
|
1488
|
+
* const next = doc.getBody().createSibling(element.getType(), element.getKey(), 1);
|
|
1489
|
+
* console.log(next?.getType());
|
|
1490
|
+
* ```
|
|
1491
|
+
*/
|
|
1492
|
+
createSibling(type, key, direction) {
|
|
1493
|
+
const index = this.getChildIndex(this._createElement(type, key));
|
|
1494
|
+
const child = this._getChildren()[index + direction];
|
|
1495
|
+
return child ? this._createElement(child.type, child.key) : null;
|
|
1496
|
+
}
|
|
1497
|
+
_getChildren() {
|
|
1498
|
+
var _body$paragraphs$leng, _body$paragraphs2, _body$blockRanges, _body$tables, _body$customBlocks;
|
|
1499
|
+
const body = this._getBody();
|
|
1500
|
+
const children = [];
|
|
1501
|
+
for (let index = 0; index < ((_body$paragraphs$leng = (_body$paragraphs2 = body.paragraphs) === null || _body$paragraphs2 === void 0 ? void 0 : _body$paragraphs2.length) !== null && _body$paragraphs$leng !== void 0 ? _body$paragraphs$leng : 0); index++) {
|
|
1502
|
+
const paragraph = body.paragraphs[index];
|
|
1503
|
+
children.push({
|
|
1504
|
+
type: "paragraph",
|
|
1505
|
+
key: this._getParagraphId(paragraph, index),
|
|
1506
|
+
position: index > 0 ? body.paragraphs[index - 1].startIndex + 1 : 0,
|
|
1507
|
+
priority: 3
|
|
1508
|
+
});
|
|
1509
|
+
}
|
|
1510
|
+
(_body$blockRanges = body.blockRanges) === null || _body$blockRanges === void 0 || _body$blockRanges.forEach((blockRange) => children.push({
|
|
1511
|
+
type: "blockRange",
|
|
1512
|
+
key: blockRange.blockId,
|
|
1513
|
+
position: blockRange.startIndex,
|
|
1514
|
+
priority: 0
|
|
1515
|
+
}));
|
|
1516
|
+
(_body$tables = body.tables) === null || _body$tables === void 0 || _body$tables.forEach((table) => children.push({
|
|
1517
|
+
type: "table",
|
|
1518
|
+
key: table.tableId,
|
|
1519
|
+
position: table.startIndex,
|
|
1520
|
+
priority: 1
|
|
1521
|
+
}));
|
|
1522
|
+
(_body$customBlocks = body.customBlocks) === null || _body$customBlocks === void 0 || _body$customBlocks.forEach((customBlock) => children.push({
|
|
1523
|
+
type: "customBlock",
|
|
1524
|
+
key: customBlock.blockId,
|
|
1525
|
+
position: customBlock.startIndex,
|
|
1526
|
+
priority: 2
|
|
1527
|
+
}));
|
|
1528
|
+
return children.sort((a, b) => a.position - b.position || a.priority - b.priority);
|
|
1529
|
+
}
|
|
1530
|
+
_createElement(type, key) {
|
|
1531
|
+
return new FDocElement(this, type, key);
|
|
1532
|
+
}
|
|
1533
|
+
_getBody() {
|
|
1534
|
+
const body = this._documentDataModel.getSelfOrHeaderFooterModel(this._segmentId).getBody();
|
|
1535
|
+
if (!body) throw new Error("The document body is empty");
|
|
1536
|
+
return body;
|
|
1537
|
+
}
|
|
1538
|
+
_getParagraphInsertOffset(index) {
|
|
1539
|
+
var _body$paragraphs3;
|
|
1540
|
+
const body = this._getBody();
|
|
1541
|
+
if (index <= 0) return 0;
|
|
1542
|
+
const paragraphs = (_body$paragraphs3 = body.paragraphs) !== null && _body$paragraphs3 !== void 0 ? _body$paragraphs3 : [];
|
|
1543
|
+
if (paragraphs.length === 0) return Math.max(0, body.dataStream.length - 1);
|
|
1544
|
+
if (index >= paragraphs.length) return paragraphs[paragraphs.length - 1].startIndex + 1;
|
|
1545
|
+
return paragraphs[index - 1].startIndex + 1;
|
|
1546
|
+
}
|
|
1547
|
+
_normalizeInsertedParagraphIndex(index) {
|
|
1548
|
+
var _this$_getBody$paragr4;
|
|
1549
|
+
const paragraphs = (_this$_getBody$paragr4 = this._getBody().paragraphs) !== null && _this$_getBody$paragr4 !== void 0 ? _this$_getBody$paragr4 : [];
|
|
1550
|
+
return Math.max(0, Math.min(index, paragraphs.length - 1));
|
|
1551
|
+
}
|
|
1552
|
+
_getParagraphId(paragraph, paragraphIndex) {
|
|
1553
|
+
if (!paragraph) throw new RangeError(`Paragraph index ${paragraphIndex} is out of range.`);
|
|
1554
|
+
if (!paragraph.paragraphId) throw new DocElementStaleError(`Paragraph at index ${paragraphIndex} is missing paragraphId.`);
|
|
1555
|
+
return paragraph.paragraphId;
|
|
1556
|
+
}
|
|
1557
|
+
_preserveExplicitParagraphIds(body) {
|
|
1558
|
+
body[RESTORE_INSERTED_PARAGRAPH_IDS] = true;
|
|
1559
|
+
}
|
|
1560
|
+
_findParagraphByRange(range) {
|
|
1561
|
+
var _this$_getBody$paragr5;
|
|
1562
|
+
const paragraphs = (_this$_getBody$paragr5 = this._getBody().paragraphs) !== null && _this$_getBody$paragr5 !== void 0 ? _this$_getBody$paragr5 : [];
|
|
1563
|
+
const paragraphIndex = paragraphs.findIndex((paragraph, index) => {
|
|
1564
|
+
return (index > 0 ? paragraphs[index - 1].startIndex + 1 : 0) <= range.startOffset && range.endOffset <= paragraph.startIndex;
|
|
1565
|
+
});
|
|
1566
|
+
if (paragraphIndex < 0) throw new RangeError("Range does not resolve to a paragraph.");
|
|
1567
|
+
const paragraph = paragraphs[paragraphIndex];
|
|
1568
|
+
return {
|
|
1569
|
+
paragraph,
|
|
1570
|
+
paragraphIndex,
|
|
1571
|
+
startOffset: paragraphIndex > 0 ? paragraphs[paragraphIndex - 1].startIndex + 1 : 0,
|
|
1572
|
+
endOffset: paragraph.startIndex
|
|
1573
|
+
};
|
|
1574
|
+
}
|
|
1575
|
+
_replaceBodyRange(range, insertBody) {
|
|
1576
|
+
const startOffset = range.startOffset;
|
|
1577
|
+
const endOffset = range.endOffset;
|
|
1578
|
+
const textX = new TextX();
|
|
1579
|
+
if (startOffset > 0) textX.push({
|
|
1580
|
+
t: TextXActionType.RETAIN,
|
|
1581
|
+
len: startOffset
|
|
1582
|
+
});
|
|
1583
|
+
if (endOffset > startOffset) textX.push({
|
|
1584
|
+
t: TextXActionType.DELETE,
|
|
1585
|
+
len: endOffset - startOffset
|
|
1586
|
+
});
|
|
1587
|
+
if (insertBody.dataStream.length > 0) textX.push({
|
|
1588
|
+
t: TextXActionType.INSERT,
|
|
1589
|
+
body: insertBody,
|
|
1590
|
+
len: insertBody.dataStream.length
|
|
1591
|
+
});
|
|
1592
|
+
return this._executeTextX(textX);
|
|
1593
|
+
}
|
|
1594
|
+
_retainBodyRange(range, body, coverType) {
|
|
1595
|
+
var _body$textRuns;
|
|
1596
|
+
if (((_body$textRuns = body.textRuns) === null || _body$textRuns === void 0 ? void 0 : _body$textRuns.length) && this._getBody().textRuns == null) this._ensureTextRuns();
|
|
1597
|
+
const textX = new TextX();
|
|
1598
|
+
if (range.startOffset > 0) textX.push({
|
|
1599
|
+
t: TextXActionType.RETAIN,
|
|
1600
|
+
len: range.startOffset
|
|
1601
|
+
});
|
|
1602
|
+
textX.push({
|
|
1603
|
+
t: TextXActionType.RETAIN,
|
|
1604
|
+
body,
|
|
1605
|
+
coverType,
|
|
1606
|
+
len: range.endOffset - range.startOffset
|
|
1607
|
+
});
|
|
1608
|
+
return this._executeTextX(textX);
|
|
1609
|
+
}
|
|
1610
|
+
_ensureTextRuns() {
|
|
1611
|
+
if (this._getBody().textRuns != null) return;
|
|
1612
|
+
const actions = JSONX.getInstance().replaceOp([...getRichTextEditPath(this._documentDataModel, this._segmentId), "textRuns"], void 0, []);
|
|
1613
|
+
this._commandService.syncExecuteCommand(RichTextEditingMutation.id, {
|
|
1614
|
+
unitId: this._documentDataModel.getUnitId(),
|
|
1615
|
+
segmentId: this._segmentId,
|
|
1616
|
+
actions,
|
|
1617
|
+
textRanges: [],
|
|
1618
|
+
trigger: FACADE_TRIGGER,
|
|
1619
|
+
isEditing: false
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
_executeTextX(textX) {
|
|
1623
|
+
const actions = JSONX.getInstance().editOp(textX.serialize(), getRichTextEditPath(this._documentDataModel, this._segmentId));
|
|
1624
|
+
const result = this._commandService.syncExecuteCommand(RichTextEditingMutation.id, {
|
|
1625
|
+
unitId: this._documentDataModel.getUnitId(),
|
|
1626
|
+
segmentId: this._segmentId,
|
|
1627
|
+
actions,
|
|
1628
|
+
textRanges: [],
|
|
1629
|
+
trigger: FACADE_TRIGGER,
|
|
1630
|
+
isEditing: false
|
|
1631
|
+
});
|
|
1632
|
+
return Boolean(result);
|
|
1633
|
+
}
|
|
1634
|
+
};
|
|
1635
|
+
|
|
1636
|
+
//#endregion
|
|
1637
|
+
//#region \0@oxc-project+runtime@0.137.0/helpers/esm/typeof.js
|
|
1638
|
+
function _typeof(o) {
|
|
1639
|
+
"@babel/helpers - typeof";
|
|
1640
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
|
|
1641
|
+
return typeof o;
|
|
1642
|
+
} : function(o) {
|
|
1643
|
+
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
1644
|
+
}, _typeof(o);
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
//#endregion
|
|
1648
|
+
//#region \0@oxc-project+runtime@0.137.0/helpers/esm/toPrimitive.js
|
|
1649
|
+
function toPrimitive(t, r) {
|
|
1650
|
+
if ("object" != _typeof(t) || !t) return t;
|
|
1651
|
+
var e = t[Symbol.toPrimitive];
|
|
1652
|
+
if (void 0 !== e) {
|
|
1653
|
+
var i = e.call(t, r || "default");
|
|
1654
|
+
if ("object" != _typeof(i)) return i;
|
|
1655
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
1656
|
+
}
|
|
1657
|
+
return ("string" === r ? String : Number)(t);
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
//#endregion
|
|
1661
|
+
//#region \0@oxc-project+runtime@0.137.0/helpers/esm/toPropertyKey.js
|
|
1662
|
+
function toPropertyKey(t) {
|
|
1663
|
+
var i = toPrimitive(t, "string");
|
|
1664
|
+
return "symbol" == _typeof(i) ? i : i + "";
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
//#endregion
|
|
1668
|
+
//#region \0@oxc-project+runtime@0.137.0/helpers/esm/defineProperty.js
|
|
1669
|
+
function _defineProperty(e, r, t) {
|
|
1670
|
+
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
1671
|
+
value: t,
|
|
1672
|
+
enumerable: !0,
|
|
1673
|
+
configurable: !0,
|
|
1674
|
+
writable: !0
|
|
1675
|
+
}) : e[r] = t, e;
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
//#endregion
|
|
1679
|
+
//#region \0@oxc-project+runtime@0.137.0/helpers/esm/decorateParam.js
|
|
1680
|
+
function __decorateParam(paramIndex, decorator) {
|
|
1681
|
+
return function(target, key) {
|
|
1682
|
+
decorator(target, key, paramIndex);
|
|
1683
|
+
};
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
//#endregion
|
|
1687
|
+
//#region \0@oxc-project+runtime@0.137.0/helpers/esm/decorate.js
|
|
1688
|
+
function __decorate(decorators, target, key, desc) {
|
|
1689
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1690
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1691
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1692
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
//#endregion
|
|
1696
|
+
//#region src/facade/f-document.ts
|
|
1697
|
+
let FDocument = class FDocument extends FBaseInitialable {
|
|
1698
|
+
constructor(_documentDataModel, _injector, _univerInstanceService, _resourceLoaderService, _commandService) {
|
|
1699
|
+
super(_injector);
|
|
1700
|
+
this._documentDataModel = _documentDataModel;
|
|
1701
|
+
this._injector = _injector;
|
|
1702
|
+
this._univerInstanceService = _univerInstanceService;
|
|
1703
|
+
this._resourceLoaderService = _resourceLoaderService;
|
|
1704
|
+
this._commandService = _commandService;
|
|
1705
|
+
_defineProperty(this, "id", void 0);
|
|
1706
|
+
_defineProperty(this, "_docElementRegistry", new DocElementRegistry());
|
|
1707
|
+
this.id = this._documentDataModel.getUnitId();
|
|
1708
|
+
}
|
|
1709
|
+
/**
|
|
1710
|
+
* Get the document data model of the document.
|
|
1711
|
+
* @returns {DocumentDataModel} The document data model.
|
|
1712
|
+
* @example
|
|
1713
|
+
* ```typescript
|
|
1714
|
+
* const fDocument = univerAPI.getActiveDocument();
|
|
1715
|
+
* const documentDataModel = fDocument.getDocumentDataModel();
|
|
1716
|
+
* console.log(documentDataModel);
|
|
1717
|
+
* ```
|
|
1718
|
+
*/
|
|
1719
|
+
getDocumentDataModel() {
|
|
1720
|
+
return this._documentDataModel;
|
|
1721
|
+
}
|
|
1722
|
+
/**
|
|
1723
|
+
* Get the document body facade.
|
|
1724
|
+
*
|
|
1725
|
+
* The returned body facade provides synchronous Google Docs-like element APIs
|
|
1726
|
+
* for reading and editing top-level document body elements. Paragraph elements
|
|
1727
|
+
* use their persisted `paragraphId` values. Persisted elements, such as tables
|
|
1728
|
+
* and custom blocks, use their existing ids.
|
|
1729
|
+
*
|
|
1730
|
+
* @returns {FDocBody} The document body API instance.
|
|
1731
|
+
* @example
|
|
1732
|
+
* ```typescript
|
|
1733
|
+
* const doc = univerAPI.getActiveDocument();
|
|
1734
|
+
* if (!doc) throw new Error('No active document');
|
|
1735
|
+
*
|
|
1736
|
+
* const body = doc.getBody();
|
|
1737
|
+
* const paragraph = body.getChild(0).asParagraph();
|
|
1738
|
+
* paragraph.appendText(' updated');
|
|
1739
|
+
* ```
|
|
1740
|
+
*/
|
|
1741
|
+
getBody() {
|
|
1742
|
+
return new FDocBody(this._documentDataModel, this._commandService, this._docElementRegistry);
|
|
1743
|
+
}
|
|
1744
|
+
dispose() {
|
|
1745
|
+
super.dispose();
|
|
1746
|
+
}
|
|
1747
|
+
/**
|
|
1748
|
+
* Get the document id.
|
|
1749
|
+
* @returns {string} The document id.
|
|
1750
|
+
* @example
|
|
1751
|
+
* ```typescript
|
|
1752
|
+
* const fDocument = univerAPI.getActiveDocument();
|
|
1753
|
+
* const unitId = fDocument.getId();
|
|
1754
|
+
* console.log(unitId);
|
|
1755
|
+
* ```
|
|
1756
|
+
*/
|
|
1757
|
+
getId() {
|
|
1758
|
+
return this.id;
|
|
1759
|
+
}
|
|
1760
|
+
/**
|
|
1761
|
+
* Get the document name.
|
|
1762
|
+
* @returns {string} The document name.
|
|
1763
|
+
* @example
|
|
1764
|
+
* ```typescript
|
|
1765
|
+
* const fDocument = univerAPI.getActiveDocument();
|
|
1766
|
+
* const name = fDocument.getName();
|
|
1767
|
+
* console.log(name);
|
|
1768
|
+
* ```
|
|
1769
|
+
*/
|
|
1770
|
+
getName() {
|
|
1771
|
+
return this._documentDataModel.getTitle() || "";
|
|
1772
|
+
}
|
|
1773
|
+
/**
|
|
1774
|
+
* Save the document snapshot data, including the document content and resource data, etc.
|
|
1775
|
+
* @returns {IDocumentData} The document snapshot data.
|
|
1776
|
+
* @example
|
|
1777
|
+
* ```typescript
|
|
1778
|
+
* const fDocument = univerAPI.getActiveDocument();
|
|
1779
|
+
* const snapshot = fDocument.save();
|
|
1780
|
+
* console.log(snapshot);
|
|
1781
|
+
* ```
|
|
1782
|
+
*/
|
|
1783
|
+
save() {
|
|
1784
|
+
return this._resourceLoaderService.saveUnit(this._documentDataModel.getUnitId());
|
|
1785
|
+
}
|
|
1786
|
+
/**
|
|
1787
|
+
* Undo the last operation in the document.
|
|
1788
|
+
* @returns {boolean} `true` if the undo operation was successful, or `false` if it failed.
|
|
1789
|
+
* @example
|
|
1790
|
+
* ```typescript
|
|
1791
|
+
* const fDocument = univerAPI.getActiveDocument();
|
|
1792
|
+
* const success = fDocument.undo();
|
|
1793
|
+
* console.log(success);
|
|
1794
|
+
* ```
|
|
1795
|
+
*/
|
|
1796
|
+
undo() {
|
|
1797
|
+
this._univerInstanceService.focusUnit(this.id);
|
|
1798
|
+
return this._commandService.syncExecuteCommand(UndoCommand.id);
|
|
1799
|
+
}
|
|
1800
|
+
/**
|
|
1801
|
+
* Redo the last undone operation in the document.
|
|
1802
|
+
* @returns {boolean} `true` if the redo operation was successful, or `false` if it failed.
|
|
1803
|
+
* @example
|
|
1804
|
+
* ```typescript
|
|
1805
|
+
* const fDocument = univerAPI.getActiveDocument();
|
|
1806
|
+
* const success = fDocument.redo();
|
|
1807
|
+
* console.log(success);
|
|
1808
|
+
* ```
|
|
1809
|
+
*/
|
|
1810
|
+
redo() {
|
|
1811
|
+
this._univerInstanceService.focusUnit(this.id);
|
|
1812
|
+
return this._commandService.syncExecuteCommand(RedoCommand.id);
|
|
1813
|
+
}
|
|
1814
|
+
/**
|
|
1815
|
+
* Adds the specified text to the end of this text region.
|
|
1816
|
+
* @param {string} text - The text to be added to the end of this text region.
|
|
1817
|
+
* @return {boolean} `true` if the text was successfully appended, or `false` if it failed.
|
|
1818
|
+
* @example
|
|
1819
|
+
* ```typescript
|
|
1820
|
+
* const fDocument = univerAPI.getActiveDocument();
|
|
1821
|
+
* const success = fDocument.appendText('Hello, world!');
|
|
1822
|
+
* console.log(success);
|
|
1823
|
+
* ```
|
|
1824
|
+
*/
|
|
1825
|
+
appendText(text) {
|
|
1826
|
+
const { body } = this.save();
|
|
1827
|
+
if (!body) throw new Error("The document body is empty");
|
|
1828
|
+
const lastPosition = body.dataStream.length - 2;
|
|
1829
|
+
return this.insertText(text, {
|
|
1830
|
+
startOffset: lastPosition,
|
|
1831
|
+
endOffset: lastPosition,
|
|
1832
|
+
segmentId: ""
|
|
1833
|
+
});
|
|
1834
|
+
}
|
|
1835
|
+
/**
|
|
1836
|
+
* Inserts text at the provided document range. Defaults to appending before the final section break.
|
|
1837
|
+
* @param {string} text - The text to insert.
|
|
1838
|
+
* @param {IDocumentInsertTextFacadeOptions} options - Optional target range, segment id, and cursor offset.
|
|
1839
|
+
* @returns {boolean} `true` if the text was successfully inserted, or `false` if it failed.
|
|
1840
|
+
* @example
|
|
1841
|
+
*
|
|
1842
|
+
* // Insert text at a specific range in the document body
|
|
1843
|
+
* ```typescript
|
|
1844
|
+
* const fDocument = univerAPI.getActiveDocument();
|
|
1845
|
+
* const success = fDocument.insertText('Hello, world!', {
|
|
1846
|
+
* startOffset: 5,
|
|
1847
|
+
* endOffset: 5,
|
|
1848
|
+
* segmentId: '',
|
|
1849
|
+
* cursorOffset: 13,
|
|
1850
|
+
* });
|
|
1851
|
+
* console.log(success);
|
|
1852
|
+
* ```
|
|
1853
|
+
*
|
|
1854
|
+
* // Insert text at the beginning of a header or footer segment
|
|
1855
|
+
* ```typescript
|
|
1856
|
+
* const fDocument = univerAPI.getActiveDocument();
|
|
1857
|
+
* const snapshot = fDocument.save();
|
|
1858
|
+
* const { headers, footers } = snapshot;
|
|
1859
|
+
*
|
|
1860
|
+
* if (headers) {
|
|
1861
|
+
* for (const headerId in headers) {
|
|
1862
|
+
* if (headerId === 'target-header-id') {
|
|
1863
|
+
* fDocument.insertText('Hello, header!', {
|
|
1864
|
+
* startOffset: 0,
|
|
1865
|
+
* endOffset: 0,
|
|
1866
|
+
* segmentId: headerId,
|
|
1867
|
+
* });
|
|
1868
|
+
* }
|
|
1869
|
+
* }
|
|
1870
|
+
* }
|
|
1871
|
+
*
|
|
1872
|
+
* if (footers) {
|
|
1873
|
+
* for (const footerId in footers) {
|
|
1874
|
+
* if (footerId === 'target-footer-id') {
|
|
1875
|
+
* fDocument.insertText('Hello, footer!', {
|
|
1876
|
+
* startOffset: 0,
|
|
1877
|
+
* endOffset: 0,
|
|
1878
|
+
* segmentId: footerId,
|
|
1879
|
+
* });
|
|
1880
|
+
* }
|
|
1881
|
+
* }
|
|
1882
|
+
* }
|
|
1883
|
+
* ```
|
|
1884
|
+
*/
|
|
1885
|
+
insertText(text, options = {}) {
|
|
1886
|
+
var _options$startOffset, _options$endOffset, _options$segmentId;
|
|
1887
|
+
const unitId = this.id;
|
|
1888
|
+
const { body } = this.save();
|
|
1889
|
+
if (!body) throw new Error("The document body is empty");
|
|
1890
|
+
const startOffset = (_options$startOffset = options.startOffset) !== null && _options$startOffset !== void 0 ? _options$startOffset : Math.max(0, body.dataStream.length - 2);
|
|
1891
|
+
const endOffset = (_options$endOffset = options.endOffset) !== null && _options$endOffset !== void 0 ? _options$endOffset : startOffset;
|
|
1892
|
+
const segmentId = (_options$segmentId = options.segmentId) !== null && _options$segmentId !== void 0 ? _options$segmentId : "";
|
|
1893
|
+
const activeRange = {
|
|
1894
|
+
startOffset,
|
|
1895
|
+
endOffset,
|
|
1896
|
+
collapsed: startOffset === endOffset,
|
|
1897
|
+
segmentId
|
|
1898
|
+
};
|
|
1899
|
+
const removeLeadingParagraphBreak = startOffset === 0;
|
|
1900
|
+
const insertBody = buildPlainTextInsertBody(text, {
|
|
1901
|
+
paragraphStyle: getParagraphStyleAtOffset(body, startOffset),
|
|
1902
|
+
removeLeadingParagraphBreak
|
|
1903
|
+
});
|
|
1904
|
+
const cursorOffset = options.cursorOffset == null ? void 0 : getNormalizedPlainTextCursorOffset(text, options.cursorOffset, removeLeadingParagraphBreak);
|
|
1905
|
+
return this._commandService.syncExecuteCommand(InsertTextCommand.id, {
|
|
1906
|
+
unitId,
|
|
1907
|
+
body: insertBody,
|
|
1908
|
+
range: activeRange,
|
|
1909
|
+
segmentId,
|
|
1910
|
+
...cursorOffset == null ? {} : { cursorOffset }
|
|
1911
|
+
});
|
|
1912
|
+
}
|
|
1913
|
+
/**
|
|
1914
|
+
* Inserts one or more plain-text paragraphs at the provided document range.
|
|
1915
|
+
* @param {string} text - The paragraph text to insert. Newlines are normalized to document paragraph separators.
|
|
1916
|
+
* @param {IDocumentInsertTextFacadeOptions} options - Optional target range, segment id, and cursor offset.
|
|
1917
|
+
* @returns {boolean} `true` if the paragraphs were successfully inserted, or `false` if it failed.
|
|
1918
|
+
* @example
|
|
1919
|
+
* ```typescript
|
|
1920
|
+
* const fDocument = univerAPI.getActiveDocument();
|
|
1921
|
+
* const success = fDocument.insertParagraph('Hello, world! This is a new paragraph.', {
|
|
1922
|
+
* startOffset: 5,
|
|
1923
|
+
* endOffset: 5,
|
|
1924
|
+
* });
|
|
1925
|
+
* console.log(success);
|
|
1926
|
+
* ```
|
|
1927
|
+
*/
|
|
1928
|
+
insertParagraph(text = "", options = {}) {
|
|
1929
|
+
var _options$cursorOffset;
|
|
1930
|
+
const dataStream = `${text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n").join("\r\n")}\r\n`;
|
|
1931
|
+
return this.insertText(dataStream, {
|
|
1932
|
+
...options,
|
|
1933
|
+
cursorOffset: (_options$cursorOffset = options.cursorOffset) !== null && _options$cursorOffset !== void 0 ? _options$cursorOffset : dataStream.length
|
|
1934
|
+
});
|
|
1935
|
+
}
|
|
1936
|
+
};
|
|
1937
|
+
FDocument = __decorate([
|
|
1938
|
+
__decorateParam(1, Inject(Injector)),
|
|
1939
|
+
__decorateParam(2, IUniverInstanceService),
|
|
1940
|
+
__decorateParam(3, Inject(IResourceLoaderService)),
|
|
1941
|
+
__decorateParam(4, ICommandService)
|
|
1942
|
+
], FDocument);
|
|
1943
|
+
|
|
1944
|
+
//#endregion
|
|
1945
|
+
//#region src/facade/f-univer.ts
|
|
1946
|
+
var FUniverDocsMixin = class extends FUniver {
|
|
1947
|
+
createDocument(data) {
|
|
1948
|
+
const document = this._injector.get(IUniverInstanceService).createUnit(UniverInstanceType.UNIVER_DOC, data);
|
|
1949
|
+
return this._injector.createInstance(FDocument, document);
|
|
1950
|
+
}
|
|
1951
|
+
getActiveDocument() {
|
|
1952
|
+
const document = this._univerInstanceService.getCurrentUnitOfType(UniverInstanceType.UNIVER_DOC);
|
|
1953
|
+
if (!document) return null;
|
|
1954
|
+
return this._injector.createInstance(FDocument, document);
|
|
1955
|
+
}
|
|
1956
|
+
getDocument(id) {
|
|
1957
|
+
const document = this._univerInstanceService.getUnit(id, UniverInstanceType.UNIVER_DOC);
|
|
1958
|
+
if (!document) return null;
|
|
1959
|
+
return this._injector.createInstance(FDocument, document);
|
|
1960
|
+
}
|
|
1961
|
+
};
|
|
1962
|
+
FUniver.extend(FUniverDocsMixin);
|
|
1963
|
+
|
|
1964
|
+
//#endregion
|
|
1965
|
+
export { DocElementRegistry, DocElementStaleError, FDocBlockRange, FDocBody, FDocCustomBlock, FDocElement, FDocParagraph, FDocTable, FDocument };
|