@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/.turbo/turbo-build.log +6 -6
- package/CHANGELOG.md +9 -0
- package/dist/index.css +1 -1
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +34 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +34 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/components/Toast.tsx +25 -18
- package/src/components/workspace/DrawerWorkspaceLayout.tsx +29 -1
- package/src/components/workspace/WorkspaceLayout.tsx +7 -0
- package/src/components/workspace/__tests__/DrawerWorkspaceLayout.test.tsx +34 -0
- package/src/components/workspace/types.ts +5 -0
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.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.
|
|
32
|
+
"@noya-app/noya-colorpicker": "0.1.35",
|
|
33
33
|
"@noya-app/noya-utils": "0.1.9",
|
|
34
|
-
"@noya-app/noya-geometry": "0.1.
|
|
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",
|
package/src/components/Toast.tsx
CHANGED
|
@@ -38,28 +38,35 @@ function ToastContent({ toast }: { toast: ToastObject }) {
|
|
|
38
38
|
return (
|
|
39
39
|
<ToastPrimitive.Root
|
|
40
40
|
toast={toast}
|
|
41
|
-
className="n-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
|
48
|
-
</ToastPrimitive.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
</
|
|
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={
|
|
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
|
*/
|