@ioca/react 1.5.18 → 1.5.20

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.
Files changed (62) hide show
  1. package/lib/cjs/components/datagrid/virtual.js +129 -102
  2. package/lib/cjs/components/datagrid/virtual.js.map +1 -1
  3. package/lib/cjs/components/editor/editor.js +4 -2
  4. package/lib/cjs/components/editor/editor.js.map +1 -1
  5. package/lib/cjs/components/form/useForm.js +1 -1
  6. package/lib/cjs/components/form/useForm.js.map +1 -1
  7. package/lib/cjs/components/input/textarea.js +1 -1
  8. package/lib/cjs/components/input/textarea.js.map +1 -1
  9. package/lib/cjs/components/modal/content.js +16 -8
  10. package/lib/cjs/components/modal/content.js.map +1 -1
  11. package/lib/cjs/components/modal/modal.js +60 -19
  12. package/lib/cjs/components/modal/modal.js.map +1 -1
  13. package/lib/cjs/components/modal/modalManager.js +83 -0
  14. package/lib/cjs/components/modal/modalManager.js.map +1 -0
  15. package/lib/cjs/components/tabs/tabs.js +8 -17
  16. package/lib/cjs/components/tabs/tabs.js.map +1 -1
  17. package/lib/cjs/components/tree/item.js +44 -56
  18. package/lib/cjs/components/tree/item.js.map +1 -1
  19. package/lib/cjs/components/tree/tree.js +121 -18
  20. package/lib/cjs/components/tree/tree.js.map +1 -1
  21. package/lib/cjs/components/tree/virtual.js +52 -0
  22. package/lib/cjs/components/tree/virtual.js.map +1 -0
  23. package/lib/cjs/js/hooks.js +5 -2
  24. package/lib/cjs/js/hooks.js.map +1 -1
  25. package/lib/cjs/js/usePreview/content.js +3 -3
  26. package/lib/cjs/js/usePreview/content.js.map +1 -1
  27. package/lib/cjs/js/usePreview/type.js.map +1 -1
  28. package/lib/css/index.css +1 -1
  29. package/lib/css/index.css.map +1 -1
  30. package/lib/css/input.css +8 -8
  31. package/lib/es/components/datagrid/virtual.js +130 -103
  32. package/lib/es/components/datagrid/virtual.js.map +1 -1
  33. package/lib/es/components/editor/editor.js +4 -2
  34. package/lib/es/components/editor/editor.js.map +1 -1
  35. package/lib/es/components/form/useForm.js +1 -1
  36. package/lib/es/components/form/useForm.js.map +1 -1
  37. package/lib/es/components/input/textarea.js +1 -1
  38. package/lib/es/components/input/textarea.js.map +1 -1
  39. package/lib/es/components/modal/content.js +18 -10
  40. package/lib/es/components/modal/content.js.map +1 -1
  41. package/lib/es/components/modal/modal.js +61 -20
  42. package/lib/es/components/modal/modal.js.map +1 -1
  43. package/lib/es/components/modal/modalManager.js +76 -0
  44. package/lib/es/components/modal/modalManager.js.map +1 -0
  45. package/lib/es/components/tabs/tabs.js +8 -17
  46. package/lib/es/components/tabs/tabs.js.map +1 -1
  47. package/lib/es/components/tree/item.js +44 -57
  48. package/lib/es/components/tree/item.js.map +1 -1
  49. package/lib/es/components/tree/tree.js +122 -19
  50. package/lib/es/components/tree/tree.js.map +1 -1
  51. package/lib/es/components/tree/virtual.js +44 -0
  52. package/lib/es/components/tree/virtual.js.map +1 -0
  53. package/lib/es/js/hooks.js +5 -2
  54. package/lib/es/js/hooks.js.map +1 -1
  55. package/lib/es/js/usePreview/content.js +3 -3
  56. package/lib/es/js/usePreview/content.js.map +1 -1
  57. package/lib/es/js/usePreview/type.js.map +1 -1
  58. package/lib/index.js +499 -231
  59. package/lib/types/components/tabs/type.d.ts +1 -1
  60. package/lib/types/components/tree/type.d.ts +8 -5
  61. package/lib/types/js/usePreview/type.d.ts +1 -1
  62. package/package.json +6 -5
package/lib/index.js CHANGED
@@ -5,7 +5,7 @@ import { useState, useRef, useEffect, useCallback, useMemo, Children, cloneEleme
5
5
  import { SkipPreviousRound, CloseRound, MinusRound, PlusRound, InboxTwotone, UndoRound, RedoRound, FormatBoldRound, FormatItalicRound, FormatUnderlinedRound, StrikethroughSRound, ClearAllRound, PlayArrowRound, PauseRound, StopRound, VolumeDownRound, VolumeOffRound, FullscreenRound, FullscreenExitRound, FeedOutlined, AspectRatioRound, OpenInNewRound, FileDownloadOutlined, RotateRightRound, RotateLeftRound, KeyboardArrowLeftRound, KeyboardArrowRightRound, KeyboardDoubleArrowUpRound, SyncAltRound, VisibilityRound, VisibilityOffRound, MoreHorizRound, SearchRound, CheckRound, UnfoldMoreRound, CalendarMonthTwotone, AccessTimeRound, KeyboardArrowDownRound, MoveToInboxTwotone, OutboxTwotone, FilePresentOutlined, DriveFolderUploadOutlined } from '@ricons/material';
6
6
  import { createRoot } from 'react-dom/client';
7
7
  import { getScrollbarSize, List as List$2 } from 'react-window';
8
- import { createPortal } from 'react-dom';
8
+ import { createPortal, flushSync } from 'react-dom';
9
9
  import xss from 'xss';
10
10
  import { renderToStaticMarkup } from 'react-dom/server';
11
11
  import PubSub from 'pubsub-js';
@@ -135,12 +135,15 @@ function useMouseUp(listener, options) {
135
135
  }
136
136
  function useKeydown(listener, options) {
137
137
  initEventsOnce();
138
+ const listenerRef = useRef(listener);
139
+ listenerRef.current = listener;
138
140
  useEffect(() => {
139
141
  if (options?.disabled)
140
142
  return;
141
- KeydownEvents.add(listener);
143
+ const handler = (e) => listenerRef.current(e);
144
+ KeydownEvents.add(handler);
142
145
  return () => {
143
- KeydownEvents.delete(listener);
146
+ KeydownEvents.delete(handler);
144
147
  };
145
148
  }, []);
146
149
  }
@@ -1183,7 +1186,7 @@ function Row(props) {
1183
1186
  }, [data, onRowClick, row]);
1184
1187
  return (jsx("div", { className: 'i-datagrid-row', onClick: handleRowClick, children: columns.map((col, i) => (jsx(Cell, { column: col, col: i, row: row, data: data, cellEllipsis: cellEllipsis, onCellClick: onCellClick, onCellDoubleClick: onCellDoubleClick }, i))) }));
1185
1188
  }
1186
- function Header$1(props) {
1189
+ function Header(props) {
1187
1190
  const { columns, resizable, cellEllipsis, sortBy, sortType, onWidthChange, onHeaderClick, } = props;
1188
1191
  const columnById = useMemo(() => {
1189
1192
  const map = new Map();
@@ -1213,6 +1216,84 @@ function Header$1(props) {
1213
1216
  }) }));
1214
1217
  }
1215
1218
 
