@milkdown/preset-gfm 5.3.1 → 5.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/lib/auto-link.d.ts +1 -15
  2. package/lib/auto-link.d.ts.map +1 -1
  3. package/lib/index.d.ts +2 -2
  4. package/lib/index.d.ts.map +1 -1
  5. package/lib/index.es.js +768 -16
  6. package/lib/index.es.js.map +1 -1
  7. package/lib/supported-keys.d.ts.map +1 -1
  8. package/lib/table/command.d.ts +3 -0
  9. package/lib/table/command.d.ts.map +1 -0
  10. package/lib/table/index.d.ts +10 -0
  11. package/lib/table/index.d.ts.map +1 -0
  12. package/lib/table/nodes/index.d.ts +32 -0
  13. package/lib/table/nodes/index.d.ts.map +1 -0
  14. package/lib/table/nodes/schema.d.ts +2 -0
  15. package/lib/table/nodes/schema.d.ts.map +1 -0
  16. package/lib/table/nodes/style.d.ts +3 -0
  17. package/lib/table/nodes/style.d.ts.map +1 -0
  18. package/lib/table/operator-plugin/actions.d.ts +19 -0
  19. package/lib/table/operator-plugin/actions.d.ts.map +1 -0
  20. package/lib/table/operator-plugin/calc-pos.d.ts +3 -0
  21. package/lib/table/operator-plugin/calc-pos.d.ts.map +1 -0
  22. package/lib/table/operator-plugin/constant.d.ts +6 -0
  23. package/lib/table/operator-plugin/constant.d.ts.map +1 -0
  24. package/lib/table/operator-plugin/helper.d.ts +6 -0
  25. package/lib/table/operator-plugin/helper.d.ts.map +1 -0
  26. package/lib/table/operator-plugin/index.d.ts +6 -0
  27. package/lib/table/operator-plugin/index.d.ts.map +1 -0
  28. package/lib/table/operator-plugin/style.d.ts +3 -0
  29. package/lib/table/operator-plugin/style.d.ts.map +1 -0
  30. package/lib/table/operator-plugin/widget.d.ts +8 -0
  31. package/lib/table/operator-plugin/widget.d.ts.map +1 -0
  32. package/lib/table/utils.d.ts +20 -0
  33. package/lib/table/utils.d.ts.map +1 -0
  34. package/lib/task-list-item.d.ts.map +1 -1
  35. package/package.json +8 -15
  36. package/src/auto-link.ts +4 -9
  37. package/src/index.ts +13 -6
  38. package/src/supported-keys.ts +2 -1
  39. package/src/table/command.ts +16 -0
  40. package/src/table/index.ts +13 -0
  41. package/src/table/nodes/index.ts +169 -0
  42. package/src/table/nodes/schema.ts +16 -0
  43. package/src/table/nodes/style.ts +170 -0
  44. package/src/table/operator-plugin/actions.ts +115 -0
  45. package/src/table/operator-plugin/calc-pos.ts +25 -0
  46. package/src/table/operator-plugin/constant.ts +7 -0
  47. package/src/table/operator-plugin/helper.ts +38 -0
  48. package/src/table/operator-plugin/index.ts +98 -0
  49. package/src/table/operator-plugin/style.ts +51 -0
  50. package/src/table/operator-plugin/widget.ts +66 -0
  51. package/src/table/utils.ts +158 -0
  52. package/src/task-list-item.ts +2 -1
