@nxgt/material 3.27.0 → 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.
Files changed (100) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +1 -1
  3. package/dist/{async-slice-gAbGnQT8.js → async-slice-DmhasXmq.js} +2 -2
  4. package/dist/{button-Cr9GIIj4.js → button-BjIg_Lz6.js} +1 -1
  5. package/dist/charts.js +3 -3
  6. package/dist/{chip-s11lOfxB.js → chip-DIBIDUZH.js} +1 -1
  7. package/dist/{code-editor-DGIjL8Hr.js → code-editor-2EU-q9FV.js} +4 -4
  8. package/dist/code-editor.js +4 -4
  9. package/dist/{code-language-items-DCOQnaCo.js → code-language-items-C_UeMQMm.js} +2 -2
  10. package/dist/components/ui/display/chip/chip-parts.css +19 -0
  11. package/dist/{components-DgpD3FJo.js → components-Cl3Zyoe-.js} +11757 -11283
  12. package/dist/components.js +26 -26
  13. package/dist/{dialog-Crvg-MN5.js → dialog-LtXwv6N3.js} +1 -1
  14. package/dist/dnd.js +1 -1
  15. package/dist/{dropdown-menu-B71aggp0.js → dropdown-menu-ppMPD12c.js} +1 -1
  16. package/dist/editor.js +7 -7
  17. package/dist/{empty-state-8LddxMU4.js → empty-state-X_nikpBS.js} +1 -1
  18. package/dist/hooks.js +2 -2
  19. package/dist/{i18n-provider-BDi9-6DO.js → i18n-provider-BfyDgd2o.js} +1 -1
  20. package/dist/i18n.js +5 -5
  21. package/dist/{icon-badge-DG7wLnHh.js → icon-badge-oLBh3Mlk.js} +1 -1
  22. package/dist/lib/components/ui/collections/facet-group/facet-group.d.ts +1 -1
  23. package/dist/lib/components/ui/collections/facet-group/types.d.ts +2 -0
  24. package/dist/lib/components/ui/collections/filter/bar/filter-bar-classes.d.ts +11 -1
  25. package/dist/lib/components/ui/collections/filter/bar/{filter-bar.d.ts → filter-bar-view.d.ts} +2 -2
  26. package/dist/lib/components/ui/collections/filter/bar/partials/filter-clear-all.d.ts +2 -2
  27. package/dist/lib/components/ui/collections/filter/bar/partials/filter-quick-pill.d.ts +4 -1
  28. package/dist/lib/components/ui/collections/filter/bar/partials/filter-quick-toggle.d.ts +3 -0
  29. package/dist/lib/components/ui/collections/filter/bar/partials/filter-scope-list.d.ts +6 -0
  30. package/dist/lib/components/ui/collections/filter/bar/partials/filter-scope-pill.d.ts +13 -0
  31. package/dist/lib/components/ui/collections/filter/bar/partials/use-quick-condition.d.ts +19 -0
  32. package/dist/lib/components/ui/collections/filter/bar/partials/use-quick-pill.d.ts +30 -0
  33. package/dist/lib/components/ui/collections/filter/bar/use-filter-bar-wiring.d.ts +5 -0
  34. package/dist/lib/components/ui/collections/filter/editors/boolean-editor.d.ts +3 -2
  35. package/dist/lib/components/ui/collections/filter/editors/checkbox-editor.d.ts +1 -1
  36. package/dist/lib/components/ui/collections/filter/editors/switch-editor.d.ts +1 -1
  37. package/dist/lib/components/ui/collections/filter/facets/filter-facets.d.ts +2 -2
  38. package/dist/lib/components/ui/collections/filter/filter-bar-url.d.ts +51 -0
  39. package/dist/lib/components/ui/collections/filter/index.d.ts +2 -0
  40. package/dist/lib/components/ui/collections/filter/internal.d.ts +3 -1
  41. package/dist/lib/components/ui/collections/filter/query/filter-results-status.d.ts +4 -1
  42. package/dist/lib/components/ui/collections/filter/query/types.d.ts +21 -3
  43. package/dist/lib/components/ui/collections/{server-table → filter/query}/use-answer-wait.d.ts +3 -1
  44. package/dist/lib/components/ui/collections/filter/query/use-apply-now.d.ts +6 -0
  45. package/dist/lib/components/ui/collections/filter/query/use-defaults.d.ts +15 -0
  46. package/dist/lib/components/ui/collections/filter/query/use-field-error.d.ts +5 -0
  47. package/dist/lib/components/ui/collections/filter/query/use-filter-query-options.d.ts +14 -0
  48. package/dist/lib/components/ui/collections/filter/query/use-filter-query-value.d.ts +2 -0
  49. package/dist/lib/components/ui/collections/filter/query/use-filter-query.d.ts +6 -2
  50. package/dist/lib/components/ui/collections/filter/query/use-filter-url-query.d.ts +52 -0
  51. package/dist/lib/components/ui/collections/filter/query/use-refusal-announcement.d.ts +10 -0
  52. package/dist/lib/components/ui/collections/filter/sections/chips/condition-panel.d.ts +2 -1
  53. package/dist/lib/components/ui/collections/filter/sections/chips/filter-chip-body.d.ts +4 -2
  54. package/dist/lib/components/ui/collections/filter/sections/chips/filter-chip-list.d.ts +3 -2
  55. package/dist/lib/components/ui/collections/filter/sections/chips/filter-default-chip.d.ts +14 -0
  56. package/dist/lib/components/ui/collections/filter/sections/chips/filter-default-group.d.ts +16 -0
  57. package/dist/lib/components/ui/collections/filter/sections/chips/filter-default-parts.d.ts +12 -0
  58. package/dist/lib/components/ui/collections/filter/sections/chips/filter-error-parts.d.ts +22 -0
  59. package/dist/lib/components/ui/collections/filter/sections/chips/use-control-description.d.ts +13 -0
  60. package/dist/lib/components/ui/collections/filter/sections/filter-facet-section.d.ts +5 -0
  61. package/dist/lib/components/ui/collections/filter/sections/filter-search.d.ts +4 -3
  62. package/dist/lib/components/ui/collections/filter/sections/filter-sections-classes.d.ts +8 -0
  63. package/dist/lib/components/ui/collections/filter/sections/renderers/choice-facet.d.ts +1 -1
  64. package/dist/lib/components/ui/collections/filter/sections/use-filter-shortcuts.d.ts +4 -3
  65. package/dist/lib/components/ui/collections/server-table/partials/server-table-empty-chips.d.ts +1 -1
  66. package/dist/lib/components/ui/collections/server-table/types.d.ts +13 -1
  67. package/dist/lib/components/ui/collections/server-table/use-filter-params.d.ts +5 -4
  68. package/dist/lib/components/ui/collections/server-table/use-table-answer.d.ts +1 -1
  69. package/dist/lib/i18n/resources/en/en.d.ts +11 -0
  70. package/dist/lib/i18n/resources/en/material.json.d.ts +11 -0
  71. package/dist/lib/i18n/resources/fr/fr.d.ts +11 -0
  72. package/dist/lib/i18n/resources/fr/material.json.d.ts +11 -0
  73. package/dist/lib/i18n/resources/resources.d.ts +22 -0
  74. package/dist/lib/i18n/translator.d.ts +1 -1
  75. package/dist/lib.js +1 -1
  76. package/dist/main.js +43 -43
  77. package/dist/map.js +4 -4
  78. package/dist/material.css +1 -1
  79. package/dist/pdf-reader.js +1 -1
  80. package/dist/print.js +2 -2
  81. package/dist/{public-QUiFLEoj.js → public-BeM7-x-m.js} +6 -6
  82. package/dist/{rich-code-block-CjD54THr.js → rich-code-block-B4TIjZRA.js} +4 -4
  83. package/dist/{sortable-provider-DYnyzfgR.js → sortable-provider-Dcsw0pgW.js} +1 -1
  84. package/dist/{spinner-CJtTRNAX.js → spinner-1Ia6Akhy.js} +1 -1
  85. package/dist/{stored-language-ClkfkueV.js → stored-language-DoWMr969.js} +22 -0
  86. package/dist/theme-picker.js +2 -2
  87. package/dist/{theme-toggle-ufQL3SO6.js → theme-toggle-GlKtdgYz.js} +1 -1
  88. package/dist/{toolbar-items-DlioFgpM.js → toolbar-items-C1GdN9P9.js} +2 -2
  89. package/dist/{translate-material-O-uasx3u.js → translate-material-BneuBHXZ.js} +2 -2
  90. package/dist/{translator-C0uxN1Nh.js → translator-D88BbpXZ.js} +1 -1
  91. package/dist/{use-chat-composer-DzVO5mwz.js → use-chat-composer-beyfZPBD.js} +12 -12
  92. package/dist/{use-heading-spy-BHCVdJfr.js → use-heading-spy-BN0yeBod.js} +1 -1
  93. package/dist/{use-material-translation-B4Frk3vC.js → use-material-translation-Cqcwkuur.js} +2 -2
  94. package/docs/components/data-table-filter-and-lists.md +184 -3
  95. package/docs/guides/i18n.md +7 -0
  96. package/docs/guides/server-lists-url.md +114 -1
  97. package/docs/roadmap.md +16 -0
  98. package/docs/troubleshooting.md +59 -2
  99. package/lib/components/ui/display/chip/chip-parts.css +19 -0
  100. package/package.json +2 -2
