@nxgt/material 3.34.0 → 3.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/README.md +4 -4
- package/dist/{async-slice-C9q_eMJD.js → async-slice-BNI_F0jz.js} +2 -2
- package/dist/{button-CoAu_GuF.js → button-EMWOFSDB.js} +1 -1
- package/dist/charts.js +3 -3
- package/dist/{chip-Ch21Qbnj.js → chip-B6m3yagf.js} +1 -1
- package/dist/{code-editor-CEdEmolU.js → code-editor-BdiufnpC.js} +4 -4
- package/dist/code-editor.js +4 -4
- package/dist/{code-language-items-BNMM7YlZ.js → code-language-items-v5Rnq425.js} +2 -2
- package/dist/{components-BM9WHviO.js → components-C9Zz2dir.js} +18597 -15903
- package/dist/components.js +26 -26
- package/dist/{dialog-C_U4twIJ.js → dialog-Cdv9IXVo.js} +1 -1
- package/dist/dnd.js +1 -1
- package/dist/{dropdown-menu-DSgkmogX.js → dropdown-menu-CpSnRXd-.js} +1 -1
- package/dist/editor.js +7 -7
- package/dist/{empty-state-CJZ0sVE8.js → empty-state-DMr94lsF.js} +1 -1
- package/dist/hooks.js +2 -2
- package/dist/{i18n-provider-CLq-1Qgc.js → i18n-provider-A4mI4vc1.js} +1 -1
- package/dist/i18n.js +5 -5
- package/dist/{icon-badge-Dssl4rfn.js → icon-badge-BFScQnp9.js} +1 -1
- package/dist/lib/components/ui/dates/picker-trigger/typed-box-compact.d.ts +12 -0
- package/dist/lib/components/ui/dates/picker-trigger/typed-box.d.ts +6 -2
- package/dist/lib/components/ui/dates/picker-trigger/typed-trigger.d.ts +3 -2
- package/dist/lib/components/ui/dates/time-field/index.d.ts +1 -0
- package/dist/lib/components/ui/dates/time-field/partials/time-field-body.d.ts +2 -1
- package/dist/lib/components/ui/dates/time-field/partials/time-field-entry.d.ts +1 -0
- package/dist/lib/components/ui/dates/time-field/partials/time-field-typed.d.ts +2 -2
- package/dist/lib/components/ui/dates/time-field/time-field.d.ts +6 -1
- package/dist/lib/components/ui/dates/time-field/time-string-field.d.ts +14 -0
- package/dist/lib/components/ui/fields/attributes/attribute-dialog.d.ts +3 -1
- package/dist/lib/components/ui/fields/attributes/attribute-form/attribute-form.d.ts +3 -1
- package/dist/lib/components/ui/fields/attributes/attribute-form/attribute-text-form-fields.d.ts +6 -0
- package/dist/lib/components/ui/fields/attributes/attribute-form/consts.d.ts +7 -6
- package/dist/lib/components/ui/fields/attributes/attribute-form/use-attribute-form.d.ts +44 -0
- package/dist/lib/components/ui/fields/attributes/attributes-editor.d.ts +22 -0
- package/dist/lib/components/ui/fields/attributes/attributes-field/attributes-dialog-field.d.ts +14 -0
- package/dist/lib/components/ui/fields/attributes/attributes-field/attributes-field.d.ts +14 -12
- package/dist/lib/components/ui/fields/attributes/attributes-field/attributes-inline-field.d.ts +8 -0
- package/dist/lib/components/ui/fields/attributes/attributes-field/use-dialog-field.d.ts +15 -0
- package/dist/lib/components/ui/fields/attributes/attributes-form-field/attributes-form-field.d.ts +7 -1
- package/dist/lib/components/ui/fields/attributes/attributes-summary.d.ts +22 -0
- package/dist/lib/components/ui/fields/attributes/index.d.ts +3 -0
- package/dist/lib/components/ui/fields/attributes/logic/names.d.ts +13 -0
- package/dist/lib/components/ui/fields/attributes/logic/order.d.ts +15 -0
- package/dist/lib/components/ui/fields/attributes/logic/reorder-keys.d.ts +9 -0
- package/dist/lib/components/ui/fields/attributes/logic/value.d.ts +42 -0
- package/dist/lib/components/ui/fields/attributes/models/attribute.d.ts +5 -1
- package/dist/lib/components/ui/fields/attributes/partials/attribute-row.d.ts +18 -0
- package/dist/lib/components/ui/fields/attributes/partials/default-choices.d.ts +5 -0
- package/dist/lib/components/ui/fields/attributes/partials/default-field.d.ts +12 -0
- package/dist/lib/components/ui/fields/attributes/partials/default-inputs.d.ts +16 -0
- package/dist/lib/components/ui/fields/attributes/partials/editor-notes.d.ts +11 -0
- package/dist/lib/components/ui/fields/attributes/partials/labeled.d.ts +10 -0
- package/dist/lib/components/ui/fields/attributes/partials/name-field.d.ts +15 -0
- package/dist/lib/components/ui/fields/attributes/partials/options-field.d.ts +11 -0
- package/dist/lib/components/ui/fields/attributes/partials/phone-sheet.d.ts +12 -0
- package/dist/lib/components/ui/fields/attributes/partials/reorder-handle.d.ts +19 -0
- package/dist/lib/components/ui/fields/attributes/partials/row-fields.d.ts +16 -0
- package/dist/lib/components/ui/fields/attributes/partials/row-footer.d.ts +12 -0
- package/dist/lib/components/ui/fields/attributes/partials/row-head.d.ts +12 -0
- package/dist/lib/components/ui/fields/attributes/partials/row-panel.d.ts +15 -0
- package/dist/lib/components/ui/fields/attributes/partials/rows-dnd.d.ts +11 -0
- package/dist/lib/components/ui/fields/attributes/partials/simple-fields.d.ts +20 -0
- package/dist/lib/components/ui/fields/attributes/partials/summary-item.d.ts +9 -0
- package/dist/lib/components/ui/fields/attributes/partials/type-icon.d.ts +13 -0
- package/dist/lib/components/ui/fields/attributes/partials/value-text.d.ts +12 -0
- package/dist/lib/components/ui/fields/attributes/schema.d.ts +32 -0
- package/dist/lib/components/ui/fields/attributes/use-announce.d.ts +14 -0
- package/dist/lib/components/ui/fields/attributes/use-attribute-text.d.ts +13 -0
- package/dist/lib/components/ui/fields/attributes/use-attributes-editor.d.ts +34 -0
- package/dist/lib/components/ui/fields/attributes/use-reorder.d.ts +26 -0
- package/dist/lib/components/ui/fields/contacts/contacts-dialog-form-field.d.ts +9 -0
- package/dist/lib/components/ui/fields/contacts/contacts-editor.d.ts +29 -0
- package/dist/lib/components/ui/fields/contacts/contacts-form-field.d.ts +14 -1
- package/dist/lib/components/ui/fields/contacts/contacts-summary.d.ts +23 -0
- package/dist/lib/components/ui/fields/contacts/index.d.ts +3 -0
- package/dist/lib/components/ui/fields/contacts/logic/check-contact.d.ts +11 -0
- package/dist/lib/components/ui/fields/contacts/logic/contact-action.d.ts +11 -0
- package/dist/lib/components/ui/fields/contacts/logic/duplicates.d.ts +17 -0
- package/dist/lib/components/ui/fields/contacts/logic/issues.d.ts +7 -0
- package/dist/lib/components/ui/fields/contacts/logic/primary.d.ts +33 -0
- package/dist/lib/components/ui/fields/contacts/models.d.ts +7 -0
- package/dist/lib/components/ui/fields/contacts/partials/contact-fields.d.ts +29 -0
- package/dist/lib/components/ui/fields/contacts/partials/contact-links.d.ts +15 -0
- package/dist/lib/components/ui/fields/contacts/partials/contact-row.d.ts +14 -0
- package/dist/lib/components/ui/fields/contacts/partials/phone-sheet.d.ts +14 -0
- package/dist/lib/components/ui/fields/contacts/partials/quick-add.d.ts +12 -0
- package/dist/lib/components/ui/fields/contacts/partials/row-actions.d.ts +16 -0
- package/dist/lib/components/ui/fields/contacts/partials/summary-item.d.ts +11 -0
- package/dist/lib/components/ui/fields/contacts/partials/summary-row.d.ts +14 -0
- package/dist/lib/components/ui/fields/contacts/partials/type-group.d.ts +15 -0
- package/dist/lib/components/ui/fields/contacts/partials/type-icon.d.ts +12 -0
- package/dist/lib/components/ui/fields/contacts/partials/value-field.d.ts +25 -0
- package/dist/lib/components/ui/fields/contacts/placeholders.d.ts +2 -0
- package/dist/lib/components/ui/fields/contacts/schema.d.ts +18 -0
- package/dist/lib/components/ui/fields/contacts/use-contact-text.d.ts +9 -0
- package/dist/lib/components/ui/fields/contacts/use-contacts-editor.d.ts +33 -0
- package/dist/lib/components/ui/fields/inline-editor/action-tone.d.ts +2 -0
- package/dist/lib/components/ui/fields/inline-editor/editor-header.d.ts +19 -0
- package/dist/lib/components/ui/fields/inline-editor/editor-sheet.d.ts +20 -0
- package/dist/lib/components/ui/fields/inline-editor/empty-summary.d.ts +19 -0
- package/dist/lib/components/ui/fields/inline-editor/use-focus-request.d.ts +13 -0
- package/dist/lib/components/ui/fields/inline-editor/use-row-ids.d.ts +13 -0
- package/dist/lib/components/ui/fields/opening-hours/consts.d.ts +17 -3
- package/dist/lib/components/ui/fields/opening-hours/index.d.ts +2 -0
- package/dist/lib/components/ui/fields/opening-hours/logic/edit-days.d.ts +9 -0
- package/dist/lib/components/ui/fields/opening-hours/logic/first-day.d.ts +14 -0
- package/dist/lib/components/ui/fields/opening-hours/logic/format.d.ts +6 -0
- package/dist/lib/components/ui/fields/opening-hours/logic/group.d.ts +13 -0
- package/dist/lib/components/ui/fields/opening-hours/logic/range-errors.d.ts +20 -0
- package/dist/lib/components/ui/fields/opening-hours/logic/status.d.ts +19 -0
- package/dist/lib/components/ui/fields/opening-hours/logic/week.d.ts +32 -0
- package/dist/lib/components/ui/fields/opening-hours/logic/zoned.d.ts +10 -0
- package/dist/lib/components/ui/fields/opening-hours/opening-hours-dialog-form-field.d.ts +10 -0
- package/dist/lib/components/ui/fields/opening-hours/opening-hours-editor.d.ts +26 -0
- package/dist/lib/components/ui/fields/opening-hours/opening-hours-form-field.d.ts +17 -1
- package/dist/lib/components/ui/fields/opening-hours/opening-hours-summary.d.ts +32 -0
- package/dist/lib/components/ui/fields/opening-hours/partials/copy-chips.d.ts +12 -0
- package/dist/lib/components/ui/fields/opening-hours/partials/copy-menu.d.ts +10 -0
- package/dist/lib/components/ui/fields/opening-hours/partials/day-controls.d.ts +14 -0
- package/dist/lib/components/ui/fields/opening-hours/partials/day-row.d.ts +15 -0
- package/dist/lib/components/ui/fields/opening-hours/partials/day-summary-row.d.ts +15 -0
- package/dist/lib/components/ui/fields/opening-hours/partials/group-row.d.ts +13 -0
- package/dist/lib/components/ui/fields/opening-hours/partials/phone-sheet.d.ts +18 -0
- package/dist/lib/components/ui/fields/opening-hours/partials/range-field-props.d.ts +28 -0
- package/dist/lib/components/ui/fields/opening-hours/partials/range-row.d.ts +24 -0
- package/dist/lib/components/ui/fields/opening-hours/partials/status-pill.d.ts +13 -0
- package/dist/lib/components/ui/fields/opening-hours/use-first-day.d.ts +8 -0
- package/dist/lib/components/ui/fields/opening-hours/use-hours-editor.d.ts +26 -0
- package/dist/lib/components/ui/fields/opening-hours/use-hours-text.d.ts +8 -0
- package/dist/lib/components/ui/fields/opening-hours/use-now.d.ts +6 -0
- package/dist/lib/i18n/resources/en/en.d.ts +75 -0
- package/dist/lib/i18n/resources/en/material.json.d.ts +86 -11
- package/dist/lib/i18n/resources/fr/fr.d.ts +75 -0
- package/dist/lib/i18n/resources/fr/material.json.d.ts +86 -11
- package/dist/lib/i18n/resources/resources.d.ts +150 -0
- package/dist/lib/i18n/translator.d.ts +1 -1
- package/dist/lib.js +1 -1
- package/dist/main.js +43 -43
- package/dist/map.js +4 -4
- package/dist/material.css +1 -1
- package/dist/pdf-reader.js +1 -1
- package/dist/print.js +2 -2
- package/dist/{public-CX7FN97X.js → public-CYdnlHqA.js} +6 -6
- package/dist/{rich-code-block-cc1NZtbD.js → rich-code-block-Bwhfhu7J.js} +4 -4
- package/dist/{sortable-provider-D43VdFMP.js → sortable-provider-CY_YD3Ee.js} +1 -1
- package/dist/{spinner-wKfFAVU9.js → spinner-CKlDba5I.js} +1 -1
- package/dist/{stored-language-D73ujVW4.js → stored-language-CfQGHLq9.js} +176 -22
- package/dist/theme-picker.js +2 -2
- package/dist/{theme-toggle-Drab-hke.js → theme-toggle--Zcf5AM8.js} +1 -1
- package/dist/{toolbar-items-D7R9YtYx.js → toolbar-items-B1W-uVHS.js} +2 -2
- package/dist/{translate-material-CdpLjkcD.js → translate-material-xDh10aWZ.js} +2 -2
- package/dist/{translator-BE-yEb0p.js → translator-BbOZntEF.js} +1 -1
- package/dist/{use-chat-composer-CpKh8564.js → use-chat-composer-CoyqPKbt.js} +12 -12
- package/dist/{use-heading-spy-DgWhvYfh.js → use-heading-spy-DXYCeegh.js} +1 -1
- package/dist/{use-material-translation-Da9Ponn5.js → use-material-translation-BK2YYCY3.js} +2 -2
- package/docs/components/date-and-time.md +9 -1
- package/docs/components/form-fields.md +298 -67
- package/docs/guides/forms.md +18 -12
- package/docs/guides/i18n.md +1 -1
- package/docs/guides/ssr.md +1 -0
- package/docs/previews/form-fields-contacts.webp +0 -0
- package/docs/previews/form-fields-openinghours.webp +0 -0
- package/package.json +1 -1
|
@@ -32,10 +32,10 @@ for the value type each one writes. Everything is imported from
|
|
|
32
32
|
| <img src="../previews/form-fields-ratingfield.webp" width="96" alt=""> | [RatingField](#ratingfield) | Star rating |
|
|
33
33
|
| <img src="../previews/form-fields-slider.webp" width="96" alt=""> | [Slider](#slider) | Single or range slider with a readout |
|
|
34
34
|
| <img src="../previews/form-fields-switch.webp" width="96" alt=""> | [Switch](#switch) | Toggle switch, optionally in a labelled row |
|
|
35
|
-
| <img src="../previews/form-fields-attributes.webp" width="96" alt=""> | [Attributes](#attributes) |
|
|
36
|
-
| <img src="../previews/form-fields-contacts.webp" width="96" alt=""> | [Contacts](#contacts) | Typed
|
|
35
|
+
| <img src="../previews/form-fields-attributes.webp" width="96" alt=""> | [Attributes](#attributes) | Typed custom attributes edited in place, and a read-only summary |
|
|
36
|
+
| <img src="../previews/form-fields-contacts.webp" width="96" alt=""> | [Contacts](#contacts) | Typed contacts edited in place, and a read-only summary |
|
|
37
37
|
| <img src="../previews/form-fields-postaladdress.webp" width="96" alt=""> | [PostalAddress](#postaladdress) | Address card with an edit dialog |
|
|
38
|
-
| <img src="../previews/form-fields-openinghours.webp" width="96" alt=""> | [OpeningHours](#openinghours) |
|
|
38
|
+
| <img src="../previews/form-fields-openinghours.webp" width="96" alt=""> | [OpeningHours](#openinghours) | Opening hours edited in place, and an open-now summary |
|
|
39
39
|
|
|
40
40
|
## Field
|
|
41
41
|
|
|
@@ -180,8 +180,8 @@ row.
|
|
|
180
180
|
|
|
181
181
|

|
|
182
182
|
|
|
183
|
-
Rows of fields a user adds, removes and, optionally, reorders — a
|
|
184
|
-
|
|
183
|
+
Rows of fields a user adds, removes and, optionally, reorders — a plan's
|
|
184
|
+
periods, a list of contacts. `RepeatableGroupFormField` wires it to
|
|
185
185
|
react-hook-form's `useFieldArray`; each row's fields are your own form
|
|
186
186
|
wrappers, rendered by `renderRow`.
|
|
187
187
|
|
|
@@ -197,7 +197,7 @@ import {
|
|
|
197
197
|
type Period = { name: string; start?: Date; end?: Date };
|
|
198
198
|
type Values = { periods: Period[] };
|
|
199
199
|
|
|
200
|
-
export function
|
|
200
|
+
export function PlanForm({ onSave }: { onSave: (values: Values) => void }) {
|
|
201
201
|
const { control, handleSubmit } = useForm<Values>({
|
|
202
202
|
defaultValues: { periods: [{ name: 'Term 1' }] },
|
|
203
203
|
});
|
|
@@ -1312,41 +1312,150 @@ libraries; neither name is deprecated, and both keep working.
|
|
|
1312
1312
|
|
|
1313
1313
|

|
|
1314
1314
|
|
|
1315
|
-
|
|
1315
|
+
Typed custom attributes (string, number, boolean, date, select, rich text…),
|
|
1316
|
+
edited in place or shown read-only. One value model throughout: `Attribute[]`,
|
|
1317
|
+
`{ name, label, type, options?, unit?, defaultValue?, description?, priority? }`.
|
|
1318
|
+
`type` is an `AttributeType`: `STRING`, `TEXT`, `NUMBER`, `BOOLEAN`, `COLOR`,
|
|
1319
|
+
`DATE`, `DATETIME`, `TIME`, `SELECT`, `MULTI_SELECT` or `RICH_TEXT`. The
|
|
1320
|
+
`BOOLEAN` default is a `boolean`. `priority` is the place in the list, `1`
|
|
1321
|
+
first.
|
|
1322
|
+
|
|
1323
|
+
Three parts, each for one job:
|
|
1324
|
+
|
|
1325
|
+
| Part | Use it for |
|
|
1326
|
+
| --- | --- |
|
|
1327
|
+
| `AttributesField` · `AttributesFormField` | the field, and its React Hook Form wrapper. Inline by default; `editing="dialog"` keeps the list and dialog |
|
|
1328
|
+
| `AttributesEditor` | the same inline editor, controlled (`value`, `onValueChange`), outside a form |
|
|
1329
|
+
| `AttributesSummary` | the read-only display: type icons, labels and default values |
|
|
1330
|
+
|
|
1331
|
+
`AttributesList` (the list with `onAdd` / `onEdit`), `AttributeDialog` and
|
|
1332
|
+
`AttributeForm` stay: they are what `editing="dialog"` renders. `AttributeValuesForm`,
|
|
1333
|
+
`AttributeValuesFormFieldSet` and `AttributeValueFormField` fill the values of
|
|
1334
|
+
a set of attributes, and now draw a `BOOLEAN` attribute as a switch.
|
|
1316
1335
|
|
|
1317
1336
|
```tsx
|
|
1318
|
-
import {
|
|
1337
|
+
import { AttributesFormField } from '@nxgt/material/components';
|
|
1338
|
+
|
|
1339
|
+
<AttributesFormField control={control} name="attributes" label="Attributes" />
|
|
1340
|
+
```
|
|
1341
|
+
|
|
1342
|
+
**The inline editor** is one row per attribute: a grip, the type's icon, the
|
|
1343
|
+
label with `name · Type` under it, the unit as a badge and the default value
|
|
1344
|
+
(a Yes or No, a colour swatch, a number with its unit). A row opens to its
|
|
1345
|
+
fields: label, name, type, unit (for a number), options (for a select), the
|
|
1346
|
+
default value edited the way its type is, and a description; Delete removes
|
|
1347
|
+
it and Done closes it. Nothing is toasted: an edit is in the value at once.
|
|
1348
|
+
Changing the type resets the default value, and the options unless both types
|
|
1349
|
+
take options. "Add attribute" appends a row, opens it and focuses its label.
|
|
1350
|
+
|
|
1351
|
+
Outside a form, control it yourself; `editing="dialog"` is the way back to
|
|
1352
|
+
the earlier list and dialog:
|
|
1353
|
+
|
|
1354
|
+
```tsx
|
|
1355
|
+
import { useState } from 'react';
|
|
1356
|
+
import { AttributesEditor, AttributesFormField, type Attribute } from '@nxgt/material/components';
|
|
1357
|
+
|
|
1358
|
+
const [attributes, setAttributes] = useState<Attribute[]>([]);
|
|
1359
|
+
<AttributesEditor label="Attributes" value={attributes} onValueChange={setAttributes} />
|
|
1360
|
+
|
|
1361
|
+
<AttributesFormField control={control} name="attributes" label="Attributes" editing="dialog" />
|
|
1362
|
+
```
|
|
1363
|
+
|
|
1364
|
+
*Reorder.* Drag the grip, or focus it and press Space (or Enter) to lift the
|
|
1365
|
+
row, the arrow keys to move it, Space (or Enter) to drop it and Escape to put
|
|
1366
|
+
it back. Each step is said in a live region ("Active moved to position 2 of
|
|
1367
|
+
6"). A drop rewrites the list in its new order and sets every `priority` to
|
|
1368
|
+
its place (`1`, `2`, `3`…); the editor shows the attributes by `priority`
|
|
1369
|
+
(lowest first, none after the others), so a list without priorities keeps its
|
|
1370
|
+
order until the first move.
|
|
1371
|
+
|
|
1372
|
+
*Names.* A name must be unique and not empty (spaces around it are ignored;
|
|
1373
|
+
there is no minimum length any more). A name another attribute has is a
|
|
1374
|
+
`role="alert"` under the field at once ("Another attribute already has this
|
|
1375
|
+
name."); an empty one once the field was left. The header counts the problems
|
|
1376
|
+
("2 issues to fix"), and a row with a bad name shows a warning mark.
|
|
1319
1377
|
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
];
|
|
1378
|
+
*Phone.* Below 640px each attribute is a row with its icon, title and a
|
|
1379
|
+
chevron; tapping it opens a bottom sheet with the same fields (and Delete and
|
|
1380
|
+
Done). The grip stays, so a row can be moved on a phone too.
|
|
1324
1381
|
|
|
1325
|
-
|
|
1382
|
+
**The summary** takes the same `data`:
|
|
1383
|
+
|
|
1384
|
+
```tsx
|
|
1385
|
+
import { AttributesSummary } from '@nxgt/material/components';
|
|
1386
|
+
|
|
1387
|
+
<AttributesSummary data={attributes} label="Item" />
|
|
1326
1388
|
```
|
|
1327
1389
|
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
`
|
|
1333
|
-
`
|
|
1334
|
-
|
|
1390
|
+
It is a description list in `priority` order (list order without one), each
|
|
1391
|
+
attribute with its type icon, its label and its default value, a dash when it
|
|
1392
|
+
has none. It is a new component, not a read-only mode of `AttributesList`, so
|
|
1393
|
+
the list keeps its callbacks. With nothing to list it shows a quiet "No attributes"
|
|
1394
|
+
(`empty` replaces the text, `empty={null}` renders nothing). A `DATETIME` or
|
|
1395
|
+
`TIME` default stored as an instant is written in UTC on the server and in the
|
|
1396
|
+
first client render, and in the viewer's time zone and locale once the page
|
|
1397
|
+
has mounted, so hydration matches; a bare `yyyy-MM-dd` or `HH:mm` reads the
|
|
1398
|
+
same everywhere.
|
|
1335
1399
|
|
|
1336
1400
|
| Prop | Type | Default | Effect |
|
|
1337
1401
|
| --- | --- | --- | --- |
|
|
1402
|
+
| `data` | `Attribute[] \| null` | `[]` | the attributes |
|
|
1403
|
+
| `label` | `string` | "Attributes" | names the region |
|
|
1404
|
+
| `empty` | `ReactNode` | "No attributes" | shown when there is nothing to list; `null` renders nothing |
|
|
1405
|
+
|
|
1406
|
+
| Prop (field) | Type | Default | Effect |
|
|
1407
|
+
| --- | --- | --- | --- |
|
|
1338
1408
|
| `attributes` | `Attribute[]` | `[]` | the definitions; a new value (a form `reset`) replaces the list |
|
|
1339
|
-
| `onChange` | `(values: Attribute[]) => void` | — | after each
|
|
1340
|
-
| `
|
|
1409
|
+
| `onChange` | `(values: Attribute[]) => void` | — | after each edit |
|
|
1410
|
+
| `editing` | `'inline' \| 'dialog'` | `'inline'` | in place, or the list and dialog |
|
|
1411
|
+
| `readonly` | `boolean` | — | the list without add and edit (also with `editing="inline"`) |
|
|
1412
|
+
| `disabled` | `boolean` | — | disables the controls |
|
|
1341
1413
|
| `label` | `ReactNode` | — | required; the section title |
|
|
1342
|
-
| `helperText` / `error` | `ReactNode` / `boolean` | — | text under the
|
|
1414
|
+
| `helperText` / `error` | `ReactNode` / `boolean` | — | text under the field; red when `error` (`AttributesFormField` sets both from the field's error, and shows your own `helperText` while there is none) |
|
|
1415
|
+
|
|
1416
|
+
| `AttributesEditor` prop | Type | Default | Effect |
|
|
1417
|
+
| --- | --- | --- | --- |
|
|
1418
|
+
| `value` | `Attribute[]` | `[]` | the attributes |
|
|
1419
|
+
| `onValueChange` | `(value: Attribute[]) => void` | — | after each edit |
|
|
1420
|
+
| `label` | `ReactNode` | "Attributes" | the heading |
|
|
1421
|
+
| `headingLevel` | `2 \| 3 \| 4 \| 5 \| 6` | `3` | the heading's level |
|
|
1422
|
+
| `disabled` | `boolean` | — | disables the controls |
|
|
1423
|
+
| `className` | `string` | — | on the section |
|
|
1424
|
+
|
|
1425
|
+
`AttributesSummary` also takes `className`. `AttributesFormField` takes
|
|
1426
|
+
`control` and `name` in place of `attributes` and `onChange`, an optional
|
|
1427
|
+
`value` that overrides the form's, and the props above; its `disabled` also
|
|
1428
|
+
follows `useForm({ disabled })`.
|
|
1429
|
+
|
|
1430
|
+
`AttributeDialog` and `AttributeForm` take `otherNames`: saving a
|
|
1431
|
+
name in it is an inline error on the name. `AttributeForm` builds its zod
|
|
1432
|
+
schema when it first renders, not when the module loads
|
|
1433
|
+
([CSP](../guides/csp.md#zod-and-unsafe-eval)); its `name` is only required to
|
|
1434
|
+
be non-empty (it was at least four characters).
|
|
1435
|
+
|
|
1436
|
+
`attributesListSchema()` is the zod schema of a whole list, with the editor's
|
|
1437
|
+
own rules (the same functions): a name that is set and that no other attribute
|
|
1438
|
+
has (`[1, 'name']`), and a default value that fits the type, a listed option, a
|
|
1439
|
+
boolean, a number or a date (`[1, 'defaultValue']`). Built on the first call
|
|
1440
|
+
([CSP](../guides/csp.md#zod-and-unsafe-eval)). Each issue's `message` is an
|
|
1441
|
+
English default, and its `params.code` (`'empty'`, `'duplicate'`, `'option'`,
|
|
1442
|
+
`'boolean'`, `'number'`, `'date'`; typed as `AttributeIssueCode`) lets a form show its own translated text.
|
|
1343
1443
|
|
|
1344
|
-
|
|
1345
|
-
|
|
1444
|
+
```ts
|
|
1445
|
+
import { attributesListSchema } from '@nxgt/material/components';
|
|
1446
|
+
import { z } from 'zod';
|
|
1447
|
+
|
|
1448
|
+
const schema = z.object({ attributes: attributesListSchema() });
|
|
1449
|
+
```
|
|
1450
|
+
|
|
1451
|
+
**Emits:** `Attribute[]`; the inline editor writes a date as `yyyy-MM-dd`, a
|
|
1452
|
+
date-time as an ISO instant and a time as `HH:mm`. `AttributeValuesFormFieldSet`
|
|
1453
|
+
writes an object keyed by attribute `name`; `AttributeValuesForm` reads a bare
|
|
1454
|
+
`yyyy-MM-dd` default as that local day.
|
|
1346
1455
|
**Form wrapper:** `AttributesFormField` — `control` and `name`, see
|
|
1347
1456
|
[forms](../guides/forms.md#the-pair), [what each field emits](../guides/forms.md#what-each-field-emits)
|
|
1348
1457
|
and [composite values](../guides/forms.md#composite-values).
|
|
1349
|
-
**Storybook:** `Form fields/Attributes` · **Vue:** `AttributesField`, `AttributesList`, `AttributeDialog`, `AttributeForm`, `AttributeValuesForm`, `AttributesFormField`, `AttributeValuesFormFieldSet`, `AttributeValueFormField`
|
|
1458
|
+
**Storybook:** `Form fields/Attributes`, `Form fields/Attributes/Editor`, `Form fields/Attributes/Editor reorder`, `Form fields/Attributes/Editor on a phone`, `Form fields/Attributes/Summary` · **Vue:** `AttributesField`, `AttributesList`, `AttributeDialog`, `AttributeForm`, `AttributeValuesForm`, `AttributesFormField`, `AttributeValuesFormFieldSet`, `AttributeValueFormField`
|
|
1350
1459
|
|
|
1351
1460
|
---
|
|
1352
1461
|
|
|
@@ -1354,35 +1463,105 @@ and [composite values](../guides/forms.md#composite-values).
|
|
|
1354
1463
|
|
|
1355
1464
|

|
|
1356
1465
|
|
|
1357
|
-
Typed
|
|
1466
|
+
Typed contacts (phone, mobile, fax, email, website), edited in place or shown
|
|
1467
|
+
read-only. One value model throughout: `Contact[]`, `{ type, value, label?,
|
|
1468
|
+
primary? }` with `type` a `ContactType`. `primary` is optional and additive:
|
|
1469
|
+
**one per type**, the contact to use first among those of its type. A value
|
|
1470
|
+
that marks several of a type is read as marking the first only.
|
|
1471
|
+
|
|
1472
|
+
Three parts, each for one job:
|
|
1473
|
+
|
|
1474
|
+
| Part | Use it for |
|
|
1475
|
+
| --- | --- |
|
|
1476
|
+
| `ContactsFormField` | the React Hook Form field. Inline by default; `editing="dialog"` keeps the list and dialog |
|
|
1477
|
+
| `ContactsEditor` | the same inline editor, controlled (`value`, `onValueChange`), outside a form |
|
|
1478
|
+
| `ContactsSummary` | the read-only display: icons, values as links, copy, the primary one marked |
|
|
1479
|
+
|
|
1480
|
+
`Contacts` (the list with `onAdd` / `onEdit`) and `ContactDialog` stay as they
|
|
1481
|
+
were: they are what `editing="dialog"` renders.
|
|
1358
1482
|
|
|
1359
1483
|
```tsx
|
|
1360
|
-
import {
|
|
1484
|
+
import { ContactsFormField } from '@nxgt/material/components';
|
|
1361
1485
|
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1486
|
+
<ContactsFormField control={control} name="contacts" label="Contacts" />
|
|
1487
|
+
```
|
|
1488
|
+
|
|
1489
|
+
**The inline editor** groups the contacts by type (phone, mobile, email,
|
|
1490
|
+
website, fax), the primary one first in its group. A row is the type icon, a
|
|
1491
|
+
type select, the value, an optional label and its actions: call (`tel:`),
|
|
1492
|
+
write (`mailto:`) or open the site (links, shown only when the value is
|
|
1493
|
+
well-formed), copy (announces "Copied"), a star that makes it the primary
|
|
1494
|
+
contact of its type, and remove. "Add Contact" chips add an empty contact of a
|
|
1495
|
+
type and focus its value. A wrong format (an email without a domain, a number
|
|
1496
|
+
that is not 7 to 15 digits, a website that is not `http(s)`) or a duplicate
|
|
1497
|
+
(the same type and value, ignoring case and number formatting) is a
|
|
1498
|
+
`role="alert"` under its value. A duplicate shows and counts at once; a wrong
|
|
1499
|
+
format shows and counts once its field was left. The header counts them
|
|
1500
|
+
("2 issues to fix").
|
|
1501
|
+
The editor checks the shape of a value, not that the line exists. Below 640px
|
|
1502
|
+
each contact is a summary row; tapping it opens a bottom sheet with the same
|
|
1503
|
+
fields and actions, and closing it with an empty value drops the contact.
|
|
1504
|
+
|
|
1505
|
+
`variant="cards"` is the Expressive variant: each contact is its own tinted
|
|
1506
|
+
card with a round tile for the primary one. The fields keep the library's
|
|
1507
|
+
field radius in both. `variant` is on `ContactsFormField`, `ContactsEditor`
|
|
1508
|
+
and `ContactsSummary`; it is the only prop that picks the look, so the two
|
|
1509
|
+
are one component, not two.
|
|
1510
|
+
|
|
1511
|
+
```tsx
|
|
1512
|
+
<ContactsFormField control={control} name="contacts" variant="cards" />
|
|
1513
|
+
```
|
|
1514
|
+
|
|
1515
|
+
**The summary** takes the same `data`:
|
|
1516
|
+
|
|
1517
|
+
```tsx
|
|
1518
|
+
import { ContactsSummary } from '@nxgt/material/components';
|
|
1366
1519
|
|
|
1367
|
-
<
|
|
1520
|
+
<ContactsSummary data={contacts} label="Acme headquarters" />
|
|
1368
1521
|
```
|
|
1369
1522
|
|
|
1370
|
-
|
|
1371
|
-
|
|
1523
|
+
It is a new component, not a read-only mode of `Contacts`, so the list keeps
|
|
1524
|
+
its callbacks and nothing changes for code that uses it. With nothing to list
|
|
1525
|
+
it shows a quiet "No contacts" (`empty` replaces the text, `empty={null}`
|
|
1526
|
+
renders nothing); it skips contacts without a value. A fax number offers copy
|
|
1527
|
+
only, and a value that fails its check is shown without a link.
|
|
1372
1528
|
|
|
1373
1529
|
| Prop | Type | Default | Effect |
|
|
1374
1530
|
| --- | --- | --- | --- |
|
|
1375
|
-
| `data` | `Contact[] \| null` | `[]` |
|
|
1376
|
-
| `
|
|
1377
|
-
| `
|
|
1378
|
-
| `
|
|
1531
|
+
| `data` | `Contact[] \| null` | `[]` | the contacts |
|
|
1532
|
+
| `label` | `string` | "Contacts" | names the region |
|
|
1533
|
+
| `variant` | `'list' \| 'cards'` | `'list'` | a bordered list, or tinted cards |
|
|
1534
|
+
| `empty` | `ReactNode` | "No contacts" | shown when there is nothing to list; `null` renders nothing |
|
|
1535
|
+
|
|
1536
|
+
`ContactsEditor` takes `value`, `onValueChange`, `label`, `headingLevel` (`3`
|
|
1537
|
+
by default; the type headings sit one below it), `variant` and `disabled`. A
|
|
1538
|
+
wrong format is said once the field was left, a duplicate at once. `ContactsFormField` takes `editing` (`'inline' \| 'dialog'`,
|
|
1539
|
+
default `'inline'`) and `variant` (inline only). `Contacts` takes `data`,
|
|
1540
|
+
`onAdd`, `onEdit` and `disabled`.
|
|
1541
|
+
|
|
1542
|
+
`contactsListSchema()` is the zod schema of a whole list, with the editor's
|
|
1543
|
+
own rules (the same functions): the value's format by type (an email, an
|
|
1544
|
+
`http(s)` address, 7 to 15 digits for a phone, mobile or fax) and no value
|
|
1545
|
+
repeated within a type, each issue on the entry's `value` (`[1, 'value']`). A
|
|
1546
|
+
blank value is not an issue, as in the editor. Each issue's `message` is an
|
|
1547
|
+
English default, and its `params.code` (`'email'`, `'phone'`, `'website'`,
|
|
1548
|
+
`'duplicate'`; typed as `ContactIssueCode`) lets a form show its own translated text.
|
|
1379
1549
|
|
|
1380
|
-
|
|
1381
|
-
|
|
1550
|
+
```ts
|
|
1551
|
+
import { contactsListSchema } from '@nxgt/material/components';
|
|
1552
|
+
import { z } from 'zod';
|
|
1553
|
+
|
|
1554
|
+
const schema = z.object({ contacts: contactsListSchema() });
|
|
1555
|
+
```
|
|
1556
|
+
|
|
1557
|
+
**Emits:** `ContactsFormField` stores `Contact[]` (the inline editor writes
|
|
1558
|
+
`primary: true` on one contact per type and leaves the flag off elsewhere), and
|
|
1559
|
+
shows the array's error (`errors.<name>.message` or `.root.message`) under the
|
|
1560
|
+
editor.
|
|
1382
1561
|
**Form wrapper:** `ContactsFormField` — `control` and `name`, see
|
|
1383
1562
|
[forms](../guides/forms.md#the-pair), [what each field emits](../guides/forms.md#what-each-field-emits)
|
|
1384
1563
|
and [composite values](../guides/forms.md#composite-values).
|
|
1385
|
-
**Storybook:** `Form fields/Contacts` · **Vue:** `Contacts`, `ContactDialog`, `ContactsFormField`
|
|
1564
|
+
**Storybook:** `Form fields/Contacts`, `Form fields/Contacts/Editor`, `Form fields/Contacts/Summary` · **Vue:** `Contacts`, `ContactDialog`, `ContactsFormField`
|
|
1386
1565
|
|
|
1387
1566
|
---
|
|
1388
1567
|
|
|
@@ -1426,45 +1605,97 @@ and [composite values](../guides/forms.md#composite-values).
|
|
|
1426
1605
|
|
|
1427
1606
|

|
|
1428
1607
|
|
|
1429
|
-
|
|
1608
|
+
A week of opening hours, edited in place or shown read-only. One value
|
|
1609
|
+
model throughout: `OpeningHour[]`, **one entry per range** (`{ dayOfWeek,
|
|
1610
|
+
openTime?, closeTime?, isClosed? }`, `dayOfWeek` `0` Sunday, times as
|
|
1611
|
+
`'HH:mm'`), so a day with a lunch break is two entries.
|
|
1612
|
+
|
|
1613
|
+
Three parts, each for one job:
|
|
1614
|
+
|
|
1615
|
+
| Part | Use it for |
|
|
1616
|
+
| --- | --- |
|
|
1617
|
+
| `OpeningHoursFormField` | the React Hook Form field. Inline by default; `editing="dialog"` keeps the list and dialog |
|
|
1618
|
+
| `OpeningHoursEditor` | the same inline editor, controlled (`value`, `onValueChange`), outside a form |
|
|
1619
|
+
| `OpeningHoursSummary` | the read-only display: open now, and the grouped week |
|
|
1620
|
+
|
|
1621
|
+
`OpeningHours` (the list with `onAdd` / `onEdit`) and `OpeningHoursDialog`
|
|
1622
|
+
stay as they were: they are what `editing="dialog"` renders.
|
|
1430
1623
|
|
|
1431
1624
|
```tsx
|
|
1432
|
-
import {
|
|
1625
|
+
import { OpeningHoursFormField } from '@nxgt/material/components';
|
|
1626
|
+
|
|
1627
|
+
<OpeningHoursFormField control={control} name="hours" label="Opening hours" />
|
|
1628
|
+
```
|
|
1629
|
+
|
|
1630
|
+
**The inline editor** shows the seven days. Each has an open / closed switch
|
|
1631
|
+
and one or more ranges (add and remove), and a "Copy to" menu: Monday to
|
|
1632
|
+
Friday, the weekend, or all days, with a "Copied to Sat, Sun" note. A range
|
|
1633
|
+
that is wrong says so under itself (`role="alert"`): the close is not after the
|
|
1634
|
+
open, it overlaps another range of the day, or a time is missing; the header
|
|
1635
|
+
counts them ("2 issues to fix"). Times are `TimeStringField`s at `size="sm"`.
|
|
1636
|
+
`weekStartsOn` (`0` Sunday to `6`) defaults to the locale's first day: pass `1`
|
|
1637
|
+
for a Monday start under `en`. Below 640px each day is a summary row with a
|
|
1638
|
+
chevron; tapping it opens a bottom sheet with the same controls and day chips
|
|
1639
|
+
to copy to.
|
|
1433
1640
|
|
|
1434
|
-
|
|
1435
|
-
{ dayOfWeek: 1, openTime: '09:00', closeTime: '18:00' },
|
|
1436
|
-
{ dayOfWeek: 6, isClosed: true },
|
|
1437
|
-
];
|
|
1641
|
+
**The summary** takes the same `data`:
|
|
1438
1642
|
|
|
1439
|
-
|
|
1643
|
+
```tsx
|
|
1644
|
+
import { OpeningHoursSummary } from '@nxgt/material/components';
|
|
1645
|
+
|
|
1646
|
+
<OpeningHoursSummary data={hours} timeZone="America/Toronto" label="Opening hours" />
|
|
1440
1647
|
```
|
|
1441
1648
|
|
|
1442
|
-
|
|
1649
|
+
It prints a status pill, "Open · closes at 6:00 PM" or "Closed · opens Monday
|
|
1650
|
+
at 9:00 AM", then the week with identical consecutive days grouped ("Mon–Fri
|
|
1651
|
+
9:00 AM – 6:00 PM"), today's group tagged "Today" with the selected-row look.
|
|
1652
|
+
It is a new component, not a mode of `OpeningHours`, so the list keeps its
|
|
1653
|
+
callbacks and nothing changes for code that uses it. Without `now` the status
|
|
1654
|
+
and the "Today" mark appear once the page has mounted (a server render and its
|
|
1655
|
+
hydration then match); pass `now` to render them on the server or in a test.
|
|
1656
|
+
The week starts on the language's first day until the page has mounted, then
|
|
1657
|
+
on the locale's own (`weekStartsOn` sets it for both). With no hours, the
|
|
1658
|
+
summary shows a quiet "Hours not set" (`empty` replaces the text,
|
|
1659
|
+
`empty={null}` renders nothing).
|
|
1660
|
+
|
|
1661
|
+
| Prop | Type | Default | Effect |
|
|
1662
|
+
| --- | --- | --- | --- |
|
|
1663
|
+
| `data` | `OpeningHour[] \| null` | `[]` | the hours |
|
|
1664
|
+
| `now` | `Date` | the clock | the moment read as now |
|
|
1665
|
+
| `timeZone` | `string` | the viewer's | the IANA zone of the place, resolved with `Intl.DateTimeFormat` |
|
|
1666
|
+
| `locale` | `string` | the `I18nProvider` language, else `en` | day and time names |
|
|
1667
|
+
| `weekStartsOn` | `0`-`6` | the locale's | first day of the week |
|
|
1668
|
+
| `label` | `string` | "Opening Hours" | names the region |
|
|
1669
|
+
| `empty` | `ReactNode` | "Hours not set" | shown when no day is open; `null` renders nothing |
|
|
1670
|
+
|
|
1671
|
+
`OpeningHoursEditor` takes `value`, `onValueChange`, `label`, `headingLevel` (`3` by default), `weekStartsOn`,
|
|
1672
|
+
`locale` and `disabled`. `OpeningHoursFormField` takes `editing`
|
|
1673
|
+
(`'inline' \| 'dialog'`, default `'inline'`), `weekStartsOn` and `locale`
|
|
1674
|
+
(inline), and `showAddAllDays` (dialog, default `false`).
|
|
1675
|
+
|
|
1443
1676
|
`openingHoursSchema()` returns the zod schema of one `OpeningHour`
|
|
1444
|
-
(`dayOfWeek` coerced from a string), built on the first call.
|
|
1445
|
-
|
|
1677
|
+
(`dayOfWeek` coerced from a string), built on the first call. It is unchanged.
|
|
1678
|
+
`openingHoursListSchema()` is the schema of the whole week: the entries, plus
|
|
1679
|
+
the cross-field rules of the editor (close after open, both times set, no
|
|
1680
|
+
overlap on a day), each issue on its entry and field (`[2, 'closeTime']`). A
|
|
1681
|
+
blank time (`''`) is accepted by the entry schema and reported once, as
|
|
1682
|
+
`missing`. Each issue's `message` is an English default, and its `params.code`
|
|
1683
|
+
(`'close'`, `'overlap'`, `'missing'`) lets a form show the translated
|
|
1684
|
+
`material.opening-hours.errors.*` text the editor uses.
|
|
1685
|
+
Under a strict CSP, call `z.config({ jitless: true })` first: see
|
|
1446
1686
|
[CSP](../guides/csp.md#zod-and-unsafe-eval).
|
|
1447
1687
|
|
|
1448
1688
|
```ts
|
|
1449
|
-
import {
|
|
1689
|
+
import { openingHoursListSchema } from '@nxgt/material/components';
|
|
1450
1690
|
import { z } from 'zod';
|
|
1451
1691
|
|
|
1452
|
-
const schema = z.object({ hours:
|
|
1692
|
+
const schema = z.object({ hours: openingHoursListSchema() });
|
|
1453
1693
|
```
|
|
1454
1694
|
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
| `onAdd` | `() => void` | — | shows the add button |
|
|
1459
|
-
| `onAddAll` | `() => void` | — | shows "Add all days" |
|
|
1460
|
-
| `onEdit` | `(value: OpeningHour, index: number) => void` | — | shows an edit button per day |
|
|
1461
|
-
|
|
1462
|
-
`OpeningHoursFormField` takes `showAddAllDays` (default `false`) to offer the
|
|
1463
|
-
"Add all days" button.
|
|
1464
|
-
|
|
1465
|
-
**Emits:** `OpeningHoursFormField` stores `OpeningHour[]`, and shows the
|
|
1466
|
-
array's error (`errors.<name>.message` or `.root.message`) under the list.
|
|
1695
|
+
**Emits:** `OpeningHoursFormField` stores `OpeningHour[]`: the inline editor
|
|
1696
|
+
writes a closed day as one `{ dayOfWeek, isClosed: true }` entry, and shows the
|
|
1697
|
+
array's error (`errors.<name>.message` or `.root.message`) under the editor.
|
|
1467
1698
|
**Form wrapper:** `OpeningHoursFormField` — `control` and `name`, see
|
|
1468
1699
|
[forms](../guides/forms.md#the-pair), [what each field emits](../guides/forms.md#what-each-field-emits)
|
|
1469
1700
|
and [composite values](../guides/forms.md#composite-values).
|
|
1470
|
-
**Storybook:** `Form fields/OpeningHours` · **Vue:** `OpeningHours`, `OpeningHoursDialog`, `OpeningHoursFormField`
|
|
1701
|
+
**Storybook:** `Form fields/OpeningHours`, `Form fields/OpeningHours/Editor`, `Form fields/OpeningHours/Summary` · **Vue:** `OpeningHours`, `OpeningHoursDialog`, `OpeningHoursFormField`
|
package/docs/guides/forms.md
CHANGED
|
@@ -115,9 +115,9 @@ export function PlanForm() {
|
|
|
115
115
|
| The field | Touched when |
|
|
116
116
|
| --- | --- |
|
|
117
117
|
| one input (`TextFormField`, `EmailFormField`, `TextareaFormField`, `SwitchFormField`, `CheckboxFormField`, …) | its input loses focus, as a native `blur` |
|
|
118
|
-
| several controls (`PasswordFormField` and its eye button, `NumberFormField` and its steppers, `PhoneFormField`, `OtpFormField`, `SliderFormField` with two thumbs, `DurationFormField`, `RatingFormField`, `RadioGroupFormField`, `CheckboxGroupFormField`, `ChipGroupFormField`, `ChoiceCardGroupFormField`, `SortableListFormField`, `TransferListFormField`, `InlineEditFormField`, `LocalizedTextFormField`, `AttributeValuesFormFieldSet`, …) | focus leaves all of them; moving between them is not leaving |
|
|
118
|
+
| several controls (`OpeningHoursFormField`, inline by default: focus leaves the week editor and its phone sheet; `ContactsFormField`, inline by default: focus leaves the rows and the phone sheet; `AttributesFormField`, inline by default: focus leaves the rows, their open fields and the phone sheet; `PasswordFormField` and its eye button, `NumberFormField` and its steppers, `PhoneFormField`, `OtpFormField`, `SliderFormField` with two thumbs, `DurationFormField`, `RatingFormField`, `RadioGroupFormField`, `CheckboxGroupFormField`, `ChipGroupFormField`, `ChoiceCardGroupFormField`, `SortableListFormField`, `TransferListFormField`, `InlineEditFormField`, `LocalizedTextFormField`, `AttributeValuesFormFieldSet`, …) | focus leaves all of them; moving between them is not leaving |
|
|
119
119
|
| a picker (`SelectFormField`, `ComboboxFormField`, `AutocompleteFormField`, `TreeSelectFormField`, `CountryFormField`, `TimezoneFormField`, `ColorFormField`, `DateFormField`, `DateRangeFormField`, `TimeFormField`, …) | focus leaves the trigger **and** the popover it opened. Opening the popover, or picking in it, is not leaving; closing it hands focus back to the trigger, and the next Tab or click elsewhere is. A typed `DateFormField`, `DateRangeFormField` or `TimeFormField` is one trigger: moving between its segments (both ends of a range) and its buttons is not leaving |
|
|
120
|
-
| a dialog editor (`PostalAddressFormField`, `ContactsFormField`, `OpeningHoursFormField`, `AttributesFormField`, and `LocationFormField` from `@nxgt/material/map`) | focus leaves its buttons and the dialog they open. Closing the dialog hands focus back to the button that opened it |
|
|
120
|
+
| a dialog editor (`PostalAddressFormField`, `ContactsFormField` with `editing="dialog"`, `OpeningHoursFormField` with `editing="dialog"`, `AttributesFormField` with `editing="dialog"`, and `LocationFormField` from `@nxgt/material/map`) | focus leaves its buttons and the dialog they open. Closing the dialog hands focus back to the button that opened it |
|
|
121
121
|
| an upload (`UploadFormField`, `NetworkUploadFormField`, `S3UploadFormField`, `ImageFormField`, `ImageS3FormField`, `FileListFormField`, `FileListS3FormField`) | focus leaves the drop zone and its buttons. Choosing a file in the browser's own dialog is not leaving |
|
|
122
122
|
| `RichTextEditorFormField`, `CodeEditorFormField` | focus leaves the editor and its toolbar |
|
|
123
123
|
| `FormField` (config-driven) | the rule of the field its `type` renders |
|
|
@@ -308,16 +308,16 @@ shows them right under its fields.
|
|
|
308
308
|
`p-4` are recognised) sets `--form-actions-fill` to it:
|
|
309
309
|
|
|
310
310
|
```css
|
|
311
|
-
.edit-
|
|
311
|
+
.edit-record-form {
|
|
312
312
|
--form-actions-clearance: 9rem;
|
|
313
313
|
}
|
|
314
|
-
.edit-
|
|
314
|
+
.edit-record-dialog {
|
|
315
315
|
--form-actions-fill: 2rem; /* its pb-8 */
|
|
316
316
|
}
|
|
317
317
|
```
|
|
318
318
|
|
|
319
319
|
```tsx
|
|
320
|
-
<form className="form-grid-layout edit-
|
|
320
|
+
<form className="form-grid-layout edit-record-form">
|
|
321
321
|
```
|
|
322
322
|
- To scroll a form in a box of your own, give it a height and the scroll
|
|
323
323
|
(`className="form-grid-layout max-h-[60vh] overflow-y-auto"`); to keep the
|
|
@@ -500,7 +500,7 @@ or fails validation.
|
|
|
500
500
|
| `Slider` · `SliderFormField` | `number` when the default is a number; `number[]` when the default is an array (a range) | `0` / `[20, 80]` |
|
|
501
501
|
| `Switch` · `SwitchFormField` | `boolean` | `false` |
|
|
502
502
|
| `AttributesField` · `AttributesFormField` | `Attribute[]` | `[]` |
|
|
503
|
-
| `ContactsFormField` | `Contact[]`: `{ type: ContactType; value: string; label?: string \| null }` | `[]` |
|
|
503
|
+
| `ContactsFormField` | `Contact[]`: `{ type: ContactType; value: string; label?: string \| null; primary?: boolean \| null }` | `[]` |
|
|
504
504
|
| `PostalAddressFormField` | `PostalAddressFieldValue`: `{ street?, locality?, region?, postalCode?, country? }`; `{}` after delete | `{}` |
|
|
505
505
|
| `OpeningHoursFormField` | `OpeningHour[]`: `{ dayOfWeek: 0–6; openTime?: 'HH:mm'; closeTime?: 'HH:mm'; isClosed?: boolean }` | `[]` |
|
|
506
506
|
|
|
@@ -607,16 +607,22 @@ export function SupplierForm({ onSave }: { onSave: (values: Values) => void }) {
|
|
|
607
607
|
|
|
608
608
|
| Wrapper | Value | Notes |
|
|
609
609
|
| --- | --- | --- |
|
|
610
|
-
| `ContactsFormField` | `Contact[]` |
|
|
611
|
-
| `OpeningHoursFormField` | `OpeningHour[]` | `showAddAllDays` adds Monday–Friday 09:00–17:00 and a closed weekend in one click |
|
|
610
|
+
| `ContactsFormField` | `Contact[]` | Edited in place by default (one `primary` per type); `editing="dialog"` goes through `ContactDialog`. `ContactType` is `EMAIL`, `FAX`, `MOBILE`, `PHONE`, `WEBSITE` |
|
|
611
|
+
| `OpeningHoursFormField` | `OpeningHour[]` | Edits the week inline by default (`weekStartsOn` and `locale` apply there); `editing="dialog"` keeps the list and dialog, where `showAddAllDays` adds Monday–Friday 09:00–17:00 and a closed weekend in one click |
|
|
612
612
|
| `PostalAddressFormField` | `PostalAddressFieldValue` | Deleting the address writes `{}`, not `null` |
|
|
613
|
-
| `AttributesFormField` | `Attribute[]` | A typed attribute editor. Its `label` prop is required |
|
|
614
|
-
|
|
613
|
+
| `AttributesFormField` | `Attribute[]` | A typed attribute editor, in place by default (rows that open, drag and keyboard reorder that sets `priority`, unique names); `editing="dialog"` keeps the list and dialog. Its `label` prop is required |
|
|
614
|
+
|
|
615
|
+
- **The list schemas carry the editors' rules.** `contactsListSchema()`,
|
|
616
|
+
`attributesListSchema()` and `openingHoursListSchema()` are zod schemas of
|
|
617
|
+
the whole array (see [Contacts](../components/form-fields.md#contacts),
|
|
618
|
+
[Attributes](../components/form-fields.md#attributes) and
|
|
619
|
+
[OpeningHours](../components/form-fields.md#openinghours)). Each issue has an
|
|
620
|
+
English `message` and a `params.code` to translate from.
|
|
615
621
|
- **The array's own error is shown under the field.** `ContactsFormField`,
|
|
616
622
|
`OpeningHoursFormField` and `AttributesFormField` display
|
|
617
623
|
`errors.<name>.message` (where a Zod `.min(1)` on the array lands) and
|
|
618
624
|
`errors.<name>.root` (where a `useFieldArray` rule lands). Errors of one
|
|
619
|
-
item are shown in its dialog.
|
|
625
|
+
item are shown in its dialog, or under its range or name in the inline editor.
|
|
620
626
|
- `AttributesFormField` follows the form value: `form.reset()` and
|
|
621
627
|
`form.setValue()` replace the list it shows.
|
|
622
628
|
- `AttributeValuesFormFieldSet` is the other half: given `attributes`, it
|
|
@@ -625,7 +631,7 @@ export function SupplierForm({ onSave }: { onSave: (values: Values) => void }) {
|
|
|
625
631
|
|
|
626
632
|
### Rows a user adds and removes
|
|
627
633
|
|
|
628
|
-
When the rows are fields of their own — a
|
|
634
|
+
When the rows are fields of their own — a plan's periods, each with a
|
|
629
635
|
name, a start date and an end date — use
|
|
630
636
|
[`RepeatableGroupFormField`](../components/form-fields.md#repeatablegroup)
|
|
631
637
|
over `useFieldArray`, and render each row's fields with the form's own
|
package/docs/guides/i18n.md
CHANGED
|
@@ -255,7 +255,7 @@ their chevron, "Show the options" / "Afficher les options"
|
|
|
255
255
|
(`material.combobox.toggle`),
|
|
256
256
|
the `ThemePicker`'s group and mode names (`material.theme-picker.label` "Theme" / "Thème", `material.theme-picker.mode` "Mode"; the Light, Dark and System names are `ThemeToggle`'s own), the `LocationPreview` trigger
|
|
257
257
|
(`material.map.preview` / `preview-named`), and the delete buttons of the
|
|
258
|
-
`Contacts` / `OpeningHours` rows. `FacetGroup`'s toggle is named "Level, 2 selected" / "Level, 2 sélectionnés" (`material.facet-group.name`, over `{title}` and `{count}`: plain in English, an ICU plural in French) and its Clear button "Clear Level" / "Effacer Level" (`material.facet-group.clear-label`, with the visible `material.facet-group.clear`). `AccountMenu`'s avatar button is named "Account menu, {name}" / "Menu du compte, {name}" (`material.account-menu.trigger`, over `{name}`) and its last item reads "Sign out" / "Se déconnecter" (`material.account-menu.sign-out`), unless you pass `signOutLabel`; with `themeChoice` its "Theme" group reads "Theme" / "Thème", "Light" / "Clair", "Dark" / "Sombre", "System" / "Système" (`material.account-menu.theme.label`, `light`, `dark`, `system`). `LocalizedTextFields`' reset button is named "Reset {language} to the default" / "Rétablir la valeur par défaut ({language})" (`material.localized-text-fields.reset`), its tooltip reads "Default: {value}" (`reset-tooltip`) and its polite status "{language}: back to the default" (`reset-done`). Its "Other languages" trigger reads "Other languages ({count})" / "Autres langues ({count})" (`others-count`; `others` names the folded rows' group), and while closed over errors it is described by "{count} errors" / "{count} erreurs" (`others-errors`, an ICU plural over `{count}`). The language names come from `material.language.<code>` (English and French ship), then `Intl.DisplayNames`. `PrintDocument`'s page numbers
|
|
258
|
+
`Contacts` / `OpeningHours` rows, and the empty text of `ContactsSummary` and `AttributesSummary` (`material.contacts.labels.none` "No contacts" / "Aucun contact", `material.attributes.labels.none` "No attributes" / "Aucun attribut"; `@nxgt/material-vue` carries the same keys). `FacetGroup`'s toggle is named "Level, 2 selected" / "Level, 2 sélectionnés" (`material.facet-group.name`, over `{title}` and `{count}`: plain in English, an ICU plural in French) and its Clear button "Clear Level" / "Effacer Level" (`material.facet-group.clear-label`, with the visible `material.facet-group.clear`). `AccountMenu`'s avatar button is named "Account menu, {name}" / "Menu du compte, {name}" (`material.account-menu.trigger`, over `{name}`) and its last item reads "Sign out" / "Se déconnecter" (`material.account-menu.sign-out`), unless you pass `signOutLabel`; with `themeChoice` its "Theme" group reads "Theme" / "Thème", "Light" / "Clair", "Dark" / "Sombre", "System" / "Système" (`material.account-menu.theme.label`, `light`, `dark`, `system`). `LocalizedTextFields`' reset button is named "Reset {language} to the default" / "Rétablir la valeur par défaut ({language})" (`material.localized-text-fields.reset`), its tooltip reads "Default: {value}" (`reset-tooltip`) and its polite status "{language}: back to the default" (`reset-done`). Its "Other languages" trigger reads "Other languages ({count})" / "Autres langues ({count})" (`others-count`; `others` names the folded rows' group), and while closed over errors it is described by "{count} errors" / "{count} erreurs" (`others-errors`, an ICU plural over `{count}`). The language names come from `material.language.<code>` (English and French ship), then `Intl.DisplayNames`. `PrintDocument`'s page numbers
|
|
259
259
|
read `material.print.page-of` ("Page {page} of {pages}", "Page {page} sur
|
|
260
260
|
{pages}") and `material.print.page` ("Page {page}"), and a `PrintDataTable`
|
|
261
261
|
reads `material.print.table.total` ("Total", "Total") for its totals row and
|
package/docs/guides/ssr.md
CHANGED
|
@@ -168,6 +168,7 @@ State the server cannot know must be resolved the same way on both sides:
|
|
|
168
168
|
| Theme | no preference: light | An inline boot script in `<head>`, and `suppressHydrationWarning` on `<html>`. See [theming](theming.md#no-flash-on-load-a-boot-script) |
|
|
169
169
|
| `formatAmount`, `formatDate` | the server's locale and time zone, unless you pass them | Use `useFormatters({ timeZone })`, which follows the `I18nProvider` language, or pass `locale` and `timeZone` (`createFormatters` binds them). A date-only `YYYY-MM-DD` is the same everywhere. See [printing](printing.md#formatamount-and-formatdate) |
|
|
170
170
|
| Screen width, `prefers-color-scheme` (`useIsMobile`, `useTheme`, `AppHeader`, `DataTable`, `TimeField`, `Stepper`, `PaneLayout`, `ListDetailsLayout`, the code blocks, …) | no screen: a desktop, light | Nothing: a hydrating client renders the same, then switches on a phone or a dark screen |
|
|
171
|
+
| An attribute's `DATETIME` / `TIME` instant (`AttributesSummary`, `AttributesEditor`), the opening hours' first day of the week | the server's time zone and Intl data differ from the browser's | Nothing: the instant is written in UTC and the week starts on the language's first day until mount, then the viewer's zone and the locale's week info take over, so hydration matches |
|
|
171
172
|
| Step form (`createStepFormSlice`) | no `localStorage`: step 0 | Nothing with `useSliceReducer`: a hydrating client renders step 0, then resumes. A slice in a Redux store: see [forms](forms.md#a-multi-step-form) |
|
|
172
173
|
| Zod messages | no `localStorage`: English | Pass the request's language, `zodLocaleError({ language })`, where the server formats messages. See [forms](forms.md#validation-messages) |
|
|
173
174
|
|
|
Binary file
|
|
Binary file
|