@nxgt/material-vue 0.31.0 → 0.33.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 +73 -0
- package/README.md +20 -7
- package/dist/{Button-CNbJT5qV.js → Button-DEA5W6O3.js} +1 -1
- package/dist/{HoverCardTrigger-BTAHE6wK.js → HoverCardTrigger-CjSteFgk.js} +2 -2
- package/dist/{camera-rJmgxIEn.js → camera-B063QUeM.js} +4 -4
- package/dist/components/components.css +3 -1
- package/dist/components/ui/fields/otp-field/input-otp.css +48 -0
- package/dist/{components-Dxy9DvEe.js → components-CmIOfRO4.js} +13547 -17345
- package/dist/components.js +8 -8
- package/dist/composables.js +25 -25
- package/dist/dnd.js +1 -1
- package/dist/fonts/katex/KaTeX_AMS-Regular.woff2 +0 -0
- package/dist/fonts/katex/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/dist/fonts/katex/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/dist/fonts/katex/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/dist/fonts/katex/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/dist/fonts/katex/KaTeX_Main-Bold.woff2 +0 -0
- package/dist/fonts/katex/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/dist/fonts/katex/KaTeX_Main-Italic.woff2 +0 -0
- package/dist/fonts/katex/KaTeX_Main-Regular.woff2 +0 -0
- package/dist/fonts/katex/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/dist/fonts/katex/KaTeX_Math-Italic.woff2 +0 -0
- package/dist/fonts/katex/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/dist/fonts/katex/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/dist/fonts/katex/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/dist/fonts/katex/KaTeX_Script-Regular.woff2 +0 -0
- package/dist/fonts/katex/KaTeX_Size1-Regular.woff2 +0 -0
- package/dist/fonts/katex/KaTeX_Size2-Regular.woff2 +0 -0
- package/dist/fonts/katex/KaTeX_Size3-Regular.woff2 +0 -0
- package/dist/fonts/katex/KaTeX_Size4-Regular.woff2 +0 -0
- package/dist/fonts/katex/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/dist/{form-scope-BhNw4Mm6.js → form-scope-DuDx5Jyx.js} +1 -1
- package/dist/{i18n-provider-BPK68eqM.js → i18n-provider-CiVi05fs.js} +1 -1
- package/dist/i18n.js +4 -4
- package/dist/{icon-button-DXXjxBgr.js → icon-button-CWcOcNHr.js} +1 -1
- package/dist/lib/components/ui/fields/attributes/form-schema.d.ts +11 -2
- package/dist/lib/components/ui/fields/contacts/consts.d.ts +1 -1
- package/dist/lib/components/ui/fields/opening-hours/consts.d.ts +12 -1
- package/dist/lib/components/ui/fields/search-field/index.d.ts +2 -1
- package/dist/lib/components/ui/primitives/index.d.ts +1 -0
- package/dist/lib/components/ui/primitives/nonce-provider/index.d.ts +2 -0
- package/dist/lib/components/ui/primitives/nonce-provider/nonce-provider.vue.d.ts +20 -0
- package/dist/lib/components/ui/selection/autocomplete/autocomplete-items.d.ts +5 -0
- package/dist/lib/components/ui/selection/autocomplete/autocomplete.vue.d.ts +6 -3
- package/dist/lib/components/ui/selection/autocomplete/filter-options.d.ts +9 -2
- package/dist/lib/components/ui/selection/autocomplete/index.d.ts +1 -0
- package/dist/lib/components/ui/selection/autocomplete/partials/autocomplete-list.vue.d.ts +8 -1
- package/dist/lib/components/ui/selection/autocomplete/search-types.d.ts +34 -0
- package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-items.d.ts +20 -3
- package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-list.d.ts +28 -0
- package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-search.d.ts +11 -0
- package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-status.d.ts +32 -0
- package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-texts.d.ts +1 -3
- package/dist/lib/components/ui/selection/autocomplete/use-autocomplete.d.ts +10 -5
- package/dist/lib/components/ui/selection/radio-group/radio-group-field.vue.d.ts +2 -0
- package/dist/lib/composables/index.d.ts +1 -0
- package/dist/lib/composables/use-csp-nonce.d.ts +18 -0
- package/dist/lib/i18n/resources/en/en.d.ts +4 -0
- package/dist/lib/i18n/resources/en/material.json.d.ts +5 -1
- package/dist/lib/i18n/resources/fr/fr.d.ts +4 -0
- package/dist/lib/i18n/resources/fr/material.json.d.ts +5 -1
- package/dist/lib/i18n/resources/resources.d.ts +8 -0
- package/dist/lib/i18n/translator.d.ts +1 -1
- package/dist/lib/lib/index.d.ts +1 -0
- package/dist/lib/nuxt/auto-imports.d.ts +1 -1
- package/dist/lib/nuxt/component-names.d.ts +1 -1
- package/dist/lib/vite/index.d.ts +3 -2
- package/dist/lib.js +50 -49
- package/dist/main.js +17 -17
- package/dist/map.js +4 -4
- package/dist/material-vue.css +1 -1
- package/dist/{pdf-outline-sidebar-6pIxLP_o.js → pdf-outline-sidebar-CzY59j_Y.js} +2 -2
- package/dist/{pdf-page-list-ojy2YSTr.js → pdf-page-list-BMxNOnPk.js} +2 -2
- package/dist/{pdf-page--zE3j8bc.js → pdf-page-nM_fvTHh.js} +1 -1
- package/dist/{pdf-reader-CcSJaFFp.js → pdf-reader-Dpv3oWe1.js} +52 -49
- package/dist/{pdf-sidebar-8DC9VdCG.js → pdf-sidebar-DkefUgWg.js} +3 -3
- package/dist/{pdf-thumbnail-sidebar-BbPcfCYm.js → pdf-thumbnail-sidebar-I6i1lv5J.js} +3 -3
- package/dist/{pdf-toolbar-B_MDicKL.js → pdf-toolbar-Da6UMt1O.js} +4 -4
- package/dist/pdf.js +2 -2
- package/dist/{sortable-provider-Cb3tm9Cy.js → sortable-provider-Cvpqk8tp.js} +2 -2
- package/dist/{stored-language-UnInBB6T.js → stored-language-GDRdeYD8.js} +10 -2
- package/dist/{stx-player-CjGKQF6K.js → stx-player-CN7Ic8tP.js} +14 -14
- package/dist/{translator-J3_vaV_A.js → translator-BDFEmlcL.js} +1 -1
- package/dist/{use-heading-spy-_dk8kGwL.js → use-heading-spy-DimKb49j.js} +54 -46
- package/dist/{use-material-translation-Do6ztdtV.js → use-material-translation-CaHS6oZz.js} +3 -3
- package/dist/vite.js +2 -2
- package/docs/README.md +1 -0
- package/docs/components/data-display.md +5 -0
- package/docs/components/form-fields.md +23 -0
- package/docs/components/primitives.md +41 -0
- package/docs/components/selection-and-choice.md +75 -4
- package/docs/getting-started.md +5 -1
- package/docs/guides/csp.md +420 -0
- package/docs/guides/helpers.md +3 -1
- package/docs/guides/theming.md +27 -0
- package/docs/roadmap.md +21 -0
- package/docs/troubleshooting.md +217 -0
- package/lib/components/components.css +3 -1
- package/lib/components/ui/fields/otp-field/input-otp.css +48 -0
- package/lib/nuxt/auto-imports.ts +1 -0
- package/lib/nuxt/component-names.ts +1 -0
- package/lib/vite/index.ts +6 -2
- package/package.json +9 -4
- package/dist/lib/components/ui/fields/search-field/debounce.d.ts +0 -5
|
@@ -1,28 +1,29 @@
|
|
|
1
|
-
import { a as e } from "./i18n-provider-
|
|
1
|
+
import { a as e } from "./i18n-provider-CiVi05fs.js";
|
|
2
2
|
import { c as t, d as n, f as r, l as i, n as a, u as o } from "./number.utils-Cy1z6qI7.js";
|
|
3
|
-
import { computed as s, getCurrentInstance as c,
|
|
4
|
-
import {
|
|
3
|
+
import { computed as s, getCurrentInstance as c, inject as l, onMounted as u, ref as d, toValue as f } from "vue";
|
|
4
|
+
import { injectConfigProviderContext as p } from "reka-ui";
|
|
5
|
+
import { useIntersectionObserver as m, useMediaQuery as h, useSSRWidth as g, useStorage as _ } from "@vueuse/core";
|
|
5
6
|
//#region lib/composables/resolve-theme.ts
|
|
6
|
-
function
|
|
7
|
+
function v(e, t) {
|
|
7
8
|
let n = e === "light" || e === "dark" ? e : "system";
|
|
8
9
|
return {
|
|
9
10
|
theme: n === "system" ? t ? "dark" : "light" : n,
|
|
10
11
|
preference: n
|
|
11
12
|
};
|
|
12
13
|
}
|
|
13
|
-
function
|
|
14
|
+
function y(e) {
|
|
14
15
|
typeof document > "u" || (document.documentElement.classList.toggle("dark", e === "dark"), document.documentElement.style.colorScheme = e);
|
|
15
16
|
}
|
|
16
17
|
//#endregion
|
|
17
18
|
//#region lib/composables/use-ssr-media-query.ts
|
|
18
|
-
var
|
|
19
|
-
function
|
|
20
|
-
return c() ?
|
|
19
|
+
var b = 1024;
|
|
20
|
+
function x(e) {
|
|
21
|
+
return c() ? h(e, { ssrWidth: g() ?? b }) : h(e);
|
|
21
22
|
}
|
|
22
23
|
//#endregion
|
|
23
24
|
//#region lib/composables/use-theme.ts
|
|
24
|
-
function
|
|
25
|
-
let e =
|
|
25
|
+
function S() {
|
|
26
|
+
let e = x("(prefers-color-scheme: dark)"), t = _("theme", null, void 0, { initOnMounted: !0 }), n = s(() => v(t.value, !!e.value));
|
|
26
27
|
return {
|
|
27
28
|
get theme() {
|
|
28
29
|
return n.value.theme;
|
|
@@ -31,19 +32,19 @@ function b() {
|
|
|
31
32
|
return n.value.preference;
|
|
32
33
|
},
|
|
33
34
|
updateTheme: (n) => {
|
|
34
|
-
n === "system" ? t.value = null : t.value = n,
|
|
35
|
+
n === "system" ? t.value = null : t.value = n, y(v(n === "system" ? void 0 : n, !!e.value).theme);
|
|
35
36
|
}
|
|
36
37
|
};
|
|
37
38
|
}
|
|
38
39
|
//#endregion
|
|
39
40
|
//#region lib/composables/use-mobile.ts
|
|
40
|
-
var
|
|
41
|
-
function
|
|
42
|
-
return
|
|
41
|
+
var C = 768;
|
|
42
|
+
function w() {
|
|
43
|
+
return x(`(max-width: ${C - 1}px)`);
|
|
43
44
|
}
|
|
44
45
|
//#endregion
|
|
45
46
|
//#region ../../node_modules/date-fns/locale/fr/_lib/formatDistance.js
|
|
46
|
-
var
|
|
47
|
+
var T = {
|
|
47
48
|
lessThanXSeconds: {
|
|
48
49
|
one: "moins d’une seconde",
|
|
49
50
|
other: "moins de {{count}} secondes"
|
|
@@ -105,10 +106,10 @@ var C = {
|
|
|
105
106
|
one: "presqu’un an",
|
|
106
107
|
other: "presque {{count}} ans"
|
|
107
108
|
}
|
|
108
|
-
},
|
|
109
|
-
let r, i =
|
|
109
|
+
}, E = (e, t, n) => {
|
|
110
|
+
let r, i = T[e];
|
|
110
111
|
return r = typeof i == "string" ? i : t === 1 ? i.one : i.other.replace("{{count}}", String(t)), n?.addSuffix ? n.comparison && n.comparison > 0 ? "dans " + r : "il y a " + r : r;
|
|
111
|
-
},
|
|
112
|
+
}, D = {
|
|
112
113
|
date: r({
|
|
113
114
|
formats: {
|
|
114
115
|
full: "EEEE d MMMM y",
|
|
@@ -136,18 +137,18 @@ var C = {
|
|
|
136
137
|
},
|
|
137
138
|
defaultWidth: "full"
|
|
138
139
|
})
|
|
139
|
-
},
|
|
140
|
+
}, O = {
|
|
140
141
|
lastWeek: "eeee 'dernier à' p",
|
|
141
142
|
yesterday: "'hier à' p",
|
|
142
143
|
today: "'aujourd’hui à' p",
|
|
143
144
|
tomorrow: "'demain à' p'",
|
|
144
145
|
nextWeek: "eeee 'prochain à' p",
|
|
145
146
|
other: "P"
|
|
146
|
-
},
|
|
147
|
+
}, k = (e, t, n, r) => O[e], A = {
|
|
147
148
|
narrow: ["av. J.-C", "ap. J.-C"],
|
|
148
149
|
abbreviated: ["av. J.-C", "ap. J.-C"],
|
|
149
150
|
wide: ["avant Jésus-Christ", "après Jésus-Christ"]
|
|
150
|
-
},
|
|
151
|
+
}, j = {
|
|
151
152
|
narrow: [
|
|
152
153
|
"T1",
|
|
153
154
|
"T2",
|
|
@@ -166,7 +167,7 @@ var C = {
|
|
|
166
167
|
"3ème trimestre",
|
|
167
168
|
"4ème trimestre"
|
|
168
169
|
]
|
|
169
|
-
},
|
|
170
|
+
}, M = {
|
|
170
171
|
narrow: [
|
|
171
172
|
"J",
|
|
172
173
|
"F",
|
|
@@ -209,7 +210,7 @@ var C = {
|
|
|
209
210
|
"novembre",
|
|
210
211
|
"décembre"
|
|
211
212
|
]
|
|
212
|
-
},
|
|
213
|
+
}, N = {
|
|
213
214
|
narrow: [
|
|
214
215
|
"D",
|
|
215
216
|
"L",
|
|
@@ -246,7 +247,7 @@ var C = {
|
|
|
246
247
|
"vendredi",
|
|
247
248
|
"samedi"
|
|
248
249
|
]
|
|
249
|
-
},
|
|
250
|
+
}, P = {
|
|
250
251
|
narrow: {
|
|
251
252
|
am: "AM",
|
|
252
253
|
pm: "PM",
|
|
@@ -277,7 +278,7 @@ var C = {
|
|
|
277
278
|
evening: "du soir",
|
|
278
279
|
night: "du matin"
|
|
279
280
|
}
|
|
280
|
-
},
|
|
281
|
+
}, F = (e, t) => {
|
|
281
282
|
let n = Number(e), r = t?.unit;
|
|
282
283
|
if (n === 0) return "0";
|
|
283
284
|
let i = [
|
|
@@ -288,38 +289,38 @@ var C = {
|
|
|
288
289
|
"second"
|
|
289
290
|
], a;
|
|
290
291
|
return a = n === 1 ? r && i.includes(r) ? "ère" : "er" : "ème", n + a;
|
|
291
|
-
},
|
|
292
|
+
}, I = ["MMM", "MMMM"], L = {
|
|
292
293
|
en: t,
|
|
293
294
|
fr: {
|
|
294
295
|
code: "fr",
|
|
295
|
-
formatDistance:
|
|
296
|
-
formatLong:
|
|
297
|
-
formatRelative:
|
|
296
|
+
formatDistance: E,
|
|
297
|
+
formatLong: D,
|
|
298
|
+
formatRelative: k,
|
|
298
299
|
localize: {
|
|
299
|
-
preprocessor: (e, t) => e.getDate() === 1 || !t.some((e) => e.isToken &&
|
|
300
|
+
preprocessor: (e, t) => e.getDate() === 1 || !t.some((e) => e.isToken && I.includes(e.value)) ? t : t.map((e) => e.isToken && e.value === "do" ? {
|
|
300
301
|
isToken: !0,
|
|
301
302
|
value: "d"
|
|
302
303
|
} : e),
|
|
303
|
-
ordinalNumber:
|
|
304
|
+
ordinalNumber: F,
|
|
304
305
|
era: n({
|
|
305
|
-
values:
|
|
306
|
+
values: A,
|
|
306
307
|
defaultWidth: "wide"
|
|
307
308
|
}),
|
|
308
309
|
quarter: n({
|
|
309
|
-
values:
|
|
310
|
+
values: j,
|
|
310
311
|
defaultWidth: "wide",
|
|
311
312
|
argumentCallback: (e) => e - 1
|
|
312
313
|
}),
|
|
313
314
|
month: n({
|
|
314
|
-
values:
|
|
315
|
+
values: M,
|
|
315
316
|
defaultWidth: "wide"
|
|
316
317
|
}),
|
|
317
318
|
day: n({
|
|
318
|
-
values:
|
|
319
|
+
values: N,
|
|
319
320
|
defaultWidth: "wide"
|
|
320
321
|
}),
|
|
321
322
|
dayPeriod: n({
|
|
322
|
-
values:
|
|
323
|
+
values: P,
|
|
323
324
|
defaultWidth: "wide"
|
|
324
325
|
})
|
|
325
326
|
},
|
|
@@ -449,8 +450,8 @@ var C = {
|
|
|
449
450
|
}
|
|
450
451
|
}
|
|
451
452
|
};
|
|
452
|
-
function
|
|
453
|
-
let n = e(), r = s(() => n?.language ?? "en"), i = s(() =>
|
|
453
|
+
function R() {
|
|
454
|
+
let n = e(), r = s(() => n?.language ?? "en"), i = s(() => L[r.value] ?? t);
|
|
454
455
|
return {
|
|
455
456
|
get language() {
|
|
456
457
|
return r.value;
|
|
@@ -462,19 +463,26 @@ function I() {
|
|
|
462
463
|
};
|
|
463
464
|
}
|
|
464
465
|
//#endregion
|
|
466
|
+
//#region lib/composables/use-csp-nonce.ts
|
|
467
|
+
var z = Symbol("nxgt-csp-nonce"), B = Symbol("nxgt-csp-nonce-depth");
|
|
468
|
+
function V() {
|
|
469
|
+
let e = l(z, null), t = p(null);
|
|
470
|
+
return s(() => e?.value || t?.nonce?.value || void 0);
|
|
471
|
+
}
|
|
472
|
+
//#endregion
|
|
465
473
|
//#region lib/composables/use-heading-spy.ts
|
|
466
|
-
var
|
|
467
|
-
function
|
|
468
|
-
let r =
|
|
469
|
-
|
|
474
|
+
var H = "0px 0px -65% 0px", U = [0, 1];
|
|
475
|
+
function W(e, t, n) {
|
|
476
|
+
let r = d(!1);
|
|
477
|
+
u(() => {
|
|
470
478
|
r.value = !0;
|
|
471
|
-
}),
|
|
479
|
+
}), m(s(() => r.value ? f(e).map((e) => document.getElementById(e)).filter((e) => e != null) : []), (e) => {
|
|
472
480
|
let n = e.filter((e) => e.isIntersecting).sort((e, t) => e.boundingClientRect.top - t.boundingClientRect.top)[0]?.target.id;
|
|
473
481
|
n && t(n);
|
|
474
482
|
}, {
|
|
475
|
-
rootMargin: n?.rootMargin ??
|
|
476
|
-
threshold: n?.threshold ??
|
|
483
|
+
rootMargin: n?.rootMargin ?? H,
|
|
484
|
+
threshold: n?.threshold ?? U
|
|
477
485
|
});
|
|
478
486
|
}
|
|
479
487
|
//#endregion
|
|
480
|
-
export {
|
|
488
|
+
export { R as a, x as c, V as i, y as l, B as n, w as o, z as r, S as s, W as t, v as u };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import "./stored-language-
|
|
2
|
-
import { a as e } from "./i18n-provider-
|
|
3
|
-
import { i as t, r as n } from "./translator-
|
|
1
|
+
import "./stored-language-GDRdeYD8.js";
|
|
2
|
+
import { a as e } from "./i18n-provider-CiVi05fs.js";
|
|
3
|
+
import { i as t, r as n } from "./translator-BDFEmlcL.js";
|
|
4
4
|
//#region lib/i18n/translate-material.ts
|
|
5
5
|
function r(e, r, i, a) {
|
|
6
6
|
if (e) for (let t of /* @__PURE__ */ new Set([e, e.split("-")[0]])) {
|
package/dist/vite.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { e as pdfkitStandardFonts };
|
|
1
|
+
import { cspAssetsInlineLimit as e, pdfkitStandardFonts as t } from "@nxgt/material-core/vite";
|
|
2
|
+
export { e as cspAssetsInlineLimit, t as pdfkitStandardFonts };
|
package/docs/README.md
CHANGED
|
@@ -15,6 +15,7 @@ detail: every option, the second example, and the traps.
|
|
|
15
15
|
| [PDF documents](guides/pdf.md) | You produce a PDF file from data with `@nxgt/material-vue/pdf`: in the browser (`usePdf`, `PdfDownloadButton`) or from a Nuxt server route (`pdfResponse`), fonts on an inlined server |
|
|
16
16
|
| [Maps](guides/maps.md) | You render Leaflet maps or a location field with `@nxgt/material-vue/map` |
|
|
17
17
|
| [SSR](guides/ssr.md) | Your app renders on a server — Nuxt or a Vite SSR server — and bundles this package |
|
|
18
|
+
| [Content Security Policy](guides/csp.md) | Your app sends a strict CSP: the directives to use, the components that still inject a `<style>`, the zod and KaTeX settings, self-hosting a font, the per-request nonce |
|
|
18
19
|
| [Helpers](guides/helpers.md) | You need a utility, composable, Pinia store factory or type from `/lib`, `/composables`, `/i18n`, `/models`, `/types` or `/dnd` |
|
|
19
20
|
| [Troubleshooting](troubleshooting.md) | Something failed and you have an error message or a symptom |
|
|
20
21
|
| [Roadmap](roadmap.md) | You want to know what is planned, what shipped, and what will not be done |
|
|
@@ -706,6 +706,11 @@ left alone. Mermaid diagrams
|
|
|
706
706
|
render in the browser only, after mount; on the server the fence is left
|
|
707
707
|
empty.
|
|
708
708
|
|
|
709
|
+
Math needs no import of its own: KaTeX's CSS is in the stylesheet, and its
|
|
710
|
+
fonts are files the stylesheet loads (`dist/fonts/katex/` on the compiled
|
|
711
|
+
route), allowed by `font-src 'self'`. Math in system fonts means the fonts
|
|
712
|
+
were not served ([troubleshooting](../troubleshooting.md#math-renders-in-the-system-fonts)).
|
|
713
|
+
|
|
709
714
|
`MermaidDiagram` renders one chart outside markdown, the same way: client-only,
|
|
710
715
|
with `mermaid` imported on mount.
|
|
711
716
|
|
|
@@ -499,6 +499,7 @@ const code = ref('');
|
|
|
499
499
|
| `v-model` | `string` | — | the digits typed so far |
|
|
500
500
|
| `@complete` | `(value: string) => void` | — | once every slot is filled (from `vue-input-otp`) |
|
|
501
501
|
| `error` | `boolean` | — | error colour on the slots (`data-invalid` on each); `aria-invalid` goes on the input only, the one element a screen reader reads |
|
|
502
|
+
| `nonce` | `string` | the `NonceProvider`'s | the CSP nonce of the `<style>` `vue-input-otp` injects; an explicit one wins (passed down to `InputOTP`, [CSP](../guides/csp.md)) |
|
|
502
503
|
| `id` | `string` | from `useId()` | the input's id, which the label points to; it differs per field — `OtpFormField` too (not the field `name`), so two code forms on one page each keep their name |
|
|
503
504
|
|
|
504
505
|
`InputOTP`, `InputOTPGroup`, `InputOTPSlot` and `InputOTPSeparator` are the
|
|
@@ -891,6 +892,16 @@ attribute), `AttributeValuesForm` (fill values for a set of attributes).
|
|
|
891
892
|
Form wrappers: `AttributesFormField` (the definitions),
|
|
892
893
|
`AttributeValuesFormFieldSet` and `AttributeValueFormField` (the values).
|
|
893
894
|
Add and edit confirmations use a toast, so mount a `Toaster` once in the app.
|
|
895
|
+
`attributeFormSchema()` returns `AttributeForm`'s zod schema, built on the
|
|
896
|
+
first call (in the form's setup), and `toAttributeFormSchema()` the same as
|
|
897
|
+
vee-validate's typed schema; the `schema` constant is **deprecated** and
|
|
898
|
+
removed in 1.0 ([CSP](../guides/csp.md#zod-and-unsafe-eval)).
|
|
899
|
+
|
|
900
|
+
```ts
|
|
901
|
+
import { attributeFormSchema } from '@nxgt/material-vue/components';
|
|
902
|
+
|
|
903
|
+
const result = attributeFormSchema().safeParse({ label: 'Color', name: 'color', type: 'COLOR' });
|
|
904
|
+
```
|
|
894
905
|
|
|
895
906
|
| Prop | Type | Default | Effect |
|
|
896
907
|
| --- | --- | --- | --- |
|
|
@@ -1046,6 +1057,18 @@ function onEdit(value: OpeningHour, index: number) {}
|
|
|
1046
1057
|
Parts: `OpeningHours` (the list) and `OpeningHoursDialog` (add / edit).
|
|
1047
1058
|
`OpeningHoursItem`, a day row bound to a vee-validate array, is
|
|
1048
1059
|
**deprecated** and removed in 1.0: use `OpeningHoursFormField`.
|
|
1060
|
+
`openingHoursSchema()` returns the zod schema of one `OpeningHour`
|
|
1061
|
+
(`dayOfWeek` coerced from a string), built on the first call; the
|
|
1062
|
+
`OpeningHoursSchema` constant is **deprecated** and removed in 1.0. Under a
|
|
1063
|
+
strict CSP, call `z.config({ jitless: true })` first: see
|
|
1064
|
+
[CSP](../guides/csp.md#zod-and-unsafe-eval).
|
|
1065
|
+
|
|
1066
|
+
```ts
|
|
1067
|
+
import { openingHoursSchema } from '@nxgt/material-vue/components';
|
|
1068
|
+
import { z } from 'zod';
|
|
1069
|
+
|
|
1070
|
+
const schema = z.object({ hours: z.array(openingHoursSchema()) });
|
|
1071
|
+
```
|
|
1049
1072
|
|
|
1050
1073
|
| Prop | Type | Default | Effect |
|
|
1051
1074
|
| --- | --- | --- | --- |
|
|
@@ -29,6 +29,7 @@ work on them unchanged.
|
|
|
29
29
|
| <img src="../previews/primitives-togglegroup.webp" width="96" alt=""> | [ToggleGroup](#togglegroup) | Single or multiple toggle group |
|
|
30
30
|
| <img src="../previews/primitives-activity.webp" width="96" alt=""> | [Activity](#activity) | Keeps its content mounted and hides it with a boolean |
|
|
31
31
|
| <img src="../previews/primitives-scrollarea.webp" width="96" alt=""> | [ScrollArea](#scrollarea) | Scroll container with a thin custom scrollbar |
|
|
32
|
+
| | [NonceProvider](#nonceprovider) | Gives the page's CSP nonce to every component below |
|
|
32
33
|
|
|
33
34
|
## Accordion
|
|
34
35
|
|
|
@@ -859,3 +860,43 @@ through.
|
|
|
859
860
|
The React `ScrollArea` takes the same props, camel-cased (`scrollHideDelay`).
|
|
860
861
|
|
|
861
862
|
**Storybook:** `Primitives/ScrollArea` · **React:** `ScrollArea`, `ScrollBar`
|
|
863
|
+
|
|
864
|
+
---
|
|
865
|
+
|
|
866
|
+
## NonceProvider
|
|
867
|
+
|
|
868
|
+
No preview: it draws nothing of its own, it hands the page's Content Security
|
|
869
|
+
Policy nonce down.
|
|
870
|
+
|
|
871
|
+
Gives this request's CSP nonce to every component below it: reka's `Select`,
|
|
872
|
+
`Combobox`, `ScrollArea` and `Splitter` (it renders a reka `ConfigProvider`
|
|
873
|
+
with `nonce`, carrying over a parent `ConfigProvider`'s other settings),
|
|
874
|
+
`InputOTP` when it has no `nonce` of its own, and the stylesheet tiptap
|
|
875
|
+
injects for `RichTextEditor`. Render it once, at the root, on the server and
|
|
876
|
+
on the client, with the same nonce. The Nuxt module registers it.
|
|
877
|
+
|
|
878
|
+
```vue
|
|
879
|
+
<script setup lang="ts">
|
|
880
|
+
import { NonceProvider } from '@nxgt/material-vue/components';
|
|
881
|
+
|
|
882
|
+
defineProps<{ nonce?: string }>();
|
|
883
|
+
</script>
|
|
884
|
+
|
|
885
|
+
<template>
|
|
886
|
+
<NonceProvider :nonce="nonce">
|
|
887
|
+
<RouterView />
|
|
888
|
+
</NonceProvider>
|
|
889
|
+
</template>
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
| Prop | Type | Default | Effect |
|
|
893
|
+
| --- | --- | --- | --- |
|
|
894
|
+
| `nonce` | `string` | `undefined` | the nonce of the page's `style-src-elem 'nonce-…'`; `undefined` or `''` inherits the nearest outer `NonceProvider`'s (or `ConfigProvider`'s) nonce (none at the root) |
|
|
895
|
+
|
|
896
|
+
`useCspNonce()` (`@nxgt/material-vue/composables`) reads it in a component of
|
|
897
|
+
your own; `getCspNonce()` and `setCspNonce()` (`@nxgt/material-vue/lib`) read
|
|
898
|
+
and keep the page's nonce outside Vue. The Nuxt `app.vue`, the client entry
|
|
899
|
+
and what each component needs are in the [Content Security Policy
|
|
900
|
+
guide](../guides/csp.md#nonceprovider).
|
|
901
|
+
|
|
902
|
+
**Storybook:** `Primitives/NonceProvider` · **React:** `NonceProvider`
|
|
@@ -231,7 +231,7 @@ options by label. Picking the selected option again clears it.
|
|
|
231
231
|
|
|
232
232
|

|
|
233
233
|
|
|
234
|
-
Single or multiple (chips) autocomplete typed by its options, with free-solo values, a
|
|
234
|
+
Single or multiple (chips) autocomplete typed by its options, with free-solo values, accent-insensitive matching, a debounced server search and option slots.
|
|
235
235
|
|
|
236
236
|
```vue
|
|
237
237
|
<script setup lang="ts">
|
|
@@ -262,16 +262,81 @@ const picked = ref<string[]>(['nuxt']);
|
|
|
262
262
|
| --- | --- | --- | --- |
|
|
263
263
|
| `mode` | `'single' \| 'multiple'` | `'single'` | decides the `v-model` type |
|
|
264
264
|
| `v-model` | `string \| string[] \| null` | `null` | per `mode` |
|
|
265
|
-
| `options` | `T[]`, `T extends { value: string; label: string }` |
|
|
265
|
+
| `options` | `T[]`, `T extends { value: string; label: string }` | `[]` | the items, typed by your own option type; with `on-search`, the list before a search and the selected options' labels |
|
|
266
266
|
| `get-option-value` | `(option: T) => string` | `option.value` | the value an option stands for |
|
|
267
267
|
| `get-option-label` | `(option: T) => string` | `option.label` | the text it shows and is searched by |
|
|
268
|
+
| `filter` | `'accent-insensitive' \| 'none' \| (option: T, query: string) => boolean` | `'accent-insensitive'`, `'none'` with `on-search` | how the list is filtered as the user types: label or value, ignoring case and accents (`e` finds "École") in the provider's language; `'none'` shows `options` as given; a function gets the trimmed query, never an empty one |
|
|
268
269
|
| `free-solo` | `boolean` | `false` | accept a typed value that is not an option ("Add …") |
|
|
269
270
|
| `free-solo-label` | `(query: string) => string` | `Add "{query}"`, translated | the free-solo button's text |
|
|
270
|
-
| `@
|
|
271
|
+
| `@search` (prop `on-search`) | `(query: string, { signal }: { signal: AbortSignal }) => Promise<T[]>` | — | server search: called once typing pauses; its results become the list. Each search aborts the previous one, and a response that lands after a newer one is dropped |
|
|
272
|
+
| `@search-error` | `(error: unknown) => void` | — | `on-search` rejected (an abort is not a rejection) |
|
|
273
|
+
| `min-query-length` | `number` | `1` | `on-search` waits for this many characters; below it the list says "Type at least N characters to search." |
|
|
274
|
+
| `debounce-ms` | `number` | `250` | the pause before `on-search` and `@query-change` run |
|
|
275
|
+
| `@query-change` | `(query: string) => void` | — | the typed text once typing pauses — fetch `options` yourself here |
|
|
276
|
+
| `loading` | `boolean` | — | with `@query-change`: "Loading…" in the list, `aria-busy` on it, announced |
|
|
277
|
+
| `search-error` | `string` | — | with `@query-change`: shown and announced in place of the options |
|
|
271
278
|
| `empty` | `string` | "No option found." | text when nothing matches |
|
|
272
279
|
| `required` | `boolean` | — | the asterisk, and `aria-required` on the input, which the label names |
|
|
273
280
|
| `error` | `boolean` | — | red border on the box, error helper text, `aria-invalid` on the input |
|
|
274
281
|
|
|
282
|
+
### Searching a server
|
|
283
|
+
|
|
284
|
+
`on-search` is a function prop (so `@search="fn"` binds it too): it takes
|
|
285
|
+
the query and an `AbortSignal`; hand the signal to `fetch` so a search the
|
|
286
|
+
user typed past is cancelled. The field shows "Loading…", keeps the last
|
|
287
|
+
results until the new ones arrive, and never lets an older response
|
|
288
|
+
overwrite a newer one, even from an API that ignores the signal. Pass the
|
|
289
|
+
selected option in `options` so its chip has a label before any search; a
|
|
290
|
+
picked option keeps its chip once a new search no longer returns it.
|
|
291
|
+
|
|
292
|
+
```vue
|
|
293
|
+
<script setup lang="ts">
|
|
294
|
+
import { ref } from 'vue';
|
|
295
|
+
import { Autocomplete } from '@nxgt/material-vue/components';
|
|
296
|
+
|
|
297
|
+
type Person = { value: string; label: string };
|
|
298
|
+
|
|
299
|
+
async function searchPeople(query: string, { signal }: { signal: AbortSignal }) {
|
|
300
|
+
const response = await fetch(`/api/people?q=${encodeURIComponent(query)}`, { signal });
|
|
301
|
+
if (!response.ok) throw new Error(`Search failed: ${response.status}`);
|
|
302
|
+
return (await response.json()) as Person[];
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const person = ref<string | null>(null);
|
|
306
|
+
</script>
|
|
307
|
+
|
|
308
|
+
<template>
|
|
309
|
+
<Autocomplete
|
|
310
|
+
v-model="person"
|
|
311
|
+
label="Person"
|
|
312
|
+
placeholder="Search by name"
|
|
313
|
+
:on-search="searchPeople"
|
|
314
|
+
:min-query-length="2"
|
|
315
|
+
:debounce-ms="300"
|
|
316
|
+
/>
|
|
317
|
+
</template>
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
With `on-search`, `min-query-length` defaults to 1, `debounce-ms` to 250 and `filter` to `'none'`
|
|
321
|
+
(the server filters). A rejected search shows and announces "The search
|
|
322
|
+
failed. Try again." by itself and hands the error to `@search-error`; an abort is
|
|
323
|
+
never an error. Type your search with `AutocompleteSearch<T>` (its second
|
|
324
|
+
argument is `AutocompleteSearchContext`, `{ signal }`); `AutocompleteFilter<T>`
|
|
325
|
+
and `AutocompleteSearchProps<T>` are exported from `@nxgt/material-vue/components` too.
|
|
326
|
+
`filterAutocompleteOptions(options, query, locale?)`, exported beside the
|
|
327
|
+
component, is the default filter as a function: label or value, ignoring
|
|
328
|
+
case and accents, lowercased for `locale`.
|
|
329
|
+
|
|
330
|
+
To fetch it yourself instead, keep `options` and `loading` in your state,
|
|
331
|
+
update them on `@query-change`, and pass `filter="none"` when the server
|
|
332
|
+
already filtered.
|
|
333
|
+
|
|
334
|
+
**Accessibility.** A polite live region (`role="status"`) says "Loading…",
|
|
335
|
+
then "3 results" or "No results" (or the failure) once the search answers or
|
|
336
|
+
typing pauses — never on every keystroke. The list is `aria-busy` while
|
|
337
|
+
loading. The strings are `material.autocomplete.loading`, `.results`,
|
|
338
|
+
`.search-error` and `.min-query`. The keyboard works as before.
|
|
339
|
+
|
|
275
340
|
It is a generic component: `T` is inferred from `options`, and the slots —
|
|
276
341
|
React's render props — receive it whole:
|
|
277
342
|
|
|
@@ -450,7 +515,7 @@ React the labelled one is called `RadioGroup`.
|
|
|
450
515
|
|
|
451
516
|
| Prop | Type | Default | Effect |
|
|
452
517
|
| --- | --- | --- | --- |
|
|
453
|
-
| `options` | `{ value: string; label: string }[]` | — | required; the radios |
|
|
518
|
+
| `options` | `{ value: string; label: string; disabled?: boolean }[]` | — | required; the radios; `disabled` on an option disables only it |
|
|
454
519
|
| `v-model` | `string` | — | the selected `value` |
|
|
455
520
|
| `orientation` | `'vertical' \| 'horizontal'` | `'horizontal'` | layout |
|
|
456
521
|
| `label`, `helper-text`, `error`, `disabled` | | — | the field's text and state |
|
|
@@ -463,6 +528,12 @@ put on `RadioGroupField` land on the `radiogroup` (`class` and `style` stay on
|
|
|
463
528
|
the layout wrapper): your own `aria-label` / `aria-labelledby` wins, and your
|
|
464
529
|
`aria-describedby` ids come first.
|
|
465
530
|
|
|
531
|
+
An option's type is exported as `RadioGroupOption`. A disabled option (`disabled: true` on it) is announced as disabled, skipped by
|
|
532
|
+
the arrow keys (as the Reka UI primitives do) and drawn with a muted label and a
|
|
533
|
+
faded radio; the label keeps 4.5:1 contrast in both themes. The group's own
|
|
534
|
+
`disabled` disables every option. An option disabled while it is the current
|
|
535
|
+
value stays checked.
|
|
536
|
+
|
|
466
537
|
**Emits:** `update:modelValue` with `string`.
|
|
467
538
|
**Form wrapper:** `RadioGroupFormField` — `name` inside a vee-validate form, see
|
|
468
539
|
[forms](../guides/forms.md#the-pair) and [what each field emits](../guides/forms.md#what-each-field-emits).
|
package/docs/getting-started.md
CHANGED
|
@@ -32,7 +32,9 @@ does not import that entry never has them in its tree.
|
|
|
32
32
|
| `@nxgt/material-vue/pdf` | `bun add react @react-pdf/renderer@^4.9`, to render; building documents needs neither ([PDF documents](guides/pdf.md)) |
|
|
33
33
|
| `@nxgt/material-vue/nuxt` | a Nuxt app, `nuxt >= 3.13` ([below](#nuxt)) |
|
|
34
34
|
|
|
35
|
-
The tarball contains the built `dist
|
|
35
|
+
The tarball contains the built `dist/` (with KaTeX's fonts in
|
|
36
|
+
`dist/fonts/katex/`, which the compiled stylesheet loads),
|
|
37
|
+
`lib/styles.css`, the component
|
|
36
38
|
stylesheets it imports (`lib/components/**/*.css`), `lib/map/map.css`, the
|
|
37
39
|
Nuxt module (`lib/nuxt`, with the
|
|
38
40
|
`lib/vite/index.ts` it imports), these docs and `CHANGELOG.md` (from the
|
|
@@ -48,6 +50,8 @@ In the app's root stylesheet, right after Tailwind:
|
|
|
48
50
|
@import "@nxgt/material-vue/styles.css";
|
|
49
51
|
```
|
|
50
52
|
|
|
53
|
+
Toaster and InputOTP take their CSS from this stylesheet: without it they render unstyled.
|
|
54
|
+
|
|
51
55
|
- **`styles.css` is source, not compiled CSS.** It holds the Tailwind v4
|
|
52
56
|
`@theme` layer (colours, `--radius`, the `dark` variant, base styles, a few
|
|
53
57
|
utilities), so it runs through *your* Tailwind build. Nothing injects it,
|