@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
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { cx } from "../utils/classNames";
|
|
3
|
+
import { Avatar, AvatarStack } from "./Avatar";
|
|
4
|
+
import { Button } from "./Button";
|
|
5
|
+
import { Divider } from "./Divider";
|
|
6
|
+
import { DropdownMenu } from "./DropdownMenu";
|
|
7
|
+
import { IconProp, renderIcon } from "./Icons";
|
|
8
|
+
import { ExtraSmall, Small } from "./Text";
|
|
9
|
+
import { MenuItem } from "./internal/Menu";
|
|
10
|
+
|
|
11
|
+
export type ActivityIconSemanticColor = "add" | "remove" | "update";
|
|
12
|
+
|
|
13
|
+
const IconContainer = ({
|
|
14
|
+
children,
|
|
15
|
+
semanticColor,
|
|
16
|
+
}: {
|
|
17
|
+
children: React.ReactNode;
|
|
18
|
+
semanticColor?: ActivityIconSemanticColor;
|
|
19
|
+
}) => {
|
|
20
|
+
return (
|
|
21
|
+
<div
|
|
22
|
+
className={cx(
|
|
23
|
+
"n-w-[23px] n-h-[23px] n-rounded-full n-flex n-items-center n-justify-center n-flex-none",
|
|
24
|
+
semanticColor === "add" && "n-bg-secondary-pastel n-text-secondary",
|
|
25
|
+
semanticColor === "remove" &&
|
|
26
|
+
"n-bg-banner-error-background n-text-banner-error-text",
|
|
27
|
+
semanticColor === "update" && "n-bg-[#d1ecfe] n-text-[#1984cc]",
|
|
28
|
+
semanticColor === undefined && "n-bg-primary-pastel n-text-primary"
|
|
29
|
+
)}
|
|
30
|
+
>
|
|
31
|
+
{children}
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const ActivityLine = () => {
|
|
37
|
+
return (
|
|
38
|
+
<div className="n-w-[3px] n-flex-1 n-relative">
|
|
39
|
+
<div className="n-rounded-full n-bg-primary-150 n-absolute n-w-full n-top-1 -n-bottom-3" />
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const ActivityLeft = ({ children }: { children: React.ReactNode }) => {
|
|
45
|
+
return (
|
|
46
|
+
<div className="n-flex n-flex-none n-relative n-isolate n-flex-col n-items-center">
|
|
47
|
+
{children}
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const ActivityRight = ({
|
|
53
|
+
children,
|
|
54
|
+
className,
|
|
55
|
+
}: {
|
|
56
|
+
children: React.ReactNode;
|
|
57
|
+
className?: string;
|
|
58
|
+
}) => {
|
|
59
|
+
return (
|
|
60
|
+
<div
|
|
61
|
+
className={cx(
|
|
62
|
+
"n-flex-1 n-rounded n-flex n-p-2 n-gap-2 n-bg-canvas-background n-border n-border-divider-subtle",
|
|
63
|
+
className
|
|
64
|
+
)}
|
|
65
|
+
>
|
|
66
|
+
{children}
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const ActivityCard = ({ children }: { children: React.ReactNode }) => {
|
|
72
|
+
return <div className="n-flex n-gap-2">{children}</div>;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const ActivityEventIndicator = () => {
|
|
76
|
+
return (
|
|
77
|
+
<div className="n-w-1 n-bg-primary-300 n-rounded-full n-mx-0.5 n-flex-none" />
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
type ActivityUser = {
|
|
82
|
+
userId?: string;
|
|
83
|
+
name?: string;
|
|
84
|
+
image?: string;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export type ActivityLogItem<M extends string = string> = {
|
|
88
|
+
title: React.ReactNode;
|
|
89
|
+
description: React.ReactNode;
|
|
90
|
+
time?: React.ReactNode;
|
|
91
|
+
icon: IconProp;
|
|
92
|
+
iconSemanticColor?: ActivityIconSemanticColor;
|
|
93
|
+
menuItems?: MenuItem<M>[];
|
|
94
|
+
onSelectMenuItem?: (value: M) => void;
|
|
95
|
+
} & ({ type: "change"; users?: ActivityUser[] } | { type: "event" });
|
|
96
|
+
|
|
97
|
+
type ActivityLogProps<M extends string> = {
|
|
98
|
+
style?: React.CSSProperties;
|
|
99
|
+
className?: string;
|
|
100
|
+
items?: ActivityLogItem<M>[];
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export function ActivityLog<M extends string = string>({
|
|
104
|
+
style,
|
|
105
|
+
className,
|
|
106
|
+
items,
|
|
107
|
+
}: ActivityLogProps<M>) {
|
|
108
|
+
return (
|
|
109
|
+
<div
|
|
110
|
+
className={cx(
|
|
111
|
+
"n-flex n-flex-col n-relative n-gap-4 n-isolate",
|
|
112
|
+
className
|
|
113
|
+
)}
|
|
114
|
+
style={style}
|
|
115
|
+
>
|
|
116
|
+
{items?.map((item, index, list) => (
|
|
117
|
+
<ActivityCard key={index}>
|
|
118
|
+
<ActivityLeft>
|
|
119
|
+
<IconContainer semanticColor={item.iconSemanticColor}>
|
|
120
|
+
{renderIcon(item.icon)}
|
|
121
|
+
</IconContainer>
|
|
122
|
+
{index !== list.length - 1 && <ActivityLine />}
|
|
123
|
+
</ActivityLeft>
|
|
124
|
+
<ActivityRight>
|
|
125
|
+
{item.type === "event" && (
|
|
126
|
+
<>
|
|
127
|
+
<ActivityEventIndicator />
|
|
128
|
+
<div className="n-flex n-flex-col n-justify-center n-flex-1 n-gap-0.5">
|
|
129
|
+
<Small className="n-leading-snug">{item.title}</Small>
|
|
130
|
+
{item.description && (
|
|
131
|
+
<ExtraSmall
|
|
132
|
+
className="n-leading-snug n-break-all"
|
|
133
|
+
color="textSubtle"
|
|
134
|
+
>
|
|
135
|
+
{item.description}
|
|
136
|
+
</ExtraSmall>
|
|
137
|
+
)}
|
|
138
|
+
</div>
|
|
139
|
+
{item.menuItems && item.onSelectMenuItem && (
|
|
140
|
+
<DropdownMenu
|
|
141
|
+
items={item.menuItems}
|
|
142
|
+
onSelect={item.onSelectMenuItem}
|
|
143
|
+
>
|
|
144
|
+
{({ open }) => (
|
|
145
|
+
<Button
|
|
146
|
+
className="n-self-start"
|
|
147
|
+
icon="DotsVerticalIcon"
|
|
148
|
+
variant="ghost"
|
|
149
|
+
active={open}
|
|
150
|
+
/>
|
|
151
|
+
)}
|
|
152
|
+
</DropdownMenu>
|
|
153
|
+
)}
|
|
154
|
+
</>
|
|
155
|
+
)}
|
|
156
|
+
{item.type === "change" && (
|
|
157
|
+
<div className="n-flex n-flex-col n-flex-1">
|
|
158
|
+
<div className="n-flex n-items-start n-flex-1 n-gap-1.5">
|
|
159
|
+
{item.users && (
|
|
160
|
+
<AvatarStack
|
|
161
|
+
size={24}
|
|
162
|
+
overlapAmount={12}
|
|
163
|
+
className="n-flex-none n-self-start"
|
|
164
|
+
>
|
|
165
|
+
{item.users.map((user) => (
|
|
166
|
+
<Avatar
|
|
167
|
+
key={user.userId}
|
|
168
|
+
variant="borderless"
|
|
169
|
+
image={user.image}
|
|
170
|
+
name={user.name}
|
|
171
|
+
userId={user.userId}
|
|
172
|
+
/>
|
|
173
|
+
))}
|
|
174
|
+
</AvatarStack>
|
|
175
|
+
)}
|
|
176
|
+
<Small className="n-py-[3px] n-flex-1">{item.title}</Small>
|
|
177
|
+
<ExtraSmall
|
|
178
|
+
color="text"
|
|
179
|
+
className="n-flex n-items-center n-justify-center n-opacity-50 n-pt-[5px] n-whitespace-pre"
|
|
180
|
+
>
|
|
181
|
+
{item.time}
|
|
182
|
+
</ExtraSmall>
|
|
183
|
+
</div>
|
|
184
|
+
{item.description && (
|
|
185
|
+
<>
|
|
186
|
+
<Divider overflow={8} className="n-my-1.5" />
|
|
187
|
+
<Small>{item.description}</Small>
|
|
188
|
+
</>
|
|
189
|
+
)}
|
|
190
|
+
</div>
|
|
191
|
+
)}
|
|
192
|
+
</ActivityRight>
|
|
193
|
+
</ActivityCard>
|
|
194
|
+
))}
|
|
195
|
+
</div>
|
|
196
|
+
);
|
|
197
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { isDeepEqual } from "@noya-app/noya-utils";
|
|
2
4
|
import React, {
|
|
3
5
|
Children,
|
|
@@ -9,7 +11,13 @@ import React, {
|
|
|
9
11
|
useState,
|
|
10
12
|
} from "react";
|
|
11
13
|
|
|
12
|
-
type
|
|
14
|
+
export type AnimatePresenceChildState =
|
|
15
|
+
| "entering"
|
|
16
|
+
| "entered"
|
|
17
|
+
| "exiting"
|
|
18
|
+
| "exited";
|
|
19
|
+
|
|
20
|
+
export type AnimatePresenceProps = {
|
|
13
21
|
children: React.ReactNode;
|
|
14
22
|
/**
|
|
15
23
|
* Duration of enter/exit animations in milliseconds
|
|
@@ -33,17 +41,16 @@ type AnimatePresenceProps = {
|
|
|
33
41
|
*/
|
|
34
42
|
mode?: "wait" | "crossfade";
|
|
35
43
|
className?: string;
|
|
44
|
+
onChildStateChange?: (key: string, state: AnimatePresenceChildState) => void;
|
|
36
45
|
};
|
|
37
46
|
|
|
38
|
-
const defaultDuration = 150;
|
|
39
|
-
|
|
40
|
-
type AnimationState = "entering" | "entered" | "exiting" | "exited";
|
|
41
|
-
|
|
42
47
|
type PresenceChildProps = {
|
|
43
48
|
isPresent: boolean;
|
|
44
49
|
onExitComplete: () => void;
|
|
45
50
|
waitingToEnter?: boolean;
|
|
46
51
|
className?: string;
|
|
52
|
+
childKey: string;
|
|
53
|
+
onStateChange?: (key: string, state: AnimatePresenceChildState) => void;
|
|
47
54
|
} & Pick<
|
|
48
55
|
AnimatePresenceProps,
|
|
49
56
|
"animationStyles" | "children" | "duration" | "skipInitialAnimation"
|
|
@@ -54,15 +61,15 @@ const defaultAnimationStyles: Required<
|
|
|
54
61
|
> = {
|
|
55
62
|
entering: {
|
|
56
63
|
opacity: 0,
|
|
57
|
-
transition: `opacity
|
|
64
|
+
transition: `opacity 200ms ease-in`,
|
|
58
65
|
},
|
|
59
66
|
entered: {
|
|
60
67
|
opacity: 1,
|
|
61
|
-
transition: `opacity
|
|
68
|
+
transition: `opacity 200ms ease-in`,
|
|
62
69
|
},
|
|
63
70
|
exiting: {
|
|
64
71
|
opacity: 0,
|
|
65
|
-
transition: `opacity
|
|
72
|
+
transition: `opacity 200ms ease-out`,
|
|
66
73
|
},
|
|
67
74
|
};
|
|
68
75
|
|
|
@@ -74,24 +81,28 @@ const PresenceChild = ({
|
|
|
74
81
|
onExitComplete,
|
|
75
82
|
skipInitialAnimation,
|
|
76
83
|
className,
|
|
84
|
+
childKey,
|
|
85
|
+
onStateChange,
|
|
77
86
|
}: PresenceChildProps) => {
|
|
78
|
-
const [animationState, setAnimationState] =
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
const [animationState, setAnimationState] =
|
|
88
|
+
useState<AnimatePresenceChildState>(() =>
|
|
89
|
+
skipInitialAnimation && isPresent
|
|
90
|
+
? "entered"
|
|
91
|
+
: isPresent
|
|
92
|
+
? "entering"
|
|
93
|
+
: "exited"
|
|
94
|
+
);
|
|
95
|
+
const timeoutRef = useRef<number>(undefined);
|
|
86
96
|
const isInitialMount = useRef(true);
|
|
87
97
|
|
|
88
|
-
const mergedAnimationStyles
|
|
98
|
+
const mergedAnimationStyles: Record<
|
|
99
|
+
AnimatePresenceChildState,
|
|
100
|
+
React.CSSProperties
|
|
101
|
+
> = {
|
|
89
102
|
...defaultAnimationStyles,
|
|
90
103
|
...animationStyles,
|
|
91
104
|
exited: {},
|
|
92
|
-
|
|
93
|
-
backfaceVisibility: "hidden",
|
|
94
|
-
} as Record<AnimationState, React.CSSProperties>;
|
|
105
|
+
};
|
|
95
106
|
|
|
96
107
|
useLayoutEffect(() => {
|
|
97
108
|
let rafId: number;
|
|
@@ -130,6 +141,11 @@ const PresenceChild = ({
|
|
|
130
141
|
skipInitialAnimation,
|
|
131
142
|
]);
|
|
132
143
|
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
if (!childKey) return;
|
|
146
|
+
onStateChange?.(childKey, animationState);
|
|
147
|
+
}, [animationState, childKey, onStateChange]);
|
|
148
|
+
|
|
133
149
|
if (animationState === "exited" && !isPresent) {
|
|
134
150
|
return null;
|
|
135
151
|
}
|
|
@@ -142,28 +158,29 @@ const PresenceChild = ({
|
|
|
142
158
|
};
|
|
143
159
|
|
|
144
160
|
type MountedChild = {
|
|
145
|
-
child: React.ReactElement
|
|
161
|
+
child: React.ReactElement<any>;
|
|
146
162
|
isPresent: boolean;
|
|
147
163
|
skipInitialAnimation?: boolean;
|
|
148
164
|
};
|
|
149
165
|
|
|
150
166
|
export const AnimatePresence = ({
|
|
151
167
|
children,
|
|
152
|
-
duration =
|
|
168
|
+
duration = 200,
|
|
153
169
|
animationStyles,
|
|
154
170
|
skipInitialAnimation,
|
|
155
171
|
mode = "wait",
|
|
156
172
|
className,
|
|
173
|
+
onChildStateChange,
|
|
157
174
|
}: AnimatePresenceProps) => {
|
|
158
175
|
const [mountedChildren, setMountedChildren] = useState<MountedChild[]>([]);
|
|
159
|
-
const previousChildrenRef = useRef<React.ReactElement[]>([]);
|
|
176
|
+
const previousChildrenRef = useRef<React.ReactElement<any>[]>([]);
|
|
160
177
|
const removingChildrenRef = useRef<Set<string>>(new Set());
|
|
161
178
|
const waitingToEnterRef = useRef<MountedChild[]>([]);
|
|
162
179
|
|
|
163
180
|
useEffect(() => {
|
|
164
181
|
const newChildren = Children.toArray(children).filter(
|
|
165
182
|
isValidElement
|
|
166
|
-
) as React.ReactElement[];
|
|
183
|
+
) as React.ReactElement<any>[];
|
|
167
184
|
|
|
168
185
|
setMountedChildren((prev) => {
|
|
169
186
|
if (!prev.length && skipInitialAnimation) {
|
|
@@ -253,12 +270,14 @@ export const AnimatePresence = ({
|
|
|
253
270
|
{mountedChildren.map(({ child, isPresent, skipInitialAnimation }) => (
|
|
254
271
|
<PresenceChild
|
|
255
272
|
key={child.key}
|
|
273
|
+
childKey={child.key?.toString() ?? ""}
|
|
256
274
|
duration={duration}
|
|
257
275
|
animationStyles={animationStyles}
|
|
258
276
|
isPresent={isPresent}
|
|
259
277
|
skipInitialAnimation={skipInitialAnimation}
|
|
260
278
|
onExitComplete={() => removeChild(child.key?.toString() ?? "")}
|
|
261
279
|
className={className}
|
|
280
|
+
onStateChange={onChildStateChange}
|
|
262
281
|
>
|
|
263
282
|
{child}
|
|
264
283
|
</PresenceChild>
|
|
@@ -266,5 +285,3 @@ export const AnimatePresence = ({
|
|
|
266
285
|
</>
|
|
267
286
|
);
|
|
268
287
|
};
|
|
269
|
-
|
|
270
|
-
export type { AnimatePresenceProps };
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Avatar as AvatarPrimitive } from "radix-ui";
|
|
2
4
|
import React, { forwardRef, useMemo } from "react";
|
|
3
5
|
import { cx } from "../utils/classNames";
|
|
4
6
|
import { colorFromString } from "../utils/colorFromString";
|
|
5
7
|
|
|
6
8
|
const getInnerAvatarStyles = (
|
|
7
9
|
size: number,
|
|
8
|
-
variant: "normal" | "bare"
|
|
10
|
+
variant: "normal" | "bare" | "borderless"
|
|
9
11
|
): React.CSSProperties => {
|
|
10
|
-
const borderSize =
|
|
12
|
+
const borderSize =
|
|
13
|
+
variant === "bare" ? 4 : variant === "borderless" ? 0 : size > 36 ? 8 : 6;
|
|
11
14
|
const dimension = `${size - borderSize}px`;
|
|
12
15
|
return {
|
|
13
16
|
width: dimension,
|
|
@@ -18,7 +21,7 @@ const getInnerAvatarStyles = (
|
|
|
18
21
|
};
|
|
19
22
|
|
|
20
23
|
const AvatarRoot = React.forwardRef<
|
|
21
|
-
React.
|
|
24
|
+
React.ComponentRef<typeof AvatarPrimitive.Root>,
|
|
22
25
|
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> &
|
|
23
26
|
Pick<AvatarProps, "size" | "variant" | "backgroundColor" | "disabled">
|
|
24
27
|
>(
|
|
@@ -49,8 +52,8 @@ const AvatarRoot = React.forwardRef<
|
|
|
49
52
|
ref={ref}
|
|
50
53
|
style={dynamicStyles}
|
|
51
54
|
className={cx(
|
|
52
|
-
"flex items-center justify-center align-middle overflow-hidden select-none cursor-pointer rounded-full",
|
|
53
|
-
variant !== "bare" && "border border-background",
|
|
55
|
+
"n-flex n-items-center n-justify-center n-align-middle n-overflow-hidden n-select-none n-cursor-pointer n-rounded-full",
|
|
56
|
+
variant !== "bare" && "n-border n-border-background",
|
|
54
57
|
className
|
|
55
58
|
)}
|
|
56
59
|
{...props}
|
|
@@ -60,7 +63,7 @@ const AvatarRoot = React.forwardRef<
|
|
|
60
63
|
);
|
|
61
64
|
|
|
62
65
|
const AvatarImage = React.forwardRef<
|
|
63
|
-
React.
|
|
66
|
+
React.ComponentRef<typeof AvatarPrimitive.Image>,
|
|
64
67
|
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image> &
|
|
65
68
|
Pick<AvatarProps, "size" | "variant">
|
|
66
69
|
>(({ className, size = 21, variant = "normal", ...props }, ref) => {
|
|
@@ -68,7 +71,7 @@ const AvatarImage = React.forwardRef<
|
|
|
68
71
|
<AvatarPrimitive.Image
|
|
69
72
|
ref={ref}
|
|
70
73
|
className={cx(
|
|
71
|
-
"object-cover overflow-hidden rounded-full w-full h-full",
|
|
74
|
+
"n-object-cover n-overflow-hidden n-rounded-full n-w-full n-h-full",
|
|
72
75
|
className
|
|
73
76
|
)}
|
|
74
77
|
style={getInnerAvatarStyles(size, variant)}
|
|
@@ -82,7 +85,7 @@ function getMonogram(text?: string) {
|
|
|
82
85
|
}
|
|
83
86
|
|
|
84
87
|
const AvatarFallback = React.forwardRef<
|
|
85
|
-
React.
|
|
88
|
+
React.ComponentRef<typeof AvatarPrimitive.Fallback>,
|
|
86
89
|
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback> &
|
|
87
90
|
Pick<AvatarProps, "size" | "variant" | "backgroundColor">
|
|
88
91
|
>(
|
|
@@ -100,7 +103,7 @@ const AvatarFallback = React.forwardRef<
|
|
|
100
103
|
...style,
|
|
101
104
|
}}
|
|
102
105
|
className={cx(
|
|
103
|
-
"font-medium flex items-center justify-center text-white",
|
|
106
|
+
"n-font-medium n-flex n-items-center n-justify-center n-text-white",
|
|
104
107
|
className
|
|
105
108
|
)}
|
|
106
109
|
{...props}
|
|
@@ -123,7 +126,7 @@ export type AvatarProps = {
|
|
|
123
126
|
/** colors are generated from the fallback string or title, but you can override it here */
|
|
124
127
|
backgroundColor?: string;
|
|
125
128
|
/** @default normal */
|
|
126
|
-
variant?: "normal" | "bare";
|
|
129
|
+
variant?: "normal" | "bare" | "borderless";
|
|
127
130
|
disabled?: boolean;
|
|
128
131
|
/** Intended to be used in place of a fallback monogram, like an icon */
|
|
129
132
|
children?: React.ReactNode;
|
|
@@ -137,7 +140,7 @@ export const Avatar = React.memo(
|
|
|
137
140
|
name,
|
|
138
141
|
userId,
|
|
139
142
|
size = 21,
|
|
140
|
-
backgroundColor,
|
|
143
|
+
backgroundColor: backgroundColorProp,
|
|
141
144
|
variant = "normal",
|
|
142
145
|
children,
|
|
143
146
|
className,
|
|
@@ -145,14 +148,17 @@ export const Avatar = React.memo(
|
|
|
145
148
|
}: AvatarProps,
|
|
146
149
|
forwardedRef: React.ForwardedRef<HTMLDivElement>
|
|
147
150
|
) {
|
|
148
|
-
const
|
|
149
|
-
|
|
151
|
+
const backgroundColor =
|
|
152
|
+
backgroundColorProp ??
|
|
153
|
+
(variant === "borderless" && image
|
|
154
|
+
? undefined
|
|
155
|
+
: colorFromString(userId ?? name ?? ""));
|
|
150
156
|
|
|
151
157
|
return (
|
|
152
158
|
<AvatarRoot
|
|
153
159
|
ref={forwardedRef}
|
|
154
160
|
size={size}
|
|
155
|
-
backgroundColor={
|
|
161
|
+
backgroundColor={backgroundColor}
|
|
156
162
|
variant={variant}
|
|
157
163
|
className={className}
|
|
158
164
|
{...props}
|
|
@@ -161,7 +167,7 @@ export const Avatar = React.memo(
|
|
|
161
167
|
<AvatarImage src={image} alt={name} size={size} variant={variant} />
|
|
162
168
|
) : (
|
|
163
169
|
<AvatarFallback
|
|
164
|
-
backgroundColor={
|
|
170
|
+
backgroundColor={backgroundColor}
|
|
165
171
|
size={size}
|
|
166
172
|
variant={variant}
|
|
167
173
|
>
|
|
@@ -194,12 +200,14 @@ export const AvatarStack = React.memo(function AvatarStack({
|
|
|
194
200
|
children,
|
|
195
201
|
max = 3,
|
|
196
202
|
className,
|
|
203
|
+
overlapAmount = size > 36 ? 12 : 6,
|
|
197
204
|
}: {
|
|
198
205
|
size?: number;
|
|
199
206
|
/** @default 3 */
|
|
200
207
|
max?: number;
|
|
201
208
|
children: React.ReactNode;
|
|
202
209
|
className?: string;
|
|
210
|
+
overlapAmount?: number;
|
|
203
211
|
}) {
|
|
204
212
|
const avatarCount = React.Children.count(children);
|
|
205
213
|
// can add logic later to shorten values over 2 digits, etc.
|
|
@@ -207,7 +215,10 @@ export const AvatarStack = React.memo(function AvatarStack({
|
|
|
207
215
|
|
|
208
216
|
const childrenArray = useMemo(() => {
|
|
209
217
|
const processedChildren = React.Children.toArray(children)
|
|
210
|
-
.map((child) =>
|
|
218
|
+
.map((child) =>
|
|
219
|
+
React.cloneElement(child as React.ReactElement<any>, { size })
|
|
220
|
+
)
|
|
221
|
+
.reverse()
|
|
211
222
|
.slice(0, max);
|
|
212
223
|
|
|
213
224
|
if (avatarCount > max) {
|
|
@@ -220,20 +231,28 @@ export const AvatarStack = React.memo(function AvatarStack({
|
|
|
220
231
|
return processedChildren;
|
|
221
232
|
}, [children, max, size, avatarCount, overflowCount]);
|
|
222
233
|
|
|
234
|
+
const containerStyle = useMemo(
|
|
235
|
+
() => ({
|
|
236
|
+
paddingLeft: overlapAmount,
|
|
237
|
+
}),
|
|
238
|
+
[overlapAmount]
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
const itemStyle = useMemo(
|
|
242
|
+
() => ({
|
|
243
|
+
marginLeft: -overlapAmount,
|
|
244
|
+
}),
|
|
245
|
+
[overlapAmount]
|
|
246
|
+
);
|
|
247
|
+
|
|
223
248
|
return (
|
|
224
249
|
<div
|
|
225
|
-
className={cx(
|
|
226
|
-
|
|
227
|
-
size > 36 ? "pl-3" : "pl-1.5",
|
|
228
|
-
className
|
|
229
|
-
)}
|
|
250
|
+
className={cx("n-flex n-flex-row-reverse", className)}
|
|
251
|
+
style={containerStyle}
|
|
230
252
|
>
|
|
231
253
|
{childrenArray.map((child, index) => {
|
|
232
254
|
return (
|
|
233
|
-
<div
|
|
234
|
-
className={cx("relative", size > 36 ? "-ml-3" : "-ml-1.5")}
|
|
235
|
-
key={index}
|
|
236
|
-
>
|
|
255
|
+
<div key={index} className="n-relative" style={itemStyle}>
|
|
237
256
|
{child}
|
|
238
257
|
</div>
|
|
239
258
|
);
|
|
@@ -1,29 +1,75 @@
|
|
|
1
|
+
import { ExclamationTriangleIcon, InfoCircledIcon } from "@noya-app/noya-icons";
|
|
1
2
|
import React, { CSSProperties, ReactNode } from "react";
|
|
2
|
-
import {
|
|
3
|
-
import { cx } from "../utils/classNames";
|
|
3
|
+
import { cx, mergeConflictingClassNames } from "../utils/classNames";
|
|
4
4
|
import { Small } from "./Text";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
backgroundColor: cssVars.colors.inputBackground,
|
|
8
|
-
color: cssVars.colors.textDisabled,
|
|
9
|
-
};
|
|
6
|
+
type BannerVariant = "muted" | "info" | "error";
|
|
10
7
|
|
|
11
8
|
export type BannerProps = {
|
|
12
|
-
|
|
9
|
+
children?: ReactNode;
|
|
13
10
|
className?: string;
|
|
14
11
|
style?: CSSProperties;
|
|
12
|
+
variant?: BannerVariant;
|
|
13
|
+
labelStyle?: CSSProperties;
|
|
14
|
+
labelClassName?: string;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
export function Banner({
|
|
17
|
+
export function Banner({
|
|
18
|
+
children: label = "No items",
|
|
19
|
+
className,
|
|
20
|
+
style,
|
|
21
|
+
variant = "muted",
|
|
22
|
+
labelStyle,
|
|
23
|
+
labelClassName: labelClassNameProp,
|
|
24
|
+
}: BannerProps) {
|
|
25
|
+
const labelClassName = mergeConflictingClassNames(
|
|
26
|
+
[
|
|
27
|
+
"n-flex-1 n-text-center",
|
|
28
|
+
variant === "muted"
|
|
29
|
+
? "!n-text-text-disabled"
|
|
30
|
+
: variant === "error"
|
|
31
|
+
? "!n-text-banner-error-text"
|
|
32
|
+
: "!n-text-banner-info-text",
|
|
33
|
+
labelClassNameProp,
|
|
34
|
+
],
|
|
35
|
+
{
|
|
36
|
+
categories: ["textAlign"],
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
|
|
18
40
|
return (
|
|
19
41
|
<div
|
|
20
42
|
className={cx(
|
|
21
|
-
"flex
|
|
43
|
+
"n-flex n-py-1 n-px-1.5 n-items-center n-justify-center n-select-none n-rounded n-gap-1.5 n-min-h-input-height",
|
|
44
|
+
variant === "muted" && "n-bg-input-background",
|
|
45
|
+
variant === "info" &&
|
|
46
|
+
"n-bg-banner-info-background n-border n-border-banner-info-border",
|
|
47
|
+
variant === "error" &&
|
|
48
|
+
"n-bg-banner-error-background n-border n-border-banner-error-border",
|
|
22
49
|
className
|
|
23
50
|
)}
|
|
24
|
-
style={
|
|
51
|
+
style={style}
|
|
25
52
|
>
|
|
26
|
-
|
|
53
|
+
{(variant === "info" || variant === "error") && (
|
|
54
|
+
<Small
|
|
55
|
+
className={cx(
|
|
56
|
+
"n-flex-none n-self-start n-flex n-items-center",
|
|
57
|
+
variant === "error"
|
|
58
|
+
? "!n-text-banner-error-text"
|
|
59
|
+
: "!n-text-banner-info-text"
|
|
60
|
+
)}
|
|
61
|
+
>
|
|
62
|
+
{`\u200B`}
|
|
63
|
+
{variant === "error" ? (
|
|
64
|
+
<ExclamationTriangleIcon className="n-text-heading5" />
|
|
65
|
+
) : (
|
|
66
|
+
<InfoCircledIcon className="n-text-heading5" />
|
|
67
|
+
)}
|
|
68
|
+
</Small>
|
|
69
|
+
)}
|
|
70
|
+
<Small className={labelClassName} style={labelStyle}>
|
|
71
|
+
{label}
|
|
72
|
+
</Small>
|
|
27
73
|
</div>
|
|
28
74
|
);
|
|
29
75
|
}
|