@noya-app/noya-designsystem 0.1.20 → 0.1.22
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 +9 -9
- package/CHANGELOG.md +18 -0
- package/dist/index.d.mts +53 -31
- package/dist/index.d.ts +53 -31
- package/dist/index.js +261 -165
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +297 -205
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -4
- package/src/components/Chip.tsx +90 -78
- package/src/components/ContextMenu.tsx +15 -21
- package/src/components/InputField.tsx +49 -5
- package/src/components/ListView.tsx +9 -0
- package/src/components/Popover.tsx +6 -6
- package/src/components/Select.tsx +1 -1
- package/src/components/SelectMenu.tsx +100 -0
- package/src/components/Stack.tsx +12 -1
- package/src/components/Text.tsx +6 -0
- package/src/components/TreeView.tsx +7 -7
- package/src/components/WorkspaceLayout.tsx +3 -0
- package/src/components/internal/Menu.tsx +2 -2
- package/src/index.tsx +1 -0
- package/src/theme/light.ts +4 -2
- package/src/utils/breakpoints.ts +6 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noya-app/noya-designsystem",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.22",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"module": "./dist/index.mjs",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
"@dnd-kit/core": "3.1.1",
|
|
14
14
|
"@dnd-kit/modifiers": "3.0.0",
|
|
15
15
|
"@dnd-kit/sortable": "4.0.0",
|
|
16
|
-
"@noya-app/noya-colorpicker": "0.1.
|
|
17
|
-
"@noya-app/noya-utils": "0.1.
|
|
18
|
-
"@noya-app/noya-geometry": "0.1.
|
|
16
|
+
"@noya-app/noya-colorpicker": "0.1.10",
|
|
17
|
+
"@noya-app/noya-utils": "0.1.2",
|
|
18
|
+
"@noya-app/noya-geometry": "0.1.4",
|
|
19
19
|
"@noya-app/noya-icons": "0.1.4",
|
|
20
20
|
"@noya-app/noya-keymap": "0.1.2",
|
|
21
21
|
"@radix-ui/primitive": "^1.0.0",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"@radix-ui/react-toggle-group": "^1.0.1",
|
|
35
35
|
"@radix-ui/react-tooltip": "^1.0.2",
|
|
36
36
|
"@radix-ui/react-utils": "^0.0.5",
|
|
37
|
+
"@radix-ui/react-select": "^2.1.1",
|
|
37
38
|
"immer": "9.0.5",
|
|
38
39
|
"kiwi.js": "^1.1.3",
|
|
39
40
|
"react-resizable-panels": "2.0.22",
|
package/src/components/Chip.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Cross1Icon, PlusIcon } from "@noya-app/noya-icons";
|
|
2
|
-
import React, { memo } from "react";
|
|
2
|
+
import React, { forwardRef, memo } from "react";
|
|
3
3
|
import styled from "styled-components";
|
|
4
4
|
import { useDesignSystemTheme } from "../contexts/DesignSystemConfiguration";
|
|
5
5
|
import { useHover } from "../hooks/useHover";
|
|
@@ -46,7 +46,8 @@ const ChipElement = styled.span<{
|
|
|
46
46
|
userSelect: "none",
|
|
47
47
|
display: "inline-flex",
|
|
48
48
|
alignItems: "center",
|
|
49
|
-
lineHeight: "1.4",
|
|
49
|
+
// lineHeight: "1.4",
|
|
50
|
+
lineHeight: "15px",
|
|
50
51
|
whiteSpace: "pre",
|
|
51
52
|
...($size === "medium"
|
|
52
53
|
? {
|
|
@@ -56,7 +57,6 @@ const ChipElement = styled.span<{
|
|
|
56
57
|
: {
|
|
57
58
|
fontSize: "9px",
|
|
58
59
|
padding: "0px 4px",
|
|
59
|
-
lineHeight: "15px",
|
|
60
60
|
}),
|
|
61
61
|
...($monospace && {
|
|
62
62
|
fontFamily: theme.fonts.monospace,
|
|
@@ -141,10 +141,13 @@ const AddElement = styled(PlusIcon).withConfig({
|
|
|
141
141
|
}));
|
|
142
142
|
|
|
143
143
|
export interface ChipProps {
|
|
144
|
+
id?: string;
|
|
145
|
+
className?: string;
|
|
144
146
|
colorScheme?: ChipColorScheme;
|
|
145
147
|
variant?: ChipVariant;
|
|
146
148
|
size?: ChipSize;
|
|
147
149
|
children?: React.ReactNode;
|
|
150
|
+
clickable?: boolean;
|
|
148
151
|
deletable?: boolean;
|
|
149
152
|
addable?: boolean;
|
|
150
153
|
monospace?: boolean;
|
|
@@ -156,78 +159,87 @@ export interface ChipProps {
|
|
|
156
159
|
tabIndex?: number;
|
|
157
160
|
}
|
|
158
161
|
|
|
159
|
-
export const Chip = memo(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
162
|
+
export const Chip = memo(
|
|
163
|
+
forwardRef(function Chip(
|
|
164
|
+
{
|
|
165
|
+
colorScheme,
|
|
166
|
+
children,
|
|
167
|
+
deletable,
|
|
168
|
+
addable,
|
|
169
|
+
clickable,
|
|
170
|
+
style,
|
|
171
|
+
size = "medium",
|
|
172
|
+
variant = "solid",
|
|
173
|
+
monospace = false,
|
|
174
|
+
tabIndex,
|
|
175
|
+
onDelete,
|
|
176
|
+
onClick,
|
|
177
|
+
onAdd,
|
|
178
|
+
onHoverDeleteChange,
|
|
179
|
+
...rest
|
|
180
|
+
}: ChipProps,
|
|
181
|
+
forwardedRef: React.ForwardedRef<HTMLSpanElement>
|
|
182
|
+
) {
|
|
183
|
+
const theme = useDesignSystemTheme();
|
|
184
|
+
|
|
185
|
+
const { hoverProps: hoverDeleteProps } = useHover({
|
|
186
|
+
onHoverChange: onHoverDeleteChange,
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const handleClick = !children && !deletable && addable ? onAdd : onClick;
|
|
190
|
+
|
|
191
|
+
const color =
|
|
192
|
+
colorScheme === "primary"
|
|
193
|
+
? theme.colors.primary
|
|
194
|
+
: colorScheme === "secondary"
|
|
195
|
+
? theme.colors.secondary
|
|
196
|
+
: theme.colors.text;
|
|
197
|
+
|
|
198
|
+
return (
|
|
199
|
+
<ChipElement
|
|
200
|
+
ref={forwardedRef}
|
|
201
|
+
$variant={variant}
|
|
202
|
+
$colorScheme={colorScheme}
|
|
203
|
+
style={style}
|
|
204
|
+
onClick={handleClick}
|
|
205
|
+
$size={size}
|
|
206
|
+
$monospace={monospace}
|
|
207
|
+
$isInteractive={!!handleClick || clickable === true}
|
|
208
|
+
tabIndex={tabIndex}
|
|
209
|
+
{...rest}
|
|
210
|
+
>
|
|
211
|
+
{addable && (
|
|
212
|
+
<AddElement
|
|
213
|
+
size={size}
|
|
214
|
+
isOnlyChild={!children && !deletable}
|
|
215
|
+
onClick={
|
|
216
|
+
onAdd
|
|
217
|
+
? (event) => {
|
|
218
|
+
event.stopPropagation();
|
|
219
|
+
onAdd?.();
|
|
220
|
+
}
|
|
221
|
+
: undefined
|
|
222
|
+
}
|
|
223
|
+
color={color}
|
|
224
|
+
/>
|
|
225
|
+
)}
|
|
226
|
+
{children}
|
|
227
|
+
{deletable && (
|
|
228
|
+
<DeleteElement
|
|
229
|
+
size={size}
|
|
230
|
+
{...(hoverDeleteProps as any)}
|
|
231
|
+
onClick={
|
|
232
|
+
onDelete
|
|
233
|
+
? (event) => {
|
|
234
|
+
event.stopPropagation();
|
|
235
|
+
onDelete?.();
|
|
236
|
+
}
|
|
237
|
+
: undefined
|
|
238
|
+
}
|
|
239
|
+
color={color}
|
|
240
|
+
/>
|
|
241
|
+
)}
|
|
242
|
+
</ChipElement>
|
|
243
|
+
);
|
|
244
|
+
})
|
|
245
|
+
);
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import { CheckIcon, ChevronRightIcon } from
|
|
2
|
-
import { useKeyboardShortcuts } from
|
|
3
|
-
import * as RadixContextMenu from
|
|
4
|
-
import React, {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
memo,
|
|
8
|
-
useCallback,
|
|
9
|
-
useMemo,
|
|
10
|
-
} from 'react';
|
|
11
|
-
import styled from 'styled-components';
|
|
12
|
-
import { Spacer } from './Spacer';
|
|
1
|
+
import { CheckIcon, ChevronRightIcon } from "@noya-app/noya-icons";
|
|
2
|
+
import { useKeyboardShortcuts } from "@noya-app/noya-keymap";
|
|
3
|
+
import * as RadixContextMenu from "@radix-ui/react-context-menu";
|
|
4
|
+
import React, { ReactNode, memo, useCallback, useMemo } from "react";
|
|
5
|
+
import styled from "styled-components";
|
|
6
|
+
import { Spacer } from "./Spacer";
|
|
13
7
|
import {
|
|
14
8
|
CHECKBOX_RIGHT_INSET,
|
|
15
9
|
CHECKBOX_WIDTH,
|
|
@@ -18,14 +12,14 @@ import {
|
|
|
18
12
|
SEPARATOR_ITEM,
|
|
19
13
|
getKeyboardShortcutsForMenuItems,
|
|
20
14
|
styles,
|
|
21
|
-
} from
|
|
15
|
+
} from "./internal/Menu";
|
|
22
16
|
|
|
23
17
|
/* ----------------------------------------------------------------------------
|
|
24
18
|
* Separator
|
|
25
19
|
* ------------------------------------------------------------------------- */
|
|
26
20
|
|
|
27
21
|
const SeparatorElement = styled(RadixContextMenu.Separator)(
|
|
28
|
-
styles.separatorStyle
|
|
22
|
+
styles.separatorStyle
|
|
29
23
|
);
|
|
30
24
|
|
|
31
25
|
/* ----------------------------------------------------------------------------
|
|
@@ -35,11 +29,11 @@ const SeparatorElement = styled(RadixContextMenu.Separator)(
|
|
|
35
29
|
const ItemElement = styled(RadixContextMenu.Item)(styles.itemStyle);
|
|
36
30
|
|
|
37
31
|
const CheckboxItemElement = styled(RadixContextMenu.CheckboxItem)(
|
|
38
|
-
styles.itemStyle
|
|
32
|
+
styles.itemStyle
|
|
39
33
|
);
|
|
40
34
|
|
|
41
35
|
const StyledItemIndicator = styled(RadixContextMenu.ItemIndicator)(
|
|
42
|
-
styles.itemIndicatorStyle
|
|
36
|
+
styles.itemIndicatorStyle
|
|
43
37
|
);
|
|
44
38
|
|
|
45
39
|
export interface MenuItemProps<T extends string> {
|
|
@@ -50,7 +44,7 @@ export interface MenuItemProps<T extends string> {
|
|
|
50
44
|
disabled: boolean;
|
|
51
45
|
indented: boolean;
|
|
52
46
|
shortcut?: string;
|
|
53
|
-
icon?:
|
|
47
|
+
icon?: ReactNode;
|
|
54
48
|
items?: MenuItem<T>[];
|
|
55
49
|
}
|
|
56
50
|
|
|
@@ -69,7 +63,7 @@ const ContextMenuItem = memo(function ContextMenuItem<T extends string>({
|
|
|
69
63
|
// context menu unless we stop it here.
|
|
70
64
|
const handlePointerDown = useCallback(
|
|
71
65
|
(event: React.PointerEvent) => event.stopPropagation(),
|
|
72
|
-
[]
|
|
66
|
+
[]
|
|
73
67
|
);
|
|
74
68
|
|
|
75
69
|
const handleSelectItem = useCallback(() => {
|
|
@@ -162,7 +156,7 @@ function ContextMenuRoot<T extends string>({
|
|
|
162
156
|
onOpenChange,
|
|
163
157
|
}: MenuProps<T>) {
|
|
164
158
|
const hasCheckedItem = items.some(
|
|
165
|
-
(item) => item !== SEPARATOR_ITEM && item.checked
|
|
159
|
+
(item) => item !== SEPARATOR_ITEM && item.checked
|
|
166
160
|
);
|
|
167
161
|
|
|
168
162
|
const keymap = useMemo(
|
|
@@ -170,7 +164,7 @@ function ContextMenuRoot<T extends string>({
|
|
|
170
164
|
isNested || shouldBindKeyboardShortcuts === false
|
|
171
165
|
? {}
|
|
172
166
|
: getKeyboardShortcutsForMenuItems(items, onSelect),
|
|
173
|
-
[isNested, items, onSelect, shouldBindKeyboardShortcuts]
|
|
167
|
+
[isNested, items, onSelect, shouldBindKeyboardShortcuts]
|
|
174
168
|
);
|
|
175
169
|
|
|
176
170
|
useKeyboardShortcuts(keymap);
|
|
@@ -213,7 +207,7 @@ function ContextMenuRoot<T extends string>({
|
|
|
213
207
|
>
|
|
214
208
|
{item.title}
|
|
215
209
|
</ContextMenuItem>
|
|
216
|
-
)
|
|
210
|
+
)
|
|
217
211
|
)}
|
|
218
212
|
</ContentComponent>
|
|
219
213
|
</RadixContextMenu.Portal>
|
|
@@ -15,6 +15,7 @@ import React, {
|
|
|
15
15
|
useLayoutEffect,
|
|
16
16
|
useMemo,
|
|
17
17
|
useRef,
|
|
18
|
+
useState,
|
|
18
19
|
} from "react";
|
|
19
20
|
import styled from "styled-components";
|
|
20
21
|
import handleNudge from "../utils/handleNudge";
|
|
@@ -322,7 +323,11 @@ export const InputElement = styled(TextInput)<{
|
|
|
322
323
|
|
|
323
324
|
const InputFieldInput = forwardRef(function InputFieldInput(
|
|
324
325
|
// onFocusChange should only be passed from the root, never directly
|
|
325
|
-
|
|
326
|
+
{
|
|
327
|
+
textAlign,
|
|
328
|
+
variant,
|
|
329
|
+
...rest
|
|
330
|
+
}: TextInputProps & {
|
|
326
331
|
textAlign?: Property.TextAlign;
|
|
327
332
|
variant?: "bare";
|
|
328
333
|
},
|
|
@@ -357,8 +362,10 @@ const InputFieldInput = forwardRef(function InputFieldInput(
|
|
|
357
362
|
$hasLabel={hasLabel}
|
|
358
363
|
$hasDropdown={hasDropdown}
|
|
359
364
|
$size={size}
|
|
365
|
+
$variant={variant}
|
|
366
|
+
$textAlign={textAlign}
|
|
360
367
|
onFocusChange={handleFocusChange}
|
|
361
|
-
{...
|
|
368
|
+
{...rest}
|
|
362
369
|
/>
|
|
363
370
|
);
|
|
364
371
|
});
|
|
@@ -425,10 +432,15 @@ function parseNumber(value: string) {
|
|
|
425
432
|
}
|
|
426
433
|
|
|
427
434
|
function InputFieldNumberInput(props: InputFieldNumberInputProps) {
|
|
428
|
-
const { value, placeholder, onNudge, ...rest } = props;
|
|
435
|
+
const { value, placeholder, onNudge, onBlur, ...rest } = props;
|
|
429
436
|
const onSubmit = "onSubmit" in props ? props.onSubmit : undefined;
|
|
430
437
|
const onChange = "onChange" in props ? props.onChange : undefined;
|
|
431
438
|
|
|
439
|
+
// Internal value is used to handle empty string as a valid state,
|
|
440
|
+
// e.g. when the user deletes the value before entering a new one.
|
|
441
|
+
// This is only used for in the onChange case.
|
|
442
|
+
const [internalValue, setInternalValue] = useState<string | undefined>();
|
|
443
|
+
|
|
432
444
|
const handleSubmit = useCallback(
|
|
433
445
|
(value: string) => {
|
|
434
446
|
const newValue = parseNumber(value);
|
|
@@ -447,6 +459,7 @@ function InputFieldNumberInput(props: InputFieldNumberInputProps) {
|
|
|
447
459
|
if (!amount) return;
|
|
448
460
|
|
|
449
461
|
onNudge?.(amount);
|
|
462
|
+
setInternalValue(undefined);
|
|
450
463
|
|
|
451
464
|
event.preventDefault();
|
|
452
465
|
event.stopPropagation();
|
|
@@ -456,17 +469,48 @@ function InputFieldNumberInput(props: InputFieldNumberInputProps) {
|
|
|
456
469
|
|
|
457
470
|
const handleChange = useCallback(
|
|
458
471
|
(value: string) => {
|
|
459
|
-
|
|
472
|
+
if (value === "" || value === "-" || value === ".") {
|
|
473
|
+
setInternalValue(value);
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (value.endsWith(".")) {
|
|
478
|
+
setInternalValue(value);
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
setInternalValue(undefined);
|
|
483
|
+
|
|
484
|
+
const newValue = parseNumber(value);
|
|
485
|
+
|
|
486
|
+
if (!isNaN(newValue)) {
|
|
487
|
+
onChange?.(newValue);
|
|
488
|
+
}
|
|
460
489
|
},
|
|
461
490
|
[onChange]
|
|
462
491
|
);
|
|
463
492
|
|
|
493
|
+
const handleBlur = useCallback(
|
|
494
|
+
(event: React.FocusEvent<HTMLInputElement>) => {
|
|
495
|
+
setInternalValue(undefined);
|
|
496
|
+
onBlur?.(event);
|
|
497
|
+
},
|
|
498
|
+
[onBlur]
|
|
499
|
+
);
|
|
500
|
+
|
|
464
501
|
return (
|
|
465
502
|
<InputFieldInput
|
|
466
503
|
{...rest}
|
|
467
|
-
value={
|
|
504
|
+
value={
|
|
505
|
+
internalValue !== undefined
|
|
506
|
+
? String(internalValue)
|
|
507
|
+
: value === undefined
|
|
508
|
+
? ""
|
|
509
|
+
: String(value)
|
|
510
|
+
}
|
|
468
511
|
placeholder={placeholder}
|
|
469
512
|
onKeyDown={handleKeyDown}
|
|
513
|
+
onBlur={handleBlur}
|
|
470
514
|
{...("onChange" in props
|
|
471
515
|
? { onChange: handleChange }
|
|
472
516
|
: { onSubmit: handleSubmit })}
|
|
@@ -676,6 +676,9 @@ type ListViewVariant = "normal" | "padded" | "bare";
|
|
|
676
676
|
type ListViewSectionHeaderVariant = "normal" | "label";
|
|
677
677
|
|
|
678
678
|
type ListViewRootProps = {
|
|
679
|
+
id?: string;
|
|
680
|
+
className?: string;
|
|
681
|
+
style?: CSSProperties;
|
|
679
682
|
onPress?: () => void;
|
|
680
683
|
scrollable?: boolean;
|
|
681
684
|
expandable?: boolean;
|
|
@@ -696,6 +699,9 @@ type ListViewRootProps = {
|
|
|
696
699
|
|
|
697
700
|
const ListViewRootInner = forwardRef(function ListViewRootInner<T>(
|
|
698
701
|
{
|
|
702
|
+
id,
|
|
703
|
+
className,
|
|
704
|
+
style,
|
|
699
705
|
onPress,
|
|
700
706
|
scrollable = false,
|
|
701
707
|
expandable = true,
|
|
@@ -895,6 +901,9 @@ const ListViewRootInner = forwardRef(function ListViewRootInner<T>(
|
|
|
895
901
|
return (
|
|
896
902
|
<DraggingContext.Provider value={draggingContextValue}>
|
|
897
903
|
<RootContainer
|
|
904
|
+
id={id}
|
|
905
|
+
className={className}
|
|
906
|
+
style={style}
|
|
898
907
|
{...{
|
|
899
908
|
[pressEventName]: handleClick,
|
|
900
909
|
}}
|
|
@@ -93,12 +93,12 @@ export function Popover({
|
|
|
93
93
|
collisionPadding={8}
|
|
94
94
|
arrowPadding={2}
|
|
95
95
|
// Don't propagate pointer down events to the canvas
|
|
96
|
-
onPointerDown={(event) => {
|
|
97
|
-
|
|
98
|
-
}}
|
|
99
|
-
onWheel={(event) => {
|
|
100
|
-
|
|
101
|
-
}}
|
|
96
|
+
// onPointerDown={(event) => {
|
|
97
|
+
// event.stopPropagation();
|
|
98
|
+
// }}
|
|
99
|
+
// onWheel={(event) => {
|
|
100
|
+
// event.stopPropagation();
|
|
101
|
+
// }}
|
|
102
102
|
>
|
|
103
103
|
{children}
|
|
104
104
|
{showArrow && <ArrowElement />}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { DropdownChevronIcon } from "@noya-app/noya-icons";
|
|
2
|
+
import * as Select from "@radix-ui/react-select";
|
|
3
|
+
import React, { memo } from "react";
|
|
4
|
+
import { styled } from "styled-components";
|
|
5
|
+
import { Button } from "./Button";
|
|
6
|
+
import { Spacer } from "./Spacer";
|
|
7
|
+
import { MenuItem, RegularMenuItem, styles } from "./internal/Menu";
|
|
8
|
+
|
|
9
|
+
type Props<T extends string> = {
|
|
10
|
+
id?: string;
|
|
11
|
+
style?: React.CSSProperties;
|
|
12
|
+
className?: string;
|
|
13
|
+
menuItems: MenuItem<T>[];
|
|
14
|
+
value: T;
|
|
15
|
+
onSelect?: (value: T) => void;
|
|
16
|
+
placeholder?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const SelectMenu = memo(function SelectMenu<T extends string = string>({
|
|
20
|
+
id,
|
|
21
|
+
style,
|
|
22
|
+
className,
|
|
23
|
+
menuItems,
|
|
24
|
+
value,
|
|
25
|
+
onSelect,
|
|
26
|
+
placeholder,
|
|
27
|
+
}: Props<T>) {
|
|
28
|
+
const selectedItem = menuItems.find(
|
|
29
|
+
(item): item is RegularMenuItem<T> =>
|
|
30
|
+
typeof item !== "string" && item.value === value
|
|
31
|
+
);
|
|
32
|
+
const icon = selectedItem?.icon;
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<Select.Root value={value} onValueChange={onSelect}>
|
|
36
|
+
<Select.SelectTrigger asChild>
|
|
37
|
+
<Button id={id} style={style} className={className}>
|
|
38
|
+
{icon && (
|
|
39
|
+
<>
|
|
40
|
+
{icon}
|
|
41
|
+
<Spacer.Horizontal size={8} />
|
|
42
|
+
</>
|
|
43
|
+
)}
|
|
44
|
+
<Select.Value placeholder={placeholder} />
|
|
45
|
+
<Spacer.Horizontal />
|
|
46
|
+
<DropdownChevronIcon />
|
|
47
|
+
</Button>
|
|
48
|
+
</Select.SelectTrigger>
|
|
49
|
+
<Select.Portal>
|
|
50
|
+
<SelectContent>
|
|
51
|
+
<SelectViewport>
|
|
52
|
+
{menuItems.map((menuItem) => {
|
|
53
|
+
if (typeof menuItem === "string") {
|
|
54
|
+
return <StyledSeparator />;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const value = menuItem.value ?? "";
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<SelectItem key={value} value={value} icon={menuItem.icon}>
|
|
61
|
+
{menuItem.title ?? value}
|
|
62
|
+
</SelectItem>
|
|
63
|
+
);
|
|
64
|
+
})}
|
|
65
|
+
</SelectViewport>
|
|
66
|
+
</SelectContent>
|
|
67
|
+
</Select.Portal>
|
|
68
|
+
</Select.Root>
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const SelectContent = styled(Select.Content)(styles.contentStyle);
|
|
73
|
+
|
|
74
|
+
const SelectViewport = styled(Select.Viewport)({});
|
|
75
|
+
|
|
76
|
+
const SelectItem = React.forwardRef(
|
|
77
|
+
(
|
|
78
|
+
{
|
|
79
|
+
children,
|
|
80
|
+
icon,
|
|
81
|
+
...props
|
|
82
|
+
}: Select.SelectItemProps & { icon?: React.ReactNode },
|
|
83
|
+
forwardedRef: React.ForwardedRef<HTMLDivElement>
|
|
84
|
+
) => {
|
|
85
|
+
return (
|
|
86
|
+
<StyledItem {...props} ref={forwardedRef}>
|
|
87
|
+
{icon && (
|
|
88
|
+
<>
|
|
89
|
+
{icon}
|
|
90
|
+
<Spacer.Horizontal size={8} />
|
|
91
|
+
</>
|
|
92
|
+
)}
|
|
93
|
+
<Select.ItemText>{children}</Select.ItemText>
|
|
94
|
+
</StyledItem>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const StyledItem = styled(Select.Item)(styles.itemStyle);
|
|
100
|
+
const StyledSeparator = styled(Select.Separator)(styles.separatorStyle);
|
package/src/components/Stack.tsx
CHANGED
|
@@ -89,7 +89,17 @@ const Element = styled.div<{
|
|
|
89
89
|
}));
|
|
90
90
|
|
|
91
91
|
const StackBase = forwardRef(function StackBase(
|
|
92
|
-
{
|
|
92
|
+
{
|
|
93
|
+
id,
|
|
94
|
+
className,
|
|
95
|
+
as,
|
|
96
|
+
children,
|
|
97
|
+
separator,
|
|
98
|
+
breakpoints,
|
|
99
|
+
tabIndex,
|
|
100
|
+
href,
|
|
101
|
+
...rest
|
|
102
|
+
}: Props,
|
|
93
103
|
forwardedRef: ForwardedRef<HTMLElement>
|
|
94
104
|
) {
|
|
95
105
|
const elements = separator
|
|
@@ -107,6 +117,7 @@ const StackBase = forwardRef(function StackBase(
|
|
|
107
117
|
<Element
|
|
108
118
|
ref={forwardedRef as any}
|
|
109
119
|
id={id}
|
|
120
|
+
className={className}
|
|
110
121
|
as={as}
|
|
111
122
|
$styleProps={styleProps}
|
|
112
123
|
$breakpoints={breakpoints}
|
package/src/components/Text.tsx
CHANGED
|
@@ -61,6 +61,8 @@ interface Props extends StyleProps {
|
|
|
61
61
|
children: ReactNode;
|
|
62
62
|
onClick?: () => void;
|
|
63
63
|
onDoubleClick?: () => void;
|
|
64
|
+
onKeyDown?: (event: React.KeyboardEvent<HTMLSpanElement>) => void;
|
|
65
|
+
tabIndex?: number;
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
const StyledElement = styled.span<{
|
|
@@ -84,7 +86,9 @@ export const Text = forwardRef(function Text(
|
|
|
84
86
|
className,
|
|
85
87
|
color,
|
|
86
88
|
href,
|
|
89
|
+
tabIndex,
|
|
87
90
|
onClick,
|
|
91
|
+
onKeyDown,
|
|
88
92
|
...rest
|
|
89
93
|
}: Props,
|
|
90
94
|
forwardedRef: ForwardedRef<HTMLElement>
|
|
@@ -96,11 +100,13 @@ export const Text = forwardRef(function Text(
|
|
|
96
100
|
ref={forwardedRef}
|
|
97
101
|
as={element}
|
|
98
102
|
className={className}
|
|
103
|
+
tabIndex={tabIndex}
|
|
99
104
|
$variant={variant}
|
|
100
105
|
$breakpoints={breakpoints}
|
|
101
106
|
$styleProps={rest}
|
|
102
107
|
$color={color}
|
|
103
108
|
onClick={onClick}
|
|
109
|
+
onKeyDown={onKeyDown}
|
|
104
110
|
{...((href && { href }) as any)}
|
|
105
111
|
>
|
|
106
112
|
{children}
|