mirror of
https://github.com/docmost/docmost.git
synced 2026-05-14 12:44:16 +08:00
tableview
This commit is contained in:
@@ -56,6 +56,7 @@ import {
|
||||
Status,
|
||||
TransclusionSource,
|
||||
TransclusionReference,
|
||||
TableView,
|
||||
} from "@docmost/editor-ext";
|
||||
import {
|
||||
randomElement,
|
||||
@@ -259,6 +260,7 @@ export const mainExtensions = [
|
||||
resizable: true,
|
||||
lastColumnResizable: true,
|
||||
allowTableNodeSelection: true,
|
||||
View: TableView,
|
||||
}),
|
||||
TableRow,
|
||||
TableCell,
|
||||
|
||||
@@ -2,4 +2,5 @@ export * from "./row";
|
||||
export * from "./cell";
|
||||
export * from "./header";
|
||||
export * from "./table";
|
||||
export * from "./dnd";
|
||||
export * from "./dnd";
|
||||
export * from "./table-view";
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
import type { Node as ProseMirrorNode } from '@tiptap/pm/model';
|
||||
import type { NodeView, ViewMutationRecord } from '@tiptap/pm/view';
|
||||
import { getColStyleDeclaration } from './utils/col-style';
|
||||
|
||||
export function updateColumns(
|
||||
node: ProseMirrorNode,
|
||||
colgroup: HTMLTableColElement, // <colgroup> has the same prototype as <col>
|
||||
table: HTMLTableElement,
|
||||
cellMinWidth: number,
|
||||
overrideCol?: number,
|
||||
overrideValue?: number,
|
||||
) {
|
||||
let totalWidth = 0;
|
||||
let fixedWidth = true;
|
||||
let nextDOM = colgroup.firstChild;
|
||||
const row = node.firstChild;
|
||||
|
||||
if (row !== null) {
|
||||
for (let i = 0, col = 0; i < row.childCount; i += 1) {
|
||||
const { colspan, colwidth } = row.child(i).attrs;
|
||||
|
||||
for (let j = 0; j < colspan; j += 1, col += 1) {
|
||||
const hasWidth =
|
||||
overrideCol === col
|
||||
? overrideValue
|
||||
: ((colwidth && colwidth[j]) as number | undefined);
|
||||
const cssWidth = hasWidth ? `${hasWidth}px` : '';
|
||||
|
||||
totalWidth += hasWidth || cellMinWidth;
|
||||
|
||||
if (!hasWidth) {
|
||||
fixedWidth = false;
|
||||
}
|
||||
|
||||
if (!nextDOM) {
|
||||
const colElement = document.createElement('col');
|
||||
|
||||
const [propertyKey, propertyValue] = getColStyleDeclaration(
|
||||
cellMinWidth,
|
||||
hasWidth,
|
||||
);
|
||||
|
||||
colElement.style.setProperty(propertyKey, propertyValue);
|
||||
|
||||
colgroup.appendChild(colElement);
|
||||
} else {
|
||||
if ((nextDOM as HTMLTableColElement).style.width !== cssWidth) {
|
||||
const [propertyKey, propertyValue] = getColStyleDeclaration(
|
||||
cellMinWidth,
|
||||
hasWidth,
|
||||
);
|
||||
|
||||
(nextDOM as HTMLTableColElement).style.setProperty(
|
||||
propertyKey,
|
||||
propertyValue,
|
||||
);
|
||||
}
|
||||
|
||||
nextDOM = nextDOM.nextSibling;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (nextDOM) {
|
||||
const after = nextDOM.nextSibling;
|
||||
|
||||
nextDOM.parentNode?.removeChild(nextDOM);
|
||||
nextDOM = after;
|
||||
}
|
||||
|
||||
// Check if user has set a width style on the table node
|
||||
const hasUserWidth =
|
||||
node.attrs.style &&
|
||||
typeof node.attrs.style === 'string' &&
|
||||
/\bwidth\s*:/i.test(node.attrs.style);
|
||||
|
||||
if (fixedWidth && !hasUserWidth) {
|
||||
table.style.width = `${totalWidth}px`;
|
||||
table.style.minWidth = '';
|
||||
} else {
|
||||
table.style.width = '';
|
||||
table.style.minWidth = `${totalWidth}px`;
|
||||
}
|
||||
}
|
||||
|
||||
export class TableView implements NodeView {
|
||||
node: ProseMirrorNode;
|
||||
|
||||
cellMinWidth: number;
|
||||
|
||||
dom: HTMLDivElement;
|
||||
|
||||
table: HTMLTableElement;
|
||||
|
||||
colgroup: HTMLTableColElement;
|
||||
|
||||
contentDOM: HTMLTableSectionElement;
|
||||
|
||||
constructor(node: ProseMirrorNode, cellMinWidth: number) {
|
||||
this.node = node;
|
||||
this.cellMinWidth = cellMinWidth;
|
||||
this.dom = document.createElement('div');
|
||||
this.dom.className = 'tableWrapper';
|
||||
this.table = this.dom.appendChild(document.createElement('table'));
|
||||
|
||||
// Apply user styles to the table element
|
||||
if (node.attrs.style) {
|
||||
this.table.style.cssText = node.attrs.style;
|
||||
}
|
||||
|
||||
this.colgroup = this.table.appendChild(document.createElement('colgroup'));
|
||||
updateColumns(node, this.colgroup, this.table, cellMinWidth);
|
||||
this.contentDOM = this.table.appendChild(document.createElement('tbody'));
|
||||
}
|
||||
|
||||
update(node: ProseMirrorNode) {
|
||||
if (node.type !== this.node.type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.node = node;
|
||||
updateColumns(node, this.colgroup, this.table, this.cellMinWidth);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ignoreMutation(mutation: ViewMutationRecord) {
|
||||
const target = mutation.target as Node;
|
||||
const isInsideWrapper = this.dom.contains(target);
|
||||
const isInsideContent = this.contentDOM.contains(target);
|
||||
|
||||
if (isInsideWrapper && !isInsideContent) {
|
||||
if (
|
||||
mutation.type === 'attributes' ||
|
||||
mutation.type === 'childList' ||
|
||||
mutation.type === 'characterData'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ function handleListOutdent(editor: Editor): boolean {
|
||||
}
|
||||
|
||||
export const CustomTable = Table.extend({
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
...this.parent?.(),
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
export function getColStyleDeclaration(minWidth: number, width: number | undefined): [string, string] {
|
||||
if (width) {
|
||||
// apply the stored width unless it is below the configured minimum cell width
|
||||
return ['width', `${Math.max(width, minWidth)}px`]
|
||||
}
|
||||
|
||||
// set the minimum with on the column if it has no stored width
|
||||
return ['min-width', `${minWidth}px`]
|
||||
}
|
||||
Reference in New Issue
Block a user