mirror of
https://github.com/docmost/docmost.git
synced 2026-05-18 23:44:24 +08:00
tableview
This commit is contained in:
@@ -56,6 +56,7 @@ import {
|
|||||||
Status,
|
Status,
|
||||||
TransclusionSource,
|
TransclusionSource,
|
||||||
TransclusionReference,
|
TransclusionReference,
|
||||||
|
TableView,
|
||||||
} from "@docmost/editor-ext";
|
} from "@docmost/editor-ext";
|
||||||
import {
|
import {
|
||||||
randomElement,
|
randomElement,
|
||||||
@@ -259,6 +260,7 @@ export const mainExtensions = [
|
|||||||
resizable: true,
|
resizable: true,
|
||||||
lastColumnResizable: true,
|
lastColumnResizable: true,
|
||||||
allowTableNodeSelection: true,
|
allowTableNodeSelection: true,
|
||||||
|
View: TableView,
|
||||||
}),
|
}),
|
||||||
TableRow,
|
TableRow,
|
||||||
TableCell,
|
TableCell,
|
||||||
|
|||||||
@@ -2,4 +2,5 @@ export * from "./row";
|
|||||||
export * from "./cell";
|
export * from "./cell";
|
||||||
export * from "./header";
|
export * from "./header";
|
||||||
export * from "./table";
|
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({
|
export const CustomTable = Table.extend({
|
||||||
|
|
||||||
addKeyboardShortcuts() {
|
addKeyboardShortcuts() {
|
||||||
return {
|
return {
|
||||||
...this.parent?.(),
|
...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