@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.
Files changed (118) hide show
  1. package/.eslintrc.js +1 -5
  2. package/.turbo/turbo-build.log +10 -10
  3. package/.turbo/turbo-lint.log +0 -1
  4. package/CHANGELOG.md +23 -0
  5. package/dist/index.css +1 -1
  6. package/dist/index.d.mts +299 -179
  7. package/dist/index.d.ts +299 -179
  8. package/dist/index.js +3772 -2999
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +3485 -2720
  11. package/dist/index.mjs.map +1 -1
  12. package/package.json +10 -22
  13. package/src/__tests__/validateDropIndicator.test.ts +26 -41
  14. package/src/__tests__/workspaceLayout.test.ts +281 -0
  15. package/src/components/ActionMenu.tsx +2 -0
  16. package/src/components/ActivityIndicator.tsx +5 -3
  17. package/src/components/AnimatePresence.tsx +6 -4
  18. package/src/components/Avatar.tsx +4 -2
  19. package/src/components/BaseToolbar.tsx +45 -34
  20. package/src/components/Breadcrumbs.tsx +2 -0
  21. package/src/components/Button.tsx +35 -21
  22. package/src/components/Chip.tsx +2 -0
  23. package/src/components/Collection.tsx +4 -1
  24. package/src/components/ColorSwatch.tsx +42 -22
  25. package/src/components/ColorSwatchControl.tsx +5 -3
  26. package/src/components/Combobox.tsx +2 -0
  27. package/src/components/CommandPalette.tsx +2 -0
  28. package/src/components/ContextMenu.tsx +3 -1
  29. package/src/components/Dialog.tsx +3 -1
  30. package/src/components/DimensionInput.tsx +3 -1
  31. package/src/components/Divider.tsx +10 -6
  32. package/src/components/DraggableMenuButton.tsx +22 -4
  33. package/src/components/Drawer.tsx +11 -3
  34. package/src/components/DropdownMenu.tsx +34 -6
  35. package/src/components/EditableText.tsx +2 -0
  36. package/src/components/FileUploadIndicator.tsx +108 -0
  37. package/src/components/FillPreviewBackground.tsx +2 -0
  38. package/src/components/FloatingWindow.tsx +2 -0
  39. package/src/components/GradientPicker.tsx +2 -0
  40. package/src/components/Grid.tsx +47 -38
  41. package/src/components/GridView.tsx +33 -11
  42. package/src/components/IconButton.tsx +4 -2
  43. package/src/components/Icons.tsx +1 -1
  44. package/src/components/InputField.tsx +7 -5
  45. package/src/components/Label.tsx +1 -3
  46. package/src/components/LabeledElementView.tsx +6 -2
  47. package/src/components/LabeledField.tsx +38 -34
  48. package/src/components/List.tsx +10 -2
  49. package/src/components/ListView.tsx +27 -14
  50. package/src/components/MediaThumbnail.tsx +31 -2
  51. package/src/components/Message.tsx +8 -9
  52. package/src/components/Popover.tsx +15 -4
  53. package/src/components/Progress.tsx +20 -7
  54. package/src/components/ScrollArea.tsx +3 -1
  55. package/src/components/SearchCompletionMenu.tsx +2 -0
  56. package/src/components/Section.tsx +2 -0
  57. package/src/components/SegmentedControl.tsx +43 -17
  58. package/src/components/SelectMenu.tsx +6 -4
  59. package/src/components/SelectionToolbar.tsx +31 -8
  60. package/src/components/Slider.tsx +4 -2
  61. package/src/components/Spacer.tsx +0 -1
  62. package/src/components/Switch.tsx +3 -1
  63. package/src/components/Tabs.tsx +48 -0
  64. package/src/components/Text.tsx +6 -5
  65. package/src/components/TextArea.tsx +2 -0
  66. package/src/components/Toast.tsx +1 -1
  67. package/src/components/Toolbar.tsx +113 -12
  68. package/src/components/ToolbarDrawer.tsx +29 -0
  69. package/src/components/Tooltip.tsx +9 -9
  70. package/src/components/TreeView.tsx +2 -0
  71. package/src/components/UserPointer.tsx +2 -0
  72. package/src/components/ai-assistant/AIAssistantLayout.tsx +2 -0
  73. package/src/components/blocks/ImageBlockComponent.tsx +156 -0
  74. package/src/components/connected-users-menu/ConnectedUsersMenuLayout.tsx +2 -0
  75. package/src/components/file-explorer/FileExplorerLayout.tsx +14 -6
  76. package/src/components/internal/Checkmark.tsx +24 -0
  77. package/src/components/internal/Menu.tsx +21 -3
  78. package/src/components/internal/MenuViewport.tsx +8 -5
  79. package/src/components/internal/SelectItem.tsx +4 -23
  80. package/src/components/internal/TextInput.tsx +2 -0
  81. package/src/components/sorting/DragRegistration.tsx +8 -4
  82. package/src/components/sorting/SharedDragProvider.tsx +43 -18
  83. package/src/components/sorting/Sortable.tsx +19 -10
  84. package/src/components/sorting/createSharedDrag.tsx +7 -6
  85. package/src/components/sorting/sorting.ts +6 -9
  86. package/src/components/workspace/DrawerWorkspaceLayout.tsx +113 -28
  87. package/src/components/workspace/PanelWorkspaceLayout.tsx +149 -16
  88. package/src/components/workspace/VerticalTabMenu.tsx +36 -0
  89. package/src/components/workspace/WorkspaceLayout.tsx +132 -47
  90. package/src/components/workspace/constants.ts +4 -0
  91. package/src/components/workspace/panelStorage.ts +216 -0
  92. package/src/components/workspace/renderPanelChildren.tsx +12 -0
  93. package/src/components/workspace/types.ts +30 -7
  94. package/src/contexts/DesignSystemConfiguration.tsx +12 -5
  95. package/src/contexts/DialogContext.tsx +2 -0
  96. package/src/contexts/FloatingWindowContext.tsx +2 -0
  97. package/src/contexts/GlobalInputBlurContext.tsx +2 -0
  98. package/src/contexts/ImageDataContext.tsx +2 -0
  99. package/src/contexts/LinkComponentContext.tsx +17 -0
  100. package/src/contexts/OpenPortalsContext.tsx +2 -0
  101. package/src/contexts/PortalScopeContext.tsx +2 -0
  102. package/src/hooks/mergeEventHandlers.ts +1 -1
  103. package/src/hooks/useHover.ts +30 -17
  104. package/src/hooks/useIndent.ts +2 -0
  105. package/src/hooks/useLabel.ts +12 -10
  106. package/src/hooks/useObjectURL.ts +5 -3
  107. package/src/hooks/usePlatform.ts +5 -3
  108. package/src/hooks/useTheme.ts +3 -1
  109. package/src/index.css +25 -4
  110. package/src/index.tsx +7 -2
  111. package/src/utils/classNames.ts +4 -3
  112. package/src/utils/combobox.ts +2 -0
  113. package/src/utils/editableBlockStyles.ts +10 -0
  114. package/src/utils/formatByteSize.ts +8 -0
  115. package/src/utils/withSeparatorElements.ts +1 -1
  116. package/src/hooks/usePreservePanelSize.tsx +0 -183
  117. package/src/hooks/useWindowSize.tsx +0 -26
  118. package/src/mediaQuery.ts +0 -13
