@airplane/views
Version:
A React library for building Airplane views. Views components are optimized in style and functionality to produce internal apps that are easy to build and maintain.
293 lines (292 loc) • 8.68 kB
JavaScript
import React__default from "react";
import { defaultColumn, actions, useGetLatest, makePropGetter, ensurePluginOrder, useMountedLayoutEffect } from "react-table";
defaultColumn.canResize = true;
actions.columnStartResizing = "columnStartResizing";
actions.columnResizing = "columnResizing";
actions.columnDoneResizing = "columnDoneResizing";
actions.resetResize = "resetResize";
const useResizeColumns = (hooks) => {
hooks.getResizerProps = [defaultGetResizerProps];
hooks.getHeaderProps.push({
style: {
position: "relative"
}
});
hooks.stateReducers.push(reducer);
hooks.useInstance.push(useInstance);
hooks.useInstanceBeforeDimensions.push(useInstanceBeforeDimensions);
};
function getFirstDefined(...args) {
for (let i = 0; i < args.length; i += 1) {
if (typeof args[i] !== "undefined") {
return args[i];
}
}
}
let passiveSupported = null;
function passiveEventSupported() {
if (typeof passiveSupported === "boolean")
return passiveSupported;
let supported = false;
try {
const options = {
get passive() {
supported = true;
return false;
}
};
window.addEventListener("test", null, options);
window.removeEventListener("test", null, options);
} catch (err) {
supported = false;
}
passiveSupported = supported;
return passiveSupported;
}
const defaultGetResizerProps = (props, {
instance,
header
}) => {
const {
dispatch,
visibleColumns,
rowOffsetWidth
} = instance;
const onResizeStart = (e, header2) => {
let isTouchEvent = false;
if (e.type === "touchstart") {
if (e.touches && e.touches.length > 1) {
return;
}
isTouchEvent = true;
}
const headersToResize = getLeafHeaders(header2);
const headerIdWidths = headersToResize.map((d) => [d.id, d.totalWidth]);
const clientX = isTouchEvent ? Math.round(e.touches[0].clientX) : e.clientX;
let raf;
let mostRecentClientX;
const dispatchEnd = () => {
window.cancelAnimationFrame(raf);
raf = null;
dispatch({
type: actions.columnDoneResizing
});
};
const dispatchMove = () => {
window.cancelAnimationFrame(raf);
raf = null;
dispatch({
type: actions.columnResizing,
clientX: mostRecentClientX
});
};
const scheduleDispatchMoveOnNextAnimationFrame = (clientXPos) => {
mostRecentClientX = clientXPos;
if (!raf) {
raf = window.requestAnimationFrame(dispatchMove);
}
};
const handlersAndEvents = {
mouse: {
moveEvent: "mousemove",
moveHandler: (e2) => scheduleDispatchMoveOnNextAnimationFrame(e2.clientX),
upEvent: "mouseup",
upHandler: (e2) => {
document.removeEventListener("mousemove", handlersAndEvents.mouse.moveHandler);
document.removeEventListener("mouseup", handlersAndEvents.mouse.upHandler);
dispatchEnd();
}
},
touch: {
moveEvent: "touchmove",
moveHandler: (e2) => {
if (e2.cancelable) {
e2.preventDefault();
e2.stopPropagation();
}
scheduleDispatchMoveOnNextAnimationFrame(e2.touches[0].clientX);
return false;
},
upEvent: "touchend",
upHandler: (e2) => {
document.removeEventListener(handlersAndEvents.touch.moveEvent, handlersAndEvents.touch.moveHandler);
document.removeEventListener(handlersAndEvents.touch.upEvent, handlersAndEvents.touch.moveHandler);
dispatchEnd();
}
}
};
const events = isTouchEvent ? handlersAndEvents.touch : handlersAndEvents.mouse;
const passiveIfSupported = passiveEventSupported() ? {
passive: false
} : false;
document.addEventListener(events.moveEvent, events.moveHandler, passiveIfSupported);
document.addEventListener(events.upEvent, events.upHandler, passiveIfSupported);
dispatch({
type: actions.columnStartResizing,
columnId: header2.id,
columnWidth: header2.totalWidth,
headerIdWidths,
clientX,
// CHANGE: we dispatch the start-resizing event including both the real
// row width, as well as the sum of the flex width (to compute the ratio between them).
rowOffsetWidth,
totalFlexWidth: visibleColumns.reduce((sum, col) => sum + col.totalFlexWidth, 0)
});
};
return [props, {
onMouseDown: (e) => e.persist() || onResizeStart(e, header),
onTouchStart: (e) => e.persist() || onResizeStart(e, header),
style: {
cursor: "col-resize"
},
draggable: false,
role: "separator"
}];
};
useResizeColumns.pluginName = "useResizeColumns";
function reducer(state, action) {
if (action.type === actions.init) {
return {
columnResizing: {
columnWidths: {}
},
...state
};
}
if (action.type === actions.resetResize) {
return {
...state,
columnResizing: {
columnWidths: {}
}
};
}
if (action.type === actions.columnStartResizing) {
const {
clientX,
columnId,
columnWidth,
headerIdWidths,
rowOffsetWidth,
totalFlexWidth
} = action;
return {
...state,
columnResizing: {
...state.columnResizing,
startX: clientX,
headerIdWidths,
columnWidth,
isResizingColumn: columnId,
rowOffsetWidth,
totalFlexWidth
}
};
}
if (action.type === actions.columnResizing) {
const {
clientX
} = action;
const {
startX,
columnWidth,
headerIdWidths = [],
rowOffsetWidth
} = state.columnResizing;
const deltaX = clientX - startX;
const actualColumnWidth = state.columnResizing.totalFlexWidth !== 0 ? columnWidth * rowOffsetWidth / state.columnResizing.totalFlexWidth : columnWidth;
const targetColumnWidth = deltaX + actualColumnWidth;
const columnWidthDelta = rowOffsetWidth !== targetColumnWidth ? (targetColumnWidth * state.columnResizing.totalFlexWidth - rowOffsetWidth * columnWidth) / (rowOffsetWidth - targetColumnWidth) : deltaX;
const newColumnWidths = {};
headerIdWidths.forEach(([headerId, headerWidth]) => {
newColumnWidths[headerId] = Math.max(headerWidth + columnWidthDelta, 0);
});
return {
...state,
columnResizing: {
...state.columnResizing,
columnWidths: {
...state.columnResizing.columnWidths,
...newColumnWidths
}
}
};
}
if (action.type === actions.columnDoneResizing) {
return {
...state,
columnResizing: {
...state.columnResizing,
startX: null,
isResizingColumn: null
}
};
}
}
const useInstanceBeforeDimensions = (instance) => {
const {
flatHeaders,
disableResizing,
getHooks,
state: {
columnResizing
}
} = instance;
const getInstance = useGetLatest(instance);
flatHeaders.forEach((header) => {
const canResize = getFirstDefined(header.disableResizing === true ? false : void 0, disableResizing === true ? false : void 0, true);
header.canResize = canResize;
if (columnResizing.columnWidths[header.id] > 0) {
header.width = columnResizing.columnWidths[header.id];
} else if (columnResizing.columnWidths[header.id] === 0) {
header.width = header.minWidth || header.originalWidth || header.width;
} else {
header.width = header.originalWidth || header.width;
}
header.isResizing = columnResizing.isResizingColumn === header.id;
if (canResize) {
header.getResizerProps = makePropGetter(getHooks().getResizerProps, {
instance: getInstance(),
header
});
}
});
};
function useInstance(instance) {
const {
plugins,
dispatch,
autoResetResize = true,
columns
} = instance;
ensurePluginOrder(plugins, ["useAbsoluteLayout"], "useResizeColumns");
const getAutoResetResize = useGetLatest(autoResetResize);
useMountedLayoutEffect(() => {
if (getAutoResetResize()) {
dispatch({
type: actions.resetResize
});
}
}, [columns]);
const resetResizing = React__default.useCallback(() => dispatch({
type: actions.resetResize
}), [dispatch]);
Object.assign(instance, {
resetResizing
});
}
function getLeafHeaders(header) {
const leafHeaders = [];
const recurseHeader = (header2) => {
if (header2.columns && header2.columns.length) {
header2.columns.map(recurseHeader);
}
leafHeaders.push(header2);
};
recurseHeader(header);
return leafHeaders;
}
export {
useResizeColumns
};
//# sourceMappingURL=useResizeColumns.js.map