@@ -1,3 +1,3 @@
1
- import { t as e } from "./public-QUiFLEoj.js";
1
+ import { t as e } from "./public-BeM7-x-m.js";
2
2
  import { configurePdfReader as t } from "@nxgt/material-core/pdf-reader";
3
3
  export { e as PdfReader, t as configurePdfReader };
package/dist/print.js CHANGED
@@ -1,5 +1,5 @@
1
- import { r as e } from "./i18n-provider-BDi9-6DO.js";
2
- import { t } from "./translate-material-O-uasx3u.js";
1
+ import { r as e } from "./i18n-provider-BfyDgd2o.js";
2
+ import { t } from "./translate-material-BneuBHXZ.js";
3
3
  import { Children as n, createContext as r, isValidElement as i, useCallback as a, useContext as o, useEffect as s, useMemo as c, useState as l } from "react";
4
4
  import { jsx as u, jsxs as d } from "react/jsx-runtime";
5
5
  import { resolvePdfFileName as f } from "@nxgt/material-core/pdf-file-name";
@@ -6,18 +6,18 @@ import { c as i, i as a, l as o, n as s, o as c, s as l } from "./dist-lmHmz_bu.
6
6
  import { a as u, o as d } from "./tooltip-CkttDIab.js";
7
7
  import { i as f } from "./dist-C4wtXCjf.js";
8
8
  import { r as p, t as m } from "./in-dialog-Vwkyp-M5.js";
9
- import { C as h, D as g, E as _, O as v, S as y, T as b, _ as x, f as S, g as ee, k as te, r as ne, t as C, u as re, w } from "./dialog-Crvg-MN5.js";
10
- import { c as T, d as ie, f as E, i as ae, l as oe, n as se, o as D, t as ce, u as O } from "./dropdown-menu-B71aggp0.js";
11
- import { S as le, a as k, d as A, g as ue, h as de, i as fe, m as pe, n as j, r as me, t as M, u as he, w as ge } from "./toolbar-items-DlioFgpM.js";
12
- import { t as N } from "./use-material-translation-B4Frk3vC.js";
13
- import { t as _e } from "./button-Cr9GIIj4.js";
9
+ import { C as h, D as g, E as _, O as v, S as y, T as b, _ as x, f as S, g as ee, k as te, r as ne, t as C, u as re, w } from "./dialog-LtXwv6N3.js";
10
+ import { c as T, d as ie, f as E, i as ae, l as oe, n as se, o as D, t as ce, u as O } from "./dropdown-menu-ppMPD12c.js";
11
+ import { S as le, a as k, d as A, g as ue, h as de, i as fe, m as pe, n as j, r as me, t as M, u as he, w as ge } from "./toolbar-items-C1GdN9P9.js";
12
+ import { t as N } from "./use-material-translation-Cqcwkuur.js";
13
+ import { t as _e } from "./button-BjIg_Lz6.js";
14
14
  import { n as ve, t as ye } from "./icon-button-ifTpXt0A.js";
