@noya-app/noya-designsystem 0.1.53 → 0.1.55
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 -10
- package/CHANGELOG.md +16 -0
- package/dist/index.css +1 -1
- package/dist/index.d.mts +35 -11
- package/dist/index.d.ts +35 -11
- package/dist/index.js +443 -315
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1061 -937
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/components/Button.tsx +13 -0
- package/src/components/ColorSwatch.tsx +5 -2
- package/src/components/Dialog.tsx +3 -5
- package/src/components/Grid.tsx +3 -1
- package/src/components/GridView.tsx +1 -0
- package/src/components/InputField.tsx +10 -3
- package/src/components/Popover.tsx +68 -4
- package/src/components/SegmentedControl.tsx +47 -53
- package/src/components/SelectionToolbar.tsx +35 -22
- package/src/components/Slider.tsx +1 -1
- package/src/components/Toolbar.tsx +62 -48
- package/src/components/connected-users-menu/ConnectedUsersMenuLayout.tsx +3 -26
- package/src/components/internal/Menu.tsx +1 -1
- package/src/components/internal/SelectItem.tsx +24 -3
- package/src/contexts/DesignSystemConfiguration.tsx +8 -5
- package/src/contexts/OpenPortalsContext.tsx +67 -0
- package/src/index.css +2 -2
- package/src/index.tsx +1 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { DropdownChevronIcon } from "@noya-app/noya-icons";
|
|
2
1
|
import { useKeyboardShortcuts } from "@noya-app/noya-keymap";
|
|
3
2
|
import React from "react";
|
|
4
3
|
|
|
@@ -7,7 +6,6 @@ import { BaseToolbar } from "./BaseToolbar";
|
|
|
7
6
|
import { Button } from "./Button";
|
|
8
7
|
import { DividerVertical } from "./Divider";
|
|
9
8
|
import { DropdownMenu } from "./DropdownMenu";
|
|
10
|
-
import { renderIcon } from "./Icons";
|
|
11
9
|
import {
|
|
12
10
|
getKeyboardShortcutsForMenuItems,
|
|
13
11
|
isSelectableMenuItem,
|
|
@@ -15,9 +13,14 @@ import {
|
|
|
15
13
|
SelectableMenuItem,
|
|
16
14
|
SubMenuItem,
|
|
17
15
|
} from "./internal/Menu";
|
|
18
|
-
import { Spacer } from "./Spacer";
|
|
19
16
|
import { Tooltip } from "./Tooltip";
|
|
20
17
|
|
|
18
|
+
const iconButtonStyle = {
|
|
19
|
+
height: 27,
|
|
20
|
+
width: 27,
|
|
21
|
+
minWidth: 27,
|
|
22
|
+
};
|
|
23
|
+
|
|
21
24
|
export const ToolbarMenuDropdown = memoGeneric(function ToolbarMenuDropdown<
|
|
22
25
|
T extends string,
|
|
23
26
|
>({
|
|
@@ -40,12 +43,13 @@ export const ToolbarMenuDropdown = memoGeneric(function ToolbarMenuDropdown<
|
|
|
40
43
|
}
|
|
41
44
|
}}
|
|
42
45
|
>
|
|
43
|
-
<Button
|
|
46
|
+
<Button
|
|
47
|
+
disabled={item.disabled}
|
|
48
|
+
active={item.checked || open}
|
|
49
|
+
icon={item.icon}
|
|
50
|
+
iconRight="DropdownChevronIcon"
|
|
51
|
+
>
|
|
44
52
|
{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
53
|
</Button>
|
|
50
54
|
</DropdownMenu>
|
|
51
55
|
);
|
|
@@ -64,6 +68,8 @@ export const ToolbarMenuButton = memoGeneric(function ToolbarMenuButton<
|
|
|
64
68
|
<Button
|
|
65
69
|
disabled={item.disabled}
|
|
66
70
|
active={item.checked}
|
|
71
|
+
icon={item.icon}
|
|
72
|
+
style={item.icon && !item.title ? iconButtonStyle : undefined}
|
|
67
73
|
onClick={() => {
|
|
68
74
|
if (onSelectMenuItem && item.value) {
|
|
69
75
|
onSelectMenuItem(item.value);
|
|
@@ -71,8 +77,6 @@ export const ToolbarMenuButton = memoGeneric(function ToolbarMenuButton<
|
|
|
71
77
|
}}
|
|
72
78
|
>
|
|
73
79
|
{item.title}
|
|
74
|
-
{item.title && item.icon && <Spacer.Horizontal inline size={6} />}
|
|
75
|
-
{item.icon && renderIcon(item.icon)}
|
|
76
80
|
</Button>
|
|
77
81
|
);
|
|
78
82
|
|
|
@@ -85,6 +89,54 @@ export const ToolbarMenuButton = memoGeneric(function ToolbarMenuButton<
|
|
|
85
89
|
);
|
|
86
90
|
});
|
|
87
91
|
|
|
92
|
+
export const ToolbarMenuItem = memoGeneric(function ToolbarMenuItem<
|
|
93
|
+
T extends string,
|
|
94
|
+
>({
|
|
95
|
+
item,
|
|
96
|
+
onSelectMenuItem,
|
|
97
|
+
}: {
|
|
98
|
+
item: MenuItem<T>;
|
|
99
|
+
onSelectMenuItem?: (value: T) => void;
|
|
100
|
+
}) {
|
|
101
|
+
if (item.type === "sectionHeader") return null;
|
|
102
|
+
|
|
103
|
+
if (item.type === "separator") {
|
|
104
|
+
return <DividerVertical overflow={4} />;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (isSelectableMenuItem(item)) {
|
|
108
|
+
return (
|
|
109
|
+
<ToolbarMenuButton item={item} onSelectMenuItem={onSelectMenuItem} />
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
<ToolbarMenuDropdown item={item} onSelectMenuItem={onSelectMenuItem} />
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
export const ToolbarMenu = memoGeneric(function ToolbarMenu<T extends string>({
|
|
119
|
+
items,
|
|
120
|
+
onSelectMenuItem,
|
|
121
|
+
}: {
|
|
122
|
+
items: MenuItem<T>[];
|
|
123
|
+
onSelectMenuItem?: (value: T) => void;
|
|
124
|
+
}) {
|
|
125
|
+
return (
|
|
126
|
+
<>
|
|
127
|
+
{items.map((item, i) => {
|
|
128
|
+
return (
|
|
129
|
+
<ToolbarMenuItem
|
|
130
|
+
key={i}
|
|
131
|
+
item={item}
|
|
132
|
+
onSelectMenuItem={onSelectMenuItem}
|
|
133
|
+
/>
|
|
134
|
+
);
|
|
135
|
+
})}
|
|
136
|
+
</>
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
|
|
88
140
|
export interface ToolbarProps<T extends string = string> {
|
|
89
141
|
children?: React.ReactNode;
|
|
90
142
|
logo?: React.ReactNode;
|
|
@@ -147,41 +199,3 @@ export function Toolbar<T extends string = string>({
|
|
|
147
199
|
</BaseToolbar>
|
|
148
200
|
);
|
|
149
201
|
}
|
|
150
|
-
|
|
151
|
-
export const ToolbarMenu = memoGeneric(function ToolbarMenu<T extends string>({
|
|
152
|
-
items,
|
|
153
|
-
onSelectMenuItem,
|
|
154
|
-
}: {
|
|
155
|
-
items: MenuItem<T>[];
|
|
156
|
-
onSelectMenuItem?: (value: T) => void;
|
|
157
|
-
}) {
|
|
158
|
-
return (
|
|
159
|
-
<>
|
|
160
|
-
{items.map((item, i) => {
|
|
161
|
-
if (item.type === "sectionHeader") return null;
|
|
162
|
-
|
|
163
|
-
if (item.type === "separator") {
|
|
164
|
-
return <DividerVertical key={i} overflow={4} />;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (isSelectableMenuItem(item)) {
|
|
168
|
-
return (
|
|
169
|
-
<ToolbarMenuButton
|
|
170
|
-
key={i}
|
|
171
|
-
item={item}
|
|
172
|
-
onSelectMenuItem={onSelectMenuItem}
|
|
173
|
-
/>
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return (
|
|
178
|
-
<ToolbarMenuDropdown
|
|
179
|
-
key={i}
|
|
180
|
-
item={item}
|
|
181
|
-
onSelectMenuItem={onSelectMenuItem}
|
|
182
|
-
/>
|
|
183
|
-
);
|
|
184
|
-
})}
|
|
185
|
-
</>
|
|
186
|
-
);
|
|
187
|
-
});
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
import { cssVars } from "../../theme";
|
|
2
|
-
|
|
3
|
-
import { ChatBubbleWithDotsIcon } from "@noya-app/noya-icons";
|
|
4
1
|
import React from "react";
|
|
5
2
|
import { INPUT_HEIGHT } from "../../theme";
|
|
6
|
-
import { colorFromString } from "../../utils/colorFromString";
|
|
7
3
|
import { Avatar, AvatarProps, AvatarStack } from "../Avatar";
|
|
8
4
|
import Button from "../Button";
|
|
9
|
-
import { Spacer } from "../Spacer";
|
|
10
5
|
import { Small } from "../Text";
|
|
11
6
|
import { Tooltip } from "../Tooltip";
|
|
12
7
|
|
|
@@ -25,14 +20,12 @@ export const UserAvatar = ({ userId, name, image }: AvatarProps) => (
|
|
|
25
20
|
|
|
26
21
|
type ConnectedUsersMenuLayoutProps = {
|
|
27
22
|
renderUsers: () => React.ReactNode;
|
|
28
|
-
currentUserId?: string;
|
|
29
23
|
launchAIAssistant?: () => void;
|
|
30
24
|
isAssistantOpen?: boolean;
|
|
31
25
|
};
|
|
32
26
|
|
|
33
27
|
export const ConnectedUsersMenuLayout = ({
|
|
34
28
|
renderUsers,
|
|
35
|
-
currentUserId,
|
|
36
29
|
launchAIAssistant,
|
|
37
30
|
isAssistantOpen,
|
|
38
31
|
}: ConnectedUsersMenuLayoutProps) => {
|
|
@@ -41,30 +34,14 @@ export const ConnectedUsersMenuLayout = ({
|
|
|
41
34
|
<AvatarStack size={INPUT_HEIGHT} className="mr-1">
|
|
42
35
|
{renderUsers()}
|
|
43
36
|
</AvatarStack>
|
|
44
|
-
|
|
45
|
-
{/* AI Assistant Avatar */}
|
|
46
37
|
{launchAIAssistant && (
|
|
47
|
-
<Tooltip
|
|
48
|
-
content={
|
|
49
|
-
<div className="flex flex-col">
|
|
50
|
-
<Small>AI Assistant</Small>
|
|
51
|
-
</div>
|
|
52
|
-
}
|
|
53
|
-
>
|
|
38
|
+
<Tooltip content="AI Assistant">
|
|
54
39
|
<Button
|
|
55
|
-
|
|
40
|
+
active={isAssistantOpen}
|
|
56
41
|
onClick={launchAIAssistant}
|
|
57
|
-
|
|
58
|
-
backgroundColor: isAssistantOpen
|
|
59
|
-
? colorFromString(currentUserId ?? "")
|
|
60
|
-
: undefined,
|
|
61
|
-
}}
|
|
42
|
+
icon="ChatBubbleWithDotsIcon"
|
|
62
43
|
>
|
|
63
44
|
AI
|
|
64
|
-
<Spacer.Horizontal size={8} />
|
|
65
|
-
<ChatBubbleWithDotsIcon
|
|
66
|
-
color={isAssistantOpen ? undefined : cssVars.colors.icon}
|
|
67
|
-
/>
|
|
68
45
|
</Button>
|
|
69
46
|
</Tooltip>
|
|
70
47
|
)}
|
|
@@ -159,7 +159,7 @@ export const getMenuItemKey = <T extends string>(
|
|
|
159
159
|
};
|
|
160
160
|
|
|
161
161
|
export const CHECKBOX_WIDTH = 15;
|
|
162
|
-
export const CHECKBOX_RIGHT_INSET =
|
|
162
|
+
export const CHECKBOX_RIGHT_INSET = 6;
|
|
163
163
|
export const CHECKBOX_INDENT_WIDTH = 6;
|
|
164
164
|
|
|
165
165
|
export const styles = {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { renderIcon } from "../Icons";
|
|
2
2
|
|
|
3
|
-
import { CheckIcon } from "@noya-app/noya-icons";
|
|
4
3
|
import * as Select from "@radix-ui/react-select";
|
|
5
4
|
import React, { useCallback } from "react";
|
|
6
5
|
import { cx } from "../../utils/classNames";
|
|
@@ -68,7 +67,7 @@ export const SelectItem = React.forwardRef(
|
|
|
68
67
|
<Spacer.Horizontal size={CHECKBOX_INDENT_WIDTH} />
|
|
69
68
|
{Components.ItemIndicator && (
|
|
70
69
|
<Components.ItemIndicator {...styles.itemIndicator}>
|
|
71
|
-
<
|
|
70
|
+
<Checkmark />
|
|
72
71
|
</Components.ItemIndicator>
|
|
73
72
|
)}
|
|
74
73
|
{icon && (
|
|
@@ -109,7 +108,7 @@ export const SelectItem = React.forwardRef(
|
|
|
109
108
|
{indented && <Spacer.Horizontal size={CHECKBOX_INDENT_WIDTH} />}
|
|
110
109
|
{checked ? (
|
|
111
110
|
<div {...styles.itemIndicator}>
|
|
112
|
-
<
|
|
111
|
+
<Checkmark />
|
|
113
112
|
</div>
|
|
114
113
|
) : indented ? (
|
|
115
114
|
<Spacer.Horizontal size={CHECKBOX_WIDTH - CHECKBOX_RIGHT_INSET} />
|
|
@@ -134,3 +133,25 @@ export const SelectItem = React.forwardRef(
|
|
|
134
133
|
);
|
|
135
134
|
}
|
|
136
135
|
);
|
|
136
|
+
|
|
137
|
+
function Checkmark() {
|
|
138
|
+
return (
|
|
139
|
+
<svg
|
|
140
|
+
viewBox="0 0 15 15"
|
|
141
|
+
fill="none"
|
|
142
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
143
|
+
aria-hidden="true"
|
|
144
|
+
width={15}
|
|
145
|
+
height={15}
|
|
146
|
+
>
|
|
147
|
+
<path
|
|
148
|
+
className="scale-90 origin-center"
|
|
149
|
+
d="M3 8L6 11L12 5"
|
|
150
|
+
strokeWidth="1.1"
|
|
151
|
+
strokeLinecap="round"
|
|
152
|
+
strokeLinejoin="round"
|
|
153
|
+
stroke="currentColor"
|
|
154
|
+
/>
|
|
155
|
+
</svg>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
@@ -3,6 +3,7 @@ import * as React from "react";
|
|
|
3
3
|
import { ToastProvider } from "../components/Toast";
|
|
4
4
|
import { DialogProvider } from "./DialogContext";
|
|
5
5
|
import { FloatingWindowProvider } from "./FloatingWindowContext";
|
|
6
|
+
import { OpenPortalsProvider } from "./OpenPortalsContext";
|
|
6
7
|
|
|
7
8
|
export type DesignSystemConfigurationContextValue = {
|
|
8
9
|
platform: PlatformName;
|
|
@@ -39,11 +40,13 @@ export const DesignSystemConfigurationProvider = React.memo(
|
|
|
39
40
|
|
|
40
41
|
return (
|
|
41
42
|
<DesignSystemConfigurationContext.Provider value={contextValue}>
|
|
42
|
-
<
|
|
43
|
-
<
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
<OpenPortalsProvider>
|
|
44
|
+
<DialogProvider>
|
|
45
|
+
<ToastProvider>
|
|
46
|
+
<FloatingWindowProvider>{children}</FloatingWindowProvider>
|
|
47
|
+
</ToastProvider>
|
|
48
|
+
</DialogProvider>
|
|
49
|
+
</OpenPortalsProvider>
|
|
47
50
|
</DesignSystemConfigurationContext.Provider>
|
|
48
51
|
);
|
|
49
52
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
export type OpenPortalsContextValue = {
|
|
4
|
+
hasOpenPortals: boolean;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type OpenPortalControlsContextValue = {
|
|
8
|
+
addOpenPortal: (portal: string) => void;
|
|
9
|
+
removeOpenPortal: (portal: string) => void;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* This context is used to track the open portals in the design system.
|
|
14
|
+
*
|
|
15
|
+
* We currently use this so that we can cover up iframes when there are open portals.
|
|
16
|
+
* This lets us intercept click events that would otherwise be swallowed by the iframe.
|
|
17
|
+
*/
|
|
18
|
+
const OpenPortalsContext = React.createContext<OpenPortalsContextValue>({
|
|
19
|
+
hasOpenPortals: false,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const OpenPortalControlsContext =
|
|
23
|
+
React.createContext<OpenPortalControlsContextValue>({
|
|
24
|
+
addOpenPortal: () => {},
|
|
25
|
+
removeOpenPortal: () => {},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export const OpenPortalsProvider = React.memo(function OpenPortalsProvider({
|
|
29
|
+
children,
|
|
30
|
+
}: {
|
|
31
|
+
children: React.ReactNode;
|
|
32
|
+
}) {
|
|
33
|
+
const [openPortals, setOpenPortals] = React.useState<string[]>([]);
|
|
34
|
+
|
|
35
|
+
const value = React.useMemo(
|
|
36
|
+
() => ({ hasOpenPortals: openPortals.length > 0 }),
|
|
37
|
+
[openPortals]
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const controlsValue = React.useMemo(
|
|
41
|
+
() => ({
|
|
42
|
+
addOpenPortal: (portal: string) => {
|
|
43
|
+
setOpenPortals((prev) => [...prev, portal]);
|
|
44
|
+
},
|
|
45
|
+
removeOpenPortal: (portal: string) => {
|
|
46
|
+
setOpenPortals((prev) => prev.filter((p) => p !== portal));
|
|
47
|
+
},
|
|
48
|
+
}),
|
|
49
|
+
[]
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<OpenPortalsContext.Provider value={value}>
|
|
54
|
+
<OpenPortalControlsContext.Provider value={controlsValue}>
|
|
55
|
+
{children}
|
|
56
|
+
</OpenPortalControlsContext.Provider>
|
|
57
|
+
</OpenPortalsContext.Provider>
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
export function useHasOpenPortals(): boolean {
|
|
62
|
+
return React.useContext(OpenPortalsContext).hasOpenPortals;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function useOpenPortalsControls(): OpenPortalControlsContextValue {
|
|
66
|
+
return React.useContext(OpenPortalControlsContext);
|
|
67
|
+
}
|
package/src/index.css
CHANGED
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
--n-toolbar-separator: 8px;
|
|
79
79
|
--n-inspector-h-separator: 8px;
|
|
80
80
|
--n-inspector-v-separator: 10px;
|
|
81
|
-
--n-dialog-padding:
|
|
81
|
+
--n-dialog-padding: 32px;
|
|
82
82
|
--n-input-height: 27px;
|
|
83
83
|
--n-icon: var(--n-indigo-300);
|
|
84
84
|
--n-icon-selected: rgb(220, 220, 220);
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
--n-floating-button: rgb(248, 248, 250);
|
|
102
102
|
--n-block-border: rgb(184, 201, 218);
|
|
103
103
|
--n-block-highlight: rgb(255, 165, 0);
|
|
104
|
-
--n-markdown-editor-line-placeholder-content: "
|
|
104
|
+
--n-markdown-editor-line-placeholder-content: "";
|
|
105
105
|
--n-selected-list-item-background: var(--n-primary-100);
|
|
106
106
|
--n-selected-list-item-text: var(--n-primary);
|
|
107
107
|
--n-selected-list-item-icon-background: var(--n-primary-150);
|
package/src/index.tsx
CHANGED
|
@@ -78,6 +78,7 @@ export * from "./contexts/DialogContext";
|
|
|
78
78
|
export * from "./contexts/FloatingWindowContext";
|
|
79
79
|
export * from "./contexts/GlobalInputBlurContext";
|
|
80
80
|
export * from "./contexts/ImageDataContext";
|
|
81
|
+
export * from "./contexts/OpenPortalsContext";
|
|
81
82
|
export * from "./contexts/PortalScopeContext";
|
|
82
83
|
// Hooks
|
|
83
84
|
export * from "./hooks/useHover";
|