@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,7 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { useControlledOrUncontrolled } from "@noya-app/react-utils";
|
|
2
|
-
import
|
|
4
|
+
import { Popover as PopoverPrimitive } from "radix-ui";
|
|
3
5
|
import React, {
|
|
4
6
|
ComponentProps,
|
|
5
7
|
useCallback,
|
|
@@ -44,13 +46,15 @@ interface Props
|
|
|
44
46
|
onClickClose?: () => void;
|
|
45
47
|
showArrow?: boolean;
|
|
46
48
|
className?: string;
|
|
49
|
+
style?: React.CSSProperties;
|
|
47
50
|
portalContainer?: HTMLElement | null;
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
export const popoverStyle = {
|
|
51
|
-
base:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
base:
|
|
55
|
+
"n-rounded n-font-[14px] n-bg-popover-background n-shadow-popover n-max-h-[600px] n-overflow-y-auto n-text-text-muted n-z-[1000]",
|
|
56
|
+
large: "n-w-[680px]",
|
|
57
|
+
normal: "n-w-[240px]",
|
|
54
58
|
};
|
|
55
59
|
|
|
56
60
|
export function Popover({
|
|
@@ -71,6 +75,7 @@ export function Popover({
|
|
|
71
75
|
onClickClose,
|
|
72
76
|
className,
|
|
73
77
|
portalContainer,
|
|
78
|
+
style,
|
|
74
79
|
}: Props) {
|
|
75
80
|
const defaultId = useId();
|
|
76
81
|
const portalScopeId = usePortalScopeId();
|
|
@@ -101,6 +106,7 @@ export function Popover({
|
|
|
101
106
|
<PopoverPrimitive.Content
|
|
102
107
|
{...portalScopeProps(portalScopeId)}
|
|
103
108
|
ref={contentRef}
|
|
109
|
+
style={style}
|
|
104
110
|
className={cx(
|
|
105
111
|
popoverStyle.base,
|
|
106
112
|
variant === "large" && popoverStyle.large,
|
|
@@ -138,11 +144,11 @@ export function Popover({
|
|
|
138
144
|
>
|
|
139
145
|
{typeof children === "function" ? children({ close }) : children}
|
|
140
146
|
{showArrow && (
|
|
141
|
-
<PopoverPrimitive.Arrow className="fill-popover-background" />
|
|
147
|
+
<PopoverPrimitive.Arrow className="n-fill-popover-background" />
|
|
142
148
|
)}
|
|
143
149
|
{closable && (
|
|
144
150
|
<PopoverPrimitive.Close
|
|
145
|
-
className="rounded-full h-[25px] w-[25px] inline-flex items-center justify-center absolute top-[5px] right-[5px]"
|
|
151
|
+
className="n-rounded-full n-h-[25px] n-w-[25px] n-inline-flex n-items-center n-justify-center n-absolute n-top-[5px] n-right-[5px]"
|
|
146
152
|
style={closeStyles}
|
|
147
153
|
>
|
|
148
154
|
<IconButton iconName="Cross2Icon" onClick={onClickClose} />
|
|
@@ -1,35 +1,58 @@
|
|
|
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",
|
|
13
|
+
color,
|
|
11
14
|
className,
|
|
15
|
+
style: styleProp,
|
|
12
16
|
}: {
|
|
17
|
+
/** A value between 0 and 100. */
|
|
13
18
|
value: number;
|
|
14
|
-
|
|
19
|
+
|
|
20
|
+
colorScheme?: ColorScheme;
|
|
21
|
+
color?: string;
|
|
15
22
|
className?: string;
|
|
23
|
+
style?: React.CSSProperties;
|
|
16
24
|
}) {
|
|
17
25
|
const clampedValue = clamp(value, 0, 100);
|
|
18
26
|
const transformStyles = React.useMemo(
|
|
19
27
|
() => ({
|
|
20
28
|
transform: `translateX(-${100 - clampedValue}%)`,
|
|
29
|
+
backgroundColor: color,
|
|
21
30
|
}),
|
|
22
|
-
[clampedValue]
|
|
31
|
+
[color, clampedValue]
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const style = React.useMemo(
|
|
35
|
+
() => ({ transform: "translateZ(0)", ...styleProp }),
|
|
36
|
+
[styleProp]
|
|
23
37
|
);
|
|
24
38
|
|
|
25
39
|
return (
|
|
26
40
|
<ProgressPrimitive.Root
|
|
27
|
-
className={cx(
|
|
28
|
-
|
|
41
|
+
className={cx(
|
|
42
|
+
`n-relative n-overflow-hidden n-bg-input-background n-h-[5px]`,
|
|
43
|
+
className
|
|
44
|
+
)}
|
|
45
|
+
style={style}
|
|
29
46
|
value={clampedValue}
|
|
30
47
|
>
|
|
31
48
|
<ProgressPrimitive.Indicator
|
|
32
|
-
className={
|
|
49
|
+
className={cx(
|
|
50
|
+
"n-transition-[transform_660ms_cubic-bezier(0.65,0,0.35,1)] n-w-full n-h-full",
|
|
51
|
+
colorScheme === "primary" && "n-bg-primary",
|
|
52
|
+
colorScheme === "secondary" && "n-bg-secondary",
|
|
53
|
+
colorScheme === "warning" && "n-bg-warning",
|
|
54
|
+
colorScheme === "normal" && "n-bg-text"
|
|
55
|
+
)}
|
|
33
56
|
style={transformStyles}
|
|
34
57
|
/>
|
|
35
58
|
</ProgressPrimitive.Root>
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createClickCounter } from "@noya-app/react-utils";
|
|
3
|
+
import React, { ReactNode, useCallback, useRef, useState } from "react";
|
|
4
|
+
|
|
5
|
+
interface ResizableContainerProps {
|
|
6
|
+
children: ReactNode | ((children: { width?: number }) => ReactNode);
|
|
7
|
+
minWidth: number;
|
|
8
|
+
maxWidth?: number;
|
|
9
|
+
style?: React.CSSProperties;
|
|
10
|
+
innerStyle?: React.CSSProperties;
|
|
11
|
+
handleContainerStyle?: React.CSSProperties;
|
|
12
|
+
handleStyle?: React.CSSProperties;
|
|
13
|
+
className?: string;
|
|
14
|
+
onChangeWidth?: (width: number) => void;
|
|
15
|
+
shouldResetWidthOnGestureEnd?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const ResizableContainer: React.FC<ResizableContainerProps> = ({
|
|
19
|
+
children,
|
|
20
|
+
minWidth,
|
|
21
|
+
maxWidth,
|
|
22
|
+
style,
|
|
23
|
+
innerStyle,
|
|
24
|
+
handleContainerStyle,
|
|
25
|
+
handleStyle,
|
|
26
|
+
className,
|
|
27
|
+
onChangeWidth,
|
|
28
|
+
shouldResetWidthOnGestureEnd: shouldResetWidth,
|
|
29
|
+
}) => {
|
|
30
|
+
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
31
|
+
const resizeableRef = useRef<HTMLDivElement | null>(null);
|
|
32
|
+
const handleRef = useRef<HTMLDivElement | null>(null);
|
|
33
|
+
const clickCounter = useRef(createClickCounter({ delay: 250 }));
|
|
34
|
+
|
|
35
|
+
const [width, setWidth] = useState<number>();
|
|
36
|
+
const [drag, setDrag] = useState<{
|
|
37
|
+
startX: number;
|
|
38
|
+
initialWidth: number;
|
|
39
|
+
minWidth: number;
|
|
40
|
+
maxWidth: number;
|
|
41
|
+
}>();
|
|
42
|
+
|
|
43
|
+
const handlePointerDown = useCallback(
|
|
44
|
+
(event: React.PointerEvent<HTMLDivElement>) => {
|
|
45
|
+
const containerWidth =
|
|
46
|
+
containerRef.current?.getBoundingClientRect().width;
|
|
47
|
+
const resizeableWidth =
|
|
48
|
+
resizeableRef.current?.getBoundingClientRect().width;
|
|
49
|
+
|
|
50
|
+
if (containerWidth === undefined) return;
|
|
51
|
+
if (resizeableWidth === undefined) return;
|
|
52
|
+
|
|
53
|
+
// start pointer capture
|
|
54
|
+
handleRef.current?.setPointerCapture(event.pointerId);
|
|
55
|
+
|
|
56
|
+
setDrag({
|
|
57
|
+
startX: event.clientX,
|
|
58
|
+
initialWidth: resizeableWidth,
|
|
59
|
+
minWidth: minWidth,
|
|
60
|
+
maxWidth: maxWidth ?? containerWidth,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
event.preventDefault();
|
|
64
|
+
event.stopPropagation();
|
|
65
|
+
},
|
|
66
|
+
[maxWidth, minWidth]
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const handlePointerMove = useCallback(
|
|
70
|
+
(event: React.PointerEvent<HTMLDivElement>) => {
|
|
71
|
+
if (!drag) return;
|
|
72
|
+
|
|
73
|
+
const delta = event.clientX - drag.startX;
|
|
74
|
+
|
|
75
|
+
let newWidth = clamp(
|
|
76
|
+
drag.initialWidth + delta,
|
|
77
|
+
drag.minWidth,
|
|
78
|
+
drag.maxWidth
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
setWidth(newWidth);
|
|
82
|
+
onChangeWidth?.(newWidth);
|
|
83
|
+
|
|
84
|
+
event.preventDefault();
|
|
85
|
+
event.stopPropagation();
|
|
86
|
+
},
|
|
87
|
+
[drag, onChangeWidth]
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const handlePointerUp = useCallback(
|
|
91
|
+
(event: React.PointerEvent<HTMLDivElement>) => {
|
|
92
|
+
setDrag(undefined);
|
|
93
|
+
|
|
94
|
+
// Release pointer
|
|
95
|
+
handleRef.current?.releasePointerCapture(event.pointerId);
|
|
96
|
+
|
|
97
|
+
event.preventDefault();
|
|
98
|
+
event.stopPropagation();
|
|
99
|
+
|
|
100
|
+
const clickCount = clickCounter.current.handleClick({
|
|
101
|
+
x: event.clientX,
|
|
102
|
+
y: event.clientY,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
if (clickCount >= 2 || shouldResetWidth) {
|
|
106
|
+
setWidth(undefined);
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
[shouldResetWidth]
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<div
|
|
114
|
+
ref={containerRef}
|
|
115
|
+
id={"resize"}
|
|
116
|
+
style={{ position: "relative", ...style }}
|
|
117
|
+
className={className}
|
|
118
|
+
>
|
|
119
|
+
<div
|
|
120
|
+
ref={resizeableRef}
|
|
121
|
+
style={{
|
|
122
|
+
position: "relative",
|
|
123
|
+
width: width !== undefined ? `${width}px` : "auto",
|
|
124
|
+
...innerStyle,
|
|
125
|
+
}}
|
|
126
|
+
>
|
|
127
|
+
{typeof children === "function" ? children({ width }) : children}
|
|
128
|
+
<div
|
|
129
|
+
ref={handleRef}
|
|
130
|
+
style={{
|
|
131
|
+
position: "absolute",
|
|
132
|
+
right: "-1.25rem",
|
|
133
|
+
bottom: 0,
|
|
134
|
+
top: 0,
|
|
135
|
+
display: "flex",
|
|
136
|
+
width: "1rem",
|
|
137
|
+
cursor: "ew-resize",
|
|
138
|
+
touchAction: "none",
|
|
139
|
+
flexDirection: "column",
|
|
140
|
+
justifyContent: "center",
|
|
141
|
+
paddingLeft: "5px",
|
|
142
|
+
paddingRight: "5px",
|
|
143
|
+
opacity: 1,
|
|
144
|
+
...handleContainerStyle,
|
|
145
|
+
}}
|
|
146
|
+
onPointerDown={handlePointerDown}
|
|
147
|
+
onPointerMove={handlePointerMove}
|
|
148
|
+
onPointerUp={handlePointerUp}
|
|
149
|
+
onPointerCancel={handlePointerUp}
|
|
150
|
+
>
|
|
151
|
+
<div
|
|
152
|
+
style={{
|
|
153
|
+
position: "relative",
|
|
154
|
+
display: "flex",
|
|
155
|
+
height: "2.5rem",
|
|
156
|
+
width: "100%",
|
|
157
|
+
flexDirection: "column",
|
|
158
|
+
alignItems: "center",
|
|
159
|
+
justifyContent: "center",
|
|
160
|
+
borderRadius: "9999px",
|
|
161
|
+
backgroundColor: "rgba(17, 24, 39, 0.5)",
|
|
162
|
+
...handleStyle,
|
|
163
|
+
}}
|
|
164
|
+
></div>
|
|
165
|
+
</div>
|
|
166
|
+
{/* Prevent iframe from capturing pointer events */}
|
|
167
|
+
{drag && (
|
|
168
|
+
<div
|
|
169
|
+
style={{
|
|
170
|
+
position: "fixed",
|
|
171
|
+
inset: 0,
|
|
172
|
+
zIndex: 50,
|
|
173
|
+
cursor: "ew-resize",
|
|
174
|
+
}}
|
|
175
|
+
onPointerMove={handlePointerMove}
|
|
176
|
+
onPointerUp={handlePointerUp}
|
|
177
|
+
onPointerCancel={handlePointerUp}
|
|
178
|
+
/>
|
|
179
|
+
)}
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
);
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
function clamp(value: number, min: number, max: number) {
|
|
186
|
+
return Math.min(Math.max(value, min), max);
|
|
187
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { clamp } from "@noya-app/noya-utils";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { cx } from "../utils/classNames";
|
|
6
|
+
|
|
7
|
+
type ColorScheme = "normal" | "warning" | "primary" | "secondary";
|
|
8
|
+
|
|
9
|
+
export function RingProgress({
|
|
10
|
+
value,
|
|
11
|
+
dividedBy = 100,
|
|
12
|
+
size = 24,
|
|
13
|
+
thickness = 3,
|
|
14
|
+
colorScheme = "normal",
|
|
15
|
+
color,
|
|
16
|
+
trackColor,
|
|
17
|
+
className,
|
|
18
|
+
style,
|
|
19
|
+
ariaLabel,
|
|
20
|
+
}: {
|
|
21
|
+
/** Current value (numerator). */
|
|
22
|
+
value: number;
|
|
23
|
+
/** Total value representing 100%. Defaults to 100. */
|
|
24
|
+
dividedBy?: number;
|
|
25
|
+
/** Diameter in pixels of the ring (outer size). */
|
|
26
|
+
size?: number;
|
|
27
|
+
/** Stroke width of the ring in pixels. */
|
|
28
|
+
thickness?: number;
|
|
29
|
+
/** Ring color scheme, aligned with Progress component. */
|
|
30
|
+
colorScheme?: ColorScheme;
|
|
31
|
+
/** Optional custom color overrides. */
|
|
32
|
+
color?: string;
|
|
33
|
+
trackColor?: string;
|
|
34
|
+
className?: string;
|
|
35
|
+
style?: React.CSSProperties;
|
|
36
|
+
/** Optional aria-label when no visible label is adjacent. */
|
|
37
|
+
ariaLabel?: string;
|
|
38
|
+
}) {
|
|
39
|
+
const safeDivisor = dividedBy > 0 ? dividedBy : 1;
|
|
40
|
+
const clampedRaw = clamp(value, 0, safeDivisor);
|
|
41
|
+
const clampedPercent = (clampedRaw / safeDivisor) * 100;
|
|
42
|
+
|
|
43
|
+
// Geometry
|
|
44
|
+
const radius = React.useMemo(() => (size - thickness) / 2, [size, thickness]);
|
|
45
|
+
const circumference = React.useMemo(() => 2 * Math.PI * radius, [radius]);
|
|
46
|
+
const dashOffset = React.useMemo(
|
|
47
|
+
() => ((100 - clampedPercent) / 100) * circumference,
|
|
48
|
+
[circumference, clampedPercent]
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// Colors: prefer explicit color props, fall back to colorScheme tokens (with safe fallbacks)
|
|
52
|
+
const trackStroke =
|
|
53
|
+
trackColor ?? "var(--n-input-background, rgba(0,0,0,0.08))";
|
|
54
|
+
const colorClass = React.useMemo(() => {
|
|
55
|
+
switch (colorScheme) {
|
|
56
|
+
case "primary":
|
|
57
|
+
return "n-stroke-primary";
|
|
58
|
+
case "secondary":
|
|
59
|
+
return "n-stroke-secondary";
|
|
60
|
+
case "warning":
|
|
61
|
+
return "n-stroke-warning";
|
|
62
|
+
case "normal":
|
|
63
|
+
default:
|
|
64
|
+
return "n-stroke-text";
|
|
65
|
+
}
|
|
66
|
+
}, [colorScheme]);
|
|
67
|
+
|
|
68
|
+
const computedStroke = React.useMemo(() => {
|
|
69
|
+
if (color) return color;
|
|
70
|
+
switch (colorScheme) {
|
|
71
|
+
case "primary":
|
|
72
|
+
return "var(--n-primary, currentColor)";
|
|
73
|
+
case "secondary":
|
|
74
|
+
return "var(--n-secondary, currentColor)";
|
|
75
|
+
case "warning":
|
|
76
|
+
return "var(--n-warning, currentColor)";
|
|
77
|
+
case "normal":
|
|
78
|
+
default:
|
|
79
|
+
return "var(--n-text, currentColor)";
|
|
80
|
+
}
|
|
81
|
+
}, [color, colorScheme]);
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<span
|
|
85
|
+
className={cx("n-inline-flex n-items-center n-justify-center", className)}
|
|
86
|
+
style={style}
|
|
87
|
+
role="progressbar"
|
|
88
|
+
aria-valuenow={clampedRaw}
|
|
89
|
+
aria-valuemin={0}
|
|
90
|
+
aria-valuemax={safeDivisor}
|
|
91
|
+
aria-label={ariaLabel}
|
|
92
|
+
>
|
|
93
|
+
<svg
|
|
94
|
+
width={size}
|
|
95
|
+
height={size}
|
|
96
|
+
viewBox={`0 0 ${size} ${size}`}
|
|
97
|
+
style={{ display: "block" }}
|
|
98
|
+
>
|
|
99
|
+
{/* Track */}
|
|
100
|
+
<circle
|
|
101
|
+
cx={size / 2}
|
|
102
|
+
cy={size / 2}
|
|
103
|
+
r={radius}
|
|
104
|
+
fill="none"
|
|
105
|
+
stroke={trackStroke}
|
|
106
|
+
strokeWidth={thickness}
|
|
107
|
+
/>
|
|
108
|
+
{/* Indicator */}
|
|
109
|
+
<circle
|
|
110
|
+
cx={size / 2}
|
|
111
|
+
cy={size / 2}
|
|
112
|
+
r={radius}
|
|
113
|
+
fill="none"
|
|
114
|
+
className={cx(
|
|
115
|
+
"n-transition-[stroke-dashoffset] n-duration-500 n-ease-[cubic-bezier(0.65,0,0.35,1)]",
|
|
116
|
+
!color && colorClass
|
|
117
|
+
)}
|
|
118
|
+
style={{ stroke: computedStroke }}
|
|
119
|
+
strokeWidth={thickness}
|
|
120
|
+
strokeLinecap="round"
|
|
121
|
+
strokeDasharray={`${circumference} ${circumference}`}
|
|
122
|
+
strokeDashoffset={dashOffset}
|
|
123
|
+
transform={`rotate(-90 ${size / 2} ${size / 2})`}
|
|
124
|
+
/>
|
|
125
|
+
</svg>
|
|
126
|
+
</span>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { ScrollArea as RadixScrollArea } from "radix-ui";
|
|
2
4
|
import * as React from "react";
|
|
3
5
|
|
|
4
6
|
interface Props {
|
|
@@ -12,10 +14,10 @@ export const ScrollArea = React.memo(function ScrollArea({ children }: Props) {
|
|
|
12
14
|
React.useState<HTMLDivElement | null>(null);
|
|
13
15
|
|
|
14
16
|
return (
|
|
15
|
-
<div className="flex-1 min-h-0">
|
|
17
|
+
<div className="n-flex-1 n-min-h-0">
|
|
16
18
|
<RadixScrollArea.Root style={{ width: "100%", height: "100%" }}>
|
|
17
19
|
<RadixScrollArea.Viewport
|
|
18
|
-
className="w-full h-full [&>div]:block"
|
|
20
|
+
className="n-w-full n-h-full [&>div]:n-block"
|
|
19
21
|
ref={React.useCallback(
|
|
20
22
|
(ref: HTMLDivElement | null) => setScrollElementRef(ref),
|
|
21
23
|
[]
|
|
@@ -29,9 +31,9 @@ export const ScrollArea = React.memo(function ScrollArea({ children }: Props) {
|
|
|
29
31
|
</RadixScrollArea.Viewport>
|
|
30
32
|
<RadixScrollArea.Scrollbar
|
|
31
33
|
orientation="vertical"
|
|
32
|
-
className="scroll-component flex p-[3px] data-[orientation=vertical]:w-[10px]"
|
|
34
|
+
className="n-scroll-component n-flex n-p-[3px] data-[orientation=vertical]:n-w-[10px]"
|
|
33
35
|
>
|
|
34
|
-
<RadixScrollArea.Thumb className="scroll-component flex-1 rounded-[10px] bg-scrollbar" />
|
|
36
|
+
<RadixScrollArea.Thumb className="n-scroll-component n-flex-1 n-rounded-[10px] n-bg-scrollbar" />
|
|
35
37
|
</RadixScrollArea.Scrollbar>
|
|
36
38
|
</RadixScrollArea.Root>
|
|
37
39
|
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { ScrollArea } from "./ScrollArea";
|
|
3
|
+
|
|
4
|
+
export function ScrollableSidebar({ children }: { children: React.ReactNode }) {
|
|
5
|
+
return (
|
|
6
|
+
<div className="n-relative n-flex n-flex-col n-flex-1 n-max-h-full">
|
|
7
|
+
<ScrollArea>{children}</ScrollArea>
|
|
8
|
+
</div>
|
|
9
|
+
);
|
|
10
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { getCurrentPlatform, handleKeyboardEvent } from "@noya-app/noya-keymap";
|
|
2
4
|
import React, {
|
|
3
5
|
forwardRef,
|
|
@@ -153,8 +155,8 @@ export const SearchCompletionMenu = forwardRef(function SearchCompletionMenu(
|
|
|
153
155
|
}));
|
|
154
156
|
|
|
155
157
|
return (
|
|
156
|
-
<div {...elementSize} className="flex flex-col overflow-hidden">
|
|
157
|
-
<InputField.Root className="flex flex-0">
|
|
158
|
+
<div {...elementSize} className="n-flex n-flex-col n-overflow-hidden">
|
|
159
|
+
<InputField.Root className="n-flex n-flex-0">
|
|
158
160
|
<InputField.Input
|
|
159
161
|
ref={inputRef}
|
|
160
162
|
value={search}
|
|
@@ -194,7 +196,7 @@ export const SearchCompletionMenu = forwardRef(function SearchCompletionMenu(
|
|
|
194
196
|
) : (
|
|
195
197
|
<div
|
|
196
198
|
{...listSize}
|
|
197
|
-
className="flex flex-col p-20 items-center justify-center"
|
|
199
|
+
className="n-flex n-flex-col n-p-20 n-items-center n-justify-center"
|
|
198
200
|
>
|
|
199
201
|
<Small color="textDisabled">No results</Small>
|
|
200
202
|
</div>
|