@noya-app/noya-designsystem 0.1.62 → 0.1.64
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/.eslintrc.js +1 -5
- package/.turbo/turbo-build.log +10 -10
- package/.turbo/turbo-lint.log +0 -1
- package/CHANGELOG.md +23 -0
- package/dist/index.css +1 -1
- package/dist/index.d.mts +299 -179
- package/dist/index.d.ts +299 -179
- package/dist/index.js +3772 -2999
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3485 -2720
- 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 +5 -3
- package/src/components/AnimatePresence.tsx +6 -4
- package/src/components/Avatar.tsx +4 -2
- package/src/components/BaseToolbar.tsx +45 -34
- package/src/components/Breadcrumbs.tsx +2 -0
- package/src/components/Button.tsx +35 -21
- package/src/components/Chip.tsx +2 -0
- package/src/components/Collection.tsx +4 -1
- package/src/components/ColorSwatch.tsx +42 -22
- package/src/components/ColorSwatchControl.tsx +5 -3
- package/src/components/Combobox.tsx +2 -0
- package/src/components/CommandPalette.tsx +2 -0
- package/src/components/ContextMenu.tsx +3 -1
- package/src/components/Dialog.tsx +3 -1
- package/src/components/DimensionInput.tsx +3 -1
- package/src/components/Divider.tsx +10 -6
- package/src/components/DraggableMenuButton.tsx +22 -4
- package/src/components/Drawer.tsx +11 -3
- package/src/components/DropdownMenu.tsx +34 -6
- package/src/components/EditableText.tsx +2 -0
- package/src/components/FileUploadIndicator.tsx +108 -0
- package/src/components/FillPreviewBackground.tsx +2 -0
- package/src/components/FloatingWindow.tsx +2 -0
- package/src/components/GradientPicker.tsx +2 -0
- package/src/components/Grid.tsx +47 -38
- package/src/components/GridView.tsx +33 -11
- package/src/components/IconButton.tsx +4 -2
- package/src/components/Icons.tsx +1 -1
- package/src/components/InputField.tsx +7 -5
- package/src/components/Label.tsx +1 -3
- package/src/components/LabeledElementView.tsx +6 -2
- package/src/components/LabeledField.tsx +38 -34
- package/src/components/List.tsx +10 -2
- package/src/components/ListView.tsx +27 -14
- package/src/components/MediaThumbnail.tsx +31 -2
- package/src/components/Message.tsx +8 -9
- package/src/components/Popover.tsx +15 -4
- package/src/components/Progress.tsx +20 -7
- package/src/components/ScrollArea.tsx +3 -1
- package/src/components/SearchCompletionMenu.tsx +2 -0
- package/src/components/Section.tsx +2 -0
- package/src/components/SegmentedControl.tsx +43 -17
- package/src/components/SelectMenu.tsx +6 -4
- package/src/components/SelectionToolbar.tsx +31 -8
- package/src/components/Slider.tsx +4 -2
- package/src/components/Spacer.tsx +0 -1
- package/src/components/Switch.tsx +3 -1
- package/src/components/Tabs.tsx +48 -0
- package/src/components/Text.tsx +6 -5
- package/src/components/TextArea.tsx +2 -0
- package/src/components/Toast.tsx +1 -1
- package/src/components/Toolbar.tsx +113 -12
- package/src/components/ToolbarDrawer.tsx +29 -0
- package/src/components/Tooltip.tsx +9 -9
- package/src/components/TreeView.tsx +2 -0
- package/src/components/UserPointer.tsx +2 -0
- package/src/components/ai-assistant/AIAssistantLayout.tsx +2 -0
- package/src/components/blocks/ImageBlockComponent.tsx +156 -0
- package/src/components/connected-users-menu/ConnectedUsersMenuLayout.tsx +2 -0
- package/src/components/file-explorer/FileExplorerLayout.tsx +14 -6
- package/src/components/internal/Checkmark.tsx +24 -0
- package/src/components/internal/Menu.tsx +21 -3
- package/src/components/internal/MenuViewport.tsx +8 -5
- package/src/components/internal/SelectItem.tsx +4 -23
- package/src/components/internal/TextInput.tsx +2 -0
- package/src/components/sorting/DragRegistration.tsx +8 -4
- package/src/components/sorting/SharedDragProvider.tsx +43 -18
- package/src/components/sorting/Sortable.tsx +19 -10
- package/src/components/sorting/createSharedDrag.tsx +7 -6
- package/src/components/sorting/sorting.ts +6 -9
- package/src/components/workspace/DrawerWorkspaceLayout.tsx +113 -28
- package/src/components/workspace/PanelWorkspaceLayout.tsx +149 -16
- package/src/components/workspace/VerticalTabMenu.tsx +36 -0
- package/src/components/workspace/WorkspaceLayout.tsx +132 -47
- package/src/components/workspace/constants.ts +4 -0
- package/src/components/workspace/panelStorage.ts +216 -0
- package/src/components/workspace/renderPanelChildren.tsx +12 -0
- package/src/components/workspace/types.ts +30 -7
- package/src/contexts/DesignSystemConfiguration.tsx +12 -5
- package/src/contexts/DialogContext.tsx +2 -0
- 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 +2 -0
- package/src/hooks/useLabel.ts +12 -10
- 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 +25 -4
- package/src/index.tsx +7 -2
- package/src/utils/classNames.ts +4 -3
- package/src/utils/combobox.ts +2 -0
- package/src/utils/editableBlockStyles.ts +10 -0
- package/src/utils/formatByteSize.ts +8 -0
- package/src/utils/withSeparatorElements.ts +1 -1
- package/src/hooks/usePreservePanelSize.tsx +0 -183
- package/src/hooks/useWindowSize.tsx +0 -26
- package/src/mediaQuery.ts +0 -13
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import React, {
|
|
2
4
|
CSSProperties,
|
|
3
5
|
ForwardedRef,
|
|
@@ -12,29 +14,27 @@ import { Spacer } from "./Spacer";
|
|
|
12
14
|
import { Tooltip } from "./Tooltip";
|
|
13
15
|
import type { MenuItemIcon } from "./internal/Menu";
|
|
14
16
|
|
|
15
|
-
type ButtonVariant =
|
|
16
|
-
| "normal"
|
|
17
|
-
| "primary"
|
|
18
|
-
| "secondary"
|
|
19
|
-
| "secondaryBright"
|
|
20
|
-
| "thin"
|
|
21
|
-
| "floating"
|
|
22
|
-
| "none";
|
|
17
|
+
type ButtonVariant = "normal" | "thin" | "floating" | "none" | "ghost";
|
|
23
18
|
|
|
24
19
|
type ButtonSize = "small" | "normal" | "large" | "floating";
|
|
25
20
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"bg-
|
|
21
|
+
type ButtonColorScheme = "primary" | "secondary";
|
|
22
|
+
|
|
23
|
+
const colorSchemeStyles: Record<ButtonColorScheme | "default", string> = {
|
|
24
|
+
default:
|
|
25
|
+
"bg-button-background text-button-text hover:bg-button-background-hover active:bg-button-background-active",
|
|
26
|
+
primary: "bg-primary text-white hover:bg-primary-light active:bg-primary",
|
|
31
27
|
secondary:
|
|
32
|
-
"bg-secondary text-white hover:bg-secondary-light active:bg-secondary
|
|
33
|
-
|
|
28
|
+
"bg-secondary text-white hover:bg-secondary-light active:bg-secondary",
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const variantStyles: Record<ButtonVariant, string> = {
|
|
32
|
+
normal: "",
|
|
34
33
|
floating:
|
|
35
34
|
"bg-floating-button text-text shadow-[0_1px_2px_rgba(0,0,0,0.1)] hover:opacity-80 active:opacity-90",
|
|
36
35
|
thin: "bg-transparent text-text hover:bg-input-background-light",
|
|
37
36
|
none: "bg-transparent text-text hover:bg-input-background-light",
|
|
37
|
+
ghost: "bg-transparent text-text hover:bg-input-background-light",
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
const sizeStyles: Record<ButtonSize | "thin" | "none", string> = {
|
|
@@ -46,16 +46,21 @@ const sizeStyles: Record<ButtonSize | "thin" | "none", string> = {
|
|
|
46
46
|
none: "py-0 px-0",
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
-
export interface
|
|
49
|
+
export interface ButtonProps
|
|
50
|
+
extends React.DetailedHTMLProps<
|
|
51
|
+
React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
52
|
+
HTMLButtonElement
|
|
53
|
+
> {
|
|
50
54
|
id?: string;
|
|
51
55
|
className?: string;
|
|
52
56
|
style?: CSSProperties;
|
|
53
57
|
tabIndex?: number;
|
|
54
58
|
icon?: MenuItemIcon;
|
|
55
59
|
iconRight?: MenuItemIcon;
|
|
56
|
-
children
|
|
60
|
+
children?: ReactNode;
|
|
57
61
|
active?: boolean;
|
|
58
62
|
disabled?: boolean;
|
|
63
|
+
colorScheme?: ButtonColorScheme;
|
|
59
64
|
variant?: ButtonVariant;
|
|
60
65
|
size?: ButtonSize;
|
|
61
66
|
tooltip?: ReactNode;
|
|
@@ -81,6 +86,7 @@ export const Button = forwardRef(function Button(
|
|
|
81
86
|
tooltip,
|
|
82
87
|
active = false,
|
|
83
88
|
disabled = false,
|
|
89
|
+
colorScheme: colorSchemeProp,
|
|
84
90
|
variant = "normal",
|
|
85
91
|
size = "normal",
|
|
86
92
|
contentStyle,
|
|
@@ -88,15 +94,18 @@ export const Button = forwardRef(function Button(
|
|
|
88
94
|
children,
|
|
89
95
|
as: Component = "button",
|
|
90
96
|
...rest
|
|
91
|
-
}:
|
|
97
|
+
}: ButtonProps,
|
|
92
98
|
forwardedRef: ForwardedRef<HTMLButtonElement>
|
|
93
99
|
) {
|
|
100
|
+
const colorScheme = colorSchemeProp ?? "default";
|
|
101
|
+
|
|
94
102
|
const baseClassName = cx(
|
|
95
|
-
"no-underline leading-[1] relative
|
|
103
|
+
"no-underline leading-[1] relative outline-none select-none text-left rounded flex items-center justify-center [&>*]:pointer-events-none [-webkit-app-region:no-drag] focus:ring-2 focus:ring-primary focus:shadow-[0_0_0_1px_var(--n-popover-background)_inset] transition-all",
|
|
104
|
+
variant === "ghost" ? "border" : "border-0",
|
|
96
105
|
disabled ? "opacity-25" : "",
|
|
97
106
|
!className?.includes("flex") ? "flex-none" : "",
|
|
98
107
|
active
|
|
99
|
-
? "bg-button-background-active text-button-text-active hover:bg-button-background-active-hover
|
|
108
|
+
? "bg-button-background-active text-button-text-active hover:bg-button-background-active-hover"
|
|
100
109
|
: variantStyles[variant],
|
|
101
110
|
sizeStyles[
|
|
102
111
|
variant === "thin"
|
|
@@ -106,7 +115,12 @@ export const Button = forwardRef(function Button(
|
|
|
106
115
|
: variant === "none"
|
|
107
116
|
? "none"
|
|
108
117
|
: size
|
|
109
|
-
]
|
|
118
|
+
],
|
|
119
|
+
variant === "normal" && (children ? "min-w-[31px]" : "min-w-[27px]"),
|
|
120
|
+
variant === "ghost"
|
|
121
|
+
? "border-solid border-input-background hover:bg-input-background-light"
|
|
122
|
+
: colorSchemeStyles[colorScheme],
|
|
123
|
+
!children && (icon || iconRight) && !(icon && iconRight) && "w-[27px]"
|
|
110
124
|
);
|
|
111
125
|
|
|
112
126
|
const buttonClassName = useMemo(() => {
|
package/src/components/Chip.tsx
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { forwardRefGeneric } from "@noya-app/react-utils";
|
|
2
4
|
import React from "react";
|
|
3
5
|
import { Grid } from "../components/Grid";
|
|
@@ -35,6 +37,7 @@ export interface CollectionProps<T, M extends string = string> {
|
|
|
35
37
|
items: T[];
|
|
36
38
|
getId: (item: T) => string;
|
|
37
39
|
getName: (item: T) => string;
|
|
40
|
+
getHref?: (item: T) => string;
|
|
38
41
|
getExpanded?: (item: T) => boolean | undefined;
|
|
39
42
|
getPlaceholder?: (item: T) => string;
|
|
40
43
|
/**
|
|
@@ -90,7 +93,7 @@ export interface CollectionProps<T, M extends string = string> {
|
|
|
90
93
|
viewType?: CollectionViewType;
|
|
91
94
|
onClickItem?: (itemId: string) => void;
|
|
92
95
|
onDoubleClickItem?: (itemId: string) => void;
|
|
93
|
-
renderEmptyState?: () => React.ReactElement
|
|
96
|
+
renderEmptyState?: () => React.ReactElement<any>;
|
|
94
97
|
/** @default false */
|
|
95
98
|
sortable?: boolean;
|
|
96
99
|
dragIndicatorStyle?: ListViewRowProps<M>["dragIndicatorStyle"];
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
import { getContrastColorForBackground } from "@noya-app/noya-colorpicker";
|
|
2
|
+
import { CheckIcon } from "@noya-app/noya-icons";
|
|
1
3
|
import React, { forwardRef } from "react";
|
|
2
|
-
import { cssVars } from "../theme";
|
|
3
4
|
|
|
4
|
-
export type ColorSwatchSize =
|
|
5
|
+
export type ColorSwatchSize =
|
|
6
|
+
| "xxsmall"
|
|
7
|
+
| "xsmall"
|
|
8
|
+
| "small"
|
|
9
|
+
| "medium"
|
|
10
|
+
| "large";
|
|
5
11
|
|
|
6
|
-
export const colorSwatchSizeMap = {
|
|
12
|
+
export const colorSwatchSizeMap: Record<ColorSwatchSize, number> = {
|
|
13
|
+
xxsmall: 15,
|
|
7
14
|
xsmall: 21,
|
|
8
15
|
small: 27,
|
|
9
16
|
medium: 36,
|
|
@@ -14,32 +21,45 @@ type ColorSwatchProps = {
|
|
|
14
21
|
color: string;
|
|
15
22
|
size?: ColorSwatchSize;
|
|
16
23
|
checked?: boolean;
|
|
24
|
+
as?: React.ElementType;
|
|
17
25
|
} & React.ButtonHTMLAttributes<HTMLButtonElement>;
|
|
18
26
|
|
|
19
27
|
export const ColorSwatch = forwardRef(function ColorSwatch(
|
|
20
|
-
{
|
|
28
|
+
{
|
|
29
|
+
color,
|
|
30
|
+
size = "small",
|
|
31
|
+
checked,
|
|
32
|
+
style,
|
|
33
|
+
as: Component = "button",
|
|
34
|
+
...props
|
|
35
|
+
}: ColorSwatchProps,
|
|
21
36
|
ref: React.ForwardedRef<HTMLButtonElement>
|
|
22
37
|
) {
|
|
23
38
|
const sizePx = colorSwatchSizeMap[size];
|
|
39
|
+
const checkedColor = getContrastColorForBackground(color);
|
|
24
40
|
|
|
25
41
|
return (
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
: "1px solid rgba(0,0,0,0.1)",
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
<div className="relative" style={{ width: sizePx, height: sizePx }}>
|
|
43
|
+
<Component
|
|
44
|
+
{...props}
|
|
45
|
+
ref={ref}
|
|
46
|
+
style={{
|
|
47
|
+
display: "inline-block",
|
|
48
|
+
background: color,
|
|
49
|
+
width: sizePx,
|
|
50
|
+
height: sizePx,
|
|
51
|
+
borderRadius: "4px",
|
|
52
|
+
outline: "1px solid rgba(0,0,0,0.1)",
|
|
53
|
+
outlineOffset: -1,
|
|
54
|
+
...style,
|
|
55
|
+
}}
|
|
56
|
+
/>
|
|
57
|
+
{checked && (
|
|
58
|
+
<CheckIcon
|
|
59
|
+
color={checkedColor}
|
|
60
|
+
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"
|
|
61
|
+
/>
|
|
62
|
+
)}
|
|
63
|
+
</div>
|
|
44
64
|
);
|
|
45
65
|
});
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { ToggleGroup as ToggleGroupPrimitive } from "radix-ui";
|
|
2
4
|
import React, { useMemo } from "react";
|
|
3
5
|
import { cx } from "../utils/classNames";
|
|
4
6
|
import {
|
|
@@ -52,7 +54,7 @@ export function ColorSwatchControl(props: ColorSwatchControlProps) {
|
|
|
52
54
|
{options.map((option) => {
|
|
53
55
|
const item = (
|
|
54
56
|
<ColorSwatch
|
|
55
|
-
key={option as string}
|
|
57
|
+
key={`${option as string}-color-swatch`}
|
|
56
58
|
color={option as string}
|
|
57
59
|
size={size}
|
|
58
60
|
onFocus={onFocus}
|
|
@@ -67,7 +69,7 @@ export function ColorSwatchControl(props: ColorSwatchControlProps) {
|
|
|
67
69
|
<ToggleGroupPrimitive.Item
|
|
68
70
|
asChild
|
|
69
71
|
value={option as string}
|
|
70
|
-
key={option as string}
|
|
72
|
+
key={`${option as string}-toggle-group-item`}
|
|
71
73
|
disabled={readOnly}
|
|
72
74
|
>
|
|
73
75
|
{item}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { useKeyboardShortcuts } from "@noya-app/noya-keymap";
|
|
2
4
|
import { memoGeneric } from "@noya-app/react-utils";
|
|
3
|
-
import
|
|
5
|
+
import { ContextMenu as RadixContextMenu } from "radix-ui";
|
|
4
6
|
import React, { ReactNode, useCallback, useMemo } from "react";
|
|
5
7
|
import {
|
|
6
8
|
portalScopeProps,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { round } from "@noya-app/noya-utils";
|
|
2
4
|
import * as React from "react";
|
|
3
5
|
import { InputField } from "./InputField";
|
|
@@ -51,7 +53,7 @@ export const DimensionInput = React.memo(function DimensionInput({
|
|
|
51
53
|
);
|
|
52
54
|
|
|
53
55
|
return (
|
|
54
|
-
<LabeledField label={label}
|
|
56
|
+
<LabeledField label={label} labelType="inset">
|
|
55
57
|
<InputField.Root id={id} width={size}>
|
|
56
58
|
<InputField.NumberInput
|
|
57
59
|
value={value === undefined ? value : round(value, 2)}
|
|
@@ -12,7 +12,7 @@ interface DividerProps {
|
|
|
12
12
|
|
|
13
13
|
export const Divider = memo(function Divider({
|
|
14
14
|
variant = "normal",
|
|
15
|
-
overflow
|
|
15
|
+
overflow,
|
|
16
16
|
className,
|
|
17
17
|
style,
|
|
18
18
|
...props
|
|
@@ -29,8 +29,10 @@ export const Divider = memo(function Divider({
|
|
|
29
29
|
className
|
|
30
30
|
)}
|
|
31
31
|
style={{
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
...(typeof overflow === "number" && {
|
|
33
|
+
marginLeft: `-${overflow}px`,
|
|
34
|
+
marginRight: `-${overflow}px`,
|
|
35
|
+
}),
|
|
34
36
|
...style,
|
|
35
37
|
}}
|
|
36
38
|
{...props}
|
|
@@ -40,7 +42,7 @@ export const Divider = memo(function Divider({
|
|
|
40
42
|
|
|
41
43
|
export const DividerVertical = memo(function DividerVertical({
|
|
42
44
|
variant = "normal",
|
|
43
|
-
overflow
|
|
45
|
+
overflow,
|
|
44
46
|
className,
|
|
45
47
|
style,
|
|
46
48
|
...props
|
|
@@ -57,8 +59,10 @@ export const DividerVertical = memo(function DividerVertical({
|
|
|
57
59
|
className
|
|
58
60
|
)}
|
|
59
61
|
style={{
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
...(typeof overflow === "number" && {
|
|
63
|
+
marginTop: `-${overflow}px`,
|
|
64
|
+
marginBottom: `-${overflow}px`,
|
|
65
|
+
}),
|
|
62
66
|
...style,
|
|
63
67
|
}}
|
|
64
68
|
{...props}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { DragHandleDots2Icon } from "@noya-app/noya-icons";
|
|
2
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
memoGeneric,
|
|
6
|
+
useControlledOrUncontrolled,
|
|
7
|
+
} from "@noya-app/react-utils";
|
|
3
8
|
import React, { useCallback, useState } from "react";
|
|
4
9
|
import { cssVars } from "../theme";
|
|
5
10
|
import { cx } from "../utils/classNames";
|
|
@@ -12,21 +17,33 @@ import { MenuItem } from "./internal/Menu";
|
|
|
12
17
|
export const DraggableMenuButton = memoGeneric(function DraggableMenuButton<
|
|
13
18
|
T extends string,
|
|
14
19
|
>({
|
|
20
|
+
open: openProp,
|
|
21
|
+
onOpenChange: onOpenChangeProp,
|
|
15
22
|
items,
|
|
16
23
|
onSelect,
|
|
17
24
|
onClick,
|
|
18
25
|
isVisible = true,
|
|
19
26
|
className,
|
|
20
27
|
style,
|
|
28
|
+
...rest
|
|
21
29
|
}: {
|
|
30
|
+
open?: boolean;
|
|
31
|
+
onOpenChange?: (open: boolean) => void;
|
|
22
32
|
items?: MenuItem<T>[];
|
|
23
33
|
onSelect?: (value: T) => void;
|
|
24
34
|
onClick?: () => void;
|
|
25
35
|
isVisible?: boolean;
|
|
26
36
|
className?: string;
|
|
27
37
|
style?: React.CSSProperties;
|
|
28
|
-
}
|
|
29
|
-
|
|
38
|
+
} & Omit<
|
|
39
|
+
React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>,
|
|
40
|
+
"onSelect"
|
|
41
|
+
>) {
|
|
42
|
+
const [open, setOpen] = useControlledOrUncontrolled({
|
|
43
|
+
value: openProp,
|
|
44
|
+
onChange: onOpenChangeProp,
|
|
45
|
+
defaultValue: false,
|
|
46
|
+
});
|
|
30
47
|
const [downPosition, setDownPosition] = useState<{
|
|
31
48
|
x: number;
|
|
32
49
|
y: number;
|
|
@@ -64,7 +81,7 @@ export const DraggableMenuButton = memoGeneric(function DraggableMenuButton<
|
|
|
64
81
|
|
|
65
82
|
setDownPosition(null);
|
|
66
83
|
},
|
|
67
|
-
[downPosition, open]
|
|
84
|
+
[downPosition, open, setOpen]
|
|
68
85
|
);
|
|
69
86
|
|
|
70
87
|
const handleOpenChange = useCallback(
|
|
@@ -76,6 +93,7 @@ export const DraggableMenuButton = memoGeneric(function DraggableMenuButton<
|
|
|
76
93
|
|
|
77
94
|
return (
|
|
78
95
|
<div
|
|
96
|
+
{...rest}
|
|
79
97
|
className={cx(
|
|
80
98
|
"cursor-pointer rounded flex items-center justify-center py-[2px] hover:bg-input-background-light active:bg-active-background ",
|
|
81
99
|
className
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { useControlledOrUncontrolled } from "@noya-app/react-utils";
|
|
2
|
-
import
|
|
4
|
+
import { Dialog } from "radix-ui";
|
|
3
5
|
import * as React from "react";
|
|
4
6
|
import { useImperativeHandle } from "react";
|
|
5
7
|
import { cx } from "../utils/classNames";
|
|
@@ -14,6 +16,8 @@ export type DrawerProps = {
|
|
|
14
16
|
className?: string;
|
|
15
17
|
id?: string;
|
|
16
18
|
style?: React.CSSProperties;
|
|
19
|
+
overlayClassName?: string;
|
|
20
|
+
overlayStyle?: React.CSSProperties;
|
|
17
21
|
positioning?: "fixed" | "absolute";
|
|
18
22
|
side?: "left" | "right";
|
|
19
23
|
portalled?: boolean;
|
|
@@ -35,6 +39,8 @@ export const Drawer = React.memo(
|
|
|
35
39
|
side = "left",
|
|
36
40
|
portalled = true,
|
|
37
41
|
portalContainer,
|
|
42
|
+
overlayClassName,
|
|
43
|
+
overlayStyle,
|
|
38
44
|
}: DrawerProps,
|
|
39
45
|
forwardedRef: React.ForwardedRef<SidebarRef>
|
|
40
46
|
) {
|
|
@@ -61,14 +67,16 @@ export const Drawer = React.memo(
|
|
|
61
67
|
<Dialog.Overlay
|
|
62
68
|
className={cx(
|
|
63
69
|
"inset-0 bg-black/50",
|
|
64
|
-
positioning === "absolute" ? "absolute" : "fixed"
|
|
70
|
+
positioning === "absolute" ? "absolute" : "fixed",
|
|
71
|
+
overlayClassName
|
|
65
72
|
)}
|
|
73
|
+
style={overlayStyle}
|
|
66
74
|
/>
|
|
67
75
|
<Dialog.Content
|
|
68
76
|
id={id}
|
|
69
77
|
style={style}
|
|
70
78
|
className={cx(
|
|
71
|
-
`top-0 bottom-0 z-50 overflow-y-auto bg-sidebar-background rounded-none max-h-none max-w-none sm
|
|
79
|
+
`top-0 bottom-0 z-50 overflow-y-auto bg-sidebar-background rounded-none max-h-none max-w-none sm:!max-w-sm w-full border-divider-strong`,
|
|
72
80
|
positioning === "absolute" ? "absolute" : "fixed",
|
|
73
81
|
side === "left" ? "left-0 border-r" : "right-0 border-l",
|
|
74
82
|
className
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { useKeyboardShortcuts } from "@noya-app/noya-keymap";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
+
import {
|
|
5
|
+
forwardRefGeneric,
|
|
6
|
+
memoGeneric,
|
|
7
|
+
useControlledOrUncontrolled,
|
|
8
|
+
} from "@noya-app/react-utils";
|
|
9
|
+
import { DropdownMenu as RadixDropdownMenu } from "radix-ui";
|
|
4
10
|
import React, {
|
|
5
11
|
ComponentProps,
|
|
6
12
|
ForwardedRef,
|
|
13
|
+
ReactNode,
|
|
7
14
|
SyntheticEvent,
|
|
8
15
|
useMemo,
|
|
9
16
|
} from "react";
|
|
@@ -11,6 +18,7 @@ import {
|
|
|
11
18
|
portalScopeProps,
|
|
12
19
|
usePortalScopeId,
|
|
13
20
|
} from "../contexts/PortalScopeContext";
|
|
21
|
+
import Button, { ButtonProps } from "./Button";
|
|
14
22
|
import { MenuProps } from "./ContextMenu";
|
|
15
23
|
import { getKeyboardShortcutsForMenuItems, styles } from "./internal/Menu";
|
|
16
24
|
import { MenuViewport } from "./internal/MenuViewport";
|
|
@@ -19,7 +27,13 @@ import { MenuViewport } from "./internal/MenuViewport";
|
|
|
19
27
|
* Root
|
|
20
28
|
* ------------------------------------------------------------------------- */
|
|
21
29
|
|
|
22
|
-
export type DropdownRootProps<T extends string> =
|
|
30
|
+
export type DropdownRootProps<T extends string> = Omit<
|
|
31
|
+
MenuProps<T>,
|
|
32
|
+
"children"
|
|
33
|
+
> & {
|
|
34
|
+
children?: ReactNode | ((props: { open: boolean }) => ReactNode);
|
|
35
|
+
title?: ReactNode;
|
|
36
|
+
icon?: ButtonProps["icon"];
|
|
23
37
|
open?: boolean;
|
|
24
38
|
onCloseAutoFocus?: React.EventHandler<SyntheticEvent<unknown>>;
|
|
25
39
|
emptyState?: React.ReactNode;
|
|
@@ -50,7 +64,7 @@ const DropdownMenuRoot = forwardRefGeneric(function DropdownMenuRoot<
|
|
|
50
64
|
onSelect,
|
|
51
65
|
shouldBindKeyboardShortcuts,
|
|
52
66
|
onOpenChange,
|
|
53
|
-
open,
|
|
67
|
+
open: openProp,
|
|
54
68
|
onCloseAutoFocus,
|
|
55
69
|
side,
|
|
56
70
|
sideOffset = 4,
|
|
@@ -59,8 +73,16 @@ const DropdownMenuRoot = forwardRefGeneric(function DropdownMenuRoot<
|
|
|
59
73
|
collisionPadding = 8,
|
|
60
74
|
emptyState,
|
|
61
75
|
contentStyle,
|
|
76
|
+
title,
|
|
77
|
+
icon,
|
|
62
78
|
} = props;
|
|
63
79
|
|
|
80
|
+
const [open, setOpen] = useControlledOrUncontrolled({
|
|
81
|
+
value: openProp,
|
|
82
|
+
onChange: onOpenChange,
|
|
83
|
+
defaultValue: false,
|
|
84
|
+
});
|
|
85
|
+
|
|
64
86
|
const portalScopeId = usePortalScopeId();
|
|
65
87
|
|
|
66
88
|
const keymap = useMemo(
|
|
@@ -83,12 +105,18 @@ const DropdownMenuRoot = forwardRefGeneric(function DropdownMenuRoot<
|
|
|
83
105
|
);
|
|
84
106
|
|
|
85
107
|
return (
|
|
86
|
-
<RadixDropdownMenu.Root onOpenChange={
|
|
108
|
+
<RadixDropdownMenu.Root onOpenChange={setOpen} open={open}>
|
|
87
109
|
<RadixDropdownMenu.Trigger
|
|
88
110
|
ref={forwardedRef as ForwardedRef<any>}
|
|
89
111
|
asChild
|
|
90
112
|
>
|
|
91
|
-
{children
|
|
113
|
+
{typeof children === "function"
|
|
114
|
+
? children({ open })
|
|
115
|
+
: children || (
|
|
116
|
+
<Button icon={icon} active={open} iconRight="DropdownChevronIcon">
|
|
117
|
+
{title}
|
|
118
|
+
</Button>
|
|
119
|
+
)}
|
|
92
120
|
</RadixDropdownMenu.Trigger>
|
|
93
121
|
<RadixDropdownMenu.Portal>
|
|
94
122
|
<RadixDropdownMenu.Content
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { formatByteSize } from "../utils/formatByteSize";
|
|
3
|
+
import { ActivityIndicator } from "./ActivityIndicator";
|
|
4
|
+
import { Divider } from "./Divider";
|
|
5
|
+
import { MediaThumbnail } from "./MediaThumbnail";
|
|
6
|
+
import { Progress } from "./Progress";
|
|
7
|
+
import { Heading5, Small } from "./Text";
|
|
8
|
+
import { Checkmark } from "./internal/Checkmark";
|
|
9
|
+
|
|
10
|
+
type FileUploadIndicatorProps = {
|
|
11
|
+
name?: string;
|
|
12
|
+
percent?: number;
|
|
13
|
+
total?: number;
|
|
14
|
+
contentType?: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function FileUploadIndicator({
|
|
18
|
+
name,
|
|
19
|
+
percent,
|
|
20
|
+
total,
|
|
21
|
+
contentType,
|
|
22
|
+
}: FileUploadIndicatorProps) {
|
|
23
|
+
const done = percent === 1;
|
|
24
|
+
const value = typeof percent === "number" ? Math.round(percent) : 0;
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className="flex items-center gap-2">
|
|
28
|
+
<div className="w-12 h-12 bg-background rounded-md overflow-hidden">
|
|
29
|
+
<MediaThumbnail
|
|
30
|
+
size="medium"
|
|
31
|
+
fileName={name}
|
|
32
|
+
contentType={contentType}
|
|
33
|
+
/>
|
|
34
|
+
</div>
|
|
35
|
+
<div className="flex flex-col flex-1 justify-center gap-1 min-w-0">
|
|
36
|
+
{name && (
|
|
37
|
+
<Small color="text" className="truncate leading-none">
|
|
38
|
+
{name}
|
|
39
|
+
</Small>
|
|
40
|
+
)}
|
|
41
|
+
{typeof total === "number" && typeof percent === "number" && (
|
|
42
|
+
<Small
|
|
43
|
+
color="textSubtle"
|
|
44
|
+
className="leading-none"
|
|
45
|
+
style={{ fontSize: "11px" }}
|
|
46
|
+
>
|
|
47
|
+
{formatByteSize(Math.round((percent / 100) * total))} /{" "}
|
|
48
|
+
{formatByteSize(total)}
|
|
49
|
+
</Small>
|
|
50
|
+
)}
|
|
51
|
+
{typeof value === "number" && (
|
|
52
|
+
<Progress
|
|
53
|
+
value={value}
|
|
54
|
+
style={{ marginTop: "3px", height: "3px" }}
|
|
55
|
+
className="rounded-sm"
|
|
56
|
+
colorScheme="secondary"
|
|
57
|
+
/>
|
|
58
|
+
)}
|
|
59
|
+
</div>
|
|
60
|
+
<div className="flex items-center justify-center gap-2">
|
|
61
|
+
{typeof value === "number" && (
|
|
62
|
+
<Small
|
|
63
|
+
color="textSubtle"
|
|
64
|
+
style={{ lineHeight: "1", fontSize: "13px" }}
|
|
65
|
+
className="tabular-nums text-right w-10"
|
|
66
|
+
>
|
|
67
|
+
{value}%
|
|
68
|
+
</Small>
|
|
69
|
+
)}
|
|
70
|
+
{done ? (
|
|
71
|
+
<div className="w-[15px] h-[15px] bg-secondary rounded-full flex items-center justify-center text-white">
|
|
72
|
+
<Checkmark />
|
|
73
|
+
</div>
|
|
74
|
+
) : (
|
|
75
|
+
<ActivityIndicator size={15} />
|
|
76
|
+
)}
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function FileUploadList({
|
|
83
|
+
uploads,
|
|
84
|
+
}: {
|
|
85
|
+
uploads: (FileUploadIndicatorProps & { id: string })[];
|
|
86
|
+
}) {
|
|
87
|
+
return (
|
|
88
|
+
<>
|
|
89
|
+
<div className="flex items-center gap-2 py-2 px-4">
|
|
90
|
+
<Heading5>
|
|
91
|
+
Uploading {uploads.length} {uploads.length === 1 ? "asset" : "assets"}
|
|
92
|
+
</Heading5>
|
|
93
|
+
</div>
|
|
94
|
+
<Divider />
|
|
95
|
+
<div className="flex flex-col gap-3 max-h-[500px] overflow-y-auto overflow-x-hidden py-4 px-4">
|
|
96
|
+
{uploads.map((upload) => (
|
|
97
|
+
<FileUploadIndicator
|
|
98
|
+
key={upload.id}
|
|
99
|
+
name={upload?.name}
|
|
100
|
+
percent={upload?.percent}
|
|
101
|
+
total={upload?.total}
|
|
102
|
+
contentType={upload?.contentType}
|
|
103
|
+
/>
|
|
104
|
+
))}
|
|
105
|
+
</div>
|
|
106
|
+
</>
|
|
107
|
+
);
|
|
108
|
+
}
|