@snksergio/design-system 0.4.0 → 0.5.0

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.
@@ -312,7 +312,7 @@ const Avatar = React.forwardRef(
312
312
  }
313
313
  );
314
314
  Avatar.displayName = "Avatar";
315
- const DEFAULT_COLUMN_WIDTH = 160;
315
+ const DEFAULT_COLUMN_WIDTH$1 = 160;
316
316
  function useColumnWidths(columns) {
317
317
  return React.useMemo(() => {
318
318
  const widths = {};
@@ -320,7 +320,7 @@ function useColumnWidths(columns) {
320
320
  let totalPinLeft = 0;
321
321
  let totalPinRight = 0;
322
322
  for (const col of columns) {
323
- widths[col.field] = col.width ?? col.defaultWidth ?? DEFAULT_COLUMN_WIDTH;
323
+ widths[col.field] = col.width ?? col.defaultWidth ?? DEFAULT_COLUMN_WIDTH$1;
324
324
  }
325
325
  let leftSum = 0;
326
326
  for (const col of columns) {
@@ -5348,7 +5348,8 @@ function useDataTableColumns({
5348
5348
  initialWidthOverrides,
5349
5349
  initialPinnedOverrides,
5350
5350
  initialHiddenColumns,
5351
- initialColumnOrder
5351
+ initialColumnOrder,
5352
+ autoWidths
5352
5353
  }) {
5353
5354
  const [widthOverrides, setWidthOverrides] = React.useState(
5354
5355
  () => initialWidthOverrides ?? {}
@@ -5377,13 +5378,13 @@ function useDataTableColumns({
5377
5378
  const typeDef = col.type ? columnTypeRegistry.get(col.type) : void 0;
5378
5379
  ordered.push({
5379
5380
  ...col,
5380
- width: widthOverrides[field] ?? col.width ?? typeDef?.defaultWidth,
5381
+ width: widthOverrides[field] ?? autoWidths?.[field] ?? col.width ?? typeDef?.defaultWidth,
5381
5382
  sortable: col.sortable ?? typeDef?.defaultSortable,
5382
5383
  pinned: field in pinnedOverrides ? pinnedOverrides[field] : col.pinned
5383
5384
  });
5384
5385
  }
5385
5386
  return ordered;
5386
- }, [columns, columnOrder, hiddenColumns, widthOverrides, pinnedOverrides]);
5387
+ }, [columns, columnOrder, hiddenColumns, widthOverrides, pinnedOverrides, autoWidths]);
5387
5388
  const widthsInput = React.useMemo(
5388
5389
  () => effectiveColumns.map((c) => ({
5389
5390
  field: String(c.field),
@@ -5451,6 +5452,177 @@ function useDataTableColumns({
5451
5452
  applyColumnState
5452
5453
  };
5453
5454
  }
5455
+ let measureCanvas = null;
5456
+ let measureCtx = null;
5457
+ const DEFAULT_FONT = "13px Geist, system-ui, -apple-system, BlinkMacSystemFont, sans-serif";
5458
+ function ensureCtx(font = DEFAULT_FONT) {
5459
+ if (typeof document === "undefined") return null;
5460
+ if (!measureCanvas) {
5461
+ measureCanvas = document.createElement("canvas");
5462
+ measureCtx = measureCanvas.getContext("2d");
5463
+ }
5464
+ if (measureCtx) measureCtx.font = font;
5465
+ return measureCtx;
5466
+ }
5467
+ function measureTextWidth(text, font) {
5468
+ if (!text) return 0;
5469
+ const ctx = ensureCtx(font);
5470
+ if (!ctx) return 0;
5471
+ return ctx.measureText(text).width;
5472
+ }
5473
+ function getFieldValue$1(row, field) {
5474
+ if (!field.includes(".")) {
5475
+ return row[field];
5476
+ }
5477
+ return field.split(".").reduce(
5478
+ (acc, key) => acc?.[key],
5479
+ row
5480
+ );
5481
+ }
5482
+ function applyValueGetter(row, column2) {
5483
+ if (column2.valueGetter) return column2.valueGetter(row);
5484
+ return getFieldValue$1(row, String(column2.field));
5485
+ }
5486
+ function applyFormatter(row, column2) {
5487
+ const value = applyValueGetter(row, column2);
5488
+ if (column2.valueFormatter) return column2.valueFormatter(value);
5489
+ return value == null ? "" : String(value);
5490
+ }
5491
+ const DEFAULT_COLUMN_WIDTH = 160;
5492
+ const CELL_PADDING_PX = 32;
5493
+ const DEFAULT_SAMPLE_SIZE = 20;
5494
+ function calculateColumnWidths(columns, rows, options) {
5495
+ const { containerWidth, autoFit, sampleSize = DEFAULT_SAMPLE_SIZE } = options;
5496
+ if (containerWidth <= 0) return {};
5497
+ const widths = {};
5498
+ let totalContentWidth = 0;
5499
+ for (const col of columns) {
5500
+ const field = String(col.field);
5501
+ if (col.width !== void 0) {
5502
+ totalContentWidth += col.width;
5503
+ continue;
5504
+ }
5505
+ const typeDef = col.type ? columnTypeRegistry.get(col.type) : void 0;
5506
+ let calculatedWidth = typeDef?.defaultWidth ?? DEFAULT_COLUMN_WIDTH;
5507
+ if (autoFit) {
5508
+ const headerText = String(col.headerName ?? field);
5509
+ let maxWidth = measureTextWidth(headerText) + CELL_PADDING_PX;
5510
+ const sample = rows.slice(0, sampleSize);
5511
+ for (const row of sample) {
5512
+ const text = applyFormatter(row, col);
5513
+ if (!text) continue;
5514
+ const w = measureTextWidth(text) + CELL_PADDING_PX;
5515
+ if (w > maxWidth) maxWidth = w;
5516
+ }
5517
+ if (maxWidth > calculatedWidth) calculatedWidth = maxWidth;
5518
+ }
5519
+ if (col.minWidth !== void 0 && calculatedWidth < col.minWidth) {
5520
+ calculatedWidth = col.minWidth;
5521
+ }
5522
+ if (col.maxWidth !== void 0 && calculatedWidth > col.maxWidth) {
5523
+ calculatedWidth = col.maxWidth;
5524
+ }
5525
+ widths[field] = Math.round(calculatedWidth);
5526
+ totalContentWidth += calculatedWidth;
5527
+ }
5528
+ if (totalContentWidth < containerWidth) {
5529
+ const remainingSpace = containerWidth - totalContentWidth;
5530
+ const flexibleColumns = columns.filter((col) => col.width === void 0);
5531
+ const targets = flexibleColumns.length > 0 ? flexibleColumns : autoFit ? columns : [];
5532
+ if (targets.length > 0) {
5533
+ const extraPerColumn = Math.floor(remainingSpace / targets.length);
5534
+ let distributed = 0;
5535
+ targets.forEach((col, index) => {
5536
+ const field = String(col.field);
5537
+ const isLast = index === targets.length - 1;
5538
+ const extra = isLast ? remainingSpace - distributed : extraPerColumn;
5539
+ const current = widths[field] ?? col.width ?? 0;
5540
+ let next = current + extra;
5541
+ if (col.maxWidth !== void 0 && next > col.maxWidth) {
5542
+ next = col.maxWidth;
5543
+ }
5544
+ widths[field] = Math.round(next);
5545
+ distributed += extra;
5546
+ });
5547
+ }
5548
+ }
5549
+ return widths;
5550
+ }
5551
+ function useColumnAutoWidth(containerRef, columns, rows, options) {
5552
+ const { enabled, sampleSize, reservedWidth = 0 } = options;
5553
+ const [autoWidths, setAutoWidths] = React.useState({});
5554
+ const rafIdRef = React.useRef(null);
5555
+ const columnsRef = React.useRef(columns);
5556
+ const rowsRef = React.useRef(rows);
5557
+ columnsRef.current = columns;
5558
+ rowsRef.current = rows;
5559
+ React.useEffect(() => {
5560
+ if (!enabled) {
5561
+ setAutoWidths((prev) => Object.keys(prev).length === 0 ? prev : {});
5562
+ return;
5563
+ }
5564
+ const el = containerRef.current;
5565
+ if (!el) return;
5566
+ if (typeof ResizeObserver === "undefined") return;
5567
+ const recalculate = (containerWidth) => {
5568
+ const effectiveWidth = Math.max(0, containerWidth - reservedWidth);
5569
+ const calcOptions = {
5570
+ containerWidth: effectiveWidth,
5571
+ autoFit: enabled,
5572
+ sampleSize
5573
+ };
5574
+ const next = calculateColumnWidths(
5575
+ columnsRef.current,
5576
+ rowsRef.current,
5577
+ calcOptions
5578
+ );
5579
+ setAutoWidths((prev) => {
5580
+ const prevKeys = Object.keys(prev);
5581
+ const nextKeys = Object.keys(next);
5582
+ if (prevKeys.length === nextKeys.length) {
5583
+ let same = true;
5584
+ for (const k of nextKeys) {
5585
+ if (prev[k] !== next[k]) {
5586
+ same = false;
5587
+ break;
5588
+ }
5589
+ }
5590
+ if (same) return prev;
5591
+ }
5592
+ return next;
5593
+ });
5594
+ };
5595
+ const initialRect = el.getBoundingClientRect();
5596
+ recalculate(initialRect.width);
5597
+ const observer = new ResizeObserver((entries) => {
5598
+ const entry = entries[0];
5599
+ if (!entry) return;
5600
+ if (rafIdRef.current !== null) cancelAnimationFrame(rafIdRef.current);
5601
+ rafIdRef.current = requestAnimationFrame(() => {
5602
+ rafIdRef.current = null;
5603
+ recalculate(entry.contentRect.width);
5604
+ });
5605
+ });
5606
+ observer.observe(el);
5607
+ return () => {
5608
+ observer.disconnect();
5609
+ if (rafIdRef.current !== null) {
5610
+ cancelAnimationFrame(rafIdRef.current);
5611
+ rafIdRef.current = null;
5612
+ }
5613
+ };
5614
+ }, [
5615
+ enabled,
5616
+ containerRef,
5617
+ sampleSize,
5618
+ reservedWidth,
5619
+ columns.length,
5620
+ rows.length,
5621
+ // Inclui campos das columns pra recalcular quando consumer muda a schema
5622
+ columns.map((c) => String(c.field)).join("|")
5623
+ ]);
5624
+ return { autoWidths };
5625
+ }
5454
5626
  function toArray(v) {
5455
5627
  if (v == null) return [];
5456
5628
  if (Array.isArray(v)) return v;
@@ -5505,10 +5677,11 @@ function useDataTablePagination({
5505
5677
  paginationModel: controlledModel,
5506
5678
  onPaginationModelChange,
5507
5679
  initialPageSize = DEFAULT_PAGE_SIZE,
5680
+ initialPage = 1,
5508
5681
  resetTriggers = []
5509
5682
  } = {}) {
5510
5683
  const [uncontrolled, setUncontrolled] = React.useState({
5511
- page: 1,
5684
+ page: initialPage,
5512
5685
  pageSize: initialPageSize
5513
5686
  });
5514
5687
  const isControlled = controlledModel !== void 0;
@@ -5663,9 +5836,10 @@ const DEFAULT_DEBOUNCE = 500;
5663
5836
  function useDataTableSearch({
5664
5837
  search: controlledSearch,
5665
5838
  onSearchChange,
5666
- debounceMs = DEFAULT_DEBOUNCE
5839
+ debounceMs = DEFAULT_DEBOUNCE,
5840
+ initialSearch
5667
5841
  } = {}) {
5668
- const [uncontrolled, setUncontrolled] = React.useState("");
5842
+ const [uncontrolled, setUncontrolled] = React.useState(initialSearch ?? "");
5669
5843
  const isControlled = controlledSearch !== void 0;
5670
5844
  const inputValue = isControlled ? controlledSearch : uncontrolled;
5671
5845
  const [debouncedValue, setDebouncedValue] = React.useState(inputValue);
@@ -5715,24 +5889,6 @@ function useDataTableFilters({
5715
5889
  }, [setFilterModel]);
5716
5890
  return { filterModel, setFilterModel, clearFilters };
5717
5891
  }
5718
- function getFieldValue$1(row, field) {
5719
- if (!field.includes(".")) {
5720
- return row[field];
5721
- }
5722
- return field.split(".").reduce(
5723
- (acc, key) => acc?.[key],
5724
- row
5725
- );
5726
- }
5727
- function applyValueGetter(row, column2) {
5728
- if (column2.valueGetter) return column2.valueGetter(row);
5729
- return getFieldValue$1(row, String(column2.field));
5730
- }
5731
- function applyFormatter(row, column2) {
5732
- const value = applyValueGetter(row, column2);
5733
- if (column2.valueFormatter) return column2.valueFormatter(value);
5734
- return value == null ? "" : String(value);
5735
- }
5736
5892
  function matchesFilter(value, item, col) {
5737
5893
  const { operator } = item;
5738
5894
  const target = item.value;
@@ -5978,7 +6134,7 @@ function downloadCsv(csv, filename) {
5978
6134
  URL.revokeObjectURL(url);
5979
6135
  }
5980
6136
  const STORAGE_PREFIX$1 = "igreen-datatable:";
5981
- const SCHEMA_VERSION = 3;
6137
+ const SCHEMA_VERSION = 4;
5982
6138
  function storageKey$1(persistId) {
5983
6139
  return `${STORAGE_PREFIX$1}${persistId}`;
5984
6140
  }
@@ -6143,12 +6299,22 @@ function useDataTableController(props, ref) {
6143
6299
  () => loadPersistedState(props.persistId),
6144
6300
  [props.persistId]
6145
6301
  );
6302
+ const scrollContainerRef = React.useRef(null);
6303
+ const autoFitEnabled = props.autoFit !== false;
6304
+ const selectionReservedWidth = props.selectionConfig?.enabled ? SELECTION_COLUMN_WIDTH : 0;
6305
+ const { autoWidths } = useColumnAutoWidth(
6306
+ scrollContainerRef,
6307
+ props.columns,
6308
+ props.rows ?? [],
6309
+ { enabled: autoFitEnabled, reservedWidth: selectionReservedWidth }
6310
+ );
6146
6311
  const cols = useDataTableColumns({
6147
6312
  columns: props.columns,
6148
6313
  initialWidthOverrides: persistedInitial?.columnWidths,
6149
6314
  initialPinnedOverrides: persistedInitial?.pinnedColumns,
6150
6315
  initialHiddenColumns: persistedInitial?.hiddenColumns,
6151
- initialColumnOrder: persistedInitial?.columnOrder
6316
+ initialColumnOrder: persistedInitial?.columnOrder,
6317
+ autoWidths
6152
6318
  });
6153
6319
  const sort = useDataTableSort({
6154
6320
  sortModel: props.sortModel,
@@ -6157,16 +6323,23 @@ function useDataTableController(props, ref) {
6157
6323
  });
6158
6324
  const filters = useDataTableFilters({
6159
6325
  filterModel: props.filterModel,
6160
- onFilterModelChange: props.onFilterModelChange
6326
+ onFilterModelChange: props.onFilterModelChange,
6327
+ // Hidrata filtros do workspace Default (v4+). Quando uma view custom estava
6328
+ // ativa no último mount, o effect de restore (linha ~410) aplica a view depois.
6329
+ initialFilterModel: persistedInitial?.filterModel
6161
6330
  });
6162
6331
  const search = useDataTableSearch({
6163
6332
  search: props.search,
6164
- onSearchChange: props.onSearchChange
6333
+ onSearchChange: props.onSearchChange,
6334
+ // Hidrata texto de busca do workspace Default (v4+).
6335
+ initialSearch: persistedInitial?.search
6165
6336
  });
6166
6337
  const pagination = useDataTablePagination({
6167
6338
  paginationModel: props.paginationModel,
6168
6339
  onPaginationModelChange: props.onPaginationModelChange,
6169
6340
  initialPageSize: persistedInitial?.pageSize ?? props.paginationConfig?.initialPageSize,
6341
+ // Hidrata página atual do workspace Default (v4+).
6342
+ initialPage: persistedInitial?.currentPage,
6170
6343
  resetTriggers: [filters.filterModel.items.length, search.debouncedValue]
6171
6344
  });
6172
6345
  const density = useDataTableDensity({
@@ -6290,10 +6463,17 @@ function useDataTableController(props, ref) {
6290
6463
  density: density.density,
6291
6464
  sortModel: sort.sortModels,
6292
6465
  pageSize: pagination.paginationModel.pageSize,
6466
+ // v4 — currentPage/filterModel/search persistem como parte do workspace Default.
6467
+ // O defaultSnapshotRef abaixo só atualiza quando currentViewId === null, ou seja
6468
+ // só "captura" esses valores quando a Default está ativa. Aplicar uma view custom
6469
+ // não polui o Default (o save sempre usa defaultSnapshotRef, não persistedSnapshot).
6470
+ currentPage: pagination.paginationModel.page,
6293
6471
  columnWidths: cols.columnWidths,
6294
6472
  pinnedColumns: cols.pinnedColumns,
6295
6473
  hiddenColumns: Array.from(cols.hiddenColumns),
6296
6474
  columnOrder: cols.columnOrder,
6475
+ filterModel: filters.filterModel,
6476
+ search: search.debouncedValue,
6297
6477
  // Fase v3 — features novas também persistem (workspace completo da Default)
6298
6478
  viewMode,
6299
6479
  groupBy,
@@ -6304,10 +6484,13 @@ function useDataTableController(props, ref) {
6304
6484
  density.density,
6305
6485
  sort.sortModels,
6306
6486
  pagination.paginationModel.pageSize,
6487
+ pagination.paginationModel.page,
6307
6488
  cols.columnWidths,
6308
6489
  cols.pinnedColumns,
6309
6490
  cols.hiddenColumns,
6310
6491
  cols.columnOrder,
6492
+ filters.filterModel,
6493
+ search.debouncedValue,
6311
6494
  viewMode,
6312
6495
  groupBy,
6313
6496
  expandedRowIds,
@@ -6320,9 +6503,13 @@ function useDataTableController(props, ref) {
6320
6503
  defaultSnapshotRef.current = rest;
6321
6504
  }
6322
6505
  }, [persistedSnapshot, savedViews.currentViewId]);
6506
+ const persistedSnapshotForSave = React.useMemo(() => {
6507
+ const base = savedViews.currentViewId === null ? persistedSnapshot : { ...defaultSnapshotRef.current, lastActiveViewId: savedViews.currentViewId };
6508
+ return base;
6509
+ }, [persistedSnapshot, savedViews.currentViewId]);
6323
6510
  useDataTableStatePersistence({
6324
6511
  persistId: props.persistId,
6325
- state: persistedSnapshot
6512
+ state: persistedSnapshotForSave
6326
6513
  });
6327
6514
  const applyViewState = React.useCallback(
6328
6515
  (id, state) => {
@@ -6434,6 +6621,12 @@ function useDataTableController(props, ref) {
6434
6621
  setViewMode(snapshot.viewMode ?? "table");
6435
6622
  setGroupBy(snapshot.groupBy);
6436
6623
  setExpandedRowIds(snapshot.expandedRowIds ?? []);
6624
+ filters.setFilterModel(snapshot.filterModel ?? { items: [], logicOperator: "AND" });
6625
+ search.setInputValue(snapshot.search ?? "");
6626
+ pagination.setPaginationModel({
6627
+ page: snapshot.currentPage ?? 1,
6628
+ pageSize: snapshot.pageSize ?? pagination.paginationModel.pageSize
6629
+ });
6437
6630
  } else {
6438
6631
  density.setDensity("standard");
6439
6632
  sort.setSortModels([]);
@@ -6446,10 +6639,11 @@ function useDataTableController(props, ref) {
6446
6639
  setViewMode("table");
6447
6640
  setGroupBy(void 0);
6448
6641
  setExpandedRowIds([]);
6642
+ filters.setFilterModel({ items: [], logicOperator: "AND" });
6643
+ search.setInputValue("");
6449
6644
  }
6450
- filters.setFilterModel({ items: [], logicOperator: "AND" });
6451
6645
  savedViews.setCurrentViewId(null);
6452
- }, [props.persistId, props.columns, density, sort, filters, cols, setViewMode, setGroupBy, setExpandedRowIds, savedViews]);
6646
+ }, [props.persistId, props.columns, density, sort, filters, search, pagination, cols, setViewMode, setGroupBy, setExpandedRowIds, savedViews]);
6453
6647
  React.useImperativeHandle(ref, () => ({
6454
6648
  refresh: () => {
6455
6649
  if (isServerMode) query.refresh();
@@ -6505,6 +6699,8 @@ function useDataTableController(props, ref) {
6505
6699
  isLoading,
6506
6700
  isDataEmpty,
6507
6701
  isNoResults,
6702
+ /** Ref ao scroll container (compartilhado entre Table.scrollRef + useColumnAutoWidth). */
6703
+ scrollContainerRef,
6508
6704
  rowsToRender: effectiveRows,
6509
6705
  /** Todas as rows após filter/search/sort (sem paginate) — usado por totalizers
6510
6706
  * pra agregar sobre tudo, nao só pagina atual. Em server mode = current page. */
@@ -7791,6 +7987,7 @@ function DataTableInternal(props, ref) {
7791
7987
  isLoading,
7792
7988
  isDataEmpty,
7793
7989
  isNoResults,
7990
+ scrollContainerRef,
7794
7991
  rowsToRender,
7795
7992
  rowsAllPagesProcessed,
7796
7993
  totalAfterFilter,
@@ -8185,7 +8382,6 @@ function DataTableInternal(props, ref) {
8185
8382
  rowsToRender,
8186
8383
  contextValue
8187
8384
  ]);
8188
- const scrollContainerRef = React.useRef(null);
8189
8385
  const defaultRowHeight = density.density === "compact" ? 40 : density.density === "comfortable" ? 64 : 56;
8190
8386
  const estimateRowHeight = props.estimateRowHeight ?? defaultRowHeight;
8191
8387
  const virtualize = props.virtualize === true;
@@ -10022,4 +10218,4 @@ exports.savedViewsMockService = savedViewsMockService;
10022
10218
  exports.useColumnWidths = useColumnWidths;
10023
10219
  exports.useDataTableContext = useDataTableContext;
10024
10220
  exports.useFloatingPanelResize = useFloatingPanelResize;
10025
- //# sourceMappingURL=panel-CjfnpA-4.cjs.map
10221
+ //# sourceMappingURL=panel-CSuFQHMl.cjs.map