@ahrowe/ui 0.48.0 → 0.49.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 -0
- package/dist/cjs-types/common/actionIcon/actionIcon.types.d.ts +1 -1
- package/dist/cjs-types/common/button/button.types.d.ts +6 -1
- package/dist/cjs-types/common/hooks/useHotkeys.d.ts +11 -0
- package/dist/esm/common/button/button.mjs +1 -1
- package/dist/esm/common/button/button.mjs.map +1 -1
- package/dist/esm/common/button/button.module.mjs +1 -1
- package/dist/esm/common/button/button.module.mjs.map +1 -1
- package/dist/esm/common/button/button.types.mjs.map +1 -1
- package/dist/esm/common/commandPalette/commandPalette.mjs +1 -1
- package/dist/esm/common/commandPalette/commandPalette.mjs.map +1 -1
- package/dist/esm/common/hooks/useHotkeys.mjs +1 -1
- package/dist/esm/common/hooks/useHotkeys.mjs.map +1 -1
- package/dist/esm/common/menu/menu.mjs +1 -1
- package/dist/esm/common/menu/menu.mjs.map +1 -1
- package/dist/esm/index.mjs +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/dist/types/common/actionIcon/actionIcon.types.d.ts +1 -1
- package/dist/types/common/button/button.types.d.ts +6 -1
- package/dist/types/common/hooks/useHotkeys.d.ts +11 -0
- package/docs/ActionIcon.md +2 -2
- package/docs/Button.md +18 -2
- package/docs/Hooks.md +23 -4
- package/docs/Menu.md +1 -1
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@ export type ActionIconSlots = 'root' | 'icon';
|
|
|
6
6
|
* A Button with only an icon, sized by that icon, so every Button prop applies except the ones
|
|
7
7
|
* that describe a label or a size.
|
|
8
8
|
*/
|
|
9
|
-
export interface ActionIconProps extends Omit<ButtonProps, 'icon' | 'size' | 'children' | 'isLoading' | 'classNames' | 'styles'> {
|
|
9
|
+
export interface ActionIconProps extends Omit<ButtonProps, 'icon' | 'size' | 'children' | 'isLoading' | 'shortcut' | 'classNames' | 'styles'> {
|
|
10
10
|
icon: IconContent;
|
|
11
11
|
classNames?: SlotClassNames<ActionIconSlots>;
|
|
12
12
|
styles?: SlotStyles<ActionIconSlots>;
|
|
@@ -26,7 +26,7 @@ export declare enum ButtonType {
|
|
|
26
26
|
Reset = "reset",
|
|
27
27
|
Submit = "submit"
|
|
28
28
|
}
|
|
29
|
-
export type ButtonSlots = 'background' | 'content' | 'loadingContainer' | 'label';
|
|
29
|
+
export type ButtonSlots = 'background' | 'content' | 'loadingContainer' | 'label' | 'shortcut';
|
|
30
30
|
/**
|
|
31
31
|
* `DataProps` supplies the `data-*` index signature, which `ButtonHTMLAttributes` doesn't cover.
|
|
32
32
|
* `type`, `color` and `onClick` below deliberately narrow their inherited counterparts.
|
|
@@ -43,6 +43,11 @@ export interface ButtonProps extends DataProps, React.ButtonHTMLAttributes<HTMLB
|
|
|
43
43
|
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => Promise<void> | void;
|
|
44
44
|
children?: React.ReactNode;
|
|
45
45
|
icon?: IconContent;
|
|
46
|
+
/**
|
|
47
|
+
* A shortcut drawn after the label, in `useHotkeys` spelling (`'mod+s'`): `⌘S` on Apple
|
|
48
|
+
* platforms, `Ctrl+S` elsewhere. Drawn only; bind it with `useHotkeys`.
|
|
49
|
+
*/
|
|
50
|
+
shortcut?: string;
|
|
46
51
|
isLoading?: boolean;
|
|
47
52
|
disabled?: boolean;
|
|
48
53
|
title?: string;
|
|
@@ -18,6 +18,11 @@ export interface UseHotkeysOptions {
|
|
|
18
18
|
* Default `true`.
|
|
19
19
|
*/
|
|
20
20
|
preventDefault?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Fire again for each repeat while the combo is held. Default `false`, so holding `mod+s` saves
|
|
23
|
+
* once. Turn it on for keys that step through something, such as arrows or zoom.
|
|
24
|
+
*/
|
|
25
|
+
repeat?: boolean;
|
|
21
26
|
}
|
|
22
27
|
/**
|
|
23
28
|
* A combo written for the reader: `'mod+shift+k'` reads `⇧⌘K` on an Apple platform and
|
|
@@ -30,6 +35,12 @@ export interface UseHotkeysOptions {
|
|
|
30
35
|
* Resolved from the platform at call time, so a server-rendered page renders the non-Apple form.
|
|
31
36
|
*/
|
|
32
37
|
export declare function formatHotkey(combo: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* A combo as `aria-keyshortcuts` spells it: `'mod+shift+k'` is `Control+Shift+K`, or
|
|
40
|
+
* `Meta+Shift+K` on an Apple platform. Resolved from the platform like `formatHotkey`, so the two
|
|
41
|
+
* always name the same keys.
|
|
42
|
+
*/
|
|
43
|
+
export declare function ariaKeyshortcuts(combo: string | undefined): string | undefined;
|
|
33
44
|
/**
|
|
34
45
|
* Keyboard shortcuts, for a whole app or for one element.
|
|
35
46
|
*
|
package/docs/ActionIcon.md
CHANGED
|
@@ -38,8 +38,8 @@ import { faTrash, faXmark } from '@fortawesome/free-solid-svg-icons';
|
|
|
38
38
|
| `ref` | `Ref<HTMLButtonElement>` | Ref to the button |
|
|
39
39
|
|
|
40
40
|
Every other Button prop and native `<button>` attribute passes through, except `size`,
|
|
41
|
-
`children` and `
|
|
42
|
-
for a different one.
|
|
41
|
+
`children`, `isLoading` and `shortcut`. The box is the icon plus `4px` on each side; set `width`
|
|
42
|
+
and `height` for a different one.
|
|
43
43
|
|
|
44
44
|
**Slots:** `root` `icon`
|
|
45
45
|
|
package/docs/Button.md
CHANGED
|
@@ -11,7 +11,7 @@ works with any colour. The colours are the library-wide `ThemeColor`s or any CSS
|
|
|
11
11
|
[ThemeColor.md](ThemeColor.md).
|
|
12
12
|
|
|
13
13
|
```tsx
|
|
14
|
-
import { Button, ButtonStyleType, ButtonSize, ButtonType, ThemeColor } from '@ahrowe/ui';
|
|
14
|
+
import { Button, ButtonStyleType, ButtonSize, ButtonType, ThemeColor, useHotkeys } from '@ahrowe/ui';
|
|
15
15
|
import { faPlus, faSave, faTrash, faXmark } from '@fortawesome/free-solid-svg-icons';
|
|
16
16
|
|
|
17
17
|
// The main action
|
|
@@ -52,6 +52,10 @@ import { faPlus, faSave, faTrash, faXmark } from '@fortawesome/free-solid-svg-ic
|
|
|
52
52
|
// Small
|
|
53
53
|
<Button size={ButtonSize.Small}>Cancel</Button>
|
|
54
54
|
|
|
55
|
+
// A keyboard shortcut: the button draws it, useHotkeys binds it
|
|
56
|
+
useHotkeys({ 'mod+s': handleSave });
|
|
57
|
+
<Button styleType={ButtonStyleType.Filled} shortcut='mod+s' onClick={handleSave}>Save</Button>
|
|
58
|
+
|
|
55
59
|
// Press-and-hold: act on press rather than release
|
|
56
60
|
<Button
|
|
57
61
|
icon={faPlus}
|
|
@@ -81,6 +85,7 @@ import { faPlus, faSave, faTrash, faXmark } from '@fortawesome/free-solid-svg-ic
|
|
|
81
85
|
| `size` | `ButtonSize` | Size variant. `Icon` strips all padding, inherits the surrounding text size, and centres a lone icon — set the width/height yourself |
|
|
82
86
|
| `type` | `ButtonType` | HTML button type |
|
|
83
87
|
| `icon` | `IconDefinition \| ReactElement` | FontAwesome icon or any React element, shown before label |
|
|
88
|
+
| `shortcut` | `string` | A combo in [useHotkeys](Hooks.md) spelling, e.g. `'mod+s'`. Shown after the label, written for the platform. Binding it is yours, see below |
|
|
84
89
|
| `isLoading` | `boolean` | Replaces content with a [Loader](Loader.md) in the button's text colour. Blocks clicks like `disabled` but keeps full colour |
|
|
85
90
|
| `disabled` | `boolean` | Fades the button to half opacity |
|
|
86
91
|
| `onClick` | `(e: MouseEvent) => void \| Promise<void>` | |
|
|
@@ -92,6 +97,17 @@ press-and-hold controls that act on press rather than release), `onContextMenu`,
|
|
|
92
97
|
takes the `ButtonType` enum, and `onClick` may return a promise. `color` is Button's own prop, not
|
|
93
98
|
the obsolete HTML attribute.
|
|
94
99
|
|
|
100
|
+
**Shortcuts are drawn, not bound.** `shortcut` writes the combo in a pill after the label,
|
|
101
|
+
`⌘S` on an Apple platform and `Ctrl+S` elsewhere. It also sets `aria-keyshortcuts`, so a screen
|
|
102
|
+
reader announces it apart from the button's name. It is hidden on a touch screen
|
|
103
|
+
(`pointer: coarse`), which usually has no keyboard to press it on.
|
|
104
|
+
|
|
105
|
+
Listening for it is yours, with `useHotkeys` and the same string, because only your app knows
|
|
106
|
+
when the action is available. A button cannot tell: it would still fire while hidden in an
|
|
107
|
+
inactive tab, behind an open Modal, or twice when the same action has a button in the toolbar and
|
|
108
|
+
another in the footer. Turn the binding off wherever the button is disabled:
|
|
109
|
+
`useHotkeys({ 'mod+s': handleSave }, { enabled: !isSaving })`.
|
|
110
|
+
|
|
95
111
|
**Theming:** each colour reads the theme variables listed in [ThemeColor.md](ThemeColor.md). The
|
|
96
112
|
`Filled` `Primary` button also paints `--primary-gradient` when the theme sets one. See
|
|
97
113
|
[ThemeProvider.md](ThemeProvider.md).
|
|
@@ -112,7 +128,7 @@ sits inside the edge, in `--focus-border-color`, or in the text colour on a `Fil
|
|
|
112
128
|
ripple is in the text colour too. Every button reserves a `1px` border, so switching style never
|
|
113
129
|
shifts layout or changes its height.
|
|
114
130
|
|
|
115
|
-
**Slots:** `background` `content` `loadingContainer` `label`
|
|
131
|
+
**Slots:** `background` `content` `loadingContainer` `label` `shortcut`
|
|
116
132
|
|
|
117
133
|
**Sizing in a flex row:** a Button never shrinks below its own content (`min-width: min-content`),
|
|
118
134
|
so an icon is never cut off by a flexible sibling. A multi-word label still wraps above that floor.
|
package/docs/Hooks.md
CHANGED
|
@@ -59,6 +59,7 @@ Two rules worth knowing:
|
|
|
59
59
|
| `target` | `RefObject<HTMLElement \| null>` | window | Scope the shortcuts to one element |
|
|
60
60
|
| `enableInFields` | `boolean` | `false` | Let a combo fire while a text field or `contenteditable` has focus. Only combos the field would otherwise consume are held back: a single character, or an editing key (`arrowup`, `home`, `backspace`, `enter`, `tab`, …). Anything carrying Ctrl, Cmd or Alt fires regardless, and so do `f2`, `escape` and the other keys a field does not use |
|
|
61
61
|
| `preventDefault` | `boolean` | `true` | Call `preventDefault()` on a match, so `mod+s` does not also open the browser's save dialog |
|
|
62
|
+
| `repeat` | `boolean` | `false` | Fire again for each repeat while the combo is held. Off, holding `mod+s` saves once; turn it on for keys that step through something |
|
|
62
63
|
|
|
63
64
|
Shortcuts keep firing while a `Modal` or `Drawer` is open, which is usually what a command palette
|
|
64
65
|
wants. Pass `enabled` for the ones that should not:
|
|
@@ -67,7 +68,14 @@ wants. Pass `enabled` for the ones that should not:
|
|
|
67
68
|
useHotkeys({ n: createRecord }, { enabled: !isModalOpen });
|
|
68
69
|
```
|
|
69
70
|
|
|
70
|
-
A held
|
|
71
|
+
A held combo fires once, where the browser's key repeat would save over and over or flicker a
|
|
72
|
+
toggle. The skipped repeats are still `preventDefault()`-ed. Where a repeat is the point, as with
|
|
73
|
+
arrows or zoom, ask for it, in a call of its own if the page also has actions:
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
useHotkeys({ 'mod+s': save, '?': toggleHelp });
|
|
77
|
+
useHotkeys({ j: selectNext, k: selectPrevious }, { repeat: true });
|
|
78
|
+
```
|
|
71
79
|
|
|
72
80
|
### formatHotkey
|
|
73
81
|
|
|
@@ -79,9 +87,10 @@ formatHotkey('mod+shift+k'); // '⇧⌘K' on an Apple platform, 'Ctrl+Shift+K' e
|
|
|
79
87
|
formatHotkey('f2'); // 'F2'
|
|
80
88
|
```
|
|
81
89
|
|
|
82
|
-
This is what `Menu`
|
|
83
|
-
|
|
84
|
-
time, so a server-rendered page renders the
|
|
90
|
+
This is what `Menu`, `CommandPalette` and `Button` use to draw a shortcut, from the same string
|
|
91
|
+
`useHotkeys` binds, see [Menu.md](Menu.md) and [Button.md](Button.md). A combo it cannot parse is
|
|
92
|
+
handed back unchanged. The platform is read at call time, so a server-rendered page renders the
|
|
93
|
+
non-Apple spelling.
|
|
85
94
|
|
|
86
95
|
## useMediaQuery
|
|
87
96
|
|
|
@@ -266,3 +275,13 @@ Hooks documented with the thing they belong to:
|
|
|
266
275
|
- `useComponentDefaults`, `useLabels` — see [ConfigProvider.md](ConfigProvider.md)
|
|
267
276
|
- `useFormValidator` — see [FormValidator.md](FormValidator.md)
|
|
268
277
|
- `useFormValidatorGroup` — see [FormValidatorGroup.md](FormValidatorGroup.md)
|
|
278
|
+
|
|
279
|
+
## Migration from repeating hotkeys
|
|
280
|
+
|
|
281
|
+
Before 0.49, a held `useHotkeys` combo fired its handler for every repeat the browser sent. It now
|
|
282
|
+
fires once. Nothing fails to compile, so look for shortcuts that are meant to be held:
|
|
283
|
+
|
|
284
|
+
| Before | After |
|
|
285
|
+
|--------|-------|
|
|
286
|
+
| `useHotkeys({ arrowdown: next })`, relying on the repeat | `useHotkeys({ arrowdown: next }, { repeat: true })` |
|
|
287
|
+
| `if (event.repeat) return;` at the top of a handler | nothing: drop the check |
|
package/docs/Menu.md
CHANGED
|
@@ -34,7 +34,7 @@ const items: MenuEntry[] = [
|
|
|
34
34
|
|
|
35
35
|
`keywords` are extra search terms for [CommandPalette](CommandPalette.md), which takes the same entries. The menu ignores them.
|
|
36
36
|
|
|
37
|
-
**Shortcuts are drawn, not bound.** A `shortcut` is a combo in [useHotkeys](Hooks.md) spelling, and the menu writes it on the trailing edge for the platform the reader is on: `mod+s` shows as `⌘S` on an Apple one and `Ctrl+S` everywhere else. So there is one spelling per shortcut and a hand-written `'⌘S'` can no longer be wrong on Windows.
|
|
37
|
+
**Shortcuts are drawn, not bound.** A `shortcut` is a combo in [useHotkeys](Hooks.md) spelling, and the menu writes it on the trailing edge for the platform the reader is on: `mod+s` shows as `⌘S` on an Apple one and `Ctrl+S` everywhere else. So there is one spelling per shortcut and a hand-written `'⌘S'` can no longer be wrong on Windows. Screen readers get it through `aria-keyshortcuts`, not as part of the item's name.
|
|
38
38
|
|
|
39
39
|
Listening for it is yours, because only your app knows what else is listening: if it already binds `⌘S` and the menu bound it too, both would fire and the action would run twice. `menuHotkeys` turns the same entries into a map for `useHotkeys`, so the one spelling covers the hint and the binding:
|
|
40
40
|
|