@noya-app/noya-designsystem 0.1.51 → 0.1.53
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 +10 -13
- package/CHANGELOG.md +17 -0
- package/dist/index.css +1 -1
- package/dist/index.d.mts +307 -12
- package/dist/index.d.ts +307 -12
- package/dist/index.js +3408 -647
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3660 -908
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -9
- package/postcss.config.js +7 -0
- package/src/components/ActionMenu.tsx +8 -3
- package/src/components/Banner.tsx +2 -2
- package/src/components/BaseToolbar.tsx +6 -2
- package/src/components/Breadcrumbs.tsx +32 -2
- package/src/components/Button.tsx +30 -19
- package/src/components/Chip.tsx +3 -0
- package/src/components/ColorSwatch.tsx +42 -0
- package/src/components/ColorSwatchControl.tsx +90 -0
- package/src/components/ContextMenu.tsx +10 -2
- package/src/components/Dialog.tsx +1 -1
- package/src/components/DraggableMenuButton.tsx +17 -30
- package/src/components/DropdownMenu.tsx +7 -0
- package/src/components/FillInputField.tsx +1 -1
- package/src/components/Grid.tsx +11 -4
- package/src/components/GridView.tsx +1 -1
- package/src/components/LabeledField.tsx +4 -1
- package/src/components/List.tsx +12 -7
- package/src/components/ListView.tsx +7 -4
- package/src/components/MediaThumbnail.tsx +44 -10
- package/src/components/Popover.tsx +8 -1
- package/src/components/SegmentedControl.tsx +1 -1
- package/src/components/SelectMenu.tsx +7 -0
- package/src/components/SelectionToolbar.tsx +76 -0
- package/src/components/Switch.tsx +1 -1
- package/src/components/Text.tsx +2 -2
- package/src/components/Toast.tsx +13 -7
- package/src/components/Toolbar.tsx +78 -37
- package/src/components/Tooltip.tsx +11 -2
- package/src/components/catppuccin/fileIcons.ts +2430 -0
- package/src/components/connected-users-menu/ConnectedUsersMenuLayout.tsx +3 -1
- package/src/components/internal/Menu.tsx +6 -5
- package/src/components/internal/MenuViewport.tsx +11 -1
- package/src/components/pipeline/PipelineResultLayout.tsx +18 -0
- package/src/contexts/DialogContext.tsx +20 -5
- package/src/contexts/PortalScopeContext.tsx +48 -0
- package/src/index.css +101 -39
- package/src/index.tsx +4 -0
- package/src/theme/index.ts +6 -1
- package/src/theme/themeUtils.ts +10 -3
- package/src/utils/classNames.ts +3 -2
- package/src/utils/moveTreeItem.ts +4 -4
- package/tsup.config.ts +1 -1
|
@@ -29,11 +29,13 @@ export const LabeledField = memo(function LabeledField({
|
|
|
29
29
|
minWidth = 100,
|
|
30
30
|
className,
|
|
31
31
|
style,
|
|
32
|
+
labelStyle: labelStyleProp,
|
|
32
33
|
...props
|
|
33
34
|
}: {
|
|
34
35
|
children: React.ReactNode;
|
|
35
36
|
label: React.ReactNode;
|
|
36
37
|
labelPosition?: LabelPosition;
|
|
38
|
+
labelStyle?: React.CSSProperties;
|
|
37
39
|
/** Used for both the `id` of the field and the `htmlFor` of the label, unless `id` and `htmlFor` are explicitly provided */
|
|
38
40
|
fieldId?: string;
|
|
39
41
|
className?: string;
|
|
@@ -79,8 +81,9 @@ export const LabeledField = memo(function LabeledField({
|
|
|
79
81
|
const labelStyle = useMemo(
|
|
80
82
|
() => ({
|
|
81
83
|
minWidth: `calc(${labelWidth}px - ${indentLevel * 8}px)`,
|
|
84
|
+
...labelStyleProp,
|
|
82
85
|
}),
|
|
83
|
-
[labelWidth, indentLevel]
|
|
86
|
+
[labelWidth, indentLevel, labelStyleProp]
|
|
84
87
|
);
|
|
85
88
|
|
|
86
89
|
return (
|
package/src/components/List.tsx
CHANGED
|
@@ -17,7 +17,7 @@ import { ListView, ListViewItemInfo } from "./ListView";
|
|
|
17
17
|
import { TreeView } from "./TreeView";
|
|
18
18
|
|
|
19
19
|
const cssVarOverrides = {
|
|
20
|
-
"--n-icon-selected": cssVars.colors.
|
|
20
|
+
"--n-icon-selected": cssVars.colors.selectedListItemText,
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
const emptyArray: string[] = [];
|
|
@@ -117,7 +117,7 @@ export const List = memoGeneric(
|
|
|
117
117
|
variant="bare"
|
|
118
118
|
className={cx(
|
|
119
119
|
className,
|
|
120
|
-
isDropTargetActive && "bg-
|
|
120
|
+
isDropTargetActive && "bg-selected-list-item-background",
|
|
121
121
|
scrollable && "basis-0 min-h-0"
|
|
122
122
|
)}
|
|
123
123
|
containerRef={fileDropTargetRef}
|
|
@@ -174,6 +174,14 @@ export const List = memoGeneric(
|
|
|
174
174
|
selected={isSelected}
|
|
175
175
|
onOpenChange={onOpenChange}
|
|
176
176
|
variant="bare"
|
|
177
|
+
style={{
|
|
178
|
+
backgroundColor: isSelected
|
|
179
|
+
? cssVars.colors.selectedListItemBackground
|
|
180
|
+
: "transparent",
|
|
181
|
+
color: isSelected
|
|
182
|
+
? cssVars.colors.selectedListItemText
|
|
183
|
+
: undefined,
|
|
184
|
+
}}
|
|
177
185
|
/>
|
|
178
186
|
));
|
|
179
187
|
|
|
@@ -193,9 +201,6 @@ export const List = memoGeneric(
|
|
|
193
201
|
aria-label={getName(item)}
|
|
194
202
|
aria-selected={isSelected}
|
|
195
203
|
className={cx("cursor-pointer rounded px-1", pyMap[size])}
|
|
196
|
-
backgroundColor={
|
|
197
|
-
isSelected ? cssVars.colors.primaryPastel : undefined
|
|
198
|
-
}
|
|
199
204
|
testRelativeDropPosition={
|
|
200
205
|
testShowDropIndicatorId === id ? "inside" : undefined
|
|
201
206
|
}
|
|
@@ -245,7 +250,7 @@ export const List = memoGeneric(
|
|
|
245
250
|
className={cx(
|
|
246
251
|
"overflow-hidden flex-none flex items-center justify-center",
|
|
247
252
|
thumbnailSize === "auto" && thumbnailSizeMap[size],
|
|
248
|
-
isSelected && "text-
|
|
253
|
+
isSelected && "text-selected-list-item-text",
|
|
249
254
|
size === "small" ? "rounded-sm" : "rounded"
|
|
250
255
|
)}
|
|
251
256
|
tabIndex={-1}
|
|
@@ -274,7 +279,7 @@ export const List = memoGeneric(
|
|
|
274
279
|
<ViewComponent.RowTitle
|
|
275
280
|
className={cx(
|
|
276
281
|
fontStyleMap[size],
|
|
277
|
-
isSelected && "text-
|
|
282
|
+
isSelected && "text-selected-list-item-text"
|
|
278
283
|
)}
|
|
279
284
|
>
|
|
280
285
|
{getName(item)}
|
|
@@ -232,7 +232,7 @@ const RowContainer = forwardRef<
|
|
|
232
232
|
: "font-sans text-heading5 font-normal",
|
|
233
233
|
!$isSectionHeader &&
|
|
234
234
|
$selected &&
|
|
235
|
-
"text-
|
|
235
|
+
"text-selected-list-item-text active:bg-selected-list-item-background",
|
|
236
236
|
!$isSectionHeader && $disabled && "text-text-disabled",
|
|
237
237
|
$clickable ? "cursor-pointer" : "cursor-default",
|
|
238
238
|
$isSectionHeader && "bg-listview-raised-background",
|
|
@@ -254,7 +254,10 @@ const RowContainer = forwardRef<
|
|
|
254
254
|
}),
|
|
255
255
|
...($selected &&
|
|
256
256
|
!$isSectionHeader && {
|
|
257
|
-
backgroundColor:
|
|
257
|
+
backgroundColor:
|
|
258
|
+
$colorScheme === "primary"
|
|
259
|
+
? cssVars.colors["selectedListItemBackground"]
|
|
260
|
+
: cssVars.colors[$colorScheme],
|
|
258
261
|
}),
|
|
259
262
|
...($selected &&
|
|
260
263
|
!$isSectionHeader &&
|
|
@@ -279,7 +282,7 @@ const RowContainer = forwardRef<
|
|
|
279
282
|
backgroundColor: $selected
|
|
280
283
|
? $colorScheme === "secondary"
|
|
281
284
|
? cssVars.colors.secondaryLight
|
|
282
|
-
: cssVars.colors.
|
|
285
|
+
: cssVars.colors.selectedListItemBackground
|
|
283
286
|
: cssVars.colors.activeBackground,
|
|
284
287
|
},
|
|
285
288
|
}),
|
|
@@ -358,7 +361,7 @@ const ListViewDragIndicatorElement = forwardRef<
|
|
|
358
361
|
backgroundColor:
|
|
359
362
|
$colorScheme === "secondary"
|
|
360
363
|
? cssVars.colors.secondary
|
|
361
|
-
: cssVars.colors.
|
|
364
|
+
: cssVars.colors.selectedListItemBackground,
|
|
362
365
|
border: `2px solid white`,
|
|
363
366
|
boxShadow: "0 0 2px rgba(0,0,0,0.5)",
|
|
364
367
|
}),
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
FileIcon,
|
|
3
|
+
ImageIcon,
|
|
4
|
+
SpeakerLoudIcon,
|
|
5
|
+
VideoIcon,
|
|
6
|
+
} from "@noya-app/noya-icons";
|
|
2
7
|
import React, { memo, useMemo } from "react";
|
|
3
8
|
import { cssVars } from "../theme";
|
|
4
9
|
import { cx } from "../utils/classNames";
|
|
5
10
|
import { CollectionItemSize } from "./Collection";
|
|
6
11
|
import { IconName, Icons } from "./Icons";
|
|
12
|
+
import { fileExtensions, fileNames } from "./catppuccin/fileIcons";
|
|
7
13
|
|
|
8
14
|
type ColorScheme = "primary" | "secondary" | "icon" | "warning";
|
|
9
15
|
|
|
@@ -31,6 +37,8 @@ type MediaThumbnailProps = {
|
|
|
31
37
|
*/
|
|
32
38
|
iconName?: IconName;
|
|
33
39
|
size?: CollectionItemSize;
|
|
40
|
+
|
|
41
|
+
fileName?: string;
|
|
34
42
|
};
|
|
35
43
|
|
|
36
44
|
export const getThumbnailColors = (
|
|
@@ -39,8 +47,8 @@ export const getThumbnailColors = (
|
|
|
39
47
|
) => {
|
|
40
48
|
if (selected) {
|
|
41
49
|
return {
|
|
42
|
-
icon: cssVars.colors.
|
|
43
|
-
background: cssVars.colors.
|
|
50
|
+
icon: cssVars.colors.selectedListItemText,
|
|
51
|
+
background: cssVars.colors.selectedListItemIconBackground,
|
|
44
52
|
};
|
|
45
53
|
}
|
|
46
54
|
|
|
@@ -75,8 +83,9 @@ export const MediaThumbnail = memo(function MediaThumbnail({
|
|
|
75
83
|
className,
|
|
76
84
|
colorScheme = "icon",
|
|
77
85
|
url,
|
|
78
|
-
iconName
|
|
86
|
+
iconName,
|
|
79
87
|
size = "medium",
|
|
88
|
+
fileName,
|
|
80
89
|
}: MediaThumbnailProps) {
|
|
81
90
|
const { icon: iconColor, background: backgroundColor } = getThumbnailColors(
|
|
82
91
|
colorScheme,
|
|
@@ -95,6 +104,7 @@ export const MediaThumbnail = memo(function MediaThumbnail({
|
|
|
95
104
|
switch (contentType) {
|
|
96
105
|
case "image":
|
|
97
106
|
if (!url) return <ImageIcon color={iconColor} style={iconStyles} />;
|
|
107
|
+
|
|
98
108
|
return (
|
|
99
109
|
<img
|
|
100
110
|
src={url}
|
|
@@ -103,18 +113,32 @@ export const MediaThumbnail = memo(function MediaThumbnail({
|
|
|
103
113
|
tabIndex={-1}
|
|
104
114
|
/>
|
|
105
115
|
);
|
|
106
|
-
|
|
107
116
|
case "video":
|
|
108
117
|
return <VideoIcon color={iconColor} style={iconStyles} />;
|
|
109
|
-
|
|
110
118
|
case "audio":
|
|
111
119
|
return <SpeakerLoudIcon color={iconColor} style={iconStyles} />;
|
|
112
|
-
|
|
113
120
|
default:
|
|
114
|
-
|
|
115
|
-
|
|
121
|
+
if (iconName) {
|
|
122
|
+
const Icon = Icons[iconName];
|
|
123
|
+
return <Icon color={iconColor} style={iconStyles} />;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (fileName) {
|
|
127
|
+
const fileIcon = findFileIcon(fileName);
|
|
128
|
+
if (fileIcon) {
|
|
129
|
+
return (
|
|
130
|
+
<img
|
|
131
|
+
src={`https://api.iconify.design/catppuccin/${fileIcon}.svg?height=none&box=1`}
|
|
132
|
+
alt=""
|
|
133
|
+
className="w-4 h-4"
|
|
134
|
+
/>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return <FileIcon color={iconColor} style={iconStyles} />;
|
|
116
140
|
}
|
|
117
|
-
}, [contentType, url, iconColor, iconStyles, iconName]);
|
|
141
|
+
}, [contentType, url, iconColor, iconStyles, iconName, fileName]);
|
|
118
142
|
|
|
119
143
|
return (
|
|
120
144
|
<div
|
|
@@ -137,3 +161,13 @@ const iconSizeMap: Record<CollectionItemSize, number | string> = {
|
|
|
137
161
|
large: "max(15px, 20%)",
|
|
138
162
|
xl: "max(15px, 20%)",
|
|
139
163
|
};
|
|
164
|
+
|
|
165
|
+
function findFileIcon(fileName: string) {
|
|
166
|
+
const fileIcon = fileNames[fileName];
|
|
167
|
+
if (fileIcon) return fileIcon;
|
|
168
|
+
const fileExtensionIndex = fileName.lastIndexOf(".");
|
|
169
|
+
if (fileExtensionIndex === -1) return undefined;
|
|
170
|
+
const fileExtension = fileName.slice(fileExtensionIndex + 1);
|
|
171
|
+
const fileExtensionIcon = fileExtensions[fileExtension];
|
|
172
|
+
return fileExtensionIcon;
|
|
173
|
+
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
2
2
|
import React, { ComponentProps } from "react";
|
|
3
|
+
import {
|
|
4
|
+
portalScopeProps,
|
|
5
|
+
usePortalScopeId,
|
|
6
|
+
} from "../contexts/PortalScopeContext";
|
|
3
7
|
import { cx } from "../utils/classNames";
|
|
4
8
|
import { IconButton } from "./IconButton";
|
|
5
9
|
|
|
@@ -33,7 +37,7 @@ interface Props
|
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
export const popoverStyle = {
|
|
36
|
-
base: "rounded font-[14px] bg-popover-background shadow-
|
|
40
|
+
base: "rounded font-[14px] bg-popover-background shadow-popover max-h-[600px] overflow-y-auto text-text-muted z-[1000]",
|
|
37
41
|
large: "w-[680px]",
|
|
38
42
|
normal: "w-[240px]",
|
|
39
43
|
};
|
|
@@ -56,11 +60,14 @@ export function Popover({
|
|
|
56
60
|
onClickClose,
|
|
57
61
|
className,
|
|
58
62
|
}: Props) {
|
|
63
|
+
const portalScopeId = usePortalScopeId();
|
|
64
|
+
|
|
59
65
|
return (
|
|
60
66
|
<PopoverPrimitive.Root open={open} onOpenChange={onOpenChange}>
|
|
61
67
|
<PopoverPrimitive.Trigger asChild>{trigger}</PopoverPrimitive.Trigger>
|
|
62
68
|
<PopoverPrimitive.Portal>
|
|
63
69
|
<PopoverPrimitive.Content
|
|
70
|
+
{...portalScopeProps(portalScopeId)}
|
|
64
71
|
className={cx(
|
|
65
72
|
popoverStyle.base,
|
|
66
73
|
variant === "large" && popoverStyle.large,
|
|
@@ -80,7 +80,7 @@ const SegmentedControlItem = forwardRef(function SegmentedControlItem<
|
|
|
80
80
|
? "aria-checked:bg-primary aria-checked:text-white"
|
|
81
81
|
: "aria-checked:bg-background aria-checked:text-text",
|
|
82
82
|
"focus:z-interactable",
|
|
83
|
-
"aria-checked:shadow-
|
|
83
|
+
"aria-checked:shadow-segment",
|
|
84
84
|
"whitespace-pre",
|
|
85
85
|
className
|
|
86
86
|
)}
|
|
@@ -15,6 +15,10 @@ import React, {
|
|
|
15
15
|
import { useLabel, useLabelPosition } from "../hooks/useLabel";
|
|
16
16
|
import { cx } from "../utils/classNames";
|
|
17
17
|
|
|
18
|
+
import {
|
|
19
|
+
portalScopeProps,
|
|
20
|
+
usePortalScopeId,
|
|
21
|
+
} from "../contexts/PortalScopeContext";
|
|
18
22
|
import { getInsetEndStyles } from "../utils/inputs";
|
|
19
23
|
import { Button } from "./Button";
|
|
20
24
|
import { renderIcon } from "./Icons";
|
|
@@ -148,6 +152,8 @@ export const SelectMenu = memoGeneric(function SelectMenu<
|
|
|
148
152
|
contentStyle: contentStyleProp,
|
|
149
153
|
...props
|
|
150
154
|
}: Props<T>) {
|
|
155
|
+
const portalScopeId = usePortalScopeId();
|
|
156
|
+
|
|
151
157
|
const selectedItem = items.find(
|
|
152
158
|
(item): item is SelectableMenuItem<T> =>
|
|
153
159
|
isSelectableMenuItem(item) && item.value === value
|
|
@@ -213,6 +219,7 @@ export const SelectMenu = memoGeneric(function SelectMenu<
|
|
|
213
219
|
/>
|
|
214
220
|
<Select.Portal>
|
|
215
221
|
<Select.Content
|
|
222
|
+
{...portalScopeProps(portalScopeId)}
|
|
216
223
|
className={styles.contentStyle}
|
|
217
224
|
position="popper"
|
|
218
225
|
sideOffset={6}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {
|
|
2
|
+
portalScopeProps,
|
|
3
|
+
usePortalScopeId,
|
|
4
|
+
} from "@noya-app/noya-designsystem";
|
|
5
|
+
import { Rect } from "@noya-app/noya-geometry";
|
|
6
|
+
import { useSize } from "@noya-app/react-utils";
|
|
7
|
+
import * as PopperPrimitive from "@radix-ui/react-popper";
|
|
8
|
+
import type { MeasurableElement } from "@radix-ui/utils";
|
|
9
|
+
import React, { CSSProperties, memo, useMemo } from "react";
|
|
10
|
+
|
|
11
|
+
// Create a Measurable from a Rect
|
|
12
|
+
const createVirtualElement = (rect: Rect): MeasurableElement => ({
|
|
13
|
+
getBoundingClientRect: () => ({
|
|
14
|
+
width: rect.width,
|
|
15
|
+
height: rect.height,
|
|
16
|
+
x: rect.x,
|
|
17
|
+
y: rect.y,
|
|
18
|
+
top: rect.y,
|
|
19
|
+
right: rect.x + rect.width,
|
|
20
|
+
bottom: rect.y + rect.height,
|
|
21
|
+
left: rect.x,
|
|
22
|
+
toJSON: () => null,
|
|
23
|
+
}),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
type SelectionToolbarContainerProps = {
|
|
27
|
+
rect: Rect;
|
|
28
|
+
children: React.ReactNode;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const SelectionToolbarContainer = memo(
|
|
32
|
+
function SelectionToolbarContainer({
|
|
33
|
+
rect,
|
|
34
|
+
children,
|
|
35
|
+
}: SelectionToolbarContainerProps) {
|
|
36
|
+
const portalScopeId = usePortalScopeId();
|
|
37
|
+
const containerRef = React.useRef<HTMLDivElement>(null);
|
|
38
|
+
const size = useSize(containerRef, "width");
|
|
39
|
+
|
|
40
|
+
// Create a virtual reference element from the rect
|
|
41
|
+
const virtualRef = useMemo(
|
|
42
|
+
() => ({
|
|
43
|
+
current: createVirtualElement(rect),
|
|
44
|
+
}),
|
|
45
|
+
[rect]
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<PopperPrimitive.Root>
|
|
50
|
+
<PopperPrimitive.Anchor virtualRef={virtualRef} />
|
|
51
|
+
<PopperPrimitive.Content
|
|
52
|
+
{...portalScopeProps(portalScopeId)}
|
|
53
|
+
side="top"
|
|
54
|
+
sideOffset={10}
|
|
55
|
+
align="center"
|
|
56
|
+
avoidCollisions
|
|
57
|
+
collisionPadding={10}
|
|
58
|
+
contentEditable={false}
|
|
59
|
+
style={{
|
|
60
|
+
zIndex: 1000,
|
|
61
|
+
opacity: size ? 1 : 0,
|
|
62
|
+
transition: "opacity 0.2s ease-in-out",
|
|
63
|
+
["--n-input-background" as keyof CSSProperties]: "transparent",
|
|
64
|
+
}}
|
|
65
|
+
>
|
|
66
|
+
<div
|
|
67
|
+
ref={containerRef}
|
|
68
|
+
className="flex gap-1 bg-popover-background rounded shadow-popover p-1"
|
|
69
|
+
>
|
|
70
|
+
{children}
|
|
71
|
+
</div>
|
|
72
|
+
</PopperPrimitive.Content>
|
|
73
|
+
</PopperPrimitive.Root>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
);
|
|
@@ -38,7 +38,7 @@ export const Switch = function Switch({
|
|
|
38
38
|
onChange(newValue);
|
|
39
39
|
}}
|
|
40
40
|
className={cx(
|
|
41
|
-
"all-unset w-8 h-[19px] bg-active-background rounded-full relative cursor-pointer ring-offset-background [webkit-tap-highlight-color:rgba(0,0,0,0)] focus:ring-2 focus:ring-primary focus:ring-offset-[1px] outline-none data-[state=checked]:bg-primary transition-all",
|
|
41
|
+
"all-unset w-8 h-[19px] bg-active-background rounded-full relative cursor-pointer ring-offset-background [-webkit-tap-highlight-color:rgba(0,0,0,0)] focus:ring-2 focus:ring-primary focus:ring-offset-[1px] outline-none data-[state=checked]:bg-primary transition-all",
|
|
42
42
|
colorScheme === "secondary" && "data-[state=checked]:bg-secondary",
|
|
43
43
|
"focus:z-interactable",
|
|
44
44
|
className
|
package/src/components/Text.tsx
CHANGED
|
@@ -33,8 +33,8 @@ const elements: Record<Variant, keyof ReactHTML> = {
|
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
export const textStyles: Record<Variant, string> = {
|
|
36
|
-
title: "font-sans text-title font-bold
|
|
37
|
-
subtitle: "font-sans text-subtitle font-medium
|
|
36
|
+
title: "font-sans text-title font-bold",
|
|
37
|
+
subtitle: "font-sans text-subtitle font-medium",
|
|
38
38
|
heading1: "font-sans text-heading1 font-semibold",
|
|
39
39
|
heading2: "font-sans text-heading2 font-medium",
|
|
40
40
|
heading3: "font-sans text-heading3 font-normal",
|
package/src/components/Toast.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import * as ToastPrimitive from
|
|
2
|
-
import React, { ReactNode } from
|
|
3
|
-
import { IconButton } from
|
|
4
|
-
import { textStyles } from
|
|
1
|
+
import * as ToastPrimitive from "@radix-ui/react-toast";
|
|
2
|
+
import React, { ReactNode } from "react";
|
|
3
|
+
import { IconButton } from "./IconButton";
|
|
4
|
+
import { textStyles } from "./Text";
|
|
5
5
|
|
|
6
6
|
export const Toast = ({
|
|
7
7
|
title,
|
|
@@ -15,15 +15,21 @@ export const Toast = ({
|
|
|
15
15
|
}) => {
|
|
16
16
|
return (
|
|
17
17
|
<ToastPrimitive.Root
|
|
18
|
-
className="px-2 py-2.5 grid grid-cols-[auto_max-content] gap-x-2.5 items-center rounded text-sm bg-popover-background shadow-
|
|
18
|
+
className="px-2 py-2.5 grid grid-cols-[auto_max-content] gap-x-2.5 items-center rounded text-sm bg-popover-background shadow-popover text-text-muted"
|
|
19
19
|
{...props}
|
|
20
20
|
>
|
|
21
21
|
{title && (
|
|
22
|
-
<ToastPrimitive.Title
|
|
22
|
+
<ToastPrimitive.Title
|
|
23
|
+
className={`mb-[5px] ${textStyles.label} font-bold text-text`}
|
|
24
|
+
>
|
|
23
25
|
{title}
|
|
24
26
|
</ToastPrimitive.Title>
|
|
25
27
|
)}
|
|
26
|
-
<ToastPrimitive.Description
|
|
28
|
+
<ToastPrimitive.Description
|
|
29
|
+
className={`${textStyles.small} text-text-muted`}
|
|
30
|
+
>
|
|
31
|
+
{content}
|
|
32
|
+
</ToastPrimitive.Description>
|
|
27
33
|
{children && (
|
|
28
34
|
<ToastPrimitive.Action asChild altText="">
|
|
29
35
|
{children}
|
|
@@ -3,7 +3,6 @@ import { useKeyboardShortcuts } from "@noya-app/noya-keymap";
|
|
|
3
3
|
import React from "react";
|
|
4
4
|
|
|
5
5
|
import { memoGeneric } from "@noya-app/react-utils";
|
|
6
|
-
import { cssVars } from "../theme";
|
|
7
6
|
import { BaseToolbar } from "./BaseToolbar";
|
|
8
7
|
import { Button } from "./Button";
|
|
9
8
|
import { DividerVertical } from "./Divider";
|
|
@@ -13,8 +12,78 @@ import {
|
|
|
13
12
|
getKeyboardShortcutsForMenuItems,
|
|
14
13
|
isSelectableMenuItem,
|
|
15
14
|
MenuItem,
|
|
15
|
+
SelectableMenuItem,
|
|
16
|
+
SubMenuItem,
|
|
16
17
|
} from "./internal/Menu";
|
|
17
18
|
import { Spacer } from "./Spacer";
|
|
19
|
+
import { Tooltip } from "./Tooltip";
|
|
20
|
+
|
|
21
|
+
export const ToolbarMenuDropdown = memoGeneric(function ToolbarMenuDropdown<
|
|
22
|
+
T extends string,
|
|
23
|
+
>({
|
|
24
|
+
item,
|
|
25
|
+
onSelectMenuItem,
|
|
26
|
+
}: {
|
|
27
|
+
item: SubMenuItem<T>;
|
|
28
|
+
onSelectMenuItem?: (value: T) => void;
|
|
29
|
+
}) {
|
|
30
|
+
const [open, setOpen] = React.useState(false);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<DropdownMenu
|
|
34
|
+
open={open}
|
|
35
|
+
onOpenChange={setOpen}
|
|
36
|
+
items={item.items}
|
|
37
|
+
onSelect={(value) => {
|
|
38
|
+
if (onSelectMenuItem && value) {
|
|
39
|
+
onSelectMenuItem(value);
|
|
40
|
+
}
|
|
41
|
+
}}
|
|
42
|
+
>
|
|
43
|
+
<Button disabled={item.disabled} active={item.checked || open}>
|
|
44
|
+
{item.title}
|
|
45
|
+
{item.title && item.icon && <Spacer.Horizontal inline size={6} />}
|
|
46
|
+
{item.icon && renderIcon(item.icon)}
|
|
47
|
+
<Spacer.Horizontal size={6} />
|
|
48
|
+
<DropdownChevronIcon />
|
|
49
|
+
</Button>
|
|
50
|
+
</DropdownMenu>
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export const ToolbarMenuButton = memoGeneric(function ToolbarMenuButton<
|
|
55
|
+
T extends string,
|
|
56
|
+
>({
|
|
57
|
+
item,
|
|
58
|
+
onSelectMenuItem,
|
|
59
|
+
}: {
|
|
60
|
+
item: SelectableMenuItem<T>;
|
|
61
|
+
onSelectMenuItem?: (value: T) => void;
|
|
62
|
+
}) {
|
|
63
|
+
const content = (
|
|
64
|
+
<Button
|
|
65
|
+
disabled={item.disabled}
|
|
66
|
+
active={item.checked}
|
|
67
|
+
onClick={() => {
|
|
68
|
+
if (onSelectMenuItem && item.value) {
|
|
69
|
+
onSelectMenuItem(item.value);
|
|
70
|
+
}
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
{item.title}
|
|
74
|
+
{item.title && item.icon && <Spacer.Horizontal inline size={6} />}
|
|
75
|
+
{item.icon && renderIcon(item.icon)}
|
|
76
|
+
</Button>
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
return item.tooltip ? (
|
|
80
|
+
<Tooltip sideOffset={10} content={item.tooltip}>
|
|
81
|
+
{content}
|
|
82
|
+
</Tooltip>
|
|
83
|
+
) : (
|
|
84
|
+
content
|
|
85
|
+
);
|
|
86
|
+
});
|
|
18
87
|
|
|
19
88
|
export interface ToolbarProps<T extends string = string> {
|
|
20
89
|
children?: React.ReactNode;
|
|
@@ -79,10 +148,6 @@ export function Toolbar<T extends string = string>({
|
|
|
79
148
|
);
|
|
80
149
|
}
|
|
81
150
|
|
|
82
|
-
const activeButtonStyle = {
|
|
83
|
-
backgroundColor: cssVars.colors.primaryPastel,
|
|
84
|
-
};
|
|
85
|
-
|
|
86
151
|
export const ToolbarMenu = memoGeneric(function ToolbarMenu<T extends string>({
|
|
87
152
|
items,
|
|
88
153
|
onSelectMenuItem,
|
|
@@ -101,44 +166,20 @@ export const ToolbarMenu = memoGeneric(function ToolbarMenu<T extends string>({
|
|
|
101
166
|
|
|
102
167
|
if (isSelectableMenuItem(item)) {
|
|
103
168
|
return (
|
|
104
|
-
<
|
|
169
|
+
<ToolbarMenuButton
|
|
105
170
|
key={i}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (onSelectMenuItem && item.value) {
|
|
110
|
-
onSelectMenuItem(item.value);
|
|
111
|
-
}
|
|
112
|
-
}}
|
|
113
|
-
>
|
|
114
|
-
{item.title}
|
|
115
|
-
{item.title && item.icon && <Spacer.Horizontal inline size={6} />}
|
|
116
|
-
{item.icon && renderIcon(item.icon)}
|
|
117
|
-
</Button>
|
|
171
|
+
item={item}
|
|
172
|
+
onSelectMenuItem={onSelectMenuItem}
|
|
173
|
+
/>
|
|
118
174
|
);
|
|
119
175
|
}
|
|
120
176
|
|
|
121
177
|
return (
|
|
122
|
-
<
|
|
178
|
+
<ToolbarMenuDropdown
|
|
123
179
|
key={i}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
onSelectMenuItem(value);
|
|
128
|
-
}
|
|
129
|
-
}}
|
|
130
|
-
>
|
|
131
|
-
<Button
|
|
132
|
-
disabled={item.disabled}
|
|
133
|
-
style={item.checked ? activeButtonStyle : undefined}
|
|
134
|
-
>
|
|
135
|
-
{item.title}
|
|
136
|
-
{item.title && item.icon && <Spacer.Horizontal inline size={6} />}
|
|
137
|
-
{item.icon && renderIcon(item.icon)}
|
|
138
|
-
<Spacer.Horizontal size={6} />
|
|
139
|
-
<DropdownChevronIcon />
|
|
140
|
-
</Button>
|
|
141
|
-
</DropdownMenu>
|
|
180
|
+
item={item}
|
|
181
|
+
onSelectMenuItem={onSelectMenuItem}
|
|
182
|
+
/>
|
|
142
183
|
);
|
|
143
184
|
})}
|
|
144
185
|
</>
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
2
2
|
import * as React from "react";
|
|
3
|
+
import {
|
|
4
|
+
portalScopeProps,
|
|
5
|
+
usePortalScopeId,
|
|
6
|
+
} from "../contexts/PortalScopeContext";
|
|
3
7
|
import { textStyles } from "./Text";
|
|
4
8
|
|
|
5
9
|
// const Arrow = styled(TooltipPrimitive.Arrow)(({ theme }) => ({
|
|
@@ -9,23 +13,28 @@ import { textStyles } from "./Text";
|
|
|
9
13
|
interface Props {
|
|
10
14
|
children: React.ReactNode;
|
|
11
15
|
content: React.ReactNode;
|
|
16
|
+
sideOffset?: number;
|
|
12
17
|
}
|
|
13
18
|
|
|
14
19
|
export const Tooltip = React.memo(function Tooltip({
|
|
15
20
|
children,
|
|
16
21
|
content,
|
|
22
|
+
sideOffset = 2,
|
|
17
23
|
}: Props) {
|
|
24
|
+
const portalScopeId = usePortalScopeId();
|
|
25
|
+
|
|
18
26
|
return (
|
|
19
27
|
<TooltipPrimitive.Provider>
|
|
20
28
|
<TooltipPrimitive.Root>
|
|
21
29
|
<TooltipPrimitive.Trigger asChild>{children}</TooltipPrimitive.Trigger>
|
|
22
30
|
<TooltipPrimitive.Portal>
|
|
23
31
|
<TooltipPrimitive.Content
|
|
32
|
+
{...portalScopeProps(portalScopeId)}
|
|
24
33
|
side="bottom"
|
|
25
34
|
align="center"
|
|
26
|
-
sideOffset={
|
|
35
|
+
sideOffset={sideOffset}
|
|
27
36
|
collisionPadding={8}
|
|
28
|
-
className={`${textStyles.small} text-text rounded-[3px] py-small px-medium bg-popover-background shadow-
|
|
37
|
+
className={`${textStyles.small} text-text rounded-[3px] py-small px-medium bg-popover-background shadow-popover border border-solid border-divider-strong z-menu`}
|
|
29
38
|
>
|
|
30
39
|
{content}
|
|
31
40
|
{/* <Arrow /> */}
|