Skip to content

Commit 1a66cc1

Browse files
committed
Improve shortcut support
1 parent 5a4bb64 commit 1a66cc1

File tree

12 files changed

+243
-73
lines changed

12 files changed

+243
-73
lines changed

packages/ra-core/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@
6666
"lodash": "^4.17.21",
6767
"query-string": "^7.1.3",
6868
"react-error-boundary": "^4.0.13",
69-
"react-hotkeys-hook": "^5.1.0",
7069
"react-is": "^18.2.0 || ^19.0.0"
7170
},
7271
"gitHead": "587df4c27bfcec4a756df4f95e5fc14728dfc0d7"

packages/ra-core/src/util/KeyboardShortcut.tsx

Lines changed: 0 additions & 19 deletions
This file was deleted.

packages/ra-core/src/util/getKeyboardShortcutLabel.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.

packages/ra-core/src/util/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,3 @@ export * from './asyncDebounce';
2828
export * from './hooks';
2929
export * from './shallowEqual';
3030
export * from './useCheckForApplicationUpdate';
31-
export * from './getKeyboardShortcutLabel';
32-
export * from './KeyboardShortcut';

packages/ra-ui-materialui/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
"query-string": "^7.1.3",
8080
"react-dropzone": "^14.2.3",
8181
"react-error-boundary": "^4.0.13",
82+
"react-hotkeys-hook": "^5.1.0",
8283
"react-transition-group": "^4.4.5"
8384
},
8485
"gitHead": "587df4c27bfcec4a756df4f95e5fc14728dfc0d7"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import * as React from 'react';
2+
import { createTheme, ThemeProvider } from '@mui/material';
3+
import { KeyboardShortcut } from './KeyboardShortcut';
4+
import { defaultTheme } from './theme';
5+
6+
export default {
7+
title: 'ra-ui-materialui/KeyboardShortcut',
8+
};
9+
10+
const Wrapper = ({ children }: { children: React.ReactNode }) => (
11+
<ThemeProvider theme={createTheme(defaultTheme)}>{children}</ThemeProvider>
12+
);
13+
14+
export const Default = () => (
15+
<Wrapper>
16+
<KeyboardShortcut keyboardShortcut="meta+K" />
17+
</Wrapper>
18+
);
19+
20+
export const Sequential = () => (
21+
<Wrapper>
22+
<KeyboardShortcut keyboardShortcut="meta+K>X" />
23+
</Wrapper>
24+
);
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import * as React from 'react';
2+
import {
3+
ComponentsOverrides,
4+
styled,
5+
SxProps,
6+
Typography,
7+
} from '@mui/material';
8+
import clsx from 'clsx';
9+
10+
export const KeyboardShortcut = ({
11+
className,
12+
keyboardShortcut,
13+
...rest
14+
}: KeyboardShortcutProps) => {
15+
if (!keyboardShortcut) {
16+
return null;
17+
}
18+
19+
return (
20+
<Root
21+
className={clsx(KeyboardShortcutClasses.root, className)}
22+
{...rest}
23+
>
24+
{keyboardShortcut
25+
.split('>')
26+
.map((sequence, sequenceIndex, sequences) => (
27+
<React.Fragment key={sequence}>
28+
{sequence.split('+').map((key, keyIndex, keys) => (
29+
<React.Fragment key={key}>
30+
<Typography
31+
component="kbd"
32+
className={KeyboardShortcutClasses.kbd}
33+
key={key}
34+
>
35+
{KeyMap[key]
36+
? KeyMap[key]
37+
: key.toUpperCase()}
38+
</Typography>
39+
{keyIndex < keys.length - 1 ? (
40+
<>&nbsp;</>
41+
) : null}
42+
</React.Fragment>
43+
))}
44+
{sequenceIndex < sequences.length - 1 ? (
45+
<>&nbsp;</>
46+
) : null}
47+
</React.Fragment>
48+
))}
49+
</Root>
50+
);
51+
};
52+
53+
const KeyMap = {
54+
meta: '⌘',
55+
ctrl: '⌃',
56+
shift: '⇧',
57+
alt: '⌥',
58+
enter: '⏎',
59+
escape: '⎋',
60+
backspace: '⌫',
61+
delete: '⌦',
62+
tab: '⇥',
63+
space: '␣',
64+
up: '↑',
65+
down: '↓',
66+
left: '←',
67+
right: '→',
68+
home: '↖',
69+
end: '↘',
70+
pageup: '⇞',
71+
pagedown: '⇟',
72+
};
73+
74+
export interface KeyboardShortcutProps
75+
extends React.DetailedHTMLProps<
76+
React.HTMLAttributes<HTMLDivElement>,
77+
HTMLDivElement
78+
> {
79+
keyboardShortcut?: string;
80+
sx?: SxProps;
81+
}
82+
83+
const PREFIX = 'RaKeyboardShortcut';
84+
const KeyboardShortcutClasses = {
85+
root: `${PREFIX}-root`,
86+
kbd: `${PREFIX}-kbd`,
87+
};
88+
89+
const Root = styled('div')(({ theme }) => ({
90+
[`& .${KeyboardShortcutClasses.kbd}`]: {
91+
padding: '4px 5px',
92+
display: 'inline-block',
93+
whiteSpace: 'nowrap',
94+
margin: '0 1px',
95+
fontSize: '11px',
96+
lineHeight: '10px',
97+
color: theme.palette.text.primary,
98+
verticalAlign: 'middle',
99+
border: `1px solid ${theme.palette.divider}`,
100+
borderRadius: 6,
101+
boxShadow: `inset 0 -1px 0 ${theme.palette.divider}`,
102+
},
103+
}));
104+
105+
declare module '@mui/material/styles' {
106+
interface ComponentNameToClassKey {
107+
[PREFIX]: 'root' | 'kbd';
108+
}
109+
110+
interface ComponentsPropsList {
111+
[PREFIX]: Partial<KeyboardShortcutProps>;
112+
}
113+
114+
interface Components {
115+
[PREFIX]?: {
116+
defaultProps?: ComponentsPropsList[typeof PREFIX];
117+
styleOverrides?: ComponentsOverrides<
118+
Omit<Theme, 'components'>
119+
>[typeof PREFIX];
120+
};
121+
}
122+
}

