tempest-react-sdk 0.67.0 → 0.69.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 (147) 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/sw/create-push-handler.cjs +1 -1
  131. package/dist/sw/create-push-handler.cjs.map +1 -1
  132. package/dist/sw/create-push-handler.js +58 -30
  133. package/dist/sw/create-push-handler.js.map +1 -1
  134. package/dist/sw.d.ts +53 -2
  135. package/dist/tempest-react-sdk.cjs +1 -1
  136. package/dist/tempest-react-sdk.d.ts +711 -24
  137. package/dist/tempest-react-sdk.js +347 -342
  138. package/dist/vite/tempest-csp.cjs +3 -0
  139. package/dist/vite/tempest-csp.cjs.map +1 -0
  140. package/dist/vite/tempest-csp.js +189 -0
  141. package/dist/vite/tempest-csp.js.map +1 -0
  142. package/dist/vite.cjs +1 -1
  143. package/dist/vite.d.ts +145 -0
  144. package/dist/vite.js +2 -1
  145. package/package.json +2 -2
  146. package/template/vite.config.ts +8 -1
  147. 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
  /**
@@ -7676,12 +8007,20 @@ export declare type InstallMethod = "native" | "ios" | "manual" | "none";
7676
8007
  /**
7677
8008
  * Install a `notificationclick` handler that focuses an existing client when
7678
8009
  * possible and falls back to opening a new window.
8010
+ *
8011
+ * A click on an action button opens that action's URL (see
8012
+ * {@link PushNotificationAction.url}); a click on the body, or on an action
8013
+ * without one, opens the top-level `url`.
7679
8014
  */
7680
8015
  export declare function installNotificationClickHandler(options?: InstallNotificationClickHandlerOptions): void;
7681
8016
 
7682
8017
  export declare interface InstallNotificationClickHandlerOptions {
7683
- /** Resolve the destination URL from the notification data. Default: `data.url`. */
7684
- resolveUrl?: (data: unknown) => string;
8018
+ /**
8019
+ * Resolve the destination URL from the notification data and the clicked
8020
+ * action id (`""` or `undefined` for a click on the body). Default: the
8021
+ * action's entry in `data.actionUrls`, else `data.url`, else `"/"`.
8022
+ */
8023
+ resolveUrl?: (data: unknown, action: string | undefined) => string;
7685
8024
  }
7686
8025
 
7687
8026
  export declare type InstallOutcome = "accepted" | "dismissed" | "unsupported";
