@redsift/table 12.5.8-muiv8-alpha.2 → 12.5.8-muiv8-alpha.4

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.
@@ -1565,7 +1565,7 @@ const isValueValid = (value, field, columns, operator) => {
1565
1565
 
1566
1566
  // example:
1567
1567
  // unicodeDomain[contains]=a&unicodeDomain[contains]=dsa&logicOperator=and&tab=ignored
1568
- const getFilterModelFromString = (searchString, columns) => {
1568
+ const getFilterModelFromString = (searchString, columns, previousModel) => {
1569
1569
  if (!searchString) {
1570
1570
  return 'invalid';
1571
1571
  }
@@ -1595,6 +1595,21 @@ const getFilterModelFromString = (searchString, columns) => {
1595
1595
  }
1596
1596
  }
1597
1597
  let id = 5000;
1598
+
1599
+ // Positional id-reuse map keyed by `${field}|${operator}` (decoded operator, matching
1600
+ // the item we push below). FIFO per key so duplicate field+operator items map by order.
1601
+ // Reusing the id of a previously parsed/emitted item keeps `item.id` stable across the
1602
+ // URL write→parse roundtrip (the URL never carries the id), so MUI's GridFilterForm
1603
+ // React key does not change and the filter panel input keeps focus. (DS-81)
1604
+ const previousIdsByKey = new Map();
1605
+ if (previousModel) {
1606
+ for (const prev of previousModel.items) {
1607
+ if (prev.id === undefined) continue;
1608
+ const key = `${prev.field}|${prev.operator}`;
1609
+ const bucket = previousIdsByKey.get(key);
1610
+ if (bucket) bucket.push(prev.id);else previousIdsByKey.set(key, [prev.id]);
1611
+ }
1612
+ }
1598
1613
  const fields = columns.map(column => column.field);
1599
1614
  let isInvalid = false;
1600
1615
  const items = [];
@@ -1630,10 +1645,13 @@ const getFilterModelFromString = (searchString, columns) => {
1630
1645
  isInvalid = true;
1631
1646
  return;
1632
1647
  }
1648
+ const finalOperator = columnType === 'number' && Object.keys(numberOperatorDecoder).includes(operator) ? numberOperatorDecoder[operator] : operator;
1649
+ const reuseBucket = previousIdsByKey.get(`${field}|${finalOperator}`);
1650
+ const stableId = reuseBucket && reuseBucket.length > 0 ? reuseBucket.shift() : id;
1633
1651
  items.push({
1634
1652
  field,
1635
- operator: columnType === 'number' && Object.keys(numberOperatorDecoder).includes(operator) ? numberOperatorDecoder[operator] : operator,
1636
- id,
1653
+ operator: finalOperator,
1654
+ id: stableId,
1637
1655
  value: listOperators.includes(operator) && decodedValue === '' ? [] : decodedValue,
1638
1656
  type
1639
1657
  });
@@ -1727,7 +1745,7 @@ const getSearchParamsFromFilterModel = filterModel => {
1727
1745
  // - if we have something in the URL, use that info
1728
1746
  // - if we don't have that, use the localStorage and update the URL
1729
1747
  // - if we don't have that, return an empty FilterModel
1730
- const getFilterModel = (search, columns, localStorageFilters, setLocalStorageFilters, initialState, isNewVersion) => {
1748
+ const getFilterModel = (search, columns, localStorageFilters, setLocalStorageFilters, initialState, isNewVersion, previousModel) => {
1731
1749
  const defaultValue = initialState && initialState.filter && initialState.filter.filterModel ? initialState.filter.filterModel : {
1732
1750
  items: [],
1733
1751
  logicOperator: GridLogicOperator.And
@@ -1745,7 +1763,7 @@ const getFilterModel = (search, columns, localStorageFilters, setLocalStorageFil
1745
1763
  persistDefaultFilters();
1746
1764
  return defaultValue;
1747
1765
  }
1748
- const filterModelFromSearch = getFilterModelFromString(search, columns);
1766
+ const filterModelFromSearch = getFilterModelFromString(search, columns, previousModel);
1749
1767
  if (filterModelFromSearch !== 'invalid') {
1750
1768
  const searchFromFilterModel = getSearchParamsFromFilterModel(filterModelFromSearch);
1751
1769
  const searchString = urlSearchParamsToString(searchFromFilterModel);
@@ -2610,7 +2628,7 @@ const getFinalSearch = _ref3 => {
2610
2628
  return new URLSearchParams([...searchParams, ...filterModelSearch, ...sortModelSearch, ...paginationModelSearch, ...tabSearch, ...pinnedColumnsModelSearch, ...columnVisibilityModelSearch, ...densitySearch, ...columnOrderSearch, ...rowGroupingSearch, ...aggregationSearch, ...pivotSearch, ...pivotActiveSearch]);
2611
2629
  };
2612
2630
  /** Return the state of the table given the URL and the local storage state */
2613
- const getModelsParsedOrUpdateLocalStorage = (search, localStorageVersion, columns, initialState, localStorage) => {
2631
+ const getModelsParsedOrUpdateLocalStorage = (search, localStorageVersion, columns, initialState, localStorage, previousFilterModel) => {
2614
2632
  var _initialState$columns6, _initialState$columns7;
2615
2633
  // Decompress any compressed params in the search string before processing
2616
2634
  const decompressedSearch = decompressSearchParams(search);
@@ -2657,7 +2675,7 @@ const getModelsParsedOrUpdateLocalStorage = (search, localStorageVersion, column
2657
2675
  localStoragePivotActive,
2658
2676
  setLocalStoragePivotActive
2659
2677
  } = localStorage;
2660
- const filterModel = getFilterModel(decodedSearch, columns, localStorageFilters, setLocalStorageFilters, initialState, isNewVersion);
2678
+ const filterModel = getFilterModel(decodedSearch, columns, localStorageFilters, setLocalStorageFilters, initialState, isNewVersion, previousFilterModel);
2661
2679
  const sortModel = getSortModel(decodedSearch, columns, localStorageSorting, setLocalStorageSorting, initialState, isNewVersion);
2662
2680
  const paginationModel = getPaginationModel(decodedSearch, localStoragePagination, setLocalStoragePagination, initialState, isNewVersion);
2663
2681
  const columnVisibilityModel = getColumnsVisibility(decodedSearch, columns, localStorageColumnsVisibility, setLocalStorageColumnsVisibility, initialState, isNewVersion);
@@ -3085,6 +3103,11 @@ const useStatefulTable = props => {
3085
3103
  [field]: newWidth
3086
3104
  }));
3087
3105
  }, [dimensionModel, setDimensionModel]);
3106
+
3107
+ // Source of ids for content-matching in the parser, so item.id is stable across the
3108
+ // URL write→parse roundtrip (DS-81). Holds MUI's just-emitted model (random placeholder
3109
+ // id) after onFilterModelChange, then the stabilised parsed model each render.
3110
+ const filterIdSourceRef = useRef(undefined);
3088
3111
  const {
3089
3112
  filterModel: filterParsed,
3090
3113
  sortModel: sortModelParsed,
@@ -3121,7 +3144,7 @@ const useStatefulTable = props => {
3121
3144
  setLocalStoragePivot,
3122
3145
  localStoragePivotActive: localStoragePivotActive,
3123
3146
  setLocalStoragePivotActive: setLocalStoragePivotActive
3124
- });
3147
+ }, filterIdSourceRef.current);
3125
3148
 
3126
3149
  // Sync URL in an effect rather than during render to comply with React rules
3127
3150
  useEffect(() => {
@@ -3136,6 +3159,10 @@ const useStatefulTable = props => {
3136
3159
  if (!isDeepEqual(filterParsedRef.current, filterParsed)) {
3137
3160
  filterParsedRef.current = filterParsed;
3138
3161
  }
3162
+ // Keep the id source in sync with the stabilised parsed model so re-parses triggered
3163
+ // by unrelated state (pagination/sort/visibility) reuse the same ids (DS-81). The parse
3164
+ // call above reads the ref before this write (read-before-write within the render).
3165
+ filterIdSourceRef.current = filterParsed;
3139
3166
  const sortModelParsedRef = useRef(sortModelParsed);
3140
3167
  if (!isDeepEqual(sortModelParsedRef.current, sortModelParsed)) {
3141
3168
  sortModelParsedRef.current = sortModelParsed;
@@ -3292,6 +3319,10 @@ const useStatefulTable = props => {
3292
3319
  });
3293
3320
  if (isDeepEqual(filterModel, lastEmittedFilterRef.current)) return;
3294
3321
  lastEmittedFilterRef.current = filterModel;
3322
+ // Capture MUI's just-emitted model (carrying the placeholder's random id) so the
3323
+ // next render's URL re-parse reuses that id and the GridFilterForm key — and thus
3324
+ // the value input's focus — survives the write→parse roundtrip (DS-81).
3325
+ filterIdSourceRef.current = filterModel;
3295
3326
  updateUrl(buildModel({
3296
3327
  filterModel
3297
3328
  }), search, localStorageVersion, historyReplace, columns);