@trackunit/react-components 1.21.18 → 1.22.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.
package/index.cjs.js CHANGED
@@ -11048,7 +11048,7 @@ const useSearchParamSync = ({ key, enabled = true, onExternalChange, replace: re
11048
11048
  const urlBase = location.href.length - (location.searchStr.length || 0) + (location.hash.length || 0);
11049
11049
  return urlBase + otherParamsLength + 1 + paramKey.length + 1 + paramValue.length;
11050
11050
  }, [location]);
11051
- const updateSearchParam = react.useCallback((encodedValue) => {
11051
+ const updateSearchParam = react.useCallback((encodedValue, options) => {
11052
11052
  if (!enabled) {
11053
11053
  return;
11054
11054
  }
@@ -11060,7 +11060,7 @@ const useSearchParamSync = ({ key, enabled = true, onExternalChange, replace: re
11060
11060
  return;
11061
11061
  }
11062
11062
  requestAnimationFrame(() => {
11063
- const shouldReplace = replaceOption ?? !Boolean(currentSearchValue);
11063
+ const shouldReplace = options?.replace ?? replaceOption ?? !Boolean(currentSearchValue);
11064
11064
  void navigate({
11065
11065
  to: ".",
11066
11066
  search: (prev) => {
@@ -11141,14 +11141,14 @@ const usePersistedState = ({ key, validate, serialize, toUrlValue, fromUrlValue,
11141
11141
  react.useEffect(() => {
11142
11142
  serializeRef.current = serialize;
11143
11143
  }, [serialize]);
11144
- const [initialState] = react.useState(() => {
11144
+ const [{ initialState, loadedFromStorage }] = react.useState(() => {
11145
11145
  if (enabled && searchValue) {
11146
11146
  try {
11147
11147
  const decoded = decode(searchValue);
11148
11148
  const transformed = fromUrlValue ? fromUrlValue(decoded) : decoded;
11149
11149
  const validated = validate(transformed);
11150
11150
  if (validated !== undefined) {
11151
- return validated;
11151
+ return { initialState: validated, loadedFromStorage: false };
11152
11152
  }
11153
11153
  }
11154
11154
  catch {
@@ -11159,15 +11159,28 @@ const usePersistedState = ({ key, validate, serialize, toUrlValue, fromUrlValue,
11159
11159
  const raw = localStorage.getItem(storageKey);
11160
11160
  if (raw) {
11161
11161
  const parsed = storageSerializer.deserialize(raw);
11162
- return validate(parsed);
11162
+ const validated = validate(parsed);
11163
+ return { initialState: validated, loadedFromStorage: validated !== undefined };
11163
11164
  }
11164
11165
  }
11165
11166
  catch {
11166
11167
  // no valid stored state
11167
11168
  }
11168
- return undefined;
11169
+ return { initialState: undefined, loadedFromStorage: false };
11169
11170
  });
11170
11171
  const lastPersistedRef = react.useRef(initialState);
11172
+ const hasRestoredUrlRef = react.useRef(false);
11173
+ react.useEffect(() => {
11174
+ if (hasRestoredUrlRef.current) {
11175
+ return;
11176
+ }
11177
+ if (!enabled || !loadedFromStorage || initialState === undefined || searchValue !== undefined) {
11178
+ return;
11179
+ }
11180
+ hasRestoredUrlRef.current = true;
11181
+ const urlValue = toUrlValueRef.current ? toUrlValueRef.current(initialState) : initialState;
11182
+ updateSearchParam(encode(urlValue), { replace: true });
11183
+ }, [enabled, loadedFromStorage, initialState, searchValue, updateSearchParam, encode]);
11171
11184
  const persistState = react.useCallback((state) => {
11172
11185
  if (dequal.dequal(lastPersistedRef.current, state)) {
11173
11186
  return;
package/index.esm.js CHANGED
@@ -11046,7 +11046,7 @@ const useSearchParamSync = ({ key, enabled = true, onExternalChange, replace: re
11046
11046
  const urlBase = location.href.length - (location.searchStr.length || 0) + (location.hash.length || 0);
11047
11047
  return urlBase + otherParamsLength + 1 + paramKey.length + 1 + paramValue.length;
11048
11048
  }, [location]);
11049
- const updateSearchParam = useCallback((encodedValue) => {
11049
+ const updateSearchParam = useCallback((encodedValue, options) => {
11050
11050
  if (!enabled) {
11051
11051
  return;
11052
11052
  }
@@ -11058,7 +11058,7 @@ const useSearchParamSync = ({ key, enabled = true, onExternalChange, replace: re
11058
11058
  return;
11059
11059
  }
11060
11060
  requestAnimationFrame(() => {
11061
- const shouldReplace = replaceOption ?? !Boolean(currentSearchValue);
11061
+ const shouldReplace = options?.replace ?? replaceOption ?? !Boolean(currentSearchValue);
11062
11062
  void navigate({
11063
11063
  to: ".",
11064
11064
  search: (prev) => {
@@ -11139,14 +11139,14 @@ const usePersistedState = ({ key, validate, serialize, toUrlValue, fromUrlValue,
11139
11139
  useEffect(() => {
11140
11140
  serializeRef.current = serialize;
11141
11141
  }, [serialize]);
11142
- const [initialState] = useState(() => {
11142
+ const [{ initialState, loadedFromStorage }] = useState(() => {
11143
11143
  if (enabled && searchValue) {
11144
11144
  try {
11145
11145
  const decoded = decode(searchValue);
11146
11146
  const transformed = fromUrlValue ? fromUrlValue(decoded) : decoded;
11147
11147
  const validated = validate(transformed);
11148
11148
  if (validated !== undefined) {
11149
- return validated;
11149
+ return { initialState: validated, loadedFromStorage: false };
11150
11150
  }
11151
11151
  }
11152
11152
  catch {
@@ -11157,15 +11157,28 @@ const usePersistedState = ({ key, validate, serialize, toUrlValue, fromUrlValue,
11157
11157
  const raw = localStorage.getItem(storageKey);
11158
11158
  if (raw) {
11159
11159
  const parsed = storageSerializer.deserialize(raw);
11160
- return validate(parsed);
11160
+ const validated = validate(parsed);
11161
+ return { initialState: validated, loadedFromStorage: validated !== undefined };
11161
11162
  }
11162
11163
  }
11163
11164
  catch {
11164
11165
  // no valid stored state
11165
11166
  }
11166
- return undefined;
11167
+ return { initialState: undefined, loadedFromStorage: false };
11167
11168
  });
11168
11169
  const lastPersistedRef = useRef(initialState);
11170
+ const hasRestoredUrlRef = useRef(false);
11171
+ useEffect(() => {
11172
+ if (hasRestoredUrlRef.current) {
11173
+ return;
11174
+ }
11175
+ if (!enabled || !loadedFromStorage || initialState === undefined || searchValue !== undefined) {
11176
+ return;
11177
+ }
11178
+ hasRestoredUrlRef.current = true;
11179
+ const urlValue = toUrlValueRef.current ? toUrlValueRef.current(initialState) : initialState;
11180
+ updateSearchParam(encode(urlValue), { replace: true });
11181
+ }, [enabled, loadedFromStorage, initialState, searchValue, updateSearchParam, encode]);
11169
11182
  const persistState = useCallback((state) => {
11170
11183
  if (dequal(lastPersistedRef.current, state)) {
11171
11184
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-components",
3
- "version": "1.21.18",
3
+ "version": "1.22.0",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -7,7 +7,9 @@ type UseSearchParamSyncOptions = {
7
7
  };
8
8
  type UseSearchParamSyncReturn = {
9
9
  readonly searchValue: string | undefined;
10
- readonly updateSearchParam: (encodedValue: string | undefined) => void;
10
+ readonly updateSearchParam: (encodedValue: string | undefined, options?: {
11
+ readonly replace?: boolean;
12
+ }) => void;
11
13
  };
12
14
  /**
13
15
  * Syncs an encoded string value with a URL search parameter via Tanstack Router.