@@ -7722,6 +8061,12 @@ export declare interface InstallPrecacheOptions {
7722
8061
  /**
7723
8062
  * Install a `push` event listener that parses the payload as JSON (with a
7724
8063
  * plain-text fallback) and shows a notification.
8064
+ *
8065
+ * Every `NotificationOptions` member the payload carries is forwarded —
8066
+ * `actions`, `requireInteraction`, `vibrate`, `silent`, `renotify`,
8067
+ * `timestamp`, `dir` and `lang` alongside `body`, `icon`, `badge`, `image` and
8068
+ * `tag`. `data` receives the top-level `url` (default `"/"`), the payload's
8069
+ * own `data`, and `actionUrls` mapping each action id to its `url`.
7725
8070
  */
7726
8071
  export declare function installPushHandler(options?: InstallPushHandlerOptions): void;
7727
8072
 
@@ -9298,6 +9643,13 @@ export declare type MicrophoneStatus = "idle" | "requesting" | "ready" | "error"
9298
9643
  * Portal-rendered modal dialog with backdrop, Esc handler, and slots for
9299
9644
  * header/body/footer. Locks body scroll while open.
9300
9645
  *
9646
+ * Keyboard focus is trapped in the dialog while it is open (`useFocusTrap`): it
9647
+ * enters on the first tab stop — or the dialog itself when there is none —,
9648
+ * `Tab`/`Shift+Tab` cycle inside it, including the panels of a `Popover` or
9649
+ * `HoverCard` opened from it, and closing returns focus to the element that
9650
+ * opened it. `aria-modal="true"` tells a screen reader the page behind is inert;
9651
+ * the trap is what makes that true for the keyboard.
9652
+ *
9301
9653
  * The `dialog` role needs an accessible name: a `title` supplies it via
9302
9654
  * `aria-labelledby`, and a titleless dialog should pass `aria-label`. The
9303
9655
  * header heading is only rendered when there is a title, so a close-button-only
@@ -10204,6 +10556,26 @@ export declare interface OpenModalOptions {
10204
10556
  /** The operators a field offers. */
10205
10557
  export declare function operatorsFor(field: FilterField): FilterOperator[];
10206
10558
 
10559
+ /**
10560
+ * The shape every option list in the SDK shares: `Select`, `MultiSelect` and
10561
+ * `Combobox` all accept an array of it.
10562
+ *
10563
+ * `value` is always a `string` here, because that is what a `<select>` hands
10564
+ * back in `event.target.value` whatever the option was built from: an option
10565
+ * built with the number `0` comes back as the string `"0"`, so a list that kept
10566
+ * numbers could never be compared with `===` against what the control emits.
10567
+ */
10568
+ export declare interface OptionEntry {
10569
+ value: string;
10570
+ label: string;
10571
+ }
10572
+
10573
+ /** The minimum an option needs for the helpers to prepend to its list. */
10574
+ declare type OptionLike = {
10575
+ value: string | number;
10576
+ label: string;
10577
+ };
10578
+
10207
10579
  /**
10208
10580
  * @tempest-limits file-lines — parsing a session description, merging one
10209
10581
  * `fmtp` line and inserting a missing one are the same pass over the same
@@ -10361,6 +10733,9 @@ declare type OverriddenDomProps_12 = "children";
10361
10733
  /** DOM attributes this component redefines. */
10362
10734
  declare type OverriddenDomProps_13 = "children" | "onChange";
10363
10735
 
10736
+ /** DOM attributes this component redefines. */
10737
+ declare type OverriddenDomProps_14 = "title" | "children" | "onSubmit";
10738
+
10364
10739
  /**
10365
10740
  * DOM attributes this component redefines.
10366
10741
  *
@@ -10445,6 +10820,14 @@ export declare interface PaginationProps {
10445
10820
  onPageChange: (page: number) => void;
10446
10821
  pageSize?: number;
10447
10822
  onPageSizeChange?: (size: number) => void;
10823
+ /**
10824
+ * Sizes offered in the selector. Default `[10, 25, 50, 100]`.
10825
+ *
10826
+ * The current `pageSize` is always offered too, merged in ascending order. A
10827
+ * `<select>` whose value matches no option displays its first option, so
10828
+ * `pageSize={20}` against the default list used to read "10 / página" over a
10829
+ * table showing 20 rows.
10830
+ */
10448
10831
  pageSizeOptions?: number[];
10449
10832
  /** Total items count; if provided, renders the summary text. */
10450
10833
  totalItems?: number;
@@ -11323,6 +11706,26 @@ export declare const PUSH_TO_TALK_KEYS: readonly PushToTalkKey[];
11323
11706
  * installPushHandler({ defaultIcon: "/icons/Logo.png" });
11324
11707
  * installNotificationClickHandler();
11325
11708
  */
11709
+ /**
11710
+ * A notification button as the push payload describes it.
11711
+ *
11712
+ * `action`, `title` and `icon` are forwarded to the browser as a
11713
+ * `NotificationAction`. `url` is not part of that type — the browser would drop
11714
+ * it — so {@link installPushHandler} moves it into `data.actionUrls`, where
11715
+ * {@link installNotificationClickHandler} reads it back when that button is
11716
+ * clicked.
11717
+ */
11718
+ export declare interface PushNotificationAction {
11719
+ /** Identifier delivered as `event.action` when the button is clicked. */
11720
+ action: string;
11721
+ /** Button label. */
11722
+ title: string;
11723
+ /** Button icon URL. */
11724
+ icon?: string;
11725
+ /** URL opened when this button is clicked. Falls back to the top-level `url`. */
11726
+ url?: string;
11727
+ }
11728
+
11326
11729
  export declare interface PushPayload {
11327
11730
  title?: string;
11328
11731
  body?: string;
@@ -11330,7 +11733,24 @@ export declare interface PushPayload {
11330
11733
  badge?: string;
11331
11734
  image?: string;
11332
11735
  tag?: string;
11736
+ /** URL opened when the notification body is clicked. Default: `"/"`. */
11333
11737
  url?: string;
11738
+ /** Buttons rendered on the notification (browsers show up to `Notification.maxActions`). */
11739
+ actions?: PushNotificationAction[];
11740
+ /** Keep the notification on screen until the user interacts with it. */
11741
+ requireInteraction?: boolean;
11742
+ /** Vibration pattern in milliseconds (mobile). */
11743
+ vibrate?: number | number[];
11744
+ /** Show the notification without sound or vibration. */
11745
+ silent?: boolean;
11746
+ /** Alert again when a notification with the same `tag` replaces an older one. */
11747
+ renotify?: boolean;
11748
+ /** Event time in epoch milliseconds, shown by some platforms. */
11749
+ timestamp?: number;
11750
+ /** Text direction of the title and body. */
11751
+ dir?: NotificationDirection;
11752
+ /** BCP 47 language tag of the title and body. */
11753
+ lang?: string;
11334
11754
  /** Arbitrary extra data forwarded to `event.notification.data`. */
11335
11755
  data?: Record<string, unknown>;
11336
11756
  }
@@ -12132,6 +12552,17 @@ export declare interface ResumableUploadOptions {
12132
12552
  trustedOrigins?: readonly string[];
12133
12553
  /** Send cookies. Default `false`. */
12134
12554
  withCredentials?: boolean;
12555
+ /**
12556
+ * Send a CSRF token on the creation `POST`, every chunk `PATCH` and the
12557
+ * discarding `DELETE` — never on the offset `HEAD`. Off by default; `true`
12558
+ * reads the `csrf_token` cookie into `X-CSRF-Token`, as `createApiClient`
12559
+ * does.
12560
+ *
12561
+ * Scoped like the bearer token, to `endpoint`'s origin plus
12562
+ * {@link trustedOrigins}: a `Location` pointing at object storage on
12563
+ * another host gets neither.
12564
+ */
12565
+ csrf?: boolean | CsrfOptions;
12135
12566
  /**
12136
12567
  * Resume key. Defaults to a fingerprint of endpoint + file name/size/mtime, so
12137
12568
  * picking the same file after a reload resumes instead of restarting.
@@ -12655,7 +13086,7 @@ export declare interface SegmentedControlProps<TValue extends string = string> e
12655
13086
  * Native `<select>` wrapper. Either provide `options` for a quick render, or
12656
13087
  * pass `<option>` children directly.
12657
13088
  *
12658
- * Two variants:
13089
+ * Three variants:
12659
13090
  *
12660
13091
  * - `"field"` (default) — label, control, helper text and error slot, for a form.
12661
13092
  * - `"chip"` — the control alone, sized to its content, for a settings row where
@@ -12664,20 +13095,60 @@ export declare interface SegmentedControlProps<TValue extends string = string> e
12664
13095
  * list item, which is why every app ended up hand-rolling a native `<select>`
12665
13096
  * with `appearance: none` — and hand-rolling it loses the focus ring, the
12666
13097
  * disabled styling and the caret this component already solves.
13098
+ * - `"bare"` — the same mechanism with none of the look, for an app with its own
13099
+ * visual identity: the shell renders `wrapper > select + caret`, and the app
13100
+ * dresses the wrapper through `wrapperClassName`. See {@link SelectBareProps}.
12667
13101
  *
12668
13102
  * @example
12669
13103
  * <Select label="Estado" options={states} placeholder="Selecione" />
12670
13104
  *
12671
13105
  * @example
12672
13106
  * <ListTile title="Idioma" trailing={<Select variant="chip" aria-label="Idioma" options={langs} />} />
13107
+ *
13108
+ * @example
13109
+ * <Select variant="bare" wrapperClassName={styles.filtro} aria-label="Status" options={status} />
12673
13110
  */
12674
13111
  export declare const Select: ForwardRefExoticComponent<SelectProps & RefAttributes<HTMLSelectElement>>;
12675
13112
 
12676
- /** What both variants of {@link Select} accept. */
12677
- declare interface SelectBaseProps extends SelectHTMLAttributes<HTMLSelectElement> {
13113
+ /**
13114
+ * The mechanism without the look: `appearance: none`, the caret and the focus
13115
+ * ring, and no border, background, radius, shadow or height.
13116
+ *
13117
+ * The `<select>` inherits `background-color` and `color` from the shell (the
13118
+ * element `wrapperClassName` lands on). The background is not cosmetic: the
13119
+ * native popup paints each `<option>` from the `<select>`'s background, so a
13120
+ * transparent control would hand the menu back to the system white.
13121
+ *
13122
+ * Like `"chip"`, the variant has no visible label, so an accessible name is
13123
+ * required — through `aria-label` or `aria-labelledby`.
13124
+ */
13125
+ export declare type SelectBareProps = SelectBaseProps & {
13126
+ variant: "bare";
13127
+ } & ({
13128
+ "aria-label": string;
13129
+ } | {
13130
+ "aria-labelledby": string;
13131
+ });
13132
+
13133
+ /** What every variant of {@link Select} accepts. */
13134
+ export declare interface SelectBaseProps extends SelectHTMLAttributes<HTMLSelectElement> {
12678
13135
  options?: SelectOption[];
12679
13136
  placeholder?: string;
13137
+ /**
13138
+ * Class for the outermost element. On `"bare"` that element is the shell
13139
+ * holding the `<select>` and the caret, which is where the app's own look goes.
13140
+ */
12680
13141
  wrapperClassName?: string;
13142
+ /**
13143
+ * Replaces the built-in chevron, following the `icon?: ReactNode` convention
13144
+ * the rest of the SDK uses.
13145
+ *
13146
+ * The icon is drawn in the caret slot, which is positioned by
13147
+ * `--tempest-control-caret-offset` and sized by `--tempest-control-caret-size`;
13148
+ * an `<svg>` passed here is stretched to that size, so the token — not the
13149
+ * icon's own `width`/`height` — decides how big the caret is in every density.
13150
+ */
13151
+ caretIcon?: ReactNode;
12681
13152
  }
12682
13153
 
12683
13154
  /**
@@ -12688,13 +13159,13 @@ declare interface SelectBaseProps extends SelectHTMLAttributes<HTMLSelectElement
12688
13159
  * row's own text, which the `<select>` has no relationship to — so without this
12689
13160
  * the screen reader announces a combobox with no name at all.
12690
13161
  */
12691
- declare interface SelectChipProps extends SelectBaseProps {
13162
+ export declare interface SelectChipProps extends SelectBaseProps {
12692
13163
  variant: "chip";
12693
13164
  "aria-label": string;
12694
13165
  }
12695
13166
 
12696
13167
  /** The full form field: label, control, helper text and error slot. */
12697
- declare interface SelectFieldProps extends SelectBaseProps {
13168
+ export declare interface SelectFieldProps extends SelectBaseProps {
12698
13169
  variant?: "field";
12699
13170
  label?: string;
12700
13171
  helperText?: string;
@@ -12707,7 +13178,7 @@ export declare interface SelectOption {
12707
13178
  disabled?: boolean;
12708
13179
  }
12709
13180
 
12710
- export declare type SelectProps = SelectFieldProps | SelectChipProps;
13181
+ export declare type SelectProps = SelectFieldProps | SelectChipProps | SelectBareProps;
12711
13182
 
12712
13183
  /**
12713
13184
  * Minimal subset of `@sentry/browser` used by the adapter. Lets you pass
@@ -13058,6 +13529,17 @@ export declare interface ShowProps {
13058
13529
  * </Show>
13059
13530
  *
13060
13531
  * @example
13532
+ * const { pathname } = useLocation();
13533
+ * <Sidebar
13534
+ * match="route"
13535
+ * value={pathname}
13536
+ * items={[
13537
+ * { key: "overview", label: "Overview", href: "/dashboard" },
13538
+ * { key: "tips", label: "Tips", href: "/dashboard/tips" },
13539
+ * ]}
13540
+ * />
13541
+ *
13542
+ * @example
13061
13543
  * <Sidebar
13062
13544
  * items={[
13063
13545
  * { type: "section", key: "monitoring", label: "Monitoring" },
@@ -13070,7 +13552,7 @@ export declare interface ShowProps {
13070
13552
  * onChange={setTab}
13071
13553
  * />
13072
13554
  */
13073
- export declare function Sidebar({ header, items, value, onChange, footer, collapsed, width, collapsedWidth, className, style, ...props }: SidebarProps): JSX.Element;
13555
+ export declare function Sidebar({ header, items, value, onChange, match, footer, collapsed, width, collapsedWidth, className, style, ...props }: SidebarProps): JSX.Element;
13074
13556
 
13075
13557
  /**
13076
13558
  * One entry of the navigation list.
@@ -13109,8 +13591,28 @@ export declare interface SidebarProps extends Omit<HTMLAttributes<HTMLElement>,
13109
13591
  header?: ReactNode;
13110
13592
  /** Navigation entries. An entry with no `type` is an item. */
13111
13593
  items: SidebarEntry[];
13112
- /** Active item key. */
13594
+ /**
13595
+ * The active item: its `key` by default, or the current route with
13596
+ * `match="route"`.
13597
+ */
13113
13598
  value?: string;
13599
+ /**
13600
+ * How `value` picks the active item.
13601
+ *
13602
+ * - `"key"` (default): the item whose `key` equals `value`.
13603
+ * - `"route"`: `value` is the current pathname, and the active item is the one
13604
+ * whose `href` (or `key`, when it has no `href`) is the longest path covering
13605
+ * it on a segment boundary — see `activeNavKey`. `/dashboard/tips/42` lights
13606
+ * `/dashboard/tips`, not `/dashboard`, and `/users-admin` never lights
13607
+ * `/users`. Sections and separators take no part.
13608
+ *
13609
+ * `"route"` is what lets the app pass `useLocation().pathname` and stop
13610
+ * computing a key: the component already holds the entries the resolution runs
13611
+ * against, so asking the app to repeat them is how the two drift apart. It
13612
+ * reads no router itself — the value comes in as a string — so the component
13613
+ * keeps working with no router mounted.
13614
+ */
13615
+ match?: "key" | "route";
13114
13616
  /** Fires when an item is clicked. Receives the item's `key`. */
13115
13617
  onChange?: (key: string) => void;
13116
13618
  /** Bottom slot — typically settings/profile/logout. */
@@ -14439,6 +14941,35 @@ export declare interface TooltipProps {
14439
14941
  portal?: boolean;
14440
14942
  }
14441
14943
 
14944
+ /**
14945
+ * Turn a `value → label` map into the option list `Select`, `MultiSelect` and
14946
+ * `Combobox` take.
14947
+ *
14948
+ * Every key becomes a string `value` (see {@link OptionEntry} for why), so the
14949
+ * result is assignable to `SelectOption[]`, `MultiSelectOption[]` and
14950
+ * `ComboboxOption[]` alike.
14951
+ *
14952
+ * **Order.** A `Map` keeps the order it was written in, always. A plain object
14953
+ * does too — **except for integer-like keys**, which JavaScript enumerates first
14954
+ * and in ascending order whatever order they were written in
14955
+ * (`OrdinaryOwnPropertyKeys`, ECMA-262 §10.1.11.1). Measured in Node 24:
14956
+ * `Object.keys({ 5: "Ótimo", 3: "Ok", 1: "Ruim" })` is `["1", "3", "5"]`, and
14957
+ * `{ baixa, 10, 2, alta }` enumerates as `["2", "10", "baixa", "alta"]`. When
14958
+ * the business order of a numeric scale is not ascending, pass a `Map`.
14959
+ *
14960
+ * @param source - A record or a `Map` from value to label.
14961
+ * @returns A new array, one entry per key, in the order described above.
14962
+ *
14963
+ * @example
14964
+ * const STATUS = { active: "Ativo", paused: "Pausado" };
14965
+ * <Select aria-label="Status" variant="chip" options={toOptions(STATUS)} />
14966
+ *
14967
+ * @example
14968
+ * const HUMOR = new Map([[5, "Ótimo"], [3, "Ok"], [1, "Ruim"]]);
14969
+ * toOptions(HUMOR); // 5, 3, 1 — the order written
14970
+ */
14971
+ export declare function toOptions(source: Readonly<Record<string, string>> | ReadonlyMap<string | number, string>): OptionEntry[];
14972
+
14442
14973
  /** Convert degrees to radians. */
14443
14974
  export declare function toRadians(degrees: number): number;
14444
14975
 
@@ -14954,6 +15485,18 @@ export declare interface UploadWithProgressOptions {
14954
15485
  trustedOrigins?: readonly string[];
14955
15486
  /** Send cookies. Defaults to false. */
14956
15487
  withCredentials?: boolean;
15488
+ /**
15489
+ * Send a CSRF token with the upload, as `createApiClient`'s `csrf` does.
15490
+ * Off by default; `true` reads the `csrf_token` cookie into `X-CSRF-Token`.
15491
+ *
15492
+ * Scoped to {@link credentialOrigin} plus {@link trustedOrigins} — and,
15493
+ * unlike the bearer token, scoped even when `credentialOrigin` is unset:
15494
+ * then the scope is the page's own origin. The bearer default stays open
15495
+ * only for compatibility with every version before 0.66.0; this option is
15496
+ * new, so it starts closed. An API on another origin sets
15497
+ * `credentialOrigin` to receive the token.
15498
+ */
15499
+ csrf?: boolean | CsrfOptions;
14957
15500
  /** Called on every `progress` event from the XHR upload channel. */
14958
15501
  onProgress?: (event: UploadProgressEvent) => void;
14959
15502
  /** Abort the request. */
@@ -15551,6 +16094,80 @@ export declare function useDocumentTitle(title: string): void;
15551
16094
  /** Subscribe to `document.visibilityState`. Returns `"visible"` during SSR. */
15552
16095
  export declare function useDocumentVisibility(): DocumentVisibility;
15553
16096
 
16097
+ /**
16098
+ * Draft vs applied state for a server-side filter: the controls edit a draft
16099
+ * freely, and the query only sees it when the user applies (a click, or Enter).
16100
+ *
16101
+ * A server-side filter cannot react to every keystroke — each character typed in
16102
+ * a search box would be a request. Every admin screen then writes the same pair of
16103
+ * `useState`s, and gets the same three details wrong:
16104
+ *
16105
+ * - **Clearing moves both halves.** Resetting only the draft leaves a filtered
16106
+ * list under an empty bar; resetting only the applied value leaves the opposite.
16107
+ * - **Applying a value that is not in state yet.** "Filter by this code" has to
16108
+ * write the draft and publish it in one gesture; calling `setDraft(x)` and then
16109
+ * publishing `draft` reads the previous value. `apply(patch)` computes the next
16110
+ * draft from a ref that every call keeps current, so it never depends on the
16111
+ * order React flushes state in.
16112
+ * - **Resetting the page.** Filtering while on page 7 of a result that now has two
16113
+ * pages shows an empty table. `usePaginatedQuery` handles it by itself when
16114
+ * `applied` is in its `queryKey`; a page kept elsewhere resets in `onApply`.
16115
+ *
16116
+ * There is no effect in here: applying happens inside the event that asked for it,
16117
+ * so one click is one request. `applied` keeps its reference while nothing new is
16118
+ * applied, so it can sit in a `queryKey` without a phantom refetch.
16119
+ *
16120
+ * Like `useState`, the initial value is read on the first render only; `clear()`
16121
+ * returns to it.
16122
+ *
16123
+ * @param initial - The empty filter form: what the screen shows and queries first.
16124
+ * @param options - `onApply` and `isEqual`.
16125
+ * @returns The draft, the applied value and the gestures between them.
16126
+ *
16127
+ * @example
16128
+ * const filters = useDraftFilters({ code: "", level: "all" });
16129
+ * const logs = usePaginatedQuery({
16130
+ * queryKey: ["logs", filters.applied],
16131
+ * queryFn: (params) => api.listLogs({ ...params, ...toQuery(filters.applied) }),
16132
+ * });
16133
+ * // <Input value={filters.draft.code} onChange={(e) => filters.set({ code: e.target.value })} />
16134
+ * // <Button disabled={!filters.isDirty} onClick={() => filters.apply()}>Filtrar</Button>
16135
+ */
16136
+ export declare function useDraftFilters<T extends object>(initial: T, options?: UseDraftFiltersOptions<T>): UseDraftFiltersResult<T>;
16137
+
16138
+ export declare interface UseDraftFiltersOptions<T> {
16139
+ /**
16140
+ * Called with the applied value on every `apply()` and `clear()` — the place to
16141
+ * reset a page number you keep yourself. `usePaginatedQuery` does not need it:
16142
+ * it goes back to page 1 on its own when `applied` changes its `queryKey`.
16143
+ */
16144
+ onApply?: (applied: T) => void;
16145
+ /** Equality behind `isDirty` and the stable `applied`. Default: structural. */
16146
+ isEqual?: (a: T, b: T) => boolean;
16147
+ }
16148
+
16149
+ export declare interface UseDraftFiltersResult<T> {
16150
+ /** What the filter controls edit. Changes on every keystroke. */
16151
+ draft: T;
16152
+ /**
16153
+ * What the query reads. Changes only on `apply()`/`clear()`, and keeps its
16154
+ * reference when the value applied is equal to the current one — safe to put
16155
+ * in a `queryKey`.
16156
+ */
16157
+ applied: T;
16158
+ /** Whether the draft differs from what is applied — enable the apply button on it. */
16159
+ isDirty: boolean;
16160
+ /** Edit the draft, merging a partial object or running an updater. Never applies. */
16161
+ set: (patch: DraftFiltersPatch<T>) => void;
16162
+ /**
16163
+ * Publish the draft. With a patch, merge it into the draft **and** publish the
16164
+ * result in the same call — "filter by this code" from a table row.
16165
+ */
16166
+ apply: (patch?: DraftFiltersPatch<T>) => void;
16167
+ /** Reset the draft and the applied value to the initial one, together. */
16168
+ clear: () => void;
16169
+ }
16170
+
15554
16171
  /**
15555
16172
  * Hook that propagates errors to the nearest {@link ErrorBoundary}.
15556
16173
  *
@@ -15657,9 +16274,38 @@ export { UseFieldArrayReturn }
15657
16274
  export declare function useFlagValue<T extends FlagValue = FlagValue>(key: string, defaultValue?: T): T;
15658
16275
 
15659
16276
  /**
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.
16277
+ * Trap keyboard focus inside `containerRef` while `active` is true.
16278
+ *
16279
+ * - **On activation** focus moves to the first tab stop, or to the container
16280
+ * itself when it has none (give it `tabIndex={-1}`). Content that already put
16281
+ * focus inside — an `autoFocus` input — is left where it is.
16282
+ * - **`Tab` / `Shift+Tab`** wrap between the first and the last stop, and pull
16283
+ * focus back in when it sits outside.
16284
+ * - **On release** focus returns to the element that held it when the trap
16285
+ * armed. That element is read during the render that arms the trap, before
16286
+ * React commits: an `autoFocus` child takes focus in the commit, ahead of any
16287
+ * effect, and reading it there would "restore" to the child that is about to
16288
+ * unmount — which dropped focus on `body` in `Command`.
16289
+ *
16290
+ * **Portalled panels stay reachable.** A `Popover` or `HoverCard` opened from
16291
+ * inside the container renders at the end of `body`, outside it. Those panels
16292
+ * register themselves (see `usePortalTabOrder`), and the trap treats them as
16293
+ * part of the container, spliced right after their trigger. A `Tab` another
16294
+ * layer already consumed is not moved again, unless it moved focus out of the
16295
+ * container and its panels — the portal bridge leaving the last panel stop for
16296
+ * whatever follows the trap in the page.
16297
+ *
16298
+ * **Traps stack.** Only the most recent active trap handles `Tab`, so a modal
16299
+ * opened over another owns the keyboard until it closes, and then hands focus
16300
+ * back to the element it was opened from, inside the one below.
16301
+ *
16302
+ * The listener is on `document` in the bubble phase, after React's own
16303
+ * handlers: a menu that closes on `Tab` and returns focus to its trigger has
16304
+ * already done so when the trap looks at where focus is.
16305
+ *
16306
+ * @param containerRef - The element focus is confined to.
16307
+ * @param active - Whether the trap is armed.
16308
+ * @returns Nothing.
15663
16309
  */
15664
16310
  export declare function useFocusTrap(containerRef: RefObject<HTMLElement | null>, active: boolean): void;
15665
16311
 
@@ -16624,6 +17270,13 @@ export declare function useOptionalI18n(): I18nContextValue | null;
16624
17270
  * fetcher, keeps the previous page visible while the next loads, and derives
16625
17271
  * `hasNext`/`hasPrev`/`pageCount`.
16626
17272
  *
17273
+ * A structurally different `queryKey` (compared with TanStack's `hashKey`) is a
17274
+ * different result set, so the page returns to 1. The reset is applied during
17275
+ * render rather than in an effect: an effect would let one render go out with the
17276
+ * new key and the old page, and that render is a request — on page 7 of an
17277
+ * unfiltered list, applying a filter used to ask for page 7 of a two-page result
17278
+ * and show an empty table (#356).
17279
+ *
16627
17280
  * @example
16628
17281
  * const users = usePaginatedQuery<User>({
16629
17282
  * queryKey: ["users"],
@@ -16635,7 +17288,11 @@ export declare function useOptionalI18n(): I18nContextValue | null;
16635
17288
  export declare function usePaginatedQuery<T>(options: UsePaginatedQueryOptions<T>): UsePaginatedQueryResult<T>;
16636
17289
 
16637
17290
  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. */
17291
+ /**
17292
+ * Base query key — the current page/sort is appended for cache isolation.
17293
+ * Put the applied filters here: when the key changes structurally, the page
17294
+ * goes back to 1 in the same render.
17295
+ */
16639
17296
  queryKey: QueryKey;
16640
17297
  /** Fetcher receiving the offset params; return the backend envelope. */
16641
17298
  queryFn: (params: OffsetParams) => Promise<OffsetPage<T>> | OffsetPage<T>;
@@ -16659,7 +17316,7 @@ export declare interface UsePaginatedQueryResult<T> {
16659
17316
  page: OffsetPage<T> | undefined;
16660
17317
  /** The rows of the current page (empty array while pending). */
16661
17318
  items: T[];
16662
- /** Current 1-based page number (controlled by the hook). */
17319
+ /** Current 1-based page number (controlled by the hook; back to 1 on a new `queryKey`). */
16663
17320
  pageNumber: number;
16664
17321
  /** Total page count from the last successful response. */
16665
17322
  pageCount: number;
@@ -18703,6 +19360,36 @@ export declare interface WindowSize {
18703
19360
  height: number;
18704
19361
  }
18705
19362
 
19363
+ /**
19364
+ * Prepend the "no filter" entry to an option list.
19365
+ *
19366
+ * A filter needs this as a real option, not as the select's placeholder: a
19367
+ * placeholder reads as "nothing chosen yet", so the screen cannot tell whether
19368
+ * the column is filtered. The entry uses {@link ALL_OPTION_VALUE}.
19369
+ *
19370
+ * @param options - The list to extend. It is not mutated.
19371
+ * @param label - The entry's label. Defaults to `"Todos"`.
19372
+ * @returns A new array with the entry first.
19373
+ */
19374
+ export declare function withAllOption<T extends OptionLike>(options: readonly T[], label?: string): (T | OptionEntry)[];
19375
+
19376
+ /**
19377
+ * Prepend the empty entry a nullable field needs.
19378
+ *
19379
+ * Without it a `<select>` has no way to show "unset": a controlled
19380
+ * `value=""` with no matching option reads back as the **first option** (measured
19381
+ * in jsdom: `select.value === "a"`), so opening an edit form and saving it
19382
+ * writes a value nobody chose. `Select`'s `placeholder` does not cover this — its
19383
+ * entry is `disabled hidden`, so once something is picked the field can never be
19384
+ * cleared again. The entry's value is `""`, which is what an unanswered nullable field
19385
+ * submits.
19386
+ *
19387
+ * @param options - The list to extend. It is not mutated.
19388
+ * @param label - The entry's label. Defaults to `"— Não definido —"`.
19389
+ * @returns A new array with the entry first.
19390
+ */
19391
+ export declare function withEmptyOption<T extends OptionLike>(options: readonly T[], label?: string): (T | OptionEntry)[];
19392
+
18706
19393
  /**
18707
19394
  * A bound Zustand hook augmented with auto-generated per-field selector hooks
18708
19395
  * under `.use`. Reading a single field via `store.use.field()` subscribes the