@atlaskit/editor-plugin-table
Version:
Table plugin for the @atlaskit/editor
296 lines (289 loc) • 11.2 kB
JavaScript
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
import { useEffect, useRef } from 'react';
import { ACTION_SUBJECT, EVENT_TYPE, TABLE_ACTION } from '@atlaskit/editor-common/analytics';
import { getBreakpointKey } from '@atlaskit/editor-common/utils/analytics';
import { TableMap } from '@atlaskit/editor-tables/table-map';
import { findTable, getSelectionRect } from '@atlaskit/editor-tables/utils';
import { hasTableBeenResized } from '../table-resizing/utils/colgroup';
import { getTableWidth } from './nodes';
export function getSelectedTableInfo(selection) {
var map;
var totalRowCount = 0;
var totalColumnCount = 0;
var table = findTable(selection);
if (table) {
map = TableMap.get(table.node);
totalRowCount = map.height;
totalColumnCount = map.width;
}
return {
table: table,
map: map,
totalRowCount: totalRowCount,
totalColumnCount: totalColumnCount
};
}
export function getSelectedCellInfo(selection) {
var horizontalCells = 1;
var verticalCells = 1;
var totalCells = 1;
var _getSelectedTableInfo = getSelectedTableInfo(selection),
table = _getSelectedTableInfo.table,
map = _getSelectedTableInfo.map,
totalRowCount = _getSelectedTableInfo.totalRowCount,
totalColumnCount = _getSelectedTableInfo.totalColumnCount;
if (table && map) {
var rect = getSelectionRect(selection);
if (rect) {
totalCells = map.cellsInRect(rect).length;
horizontalCells = rect.right - rect.left;
verticalCells = rect.bottom - rect.top;
}
}
return {
totalRowCount: totalRowCount,
totalColumnCount: totalColumnCount,
horizontalCells: horizontalCells,
verticalCells: verticalCells,
totalCells: totalCells
};
}
export var withEditorAnalyticsAPI = function withEditorAnalyticsAPI(payload) {
return function (editorAnalyticsAPI) {
return function (command) {
return function (state, dispatch, view) {
return command(state, function (tr) {
var dynamicPayload = payload instanceof Function ? payload(state) : payload;
if (dynamicPayload) {
editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent(dynamicPayload)(tr);
}
if (dispatch) {
dispatch(tr);
}
return true;
}, view);
};
};
};
};
export var generateResizedPayload = function generateResizedPayload(props) {
var _props$originalNode$a;
var tableMap = TableMap.get(props.resizedNode);
return {
action: TABLE_ACTION.RESIZED,
actionSubject: ACTION_SUBJECT.TABLE,
eventType: EVENT_TYPE.TRACK,
attributes: {
newWidth: props.resizedNode.attrs.width,
prevWidth: (_props$originalNode$a = props.originalNode.attrs.width) !== null && _props$originalNode$a !== void 0 ? _props$originalNode$a : null,
nodeSize: props.resizedNode.nodeSize,
totalTableWidth: hasTableBeenResized(props.resizedNode) ? getTableWidth(props.resizedNode) : null,
totalRowCount: tableMap.height,
totalColumnCount: tableMap.width
}
};
};
export var reduceResizeFrameRateSamples = function reduceResizeFrameRateSamples(frameRateSamples) {
if (frameRateSamples.length > 1) {
var frameRateSum = frameRateSamples.reduce(function (sum, frameRate, index) {
if (index === 0) {
return sum;
} else {
return sum + frameRate;
}
}, 0);
var averageFrameRate = Math.round(frameRateSum / (frameRateSamples.length - 1));
return [frameRateSamples[0], averageFrameRate];
} else {
return frameRateSamples;
}
};
export var generateResizeFrameRatePayloads = function generateResizeFrameRatePayloads(props) {
var reducedResizeFrameRateSamples = reduceResizeFrameRateSamples(props.frameRateSamples);
return reducedResizeFrameRateSamples.map(function (frameRateSample, index) {
return {
action: TABLE_ACTION.RESIZE_PERF_SAMPLING,
actionSubject: ACTION_SUBJECT.TABLE,
eventType: EVENT_TYPE.OPERATIONAL,
attributes: {
frameRate: frameRateSample,
nodeSize: props.originalNode.nodeSize,
docSize: props.docSize,
isInitialSample: index === 0
}
};
});
};
/**
* Measures the framerate of a component over a given time period.
*/
export var useMeasureFramerate = function useMeasureFramerate() {
var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var _config$maxSamples = config.maxSamples,
maxSamples = _config$maxSamples === void 0 ? 10 : _config$maxSamples,
_config$minFrames = config.minFrames,
minFrames = _config$minFrames === void 0 ? 5 : _config$minFrames,
_config$minTimeMs = config.minTimeMs,
minTimeMs = _config$minTimeMs === void 0 ? 500 : _config$minTimeMs,
_config$sampleRateMs = config.sampleRateMs,
sampleRateMs = _config$sampleRateMs === void 0 ? 1000 : _config$sampleRateMs,
_config$timeoutMs = config.timeoutMs,
timeoutMs = _config$timeoutMs === void 0 ? 200 : _config$timeoutMs;
var frameCount = useRef(0);
var lastTime = useRef(0);
var timeoutId = useRef();
var frameRateSamples = useRef([]);
useEffect(function () {
return function () {
if (timeoutId.current) {
clearTimeout(timeoutId.current);
}
};
}, []);
var startMeasure = function startMeasure() {
frameCount.current = 0;
lastTime.current = performance.now();
};
/**
* Returns an array of frame rate samples as integers.
*/
var endMeasure = function endMeasure() {
var samples = frameRateSamples.current;
frameRateSamples.current = [];
return samples;
};
var sampleFrameRate = function sampleFrameRate() {
var delay = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
var currentTime = performance.now();
var deltaTime = currentTime - lastTime.current - delay;
var isValidSample = deltaTime > minTimeMs && frameCount.current >= minFrames;
if (isValidSample) {
var frameRate = Math.round(frameCount.current / (deltaTime / 1000));
frameRateSamples.current.push(frameRate);
}
frameCount.current = 0;
lastTime.current = 0;
};
/**
* Counts the number of frames that occur within a given time period. Intended to be called
* inside a `requestAnimationFrame` callback.
*/
var countFrames = function countFrames() {
if (frameRateSamples.current.length >= maxSamples && timeoutId.current) {
clearTimeout(timeoutId.current);
return;
}
/**
* Allows us to keep counting frames even if `startMeasure` is not called
*/
if (lastTime.current === 0) {
lastTime.current = performance.now();
}
frameCount.current++;
if (timeoutId.current) {
clearTimeout(timeoutId.current);
}
if (performance.now() - lastTime.current > sampleRateMs) {
sampleFrameRate();
} else {
timeoutId.current = setTimeout(function () {
return sampleFrameRate(timeoutMs);
}, timeoutMs);
}
};
return {
startMeasure: startMeasure,
endMeasure: endMeasure,
countFrames: countFrames
};
};
var tableContainerNodes = new Set(['layoutSection', 'layoutColumn', 'expand', 'nestedExpand', 'extension', 'bodiedExtension', 'multiBodiedExtension', 'extensionFrame', 'table', 'tableCell', 'tableHeader', 'tableRow']);
export var getWidthInfoPayload = function getWidthInfoPayload(editorView, editorWidth) {
var tablesInfo = [];
editorView.state.doc.nodesBetween(0, editorView.state.doc.content.size, function (node, pos, parent) {
if (!tableContainerNodes.has(node.type.name)) {
return false;
}
if (node.type.name === 'table') {
var _domAtPos$node;
var domAtPos = editorView.domAtPos(pos + 1);
var table = (_domAtPos$node = domAtPos.node) === null || _domAtPos$node === void 0 ? void 0 : _domAtPos$node.parentElement;
var isNestedTable = (parent === null || parent === void 0 ? void 0 : parent.type.name) === 'tableCell' || (parent === null || parent === void 0 ? void 0 : parent.type.name) === 'tableHeader';
if (table instanceof HTMLTableElement) {
tablesInfo.push({
tableWidth: table.scrollWidth,
isNestedTable: isNestedTable,
hasScrollbar: table.parentElement ? (table === null || table === void 0 ? void 0 : table.parentElement.clientWidth) < table.scrollWidth : false
});
}
}
});
// only send the event if there are tables on the page
if (tablesInfo.length === 0) {
return undefined;
}
var maxTableWidth = Math.max.apply(Math, _toConsumableArray(tablesInfo.map(function (table) {
return table.tableWidth;
})));
return {
action: TABLE_ACTION.TABLE_WIDTH_INFO,
actionSubject: ACTION_SUBJECT.TABLE,
attributes: {
editorWidth: editorWidth,
editorWidthBreakpoint: getBreakpointKey(editorWidth),
hasTableWithScrollbar: tablesInfo.some(function (table) {
return table.hasScrollbar;
}),
hasTableWiderThanEditor: maxTableWidth > editorWidth,
maxTableWidthBreakpoint: getBreakpointKey(maxTableWidth),
tableWidthInfo: tablesInfo,
mode: 'editor'
},
eventType: EVENT_TYPE.OPERATIONAL
};
};
export var getHeightInfoPayload = function getHeightInfoPayload(editorView) {
var tablesInfo = [];
var editorPopupScrollParent = editorView.dom.closest('.fabric-editor-popup-scroll-parent');
// don't send the event if the editor scroll parent is not available
if (!editorPopupScrollParent) {
return undefined;
}
var editorScrollParentClientHeight = editorPopupScrollParent.clientHeight;
var isEditorScrollable = editorPopupScrollParent.scrollHeight > editorScrollParentClientHeight;
editorView.state.doc.nodesBetween(0, editorView.state.doc.content.size, function (node, pos, parent) {
if (!tableContainerNodes.has(node.type.name)) {
return false;
}
if (node.type.name === 'table') {
var _domAtPos$node2;
var domAtPos = editorView.domAtPos(pos + 1);
var table = (_domAtPos$node2 = domAtPos.node) === null || _domAtPos$node2 === void 0 ? void 0 : _domAtPos$node2.parentElement;
var isNestedTable = (parent === null || parent === void 0 ? void 0 : parent.type.name) === 'tableCell' || (parent === null || parent === void 0 ? void 0 : parent.type.name) === 'tableHeader';
if (table instanceof HTMLTableElement) {
tablesInfo.push({
isNestedTable: isNestedTable,
tableHeight: table.scrollHeight
});
}
}
});
// only send the event if there are tables on the page
if (tablesInfo.length === 0) {
return undefined;
}
var maxTableHeight = Math.max.apply(Math, _toConsumableArray(tablesInfo.map(function (table) {
return table.tableHeight;
})));
return {
action: TABLE_ACTION.TABLE_EDITOR_HEIGHT_INFO,
actionSubject: ACTION_SUBJECT.TABLE,
attributes: {
editorScrollParentClientHeight: editorScrollParentClientHeight,
isEditorScrollable: isEditorScrollable,
maxTableToEditorHeightRatio: maxTableHeight / editorScrollParentClientHeight,
tableHeightInfo: tablesInfo
},
eventType: EVENT_TYPE.OPERATIONAL
};
};