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