@trackunit/react-components 3.8.3-alpha-e1e6a0f33ce.0 → 3.8.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-components",
3
- "version": "3.8.3-alpha-e1e6a0f33ce.0",
3
+ "version": "3.8.4",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "migrations": "./migrations.json",
@@ -14,16 +14,16 @@
14
14
  "@floating-ui/react": "^0.26.25",
15
15
  "string-ts": "^2.0.0",
16
16
  "tailwind-merge": "^2.0.0",
17
- "@trackunit/ui-design-tokens": "1.18.6-alpha-e1e6a0f33ce.0",
18
- "@trackunit/css-class-variance-utilities": "2.3.6-alpha-e1e6a0f33ce.0",
19
- "@trackunit/shared-utils": "1.19.6-alpha-e1e6a0f33ce.0",
20
- "@trackunit/ui-icons": "1.17.6-alpha-e1e6a0f33ce.0",
17
+ "@trackunit/ui-design-tokens": "1.18.6",
18
+ "@trackunit/css-class-variance-utilities": "2.3.6",
19
+ "@trackunit/shared-utils": "1.19.6",
20
+ "@trackunit/ui-icons": "1.17.6",
21
21
  "es-toolkit": "^1.52.0",
22
22
  "@tanstack/react-virtual": "^3.14.13",
23
23
  "dequal": "^2.0.3",
24
24
  "fflate": "^0.8.3",
25
25
  "zod": "^3.25.76",
26
- "@trackunit/i18n-library-translation": "2.8.6-alpha-e1e6a0f33ce.0"
26
+ "@trackunit/i18n-library-translation": "2.8.6"
27
27
  },
