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
|
@@ -11,12 +11,15 @@
|
|
|
11
11
|
- Values with a fixed prefix/suffix (`https://`, domain, `₽`, `%`).
|
|
12
12
|
- Inline editing in a record card: each field saves itself (`onCommit`) on blur and Enter.
|
|
13
13
|
- A value with actions beside it: copy, write, open a profile (`Input.CopyButton`, `Input.Action`); a password with a show toggle (`Input.RevealButton`).
|
|
14
|
+
- Free text with suggestions (a combobox): a place, a city, a tag the team typed before — `suggestions`; any other text stays allowed.
|
|
15
|
+
- A picker and a text as one field (an icon and a name, a channel and an address): a `Select` inside `Input.Wrapper` is the box's start segment.
|
|
16
|
+
- A time of day: `Input.Field type="time"` — the browser's hour and minute segments (arrows step them, `step`, `min`, `max`), a wheel on phones, and no calendar to open.
|
|
14
17
|
- As the reference field: Select, Datepicker, TagSelect and Textarea follow the same label / field / support-row contract.
|
|
15
18
|
|
|
16
19
|
## When not to use
|
|
17
20
|
- Multi-line text → use [Textarea](../textarea/COMPONENT.md).
|
|
18
21
|
- One-time codes split into cells → use [DigitInput](../digit-input/COMPONENT.md).
|
|
19
|
-
- Choosing from a fixed list → use [Select](../select/COMPONENT.md) or [TagSelect](../tag-select/COMPONENT.md).
|
|
22
|
+
- Choosing from a fixed list, where the value must be one of it → use [Select](../select/COMPONENT.md) or [TagSelect](../tag-select/COMPONENT.md); `suggestions` only help typing free text.
|
|
20
23
|
- Dates → use [Datepicker](../datepicker/COMPONENT.md).
|
|
21
24
|
- A standalone label or message without an Input field → use [Label](../label/COMPONENT.md) / [Hint](../hint/COMPONENT.md).
|
|
22
25
|
- Global search across the app in a dialog → use [CommandMenu](../command-menu/COMPONENT.md).
|
|
@@ -33,14 +36,16 @@ Input.Root label row, field body, support row; provides size / invali
|
|
|
33
36
|
├─ Input.Affix tinted section flush with the edge (start | end)
|
|
34
37
|
├─ Input.Icon decorative icon (start | end)
|
|
35
38
|
├─ Input.InlineAffix muted text next to the value (start | end)
|
|
36
|
-
├─
|
|
39
|
+
├─ Select.Root optional start segment: an icon-only picker in the box (no frame)
|
|
40
|
+
├─ Input.Field native <input>; a combobox with `suggestions`
|
|
37
41
|
├─ Input.ClearButton clear segment (kit-rendered)
|
|
38
42
|
├─ Input.CopyButton copy segment; the glyph morphs into a check (kit-rendered)
|
|
39
43
|
├─ Input.RevealButton show / hide a password segment, aria-pressed (kit-rendered)
|
|
40
44
|
└─ Input.Action the consumer's named icon action with a tooltip (button or asChild link)
|
|
45
|
+
listbox `suggestions`: a floating list under the box, as wide as it
|
|
41
46
|
Input.Counter goes into Input.Root `counter` (support row, right side)
|
|
42
47
|
```
|
|
43
|
-
The trailing side has a fixed CSS `order`, independent of JSX order: value · end inline affix · commit status (`onCommit`) · end icon · segments · end affix. The segments (`ClearButton`, `CopyButton`, `RevealButton`, `Action`) keep their JSX order among themselves and sit edge to edge, full field height, flush with the end edge. All `side="start"` slots share one order before the value, so among them the JSX order decides — write them as affix, icon, inline affix.
|
|
48
|
+
The trailing side has a fixed CSS `order`, independent of JSX order: value · end inline affix · commit status (`onCommit`) · end icon · segments · end affix. The segments (`ClearButton`, `CopyButton`, `RevealButton`, `Action`) keep their JSX order among themselves and sit edge to edge, full field height, flush with the end edge. A `Select` segment comes first, flush with the start edge. All `side="start"` slots share one order before the value, so among them the JSX order decides — write them as affix, icon, inline affix.
|
|
44
49
|
|
|
45
50
|
## API
|
|
46
51
|
|
|
@@ -56,6 +61,7 @@ The trailing side has a fixed CSS `order`, independent of JSX order: value · en
|
|
|
56
61
|
| `required` | `boolean` | `false` | Red `*` after the label (`aria-hidden`) and native `required` on `Input.Field`. |
|
|
57
62
|
| `optional` | `boolean` | `false` | Muted marker right after the label text (`labels.optional`). |
|
|
58
63
|
| `hint` | `ReactNode` | — | Help text under the field. Hidden while `error` is shown. |
|
|
64
|
+
| `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. |
|
|
59
65
|
| `error` | `ReactNode` | — | Error message in the hint slot; implies `invalid`. |
|
|
60
66
|
| `invalid` | `boolean` | `false` | Danger inset ring on the field, `aria-invalid` on the input. A non-empty `error` implies it. |
|
|
61
67
|
| `focusRing` | `boolean` | `true` | `false` hides only the visual focus ring on `Input.Wrapper` (`data-focus-ring="false"`); focus, keyboard, ARIA and the invalid ring stay. |
|
|
@@ -65,6 +71,9 @@ The trailing side has a fixed CSS `order`, independent of JSX order: value · en
|
|
|
65
71
|
| `strength` | `boolean` | `false` | A password strength meter for new-password fields: a stepped `ProgressBar` under the field and the level word (weak … hard) in the support row. Follows the value of `Input.Field`. |
|
|
66
72
|
| `getStrength` | `(value: string) => 0 \| 1 \| 2 \| 3 \| 4` | `getPasswordStrength` | Replaces the kit's estimate of `strength`. Return 0 for empty, 1 weak … 4 hard. A hint for the person typing, not a security check. |
|
|
67
73
|
| `onCommit` | `(value: string) => void \| Promise<void>` | — | Inline save: fires with a changed value on blur and Enter; Escape reverts. Return a Promise: the field shows a spinner and stays editable; a rejection restores the value — show why through `error`. |
|
|
74
|
+
| `suggestions` | `readonly string[]` | — | Suggestions for free text (a combobox): a list under the box shows those containing the text; a pick goes into the field as typing would. Any text stays allowed; a value from a list only is `Select`. |
|
|
75
|
+
| `closeOnEscape` | `boolean` | `true` | Escape closes the `suggestions` list; the text and the focus stay in the field. |
|
|
76
|
+
| `closeOnOutsideClick` | `boolean` | `true` | A press outside the field and the `suggestions` list closes the list. |
|
|
68
77
|
| `labels` | `Partial<InputLabels>` | — | Built-in strings, see Labels. |
|
|
69
78
|
| `children` | `ReactNode` | — (required) | Usually `Input.Wrapper`. |
|
|
70
79
|
| `…rest` | `Omit<HTMLAttributes<HTMLDivElement>, "id" \| "children" \| "defaultValue" \| "defaultChecked" \| "onChange">` | — | `className`, `data-*` and the other attributes of the field frame `<div>` (field-root rule: `className`, `ref` and the rest → frame, `id` → control). |
|
|
@@ -83,7 +92,7 @@ The trailing side has a fixed CSS `order`, independent of JSX order: value · en
|
|
|
83
92
|
| Prop | Type | Default | Description |
|
|
84
93
|
|---|---|---|---|
|
|
85
94
|
| `onValueChange` | `(value: string) => void` | — | Called with the new string; native `onChange` still fires first. |
|
|
86
|
-
| `aria-describedby` | `string` | — | Merged with the hint/error ids from the root. |
|
|
95
|
+
| `aria-describedby` | `string` | — | Merged with the affix and hint/error ids from the root. |
|
|
87
96
|
| `required` | `boolean` | — | Overrides the root's `required` for the native input. |
|
|
88
97
|
| `…rest` | `Omit<InputHTMLAttributes<HTMLInputElement>, "size" \| "id">` | — | `value`, `defaultValue`, `onChange`, `type`, `disabled`, `readOnly`, `maxLength`, `placeholder`… |
|
|
89
98
|
|
|
@@ -97,21 +106,23 @@ The trailing side has a fixed CSS `order`, independent of JSX order: value · en
|
|
|
97
106
|
| `…rest` | `Omit<HTMLAttributes<HTMLSpanElement>, "children">` | — | `className` and the other attributes of the `<span>`. |
|
|
98
107
|
|
|
99
108
|
### Input.Affix
|
|
100
|
-
`ref` → `HTMLDivElement`. Tinted section flush with the edge (`aria-hidden
|
|
109
|
+
`ref` → `HTMLDivElement`. Tinted section flush with the edge (`aria-hidden`, its text describes the input); the wrapper drops its padding there.
|
|
101
110
|
|
|
102
111
|
| Prop | Type | Default | Description |
|
|
103
112
|
|---|---|---|---|
|
|
104
113
|
| `side` | `"start" \| "end"` | — (required) | Edge the section sits on. |
|
|
105
114
|
| `children` | `ReactNode` | — (required) | Static text: protocol, domain, country code. |
|
|
115
|
+
| `aria-label` | `string` | — | What a screen reader says instead of the text in the input's description. |
|
|
106
116
|
| `…rest` | `Omit<HTMLAttributes<HTMLDivElement>, "children">` | — | `className` and the other attributes of the `<div>`. |
|
|
107
117
|
|
|
108
118
|
### Input.InlineAffix
|
|
109
|
-
`ref` → `HTMLSpanElement`. Muted unit next to the value (`aria-hidden
|
|
119
|
+
`ref` → `HTMLSpanElement`. Muted unit next to the value (`aria-hidden`, its text describes the input) — the one place of the unit.
|
|
110
120
|
|
|
111
121
|
| Prop | Type | Default | Description |
|
|
112
122
|
|---|---|---|---|
|
|
113
123
|
| `side` | `"start" \| "end"` | — (required) | Side of the value. |
|
|
114
124
|
| `children` | `ReactNode` | — (required) | Short unit: `₽`, `%`, `кг`. |
|
|
125
|
+
| `aria-label` | `string` | — | The unit in words for a screen reader («рублей»), said instead of the sign in the input's description. |
|
|
115
126
|
| `…rest` | `Omit<HTMLAttributes<HTMLSpanElement>, "children">` | — | `className` and the other attributes of the `<span>`. |
|
|
116
127
|
|
|
117
128
|
### Input.ClearButton
|
|
@@ -181,6 +192,8 @@ Input has no `variant` or `tone`: there is one field treatment (fill, no visible
|
|
|
181
192
|
|---|---|---|---|
|
|
182
193
|
| `focusRing` | `false`: no focus ring; the field still switches to the focus fill, caret visible | A single search field where focus is obvious | `true` |
|
|
183
194
|
| `reserveSupportRow` | Empty support row of hint height under the field | Fields validated on the fly; neighbouring fields in one grid row | `false` |
|
|
195
|
+
| `closeOnOutsideClick` | `false`: a press beside the field keeps the `suggestions` list open | The person copies the value from elsewhere on the page | `true` |
|
|
196
|
+
| `closeOnEscape` | `false`: Escape leaves the `suggestions` list open | Rare: the list closes only by a pick, Tab or blur | `true` |
|
|
184
197
|
| `required` / `optional` | Red `*` / muted «необязательно» after the label | Mark the minority: required when most are optional, optional when most are required | `false` |
|
|
185
198
|
|
|
186
199
|
**Combinations**
|
|
@@ -202,6 +215,8 @@ Input has no `variant` or `tone`: there is one field treatment (fill, no visible
|
|
|
202
215
|
| read-only | native `readOnly` on `Input.Field` | no hover, default cursor |
|
|
203
216
|
| reserved support row | `reserveSupportRow` | `data-reserve="true"` on the support row |
|
|
204
217
|
| counter over limit | `current > max` | `data-invalid="true"` on `Input.Counter`; the current count rolls its digits to each new value |
|
|
218
|
+
| suggesting | `suggestions` + typing, ↓ / ↑ or a click in the field | `role="combobox"`, `aria-expanded="true"` and `aria-controls` on the input; the list fades in under the box (the floating panel's motion) with the matching part of each suggestion marked; the highlight is a fill (`data-highlighted`, `aria-selected`) named by `aria-activedescendant`; closes on a pick, Escape, Tab, blur or a press outside |
|
|
219
|
+
| select segment | a `Select` inside `Input.Wrapper` | `data-field-segment` on the select root, `data-segment="true"` on its trigger: a tinted section (`fill-subtle`) flush with the start edge, a stronger fill on hover and while open, its own inset focus ring; the box's ring stays for the text |
|
|
205
220
|
| strength | `strength` + the value of `Input.Field` | a stepped `ProgressBar` (4 cells, `aria-label` = `labels.strength`) under the field fills one cell after another, weak · easy · medium · hard, toned danger → warning → accent → success; the level word at the end of the support row flows in letter by letter and is announced (`aria-live`); the support row is reserved |
|
|
206
221
|
| saving | `onCommit` returned a pending Promise | `aria-busy="true"` on the input; a `Spinner` at the end of the field (`data-commit="pending"`); the field stays editable and focusable, neighbours are untouched; a live region says `labels.saving` |
|
|
207
222
|
| saved | the Promise resolved (or `onCommit` returned nothing) | the spinner cross-fades into a success check (`data-commit="saved"`) that fades out after a moment; the live region says `labels.saved` |
|
|
@@ -215,6 +230,8 @@ Inline save (`onCommit`): the value at focus is the committed one. Blur and Ente
|
|
|
215
230
|
|
|
216
231
|
Strength estimate (`strength`, replaced by `getStrength`): every trait adds a point on its own, in any order — lowercase, uppercase, digits, symbols, 10+ and 14+ characters; 0–2 points weak, 3 easy, 4 medium, 5+ hard, and under 8 characters at most easy. The meter follows `Input.Field` controlled or not.
|
|
217
232
|
|
|
233
|
+
Suggestions (`suggestions`): the list shows the suggestions that contain the typed text (case-insensitive), and nothing when the text already is the only match. A pick writes the suggestion into the input the way typing does, so `onValueChange` (and `onCommit` on blur) receive it; the text is never restricted to the list.
|
|
234
|
+
|
|
218
235
|
Controlled: `value` + `onChange` (or `onValueChange`) on `Input.Field`. Uncontrolled: `defaultValue`. Both are native input props.
|
|
219
236
|
|
|
220
237
|
## Layout & spacing
|
|
@@ -234,6 +251,10 @@ Controlled: `value` + `onChange` (or `onValueChange`) on `Input.Field`. Uncontro
|
|
|
234
251
|
| `Tab` | Focuses the field, then the segments at its end (clear, copy, reveal, actions). |
|
|
235
252
|
| `Enter` · `Space` | On a segment: runs its action (the clear button returns focus to the field). |
|
|
236
253
|
| `Enter` | With `onCommit`: saves a changed value; focus stays in the field. |
|
|
254
|
+
| `↓` · `↑` | With `suggestions`: opens the list on the first (last) suggestion; in the open list moves the highlight, wrapping. Focus stays in the field. |
|
|
255
|
+
| `Alt + ↓` | With `suggestions`: opens the list without highlighting. |
|
|
256
|
+
| `Enter` | With `suggestions`: puts the highlighted suggestion into the field and closes the list; with no highlight the typed text stays. |
|
|
257
|
+
| `Escape` | With `suggestions`: closes the open list, the text stays; a dialog around the field stays open. |
|
|
237
258
|
| `Escape` | With `onCommit`: puts the committed value back. |
|
|
238
259
|
|
|
239
260
|
### ARIA
|
|
@@ -241,7 +262,9 @@ Controlled: `value` + `onChange` (or `onValueChange`) on `Input.Field`. Uncontro
|
|
|
241
262
|
|
|
242
263
|
- `label` renders a `<label htmlFor>` bound to the input. Without `label`, set `aria-label` on `Input.Field`. A placeholder never replaces the label.
|
|
243
264
|
- Hint and error are linked through `aria-describedby`; the error replaces the hint and sets `aria-invalid="true"`.
|
|
244
|
-
- `Input.Icon
|
|
265
|
+
- `Input.Icon` is decorative (`aria-hidden`). `Input.Affix` and `Input.InlineAffix` are `aria-hidden` in the reading order but describe the input (`aria-describedby`, before the hint): the unit is heard with the field, so the label never repeats it. An `aria-label` on the affix is said instead of its text («рублей» for `₽`).
|
|
266
|
+
- `suggestions` make the input a `combobox` (`aria-autocomplete="list"`, `aria-expanded`, `aria-controls` on the open `listbox`, which the label names); the highlight is `aria-activedescendant`, so focus never leaves the field.
|
|
267
|
+
- A `Select` inside `Input.Wrapper` is the box's start segment: its trigger is named by the select root's `aria-label` and the picked value («Иконка: Звезда»).
|
|
245
268
|
- `Input.ClearButton` is a real button named by `labels.clear`, with `aria-controls` on the input.
|
|
246
269
|
- `Input.Action` and `Input.CopyButton` are buttons (or a link with `asChild`) named by their label, with a tooltip; copying announces `labels.copied` through a polite live region.
|
|
247
270
|
- `Input.RevealButton` is a toggle: a constant name `labels.reveal`, `aria-pressed` and `aria-controls` on the input; a pointer press keeps the focus in the field.
|
|
@@ -276,11 +299,15 @@ Controlled: `value` + `onChange` (or `onValueChange`) on `Input.Field`. Uncontro
|
|
|
276
299
|
| [states.tsx](examples/states.tsx) | A default field next to a disabled, a read-only and an invalid one with no message — `disabled`, `readOnly`, `invalid`. |
|
|
277
300
|
| [validation.tsx](examples/validation.tsx) | Live validation: an error shakes the field as it arrives, drops in and leaves as soon as the value is fixed; required and optional markers, a hint and a support row that does not shift — `required`, `optional`, `hint`, `error`, `reserveSupportRow`. |
|
|
278
301
|
| [with-icon.tsx](examples/with-icon.tsx) | A decorative icon at either end of the value — `Input.Icon`, `side`. |
|
|
279
|
-
| [affixes.tsx](examples/affixes.tsx) | A fixed prefix and suffix flush with the edges and a unit next to the value — `Input.Affix`, `Input.InlineAffix`. |
|
|
302
|
+
| [affixes.tsx](examples/affixes.tsx) | A fixed prefix and suffix flush with the edges and a unit next to the value, heard as a word by a screen reader — `Input.Affix`, `Input.InlineAffix`, `aria-label`. |
|
|
280
303
|
| [without-focus-ring.tsx](examples/without-focus-ring.tsx) | A single search field where the caret and the lighter fill show focus — `focusRing`. |
|
|
281
304
|
| [password-strength.tsx](examples/password-strength.tsx) | A new password with a show toggle and a meter that fills step by step and names the level beside the hint, and a service password measured by the company's own rule — `Input.RevealButton`, `strength`, `getStrength`. |
|
|
282
305
|
| [edge-actions.tsx](examples/edge-actions.tsx) | Actions at the field's end: a mail link with a tooltip beside copying the value, and a profile link on a read-only field — `Input.Action`, `Input.CopyButton`. |
|
|
283
306
|
| [inline-edit.tsx](examples/inline-edit.tsx) | Record card fields that save on blur and Enter, revert on Escape and show the server's refusal — `onCommit`, `error`. |
|
|
307
|
+
| [suggestions.tsx](examples/suggestions.tsx) | Where a returned order is kept: free text with the places already used as suggestions — `suggestions`. |
|
|
308
|
+
| [with-select.tsx](examples/with-select.tsx) | A menu section's icon and name as one field: an icon-only Select is the box's start segment — `Input.Wrapper`, `Select.Root`, `aria-label`. |
|
|
309
|
+
| [time.tsx](examples/time.tsx) | A call slot as a time of day: the browser's hour and minute segments, arrows step them, working hours only — `type`, `step`, `min`, `max`. |
|
|
310
|
+
| [dismiss.tsx](examples/dismiss.tsx) | A delivery city whose suggestions stay open on a press beside the field while the address is copied from a letter; Escape still closes them — `closeOnOutsideClick`, `closeOnEscape`. |
|
|
284
311
|
| [controlled.tsx](examples/controlled.tsx) | The parent owns the value: a clear button and a character counter follow it — `value`, `onValueChange`, `Input.ClearButton`, `Input.Counter`. |
|
|
285
312
|
| [in-form.tsx](examples/in-form.tsx) | Company details form: required fields checked on submit, each error leaves as its field is fixed and a saved form turns the button green; neighbours keep their bottoms aligned — `required`, `error`, `reserveSupportRow`. |
|
|
286
313
|
|
|
@@ -294,8 +321,11 @@ Controlled: `value` + `onChange` (or `onValueChange`) on `Input.Field`. Uncontro
|
|
|
294
321
|
- `tone="danger"` / `error` as a boolean → use `invalid` or an `error` message.
|
|
295
322
|
- A `Button` placed next to the field for «copy» or «call» → `Input.CopyButton` / `Input.Action` inside `Input.Wrapper`.
|
|
296
323
|
- Hand-written blur / Enter / Escape handlers and a `disabled` field while saving → `onCommit` returning the save Promise; the field stays editable.
|
|
324
|
+
- `<datalist>` with `list` on `Input.Field` → `suggestions` on `Input.Root`: the kit's list, keyboard and ARIA in both themes.
|
|
325
|
+
- A Select and an Input side by side for one value (an icon + a name) → the Select inside `Input.Wrapper`, named by its `aria-label`.
|
|
326
|
+
- A text field with a hand-made time mask, or a Datepicker for a time of day → `Input.Field type="time"`.
|
|
297
327
|
- Returning `false` from `onCommit` to refuse → reject the Promise (throw) and set `error`.
|
|
298
328
|
|
|
299
329
|
## Related
|
|
300
|
-
- **Built from:** [Label](../label/COMPONENT.md) (`label`), [Hint](../hint/COMPONENT.md) (`hint`, `error`), [Tooltip](../tooltip/COMPONENT.md) (`Input.Action`, `Input.CopyButton`), [Spinner](../spinner/COMPONENT.md) (`onCommit`), [ProgressBar](../progress-bar/COMPONENT.md) (`strength`), `Icon` (segments), [VisuallyHidden](../visually-hidden/COMPONENT.md)
|
|
330
|
+
- **Built from:** [Label](../label/COMPONENT.md) (`label`), [Hint](../hint/COMPONENT.md) (`hint`, `error`), [Tooltip](../tooltip/COMPONENT.md) (`Input.Action`, `Input.CopyButton`), [Spinner](../spinner/COMPONENT.md) (`onCommit`), [ProgressBar](../progress-bar/COMPONENT.md) (`strength`), `Icon` (segments), [VisuallyHidden](../visually-hidden/COMPONENT.md), [ScrollContainer](../scroll-container/COMPONENT.md) (the `suggestions` list)
|
|
301
331
|
- **See also:** [Textarea](../textarea/COMPONENT.md), [Select](../select/COMPONENT.md), [Datepicker](../datepicker/COMPONENT.md), [TagSelect](../tag-select/COMPONENT.md) — fields with the same contract
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** A fixed prefix and suffix flush with the edges and a unit next to the value — `Input.Affix`, `Input.InlineAffix`. */
|
|
1
|
+
/** A fixed prefix and suffix flush with the edges and a unit next to the value, heard as a word by a screen reader — `Input.Affix`, `Input.InlineAffix`, `aria-label`. */
|
|
2
2
|
import { Input } from "prime-ui-kit";
|
|
3
3
|
|
|
4
4
|
export default function InputAffixesExample() {
|
|
@@ -14,7 +14,9 @@ export default function InputAffixesExample() {
|
|
|
14
14
|
<Input.Root label="Сумма платежа">
|
|
15
15
|
<Input.Wrapper>
|
|
16
16
|
<Input.Field placeholder="0,00" inputMode="decimal" />
|
|
17
|
-
<Input.InlineAffix side="end"
|
|
17
|
+
<Input.InlineAffix side="end" aria-label="рублей">
|
|
18
|
+
₽
|
|
19
|
+
</Input.InlineAffix>
|
|
18
20
|
</Input.Wrapper>
|
|
19
21
|
</Input.Root>
|
|
20
22
|
</>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** A delivery city whose suggestions stay open on a press beside the field while the address is copied from a letter; Escape still closes them — `closeOnOutsideClick`, `closeOnEscape`. */
|
|
2
|
+
import { Input } from "prime-ui-kit";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
const CITIES = [
|
|
6
|
+
"Москва",
|
|
7
|
+
"Санкт-Петербург",
|
|
8
|
+
"Новосибирск",
|
|
9
|
+
"Екатеринбург",
|
|
10
|
+
"Казань",
|
|
11
|
+
"Нижний Новгород",
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
export default function InputDismissExample() {
|
|
15
|
+
const [city, setCity] = React.useState("");
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<Input.Root
|
|
19
|
+
label="Город доставки"
|
|
20
|
+
suggestions={CITIES}
|
|
21
|
+
closeOnOutsideClick={false}
|
|
22
|
+
closeOnEscape
|
|
23
|
+
>
|
|
24
|
+
<Input.Wrapper>
|
|
25
|
+
<Input.Field value={city} onValueChange={setCity} />
|
|
26
|
+
</Input.Wrapper>
|
|
27
|
+
</Input.Root>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Where a returned order is kept: free text with the places already used as suggestions — `suggestions`. */
|
|
2
|
+
import { Input } from "prime-ui-kit";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
const PLACES = [
|
|
6
|
+
"Склад на Складочной, стеллаж А",
|
|
7
|
+
"Склад на Складочной, зона возвратов",
|
|
8
|
+
"Пункт выдачи на Лесной",
|
|
9
|
+
"Сервисный центр на Нагатинской",
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
export default function InputSuggestionsExample() {
|
|
13
|
+
const [place, setPlace] = React.useState("");
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<Input.Root
|
|
17
|
+
label="Где хранится возврат"
|
|
18
|
+
hint="Выберите из прошлых мест или впишите новое"
|
|
19
|
+
suggestions={PLACES}
|
|
20
|
+
>
|
|
21
|
+
<Input.Wrapper>
|
|
22
|
+
<Input.Field value={place} onValueChange={setPlace} placeholder="Склад на Складочной" />
|
|
23
|
+
</Input.Wrapper>
|
|
24
|
+
</Input.Root>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** A call slot as a time of day: the browser's hour and minute segments, arrows step them, working hours only — `type`, `step`, `min`, `max`. */
|
|
2
|
+
import { Input } from "prime-ui-kit";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
const QUARTER_HOUR = 15 * 60;
|
|
6
|
+
|
|
7
|
+
export default function InputTimeExample() {
|
|
8
|
+
const [time, setTime] = React.useState("10:30");
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<Input.Root label="Время звонка" hint="С 09:00 до 18:00, шаг 15 минут">
|
|
12
|
+
<Input.Wrapper>
|
|
13
|
+
<Input.Field
|
|
14
|
+
type="time"
|
|
15
|
+
value={time}
|
|
16
|
+
onValueChange={setTime}
|
|
17
|
+
step={QUARTER_HOUR}
|
|
18
|
+
min="09:00"
|
|
19
|
+
max="18:00"
|
|
20
|
+
/>
|
|
21
|
+
</Input.Wrapper>
|
|
22
|
+
</Input.Root>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** A menu section's icon and name as one field: an icon-only Select is the box's start segment — `Input.Wrapper`, `Select.Root`, `aria-label`. */
|
|
2
|
+
import { Icon, type IconName, Input, Select } from "prime-ui-kit";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
const ICONS: { value: IconName; label: string }[] = [
|
|
6
|
+
{ value: "object.package", label: "Склад" },
|
|
7
|
+
{ value: "object.truck", label: "Доставка" },
|
|
8
|
+
{ value: "object.receipt", label: "Счета" },
|
|
9
|
+
{ value: "object.users", label: "Клиенты" },
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
export default function InputWithSelectExample() {
|
|
13
|
+
const [icon, setIcon] = React.useState<IconName>("object.truck");
|
|
14
|
+
const [name, setName] = React.useState("Отгрузки");
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<Input.Root label="Раздел в меню" hint="Иконка и название видны в боковой панели">
|
|
18
|
+
<Input.Wrapper>
|
|
19
|
+
<Select.Root
|
|
20
|
+
aria-label="Иконка раздела"
|
|
21
|
+
value={icon}
|
|
22
|
+
onValueChange={(next) => setIcon(next as IconName)}
|
|
23
|
+
>
|
|
24
|
+
<Select.Trigger>
|
|
25
|
+
<Select.TriggerIcon>
|
|
26
|
+
<Icon name={icon} />
|
|
27
|
+
</Select.TriggerIcon>
|
|
28
|
+
</Select.Trigger>
|
|
29
|
+
<Select.Content>
|
|
30
|
+
{ICONS.map((option) => (
|
|
31
|
+
<Select.Item key={option.value} value={option.value}>
|
|
32
|
+
<Select.ItemIcon>
|
|
33
|
+
<Icon name={option.value} />
|
|
34
|
+
</Select.ItemIcon>
|
|
35
|
+
{option.label}
|
|
36
|
+
</Select.Item>
|
|
37
|
+
))}
|
|
38
|
+
</Select.Content>
|
|
39
|
+
</Select.Root>
|
|
40
|
+
<Input.Field value={name} onValueChange={setName} />
|
|
41
|
+
</Input.Wrapper>
|
|
42
|
+
</Input.Root>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
@@ -31,17 +31,18 @@ import {
|
|
|
31
31
|
## Anatomy
|
|
32
32
|
```
|
|
33
33
|
Kanban.Root board: horizontal strip of columns, its own Dnd.Root if none above
|
|
34
|
+
├─ placeholder columns while `loading` and `columns` is empty: `loadingColumns` columns of Skeleton title and cards
|
|
34
35
|
└─ <ul> (Dnd.Sortable, axis x) the columns in `columnOrder`
|
|
35
36
|
└─ <li> column (from `columns`) no well with a header plate (`plain`) or a header line (`line`), or a `fill-muted` well (`well`)
|
|
36
37
|
├─ header title (`columnTitleAs`, h3) · count Badge (the stage color) · renderColumnActions(column) · [grip Dnd.Handle, a lock when `locked`]
|
|
37
38
|
│ ├─ description muted line under the title (`column.description`)
|
|
38
|
-
│ └─ meta the stage's figures (`renderColumnMeta(column)`)
|
|
39
|
+
│ └─ meta the stage's figures (`renderColumnMeta(column)`), a line per array entry
|
|
39
40
|
└─ <ul> (Dnd.Sortable) own vertical scroll; Skeleton cards while loading; EmptyPage (Dnd `empty`) when empty
|
|
40
41
|
└─ Kanban.Item <li> card from renderItem(item); with getItemHref an <a> covers it
|
|
41
42
|
├─ Kanban.ItemHeader optional top row over a faint hairline: key · priority (or a full-width Thumbnail cover)
|
|
42
43
|
├─ Kanban.ItemTitle title, up to three lines
|
|
43
44
|
├─ Kanban.ItemDescription muted meta line: id, due date
|
|
44
|
-
├─ Kanban.ItemBadges wrapping row of Badge
|
|
45
|
+
├─ Kanban.ItemBadges wrapping row of Badge (a Badge.Group)
|
|
45
46
|
└─ Kanban.ItemFooter row: Kanban.ItemCount … at the start, the assignee at the end
|
|
46
47
|
└─ Kanban.ItemCount icon + number
|
|
47
48
|
```
|
|
@@ -72,10 +73,11 @@ Generic `Kanban.Root<T>`; `ref` → `HTMLDivElement`. The board: a strip of colu
|
|
|
72
73
|
| `onItemOpen` | `(item: T, event: KanbanItemOpenEvent) => void` | — | Opens a card in place (a Drawer): a click, Enter or Space; never from a control inside it or after a drag. With `getItemHref` it gets plain clicks: `event.preventDefault()` routes in the app. |
|
|
73
74
|
| `columnTitleAs` | `"h2" \| "h3" \| "h4"` | `"h3"` | Heading level of the column titles in the page outline; the look does not change. |
|
|
74
75
|
| `renderColumnActions` | `(column: KanbanColumn) => ReactNode` | — | Trailing actions of a column header (add a card, a menu); kit controls inside take the `s` tier. |
|
|
75
|
-
| `renderColumnMeta` | `(column: KanbanColumn) => ReactNode` | — | The meta
|
|
76
|
+
| `renderColumnMeta` | `(column: KanbanColumn) => ReactNode \| readonly ReactNode[]` | — | The meta under a column's title and description: the stage's figures («12 сделок · 4,8 млн ₽»); an array is a line per entry, in order. Describes the column's list. |
|
|
76
77
|
| `canDrop` | `(id: string, columnId: string) => boolean \| string` | — | A workflow rule: refuses card `id` in column `columnId` — `false`, or a string that says why (spoken and shown under the carried card). The column turns `danger` before the release. |
|
|
77
78
|
| `variant` | `"plain" \| "line" \| "well"` | `"plain"` | No wells: `plain` — a filled header plate over the cards, `line` — a bare header over a faint line; cards stand on the host. `well`: every column is a filled well holding its cards (bounded queues). |
|
|
78
79
|
| `loading` | `boolean` | `false` | Columns show skeleton cards in the card geometry; the cards cross-fade in when it turns off. |
|
|
80
|
+
| `loadingColumns` | `number` | `3` | Placeholder columns a `loading` board draws while `columns` is empty (the stages are still loading); the columns fade in where they stood. |
|
|
79
81
|
| `disabled` | `boolean` | `false` | Read-only board: no drag, no keyboard moves, no column grips; `onItemOpen` and `getItemHref` still open cards. |
|
|
80
82
|
| `labels` | `Partial<KanbanLabels>` | — | Built-in strings, see Labels. |
|
|
81
83
|
| `…rest` | `Omit<HTMLAttributes<HTMLDivElement>, "children" \| "defaultValue">` | — | `className` (the board height), `aria-label` and the other div attributes. |
|
|
@@ -103,7 +105,7 @@ Generic `Kanban.Root<T>`; `ref` → `HTMLDivElement`. The board: a strip of colu
|
|
|
103
105
|
| `…rest` | `HTMLAttributes<HTMLSpanElement>` | — | `children`, `className` and the other span attributes. |
|
|
104
106
|
|
|
105
107
|
### Kanban.ItemBadges
|
|
106
|
-
`ref` → `HTMLDivElement`. A wrapping row of `Badge`s: labels, priority.
|
|
108
|
+
`ref` → `HTMLDivElement`. A wrapping row of `Badge`s (a `Badge.Group`): labels, priority.
|
|
107
109
|
|
|
108
110
|
| Prop | Type | Default | Description |
|
|
109
111
|
|---|---|---|---|
|
|
@@ -138,12 +140,13 @@ Do not wrap a `plain` board's columns in your own boxes, and do not put a `well`
|
|
|
138
140
|
|---|---|---|---|
|
|
139
141
|
| `limit` (column) | count reads `count / limit`, orange when full; a full column refuses cards from others with the reason `labels.full` | a WIP limit per status | — |
|
|
140
142
|
| `description` (column) | a muted caption line under the column title, one line with an ellipsis | the column's rule or meaning («Ответить в течение дня») | — |
|
|
141
|
-
| `renderColumnMeta` |
|
|
143
|
+
| `renderColumnMeta` | caption lines in tabular digits under the description: one, or a line per entry of an array, in order | the stage's figures («2 сделки · 576 000 ₽», «Средний чек 288 000 ₽») | — |
|
|
142
144
|
| `color` (column) | the count Badge in the stage's hue (soft) | telling stages apart, matching their color elsewhere; the title still names the column | — |
|
|
143
145
|
| `locked` (column) | a lock where the grip would be; the column holds its place, no column moves past it | system stages: the first «Новые», the last «Закрыто» | — |
|
|
144
146
|
| `reorderableColumns` | a ghost grip at the end of every column header, after the actions; the column is carried by it | the user arranges the stages | `false` |
|
|
145
147
|
| `canDrop` | the column turns `danger` under a refused card; a string reason is spoken and shown under the carried card; a keyboard move is announced as refused | workflow rules (a blocked ticket cannot be closed) | — |
|
|
146
148
|
| `loading` | skeleton cards in the card geometry, no counts | the first load or a refresh | `false` |
|
|
149
|
+
| `loadingColumns` | placeholder columns (a Skeleton title, Skeleton cards) while `loading` and `columns` is empty | the stages themselves come from the server | `3` |
|
|
147
150
|
| `disabled` | cards do not drag, Alt + arrows do nothing | read-only boards, no permission | `false` |
|
|
148
151
|
|
|
149
152
|
## States
|
|
@@ -158,12 +161,13 @@ Do not wrap a `plain` board's columns in your own boxes, and do not put a `well`
|
|
|
158
161
|
| full | count ≥ `limit` | `data-full` on the column, orange count `count / limit` |
|
|
159
162
|
| empty | no cards | a compact `EmptyPage` in Dnd's dashed drop slot at the top (`fill-strong` dashed line, no fill): «Нет карточек» and the hint «Перетащите сюда карточку»; a `disabled` board shows the text alone; the whole column stays a drop area |
|
|
160
163
|
| loading | `loading` | `aria-busy` and `data-loading` on the root; `Skeleton` cards; `Crossfade` swaps them for the cards |
|
|
164
|
+
| loading, no columns yet | `loading` with empty `columns` | an `aria-hidden` row of `loadingColumns` placeholder columns; the columns fade in where they stood (`enterMotion`) |
|
|
161
165
|
| disabled | `disabled` / `Kanban.Item disabled` | `data-disabled` on the root / card; no `aria-roledescription`, no shortcuts, no column grips |
|
|
162
166
|
| saving | `onValueChange` / `onColumnOrderChange` returned a pending promise | the card (column) stays in its new place with `aria-busy` and `data-pending`; a card's content quiets to 0.6 under a centred `Spinner`, a column's heading quiets and its grip is busy (Dnd); it does not move again until the answer |
|
|
163
167
|
| opens | `onItemOpen` / `getItemHref` | `data-clickable` (in place) or `data-link` (a link over the card, its focus ring drawn on the card); a card that opens lifts on hover on a read-only board too |
|
|
164
168
|
| refused save | the promise rejected | the card glides back to its old column and place (every card it pushed aside glides too), a column glides back in the strip; `labels.moveFailed` / `labels.columnMoveFailed` is announced |
|
|
165
169
|
|
|
166
|
-
Motion: the hover lift (shadow, the card rises above its neighbours) and the empty slot fade (`fast`, `standard`); a card dropped under the mouse lands straight into its hover lift (`data-dnd-hover`); the swap from skeletons is `Crossfade
|
|
170
|
+
Motion: the hover lift (shadow, the card rises above its neighbours) and the empty slot fade (`fast`, `standard`); a card dropped under the mouse lands straight into its hover lift (`data-dnd-hover`); the swap from skeletons is `Crossfade`, the columns that replace placeholder columns fade in; drag motion is Dnd's (lift, gap, glide, landing); a saving card or column quiets (`fast`, both ways) and its spinner fades in by itself; a refused save glides back (`base`, the list's FLIP glide), never jumps. Under `prefers-reduced-motion` all of it is off.
|
|
167
171
|
|
|
168
172
|
A card's own `onKeyDown` runs before the board's: `preventDefault()` in it keeps the move from happening. A controlled owner applies the new `value` (or `columnOrder`) once the request succeeds — in the promise chain, before the returned promise resolves — never before the answer.
|
|
169
173
|
|
|
@@ -228,10 +232,10 @@ A card's own `onKeyDown` runs before the board's: `preventDefault()` in it keeps
|
|
|
228
232
|
| [structure.tsx](examples/structure.tsx) | Cards take only the parts they need: a title alone, a meta line, labels, a footer with counts and an assignee — `Kanban.ItemTitle`, `Kanban.ItemDescription`, `Kanban.ItemBadges`, `Kanban.ItemFooter`, `Kanban.ItemCount`. |
|
|
229
233
|
| [cards.tsx](examples/cards.tsx) | Three kinds of cards on one board: a plain note, a ticket with a header row of key and priority, a design task with a cover — `Kanban.ItemHeader`, `Thumbnail`, `Kanban.ItemFooter`. |
|
|
230
234
|
| [limits.tsx](examples/limits.tsx) | A support queue with a work-in-progress limit and a workflow rule, its columns drawn as wells so each reads as a bounded queue: a full column and a blocked ticket are refused before the release with the reason under the card, from the keyboard too — `limit`, `canDrop`, `variant`. |
|
|
231
|
-
| [columns.tsx](examples/columns.tsx) | A sales pipeline whose stages move by the grip in their header while the first and the last stay locked; each stage carries its color on the count, a rule and its figures under the title, and is edited in a popover; the page keeps the order — `reorderableColumns`, `columnOrder`, `onColumnOrderChange`, `locked`, `color`, `description`, `renderColumnMeta`, `renderColumnActions`. |
|
|
235
|
+
| [columns.tsx](examples/columns.tsx) | A sales pipeline whose stages move by the grip in their header while the first and the last stay locked; each stage carries its color on the count, a rule and its figures in two lines under the title, and is edited in a popover; the page keeps the order — `reorderableColumns`, `columnOrder`, `onColumnOrderChange`, `locked`, `color`, `description`, `renderColumnMeta`, `renderColumnActions`. |
|
|
232
236
|
| [saving.tsx](examples/saving.tsx) | Every move is saved on the server: the card waits busy in its new place, and an invoice the server refuses to close glides back to its column — `onValueChange`. |
|
|
233
237
|
| [item-link.tsx](examples/item-link.tsx) | Tickets that open: each card is a link to its page, so ⌘-click or a middle click opens a new tab, a plain click, Enter or Space opens it in place, and a drag only moves it — `getItemHref`, `onItemOpen`. |
|
|
234
|
-
| [states.tsx](examples/states.tsx) | Loading draws skeleton cards in the card geometry; an empty column keeps its drop area and a closed order stays in place while the rest move; a read-only board drags nothing — `loading`, `disabled`. |
|
|
238
|
+
| [states.tsx](examples/states.tsx) | Loading draws skeleton cards in the card geometry, and placeholder columns before the columns arrive; an empty column keeps its drop area and a closed order stays in place while the rest move; a read-only board drags nothing — `loading`, `loadingColumns`, `disabled`. |
|
|
235
239
|
| [controlled.tsx](examples/controlled.tsx) | The placement lives in the parent: every move arrives with its source, target and position to save, and a button resets the board — `value`, `onValueChange`. |
|
|
236
240
|
| [narrow.tsx](examples/narrow.tsx) | On a phone-width screen a column takes 85% of the board and snaps into place, so the next one peeks; the strip scrolls inside the board, never the page — `Kanban.Root`, `columns`. |
|
|
237
241
|
| [screen.tsx](examples/screen.tsx) | A sales pipeline as a work screen: the title with the open deals and their sum, search, filter, the board / list switch and the one primary action in its top bar, a full-height Kanban of deals by stage with totals, moves saved on the server that can be refused, and the deal in a Drawer — `value`, `onValueChange`, `renderColumnMeta`, `onItemOpen`. |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** A sales pipeline whose stages move by the grip in their header while the first and the last stay locked; each stage carries its color on the count, a rule and its figures under the title, and is edited in a popover; the page keeps the order — `reorderableColumns`, `columnOrder`, `onColumnOrderChange`, `locked`, `color`, `description`, `renderColumnMeta`, `renderColumnActions`. */
|
|
1
|
+
/** A sales pipeline whose stages move by the grip in their header while the first and the last stay locked; each stage carries its color on the count, a rule and its figures in two lines under the title, and is edited in a popover; the page keeps the order — `reorderableColumns`, `columnOrder`, `onColumnOrderChange`, `locked`, `color`, `description`, `renderColumnMeta`, `renderColumnActions`. */
|
|
2
2
|
import {
|
|
3
3
|
Button,
|
|
4
4
|
ColorSwatches,
|
|
@@ -89,7 +89,9 @@ export default function KanbanColumnsExample() {
|
|
|
89
89
|
DEALS.filter((deal) => deal.id === id),
|
|
90
90
|
);
|
|
91
91
|
const sum = deals.reduce((total, deal) => total + deal.amount, 0);
|
|
92
|
-
|
|
92
|
+
const lines = [`${deals.length} ${dealsWord(deals.length)} · ${RUBLES.format(sum)}`];
|
|
93
|
+
if (deals.length > 0) lines.push(`Средний чек ${RUBLES.format(sum / deals.length)}`);
|
|
94
|
+
return lines;
|
|
93
95
|
};
|
|
94
96
|
|
|
95
97
|
return (
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Loading draws skeleton cards in the card geometry; an empty column keeps its drop area and a closed order stays in place while the rest move; a read-only board drags nothing — `loading`, `disabled`. */
|
|
1
|
+
/** Loading draws skeleton cards in the card geometry, and placeholder columns before the columns arrive; an empty column keeps its drop area and a closed order stays in place while the rest move; a read-only board drags nothing — `loading`, `loadingColumns`, `disabled`. */
|
|
2
2
|
import { Badge, Kanban, Typography } from "prime-ui-kit";
|
|
3
3
|
|
|
4
4
|
type Order = { id: string; title: string; total: string; closed?: boolean };
|
|
@@ -24,9 +24,10 @@ const PLACEMENT = {
|
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
const STATES = [
|
|
27
|
-
{ caption: "loading", loading: true, disabled: false },
|
|
28
|
-
{ caption: "
|
|
29
|
-
{ caption: "disabled", loading: false, disabled:
|
|
27
|
+
{ caption: "loading · columns=[]", columns: [], loading: true, disabled: false },
|
|
28
|
+
{ caption: "loading", columns: COLUMNS, loading: true, disabled: false },
|
|
29
|
+
{ caption: "Kanban.Item disabled", columns: COLUMNS, loading: false, disabled: false },
|
|
30
|
+
{ caption: "disabled", columns: COLUMNS, loading: false, disabled: true },
|
|
30
31
|
];
|
|
31
32
|
|
|
32
33
|
export default function KanbanStatesExample() {
|
|
@@ -35,7 +36,8 @@ export default function KanbanStatesExample() {
|
|
|
35
36
|
<div>
|
|
36
37
|
<Kanban.Root
|
|
37
38
|
aria-label={`Заказы склада: ${state.caption}`}
|
|
38
|
-
columns={
|
|
39
|
+
columns={state.columns}
|
|
40
|
+
loadingColumns={COLUMNS.length}
|
|
39
41
|
items={ORDERS}
|
|
40
42
|
getId={(order) => order.id}
|
|
41
43
|
getLabel={(order) => order.id}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
- The list side of a list + detail screen (`Page.Split` → `Page.List`): tickets, letters, invoices worked through one by one; the open one is `current`.
|
|
10
10
|
- A short column of records that each open something — a page (`href`, `asChild` with a router link) or a detail pane (`onClick`).
|
|
11
11
|
- A short list the team puts in order (funnel stages, queues, priorities) — `onReorder`, a `value` per item.
|
|
12
|
+
- Rows with controls of their own beside the row that opens the record (a stage shown on the board, a ⋯ menu) — `List.ItemActions`.
|
|
12
13
|
|
|
13
14
|
## When not to use
|
|
14
15
|
- App sections → use [Sidebar](../../layout/sidebar/COMPONENT.md) (wide) and [BottomNav](../../layout/bottom-nav/COMPONENT.md) (phone).
|
|
@@ -27,12 +28,14 @@ import { List } from "prime-ui-kit";
|
|
|
27
28
|
```
|
|
28
29
|
List.Root <ul aria-label>; size (else the host tier) → row tier; onReorder → Dnd.Sortable <ul>
|
|
29
30
|
└─ List.Item <li> with a full-width <button> | <a href> | asChild element; current, disabled
|
|
30
|
-
│ value; with onReorder: Dnd.Handle (busy while saving)
|
|
31
|
-
├─ List.ItemIcon leading Avatar.Root / Icon (aria-hidden); after the text — a trailing glyph
|
|
31
|
+
│ value; with onReorder: Dnd.Handle (busy while saving) at the row's end, after its actions
|
|
32
|
+
├─ List.ItemIcon leading Avatar.Root / Icon / Badge.Dot (aria-hidden); after the text — a trailing glyph
|
|
32
33
|
├─ List.ItemText primary line, truncates
|
|
33
34
|
├─ List.ItemDescription second line, muted, truncates
|
|
34
35
|
├─ List.ItemMeta trailing small text (time, date, amount)
|
|
35
|
-
|
|
36
|
+
├─ List.ItemCount trailing Badge, one tier down
|
|
37
|
+
└─ List.ItemActions the row's own controls (Switch, Dropdown): beside the row element, one tier down;
|
|
38
|
+
side="end" after it, side="start" before it (a reorderable row's toggle)
|
|
36
39
|
```
|
|
37
40
|
|
|
38
41
|
## API
|
|
@@ -45,7 +48,7 @@ List.Root <ul aria-label>; size (else the host tier) → row t
|
|
|
45
48
|
| Prop | Type | Default | Description |
|
|
46
49
|
|---|---|---|---|
|
|
47
50
|
| `size` | `"xs" \| "s" \| "m" \| "l" \| "xl"` | host tier, else `"m"` | Row tier: height, text, icon and avatar size; counts take one tier down. Without it the tier of its host, else `m`. |
|
|
48
|
-
| `onReorder` | `(id: string, beforeId: string \| null) => void \| Promise<void>` | — | Rows reorder by
|
|
51
|
+
| `onReorder` | `(id: string, beforeId: string \| null) => void \| Promise<void>` | — | Rows reorder by an end handle (drag, touch hold, Alt + ↑ / ↓): item `value` `id` goes before `beforeId` (`null` = last). Apply with `moveBefore`; a returned promise keeps the row busy, a rejection moves it back. |
|
|
49
52
|
| `labels` | `Partial<ListLabels>` | — | Built-in strings, see Labels. |
|
|
50
53
|
| `aria-label` | `string` | — | Name of the list for screen readers («Обращения»), when no visible heading names it. |
|
|
51
54
|
| `children` | `ReactNode` | — | `List.Item`s. |
|
|
@@ -64,15 +67,15 @@ List.Root <ul aria-label>; size (else the host tier) → row t
|
|
|
64
67
|
| `rel` | `string` | — | Relationship of the `<a>` (`href` only). |
|
|
65
68
|
| `asChild` | `boolean` | `false` | Renders the single child (a router link) as the row; its children are the content. |
|
|
66
69
|
| `disabled` | `boolean` | `false` | Muted and out of the Tab order: a disabled button, or `aria-disabled` and no navigation on a link. |
|
|
67
|
-
| `children` | `ReactNode` | — | `List.ItemIcon`, `List.ItemText`, `List.ItemDescription`, `List.ItemMeta`, `List.ItemCount`; loose text becomes the text line. |
|
|
70
|
+
| `children` | `ReactNode` | — | `List.ItemIcon`, `List.ItemText`, `List.ItemDescription`, `List.ItemMeta`, `List.ItemCount`, `List.ItemActions`; loose text becomes the text line. |
|
|
68
71
|
| `…rest` | `ButtonHTMLAttributes<HTMLButtonElement>` | — | `className` and native attributes of the row element (`target`, `rel` on a link). |
|
|
69
72
|
|
|
70
73
|
### List.ItemIcon
|
|
71
|
-
`ref` → `HTMLSpanElement`. Leading `aria-hidden` slot: an `Avatar.Root` (without its own `size` it takes the row tier),
|
|
74
|
+
`ref` → `HTMLSpanElement`. Leading `aria-hidden` slot: an `Avatar.Root` (without its own `size` it takes the row tier), an `Icon` or a colour `Badge.Dot` in an icon-wide box. After the text it is a trailing glyph.
|
|
72
75
|
|
|
73
76
|
| Prop | Type | Default | Description |
|
|
74
77
|
|---|---|---|---|
|
|
75
|
-
| `children` | `ReactNode` | — (required) | An `Avatar.Root` or
|
|
78
|
+
| `children` | `ReactNode` | — (required) | An `Avatar.Root`, an `Icon`, or a `Badge.Dot` with `color` (a stage, a project): the large dot. |
|
|
76
79
|
| `…rest` | `Omit<HTMLAttributes<HTMLSpanElement>, "children">` | — | `className` and the other `span` attributes. |
|
|
77
80
|
|
|
78
81
|
### List.ItemText
|
|
@@ -105,6 +108,14 @@ List.Root <ul aria-label>; size (else the host tier) → row t
|
|
|
105
108
|
| `children` | `ReactNode` | — (required) | The number or a short status. |
|
|
106
109
|
| `…rest` | `Omit<HTMLAttributes<HTMLSpanElement>, "children" \| "color">` | — | `className` and the other `span` attributes. |
|
|
107
110
|
|
|
111
|
+
### List.ItemActions
|
|
112
|
+
`ref` → `HTMLDivElement`. The row's own controls (a `Switch`, a `Dropdown` with its icon `Button`) at its end or its start, beside the row element and never inside it, one tier below the row.
|
|
113
|
+
|
|
114
|
+
| Prop | Type | Default | Description |
|
|
115
|
+
|---|---|---|---|
|
|
116
|
+
| `side` | `"start" \| "end"` | `"end"` | `"end"` after the row element: a plain settings row, label first. `"start"` before it, in the DOM too: the on / off of a reorderable row reads first and the grip keeps the end. |
|
|
117
|
+
| `…rest` | `HTMLAttributes<HTMLDivElement>` | — | `children` (the controls, each named on its own), `className` and the other `div` attributes. |
|
|
118
|
+
|
|
108
119
|
## Variants
|
|
109
120
|
|
|
110
121
|
### size
|
|
@@ -128,6 +139,12 @@ A second line or an avatar makes a row taller than the one-line height; the vert
|
|
|
128
139
|
### List.ItemCount color
|
|
129
140
|
`gray` (default) for a plain number; a hue for what needs attention (`blue` for unread). Any palette hue.
|
|
130
141
|
|
|
142
|
+
### List.ItemActions side
|
|
143
|
+
| Value | Looks like | Use when | Default |
|
|
144
|
+
|---|---|---|---|
|
|
145
|
+
| `end` | the controls after the row's text and trail | a plain row: the label first, the toggle or ⋯ menu at the end | yes |
|
|
146
|
+
| `start` | the controls before the row's icon and text, first in the DOM and the Tab order | a reorderable row's on / off: the state reads first, the grip keeps the end edge | |
|
|
147
|
+
|
|
131
148
|
## States
|
|
132
149
|
| State | Driven by | DOM |
|
|
133
150
|
|---|---|---|
|
|
@@ -137,8 +154,9 @@ A second line or an avatar makes a row taller than the one-line height; the vert
|
|
|
137
154
|
| current | `current` | `aria-current="true"` (button) or `"page"` (link); `fill-muted-hover` fill, primary text |
|
|
138
155
|
| focus-visible | keyboard | the focus ring inside the row edge |
|
|
139
156
|
| disabled | `disabled` | `disabled` on a button; `aria-disabled="true"`, no `href` on a link, `tabindex="-1"` on an `asChild` element; `data-disabled="true"`; disabled text; in a reorderable list the handle is disabled and the row holds its place |
|
|
140
|
-
| reorderable | `onReorder` | `data-reorderable="true"` on the root (a `Dnd.Sortable` `<ul>`); each `<li>` is a sortable item (`data-dnd-item` = `value`) with a
|
|
157
|
+
| reorderable | `onReorder` | `data-reorderable="true"` on the root (a `Dnd.Sortable` `<ul>`); each `<li>` is a sortable item (`data-dnd-item` = `value`) with a `Dnd.Handle` at its end; drag by the handle (a touch hold on touch), Alt + ↑ / ↓ from the handle or the row; a lifted row leaves a dashed gap in the row's radius, its neighbours glide, the copy (handle and row, in the row's padding and radius) follows the pointer and sets down in its new place without a jump |
|
|
141
158
|
| saving a move | `onReorder` returned a pending promise | the row stays in its new place, `aria-busy="true"`, the handle is busy (Dnd: a spinner in the disabled grip), the row content dims to 0.6 (`--prime-motion-fade-fast` both ways); a rejection glides the row back |
|
|
159
|
+
| own controls | `List.ItemActions` in the item | the row element and the controls stand in one frame that takes the hover, press and current fills for both; the element itself stays clear; `side="start"` controls come before the element in the frame, `data-side` on the actions |
|
|
142
160
|
| rows come and go | the data adds or takes out a `List.Item` (`value`, else its key) | a new row rises in once (`enterMotion`), a removed one closes up in place (`listPresence`); the first render stays still |
|
|
143
161
|
|
|
144
162
|
Root attributes: `data-size`, `role="list"` (the list style reset would drop the list semantics in WebKit). The fill changes on `background-color` with `--prime-motion-fade-xfast` both ways (a fade: reduced motion keeps it).
|
|
@@ -149,7 +167,10 @@ Root attributes: `data-size`, `role="list"` (the list style reset would drop the
|
|
|
149
167
|
- Row: leading icon · body (text over description) · trail. Beside two lines the trail stacks — meta on top, count below; beside one line they sit in a row.
|
|
150
168
|
- Text and description truncate with an ellipsis; give a long `List.ItemText` a `title` with the full text.
|
|
151
169
|
- On a coarse pointer a row is at least the touch target (44px).
|
|
152
|
-
- Reorderable (`onReorder`): an `xs` handle box
|
|
170
|
+
- Reorderable (`onReorder`): an `xs` handle box ends each row, 4px after the row element and its `List.ItemActions` (inline-end: on the left in RTL); the row keeps its own padding and fill, so every row's handle stands on one line.
|
|
171
|
+
- `List.ItemActions`: the controls one tier below the row, 4px apart, as far from the row's edge as from its top and bottom — the end edge, or the start edge with `side="start"`, where the row's own padding then parts the controls from its icon or text.
|
|
172
|
+
- One toggle place per row: a plain row ends with its toggle (label first); a reorderable row leads with it (`side="start"`) and ends with the grip — never a toggle between the label and the grip.
|
|
173
|
+
- A colour dot in `List.ItemIcon` is the large `Badge.Dot` (8px) centred in an icon-wide box, so the text lines up with the rows that carry icons.
|
|
153
174
|
|
|
154
175
|
## Accessibility
|
|
155
176
|
|
|
@@ -169,7 +190,8 @@ Root attributes: `data-size`, `role="list"` (the list style reset would drop the
|
|
|
169
190
|
- The open record: `aria-current="true"` on a button (a selection inside the screen), `aria-current="page"` on a link (`href`, `asChild`) — the link points at the current URL. A router link may set it itself.
|
|
170
191
|
- The row's name is its text, description, meta and count, in that order; the leading icon or avatar is `aria-hidden` — the text names the row.
|
|
171
192
|
- Arrow keys do not move between rows: rows are ordinary Tab stops, as in the Sidebar.
|
|
172
|
-
-
|
|
193
|
+
- `List.ItemActions` stand beside the row element, never inside it: the row's name stays its text. Tab follows the row as it reads: the `side="start"` controls, the row, the `side="end"` controls.
|
|
194
|
+
- With `onReorder` each row gets a handle named «Переместить {label}» (`labels.reorderItem`, `{label}` is the row's text line) and `aria-keyshortcuts`, last in the row: Tab reaches the row with its `List.ItemActions`, then the handle; the new place is announced politely («Новый лид: позиция 2 из 5»), a row being saved is `aria-busy`.
|
|
173
195
|
|
|
174
196
|
### Labels
|
|
175
197
|
<!-- Generated from api.ts by `bun run docs:build`. Edit api.ts, not this section. -->
|
|
@@ -184,10 +206,11 @@ Root attributes: `data-size`, `role="list"` (the list style reset would drop the
|
|
|
184
206
|
| [overview.tsx](examples/overview.tsx) | A support ticket queue with the client, the last update and the open count; a click opens a ticket and makes it the current row — `current`, `onClick`. |
|
|
185
207
|
| [sizes.tsx](examples/sizes.tsx) | Every size tier: row height, text, icon and count follow the tier — `size`. |
|
|
186
208
|
| [states.tsx](examples/states.tsx) | The open record, a regular row and an unavailable one — `current`, `disabled`. |
|
|
187
|
-
| [with-icon.tsx](examples/with-icon.tsx) | Leading avatars of clients and
|
|
209
|
+
| [with-icon.tsx](examples/with-icon.tsx) | Leading avatars of clients, icons of queues and colour dots of projects; an avatar takes the row tier, a dot stands in an icon-wide box — `List.ItemIcon`, `Badge.Dot`. |
|
|
188
210
|
| [overflow.tsx](examples/overflow.tsx) | A long inbox in a pane of a fixed height: the list scrolls in a ScrollContainer and long names and previews truncate — `List.ItemText`, `List.ItemDescription`. |
|
|
189
211
|
| [as-link.tsx](examples/as-link.tsx) | Rows as links to the invoice pages: a plain link, or your router link through the slot — `href`, `asChild`. |
|
|
190
|
-
| [reorder.tsx](examples/reorder.tsx) | Funnel stages in the team's order: a stage moves by its handle or Alt+arrows and is saved on the server — `onReorder`, `value`. |
|
|
212
|
+
| [reorder.tsx](examples/reorder.tsx) | Funnel stages in the team's order: a stage moves by its handle or Alt+arrows and is saved on the server; its switch leads the row, the grip ends it — `onReorder`, `value`, `side`. |
|
|
213
|
+
| [row-controls.tsx](examples/row-controls.tsx) | Funnel stages with their own controls: a colour dot names the stage's hue, a switch shows it on the board and a menu edits it, all beside the row that opens the stage — `List.ItemActions`, `Badge.Dot`. |
|
|
191
214
|
|
|
192
215
|
## Mistakes
|
|
193
216
|
- Rows hand-drawn from `Button.Root variant="ghost"` with a `soft` current → `List.Item current`.
|
|
@@ -197,7 +220,10 @@ Root attributes: `data-size`, `role="list"` (the list style reset would drop the
|
|
|
197
220
|
- A long list without a scroller → wrap it in `ScrollContainer` inside the pane.
|
|
198
221
|
- Up / down buttons on rows or a hand-made `Dnd.Sortable` of rows to order them → `List.Root onReorder` with a `value` on every item.
|
|
199
222
|
- `onReorder` that rolls the order back itself on an error → return the promise; a rejection moves the row back.
|
|
223
|
+
- A Switch or a menu button put inside a row, or a row rebuilt as a `div` to hold one → `List.ItemActions` beside the row element.
|
|
224
|
+
- A Switch at the end of a reorderable row, between its label and the grip → `List.ItemActions side="start"`; the grip keeps the end.
|
|
225
|
+
- A hollow `Icon` circle or a hand-drawn dot as a stage colour → `Badge.Dot color` in `List.ItemIcon`.
|
|
200
226
|
|
|
201
227
|
## Related
|
|
202
|
-
- **Built from:** [Badge](../badge/COMPONENT.md) (`List.ItemCount`), [Avatar](../avatar/COMPONENT.md) (`List.ItemIcon` gives an avatar the row tier), [Dnd](../dnd/COMPONENT.md) (`onReorder`: `Dnd.Sortable`, the busy `Dnd.Handle`)
|
|
228
|
+
- **Built from:** [Badge](../badge/COMPONENT.md) (`List.ItemCount`; `Badge.Dot` in `List.ItemIcon`), [Avatar](../avatar/COMPONENT.md) (`List.ItemIcon` gives an avatar the row tier), [Dnd](../dnd/COMPONENT.md) (`onReorder`: `Dnd.Sortable`, the busy `Dnd.Handle`)
|
|
203
229
|
- **See also:** [Page](../../layout/page/COMPONENT.md), [Sidebar](../../layout/sidebar/COMPONENT.md), [Tabs](../tabs/COMPONENT.md), [DataTable](../data-table/COMPONENT.md), [Card](../card/COMPONENT.md)
|