@rufous/ui 0.3.23 → 0.3.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.cjs CHANGED
@@ -1869,6 +1869,7 @@ var TextField = (0, import_react17.forwardRef)(({
1869
1869
  slotProps,
1870
1870
  InputProps,
1871
1871
  numberVariant,
1872
+ step: stepProp,
1872
1873
  multiline = false,
1873
1874
  rows,
1874
1875
  maxRows,
@@ -2004,7 +2005,7 @@ var TextField = (0, import_react17.forwardRef)(({
2004
2005
  required,
2005
2006
  disabled,
2006
2007
  readOnly,
2007
- step: type === "number" && numberVariant ? STEP_BY_VARIANT[numberVariant] : void 0,
2008
+ step: type === "number" ? stepProp ?? (numberVariant ? STEP_BY_VARIANT[numberVariant] : void 0) : void 0,
2008
2009
  min: type === "number" && numberVariant ? MIN_BY_VARIANT[numberVariant] : void 0,
2009
2010
  ...slotProps?.input,
2010
2011
  ...props,
@@ -4619,6 +4620,54 @@ var RufousLogoLoader = ({ size = 80, sx, className }) => {
4619
4620
  // lib/DataGrid/DataGrid.tsx
4620
4621
  var import_react23 = __toESM(require("react"), 1);
4621
4622
  var import_lucide_react2 = require("lucide-react");
4623
+ function getAllGroupIds(rows, fields, getKey, depth = 0, parentId = "") {
4624
+ if (!fields.length || !rows.length) return [];
4625
+ const [field, ...rest] = fields;
4626
+ const buckets = /* @__PURE__ */ new Map();
4627
+ rows.forEach((row) => {
4628
+ const k = getKey(row, field) || "(Blank)";
4629
+ if (!buckets.has(k)) buckets.set(k, []);
4630
+ buckets.get(k).push(row);
4631
+ });
4632
+ const out = [];
4633
+ buckets.forEach((children, k) => {
4634
+ const id = `${parentId}::${field}::${k}`;
4635
+ out.push({ id, field, key: k, depth, count: children.length });
4636
+ if (rest.length) out.push(...getAllGroupIds(children, rest, getKey, depth + 1, id));
4637
+ });
4638
+ return out;
4639
+ }
4640
+ function buildFlatEntries(rows, fields, getKey, expanded, depth = 0, parentId = "") {
4641
+ if (!fields.length) return rows.map((row) => ({ kind: "data", row, depth }));
4642
+ const [field, ...rest] = fields;
4643
+ const buckets = /* @__PURE__ */ new Map();
4644
+ rows.forEach((row) => {
4645
+ const k = getKey(row, field) || "(Blank)";
4646
+ if (!buckets.has(k)) buckets.set(k, []);
4647
+ buckets.get(k).push(row);
4648
+ });
4649
+ const out = [];
4650
+ buckets.forEach((children, k) => {
4651
+ const id = `${parentId}::${field}::${k}`;
4652
+ out.push({ kind: "group", id, field, key: k, depth, leafCount: children.length });
4653
+ if (expanded.has(id)) {
4654
+ out.push(...buildFlatEntries(children, rest, getKey, expanded, depth + 1, id));
4655
+ }
4656
+ });
4657
+ return out;
4658
+ }
4659
+ function buildTreeEntries(rows, getChildren, expanded, depth = 0) {
4660
+ const out = [];
4661
+ rows.forEach((row) => {
4662
+ const children = getChildren(row) ?? [];
4663
+ const hasChildren = children.length > 0;
4664
+ out.push({ kind: "data", row, depth, hasChildren });
4665
+ if (hasChildren && expanded.has(row.id)) {
4666
+ out.push(...buildTreeEntries(children, getChildren, expanded, depth + 1));
4667
+ }
4668
+ });
4669
+ return out;
4670
+ }
4622
4671
  function FilterSelect({
4623
4672
  value,
4624
4673
  onChange,
@@ -4716,7 +4765,21 @@ function DataGrid({
4716
4765
  searchableColumns,
4717
4766
  onSearchChange,
4718
4767
  onFiltersChange,
4719
- hideTopExport = false
4768
+ initialFilters,
4769
+ toolbarOptions,
4770
+ customToolbar,
4771
+ customFooter,
4772
+ hideTopExport = false,
4773
+ rowGroupingModel,
4774
+ defaultRowGroupingModel,
4775
+ onRowGroupingModelChange,
4776
+ rowGroupingColumnMode = "single",
4777
+ groupingColDef,
4778
+ defaultGroupingExpansionDepth = 0,
4779
+ isGroupExpandedByDefault,
4780
+ disableRowGrouping = false,
4781
+ treeData = false,
4782
+ getChildRows
4720
4783
  }) {
4721
4784
  const sxClass = useSx(sx);
4722
4785
  const [editingCell, setEditingCell] = (0, import_react23.useState)(null);
@@ -4776,10 +4839,46 @@ function DataGrid({
4776
4839
  const [focusFilterIdx, setFocusFilterIdx] = (0, import_react23.useState)(-1);
4777
4840
  const filterableColumnsProp = initialColumnsProp.filter((c) => c.filterable !== false);
4778
4841
  const initialFilterCol = String(filterableColumnsProp[0]?.field || filterableColumnsProp[0]?.key || "");
4779
- const [advancedFilters, setAdvancedFilters] = (0, import_react23.useState)([
4780
- { column: initialFilterCol, operator: getDefaultOperator(filterableColumnsProp[0]?.type), value: "", logic: "AND" }
4781
- ]);
4842
+ const [advancedFilters, setAdvancedFilters] = (0, import_react23.useState)(() => {
4843
+ if (initialFilters && initialFilters.length > 0) return initialFilters;
4844
+ return [{ column: initialFilterCol, operator: getDefaultOperator(filterableColumnsProp[0]?.type), value: "", logic: "AND" }];
4845
+ });
4782
4846
  const [colSearch, setColSearch] = (0, import_react23.useState)("");
4847
+ const [internalGroupingModel, setInternalGroupingModel] = (0, import_react23.useState)(
4848
+ defaultRowGroupingModel ?? []
4849
+ );
4850
+ const activeGroupingModel = rowGroupingModel ?? internalGroupingModel;
4851
+ const isGroupingActive = activeGroupingModel.length > 0;
4852
+ const setGroupingModel = (model) => {
4853
+ if (!rowGroupingModel) setInternalGroupingModel(model);
4854
+ onRowGroupingModelChange?.(model);
4855
+ };
4856
+ const addToGrouping = (field) => {
4857
+ if (activeGroupingModel.includes(field)) return;
4858
+ setGroupingModel([...activeGroupingModel, field]);
4859
+ };
4860
+ const removeFromGrouping = (field) => {
4861
+ setGroupingModel(activeGroupingModel.filter((f) => f !== field));
4862
+ };
4863
+ const [expandedGroups, setExpandedGroups] = (0, import_react23.useState)(/* @__PURE__ */ new Set());
4864
+ const prevGroupModelKeyRef = (0, import_react23.useRef)("");
4865
+ const toggleGroup = (groupId) => {
4866
+ setExpandedGroups((prev) => {
4867
+ const next = new Set(prev);
4868
+ if (next.has(groupId)) next.delete(groupId);
4869
+ else next.add(groupId);
4870
+ return next;
4871
+ });
4872
+ };
4873
+ const [treeExpandedRows, setTreeExpandedRows] = (0, import_react23.useState)(/* @__PURE__ */ new Set());
4874
+ const toggleTreeRow = (id) => {
4875
+ setTreeExpandedRows((prev) => {
4876
+ const next = new Set(prev);
4877
+ if (next.has(id)) next.delete(id);
4878
+ else next.add(id);
4879
+ return next;
4880
+ });
4881
+ };
4783
4882
  (0, import_react23.useEffect)(() => {
4784
4883
  const handleMouseMove = (e) => {
4785
4884
  if (!resizingColumn) return;
@@ -4822,6 +4921,13 @@ function DataGrid({
4822
4921
  return next;
4823
4922
  });
4824
4923
  }, [initialColumnsProp]);
4924
+ const getGroupValue = (item, field) => {
4925
+ const col = resolvedColumns.find((c) => String(c.field) === field || String(c.key) === field);
4926
+ const raw = item[field];
4927
+ if (col?.groupingValueGetter) return String(col.groupingValueGetter(raw, item) ?? "");
4928
+ if (col?.valueGetter) return String(col.valueGetter({ value: raw, row: item, field }) ?? "");
4929
+ return raw == null ? "" : String(raw);
4930
+ };
4825
4931
  const onFiltersChangeRef = (0, import_react23.useRef)(onFiltersChange);
4826
4932
  (0, import_react23.useEffect)(() => {
4827
4933
  onFiltersChangeRef.current = onFiltersChange;
@@ -5009,14 +5115,36 @@ function DataGrid({
5009
5115
  return 0;
5010
5116
  });
5011
5117
  }, [filteredData, sortField, sortDirection, resolvedColumns]);
5118
+ (0, import_react23.useEffect)(() => {
5119
+ const key = activeGroupingModel.join("\0");
5120
+ if (key === prevGroupModelKeyRef.current) return;
5121
+ prevGroupModelKeyRef.current = key;
5122
+ if (!activeGroupingModel.length) {
5123
+ setExpandedGroups(/* @__PURE__ */ new Set());
5124
+ return;
5125
+ }
5126
+ const allGroups = getAllGroupIds(sortedData, activeGroupingModel, getGroupValue);
5127
+ setExpandedGroups(new Set(
5128
+ allGroups.filter((g) => isGroupExpandedByDefault ? isGroupExpandedByDefault({ id: g.id, field: g.field, key: g.key, depth: g.depth, count: g.count }) : defaultGroupingExpansionDepth === -1 || g.depth < defaultGroupingExpansionDepth).map((g) => g.id)
5129
+ ));
5130
+ }, [activeGroupingModel.join("\0")]);
5131
+ const resolveChildren = getChildRows ?? ((row) => row.children ?? null);
5132
+ const flatEntries = (0, import_react23.useMemo)(() => {
5133
+ if (treeData) {
5134
+ return buildTreeEntries(sortedData, resolveChildren, treeExpandedRows);
5135
+ }
5136
+ if (!isGroupingActive) return null;
5137
+ return buildFlatEntries(sortedData, activeGroupingModel, getGroupValue, expandedGroups);
5138
+ }, [treeData, isGroupingActive, sortedData, activeGroupingModel.join("\0"), expandedGroups, treeExpandedRows]);
5012
5139
  const isServer = paginationMode === "server";
5013
- const totalRows = isServer ? rowCount ?? data.length : filteredData.length;
5140
+ const totalRows = isServer ? rowCount ?? data.length : flatEntries ? flatEntries.length : filteredData.length;
5014
5141
  const totalPages = Math.max(1, Math.ceil(totalRows / activePageSize));
5015
- const paginatedData = (0, import_react23.useMemo)(() => {
5016
- if (isServer) return data;
5142
+ const displayRows = (0, import_react23.useMemo)(() => {
5143
+ if (isServer) return data.map((row) => ({ kind: "data", row, depth: 0 }));
5144
+ const source = flatEntries ?? sortedData.map((row) => ({ kind: "data", row, depth: 0 }));
5017
5145
  const start = (activePage - 1) * activePageSize;
5018
- return sortedData.slice(start, start + activePageSize);
5019
- }, [isServer, data, sortedData, activePage, activePageSize]);
5146
+ return source.slice(start, start + activePageSize);
5147
+ }, [isServer, data, flatEntries, sortedData, activePage, activePageSize]);
5020
5148
  const handleExport = () => {
5021
5149
  const exportableCols = resolvedColumns.filter((c) => !c.hidden && c.isExportable !== false);
5022
5150
  const headers = exportableCols.map((c) => c.headerName).join(",");
@@ -5066,8 +5194,57 @@ function DataGrid({
5066
5194
  const left = resolvedColumns.filter((c) => !c.hidden && c.pinned === "left");
5067
5195
  const mid = resolvedColumns.filter((c) => !c.hidden && !c.pinned);
5068
5196
  const right = resolvedColumns.filter((c) => !c.hidden && c.pinned === "right");
5069
- return [...left, ...mid, ...right];
5070
- }, [resolvedColumns]);
5197
+ const dataCols = [...left, ...mid, ...right];
5198
+ if (treeData) {
5199
+ const treeCol = {
5200
+ key: "__tree__",
5201
+ field: "__tree__",
5202
+ header: "",
5203
+ headerName: "",
5204
+ width: 44,
5205
+ sortable: false,
5206
+ filterable: false,
5207
+ disableColumnMenu: true,
5208
+ hideable: false
5209
+ };
5210
+ return [treeCol, ...dataCols];
5211
+ }
5212
+ if (!isGroupingActive) return dataCols;
5213
+ if (rowGroupingColumnMode === "multiple") {
5214
+ const groupCols = activeGroupingModel.map((gField, i) => {
5215
+ const src = resolvedColumns.find((c) => String(c.field) === gField || String(c.key) === gField);
5216
+ const def2 = typeof groupingColDef === "function" ? groupingColDef(gField) : groupingColDef;
5217
+ return {
5218
+ key: `__group_${i}__`,
5219
+ field: `__group_${i}__`,
5220
+ header: def2?.headerName ?? src?.header ?? src?.headerName ?? gField,
5221
+ headerName: def2?.headerName ?? src?.header ?? src?.headerName ?? gField,
5222
+ width: def2?.width ?? 160,
5223
+ sortable: false,
5224
+ filterable: false,
5225
+ disableColumnMenu: true,
5226
+ hideable: false,
5227
+ __groupField: gField,
5228
+ __groupIndex: i
5229
+ };
5230
+ });
5231
+ return [...groupCols, ...dataCols];
5232
+ }
5233
+ const def = typeof groupingColDef === "function" ? groupingColDef(activeGroupingModel[0]) : groupingColDef;
5234
+ const singleSrc = resolvedColumns.find((c) => String(c.field) === activeGroupingModel[0] || String(c.key) === activeGroupingModel[0]);
5235
+ const singleGroupCol = {
5236
+ key: "__group__",
5237
+ field: "__group__",
5238
+ header: def?.headerName ?? (activeGroupingModel.length === 1 ? singleSrc?.header ?? singleSrc?.headerName ?? "Group" : "Group"),
5239
+ headerName: def?.headerName ?? (activeGroupingModel.length === 1 ? singleSrc?.header ?? singleSrc?.headerName ?? "Group" : "Group"),
5240
+ width: def?.width ?? 200,
5241
+ sortable: false,
5242
+ filterable: false,
5243
+ disableColumnMenu: true,
5244
+ hideable: false
5245
+ };
5246
+ return [singleGroupCol, ...dataCols];
5247
+ }, [resolvedColumns, isGroupingActive, activeGroupingModel, rowGroupingColumnMode, groupingColDef]);
5071
5248
  const getLeftOffset = (col, idx) => {
5072
5249
  if (col.pinned !== "left") return void 0;
5073
5250
  let offset2 = 0;
@@ -5102,7 +5279,14 @@ function DataGrid({
5102
5279
  };
5103
5280
  const activeMenuCol = activeMenu ? resolvedColumns.find((c) => String(c.field) === activeMenu) : null;
5104
5281
  const alignClass = (align) => align === "center" ? "dg-slot--center" : align === "right" ? "dg-slot--right" : "dg-slot--left";
5105
- return /* @__PURE__ */ import_react23.default.createElement("div", { className: ["dg-root", sxClass, className].filter(Boolean).join(" ") }, /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-header" }, /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-header-info" }, /* @__PURE__ */ import_react23.default.createElement("h2", null, title), /* @__PURE__ */ import_react23.default.createElement("p", null, filteredData.length, " total records")), /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-header-actions" }, /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-search-wrap" }, /* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.Search, { size: 15 }), /* @__PURE__ */ import_react23.default.createElement(
5282
+ const tOpts = toolbarOptions ?? {};
5283
+ const showSearch = !tOpts.hideSearch;
5284
+ const showFilterBtn = !tOpts.hideFilter;
5285
+ const showColumnsBtn = !tOpts.hideColumns;
5286
+ const showExportBtn = !tOpts.hideExport && !hideTopExport;
5287
+ const showTitle = !tOpts.hideTitle;
5288
+ const showRecordCount = !tOpts.hideRecordCount;
5289
+ return /* @__PURE__ */ import_react23.default.createElement("div", { className: ["dg-root", sxClass, className].filter(Boolean).join(" ") }, !tOpts.hideHeader && /* @__PURE__ */ import_react23.default.createElement("div", { className: `dg-header${customToolbar ? " dg-header--custom" : ""}` }, !customToolbar && (showTitle || showRecordCount) && /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-header-info" }, showTitle && /* @__PURE__ */ import_react23.default.createElement("h2", null, title), showRecordCount && /* @__PURE__ */ import_react23.default.createElement("p", null, filteredData.length, " total records")), /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-header-actions" }, customToolbar ?? /* @__PURE__ */ import_react23.default.createElement(import_react23.default.Fragment, null, showSearch && /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-search-wrap" }, /* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.Search, { size: 15 }), /* @__PURE__ */ import_react23.default.createElement(
5106
5290
  "input",
5107
5291
  {
5108
5292
  className: "dg-search",
@@ -5113,23 +5297,35 @@ function DataGrid({
5113
5297
  setCurrentPage(1);
5114
5298
  }
5115
5299
  }
5116
- )), /* @__PURE__ */ import_react23.default.createElement(Tooltip, { title: "Filters", placement: "top" }, /* @__PURE__ */ import_react23.default.createElement(
5300
+ )), showFilterBtn && /* @__PURE__ */ import_react23.default.createElement(Tooltip, { title: "Filters", placement: "top" }, /* @__PURE__ */ import_react23.default.createElement(
5117
5301
  "button",
5118
5302
  {
5119
5303
  className: `dg-icon-btn ${hasActiveFilters ? "active" : ""}`,
5120
5304
  onClick: () => setShowAdvancedFilter(true)
5121
5305
  },
5122
5306
  /* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.Filter, { size: 16 })
5123
- )), /* @__PURE__ */ import_react23.default.createElement(Tooltip, { title: "Manage Columns", placement: "top" }, /* @__PURE__ */ import_react23.default.createElement(
5307
+ )), showColumnsBtn && /* @__PURE__ */ import_react23.default.createElement(Tooltip, { title: "Manage Columns", placement: "top" }, /* @__PURE__ */ import_react23.default.createElement(
5124
5308
  "button",
5125
5309
  {
5126
5310
  className: "dg-icon-btn",
5127
5311
  onClick: () => setShowManageColumns(true)
5128
5312
  },
5129
5313
  /* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.Columns, { size: 16 })
5130
- )), !hideTopExport && /* @__PURE__ */ import_react23.default.createElement("button", { className: "dg-action-btn", onClick: handleExport }, /* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.Download, { size: 14 }), " Export CSV"), headerActions && /* @__PURE__ */ import_react23.default.createElement("div", { className: `dg-header-slot ${alignClass(headerActions.align)}` }, headerActions.content))), /* @__PURE__ */ import_react23.default.createElement("div", { className: `dg-toolbar ${alignClass(toolbarContent?.align)}` }, toolbarContent?.content || ""), /* @__PURE__ */ import_react23.default.createElement("div", { className: `dg-table-wrap${paginatedData.length === 0 && !loading ? " dg-table-wrap--empty" : ""}` }, loading && /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-loading-overlay" }, /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-loading-spinner" })), /* @__PURE__ */ import_react23.default.createElement("table", { className: "dg-table" }, /* @__PURE__ */ import_react23.default.createElement("thead", null, /* @__PURE__ */ import_react23.default.createElement("tr", null, visibleColumns.map((col, idx) => {
5314
+ )), showExportBtn && /* @__PURE__ */ import_react23.default.createElement("button", { className: "dg-action-btn", onClick: handleExport }, /* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.Download, { size: 14 }), " Export CSV")), headerActions && /* @__PURE__ */ import_react23.default.createElement("div", { className: `dg-header-slot ${alignClass(headerActions.align)}` }, headerActions.content))), !tOpts.hideHeader && /* @__PURE__ */ import_react23.default.createElement("div", { className: `dg-toolbar ${alignClass(toolbarContent?.align)}` }, toolbarContent?.content || ""), isGroupingActive && /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-grouping-bar" }, /* @__PURE__ */ import_react23.default.createElement("span", { className: "dg-grouping-bar-label" }, "Grouped by"), activeGroupingModel.map((gField) => {
5315
+ const col = resolvedColumns.find((c) => String(c.field) === gField || String(c.key) === gField);
5316
+ return /* @__PURE__ */ import_react23.default.createElement("div", { key: gField, className: "dg-group-chip" }, /* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.Layers, { size: 11 }), /* @__PURE__ */ import_react23.default.createElement("span", null, col?.header ?? col?.headerName ?? gField), !disableRowGrouping && /* @__PURE__ */ import_react23.default.createElement(
5317
+ "button",
5318
+ {
5319
+ className: "dg-group-chip-remove",
5320
+ onClick: () => removeFromGrouping(gField),
5321
+ title: `Remove grouping by ${col?.header ?? gField}`
5322
+ },
5323
+ /* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.X, { size: 10 })
5324
+ ));
5325
+ })), /* @__PURE__ */ import_react23.default.createElement("div", { className: `dg-table-wrap${filteredData.length === 0 && !loading ? " dg-table-wrap--empty" : ""} ${tOpts.hideHeader ? "rm-top-border" : ""} ${tOpts.hideFooter ? "rm-bottom-border" : ""}` }, loading && /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-loading-overlay" }, /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-loading-spinner" })), /* @__PURE__ */ import_react23.default.createElement("table", { className: "dg-table" }, /* @__PURE__ */ import_react23.default.createElement("thead", null, /* @__PURE__ */ import_react23.default.createElement("tr", null, visibleColumns.map((col, idx) => {
5131
5326
  const colField = String(col.field);
5132
- const width = columnWidths[colField] || 200;
5327
+ const colNativeWidth = col.width ? typeof col.width === "number" ? col.width : parseInt(String(col.width)) : 200;
5328
+ const width = columnWidths[colField] || colNativeWidth;
5133
5329
  const leftOffset = getLeftOffset(col, idx);
5134
5330
  const rightOffset = getRightOffset(col, idx);
5135
5331
  const isSorted = sortField === col.field;
@@ -5180,79 +5376,150 @@ function DataGrid({
5180
5376
  }
5181
5377
  )))
5182
5378
  );
5183
- }), actions && /* @__PURE__ */ import_react23.default.createElement("th", { style: { width: 0, padding: 0 } }))), /* @__PURE__ */ import_react23.default.createElement("tbody", null, paginatedData.length > 0 && paginatedData.map((item) => /* @__PURE__ */ import_react23.default.createElement("tr", { key: item.id, className: "dg-tbody-row", onDoubleClick: () => onRowDoubleClick?.(item) }, visibleColumns.map((col, idx) => {
5184
- const colField = String(col.field);
5185
- const width = columnWidths[colField] || 200;
5186
- const leftOffset = getLeftOffset(col, idx);
5187
- const rightOffset = getRightOffset(col, idx);
5188
- return /* @__PURE__ */ import_react23.default.createElement(
5189
- "td",
5190
- {
5191
- key: `${item.id}-${colField}`,
5192
- className: `dg-td${col.pinned === "left" ? " pinned-left" : col.pinned === "right" ? " pinned-right" : ""}${col.editable ? " dg-td--editable" : ""} ${col.cellClassName || ""}`,
5193
- style: { width, minWidth: width, maxWidth: width, left: leftOffset, right: rightOffset, flex: col.flex },
5194
- onDoubleClick: () => onCellDoubleClick?.({ row: item, field: colField, value: item[col.field || ""] }),
5195
- onClick: col.editable ? () => {
5196
- const field = String(col.field);
5197
- const rawValue = item[col.field || ""];
5198
- const value = col.valueGetter ? col.valueGetter({ value: rawValue, row: item, field }) : rawValue;
5199
- setEditingCell({ rowId: item.id, field, value });
5200
- } : void 0
5201
- },
5202
- (() => {
5203
- const field = String(col.field);
5204
- const rawValue = item[col.field || ""];
5205
- let value = col.valueGetter ? col.valueGetter({ value: rawValue, row: item, field }) : rawValue;
5206
- if (col.editable && editingCell?.rowId === item.id && editingCell?.field === field) {
5207
- const inputType = col.type === "number" ? "number" : col.type === "date" ? "date" : "text";
5208
- const commit = (finalValue) => {
5209
- setEditingCell(null);
5210
- col.onEnter?.({ value: finalValue, row: item, field });
5211
- };
5212
- return /* @__PURE__ */ import_react23.default.createElement(
5213
- "input",
5379
+ }), actions && /* @__PURE__ */ import_react23.default.createElement("th", { style: { width: 0, padding: 0 } }))), /* @__PURE__ */ import_react23.default.createElement("tbody", null, displayRows.length > 0 && displayRows.map((entry) => {
5380
+ if (entry.kind === "group") {
5381
+ const isExpanded = expandedGroups.has(entry.id);
5382
+ const colDef = typeof groupingColDef === "function" ? groupingColDef(entry.field) : groupingColDef;
5383
+ const fieldLabel = resolvedColumns.find(
5384
+ (c) => String(c.field) === entry.field || String(c.key) === entry.field
5385
+ )?.header ?? entry.field;
5386
+ const totalCols = visibleColumns.length + (actions ? 1 : 0);
5387
+ return /* @__PURE__ */ import_react23.default.createElement("tr", { key: entry.id, className: "dg-group-row" }, /* @__PURE__ */ import_react23.default.createElement("td", { colSpan: totalCols, className: "dg-group-cell" }, /* @__PURE__ */ import_react23.default.createElement(
5388
+ "div",
5389
+ {
5390
+ className: "dg-group-cell-inner",
5391
+ style: { paddingLeft: entry.depth * 20 },
5392
+ onClick: () => toggleGroup(entry.id)
5393
+ },
5394
+ /* @__PURE__ */ import_react23.default.createElement("button", { className: "dg-group-toggle", onClick: (e) => {
5395
+ e.stopPropagation();
5396
+ toggleGroup(entry.id);
5397
+ } }, isExpanded ? /* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.ChevronDown, { size: 15 }) : /* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.ChevronRight, { size: 15 })),
5398
+ activeGroupingModel.length > 1 && /* @__PURE__ */ import_react23.default.createElement("span", { className: "dg-group-field-label" }, fieldLabel, ":"),
5399
+ colDef?.renderCell ? colDef.renderCell({ groupKey: entry.key, field: entry.field, depth: entry.depth, leafCount: entry.leafCount }) : /* @__PURE__ */ import_react23.default.createElement("span", { className: "dg-group-key" }, entry.key),
5400
+ !colDef?.hideDescendantCount && /* @__PURE__ */ import_react23.default.createElement("span", { className: "dg-group-count" }, entry.leafCount)
5401
+ )));
5402
+ }
5403
+ const item = entry.row;
5404
+ const rowDepth = entry.depth;
5405
+ return /* @__PURE__ */ import_react23.default.createElement("tr", { key: item.id, className: "dg-tbody-row", onDoubleClick: () => onRowDoubleClick?.(item) }, visibleColumns.map((col, idx) => {
5406
+ const colField = String(col.field);
5407
+ if (colField === "__tree__") {
5408
+ const treeColWidth = columnWidths["__tree__"] || 44;
5409
+ const isExpanded = treeExpandedRows.has(item.id);
5410
+ return /* @__PURE__ */ import_react23.default.createElement(
5411
+ "td",
5412
+ {
5413
+ key: `${item.id}-__tree__`,
5414
+ className: "dg-tree-cell",
5415
+ style: { width: treeColWidth, minWidth: treeColWidth, maxWidth: treeColWidth }
5416
+ },
5417
+ /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-tree-cell-inner", style: { paddingLeft: rowDepth * 20 } }, entry.hasChildren ? /* @__PURE__ */ import_react23.default.createElement(
5418
+ "button",
5214
5419
  {
5215
- className: "dg-cell-editor",
5216
- type: inputType,
5217
- autoFocus: true,
5218
- defaultValue: editingCell.value ?? "",
5219
- onClick: (e) => e.stopPropagation(),
5220
- onKeyDown: (e) => {
5221
- if (e.key === "Enter") commit(e.target.value);
5222
- if (e.key === "Escape") setEditingCell(null);
5223
- },
5224
- onBlur: (e) => {
5225
- const finalValue = e.target.value;
5226
- setEditingCell(null);
5227
- col.onBlur?.({ value: finalValue, row: item, field });
5420
+ className: "dg-group-toggle",
5421
+ onClick: (e) => {
5422
+ e.stopPropagation();
5423
+ toggleTreeRow(item.id);
5228
5424
  }
5229
- }
5230
- );
5231
- }
5232
- const formattedValue = col.valueFormatter ? col.valueFormatter({ value, row: item, field }) : value;
5233
- if (col.renderCell) {
5234
- return col.renderCell({ value, row: item, field });
5235
- }
5236
- if (col.render) {
5237
- return col.render(value, item);
5425
+ },
5426
+ isExpanded ? /* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.ChevronDown, { size: 15 }) : /* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.ChevronRight, { size: 15 })
5427
+ ) : /* @__PURE__ */ import_react23.default.createElement("span", { style: { display: "inline-block", width: 22 } }))
5428
+ );
5429
+ }
5430
+ if (colField === "__group__" || colField.startsWith("__group_")) {
5431
+ const colDef = typeof groupingColDef === "function" ? groupingColDef(colField) : groupingColDef;
5432
+ const leafField = colDef?.leafField;
5433
+ let leafContent = null;
5434
+ if (leafField) {
5435
+ const leafCol = resolvedColumns.find((c) => String(c.field) === leafField || String(c.key) === leafField);
5436
+ if (leafCol) {
5437
+ const raw = item[String(leafCol.field)];
5438
+ let val = leafCol.valueGetter ? leafCol.valueGetter({ value: raw, row: item, field: leafField }) : raw;
5439
+ if (leafCol.valueFormatter) val = leafCol.valueFormatter({ value: val, row: item, field: leafField });
5440
+ if (leafCol.renderCell) leafContent = leafCol.renderCell({ value: val, row: item, field: leafField });
5441
+ else leafContent = val == null ? "" : String(val);
5442
+ }
5238
5443
  }
5239
- return String(formattedValue ?? "");
5240
- })()
5241
- );
5242
- }), actions && /* @__PURE__ */ import_react23.default.createElement("td", { className: "dg-row-actions-cell" }, (() => {
5243
- const resolvedActions = typeof actions === "function" ? actions(item) : actions;
5244
- const visibleActions = resolvedActions.filter((a) => !a.show || a.show(item));
5245
- if (visibleActions.length === 0) return null;
5246
- return /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-row-actions" }, /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-action-group" }, visibleActions.map((action, i) => /* @__PURE__ */ import_react23.default.createElement(Tooltip, { key: i, title: action.label, placement: "top" }, /* @__PURE__ */ import_react23.default.createElement(
5247
- "button",
5248
- {
5249
- className: "dg-row-action-btn",
5250
- style: { color: action.color || "var(--text-secondary)" },
5251
- onClick: () => action.onClick(item)
5252
- },
5253
- action.icon
5254
- )))));
5255
- })()))))), paginatedData.length === 0 && /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-empty-state" }, /* @__PURE__ */ import_react23.default.createElement("svg", { className: "dg-empty-icon", viewBox: "0 0 200 160", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ import_react23.default.createElement("rect", { x: "20", y: "30", width: "160", height: "100", rx: "8", fill: "var(--hover-color)", stroke: "var(--border-color)", strokeWidth: "1.5" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "20", y: "30", width: "160", height: "28", rx: "8", fill: "var(--border-color)", opacity: "0.5" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "20", y: "50", width: "160", height: "8", rx: "0", fill: "var(--border-color)", opacity: "0.5" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "72", y1: "30", x2: "72", y2: "130", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "128", y1: "30", x2: "128", y2: "130", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "20", y1: "78", x2: "180", y2: "78", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "20", y1: "104", x2: "180", y2: "104", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "32", y: "87", width: "28", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.4" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "84", y: "87", width: "28", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.4" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "140", y: "87", width: "28", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.4" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "32", y: "113", width: "20", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.3" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "84", y: "113", width: "32", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.3" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "140", y: "113", width: "20", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.3" }), /* @__PURE__ */ import_react23.default.createElement("circle", { cx: "148", cy: "108", r: "26", fill: "var(--surface-color)", stroke: "var(--border-color)", strokeWidth: "1.5" }), /* @__PURE__ */ import_react23.default.createElement("circle", { cx: "145", cy: "105", r: "10", stroke: "var(--text-secondary)", strokeWidth: "2.5", opacity: "0.5" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "152", y1: "113", x2: "161", y2: "122", stroke: "var(--text-secondary)", strokeWidth: "2.5", strokeLinecap: "round", opacity: "0.5" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "141", y1: "101", x2: "149", y2: "109", stroke: "var(--text-secondary)", strokeWidth: "2", strokeLinecap: "round", opacity: "0.5" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "149", y1: "101", x2: "141", y2: "109", stroke: "var(--text-secondary)", strokeWidth: "2", strokeLinecap: "round", opacity: "0.5" })), /* @__PURE__ */ import_react23.default.createElement("p", { className: "dg-empty-title" }, "No data found"), /* @__PURE__ */ import_react23.default.createElement("p", { className: "dg-empty-subtitle" }, filterText || hasActiveFilters ? "Try adjusting your search or filters" : "No records to display"))), pagination && /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-pagination" }, /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-page-info" }, /* @__PURE__ */ import_react23.default.createElement(Tooltip, { title: "Export CSV", placement: "top" }, /* @__PURE__ */ import_react23.default.createElement("button", { className: "dg-icon-btn", onClick: handleExport }, /* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.Download, { size: 14 }))), /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-per-page" }, /* @__PURE__ */ import_react23.default.createElement("span", null, "Rows per page:"), /* @__PURE__ */ import_react23.default.createElement(
5444
+ const groupColWidth = columnWidths[colField] || 200;
5445
+ return /* @__PURE__ */ import_react23.default.createElement(
5446
+ "td",
5447
+ {
5448
+ key: `${item.id}-${colField}`,
5449
+ className: "dg-group-leaf-cell",
5450
+ style: { width: groupColWidth, minWidth: groupColWidth, maxWidth: groupColWidth, paddingLeft: rowDepth * 20 + 32 }
5451
+ },
5452
+ leafContent
5453
+ );
5454
+ }
5455
+ const width = columnWidths[colField] || 200;
5456
+ const leftOffset = getLeftOffset(col, idx);
5457
+ const rightOffset = getRightOffset(col, idx);
5458
+ return /* @__PURE__ */ import_react23.default.createElement(
5459
+ "td",
5460
+ {
5461
+ key: `${item.id}-${colField}`,
5462
+ className: `dg-td${col.pinned === "left" ? " pinned-left" : col.pinned === "right" ? " pinned-right" : ""}${col.editable ? " dg-td--editable" : ""} ${col.cellClassName || ""}`,
5463
+ style: { width, minWidth: width, maxWidth: width, left: leftOffset, right: rightOffset, flex: col.flex },
5464
+ onDoubleClick: () => onCellDoubleClick?.({ row: item, field: colField, value: item[col.field || ""] }),
5465
+ onClick: col.editable ? () => {
5466
+ const field = String(col.field);
5467
+ const rawValue = item[col.field || ""];
5468
+ const value = col.valueGetter ? col.valueGetter({ value: rawValue, row: item, field }) : rawValue;
5469
+ setEditingCell({ rowId: item.id, field, value });
5470
+ } : void 0
5471
+ },
5472
+ (() => {
5473
+ const field = String(col.field);
5474
+ const rawValue = item[col.field || ""];
5475
+ let value = col.valueGetter ? col.valueGetter({ value: rawValue, row: item, field }) : rawValue;
5476
+ if (col.editable && editingCell?.rowId === item.id && editingCell?.field === field) {
5477
+ const inputType = col.type === "number" ? "number" : col.type === "date" ? "date" : "text";
5478
+ const commit = (finalValue) => {
5479
+ setEditingCell(null);
5480
+ col.onEnter?.({ value: finalValue, row: item, field });
5481
+ };
5482
+ return /* @__PURE__ */ import_react23.default.createElement(
5483
+ "input",
5484
+ {
5485
+ className: "dg-cell-editor",
5486
+ type: inputType,
5487
+ autoFocus: true,
5488
+ defaultValue: editingCell.value ?? "",
5489
+ onClick: (e) => e.stopPropagation(),
5490
+ onKeyDown: (e) => {
5491
+ if (e.key === "Enter") commit(e.target.value);
5492
+ if (e.key === "Escape") setEditingCell(null);
5493
+ },
5494
+ onBlur: (e) => {
5495
+ const finalValue = e.target.value;
5496
+ setEditingCell(null);
5497
+ col.onBlur?.({ value: finalValue, row: item, field });
5498
+ }
5499
+ }
5500
+ );
5501
+ }
5502
+ const formattedValue = col.valueFormatter ? col.valueFormatter({ value, row: item, field }) : value;
5503
+ if (col.renderCell) return col.renderCell({ value, row: item, field });
5504
+ if (col.render) return col.render(value, item);
5505
+ return String(formattedValue ?? "");
5506
+ })()
5507
+ );
5508
+ }), actions && /* @__PURE__ */ import_react23.default.createElement("td", { className: "dg-row-actions-cell" }, (() => {
5509
+ const resolvedActions = typeof actions === "function" ? actions(item) : actions;
5510
+ const visibleActions = resolvedActions.filter((a) => !a.show || a.show(item));
5511
+ if (visibleActions.length === 0) return null;
5512
+ return /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-row-actions" }, /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-action-group" }, visibleActions.map((action, i) => /* @__PURE__ */ import_react23.default.createElement(Tooltip, { key: i, title: action.label, placement: "top" }, /* @__PURE__ */ import_react23.default.createElement(
5513
+ "button",
5514
+ {
5515
+ className: "dg-row-action-btn",
5516
+ style: { color: action.color || "var(--text-secondary)" },
5517
+ onClick: () => action.onClick(item)
5518
+ },
5519
+ action.icon
5520
+ )))));
5521
+ })()));
5522
+ }))), filteredData.length === 0 && /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-empty-state" }, /* @__PURE__ */ import_react23.default.createElement("svg", { className: "dg-empty-icon", viewBox: "0 0 200 160", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ import_react23.default.createElement("rect", { x: "20", y: "30", width: "160", height: "100", rx: "8", fill: "var(--hover-color)", stroke: "var(--border-color)", strokeWidth: "1.5" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "20", y: "30", width: "160", height: "28", rx: "8", fill: "var(--border-color)", opacity: "0.5" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "20", y: "50", width: "160", height: "8", rx: "0", fill: "var(--border-color)", opacity: "0.5" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "72", y1: "30", x2: "72", y2: "130", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "128", y1: "30", x2: "128", y2: "130", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "20", y1: "78", x2: "180", y2: "78", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "20", y1: "104", x2: "180", y2: "104", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "32", y: "87", width: "28", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.4" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "84", y: "87", width: "28", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.4" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "140", y: "87", width: "28", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.4" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "32", y: "113", width: "20", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.3" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "84", y: "113", width: "32", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.3" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "140", y: "113", width: "20", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.3" }), /* @__PURE__ */ import_react23.default.createElement("circle", { cx: "148", cy: "108", r: "26", fill: "var(--surface-color)", stroke: "var(--border-color)", strokeWidth: "1.5" }), /* @__PURE__ */ import_react23.default.createElement("circle", { cx: "145", cy: "105", r: "10", stroke: "var(--text-secondary)", strokeWidth: "2.5", opacity: "0.5" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "152", y1: "113", x2: "161", y2: "122", stroke: "var(--text-secondary)", strokeWidth: "2.5", strokeLinecap: "round", opacity: "0.5" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "141", y1: "101", x2: "149", y2: "109", stroke: "var(--text-secondary)", strokeWidth: "2", strokeLinecap: "round", opacity: "0.5" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "149", y1: "101", x2: "141", y2: "109", stroke: "var(--text-secondary)", strokeWidth: "2", strokeLinecap: "round", opacity: "0.5" })), /* @__PURE__ */ import_react23.default.createElement("p", { className: "dg-empty-title" }, "No data found"), /* @__PURE__ */ import_react23.default.createElement("p", { className: "dg-empty-subtitle" }, filterText || hasActiveFilters ? "Try adjusting your search or filters" : "No records to display"))), customFooter ? /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-pagination dg-pagination--custom" }, customFooter) : pagination && !tOpts.hideFooter && /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-pagination" }, /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-page-info" }, !tOpts.hideBottomExport && !tOpts.hideExport && /* @__PURE__ */ import_react23.default.createElement(Tooltip, { title: "Export CSV", placement: "top" }, /* @__PURE__ */ import_react23.default.createElement("button", { className: "dg-icon-btn", onClick: handleExport }, /* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.Download, { size: 14 }))), /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-per-page" }, /* @__PURE__ */ import_react23.default.createElement("span", null, "Rows per page:"), /* @__PURE__ */ import_react23.default.createElement(
5256
5523
  FilterSelect,
5257
5524
  {
5258
5525
  placement: "top",
@@ -5293,6 +5560,17 @@ function DataGrid({
5293
5560
  setShowAdvancedFilter(true);
5294
5561
  setActiveMenu(null);
5295
5562
  } }, /* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.Filter, { size: 14 }), " Filter\u2026"),
5563
+ !disableRowGrouping && activeMenuCol?.groupable !== false && (() => {
5564
+ const gField = String(activeMenuCol?.field ?? "");
5565
+ const isGrouped = activeGroupingModel.includes(gField);
5566
+ return isGrouped ? /* @__PURE__ */ import_react23.default.createElement("button", { className: "dg-menu-item", onClick: () => {
5567
+ removeFromGrouping(gField);
5568
+ setActiveMenu(null);
5569
+ } }, /* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.Layers, { size: 14 }), " Ungroup") : /* @__PURE__ */ import_react23.default.createElement("button", { className: "dg-menu-item", onClick: () => {
5570
+ addToGrouping(gField);
5571
+ setActiveMenu(null);
5572
+ } }, /* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.Layers, { size: 14 }), " Group by");
5573
+ })(),
5296
5574
  /* @__PURE__ */ import_react23.default.createElement("button", { className: "dg-menu-item", onClick: () => toggleHide(activeMenu) }, /* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.EyeOff, { size: 14 }), " Hide column"),
5297
5575
  /* @__PURE__ */ import_react23.default.createElement("button", { className: "dg-menu-item", onClick: () => {
5298
5576
  setShowManageColumns(true);
@@ -9055,21 +9333,24 @@ PhoneField.displayName = "PhoneField";
9055
9333
  var import_react49 = __toESM(require("react"), 1);
9056
9334
  var import_react_dom11 = __toESM(require("react-dom"), 1);
9057
9335
  var import_lucide_react3 = require("lucide-react");
9336
+ function nodeId(node) {
9337
+ return node.id ?? node._id;
9338
+ }
9058
9339
  function collectDescendants(node) {
9059
- const ids = [String(node.id)];
9340
+ const ids = [String(nodeId(node))];
9060
9341
  node.children?.forEach((c) => ids.push(...collectDescendants(c)));
9061
9342
  return ids;
9062
9343
  }
9063
9344
  function findNodeById(nodes, id) {
9064
9345
  for (const node of nodes) {
9065
- if (String(node.id) === id) return node;
9346
+ if (String(nodeId(node)) === id) return node;
9066
9347
  const found = findNodeById(node.children ?? [], id);
9067
9348
  if (found) return found;
9068
9349
  }
9069
9350
  return null;
9070
9351
  }
9071
9352
  function getNodeState(node, selected) {
9072
- if (selected.has(String(node.id))) return "checked";
9353
+ if (selected.has(String(nodeId(node)))) return "checked";
9073
9354
  if (!node.children?.length) return "unchecked";
9074
9355
  const states = node.children.map((c) => getNodeState(c, selected));
9075
9356
  if (states.every((s2) => s2 === "checked")) return "checked";
@@ -9091,7 +9372,7 @@ function getTopLevelSelected(nodes, selected) {
9091
9372
  function getAllSelected(nodes, selected) {
9092
9373
  const result = [];
9093
9374
  for (const node of nodes) {
9094
- if (selected.has(String(node.id))) result.push(node);
9375
+ if (selected.has(String(nodeId(node)))) result.push(node);
9095
9376
  if (node.children?.length) result.push(...getAllSelected(node.children, selected));
9096
9377
  }
9097
9378
  return result;
@@ -9129,10 +9410,10 @@ function TreeNodeItem({
9129
9410
  onSelect,
9130
9411
  isFiltering
9131
9412
  }) {
9132
- const nodeId = String(node.id);
9413
+ const nid = String(nodeId(node));
9133
9414
  const hasChildren = !!node.children?.length;
9134
- const isExpanded = isFiltering || expanded.has(nodeId);
9135
- const state = allowChildSelection ? getNodeState(node, selected) : selected.has(nodeId) ? "checked" : "unchecked";
9415
+ const isExpanded = isFiltering || expanded.has(nid);
9416
+ const state = allowChildSelection ? getNodeState(node, selected) : selected.has(nid) ? "checked" : "unchecked";
9136
9417
  return /* @__PURE__ */ import_react49.default.createElement("div", { className: "rf-tsn" }, /* @__PURE__ */ import_react49.default.createElement(
9137
9418
  "div",
9138
9419
  {
@@ -9147,7 +9428,7 @@ function TreeNodeItem({
9147
9428
  className: "rf-tsn__expand",
9148
9429
  onClick: (e) => {
9149
9430
  e.stopPropagation();
9150
- onToggleExpand(nodeId);
9431
+ onToggleExpand(nid);
9151
9432
  }
9152
9433
  },
9153
9434
  isExpanded ? /* @__PURE__ */ import_react49.default.createElement(import_lucide_react3.ChevronDown, { size: 14 }) : /* @__PURE__ */ import_react49.default.createElement(import_lucide_react3.ChevronRight, { size: 14 })
@@ -9296,7 +9577,7 @@ function TreeSelect({
9296
9577
  };
9297
9578
  }, [open, computePosition3]);
9298
9579
  const handleSelect = (node) => {
9299
- const nodeId = String(node.id);
9580
+ const nid = String(nodeId(node));
9300
9581
  if (isMultiple) {
9301
9582
  const newSet = new Set(selectedSet);
9302
9583
  if (allowChildSelection) {
@@ -9310,21 +9591,21 @@ function TreeSelect({
9310
9591
  onNodeSelect?.({ node });
9311
9592
  }
9312
9593
  } else {
9313
- if (newSet.has(nodeId)) {
9314
- newSet.delete(nodeId);
9594
+ if (newSet.has(nid)) {
9595
+ newSet.delete(nid);
9315
9596
  onNodeUnselect?.({ node });
9316
9597
  } else {
9317
- newSet.add(nodeId);
9598
+ newSet.add(nid);
9318
9599
  onNodeSelect?.({ node });
9319
9600
  }
9320
9601
  }
9321
- onChange?.({ value: setToRecord(newSet) });
9602
+ onChange?.({ value: setToRecord(newSet), node });
9322
9603
  } else {
9323
- if (selectedSet.has(nodeId)) {
9324
- onChange?.({ value: null });
9604
+ if (selectedSet.has(nid)) {
9605
+ onChange?.({ value: null, node: null });
9325
9606
  onNodeUnselect?.({ node });
9326
9607
  } else {
9327
- onChange?.({ value: node.id });
9608
+ onChange?.({ value: nodeId(node), node });
9328
9609
  onNodeSelect?.({ node });
9329
9610
  closeDropdown();
9330
9611
  }
@@ -9339,7 +9620,7 @@ function TreeSelect({
9339
9620
  };
9340
9621
  const handleClear = (e) => {
9341
9622
  e.stopPropagation();
9342
- onChange?.({ value: isMultiple ? {} : null });
9623
+ onChange?.({ value: isMultiple ? {} : null, node: null });
9343
9624
  };
9344
9625
  const handleRemoveTag = (e, node) => {
9345
9626
  e.stopPropagation();
@@ -9764,10 +10045,13 @@ function SmartSelect({
9764
10045
  return [...selected, ...filteredUnselected];
9765
10046
  }
9766
10047
  if (value != null) {
10048
+ const selectedKey = getValue(value);
9767
10049
  const selectedLabel = getOptionLabel(value);
9768
- if (inputValue2 === selectedLabel) {
9769
- const selectedKey = getValue(value);
10050
+ const inOpts = opts.some((o) => getValue(o) === selectedKey);
10051
+ const selectedFallback = inOpts ? [] : [value];
10052
+ if (!inputValue2 || inputValue2 === selectedLabel) {
9770
10053
  return [
10054
+ ...selectedFallback,
9771
10055
  ...opts.filter((o) => getValue(o) === selectedKey),
9772
10056
  ...opts.filter((o) => getValue(o) !== selectedKey)
9773
10057
  ];