tempest-react-sdk 0.67.0 → 0.68.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.
Files changed (142) hide show
  1. package/README.md +97 -93
  2. package/dist/auth/create-tempest-auth.cjs +1 -1
  3. package/dist/auth/create-tempest-auth.cjs.map +1 -1
  4. package/dist/auth/create-tempest-auth.js +41 -40
  5. package/dist/auth/create-tempest-auth.js.map +1 -1
  6. package/dist/components/BottomSheet/BottomSheet.cjs +1 -1
  7. package/dist/components/BottomSheet/BottomSheet.cjs.map +1 -1
  8. package/dist/components/BottomSheet/BottomSheet.js +34 -30
  9. package/dist/components/BottomSheet/BottomSheet.js.map +1 -1
  10. package/dist/components/Card/Card.module.cjs.map +1 -1
  11. package/dist/components/Card/Card.module.js.map +1 -1
  12. package/dist/components/Combobox/Combobox.cjs +1 -1
  13. package/dist/components/Combobox/Combobox.cjs.map +1 -1
  14. package/dist/components/Combobox/Combobox.js +78 -77
  15. package/dist/components/Combobox/Combobox.js.map +1 -1
  16. package/dist/components/Combobox/Combobox.module.cjs.map +1 -1
  17. package/dist/components/Combobox/Combobox.module.js.map +1 -1
  18. package/dist/components/Command/Command.cjs +1 -1
  19. package/dist/components/Command/Command.js +4 -4
  20. package/dist/components/DataTable/DataTable.cjs +1 -1
  21. package/dist/components/DataTable/DataTable.cjs.map +1 -1
  22. package/dist/components/DataTable/DataTable.js +138 -127
  23. package/dist/components/DataTable/DataTable.js.map +1 -1
  24. package/dist/components/DataTable/use-dev-warnings.cjs +1 -1
  25. package/dist/components/DataTable/use-dev-warnings.cjs.map +1 -1
  26. package/dist/components/DataTable/use-dev-warnings.js +5 -3
  27. package/dist/components/DataTable/use-dev-warnings.js.map +1 -1
  28. package/dist/components/Drawer/Drawer.cjs +1 -1
  29. package/dist/components/Drawer/Drawer.cjs.map +1 -1
  30. package/dist/components/Drawer/Drawer.js +43 -40
  31. package/dist/components/Drawer/Drawer.js.map +1 -1
  32. package/dist/components/FilterPanel/FilterPanel.cjs +2 -0
  33. package/dist/components/FilterPanel/FilterPanel.cjs.map +1 -0
  34. package/dist/components/FilterPanel/FilterPanel.js +75 -0
  35. package/dist/components/FilterPanel/FilterPanel.js.map +1 -0
  36. package/dist/components/FilterPanel/FilterPanel.module.cjs +3 -0
  37. package/dist/components/FilterPanel/FilterPanel.module.cjs.map +1 -0
  38. package/dist/components/FilterPanel/FilterPanel.module.js +12 -0
  39. package/dist/components/FilterPanel/FilterPanel.module.js.map +1 -0
  40. package/dist/components/Lightbox/Lightbox.cjs +1 -1
  41. package/dist/components/Lightbox/Lightbox.js +5 -5
  42. package/dist/components/Modal/Modal.cjs +1 -1
  43. package/dist/components/Modal/Modal.cjs.map +1 -1
  44. package/dist/components/Modal/Modal.js +44 -41
  45. package/dist/components/Modal/Modal.js.map +1 -1
  46. package/dist/components/Pagination/Pagination.cjs +1 -1
  47. package/dist/components/Pagination/Pagination.cjs.map +1 -1
  48. package/dist/components/Pagination/Pagination.js +31 -30
  49. package/dist/components/Pagination/Pagination.js.map +1 -1
  50. package/dist/components/Portal/tab-bridge.cjs +1 -1
  51. package/dist/components/Portal/tab-bridge.cjs.map +1 -1
  52. package/dist/components/Portal/tab-bridge.js +21 -23
  53. package/dist/components/Portal/tab-bridge.js.map +1 -1
  54. package/dist/components/Select/CaretIcon.cjs +2 -0
  55. package/dist/components/Select/CaretIcon.cjs.map +1 -0
  56. package/dist/components/Select/CaretIcon.js +21 -0
  57. package/dist/components/Select/CaretIcon.js.map +1 -0
  58. package/dist/components/Select/Select.cjs +1 -1
  59. package/dist/components/Select/Select.cjs.map +1 -1
  60. package/dist/components/Select/Select.js +33 -47
  61. package/dist/components/Select/Select.js.map +1 -1
  62. package/dist/components/Select/Select.module.cjs +1 -1
  63. package/dist/components/Select/Select.module.cjs.map +1 -1
  64. package/dist/components/Select/Select.module.js +4 -3
  65. package/dist/components/Select/Select.module.js.map +1 -1
  66. package/dist/components/Select/options.cjs +2 -0
  67. package/dist/components/Select/options.cjs.map +1 -0
  68. package/dist/components/Select/options.js +25 -0
  69. package/dist/components/Select/options.js.map +1 -0
  70. package/dist/components/Sidebar/Sidebar.cjs +1 -1
  71. package/dist/components/Sidebar/Sidebar.cjs.map +1 -1
  72. package/dist/components/Sidebar/Sidebar.js +49 -43
  73. package/dist/components/Sidebar/Sidebar.js.map +1 -1
  74. package/dist/components/Sidebar/active-nav-key.cjs +2 -0
  75. package/dist/components/Sidebar/active-nav-key.cjs.map +1 -0
  76. package/dist/components/Sidebar/active-nav-key.js +22 -0
  77. package/dist/components/Sidebar/active-nav-key.js.map +1 -0
  78. package/dist/hooks/tab-sequence.cjs +2 -0
  79. package/dist/hooks/tab-sequence.cjs.map +1 -0
  80. package/dist/hooks/tab-sequence.js +53 -0
  81. package/dist/hooks/tab-sequence.js.map +1 -0
  82. package/dist/hooks/use-deep-memo.cjs +1 -1
  83. package/dist/hooks/use-deep-memo.cjs.map +1 -1
  84. package/dist/hooks/use-deep-memo.js +1 -1
  85. package/dist/hooks/use-deep-memo.js.map +1 -1
  86. package/dist/hooks/use-draft-filters.cjs +2 -0
  87. package/dist/hooks/use-draft-filters.cjs.map +1 -0
  88. package/dist/hooks/use-draft-filters.js +49 -0
  89. package/dist/hooks/use-draft-filters.js.map +1 -0
  90. package/dist/hooks/use-focus-trap.cjs +1 -1
  91. package/dist/hooks/use-focus-trap.cjs.map +1 -1
  92. package/dist/hooks/use-focus-trap.js +31 -34
  93. package/dist/hooks/use-focus-trap.js.map +1 -1
  94. package/dist/http/api-client.cjs +1 -1
  95. package/dist/http/api-client.cjs.map +1 -1
  96. package/dist/http/api-client.js +92 -80
  97. package/dist/http/api-client.js.map +1 -1
  98. package/dist/http/credential-scope.cjs +1 -1
  99. package/dist/http/credential-scope.cjs.map +1 -1
  100. package/dist/http/credential-scope.js +5 -3
  101. package/dist/http/credential-scope.js.map +1 -1
  102. package/dist/http/csrf.cjs +2 -0
  103. package/dist/http/csrf.cjs.map +1 -0
  104. package/dist/http/csrf.js +46 -0
  105. package/dist/http/csrf.js.map +1 -0
  106. package/dist/http/resumable-upload.cjs +1 -1
  107. package/dist/http/resumable-upload.cjs.map +1 -1
  108. package/dist/http/resumable-upload.js +131 -123
  109. package/dist/http/resumable-upload.js.map +1 -1
  110. package/dist/http/upload-with-progress.cjs +1 -1
  111. package/dist/http/upload-with-progress.cjs.map +1 -1
  112. package/dist/http/upload-with-progress.js +41 -33
  113. package/dist/http/upload-with-progress.js.map +1 -1
  114. package/dist/query/use-paginated-query.cjs +1 -1
  115. package/dist/query/use-paginated-query.cjs.map +1 -1
  116. package/dist/query/use-paginated-query.js +47 -30
  117. package/dist/query/use-paginated-query.js.map +1 -1
  118. package/dist/styles/FilterPanel.css +2 -0
  119. package/dist/styles/component/Card.css +1 -1
  120. package/dist/styles/component/Combobox.css +4 -3
  121. package/dist/styles/component/FilterPanel.css +5 -0
  122. package/dist/styles/component/Select.css +13 -6
  123. package/dist/styles/core.css +4 -4
  124. package/dist/styles/data.css +4 -0
  125. package/dist/styles/forms.css +17 -9
  126. package/dist/styles/identity.css +1 -1
  127. package/dist/styles/manifest.json +6 -0
  128. package/dist/styles/tokens.css +4 -4
  129. package/dist/styles.css +1 -1
  130. package/dist/tempest-react-sdk.cjs +1 -1
  131. package/dist/tempest-react-sdk.d.ts +658 -22
  132. package/dist/tempest-react-sdk.js +347 -342
  133. package/dist/vite/tempest-csp.cjs +3 -0
  134. package/dist/vite/tempest-csp.cjs.map +1 -0
  135. package/dist/vite/tempest-csp.js +189 -0
  136. package/dist/vite/tempest-csp.js.map +1 -0
  137. package/dist/vite.cjs +1 -1
  138. package/dist/vite.d.ts +145 -0
  139. package/dist/vite.js +2 -1
  140. package/package.json +2 -2
  141. package/template/vite.config.ts +8 -1
  142. package/template-pwa/vite.config.ts +6 -0
