@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,4 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
clientStorage,
|
|
5
|
+
memoGeneric,
|
|
6
|
+
usePersistentStateObject,
|
|
7
|
+
} from "@noya-app/react-utils";
|
|
8
|
+
import React, { useMemo, useRef } from "react";
|
|
2
9
|
import {
|
|
3
10
|
ImperativePanelGroupHandle,
|
|
4
11
|
ImperativePanelHandle,
|
|
@@ -6,16 +13,30 @@ import {
|
|
|
6
13
|
PanelGroup,
|
|
7
14
|
PanelResizeHandle,
|
|
8
15
|
} from "react-resizable-panels";
|
|
16
|
+
import { DividerVertical } from "../Divider";
|
|
9
17
|
import {
|
|
10
18
|
CONTENT_AREA_ID,
|
|
11
19
|
EDITOR_PANEL_GROUP_ID,
|
|
12
20
|
LEFT_SIDEBAR_ID,
|
|
13
21
|
RIGHT_SIDEBAR_ID,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
22
|
+
} from "./constants";
|
|
23
|
+
import {
|
|
24
|
+
getInitialLayout,
|
|
25
|
+
getLayoutCollapsedState,
|
|
26
|
+
getLayoutIds,
|
|
27
|
+
getPropertyKey,
|
|
28
|
+
getStorageKey,
|
|
29
|
+
panelStorage,
|
|
30
|
+
setInitialLayout,
|
|
31
|
+
toPixelSize,
|
|
32
|
+
} from "./panelStorage";
|
|
33
|
+
import { LayoutProps, WorkspaceLayoutState } from "./types";
|
|
34
|
+
import { VerticalTabMenu } from "./VerticalTabMenu";
|
|
17
35
|
|
|
18
|
-
export const PanelWorkspaceLayout =
|
|
36
|
+
export const PanelWorkspaceLayout = memoGeneric(function PanelWorkspaceLayout<
|
|
37
|
+
LeftTab extends string,
|
|
38
|
+
RightTab extends string,
|
|
39
|
+
>({
|
|
19
40
|
leftPanel,
|
|
20
41
|
rightPanel,
|
|
21
42
|
centerPanel,
|
|
@@ -23,14 +44,67 @@ export const PanelWorkspaceLayout = memo(function PanelWorkspaceLayout({
|
|
|
23
44
|
rightSidebarOptions,
|
|
24
45
|
centerPanelPercentage,
|
|
25
46
|
autoSavePrefix,
|
|
26
|
-
internalLayoutState,
|
|
27
47
|
leftSidebarRef,
|
|
28
48
|
rightSidebarRef,
|
|
29
|
-
|
|
30
|
-
|
|
49
|
+
leftTabItems,
|
|
50
|
+
rightTabItems,
|
|
51
|
+
leftTabValue,
|
|
52
|
+
rightTabValue,
|
|
53
|
+
onChangeLeftTab,
|
|
54
|
+
onChangeRightTab,
|
|
55
|
+
}: LayoutProps<LeftTab, RightTab>) {
|
|
31
56
|
const panelGroupRef = useRef<ImperativePanelGroupHandle>(null);
|
|
32
57
|
|
|
33
|
-
|
|
58
|
+
const hasLeftPanel = !!leftPanel;
|
|
59
|
+
const hasRightPanel = !!rightPanel;
|
|
60
|
+
const hasCenterPanel = !!centerPanel;
|
|
61
|
+
|
|
62
|
+
const autoSaveId = useMemo(() => {
|
|
63
|
+
return autoSavePrefix
|
|
64
|
+
? `${autoSavePrefix}--v2--${EDITOR_PANEL_GROUP_ID}`
|
|
65
|
+
: Math.random().toString(36).substring(2, 15);
|
|
66
|
+
}, [autoSavePrefix]);
|
|
67
|
+
|
|
68
|
+
const layoutIds = getLayoutIds({
|
|
69
|
+
hasLeftPanel,
|
|
70
|
+
hasRightPanel,
|
|
71
|
+
hasCenterPanel,
|
|
72
|
+
});
|
|
73
|
+
const storageKey = getStorageKey(autoSaveId);
|
|
74
|
+
const propertyKey = getPropertyKey(layoutIds);
|
|
75
|
+
|
|
76
|
+
setInitialLayout({
|
|
77
|
+
storageKey,
|
|
78
|
+
propertyKey,
|
|
79
|
+
calculateLayout: () => {
|
|
80
|
+
return getInitialLayout({
|
|
81
|
+
layoutIds,
|
|
82
|
+
leftSidebarOptions,
|
|
83
|
+
rightSidebarOptions,
|
|
84
|
+
centerPanelPercentage,
|
|
85
|
+
hasLeftPanel,
|
|
86
|
+
hasRightPanel,
|
|
87
|
+
hasCenterPanel,
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const [data, setData] = usePersistentStateObject<
|
|
93
|
+
WorkspaceLayoutState,
|
|
94
|
+
string
|
|
95
|
+
>(clientStorage, storageKey, {});
|
|
96
|
+
|
|
97
|
+
const layoutGroup = data?.[propertyKey];
|
|
98
|
+
|
|
99
|
+
const { leftSidebarCollapsed, rightSidebarCollapsed } = useMemo(() => {
|
|
100
|
+
return getLayoutCollapsedState({
|
|
101
|
+
layoutGroup,
|
|
102
|
+
propertyKey,
|
|
103
|
+
layoutIds,
|
|
104
|
+
hasLeftPanel,
|
|
105
|
+
hasRightPanel,
|
|
106
|
+
});
|
|
107
|
+
}, [layoutGroup, propertyKey, layoutIds, hasLeftPanel, hasRightPanel]);
|
|
34
108
|
|
|
35
109
|
return (
|
|
36
110
|
<PanelGroup
|
|
@@ -38,14 +112,48 @@ export const PanelWorkspaceLayout = memo(function PanelWorkspaceLayout({
|
|
|
38
112
|
id={EDITOR_PANEL_GROUP_ID}
|
|
39
113
|
className="flex-1"
|
|
40
114
|
direction="horizontal"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
115
|
+
storage={panelStorage}
|
|
116
|
+
autoSaveId={autoSaveId}
|
|
117
|
+
onLayout={(layout) => {
|
|
118
|
+
setData((prev) => {
|
|
119
|
+
const newData = {
|
|
120
|
+
...prev,
|
|
121
|
+
[propertyKey]: {
|
|
122
|
+
layout: layout.map((size) => toPixelSize(size)),
|
|
123
|
+
expandToSizes: prev?.[propertyKey]?.expandToSizes ?? {},
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
return newData;
|
|
127
|
+
});
|
|
128
|
+
}}
|
|
46
129
|
>
|
|
47
130
|
{leftPanel && (
|
|
48
131
|
<>
|
|
132
|
+
{leftSidebarOptions.showTabs && leftTabItems && (
|
|
133
|
+
<VerticalTabMenu
|
|
134
|
+
tooltipSide="right"
|
|
135
|
+
items={leftTabItems}
|
|
136
|
+
activeValue={leftTabValue}
|
|
137
|
+
isSidebarCollapsed={leftSidebarCollapsed}
|
|
138
|
+
onChange={(value) => {
|
|
139
|
+
if (value === leftTabValue) {
|
|
140
|
+
if (leftSidebarRef.current?.isExpanded()) {
|
|
141
|
+
leftSidebarRef.current?.collapse();
|
|
142
|
+
} else {
|
|
143
|
+
leftSidebarRef.current?.expand();
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
if (!leftSidebarRef.current?.isExpanded()) {
|
|
147
|
+
leftSidebarRef.current?.expand();
|
|
148
|
+
}
|
|
149
|
+
onChangeLeftTab?.(value);
|
|
150
|
+
}
|
|
151
|
+
}}
|
|
152
|
+
/>
|
|
153
|
+
)}
|
|
154
|
+
{leftSidebarOptions.showTabs &&
|
|
155
|
+
leftTabItems &&
|
|
156
|
+
!leftSidebarCollapsed && <DividerVertical className="h-full" />}
|
|
49
157
|
<Panel
|
|
50
158
|
id={LEFT_SIDEBAR_ID}
|
|
51
159
|
className="relative"
|
|
@@ -61,7 +169,7 @@ export const PanelWorkspaceLayout = memo(function PanelWorkspaceLayout({
|
|
|
61
169
|
}
|
|
62
170
|
collapsible
|
|
63
171
|
>
|
|
64
|
-
{
|
|
172
|
+
{leftSidebarCollapsed ? null : leftPanel}
|
|
65
173
|
</Panel>
|
|
66
174
|
<PanelResizeHandle className="cursor-col-resize w-px h-full bg-divider" />
|
|
67
175
|
</>
|
|
@@ -93,8 +201,33 @@ export const PanelWorkspaceLayout = memo(function PanelWorkspaceLayout({
|
|
|
93
201
|
}
|
|
94
202
|
collapsible
|
|
95
203
|
>
|
|
96
|
-
{
|
|
204
|
+
{rightSidebarCollapsed ? null : rightPanel}
|
|
97
205
|
</Panel>
|
|
206
|
+
{rightSidebarOptions.showTabs &&
|
|
207
|
+
rightTabItems &&
|
|
208
|
+
!rightSidebarCollapsed && <DividerVertical className="h-full" />}
|
|
209
|
+
{rightSidebarOptions.showTabs && rightTabItems && (
|
|
210
|
+
<VerticalTabMenu
|
|
211
|
+
tooltipSide="left"
|
|
212
|
+
items={rightTabItems}
|
|
213
|
+
activeValue={rightTabValue}
|
|
214
|
+
isSidebarCollapsed={rightSidebarCollapsed}
|
|
215
|
+
onChange={(value) => {
|
|
216
|
+
if (value === rightTabValue) {
|
|
217
|
+
if (rightSidebarRef.current?.isExpanded()) {
|
|
218
|
+
rightSidebarRef.current?.collapse();
|
|
219
|
+
} else {
|
|
220
|
+
rightSidebarRef.current?.expand();
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
if (!rightSidebarRef.current?.isExpanded()) {
|
|
224
|
+
rightSidebarRef.current?.expand();
|
|
225
|
+
}
|
|
226
|
+
onChangeRightTab?.(value);
|
|
227
|
+
}
|
|
228
|
+
}}
|
|
229
|
+
/>
|
|
230
|
+
)}
|
|
98
231
|
</>
|
|
99
232
|
)}
|
|
100
233
|
</PanelGroup>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React, { ComponentProps } from "react";
|
|
2
|
+
import { cssVarNames } from "../../theme";
|
|
3
|
+
import { MenuItem } from "../internal/Menu";
|
|
4
|
+
import { ToolbarMenu } from "../Toolbar";
|
|
5
|
+
import { Tooltip } from "../Tooltip";
|
|
6
|
+
|
|
7
|
+
export function VerticalTabMenu<TabValue extends string>({
|
|
8
|
+
items,
|
|
9
|
+
activeValue,
|
|
10
|
+
isSidebarCollapsed,
|
|
11
|
+
onChange,
|
|
12
|
+
tooltipSide,
|
|
13
|
+
}: {
|
|
14
|
+
items: MenuItem<TabValue>[];
|
|
15
|
+
activeValue: TabValue | undefined;
|
|
16
|
+
isSidebarCollapsed: boolean;
|
|
17
|
+
onChange: (value: TabValue) => void;
|
|
18
|
+
tooltipSide?: ComponentProps<typeof Tooltip>["side"];
|
|
19
|
+
}) {
|
|
20
|
+
return (
|
|
21
|
+
<div
|
|
22
|
+
className="w-[47px] h-full bg-sidebar-background flex flex-col items-center py-3 gap-3"
|
|
23
|
+
style={{
|
|
24
|
+
[cssVarNames.colors.inputBackground]: "transparent",
|
|
25
|
+
[cssVarNames.colors.buttonBackground]: "transparent",
|
|
26
|
+
}}
|
|
27
|
+
>
|
|
28
|
+
<ToolbarMenu
|
|
29
|
+
items={items}
|
|
30
|
+
activeValue={isSidebarCollapsed ? undefined : activeValue}
|
|
31
|
+
onSelectMenuItem={onChange}
|
|
32
|
+
tooltipSide={tooltipSide}
|
|
33
|
+
/>
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -1,20 +1,22 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { useKeyboardShortcuts } from "@noya-app/noya-keymap";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
useState,
|
|
10
|
-
} from "react";
|
|
11
|
-
import { useWindowSize } from "../../hooks/useWindowSize";
|
|
4
|
+
import {
|
|
5
|
+
forwardRefGeneric,
|
|
6
|
+
useControlledOrUncontrolled,
|
|
7
|
+
useSize,
|
|
8
|
+
useWindowSize,
|
|
9
|
+
} from "@noya-app/react-utils";
|
|
10
|
+
import React, { memo, useImperativeHandle, useMemo, useRef } from "react";
|
|
12
11
|
import { cx } from "../../utils/classNames";
|
|
12
|
+
import { MenuItem } from "../internal/Menu";
|
|
13
13
|
import { DrawerWorkspaceLayout } from "./DrawerWorkspaceLayout";
|
|
14
14
|
import { PanelWorkspaceLayout } from "./PanelWorkspaceLayout";
|
|
15
|
+
import { renderPanelChildren } from "./renderPanelChildren";
|
|
15
16
|
import {
|
|
16
|
-
|
|
17
|
+
LayoutProps,
|
|
17
18
|
PercentageSidebarOptions,
|
|
19
|
+
RenderPanel,
|
|
18
20
|
SidebarRef,
|
|
19
21
|
} from "./types";
|
|
20
22
|
|
|
@@ -22,22 +24,32 @@ export type SideType = "auto" | "panel" | "drawer";
|
|
|
22
24
|
|
|
23
25
|
export type DetectSizeType = "container" | "window";
|
|
24
26
|
|
|
25
|
-
export interface WorkspaceLayoutProps
|
|
27
|
+
export interface WorkspaceLayoutProps<
|
|
28
|
+
LeftTab extends string,
|
|
29
|
+
RightTab extends string,
|
|
30
|
+
> extends Pick<
|
|
31
|
+
LayoutProps<LeftTab, RightTab>,
|
|
32
|
+
| "leftTabItems"
|
|
33
|
+
| "leftTabValue"
|
|
34
|
+
| "rightTabItems"
|
|
35
|
+
| "rightTabValue"
|
|
36
|
+
| "onChangeLeftTab"
|
|
37
|
+
| "onChangeRightTab"
|
|
38
|
+
> {
|
|
26
39
|
id?: string;
|
|
27
40
|
className?: string;
|
|
28
41
|
style?: React.CSSProperties;
|
|
29
42
|
theme?: "light" | "dark";
|
|
30
43
|
autoSavePrefix?: string;
|
|
31
|
-
left?:
|
|
44
|
+
left?: RenderPanel<LeftTab>;
|
|
32
45
|
leftOptions?: SideOptions;
|
|
33
46
|
children?: React.ReactNode;
|
|
34
|
-
right?:
|
|
47
|
+
right?: RenderPanel<RightTab>;
|
|
35
48
|
rightOptions?: SideOptions;
|
|
36
49
|
toolbar?: React.ReactNode;
|
|
37
50
|
sideType?: SideType;
|
|
38
51
|
sideTypeBreakpoint?: number;
|
|
39
52
|
detectSize?: DetectSizeType;
|
|
40
|
-
onChangeLayoutState?: (layoutState: PanelLayoutState) => void;
|
|
41
53
|
}
|
|
42
54
|
|
|
43
55
|
export type WorkspaceLayoutRef = {
|
|
@@ -59,19 +71,47 @@ export type SideOptions = {
|
|
|
59
71
|
initialSize?: number | string;
|
|
60
72
|
minSize?: number | string;
|
|
61
73
|
maxSize?: number | string;
|
|
74
|
+
defaultCollapsed?: boolean;
|
|
75
|
+
showTabs?: boolean;
|
|
62
76
|
};
|
|
63
77
|
|
|
64
|
-
|
|
78
|
+
const defaultLeftTabItems: MenuItem<string>[] = [
|
|
79
|
+
{
|
|
80
|
+
title: "",
|
|
81
|
+
value: "defaultLeft",
|
|
82
|
+
icon: "BorderLeftIcon",
|
|
83
|
+
tooltip: "Left Sidebar",
|
|
84
|
+
},
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
const defaultRightTabItems: MenuItem<string>[] = [
|
|
88
|
+
{
|
|
89
|
+
title: "",
|
|
90
|
+
value: "defaultRight",
|
|
91
|
+
icon: "BorderRightIcon",
|
|
92
|
+
tooltip: "Right Sidebar",
|
|
93
|
+
},
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
export const WorkspaceLayout = forwardRefGeneric(function WorkspaceLayout<
|
|
97
|
+
LeftTab extends string = string,
|
|
98
|
+
RightTab extends string = string,
|
|
99
|
+
>(
|
|
65
100
|
{
|
|
66
101
|
autoSavePrefix,
|
|
67
102
|
left,
|
|
103
|
+
leftTabItems = defaultLeftTabItems as MenuItem<LeftTab>[],
|
|
104
|
+
leftTabValue: leftTabValueProp,
|
|
105
|
+
onChangeLeftTab,
|
|
68
106
|
right,
|
|
107
|
+
rightTabItems = defaultRightTabItems as MenuItem<RightTab>[],
|
|
108
|
+
rightTabValue: rightTabValueProp,
|
|
109
|
+
onChangeRightTab,
|
|
69
110
|
id,
|
|
70
111
|
className,
|
|
71
112
|
style,
|
|
72
113
|
toolbar,
|
|
73
114
|
children,
|
|
74
|
-
onChangeLayoutState,
|
|
75
115
|
sideType = "auto",
|
|
76
116
|
sideTypeBreakpoint = 800,
|
|
77
117
|
detectSize = "container",
|
|
@@ -82,6 +122,8 @@ export const WorkspaceLayout = forwardRef(function WorkspaceLayout(
|
|
|
82
122
|
resizable: leftResizable = true,
|
|
83
123
|
style: leftStyle,
|
|
84
124
|
className: leftClassName,
|
|
125
|
+
defaultCollapsed: leftDefaultCollapsed = false,
|
|
126
|
+
showTabs: leftShowTabs = true,
|
|
85
127
|
} = {},
|
|
86
128
|
rightOptions: {
|
|
87
129
|
minSize: rightMinSize = 200,
|
|
@@ -90,9 +132,11 @@ export const WorkspaceLayout = forwardRef(function WorkspaceLayout(
|
|
|
90
132
|
resizable: rightResizable = true,
|
|
91
133
|
style: rightStyle,
|
|
92
134
|
className: rightClassName,
|
|
135
|
+
defaultCollapsed: rightDefaultCollapsed = false,
|
|
136
|
+
showTabs: rightShowTabs = true,
|
|
93
137
|
} = {},
|
|
94
138
|
theme,
|
|
95
|
-
}: WorkspaceLayoutProps,
|
|
139
|
+
}: WorkspaceLayoutProps<LeftTab, RightTab>,
|
|
96
140
|
forwardedRef: React.ForwardedRef<WorkspaceLayoutRef>
|
|
97
141
|
) {
|
|
98
142
|
const containerRef = React.useRef<HTMLDivElement | null>(null);
|
|
@@ -152,16 +196,18 @@ export const WorkspaceLayout = forwardRef(function WorkspaceLayout(
|
|
|
152
196
|
const leftSidebarRef = useRef<SidebarRef>(null);
|
|
153
197
|
const rightSidebarRef = useRef<SidebarRef>(null);
|
|
154
198
|
|
|
155
|
-
const [
|
|
156
|
-
|
|
199
|
+
const [leftTabValue, setLeftTabValue] = useControlledOrUncontrolled<LeftTab>({
|
|
200
|
+
value: leftTabValueProp,
|
|
201
|
+
onChange: onChangeLeftTab,
|
|
202
|
+
defaultValue: getFirstTabValue(leftTabItems, "" as LeftTab),
|
|
203
|
+
});
|
|
157
204
|
|
|
158
|
-
const
|
|
159
|
-
(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
);
|
|
205
|
+
const [rightTabValue, setRightTabValue] =
|
|
206
|
+
useControlledOrUncontrolled<RightTab>({
|
|
207
|
+
value: rightTabValueProp,
|
|
208
|
+
onChange: onChangeRightTab,
|
|
209
|
+
defaultValue: getFirstTabValue(rightTabItems, "" as RightTab),
|
|
210
|
+
});
|
|
165
211
|
|
|
166
212
|
useImperativeHandle(forwardedRef, () => ({
|
|
167
213
|
setLeftExpanded: (expanded: boolean) => {
|
|
@@ -235,7 +281,7 @@ export const WorkspaceLayout = forwardRef(function WorkspaceLayout(
|
|
|
235
281
|
style={leftStyle}
|
|
236
282
|
className={cx("bg-sidebar-background flex-col", leftClassName)}
|
|
237
283
|
>
|
|
238
|
-
{left}
|
|
284
|
+
{leftTabValue ? renderPanelChildren(left, leftTabValue) : null}
|
|
239
285
|
</PanelInner>
|
|
240
286
|
) : null;
|
|
241
287
|
|
|
@@ -244,7 +290,7 @@ export const WorkspaceLayout = forwardRef(function WorkspaceLayout(
|
|
|
244
290
|
style={rightStyle}
|
|
245
291
|
className={cx("bg-sidebar-background flex-col", rightClassName)}
|
|
246
292
|
>
|
|
247
|
-
{right}
|
|
293
|
+
{rightTabValue ? renderPanelChildren(right, rightTabValue) : null}
|
|
248
294
|
</PanelInner>
|
|
249
295
|
) : null;
|
|
250
296
|
|
|
@@ -252,28 +298,52 @@ export const WorkspaceLayout = forwardRef(function WorkspaceLayout(
|
|
|
252
298
|
<PanelInner className="bg-canvas-background">{children}</PanelInner>
|
|
253
299
|
);
|
|
254
300
|
|
|
255
|
-
const leftSidebarOptions: PercentageSidebarOptions =
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
301
|
+
const leftSidebarOptions: PercentageSidebarOptions = useMemo(
|
|
302
|
+
() => ({
|
|
303
|
+
minSize: leftSidebarMinPercentage,
|
|
304
|
+
maxSize: leftSidebarMaxPercentage,
|
|
305
|
+
initialSize: leftSidebarPercentage,
|
|
306
|
+
resizable: leftResizable,
|
|
307
|
+
defaultCollapsed: leftDefaultCollapsed,
|
|
308
|
+
showTabs: leftShowTabs,
|
|
309
|
+
}),
|
|
310
|
+
[
|
|
311
|
+
leftSidebarMinPercentage,
|
|
312
|
+
leftSidebarMaxPercentage,
|
|
313
|
+
leftSidebarPercentage,
|
|
314
|
+
leftResizable,
|
|
315
|
+
leftDefaultCollapsed,
|
|
316
|
+
leftShowTabs,
|
|
317
|
+
]
|
|
318
|
+
);
|
|
261
319
|
|
|
262
|
-
const rightSidebarOptions: PercentageSidebarOptions =
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
320
|
+
const rightSidebarOptions: PercentageSidebarOptions = useMemo(
|
|
321
|
+
() => ({
|
|
322
|
+
minSize: rightSidebarMinPercentage,
|
|
323
|
+
maxSize: rightSidebarMaxPercentage,
|
|
324
|
+
initialSize: rightSidebarPercentage,
|
|
325
|
+
resizable: rightResizable,
|
|
326
|
+
defaultCollapsed: rightDefaultCollapsed,
|
|
327
|
+
showTabs: rightShowTabs,
|
|
328
|
+
}),
|
|
329
|
+
[
|
|
330
|
+
rightSidebarMinPercentage,
|
|
331
|
+
rightSidebarMaxPercentage,
|
|
332
|
+
rightSidebarPercentage,
|
|
333
|
+
rightResizable,
|
|
334
|
+
rightDefaultCollapsed,
|
|
335
|
+
rightShowTabs,
|
|
336
|
+
]
|
|
337
|
+
);
|
|
268
338
|
|
|
269
339
|
const LayoutComponent =
|
|
270
340
|
sideType === "panel"
|
|
271
|
-
? PanelWorkspaceLayout
|
|
341
|
+
? PanelWorkspaceLayout<LeftTab, RightTab>
|
|
272
342
|
: sideType === "drawer"
|
|
273
|
-
? DrawerWorkspaceLayout
|
|
343
|
+
? DrawerWorkspaceLayout<LeftTab, RightTab>
|
|
274
344
|
: parentSize.width > sideTypeBreakpoint
|
|
275
|
-
? PanelWorkspaceLayout
|
|
276
|
-
: DrawerWorkspaceLayout
|
|
345
|
+
? PanelWorkspaceLayout<LeftTab, RightTab>
|
|
346
|
+
: DrawerWorkspaceLayout<LeftTab, RightTab>;
|
|
277
347
|
|
|
278
348
|
// Wait until the container size is available to render the layout.
|
|
279
349
|
// This is to avoid flickering when the layout is initially rendered.
|
|
@@ -300,14 +370,18 @@ export const WorkspaceLayout = forwardRef(function WorkspaceLayout(
|
|
|
300
370
|
leftPanel={leftPanel}
|
|
301
371
|
rightPanel={rightPanel}
|
|
302
372
|
centerPanel={centerPanel}
|
|
373
|
+
leftTabItems={leftTabItems}
|
|
374
|
+
leftTabValue={leftTabValue}
|
|
375
|
+
onChangeLeftTab={setLeftTabValue}
|
|
376
|
+
rightTabItems={rightTabItems}
|
|
377
|
+
rightTabValue={rightTabValue}
|
|
378
|
+
onChangeRightTab={setRightTabValue}
|
|
303
379
|
leftSidebarOptions={leftSidebarOptions}
|
|
304
380
|
rightSidebarOptions={rightSidebarOptions}
|
|
305
381
|
centerPanelPercentage={centerPanelPercentage}
|
|
306
382
|
autoSavePrefix={autoSavePrefix}
|
|
307
|
-
internalLayoutState={internalLayoutState}
|
|
308
383
|
leftSidebarRef={leftSidebarRef}
|
|
309
384
|
rightSidebarRef={rightSidebarRef}
|
|
310
|
-
handleChangeLayoutState={handleChangeLayoutState}
|
|
311
385
|
/>
|
|
312
386
|
)}
|
|
313
387
|
</div>
|
|
@@ -330,3 +404,14 @@ const PanelInner = memo(function PanelInner({
|
|
|
330
404
|
</div>
|
|
331
405
|
);
|
|
332
406
|
});
|
|
407
|
+
|
|
408
|
+
function getFirstTabValue<TabValue extends string>(
|
|
409
|
+
items: MenuItem<TabValue>[],
|
|
410
|
+
defaultValue: TabValue
|
|
411
|
+
): TabValue {
|
|
412
|
+
const firstItem = items.at(0);
|
|
413
|
+
|
|
414
|
+
return firstItem?.type === undefined
|
|
415
|
+
? firstItem?.value ?? defaultValue
|
|
416
|
+
: defaultValue;
|
|
417
|
+
}
|