trotl-table 1.0.97 → 1.0.99

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/Table.esm.js CHANGED
@@ -9665,6 +9665,8 @@ function TableInner({
9665
9665
  tableId = "default",
9666
9666
  columns = [],
9667
9667
  data = [],
9668
+ mobileColumnCount = 2,
9669
+ mobileRowClick = null,
9668
9670
  highlightRow = null,
9669
9671
  isGrouped = false,
9670
9672
  groupKey = "groupId",
@@ -9732,6 +9734,8 @@ function TableInner({
9732
9734
  share: "Share",
9733
9735
  duplicate: "Duplicate",
9734
9736
  download: "Download",
9737
+ details: "Details",
9738
+ close: "Close",
9735
9739
  confirmDelete: "Confirm Delete",
9736
9740
  yesDelete: "Yes, Delete",
9737
9741
  areYouSureYouWantToDelete: "Are you sure you want to delete",
@@ -9755,6 +9759,11 @@ function TableInner({
9755
9759
  const duplicateCallbackRef = useRef(duplicateCallback);
9756
9760
  const downloadCallbackRef = useRef(downloadCallback);
9757
9761
  const undeleteCallbackRef = useRef(undeleteCallback);
9762
+ const mobileRowClickRef = useRef(mobileRowClick);
9763
+ const lastTapRef = useRef({
9764
+ timer: null,
9765
+ rowId: null
9766
+ });
9758
9767
  const onCellChangeRef = useRef(onCellChange);
9759
9768
  const cellCallbackRef = useRef(cellCallback);
9760
9769
 
@@ -9766,9 +9775,16 @@ function TableInner({
9766
9775
  editCallbackRef.current = editCallback;
9767
9776
  deleteCallbackRef.current = deleteCallback;
9768
9777
  undeleteCallbackRef.current = undeleteCallback;
9778
+ mobileRowClickRef.current = mobileRowClick;
9769
9779
  onCellChangeRef.current = onCellChange;
9770
9780
  cellCallbackRef.current = cellCallback;
9771
9781
  });
9782
+ const [isMobile, setIsMobile] = useState(() => typeof window !== "undefined" && window.innerWidth <= 768);
9783
+ useEffect(() => {
9784
+ const updateMobileState = () => setIsMobile(window.innerWidth <= 768);
9785
+ window.addEventListener("resize", updateMobileState);
9786
+ return () => window.removeEventListener("resize", updateMobileState);
9787
+ }, []);
9772
9788
 
9773
9789
  // Context menu right click state
9774
9790
  const [contextMenu, setContextMenu] = useState({
@@ -9854,6 +9870,22 @@ function TableInner({
9854
9870
  flex: "1 1 0%"
9855
9871
  };
9856
9872
  }, [columnWidths, getColumnKey]);
9873
+ const allColumns = useMemo(() => {
9874
+ const composedColumns = [...columns];
9875
+ if (Array.isArray(customColumns)) {
9876
+ customColumns.forEach(col => {
9877
+ if (typeof col.place === "number") {
9878
+ composedColumns.splice(col.place, 0, {
9879
+ ...col,
9880
+ isCustom: true
9881
+ });
9882
+ }
9883
+ });
9884
+ }
9885
+ return composedColumns;
9886
+ }, [columns, customColumns]);
9887
+ const normalizedMobileColumnCount = Math.min(2, Math.max(1, Math.floor(Number(mobileColumnCount) || 2)));
9888
+ const visibleColumns = isMobile ? allColumns.slice(0, normalizedMobileColumnCount) : allColumns;
9857
9889
  const getNestedValue = useCallback((row, accessor) => {
9858
9890
  if (!row || accessor == null) return undefined;
9859
9891
  if (Array.isArray(accessor)) {
@@ -10232,6 +10264,21 @@ function TableInner({
10232
10264
  }).filter(Boolean);
10233
10265
  return [...new Set(parts)].join(", ");
10234
10266
  }, []);
10267
+ const formatMobileDetailValue = useCallback((value, col) => {
10268
+ if (value == null || value === "") return "...";
10269
+ if (typeof value === "boolean") return value ? "✓" : "✗";
10270
+ if (Array.isArray(value)) return formatArrayValue(value, col) || "...";
10271
+ if (value instanceof Date) {
10272
+ return formatDateValue(value, getColumnDateFormat(col)) || "...";
10273
+ }
10274
+ if (getColumnType(col) === "date" || col?.dateFormat) {
10275
+ return formatDateValue(value, getColumnDateFormat(col)) || String(value);
10276
+ }
10277
+ if (typeof value === "object") {
10278
+ return String(value.label ?? value.name ?? value.value ?? value);
10279
+ }
10280
+ return String(col?.translate ? translate(String(value)) : value);
10281
+ }, [formatArrayValue, translate]);
10235
10282
 
10236
10283
  // console.log(extraSearchTerm)
10237
10284
 
@@ -10546,6 +10593,7 @@ function TableInner({
10546
10593
  // DELETE
10547
10594
  const [showConfirm, setShowConfirm] = useState(false);
10548
10595
  const [pendingDelete, setPendingDelete] = useState(null);
10596
+ const [mobileDetailRow, setMobileDetailRow] = useState(null);
10549
10597
 
10550
10598
  // perform deletion for a given row (shared by immediate & modal-confirm flows)
10551
10599
  const doDelete = useCallback(async row => {
@@ -10913,7 +10961,7 @@ function TableInner({
10913
10961
  const showUndelete = buttons.includes("undelete");
10914
10962
  const showDuplicate = buttons.includes("duplicate") || buttons.includes("duplicates");
10915
10963
  const showDownload = buttons.includes("download");
10916
- const showActions = showView || showEdit || showDelete || showShare || showUndelete || showDuplicate || showDownload;
10964
+ const showActions = !isMobile && (showView || showEdit || showDelete || showShare || showUndelete || showDuplicate || showDownload);
10917
10965
  const visibleActionCount = [showView, showEdit, showDuplicate, showDelete, showShare, showUndelete, showDownload].filter(Boolean).length;
10918
10966
  const actionColumnStyle = useMemo(() => {
10919
10967
  if (!showActions) return undefined;
@@ -11141,19 +11189,6 @@ function TableInner({
11141
11189
  const visualIndex = tableDataFlat.slice(0, index).filter(i => i.type === "row").length + 1;
11142
11190
  const highlightRowStyle = getHighlightRowStyle(row);
11143
11191
 
11144
- // Compose columns + custom columns
11145
- const allColumns = [...columns];
11146
- // Insert custom columns at specified places
11147
- if (Array.isArray(customColumns)) {
11148
- customColumns.forEach(col => {
11149
- if (typeof col.place === "number") {
11150
- allColumns.splice(col.place, 0, {
11151
- ...col,
11152
- isCustom: true
11153
- });
11154
- }
11155
- });
11156
- }
11157
11192
  // handler called when user double-clicks the non-action portion of the row
11158
11193
  const handleRowDoubleClick = e => {
11159
11194
  if (!doubleClickEnable) return;
@@ -11163,13 +11198,60 @@ function TableInner({
11163
11198
  }
11164
11199
  editCallbackRef.current(row);
11165
11200
  };
11201
+ const handleMobileRowClick = event => {
11202
+ if (!isMobile || event.target.closest(".checkbox-cell, .action-cell, button, input, select, textarea, a, [contenteditable='true']")) {
11203
+ return;
11204
+ }
11205
+ const openSingleTap = () => {
11206
+ if (typeof mobileRowClickRef.current === "function") {
11207
+ mobileRowClickRef.current(row);
11208
+ } else {
11209
+ setMobileDetailRow(row);
11210
+ }
11211
+ };
11212
+ const hasContextActions = Array.isArray(enableMouseRightClick) && enableMouseRightClick.length > 0;
11213
+ if (!hasContextActions) {
11214
+ openSingleTap();
11215
+ return;
11216
+ }
11217
+
11218
+ // Double tap opens the context menu; a single tap waits briefly so
11219
+ // the first tap of a double tap does not open the details modal.
11220
+ const tap = lastTapRef.current;
11221
+ if (tap.timer && tap.rowId === row.id) {
11222
+ window.clearTimeout(tap.timer);
11223
+ lastTapRef.current = {
11224
+ timer: null,
11225
+ rowId: null
11226
+ };
11227
+ setContextMenu({
11228
+ visible: true,
11229
+ x: Math.max(0, Math.min(event.clientX, window.innerWidth - 180)),
11230
+ y: Math.max(0, Math.min(event.clientY, window.innerHeight - 160)),
11231
+ row
11232
+ });
11233
+ return;
11234
+ }
11235
+ window.clearTimeout(tap.timer);
11236
+ lastTapRef.current = {
11237
+ rowId: row.id,
11238
+ timer: window.setTimeout(() => {
11239
+ lastTapRef.current = {
11240
+ timer: null,
11241
+ rowId: null
11242
+ };
11243
+ openSingleTap();
11244
+ }, 280)
11245
+ };
11246
+ };
11166
11247
  const content = /*#__PURE__*/React__default.createElement("div", {
11167
11248
  key: key,
11168
11249
  style: highlightRowStyle ? {
11169
11250
  ...style,
11170
11251
  ...highlightRowStyle
11171
11252
  } : style,
11172
- className: "table-row",
11253
+ className: isMobile ? "table-row mobile-clickable" : "table-row",
11254
+ onClick: isMobile ? handleMobileRowClick : undefined,
11173
11255
  onContextMenu: e => {
11174
11256
  if (!Array.isArray(enableMouseRightClick) || enableMouseRightClick.length === 0) return;
11175
11257
  e.preventDefault();
@@ -11203,14 +11285,20 @@ function TableInner({
11203
11285
  minWidth: keyWidth,
11204
11286
  maxWidth: keyWidth
11205
11287
  } : undefined
11206
- }, visualIndex), allColumns.map((col, i) => {
11288
+ }, visualIndex), visibleColumns.map((col, i) => {
11207
11289
  const resolvedAccessor = getAccessorKey(row, col.accessor);
11208
11290
  const cellValue = getAccessorValue(row, col.accessor);
11209
11291
  const cellStyle = getCellStyle(col, i);
11210
11292
  return /*#__PURE__*/React__default.createElement("div", {
11211
11293
  key: i,
11212
11294
  className: "table-cell",
11213
- style: cellStyle,
11295
+ style: isMobile ? {
11296
+ ...cellStyle,
11297
+ flex: "1 1 0%",
11298
+ width: undefined,
11299
+ minWidth: 0,
11300
+ maxWidth: "100%"
11301
+ } : cellStyle,
11214
11302
  onClick: () => triggerCellCallback(row, col)
11215
11303
  }, col.isCustom ? typeof col.render === "function" ? col.render(row, i) : col.render : renderCell(cellValue, resolvedAccessor, row, col));
11216
11304
  })), showActions && /*#__PURE__*/React__default.createElement("div", {
@@ -11295,7 +11383,7 @@ function TableInner({
11295
11383
  }, content);
11296
11384
  }
11297
11385
  return content;
11298
- }, [tableDataFlat, columns, selectedRows, toggleRowSelection, groupRowsById, renderCell, getCellStyle, showActions, showDelete, showEdit, showKey, showView, translate, enableDragRow, moveRow, enableMultiSelect, rowHeight, tableId, customColumns, doubleClickEnable, keyWidth, actionColumnStyle, showDuplicate, showDownload, showIcons, showShare, getHighlightRowStyle, triggerCellCallback, getAccessorKey, getAccessorValue]);
11386
+ }, [tableDataFlat, visibleColumns, selectedRows, toggleRowSelection, groupRowsById, renderCell, getCellStyle, showActions, showDelete, showEdit, showKey, showView, translate, enableDragRow, moveRow, enableMultiSelect, rowHeight, tableId, customColumns, isMobile, enableMouseRightClick, doubleClickEnable, keyWidth, actionColumnStyle, showDuplicate, showDownload, showIcons, showShare, getHighlightRowStyle, triggerCellCallback, getAccessorKey, getAccessorValue]);
11299
11387
  const rightClickActions = Array.isArray(enableMouseRightClick) ? enableMouseRightClick : [];
11300
11388
  const [hoveredActionIndex, setHoveredActionIndex] = useState(null);
11301
11389
  const rowHeightGetter = ({
@@ -11325,19 +11413,6 @@ function TableInner({
11325
11413
  externalDrop(node);
11326
11414
  }
11327
11415
  }, [useExternalDndContext, enableExternalRowDrop, externalDrop]);
11328
-
11329
- // Compose columns + custom columns for header
11330
- const allColumns = [...columns];
11331
- if (Array.isArray(customColumns)) {
11332
- customColumns.forEach(col => {
11333
- if (typeof col.place === "number") {
11334
- allColumns.splice(col.place, 0, {
11335
- ...col,
11336
- isCustom: true
11337
- });
11338
- }
11339
- });
11340
- }
11341
11416
  const tableMarkup = /*#__PURE__*/React__default.createElement("div", {
11342
11417
  className: "table-container",
11343
11418
  ref: setTableRef,
@@ -11378,14 +11453,20 @@ function TableInner({
11378
11453
  minWidth: keyWidth,
11379
11454
  maxWidth: keyWidth
11380
11455
  } : undefined
11381
- }, "#"), allColumns.map((col, i) => {
11456
+ }, "#"), visibleColumns.map((col, i) => {
11382
11457
  const cellStyle = getCellStyle(col, i);
11383
11458
  const translatedHeader = typeof col.header === "string" ? translate(col.header) : col.header;
11384
11459
  const headerTitle = typeof translatedHeader === "string" ? translatedHeader : undefined;
11385
11460
  return /*#__PURE__*/React__default.createElement("div", {
11386
11461
  key: i,
11387
11462
  className: "table-cell header-cell",
11388
- style: cellStyle,
11463
+ style: isMobile ? {
11464
+ ...cellStyle,
11465
+ flex: "1 1 0%",
11466
+ width: undefined,
11467
+ minWidth: 0,
11468
+ maxWidth: "100%"
11469
+ } : cellStyle,
11389
11470
  title: headerTitle,
11390
11471
  onClick: col.isCustom ? undefined : () => setSort(col.accessor)
11391
11472
  }, col.isCustom ? translatedHeader ?? "" : translatedHeader, !col.isCustom && isSameAccessor(sorting?.column, col.accessor) && (sorting.direction === "asc" ? " ↑" : " ↓"), /*#__PURE__*/React__default.createElement("div", {
@@ -11478,7 +11559,22 @@ function TableInner({
11478
11559
  }, typeof contextMenuModal.component === "function" ? /*#__PURE__*/React__default.createElement(contextMenuModal.component, {
11479
11560
  row: contextMenu.row
11480
11561
  }) : contextMenuModal.component) : null;
11481
- return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, tableMarkup, contextMenuMarkup, contextMenuModalMarkup);
11562
+ const mobileDetailModalMarkup = mobileDetailRow ? /*#__PURE__*/React__default.createElement(Modal, {
11563
+ isOpen: true,
11564
+ title: String(mobileDetailRow.name ?? mobileDetailRow.title ?? translate("details")),
11565
+ onCancel: () => setMobileDetailRow(null),
11566
+ cancelLabel: translate("close"),
11567
+ showCancel: true
11568
+ }, /*#__PURE__*/React__default.createElement("dl", {
11569
+ className: "mobile-row-details"
11570
+ }, allColumns.map((col, index) => {
11571
+ const header = typeof col.header === "string" ? translate(col.header) : col.header;
11572
+ const value = col.isCustom ? typeof col.render === "function" ? col.render(mobileDetailRow, index) : col.render ?? "..." : formatMobileDetailValue(getAccessorValue(mobileDetailRow, col.accessor), col);
11573
+ return /*#__PURE__*/React__default.createElement(React__default.Fragment, {
11574
+ key: index
11575
+ }, /*#__PURE__*/React__default.createElement("dt", null, header), /*#__PURE__*/React__default.createElement("dd", null, value));
11576
+ }))) : null;
11577
+ return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, tableMarkup, contextMenuMarkup, contextMenuModalMarkup, mobileDetailModalMarkup);
11482
11578
  }
11483
11579
  function Table(props) {
11484
11580
  const {