@@ -171,6 +171,47 @@ export declare interface AccordionProps {
171
171
  className?: string;
172
172
  }
173
173
 
174
+ /**
175
+ * Resolve which navigation key is active for the current route.
176
+ *
177
+ * A menu whose keys are URLs almost never has a key equal to the pathname —
178
+ * `/dashboard/tips/42` has to highlight `/dashboard/tips`. This picks the
179
+ * **longest key that covers the path on a segment boundary**, which is what fixes
180
+ * the two ways the one-line `keys.find((k) => pathname.startsWith(k))` goes wrong:
181
+ * `/dashboard` stays lit on every screen of the panel (the array order decides),
182
+ * and `/dashboard/tip` lights `/dashboard/tips`.
183
+ *
184
+ * The rules:
185
+ *
186
+ * - Query string and hash are ignored, on both sides.
187
+ * - A trailing slash, on the route or on the key, changes nothing.
188
+ * - `/` is active only on `/` itself, as with react-router's `NavLink`.
189
+ * - The comparison is case-sensitive, as URL paths are.
190
+ * - No match returns `""` — never `undefined`, never a throw — so the result can
191
+ * go straight into a `value` prop.
192
+ *
193
+ * The keys and the pathname have to live in the same space: with a router
194
+ * `basename`, pass `useLocation().pathname` (which has the basename stripped) and
195
+ * keys without it.
196
+ *
197
+ * Framework-agnostic on purpose: it takes a string, so it works with
198
+ * react-router, with `window.location.pathname`, and for `BottomNavigation`,
199
+ * `NavigationRail` or a `Drawer` menu just as for `Sidebar` — which does the same
200
+ * resolution itself with `match="route"`.
201
+ *
202
+ * @param pathname - The current route, e.g. `useLocation().pathname`.
203
+ * @param keys - The navigation keys, each one a URL path.
204
+ * @returns The active key exactly as given in `keys`, or `""` when none covers
205
+ * the route.
206
+ *
207
+ * @example
208
+ * const { pathname } = useLocation();
209
+ * const value = activeNavKey(pathname, ["/dashboard", "/dashboard/tips"]);
210
+ * // "/dashboard/tips/42" → "/dashboard/tips"
211
+ * // "/dashboard/tip" → "/dashboard"
212
+ */
213
+ export declare function activeNavKey(pathname: string, keys: readonly string[]): string;
214
+
174
215
  /**
175
216
  * A conversation with a model: role-based turns, Markdown answers, a reasoning
176
217
  * block, a streaming caret, per-turn actions and a composer that turns into a stop
@@ -518,6 +559,15 @@ export declare type AlertVariant = "neutral" | "info" | "success" | "warning" |
518
559
  /** Every symbology in the spec, used to validate what a detector reports back. */
519
560
  export declare const ALL_BARCODE_FORMATS: readonly BarcodeFormat[];
520
561
 
562
+ /**
563
+ * The value {@link withAllOption} gives its entry, exported so the filter code
564
+ * compares against a name instead of a magic string.
565
+ *
566
+ * @example
567
+ * const where = status === ALL_OPTION_VALUE ? {} : { status };
568
+ */
569
+ export declare const ALL_OPTION_VALUE = "all";
570
+
521
571
  /**
522
572
  * Announce a message to screen readers, from anywhere — a hook, an event handler,
523
573
  * a plain function outside React.
@@ -739,7 +789,6 @@ export declare interface ApiClientConfig {
739
789
  * permission the caller does not have.
740
790
  */
741
791
  retry?: boolean | RetryOptions;
742
- /** Whether to send cookies on cross-origin requests (default: false). */
743
792
  /**
744
793
  * Milliseconds before a request is abandoned. Default `15_000`. `null` turns
745
794
  * it off.
@@ -769,7 +818,32 @@ export declare interface ApiClientConfig {
769
818
  * particular call does not fit either default.
770
819
  */
771
820
  uploadTimeout?: number | null;
821
+ /** Whether to send cookies on cross-origin requests (default: false). */
772
822
  withCredentials?: boolean;
823
+ /**
824
+ * Send a CSRF token on every request that can change state. Off by default.
825
+ *
826
+ * A bearer token is immune to cross-site request forgery because the
827
+ * browser never attaches `Authorization` on its own. A cookie session is
828
+ * not: the browser sends the cookie with a request another site triggers.
829
+ * That is the mode `withCredentials: true` exists for — and before 0.68.0
830
+ * the SDK had no piece of the defense.
831
+ *
832
+ * `true` uses the defaults of `tempest-fastapi-sdk`'s `CSRFMiddleware`:
833
+ * read the `csrf_token` cookie, send it as `X-CSRF-Token`. Pass
834
+ * {@link CsrfOptions} to rename either, or to supply the token yourself.
835
+ *
836
+ * The header goes on `POST`, `PUT`, `PATCH`, `DELETE` and any other
837
+ * non-safe method, never on `GET`/`HEAD`/`OPTIONS`/`TRACE`; only to the
838
+ * origins the bearer token may reach (`baseURL`'s plus
839
+ * {@link trustedOrigins}); on `skipAuth` requests too, because login and
840
+ * refresh are the writes a cookie session exposes; and never over a CSRF
841
+ * header you set yourself.
842
+ *
843
+ * Independent of `withCredentials`: a same-origin app sends its cookies
844
+ * without it, and is exposed the same way.
845
+ */
846
+ csrf?: boolean | CsrfOptions;
773
847
  /** Default headers merged into every request. */
774
848
  headers?: Record<string, string>;
775
849
  /** Optional fetch implementation (defaults to globalThis.fetch). */
@@ -1908,7 +1982,9 @@ export declare interface BottomNavigationProps extends Omit<HTMLAttributes<HTMLE
1908
1982
 