@@ -0,0 +1,169 @@
1
+ /* Copyright 2021, Milkdown by Mirone. */
2
+ import { createCmd, createCmdKey, MarkdownNode, schemaCtx } from '@milkdown/core';
3
+ import { columnResizing, goToNextCell, InputRule, Selection, tableEditing, TextSelection } from '@milkdown/prose';
4
+ import { createPlugin, createShortcut } from '@milkdown/utils';
5
+
6
+ import { exitTable } from '../command';
7
+ import { operatorPlugin } from '../operator-plugin';
8
+ import { createTable } from '../utils';
9
+ import { schema } from './schema';
10
+ import { injectStyle } from './style';
11
+
12
+ export const SupportedKeys = {
13
+ NextCell: 'NextCell',
14
+ PrevCell: 'PrevCell',
15
+ ExitTable: 'ExitTable',
16
+ } as const;
17
+ export type SupportedKeys = typeof SupportedKeys;
18
+
19
+ type Keys = keyof SupportedKeys;
20
+
21
+ export const PrevCell = createCmdKey();
22
+ export const NextCell = createCmdKey();
23
+ export const BreakTable = createCmdKey();
24
+ export const InsertTable = createCmdKey();
25
+
26
+ export const table = createPlugin<Keys, Record<string, unknown>, keyof typeof schema>((utils) => {
27
+ injectStyle(utils);
28
+ return {
29
+ schema: () => ({
30
+ node: {
31
+ table: {
32
+ ...schema.table,
33
+ parseMarkdown: {
34
+ match: (node) => node.type === 'table',
35
+ runner: (state, node, type) => {
36
+ const align = node.align as (string | null)[];
37
+ const children = (node.children as MarkdownNode[]).map((x, i) => ({
38
+ ...x,
39
+ align,
40
+ isHeader: i === 0,
41
+ }));
42
+ state.openNode(type);
43
+ state.next(children);
44
+ state.closeNode();
45
+ },
46
+ },
47
+ toMarkdown: {
48
+ match: (node) => node.type.name === 'table',
49
+ runner: (state, node) => {
50
+ const firstLine = node.content.firstChild?.content;
51
+ if (!firstLine) return;
52
+
53
+ const align: (string | null)[] = [];
54
+ firstLine.forEach((cell) => {
55
+ align.push(cell.attrs.alignment);
56
+ });
57
+ state.openNode('table', undefined, { align });
58
+ state.next(node.content);
59
+ state.closeNode();
60
+ },
61
+ },
62
+ },
63
+ table_row: {
64
+ ...schema.table_row,
65
+ parseMarkdown: {
66
+ match: (node) => node.type === 'tableRow',
67
+ runner: (state, node, type) => {
68
+ const align = node.align as (string | null)[];
69
+ const children = (node.children as MarkdownNode[]).map((x, i) => ({
70
+ ...x,
71
+ align: align[i],
72
+ isHeader: node.isHeader,
73
+ }));
74
+ state.openNode(type);
75
+ state.next(children);
76
+ state.closeNode();
77
+ },
78
+ },
79
+ toMarkdown: {
80
+ match: (node) => node.type.name === 'table_row',
81
+ runner: (state, node) => {
82
+ state.openNode('tableRow');
83
+ state.next(node.content);
84
+ state.closeNode();
85
+ },
86
+ },
87
+ },
88
+ table_cell: {
89
+ ...schema.table_cell,
90
+ parseMarkdown: {
91
+ match: (node) => node.type === 'tableCell' && !node.isHeader,
92
+ runner: (state, node, type) => {
93
+ const align = node.align as string;
94
+ state
95
+ .openNode(type, { alignment: align })
96
+ .openNode(state.schema.nodes.paragraph)
97
+ .next(node.children)
98
+ .closeNode()
99
+ .closeNode();
100
+ },
101
+ },
102
+ toMarkdown: {
103
+ match: (node) => node.type.name === 'table_cell',
104
+ runner: (state, node) => {
105
+ state.openNode('tableCell').next(node.content).closeNode();
106
+ },
107
+ },
108
+ },
109
+ table_header: {
110
+ ...schema.table_header,
111
+ parseMarkdown: {
112
+ match: (node) => node.type === 'tableCell' && !!node.isHeader,
113
+ runner: (state, node, type) => {
114
+ const align = node.align as string;
115
+ state.openNode(type, { alignment: align });
116
+ state.openNode(state.schema.nodes.paragraph);
117
+ state.next(node.children);
118
+ state.closeNode();
119
+ state.closeNode();
120
+ },
121
+ },
122
+ toMarkdown: {
123
+ match: (node) => node.type.name === 'table_header',
124
+ runner: (state, node) => {
125
+ state.openNode('tableCell');
126
+ state.next(node.content);
127
+ state.closeNode();
128
+ },
129
+ },
130
+ },
131
+ },
132
+ }),
133
+ inputRules: (nodeType, ctx) => [
134
+ new InputRule(/^\|\|\s$/, (state, _match, start, end) => {
135
+ const $start = state.doc.resolve(start);
136
+ if (!$start.node(-1).canReplaceWith($start.index(-1), $start.indexAfter(-1), nodeType.table))
137
+ return null;
138
+
139
+ const tableNode = createTable(ctx.get(schemaCtx));
140
+ const tr = state.tr.replaceRangeWith(start, end, tableNode).scrollIntoView();
141
+ return tr.setSelection(TextSelection.create(tr.doc, start + 3));
142
+ }),
143
+ ],
144
+ commands: (_, ctx) => [
145
+ createCmd(PrevCell, () => goToNextCell(-1)),
146
+ createCmd(NextCell, () => goToNextCell(1)),
147
+ createCmd(BreakTable, () => exitTable(ctx.get(schemaCtx).nodes.paragraph)),
148
+ createCmd(InsertTable, () => (state, dispatch) => {
149
+ const { selection, tr } = state;
150
+ const { from } = selection;
151
+ const table = createTable(ctx.get(schemaCtx));
152
+ const _tr = tr.replaceSelectionWith(table);
153
+ const sel = Selection.findFrom(_tr.doc.resolve(from), 1, true);
154
+ if (sel) {
155
+ dispatch?.(_tr.setSelection(sel));
156
+ }
157
+ return true;
158
+ }),
159
+ ],
160
+ shortcuts: {
161
+ [SupportedKeys.NextCell]: createShortcut(NextCell, 'Mod-]'),
162
+ [SupportedKeys.PrevCell]: createShortcut(PrevCell, 'Mod-['),
163
+ [SupportedKeys.ExitTable]: createShortcut(BreakTable, 'Mod-Enter'),
164
+ },
165
+ prosePlugins: (_, ctx) => {
166
+ return [operatorPlugin(ctx, utils), columnResizing({}), tableEditing()];
167
+ },
168
+ };
169
+ });
@@ -0,0 +1,16 @@
1
+ /* Copyright 2021, Milkdown by Mirone. */
2
+ import { tableNodes as tableNodesSpecCreator } from '@milkdown/prose';
3
+
4
+ export const schema = tableNodesSpecCreator({
5
+ tableGroup: 'block',
6
+ cellContent: 'paragraph',
7
+ cellAttributes: {
8
+ alignment: {
9
+ default: 'left',
10
+ getFromDOM: (dom) => (dom as HTMLElement).style.textAlign || 'left',
11
+ setDOMAttr: (value, attrs) => {
12
+ attrs.style = `text-align: ${value || 'left'}`;
13
+ },
14
+ },
15
+ },
16
+ });
@@ -0,0 +1,170 @@
1
+ /* Copyright 2021, Milkdown by Mirone. */
2
+ import { css, injectGlobal } from '@emotion/css';
3
+ import { Utils } from '@milkdown/utils';
4
+
5
+ const proseTableStyle = css`
6
+ /* copy from https://github.com/ProseMirror/prosemirror-tables/blob/master/style/tables.css */
7
+ .ProseMirror .tableWrapper {
8
+ overflow-x: auto;
9
+ }
10
+ .ProseMirror table {
11
+ border-collapse: collapse;
12
+ table-layout: fixed;
13
+ width: 100%;
14
+ overflow: hidden;
15
+ }
16
+ .ProseMirror td,
17
+ .ProseMirror th {
18
+ vertical-align: top;
19
+ box-sizing: border-box;
20
+ position: relative;
21
+ }
22
+ .ProseMirror .column-resize-handle {
23
+ position: absolute;
24
+ right: -2px;
25
+ top: 0;
26
+ bottom: 0;
27
+ width: 4px;
28
+ z-index: 20;
29
+ background-color: #adf;
30
+ pointer-events: none;
31
+ }
32
+ .ProseMirror.resize-cursor {
33
+ cursor: ew-resize;
34
+ cursor: col-resize;
35
+ }
36
+ /* Give selected cells a blue overlay */
37
+ .ProseMirror .selectedCell:after {
38
+ z-index: 2;
39
+ position: absolute;
40
+ content: '';
41
+ left: 0;
42
+ right: 0;
43
+ top: 0;
44
+ bottom: 0;
45
+ background: rgba(200, 200, 255, 0.4);
46
+ pointer-events: none;
47
+ }
48
+ `;
49
+
50
+ export const injectStyle = (utils: Utils) => {
51
+ const css = injectGlobal;
52
+ return utils.getStyle(({ size, palette, mixin }) => {
53
+ css`
54
+ ${proseTableStyle}
55
+
56
+ .tableWrapper {
57
+ margin: 0 !important;
58
+
59
+ ${mixin.scrollbar?.('x')};
60
+
61
+ width: 100%;
62
+
63
+ table {
64
+ width: calc(100% - 2rem) !important;
65
+ border-radius: ${size.radius};
66
+ box-sizing: border-box;
67
+ margin: 1rem 0 1rem 1rem !important;
68
+ overflow: auto !important;
69
+ * {
70
+ margin: 0 !important;
71
+ box-sizing: border-box;
72
+ font-size: 1rem;
73
+ }
74
+ tr {
75
+ ${mixin.border?.('bottom')};
76
+ }
77
+
78
+ th {
79
+ background: ${palette('background', 0.5)};
80
+ font-weight: 400;
81
+ }
82
+
83
+ th,
84
+ td {
85
+ min-width: 100px;
86
+ ${mixin.border?.()};
87
+ text-align: left;
88
+ position: relative;
89
+ line-height: 3rem;
90
+ box-sizing: border-box;
91
+ height: 3rem;
92
+ }
93
+
94
+ .selectedCell {
95
+ &::after {
96
+ background: ${palette('secondary', 0.38)};
97
+ }
98
+ & ::selection {
99
+ background: transparent;
100
+ }
101
+ }
102
+
103
+ .column-resize-handle {
104
+ background: ${palette('primary')};
105
+ width: ${size.lineWidth};
106
+ }
107
+
108
+ th,
109
+ td {
110
+ padding: 0 1rem;
111
+ p {
112
+ line-height: unset !important;
113
+ }
114
+ }
115
+
116
+ .milkdown-cell-left,
117
+ .milkdown-cell-point,
118
+ .milkdown-cell-top {
119
+ position: absolute;
120
+
121
+ &::after {
122
+ cursor: pointer;
123
+ position: absolute;
124
+ top: 0;
125
+ left: 0;
126
+ height: 100%;
127
+ width: 100%;
128
+ display: block;
129
+ transition: all 0.2s ease-in-out;
130
+ background: ${palette('secondary', 0.12)};
131
+ content: '';
132
+ }
133
+ &:hover::after {
134
+ background: ${palette('secondary', 0.38)};
135
+ }
136
+ }
137
+
138
+ .milkdown-cell-left {
139
+ left: calc(-6px - 0.5rem);
140
+ top: 0;
141
+ bottom: 0;
142
+ width: 0.5rem;
143
+ }
144
+
145
+ .milkdown-cell-top {
146
+ left: 0;
147
+ right: 0;
148
+ top: calc(-6px - 0.5rem);
149
+ height: 0.5rem;
150
+ }
151
+
152
+ .milkdown-cell-point {
153
+ left: calc(-2px - 1rem);
154
+ top: calc(-2px - 1rem);
155
+ width: 1rem;
156
+ height: 1rem;
157
+
158
+ .icon {
159
+ position: absolute;
160
+ top: 0;
161
+ bottom: 0;
162
+ left: 0;
163
+ right: 0;
164
+ }
165
+ }
166
+ }
167
+ }
168
+ `;
169
+ });
170
+ };
@@ -0,0 +1,115 @@
1
+ /* Copyright 2021, Milkdown by Mirone. */
2
+ import { Ctx, themeToolCtx } from '@milkdown/core';
3
+ import {
4
+ addColumnAfter,
5
+ addColumnBefore,
6
+ Command,
7
+ deleteColumn,
8
+ deleteRow,
9
+ deleteTable,
10
+ EditorView,
11
+ isInTable,
12
+ selectedRect,
13
+ setCellAttr,
14
+ } from '@milkdown/prose';
15
+
16
+ import { addRowWithAlignment } from '../utils';
17
+ import { getCellSelection, isFirstRowSelected } from './helper';
18
+
19
+ export type Item = {
20
+ $: HTMLElement;
21
+ command: (e: Event, view: EditorView) => Command;
22
+ disable?: (view: EditorView) => boolean;
23
+ };
24
+
25
+ export enum Action {
26
+ AddColLeft,
27
+ AddColRight,
28
+ AddRowTop,
29
+ AddRowBottom,
30
+ AlignLeft,
31
+ AlignCenter,
32
+ AlignRight,
33
+ Delete,
34
+ }
35
+
36
+ export const createActions: (ctx: Ctx) => Record<Action, Item> = (ctx) => ({
37
+ [Action.AddColLeft]: {
38
+ $: ctx.get(themeToolCtx).slots.icon('leftArrow'),
39
+ command: () => addColumnBefore,
40
+ disable: (view) => !getCellSelection(view).isColSelection(),
41
+ },
42
+ [Action.AddColRight]: {
43
+ $: ctx.get(themeToolCtx).slots.icon('rightArrow'),
44
+ command: () => addColumnAfter,
45
+ disable: (view) => !getCellSelection(view).isColSelection(),
46
+ },
47
+ [Action.AddRowTop]: {
48
+ $: ctx.get(themeToolCtx).slots.icon('upArrow'),
49
+ command: () => (state, dispatch) => {
50
+ if (!isInTable(state)) return false;
51
+ if (dispatch) {
52
+ const rect = selectedRect(state);
53
+ dispatch(addRowWithAlignment(state.tr, rect, rect.top));
54
+ }
55
+ return true;
56
+ },
57
+ disable: (view) =>
58
+ !getCellSelection(view).isRowSelection() ||
59
+ getCellSelection(view).$head.parent.type.name === 'table_header',
60
+ },
61
+ [Action.AddRowBottom]: {
62
+ $: ctx.get(themeToolCtx).slots.icon('downArrow'),
63
+ command: () => (state, dispatch) => {
64
+ if (!isInTable(state)) return false;
65
+ if (dispatch) {
66
+ const rect = selectedRect(state);
67
+ dispatch(addRowWithAlignment(state.tr, rect, rect.bottom));
68
+ }
69
+ return true;
70
+ },
71
+ disable: (view) => !getCellSelection(view).isRowSelection(),
72
+ },
73
+ [Action.AlignLeft]: {
74
+ $: ctx.get(themeToolCtx).slots.icon('alignLeft'),
75
+ command: () => setCellAttr('alignment', 'left'),
76
+ disable: (view) => !getCellSelection(view).isColSelection(),
77
+ },
78
+ [Action.AlignCenter]: {
79
+ $: ctx.get(themeToolCtx).slots.icon('alignCenter'),
80
+ command: () => setCellAttr('alignment', 'center'),
81
+ disable: (view) => !getCellSelection(view).isColSelection(),
82
+ },
83
+ [Action.AlignRight]: {
84
+ $: ctx.get(themeToolCtx).slots.icon('alignRight'),
85
+ command: () => setCellAttr('alignment', 'right'),
86
+ disable: (view) => !getCellSelection(view).isColSelection(),
87
+ },
88
+ [Action.Delete]: {
89
+ $: ctx.get(themeToolCtx).slots.icon('delete'),
90
+ command: (_, view) => {
91
+ const selection = getCellSelection(view);
92
+ const isCol = selection.isColSelection();
93
+ const isRow = selection.isRowSelection();
94
+ if (isCol && isRow) {
95
+ return deleteTable;
96
+ }
97
+
98
+ if (isCol) {
99
+ return deleteColumn;
100
+ }
101
+
102
+ return deleteRow;
103
+ },
104
+ disable: (view) => {
105
+ const selection = getCellSelection(view);
106
+ if (selection.isRowSelection()) {
107
+ if (selection.isColSelection()) {
108
+ return false;
109
+ }
110
+ return isFirstRowSelected(selection);
111
+ }
112
+ return false;
113
+ },
114
+ },
115
+ });
@@ -0,0 +1,25 @@
1
+ /* Copyright 2021, Milkdown by Mirone. */
2
+
3
+ import { calculateNodePosition, CellSelection, EditorView } from '@milkdown/prose';
4
+
5
+ export const calculatePosition = (view: EditorView, dom: HTMLElement) => {
6
+ const { selection } = view.state as unknown as { selection: CellSelection };
7
+ const isCol = selection.isColSelection();
8
+ const isRow = selection.isRowSelection();
9
+
10
+ calculateNodePosition(view, dom, (selected, target, parent) => {
11
+ const $editor = dom.parentElement;
12
+ if (!$editor) {
13
+ throw new Error();
14
+ }
15
+ let left = !isRow
16
+ ? selected.left - parent.left + (selected.width - target.width) / 2
17
+ : selected.left - parent.left - target.width / 2 - 8;
18
+ const top = selected.top - parent.top - target.height - (isCol ? 14 : 0) - 14 + $editor.scrollTop;
19
+
20
+ if (left < 0) {
21
+ left = 0;
22
+ }
23
+ return [top, left];
24
+ });
25
+ };
@@ -0,0 +1,7 @@
1
+ /* Copyright 2021, Milkdown by Mirone. */
2
+
3
+ export enum ToolTipPos {
4
+ Left = 'Left',
5
+ Top = 'Top',
6
+ Point = 'Point',
7
+ }
@@ -0,0 +1,38 @@
1
+ /* Copyright 2021, Milkdown by Mirone. */
2
+ import { CellSelection, EditorView, TableMap } from '@milkdown/prose';
3
+
4
+ import { Item } from './actions';
5
+
6
+ export const getCellSelection = (view: EditorView): CellSelection => view.state.selection as unknown as CellSelection;
7
+
8
+ export const isFirstRowSelected = (selection: CellSelection) => {
9
+ const map = TableMap.get(selection.$anchorCell.node(-1));
10
+ const start = selection.$anchorCell.start(-1);
11
+ const cells = map.cellsInRect({
12
+ left: 0,
13
+ right: map.width,
14
+ top: 0,
15
+ bottom: 1,
16
+ });
17
+ const selectedCells = map.cellsInRect(
18
+ map.rectBetween(selection.$anchorCell.pos - start, selection.$headCell.pos - start),
19
+ );
20
+
21
+ for (let i = 0, count = cells.length; i < count; i++) {
22
+ if (selectedCells.indexOf(cells[i]) === -1) {
23
+ return false;
24
+ }
25
+ }
26
+ return true;
27
+ };
28
+
29
+ export const calculateItem = (actions: Record<string, Item>, view: EditorView) => {
30
+ Object.values(actions).forEach((item) => {
31
+ const disable = item.disable?.(view);
32
+ if (disable) {
33
+ item.$.classList.add('hide');
34
+ return;
35
+ }
36
+ item.$.classList.remove('hide');
37
+ });
38
+ };
@@ -0,0 +1,98 @@
1
+ /* Copyright 2021, Milkdown by Mirone. */
2
+
3
+ import { Ctx } from '@milkdown/core';
4
+ import { CellSelection, Decoration, DecorationSet, Plugin, PluginKey } from '@milkdown/prose';
5
+ import { Utils } from '@milkdown/utils';
6
+
7
+ import { getCellsInColumn, getCellsInRow } from '../utils';
8
+ import { createActions } from './actions';
9
+ import { calculatePosition } from './calc-pos';
10
+ import { ToolTipPos } from './constant';
11
+ import { calculateItem } from './helper';
12
+ import { injectStyle } from './style';
13
+ import { createWidget } from './widget';
14
+
15
+ export const key = 'MILKDOWN_PLUGIN_TABLE';
16
+
17
+ export const operatorPlugin = (ctx: Ctx, utils: Utils) => {
18
+ const items = createActions(ctx);
19
+ const tooltip = document.createElement('div');
20
+ const style = utils.getStyle(injectStyle);
21
+ if (style) {
22
+ tooltip.classList.add(style);
23
+ }
24
+ tooltip.classList.add('table-tooltip', 'hide');
25
+
26
+ return new Plugin({
27
+ key: new PluginKey('MILKDOWN_TABLE_OP'),
28
+ props: {
29
+ decorations: (state) => {
30
+ const decorations: Decoration[] = [];
31
+ const leftCells = getCellsInColumn(0)(state.selection);
32
+ if (!leftCells) return null;
33
+ const topCells = getCellsInRow(0)(state.selection);
34
+ if (!topCells) return null;
35
+
36
+ const [topLeft] = leftCells;
37
+
38
+ decorations.push(createWidget(ctx, topLeft, ToolTipPos.Point));
39
+ leftCells.forEach((cell, i) => {
40
+ decorations.push(createWidget(ctx, cell, ToolTipPos.Left, i));
41
+ });
42
+ topCells.forEach((cell, i) => {
43
+ decorations.push(createWidget(ctx, cell, ToolTipPos.Top, i));
44
+ });
45
+
46
+ return DecorationSet.create(state.doc, decorations);
47
+ },
48
+ },
49
+ view: (editorView) => {
50
+ Object.values(items).forEach(({ $ }) => tooltip.appendChild($));
51
+ editorView.dom.parentNode?.appendChild(tooltip);
52
+
53
+ const listener = (e: Event) => {
54
+ if (!editorView) return;
55
+ e.stopPropagation();
56
+ e.preventDefault();
57
+ Object.values(items).forEach(({ $, command }) => {
58
+ if ($.contains(e.target as Element)) {
59
+ command(e, editorView)(editorView.state, editorView.dispatch, editorView);
60
+ }
61
+ });
62
+ };
63
+
64
+ const hide = () => {
65
+ tooltip.classList.add('hide');
66
+ };
67
+
68
+ tooltip.addEventListener('mousedown', listener);
69
+
70
+ return {
71
+ update: (view, prevState) => {
72
+ const state = view.state;
73
+
74
+ if (prevState?.doc.eq(state.doc) && prevState.selection.eq(state.selection)) return;
75
+
76
+ const isCellSelection = state.selection instanceof CellSelection;
77
+
78
+ if (!isCellSelection || !view.editable) {
79
+ hide();
80
+ return;
81
+ }
82
+
83
+ calculateItem(items, view);
84
+ if (Object.values(items).every(({ $ }) => $.classList.contains('hide'))) {
85
+ hide();
86
+ return;
87
+ }
88
+ tooltip.classList.remove('hide');
89
+ calculatePosition(view, tooltip);
90
+ },
91
+ destroy: () => {
92
+ tooltip.removeEventListener('mousedown', listener);
93
+ tooltip.remove();
94
+ },
95
+ };
96
+ },
97
+ });
98
+ };
@@ -0,0 +1,51 @@
1
+ /* Copyright 2021, Milkdown by Mirone. */
2
+ import { css } from '@emotion/css';
3
+ import { ThemeTool } from '@milkdown/core';
4
+
5
+ export const injectStyle = ({ size, mixin, palette }: ThemeTool) => css`
6
+ display: inline-flex;
7
+ cursor: pointer;
8
+ z-index: 2;
9
+
10
+ justify-content: space-evenly;
11
+
12
+ position: absolute;
13
+
14
+ border-radius: ${size.radius};
15
+
16
+ ${mixin.border?.()};
17
+
18
+ ${mixin.shadow?.()};
19
+
20
+ overflow: hidden;
21
+ background: ${palette('surface')};
22
+
23
+ .icon {
24
+ position: relative;
25
+ color: ${palette('solid', 0.87)};
26
+
27
+ width: 3rem;
28
+ line-height: 3rem;
29
+ text-align: center;
30
+ transition: all 0.4s ease-in-out;
31
+ &:hover {
32
+ background-color: ${palette('secondary', 0.12)};
33
+ }
34
+ &.active {
35
+ color: ${palette('primary')};
36
+ }
37
+ &:not(:last-child)::after {
38
+ content: '';
39
+ position: absolute;
40
+ right: 0px;
41
+ top: 0;
42
+ width: ${size.lineWidth};
43
+ bottom: 0;
44
+ background: ${palette('line')};
45
+ }
46
+ }
47
+ &.hide,
48
+ .hide {
49
+ display: none;
50
+ }
51
+ `;