@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
JavaScript
/**
* @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
};