15
15
  import { t as be } from "./useEvent-2fKq-6w8.js";
16
16
  import { n as xe, t as Se } from "./toggle-group-BMJNcFAC.js";
17
17
  import { o as Ce } from "./escape-owner-C4W_sOAU.js";
18
18
  import { n as we } from "./use-slice-reducer-BGkeGtML.js";
19
19
  import { r as Te } from "./use-csp-nonce-ZKs9k-ov.js";
20
- import { n as Ee, t as De } from "./empty-state-8LddxMU4.js";
20
+ import { n as Ee, t as De } from "./empty-state-X_nikpBS.js";
21
21
  import { t as Oe } from "./client-only.utils-3nHNzCki.js";
22
22
  import { t as ke } from "./use-scroll-away-PQRFZkT2.js";
23
23
  import { ArrowLeft as Ae, BookOpenText as je, ChevronDown as Me, ChevronLeft as Ne, ChevronRight as Pe, ChevronUp as Fe, Download as Ie, Eraser as Le, FileText as Re, FileWarning as ze, FileX2 as Be, Highlighter as Ve, KeyRound as He, LayoutGrid as Ue, List as We, Loader2 as Ge, Maximize as Ke, MessageSquareText as qe, Minimize as Je, MoreVertical as Ye, MousePointer2 as Xe, MoveHorizontal as Ze, PanelLeft as Qe, Pen as $e, PencilLine as et, Printer as tt, RotateCw as nt, Search as rt, StickyNote as it, Type as at, Underline as ot, Undo2 as st, Volume2 as ct, X as lt, XIcon as ut, ZoomIn as dt, ZoomOut as ft } from "lucide-react";
@@ -1,7 +1,7 @@
1
1
  import { t as e } from "./tooltip-CkttDIab.js";
2
- import { c as t, n, t as r } from "./dropdown-menu-B71aggp0.js";
3
- import { t as i } from "./use-material-translation-B4Frk3vC.js";
4
- import { a, i as o, n as s, o as c, t as l } from "./code-language-items-DCOQnaCo.js";
2
+ import { c as t, n, t as r } from "./dropdown-menu-ppMPD12c.js";
3
+ import { t as i } from "./use-material-translation-Cqcwkuur.js";
4
+ import { a, i as o, n as s, o as c, t as l } from "./code-language-items-C_UeMQMm.js";
5
5
  import { i as u, n as d, r as f, s as p } from "./dist-C3GR7kJd.js";
6
6
  import { t as m } from "./code-block-spec-CGLKRhwD.js";
7
7
  import { Check as h, ChevronDown as g, Copy as _, Maximize2 as v, WrapText as y } from "lucide-react";
