Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"@angular/router": "^19.2.6",
"@angular/ssr": "^19.2.7",
"express": "^4.18.2",
"prosemirror-tables": "^1.7.1",
"rxjs": "~7.8.0",
"tslib": "^2.3.1",
"zone.js": "~0.15.0"
Expand Down
32 changes: 32 additions & 0 deletions projects/ngx-editor/schema/nodes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { DOMOutputSpec, Node as ProseMirrorNode, NodeSpec } from 'prosemirror-model';
import { tableNodes } from 'prosemirror-tables';
import * as sl from 'prosemirror-schema-list';

import { toStyleString } from 'ngx-editor/utils';
Expand Down Expand Up @@ -291,6 +292,36 @@ export const image: NodeSpec = {
},
};


const tableNodeSpecs = tableNodes({
tableGroup: 'block',
cellContent: 'block+',
cellAttributes: {
background: {
default: null,
getFromDOM(dom: HTMLElement) {
return (dom.style && dom.style.backgroundColor) || null;
},
setDOMAttr(value: unknown, attrs: Record<string, any>) {
if (typeof value === 'string' && value) {
attrs['style'] = (attrs['style'] || '') + `background-color: ${value};`;
}
},
},
colwidth: {
default: null,
getFromDOM(dom: HTMLElement) {
return dom.dataset['colwidth'] ? dom.dataset['colwidth'].split(',').map(Number) : null;
},
setDOMAttr(value: unknown, attrs: Record<string, any>) {
if (Array.isArray(value)) {
attrs['data-colwidth'] = value.join(',');
}
},
},
},
});

