Skip to content

Commit 68c3fcf

Browse files
DavidAkkermanjohn-traas
authored andcommitted
feat(text-editor tables): add basic plugin for parsing tables
1 parent 785d6cd commit 68c3fcf

File tree

4 files changed

+109
-0
lines changed

4 files changed

+109
-0
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { Component, h, State } from '@stencil/core';
2+
/**
3+
* Text editor with tables (HTML mode only).
4+
*
5+
* When using the text editor in HTML mode, it is possible to paste and
6+
* display tables in the text editor.
7+
* Basic interaction with the table is supported, but you cannot do
8+
* complex operations
9+
*/
10+
@Component({
11+
tag: 'limel-example-text-editor-with-tables',
12+
shadow: true,
13+
})
14+
export class TextEditorWithTablesExample {
15+
@State()
16+
private value: string =
17+
'<table><tbody><tr><td style="background-color: rgb(25, 107, 36);color: white;"><p><strong>Column1</strong></p></td><td style="background-color: rgb(25, 107, 36);color: white;"><p><strong>Column2</strong></p></td></tr><tr><td style="background-color: rgb(193, 240, 200);color: black;"><p>Cell A1</p></td><td style="background-color: rgb(193, 240, 200);color: black;"><p>Cell B1</p></td></tr><tr><td style="color: black;"><p>Cell A2</p></td><td style="background-color: yellow;color: red;"><p>Cell B2</p></td></tr></tbody></table>';
18+
19+
@State()
20+
private readonly = false;
21+
22+
public render() {
23+
return [
24+
<limel-text-editor
25+
value={this.value}
26+
onChange={this.handleChange}
27+
readonly={this.readonly}
28+
contentType="html"
29+
enableTables={true}
30+
/>,
31+
<limel-example-controls>
32+
<limel-checkbox
33+
checked={this.readonly}
34+
label="Readonly"
35+
onChange={this.setReadonly}
36+
/>
37+
</limel-example-controls>,
38+
<limel-example-value value={this.value} />,
39+
];
40+
}
41+
42+
private setReadonly = (event: CustomEvent<boolean>) => {
43+
event.stopPropagation();
44+
this.readonly = event.detail;
45+
};
46+
47+
private handleChange = (event: CustomEvent<string>) => {
48+
this.value = event.detail;
49+
};
50+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { tableNodes, tableEditing } from 'prosemirror-tables';
2+
import { Plugin } from 'prosemirror-state';
3+
4+
export const getTableEditingPlugins = (tablesEnabled: boolean): Plugin[] => {
5+
if (tablesEnabled) {
6+
return [tableEditing()];
7+
}
8+
9+
return [];
10+
};
11+
12+
export const getTableNodes = () => {
13+
return tableNodes({
14+
tableGroup: 'block',
15+
cellContent: 'block+',
16+
cellAttributes: {
17+
background: {
18+
default: null,
19+
getFromDOM: (dom) => {
20+
return dom.style.backgroundColor || null;
21+
},
22+
setDOMAttr: (value: string, attrs: Record<string, string>) => {
23+
if (value) {
24+
attrs.style =
25+
(attrs.style || '') + `background-color: ${value};`;
26+
}
27+
},
28+
},
29+
color: {
30+
default: null,
31+
getFromDOM: (dom) => {
32+
return dom.style.color || null;
33+
},
34+
setDOMAttr: (value: string, attrs: Record<string, string>) => {
35+
if (value) {
36+
attrs.style = (attrs.style || '') + `color: ${value};`;
37+
}
38+
},
39+
},
40+
},
41+
});
42+
};

src/components/text-editor/prosemirror-adapter/prosemirror-adapter.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import { CustomElementDefinition } from '../../../global/shared-types/custom-ele
4444
import { createNodeSpec } from '../utils/plugin-factory';
4545
import { createTriggerPlugin } from './plugins/trigger/factory';
4646
import { TriggerCharacter } from '../text-editor.types';
47+
import { getTableNodes, getTableEditingPlugins } from './plugins/table-plugin';
4748

4849
const DEBOUNCE_TIMEOUT = 300;
4950

@@ -100,6 +101,9 @@ export class ProsemirrorAdapter {
100101
@Prop()
101102
triggerCharacters: TriggerCharacter[] = [];
102103

104+
@Prop()
105+
public supportTables: boolean = false;
106+
103107
@Element()
104108
private host: HTMLLimelTextEditorElement;
105109

@@ -304,6 +308,10 @@ export class ProsemirrorAdapter {
304308
});
305309
nodes = addListNodes(nodes, 'paragraph block*', 'block');
306310

311+
if (this.supportTables) {
312+
nodes = nodes.append(getTableNodes());
313+
}
314+
307315
return new Schema({
308316
nodes: nodes,
309317
marks: schema.spec.marks.append({
@@ -343,6 +351,7 @@ export class ProsemirrorAdapter {
343351
this.updateActiveActionBarItems,
344352
),
345353
createActionBarInteractionPlugin(this.menuCommandFactory),
354+
...getTableEditingPlugins(this.supportTables),
346355
],
347356
});
348357
}

src/components/text-editor/text-editor.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { TriggerCharacter, TriggerEventDetail } from './text-editor.types';
1818
* @exampleComponent limel-example-text-editor-as-form-component
1919
* @exampleComponent limel-example-text-editor-with-markdown
2020
* @exampleComponent limel-example-text-editor-with-html
21+
* @exampleComponent limel-example-text-editor-with-tables
2122
* @exampleComponent limel-example-text-editor-allow-resize
2223
* @exampleComponent limel-example-text-editor-size
2324
* @exampleComponent limel-example-text-editor-ui
@@ -153,6 +154,12 @@ export class TextEditor implements FormComponent<string> {
153154
@Prop({ reflect: true })
154155
public ui?: 'standard' | 'minimal' = 'standard';
155156

157+
/**
158+
* Set to `true` to allow parsing of table data. Only works when `type` is `html`.
159+
*/
160+
@Prop({ reflect: true })
161+
public enableTables?: boolean;
162+
156163
/**
157164
* Dispatched when a change is made to the editor
158165
*/
@@ -246,6 +253,7 @@ export class TextEditor implements FormComponent<string> {
246253
aria-disabled={this.disabled}
247254
language={this.language}
248255
triggerCharacters={this.triggers}
256+
supportTables={this.enableTables}
249257
/>,
250258
this.renderPlaceholder(),
251259
this.renderHelperLine(),

0 commit comments

Comments
 (0)