prime-ui-kit 0.17.0 → 0.19.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/README.md +2 -1
- package/dist/chunks/{chunk-PRP4B6OG.js → chunk-IVS3J7AS.js} +1 -1
- package/dist/chunks/{chunk-2ODXLPW3.js → chunk-QFHE5GKW.js} +1 -0
- package/dist/chunks/{chunk-7WWZYGRC.js → chunk-RZNGLELR.js} +4497 -3896
- package/dist/chunks/{chunk-A4WE3UNC.js → chunk-WYCTXD63.js} +18399 -17624
- package/dist/chunks/{chunk-SVFWPLNW.js → chunk-ZTZ2ML2B.js} +14 -3
- package/dist/color-picker.js +3 -3
- package/dist/components/badge/Badge.d.ts +17 -0
- package/dist/components/breadcrumb/Breadcrumb.d.ts +11 -2
- package/dist/components/button/Button.d.ts +1 -1
- package/dist/components/button-group/ButtonGroup.d.ts +1 -1
- package/dist/components/card/Card.d.ts +8 -4
- package/dist/components/chat/Chat.d.ts +3 -3
- package/dist/components/chat/MessageList.d.ts +5 -1
- package/dist/components/chat/groupMessages.d.ts +12 -4
- package/dist/components/chat-list/ChatList.d.ts +18 -9
- package/dist/components/chat-list/formatChatTime.d.ts +3 -3
- package/dist/components/checkbox/Checkbox.d.ts +6 -1
- package/dist/components/color-field/ColorField.d.ts +1 -1
- package/dist/components/color-picker/ColorPicker.d.ts +6 -1
- package/dist/components/color-picker/ColorPresets.d.ts +9 -1
- package/dist/components/crossfade/Crossfade.d.ts +1 -1
- package/dist/components/data-table/ColumnsMenu.d.ts +16 -20
- package/dist/components/data-table/DataTable.d.ts +16 -11
- package/dist/components/data-table/Head.d.ts +10 -1
- package/dist/components/data-table/Row.d.ts +7 -1
- package/dist/components/data-table/StateRows.d.ts +3 -1
- package/dist/components/data-table/types.d.ts +5 -0
- package/dist/components/digit-input/DigitInput.d.ts +1 -1
- package/dist/components/drawer/Drawer.d.ts +3 -2
- package/dist/components/field-list/FieldList.d.ts +24 -12
- package/dist/components/hint/Hint.d.ts +8 -2
- package/dist/components/index.d.ts +7 -4
- package/dist/components/input/Input.d.ts +27 -5
- package/dist/components/input/inputSuggestions.d.ts +39 -0
- package/dist/components/kanban/Kanban.d.ts +8 -9
- package/dist/components/list/List.d.ts +25 -6
- package/dist/components/message/Message.d.ts +15 -3
- package/dist/components/message/MessageSticker.d.ts +27 -0
- package/dist/components/message/formatMessageTime.d.ts +5 -2
- package/dist/components/modal/Modal.d.ts +3 -2
- package/dist/components/native-select/NativeSelect.d.ts +1 -1
- package/dist/components/radio/Radio.d.ts +11 -2
- package/dist/components/rich-text/RichText.d.ts +23 -0
- package/dist/components/scroll-container/ScrollContainer.d.ts +1 -1
- package/dist/components/scroll-container/scrollGlide.d.ts +26 -0
- package/dist/components/scroll-container/useScrollMotion.d.ts +25 -0
- package/dist/components/segmented-control/SegmentedControl.d.ts +1 -1
- package/dist/components/select/selectContext.d.ts +7 -0
- package/dist/components/slider/Slider.d.ts +1 -1
- package/dist/components/switch/Switch.d.ts +6 -1
- package/dist/components/tag-select/TagSelect.d.ts +1 -1
- package/dist/components/textarea/Textarea.d.ts +6 -1
- package/dist/components/timeline/Timeline.d.ts +1 -1
- package/dist/icon-set.js +2 -2
- package/dist/index.css +1 -1
- package/dist/index.js +5 -3
- package/dist/internal/ChoiceField.d.ts +3 -1
- package/dist/internal/FieldFrame.d.ts +8 -2
- package/dist/internal/fieldCell.d.ts +7 -1
- package/dist/internal/formatClock.d.ts +23 -2
- package/dist/internal/leaveGuard.d.ts +27 -0
- package/dist/internal/messageList.d.ts +2 -0
- package/dist/internal/overlay/DialogGuard.d.ts +3 -2
- package/dist/internal/overlay/DialogParts.d.ts +40 -6
- package/dist/internal/swatch.d.ts +5 -0
- package/dist/internal/tokenDefaults.d.ts +1 -0
- package/dist/layout/index.d.ts +1 -1
- package/dist/layout/page/Page.d.ts +48 -15
- package/dist/skill/anti-slop.md +28 -0
- package/dist/skill/api-contract.md +14 -7
- package/dist/skill/cheatsheet.md +28 -9
- package/dist/skill/checklist.md +9 -0
- package/dist/skill/choosing.md +25 -12
- package/dist/skill/components.md +1 -0
- package/dist/skill/composition.md +88 -16
- package/dist/skill/layouts.md +6 -4
- package/dist/skill/reference/components/badge/COMPONENT.md +14 -1
- package/dist/skill/reference/components/badge/examples/examples.module.css +7 -0
- package/dist/skill/reference/components/badge/examples/tag-row.tsx +34 -0
- package/dist/skill/reference/components/breadcrumb/COMPONENT.md +9 -4
- package/dist/skill/reference/components/breadcrumb/examples/router.tsx +17 -0
- package/dist/skill/reference/components/button/COMPONENT.md +1 -1
- package/dist/skill/reference/components/button-group/COMPONENT.md +1 -0
- package/dist/skill/reference/components/card/COMPONENT.md +7 -1
- package/dist/skill/reference/components/card/examples/states.tsx +59 -0
- package/dist/skill/reference/components/chat/COMPONENT.md +4 -1
- package/dist/skill/reference/components/chat/examples/time-zone.tsx +83 -0
- package/dist/skill/reference/components/chat-list/COMPONENT.md +12 -6
- package/dist/skill/reference/components/chat-list/examples/links.tsx +1 -1
- package/dist/skill/reference/components/chat-list/examples/router.tsx +63 -0
- package/dist/skill/reference/components/checkbox/COMPONENT.md +1 -0
- package/dist/skill/reference/components/color-field/COMPONENT.md +2 -1
- package/dist/skill/reference/components/color-picker/COMPONENT.md +14 -5
- package/dist/skill/reference/components/color-picker/examples/presets-custom.tsx +31 -0
- package/dist/skill/reference/components/color-swatches/COMPONENT.md +1 -0
- package/dist/skill/reference/components/confirm-dialog/COMPONENT.md +1 -1
- package/dist/skill/reference/components/crossfade/COMPONENT.md +1 -1
- package/dist/skill/reference/components/data-table/COMPONENT.md +46 -41
- package/dist/skill/reference/components/data-table/examples/actions-column.tsx +154 -0
- package/dist/skill/reference/components/data-table/examples/columns-visibility.tsx +3 -12
- package/dist/skill/reference/components/data-table/examples/controlled.tsx +1 -1
- package/dist/skill/reference/components/data-table/examples/narrow.tsx +4 -11
- package/dist/skill/reference/components/data-table/examples/numeric.tsx +9 -2
- package/dist/skill/reference/components/data-table/examples/row-link.tsx +18 -22
- package/dist/skill/reference/components/data-table/examples/screen.tsx +12 -1
- package/dist/skill/reference/components/data-table/examples/toolbar.tsx +26 -32
- package/dist/skill/reference/components/datepicker/COMPONENT.md +3 -1
- package/dist/skill/reference/components/datepicker/examples/deadline.tsx +33 -0
- package/dist/skill/reference/components/digit-input/COMPONENT.md +1 -0
- package/dist/skill/reference/components/dnd/COMPONENT.md +4 -3
- package/dist/skill/reference/components/dnd/examples/examples.module.css +4 -3
- package/dist/skill/reference/components/dnd/examples/handle.tsx +5 -5
- package/dist/skill/reference/components/dnd/examples/locked.tsx +1 -1
- package/dist/skill/reference/components/dnd/examples/table.tsx +6 -6
- package/dist/skill/reference/components/drawer/COMPONENT.md +38 -9
- package/dist/skill/reference/components/drawer/examples/client-card.tsx +32 -8
- package/dist/skill/reference/components/drawer/examples/header-actions.tsx +1 -1
- package/dist/skill/reference/components/drawer/examples/loading.tsx +58 -0
- package/dist/skill/reference/components/field-list/COMPONENT.md +40 -24
- package/dist/skill/reference/components/field-list/examples/base-row.tsx +2 -1
- package/dist/skill/reference/components/field-list/examples/locked.tsx +11 -11
- package/dist/skill/reference/components/field-list/examples/reorder.tsx +1 -1
- package/dist/skill/reference/components/field-list/examples/saving.tsx +83 -0
- package/dist/skill/reference/components/field-list/examples/screen.module.css +1 -1
- package/dist/skill/reference/components/field-list/examples/screen.tsx +168 -162
- package/dist/skill/reference/components/field-list/examples/totals.tsx +83 -0
- package/dist/skill/reference/components/file-upload/COMPONENT.md +1 -0
- package/dist/skill/reference/components/hint/COMPONENT.md +16 -2
- package/dist/skill/reference/components/hint/examples/variants.tsx +48 -0
- package/dist/skill/reference/components/input/COMPONENT.md +39 -9
- package/dist/skill/reference/components/input/examples/affixes.tsx +4 -2
- package/dist/skill/reference/components/input/examples/dismiss.tsx +29 -0
- package/dist/skill/reference/components/input/examples/suggestions.tsx +26 -0
- package/dist/skill/reference/components/input/examples/time.tsx +24 -0
- package/dist/skill/reference/components/input/examples/with-select.tsx +44 -0
- package/dist/skill/reference/components/kanban/COMPONENT.md +12 -8
- package/dist/skill/reference/components/kanban/examples/columns.tsx +4 -2
- package/dist/skill/reference/components/kanban/examples/states.tsx +7 -5
- package/dist/skill/reference/components/list/COMPONENT.md +39 -13
- package/dist/skill/reference/components/list/examples/reorder.tsx +42 -7
- package/dist/skill/reference/components/list/examples/row-controls.tsx +80 -0
- package/dist/skill/reference/components/list/examples/with-icon.tsx +23 -2
- package/dist/skill/reference/components/login-form/COMPONENT.md +8 -7
- package/dist/skill/reference/components/login-form/examples/states.tsx +12 -14
- package/dist/skill/reference/components/message/COMPONENT.md +30 -9
- package/dist/skill/reference/components/message/examples/preview.tsx +28 -0
- package/dist/skill/reference/components/message/examples/sticker.tsx +35 -0
- package/dist/skill/reference/components/modal/COMPONENT.md +35 -10
- package/dist/skill/reference/components/modal/examples/long-content.tsx +37 -18
- package/dist/skill/reference/components/native-select/COMPONENT.md +1 -0
- package/dist/skill/reference/components/notification/COMPONENT.md +1 -1
- package/dist/skill/reference/components/radio/COMPONENT.md +2 -0
- package/dist/skill/reference/components/rich-text/COMPONENT.md +93 -0
- package/dist/skill/reference/components/rich-text/examples/overview.tsx +19 -0
- package/dist/skill/reference/components/scroll-container/COMPONENT.md +8 -5
- package/dist/skill/reference/components/segmented-control/COMPONENT.md +4 -2
- package/dist/skill/reference/components/segmented-control/examples/in-form.tsx +1 -1
- package/dist/skill/reference/components/select/COMPONENT.md +7 -3
- package/dist/skill/reference/components/sheet/COMPONENT.md +18 -7
- package/dist/skill/reference/components/slider/COMPONENT.md +1 -0
- package/dist/skill/reference/components/slider/examples/controlled.tsx +1 -1
- package/dist/skill/reference/components/switch/COMPONENT.md +1 -0
- package/dist/skill/reference/components/tag-select/COMPONENT.md +1 -0
- package/dist/skill/reference/components/textarea/COMPONENT.md +1 -0
- package/dist/skill/reference/components/timeline/COMPONENT.md +3 -3
- package/dist/skill/reference/components/timeline/examples/structure.tsx +2 -1
- package/dist/skill/reference/layout/app-shell/COMPONENT.md +2 -2
- package/dist/skill/reference/layout/bottom-nav/COMPONENT.md +1 -0
- package/dist/skill/reference/layout/page/COMPONENT.md +42 -20
- package/dist/skill/reference/layout/page/examples/examples.module.css +7 -0
- package/dist/skill/reference/layout/page/examples/guard.tsx +87 -0
- package/dist/skill/reference/layout/page/examples/loading.tsx +57 -0
- package/dist/skill/reference/layout/page/examples/screen.module.css +1 -1
- package/dist/skill/reference/layout/page/examples/screen.tsx +93 -104
- package/dist/skill/reference/layout/page/examples/sections.tsx +2 -15
- package/dist/skill/reference/layout/sidebar/COMPONENT.md +1 -0
- package/dist/skill/responsive.md +19 -12
- package/dist/skill/ux.md +5 -3
- package/dist/tokens/semantic.d.ts +3 -1
- package/dist/tokens/themes/dark.d.ts +1 -1
- package/package.json +2 -2
- package/src/components/badge/COMPONENT.md +14 -1
- package/src/components/badge/examples/examples.module.css +7 -0
- package/src/components/badge/examples/tag-row.tsx +34 -0
- package/src/components/breadcrumb/COMPONENT.md +9 -4
- package/src/components/breadcrumb/examples/router.tsx +17 -0
- package/src/components/button/COMPONENT.md +1 -1
- package/src/components/button-group/COMPONENT.md +1 -0
- package/src/components/card/COMPONENT.md +7 -1
- package/src/components/card/examples/states.tsx +59 -0
- package/src/components/chat/COMPONENT.md +4 -1
- package/src/components/chat/examples/time-zone.tsx +83 -0
- package/src/components/chat-list/COMPONENT.md +12 -6
- package/src/components/chat-list/examples/links.tsx +1 -1
- package/src/components/chat-list/examples/router.tsx +63 -0
- package/src/components/checkbox/COMPONENT.md +1 -0
- package/src/components/color-field/COMPONENT.md +2 -1
- package/src/components/color-picker/COMPONENT.md +14 -5
- package/src/components/color-picker/examples/presets-custom.tsx +31 -0
- package/src/components/color-swatches/COMPONENT.md +1 -0
- package/src/components/confirm-dialog/COMPONENT.md +1 -1
- package/src/components/crossfade/COMPONENT.md +1 -1
- package/src/components/data-table/COMPONENT.md +46 -41
- package/src/components/data-table/examples/actions-column.tsx +154 -0
- package/src/components/data-table/examples/columns-visibility.tsx +3 -12
- package/src/components/data-table/examples/controlled.tsx +1 -1
- package/src/components/data-table/examples/narrow.tsx +4 -11
- package/src/components/data-table/examples/numeric.tsx +9 -2
- package/src/components/data-table/examples/row-link.tsx +18 -22
- package/src/components/data-table/examples/screen.tsx +12 -1
- package/src/components/data-table/examples/toolbar.tsx +26 -32
- package/src/components/datepicker/COMPONENT.md +3 -1
- package/src/components/datepicker/examples/deadline.tsx +33 -0
- package/src/components/digit-input/COMPONENT.md +1 -0
- package/src/components/dnd/COMPONENT.md +4 -3
- package/src/components/dnd/examples/examples.module.css +4 -3
- package/src/components/dnd/examples/handle.tsx +5 -5
- package/src/components/dnd/examples/locked.tsx +1 -1
- package/src/components/dnd/examples/table.tsx +6 -6
- package/src/components/drawer/COMPONENT.md +38 -9
- package/src/components/drawer/examples/client-card.tsx +32 -8
- package/src/components/drawer/examples/header-actions.tsx +1 -1
- package/src/components/drawer/examples/loading.tsx +58 -0
- package/src/components/field-list/COMPONENT.md +40 -24
- package/src/components/field-list/examples/base-row.tsx +2 -1
- package/src/components/field-list/examples/locked.tsx +11 -11
- package/src/components/field-list/examples/reorder.tsx +1 -1
- package/src/components/field-list/examples/saving.tsx +83 -0
- package/src/components/field-list/examples/screen.module.css +1 -1
- package/src/components/field-list/examples/screen.tsx +168 -162
- package/src/components/field-list/examples/totals.tsx +83 -0
- package/src/components/file-upload/COMPONENT.md +1 -0
- package/src/components/hint/COMPONENT.md +16 -2
- package/src/components/hint/examples/variants.tsx +48 -0
- package/src/components/input/COMPONENT.md +39 -9
- package/src/components/input/examples/affixes.tsx +4 -2
- package/src/components/input/examples/dismiss.tsx +29 -0
- package/src/components/input/examples/suggestions.tsx +26 -0
- package/src/components/input/examples/time.tsx +24 -0
- package/src/components/input/examples/with-select.tsx +44 -0
- package/src/components/kanban/COMPONENT.md +12 -8
- package/src/components/kanban/examples/columns.tsx +4 -2
- package/src/components/kanban/examples/states.tsx +7 -5
- package/src/components/list/COMPONENT.md +39 -13
- package/src/components/list/examples/reorder.tsx +42 -7
- package/src/components/list/examples/row-controls.tsx +80 -0
- package/src/components/list/examples/with-icon.tsx +23 -2
- package/src/components/login-form/COMPONENT.md +8 -7
- package/src/components/login-form/examples/states.tsx +12 -14
- package/src/components/message/COMPONENT.md +30 -9
- package/src/components/message/examples/preview.tsx +28 -0
- package/src/components/message/examples/sticker.tsx +35 -0
- package/src/components/modal/COMPONENT.md +35 -10
- package/src/components/modal/examples/long-content.tsx +37 -18
- package/src/components/native-select/COMPONENT.md +1 -0
- package/src/components/notification/COMPONENT.md +1 -1
- package/src/components/radio/COMPONENT.md +2 -0
- package/src/components/rich-text/COMPONENT.md +93 -0
- package/src/components/rich-text/examples/overview.tsx +19 -0
- package/src/components/scroll-container/COMPONENT.md +8 -5
- package/src/components/segmented-control/COMPONENT.md +4 -2
- package/src/components/segmented-control/examples/in-form.tsx +1 -1
- package/src/components/select/COMPONENT.md +7 -3
- package/src/components/sheet/COMPONENT.md +18 -7
- package/src/components/slider/COMPONENT.md +1 -0
- package/src/components/slider/examples/controlled.tsx +1 -1
- package/src/components/switch/COMPONENT.md +1 -0
- package/src/components/tag-select/COMPONENT.md +1 -0
- package/src/components/textarea/COMPONENT.md +1 -0
- package/src/components/timeline/COMPONENT.md +3 -3
- package/src/components/timeline/examples/structure.tsx +2 -1
- package/src/layout/app-shell/COMPONENT.md +2 -2
- package/src/layout/bottom-nav/COMPONENT.md +1 -0
- package/src/layout/page/COMPONENT.md +42 -20
- package/src/layout/page/examples/examples.module.css +7 -0
- package/src/layout/page/examples/guard.tsx +87 -0
- package/src/layout/page/examples/loading.tsx +57 -0
- package/src/layout/page/examples/screen.module.css +1 -1
- package/src/layout/page/examples/screen.tsx +93 -104
- package/src/layout/page/examples/sections.tsx +2 -15
- package/src/layout/sidebar/COMPONENT.md +1 -0
- package/src/styles/theme-dark.css +1 -1
- package/src/styles/theme-light.css +2 -1
- package/dist/components/data-table/context.d.ts +0 -14
- package/dist/components/scroll-container/useWheelToInline.d.ts +0 -10
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
## When to use
|
|
9
9
|
- A form part where a person adds, edits and removes several records of the same fields: invoice lines, contacts of a client, specs of a plan, recipients.
|
|
10
10
|
- A base value with overrides (a price and its changes by month): the base is the first row, pinned with `getLocked`, each override is a row.
|
|
11
|
-
- A list of short texts in the order a person sets (selling points, checklist items): one column, `reorderable
|
|
11
|
+
- A list of short texts in the order a person sets (selling points, checklist items): one column, `reorderable`; each row's remove button is the last segment of its field.
|
|
12
12
|
- Small attributes of a record beside its text (an icon, a color): `width="compact"` columns with an icon-only `Select` (`Select.TriggerIcon` and no `Select.Value`) and a `ColorPresets` swatch.
|
|
13
13
|
|
|
14
14
|
## When not to use
|
|
@@ -28,11 +28,13 @@ import { FieldList, type FieldListColumn } from "prime-ui-kit";
|
|
|
28
28
|
FieldList field frame: label · the list · hint / error
|
|
29
29
|
├─ heading row the column headings, once (aria-hidden; each field carries its heading as its label)
|
|
30
30
|
├─ <ol> the rows (Dnd.Sortable with `reorderable`)
|
|
31
|
-
│ └─ <li> row [
|
|
31
|
+
│ └─ <li> row [a cell per column] [end: remove] [grip: handle | lock]
|
|
32
32
|
│ └─ cell one kit field without `label`, named by its column heading
|
|
33
|
+
│ (one column: no end slot, the remove is the field's last segment)
|
|
34
|
+
├─ totals row `renderTotals`: a group «Итого», a total per column on the same tracks
|
|
33
35
|
└─ add row Button «addLabel» (+ `labels.maxReached` at `max`)
|
|
34
36
|
```
|
|
35
|
-
A leaf: rows come from `value` through `renderItem`, which returns one element per column (a fragment). Every framed kit field (Input, Select, Datepicker, NativeSelect, ColorField…) in a cell takes its column heading as its label: read by screen readers, not shown while the heading row is on screen, shown above the field once the rows stack in a narrow container. `ColorPresets` takes it as the name of its trigger.
|
|
37
|
+
A leaf: rows come from `value` through `renderItem`, which returns one element per column (a fragment). Every framed kit field (Input, Select, Datepicker, NativeSelect, ColorField…) in a cell takes its column heading as its label: read by screen readers, not shown while the heading row is on screen, shown above the field once the rows stack in a narrow container. `ColorPresets` takes it as the name of its trigger. In a one-column list the row's remove button goes into the field as its last action segment (`Input.Wrapper`, `Textarea`, the same segment as `Input.Action`); a field without an action edge (a `Select`) keeps it in the end slot.
|
|
36
38
|
|
|
37
39
|
## API
|
|
38
40
|
|
|
@@ -43,22 +45,24 @@ Generic `FieldList<T>`; `ref` → `HTMLDivElement` (the field frame). An editabl
|
|
|
43
45
|
|
|
44
46
|
| Prop | Type | Default | Description |
|
|
45
47
|
|---|---|---|---|
|
|
46
|
-
| `columns` | `readonly FieldListColumn[]` | — (required) | The columns in cell order: `label` (the heading; it names the field of the column in every row), `width` (`fill` \| `compact` \| `s` \| `m`), `required` (a `*` after the heading). |
|
|
47
|
-
| `renderItem` | `(item: T, row: FieldListRow<T>) => ReactNode` | — (required) | The cells of a row, one element per column (a fragment): kit fields without `label`. `row` has `index`, `update(patch)` and `remove()`. |
|
|
48
|
+
| `columns` | `readonly FieldListColumn[]` | — (required) | The columns in cell order: `label` (the heading; it names the field of the column in every row), `width` (`fill` \| `compact` \| `xs` \| `s` \| `m`), `required` (a `*` after the heading). |
|
|
49
|
+
| `renderItem` | `(item: T, row: FieldListRow<T>) => ReactNode` | — (required) | The cells of a row, one element per column (a fragment): kit fields without `label`. `row` has `index`, `update(patch)` and `remove()`. In one column the remove is the field's last segment. |
|
|
50
|
+
| `renderTotals` | `(items: readonly T[]) => ReactNode` | — | The totals row under the rows: one node per column (a fragment), `null` where none; each aligned as its column's field text. The first cell without a total reads `labels.totals`. |
|
|
48
51
|
| `getId` | `(item: T) => string` | — (required) | Stable unique id of an item: the row key and the drag id. |
|
|
49
52
|
| `value` | `readonly T[]` | — | The rows (controlled). |
|
|
50
53
|
| `defaultValue` | `readonly T[]` | `[]` | The first rows (uncontrolled). |
|
|
51
|
-
| `onValueChange` | `(value: T[]) => void
|
|
54
|
+
| `onValueChange` | `(value: T[]) => void \| Promise<void>` | — | The new list after an edit (`row.update`), an add, a remove or a move. Return the save's promise: the new list shows while it is pending, the owner's again on a reject (say why with `error`). |
|
|
52
55
|
| `createItem` | `() => T` | — | A new empty item; with it the add button shows under the rows and focus goes to the new row's first field. |
|
|
53
56
|
| `addLabel` | `string` | — | Text of the add button: what is added («Добавить позицию»). |
|
|
54
57
|
| `getLabel` | `(item: T) => string` | — | The row's own text (its title field) for the remove button, the handle and drag announcements; else the row number. |
|
|
55
|
-
| `getLocked` | `(item: T) => boolean` | — | Pinned rows:
|
|
56
|
-
| `reorderable` | `boolean` | `false` | Rows move by a
|
|
58
|
+
| `getLocked` | `(item: T) => boolean` | — | Pinned rows: the remove button stays in its place, off, its tooltip says why (`labels.lockedReason`); with `reorderable` a lock takes the handle's place and the row holds it while others move. |
|
|
59
|
+
| `reorderable` | `boolean` | `false` | Rows move by a handle at the row's end, after the remove button: drag, a touch hold, Alt + ↑ / ↓. |
|
|
57
60
|
| `min` | `number` | `0` | The fewest rows: at this count the remove buttons are disabled. |
|
|
58
61
|
| `max` | `number` | — | The most rows: at this count the add button is disabled with `labels.maxReached` beside it. |
|
|
59
62
|
| `label` | `ReactNode` | — | Heading of the list; names the `<ol>`. Omit it inside a `Fieldset` whose legend names the list. |
|
|
60
63
|
| `aria-label` | `string` | — | Name of the `<ol>` when no visible `label` names it (a section heading above the list). |
|
|
61
64
|
| `hint` | `ReactNode` | — | Help text under the list; hidden while `error` shows. |
|
|
65
|
+
| `hintTone` | `"neutral" \| "success" \| "warning" \| "danger"` | `"neutral"` | Status color of `hint` («Осталось 12 дней» in `warning`). The words carry the status, the color repeats it; an `error` replaces the hint and its tone. A validation error is `error`, never a `danger` hint. |
|
|
62
66
|
| `error` | `ReactNode` | — | A problem of the list as a whole («Добавьте хотя бы одну позицию»); a field's own error goes on that field. |
|
|
63
67
|
| `disabled` | `boolean` | `false` | Disables every field, handle and button; a disabled `Fieldset` around it sets it too. |
|
|
64
68
|
| `size` | `"xs" \| "s" \| "m" \| "l" \| "xl"` | host tier, else `"m"` | Tier of the headings, buttons and the kit fields that read the host tier. Without it the host tier, else `m`. |
|
|
@@ -71,17 +75,19 @@ Generic `FieldList<T>`; `ref` → `HTMLDivElement` (the field frame). An editabl
|
|
|
71
75
|
| Value | Looks like | Use when | Default |
|
|
72
76
|
|---|---|---|---|
|
|
73
77
|
| `fill` | shares the free width with the other `fill` columns | names, descriptions, any text | yes |
|
|
74
|
-
| `compact` |
|
|
78
|
+
| `compact` | as wide as the widest of its controls, heading and total — the gap after it is the column gap like every other | an icon-only `Select`, a `ColorPresets` swatch, a checkbox | |
|
|
79
|
+
| `xs` | 96 px | a number of a few digits: a share, a percent, a count | |
|
|
75
80
|
| `s` | 144 px | a quantity, a code, a number of days | |
|
|
76
81
|
| `m` | 192 px | an amount with its unit, a date, a phone | |
|
|
77
82
|
|
|
78
83
|
### Flags
|
|
79
84
|
| Value | Looks like | Use when | Default |
|
|
80
85
|
|---|---|---|---|
|
|
81
|
-
| `reorderable` | a grip
|
|
82
|
-
| `getLocked` | a lock where the grip is
|
|
86
|
+
| `reorderable` | a grip at the end of every row, after the remove button; Alt + ↑ / ↓ on it | the order of the records matters (a pricing page, a checklist) | `false` |
|
|
87
|
+
| `getLocked` | the remove button in its place, off, with a tooltip saying why (`labels.lockedReason`); with `reorderable` a lock where the grip is and the row holds its place | a base value, system records a person cannot drop | — |
|
|
83
88
|
| `createItem` | the add button under the rows, aligned with the first column | the person grows the list | — |
|
|
84
89
|
| `min` · `max` | remove buttons off at `min`; the add button off with «Не больше {max}» at `max` | the record count is bounded | `0` · — |
|
|
90
|
+
| `renderTotals` | a row of totals under the rows over a faint line, each on its column, aligned as the column's field text; «Итого» in the first cell without a total | the rows add up (shares, amounts, hours) | — |
|
|
85
91
|
|
|
86
92
|
**Combinations**
|
|
87
93
|
- Recommended: `label` on the list (or a `Fieldset` legend around it), one heading per column, a `getLabel` from the title field, an `addLabel` that says what is added.
|
|
@@ -95,16 +101,19 @@ Generic `FieldList<T>`; `ref` → `HTMLDivElement` (the field frame). An editabl
|
|
|
95
101
|
| empty | `value` with no items | no heading row; only the add button (with `createItem`) |
|
|
96
102
|
| at min | `value.length <= min` | remove buttons `disabled` |
|
|
97
103
|
| at max | `value.length >= max` | add button `disabled`, `labels.maxReached` beside it |
|
|
98
|
-
| pinned row | `getLocked(item)` | `data-locked="true"` on the `<li>`; a
|
|
104
|
+
| pinned row | `getLocked(item)` | `data-locked="true"` on the `<li>`; the remove button stays in its slot, off (`disabled` in the end slot behind a focusable Tooltip host, `aria-disabled="true"` as a field segment), its tooltip `labels.lockedReason`; with `reorderable` Dnd's lock in the grip box |
|
|
99
105
|
| invalid | `error` (the list), a field's own `error` | the list's message under the add row; a field's under the field in its row |
|
|
100
106
|
| disabled | `disabled` (or a disabled Fieldset) | `data-disabled="true"` on the frame; fields, handles and buttons disabled through the kit's disabled context |
|
|
101
|
-
| stacked | container narrower than 30rem | heading row hidden, each row's fields in one column with their labels shown |
|
|
107
|
+
| stacked | container narrower than 30rem | heading row hidden, each row's fields in one column with their labels shown; each total on its own line after its column name |
|
|
108
|
+
| saving | a promise from `onValueChange` | the rows show the new list, `aria-busy="true"` on the `<ol>`; a reject brings the owner's list back |
|
|
102
109
|
| rows come and go | the add button, a remove button, a new `value` | a row the data adds rises in once (`enterMotion`), a removed one closes up in place and the rows below glide up (`listPresence`); the heading row rises in with the first row and closes up with the last (`partPresence`); the first render stays still |
|
|
103
110
|
|
|
104
111
|
## Layout & spacing
|
|
105
112
|
- Rows 12 apart (`--prime-space-3`); columns 12 apart; the heading row sits at the tier's label gap above the first row.
|
|
106
|
-
- Every row and the
|
|
107
|
-
-
|
|
113
|
+
- Every row, the heading row and the totals row share one track list: `[columns] [end] [grip]`; the end fits a remove button of the tier, the grip (with `reorderable`) is one `xs` control wide (the handle or the lock) and ends the row on the inline-end side (on the left in RTL). Every row fills the same slots: a pinned row shows its remove off, never a hole. A one-column list has no end track: the remove is inside the field.
|
|
114
|
+
- A `compact` column is measured: its track is the widest of its controls, its heading and its total, so the gaps between columns are all 12.
|
|
115
|
+
- The remove button, the handle and the lock stand on the middle of the first line of fields, so a field's error under it grows the row downwards only.
|
|
116
|
+
- The totals row stands 12 under the last row over a faint line, 12 more above its totals; a total's text sits where its column's field text does: at the start, or at the end for a field that ends its number there (an `Input` with `inputMode="decimal"` and an end `Input.InlineAffix`). Stacked, every total ends its line.
|
|
108
117
|
- The add button lines up with the first column of fields, 12 under the last row.
|
|
109
118
|
- Under 30rem of its own width (a container query): rows stack, fields 16 apart inside a row, rows 24 apart.
|
|
110
119
|
|
|
@@ -115,7 +124,7 @@ Generic `FieldList<T>`; `ref` → `HTMLDivElement` (the field frame). An editabl
|
|
|
115
124
|
|
|
116
125
|
| Key | Action |
|
|
117
126
|
|---|---|
|
|
118
|
-
| `Tab` | Moves through the fields of a row, then to its remove button and on. |
|
|
127
|
+
| `Tab` | Moves through the fields of a row, then to its remove button (inside the field in a one-column list), its handle and on; a pinned row's remove takes focus to show why it is off. |
|
|
119
128
|
| `Enter` · `Space` | On the add button adds a row and focuses its first field; on a remove button removes the row and focuses the next row's remove button. |
|
|
120
129
|
| `Alt + ↑` · `Alt + ↓` | With `reorderable`, moves the row up or down (from its handle). |
|
|
121
130
|
|
|
@@ -126,8 +135,9 @@ Generic `FieldList<T>`; `ref` → `HTMLDivElement` (the field frame). An editabl
|
|
|
126
135
|
- The heading row is `aria-hidden`: each field in a cell carries its column heading as its own label, read but not shown on wide rows.
|
|
127
136
|
- The `<ol>` is described by the list's `hint` and `error` (`aria-describedby`), which speak for every row.
|
|
128
137
|
- In a narrow container the heading row goes and each field's label shows above it.
|
|
138
|
+
- The totals row (`renderTotals`) is a `group` named by `labels.totals`; each total is read after its column heading.
|
|
129
139
|
- The remove button is «Удалить {row}», the handle «Переместить {row}»: the row's name from `getLabel`, else its number.
|
|
130
|
-
- A pinned row
|
|
140
|
+
- A pinned row keeps its remove button: `aria-disabled`, described by `labels.lockedReason` (a tooltip on hover and focus); with `reorderable` Dnd's lock stands in place of the handle.
|
|
131
141
|
|
|
132
142
|
### Labels
|
|
133
143
|
<!-- Generated from api.ts by `bun run docs:build`. Edit api.ts, not this section. -->
|
|
@@ -138,23 +148,26 @@ Generic `FieldList<T>`; `ref` → `HTMLDivElement` (the field frame). An editabl
|
|
|
138
148
|
| `reorder` | `"Переместить {row}"` | `aria-label` of a row's handle (`reorderable`); `{row}` is the row's name. |
|
|
139
149
|
| `row` | `"строку {index}"` | Name of a row without `getLabel` text; `{index}` counts from 1. |
|
|
140
150
|
| `namedRow` | `"«{label}»"` | Name of a row with `getLabel` text; `{label}` is that text. |
|
|
141
|
-
| `
|
|
151
|
+
| `lockedReason` | `"Закреплённую строку нельзя удалить"` | Why the remove button of a pinned row (`getLocked`) is off: its tooltip and description. |
|
|
142
152
|
| `maxReached` | `"Не больше {max}"` | Text beside the disabled add button once the list holds `max` rows. |
|
|
153
|
+
| `totals` | `"Итого"` | Name of the totals row (`renderTotals`), shown in its first cell when that has no total. |
|
|
143
154
|
|
|
144
155
|
## Examples
|
|
145
156
|
| Example | Shows |
|
|
146
157
|
|---|---|
|
|
147
158
|
| [overview.tsx](examples/overview.tsx) | Lines of an invoice: the column headings stand once above the rows, each row has a remove button and the add button sits under them — `columns`, `renderItem`, `createItem`, `addLabel`. |
|
|
148
159
|
| [sizes.tsx](examples/sizes.tsx) | Every tier: the headings, the fields, the handle and the buttons follow the list's size — `size`. |
|
|
149
|
-
| [reorder.tsx](examples/reorder.tsx) | Selling points of a plan in the order of the pricing page: a one-column list has no headings and no numbers, a point moves by its handle or Alt+arrows — `reorderable`, `getLabel`. |
|
|
150
|
-
| [locked.tsx](examples/locked.tsx) | Specs of a plan on one line each: an icon picker and
|
|
151
|
-
| [base-row.tsx](examples/base-row.tsx) | A plan price and its changes by month in one table: the base price is the first, pinned row, each change is a row with its month and its price, and the unit sits in the field, not in a heading — `getLocked`, `width`. |
|
|
160
|
+
| [reorder.tsx](examples/reorder.tsx) | Selling points of a plan in the order of the pricing page: a one-column list has no headings and no numbers, the remove button is the last segment of each field, a point moves by its handle or Alt+arrows — `reorderable`, `getLabel`. |
|
|
161
|
+
| [locked.tsx](examples/locked.tsx) | Specs of a plan on one line each: an icon picker and its color side by side in compact columns, the first three specs are pinned with a lock in the handle's place and a remove button that is off and says why, the rest reorder below them — `getLocked`, `width`, `reorderable`. |
|
|
162
|
+
| [base-row.tsx](examples/base-row.tsx) | A plan price and its changes by month in one table: the base price is the first, pinned row whose remove button is off and says why, each change is a row with its month and its price, and the unit sits in the field, not in a heading — `getLocked`, `labels`, `width`. |
|
|
152
163
|
| [limits.tsx](examples/limits.tsx) | Recipients of invoice reminders, from one to three: the last row cannot be removed and the add button says why it is off once the list is full — `min`, `max`, `hint`. |
|
|
153
164
|
| [errors.tsx](examples/errors.tsx) | Contacts of a client checked on save: a field's problem shows under that field in its row, a problem of the whole list under the list, and focus goes to the first wrong field; a `*` after a heading marks a required column — `error`, `required`. |
|
|
165
|
+
| [totals.tsx](examples/totals.tsx) | A deal bonus split between the team: the share takes a narrow column, and the totals row under the lines sums the shares and the amounts, each ending where its fields end their numbers — `renderTotals`, `width`. |
|
|
166
|
+
| [saving.tsx](examples/saving.tsx) | Removing a recipient is saved on the server: the row leaves at once and comes back with the reason if the server refuses — `onValueChange`, `error`. |
|
|
154
167
|
| [states.tsx](examples/states.tsx) | The same list editable and disabled while the request saves it: fields, handles and buttons turn off together — `disabled`. |
|
|
155
168
|
| [controlled.tsx](examples/controlled.tsx) | The parent owns the lines: it sums them under the list and puts the saved lines back on reset — `value`, `onValueChange`. |
|
|
156
169
|
| [narrow.tsx](examples/narrow.tsx) | The invoice lines in a 320 px column: the headings give way, each row stacks its fields with their own labels and rows stand a group apart — `columns`. |
|
|
157
|
-
| [screen.tsx](examples/screen.tsx) | A record editor as a document page, a long form whose title, description and save stand in the content header: a plan's name, its specs and prices
|
|
170
|
+
| [screen.tsx](examples/screen.tsx) | A record editor as a document page, a long form whose title, description and save stand in the content header and whose blocks are titled cards one under another: a plan's name, its specs and prices; repeating records as FieldList rows with the headings once, an icon and its color side by side, a base price pinned above its changes, one value in three currencies in one row — `getLocked`, `compact`. |
|
|
158
171
|
|
|
159
172
|
## Mistakes
|
|
160
173
|
- A `label` above every field of every row («Иконка», «Параметр», «Значение» four times) → a FieldList: the headings stand once, the fields take them as hidden labels.
|
|
@@ -162,10 +175,13 @@ Generic `FieldList<T>`; `ref` → `HTMLDivElement` (the field frame). An editabl
|
|
|
162
175
|
- One record split over two lines of half-width fields → one row; small attributes go to `compact` columns (icon-only `Select`, `ColorPresets`).
|
|
163
176
|
- A base value as a separate field above its overrides, in another geometry → the first row, pinned with `getLocked`.
|
|
164
177
|
- The unit in every heading («Цена, ₽ / день») and again in the field → the unit once, in `Input.InlineAffix`.
|
|
165
|
-
- A hint that explains what the lock already shows («Первые три всегда первыми») → no hint; the lock
|
|
178
|
+
- A hint that explains what the lock already shows («Первые три всегда первыми») → no hint; the lock and the remove button's reason say it.
|
|
179
|
+
- A pinned row with an empty place where the others have a remove button → none to fix by hand: FieldList draws it off with `labels.lockedReason`; say the domain reason there («Базовую цену нельзя удалить»).
|
|
180
|
+
- A one-field row with a remove button in a column beside the field → FieldList puts it inside the field; no `Input.Action` of your own for it.
|
|
166
181
|
- `aria-label` written on every field in a cell → none: the column heading names the field.
|
|
167
182
|
- A grip, a remove button and an add button drawn by hand around a `.map()` of fields → FieldList.
|
|
183
|
+
- A total under the list typed by hand in a line of text, or a DataTable of fields to get a totals row → `renderTotals`.
|
|
168
184
|
|
|
169
185
|
## Related
|
|
170
|
-
- **Built from:** [Button](../button/COMPONENT.md) (add, remove), [Dnd](../dnd/COMPONENT.md) (`reorderable`: `Dnd.Sortable`, `Dnd.Handle`; the lock of a pinned row), [Label](../label/COMPONENT.md) (headings), the field frame (label, [Hint](../hint/COMPONENT.md) for the hint and error), [VisuallyHidden](../visually-hidden/COMPONENT.md)
|
|
186
|
+
- **Built from:** [Button](../button/COMPONENT.md) (add, remove), [Dnd](../dnd/COMPONENT.md) (`reorderable`: `Dnd.Sortable`, `Dnd.Handle`; the lock of a pinned row), [Label](../label/COMPONENT.md) (headings), [Tooltip](../tooltip/COMPONENT.md) (why a pinned row's remove is off), the field action segment (the remove inside the field of a one-column list), the field frame (label, [Hint](../hint/COMPONENT.md) for the hint and error), [VisuallyHidden](../visually-hidden/COMPONENT.md)
|
|
171
187
|
- **See also:** [Input](../input/COMPONENT.md), [Select](../select/COMPONENT.md) (icon-only trigger), [ColorPicker](../color-picker/COMPONENT.md) (`ColorPresets`), [Fieldset](../fieldset/COMPONENT.md), [DataTable](../data-table/COMPONENT.md)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** A plan price and its changes by month in one table: the base price is the first, pinned row, each change is a row with its month and its price, and the unit sits in the field, not in a heading — `getLocked`, `width`. */
|
|
1
|
+
/** A plan price and its changes by month in one table: the base price is the first, pinned row whose remove button is off and says why, each change is a row with its month and its price, and the unit sits in the field, not in a heading — `getLocked`, `labels`, `width`. */
|
|
2
2
|
import { FieldList, type FieldListColumn, Input, Select } from "prime-ui-kit";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
|
|
@@ -30,6 +30,7 @@ export default function FieldListBaseRowExample() {
|
|
|
30
30
|
onValueChange={setPeriods}
|
|
31
31
|
getId={(period) => period.id}
|
|
32
32
|
getLocked={(period) => period.base ?? false}
|
|
33
|
+
labels={{ lockedReason: "Базовую цену нельзя удалить" }}
|
|
33
34
|
createItem={() => ({ id: crypto.randomUUID(), from: "", price: "" })}
|
|
34
35
|
addLabel="Добавить изменение цены"
|
|
35
36
|
renderItem={(period, row) => (
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Specs of a plan on one line each: an icon picker and
|
|
1
|
+
/** Specs of a plan on one line each: an icon picker and its color side by side in compact columns, the first three specs are pinned with a lock in the handle's place and a remove button that is off and says why, the rest reorder below them — `getLocked`, `width`, `reorderable`. */
|
|
2
2
|
import {
|
|
3
3
|
ColorPresets,
|
|
4
4
|
FieldList,
|
|
@@ -21,9 +21,9 @@ type Spec = {
|
|
|
21
21
|
|
|
22
22
|
const COLUMNS: FieldListColumn[] = [
|
|
23
23
|
{ label: "Иконка", width: "compact" },
|
|
24
|
+
{ label: "Цвет", width: "compact" },
|
|
24
25
|
{ label: "Параметр" },
|
|
25
26
|
{ label: "Значение" },
|
|
26
|
-
{ label: "Цвет", width: "compact" },
|
|
27
27
|
];
|
|
28
28
|
|
|
29
29
|
const ICONS: { name: IconName; label: string }[] = [
|
|
@@ -107,6 +107,15 @@ export default function FieldListLockedExample() {
|
|
|
107
107
|
))}
|
|
108
108
|
</Select.Content>
|
|
109
109
|
</Select.Root>
|
|
110
|
+
<ColorPresets.Root
|
|
111
|
+
value={spec.color}
|
|
112
|
+
onValueChange={(color) => row.update({ color: color ?? spec.color })}
|
|
113
|
+
>
|
|
114
|
+
<ColorPresets.Trigger>
|
|
115
|
+
<ColorPresets.SwatchButton />
|
|
116
|
+
</ColorPresets.Trigger>
|
|
117
|
+
<ColorPresets.Content />
|
|
118
|
+
</ColorPresets.Root>
|
|
110
119
|
<Input.Root>
|
|
111
120
|
<Input.Wrapper>
|
|
112
121
|
<Input.Field
|
|
@@ -123,15 +132,6 @@ export default function FieldListLockedExample() {
|
|
|
123
132
|
/>
|
|
124
133
|
</Input.Wrapper>
|
|
125
134
|
</Input.Root>
|
|
126
|
-
<ColorPresets.Root
|
|
127
|
-
value={spec.color}
|
|
128
|
-
onValueChange={(color) => row.update({ color: color ?? spec.color })}
|
|
129
|
-
>
|
|
130
|
-
<ColorPresets.Trigger>
|
|
131
|
-
<ColorPresets.SwatchButton />
|
|
132
|
-
</ColorPresets.Trigger>
|
|
133
|
-
<ColorPresets.Content />
|
|
134
|
-
</ColorPresets.Root>
|
|
135
135
|
</>
|
|
136
136
|
)}
|
|
137
137
|
/>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Selling points of a plan in the order of the pricing page: a one-column list has no headings and no numbers, a point moves by its handle or Alt+arrows — `reorderable`, `getLabel`. */
|
|
1
|
+
/** Selling points of a plan in the order of the pricing page: a one-column list has no headings and no numbers, the remove button is the last segment of each field, a point moves by its handle or Alt+arrows — `reorderable`, `getLabel`. */
|
|
2
2
|
import { FieldList, type FieldListColumn, Input } from "prime-ui-kit";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/** Removing a recipient is saved on the server: the row leaves at once and comes back with the reason if the server refuses — `onValueChange`, `error`. */
|
|
2
|
+
import { FieldList, type FieldListColumn, Input } from "prime-ui-kit";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
type Recipient = { id: string; name: string; email: string; billing: boolean };
|
|
6
|
+
|
|
7
|
+
const COLUMNS: FieldListColumn[] = [{ label: "Имя" }, { label: "Почта" }];
|
|
8
|
+
|
|
9
|
+
const RECIPIENTS: Recipient[] = [
|
|
10
|
+
{ id: "accounting", name: "Бухгалтерия", email: "buh@severstroy.ru", billing: true },
|
|
11
|
+
{ id: "director", name: "Ирина Котова", email: "kotova@severstroy.ru", billing: false },
|
|
12
|
+
{ id: "manager", name: "Олег Демин", email: "demin@severstroy.ru", billing: false },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
/** The server's answer to a removal, a beat later: the billing contact stays. */
|
|
16
|
+
function saveRemoval(removed: Recipient[]) {
|
|
17
|
+
return new Promise<void>((resolve, reject) => {
|
|
18
|
+
setTimeout(() => {
|
|
19
|
+
if (removed.some((recipient) => recipient.billing)) {
|
|
20
|
+
reject(new Error("Бухгалтерию нельзя убрать: на неё уходят счета"));
|
|
21
|
+
} else {
|
|
22
|
+
resolve();
|
|
23
|
+
}
|
|
24
|
+
}, 900);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default function FieldListSavingExample() {
|
|
29
|
+
const [recipients, setRecipients] = React.useState(RECIPIENTS);
|
|
30
|
+
const [failure, setFailure] = React.useState<string | null>(null);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<FieldList
|
|
34
|
+
label="Кому отправлять счета"
|
|
35
|
+
error={failure}
|
|
36
|
+
columns={COLUMNS}
|
|
37
|
+
value={recipients}
|
|
38
|
+
onValueChange={(next) => {
|
|
39
|
+
setFailure(null);
|
|
40
|
+
const removed = recipients.filter(
|
|
41
|
+
(recipient) => !next.some((item) => item.id === recipient.id),
|
|
42
|
+
);
|
|
43
|
+
if (removed.length === 0) {
|
|
44
|
+
setRecipients(next);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
return saveRemoval(removed).then(
|
|
48
|
+
() => setRecipients(next),
|
|
49
|
+
(reason: Error) => {
|
|
50
|
+
setFailure(reason.message);
|
|
51
|
+
throw reason;
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
}}
|
|
55
|
+
getId={(recipient) => recipient.id}
|
|
56
|
+
getLabel={(recipient) => recipient.name}
|
|
57
|
+
createItem={() => ({ id: crypto.randomUUID(), name: "", email: "", billing: false })}
|
|
58
|
+
addLabel="Добавить получателя"
|
|
59
|
+
renderItem={(recipient, row) => (
|
|
60
|
+
<>
|
|
61
|
+
<Input.Root>
|
|
62
|
+
<Input.Wrapper>
|
|
63
|
+
<Input.Field
|
|
64
|
+
value={recipient.name}
|
|
65
|
+
onChange={(event) => row.update({ name: event.target.value })}
|
|
66
|
+
/>
|
|
67
|
+
</Input.Wrapper>
|
|
68
|
+
</Input.Root>
|
|
69
|
+
<Input.Root>
|
|
70
|
+
<Input.Wrapper>
|
|
71
|
+
<Input.Field
|
|
72
|
+
type="email"
|
|
73
|
+
value={recipient.email}
|
|
74
|
+
placeholder="name@company.ru"
|
|
75
|
+
onChange={(event) => row.update({ email: event.target.value })}
|
|
76
|
+
/>
|
|
77
|
+
</Input.Wrapper>
|
|
78
|
+
</Input.Root>
|
|
79
|
+
</>
|
|
80
|
+
)}
|
|
81
|
+
/>
|
|
82
|
+
);
|
|
83
|
+
}
|