1219
+ const VirtualCell = memo(function VirtualCell({ column, data, row, col, isHeader, cellEllipsis, sortBy, sortType, resizable, onWidthChange, }) {
1220
+ const { id, fixed, justify, colSpan, render, title, sorter, renderHeader } = column;
1221
+ const style = useMemo(() => ({
1222
+ "--datagrid-justify": justify,
1223
+ gridColumn: `${col + 1} / span ${colSpan ?? 1}`,
1224
+ insetInline: `var(--datagrid-cell-inset-${col})`,
1225
+ ...(isHeader ? { insetBlockStart: 0 } : null),
1226
+ position: !isHeader && !fixed ? "static" : undefined,
1227
+ }), [col, colSpan, fixed, isHeader, justify]);
1228
+ const order = isHeader && sortBy === id ? sortType : "";
1229
+ return (jsxs("div", { "data-col": id, className: classNames("i-datagrid-cell", {
1230
+ [`i-datagrid-cell-fixed-${fixed}`]: fixed,
1231
+ "i-datagrid-has-sorter": isHeader && sorter,
1232
+ }), style: style, children: [isHeader
1233
+ ? (renderHeader?.(column, col) ?? (jsx("div", { className: classNames("i-datagrid-cell-content", {
1234
+ "i-datagrid-cell-content-ellipsis": cellEllipsis,
1235
+ }), children: title || id })))
1236
+ : (render?.(data?.[id], data, row, col) ?? (jsx("div", { className: classNames("i-datagrid-cell-content", {
1237
+ "i-datagrid-cell-content-ellipsis": cellEllipsis,
1238
+ }), children: data?.[id] }))), isHeader && sorter && jsx(Sorter, { type: order }), isHeader && resizable && (jsx(Resize, { index: col, onWidthChange: onWidthChange }))] }));
1239
+ });
1240
+ // ---------------------------------------------------------------------------
1241
+ // VirtualRow — memoised row rendered by react-window
1242
+ // ---------------------------------------------------------------------------
1243
+ const VirtualRow = memo(function VirtualRow({ index, style: itemStyle, ariaAttributes, rows, columns, columnById, columnIndexById, contentWidthPx, virtualRowHeight, header, striped, cellEllipsis, loaderNode, sortBy, sortType, resizable, onWidthChange, onRowClick, onCellClick, onCellDoubleClick, }) {
1244
+ if (index >= rows.length) {
1245
+ return (jsx("div", { ...ariaAttributes, style: {
1246
+ ...itemStyle,
1247
+ width: contentWidthPx,
1248
+ minWidth: "100%",
1249
+ display: "flex",
1250
+ alignItems: "center",
1251
+ justifyContent: "center",
1252
+ }, children: loaderNode }));
1253
+ }
1254
+ const rowData = rows[index];
1255
+ const rowNum = index + (header ? 1 : 0);
1256
+ const bg = striped && index % 2 === 0 ? "var(--background-1)" : undefined;
1257
+ const handleCellClick = useCallback((e) => {
1258
+ if (!onCellClick)
1259
+ return;
1260
+ const el = e.target?.closest?.(".i-datagrid-cell[data-col]");
1261
+ const id = el?.dataset?.col;
1262
+ if (!id)
1263
+ return;
1264
+ const column = columnById.get(id);
1265
+ const col = columnIndexById.get(id);
1266
+ if (!column || col == null)
1267
+ return;
1268
+ onCellClick(rowData, column, rowNum, col, e);
1269
+ }, [columnById, columnIndexById, onCellClick, rowData, rowNum]);
1270
+ const handleCellDoubleClick = useCallback((e) => {
1271
+ if (!onCellDoubleClick)
1272
+ return;
1273
+ const el = e.target?.closest?.(".i-datagrid-cell[data-col]");
1274
+ const id = el?.dataset?.col;
1275
+ if (!id)
1276
+ return;
1277
+ const column = columnById.get(id);
1278
+ const col = columnIndexById.get(id);
1279
+ if (!column || col == null)
1280
+ return;
1281
+ onCellDoubleClick(rowData, column, rowNum, col, e);
1282
+ }, [columnById, columnIndexById, onCellDoubleClick, rowData, rowNum]);
1283
+ const handleRowClick = useCallback(() => onRowClick?.(rowData, rowNum), [onRowClick, rowData, rowNum]);
1284
+ return (jsx("div", { style: {
1285
+ ...itemStyle,
1286
+ width: contentWidthPx,
1287
+ minWidth: "100%",
1288
+ display: "grid",
1289
+ gridTemplateColumns: "var(--grid-template-columns)",
1290
+ height: virtualRowHeight,
1291
+ "--datagrid-cell-background": bg,
1292
+ }, className: 'i-datagrid-row', onClick: handleRowClick, onClickCapture: handleCellClick, onDoubleClickCapture: handleCellDoubleClick, children: columns.map((c, col) => (jsx(VirtualCell, { column: c, data: rowData, row: rowNum, col: col, cellEllipsis: cellEllipsis, sortBy: sortBy, sortType: sortType, resizable: resizable, onWidthChange: onWidthChange }, c.id))) }));
1293
+ });
1294
+ // ---------------------------------------------------------------------------
1295
+ // VirtualDatagrid
1296
+ // ---------------------------------------------------------------------------
1216
1297
  function VirtualDatagrid(props) {
1217
1298
  const { virtual, columns, rows, header, sortBy, sortType, height, loading, resizable, striped, cellEllipsis, empty, wrapRef, containerRef, onHeaderClick, onWidthChange, onRowClick, onCellClick, onCellDoubleClick, onScroll, } = props;
1218
1299
  const headerRef = useRef(null);
@@ -1225,6 +1306,7 @@ function VirtualDatagrid(props) {
1225
1306
  const rafRef = useRef({
1226
1307
  viewport: 0,
1227
1308
  contentWidth: 0,
1309
+ scrollSync: 0,
1228
1310
  });
1229
1311
  const columnById = useMemo(() => {
1230
1312
  const map = new Map();
@@ -1236,38 +1318,6 @@ function VirtualDatagrid(props) {
1236
1318
  columns.forEach((c, i) => map.set(c.id, i));
1237
1319
  return map;
1238
1320
  }, [columns]);
1239
- const getVirtualCellStyle = useCallback(({ justify, col, colSpan = 1, }) => {
1240
- return {
1241
- "--datagrid-justify": justify,
1242
- gridColumn: `${col + 1} / span ${colSpan}`,
1243
- insetInline: `var(--datagrid-cell-inset-${col})`,
1244
- };
1245
- }, []);
1246
- const renderVirtualCell = useCallback(({ column, data, row, col, isHeader, }) => {
1247
- const { id, fixed, justify, colSpan, render, title, sorter, renderHeader, } = column;
1248
- const style = {
1249
- ...getVirtualCellStyle({ justify, col, colSpan }),
1250
- ...(isHeader ? { insetBlockStart: 0 } : null),
1251
- };
1252
- const order = isHeader && sortBy === id ? sortType : "";
1253
- return (jsxs("div", { "data-col": id, className: classNames("i-datagrid-cell", {
1254
- [`i-datagrid-cell-fixed-${fixed}`]: fixed,
1255
- "i-datagrid-has-sorter": isHeader && sorter,
1256
- }), style: style, children: [isHeader
1257
- ? (renderHeader?.(column, col) ?? (jsx("div", { className: classNames("i-datagrid-cell-content", {
1258
- "i-datagrid-cell-content-ellipsis": cellEllipsis,
1259
- }), children: title || id })))
1260
- : (render?.(data?.[id], data, row, col) ?? (jsx("div", { className: classNames("i-datagrid-cell-content", {
1261
- "i-datagrid-cell-content-ellipsis": cellEllipsis,
1262
- }), children: data?.[id] }))), isHeader && sorter && jsx(Sorter, { type: order }), isHeader && resizable && (jsx(Resize, { index: col, onWidthChange: onWidthChange }))] }, id));
1263
- }, [
1264
- cellEllipsis,
1265
- getVirtualCellStyle,
1266
- onWidthChange,
1267
- resizable,
1268
- sortBy,
1269
- sortType,
1270
- ]);
1271
1321
  const handleHeaderClick = useCallback((e) => {
1272
1322
  const el = e.target?.closest?.(".i-datagrid-cell[data-col]");
1273
1323
  const id = el?.dataset?.col;
@@ -1277,10 +1327,17 @@ function VirtualDatagrid(props) {
1277
1327
  const el = e.currentTarget;
1278
1328
  if (!el)
1279
1329
  return;
1280
- if (headerRef.current &&
1281
- headerRef.current.scrollLeft !== el.scrollLeft) {
1282
- headerRef.current.scrollLeft = el.scrollLeft;
1283
- }
1330
+ if (rafRef.current.scrollSync)
1331
+ return;
1332
+ rafRef.current.scrollSync = requestAnimationFrame(() => {
1333
+ rafRef.current.scrollSync = 0;
1334
+ if (!headerRef.current)
1335
+ return;
1336
+ const target = el.scrollLeft;
1337
+ if (headerRef.current.scrollLeft !== target) {
1338
+ headerRef.current.scrollLeft = target;
1339
+ }
1340
+ });
1284
1341
  }, []);
1285
1342
  const handleReachEnd = useCallback(() => {
1286
1343
  if (virtual.onReachEnd) {
@@ -1310,32 +1367,6 @@ function VirtualDatagrid(props) {
1310
1367
  listEl.scrollLeft = el.scrollLeft;
1311
1368
  }
1312
1369
  }, []);
1313
- const handleBodyClickCapture = useCallback((e, rowData, rowNum) => {
1314
- if (!onCellClick)
1315
- return;
1316
- const el = e.target?.closest?.(".i-datagrid-cell[data-col]");
1317
- const id = el?.dataset?.col;
1318
- if (!id)
1319
- return;
1320
- const column = columnById.get(id);
1321
- const col = columnIndexById.get(id);
1322
- if (!column || col == null)
1323
- return;
1324
- onCellClick(rowData, column, rowNum, col, e);
1325
- }, [columnById, columnIndexById, onCellClick]);
1326
- const handleBodyDoubleClickCapture = useCallback((e, rowData, rowNum) => {
1327
- if (!onCellDoubleClick)
1328
- return;
1329
- const el = e.target?.closest?.(".i-datagrid-cell[data-col]");
1330
- const id = el?.dataset?.col;
1331
- if (!id)
1332
- return;
1333
- const column = columnById.get(id);
1334
- const col = columnIndexById.get(id);
1335
- if (!column || col == null)
1336
- return;
1337
- onCellDoubleClick(rowData, column, rowNum, col, e);
1338
- }, [columnById, columnIndexById, onCellDoubleClick]);
1339
1370
  useEffect(() => {
1340
1371
  const el = wrapRef.current;
1341
1372
  if (!el)
@@ -1406,12 +1437,14 @@ function VirtualDatagrid(props) {
1406
1437
  const contentWidthPx = measuredContentWidth
1407
1438
  ? `${measuredContentWidth}px`
1408
1439
  : "fit-content";
1409
- const headerCells = useMemo(() => columns.map((c, col) => renderVirtualCell({
1410
- column: c,
1411
- row: 0,
1412
- col,
1413
- isHeader: true,
1414
- })), [columns, renderVirtualCell]);
1440
+ const headerCells = useMemo(() => columns.map((c, col) => (jsx(VirtualCell, { column: c, row: 0, col: col, isHeader: true, cellEllipsis: cellEllipsis, sortBy: sortBy, sortType: sortType, resizable: resizable, onWidthChange: onWidthChange }, c.id))), [
1441
+ cellEllipsis,
1442
+ columns,
1443
+ onWidthChange,
1444
+ resizable,
1445
+ sortBy,
1446
+ sortType,
1447
+ ]);
1415
1448
  const headerInnerStyle = useMemo(() => ({
1416
1449
  display: "grid",
1417
1450
  gridTemplateColumns: "var(--grid-template-columns)",
@@ -1431,50 +1464,47 @@ function VirtualDatagrid(props) {
1431
1464
  }
1432
1465
  : undefined, [scrollbarSize]);
1433
1466
  const loaderNode = useMemo(() => virtual.loader ?? jsx(Loading, { className: 'my-12' }), [virtual.loader]);
1434
- const rowComponent = useCallback(({ index, style: itemStyle }) => {
1435
- if (index >= rows.length) {
1436
- return (jsx("div", { style: {
1437
- ...itemStyle,
1438
- width: contentWidthPx,
1439
- minWidth: "100%",
1440
- display: "flex",
1441
- alignItems: "center",
1442
- justifyContent: "center",
1443
- }, children: loaderNode }));
1444
- }
1445
- const rowData = rows[index];
1446
- const rowNum = index + (header ? 1 : 0);
1447
- const bg = striped && index % 2 === 0 ? "var(--background-1)" : undefined;
1448
- return (jsx("div", { style: {
1449
- ...itemStyle,
1450
- width: contentWidthPx,
1451
- minWidth: "100%",
1452
- display: "grid",
1453
- gridTemplateColumns: "var(--grid-template-columns)",
1454
- height: virtual.rowHeight,
1455
- "--datagrid-cell-background": bg,
1456
- }, className: 'i-datagrid-row', onClickCapture: (e) => handleBodyClickCapture(e, rowData, rowNum), onDoubleClickCapture: (e) => handleBodyDoubleClickCapture(e, rowData, rowNum), onClick: () => onRowClick?.(rowData, rowNum), children: columns.map((c, col) => renderVirtualCell({
1457
- column: c,
1458
- data: rowData,
1459
- row: rowNum,
1460
- col,
1461
- })) }));
1462
- }, [
1467
+ // Stable row-component reference so react-window can memoise rows.
1468
+ const rowProps = useMemo(() => ({
1469
+ rows,
1463
1470
  columns,
1471
+ columnById,
1472
+ columnIndexById,
1464
1473
  contentWidthPx,
1465
- handleBodyClickCapture,
1466
- handleBodyDoubleClickCapture,
1474
+ virtualRowHeight: virtual.rowHeight,
1467
1475
  header,
1476
+ striped,
1477
+ cellEllipsis,
1468
1478
  loaderNode,
1479
+ sortBy,
1480
+ sortType,
1481
+ resizable,
1482
+ onWidthChange,
1469
1483
  onRowClick,
1470
- renderVirtualCell,
1484
+ onCellClick,
1485
+ onCellDoubleClick,
1486
+ }), [
1471
1487
  rows,
1472
- striped,
1488
+ columns,
1489
+ columnById,
1490
+ columnIndexById,
1491
+ contentWidthPx,
1473
1492
  virtual.rowHeight,
1493
+ header,
1494
+ striped,
1495
+ cellEllipsis,
1496
+ loaderNode,
1497
+ sortBy,
1498
+ sortType,
1499
+ resizable,
1500
+ onWidthChange,
1501
+ onRowClick,
1502
+ onCellClick,
1503
+ onCellDoubleClick,
1474
1504
  ]);
1475
1505
  return (jsxs("div", { ref: containerRef, className: classNames("i-datagrid", {
1476
1506
  "i-datagrid-loading": loading,
1477
- }), style: { display: "block", width: "100%", maxWidth: "100%" }, children: [header && (jsx("div", { ref: headerRef, className: 'i-datagrid-virtual-header', onScroll: handleHeaderScroll, style: headerWrapStyle, children: jsx("div", { ref: headerInnerRef, className: 'i-datagrid-header i-datagrid-row', style: headerInnerStyle, onClick: handleHeaderClick, children: headerCells }) })), jsx(List$2, { listRef: listRef, rowCount: rows.length + (virtual.hasMore ? 1 : 0), rowHeight: virtual.rowHeight, overscanCount: Math.max(3, virtual.threshold ?? 8), rowProps: {}, style: listStyle, onScroll: handleBodyScroll, onRowsRendered: handleRowsRendered, rowComponent: rowComponent }), rows.length < 1 && !virtual.hasMore && empty] }));
1507
+ }), style: { display: "block", width: "100%", maxWidth: "100%" }, children: [header && (jsx("div", { ref: headerRef, className: 'i-datagrid-virtual-header', onScroll: handleHeaderScroll, style: headerWrapStyle, children: jsx("div", { ref: headerInnerRef, className: 'i-datagrid-header i-datagrid-row', style: headerInnerStyle, onClick: handleHeaderClick, children: headerCells }) })), jsx(List$2, { listRef: listRef, rowCount: rows.length + (virtual.hasMore ? 1 : 0), rowHeight: virtual.rowHeight, overscanCount: Math.max(3, virtual.threshold ?? 8), rowProps: rowProps, style: listStyle, onScroll: handleBodyScroll, onRowsRendered: handleRowsRendered, rowComponent: VirtualRow }), rows.length < 1 && !virtual.hasMore && empty] }));
1478
1508
  }
1479
1509
 
1480
1510
  const Datagrid = (props) => {
@@ -1672,7 +1702,7 @@ const Datagrid = (props) => {
1672
1702
  "i-datagrid-striped": striped,
1673
1703
  }), children: [useVirtual && virtual ? (jsx(VirtualDatagrid, { virtual: virtual, columns: columns, rows: rows, header: header, sortBy: state.sortBy, sortType: state.sortType, height: height, loading: loading, resizable: resizable, striped: striped, cellEllipsis: cellEllipsis, empty: empty, wrapRef: wrapRef, containerRef: container, getRowKey: getRowKey, onHeaderClick: handleHeaderClick, onWidthChange: handleWidthChange, onRowClick: onRowClick, onCellClick: onCellClick, onCellDoubleClick: onCellDoubleClick, onScroll: onScroll })) : (jsxs("div", { ref: container, className: classNames("i-datagrid", {
1674
1704
  "i-datagrid-loading": loading,
1675
- }), onWheel: onScroll, children: [header && (jsx(Header$1, { columns: columns, resizable: resizable, sortType: state.sortType, sortBy: state.sortBy, cellEllipsis: cellEllipsis, onWidthChange: handleWidthChange, onHeaderClick: handleHeaderClick })), rows.map((row, i) => (jsx(Row, { row: i + (header ? 1 : 0), data: row, cellEllipsis: cellEllipsis, columns: columns, onCellClick: onCellClick, onRowClick: onRowClick, onCellDoubleClick: onCellDoubleClick }, getRowKey(row, i) ?? i))), rows.length < 1 && empty] })), loading && renderLoading()] }));
1705
+ }), onWheel: onScroll, children: [header && (jsx(Header, { columns: columns, resizable: resizable, sortType: state.sortType, sortBy: state.sortBy, cellEllipsis: cellEllipsis, onWidthChange: handleWidthChange, onHeaderClick: handleHeaderClick })), rows.map((row, i) => (jsx(Row, { row: i + (header ? 1 : 0), data: row, cellEllipsis: cellEllipsis, columns: columns, onCellClick: onCellClick, onRowClick: onRowClick, onCellDoubleClick: onCellDoubleClick }, getRowKey(row, i) ?? i))), rows.length < 1 && empty] })), loading && renderLoading()] }));
1676
1706
  };
