UNPKG

react-select-table

Version:
1,510 lines (1,457 loc) 95 kB
var __defProp = Object.defineProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; // src/components/Table.jsx import React21, { useEffect as useEffect5 } from "react"; // src/models/Hooks.js import { useMemo, useCallback } from "react"; import { createDispatchHook, createSelectorHook, createStoreHook } from "react-redux"; import { bindActionCreators } from "redux"; // src/utils/classUtils.js function bindPrototypeMethods(instance4) { const propertyNames = Object.getOwnPropertyNames(Object.getPrototypeOf(instance4)); for (const name of propertyNames) { if (name === "constructor") continue; if (typeof instance4[name] !== "function") continue; instance4[name] = instance4[name].bind(instance4); } } // src/models/Hooks.js var Hooks = class { constructor(options, selectors, actions) { bindPrototypeMethods(this); const { context } = options; this.actions = actions; this.selectors = selectors; this.useRootSelector = createSelectorHook(context); this.useDispatch = createDispatchHook(context); this.useStore = createStoreHook(context); } useSelector(selector, equalityFn) { return this.useRootSelector((state) => selector(this.selectors.getTableState(state)), equalityFn); } useGetState() { const store = this.useStore(); return useCallback(() => this.selectors.getTableState(store.getState()), [store]); } useActions(metadata = null) { const dispatch = this.useDispatch(); return useMemo(() => { const dispatchWithExtra = (action) => dispatch(Object.assign(action, metadata)); return bindActionCreators(this.actions, dispatchWithExtra); }, [dispatch, metadata]); } }; // src/constants/actionTypes.js var types = { SET_ITEMS: "", ADD_ITEMS: "", DELETE_ITEMS: "", PATCH_ITEMS: "", PATCH_ITEMS_BY_KEY: "", CLEAR_ITEMS: "", SORT_ITEMS: "", REPLACE_ITEMS: "", SET_ITEM_FILTER: "", SET_ERROR: "", START_LOADING: "", SET_SELECTED: "", SELECT: "", CLEAR_SELECTION: "", SELECT_ALL: "", SET_ACTIVE: "", SEARCH: "", GO_TO_MATCH: "", SET_PAGE_SIZE: "", DEBUG: "" }; for (const name in types) types[name] = `RST_${name}`; var actionTypes_default = Object.freeze(types); // src/constants/enums.js var DragModes = Object.freeze({ Resize: "resize", Select: "select" }); var GestureTargetTypes = Object.freeze({ None: "none", Header: "header", BelowRows: "belowRows", Row: "row" }); var SortOrders = Object.freeze({ Ascending: true, Descending: false, None: null, Toggle: "toggle" }); // src/models/Actions.js var Actions = class { constructor(namespace) { bindPrototypeMethods(this); this.getAction = (type, payload = null) => ({ namespace, type, payload }); } search(phrase) { return this.getAction(actionTypes_default.SEARCH, { phrase }); } goToMatch(index) { return this.getAction(actionTypes_default.GO_TO_MATCH, { index }); } setPageSize(size) { return this.getAction(actionTypes_default.SET_PAGE_SIZE, { size }); } clearItems() { return this.getAction(actionTypes_default.CLEAR_ITEMS); } setItemFilter(filter) { return this.getAction(actionTypes_default.SET_ITEM_FILTER, { filter }); } patchItems(...patches) { return this.getAction(actionTypes_default.PATCH_ITEMS, { patches }); } patchItemsByKey(patchMap) { return this.getAction(actionTypes_default.PATCH_ITEMS_BY_KEY, { patchMap }); } deleteItems(...keys) { return this.getAction(actionTypes_default.DELETE_ITEMS, { keys }); } addItems(...items) { return this.getAction(actionTypes_default.ADD_ITEMS, { items }); } setItems(items) { return this.getAction(actionTypes_default.SET_ITEMS, { items }); } replaceItems(items) { return this.getAction(actionTypes_default.REPLACE_ITEMS, { items }); } sortItems(path, addToPrev = false, order = SortOrders.Toggle) { return this.getAction(actionTypes_default.SORT_ITEMS, { path, addToPrev, order }); } select(index, isRange = false, addToPrev = false) { return this.getAction(actionTypes_default.SELECT, { index, addToPrev, isRange }); } clearSelection() { return this.getAction(actionTypes_default.CLEAR_SELECTION); } setActive(index) { return this.getAction(actionTypes_default.SET_ACTIVE, { index }); } selectAll() { return this.getAction(actionTypes_default.SELECT_ALL); } setSelected(map, activeIndex = null, pivotIndex = null) { return this.getAction(actionTypes_default.SET_SELECTED, { map, activeIndex, pivotIndex }); } setError(error) { return this.getAction(actionTypes_default.SET_ERROR, { error }); } startLoading() { return this.getAction(actionTypes_default.START_LOADING); } }; // src/types/TableProps.js import PropTypes from "prop-types"; import React from "react"; var columnShape = { title: PropTypes.string, path: PropTypes.string, key: PropTypes.string, render: PropTypes.func, isHeader: PropTypes.bool, defaultWidth: PropTypes.number }; var componentEventHandlersPropTypes = { onColumnResizeEnd: PropTypes.func, onKeyDown: PropTypes.func, onItemsOpen: PropTypes.func }; var reduxEventHandlersPropTypes = { onContextMenu: PropTypes.func, onSelectionChange: PropTypes.func, onActionDispatched: PropTypes.func }; var commonTablePropTypes = { ...componentEventHandlersPropTypes, namespace: PropTypes.string.isRequired, columns: PropTypes.arrayOf(PropTypes.shape(columnShape)).isRequired, initColumnWidths: PropTypes.objectOf(PropTypes.number), errorComponent: PropTypes.elementType, paginationComponent: PropTypes.elementType, loadingIndicator: PropTypes.node, emptyPlaceholder: PropTypes.node, id: PropTypes.string, className: PropTypes.string, autoFocus: PropTypes.bool, dragSelectScrollFactor: PropTypes.number, columnResizeScrollFactor: PropTypes.number, getRowClassName: PropTypes.func }; var tablePropTypes = { ...commonTablePropTypes, ...reduxEventHandlersPropTypes }; var slaveTablePropTypes = { ...commonTablePropTypes, name: PropTypes.string.isRequired }; // src/models/Events.js import _ from "lodash"; var handlersSymbol = Symbol("Event handlers"); var noopEventHandler = () => { }; var getNoopHandlers = () => _.mapValues(reduxEventHandlersPropTypes, _.constant(noopEventHandler)); var Events = class { constructor(selectors) { bindPrototypeMethods(this); this.selectors = selectors; this[handlersSymbol] = getNoopHandlers(); } hasListener(event) { return this[handlersSymbol][event] !== noopEventHandler; } selectionChange(state) { this[handlersSymbol].onSelectionChange(this.selectors.getSelectionArg(state)); } contextMenu(state, forceEmpty, forceSelection) { this[handlersSymbol].onContextMenu(this.selectors.getContextMenuArg(state, forceEmpty, forceSelection)); } actionDispatched(internal = false) { this[handlersSymbol].onActionDispatched(internal); } }; // src/models/Selectors.js import _5 from "lodash"; // src/constants/saveModules.js var saveModules_exports = {}; __export(saveModules_exports, { Active: () => Active, Filter: () => Filter, Items: () => Items, Pagination: () => Pagination, Pivot: () => Pivot, Search: () => Search, Selection: () => Selection, SortOrder: () => SortOrder }); // src/utils/flagUtils.js var flagUtils_exports = {}; __export(flagUtils_exports, { addFlag: () => addFlag, flagGenerator: () => flagGenerator, hasFlag: () => hasFlag, removeFlag: () => removeFlag, toggleFlag: () => toggleFlag }); import _2 from "lodash"; function flagGenerator() { let power = 0; return () => Math.pow(2, power++); } function hasFlag(flags2, flag) { return (flags2 & flag) === flag; } function addFlag(flags2, flag) { return flags2 | flag; } function removeSingleFlag(flags2, flag) { return flags2 & ~flag; } function isolateFlag(flag) { return 1 << 31 - Math.clz32(flag); } function getDependant(flags2, flag) { return _2.filter(flags2, (f) => hasFlag(f, flag)).map(isolateFlag); } function removeFlag(flags2, flag, allFlags = [flag]) { const dependant = getDependant(allFlags, flag); return _2.reduce(dependant, removeSingleFlag, flags2); } function toggleFlag(flags2, flag, enabled, allFlags = [flag]) { if (enabled) return addFlag(flags2, flag); return removeFlag(flags2, flag, allFlags); } // src/constants/saveModules.js var nextFlag = flagGenerator(); var Filter = nextFlag(); var SortOrder = nextFlag(); var Items = nextFlag(); var Pagination = nextFlag(); var Active = nextFlag() | Items | Filter | SortOrder; var Search = nextFlag() | Active; var Selection = nextFlag() | Items; var Pivot = nextFlag() | Active | Selection; // src/utils/dlMapUtils.js var dlMapUtils_exports = {}; __export(dlMapUtils_exports, { addUnlinkedItem: () => addUnlinkedItem, deleteItem: () => deleteItem, getItem: () => getItem, getItemMetadata: () => getItemMetadata, getItems: () => getItems, getKeyedItems: () => getKeyedItems, instance: () => instance, keyIterator: () => keyIterator, sortAndLinkItems: () => sortAndLinkItems, sortItems: () => sortItems }); import _3 from "lodash"; function instance() { return { headKey: null, tailKey: null, nodes: {} }; } var getItems = (map) => _3.map(map.nodes, (node) => node.value); var getKeyedItems = (map) => _3.mapValues(map.nodes, (node) => node.value); var getItem = (map, key) => { var _a; return (_a = map.nodes[key]) == null ? void 0 : _a.value; }; var getItemMetadata = (map, key) => { var _a; return (_a = map.nodes[key]) == null ? void 0 : _a.metadata; }; var getNextKey = (map, key) => key == null ? map.headKey : map.nodes[key].nextKey; var getPrevKey = (map, key) => key == null ? map.tailKey : map.nodes[key].prevKey; function* keyIterator(map, forward = true, key = null) { const _getNextKey = forward ? getNextKey : getPrevKey; while (true) { key = _getNextKey(map, key); if (key == null) return; yield key; } } function setNextItem(map, key, nextKey) { const { [nextKey]: nextNode, [key]: node } = map.nodes; if (nextNode) nextNode.prevKey = key; else map.tailKey = key; if (node) node.nextKey = nextKey; else map.headKey = nextKey; } function linkItem(map, prevKey, key, nextKey) { setNextItem(map, prevKey, key); setNextItem(map, key, nextKey); } function sortAndLinkItems(map, keys, keyComparator) { keys = keys.sort(keyComparator); const linkedKeys = keyIterator(map); let linkedKeyNext = linkedKeys.next(); let keyIndex = 0; while (!linkedKeyNext.done && keyIndex < keys.length) { const key = keys[keyIndex]; const linkedKey = linkedKeyNext.value; if (keyComparator(linkedKey, key) > 0) { const linkedNode = map.nodes[linkedKey]; linkItem(map, linkedNode.prevKey, key, linkedKey); keyIndex++; } else linkedKeyNext = linkedKeys.next(); } for (; keyIndex < keys.length; keyIndex++) linkItem(map, map.tailKey, keys[keyIndex], null); } function deleteItem(map, key) { const node = map.nodes[key]; if (!node) return; setNextItem(map, node.prevKey, node.nextKey); delete map.nodes[key]; return node.value; } function addUnlinkedItem(map, key, value, metadata) { const isReplacing = deleteItem(map, key) !== void 0; map.nodes[key] = { metadata, value, prevKey: null, nextKey: null }; return isReplacing; } function sortItems(map, keyComparator) { const keys = [...keyIterator(map)].sort(keyComparator); let prevKey = null; for (const key of keys) { setNextItem(map, prevKey, key); prevKey = key; } setNextItem(map, prevKey, null); } // src/utils/setUtils.js var setUtils_exports = {}; __export(setUtils_exports, { addItem: () => addItem, getItems: () => getItems2, hasItem: () => hasItem, instance: () => instance2, isEmpty: () => isEmpty, isEqual: () => isEqual, removeItem: () => removeItem, toggleItem: () => toggleItem }); import _4 from "lodash"; function instance2() { return {}; } function removeItem(set, value) { delete set[value]; } function addItem(set, value) { set[value] = true; } function toggleItem(set, value, exists) { const action = exists ? addItem : removeItem; action(set, value); } function hasItem(set, value) { return set[value] === true; } function getItems2(set) { return Object.keys(set); } function isEmpty(set) { return _4.isEmpty(set); } function isEqual(setA, setB) { return _4.isEqual(setA, setB); } // src/models/Selectors.js var moduleProperties = { [Items]: ["isLoading", "error", "items"], [Selection]: "selected", [Filter]: "filter", [SortOrder]: "sortAscending", [Pagination]: "pageSize", [Active]: "activeIndex", [Pivot]: "pivotIndex", [Search]: "searchPhrase" }; var Selectors = class { constructor(options) { bindPrototypeMethods(this); this.options = options; } getTableState(state) { return this.options.statePath ? _5.get(state, this.options.statePath) : state; } getPageSize(state) { return state.pageSize || state.visibleItemCount; } getPageCount(state) { return Math.ceil(state.visibleItemCount / this.getPageSize(state)); } getItemPageIndex(state, itemIndex) { return Math.floor(itemIndex / this.getPageSize(state)); } getPageIndex(state) { return this.getItemPageIndex(state, state.activeIndex); } getPageIndexOffset(state) { return this.getPageIndex(state) * state.pageSize; } getSaveState(state, modules) { const saveState = {}; for (const module in moduleProperties) { if (!hasFlag(modules, parseInt(module))) continue; Object.assign(saveState, _5.pick(state, moduleProperties[module])); } if (saveState.items) saveState.items = getItems(saveState.items); if (saveState.selected) saveState.selected = getItems2(saveState.selected); return saveState; } getActiveRowIndex(state) { return state.activeIndex % this.getPageSize(state); } getActiveKey(state) { return state.rowKeys[this.getActiveRowIndex(state)]; } getSelected(state, rowIndex) { return hasItem(state.selected, state.rowKeys[rowIndex]); } getIsStateNormal(state) { return !state.isLoading && !state.error; } getSelection(state) { return this.getIsStateNormal(state) ? state.selected : instance2(); } getSelectionArg(state) { var _a; const selectedKeys = getItems2(this.getSelection(state)); if (this.options.multiSelect) return new Set(selectedKeys); return (_a = selectedKeys[0]) != null ? _a : null; } getContextMenuArg(state, forceEmpty = false, forceSelection = false) { const { listBox, multiSelect } = this.options; const activeKey = this.getActiveKey(state); if (forceEmpty || activeKey == null) return multiSelect ? /* @__PURE__ */ new Set() : null; if (forceSelection || !listBox) return this.getSelectionArg(state); return multiSelect ? /* @__PURE__ */ new Set([activeKey]) : activeKey; } }; // src/utils/optionsUtils.js import _6 from "lodash"; var defaultOptions = { itemPredicate: _6.isMatch, itemComparator: () => { }, searchPhraseParser: (phrase) => phrase.normalize("NFD").toLowerCase(), searchProperty: "", multiSelect: true, listBox: false, minColumnWidth: 50, constantWidth: false, statePath: "", savedState: {}, context: void 0, keyBy: "_id", chunkSize: 10 }; function getOptions(options) { return Object.freeze(_6.defaults(options, defaultOptions)); } function setDefaultOptions(options) { Object.assign(defaultOptions, options); } // src/models/Utils.js var Utils = class { constructor(namespace, options) { this.options = getOptions(options); this.actions = new Actions(namespace); this.selectors = new Selectors(this.options); this.events = new Events(this.selectors); this.hooks = new Hooks(this.options, this.selectors, this.actions); } }; // src/utils/tableUtils.js var px = (n) => `${n}px`; var pc = (n) => `${n}%`; var negative = (n) => -Math.abs(n); var tableUtils = {}; function createTableUtils(namespace, options) { return tableUtils[namespace] = new Utils(namespace, options); } function getTableUtils(namespace) { return tableUtils[namespace]; } // src/components/Root.jsx import React19, { useCallback as useCallback11, useEffect as useEffect4, useMemo as useMemo5, useRef as useRef6 } from "react"; // src/components/ScrollingContainer.jsx import _16 from "lodash"; import React16, { useCallback as useCallback8, useContext as useContext7, useEffect as useEffect3, useLayoutEffect as useLayoutEffect4, useMemo as useMemo4, useRef as useRef5, useState } from "react"; // src/components/ResizingContainer.jsx import React15, { Fragment as Fragment2, useContext as useContext6, useCallback as useCallback5 } from "react"; import _14 from "lodash"; // src/components/TableBody.jsx import React10, { useMemo as useMemo2 } from "react"; // src/components/ColGroup.jsx import React3, { useContext } from "react"; // src/context/ColumnGroup.js import React2 from "react"; var ColumnGroupContext = React2.createContext(null); ColumnGroupContext.displayName = "ColumnGroupContext"; var ColumnGroup_default = ColumnGroupContext; // src/components/ColGroup.jsx import _7 from "lodash"; var ColGroup = ({ name, columns }, ref) => { const { widths, widthUnit, containerWidth } = useContext(ColumnGroup_default); const factor = containerWidth > 100 ? 100 / _7.min(_7.values(widths)) : 1; return /* @__PURE__ */ React3.createElement("colgroup", { ref }, columns.map( ({ key }) => /* @__PURE__ */ React3.createElement("col", { key: `col_${name}_${key}`, style: { width: widthUnit(widths[key] * factor) }, "data-col-key": key }) ), /* @__PURE__ */ React3.createElement("col", { className: "rst-endCap" }), /* @__PURE__ */ React3.createElement("col", { className: "rst-spacer" })); }; var ColGroup_default = React3.forwardRef(ColGroup); // src/components/TableBody.jsx import _12 from "lodash"; // src/components/ChunkObserver.jsx import React9, { useCallback as useCallback3, useRef, useEffect, useContext as useContext3, useLayoutEffect as useLayoutEffect2 } from "react"; // src/components/TableChunk.jsx import React8, { useLayoutEffect } from "react"; // src/components/TableRow.jsx import React7 from "react"; // src/components/TableCell.jsx import _8 from "lodash"; import React4 from "react"; var TableCell = ({ render, data, rowIndex, path, isHeader }) => { const options = { className: "" }; const defaultContent = path ? _8.get(data, path) : rowIndex + 1; const content = render(defaultContent, data, options); const CellType = isHeader ? "th" : "td"; return /* @__PURE__ */ React4.createElement(CellType, { className: "rst-cell " + options.className }, content); }; var TableCell_default = TableCell; // src/hoc/withGestures.jsx import React6, { useCallback as useCallback2, useContext as useContext2 } from "react"; // src/context/GestureTarget.js import React5 from "react"; var GestureContext = React5.createContext(null); GestureContext.displayName = "GestureContext"; var GestureTarget_default = GestureContext; // src/hoc/withGestures.jsx import _9 from "lodash"; function withGestures(Component) { return function WithGestures({ gestureTarget, onDualTap, onDualTapDirect, ...props }) { const gesture = useContext2(GestureTarget_default); props.handleGestureTouchStart = useCallback2((e) => { if (gesture.isDragging) return; if (e.touches.length !== 2) return; if (_9.every(e.touches, (t) => e.currentTarget === t.target) && (onDualTapDirect == null ? void 0 : onDualTapDirect(e)) === false || _9.every(e.touches, (t) => e.currentTarget.contains(t.target)) && (onDualTap == null ? void 0 : onDualTap(e)) === false) e.stopPropagation(); }, [gesture, onDualTap, onDualTapDirect]); props.handleGesturePointerDownCapture = useCallback2(() => { if (!gestureTarget) return; gesture.target = gestureTarget; }, [gesture, gestureTarget]); return /* @__PURE__ */ React6.createElement(Component, { ...props }); }; } // src/utils/dataAttributeUtils.js import _10 from "lodash"; var getFlagAttribute = (flagName) => "data-is-" + flagName; var getFlagAttributes = (flagNames) => _10.mapValues(flagNames, getFlagAttribute); function dataAttributeFlags(flags2) { const attributes = _10.mapKeys(flags2, (enabled, name) => getFlagAttribute(name)); return _10.mapValues(attributes, (enabled) => enabled ? "" : void 0); } // src/components/TableRow.jsx var flags = { Selected: "selected", Active: "active" }; var RowAttributes = getFlagAttributes(flags); function getRowBounds(row) { if (!row) return null; const { offsetHeight: height, offsetTop: top } = row; if (!height) return null; return { top, bottom: top + height }; } var TableRow = ({ handleGesturePointerDownCapture, handleGestureTouchStart, columns, name, getRowClassName, row: { index, key, selected, active, data, ref } }) => { const renderColumn = ({ key: colKey, ...column }) => /* @__PURE__ */ React7.createElement(TableCell_default, { ...column, data, rowIndex: index, key: `cell_${name}_${key}_${colKey}` }); return /* @__PURE__ */ React7.createElement("tr", { ref, className: "rst-row " + getRowClassName(data, key), onPointerDownCapture: handleGesturePointerDownCapture, onTouchStart: handleGestureTouchStart, ...dataAttributeFlags({ [flags.Active]: active, [flags.Selected]: selected }) }, columns.map(renderColumn), /* @__PURE__ */ React7.createElement("td", { className: "rst-endCap" }), /* @__PURE__ */ React7.createElement("td", { className: "rst-spacer" })); }; var TableRow_default = withGestures(TableRow); // src/models/GestureTarget.js function GestureTarget(type, index = -1) { return { type, index }; } // src/utils/memoUtils.js import _11 from "lodash"; function comparePropsDeep(prev, next) { return _11.isEqualWith(prev, next, (pv, nv, key, po) => { if (po === prev && key.endsWith("Ref")) return true; }); } // src/components/TableChunk.jsx function TableChunk(props, ref) { const { rows, contextMenu, refresh, ...rowCommonProps } = props; useLayoutEffect(refresh); const renderRow = (row) => { const rowProps = { ...rowCommonProps, row, key: `row_${props.name}_${row.key}`, gestureTarget: GestureTarget(GestureTargetTypes.Row, row.index), onDualTap: contextMenu }; return /* @__PURE__ */ React8.createElement(TableRow_default, { ...rowProps }); }; return /* @__PURE__ */ React8.createElement("tbody", { className: "rst-chunk", ref }, rows.map(renderRow)); } var TableChunk_default = React8.memo(React8.forwardRef(TableChunk), comparePropsDeep); // src/components/ChunkObserver.jsx var HiddenAttribute = getFlagAttribute("hidden"); var LastWidthAttribute = "data-last-width"; function ChunkObserver(props) { const { chunkObserverRef, indexOffset, utils: { hooks, selectors }, ...chunkProps } = props; const activeRowIndex = hooks.useSelector(selectors.getActiveRowIndex); const chunkRef = useRef(); const isRefreshingRef = useRef(false); const { resizingIndex } = useContext3(ColumnGroup_default); const refreshChunk = useCallback3(() => { const observer = chunkObserverRef.current; if (!observer) return; const chunk = chunkRef.current; const isHidden = chunk.hasAttribute(HiddenAttribute); if (!isHidden) return; observer.unobserve(chunk); chunk.toggleAttribute(HiddenAttribute, false); isRefreshingRef.current = true; }, [chunkObserverRef]); useEffect(() => { if (!isRefreshingRef.current) return; chunkObserverRef.current.observe(chunkRef.current); isRefreshingRef.current = false; }); useLayoutEffect2(() => { if (resizingIndex >= 0) return; refreshChunk(); }, [resizingIndex, refreshChunk]); useLayoutEffect2(() => { if (activeRowIndex <= indexOffset) return; const chunk = chunkRef.current; const lastWidth = chunk.getAttribute(LastWidthAttribute); const width = chunk.clientWidth.toString(); if (width === lastWidth) return; refreshChunk(); }, [activeRowIndex, indexOffset, refreshChunk]); useEffect(() => { const observer = chunkObserverRef.current; if (!observer) return; const chunk = chunkRef.current; observer.observe(chunk); return () => observer.unobserve(chunk); }, [chunkObserverRef]); return /* @__PURE__ */ React9.createElement(TableChunk_default, { ref: chunkRef, refresh: refreshChunk, ...chunkProps }); } var ChunkObserver_default = ChunkObserver; // src/components/TableBody.jsx function TableBody(props) { const { showPlaceholder, tableBodyRef, ...chunkCommonProps } = props; const { columns, name, utils: { hooks, selectors, options } } = props; const rowKeys = hooks.useSelector((s) => s.rowKeys); const selected = hooks.useSelector((s) => s.selected); const items = hooks.useSelector((s) => s.items); const activeRowIndex = hooks.useSelector(selectors.getActiveRowIndex); const chunkSize = useMemo2(() => Math.ceil(options.chunkSize) * 2, [options]); const keyChunks = useMemo2( () => chunkSize > 0 ? _12.chunk(rowKeys, chunkSize) : [rowKeys], [rowKeys, chunkSize] ); const renderChunk = (keys, chunkIndex) => { const chunkIndexOffset = chunkIndex * chunkSize; const rows = keys.map((key, rowIndex) => { const index = chunkIndexOffset + rowIndex; return { key, index, data: getItem(items, key), selected: hasItem(selected, key), active: index === activeRowIndex }; }); return /* @__PURE__ */ React10.createElement(ChunkObserver_default, { indexOffset: chunkIndexOffset, rows, key: `chunk_${props.name}_${chunkIndex}`, ...chunkCommonProps }); }; return /* @__PURE__ */ React10.createElement("div", { className: "rst-body", tabIndex: 0 }, /* @__PURE__ */ React10.createElement("table", { ref: tableBodyRef }, /* @__PURE__ */ React10.createElement(ColGroup_default, { name, columns }), !showPlaceholder && keyChunks.map(renderChunk))); } var TableBody_default = TableBody; // src/components/TableHead.jsx import React14, { useMemo as useMemo3, useContext as useContext5 } from "react"; import _13 from "lodash"; // src/components/TableHeader.jsx import React13, { Fragment, useCallback as useCallback4, useContext as useContext4, useLayoutEffect as useLayoutEffect3, useRef as useRef2 } from "react"; // src/components/AngleIcon.jsx import React11 from "react"; var angleRotation = Object.freeze({ Up: 0, Down: 180, Right: 90, Left: -90 }); function AngleIcon({ rotation }) { return /* @__PURE__ */ React11.createElement("svg", { className: "rst-icon", viewBox: "0 0 24 24", style: { transform: `rotate(${rotation}deg)` } }, /* @__PURE__ */ React11.createElement("path", { d: "M 23.514324,16.51929 11.995,4.5999889 0.47567595,16.51929 A 1.7007899,1.7007899 0 0 0 2.9255324,18.879152 L 11.995,9.4897022 21.074467,18.879152 a 1.7007901,1.7007901 0 0 0 2.449857,-2.359862 z" })); } AngleIcon.defaultProps = { rotation: angleRotation.Up }; var AngleIcon_default = AngleIcon; // src/components/HourGlassIcon.jsx import React12 from "react"; function HourGlassIcon({ rotation, ...rest }, ref) { return /* @__PURE__ */ React12.createElement("svg", { className: "rst-icon rst-loadingIcon", viewBox: "0 0 24 32", ref, ...rest }, /* @__PURE__ */ React12.createElement("path", { d: "m 23,30 h -3 v -7.09 a 6.67,6.67 0 0 0 -2.69,-5.33 l -1.28,-1 A 6.36,6.36 0 0 0 15,16 v 0 a 6.29,6.29 0 0 0 1,-0.62 l 1.28,-1 A 6.67,6.67 0 0 0 20,9.09 V 2 h 3 A 1,1 0 0 0 23,0 H 1 a 1,1 0 0 0 0,2 h 3 v 7.09 a 6.67,6.67 0 0 0 2.69,5.33 l 1.28,1 A 6.36,6.36 0 0 0 9,16 v 0 a 6.27,6.27 0 0 0 -1,0.62 l -1.28,1 A 6.67,6.67 0 0 0 4,22.91 V 30 H 1 a 1,1 0 0 0 0,2 h 22 a 1,1 0 0 0 0,-2 z M 6,22.91 a 4.66,4.66 0 0 1 1.88,-3.72 l 1.28,-1 a 4.66,4.66 0 0 1 1.18,-0.63 1,1 0 0 0 0.65,-0.94 V 15.33 A 1,1 0 0 0 10.34,14.39 4.67,4.67 0 0 1 9.15,13.76 l -1.28,-1 A 4.66,4.66 0 0 1 6,9.09 V 2 h 12 v 7.09 a 4.66,4.66 0 0 1 -1.88,3.72 l -1.28,1 v 0 a 4.66,4.66 0 0 1 -1.18,0.63 1,1 0 0 0 -0.65,0.94 v 1.34 a 1,1 0 0 0 0.65,0.94 4.67,4.67 0 0 1 1.19,0.63 l 1.28,1 A 4.66,4.66 0 0 1 18,22.91 V 30 H 6 Z" })); } var HourGlassIcon_default = React12.forwardRef(HourGlassIcon); // src/components/TableHeader.jsx var LoadingAttribute = getFlagAttribute("loading"); function TableHeader({ handleGesturePointerDownCapture, handleGestureTouchStart, path, title, columnResize, actions, sortAscending, sortPriority, showPriority, isResizing, isResizable, className }) { const gesture = useContext4(GestureTarget_default); const isSortable = !!path; const loadingIconRef = useRef2(); const sortColumn = useCallback4((addToPrev) => { if (!isSortable) return; requestAnimationFrame(() => { loadingIconRef.current.toggleAttribute(LoadingAttribute, true); setTimeout(() => actions.sortItems(path, addToPrev)); }); return true; }, [actions, path, isSortable]); const handleTitleMouseDown = useCallback4((e) => { if (e.button !== 0) return; sortColumn(e.shiftKey); }, [sortColumn]); const handleTitleKeyDown = useCallback4((e) => { if (e.keyCode !== 32) return; e.stopPropagation(); if (!sortColumn(e.shiftKey)) return; e.preventDefault(); }, [sortColumn]); const handleTitleContextMenu = useCallback4(() => { if (gesture.pointerType !== "touch") return; sortColumn(true); }, [sortColumn, gesture]); useLayoutEffect3(() => { loadingIconRef.current.toggleAttribute(LoadingAttribute, false); }, [sortAscending]); return /* @__PURE__ */ React13.createElement("th", { className, onPointerDownCapture: handleGesturePointerDownCapture, onTouchStart: handleGestureTouchStart, ...dataAttributeFlags({ sortable: isSortable, resizing: isResizing }) }, /* @__PURE__ */ React13.createElement("div", { className: "rst-headerContent", onMouseDown: handleTitleMouseDown, onContextMenu: handleTitleContextMenu, onKeyDown: handleTitleKeyDown, tabIndex: isSortable ? 0 : -1, role: "none" }, /* @__PURE__ */ React13.createElement("span", { className: "rst-headerText" }, title), /* @__PURE__ */ React13.createElement(HourGlassIcon_default, { ref: loadingIconRef }), sortPriority >= 0 && /* @__PURE__ */ React13.createElement(Fragment, null, /* @__PURE__ */ React13.createElement(AngleIcon_default, { rotation: sortAscending ? angleRotation.Up : angleRotation.Down }), showPriority && /* @__PURE__ */ React13.createElement("small", null, sortPriority))), isResizable && /* @__PURE__ */ React13.createElement("div", { className: "rst-columnResizer", onDragStart: (e) => e.preventDefault(), onPointerDown: columnResize })); } var TableHeader_default = withGestures(TableHeader); // src/components/TableHead.jsx function TableHead(props) { const { handleGesturePointerDownCapture, handleGestureTouchStart, columns, name, headColGroupRef, headRowRef, utils: { hooks, options }, ...commonHeaderProps } = props; const sortAscending = hooks.useSelector((s) => s.sortAscending); const sorting = useMemo3(() => { let priority = 0; return { orders: _13.mapValues(sortAscending, (ascending) => ({ ascending, priority: ++priority })), maxPriority: priority }; }, [sortAscending]); const { resizingIndex } = useContext5(ColumnGroup_default); const renderHeader = (column, index) => { const { path, title } = column; const sortOrder = sorting.orders[path]; return /* @__PURE__ */ React14.createElement(TableHeader_default, { ...commonHeaderProps, path, title, key: `header_${name}_${column.key}`, className: "rst-header", gestureTarget: GestureTarget(GestureTargetTypes.Header, index), isResizable: !!index, isResizing: resizingIndex === index, sortAscending: sortOrder == null ? void 0 : sortOrder.ascending, sortPriority: sortOrder == null ? void 0 : sortOrder.priority, showPriority: sorting.maxPriority > 1 }); }; return /* @__PURE__ */ React14.createElement("div", { className: "rst-head", onPointerDownCapture: handleGesturePointerDownCapture, onTouchStart: handleGestureTouchStart }, /* @__PURE__ */ React14.createElement("table", null, /* @__PURE__ */ React14.createElement(ColGroup_default, { name, columns, ref: headColGroupRef }), /* @__PURE__ */ React14.createElement("thead", null, /* @__PURE__ */ React14.createElement("tr", { className: "rst-row", ref: headRowRef }, columns.map(renderHeader), /* @__PURE__ */ React14.createElement("th", { className: "rst-endCap" }), /* @__PURE__ */ React14.createElement(TableHeader_default, { ...commonHeaderProps, path: "", title: "", gestureTarget: null, className: "rst-spacer", isResizable: !options.constantWidth, isResizing: resizingIndex === columns.length, sortAscending: true, sortPriority: -1, showPriority: false }))))); } var TableHead_default = withGestures(TableHead); // src/components/ResizingContainer.jsx function ResizingContainer(props) { const { handleGesturePointerDownCapture, handleGestureTouchStart, dragSelect, placeholder, selectionRectRef, dragMode, contextMenu, headColGroupRef, headRowRef, columnResize, actions, getRowClassName, tableBodyRef, chunkObserverRef, ...commonProps } = props; const { utils: { options, hooks, selectors, events }, columns } = props; const showPlaceholder = !!placeholder; const { containerWidth, widths, resizingIndex } = useContext6(ColumnGroup_default); const gesture = useContext6(GestureTarget_default); const rowCount = hooks.useSelector((s) => s.rowKeys.length); const indexOffset = hooks.useSelector(selectors.getPageIndexOffset); const handleMouseDown = useCallback5((e) => { if (showPlaceholder || e.button !== 0 || e.altKey) return; const { target } = gesture; switch (target.type) { case GestureTargetTypes.BelowRows: if (e.shiftKey) actions.select(indexOffset + rowCount - 1, e.shiftKey, e.ctrlKey); else if (!options.listBox && !e.ctrlKey) actions.clearSelection(); break; case GestureTargetTypes.Row: actions.select(indexOffset + target.index, e.shiftKey, e.ctrlKey); break; default: return; } if (gesture.pointerType === "mouse") { getSelection().removeAllRanges(); dragSelect(e); } }, [gesture, actions, options, indexOffset, rowCount, dragSelect, showPlaceholder]); const handleContextMenu = useCallback5((e) => { if (e.shiftKey) return; const { target } = gesture; if (gesture.pointerType !== "mouse") { if (showPlaceholder) return; if (target.type === GestureTargetTypes.Row) actions.select(indexOffset + target.index, false, true); else if (target.type !== GestureTargetTypes.BelowRows) return; return dragSelect(e); } if (events.hasListener("onContextMenu")) e.preventDefault(); contextMenu(e); }, [gesture, indexOffset, contextMenu, actions, events, dragSelect, showPlaceholder]); const gestureEventHandlers = { onMouseDown: handleMouseDown, onContextMenu: handleContextMenu, onPointerDownCapture: handleGesturePointerDownCapture, onTouchStart: handleGestureTouchStart }; const columnKeys = _14.map(columns, "key"); const clippingStoppers = /* @__PURE__ */ React15.createElement("div", { className: "rst-stoppers" }, _14.map(columnKeys, (referenceKey) => { const minWidthScale = options.minColumnWidth / widths[referenceKey]; return /* @__PURE__ */ React15.createElement("div", { className: "rst-clippingStopper", "data-col-key": referenceKey, key: `stoppers-${referenceKey}` }, _14.map(columnKeys, (key) => /* @__PURE__ */ React15.createElement("div", { "data-col-key": key, key: `stopper-${key}`, style: { width: widths[key] * minWidthScale } }))); })); const resizingStopperWidths = _14.map(columnKeys, (key) => containerWidth / widths[key] * options.minColumnWidth); const isOverflowing = containerWidth > 100; const isResizing = resizingIndex >= 0; const showClippingStoppers = !isResizing && !isOverflowing; const headProps = { ...commonProps, headColGroupRef, actions, headRowRef, columnResize }; const bodyProps = { ...commonProps, tableBodyRef, chunkObserverRef, showPlaceholder, getRowClassName, contextMenu }; return /* @__PURE__ */ React15.createElement(Fragment2, null, /* @__PURE__ */ React15.createElement("div", { className: "rst-clippingContainer", ...dataAttributeFlags({ clipping: showClippingStoppers }) }, showClippingStoppers && clippingStoppers, /* @__PURE__ */ React15.createElement("div", { className: "rst-resizingContainer", style: { width: pc(containerWidth), marginRight: showClippingStoppers ? -_14.max(resizingStopperWidths) : 0 }, ...gestureEventHandlers }, !isResizing && (isOverflowing ? clippingStoppers : /* @__PURE__ */ React15.createElement("div", { className: "rst-stoppers" }, _14.map(columnKeys, (key, index) => /* @__PURE__ */ React15.createElement("div", { className: "rst-resizingStopper", "data-col-key": key, key: `stopper-${key}`, style: { width: resizingStopperWidths[index] } })))), /* @__PURE__ */ React15.createElement(TableHead_default, { ...headProps, gestureTarget: GestureTarget(GestureTargetTypes.Header, columns.length), onDualTap: contextMenu }), /* @__PURE__ */ React15.createElement(TableBody_default, { ...bodyProps }), dragMode === DragModes.Select && /* @__PURE__ */ React15.createElement("div", { className: "rst-dragSelection", ref: selectionRectRef }))), showPlaceholder && /* @__PURE__ */ React15.createElement("div", { className: "rst-placeholder", ...gestureEventHandlers }, placeholder)); } var ResizingContainer_default = withGestures(ResizingContainer); // src/hooks/useDecoupledCallback.js import { useRef as useRef3, useEffect as useEffect2, useCallback as useCallback6 } from "react"; function useDecoupledCallback(callback) { const callbackRef = useRef3(callback); useEffect2(() => { callbackRef.current = callback; }, [callback]); return useCallback6((...args) => { var _a; return (_a = callbackRef.current) == null ? void 0 : _a.call(callbackRef, ...args); }, [callbackRef]); } // src/hooks/useEventListener.js import { useCallback as useCallback7 } from "react"; // src/hooks/useObjectMemo.js import { useRef as useRef4 } from "react"; import _15 from "lodash"; function useObjectMemo(obj) { const objRef = useRef4(obj); if (!_15.isEqual(obj, objRef.current)) objRef.current = obj; return objRef.current; } // src/hooks/useEventListener.js var activeListenerOptions = { passive: false }; var defaultOptions2 = {}; function useEventListener(type, handler, options = defaultOptions2) { const decoupledHandler = useDecoupledCallback(handler); const add = useCallback7( (element) => element.addEventListener(type, decoupledHandler, options), [decoupledHandler, type, options] ); const remove = useCallback7( (element) => element.removeEventListener(type, decoupledHandler, options), [decoupledHandler, type, options] ); return useObjectMemo({ add, remove }); } // src/components/ScrollingContainer.jsx var isColumnVisible = (width) => width > 0; var Point = (x, y) => ({ x, y }); var getClientX = (element) => element.getBoundingClientRect().x; var getClientY = (element) => element.getBoundingClientRect().y; function getLine(pointA, pointB) { const min = pointA < pointB ? pointA : pointB; const max = pointA > pointB ? pointA : pointB; return { origin: min, size: max - min }; } function getRelativeOffset(absolute, origin, minVisible, maxVisible, scrollFactor) { const reference = _16.clamp(absolute, minVisible, maxVisible); const scrollOffset = (absolute - reference) * scrollFactor; return { scrollOffset, relToOrigin: reference - origin, relToMin: reference - minVisible, relToMax: maxVisible - reference }; } function ScrollingContainer(props) { const { dragSelectScrollFactor, columnResizeScrollFactor, columns, initColumnWidths, componentEvents, ...resizingProps } = props; const { utils: { options, hooks, selectors, events }, actions, placeholder } = props; const gesture = useContext7(GestureTarget_default); const [dragMode, setDragMode] = useState(null); useLayoutEffect4(() => { gesture.isDragging = !!dragMode; }, [gesture, dragMode]); const rowCount = hooks.useSelector((s) => s.rowKeys.length); const rowKeys = hooks.useSelector((s) => s.rowKeys); const indexOffset = hooks.useSelector(selectors.getPageIndexOffset); const activeRowIndex = hooks.useSelector(selectors.getActiveRowIndex); const noSelection = hooks.useSelector((s) => isEmpty(s.selected)); const getState = hooks.useGetState(); const tableBodyRef = useRef5(); const headColGroupRef = useRef5(); const headRowRef = useRef5(); const selectionRectRef = useRef5(); const scrollingContainerRef = useRef5(); const getRow = useCallback8((index) => tableBodyRef.current.rows[index], []); const getCurrentHeaderWidths = useCallback8( () => _16.map(_16.take(headRowRef.current.children, columns.length), (h) => h.getBoundingClientRect().width), [columns] ); const [columnGroup, setColumnGroup] = useState({ widths: initColumnWidths, resizingIndex: -1 }); const getRenderedColumnsWidthsPatch = useCallback8((widths) => _16.zipObject(_16.map(columns, "key"), widths), [columns]); const defaultWidths = useMemo4(() => { const defaultWidth = 100 / columns.length; const widths = _16.map(columns, (c) => +c.defaultWidth || defaultWidth); return getRenderedColumnsWidthsPatch(widths); }, [columns, getRenderedColumnsWidthsPatch]); const allWidths = useMemo4(() => { const validWidths = _16.mapValues( columnGroup.widths, (width, key) => width || negative(defaultWidths[key]) ); return { ...defaultWidths, ...validWidths }; }, [columnGroup, defaultWidths]); const fullColumnGroup = useMemo4(() => { const visibleWidths = columns.map((c) => allWidths[c.key]).filter((w) => w >= 0); const { resizingIndex } = columnGroup; const isResizing = resizingIndex >= 0; return { resizingIndex, widths: _16.mapValues(allWidths, Math.abs), containerWidth: isResizing ? 0 : Math.max(100, _16.sum(visibleWidths)), widthUnit: isResizing ? px : pc }; }, [columnGroup, columns, allWidths]); const setRenderedColumnWidths = useCallback8((widths, resizingIndex = -1) => { const renderedPatch = getRenderedColumnsWidthsPatch(widths); const visiblePatch = _16.pickBy(renderedPatch, isColumnVisible); const hiddenPatch = _16.mapValues(renderedPatch, (w, key) => negative(allWidths[key])); setColumnGroup({ widths: _16.defaults(visiblePatch, hiddenPatch, allWidths), resizingIndex }); if (resizingIndex >= 0) return; componentEvents.columnResize(visiblePatch); }, [getRenderedColumnsWidthsPatch, componentEvents, allWidths]); const drag = useRef5({ invertScroll: false, animationId: null, pointerPos: Point(), pointerId: null, movement: Point(0, 0), ctrlKey: false }).current; const columnResizing = useRef5({ prevVisibleIndex: -1, borderLeft: 0, header: null, distanceOfPrevToStart: 0, isResizingSpacer: false, minWidth: 0 }).current; const dragSelection = useRef5({ selection: {}, selectionBuffer: {}, originRel: Point(), activeIndex: null, pivotIndex: null, prevRowIndex: -1, prevRelY: 0 }).current; const columnResizeEnd = useCallback8(() => { const { offsetLeft: fullWidth, previousElementSibling: { offsetLeft: availableWidth } } = headRowRef.current.lastChild; const container = scrollingContainerRef.current; const { clientWidth, offsetWidth } = container; const visibleWidth = container.getBoundingClientRect().width - (offsetWidth - clientWidth); const scale = fullWidth > visibleWidth ? fullWidth / availableWidth : 1; const widths = getCurrentHeaderWidths().map((px2) => px2 / visibleWidth * scale * 100); setRenderedColumnWidths(widths); }, [getCurrentHeaderWidths, setRenderedColumnWidths]); const dragSelectEnd = useCallback8(() => { if (dragSelection.activeIndex == null) return; actions.setSelected( _16.mapKeys(dragSelection.selection, (_23, rowIndex) => rowKeys[rowIndex]), dragSelection.activeIndex + indexOffset, dragSelection.pivotIndex + indexOffset ); }, [dragSelection, actions, rowKeys, indexOffset]); const dragEnd = useMemo4(() => ({ [DragModes.Resize]: columnResizeEnd, [DragModes.Select]: dragSelectEnd })[dragMode], [dragMode, columnResizeEnd, dragSelectEnd]); const dragStop = useCallback8(() => { if (drag.pointerId != null) { scrollingContainerRef.current.releasePointerCapture(drag.pointerId); drag.pointerId = null; } if (drag.animationId != null) return; dragEnd(); setDragMode(null); }, [drag, dragEnd]); const dragAnimate = useCallback8((callback) => { cancelAnimationFrame(drag.animationId); drag.animationId = requestAnimationFrame(() => { callback(); drag.animationId = null; drag.movement.x = 0; drag.movement.y = 0; if (drag.pointerId == null) setTimeout(dragStop, 0); }); }, [drag, dragStop]); const columnResizeAnimation = useCallback8((changedWidths, newScroll) => { const colGroup = headColGroupRef.current; const container = scrollingContainerRef.current; for (const index in changedWidths) colGroup.children[index].style.width = px(changedWidths[index]); container.scrollLeft = newScroll; }, []); const dragSelectAnimation = useCallback8((relX, relY, scrollLeftOffset, scrollTopOffset) => { const container = scrollingContainerRef.current; container.scrollLeft += scrollLeftOffset; container.scrollTop += scrollTopOffset; const { originRel } = dragSelection; const lineX = getLine(relX, originRel.x); const lineY = getLine(relY, originRel.y); const body = tableBodyRef.current; Object.assign(selectionRectRef.current.style, _16.mapValues({ left: lineX.origin, width: lineX.size, top: lineY.origin + body.offsetTop, height: lineY.size }, px)); _16.forEach(dragSelection.selectionBuffer, (selected, index) => { getRow(index).toggleAttribute(RowAttributes.Selected, selected); }); dragSelection.selectionBuffer = {}; if (dragSelection.activeIndex == null) return; const newActiveRow = getRow(dragSelection.activeIndex); if (newActiveRow === dragSelection.activeRow) return; dragSelection.activeRow.toggleAttribute(RowAttributes.Active, false); dragSelection.activeRow = newActiveRow; dragSelection.activeRow.toggleAttribute(RowAttributes.Active, true); }, [dragSelection, getRow]); const columnResizeUpdate = useCallback8(() => { const index = columnGroup.resizingIndex; if (index < 0) return; const { prevVisibleIndex, header, distanceOfPrevToStart, isResizingSpacer, minWidth, borderLeft } = columnResizing; const constantWidth = options.constantWidth || drag.ctrlKey; const container = scrollingContainerRef.current; const containerX = getClientX(container); const { clientWidth, scrollWidth, scrollLeft } = container; const head = headRowRef.current; const { lastChild: spacer } = head; const shrinkThresholdColumn = scrollLeft && (isResizingSpacer || !constantWidth) ? clientWidth - spacer.offsetLeft + header.offsetLeft : -Infinity; const shrinkThreshold = containerX + Math.max(0, shrinkThresholdColumn); const expandThreshold = containerX + clientWidth; const { relToOrigin: relX, scrollOffset } = getRelativeOffset( drag.pointerPos.x, getClientX(head), shrinkThreshold, expandThreshold, columnResizeScrollFactor ); let movementOffset = 0; if (scrollWidth > clientWidth) { const scrollRemaining = constantWidth ? scrollWidth - scrollLeft - clientWidth : Infinity; movementOffset = _16.clamp(drag.movement.x, -scrollLeft, scrollRemaining); } const offsetSum = Math.floor(movementOffset + scrollOffset); const offsetRight = isResizingSpacer ? head.offsetWidth : header.offsetLeft + header.offsetWidth; const sharedWidth = constantWidth ? offsetRight - distanceOfPrevToStart : Infinity; const minPrevWidth = options.minColumnWidth; const targetWidth = Math.floor(relX - distanceOfPrevToStart) + offsetSum - borderLeft; const newPrevWidth = _16.clamp(targetWidth, minPrevWidth, sharedWidth - minWidth); const changedWidths = { [prevVisibleIndex]: newPrevWidth }; if (constantWidth && !isResizingSpacer) changedWidths[index] = sharedWidth - newPrevWidth; const newScroll = Math.ceil(scrollLeft) + offsetSum; dragAnimate(() =