@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 { Size } from "@noya-app/noya-geometry";
|
|
2
4
|
import { range } from "@noya-app/noya-utils";
|
|
3
5
|
import { forwardRefGeneric, memoGeneric } from "@noya-app/react-utils";
|
|
@@ -192,10 +194,14 @@ const RowContainer = forwardRef<
|
|
|
192
194
|
$gap: number;
|
|
193
195
|
$backgroundColor?: CSSProperties["backgroundColor"];
|
|
194
196
|
$clickable?: boolean;
|
|
197
|
+
href?: string;
|
|
198
|
+
as?: React.ElementType;
|
|
195
199
|
}
|
|
196
200
|
>(
|
|
197
201
|
(
|
|
198
202
|
{
|
|
203
|
+
as: Component = "div",
|
|
204
|
+
href,
|
|
199
205
|
className,
|
|
200
206
|
$marginType,
|
|
201
207
|
$selected,
|
|
@@ -222,8 +228,9 @@ const RowContainer = forwardRef<
|
|
|
222
228
|
ref
|
|
223
229
|
) => {
|
|
224
230
|
return (
|
|
225
|
-
<
|
|
231
|
+
<Component
|
|
226
232
|
ref={ref}
|
|
233
|
+
href={href}
|
|
227
234
|
role={role}
|
|
228
235
|
aria-label={ariaLabel}
|
|
229
236
|
aria-roledescription={ariaRoleDescription}
|
|
@@ -363,7 +370,7 @@ const ListViewDragIndicatorElement = forwardRef<
|
|
|
363
370
|
backgroundColor:
|
|
364
371
|
$colorScheme === "secondary"
|
|
365
372
|
? cssVars.colors.secondary
|
|
366
|
-
: cssVars.colors.
|
|
373
|
+
: cssVars.colors.dragOutline,
|
|
367
374
|
border: `2px solid white`,
|
|
368
375
|
boxShadow: "0 0 2px rgba(0,0,0,0.5)",
|
|
369
376
|
}),
|
|
@@ -410,6 +417,8 @@ export interface ListViewRowProps<MenuItemType extends string = string> {
|
|
|
410
417
|
| CSSProperties
|
|
411
418
|
| ((props: DragIndicatorStyleProps) => CSSProperties);
|
|
412
419
|
testRelativeDropPosition?: RelativeDropPosition;
|
|
420
|
+
href?: string;
|
|
421
|
+
as?: React.ElementType;
|
|
413
422
|
}
|
|
414
423
|
|
|
415
424
|
export type DragIndicatorStyleProps = {
|
|
@@ -446,6 +455,8 @@ const ListViewRow = forwardRefGeneric(function ListViewRow<
|
|
|
446
455
|
dragIndicatorStyle,
|
|
447
456
|
className,
|
|
448
457
|
testRelativeDropPosition,
|
|
458
|
+
href,
|
|
459
|
+
as,
|
|
449
460
|
}: ListViewRowProps<MenuItemType>,
|
|
450
461
|
forwardedRef: ForwardedRef<HTMLElement>
|
|
451
462
|
) {
|
|
@@ -472,7 +483,7 @@ const ListViewRow = forwardRefGeneric(function ListViewRow<
|
|
|
472
483
|
// We use preventDefault as a hack to mark this event as handled. We check for
|
|
473
484
|
// this in the ListView.Root. We can't stopPropagation here or existing ContextMenus
|
|
474
485
|
// won't close (onPointerDownOutside won't fire).
|
|
475
|
-
event.preventDefault();
|
|
486
|
+
// event.preventDefault();
|
|
476
487
|
|
|
477
488
|
// We only want to handle left clicks
|
|
478
489
|
if (event.button !== 0) return;
|
|
@@ -517,6 +528,8 @@ const ListViewRow = forwardRefGeneric(function ListViewRow<
|
|
|
517
528
|
|
|
518
529
|
const element = (
|
|
519
530
|
<Component
|
|
531
|
+
as={as}
|
|
532
|
+
href={href}
|
|
520
533
|
ref={ref as any}
|
|
521
534
|
$colorScheme={colorScheme}
|
|
522
535
|
onContextMenu={onContextMenu}
|
|
@@ -787,7 +800,7 @@ export type ListViewRootProps = {
|
|
|
787
800
|
divider?: boolean;
|
|
788
801
|
gap?: number;
|
|
789
802
|
colorScheme?: ListColorScheme;
|
|
790
|
-
containerRef?: React.RefObject<HTMLDivElement>;
|
|
803
|
+
containerRef?: React.RefObject<HTMLDivElement | null>;
|
|
791
804
|
onDragOver?: React.DragEventHandler<HTMLDivElement>;
|
|
792
805
|
onDragEnter?: React.DragEventHandler<HTMLDivElement>;
|
|
793
806
|
onDragLeave?: React.DragEventHandler<HTMLDivElement>;
|
|
@@ -897,18 +910,18 @@ const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
|
897
910
|
const prevChild = i - 1 >= 0 && renderChild(i - 1);
|
|
898
911
|
const nextChild = i + 1 < data.length && renderChild(i + 1);
|
|
899
912
|
|
|
900
|
-
const next: ReactElement | undefined = isValidElement(nextChild)
|
|
913
|
+
const next: ReactElement<any> | undefined = isValidElement(nextChild)
|
|
901
914
|
? nextChild
|
|
902
915
|
: undefined;
|
|
903
|
-
const prev: ReactElement | undefined = isValidElement(prevChild)
|
|
916
|
+
const prev: ReactElement<any> | undefined = isValidElement(prevChild)
|
|
904
917
|
? prevChild
|
|
905
918
|
: undefined;
|
|
906
919
|
|
|
907
920
|
const hasMarginTop = !prev;
|
|
908
921
|
const hasMarginBottom =
|
|
909
922
|
!next ||
|
|
910
|
-
current.props.isSectionHeader ||
|
|
911
|
-
(next && next.props.isSectionHeader);
|
|
923
|
+
(current as any).props.isSectionHeader ||
|
|
924
|
+
(next && (next as any).props.isSectionHeader);
|
|
912
925
|
|
|
913
926
|
let marginType: ListRowMarginType;
|
|
914
927
|
|
|
@@ -924,11 +937,11 @@ const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
|
924
937
|
|
|
925
938
|
let selectedPosition: ListRowPosition = "only";
|
|
926
939
|
|
|
927
|
-
if (current.props.selected) {
|
|
940
|
+
if ((current as any).props.selected) {
|
|
928
941
|
const nextSelected =
|
|
929
|
-
next && !next.props.isSectionHeader && next.props.selected;
|
|
942
|
+
next && !next.props.isSectionHeader && (next as any).props.selected;
|
|
930
943
|
const prevSelected =
|
|
931
|
-
prev && !prev.props.isSectionHeader && prev.props.selected;
|
|
944
|
+
prev && !prev.props.isSectionHeader && (prev as any).props.selected;
|
|
932
945
|
|
|
933
946
|
if (nextSelected && prevSelected) {
|
|
934
947
|
selectedPosition = "middle";
|
|
@@ -949,7 +962,7 @@ const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
|
949
962
|
pressEventName,
|
|
950
963
|
variant,
|
|
951
964
|
sectionHeaderVariant,
|
|
952
|
-
isSectionHeader: current.props.isSectionHeader,
|
|
965
|
+
isSectionHeader: (current as any).props.isSectionHeader,
|
|
953
966
|
gap,
|
|
954
967
|
};
|
|
955
968
|
},
|
|
@@ -1100,7 +1113,7 @@ const ChildrenListViewInner = forwardRef(function ChildrenListViewInner(
|
|
|
1100
1113
|
{ children, ...rest }: ChildrenProps & ListViewRootProps,
|
|
1101
1114
|
forwardedRef: ForwardedRef<IVirtualizedList>
|
|
1102
1115
|
) {
|
|
1103
|
-
const items: ReactElement[] = useMemo(
|
|
1116
|
+
const items: ReactElement<any>[] = useMemo(
|
|
1104
1117
|
() =>
|
|
1105
1118
|
Children.toArray(children).flatMap((child) =>
|
|
1106
1119
|
isValidElement(child) ? [child] : []
|
|
@@ -1118,7 +1131,7 @@ const ChildrenListViewInner = forwardRef(function ChildrenListViewInner(
|
|
|
1118
1131
|
typeof key === "string" ? key : (key ?? index).toString(),
|
|
1119
1132
|
[]
|
|
1120
1133
|
)}
|
|
1121
|
-
renderItem={useCallback((item: ReactElement) => item, [])}
|
|
1134
|
+
renderItem={useCallback((item: ReactElement<any>) => item, [])}
|
|
1122
1135
|
/>
|
|
1123
1136
|
);
|
|
1124
1137
|
});
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
FileIcon,
|
|
3
5
|
ImageIcon,
|
|
6
|
+
NoyaIcon,
|
|
4
7
|
SpeakerLoudIcon,
|
|
5
8
|
VideoIcon,
|
|
6
9
|
} from "@noya-app/noya-icons";
|
|
@@ -23,7 +26,12 @@ const getAssetType = (contentType?: string): AssetType | undefined => {
|
|
|
23
26
|
return undefined;
|
|
24
27
|
};
|
|
25
28
|
|
|
26
|
-
type
|
|
29
|
+
export type RenderThumbnailIconProps = {
|
|
30
|
+
fileName?: string;
|
|
31
|
+
contentType?: string;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type MediaThumbnailProps = {
|
|
27
35
|
contentType?: string;
|
|
28
36
|
url?: string;
|
|
29
37
|
selected?: boolean;
|
|
@@ -39,6 +47,7 @@ type MediaThumbnailProps = {
|
|
|
39
47
|
size?: CollectionItemSize;
|
|
40
48
|
|
|
41
49
|
fileName?: string;
|
|
50
|
+
renderThumbnailIcon?: (props: RenderThumbnailIconProps) => React.ReactNode;
|
|
42
51
|
};
|
|
43
52
|
|
|
44
53
|
export const getThumbnailColors = (
|
|
@@ -86,6 +95,7 @@ export const MediaThumbnail = memo(function MediaThumbnail({
|
|
|
86
95
|
iconName,
|
|
87
96
|
size = "medium",
|
|
88
97
|
fileName,
|
|
98
|
+
renderThumbnailIcon,
|
|
89
99
|
}: MediaThumbnailProps) {
|
|
90
100
|
const { icon: iconColor, background: backgroundColor } = getThumbnailColors(
|
|
91
101
|
colorScheme,
|
|
@@ -101,6 +111,12 @@ export const MediaThumbnail = memo(function MediaThumbnail({
|
|
|
101
111
|
);
|
|
102
112
|
|
|
103
113
|
const content = useMemo(() => {
|
|
114
|
+
if (renderThumbnailIcon) {
|
|
115
|
+
const rendered = renderThumbnailIcon({ fileName, contentType });
|
|
116
|
+
|
|
117
|
+
if (rendered) return rendered;
|
|
118
|
+
}
|
|
119
|
+
|
|
104
120
|
switch (contentType) {
|
|
105
121
|
case "image":
|
|
106
122
|
if (!url) return <ImageIcon color={iconColor} style={iconStyles} />;
|
|
@@ -124,7 +140,12 @@ export const MediaThumbnail = memo(function MediaThumbnail({
|
|
|
124
140
|
}
|
|
125
141
|
|
|
126
142
|
if (fileName) {
|
|
143
|
+
if (fileName.endsWith(".noya")) {
|
|
144
|
+
return <NoyaIcon color={iconColor} style={iconStyles} />;
|
|
145
|
+
}
|
|
146
|
+
|
|
127
147
|
const fileIcon = findFileIcon(fileName);
|
|
148
|
+
|
|
128
149
|
if (fileIcon) {
|
|
129
150
|
return (
|
|
130
151
|
<img
|
|
@@ -138,7 +159,15 @@ export const MediaThumbnail = memo(function MediaThumbnail({
|
|
|
138
159
|
|
|
139
160
|
return <FileIcon color={iconColor} style={iconStyles} />;
|
|
140
161
|
}
|
|
141
|
-
}, [
|
|
162
|
+
}, [
|
|
163
|
+
contentType,
|
|
164
|
+
url,
|
|
165
|
+
iconColor,
|
|
166
|
+
iconStyles,
|
|
167
|
+
iconName,
|
|
168
|
+
renderThumbnailIcon,
|
|
169
|
+
fileName,
|
|
170
|
+
]);
|
|
142
171
|
|
|
143
172
|
return (
|
|
144
173
|
<div
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { MultiplayerUser } from "@noya-app/noya-multiplayer-react";
|
|
4
|
+
import { uuid } from "@noya-app/noya-utils";
|
|
2
5
|
import React, { forwardRef, memo, useMemo } from "react";
|
|
3
6
|
import { cssVars, INPUT_HEIGHT } from "../theme";
|
|
4
7
|
import { cx } from "../utils/classNames";
|
|
5
8
|
import { colorFromString } from "../utils/colorFromString";
|
|
6
9
|
import { Avatar } from "./Avatar";
|
|
7
10
|
import { Logo } from "./NoyaLogo";
|
|
8
|
-
import { Text } from "./Text";
|
|
9
11
|
|
|
10
12
|
export type MessageProps = {
|
|
11
13
|
role: "user" | "assistant" | "system" | "data";
|
|
@@ -20,7 +22,7 @@ export const Message = memo(
|
|
|
20
22
|
{ role, children, user, avatarSize = INPUT_HEIGHT }: MessageProps,
|
|
21
23
|
ref: React.ForwardedRef<HTMLDivElement>
|
|
22
24
|
) {
|
|
23
|
-
const randomId =
|
|
25
|
+
const randomId = uuid();
|
|
24
26
|
const userMessageBackgroundColor = colorFromString(user?.id ?? randomId);
|
|
25
27
|
const styles = useMemo(
|
|
26
28
|
() =>
|
|
@@ -56,18 +58,15 @@ export const Message = memo(
|
|
|
56
58
|
)}
|
|
57
59
|
<div
|
|
58
60
|
className={cx(
|
|
59
|
-
"
|
|
61
|
+
"markdown-message",
|
|
62
|
+
"flex flex-row gap-2 rounded-lg py-2 px-3 transition-all duration-200 max-w-[70%] text-sm",
|
|
63
|
+
role === "user" ? "text-white" : "text-text",
|
|
60
64
|
role === "user" ? "ml-auto" : "bg-input-background"
|
|
61
65
|
)}
|
|
62
66
|
style={styles}
|
|
63
67
|
ref={ref}
|
|
64
68
|
>
|
|
65
|
-
|
|
66
|
-
variant="small"
|
|
67
|
-
className={role === "user" ? "text-white" : "text-text"}
|
|
68
|
-
>
|
|
69
|
-
{children}
|
|
70
|
-
</Text>
|
|
69
|
+
{children}
|
|
71
70
|
</div>
|
|
72
71
|
</div>
|
|
73
72
|
);
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { useControlledOrUncontrolled } from "@noya-app/react-utils";
|
|
2
|
-
import
|
|
3
|
-
import React, {
|
|
4
|
+
import { Popover as PopoverPrimitive } from "radix-ui";
|
|
5
|
+
import React, {
|
|
6
|
+
ComponentProps,
|
|
7
|
+
useCallback,
|
|
8
|
+
useEffect,
|
|
9
|
+
useId,
|
|
10
|
+
useRef,
|
|
11
|
+
} from "react";
|
|
4
12
|
import { useOpenPortalsControls } from "../contexts/OpenPortalsContext";
|
|
5
13
|
import {
|
|
6
14
|
portalScopeProps,
|
|
@@ -26,7 +34,9 @@ interface Props
|
|
|
26
34
|
| "onFocusOutside"
|
|
27
35
|
| "side"
|
|
28
36
|
> {
|
|
29
|
-
children:
|
|
37
|
+
children:
|
|
38
|
+
| React.ReactNode
|
|
39
|
+
| ((props: { close: () => void }) => React.ReactNode);
|
|
30
40
|
trigger: React.ReactNode;
|
|
31
41
|
variant?: PopoverVariant;
|
|
32
42
|
closable?: boolean;
|
|
@@ -72,6 +82,7 @@ export function Popover({
|
|
|
72
82
|
onChange: onOpenChange,
|
|
73
83
|
defaultValue: false,
|
|
74
84
|
});
|
|
85
|
+
const close = useCallback(() => setIsOpen(false), [setIsOpen]);
|
|
75
86
|
|
|
76
87
|
const { addOpenPortal, removeOpenPortal } = useOpenPortalsControls();
|
|
77
88
|
|
|
@@ -127,7 +138,7 @@ export function Popover({
|
|
|
127
138
|
// event.stopPropagation();
|
|
128
139
|
// }}
|
|
129
140
|
>
|
|
130
|
-
{children}
|
|
141
|
+
{typeof children === "function" ? children({ close }) : children}
|
|
131
142
|
{showArrow && (
|
|
132
143
|
<PopoverPrimitive.Arrow className="fill-popover-background" />
|
|
133
144
|
)}
|
|
@@ -1,18 +1,22 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { clamp } from "@noya-app/noya-utils";
|
|
2
|
-
import
|
|
4
|
+
import { Progress as ProgressPrimitive } from "radix-ui";
|
|
3
5
|
import * as React from "react";
|
|
4
6
|
import { cx } from "../utils/classNames";
|
|
5
7
|
|
|
6
|
-
type
|
|
8
|
+
type ColorScheme = "normal" | "warning" | "primary" | "secondary";
|
|
7
9
|
|
|
8
10
|
export function Progress({
|
|
9
11
|
value,
|
|
10
|
-
|
|
12
|
+
colorScheme = "normal",
|
|
11
13
|
className,
|
|
14
|
+
style,
|
|
12
15
|
}: {
|
|
13
16
|
value: number;
|
|
14
|
-
|
|
17
|
+
colorScheme?: ColorScheme;
|
|
15
18
|
className?: string;
|
|
19
|
+
style?: React.CSSProperties;
|
|
16
20
|
}) {
|
|
17
21
|
const clampedValue = clamp(value, 0, 100);
|
|
18
22
|
const transformStyles = React.useMemo(
|
|
@@ -24,12 +28,21 @@ export function Progress({
|
|
|
24
28
|
|
|
25
29
|
return (
|
|
26
30
|
<ProgressPrimitive.Root
|
|
27
|
-
className={cx(
|
|
28
|
-
|
|
31
|
+
className={cx(
|
|
32
|
+
`relative overflow-hidden bg-input-background h-[5px]`,
|
|
33
|
+
className
|
|
34
|
+
)}
|
|
35
|
+
style={{ transform: "translateZ(0)", ...style }}
|
|
29
36
|
value={clampedValue}
|
|
30
37
|
>
|
|
31
38
|
<ProgressPrimitive.Indicator
|
|
32
|
-
className={
|
|
39
|
+
className={cx(
|
|
40
|
+
"transition-[transform_660ms_cubic-bezier(0.65,0,0.35,1) w-full h-full",
|
|
41
|
+
colorScheme === "primary" && "bg-primary",
|
|
42
|
+
colorScheme === "secondary" && "bg-secondary",
|
|
43
|
+
colorScheme === "warning" && "bg-warning",
|
|
44
|
+
colorScheme === "normal" && "bg-text"
|
|
45
|
+
)}
|
|
33
46
|
style={transformStyles}
|
|
34
47
|
/>
|
|
35
48
|
</ProgressPrimitive.Root>
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { memoGeneric } from "@noya-app/react-utils";
|
|
2
|
-
import
|
|
4
|
+
import { ToggleGroup as ToggleGroupPrimitive } from "radix-ui";
|
|
3
5
|
import React, {
|
|
4
6
|
createContext,
|
|
5
7
|
forwardRef,
|
|
@@ -15,7 +17,7 @@ import { SelectableMenuItem } from "./internal/Menu";
|
|
|
15
17
|
import { Tooltip } from "./Tooltip";
|
|
16
18
|
|
|
17
19
|
type SegmentedControlColorScheme = "primary" | "secondary";
|
|
18
|
-
type SegmentedControlVariant = "default" | "tabs";
|
|
20
|
+
type SegmentedControlVariant = "default" | "tabs" | "buttons";
|
|
19
21
|
|
|
20
22
|
type SegmentedControlContextValue = {
|
|
21
23
|
/** @default primary */
|
|
@@ -75,26 +77,49 @@ const SegmentedControlItem = forwardRef(function SegmentedControlItem<
|
|
|
75
77
|
className={cx(
|
|
76
78
|
"group",
|
|
77
79
|
"flex items-center justify-center",
|
|
78
|
-
"font-sans text-button relative flex-1 appearance-none
|
|
79
|
-
|
|
80
|
+
"font-sans text-button relative flex-1 appearance-none focus:outline-none transition-colors",
|
|
81
|
+
// text color
|
|
82
|
+
variant === "buttons"
|
|
83
|
+
? "text-button-text"
|
|
84
|
+
: "text-segmented-control-item",
|
|
85
|
+
// font weight
|
|
86
|
+
variant === "tabs"
|
|
87
|
+
? "font-semibold"
|
|
88
|
+
: variant === "buttons"
|
|
89
|
+
? "font-medium"
|
|
90
|
+
: "font-normal",
|
|
91
|
+
// focus ring
|
|
80
92
|
variant === "tabs"
|
|
81
93
|
? ""
|
|
82
|
-
:
|
|
83
|
-
? "focus:ring-2 focus:ring-
|
|
84
|
-
:
|
|
94
|
+
: variant === "buttons"
|
|
95
|
+
? "focus-visible:ring-2 focus-visible:ring-primary focus:shadow-[0_0_0_1px_var(--n-popover-background)_inset] transition-all"
|
|
96
|
+
: colorScheme === "secondary"
|
|
97
|
+
? "focus:ring-2 focus:ring-secondary focus:ring-offset-1"
|
|
98
|
+
: "focus:ring-2 focus:ring-primary focus:ring-offset-1",
|
|
99
|
+
// checked
|
|
85
100
|
variant === "tabs"
|
|
86
101
|
? "aria-checked:text-primary"
|
|
87
|
-
:
|
|
88
|
-
? "
|
|
89
|
-
: colorScheme === "
|
|
90
|
-
? "aria-checked:bg-
|
|
91
|
-
:
|
|
102
|
+
: variant === "buttons"
|
|
103
|
+
? "active:text-button-text-active active:bg-button-background-active aria-checked:text-button-text-active aria-checked:bg-button-background-active"
|
|
104
|
+
: colorScheme === "secondary"
|
|
105
|
+
? "aria-checked:bg-secondary aria-checked:text-white"
|
|
106
|
+
: colorScheme === "primary"
|
|
107
|
+
? "aria-checked:bg-primary aria-checked:text-white"
|
|
108
|
+
: "aria-checked:bg-segmented-control-item-active-background aria-checked:text-segmented-control-item-active-text",
|
|
92
109
|
"focus:z-interactable",
|
|
93
|
-
|
|
94
|
-
? "px-1 rounded aria-checked:shadow-segment"
|
|
95
|
-
: "px-1.5 border-y-2 border-y-transparent aria-checked:border-b-primary",
|
|
110
|
+
// checked default (all colorSchemes)
|
|
96
111
|
variant === "default" &&
|
|
97
|
-
"
|
|
112
|
+
"aria-checked:border-r-transparent [&:has(+[aria-checked=true])]:border-r-transparent aria-checked:shadow-segment",
|
|
113
|
+
// hover
|
|
114
|
+
variant === "buttons" && "hover:bg-button-background-hover",
|
|
115
|
+
// spacing and borders
|
|
116
|
+
variant === "default"
|
|
117
|
+
? "px-1 rounded border-r border-divider last:border-r-0"
|
|
118
|
+
: variant === "buttons"
|
|
119
|
+
? "px-1 rounded-sm max-w-fit"
|
|
120
|
+
: "px-1.5 border-y-2 border-y-transparent aria-checked:border-b-primary",
|
|
121
|
+
// icon only buttons
|
|
122
|
+
variant === "buttons" && title === "" && icon && "min-w-input-height",
|
|
98
123
|
className
|
|
99
124
|
)}
|
|
100
125
|
{...props}
|
|
@@ -103,7 +128,7 @@ const SegmentedControlItem = forwardRef(function SegmentedControlItem<
|
|
|
103
128
|
className={cx(
|
|
104
129
|
"inline-flex whitespace-pre flex-nowrap gap-1.5 items-center justify-center align-middle text-center",
|
|
105
130
|
variant === "tabs" &&
|
|
106
|
-
"group-focus:ring-2 group-focus:ring-primary-pastel group-focus:ring-offset-2"
|
|
131
|
+
"group-focus-visible:ring-2 group-focus-visible:ring-primary-pastel group-focus-visible:ring-offset-2"
|
|
107
132
|
)}
|
|
108
133
|
>
|
|
109
134
|
{icon && renderIcon(icon)}
|
|
@@ -155,6 +180,7 @@ export const SegmentedControl = memoGeneric(function SegmentedControl<
|
|
|
155
180
|
variant === "default" &&
|
|
156
181
|
"grid min-h-input-height bg-input-background rounded py-0.5 px-0.5",
|
|
157
182
|
variant === "tabs" && "flex gap-1.5 min-h-[33px]",
|
|
183
|
+
variant === "buttons" && "flex gap-1.5 min-h-input-height",
|
|
158
184
|
className
|
|
159
185
|
)}
|
|
160
186
|
style={{
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
ChevronDownIcon,
|
|
3
5
|
ChevronUpIcon,
|
|
4
6
|
DropdownChevronIcon,
|
|
5
7
|
} from "@noya-app/noya-icons";
|
|
6
8
|
import { memoGeneric } from "@noya-app/react-utils";
|
|
7
|
-
import
|
|
9
|
+
import { Select } from "radix-ui";
|
|
8
10
|
import { type SelectProps } from "@radix-ui/react-select";
|
|
9
11
|
import React, {
|
|
10
12
|
CSSProperties,
|
|
@@ -12,7 +14,7 @@ import React, {
|
|
|
12
14
|
useMemo,
|
|
13
15
|
useState,
|
|
14
16
|
} from "react";
|
|
15
|
-
import { useLabel,
|
|
17
|
+
import { useLabel, useLabelType } from "../hooks/useLabel";
|
|
16
18
|
import { cx } from "../utils/classNames";
|
|
17
19
|
|
|
18
20
|
import {
|
|
@@ -92,7 +94,7 @@ const SelectMenuTrigger = memoGeneric(function SelectMenuTrigger({
|
|
|
92
94
|
label: props.label,
|
|
93
95
|
fieldId: props.id,
|
|
94
96
|
});
|
|
95
|
-
const
|
|
97
|
+
const labelType = useLabelType();
|
|
96
98
|
|
|
97
99
|
return (
|
|
98
100
|
<Select.SelectTrigger asChild onFocus={onFocus} onBlur={onBlur}>
|
|
@@ -107,7 +109,7 @@ const SelectMenuTrigger = memoGeneric(function SelectMenuTrigger({
|
|
|
107
109
|
disabled={disabled}
|
|
108
110
|
>
|
|
109
111
|
{icon && <span className="pr-1.5">{renderIcon(icon)}</span>}
|
|
110
|
-
{label &&
|
|
112
|
+
{label && labelType === "inset" && (
|
|
111
113
|
<div className={insetEndStyles}>
|
|
112
114
|
<Label className="pr-4 text-text-disabled" htmlFor={id}>
|
|
113
115
|
{label}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
cssVarNames,
|
|
5
|
+
cx,
|
|
3
6
|
portalScopeProps,
|
|
4
7
|
usePortalScopeId,
|
|
5
8
|
} from "@noya-app/noya-designsystem";
|
|
@@ -7,7 +10,7 @@ import { Rect } from "@noya-app/noya-geometry";
|
|
|
7
10
|
import { useSize } from "@noya-app/react-utils";
|
|
8
11
|
import * as PopperPrimitive from "@radix-ui/react-popper";
|
|
9
12
|
import type { MeasurableElement } from "@radix-ui/utils";
|
|
10
|
-
import React, { memo, useMemo } from "react";
|
|
13
|
+
import React, { ComponentProps, memo, useMemo } from "react";
|
|
11
14
|
import { createPortal } from "react-dom";
|
|
12
15
|
|
|
13
16
|
// Create a Measurable from a Rect
|
|
@@ -25,10 +28,20 @@ const createVirtualElement = (rect: Rect): MeasurableElement => ({
|
|
|
25
28
|
}),
|
|
26
29
|
});
|
|
27
30
|
|
|
28
|
-
type
|
|
31
|
+
type PopperPrimitiveContentProps = ComponentProps<
|
|
32
|
+
typeof PopperPrimitive.Content
|
|
33
|
+
>;
|
|
34
|
+
|
|
35
|
+
type SelectionToolbarContainerProps = Pick<
|
|
36
|
+
PopperPrimitiveContentProps,
|
|
37
|
+
"side" | "sideOffset" | "align"
|
|
38
|
+
> & {
|
|
29
39
|
rect: Rect;
|
|
30
40
|
children: React.ReactNode;
|
|
31
41
|
portalContainer?: HTMLElement | null;
|
|
42
|
+
containerClassName?: string;
|
|
43
|
+
containerStyle?: React.CSSProperties;
|
|
44
|
+
transitionDuration?: number;
|
|
32
45
|
};
|
|
33
46
|
|
|
34
47
|
export const SelectionToolbarContainer = memo(
|
|
@@ -36,6 +49,12 @@ export const SelectionToolbarContainer = memo(
|
|
|
36
49
|
rect,
|
|
37
50
|
children,
|
|
38
51
|
portalContainer,
|
|
52
|
+
side = "top",
|
|
53
|
+
sideOffset = 10,
|
|
54
|
+
align = "center",
|
|
55
|
+
containerClassName,
|
|
56
|
+
containerStyle,
|
|
57
|
+
transitionDuration = 0.2,
|
|
39
58
|
}: SelectionToolbarContainerProps) {
|
|
40
59
|
const portalScopeId = usePortalScopeId();
|
|
41
60
|
const containerRef = React.useRef<HTMLDivElement>(null);
|
|
@@ -52,23 +71,27 @@ export const SelectionToolbarContainer = memo(
|
|
|
52
71
|
const popperContent = (
|
|
53
72
|
<PopperPrimitive.Content
|
|
54
73
|
{...portalScopeProps(portalScopeId)}
|
|
55
|
-
side=
|
|
56
|
-
sideOffset={
|
|
57
|
-
align=
|
|
74
|
+
side={side}
|
|
75
|
+
sideOffset={sideOffset}
|
|
76
|
+
align={align}
|
|
58
77
|
avoidCollisions
|
|
59
78
|
collisionPadding={10}
|
|
60
79
|
contentEditable={false}
|
|
61
80
|
style={{
|
|
62
|
-
zIndex:
|
|
81
|
+
zIndex: 10,
|
|
63
82
|
opacity: size ? 1 : 0,
|
|
64
|
-
transition:
|
|
83
|
+
transition: `opacity ${transitionDuration}s ease-in-out`,
|
|
65
84
|
[cssVarNames.colors.inputBackground]: "transparent",
|
|
66
85
|
[cssVarNames.colors.buttonBackground]: "transparent",
|
|
67
86
|
}}
|
|
68
87
|
>
|
|
69
88
|
<div
|
|
70
89
|
ref={containerRef}
|
|
71
|
-
className=
|
|
90
|
+
className={cx(
|
|
91
|
+
"flex gap-1 bg-popover-background rounded shadow-popover p-1",
|
|
92
|
+
containerClassName
|
|
93
|
+
)}
|
|
94
|
+
style={containerStyle}
|
|
72
95
|
onClick={(e) => e.stopPropagation()}
|
|
73
96
|
onMouseDown={(e) => e.stopPropagation()}
|
|
74
97
|
onPointerDown={(e) => e.stopPropagation()}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Slider as RadixSlider } from "radix-ui";
|
|
2
4
|
import React, { FocusEventHandler, memo, useCallback, useMemo } from "react";
|
|
3
5
|
import { useLabel } from "../hooks/useLabel";
|
|
4
6
|
import { cx } from "../utils/classNames";
|
|
@@ -78,7 +80,7 @@ export const Slider = memo(function Slider({
|
|
|
78
80
|
>
|
|
79
81
|
<div className="relative flex-grow h-[3px] overflow-hidden">
|
|
80
82
|
<div
|
|
81
|
-
className="absolute h-full rounded-l bg-
|
|
83
|
+
className="absolute h-full rounded-l bg-slider-active-background"
|
|
82
84
|
style={{
|
|
83
85
|
left: 0,
|
|
84
86
|
width: `calc(${ratio * 100}% - ${ratio * 8}px - 2px)`,
|