@capra/core 1.12.0 → 1.14.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/dist/index.cjs +781 -72
- package/dist/index.d.cts +342 -47
- package/dist/index.d.mts +342 -47
- package/dist/index.mjs +780 -77
- package/dist/style.css +647 -93
- package/docs/core-autocompletefield--usage.md +1 -1
- package/docs/core-badge--design.md +10 -2
- package/docs/core-badge--usage.md +7 -1
- package/docs/core-checkbox--usage.md +1 -0
- package/docs/core-selectfield--design.md +66 -0
- package/docs/core-selectfield--usage.md +145 -0
- package/docs/core-table--design.md +17 -0
- package/docs/core-table--usage.md +198 -0
- package/docs/core-togglebuttongroup--design.md +89 -0
- package/docs/core-togglebuttongroup--usage.md +113 -0
- package/docs/history-changelogs-capra-core--docs.md +31 -0
- package/docs/index.md +6 -0
- package/package.json +3 -3
|
@@ -5,7 +5,7 @@ AutocompleteField combines a text field with a suggestions list. Consumers can s
|
|
|
5
5
|
| Prop | Type | Required | Default | Description |
|
|
6
6
|
| --- | --- | --- | --- | --- |
|
|
7
7
|
| `canClear` | `boolean` | No | `--` | Whether to show a clear button for the input. |
|
|
8
|
-
| `children` | `
|
|
8
|
+
| `children` | `ChildrenOrFunction` | No | `--` | The children to render in the dropdown. |
|
|
9
9
|
| `itemFilter` | `(textValue: string, inputValue: string) => boolean` | No | `--` | A function that filters the items based on the input value. |
|
|
10
10
|
| `onChange` | `(value: string) => void` | No | `--` | A callback function that is called when the input value changes. |
|
|
11
11
|
| `onOpenChange` | `(isOpen: boolean) => void` | No | `--` | A callback function that is called when the dropdown is opened or closed. |
|
|
@@ -16,6 +16,10 @@ Badges support `md` and `sm` sizes. Use `sm` when composing with smaller control
|
|
|
16
16
|
|
|
17
17
|
Use a dot to show notifications without a count.
|
|
18
18
|
|
|
19
|
+
### Decorative
|
|
20
|
+
|
|
21
|
+
Use `decorative` when the badge is purely visual and its meaning is already conveyed nearby, such as adjacent text. Decorative badges are removed from the accessibility tree and must not be combined with `aria-label` or `aria-labelledby`.
|
|
22
|
+
|
|
19
23
|
### Appearance
|
|
20
24
|
|
|
21
25
|
Badges can be colored based on their status. The `attention` appearance is the general purpose color used for numbered badges and does not necessarily communicate severity.
|
|
@@ -49,6 +53,7 @@ Do:
|
|
|
49
53
|
* Do cap displayed values at 99 with a "99+" label.
|
|
50
54
|
* Do use the dot variant for simple presence indicators.
|
|
51
55
|
* Do associate interactive anchors with badge count context using `aria-describedby`.
|
|
56
|
+
* Do use `decorative` when the badge is redundant with nearby text or another accessible name.
|
|
52
57
|
|
|
53
58
|
Don't:
|
|
54
59
|
|
|
@@ -57,12 +62,15 @@ Don't:
|
|
|
57
62
|
* Don't place Badge on non-interactive decorative elements.
|
|
58
63
|
* Don't use Badge colors as the only method to communicate status.
|
|
59
64
|
* Don't include badge count context in the anchor's `aria-label`.
|
|
65
|
+
* Don't combine `decorative` with `aria-label` or `aria-labelledby`.
|
|
60
66
|
|
|
61
67
|
## Accessibility
|
|
62
68
|
|
|
63
|
-
*
|
|
69
|
+
* Named badges require `aria-label` or `aria-labelledby` that describes the count or status (e.g., "76 unread").
|
|
70
|
+
* Counter badges use `role="status"` so naming attributes are valid and the visible count is not presentational. Dot badges use `role="img"` because they have no text.
|
|
71
|
+
* Use `decorative` when the badge is purely presentational and the meaning is already available to assistive technology. Decorative badges omit the role and accessible name and set `aria-hidden`. Do not combine `decorative` with `aria-label` or `aria-labelledby`.
|
|
64
72
|
* When composing with interactive controls such as `Button` or `IconButton`, set `aria-describedby` on the anchor to the badge's `id`. Keep the anchor's accessible name limited to the control action or label (e.g., "Messages"), not the badge content.
|
|
65
|
-
* Use `aria-live="polite"` on
|
|
73
|
+
* Counter badges are a polite live region via `role="status"`. Use `aria-live="polite"` on a parent only when you need to announce grouped updates beyond the badge itself.
|
|
66
74
|
|
|
67
75
|
## Content
|
|
68
76
|
|
|
@@ -13,4 +13,10 @@ Badges display an indicator/counter on an associated component.
|
|
|
13
13
|
| `className` | `never` | No | `--` | Use `FORCE__className` instead. |
|
|
14
14
|
| `style` | `never` | No | `--` | Inline styles are not supported; use component props or `FORCE__className`. |
|
|
15
15
|
| `FORCE__className` | `string` | No | `--` | 🚨 This prop is meant to be an escape hatch. 🚨<br><br>If the desired style cannot be achieved using component props, use this as a last resort. The inner workings of Capra components are implementation details and this escape hatch gives one access to those implementation details. We cannot make any guarantees that styles will applied correctly across version updates. Please use it responsibly.<br><br>Add a CSS class to the component. |
|
|
16
|
-
| `aria-live` | `React.AriaAttributes['aria-live']` | No | `--` | `--` |
|
|
16
|
+
| `aria-live` | `React.AriaAttributes['aria-live']` | No | `--` | `--` |
|
|
17
|
+
|
|
18
|
+
## Accessibility
|
|
19
|
+
|
|
20
|
+
Named badges require `aria-label` or `aria-labelledby`. Counter badges expose `role="status"` so the visible count is not treated as presentational. Dot badges expose `role="img"` because they have no text.
|
|
21
|
+
|
|
22
|
+
Use `decorative` when the badge is purely presentational and the meaning is already conveyed nearby. Decorative badges omit the role and accessible name and set `aria-hidden`. Do not combine `decorative` with `aria-label` or `aria-labelledby`.
|
|
@@ -8,6 +8,7 @@ Checkbox component for capturing boolean user input. Supports controlled and unc
|
|
|
8
8
|
| `checked` | `boolean` | No | `--` | Controlled checked state. Use with onChange for controlled component. |
|
|
9
9
|
| `defaultChecked` | `boolean` | No | `--` | Default checked state for uncontrolled component. |
|
|
10
10
|
| `children` | `string \| React.ReactNode` | No | `--` | The label for the checkbox. |
|
|
11
|
+
| `slot` | `string \| null` | No | `--` | Slot name for React Aria context wiring (e.g. "selection" in Table). |
|
|
11
12
|
| `className` | `never` | No | `--` | Use `FORCE__className` instead. |
|
|
12
13
|
| `style` | `never` | No | `--` | Inline styles are not supported; use component props or `FORCE__className`. |
|
|
13
14
|
| `FORCE__className` | `string` | No | `--` | 🚨 This prop is meant to be an escape hatch. 🚨<br><br>If the desired style cannot be achieved using component props, use this as a last resort. The inner workings of Capra components are implementation details and this escape hatch gives one access to those implementation details. We cannot make any guarantees that styles will applied correctly across version updates. Please use it responsibly.<br><br>Add a CSS class to the component. |
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Core/SelectField - Design
|
|
2
|
+
|
|
3
|
+
## Anatomy
|
|
4
|
+
|
|
5
|
+
1. **Label**: describes the field and carries the required marker when needed.
|
|
6
|
+
2. **Trigger**: matches Capra text-input sizing and state treatments.
|
|
7
|
+
3. **Popover**: contains the option list and optional search field.
|
|
8
|
+
4. **ListBox items**: support single-select rows or multi-select rows with checkmarks.
|
|
9
|
+
5. **Sections**: optional grouped options with a non-selectable header label.
|
|
10
|
+
|
|
11
|
+
## Properties
|
|
12
|
+
|
|
13
|
+
### Layout
|
|
14
|
+
|
|
15
|
+
Use `layout="vertical"` by default. Use `layout="horizontal"` only when space is constrained.
|
|
16
|
+
|
|
17
|
+
### Multiple Selection
|
|
18
|
+
|
|
19
|
+
For multi-select fields, selected options appear as removable tags inside the trigger. Tags wrap onto additional lines when they exceed the field width, and selected rows show a checkmark in the popover.
|
|
20
|
+
|
|
21
|
+
### Search
|
|
22
|
+
|
|
23
|
+
Enable `canSearch` for longer option lists. Search filters the visible options inside the popover without changing the trigger layout. When nothing matches, the popover shows "No results" so the list never appears empty.
|
|
24
|
+
|
|
25
|
+
### Sections
|
|
26
|
+
|
|
27
|
+
Larger option lists can be organized into smaller groups using sections. Each section has a header to label the grouping. Pass `items` with nested `children` to build sections from data, or wrap static options in `SelectField.Section` with a `SelectField.Header`.
|
|
28
|
+
|
|
29
|
+
## States
|
|
30
|
+
|
|
31
|
+
* Match the standard input states: default, hover, focus, disabled, placeholder, warning, and danger.
|
|
32
|
+
* `appearance="danger"` drives the invalid visual state and helper text treatment.
|
|
33
|
+
* Small and medium sizes align with `TextInput`.
|
|
34
|
+
|
|
35
|
+
## Behavior
|
|
36
|
+
|
|
37
|
+
* Use `SelectField` for fixed option sets, especially when the option count is greater than about five.
|
|
38
|
+
* Prefer `RadioGroup` when the user should see all mutually exclusive choices at once.
|
|
39
|
+
* Prefer `AutocompleteField` when freeform typing or custom values are required.
|
|
40
|
+
|
|
41
|
+
## Accessibility
|
|
42
|
+
|
|
43
|
+
### Keyboard interaction
|
|
44
|
+
|
|
45
|
+
| Key | Function |
|
|
46
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
47
|
+
| `Tab` / `Shift + Tab` | Moves focus into the multiselect TagGroup (first selected tag) or the open button, then to the next or previous focusable element. Remove buttons are not sequential tab stops. If the option list is open, these keys are ignored. |
|
|
48
|
+
| `Left Arrow` / `Right Arrow` | In multiple-select mode with the TagGroup focused: moves focus between selected tags. |
|
|
49
|
+
| `Delete` / `Backspace` | In multiple-select mode with a selected tag focused: removes that tag. |
|
|
50
|
+
| `Down Arrow` / `Up Arrow` | If the option list is closed and the open button has focus: opens the list and moves focus to the selected item (the first selected item in multiple-select mode), or the first/last item when nothing is selected. If the list is open: moves focus to the next or previous option. |
|
|
51
|
+
| `Enter` / `Space` | If the option list is closed: opens the option list (from the open button). If the list is open: selects the focused option. In single-select mode the list closes after selection; in multiple-select mode the list stays open. |
|
|
52
|
+
| `Esc` | If the option list is open: closes the list and returns focus to the select trigger. |
|
|
53
|
+
|
|
54
|
+
While the option list is open, focus stays inside the popover in both selection modes.
|
|
55
|
+
|
|
56
|
+
In multiple-select mode, selected values use React Aria’s `TagGroup` in the trigger (styled to match Capra Tag). Keyboard behavior follows TagGroup defaults: arrows move between tags; Delete/Backspace removes; clicking the remove control also removes.
|
|
57
|
+
|
|
58
|
+
### Labeling
|
|
59
|
+
|
|
60
|
+
All select fields require a label. Provide a visible `label` prop when possible. When a visible label is not spatially associated with the trigger, use `aria-label` or `aria-labelledby`.
|
|
61
|
+
|
|
62
|
+
### Focus treatment
|
|
63
|
+
|
|
64
|
+
* The trigger uses the Capra 2px focus outline (`token('border.focus')`).
|
|
65
|
+
* List options show a keyboard focus ring on the active row; the popover container suppresses the browser default panel outline (same pattern as `Menu`).
|
|
66
|
+
* Warning and danger icons in the trigger are decorative (`aria-hidden`); invalid state is conveyed with `aria-invalid` on the trigger.
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Core/SelectField - Usage
|
|
2
|
+
|
|
3
|
+
## Basic usage
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { SelectField } from '@private/core';
|
|
7
|
+
|
|
8
|
+
const animals = [
|
|
9
|
+
{ id: '1', label: 'Dog' },
|
|
10
|
+
{ id: '2', label: 'Cat' },
|
|
11
|
+
{ id: '3', label: 'Bird' },
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
function Example() {
|
|
15
|
+
return (
|
|
16
|
+
<SelectField label="Animal" placeholder="Select" items={animals} />
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Props
|
|
22
|
+
|
|
23
|
+
## Examples
|
|
24
|
+
|
|
25
|
+
### Single select
|
|
26
|
+
|
|
27
|
+
Pass `items` to render a list of options. Each item should have an `id` and a `label` property if using the default item renderer. You can also pass a custom item renderer to render each item as you need (see [Custom item renderer](#custom-item-renderer)).
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
const animals = [
|
|
31
|
+
{ id: '1', label: 'Dog' },
|
|
32
|
+
{ id: '2', label: 'Cat' },
|
|
33
|
+
{ id: '3', label: 'Bird' },
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
<SelectField label="Animal" placeholder="Select" items={animals} />
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Multiple select
|
|
40
|
+
|
|
41
|
+
Set `selectionMode="multiple"` when more than one value can be selected. Selected options render as tags in the input.
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
const states = [
|
|
45
|
+
{ id: '1', label: 'California' },
|
|
46
|
+
{ id: '2', label: 'New York' },
|
|
47
|
+
{ id: '3', label: 'Texas' },
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
<SelectField label="States" selectionMode="multiple" defaultValue={['CA', 'NY']} items={states} />
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Custom item renderer
|
|
54
|
+
|
|
55
|
+
When the default item renderer doesn't meet your needs, you can provide a custom item renderer. Each item **must** be a `SelectField.Item` or `SelectField.Section` component. `SelectField.Item` should be passed an `id` and `textValue` prop. `textValue` is used for typeahead and accessibility naming.
|
|
56
|
+
|
|
57
|
+
:::warning
|
|
58
|
+
|
|
59
|
+
For performance reasons, custom item renderers are cached on component mount. Custom item render functions should be pure functions that take only the passed item as an argument. Any reliance on external state or props will mostly likely cause unexpected and undesired behavior.
|
|
60
|
+
|
|
61
|
+
:::
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
const users = [
|
|
65
|
+
{ id: '1', name: 'Malcolm Reynolds', email: 'malcolm.reynolds@example.com' },
|
|
66
|
+
{ id: '2', name: 'Inara Serra', email: 'inara.serra@example.com' },
|
|
67
|
+
{ id: '3', name: 'Kaylee Frye', email: 'kaylee.frye@example.com' },
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
<SelectField label="Users" selectionMode="multiple" defaultValue={['1', '2']} items={users}>
|
|
71
|
+
{(user) => (
|
|
72
|
+
<SelectField.Item id={user.id} textValue={user.name}>
|
|
73
|
+
<div>{user.name}</div>
|
|
74
|
+
<div>{user.email}</div>
|
|
75
|
+
</SelectField.Item>
|
|
76
|
+
)}
|
|
77
|
+
</SelectField>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Searchable options
|
|
81
|
+
|
|
82
|
+
Enable `canSearch` for longer lists so users can filter options inside the popover.
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
const categories = [
|
|
86
|
+
{ id: '1', name: 'Electronics' },
|
|
87
|
+
{ id: '2', name: 'Clothing' },
|
|
88
|
+
{ id: '3', name: 'Books' },
|
|
89
|
+
// ...
|
|
90
|
+
{ id: '100', name: 'Other' },
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
<SelectField label="Category" items={categories} canSearch searchPlaceholder="Search categories">
|
|
94
|
+
{(item) => (
|
|
95
|
+
<SelectField.Item id={item.id} textValue={item.name}>
|
|
96
|
+
{item.name}
|
|
97
|
+
</SelectField.Item>
|
|
98
|
+
)}
|
|
99
|
+
</SelectField>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
When a search matches none of the options, the popover shows "No results" in place of the option list.
|
|
103
|
+
|
|
104
|
+
### Sections
|
|
105
|
+
|
|
106
|
+
Organize larger option lists into labeled groups by passing `items` with nested `children`, using the default item renderer. If a section has no visible `label`, provide `aria-label` on the section.
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
const items = [
|
|
110
|
+
{
|
|
111
|
+
id: 'fruit',
|
|
112
|
+
label: 'Fruit',
|
|
113
|
+
children: [
|
|
114
|
+
{ id: 'apple', label: 'Apple' },
|
|
115
|
+
{ id: 'banana', label: 'Banana' },
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
id: 'veg',
|
|
120
|
+
label: 'Vegetable',
|
|
121
|
+
children: [
|
|
122
|
+
{ id: 'broccoli', label: 'Broccoli' },
|
|
123
|
+
{ id: 'carrots', label: 'Carrots' },
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
<SelectField label="Healthy Snack" placeholder="Select an item" items={items} />
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Sections can also be built with `SelectField.Section` and `SelectField.Header` JSX children when using static options instead of `items`.
|
|
132
|
+
|
|
133
|
+
### Item icons
|
|
134
|
+
|
|
135
|
+
When an item has an `icon`, it renders before the label in the list. Selecting that item also shows the icon in the trigger for single select, or in the tag for multiple select.
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
const items = [
|
|
139
|
+
{ id: '1', label: 'Favorites', icon: StarIcon },
|
|
140
|
+
{ id: '2', label: 'Settings', icon: CogIcon },
|
|
141
|
+
{ id: '3', label: 'Help', icon: HelpIcon },
|
|
142
|
+
];
|
|
143
|
+
|
|
144
|
+
<SelectField label="Menu" items={items} />
|
|
145
|
+
```
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Core/Table - Design
|
|
2
|
+
|
|
3
|
+
A component for rendering tabular data
|
|
4
|
+
|
|
5
|
+
## Properties
|
|
6
|
+
|
|
7
|
+
### Density
|
|
8
|
+
|
|
9
|
+
Tables have 3 different row sizes: `default`, `compact`, and `tight`.
|
|
10
|
+
|
|
11
|
+
### Appearance
|
|
12
|
+
|
|
13
|
+
Striped rows (aka: Zebra striping) provide contrast between rows by alternating the background color. This can be disabled with `appearance="flat"`.
|
|
14
|
+
|
|
15
|
+
### Loading
|
|
16
|
+
|
|
17
|
+
When tables are in a loading state the content is replaced with 5 rows of skeleton content that represents the general shape of the data.
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# Core/Table - Usage
|
|
2
|
+
|
|
3
|
+
A component for rendering tabular data
|
|
4
|
+
|
|
5
|
+
## Basic usage
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Table } from '@capra/core';
|
|
9
|
+
|
|
10
|
+
type User = {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const userColumns = defineColumns<User>([
|
|
16
|
+
{ id: 'name', label: 'Name', allowsSorting: true },
|
|
17
|
+
])
|
|
18
|
+
|
|
19
|
+
const users = [
|
|
20
|
+
{
|
|
21
|
+
id: 1,
|
|
22
|
+
name: 'Alice Johnson',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 2,
|
|
26
|
+
name: 'Bob Williams',
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
<Table
|
|
31
|
+
columns={userColumns}
|
|
32
|
+
visibleColumns={['name']}
|
|
33
|
+
items={users}
|
|
34
|
+
/>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Props
|
|
38
|
+
|
|
39
|
+
| Prop | Type | Required | Default | Description |
|
|
40
|
+
| --- | --- | --- | --- | --- |
|
|
41
|
+
| `items` | `ItemRecord[]` | Yes | `--` | The data to render as rows in the Table |
|
|
42
|
+
| `density` | `'default' \| 'compact' \| 'tight'` | No | 'default' | The density of the table, affects row height |
|
|
43
|
+
| `appearance` | `'zebra' \| 'flat'` | No | 'zebra' | Whether to apply zebra-striping to rows |
|
|
44
|
+
| `sortDescriptor` | `SortDescriptor` | No | `--` | Key and direction for sorting the Table |
|
|
45
|
+
| `onSortChange` | `(sortDescriptor: SortDescriptor) => void` | No | `--` | Callback function fired when a the sorting configuration is changed |
|
|
46
|
+
| `columns` | `ColumnConfig<ColumnKey, ItemRecord>[]` | Yes | `--` | The columns to render in the Table |
|
|
47
|
+
| `visibleColumns` | `ColumnKey[]` | Yes | `--` | Which columns should be visible |
|
|
48
|
+
| `isLoading` | `boolean` | No | false | Whether the table is in a loading state |
|
|
49
|
+
| `enableDragAndDrop` | `boolean` | No | false | Whether drag-and-dropping rows is enabled |
|
|
50
|
+
| `renderActionColumn` | `(item: ItemRecord) => React.ReactNode` | No | `--` | todo: is this prop needed? |
|
|
51
|
+
| `selectionMode` | `SelectionMode` | No | `--` | Mode of row selection<br>@default 'none' |
|
|
52
|
+
| `selectedKeys` | `Selection` | No | `--` | The currently selected keys (controlled) |
|
|
53
|
+
| `onSelectionChange` | `(keys: Selection) => void` | No | `--` | Callback fired when selection changes |
|
|
54
|
+
| `className` | `never` | No | `--` | Use `FORCE__className` instead. |
|
|
55
|
+
| `style` | `never` | No | `--` | Inline styles are not supported; use component props or `FORCE__className`. |
|
|
56
|
+
| `FORCE__className` | `string` | No | `--` | 🚨 This prop is meant to be an escape hatch. 🚨<br><br>If the desired style cannot be achieved using component props, use this as a last resort. The inner workings of Capra components are implementation details and this escape hatch gives one access to those implementation details. We cannot make any guarantees that styles will applied correctly across version updates. Please use it responsibly.<br><br>Add a CSS class to the component. |
|
|
57
|
+
|
|
58
|
+
## Examples
|
|
59
|
+
|
|
60
|
+
### Sorting
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
import { Table } from '@capra/core';
|
|
64
|
+
|
|
65
|
+
const userColumns = defineColumns<User>([
|
|
66
|
+
{ id: 'name', label: 'Name', allowsSorting: true },
|
|
67
|
+
// ...
|
|
68
|
+
])
|
|
69
|
+
|
|
70
|
+
function SortableTable() {
|
|
71
|
+
const [sortDescriptor, setSortDescriptor] = useState<SortDescriptor | undefined>({
|
|
72
|
+
column: 'name',
|
|
73
|
+
direction: 'ascending',
|
|
74
|
+
});
|
|
75
|
+
const sortedItems = useMemo(() => sortItems(items, sortDescriptor), [sortDescriptor, items]);
|
|
76
|
+
return (
|
|
77
|
+
<Table
|
|
78
|
+
{...props}
|
|
79
|
+
items={sortedItems}
|
|
80
|
+
sortDescriptor={sortDescriptor}
|
|
81
|
+
onSortChange={setSortDescriptor}
|
|
82
|
+
visibleColumns={reverseColumns ? visibleColumns.toReversed() : visibleColumns}
|
|
83
|
+
/>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Selection
|
|
89
|
+
|
|
90
|
+
Use the `selectionMode` prop to enable `single` or `multiple` selection of rows.
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
<Table
|
|
94
|
+
columns={userColumns}
|
|
95
|
+
visibleColumns={['name', 'email', 'balance', 'enabled', 'description', 'trend']}
|
|
96
|
+
items={sortedItems}
|
|
97
|
+
selectionMode="multiple"
|
|
98
|
+
/>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Filter toolbar
|
|
102
|
+
|
|
103
|
+
Use the `FilterToolbar` component to support filtering and/or bulk editing.
|
|
104
|
+
|
|
105
|
+
Note: `FilterToolbar` is only supported instances of `Table` with controlled selection.
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
import { FilterToolbar, Table } from '@capra/core';
|
|
109
|
+
|
|
110
|
+
function TableWithToolbar() {
|
|
111
|
+
const [filterString, setFilterString] = useState('');
|
|
112
|
+
const [selectedKeys, setSelectedKeys] = useState<Selection>(new Set());
|
|
113
|
+
const sortedItems = useMemo(() => sortItems(users, sortDescriptor), [sortDescriptor] );
|
|
114
|
+
const filteredItems = filterUsers(sortedItems, filterString)
|
|
115
|
+
const selectableCount = sortedItems.filter((item) => !item.isDisabled).length;
|
|
116
|
+
const selectedCount = selectedKeys === 'all' ? selectableCount : selectedKeys.size;
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
<div style={{ display: 'flex', gap: '8px', flexDirection: 'column' }}>
|
|
120
|
+
<TableToolbar
|
|
121
|
+
filterInputProps={{
|
|
122
|
+
onChange: (value: string) => setFilterString(value)
|
|
123
|
+
}}
|
|
124
|
+
renderActions={() => (
|
|
125
|
+
<Button size="sm" variant="primary">Add user</Button>
|
|
126
|
+
)}
|
|
127
|
+
renderBulkEditText={() => (
|
|
128
|
+
<Text>
|
|
129
|
+
{selectedCount} of {sortedItems.length} users selected
|
|
130
|
+
</Text>
|
|
131
|
+
)}
|
|
132
|
+
renderBulkEditActions={() => (
|
|
133
|
+
<div style={{ display: 'flex', gap: '8px' }}>
|
|
134
|
+
<Button appearance="neutral" size="sm">
|
|
135
|
+
Delete
|
|
136
|
+
</Button>
|
|
137
|
+
<Button appearance="neutral" variant="tertiary" size="sm">
|
|
138
|
+
Cancel
|
|
139
|
+
</Button>
|
|
140
|
+
</div>
|
|
141
|
+
)}
|
|
142
|
+
selectedKeys={selectedKeys}
|
|
143
|
+
/>
|
|
144
|
+
<Table
|
|
145
|
+
columns={userColumns}
|
|
146
|
+
visibleColumns={['name', 'email', 'balance']}
|
|
147
|
+
items={filteredItems}
|
|
148
|
+
selectionMode="multiple"
|
|
149
|
+
selectedKeys={selectedKeys}
|
|
150
|
+
onSelectionChange={setSelectedKeys}
|
|
151
|
+
/>
|
|
152
|
+
</div>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Custom cell rendering
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
import { Table } from '@capra/core';
|
|
161
|
+
|
|
162
|
+
type User = {
|
|
163
|
+
id: string;
|
|
164
|
+
name: string;
|
|
165
|
+
email: string;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const userColumns = defineColumns<User>([
|
|
169
|
+
{ id: 'name', label: 'Name', allowsSorting: true },
|
|
170
|
+
{
|
|
171
|
+
id: 'email',
|
|
172
|
+
label: 'Email',
|
|
173
|
+
allowsSorting: true,
|
|
174
|
+
render(value) {
|
|
175
|
+
return <Link href={`mailto:${value as string}`}>{value as string}</Link>;
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
])
|
|
179
|
+
|
|
180
|
+
const users = [
|
|
181
|
+
{
|
|
182
|
+
id: 1,
|
|
183
|
+
name: 'Alice Johnson',
|
|
184
|
+
email: 'alice@example.com',
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
id: 2,
|
|
188
|
+
name: 'Bob Williams',
|
|
189
|
+
email: 'bob@example.com',
|
|
190
|
+
},
|
|
191
|
+
];
|
|
192
|
+
|
|
193
|
+
<Table
|
|
194
|
+
columns={userColumns}
|
|
195
|
+
visibleColumns={['name', 'email']}
|
|
196
|
+
items={users}
|
|
197
|
+
/>
|
|
198
|
+
```
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Core/ToggleButtonGroup - Design
|
|
2
|
+
|
|
3
|
+
A group of toggle buttons for choosing one or more options from a set.
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
Toggle button groups behave similarly to radio buttons, providing selection between multiple mutually exclusive options. They are also used to switch views, such as between list and grid view.
|
|
8
|
+
|
|
9
|
+
Toggle button groups affect section-level views and are not a replacement for tabs.
|
|
10
|
+
|
|
11
|
+
## Properties
|
|
12
|
+
|
|
13
|
+
### Size
|
|
14
|
+
|
|
15
|
+
The default size for toggle button groups is medium, which should be used in most cases. A small size is also available when the group appears inline with another component.
|
|
16
|
+
|
|
17
|
+
### Label
|
|
18
|
+
|
|
19
|
+
Labels should be short and succinct. For longer labels, consider a [Radio](?path=/docs/core-radio--design) or [SelectField](?path=/docs/core-selectfield--design).
|
|
20
|
+
|
|
21
|
+
### Icon
|
|
22
|
+
|
|
23
|
+
Icons may appear with a label as part of each button. All buttons within a group should have an icon, or none should.
|
|
24
|
+
|
|
25
|
+
## Behavior
|
|
26
|
+
|
|
27
|
+
### Control count
|
|
28
|
+
|
|
29
|
+
Toggle button groups can have between 2 and 4 buttons.
|
|
30
|
+
|
|
31
|
+
### View switching
|
|
32
|
+
|
|
33
|
+
Toggle button groups can be used to switch the view of data at the section level, such as between a list view and grid view.
|
|
34
|
+
|
|
35
|
+
Views should be presented in a consistent order:
|
|
36
|
+
|
|
37
|
+
* List
|
|
38
|
+
* Grid
|
|
39
|
+
* Graph / Chart
|
|
40
|
+
* Metrics
|
|
41
|
+
|
|
42
|
+
### Filtering
|
|
43
|
+
|
|
44
|
+
Toggle button groups can be used to filter data at the section level, such as a data range within a graph.
|
|
45
|
+
|
|
46
|
+
## Best practices
|
|
47
|
+
|
|
48
|
+
### Count
|
|
49
|
+
|
|
50
|
+
**Do**
|
|
51
|
+
|
|
52
|
+
Do consider a toggle button group if you have 2 to 4 succinct choices.
|
|
53
|
+
|
|
54
|
+
**Don't**
|
|
55
|
+
|
|
56
|
+
Don't use more than 4 buttons in a single group. Consider a [SelectField](?path=/docs/core-selectfield--design) if more than 4 choices are needed.
|
|
57
|
+
|
|
58
|
+
### Wording
|
|
59
|
+
|
|
60
|
+
**Do**
|
|
61
|
+
|
|
62
|
+
Do keep labels short.
|
|
63
|
+
|
|
64
|
+
**Don't**
|
|
65
|
+
|
|
66
|
+
Don't use long, multi-word labels.
|
|
67
|
+
|
|
68
|
+
### Consistent layout
|
|
69
|
+
|
|
70
|
+
**Do**
|
|
71
|
+
|
|
72
|
+
Do keep icons consistent and unchanged when a button becomes active.
|
|
73
|
+
|
|
74
|
+
**Don't**
|
|
75
|
+
|
|
76
|
+
Don't replace icons when in an active state.
|
|
77
|
+
|
|
78
|
+
## Accessibility
|
|
79
|
+
|
|
80
|
+
### Keyboard interaction
|
|
81
|
+
|
|
82
|
+
| Key | Function |
|
|
83
|
+
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
84
|
+
| `Tab`<br />`Shift + Tab` | Move focus into and out of the button group. When focus moves into a button group:<br />- if a button is checked, focus is set on the checked button;<br />- if no buttons are checked, focus is set on the first button in the group. |
|
|
85
|
+
| `Enter`<br />`Space` | Checks the focused button if not already selected. |
|
|
86
|
+
| `Right Arrow`<br />`Down Arrow` | Move focus to the next button in the group. |
|
|
87
|
+
| `Left Arrow`<br />`Up Arrow` | Move focus to the previous button in the group. |
|
|
88
|
+
|
|
89
|
+
Icon-only items require an `aria-label` because there is no visible text label. The group itself requires an `aria-label` (or `aria-labelledby`) so assistive technologies can identify the control set.
|