@noya-app/noya-designsystem 0.1.49 → 0.1.50
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 +10 -10
- package/CHANGELOG.md +11 -0
- package/dist/index.css +1 -1
- package/dist/index.d.mts +137 -12
- package/dist/index.d.ts +137 -12
- package/dist/index.js +1627 -1129
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1582 -1102
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
- package/src/components/Avatar.tsx +2 -2
- package/src/components/Banner.tsx +29 -0
- package/src/components/Button.tsx +2 -2
- package/src/components/Chip.tsx +8 -8
- package/src/components/Collection.tsx +6 -1
- package/src/components/Dialog.tsx +2 -2
- package/src/components/FillInputField.tsx +1 -1
- package/src/components/Grid.tsx +1 -3
- package/src/components/Label.tsx +11 -6
- package/src/components/LabeledField.tsx +6 -1
- package/src/components/List.tsx +4 -4
- package/src/components/ListView.tsx +10 -5
- package/src/components/MediaThumbnail.tsx +11 -3
- package/src/components/Popover.tsx +9 -6
- package/src/components/Section.tsx +172 -0
- package/src/components/SelectMenu.tsx +1 -1
- package/src/components/Slider.tsx +1 -1
- package/src/components/Sortable.tsx +124 -22
- package/src/components/UserPointer.tsx +1 -1
- package/src/components/ai-assistant/AIAssistantLayout.tsx +102 -0
- package/src/components/connected-users-menu/ConnectedUsersMenuLayout.tsx +71 -0
- package/src/components/file-explorer/FileExplorerLayout.tsx +71 -0
- package/src/components/internal/Menu.tsx +12 -7
- package/src/components/pipeline/PipelineResultLayout.tsx +32 -0
- package/src/index.css +118 -118
- package/src/index.tsx +7 -0
- package/src/utils/classNames.ts +27 -2
- package/src/utils/moveTreeItem.ts +99 -0
- package/tailwind.config.ts +79 -79
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noya-app/noya-designsystem",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.50",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"module": "./dist/index.mjs",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@dnd-kit/core": "6.3.1",
|
|
24
24
|
"@dnd-kit/sortable": "10.0.0",
|
|
25
|
-
"@noya-app/noya-colorpicker": "0.1.
|
|
26
|
-
"@noya-app/noya-utils": "0.1.
|
|
27
|
-
"@noya-app/noya-geometry": "0.1.
|
|
28
|
-
"@noya-app/noya-icons": "0.1.
|
|
25
|
+
"@noya-app/noya-colorpicker": "0.1.19",
|
|
26
|
+
"@noya-app/noya-utils": "0.1.5",
|
|
27
|
+
"@noya-app/noya-geometry": "0.1.10",
|
|
28
|
+
"@noya-app/noya-icons": "0.1.7",
|
|
29
29
|
"@noya-app/noya-keymap": "0.1.3",
|
|
30
30
|
"@radix-ui/primitive": "^1.0.0",
|
|
31
31
|
"@radix-ui/react-avatar": "^1.0.1",
|
|
@@ -181,8 +181,8 @@ const OverflowAvatar = React.memo(function OverflowAvatar({
|
|
|
181
181
|
count: number;
|
|
182
182
|
}) {
|
|
183
183
|
return (
|
|
184
|
-
<AvatarRoot size={size} backgroundColor="var(--text-disabled)" disabled>
|
|
185
|
-
<AvatarFallback backgroundColor="var(--text-disabled)" size={size}>
|
|
184
|
+
<AvatarRoot size={size} backgroundColor="var(--n-text-disabled)" disabled>
|
|
185
|
+
<AvatarFallback backgroundColor="var(--n-text-disabled)" size={size}>
|
|
186
186
|
+{count}
|
|
187
187
|
</AvatarFallback>
|
|
188
188
|
</AvatarRoot>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React, { CSSProperties } from "react";
|
|
2
|
+
import { cssVars } from "../theme";
|
|
3
|
+
import { cx } from "../utils/classNames";
|
|
4
|
+
import { Small } from "./Text";
|
|
5
|
+
|
|
6
|
+
const bannerStyles = {
|
|
7
|
+
backgroundColor: cssVars.colors.inputBackground,
|
|
8
|
+
color: cssVars.colors.textDisabled,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type BannerProps = {
|
|
12
|
+
label?: string;
|
|
13
|
+
className?: string;
|
|
14
|
+
style?: CSSProperties;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function Banner({ label = "No items", className, style }: BannerProps) {
|
|
18
|
+
return (
|
|
19
|
+
<div
|
|
20
|
+
className={cx(
|
|
21
|
+
"flex p-1 items-center justify-center select-none rounded",
|
|
22
|
+
className
|
|
23
|
+
)}
|
|
24
|
+
style={{ ...bannerStyles, ...style }}
|
|
25
|
+
>
|
|
26
|
+
<Small color="textDisabled">{label}</Small>
|
|
27
|
+
</div>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
@@ -5,8 +5,8 @@ import React, {
|
|
|
5
5
|
ReactNode,
|
|
6
6
|
useCallback,
|
|
7
7
|
} from "react";
|
|
8
|
-
import { Tooltip } from "./Tooltip";
|
|
9
8
|
import { cx } from "../utils/classNames";
|
|
9
|
+
import { Tooltip } from "./Tooltip";
|
|
10
10
|
|
|
11
11
|
type ButtonVariant =
|
|
12
12
|
| "normal"
|
|
@@ -87,7 +87,7 @@ export const Button = forwardRef(function Button(
|
|
|
87
87
|
ref={forwardedRef}
|
|
88
88
|
id={id}
|
|
89
89
|
className={cx(
|
|
90
|
-
"no-underline leading-[1] relative border-0 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(--popover-background)_inset] transition-all",
|
|
90
|
+
"no-underline leading-[1] relative border-0 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",
|
|
91
91
|
disabled ? "opacity-25" : "",
|
|
92
92
|
!className?.includes("flex") ? "flex-none" : "",
|
|
93
93
|
active
|
package/src/components/Chip.tsx
CHANGED
|
@@ -19,32 +19,32 @@ const SIZE_STYLES = {
|
|
|
19
19
|
} as const;
|
|
20
20
|
|
|
21
21
|
const SHADOW_STYLES = {
|
|
22
|
-
primary: "shadow-[0_0_0_1px_var(--chip-primary-shadow)_inset]",
|
|
23
|
-
secondary: "shadow-[0_0_0_1px_var(--chip-secondary-shadow)_inset]",
|
|
24
|
-
error: "shadow-[0_0_0_1px_var(--chip-error-shadow)_inset]",
|
|
25
|
-
default: "shadow-[0_0_0_1px_var(--chip-default-shadow)_inset]",
|
|
22
|
+
primary: "shadow-[0_0_0_1px_var(--n-chip-primary-shadow)_inset]",
|
|
23
|
+
secondary: "shadow-[0_0_0_1px_var(--n-chip-secondary-shadow)_inset]",
|
|
24
|
+
error: "shadow-[0_0_0_1px_var(--n-chip-error-shadow)_inset]",
|
|
25
|
+
default: "shadow-[0_0_0_1px_var(--n-chip-default-shadow)_inset]",
|
|
26
26
|
} as const;
|
|
27
27
|
|
|
28
28
|
const COLOR_STYLES = {
|
|
29
29
|
primary: {
|
|
30
30
|
text: "text-primary",
|
|
31
31
|
bg: "bg-primary-pastel",
|
|
32
|
-
subtle: "bg-[var(--chip-primary-bg)]",
|
|
32
|
+
subtle: "bg-[var(--n-chip-primary-bg)]",
|
|
33
33
|
},
|
|
34
34
|
secondary: {
|
|
35
35
|
text: "text-secondary",
|
|
36
36
|
bg: "bg-secondary-pastel",
|
|
37
|
-
subtle: "bg-[var(--chip-secondary-bg)]",
|
|
37
|
+
subtle: "bg-[var(--n-chip-secondary-bg)]",
|
|
38
38
|
},
|
|
39
39
|
error: {
|
|
40
40
|
text: "text-text",
|
|
41
41
|
bg: "bg-[rgb(255,219,219)]",
|
|
42
|
-
subtle: "bg-[var(--chip-error-bg)]",
|
|
42
|
+
subtle: "bg-[var(--n-chip-error-bg)]",
|
|
43
43
|
},
|
|
44
44
|
default: {
|
|
45
45
|
text: "text-text",
|
|
46
46
|
bg: "bg-input-background",
|
|
47
|
-
subtle: "bg-[var(--chip-default-bg)]",
|
|
47
|
+
subtle: "bg-[var(--n-chip-default-bg)]",
|
|
48
48
|
},
|
|
49
49
|
} as const;
|
|
50
50
|
|
|
@@ -2,7 +2,11 @@ import React from "react";
|
|
|
2
2
|
import { forwardRefGeneric } from "../../../noya-react-utils/src/utils/reactGenerics";
|
|
3
3
|
import { Grid, GridViewSize } from "../components/Grid";
|
|
4
4
|
import { MenuItem } from "../components/internal/Menu";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
ListView,
|
|
7
|
+
ListViewRootProps,
|
|
8
|
+
ListViewRowProps,
|
|
9
|
+
} from "../components/ListView";
|
|
6
10
|
import { List } from "./List";
|
|
7
11
|
|
|
8
12
|
export type CollectionViewType = "grid" | "list";
|
|
@@ -86,6 +90,7 @@ export interface CollectionProps<T, M extends string = string> {
|
|
|
86
90
|
renderEmptyState?: () => React.ReactElement;
|
|
87
91
|
/** @default false */
|
|
88
92
|
sortable?: boolean;
|
|
93
|
+
dragIndicatorStyle?: ListViewRowProps<M>["dragIndicatorStyle"];
|
|
89
94
|
}
|
|
90
95
|
|
|
91
96
|
export const Collection = forwardRefGeneric(function Collection<
|
|
@@ -138,8 +138,8 @@ export const Dialog = forwardRef(function Dialog(
|
|
|
138
138
|
className="z-[1]"
|
|
139
139
|
style={{
|
|
140
140
|
position: "absolute",
|
|
141
|
-
top: "var(--dialog-padding)",
|
|
142
|
-
right: "var(--dialog-padding)",
|
|
141
|
+
top: "var(--n-dialog-padding)",
|
|
142
|
+
right: "var(--n-dialog-padding)",
|
|
143
143
|
}}
|
|
144
144
|
/>
|
|
145
145
|
</DialogPrimitive.Close>
|
|
@@ -11,7 +11,7 @@ const FillButton = forwardRef<
|
|
|
11
11
|
<button
|
|
12
12
|
ref={ref}
|
|
13
13
|
className={cx(
|
|
14
|
-
`outline-none p-0 w-[50px] h-input-height rounded overflow-hidden border-none shadow-[0_0_0_1px_var(--divider)_inset] bg-transparent relative focus:shadow-[0_0_0_1px_var(--sidebar-background),0_0_0_3px_var(--primary)] `,
|
|
14
|
+
`outline-none p-0 w-[50px] h-input-height rounded overflow-hidden border-none shadow-[0_0_0_1px_var(--n-divider)_inset] bg-transparent relative focus:shadow-[0_0_0_1px_var(--n-sidebar-background),0_0_0_3px_var(--n-primary)] `,
|
|
15
15
|
className
|
|
16
16
|
)}
|
|
17
17
|
{...props}
|
package/src/components/Grid.tsx
CHANGED
|
@@ -216,9 +216,7 @@ export const Grid = memoGeneric(
|
|
|
216
216
|
testFocused={isTestingRenaming}
|
|
217
217
|
readOnly={!itemIsRenamable || !onRename}
|
|
218
218
|
onSubmit={(value) => {
|
|
219
|
-
|
|
220
|
-
onRename?.(item, value);
|
|
221
|
-
}
|
|
219
|
+
onRename?.(item, value);
|
|
222
220
|
setRenamingId(undefined);
|
|
223
221
|
}}
|
|
224
222
|
onBlur={() => {
|
package/src/components/Label.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { LabelHTMLAttributes, forwardRef, memo } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { mergeConflictingClassNames } from "../utils/classNames";
|
|
3
3
|
|
|
4
4
|
export type LabelPosition = "inset" | "start" | "end" | "above";
|
|
5
5
|
|
|
@@ -17,11 +17,16 @@ export const Label = memo(
|
|
|
17
17
|
return (
|
|
18
18
|
<label
|
|
19
19
|
ref={ref}
|
|
20
|
-
className={
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
className={mergeConflictingClassNames(
|
|
21
|
+
[
|
|
22
|
+
labelStyles,
|
|
23
|
+
labelTextStyles,
|
|
24
|
+
"text-left min-h-input-height",
|
|
25
|
+
className,
|
|
26
|
+
],
|
|
27
|
+
{
|
|
28
|
+
categories: ["color", "font"],
|
|
29
|
+
}
|
|
25
30
|
)}
|
|
26
31
|
{...props}
|
|
27
32
|
>
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
LabelContext,
|
|
5
5
|
LabelPositionContext,
|
|
6
6
|
LabelPositionContextValue,
|
|
7
|
+
useLabelPosition,
|
|
7
8
|
useLabelWidth,
|
|
8
9
|
} from "../hooks/useLabel";
|
|
9
10
|
import { cx } from "../utils/classNames";
|
|
@@ -23,7 +24,7 @@ const labelPositionInset: LabelPositionContextValue = {
|
|
|
23
24
|
export const LabeledField = memo(function LabeledField({
|
|
24
25
|
children,
|
|
25
26
|
label,
|
|
26
|
-
labelPosition
|
|
27
|
+
labelPosition: labelPositionProp,
|
|
27
28
|
fieldId,
|
|
28
29
|
minWidth = 100,
|
|
29
30
|
className,
|
|
@@ -44,6 +45,10 @@ export const LabeledField = memo(function LabeledField({
|
|
|
44
45
|
const labelWidthFromContext = useLabelWidth();
|
|
45
46
|
const labelWidth = labelWidthFromContext ?? minWidth;
|
|
46
47
|
|
|
48
|
+
const labelPositionFromContext = useLabelPosition();
|
|
49
|
+
const labelPosition =
|
|
50
|
+
labelPositionProp ?? labelPositionFromContext ?? "start";
|
|
51
|
+
|
|
47
52
|
const labeledFieldClasses = useMemo(
|
|
48
53
|
() => labeledFieldStyles(labelPosition),
|
|
49
54
|
[labelPosition]
|
package/src/components/List.tsx
CHANGED
|
@@ -14,7 +14,7 @@ import { ListView, ListViewItemInfo } from "./ListView";
|
|
|
14
14
|
import { TreeView } from "./TreeView";
|
|
15
15
|
|
|
16
16
|
const cssVarOverrides = {
|
|
17
|
-
"--icon-selected": cssVars.colors.primary,
|
|
17
|
+
"--n-icon-selected": cssVars.colors.primary,
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
const emptyArray: string[] = [];
|
|
@@ -60,6 +60,7 @@ export const List = memoGeneric(
|
|
|
60
60
|
getDropTargetParentIndex,
|
|
61
61
|
getPlaceholder,
|
|
62
62
|
onClickItem,
|
|
63
|
+
dragIndicatorStyle,
|
|
63
64
|
} = props;
|
|
64
65
|
|
|
65
66
|
const [internalHoveredId, setHoveredId] = useState<string>();
|
|
@@ -229,6 +230,7 @@ export const List = memoGeneric(
|
|
|
229
230
|
if (!expandable) return;
|
|
230
231
|
setExpanded?.(item, !expanded);
|
|
231
232
|
}}
|
|
233
|
+
dragIndicatorStyle={dragIndicatorStyle}
|
|
232
234
|
>
|
|
233
235
|
<div
|
|
234
236
|
className={cx("flex flex-1 items-center px-2", rowGapMap[size])}
|
|
@@ -255,9 +257,7 @@ export const List = memoGeneric(
|
|
|
255
257
|
className={fontStyleMap[size]}
|
|
256
258
|
autoFocus={!isTestingRenaming}
|
|
257
259
|
onSubmitEditing={(value: string) => {
|
|
258
|
-
|
|
259
|
-
onRename?.(item, value);
|
|
260
|
-
}
|
|
260
|
+
onRename?.(item, value);
|
|
261
261
|
setRenamingId(undefined);
|
|
262
262
|
}}
|
|
263
263
|
onKeyDown={(e: React.KeyboardEvent) => {
|
|
@@ -26,7 +26,7 @@ import { ListChildComponentProps, VariableSizeList } from "react-window";
|
|
|
26
26
|
import { mergeEventHandlers } from "../hooks/mergeEventHandlers";
|
|
27
27
|
import { useHover } from "../hooks/useHover";
|
|
28
28
|
import { cssVars, INPUT_HEIGHT } from "../theme";
|
|
29
|
-
import { cx } from "../utils/classNames";
|
|
29
|
+
import { cx, mergeConflictingClassNames } from "../utils/classNames";
|
|
30
30
|
import { ContextMenu } from "./ContextMenu";
|
|
31
31
|
import { InputField } from "./InputField";
|
|
32
32
|
import { MenuItem } from "./internal/Menu";
|
|
@@ -376,7 +376,7 @@ interface ListViewClickInfo {
|
|
|
376
376
|
ctrlKey: boolean;
|
|
377
377
|
}
|
|
378
378
|
|
|
379
|
-
interface ListViewRowProps<MenuItemType extends string = string> {
|
|
379
|
+
export interface ListViewRowProps<MenuItemType extends string = string> {
|
|
380
380
|
id?: string;
|
|
381
381
|
tabIndex?: number;
|
|
382
382
|
role?: string;
|
|
@@ -1044,9 +1044,14 @@ const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
|
1044
1044
|
aria-labelledby={ariaLabelledby}
|
|
1045
1045
|
aria-orientation={ariaOrientation}
|
|
1046
1046
|
aria-multiselectable={ariaMultiselectable}
|
|
1047
|
-
className={
|
|
1048
|
-
|
|
1049
|
-
|
|
1047
|
+
className={mergeConflictingClassNames(
|
|
1048
|
+
[
|
|
1049
|
+
`flex flex-col text-text-muted ${scrollable ? "flex-1" : "flex-none"} `,
|
|
1050
|
+
className,
|
|
1051
|
+
],
|
|
1052
|
+
{
|
|
1053
|
+
categories: ["flex"],
|
|
1054
|
+
}
|
|
1050
1055
|
)}
|
|
1051
1056
|
style={{ ...gapStyle, ...style }}
|
|
1052
1057
|
{...{
|
|
@@ -4,7 +4,7 @@ import { cssVars } from "../theme";
|
|
|
4
4
|
import { cx } from "../utils/classNames";
|
|
5
5
|
import { IconName, Icons } from "./Icons";
|
|
6
6
|
|
|
7
|
-
type ColorScheme = "primary" | "icon" | "warning";
|
|
7
|
+
type ColorScheme = "primary" | "secondary" | "icon" | "warning";
|
|
8
8
|
|
|
9
9
|
type AssetType = "image" | "video" | "audio";
|
|
10
10
|
|
|
@@ -31,7 +31,10 @@ type MediaThumbnailProps = {
|
|
|
31
31
|
iconName?: IconName;
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
-
const
|
|
34
|
+
export const getThumbnailColors = (
|
|
35
|
+
colorScheme: ColorScheme,
|
|
36
|
+
selected: boolean
|
|
37
|
+
) => {
|
|
35
38
|
if (selected) {
|
|
36
39
|
return {
|
|
37
40
|
icon: cssVars.colors.primary,
|
|
@@ -45,6 +48,11 @@ const getColors = (colorScheme: ColorScheme, selected: boolean) => {
|
|
|
45
48
|
icon: cssVars.colors.primary,
|
|
46
49
|
background: cssVars.colors.listViewThumbnailBackground,
|
|
47
50
|
};
|
|
51
|
+
case "secondary":
|
|
52
|
+
return {
|
|
53
|
+
icon: cssVars.colors.secondary,
|
|
54
|
+
background: cssVars.colors.secondaryPastel,
|
|
55
|
+
};
|
|
48
56
|
case "warning":
|
|
49
57
|
return {
|
|
50
58
|
icon: cssVars.colors.warning,
|
|
@@ -72,7 +80,7 @@ export const MediaThumbnail = memo(function MediaThumbnail({
|
|
|
72
80
|
url,
|
|
73
81
|
iconName = "FileIcon",
|
|
74
82
|
}: MediaThumbnailProps) {
|
|
75
|
-
const { icon: iconColor, background: backgroundColor } =
|
|
83
|
+
const { icon: iconColor, background: backgroundColor } = getThumbnailColors(
|
|
76
84
|
colorScheme,
|
|
77
85
|
selected
|
|
78
86
|
);
|
|
@@ -32,6 +32,12 @@ interface Props
|
|
|
32
32
|
className?: string;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
export const popoverStyle = {
|
|
36
|
+
base: "rounded font-[14px] bg-popover-background shadow-[0_2px_4px_rgba(0,0,0,0.2),0_0_12px_rgba(0,0,0,0.1)] max-h-[600px] overflow-y-auto text-text-muted z-[1000]",
|
|
37
|
+
large: "w-[680px]",
|
|
38
|
+
normal: "w-[240px]",
|
|
39
|
+
};
|
|
40
|
+
|
|
35
41
|
export function Popover({
|
|
36
42
|
children,
|
|
37
43
|
trigger,
|
|
@@ -56,12 +62,9 @@ export function Popover({
|
|
|
56
62
|
<PopoverPrimitive.Portal>
|
|
57
63
|
<PopoverPrimitive.Content
|
|
58
64
|
className={cx(
|
|
59
|
-
|
|
60
|
-
variant === "large"
|
|
61
|
-
|
|
62
|
-
: variant === "normal"
|
|
63
|
-
? "w-[240px]"
|
|
64
|
-
: "",
|
|
65
|
+
popoverStyle.base,
|
|
66
|
+
variant === "large" && popoverStyle.large,
|
|
67
|
+
variant === "normal" && popoverStyle.normal,
|
|
65
68
|
className
|
|
66
69
|
)}
|
|
67
70
|
side={side}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Button,
|
|
3
|
+
IconButton,
|
|
4
|
+
IndentContext,
|
|
5
|
+
INPUT_HEIGHT,
|
|
6
|
+
InspectorPrimitives,
|
|
7
|
+
Spacer,
|
|
8
|
+
textStyles,
|
|
9
|
+
} from "@noya-app/noya-designsystem";
|
|
10
|
+
import { clientStorage, usePersistentStateString } from "@noya-app/react-utils";
|
|
11
|
+
import React, { CSSProperties, memo, useMemo } from "react";
|
|
12
|
+
|
|
13
|
+
type TitleIconOptions = {
|
|
14
|
+
inlineBlockWrapper?: boolean;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type SectionProps = {
|
|
18
|
+
children: React.ReactNode;
|
|
19
|
+
title?: React.ReactNode;
|
|
20
|
+
onClickTitle?: () => void;
|
|
21
|
+
titleTextStyle?: "small" | "heading5" | "heading4" | "heading3";
|
|
22
|
+
right?: React.ReactNode;
|
|
23
|
+
hideRightWhenCollapsed?: boolean;
|
|
24
|
+
extraPadding?: boolean;
|
|
25
|
+
className?: string;
|
|
26
|
+
style?: CSSProperties;
|
|
27
|
+
titleIcon?: React.ReactNode;
|
|
28
|
+
titleIconOptions?: TitleIconOptions;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const titleIconStyle: CSSProperties = {
|
|
32
|
+
marginRight: "8px",
|
|
33
|
+
alignSelf: "flex-start",
|
|
34
|
+
position: "relative",
|
|
35
|
+
top: "2px",
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const titleIconWrapperStyle: CSSProperties = {
|
|
39
|
+
display: "inline-block",
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const SectionInternal = ({
|
|
43
|
+
children,
|
|
44
|
+
title,
|
|
45
|
+
onClickTitle,
|
|
46
|
+
titleTextStyle = "heading4",
|
|
47
|
+
right,
|
|
48
|
+
extraPadding,
|
|
49
|
+
className,
|
|
50
|
+
style: styleProp,
|
|
51
|
+
titleIcon,
|
|
52
|
+
titleIconOptions = {
|
|
53
|
+
inlineBlockWrapper: true,
|
|
54
|
+
},
|
|
55
|
+
}: SectionProps) => {
|
|
56
|
+
const style: CSSProperties = useMemo(
|
|
57
|
+
() => ({
|
|
58
|
+
display: "flex",
|
|
59
|
+
flexDirection: "column",
|
|
60
|
+
gap: "8px",
|
|
61
|
+
paddingTop:
|
|
62
|
+
titleTextStyle === "heading3"
|
|
63
|
+
? "12px"
|
|
64
|
+
: title && extraPadding
|
|
65
|
+
? "24px"
|
|
66
|
+
: "12px",
|
|
67
|
+
paddingBottom:
|
|
68
|
+
titleTextStyle === "heading3"
|
|
69
|
+
? "12px"
|
|
70
|
+
: title && extraPadding
|
|
71
|
+
? "12px"
|
|
72
|
+
: "12px",
|
|
73
|
+
...styleProp,
|
|
74
|
+
}),
|
|
75
|
+
[titleTextStyle, title, extraPadding, styleProp]
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const hasChildren = React.Children.toArray(children).some((child) => !!child);
|
|
79
|
+
|
|
80
|
+
const headerStyle: CSSProperties = useMemo(
|
|
81
|
+
() => ({
|
|
82
|
+
marginBottom: hasChildren ? "4px" : undefined,
|
|
83
|
+
minHeight: INPUT_HEIGHT,
|
|
84
|
+
}),
|
|
85
|
+
[hasChildren]
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<div style={style} className={className}>
|
|
90
|
+
{(title || titleIcon || right) && (
|
|
91
|
+
<InspectorPrimitives.SectionHeader style={headerStyle}>
|
|
92
|
+
{titleIcon && (
|
|
93
|
+
<div style={titleIconStyle} className={textStyles[titleTextStyle]}>
|
|
94
|
+
{titleIconOptions.inlineBlockWrapper ? (
|
|
95
|
+
<div style={titleIconWrapperStyle}>{titleIcon}</div>
|
|
96
|
+
) : (
|
|
97
|
+
titleIcon
|
|
98
|
+
)}
|
|
99
|
+
</div>
|
|
100
|
+
)}
|
|
101
|
+
{title && (
|
|
102
|
+
<>
|
|
103
|
+
{onClickTitle ? (
|
|
104
|
+
<Button variant="none" onClick={onClickTitle}>
|
|
105
|
+
<InspectorPrimitives.Title $textStyle={titleTextStyle}>
|
|
106
|
+
{title}
|
|
107
|
+
</InspectorPrimitives.Title>
|
|
108
|
+
</Button>
|
|
109
|
+
) : (
|
|
110
|
+
<InspectorPrimitives.Title $textStyle={titleTextStyle}>
|
|
111
|
+
{title}
|
|
112
|
+
</InspectorPrimitives.Title>
|
|
113
|
+
)}
|
|
114
|
+
</>
|
|
115
|
+
)}
|
|
116
|
+
<Spacer.Horizontal />
|
|
117
|
+
{right}
|
|
118
|
+
</InspectorPrimitives.SectionHeader>
|
|
119
|
+
)}
|
|
120
|
+
<IndentContext.Provider value={{ indentLevel: 1 }}>
|
|
121
|
+
{children}
|
|
122
|
+
</IndentContext.Provider>
|
|
123
|
+
</div>
|
|
124
|
+
);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
type ExpandableSectionProps = {
|
|
128
|
+
storageKey: string;
|
|
129
|
+
initialVisibility?: "show" | "hide";
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const ExpandableSection = ({
|
|
133
|
+
storageKey,
|
|
134
|
+
initialVisibility = "show",
|
|
135
|
+
...props
|
|
136
|
+
}: SectionProps & ExpandableSectionProps) => {
|
|
137
|
+
const [visibility, setVisibility] = usePersistentStateString<"show" | "hide">(
|
|
138
|
+
clientStorage,
|
|
139
|
+
storageKey,
|
|
140
|
+
initialVisibility
|
|
141
|
+
);
|
|
142
|
+
const expanded = visibility === "show";
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<SectionInternal
|
|
146
|
+
{...props}
|
|
147
|
+
right={!props.hideRightWhenCollapsed || expanded ? props.right : null}
|
|
148
|
+
title={
|
|
149
|
+
<div className="flex gap-1 items-center">
|
|
150
|
+
{props.title}
|
|
151
|
+
<IconButton
|
|
152
|
+
iconName={expanded ? "ChevronDownIcon2" : "ChevronRightIcon2"}
|
|
153
|
+
onClick={() => setVisibility(expanded ? "hide" : "show")}
|
|
154
|
+
/>
|
|
155
|
+
</div>
|
|
156
|
+
}
|
|
157
|
+
>
|
|
158
|
+
{expanded && props.children}
|
|
159
|
+
</SectionInternal>
|
|
160
|
+
);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export const Section = memo(function InspectorSection({
|
|
164
|
+
storageKey,
|
|
165
|
+
...props
|
|
166
|
+
}: SectionProps & Partial<ExpandableSectionProps>) {
|
|
167
|
+
return storageKey ? (
|
|
168
|
+
<ExpandableSection {...props} storageKey={storageKey} />
|
|
169
|
+
) : (
|
|
170
|
+
<SectionInternal {...props} />
|
|
171
|
+
);
|
|
172
|
+
});
|
|
@@ -105,7 +105,7 @@ const SelectMenuTrigger = memoGeneric(function SelectMenuTrigger({
|
|
|
105
105
|
{icon && <span className="pr-1.5">{renderIcon(icon)}</span>}
|
|
106
106
|
{label && labelPosition === "inset" && (
|
|
107
107
|
<div className={insetEndStyles}>
|
|
108
|
-
<Label className="pr-4
|
|
108
|
+
<Label className="pr-4 text-text-disabled" htmlFor={id}>
|
|
109
109
|
{label}
|
|
110
110
|
</Label>
|
|
111
111
|
</div>
|
|
@@ -109,7 +109,7 @@ export const Slider = memo(function Slider({
|
|
|
109
109
|
<RadixSlider.Track className="flex-grow h-full rounded overflow-hidden bg-input-background">
|
|
110
110
|
{label && labelPosition === "inset" && (
|
|
111
111
|
<div className={insetEndStyles}>
|
|
112
|
-
<Label htmlFor={id} className="
|
|
112
|
+
<Label htmlFor={id} className="text-text-disabled">
|
|
113
113
|
{label}
|
|
114
114
|
</Label>
|
|
115
115
|
</div>
|