@linzjs/windows 1.2.0 → 1.4.0
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 +6 -6
- package/src/panel/Panel.scss +97 -11
- package/src/panel/Panel.tsx +23 -65
- package/src/panel/PanelBlue.scss +18 -0
- package/src/panel/PanelDock.tsx +19 -0
- package/src/panel/PanelHeader.tsx +59 -0
- package/src/panel/PanelHeaderButton.tsx +22 -0
- package/src/panel/PanelInstanceContext.ts +8 -0
- package/src/panel/PanelInstanceContextProvider.tsx +25 -6
- package/src/panel/PanelsContext.tsx +4 -2
- package/src/panel/PanelsContextProvider.tsx +13 -12
- package/src/panel/generateId.test.ts +9 -0
- package/src/panel/generateId.ts +16 -17
- package/src/stories/panel/PanelButtons/ShowPanel.tsx +3 -1
- package/src/stories/panel/ShowPanel/ShowPanel.tsx +33 -32
- package/src/stories/panel/ShowPanelDocking/ShowPanel.mdx +20 -0
- package/src/stories/panel/ShowPanelDocking/ShowPanel.stories.tsx +27 -0
- package/src/stories/panel/ShowPanelDocking/ShowPanel.tsx +64 -0
- package/src/stories/panel/ShowPanelResizingAgGrid/ShowPanelResizingAgGrid.mdx +1 -1
- package/src/stories/panel/ShowPanelResizingAgGrid/ShowPanelResizingStepAgGrid.tsx +4 -2
- package/src/stories/panel/ShowTabbedPanel/ShowPanel.mdx +20 -0
- package/src/stories/panel/ShowTabbedPanel/ShowPanel.stories.tsx +27 -0
- package/src/stories/panel/ShowTabbedPanel/ShowPanel.tsx +76 -0
- package/src/stories/panel/story.scss +13 -0
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"panel",
|
|
13
13
|
"popout"
|
|
14
14
|
],
|
|
15
|
-
"version": "1.
|
|
15
|
+
"version": "1.4.0",
|
|
16
16
|
"peerDependencies": {
|
|
17
17
|
"@linzjs/lui": "^17",
|
|
18
18
|
"lodash-es": ">=4",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
|
|
42
42
|
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
|
|
43
|
-
"@linzjs/step-ag-grid": "^14.
|
|
43
|
+
"@linzjs/step-ag-grid": "^14.10.0",
|
|
44
44
|
"@rollup/plugin-commonjs": "^25.0.2",
|
|
45
45
|
"@rollup/plugin-json": "^6.0.0",
|
|
46
46
|
"@rollup/plugin-node-resolve": "^15.1.0",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"@trivago/prettier-plugin-sort-imports": "^4.1.1",
|
|
63
63
|
"@types/jest": "^29.5.2",
|
|
64
64
|
"@types/lodash-es": "^4.17.7",
|
|
65
|
-
"@types/node": "^20.
|
|
65
|
+
"@types/node": "^20.4.2",
|
|
66
66
|
"@types/react": "^18.2.14",
|
|
67
67
|
"@types/react-dom": "^18.2.6",
|
|
68
68
|
"@types/uuid": "^9.0.2",
|
|
@@ -82,14 +82,14 @@
|
|
|
82
82
|
"eslint-plugin-testing-library": "^5.11.0",
|
|
83
83
|
"jest": "^29.5.0",
|
|
84
84
|
"jest-canvas-mock": "^2.5.2",
|
|
85
|
-
"jest-environment-jsdom": "^29.
|
|
85
|
+
"jest-environment-jsdom": "^29.6.1",
|
|
86
86
|
"jest-expect-message": "^1.1.3",
|
|
87
87
|
"mkdirp": "^3.0.1",
|
|
88
88
|
"npm-run-all": "^4.1.5",
|
|
89
89
|
"prettier": "^2.8.8",
|
|
90
90
|
"prop-types": "^15.8.1",
|
|
91
91
|
"react-scripts": "5.0.1",
|
|
92
|
-
"rollup": "^3.
|
|
92
|
+
"rollup": "^3.26.2",
|
|
93
93
|
"rollup-plugin-copy": "^3.4.0",
|
|
94
94
|
"sass": "^1.63.6",
|
|
95
95
|
"sass-loader": "^13.3.2",
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
"stylelint-prettier": "3.0.0",
|
|
104
104
|
"stylelint-scss": "5.0.1",
|
|
105
105
|
"typescript": "^4.9.5",
|
|
106
|
-
"vite": "^4.
|
|
106
|
+
"vite": "^4.4.4"
|
|
107
107
|
},
|
|
108
108
|
"scripts": {
|
|
109
109
|
"build": "run-s clean stylelint lint lint-circular-deps bundle",
|
package/src/panel/Panel.scss
CHANGED
|
@@ -1,30 +1,116 @@
|
|
|
1
|
-
.
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
@use "node_modules/@linzjs/lui/dist/scss/Foundation/Variables/ColorVars.scss" as colours;
|
|
2
|
+
|
|
3
|
+
.panelDock > .panelDock-content:not(:empty) + .panelDock-empty {
|
|
4
|
+
display: none;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.WindowPanel-children {
|
|
4
8
|
display: flex;
|
|
5
|
-
|
|
6
|
-
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
text-overflow: ellipsis;
|
|
11
|
+
flex: 1;
|
|
7
12
|
}
|
|
8
13
|
|
|
9
14
|
.WindowPanel-header {
|
|
10
15
|
height: 48px;
|
|
11
16
|
line-height: 48px;
|
|
12
|
-
color:
|
|
13
|
-
padding: 0
|
|
17
|
+
color: colours.$charcoal;
|
|
18
|
+
padding: 0 16px;
|
|
14
19
|
display: flex;
|
|
15
20
|
overflow: hidden;
|
|
16
21
|
justify-content: space-between;
|
|
17
|
-
border-bottom: 2px
|
|
22
|
+
border-bottom: 2px colours.$lily solid;
|
|
18
23
|
font-size: 1em;
|
|
19
24
|
font-weight: 600;
|
|
20
25
|
flex-direction: row;
|
|
26
|
+
align-items: center;
|
|
27
|
+
|
|
28
|
+
.LuiTabsGroup {
|
|
29
|
+
margin-top: 4px;
|
|
30
|
+
line-height: 16px;
|
|
31
|
+
|
|
32
|
+
.LuiTab {
|
|
33
|
+
min-width: 0;
|
|
34
|
+
padding-left: 8px;
|
|
35
|
+
padding-right: 8px;
|
|
36
|
+
padding-bottom: 11px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.LuiTab:not(.LuiTab--active):focus {
|
|
40
|
+
padding-top: 8px;
|
|
41
|
+
padding-bottom: 8px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.LuiTab--active {
|
|
45
|
+
padding-left: 7px;
|
|
46
|
+
padding-right: 7px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.LuiTab--active::after, .LuiTab--active:focus::after {
|
|
50
|
+
padding: 0;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.lui-button + .lui-button, a.lui-button + .lui-button {
|
|
55
|
+
margin-left: 0 !important;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.WindowPanel {
|
|
60
|
+
box-shadow: 0 1px 6px 0 #00000026, 0 6px 10px 0 #00000040;
|
|
61
|
+
background-color: #fff;
|
|
62
|
+
display: flex;
|
|
63
|
+
flex-direction: column;
|
|
64
|
+
border-radius: 9px;
|
|
65
|
+
|
|
66
|
+
.WindowPanel-header {
|
|
67
|
+
border-radius: 9px 9px 0 0;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
%WindowPanel-divider {
|
|
72
|
+
border-left: 2px solid colours.$lily;
|
|
73
|
+
background-color: colours.$lily;
|
|
74
|
+
width: 0;
|
|
75
|
+
height: 19px;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.WindowPanel-divider-left {
|
|
79
|
+
@extend %WindowPanel-divider;
|
|
80
|
+
margin-right: 15px;
|
|
81
|
+
margin-left: 19px;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.WindowPanel-divider-right {
|
|
85
|
+
@extend %WindowPanel-divider;
|
|
86
|
+
margin-right: 8px;
|
|
87
|
+
margin-left: 8px;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.WindowPanel-buttons {
|
|
91
|
+
display: flex;
|
|
92
|
+
align-items: center;
|
|
93
|
+
|
|
94
|
+
button.lui-button {
|
|
95
|
+
display: flex;
|
|
96
|
+
align-items: center;
|
|
97
|
+
|
|
98
|
+
span.LuiIcon {
|
|
99
|
+
margin-left: auto;
|
|
100
|
+
margin-right: auto;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
21
103
|
}
|
|
22
104
|
|
|
23
105
|
.WindowPanel-header-title {
|
|
106
|
+
display: flex;
|
|
107
|
+
align-items: center;
|
|
24
108
|
white-space: nowrap;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.WindowPanel-header-title-icon {
|
|
112
|
+
fill: colours.$fuscous !important;
|
|
113
|
+
margin-right: 8px;
|
|
28
114
|
}
|
|
29
115
|
|
|
30
116
|
.WindowPanel-content {
|
package/src/panel/Panel.tsx
CHANGED
|
@@ -1,26 +1,34 @@
|
|
|
1
1
|
import "./Panel.scss";
|
|
2
|
+
import "./PanelBlue.scss";
|
|
2
3
|
import "@linzjs/lui/dist/scss/base.scss";
|
|
3
4
|
|
|
4
5
|
import { PanelContext } from "./PanelContext";
|
|
5
6
|
import { PanelInstanceContext, PanelSize } from "./PanelInstanceContext";
|
|
6
7
|
import { PanelPosition, PanelsContext } from "./PanelsContext";
|
|
7
8
|
import { PopoutWindow } from "./PopoutWindow";
|
|
9
|
+
import clsx from "clsx";
|
|
8
10
|
import { ReactElement, ReactNode, useContext, useEffect, useState } from "react";
|
|
11
|
+
import ReactDOM from "react-dom";
|
|
9
12
|
import { Rnd } from "react-rnd";
|
|
10
13
|
|
|
11
|
-
import { LuiButton, LuiIcon } from "@linzjs/lui";
|
|
12
|
-
import { LuiIconName } from "@linzjs/lui/dist/assets/svg-content";
|
|
13
|
-
|
|
14
14
|
export interface PanelProps {
|
|
15
15
|
title: string;
|
|
16
16
|
position?: PanelPosition;
|
|
17
17
|
size?: PanelSize;
|
|
18
|
+
className?: string;
|
|
18
19
|
children: ReactNode;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
export const Panel = ({
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
export const Panel = ({
|
|
23
|
+
title,
|
|
24
|
+
position,
|
|
25
|
+
size = { width: 320, height: 200 },
|
|
26
|
+
className,
|
|
27
|
+
children,
|
|
28
|
+
}: PanelProps): ReactElement => {
|
|
29
|
+
const { nextStackPosition, dockElements } = useContext(PanelsContext);
|
|
30
|
+
const { panelPoppedOut, bounds, zIndex, bringPanelToFront, uniqueId, setTitle, dockId, docked } =
|
|
31
|
+
useContext(PanelInstanceContext);
|
|
24
32
|
|
|
25
33
|
const [panelPosition, setPanelPosition] = useState(() => position ?? nextStackPosition());
|
|
26
34
|
const [panelSize, setPanelSize] = useState(size ?? { width: 320, height: 200 });
|
|
@@ -37,10 +45,13 @@ export const Panel = ({ title, position, size = { width: 320, height: 200 }, chi
|
|
|
37
45
|
setTitle(title);
|
|
38
46
|
}, [setTitle, title]);
|
|
39
47
|
|
|
40
|
-
|
|
48
|
+
const dockElement = docked && dockId && dockElements[dockId];
|
|
49
|
+
|
|
41
50
|
return (
|
|
42
51
|
<PanelContext.Provider value={{ resizePanel }}>
|
|
43
|
-
{
|
|
52
|
+
{dockElement && dockElement.isConnected ? (
|
|
53
|
+
ReactDOM.createPortal(children, dockElement)
|
|
54
|
+
) : panelPoppedOut ? (
|
|
44
55
|
<PopoutWindow name={uniqueId} title={title} size={panelSize}>
|
|
45
56
|
<div
|
|
46
57
|
style={{
|
|
@@ -49,13 +60,14 @@ export const Panel = ({ title, position, size = { width: 320, height: 200 }, chi
|
|
|
49
60
|
width: "100%",
|
|
50
61
|
height: "100%",
|
|
51
62
|
}}
|
|
63
|
+
className={className}
|
|
52
64
|
>
|
|
53
65
|
{children}
|
|
54
66
|
</div>
|
|
55
67
|
</PopoutWindow>
|
|
56
68
|
) : (
|
|
57
69
|
<Rnd
|
|
58
|
-
className={"WindowPanel"}
|
|
70
|
+
className={clsx("WindowPanel", className)}
|
|
59
71
|
dragHandleClassName={"draggable-handle"}
|
|
60
72
|
minWidth={100}
|
|
61
73
|
minHeight={100}
|
|
@@ -91,60 +103,6 @@ export const Panel = ({ title, position, size = { width: 320, height: 200 }, chi
|
|
|
91
103
|
);
|
|
92
104
|
};
|
|
93
105
|
|
|
94
|
-
export
|
|
95
|
-
"
|
|
96
|
-
icon: LuiIconName;
|
|
97
|
-
onClick: () => void;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export const PanelHeaderButton = ({ "aria-label": ariaLabel, icon, onClick }: PanelHeaderButtonProps) => (
|
|
101
|
-
<LuiButton
|
|
102
|
-
level={"plain-text"}
|
|
103
|
-
className={"lui-button-icon-only"}
|
|
104
|
-
size={"sm"}
|
|
105
|
-
onClick={onClick}
|
|
106
|
-
buttonProps={{
|
|
107
|
-
onTouchStart: onClick,
|
|
108
|
-
}}
|
|
109
|
-
>
|
|
110
|
-
<LuiIcon name={icon} alt={ariaLabel} />
|
|
111
|
-
</LuiButton>
|
|
106
|
+
export const PanelContent = ({ children }: { children: ReactNode }) => (
|
|
107
|
+
<div className={"WindowPanel-content"}>{children}</div>
|
|
112
108
|
);
|
|
113
|
-
|
|
114
|
-
export interface PanelHeaderProps {
|
|
115
|
-
extraButtons?: ReactNode;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export const PanelHeader = ({ extraButtons }: PanelHeaderProps) => {
|
|
119
|
-
const { panelClose, panelTogglePopout, panelPoppedOut, title } = useContext(PanelInstanceContext);
|
|
120
|
-
const [cursor, setCursor] = useState<"grab" | "grabbing">("grab");
|
|
121
|
-
|
|
122
|
-
const headerMouseDown = () => {
|
|
123
|
-
!panelPoppedOut && setCursor("grabbing");
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
return (
|
|
127
|
-
<div
|
|
128
|
-
className={"WindowPanel-header draggable-handle"}
|
|
129
|
-
onMouseDown={headerMouseDown}
|
|
130
|
-
onMouseUp={() => !panelPoppedOut && setCursor("grab")}
|
|
131
|
-
style={{ cursor }}
|
|
132
|
-
>
|
|
133
|
-
<div className={"WindowPanel-header-title"}>{title}</div>
|
|
134
|
-
<div className={"LuiFloatingWindow-buttons"}>{extraButtons}</div>
|
|
135
|
-
<div className={"LuiFloatingWindow-extra-buttons-divider"}>|</div>
|
|
136
|
-
<div className={"LuiFloatingWindow-buttons"}>
|
|
137
|
-
<PanelHeaderButton
|
|
138
|
-
aria-label={panelPoppedOut ? "Pop-in" : "Pop-out"}
|
|
139
|
-
onClick={panelTogglePopout}
|
|
140
|
-
icon={panelPoppedOut ? "ic_pop_back" : "ic_launch_new window_open"}
|
|
141
|
-
/>
|
|
142
|
-
<PanelHeaderButton aria-label={"Close"} onClick={panelClose} icon={"ic_clear"} />
|
|
143
|
-
</div>
|
|
144
|
-
</div>
|
|
145
|
-
);
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
export const PanelContent = ({ children }: { children: ReactNode }) => {
|
|
149
|
-
return <div className={"WindowPanel-content"}>{children}</div>;
|
|
150
|
-
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
@use "node_modules/@linzjs/lui/dist/scss/Foundation/Variables/ColorVars.scss" as colours;
|
|
2
|
+
|
|
3
|
+
.WindowPanel-blue.WindowPanel-header, .WindowPanel-blue .WindowPanel-header {
|
|
4
|
+
background: linear-gradient(270deg,#007198,#00425b 100%);
|
|
5
|
+
color: #fff;
|
|
6
|
+
|
|
7
|
+
svg * {
|
|
8
|
+
fill: #fff;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.WindowPanel-blue .LuiTab {
|
|
13
|
+
color: colours.$iceberg
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.WindowPanel-blue .LuiTab.LuiTab--active {
|
|
17
|
+
color: colours.$downy
|
|
18
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { PanelsContext } from "./PanelsContext";
|
|
2
|
+
import { PropsWithChildren, ReactElement, useContext } from "react";
|
|
3
|
+
|
|
4
|
+
export interface PanelDockProps {
|
|
5
|
+
id: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const PanelDock = ({ id, children }: PropsWithChildren<PanelDockProps>): ReactElement => {
|
|
9
|
+
const { setDockElement } = useContext(PanelsContext);
|
|
10
|
+
|
|
11
|
+
const setElement = (element: HTMLDivElement) => setDockElement(id, element);
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div className={"panelDock"}>
|
|
15
|
+
<div ref={setElement} className={"panelDock-content"} />
|
|
16
|
+
<div className={"panelDock-empty"}>{children}</div>
|
|
17
|
+
</div>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { PanelHeaderButton } from "./PanelHeaderButton";
|
|
2
|
+
import { PanelInstanceContext } from "./PanelInstanceContext";
|
|
3
|
+
import { ReactNode, useContext, useState } from "react";
|
|
4
|
+
|
|
5
|
+
import { LuiIcon } from "@linzjs/lui";
|
|
6
|
+
import { LuiIconName } from "@linzjs/lui/dist/assets/svg-content";
|
|
7
|
+
|
|
8
|
+
export interface PanelHeaderProps {
|
|
9
|
+
icon?: LuiIconName;
|
|
10
|
+
extraLeft?: ReactNode;
|
|
11
|
+
extraRight?: ReactNode;
|
|
12
|
+
onHelpClick?: () => void;
|
|
13
|
+
dockTo?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const PanelHeader = ({ icon, extraLeft, extraRight, onHelpClick, dockTo }: PanelHeaderProps) => {
|
|
17
|
+
const { panelClose, panelTogglePopout, panelPoppedOut, title, dock, undock, docked } =
|
|
18
|
+
useContext(PanelInstanceContext);
|
|
19
|
+
const [cursor, setCursor] = useState<"grab" | "grabbing">("grab");
|
|
20
|
+
|
|
21
|
+
const headerMouseDown = () => {
|
|
22
|
+
!panelPoppedOut && setCursor("grabbing");
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div
|
|
27
|
+
className={"WindowPanel-header draggable-handle"}
|
|
28
|
+
onMouseDown={headerMouseDown}
|
|
29
|
+
onMouseUp={() => !panelPoppedOut && setCursor("grab")}
|
|
30
|
+
style={{ cursor }}
|
|
31
|
+
>
|
|
32
|
+
<div className={"WindowPanel-header-title"}>
|
|
33
|
+
{icon && <LuiIcon name={icon} alt={"icon"} size={"md"} className={"WindowPanel-header-title-icon"} />}
|
|
34
|
+
{title}
|
|
35
|
+
</div>
|
|
36
|
+
{extraLeft && <div className={"WindowPanel-divider-left"} />}
|
|
37
|
+
<div className={"WindowPanel-children"}>{extraLeft}</div>
|
|
38
|
+
<div className={"WindowPanel-buttons"}>
|
|
39
|
+
{extraRight}
|
|
40
|
+
{onHelpClick && <PanelHeaderButton aria-label={"Help"} onClick={onHelpClick} icon={"ic_help_outline"} />}
|
|
41
|
+
</div>
|
|
42
|
+
<div className={"WindowPanel-divider-right"} />
|
|
43
|
+
<div className={"WindowPanel-buttons"}>
|
|
44
|
+
{dockTo &&
|
|
45
|
+
(docked ? (
|
|
46
|
+
<PanelHeaderButton aria-label={"Undock"} onClick={undock} icon={"ic_close_list"} />
|
|
47
|
+
) : (
|
|
48
|
+
<PanelHeaderButton aria-label={"Undock"} onClick={() => dock(dockTo)} icon={"ic_left_col"} />
|
|
49
|
+
))}
|
|
50
|
+
<PanelHeaderButton
|
|
51
|
+
aria-label={panelPoppedOut ? "Pop-in" : "Pop-out"}
|
|
52
|
+
onClick={panelTogglePopout}
|
|
53
|
+
icon={panelPoppedOut ? "ic_pop_back" : "ic_launch_new window_open"}
|
|
54
|
+
/>
|
|
55
|
+
<PanelHeaderButton aria-label={"Close"} onClick={panelClose} icon={"ic_clear"} />
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { LuiButton, LuiIcon } from "@linzjs/lui";
|
|
2
|
+
import { LuiIconName } from "@linzjs/lui/dist/assets/svg-content";
|
|
3
|
+
|
|
4
|
+
export interface PanelHeaderButtonProps {
|
|
5
|
+
"aria-label": string;
|
|
6
|
+
icon: LuiIconName;
|
|
7
|
+
onClick: () => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const PanelHeaderButton = ({ "aria-label": ariaLabel, icon, onClick }: PanelHeaderButtonProps) => (
|
|
11
|
+
<LuiButton
|
|
12
|
+
level={"plain-text"}
|
|
13
|
+
className={"lui-button-icon-only"}
|
|
14
|
+
size={"sm"}
|
|
15
|
+
onClick={onClick}
|
|
16
|
+
buttonProps={{
|
|
17
|
+
onTouchStart: onClick,
|
|
18
|
+
}}
|
|
19
|
+
>
|
|
20
|
+
<LuiIcon name={icon} alt={ariaLabel} size={"md"} />
|
|
21
|
+
</LuiButton>
|
|
22
|
+
);
|
|
@@ -17,6 +17,10 @@ export interface PanelInstanceContextType {
|
|
|
17
17
|
bringPanelToFront: () => void;
|
|
18
18
|
panelPoppedOut: boolean;
|
|
19
19
|
zIndex: number;
|
|
20
|
+
dockId: string | undefined;
|
|
21
|
+
docked: boolean;
|
|
22
|
+
dock: (id: string) => void;
|
|
23
|
+
undock: () => void;
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
const NoContextError = () => {
|
|
@@ -36,6 +40,10 @@ export const PanelInstanceContext = createContext<PanelInstanceContextType>({
|
|
|
36
40
|
panelClose: NoContextError,
|
|
37
41
|
setPanelWindow: NoContextError,
|
|
38
42
|
bringPanelToFront: NoContextError,
|
|
43
|
+
dock: NoContextError,
|
|
44
|
+
undock: NoContextError,
|
|
45
|
+
docked: false,
|
|
46
|
+
dockId: undefined,
|
|
39
47
|
panelPoppedOut: false,
|
|
40
48
|
zIndex: 0,
|
|
41
49
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PanelInstanceContext } from "./PanelInstanceContext";
|
|
2
2
|
import { PanelInstance, PanelsContext } from "./PanelsContext";
|
|
3
|
-
import { ReactElement, ReactNode, useContext, useState } from "react";
|
|
3
|
+
import { ReactElement, ReactNode, useCallback, useContext, useState } from "react";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Provides access to closing/popping panels
|
|
@@ -14,8 +14,25 @@ export const PanelInstanceContextProvider = ({
|
|
|
14
14
|
panelInstance: PanelInstance;
|
|
15
15
|
children: ReactNode;
|
|
16
16
|
}): ReactElement => {
|
|
17
|
-
const { closePanel,
|
|
17
|
+
const { closePanel, bringPanelToFront } = useContext(PanelsContext);
|
|
18
18
|
const [title, setTitle] = useState("");
|
|
19
|
+
const [dockId, setDockId] = useState<string>();
|
|
20
|
+
const [poppedOut, setPoppedOut] = useState(false);
|
|
21
|
+
|
|
22
|
+
const togglePopOut = useCallback(() => {
|
|
23
|
+
panelInstance.window = null;
|
|
24
|
+
setPoppedOut(!poppedOut);
|
|
25
|
+
}, [panelInstance, poppedOut]);
|
|
26
|
+
|
|
27
|
+
const dock = useCallback(
|
|
28
|
+
(id: string) => {
|
|
29
|
+
if (poppedOut) togglePopOut();
|
|
30
|
+
setDockId(id);
|
|
31
|
+
},
|
|
32
|
+
[poppedOut, togglePopOut],
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const undock = useCallback(() => setDockId(undefined), []);
|
|
19
36
|
|
|
20
37
|
return (
|
|
21
38
|
<PanelInstanceContext.Provider
|
|
@@ -30,15 +47,17 @@ export const PanelInstanceContextProvider = ({
|
|
|
30
47
|
panelInstance.window = null;
|
|
31
48
|
closePanel(panelInstance);
|
|
32
49
|
},
|
|
33
|
-
panelTogglePopout:
|
|
34
|
-
|
|
35
|
-
},
|
|
36
|
-
panelPoppedOut: panelInstance.poppedOut,
|
|
50
|
+
panelTogglePopout: togglePopOut,
|
|
51
|
+
panelPoppedOut: poppedOut,
|
|
37
52
|
setPanelWindow: (w: Window) => {
|
|
38
53
|
panelInstance.window = w;
|
|
39
54
|
},
|
|
40
55
|
bringPanelToFront: () => bringPanelToFront(panelInstance),
|
|
41
56
|
zIndex: panelInstance.zIndex,
|
|
57
|
+
docked: !poppedOut && !!dockId,
|
|
58
|
+
dockId,
|
|
59
|
+
dock,
|
|
60
|
+
undock,
|
|
42
61
|
}}
|
|
43
62
|
>
|
|
44
63
|
{children}
|
|
@@ -17,9 +17,10 @@ export interface PanelsContextType {
|
|
|
17
17
|
openPanels: Set<string>;
|
|
18
18
|
openPanel: (uniqueId: string, component: () => ReactElement) => void;
|
|
19
19
|
closePanel: (panelInstance: PanelInstance) => void;
|
|
20
|
-
togglePopOut: (panelInstance: PanelInstance) => void;
|
|
21
20
|
bringPanelToFront: (panelInstance: PanelInstance) => void;
|
|
22
21
|
nextStackPosition: () => PanelPosition;
|
|
22
|
+
dockElements: Record<string, HTMLDivElement>;
|
|
23
|
+
setDockElement: (id: string, element: HTMLDivElement) => void;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
const NoContext = () => {
|
|
@@ -30,7 +31,8 @@ export const PanelsContext = createContext<PanelsContextType>({
|
|
|
30
31
|
openPanels: new Set(),
|
|
31
32
|
openPanel: NoContext,
|
|
32
33
|
closePanel: NoContext,
|
|
33
|
-
togglePopOut: NoContext,
|
|
34
34
|
bringPanelToFront: NoContext,
|
|
35
|
+
dockElements: {},
|
|
36
|
+
setDockElement: NoContext,
|
|
35
37
|
nextStackPosition: () => ({ x: 0, y: 0 }),
|
|
36
38
|
});
|
|
@@ -25,6 +25,17 @@ export const PanelsContextProvider = ({
|
|
|
25
25
|
// Can't use a map here as we need to retain render order for performance
|
|
26
26
|
const [panelInstances, setPanelInstances] = useState<PanelInstance[]>([]);
|
|
27
27
|
|
|
28
|
+
const [dockElements, setDockElements] = useState<Record<string, HTMLDivElement>>({});
|
|
29
|
+
|
|
30
|
+
const setDockElement = useCallback(
|
|
31
|
+
(id: string, element: HTMLDivElement) => {
|
|
32
|
+
if (dockElements[id] !== element) {
|
|
33
|
+
setDockElements({ ...dockElements, [id]: element });
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
[dockElements],
|
|
37
|
+
);
|
|
38
|
+
|
|
28
39
|
const bringPanelToFront = useCallback(
|
|
29
40
|
(panelInstance: PanelInstance) => {
|
|
30
41
|
if (panelInstance.window) {
|
|
@@ -82,17 +93,6 @@ export const PanelsContextProvider = ({
|
|
|
82
93
|
[panelInstances],
|
|
83
94
|
);
|
|
84
95
|
|
|
85
|
-
const togglePopOut = useCallback(
|
|
86
|
-
(panelInstance: PanelInstance) => {
|
|
87
|
-
panelInstance.poppedOut = !panelInstance.poppedOut;
|
|
88
|
-
if (!panelInstance.poppedOut) {
|
|
89
|
-
panelInstance.window = null;
|
|
90
|
-
}
|
|
91
|
-
setPanelInstances([...panelInstances]);
|
|
92
|
-
},
|
|
93
|
-
[panelInstances],
|
|
94
|
-
);
|
|
95
|
-
|
|
96
96
|
/**
|
|
97
97
|
* It's not easy to tell the difference between a window closing and a window popping in via events,
|
|
98
98
|
* so we're periodically checking instead.
|
|
@@ -122,9 +122,10 @@ export const PanelsContextProvider = ({
|
|
|
122
122
|
openPanels,
|
|
123
123
|
openPanel,
|
|
124
124
|
closePanel,
|
|
125
|
-
togglePopOut,
|
|
126
125
|
bringPanelToFront,
|
|
127
126
|
nextStackPosition,
|
|
127
|
+
dockElements,
|
|
128
|
+
setDockElement,
|
|
128
129
|
}}
|
|
129
130
|
>
|
|
130
131
|
<Fragment key={"panels"}>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { makePopoutId } from "./generateId";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The only valid characters for an @emotion key are lower case chars or '-'.
|
|
5
|
+
* We need to replace any char that is not valid with a substitute.
|
|
6
|
+
*/
|
|
7
|
+
test("makePopoutId", () => {
|
|
8
|
+
expect(makePopoutId("Hello-59.!---&$*(--")).toBe("hello-five-nine-");
|
|
9
|
+
});
|
package/src/panel/generateId.ts
CHANGED
|
@@ -2,22 +2,21 @@
|
|
|
2
2
|
* The only valid characters for an @emotion key are lower case chars or '-'.
|
|
3
3
|
* We need to replace any char that is not valid with a substitute.
|
|
4
4
|
*/
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
.
|
|
8
|
-
.replaceAll(
|
|
9
|
-
.replaceAll("
|
|
10
|
-
.replaceAll("
|
|
11
|
-
.replaceAll("
|
|
12
|
-
.replaceAll("
|
|
13
|
-
.replaceAll("
|
|
14
|
-
.replaceAll("
|
|
15
|
-
.replaceAll("
|
|
16
|
-
.replaceAll("
|
|
17
|
-
.replaceAll("
|
|
5
|
+
export const makePopoutId = (str: string): string =>
|
|
6
|
+
str
|
|
7
|
+
.toLowerCase()
|
|
8
|
+
.replaceAll(/[^a-z0-9-]/g, "-")
|
|
9
|
+
.replaceAll("0", "-zero-")
|
|
10
|
+
.replaceAll("1", "-one-")
|
|
11
|
+
.replaceAll("2", "-two-")
|
|
12
|
+
.replaceAll("3", "-three-")
|
|
13
|
+
.replaceAll("4", "-four-")
|
|
14
|
+
.replaceAll("5", "-five-")
|
|
15
|
+
.replaceAll("6", "-six-")
|
|
16
|
+
.replaceAll("7", "-seven-")
|
|
17
|
+
.replaceAll("8", "-eight-")
|
|
18
|
+
.replaceAll("9", "-nine-")
|
|
19
|
+
.replaceAll(/-+/g, "-")
|
|
18
20
|
.toLowerCase();
|
|
19
|
-
}
|
|
20
21
|
|
|
21
|
-
export
|
|
22
|
-
return `popoutWindow-${makePopoutId(windowTitle)}`;
|
|
23
|
-
}
|
|
22
|
+
export const popoutWindowDivId = (windowTitle: string): string => `popoutWindow-${makePopoutId(windowTitle)}`;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import "../story.scss";
|
|
1
2
|
import "@linzjs/lui/dist/scss/base.scss";
|
|
2
3
|
|
|
3
4
|
import { OpenPanelButton } from "../../../panel/OpenPanelButton";
|
|
@@ -7,7 +8,8 @@ import {
|
|
|
7
8
|
ButtonIconVerticalGroup,
|
|
8
9
|
OpenPanelIcon,
|
|
9
10
|
} from "../../../panel/OpenPanelIcon";
|
|
10
|
-
import { Panel, PanelContent
|
|
11
|
+
import { Panel, PanelContent } from "../../../panel/Panel";
|
|
12
|
+
import { PanelHeader } from "../../../panel/PanelHeader";
|
|
11
13
|
|
|
12
14
|
/* exclude */
|
|
13
15
|
export interface ShowPanelProps {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import "../story.scss";
|
|
1
2
|
import "@linzjs/lui/dist/scss/base.scss";
|
|
2
3
|
|
|
3
4
|
import { OpenPanelButton } from "../../../panel/OpenPanelButton";
|
|
4
|
-
import { Panel, PanelContent
|
|
5
|
+
import { Panel, PanelContent } from "../../../panel/Panel";
|
|
6
|
+
import { PanelHeader } from "../../../panel/PanelHeader";
|
|
5
7
|
import { PanelsContextProvider } from "../../../panel/PanelsContextProvider";
|
|
6
8
|
|
|
7
9
|
// #Example: Panel Context Provider
|
|
@@ -19,7 +21,7 @@ export interface ShowPanelComponentProps {
|
|
|
19
21
|
|
|
20
22
|
export const ShowPanelComponent = ({ data }: ShowPanelComponentProps) => (
|
|
21
23
|
<Panel title={`Panel demo ${data}`} size={{ width: 640, height: 400 }}>
|
|
22
|
-
<PanelHeader />
|
|
24
|
+
<PanelHeader icon={"ic_add_adopt"} />
|
|
23
25
|
<PanelContent>
|
|
24
26
|
<PanelContents />
|
|
25
27
|
</PanelContent>
|
|
@@ -27,36 +29,35 @@ export const ShowPanelComponent = ({ data }: ShowPanelComponentProps) => (
|
|
|
27
29
|
);
|
|
28
30
|
|
|
29
31
|
/* exclude */
|
|
30
|
-
export const PanelContents = () =>
|
|
31
|
-
|
|
32
|
-
<
|
|
33
|
-
<
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
<
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
<
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
};
|
|
32
|
+
export const PanelContents = () => (
|
|
33
|
+
<table>
|
|
34
|
+
<thead>
|
|
35
|
+
<tr>
|
|
36
|
+
<th>Owner</th>
|
|
37
|
+
<th>Title</th>
|
|
38
|
+
<th>Address</th>
|
|
39
|
+
</tr>
|
|
40
|
+
</thead>
|
|
41
|
+
<tbody>
|
|
42
|
+
<tr>
|
|
43
|
+
<td>Roy</td>
|
|
44
|
+
<td>OT10A/1011</td>
|
|
45
|
+
<td>5 Grange St, Auckland</td>
|
|
46
|
+
</tr>
|
|
47
|
+
<tr>
|
|
48
|
+
<td>Ryan</td>
|
|
49
|
+
<td>OT10A/154</td>
|
|
50
|
+
<td>145 Garry St, Wellington</td>
|
|
51
|
+
</tr>
|
|
52
|
+
<tr>
|
|
53
|
+
<td>Suzy</td>
|
|
54
|
+
<td>OT10A/155</td>
|
|
55
|
+
<td>15 Aix St, Auckland</td>
|
|
56
|
+
</tr>
|
|
57
|
+
</tbody>
|
|
58
|
+
</table>
|
|
59
|
+
);
|
|
60
|
+
|
|
60
61
|
/* exclude */
|
|
61
62
|
|
|
62
63
|
// #Example: Panel Invocation
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {getBlocks, blockToString} from "../../support.js";
|
|
2
|
+
import {Meta, Source, Story} from "@storybook/blocks";
|
|
3
|
+
import * as ShowPanelStories from "./ShowPanel.stories.tsx"
|
|
4
|
+
|
|
5
|
+
import myModule from './ShowPanel.tsx?raw';
|
|
6
|
+
|
|
7
|
+
<Meta name="Docking Panel" of={ShowPanelStories}/>
|
|
8
|
+
# Panel Docking
|
|
9
|
+
## Example
|
|
10
|
+
Click to show the panel:
|
|
11
|
+
<Story of={ShowPanelStories.ShowPanelDocking}/>
|
|
12
|
+
|
|
13
|
+
## Code
|
|
14
|
+
<br/>
|
|
15
|
+
{getBlocks(myModule).map(block => (
|
|
16
|
+
<>
|
|
17
|
+
<h3>{block.split("\n")[0]}</h3>
|
|
18
|
+
<Source code={blockToString(block)} language='typescript'/>
|
|
19
|
+
</>
|
|
20
|
+
))}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { PanelsContextProvider } from "../../../panel/PanelsContextProvider";
|
|
2
|
+
import { TestShowPanel } from "./ShowPanel";
|
|
3
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof TestShowPanel> = {
|
|
6
|
+
title: "Components/Panel",
|
|
7
|
+
component: TestShowPanel,
|
|
8
|
+
argTypes: {
|
|
9
|
+
backgroundColor: { control: "color" },
|
|
10
|
+
},
|
|
11
|
+
decorators: [
|
|
12
|
+
(Story: any) => (
|
|
13
|
+
<div>
|
|
14
|
+
<PanelsContextProvider baseZIndex={500}>
|
|
15
|
+
<Story />
|
|
16
|
+
</PanelsContextProvider>
|
|
17
|
+
</div>
|
|
18
|
+
),
|
|
19
|
+
],
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
type Story = StoryObj<typeof meta>;
|
|
24
|
+
|
|
25
|
+
export const ShowPanelDocking: Story = {
|
|
26
|
+
args: {},
|
|
27
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import "../story.scss";
|
|
2
|
+
import "@linzjs/lui/dist/scss/base.scss";
|
|
3
|
+
|
|
4
|
+
import { OpenPanelButton } from "../../../panel/OpenPanelButton";
|
|
5
|
+
import { Panel, PanelContent } from "../../../panel/Panel";
|
|
6
|
+
import { PanelDock } from "../../../panel/PanelDock";
|
|
7
|
+
import { PanelHeader } from "../../../panel/PanelHeader";
|
|
8
|
+
|
|
9
|
+
// #Example: Panel Component
|
|
10
|
+
export interface ShowPanelComponentProps {
|
|
11
|
+
data: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const ShowPanelComponent = ({ data }: ShowPanelComponentProps) => (
|
|
15
|
+
<Panel title={`Panel demo ${data}`} size={{ width: 640, height: 400 }}>
|
|
16
|
+
<PanelHeader dockTo={"leftSide"} />
|
|
17
|
+
<PanelContent>
|
|
18
|
+
<PanelContents />
|
|
19
|
+
</PanelContent>
|
|
20
|
+
</Panel>
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
/* exclude */
|
|
24
|
+
export const PanelContents = () => {
|
|
25
|
+
return (
|
|
26
|
+
<table>
|
|
27
|
+
<thead>
|
|
28
|
+
<tr>
|
|
29
|
+
<th>Owner</th>
|
|
30
|
+
<th>Title</th>
|
|
31
|
+
<th>Address</th>
|
|
32
|
+
</tr>
|
|
33
|
+
</thead>
|
|
34
|
+
<tbody>
|
|
35
|
+
<tr>
|
|
36
|
+
<td>Roy</td>
|
|
37
|
+
<td>OT10A/1011</td>
|
|
38
|
+
<td>5 Grange St, Auckland</td>
|
|
39
|
+
</tr>
|
|
40
|
+
<tr>
|
|
41
|
+
<td>Ryan</td>
|
|
42
|
+
<td>OT10A/154</td>
|
|
43
|
+
<td>145 Garry St, Wellington</td>
|
|
44
|
+
</tr>
|
|
45
|
+
<tr>
|
|
46
|
+
<td>Suzy</td>
|
|
47
|
+
<td>OT10A/155</td>
|
|
48
|
+
<td>15 Aix St, Auckland</td>
|
|
49
|
+
</tr>
|
|
50
|
+
</tbody>
|
|
51
|
+
</table>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
/* exclude */
|
|
55
|
+
|
|
56
|
+
// #Example: Panel Invocation
|
|
57
|
+
export const TestShowPanel = () => {
|
|
58
|
+
return (
|
|
59
|
+
<>
|
|
60
|
+
<OpenPanelButton buttonText={"TestPanel"} component={() => <ShowPanelComponent data={1} />} />
|
|
61
|
+
<PanelDock id={"leftSide"}>The Panel will dock in here</PanelDock>
|
|
62
|
+
</>
|
|
63
|
+
);
|
|
64
|
+
};
|
|
@@ -4,7 +4,7 @@ import * as PanelStories from "./ShowPanelResizingAgGrid.stories.tsx"
|
|
|
4
4
|
|
|
5
5
|
import myModule from './ShowPanelResizingStepAgGrid.tsx?raw';
|
|
6
6
|
|
|
7
|
-
<Meta name="
|
|
7
|
+
<Meta name="Resizing Panel step-ag-grid" of={PanelStories}/>
|
|
8
8
|
# Show Panel
|
|
9
9
|
## Example
|
|
10
10
|
Click to show the panel:
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import "../story.scss";
|
|
1
2
|
import "@linzjs/lui/dist/scss/base.scss";
|
|
2
3
|
import "@linzjs/step-ag-grid/dist/GridTheme.scss";
|
|
3
4
|
import "@linzjs/step-ag-grid/dist/index.css";
|
|
4
5
|
|
|
5
6
|
import { OpenPanelButton } from "../../../panel/OpenPanelButton";
|
|
6
|
-
import { Panel, PanelContent
|
|
7
|
+
import { Panel, PanelContent } from "../../../panel/Panel";
|
|
7
8
|
import { PanelContext } from "../../../panel/PanelContext";
|
|
9
|
+
import { PanelHeader } from "../../../panel/PanelHeader";
|
|
8
10
|
import { PanelsContextProvider } from "../../../panel/PanelsContextProvider";
|
|
9
11
|
import { useContext, useMemo, useState } from "react";
|
|
10
12
|
|
|
@@ -35,7 +37,7 @@ export interface TestPanelProps {
|
|
|
35
37
|
|
|
36
38
|
export const TestPanelResizing = ({ data }: TestPanelProps) => {
|
|
37
39
|
return (
|
|
38
|
-
<Panel title={`Panel resizing demo ${data}`} size={{ width: 320, height: 400 }}>
|
|
40
|
+
<Panel title={`Panel resizing demo ${data}`} size={{ width: 320, height: 400 }} className={"WindowPanel-blue"}>
|
|
39
41
|
<PanelHeader />
|
|
40
42
|
<PanelContent>
|
|
41
43
|
<PanelContentsWithResize />
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {getBlocks, blockToString} from "../../support.js";
|
|
2
|
+
import {Meta, Source, Story} from "@storybook/blocks";
|
|
3
|
+
import * as ShowPanelStories from "./ShowPanel.stories.tsx"
|
|
4
|
+
|
|
5
|
+
import myModule from './ShowPanel.tsx?raw';
|
|
6
|
+
|
|
7
|
+
<Meta name="Tabbed Panel" of={ShowPanelStories}/>
|
|
8
|
+
# Show Tabbed Panel
|
|
9
|
+
## Example
|
|
10
|
+
Click to show the tabbed panel:
|
|
11
|
+
<Story of={ShowPanelStories.ShowTabbedPanel}/>
|
|
12
|
+
|
|
13
|
+
## Code
|
|
14
|
+
<br/>
|
|
15
|
+
{getBlocks(myModule).map(block => (
|
|
16
|
+
<>
|
|
17
|
+
<h3>{block.split("\n")[0]}</h3>
|
|
18
|
+
<Source code={blockToString(block)} language='typescript'/>
|
|
19
|
+
</>
|
|
20
|
+
))}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { PanelsContextProvider } from "../../../panel/PanelsContextProvider";
|
|
2
|
+
import { TestShowTabbedPanel } from "./ShowPanel";
|
|
3
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof TestShowTabbedPanel> = {
|
|
6
|
+
title: "Components/Panel",
|
|
7
|
+
component: TestShowTabbedPanel,
|
|
8
|
+
argTypes: {
|
|
9
|
+
backgroundColor: { control: "color" },
|
|
10
|
+
},
|
|
11
|
+
decorators: [
|
|
12
|
+
(Story: any) => (
|
|
13
|
+
<div>
|
|
14
|
+
<PanelsContextProvider baseZIndex={500}>
|
|
15
|
+
<Story />
|
|
16
|
+
</PanelsContextProvider>
|
|
17
|
+
</div>
|
|
18
|
+
),
|
|
19
|
+
],
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
type Story = StoryObj<typeof meta>;
|
|
24
|
+
|
|
25
|
+
export const ShowTabbedPanel: Story = {
|
|
26
|
+
args: {},
|
|
27
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import "../story.scss";
|
|
2
|
+
import "@linzjs/lui/dist/scss/base.scss";
|
|
3
|
+
|
|
4
|
+
import { OpenPanelButton } from "../../../panel/OpenPanelButton";
|
|
5
|
+
import { Panel, PanelContent } from "../../../panel/Panel";
|
|
6
|
+
import { PanelHeader } from "../../../panel/PanelHeader";
|
|
7
|
+
import { PanelsContextProvider } from "../../../panel/PanelsContextProvider";
|
|
8
|
+
|
|
9
|
+
import { LuiTabs, LuiTabsGroup, LuiTabsPanel, LuiTabsPanelSwitch } from "@linzjs/lui";
|
|
10
|
+
|
|
11
|
+
// #Example: Panel Context Provider
|
|
12
|
+
// Don't forget to add a PanelContextProvider at the root of your project
|
|
13
|
+
export const App = () => (
|
|
14
|
+
<PanelsContextProvider baseZIndex={500}>
|
|
15
|
+
<div>...the rest of your app...</div>
|
|
16
|
+
</PanelsContextProvider>
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
// #Example: Panel Component
|
|
20
|
+
export interface ShowPanelComponentProps {
|
|
21
|
+
data: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const ShowPanelComponent = ({ data }: ShowPanelComponentProps) => (
|
|
25
|
+
<LuiTabs defaultPanel="africa">
|
|
26
|
+
<Panel title={`Panel demo ${data}`} size={{ width: 640, height: 400 }} className={"WindowPanel-blue"}>
|
|
27
|
+
<PanelHeader
|
|
28
|
+
icon={"ic_send"}
|
|
29
|
+
onHelpClick={() => alert("Help!!!")}
|
|
30
|
+
extraLeft={
|
|
31
|
+
<LuiTabsGroup ariaLabel="Animals">
|
|
32
|
+
<LuiTabsPanelSwitch targetPanel="africa">Africa</LuiTabsPanelSwitch>
|
|
33
|
+
<LuiTabsPanelSwitch targetPanel="asia">Asia</LuiTabsPanelSwitch>
|
|
34
|
+
</LuiTabsGroup>
|
|
35
|
+
}
|
|
36
|
+
/>
|
|
37
|
+
<PanelContent>
|
|
38
|
+
<PanelContents />
|
|
39
|
+
</PanelContent>
|
|
40
|
+
</Panel>
|
|
41
|
+
</LuiTabs>
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
/* exclude */
|
|
45
|
+
export const PanelContents = () => (
|
|
46
|
+
<div style={{ margin: "10px 16px" }}>
|
|
47
|
+
<LuiTabsPanel panel="africa">
|
|
48
|
+
<h2>African Countries</h2>
|
|
49
|
+
<ul className={"lui-list-bullet"}>
|
|
50
|
+
<li>Gabon</li>
|
|
51
|
+
<li>Malawi</li>
|
|
52
|
+
<li>Mozambique</li>
|
|
53
|
+
</ul>
|
|
54
|
+
</LuiTabsPanel>
|
|
55
|
+
<LuiTabsPanel panel="asia">
|
|
56
|
+
<h2>Asian Countries</h2>
|
|
57
|
+
<ul className={"lui-list-bullet"}>
|
|
58
|
+
<li>Laos</li>
|
|
59
|
+
<li>Nepal</li>
|
|
60
|
+
<li>Philippines</li>
|
|
61
|
+
</ul>
|
|
62
|
+
</LuiTabsPanel>
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
/* exclude */
|
|
67
|
+
|
|
68
|
+
// #Example: Panel Invocation
|
|
69
|
+
export const TestShowTabbedPanel = () => {
|
|
70
|
+
return (
|
|
71
|
+
<>
|
|
72
|
+
<OpenPanelButton buttonText={"TestPanel 1"} component={() => <ShowPanelComponent data={1} />} />{" "}
|
|
73
|
+
<OpenPanelButton buttonText={"TestPanel 2"} component={() => <ShowPanelComponent data={2} />} />
|
|
74
|
+
</>
|
|
75
|
+
);
|
|
76
|
+
};
|