@open-mercato/ui 0.6.7-develop.6553.1.2ec7246e04 → 0.6.7-develop.6565.1.1026d85fff

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.
@@ -200,6 +200,8 @@ function resolveExportSections(config) {
200
200
  }
201
201
  const PERSPECTIVE_COOKIE_PREFIX = "om_table_perspective";
202
202
  const PERSPECTIVE_STORAGE_PREFIX = "om_table_perspective_snapshot";
203
+ const COLUMN_MIN_WIDTH = 60;
204
+ const COLUMN_MAX_WIDTH = 900;
203
205
  function formatDurationLabel(durationMs) {
204
206
  if (durationMs == null) return "";
205
207
  if (!Number.isFinite(durationMs)) return "";
@@ -271,6 +273,16 @@ function sanitizePerspectiveSettings(source) {
271
273
  result.columnVisibility = visibility;
272
274
  }
273
275
  }
276
+ if (source.columnSizing && typeof source.columnSizing === "object") {
277
+ const sizing = {};
278
+ for (const [key, value] of Object.entries(source.columnSizing)) {
279
+ const id = typeof key === "string" ? key.trim() : "";
280
+ if (!id || forbidden.has(id)) continue;
281
+ if (typeof value !== "number" || !Number.isFinite(value)) continue;
282
+ sizing[id] = Math.max(COLUMN_MIN_WIDTH, Math.min(COLUMN_MAX_WIDTH, Math.round(value)));
283
+ }
284
+ if (Object.keys(sizing).length) result.columnSizing = sizing;
285
+ }
274
286
  if (Array.isArray(source.sorting)) {
275
287
  const sorting = source.sorting.map((item) => {
276
288
  const id = typeof item?.id === "string" ? item.id.trim() : "";
@@ -487,7 +499,86 @@ function HeaderDndWrapper({ enabled, contextId, sensors, columnIds, onDragEnd, c
487
499
  if (!enabled) return /* @__PURE__ */ jsx(Fragment, { children });
488
500
  return /* @__PURE__ */ jsx(DndContext, { id: contextId, sensors, collisionDetection: closestCenter, onDragEnd, children: /* @__PURE__ */ jsx(SortableContext, { items: columnIds, strategy: horizontalListSortingStrategy, children }) });
489
501
  }
490
- function SortableHeaderCell({ id, children, className }) {
502
+ function ColumnResizeHandle({
503
+ columnId,
504
+ onResize,
505
+ onCommit,
506
+ onReset,
507
+ ariaLabel
508
+ }) {
509
+ const [active, setActive] = React.useState(false);
510
+ const dragCleanupRef = React.useRef(null);
511
+ React.useEffect(() => () => {
512
+ dragCleanupRef.current?.();
513
+ }, []);
514
+ const handlePointerDown = React.useCallback((event) => {
515
+ if (event.button !== 0) return;
516
+ event.preventDefault();
517
+ event.stopPropagation();
518
+ const headerCell = event.currentTarget.parentElement;
519
+ if (!headerCell) return;
520
+ const startX = event.clientX;
521
+ const startWidth = headerCell.getBoundingClientRect().width;
522
+ setActive(true);
523
+ let frame = 0;
524
+ let latest = startWidth;
525
+ const onMove = (moveEvent) => {
526
+ latest = Math.max(COLUMN_MIN_WIDTH, Math.min(COLUMN_MAX_WIDTH, Math.round(startWidth + (moveEvent.clientX - startX))));
527
+ if (frame) return;
528
+ frame = window.requestAnimationFrame(() => {
529
+ frame = 0;
530
+ onResize(columnId, latest);
531
+ });
532
+ };
533
+ const teardown = () => {
534
+ if (frame) window.cancelAnimationFrame(frame);
535
+ document.removeEventListener("pointermove", onMove);
536
+ document.removeEventListener("pointerup", onUp);
537
+ document.body.style.cursor = "";
538
+ document.body.style.userSelect = "";
539
+ dragCleanupRef.current = null;
540
+ };
541
+ const onUp = () => {
542
+ onResize(columnId, latest);
543
+ teardown();
544
+ setActive(false);
545
+ onCommit();
546
+ };
547
+ dragCleanupRef.current = teardown;
548
+ document.addEventListener("pointermove", onMove);
549
+ document.addEventListener("pointerup", onUp);
550
+ document.body.style.cursor = "col-resize";
551
+ document.body.style.userSelect = "none";
552
+ }, [columnId, onResize, onCommit]);
553
+ return /* @__PURE__ */ jsx(
554
+ "div",
555
+ {
556
+ role: "separator",
557
+ "aria-orientation": "vertical",
558
+ "aria-label": ariaLabel,
559
+ title: ariaLabel,
560
+ onPointerDown: handlePointerDown,
561
+ onClick: (event) => event.stopPropagation(),
562
+ onDoubleClick: (event) => {
563
+ event.preventDefault();
564
+ event.stopPropagation();
565
+ onReset(columnId);
566
+ },
567
+ className: "group/resize absolute right-0 top-0 z-10 flex h-full w-3 translate-x-1/2 cursor-col-resize touch-none select-none items-center justify-center",
568
+ children: /* @__PURE__ */ jsx(
569
+ "span",
570
+ {
571
+ "aria-hidden": true,
572
+ className: cn(
573
+ "w-0.5 rounded-full transition-all",
574
+ active ? "h-full bg-primary" : "h-3.5 bg-border group-hover:h-full group-hover:bg-border group-hover/resize:h-full group-hover/resize:bg-primary"
575
+ )
576
+ }
577
+ )
578
+ }
579
+ );
580
+ }
581
+ function SortableHeaderCell({ id, children, className, width }) {
491
582
  const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id });
492
583
  const isSticky = typeof className === "string" && className.includes("sticky");
493
584
  const style = {
@@ -495,7 +586,8 @@ function SortableHeaderCell({ id, children, className }) {
495
586
  transition,
496
587
  opacity: isDragging ? 0.5 : 1,
497
588
  cursor: "grab",
498
- position: isSticky ? "sticky" : "relative"
589
+ position: isSticky ? "sticky" : "relative",
590
+ ...typeof width === "number" ? { width, minWidth: width, maxWidth: width } : {}
499
591
  };
500
592
  return /* @__PURE__ */ jsx(TableHead, { ref: setNodeRef, style, className, ...attributes, ...listeners, children });
501
593
  }
@@ -716,6 +808,11 @@ function DataTable({
716
808
  const [activePerspectiveId, setActivePerspectiveId] = React.useState(initialActiveId);
717
809
  const [columnVisibility, setColumnVisibility] = React.useState(() => mergedInitialSettings?.columnVisibility ?? {});
718
810
  const [columnOrder, setColumnOrder] = React.useState(() => mergedInitialSettings?.columnOrder ?? []);
811
+ const [columnSizing, setColumnSizing] = React.useState(() => mergedInitialSettings?.columnSizing ?? {});
812
+ const columnSizingRef = React.useRef(columnSizing);
813
+ React.useEffect(() => {
814
+ columnSizingRef.current = columnSizing;
815
+ }, [columnSizing]);
719
816
  const [deletingIds, setDeletingIds] = React.useState([]);
720
817
  const [roleClearingIds, setRoleClearingIds] = React.useState([]);
721
818
  const [perspectiveApiMissing, setPerspectiveApiMissing] = React.useState(false);
@@ -1147,12 +1244,13 @@ function DataTable({
1147
1244
  const candidate = {
1148
1245
  columnOrder,
1149
1246
  columnVisibility: visibility,
1247
+ columnSizing,
1150
1248
  sorting,
1151
1249
  filters: filtersPayload,
1152
1250
  searchValue
1153
1251
  };
1154
1252
  return sanitizePerspectiveSettings(candidate) ?? {};
1155
- }, [columnOrder, columnVisibility, sorting, filterValues, searchValue, advancedFilter]);
1253
+ }, [columnOrder, columnVisibility, columnSizing, sorting, filterValues, searchValue, advancedFilter]);
1156
1254
  const applyPerspectiveSettings = React.useCallback((settings, nextId, options) => {
1157
1255
  const normalized = sanitizePerspectiveSettings(settings) ?? {};
1158
1256
  if (normalized.columnOrder && normalized.columnOrder.length) {
@@ -1174,6 +1272,13 @@ function DataTable({
1174
1272
  setSorting([]);
1175
1273
  onSortingChange?.([]);
1176
1274
  }
1275
+ if (normalized.columnSizing) {
1276
+ setColumnSizing(normalized.columnSizing);
1277
+ columnSizingRef.current = normalized.columnSizing;
1278
+ } else {
1279
+ setColumnSizing({});
1280
+ columnSizingRef.current = {};
1281
+ }
1177
1282
  if (!options?.preserveAdvancedFilter) {
1178
1283
  const restoredTree = isPersistedFilterTree(normalized.filters) ? deserializeTreeFromPersist(normalized.filters) : null;
1179
1284
  if (advancedFilter?.onApplyTree) {
@@ -1194,12 +1299,50 @@ function DataTable({
1194
1299
  const snapshot = { perspectiveId: nextId, settings: normalized, updatedAt: Date.now() };
1195
1300
  writePerspectiveSnapshot(perspectiveTableId, snapshot);
1196
1301
  initialSnapshotRef.current = snapshot;
1302
+ } else if (normalized.columnSizing && Object.keys(normalized.columnSizing).length) {
1303
+ const snapshot = {
1304
+ perspectiveId: null,
1305
+ settings: { columnSizing: normalized.columnSizing },
1306
+ updatedAt: Date.now()
1307
+ };
1308
+ writePerspectiveSnapshot(perspectiveTableId, snapshot);
1309
+ initialSnapshotRef.current = snapshot;
1197
1310
  } else {
1198
1311
  writePerspectiveSnapshot(perspectiveTableId, null);
1199
1312
  initialSnapshotRef.current = null;
1200
1313
  }
1201
1314
  }
1202
1315
  }, [onFiltersApply, onSearchChange, onSortingChange, perspectiveTableId, table, advancedFilter]);
1316
+ const persistColumnSizingSnapshot = React.useCallback(() => {
1317
+ if (!perspectiveTableId) return;
1318
+ const sizing = columnSizingRef.current;
1319
+ const existing = readPerspectiveSnapshot(perspectiveTableId) ?? initialSnapshotRef.current;
1320
+ const baseSettings = existing?.settings ? { ...existing.settings } : mergedInitialSettings ? { ...mergedInitialSettings } : {};
1321
+ if (sizing && Object.keys(sizing).length) baseSettings.columnSizing = sizing;
1322
+ else delete baseSettings.columnSizing;
1323
+ const snapshot = {
1324
+ perspectiveId: existing?.perspectiveId ?? activePerspectiveId ?? null,
1325
+ settings: baseSettings,
1326
+ updatedAt: Date.now()
1327
+ };
1328
+ writePerspectiveSnapshot(perspectiveTableId, snapshot);
1329
+ initialSnapshotRef.current = snapshot;
1330
+ }, [perspectiveTableId, activePerspectiveId, mergedInitialSettings]);
1331
+ const handleColumnResize = React.useCallback((colId, width) => {
1332
+ const next = { ...columnSizingRef.current, [colId]: width };
1333
+ columnSizingRef.current = next;
1334
+ setColumnSizing(next);
1335
+ }, []);
1336
+ const commitColumnSizing = React.useCallback(() => {
1337
+ persistColumnSizingSnapshot();
1338
+ }, [persistColumnSizingSnapshot]);
1339
+ const resetColumnSize = React.useCallback((colId) => {
1340
+ const next = { ...columnSizingRef.current };
1341
+ delete next[colId];
1342
+ columnSizingRef.current = next;
1343
+ setColumnSizing(next);
1344
+ persistColumnSizingSnapshot();
1345
+ }, [persistColumnSizingSnapshot]);
1203
1346
  React.useLayoutEffect(() => {
1204
1347
  if (!perspectiveTableId) return;
1205
1348
  if (snapshotHydratedTableRef.current === perspectiveTableId) return;
@@ -1470,6 +1613,7 @@ function DataTable({
1470
1613
  }, [table]);
1471
1614
  const dndSensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 5 } }));
1472
1615
  const enableHeaderDnd = Boolean(columnChooser);
1616
+ const enableColumnResize = perspectiveEnabled;
1473
1617
  const stableDndContextId = React.useMemo(
1474
1618
  () => sanitizeDndContextId(
1475
1619
  extensionTableId ?? perspectiveTableId ?? resolvedReplacementHandle ?? (typeof title === "string" && title.trim().length > 0 ? title : "data-table")
@@ -2177,7 +2321,33 @@ function DataTable({
2177
2321
  ]
2178
2322
  }
2179
2323
  );
2180
- return enableHeaderDnd ? /* @__PURE__ */ jsx(SortableHeaderCell, { id: header.id, className: responsiveClass(priority, columnMeta?.hidden) + stickyClass, children: headerCellContent }, header.id) : /* @__PURE__ */ jsx(TableHead, { className: responsiveClass(priority, columnMeta?.hidden) + stickyClass, children: headerCellContent }, header.id);
2324
+ const columnId = header.column.id;
2325
+ const sizedWidth = enableColumnResize && columnId ? columnSizing[columnId] : void 0;
2326
+ const resizeHandle = enableColumnResize && !header.isPlaceholder && columnId ? /* @__PURE__ */ jsx(
2327
+ ColumnResizeHandle,
2328
+ {
2329
+ columnId,
2330
+ onResize: handleColumnResize,
2331
+ onCommit: commitColumnSizing,
2332
+ onReset: resetColumnSize,
2333
+ ariaLabel: t("ui.dataTable.resizeColumn", "Resize column")
2334
+ }
2335
+ ) : null;
2336
+ return enableHeaderDnd ? /* @__PURE__ */ jsxs(SortableHeaderCell, { id: header.id, width: sizedWidth, className: cn("group", responsiveClass(priority, columnMeta?.hidden) + stickyClass), children: [
2337
+ headerCellContent,
2338
+ resizeHandle
2339
+ ] }, header.id) : /* @__PURE__ */ jsxs(
2340
+ TableHead,
2341
+ {
2342
+ className: cn("group relative", responsiveClass(priority, columnMeta?.hidden) + stickyClass),
2343
+ style: typeof sizedWidth === "number" ? { width: sizedWidth, minWidth: sizedWidth, maxWidth: sizedWidth } : void 0,
2344
+ children: [
2345
+ headerCellContent,
2346
+ resizeHandle
2347
+ ]
2348
+ },
2349
+ header.id
2350
+ );
2181
2351
  }),
2182
2352
  rowActions || injectedRowActions.length > 0 ? /* @__PURE__ */ jsx(
2183
2353
  TableHead,
@@ -2259,8 +2429,18 @@ function DataTable({
2259
2429
  } else {
2260
2430
  tooltipText = cellValue != null ? String(cellValue) : void 0;
2261
2431
  }
2262
- const wrappedContent = shouldTruncate ? /* @__PURE__ */ jsx(TruncatedCell, { maxWidth, tooltipContent: tooltipText, children: content }) : content;
2263
- return /* @__PURE__ */ jsx(TableCell, { className: responsiveClass(priority, columnMeta?.hidden) + (isStickyCell ? ` md:sticky md:left-0 md:z-10 md:bg-background ${STICKY_LEFT_SHADOW_CLASS}` : ""), children: wrappedContent }, cell.id);
2432
+ const sizedWidth = enableColumnResize && columnId ? columnSizing[columnId] : void 0;
2433
+ const effectiveMaxWidth = typeof sizedWidth === "number" ? `${sizedWidth}px` : maxWidth;
2434
+ const wrappedContent = shouldTruncate ? /* @__PURE__ */ jsx(TruncatedCell, { maxWidth: effectiveMaxWidth, tooltipContent: tooltipText, children: content }) : content;
2435
+ return /* @__PURE__ */ jsx(
2436
+ TableCell,
2437
+ {
2438
+ className: responsiveClass(priority, columnMeta?.hidden) + (isStickyCell ? ` md:sticky md:left-0 md:z-10 md:bg-background ${STICKY_LEFT_SHADOW_CLASS}` : ""),
2439
+ style: typeof sizedWidth === "number" ? { width: sizedWidth, minWidth: sizedWidth, maxWidth: sizedWidth } : void 0,
2440
+ children: wrappedContent
2441
+ },
2442
+ cell.id
2443
+ );
2264
2444
  }),
2265
2445
  rowActions || injectedRowActions.length > 0 ? /* @__PURE__ */ jsx(
2266
2446
  TableCell,
@@ -2353,6 +2533,7 @@ function DataTable({
2353
2533
  export {
2354
2534
  DataTable,
2355
2535
  readPerspectiveSnapshot,
2536
+ sanitizePerspectiveSettings,
2356
2537
  withDataTableNamespaces,
2357
2538
  writePerspectiveSnapshot
2358
2539
  };