mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
Lexical: Wired table properties, and other buttons
This commit is contained in:
parent
abbfd42a6c
commit
ebf95f637a
@ -20,7 +20,7 @@ import {
|
|||||||
TableCellNode
|
TableCellNode
|
||||||
} from "@lexical/table";
|
} from "@lexical/table";
|
||||||
import {TableCellHeaderState} from "@lexical/table/LexicalTableCellNode";
|
import {TableCellHeaderState} from "@lexical/table/LexicalTableCellNode";
|
||||||
import {createStyleMapFromDomStyles, StyleMap} from "../utils/styles";
|
import {extractStyleMapFromElement, StyleMap} from "../utils/dom";
|
||||||
|
|
||||||
export type SerializedCustomTableCellNode = Spread<{
|
export type SerializedCustomTableCellNode = Spread<{
|
||||||
styles: Record<string, string>,
|
styles: Record<string, string>,
|
||||||
@ -45,6 +45,11 @@ export class CustomTableCellNode extends TableCellNode {
|
|||||||
return cellNode;
|
return cellNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearWidth(): void {
|
||||||
|
const self = this.getWritable();
|
||||||
|
self.__width = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
getStyles(): StyleMap {
|
getStyles(): StyleMap {
|
||||||
const self = this.getLatest();
|
const self = this.getLatest();
|
||||||
return new Map(self.__styles);
|
return new Map(self.__styles);
|
||||||
@ -122,7 +127,7 @@ function $convertCustomTableCellNodeElement(domNode: Node): DOMConversionOutput
|
|||||||
const output = $convertTableCellNodeElement(domNode);
|
const output = $convertTableCellNodeElement(domNode);
|
||||||
|
|
||||||
if (domNode instanceof HTMLElement && output.node instanceof CustomTableCellNode) {
|
if (domNode instanceof HTMLElement && output.node instanceof CustomTableCellNode) {
|
||||||
output.node.setStyles(createStyleMapFromDomStyles(domNode.style));
|
output.node.setStyles(extractStyleMapFromElement(domNode));
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
import {
|
import {
|
||||||
$createParagraphNode,
|
|
||||||
$isElementNode,
|
|
||||||
$isLineBreakNode,
|
|
||||||
$isTextNode,
|
|
||||||
DOMConversionMap,
|
DOMConversionMap,
|
||||||
DOMConversionOutput,
|
DOMConversionOutput,
|
||||||
EditorConfig,
|
EditorConfig,
|
||||||
@ -11,14 +7,11 @@ import {
|
|||||||
} from "lexical";
|
} from "lexical";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
$createTableCellNode,
|
|
||||||
$isTableCellNode,
|
|
||||||
SerializedTableRowNode,
|
SerializedTableRowNode,
|
||||||
TableCellHeaderStates,
|
|
||||||
TableRowNode
|
TableRowNode
|
||||||
} from "@lexical/table";
|
} from "@lexical/table";
|
||||||
import {createStyleMapFromDomStyles, StyleMap} from "../utils/styles";
|
|
||||||
import {NodeKey} from "lexical/LexicalNode";
|
import {NodeKey} from "lexical/LexicalNode";
|
||||||
|
import {extractStyleMapFromElement, StyleMap} from "../utils/dom";
|
||||||
|
|
||||||
export type SerializedCustomTableRowNode = Spread<{
|
export type SerializedCustomTableRowNode = Spread<{
|
||||||
styles: Record<string, string>,
|
styles: Record<string, string>,
|
||||||
@ -98,7 +91,7 @@ export function $convertTableRowElement(domNode: Node): DOMConversionOutput {
|
|||||||
const rowNode = $createCustomTableRowNode();
|
const rowNode = $createCustomTableRowNode();
|
||||||
|
|
||||||
if (domNode instanceof HTMLElement) {
|
if (domNode instanceof HTMLElement) {
|
||||||
rowNode.setStyles(createStyleMapFromDomStyles(domNode.style));
|
rowNode.setStyles(extractStyleMapFromElement(domNode));
|
||||||
}
|
}
|
||||||
|
|
||||||
return {node: rowNode};
|
return {node: rowNode};
|
||||||
|
@ -2,17 +2,19 @@ import {SerializedTableNode, TableNode} from "@lexical/table";
|
|||||||
import {DOMConversion, DOMConversionMap, DOMConversionOutput, LexicalNode, Spread} from "lexical";
|
import {DOMConversion, DOMConversionMap, DOMConversionOutput, LexicalNode, Spread} from "lexical";
|
||||||
import {EditorConfig} from "lexical/LexicalEditor";
|
import {EditorConfig} from "lexical/LexicalEditor";
|
||||||
|
|
||||||
import {el} from "../utils/dom";
|
import {el, extractStyleMapFromElement, StyleMap} from "../utils/dom";
|
||||||
import {getTableColumnWidths} from "../utils/tables";
|
import {getTableColumnWidths} from "../utils/tables";
|
||||||
|
|
||||||
export type SerializedCustomTableNode = Spread<{
|
export type SerializedCustomTableNode = Spread<{
|
||||||
id: string;
|
id: string;
|
||||||
colWidths: string[];
|
colWidths: string[];
|
||||||
|
styles: Record<string, string>,
|
||||||
}, SerializedTableNode>
|
}, SerializedTableNode>
|
||||||
|
|
||||||
export class CustomTableNode extends TableNode {
|
export class CustomTableNode extends TableNode {
|
||||||
__id: string = '';
|
__id: string = '';
|
||||||
__colWidths: string[] = [];
|
__colWidths: string[] = [];
|
||||||
|
__styles: StyleMap = new Map;
|
||||||
|
|
||||||
static getType() {
|
static getType() {
|
||||||
return 'custom-table';
|
return 'custom-table';
|
||||||
@ -38,10 +40,21 @@ export class CustomTableNode extends TableNode {
|
|||||||
return self.__colWidths;
|
return self.__colWidths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getStyles(): StyleMap {
|
||||||
|
const self = this.getLatest();
|
||||||
|
return new Map(self.__styles);
|
||||||
|
}
|
||||||
|
|
||||||
|
setStyles(styles: StyleMap): void {
|
||||||
|
const self = this.getWritable();
|
||||||
|
self.__styles = new Map(styles);
|
||||||
|
}
|
||||||
|
|
||||||
static clone(node: CustomTableNode) {
|
static clone(node: CustomTableNode) {
|
||||||
const newNode = new CustomTableNode(node.__key);
|
const newNode = new CustomTableNode(node.__key);
|
||||||
newNode.__id = node.__id;
|
newNode.__id = node.__id;
|
||||||
newNode.__colWidths = node.__colWidths;
|
newNode.__colWidths = node.__colWidths;
|
||||||
|
newNode.__styles = new Map(node.__styles);
|
||||||
return newNode;
|
return newNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,6 +78,10 @@ export class CustomTableNode extends TableNode {
|
|||||||
dom.append(colgroup);
|
dom.append(colgroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const [name, value] of this.__styles.entries()) {
|
||||||
|
dom.style.setProperty(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
return dom;
|
return dom;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,6 +96,7 @@ export class CustomTableNode extends TableNode {
|
|||||||
version: 1,
|
version: 1,
|
||||||
id: this.__id,
|
id: this.__id,
|
||||||
colWidths: this.__colWidths,
|
colWidths: this.__colWidths,
|
||||||
|
styles: Object.fromEntries(this.__styles),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,6 +104,7 @@ export class CustomTableNode extends TableNode {
|
|||||||
const node = $createCustomTableNode();
|
const node = $createCustomTableNode();
|
||||||
node.setId(serializedNode.id);
|
node.setId(serializedNode.id);
|
||||||
node.setColWidths(serializedNode.colWidths);
|
node.setColWidths(serializedNode.colWidths);
|
||||||
|
node.setStyles(new Map(Object.entries(serializedNode.styles)));
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,6 +121,7 @@ export class CustomTableNode extends TableNode {
|
|||||||
|
|
||||||
const colWidths = getTableColumnWidths(element as HTMLTableElement);
|
const colWidths = getTableColumnWidths(element as HTMLTableElement);
|
||||||
node.setColWidths(colWidths);
|
node.setColWidths(colWidths);
|
||||||
|
node.setStyles(extractStyleMapFromElement(element));
|
||||||
|
|
||||||
return {node};
|
return {node};
|
||||||
},
|
},
|
||||||
|
@ -2,13 +2,6 @@
|
|||||||
|
|
||||||
## In progress
|
## In progress
|
||||||
|
|
||||||
- Table features
|
|
||||||
- Table properties form logic
|
|
||||||
- Caption text support
|
|
||||||
- Resize to contents button
|
|
||||||
- Remove formatting button
|
|
||||||
- Cut/Copy/Paste column
|
|
||||||
|
|
||||||
## Main Todo
|
## Main Todo
|
||||||
|
|
||||||
- Alignments: Use existing classes for blocks (including table cells)
|
- Alignments: Use existing classes for blocks (including table cells)
|
||||||
@ -23,6 +16,8 @@
|
|||||||
- Drawing gallery integration
|
- Drawing gallery integration
|
||||||
- Support media src conversions (https://github.com/tinymce/tinymce/blob/release/6.6/modules/tinymce/src/plugins/media/main/ts/core/UrlPatterns.ts)
|
- Support media src conversions (https://github.com/tinymce/tinymce/blob/release/6.6/modules/tinymce/src/plugins/media/main/ts/core/UrlPatterns.ts)
|
||||||
- Media resize support (like images)
|
- Media resize support (like images)
|
||||||
|
- Table caption text support
|
||||||
|
- Table Cut/Copy/Paste column
|
||||||
|
|
||||||
## Secondary Todo
|
## Secondary Todo
|
||||||
|
|
||||||
|
@ -8,24 +8,27 @@ import insertColumnBeforeIcon from "@icons/editor/table-insert-column-before.svg
|
|||||||
import insertRowAboveIcon from "@icons/editor/table-insert-row-above.svg";
|
import insertRowAboveIcon from "@icons/editor/table-insert-row-above.svg";
|
||||||
import insertRowBelowIcon from "@icons/editor/table-insert-row-below.svg";
|
import insertRowBelowIcon from "@icons/editor/table-insert-row-below.svg";
|
||||||
import {EditorUiContext} from "../../framework/core";
|
import {EditorUiContext} from "../../framework/core";
|
||||||
import {$createNodeSelection, $createRangeSelection, $getSelection, BaseSelection} from "lexical";
|
import {$getSelection, BaseSelection} from "lexical";
|
||||||
import {$isCustomTableNode} from "../../../nodes/custom-table";
|
import {$isCustomTableNode} from "../../../nodes/custom-table";
|
||||||
import {
|
import {
|
||||||
$deleteTableColumn__EXPERIMENTAL,
|
$deleteTableColumn__EXPERIMENTAL,
|
||||||
$deleteTableRow__EXPERIMENTAL,
|
$deleteTableRow__EXPERIMENTAL,
|
||||||
$insertTableColumn__EXPERIMENTAL,
|
$insertTableColumn__EXPERIMENTAL,
|
||||||
$insertTableRow__EXPERIMENTAL,
|
$insertTableRow__EXPERIMENTAL,
|
||||||
$isTableNode, $isTableRowNode, $isTableSelection, $unmergeCell, TableCellNode,
|
$isTableNode, $isTableSelection, $unmergeCell, TableCellNode,
|
||||||
} from "@lexical/table";
|
} from "@lexical/table";
|
||||||
import {$getNodeFromSelection, $selectionContainsNodeType} from "../../../utils/selection";
|
import {$getNodeFromSelection, $selectionContainsNodeType} from "../../../utils/selection";
|
||||||
import {$getParentOfType} from "../../../utils/nodes";
|
import {$getParentOfType} from "../../../utils/nodes";
|
||||||
import {$isCustomTableCellNode} from "../../../nodes/custom-table-cell";
|
import {$isCustomTableCellNode} from "../../../nodes/custom-table-cell";
|
||||||
import {$showCellPropertiesForm, $showRowPropertiesForm} from "../forms/tables";
|
import {$showCellPropertiesForm, $showRowPropertiesForm, $showTablePropertiesForm} from "../forms/tables";
|
||||||
import {$getTableRowsFromSelection, $mergeTableCellsInSelection} from "../../../utils/tables";
|
import {
|
||||||
|
$clearTableFormatting,
|
||||||
|
$clearTableSizes, $getTableFromSelection,
|
||||||
|
$getTableRowsFromSelection,
|
||||||
|
$mergeTableCellsInSelection
|
||||||
|
} from "../../../utils/tables";
|
||||||
import {$isCustomTableRowNode, CustomTableRowNode} from "../../../nodes/custom-table-row";
|
import {$isCustomTableRowNode, CustomTableRowNode} from "../../../nodes/custom-table-row";
|
||||||
import {NodeClipboard} from "../../../services/node-clipboard";
|
import {NodeClipboard} from "../../../services/node-clipboard";
|
||||||
import {r} from "@codemirror/legacy-modes/mode/r";
|
|
||||||
import {$generateHtmlFromNodes} from "@lexical/html";
|
|
||||||
|
|
||||||
const neverActive = (): boolean => false;
|
const neverActive = (): boolean => false;
|
||||||
const cellNotSelected = (selection: BaseSelection|null) => !$selectionContainsNodeType(selection, $isCustomTableCellNode);
|
const cellNotSelected = (selection: BaseSelection|null) => !$selectionContainsNodeType(selection, $isCustomTableCellNode);
|
||||||
@ -40,15 +43,10 @@ export const tableProperties: EditorButtonDefinition = {
|
|||||||
icon: tableIcon,
|
icon: tableIcon,
|
||||||
action(context: EditorUiContext) {
|
action(context: EditorUiContext) {
|
||||||
context.editor.getEditorState().read(() => {
|
context.editor.getEditorState().read(() => {
|
||||||
const cell = $getNodeFromSelection($getSelection(), $isCustomTableCellNode);
|
const table = $getTableFromSelection($getSelection());
|
||||||
if (!$isCustomTableCellNode(cell)) {
|
if ($isCustomTableNode(table)) {
|
||||||
return;
|
$showTablePropertiesForm(table, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
const table = $getParentOfType(cell, $isTableNode);
|
|
||||||
const modalForm = context.manager.createModal('table_properties');
|
|
||||||
modalForm.show({});
|
|
||||||
// TODO
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
isActive: neverActive,
|
isActive: neverActive,
|
||||||
@ -59,14 +57,16 @@ export const clearTableFormatting: EditorButtonDefinition = {
|
|||||||
label: 'Clear table formatting',
|
label: 'Clear table formatting',
|
||||||
format: 'long',
|
format: 'long',
|
||||||
action(context: EditorUiContext) {
|
action(context: EditorUiContext) {
|
||||||
context.editor.getEditorState().read(() => {
|
context.editor.update(() => {
|
||||||
const cell = $getNodeFromSelection($getSelection(), $isCustomTableCellNode);
|
const cell = $getNodeFromSelection($getSelection(), $isCustomTableCellNode);
|
||||||
if (!$isCustomTableCellNode(cell)) {
|
if (!$isCustomTableCellNode(cell)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const table = $getParentOfType(cell, $isTableNode);
|
const table = $getParentOfType(cell, $isTableNode);
|
||||||
// TODO
|
if ($isCustomTableNode(table)) {
|
||||||
|
$clearTableFormatting(table);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
isActive: neverActive,
|
isActive: neverActive,
|
||||||
@ -77,22 +77,15 @@ export const resizeTableToContents: EditorButtonDefinition = {
|
|||||||
label: 'Resize to contents',
|
label: 'Resize to contents',
|
||||||
format: 'long',
|
format: 'long',
|
||||||
action(context: EditorUiContext) {
|
action(context: EditorUiContext) {
|
||||||
context.editor.getEditorState().read(() => {
|
context.editor.update(() => {
|
||||||
const cell = $getNodeFromSelection($getSelection(), $isCustomTableCellNode);
|
const cell = $getNodeFromSelection($getSelection(), $isCustomTableCellNode);
|
||||||
if (!$isCustomTableCellNode(cell)) {
|
if (!$isCustomTableCellNode(cell)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const table = $getParentOfType(cell, $isCustomTableNode);
|
const table = $getParentOfType(cell, $isCustomTableNode);
|
||||||
if (!$isCustomTableNode(table)) {
|
if ($isCustomTableNode(table)) {
|
||||||
return;
|
$clearTableSizes(table);
|
||||||
}
|
|
||||||
|
|
||||||
for (const row of table.getChildren()) {
|
|
||||||
if ($isTableRowNode(row)) {
|
|
||||||
// TODO - Come back later as this may depend on if we
|
|
||||||
// are using a custom table row
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -165,14 +158,9 @@ export const rowProperties: EditorButtonDefinition = {
|
|||||||
format: 'long',
|
format: 'long',
|
||||||
action(context: EditorUiContext) {
|
action(context: EditorUiContext) {
|
||||||
context.editor.getEditorState().read(() => {
|
context.editor.getEditorState().read(() => {
|
||||||
const cell = $getNodeFromSelection($getSelection(), $isCustomTableCellNode);
|
const rows = $getTableRowsFromSelection($getSelection());
|
||||||
if (!$isCustomTableCellNode(cell)) {
|
if ($isCustomTableRowNode(rows[0])) {
|
||||||
return;
|
$showRowPropertiesForm(rows[0], context);
|
||||||
}
|
|
||||||
|
|
||||||
const row = $getParentOfType(cell, $isCustomTableRowNode);
|
|
||||||
if ($isCustomTableRowNode(row)) {
|
|
||||||
$showRowPropertiesForm(row, context);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -9,13 +9,15 @@ import {CustomTableCellNode} from "../../../nodes/custom-table-cell";
|
|||||||
import {EditorFormModal} from "../../framework/modals";
|
import {EditorFormModal} from "../../framework/modals";
|
||||||
import {$getSelection, ElementFormatType} from "lexical";
|
import {$getSelection, ElementFormatType} from "lexical";
|
||||||
import {
|
import {
|
||||||
|
$forEachTableCell, $getCellPaddingForTable,
|
||||||
$getTableCellColumnWidth,
|
$getTableCellColumnWidth,
|
||||||
$getTableCellsFromSelection,
|
$getTableCellsFromSelection, $getTableFromSelection,
|
||||||
$getTableRowsFromSelection,
|
$getTableRowsFromSelection,
|
||||||
$setTableCellColumnWidth
|
$setTableCellColumnWidth
|
||||||
} from "../../../utils/tables";
|
} from "../../../utils/tables";
|
||||||
import {formatSizeValue} from "../../../utils/dom";
|
import {formatSizeValue} from "../../../utils/dom";
|
||||||
import {CustomTableRowNode} from "../../../nodes/custom-table-row";
|
import {CustomTableRowNode} from "../../../nodes/custom-table-row";
|
||||||
|
import {CustomTableNode} from "../../../nodes/custom-table";
|
||||||
|
|
||||||
const borderStyleInput: EditorSelectFormFieldDefinition = {
|
const borderStyleInput: EditorSelectFormFieldDefinition = {
|
||||||
label: 'Border style',
|
label: 'Border style',
|
||||||
@ -213,10 +215,58 @@ export const rowProperties: EditorFormDefinition = {
|
|||||||
backgroundColorInput, // style on tr: height
|
backgroundColorInput, // style on tr: height
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function $showTablePropertiesForm(table: CustomTableNode, context: EditorUiContext): EditorFormModal {
|
||||||
|
const styles = table.getStyles();
|
||||||
|
const modalForm = context.manager.createModal('table_properties');
|
||||||
|
modalForm.show({
|
||||||
|
width: styles.get('width') || '',
|
||||||
|
height: styles.get('height') || '',
|
||||||
|
cell_spacing: styles.get('cell-spacing') || '',
|
||||||
|
cell_padding: $getCellPaddingForTable(table),
|
||||||
|
border_width: styles.get('border-width') || '',
|
||||||
|
border_style: styles.get('border-style') || '',
|
||||||
|
border_color: styles.get('border-color') || '',
|
||||||
|
background_color: styles.get('background-color') || '',
|
||||||
|
// caption: '', TODO
|
||||||
|
align: table.getFormatType(),
|
||||||
|
});
|
||||||
|
return modalForm;
|
||||||
|
}
|
||||||
|
|
||||||
export const tableProperties: EditorFormDefinition = {
|
export const tableProperties: EditorFormDefinition = {
|
||||||
submitText: 'Save',
|
submitText: 'Save',
|
||||||
async action(formData, context: EditorUiContext) {
|
async action(formData, context: EditorUiContext) {
|
||||||
// TODO
|
context.editor.update(() => {
|
||||||
|
const table = $getTableFromSelection($getSelection());
|
||||||
|
if (!table) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = table.getStyles();
|
||||||
|
styles.set('width', formatSizeValue(formData.get('width')?.toString() || ''));
|
||||||
|
styles.set('height', formatSizeValue(formData.get('height')?.toString() || ''));
|
||||||
|
styles.set('cell-spacing', formatSizeValue(formData.get('cell_spacing')?.toString() || ''));
|
||||||
|
styles.set('border-width', formatSizeValue(formData.get('border_width')?.toString() || ''));
|
||||||
|
styles.set('border-style', formData.get('border_style')?.toString() || '');
|
||||||
|
styles.set('border-color', formData.get('border_color')?.toString() || '');
|
||||||
|
styles.set('background-color', formData.get('background_color')?.toString() || '');
|
||||||
|
table.setStyles(styles);
|
||||||
|
|
||||||
|
table.setFormat(formData.get('align') as ElementFormatType);
|
||||||
|
|
||||||
|
const cellPadding = (formData.get('cell_padding')?.toString() || '');
|
||||||
|
if (cellPadding) {
|
||||||
|
const cellPaddingFormatted = formatSizeValue(cellPadding);
|
||||||
|
$forEachTableCell(table, (cell: CustomTableCellNode) => {
|
||||||
|
const styles = cell.getStyles();
|
||||||
|
styles.set('padding', cellPaddingFormatted);
|
||||||
|
cell.setStyles(styles);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO - cell caption
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
fields: [
|
fields: [
|
||||||
@ -224,42 +274,42 @@ export const tableProperties: EditorFormDefinition = {
|
|||||||
build() {
|
build() {
|
||||||
const generalFields: EditorFormFieldDefinition[] = [
|
const generalFields: EditorFormFieldDefinition[] = [
|
||||||
{
|
{
|
||||||
label: 'Width',
|
label: 'Width', // Style - width
|
||||||
name: 'width',
|
name: 'width',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Height',
|
label: 'Height', // Style - height
|
||||||
name: 'height',
|
name: 'height',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Cell spacing',
|
label: 'Cell spacing', // Style - border-spacing
|
||||||
name: 'cell_spacing',
|
name: 'cell_spacing',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Cell padding',
|
label: 'Cell padding', // Style - padding on child cells?
|
||||||
name: 'cell_padding',
|
name: 'cell_padding',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Border width',
|
label: 'Border width', // Style - border-width
|
||||||
name: 'border_width',
|
name: 'border_width',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'caption',
|
label: 'caption', // Caption element
|
||||||
name: 'height',
|
name: 'caption',
|
||||||
type: 'text', // TODO -
|
type: 'text', // TODO -
|
||||||
},
|
},
|
||||||
alignmentInput,
|
alignmentInput, // alignment class
|
||||||
];
|
];
|
||||||
|
|
||||||
const advancedFields: EditorFormFieldDefinition[] = [
|
const advancedFields: EditorFormFieldDefinition[] = [
|
||||||
borderStyleInput,
|
borderStyleInput, // Style - border-style
|
||||||
borderColorInput,
|
borderColorInput, // Style - border-color
|
||||||
backgroundColorInput,
|
backgroundColorInput, // Style - background-color
|
||||||
];
|
];
|
||||||
|
|
||||||
return new EditorFormTabs([
|
return new EditorFormTabs([
|
||||||
|
@ -30,3 +30,28 @@ export function formatSizeValue(size: number | string, defaultSuffix: string = '
|
|||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type StyleMap = Map<string, string>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a map from an element's styles.
|
||||||
|
* Uses direct attribute value string handling since attempting to iterate
|
||||||
|
* over .style will expand out any shorthand properties (like 'padding') making
|
||||||
|
* rather than being representative of the actual properties set.
|
||||||
|
*/
|
||||||
|
export function extractStyleMapFromElement(element: HTMLElement): StyleMap {
|
||||||
|
const map: StyleMap = new Map();
|
||||||
|
const styleText= element.getAttribute('style') || '';
|
||||||
|
|
||||||
|
const rules = styleText.split(';');
|
||||||
|
for (const rule of rules) {
|
||||||
|
const [name, value] = rule.split(':');
|
||||||
|
if (!name || !value) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
map.set(name.trim().toLowerCase(), value.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
@ -1,11 +0,0 @@
|
|||||||
|
|
||||||
export type StyleMap = Map<string, string>;
|
|
||||||
|
|
||||||
export function createStyleMapFromDomStyles(domStyles: CSSStyleDeclaration): StyleMap {
|
|
||||||
const styleMap: StyleMap = new Map();
|
|
||||||
const styleNames: string[] = Array.from(domStyles);
|
|
||||||
for (const style of styleNames) {
|
|
||||||
styleMap.set(style, domStyles.getPropertyValue(style));
|
|
||||||
}
|
|
||||||
return styleMap;
|
|
||||||
}
|
|
@ -206,8 +206,107 @@ export function $getTableRowsFromSelection(selection: BaseSelection|null): Custo
|
|||||||
return Object.values(rowsByKey);
|
return Object.values(rowsByKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function $getTableFromSelection(selection: BaseSelection|null): CustomTableNode|null {
|
||||||
|
const cells = $getTableCellsFromSelection(selection);
|
||||||
|
if (cells.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const table = $getParentOfType(cells[0], $isCustomTableNode);
|
||||||
|
if ($isCustomTableNode(table)) {
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function $clearTableSizes(table: CustomTableNode): void {
|
||||||
|
table.setColWidths([]);
|
||||||
|
|
||||||
|
// TODO - Extra form things once table properties and extra things
|
||||||
|
// are supported
|
||||||
|
|
||||||
|
for (const row of table.getChildren()) {
|
||||||
|
if (!$isCustomTableRowNode(row)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rowStyles = row.getStyles();
|
||||||
|
rowStyles.delete('height');
|
||||||
|
rowStyles.delete('width');
|
||||||
|
row.setStyles(rowStyles);
|
||||||
|
|
||||||
|
const cells = row.getChildren().filter(c => $isCustomTableCellNode(c));
|
||||||
|
for (const cell of cells) {
|
||||||
|
const cellStyles = cell.getStyles();
|
||||||
|
cellStyles.delete('height');
|
||||||
|
cellStyles.delete('width');
|
||||||
|
cell.setStyles(cellStyles);
|
||||||
|
cell.clearWidth();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function $clearTableFormatting(table: CustomTableNode): void {
|
||||||
|
table.setColWidths([]);
|
||||||
|
table.setStyles(new Map);
|
||||||
|
|
||||||
|
for (const row of table.getChildren()) {
|
||||||
|
if (!$isCustomTableRowNode(row)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
row.setStyles(new Map);
|
||||||
|
row.setFormat('');
|
||||||
|
|
||||||
|
const cells = row.getChildren().filter(c => $isCustomTableCellNode(c));
|
||||||
|
for (const cell of cells) {
|
||||||
|
cell.setStyles(new Map);
|
||||||
|
cell.clearWidth();
|
||||||
|
cell.setFormat('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform the given callback for each cell in the given table.
|
||||||
|
* Returning false from the callback stops the function early.
|
||||||
|
*/
|
||||||
|
export function $forEachTableCell(table: CustomTableNode, callback: (c: CustomTableCellNode) => void|false): void {
|
||||||
|
outer: for (const row of table.getChildren()) {
|
||||||
|
if (!$isCustomTableRowNode(row)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const cells = row.getChildren();
|
||||||
|
for (const cell of cells) {
|
||||||
|
if (!$isCustomTableCellNode(cell)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const result = callback(cell);
|
||||||
|
if (result === false) {
|
||||||
|
break outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function $getCellPaddingForTable(table: CustomTableNode): string {
|
||||||
|
let padding: string|null = null;
|
||||||
|
|
||||||
|
$forEachTableCell(table, (cell: CustomTableCellNode) => {
|
||||||
|
const cellPadding = cell.getStyles().get('padding') || ''
|
||||||
|
if (padding === null) {
|
||||||
|
padding = cellPadding;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cellPadding !== padding) {
|
||||||
|
padding = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return padding || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user