@noya-app/noya-designsystem 0.1.84 → 0.1.85

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noya-app/noya-designsystem",
3
- "version": "0.1.84",
3
+ "version": "0.1.85",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",
@@ -29,9 +29,9 @@
29
29
  "@dnd-kit/core": "6.3.1",
30
30
  "@dnd-kit/sortable": "10.0.0",
31
31
  "@floating-ui/react": "^0.27.0",
32
- "@noya-app/noya-colorpicker": "0.1.34",
32
+ "@noya-app/noya-colorpicker": "0.1.35",
33
33
  "@noya-app/noya-utils": "0.1.9",
34
- "@noya-app/noya-geometry": "0.1.17",
34
+ "@noya-app/noya-geometry": "0.1.18",
35
35
  "@noya-app/noya-icons": "0.1.19",
36
36
  "@noya-app/noya-keymap": "0.1.4",
37
37
  "@noya-app/noya-tailwind-config": "0.1.10",
@@ -38,28 +38,35 @@ function ToastContent({ toast }: { toast: ToastObject }) {
38
38
  return (
39
39
  <ToastPrimitive.Root
40
40
  toast={toast}
41
- className="n-px-2 n-py-2.5 n-grid n-grid-cols-[auto_max-content] n-gap-x-2.5 n-items-center n-rounded n-text-sm n-bg-popover-background n-shadow-popover n-text-text-muted"
41
+ className="n-relative n-flex n-items-start n-gap-2 n-rounded n-text-sm n-bg-popover-background n-shadow-popover n-text-text-muted n-px-3 n-py-2.5 n-pr-10"
42
42
  >
43
- {toast.data?.title && (
44
- <ToastPrimitive.Title
45
- className={`n-mb-[5px] ${textStyles.label} n-font-bold n-text-text`}
43
+ <div className="n-flex n-flex-col n-gap-0.5 n-min-w-0">
44
+ {toast.data?.title && (
45
+ <ToastPrimitive.Title
46
+ className={`${textStyles.label} n-font-bold n-text-text`}
47
+ >
48
+ {toast.data.title}
49
+ </ToastPrimitive.Title>
50
+ )}
51
+ <ToastPrimitive.Description
52
+ className={`${textStyles.small} n-text-text-muted`}
46
53
  >
47
- {toast.data.title}
48
- </ToastPrimitive.Title>
49
- )}
50
- <ToastPrimitive.Description
51
- className={`${textStyles.small} n-text-text-muted`}
52
- >
53
- {toast.data?.content}
54
- </ToastPrimitive.Description>
55
- {toast.data?.action && (
56
- <ToastPrimitive.Action render={<span />}>
57
- {toast.data.action}
58
- </ToastPrimitive.Action>
59
- )}
54
+ {toast.data?.content}
55
+ </ToastPrimitive.Description>
56
+ {toast.data?.action && (
57
+ <ToastPrimitive.Action render={<span className="n-mt-1" />}>
58
+ {toast.data.action}
59
+ </ToastPrimitive.Action>
60
+ )}
61
+ </div>
60
62
  <ToastPrimitive.Close
61
63
  aria-label="Close"
62
- render={<IconButton iconName="Cross1Icon" />}
64
+ render={
65
+ <IconButton
66
+ className="!n-absolute n-top-2 n-right-2"
67
+ iconName="Cross1Icon"
68
+ />
69
+ }
63
70
  />
64
71
  </ToastPrimitive.Root>
65
72
  );
@@ -3,6 +3,7 @@
3
3
  import { memoGeneric } from "@noya-app/react-utils";
4
4
  import React, { useMemo, useState } from "react";
5
5
  import { cssVarNames } from "../../theme";
6
+ import { Button } from "../Button";
6
7
  import { Divider } from "../Divider";
7
8
  import { isSelectableMenuItem, MenuItem } from "../internal/Menu";
8
9
  import { ToolbarMenu } from "../Toolbar";
@@ -24,6 +25,7 @@ export const DrawerWorkspaceLayout = memoGeneric(function DrawerWorkspaceLayout<
24
25
  rightTabValue,
25
26
  onChangeRightTab,
26
27
  onChangeLeftTab,
28
+ compactDrawerMenu,
27
29
  }: LayoutProps<LeftTab, RightTab>) {
28
30
  const [{ leftSidebarCollapsed, rightSidebarCollapsed }, setLayoutState] =
29
31
  useState<PanelLayoutState>({
@@ -79,6 +81,10 @@ export const DrawerWorkspaceLayout = memoGeneric(function DrawerWorkspaceLayout<
79
81
  );
80
82
 
81
83
  const hasTabs = allSelectableTabItems.length > 0;
84
+ const compactTabItem =
85
+ compactDrawerMenu && allSelectableTabItems.length === 1
86
+ ? allSelectableTabItems[0]
87
+ : undefined;
82
88
 
83
89
  const leftSelectableTabItems = useMemo(
84
90
  () => (leftTabItems ?? []).filter(isSelectableMenuItem),
@@ -157,7 +163,7 @@ export const DrawerWorkspaceLayout = memoGeneric(function DrawerWorkspaceLayout<
157
163
  className="n-flex n-flex-col n-flex-1 n-relative focus:n-outline-none"
158
164
  >
159
165
  {/* Horizontal toggle bar - secondary toolbar row */}
160
- {hasTabs && (
166
+ {hasTabs && !compactTabItem && (
161
167
  <>
162
168
  <div
163
169
  className="n-flex n-flex-row n-items-center n-gap-1 n-px-2 n-py-1.5 n-bg-sidebar-background n-flex-none"
@@ -175,6 +181,28 @@ export const DrawerWorkspaceLayout = memoGeneric(function DrawerWorkspaceLayout<
175
181
  </>
176
182
  )}
177
183
 
184
+ {compactTabItem && (
185
+ <div className="n-absolute n-top-0 n-left-0 n-h-[calc(var(--n-toolbar-height)+1px)] n-w-[calc(var(--n-toolbar-height)+1px)] n-bg-sidebar-background n-border-r n-border-b n-border-divider-strong n-z-10 n-flex n-items-center n-justify-center">
186
+ <Button
187
+ aria-label={
188
+ typeof compactTabItem.title === "string"
189
+ ? compactTabItem.title
190
+ : "Toggle sidebar"
191
+ }
192
+ variant="none"
193
+ className="n-rounded"
194
+ style={{
195
+ width: "var(--n-input-height)",
196
+ height: "var(--n-input-height)",
197
+ }}
198
+ icon={compactTabItem.icon}
199
+ tooltip={compactTabItem.tooltip ?? compactTabItem.title}
200
+ active={activeTabValue === compactTabItem.value}
201
+ onClick={() => handleSelectTab(compactTabItem.value)}
202
+ />
203
+ </div>
204
+ )}
205
+
178
206
  {/* Main content area with drawer overlay */}
179
207
  <div className="n-flex-1 n-flex n-flex-col n-relative n-min-h-0">
180
208
  {/* Drawer panel - positioned absolutely over content */}
@@ -79,6 +79,11 @@ export interface WorkspaceLayoutProps<
79
79
  sideType?: SideType;
80
80
  sideTypeBreakpoint?: number;
81
81
  detectSize?: DetectSizeType;
82
+ /**
83
+ * Render a single drawer tab as an overlaid toolbar button instead of a
84
+ * dedicated toolbar row.
85
+ */
86
+ compactDrawerMenu?: boolean;
82
87
  /**
83
88
  * Callback when left sidebar state changes (width or collapsed).
84
89
  */
@@ -164,6 +169,7 @@ export const WorkspaceLayout = forwardRefGeneric(function WorkspaceLayout<
164
169
  sideType = "auto",
165
170
  sideTypeBreakpoint = 800,
166
171
  detectSize = "container",
172
+ compactDrawerMenu,
167
173
  leftOptions: {
168
174
  visible: leftVisible = true,
169
175
  minSize: leftMinSize = 200,
@@ -462,6 +468,7 @@ export const WorkspaceLayout = forwardRefGeneric(function WorkspaceLayout<
462
468
  autoSavePrefix={autoSavePrefix}
463
469
  leftSidebarRef={leftSidebarRef}
464
470
  rightSidebarRef={rightSidebarRef}
471
+ compactDrawerMenu={compactDrawerMenu}
465
472
  onLeftSidebarStateChange={onLeftSidebarStateChange}
466
473
  onRightSidebarStateChange={onRightSidebarStateChange}
467
474
  shouldAutoSave={shouldAutoSave}
@@ -0,0 +1,34 @@
1
+ import { afterEach, describe, expect, test } from "bun:test";
2
+ import React from "react";
3
+ import { cleanup, fireEvent, render } from "@testing-library/react";
4
+ import { DrawerWorkspaceLayout } from "../DrawerWorkspaceLayout";
5
+ import type { SidebarRef } from "../types";
6
+
7
+ afterEach(cleanup);
8
+
9
+ describe("DrawerWorkspaceLayout", () => {
10
+ test("renders a single compact tab without a dedicated toolbar row", () => {
11
+ const leftSidebarRef = React.createRef<SidebarRef>();
12
+ const rightSidebarRef = React.createRef<SidebarRef>();
13
+ const { container, getByRole, getByText } = render(
14
+ <DrawerWorkspaceLayout
15
+ leftPanel={<div>Sidebar content</div>}
16
+ leftTabItems={[{ title: "Navigation", value: "navigation" }]}
17
+ leftTabValue="navigation"
18
+ rightPanel={null}
19
+ centerPanel={<div>Center content</div>}
20
+ leftSidebarOptions={{}}
21
+ rightSidebarOptions={{}}
22
+ leftSidebarRef={leftSidebarRef}
23
+ rightSidebarRef={rightSidebarRef}
24
+ compactDrawerMenu
25
+ />
26
+ );
27
+
28
+ expect(container.textContent).toBe("Center content");
29
+
30
+ fireEvent.click(getByRole("button", { name: "Navigation" }));
31
+
32
+ expect(getByText("Sidebar content")).toBeTruthy();
33
+ });
34
+ });
@@ -58,6 +58,11 @@ export type LayoutProps<
58
58
  autoSavePrefix?: string;
59
59
  leftSidebarRef: React.RefObject<SidebarRef | null>;
60
60
  rightSidebarRef: React.RefObject<SidebarRef | null>;
61
+ /**
62
+ * Render a single drawer tab as an overlaid toolbar button instead of a
63
+ * dedicated toolbar row.
64
+ */
65
+ compactDrawerMenu?: boolean;
61
66
  /**
62
67
  * Callback when left sidebar state changes (width or collapsed).
63
68
  */