1909
1983
  /**
1910
1984
  * Slide-up modal panel — mobile-style sheet anchored to the bottom edge.
1911
- * Uses portal + safe-area padding + scroll lock.
1985
+ * Uses portal + safe-area padding + scroll lock, and traps keyboard focus in the
1986
+ * sheet like `Modal`: focus enters on open, `Tab` cycles inside and returns to
1987
+ * the opener on close.
1912
1988
  *
1913
1989
  * @example
1914
1990
  * <BottomSheet open={open} onClose={() => setOpen(false)} title="Filters">
@@ -3313,6 +3389,12 @@ export declare const CPFInput: ForwardRefExoticComponent<Omit<InputProps, "value
3313
3389
  * header, and a development build says so once. Declare the exceptions in
3314
3390
  * {@link ApiClientConfig.trustedOrigins}.
3315
3391
  *
3392
+ * **CSRF** is off unless you set `csrf`. With it, every unsafe request (anything
3393
+ * but `GET`/`HEAD`/`OPTIONS`/`TRACE`) echoes the `csrf_token` cookie in an
3394
+ * `X-CSRF-Token` header — the double-submit contract of `tempest-fastapi-sdk`'s
3395
+ * `CSRFMiddleware` — scoped to the same origins as the bearer token and sent on
3396
+ * `skipAuth` requests too. See {@link ApiClientConfig.csrf}.
3397
+ *
3316
3398
  * **Retries** are off unless you set `retry`. See {@link ApiClientConfig.retry}
3317
3399
  * for the built-in policy; it never replays a write. A single call overrides it
3318
3400
  * with {@link RequestOptions.retry}, and opts out of auth entirely with
@@ -4451,6 +4533,16 @@ export declare interface CreateTempestAuthOptions<TUser> {
4451
4533
  storage?: "local" | "session";
4452
4534
  /** Send cookies (needed when the refresh token lives in an httpOnly cookie). */
4453
4535
  withCredentials?: boolean;
4536
+ /**
4537
+ * CSRF token for every write, forwarded to `createApiClient`'s `csrf`. Off
4538
+ * by default; `true` echoes the `csrf_token` cookie as `X-CSRF-Token`.
4539
+ *
4540
+ * The pair to `withCredentials`: once the refresh token lives in a cookie,
4541
+ * the browser attaches it to a `POST /auth/refresh` that another site
4542
+ * triggers. Login and refresh carry the header too — they go out with
4543
+ * `skipAuth`, and the CSRF header does not follow auth.
4544
+ */
4545
+ csrf?: boolean | CsrfOptions;
4454
4546
  /** Custom fetch implementation (testing / SSR). Defaults to `globalThis.fetch`. */
4455
4547
  fetcher?: typeof fetch;
4456
4548
  /**
@@ -4821,6 +4913,91 @@ export declare interface CredentialsContainerLike {
4821
4913
  }): Promise<Credential | null>;
4822
4914
  }
4823
4915
 
4916
+ /**
4917
+ * The CSRF header a request should carry, or an empty object.
4918
+ *
4919
+ * Every request path in the SDK — `createApiClient`, `uploadWithProgress`,
4920
+ * `createResumableUpload` — asks this function, so the rules are one rule:
4921
+ *
4922
+ * - **Off unless configured.** Without `csrf`, nothing changes.
4923
+ * - **Unsafe methods only.** `GET`, `HEAD`, `OPTIONS` and `TRACE` never carry
4924
+ * it; they must not change state, and a token in a `GET` ends up in logs
4925
+ * and caches for nothing.
4926
+ * - **Never across an origin.** The token follows the same scope as the bearer
4927
+ * credential ({@link isTrustedCredentialTarget}), because a URL can come off
4928
+ * the network — a tus `Location`, a pagination link — and a CSRF token sent
4929
+ * to a third party is a token that third party can replay. Withheld tokens
4930
+ * are reported once per origin in a development build.
4931
+ * - **Your header wins.** A CSRF header you set by hand, in any casing, is
4932
+ * left alone.
4933
+ * - **Independent of auth.** It is sent on `skipAuth` requests too: login and
4934
+ * refresh are exactly the writes a cookie session exposes to forgery.
4935
+ *
4936
+ * Exported for the request you build by hand, so it follows the same rules.
4937
+ *
4938
+ * @example
4939
+ * const headers = csrfHeaders({
4940
+ * method: "POST",
4941
+ * url: "https://api.acme.com/orders",
4942
+ * reference: "https://api.acme.com",
4943
+ * csrf: true,
4944
+ * });
4945
+ * // { "X-CSRF-Token": "<value of the csrf_token cookie>" }
4946
+ *
4947
+ * @param input - The request and the CSRF config.
4948
+ * @returns A header record to spread into the request's headers.
4949
+ */
4950
+ export declare function csrfHeaders(input: CsrfHeadersInput): Record<string, string>;
4951
+
4952
+ /** What {@link csrfHeaders} needs to decide. */
4953
+ export declare interface CsrfHeadersInput {
4954
+ /** The HTTP method of the request. Case-insensitive. */
4955
+ method: string;
4956
+ /** The URL the request is going to. */
4957
+ url: string;
4958
+ /**
4959
+ * The origin the token belongs to, normally the API's `baseURL`. The token
4960
+ * is only sent to this origin or to one listed in `trustedOrigins`.
4961
+ */
4962
+ reference: string;
4963
+ /** The CSRF config. `undefined` or `false` turns the header off. */
4964
+ csrf: boolean | CsrfOptions | undefined;
4965
+ /** Extra origins allowed to receive the token. */
4966
+ trustedOrigins?: readonly string[];
4967
+ /** Headers the caller already set. A CSRF header among them is never replaced. */
4968
+ headers?: HeadersInit;
4969
+ }
4970
+
4971
+ /**
4972
+ * Where the CSRF token comes from and which header carries it.
4973
+ *
4974
+ * The default is the **double-submit cookie**: the server sets a cookie that is
4975
+ * deliberately not `HttpOnly`, the client reads it and echoes the value in a
4976
+ * header, and the server rejects a write whose header does not match the
4977
+ * cookie. A page on another site can make the browser *send* the cookie, but
4978
+ * cannot *read* it, so it cannot write the header.
4979
+ *
4980
+ * The token is read again on every request, so a server that rotates the
4981
+ * cookie is followed without any capture step: the browser's cookie jar is the
4982
+ * store. That is also why there is no "read the token from a response header"
4983
+ * mode — it would depend on `Access-Control-Expose-Headers`, and a missing
4984
+ * expose header fails silently, as a `403` on the second write.
4985
+ */
4986
+ export declare interface CsrfOptions {
4987
+ /** Cookie to read the token from. Default {@link DEFAULT_CSRF_COOKIE_NAME}. */
4988
+ cookieName?: string;
4989
+ /** Header to send the token in. Default {@link DEFAULT_CSRF_HEADER_NAME}. */
4990
+ headerName?: string;
4991
+ /**
4992
+ * Supply the token yourself instead of reading `cookieName`.
4993
+ *
4994
+ * For the cases the cookie cannot serve: an API on another **site**, whose
4995
+ * cookie `document.cookie` cannot see, or a backend that hands the token
4996
+ * out in a response body. Called per request, like `getToken`.
4997
+ */
4998
+ getToken?: () => string | null | undefined;
4999
+ }
5000
+
4824
5001
  /**
4825
5002
  * @tempest-limits param-count — `downloadCsv(rows, columns, "usuarios.csv")` reads
4826
5003
  * in the order the sentence does, and its two trailing parameters have defaults,
@@ -4994,7 +5171,8 @@ export declare interface DataProviderOptions {
4994
5171
  * - Clicking a sortable header cycles asc → desc → unsorted.
4995
5172
  * - Search matches a case-insensitive substring across `searchKeys`
4996
5173
  * (or every string/number column when not provided).
4997
- * - Pagination is hidden when the result fits on a single page.
5174
+ * - Pagination is hidden when the result fits on a single page — unless
5175
+ * `onPageSizeChange` is set, because the footer then carries the size selector.
4998
5176
  * - A column with `editable` renders a button that opens an inline editor;
4999
5177
  * `Enter` commits, `Escape` discards, `Tab` walks to the next editable cell.
5000
5178
  *
@@ -5015,7 +5193,7 @@ export declare interface DataProviderOptions {
5015
5193
  * render without changing behaviour. That is why `exhaustive-deps` is silenced on
5016
5194
  * that dependency array.
5017
5195
  */
