@object-ui/components 17.6.0 → 17.7.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 (56) hide show
  1. package/CHANGELOG.md +8750 -0
  2. package/README.md +5 -3
  3. package/dist/custom/button-group.d.ts +1 -1
  4. package/dist/custom/combobox.d.ts +2 -4
  5. package/dist/custom/empty.d.ts +16 -4
  6. package/dist/custom/field.d.ts +1 -1
  7. package/dist/custom/filter-builder.d.ts +198 -66
  8. package/dist/custom/index.d.ts +0 -1
  9. package/dist/custom/item.d.ts +1 -1
  10. package/dist/custom/kbd.d.ts +1 -1
  11. package/dist/custom/mobile-dialog-content.d.ts +22 -1
  12. package/dist/custom/navigation-overlay.d.ts +105 -0
  13. package/dist/custom/refresh-indicator.d.ts +8 -2
  14. package/dist/custom/view-states.d.ts +74 -1
  15. package/dist/hooks/related-count-store.d.ts +26 -28
  16. package/dist/index.css +157 -363
  17. package/dist/index.d.ts +3 -3
  18. package/dist/index.js +6603 -5987
  19. package/dist/index.umd.cjs +10 -15
  20. package/dist/lib/close-label.d.ts +1 -2
  21. package/dist/lib/date-fns-locale.d.ts +41 -0
  22. package/dist/lib/lazy-icon.d.ts +32 -1
  23. package/dist/lib/lucide-record-icon-names.d.ts +30 -0
  24. package/dist/lib/utils.d.ts +113 -0
  25. package/dist/renderers/action/action-bar.d.ts +8 -36
  26. package/dist/renderers/action/action-button.d.ts +15 -4
  27. package/dist/renderers/action/action-group.d.ts +5 -4
  28. package/dist/renderers/action/action-icon.d.ts +12 -2
  29. package/dist/renderers/action/action-menu.d.ts +5 -2
  30. package/dist/renderers/action/auto-trigger.d.ts +19 -4
  31. package/dist/renderers/action/resolve-icon.d.ts +27 -2
  32. package/dist/renderers/action/static-params.d.ts +79 -0
  33. package/dist/renderers/action/visibility-gate.d.ts +11 -3
  34. package/dist/renderers/basic/elements.d.ts +3 -0
  35. package/dist/renderers/basic/html-elements.d.ts +7 -3
  36. package/dist/renderers/basic/readProps.d.ts +60 -0
  37. package/dist/renderers/complex/data-table.d.ts +31 -14
  38. package/dist/renderers/complex/dataTableBindDiagnostic.d.ts +187 -0
  39. package/dist/renderers/layout/box.d.ts +8 -0
  40. package/dist/renderers/layout/containers.d.ts +1 -10
  41. package/dist/renderers/layout/react-page.d.ts +4 -0
  42. package/dist/renderers/navigation/sidebar.d.ts +31 -0
  43. package/dist/ui/badge.d.ts +1 -1
  44. package/dist/ui/button.d.ts +1 -1
  45. package/dist/ui/calendar.d.ts +2 -1
  46. package/dist/ui/command.d.ts +7 -7
  47. package/dist/ui/index.d.ts +0 -1
  48. package/dist/ui/input-otp.d.ts +2 -2
  49. package/dist/ui/sheet.d.ts +1 -1
  50. package/dist/ui/sidebar.d.ts +2 -1
  51. package/package.json +13 -14
  52. package/dist/SchemaRenderer.d.ts +0 -4
  53. package/dist/custom/export-progress-dialog.d.ts +0 -43
  54. package/dist/hooks/use-export-job.d.ts +0 -34
  55. package/dist/ui/chart.d.ts +0 -56
  56. package/dist/ui/toast.d.ts +0 -22
package/README.md CHANGED
@@ -86,15 +86,16 @@ tree-shake that import away.
86
86
  ```tsx
87
87
  import { SchemaRenderer } from '@object-ui/react'
88
88
  import { initializeComponents } from '@object-ui/components'
89
+ import type { DeclaredNode } from '@object-ui/types'
89
90
 
90
91
  initializeComponents()
91
92
 
92
- const schema = {
93
+ const schema: DeclaredNode = {
93
94
  type: 'card',
94
95
  title: 'Welcome',
95
- body: {
96
+ children: {
96
97
  type: 'text',
97
- value: 'Hello from Object UI!'
98
+ content: 'Hello from Object UI!'
98
99
  }
99
100
  }
100
101
 
@@ -175,6 +176,7 @@ them draw the notification's `severity` icon unless it declares an `icon`
175
176
  override naming a real Lucide icon. See the
176
177
  [notifications guide](https://objectui.org/docs/guide/notifications).
177
178
 
179
+ <!-- doc-snippet: fragment — router-layout excerpt: `Outlet` is react-router's, supplied by the host application -->
178
180
  ```tsx
