@atlaskit/editor-plugin-table 5.2.2 → 5.3.0
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.
- package/CHANGELOG.md +6 -0
- package/dist/cjs/plugins/table/nodeviews/TableComponent.js +20 -2
- package/dist/cjs/plugins/table/nodeviews/table.js +1 -0
- package/dist/cjs/plugins/table/pm-plugins/drag-and-drop/handlers.js +24 -0
- package/dist/cjs/plugins/table/pm-plugins/drag-and-drop/plugin-factory.js +29 -4
- package/dist/cjs/plugins/table/pm-plugins/drag-and-drop/plugin.js +17 -5
- package/dist/cjs/plugins/table/types.js +4 -0
- package/dist/cjs/plugins/table/ui/TableFloatingColumnControls/ColumnDropTargets/index.js +106 -0
- package/dist/cjs/plugins/table/ui/TableFloatingColumnControls/index.js +90 -0
- package/dist/cjs/plugins/table/ui/common-styles.js +1 -1
- package/dist/cjs/plugins/table/ui/ui-styles.js +12 -9
- package/dist/es2019/plugins/table/nodeviews/TableComponent.js +20 -2
- package/dist/es2019/plugins/table/nodeviews/table.js +1 -0
- package/dist/es2019/plugins/table/pm-plugins/drag-and-drop/handlers.js +10 -0
- package/dist/es2019/plugins/table/pm-plugins/drag-and-drop/plugin-factory.js +28 -4
- package/dist/es2019/plugins/table/pm-plugins/drag-and-drop/plugin.js +14 -3
- package/dist/es2019/plugins/table/types.js +4 -0
- package/dist/es2019/plugins/table/ui/TableFloatingColumnControls/ColumnDropTargets/index.js +101 -0
- package/dist/es2019/plugins/table/ui/TableFloatingColumnControls/index.js +60 -0
- package/dist/es2019/plugins/table/ui/common-styles.js +13 -1
- package/dist/es2019/plugins/table/ui/ui-styles.js +13 -0
- package/dist/esm/plugins/table/nodeviews/TableComponent.js +20 -2
- package/dist/esm/plugins/table/nodeviews/table.js +1 -0
- package/dist/esm/plugins/table/pm-plugins/drag-and-drop/handlers.js +18 -0
- package/dist/esm/plugins/table/pm-plugins/drag-and-drop/plugin-factory.js +28 -3
- package/dist/esm/plugins/table/pm-plugins/drag-and-drop/plugin.js +18 -6
- package/dist/esm/plugins/table/types.js +4 -0
- package/dist/esm/plugins/table/ui/TableFloatingColumnControls/ColumnDropTargets/index.js +97 -0
- package/dist/esm/plugins/table/ui/TableFloatingColumnControls/index.js +80 -0
- package/dist/esm/plugins/table/ui/common-styles.js +2 -2
- package/dist/esm/plugins/table/ui/ui-styles.js +11 -8
- package/dist/types/plugins/table/index.d.ts +1 -1
- package/dist/types/plugins/table/nodeviews/TableComponent.d.ts +1 -0
- package/dist/types/plugins/table/pm-plugins/drag-and-drop/handlers.d.ts +3 -0
- package/dist/types/plugins/table/pm-plugins/drag-and-drop/plugin-factory.d.ts +1 -2
- package/dist/types/plugins/table/pm-plugins/drag-and-drop/types.d.ts +2 -0
- package/dist/types/plugins/table/types.d.ts +4 -0
- package/dist/types/plugins/table/ui/TableFloatingColumnControls/ColumnDropTargets/index.d.ts +11 -0
- package/dist/types/plugins/table/ui/TableFloatingColumnControls/index.d.ts +20 -0
- package/dist/types/plugins/table/ui/ui-styles.d.ts +1 -0
- package/dist/types-ts4.5/plugins/table/index.d.ts +1 -1
- package/dist/types-ts4.5/plugins/table/nodeviews/TableComponent.d.ts +1 -0
- package/dist/types-ts4.5/plugins/table/pm-plugins/drag-and-drop/handlers.d.ts +3 -0
- package/dist/types-ts4.5/plugins/table/pm-plugins/drag-and-drop/plugin-factory.d.ts +1 -2
- package/dist/types-ts4.5/plugins/table/pm-plugins/drag-and-drop/types.d.ts +2 -0
- package/dist/types-ts4.5/plugins/table/types.d.ts +4 -0
- package/dist/types-ts4.5/plugins/table/ui/TableFloatingColumnControls/ColumnDropTargets/index.d.ts +11 -0
- package/dist/types-ts4.5/plugins/table/ui/TableFloatingColumnControls/index.d.ts +20 -0
- package/dist/types-ts4.5/plugins/table/ui/ui-styles.d.ts +1 -0
- package/package.json +1 -1
- package/src/__tests__/unit/ui/TableFloatingColumnControls.tsx +139 -0
- package/src/plugins/table/index.tsx +1 -1
- package/src/plugins/table/nodeviews/TableComponent.tsx +25 -0
- package/src/plugins/table/nodeviews/table.tsx +1 -0
- package/src/plugins/table/pm-plugins/drag-and-drop/handlers.ts +35 -0
- package/src/plugins/table/pm-plugins/drag-and-drop/plugin-factory.ts +27 -2
- package/src/plugins/table/pm-plugins/drag-and-drop/plugin.ts +12 -3
- package/src/plugins/table/pm-plugins/drag-and-drop/types.ts +3 -0
- package/src/plugins/table/types.ts +5 -0
- package/src/plugins/table/ui/TableFloatingColumnControls/ColumnDropTargets/index.tsx +128 -0
- package/src/plugins/table/ui/TableFloatingColumnControls/index.tsx +101 -0
- package/src/plugins/table/ui/common-styles.ts +13 -0
- package/src/plugins/table/ui/ui-styles.ts +14 -0
|
@@ -225,6 +225,7 @@ export declare enum TableDecorations {
|
|
|
225
225
|
TABLE_CONTROLS_HOVER = "TABLE_CONTROLS_HOVER",
|
|
226
226
|
CELL_CONTROLS_HOVER = "CELL_CONTROLS_HOVER",
|
|
227
227
|
COLUMN_CONTROLS_DECORATIONS = "COLUMN_CONTROLS_DECORATIONS",
|
|
228
|
+
COLUMN_DROP_TARGET_DECORATIONS = "COLUMN_DROP_TARGET_DECORATIONS",
|
|
228
229
|
COLUMN_SELECTED = "COLUMN_SELECTED",
|
|
229
230
|
COLUMN_RESIZING_HANDLE = "COLUMN_RESIZING_HANDLE",
|
|
230
231
|
COLUMN_RESIZING_HANDLE_WIDGET = "COLUMN_RESIZING_HANDLE_WIDGET",
|
|
@@ -235,6 +236,9 @@ export declare const TableCssClassName: {
|
|
|
235
236
|
COLUMN_CONTROLS: string;
|
|
236
237
|
COLUMN_CONTROLS_DECORATIONS: string;
|
|
237
238
|
COLUMN_SELECTED: string;
|
|
239
|
+
COLUMN_CONTROLS_WRAPPER: string;
|
|
240
|
+
COLUMN_DROP_TARGET_CONTROLS: string;
|
|
241
|
+
COLUMN_CONTROLS_INNER: string;
|
|
238
242
|
ROW_CONTROLS_WRAPPER: string;
|
|
239
243
|
ROW_CONTROLS: string;
|
|
240
244
|
ROW_CONTROLS_INNER: string;
|
package/dist/types-ts4.5/plugins/table/ui/TableFloatingColumnControls/ColumnDropTargets/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
3
|
+
export interface Props {
|
|
4
|
+
editorView: EditorView;
|
|
5
|
+
tableRef: HTMLTableElement;
|
|
6
|
+
stickyTop?: number;
|
|
7
|
+
tableHeight?: number;
|
|
8
|
+
localId?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare const ColumnDropTargets: React.FC<Props>;
|
|
11
|
+
export default ColumnDropTargets;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { TableColumnOrdering } from '@atlaskit/custom-steps';
|
|
3
|
+
import type { GetEditorFeatureFlags } from '@atlaskit/editor-common/types';
|
|
4
|
+
import type { Selection } from '@atlaskit/editor-prosemirror/state';
|
|
5
|
+
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
6
|
+
import type { RowStickyState } from '../../pm-plugins/sticky-headers';
|
|
7
|
+
export interface Props {
|
|
8
|
+
editorView: EditorView;
|
|
9
|
+
getEditorFeatureFlags: GetEditorFeatureFlags;
|
|
10
|
+
selection?: Selection;
|
|
11
|
+
tableRef?: HTMLTableElement;
|
|
12
|
+
tableActive?: boolean;
|
|
13
|
+
hasHeaderRow?: boolean;
|
|
14
|
+
headerRowHeight?: number;
|
|
15
|
+
hoveredRows?: number[];
|
|
16
|
+
ordering?: TableColumnOrdering;
|
|
17
|
+
stickyHeader?: RowStickyState;
|
|
18
|
+
}
|
|
19
|
+
export declare const TableFloatingColumnControls: React.FC<Props>;
|
|
20
|
+
export default TableFloatingColumnControls;
|
|
@@ -8,6 +8,7 @@ export declare const insertRowButtonWrapper: (props: ThemeProps) => import("@emo
|
|
|
8
8
|
export declare const columnControlsLineMarker: () => import("@emotion/react").SerializedStyles;
|
|
9
9
|
export declare const DeleteButton: (props: ThemeProps) => import("@emotion/react").SerializedStyles;
|
|
10
10
|
export declare const OverflowShadow: (props: ThemeProps) => import("@emotion/react").SerializedStyles;
|
|
11
|
+
export declare const floatingColumnControls: (props: ThemeProps) => import("@emotion/react").SerializedStyles;
|
|
11
12
|
export declare const columnControlsDecoration: (props: ThemeProps) => import("@emotion/react").SerializedStyles;
|
|
12
13
|
export declare const hoveredDeleteButton: (props: ThemeProps) => import("@emotion/react").SerializedStyles;
|
|
13
14
|
export declare const hoveredCell: (props: ThemeProps) => import("@emotion/react").SerializedStyles;
|
package/package.json
CHANGED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { render, screen } from '@testing-library/react';
|
|
4
|
+
import { IntlProvider } from 'react-intl-next';
|
|
5
|
+
|
|
6
|
+
import type { DocBuilder } from '@atlaskit/editor-common/types';
|
|
7
|
+
// eslint-disable-next-line import/no-extraneous-dependencies -- Removed import for fixing circular dependencies
|
|
8
|
+
import { analyticsPlugin } from '@atlaskit/editor-plugin-analytics';
|
|
9
|
+
import { contentInsertionPlugin } from '@atlaskit/editor-plugin-content-insertion';
|
|
10
|
+
import { featureFlagsPlugin } from '@atlaskit/editor-plugin-feature-flags';
|
|
11
|
+
import { guidelinePlugin } from '@atlaskit/editor-plugin-guideline';
|
|
12
|
+
import { selectionPlugin } from '@atlaskit/editor-plugin-selection';
|
|
13
|
+
import { widthPlugin } from '@atlaskit/editor-plugin-width';
|
|
14
|
+
import type { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
15
|
+
import type { LightEditorPlugin } from '@atlaskit/editor-test-helpers/create-prosemirror-editor';
|
|
16
|
+
// eslint-disable-next-line import/no-extraneous-dependencies -- Removed import for fixing circular dependencies
|
|
17
|
+
import {
|
|
18
|
+
createProsemirrorEditorFactory,
|
|
19
|
+
Preset,
|
|
20
|
+
} from '@atlaskit/editor-test-helpers/create-prosemirror-editor';
|
|
21
|
+
// eslint-disable-next-line import/no-extraneous-dependencies -- Removed import for fixing circular dependencies
|
|
22
|
+
import {
|
|
23
|
+
doc,
|
|
24
|
+
p,
|
|
25
|
+
table,
|
|
26
|
+
tdEmpty,
|
|
27
|
+
tr,
|
|
28
|
+
} from '@atlaskit/editor-test-helpers/doc-builder';
|
|
29
|
+
|
|
30
|
+
import tablePlugin from '../../../plugins/table-plugin';
|
|
31
|
+
import { pluginKey } from '../../../plugins/table/pm-plugins/plugin-key';
|
|
32
|
+
import type { TablePluginState } from '../../../plugins/table/types';
|
|
33
|
+
import TableFloatingColumnControls from '../../../plugins/table/ui/TableFloatingColumnControls';
|
|
34
|
+
|
|
35
|
+
describe('TableFloatingColumnControls', () => {
|
|
36
|
+
const createEditor = createProsemirrorEditorFactory();
|
|
37
|
+
const fakeGetEditorFeatureFlags = () => ({});
|
|
38
|
+
const editor = (
|
|
39
|
+
doc: DocBuilder,
|
|
40
|
+
options?: {
|
|
41
|
+
dragAndDropEnabled?: boolean;
|
|
42
|
+
},
|
|
43
|
+
) => {
|
|
44
|
+
const preset = new Preset<LightEditorPlugin>()
|
|
45
|
+
.add([featureFlagsPlugin, {}])
|
|
46
|
+
.add([analyticsPlugin, {}])
|
|
47
|
+
.add(contentInsertionPlugin)
|
|
48
|
+
.add(widthPlugin)
|
|
49
|
+
.add(guidelinePlugin)
|
|
50
|
+
.add(selectionPlugin)
|
|
51
|
+
.add([tablePlugin, { ...options, tableOptions: {} }]);
|
|
52
|
+
return createEditor<TablePluginState, PluginKey, typeof preset>({
|
|
53
|
+
doc,
|
|
54
|
+
preset,
|
|
55
|
+
pluginKey: pluginKey,
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
it('should not render floating column controls when tableRef undefined and drag and drop is not enabled', () => {
|
|
60
|
+
const { editorView } = editor(
|
|
61
|
+
doc(p('text'), table()(tr(tdEmpty, tdEmpty, tdEmpty))),
|
|
62
|
+
);
|
|
63
|
+
const { container } = render(
|
|
64
|
+
<TableFloatingColumnControls
|
|
65
|
+
editorView={editorView}
|
|
66
|
+
getEditorFeatureFlags={fakeGetEditorFeatureFlags}
|
|
67
|
+
/>,
|
|
68
|
+
);
|
|
69
|
+
expect(container.innerHTML).toEqual('');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should not render floating column controls when tableRef undefined and drag and drop is enabled', () => {
|
|
73
|
+
const { editorView } = editor(
|
|
74
|
+
doc(p('text'), table()(tr(tdEmpty, tdEmpty, tdEmpty))),
|
|
75
|
+
{
|
|
76
|
+
dragAndDropEnabled: true,
|
|
77
|
+
},
|
|
78
|
+
);
|
|
79
|
+
const { container } = render(
|
|
80
|
+
<TableFloatingColumnControls
|
|
81
|
+
editorView={editorView}
|
|
82
|
+
getEditorFeatureFlags={fakeGetEditorFeatureFlags}
|
|
83
|
+
/>,
|
|
84
|
+
);
|
|
85
|
+
expect(container.innerHTML).toEqual('');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should render a drop target per column', () => {
|
|
89
|
+
const { editorView } = editor(
|
|
90
|
+
doc(p('text'), table()(tr(tdEmpty, tdEmpty, tdEmpty, tdEmpty, tdEmpty))),
|
|
91
|
+
{
|
|
92
|
+
dragAndDropEnabled: true,
|
|
93
|
+
},
|
|
94
|
+
);
|
|
95
|
+
const ref = editorView.dom.querySelector('table') || undefined;
|
|
96
|
+
|
|
97
|
+
render(
|
|
98
|
+
<IntlProvider locale="en">
|
|
99
|
+
<TableFloatingColumnControls
|
|
100
|
+
tableRef={ref}
|
|
101
|
+
tableActive={true}
|
|
102
|
+
editorView={editorView}
|
|
103
|
+
getEditorFeatureFlags={fakeGetEditorFeatureFlags}
|
|
104
|
+
/>
|
|
105
|
+
</IntlProvider>,
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const dropTargets = screen.getAllByTestId(
|
|
109
|
+
'table-floating-column-controls-drop-target',
|
|
110
|
+
);
|
|
111
|
+
expect(dropTargets).toHaveLength(5);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should render a drop target per column regardless of row count', () => {
|
|
115
|
+
const { editorView } = editor(
|
|
116
|
+
doc(p('text'), table()(tr(tdEmpty), tr(tdEmpty), tr(tdEmpty))),
|
|
117
|
+
{
|
|
118
|
+
dragAndDropEnabled: true,
|
|
119
|
+
},
|
|
120
|
+
);
|
|
121
|
+
const ref = editorView.dom.querySelector('table') || undefined;
|
|
122
|
+
|
|
123
|
+
render(
|
|
124
|
+
<IntlProvider locale="en">
|
|
125
|
+
<TableFloatingColumnControls
|
|
126
|
+
tableRef={ref}
|
|
127
|
+
tableActive={true}
|
|
128
|
+
editorView={editorView}
|
|
129
|
+
getEditorFeatureFlags={fakeGetEditorFeatureFlags}
|
|
130
|
+
/>
|
|
131
|
+
</IntlProvider>,
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const dropTargets = screen.getAllByTestId(
|
|
135
|
+
'table-floating-column-controls-drop-target',
|
|
136
|
+
);
|
|
137
|
+
expect(dropTargets).toHaveLength(1);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
@@ -78,7 +78,7 @@ import FloatingDeleteButton from './ui/FloatingDeleteButton';
|
|
|
78
78
|
import FloatingInsertButton from './ui/FloatingInsertButton';
|
|
79
79
|
import LayoutButton from './ui/LayoutButton';
|
|
80
80
|
import { isLayoutSupported } from './utils';
|
|
81
|
-
interface TablePluginOptions {
|
|
81
|
+
export interface TablePluginOptions {
|
|
82
82
|
tableOptions: PluginConfig;
|
|
83
83
|
// experimental custom table resizing experience, set inside editor-core behind a feature flag
|
|
84
84
|
// will eventually replace breakoutEnabled
|
|
@@ -58,6 +58,7 @@ import {
|
|
|
58
58
|
tableOverflowShadowWidth,
|
|
59
59
|
tableOverflowShadowWidthWide,
|
|
60
60
|
} from '../ui/consts';
|
|
61
|
+
import TableFloatingColumnControls from '../ui/TableFloatingColumnControls';
|
|
61
62
|
import TableFloatingControls from '../ui/TableFloatingControls';
|
|
62
63
|
import {
|
|
63
64
|
containsHeaderRow,
|
|
@@ -91,6 +92,7 @@ export interface ComponentProps {
|
|
|
91
92
|
isHeaderRowEnabled: boolean;
|
|
92
93
|
isHeaderColumnEnabled: boolean;
|
|
93
94
|
isMediaFullscreen?: boolean;
|
|
95
|
+
isDragAndDropEnabled?: boolean;
|
|
94
96
|
tableActive: boolean;
|
|
95
97
|
ordering: TableColumnOrdering;
|
|
96
98
|
isResizing?: boolean;
|
|
@@ -396,6 +398,7 @@ class TableComponent extends React.Component<ComponentProps, TableState> {
|
|
|
396
398
|
options,
|
|
397
399
|
getPos,
|
|
398
400
|
pluginInjectionApi,
|
|
401
|
+
isDragAndDropEnabled,
|
|
399
402
|
} = this.props;
|
|
400
403
|
const { showBeforeShadow, showAfterShadow } = this.state;
|
|
401
404
|
const node = getNode();
|
|
@@ -431,6 +434,24 @@ class TableComponent extends React.Component<ComponentProps, TableState> {
|
|
|
431
434
|
</div>
|
|
432
435
|
);
|
|
433
436
|
|
|
437
|
+
const colControls = (
|
|
438
|
+
<div className={ClassName.COLUMN_CONTROLS_WRAPPER}>
|
|
439
|
+
<TableFloatingColumnControls
|
|
440
|
+
editorView={view}
|
|
441
|
+
tableRef={tableRef}
|
|
442
|
+
tableActive={tableActive}
|
|
443
|
+
hoveredRows={hoveredRows}
|
|
444
|
+
ordering={ordering}
|
|
445
|
+
hasHeaderRow={hasHeaderRow}
|
|
446
|
+
// pass `selection` and `tableHeight` to control re-render
|
|
447
|
+
selection={view.state.selection}
|
|
448
|
+
headerRowHeight={headerRow ? headerRow.offsetHeight : undefined}
|
|
449
|
+
stickyHeader={this.state.stickyHeader}
|
|
450
|
+
getEditorFeatureFlags={this.props.getEditorFeatureFlags}
|
|
451
|
+
/>
|
|
452
|
+
</div>
|
|
453
|
+
);
|
|
454
|
+
|
|
434
455
|
const shadowPadding =
|
|
435
456
|
allowControls && tableActive ? -tableToolbarSize : tableMarginSides;
|
|
436
457
|
|
|
@@ -482,7 +503,11 @@ class TableComponent extends React.Component<ComponentProps, TableState> {
|
|
|
482
503
|
data-testid="sticky-scrollbar-sentinel-top"
|
|
483
504
|
/>
|
|
484
505
|
)}
|
|
506
|
+
|
|
485
507
|
{allowControls && rowControls}
|
|
508
|
+
|
|
509
|
+
{isDragAndDropEnabled && allowControls && colControls}
|
|
510
|
+
|
|
486
511
|
<div
|
|
487
512
|
style={shadowStyle(showBeforeShadow)}
|
|
488
513
|
className={ClassName.TABLE_LEFT_SHADOW}
|
|
@@ -248,6 +248,7 @@ export default class TableView extends ReactNodeView<Props> {
|
|
|
248
248
|
allowControls={pluginState!.pluginConfig.allowControls!}
|
|
249
249
|
isHeaderRowEnabled={pluginState!.isHeaderRowEnabled}
|
|
250
250
|
isHeaderColumnEnabled={pluginState!.isHeaderColumnEnabled}
|
|
251
|
+
isDragAndDropEnabled={pluginState!.isDragAndDropEnabled}
|
|
251
252
|
tableActive={tableActive}
|
|
252
253
|
ordering={pluginState!.ordering as TableColumnOrdering}
|
|
253
254
|
isResizing={isResizing}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// @ts-ignore -- ReadonlyTransaction is a local declaration and will cause a TS2305 error in CCFE typecheck
|
|
2
|
+
import type {
|
|
3
|
+
ReadonlyTransaction,
|
|
4
|
+
Transaction,
|
|
5
|
+
} from '@atlaskit/editor-prosemirror/state';
|
|
6
|
+
import type { ContentNodeWithPos } from '@atlaskit/editor-prosemirror/utils';
|
|
7
|
+
import { findTable } from '@atlaskit/editor-tables/utils';
|
|
8
|
+
|
|
9
|
+
import type { DragAndDropPluginState } from './types';
|
|
10
|
+
|
|
11
|
+
type BuilderDragAndDropPluginState = (props: {
|
|
12
|
+
tr: Transaction | ReadonlyTransaction;
|
|
13
|
+
table?: ContentNodeWithPos;
|
|
14
|
+
}) => (pluginState: DragAndDropPluginState) => DragAndDropPluginState;
|
|
15
|
+
|
|
16
|
+
const buildPluginState =
|
|
17
|
+
(
|
|
18
|
+
builders: Array<BuilderDragAndDropPluginState>,
|
|
19
|
+
): BuilderDragAndDropPluginState =>
|
|
20
|
+
(props) =>
|
|
21
|
+
(pluginState) => {
|
|
22
|
+
return builders.reduce(
|
|
23
|
+
(_pluginState, transform) => transform(props)(_pluginState),
|
|
24
|
+
pluginState,
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const handleDocOrSelectionChanged = (
|
|
29
|
+
tr: Transaction | ReadonlyTransaction,
|
|
30
|
+
pluginState: DragAndDropPluginState,
|
|
31
|
+
): DragAndDropPluginState =>
|
|
32
|
+
buildPluginState([])({
|
|
33
|
+
tr,
|
|
34
|
+
table: findTable(tr.selection),
|
|
35
|
+
})(pluginState);
|
|
@@ -1,8 +1,33 @@
|
|
|
1
1
|
import { pluginFactory } from '@atlaskit/editor-common/utils';
|
|
2
2
|
|
|
3
|
+
import { pluginKey as tablePluginKey } from '../plugin-key';
|
|
4
|
+
|
|
5
|
+
import { handleDocOrSelectionChanged } from './handlers';
|
|
3
6
|
import { pluginKey } from './plugin-key';
|
|
4
7
|
import reducer from './reducer';
|
|
5
8
|
|
|
6
|
-
const { createPluginState, createCommand } =
|
|
9
|
+
export const { createPluginState, createCommand, getPluginState } =
|
|
10
|
+
pluginFactory(pluginKey, reducer, {
|
|
11
|
+
mapping: (tr, pluginState) => {
|
|
12
|
+
if (tr.docChanged) {
|
|
13
|
+
let decorationSet = pluginState.decorationSet;
|
|
14
|
+
|
|
15
|
+
const meta = tr.getMeta(tablePluginKey);
|
|
16
|
+
if (meta && meta.data && meta.data.decorationSet) {
|
|
17
|
+
decorationSet = meta.data.decorationSet;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (decorationSet) {
|
|
21
|
+
decorationSet = decorationSet.map(tr.mapping, tr.doc);
|
|
22
|
+
}
|
|
7
23
|
|
|
8
|
-
|
|
24
|
+
return {
|
|
25
|
+
...pluginState,
|
|
26
|
+
...{ decorationSet },
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return pluginState;
|
|
30
|
+
},
|
|
31
|
+
onDocChanged: handleDocOrSelectionChanged,
|
|
32
|
+
onSelectionChanged: handleDocOrSelectionChanged,
|
|
33
|
+
});
|
|
@@ -4,9 +4,10 @@ import type {
|
|
|
4
4
|
} from '@atlaskit/editor-common/event-dispatcher';
|
|
5
5
|
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
6
6
|
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
7
|
+
import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
7
8
|
|
|
8
9
|
import { DropTargetType } from './consts';
|
|
9
|
-
import { createPluginState } from './plugin-factory';
|
|
10
|
+
import { createPluginState, getPluginState } from './plugin-factory';
|
|
10
11
|
import { pluginKey } from './plugin-key';
|
|
11
12
|
|
|
12
13
|
export const createPlugin = (
|
|
@@ -14,19 +15,27 @@ export const createPlugin = (
|
|
|
14
15
|
eventDispatcher: EventDispatcher,
|
|
15
16
|
) => {
|
|
16
17
|
return new SafePlugin({
|
|
17
|
-
state: createPluginState(dispatch, {
|
|
18
|
+
state: createPluginState(dispatch, (state) => ({
|
|
19
|
+
decorationSet: DecorationSet.empty,
|
|
18
20
|
// TODO: This is example placeholder state. We could use this to track which row/col is currently set as the drop target
|
|
19
21
|
// This would result in a blue highlight being displayed on the corrisponding row/column to single the drop target location.
|
|
20
22
|
dropTargetType: DropTargetType.NONE,
|
|
21
23
|
dropTargetIndex: 0,
|
|
22
|
-
}),
|
|
24
|
+
})),
|
|
23
25
|
key: pluginKey,
|
|
24
26
|
view: (editorView: EditorView) => {
|
|
25
27
|
// TODO: Add Pragmatic DnD monitor when the view is constructed.
|
|
28
|
+
|
|
26
29
|
return {
|
|
27
30
|
// TODO: Cleanup monitor instance
|
|
28
31
|
// destroy: cleanup,
|
|
29
32
|
};
|
|
30
33
|
},
|
|
34
|
+
props: {
|
|
35
|
+
decorations: (state) => {
|
|
36
|
+
const { decorationSet } = getPluginState(state);
|
|
37
|
+
return decorationSet;
|
|
38
|
+
},
|
|
39
|
+
},
|
|
31
40
|
});
|
|
32
41
|
};
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import type { DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
2
|
+
|
|
1
3
|
import type { DropTargetType } from './consts';
|
|
2
4
|
|
|
3
5
|
export interface DragAndDropPluginState {
|
|
6
|
+
decorationSet: DecorationSet;
|
|
4
7
|
dropTargetType: DropTargetType;
|
|
5
8
|
dropTargetIndex: number;
|
|
6
9
|
}
|
|
@@ -239,6 +239,7 @@ export enum TableDecorations {
|
|
|
239
239
|
CELL_CONTROLS_HOVER = 'CELL_CONTROLS_HOVER',
|
|
240
240
|
|
|
241
241
|
COLUMN_CONTROLS_DECORATIONS = 'COLUMN_CONTROLS_DECORATIONS',
|
|
242
|
+
COLUMN_DROP_TARGET_DECORATIONS = 'COLUMN_DROP_TARGET_DECORATIONS',
|
|
242
243
|
COLUMN_SELECTED = 'COLUMN_SELECTED',
|
|
243
244
|
COLUMN_RESIZING_HANDLE = 'COLUMN_RESIZING_HANDLE',
|
|
244
245
|
COLUMN_RESIZING_HANDLE_WIDGET = 'COLUMN_RESIZING_HANDLE_WIDGET',
|
|
@@ -254,6 +255,10 @@ export const TableCssClassName = {
|
|
|
254
255
|
COLUMN_CONTROLS_DECORATIONS: `${tablePrefixSelector}-column-controls-decoration`,
|
|
255
256
|
COLUMN_SELECTED: `${tablePrefixSelector}-column__selected`,
|
|
256
257
|
|
|
258
|
+
COLUMN_CONTROLS_WRAPPER: `${tablePrefixSelector}-col-controls-wrapper`,
|
|
259
|
+
COLUMN_DROP_TARGET_CONTROLS: `${tablePrefixSelector}-col-drop-target-controls`,
|
|
260
|
+
COLUMN_CONTROLS_INNER: `${tablePrefixSelector}-col-controls__inner`,
|
|
261
|
+
|
|
257
262
|
ROW_CONTROLS_WRAPPER: `${tablePrefixSelector}-row-controls-wrapper`,
|
|
258
263
|
ROW_CONTROLS: `${tablePrefixSelector}-row-controls`,
|
|
259
264
|
ROW_CONTROLS_INNER: `${tablePrefixSelector}-row-controls__inner`,
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
4
|
+
import { attachClosestEdge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/addon/closest-edge';
|
|
5
|
+
import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/adapter/element';
|
|
6
|
+
|
|
7
|
+
import type { DraggableSourceData } from '../../../types';
|
|
8
|
+
import { TableCssClassName as ClassName } from '../../../types';
|
|
9
|
+
import { getColumnsWidths, getRowHeights } from '../../../utils';
|
|
10
|
+
|
|
11
|
+
export interface Props {
|
|
12
|
+
editorView: EditorView;
|
|
13
|
+
tableRef: HTMLTableElement;
|
|
14
|
+
stickyTop?: number;
|
|
15
|
+
tableHeight?: number;
|
|
16
|
+
localId?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const ColumnDropTargets: React.FC<Props> = ({
|
|
20
|
+
editorView,
|
|
21
|
+
tableRef,
|
|
22
|
+
tableHeight,
|
|
23
|
+
stickyTop,
|
|
24
|
+
localId,
|
|
25
|
+
}) => {
|
|
26
|
+
const colWidths = getColumnsWidths(editorView);
|
|
27
|
+
const rowHeights = useMemo(() => {
|
|
28
|
+
// NOTE: we don't care so much as to what tableHeight is, we only care that it changed and is a sane value.
|
|
29
|
+
if (tableRef && !!tableHeight) {
|
|
30
|
+
return getRowHeights(tableRef);
|
|
31
|
+
}
|
|
32
|
+
return [0];
|
|
33
|
+
}, [tableRef, tableHeight]);
|
|
34
|
+
|
|
35
|
+
if (!tableRef) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const firstRow = tableRef.querySelector('tr');
|
|
40
|
+
const hasHeaderRow = firstRow
|
|
41
|
+
? firstRow.getAttribute('data-header-row')
|
|
42
|
+
: false;
|
|
43
|
+
|
|
44
|
+
const marginTop =
|
|
45
|
+
hasHeaderRow && stickyTop !== undefined ? rowHeights?.[0] ?? 0 : 0;
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div className={ClassName.COLUMN_DROP_TARGET_CONTROLS}>
|
|
49
|
+
<div
|
|
50
|
+
className={ClassName.COLUMN_CONTROLS_INNER}
|
|
51
|
+
data-testid="table-floating-column-controls-drop-targets"
|
|
52
|
+
>
|
|
53
|
+
{colWidths.map((width, index) => {
|
|
54
|
+
return (
|
|
55
|
+
<ColumnDropTarget
|
|
56
|
+
key={index}
|
|
57
|
+
index={index}
|
|
58
|
+
localId={localId}
|
|
59
|
+
width={width}
|
|
60
|
+
height={tableHeight}
|
|
61
|
+
marginTop={marginTop}
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
})}
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export default ColumnDropTargets;
|
|
71
|
+
|
|
72
|
+
const ColumnDropTarget: React.FC<{
|
|
73
|
+
index: number;
|
|
74
|
+
localId?: string;
|
|
75
|
+
width?: number;
|
|
76
|
+
height?: number;
|
|
77
|
+
marginTop?: number;
|
|
78
|
+
}> = ({ index, localId, width, height, marginTop }) => {
|
|
79
|
+
const dropTargetRef = useRef<HTMLDivElement | null>(null);
|
|
80
|
+
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (!dropTargetRef.current) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return dropTargetForElements({
|
|
87
|
+
element: dropTargetRef.current,
|
|
88
|
+
canDrop({ source }) {
|
|
89
|
+
const data = source.data as DraggableSourceData;
|
|
90
|
+
return (
|
|
91
|
+
// Only draggables of row type can be dropped on this target
|
|
92
|
+
data.type === 'table-column' &&
|
|
93
|
+
// Only draggables which came from the same table can be dropped on this target
|
|
94
|
+
data.localId === localId &&
|
|
95
|
+
// Only draggables which DO NOT include this drop targets index can be dropped
|
|
96
|
+
!!data.indexes?.length &&
|
|
97
|
+
data.indexes?.indexOf(index) === -1
|
|
98
|
+
);
|
|
99
|
+
},
|
|
100
|
+
getData({ input, element }) {
|
|
101
|
+
const data = {
|
|
102
|
+
localId,
|
|
103
|
+
type: 'table-column',
|
|
104
|
+
targetIndex: index,
|
|
105
|
+
};
|
|
106
|
+
return attachClosestEdge(data, {
|
|
107
|
+
input,
|
|
108
|
+
element,
|
|
109
|
+
allowedEdges: ['left', 'right'],
|
|
110
|
+
});
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
}, [index, localId]);
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<div
|
|
117
|
+
ref={dropTargetRef}
|
|
118
|
+
style={{
|
|
119
|
+
width: width && `${width - 1}px`,
|
|
120
|
+
height: height && `${height}px`,
|
|
121
|
+
marginTop: marginTop && `${marginTop}px`,
|
|
122
|
+
}}
|
|
123
|
+
data-drop-target-index={index}
|
|
124
|
+
data-drop-target-localid={localId}
|
|
125
|
+
data-testid="table-floating-column-controls-drop-target"
|
|
126
|
+
></div>
|
|
127
|
+
);
|
|
128
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import type { TableColumnOrdering } from '@atlaskit/custom-steps';
|
|
4
|
+
import type { GetEditorFeatureFlags } from '@atlaskit/editor-common/types';
|
|
5
|
+
import type { Selection } from '@atlaskit/editor-prosemirror/state';
|
|
6
|
+
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
7
|
+
import { findTable } from '@atlaskit/editor-tables';
|
|
8
|
+
|
|
9
|
+
import type { RowStickyState } from '../../pm-plugins/sticky-headers';
|
|
10
|
+
|
|
11
|
+
import { ColumnDropTargets } from './ColumnDropTargets';
|
|
12
|
+
|
|
13
|
+
export interface Props {
|
|
14
|
+
editorView: EditorView;
|
|
15
|
+
getEditorFeatureFlags: GetEditorFeatureFlags;
|
|
16
|
+
selection?: Selection;
|
|
17
|
+
tableRef?: HTMLTableElement;
|
|
18
|
+
tableActive?: boolean;
|
|
19
|
+
hasHeaderRow?: boolean;
|
|
20
|
+
headerRowHeight?: number;
|
|
21
|
+
hoveredRows?: number[];
|
|
22
|
+
ordering?: TableColumnOrdering;
|
|
23
|
+
stickyHeader?: RowStickyState;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const TableFloatingColumnControls: React.FC<Props> = ({
|
|
27
|
+
editorView,
|
|
28
|
+
tableRef,
|
|
29
|
+
tableActive,
|
|
30
|
+
hasHeaderRow,
|
|
31
|
+
stickyHeader,
|
|
32
|
+
selection,
|
|
33
|
+
}) => {
|
|
34
|
+
const [tableRect, setTableRect] = useState<{ width: number; height: number }>(
|
|
35
|
+
{ width: 0, height: 0 },
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (tableRef && window?.ResizeObserver) {
|
|
40
|
+
const resizeObserver = new ResizeObserver((entries) => {
|
|
41
|
+
for (let entry of entries) {
|
|
42
|
+
setTableRect((prev) => {
|
|
43
|
+
if (
|
|
44
|
+
prev.width !== entry.contentRect.width ||
|
|
45
|
+
prev.height !== entry.contentRect.height
|
|
46
|
+
) {
|
|
47
|
+
return entry.contentRect;
|
|
48
|
+
}
|
|
49
|
+
return prev;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
resizeObserver.observe(tableRef);
|
|
54
|
+
|
|
55
|
+
return () => {
|
|
56
|
+
resizeObserver.disconnect();
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}, [tableRef]);
|
|
60
|
+
|
|
61
|
+
const selectedLocalId = useMemo(() => {
|
|
62
|
+
if (!selection) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const tableNode = findTable(selection);
|
|
67
|
+
if (!tableNode) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return tableNode.node.attrs.localId;
|
|
72
|
+
}, [selection]);
|
|
73
|
+
|
|
74
|
+
if (!tableRef) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const stickyTop =
|
|
79
|
+
stickyHeader && stickyHeader.sticky && hasHeaderRow
|
|
80
|
+
? stickyHeader.top
|
|
81
|
+
: undefined;
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<div
|
|
85
|
+
onMouseDown={(e) => e.preventDefault()}
|
|
86
|
+
data-testid="table-floating-column-controls-wrapper"
|
|
87
|
+
>
|
|
88
|
+
{tableActive && (
|
|
89
|
+
<ColumnDropTargets
|
|
90
|
+
editorView={editorView}
|
|
91
|
+
tableRef={tableRef}
|
|
92
|
+
stickyTop={tableActive ? stickyTop : undefined}
|
|
93
|
+
tableHeight={tableRect.height}
|
|
94
|
+
localId={selectedLocalId}
|
|
95
|
+
/>
|
|
96
|
+
)}
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export default TableFloatingColumnControls;
|