1677
1707
 
1678
1708
  const Description = (props) => {
@@ -1785,7 +1815,7 @@ const List$1 = forwardRef((props, ref) => {
1785
1815
  });
1786
1816
  List$1.Item = Item$4;
1787
1817
 
1788
- const Content$2 = forwardRef((props, ref) => {
1818
+ const Content$3 = forwardRef((props, ref) => {
1789
1819
  const { arrow, arrowProps = {}, className, children, ...restProps } = props;
1790
1820
  const arrowCSS = useMemo(() => {
1791
1821
  let { left = 0, top = 0, pos } = arrowProps;
@@ -2297,7 +2327,7 @@ function Popup(props) {
2297
2327
  window.removeEventListener("scroll", onScrollOrResize, true);
2298
2328
  };
2299
2329
  }, [show]);
2300
- return (jsxs(Fragment, { children: [triggerNode, show && (jsx(Content$2, { ref: contentRef, arrow: arrow && trigger !== "contextmenu", style: {
2330
+ return (jsxs(Fragment, { children: [triggerNode, show && (jsx(Content$3, { ref: contentRef, arrow: arrow && trigger !== "contextmenu", style: {
2301
2331
  ...style,
2302
2332
  position: "fixed",
2303
2333
  }, className: className, ...contentTouch, trigger: triggerRef.current, children: content }))] }));
@@ -2703,6 +2733,7 @@ const Editor = (props) => {
2703
2733
  const [memtionKeyword, setMemtionKeyword] = useState("");
2704
2734
  const [memtionActiveIndex, setMemtionActiveIndex] = useState(0);
2705
2735
  const [activeMemtionIndex, setActiveMemtionIndex] = useState(-1);
2736
+ const activeMemtionIndexRef = useRef(-1);
2706
2737
  const memtionOptions = useMemo(() => {
2707
2738
  if (activeMemtionIndex < 0 || !memtion?.length)
2708
2739
  return [];
@@ -2838,6 +2869,7 @@ const Editor = (props) => {
2838
2869
  memtionTriggerRangeRef.current =
2839
2870
  selectionRef.current?.cloneRange() ?? null;
2840
2871
  pendingMemtionRef.current = true;
2872
+ activeMemtionIndexRef.current = matchedIndex;
2841
2873
  setActiveMemtionIndex(matchedIndex);
2842
2874
  }
2843
2875
  }
@@ -2887,8 +2919,8 @@ const Editor = (props) => {
2887
2919
  setEditorValue(nextValue);
2888
2920
  }
2889
2921
  rememberSelection();
2890
- if (activeMemtionIndex >= 0 && (pendingMemtionRef.current || memtionVisible)) {
2891
- const active = memtion?.[activeMemtionIndex];
2922
+ if (activeMemtionIndexRef.current >= 0 && (pendingMemtionRef.current || memtionVisible)) {
2923
+ const active = memtion?.[activeMemtionIndexRef.current];
2892
2924
  if (!active) {
2893
2925
  hideMemtion();
2894
2926
  return;
@@ -3137,7 +3169,7 @@ class IFormInstance {
3137
3169
  const { id, rules, data } = this;
3138
3170
  if (!rules)
3139
3171
  return data;
3140
- const names = field ? [field] : Object.keys(this.cacheData);
3172
+ const names = field ? [field] : [...new Set([...Object.keys(this.cacheData), ...Object.keys(rules)])];
3141
3173
  let isAllValid = true;
3142
3174
  names.forEach((name) => {
3143
3175
  const o = rules[name];
@@ -3261,14 +3293,21 @@ Form.useForm = useForm;
3261
3293
  Form.Field = Field;
3262
3294
  Form.useConfig = useConfig;
3263
3295
 
3264
- function Content$1(props) {
3296
+ const Content$1 = (props) => {
3265
3297
  const { title, footer, hideCloseButton, footerLeft, okButtonProps, cancelButtonProps, children, onOk, onClose, } = props;
3266
3298
  const showHeader = title || !hideCloseButton;
3299
+ const [loading, setLoading] = useState(false);
3267
3300
  const handleOk = async () => {
3268
- const ret = await onOk?.();
3269
- if (ret === false)
3270
- return;
3271
- onClose?.();
3301
+ setLoading(true);
3302
+ try {
3303
+ const ret = await onOk?.();
3304
+ if (ret === false)
3305
+ return;
3306
+ onClose?.();
3307
+ }
3308
+ finally {
3309
+ setLoading(false);
3310
+ }
3272
3311
  };
3273
3312
  const renderFooter = useMemo(() => {
3274
3313
  if (footer || footer === null)
@@ -3276,19 +3315,94 @@ function Content$1(props) {
3276
3315
  const propsOk = Object.assign({
3277
3316
  children: "确定",
3278
3317
  onClick: handleOk,
3279
- }, okButtonProps);
3318
+ }, okButtonProps, { loading });
3280
3319
  const propsCancel = Object.assign({
3281
3320
  secondary: true,
3282
3321
  children: "关闭",
3283
3322
  onClick: onClose,
3284
3323
  }, cancelButtonProps);
3285
3324
  return (jsxs(Fragment, { children: [footerLeft, jsx(Button, { ...propsOk }), jsx(Button, { ...propsCancel })] }));
3286
- }, [footer, okButtonProps, cancelButtonProps]);
3325
+ }, [footer, okButtonProps, cancelButtonProps, loading]);
3287
3326
  return (jsxs(Fragment, { children: [showHeader && (jsxs("header", { className: 'i-modal-header', children: [title && jsx("b", { children: title }), jsx(Helpericon, { active: !hideCloseButton, className: 'i-modal-close', onClick: onClose })] })), jsx("div", { className: 'i-modal-content', children: children }), jsx("footer", { className: 'i-modal-footer', children: renderFooter })] }));
3288
- }
3327
+ };
3328
+ var Content$2 = memo(Content$1);
3289
3329
 
3290
3330
  const ModalContext = createContext(false);
3291
3331
 
3332
+ const CONTAINER_ID = "i-modal-container";
3333
+ const BACKDROP_ID = "i-modal-backdrop";
3334
+ let containerEl = null;
3335
+ let backdropEl = null;
3336
+ const stack = [];
3337
+ const listeners = new Set();
3338
+ function ensureContainer() {
3339
+ if (containerEl)
3340
+ return containerEl;
3341
+ containerEl = document.createElement("div");
3342
+ containerEl.id = CONTAINER_ID;
3343
+ containerEl.className = "i-modal-container";
3344
+ document.body.append(containerEl);
3345
+ backdropEl = document.createElement("div");
3346
+ backdropEl.id = BACKDROP_ID;
3347
+ backdropEl.className = "i-modal-backdrop";
3348
+ containerEl.prepend(backdropEl);
3349
+ return containerEl;
3350
+ }
3351
+ function syncBackdrop() {
3352
+ if (!backdropEl)
3353
+ return;
3354
+ const show = stack.some((e) => e.visible && !e.hideBackdrop);
3355
+ backdropEl.classList.toggle("i-modal-backdrop-active", show);
3356
+ }
3357
+ function notify$1() {
3358
+ listeners.forEach((fn) => fn());
3359
+ }
3360
+ function register(entry) {
3361
+ ensureContainer();
3362
+ stack.push(entry);
3363
+ syncBackdrop();
3364
+ notify$1();
3365
+ return () => {
3366
+ const idx = stack.findIndex((e) => e.mid === entry.mid);
3367
+ if (idx !== -1)
3368
+ stack.splice(idx, 1);
3369
+ syncBackdrop();
3370
+ notify$1();
3371
+ if (stack.length === 0) {
3372
+ backdropEl?.remove();
3373
+ backdropEl = null;
3374
+ containerEl?.remove();
3375
+ containerEl = null;
3376
+ }
3377
+ };
3378
+ }
3379
+ function updateVisible(mid, visible) {
3380
+ const entry = stack.find((e) => e.mid === mid);
3381
+ if (entry) {
3382
+ entry.visible = visible;
3383
+ syncBackdrop();
3384
+ notify$1();
3385
+ }
3386
+ }
3387
+ function isTop(mid) {
3388
+ const top = getTopVisible();
3389
+ return top?.mid === mid;
3390
+ }
3391
+ function getTopVisible() {
3392
+ for (let i = stack.length - 1; i >= 0; i--) {
3393
+ if (stack[i].visible)
3394
+ return stack[i];
3395
+ }
3396
+ return undefined;
3397
+ }
3398
+ function getContainer() {
3399
+ return ensureContainer();
3400
+ }
3401
+ function subscribe(fn) {
3402
+ listeners.add(fn);
3403
+ return () => listeners.delete(fn);
3404
+ }
3405
+
3292
3406
  function useModal() {
3293
3407
  const ref = useRef(null);
3294
3408
  const handleOpen = (props) => {
@@ -3316,26 +3430,41 @@ function useModal() {
3316
3430
  };
3317
3431
  }
3318
3432
 
3433
+ let midCounter = 0;
3319
3434
  function Modal(props) {
3320
3435
  const { visible, title, footer, okButtonProps, cancelButtonProps, closable = true, hideBackdrop, backdropClosable = true, hideCloseButton, disableEsc, width, height, customized, fixed, hideShadow, children, style, className, keepDOM, footerLeft, onClick, onVisibleChange, onClose, onOk, ...restProps } = props;
3436
+ const midRef = useRef(`modal-${++midCounter}`);
3437
+ const mid = midRef.current;
3321
3438
  const [show, setShow] = useState(visible);
3322
3439
  const [active, setActive] = useState(false);
3323
3440
  const [bounced, setBounced] = useState(false);
3324
3441
  const [mounted, setMounted] = useState(false);
3442
+ const [top, setTop] = useState(false);
3325
3443
  const toggable = useRef(true);
3326
- const handleShow = async () => {
3444
+ const layerRef = useRef(null);
3445
+ const handleShow = useCallback(() => {
3327
3446
  if (!toggable.current)
3328
3447
  return;
3329
- (!keepDOM || !show) && setShow(true);
3448
+ if (!keepDOM || !show)
3449
+ setShow(true);
3330
3450
  toggable.current = false;
3331
- const timer = setTimeout(() => {
3451
+ updateVisible(mid, true);
3452
+ const raf = requestAnimationFrame(() => {
3453
+ if (!layerRef.current) {
3454
+ requestAnimationFrame(() => {
3455
+ setActive(true);
3456
+ onVisibleChange?.(true);
3457
+ toggable.current = true;
3458
+ });
3459
+ return;
3460
+ }
3332
3461
  setActive(true);
3333
3462
  onVisibleChange?.(true);
3334
3463
  toggable.current = true;
3335
- }, 64);
3336
- return () => clearTimeout(timer);
3337
- };
3338
- const handleHide = () => {
3464
+ });
3465
+ return () => cancelAnimationFrame(raf);
3466
+ }, [keepDOM, show, onVisibleChange]);
3467
+ const handleHide = useCallback(() => {
3339
3468
  if (!toggable.current)
3340
3469
  return;
3341
3470
  toggable.current = false;
@@ -3348,28 +3477,53 @@ function Modal(props) {
3348
3477
  return () => clearTimeout(timer);
3349
3478
  }
3350
3479
  setActive(false);
3480
+ updateVisible(mid, false);
3351
3481
  const timer = setTimeout(() => {
3352
- !keepDOM && setShow(false);
3482
+ if (!keepDOM)
3483
+ setShow(false);
3353
3484
  toggable.current = true;
3354
3485
  onVisibleChange?.(false);
3355
3486
  onClose?.();
3356
3487
  }, 240);
3357
3488
  return () => clearTimeout(timer);
3358
- };
3489
+ }, [closable, keepDOM, onClose, onVisibleChange]);
3359
3490
  const handleBackdropClick = () => {
3360
3491
  backdropClosable && handleHide();
3361
3492
  };
3362
- useKeydown((e) => {
3363
- if (e.code !== "Escape" || !visible)
3364
- return;
3365
- handleHide();
3366
- }, { disabled: disableEsc });
3493
+ useEffect(() => {
3494
+ const unsub = subscribe(() => {
3495
+ setTop(isTop(mid));
3496
+ });
3497
+ return unsub;
3498
+ }, []);
3499
+ useEffect(() => {
3500
+ const unregister = register({
3501
+ mid,
3502
+ visible: !!visible,
3503
+ hideBackdrop: !!hideBackdrop,
3504
+ closable: !!closable,
3505
+ });
3506
+ return unregister;
3507
+ }, []);
3367
3508
  useEffect(() => {
3368
3509
  visible ? handleShow() : handleHide();
3369
3510
  }, [visible]);
3511
+ useEffect(() => {
3512
+ if (!show)
3513
+ return;
3514
+ const raf = requestAnimationFrame(() => {
3515
+ setActive(top);
3516
+ });
3517
+ return () => cancelAnimationFrame(raf);
3518
+ }, [top]);
3370
3519
  useEffect(() => {
3371
3520
  setMounted(true);
3372
3521
  }, []);
3522
+ useKeydown((e) => {
3523
+ if (e.code !== "Escape" || !visible || !top)
3524
+ return;
3525
+ handleHide();
3526
+ }, { disabled: disableEsc });
3373
3527
  const handleClick = () => {
3374
3528
  if (typeof document === "undefined")
3375
3529
  return;
@@ -3377,12 +3531,12 @@ function Modal(props) {
3377
3531
  };
3378
3532
  if (!show || !mounted)
3379
3533
  return null;
3380
- return createPortal(jsx("div", { className: classNames("i-modal-container", {
3381
- "i-modal-backdrop": !hideBackdrop,
3382
- "i-modal-customized": customized,
3534
+ return createPortal(jsx("div", { ref: layerRef, className: classNames("i-modal-layer", {
3383
3535
  "i-modal-active": active,
3536
+ "i-modal-customized": customized,
3537
+ "i-modal-hide-backdrop": hideBackdrop,
3384
3538
  fixed,
3385
- }, className), style: style, onClick: handleBackdropClick, "aria-modal": 'true', inert: !active, children: jsx("div", { className: classNames("i-modal", {
3539
+ }, className), style: style, onClick: handleBackdropClick, children: jsx("div", { className: classNames("i-modal", {
3386
3540
  bounced,
3387
3541
  shadow: !hideShadow,
3388
3542
  }), style: {
@@ -3392,7 +3546,7 @@ function Modal(props) {
3392
3546
  e.stopPropagation();
3393
3547
  handleClick();
3394
3548
  onClick?.(e);
3395
- }, role: 'dialog', "aria-labelledby": title ? "modal-title" : undefined, ...restProps, children: jsxs(ModalContext.Provider, { value: true, children: [customized && children, !customized && (jsx(Content$1, { title: title, hideCloseButton: hideCloseButton, footer: footer, okButtonProps: okButtonProps, cancelButtonProps: cancelButtonProps, children: children, footerLeft: footerLeft, onOk: onOk, onClose: handleHide }))] }) }) }), document?.body ?? null);
3549
+ }, role: "dialog", "aria-modal": top, "data-mid": mid, ...restProps, children: jsxs(ModalContext.Provider, { value: true, children: [customized && children, !customized && (jsx(Content$2, { title: title, hideCloseButton: hideCloseButton, footer: footer, okButtonProps: okButtonProps, cancelButtonProps: cancelButtonProps, children: children, footerLeft: footerLeft, onOk: onOk, onClose: handleHide }))] }) }) }), getContainer());
3396
3550
  }
3397
3551
  Modal.useModal = useModal;
3398
3552
 
@@ -3767,7 +3921,7 @@ function renderFile(props) {
3767
3921
  }
3768
3922
 
3769
3923
  function Content(props) {
3770
- const { items = [], initial = 0, renderFile: renderFile$1 = renderFile, onRotate, onChange, onClose, onZoom, } = props;
3924
+ const { items = [], initial = 0, hideControl, renderFile: renderFile$1 = renderFile, onRotate, onChange, onClose, onZoom, } = props;
3771
3925
  const state = useReactive({
3772
3926
  current: initial,
3773
3927
  rotate: 0,
@@ -3903,9 +4057,9 @@ function Content(props) {
3903
4057
  transform: `translate(${state.translate
3904
4058
  .map((n) => `${n}px`)
3905
4059
  .join(",")}) rotate(${state.rotate}deg) scale(${state.scale})`,
3906
- }, onMouseDown: handleMouseDown, onClick: (e) => e.stopPropagation(), children: content }), jsxs("div", { className: classNames("i-preview-controls", {
4060
+ }, onMouseDown: handleMouseDown, onClick: (e) => e.stopPropagation(), children: content }), !hideControl && (jsxs("div", { className: classNames("i-preview-controls", {
3907
4061
  "i-preview-controls-hidden": state.controlHidden,
3908
- }), children: [jsx(Button, { square: true, flat: true, onClick: onClose, children: jsx(Icon, { icon: jsx(CloseRound, {}) }) }), files.length > 1 && (jsxs("span", { className: 'px-8', children: [state.current + 1, " / ", files.length] })), state.scale !== 1 && (jsxs(Button, { flat: true, onClick: () => (state.scale = 1), children: [jsx(Icon, { icon: jsx(AspectRatioRound, {}) }), jsxs("span", { className: 'mt-2', children: [(state.scale * 100).toFixed(0), "%"] })] })), jsx(Button, { square: true, flat: true, href: file.src, target: '_blank', children: jsx(Icon, { icon: jsx(OpenInNewRound, {}) }) }), jsx(Button, { square: true, flat: true, href: file.src, download: true, target: '_blank', children: jsx(Icon, { icon: jsx(FileDownloadOutlined, {}) }) }), jsx(Button, { square: true, flat: true, onClick: () => handleRotate(90), children: jsx(Icon, { icon: jsx(RotateRightRound, {}) }) }), jsx(Button, { square: true, flat: true, onClick: () => handleRotate(-90), children: jsx(Icon, { icon: jsx(RotateLeftRound, {}) }) }), files.length > 1 && (jsxs(Fragment, { children: [jsx(Button, { square: true, flat: true, onClick: () => handleSwitch(state.current - 1), children: jsx(Icon, { icon: jsx(KeyboardArrowLeftRound, {}) }) }), jsx(Button, { square: true, flat: true, onClick: () => handleSwitch(state.current + 1), children: jsx(Icon, { icon: jsx(KeyboardArrowRightRound, {}) }) })] }))] })] }));
4062
+ }), children: [jsx(Button, { square: true, flat: true, onClick: onClose, children: jsx(Icon, { icon: jsx(CloseRound, {}) }) }), files.length > 1 && (jsxs("span", { className: 'px-8', children: [state.current + 1, " / ", files.length] })), state.scale !== 1 && (jsxs(Button, { flat: true, onClick: () => (state.scale = 1), children: [jsx(Icon, { icon: jsx(AspectRatioRound, {}) }), jsxs("span", { className: 'mt-2', children: [(state.scale * 100).toFixed(0), "%"] })] })), jsx(Button, { square: true, flat: true, href: file.src, target: '_blank', children: jsx(Icon, { icon: jsx(OpenInNewRound, {}) }) }), jsx(Button, { square: true, flat: true, href: file.src, download: true, target: '_blank', children: jsx(Icon, { icon: jsx(FileDownloadOutlined, {}) }) }), jsx(Button, { square: true, flat: true, onClick: () => handleRotate(90), children: jsx(Icon, { icon: jsx(RotateRightRound, {}) }) }), jsx(Button, { square: true, flat: true, onClick: () => handleRotate(-90), children: jsx(Icon, { icon: jsx(RotateLeftRound, {}) }) }), files.length > 1 && (jsxs(Fragment, { children: [jsx(Button, { square: true, flat: true, onClick: () => handleSwitch(state.current - 1), children: jsx(Icon, { icon: jsx(KeyboardArrowLeftRound, {}) }) }), jsx(Button, { square: true, flat: true, onClick: () => handleSwitch(state.current + 1), children: jsx(Icon, { icon: jsx(KeyboardArrowRightRound, {}) }) })] }))] }))] }));
3909
4063
  }
3910
4064
 
3911
4065
  function usePreview() {
@@ -4258,7 +4412,7 @@ const Textarea = (props) => {
4258
4412
  onKeyDown: handleKeydown,
4259
4413
  ...restProps,
4260
4414
  };
4261
- return (jsx(InputContainer, { label: label, labelInline: labelInline, className: className, style: { width, ...style }, tip: message ?? tip, status: status, children: jsx("div", { className: classNames("i-input-item", {
4415
+ return (jsx(InputContainer, { label: label, labelInline: labelInline, className: classNames("i-textarea-label", className), style: { width, ...style }, tip: message ?? tip, status: status, children: jsx("div", { className: classNames("i-input-item", {
4262
4416
  [`i-input-${status}`]: status !== "normal",
4263
4417
  "i-input-borderless": !border,
4264
4418
  }), children: jsx("textarea", { ...inputProps }) }) }));
@@ -5925,11 +6079,8 @@ const Tabs = ((props) => {
5925
6079
  });
5926
6080
  sourceKeysRef.current = sourceKeys;
5927
6081
  contentsRef.current = nextContents;
5928
- const nextTabs = [
5929
- ...sourceTabs.filter((tab) => !hiddenSourceKeysRef.current.has(String(tab.key))),
5930
- ...dynamicTabs,
5931
- ];
5932
- setTabs((currentTabs) => isSameTabs(currentTabs, nextTabs) ? currentTabs : nextTabs);
6082
+ const nextTabs = [...sourceTabs.filter((tab) => !hiddenSourceKeysRef.current.has(String(tab.key))), ...dynamicTabs];
6083
+ setTabs((currentTabs) => (isSameTabs(currentTabs, nextTabs) ? currentTabs : nextTabs));
5933
6084
  }, [parsedTabs]);
5934
6085
  const add = (tab, position) => {
5935
6086
  const currentTabs = tabsRef.current;
@@ -5943,9 +6094,7 @@ const Tabs = ((props) => {
5943
6094
  const { content, ...rest } = tab;
5944
6095
  setTabs((ts) => {
5945
6096
  const nextTabs = [...ts];
5946
- const index = position === undefined
5947
- ? nextTabs.length
5948
- : Math.max(0, Math.min(position, nextTabs.length));
6097
+ const index = position === undefined ? nextTabs.length : Math.max(0, Math.min(position, nextTabs.length));
5949
6098
  nextTabs.splice(index, 0, { ...rest, key: tkey });
5950
6099
  return nextTabs;
5951
6100
  });
@@ -6027,8 +6176,7 @@ const Tabs = ((props) => {
6027
6176
  setTabs((ts) => {
6028
6177
  let changed = false;
6029
6178
  const next = ts.map((t) => {
6030
- if (t.intersecting === undefined ||
6031
- t.intersecting === true) {
6179
+ if (t.intersecting === undefined || t.intersecting === true) {
6032
6180
  return t;
6033
6181
  }
6034
6182
  changed = true;
@@ -6049,7 +6197,7 @@ const Tabs = ((props) => {
6049
6197
  return ts;
6050
6198
  if (ts[i]?.intersecting === visible)
6051
6199
  return ts;
6052
- return ts.map((t, idx) => idx === i ? { ...t, intersecting: visible } : t);
6200
+ return ts.map((t, idx) => (idx === i ? { ...t, intersecting: visible } : t));
6053
6201
  });
6054
6202
  });
6055
6203
  });
@@ -6074,10 +6222,9 @@ const Tabs = ((props) => {
6074
6222
  });
6075
6223
  }
6076
6224
  const { offsetHeight, offsetLeft, offsetTop, offsetWidth } = nav;
6077
- const isLine = type === "line";
6078
6225
  setBarStyle({
6079
- height: !vertical && isLine ? ".25em" : offsetHeight * 0.5,
6080
- width: vertical && isLine ? ".25em" : offsetWidth,
6226
+ height: offsetHeight * 0.5,
6227
+ width: offsetWidth,
6081
6228
  transform: `translate(${offsetLeft}px, ${offsetTop}px)`,
6082
6229
  });
6083
6230
  }, 16);
@@ -6119,39 +6266,14 @@ const Tabs = ((props) => {
6119
6266
  navs: navsRef,
6120
6267
  }));
6121
6268
  const cachedTabKeySet = useMemo(() => new Set(cachedTabs), [cachedTabs]);
6122
- const moreTabs = useMemo(() => !hideMore && overflow
6123
- ? tabs.filter((tab) => tab.intersecting === false)
6124
- : [], [hideMore, overflow, tabs]);
6269
+ const moreTabs = useMemo(() => (!hideMore && overflow ? tabs.filter((tab) => tab.intersecting === false) : []), [hideMore, overflow, tabs]);
6125
6270
  return (jsxs("div", { className: classNames("i-tabs", { flex: vertical, [`i-tabs-${type}`]: type !== "default" }, className), ...rest, children: [jsxs("div", { className: classNames("i-tab-navs-container", navsClass, {
6126
6271
  "i-tab-navs-vertical": vertical,
6127
6272
  }), children: [prepend, jsx(TabsNavs$1, { tabs: tabs, moreTabs: moreTabs, activeKey: activeKey, vertical: vertical, overflow: overflow, hideMore: hideMore, navsJustify: navsJustify, bar: bar, barClass: barClass, barStyle: barStyle, navsRef: navsRef, renderMore: renderMore, setNavRef: setNavRef, onOpen: open, onClose: close, onMoreTabClick: handleMoreTabClick, onKeyAction: handleKeyAction }), append] }), jsx(TabsContents$1, { tabs: tabs, activeKey: activeKey, cachedTabKeySet: cachedTabKeySet, getContent: getContent })] }));
6128
6273
  });
6129
6274
  Tabs.Item = Item;
6130
6275
 
6131
- function TreeList(props) {
6132
- const { data = [], depth = 0, round, style, className, parent, nodeProps, ...restProps } = props;
6133
- const contents = data.map((item, i) => {
6134
- const { type } = item;
6135
- const title = item[nodeProps.title];
6136
- const itemKey = item[nodeProps.key] ||
6137
- (parent?.key !== undefined ? `${parent.key}-${i}` : `${i}`);
6138
- item.key = itemKey;
6139
- item.parent = parent;
6140
- if (type === "title") {
6141
- return (jsx("div", { className: 'i-tree-group-title', children: title }, i));
6142
- }
6143
- if (type && type !== "item") {
6144
- return (jsx("div", { className: `i-tree-type-${type}`, children: title }, i));
6145
- }
6146
- return (jsx(TreeItem, { index: i, item: item, depth: depth, nodeProps: nodeProps, ...restProps }, itemKey));
6147
- });
6148
- if (depth > 0)
6149
- return jsx(Fragment, { children: contents });
6150
- return (jsx("div", { className: classNames("i-tree", className, {
6151
- "i-tree-round": round,
6152
- }), style: style, children: contents }));
6153
- }
6154
- const Header = (props) => {
6276
+ const TreeItemHeader = (props) => {
6155
6277
  const { as: Tag = "a", href, selected, children, ...restProps } = props;
6156
6278
  const className = classNames("i-tree-item-header", {
6157
6279
  "i-tree-item-selected": selected,
@@ -6161,50 +6283,166 @@ const Header = (props) => {
6161
6283
  }
6162
6284
  return (jsx(Tag, { to: href || "", className: className, ...restProps, children: children }));
6163
6285
  };
6164
- const TreeItem = (props) => {
6165
- const { item, depth = 0, index, selected, checked = [], partofs = {}, checkable, nodeProps, renderExtra, onItemClick, onItemSelect, onItemCheck, ...restProps } = props;
6166
- const { as, key = "", href, icon, title, expanded, disabled } = item;
6167
- const children = item[nodeProps.children];
6168
- const [expand, setExpand] = useState(expanded);
6169
- const handleExpand = (e, fromToggle) => {
6170
- if (fromToggle) {
6171
- e.preventDefault();
6172
- e.stopPropagation();
6173
- }
6174
- if (disabled || !children?.length)
6175
- return;
6176
- setExpand((v) => !v);
6177
- };
6178
- const handleItemClick = (e) => {
6179
- if (disabled) {
6180
- e.preventDefault();
6181
- e.stopPropagation();
6286
+ function TreeRow(props) {
6287
+ const { flatNode, wrapperStyle, virtualMode, selected, checkedSet, partofs = {}, checkable, nodeProps, renderExtra, loadingKeys, onExpand, onItemClick, onItemSelect, onItemCheck, } = props;
6288
+ const { node, depth, isExpanded } = flatNode;
6289
+ const { key = "", as, href, icon, title, disabled, type } = node;
6290
+ const children = node[nodeProps.children];
6291
+ const hasChildren = children instanceof Promise || (Array.isArray(children) && children.length > 0);
6292
+ const loading = loadingKeys?.includes(key);
6293
+ if (type === "title") {
6294
+ return jsx("div", { style: wrapperStyle, className: "i-tree-group-title", children: title });
6295
+ }
6296
+ if (type && type !== "item") {
6297
+ return jsx("div", { style: wrapperStyle, className: `i-tree-type-${type}`, children: title });
6298
+ }
6299
+ return (jsx("div", { className: !virtualMode ? classNames("i-tree-item", { "i-tree-expand": isExpanded }) : undefined, style: wrapperStyle, children: jsxs(TreeItemHeader, { as: as, href: href, style: { paddingLeft: `${depth * 1.5 + 0.5}em` }, selected: selected === key, onClick: (e) => {
6300
+ if (disabled) {
6301
+ e.preventDefault();
6302
+ e.stopPropagation();
6303
+ return;
6304
+ }
6305
+ if (hasChildren)
6306
+ onExpand(key);
6307
+ onItemClick?.(node, e);
6308
+ onItemSelect?.(key, node);
6309
+ }, children: [checkable && (jsx(Checkbox.Item, { value: checkedSet.has(key), partof: !checkedSet.has(key) && partofs[key], className: "i-tree-checkbox", onChange: () => onItemCheck?.(node, !checkedSet.has(key), []), onClick: (e) => e.stopPropagation() })), icon && jsx("span", { className: "i-tree-item-icon", children: icon }), jsx("span", { className: "i-tree-item-title", children: title }), renderExtra?.(node), hasChildren && (jsx(Icon, { icon: loading ? jsx(Loading, { size: ".86em" }) : jsx(KeyboardArrowDownRound, {}), className: classNames("i-tree-toggle", {
6310
+ "i-tree-expand": virtualMode ? isExpanded : false,
6311
+ }), onClick: (e) => {
6312
+ e.preventDefault();
6313
+ e.stopPropagation();
6314
+ onExpand(key);
6315
+ } }))] }) }));
6316
+ }
6317
+ function TreeList(props) {
6318
+ const { flatNodes, onExpand, selected, checkedSet, partofs = {}, checkable, nodeProps, renderExtra, loadingKeys, round, className, style, onItemClick, onItemSelect, onItemCheck } = props;
6319
+ return (jsx("div", { className: classNames("i-tree", className, {
6320
+ "i-tree-round": round,
6321
+ }), style: style, children: flatNodes.map((flatNode) => {
6322
+ const { key = "" } = flatNode.node;
6323
+ return (jsx(TreeRow, { flatNode: flatNode, selected: selected, checkedSet: checkedSet, partofs: partofs, checkable: checkable, nodeProps: nodeProps, renderExtra: renderExtra, loadingKeys: loadingKeys, onExpand: onExpand, onItemClick: onItemClick, onItemSelect: onItemSelect, onItemCheck: onItemCheck }, key));
6324
+ }) }));
6325
+ }
6326
+
6327
+ function VirtualTree(props) {
6328
+ const { flatNodes, onExpand, selected, checkedSet, partofs = {}, checkable, nodeProps, renderExtra, loadingKeys, height, useVirtual, className, style, onItemClick, onItemSelect, onItemCheck, } = props;
6329
+ const listRef = useRef(null);
6330
+ const wrapRef = useRef(null);
6331
+ const ro = useResizeObserver();
6332
+ const [viewportHeight, setViewportHeight] = useState(0);
6333
+ useEffect(() => {
6334
+ const el = wrapRef.current;
6335
+ if (!el)
6182
6336
  return;
6183
- }
6184
- handleExpand(e);
6185
- onItemClick?.(item, e);
6186
- onItemSelect?.(key, item);
6187
- };
6188
- const handleItemCheck = (checked) => onItemCheck?.(item, checked, []);
6189
- const itemChecked = checked.includes(key);
6190
- return (jsxs("div", { className: classNames("i-tree-item", {
6191
- "i-tree-expand": expand,
6192
- }), children: [jsxs(Header, { as: as, href: href, style: { paddingLeft: `${depth * 1.5 + 0.5}em` }, selected: selected === key, onClick: handleItemClick, children: [checkable && (jsx(Checkbox.Item, { value: itemChecked, partof: !itemChecked && partofs[key], className: 'i-tree-checkbox', onChange: handleItemCheck, onClick: (e) => e.stopPropagation() })), icon && jsx("span", { className: 'i-tree-item-icon', children: icon }), jsx("span", { className: 'i-tree-item-title', children: title }), renderExtra?.(item), children && (jsx(Icon, { icon: jsx(KeyboardArrowDownRound, {}), className: 'i-tree-toggle', onClick: (e) => handleExpand(e, true) }))] }), children?.length && (jsx("div", { className: 'i-tree-item-content', children: jsx(TreeList, { data: children, depth: depth + 1, selected: selected, checkable: checkable, parent: item, partofs: partofs, checked: checked, nodeProps: nodeProps, renderExtra: renderExtra, onItemClick: onItemClick, onItemSelect: onItemSelect, onItemCheck: onItemCheck, ...restProps }) }))] }));
6193
- };
6337
+ const update = () => {
6338
+ const r = el.getBoundingClientRect();
6339
+ setViewportHeight((prev) => (prev === r.height ? prev : r.height));
6340
+ };
6341
+ update();
6342
+ ro.observe?.(el, update);
6343
+ return () => ro.unobserve?.(el);
6344
+ }, [ro]);
6345
+ const listHeight = Math.max(0, (typeof height === "number" ? height : viewportHeight || 360));
6346
+ const propsRef = useRef(props);
6347
+ propsRef.current = props;
6348
+ const rowComponent = useCallback(({ index, style, }) => {
6349
+ const p = propsRef.current;
6350
+ const flatNode = p.flatNodes[index];
6351
+ if (!flatNode)
6352
+ return null;
6353
+ return (jsx(TreeRow, { flatNode: flatNode, wrapperStyle: style, virtualMode: true, selected: p.selected, checkedSet: p.checkedSet, partofs: p.partofs, checkable: p.checkable, nodeProps: p.nodeProps, renderExtra: p.renderExtra, loadingKeys: p.loadingKeys, onExpand: p.onExpand, onItemClick: p.onItemClick, onItemSelect: p.onItemSelect, onItemCheck: p.onItemCheck }));
6354
+ }, []);
6355
+ return (jsx("div", { ref: wrapRef, className: classNames("i-tree", className), style: { display: "block", width: "100%", height: "100%", ...style }, children: jsx(List$2, { listRef: listRef, rowCount: flatNodes.length, rowHeight: useVirtual.rowHeight, overscanCount: Math.max(3, useVirtual.threshold ?? 8), rowProps: {}, style: {
6356
+ width: "100%",
6357
+ height: listHeight,
6358
+ overflow: "auto",
6359
+ }, rowComponent: rowComponent }) }));
6360
+ }
6194
6361
 
6195
6362
  const defaultNodeProps = {
6196
6363
  key: "key",
6197
6364
  title: "title",
6198
6365
  children: "children",
6199
6366
  };
6367
+ function flattenTree(nodes, expandedMap, nodeProps, depth = 0, parentItem, asyncChildrenMap = {}) {
6368
+ const result = [];
6369
+ nodes.forEach((item, i) => {
6370
+ const mapKey = item[nodeProps.key];
6371
+ item.key = mapKey || `${parentItem?.key ?? ""}-${i}`;
6372
+ item.parent = parentItem;
6373
+ const isExpanded = !!expandedMap[item.key];
6374
+ result.push({ node: item, depth, isExpanded });
6375
+ const children = asyncChildrenMap[item.key] || item[nodeProps.children];
6376
+ if (Array.isArray(children) && children.length) {
6377
+ const childNodes = flattenTree(children, expandedMap, nodeProps, depth + 1, item, asyncChildrenMap);
6378
+ if (isExpanded)
6379
+ result.push(...childNodes);
6380
+ }
6381
+ });
6382
+ return result;
6383
+ }
6200
6384
  const Tree = (props) => {
6201
- const { data = [], ref, selected, checked = [], disabledRelated, nodeProps, onItemSelect, onItemCheck, ...restProps } = props;
6385
+ const { data = [], ref, selected, checked = [], disabledRelated, nodeProps, height, useVirtual, onItemSelect, onItemCheck, ...restProps } = props;
6202
6386
  const [selectedKey, setSelectedKey] = useState(selected);
6203
6387
  const [checkedKeys, setCheckedKeys] = useState(checked);
6204
6388
  const [partofs, setPartofs] = useState({});
6389
+ const [loadingMap, setLoadingMap] = useState({});
6390
+ const [asyncChildrenMap, setAsyncChildrenMap] = useState({});
6205
6391
  const nodeMapsRef = useRef(new Map());
6206
- const oNodeProps = Object.assign({}, defaultNodeProps, nodeProps);
6207
- const isChecked = (key) => checkedKeys.includes(key || "");
6392
+ const oNodeProps = useMemo(() => ({ ...defaultNodeProps, ...nodeProps }), [nodeProps]);
6393
+ const checkedSet = useMemo(() => new Set(checkedKeys), [checkedKeys]);
6394
+ const [expandedMap, setExpandedMap] = useState(() => {
6395
+ const map = {};
6396
+ const walk = (nodes, parentKey = "") => {
6397
+ nodes.forEach((item, i) => {
6398
+ const mapKey = item[oNodeProps.key];
6399
+ const key = mapKey || `${parentKey}-${i}`;
6400
+ if (item.expanded)
6401
+ map[key] = true;
6402
+ const children = item[oNodeProps.children];
6403
+ if (Array.isArray(children) && children.length)
6404
+ walk(children, key);
6405
+ });
6406
+ };
6407
+ walk(data);
6408
+ return map;
6409
+ });
6410
+ const handleExpand = (key) => {
6411
+ if (loadingMap[key])
6412
+ return;
6413
+ const item = nodeMapsRef.current.get(key);
6414
+ if (!item)
6415
+ return;
6416
+ const rawChildren = item[oNodeProps.children];
6417
+ const isAsync = rawChildren instanceof Promise;
6418
+ const isExpanded = !!expandedMap[key];
6419
+ if (isAsync && !isExpanded) {
6420
+ flushSync(() => {
6421
+ setLoadingMap((prev) => ({ ...prev, [key]: true }));
6422
+ setExpandedMap((prev) => ({ ...prev, [key]: true }));
6423
+ });
6424
+ rawChildren
6425
+ .then((resolved) => {
6426
+ item[oNodeProps.children] = resolved;
6427
+ setAsyncChildrenMap((prev) => ({ ...prev, [key]: resolved }));
6428
+ })
6429
+ .finally(() => {
6430
+ setLoadingMap((prev) => {
6431
+ const next = { ...prev };
6432
+ delete next[key];
6433
+ return next;
6434
+ });
6435
+ });
6436
+ }
6437
+ else {
6438
+ setExpandedMap((prev) => ({
6439
+ ...prev,
6440
+ [key]: !prev[key],
6441
+ }));
6442
+ }
6443
+ };
6444
+ const flatNodes = useMemo(() => flattenTree(data, expandedMap, oNodeProps, 0, undefined, asyncChildrenMap), [data, expandedMap, oNodeProps, asyncChildrenMap]);
6445
+ const loadingKeys = useMemo(() => Object.keys(loadingMap).filter((k) => loadingMap[k]), [loadingMap]);
6208
6446
  const checkItem = (item, checked, direction) => {
6209
6447
  const { key = "", parent, children } = item;
6210
6448
  const shouldChanged = { [key]: checked };
@@ -6213,7 +6451,9 @@ const Tree = (props) => {
6213
6451
  return [shouldChanged];
6214
6452
  if (checked) {
6215
6453
  if (parent && direction !== "leaf") {
6216
- const hasUnchecked = parent.children?.some((o) => o.key !== key && !isChecked(o.key));
6454
+ const hasUnchecked = Array.isArray(parent.children)
6455
+ ? parent.children.some((o) => o.key !== key && !checkedSet.has(o.key))
6456
+ : false;
6217
6457
  const [changes, parts] = checkItem(parent, true, "root");
6218
6458
  if (!hasUnchecked) {
6219
6459
  Object.assign(shouldChanged, changes);
@@ -6222,9 +6462,9 @@ const Tree = (props) => {
6222
6462
  [parent.key]: true,
6223
6463
  });
6224
6464
  }
6225
- if (children?.length && direction !== "root") {
6465
+ if (Array.isArray(children) && children.length && direction !== "root") {
6226
6466
  children.map((o) => {
6227
- if (isChecked(o.key))
6467
+ if (checkedSet.has(o.key))
6228
6468
  return;
6229
6469
  const [changes] = checkItem(o, true, "leaf");
6230
6470
  Object.assign(shouldChanged, changes);
@@ -6236,16 +6476,18 @@ const Tree = (props) => {
6236
6476
  if (parent && direction !== "leaf") {
6237
6477
  const [changes, parts] = checkItem(parent, false, "root");
6238
6478
  Object.assign(shouldChanged, changes);
6239
- const hasChecked = parent.children?.some((o) => isChecked(o.key) && o.key !== key);
6479
+ const hasChecked = Array.isArray(parent.children)
6480
+ ? parent.children.some((o) => checkedSet.has(o.key) && o.key !== key)
6481
+ : false;
6240
6482
  Object.assign(partofs, hasChecked ? {} : parts, {
6241
6483
  [parent.key]: hasChecked,
6242
6484
  [key]: false,
6243
6485
  });
6244
6486
  }
6245
- if (children?.length && direction !== "root") {
6487
+ if (Array.isArray(children) && children.length && direction !== "root") {
6246
6488
  children.map((o) => {
6247
6489
  const [changes] = checkItem(o, false, "leaf");
6248
- if (!isChecked(o.key))
6490
+ if (!checkedSet.has(o.key))
6249
6491
  return;
6250
6492
  Object.assign(shouldChanged, changes);
6251
6493
  partofs[o.key] = false;
@@ -6256,9 +6498,10 @@ const Tree = (props) => {
6256
6498
  const handleCheck = (item, checked) => {
6257
6499
  const [shouldChanged, partofs] = checkItem(item, checked);
6258
6500
  const changedKeys = Object.keys(shouldChanged);
6501
+ const changedKeysSet = new Set(changedKeys);
6259
6502
  const nextChecked = checked
6260
6503
  ? Array.from(new Set([...checkedKeys, ...changedKeys]))
6261
- : checkedKeys.filter((k) => !changedKeys.includes(k));
6504
+ : checkedKeys.filter((k) => !changedKeysSet.has(k));
6262
6505
  setCheckedKeys(nextChecked);
6263
6506
  setPartofs((p) => ({ ...p, ...partofs }));
6264
6507
  onItemCheck?.(item, checked, nextChecked);
@@ -6276,15 +6519,37 @@ const Tree = (props) => {
6276
6519
  }, [selected]);
6277
6520
  useEffect(() => {
6278
6521
  nodeMapsRef.current.clear();
6279
- const { key, children } = oNodeProps;
6280
- const recursive = (nodes) => {
6281
- nodes.map((o) => {
6282
- nodeMapsRef.current.set(o[key], o);
6283
- o[children]?.length > 0 && recursive(o[children]);
6522
+ const { key: keyProp, children: childrenProp } = oNodeProps;
6523
+ const walk = (nodes, parentKey = "") => {
6524
+ nodes.forEach((item, i) => {
6525
+ const mapKey = item[keyProp];
6526
+ const key = (mapKey || `${parentKey}-${i}`);
6527
+ nodeMapsRef.current.set(key, item);
6528
+ const itemChildren = item[childrenProp];
6529
+ if (Array.isArray(itemChildren) && itemChildren.length) {
6530
+ walk(itemChildren, key);
6531
+ }
6284
6532
  });
6285
6533
  };
6286
- recursive(data);
6287
- }, [data]);
6534
+ walk(data);
6535
+ }, [data, oNodeProps, asyncChildrenMap]);
6536
+ useEffect(() => {
6537
+ if (!props.selected)
6538
+ return;
6539
+ const node = nodeMapsRef.current.get(props.selected);
6540
+ if (!node)
6541
+ return;
6542
+ const toExpand = {};
6543
+ let p = node.parent;
6544
+ while (p) {
6545
+ if (p.key)
6546
+ toExpand[p.key] = true;
6547
+ p = p.parent;
6548
+ }
6549
+ if (Object.keys(toExpand).length > 0) {
6550
+ setExpandedMap((prev) => ({ ...prev, ...toExpand }));
6551
+ }
6552
+ }, [props.selected]);
6288
6553
  useImperativeHandle(ref, () => {
6289
6554
  return {
6290
6555
  getChecked: () => {
@@ -6313,7 +6578,10 @@ const Tree = (props) => {
6313
6578
  },
6314
6579
  };
6315
6580
  });
6316
- return (jsx(TreeList, { data: data, selected: selectedKey, checked: checkedKeys, partofs: partofs, nodeProps: oNodeProps, onItemCheck: handleCheck, onItemSelect: handleSelect, ...restProps }));
6581
+ if (useVirtual) {
6582
+ return (jsx(VirtualTree, { flatNodes: flatNodes, onExpand: handleExpand, height: height, useVirtual: useVirtual, selected: selectedKey, checkedSet: checkedSet, partofs: partofs, nodeProps: oNodeProps, loadingKeys: loadingKeys, onItemCheck: handleCheck, onItemSelect: handleSelect, ...restProps }));
6583
+ }
6584
+ return (jsx(TreeList, { flatNodes: flatNodes, onExpand: handleExpand, selected: selectedKey, checkedSet: checkedSet, partofs: partofs, nodeProps: oNodeProps, loadingKeys: loadingKeys, onItemCheck: handleCheck, onItemSelect: handleSelect, ...restProps }));
6317
6585
  };
6318
6586
 
6319
6587
  const Dropbox = (props) => {