UNPKG

@progress/kendo-react-gantt

Version:

React Gantt enables the display of self-referencing tabular data with many features. KendoReact Gantt package

589 lines (588 loc) 21.8 kB
/** * @license *------------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the package root for more information *------------------------------------------------------------------------------------------- */ import * as l from "react"; import o from "prop-types"; import { noop as H, canUseDOM as Y, getter as T, classNames as Z } from "@progress/kendo-react-common"; import { tableKeyboardNavigation as w, getSelectionOptions as P, CommonDragLogic as _, ColumnResize as ee, TableKeyboardNavigationContext as A, tableKeyboardNavigationTools as K, Header as te, HeaderRow as oe, tableColumnsVirtualization as ne, tableKeyboardNavigationScopeAttributes as se, TableSelection as ae, tableKeyboardNavigationBodyAttributes as ie, DropClue as le, DragClue as re, FilterRow as he } from "@progress/kendo-react-data-tools"; import { setHeaderRowsTop as ce, tableRowsVirtualization as de, TreeListRow as pe, TreeListNoRecords as ge, TreeListCell as ue } from "@progress/kendo-react-treelist"; import { isExpanded as O, hasChildren as me } from "../utils/index.mjs"; const F = class F extends l.Component { constructor(p) { super(p), this.state = { activeId: void 0, level: 0 }, this.tableElement = null, this.tbodyElement = null, this.wrapperScrollLeft = 0, this.wrapperScrollTop = 0, this.updateOnScroll = !1, this.tbodyOffsetTop = 0, this.prevData = [], this.flattedData = [], this.element = null, this.contextStateRef = { current: void 0 }, this.navigationStateRef = { current: void 0 }, this.scrollIntoView = (e) => { if (!this.element) return; const { rowIndex: t = 0 } = e, { rowHeight: n = 0 } = this.props; this.element.scroll(0, (t - 1) * n); }, this.getColumns = (e) => { const t = e.filter((s) => s.declarationIndex >= 0 && s.parentIndex === -1), n = (s) => (s.sort((i, r) => i.declarationIndex - r.declarationIndex), s.map((i) => { const { declarationIndex: r, parentIndex: d, depth: g, colSpan: c, rowSpan: E, index: L, kFirst: C, groupable: N, children: b, ...x } = i; return b.length ? { children: n(b), ...x } : x; })); return n(t); }, this.onKeyDown = (e) => { if (w.onKeyDown(e, { navigatable: !1, contextStateRef: this.contextStateRef, navigationStateRef: this.navigationStateRef }), this.props.onKeyDown) { const { mode: t, cell: n } = P(this.props.selectable), s = { dataItems: this.getLeafDataItems(), mode: t, cell: n, componentId: this.props.treelistId, selectedField: this.props.selectedField, ...this.getEventArguments(e) }; this.props.onKeyDown.call(void 0, s); } }, this.onFocus = (e) => { w.onFocus(e, { contextStateRef: this.contextStateRef }); }, this.onRowDrag = (e) => { if (this.props.onRowDrag) { const t = { ...e, target: this }; this.props.onRowDrag.call(void 0, t); } }, this.onRowDrop = (e) => { if (this.props.onRowDrop) { const t = { ...e, target: this }; this.props.onRowDrop.call(void 0, t); } }, this.onColumnReorder = (e, t, n) => { const { extendedColumns: s } = this.props, i = s[e].depth, r = (c) => { do c++; while (c < s.length && s[c].depth > i); return c; }, d = [...s], g = d.splice(e, r(e) - e); if (d.splice(e < t ? r(t - g.length) : t, 0, ...g), d.filter((c) => c.declarationIndex >= 0).forEach((c, E) => c.orderIndex = E), this.props.onColumnReorder) { const c = { target: this, columns: this.getColumns(d), nativeEvent: n }; this.props.onColumnReorder.call(void 0, c); } }, this.onColumnResize = (e, t, n, s, i) => { if (this.props.onColumnResize) { const r = this.props.extendedColumns.filter((g) => g.children.length === 0).reduce((g, c) => g += parseFloat(String(c.width)), 0); this.tableElement && (this.tableElement.style.width = r + "px"); const d = { columns: this.getColumns(this.props.extendedColumns), totalWidth: r, index: e, nativeEvent: s, newWidth: t, oldWidth: n, end: i, target: this }; this.props.onColumnResize.call(void 0, d); } }, this.onScroll = (e) => { const t = e.currentTarget.scrollLeft, n = e.currentTarget.scrollTop, { columnVirtualization: s, scrollable: i, rowHeight: r = 0 } = this.props, d = r, g = 0; let c = !1; s && Math.abs(this.wrapperScrollLeft - t) > g && (this.wrapperScrollLeft = t, c = !0), i === "virtual" && Math.abs(this.wrapperScrollTop - n) > d && (this.wrapperScrollTop = n, c = !0), c && (this.updateOnScroll = !0, this.forceUpdate()); }, this.calculateSizes = (e) => { if (!e || this.props.scrollable === "none") return; const t = Array.from(e.childNodes), n = t.find((r) => r.nodeName === "TABLE"), s = this.props.toolbar && t.find( (r) => r.nodeType === 1 && r.classList.contains("k-grid-toolbar") ); let i = 0; if (s) { const r = s.style.boxSizing; s.style.boxSizing = "border-box", i = parseFloat(String(window.getComputedStyle(s).height)) || s.offsetHeight, s.style.boxSizing = r, s.getAttribute("style") || s.removeAttribute("style"); } this.tbodyOffsetTop = n.tBodies[0].offsetTop, ce(n, i); }, this.onItemChange = (e) => { if (e.field === this.props.expandField) { const t = this.props.onExpandChange; if (t) { const n = { ...this.getEventArguments(e.syntheticEvent), dataItem: e.dataItem, level: e.level, value: e.value }; t.call(void 0, n); } return; } if (this.props.onItemChange) { const t = { ...this.getEventArguments(e.syntheticEvent), dataItem: e.dataItem, level: e.level, field: e.field, value: e.value }; this.props.onItemChange.call(void 0, t); } }, this.onHeaderSelectionChange = (e) => { if (this.props.onHeaderSelectionChange) { const t = { field: e.field, nativeEvent: e.syntheticEvent && e.syntheticEvent.nativeEvent, syntheticEvent: e.syntheticEvent, target: this, dataItems: this.getLeafDataItems() }; this.props.onHeaderSelectionChange.call(void 0, t); } }, this.selectionRelease = (e) => { if (this.props.onSelectionChange) { const t = { syntheticEvent: void 0, target: this, selectedField: this.props.selectedField || "", componentId: this.props.treelistId, dataItems: this.getLeafDataItems(), dataItem: null, level: [], ...e }; this.props.onSelectionChange.call(void 0, t); } }, this.onSortChange = (e, t, n) => { this.onDataStateChange(this.props.onSortChange, { sort: t, field: n }, e); }, this.onFilterChange = (e) => { const { filter: t, field: n } = e; this.onDataStateChange(this.props.onFilterChange, { filter: t, field: n }, e.syntheticEvent); }, this.onColumnMenuFilterChange = (e, t, n) => { if (!n) return; const { onColumnMenuFilterChange: s } = this.props; if (!s) return; const i = { syntheticEvent: e, filter: t, field: n, target: this, nativeEvent: e.nativeEvent }; s.call(void 0, i); }, this.onExpandChange = (e, t, n) => { const { expandField: s, onExpandChange: i } = this.props; if (s && i) { const r = { ...this.getEventArguments(e), dataItem: t, level: n, value: O(t, this.props.expandField) }; i.call(void 0, r); } }, this.onRowClick = (e, t) => { if (this.props.onRowClick && e.target.nodeName === "TD") { const n = { dataItem: t.dataItem, level: t.level, ...this.getEventArguments(e) }; this.props.onRowClick.call(void 0, n); } }, this.rowDoubleClick = (e, t) => { if (this.props.onRowDoubleClick && e.target.nodeName === "TD") { const n = { dataItem: t.dataItem, level: t.level, ...this.getEventArguments(e) }; this.props.onRowDoubleClick.call(void 0, n); } }, this.rowContextMenu = (e, t) => { if (this.props.onRowContextMenu && e.target.nodeName === "TD") { const n = { dataItem: t.dataItem, level: t.level, ...this.getEventArguments(e) }; this.props.onRowContextMenu.call(void 0, n); } }, this.onPageChange = (e) => { if (this.props.onPageChange) { const t = { ...this.getEventArguments(e.syntheticEvent), skip: e.skip, take: e.take }; this.props.onPageChange.call(void 0, t); } }, this.onDataStateChange = (e, t, n) => { if (e && e.call(void 0, { ...this.getEventArguments(n), ...t }), this.props.onDataStateChange) { const s = { ...this.getEventArguments(n), dataState: { ...this.getDataState(), ...t } }; this.props.onDataStateChange.call(void 0, s); } }, this.getDataState = () => ({ filter: this.props.filter, sort: this.props.sort }), this.getEventArguments = (e) => ({ nativeEvent: e && e.nativeEvent, syntheticEvent: e, target: this }), this.getLeafDataItems = () => this.flatData.map((e) => e.dataItem), this.dragLogic = new _(this.onColumnReorder, H, H), this.columnResize = new ee(this.onColumnResize), w.onConstructor({ navigatable: !!p.navigatable, contextStateRef: this.contextStateRef, navigationStateRef: this.navigationStateRef, idPrefix: p.idPrefix }); } get document() { if (Y) return this.element && this.element.ownerDocument || document; } /** * @hidden */ componentDidMount() { this.calculateSizes(this.element), w.onComponentDidMount({ scope: this.element || void 0, contextStateRef: this.contextStateRef, navigationStateRef: this.navigationStateRef }); } /** * @hidden */ getSnapshotBeforeUpdate() { return w.onGetSnapshotBeforeUpdate({ document: this.document, contextStateRef: this.contextStateRef, navigationStateRef: this.navigationStateRef }), null; } /** * @hidden */ componentDidUpdate(p) { p.columns !== this.props.columns && this.calculateSizes(this.element), w.onComponentDidUpdate({ scope: this.element || void 0, contextStateRef: this.contextStateRef, navigationStateRef: this.navigationStateRef }); } /** * @hidden */ componentWillUnmount() { this.columnResize.columns = [], this.dragLogic.columns = [], this.prevData = [], this.flattedData = [], this.updateOnScroll = !1; } /** * @hidden */ render() { const { filterRow: p, scrollable: e = "scrollable", resizable: t = !1, reorderable: n = !1, skip: s, take: i, afterContent: r, extendedColumns: d = [], columnsMap: g } = this.props, c = d.some((a) => !!a.filter) || p !== void 0, E = p || he, L = K.getIdPrefix(this.navigationStateRef); this.columnResize.columns = d, this.columnResize.resizable = t, this.dragLogic.columns = d, this.dragLogic.reorderable = n, this.dragLogic.groupable = !1; const C = d.filter((a) => a.children.length === 0), N = /* @__PURE__ */ l.createElement( te, { headerRow: /* @__PURE__ */ l.createElement( oe, { sort: this.props.sort, sortable: this.props.sortable, sortChange: this.onSortChange, selectionChange: this.onHeaderSelectionChange, columns: d, columnsMap: g, cellRender: this.props.headerCellRender, columnResize: this.columnResize, columnMenu: this.props.columnMenu, columnMenuFilter: this.props.columnMenuFilter, columnMenuFilterChange: this.onColumnMenuFilterChange, pressHandler: this.dragLogic.pressHandler, dragHandler: this.dragLogic.dragHandler, releaseHandler: this.dragLogic.releaseHandler } ), filterRow: c && /* @__PURE__ */ l.createElement( E, { columns: C, filter: this.props.filter, filterChange: this.onFilterChange, sort: this.props.sort, ariaRowIndex: g.length + 1 } ) || void 0, columnResize: this.columnResize } ), b = this.props.style || {}, { colSpans: x, hiddenColumns: V } = ne({ enabled: this.props.columnVirtualization, columns: C, scrollLeft: this.wrapperScrollLeft, tableViewPortWidth: parseFloat((b.width || "").toString()) }), B = (a, m, I, D, k, y) => C.map((h, u) => { if (V[u]) return null; const S = h.id ? h.id : u, z = `k-table-td ${h.className ? h.className + " " : ""}${h.locked ? "k-grid-content-sticky" : ""}`, R = { id: K.generateNavigatableId(`${I}-${String(u)}`, L), colSpan: x[u], dataItem: a.dataItem, field: h.field, format: h.format, className: z || void 0, render: this.props.cellRender, onChange: this.onItemChange, selectionChange: this.props.onSelectionChange ? (v) => { this.onSelectionChange({ event: v, item: a, columnIndex: u, dataIndex: k }); } : void 0, level: a.level, expandable: h.expandable, expanded: D, hasChildren: me(a.dataItem, this.props.subItemsField), colIndex: u, ariaColumnIndex: h.ariaColumnIndex, onExpandChange: this.onExpandChange, style: h.left !== void 0 && { left: h.left, right: h.right, borderRightWidth: h.rightBorder ? "1px" : "" } || {}, isSelected: Array.isArray(y) && y.indexOf(u) > -1 }; return m && h.editCell ? /* @__PURE__ */ l.createElement(h.editCell, { ...R, onChange: this.onItemChange, key: S }) : h.cell ? /* @__PURE__ */ l.createElement(h.cell, { key: S, ...R }) : /* @__PURE__ */ l.createElement(ue, { key: S, ...R }); }); let f = this.flatData; const M = f.length; s !== void 0 && i !== void 0 && (f = f.slice(s, s + i)), e === "virtual" && (f = de({ rows: f, tableViewPortHeight: parseFloat((b.height || b.maxHeight || "").toString()), scrollTop: this.wrapperScrollTop }), this.updateOnScroll = !1); const j = f.map((a) => a.level), W = g.length + (c ? 1 : 0) + 1, $ = f.length > 0 && f.map((a, m) => { const I = T(this.props.editField || "")(a.dataItem), D = T(this.props.dataItemKey)(a.dataItem), k = String(D || a.level.join(".")), y = O(a.dataItem, this.props.expandField), h = this.props.selectedField ? T(this.props.selectedField)(a.dataItem) : void 0, u = { key: k, level: a.level, levels: j, dataItem: a.dataItem, selectedField: this.props.selectedField, rowHeight: a.height, render: this.props.rowRender, onDrop: this.onRowDrop, onDrag: this.onRowDrag, onClick: (v) => this.onRowClick(v, a), onDoubleClick: (v) => this.rowDoubleClick(v, a), onContextMenu: (v) => this.rowContextMenu(v, a), isAltRow: m % 2 !== 0, expanded: y, rowIndex: m, ariaRowIndex: W + m, ariaSetSize: a.levelCount, ariaPosInSet: a.level[a.level.length - 1] + 1, isSelected: typeof h == "boolean" && h }, S = this.props.editRow, z = this.props.row || pe, R = B(a, I, k, y, m, h); return I && S ? /* @__PURE__ */ l.createElement(S, { ...u, key: u.key }, R) : /* @__PURE__ */ l.createElement(z, { ...u, key: u.key }, R); }) || /* @__PURE__ */ l.createElement("tr", { className: "k-table-row k-grid-norecords" }, /* @__PURE__ */ l.createElement("td", { className: "k-table-td", colSpan: C.length }, this.props.noRecords || /* @__PURE__ */ l.createElement(ge, null))), q = (a) => this.props.sort && this.props.sort.some((m) => m.field === a), G = /* @__PURE__ */ l.createElement( "colgroup", { ref: (a) => { this.columnResize.colGroupMain = a; } }, C.map((a, m) => /* @__PURE__ */ l.createElement( "col", { key: m.toString(), className: q(a.field) ? "k-sorted" : void 0, style: a.width !== void 0 ? { width: a.width } : void 0 } )) ), J = this.props.columnVirtualization || this.props.scrollable === "virtual", Q = this.props.selectable && this.props.selectable.drag ? "none" : void 0, X = this.props.tableProps || {}; return /* @__PURE__ */ l.createElement(A.Provider, { value: this.contextStateRef.current }, /* @__PURE__ */ l.createElement( "div", { id: this.props.treelistId, style: this.props.style, className: Z("k-grid k-grid-md", this.props.className, { "k-treelist-scrollable": e !== "none" }), ref: (a) => { this.element = a; }, onScroll: J ? this.onScroll : void 0, onKeyDown: this.onKeyDown, onFocus: this.onFocus, "aria-rowcount": M, "aria-colcount": C.length, role: "treegrid", ...se }, this.props.toolbar, /* @__PURE__ */ l.createElement( ae, { selectable: this.props.selectable, onRelease: this.selectionRelease, childRef: (a) => { this.tableElement = a; } }, /* @__PURE__ */ l.createElement( "table", { className: "k-table k-table-md", ...this.props.tableProps || {}, style: { ...X.style || {}, userSelect: Q }, role: "presentation" }, G, N, /* @__PURE__ */ l.createElement( "tbody", { className: "k-table-tbody", ...ie, ref: (a) => { this.tbodyElement = a; }, role: "presentation" }, $ ) ) ), this.props.pager && /* @__PURE__ */ l.createElement( this.props.pager, { className: "k-grid-pager", total: M, skip: s, take: i, onPageChange: this.onPageChange } ), n && /* @__PURE__ */ l.createElement(l.Fragment, null, /* @__PURE__ */ l.createElement(le, { ref: this.dragLogic.refDropElementClue }), /* @__PURE__ */ l.createElement(re, { ref: this.dragLogic.refDragElementClue })), r )); } get flatData() { const { data: p = [], rowHeight: e = 0 } = this.props; let t = 0; const n = () => { const i = { height: e, offsetTop: t }; return t += i.height, i; }, s = this.updateOnScroll && this.prevData === p && this.tbodyOffsetTop > 0 && this.flattedData.length ? this.flattedData : p.map((i) => ({ ...i, ...n() })); return this.prevData = p, this.flattedData = s, s; } onSelectionChange(p) { if (this.props.onSelectionChange) { const { event: e, item: t, dataIndex: n, columnIndex: s } = p, { mode: i, cell: r } = P(this.props.selectable), d = { ...this.getEventArguments(e.syntheticEvent), dataItem: t.dataItem, level: t.level, startColIndex: s, endColIndex: s, startRowIndex: n, endRowIndex: n, dataItems: this.getLeafDataItems(), altKey: !1, ctrlKey: !1, shiftKey: !1, metaKey: !1, mode: i, cell: r, isDrag: !1, componentId: this.props.treelistId, selectedField: this.props.selectedField || "" }; this.props.onSelectionChange.call(void 0, d); } } }; F.propTypes = { data: o.array, resizable: o.bool, reorderable: o.bool, sortable: o.oneOfType([ o.bool, o.shape({ mode: o.oneOf(["single", "multiple"]), allowUnsort: o.bool }) ]), onSortChange: o.func, sort: o.array, columns: o.arrayOf(o.object), columnVirtualization: o.bool, filter: o.array, onFilterChange: o.func, filterRow: o.any, toolbar: o.any, noRecords: o.any, onExpandChange: o.func, expandField: o.string, subItemsField: o.string, selectedField: o.string, onSelectionChange: o.func, onHeaderSelectionChange: o.func, onRowClick: o.func, onItemChange: o.func, editField: o.string, scrollable: o.oneOf(["none", "scrollable", "virtual"]), rowHeight: o.number, style: o.object, tableProps: o.object, pager: o.any, skip: o.number, take: o.number, onPageChange: o.func, onDataStateChange: o.func, onColumnResize: o.func, onColumnReorder: o.func, extendedColumns: o.arrayOf(o.object), columnsMap: o.arrayOf(o.array), dataItemKey: o.string.isRequired, afterContent: o.any, navigatable: o.bool, idPrefix: o.string, treelistId: o.string }, F.contextType = A; let U = F; export { U as GanttTreeList };