@@ -1,4 +1,11 @@
1
- import React, { memo, useRef } from "react";
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
- usePreservePanelSize,
15
- } from "../../hooks/usePreservePanelSize";
16
- import { LayoutProps } from "./types";
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 = memo(function 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
- handleChangeLayoutState,
30
- }: LayoutProps) {
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
- usePreservePanelSize(panelGroupRef, handleChangeLayoutState);
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
- autoSaveId={
42
- autoSavePrefix
43
- ? `${autoSavePrefix}--${EDITOR_PANEL_GROUP_ID}`
44
- : undefined
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
- {internalLayoutState?.leftSidebarCollapsed ? null : leftPanel}
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
- {internalLayoutState?.rightSidebarCollapsed ? null : rightPanel}
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 { useSize } from "@noya-app/react-utils";
3
- import React, {
4
- forwardRef,
5
- memo,
6
- useCallback,
7
- useImperativeHandle,
8
- useRef,
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
- PanelLayoutState,
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?: React.ReactNode;
44
+ left?: RenderPanel<LeftTab>;
32
45
  leftOptions?: SideOptions;
33
46
  children?: React.ReactNode;
34
- right?: React.ReactNode;
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
- export const WorkspaceLayout = forwardRef(function WorkspaceLayout(
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 [internalLayoutState, setInternalLayoutState] =
156
- useState<PanelLayoutState | null>(null);
199
+ const [leftTabValue, setLeftTabValue] = useControlledOrUncontrolled<LeftTab>({
200
+ value: leftTabValueProp,
201
+ onChange: onChangeLeftTab,
202
+ defaultValue: getFirstTabValue(leftTabItems, "" as LeftTab),
203
+ });
157
204
 
158
- const handleChangeLayoutState = useCallback(
159
- (state: PanelLayoutState) => {
160
- setInternalLayoutState(state);
161
- onChangeLayoutState?.(state);
162
- },
163
- [onChangeLayoutState]
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
- minSize: leftSidebarMinPercentage,
257
- maxSize: leftSidebarMaxPercentage,
258
- initialSize: leftSidebarPercentage,
259
- resizable: leftResizable,
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
- minSize: rightSidebarMinPercentage,
264
- maxSize: rightSidebarMaxPercentage,
265
- initialSize: rightSidebarPercentage,
266
- resizable: rightResizable,
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
+ }
@@ -0,0 +1,4 @@
1
+ export const EDITOR_PANEL_GROUP_ID = "editor-panel-group";
2
+ export const LEFT_SIDEBAR_ID = "editor-1-left-sidebar";
3
+ export const RIGHT_SIDEBAR_ID = "editor-3-right-sidebar";
4
+ export const CONTENT_AREA_ID = "editor-2-content-area";