@object-ui/plugin-list 16.1.0 → 17.1.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 +688 -0
- package/README.md +29 -6
- package/dist/ListView.d.ts +53 -1
- package/dist/ListView.d.ts.map +1 -1
- package/dist/ObjectGallery.d.ts +0 -1
- package/dist/ObjectGallery.d.ts.map +1 -1
- package/dist/UserFilters.d.ts +17 -0
- package/dist/UserFilters.d.ts.map +1 -1
- package/dist/index.js +1349 -1142
- package/dist/index.umd.cjs +3 -3
- package/package.json +18 -17
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,693 @@
|
|
|
1
1
|
# @object-ui/plugin-list
|
|
2
2
|
|
|
3
|
+
## 17.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- d21794c: fix(list,i18n): a 400 from the server no longer reads as "check your connection"
|
|
8
|
+
|
|
9
|
+
`classifyLoadError` was written because a 403 rendered the same
|
|
10
|
+
"check your connection and try again" panel as a genuine outage — its own doc
|
|
11
|
+
comment says users "were told to debug their network when the server had
|
|
12
|
+
(correctly) denied them access." It made that distinction for 401 and 403 and
|
|
13
|
+
then sent **everything else**, including 4xx, to the network branch.
|
|
14
|
+
|
|
15
|
+
A **400** is the server saying it understood the request and will never accept
|
|
16
|
+
it. Retrying resends the identical bad request, so "check your connection and
|
|
17
|
+
try again" is advice that cannot work — the same mistake the function exists to
|
|
18
|
+
prevent, one status code over.
|
|
19
|
+
|
|
20
|
+
This became reachable from ordinary stored metadata with
|
|
21
|
+
objectstack-ai/objectstack#4121: a `$filter` array that is not a filter AST is
|
|
22
|
+
now rejected at the protocol with `400 INVALID_FILTER`, where it previously
|
|
23
|
+
reached a driver (and, for a lone `['and']`, silently returned every row). A
|
|
24
|
+
view saved with such a filter now answers 400 on every load.
|
|
25
|
+
|
|
26
|
+
Adds a fourth classification, `rejected`, for `status === 400` and for the
|
|
27
|
+
server's 400-class codes (`INVALID_FILTER`, `UNSUPPORTED_QUERY_PARAM`,
|
|
28
|
+
`INVALID_QUERY`). Its copy points at the filter rather than the network, and
|
|
29
|
+
says who can fix it when the view is saved that way. 403/401 keep priority, so a
|
|
30
|
+
permission denial can never read as a bad request — pinned by a test.
|
|
31
|
+
|
|
32
|
+
The two new strings are added to **all ten locale packs**, not just `en`: the
|
|
33
|
+
neighbouring panels are translated, and `fallbackLng: 'en'` would have rendered
|
|
34
|
+
this one in English beside them. The full-parity gate
|
|
35
|
+
(`all-locales-key-parity.test.ts`) caught the pack I missed.
|
|
36
|
+
|
|
37
|
+
Verified: 5 new tests — numeric status, error code without a status, a status
|
|
38
|
+
embedded in the message text, and the 403/401 ordering guard. Reverting the
|
|
39
|
+
branch fails four of them. `plugin-list` + `i18n`: **403 tests across 29 files**,
|
|
40
|
+
green.
|
|
41
|
+
|
|
42
|
+
- c4db402: refactor(views): ListView's `aria` and `sharing` are the spec sub-shapes (#2890 scope A step 5)
|
|
43
|
+
|
|
44
|
+
Last rename batch in the ListView vocabulary migration.
|
|
45
|
+
|
|
46
|
+
**`aria`** is now the spec's `AriaPropsSchema`: `label` → `ariaLabel`,
|
|
47
|
+
`describedBy` → `ariaDescribedBy`, folded at the ListView boundary like every
|
|
48
|
+
other legacy key. Two things fall out of adopting the spec shape:
|
|
49
|
+
|
|
50
|
+
- `role` becomes authorable. The list region hardcoded `role="region"`; it now
|
|
51
|
+
reads `aria.role` and falls back to `region`.
|
|
52
|
+
- `aria.live` stays as a documented local extension — it has no spec
|
|
53
|
+
counterpart, and dropping it would silently disable a shipped capability.
|
|
54
|
+
Promote it rather than growing that extension.
|
|
55
|
+
|
|
56
|
+
**`sharing`** is now the spec's `ViewSharingSchema` (`{ type, lockedBy }`),
|
|
57
|
+
imported by reference — the local four-key object is gone. The legacy pair folds
|
|
58
|
+
in: `visibility` collapses onto the two ownership kinds the spec models (only
|
|
59
|
+
`private` is `personal`; `team` / `organization` / `public` are all
|
|
60
|
+
`collaborative`), and a bare `enabled: true` maps to `personal`, which is the
|
|
61
|
+
badge the user already saw (the old title fell back to `'private'`).
|
|
62
|
+
|
|
63
|
+
_Visible change_: the share badge's tooltip shows the spec ownership type, so a
|
|
64
|
+
view authored with `visibility: 'team'` reads "Sharing: collaborative" instead
|
|
65
|
+
of "Sharing: team". The four-value audience has no spec home and nothing but
|
|
66
|
+
that tooltip consumed it; keeping a second audience enum alive would re-open the
|
|
67
|
+
fork this issue closes.
|
|
68
|
+
|
|
69
|
+
Also fixes the **spec bridge**, which was doing the opposite of its job: given a
|
|
70
|
+
spec-shaped `sharing`, `transformListView` _downgraded_ it — inventing a legacy
|
|
71
|
+
`visibility` audience and an `enabled` flag that the renderer then had to fold
|
|
72
|
+
back. Both sides speak `ViewSharing` now, so it passes through.
|
|
73
|
+
|
|
74
|
+
`conditionalFormatting` and `exportOptions` are deliberately **not** folded.
|
|
75
|
+
Both objectui shapes are supersets carrying capability the spec cannot express —
|
|
76
|
+
the `{ field, operator, value }` rule form, and `maxRecords` / `includeHeaders`
|
|
77
|
+
/ `fileNamePrefix`. Folding them onto the narrower spec shapes would delete
|
|
78
|
+
working features; they want promotion upstream, not a rename.
|
|
79
|
+
|
|
80
|
+
- 5319bf1: feat(views): the list toolbar speaks one vocabulary — `userActions` (#2890 scope A step 3)
|
|
81
|
+
|
|
82
|
+
The seven bare `show*` toolbar flags fold into the spec's `userActions`, and the
|
|
83
|
+
renderer reads nothing else. `showDescription` folds into
|
|
84
|
+
`appearance.showDescription` at the same boundary.
|
|
85
|
+
|
|
86
|
+
| legacy | canonical |
|
|
87
|
+
| :-------------------------------------------------------- | :-------------------------------------------------------- |
|
|
88
|
+
| `showSearch` / `showSort` / `showFilters` / `showDensity` | `userActions.search` / `.sort` / `.filter` / `.rowHeight` |
|
|
89
|
+
| `showGroup` / `showHideFields` / `showColor` | `userActions.group` / `.hideFields` / `.rowColor` |
|
|
90
|
+
| `showDescription` | `appearance.showDescription` |
|
|
91
|
+
|
|
92
|
+
**The last three are new keys, and they close a capability hole rather than just
|
|
93
|
+
renaming one.** `@objectstack/spec`'s `UserActionsConfigSchema` documents itself
|
|
94
|
+
as "which interactive actions are available to users in the view toolbar — each
|
|
95
|
+
boolean toggles the corresponding toolbar element on/off", and already carries
|
|
96
|
+
`rowHeight` (objectui's `showDensity` under its spec name). Grouping, column
|
|
97
|
+
visibility and row coloring are the same kind of toggle: the spec models all
|
|
98
|
+
three as _configuration_ (`grouping`, `hiddenFields`, `rowColor`) but has no
|
|
99
|
+
"may the user change it" switch for any of them.
|
|
100
|
+
|
|
101
|
+
The consequence was visible in the product. With no `userActions` key to read,
|
|
102
|
+
the two list surfaces **hardcoded opposite policies**: `InterfaceListPage` (the
|
|
103
|
+
author-curated interface page) pinned all three OFF, `ObjectDataPage` pinned two
|
|
104
|
+
ON — and an interface-page author could not turn grouping back on for end users
|
|
105
|
+
at all. Both surfaces now express their policy as `userActions` defaults, which
|
|
106
|
+
an author can override.
|
|
107
|
+
|
|
108
|
+
Until the keys land in `@objectstack/spec`, `@object-ui/types` carries them as a
|
|
109
|
+
documented `.extend()` on `UserActionsConfigSchema` (the same shape
|
|
110
|
+
`ListColumnSchema` uses while waiting on objectstack#3761); it collapses into a
|
|
111
|
+
plain re-export once they do. Note the spec schema is not `.strict()`, so before
|
|
112
|
+
this an author writing `userActions: { group: false }` had it **silently
|
|
113
|
+
stripped** — valid on parse, no effect at render.
|
|
114
|
+
|
|
115
|
+
Defaults are unchanged and deliberately asymmetric, matching what these flags
|
|
116
|
+
have always done: `search` / `sort` / `filter` / `rowHeight` / `group` are on
|
|
117
|
+
unless turned off; `hideFields` / `rowColor` are off unless turned on. Making
|
|
118
|
+
them uniform would grow two buttons on every existing view, so it is left as its
|
|
119
|
+
own product decision rather than smuggled into a vocabulary migration.
|
|
120
|
+
|
|
121
|
+
Also drops a dead relay in app-shell's `ObjectView`, which forwarded
|
|
122
|
+
`showDescription` onto the node although `ListView` has only ever read
|
|
123
|
+
`appearance.showDescription`.
|
|
124
|
+
|
|
125
|
+
- 24e0e0a: feat(components,grid,list): a column-header sort orders the whole list, not the page you can see — #3106
|
|
126
|
+
|
|
127
|
+
Clicking a column header under server pagination sorted **the current page**.
|
|
128
|
+
The user saw "sorted by this column" and got "these fifty rows are in order;
|
|
129
|
+
page 2 starts over". The sort was real — its scope was not the one the screen
|
|
130
|
+
implied — and it had no way out of `data-table` at all: the sort lived in two
|
|
131
|
+
`useState`s with no callback, so the layer that issues the request could not
|
|
132
|
+
see it even in principle.
|
|
133
|
+
|
|
134
|
+
`DataTable` gains `manualSorting` + a controlled `sort` + `onSortChange`. In
|
|
135
|
+
that mode it sorts nothing, reports what a header click asks for, and renders
|
|
136
|
+
`sort` as the indicator — keeping **no** sort state of its own, because a
|
|
137
|
+
private copy beside a controlled prop is the shape the defect had.
|
|
138
|
+
|
|
139
|
+
`ObjectGrid` turns that into a `$orderby` in both of its server modes (its own
|
|
140
|
+
fetch, and a parent-driven one), and `ListView` lands it in `currentSort` — the
|
|
141
|
+
same state the toolbar's sort builder writes. One sort, two controls: that is
|
|
142
|
+
what makes "does a header sort outrank the saved view's sort?" a non-question
|
|
143
|
+
rather than a precedence rule someone has to remember.
|
|
144
|
+
|
|
145
|
+
Three details that are decisions, not incidentals:
|
|
146
|
+
|
|
147
|
+
- **A header click replaces the order** instead of appending to it, so the
|
|
148
|
+
column under the cursor is the one the list is sorted by. Multi-key orders
|
|
149
|
+
still come from the sort builder, and the headers render them numbered.
|
|
150
|
+
- **It cannot ask for "no sort".** In client mode the third click clears, and
|
|
151
|
+
that is meaningful there — the rows return to the order they arrived in.
|
|
152
|
+
Across a server-paged collection there is no such order (objectstack#4363), so
|
|
153
|
+
a header offering it would hand the user a worse lie than the one being fixed.
|
|
154
|
+
Clearing stays with the sort builder, which can restore the view's default.
|
|
155
|
+
- **Relational columns render no sort affordance** under server sorting. A
|
|
156
|
+
`lookup` column shows a related record's name while `$orderby` can only order
|
|
157
|
+
by the stored id (objectstack#4256) — the same reason #3096 removed them from
|
|
158
|
+
the toolbar's sort picker. Client-side sorting keys off the rendered label, so
|
|
159
|
+
those headers stay live there.
|
|
160
|
+
|
|
161
|
+
Client-side tables are untouched: same three-state cycle, same local sort.
|
|
162
|
+
|
|
163
|
+
### Patch Changes
|
|
164
|
+
|
|
165
|
+
- 95b7214: fix(list,grid,detail,tree,core): every column resolver reads one key (#3104 PR2)
|
|
166
|
+
|
|
167
|
+
PR1 (#3119) put a canonicalizing fold at ListView's ingestion boundary. This
|
|
168
|
+
converges the 22 read sites themselves onto `columnIdentity()` from
|
|
169
|
+
`@object-ui/core`, so a surface that is NOT downstream of that fold resolves
|
|
170
|
+
the same identity anyway.
|
|
171
|
+
|
|
172
|
+
That distinction is the user-visible part. A standalone `object-grid` node —
|
|
173
|
+
authored directly on a page, with no `list-view` above it — never passed
|
|
174
|
+
through `normalizeListViewSchema`. Its `getSelectFields` read `c.field` alone
|
|
175
|
+
while the `ensureId` probe one line above read `f?.name || f?.field`, so a
|
|
176
|
+
legacy `{ name: 'account' }` column reached `$select` as a literal `undefined`
|
|
177
|
+
hole: the server never returned the field and every cell in that column came
|
|
178
|
+
back empty. Same for `ObjectTree`, `RelatedList` and the `record:details` /
|
|
179
|
+
`record:related_list` renderers.
|
|
180
|
+
|
|
181
|
+
Converged:
|
|
182
|
+
|
|
183
|
+
| Surface | Was | Now |
|
|
184
|
+
| ---------------------------------------- | ---------------------------------------------- | ----------------------------------- |
|
|
185
|
+
| `ListView` ×9 + its 2 request builders | `name \|\| fieldName \|\| field` vs `f?.field` | `columnIdentity()` |
|
|
186
|
+
| `RelatedList` ×8 | `accessorKey \|\| field \|\| name` | `accessorKey \|\| columnIdentity()` |
|
|
187
|
+
| `ObjectGrid` | name-first probe vs `c.field` projection | `columnIdentity()` |
|
|
188
|
+
| `ObjectTree` | `name \|\| fieldName \|\| field \|\| key` | `columnIdentity() \|\| key` |
|
|
189
|
+
| `buildExpandFields` | `field ?? name ?? fieldName` | `columnIdentity()` |
|
|
190
|
+
| `record-details` / `record-related-list` | `field \|\| name (\|\| key)` | `columnIdentity() (\|\| key)` |
|
|
191
|
+
|
|
192
|
+
`accessorKey` keeps its precedence in `RelatedList` — it is TanStack Table's
|
|
193
|
+
column key, not ObjectStack metadata identity, and only the `field || name`
|
|
194
|
+
tail was converged. `key` stays a tail fallback in `ObjectTree` and
|
|
195
|
+
`record-related-list` for the same reason: it is a generic entry key.
|
|
196
|
+
|
|
197
|
+
Two incidental fixes that TypeScript surfaced once the resolver stopped
|
|
198
|
+
returning `any`: ListView's filter-field options and its hide-fields popover
|
|
199
|
+
both built entries keyed `undefined` for a column with no resolvable identity.
|
|
200
|
+
Those entries could never match a column; they are now dropped.
|
|
201
|
+
|
|
202
|
+
**Inventory re-triage.** PR1 recorded 24 family members. Two were mis-classified
|
|
203
|
+
and are reclassified here rather than converged — reading what they actually
|
|
204
|
+
feed shows they are not column reads at all:
|
|
205
|
+
|
|
206
|
+
- `ViewPreview.tsx` adapts a ViewItem **form** section to what `object-form`
|
|
207
|
+
selects by (`field` → `name`) — the #3090 two-layer join.
|
|
208
|
+
- `SchemaForm.tsx` renders an arbitrary metadata **array** into a popover
|
|
209
|
+
summary and guesses at a display key; the entries are validations, actions,
|
|
210
|
+
or whatever the JSON schema declares.
|
|
211
|
+
|
|
212
|
+
So the family was 22, and it is now **0**. The ratchet asserts that, asserts
|
|
213
|
+
each converged surface actually routes through the shared reader (a surface
|
|
214
|
+
that dropped identity resolution instead of converging it goes red), and pins
|
|
215
|
+
`accessorKey`'s precedence in `RelatedList`.
|
|
216
|
+
|
|
217
|
+
- 9eb932b: fix(console): three real-user console failures — 403 blamed on the network, ⌘K search capped at 8 objects, nav gating fields inert
|
|
218
|
+
|
|
219
|
+
1. **List error panel classifies the failure** (`plugin-list`, `i18n`): a 403/401 from the data source used to render the same "check your connection" copy as a genuine outage, sending users to debug their network while the server was correctly denying access. The panel now classifies by `httpStatus`/`status`/`statusCode`, the `PERMISSION_DENIED`/`UNAUTHORIZED` error codes, or an `HTTP <status>` message prefix, and renders dedicated permission-denied / sign-in-required copy (all nine locales).
|
|
220
|
+
|
|
221
|
+
2. **⌘K / full-page search scope is no longer truncated** (`react`): `maxObjectsQueried` caps the per-object fanout fallback, not the search scope — it used to slice the candidate pool itself, so the `objects` whitelist sent to the platform's `/api/v1/search` only ever named the first 8 nav objects. Which sidebar group came first decided which records were findable; everything later in the nav was unsearchable no matter what the user typed.
|
|
222
|
+
|
|
223
|
+
3. **Nav gating fields finally gate** (`app-shell`): `evaluateVisibility` only evaluated `${…}` template strings, so the `{ dialect: 'cel', source }` envelopes the spec normalizes every authored `visible` predicate into fell through to a blanket "visible" — a constant-false predicate still rendered for everyone. It now delegates to `ExpressionEvaluator.evaluateCondition`, which routes CEL envelopes to the canonical `@objectstack/formula` engine. And the sidebars' `requiredPermissions` check treats a bare name as an ADR-0066 system capability (union of the user's permission-set `systemPermissions` from `/me/permissions`) — the same subset rule the server applies to `AppSchema.requiredPermissions` — instead of misreading it as `can(<name>, 'read')`, which had degraded `requiredPermissions` into a hide-from-everyone switch (admins included). The `object:action` form and the legacy object-read fallback keep working.
|
|
224
|
+
|
|
225
|
+
- 7f0252e: fix(list,data-objectstack,types): exporting a searched list no longer downloads the unsearched superset
|
|
226
|
+
|
|
227
|
+
The server-streamed export mirrored the view's `filter` and `sort`, and the
|
|
228
|
+
code comment claimed that made the file match the screen:
|
|
229
|
+
|
|
230
|
+
> Mirrors the active view's filter + sort so the exported file matches what the
|
|
231
|
+
> user sees.
|
|
232
|
+
|
|
233
|
+
It mirrored one half. There was no way to carry the term a user had typed into
|
|
234
|
+
the search box — `ExportDownloadRequest` had no field for one — so exporting
|
|
235
|
+
during a search produced **more rows than the list showed**, in a file that
|
|
236
|
+
looks authoritative, with nothing indicating the difference. The client-side
|
|
237
|
+
fallback was always correct (it serializes the already-searched `data`); only
|
|
238
|
+
the server path was wrong, and it is the one that handles xlsx.
|
|
239
|
+
|
|
240
|
+
Same family as a dropped filter (objectstack#3948, objectstack#4181): a
|
|
241
|
+
plausible answer that is quietly broader than the one asked for.
|
|
242
|
+
|
|
243
|
+
- `ExportDownloadRequest` gains `search` / `searchFields`.
|
|
244
|
+
- `ObjectStackAdapter.exportDownload` sends them as `search=` / `searchFields=`,
|
|
245
|
+
trimming the term and omitting both when it is blank (`searchFields` alone
|
|
246
|
+
means nothing).
|
|
247
|
+
- `ListView` passes the active `searchTerm` and the view's `searchableFields`,
|
|
248
|
+
and both are now in the export callback's dependency array — a stale closure
|
|
249
|
+
would export the wrong row set.
|
|
250
|
+
|
|
251
|
+
Requires a server with objectstack#4230. Older servers ignore unknown query
|
|
252
|
+
params on this route, so they keep today's behaviour rather than erroring.
|
|
253
|
+
|
|
254
|
+
**Also: the filter merge is no longer written twice.** The three filter sources
|
|
255
|
+
(view filter, filter-panel group, per-field user filters) were merged by
|
|
256
|
+
verbatim copies in the data fetch and in the export — two copies that must
|
|
257
|
+
agree, deciding respectively what the user _sees_ and what they _download_.
|
|
258
|
+
Both now call `buildEffectiveFilter`. This is a pure extraction: the copies did
|
|
259
|
+
agree, and the four parity tests added for it pass against the old code too.
|
|
260
|
+
They exist to keep it that way — the adapter's duplicated filter-shape check
|
|
261
|
+
had already drifted apart unnoticed (#3072).
|
|
262
|
+
|
|
263
|
+
- c4d7b20: fix(view,list,core): a view's filter no longer disappears, or arrives as a predicate on columns that don't exist
|
|
264
|
+
|
|
265
|
+
Sweeping the other `$filter` producers after #3078 turned up two live defects in
|
|
266
|
+
`ObjectView`, which fetches its own data for calendar / kanban / gallery /
|
|
267
|
+
timeline (grid delegates to `ObjectGrid`).
|
|
268
|
+
|
|
269
|
+
**1. An object filter was dropped, and only for non-grid views.**
|
|
270
|
+
`table.defaultFilters` is declared `Record<string, any>`, and the merge tested
|
|
271
|
+
`baseFilter.length > 0` — `undefined > 0` for an object. So the filter vanished
|
|
272
|
+
and the view returned **every record**. `ObjectGrid` assigns the same value
|
|
273
|
+
straight to `params.$filter`, so one view definition filtered correctly as a
|
|
274
|
+
grid and returned everything as a calendar.
|
|
275
|
+
|
|
276
|
+
**2. Rule objects were spread into the `and`, not wrapped.**
|
|
277
|
+
`['and', ...baseFilter, ...userFilter]` is only correct when the source is an
|
|
278
|
+
array of AST nodes. `activeView.filter` is a spec `ViewFilterRule[]`, so
|
|
279
|
+
spreading put bare rule objects where the AST expects nodes:
|
|
280
|
+
|
|
281
|
+
```js
|
|
282
|
+
isFilterAST([
|
|
283
|
+
"and",
|
|
284
|
+
{ field: "stage", operator: "eq", value: "won" },
|
|
285
|
+
["owner", "=", "me"],
|
|
286
|
+
]);
|
|
287
|
+
// false → 400 since objectstack#4121
|
|
288
|
+
parseFilterAST(same);
|
|
289
|
+
// {$and:[{field:'stage',operator:'eq',value:'won'}, {owner:'me'}]}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
That second line is a predicate over three columns named `field`, `operator`
|
|
293
|
+
and `value` — which don't exist.
|
|
294
|
+
|
|
295
|
+
> **Correction.** The first version of this note said the spread was "reachable
|
|
296
|
+
> whenever a view with a filter meets a user filter value". That was wrong for
|
|
297
|
+
> `ObjectView`: the branch required a non-empty user filter, and nothing ever
|
|
298
|
+
> wrote the state it was built from, so it could never run. The shape is
|
|
299
|
+
> genuinely broken — a live server answers it with a 400 — and the adapter-level
|
|
300
|
+
> defence added alongside is still warranted for any producer that emits it, but
|
|
301
|
+
> **this particular site was dead code, not a live defect.** Defect 1 above was
|
|
302
|
+
> live: it sat on the always-taken path. The dead machinery behind the wrong
|
|
303
|
+
> claim is removed in a follow-up.
|
|
304
|
+
|
|
305
|
+
New in `@object-ui/core`: `toFilterNode` normalizes one source (rule array / AST
|
|
306
|
+
/ MongoDB object) and `mergeFilterNodes` combines sources as siblings under one
|
|
307
|
+
`and`. `ObjectView` and `ListView.buildEffectiveFilter` both use them, so the
|
|
308
|
+
three filter shapes are reconciled in one place instead of by hand at each
|
|
309
|
+
renderer.
|
|
310
|
+
|
|
311
|
+
`ObjectStackAdapter` also now translates a bare rule object sitting directly
|
|
312
|
+
under a logical node — the chokepoint defence for any producer still emitting
|
|
313
|
+
the spread shape. Only rule-_shaped_ objects are touched; a child with no
|
|
314
|
+
`field` is a genuine MongoDB condition and passes through untouched.
|
|
315
|
+
|
|
316
|
+
**Correcting a comment shipped in #3078.** `buildEffectiveFilter` documented the
|
|
317
|
+
dropped-object case as unreachable, "nothing in this repo produces one for a
|
|
318
|
+
list view". That was wrong: `ObjectView` passes `mergedFilters` straight into
|
|
319
|
+
that schema's `filter`, and its last fallback is `table.defaultFilters`. The
|
|
320
|
+
case is now handled rather than explained away.
|
|
321
|
+
|
|
322
|
+
Verified with 19 tests across the four packages; reverting each source file
|
|
323
|
+
fails the ones that cover it. Emitted filters are asserted against the spec's
|
|
324
|
+
own `isFilterAST` / `parseFilterAST`, including an executable pin on what the
|
|
325
|
+
old spread shape produced.
|
|
326
|
+
|
|
327
|
+
- 2d5d594: fix(list,detail): sorting a lookup column no longer orders by an invisible key — #3096
|
|
328
|
+
|
|
329
|
+
A relational column (`lookup` / `master_detail` / `user` / `tree`) never holds
|
|
330
|
+
the string its cell shows: it holds the `$expand`-ed record, or a raw foreign-key
|
|
331
|
+
id whose label was resolved separately. Every sort path took that raw value as
|
|
332
|
+
its key, so the column of names came back in an order with no relation to the
|
|
333
|
+
names — sorting looked broken, with nothing saying the key was something else.
|
|
334
|
+
|
|
335
|
+
The two halves are fixed differently, because they can order by different things:
|
|
336
|
+
|
|
337
|
+
- **Client-side sorts** (grid column headers, any `data-table`, a non-windowed
|
|
338
|
+
related list) now key off the label the cell renders, via the new
|
|
339
|
+
`getSortValue` / `compareSortValues` in `@object-ui/core` — which resolves an
|
|
340
|
+
expanded record through `getRecordDisplayName` (ADR-0079), so the sort key and
|
|
341
|
+
the lookup cell agree on which field names a record. This replaces two broken
|
|
342
|
+
comparators: `a[col] < b[col]` is always false between two objects (the
|
|
343
|
+
comparator collapsed to a constant and permuted the rows), and
|
|
344
|
+
`String(a[col])` is `"[object Object]"` (every row compared equal, so the sort
|
|
345
|
+
silently did nothing).
|
|
346
|
+
- **Server `$orderby` sorts** cannot be fixed here — the key is the stored id by
|
|
347
|
+
construction, and `objectstack#4256` settled that no relation join is coming.
|
|
348
|
+
So those entry points stop offering the illusion: the ListView toolbar sort
|
|
349
|
+
picker withholds relational fields and explains why (pointing at a formula
|
|
350
|
+
field as the supported way to sort by a related name), and a windowed related
|
|
351
|
+
list renders no sort button for them.
|
|
352
|
+
|
|
353
|
+
A relational field the view's CURRENT sort already uses stays listed, labelled
|
|
354
|
+
`(by ID)`, so view metadata authored or saved with such a sort round-trips
|
|
355
|
+
instead of rendering a blank row and losing the sort on the next edit.
|
|
356
|
+
|
|
357
|
+
## 17.0.0
|
|
358
|
+
|
|
359
|
+
### Minor Changes
|
|
360
|
+
|
|
361
|
+
- 2735de6: feat: render the server's effective API operation set (#3391 PR-4)
|
|
362
|
+
|
|
363
|
+
The frontend now consumes the per-object **effective API operation set** the
|
|
364
|
+
server resolves (from `/me/permissions` `apiOperations`, framework #3391) —
|
|
365
|
+
never the raw `apiMethods` — so Import/Export/New/Edit/Delete buttons match what
|
|
366
|
+
the server will actually admit, and a 405 import refusal shows a dedicated
|
|
367
|
+
message instead of silently falling back.
|
|
368
|
+
|
|
369
|
+
- **core** `resolveCrudAffordances(obj, effectiveApiOperations?)` — new optional
|
|
370
|
+
second argument intersects each affordance bit with its API operation
|
|
371
|
+
(create/import→create/import, edit→update, delete→delete, exportCsv→export).
|
|
372
|
+
Omitting it (old backend / no effective set) leaves affordances unchanged.
|
|
373
|
+
- **permissions** — `/me/permissions` response carries per-object
|
|
374
|
+
`apiOperations`; `PermissionContextValue.getObjectApiOperations(object)`
|
|
375
|
+
exposes it (undefined when absent → callers keep current behavior); `check()`
|
|
376
|
+
maps `import→allowCreate`, `export→allowRead`.
|
|
377
|
+
- **app-shell** `ObjectView` intersects its toolbar affordances with the object's
|
|
378
|
+
effective operations (Import); the platform-admin identity-import bypass is
|
|
379
|
+
unaffected.
|
|
380
|
+
- **plugin-list** `ListView` / **plugin-grid** `ObjectGrid` gate the Export
|
|
381
|
+
button (and export handler) on effective `export`; `plugin-grid` gains the
|
|
382
|
+
`@object-ui/permissions` workspace dependency.
|
|
383
|
+
- **plugin-grid** `ImportWizard` — a 405 / `OBJECT_API_METHOD_NOT_ALLOWED`
|
|
384
|
+
import refusal is detected by a new `isImportNotAllowed` predicate at every
|
|
385
|
+
catch site (async, sync, dry-run) and STOPS with a dedicated
|
|
386
|
+
`grid.import.notAllowed` message (10 locales + fallback dict) — it never falls
|
|
387
|
+
back to the sync/legacy path (which 405s too), distinct from the 404
|
|
388
|
+
route-absent fallback.
|
|
389
|
+
|
|
390
|
+
Backward-compatible: a missing effective set (unrestricted object, older
|
|
391
|
+
backend, or no permission provider) preserves the current default-allow
|
|
392
|
+
behavior everywhere.
|
|
393
|
+
|
|
394
|
+
- ba45145: feat: gate list row Edit/Delete and bulk delete on the server's effective operation set (#3720)
|
|
395
|
+
|
|
396
|
+
The **fourth** surface #3391 left open. The three earlier rounds — the toolbar
|
|
397
|
+
(objectui#2823), detail/form (#3546, objectui#2832 + #2876) and related lists
|
|
398
|
+
(#3546) — all route through `resolveCrudAffordances`. The main list's **row
|
|
399
|
+
CRUD** does not: it has its own resolver (`plugin-grid`'s
|
|
400
|
+
`resolveRowCrudAffordances`), so none of those rounds ever reached it.
|
|
401
|
+
|
|
402
|
+
Its gate was `operations ?? { update: !!onEdit, delete: !!onDelete }` — and
|
|
403
|
+
`ObjectView` wires `onEdit`/`onDelete` unconditionally while view JSON rarely
|
|
404
|
+
declares `operations`, so it was effectively always-on. A caller whose effective
|
|
405
|
+
set carried neither `update` nor `delete` still got the row kebab's Edit/Delete
|
|
406
|
+
**and** the bulk delete, the most destructive affordance on the list.
|
|
407
|
+
|
|
408
|
+
- **plugin-grid** `resolveRowCrudAffordances` now takes `managedBy` and
|
|
409
|
+
`effectiveApiOperations` and resolves the object verdict through the shared
|
|
410
|
+
`resolveCrudAffordances` policy — so the row gate is the SAME decision the
|
|
411
|
+
toolbar, record header, form and related lists make. It also returns
|
|
412
|
+
`objectCanDelete`, the object-level delete verdict that bulk delete gates on
|
|
413
|
+
(bulk rides `onBulkDelete`, a different callback from the row `onDelete`).
|
|
414
|
+
- **plugin-grid** `ObjectGrid` threads its existing `effectiveApiOps` — until
|
|
415
|
+
now fed only to Export — into the row gate, and applies the delete verdict to
|
|
416
|
+
bulk delete: the implicit `['delete']`, an author-declared
|
|
417
|
+
`bulkActions: ['delete']`, and any `bulkActionDefs` entry with
|
|
418
|
+
`operation: 'delete'`. A declared bulk action is a _wiring_ declaration, not a
|
|
419
|
+
permission grant. Custom action ids and non-delete operations pass through
|
|
420
|
+
untouched.
|
|
421
|
+
- **plugin-list** `ListView`'s own bulk bar (the non-grid views — kanban /
|
|
422
|
+
calendar / gallery; the grid path delegates to `ObjectGrid`) drops its
|
|
423
|
+
built-in `delete` under the same verdict.
|
|
424
|
+
|
|
425
|
+
Also closes the ADR-0103 gap on this chain: `rowCrudAffordances` documented the
|
|
426
|
+
bucket lock as "applied upstream via the view's `operations.*`", but the
|
|
427
|
+
all-open default meant it never was — an engine-owned `system` / `append-only` /
|
|
428
|
+
`better-auth` object leaked a generic row Edit/Delete that the engine rejects
|
|
429
|
+
(`assertEngineOwnedWriteAllowed`). Running the shared policy applies it, and a
|
|
430
|
+
`userActions` opt-in still re-opens it (e.g. `sys_user`'s `edit`).
|
|
431
|
+
|
|
432
|
+
Same semantics as the earlier rounds: **intersection, never union** — a server
|
|
433
|
+
grant cannot re-open what the bucket or `userActions` closed, and a
|
|
434
|
+
`userActions` opt-in cannot survive a server denial. A missing effective set
|
|
435
|
+
(unrestricted object, older backend, or no `PermissionProvider`) preserves the
|
|
436
|
+
current behavior.
|
|
437
|
+
|
|
438
|
+
- cd09a7b: refactor(views): ListView reads the spec-canonical `columns`, with legacy `fields` folded in one normalizer (#2890 scope A step 1)
|
|
439
|
+
|
|
440
|
+
`ListViewSchema` has been derived from `@objectstack/spec/ui` since #2231, but
|
|
441
|
+
the renderer still spoke objectui's own vocabulary for the same concepts. First
|
|
442
|
+
rename closed: **`fields` → `columns`**.
|
|
443
|
+
|
|
444
|
+
Legacy acceptance does not disappear — stored view metadata in user databases
|
|
445
|
+
carries `fields` — but it now lives in exactly one place instead of being
|
|
446
|
+
re-implemented per read-site:
|
|
447
|
+
|
|
448
|
+
- **New `normalizeListViewSchema` (`@object-ui/core`)** folds `fields` into
|
|
449
|
+
`columns` (canonical wins when both are present) and drops the legacy key, so
|
|
450
|
+
a read-site that was missed fails loudly instead of quietly taking the legacy
|
|
451
|
+
path. It also absorbs the `viewType` renderability default ListView applied
|
|
452
|
+
inline. Non-mutating, idempotent, and returns its input by reference when
|
|
453
|
+
there is nothing to fold, so ListView's downstream memos keep a stable
|
|
454
|
+
dependency identity.
|
|
455
|
+
- **`ListView` normalizes once at the component boundary**, before anything
|
|
456
|
+
reads the schema. This is what guarantees the fold runs: nothing on the render
|
|
457
|
+
path parses view metadata through zod (the zod schemas serve the CLI
|
|
458
|
+
validator, the VS Code extension and tests), so a `z.preprocess` on
|
|
459
|
+
`ListViewSchema` — spec-side or local — would never execute.
|
|
460
|
+
- **Producers emit `columns`**: `ObjectView`'s `renderListView` payload,
|
|
461
|
+
`ObjectDataPage`, `InterfaceListPage` and the `list-view` registry defaults
|
|
462
|
+
had been _downgrading_ already-canonical `columns` config back to `fields`.
|
|
463
|
+
|
|
464
|
+
Two latent inconsistencies go away with it: the filter builder's
|
|
465
|
+
objectDef-not-loaded fallback now resolves `ListColumn.field` (it read only
|
|
466
|
+
`name`/`fieldName`, so object-form columns produced unnamed filter entries), and
|
|
467
|
+
the column list no longer depends on which of the two keys a host happened to
|
|
468
|
+
emit.
|
|
469
|
+
|
|
470
|
+
`fields` stays declared on `ListViewSchema` and in the drift guard's sanctioned
|
|
471
|
+
set — it is still valid input, and `@objectstack/spec`'s `react-blocks.ts`
|
|
472
|
+
sanctions it as the React-tier `<ListView fields>` prop — but it is input-only.
|
|
473
|
+
|
|
474
|
+
- f1abf0e: fix(views): ListView reads the spec-canonical `filter`, so a view's base filter reaches every visualization (#2890 scope A step 4)
|
|
475
|
+
|
|
476
|
+
Third rename in the ListView vocabulary migration: **`filters` → `filter`**. Unlike
|
|
477
|
+
the first two this closes a live bug, because the fork was asymmetric.
|
|
478
|
+
|
|
479
|
+
`ListView` was the **only** surface in the repo reading `filters`. Every child
|
|
480
|
+
view — `ObjectGrid`, `ObjectGallery`, `ObjectKanban`, `ObjectCalendar`,
|
|
481
|
+
`ObjectGantt`, `ObjectMap`, `ObjectTree`, `ObjectChart` — reads `filter`, and
|
|
482
|
+
`ListView` handed them `filters`. Wherever a child fetches its own rows instead
|
|
483
|
+
of receiving `ListView`'s, the view's base filter was silently dropped:
|
|
484
|
+
|
|
485
|
+
- **a `chart` list view aggregated the whole object.** The chart branch built an
|
|
486
|
+
`object-chart` node with `filters:`; `ObjectChart` reads `schema.filter` and
|
|
487
|
+
never read `filters`, so a chart view with a base filter charted unfiltered
|
|
488
|
+
totals.
|
|
489
|
+
- the same applied to any of the other view components rendered standalone from
|
|
490
|
+
a list-view-shaped config.
|
|
491
|
+
|
|
492
|
+
Conversely, a **spec-authored** list view — one carrying `filter`, which is what
|
|
493
|
+
the spec says and what `runtime-metadata-persistence` and "Save as view" already
|
|
494
|
+
persist — rendered **unfiltered** in `ListView`, because nothing read that key.
|
|
495
|
+
|
|
496
|
+
The fold is a key rename only. Both keys carry an ObjectQL FilterNode array
|
|
497
|
+
everywhere in objectui; every consumer passes the value straight to `$filter`.
|
|
498
|
+
(The spec types `filter` as `ViewFilterRule[]` — `{field, operator, value}`
|
|
499
|
+
objects — so objectui's field is typed from the spec but used as something else.
|
|
500
|
+
That mismatch is real and left alone here: converting formats inside a
|
|
501
|
+
vocabulary fold would change what reaches the data source.)
|
|
502
|
+
|
|
503
|
+
Also collapses a duplicated computation in `app-shell`'s `ObjectView`, which
|
|
504
|
+
computed the same effective filter **twice** — once as `filter` for the child
|
|
505
|
+
views, once as `filters` for `ListView` — with the two copies subtly different
|
|
506
|
+
(only one fell back to `listSchema.filter`; only the other ran token
|
|
507
|
+
substitution over the URL filters). There is now one computation, keeping both
|
|
508
|
+
behaviors.
|
|
509
|
+
|
|
510
|
+
`filters` stays declared on `ListViewSchema` and in the drift guard's sanctioned
|
|
511
|
+
set — stored views carry it and it is still valid input — but it is input-only.
|
|
512
|
+
|
|
513
|
+
- f05b84e: refactor(views): ListView resolves density from the spec-canonical `rowHeight` (#2890 scope A step 2)
|
|
514
|
+
|
|
515
|
+
Second rename in the ListView vocabulary migration: **`densityMode` → `rowHeight`**,
|
|
516
|
+
folded in the same `normalizeListViewSchema` that step 1 introduced.
|
|
517
|
+
|
|
518
|
+
Unlike `fields`/`columns` this is not a pure alias — the two vocabularies are
|
|
519
|
+
different sizes. The spec has five row heights (`compact`/`short`/`medium`/
|
|
520
|
+
`tall`/`extra_tall`); ListView's toolbar offers three densities
|
|
521
|
+
(`compact`/`comfortable`/`spacious`). Both directions now live in one place as
|
|
522
|
+
`DENSITY_MODE_TO_ROW_HEIGHT` / `ROW_HEIGHT_TO_DENSITY_MODE`, chosen so a fold
|
|
523
|
+
followed by a read is a round trip (`spacious` → `tall` → `spacious`), with the
|
|
524
|
+
narrowing collapse (`short` → `compact`, `extra_tall` → `spacious`) stated once
|
|
525
|
+
instead of being re-derived per call site.
|
|
526
|
+
|
|
527
|
+
Two behavior fixes fall out of it:
|
|
528
|
+
|
|
529
|
+
- **Precedence is no longer inverted.** `ListView` read `densityMode` _first_, so
|
|
530
|
+
a view carrying both keys rendered the legacy value — backwards from every
|
|
531
|
+
other legacy/canonical pair in the schema. The canonical key now wins.
|
|
532
|
+
- **The toolbar stops re-seeding the legacy key.** `ObjectView`'s
|
|
533
|
+
`onDensityChange` persisted `densityMode` into stored view metadata on every
|
|
534
|
+
density toggle, so the legacy vocabulary kept regrowing underneath the
|
|
535
|
+
migration. It persists `rowHeight` now.
|
|
536
|
+
|
|
537
|
+
`densityMode` stays declared on `ListViewSchema` and in the drift guard's
|
|
538
|
+
sanctioned set — stored views carry it and it is still valid input — but it is
|
|
539
|
+
input-only.
|
|
540
|
+
|
|
541
|
+
### Patch Changes
|
|
542
|
+
|
|
543
|
+
- ab46110: fix(list): show the real match total in the record-count status bar under server pagination
|
|
544
|
+
|
|
545
|
+
The Airtable-style record-count bar read `data.length`, but under server-side
|
|
546
|
+
pagination (#2212) `data` is only the current page window — so a 158-row result
|
|
547
|
+
paginated 100/page reported "100 条记录" on page 1 and "58 条记录" on page 2,
|
|
548
|
+
never the true total. There was no other place to see how many records the
|
|
549
|
+
query matched.
|
|
550
|
+
|
|
551
|
+
The bar now shows the server's grand total (`serverTotal`) when known, falling
|
|
552
|
+
back to `data.length` when the whole result set is in memory (non-paginated,
|
|
553
|
+
grouped and non-grid views are unchanged — `serverTotal` is null there, so the
|
|
554
|
+
count is identical to before). Browser-verified against the showcase contacts
|
|
555
|
+
list: the bar reads "158 条记录" and stays stable across pages, and switching to
|
|
556
|
+
grouped/other views correctly resets to the loaded count.
|
|
557
|
+
|
|
558
|
+
- 7d46648: fix(hooks): stop calling translation hooks inside try/catch (objectui#2879)
|
|
559
|
+
|
|
560
|
+
Eleven call sites wrapped a React hook in `try`/`catch` to make it
|
|
561
|
+
"provider-safe". `useObjectTranslation` and `useObjectLabel` already are — they
|
|
562
|
+
read context optionally and fall back to react-i18next's global instance, and
|
|
563
|
+
never throw. The `catch` bought nothing and cost correctness: a throw _after_
|
|
564
|
+
the hook ran desyncs hook order on the next render, because React matches hooks
|
|
565
|
+
positionally. objectui#2595/#2596 fixed exactly this in `@object-ui/i18n`'s
|
|
566
|
+
`createSafeTranslation`; nine plugin-local re-implementations kept their own
|
|
567
|
+
copy of the bug, and two more (`ObjectTimeline`, `ObjectView`) were found by the
|
|
568
|
+
new lint rule below — `ObjectView` had even suppressed
|
|
569
|
+
`react-hooks/rules-of-hooks` inline to keep it.
|
|
570
|
+
|
|
571
|
+
- Six exact re-implementations now delegate to `createSafeTranslation`:
|
|
572
|
+
`plugin-detail`, `plugin-timeline`, `plugin-list`, `plugin-calendar`,
|
|
573
|
+
`plugin-grid`'s `ObjectGrid`, `plugin-designer`.
|
|
574
|
+
- `components`' `data-table` also delegates; `createSafeTranslation` now
|
|
575
|
+
returns `language` alongside `t` so consumers that localize dates don't need
|
|
576
|
+
a second hook call. Purely additive.
|
|
577
|
+
- `plugin-gantt` and `plugin-grid`'s `ImportWizard` keep their local hooks —
|
|
578
|
+
they fall back _per key_, which a single-probe factory cannot express and
|
|
579
|
+
which their comments justify (a host dictionary that covers common keys but
|
|
580
|
+
lags on newer ones). Only the `try`/`catch` is removed.
|
|
581
|
+
- `ObjectTimeline` and `ObjectView` call the hook directly and probe the
|
|
582
|
+
returned value, mirroring `useSafeFieldLabel`.
|
|
583
|
+
|
|
584
|
+
Adds `object-ui/no-try-catch-around-hook` (error) so a twelfth copy fails CI.
|
|
585
|
+
It only matches `use*` names, accepts member calls solely on `React` (so
|
|
586
|
+
`vi.useRealTimers()` is not a hook), and resets its try-depth inside nested
|
|
587
|
+
functions (so `renderHook(() => useThing())` inside a `try` is fine) — both
|
|
588
|
+
false positives were real code in this repo and are pinned in the rule's tests.
|
|
589
|
+
|
|
590
|
+
`eslint-rules/**/*.test.js` matched no vitest project glob, so the local
|
|
591
|
+
plugin's specs had never run in CI. They are now included; all three pass.
|
|
592
|
+
|
|
593
|
+
`ObjectTimeline`'s test mock of `@object-ui/react` omitted `useObjectLabel` —
|
|
594
|
+
the removed `try`/`catch` had been silently absorbing that gap. The mock is now
|
|
595
|
+
complete.
|
|
596
|
+
|
|
597
|
+
- 8aae006: fix(views): the five per-view-type configs speak the spec vocabulary (#2231 phase 3)
|
|
598
|
+
|
|
599
|
+
`kanban`/`calendar`/`gantt`/`gallery`/`timeline` on `ListViewSchema` were the last
|
|
600
|
+
hand-written forks left after #2882 — and the fork was not cosmetic: objectui named
|
|
601
|
+
the same concepts differently from `@objectstack/spec/ui`, and several read-sites
|
|
602
|
+
only understood one of the two dialects. Two of those gaps were live bugs.
|
|
603
|
+
|
|
604
|
+
**Kanban lanes ignored the spec key.** `ListView` gated the Kanban tab on
|
|
605
|
+
`groupByField || groupField` but rendered lanes off `groupField` alone. A config
|
|
606
|
+
authored with the spec key — which is exactly what the product's own
|
|
607
|
+
`CreateViewDialog` emits — offered the tab and then grouped by whatever
|
|
608
|
+
`detectStatusField()` guessed. The spec's `columns` (the fields shown on each card)
|
|
609
|
+
was also spread onto the board verbatim, where `columns` means _lanes_, so
|
|
610
|
+
`ObjectKanban` built lanes with `undefined` id and title. `columns` now maps to
|
|
611
|
+
`cardFields` and the vocabulary keys are stripped from the passthrough.
|
|
612
|
+
|
|
613
|
+
**Timeline lost every spec key in app-shell.** `ObjectView`'s `timeline` branch was
|
|
614
|
+
a three-key whitelist while its `gallery`/`gantt` siblings had already been fixed to
|
|
615
|
+
spread-first, so a stored `timeline: { startDateField, endDateField, groupByField,
|
|
616
|
+
colorField, scale }` arrived with only `titleField` and an axis pinned to the
|
|
617
|
+
`'due_date'` fallback.
|
|
618
|
+
|
|
619
|
+
Also: `plugin-view`'s `ObjectView` now reads `gallery.coverField` and
|
|
620
|
+
`timeline.startDateField` (it only understood the legacy aliases), and the dead
|
|
621
|
+
`gallery.subtitleField` is removed — three producers computed it and `ObjectGallery`
|
|
622
|
+
never read it.
|
|
623
|
+
|
|
624
|
+
The schema side now derives from the spec configs (`.partial()`, since the product
|
|
625
|
+
authors partial configs and spec marks `columns`/`titleField`/`startDateField`
|
|
626
|
+
required). `gantt` needed no local schema at all. The pre-#2231 names
|
|
627
|
+
(`groupField`, `cardFields`, `imageField`, `dateField`) remain accepted as deprecated
|
|
628
|
+
aliases so stored views keep validating; the spec key wins wherever both appear.
|
|
629
|
+
`calendar.defaultView` stays local — it has no spec counterpart.
|
|
630
|
+
|
|
631
|
+
- Updated dependencies [7b21891]
|
|
632
|
+
- Updated dependencies [0b3be01]
|
|
633
|
+
- Updated dependencies [3c4d935]
|
|
634
|
+
- Updated dependencies [4b1ed7d]
|
|
635
|
+
- Updated dependencies [4b60d2d]
|
|
636
|
+
- Updated dependencies [952b978]
|
|
637
|
+
- Updated dependencies [de5e40c]
|
|
638
|
+
- Updated dependencies [1a03af6]
|
|
639
|
+
- Updated dependencies [3e886eb]
|
|
640
|
+
- Updated dependencies [cfc675e]
|
|
641
|
+
- Updated dependencies [20df08c]
|
|
642
|
+
- Updated dependencies [1767124]
|
|
643
|
+
- Updated dependencies [8ecf5a6]
|
|
644
|
+
- Updated dependencies [af705b9]
|
|
645
|
+
- Updated dependencies [0502a7c]
|
|
646
|
+
- Updated dependencies [7b35e4b]
|
|
647
|
+
- Updated dependencies [8fb1295]
|
|
648
|
+
- Updated dependencies [e16ed2d]
|
|
649
|
+
- Updated dependencies [c6fd752]
|
|
650
|
+
- Updated dependencies [f9bbddb]
|
|
651
|
+
- Updated dependencies [dfd3705]
|
|
652
|
+
- Updated dependencies [c77108c]
|
|
653
|
+
- Updated dependencies [2735de6]
|
|
654
|
+
- Updated dependencies [697cda4]
|
|
655
|
+
- Updated dependencies [c19ac11]
|
|
656
|
+
- Updated dependencies [6dee2cb]
|
|
657
|
+
- Updated dependencies [e05f052]
|
|
658
|
+
- Updated dependencies [0502a7c]
|
|
659
|
+
- Updated dependencies [faad45e]
|
|
660
|
+
- Updated dependencies [09c6a17]
|
|
661
|
+
- Updated dependencies [c7cff19]
|
|
662
|
+
- Updated dependencies [ba73a02]
|
|
663
|
+
- Updated dependencies [cd09a7b]
|
|
664
|
+
- Updated dependencies [f1abf0e]
|
|
665
|
+
- Updated dependencies [f05b84e]
|
|
666
|
+
- Updated dependencies [9b4b952]
|
|
667
|
+
- Updated dependencies [341bfb5]
|
|
668
|
+
- Updated dependencies [2f947e4]
|
|
669
|
+
- Updated dependencies [7d46648]
|
|
670
|
+
- Updated dependencies [9b53d72]
|
|
671
|
+
- Updated dependencies [bb4aa25]
|
|
672
|
+
- Updated dependencies [75f1cdf]
|
|
673
|
+
- Updated dependencies [662bdf9]
|
|
674
|
+
- Updated dependencies [059a052]
|
|
675
|
+
- Updated dependencies [53642d4]
|
|
676
|
+
- Updated dependencies [8aae006]
|
|
677
|
+
- Updated dependencies [c6cfdf1]
|
|
678
|
+
- Updated dependencies [d147a13]
|
|
679
|
+
- Updated dependencies [c6aaed8]
|
|
680
|
+
- Updated dependencies [263f885]
|
|
681
|
+
- Updated dependencies [dc334da]
|
|
682
|
+
- @object-ui/components@17.0.0
|
|
683
|
+
- @object-ui/i18n@17.0.0
|
|
684
|
+
- @object-ui/fields@17.0.0
|
|
685
|
+
- @object-ui/react@17.0.0
|
|
686
|
+
- @object-ui/types@17.0.0
|
|
687
|
+
- @object-ui/core@17.0.0
|
|
688
|
+
- @object-ui/permissions@17.0.0
|
|
689
|
+
- @object-ui/mobile@17.0.0
|
|
690
|
+
|
|
3
691
|
## 16.1.0
|
|
4
692
|
|
|
5
693
|
### Patch Changes
|