@kedataindo/docflow-plugins 0.0.50 → 0.0.52
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/dist/index.cjs +251 -6
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +250 -6
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -84,6 +84,7 @@ __export(index_exports, {
|
|
|
84
84
|
slashMenuPlugin: () => slashMenuPlugin,
|
|
85
85
|
slashState: () => slashState,
|
|
86
86
|
smartElementsPlugin: () => smartElementsPlugin,
|
|
87
|
+
tablePageSplitPlugin: () => tablePageSplitPlugin,
|
|
87
88
|
tablePlugin: () => tablePlugin,
|
|
88
89
|
textColorPlugin: () => textColorPlugin,
|
|
89
90
|
tocPlugin: () => tocPlugin
|
|
@@ -274,6 +275,20 @@ var import_extension_table_row = __toESM(require("@tiptap/extension-table-row"),
|
|
|
274
275
|
var import_extension_table_cell = __toESM(require("@tiptap/extension-table-cell"), 1);
|
|
275
276
|
var import_extension_table_header = __toESM(require("@tiptap/extension-table-header"), 1);
|
|
276
277
|
var CustomTable = import_extension_table.default.extend({
|
|
278
|
+
addAttributes() {
|
|
279
|
+
return {
|
|
280
|
+
...this.parent?.(),
|
|
281
|
+
// Marker used by the experimental `tablePageSplitPlugin` to tag the
|
|
282
|
+
// head of a split pair. Backwards-compatible (default null) so docs
|
|
283
|
+
// without this attr round-trip cleanly. See
|
|
284
|
+
// packages/plugins/src/tablePageSplit.ts.
|
|
285
|
+
tablePageSplit: {
|
|
286
|
+
default: null,
|
|
287
|
+
parseHTML: () => null,
|
|
288
|
+
renderHTML: () => ({})
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
},
|
|
277
292
|
renderHTML({ node, HTMLAttributes }) {
|
|
278
293
|
let colCount = 0;
|
|
279
294
|
const firstRow = node.firstChild;
|
|
@@ -316,9 +331,11 @@ var CustomTable = import_extension_table.default.extend({
|
|
|
316
331
|
}
|
|
317
332
|
}
|
|
318
333
|
const tableAttrs = (0, import_core.mergeAttributes)(HTMLAttributes);
|
|
334
|
+
tableAttrs["data-tps-splittable"] = "";
|
|
319
335
|
if (hasExplicitWidths && totalWidth > 0) {
|
|
320
336
|
const existingStyle = tableAttrs.style || "";
|
|
321
337
|
tableAttrs.style = existingStyle ? `${existingStyle}; width: ${totalWidth}px !important` : `width: ${totalWidth}px !important`;
|
|
338
|
+
tableAttrs["data-colwidth"] = "explicit";
|
|
322
339
|
}
|
|
323
340
|
return [
|
|
324
341
|
"table",
|
|
@@ -3282,10 +3299,236 @@ var smartElementsPlugin = (0, import_docflow_core19.definePlugin)({
|
|
|
3282
3299
|
}
|
|
3283
3300
|
});
|
|
3284
3301
|
|
|
3285
|
-
// src/
|
|
3302
|
+
// src/tablePageSplit.ts
|
|
3303
|
+
var import_docflow_core20 = require("@kedataindo/docflow-core");
|
|
3286
3304
|
var import_core12 = require("@tiptap/core");
|
|
3287
3305
|
var import_state3 = require("@tiptap/pm/state");
|
|
3288
|
-
var
|
|
3306
|
+
var PLUGIN_KEY = new import_state3.PluginKey("docflow/table-page-split");
|
|
3307
|
+
var SPLIT_MARK = "head";
|
|
3308
|
+
var MAX_SPLIT_DEPTH = 16;
|
|
3309
|
+
function readPageContentHeightPx(view, editor) {
|
|
3310
|
+
const storage = editor?.storage?.PaginationPlus;
|
|
3311
|
+
if (storage && typeof storage.pageHeight === "number") {
|
|
3312
|
+
const h1 = storage.headerHeight?.get?.(1) ?? 0;
|
|
3313
|
+
const f1 = storage.footerHeight?.get?.(1) ?? 0;
|
|
3314
|
+
const contentPx = storage.pageHeight - (storage.marginTop ?? 0) - (storage.marginBottom ?? 0) - (storage.contentMarginTop ?? 0) - (storage.contentMarginBottom ?? 0) - h1 - f1;
|
|
3315
|
+
if (Number.isFinite(contentPx) && contentPx > 0) return contentPx;
|
|
3316
|
+
}
|
|
3317
|
+
const raw = view.dom.style.getPropertyValue("--rm-max-content-child-height");
|
|
3318
|
+
if (raw) {
|
|
3319
|
+
const px = parseFloat(raw);
|
|
3320
|
+
if (Number.isFinite(px) && px > 0) return px;
|
|
3321
|
+
}
|
|
3322
|
+
return null;
|
|
3323
|
+
}
|
|
3324
|
+
function measureElementPx(el) {
|
|
3325
|
+
if (!el || !(el instanceof HTMLElement)) return 0;
|
|
3326
|
+
return el.getBoundingClientRect().height;
|
|
3327
|
+
}
|
|
3328
|
+
function availablePxAt(view, pos, defaultPageContentPx) {
|
|
3329
|
+
try {
|
|
3330
|
+
const tableCoords = view.coordsAtPos(pos + 1);
|
|
3331
|
+
const tableTop = tableCoords.top;
|
|
3332
|
+
const paginationWrapper = view.dom.querySelector("[data-rm-pagination]");
|
|
3333
|
+
if (!paginationWrapper) return defaultPageContentPx;
|
|
3334
|
+
const breakers = paginationWrapper.querySelectorAll(".rm-page-break .breaker");
|
|
3335
|
+
for (let i = 0; i < breakers.length; i++) {
|
|
3336
|
+
const br = breakers[i];
|
|
3337
|
+
if (!(br instanceof HTMLElement)) continue;
|
|
3338
|
+
const breakTop = br.getBoundingClientRect().top;
|
|
3339
|
+
if (breakTop > tableTop) {
|
|
3340
|
+
return Math.max(1, Math.floor(breakTop - tableTop));
|
|
3341
|
+
}
|
|
3342
|
+
}
|
|
3343
|
+
return defaultPageContentPx;
|
|
3344
|
+
} catch {
|
|
3345
|
+
return null;
|
|
3346
|
+
}
|
|
3347
|
+
}
|
|
3348
|
+
function measureRows(tableNode, tableDom) {
|
|
3349
|
+
const expectedRows = tableNode.childCount;
|
|
3350
|
+
const trs = tableDom.querySelectorAll(":scope > tbody > tr");
|
|
3351
|
+
if (trs.length !== expectedRows) return null;
|
|
3352
|
+
const measurements = [];
|
|
3353
|
+
let cumulative = 0;
|
|
3354
|
+
for (let i = 0; i < trs.length; i++) {
|
|
3355
|
+
const h = measureElementPx(trs[i]);
|
|
3356
|
+
cumulative += h;
|
|
3357
|
+
measurements.push({ rowIndex: i, heightPx: h, cumulativePx: cumulative });
|
|
3358
|
+
}
|
|
3359
|
+
return measurements;
|
|
3360
|
+
}
|
|
3361
|
+
function findSplitRow(measurements, pageContentPx) {
|
|
3362
|
+
if (measurements.length < 2) return -1;
|
|
3363
|
+
if (measurements[measurements.length - 1].cumulativePx <= pageContentPx) return -1;
|
|
3364
|
+
let lo = 0;
|
|
3365
|
+
let hi = measurements.length - 2;
|
|
3366
|
+
let best = -1;
|
|
3367
|
+
while (lo <= hi) {
|
|
3368
|
+
const mid = lo + hi >> 1;
|
|
3369
|
+
if (measurements[mid].cumulativePx <= pageContentPx) {
|
|
3370
|
+
best = mid;
|
|
3371
|
+
lo = mid + 1;
|
|
3372
|
+
} else {
|
|
3373
|
+
hi = mid - 1;
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3376
|
+
return best;
|
|
3377
|
+
}
|
|
3378
|
+
function rowBoundaryOffset(tableNode, splitRow) {
|
|
3379
|
+
if (splitRow < 0 || tableNode.childCount === 0) return 0;
|
|
3380
|
+
let boundary = 0;
|
|
3381
|
+
tableNode.forEach((row, offset, index) => {
|
|
3382
|
+
if (index <= splitRow) boundary = offset + row.nodeSize;
|
|
3383
|
+
});
|
|
3384
|
+
return boundary;
|
|
3385
|
+
}
|
|
3386
|
+
function tailMeasurements(measurements, splitRow) {
|
|
3387
|
+
const tail = [];
|
|
3388
|
+
let cumulative = 0;
|
|
3389
|
+
for (let i = splitRow + 1; i < measurements.length; i++) {
|
|
3390
|
+
cumulative += measurements[i].heightPx;
|
|
3391
|
+
tail.push({ rowIndex: i, heightPx: measurements[i].heightPx, cumulativePx: cumulative });
|
|
3392
|
+
}
|
|
3393
|
+
return tail;
|
|
3394
|
+
}
|
|
3395
|
+
function buildSplitChain(tableNode, measurements, headBudgetPx, pageContentPx, schema, tableType, depth) {
|
|
3396
|
+
const budget = depth === 0 ? headBudgetPx : pageContentPx;
|
|
3397
|
+
const splitRow = findSplitRow(measurements, budget);
|
|
3398
|
+
if (splitRow < 0) return [tableNode];
|
|
3399
|
+
if (depth >= MAX_SPLIT_DEPTH) return [tableNode];
|
|
3400
|
+
const splitPos = rowBoundaryOffset(tableNode, splitRow);
|
|
3401
|
+
const content = tableNode.content;
|
|
3402
|
+
const leftContent = content.cut(0, splitPos);
|
|
3403
|
+
const rightContent = content.cut(splitPos);
|
|
3404
|
+
const leftAttrs = { ...tableNode.attrs, tablePageSplit: SPLIT_MARK };
|
|
3405
|
+
const rightAttrs = { ...tableNode.attrs, tablePageSplit: null };
|
|
3406
|
+
const leftTable = tableType.create(leftAttrs, leftContent, tableNode.marks);
|
|
3407
|
+
const rightTable = tableType.create(rightAttrs, rightContent, tableNode.marks);
|
|
3408
|
+
const expectedRows = tableNode.childCount;
|
|
3409
|
+
const leftRows = leftTable.childCount;
|
|
3410
|
+
const rightRows = rightTable.childCount;
|
|
3411
|
+
if (leftRows + rightRows !== expectedRows) {
|
|
3412
|
+
throw new Error(
|
|
3413
|
+
`[tablePageSplitPlugin] row-count invariant violated: expected ${expectedRows}, got ${leftRows} + ${rightRows} = ${leftRows + rightRows}`
|
|
3414
|
+
);
|
|
3415
|
+
}
|
|
3416
|
+
const tail = tailMeasurements(measurements, splitRow);
|
|
3417
|
+
const tailChain = buildSplitChain(rightTable, tail, headBudgetPx, pageContentPx, schema, tableType, depth + 1);
|
|
3418
|
+
if (tailChain.length === 1) {
|
|
3419
|
+
return [leftTable, rightTable];
|
|
3420
|
+
}
|
|
3421
|
+
return [leftTable, ...tailChain];
|
|
3422
|
+
}
|
|
3423
|
+
function lastChunkHeightPx(measurements, headBudgetPx, pageContentPx) {
|
|
3424
|
+
let current = measurements;
|
|
3425
|
+
let budget = headBudgetPx;
|
|
3426
|
+
let splitCount = 0;
|
|
3427
|
+
for (let depth = 0; depth < MAX_SPLIT_DEPTH; depth++) {
|
|
3428
|
+
const splitRow = findSplitRow(current, budget);
|
|
3429
|
+
if (splitRow < 0) {
|
|
3430
|
+
return splitCount === 0 ? null : current[current.length - 1]?.cumulativePx ?? 0;
|
|
3431
|
+
}
|
|
3432
|
+
splitCount++;
|
|
3433
|
+
current = tailMeasurements(current, splitRow);
|
|
3434
|
+
budget = pageContentPx;
|
|
3435
|
+
}
|
|
3436
|
+
return current[current.length - 1]?.cumulativePx ?? 0;
|
|
3437
|
+
}
|
|
3438
|
+
function runSplitPass(view, editor) {
|
|
3439
|
+
if (!view || view.isDestroyed) return;
|
|
3440
|
+
const v = view;
|
|
3441
|
+
const state = v.state;
|
|
3442
|
+
const { schema } = state;
|
|
3443
|
+
const tableType = schema.nodes.table;
|
|
3444
|
+
if (!tableType) return;
|
|
3445
|
+
const pageContentPx = readPageContentHeightPx(v, editor);
|
|
3446
|
+
if (pageContentPx == null) return;
|
|
3447
|
+
const tables = [];
|
|
3448
|
+
state.doc.descendants((node, pos) => {
|
|
3449
|
+
if (node.type !== tableType) return true;
|
|
3450
|
+
if (node.attrs.tablePageSplit === SPLIT_MARK) return true;
|
|
3451
|
+
const domAt = v.nodeDOM(pos);
|
|
3452
|
+
if (!(domAt instanceof HTMLElement)) return true;
|
|
3453
|
+
const measurements = measureRows(node, domAt);
|
|
3454
|
+
if (!measurements) return true;
|
|
3455
|
+
tables.push({ pos, node, measurements });
|
|
3456
|
+
return true;
|
|
3457
|
+
});
|
|
3458
|
+
if (tables.length === 0) return;
|
|
3459
|
+
const pending = [];
|
|
3460
|
+
let cursorAvailablePx = null;
|
|
3461
|
+
for (let i = 0; i < tables.length; i++) {
|
|
3462
|
+
const { pos, node, measurements } = tables[i];
|
|
3463
|
+
const availablePx = i === 0 ? availablePxAt(v, pos, pageContentPx) ?? pageContentPx : cursorAvailablePx ?? pageContentPx;
|
|
3464
|
+
const chain = buildSplitChain(node, measurements, availablePx, pageContentPx, schema, tableType, 0);
|
|
3465
|
+
if (chain.length > 1) pending.push({ pos, node, chain });
|
|
3466
|
+
const lastChunkPx = lastChunkHeightPx(measurements, availablePx, pageContentPx);
|
|
3467
|
+
if (lastChunkPx === null) {
|
|
3468
|
+
const totalPx = measurements[measurements.length - 1]?.cumulativePx ?? 0;
|
|
3469
|
+
cursorAvailablePx = Math.max(0, availablePx - totalPx);
|
|
3470
|
+
} else {
|
|
3471
|
+
cursorAvailablePx = Math.max(0, pageContentPx - lastChunkPx);
|
|
3472
|
+
}
|
|
3473
|
+
}
|
|
3474
|
+
if (pending.length === 0) return;
|
|
3475
|
+
const tr = state.tr;
|
|
3476
|
+
pending.sort((a, b) => b.pos - a.pos);
|
|
3477
|
+
for (const { pos, node, chain } of pending) {
|
|
3478
|
+
tr.replaceWith(pos, pos + node.nodeSize, chain);
|
|
3479
|
+
}
|
|
3480
|
+
tr.setMeta(PLUGIN_KEY, { splitAtRow: -1 });
|
|
3481
|
+
v.dispatch(tr);
|
|
3482
|
+
}
|
|
3483
|
+
var TablePageSplitExtension = import_core12.Extension.create({
|
|
3484
|
+
name: "tablePageSplit",
|
|
3485
|
+
addProseMirrorPlugins() {
|
|
3486
|
+
let viewRef = null;
|
|
3487
|
+
let pendingRun = false;
|
|
3488
|
+
const ext = this;
|
|
3489
|
+
const editorRef = ext.editor ?? null;
|
|
3490
|
+
return [
|
|
3491
|
+
new import_state3.Plugin({
|
|
3492
|
+
key: PLUGIN_KEY,
|
|
3493
|
+
view(_editorView) {
|
|
3494
|
+
viewRef = _editorView;
|
|
3495
|
+
return {
|
|
3496
|
+
destroy() {
|
|
3497
|
+
viewRef = null;
|
|
3498
|
+
}
|
|
3499
|
+
};
|
|
3500
|
+
},
|
|
3501
|
+
appendTransaction(transactions, _oldState, _newState) {
|
|
3502
|
+
if (transactions.some((t) => t.getMeta("PAGE_COUNT_META_KEY") !== void 0)) {
|
|
3503
|
+
return null;
|
|
3504
|
+
}
|
|
3505
|
+
if (transactions.some((t) => {
|
|
3506
|
+
const m = t.getMeta(PLUGIN_KEY);
|
|
3507
|
+
return m !== void 0 && m.splitAtRow !== void 0;
|
|
3508
|
+
})) return null;
|
|
3509
|
+
if (!viewRef || viewRef.isDestroyed) return null;
|
|
3510
|
+
if (pendingRun) return null;
|
|
3511
|
+
pendingRun = true;
|
|
3512
|
+
queueMicrotask(() => {
|
|
3513
|
+
pendingRun = false;
|
|
3514
|
+
if (!viewRef || viewRef.isDestroyed) return;
|
|
3515
|
+
runSplitPass(viewRef, editorRef);
|
|
3516
|
+
});
|
|
3517
|
+
return null;
|
|
3518
|
+
}
|
|
3519
|
+
})
|
|
3520
|
+
];
|
|
3521
|
+
}
|
|
3522
|
+
});
|
|
3523
|
+
var tablePageSplitPlugin = (0, import_docflow_core20.definePlugin)({
|
|
3524
|
+
id: "tablePageSplit",
|
|
3525
|
+
tiptapExtensions: [TablePageSplitExtension]
|
|
3526
|
+
});
|
|
3527
|
+
|
|
3528
|
+
// src/slashMenu.ts
|
|
3529
|
+
var import_core13 = require("@tiptap/core");
|
|
3530
|
+
var import_state4 = require("@tiptap/pm/state");
|
|
3531
|
+
var import_docflow_core21 = require("@kedataindo/docflow-core");
|
|
3289
3532
|
var slashState = {
|
|
3290
3533
|
open: false,
|
|
3291
3534
|
query: "",
|
|
@@ -3339,12 +3582,12 @@ function getRegisteredCommands() {
|
|
|
3339
3582
|
return true;
|
|
3340
3583
|
});
|
|
3341
3584
|
}
|
|
3342
|
-
var SlashMenuExtension =
|
|
3585
|
+
var SlashMenuExtension = import_core13.Extension.create({
|
|
3343
3586
|
name: "slashMenu",
|
|
3344
3587
|
addProseMirrorPlugins() {
|
|
3345
3588
|
return [
|
|
3346
|
-
new
|
|
3347
|
-
key: new
|
|
3589
|
+
new import_state4.Plugin({
|
|
3590
|
+
key: new import_state4.PluginKey("slashMenu"),
|
|
3348
3591
|
props: {
|
|
3349
3592
|
handleTextInput(view, from, _to, text) {
|
|
3350
3593
|
if (text === "/") {
|
|
@@ -3430,7 +3673,7 @@ var SlashMenuExtension = import_core12.Extension.create({
|
|
|
3430
3673
|
];
|
|
3431
3674
|
}
|
|
3432
3675
|
});
|
|
3433
|
-
var slashMenuPlugin = (0,
|
|
3676
|
+
var slashMenuPlugin = (0, import_docflow_core21.definePlugin)({
|
|
3434
3677
|
id: "slash-menu",
|
|
3435
3678
|
tiptapExtensions: [SlashMenuExtension],
|
|
3436
3679
|
hooks: {
|
|
@@ -3450,6 +3693,7 @@ var defaultPlugins = [
|
|
|
3450
3693
|
linkPlugin,
|
|
3451
3694
|
imagePlugin,
|
|
3452
3695
|
tablePlugin,
|
|
3696
|
+
tablePageSplitPlugin,
|
|
3453
3697
|
blockquotePlugin,
|
|
3454
3698
|
codeBlockPlugin,
|
|
3455
3699
|
placeholderPlugin,
|
|
@@ -3520,6 +3764,7 @@ var defaultPlugins = [
|
|
|
3520
3764
|
slashMenuPlugin,
|
|
3521
3765
|
slashState,
|
|
3522
3766
|
smartElementsPlugin,
|
|
3767
|
+
tablePageSplitPlugin,
|
|
3523
3768
|
tablePlugin,
|
|
3524
3769
|
textColorPlugin,
|
|
3525
3770
|
tocPlugin
|
package/dist/index.d.cts
CHANGED
|
@@ -281,6 +281,8 @@ declare const smartElementsPlugin: _kedata_indonesia_docflow_core.DocsEditorPlug
|
|
|
281
281
|
*/
|
|
282
282
|
declare const BibliographyNode: Node<any, any>;
|
|
283
283
|
|
|
284
|
+
declare const tablePageSplitPlugin: _kedata_indonesia_docflow_core.DocsEditorPlugin;
|
|
285
|
+
|
|
284
286
|
interface SlashState {
|
|
285
287
|
open: boolean;
|
|
286
288
|
query: string;
|
|
@@ -306,4 +308,4 @@ declare const slashMenuPlugin: _kedata_indonesia_docflow_core.DocsEditorPlugin;
|
|
|
306
308
|
|
|
307
309
|
declare const defaultPlugins: _kedata_indonesia_docflow_core.DocsEditorPlugin[];
|
|
308
310
|
|
|
309
|
-
export { AIExtension, type AIPreviewState, BibliographyNode, CitationEngineExtension, CitationNode, CiteEngine, type CommentMarkAttrs, CommentMark as CommentMarkExtension, DateChipNode, DropdownChipNode, FileChipNode, FootnoteNode, LocationChipNode, PageBreak, PeopleChipNode, type PlaceholderPluginOptions, SlashMenuExtension, TocEntryNode, TocNode, TocPageNumNode, aiPlugin, aiPluginKey, alignmentPlugin, blockquotePlugin, citationPlugin, codeBlockPlugin, collectHeadings, commentPlugin, createPlaceholderPlugin, defaultPlugins, fontSizePlugin, footnotePlugin, formattingPlugin, getAIPreview, getCitationEngine, headingsPlugin, highlightPlugin, imagePlugin, insertMarkdownBlock, linkPlugin, listsPlugin, markdownToFragment, onSlashStateChange, pageBreakPlugin, placeholderPlugin, regenerateToc, registerSlashCommands, slashMenuPlugin, slashState, smartElementsPlugin, tablePlugin, textColorPlugin, tocPlugin };
|
|
311
|
+
export { AIExtension, type AIPreviewState, BibliographyNode, CitationEngineExtension, CitationNode, CiteEngine, type CommentMarkAttrs, CommentMark as CommentMarkExtension, DateChipNode, DropdownChipNode, FileChipNode, FootnoteNode, LocationChipNode, PageBreak, PeopleChipNode, type PlaceholderPluginOptions, SlashMenuExtension, TocEntryNode, TocNode, TocPageNumNode, aiPlugin, aiPluginKey, alignmentPlugin, blockquotePlugin, citationPlugin, codeBlockPlugin, collectHeadings, commentPlugin, createPlaceholderPlugin, defaultPlugins, fontSizePlugin, footnotePlugin, formattingPlugin, getAIPreview, getCitationEngine, headingsPlugin, highlightPlugin, imagePlugin, insertMarkdownBlock, linkPlugin, listsPlugin, markdownToFragment, onSlashStateChange, pageBreakPlugin, placeholderPlugin, regenerateToc, registerSlashCommands, slashMenuPlugin, slashState, smartElementsPlugin, tablePageSplitPlugin, tablePlugin, textColorPlugin, tocPlugin };
|
package/dist/index.d.ts
CHANGED
|
@@ -281,6 +281,8 @@ declare const smartElementsPlugin: _kedata_indonesia_docflow_core.DocsEditorPlug
|
|
|
281
281
|
*/
|
|
282
282
|
declare const BibliographyNode: Node<any, any>;
|
|
283
283
|
|
|
284
|
+
declare const tablePageSplitPlugin: _kedata_indonesia_docflow_core.DocsEditorPlugin;
|
|
285
|
+
|
|
284
286
|
interface SlashState {
|
|
285
287
|
open: boolean;
|
|
286
288
|
query: string;
|
|
@@ -306,4 +308,4 @@ declare const slashMenuPlugin: _kedata_indonesia_docflow_core.DocsEditorPlugin;
|
|
|
306
308
|
|
|
307
309
|
declare const defaultPlugins: _kedata_indonesia_docflow_core.DocsEditorPlugin[];
|
|
308
310
|
|
|
309
|
-
export { AIExtension, type AIPreviewState, BibliographyNode, CitationEngineExtension, CitationNode, CiteEngine, type CommentMarkAttrs, CommentMark as CommentMarkExtension, DateChipNode, DropdownChipNode, FileChipNode, FootnoteNode, LocationChipNode, PageBreak, PeopleChipNode, type PlaceholderPluginOptions, SlashMenuExtension, TocEntryNode, TocNode, TocPageNumNode, aiPlugin, aiPluginKey, alignmentPlugin, blockquotePlugin, citationPlugin, codeBlockPlugin, collectHeadings, commentPlugin, createPlaceholderPlugin, defaultPlugins, fontSizePlugin, footnotePlugin, formattingPlugin, getAIPreview, getCitationEngine, headingsPlugin, highlightPlugin, imagePlugin, insertMarkdownBlock, linkPlugin, listsPlugin, markdownToFragment, onSlashStateChange, pageBreakPlugin, placeholderPlugin, regenerateToc, registerSlashCommands, slashMenuPlugin, slashState, smartElementsPlugin, tablePlugin, textColorPlugin, tocPlugin };
|
|
311
|
+
export { AIExtension, type AIPreviewState, BibliographyNode, CitationEngineExtension, CitationNode, CiteEngine, type CommentMarkAttrs, CommentMark as CommentMarkExtension, DateChipNode, DropdownChipNode, FileChipNode, FootnoteNode, LocationChipNode, PageBreak, PeopleChipNode, type PlaceholderPluginOptions, SlashMenuExtension, TocEntryNode, TocNode, TocPageNumNode, aiPlugin, aiPluginKey, alignmentPlugin, blockquotePlugin, citationPlugin, codeBlockPlugin, collectHeadings, commentPlugin, createPlaceholderPlugin, defaultPlugins, fontSizePlugin, footnotePlugin, formattingPlugin, getAIPreview, getCitationEngine, headingsPlugin, highlightPlugin, imagePlugin, insertMarkdownBlock, linkPlugin, listsPlugin, markdownToFragment, onSlashStateChange, pageBreakPlugin, placeholderPlugin, regenerateToc, registerSlashCommands, slashMenuPlugin, slashState, smartElementsPlugin, tablePageSplitPlugin, tablePlugin, textColorPlugin, tocPlugin };
|
package/dist/index.js
CHANGED
|
@@ -182,6 +182,20 @@ import TableRow from "@tiptap/extension-table-row";
|
|
|
182
182
|
import TableCell from "@tiptap/extension-table-cell";
|
|
183
183
|
import TableHeader from "@tiptap/extension-table-header";
|
|
184
184
|
var CustomTable = Table.extend({
|
|
185
|
+
addAttributes() {
|
|
186
|
+
return {
|
|
187
|
+
...this.parent?.(),
|
|
188
|
+
// Marker used by the experimental `tablePageSplitPlugin` to tag the
|
|
189
|
+
// head of a split pair. Backwards-compatible (default null) so docs
|
|
190
|
+
// without this attr round-trip cleanly. See
|
|
191
|
+
// packages/plugins/src/tablePageSplit.ts.
|
|
192
|
+
tablePageSplit: {
|
|
193
|
+
default: null,
|
|
194
|
+
parseHTML: () => null,
|
|
195
|
+
renderHTML: () => ({})
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
},
|
|
185
199
|
renderHTML({ node, HTMLAttributes }) {
|
|
186
200
|
let colCount = 0;
|
|
187
201
|
const firstRow = node.firstChild;
|
|
@@ -224,9 +238,11 @@ var CustomTable = Table.extend({
|
|
|
224
238
|
}
|
|
225
239
|
}
|
|
226
240
|
const tableAttrs = mergeAttributes(HTMLAttributes);
|
|
241
|
+
tableAttrs["data-tps-splittable"] = "";
|
|
227
242
|
if (hasExplicitWidths && totalWidth > 0) {
|
|
228
243
|
const existingStyle = tableAttrs.style || "";
|
|
229
244
|
tableAttrs.style = existingStyle ? `${existingStyle}; width: ${totalWidth}px !important` : `width: ${totalWidth}px !important`;
|
|
245
|
+
tableAttrs["data-colwidth"] = "explicit";
|
|
230
246
|
}
|
|
231
247
|
return [
|
|
232
248
|
"table",
|
|
@@ -3190,10 +3206,236 @@ var smartElementsPlugin = definePlugin19({
|
|
|
3190
3206
|
}
|
|
3191
3207
|
});
|
|
3192
3208
|
|
|
3193
|
-
// src/
|
|
3209
|
+
// src/tablePageSplit.ts
|
|
3210
|
+
import { definePlugin as definePlugin20 } from "@kedataindo/docflow-core";
|
|
3194
3211
|
import { Extension as Extension4 } from "@tiptap/core";
|
|
3195
3212
|
import { Plugin as Plugin2, PluginKey as PluginKey2 } from "@tiptap/pm/state";
|
|
3196
|
-
|
|
3213
|
+
var PLUGIN_KEY = new PluginKey2("docflow/table-page-split");
|
|
3214
|
+
var SPLIT_MARK = "head";
|
|
3215
|
+
var MAX_SPLIT_DEPTH = 16;
|
|
3216
|
+
function readPageContentHeightPx(view, editor) {
|
|
3217
|
+
const storage = editor?.storage?.PaginationPlus;
|
|
3218
|
+
if (storage && typeof storage.pageHeight === "number") {
|
|
3219
|
+
const h1 = storage.headerHeight?.get?.(1) ?? 0;
|
|
3220
|
+
const f1 = storage.footerHeight?.get?.(1) ?? 0;
|
|
3221
|
+
const contentPx = storage.pageHeight - (storage.marginTop ?? 0) - (storage.marginBottom ?? 0) - (storage.contentMarginTop ?? 0) - (storage.contentMarginBottom ?? 0) - h1 - f1;
|
|
3222
|
+
if (Number.isFinite(contentPx) && contentPx > 0) return contentPx;
|
|
3223
|
+
}
|
|
3224
|
+
const raw = view.dom.style.getPropertyValue("--rm-max-content-child-height");
|
|
3225
|
+
if (raw) {
|
|
3226
|
+
const px = parseFloat(raw);
|
|
3227
|
+
if (Number.isFinite(px) && px > 0) return px;
|
|
3228
|
+
}
|
|
3229
|
+
return null;
|
|
3230
|
+
}
|
|
3231
|
+
function measureElementPx(el) {
|
|
3232
|
+
if (!el || !(el instanceof HTMLElement)) return 0;
|
|
3233
|
+
return el.getBoundingClientRect().height;
|
|
3234
|
+
}
|
|
3235
|
+
function availablePxAt(view, pos, defaultPageContentPx) {
|
|
3236
|
+
try {
|
|
3237
|
+
const tableCoords = view.coordsAtPos(pos + 1);
|
|
3238
|
+
const tableTop = tableCoords.top;
|
|
3239
|
+
const paginationWrapper = view.dom.querySelector("[data-rm-pagination]");
|
|
3240
|
+
if (!paginationWrapper) return defaultPageContentPx;
|
|
3241
|
+
const breakers = paginationWrapper.querySelectorAll(".rm-page-break .breaker");
|
|
3242
|
+
for (let i = 0; i < breakers.length; i++) {
|
|
3243
|
+
const br = breakers[i];
|
|
3244
|
+
if (!(br instanceof HTMLElement)) continue;
|
|
3245
|
+
const breakTop = br.getBoundingClientRect().top;
|
|
3246
|
+
if (breakTop > tableTop) {
|
|
3247
|
+
return Math.max(1, Math.floor(breakTop - tableTop));
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3250
|
+
return defaultPageContentPx;
|
|
3251
|
+
} catch {
|
|
3252
|
+
return null;
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
function measureRows(tableNode, tableDom) {
|
|
3256
|
+
const expectedRows = tableNode.childCount;
|
|
3257
|
+
const trs = tableDom.querySelectorAll(":scope > tbody > tr");
|
|
3258
|
+
if (trs.length !== expectedRows) return null;
|
|
3259
|
+
const measurements = [];
|
|
3260
|
+
let cumulative = 0;
|
|
3261
|
+
for (let i = 0; i < trs.length; i++) {
|
|
3262
|
+
const h = measureElementPx(trs[i]);
|
|
3263
|
+
cumulative += h;
|
|
3264
|
+
measurements.push({ rowIndex: i, heightPx: h, cumulativePx: cumulative });
|
|
3265
|
+
}
|
|
3266
|
+
return measurements;
|
|
3267
|
+
}
|
|
3268
|
+
function findSplitRow(measurements, pageContentPx) {
|
|
3269
|
+
if (measurements.length < 2) return -1;
|
|
3270
|
+
if (measurements[measurements.length - 1].cumulativePx <= pageContentPx) return -1;
|
|
3271
|
+
let lo = 0;
|
|
3272
|
+
let hi = measurements.length - 2;
|
|
3273
|
+
let best = -1;
|
|
3274
|
+
while (lo <= hi) {
|
|
3275
|
+
const mid = lo + hi >> 1;
|
|
3276
|
+
if (measurements[mid].cumulativePx <= pageContentPx) {
|
|
3277
|
+
best = mid;
|
|
3278
|
+
lo = mid + 1;
|
|
3279
|
+
} else {
|
|
3280
|
+
hi = mid - 1;
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
return best;
|
|
3284
|
+
}
|
|
3285
|
+
function rowBoundaryOffset(tableNode, splitRow) {
|
|
3286
|
+
if (splitRow < 0 || tableNode.childCount === 0) return 0;
|
|
3287
|
+
let boundary = 0;
|
|
3288
|
+
tableNode.forEach((row, offset, index) => {
|
|
3289
|
+
if (index <= splitRow) boundary = offset + row.nodeSize;
|
|
3290
|
+
});
|
|
3291
|
+
return boundary;
|
|
3292
|
+
}
|
|
3293
|
+
function tailMeasurements(measurements, splitRow) {
|
|
3294
|
+
const tail = [];
|
|
3295
|
+
let cumulative = 0;
|
|
3296
|
+
for (let i = splitRow + 1; i < measurements.length; i++) {
|
|
3297
|
+
cumulative += measurements[i].heightPx;
|
|
3298
|
+
tail.push({ rowIndex: i, heightPx: measurements[i].heightPx, cumulativePx: cumulative });
|
|
3299
|
+
}
|
|
3300
|
+
return tail;
|
|
3301
|
+
}
|
|
3302
|
+
function buildSplitChain(tableNode, measurements, headBudgetPx, pageContentPx, schema, tableType, depth) {
|
|
3303
|
+
const budget = depth === 0 ? headBudgetPx : pageContentPx;
|
|
3304
|
+
const splitRow = findSplitRow(measurements, budget);
|
|
3305
|
+
if (splitRow < 0) return [tableNode];
|
|
3306
|
+
if (depth >= MAX_SPLIT_DEPTH) return [tableNode];
|
|
3307
|
+
const splitPos = rowBoundaryOffset(tableNode, splitRow);
|
|
3308
|
+
const content = tableNode.content;
|
|
3309
|
+
const leftContent = content.cut(0, splitPos);
|
|
3310
|
+
const rightContent = content.cut(splitPos);
|
|
3311
|
+
const leftAttrs = { ...tableNode.attrs, tablePageSplit: SPLIT_MARK };
|
|
3312
|
+
const rightAttrs = { ...tableNode.attrs, tablePageSplit: null };
|
|
3313
|
+
const leftTable = tableType.create(leftAttrs, leftContent, tableNode.marks);
|
|
3314
|
+
const rightTable = tableType.create(rightAttrs, rightContent, tableNode.marks);
|
|
3315
|
+
const expectedRows = tableNode.childCount;
|
|
3316
|
+
const leftRows = leftTable.childCount;
|
|
3317
|
+
const rightRows = rightTable.childCount;
|
|
3318
|
+
if (leftRows + rightRows !== expectedRows) {
|
|
3319
|
+
throw new Error(
|
|
3320
|
+
`[tablePageSplitPlugin] row-count invariant violated: expected ${expectedRows}, got ${leftRows} + ${rightRows} = ${leftRows + rightRows}`
|
|
3321
|
+
);
|
|
3322
|
+
}
|
|
3323
|
+
const tail = tailMeasurements(measurements, splitRow);
|
|
3324
|
+
const tailChain = buildSplitChain(rightTable, tail, headBudgetPx, pageContentPx, schema, tableType, depth + 1);
|
|
3325
|
+
if (tailChain.length === 1) {
|
|
3326
|
+
return [leftTable, rightTable];
|
|
3327
|
+
}
|
|
3328
|
+
return [leftTable, ...tailChain];
|
|
3329
|
+
}
|
|
3330
|
+
function lastChunkHeightPx(measurements, headBudgetPx, pageContentPx) {
|
|
3331
|
+
let current = measurements;
|
|
3332
|
+
let budget = headBudgetPx;
|
|
3333
|
+
let splitCount = 0;
|
|
3334
|
+
for (let depth = 0; depth < MAX_SPLIT_DEPTH; depth++) {
|
|
3335
|
+
const splitRow = findSplitRow(current, budget);
|
|
3336
|
+
if (splitRow < 0) {
|
|
3337
|
+
return splitCount === 0 ? null : current[current.length - 1]?.cumulativePx ?? 0;
|
|
3338
|
+
}
|
|
3339
|
+
splitCount++;
|
|
3340
|
+
current = tailMeasurements(current, splitRow);
|
|
3341
|
+
budget = pageContentPx;
|
|
3342
|
+
}
|
|
3343
|
+
return current[current.length - 1]?.cumulativePx ?? 0;
|
|
3344
|
+
}
|
|
3345
|
+
function runSplitPass(view, editor) {
|
|
3346
|
+
if (!view || view.isDestroyed) return;
|
|
3347
|
+
const v = view;
|
|
3348
|
+
const state = v.state;
|
|
3349
|
+
const { schema } = state;
|
|
3350
|
+
const tableType = schema.nodes.table;
|
|
3351
|
+
if (!tableType) return;
|
|
3352
|
+
const pageContentPx = readPageContentHeightPx(v, editor);
|
|
3353
|
+
if (pageContentPx == null) return;
|
|
3354
|
+
const tables = [];
|
|
3355
|
+
state.doc.descendants((node, pos) => {
|
|
3356
|
+
if (node.type !== tableType) return true;
|
|
3357
|
+
if (node.attrs.tablePageSplit === SPLIT_MARK) return true;
|
|
3358
|
+
const domAt = v.nodeDOM(pos);
|
|
3359
|
+
if (!(domAt instanceof HTMLElement)) return true;
|
|
3360
|
+
const measurements = measureRows(node, domAt);
|
|
3361
|
+
if (!measurements) return true;
|
|
3362
|
+
tables.push({ pos, node, measurements });
|
|
3363
|
+
return true;
|
|
3364
|
+
});
|
|
3365
|
+
if (tables.length === 0) return;
|
|
3366
|
+
const pending = [];
|
|
3367
|
+
let cursorAvailablePx = null;
|
|
3368
|
+
for (let i = 0; i < tables.length; i++) {
|
|
3369
|
+
const { pos, node, measurements } = tables[i];
|
|
3370
|
+
const availablePx = i === 0 ? availablePxAt(v, pos, pageContentPx) ?? pageContentPx : cursorAvailablePx ?? pageContentPx;
|
|
3371
|
+
const chain = buildSplitChain(node, measurements, availablePx, pageContentPx, schema, tableType, 0);
|
|
3372
|
+
if (chain.length > 1) pending.push({ pos, node, chain });
|
|
3373
|
+
const lastChunkPx = lastChunkHeightPx(measurements, availablePx, pageContentPx);
|
|
3374
|
+
if (lastChunkPx === null) {
|
|
3375
|
+
const totalPx = measurements[measurements.length - 1]?.cumulativePx ?? 0;
|
|
3376
|
+
cursorAvailablePx = Math.max(0, availablePx - totalPx);
|
|
3377
|
+
} else {
|
|
3378
|
+
cursorAvailablePx = Math.max(0, pageContentPx - lastChunkPx);
|
|
3379
|
+
}
|
|
3380
|
+
}
|
|
3381
|
+
if (pending.length === 0) return;
|
|
3382
|
+
const tr = state.tr;
|
|
3383
|
+
pending.sort((a, b) => b.pos - a.pos);
|
|
3384
|
+
for (const { pos, node, chain } of pending) {
|
|
3385
|
+
tr.replaceWith(pos, pos + node.nodeSize, chain);
|
|
3386
|
+
}
|
|
3387
|
+
tr.setMeta(PLUGIN_KEY, { splitAtRow: -1 });
|
|
3388
|
+
v.dispatch(tr);
|
|
3389
|
+
}
|
|
3390
|
+
var TablePageSplitExtension = Extension4.create({
|
|
3391
|
+
name: "tablePageSplit",
|
|
3392
|
+
addProseMirrorPlugins() {
|
|
3393
|
+
let viewRef = null;
|
|
3394
|
+
let pendingRun = false;
|
|
3395
|
+
const ext = this;
|
|
3396
|
+
const editorRef = ext.editor ?? null;
|
|
3397
|
+
return [
|
|
3398
|
+
new Plugin2({
|
|
3399
|
+
key: PLUGIN_KEY,
|
|
3400
|
+
view(_editorView) {
|
|
3401
|
+
viewRef = _editorView;
|
|
3402
|
+
return {
|
|
3403
|
+
destroy() {
|
|
3404
|
+
viewRef = null;
|
|
3405
|
+
}
|
|
3406
|
+
};
|
|
3407
|
+
},
|
|
3408
|
+
appendTransaction(transactions, _oldState, _newState) {
|
|
3409
|
+
if (transactions.some((t) => t.getMeta("PAGE_COUNT_META_KEY") !== void 0)) {
|
|
3410
|
+
return null;
|
|
3411
|
+
}
|
|
3412
|
+
if (transactions.some((t) => {
|
|
3413
|
+
const m = t.getMeta(PLUGIN_KEY);
|
|
3414
|
+
return m !== void 0 && m.splitAtRow !== void 0;
|
|
3415
|
+
})) return null;
|
|
3416
|
+
if (!viewRef || viewRef.isDestroyed) return null;
|
|
3417
|
+
if (pendingRun) return null;
|
|
3418
|
+
pendingRun = true;
|
|
3419
|
+
queueMicrotask(() => {
|
|
3420
|
+
pendingRun = false;
|
|
3421
|
+
if (!viewRef || viewRef.isDestroyed) return;
|
|
3422
|
+
runSplitPass(viewRef, editorRef);
|
|
3423
|
+
});
|
|
3424
|
+
return null;
|
|
3425
|
+
}
|
|
3426
|
+
})
|
|
3427
|
+
];
|
|
3428
|
+
}
|
|
3429
|
+
});
|
|
3430
|
+
var tablePageSplitPlugin = definePlugin20({
|
|
3431
|
+
id: "tablePageSplit",
|
|
3432
|
+
tiptapExtensions: [TablePageSplitExtension]
|
|
3433
|
+
});
|
|
3434
|
+
|
|
3435
|
+
// src/slashMenu.ts
|
|
3436
|
+
import { Extension as Extension5 } from "@tiptap/core";
|
|
3437
|
+
import { Plugin as Plugin3, PluginKey as PluginKey3 } from "@tiptap/pm/state";
|
|
3438
|
+
import { definePlugin as definePlugin21 } from "@kedataindo/docflow-core";
|
|
3197
3439
|
var slashState = {
|
|
3198
3440
|
open: false,
|
|
3199
3441
|
query: "",
|
|
@@ -3247,12 +3489,12 @@ function getRegisteredCommands() {
|
|
|
3247
3489
|
return true;
|
|
3248
3490
|
});
|
|
3249
3491
|
}
|
|
3250
|
-
var SlashMenuExtension =
|
|
3492
|
+
var SlashMenuExtension = Extension5.create({
|
|
3251
3493
|
name: "slashMenu",
|
|
3252
3494
|
addProseMirrorPlugins() {
|
|
3253
3495
|
return [
|
|
3254
|
-
new
|
|
3255
|
-
key: new
|
|
3496
|
+
new Plugin3({
|
|
3497
|
+
key: new PluginKey3("slashMenu"),
|
|
3256
3498
|
props: {
|
|
3257
3499
|
handleTextInput(view, from, _to, text) {
|
|
3258
3500
|
if (text === "/") {
|
|
@@ -3338,7 +3580,7 @@ var SlashMenuExtension = Extension4.create({
|
|
|
3338
3580
|
];
|
|
3339
3581
|
}
|
|
3340
3582
|
});
|
|
3341
|
-
var slashMenuPlugin =
|
|
3583
|
+
var slashMenuPlugin = definePlugin21({
|
|
3342
3584
|
id: "slash-menu",
|
|
3343
3585
|
tiptapExtensions: [SlashMenuExtension],
|
|
3344
3586
|
hooks: {
|
|
@@ -3358,6 +3600,7 @@ var defaultPlugins = [
|
|
|
3358
3600
|
linkPlugin,
|
|
3359
3601
|
imagePlugin,
|
|
3360
3602
|
tablePlugin,
|
|
3603
|
+
tablePageSplitPlugin,
|
|
3361
3604
|
blockquotePlugin,
|
|
3362
3605
|
codeBlockPlugin,
|
|
3363
3606
|
placeholderPlugin,
|
|
@@ -3427,6 +3670,7 @@ export {
|
|
|
3427
3670
|
slashMenuPlugin,
|
|
3428
3671
|
slashState,
|
|
3429
3672
|
smartElementsPlugin,
|
|
3673
|
+
tablePageSplitPlugin,
|
|
3430
3674
|
tablePlugin,
|
|
3431
3675
|
textColorPlugin,
|
|
3432
3676
|
tocPlugin
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kedataindo/docflow-plugins",
|
|
3
3
|
"license": "UNLICENSED",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.52",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.js",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@tiptap/pm": "^2.11.0",
|
|
32
32
|
"citeproc": "^2.4.63",
|
|
33
33
|
"tiptap-markdown": "0.8.10",
|
|
34
|
-
"@kedataindo/docflow-core": "0.0.
|
|
34
|
+
"@kedataindo/docflow-core": "0.0.50"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"@tiptap/core": "^2.11.0",
|