28
28
  "peerDependencies": {
29
29
  "react": "^19.0.0",
@@ -38,29 +38,36 @@ interface TagBaseProps extends CommonProps, Styleable, Refable<HTMLDivElement> {
38
38
  }
39
39
  interface NonDismissibleTagProps extends TagBaseProps {
40
40
  /**
41
- * A handler for a click event triggered on X icon.
42
- * If not passed then x icon won't be visible.
43
- * Available only for neutral, primary and white tags.
41
+ * Called when the remove button is clicked.
42
+ * If not passed then the remove button won't be visible.
43
+ * Available only for medium tags.
44
44
  *
45
45
  */
46
46
  onClickClose?: undefined;
47
47
  /**
48
- * Label for the remove button.
48
+ * Tooltip of the remove button, such as "Remove".
49
49
  */
50
50
  removeTagLabel?: string;
51
+ removeButtonInTabOrder?: undefined;
51
52
  }
52
53
  interface DismissibleTagProps extends TagBaseProps {
53
54
  /**
54
- * A handler for a click event triggered on X icon.
55
- * If not passed then x icon won't be visible.
56
- * Available only for neutral, primary and white tags.
55
+ * Called when the remove button is clicked.
56
+ * If not passed then the remove button won't be visible.
57
+ * Available only for medium tags.
57
58
  *
58
59
  */
59
60
  onClickClose: MouseEventHandler<HTMLDivElement>;
60
61
  /**
61
- * Label for the remove button.
62
+ * Tooltip of the remove button, such as "Remove".
63
+ * Screen readers hear it followed by the tag's text, such as "Remove Chocolate".
62
64
  */
63
65
  removeTagLabel: string;
66
+ /**
67
+ * Set to false when the surrounding widget already offers keyboard removal, such as a multi-select,
68
+ * so the remove button adds no Tab stop. Defaults to true.
69
+ */
70
+ removeButtonInTabOrder?: boolean;
64
71
  }
65
72
  export type TagProps = DismissibleTagProps | NonDismissibleTagProps;
66
73
  /**
@@ -111,5 +118,5 @@ export type TagProps = DismissibleTagProps | NonDismissibleTagProps;
111
118
  * @param {TagProps} props - The props for the Tag component.
112
119
  * @returns {ReactElement} The rendered Tag component.
113
120
  */
114
- export declare const Tag: ({ className, "data-testid": dataTestId, children, size, onClickClose, color, disabled, ref, icon, onMouseEnter, style, removeTagLabel, }: TagProps) => ReactElement;
121
+ export declare const Tag: ({ className, "data-testid": dataTestId, children, size, onClickClose, color, disabled, ref, icon, onMouseEnter, style, removeTagLabel, removeButtonInTabOrder, }: TagProps) => ReactElement;
115
122
  export {};
@@ -154,6 +154,12 @@ export declare const cvaTagIconContainer: import("cva").CVAComponent<Omit<{
154
154
  variants: unknown;
155
155
  defaultVariants: Omit<{}, never>;
156
156
  }, unknown>;
157
+ export declare const cvaTagDismissButton: import("cva").CVAComponent<Omit<{
158
+ base: string[];
159
+ }, "defaultVariants"> & {
160
+ variants: unknown;
161
+ defaultVariants: Omit<{}, never>;
162
+ }, unknown>;
157
163
  export declare const cvaTagIcon: import("cva").CVAComponent<Omit<{
158
164
  base: string[];
159
165
  }, "defaultVariants"> & {
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Default maximum length of the resulting URL fragment after adding one
3
+ * persisted entry. Oversized new entries are skipped; oversized replacements
4
+ * remove the stale entry so localStorage remains authoritative.
5
+ *
6
+ * The fragment is never sent to the server, so it is not subject to the
7
+ * AWS WAF managed-rule `SizeRestrictions_QUERYSTRING` cap that applies to
8
+ * search params (5000 bytes). 64 KB stays well below typical browser URL
9
+ * limits (Chromium ≳ 100 KB, Firefox ≳ 64 KB on the address bar) while
10
+ * still leaving generous headroom for any realistic table state.
11
+ */
12
+ export declare const MAX_HASH_LENGTH: number;
13
+ type UseHashParamSyncOptions = {
14
+ readonly key: string;
15
+ readonly enabled?: boolean;
16
+ readonly onExternalChange?: () => void;
17
+ readonly replace?: boolean;
18
+ };
19
+ type UseHashParamSyncReturn = {
20
+ /**
21
+ * Mirrors the field name on `useSearchParamSync` so this hook is a
22
+ * drop-in replacement when only the URL location changes (search ↔ hash).
23
+ * Despite the name, this value is read from `location.hash`.
24
+ */
25
+ readonly searchValue: string | undefined;
26
+ /**
27
+ * Mirrors the function name on `useSearchParamSync`. Writes to
28
+ * `location.hash`, preserving any foreign fragment content untouched.
29
+ */
30
+ readonly updateSearchParam: (encodedValue: string | undefined, options?: {
31
+ readonly replace?: boolean;
32
+ }) => void;
33
+ };
34
+ /**
35
+ * Syncs an encoded string value with a single named entry inside the URL
36
+ * fragment (`location.hash`) via Tanstack Router.
37
+ *
38
+ * Behaviour mirrors `useSearchParamSync`, with two differences:
39
+ * - Reads from and writes to `location.hash` instead of `location.search`.
40
+ * - Oversized replacements remove stale values using
41
+ * {@link MAX_HASH_LENGTH}, rather than the AWS-WAF-driven search-param cap.
42
+ *
43
+ * The fragment is treated as a sequence of `&`-separated tokens via the
44
+ * pure utilities in `./hashFragment`. Foreign tokens (bare anchors, other
45
+ * named entries) are preserved byte-for-byte on every write.
46
+ *
47
+ * @param options - Configuration for the hash entry sync.
48
+ * @param options.key - The named entry inside the fragment.
49
+ * @param options.enabled - Set to `false` to disable all URL interaction (default `true`).
50
+ * @param options.onExternalChange - Called when the entry changes externally
51
+ * (e.g. browser back/forward, pasted link, foreign router push).
52
+ * @param options.replace - `true` to always use `replaceState` instead of pushState.
53
+ */
54
+ export declare const useHashParamSync: ({ key, enabled, onExternalChange, replace: replaceOption, }: UseHashParamSyncOptions) => UseHashParamSyncReturn;
55
+ export {};
@@ -0,0 +1,62 @@
1
+ type UseMigrateLegacySearchParamOptions<TState> = {
2
+ readonly key: string;
3
+ readonly enabled: boolean;
4
+ /**
5
+ * Pure decoder for the raw search-param string. Should not throw — return
6
+ * `undefined` when the input can't be decoded.
7
+ */
8
+ readonly decode: (rawSearchValue: string) => unknown;
9
+ /**
10
+ * Optional transform applied to the decoded payload before validation
11
+ * (mirrors `usePersistedState`'s `fromUrlValue`).
12
+ */
13
+ readonly fromUrlValue?: (decoded: unknown) => unknown;
14
+ /**
15
+ * Schema validator. Return the parsed state on success, `undefined`
16
+ * otherwise.
17
+ */
18
+ readonly validate: (raw: unknown) => TState | undefined;
19
+ /**
20
+ * When `true`, the hash already carries a valid value for this key. The
21
+ * hook still strips the legacy search param (so the URL is clean) but
22
+ * does NOT fire `onMigrated` — the hash is canonical and we must not
23
+ * stomp on it.
24
+ */
25
+ readonly hashAlreadyHasValue: boolean;
26
+ /**
27
+ * Called at most once when a valid legacy search-param value is found
28
+ * and the hash is currently empty. Pipe through your normal
29
+ * `persistState` so the value ends up in both the hash and localStorage.
30
+ */
31
+ readonly onMigrated: (state: TState) => void;
32
+ };
33
+ /**
34
+ * One-shot migration: if the URL still carries a legacy `?<key>=...`
35
+ * search param from before this hook moved table-style state to the
36
+ * fragment, decode it, hand it to the caller via `onMigrated`, then strip
37
+ * the search param via a `replace` navigation so back/forward history
38
+ * isn't polluted.
39
+ *
40
+ * Invariants:
41
+ * - The strip navigation fires at most once per mount (guarded by
42
+ * `hasStrippedRef`) so we don't loop on the navigation we ourselves
43
+ * triggered.
44
+ * - `onMigrated` is also fired at most once per mount (guarded by
45
+ * `hasMigratedRef`), but its gating is independent of the strip:
46
+ * if `hashAlreadyHasValue` flips `true → false` after the strip
47
+ * (e.g. an external actor clears the canonical hash mid-mount), the
48
+ * next render still gets a chance to migrate the legacy value into
49
+ * the hash. Conflating the two flags would have permanently lost
50
+ * the legacy state in that race.
51
+ * - Never throws — a decode/validate failure silently drops the value
52
+ * and still strips the search param. The legacy key is owned by this
53
+ * codebase's persistence convention (camelCase + `Tp` suffix), so
54
+ * stripping unparseable values cannot destroy third-party data.
55
+ * - When `hashAlreadyHasValue` is `true` the hash wins: the search
56
+ * param is stripped but `onMigrated` is not fired.
57
+ * - The `location.hash` is preserved verbatim across the strip
58
+ * navigation so any concurrent hash content (including a freshly
59
+ * written value) survives the cleanup.
60
+ */
61
+ export declare const useMigrateLegacySearchParam: <TState>({ key, enabled, decode, fromUrlValue, validate, hashAlreadyHasValue, onMigrated, }: UseMigrateLegacySearchParamOptions<TState>) => void;
62
+ export {};
@@ -1,19 +1,24 @@
1
- import { type UrlParamLocation } from "./useUrlParamSync";
2
1
  /**
3
2
  * Selects which part of the URL backs the persisted state.
4
3
  *
5
4
  * - `"search"` (default): `?<key>=<encoded>`. Subject to the AWS WAF
6
- * managed-rule cap (~5000 bytes) — large states are dropped from the URL.
5
+ * managed-rule cap (~5000 bytes) — large states may be silently
6
+ * truncated before they hit the browser bar.
7
7
  * - `"hash"`: `#<key>=<encoded>`. Stays on the client, so the cap is
8
- * driven only by browser URL limits.
8
+ * driven only by browser URL limits. When this mode is selected,
9
+ * `usePersistedState` also performs a one-shot migration from any
10
+ * legacy `?<key>=...` value so previously shared links don't lose state.
9
11
  */
10
- export type UrlPersistenceLocation = UrlParamLocation;
12
+ export type UrlPersistenceLocation = "search" | "hash";
11
13
  type UsePersistedStateOptions<TState> = {
12
14
  readonly key: string;
13
15
  readonly validate: (raw: unknown) => TState | undefined;
16
+ readonly serialize?: (state: TState) => string;
14
17
  readonly toUrlValue?: (state: TState) => string | object | Array<object>;
15
18
  readonly fromUrlValue?: (decoded: unknown) => unknown;
19
+ readonly enabled?: boolean;
16
20
  readonly localStorageEnabled?: boolean;
21
+ readonly onExternalChange?: () => void;
17
22
  readonly replace?: boolean;
18
23
  readonly clientSideUserId?: string;
19
24
  /**
@@ -25,7 +30,8 @@ type UsePersistedStateOptions<TState> = {
25
30
  * (e.g. per-user layout details that shouldn't be shareable) so the
26
31
  * omitted fields still survive reloads via localStorage.
27
32
  *
28
- * Default is `false`: URL state fully replaces localStorage state on read.
33
+ * Default is `false`, preserving the original behavior where URL state
34
+ * fully replaces localStorage state on read.
29
35
  */
30
36
  readonly mergeWithStorageOnRead?: boolean;
31
37
  /**
@@ -50,16 +56,15 @@ type UsePersistedStateOptions<TState> = {
50
56
  };
51
57
  type UsePersistedStateReturn<TState> = {
52
58
  readonly initialState: TState | undefined;
53
- /** Writes `state` to localStorage and the URL; each slot skips a value equal to its last write. */
54
59
  readonly persistState: (state: TState) => void;
60
+ readonly clearState: () => void;
55
61
  /**
56
- * Writes `state` to localStorage only; `undefined` removes the entry.
57
- * Use together with `persistToUrl` when the two writes should not share a
58
- * schedule — e.g. storage immediately, URL debounced.
62
+ * Writes the hydrated state to localStorage when it came from the URL and
63
+ * differs from what this identity has stored. Returns `false` when the
64
+ * snapshot is still for a previous read (identity not resolved yet), so
65
+ * the caller can retry. A no-op success returns `true`.
59
66
  */
60
- readonly persistToStorage: (state: TState | undefined) => void;
61
- /** Writes `state` to the URL only; `undefined` removes the entry. */
62
- readonly persistToUrl: (state: TState | undefined) => void;
67
+ readonly commitHydratedState: () => boolean;
63
68
  };
64
69
  /**
65
70
  * Generic persistence hook that loads state from a URL slot (search param
@@ -67,43 +72,47 @@ type UsePersistedStateReturn<TState> = {
67
72
  * and writes changes to both.
68
73
  *
69
74
  * On mount the hook resolves the initial state as follows:
70
- * - If the URL slot holds a value, decode it and pass through `validate`.
75
+ * - If the canonical URL slot (search OR hash, per `urlLocation`) holds a
76
+ * value, decode it and pass through `validate`.
77
+ * - In `urlLocation: "hash"` mode, if the canonical hash slot has no valid
78
+ * value but a **legacy** `?<key>=...` search param is present, decode and
79
+ * validate that as a one-shot fallback. The value is copied into the hash
80
+ * and the search param is stripped via a `replace` navigation.
81
+ * - A value that comes only from localStorage is not written to the URL.
82
+ * Copying it on load left a layout in history, bookmarks, and login
83
+ * redirects, which then replayed an older layout over the saved one
84
+ * (GLU-2396). The URL is written when the user changes the state.
71
85
  * - If localStorage is enabled, read and validate its value.
72
86
  * - When `mergeWithStorageOnRead` is `false` (default): URL state wins
73
87
  * entirely when present, otherwise fall back to localStorage.
74
88
  * - When `mergeWithStorageOnRead` is `true`: shallow-merge URL fields over
75
89
  * localStorage so fields stripped by `toUrlValue` survive via storage.
76
90
  *
77
- * A value that comes only from localStorage is never written to the URL on
78
- * load. Copying it left a layout in history, bookmarks, and login redirects,
79
- * which then replayed an older layout over the saved one (GLU-2396). The URL
80
- * is written when the consumer persists a change.
91
+ * `persistState` writes the state to localStorage (via `serialize`, default
92
+ * `storageSerializer.serialize`) and syncs to the canonical URL slot (via
93
+ * `toUrlValue` + encoding). Duplicate writes where the state has not
94
+ * changed (deep equality) are skipped.
81
95
  *
82
- * `persistState` writes to localStorage (`storageSerializer`) and to the URL
83
- * (via `toUrlValue` + encoding).
84
- * Each slot skips a value equal to its last write. The URL side compares what
85
- * would land in the URL (the `toUrlValue` projection and the identity stamp),
86
- * starting from the projection of the hydrated state and the stamp the URL
87
- * carries: echoing `initialState`, or changing only a field the projection
88
- * strips, writes no URL entry, while an equal state persisted after the
89
- * identity resolves rewrites the URL with its stamp. The storage side starts
90
- * from what localStorage held, so a consumer that persists `initialState`
91
- * after opening a link saves that link's state for the account that opened
92
- * it, and a reload of the account's own link writes nothing.
93
- * `persistToStorage` and `persistToUrl` are the same two halves exposed
94
- * separately for consumers that schedule them differently; passing `undefined`
95
- * removes the entry rather than writing an empty value.
96
+ * `commitHydratedState` writes the hydrated value to localStorage only,
97
+ * when it came from the URL and differs from what this identity has stored.
98
+ * It does not update the URL. It returns `false` while the snapshot is
99
+ * still for a previous read, so the caller can retry after identity
100
+ * resolution. The caller decides which identity is allowed to commit.
96
101
  *
97
- * The snapshot is re-read when the storage key or the storage/merge options
98
- * change, in the same render, so a write in that commit already sees the
99
- * state for the new key.
102
+ * `clearState` removes the entry from localStorage and from the URL slot,
103
+ * rather than writing an empty value. Use it when the state a consumer
104
+ * derives has become empty, so no leftover entry is stored for a value the
105
+ * user has reset. It is idempotent — clearing an absent entry is a no-op.
100
106
  *
101
107
  * @param options - Configuration for the persisted state.
102
108
  * @param options.key - Unique identifier used for both the URL slot and the localStorage key.
103
109
  * @param options.validate - Called with the decoded/parsed value; must return `TState` or `undefined`.
110
+ * @param options.serialize - Custom localStorage serialiser (default `storageSerializer.serialize`).
104
111
  * @param options.toUrlValue - Transform applied before URL encoding (default: encode state as-is).
105
112
  * @param options.fromUrlValue - Transform applied after URL decoding, before validation (default: identity).
113
+ * @param options.enabled - When `false` the URL is neither read nor written (default `true`).
106
114
  * @param options.localStorageEnabled - When `false` localStorage is neither read nor written (default `true`).
115
+ * @param options.onExternalChange - Fired when the URL value changes externally (e.g. browser back).
107
116
  * @param options.replace - Forwarded to the underlying URL sync hook.
108
117
  * @param options.clientSideUserId - The user ID to use for the localStorage key.
109
118
  * @param options.mergeWithStorageOnRead - Merge URL state on top of localStorage
@@ -113,5 +122,5 @@ type UsePersistedStateReturn<TState> = {
113
122
  * @param options.stampUrlWithIdentity - Stamp URL writes with the current identity so the login
114
123
  * redirect can drop entries left by another account on this browser. Default `false`.
115
124
  */
116
- export declare const usePersistedState: <TState extends object>({ key, validate, toUrlValue, fromUrlValue, localStorageEnabled, replace, clientSideUserId, mergeWithStorageOnRead, urlLocation, stampUrlWithIdentity, }: UsePersistedStateOptions<TState>) => UsePersistedStateReturn<TState>;
125
+ export declare const usePersistedState: <TState extends object>({ key, validate, serialize, toUrlValue, fromUrlValue, enabled, localStorageEnabled, onExternalChange, replace, clientSideUserId, mergeWithStorageOnRead, urlLocation, stampUrlWithIdentity, }: UsePersistedStateOptions<TState>) => UsePersistedStateReturn<TState>;
117
126
  export {};
@@ -1,12 +1,30 @@
1
- import { type UseUrlParamSyncOptions, type UseUrlParamSyncReturn } from "./useUrlParamSync";
2
- export { MAX_URL_LENGTH } from "./useUrlParamSync";
3
- type UseSearchParamSyncOptions = Omit<UseUrlParamSyncOptions, "location">;
1
+ /**
2
+ * Default maximum full URL length after adding one persisted search param
3
+ * before the URL write is skipped (localStorage still persists the full state).
4
+ *
5
+ * Sized to stay safely below the AWS WAF managed-rule default
6
+ * `SizeRestrictions_QUERYSTRING` cap of 5000 bytes for the entire url host + path + query + hash string
7
+ */
8
+ export declare const MAX_URL_LENGTH = 5000;
9
+ type UseSearchParamSyncOptions = {
10
+ readonly key: string;
11
+ readonly enabled?: boolean;
12
+ readonly onExternalChange?: () => void;
13
+ readonly replace?: boolean;
14
+ };
15
+ type UseSearchParamSyncReturn = {
16
+ readonly searchValue: string | undefined;
17
+ readonly updateSearchParam: (encodedValue: string | undefined, options?: {
18
+ readonly replace?: boolean;
19
+ }) => void;
20
+ };
4
21
  /**
5
22
  * Syncs an encoded string value with a URL search parameter via Tanstack Router.
6
23
  *
7
- * Thin wrapper over {@link useUrlParamSync} with `location: "search"`; see it
8
- * for the write queue, the size guard (`MAX_URL_LENGTH`), and external-change
9
- * detection.
24
+ * Provides a write function that updates the URL through the application
25
+ * router (preserving other search params), guards against exceeding
26
+ * {@link MAX_URL_LENGTH}, and detects external URL changes (browser
27
+ * back/forward, shared links) via an optional callback.
10
28
  *
11
29
  * @param options - Configuration for the search param sync.
12
30
  * @param options.key - The URL search parameter name.
@@ -14,4 +32,5 @@ type UseSearchParamSyncOptions = Omit<UseUrlParamSyncOptions, "location">;
14
32
  * @param options.onExternalChange - Called when the param changes externally.
15
33
  * @param options.replace - `true` to always use `replaceState`.
16
34
  */
17
- export declare const useSearchParamSync: (options: UseSearchParamSyncOptions) => UseUrlParamSyncReturn;
35
+ export declare const useSearchParamSync: ({ key, enabled, onExternalChange, replace: replaceOption, }: UseSearchParamSyncOptions) => UseSearchParamSyncReturn;
36
+ export {};
package/src/index.d.ts CHANGED
@@ -126,6 +126,7 @@ export * from "./hooks/localStorage/useLocalStorageReducer";
126
126
  export * from "./hooks/localStorage/useSessionStorage";
127
127
  export * from "./hooks/localStorage/useSessionStorageReducer";
128
128
  export * from "./hooks/noPagination";
129
+ export * from "./hooks/persistence/useHashParamSync";
129
130
  export * from "./hooks/persistence/usePersistedState";
130
131
  export * from "./hooks/persistence/useSearchParamSync";
131
132
  export * from "./hooks/persistence/useStorageKey";
@@ -1,71 +0,0 @@
1
- /**
2
- * Maximum full URL length after adding one persisted search param before the
3
- * URL write is skipped (localStorage still persists the full state).
4
- *
5
- * Sized to stay safely below the AWS WAF managed-rule default
6
- * `SizeRestrictions_QUERYSTRING` cap of 5000 bytes for the entire
7
- * host + path + query + hash string.
8
- */
9
- export declare const MAX_URL_LENGTH = 5000;
10
- /**
11
- * Maximum length of the URL fragment after adding one persisted entry.
12
- * Oversized new entries are skipped; oversized replacements remove the stale
13
- * entry so localStorage remains authoritative.
14
- *
15
- * The fragment is never sent to the server, so the WAF cap above does not
16
- * apply. 64 KB stays well below typical browser URL limits (Chromium ≳ 100 KB,
17
- * Firefox ≳ 64 KB on the address bar) while leaving generous headroom for any
18
- * realistic table state.
19
- */
20
- export declare const MAX_HASH_LENGTH: number;
21
- /**
22
- * Which part of the URL a persisted value lives in.
23
- *
24
- * - `"search"`: `?<key>=<encoded>`. Subject to the AWS WAF cap
25
- * ({@link MAX_URL_LENGTH}); large states are dropped from the URL.
26
- * - `"hash"`: `#<key>=<encoded>`. Client-only, capped by
27
- * {@link MAX_HASH_LENGTH}.
28
- */
29
- export type UrlParamLocation = "search" | "hash";
30
- export type UseUrlParamSyncOptions = {
31
- readonly key: string;
32
- readonly location: UrlParamLocation;
33
- readonly enabled?: boolean;
34
- readonly onExternalChange?: () => void;
35
- readonly replace?: boolean;
36
- };
37
- export type UseUrlParamSyncReturn = {
38
- /** The raw encoded value currently in the URL slot, or `undefined` when absent or disabled. */
39
- readonly searchValue: string | undefined;
40
- /** Writes `encodedValue` into the URL slot; `undefined` removes the entry. */
41
- readonly updateSearchParam: (encodedValue: string | undefined) => void;
42
- };
43
- /**
44
- * Syncs an encoded string value with a single named entry in the URL — either
45
- * a search param or an entry inside the hash fragment — via Tanstack Router.
46
- *
47
- * Writes go through the application router so other search params and
48
- * foreign fragment content are preserved. Each slot has a size guard
49
- * ({@link MAX_URL_LENGTH} for search, {@link MAX_HASH_LENGTH} for hash); an
50
- * oversized value is dropped from the URL and localStorage stays
51
- * authoritative. External changes to the entry (browser back/forward, a
52
- * pasted link, a foreign router push) fire `onExternalChange`.
53
- *
54
- * Writes are dispatched via a pending-write queue rather than calling
55
- * `navigate` directly. Tanstack Router's `router.state.status === "pending"`
56
- * is common during the first paint of a route (loaders, suspense), and an
57
- * implementation that simply returned in that case lost the write outright.
58
- * `updateSearchParam` stores the latest attempted write and schedules an rAF
59
- * flush; the flush only navigates when the router is idle, and an effect
60
- * retries whenever the router settles. A queued write is dropped on unmount so
61
- * it cannot drain onto the next route.
62
- *
63
- * @param options - Configuration for the URL entry sync.
64
- * @param options.key - The search param name or fragment entry name.
65
- * @param options.location - `"search"` or `"hash"`.
66
- * @param options.enabled - Set to `false` to disable all URL interaction (default `true`).
67
- * @param options.onExternalChange - Called when the entry changes externally.
68
- * @param options.replace - `true` to always use `replaceState`; `false` to always push.
69
- * Default: replace when the entry is being added, push when it already has a value.
70
- */
71
- export declare const useUrlParamSync: ({ key, location: urlLocation, enabled, onExternalChange, replace: replaceOption, }: UseUrlParamSyncOptions) => UseUrlParamSyncReturn;