packages/ra-ui-materialui/src/field/DateField.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@ const toLocaleStringSupportsLocales = (() => {
148148
})();
149149

150150
const PREFIX = 'RaDateField';
151-
152151
const StyledTypography = styled(Typography, {
153152
name: PREFIX,
154153
overridesResolver: (props, styles) => styles.root,

packages/ra-ui-materialui/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ export * from './list';
1212
export * from './preferences';
1313
export * from './AdminUI';
1414
export * from './AdminContext';
15+
export * from './KeyboardShortcut';

packages/ra-ui-materialui/src/layout/Menu.stories.tsx

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,23 +163,77 @@ export const Custom = () => {
163163
export const WithKeyboardShortcuts = () => {
164164
const CustomMenu = () => (
165165
<Menu>
166-
<Menu.DashboardItem keyboardShortcut="ctrl+alt+D" />
166+
<Menu.DashboardItem keyboardShortcut="G>D" />
167+
<Menu.Item
168+
to="/sales"
169+
leftIcon={<PieChartOutlined />}
170+
primaryText="Sales"
171+
keyboardShortcut="G>S"
172+
/>
173+
<Menu.Item
174+
to="/customers"
175+
leftIcon={<PeopleOutlined />}
176+
primaryText="Customers"
177+
keyboardShortcut="G>C"
178+
/>
179+
<Menu.ResourceItem
180+
name="products"
181+
leftIcon={<Inventory />}
182+
keyboardShortcut="G>P"
183+
/>
184+
</Menu>
185+
);
186+
const CustomLayout = ({ children }) => (
187+
<Layout menu={CustomMenu}>{children}</Layout>
188+
);
189+
190+
const Dashboard = () => <Page title="Dashboard" />;
191+
return (
192+
<TestMemoryRouter initialEntries={['/']}>
193+
<Admin
194+
dataProvider={testDataProvider()}
195+
layout={CustomLayout}
196+
dashboard={Dashboard}
197+
>
198+
<Resource name="products" list={<Page title="Products" />} />
199+
<CustomRoutes>
200+
<Route path="/sales" element={<Page title="Sales" />} />
201+
<Route
202+
path="/customers"
203+
element={<Page title="Customers" />}
204+
/>
205+
</CustomRoutes>
206+
</Admin>
207+
</TestMemoryRouter>
208+
);
209+
};
210+
211+
export const WithCustomKeyboardShortcutRepresentation = () => {
212+
const CustomMenu = () => (
213+
<Menu>
214+
<Menu.DashboardItem
215+
keyboardShortcut="ctrl+alt+D"
216+
keyboardShortcutRepresentation="ctrl+alt+D"
217+
/>
167218
<Menu.Item
168219
to="/sales"
169220
leftIcon={<PieChartOutlined />}
170221
primaryText="Sales"
171222
keyboardShortcut="ctrl+alt+S"
223+
keyboardShortcutRepresentation="ctrl+alt+S"
172224
/>
173225
<Menu.Item
174226
to="/customers"
175227
leftIcon={<PeopleOutlined />}
176228
primaryText="Customers"
177229
keyboardShortcut="ctrl+alt+C"
230+
keyboardShortcutRepresentation="ctrl+alt+C"
178231
/>
179232
<Menu.ResourceItem
180233
name="products"
181234
leftIcon={<Inventory />}
182235
keyboardShortcut="ctrl+alt+P"
236+
keyboardShortcutRepresentation="ctrl+alt+P"
183237
/>
184238
</Menu>
185239
);

0 commit comments

Comments
 (0)