@noya-app/noya-designsystem 0.1.63 → 0.1.65
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 +13 -10
- package/CHANGELOG.md +25 -0
- package/dist/index.css +1 -1
- package/dist/index.d.mts +648 -333
- package/dist/index.d.ts +648 -333
- package/dist/index.js +13813 -9157
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +13949 -9269
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -22
- package/src/__tests__/validateDropIndicator.test.ts +26 -41
- package/src/__tests__/workspaceLayout.test.ts +281 -0
- package/src/components/ActionMenu.tsx +2 -0
- package/src/components/ActivityIndicator.tsx +13 -6
- package/src/components/ActivityLog.tsx +197 -0
- package/src/components/AnimatePresence.tsx +43 -26
- package/src/components/Avatar.tsx +45 -26
- package/src/components/Banner.tsx +57 -11
- package/src/components/BaseToolbar.tsx +67 -34
- package/src/components/Breadcrumbs.tsx +4 -2
- package/src/components/Button.tsx +28 -23
- package/src/components/Checkbox.tsx +6 -35
- package/src/components/Chip.tsx +47 -33
- package/src/components/Collection.tsx +20 -12
- package/src/components/ColorSwatch.tsx +42 -22
- package/src/components/ColorSwatchControl.tsx +7 -5
- package/src/components/Combobox.tsx +8 -6
- package/src/components/ComboboxMenu.tsx +6 -5
- package/src/components/CommandPalette.tsx +3 -1
- package/src/components/ContextMenu.tsx +3 -1
- package/src/components/Dialog.tsx +9 -13
- package/src/components/DimensionInput.tsx +2 -0
- package/src/components/Divider.tsx +18 -14
- package/src/components/DraggableMenuButton.tsx +11 -5
- package/src/components/Drawer.tsx +14 -6
- package/src/components/DropdownMenu.tsx +4 -2
- package/src/components/EditableText.tsx +4 -2
- package/src/components/Fade.tsx +13 -13
- package/src/components/FileUploadIndicator.tsx +108 -0
- package/src/components/FloatingWindow.tsx +4 -2
- package/src/components/Grid.tsx +58 -42
- package/src/components/GridView.tsx +65 -36
- package/src/components/IVirtualizedList.tsx +5 -0
- package/src/components/IconButton.tsx +2 -0
- package/src/components/Icons.tsx +24 -4
- package/src/components/InputField.tsx +21 -19
- package/src/components/InspectorContainer.tsx +2 -2
- package/src/components/InspectorPrimitives.tsx +15 -9
- package/src/components/Label.tsx +4 -3
- package/src/components/LabeledElementView.tsx +9 -5
- package/src/components/LabeledField.tsx +8 -6
- package/src/components/List.tsx +112 -46
- package/src/components/ListMenu.tsx +91 -0
- package/src/components/ListNavigator.tsx +106 -0
- package/src/components/ListView.tsx +22 -1147
- package/src/components/MediaThumbnail.tsx +84 -14
- package/src/components/Message.tsx +11 -12
- package/src/components/Navigator.tsx +461 -0
- package/src/components/NoyaLogo.tsx +1 -1
- package/src/components/Popover.tsx +12 -6
- package/src/components/Progress.tsx +31 -8
- package/src/components/ResizableContainer.tsx +187 -0
- package/src/components/RingProgress.tsx +128 -0
- package/src/components/ScrollArea.tsx +7 -5
- package/src/components/ScrollableSidebar.tsx +10 -0
- package/src/components/SearchCompletionMenu.tsx +5 -3
- package/src/components/Section.tsx +109 -57
- package/src/components/SegmentedControl.tsx +51 -25
- package/src/components/SelectMenu.tsx +16 -21
- package/src/components/SelectionToolbar.tsx +7 -4
- package/src/components/Slider.tsx +12 -10
- package/src/components/Spacer.tsx +2 -2
- package/src/components/StackNavigator.tsx +378 -0
- package/src/components/Stepper.tsx +88 -0
- package/src/components/Switch.tsx +7 -5
- package/src/components/Tabs.tsx +6 -4
- package/src/components/Text.tsx +29 -18
- package/src/components/TextArea.tsx +5 -3
- package/src/components/Toast.tsx +5 -5
- package/src/components/Toolbar.tsx +57 -10
- package/src/components/ToolbarDrawer.tsx +29 -0
- package/src/components/Tooltip.tsx +2 -2
- package/src/components/TreeView.tsx +13 -4
- package/src/components/UserPointer.tsx +6 -4
- package/src/components/ai-assistant/AIAssistantLayout.tsx +10 -8
- package/src/components/blocks/ImageBlockComponent.tsx +162 -0
- package/src/components/connected-users-menu/ConnectedUsersMenuLayout.tsx +6 -4
- package/src/components/file-explorer/FileExplorerLayout.tsx +22 -8
- package/src/components/internal/Checkmark.tsx +24 -0
- package/src/components/internal/Menu.tsx +24 -19
- package/src/components/internal/MenuViewport.tsx +15 -6
- package/src/components/internal/SelectItem.tsx +7 -26
- package/src/components/internal/TextInput.tsx +7 -1
- package/src/components/listView/ListViewContexts.tsx +68 -0
- package/src/components/listView/ListViewEditableRowTitle.tsx +55 -0
- package/src/components/listView/ListViewRoot.tsx +521 -0
- package/src/components/listView/ListViewRow.tsx +475 -0
- package/src/components/listView/ListViewRowTitle.tsx +21 -0
- package/src/components/listView/types.ts +11 -0
- package/src/components/pipeline/PipelineResultLayout.tsx +6 -6
- package/src/components/sorting/DragRegistration.tsx +10 -5
- package/src/components/sorting/SharedDragProvider.tsx +134 -27
- package/src/components/sorting/Sortable.tsx +90 -27
- package/src/components/sorting/createSharedDrag.tsx +7 -6
- package/src/components/sorting/sorting.ts +6 -9
- package/src/components/workspace/DrawerWorkspaceLayout.tsx +110 -29
- package/src/components/workspace/PanelWorkspaceLayout.tsx +148 -99
- package/src/components/workspace/VerticalTabMenu.tsx +46 -0
- package/src/components/workspace/WorkspaceLayout.tsx +76 -63
- package/src/components/workspace/constants.ts +4 -0
- package/src/components/workspace/panelStorage.ts +216 -0
- package/src/components/workspace/types.ts +11 -4
- package/src/contexts/DesignSystemConfiguration.tsx +12 -5
- package/src/contexts/DialogContext.tsx +19 -5
- package/src/contexts/FloatingWindowContext.tsx +2 -0
- package/src/contexts/GlobalInputBlurContext.tsx +2 -0
- package/src/contexts/ImageDataContext.tsx +2 -0
- package/src/contexts/LinkComponentContext.tsx +17 -0
- package/src/contexts/OpenPortalsContext.tsx +2 -0
- package/src/contexts/PortalScopeContext.tsx +2 -0
- package/src/hooks/mergeEventHandlers.ts +1 -1
- package/src/hooks/useHover.ts +30 -17
- package/src/hooks/useIndent.ts +5 -8
- package/src/hooks/useLabel.ts +2 -0
- package/src/hooks/useObjectURL.ts +5 -3
- package/src/hooks/usePlatform.ts +5 -3
- package/src/hooks/useTheme.ts +3 -1
- package/src/index.css +43 -10
- package/src/index.tsx +15 -7
- package/src/utils/classNames.ts +56 -9
- package/src/utils/combobox.ts +2 -0
- package/src/utils/editableBlockStyles.ts +2 -2
- package/src/utils/formatByteSize.ts +9 -0
- package/src/utils/inputs.ts +8 -6
- package/src/utils/sketchColor.ts +0 -34
- package/src/utils/withSeparatorElements.ts +1 -1
- package/tailwind.config.ts +1 -0
- package/src/components/FillInputField.tsx +0 -37
- package/src/components/FillPreviewBackground.tsx +0 -132
- package/src/components/GradientPicker.tsx +0 -88
- package/src/hooks/usePreservePanelSize.tsx +0 -183
- package/src/mediaQuery.ts +0 -13
- package/src/utils/getGradientBackground.tsx +0 -31
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { useKeyboardShortcuts } from "@noya-app/noya-keymap";
|
|
2
|
-
import React, {
|
|
4
|
+
import React, {
|
|
5
|
+
ComponentProps,
|
|
6
|
+
createContext,
|
|
7
|
+
ReactNode,
|
|
8
|
+
useContext,
|
|
9
|
+
} from "react";
|
|
3
10
|
|
|
4
11
|
import { memoGeneric } from "@noya-app/react-utils";
|
|
5
12
|
import { BaseToolbar } from "./BaseToolbar";
|
|
@@ -17,10 +24,15 @@ import {
|
|
|
17
24
|
} from "./internal/Menu";
|
|
18
25
|
import { Popover } from "./Popover";
|
|
19
26
|
import { Spacer } from "./Spacer";
|
|
27
|
+
import { ToolbarDrawer } from "./ToolbarDrawer";
|
|
20
28
|
import { Tooltip } from "./Tooltip";
|
|
21
29
|
|
|
22
30
|
const SIDE_OFFSET = 6;
|
|
23
31
|
|
|
32
|
+
export const ToolbarMenuContext = createContext<{
|
|
33
|
+
dividerOverflow?: number;
|
|
34
|
+
}>({});
|
|
35
|
+
|
|
24
36
|
export const ToolbarMenuDropdown = memoGeneric(function ToolbarMenuDropdown<
|
|
25
37
|
T extends string,
|
|
26
38
|
>({
|
|
@@ -63,7 +75,7 @@ export const ToolbarShortcut = ({
|
|
|
63
75
|
label?: ReactNode;
|
|
64
76
|
}) => {
|
|
65
77
|
return (
|
|
66
|
-
<div className="flex items-center">
|
|
78
|
+
<div className="n-flex n-items-center">
|
|
67
79
|
{label}
|
|
68
80
|
<Spacer.Horizontal size={6} />
|
|
69
81
|
<KeyboardShortcut shortcut={shortcut} />
|
|
@@ -137,19 +149,23 @@ export const ToolbarMenuButton = memoGeneric(function ToolbarMenuButton<
|
|
|
137
149
|
as,
|
|
138
150
|
activeValue,
|
|
139
151
|
tooltipSide,
|
|
152
|
+
displayFilter,
|
|
140
153
|
}: {
|
|
141
154
|
item: SelectableMenuItem<T>;
|
|
142
155
|
onSelectMenuItem?: (value: T) => void;
|
|
143
156
|
as?: ComponentProps<typeof Button>["as"];
|
|
144
157
|
activeValue?: T;
|
|
145
158
|
tooltipSide?: ComponentProps<typeof Tooltip>["side"];
|
|
159
|
+
displayFilter?: DisplayFilter;
|
|
146
160
|
}) {
|
|
161
|
+
const isActive = item.checked || activeValue === item.value;
|
|
162
|
+
|
|
147
163
|
const content = (
|
|
148
164
|
<Button
|
|
149
165
|
as={as}
|
|
150
166
|
disabled={item.disabled}
|
|
151
|
-
active={
|
|
152
|
-
icon={item.icon}
|
|
167
|
+
active={isActive}
|
|
168
|
+
icon={displayFilter === "textOnly" ? undefined : item.icon}
|
|
153
169
|
{...(as === "a" && { href: item.value })}
|
|
154
170
|
onClick={() => {
|
|
155
171
|
if (onSelectMenuItem && item.value) {
|
|
@@ -157,12 +173,18 @@ export const ToolbarMenuButton = memoGeneric(function ToolbarMenuButton<
|
|
|
157
173
|
}
|
|
158
174
|
}}
|
|
159
175
|
>
|
|
160
|
-
{item.title}
|
|
176
|
+
{displayFilter === "iconOnly" ? undefined : item.title}
|
|
161
177
|
</Button>
|
|
162
178
|
);
|
|
163
179
|
|
|
164
|
-
|
|
165
|
-
|
|
180
|
+
const tooltip =
|
|
181
|
+
item.tooltip ??
|
|
182
|
+
(displayFilter === "iconOnly" && item.title ? item.title : undefined);
|
|
183
|
+
|
|
184
|
+
return item.drawer && isActive ? (
|
|
185
|
+
<ToolbarDrawer trigger={content}>{item.drawer}</ToolbarDrawer>
|
|
186
|
+
) : tooltip ? (
|
|
187
|
+
<Tooltip sideOffset={SIDE_OFFSET} content={tooltip} side={tooltipSide}>
|
|
166
188
|
{content}
|
|
167
189
|
</Tooltip>
|
|
168
190
|
) : (
|
|
@@ -179,6 +201,8 @@ export const ToolbarMenuItem = memoGeneric(function ToolbarMenuItem<
|
|
|
179
201
|
activeValue,
|
|
180
202
|
tooltipSide,
|
|
181
203
|
portalContainer,
|
|
204
|
+
displayFilter,
|
|
205
|
+
dividerOverflow: dividerOverflowProp,
|
|
182
206
|
}: {
|
|
183
207
|
item: MenuItem<T>;
|
|
184
208
|
onSelectMenuItem?: (value: T) => void;
|
|
@@ -186,14 +210,24 @@ export const ToolbarMenuItem = memoGeneric(function ToolbarMenuItem<
|
|
|
186
210
|
activeValue?: T;
|
|
187
211
|
tooltipSide?: ComponentProps<typeof Tooltip>["side"];
|
|
188
212
|
portalContainer?: HTMLElement | null;
|
|
213
|
+
displayFilter?: DisplayFilter;
|
|
214
|
+
dividerOverflow?: number;
|
|
189
215
|
}) {
|
|
216
|
+
const { dividerOverflow: contextDividerOverflow } =
|
|
217
|
+
useContext(ToolbarMenuContext);
|
|
218
|
+
|
|
219
|
+
const dividerOverflow = dividerOverflowProp ?? contextDividerOverflow ?? 4;
|
|
220
|
+
|
|
190
221
|
if (item.type === "sectionHeader") return null;
|
|
222
|
+
|
|
191
223
|
if (item.type === "separator") {
|
|
192
|
-
return <DividerVertical overflow={
|
|
224
|
+
return <DividerVertical overflow={dividerOverflow} />;
|
|
193
225
|
}
|
|
226
|
+
|
|
194
227
|
if (item.type === "popover") {
|
|
195
228
|
return <ToolbarMenuPopover item={item} portalContainer={portalContainer} />;
|
|
196
229
|
}
|
|
230
|
+
|
|
197
231
|
if (isSelectableMenuItem(item)) {
|
|
198
232
|
return (
|
|
199
233
|
<ToolbarMenuButton
|
|
@@ -202,6 +236,7 @@ export const ToolbarMenuItem = memoGeneric(function ToolbarMenuItem<
|
|
|
202
236
|
as={buttonAs}
|
|
203
237
|
activeValue={activeValue}
|
|
204
238
|
tooltipSide={tooltipSide}
|
|
239
|
+
displayFilter={displayFilter}
|
|
205
240
|
/>
|
|
206
241
|
);
|
|
207
242
|
}
|
|
@@ -211,6 +246,8 @@ export const ToolbarMenuItem = memoGeneric(function ToolbarMenuItem<
|
|
|
211
246
|
);
|
|
212
247
|
});
|
|
213
248
|
|
|
249
|
+
type DisplayFilter = "iconOnly" | "iconAndText" | "textOnly";
|
|
250
|
+
|
|
214
251
|
export const ToolbarMenu = memoGeneric(function ToolbarMenu<T extends string>({
|
|
215
252
|
items,
|
|
216
253
|
onSelectMenuItem,
|
|
@@ -218,6 +255,8 @@ export const ToolbarMenu = memoGeneric(function ToolbarMenu<T extends string>({
|
|
|
218
255
|
activeValue,
|
|
219
256
|
tooltipSide,
|
|
220
257
|
portalContainer,
|
|
258
|
+
displayFilter = "iconAndText",
|
|
259
|
+
dividerOverflow,
|
|
221
260
|
}: {
|
|
222
261
|
items: MenuItem<T>[];
|
|
223
262
|
onSelectMenuItem?: (value: T) => void;
|
|
@@ -225,6 +264,8 @@ export const ToolbarMenu = memoGeneric(function ToolbarMenu<T extends string>({
|
|
|
225
264
|
activeValue?: T;
|
|
226
265
|
tooltipSide?: ComponentProps<typeof Tooltip>["side"];
|
|
227
266
|
portalContainer?: HTMLElement | null;
|
|
267
|
+
displayFilter?: DisplayFilter;
|
|
268
|
+
dividerOverflow?: number;
|
|
228
269
|
}) {
|
|
229
270
|
return (
|
|
230
271
|
<>
|
|
@@ -238,6 +279,8 @@ export const ToolbarMenu = memoGeneric(function ToolbarMenu<T extends string>({
|
|
|
238
279
|
activeValue={activeValue}
|
|
239
280
|
tooltipSide={tooltipSide}
|
|
240
281
|
portalContainer={portalContainer}
|
|
282
|
+
displayFilter={displayFilter}
|
|
283
|
+
dividerOverflow={dividerOverflow}
|
|
241
284
|
/>
|
|
242
285
|
);
|
|
243
286
|
})}
|
|
@@ -253,6 +296,7 @@ export interface ToolbarProps<T extends string = string> {
|
|
|
253
296
|
onSelectMenuItem?: (value: T) => void;
|
|
254
297
|
/** Whether to bind keyboard shortcuts for menu items. @default true */
|
|
255
298
|
shouldBindKeyboardShortcuts?: boolean;
|
|
299
|
+
dividerOverflow?: number;
|
|
256
300
|
}
|
|
257
301
|
|
|
258
302
|
export function Toolbar<T extends string = string>({
|
|
@@ -262,6 +306,7 @@ export function Toolbar<T extends string = string>({
|
|
|
262
306
|
rightMenuItems = [],
|
|
263
307
|
onSelectMenuItem,
|
|
264
308
|
shouldBindKeyboardShortcuts = true,
|
|
309
|
+
dividerOverflow,
|
|
265
310
|
}: ToolbarProps<T>) {
|
|
266
311
|
// Register keyboard shortcuts for all menu items
|
|
267
312
|
const allMenuItems = React.useMemo(
|
|
@@ -284,20 +329,22 @@ export function Toolbar<T extends string = string>({
|
|
|
284
329
|
logo={logo}
|
|
285
330
|
left={
|
|
286
331
|
leftMenuItems.length > 0 && (
|
|
287
|
-
<div className="flex gap-2">
|
|
332
|
+
<div className="n-flex n-gap-2">
|
|
288
333
|
<ToolbarMenu
|
|
289
334
|
items={leftMenuItems}
|
|
290
335
|
onSelectMenuItem={onSelectMenuItem}
|
|
336
|
+
dividerOverflow={dividerOverflow}
|
|
291
337
|
/>
|
|
292
338
|
</div>
|
|
293
339
|
)
|
|
294
340
|
}
|
|
295
341
|
right={
|
|
296
342
|
rightMenuItems.length > 0 && (
|
|
297
|
-
<div className="flex gap-2">
|
|
343
|
+
<div className="n-flex n-gap-2">
|
|
298
344
|
<ToolbarMenu
|
|
299
345
|
items={rightMenuItems}
|
|
300
346
|
onSelectMenuItem={onSelectMenuItem}
|
|
347
|
+
dividerOverflow={dividerOverflow}
|
|
301
348
|
/>
|
|
302
349
|
</div>
|
|
303
350
|
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React, { ForwardedRef, forwardRef, memo, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
type ToolbarDrawerProps = {
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
trigger: ReactNode;
|
|
6
|
+
sideOffset?: number;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const ToolbarDrawer = memo(
|
|
10
|
+
forwardRef(function ToolbarDrawer(
|
|
11
|
+
{ children, trigger, sideOffset = 8 }: ToolbarDrawerProps,
|
|
12
|
+
ref: ForwardedRef<HTMLDivElement>
|
|
13
|
+
) {
|
|
14
|
+
return (
|
|
15
|
+
<div className="n-relative" ref={ref}>
|
|
16
|
+
<div
|
|
17
|
+
className="n-absolute n-bottom-full n-left-1/2 -n-translate-x-1/2 n-rounded-t-lg n-border n-border-toolbar-drawer-border n-border-b-transparent n-bg-toolbar-drawer-background -n-z-10"
|
|
18
|
+
style={{
|
|
19
|
+
marginBottom: `${sideOffset}px`,
|
|
20
|
+
boxShadow: "0px -2px 12px 0px var(--n-toolbar-drawer-shadow)",
|
|
21
|
+
}}
|
|
22
|
+
>
|
|
23
|
+
{children}
|
|
24
|
+
</div>
|
|
25
|
+
{trigger}
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
28
|
+
})
|
|
29
|
+
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Tooltip as TooltipPrimitive } from "radix-ui";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { ComponentProps } from "react";
|
|
4
4
|
import {
|
|
@@ -35,7 +35,7 @@ export const Tooltip = React.memo(function Tooltip({
|
|
|
35
35
|
align="center"
|
|
36
36
|
sideOffset={sideOffset}
|
|
37
37
|
collisionPadding={8}
|
|
38
|
-
className={`${textStyles.small} text-text rounded-sm p-2 bg-popover-background shadow-tooltip z-menu`}
|
|
38
|
+
className={`${textStyles.small} n-text-text n-rounded-sm n-p-2 n-bg-popover-background n-shadow-tooltip n-z-menu`}
|
|
39
39
|
>
|
|
40
40
|
{content}
|
|
41
41
|
</TooltipPrimitive.Content>
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
forwardRefGeneric,
|
|
5
|
+
memoGeneric,
|
|
6
|
+
useStableCallback,
|
|
7
|
+
} from "@noya-app/react-utils";
|
|
2
8
|
import React, { ForwardedRef, ReactNode, useCallback, useContext } from "react";
|
|
3
9
|
import { IconButton } from "./IconButton";
|
|
4
10
|
import { IconName, renderIcon } from "./Icons";
|
|
5
11
|
import { ListView } from "./ListView";
|
|
6
12
|
import { Spacer } from "./Spacer";
|
|
13
|
+
import { ListViewContext } from "./listView/ListViewContexts";
|
|
7
14
|
|
|
8
15
|
/* ----------------------------------------------------------------------------
|
|
9
16
|
* Row
|
|
@@ -31,14 +38,16 @@ const TreeRow = forwardRefGeneric(function TreeRow<MenuItemType extends string>(
|
|
|
31
38
|
}: TreeViewRowProps<MenuItemType>,
|
|
32
39
|
forwardedRef: ForwardedRef<HTMLLIElement>
|
|
33
40
|
) {
|
|
34
|
-
const { expandable } = useContext(
|
|
41
|
+
const { expandable } = useContext(ListViewContext);
|
|
42
|
+
|
|
43
|
+
const stableOnClickChevron = useStableCallback(onClickChevron);
|
|
35
44
|
|
|
36
45
|
const handleClickChevron = useCallback(
|
|
37
46
|
(event: React.MouseEvent) => {
|
|
38
47
|
event.stopPropagation();
|
|
39
|
-
|
|
48
|
+
stableOnClickChevron?.({ altKey: event.altKey });
|
|
40
49
|
},
|
|
41
|
-
[
|
|
50
|
+
[stableOnClickChevron]
|
|
42
51
|
);
|
|
43
52
|
|
|
44
53
|
return (
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import React, { memo, useMemo } from "react";
|
|
2
4
|
import { cx } from "../utils/classNames";
|
|
3
5
|
import { colorFromString } from "../utils/colorFromString";
|
|
@@ -25,8 +27,8 @@ const UserPointerContainer = memo(function UserPointerContainer({
|
|
|
25
27
|
...style,
|
|
26
28
|
}}
|
|
27
29
|
className={cx(
|
|
28
|
-
"pointer-events-none",
|
|
29
|
-
visible ? "opacity-100" : "opacity-0",
|
|
30
|
+
"n-pointer-events-none",
|
|
31
|
+
visible ? "n-opacity-100" : "n-opacity-0",
|
|
30
32
|
className
|
|
31
33
|
)}
|
|
32
34
|
>
|
|
@@ -49,7 +51,7 @@ const UserNameTag = memo(function UserNameTag({
|
|
|
49
51
|
return (
|
|
50
52
|
<span
|
|
51
53
|
className={cx(
|
|
52
|
-
"relative inline-block rounded-full text-xs font-medium leading-none text-white shadow-sm px-2 py-1.5 border border-background",
|
|
54
|
+
"n-relative n-inline-block n-rounded-full n-text-xs n-font-medium n-leading-none n-text-white n-shadow-sm n-px-2 n-py-1.5 n-border n-border-background",
|
|
53
55
|
className
|
|
54
56
|
)}
|
|
55
57
|
style={{
|
|
@@ -151,7 +153,7 @@ export const UserPointer = memo(function UserPointer({
|
|
|
151
153
|
style={style}
|
|
152
154
|
>
|
|
153
155
|
{name && (
|
|
154
|
-
<div className="relative">
|
|
156
|
+
<div className="n-relative">
|
|
155
157
|
<UserPointerIcon
|
|
156
158
|
size={POINTER_SIZE}
|
|
157
159
|
color={userBackgroundColor}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { IconButton } from "../IconButton";
|
|
2
4
|
|
|
3
5
|
import { cssVars } from "../../theme";
|
|
@@ -12,7 +14,7 @@ import { TextAreaRow } from "../TextArea";
|
|
|
12
14
|
|
|
13
15
|
export const AIAssistantLoadingIndicator = () => {
|
|
14
16
|
return (
|
|
15
|
-
<div className="flex gap-2">
|
|
17
|
+
<div className="n-flex n-gap-2">
|
|
16
18
|
<Avatar
|
|
17
19
|
name="Assistant"
|
|
18
20
|
size={INPUT_HEIGHT}
|
|
@@ -20,7 +22,7 @@ export const AIAssistantLoadingIndicator = () => {
|
|
|
20
22
|
>
|
|
21
23
|
<Logo style={{ width: 15 }} fill={cssVars.colors.primary} />
|
|
22
24
|
</Avatar>
|
|
23
|
-
<div className="animate-pulse">▋</div>
|
|
25
|
+
<div className="n-animate-pulse">▋</div>
|
|
24
26
|
</div>
|
|
25
27
|
);
|
|
26
28
|
};
|
|
@@ -43,7 +45,7 @@ export const AIAssistantInput = forwardRef<
|
|
|
43
45
|
value={value}
|
|
44
46
|
onChange={onChange}
|
|
45
47
|
ref={ref}
|
|
46
|
-
textAreaClassName="py-2 pl-3 !pr-9 w-full mt-3"
|
|
48
|
+
textAreaClassName="n-py-2 n-pl-3 !n-pr-9 n-w-full n-mt-3"
|
|
47
49
|
placeholder="Tell me what you'd like to do..."
|
|
48
50
|
onKeyDown={onKeyDown}
|
|
49
51
|
disabled={disabled}
|
|
@@ -52,7 +54,7 @@ export const AIAssistantInput = forwardRef<
|
|
|
52
54
|
iconName="ArrowUpIcon"
|
|
53
55
|
onClick={onSubmitClick}
|
|
54
56
|
disabled={!value.trim() || disabled}
|
|
55
|
-
className="h-8 w-8 z-20 rounded-full"
|
|
57
|
+
className="n-h-8 n-w-8 n-z-20 n-rounded-full"
|
|
56
58
|
size={20}
|
|
57
59
|
color={cssVars.colors.text}
|
|
58
60
|
style={{
|
|
@@ -88,14 +90,14 @@ export const AIAssistantLayout = forwardRef<
|
|
|
88
90
|
ref
|
|
89
91
|
) => {
|
|
90
92
|
return (
|
|
91
|
-
<div className={cx("flex flex-col flex-1", className)} style={style}>
|
|
92
|
-
<div className="flex-1 min-h-0 overflow-auto" ref={ref}>
|
|
93
|
-
<div className="flex flex-col gap-2 pb-4 pt-1">
|
|
93
|
+
<div className={cx("n-flex n-flex-col n-flex-1", className)} style={style}>
|
|
94
|
+
<div className="n-flex-1 n-min-h-0 n-overflow-auto" ref={ref}>
|
|
95
|
+
<div className="n-flex n-flex-col n-gap-2 n-pb-4 n-pt-1">
|
|
94
96
|
{renderMessages()}
|
|
95
97
|
{isLoading ? renderLoadingIndicator?.() : null}
|
|
96
98
|
</div>
|
|
97
99
|
</div>
|
|
98
|
-
<div className="border-t border-divider pt-1 px-1">{renderInput()}</div>
|
|
100
|
+
<div className="n-border-t n-border-divider n-pt-1 n-px-1">{renderInput()}</div>
|
|
99
101
|
</div>
|
|
100
102
|
);
|
|
101
103
|
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { ActivityIndicator, cx } from "@noya-app/noya-designsystem";
|
|
2
|
+
import { ImageIcon } from "@noya-app/noya-icons";
|
|
3
|
+
import React, { ForwardedRef, forwardRef } from "react";
|
|
4
|
+
|
|
5
|
+
type ImageState = "loading" | "error" | "loaded";
|
|
6
|
+
|
|
7
|
+
const placeholderClassName =
|
|
8
|
+
"n-cursor-pointer n-flex n-items-center n-justify-center n-p-4 n-border n-border-dashed n-border-indigo-100 n-rounded-md n-bg-indigo-50";
|
|
9
|
+
|
|
10
|
+
export function decodeImageSrc(src?: string): string | undefined {
|
|
11
|
+
if (!src) return undefined;
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
return decodeURIComponent(src);
|
|
15
|
+
} catch (error) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const ImageLoadingComponent = forwardRef(function ImageLoadingComponent(
|
|
21
|
+
props: React.HTMLAttributes<HTMLSpanElement>,
|
|
22
|
+
forwardedRef: ForwardedRef<HTMLSpanElement>
|
|
23
|
+
) {
|
|
24
|
+
return (
|
|
25
|
+
<span
|
|
26
|
+
{...props}
|
|
27
|
+
ref={forwardedRef}
|
|
28
|
+
contentEditable={false}
|
|
29
|
+
className={cx(placeholderClassName, props.className)}
|
|
30
|
+
>
|
|
31
|
+
<ActivityIndicator />
|
|
32
|
+
</span>
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const ImagePlaceholderComponent = forwardRef(function ImagePlaceholderComponent(
|
|
37
|
+
props: React.HTMLAttributes<HTMLSpanElement>,
|
|
38
|
+
forwardedRef: ForwardedRef<HTMLSpanElement>
|
|
39
|
+
) {
|
|
40
|
+
return (
|
|
41
|
+
<span
|
|
42
|
+
{...props}
|
|
43
|
+
ref={forwardedRef}
|
|
44
|
+
contentEditable={false}
|
|
45
|
+
className={cx(placeholderClassName, props.className)}
|
|
46
|
+
>
|
|
47
|
+
<ImageIcon className="n-w-[30px] n-h-[30px] n-text-indigo-900 n-pointer-events-none" />
|
|
48
|
+
</span>
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const ImageErrorComponent = forwardRef(function ImageErrorComponent(
|
|
53
|
+
{ src, ...props }: React.HTMLAttributes<HTMLSpanElement> & { src?: string },
|
|
54
|
+
forwardedRef: ForwardedRef<HTMLSpanElement>
|
|
55
|
+
) {
|
|
56
|
+
return (
|
|
57
|
+
<span
|
|
58
|
+
{...props}
|
|
59
|
+
ref={forwardedRef}
|
|
60
|
+
contentEditable={false}
|
|
61
|
+
className={cx(placeholderClassName, props.className)}
|
|
62
|
+
>
|
|
63
|
+
<span className="n-text-cm-error n-font-mono n-text-sm">
|
|
64
|
+
Invalid image URL: {src ?? "No URL provided"}
|
|
65
|
+
</span>
|
|
66
|
+
</span>
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const ImageDisplayComponent = forwardRef(function ImageDisplayComponent(
|
|
71
|
+
{
|
|
72
|
+
src,
|
|
73
|
+
alt,
|
|
74
|
+
title,
|
|
75
|
+
...props
|
|
76
|
+
}: React.DetailedHTMLProps<
|
|
77
|
+
React.ImgHTMLAttributes<HTMLImageElement>,
|
|
78
|
+
HTMLImageElement
|
|
79
|
+
>,
|
|
80
|
+
forwardedRef: ForwardedRef<HTMLImageElement>
|
|
81
|
+
) {
|
|
82
|
+
return (
|
|
83
|
+
<img
|
|
84
|
+
{...props}
|
|
85
|
+
ref={forwardedRef}
|
|
86
|
+
src={src}
|
|
87
|
+
alt={alt ?? ""}
|
|
88
|
+
title={title ?? undefined}
|
|
89
|
+
// use styles over classname for rendering to static HTML
|
|
90
|
+
style={{
|
|
91
|
+
maxWidth: "100%",
|
|
92
|
+
maxHeight: "720px",
|
|
93
|
+
height: "auto",
|
|
94
|
+
objectFit: "contain",
|
|
95
|
+
margin: "32px auto",
|
|
96
|
+
...props.style,
|
|
97
|
+
}}
|
|
98
|
+
className={cx("n-cursor-pointer", props.className)}
|
|
99
|
+
/>
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const ImageBlockComponent_ = forwardRef(function ImageBlockComponent(
|
|
104
|
+
{
|
|
105
|
+
src,
|
|
106
|
+
alt,
|
|
107
|
+
title,
|
|
108
|
+
imageState,
|
|
109
|
+
...props
|
|
110
|
+
}: React.DetailedHTMLProps<
|
|
111
|
+
React.ImgHTMLAttributes<HTMLImageElement>,
|
|
112
|
+
HTMLImageElement
|
|
113
|
+
> & { imageState?: ImageState },
|
|
114
|
+
forwardedRef: ForwardedRef<HTMLImageElement>
|
|
115
|
+
) {
|
|
116
|
+
if (imageState === "loading") {
|
|
117
|
+
return (
|
|
118
|
+
<ImageLoadingComponent
|
|
119
|
+
{...props}
|
|
120
|
+
ref={forwardedRef as ForwardedRef<HTMLSpanElement>}
|
|
121
|
+
/>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (!src) {
|
|
126
|
+
return (
|
|
127
|
+
<ImagePlaceholderComponent
|
|
128
|
+
{...props}
|
|
129
|
+
ref={forwardedRef as ForwardedRef<HTMLSpanElement>}
|
|
130
|
+
/>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const decodedSrc = decodeImageSrc(src.toString());
|
|
135
|
+
|
|
136
|
+
if (!decodedSrc || imageState === "error") {
|
|
137
|
+
return (
|
|
138
|
+
<ImageErrorComponent
|
|
139
|
+
{...props}
|
|
140
|
+
src={src.toString()}
|
|
141
|
+
ref={forwardedRef as ForwardedRef<HTMLSpanElement>}
|
|
142
|
+
/>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<ImageDisplayComponent
|
|
148
|
+
{...props}
|
|
149
|
+
ref={forwardedRef}
|
|
150
|
+
src={src}
|
|
151
|
+
alt={alt}
|
|
152
|
+
title={title}
|
|
153
|
+
/>
|
|
154
|
+
);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
export const ImageBlockComponent = Object.assign(ImageBlockComponent_, {
|
|
158
|
+
Loading: ImageLoadingComponent,
|
|
159
|
+
Placeholder: ImagePlaceholderComponent,
|
|
160
|
+
Error: ImageErrorComponent,
|
|
161
|
+
Display: ImageDisplayComponent,
|
|
162
|
+
});
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import React from "react";
|
|
2
4
|
import { INPUT_HEIGHT } from "../../theme";
|
|
3
5
|
import { Avatar, AvatarProps, AvatarStack } from "../Avatar";
|
|
4
|
-
import Button from "../Button";
|
|
6
|
+
import { Button } from "../Button";
|
|
5
7
|
import { Small } from "../Text";
|
|
6
8
|
import { Tooltip } from "../Tooltip";
|
|
7
9
|
|
|
@@ -9,7 +11,7 @@ export const UserAvatar = ({ userId, name, image }: AvatarProps) => (
|
|
|
9
11
|
<Tooltip
|
|
10
12
|
key={userId}
|
|
11
13
|
content={
|
|
12
|
-
<div className="flex flex-col">
|
|
14
|
+
<div className="n-flex n-flex-col">
|
|
13
15
|
<Small>{name}</Small>
|
|
14
16
|
</div>
|
|
15
17
|
}
|
|
@@ -30,8 +32,8 @@ export const ConnectedUsersMenuLayout = ({
|
|
|
30
32
|
isAssistantOpen,
|
|
31
33
|
}: ConnectedUsersMenuLayoutProps) => {
|
|
32
34
|
return (
|
|
33
|
-
<div className="flex gap-1.5">
|
|
34
|
-
<AvatarStack size={INPUT_HEIGHT} className="mr-1">
|
|
35
|
+
<div className="n-flex n-gap-1.5">
|
|
36
|
+
<AvatarStack size={INPUT_HEIGHT} className="n-mr-1">
|
|
35
37
|
{renderUsers()}
|
|
36
38
|
</AvatarStack>
|
|
37
39
|
{launchAIAssistant && (
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
ActivityIndicator,
|
|
3
5
|
Banner,
|
|
@@ -7,7 +9,7 @@ import {
|
|
|
7
9
|
SectionProps,
|
|
8
10
|
} from "@noya-app/noya-designsystem";
|
|
9
11
|
import { forwardRefGeneric } from "@noya-app/react-utils";
|
|
10
|
-
import React from "react";
|
|
12
|
+
import React, { CSSProperties } from "react";
|
|
11
13
|
import { cx } from "../../utils/classNames";
|
|
12
14
|
import {
|
|
13
15
|
Collection,
|
|
@@ -21,7 +23,7 @@ export const FileExplorerLayout = ({
|
|
|
21
23
|
className,
|
|
22
24
|
...props
|
|
23
25
|
}: SectionProps) => (
|
|
24
|
-
<Section className={cx(className, "px-3 flex-1")} {...props}>
|
|
26
|
+
<Section className={cx(className, "n-px-3 n-flex-1")} {...props}>
|
|
25
27
|
{children}
|
|
26
28
|
</Section>
|
|
27
29
|
);
|
|
@@ -37,7 +39,7 @@ export const FileExplorerUploadButton = ({
|
|
|
37
39
|
isUploading?: boolean;
|
|
38
40
|
children?: React.ReactNode;
|
|
39
41
|
}) => (
|
|
40
|
-
<div className="flex items-center gap-2">
|
|
42
|
+
<div className="n-flex n-items-center n-gap-2">
|
|
41
43
|
{showUploadButton && (
|
|
42
44
|
<Button
|
|
43
45
|
icon={isUploading ? <ActivityIndicator size={15} /> : "UploadIcon"}
|
|
@@ -58,7 +60,9 @@ export const FileExplorerCollection = forwardRefGeneric(
|
|
|
58
60
|
{ ...props }: CollectionProps<T, M>,
|
|
59
61
|
ref: React.ForwardedRef<CollectionRef>
|
|
60
62
|
) {
|
|
61
|
-
return
|
|
63
|
+
return (
|
|
64
|
+
<Collection<T, M> ref={ref} className="-n-mx-3 n-flex-1" {...props} />
|
|
65
|
+
);
|
|
62
66
|
}
|
|
63
67
|
);
|
|
64
68
|
|
|
@@ -66,22 +70,32 @@ export const FileExplorerDetail = ({
|
|
|
66
70
|
selected,
|
|
67
71
|
size,
|
|
68
72
|
children,
|
|
73
|
+
className,
|
|
74
|
+
style,
|
|
69
75
|
}: {
|
|
70
76
|
selected: boolean;
|
|
71
77
|
children: React.ReactNode;
|
|
72
78
|
size?: CollectionItemSize;
|
|
79
|
+
className?: string;
|
|
80
|
+
style?: CSSProperties;
|
|
73
81
|
}) => (
|
|
74
82
|
<span
|
|
75
83
|
className={cx(
|
|
76
|
-
size === "small" ? "text-list-small" : "text-sm",
|
|
77
|
-
selected ? "text-primary" : "text-text-muted"
|
|
84
|
+
size === "small" ? "n-text-list-small" : "n-text-sm",
|
|
85
|
+
selected ? "n-text-primary" : "n-text-text-muted",
|
|
86
|
+
className
|
|
78
87
|
)}
|
|
88
|
+
style={style}
|
|
79
89
|
>
|
|
80
90
|
{children}
|
|
81
91
|
</span>
|
|
82
92
|
);
|
|
83
93
|
|
|
84
94
|
export const FileExplorerEmptyState = ({
|
|
85
|
-
label = "No files",
|
|
95
|
+
children: label = "No files",
|
|
86
96
|
...props
|
|
87
|
-
}: BannerProps) =>
|
|
97
|
+
}: BannerProps) => (
|
|
98
|
+
<Banner className="n-mx-3" {...props}>
|
|
99
|
+
{label}
|
|
100
|
+
</Banner>
|
|
101
|
+
);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
export function Checkmark(props: React.SVGProps<SVGSVGElement>) {
|
|
4
|
+
return (
|
|
5
|
+
<svg
|
|
6
|
+
viewBox="0 0 15 15"
|
|
7
|
+
fill="none"
|
|
8
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
9
|
+
aria-hidden="true"
|
|
10
|
+
width={15}
|
|
11
|
+
height={15}
|
|
12
|
+
{...props}
|
|
13
|
+
>
|
|
14
|
+
<path
|
|
15
|
+
className="n-scale-90 n-origin-center"
|
|
16
|
+
d="M3 8L6 11L12 5"
|
|
17
|
+
strokeWidth="1.1"
|
|
18
|
+
strokeLinecap="round"
|
|
19
|
+
strokeLinejoin="round"
|
|
20
|
+
stroke="currentColor"
|
|
21
|
+
/>
|
|
22
|
+
</svg>
|
|
23
|
+
);
|
|
24
|
+
}
|