@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/index.cjs.js +813 -369
- package/index.esm.js +813 -371
- package/package.json +6 -6
- package/src/components/Tag/Tag.d.ts +16 -9
- package/src/components/Tag/Tag.variants.d.ts +6 -0
- package/src/hooks/persistence/useHashParamSync.d.ts +55 -0
- package/src/hooks/persistence/useMigrateLegacySearchParam.d.ts +62 -0
- package/src/hooks/persistence/usePersistedState.d.ts +44 -35
- package/src/hooks/persistence/useSearchParamSync.d.ts +26 -7
- package/src/index.d.ts +1 -0
- package/src/hooks/persistence/useUrlParamSync.d.ts +0 -71
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/react-components",
|
|
3
|
-
"version": "3.8.
|
|
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
|
|
18
|
-
"@trackunit/css-class-variance-utilities": "2.3.6
|
|
19
|
-
"@trackunit/shared-utils": "1.19.6
|
|
20
|
-
"@trackunit/ui-icons": "1.17.6
|
|
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
|
|
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
|
-
*
|
|
42
|
-
* If not passed then
|
|
43
|
-
* Available only for
|
|
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
|
-
*
|
|
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
|
-
*
|
|
55
|
-
* If not passed then
|
|
56
|
-
* Available only for
|
|
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
|
-
*
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
57
|
-
*
|
|
58
|
-
*
|
|
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
|
|
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
|
|
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
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
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
|
-
* `
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
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
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
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: (
|
|
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;
|