@nxgt/material 3.26.1 → 3.28.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 +28 -0
- package/README.md +2 -2
- package/dist/{async-slice-gAbGnQT8.js → async-slice-DmhasXmq.js} +2 -2
- package/dist/{button-Cr9GIIj4.js → button-BjIg_Lz6.js} +1 -1
- package/dist/charts.js +3 -3
- package/dist/{chip-s11lOfxB.js → chip-DIBIDUZH.js} +1 -1
- package/dist/{code-editor-DGIjL8Hr.js → code-editor-2EU-q9FV.js} +4 -4
- package/dist/code-editor.js +4 -4
- package/dist/{code-language-items-DCOQnaCo.js → code-language-items-C_UeMQMm.js} +2 -2
- package/dist/components/ui/display/chip/chip-parts.css +19 -0
- package/dist/{components-BEN2FVD-.js → components-Cl3Zyoe-.js} +15000 -14487
- package/dist/components.js +26 -26
- package/dist/{dialog-Crvg-MN5.js → dialog-LtXwv6N3.js} +1 -1
- package/dist/dnd.js +1 -1
- package/dist/{dropdown-menu-B71aggp0.js → dropdown-menu-ppMPD12c.js} +1 -1
- package/dist/editor.js +7 -7
- package/dist/{empty-state-8LddxMU4.js → empty-state-X_nikpBS.js} +1 -1
- package/dist/hooks.js +2 -2
- package/dist/{i18n-provider-BDi9-6DO.js → i18n-provider-BfyDgd2o.js} +1 -1
- package/dist/i18n.js +5 -5
- package/dist/{icon-badge-DG7wLnHh.js → icon-badge-oLBh3Mlk.js} +1 -1
- package/dist/lib/components/ui/collections/facet-group/facet-group.d.ts +1 -1
- package/dist/lib/components/ui/collections/facet-group/types.d.ts +2 -0
- package/dist/lib/components/ui/collections/filter/bar/filter-bar-classes.d.ts +11 -1
- package/dist/lib/components/ui/collections/filter/bar/{filter-bar.d.ts → filter-bar-view.d.ts} +2 -2
- package/dist/lib/components/ui/collections/filter/bar/partials/filter-clear-all.d.ts +2 -2
- package/dist/lib/components/ui/collections/filter/bar/partials/filter-quick-pill.d.ts +4 -1
- package/dist/lib/components/ui/collections/filter/bar/partials/filter-quick-toggle.d.ts +3 -0
- package/dist/lib/components/ui/collections/filter/bar/partials/filter-scope-list.d.ts +6 -0
- package/dist/lib/components/ui/collections/filter/bar/partials/filter-scope-pill.d.ts +13 -0
- package/dist/lib/components/ui/collections/filter/bar/partials/use-quick-condition.d.ts +19 -0
- package/dist/lib/components/ui/collections/filter/bar/partials/use-quick-pill.d.ts +30 -0
- package/dist/lib/components/ui/collections/filter/bar/use-filter-bar-wiring.d.ts +5 -0
- package/dist/lib/components/ui/collections/filter/editors/boolean-editor.d.ts +3 -2
- package/dist/lib/components/ui/collections/filter/editors/checkbox-editor.d.ts +1 -1
- package/dist/lib/components/ui/collections/filter/editors/switch-editor.d.ts +1 -1
- package/dist/lib/components/ui/collections/filter/facets/filter-facets.d.ts +2 -2
- package/dist/lib/components/ui/collections/filter/filter-bar-url.d.ts +51 -0
- package/dist/lib/components/ui/collections/filter/index.d.ts +2 -0
- package/dist/lib/components/ui/collections/filter/internal.d.ts +3 -1
- package/dist/lib/components/ui/collections/filter/query/filter-results-status.d.ts +4 -1
- package/dist/lib/components/ui/collections/filter/query/types.d.ts +21 -3
- package/dist/lib/components/ui/collections/{server-table → filter/query}/use-answer-wait.d.ts +3 -1
- package/dist/lib/components/ui/collections/filter/query/use-apply-now.d.ts +6 -0
- package/dist/lib/components/ui/collections/filter/query/use-defaults.d.ts +15 -0
- package/dist/lib/components/ui/collections/filter/query/use-field-error.d.ts +5 -0
- package/dist/lib/components/ui/collections/filter/query/use-filter-query-options.d.ts +14 -0
- package/dist/lib/components/ui/collections/filter/query/use-filter-query-value.d.ts +2 -0
- package/dist/lib/components/ui/collections/filter/query/use-filter-query.d.ts +6 -2
- package/dist/lib/components/ui/collections/filter/query/use-filter-url-query.d.ts +52 -0
- package/dist/lib/components/ui/collections/filter/query/use-refusal-announcement.d.ts +10 -0
- package/dist/lib/components/ui/collections/filter/sections/chips/condition-panel.d.ts +2 -1
- package/dist/lib/components/ui/collections/filter/sections/chips/filter-chip-body.d.ts +4 -2
- package/dist/lib/components/ui/collections/filter/sections/chips/filter-chip-list.d.ts +3 -2
- package/dist/lib/components/ui/collections/filter/sections/chips/filter-default-chip.d.ts +14 -0
- package/dist/lib/components/ui/collections/filter/sections/chips/filter-default-group.d.ts +16 -0
- package/dist/lib/components/ui/collections/filter/sections/chips/filter-default-parts.d.ts +12 -0
- package/dist/lib/components/ui/collections/filter/sections/chips/filter-error-parts.d.ts +22 -0
- package/dist/lib/components/ui/collections/filter/sections/chips/use-control-description.d.ts +13 -0
- package/dist/lib/components/ui/collections/filter/sections/filter-facet-section.d.ts +5 -0
- package/dist/lib/components/ui/collections/filter/sections/filter-search.d.ts +4 -3
- package/dist/lib/components/ui/collections/filter/sections/filter-sections-classes.d.ts +8 -0
- package/dist/lib/components/ui/collections/filter/sections/renderers/choice-facet.d.ts +1 -1
- package/dist/lib/components/ui/collections/filter/sections/use-filter-shortcuts.d.ts +4 -3
- package/dist/lib/components/ui/collections/server-table/partials/server-table-empty-chips.d.ts +1 -1
- package/dist/lib/components/ui/collections/server-table/types.d.ts +13 -1
- package/dist/lib/components/ui/collections/server-table/use-filter-params.d.ts +5 -4
- package/dist/lib/components/ui/collections/server-table/use-table-answer.d.ts +1 -1
- package/dist/lib/components/ui/layout/app-shell/activity-column.d.ts +2 -2
- package/dist/lib/components/ui/layout/app-shell/activity-layout.d.ts +1 -1
- package/dist/lib/components/ui/layout/app-shell/activity-layout.types.d.ts +11 -0
- package/dist/lib/components/ui/layout/app-shell/backdrop-layer.d.ts +3 -1
- package/dist/lib/components/ui/layout/app-shell/shapes-clear-content.d.ts +24 -0
- package/dist/lib/components/ui/layout/app-shell/theme-backdrop.d.ts +3 -1
- package/dist/lib/components/ui/layout/app-shell/use-keep-shapes-inside.d.ts +4 -3
- package/dist/lib/components/ui/layout/app-shell/watch-content.d.ts +10 -1
- package/dist/lib/components/ui/layout/auth-card/auth-card-backdrop.d.ts +4 -2
- package/dist/lib/components/ui/layout/auth-card/auth-card.d.ts +4 -2
- package/dist/lib/i18n/resources/en/en.d.ts +11 -0
- package/dist/lib/i18n/resources/en/material.json.d.ts +11 -0
- package/dist/lib/i18n/resources/fr/fr.d.ts +11 -0
- package/dist/lib/i18n/resources/fr/material.json.d.ts +11 -0
- package/dist/lib/i18n/resources/resources.d.ts +22 -0
- package/dist/lib/i18n/translator.d.ts +1 -1
- package/dist/lib.js +1 -1
- package/dist/main.js +43 -43
- package/dist/map.js +4 -4
- package/dist/material.css +1 -1
- package/dist/pdf-reader.js +1 -1
- package/dist/print.js +2 -2
- package/dist/{public-QUiFLEoj.js → public-BeM7-x-m.js} +6 -6
- package/dist/{rich-code-block-CjD54THr.js → rich-code-block-B4TIjZRA.js} +4 -4
- package/dist/{sortable-provider-DYnyzfgR.js → sortable-provider-Dcsw0pgW.js} +1 -1
- package/dist/{spinner-CJtTRNAX.js → spinner-1Ia6Akhy.js} +1 -1
- package/dist/{stored-language-ClkfkueV.js → stored-language-DoWMr969.js} +22 -0
- package/dist/theme-picker.js +2 -2
- package/dist/{theme-toggle-ufQL3SO6.js → theme-toggle-GlKtdgYz.js} +1 -1
- package/dist/{toolbar-items-DlioFgpM.js → toolbar-items-C1GdN9P9.js} +2 -2
- package/dist/{translate-material-O-uasx3u.js → translate-material-BneuBHXZ.js} +2 -2
- package/dist/{translator-C0uxN1Nh.js → translator-D88BbpXZ.js} +1 -1
- package/dist/{use-chat-composer-DzVO5mwz.js → use-chat-composer-beyfZPBD.js} +12 -12
- package/dist/{use-heading-spy-BHCVdJfr.js → use-heading-spy-BN0yeBod.js} +1 -1
- package/dist/{use-material-translation-B4Frk3vC.js → use-material-translation-Cqcwkuur.js} +2 -2
- package/docs/components/data-table-filter-and-lists.md +184 -3
- package/docs/components/navigation-and-layout.md +1 -1
- package/docs/guides/app-shell.md +38 -0
- package/docs/guides/backgrounds.md +5 -0
- package/docs/guides/i18n.md +7 -0
- package/docs/guides/server-lists-url.md +114 -1
- package/docs/roadmap.md +16 -0
- package/docs/troubleshooting.md +73 -5
- package/lib/components/ui/display/chip/chip-parts.css +19 -0
- package/package.json +2 -2
|
@@ -48,7 +48,9 @@ field that declares `param` and `style` (`'repeat'`, `'single'` or `'flag'`),
|
|
|
48
48
|
the app's own param, so an older link such as `?archived=1` keeps working.
|
|
49
49
|
A flag alias at `0` (`?archived=0`) means off: it is no condition, so the link
|
|
50
50
|
shows every row. To filter on "No", write `f=archived:is:0`, which the
|
|
51
|
-
serializer does for you.
|
|
51
|
+
serializer does for you. A field declared `off: 'remove'` (a narrowing switch
|
|
52
|
+
such as "Only unassigned") has no "No": its `:is:0` and `=0` are both
|
|
53
|
+
dropped, and the serializer never writes them.
|
|
52
54
|
A range is written `a..b` with either end open (`6..`, `..8`).
|
|
53
55
|
|
|
54
56
|
## Parse it in the loader
|
|
@@ -171,6 +173,107 @@ export default function Students({ loaderData }: Route.ComponentProps) {
|
|
|
171
173
|
- **Dates and times** stay relative in the URL (`f=term:between:this-month`),
|
|
172
174
|
so a bookmark keeps meaning "this month".
|
|
173
175
|
|
|
176
|
+
## A list that is no table: `FilterBar`
|
|
177
|
+
|
|
178
|
+
A feed or a card list has no `ServerTable` to bind the URL for it. `FilterBar`
|
|
179
|
+
does the same: pass it the router's search params and a setter, and it reads
|
|
180
|
+
and writes the query as above (the codec options, `reset` for the cursor, the
|
|
181
|
+
page's other params kept):
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
import { FilterBar } from '@nxgt/material/components';
|
|
185
|
+
import { useSearchParams } from 'react-router';
|
|
186
|
+
|
|
187
|
+
const [params, setParams] = useSearchParams();
|
|
188
|
+
<FilterBar
|
|
189
|
+
model={FEED_FILTERS}
|
|
190
|
+
params={params}
|
|
191
|
+
onParamsChange={(next) => setParams(next, { replace: true })}
|
|
192
|
+
codec={{ keepKnownValues: true }} // the loader's own options
|
|
193
|
+
reset={['after']}
|
|
194
|
+
resultCount={loaderData.total}
|
|
195
|
+
loading={loading}
|
|
196
|
+
/>
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
`useFilterUrlQuery` is the same binding as a hook, for a page that places the
|
|
200
|
+
`Filter` itself (it takes a `UseFilterUrlQueryOptions` and returns a
|
|
201
|
+
`FilterUrlQuery`; `FilterBarProps` and `FilterUrlCodec` name the bar's props
|
|
202
|
+
and the codec options). `ServerTable`'s `filter` uses it, so the two cannot
|
|
203
|
+
drift.
|
|
204
|
+
|
|
205
|
+
## A default the loader computes
|
|
206
|
+
|
|
207
|
+
Some views start narrowed: "Mine only" on for some users, a list showing
|
|
208
|
+
`active` and `upcoming` while nothing is picked. Declare it on the field, so an
|
|
209
|
+
absent param means it, and the URL still holds the user's explicit choices only:
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
const FILTERS = defineFilterModel({
|
|
213
|
+
fields: [
|
|
214
|
+
{ id: 'status', type: 'multi-select', label: 'Status', options,
|
|
215
|
+
default: ['active', 'upcoming'] },
|
|
216
|
+
// The page decides per request: `defaults` passes the value.
|
|
217
|
+
{ id: 'mine', type: 'switch', label: 'Mine only', default: 'page' },
|
|
218
|
+
],
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
export async function loader({ request, context }: Route.LoaderArgs) {
|
|
222
|
+
const query = parseFilterQuery(new URL(request.url).searchParams, FILTERS);
|
|
223
|
+
const defaults = { mine: context.user.prefersMine ? ['1'] : [] };
|
|
224
|
+
const conditions = effectiveConditions(query, FILTERS, LOCKED, { defaults });
|
|
225
|
+
return api.students({ search: query.search, conditions });
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Order is locked, then the user's, then defaulted. `resolveDefaults(query,
|
|
230
|
+
FILTERS, defaults, LOCKED)` also names the fields it defaulted. A default is
|
|
231
|
+
never in `query`, so `isFiltered`, `withoutFilters` and "Clear all" are
|
|
232
|
+
unchanged, and "Clear all" returns the field to its default. Choosing a value
|
|
233
|
+
replaces the default, also a value equal to it, which stays in the URL as
|
|
234
|
+
written. An explicit off of a default-bearing switch is `f=mine:is:0`, or
|
|
235
|
+
`mine=0` for a `style: 'flag'` field; on a field without `default` that `0`
|
|
236
|
+
keeps its old meaning. A link shared between users with different `'page'`
|
|
237
|
+
defaults renders per viewer, by design. A `'page'` field the page gives no value
|
|
238
|
+
for has no default, yet `mine=0` still reads as an explicit off for it. Pass the same `defaults` (and `locked`)
|
|
239
|
+
to `matchQuery` and `valueCounts` in client mode.
|
|
240
|
+
|
|
241
|
+
The page shows the default too: pass the same `defaults` to `ServerTable`'s
|
|
242
|
+
`filter` (`filter={{ model, defaults }}`), `FilterBar` or `Filter`. A defaulted
|
|
243
|
+
value is shown but uncounted, with no ✕ and no announced count, and never
|
|
244
|
+
written to the URL:
|
|
245
|
+
|
|
246
|
+
- a select or multi-select reads as a dashed hint chip, "Status: Active,
|
|
247
|
+
Upcoming · Default". It is a button named "Status: Active, Upcoming, default
|
|
248
|
+
value. Change": it opens the field's editor on the default, like an applied
|
|
249
|
+
chip, and has no ✕. A quick pill reads the same;
|
|
250
|
+
- a switch or checkbox quick toggle is pressed, marked "Default"; pressing it
|
|
251
|
+
writes the explicit off (`mine=0`), which is then counted as a "No" chip;
|
|
252
|
+
- the facets panel and the phone sheet show the default values checked, with
|
|
253
|
+
"Default" beside the field's title. Unchecking or changing one writes an
|
|
254
|
+
explicit choice (counted, in the URL); "Clear all" goes back to the checked
|
|
255
|
+
defaults. The section starts open;
|
|
256
|
+
- "Filters (N)" and the announced count leave defaults out, and "Clear all"
|
|
257
|
+
(which shows from two filters) returns every field to its default.
|
|
258
|
+
|
|
259
|
+
`defaults` must be the loader's own object, or the chips and the rows disagree.
|
|
260
|
+
|
|
261
|
+
## A scope, a refusal and a list without a search
|
|
262
|
+
|
|
263
|
+
- **A scope** (`scope: true` with a `default`, `'page'` for "the current
|
|
264
|
+
period") is the value a list is scoped by and the user changes but never
|
|
265
|
+
clears. The loader treats it as any condition (it is in `query`, and in
|
|
266
|
+
`effectiveConditions`), but `isFiltered`, `withoutFilters` and the bar's
|
|
267
|
+
count and "Clear all" leave it out: `/students?f=period:is:p1&q=ada` resets
|
|
268
|
+
to `?f=period:is:p1`. An invalid value means the default.
|
|
269
|
+
- **A refusal.** When the loader refuses a value, return the message per
|
|
270
|
+
field and pass it as `errors` (`filter={{ model, errors }}`): the chip reads
|
|
271
|
+
"Invalid", described by the message, and the editor and the live region
|
|
272
|
+
show it. Clear it once the user changes the condition.
|
|
273
|
+
- **`search: false`** in `defineFilterModel` drops the search box and frees the
|
|
274
|
+
`q` param: `parseFilterQuery` returns `search: ''` and
|
|
275
|
+
`serializeFilterQuery` never writes it.
|
|
276
|
+
|
|
174
277
|
## Client mode
|
|
175
278
|
|
|
176
279
|
When all the rows are already in the page, skip the loader's fetch and filter
|
|
@@ -198,5 +301,15 @@ page, or the hydrating page renders another count.
|
|
|
198
301
|
`FILTER_MAX_VALUES` or `FILTER_MAX_VALUE_LENGTH`, a slider value off its
|
|
199
302
|
track or an unsafe integer is dropped silently: see
|
|
200
303
|
[a condition in the URL is missing from the filter](../troubleshooting.md#a-condition-in-the-url-is-missing-from-the-filter).
|
|
304
|
+
- A field you switch to `off: 'remove'` stops reading its old `f=x:is:0`
|
|
305
|
+
links as "No": they are dropped silently, so a shared link that meant
|
|
306
|
+
"not x" now shows every row. Leave `off` out when "No" matters.
|
|
307
|
+
- A `default: 'page'` field reads its values from `defaults`: give the loader and
|
|
308
|
+
the page the same record, or the counts and the chips disagree. Compute a
|
|
309
|
+
period-style default in the loader and pass it as a string, never
|
|
310
|
+
`new Date()` in render (a hydration mismatch).
|
|
311
|
+
- Adding `default` to a field changes its `x=0` and `f=x:is:0` links from "no
|
|
312
|
+
condition" (or dropped) to an explicit off, and an absent param from "every
|
|
313
|
+
row" to the default.
|
|
201
314
|
- Parse with the same `locked` list in the loader and in the page, or the two
|
|
202
315
|
read different queries.
|
package/docs/roadmap.md
CHANGED
|
@@ -330,6 +330,22 @@ version they shipped in, and planned entries may name the major they target.
|
|
|
330
330
|
|
|
331
331
|
## Shipped
|
|
332
332
|
|
|
333
|
+
- **One filter model: defaults, a scope, a refusal, `FilterBar`** — a filter
|
|
334
|
+
field says what an absent value means and what role it plays, so a list can
|
|
335
|
+
start narrowed and a page can keep a value the user never clears.
|
|
336
|
+
`default` (values, or `'page'` with `defaults` from the loader) is shown but
|
|
337
|
+
never counted, written to the URL or removed by "Clear all": a hint chip, a
|
|
338
|
+
pressed toggle, or checked values tagged "Default". `scope: true` makes a
|
|
339
|
+
field such as the period a leading pill that "Clear all" and `withoutFilters`
|
|
340
|
+
keep. `off: 'remove'` turns a switch into a plain "only when on" filter.
|
|
341
|
+
`errors` shows a server's refusal on the control, and `search: false` drops
|
|
342
|
+
the search box. `FilterBar` and `useFilterUrlQuery` bind the bar to the URL
|
|
343
|
+
for a feed or a list that is no table, the same binding `ServerTable` uses.
|
|
344
|
+
React only for now; the Vue gap is tracked in issue #1569.
|
|
345
|
+
[Filter bar](components/data-table-filter-and-lists.md#filter-bar) ·
|
|
346
|
+
[A field's default](components/data-table-filter-and-lists.md#a-fields-default) ·
|
|
347
|
+
[A scope field](components/data-table-filter-and-lists.md#a-scope-field) ·
|
|
348
|
+
[Server lists in the URL](guides/server-lists-url.md)
|
|
333
349
|
- **Display polish** — `Badge` is a status badge: a 20 px pill with the
|
|
334
350
|
`label-sm` text and no hover, a `soft` variant (the tone's container under
|
|
335
351
|
the tone's text, 4.5:1) and a `tone` prop for `soft` and `outlined`, taking
|
package/docs/troubleshooting.md
CHANGED
|
@@ -131,6 +131,10 @@ words in your error.
|
|
|
131
131
|
- [The filter URL is not encoded like URLSearchParams](#the-filter-url-is-not-encoded-like-urlsearchparams)
|
|
132
132
|
- [A refused or reset edit stays in the filter bar](#a-refused-or-reset-edit-stays-in-the-filter-bar)
|
|
133
133
|
- [`defineFilterModel: field "level" cannot use "gt" on a select field`](#definefiltermodel-field-level-cannot-use-gt-on-a-select-field)
|
|
134
|
+
- [`defineFilterModel: field "x" can only set addable to false`](https://cdn.jsdelivr.net/npm/@nxgt/material-core@0/docs/troubleshooting.md#definefiltermodel-field-x-can-only-set-addable-to-false)
|
|
135
|
+
- [`defineFilterModel: field "x" default …` and `off …`](#definefiltermodel-field-x-default--and-off-)
|
|
136
|
+
- [`defineFilterModel: field "period" scope needs a default operator taking one value`](#definefiltermodel-field-period-scope-needs-a-default-operator-taking-one-value)
|
|
137
|
+
- [`errors` shows nothing on the filter bar](#errors-shows-nothing-on-the-filter-bar)
|
|
134
138
|
- [`@nxgt/material-core/filter: unknown time zone "…", using the runtime's`](#nxgtmaterial-corefilter-unknown-time-zone--using-the-runtimes)
|
|
135
139
|
- [``Filter: the autocomplete field(s) … have neither `options` nor a `search` in `fieldProps`: their editor has nothing to offer.``](#filter-the-autocomplete-fields--have-neither-options-nor-a-search-in-fieldprops-their-editor-has-nothing-to-offer)
|
|
136
140
|
- [`Filter bar parts must be used inside <Filter variant="bar">`](#filter-bar-parts-must-be-used-inside-filter-variantbar)
|
|
@@ -722,9 +726,20 @@ list) hold shapes.
|
|
|
722
726
|
backdrop layer). Since 3.22 a glyph whose box meets a surface, a text or a
|
|
723
727
|
control, within 8px, is dropped whole (the keep-clear pass), so a gap narrower
|
|
724
728
|
than a glyph shows none.
|
|
725
|
-
**Fix:**
|
|
726
|
-
the backgrounds guide).
|
|
727
|
-
|
|
729
|
+
**Fix:** it is the documented behaviour (see [the drop rule](./guides/backgrounds.md) in
|
|
730
|
+
the backgrounds guide). On a dense page, opt in to `keepClear="gaps"` on the
|
|
731
|
+
`ActivityLayout` (or `AuthCard`): a glyph then stays when its box fits in a gap of
|
|
732
|
+
24px or more between two surfaces, and is still dropped over text, a control or a
|
|
733
|
+
card. A glyph wider than the gap is dropped as before. Or leave open space in the page.
|
|
734
|
+
|
|
735
|
+
```tsx
|
|
736
|
+
import { ActivityLayout } from '@nxgt/material/components';
|
|
737
|
+
import { Background } from '@nxgt/material/backgrounds';
|
|
738
|
+
|
|
739
|
+
<ActivityLayout items={items} backdrop={<Background preset="shapes" />} keepClear="gaps">
|
|
740
|
+
<Outlet />
|
|
741
|
+
</ActivityLayout>
|
|
742
|
+
```
|
|
728
743
|
|
|
729
744
|
### The page and the cards look the same
|
|
730
745
|
|
|
@@ -2460,8 +2475,11 @@ other messages say `must match [A-Za-z0-9_-]+` (a field id is written in the
|
|
|
2460
2475
|
URL), `is declared twice`, `has an unknown type`, `declares an operator twice`,
|
|
2461
2476
|
`uses the param "<p>" of the search` (two fields, or a field and the search,
|
|
2462
2477
|
share a URL param), `can only use style "flag" as a switch or checkbox`,
|
|
2463
|
-
`needs a default operator taking several values for style "repeat"`, `needs a default operator taking one value for style "single"`,
|
|
2464
|
-
|
|
2478
|
+
`needs a default operator taking several values for style "repeat"`, `needs a default operator taking one value for style "single"`, the scope
|
|
2479
|
+
checks (`scope needs a default`, `scope cannot be combined with widens`,
|
|
2480
|
+
`scope needs a single-value type`, `scope default must hold exactly one
|
|
2481
|
+
value`, `can only set scope to true`), and, for a search param, `f` is
|
|
2482
|
+
reserved for the conditions (a model with `search: false` has none).
|
|
2465
2483
|
**Fix:** use the operators the type allows: numbers and sliders compare with
|
|
2466
2484
|
`gt`, `lt`, `gte`, `lte`, dates and times with `before` and `after`; the
|
|
2467
2485
|
first operator you list is the default.
|
|
@@ -2470,6 +2488,56 @@ first operator you list is the default.
|
|
|
2470
2488
|
{ id: 'age', type: 'number', label: 'Age', operators: ['gte', 'lte', 'between'] }
|
|
2471
2489
|
```
|
|
2472
2490
|
|
|
2491
|
+
### `defineFilterModel: field "x" default …` and `off …`
|
|
2492
|
+
|
|
2493
|
+
**When:** the module that calls `defineFilterModel` is imported, with a
|
|
2494
|
+
`default` that is not an array of strings or `"page"`, repeats or does not
|
|
2495
|
+
read a value, is off on a field declared `off: 'remove'`, or sits on a
|
|
2496
|
+
`widens` field, or with an `off` that is not `'remove'` or `'no'`, is on
|
|
2497
|
+
something other than a switch or checkbox, or is `'no'` on a `widens` field.
|
|
2498
|
+
**Why:** a default is applied as the field's condition, so it must be one the
|
|
2499
|
+
URL could hold; and `off` only says what off means for a boolean.
|
|
2500
|
+
**Fix:** see the exact messages and fixes in the core page:
|
|
2501
|
+
[`default …`](https://cdn.jsdelivr.net/npm/@nxgt/material-core@0/docs/troubleshooting.md#definefiltermodel-field-x-default-) and
|
|
2502
|
+
[`off …`](https://cdn.jsdelivr.net/npm/@nxgt/material-core@0/docs/troubleshooting.md#definefiltermodel-field-x-widens-needs-off-remove-and-the-other-off-errors).
|
|
2503
|
+
|
|
2504
|
+
### `defineFilterModel: field "period" scope needs a default operator taking one value`
|
|
2505
|
+
|
|
2506
|
+
**When:** the module that calls `defineFilterModel` is imported, with a
|
|
2507
|
+
`scope: true` field whose default operator (the first of its `operators`)
|
|
2508
|
+
takes several values, such as `in` on a `select`.
|
|
2509
|
+
**Why:** a scope is one value the list is scoped by, shown in a pill
|
|
2510
|
+
("Period: P2"), so its default operator (the first of `operators`, or the
|
|
2511
|
+
type's own) must read exactly one value. The other scope checks name their
|
|
2512
|
+
own rule: `scope needs a default`, `scope needs a single-value type`,
|
|
2513
|
+
`scope default must hold exactly one value`, `scope cannot be combined with
|
|
2514
|
+
widens`.
|
|
2515
|
+
**Fix:** list a one-value operator first, or use a single-value type
|
|
2516
|
+
(`select`, `radio-group`, `chips-single`):
|
|
2517
|
+
|
|
2518
|
+
```ts
|
|
2519
|
+
{ id: 'period', type: 'select', label: 'Period', options: PERIODS,
|
|
2520
|
+
operators: ['is'], scope: true, default: ['p2'] }
|
|
2521
|
+
```
|
|
2522
|
+
|
|
2523
|
+
### `errors` shows nothing on the filter bar
|
|
2524
|
+
|
|
2525
|
+
**When:** a page passes `errors={{ period: 'Too long' }}` (to `Filter`,
|
|
2526
|
+
`FilterBar` or `ServerTable`'s `filter`) and the field's chip, pill or toggle
|
|
2527
|
+
keeps its normal look, with no "Invalid" and nothing announced. Nothing is
|
|
2528
|
+
printed.
|
|
2529
|
+
**Why:** only a field of the model that the page does not lock shows an error:
|
|
2530
|
+
a key that is not a field id (a typo, the search param, an alias), an empty
|
|
2531
|
+
message, or a field in `locked` is left out, since a locked field shows its
|
|
2532
|
+
lock only.
|
|
2533
|
+
**Fix:** key `errors` by the field's `id`, give a non-empty message, and keep
|
|
2534
|
+
the field out of `locked`. `fieldErrors(errors, model, locked)` from
|
|
2535
|
+
`@nxgt/material-core/filter` lists what the bar will show:
|
|
2536
|
+
|
|
2537
|
+
```tsx
|
|
2538
|
+
<ServerTable filter={{ model, locked, errors: { period: 'The period cannot exceed 366 days.' } }} {...table} />
|
|
2539
|
+
```
|
|
2540
|
+
|
|
2473
2541
|
### `@nxgt/material-core/filter: unknown time zone "…", using the runtime's`
|
|
2474
2542
|
|
|
2475
2543
|
**When:** in the console, once, when a date preset (`this-month`) is matched or
|
|
@@ -82,6 +82,25 @@
|
|
|
82
82
|
background: color-mix(in oklab, var(--foreground) 4%, transparent);
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
/* A dashed chip in the error tone (a refused filter): the line and the
|
|
86
|
+
* text take the tone, dashed or selected, hover and focus included. */
|
|
87
|
+
.nxgt-chip[data-dashed][data-tone="error"] {
|
|
88
|
+
--chip-tone: var(--error-text);
|
|
89
|
+
border-color: color-mix(in oklab, var(--error-text) 60%, var(--background));
|
|
90
|
+
color: var(--error-text);
|
|
91
|
+
}
|
|
92
|
+
.nxgt-chip[data-dashed][data-tone="error"]:not([data-selected]):is(
|
|
93
|
+
:hover,
|
|
94
|
+
:focus-visible
|
|
95
|
+
) {
|
|
96
|
+
color: var(--error-text);
|
|
97
|
+
border-color: var(--error-text);
|
|
98
|
+
background: color-mix(in oklab, var(--error-text) 6%, transparent);
|
|
99
|
+
}
|
|
100
|
+
.nxgt-chip[data-dashed][data-tone="error"][data-selected] {
|
|
101
|
+
background: color-mix(in oklab, var(--error-text) 8%, var(--background));
|
|
102
|
+
}
|
|
103
|
+
|
|
85
104
|
.nxgt-chip[data-tone="neutral"] {
|
|
86
105
|
--chip-tone: var(--muted-foreground);
|
|
87
106
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxgt/material",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.28.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"email": "stevegires@gmail.com"
|
|
@@ -362,7 +362,7 @@
|
|
|
362
362
|
"docs:previews": "rm -rf node_modules/.cache/nxgt-preview-free/missing && NXGT_PREVIEW_FREE=1 vitest run --project previews && bun .storybook/preview-free-report.ts"
|
|
363
363
|
},
|
|
364
364
|
"dependencies": {
|
|
365
|
-
"@nxgt/material-core": "^0.
|
|
365
|
+
"@nxgt/material-core": "^0.36.0",
|
|
366
366
|
"@base-ui/react": "^1.6.0",
|
|
367
367
|
"@dnd-kit/core": "^6.3.1",
|
|
368
368
|
"@dnd-kit/sortable": "^10.0.0",
|