179
181
  import { NotificationBanners, NotificationAlerts } from '@object-ui/components';
180
182
 
@@ -1,7 +1,7 @@
1
1
  import { VariantProps } from 'class-variance-authority';
2
2
  import { Separator } from '../ui/separator.js';
3
3
  declare const buttonGroupVariants: (props?: ({
4
- orientation?: "horizontal" | "vertical" | null | undefined;
4
+ orientation?: "vertical" | "horizontal" | null | undefined;
5
5
  } & import('class-variance-authority/types').ClassProp) | undefined) => string;
6
6
  declare function ButtonGroup({ className, orientation, ...props }: React.ComponentProps<"div"> & VariantProps<typeof buttonGroupVariants>): import("react").JSX.Element;
7
7
  declare function ButtonGroupText({ className, asChild, ...props }: React.ComponentProps<"div"> & {
@@ -1,3 +1,4 @@
1
+ import { ComboboxOption } from '@object-ui/types/form';
1
2
  /**
2
3
  * ObjectUI
3
4
  * Copyright (c) 2024-present ObjectStack Inc.
@@ -6,10 +7,7 @@
6
7
  * LICENSE file in the root directory of this source tree.
7
8
  */
8
9
  import * as React from "react";
9
- export interface ComboboxOption {
10
- value: string;
11
- label: string;
12
- }
10
+ export type { ComboboxOption } from '@object-ui/types/form';
13
11
  /**
14
12
  * Beyond its own named props, the combobox accepts standard button attributes
15
13
  * (`id`, `aria-*`, `data-*`, handlers, …) and forwards them to the trigger —
@@ -6,15 +6,27 @@ declare const emptyMediaVariants: (props?: ({
6
6
  } & import('class-variance-authority/types').ClassProp) | undefined) => string;
7
7
  declare function EmptyMedia({ className, variant, ...props }: React.ComponentProps<"div"> & VariantProps<typeof emptyMediaVariants>): import("react").JSX.Element;
8
8
  declare function EmptyTitle({ className, ...props }: React.ComponentProps<"div">): import("react").JSX.Element;
9
- declare function EmptyDescription({ className, ...props }: React.ComponentProps<"p">): import("react").JSX.Element;
9
+ declare function EmptyDescription({ className, ...props }: React.ComponentProps<"div">): import("react").JSX.Element;
10
10
  declare function EmptyContent({ className, ...props }: React.ComponentProps<"div">): import("react").JSX.Element;
11
11
  /**
12
12
  * EmptyValue — universal inline placeholder for missing cell/field values.
13
13
  *
14
14
  * Use this anywhere a renderer would otherwise show "-" or "—" for a null,
15
- * undefined or empty value. It renders a muted, non-interactive en-dash that
16
- * does not inherit link/button colors from surrounding ancestors, so a missing
17
- * value never looks clickable.
15
+ * undefined or empty value. It renders a muted, non-interactive EM-dash (the
16
+ * `glyph` default below is U+2014, and every call site depends on that width)
17
+ * that does not inherit link/button colors from surrounding ancestors, so a
18
+ * missing value never looks clickable. This sentence said "en-dash" until
19
+ * objectui#8506: the word was wrong, never the code — do not "fix" the glyph
20
+ * to match a stale docblock.
21
+ *
22
+ * ⚠️ A caller that passes a `title` through `...props` must also pass
23
+ * `pointer-events-auto` in `className`. `pointer-events-none` below stops this
24
+ * span being a hit target, so a `title` on it never renders a tooltip — the
25
+ * hover falls through to whichever ancestor has one. The attribute stays in the
26
+ * DOM either way, which is exactly why nothing catches it: a test that reads
27
+ * `getAttribute('title')` is green over a tooltip that can never appear
28
+ * (objectui#8506, where `DetailSection` kept such a `title` and two landed pins
29
+ * navigate by it).
18
30
  */
19
31
  declare function EmptyValue({ className, glyph, ...props }: React.ComponentProps<"span"> & {
20
32
  glyph?: string;
@@ -25,7 +25,7 @@ export interface FieldProps extends React.HTMLAttributes<HTMLDivElement>, Varian
25
25
  * A labelled form-control wrapper: label, slotted control, description, error.
26
26
  *
27
27
  * Named `FieldContainer`, not `Field` (objectstack#4115): `@objectstack/spec/data`
28
- * exports `Field` — an object FIELD's metadata (type, reference_to, options,
28
+ * exports `Field` — an object FIELD's metadata (type, reference, options,
29
29
  * permissions, …) and its builder namespace — which has nothing to do with this
30
30
  * `<div>`. Two unrelated things under one name is the defect that issue exists
31
31
  * to remove; `@object-ui/app-shell` already renamed the same kind of collision
@@ -1,3 +1,4 @@
1
+ import { FilterBuilderCondition as AuthoredFilterBuilderCondition, FilterGroup as AuthoredFilterGroup } from '@object-ui/types';
1
2
  /**
2
3
  * ObjectUI
3
4
  * Copyright (c) 2024-present ObjectStack Inc.
@@ -16,21 +17,43 @@ import * as React from "react";
16
17
  * and `operator` here is the builder's own dropdown vocabulary
17
18
  * (`FILTER_BUILDER_OPERATORS`), not the spec's `$`-tokens.
18
19
  *
19
- * `@object-ui/types` already renamed its identical concept to
20
- * `FilterBuilderCondition` (objectui#3068); this is the same thing, so it takes
21
- * the same name rather than inventing a second dialect. Note the same name is
22
- * NOT the right answer everywhere: `@object-ui/app-shell`'s `FilterCondition`
23
- * really was the spec's ObjectQL AST and was re-exported (objectui#3169).
20
+ * DERIVED from `@object-ui/types`, never declared a second time (objectui#9306,
21
+ * which settles objectui#6349's two parked name-authority rows). That declaration is
22
+ * the one name authority for the row; this one takes every member from it
23
+ * (`id`, required since objectui#8415, and `field`) and restates exactly two,
24
+ * each a named extension:
25
+ *
26
+ * - `operator` — {@link FilterBuilderOperator}: the protocol's ids, which is
27
+ * what the authority declares, plus the opt-in `exists` / `notExists` the
28
+ * protocol has no member for (objectui#9559 ruling B keeps them opt-in and
29
+ * unfolded). It was `string`, which let a caller hand the builder an id no
30
+ * dropdown entry can hold.
31
+ * - `value` — REQUIRED and narrowed to the scalar-or-list union this builder
32
+ * edits, where the authority declares `value?: any`. Every exported helper
33
+ * below that takes a row's value (`reshapeFilterValue`, `retypeFilterValue`,
34
+ * `narrowFilterValueToOptions`, …) is typed on this member, so `any` here
35
+ * would switch their checking off in silence.
24
36
  */
25
- export interface FilterBuilderCondition {
26
- id: string;
27
- field: string;
28
- operator: string;
37
+ export interface FilterBuilderCondition extends Omit<AuthoredFilterBuilderCondition, "operator" | "value"> {
38
+ operator: FilterBuilderOperator;
29
39
  value: string | number | boolean | (string | number | boolean)[];
30
40
  }
31
- export interface FilterGroup {
32
- id: string;
33
- logic: "and" | "or";
41
+ /**
42
+ * The group the FilterBuilder edits: one `logic` over FLAT rows.
43
+ *
44
+ * DERIVED from `@object-ui/types` like the row above (objectui#9306). Only
45
+ * `conditions` is restated, to hold THIS package's row; `id` and `logic` are
46
+ * the authority's own. So `id` is OPTIONAL here, as it is there: nothing in
47
+ * this builder reads a group's `id`, `onChange` hands back whatever group the
48
+ * host passed (an id-less group comes back id-less), and only `EMPTY_GROUP`
49
+ * invents one. The `id: string` this package used to declare described a
50
+ * group the builder never guaranteed.
51
+ *
52
+ * `conditions` is flat because the builder is: it draws each entry as one row
53
+ * and has no control that creates a group. The authority retired nested
54
+ * sub-groups in the same change and its zod mirror refuses one by name.
55
+ */
56
+ export interface FilterGroup extends Omit<AuthoredFilterGroup, "conditions"> {
34
57
  conditions: FilterBuilderCondition[];
35
58
  }
36
59
  export interface FilterBuilderProps {
@@ -69,34 +92,34 @@ declare const defaultOperators: readonly [{
69
92
  readonly value: "equals";
70
93
  readonly label: "Equals";
71
94
  }, {
72
- readonly value: "notEquals";
95
+ readonly value: "not_equals";
73
96
  readonly label: "Does not equal";
74
97
  }, {
75
98
  readonly value: "contains";
76
99
  readonly label: "Contains";
77
100
  }, {
78
- readonly value: "containsCaseInsensitive";
101
+ readonly value: "icontains";
79
102
  readonly label: "Contains (ignore case)";
80
103
  }, {
81
- readonly value: "notContains";
104
+ readonly value: "not_contains";
82
105
  readonly label: "Does not contain";
83
106
  }, {
84
- readonly value: "isEmpty";
107
+ readonly value: "is_empty";
85
108
  readonly label: "Is empty";
86
109
  }, {
87
- readonly value: "isNotEmpty";
110
+ readonly value: "is_not_empty";
88
111
  readonly label: "Is not empty";
89
112
  }, {
90
- readonly value: "greaterThan";
113
+ readonly value: "greater_than";
91
114
  readonly label: "Greater than";
92
115
  }, {
93
- readonly value: "lessThan";
116
+ readonly value: "less_than";
94
117
  readonly label: "Less than";
95
118
  }, {
96
- readonly value: "greaterOrEqual";
119
+ readonly value: "greater_than_or_equal";
97
120
  readonly label: "Greater than or equal";
98
121
  }, {
99
- readonly value: "lessOrEqual";
122
+ readonly value: "less_than_or_equal";
100
123
  readonly label: "Less than or equal";
101
124
  }, {
102
125
  readonly value: "before";
@@ -111,19 +134,19 @@ declare const defaultOperators: readonly [{
111
134
  readonly value: "in";
112
135
  readonly label: "In";
113
136
  }, {
114
- readonly value: "notIn";
137
+ readonly value: "not_in";
115
138
  readonly label: "Not in";
116
139
  }, {
117
- readonly value: "startsWith";
140
+ readonly value: "starts_with";
118
141
  readonly label: "Starts with";
119
142
  }, {
120
- readonly value: "endsWith";
143
+ readonly value: "ends_with";
121
144
  readonly label: "Ends with";
122
145
  }, {
123
- readonly value: "isNull";
146
+ readonly value: "is_null";
124
147
  readonly label: "Is null";
125
148
  }, {
126
- readonly value: "isNotNull";
149
+ readonly value: "is_not_null";
127
150
  readonly label: "Is not null";
128
151
  }, {
129
152
  readonly value: "exists";
@@ -137,30 +160,79 @@ declare const defaultOperators: readonly [{
137
160
  * hold, derived from the operators it actually renders. Includes the opt-in
138
161
  * ids: they are drawable, just not offered unconditionally.
139
162
  *
140
- * Exported because several translation tables map an external vocabulary
141
- * (the spec's `VIEW_FILTER_OPERATORS`, Mongo `$`-tokens) *onto* this one, and
142
- * a value that is not in this list produces a condition row whose operator
143
- * select has nothing to select. Those tables assert against this export
144
- * rather than restating it, so adding an operator here is the only edit
145
- * needed to widen them.
163
+ * Since objectui#9306 these ARE the protocol's ids — the twenty members of the
164
+ * spec's `VIEW_FILTER_OPERATORS`, plus the opt-in `exists` / `notExists` the
165
+ * protocol has no member for — so a table that maps the spec's vocabulary onto
166
+ * this one is an identity over the twenty. It is still exported because those
167
+ * tables, and the parity pins over them, assert against this list rather than
168
+ * restating it: a value that is not in it produces a condition row whose
169
+ * operator select has nothing to select.
170
+ */
171
+ export declare const FILTER_BUILDER_OPERATORS: ("in" | "starts_with" | "ends_with" | "contains" | "equals" | "not_equals" | "greater_than" | "greater_than_or_equal" | "less_than" | "less_than_or_equal" | "before" | "after" | "not_in" | "not_contains" | "icontains" | "between" | "is_null" | "is_not_null" | "is_empty" | "is_not_empty" | "exists" | "notExists")[];
172
+ /**
173
+ * An operator id the FilterBuilder can render: the spec's `ViewFilterOperator`
174
+ * plus the two opt-in existence ids (objectui#9306). A type-level pin in
175
+ * `filter-builder-protocol-ids-9306.test.tsx` holds it EQUAL to that union, so
176
+ * a protocol member added upstream fails to compile there until the dropdown
177
+ * offers it.
146
178
  */
147
- export declare const FILTER_BUILDER_OPERATORS: ("endsWith" | "startsWith" | "in" | "after" | "equals" | "notEquals" | "contains" | "containsCaseInsensitive" | "notContains" | "isEmpty" | "isNotEmpty" | "greaterThan" | "lessThan" | "greaterOrEqual" | "lessOrEqual" | "before" | "between" | "notIn" | "isNull" | "isNotNull" | "exists" | "notExists")[];
148
- /** An operator id the FilterBuilder can render. */
149
179
  export type FilterBuilderOperator = (typeof defaultOperators)[number]['value'];
150
180
  /**
151
- * Operator ids for which this builder renders NO value input — so "no value"
152
- * is the row's FINISHED state, not an unfinished one (objectui#4744).
181
+ * The spelling a stored operator is READ as — the protocol id for every
182
+ * spelling the spec or this builder ever wrote (objectui#9306).
183
+ *
184
+ * The ruling this executes: camelCase is the deprecated alias form
185
+ * (objectui#7993), accepted on read and rewritten on write. So a filter stored
186
+ * as `{ operator: 'greaterOrEqual' }` keeps loading — it is folded here, through
187
+ * the spec's own `normalizeFilterOperator`, onto `greater_than_or_equal` — and
188
+ * the builder holds and emits that canonical id from then on, so the next write
189
+ * the author makes stores it. A lossless conversion on the read side, ⛔ not a
190
+ * second vocabulary: nothing this builder EMITS is ever a camelCase id.
191
+ *
192
+ * An id nothing folds (`exists`, `notExists`, a spelling from no vocabulary)
193
+ * comes back unchanged, exactly as the spec's normalizer returns it: inventing
194
+ * a mapping for a word the protocol does not contain would be a claim, not a
195
+ * repair.
196
+ *
197
+ * ⚠️ One guard on the spec's answer, and only because the builder now STORES
198
+ * what this returns (before objectui#9306 the fold was used for comparison
199
+ * only). `normalizeFilterOperator` is declared to return a string, but it
200
+ * looks the spelling up on a plain object, so an `Object.prototype` key comes
201
+ * back as whatever the prototype holds — `'constructor'` returns the `Object`
202
+ * function, measured on `@objectstack/spec` 17.4.0. Held in a row and written
203
+ * back, that would drop the row's operator on serialisation. A non-string
204
+ * answer is therefore treated as "nothing folds" and the spelling is kept;
205
+ * the defect itself belongs to the spec and is reported there, not patched
206
+ * around here in any other way.
207
+ */
208
+ export declare function normalizeFilterBuilderOperator(operator: string): string;
209
+ /**
210
+ * Operator ids — this dropdown's own ids, which are the protocol's canonical
211
+ * spellings since objectui#9306 — for which this builder renders no value
212
+ * input, so "no value" is the row's FINISHED state, not an unfinished one
213
+ * (objectui#4744).
153
214
  *
154
215
  * This is the source of truth for that distinction, and it lives here because
155
- * this component is the thing that decides it: `needsValueInput` below is
156
- * defined as the complement of this set, so the two cannot say different
157
- * things. Every consumer that has to tell a complete value-less row from a
158
- * half-filled one reads it FROM here rather than restating it:
216
+ * this component is the thing that decides it. But membership here is not the
217
+ * whole question: `needsValueInput` below asks it of the row's spelling AFTER
218
+ * {@link normalizeFilterBuilderOperator} (objectui#9302), so the gate answers
219
+ * "no value" for spellings that are NOT members — the deprecated camelCase ids
220
+ * a stored filter may still carry, and the alias rows the spec publishes for
221
+ * those same operators. A consumer holding a spelling that did not come from
222
+ * this dropdown must therefore fold it before asking, and fold this set's own
223
+ * members through the same normalizer too — the spec's `normalizeFilterOperator`
224
+ * is the identity on them, so that fold costs nothing and keeps the consumer
225
+ * correct whichever vocabulary this set is written in. How much wider the
226
+ * gate's preimage is is NOT restated here: the pin
227
+ * `filter-builder-valueless-canonical-spelling-9302.test.tsx` walks the
228
+ * spec's two published tables and names every row it measures, which is the
229
+ * only form of that answer that moves when those tables do (AGENTS.md #9).
230
+ *
231
+ * Every consumer that has to tell a complete value-less row from a half-filled
232
+ * one reads the membership FROM here rather than restating it:
159
233
  *
160
234
  * - `plugin-list`'s `convertFilterGroupToAST` — what the live grid QUERIES;
161
- * - `app-shell`'s `foldFilterGroupToSpecRules` — what a saved view PERSISTS
162
- * (its `VALUELESS_FILTER_OPERATORS` is this set plus the canonical spec
163
- * spellings, which only that layer sees).
235
+ * - `app-shell`'s `foldFilterGroupToSpecRules` — what a saved view PERSISTS.
164
236
  *
165
237
  * Placement is forced by the dependency graph — `app-shell` depends on
166
238
  * `plugin-list` depends on this package, so this is the only module all three
@@ -168,11 +240,12 @@ export type FilterBuilderOperator = (typeof defaultOperators)[number]['value'];
168
240
  * states is a fact about what this dropdown draws.
169
241
  *
170
242
  * Why it exists at all: each consumer used to keep its own copy, and the
171
- * live-grid copy listed only `isEmpty`/`isNotEmpty`. A fresh row is seeded
172
- * `{ operator: 'equals', value: '' }` and the operator dropdown preserves
173
- * `value`, so picking **Is null** as the first action left `value: ''` — the
174
- * grid read that as an unfinished row, dropped it, and applied NO filter at
175
- * all while the panel showed one. Silent, and every record came back.
243
+ * live-grid copy listed only the two empty-string operators. A fresh row is
244
+ * seeded `{ operator: 'equals', value: '' }` and the operator dropdown
245
+ * preserves `value`, so picking **Is null** as the first action left
246
+ * `value: ''` — the grid read that as an unfinished row, dropped it, and
247
+ * applied NO filter at all while the panel showed one. Silent, and every
248
+ * record came back.
176
249
  *
177
250
  * `exists` / `notExists` stay listed even though `OPT_IN_OPERATORS` withholds
178
251
  * them from most consumers (objectui#4736): the builder still draws them
@@ -193,13 +266,14 @@ export declare const VALUELESS_FILTER_BUILDER_OPERATORS: ReadonlySet<string>;
193
266
  */
194
267
  export type FilterValueArity = "scalar" | "list" | "pair";
195
268
  /**
196
- * Which value shape `operator` takes, in EITHER dialect.
269
+ * Which value shape `operator` takes, whichever spelling it arrives in.
197
270
  *
198
- * The argument is folded through the spec's own `normalizeFilterOperator`
199
- * first, so the builder's camelCase dropdown ids (`notIn`) and the canonical
200
- * spellings a stored rule carries (`not_in`) land on the same answer. That
201
- * fold is the whole point: one vocabulary, declared upstream, consulted here —
202
- * not a second local dialect that has to be kept in sync by hand.
271
+ * The argument is folded through {@link normalizeFilterBuilderOperator} — the
272
+ * spec's own `normalizeFilterOperator` plus the builder's one unfolded legacy
273
+ * id — first, so the canonical id this dropdown emits (`not_in`) and the
274
+ * deprecated alias a stored filter may still carry (`notIn`) land on the same
275
+ * answer. That fold is the whole point: one vocabulary, declared upstream,
276
+ * consulted here — not a second local dialect kept in sync by hand.
203
277
  *
204
278
  * @internal exported for tests
205
279
  */
@@ -230,7 +304,7 @@ export declare function reshapeFilterValue(value: FilterBuilderCondition["value"
230
304
  * NEW field offers (objectui#4768).
231
305
  *
232
306
  * Each field type has its own operator bucket, and the buckets are not nested:
233
- * a `select` column offers `in` / `notIn`, a `text` column does not. Changing
307
+ * a `select` column offers `in` / `not_in`, a `text` column does not. Changing
234
308
  * the field used to write `{ field }` alone, so the row's operator survived
235
309
  * into a bucket that no longer contains it — `in` on a text column. The Radix
236
310
  * trigger then had nothing to render (its `SelectValue` matches against the
@@ -243,22 +317,80 @@ export declare function reshapeFilterValue(value: FilterBuilderCondition["value"
243
317
  * entry (`equals` for every bucket this builder draws), and the caller then
244
318
  * re-shapes the value for that operator's family.
245
319
  *
246
- * Membership is decided CANONICALLY, through the spec's own
247
- * `normalizeFilterOperator` — the same fold `filterValueArity` uses. A stored
248
- * rule can reach this builder spelled `not_in` while the dropdown lists the
249
- * alias `notIn`; those are one operator, so a select→lookup switch must not
250
- * silently rewrite the author's operator to `equals` just because the two
251
- * spellings differ. The fold is safe to compare through because it is
252
- * INJECTIVE over this builder's vocabulary — all 22 ids in `defaultOperators`
253
- * normalize to 22 distinct canonical operators, pinned in
254
- * `filter-builder-field-switch-operator.test.tsx` — so no two OFFERED
255
- * operators can ever collapse onto one another.
320
+ * Membership is decided CANONICALLY, through
321
+ * {@link normalizeFilterBuilderOperator} — the same fold `filterValueArity`
322
+ * uses. A caller can hand this helper a row spelled with the deprecated alias
323
+ * `notIn` while the dropdown lists the protocol id `not_in`; those are one
324
+ * operator, so a select→lookup switch must not silently rewrite the author's
325
+ * operator to `equals` just because the two spellings differ. The fold is safe
326
+ * to compare through because it is INJECTIVE over this builder's vocabulary —
327
+ * all 22 ids in `defaultOperators` normalize to 22 distinct canonical
328
+ * operators, pinned in `filter-builder-field-switch-operator.test.tsx` — so no
329
+ * two OFFERED operators can ever collapse onto one another.
256
330
  *
257
331
  * @internal exported for tests
258
332
  */
259
333
  export declare function reconcileOperatorForField(operator: string, offeredOperators: ReadonlyArray<{
260
334
  value: string;
261
335
  }>): string;
336
+ /**
337
+ * The offered id the operator trigger COMPARES against — the last site in this
338
+ * component that read the operator literally instead of through the spec's
339
+ * fold (objectui#7561).
340
+ *
341
+ * Radix matches `SelectValue` against the `SelectItem`s actually MOUNTED, and
342
+ * the mounted ids are this builder's own vocabulary — the protocol's canonical
343
+ * ids since objectui#9306 (they were camelCase when this helper was written). A
344
+ * row can hold the operator under another spelling of the SAME operator and
345
+ * still be perfectly valid:
346
+ *
347
+ * - a deprecated camelCase alias such as `greaterThan`, which a filter saved
348
+ * before objectui#9306 still carries;
349
+ * - the spec's alias table `gt` / `lt` / `eq`, which three schema-catalog
350
+ * entries author today.
351
+ *
352
+ * When this helper was written the mounted id was `greaterThan` and the stored
353
+ * spelling `greater_than`; neither matched the other literally, so the trigger
354
+ * drew BLANK over a row that filtered correctly — the user's own operator,
355
+ * invisible and unreachable.
356
+ * Everywhere the operator's MEANING matters this component already folds first
357
+ * ({@link filterValueArity}, {@link reconcileOperatorForField}); this was the
358
+ * one place it did not, and that omission — not the vocabulary divergence — is
359
+ * what produced the blank.
360
+ *
361
+ * So the comparison is made canonically and the MOUNTED spelling is handed to
362
+ * the trigger. Three things this deliberately does NOT do, each of them a
363
+ * separate ruling (objectui#7561):
364
+ *
365
+ * 1. it does not rewrite `condition.operator` — the row keeps the spelling it
366
+ * arrived with, exactly as {@link reconcileOperatorForField} keeps it, and
367
+ * nothing is written back on render. (Since objectui#9306 the BUILDER
368
+ * folds a row's spelling once, at its read boundary — see
369
+ * {@link normalizeFilterBuilderOperator} — so the rows it renders already
370
+ * hold protocol ids. This helper still folds both sides, because it is
371
+ * exported and its caller's rows need not have come through that
372
+ * boundary.)
373
+ * 2. it does not mount a new `SelectItem`, so the vocabulary the dropdown
374
+ * EMITS is byte-identical — contrast the value select above, where
375
+ * objectui#4874 ruling C mounts the outside-options value as its own
376
+ * option. That answer is right there and wrong here: the value's domain is
377
+ * the author's data, while the operator's domain is a declared vocabulary,
378
+ * and mounting a foreign spelling would admit a second one;
379
+ * 3. it does not widen what any schema ACCEPTS.
380
+ *
381
+ * Safe to compare through because the fold is INJECTIVE over this builder's
382
+ * vocabulary — all 22 ids in `defaultOperators` normalize to 22 distinct
383
+ * canonical operators, pinned in `filter-builder-field-switch-operator.test.tsx`
384
+ * — so no two OFFERED operators can collapse onto one another and the match is
385
+ * unambiguous. An operator no offered id folds onto falls through to the row's
386
+ * own spelling and still draws blank: inventing a label for a word this
387
+ * vocabulary does not contain would be a claim, not a repair.
388
+ *
389
+ * @internal exported for tests
390
+ */
391
+ export declare function mountedOperatorValue(operator: string, offeredOperators: ReadonlyArray<{
392
+ value: string;
393
+ }>): string;
262
394
  /**
263
395
  * Re-TYPE a row's `value` for the field it is being changed TO — the type
264
396
  * question, standing beside {@link reshapeFilterValue}'s shape question
@@ -285,7 +417,7 @@ export declare function reconcileOperatorForField(operator: string, offeredOpera
285
417
  * - `scalar` — converted, or `""`.
286
418
  * - `list` — converted ENTRY BY ENTRY, keeping the ones that carry:
287
419
  * `["42", "acme"]` → `[42]`, and `[]` when none do. Reachable today only
288
- * between the two buckets that offer `in`/`notIn` (`select` ↔ `lookup`),
420
+ * between the two buckets that offer `in`/`not_in` (`select` ↔ `lookup`),
289
421
  * which are both text-family, so the conversion is the identity there; it
290
422
  * is written for the family the operator lands in rather than for today's
291
423
  * buckets, and pinned directly on this helper.
@@ -25,4 +25,3 @@ export * from './RecordTitleChip.js';
25
25
  export * from './refresh-indicator.js';
26
26
  export * from './view-states.js';
27
27
  export * from './mobile-dialog-content.js';
28
- export * from './export-progress-dialog.js';
@@ -18,7 +18,7 @@ declare function Item({ className, variant, size, asChild, ...props }: React.Com
18
18
  asChild?: boolean;
19
19
  }): React.JSX.Element;
20
20
  declare const itemMediaVariants: (props?: ({
21
- variant?: "image" | "default" | "icon" | null | undefined;
21
+ variant?: "default" | "icon" | "image" | null | undefined;
22
22
  } & import('class-variance-authority/types').ClassProp) | undefined) => string;
23
23
  declare function ItemMedia({ className, variant, ...props }: React.ComponentProps<"div"> & VariantProps<typeof itemMediaVariants>): React.JSX.Element;
24
24
  declare function ItemContent({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
@@ -6,5 +6,5 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  */
8
8
  declare function Kbd({ className, ...props }: React.ComponentProps<"kbd">): import("react").JSX.Element;
9
- declare function KbdGroup({ className, ...props }: React.ComponentProps<"div">): import("react").JSX.Element;
9
+ declare function KbdGroup({ className, ...props }: React.ComponentProps<"kbd">): import("react").JSX.Element;
10
10
  export { Kbd, KbdGroup };
@@ -43,5 +43,26 @@ type InteractOutsideHandler = NonNullable<React.ComponentPropsWithoutRef<typeof
43
43
  * plain backdrop click with no flyout open.
44
44
  */
45
45
  export declare function usePopperAwareInteractOutside(onInteractOutside?: InteractOutsideHandler): InteractOutsideHandler;
46
- export declare const MobileDialogContent: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
46
+ export type MobileDialogContentProps = React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
47
+ /**
48
+ * Whether the dialog draws its own close (X) button. Defaults to `true`;
49
+ * `false` leaves it out of the DOM entirely (objectui#11061), the same
50
+ * switch upstream Shadcn's `DialogContent` names `showCloseButton`. The
51
+ * dialog stays dismissable without it: Escape and a backdrop click still
52
+ * reach the `Dialog`'s `onOpenChange(false)` through Radix, so a caller that
53
+ * hides the X only has to keep whatever explicit way out its body offers.
54
+ */
55
+ showCloseButton?: boolean;
56
+ };
57
+ export declare const MobileDialogContent: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
58
+ /**
59
+ * Whether the dialog draws its own close (X) button. Defaults to `true`;
60
+ * `false` leaves it out of the DOM entirely (objectui#11061), the same
61
+ * switch upstream Shadcn's `DialogContent` names `showCloseButton`. The
62
+ * dialog stays dismissable without it: Escape and a backdrop click still
63
+ * reach the `Dialog`'s `onOpenChange(false)` through Radix, so a caller that
64
+ * hides the X only has to keep whatever explicit way out its body offers.
65
+ */
66
+ showCloseButton?: boolean;
67
+ } & React.RefAttributes<HTMLDivElement>>;
47
68
  export {};