@redsift/table-pro 12.5.8-muiv8-alpha.3 → 12.5.8

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.
@@ -1510,7 +1510,7 @@ const isValueValid = (value, field, columns, operator) => {
1510
1510
 
1511
1511
  // example:
1512
1512
  // unicodeDomain[contains]=a&unicodeDomain[contains]=dsa&logicOperator=and&tab=ignored
1513
- const getFilterModelFromString = (searchString, columns) => {
1513
+ const getFilterModelFromString = (searchString, columns, previousModel) => {
1514
1514
  if (!searchString) {
1515
1515
  return 'invalid';
1516
1516
  }
@@ -1540,6 +1540,21 @@ const getFilterModelFromString = (searchString, columns) => {
1540
1540
  }
1541
1541
  }
1542
1542
  let id = 5000;
1543
+
1544
+ // Positional id-reuse map keyed by `${field}|${operator}` (decoded operator, matching
1545
+ // the item we push below). FIFO per key so duplicate field+operator items map by order.
1546
+ // Reusing the id of a previously parsed/emitted item keeps `item.id` stable across the
1547
+ // URL write→parse roundtrip (the URL never carries the id), so MUI's GridFilterForm
1548
+ // React key does not change and the filter panel input keeps focus. (DS-81)
1549
+ const previousIdsByKey = new Map();
1550
+ if (previousModel) {
1551
+ for (const prev of previousModel.items) {
1552
+ if (prev.id === undefined) continue;
1553
+ const key = `${prev.field}|${prev.operator}`;
1554
+ const bucket = previousIdsByKey.get(key);
1555
+ if (bucket) bucket.push(prev.id);else previousIdsByKey.set(key, [prev.id]);
1556
+ }
1557
+ }
1543
1558
  const fields = columns.map(column => column.field);
1544
1559
  let isInvalid = false;
1545
1560
  const items = [];
@@ -1575,10 +1590,13 @@ const getFilterModelFromString = (searchString, columns) => {
1575
1590
  isInvalid = true;
1576
1591
  return;
1577
1592
  }
1593
+ const finalOperator = columnType === 'number' && Object.keys(numberOperatorDecoder).includes(operator) ? numberOperatorDecoder[operator] : operator;
1594
+ const reuseBucket = previousIdsByKey.get(`${field}|${finalOperator}`);
1595
+ const stableId = reuseBucket && reuseBucket.length > 0 ? reuseBucket.shift() : id;
1578
1596
  items.push({
1579
1597
  field,
1580
- operator: columnType === 'number' && Object.keys(numberOperatorDecoder).includes(operator) ? numberOperatorDecoder[operator] : operator,
1581
- id,
1598
+ operator: finalOperator,
1599
+ id: stableId,
1582
1600
  value: listOperators.includes(operator) && decodedValue === '' ? [] : decodedValue,
1583
1601
  type
1584
1602
  });
@@ -1672,7 +1690,7 @@ const getSearchParamsFromFilterModel = filterModel => {
1672
1690
  // - if we have something in the URL, use that info
1673
1691
  // - if we don't have that, use the localStorage and update the URL
1674
1692
  // - if we don't have that, return an empty FilterModel
1675
- const getFilterModel = (search, columns, localStorageFilters, setLocalStorageFilters, initialState, isNewVersion) => {
1693
+ const getFilterModel = (search, columns, localStorageFilters, setLocalStorageFilters, initialState, isNewVersion, previousModel) => {
1676
1694
  const defaultValue = initialState && initialState.filter && initialState.filter.filterModel ? initialState.filter.filterModel : {
1677
1695
  items: [],
1678
1696
  logicOperator: GridLogicOperator.And
@@ -1690,7 +1708,7 @@ const getFilterModel = (search, columns, localStorageFilters, setLocalStorageFil
1690
1708
  persistDefaultFilters();
1691
1709
  return defaultValue;
1692
1710
  }
1693
- const filterModelFromSearch = getFilterModelFromString(search, columns);
1711
+ const filterModelFromSearch = getFilterModelFromString(search, columns, previousModel);
1694
1712
  if (filterModelFromSearch !== 'invalid') {
1695
1713
  const searchFromFilterModel = getSearchParamsFromFilterModel(filterModelFromSearch);
1696
1714
  const searchString = urlSearchParamsToString(searchFromFilterModel);
@@ -2279,7 +2297,7 @@ const getFinalSearch = _ref => {
2279
2297
  return new URLSearchParams([...searchParams, ...filterModelSearch, ...sortModelSearch, ...paginationModelSearch, ...tabSearch, ...pinnedColumnsModelSearch, ...columnVisibilityModelSearch, ...densitySearch, ...columnOrderSearch]);
2280
2298
  };
2281
2299
  /** Return the state of the table given the URL and the local storage state */
2282
- const getModelsParsedOrUpdateLocalStorage = (search, localStorageVersion, columns, initialState, localStorage) => {
2300
+ const getModelsParsedOrUpdateLocalStorage = (search, localStorageVersion, columns, initialState, localStorage, previousFilterModel) => {
2283
2301
  var _initialState$columns6, _initialState$columns7;
2284
2302
  // Decompress any compressed params in the search string before processing
2285
2303
  const decompressedSearch = decompressSearchParams(search);
@@ -2318,7 +2336,7 @@ const getModelsParsedOrUpdateLocalStorage = (search, localStorageVersion, column
2318
2336
  localStorageColumnOrder,
2319
2337
  setLocalStorageColumnOrder
2320
2338
  } = localStorage;
2321
- const filterModel = getFilterModel(decodedSearch, columns, localStorageFilters, setLocalStorageFilters, initialState, isNewVersion);
2339
+ const filterModel = getFilterModel(decodedSearch, columns, localStorageFilters, setLocalStorageFilters, initialState, isNewVersion, previousFilterModel);
2322
2340
  const sortModel = getSortModel(decodedSearch, columns, localStorageSorting, setLocalStorageSorting, initialState, isNewVersion);
2323
2341
  const paginationModel = getPaginationModel(decodedSearch, localStoragePagination, setLocalStoragePagination, initialState, isNewVersion);
2324
2342
  const columnVisibilityModel = getColumnsVisibility(decodedSearch, columns, localStorageColumnsVisibility, setLocalStorageColumnsVisibility, initialState, isNewVersion);
@@ -2665,6 +2683,11 @@ const useStatefulTable = props => {
2665
2683
  [field]: newWidth
2666
2684
  }));
2667
2685
  }, [dimensionModel, setDimensionModel]);
2686
+
2687
+ // Source of ids for content-matching in the parser, so item.id is stable across the
2688
+ // URL write→parse roundtrip (DS-81). Holds MUI's just-emitted model (random placeholder
2689
+ // id) after onFilterModelChange, then the stabilised parsed model each render.
2690
+ const filterIdSourceRef = useRef(undefined);
2668
2691
  const {
2669
2692
  filterModel: filterParsed,
2670
2693
  sortModel: sortModelParsed,
@@ -2689,7 +2712,7 @@ const useStatefulTable = props => {
2689
2712
  setLocalStorageDensity: setDensityModel,
2690
2713
  localStorageColumnOrder,
2691
2714
  setLocalStorageColumnOrder
2692
- });
2715
+ }, filterIdSourceRef.current);
2693
2716
 
2694
2717
  // Sync URL in an effect rather than during render to comply with React rules
2695
2718
  useEffect(() => {
@@ -2704,6 +2727,10 @@ const useStatefulTable = props => {
2704
2727
  if (!isDeepEqual(filterParsedRef.current, filterParsed)) {
2705
2728
  filterParsedRef.current = filterParsed;
2706
2729
  }
2730
+ // Keep the id source in sync with the stabilised parsed model so re-parses triggered
2731
+ // by unrelated state (pagination/sort/visibility) reuse the same ids (DS-81). The parse
2732
+ // call above reads the ref before this write (read-before-write within the render).
2733
+ filterIdSourceRef.current = filterParsed;
2707
2734
  const sortModelParsedRef = useRef(sortModelParsed);
2708
2735
  if (!isDeepEqual(sortModelParsedRef.current, sortModelParsed)) {
2709
2736
  sortModelParsedRef.current = sortModelParsed;
@@ -2827,6 +2854,10 @@ const useStatefulTable = props => {
2827
2854
  });
2828
2855
  if (isDeepEqual(filterModel, lastEmittedFilterRef.current)) return;
2829
2856
  lastEmittedFilterRef.current = filterModel;
2857
+ // Capture MUI's just-emitted model (carrying the placeholder's random id) so the
2858
+ // next render's URL re-parse reuses that id and the GridFilterForm key — and thus
2859
+ // the value input's focus — survives the write→parse roundtrip (DS-81).
2860
+ filterIdSourceRef.current = filterModel;
2830
2861
  updateUrl(buildModel({
2831
2862
  filterModel
2832
2863
  }), search, localStorageVersion, historyReplace, columns);