@noya-app/noya-designsystem 0.0.2
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/.turbo/turbo-build.log +17 -0
- package/CHANGELOG.md +7 -0
- package/README.md +1 -0
- package/dist/index.d.mts +1178 -0
- package/dist/index.d.ts +1178 -0
- package/dist/index.js +15651 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +15684 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +49 -0
- package/src/__tests__/__snapshots__/fuzzyScorer.test.ts.snap +41 -0
- package/src/__tests__/fuzzyScorer.test.ts +85 -0
- package/src/components/ActivityIndicator.tsx +44 -0
- package/src/components/Avatar.tsx +64 -0
- package/src/components/Button.tsx +209 -0
- package/src/components/Chip.tsx +214 -0
- package/src/components/ContextMenu.tsx +224 -0
- package/src/components/Dialog.tsx +143 -0
- package/src/components/Divider.tsx +40 -0
- package/src/components/DropdownMenu.tsx +208 -0
- package/src/components/FillInputField.tsx +43 -0
- package/src/components/FillPreviewBackground.tsx +132 -0
- package/src/components/GradientPicker.tsx +88 -0
- package/src/components/Grid.tsx +54 -0
- package/src/components/GridView.tsx +468 -0
- package/src/components/IconButton.tsx +46 -0
- package/src/components/InputField.tsx +568 -0
- package/src/components/InputFieldWithCompletions.tsx +477 -0
- package/src/components/Label.tsx +62 -0
- package/src/components/LabeledElementView.tsx +185 -0
- package/src/components/ListView.tsx +950 -0
- package/src/components/Popover.tsx +113 -0
- package/src/components/Progress.tsx +57 -0
- package/src/components/RadioGroup.tsx +163 -0
- package/src/components/ScrollArea.tsx +70 -0
- package/src/components/Select.tsx +152 -0
- package/src/components/Slider.tsx +82 -0
- package/src/components/Sortable.tsx +296 -0
- package/src/components/Spacer.tsx +29 -0
- package/src/components/Stack.tsx +142 -0
- package/src/components/Switch.tsx +67 -0
- package/src/components/Text.tsx +164 -0
- package/src/components/Toast.tsx +86 -0
- package/src/components/Tooltip.tsx +43 -0
- package/src/components/TreeView.tsx +85 -0
- package/src/components/internal/Menu.tsx +222 -0
- package/src/components/internal/TextInput.tsx +296 -0
- package/src/components/internal/__tests__/TextInput.test.tsx +144 -0
- package/src/contexts/DesignSystemConfiguration.tsx +57 -0
- package/src/contexts/DialogContext.tsx +190 -0
- package/src/contexts/GlobalInputBlurContext.tsx +61 -0
- package/src/contexts/ImageDataContext.tsx +44 -0
- package/src/hooks/__tests__/mergeEventHandlers.test.ts +47 -0
- package/src/hooks/mergeEventHandlers.ts +55 -0
- package/src/hooks/useHover.ts +173 -0
- package/src/hooks/useObjectURL.ts +22 -0
- package/src/hooks/usePlatform.ts +13 -0
- package/src/index.tsx +77 -0
- package/src/mediaQuery.ts +13 -0
- package/src/theme/dark.ts +37 -0
- package/src/theme/index.ts +17 -0
- package/src/theme/light.ts +178 -0
- package/src/utils/breakpoints.ts +45 -0
- package/src/utils/completions.ts +21 -0
- package/src/utils/createSectionedMenu.ts +38 -0
- package/src/utils/fuzzyScorer.ts +105 -0
- package/src/utils/getGradientBackground.tsx +33 -0
- package/src/utils/handleNudge.ts +30 -0
- package/src/utils/mouseEvent.ts +7 -0
- package/src/utils/sketchColor.ts +34 -0
- package/src/utils/sketchPattern.ts +29 -0
- package/src/utils/withSeparatorElements.ts +31 -0
- package/tsconfig.json +3 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import * as ToastPrimitive from '@radix-ui/react-toast';
|
|
2
|
+
import React, { ReactNode } from 'react';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import { IconButton } from './IconButton';
|
|
5
|
+
|
|
6
|
+
const ToastViewport = styled(ToastPrimitive.Viewport)({
|
|
7
|
+
position: 'fixed',
|
|
8
|
+
bottom: 0,
|
|
9
|
+
right: 0,
|
|
10
|
+
display: 'flex',
|
|
11
|
+
flexDirection: 'column',
|
|
12
|
+
padding: 20,
|
|
13
|
+
gap: 10,
|
|
14
|
+
minWidth: 200,
|
|
15
|
+
maxWidth: '100vw',
|
|
16
|
+
margin: 0,
|
|
17
|
+
listStyle: 'none',
|
|
18
|
+
zIndex: 2147483647,
|
|
19
|
+
outline: 'none',
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const ToastRoot = styled(ToastPrimitive.Root)(({ theme }) => ({
|
|
23
|
+
borderRadius: 4,
|
|
24
|
+
fontSize: 14,
|
|
25
|
+
backgroundColor: theme.colors.popover.background,
|
|
26
|
+
boxShadow: '0 2px 4px rgba(0,0,0,0.2), 0 0 12px rgba(0,0,0,0.1)',
|
|
27
|
+
color: theme.colors.textMuted,
|
|
28
|
+
|
|
29
|
+
padding: '8px 10px',
|
|
30
|
+
display: 'grid',
|
|
31
|
+
gridTemplateAreas: '"title action" "description action"',
|
|
32
|
+
gridTemplateColumns: 'auto max-content',
|
|
33
|
+
columnGap: 10,
|
|
34
|
+
alignItems: 'center',
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
const ToastTitle = styled(ToastPrimitive.Title)(({ theme }) => ({
|
|
38
|
+
gridArea: 'title',
|
|
39
|
+
marginBottom: 5,
|
|
40
|
+
...theme.textStyles.label,
|
|
41
|
+
fontWeight: 'bold',
|
|
42
|
+
color: theme.colors.text,
|
|
43
|
+
}));
|
|
44
|
+
|
|
45
|
+
const ToastDescription = styled(ToastPrimitive.Description)(({ theme }) => ({
|
|
46
|
+
gridArea: 'description',
|
|
47
|
+
...theme.textStyles.small,
|
|
48
|
+
color: theme.colors.textMuted,
|
|
49
|
+
}));
|
|
50
|
+
|
|
51
|
+
const ToastAction = styled(ToastPrimitive.Action)({
|
|
52
|
+
gridArea: 'action',
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export const Toast = ({
|
|
56
|
+
title,
|
|
57
|
+
content,
|
|
58
|
+
children,
|
|
59
|
+
...props
|
|
60
|
+
}: {
|
|
61
|
+
title?: string;
|
|
62
|
+
content: ReactNode;
|
|
63
|
+
children?: React.ReactNode;
|
|
64
|
+
}) => {
|
|
65
|
+
return (
|
|
66
|
+
<ToastRoot {...props}>
|
|
67
|
+
{title && <ToastTitle>{title}</ToastTitle>}
|
|
68
|
+
<ToastDescription>{content}</ToastDescription>
|
|
69
|
+
{children && (
|
|
70
|
+
<ToastAction asChild altText="">
|
|
71
|
+
{children}
|
|
72
|
+
</ToastAction>
|
|
73
|
+
)}
|
|
74
|
+
<ToastPrimitive.Close aria-label="Close" asChild>
|
|
75
|
+
<IconButton iconName="Cross1Icon" />
|
|
76
|
+
</ToastPrimitive.Close>
|
|
77
|
+
</ToastRoot>
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const ToastProvider = ({ children }: { children: React.ReactNode }) => (
|
|
82
|
+
<ToastPrimitive.Provider>
|
|
83
|
+
{children}
|
|
84
|
+
<ToastViewport />
|
|
85
|
+
</ToastPrimitive.Provider>
|
|
86
|
+
);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
2
|
+
import React, { memo, ReactNode } from 'react';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
|
|
5
|
+
// const Arrow = styled(TooltipPrimitive.Arrow)(({ theme }) => ({
|
|
6
|
+
// fill: theme.colors.popover.background,
|
|
7
|
+
// }));
|
|
8
|
+
|
|
9
|
+
const Content = styled(TooltipPrimitive.Content)(({ theme }) => ({
|
|
10
|
+
...theme.textStyles.small,
|
|
11
|
+
color: theme.colors.text,
|
|
12
|
+
borderRadius: 3,
|
|
13
|
+
padding: `${theme.sizes.spacing.small}px ${theme.sizes.spacing.medium}px`,
|
|
14
|
+
backgroundColor: theme.colors.popover.background,
|
|
15
|
+
boxShadow: '0 2px 4px rgba(0,0,0,0.2), 0 0 12px rgba(0,0,0,0.1)',
|
|
16
|
+
border: `1px solid ${theme.colors.dividerStrong}`,
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
interface Props {
|
|
20
|
+
children: ReactNode;
|
|
21
|
+
content: ReactNode;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const Tooltip = memo(function Tooltip({ children, content }: Props) {
|
|
25
|
+
return (
|
|
26
|
+
<TooltipPrimitive.Provider>
|
|
27
|
+
<TooltipPrimitive.Root>
|
|
28
|
+
<TooltipPrimitive.Trigger asChild>{children}</TooltipPrimitive.Trigger>
|
|
29
|
+
<TooltipPrimitive.Portal>
|
|
30
|
+
<Content
|
|
31
|
+
side="bottom"
|
|
32
|
+
align="center"
|
|
33
|
+
sideOffset={2}
|
|
34
|
+
collisionPadding={8}
|
|
35
|
+
>
|
|
36
|
+
{content}
|
|
37
|
+
{/* <Arrow /> */}
|
|
38
|
+
</Content>
|
|
39
|
+
</TooltipPrimitive.Portal>
|
|
40
|
+
</TooltipPrimitive.Root>
|
|
41
|
+
</TooltipPrimitive.Provider>
|
|
42
|
+
);
|
|
43
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
ForwardedRef,
|
|
3
|
+
forwardRef,
|
|
4
|
+
memo,
|
|
5
|
+
ReactNode,
|
|
6
|
+
useCallback,
|
|
7
|
+
useContext,
|
|
8
|
+
} from 'react';
|
|
9
|
+
import { IconButton } from './IconButton';
|
|
10
|
+
import { ListView } from './ListView';
|
|
11
|
+
import { Spacer } from './Spacer';
|
|
12
|
+
|
|
13
|
+
/* ----------------------------------------------------------------------------
|
|
14
|
+
* Row
|
|
15
|
+
* ------------------------------------------------------------------------- */
|
|
16
|
+
|
|
17
|
+
type TreeRowBaseProps = {
|
|
18
|
+
icon?: ReactNode;
|
|
19
|
+
expanded?: boolean;
|
|
20
|
+
onClickChevron?: ({ altKey }: { altKey: boolean }) => void;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type TreeViewRowProps<MenuItemType extends string> =
|
|
24
|
+
ListView.RowProps<MenuItemType> & TreeRowBaseProps;
|
|
25
|
+
|
|
26
|
+
const TreeRow = forwardRef(function TreeRow<MenuItemType extends string>(
|
|
27
|
+
{
|
|
28
|
+
icon,
|
|
29
|
+
expanded,
|
|
30
|
+
onClickChevron,
|
|
31
|
+
children,
|
|
32
|
+
...rest
|
|
33
|
+
}: TreeViewRowProps<MenuItemType>,
|
|
34
|
+
forwardedRef: ForwardedRef<HTMLLIElement>,
|
|
35
|
+
) {
|
|
36
|
+
const { expandable } = useContext(ListView.RowContext);
|
|
37
|
+
|
|
38
|
+
const handleClickChevron = useCallback(
|
|
39
|
+
(event: React.MouseEvent) => {
|
|
40
|
+
event.stopPropagation();
|
|
41
|
+
onClickChevron?.({ altKey: event.altKey });
|
|
42
|
+
},
|
|
43
|
+
[onClickChevron],
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<ListView.Row ref={forwardedRef} {...rest}>
|
|
48
|
+
{expandable && (
|
|
49
|
+
<>
|
|
50
|
+
{expanded === undefined ? (
|
|
51
|
+
<Spacer.Horizontal size={19} />
|
|
52
|
+
) : (
|
|
53
|
+
<IconButton
|
|
54
|
+
iconName={expanded ? 'ChevronDownIcon' : 'ChevronRightIcon'}
|
|
55
|
+
onClick={handleClickChevron}
|
|
56
|
+
selected={rest.selected}
|
|
57
|
+
/>
|
|
58
|
+
)}
|
|
59
|
+
<Spacer.Horizontal size={6} />
|
|
60
|
+
</>
|
|
61
|
+
)}
|
|
62
|
+
{icon && (
|
|
63
|
+
<>
|
|
64
|
+
{icon}
|
|
65
|
+
<Spacer.Horizontal size={10} />
|
|
66
|
+
</>
|
|
67
|
+
)}
|
|
68
|
+
{children}
|
|
69
|
+
</ListView.Row>
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
/* ----------------------------------------------------------------------------
|
|
74
|
+
* Root
|
|
75
|
+
* ------------------------------------------------------------------------- */
|
|
76
|
+
|
|
77
|
+
export namespace TreeView {
|
|
78
|
+
export const Root = ListView.Root;
|
|
79
|
+
export const RowTitle = ListView.RowTitle;
|
|
80
|
+
export const EditableRowTitle = ListView.EditableRowTitle;
|
|
81
|
+
export const Row = memo(TreeRow);
|
|
82
|
+
export type ClickInfo = ListView.ClickInfo;
|
|
83
|
+
export type RowProps<MenuItemType extends string> =
|
|
84
|
+
TreeViewRowProps<MenuItemType>;
|
|
85
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { getShortcutDisplayParts } from "@noya-app/noya-keymap";
|
|
2
|
+
import React, { memo, ReactElement, ReactNode } from "react";
|
|
3
|
+
import styled, { CSSObject } from "styled-components";
|
|
4
|
+
import { useDesignSystemConfiguration } from "../../contexts/DesignSystemConfiguration";
|
|
5
|
+
import { Theme } from "../../theme";
|
|
6
|
+
import withSeparatorElements from "../../utils/withSeparatorElements";
|
|
7
|
+
|
|
8
|
+
export const SEPARATOR_ITEM = "separator";
|
|
9
|
+
|
|
10
|
+
// From electron
|
|
11
|
+
export type MenuItemRole =
|
|
12
|
+
| "undo"
|
|
13
|
+
| "redo"
|
|
14
|
+
| "cut"
|
|
15
|
+
| "copy"
|
|
16
|
+
| "paste"
|
|
17
|
+
| "pasteAndMatchStyle"
|
|
18
|
+
| "delete"
|
|
19
|
+
| "selectAll"
|
|
20
|
+
| "reload"
|
|
21
|
+
| "forceReload"
|
|
22
|
+
| "toggleDevTools"
|
|
23
|
+
| "resetZoom"
|
|
24
|
+
| "zoomIn"
|
|
25
|
+
| "zoomOut"
|
|
26
|
+
| "toggleSpellChecker"
|
|
27
|
+
| "togglefullscreen"
|
|
28
|
+
| "window"
|
|
29
|
+
| "minimize"
|
|
30
|
+
| "close"
|
|
31
|
+
| "help"
|
|
32
|
+
| "about"
|
|
33
|
+
| "services"
|
|
34
|
+
| "hide"
|
|
35
|
+
| "hideOthers"
|
|
36
|
+
| "unhide"
|
|
37
|
+
| "quit"
|
|
38
|
+
| "showSubstitutions"
|
|
39
|
+
| "toggleSmartQuotes"
|
|
40
|
+
| "toggleSmartDashes"
|
|
41
|
+
| "toggleTextReplacement"
|
|
42
|
+
| "startSpeaking"
|
|
43
|
+
| "stopSpeaking"
|
|
44
|
+
| "zoom"
|
|
45
|
+
| "front"
|
|
46
|
+
| "appMenu"
|
|
47
|
+
| "fileMenu"
|
|
48
|
+
| "editMenu"
|
|
49
|
+
| "viewMenu"
|
|
50
|
+
| "shareMenu"
|
|
51
|
+
| "recentDocuments"
|
|
52
|
+
| "toggleTabBar"
|
|
53
|
+
| "selectNextTab"
|
|
54
|
+
| "selectPreviousTab"
|
|
55
|
+
| "mergeAllWindows"
|
|
56
|
+
| "clearRecentDocuments"
|
|
57
|
+
| "moveTabToNewWindow"
|
|
58
|
+
| "windowMenu";
|
|
59
|
+
|
|
60
|
+
export type RegularMenuItem<T extends string> = {
|
|
61
|
+
value?: T;
|
|
62
|
+
title: ReactNode;
|
|
63
|
+
shortcut?: string;
|
|
64
|
+
checked?: boolean;
|
|
65
|
+
disabled?: boolean;
|
|
66
|
+
icon?: ReactElement;
|
|
67
|
+
items?: MenuItem<T>[];
|
|
68
|
+
role?: MenuItemRole;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export type MenuItem<T extends string> =
|
|
72
|
+
| typeof SEPARATOR_ITEM
|
|
73
|
+
| RegularMenuItem<T>;
|
|
74
|
+
|
|
75
|
+
// Extract type T of RegularMenuItem<T> within MenuItem<T>
|
|
76
|
+
export type ExtractMenuItemType<T> = T extends RegularMenuItem<infer U>
|
|
77
|
+
? U
|
|
78
|
+
: never;
|
|
79
|
+
|
|
80
|
+
export const CHECKBOX_WIDTH = 15;
|
|
81
|
+
export const CHECKBOX_RIGHT_INSET = 3;
|
|
82
|
+
|
|
83
|
+
export const styles = {
|
|
84
|
+
separatorStyle: ({ theme }: { theme: Theme }): CSSObject => ({
|
|
85
|
+
height: "1px",
|
|
86
|
+
backgroundColor: theme.colors.divider,
|
|
87
|
+
margin: "4px 8px",
|
|
88
|
+
}),
|
|
89
|
+
|
|
90
|
+
itemStyle: ({
|
|
91
|
+
theme,
|
|
92
|
+
disabled,
|
|
93
|
+
}: {
|
|
94
|
+
theme: Theme;
|
|
95
|
+
disabled?: boolean;
|
|
96
|
+
}): CSSObject => ({
|
|
97
|
+
...theme.textStyles.small,
|
|
98
|
+
fontWeight: 500,
|
|
99
|
+
fontSize: "0.8rem",
|
|
100
|
+
flex: "0 0 auto",
|
|
101
|
+
userSelect: "none",
|
|
102
|
+
cursor: "pointer",
|
|
103
|
+
borderRadius: "3px",
|
|
104
|
+
padding: "4px 8px",
|
|
105
|
+
...(disabled && {
|
|
106
|
+
color: theme.colors.textDisabled,
|
|
107
|
+
}),
|
|
108
|
+
"&:focus": {
|
|
109
|
+
outline: "none",
|
|
110
|
+
color: "white",
|
|
111
|
+
backgroundColor: theme.colors.primary,
|
|
112
|
+
|
|
113
|
+
"& kbd": {
|
|
114
|
+
color: "white",
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
"&:active": {
|
|
118
|
+
background: theme.colors.primaryLight,
|
|
119
|
+
},
|
|
120
|
+
display: "flex",
|
|
121
|
+
alignItems: "center",
|
|
122
|
+
}),
|
|
123
|
+
|
|
124
|
+
itemIndicatorStyle: {
|
|
125
|
+
display: "flex",
|
|
126
|
+
alignItems: "center",
|
|
127
|
+
left: `-${CHECKBOX_WIDTH / 2}px`,
|
|
128
|
+
position: "relative",
|
|
129
|
+
marginRight: `-${CHECKBOX_RIGHT_INSET}px`,
|
|
130
|
+
} as CSSObject,
|
|
131
|
+
|
|
132
|
+
contentStyle: ({
|
|
133
|
+
theme,
|
|
134
|
+
scrollable,
|
|
135
|
+
}: {
|
|
136
|
+
theme: Theme;
|
|
137
|
+
scrollable?: boolean;
|
|
138
|
+
}): CSSObject => ({
|
|
139
|
+
borderRadius: 4,
|
|
140
|
+
backgroundColor: theme.colors.popover.background,
|
|
141
|
+
color: theme.colors.text,
|
|
142
|
+
boxShadow: "0 2px 4px rgba(0,0,0,0.2), 0 0 12px rgba(0,0,0,0.1)",
|
|
143
|
+
padding: "4px",
|
|
144
|
+
border: `1px solid ${theme.colors.popover.divider}`,
|
|
145
|
+
zIndex: 1000,
|
|
146
|
+
...(scrollable && {
|
|
147
|
+
height: "100%",
|
|
148
|
+
maxHeight: "calc(100vh - 80px)",
|
|
149
|
+
overflow: "hidden auto",
|
|
150
|
+
}),
|
|
151
|
+
}),
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
function getKeyboardShortcuts<T extends string>(
|
|
155
|
+
item: MenuItem<T>
|
|
156
|
+
): [string, T][] {
|
|
157
|
+
if (item === SEPARATOR_ITEM) return [];
|
|
158
|
+
|
|
159
|
+
if (item.items) return item.items.flatMap(getKeyboardShortcuts);
|
|
160
|
+
|
|
161
|
+
if (item.disabled || !item.value || !item.shortcut) return [];
|
|
162
|
+
|
|
163
|
+
return [[item.shortcut, item.value]];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function getKeyboardShortcutsForMenuItems<T extends string>(
|
|
167
|
+
menuItems: MenuItem<T>[],
|
|
168
|
+
onSelect: (type: T) => void
|
|
169
|
+
): Record<string, () => void> {
|
|
170
|
+
return Object.fromEntries(
|
|
171
|
+
menuItems
|
|
172
|
+
.flatMap(getKeyboardShortcuts)
|
|
173
|
+
.map(([key, value]) => [key, () => onSelect(value)])
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const ShortcutElement = styled.kbd(
|
|
178
|
+
({
|
|
179
|
+
theme,
|
|
180
|
+
fixedWidth,
|
|
181
|
+
}: {
|
|
182
|
+
theme: Theme;
|
|
183
|
+
fixedWidth?: boolean;
|
|
184
|
+
}): CSSObject => ({
|
|
185
|
+
...theme.textStyles.small,
|
|
186
|
+
color: theme.colors.textDisabled,
|
|
187
|
+
...(fixedWidth && {
|
|
188
|
+
width: "0.9rem",
|
|
189
|
+
textAlign: "center",
|
|
190
|
+
}),
|
|
191
|
+
})
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
export const KeyboardShortcut = memo(function KeyboardShortcut({
|
|
195
|
+
shortcut,
|
|
196
|
+
}: {
|
|
197
|
+
shortcut: string;
|
|
198
|
+
}) {
|
|
199
|
+
const platform = useDesignSystemConfiguration().platform;
|
|
200
|
+
|
|
201
|
+
const { keys, separator } = getShortcutDisplayParts(shortcut, platform);
|
|
202
|
+
|
|
203
|
+
const keyElements = keys.map((key) => (
|
|
204
|
+
<ShortcutElement
|
|
205
|
+
key={key}
|
|
206
|
+
fixedWidth={platform === "mac" && key.length === 1}
|
|
207
|
+
>
|
|
208
|
+
{key}
|
|
209
|
+
</ShortcutElement>
|
|
210
|
+
));
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
<>
|
|
214
|
+
{separator
|
|
215
|
+
? withSeparatorElements(
|
|
216
|
+
keyElements,
|
|
217
|
+
<ShortcutElement>{separator}</ShortcutElement>
|
|
218
|
+
)
|
|
219
|
+
: keyElements}
|
|
220
|
+
</>
|
|
221
|
+
);
|
|
222
|
+
});
|