@atlaskit/editor-plugin-table 2.7.1 → 2.8.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 +16 -0
- package/dist/cjs/plugins/table/nodeviews/TableResizer.js +25 -26
- package/dist/cjs/plugins/table/toolbar.js +13 -17
- package/dist/cjs/plugins/table/utils/analytics.js +146 -2
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/plugins/table/nodeviews/TableResizer.js +27 -27
- package/dist/es2019/plugins/table/toolbar.js +11 -16
- package/dist/es2019/plugins/table/utils/analytics.js +130 -0
- package/dist/es2019/version.json +1 -1
- package/dist/esm/plugins/table/nodeviews/TableResizer.js +26 -27
- package/dist/esm/plugins/table/toolbar.js +13 -17
- package/dist/esm/plugins/table/utils/analytics.js +139 -0
- package/dist/esm/version.json +1 -1
- package/dist/types/plugins/table/types.d.ts +0 -1
- package/dist/types/plugins/table/utils/analytics.d.ts +28 -1
- package/dist/types-ts4.5/plugins/table/types.d.ts +0 -1
- package/dist/types-ts4.5/plugins/table/utils/analytics.d.ts +28 -1
- package/package.json +3 -2
- package/report.api.md +0 -2
- package/src/__tests__/integration/floating-toolbar.ts +0 -7
- package/src/__tests__/unit/nodeviews/TableContainer.tsx +57 -1
- package/src/__tests__/unit/utils/analytics.ts +98 -0
- package/src/__tests__/visual-regression/__image_snapshots__/cell-options-menu-ts-table-cell-options-menu-delete-column-menu-item-should-remove-the-table-column-on-click-1-snap.png +2 -2
- package/src/__tests__/visual-regression/__image_snapshots__/cell-options-menu-ts-table-cell-options-menu-delete-column-menu-item-visual-hints-should-be-added-to-the-table-column-on-hover-1-snap.png +2 -2
- package/src/__tests__/visual-regression/__image_snapshots__/cell-options-menu-ts-table-cell-options-menu-delete-row-menu-item-should-remove-the-table-row-on-click-1-snap.png +2 -2
- package/src/__tests__/visual-regression/__image_snapshots__/cell-options-menu-ts-table-cell-options-menu-delete-row-menu-item-visual-hints-should-be-added-to-the-table-row-on-hover-1-snap.png +2 -2
- package/src/plugins/table/nodeviews/TableResizer.tsx +35 -34
- package/src/plugins/table/toolbar.tsx +13 -32
- package/src/plugins/table/types.ts +0 -1
- package/src/plugins/table/utils/analytics.ts +168 -0
- package/tmp/api-report-tmp.d.ts +0 -2
|
@@ -29,6 +29,23 @@ import {
|
|
|
29
29
|
import { pluginKey } from '../../../plugins/table/pm-plugins/plugin-key';
|
|
30
30
|
import { TablePluginState } from '../../../plugins/table/types';
|
|
31
31
|
|
|
32
|
+
const mockStartMeasure = jest.fn();
|
|
33
|
+
const mockEndMeasure = jest.fn(() => {
|
|
34
|
+
return [51, 52, 53, 54];
|
|
35
|
+
});
|
|
36
|
+
const mockCountFrames = jest.fn();
|
|
37
|
+
|
|
38
|
+
jest.mock('../../../plugins/table/utils/analytics', () => ({
|
|
39
|
+
...jest.requireActual('../../../plugins/table/utils/analytics'),
|
|
40
|
+
useMeasureFramerate: () => {
|
|
41
|
+
return {
|
|
42
|
+
startMeasure: mockStartMeasure,
|
|
43
|
+
endMeasure: mockEndMeasure,
|
|
44
|
+
countFrames: mockCountFrames,
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
}));
|
|
48
|
+
|
|
32
49
|
describe('table -> nodeviews -> TableContainer.tsx', () => {
|
|
33
50
|
const createEditor = createEditorFactory<TablePluginState>();
|
|
34
51
|
const editor = (
|
|
@@ -202,7 +219,7 @@ describe('table -> nodeviews -> TableContainer.tsx', () => {
|
|
|
202
219
|
fireEvent.mouseMove(container.querySelector('.resizer-handle-right')!);
|
|
203
220
|
fireEvent.mouseUp(container.querySelector('.resizer-handle-right')!);
|
|
204
221
|
|
|
205
|
-
expect(analyticsMock).
|
|
222
|
+
expect(analyticsMock).toHaveBeenCalledWith({
|
|
206
223
|
action: TABLE_ACTION.RESIZED,
|
|
207
224
|
actionSubject: ACTION_SUBJECT.TABLE,
|
|
208
225
|
eventType: EVENT_TYPE.TRACK,
|
|
@@ -215,6 +232,45 @@ describe('table -> nodeviews -> TableContainer.tsx', () => {
|
|
|
215
232
|
totalColumnCount: 3,
|
|
216
233
|
},
|
|
217
234
|
});
|
|
235
|
+
|
|
236
|
+
expect(analyticsMock).toHaveBeenCalledWith({
|
|
237
|
+
action: TABLE_ACTION.RESIZE_PERF_SAMPLING,
|
|
238
|
+
actionSubject: ACTION_SUBJECT.TABLE,
|
|
239
|
+
eventType: EVENT_TYPE.OPERATIONAL,
|
|
240
|
+
attributes: {
|
|
241
|
+
docSize: 22,
|
|
242
|
+
frameRate: 51,
|
|
243
|
+
isInitialSample: true,
|
|
244
|
+
nodeSize: 20,
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
expect(analyticsMock).toHaveBeenCalledWith({
|
|
249
|
+
action: TABLE_ACTION.RESIZE_PERF_SAMPLING,
|
|
250
|
+
actionSubject: ACTION_SUBJECT.TABLE,
|
|
251
|
+
eventType: EVENT_TYPE.OPERATIONAL,
|
|
252
|
+
attributes: {
|
|
253
|
+
docSize: 22,
|
|
254
|
+
frameRate: 53,
|
|
255
|
+
isInitialSample: false,
|
|
256
|
+
nodeSize: 20,
|
|
257
|
+
},
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
analyticsMock.mockReset();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test('calls useMeasureFramerate handlers', async () => {
|
|
264
|
+
const { container } = buildContainer({ layout: 'wide' });
|
|
265
|
+
|
|
266
|
+
fireEvent.mouseDown(container.querySelector('.resizer-handle-right')!);
|
|
267
|
+
fireEvent.mouseMove(container.querySelector('.resizer-handle-right')!, {
|
|
268
|
+
clientX: 100,
|
|
269
|
+
});
|
|
270
|
+
fireEvent.mouseUp(container.querySelector('.resizer-handle-right')!);
|
|
271
|
+
|
|
272
|
+
expect(mockStartMeasure).toHaveBeenCalled();
|
|
273
|
+
expect(mockEndMeasure).toHaveBeenCalled();
|
|
218
274
|
});
|
|
219
275
|
});
|
|
220
276
|
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react-hooks';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
ACTION_SUBJECT,
|
|
5
|
+
EVENT_TYPE,
|
|
6
|
+
TABLE_ACTION,
|
|
7
|
+
} from '@atlaskit/editor-common/analytics';
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
generateResizeFrameRatePayloads,
|
|
11
|
+
reduceResizeFrameRateSamples,
|
|
12
|
+
useMeasureFramerate,
|
|
13
|
+
} from '../../../plugins/table/utils/analytics';
|
|
14
|
+
|
|
15
|
+
describe('reduceResizeFrameRateSamples()', () => {
|
|
16
|
+
it('should return the same array if it has only one element', () => {
|
|
17
|
+
expect(reduceResizeFrameRateSamples([1])).toEqual([1]);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should return the first element and the average of the array if length > 1', () => {
|
|
21
|
+
expect(reduceResizeFrameRateSamples([3, 2, 4, 6])).toEqual([3, 4]);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('generateResizeFrameRatePayloads()', () => {
|
|
26
|
+
it('should return an empty array if the array is empty', () => {
|
|
27
|
+
expect(
|
|
28
|
+
generateResizeFrameRatePayloads({
|
|
29
|
+
docSize: 10,
|
|
30
|
+
frameRateSamples: [],
|
|
31
|
+
originalNode: { nodeSize: 5 } as any,
|
|
32
|
+
}),
|
|
33
|
+
).toEqual([]);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should return an array of payloads with the correct attributes', () => {
|
|
37
|
+
expect(
|
|
38
|
+
generateResizeFrameRatePayloads({
|
|
39
|
+
docSize: 10,
|
|
40
|
+
frameRateSamples: [3, 2, 4, 6],
|
|
41
|
+
originalNode: { nodeSize: 5 } as any,
|
|
42
|
+
}),
|
|
43
|
+
).toEqual([
|
|
44
|
+
{
|
|
45
|
+
action: TABLE_ACTION.RESIZE_PERF_SAMPLING,
|
|
46
|
+
actionSubject: ACTION_SUBJECT.TABLE,
|
|
47
|
+
eventType: EVENT_TYPE.OPERATIONAL,
|
|
48
|
+
attributes: {
|
|
49
|
+
frameRate: 3,
|
|
50
|
+
nodeSize: 5,
|
|
51
|
+
docSize: 10,
|
|
52
|
+
isInitialSample: true,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
action: TABLE_ACTION.RESIZE_PERF_SAMPLING,
|
|
57
|
+
actionSubject: ACTION_SUBJECT.TABLE,
|
|
58
|
+
eventType: EVENT_TYPE.OPERATIONAL,
|
|
59
|
+
attributes: {
|
|
60
|
+
frameRate: 4,
|
|
61
|
+
nodeSize: 5,
|
|
62
|
+
docSize: 10,
|
|
63
|
+
isInitialSample: false,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
]);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('useMeasureFramerate()', () => {
|
|
71
|
+
jest.useFakeTimers('modern');
|
|
72
|
+
|
|
73
|
+
it('should return the correct handlers', () => {
|
|
74
|
+
const { result } = renderHook(() => useMeasureFramerate());
|
|
75
|
+
const { startMeasure, endMeasure, countFrames } = result.current;
|
|
76
|
+
|
|
77
|
+
expect(startMeasure).toBeInstanceOf(Function);
|
|
78
|
+
expect(endMeasure).toBeInstanceOf(Function);
|
|
79
|
+
expect(countFrames).toBeInstanceOf(Function);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should return the correct frame rate sample', async () => {
|
|
83
|
+
const { result } = renderHook(() =>
|
|
84
|
+
useMeasureFramerate({ minTimeMs: 0, minFrames: 0, sampleRateMs: 0 }),
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const { startMeasure, endMeasure, countFrames } = result.current;
|
|
88
|
+
jest.advanceTimersByTime(100);
|
|
89
|
+
startMeasure();
|
|
90
|
+
jest.advanceTimersByTime(100);
|
|
91
|
+
countFrames();
|
|
92
|
+
const samples = endMeasure();
|
|
93
|
+
|
|
94
|
+
expect(samples).toEqual([10]);
|
|
95
|
+
|
|
96
|
+
jest.useRealTimers();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
version https://git-lfs.github.com/spec/v1
|
|
2
|
-
oid sha256:
|
|
3
|
-
size
|
|
2
|
+
oid sha256:1b71f23d11823e24b9a7ce319b38e1c2625f9d88301ebd4ce03074214202caa2
|
|
3
|
+
size 15206
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
version https://git-lfs.github.com/spec/v1
|
|
2
|
-
oid sha256:
|
|
3
|
-
size
|
|
2
|
+
oid sha256:eca71983bb60a79e2f607ec05ab3d99ff58bc8551b232219dcf24fd8e176fea3
|
|
3
|
+
size 32711
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
version https://git-lfs.github.com/spec/v1
|
|
2
|
-
oid sha256:
|
|
3
|
-
size
|
|
2
|
+
oid sha256:6f2033158fd0e520d5dff43ca6308b8312b2321e246ffb0604aee1de57368e6b
|
|
3
|
+
size 15136
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
version https://git-lfs.github.com/spec/v1
|
|
2
|
-
oid sha256:
|
|
3
|
-
size
|
|
2
|
+
oid sha256:559ec334bb7930be94de71631ca380ff968ac8d58ce4de6a3dc20881a1e48ab9
|
|
3
|
+
size 32765
|
|
@@ -8,12 +8,7 @@ import React, {
|
|
|
8
8
|
|
|
9
9
|
import rafSchd from 'raf-schd';
|
|
10
10
|
|
|
11
|
-
import {
|
|
12
|
-
ACTION_SUBJECT,
|
|
13
|
-
EVENT_TYPE,
|
|
14
|
-
TABLE_ACTION,
|
|
15
|
-
TableEventPayload,
|
|
16
|
-
} from '@atlaskit/editor-common/analytics';
|
|
11
|
+
import { TableEventPayload } from '@atlaskit/editor-common/analytics';
|
|
17
12
|
import { getGuidelinesWithHighlights } from '@atlaskit/editor-common/guideline';
|
|
18
13
|
import type { GuidelineConfig } from '@atlaskit/editor-common/guideline';
|
|
19
14
|
import {
|
|
@@ -25,21 +20,24 @@ import { resizerHandleShadowClassName } from '@atlaskit/editor-common/styles';
|
|
|
25
20
|
import { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
26
21
|
import { Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
27
22
|
import { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
28
|
-
import { TableMap } from '@atlaskit/editor-tables';
|
|
29
23
|
import { findTable } from '@atlaskit/editor-tables/utils';
|
|
30
24
|
|
|
31
25
|
import {
|
|
32
26
|
COLUMN_MIN_WIDTH,
|
|
33
27
|
getColgroupChildrenLength,
|
|
34
|
-
hasTableBeenResized,
|
|
35
28
|
previewScaleTable,
|
|
36
29
|
scaleTable,
|
|
37
30
|
} from '../pm-plugins/table-resizing/utils';
|
|
38
31
|
import { pluginKey as tableWidthPluginKey } from '../pm-plugins/table-width';
|
|
39
32
|
import { TABLE_HIGHLIGHT_GAP, TABLE_SNAP_GAP } from '../ui/consts';
|
|
40
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
generateResizedPayload,
|
|
35
|
+
generateResizeFrameRatePayloads,
|
|
36
|
+
useMeasureFramerate,
|
|
37
|
+
} from '../utils/analytics';
|
|
41
38
|
import { defaultGuidelines, defaultGuidelineWidths } from '../utils/guidelines';
|
|
42
39
|
import { findClosestSnap } from '../utils/snapping';
|
|
40
|
+
|
|
43
41
|
interface TableResizerProps {
|
|
44
42
|
width: number;
|
|
45
43
|
maxWidth: number;
|
|
@@ -57,29 +55,6 @@ interface TableResizerProps {
|
|
|
57
55
|
const handles = { right: true };
|
|
58
56
|
const tableHandleMarginTop = 12;
|
|
59
57
|
|
|
60
|
-
const generateResizedPayload = (props: {
|
|
61
|
-
originalNode: PMNode;
|
|
62
|
-
resizedNode: PMNode;
|
|
63
|
-
}): TableEventPayload => {
|
|
64
|
-
const tableMap = TableMap.get(props.resizedNode);
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
action: TABLE_ACTION.RESIZED,
|
|
68
|
-
actionSubject: ACTION_SUBJECT.TABLE,
|
|
69
|
-
eventType: EVENT_TYPE.TRACK,
|
|
70
|
-
attributes: {
|
|
71
|
-
newWidth: props.resizedNode.attrs.width,
|
|
72
|
-
prevWidth: props.originalNode.attrs.width ?? null,
|
|
73
|
-
nodeSize: props.resizedNode.nodeSize,
|
|
74
|
-
totalTableWidth: hasTableBeenResized(props.resizedNode)
|
|
75
|
-
? getTableWidth(props.resizedNode)
|
|
76
|
-
: null,
|
|
77
|
-
totalRowCount: tableMap.height,
|
|
78
|
-
totalColumnCount: tableMap.width,
|
|
79
|
-
},
|
|
80
|
-
};
|
|
81
|
-
};
|
|
82
|
-
|
|
83
58
|
const getResizerHandleHeight = (tableRef: HTMLTableElement) => {
|
|
84
59
|
const tableHeight = tableRef?.clientHeight;
|
|
85
60
|
let handleHeightSize: HandleHeightSizeType | undefined = 'small';
|
|
@@ -129,6 +104,8 @@ export const TableResizer = ({
|
|
|
129
104
|
const resizerMinWidth = getResizerMinWidth(node);
|
|
130
105
|
const handleHeightSize = getResizerHandleHeight(tableRef);
|
|
131
106
|
|
|
107
|
+
const { startMeasure, endMeasure, countFrames } = useMeasureFramerate();
|
|
108
|
+
|
|
132
109
|
const updateActiveGuidelines = useCallback(
|
|
133
110
|
({ gap, keys }: { gap: number; keys: string[] }) => {
|
|
134
111
|
if (gap !== currentGap.current) {
|
|
@@ -157,6 +134,8 @@ export const TableResizer = ({
|
|
|
157
134
|
);
|
|
158
135
|
|
|
159
136
|
const handleResizeStart = useCallback(() => {
|
|
137
|
+
startMeasure();
|
|
138
|
+
|
|
160
139
|
const {
|
|
161
140
|
dispatch,
|
|
162
141
|
state: { tr },
|
|
@@ -165,7 +144,7 @@ export const TableResizer = ({
|
|
|
165
144
|
dispatch(tr.setMeta(tableWidthPluginKey, { resizing: true }));
|
|
166
145
|
|
|
167
146
|
setSnappingEnabled(displayGuideline(defaultGuidelines));
|
|
168
|
-
}, [displayGuideline, editorView]);
|
|
147
|
+
}, [displayGuideline, editorView, startMeasure]);
|
|
169
148
|
|
|
170
149
|
const handleResizeStop = useCallback<HandleResize>(
|
|
171
150
|
(originalState, delta) => {
|
|
@@ -174,6 +153,18 @@ export const TableResizer = ({
|
|
|
174
153
|
const pos = getPos();
|
|
175
154
|
|
|
176
155
|
let tr = state.tr.setMeta(tableWidthPluginKey, { resizing: false });
|
|
156
|
+
const frameRateSamples = endMeasure();
|
|
157
|
+
|
|
158
|
+
if (frameRateSamples.length > 0) {
|
|
159
|
+
const resizeFrameRatePayloads = generateResizeFrameRatePayloads({
|
|
160
|
+
docSize: state.doc.nodeSize,
|
|
161
|
+
frameRateSamples,
|
|
162
|
+
originalNode: node,
|
|
163
|
+
});
|
|
164
|
+
resizeFrameRatePayloads.forEach((payload) => {
|
|
165
|
+
attachAnalyticsEvent(payload)?.(tr);
|
|
166
|
+
});
|
|
167
|
+
}
|
|
177
168
|
|
|
178
169
|
if (typeof pos === 'number') {
|
|
179
170
|
tr = tr.setNodeMarkup(pos, undefined, {
|
|
@@ -219,11 +210,13 @@ export const TableResizer = ({
|
|
|
219
210
|
tableRef,
|
|
220
211
|
displayGuideline,
|
|
221
212
|
attachAnalyticsEvent,
|
|
213
|
+
endMeasure,
|
|
222
214
|
],
|
|
223
215
|
);
|
|
224
216
|
|
|
225
217
|
const handleResize = useCallback(
|
|
226
218
|
(originalState, delta) => {
|
|
219
|
+
countFrames();
|
|
227
220
|
const newWidth = originalState.width + delta.width;
|
|
228
221
|
const pos = getPos();
|
|
229
222
|
if (typeof pos !== 'number') {
|
|
@@ -254,7 +247,15 @@ export const TableResizer = ({
|
|
|
254
247
|
|
|
255
248
|
return newWidth;
|
|
256
249
|
},
|
|
257
|
-
[
|
|
250
|
+
[
|
|
251
|
+
editorView,
|
|
252
|
+
getPos,
|
|
253
|
+
node,
|
|
254
|
+
tableRef,
|
|
255
|
+
updateWidth,
|
|
256
|
+
updateActiveGuidelines,
|
|
257
|
+
countFrames,
|
|
258
|
+
],
|
|
258
259
|
);
|
|
259
260
|
|
|
260
261
|
const scheduleResize = useMemo(() => rafSchd(handleResize), [handleResize]);
|
|
@@ -452,8 +452,6 @@ export const getToolbarConfig =
|
|
|
452
452
|
editorAnalyticsAPI,
|
|
453
453
|
);
|
|
454
454
|
|
|
455
|
-
const { tableCellOptionsInFloatingToolbar } =
|
|
456
|
-
getEditorFeatureFlags() || {};
|
|
457
455
|
const cellItems = getCellItems(
|
|
458
456
|
config,
|
|
459
457
|
state,
|
|
@@ -461,15 +459,8 @@ export const getToolbarConfig =
|
|
|
461
459
|
intl,
|
|
462
460
|
getEditorContainerWidth,
|
|
463
461
|
editorAnalyticsAPI,
|
|
464
|
-
tableCellOptionsInFloatingToolbar,
|
|
465
|
-
);
|
|
466
|
-
const colorPicker = getColorPicker(
|
|
467
|
-
state,
|
|
468
|
-
menu,
|
|
469
|
-
intl,
|
|
470
|
-
editorAnalyticsAPI,
|
|
471
|
-
tableCellOptionsInFloatingToolbar,
|
|
472
462
|
);
|
|
463
|
+
const colorPicker = getColorPicker(state, menu, intl, editorAnalyticsAPI);
|
|
473
464
|
|
|
474
465
|
// Check if we need to show confirm dialog for delete button
|
|
475
466
|
let confirmDialog;
|
|
@@ -590,24 +581,18 @@ const getCellItems = (
|
|
|
590
581
|
{ formatMessage }: ToolbarMenuContext,
|
|
591
582
|
getEditorContainerWidth: GetEditorContainerWidth,
|
|
592
583
|
editorAnalyticsAPI: EditorAnalyticsAPI | undefined | null,
|
|
593
|
-
tableCellOptionsInFloatingToolbar?: boolean,
|
|
594
584
|
): Array<FloatingToolbarItem<Command>> => {
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
getEditorContainerWidth,
|
|
607
|
-
editorAnalyticsAPI,
|
|
608
|
-
);
|
|
609
|
-
return [cellOptions, separator(cellOptions.hidden!)];
|
|
610
|
-
}
|
|
585
|
+
const initialSelectionRect = getClosestSelectionRect(state);
|
|
586
|
+
if (initialSelectionRect) {
|
|
587
|
+
const cellOptions = getToolbarCellOptionsConfig(
|
|
588
|
+
state,
|
|
589
|
+
view,
|
|
590
|
+
initialSelectionRect,
|
|
591
|
+
{ formatMessage },
|
|
592
|
+
getEditorContainerWidth,
|
|
593
|
+
editorAnalyticsAPI,
|
|
594
|
+
);
|
|
595
|
+
return [cellOptions, separator(cellOptions.hidden!)];
|
|
611
596
|
}
|
|
612
597
|
return [];
|
|
613
598
|
};
|
|
@@ -617,13 +602,9 @@ const getColorPicker = (
|
|
|
617
602
|
menu: FloatingToolbarItem<Command>,
|
|
618
603
|
{ formatMessage }: ToolbarMenuContext,
|
|
619
604
|
editorAnalyticsAPI: EditorAnalyticsAPI | null | undefined,
|
|
620
|
-
tableCellOptionsInFloatingToolbar?: boolean,
|
|
621
605
|
): Array<FloatingToolbarItem<Command>> => {
|
|
622
606
|
const { targetCellPosition, pluginConfig } = getPluginState(state);
|
|
623
|
-
if (
|
|
624
|
-
!pluginConfig.allowBackgroundColor ||
|
|
625
|
-
!tableCellOptionsInFloatingToolbar
|
|
626
|
-
) {
|
|
607
|
+
if (!pluginConfig.allowBackgroundColor) {
|
|
627
608
|
return [];
|
|
628
609
|
}
|
|
629
610
|
const node = targetCellPosition
|
|
@@ -53,7 +53,6 @@ export interface PluginConfig {
|
|
|
53
53
|
permittedLayouts?: PermittedLayoutsDescriptor;
|
|
54
54
|
allowControls?: boolean;
|
|
55
55
|
stickyHeaders?: boolean;
|
|
56
|
-
allowCellOptionsInFloatingToolbar?: boolean;
|
|
57
56
|
tableCellOptimization?: boolean;
|
|
58
57
|
tableRenderOptimization?: boolean;
|
|
59
58
|
stickyHeadersOptimization?: boolean;
|
|
@@ -1,13 +1,25 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
|
|
1
3
|
import type {
|
|
2
4
|
AnalyticsEventPayload,
|
|
3
5
|
AnalyticsEventPayloadCallback,
|
|
4
6
|
EditorAnalyticsAPI,
|
|
7
|
+
TableEventPayload,
|
|
8
|
+
} from '@atlaskit/editor-common/analytics';
|
|
9
|
+
import {
|
|
10
|
+
ACTION_SUBJECT,
|
|
11
|
+
EVENT_TYPE,
|
|
12
|
+
TABLE_ACTION,
|
|
5
13
|
} from '@atlaskit/editor-common/analytics';
|
|
6
14
|
import { HigherOrderCommand } from '@atlaskit/editor-common/types';
|
|
15
|
+
import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
7
16
|
import { Selection } from '@atlaskit/editor-prosemirror/state';
|
|
8
17
|
import { TableMap } from '@atlaskit/editor-tables/table-map';
|
|
9
18
|
import { findTable, getSelectionRect } from '@atlaskit/editor-tables/utils';
|
|
10
19
|
|
|
20
|
+
import { hasTableBeenResized } from '../pm-plugins/table-resizing/utils';
|
|
21
|
+
import { getTableWidth } from '../utils';
|
|
22
|
+
|
|
11
23
|
export function getSelectedTableInfo(selection: Selection) {
|
|
12
24
|
let map;
|
|
13
25
|
let totalRowCount = 0;
|
|
@@ -78,3 +90,159 @@ export const withEditorAnalyticsAPI =
|
|
|
78
90
|
view,
|
|
79
91
|
);
|
|
80
92
|
};
|
|
93
|
+
|
|
94
|
+
interface UseMeasureFramerateConfig {
|
|
95
|
+
maxSamples?: number;
|
|
96
|
+
minFrames?: number;
|
|
97
|
+
minTimeMs?: number;
|
|
98
|
+
sampleRateMs?: number;
|
|
99
|
+
timeoutMs?: number;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export const generateResizedPayload = (props: {
|
|
103
|
+
originalNode: PMNode;
|
|
104
|
+
resizedNode: PMNode;
|
|
105
|
+
}): TableEventPayload => {
|
|
106
|
+
const tableMap = TableMap.get(props.resizedNode);
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
action: TABLE_ACTION.RESIZED,
|
|
110
|
+
actionSubject: ACTION_SUBJECT.TABLE,
|
|
111
|
+
eventType: EVENT_TYPE.TRACK,
|
|
112
|
+
attributes: {
|
|
113
|
+
newWidth: props.resizedNode.attrs.width,
|
|
114
|
+
prevWidth: props.originalNode.attrs.width ?? null,
|
|
115
|
+
nodeSize: props.resizedNode.nodeSize,
|
|
116
|
+
totalTableWidth: hasTableBeenResized(props.resizedNode)
|
|
117
|
+
? getTableWidth(props.resizedNode)
|
|
118
|
+
: null,
|
|
119
|
+
totalRowCount: tableMap.height,
|
|
120
|
+
totalColumnCount: tableMap.width,
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export const reduceResizeFrameRateSamples = (frameRateSamples: number[]) => {
|
|
126
|
+
if (frameRateSamples.length > 1) {
|
|
127
|
+
const frameRateSum = frameRateSamples.reduce((sum, frameRate, index) => {
|
|
128
|
+
if (index === 0) {
|
|
129
|
+
return sum;
|
|
130
|
+
} else {
|
|
131
|
+
return sum + frameRate;
|
|
132
|
+
}
|
|
133
|
+
}, 0);
|
|
134
|
+
const averageFrameRate = Math.round(
|
|
135
|
+
frameRateSum / (frameRateSamples.length - 1),
|
|
136
|
+
);
|
|
137
|
+
return [frameRateSamples[0], averageFrameRate];
|
|
138
|
+
} else {
|
|
139
|
+
return frameRateSamples;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export const generateResizeFrameRatePayloads = (props: {
|
|
144
|
+
docSize: number;
|
|
145
|
+
frameRateSamples: number[];
|
|
146
|
+
originalNode: PMNode;
|
|
147
|
+
}): TableEventPayload[] => {
|
|
148
|
+
const reducedResizeFrameRateSamples = reduceResizeFrameRateSamples(
|
|
149
|
+
props.frameRateSamples,
|
|
150
|
+
);
|
|
151
|
+
return reducedResizeFrameRateSamples.map((frameRateSample, index) => ({
|
|
152
|
+
action: TABLE_ACTION.RESIZE_PERF_SAMPLING,
|
|
153
|
+
actionSubject: ACTION_SUBJECT.TABLE,
|
|
154
|
+
eventType: EVENT_TYPE.OPERATIONAL,
|
|
155
|
+
attributes: {
|
|
156
|
+
frameRate: frameRateSample,
|
|
157
|
+
nodeSize: props.originalNode.nodeSize,
|
|
158
|
+
docSize: props.docSize,
|
|
159
|
+
isInitialSample: index === 0,
|
|
160
|
+
},
|
|
161
|
+
}));
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Measures the framerate of a component over a given time period.
|
|
166
|
+
*/
|
|
167
|
+
export const useMeasureFramerate = (config: UseMeasureFramerateConfig = {}) => {
|
|
168
|
+
const {
|
|
169
|
+
maxSamples = 10,
|
|
170
|
+
minFrames = 5,
|
|
171
|
+
minTimeMs = 500,
|
|
172
|
+
sampleRateMs = 1000,
|
|
173
|
+
timeoutMs = 200,
|
|
174
|
+
} = config;
|
|
175
|
+
|
|
176
|
+
let frameCount = useRef(0);
|
|
177
|
+
let lastTime = useRef(0);
|
|
178
|
+
let timeoutId = useRef<NodeJS.Timeout | undefined>();
|
|
179
|
+
let frameRateSamples = useRef<number[]>([]);
|
|
180
|
+
|
|
181
|
+
useEffect(() => {
|
|
182
|
+
return () => {
|
|
183
|
+
if (timeoutId.current) {
|
|
184
|
+
clearTimeout(timeoutId.current);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}, []);
|
|
188
|
+
|
|
189
|
+
const startMeasure = () => {
|
|
190
|
+
frameCount.current = 0;
|
|
191
|
+
lastTime.current = performance.now();
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Returns an array of frame rate samples as integers.
|
|
196
|
+
*/
|
|
197
|
+
const endMeasure = () => {
|
|
198
|
+
const samples = frameRateSamples.current;
|
|
199
|
+
frameRateSamples.current = [];
|
|
200
|
+
return samples;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const sampleFrameRate = (delay = 0) => {
|
|
204
|
+
const currentTime = performance.now();
|
|
205
|
+
const deltaTime = currentTime - lastTime.current - delay;
|
|
206
|
+
const isValidSample =
|
|
207
|
+
deltaTime > minTimeMs && frameCount.current >= minFrames;
|
|
208
|
+
if (isValidSample) {
|
|
209
|
+
const frameRate = Math.round(frameCount.current / (deltaTime / 1000));
|
|
210
|
+
frameRateSamples.current.push(frameRate);
|
|
211
|
+
}
|
|
212
|
+
frameCount.current = 0;
|
|
213
|
+
lastTime.current = 0;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Counts the number of frames that occur within a given time period. Intended to be called
|
|
218
|
+
* inside a `requestAnimationFrame` callback.
|
|
219
|
+
*/
|
|
220
|
+
const countFrames = () => {
|
|
221
|
+
if (frameRateSamples.current.length >= maxSamples && timeoutId.current) {
|
|
222
|
+
clearTimeout(timeoutId.current);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Allows us to keep counting frames even if `startMeasure` is not called
|
|
228
|
+
*/
|
|
229
|
+
if (lastTime.current === 0) {
|
|
230
|
+
lastTime.current = performance.now();
|
|
231
|
+
}
|
|
232
|
+
frameCount.current++;
|
|
233
|
+
|
|
234
|
+
if (timeoutId.current) {
|
|
235
|
+
clearTimeout(timeoutId.current);
|
|
236
|
+
}
|
|
237
|
+
if (performance.now() - lastTime.current > sampleRateMs) {
|
|
238
|
+
sampleFrameRate();
|
|
239
|
+
} else {
|
|
240
|
+
timeoutId.current = setTimeout(
|
|
241
|
+
() => sampleFrameRate(timeoutMs),
|
|
242
|
+
timeoutMs,
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
return { startMeasure, endMeasure, countFrames };
|
|
248
|
+
};
|
package/tmp/api-report-tmp.d.ts
CHANGED