const listItem = {
...sl.listItem,
content: 'paragraph block*',
Expand Down Expand Up @@ -318,6 +349,7 @@ const nodes = {
hard_break: hardBreak,
code_block: codeBlock,
image,
...tableNodeSpecs,
list_item: listItem,
ordered_list: orderedList,
bullet_list: bulletList,
Expand Down
23 changes: 23 additions & 0 deletions projects/ngx-editor/src/lib/Locals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const defaults: Record<string, string | Observable<string>> = {
insertLink: 'Insert Link',
removeLink: 'Remove Link',
insertImage: 'Insert Image',
insertTable: 'Insert Table',
indent: 'Increase Indent',
outdent: 'Decrease Indent',
superscript: 'Superscript',
Expand All @@ -44,6 +45,28 @@ export const defaults: Record<string, string | Observable<string>> = {
title: 'Title',
remove: 'Remove',
enterValidUrl: 'Please enter a valid URL',
rows: 'Number of Rows',
rowsRequired: 'Rows field is required.',
rowsMin: 'Minimum number of rows must be 1.',
cols: 'Number of Columns',
colsRequired: 'Columns field is required.',
colsMin: 'Minimum number of columns must be 1.',
// Table actions
table:'Table',
addColumnBefore: 'Insert Column Before',
addColumnAfter: 'Insert Column After',
deleteColumn: 'Delete Column',
addRowBefore: 'Insert Row Before',
addRowAfter: 'Insert Row After',
deleteRow: 'Delete Row',
deleteTable: 'Delete Table',
mergeCells: 'Merge Cells',
splitCell: 'Split Cell',
toggleHeaderRow: 'Toggle Header Row',
toggleHeaderColumn: 'Toggle Header Column',
toggleHeaderCell: 'Toggle Header Cells',
setCellBackgroundGreen: 'Make Cell Green',
clearCellBackground: 'Clear Cell Background',
required: 'This is required',
};

Expand Down
3 changes: 3 additions & 0 deletions projects/ngx-editor/src/lib/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import TextColor from './TextColor';
import FormatClear from './FormatClear';
import Indent from './Indent';
import History from './History';
import Table from './table';


export const STRONG = new Mark('strong');
export const EM = new Mark('em');
Expand All @@ -33,6 +35,7 @@ export const ALIGN_RIGHT = new TextAlign('right');
export const ALIGN_JUSTIFY = new TextAlign('justify');
export const LINK = new Link();
export const IMAGE = new Image();
export const TABLE = new Table();
export const TEXT_COLOR = new TextColor('text_color', 'color');
export const TEXT_BACKGROUND_COLOR = new TextColor('text_background_color', 'backgroundColor');
export const INDENT = new Indent('increase');
Expand Down
55 changes: 55 additions & 0 deletions projects/ngx-editor/src/lib/commands/table.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { type EditorState, NodeSelection, type Command } from 'prosemirror-state';
import { Node as ProseMirrorNode } from 'prosemirror-model';
import { Dispatch } from './types';

class Table {
insert(rows: number, cols: number): Command {
return (state: EditorState, dispatch?: Dispatch): boolean => {
const { schema, tr } = state;
const tableType = schema.nodes['table'];
const rowType = schema.nodes['table_row'];
const cellType = schema.nodes['table_cell'];
const headerType = schema.nodes['table_header'];

const tableRows: ProseMirrorNode[] = [];

const headerAttrs = {
colspan: cols,
};

const headerText = schema.text('Header');
const paragraph = schema.nodes['paragraph'].create(null, headerText);
const headerCell = headerType.create(headerAttrs, paragraph);

tableRows.push(rowType.create(null, [headerCell]));

for (let i = 0; i < rows; i++) {
const cells: ProseMirrorNode[] = [];
for (let j = 0; j < cols; j++) {
cells.push(cellType.createAndFill(null));
}
tableRows.push(rowType.create(null, cells));
}

const table = tableType.create(null, tableRows);
tr.replaceSelectionWith(table).scrollIntoView();

if (tr.docChanged) {
dispatch?.(tr);
return true;
}

return false;
};
}

isActive(state: EditorState): boolean {
const { selection } = state;
if (selection instanceof NodeSelection) {
return selection.node.type.name === 'table';
}
return false;
}
}

export default Table;
15 changes: 14 additions & 1 deletion projects/ngx-editor/src/lib/defaultPlugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
inputRules, wrappingInputRule, textblockTypeInputRule,
smartQuotes, emDash, ellipsis, InputRule,
} from 'prosemirror-inputrules';

import { columnResizing, tableEditing, goToNextCell } from 'prosemirror-tables';
import { markInputRule } from 'ngx-editor/helpers';

interface Options {
Expand Down Expand Up @@ -137,6 +137,15 @@ export const getKeyboardShortcuts = (schema: Schema, options: ShortcutOptions) =
keymap(baseKeymap),
];

if (schema.nodes['table']) {
plugins.push(
keymap({
'Tab': goToNextCell(1),
'Shift-Tab': goToNextCell(-1),
})
);
}

if (options.history) {
plugins.push(keymap(historyKeyMap));
}
Expand All @@ -158,6 +167,10 @@ const getDefaultPlugins = (schema: Schema, options: Options): Plugin[] => {
if (options.inputRules) {
plugins.push(buildInputRules(schema));
}

if (schema.nodes['table']) {
plugins.push(columnResizing(), tableEditing());
}

return plugins;
};
Expand Down
63 changes: 61 additions & 2 deletions projects/ngx-editor/src/lib/editor.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ $pointer-style: var(--ngx-editor-click-pointer, default);
}

.NgxEditor__Dropdown {
min-width: 64px;
width: fit-content;
position: relative;
display: flex;
align-items: center;
Expand All @@ -227,6 +227,7 @@ $pointer-style: var(--ngx-editor-click-pointer, default);
padding: $menubar-text-padding;
height: 100%;
width: 100%;
max-width: none;

&:focus-visible {
outline: 1px solid $focus-ring-color;
Expand All @@ -251,7 +252,8 @@ $pointer-style: var(--ngx-editor-click-pointer, default);
border-radius: $popup-border-radius;
background-color: $popup-bg-color;
z-index: 10;
width: 100%;
width: max-content;
min-width: 100%;
top: calc(#{$menubar-height} + 2px);
display: flex;
flex-direction: column;
Expand All @@ -262,6 +264,10 @@ $pointer-style: var(--ngx-editor-click-pointer, default);
padding: 8px;
white-space: nowrap;
color: inherit;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
box-sizing: border-box;

&:focus-visible {
outline: 1px solid $focus-ring-color;
Expand Down Expand Up @@ -352,3 +358,56 @@ $pointer-style: var(--ngx-editor-click-pointer, default);
color: $error-color;
}
}

.ProseMirror {
table {
margin: 0;
border-collapse: collapse;
table-layout: fixed;
width: 100%;
overflow: hidden;
border: 1px solid $editor-border-color;
}
th, td {
min-width: 1em;
border: 1px solid $editor-border-color;
padding: 3px 5px;
vertical-align: top;
box-sizing: border-box;
position: relative;
}
.tableWrapper {
margin: 1em 0;
overflow-x: auto;
}
th {
font-weight: bold;
text-align: left;
background-color: #f5f5f5;
}
.column-resize-handle {
position: absolute;
right: -2px;
top: 0;
bottom: 0;
width: 4px;
z-index: 20;
background-color: #adf;
pointer-events: none;
}
&.resize-cursor {
cursor: ew-resize;
cursor: col-resize;
}
.selectedCell:after {
z-index: 2;
position: absolute;
content: "";
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(200, 200, 255, 0.4);
pointer-events: none;
}
}
2 changes: 2 additions & 0 deletions projects/ngx-editor/src/lib/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import quote from './quote';
import link from './link';
import unlink from './unlink';
import image from './image';
import table from './table';
import alignLeft from './align_left';
import alignCenter from './align_center';
import alignRight from './align_right';
Expand Down Expand Up @@ -42,6 +43,7 @@ export const icons: Record<string, string> = {
link,
unlink,
image,
table,
align_left: alignLeft,
align_center: alignCenter,
align_right: alignRight,
Expand Down
Loading