5018
- export declare function DataTable<T>({ data, columns, pageSize, searchable, searchKeys, initialSort, rowKey, emptyMessage, onCellChange, editLabels, totalItems, page: controlledPage, onPageChange, manualSort, onSortChange, manualSearch, onSearchChange, loading, className, ...rest }: DataTableProps<T>): JSX.Element;
5196
+ export declare function DataTable<T>({ data, columns, pageSize: pageSizeProp, searchable, searchKeys, initialSort, rowKey, emptyMessage, onCellChange, editLabels, totalItems, page: controlledPage, onPageChange, onPageSizeChange, pageSizeOptions, manualSort, onSortChange, manualSearch, onSearchChange, loading, className, ...rest }: DataTableProps<T>): JSX.Element;
5019
5197
 
5020
5198
  /** Everything a table needs regardless of who owns paging, sorting and searching. */
5021
5199
  export declare interface DataTableBaseProps<T> extends HTMLAttributes<HTMLDivElement> {
@@ -5029,7 +5207,13 @@ export declare interface DataTableBaseProps<T> extends HTMLAttributes<HTMLDivEle
5029
5207
  data: T[];
5030
5208
  /** Column definitions. */
5031
5209
  columns: DataTableColumn<T>[];
5032
- /** Rows per page. Default 10. */
5210
+ /**
5211
+ * Rows per page. Default 10.
5212
+ *
5213
+ * Always read on every render, so it is already a controlled value; what turns
5214
+ * it into a user-facing choice is `onPageSizeChange` (see
5215
+ * {@link DataTablePageSizeProps}), which then makes it required.
5216
+ */
5033
5217
  pageSize?: number;
5034
5218
  /** Render a search input above the table. Default false. */
5035
5219
  searchable?: boolean;
@@ -5141,6 +5325,40 @@ export declare interface DataTableEditLabels {
5141
5325
  /** Input types an editable column can use. */
5142
5326
  export declare type DataTableEditorType = "text" | "number" | "date" | "email" | "tel" | "url";
5143
5327
 
5328
+ /**
5329
+ * The items-per-page selector: owned by the caller, or absent.
5330
+ *
5331
+ * The footer is the SDK's own `Pagination`, which already renders a size
5332
+ * `<select>`; this is the channel that reaches it. Two shapes only, for the same
5333
+ * reason as {@link DataTablePagingProps}: `pageSizeOptions` with no
5334
+ * `onPageSizeChange` is a selector that changes nothing, and `onPageSizeChange`
5335
+ * with no `pageSize` is one whose choice never comes back — the table would keep
5336
+ * counting pages by the default 10 while the selector claims 50. Both are build
5337
+ * errors instead.
5338
+ */
5339
+ export declare type DataTablePageSizeProps = {
5340
+ /** No selector: `pageSize` is fixed by the caller. */
5341
+ onPageSizeChange?: never;
5342
+ /** Nothing to choose from. */
5343
+ pageSizeOptions?: never;
5344
+ } | {
5345
+ /** The size currently shown. Required, since the selector reports and you feed it back. */
5346
+ pageSize: number;
5347
+ /**
5348
+ * Called with the size the user picked. Passing it renders the selector in the
5349
+ * footer, and keeps the footer on screen even when everything fits on one page
5350
+ * — otherwise picking 100 for 40 rows would remove the only control that can
5351
+ * pick 10 again.
5352
+ *
5353
+ * The table also moves back to page 1 (through `onPageChange` when the page
5354
+ * is controlled), after this call. Page 7 of 10-per-page has no meaning at
5355
+ * 100-per-page, and 1 is the only page guaranteed to exist under the new size.
5356
+ */
5357
+ onPageSizeChange: (size: number) => void;
5358
+ /** Sizes offered. Default `[10, 25, 50, 100]`; the current `pageSize` is always included. */
5359
+ pageSizeOptions?: number[];
5360
+ };
5361
+
5144
5362
  /**
5145
5363
  * Paging, as one of the three shapes that actually work.
5146
5364
  *
@@ -5192,10 +5410,10 @@ export declare type DataTablePagingProps = {
5192
5410
  };
5193
5411
 
5194
5412
  /**
5195
- * The table's props: the shared half, plus one valid paging shape and one valid
5196
- * sorting shape.
5413
+ * The table's props: the shared half, plus one valid shape per axis — paging,
5414
+ * page size, sorting and searching.
5197
5415
  */
5198
- export declare type DataTableProps<T> = DataTableBaseProps<T> & DataTablePagingProps & DataTableSortProps<T> & DataTableSearchProps;
5416
+ export declare type DataTableProps<T> = DataTableBaseProps<T> & DataTablePagingProps & DataTablePageSizeProps & DataTableSortProps<T> & DataTableSearchProps;
5199
5417
 
5200
5418
  /**
5201
5419
  * Searching: delegated, and therefore reported, or neither.
@@ -5424,6 +5642,25 @@ export declare const DEFAULT_CHUNK_SIZE: number;
5424
5642
  */
5425
5643
  export declare const DEFAULT_CIRCUITY_FACTOR = 1.3;
5426
5644
 
5645
+ /**
5646
+ * Default cookie the CSRF token is read from.
5647
+ *
5648
+ * Ported from `tempest-fastapi-sdk`'s `CSRF_COOKIE_NAME`
5649
+ * (`tempest_fastapi_sdk/api/middlewares/csrf.py`), which is the cookie its
5650
+ * `make_csrf_token_dependency()` sets and its `CSRFMiddleware` compares against.
5651
+ * Matching it is what lets `csrf: true` work against that backend with no
5652
+ * configuration on either side.
5653
+ */
5654
+ export declare const DEFAULT_CSRF_COOKIE_NAME = "csrf_token";
5655
+
5656
+ /**
5657
+ * Default header the CSRF token is echoed in.
5658
+ *
5659
+ * Ported from `tempest-fastapi-sdk`'s `CSRF_HEADER_NAME`, the header its
5660
+ * `CSRFMiddleware` reads on every unsafe request.
5661
+ */
5662
+ export declare const DEFAULT_CSRF_HEADER_NAME = "X-CSRF-Token";
5663
+
5427
5664
  /** Default ceiling for a per-source or master gain, as a multiplier. */
5428
5665
  export declare const DEFAULT_MAX_GAIN = 3;
5429
5666
 
@@ -5792,9 +6029,14 @@ export declare type DocumentVisibility = "visible" | "hidden";
5792
6029
  */
5793
6030
  export declare function downloadCsv<T>(rows: readonly T[], columns: readonly CsvColumn<T>[], fileName?: string, options?: CsvOptions): Promise<void>;
5794
6031
 
6032
+ /** An edit to the draft: a partial object merged over it, or an updater. */
6033
+ export declare type DraftFiltersPatch<T> = Partial<T> | ((draft: T) => T);
6034
+
5795
6035
  /**
5796
6036
  * Sliding side panel. Same building blocks as {@link Modal} but anchored to
5797
- * an edge. Locks body scroll while open.
6037
+ * an edge. Locks body scroll while open, and traps keyboard focus in the panel
6038
+ * the same way `Modal` does: focus enters on open, `Tab` cycles inside (portalled
6039
+ * panels opened from it included) and returns to the opener on close.
5798
6040
  */
5799
6041
  export declare function Drawer({ open, onClose, placement, title, children, footer, closeOnBackdrop, closeOnEsc, hideCloseButton, className, mobilePlacement, showHandle, }: DrawerProps): ReactPortal | null;
5800
6042
 
@@ -6289,6 +6531,95 @@ export declare type FilterFieldType = "text" | "number" | "date" | "select" | "b
6289
6531
  /** Comparison a filter can make. */
6290
6532
  export declare type FilterOperator = "eq" | "ne" | "contains" | "gt" | "gte" | "lt" | "lte" | "between" | "in" | "empty" | "notEmpty";
6291
6533
 
6534
+ /**
6535
+ * The frame around a fixed set of filters: a card with a title, a grid of the
6536
+ * fields the screen declares, and the actions that belong to no field — clear,
6537
+ * apply, export.
6538
+ *
6539
+ * It holds no state and has no opinion on the fields. `FilterBar` is for ad hoc
6540
+ * querying (the user picks field, operator and value); this is for the set a
6541
+ * screen already knows — município, UF, situação, a search box. Pair it with
6542
+ * `useDraftFilters`: the fields edit `draft`, `onApply` and `onClear` are the
6543
+ * hook's `apply` and `clear`.
6544
+ *
6545
+ * What it settles, each measured in Chromium against the `Card` + `Grid`
6546
+ * composition it replaces:
6547
+ *
6548
+ * - **The grid follows the panel, not the viewport.** `Grid` switches columns on
6549
+ * viewport breakpoints, so the same filters in a 360 px column of a 1280 px
6550
+ * screen got four 57.5 px fields. The default here is
6551
+ * `repeat(auto-fill, minmax(min(100%, minFieldWidth), 1fr))`: the column count
6552
+ * comes from the room the panel has, and `min(100%, …)` keeps a floor wider than
6553
+ * the phone from overflowing it.
6554
+ * - **Cells align to the top.** A field showing an error is taller; with the grid
6555
+ * default (`stretch`), its row-mates' boxes grew to match.
6556
+ * - **The header wraps.** Three actions next to the title measured 527 px in a
6557
+ * 308 px header at 390 px wide, and scrolled the whole page sideways. `Card`'s
6558
+ * header now wraps, and so do the actions inside it.
6559
+ * - **Clear and apply have a fixed place**, at the end of the header, apply last.
6560
+ *
6561
+ * @example
6562
+ * const filters = useDraftFilters({ search: "", uf: "" });
6563
+ * <FilterPanel
6564
+ * onApply={() => filters.apply()}
6565
+ * onClear={filters.clear}
6566
+ * applyDisabled={!filters.isDirty}
6567
+ * >
6568
+ * <Input label="Busca" value={filters.draft.search}
6569
+ * onChange={(e) => filters.set({ search: e.target.value })} />
6570
+ * <Select label="UF" options={UFS} value={filters.draft.uf}
6571
+ * onChange={(e) => filters.set({ uf: e.target.value })} />
6572
+ * </FilterPanel>
6573
+ */
6574
+ export declare function FilterPanel({ children, title, actions, onClear, onApply, applyDisabled, locale, labels, minFieldWidth, columns, className, style, ...props }: FilterPanelProps): JSX.Element;
6575
+
6576
+ /** Labels the panel writes itself. */
6577
+ export declare interface FilterPanelLabels {
6578
+ /** Default title. `"Filtros"` / `"Filters"`. */
6579
+ title: string;
6580
+ /** The clear button. `"Limpar filtros"` / `"Clear filters"`. */
6581
+ clear: string;
6582
+ /** The apply button. `"Filtrar"` / `"Apply filters"`. */
6583
+ apply: string;
6584
+ }
6585
+
6586
+ export declare interface FilterPanelProps extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps_14> {
6587
+ /** The filter controls the screen declares — `Input`, `Select`, a date picker. One grid cell each. */
6588
+ children: ReactNode;
6589
+ /** Heading. Default from `locale`. A string renders as an `h3`; a node renders as given. */
6590
+ title?: ReactNode;
6591
+ /** Header actions that are not fields — "Atualizar a cada 30s", "Exportar CSV". */
6592
+ actions?: ReactNode;
6593
+ /** Return the screen to its initial filters. Renders the clear button only when set. */
6594
+ onClear?: () => void;
6595
+ /**
6596
+ * Publish the draft. When set, the fields sit in a `<form>`: the apply button
6597
+ * submits it and Enter in any text field applies.
6598
+ */
6599
+ onApply?: () => void;
6600
+ /**
6601
+ * Disable the apply button — pass `!filters.isDirty`. Enter follows the button:
6602
+ * it is the form's default button, so a disabled one blocks implicit submission
6603
+ * too, and nothing is re-applied when nothing changed.
6604
+ */
6605
+ applyDisabled?: boolean;
6606
+ /** Locale for the default title and button labels. Default `"pt-BR"`. */
6607
+ locale?: "pt-BR" | "en";
6608
+ /** Override any of the labels. */
6609
+ labels?: Partial<FilterPanelLabels>;
6610
+ /**
6611
+ * Narrowest a field may get before the grid drops a column. Default `"12rem"`.
6612
+ * The count follows the **panel's** width, so the same panel fits a page and a
6613
+ * sidebar.
6614
+ */
6615
+ minFieldWidth?: string;
6616
+ /**
6617
+ * Fixed columns per viewport, as in `Grid` — `{ mobile: 1, tablet: 2, desktop: 4 }`.
6618
+ * Replaces `minFieldWidth`, and follows the **viewport**, not the panel.
6619
+ */
6620
+ columns?: ResponsiveValue<number | string>;
6621
+ }
6622
+
6292
6623
  export declare type FilterPredicate<T> = (item: T, search: string) => boolean;
6293
6624
 
6294
6625
  /**
@@ -9298,6 +9629,13 @@ export declare type MicrophoneStatus = "idle" | "requesting" | "ready" | "error"
9298
9629
  * Portal-rendered modal dialog with backdrop, Esc handler, and slots for
9299
9630
  * header/body/footer. Locks body scroll while open.
9300
9631
  *
9632
+ * Keyboard focus is trapped in the dialog while it is open (`useFocusTrap`): it
9633
+ * enters on the first tab stop — or the dialog itself when there is none —,
9634
+ * `Tab`/`Shift+Tab` cycle inside it, including the panels of a `Popover` or
9635
+ * `HoverCard` opened from it, and closing returns focus to the element that
9636
+ * opened it. `aria-modal="true"` tells a screen reader the page behind is inert;
9637
+ * the trap is what makes that true for the keyboard.
9638
+ *
9301
9639
  * The `dialog` role needs an accessible name: a `title` supplies it via
9302
9640
  * `aria-labelledby`, and a titleless dialog should pass `aria-label`. The
9303
9641
  * header heading is only rendered when there is a title, so a close-button-only
@@ -10204,6 +10542,26 @@ export declare interface OpenModalOptions {
10204
10542
  /** The operators a field offers. */
10205
10543
  export declare function operatorsFor(field: FilterField): FilterOperator[];
10206
10544
 
10545
+ /**
10546
+ * The shape every option list in the SDK shares: `Select`, `MultiSelect` and
10547
+ * `Combobox` all accept an array of it.
10548
+ *
10549
+ * `value` is always a `string` here, because that is what a `<select>` hands
10550
+ * back in `event.target.value` whatever the option was built from: an option
10551
+ * built with the number `0` comes back as the string `"0"`, so a list that kept
10552
+ * numbers could never be compared with `===` against what the control emits.
10553
+ */
10554
+ export declare interface OptionEntry {
10555
+ value: string;
10556
+ label: string;
10557
+ }
10558
+
10559
+ /** The minimum an option needs for the helpers to prepend to its list. */
10560
+ declare type OptionLike = {
10561
+ value: string | number;
10562
+ label: string;
10563
+ };
10564
+
10207
10565
  /**
10208
10566
  * @tempest-limits file-lines — parsing a session description, merging one
10209
10567
  * `fmtp` line and inserting a missing one are the same pass over the same
@@ -10361,6 +10719,9 @@ declare type OverriddenDomProps_12 = "children";
10361
10719
  /** DOM attributes this component redefines. */
10362
10720
  declare type OverriddenDomProps_13 = "children" | "onChange";
10363
10721
 
10722
+ /** DOM attributes this component redefines. */
10723
+ declare type OverriddenDomProps_14 = "title" | "children" | "onSubmit";
10724
+
10364
10725
  /**
10365
10726
  * DOM attributes this component redefines.
10366
10727
  *
@@ -10445,6 +10806,14 @@ export declare interface PaginationProps {
10445
10806
  onPageChange: (page: number) => void;
10446
10807
  pageSize?: number;
10447
10808
  onPageSizeChange?: (size: number) => void;
10809
+ /**
10810
+ * Sizes offered in the selector. Default `[10, 25, 50, 100]`.
10811
+ *
10812
+ * The current `pageSize` is always offered too, merged in ascending order. A
10813
+ * `<select>` whose value matches no option displays its first option, so
10814
+ * `pageSize={20}` against the default list used to read "10 / página" over a
10815
+ * table showing 20 rows.
10816
+ */
10448
10817
  pageSizeOptions?: number[];
10449
10818
  /** Total items count; if provided, renders the summary text. */
10450
10819
  totalItems?: number;
@@ -12132,6 +12501,17 @@ export declare interface ResumableUploadOptions {
12132
12501
  trustedOrigins?: readonly string[];
12133
12502
  /** Send cookies. Default `false`. */
12134
12503
  withCredentials?: boolean;
12504
+ /**
12505
+ * Send a CSRF token on the creation `POST`, every chunk `PATCH` and the
12506
+ * discarding `DELETE` — never on the offset `HEAD`. Off by default; `true`
12507
+ * reads the `csrf_token` cookie into `X-CSRF-Token`, as `createApiClient`
12508
+ * does.
12509
+ *
12510
+ * Scoped like the bearer token, to `endpoint`'s origin plus
12511
+ * {@link trustedOrigins}: a `Location` pointing at object storage on
12512
+ * another host gets neither.
12513
+ */
12514
+ csrf?: boolean | CsrfOptions;
12135
12515
  /**
12136
12516
  * Resume key. Defaults to a fingerprint of endpoint + file name/size/mtime, so
12137
12517
  * picking the same file after a reload resumes instead of restarting.
@@ -12655,7 +13035,7 @@ export declare interface SegmentedControlProps<TValue extends string = string> e
12655
13035
  * Native `<select>` wrapper. Either provide `options` for a quick render, or
12656
13036
  * pass `<option>` children directly.
12657
13037
  *
12658
- * Two variants:
13038
+ * Three variants:
12659
13039
  *
12660
13040
  * - `"field"` (default) — label, control, helper text and error slot, for a form.
12661
13041
  * - `"chip"` — the control alone, sized to its content, for a settings row where
@@ -12664,20 +13044,60 @@ export declare interface SegmentedControlProps<TValue extends string = string> e
12664
13044
  * list item, which is why every app ended up hand-rolling a native `<select>`
12665
13045
  * with `appearance: none` — and hand-rolling it loses the focus ring, the
12666
13046
  * disabled styling and the caret this component already solves.
13047
+ * - `"bare"` — the same mechanism with none of the look, for an app with its own
13048
+ * visual identity: the shell renders `wrapper > select + caret`, and the app
13049
+ * dresses the wrapper through `wrapperClassName`. See {@link SelectBareProps}.
12667
13050
  *
12668
13051
  * @example
12669
13052
  * <Select label="Estado" options={states} placeholder="Selecione" />
12670
13053
  *
12671
13054
  * @example
12672
13055
  * <ListTile title="Idioma" trailing={<Select variant="chip" aria-label="Idioma" options={langs} />} />
13056
+ *
13057
+ * @example
13058
+ * <Select variant="bare" wrapperClassName={styles.filtro} aria-label="Status" options={status} />
12673
13059
  */
12674
13060
  export declare const Select: ForwardRefExoticComponent<SelectProps & RefAttributes<HTMLSelectElement>>;
12675
13061
 
12676
- /** What both variants of {@link Select} accept. */
12677
- declare interface SelectBaseProps extends SelectHTMLAttributes<HTMLSelectElement> {
13062
+ /**
13063
+ * The mechanism without the look: `appearance: none`, the caret and the focus
13064
+ * ring, and no border, background, radius, shadow or height.
13065
+ *
13066
+ * The `<select>` inherits `background-color` and `color` from the shell (the
13067
+ * element `wrapperClassName` lands on). The background is not cosmetic: the
13068
+ * native popup paints each `<option>` from the `<select>`'s background, so a
13069
+ * transparent control would hand the menu back to the system white.
13070
+ *
13071
+ * Like `"chip"`, the variant has no visible label, so an accessible name is
13072
+ * required — through `aria-label` or `aria-labelledby`.
13073
+ */
13074
+ export declare type SelectBareProps = SelectBaseProps & {
13075
+ variant: "bare";
13076
+ } & ({
13077
+ "aria-label": string;
13078
+ } | {
13079
+ "aria-labelledby": string;
13080
+ });
13081
+
13082
+ /** What every variant of {@link Select} accepts. */
13083
+ export declare interface SelectBaseProps extends SelectHTMLAttributes<HTMLSelectElement> {
12678
13084
  options?: SelectOption[];
12679
13085
  placeholder?: string;
13086
+ /**
13087
+ * Class for the outermost element. On `"bare"` that element is the shell
13088
+ * holding the `<select>` and the caret, which is where the app's own look goes.
13089
+ */
12680
13090
  wrapperClassName?: string;
13091
+ /**
13092
+ * Replaces the built-in chevron, following the `icon?: ReactNode` convention
13093
+ * the rest of the SDK uses.
13094
+ *
13095
+ * The icon is drawn in the caret slot, which is positioned by
13096
+ * `--tempest-control-caret-offset` and sized by `--tempest-control-caret-size`;
13097
+ * an `<svg>` passed here is stretched to that size, so the token — not the
13098
+ * icon's own `width`/`height` — decides how big the caret is in every density.
13099
+ */
13100
+ caretIcon?: ReactNode;
12681
13101
  }
12682
13102
 
12683
13103
  /**
@@ -12688,13 +13108,13 @@ declare interface SelectBaseProps extends SelectHTMLAttributes<HTMLSelectElement
12688
13108
  * row's own text, which the `<select>` has no relationship to — so without this
12689
13109
  * the screen reader announces a combobox with no name at all.
12690
13110
  */
12691
- declare interface SelectChipProps extends SelectBaseProps {
13111
+ export declare interface SelectChipProps extends SelectBaseProps {
12692
13112
  variant: "chip";
12693
13113
  "aria-label": string;
12694
13114
  }
12695
13115
 
12696
13116
  /** The full form field: label, control, helper text and error slot. */
12697
- declare interface SelectFieldProps extends SelectBaseProps {
13117
+ export declare interface SelectFieldProps extends SelectBaseProps {
12698
13118
  variant?: "field";
12699
13119
  label?: string;
12700
13120
  helperText?: string;
@@ -12707,7 +13127,7 @@ export declare interface SelectOption {
12707
13127
  disabled?: boolean;
12708
13128
  }
12709
13129
 
12710
- export declare type SelectProps = SelectFieldProps | SelectChipProps;
13130
+ export declare type SelectProps = SelectFieldProps | SelectChipProps | SelectBareProps;
12711
13131
 
12712
13132
  /**
12713
13133
  * Minimal subset of `@sentry/browser` used by the adapter. Lets you pass
@@ -13058,6 +13478,17 @@ export declare interface ShowProps {
13058
13478
  * </Show>
13059
13479
  *
13060
13480
  * @example
13481
+ * const { pathname } = useLocation();
13482
+ * <Sidebar
13483
+ * match="route"
13484
+ * value={pathname}
13485
+ * items={[
13486
+ * { key: "overview", label: "Overview", href: "/dashboard" },
13487
+ * { key: "tips", label: "Tips", href: "/dashboard/tips" },
13488
+ * ]}
13489
+ * />
13490
+ *
13491
+ * @example
13061
13492
  * <Sidebar
13062
13493
  * items={[
13063
13494
  * { type: "section", key: "monitoring", label: "Monitoring" },
@@ -13070,7 +13501,7 @@ export declare interface ShowProps {
13070
13501
  * onChange={setTab}
13071
13502
  * />
13072
13503
  */
13073
- export declare function Sidebar({ header, items, value, onChange, footer, collapsed, width, collapsedWidth, className, style, ...props }: SidebarProps): JSX.Element;
13504
+ export declare function Sidebar({ header, items, value, onChange, match, footer, collapsed, width, collapsedWidth, className, style, ...props }: SidebarProps): JSX.Element;
13074
13505
 
13075
13506
  /**
13076
13507
  * One entry of the navigation list.
@@ -13109,8 +13540,28 @@ export declare interface SidebarProps extends Omit<HTMLAttributes<HTMLElement>,
13109
13540
  header?: ReactNode;
13110
13541
  /** Navigation entries. An entry with no `type` is an item. */
13111
13542
  items: SidebarEntry[];
13112
- /** Active item key. */
13543
+ /**
13544
+ * The active item: its `key` by default, or the current route with
13545
+ * `match="route"`.
13546
+ */
13113
13547
  value?: string;
13548
+ /**
13549
+ * How `value` picks the active item.
13550
+ *
13551
+ * - `"key"` (default): the item whose `key` equals `value`.
13552
+ * - `"route"`: `value` is the current pathname, and the active item is the one
13553
+ * whose `href` (or `key`, when it has no `href`) is the longest path covering
13554
+ * it on a segment boundary — see `activeNavKey`. `/dashboard/tips/42` lights
13555
+ * `/dashboard/tips`, not `/dashboard`, and `/users-admin` never lights
13556
+ * `/users`. Sections and separators take no part.
13557
+ *
13558
+ * `"route"` is what lets the app pass `useLocation().pathname` and stop
13559
+ * computing a key: the component already holds the entries the resolution runs
13560
+ * against, so asking the app to repeat them is how the two drift apart. It
13561
+ * reads no router itself — the value comes in as a string — so the component
13562
+ * keeps working with no router mounted.
13563
+ */
13564
+ match?: "key" | "route";
13114
13565
  /** Fires when an item is clicked. Receives the item's `key`. */
13115
13566
  onChange?: (key: string) => void;
13116
13567
  /** Bottom slot — typically settings/profile/logout. */
@@ -14439,6 +14890,35 @@ export declare interface TooltipProps {
14439
14890
  portal?: boolean;
14440
14891
  }
14441
14892
 
14893
+ /**
14894
+ * Turn a `value → label` map into the option list `Select`, `MultiSelect` and
14895
+ * `Combobox` take.
14896
+ *
14897
+ * Every key becomes a string `value` (see {@link OptionEntry} for why), so the
14898
+ * result is assignable to `SelectOption[]`, `MultiSelectOption[]` and
14899
+ * `ComboboxOption[]` alike.
14900
+ *
14901
+ * **Order.** A `Map` keeps the order it was written in, always. A plain object
14902
+ * does too — **except for integer-like keys**, which JavaScript enumerates first
14903
+ * and in ascending order whatever order they were written in
14904
+ * (`OrdinaryOwnPropertyKeys`, ECMA-262 §10.1.11.1). Measured in Node 24:
14905
+ * `Object.keys({ 5: "Ótimo", 3: "Ok", 1: "Ruim" })` is `["1", "3", "5"]`, and
14906
+ * `{ baixa, 10, 2, alta }` enumerates as `["2", "10", "baixa", "alta"]`. When
14907
+ * the business order of a numeric scale is not ascending, pass a `Map`.
14908
+ *
14909
+ * @param source - A record or a `Map` from value to label.
14910
+ * @returns A new array, one entry per key, in the order described above.
14911
+ *
14912
+ * @example
14913
+ * const STATUS = { active: "Ativo", paused: "Pausado" };
14914
+ * <Select aria-label="Status" variant="chip" options={toOptions(STATUS)} />
14915
+ *
14916
+ * @example
14917
+ * const HUMOR = new Map([[5, "Ótimo"], [3, "Ok"], [1, "Ruim"]]);
14918
+ * toOptions(HUMOR); // 5, 3, 1 — the order written
14919
+ */
14920
+ export declare function toOptions(source: Readonly<Record<string, string>> | ReadonlyMap<string | number, string>): OptionEntry[];
14921
+
14442
14922
  /** Convert degrees to radians. */
14443
14923
  export declare function toRadians(degrees: number): number;
14444
14924
 
@@ -14954,6 +15434,18 @@ export declare interface UploadWithProgressOptions {
14954
15434
  trustedOrigins?: readonly string[];
14955
15435
  /** Send cookies. Defaults to false. */
14956
15436
  withCredentials?: boolean;
15437
+ /**
15438
+ * Send a CSRF token with the upload, as `createApiClient`'s `csrf` does.
15439
+ * Off by default; `true` reads the `csrf_token` cookie into `X-CSRF-Token`.
15440
+ *
15441
+ * Scoped to {@link credentialOrigin} plus {@link trustedOrigins} — and,
15442
+ * unlike the bearer token, scoped even when `credentialOrigin` is unset:
15443
+ * then the scope is the page's own origin. The bearer default stays open
15444
+ * only for compatibility with every version before 0.66.0; this option is
15445
+ * new, so it starts closed. An API on another origin sets
15446
+ * `credentialOrigin` to receive the token.
15447
+ */
15448
+ csrf?: boolean | CsrfOptions;
14957
15449
  /** Called on every `progress` event from the XHR upload channel. */
14958
15450
  onProgress?: (event: UploadProgressEvent) => void;
14959
15451
  /** Abort the request. */
@@ -15551,6 +16043,80 @@ export declare function useDocumentTitle(title: string): void;
15551
16043
  /** Subscribe to `document.visibilityState`. Returns `"visible"` during SSR. */
15552
16044
  export declare function useDocumentVisibility(): DocumentVisibility;
15553
16045
 
16046
+ /**
16047
+ * Draft vs applied state for a server-side filter: the controls edit a draft
16048
+ * freely, and the query only sees it when the user applies (a click, or Enter).
16049
+ *
16050
+ * A server-side filter cannot react to every keystroke — each character typed in
16051
+ * a search box would be a request. Every admin screen then writes the same pair of
16052
+ * `useState`s, and gets the same three details wrong:
16053
+ *
16054
+ * - **Clearing moves both halves.** Resetting only the draft leaves a filtered
16055
+ * list under an empty bar; resetting only the applied value leaves the opposite.
16056
+ * - **Applying a value that is not in state yet.** "Filter by this code" has to
16057
+ * write the draft and publish it in one gesture; calling `setDraft(x)` and then
16058
+ * publishing `draft` reads the previous value. `apply(patch)` computes the next
16059
+ * draft from a ref that every call keeps current, so it never depends on the
16060
+ * order React flushes state in.
16061
+ * - **Resetting the page.** Filtering while on page 7 of a result that now has two
16062
+ * pages shows an empty table. `usePaginatedQuery` handles it by itself when
16063
+ * `applied` is in its `queryKey`; a page kept elsewhere resets in `onApply`.
16064
+ *
16065
+ * There is no effect in here: applying happens inside the event that asked for it,
16066
+ * so one click is one request. `applied` keeps its reference while nothing new is
16067
+ * applied, so it can sit in a `queryKey` without a phantom refetch.
16068
+ *
16069
+ * Like `useState`, the initial value is read on the first render only; `clear()`
16070
+ * returns to it.
16071
+ *
16072
+ * @param initial - The empty filter form: what the screen shows and queries first.
16073
+ * @param options - `onApply` and `isEqual`.
16074
+ * @returns The draft, the applied value and the gestures between them.
16075
+ *
16076
+ * @example
16077
+ * const filters = useDraftFilters({ code: "", level: "all" });
16078
+ * const logs = usePaginatedQuery({
16079
+ * queryKey: ["logs", filters.applied],
16080
+ * queryFn: (params) => api.listLogs({ ...params, ...toQuery(filters.applied) }),
16081
+ * });
16082
+ * // <Input value={filters.draft.code} onChange={(e) => filters.set({ code: e.target.value })} />
16083
+ * // <Button disabled={!filters.isDirty} onClick={() => filters.apply()}>Filtrar</Button>
16084
+ */
16085
+ export declare function useDraftFilters<T extends object>(initial: T, options?: UseDraftFiltersOptions<T>): UseDraftFiltersResult<T>;
16086
+
16087
+ export declare interface UseDraftFiltersOptions<T> {
16088
+ /**
16089
+ * Called with the applied value on every `apply()` and `clear()` — the place to
16090
+ * reset a page number you keep yourself. `usePaginatedQuery` does not need it:
16091
+ * it goes back to page 1 on its own when `applied` changes its `queryKey`.
16092
+ */
16093
+ onApply?: (applied: T) => void;
16094
+ /** Equality behind `isDirty` and the stable `applied`. Default: structural. */
16095
+ isEqual?: (a: T, b: T) => boolean;
16096
+ }
16097
+
16098
+ export declare interface UseDraftFiltersResult<T> {
16099
+ /** What the filter controls edit. Changes on every keystroke. */
16100
+ draft: T;
16101
+ /**
16102
+ * What the query reads. Changes only on `apply()`/`clear()`, and keeps its
16103
+ * reference when the value applied is equal to the current one — safe to put
16104
+ * in a `queryKey`.
16105
+ */
16106
+ applied: T;
16107
+ /** Whether the draft differs from what is applied — enable the apply button on it. */
16108
+ isDirty: boolean;
16109
+ /** Edit the draft, merging a partial object or running an updater. Never applies. */
16110
+ set: (patch: DraftFiltersPatch<T>) => void;
16111
+ /**
16112
+ * Publish the draft. With a patch, merge it into the draft **and** publish the
16113
+ * result in the same call — "filter by this code" from a table row.
16114
+ */
16115
+ apply: (patch?: DraftFiltersPatch<T>) => void;
16116
+ /** Reset the draft and the applied value to the initial one, together. */
16117
+ clear: () => void;
16118
+ }
16119
+
15554
16120
  /**
15555
16121
  * Hook that propagates errors to the nearest {@link ErrorBoundary}.
15556
16122
  *
@@ -15657,9 +16223,38 @@ export { UseFieldArrayReturn }
15657
16223
  export declare function useFlagValue<T extends FlagValue = FlagValue>(key: string, defaultValue?: T): T;
15658
16224
 
15659
16225
  /**
15660
- * Trap keyboard focus inside `containerRef` while `active` is true. Cycles
15661
- * Tab and Shift+Tab between the first and last focusable descendants. Pair
15662
- * with Modal/Drawer for fully-accessible overlays.
16226
+ * Trap keyboard focus inside `containerRef` while `active` is true.
16227
+ *
16228
+ * - **On activation** focus moves to the first tab stop, or to the container
16229
+ * itself when it has none (give it `tabIndex={-1}`). Content that already put
16230
+ * focus inside — an `autoFocus` input — is left where it is.
16231
+ * - **`Tab` / `Shift+Tab`** wrap between the first and the last stop, and pull
16232
+ * focus back in when it sits outside.
16233
+ * - **On release** focus returns to the element that held it when the trap
16234
+ * armed. That element is read during the render that arms the trap, before
16235
+ * React commits: an `autoFocus` child takes focus in the commit, ahead of any
16236
+ * effect, and reading it there would "restore" to the child that is about to
16237
+ * unmount — which dropped focus on `body` in `Command`.
16238
+ *
16239
+ * **Portalled panels stay reachable.** A `Popover` or `HoverCard` opened from
16240
+ * inside the container renders at the end of `body`, outside it. Those panels
16241
+ * register themselves (see `usePortalTabOrder`), and the trap treats them as
16242
+ * part of the container, spliced right after their trigger. A `Tab` another
16243
+ * layer already consumed is not moved again, unless it moved focus out of the
16244
+ * container and its panels — the portal bridge leaving the last panel stop for
16245
+ * whatever follows the trap in the page.
16246
+ *
16247
+ * **Traps stack.** Only the most recent active trap handles `Tab`, so a modal
16248
+ * opened over another owns the keyboard until it closes, and then hands focus
16249
+ * back to the element it was opened from, inside the one below.
16250
+ *
16251
+ * The listener is on `document` in the bubble phase, after React's own
16252
+ * handlers: a menu that closes on `Tab` and returns focus to its trigger has
16253
+ * already done so when the trap looks at where focus is.
16254
+ *
16255
+ * @param containerRef - The element focus is confined to.
16256
+ * @param active - Whether the trap is armed.
16257
+ * @returns Nothing.
15663
16258
  */
15664
16259
  export declare function useFocusTrap(containerRef: RefObject<HTMLElement | null>, active: boolean): void;
15665
16260
 
@@ -16624,6 +17219,13 @@ export declare function useOptionalI18n(): I18nContextValue | null;
16624
17219
  * fetcher, keeps the previous page visible while the next loads, and derives
16625
17220
  * `hasNext`/`hasPrev`/`pageCount`.
16626
17221
  *
17222
+ * A structurally different `queryKey` (compared with TanStack's `hashKey`) is a
17223
+ * different result set, so the page returns to 1. The reset is applied during
17224
+ * render rather than in an effect: an effect would let one render go out with the
17225
+ * new key and the old page, and that render is a request — on page 7 of an
17226
+ * unfiltered list, applying a filter used to ask for page 7 of a two-page result
17227
+ * and show an empty table (#356).
17228
+ *
16627
17229
  * @example
16628
17230
  * const users = usePaginatedQuery<User>({
16629
17231
  * queryKey: ["users"],
@@ -16635,7 +17237,11 @@ export declare function useOptionalI18n(): I18nContextValue | null;
16635
17237
  export declare function usePaginatedQuery<T>(options: UsePaginatedQueryOptions<T>): UsePaginatedQueryResult<T>;
16636
17238
 
16637
17239
  export declare interface UsePaginatedQueryOptions<T> extends Omit<UseQueryOptions<OffsetPage<T>, Error, OffsetPage<T>, QueryKey>, "queryKey" | "queryFn"> {
16638
- /** Base query key — the current page/sort is appended for cache isolation. */
17240
+ /**
17241
+ * Base query key — the current page/sort is appended for cache isolation.
17242
+ * Put the applied filters here: when the key changes structurally, the page
17243
+ * goes back to 1 in the same render.
17244
+ */
16639
17245
  queryKey: QueryKey;
16640
17246
  /** Fetcher receiving the offset params; return the backend envelope. */
16641
17247
  queryFn: (params: OffsetParams) => Promise<OffsetPage<T>> | OffsetPage<T>;
@@ -16659,7 +17265,7 @@ export declare interface UsePaginatedQueryResult<T> {
16659
17265
  page: OffsetPage<T> | undefined;
16660
17266
  /** The rows of the current page (empty array while pending). */
16661
17267
  items: T[];
16662
- /** Current 1-based page number (controlled by the hook). */
17268
+ /** Current 1-based page number (controlled by the hook; back to 1 on a new `queryKey`). */
16663
17269
  pageNumber: number;
16664
17270
  /** Total page count from the last successful response. */
16665
17271
  pageCount: number;
@@ -18703,6 +19309,36 @@ export declare interface WindowSize {
18703
19309
  height: number;
18704
19310
  }
18705
19311
 
19312
+ /**
19313
+ * Prepend the "no filter" entry to an option list.
19314
+ *
19315
+ * A filter needs this as a real option, not as the select's placeholder: a
19316
+ * placeholder reads as "nothing chosen yet", so the screen cannot tell whether
19317
+ * the column is filtered. The entry uses {@link ALL_OPTION_VALUE}.
19318
+ *
19319
+ * @param options - The list to extend. It is not mutated.
19320
+ * @param label - The entry's label. Defaults to `"Todos"`.
19321
+ * @returns A new array with the entry first.
19322
+ */
19323
+ export declare function withAllOption<T extends OptionLike>(options: readonly T[], label?: string): (T | OptionEntry)[];
19324
+
19325
+ /**
19326
+ * Prepend the empty entry a nullable field needs.
19327
+ *
19328
+ * Without it a `<select>` has no way to show "unset": a controlled
19329
+ * `value=""` with no matching option reads back as the **first option** (measured
19330
+ * in jsdom: `select.value === "a"`), so opening an edit form and saving it
19331
+ * writes a value nobody chose. `Select`'s `placeholder` does not cover this — its
19332
+ * entry is `disabled hidden`, so once something is picked the field can never be
19333
+ * cleared again. The entry's value is `""`, which is what an unanswered nullable field
19334
+ * submits.
19335
+ *
19336
+ * @param options - The list to extend. It is not mutated.
19337
+ * @param label - The entry's label. Defaults to `"— Não definido —"`.
19338
+ * @returns A new array with the entry first.
19339
+ */
19340
+ export declare function withEmptyOption<T extends OptionLike>(options: readonly T[], label?: string): (T | OptionEntry)[];
19341
+
18706
19342
  /**
18707
19343
  * A bound Zustand hook augmented with auto-generated per-field selector hooks
18708
19344
  * under `.use`. Reading a single field via `store.use.field()` subscribes the