@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.
- package/CHANGELOG.md +8750 -0
- package/README.md +5 -3
- package/dist/custom/button-group.d.ts +1 -1
- package/dist/custom/combobox.d.ts +2 -4
- package/dist/custom/empty.d.ts +16 -4
- package/dist/custom/field.d.ts +1 -1
- package/dist/custom/filter-builder.d.ts +198 -66
- package/dist/custom/index.d.ts +0 -1
- package/dist/custom/item.d.ts +1 -1
- package/dist/custom/kbd.d.ts +1 -1
- package/dist/custom/mobile-dialog-content.d.ts +22 -1
- package/dist/custom/navigation-overlay.d.ts +105 -0
- package/dist/custom/refresh-indicator.d.ts +8 -2
- package/dist/custom/view-states.d.ts +74 -1
- package/dist/hooks/related-count-store.d.ts +26 -28
- package/dist/index.css +157 -363
- package/dist/index.d.ts +3 -3
- package/dist/index.js +6603 -5987
- package/dist/index.umd.cjs +10 -15
- package/dist/lib/close-label.d.ts +1 -2
- package/dist/lib/date-fns-locale.d.ts +41 -0
- package/dist/lib/lazy-icon.d.ts +32 -1
- package/dist/lib/lucide-record-icon-names.d.ts +30 -0
- package/dist/lib/utils.d.ts +113 -0
- package/dist/renderers/action/action-bar.d.ts +8 -36
- package/dist/renderers/action/action-button.d.ts +15 -4
- package/dist/renderers/action/action-group.d.ts +5 -4
- package/dist/renderers/action/action-icon.d.ts +12 -2
- package/dist/renderers/action/action-menu.d.ts +5 -2
- package/dist/renderers/action/auto-trigger.d.ts +19 -4
- package/dist/renderers/action/resolve-icon.d.ts +27 -2
- package/dist/renderers/action/static-params.d.ts +79 -0
- package/dist/renderers/action/visibility-gate.d.ts +11 -3
- package/dist/renderers/basic/elements.d.ts +3 -0
- package/dist/renderers/basic/html-elements.d.ts +7 -3
- package/dist/renderers/basic/readProps.d.ts +60 -0
- package/dist/renderers/complex/data-table.d.ts +31 -14
- package/dist/renderers/complex/dataTableBindDiagnostic.d.ts +187 -0
- package/dist/renderers/layout/box.d.ts +8 -0
- package/dist/renderers/layout/containers.d.ts +1 -10
- package/dist/renderers/layout/react-page.d.ts +4 -0
- package/dist/renderers/navigation/sidebar.d.ts +31 -0
- package/dist/ui/badge.d.ts +1 -1
- package/dist/ui/button.d.ts +1 -1
- package/dist/ui/calendar.d.ts +2 -1
- package/dist/ui/command.d.ts +7 -7
- package/dist/ui/index.d.ts +0 -1
- package/dist/ui/input-otp.d.ts +2 -2
- package/dist/ui/sheet.d.ts +1 -1
- package/dist/ui/sidebar.d.ts +2 -1
- package/package.json +13 -14
- package/dist/SchemaRenderer.d.ts +0 -4
- package/dist/custom/export-progress-dialog.d.ts +0 -43
- package/dist/hooks/use-export-job.d.ts +0 -34
- package/dist/ui/chart.d.ts +0 -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
|
-
|
|
96
|
+
children: {
|
|
96
97
|
type: 'text',
|
|
97
|
-
|
|
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?: "
|
|
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
|
|
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 —
|
package/dist/custom/empty.d.ts
CHANGED
|
@@ -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<"
|
|
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
|
|
16
|
-
*
|
|
17
|
-
*
|
|
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;
|
package/dist/custom/field.d.ts
CHANGED
|
@@ -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,
|
|
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
|
|
20
|
-
*
|
|
21
|
-
* the
|
|
22
|
-
*
|
|
23
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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: "
|
|
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: "
|
|
101
|
+
readonly value: "icontains";
|
|
79
102
|
readonly label: "Contains (ignore case)";
|
|
80
103
|
}, {
|
|
81
|
-
readonly value: "
|
|
104
|
+
readonly value: "not_contains";
|
|
82
105
|
readonly label: "Does not contain";
|
|
83
106
|
}, {
|
|
84
|
-
readonly value: "
|
|
107
|
+
readonly value: "is_empty";
|
|
85
108
|
readonly label: "Is empty";
|
|
86
109
|
}, {
|
|
87
|
-
readonly value: "
|
|
110
|
+
readonly value: "is_not_empty";
|
|
88
111
|
readonly label: "Is not empty";
|
|
89
112
|
}, {
|
|
90
|
-
readonly value: "
|
|
113
|
+
readonly value: "greater_than";
|
|
91
114
|
readonly label: "Greater than";
|
|
92
115
|
}, {
|
|
93
|
-
readonly value: "
|
|
116
|
+
readonly value: "less_than";
|
|
94
117
|
readonly label: "Less than";
|
|
95
118
|
}, {
|
|
96
|
-
readonly value: "
|
|
119
|
+
readonly value: "greater_than_or_equal";
|
|
97
120
|
readonly label: "Greater than or equal";
|
|
98
121
|
}, {
|
|
99
|
-
readonly value: "
|
|
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: "
|
|
137
|
+
readonly value: "not_in";
|
|
115
138
|
readonly label: "Not in";
|
|
116
139
|
}, {
|
|
117
|
-
readonly value: "
|
|
140
|
+
readonly value: "starts_with";
|
|
118
141
|
readonly label: "Starts with";
|
|
119
142
|
}, {
|
|
120
|
-
readonly value: "
|
|
143
|
+
readonly value: "ends_with";
|
|
121
144
|
readonly label: "Ends with";
|
|
122
145
|
}, {
|
|
123
|
-
readonly value: "
|
|
146
|
+
readonly value: "is_null";
|
|
124
147
|
readonly label: "Is null";
|
|
125
148
|
}, {
|
|
126
|
-
readonly value: "
|
|
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
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
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
|
-
*
|
|
152
|
-
*
|
|
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
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
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
|
|
172
|
-
* `{ operator: 'equals', value: '' }` and the operator dropdown
|
|
173
|
-
* `value`, so picking **Is null** as the first action left
|
|
174
|
-
* grid read that as an unfinished row, dropped it, and
|
|
175
|
-
* all while the panel showed one. Silent, and every
|
|
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,
|
|
269
|
+
* Which value shape `operator` takes, whichever spelling it arrives in.
|
|
197
270
|
*
|
|
198
|
-
* The argument is folded through
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
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` / `
|
|
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
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
* spellings differ. The fold is safe
|
|
252
|
-
* INJECTIVE over this builder's vocabulary —
|
|
253
|
-
* normalize to 22 distinct canonical
|
|
254
|
-
* `filter-builder-field-switch-operator.test.tsx` — so no
|
|
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`/`
|
|
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.
|
package/dist/custom/index.d.ts
CHANGED
package/dist/custom/item.d.ts
CHANGED
|
@@ -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?: "
|
|
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;
|
package/dist/custom/kbd.d.ts
CHANGED
|
@@ -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<"
|
|
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
|
|
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 {};
|