@@ -143,7 +143,7 @@ function M({ node: e, editor: t, updateAttributes: n, editable: r, wrap: a, onWr
143
143
  }
144
144
  //#endregion
145
145
  //#region lib/components/ui/widgets/editor/extensions/code/code-block-open.tsx
146
- var N = x(() => import("./code-editor-DGIjL8Hr.js").then((e) => e.n).then((e) => ({ default: e.CodeEditor })));
146
+ var N = x(() => import("./code-editor-2EU-q9FV.js").then((e) => e.n).then((e) => ({ default: e.CodeEditor })));
147
147
  function P() {
148
148
  let { t: e } = i();
149
149
  return /* @__PURE__ */ T("div", {
@@ -1,6 +1,6 @@
1
1
  import { t as e } from "./utils-BLfntITJ.js";
2
2
  import { t } from "./dist-DVRQkaTK.js";
3
- import { t as n } from "./use-material-translation-B4Frk3vC.js";
3
+ import { t as n } from "./use-material-translation-Cqcwkuur.js";
4
4
  import { t as r } from "./icon-button-ifTpXt0A.js";
5
5
  import { t as i } from "./compose-refs-DZ0iIVF-.js";
6
6
  import { GripVertical as a } from "lucide-react";
@@ -1,6 +1,6 @@
1
1
  import { t as e } from "./utils-BLfntITJ.js";
2
2
  import { o as t } from "./tooltip-CkttDIab.js";
3
- import { t as n } from "./use-material-translation-B4Frk3vC.js";
3
+ import { t as n } from "./use-material-translation-Cqcwkuur.js";
4
4
  import { jsx as r } from "react/jsx-runtime";
5
5
  //#region lib/components/ui/feedback/spinner/spinner.tsx
6
6
  var i = t("transition-all duration-500", {
@@ -1278,6 +1278,17 @@ var t = {
1278
1278
  until: "Until {value}",
1279
1279
  "up-to": "Up to {value}"
1280
1280
  },
1281
+ default: {
1282
+ label: "Default",
1283
+ hint: "Default value, in effect until you choose one",
1284
+ tag: "· {default}",
1285
+ change: "{filter}, default value. Change"
1286
+ },
1287
+ error: {
1288
+ tag: "Invalid",
1289
+ label: "Invalid: {message}",
1290
+ announce: "{field}: {message}"
1291
+ },
1281
1292
  bar: {
1282
1293
  label: "Filters",
1283
1294
  search: "Search",
@@ -3930,6 +3941,17 @@ var t = {
3930
3941
  until: "Jusqu'au {value}",
3931
3942
  "up-to": "Jusqu'à {value}"
3932
3943
  },
3944
+ default: {
3945
+ label: "Par défaut",
3946
+ hint: "Valeur par défaut, appliquée tant que vous n'en choisissez pas une",
3947
+ tag: "· {default}",
3948
+ change: "{filter}, valeur par défaut. Modifier"
3949
+ },
3950
+ error: {
3951
+ tag: "Invalide",
3952
+ label: "Invalide : {message}",
3953
+ announce: "{field} : {message}"
3954
+ },
3933
3955
  bar: {
3934
3956
  label: "Filtres",
3935
3957
  search: "Rechercher",
@@ -1,10 +1,10 @@
1
1
  import { t as e } from "./utils-BLfntITJ.js";
2
2
  import { a as t, i as n, n as r, o as i, r as a, t as o } from "./bubble-input-B7BVd9fX.js";
3
- import { t as s } from "./use-material-translation-B4Frk3vC.js";
3
+ import { t as s } from "./use-material-translation-Cqcwkuur.js";
4
4
  import { a as c, r as l } from "./icon-sprites-CaYXzOL4.js";
5
5
  import { t as u } from "./use-theme-DMcyJ52N.js";
6
6
  import { a as d, r as f } from "./theme-context-Cofk6vgs.js";
7
- import { t as p } from "./theme-toggle-ufQL3SO6.js";
7
+ import { t as p } from "./theme-toggle-GlKtdgYz.js";
8
8
  import { n as m, t as h } from "./nxgt-theme-ZS945iMP.js";
9
9
  import { Check as g } from "lucide-react";
10
10
  import { useContext as _ } from "react";
@@ -1,4 +1,4 @@
1
- import { t as e } from "./use-material-translation-B4Frk3vC.js";
1
+ import { t as e } from "./use-material-translation-Cqcwkuur.js";
2
2
  import { i as t } from "./theme-store-CSl51uNH.js";
3
3
  import { n, t as r } from "./use-theme-DMcyJ52N.js";
4
4
  import { r as i } from "./theme-context-Cofk6vgs.js";
@@ -4,9 +4,9 @@ import { a as i, c as a, i as o, l as s, r as c, s as l } from "./dist-lmHmz_bu.
4
4
  import { b as u, c as d, d as f, f as p, l as m, t as h, u as g, x as _ } from "./tooltip-CkttDIab.js";
5
5
  import { i as v, n as y, r as b, t as x } from "./dist-C4wtXCjf.js";
6
6
  import { a as S, c as C, l as w, o as T, r as ee, s as te, t as E } from "./in-dialog-Vwkyp-M5.js";
7
- import { A as ne, M as re, N as ie, b as ae, d as oe, f as se, i as ce, j as le, l as ue, n as de, o as fe, r as pe, t as me, u as he, y as ge } from "./dropdown-menu-B71aggp0.js";
7
+ import { A as ne, M as re, N as ie, b as ae, d as oe, f as se, i as ce, j as le, l as ue, n as de, o as fe, r as pe, t as me, u as he, y as ge } from "./dropdown-menu-ppMPD12c.js";
8
8
  import { a as _e, n as ve, r as ye, t as be } from "./dist-B7RjY7SL.js";
9
- import { t as xe } from "./use-material-translation-B4Frk3vC.js";
9
+ import { t as xe } from "./use-material-translation-Cqcwkuur.js";
10
10
  import { t as Se } from "./warn-once-C_e47JM8.js";
11
11
  import { o as Ce } from "./escape-owner-C4W_sOAU.js";
12
12
  import { ChevronLeft as we, ChevronRightIcon as Te, MoreVertical as Ee } from "lucide-react";
@@ -1,5 +1,5 @@
1
- import "./stored-language-ClkfkueV.js";
2
- import { a as e, r as t } from "./translator-C0uxN1Nh.js";
1
+ import "./stored-language-DoWMr969.js";
2
+ import { a as e, r as t } from "./translator-D88BbpXZ.js";
3
3
  //#region lib/i18n/translate-material.ts
4
4
  function n(n, r, i, a) {
5
5
  if (n) for (let e of /* @__PURE__ */ new Set([n, n.split("-")[0]])) {
@@ -1,5 +1,5 @@
1
1
  import { t as e } from "./is-production-GLw59WU8.js";
2
- import { a as t, i as n, r, t as i } from "./stored-language-ClkfkueV.js";
2
+ import { a as t, i as n, r, t as i } from "./stored-language-DoWMr969.js";
3
3
  import { t as a } from "./get-CTzJAwfc.js";
4
4
  //#region ../../node_modules/@formatjs/fast-memoize/index.js
5
5
  function o(e, t) {
@@ -7,21 +7,21 @@ import { a as o, c as s, i as c, l, n as u, o as d, r as f, s as p, t as m } fro
7
7
  import { _ as h, a as g, g as _, h as v, m as y, o as b, p as x, t as S, v as C, y as w } from "./tooltip-CkttDIab.js";
8
8
  import { a as T, i as E, n as D, r as O, t as k } from "./dist-C4wtXCjf.js";
9
9
  import { i as A, n as ee, t as j } from "./in-dialog-Vwkyp-M5.js";
10
- import { A as M, C as N, D as te, E as ne, O as P, S as re, T as ie, _ as F, b as ae, f as oe, g as se, k as I, l as ce, m as le, n as ue, o as de, r as fe, s as pe, t as me, u as he, v as ge, w as _e, x as ve, y as ye } from "./dialog-Crvg-MN5.js";
10
+ import { A as M, C as N, D as te, E as ne, O as P, S as re, T as ie, _ as F, b as ae, f as oe, g as se, k as I, l as ce, m as le, n as ue, o as de, r as fe, s as pe, t as me, u as he, v as ge, w as _e, x as ve, y as ye } from "./dialog-LtXwv6N3.js";
11
11
  import { A as be, C as xe, D as Se, E as Ce, O as we, S as Te, T as Ee, _ as De, a as Oe, b as ke, d as Ae, f as je, g as Me, h as Ne, i as Pe, k as Fe, l as Ie, m as Le, n as Re, o as ze, p as Be, r as Ve, s as He, t as Ue, u as We, v as Ge, w as Ke, x as qe, y as Je } from "./floating-ui.dom-KkZSW8_E.js";
12
- import { D as Ye, E as Xe, N as Ze, O as Qe, c as $e, d as et, f as tt, i as nt, k as rt, m as it, n as at, o as ot, p as st, t as ct, u as lt } from "./dropdown-menu-B71aggp0.js";
12
+ import { D as Ye, E as Xe, N as Ze, O as Qe, c as $e, d as et, f as tt, i as nt, k as rt, m as it, n as at, o as ot, p as st, t as ct, u as lt } from "./dropdown-menu-ppMPD12c.js";
13
13
  import { a as ut, i as dt, n as ft } from "./field-blur-context-Cft9s7Hg.js";
14
- import { C as pt, S as mt, a as ht, c as gt, d as _t, l as vt, n as yt, o as bt, s as xt, u as St } from "./toolbar-items-DlioFgpM.js";
14
+ import { C as pt, S as mt, a as ht, c as gt, d as _t, l as vt, n as yt, o as bt, s as xt, u as St } from "./toolbar-items-C1GdN9P9.js";
15
15
  import { a as Ct, i as wt, n as Tt, o as Et, t as Dt } from "./bubble-input-B7BVd9fX.js";
16
16
  import { i as Ot } from "./dist-B7RjY7SL.js";
17
- import "./stored-language-ClkfkueV.js";
18
- import { a as kt } from "./translator-C0uxN1Nh.js";
17
+ import "./stored-language-DoWMr969.js";
18
+ import { a as kt } from "./translator-D88BbpXZ.js";
19
19
  import { h as At } from "./get-CTzJAwfc.js";
20
20
  import { t as jt } from "./isEmpty-F_g3jLf8.js";
21
- import { t as L } from "./use-material-translation-B4Frk3vC.js";
22
- import { t as Mt } from "./spinner-CJtTRNAX.js";
21
+ import { t as L } from "./use-material-translation-Cqcwkuur.js";
22
+ import { t as Mt } from "./spinner-1Ia6Akhy.js";
23
23
  import { a as Nt } from "./button-tones-BwuVvAmd.js";
24
- import { a as Pt, i as Ft, n as It, r as Lt, t as Rt } from "./button-Cr9GIIj4.js";
24
+ import { a as Pt, i as Ft, n as It, r as Lt, t as Rt } from "./button-BjIg_Lz6.js";
25
25
  import { t as R } from "./icon-button-ifTpXt0A.js";
26
26
  import { a as zt, r as Bt } from "./icon-sprites-CaYXzOL4.js";
27
27
  import { t as Vt } from "./use-ssr-media-query-C6M5m8sf.js";
@@ -35,10 +35,10 @@ import { a as tn, i as nn, o as rn } from "./escape-owner-C4W_sOAU.js";
35
35
  import { n as an } from "./use-slice-reducer-BGkeGtML.js";
36
36
  import { t as on } from "./browser-only-DQ94lWF8.js";
37
37
  import { t as sn } from "./compose-refs-DZ0iIVF-.js";
38
- import { n as cn, r as ln, t as un } from "./chip-s11lOfxB.js";
39
- import { n as dn, t as fn } from "./icon-badge-DG7wLnHh.js";
38
+ import { n as cn, r as ln, t as un } from "./chip-DIBIDUZH.js";
39
+ import { n as dn, t as fn } from "./icon-badge-oLBh3Mlk.js";
40
40
  import { r as pn } from "./use-csp-nonce-ZKs9k-ov.js";
41
- import { t as mn } from "./empty-state-8LddxMU4.js";
41
+ import { t as mn } from "./empty-state-X_nikpBS.js";
42
42
  import { t as hn } from "./skeleton-DSdtr2qc.js";
43
43
  import { H as gn, V as _n, X as vn, bt as yn, it as bn, lt as xn, n as Sn, t as Cn, xt as wn, yt as Tn } from "./dist-moutbfbd.js";
44
44
  import { t as En } from "./markdown-Dw_uH7j7.js";
@@ -10782,7 +10782,7 @@ var CS = (e = {}) => Dn({
10782
10782
  let [{ default: t }, { lowlight: n }, { richCodeBlock: r }] = await Promise.all([
10783
10783
  import("./dist-CRMFGjng.js"),
10784
10784
  import("./lowlight-OtcjU2Sa.js"),
10785
- import("./rich-code-block-CjD54THr.js")
10785
+ import("./rich-code-block-B4TIjZRA.js")
10786
10786
  ]);
10787
10787
  return [r(t, {
10788
10788
  lowlight: n,
@@ -1,4 +1,4 @@
1
- import { r as e } from "./i18n-provider-BDi9-6DO.js";
1
+ import { r as e } from "./i18n-provider-BfyDgd2o.js";
2
2
  import { _ as t, g as n, h as r, n as i, v as a, y as o } from "./number.utils-BG_eEt5g.js";
3
3
  import { useCallback as s, useEffect as c, useMemo as l, useRef as u } from "react";
4
4
  //#region ../../node_modules/date-fns/locale/fr/_lib/formatDistance.js
@@ -1,5 +1,5 @@
1
- import { r as e } from "./i18n-provider-BDi9-6DO.js";
2
- import { t } from "./translate-material-O-uasx3u.js";
1
+ import { r as e } from "./i18n-provider-BfyDgd2o.js";
2
+ import { t } from "./translate-material-BneuBHXZ.js";
3
3
  import { useCallback as n } from "react";
4
4
  //#region lib/i18n/use-material-translation.ts
5
5
  function r() {
@@ -878,7 +878,7 @@ Its props are `ServerTableProps<T>`; `filter` is a `ServerTableFilter`,
878
878
  | `rows`, `totalCount`, `pageInfo` | the connection's page | — | `pageInfo: { endCursor?, hasNextPage }`; `rows` stable per answer |
879
879
  | `pageSize` | `number` | — | the footer's range ("21–40 of 45") |
880
880
  | `getRowId` | `(row) => string` | — | the selection's key |
881
- | `filter` | `ServerTableFilter` | — | `{ model, locked?, fieldProps?, variant?, sheetTrigger?, quick?, density?, shortcuts?, previewCount?, counts?, liveDelayMs?, label?, searchLabel?, searchPlaceholder?, searchApply?, searchMinLength?, codec? }` |
881
+ | `filter` | `ServerTableFilter` | — | `{ model, locked?, fieldProps?, variant?, sheetTrigger?, quick?, density?, shortcuts?, previewCount?, counts?, liveDelayMs?, label?, searchLabel?, searchPlaceholder?, searchApply?, searchMinLength?, codec?, defaults?, errors? }` |
882
882
  | `filter.codec` | `{ keepKnownValues?, encoding? }` | — | how the page reads and writes the URL: pass the loader's `keepKnownValues` / `encoding` so chips and rows agree |
883
883
  | `title` | `ReactNode` | — | the band's title (a string is an `<h2>`). Leave it out when the page's `h1` is elsewhere (the app bar's): the band then has no heading |
884
884
  | `description` | `ReactNode` | — | a muted line under the `title`, or alone above the band's controls without one; the `actions` stay at the band's end |
@@ -1252,8 +1252,12 @@ overload per kind, so `<Filter variant="bar" />` does not compile).
1252
1252
  `query` / `defaultQuery` + `onQueryChange` carry the user's query, never a
1253
1253
  locked condition; `density` (a `FilterDensity`: `'comfortable'` or
1254
1254
  `'compact'`, the controls' size and the gap between them), `bandDensity`
1255
- (`'comfortable'` or `'compact'`, the bar's own vertical room: see below), `label`, `searchLabel`, `searchPlaceholder`, `liveDelayMs`
1256
- (200 ms), `searchApply` and `searchMinLength` tune it. A locked chip is a focusable group named by its text ("Year is 2026-2027"), with "Locked: <reason>" as its description. The other variants
1255
+ (`'comfortable'` or `'compact'`, the bar's own vertical room: see below), `label`,
1256
+ `searchLabel`, `searchPlaceholder`, `liveDelayMs`
1257
+ (200 ms), `searchApply` and `searchMinLength` tune it; `defaults` gives the
1258
+ `default: 'page'` fields their values (see [A field's default](#a-fields-default))
1259
+ and `errors` shows a server's refusal of a condition (see
1260
+ [A refused condition](#a-refused-condition)). A locked chip is a focusable group named by its text ("Year is 2026-2027"), with "Locked: <reason>" as its description. The other variants
1257
1261
  and `FilterProps` are unchanged.
1258
1262
 
1259
1263
  #### A compact band for a list inside a page section
@@ -1373,6 +1377,59 @@ function StudentsFilter({ total }: { total: number }) {
1373
1377
  }
1374
1378
  ```
1375
1379
 
1380
+ #### Filter bar outside a table
1381
+
1382
+ A feed or a list that is no table hosts the same bar with `FilterBar`: it
1383
+ reads its query from the search params and writes it back, so there is no
1384
+ `parseFilterQuery` / `serializeFilterQuery` to wire. It is the bar of
1385
+ `ServerTable`'s band: the search, the chips, "Clear all", the count announced
1386
+ once the page answers, "Filters (N)" opening the sheet on a phone, and a
1387
+ `widens` field (a "Show archived" switch) as a toggle, never a chip.
1388
+
1389
+ ```tsx
1390
+ import { FilterBar } from '@nxgt/material/components';
1391
+ import { useSearchParams } from 'react-router';
1392
+
1393
+ function Notifications({ items }: { items: Notification[] }) {
1394
+ const [params, setParams] = useSearchParams();
1395
+ return (
1396
+ <>
1397
+ <FilterBar
1398
+ model={NOTIFICATION_FILTERS}
1399
+ params={params}
1400
+ onParamsChange={(next) => setParams(next, { replace: true })}
1401
+ resultCount={items.length}
1402
+ />
1403
+ <ul>{items.map((n) => <NotificationRow key={n.id} {...n} />)}</ul>
1404
+ </>
1405
+ );
1406
+ }
1407
+ ```
1408
+
1409
+ - **Props** (`FilterBarProps`). `model`, `params`, `onParamsChange`, then
1410
+ `locked`, `codec` (a `FilterUrlCodec`: `keepKnownValues`, `encoding`, the
1411
+ loader's own options), `reset` (the params dropped on every write, a cursor
1412
+ or a page number), `defaults` (the values of the `default: 'page'` fields,
1413
+ the loader's own; see [A field's default](#a-fields-default)),
1414
+ `resultCount`, `loading`; every other prop of `Filter variant="bar"`
1415
+ (`quick`, `counts`, `previewCount`, `fieldProps`, `density`, `shortcuts`,
1416
+ `searchApply`, `searchMinLength`, `errors`, `label`...) passes through. There is no `query`,
1417
+ `defaultQuery` nor `onQueryChange`: the URL is the query.
1418
+ - **One write.** An edit writes the search, every `f=` and the aliased params,
1419
+ drops the `reset` ones and keeps the page's others (a tab, a sort).
1420
+ `onParamsChange` receives the next params for your router; the bar then
1421
+ holds the count back until the router has taken them, and until `loading`
1422
+ ends. Pass `replace`, so Back leaves the list rather than undoing every chip;
1423
+ a Back or a link that changes the params shows at once.
1424
+ - **The hook.** `useFilterUrlQuery(options)` takes a
1425
+ `UseFilterUrlQueryOptions` (`{ model, params, onParamsChange, locked,
1426
+ defaults, codec, reset }`) and returns a `FilterUrlQuery` (`{ query,
1427
+ onQueryChange, waiting, heading }`): the bar's URL half, for a page that
1428
+ lays `Filter` out itself.
1429
+ `ServerTable` uses the same hook, so a list and a table write the same URL.
1430
+ - **Not a router.** Nothing is imported from one: `params` is a
1431
+ `URLSearchParams`, and `onParamsChange` is yours.
1432
+
1376
1433
  #### Locked conditions
1377
1434
 
1378
1435
  A condition the page imposes (a class's roster, the school year) goes in
@@ -1411,6 +1468,116 @@ for you), pass the table `announceEmpty={false}` and `announceLoading={false}`:
1411
1468
  the bar's live region tells the count and "no results", so the table's own
1412
1469
  status stays quiet and the answer is said once.
1413
1470
 
1471
+ #### A field's default
1472
+
1473
+ A field declared with a `default` (a fixed list, or `'page'` with a `defaults`
1474
+ prop the page computes per request, the loader's own) narrows the list while the
1475
+ user has chosen nothing for it. `Filter`, `FilterBar` and `ServerTable`'s
1476
+ `filter` take `defaults`; the display is:
1477
+
1478
+ | Field | Shown as |
1479
+ | --- | --- |
1480
+ | select, multi-select, any non-quick field | a hint chip in the active filters, "Status: Active, Upcoming · Default": dashed, no ✕, a button named "Status: Active, Upcoming, default value. Change" that opens the field's editor on the default, like an applied chip |
1481
+ | quick pill | the pill reads the default, tagged "· Default" (`material.filter.default.tag`, for the eye only: the pill is described "Default value, …"), and opens its editor, which shows the default checked; no ✕ until the user picks a value |
1482
+ | switch or checkbox quick toggle | pressed (`aria-pressed`), marked "Default" and described so; pressing writes an explicit "No" |
1483
+ | facets panel and phone sheet | the default values checked, "Default" beside the field's title (`FacetGroup`'s `hint`); the section starts open |
1484
+
1485
+ A default is never in `query`: it is not counted in "Filters (N)" or the
1486
+ announcement. Choosing or changing a value replaces it (removing the last value
1487
+ returns to it). "Clear all" returns the fields to their defaults. A locked field shows
1488
+ its lock only. The words are `material.filter.default.*`.
1489
+
1490
+ ```tsx
1491
+ // `status` has a fixed `default: ['active', 'upcoming']`: its hint chip needs no
1492
+ // prop. `mine` is a switch with `default: 'page'`: the page gives its value.
1493
+ <FilterBar model={EVENT_FILTERS} params={params} onParamsChange={setParams}
1494
+ defaults={{ mine: ['1'] }} />
1495
+ <ServerTable filter={{ model: EVENT_FILTERS, defaults: { mine: ['1'] } }} {...table} />
1496
+ ```
1497
+
1498
+ #### A scope field
1499
+
1500
+ A list scoped by a value the user changes but never clears (the period or the
1501
+ workspace it shows) declares `scope: true`. It is not a narrowing filter, so
1502
+ it is never counted, chipped or cleared, and it needs a `default` (the value
1503
+ while the user chose none) and a single-value type (`select`, `radio-group` or
1504
+ `chips-single`); `defineFilterModel` throws without a default, with another
1505
+ type, or together with `widens` (a field is one role or the other).
1506
+
1507
+ ```ts
1508
+ { id: 'period', type: 'select', label: 'Period', options: PERIODS,
1509
+ scope: true, default: 'page' } // or default: ['p2']
1510
+ ```
1511
+
1512
+ - **Its control** is the bar's leading pill, "Period: P2": a button
1513
+ (`aria-haspopup="dialog"`) that opens the field's editor, with the value
1514
+ always shown (the default while the user chose none, with no "Default" tag)
1515
+ and no ✕. `ServerTable`'s band shows it the same way, and the facets panel
1516
+ and the phone sheet list it as the first section, with no count and no
1517
+ Clear. On a phone the pill stays in the bar beside the search (44px
1518
+ target): a tap opens the filters sheet on the period's section, focused,
1519
+ not a popover. Below 640px the search takes the whole first row, and the
1520
+ pill and "Filters" share the second (pill first, "Filters" at the end);
1521
+ without a scope field the single row stays.
1522
+ - **Not a filter.** It is never a chip, never in "Add filter", and never
1523
+ counted: not in "Filters (N)", not in the announcement, not in `isFiltered`
1524
+ or `isNarrowing`. "Clear all" and `withoutFilters` keep it, so the list
1525
+ keeps its scope; "Clear all" shows from two filters *beside* it.
1526
+ - **In the URL** it is an explicit choice like any other (`f=period:is:p1`, or
1527
+ its alias); an invalid value means the default. It filters the rows
1528
+ (`matchQuery`, `effectiveConditions`) as any field does. A `locked`
1529
+ condition on it wins: the lock chip shows, the pill does not.
1530
+ - **Core.** `isKeptField(model, id)` is true for a `widens` or a `scope`
1531
+ field, `isScopeField(model, id)` for a scope alone.
1532
+
1533
+ #### A field only a link sets
1534
+
1535
+ A field a detail page sets through a link (`/items?item=abc`) must be in the
1536
+ model, so its condition is parsed, filters the rows and shows as a chip. Declare
1537
+ it `addable: false` so the user is never offered it with nothing to pick:
1538
+
1539
+ ```ts
1540
+ { id: 'item', type: 'autocomplete', label: 'Item', style: 'single', multiple: false,
1541
+ addable: false }
1542
+ ```
1543
+
1544
+ It is a plain filter in every other way: read from the URL and written back,
1545
+ a chip with its ✕ while it has a value, counted, and removed by "Clear all"
1546
+ (unlike a `scope` field, which is always shown and never cleared). It is
1547
+ left out of "Add filter" and of the quick filters (also when listed in
1548
+ `quick`), and the phone sheet and the facets panel list its section only
1549
+ while the applied query holds a value. Core: `isAddableField(model, id)`.
1550
+
1551
+ #### A refused condition
1552
+
1553
+ A server can refuse a value (a date span too long, an end before a start).
1554
+ Pass its message per field id, a record `{ [fieldId]: message }`, as `errors`
1555
+ (`Filter`, `FilterBar` and `ServerTable`'s `filter`), built from what the
1556
+ loader's API answered:
1557
+
1558
+ ```tsx
1559
+ <ServerTable filter={{ model, errors: { period: 'The period cannot exceed 366 days.' } }} {...table} />
1560
+ ```
1561
+
1562
+ The field's control turns to the error tone and reads "Invalid" (an icon and
1563
+ a word, never colour alone): a chip, a quick pill, a scope pill (on a phone
1564
+ too), a quick switch or checkbox toggle, and the default chip. It is described "Invalid: <message>"; its
1565
+ editor and its facets section show the message under the title; and the
1566
+ polite live region tells it ("Period: The period cannot exceed 366 days."),
1567
+ once, after the count, also when the page opens with it; the next count replaces it. A dashed control (a toggle, the default chip) keeps its dashes
1568
+ and takes the error tone on its line and text. Only a field of the model that the page does not lock shows
1569
+ one. The page clears it when the condition changes, by passing no error. The
1570
+ words are `material.filter.error.*`; `fieldErrors(errors, model, locked)` in
1571
+ the core lists what the bar shows.
1572
+
1573
+ #### A list without a search box
1574
+
1575
+ `defineFilterModel({ search: false, fields })` declares a list with no text
1576
+ search (invitations by status, where the API has none): the bar, the facets
1577
+ panel and the phone sheet render no search box, `/` goes to the first quick
1578
+ filter (nothing without one), and the codec neither reads nor writes the
1579
+ search param, so `q` is free for a field. `model.search.enabled` tells.
1580
+
1414
1581
  #### Quick filters
1415
1582
 
1416
1583
  Fields declared `quick: true` in the model (or listed in `quick`) show as
@@ -1439,6 +1606,19 @@ archived") is a switch or checkbox in its editor, and off removes its
1439
1606
  condition (it never writes `0`). A plain boolean ("Scholarship") offers Yes
1440
1607
  and No as two radios, and "No" is a condition of its own, with its chip.
1441
1608
 
1609
+ A switch that only narrows ("Only unassigned") declares `off: 'remove'`
1610
+ (`'no'` is the default; with `widens: true` it is `'remove'`, and `widens`
1611
+ with `off: 'no'` throws). Its editor is one switch, with no Yes or No; on
1612
+ is a filter like any other, counted ("Clear all" appears, the phone sheet
1613
+ counts it) and shown as a chip, and off removes the condition and writes
1614
+ nothing to the URL. An old `?f=unassigned:is:0` link on such a field is
1615
+ dropped silently, so it shows every row. A field that does not opt in
1616
+ behaves as before.
1617
+
1618
+ ```ts
1619
+ { id: 'unassigned', type: 'switch', label: 'Only unassigned', off: 'remove' }
1620
+ ```
1621
+
1442
1622
  A chip's popover is titled with its field and operator ("Level is not") and
1443
1623
  edits the values only. To change the operator, go back through "Add filter":
1444
1624
  it lists an active field with its current condition, and picking it
@@ -1869,6 +2049,7 @@ const [levels, setLevels] = useState<string[]>(['ce1']);
1869
2049
  | Prop | Type | Default | Effect |
1870
2050
  | --- | --- | --- | --- |
1871
2051
  | `title` | `string` | — | the facet's name, in the header; a string, because it builds the accessible names |
2052
+ | `hint` | `ReactNode` | — | a quiet word after the title (a field's "Default"); part of the toggle's name unless `activeCount > 0` names it |
1872
2053
  | `activeCount` | `number` | `0` | how many values are active: the count Badge (hidden at 0) and the toggle's name |
1873
2054
  | `onClear` | `() => void` | — | the "Clear" button, shown while `activeCount > 0`; the focus then goes to the toggle |
1874
2055
  | `open` / `defaultOpen` / `onOpenChange` | `boolean` / `boolean` / `(open) => void` | uncontrolled, open | controlled or not |
@@ -286,6 +286,13 @@ chip's description), and a chip's words: `chip` "{field} {operator}
286
286
  `operator` "Operator", `operator-placeholder` "Operator…", `no-operator`,
287
287
  `back` "Back", `done` "Done", `live` "Applied as you go" and `no-value` "No
288
288
  value to enter." (an operator such as "is empty")),
289
+ `material.filter.default.*` for a field's default shown as a hint (`label`
290
+ "Default", `hint` "Default value, in effect until you choose one", `tag`
291
+ "· {default}" after a title, a pill's or a toggle's words, and `change`
292
+ "{filter}, default value. Change", the hint chip's name),
293
+ `material.filter.error.*` for a condition the server refused (`tag`
294
+ "Invalid", `label` "Invalid: {message}", the chip's description, and
295
+ `announce` "{field}: {message}", told in the live region),
289
296
  `material.filter.announce.*` for its live region (`results`, `cleared`,
290
297
  `cleared-count`, `added`, `added-count`, each an ICU plural over `count`),
291
298
  `material.filter.summary.*` for values (`yes`, `no`, `quoted`, `range-and`,
@@ -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.