@nxgt/material 3.92.0 → 3.94.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 +56 -0
- package/README.md +4 -3
- package/dist/{alert-dialog-Bz5PlYKp.js → alert-dialog-BaqK-rmd.js} +1 -1
- package/dist/{async-slice-veSVxKqt.js → async-slice-BfNpIJAH.js} +1 -1
- package/dist/{button-a3tH3pq4.js → button-CozGHXAv.js} +1 -1
- package/dist/{calculator-BtuKV3PK.js → calculator-BIx6cwgU.js} +10 -10
- package/dist/calculator-field.js +6 -6
- package/dist/{calculator-segment-CyB9IhEq.js → calculator-segment-BsaxUtzL.js} +1 -1
- package/dist/calculator.js +1 -1
- package/dist/charts.js +3 -3
- package/dist/{chip-BwGvGx_S.js → chip-DMEUGYdC.js} +2 -2
- package/dist/{code-editor-BLkmEoCi.js → code-editor-BWfky-aN.js} +4 -4
- package/dist/code-editor.js +4 -4
- package/dist/{code-language-items-DhAC_tCs.js → code-language-items-BUaNYJuw.js} +2 -2
- package/dist/components/components.css +1 -0
- package/dist/components/ui/widgets/event-calendar/event-calendar-backdrop.css +43 -0
- package/dist/{components-DX4aHtgP.js → components-CZpdAQMh.js} +19132 -18101
- package/dist/components.js +38 -38
- package/dist/{conversion-variant-C4t7aH9d.js → conversion-variant-aIBhul_U.js} +3 -3
- package/dist/{currency-field-D0UQb5pn.js → currency-field-IcEMAcAw.js} +5 -5
- package/dist/{dialog-vQMypIOm.js → dialog-H40HjqXN.js} +3 -3
- package/dist/dnd.js +1 -1
- package/dist/{dropdown-menu-B2ctUFti.js → dropdown-menu-CawkIlW8.js} +1 -1
- package/dist/editor.js +442 -442
- package/dist/{empty-state-Bs44jnWk.js → empty-state-sRVjgCis.js} +1 -1
- package/dist/{financial-variant-B5iZ3pYf.js → financial-variant-Bqo9-swy.js} +5 -5
- package/dist/{hooks-CJtRFDYD.js → hooks-BTj_WJm9.js} +2 -2
- package/dist/hooks.js +2 -2
- package/dist/{hover-card-B8PYUsv_.js → hover-card-BZU1DWGY.js} +1 -1
- package/dist/i18n.js +3 -3
- package/dist/{icon-badge-YT98b2Vx.js → icon-badge-CXgvqMsM.js} +1 -1
- package/dist/{icon-button-BylI02aX.js → icon-button-B9WjQ3RO.js} +1 -1
- package/dist/{input-group-CiayHfBN.js → input-group-Df9vp2EZ.js} +1 -1
- package/dist/lib/components/ui/collections/day-grouped-list/index.d.ts +6 -0
- package/dist/lib/components/ui/display/entity-header/entity-metadata.d.ts +0 -5
- package/dist/lib/components/ui/display/entity-header/entity-stats.d.ts +2 -1
- package/dist/lib/components/ui/display/event-chip/event-chip-badges.d.ts +21 -0
- package/dist/lib/components/ui/display/event-chip/event-chip-content.d.ts +5 -7
- package/dist/lib/components/ui/display/event-chip/event-chip-title.d.ts +9 -0
- package/dist/lib/components/ui/display/event-chip/event-chip.classes.d.ts +9 -0
- package/dist/lib/components/ui/display/event-chip/event-chip.d.ts +15 -2
- package/dist/lib/components/ui/display/markdown/markdown.d.ts +2 -2
- package/dist/lib/components/ui/display/markdown/rehype-katex-classes.d.ts +21 -0
- package/dist/lib/components/ui/fields/attributes/attribute-values-form-field-set/attribute-values-form-field-set.d.ts +1 -1
- package/dist/lib/components/ui/fields/choice-list/choice-list-control.d.ts +1 -1
- package/dist/lib/components/ui/fields/choice-list/choice-list-field.d.ts +1 -1
- package/dist/lib/components/ui/fields/choice-list/choice-list-focus.d.ts +6 -3
- package/dist/lib/components/ui/fields/choice-list/choice-list-form-field.d.ts +2 -2
- package/dist/lib/components/ui/fields/choice-list/choice-list-names.d.ts +2 -0
- package/dist/lib/components/ui/fields/choice-list/choice-list.utils.d.ts +2 -0
- package/dist/lib/components/ui/fields/choice-list/index.d.ts +1 -1
- package/dist/lib/components/ui/fields/choice-list/partials/choice-row.d.ts +9 -3
- package/dist/lib/components/ui/fields/choice-list/types.d.ts +20 -0
- package/dist/lib/components/ui/fields/contacts/placeholders.d.ts +1 -1
- package/dist/lib/components/ui/fields/index.d.ts +1 -0
- package/dist/lib/components/ui/fields/markdown-textarea-field/index.d.ts +2 -0
- package/dist/lib/components/ui/fields/markdown-textarea-field/markdown-preview.d.ts +6 -0
- package/dist/lib/components/ui/fields/markdown-textarea-field/markdown-syntax-help.d.ts +4 -0
- package/dist/lib/components/ui/fields/markdown-textarea-field/markdown-tabs.d.ts +13 -0
- package/dist/lib/components/ui/fields/markdown-textarea-field/markdown-textarea-field.d.ts +23 -0
- package/dist/lib/components/ui/fields/markdown-textarea-field/markdown-textarea-form-field.d.ts +3 -0
- package/dist/lib/components/ui/fields/markdown-textarea-field/use-text-value.d.ts +6 -0
- package/dist/lib/components/ui/layout/record-page/record-details.d.ts +6 -6
- package/dist/lib/components/ui/widgets/education/questions/index.d.ts +1 -1
- package/dist/lib/components/ui/widgets/education/questions/partials/question-hints.d.ts +9 -0
- package/dist/lib/components/ui/widgets/education/questions/partials/question-reveal.d.ts +13 -0
- package/dist/lib/components/ui/widgets/education/questions/partials/rich-text.d.ts +17 -0
- package/dist/lib/components/ui/widgets/education/questions/types.d.ts +40 -1
- package/dist/lib/components/ui/widgets/event-calendar/backdrop.types.d.ts +75 -0
- package/dist/lib/components/ui/widgets/event-calendar/chrome.types.d.ts +12 -0
- package/dist/lib/components/ui/widgets/event-calendar/event-calendar-context.d.ts +8 -1
- package/dist/lib/components/ui/widgets/event-calendar/event-calendar-events.utils.d.ts +3 -1
- package/dist/lib/components/ui/widgets/event-calendar/event-calendar-shell.d.ts +1 -1
- package/dist/lib/components/ui/widgets/event-calendar/event-calendar.backdrop.d.ts +38 -0
- package/dist/lib/components/ui/widgets/event-calendar/event-calendar.badges.d.ts +14 -0
- package/dist/lib/components/ui/widgets/event-calendar/event-calendar.labels.d.ts +14 -1
- package/dist/lib/components/ui/widgets/event-calendar/event-calendar.lanes.d.ts +9 -0
- package/dist/lib/components/ui/widgets/event-calendar/event-calendar.read-only.d.ts +57 -0
- package/dist/lib/components/ui/widgets/event-calendar/event-calendar.resources.d.ts +6 -1
- package/dist/lib/components/ui/widgets/event-calendar/index.d.ts +2 -1
- package/dist/lib/components/ui/widgets/event-calendar/partials/all-day-bars.d.ts +14 -0
- package/dist/lib/components/ui/widgets/event-calendar/partials/backdrop-bands.d.ts +11 -0
- package/dist/lib/components/ui/widgets/event-calendar/partials/backdrop-bar.d.ts +11 -0
- package/dist/lib/components/ui/widgets/event-calendar/partials/backdrop-cell.d.ts +13 -0
- package/dist/lib/components/ui/widgets/event-calendar/partials/backdrop-row.d.ts +8 -0
- package/dist/lib/components/ui/widgets/event-calendar/partials/calendar-badge.d.ts +16 -0
- package/dist/lib/components/ui/widgets/event-calendar/partials/month-day-cell.d.ts +4 -1
- package/dist/lib/components/ui/widgets/event-calendar/partials/month-day-parts.d.ts +12 -1
- package/dist/lib/components/ui/widgets/event-calendar/partials/resource-header.d.ts +2 -1
- package/dist/lib/components/ui/widgets/event-calendar/partials/span-event.d.ts +3 -1
- package/dist/lib/components/ui/widgets/event-calendar/partials/time-grid-header-day.d.ts +3 -6
- package/dist/lib/components/ui/widgets/event-calendar/partials/zone-corner.d.ts +10 -2
- package/dist/lib/components/ui/widgets/event-calendar/template.types.d.ts +7 -2
- package/dist/lib/components/ui/widgets/event-calendar/types.d.ts +25 -13
- package/dist/lib/components/ui/widgets/event-calendar/use-backdrops.d.ts +8 -0
- package/dist/lib/components/ui/widgets/event-calendar/use-badges.d.ts +5 -0
- package/dist/lib/components/ui/widgets/event-calendar/use-event-calendar.d.ts +3 -2
- package/dist/lib/components/ui/widgets/event-calendar/use-month-day.d.ts +6 -1
- package/dist/lib/components/ui/widgets/event-calendar/use-time-column.d.ts +1 -0
- package/dist/lib/i18n/resources/en/en.d.ts +31 -0
- package/dist/lib/i18n/resources/en/material.json.d.ts +31 -0
- package/dist/lib/i18n/resources/fr/fr.d.ts +31 -0
- package/dist/lib/i18n/resources/fr/material.json.d.ts +31 -0
- package/dist/lib/i18n/resources/resources.d.ts +62 -0
- package/dist/lib/i18n/translator.d.ts +1 -1
- package/dist/lib.js +1 -1
- package/dist/main.js +54 -54
- package/dist/map.js +7 -7
- package/dist/material.css +1 -1
- package/dist/pdf-reader.js +1 -1
- package/dist/{percent-field-DuoZieW9.js → percent-field-DrI4_J7x.js} +1 -1
- package/dist/{picker-surface-BKzM6TWN.js → picker-surface-D2aYI4v7.js} +4 -4
- package/dist/{popover-ukgGm5B6.js → popover-QQYPWzw6.js} +1 -1
- package/dist/{pos-types-BNXbls3v.js → pos-types-BIeZGGAs.js} +1 -1
- package/dist/{pos-variant-CZ3OZtPq.js → pos-variant-Cdn1AVoA.js} +4 -4
- package/dist/print.js +1 -1
- package/dist/{programmer-variant-DVu-pYhB.js → programmer-variant-CMBybkqw.js} +2 -2
- package/dist/{public-DtXwn8bx.js → public-CPxFxbi4.js} +9 -9
- package/dist/{rich-code-block-DUaSLxLT.js → rich-code-block-DKJ8eKzL.js} +5 -5
- package/dist/{scientific-variant-Ku9feVN1.js → scientific-variant-CXL1ayb7.js} +2 -2
- package/dist/{sortable-provider-DIb1HgZA.js → sortable-provider-BYqwwyr1.js} +2 -2
- package/dist/{spinner-CHWFXCfd.js → spinner-D7ol2n_-.js} +1 -1
- package/dist/{tabs-BideU0Rb.js → tabs-DyiFTy5g.js} +1 -1
- package/dist/theme-picker.js +2 -2
- package/dist/{theme-toggle-DUQTEe1y.js → theme-toggle-QZ0bIrg9.js} +1 -1
- package/dist/{toolbar-items-azkXY5w2.js → toolbar-items-BpOEBsSt.js} +4 -4
- package/dist/{translate-material-d8Q5K5a_.js → translate-material-DlMetRSJ.js} +1 -1
- package/dist/{translator-CxyFsfO0.js → translator-VtzVrnLm.js} +58 -0
- package/dist/{use-chat-composer-FACltVyX.js → use-chat-composer-DQ2rTD_q.js} +17 -17
- package/dist/{use-heading-spy-DHd5TScW.js → use-heading-spy-17B5xa7B.js} +4 -4
- package/dist/{use-material-translation-DA5ECZfA.js → use-material-translation-Cx5u36V5.js} +1 -1
- package/dist/{use-return-focus-CAAvJ6p5.js → use-return-focus-S7g7q_Yn.js} +1 -1
- package/docs/README.md +1 -0
- package/docs/components/data-display.md +13 -7
- package/docs/components/data-table-filter-and-lists.md +3 -0
- package/docs/components/form-fields.md +77 -5
- package/docs/components/navigation-and-layout.md +1 -0
- package/docs/components/rich-widgets.md +87 -5
- package/docs/guides/csp.md +20 -8
- package/docs/guides/event-calendar.md +163 -0
- package/docs/guides/forms.md +3 -1
- package/docs/guides/i18n.md +3 -1
- package/docs/previews/data-display-entityheader.webp +0 -0
- package/docs/previews/data-display-eventchip.webp +0 -0
- package/docs/previews/form-fields-markdowntextareafield.webp +0 -0
- package/docs/previews/rich-widgets-eventcalendar.webp +0 -0
- package/docs/roadmap.md +12 -0
- package/docs/troubleshooting.md +22 -6
- package/lib/components/components.css +1 -0
- package/lib/components/ui/widgets/event-calendar/event-calendar-backdrop.css +43 -0
- package/package.json +1 -1
|
@@ -15,6 +15,7 @@ for the value type each one writes. Everything is imported from
|
|
|
15
15
|
| <img src="../previews/form-fields-choicelistfield.webp" width="96" alt=""> | [ChoiceListField](#choicelistfield) | An editable list of answer choices with correct marks |
|
|
16
16
|
| <img src="../previews/form-fields-textfield.webp" width="96" alt=""> | [TextField](#textfield) | Labelled input with adornments and helper text |
|
|
17
17
|
| <img src="../previews/form-fields-textareafield.webp" width="96" alt=""> | [TextareaField](#textareafield) | Labelled textarea with row sizing and a counter |
|
|
18
|
+
| <img src="../previews/form-fields-markdowntextareafield.webp" width="96" alt=""> | [MarkdownTextareaField](#markdowntextareafield) | Textarea for Markdown and math with a Write / Preview tab list |
|
|
18
19
|
| <img src="../previews/form-fields-emailfield.webp" width="96" alt=""> | [EmailField](#emailfield) | Validates on blur, `mailto:` button |
|
|
19
20
|
| <img src="../previews/form-fields-passwordfield.webp" width="96" alt=""> | [PasswordField](#passwordfield) | Show / hide toggle and a strength meter |
|
|
20
21
|
| <img src="../previews/form-fields-numberfield.webp" width="96" alt=""> | [NumberField](#numberfield) | `number \| null` with clamping and +/− buttons |
|
|
@@ -333,8 +334,8 @@ masculine one.
|
|
|
333
334
|

|
|
334
335
|
|
|
335
336
|
An editable list of answer choices, for a quiz question: each row has a text
|
|
336
|
-
and a "correct" mark. `ChoiceListFormField` wires it to react-hook-form; the
|
|
337
|
-
value is an array of `{ id, text, correct }
|
|
337
|
+
and a "correct" mark (and, with `rowDetail`, a short explanation). `ChoiceListFormField` wires it to react-hook-form; the
|
|
338
|
+
value is an array of `{ id, text, correct }` (plus `explanation?` with `rowDetail`).
|
|
338
339
|
|
|
339
340
|
```tsx
|
|
340
341
|
import { useForm } from 'react-hook-form';
|
|
@@ -403,6 +404,8 @@ form.setError('choices.1.text', { message }, { shouldFocus: true });
|
|
|
403
404
|
| `label` | `ReactNode` | none | names the group (a `group`, or a `radiogroup` in `single`) |
|
|
404
405
|
| `rowLabel`, `addLabel` | `string` | "answer", "Add answer" | lower-case noun for the buttons (translated by default) |
|
|
405
406
|
| `error`, `rowErrors` | `ReactNode`, `{ [id]: ReactNode }` | none | the list's error and each row's, by id (`ChoiceListField`; the form field fills them from the form) |
|
|
407
|
+
| `rowDetail` | `boolean \| { label?, placeholder?, maxLength? }` | off | adds a textarea under each row, stored as `ChoiceRow.explanation` (why a choice is right or wrong). `true` uses the defaults (`ChoiceRowDetail` types the object): label "Explanation" (translated, numbered "Explanation 2"), no placeholder, no limit. `maxLength` limits the text and shows a counter. Off: rows are `{ id, text, correct }` and nothing changes |
|
|
408
|
+
| `rowDetailErrors` | `{ [id]: ReactNode }` | none | each row's detail error, by id (`ChoiceListField`; the form field fills them from the form) |
|
|
406
409
|
| `onBlur` | `() => void` | none | `ChoiceListField` alone: called when the focus leaves the whole list. In a form the list's root is registered with the form's `FieldBlur`, so touched works without it |
|
|
407
410
|
| `onAssignIds` | `(rows) => void` | `onChange` | `ChoiceListField`: called once on mount with ids given to rows that had none or repeated one; the form field sets them without marking the form dirty |
|
|
408
411
|
| other props | `div` props | | passed to the group's root (`className`, `id`, `data-*`, `ref`) |
|
|
@@ -414,6 +417,28 @@ form.setError('choices.1.text', { message }, { shouldFocus: true });
|
|
|
414
417
|
one on mount (the form field does it with `setValue` when the `FormProvider` above is its own form, and then leaves the form clean; otherwise it calls `onChange`). Give the rows ids in `defaultValues`, the clean path: with assigned ids `defaultValues` still lack them, so `dirtyFields.choices` marks rows after the first real edit.
|
|
415
418
|
In `single`, the new row is marked when no row is. A list that arrives with
|
|
416
419
|
several rows marked is not changed: it fails the `too-many` rule.
|
|
420
|
+
- **Row detail:** with `rowDetail`, each row gets a textarea named "Explanation 2"
|
|
421
|
+
(or your `label`) that edits `ChoiceRow.explanation`. It follows its row through
|
|
422
|
+
reorder, add and remove, like `text`. A resolver's issue on
|
|
423
|
+
`choices.2.explanation` shows under that textarea, and `setFocus('choices.2.explanation')`
|
|
424
|
+
focuses it. The built-in rules never refuse a blank explanation; require one in
|
|
425
|
+
your schema. `explanation` stays absent on a row until its textarea is edited.
|
|
426
|
+
|
|
427
|
+
```tsx
|
|
428
|
+
import type { ChoiceRowDetail } from '@nxgt/material/components';
|
|
429
|
+
|
|
430
|
+
const detail: ChoiceRowDetail = { label: 'Why', maxLength: 200 };
|
|
431
|
+
// <ChoiceListFormField … rowDetail={detail} />
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
```tsx
|
|
435
|
+
<ChoiceListFormField
|
|
436
|
+
control={control}
|
|
437
|
+
name="choices"
|
|
438
|
+
label="Answers"
|
|
439
|
+
rowDetail={{ label: 'Why', placeholder: 'Why is it right or wrong?', maxLength: 200 }}
|
|
440
|
+
/>
|
|
441
|
+
```
|
|
417
442
|
- **Semantics:** each row's text is a labelled input ("Answer 2"); its mark
|
|
418
443
|
is a radio or a checkbox named "Correct: answer 2" (the visible word
|
|
419
444
|
"Correct" starts the name), so `getByRole('checkbox', { name: 'Correct: answer 2' })`
|
|
@@ -433,7 +458,7 @@ form.setError('choices.1.text', { message }, { shouldFocus: true });
|
|
|
433
458
|
list, and blank rows are marked invalid (only for that rule, never for a resolver's or another rule's message). With a resolver (zod), an issue on
|
|
434
459
|
`choices.2.text` shows under row 3 and an issue on `choices` under the list.
|
|
435
460
|
When both fail in one pass, react-hook-form keeps only the list's.
|
|
436
|
-
- **Translations:** `material.choice-list.*` (English and French). The
|
|
461
|
+
- **Translations:** `material.choice-list.*` (English and French; `detail` is the default label of `rowDetail`). The
|
|
437
462
|
messages name the row with `rowLabel` ("Fill in every answer"); `too-few`
|
|
438
463
|
is a plural that adds `s` to it, and the French templates carry the
|
|
439
464
|
feminine article, so a French app overrides the keys for a masculine noun.
|
|
@@ -500,6 +525,44 @@ stores `string`.
|
|
|
500
525
|
|
|
501
526
|
---
|
|
502
527
|
|
|
528
|
+
## MarkdownTextareaField
|
|
529
|
+
|
|
530
|
+

|
|
531
|
+
|
|
532
|
+
Textarea for Markdown and KaTeX math with a **Write / Preview** tab list, the
|
|
533
|
+
usual label, helper text, error and `maxLength` counter, and a collapsed
|
|
534
|
+
formatting help (bold, list, code, `$…$`, `$$…$$`, escaping `\$`). The preview
|
|
535
|
+
is rendered by [`Markdown`](./data-display.md#markdown); raw HTML is never rendered.
|
|
536
|
+
|
|
537
|
+
```tsx
|
|
538
|
+
import { MarkdownTextareaField, MarkdownTextareaFormField } from '@nxgt/material/components';
|
|
539
|
+
|
|
540
|
+
<MarkdownTextareaField label="Prompt" maxLength={4000} helperText="Markdown and $math$." />
|
|
541
|
+
|
|
542
|
+
<MarkdownTextareaFormField control={control} name="prompt" label="Prompt" maxLength={4000} />
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
| Prop | Type | Default | Effect |
|
|
546
|
+
| --- | --- | --- | --- |
|
|
547
|
+
| `minRows` | `number` | `6` | minimum height of the textarea and the preview, in lines |
|
|
548
|
+
| `maxLength` | `number` | — | shows an `n/max` counter, red when over |
|
|
549
|
+
| `showCount` | `boolean` | `true` when `maxLength` is set | force the counter on or off |
|
|
550
|
+
| `math` | `boolean` | `true` | render `$…$` and `$$…$$` in the preview |
|
|
551
|
+
| `mermaid` | `boolean` | `false` | draw ```` ```mermaid ```` fences in the preview |
|
|
552
|
+
| `showHelp` | `boolean` | `true` | the formatting help under the field |
|
|
553
|
+
| `previewClassName` | `string` | — | class of the preview panel |
|
|
554
|
+
| `error` | `boolean` | — | error outline and helper colour |
|
|
555
|
+
|
|
556
|
+
The Write / Preview toggle is a `tablist` (arrow keys move between the tabs);
|
|
557
|
+
the textarea stays mounted while Preview shows. Text labels use the
|
|
558
|
+
`material.markdown-textarea-field.*` i18n keys.
|
|
559
|
+
|
|
560
|
+
**Emits:** the native `ChangeEvent<HTMLTextAreaElement>`; `MarkdownTextareaFormField`
|
|
561
|
+
stores `string`.
|
|
562
|
+
**Form wrapper:** `MarkdownTextareaFormField` — `control` and `name`, see
|
|
563
|
+
[forms](../guides/forms.md#the-pair) and [what each field emits](../guides/forms.md#what-each-field-emits).
|
|
564
|
+
**Storybook:** `Form fields/MarkdownTextareaField` · **Vue:** none yet
|
|
565
|
+
|
|
503
566
|
## EmailField
|
|
504
567
|
|
|
505
568
|

|
|
@@ -1753,7 +1816,14 @@ import { AttributesFormField } from '@nxgt/material/components';
|
|
|
1753
1816
|
```tsx
|
|
1754
1817
|
import { AttributeValuesFormFieldSet, FormSection } from '@nxgt/material/components';
|
|
1755
1818
|
|
|
1756
|
-
|
|
1819
|
+
interface ItemValues {
|
|
1820
|
+
label: string;
|
|
1821
|
+
attributes: Record<string, string>;
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
const form = useForm<ItemValues>({
|
|
1825
|
+
defaultValues: { label: 'Shirt', attributes: { color: '#2563eb', size: 'M' } },
|
|
1826
|
+
});
|
|
1757
1827
|
|
|
1758
1828
|
<FormSection title="Attributes">
|
|
1759
1829
|
<AttributeValuesFormFieldSet
|
|
@@ -1769,10 +1839,12 @@ const form = useForm({ defaultValues: { attributes: { color: '#2563eb', size: 'M
|
|
|
1769
1839
|
</FormSection>
|
|
1770
1840
|
```
|
|
1771
1841
|
|
|
1842
|
+
`T` is inferred from `control`, so `name` is checked against the form's paths and no `as never` cast is needed.
|
|
1843
|
+
|
|
1772
1844
|
| Prop | |
|
|
1773
1845
|
| --- | --- |
|
|
1774
1846
|
| `attributes` | the attributes to fill, one input each |
|
|
1775
|
-
| `control`, `name` | the form and the path of the object it writes |
|
|
1847
|
+
| `control`, `name` | the form and the path of the object it writes; generic over the form values (`Control<T>`, `Path<T>`), so a typed form needs no cast |
|
|
1776
1848
|
| `label` | the fieldset's accessible name (required) |
|
|
1777
1849
|
| `hideLabel` | default `false`. `true` keeps `label` as the name but draws nothing, for a `FormSection` that already has the title |
|
|
1778
1850
|
| `defaultValues` | optional fallback, used only while the form value at `name` is empty: `undefined`, `null` or an object with no keys (`{}`). Any other value in the form wins |
|
|
@@ -1249,6 +1249,7 @@ import { Badge, Button, Card, CardContent, Description, RecordDetails, RecordPag
|
|
|
1249
1249
|
| `created`, `updated` | `{ label: string; at: Date \| string \| number }` | — | The two segments of the muted meta line; each « label date » stays on one line |
|
|
1250
1250
|
| `timeZone` | IANA zone | `'UTC'` | The zone the dates are written in, the same on a server and in a browser. Dates have no seconds and no zone suffix |
|
|
1251
1251
|
| `maxColumns` | `1`-`6` | `4` | The most columns of the facts: the panel's own width gives one, a stacked and wide panel up to this |
|
|
1252
|
+
| other `div` props | `ComponentProps<'div'>` minus `title` | — | `data-*`, `id`, `aria-*` and `className` go to the root card; `className` is merged with its own classes |
|
|
1252
1253
|
|
|
1253
1254
|
The facts are a [`DescriptionList`](data-display.md#descriptionlist), so the columns follow the card's width, not the window's.
|
|
1254
1255
|
|
|
@@ -309,15 +309,18 @@ To edit and delete in your own dialog instead of the built-in inline fields:
|
|
|
309
309
|
| `onEventClick` | `(event: CalendarEvent) => void` | — | an event was clicked; it runs even with `onSlotClick` set (an event click never reports a slot) |
|
|
310
310
|
| `eventPreview` | `boolean` | `true` | `false` skips the preview popover, so `onEventClick` alone handles the click |
|
|
311
311
|
| `previewFields` | `EventCalendarPreviewField[]` | all three | the fields the preview edits in place (`'title' \| 'color' \| 'description'`); `[]` keeps the time range and the actions |
|
|
312
|
-
| `
|
|
313
|
-
| `
|
|
312
|
+
| `readOnlyEvents` | `boolean` | `false` | no event can be dragged, resized, moved with the keys, edited or deleted; events stay focusable, clickable and at full strength, `onSlotClick` still fires, see [Read-only events](#eventcalendar-read-only-events) |
|
|
313
|
+
| `spans` | `EventCalendarSpan[]` | — | stretches of time shaded behind the events (a closure, a term, a half day off); they never block a slot, see the [guide](../guides/event-calendar.md#spans) |
|
|
314
|
+
| `dayBadges` / `weekBadges` | `EventCalendarBadges` | — | a `{ label, tone? }` badge on a day, or on a week (keyed by its first day per `weekStartsOn`), see the [guide](../guides/event-calendar.md#day-and-week-badges) |
|
|
315
|
+
| `onEventEdit` | `(event: CalendarEvent) => void` | — | adds an Edit button to the preview; the app opens its own editor; hidden when the calendar or the event is `disabled`, and with `readOnlyEvents` |
|
|
316
|
+
| `onEventDelete` | `(event: CalendarEvent) => void` | — | the preview's delete button calls this instead of the built-in confirmation and removal; the app confirms and deletes; hidden when the calendar or the event is `disabled`, so it never runs for those, nor with `readOnlyEvents` |
|
|
314
317
|
| `view` / `defaultView` | `'month' \| 'week' \| 'workweek' \| 'day' \| 'agenda'` | `'month'` | the visible view |
|
|
315
318
|
| `date` / `defaultDate` | `string` | today | the date the view is centred on |
|
|
316
319
|
| `startHour` / `endHour` | `number` | `0` / `24` | hours shown in the time grids |
|
|
317
320
|
| `slotDurationMinutes` | `number` | `60` | grid step for creation and snapping |
|
|
318
321
|
| `toolbar` | `ReactNode` | built-in | replace (or `null` to hide) the toolbar |
|
|
319
322
|
| `views` | `EventCalendarView[]` | all five | the views the switcher offers, in order |
|
|
320
|
-
| `weekStartsOn` | `0`–`6` | the locale's | the first day of the week |
|
|
323
|
+
| `weekStartsOn` | `0`–`6` | the locale's | the first day of the week; the week view starts on it and `weekBadges` are keyed by it ([guide](../guides/event-calendar.md#weekstartson)) |
|
|
321
324
|
| `density` | `'comfortable' \| 'compact'` | `'comfortable'` | compact: 40px hours, shorter month cells |
|
|
322
325
|
| `eventVariant` | `'soft' \| 'solid' \| 'outline'` | `'soft'` | how events are drawn ([EventChip](data-display.md#eventchip)) |
|
|
323
326
|
| `sidebar` | `boolean` | `false` | the side panel from 1024px: mini month and the day's events |
|
|
@@ -339,12 +342,40 @@ trade-off: each event stays reachable by keyboard (Tab and the arrow keys move
|
|
|
339
342
|
through the events), and the agenda view lists every event in full, which is the
|
|
340
343
|
accessible route.
|
|
341
344
|
|
|
345
|
+
### EventCalendar read-only events
|
|
346
|
+
|
|
347
|
+
A schedule to read and click, not to rearrange: set `readOnlyEvents`. No event
|
|
348
|
+
can be dragged, resized or moved with Ctrl or Cmd and the arrow keys, and the
|
|
349
|
+
preview offers no edit or delete. Each event keeps its look, its tab stop and
|
|
350
|
+
`onEventClick`, empty slots still call `onSlotClick`, and no drag instruction
|
|
351
|
+
is announced: the calendar is named "Event calendar" in both cases, and only a
|
|
352
|
+
draggable one describes itself with "Drag event". Because `readOnlyEvents` leaves
|
|
353
|
+
no edit path, it also hides the Edit button of `onEventEdit`.
|
|
354
|
+
|
|
355
|
+
```tsx
|
|
356
|
+
<EventCalendar
|
|
357
|
+
defaultView="day"
|
|
358
|
+
defaultDate="2026-03-12"
|
|
359
|
+
readOnlyEvents
|
|
360
|
+
onEventClick={(event) => open(event.id)}
|
|
361
|
+
onSlotClick={(slot) => book(slot.start)}
|
|
362
|
+
events={events}
|
|
363
|
+
/>
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
It is not `disabled`, which locks the whole calendar (no slot click, no
|
|
367
|
+
navigation), nor `CalendarEvent.disabled`, which dims one event and makes its
|
|
368
|
+
button inert. Both keep their meaning; `readOnlyEvents` is React only for now
|
|
369
|
+
(the Vue twin is tracked in #2526).
|
|
370
|
+
|
|
342
371
|
### EventCalendar event badge
|
|
343
372
|
|
|
344
373
|
An event takes an optional `badge` (`CalendarEventBadge`: `{ label, tone? }`): a
|
|
345
374
|
small tinted label that marks it, such as a category or a follow-up. `tone` is a
|
|
346
375
|
tone of the library or one your app declared (neutral when omitted). The same
|
|
347
|
-
prop is on [`EventChip`](data-display.md).
|
|
376
|
+
prop is on [`EventChip`](data-display.md). An event also takes `tone` (its colour)
|
|
377
|
+
and `status: 'cancelled'` (struck through, announced as cancelled); see
|
|
378
|
+
[the EventCalendar guide](../guides/event-calendar.md#event-tone-badges-and-cancelled-events).
|
|
348
379
|
|
|
349
380
|
```tsx
|
|
350
381
|
import { EventCalendar, type CalendarEvent } from '@nxgt/material/components';
|
|
@@ -372,6 +403,28 @@ const events: CalendarEvent[] = [
|
|
|
372
403
|
in the event's accessible name. The agenda view wraps a long description
|
|
373
404
|
instead of cutting it.
|
|
374
405
|
|
|
406
|
+
For a status and a priority together, add `badges` (`CalendarEventBadge[]`):
|
|
407
|
+
further badges drawn after `badge`, in the same places and order, all part of
|
|
408
|
+
the event's accessible name (`Visit, Follow-up, Urgent, 2/3`). Each becomes its
|
|
409
|
+
own tone dot where the box is narrow or short. `badges` works with or without
|
|
410
|
+
`badge`.
|
|
411
|
+
|
|
412
|
+
```tsx
|
|
413
|
+
const events: CalendarEvent[] = [
|
|
414
|
+
{
|
|
415
|
+
id: '2',
|
|
416
|
+
title: 'Operation',
|
|
417
|
+
start: '2026-03-12T11:00:00Z',
|
|
418
|
+
end: '2026-03-12T13:00:00Z',
|
|
419
|
+
badge: { label: 'Scheduled', tone: 'info' },
|
|
420
|
+
badges: [
|
|
421
|
+
{ label: 'Urgent', tone: 'warning' },
|
|
422
|
+
{ label: '2/3', tone: 'success' },
|
|
423
|
+
],
|
|
424
|
+
},
|
|
425
|
+
];
|
|
426
|
+
```
|
|
427
|
+
|
|
375
428
|
### EventCalendar resources
|
|
376
429
|
|
|
377
430
|
`resources` splits the **day view** into one column per room, person or team
|
|
@@ -547,7 +600,7 @@ zone's wall clock. Without it, nothing changes: the machine's clock.
|
|
|
547
600
|
<EventCalendar view="day" date="2026-03-12" timeZone="Asia/Tokyo" events={events} />
|
|
548
601
|
```
|
|
549
602
|
|
|
550
|
-
**Storybook:** `Rich widgets/EventCalendar`, `Rich widgets/EventCalendar/Layouts`, `Rich widgets/EventCalendar/Resources`, `Rich widgets/EventCalendar/Resources on a phone`, `Rich widgets/EventCalendar/Resources a11y`, `Rich widgets/EventCalendar/Time zone`, `Rich widgets/EventCalendar/Back to back`, `Rich widgets/EventCalendar/Resource tone`, `Rich widgets/EventCalendar/Hydration`, `Rich widgets/EventCalendar/Template week` · **Vue:** `EventCalendar` (the new props and layouts, `resources`, `timeZone` and `mode="template"`: React only for now, #892, #1780 and #2458)
|
|
603
|
+
**Storybook:** `Rich widgets/EventCalendar`, `Rich widgets/EventCalendar/Layouts`, `Rich widgets/EventCalendar/Resources`, `Rich widgets/EventCalendar/Resources on a phone`, `Rich widgets/EventCalendar/Resources a11y`, `Rich widgets/EventCalendar/Time zone`, `Rich widgets/EventCalendar/Back to back`, `Rich widgets/EventCalendar/Resource tone`, `Rich widgets/EventCalendar/Hydration`, `Rich widgets/EventCalendar/Template week`, `Rich widgets/EventCalendar/Spans`, `Rich widgets/EventCalendar/Day and week badges`, `Rich widgets/EventCalendar/Event badge`, `Rich widgets/EventCalendar/Event tone and cancelled`, `Rich widgets/EventCalendar/Read-only events` · **Vue:** `EventCalendar` (the new props and layouts, `resources`, `timeZone` and `mode="template"`: React only for now, #892, #1780 and #2458)
|
|
551
604
|
|
|
552
605
|
---
|
|
553
606
|
|
|
@@ -2781,8 +2834,37 @@ French bundle — see [i18n](../guides/i18n.md).
|
|
|
2781
2834
|
| `QuestionCard` `preview` | `boolean` | `false` | an author's preview, `<QuestionCard question={q} preview />`: forces `revealed` and `readOnly`, drops `onValueChange`, and shows the marks (the `question.correct` options marked, the expected text or number filled in) and the `explanation`, with no verdict banner. `verdict` and `showFeedback` are always ignored in a preview; `value` is ignored when the question has a `correct` — an `open` or `code` question has none, so pass its reference answer as `value`. Not for a learner's attempt |
|
|
2782
2835
|
| `QuestionCard` `readOnly` / `busy` | `boolean` | `false` | `readOnly`: an answer already submitted, shown without the revealed marks; `busy`: read-only and `aria-busy` while it is saved; every kind honours both |
|
|
2783
2836
|
| new strings | `material.education.kind-open`, `kind-code`, `code-placeholder` | — | English fallbacks and a French bundle |
|
|
2837
|
+
| `Question` `markdown` | `boolean` | `false` | draws the `prompt`, the string option labels, `explanation`, option explanations and hints as Markdown with math (`$x^2$`), through the safe `Markdown` (raw HTML is not rendered, mermaid off). The prompt, option labels and the verdict's "Correct answer" use an inline form (paragraph unwrapped, no lists or headings) so the HTML stays valid in a `<legend>` or a label; explanations and hints keep blocks |
|
|
2838
|
+
| `Question` `hints` | `string[]` | — | revealed one at a time behind a "Show hint 1 of N" button; the focus moves to the hint just shown and the hint's name is "Hint 1 of N" (one announcement). Hidden once revealed. The single `hint` is deprecated (kept: a Show / Hide toggle), ignored when `hints` is set |
|
|
2839
|
+
| `QuestionOption` `explanation` | `string` | — | why this option is right or wrong; a list "About each answer" under the card once revealed or in a `preview` |
|
|
2840
|
+
| `Question` `references` | `{ title: string; url?: string }[]` | — | a labelled "References" list once revealed or in a `preview`; a safe `url` is a link, an external one with `rel="noopener noreferrer"` |
|
|
2841
|
+
| `Question` `kind: 'code'` | `starterCode?`, `examples?: { input, expected }[]` | — | `starterCode` fills the field while there is no draft (`value` null or undefined; an empty string is a draft), it reaches `onValueChange` on the first edit only; `examples` is a table (Input, Expected) above the field |
|
|
2842
|
+
| new strings | `material.quiz.rich.*` | — | English fallbacks and a French bundle (React only for now) |
|
|
2784
2843
|
| `QuestionCard` `variant` / `density` / `showShortcuts` / `number` / `showFeedback` | — | `'card'`, `'comfortable'`, `false`, —, `true` | framed or plain, spacing, key badges, "Question 2", the verdict banner (for a card with marks and no banner, use `preview`) |
|
|
2785
2844
|
|
|
2845
|
+
A quiz question with Markdown, math, progressive hints and references:
|
|
2846
|
+
|
|
2847
|
+
```tsx
|
|
2848
|
+
<QuestionCard
|
|
2849
|
+
question={{
|
|
2850
|
+
id: 'q1',
|
|
2851
|
+
kind: 'choice',
|
|
2852
|
+
markdown: true,
|
|
2853
|
+
prompt: 'Solve $2x + 6 = 14$',
|
|
2854
|
+
options: [
|
|
2855
|
+
{ id: 'a', label: '$x = 4$', explanation: 'Subtract 6, then divide by 2.' },
|
|
2856
|
+
{ id: 'b', label: '$x = 10$', explanation: 'You added 6 instead.' },
|
|
2857
|
+
],
|
|
2858
|
+
correct: 'a',
|
|
2859
|
+
hints: ['Isolate $x$.', 'Subtract 6 from both sides.'],
|
|
2860
|
+
references: [{ title: 'Linear equations', url: 'https://example.org/linear' }],
|
|
2861
|
+
}}
|
|
2862
|
+
value={value}
|
|
2863
|
+
onValueChange={setValue}
|
|
2864
|
+
revealed={checked}
|
|
2865
|
+
/>
|
|
2866
|
+
```
|
|
2867
|
+
|
|
2786
2868
|
An open answer and a code answer, with the code editor from its own subpath
|
|
2787
2869
|
(it is client-only, so load it lazily in an SSR app):
|
|
2788
2870
|
|
package/docs/guides/csp.md
CHANGED
|
@@ -38,7 +38,7 @@ Components outside this table need nothing more. Measured per component:
|
|
|
38
38
|
| --- | --- | --- |
|
|
39
39
|
| `PdfReader` | `worker-src 'self'` (the worker is a file of the package), `connect-src` for the PDF's and the assets' origins, and `script-src 'wasm-unsafe-eval'` for the JPEG 2000 and ICC WebAssembly decoders (without it: a JavaScript fallback for JPEG 2000, device colours for ICC profiles) | worker checked in the same Playwright run (Vite production build under `worker-src 'self'`), the rest read from pdf.js's code: [PDF reader guide](pdf-reader.md#content-security-policy) |
|
|
40
40
|
| `CodeEditor`, `CodeDiff` (Monaco) | `@monaco-editor/react` loads Monaco from `https://cdn.jsdelivr.net/npm/monaco-editor@…/min/vs` by default: that host in `style-src-elem` (Monaco's CSS) and `font-src` (its icon font), plus `worker-src blob:` | not yet verified in a browser. Safe default: allow `https://cdn.jsdelivr.net` in `script-src`, `style-src-elem` and `font-src`, and `worker-src blob:`; or stay on `'self'` by [serving Monaco yourself](#monaco-on-self) |
|
|
41
|
-
| `Markdown` (maths, mermaid, code) | nothing beyond the policy above,
|
|
41
|
+
| `Markdown` (maths, mermaid, code) | nothing beyond the policy above, on the server or the client (math needs `NonceProvider`); mermaid's mindmap and architecture diagrams still add an un-nonced `<style>` | checked in Chromium under that policy with `style-src-attr 'none'`, no `img-src data:` and no `'unsafe-eval'`: [Markdown under a strict CSP](#markdown-katex-mermaid-and-code-under-a-strict-csp) |
|
|
42
42
|
| `RichTextViewer` (`@nxgt/material/editor/viewer`) | none, but it emits `style` attributes: a text colour, a highlight, a text alignment or a table cell's background in the saved document becomes an inline `style` (only allow-listed properties with plain values survive), so a page that shows such a document needs `style-src-attr 'unsafe-inline'`. A document with none sends no style | measured by reading the renderers; a strict `style-src-attr 'none'` page shows the document without those colours |
|
|
43
43
|
| `Camera` | none: `getUserMedia` is not governed by CSP; the captured photo is a `data:` image | `media-src` for the live preview is not yet verified. Safe default: `media-src 'self' blob:` (a `srcObject` stream usually needs nothing) |
|
|
44
44
|
| `MediaPlayer` with `hlsStreaming()` | `connect-src` and `media-src blob:` for the stream; `worker-src blob:` only with `config: { enableWorker: true }` (the adapter defaults to no worker) | see the [streaming guide](media-streaming.md#content-security-policy) |
|
|
@@ -211,6 +211,7 @@ mermaid and pdf.js's font fallback are the [known gaps](#known-gaps).
|
|
|
211
211
|
| `Spinner` (variant `4`), `PdfReader` (search highlights, night mode) | nothing | their rules are in the stylesheet (`spinner.css`, `pdf-viewer.css`) |
|
|
212
212
|
| `Progress`, `CircularProgress` | nothing | the server draws SVG percentages, SMIL and path data, and the rules are in the stylesheet (`progress.css`); no `style` attribute |
|
|
213
213
|
| `ScrollArea`, `Select` (Radix) | the nonce | Radix renders a `<style>` element with `NonceProvider`'s nonce: wrap the app in [`NonceProvider`](#nonceprovider) |
|
|
214
|
+
| `Markdown` (math) | the nonce | one `<style>` of `mk-` rules replacing KaTeX's `style` attributes, with `NonceProvider`'s nonce |
|
|
214
215
|
| `RichTextEditor` (tiptap) | the nonce | tiptap, on mount, with `NonceProvider`'s nonce as `injectNonce` |
|
|
215
216
|
| `Dialog`, `AlertDialog`, `Sheet`, `ConfirmationDialog`, … (Radix scroll lock) | the nonce | react-style-singleton, when a modal opens: see below |
|
|
216
217
|
|
|
@@ -278,8 +279,17 @@ the page carry the nonce in its meta tag).
|
|
|
278
279
|
(`data-scheme`), and `maxHeight` is a custom property written through the
|
|
279
280
|
CSSOM in a layout effect (a server-rendered block is full height until
|
|
280
281
|
hydration).
|
|
281
|
-
- **KaTeX**:
|
|
282
|
-
|
|
282
|
+
- **KaTeX**: its markup carries inline `style="…"` attributes (offsets,
|
|
283
|
+
heights, widths), which `style-src-attr 'none'` blocks on a server render
|
|
284
|
+
(hydration does not rewrite them). `Markdown` turns each into a class
|
|
285
|
+
(`mk-<hash>`, one per distinct declaration list, the same on the server and
|
|
286
|
+
the client) and appends one `<style>` with the rules, given the nonce of the
|
|
287
|
+
nearest `NonceProvider`: `style-src-elem 'nonce-…'` allows it, and the
|
|
288
|
+
formula is laid out as before, with server and client rendering the same
|
|
289
|
+
markup. So `NonceProvider` is required for math under a nonce policy
|
|
290
|
+
(without it the `<style>` has no nonce and is blocked); a declaration that
|
|
291
|
+
is not a plain `property:value` is dropped, never copied to the sheet. Its
|
|
292
|
+
fonts are files ([KaTeX fonts](#katex-fonts)).
|
|
283
293
|
- **Mermaid**: the diagram stays an image, now from a `blob:` URL (revoked when
|
|
284
294
|
the diagram goes), so the SVG's own `<style>` and `style` attributes live in
|
|
285
295
|
the image and the page needs neither `data:` nor `style-src-attr` for them.
|
|
@@ -293,11 +303,13 @@ the page carry the nonce in its meta tag).
|
|
|
293
303
|
each is a pass-through once the last render ends and is restored only if
|
|
294
304
|
no other script patched it since). Unrelated calls, such as an editor
|
|
295
305
|
parsing HTML, are left alone.
|
|
296
|
-
- **Left**:
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
306
|
+
- **Left**: mermaid's mindmap and architecture diagrams (cytoscape) add a
|
|
307
|
+
`<style>` to `<head>` with no nonce, which `style-src-elem 'nonce-…'` blocks:
|
|
308
|
+
the diagram still draws, without that sheet's rules. The classes mechanism
|
|
309
|
+
above does not reach it; allow that element (a hash of the sheet, or
|
|
310
|
+
`style-src-elem 'unsafe-inline'`) only if you show those two diagram types
|
|
311
|
+
([known gaps](#known-gaps)). Flowcharts, sequence and the other types are
|
|
312
|
+
checked and need nothing.
|
|
301
313
|
|
|
302
314
|
### Known gaps
|
|
303
315
|
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# EventCalendar: spans, badges, tones and cancelled events
|
|
2
|
+
|
|
3
|
+
`EventCalendar` (`@nxgt/material/components`) shows more than events. It can
|
|
4
|
+
shade what the days themselves are (a closure, a term, a half day off), put a
|
|
5
|
+
short label on a day or a week, draw an event in a tone, mark one as cancelled,
|
|
6
|
+
and lock the whole schedule to reading and clicking.
|
|
7
|
+
|
|
8
|
+
- [Spans](#spans)
|
|
9
|
+
- [Day and week badges](#day-and-week-badges)
|
|
10
|
+
- [Event tone, badges and cancelled events](#event-tone-badges-and-cancelled-events)
|
|
11
|
+
- [Read-only events](#read-only-events)
|
|
12
|
+
- [In each view](#in-each-view)
|
|
13
|
+
- [weekStartsOn](#weekstartson)
|
|
14
|
+
- [Translations](#translations)
|
|
15
|
+
- [Exports](#exports)
|
|
16
|
+
|
|
17
|
+
## Spans
|
|
18
|
+
|
|
19
|
+
`spans` shades stretches of time behind the events. A span never blocks a slot:
|
|
20
|
+
events can still be made and dropped over it.
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import { EventCalendar, type EventCalendarSpan } from '@nxgt/material/components';
|
|
24
|
+
|
|
25
|
+
const spans: EventCalendarSpan[] = [
|
|
26
|
+
// Whole days: date-only values, `end` is exclusive (this is 24 December only).
|
|
27
|
+
{ id: 'closed', start: '2026-12-24', end: '2026-12-25', label: 'Closed', tone: 'warning' },
|
|
28
|
+
// A range of days.
|
|
29
|
+
{ id: 'term', start: '2026-12-28', end: '2027-01-04', label: 'Break', tone: 'info' },
|
|
30
|
+
// A half day: a value with a time is an instant.
|
|
31
|
+
{ id: 'half', start: '2026-12-23T12:00:00', end: '2026-12-24', label: 'Half day' },
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
<EventCalendar defaultEvents={events} spans={spans} timeZone="Europe/Paris" />;
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
- A date-only `start` / `end` means whole days, read in the calendar's
|
|
38
|
+
`timeZone`. `end` is always exclusive.
|
|
39
|
+
- `tone` is a tone of the library (default `'neutral'`).
|
|
40
|
+
- With `resources`, a span with a `resourceId` shades only its column; one with
|
|
41
|
+
none shades every column; one with an unknown id shades none.
|
|
42
|
+
- The shading is a thin hatch drawn with CSS classes (no inline gradient). It is
|
|
43
|
+
`aria-hidden`, takes no pointer event and no tab stop; its label is read in
|
|
44
|
+
the name of the day or column it falls on.
|
|
45
|
+
- `spans` is not a prop of `mode="template"`, which has no dates.
|
|
46
|
+
|
|
47
|
+
## Day and week badges
|
|
48
|
+
|
|
49
|
+
`dayBadges` and `weekBadges` are records of `{ label, tone? }` keyed by a
|
|
50
|
+
`YYYY-MM-DD` date.
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
<EventCalendar
|
|
54
|
+
defaultEvents={events}
|
|
55
|
+
weekStartsOn={1}
|
|
56
|
+
dayBadges={{
|
|
57
|
+
'2026-12-24': { label: 'Eve', tone: 'info' },
|
|
58
|
+
'2026-12-25': { label: 'Holiday', tone: 'warning' },
|
|
59
|
+
}}
|
|
60
|
+
weekBadges={{
|
|
61
|
+
// The key is the first day of the week, per `weekStartsOn` (a Monday here).
|
|
62
|
+
'2026-12-21': { label: 'Week 52', tone: 'neutral' },
|
|
63
|
+
}}
|
|
64
|
+
/>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
A day badge is read in the name of its day (the day header, or the month cell); a
|
|
68
|
+
week badge in the name of the week's first day. Where the
|
|
69
|
+
room is short (a phone's week column or month cell), a badge becomes a small
|
|
70
|
+
tone bar, distinct from the event dots. Neither prop exists in `mode="template"`.
|
|
71
|
+
|
|
72
|
+
## Event tone, badges and cancelled events
|
|
73
|
+
|
|
74
|
+
A `CalendarEvent` takes a `tone`, several `badges` and a `status`.
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
const events: CalendarEvent[] = [
|
|
78
|
+
{
|
|
79
|
+
id: 'a',
|
|
80
|
+
title: 'Planning',
|
|
81
|
+
start: '2026-12-22T09:00:00',
|
|
82
|
+
end: '2026-12-22T10:00:00',
|
|
83
|
+
tone: 'success', // a `color` still wins, then the tone, then the resource's
|
|
84
|
+
badge: { label: 'Team', tone: 'info' },
|
|
85
|
+
badges: [{ label: '2/5', tone: 'neutral' }],
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: 'b',
|
|
89
|
+
title: 'Review',
|
|
90
|
+
start: '2026-12-22T11:00:00',
|
|
91
|
+
end: '2026-12-22T12:00:00',
|
|
92
|
+
status: 'cancelled', // struck through, muted, dashed outline; still draggable
|
|
93
|
+
},
|
|
94
|
+
];
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
A cancelled event strikes its title (`<del>`) and adds "Cancelled" to its
|
|
98
|
+
accessible name, so the state is never carried by style alone. The preview
|
|
99
|
+
shows such an event's title struck and named the same way (and read-only, since
|
|
100
|
+
its title is not an input then). `tone` and
|
|
101
|
+
`badges` work in every view and in `mode="template"`. `EventChip` takes the same
|
|
102
|
+
`tone`, `badges` and `cancelled`.
|
|
103
|
+
|
|
104
|
+
## Read-only events
|
|
105
|
+
|
|
106
|
+
`readOnlyEvents` forbids dragging, resizing, key moves, and edits or deletes
|
|
107
|
+
from the preview, for every event. Events stay focusable, clickable
|
|
108
|
+
(`onEventClick`) and at full strength, and `onSlotClick` still fires.
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
<EventCalendar events={events} readOnlyEvents onEventClick={(event) => open(event.id)} />
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
No drag instruction is announced. The calendar is always named "Event calendar"
|
|
115
|
+
(`material.event-calendar.label`); a calendar whose events can be dragged adds
|
|
116
|
+
"Drag event" (`material.event-calendar.drag`) as its description, which a
|
|
117
|
+
read-only one leaves out. The preview shows no Edit or Delete button, so
|
|
118
|
+
`onEventEdit` and `onEventDelete` are not offered either.
|
|
119
|
+
|
|
120
|
+
## In each view
|
|
121
|
+
|
|
122
|
+
| View | Spans | Day badge | Week badge | Event tone and cancelled |
|
|
123
|
+
| --- | --- | --- | --- | --- |
|
|
124
|
+
| Month | shades the cell, or its half for a half day; the label shows from 7rem of cell width, with a dot under it | next to the day number | on the first cell of the row, with room below its events | chip tone; struck title, dashed outline |
|
|
125
|
+
| Week / workweek | hatched band under the hours with its label, and a bar in the all-day strip (a half day only the band) | in the day's header | in the gutter corner | block tone; struck title, dashed outline |
|
|
126
|
+
| Day | the same as the week; with `resources`, a span shades its own column | in the day's header | in the gutter corner | the same |
|
|
127
|
+
| Agenda, phone day list | a plain, non-clickable row | in the day's heading | in the week's heading | tone dot; struck title, "Cancelled" in words |
|
|
128
|
+
|
|
129
|
+
## weekStartsOn
|
|
130
|
+
|
|
131
|
+
`weekStartsOn` (`0` Sunday to `6`) sets the first day of the week. Besides the
|
|
132
|
+
month grid, the week view starts its days on it, and a week badge's key is that
|
|
133
|
+
first day: with `weekStartsOn={1}`, the Monday.
|
|
134
|
+
|
|
135
|
+
This is a change: the week view used to start on the locale's first day whatever
|
|
136
|
+
`weekStartsOn` said. An app that set a `weekStartsOn` unlike its locale sees its
|
|
137
|
+
week view shift.
|
|
138
|
+
|
|
139
|
+
## Translations
|
|
140
|
+
|
|
141
|
+
| Key | English | Used for |
|
|
142
|
+
| --- | --- | --- |
|
|
143
|
+
| `material.event-calendar.cancelled` | Cancelled | a cancelled event's accessible name |
|
|
144
|
+
| `material.event-chip.cancelled` | Cancelled | the hidden word after a cancelled `EventChip`'s title, and in the preview and the agenda row |
|
|
145
|
+
| `material.event-calendar.drag` | Drag event | the description of a calendar whose events can be dragged |
|
|
146
|
+
| `material.event-calendar.week-badge` | Week: {label} | a week badge, read in the name of the week's first day |
|
|
147
|
+
| `material.event-calendar.title` | Title | the placeholder of the preview's title field |
|
|
148
|
+
| `material.event-calendar.label` | Event calendar | the calendar's accessible name |
|
|
149
|
+
|
|
150
|
+
English and French ship. Override them through the `I18nProvider`
|
|
151
|
+
([i18n guide](i18n.md)).
|
|
152
|
+
|
|
153
|
+
## Exports
|
|
154
|
+
|
|
155
|
+
From `@nxgt/material/components`:
|
|
156
|
+
|
|
157
|
+
- `EventCalendarSpan`: the shape of one entry of `spans`.
|
|
158
|
+
- `EventCalendarBadges`: `Record<string, { label: string; tone?: Tone }>`, the
|
|
159
|
+
type of `dayBadges` and `weekBadges`.
|
|
160
|
+
- `EventCalendarDayMarksProps`: the props `spans`, `dayBadges` and `weekBadges`,
|
|
161
|
+
for a wrapper that forwards them.
|
|
162
|
+
- `CalendarEventStatus`: `'scheduled' | 'cancelled'`, the type of an event's
|
|
163
|
+
`status`.
|
package/docs/guides/forms.md
CHANGED
|
@@ -490,6 +490,7 @@ or fails validation.
|
|
|
490
490
|
| --- | --- | --- |
|
|
491
491
|
| `TextField` · `TextFormField` | `string`. With `type="number"`: `number \| null` | `''` |
|
|
492
492
|
| `TextareaField` · `TextareaFormField` | `string` | `''` |
|
|
493
|
+
| `MarkdownTextareaField` · `MarkdownTextareaFormField` | `string` (Markdown source) | `''` |
|
|
493
494
|
| `EmailField` · `EmailFormField` | `string` (as typed; the field shows its own "Enter a valid email" hint on blur) | `''` |
|
|
494
495
|
| `PasswordField` · `PasswordFormField` | `string` | `''` |
|
|
495
496
|
| `NumberField` · `NumberFormField` | `number \| null`. `null` when cleared; clamped to `min` / `max` on blur | `null` |
|
|
@@ -644,7 +645,8 @@ export function SupplierForm({ onSave }: { onSave: (values: Values) => void }) {
|
|
|
644
645
|
a `reset()` or `setValue()`), so `defaultValues` is only the fallback while
|
|
645
646
|
the form value is empty (`undefined`, `null` or `{}`). Its fields fill the row, and `hideLabel` keeps
|
|
646
647
|
`label` as the group's accessible name without drawing it, under a
|
|
647
|
-
`FormSection` that already has the title.
|
|
648
|
+
`FormSection` that already has the title. `control` and `name` are typed from your
|
|
649
|
+
form (see [Attributes](../components/form-fields.md#attributes)).
|
|
648
650
|
|
|
649
651
|
### Rows a user adds and removes
|
|
650
652
|
|
package/docs/guides/i18n.md
CHANGED
|
@@ -227,6 +227,8 @@ The package's own strings live under one top-level namespace per language,
|
|
|
227
227
|
either side are kept, and where both define the same leaf, **your value
|
|
228
228
|
wins**.
|
|
229
229
|
|
|
230
|
+
`EventCalendar`'s strings (`material.event-calendar.*`) and `EventChip`'s hidden "Cancelled" (`material.event-chip.cancelled`) are listed in the [EventCalendar guide](event-calendar.md#translations).
|
|
231
|
+
|
|
230
232
|
The copy components render on their own comes from this namespace: empty
|
|
231
233
|
states, dialogs, pickers, the rich-text editor's toolbar, menus, node dialogs,
|
|
232
234
|
slash menu, font names and table-caption alignment
|
|
@@ -245,7 +247,7 @@ until the bar is drawn), the `ThreadLayout` stepper's `nav` name `material.navig
|
|
|
245
247
|
single value's input, when nothing names the slider, `material.slider.value`
|
|
246
248
|
("Value", "Valeur"). `CopyButton` reads `material.copy.copy`, `copied` and `failed` ("Copy", "Copied",
|
|
247
249
|
"Copy failed"). An overflowing `TabsList`'s scroll buttons read
|
|
248
|
-
`material.tabs.scroll-left` / `scroll-right` ("Scroll tabs left" / "Scroll tabs right"). The other icon-only buttons are named from it too: the
|
|
250
|
+
`material.tabs.scroll-left` / `scroll-right` ("Scroll tabs left" / "Scroll tabs right"). `QuestionCard`'s rich content reads `material.quiz.rich.*` (the hint button and names "Show hint 1 of 3" / "Hint 1 of 3", "About each answer", "References", "Examples" with its Input / Expected headers). The other icon-only buttons are named from it too: the
|
|
249
251
|
`Sidebar` toggle (`material.sidebar.toggle`), the data table's row expander
|
|
250
252
|
(`material.table.labels.toggle-row`) and its header, a hidden "Expand" / "Déplier"
|
|
251
253
|
(`material.table.labels.expand`), its loading status, "Loading data…" /
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/docs/roadmap.md
CHANGED
|
@@ -389,6 +389,18 @@ version they shipped in, and planned entries may name the major they target.
|
|
|
389
389
|
|
|
390
390
|
## Shipped
|
|
391
391
|
|
|
392
|
+
- **EventCalendar background spans, badges, tones and cancelled events** —
|
|
393
|
+
`EventCalendar` shows what the days themselves are: `spans` draw closures,
|
|
394
|
+
half days and other labelled, toned stretches behind the events in every
|
|
395
|
+
view (a hatched band in the time views, a shaded cell or half cell in the
|
|
396
|
+
month, a non-clickable row in the agenda and phone list), `dayBadges` and
|
|
397
|
+
`weekBadges` put a short toned label on a day or a week, each event takes a
|
|
398
|
+
`tone` and several `badges` (a status, a priority, a progress count), and a
|
|
399
|
+
`status: 'cancelled'` event is struck through, muted and
|
|
400
|
+
announced as cancelled in words, and `readOnlyEvents` makes every event
|
|
401
|
+
undraggable and unresizable while it stays clickable and in full colour
|
|
402
|
+
([guide](guides/event-calendar.md), [#2372](https://github.com/softistx/nxgt-material/issues/2372)).
|
|
403
|
+
|
|
392
404
|
- **Cells driven by their value** — in `DataTable`, `ServerTable` and
|
|
393
405
|
`DataGrid`, `meta.cellClassName(value, row)` computes a class per cell from
|
|
394
406
|
its value; `meta.tone(value, row)` colours a cell as coloured text after a
|