@edux-design/buttons 0.1.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -6
- package/README.md +64 -48
- package/dist/index.d.mts +115 -19
- package/dist/index.d.ts +115 -19
- package/dist/index.js +401 -92
- package/dist/index.mjs +378 -68
- package/docs/toolbar.md +146 -0
- package/package.json +13 -25
package/docs/toolbar.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Toolbar controls
|
|
2
|
+
|
|
3
|
+
`ToolbarButton`, `ToolbarToggleButton`, `Toolbar`, `ToolbarGroup` and
|
|
4
|
+
`ToolbarSeparator` extend the Buttons package without changing existing Button,
|
|
5
|
+
IconButton or ButtonGroup APIs. Use them for compact editor, preview and page
|
|
6
|
+
actions. Commands, editor selection, persistence and menu state belong to the host.
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import {
|
|
10
|
+
Toolbar,
|
|
11
|
+
ToolbarGroup,
|
|
12
|
+
ToolbarSeparator,
|
|
13
|
+
ToolbarButton,
|
|
14
|
+
ToolbarToggleButton,
|
|
15
|
+
} from "@edux-design/buttons";
|
|
16
|
+
|
|
17
|
+
<Toolbar aria-label="Document formatting">
|
|
18
|
+
<ToolbarGroup aria-label="Text style">
|
|
19
|
+
<ToolbarToggleButton pressed={bold} onPressedChange={setBold}>
|
|
20
|
+
Bold
|
|
21
|
+
</ToolbarToggleButton>
|
|
22
|
+
<ToolbarToggleButton defaultPressed>Italic</ToolbarToggleButton>
|
|
23
|
+
</ToolbarGroup>
|
|
24
|
+
<ToolbarSeparator />
|
|
25
|
+
<ToolbarButton onClick={save}>Save draft</ToolbarButton>
|
|
26
|
+
</Toolbar>;
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Buttons and state
|
|
30
|
+
|
|
31
|
+
All buttons render a single native `button`, forward an `HTMLButtonElement` ref,
|
|
32
|
+
and accept native attributes, events, ARIA, data attributes, `className`, `style`
|
|
33
|
+
and arbitrary children. Their default `type` is `button`; `type="submit"` is
|
|
34
|
+
supported. Handlers receive the native React event with the actual button as
|
|
35
|
+
`event.currentTarget`, suitable for menu anchors and measurement. No wrapper is
|
|
36
|
+
inserted around the button. An optional `icon` slot is decorative (`aria-hidden`).
|
|
37
|
+
|
|
38
|
+
| Prop | Default | Contract |
|
|
39
|
+
| --------------- | --------- | ----------------------------------------------------------- |
|
|
40
|
+
| `size` | `small` | `small`, `medium`, `large`: 36, 44, 56px semantic controls |
|
|
41
|
+
| `variant` | `neutral` | `neutral`, `primary`, `danger`; existing semantic tokens |
|
|
42
|
+
| `iconOnly` | `false` | Square geometry; caller supplies an accessible name |
|
|
43
|
+
| `icon` | — | Decorative icon before children |
|
|
44
|
+
| `active` | `false` | ToolbarButton compatibility visual state, no internal state |
|
|
45
|
+
| `disabled` | `false` | Native disabled behaviour; blocks activation |
|
|
46
|
+
| `aria-disabled` | — | Also blocks activation; roving focus skips this action |
|
|
47
|
+
|
|
48
|
+
An ordinary action has no `aria-pressed` attribute by default. Native
|
|
49
|
+
`aria-pressed` true/false/mixed is forwarded and controls selected styling.
|
|
50
|
+
`active` is a compatibility flag: true selects the visual and defaults
|
|
51
|
+
`aria-pressed` to true. Explicit native `aria-pressed`, including false or
|
|
52
|
+
undefined, takes precedence for semantics. A question selector can use
|
|
53
|
+
`active={current}` and `aria-pressed={undefined}` with the host's appropriate
|
|
54
|
+
`aria-current` semantics. It need not become a toggle merely to show selection.
|
|
55
|
+
|
|
56
|
+
`ToolbarToggleButton` is an independent toggle with boolean `pressed`,
|
|
57
|
+
`defaultPressed` (false by default), and
|
|
58
|
+
`onPressedChange(nextBoolean, clickEvent)`. It always exposes true or false
|
|
59
|
+
`aria-pressed`. Controlled state changes only when its host updates `pressed`.
|
|
60
|
+
The native `onClick` runs first; `event.preventDefault()` cancels state changes
|
|
61
|
+
and the change callback. Disabled actions cannot change state. Enter and Space
|
|
62
|
+
use native button activation. Keep a toggle's accessible label stable between
|
|
63
|
+
states. `active` is reserved for ToolbarButton, not the dedicated toggle.
|
|
64
|
+
|
|
65
|
+
The selected border reserves its space in every state, keeping dimensions stable.
|
|
66
|
+
Transitions honour reduced motion. Disabled controls keep their disabled colours
|
|
67
|
+
on hover. There are no new palette or semantic tokens.
|
|
68
|
+
|
|
69
|
+
## Grouping and keyboard policy
|
|
70
|
+
|
|
71
|
+
`Toolbar` renders a `div` with `role="toolbar"`; supply `aria-label` or
|
|
72
|
+
`aria-labelledby`. `ToolbarGroup` is a native `div` with `role="group"`, not a
|
|
73
|
+
radiogroup. Give meaningful groups accessible names. Root and group support
|
|
74
|
+
`orientation="horizontal"` (default) or `vertical`, refs and native div props.
|
|
75
|
+
`ToolbarSeparator` renders a non-focusable separator; its `orientation` refers
|
|
76
|
+
to the line, and defaults to `vertical` for a horizontal toolbar.
|
|
77
|
+
|
|
78
|
+
**Native Tab order is the default.** Grouping does not change button tab stops,
|
|
79
|
+
activate toggles, intercept arrows or manage menu state. Buttons and groups also
|
|
80
|
+
work independently without a Toolbar. This preserves existing footer/editor
|
|
81
|
+
keyboard behaviour when replacing presentation.
|
|
82
|
+
|
|
83
|
+
Opt into `keyboardNavigation="roving"` deliberately to use a single tab stop
|
|
84
|
+
across ToolbarButton and ToolbarToggleButton descendants. After hydration:
|
|
85
|
+
|
|
86
|
+
- Horizontal Left/Right arrows move focus in the layout direction, including RTL.
|
|
87
|
+
- Vertical Up/Down arrows move focus; the perpendicular axis remains available.
|
|
88
|
+
- Home/End focus the first/last enabled visible action.
|
|
89
|
+
- `loop` defaults to true; false stops at either end.
|
|
90
|
+
- Tab/Shift+Tab leave normally; returning remembers the last enabled action.
|
|
91
|
+
- Arrow navigation moves focus without activating an action or toggle.
|
|
92
|
+
- Native disabled, aria-disabled, hidden/inert and CSS-hidden actions are skipped.
|
|
93
|
+
- Nested toolbars keep independent focus. Inputs and other widgets retain their
|
|
94
|
+
own keys and tab order; place complex widgets in separate labelled groups when
|
|
95
|
+
a strict one-stop toolbar is required.
|
|
96
|
+
- Modified keys and caller-prevented key events are left alone, including table
|
|
97
|
+
Alt+Arrow shortcuts. The host's root `onKeyDown` runs before navigation.
|
|
98
|
+
|
|
99
|
+
Roving mode owns action tab stops while enabled, updates them when actions change,
|
|
100
|
+
and restores the original native values when disabled/unmounted. It does not
|
|
101
|
+
capture focus, auto-focus on mount, intercept Escape or handle editor commands.
|
|
102
|
+
|
|
103
|
+
## Payload compact and selection adapters
|
|
104
|
+
|
|
105
|
+
Existing Payload toolbars use 32px controls. The package's standard small control
|
|
106
|
+
is 36px. Preserve 32px explicitly during local acceptance rather than silently
|
|
107
|
+
resizing the client. This is component/host geometry, not a new global token:
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
const compact = { height: 32, minWidth: 32 };
|
|
111
|
+
|
|
112
|
+
<ToolbarButton
|
|
113
|
+
style={compact}
|
|
114
|
+
onMouseDown={(event) => {
|
|
115
|
+
event.preventDefault(); // Host preserves editor selection.
|
|
116
|
+
onMouseDown?.(event); // Existing handler observes defaultPrevented.
|
|
117
|
+
}}
|
|
118
|
+
onClick={runCommand}
|
|
119
|
+
>
|
|
120
|
+
Insert
|
|
121
|
+
</ToolbarButton>;
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Mouse prevention is optional host behaviour. The package does not prevent pointer
|
|
125
|
+
focus or focus the editor. Keyboard navigation and activation remain available.
|
|
126
|
+
Table handles can retain their class/style/data hooks and specialised geometry,
|
|
127
|
+
pointer handlers, currentTarget anchors and Alt+Arrow reordering in local adapters.
|
|
128
|
+
|
|
129
|
+
## Tooltip, menu and theme composition
|
|
130
|
+
|
|
131
|
+
Use TooltipTrigger's `asChild` API around the same button, or pass that button
|
|
132
|
+
as the existing Menu's `button` prop. Provide TooltipProvider as usual.
|
|
133
|
+
Tooltips describe an action; icon-only actions
|
|
134
|
+
still need `aria-label`/`aria-labelledby`. Injected refs/handlers/ARIA are forwarded.
|
|
135
|
+
Menus own their `aria-expanded`, popup keyboard behaviour and focus restoration;
|
|
136
|
+
use ToolbarButton, not an independent toggle, for a menu trigger.
|
|
137
|
+
|
|
138
|
+
Import `@edux-design/tokens/theme` once, and include installed packages in Tailwind
|
|
139
|
+
source detection. Use existing `theme--light`/`theme--dark` and `brand--blue`/
|
|
140
|
+
`brand--teal` classes on the same scope. The controls use existing semantic
|
|
141
|
+
foreground, background, border and focus tokens. They do not own a theme provider.
|
|
142
|
+
|
|
143
|
+
Storybook **Components/Toolbar** includes native actions, explicit roving focus,
|
|
144
|
+
all sizes/states, light/dark × blue/teal, narrow wrapping, vertical/RTL, tooltip/menu
|
|
145
|
+
composition, a measured Payload32px recipe and the editor selection adapter. The package includes interaction,
|
|
146
|
+
structural accessibility, public consumer type and CJS/SSR fixtures.
|
package/package.json
CHANGED
|
@@ -1,44 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edux-design/buttons",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"sideEffects": [
|
|
6
6
|
"**/*.css"
|
|
7
7
|
],
|
|
8
|
-
"files": [
|
|
9
|
-
"dist",
|
|
10
|
-
"README.md",
|
|
11
|
-
"CHANGELOG.md"
|
|
12
|
-
],
|
|
13
8
|
"types": "./dist/index.d.ts",
|
|
14
9
|
"exports": {
|
|
15
10
|
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
16
12
|
"import": "./dist/index.mjs",
|
|
17
|
-
"require": "./dist/index.js"
|
|
18
|
-
"types": "./dist/index.d.ts"
|
|
13
|
+
"require": "./dist/index.js"
|
|
19
14
|
}
|
|
20
15
|
},
|
|
21
|
-
"scripts": {
|
|
22
|
-
"lint": "eslint . --max-warnings 0",
|
|
23
|
-
"build": "tsup src/index.js --format esm,cjs --dts",
|
|
24
|
-
"dev": "tsup src/index.js --watch --format esm,cjs --dts",
|
|
25
|
-
"prepack": "npm run build",
|
|
26
|
-
"generate:component": "turbo gen react-component",
|
|
27
|
-
"check-types": "tsc --noEmit"
|
|
28
|
-
},
|
|
29
|
-
"devDependencies": {
|
|
30
|
-
"typescript": "^5.9.2",
|
|
31
|
-
"tailwindcss": "^4.1.12",
|
|
32
|
-
"postcss": "^8.5.6",
|
|
33
|
-
"tsup": "^8.5.0",
|
|
34
|
-
"@tailwindcss/cli": "^4.1.12",
|
|
35
|
-
"@tailwindcss/postcss": "^4.1.12"
|
|
36
|
-
},
|
|
37
16
|
"dependencies": {
|
|
38
17
|
"@edux-design/utils": "*"
|
|
39
18
|
},
|
|
40
19
|
"peerDependencies": {
|
|
20
|
+
"@edux-design/tokens": ">=0.2.0 <0.3.0",
|
|
41
21
|
"react": "^19.1.0",
|
|
42
22
|
"react-dom": "^19.1.0"
|
|
43
|
-
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"docs",
|
|
27
|
+
"README.md",
|
|
28
|
+
"CHANGELOG.md",
|
|
29
|
+
"LICENSE",
|
|
30
|
+
"LICENSE.md"
|
|
31
|
+
]
|
|
44
32
|
}
|