mirror of
https://github.com/mathuo/dockview
synced 2025-01-22 09:25:57 +00:00
chore: fix files
This commit is contained in:
parent
19f027a0db
commit
9564319e43
@ -6,6 +6,41 @@
|
|||||||
"packages/dockview"
|
"packages/dockview"
|
||||||
],
|
],
|
||||||
"sandboxes": [
|
"sandboxes": [
|
||||||
|
"/packages/docs/sandboxes/constraints-dockview",
|
||||||
|
"/packages/docs/sandboxes/customheader-dockview",
|
||||||
|
"/packages/docs/sandboxes/demo-dockview",
|
||||||
|
"/packages/docs/sandboxes/dnd-dockview",
|
||||||
|
"/packages/docs/sandboxes/dockview-app",
|
||||||
|
"/packages/docs/sandboxes/editor-gridview",
|
||||||
|
"/packages/docs/sandboxes/events-dockview",
|
||||||
|
"/packages/docs/sandboxes/externaldnd-dockview",
|
||||||
|
"/packages/docs/sandboxes/floatinggroup-dockview",
|
||||||
|
"/packages/docs/sandboxes/fullwidthtab-dockview",
|
||||||
|
"/packages/docs/sandboxes/headeractions-dockview",
|
||||||
|
"/packages/docs/sandboxes/groupcontol-dockview",
|
||||||
|
"/packages/docs/sandboxes/iframe-dockview",
|
||||||
|
"/packages/docs/sandboxes/keyboard-dockview",
|
||||||
|
"/packages/docs/sandboxes/layout-dockview",
|
||||||
|
"/packages/docs/sandboxes/lockedgroup-dockview",
|
||||||
|
"/packages/docs/sandboxes/maximizegroup-dockview",
|
||||||
|
"/packages/docs/sandboxes/nativeapp-dockview",
|
||||||
|
"/packages/docs/sandboxes/nested-dockview",
|
||||||
|
"/packages/docs/sandboxes/popoutgroup-dockview",
|
||||||
|
"/packages/docs/sandboxes/rendering-dockview",
|
||||||
|
"/packages/docs/sandboxes/rendermode-dockview",
|
||||||
|
"/packages/docs/sandboxes/resize-dockview",
|
||||||
|
"/packages/docs/sandboxes/resizecontainer-dockview",
|
||||||
|
"/packages/docs/sandboxes/scrollbars-dockview",
|
||||||
|
"/packages/docs/sandboxes/simple-dockview",
|
||||||
|
"/packages/docs/sandboxes/simple-gridview",
|
||||||
|
"/packages/docs/sandboxes/simple-paneview",
|
||||||
|
"/packages/docs/sandboxes/tabheight-dockview",
|
||||||
|
"/packages/docs/sandboxes/updatetitle-dockview",
|
||||||
|
"/packages/docs/sandboxes/watermark-dockview",
|
||||||
|
"/packages/docs/sandboxes/javascript/fullwidthtab-dockview",
|
||||||
|
"/packages/docs/sandboxes/javascript/simple-dockview",
|
||||||
|
"/packages/docs/sandboxes/javascript/tabheight-dockview",
|
||||||
|
"/packages/docs/sandboxes/javascript/vanilla-dockview"
|
||||||
],
|
],
|
||||||
"node": "18"
|
"node": "18"
|
||||||
}
|
}
|
||||||
|
32
packages/docs/sandboxes/customheader-dockview/package.json
Normal file
32
packages/docs/sandboxes/customheader-dockview/package.json
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "customheader-dockview",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [
|
||||||
|
"dockview"
|
||||||
|
],
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "src/index.tsx",
|
||||||
|
"dependencies": {
|
||||||
|
"dockview": "*",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.0.28",
|
||||||
|
"@types/react-dom": "^18.0.11",
|
||||||
|
"typescript": "^4.9.5",
|
||||||
|
"react-scripts": "*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test --env=jsdom",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not ie <= 11",
|
||||||
|
"not op_mini all"
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is added to the
|
||||||
|
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||||
|
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<title>React App</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
You need to enable JavaScript to run this app.
|
||||||
|
</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
122
packages/docs/sandboxes/customheader-dockview/src/app.tsx
Normal file
122
packages/docs/sandboxes/customheader-dockview/src/app.tsx
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
import {
|
||||||
|
DockviewDefaultTab,
|
||||||
|
DockviewReact,
|
||||||
|
DockviewReadyEvent,
|
||||||
|
IDockviewPanelHeaderProps,
|
||||||
|
IDockviewPanelProps,
|
||||||
|
} from 'dockview';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
interface CustomProps {
|
||||||
|
valueA: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
default: (props: IDockviewPanelProps<CustomProps>) => {
|
||||||
|
return <div style={{ padding: '20px' }}>{props.api.title}</div>;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const headerComponents = {
|
||||||
|
default: (props: IDockviewPanelHeaderProps<CustomProps>) => {
|
||||||
|
const onContextMenu = (event: React.MouseEvent) => {
|
||||||
|
event.preventDefault();
|
||||||
|
alert(
|
||||||
|
`This custom header was parsed the params ${JSON.stringify(
|
||||||
|
props.params
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return <DockviewDefaultTab onContextMenu={onContextMenu} {...props} />;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const CustomHeadersDockview = (props: { theme?: string }) => {
|
||||||
|
const onReady = (event: DockviewReadyEvent) => {
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_1',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 1',
|
||||||
|
params: {
|
||||||
|
valueA: 'test value',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_2',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 2',
|
||||||
|
params: {
|
||||||
|
valueA: 'test value',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_3',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 3',
|
||||||
|
params: {
|
||||||
|
valueA: 'test value',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_4',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 4',
|
||||||
|
position: { referencePanel: 'panel_3', direction: 'right' },
|
||||||
|
params: {
|
||||||
|
valueA: 'test value',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_5',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 5',
|
||||||
|
position: { referencePanel: 'panel_4', direction: 'within' },
|
||||||
|
params: {
|
||||||
|
valueA: 'test value',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const panel6 = event.api.addPanel({
|
||||||
|
id: 'panel_6',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 6',
|
||||||
|
position: { referencePanel: 'panel_4', direction: 'below' },
|
||||||
|
params: {
|
||||||
|
valueA: 'test value',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
panel6.group.locked = true;
|
||||||
|
panel6.group.header.hidden = true;
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_7',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 7',
|
||||||
|
position: { referencePanel: 'panel_6', direction: 'right' },
|
||||||
|
params: {
|
||||||
|
valueA: 'test value',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_8',
|
||||||
|
component: 'default',
|
||||||
|
|
||||||
|
title: 'Panel 8',
|
||||||
|
position: { referencePanel: 'panel_7', direction: 'within' },
|
||||||
|
params: {
|
||||||
|
valueA: 'test value',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
event.api.addGroup();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DockviewReact
|
||||||
|
components={components}
|
||||||
|
defaultTabComponent={headerComponents.default}
|
||||||
|
onReady={onReady}
|
||||||
|
className={`${props.theme || 'dockview-theme-abyss'}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CustomHeadersDockview;
|
20
packages/docs/sandboxes/customheader-dockview/src/index.tsx
Normal file
20
packages/docs/sandboxes/customheader-dockview/src/index.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { StrictMode } from 'react';
|
||||||
|
import * as ReactDOMClient from 'react-dom/client';
|
||||||
|
import './styles.css';
|
||||||
|
import 'dockview/dist/styles/dockview.css';
|
||||||
|
|
||||||
|
import App from './app';
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root');
|
||||||
|
|
||||||
|
if (rootElement) {
|
||||||
|
const root = ReactDOMClient.createRoot(rootElement);
|
||||||
|
|
||||||
|
root.render(
|
||||||
|
<StrictMode>
|
||||||
|
<div className="app">
|
||||||
|
<App />
|
||||||
|
</div>
|
||||||
|
</StrictMode>
|
||||||
|
);
|
||||||
|
}
|
16
packages/docs/sandboxes/customheader-dockview/src/styles.css
Normal file
16
packages/docs/sandboxes/customheader-dockview/src/styles.css
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
color: white;
|
||||||
|
font-family: sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
}
|
18
packages/docs/sandboxes/customheader-dockview/tsconfig.json
Normal file
18
packages/docs/sandboxes/customheader-dockview/tsconfig.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "build/dist",
|
||||||
|
"module": "esnext",
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
|
"sourceMap": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"rootDir": "src",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strictNullChecks": true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "dockview.constraints",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [
|
||||||
|
"dockview"
|
||||||
|
],
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "src/index.tsx",
|
||||||
|
"dependencies": {
|
||||||
|
"dockview": "*",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.0.28",
|
||||||
|
"@types/react-dom": "^18.0.11",
|
||||||
|
"typescript": "^4.9.5",
|
||||||
|
"react-scripts": "*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test --env=jsdom",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not ie <= 11",
|
||||||
|
"not op_mini all"
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is added to the
|
||||||
|
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||||
|
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<title>React App</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
You need to enable JavaScript to run this app.
|
||||||
|
</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
148
packages/docs/sandboxes/react/dockview/constraints/src/app.tsx
Normal file
148
packages/docs/sandboxes/react/dockview/constraints/src/app.tsx
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
import {
|
||||||
|
DockviewApi,
|
||||||
|
DockviewReact,
|
||||||
|
DockviewReadyEvent,
|
||||||
|
GridConstraintChangeEvent,
|
||||||
|
IDockviewPanelProps,
|
||||||
|
} from 'dockview';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
default: (props: IDockviewPanelProps) => {
|
||||||
|
const [contraints, setContraints] =
|
||||||
|
React.useState<GridConstraintChangeEvent | null>(null);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
props.api.group.api.onDidConstraintsChange((event) => {
|
||||||
|
setContraints(event);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
props.api.group.api.setConstraints({
|
||||||
|
maximumWidth: 300,
|
||||||
|
maximumHeight: 300,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: '100%',
|
||||||
|
padding: '20px',
|
||||||
|
background: 'var(--dv-group-view-background-color)',
|
||||||
|
color: 'white',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<button onClick={onClick}>Set</button>
|
||||||
|
{contraints && (
|
||||||
|
<div style={{ fontSize: '13px' }}>
|
||||||
|
{typeof contraints.maximumHeight === 'number' && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
border: '1px solid grey',
|
||||||
|
margin: '2px',
|
||||||
|
padding: '1px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{ color: 'grey' }}
|
||||||
|
>{`Maximum Height: `}</span>
|
||||||
|
<span>{`${contraints.maximumHeight}px`}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{typeof contraints.minimumHeight === 'number' && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
border: '1px solid grey',
|
||||||
|
margin: '2px',
|
||||||
|
padding: '1px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{ color: 'grey' }}
|
||||||
|
>{`Minimum Height: `}</span>
|
||||||
|
<span>{`${contraints.minimumHeight}px`}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{typeof contraints.maximumWidth === 'number' && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
border: '1px solid grey',
|
||||||
|
margin: '2px',
|
||||||
|
padding: '1px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{ color: 'grey' }}
|
||||||
|
>{`Maximum Width: `}</span>
|
||||||
|
<span>{`${contraints.maximumWidth}px`}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{typeof contraints.minimumWidth === 'number' && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
border: '1px solid grey',
|
||||||
|
margin: '2px',
|
||||||
|
padding: '1px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{ color: 'grey' }}
|
||||||
|
>{`Minimum Width: `}</span>
|
||||||
|
<span>{`${contraints.minimumWidth}px`}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const App = (props: { theme?: string }) => {
|
||||||
|
const [api, setApi] = React.useState<DockviewApi>();
|
||||||
|
|
||||||
|
const onReady = (event: DockviewReadyEvent) => {
|
||||||
|
const panel1 = event.api.addPanel({
|
||||||
|
id: 'panel_1',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
|
||||||
|
const panel2 = event.api.addPanel({
|
||||||
|
id: 'panel_2',
|
||||||
|
component: 'default',
|
||||||
|
position: {
|
||||||
|
referencePanel: panel1,
|
||||||
|
direction: 'right',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const panel3 = event.api.addPanel({
|
||||||
|
id: 'panel_3',
|
||||||
|
component: 'default',
|
||||||
|
position: {
|
||||||
|
referencePanel: panel2,
|
||||||
|
direction: 'right',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const panel4 = event.api.addPanel({
|
||||||
|
id: 'panel_4',
|
||||||
|
component: 'default',
|
||||||
|
position: {
|
||||||
|
direction: 'below',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DockviewReact
|
||||||
|
onReady={onReady}
|
||||||
|
components={components}
|
||||||
|
className={`${props.theme || 'dockview-theme-abyss'}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
@ -0,0 +1,20 @@
|
|||||||
|
import { StrictMode } from 'react';
|
||||||
|
import * as ReactDOMClient from 'react-dom/client';
|
||||||
|
import './styles.css';
|
||||||
|
import 'dockview/dist/styles/dockview.css';
|
||||||
|
|
||||||
|
import App from './app';
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root');
|
||||||
|
|
||||||
|
if (rootElement) {
|
||||||
|
const root = ReactDOMClient.createRoot(rootElement);
|
||||||
|
|
||||||
|
root.render(
|
||||||
|
<StrictMode>
|
||||||
|
<div className="app">
|
||||||
|
<App />
|
||||||
|
</div>
|
||||||
|
</StrictMode>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
color: white;
|
||||||
|
font-family: sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "build/dist",
|
||||||
|
"module": "esnext",
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
|
"sourceMap": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"rootDir": "src",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strictNullChecks": true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "dockview.demo",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [
|
||||||
|
"dockview"
|
||||||
|
],
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "src/index.tsx",
|
||||||
|
"dependencies": {
|
||||||
|
"dockview": "*",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"uuid": "^9.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.0.28",
|
||||||
|
"@types/react-dom": "^18.0.11",
|
||||||
|
"@types/uuid": "^9.0.0",
|
||||||
|
"typescript": "^4.9.5",
|
||||||
|
"react-scripts": "*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test --env=jsdom",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not ie <= 11",
|
||||||
|
"not op_mini all"
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is added to the
|
||||||
|
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||||
|
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<title>React App</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
You need to enable JavaScript to run this app.
|
||||||
|
</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -0,0 +1,96 @@
|
|||||||
|
.group-control {
|
||||||
|
.action {
|
||||||
|
padding: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: var(--dv-icon-hover-background-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table {
|
||||||
|
table {
|
||||||
|
font-size: 11px;
|
||||||
|
th {
|
||||||
|
padding: 0px 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-container {
|
||||||
|
display: flex;
|
||||||
|
padding: 4px 0px;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
button {
|
||||||
|
height: 25px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #1c254a;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: 1px solid #4c65d4;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #222e62;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-button {
|
||||||
|
margin: 0px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-action {
|
||||||
|
margin: 0px 4px;
|
||||||
|
// display: flex;
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
background-color: #4864dc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group {
|
||||||
|
button {
|
||||||
|
margin-right: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-button {
|
||||||
|
min-width: 50px;
|
||||||
|
padding: 0px 2px;
|
||||||
|
border-radius: 0px;
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
align-items: center;
|
||||||
|
outline: 1px solid #4c65d4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-icon-button {
|
||||||
|
outline: 1px solid #4c65d4;
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 0px;
|
||||||
|
padding: 0px 4px;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
color: gray;
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
431
packages/docs/sandboxes/react/dockview/demo-dockview/src/app.tsx
Normal file
431
packages/docs/sandboxes/react/dockview/demo-dockview/src/app.tsx
Normal file
@ -0,0 +1,431 @@
|
|||||||
|
import {
|
||||||
|
DockviewDefaultTab,
|
||||||
|
DockviewReact,
|
||||||
|
DockviewReadyEvent,
|
||||||
|
IDockviewPanelHeaderProps,
|
||||||
|
IDockviewPanelProps,
|
||||||
|
DockviewApi,
|
||||||
|
} from 'dockview';
|
||||||
|
import * as React from 'react';
|
||||||
|
import './app.scss';
|
||||||
|
import { defaultConfig } from './defaultLayout';
|
||||||
|
import { GridActions } from './gridActions';
|
||||||
|
import { PanelActions } from './panelActions';
|
||||||
|
import { GroupActions } from './groupActions';
|
||||||
|
import { LeftControls, PrefixHeaderControls, RightControls } from './controls';
|
||||||
|
import { Table, usePanelApiMetadata } from './debugPanel';
|
||||||
|
|
||||||
|
const DebugContext = React.createContext<boolean>(false);
|
||||||
|
|
||||||
|
const Option = (props: {
|
||||||
|
title: string;
|
||||||
|
onClick: () => void;
|
||||||
|
value: string;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<span>{`${props.title}: `}</span>
|
||||||
|
<button onClick={props.onClick}>{props.value}</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
default: (props: IDockviewPanelProps) => {
|
||||||
|
const isDebug = React.useContext(DebugContext);
|
||||||
|
const metadata = usePanelApiMetadata(props.api);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: '100%',
|
||||||
|
overflow: 'auto',
|
||||||
|
position: 'relative',
|
||||||
|
padding: 5,
|
||||||
|
border: isDebug ? '2px dashed orange' : '',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '50%',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translate(-50%,-50%)',
|
||||||
|
pointerEvents: 'none',
|
||||||
|
fontSize: '42px',
|
||||||
|
opacity: 0.5,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.api.title}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{isDebug && (
|
||||||
|
<div style={{ fontSize: '0.8em' }}>
|
||||||
|
<Option
|
||||||
|
title="Panel Rendering Mode"
|
||||||
|
value={metadata.renderer.value}
|
||||||
|
onClick={() =>
|
||||||
|
props.api.setRenderer(
|
||||||
|
props.api.renderer === 'always'
|
||||||
|
? 'onlyWhenVisible'
|
||||||
|
: 'always'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Table data={metadata} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
nested: (props: IDockviewPanelProps) => {
|
||||||
|
return (
|
||||||
|
<DockviewReact
|
||||||
|
components={components}
|
||||||
|
onReady={(event: DockviewReadyEvent) => {
|
||||||
|
event.api.addPanel({ id: 'panel_1', component: 'default' });
|
||||||
|
event.api.addPanel({ id: 'panel_2', component: 'default' });
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_3',
|
||||||
|
component: 'default',
|
||||||
|
floating: true,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
className={'dockview-theme-abyss'}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
iframe: (props: IDockviewPanelProps) => {
|
||||||
|
return (
|
||||||
|
<iframe
|
||||||
|
onMouseDown={() => {
|
||||||
|
if (!props.api.isActive) {
|
||||||
|
props.api.setActive();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
}}
|
||||||
|
src="https://dockview.dev"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const headerComponents = {
|
||||||
|
default: (props: IDockviewPanelHeaderProps) => {
|
||||||
|
const onContextMenu = (event: React.MouseEvent) => {
|
||||||
|
event.preventDefault();
|
||||||
|
alert('context menu');
|
||||||
|
};
|
||||||
|
return <DockviewDefaultTab onContextMenu={onContextMenu} {...props} />;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const colors = [
|
||||||
|
'rgba(255,0,0,0.2)',
|
||||||
|
'rgba(0,255,0,0.2)',
|
||||||
|
'rgba(0,0,255,0.2)',
|
||||||
|
'rgba(255,255,0,0.2)',
|
||||||
|
'rgba(0,255,255,0.2)',
|
||||||
|
'rgba(255,0,255,0.2)',
|
||||||
|
];
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
const WatermarkComponent = () => {
|
||||||
|
return <div>custom watermark</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DockviewDemo = (props: { theme?: string }) => {
|
||||||
|
const [logLines, setLogLines] = React.useState<
|
||||||
|
{ text: string; timestamp?: Date; backgroundColor?: string }[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
|
const [panels, setPanels] = React.useState<string[]>([]);
|
||||||
|
const [groups, setGroups] = React.useState<string[]>([]);
|
||||||
|
const [api, setApi] = React.useState<DockviewApi>();
|
||||||
|
|
||||||
|
const [activePanel, setActivePanel] = React.useState<string>();
|
||||||
|
const [activeGroup, setActiveGroup] = React.useState<string>();
|
||||||
|
|
||||||
|
const [pending, setPending] = React.useState<
|
||||||
|
{ text: string; timestamp?: Date }[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
|
const addLogLine = (message: string) => {
|
||||||
|
setPending((line) => [
|
||||||
|
{ text: message, timestamp: new Date() },
|
||||||
|
...line,
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
React.useLayoutEffect(() => {
|
||||||
|
if (pending.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const color = colors[count++ % colors.length];
|
||||||
|
setLogLines((lines) => [
|
||||||
|
...pending.map((_) => ({ ..._, backgroundColor: color })),
|
||||||
|
...lines,
|
||||||
|
]);
|
||||||
|
setPending([]);
|
||||||
|
}, [pending]);
|
||||||
|
|
||||||
|
const onReady = (event: DockviewReadyEvent) => {
|
||||||
|
setApi(event.api);
|
||||||
|
|
||||||
|
event.api.onDidAddPanel((event) => {
|
||||||
|
setPanels((_) => [..._, event.id]);
|
||||||
|
addLogLine(`Panel Added ${event.id}`);
|
||||||
|
});
|
||||||
|
event.api.onDidActivePanelChange((event) => {
|
||||||
|
setActivePanel(event?.id);
|
||||||
|
addLogLine(`Panel Activated ${event?.id}`);
|
||||||
|
});
|
||||||
|
event.api.onDidRemovePanel((event) => {
|
||||||
|
setPanels((_) => {
|
||||||
|
const next = [..._];
|
||||||
|
next.splice(
|
||||||
|
next.findIndex((x) => x === event.id),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
addLogLine(`Panel Removed ${event.id}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
event.api.onDidAddGroup((event) => {
|
||||||
|
setGroups((_) => [..._, event.id]);
|
||||||
|
addLogLine(`Group Added ${event.id}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
event.api.onDidMovePanel((event) => {
|
||||||
|
addLogLine(`Panel Moved ${event.panel.id}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
event.api.onDidRemoveGroup((event) => {
|
||||||
|
setGroups((_) => {
|
||||||
|
const next = [..._];
|
||||||
|
next.splice(
|
||||||
|
next.findIndex((x) => x === event.id),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
addLogLine(`Group Removed ${event.id}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
event.api.onDidActiveGroupChange((event) => {
|
||||||
|
setActiveGroup(event?.id);
|
||||||
|
addLogLine(`Group Activated ${event?.id}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
const state = localStorage.getItem('dv-demo-state');
|
||||||
|
if (state) {
|
||||||
|
try {
|
||||||
|
event.api.fromJSON(JSON.parse(state));
|
||||||
|
return;
|
||||||
|
} catch {
|
||||||
|
localStorage.removeItem('dv-demo-state');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfig(event.api);
|
||||||
|
};
|
||||||
|
|
||||||
|
const [watermark, setWatermark] = React.useState<boolean>(false);
|
||||||
|
|
||||||
|
const [gapCheck, setGapCheck] = React.useState<boolean>(false);
|
||||||
|
|
||||||
|
const css = React.useMemo(() => {
|
||||||
|
if (!gapCheck) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
'--dv-group-gap-size': '0.5rem',
|
||||||
|
'--demo-border': '5px dashed purple',
|
||||||
|
} as React.CSSProperties;
|
||||||
|
}, [gapCheck]);
|
||||||
|
|
||||||
|
const [showLogs, setShowLogs] = React.useState<boolean>(false);
|
||||||
|
const [debug, setDebug] = React.useState<boolean>(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
flexGrow: 1,
|
||||||
|
padding: '8px',
|
||||||
|
backgroundColor: 'rgba(0,0,50,0.25)',
|
||||||
|
borderRadius: '8px',
|
||||||
|
position: 'relative',
|
||||||
|
...css,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<GridActions
|
||||||
|
api={api}
|
||||||
|
toggleCustomWatermark={() => setWatermark(!watermark)}
|
||||||
|
hasCustomWatermark={watermark}
|
||||||
|
/>
|
||||||
|
{api && (
|
||||||
|
<PanelActions
|
||||||
|
api={api}
|
||||||
|
panels={panels}
|
||||||
|
activePanel={activePanel}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{api && (
|
||||||
|
<GroupActions
|
||||||
|
api={api}
|
||||||
|
groups={groups}
|
||||||
|
activeGroup={activeGroup}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{/* <div>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setGapCheck(!gapCheck);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{gapCheck ? 'Disable Gap Check' : 'Enable Gap Check'}
|
||||||
|
</button>
|
||||||
|
</div> */}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="action-container"
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: '4px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setDebug(!debug);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="material-symbols-outlined">
|
||||||
|
engineering
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setShowLogs(!showLogs);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span style={{ paddingRight: '4px' }}>
|
||||||
|
{`${showLogs ? 'Hide' : 'Show'} Events Log`}
|
||||||
|
</span>
|
||||||
|
<span className="material-symbols-outlined">terminal</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flexGrow: 1,
|
||||||
|
height: 0,
|
||||||
|
display: 'flex',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flexGrow: 1,
|
||||||
|
overflow: 'hidden',
|
||||||
|
height: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DebugContext.Provider value={debug}>
|
||||||
|
<DockviewReact
|
||||||
|
components={components}
|
||||||
|
defaultTabComponent={headerComponents.default}
|
||||||
|
rightHeaderActionsComponent={RightControls}
|
||||||
|
leftHeaderActionsComponent={LeftControls}
|
||||||
|
prefixHeaderActionsComponent={PrefixHeaderControls}
|
||||||
|
watermarkComponent={
|
||||||
|
watermark ? WatermarkComponent : undefined
|
||||||
|
}
|
||||||
|
onReady={onReady}
|
||||||
|
className={props.theme || 'dockview-theme-abyss'}
|
||||||
|
/>
|
||||||
|
</DebugContext.Provider>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{showLogs && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: '400px',
|
||||||
|
backgroundColor: 'black',
|
||||||
|
color: 'white',
|
||||||
|
overflow: 'auto',
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
marginLeft: '10px',
|
||||||
|
flexShrink: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{logLines.map((line, i) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: '30px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
fontSize: '13px',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
|
||||||
|
backgroundColor: line.backgroundColor,
|
||||||
|
}}
|
||||||
|
key={i}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
minWidth: '20px',
|
||||||
|
maxWidth: '20px',
|
||||||
|
color: 'gray',
|
||||||
|
borderRight: '1px solid gray',
|
||||||
|
marginRight: '4px',
|
||||||
|
paddingLeft: '4px',
|
||||||
|
height: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{logLines.length - i}
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
{line.timestamp && (
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: '0.7em',
|
||||||
|
padding: '0px 2px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{line.timestamp
|
||||||
|
.toISOString()
|
||||||
|
.substring(11, 23)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<span>{line.text}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DockviewDemo;
|
@ -0,0 +1,148 @@
|
|||||||
|
import { IDockviewHeaderActionsProps } from 'dockview';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { nextId } from './defaultLayout';
|
||||||
|
|
||||||
|
const Icon = (props: {
|
||||||
|
icon: string;
|
||||||
|
title?: string;
|
||||||
|
onClick?: (event: React.MouseEvent) => void;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div title={props.title} className="action" onClick={props.onClick}>
|
||||||
|
<span
|
||||||
|
style={{ fontSize: 'inherit' }}
|
||||||
|
className="material-symbols-outlined"
|
||||||
|
>
|
||||||
|
{props.icon}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const groupControlsComponents: Record<string, React.FC> = {
|
||||||
|
panel_1: () => {
|
||||||
|
return <Icon icon="file_download" />;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RightControls = (props: IDockviewHeaderActionsProps) => {
|
||||||
|
const Component = React.useMemo(() => {
|
||||||
|
if (!props.isGroupActive || !props.activePanel) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return groupControlsComponents[props.activePanel.id];
|
||||||
|
}, [props.isGroupActive, props.activePanel]);
|
||||||
|
|
||||||
|
const [isMaximized, setIsMaximized] = React.useState<boolean>(
|
||||||
|
props.containerApi.hasMaximizedGroup()
|
||||||
|
);
|
||||||
|
|
||||||
|
const [isPopout, setIsPopout] = React.useState<boolean>(
|
||||||
|
props.api.location.type === 'popout'
|
||||||
|
);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const disposable = props.containerApi.onDidMaximizedGroupChange(() => {
|
||||||
|
setIsMaximized(props.containerApi.hasMaximizedGroup());
|
||||||
|
});
|
||||||
|
|
||||||
|
const disposable2 = props.api.onDidLocationChange(() => {
|
||||||
|
setIsPopout(props.api.location.type === 'popout');
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
disposable.dispose();
|
||||||
|
disposable2.dispose();
|
||||||
|
};
|
||||||
|
}, [props.containerApi]);
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
if (props.containerApi.hasMaximizedGroup()) {
|
||||||
|
props.containerApi.exitMaximizedGroup();
|
||||||
|
} else {
|
||||||
|
props.activePanel?.api.maximize();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClick2 = () => {
|
||||||
|
if (props.api.location.type !== 'popout') {
|
||||||
|
props.containerApi.addPopoutGroup(props.group);
|
||||||
|
} else {
|
||||||
|
props.api.moveTo({ position: 'right' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="group-control"
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: '0px 8px',
|
||||||
|
height: '100%',
|
||||||
|
color: 'var(--dv-activegroup-visiblepanel-tab-color)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.isGroupActive && <Icon icon="star" />}
|
||||||
|
{Component && <Component />}
|
||||||
|
<Icon
|
||||||
|
title={isPopout ? 'Close Window' : 'Open In New Window'}
|
||||||
|
icon={isPopout ? 'close_fullscreen' : 'open_in_new'}
|
||||||
|
onClick={onClick2}
|
||||||
|
/>
|
||||||
|
{!isPopout && (
|
||||||
|
<Icon
|
||||||
|
title={isMaximized ? 'Minimize View' : 'Maximize View'}
|
||||||
|
icon={isMaximized ? 'collapse_content' : 'expand_content'}
|
||||||
|
onClick={onClick}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LeftControls = (props: IDockviewHeaderActionsProps) => {
|
||||||
|
const onClick = () => {
|
||||||
|
props.containerApi.addPanel({
|
||||||
|
id: `id_${Date.now().toString()}`,
|
||||||
|
component: 'default',
|
||||||
|
title: `Tab ${nextId()}`,
|
||||||
|
position: {
|
||||||
|
referenceGroup: props.group,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="group-control"
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: '0px 8px',
|
||||||
|
height: '100%',
|
||||||
|
color: 'var(--dv-activegroup-visiblepanel-tab-color)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon onClick={onClick} icon="add" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PrefixHeaderControls = (props: IDockviewHeaderActionsProps) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="group-control"
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: '0px 8px',
|
||||||
|
height: '100%',
|
||||||
|
color: 'var(--dv-activegroup-visiblepanel-tab-color)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon="Menu" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,164 @@
|
|||||||
|
import {
|
||||||
|
DockviewGroupLocation,
|
||||||
|
DockviewPanelApi,
|
||||||
|
DockviewPanelRenderer,
|
||||||
|
} from 'dockview';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
export interface PanelApiMetadata {
|
||||||
|
isActive: {
|
||||||
|
value: boolean;
|
||||||
|
count: number;
|
||||||
|
};
|
||||||
|
isVisible: {
|
||||||
|
value: boolean;
|
||||||
|
count: number;
|
||||||
|
};
|
||||||
|
renderer: {
|
||||||
|
value: DockviewPanelRenderer;
|
||||||
|
count: number;
|
||||||
|
};
|
||||||
|
isGroupActive: {
|
||||||
|
value: boolean;
|
||||||
|
count: number;
|
||||||
|
};
|
||||||
|
groupChanged: {
|
||||||
|
count: number;
|
||||||
|
};
|
||||||
|
location: {
|
||||||
|
value: DockviewGroupLocation;
|
||||||
|
count: number;
|
||||||
|
};
|
||||||
|
didFocus: {
|
||||||
|
count: number;
|
||||||
|
};
|
||||||
|
dimensions: {
|
||||||
|
count: number;
|
||||||
|
value: { height: number; width: number };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Table = (props: { data: PanelApiMetadata }) => {
|
||||||
|
return (
|
||||||
|
<div className="data-table">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>{'Key'}</th>
|
||||||
|
<th>{'Count'}</th>
|
||||||
|
<th>{'Value'}</th>
|
||||||
|
</tr>
|
||||||
|
{Object.entries(props.data).map(([key, value]) => {
|
||||||
|
return (
|
||||||
|
<tr key={key}>
|
||||||
|
<th>{key}</th>
|
||||||
|
<th>{value.count}</th>
|
||||||
|
<th>{JSON.stringify(value.value, null, 4)}</th>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export function usePanelApiMetadata(api: DockviewPanelApi): PanelApiMetadata {
|
||||||
|
const [state, setState] = React.useState<PanelApiMetadata>({
|
||||||
|
isActive: { value: api.isActive, count: 0 },
|
||||||
|
isVisible: { value: api.isVisible, count: 0 },
|
||||||
|
renderer: { value: api.renderer, count: 0 },
|
||||||
|
isGroupActive: { value: api.isGroupActive, count: 0 },
|
||||||
|
groupChanged: { count: 0 },
|
||||||
|
location: { value: api.location, count: 0 },
|
||||||
|
didFocus: { count: 0 },
|
||||||
|
dimensions: {
|
||||||
|
count: 0,
|
||||||
|
value: { height: api.height, width: api.width },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const d1 = api.onDidActiveChange((event) => {
|
||||||
|
setState((_) => ({
|
||||||
|
..._,
|
||||||
|
isActive: {
|
||||||
|
value: event.isActive,
|
||||||
|
count: _.isActive.count + 1,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
const d2 = api.onDidActiveGroupChange((event) => {
|
||||||
|
setState((_) => ({
|
||||||
|
..._,
|
||||||
|
isGroupActive: {
|
||||||
|
value: event.isActive,
|
||||||
|
count: _.isGroupActive.count + 1,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
const d3 = api.onDidDimensionsChange((event) => {
|
||||||
|
setState((_) => ({
|
||||||
|
..._,
|
||||||
|
dimensions: {
|
||||||
|
count: _.dimensions.count + 1,
|
||||||
|
value: { height: event.height, width: event.width },
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
const d4 = api.onDidFocusChange((event) => {
|
||||||
|
setState((_) => ({
|
||||||
|
..._,
|
||||||
|
didFocus: {
|
||||||
|
count: _.didFocus.count + 1,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
const d5 = api.onDidGroupChange((event) => {
|
||||||
|
setState((_) => ({
|
||||||
|
..._,
|
||||||
|
groupChanged: {
|
||||||
|
count: _.groupChanged.count + 1,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
const d7 = api.onDidLocationChange((event) => {
|
||||||
|
setState((_) => ({
|
||||||
|
..._,
|
||||||
|
location: {
|
||||||
|
value: event.location,
|
||||||
|
count: _.location.count + 1,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
const d8 = api.onDidRendererChange((event) => {
|
||||||
|
setState((_) => ({
|
||||||
|
..._,
|
||||||
|
renderer: {
|
||||||
|
value: event.renderer,
|
||||||
|
count: _.renderer.count + 1,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
const d9 = api.onDidVisibilityChange((event) => {
|
||||||
|
setState((_) => ({
|
||||||
|
..._,
|
||||||
|
isVisible: {
|
||||||
|
value: event.isVisible,
|
||||||
|
count: _.isVisible.count + 1,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
d1.dispose();
|
||||||
|
d2.dispose();
|
||||||
|
d3.dispose();
|
||||||
|
d4.dispose();
|
||||||
|
d5.dispose();
|
||||||
|
d7.dispose();
|
||||||
|
d8.dispose();
|
||||||
|
d9.dispose();
|
||||||
|
};
|
||||||
|
}, [api]);
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
import { DockviewApi } from 'dockview';
|
||||||
|
|
||||||
|
export const nextId = (() => {
|
||||||
|
let counter = 0;
|
||||||
|
|
||||||
|
return () => counter++;
|
||||||
|
})();
|
||||||
|
|
||||||
|
export function defaultConfig(api: DockviewApi) {
|
||||||
|
const panel1 = api.addPanel({
|
||||||
|
id: 'panel_1',
|
||||||
|
component: 'default',
|
||||||
|
renderer: 'always',
|
||||||
|
title: 'Panel 1',
|
||||||
|
});
|
||||||
|
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_2',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 2',
|
||||||
|
position: { referencePanel: panel1 },
|
||||||
|
});
|
||||||
|
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_3',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 3',
|
||||||
|
position: { referencePanel: panel1 },
|
||||||
|
});
|
||||||
|
|
||||||
|
const panel4 = api.addPanel({
|
||||||
|
id: 'panel_4',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 4',
|
||||||
|
position: { referencePanel: panel1, direction: 'right' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const panel5 = api.addPanel({
|
||||||
|
id: 'panel_5',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 5',
|
||||||
|
position: { referencePanel: panel4 },
|
||||||
|
});
|
||||||
|
|
||||||
|
const panel6 = api.addPanel({
|
||||||
|
id: 'panel_6',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 6',
|
||||||
|
position: { referencePanel: panel5, direction: 'below' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const panel7 = api.addPanel({
|
||||||
|
id: 'panel_7',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 7',
|
||||||
|
position: { referencePanel: panel6, direction: 'left' },
|
||||||
|
});
|
||||||
|
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel8',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 8',
|
||||||
|
position: { referencePanel: panel7, direction: 'below' },
|
||||||
|
});
|
||||||
|
|
||||||
|
panel1.api.setActive();
|
||||||
|
}
|
@ -0,0 +1,216 @@
|
|||||||
|
import { DockviewApi } from 'dockview';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { defaultConfig, nextId } from './defaultLayout';
|
||||||
|
|
||||||
|
import { createRoot, Root } from 'react-dom/client';
|
||||||
|
import { PanelBuilder } from './panelBuilder';
|
||||||
|
|
||||||
|
let mount = document.querySelector('.popover-anchor') as HTMLElement | null;
|
||||||
|
|
||||||
|
if (!mount) {
|
||||||
|
mount = document.createElement('div');
|
||||||
|
mount.className = 'popover-anchor';
|
||||||
|
document.body.insertBefore(mount, document.body.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
const PopoverComponent = (props: {
|
||||||
|
close: () => void;
|
||||||
|
component: React.FC<{ close: () => void }>;
|
||||||
|
}) => {
|
||||||
|
const ref = React.useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const handler = (ev: MouseEvent) => {
|
||||||
|
let target = ev.target as HTMLElement;
|
||||||
|
|
||||||
|
while (target.parentElement) {
|
||||||
|
if (target === ref.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
target = target.parentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
props.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('mousedown', handler);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('mousedown', handler);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
zIndex: 9999,
|
||||||
|
height: '100%',
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '50%',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translate(-50%,-50%)',
|
||||||
|
backgroundColor: 'black',
|
||||||
|
color: 'white',
|
||||||
|
padding: 10,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<props.component close={props.close} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
function usePopover() {
|
||||||
|
return {
|
||||||
|
open: (Component: React.FC<{ close: () => void }>) => {
|
||||||
|
const el = document.createElement('div');
|
||||||
|
mount!.appendChild(el);
|
||||||
|
const root = createRoot(el);
|
||||||
|
|
||||||
|
root.render(
|
||||||
|
<PopoverComponent
|
||||||
|
component={Component}
|
||||||
|
close={() => {
|
||||||
|
root.unmount();
|
||||||
|
el.remove();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GridActions = (props: {
|
||||||
|
api?: DockviewApi;
|
||||||
|
hasCustomWatermark: boolean;
|
||||||
|
toggleCustomWatermark: () => void;
|
||||||
|
}) => {
|
||||||
|
const onClear = () => {
|
||||||
|
props.api?.clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onLoad = () => {
|
||||||
|
const state = localStorage.getItem('dv-demo-state');
|
||||||
|
if (state) {
|
||||||
|
try {
|
||||||
|
props.api?.fromJSON(JSON.parse(state));
|
||||||
|
} catch (err) {
|
||||||
|
console.error('failed to load state', err);
|
||||||
|
localStorage.removeItem('dv-demo-state');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSave = () => {
|
||||||
|
if (props.api) {
|
||||||
|
console.log(props.api.toJSON());
|
||||||
|
localStorage.setItem(
|
||||||
|
'dv-demo-state',
|
||||||
|
JSON.stringify(props.api.toJSON())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onReset = () => {
|
||||||
|
if (props.api) {
|
||||||
|
try {
|
||||||
|
props.api.clear();
|
||||||
|
defaultConfig(props.api);
|
||||||
|
} catch (err) {
|
||||||
|
localStorage.removeItem('dv-demo-state');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const popover = usePopover();
|
||||||
|
|
||||||
|
const onAddPanel = (options?: { advanced: boolean }) => {
|
||||||
|
if (options?.advanced) {
|
||||||
|
popover.open(({ close }) => {
|
||||||
|
return <PanelBuilder api={props.api!} done={close} />;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
props.api?.addPanel({
|
||||||
|
id: `id_${Date.now().toString()}`,
|
||||||
|
component: 'default',
|
||||||
|
title: `Tab ${nextId()}`,
|
||||||
|
renderer: 'always',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onAddGroup = () => {
|
||||||
|
props.api?.addGroup();
|
||||||
|
};
|
||||||
|
|
||||||
|
const [gap, setGap] = React.useState(0);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
props.api?.setGap(gap);
|
||||||
|
}, [gap, props.api]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="action-container">
|
||||||
|
<div className="button-group">
|
||||||
|
<button className="text-button" onClick={() => onAddPanel()}>
|
||||||
|
Add Panel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="demo-icon-button"
|
||||||
|
onClick={() => onAddPanel({ advanced: true })}
|
||||||
|
>
|
||||||
|
<span className="material-symbols-outlined">tune</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<button className="text-button" onClick={onAddGroup}>
|
||||||
|
Add Group
|
||||||
|
</button>
|
||||||
|
<span className="button-action">
|
||||||
|
<button
|
||||||
|
className={
|
||||||
|
props.hasCustomWatermark
|
||||||
|
? 'demo-button selected'
|
||||||
|
: 'demo-button'
|
||||||
|
}
|
||||||
|
onClick={props.toggleCustomWatermark}
|
||||||
|
>
|
||||||
|
Use Custom Watermark
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
<button className="text-button" onClick={onClear}>
|
||||||
|
Clear
|
||||||
|
</button>
|
||||||
|
<button className="text-button" onClick={onLoad}>
|
||||||
|
Load
|
||||||
|
</button>
|
||||||
|
<button className="text-button" onClick={onSave}>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
<button className="text-button" onClick={onReset}>
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
|
<span style={{ flexGrow: 1 }} />
|
||||||
|
<div style={{ display: 'flex' }}>
|
||||||
|
<span style={{ paddingRight: '4px' }}>Group Gap</span>
|
||||||
|
<input
|
||||||
|
style={{ width: 40 }}
|
||||||
|
type="number"
|
||||||
|
min={0}
|
||||||
|
max={99}
|
||||||
|
step={1}
|
||||||
|
value={gap}
|
||||||
|
onChange={(event) => setGap(Number(event.target.value))}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,189 @@
|
|||||||
|
import {
|
||||||
|
DockviewApi,
|
||||||
|
DockviewGroupLocation,
|
||||||
|
DockviewGroupPanel,
|
||||||
|
} from 'dockview';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
const GroupAction = (props: {
|
||||||
|
groupId: string;
|
||||||
|
groups: string[];
|
||||||
|
api: DockviewApi;
|
||||||
|
activeGroup?: string;
|
||||||
|
}) => {
|
||||||
|
const onClick = () => {
|
||||||
|
props.api?.getGroup(props.groupId)?.focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
const isActive = props.activeGroup === props.groupId;
|
||||||
|
|
||||||
|
const [group, setGroup] = React.useState<DockviewGroupPanel | undefined>(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const disposable = props.api.onDidLayoutFromJSON(() => {
|
||||||
|
setGroup(props.api.getGroup(props.groupId));
|
||||||
|
});
|
||||||
|
|
||||||
|
setGroup(props.api.getGroup(props.groupId));
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
disposable.dispose();
|
||||||
|
};
|
||||||
|
}, [props.api, props.groupId]);
|
||||||
|
|
||||||
|
const [location, setLocation] =
|
||||||
|
React.useState<DockviewGroupLocation | null>(null);
|
||||||
|
const [isMaximized, setIsMaximized] = React.useState<boolean>(false);
|
||||||
|
const [isVisible, setIsVisible] = React.useState<boolean>(true);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!group) {
|
||||||
|
setLocation(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const disposable = group.api.onDidLocationChange((event) => {
|
||||||
|
setLocation(event.location);
|
||||||
|
});
|
||||||
|
|
||||||
|
const disposable2 = props.api.onDidMaximizedGroupChange(() => {
|
||||||
|
setIsMaximized(group.api.isMaximized());
|
||||||
|
});
|
||||||
|
|
||||||
|
const disposable3 = group.api.onDidVisibilityChange(() => {
|
||||||
|
setIsVisible(group.api.isVisible);
|
||||||
|
});
|
||||||
|
|
||||||
|
setLocation(group.api.location);
|
||||||
|
setIsMaximized(group.api.isMaximized());
|
||||||
|
setIsVisible(group.api.isVisible);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
disposable.dispose();
|
||||||
|
disposable2.dispose();
|
||||||
|
disposable3.dispose();
|
||||||
|
};
|
||||||
|
}, [group]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="button-action">
|
||||||
|
<div style={{ display: 'flex' }}>
|
||||||
|
<button
|
||||||
|
onClick={onClick}
|
||||||
|
className={
|
||||||
|
isActive ? 'demo-button selected' : 'demo-button'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{props.groupId}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div style={{ display: 'flex' }}>
|
||||||
|
<button
|
||||||
|
className={
|
||||||
|
location?.type === 'floating'
|
||||||
|
? 'demo-icon-button selected'
|
||||||
|
: 'demo-icon-button'
|
||||||
|
}
|
||||||
|
onClick={() => {
|
||||||
|
if (group) {
|
||||||
|
|
||||||
|
props.api.addFloatingGroup(group, {
|
||||||
|
width: 400,
|
||||||
|
height: 300,
|
||||||
|
x: 50,
|
||||||
|
y: 50,
|
||||||
|
position: {
|
||||||
|
bottom: 50,
|
||||||
|
right: 50,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="material-symbols-outlined">ad_group</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={
|
||||||
|
location?.type === 'popout'
|
||||||
|
? 'demo-icon-button selected'
|
||||||
|
: 'demo-icon-button'
|
||||||
|
}
|
||||||
|
onClick={() => {
|
||||||
|
if (group) {
|
||||||
|
props.api.addPopoutGroup(group);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="material-symbols-outlined">
|
||||||
|
open_in_new
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={
|
||||||
|
isMaximized
|
||||||
|
? 'demo-icon-button selected'
|
||||||
|
: 'demo-icon-button'
|
||||||
|
}
|
||||||
|
onClick={() => {
|
||||||
|
if (group) {
|
||||||
|
if (group.api.isMaximized()) {
|
||||||
|
group.api.exitMaximized();
|
||||||
|
} else {
|
||||||
|
group.api.maximize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="material-symbols-outlined">
|
||||||
|
fullscreen
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="demo-icon-button"
|
||||||
|
onClick={() => {
|
||||||
|
console.log(group);
|
||||||
|
if (group) {
|
||||||
|
if (group.api.isVisible) {
|
||||||
|
group.api.setVisible(false);
|
||||||
|
} else {
|
||||||
|
group.api.setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="material-symbols-outlined">
|
||||||
|
{isVisible ? 'visibility' : 'visibility_off'}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="demo-icon-button"
|
||||||
|
onClick={() => {
|
||||||
|
const panel = props.api?.getGroup(props.groupId);
|
||||||
|
panel?.api.close();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="material-symbols-outlined">close</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GroupActions = (props: {
|
||||||
|
groups: string[];
|
||||||
|
api: DockviewApi;
|
||||||
|
activeGroup?: string;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="action-container">
|
||||||
|
{props.groups.map((groupId) => {
|
||||||
|
return (
|
||||||
|
<GroupAction key={groupId} {...props} groupId={groupId} />
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,20 @@
|
|||||||
|
import { StrictMode } from 'react';
|
||||||
|
import * as ReactDOMClient from 'react-dom/client';
|
||||||
|
import './styles.css';
|
||||||
|
import 'dockview/dist/styles/dockview.css';
|
||||||
|
|
||||||
|
import App from './app';
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root');
|
||||||
|
|
||||||
|
if (rootElement) {
|
||||||
|
const root = ReactDOMClient.createRoot(rootElement);
|
||||||
|
|
||||||
|
root.render(
|
||||||
|
<StrictMode>
|
||||||
|
<div className="app">
|
||||||
|
<App />
|
||||||
|
</div>
|
||||||
|
</StrictMode>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,138 @@
|
|||||||
|
import { DockviewApi, IDockviewPanel } from 'dockview';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
const PanelAction = (props: {
|
||||||
|
panels: string[];
|
||||||
|
api: DockviewApi;
|
||||||
|
activePanel?: string;
|
||||||
|
panelId: string;
|
||||||
|
}) => {
|
||||||
|
const onClick = () => {
|
||||||
|
props.api.getPanel(props.panelId)?.focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const panel = props.api.getPanel(props.panelId);
|
||||||
|
if (panel) {
|
||||||
|
const disposable = panel.api.onDidVisibilityChange((event) => {
|
||||||
|
setVisible(event.isVisible);
|
||||||
|
});
|
||||||
|
setVisible(panel.api.isVisible);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
disposable.dispose();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [props.api, props.panelId]);
|
||||||
|
|
||||||
|
const [panel, setPanel] = React.useState<IDockviewPanel | undefined>(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const list = [
|
||||||
|
props.api.onDidLayoutFromJSON(() => {
|
||||||
|
setPanel(props.api.getPanel(props.panelId));
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (panel) {
|
||||||
|
const disposable = panel.api.onDidVisibilityChange((event) => {
|
||||||
|
setVisible(event.isVisible);
|
||||||
|
});
|
||||||
|
setVisible(panel.api.isVisible);
|
||||||
|
|
||||||
|
list.push(disposable);
|
||||||
|
}
|
||||||
|
|
||||||
|
setPanel(props.api.getPanel(props.panelId));
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
list.forEach((l) => l.dispose());
|
||||||
|
};
|
||||||
|
}, [props.api, props.panelId]);
|
||||||
|
|
||||||
|
const [visible, setVisible] = React.useState<boolean>(true);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="button-action">
|
||||||
|
<div style={{ display: 'flex' }}>
|
||||||
|
<button
|
||||||
|
className={
|
||||||
|
props.activePanel === props.panelId
|
||||||
|
? 'demo-button selected'
|
||||||
|
: 'demo-button'
|
||||||
|
}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{props.panelId}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div style={{ display: 'flex' }}>
|
||||||
|
<button
|
||||||
|
className="demo-icon-button"
|
||||||
|
onClick={() => {
|
||||||
|
const panel = props.api.getPanel(props.panelId);
|
||||||
|
if (panel) {
|
||||||
|
props.api.addFloatingGroup(panel, {
|
||||||
|
position: {
|
||||||
|
width: 400,
|
||||||
|
height: 300,
|
||||||
|
bottom: 50,
|
||||||
|
right: 50,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="material-symbols-outlined">ad_group</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="demo-icon-button"
|
||||||
|
onClick={() => {
|
||||||
|
const panel = props.api.getPanel(props.panelId);
|
||||||
|
if (panel) {
|
||||||
|
props.api.addPopoutGroup(panel);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="material-symbols-outlined">
|
||||||
|
open_in_new
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="demo-icon-button"
|
||||||
|
onClick={() => {
|
||||||
|
const panel = props.api.getPanel(props.panelId);
|
||||||
|
panel?.api.close();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="material-symbols-outlined">close</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
title="Panel visiblity cannot be edited manually."
|
||||||
|
disabled={true}
|
||||||
|
className="demo-icon-button"
|
||||||
|
>
|
||||||
|
<span className="material-symbols-outlined">
|
||||||
|
{visible ? 'visibility' : 'visibility_off'}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PanelActions = (props: {
|
||||||
|
panels: string[];
|
||||||
|
api: DockviewApi;
|
||||||
|
activePanel?: string;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="action-container">
|
||||||
|
{props.panels.map((id) => {
|
||||||
|
return <PanelAction key={id} {...props} panelId={id} />;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,115 @@
|
|||||||
|
import { DockviewApi } from 'dockview';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { nextId } from './defaultLayout';
|
||||||
|
|
||||||
|
export const PanelBuilder = (props: { api: DockviewApi; done: () => void }) => {
|
||||||
|
const [parameters, setParameters] = React.useState<{
|
||||||
|
initialWidth?: number;
|
||||||
|
initialHeight?: number;
|
||||||
|
maximumHeight?: number;
|
||||||
|
maximumWidth?: number;
|
||||||
|
minimumHeight?: number;
|
||||||
|
minimumWidth?: number;
|
||||||
|
}>({});
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateColumns: '1fr 1fr',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>{'Initial Width'}</div>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={parameters.initialWidth}
|
||||||
|
onChange={(event) =>
|
||||||
|
setParameters((_) => ({
|
||||||
|
..._,
|
||||||
|
initialWidth: Number(event.target.value),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div>{'Initial Height'}</div>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={parameters.initialHeight}
|
||||||
|
onChange={(event) =>
|
||||||
|
setParameters((_) => ({
|
||||||
|
..._,
|
||||||
|
initialHeight: Number(event.target.value),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div>{'Maximum Width'}</div>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={parameters.maximumWidth}
|
||||||
|
onChange={(event) =>
|
||||||
|
setParameters((_) => ({
|
||||||
|
..._,
|
||||||
|
maximumWidth: Number(event.target.value),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div>{'Maximum Height'}</div>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={parameters.maximumHeight}
|
||||||
|
onChange={(event) =>
|
||||||
|
setParameters((_) => ({
|
||||||
|
..._,
|
||||||
|
maximumHeight: Number(event.target.value),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div>{'Minimum Width'}</div>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={parameters.minimumWidth}
|
||||||
|
onChange={(event) =>
|
||||||
|
setParameters((_) => ({
|
||||||
|
..._,
|
||||||
|
minimumWidth: Number(event.target.value),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div>{'Minimum Height'}</div>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={parameters.minimumHeight}
|
||||||
|
onChange={(event) =>
|
||||||
|
setParameters((_) => ({
|
||||||
|
..._,
|
||||||
|
minimumHeight: Number(event.target.value),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
props.done();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
props.api?.addPanel({
|
||||||
|
id: `id_${Date.now().toString()}`,
|
||||||
|
component: 'default',
|
||||||
|
title: `Tab ${nextId()}`,
|
||||||
|
renderer: 'always',
|
||||||
|
...parameters,
|
||||||
|
});
|
||||||
|
|
||||||
|
props.done();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Go
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,16 @@
|
|||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
color: white;
|
||||||
|
font-family: sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "build/dist",
|
||||||
|
"module": "esnext",
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
|
"sourceMap": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"rootDir": "src",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strictNullChecks": true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "dockview.dnd-events",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [
|
||||||
|
"dockview"
|
||||||
|
],
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "src/index.tsx",
|
||||||
|
"dependencies": {
|
||||||
|
"dockview": "*",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.0.28",
|
||||||
|
"@types/react-dom": "^18.0.11",
|
||||||
|
"typescript": "^4.9.5",
|
||||||
|
"react-scripts": "*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test --env=jsdom",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not ie <= 11",
|
||||||
|
"not op_mini all"
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is added to the
|
||||||
|
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||||
|
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<title>React App</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
You need to enable JavaScript to run this app.
|
||||||
|
</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
142
packages/docs/sandboxes/react/dockview/dnd-events/src/app.tsx
Normal file
142
packages/docs/sandboxes/react/dockview/dnd-events/src/app.tsx
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import {
|
||||||
|
DockviewApi,
|
||||||
|
DockviewReact,
|
||||||
|
DockviewReadyEvent,
|
||||||
|
IDockviewPanelProps,
|
||||||
|
} from 'dockview';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
const Default = (props: IDockviewPanelProps) => {
|
||||||
|
return (
|
||||||
|
<div style={{ height: '100%' }}>
|
||||||
|
<div>{props.api.title}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
default: Default,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Component = (props: { theme?: string }) => {
|
||||||
|
const [disablePanelDrag, setDisablePanelDrag] =
|
||||||
|
React.useState<boolean>(false);
|
||||||
|
const [disableGroupDrag, setDisableGroupDrag] =
|
||||||
|
React.useState<boolean>(false);
|
||||||
|
const [disableOverlay, setDisableOverlay] = React.useState<boolean>(false);
|
||||||
|
|
||||||
|
const [api, setApi] = React.useState<DockviewApi>();
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!api) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const disposables = [
|
||||||
|
api.onWillDragPanel((e) => {
|
||||||
|
if (!disablePanelDrag) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.nativeEvent.preventDefault();
|
||||||
|
}),
|
||||||
|
|
||||||
|
api.onWillDragGroup((e) => {
|
||||||
|
if (!disableGroupDrag) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.nativeEvent.preventDefault();
|
||||||
|
}),
|
||||||
|
api.onWillShowOverlay((e) => {
|
||||||
|
console.log(e);
|
||||||
|
|
||||||
|
if (!disableOverlay) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
}),
|
||||||
|
|
||||||
|
api.onWillDrop((e) => {
|
||||||
|
//
|
||||||
|
}),
|
||||||
|
|
||||||
|
api.onDidDrop((e) => {
|
||||||
|
//
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
disposables.forEach((disposable) => {
|
||||||
|
disposable.dispose();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}, [api, disablePanelDrag, disableGroupDrag, disableOverlay]);
|
||||||
|
|
||||||
|
const onReady = (event: DockviewReadyEvent) => {
|
||||||
|
setApi(event.api);
|
||||||
|
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_1',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_2',
|
||||||
|
component: 'default',
|
||||||
|
position: {
|
||||||
|
direction: 'right',
|
||||||
|
referencePanel: 'panel_1',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_3',
|
||||||
|
component: 'default',
|
||||||
|
position: {
|
||||||
|
direction: 'below',
|
||||||
|
referencePanel: 'panel_1',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_4',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_5',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{ display: 'flex', flexDirection: 'column', height: '100%' }}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
onClick={() => setDisablePanelDrag(!disablePanelDrag)}
|
||||||
|
>{`Panel Drag: ${
|
||||||
|
disablePanelDrag ? 'disabled' : 'enabled'
|
||||||
|
}`}</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setDisableGroupDrag(!disableGroupDrag)}
|
||||||
|
>{`Group Drag: ${
|
||||||
|
disableGroupDrag ? 'disabled' : 'enabled'
|
||||||
|
}`}</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setDisableOverlay(!disableOverlay)}
|
||||||
|
>{`Overlay: ${
|
||||||
|
disableOverlay ? 'disabled' : 'enabled'
|
||||||
|
}`}</button>
|
||||||
|
</div>
|
||||||
|
<div style={{ flexGrow: 1 }}>
|
||||||
|
<DockviewReact
|
||||||
|
className={`${props.theme || 'dockview-theme-abyss'}`}
|
||||||
|
onReady={onReady}
|
||||||
|
components={components}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Component;
|
@ -0,0 +1,20 @@
|
|||||||
|
import { StrictMode } from 'react';
|
||||||
|
import * as ReactDOMClient from 'react-dom/client';
|
||||||
|
import './styles.css';
|
||||||
|
import 'dockview/dist/styles/dockview.css';
|
||||||
|
|
||||||
|
import App from './app';
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root');
|
||||||
|
|
||||||
|
if (rootElement) {
|
||||||
|
const root = ReactDOMClient.createRoot(rootElement);
|
||||||
|
|
||||||
|
root.render(
|
||||||
|
<StrictMode>
|
||||||
|
<div className="app">
|
||||||
|
<App />
|
||||||
|
</div>
|
||||||
|
</StrictMode>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
color: white;
|
||||||
|
font-family: sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "build/dist",
|
||||||
|
"module": "esnext",
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
|
"sourceMap": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"rootDir": "src",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strictNullChecks": true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "dockview.dnd-external",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [
|
||||||
|
"dockview"
|
||||||
|
],
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "src/index.tsx",
|
||||||
|
"dependencies": {
|
||||||
|
"dockview": "*",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.0.28",
|
||||||
|
"@types/react-dom": "^18.0.11",
|
||||||
|
"typescript": "^4.9.5",
|
||||||
|
"react-scripts": "*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test --env=jsdom",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not ie <= 11",
|
||||||
|
"not op_mini all"
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is added to the
|
||||||
|
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||||
|
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<title>React App</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
You need to enable JavaScript to run this app.
|
||||||
|
</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
192
packages/docs/sandboxes/react/dockview/dnd-external/src/app.tsx
Normal file
192
packages/docs/sandboxes/react/dockview/dnd-external/src/app.tsx
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
import {
|
||||||
|
DockviewApi,
|
||||||
|
DockviewDndOverlayEvent,
|
||||||
|
DockviewDidDropEvent,
|
||||||
|
DockviewReact,
|
||||||
|
DockviewReadyEvent,
|
||||||
|
IDockviewPanelProps,
|
||||||
|
positionToDirection,
|
||||||
|
} from 'dockview';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
default: (props: IDockviewPanelProps<{ title: string }>) => {
|
||||||
|
return (
|
||||||
|
<div style={{ padding: '20px' }}>
|
||||||
|
<div>{props.params.title}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const DraggableElement = () => (
|
||||||
|
<span
|
||||||
|
tabIndex={-1}
|
||||||
|
onDragStart={(event) => {
|
||||||
|
if (event.dataTransfer) {
|
||||||
|
event.dataTransfer.effectAllowed = 'move';
|
||||||
|
|
||||||
|
event.dataTransfer.setData('text/plain', 'nothing');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'orange',
|
||||||
|
padding: '0px 8px',
|
||||||
|
borderRadius: '4px',
|
||||||
|
width: '100px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
}}
|
||||||
|
draggable={true}
|
||||||
|
>
|
||||||
|
Drag me into the dock
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
|
const DndDockview = (props: { renderVisibleOnly: boolean; theme?: string }) => {
|
||||||
|
const [api, setApi] = React.useState<DockviewApi>();
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!api) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_1',
|
||||||
|
component: 'default',
|
||||||
|
params: {
|
||||||
|
title: 'Panel 1',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_2',
|
||||||
|
component: 'default',
|
||||||
|
params: {
|
||||||
|
title: 'Panel 2',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_3',
|
||||||
|
component: 'default',
|
||||||
|
params: {
|
||||||
|
title: 'Panel 3',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_4',
|
||||||
|
component: 'default',
|
||||||
|
params: {
|
||||||
|
title: 'Panel 4',
|
||||||
|
},
|
||||||
|
position: { referencePanel: 'panel_1', direction: 'right' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const panelDragDisposable = api.onWillDragPanel((event) => {
|
||||||
|
const dataTransfer = event.nativeEvent.dataTransfer;
|
||||||
|
|
||||||
|
if (dataTransfer) {
|
||||||
|
dataTransfer.setData(
|
||||||
|
'text/plain',
|
||||||
|
'Some custom panel data transfer data'
|
||||||
|
);
|
||||||
|
dataTransfer.setData(
|
||||||
|
'text/json',
|
||||||
|
'{text: "Some custom panel data transfer data"}'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const groupDragDisposable = api.onWillDragGroup((event) => {
|
||||||
|
const dataTransfer = event.nativeEvent.dataTransfer;
|
||||||
|
|
||||||
|
if (dataTransfer) {
|
||||||
|
dataTransfer.setData(
|
||||||
|
'text/plain',
|
||||||
|
'Some custom group data transfer data'
|
||||||
|
);
|
||||||
|
dataTransfer.setData(
|
||||||
|
'text/json',
|
||||||
|
'{text: "Some custom group data transfer data"}'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
panelDragDisposable.dispose();
|
||||||
|
groupDragDisposable.dispose();
|
||||||
|
};
|
||||||
|
}, [api]);
|
||||||
|
|
||||||
|
const onReady = (event: DockviewReadyEvent) => {
|
||||||
|
setApi(event.api);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDidDrop = (event: DockviewDidDropEvent) => {
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'test',
|
||||||
|
component: 'default',
|
||||||
|
position: {
|
||||||
|
direction: positionToDirection(event.position),
|
||||||
|
referenceGroup: event.group || undefined,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const showDndOverlay = (event: DockviewDndOverlayEvent) => {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDrop = (event: React.DragEvent) => {
|
||||||
|
const dataTransfer = event.dataTransfer;
|
||||||
|
|
||||||
|
let text = 'The following dataTransfer data was found:\n';
|
||||||
|
|
||||||
|
for (let i = 0; i < dataTransfer.items.length; i++) {
|
||||||
|
const item = dataTransfer.items[i];
|
||||||
|
const value = dataTransfer.getData(item.type);
|
||||||
|
text += `type=${item.type},data=${value}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
alert(text);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
height: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ margin: '2px 0px' }}>
|
||||||
|
<DraggableElement />
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: '0px 4px',
|
||||||
|
backgroundColor: 'black',
|
||||||
|
borderRadius: '2px',
|
||||||
|
color: 'white',
|
||||||
|
}}
|
||||||
|
onDrop={onDrop}
|
||||||
|
>
|
||||||
|
Drop a tab or group here to inspect the attached metadata
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DockviewReact
|
||||||
|
components={components}
|
||||||
|
onReady={onReady}
|
||||||
|
className={`${props.theme || 'dockview-theme-abyss'}`}
|
||||||
|
onDidDrop={onDidDrop}
|
||||||
|
showDndOverlay={showDndOverlay}
|
||||||
|
rootOverlayModel={{
|
||||||
|
size: { value: 100, type: 'pixels' },
|
||||||
|
activationSize: { value: 5, type: 'percentage' },
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DndDockview;
|
@ -0,0 +1,20 @@
|
|||||||
|
import { StrictMode } from 'react';
|
||||||
|
import * as ReactDOMClient from 'react-dom/client';
|
||||||
|
import './styles.css';
|
||||||
|
import 'dockview/dist/styles/dockview.css';
|
||||||
|
|
||||||
|
import App from './app';
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root');
|
||||||
|
|
||||||
|
if (rootElement) {
|
||||||
|
const root = ReactDOMClient.createRoot(rootElement);
|
||||||
|
|
||||||
|
root.render(
|
||||||
|
<StrictMode>
|
||||||
|
<div className="app">
|
||||||
|
<App />
|
||||||
|
</div>
|
||||||
|
</StrictMode>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
color: white;
|
||||||
|
font-family: sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "build/dist",
|
||||||
|
"module": "esnext",
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
|
"sourceMap": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"rootDir": "src",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strictNullChecks": true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "dockview.floating-groups",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [
|
||||||
|
"dockview"
|
||||||
|
],
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "src/index.tsx",
|
||||||
|
"dependencies": {
|
||||||
|
"dockview": "*",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.0.28",
|
||||||
|
"@types/react-dom": "^18.0.11",
|
||||||
|
"typescript": "^4.9.5",
|
||||||
|
"react-scripts": "*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test --env=jsdom",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not ie <= 11",
|
||||||
|
"not op_mini all"
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is added to the
|
||||||
|
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||||
|
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<title>React App</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
You need to enable JavaScript to run this app.
|
||||||
|
</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -0,0 +1,301 @@
|
|||||||
|
import {
|
||||||
|
DockviewApi,
|
||||||
|
DockviewGroupPanel,
|
||||||
|
DockviewReact,
|
||||||
|
DockviewReadyEvent,
|
||||||
|
IDockviewHeaderActionsProps,
|
||||||
|
IDockviewPanelProps,
|
||||||
|
SerializedDockview,
|
||||||
|
} from 'dockview';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Icon } from './utils';
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
default: (props: IDockviewPanelProps<{ title: string }>) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: '100%',
|
||||||
|
padding: '20px',
|
||||||
|
background: 'var(--dv-group-view-background-color)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.params.title}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const counter = (() => {
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
return {
|
||||||
|
next: () => ++i,
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
function loadDefaultLayout(api: DockviewApi) {
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_1',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_2',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_3',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
|
||||||
|
const panel4 = api.addPanel({
|
||||||
|
id: 'panel_4',
|
||||||
|
component: 'default',
|
||||||
|
floating: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_5',
|
||||||
|
component: 'default',
|
||||||
|
floating: false,
|
||||||
|
position: { referencePanel: panel4 },
|
||||||
|
});
|
||||||
|
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_6',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let panelCount = 0;
|
||||||
|
|
||||||
|
function addPanel(api: DockviewApi) {
|
||||||
|
api.addPanel({
|
||||||
|
id: (++panelCount).toString(),
|
||||||
|
title: `Tab ${panelCount}`,
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addFloatingPanel2(api: DockviewApi) {
|
||||||
|
api.addPanel({
|
||||||
|
id: (++panelCount).toString(),
|
||||||
|
title: `Tab ${panelCount}`,
|
||||||
|
component: 'default',
|
||||||
|
floating: { width: 250, height: 150, left: 50, top: 50 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeParse<T>(value: any): T | null {
|
||||||
|
try {
|
||||||
|
return JSON.parse(value) as T;
|
||||||
|
} catch (err) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const useLocalStorage = <T,>(
|
||||||
|
key: string
|
||||||
|
): [T | null, (setter: T | null) => void] => {
|
||||||
|
const [state, setState] = React.useState<T | null>(
|
||||||
|
safeParse(localStorage.getItem(key))
|
||||||
|
);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const _state = localStorage.getItem('key');
|
||||||
|
try {
|
||||||
|
if (_state !== null) {
|
||||||
|
setState(JSON.parse(_state));
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}, [key]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
state,
|
||||||
|
(_state: T | null) => {
|
||||||
|
if (_state === null) {
|
||||||
|
localStorage.removeItem(key);
|
||||||
|
} else {
|
||||||
|
localStorage.setItem(key, JSON.stringify(_state));
|
||||||
|
setState(_state);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DockviewPersistence = (props: { theme?: string }) => {
|
||||||
|
const [api, setApi] = React.useState<DockviewApi>();
|
||||||
|
const [layout, setLayout] =
|
||||||
|
useLocalStorage<SerializedDockview>('floating.layout');
|
||||||
|
|
||||||
|
const [disableFloatingGroups, setDisableFloatingGroups] =
|
||||||
|
React.useState<boolean>(false);
|
||||||
|
|
||||||
|
const load = (api: DockviewApi) => {
|
||||||
|
api.clear();
|
||||||
|
if (layout) {
|
||||||
|
try {
|
||||||
|
api.fromJSON(layout);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
api.clear();
|
||||||
|
loadDefaultLayout(api);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loadDefaultLayout(api);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onReady = (event: DockviewReadyEvent) => {
|
||||||
|
load(event.api);
|
||||||
|
setApi(event.api);
|
||||||
|
};
|
||||||
|
|
||||||
|
const [options, setOptions] = React.useState<
|
||||||
|
'boundedWithinViewport' | undefined
|
||||||
|
>(undefined);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
height: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ height: '25px' }}>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
if (api) {
|
||||||
|
setLayout(api.toJSON());
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
if (api) {
|
||||||
|
load(api);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Load
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
api!.clear();
|
||||||
|
setLayout(null);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
addFloatingPanel2(api!);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add Floating Group
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setOptions(
|
||||||
|
options === undefined
|
||||||
|
? 'boundedWithinViewport'
|
||||||
|
: undefined
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{`Bounds: ${options ? 'Within' : 'Overflow'}`}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setDisableFloatingGroups((x) => !x);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{`${
|
||||||
|
disableFloatingGroups ? 'Enable' : 'Disable'
|
||||||
|
} floating groups`}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flexGrow: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DockviewReact
|
||||||
|
onReady={onReady}
|
||||||
|
components={components}
|
||||||
|
watermarkComponent={Watermark}
|
||||||
|
leftHeaderActionsComponent={LeftComponent}
|
||||||
|
rightHeaderActionsComponent={RightComponent}
|
||||||
|
disableFloatingGroups={disableFloatingGroups}
|
||||||
|
floatingGroupBounds={options}
|
||||||
|
className={`${props.theme || 'dockview-theme-abyss'}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const LeftComponent = (props: IDockviewHeaderActionsProps) => {
|
||||||
|
const onClick = () => {
|
||||||
|
addPanel(props.containerApi);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div style={{ height: '100%', color: 'white', padding: '0px 4px' }}>
|
||||||
|
<Icon onClick={onClick} icon={'add'} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const RightComponent = (props: IDockviewHeaderActionsProps) => {
|
||||||
|
const [floating, setFloating] = React.useState<boolean>(
|
||||||
|
props.api.location.type === 'floating'
|
||||||
|
);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const disposable = props.group.api.onDidLocationChange((event) => {
|
||||||
|
setFloating(event.location.type === 'floating');
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
disposable.dispose();
|
||||||
|
};
|
||||||
|
}, [props.group.api]);
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
if (floating) {
|
||||||
|
const group = props.containerApi.addGroup();
|
||||||
|
props.group.api.moveTo({ group });
|
||||||
|
} else {
|
||||||
|
props.containerApi.addFloatingGroup(props.group, {
|
||||||
|
position: {
|
||||||
|
width: 400,
|
||||||
|
height: 300,
|
||||||
|
bottom: 50,
|
||||||
|
right: 50,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: '100%', color: 'white', padding: '0px 4px' }}>
|
||||||
|
<Icon
|
||||||
|
onClick={onClick}
|
||||||
|
icon={floating ? 'jump_to_element' : 'back_to_tab'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DockviewPersistence;
|
||||||
|
|
||||||
|
const Watermark = () => {
|
||||||
|
return <div style={{ color: 'white', padding: '8px' }}>watermark</div>;
|
||||||
|
};
|
@ -0,0 +1,20 @@
|
|||||||
|
import { StrictMode } from 'react';
|
||||||
|
import * as ReactDOMClient from 'react-dom/client';
|
||||||
|
import './styles.css';
|
||||||
|
import 'dockview/dist/styles/dockview.css';
|
||||||
|
|
||||||
|
import App from './app';
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root');
|
||||||
|
|
||||||
|
if (rootElement) {
|
||||||
|
const root = ReactDOMClient.createRoot(rootElement);
|
||||||
|
|
||||||
|
root.render(
|
||||||
|
<StrictMode>
|
||||||
|
<div className="app">
|
||||||
|
<App />
|
||||||
|
</div>
|
||||||
|
</StrictMode>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
color: white;
|
||||||
|
font-family: sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
export const Icon = (props: {
|
||||||
|
icon: string;
|
||||||
|
title?: string;
|
||||||
|
onClick?: (event: React.MouseEvent) => void;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
title={props.title}
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
width: '30px',
|
||||||
|
height: '100%',
|
||||||
|
|
||||||
|
fontSize: '18px',
|
||||||
|
}}
|
||||||
|
onClick={props.onClick}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{ fontSize: 'inherit', cursor: 'pointer' }}
|
||||||
|
className="material-symbols-outlined"
|
||||||
|
>
|
||||||
|
{props.icon}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "build/dist",
|
||||||
|
"module": "esnext",
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
|
"sourceMap": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"rootDir": "src",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strictNullChecks": true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "dockview.group-actions",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [
|
||||||
|
"dockview"
|
||||||
|
],
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "src/index.tsx",
|
||||||
|
"dependencies": {
|
||||||
|
"dockview": "*",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.0.28",
|
||||||
|
"@types/react-dom": "^18.0.11",
|
||||||
|
"typescript": "^4.9.5",
|
||||||
|
"react-scripts": "*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test --env=jsdom",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not ie <= 11",
|
||||||
|
"not op_mini all"
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is added to the
|
||||||
|
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||||
|
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<title>React App</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
You need to enable JavaScript to run this app.
|
||||||
|
</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -0,0 +1,20 @@
|
|||||||
|
.dockview-groupcontrol-demo {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: white;
|
||||||
|
background-color: black;
|
||||||
|
padding: 0px 8px;
|
||||||
|
|
||||||
|
margin: 1px;
|
||||||
|
border: 1px dotted orange;
|
||||||
|
|
||||||
|
.dockview-groupcontrol-demo-group-active {
|
||||||
|
padding: 0px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dockview-groupcontrol-demo-active-panel {
|
||||||
|
color: yellow;
|
||||||
|
padding: 0px 8px;
|
||||||
|
}
|
||||||
|
}
|
102
packages/docs/sandboxes/react/dockview/group-actions/src/app.tsx
Normal file
102
packages/docs/sandboxes/react/dockview/group-actions/src/app.tsx
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import {
|
||||||
|
DockviewReact,
|
||||||
|
DockviewReadyEvent,
|
||||||
|
IDockviewHeaderActionsProps,
|
||||||
|
IDockviewPanelProps,
|
||||||
|
} from 'dockview';
|
||||||
|
import * as React from 'react';
|
||||||
|
import './app.scss';
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
default: (props: IDockviewPanelProps<{ title: string; x?: number }>) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
color: 'gray',
|
||||||
|
height: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>{`${props.api.title}`}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const RightHeaderActions = (props: IDockviewHeaderActionsProps) => {
|
||||||
|
const isGroupActive = props.isGroupActive;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="dockview-groupcontrol-demo">
|
||||||
|
<span
|
||||||
|
className="dockview-groupcontrol-demo-group-active"
|
||||||
|
style={{
|
||||||
|
background: isGroupActive ? 'green' : 'red',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isGroupActive ? 'Group Active' : 'Group Inactive'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const LeftHeaderActions = (props: IDockviewHeaderActionsProps) => {
|
||||||
|
const activePanel = props.activePanel;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="dockview-groupcontrol-demo">
|
||||||
|
<span className="dockview-groupcontrol-demo-active-panel">{`activePanel: ${
|
||||||
|
activePanel?.id || 'null'
|
||||||
|
}`}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const PrefixHeader = (props: IDockviewHeaderActionsProps) => {
|
||||||
|
const activePanel = props.activePanel;
|
||||||
|
|
||||||
|
return <div className="dockview-groupcontrol-demo">{'🌲'}</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DockviewGroupControl = (props: { theme: string }) => {
|
||||||
|
const onReady = (event: DockviewReadyEvent) => {
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_1',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 1',
|
||||||
|
});
|
||||||
|
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_2',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 2',
|
||||||
|
position: {
|
||||||
|
direction: 'right',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_3',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 3',
|
||||||
|
position: {
|
||||||
|
direction: 'below',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DockviewReact
|
||||||
|
onReady={onReady}
|
||||||
|
components={components}
|
||||||
|
prefixHeaderActionsComponent={PrefixHeader}
|
||||||
|
leftHeaderActionsComponent={LeftHeaderActions}
|
||||||
|
rightHeaderActionsComponent={RightHeaderActions}
|
||||||
|
className={`${props.theme || 'dockview-theme-abyss'}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DockviewGroupControl;
|
@ -0,0 +1,20 @@
|
|||||||
|
import { StrictMode } from 'react';
|
||||||
|
import * as ReactDOMClient from 'react-dom/client';
|
||||||
|
import './styles.css';
|
||||||
|
import 'dockview/dist/styles/dockview.css';
|
||||||
|
|
||||||
|
import App from './app';
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root');
|
||||||
|
|
||||||
|
if (rootElement) {
|
||||||
|
const root = ReactDOMClient.createRoot(rootElement);
|
||||||
|
|
||||||
|
root.render(
|
||||||
|
<StrictMode>
|
||||||
|
<div className="app">
|
||||||
|
<App />
|
||||||
|
</div>
|
||||||
|
</StrictMode>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
color: white;
|
||||||
|
font-family: sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "build/dist",
|
||||||
|
"module": "esnext",
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
|
"sourceMap": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"rootDir": "src",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strictNullChecks": true
|
||||||
|
}
|
||||||
|
}
|
32
packages/docs/sandboxes/react/dockview/layout/package.json
Normal file
32
packages/docs/sandboxes/react/dockview/layout/package.json
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "dockview.layout",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [
|
||||||
|
"dockview"
|
||||||
|
],
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "src/index.tsx",
|
||||||
|
"dependencies": {
|
||||||
|
"dockview": "*",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.0.28",
|
||||||
|
"@types/react-dom": "^18.0.11",
|
||||||
|
"typescript": "^4.9.5",
|
||||||
|
"react-scripts": "*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test --env=jsdom",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not ie <= 11",
|
||||||
|
"not op_mini all"
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is added to the
|
||||||
|
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||||
|
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<title>React App</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
You need to enable JavaScript to run this app.
|
||||||
|
</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
132
packages/docs/sandboxes/react/dockview/layout/src/app.tsx
Normal file
132
packages/docs/sandboxes/react/dockview/layout/src/app.tsx
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import {
|
||||||
|
DockviewApi,
|
||||||
|
DockviewReact,
|
||||||
|
DockviewReadyEvent,
|
||||||
|
IDockviewPanelProps,
|
||||||
|
} from 'dockview';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
default: (props: IDockviewPanelProps<{ title: string }>) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: '100%',
|
||||||
|
padding: '20px',
|
||||||
|
background: 'var(--dv-group-view-background-color)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.params.title}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const counter = (() => {
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
return {
|
||||||
|
next: () => ++i,
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
function loadDefaultLayout(api: DockviewApi) {
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_1',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_2',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_3',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DockviewPersistence = (props: { theme?: string }) => {
|
||||||
|
const [api, setApi] = React.useState<DockviewApi>();
|
||||||
|
|
||||||
|
const clearLayout = () => {
|
||||||
|
localStorage.removeItem('dockview_persistence_layout');
|
||||||
|
if (api) {
|
||||||
|
api.clear();
|
||||||
|
loadDefaultLayout(api);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onReady = (event: DockviewReadyEvent) => {
|
||||||
|
const layoutString = localStorage.getItem(
|
||||||
|
'dockview_persistence_layout'
|
||||||
|
);
|
||||||
|
|
||||||
|
let success = false;
|
||||||
|
|
||||||
|
if (layoutString) {
|
||||||
|
try {
|
||||||
|
const layout = JSON.parse(layoutString);
|
||||||
|
event.api.fromJSON(layout);
|
||||||
|
success = true;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
loadDefaultLayout(event.api);
|
||||||
|
}
|
||||||
|
|
||||||
|
setApi(event.api);
|
||||||
|
};
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!api) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
api.onDidLayoutChange(() => {
|
||||||
|
const layout = api.toJSON();
|
||||||
|
|
||||||
|
localStorage.setItem(
|
||||||
|
'dockview_persistence_layout',
|
||||||
|
JSON.stringify(layout)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}, [api]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
height: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ height: '25px' }}>
|
||||||
|
<button onClick={clearLayout}>Reset Layout</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flexGrow: 1,
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DockviewReact
|
||||||
|
onReady={onReady}
|
||||||
|
components={components}
|
||||||
|
watermarkComponent={Watermark}
|
||||||
|
className={`${props.theme || 'dockview-theme-abyss'}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DockviewPersistence;
|
||||||
|
|
||||||
|
const Watermark = () => {
|
||||||
|
return <div style={{ color: 'white', padding: '8px' }}>watermark</div>;
|
||||||
|
};
|
20
packages/docs/sandboxes/react/dockview/layout/src/index.tsx
Normal file
20
packages/docs/sandboxes/react/dockview/layout/src/index.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { StrictMode } from 'react';
|
||||||
|
import * as ReactDOMClient from 'react-dom/client';
|
||||||
|
import './styles.css';
|
||||||
|
import 'dockview/dist/styles/dockview.css';
|
||||||
|
|
||||||
|
import App from './app';
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root');
|
||||||
|
|
||||||
|
if (rootElement) {
|
||||||
|
const root = ReactDOMClient.createRoot(rootElement);
|
||||||
|
|
||||||
|
root.render(
|
||||||
|
<StrictMode>
|
||||||
|
<div className="app">
|
||||||
|
<App />
|
||||||
|
</div>
|
||||||
|
</StrictMode>
|
||||||
|
);
|
||||||
|
}
|
16
packages/docs/sandboxes/react/dockview/layout/src/styles.css
Normal file
16
packages/docs/sandboxes/react/dockview/layout/src/styles.css
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
color: white;
|
||||||
|
font-family: sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
}
|
18
packages/docs/sandboxes/react/dockview/layout/tsconfig.json
Normal file
18
packages/docs/sandboxes/react/dockview/layout/tsconfig.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "build/dist",
|
||||||
|
"module": "esnext",
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
|
"sourceMap": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"rootDir": "src",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strictNullChecks": true
|
||||||
|
}
|
||||||
|
}
|
32
packages/docs/sandboxes/react/dockview/locked/package.json
Normal file
32
packages/docs/sandboxes/react/dockview/locked/package.json
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "dockview.locked",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [
|
||||||
|
"dockview"
|
||||||
|
],
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "src/index.tsx",
|
||||||
|
"dependencies": {
|
||||||
|
"dockview": "*",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.0.28",
|
||||||
|
"@types/react-dom": "^18.0.11",
|
||||||
|
"typescript": "^4.9.5",
|
||||||
|
"react-scripts": "*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test --env=jsdom",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not ie <= 11",
|
||||||
|
"not op_mini all"
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is added to the
|
||||||
|
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||||
|
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<title>React App</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
You need to enable JavaScript to run this app.
|
||||||
|
</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
64
packages/docs/sandboxes/react/dockview/locked/src/app.tsx
Normal file
64
packages/docs/sandboxes/react/dockview/locked/src/app.tsx
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import {
|
||||||
|
DockviewReact,
|
||||||
|
DockviewReadyEvent,
|
||||||
|
IDockviewPanelProps,
|
||||||
|
} from 'dockview';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
const Default = (props: IDockviewPanelProps) => {
|
||||||
|
return (
|
||||||
|
<div style={{ height: '100%' }}>
|
||||||
|
<div>{props.api.title}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
default: Default,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Component = (props: { theme?: string }) => {
|
||||||
|
const onReady = (event: DockviewReadyEvent) => {
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_1',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_2',
|
||||||
|
component: 'default',
|
||||||
|
position: {
|
||||||
|
direction: 'right',
|
||||||
|
referencePanel: 'panel_1',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_3',
|
||||||
|
component: 'default',
|
||||||
|
position: {
|
||||||
|
direction: 'below',
|
||||||
|
referencePanel: 'panel_1',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_4',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_5',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DockviewReact
|
||||||
|
className={`${props.theme || 'dockview-theme-abyss'}`}
|
||||||
|
onReady={onReady}
|
||||||
|
components={components}
|
||||||
|
locked={true}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Component;
|
20
packages/docs/sandboxes/react/dockview/locked/src/index.tsx
Normal file
20
packages/docs/sandboxes/react/dockview/locked/src/index.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { StrictMode } from 'react';
|
||||||
|
import * as ReactDOMClient from 'react-dom/client';
|
||||||
|
import './styles.css';
|
||||||
|
import 'dockview/dist/styles/dockview.css';
|
||||||
|
|
||||||
|
import App from './app';
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root');
|
||||||
|
|
||||||
|
if (rootElement) {
|
||||||
|
const root = ReactDOMClient.createRoot(rootElement);
|
||||||
|
|
||||||
|
root.render(
|
||||||
|
<StrictMode>
|
||||||
|
<div className="app">
|
||||||
|
<App />
|
||||||
|
</div>
|
||||||
|
</StrictMode>
|
||||||
|
);
|
||||||
|
}
|
16
packages/docs/sandboxes/react/dockview/locked/src/styles.css
Normal file
16
packages/docs/sandboxes/react/dockview/locked/src/styles.css
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
color: white;
|
||||||
|
font-family: sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
}
|
18
packages/docs/sandboxes/react/dockview/locked/tsconfig.json
Normal file
18
packages/docs/sandboxes/react/dockview/locked/tsconfig.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "build/dist",
|
||||||
|
"module": "esnext",
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
|
"sourceMap": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"rootDir": "src",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strictNullChecks": true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "dockview.maximize-group",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [
|
||||||
|
"dockview"
|
||||||
|
],
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "src/index.tsx",
|
||||||
|
"dependencies": {
|
||||||
|
"dockview": "*",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.0.28",
|
||||||
|
"@types/react-dom": "^18.0.11",
|
||||||
|
"typescript": "^4.9.5",
|
||||||
|
"react-scripts": "*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test --env=jsdom",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not ie <= 11",
|
||||||
|
"not op_mini all"
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is added to the
|
||||||
|
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||||
|
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<title>React App</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
You need to enable JavaScript to run this app.
|
||||||
|
</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -0,0 +1,252 @@
|
|||||||
|
import {
|
||||||
|
DockviewApi,
|
||||||
|
DockviewGroupPanel,
|
||||||
|
DockviewReact,
|
||||||
|
DockviewReadyEvent,
|
||||||
|
IDockviewHeaderActionsProps,
|
||||||
|
IDockviewPanelProps,
|
||||||
|
SerializedDockview,
|
||||||
|
} from 'dockview';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Icon } from './utils';
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
default: (props: IDockviewPanelProps<{ title: string }>) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: '100%',
|
||||||
|
padding: '20px',
|
||||||
|
background: 'var(--dv-group-view-background-color)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.params.title}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const counter = (() => {
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
return {
|
||||||
|
next: () => ++i,
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
function loadDefaultLayout(api: DockviewApi) {
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_1',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_2',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_3',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_4',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_5',
|
||||||
|
component: 'default',
|
||||||
|
position: { direction: 'right' },
|
||||||
|
});
|
||||||
|
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_6',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let panelCount = 0;
|
||||||
|
|
||||||
|
function safeParse<T>(value: any): T | null {
|
||||||
|
try {
|
||||||
|
return JSON.parse(value) as T;
|
||||||
|
} catch (err) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const useLocalStorage = <T,>(
|
||||||
|
key: string
|
||||||
|
): [T | null, (setter: T | null) => void] => {
|
||||||
|
const [state, setState] = React.useState<T | null>(
|
||||||
|
safeParse(localStorage.getItem(key))
|
||||||
|
);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const _state = localStorage.getItem('key');
|
||||||
|
try {
|
||||||
|
if (_state !== null) {
|
||||||
|
setState(JSON.parse(_state));
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}, [key]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
state,
|
||||||
|
(_state: T | null) => {
|
||||||
|
if (_state === null) {
|
||||||
|
localStorage.removeItem(key);
|
||||||
|
} else {
|
||||||
|
localStorage.setItem(key, JSON.stringify(_state));
|
||||||
|
setState(_state);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const App = (props: { theme?: string }) => {
|
||||||
|
const [api, setApi] = React.useState<DockviewApi>();
|
||||||
|
const [layout, setLayout] =
|
||||||
|
useLocalStorage<SerializedDockview>('floating.layout');
|
||||||
|
|
||||||
|
const [disableFloatingGroups, setDisableFloatingGroups] =
|
||||||
|
React.useState<boolean>(false);
|
||||||
|
|
||||||
|
const load = (api: DockviewApi) => {
|
||||||
|
api.clear();
|
||||||
|
if (layout) {
|
||||||
|
try {
|
||||||
|
api.fromJSON(layout);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
api.clear();
|
||||||
|
loadDefaultLayout(api);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loadDefaultLayout(api);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onReady = (event: DockviewReadyEvent) => {
|
||||||
|
load(event.api);
|
||||||
|
setApi(event.api);
|
||||||
|
};
|
||||||
|
|
||||||
|
const [options, setOptions] = React.useState<
|
||||||
|
'boundedWithinViewport' | undefined
|
||||||
|
>(undefined);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
height: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ height: '25px' }}>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
if (api) {
|
||||||
|
setLayout(api.toJSON());
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
if (api) {
|
||||||
|
load(api);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Load
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
api!.clear();
|
||||||
|
setLayout(null);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flexGrow: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DockviewReact
|
||||||
|
onReady={onReady}
|
||||||
|
components={components}
|
||||||
|
watermarkComponent={Watermark}
|
||||||
|
leftHeaderActionsComponent={LeftComponent}
|
||||||
|
rightHeaderActionsComponent={RightComponent}
|
||||||
|
disableFloatingGroups={disableFloatingGroups}
|
||||||
|
floatingGroupBounds={options}
|
||||||
|
className={`${props.theme || 'dockview-theme-abyss'}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const LeftComponent = (props: IDockviewHeaderActionsProps) => {
|
||||||
|
const onClick = () => {
|
||||||
|
props.containerApi.addPanel({
|
||||||
|
id: (++panelCount).toString(),
|
||||||
|
title: `Tab ${panelCount}`,
|
||||||
|
component: 'default',
|
||||||
|
position: { referenceGroup: props.group },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div style={{ height: '100%', color: 'white', padding: '0px 4px' }}>
|
||||||
|
<Icon onClick={onClick} icon={'add'} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const RightComponent = (props: IDockviewHeaderActionsProps) => {
|
||||||
|
const [maximized, setMaximized] = React.useState<boolean>(
|
||||||
|
props.api.isMaximized()
|
||||||
|
);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const disposable = props.containerApi.onDidMaximizedGroupChange(() => {
|
||||||
|
setMaximized(props.api.isMaximized());
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
disposable.dispose();
|
||||||
|
};
|
||||||
|
}, [props.containerApi]);
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
if (maximized) {
|
||||||
|
props.api.exitMaximized();
|
||||||
|
} else {
|
||||||
|
props.api.maximize();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: '100%', color: 'white', padding: '0px 4px' }}>
|
||||||
|
<Icon
|
||||||
|
onClick={onClick}
|
||||||
|
icon={maximized ? 'jump_to_element' : 'back_to_tab'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
||||||
|
|
||||||
|
const Watermark = () => {
|
||||||
|
return <div style={{ color: 'white', padding: '8px' }}>watermark</div>;
|
||||||
|
};
|
@ -0,0 +1,20 @@
|
|||||||
|
import { StrictMode } from 'react';
|
||||||
|
import * as ReactDOMClient from 'react-dom/client';
|
||||||
|
import './styles.css';
|
||||||
|
import 'dockview/dist/styles/dockview.css';
|
||||||
|
|
||||||
|
import App from './app';
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root');
|
||||||
|
|
||||||
|
if (rootElement) {
|
||||||
|
const root = ReactDOMClient.createRoot(rootElement);
|
||||||
|
|
||||||
|
root.render(
|
||||||
|
<StrictMode>
|
||||||
|
<div className="app">
|
||||||
|
<App />
|
||||||
|
</div>
|
||||||
|
</StrictMode>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
color: white;
|
||||||
|
font-family: sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
export const Icon = (props: {
|
||||||
|
icon: string;
|
||||||
|
title?: string;
|
||||||
|
onClick?: (event: React.MouseEvent) => void;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
title={props.title}
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
width: '30px',
|
||||||
|
height: '100%',
|
||||||
|
|
||||||
|
fontSize: '18px',
|
||||||
|
}}
|
||||||
|
onClick={props.onClick}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{ fontSize: 'inherit', cursor: 'pointer' }}
|
||||||
|
className="material-symbols-outlined"
|
||||||
|
>
|
||||||
|
{props.icon}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "build/dist",
|
||||||
|
"module": "esnext",
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
|
"sourceMap": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"rootDir": "src",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strictNullChecks": true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "dockview.popout-group",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [
|
||||||
|
"dockview"
|
||||||
|
],
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "src/index.tsx",
|
||||||
|
"dependencies": {
|
||||||
|
"dockview": "*",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"react-laag": "^2.0.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.0.28",
|
||||||
|
"@types/react-dom": "^18.0.11",
|
||||||
|
"typescript": "^4.9.5",
|
||||||
|
"react-scripts": "*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test --env=jsdom",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not ie <= 11",
|
||||||
|
"not op_mini all"
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is added to the
|
||||||
|
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||||
|
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<title>React App</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
You need to enable JavaScript to run this app.
|
||||||
|
</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
280
packages/docs/sandboxes/react/dockview/popout-group/src/app.tsx
Normal file
280
packages/docs/sandboxes/react/dockview/popout-group/src/app.tsx
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
import {
|
||||||
|
DockviewApi,
|
||||||
|
DockviewReact,
|
||||||
|
DockviewReadyEvent,
|
||||||
|
IDockviewHeaderActionsProps,
|
||||||
|
IDockviewPanelProps,
|
||||||
|
SerializedDockview,
|
||||||
|
DockviewPanelApi,
|
||||||
|
} from 'dockview';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Icon } from './utils';
|
||||||
|
import { PopoverMenu } from './popover';
|
||||||
|
|
||||||
|
function usePanelWindowObject(api: DockviewPanelApi): Window {
|
||||||
|
const [document, setDocument] = React.useState<Window>(api.getWindow());
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const disposable = api.onDidLocationChange((event) => {
|
||||||
|
setDocument(api.getWindow());
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
disposable.dispose();
|
||||||
|
};
|
||||||
|
}, [api]);
|
||||||
|
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
default: (props: IDockviewPanelProps<{ title: string }>) => {
|
||||||
|
const _window = usePanelWindowObject(props.api);
|
||||||
|
|
||||||
|
const [reset, setReset] = React.useState<boolean>(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: '100%',
|
||||||
|
padding: '20px',
|
||||||
|
background: 'var(--dv-group-view-background-color)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
console.log(_window);
|
||||||
|
setReset(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setReset(false);
|
||||||
|
}, 2000);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Print
|
||||||
|
</button>
|
||||||
|
{!reset && <PopoverMenu window={_window} />}
|
||||||
|
{props.api.title}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function loadDefaultLayout(api: DockviewApi) {
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_1',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
|
||||||
|
// api.addPanel({
|
||||||
|
// id: 'panel_2',
|
||||||
|
// component: 'default',
|
||||||
|
// });
|
||||||
|
|
||||||
|
// api.addPanel({
|
||||||
|
// id: 'panel_3',
|
||||||
|
// component: 'default',
|
||||||
|
// });
|
||||||
|
|
||||||
|
// api.addPanel({
|
||||||
|
// id: 'panel_4',
|
||||||
|
// component: 'default',
|
||||||
|
// });
|
||||||
|
|
||||||
|
// api.addPanel({
|
||||||
|
// id: 'panel_5',
|
||||||
|
// component: 'default',
|
||||||
|
// position: { direction: 'right' },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// api.addPanel({
|
||||||
|
// id: 'panel_6',
|
||||||
|
// component: 'default',
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
let panelCount = 0;
|
||||||
|
|
||||||
|
function safeParse<T>(value: any): T | null {
|
||||||
|
try {
|
||||||
|
return JSON.parse(value) as T;
|
||||||
|
} catch (err) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const useLocalStorage = <T,>(
|
||||||
|
key: string
|
||||||
|
): [T | null, (setter: T | null) => void] => {
|
||||||
|
const [state, setState] = React.useState<T | null>(
|
||||||
|
safeParse(localStorage.getItem(key))
|
||||||
|
);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const _state = localStorage.getItem('key');
|
||||||
|
try {
|
||||||
|
if (_state !== null) {
|
||||||
|
setState(JSON.parse(_state));
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}, [key]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
state,
|
||||||
|
(_state: T | null) => {
|
||||||
|
if (_state === null) {
|
||||||
|
localStorage.removeItem(key);
|
||||||
|
} else {
|
||||||
|
localStorage.setItem(key, JSON.stringify(_state));
|
||||||
|
setState(_state);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const App = (props: { theme?: string }) => {
|
||||||
|
const [api, setApi] = React.useState<DockviewApi>();
|
||||||
|
const [layout, setLayout] =
|
||||||
|
useLocalStorage<SerializedDockview>('floating.layout');
|
||||||
|
|
||||||
|
const [disableFloatingGroups, setDisableFloatingGroups] =
|
||||||
|
React.useState<boolean>(false);
|
||||||
|
|
||||||
|
const load = (api: DockviewApi) => {
|
||||||
|
api.clear();
|
||||||
|
if (layout) {
|
||||||
|
try {
|
||||||
|
api.fromJSON(layout);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
api.clear();
|
||||||
|
loadDefaultLayout(api);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loadDefaultLayout(api);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onReady = (event: DockviewReadyEvent) => {
|
||||||
|
load(event.api);
|
||||||
|
setApi(event.api);
|
||||||
|
};
|
||||||
|
|
||||||
|
const [options, setOptions] = React.useState<
|
||||||
|
'boundedWithinViewport' | undefined
|
||||||
|
>(undefined);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
height: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ height: '25px' }}>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
if (api) {
|
||||||
|
setLayout(api.toJSON());
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
if (api) {
|
||||||
|
load(api);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Load
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
api!.clear();
|
||||||
|
setLayout(null);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flexGrow: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DockviewReact
|
||||||
|
onReady={onReady}
|
||||||
|
components={components}
|
||||||
|
watermarkComponent={Watermark}
|
||||||
|
leftHeaderActionsComponent={LeftComponent}
|
||||||
|
rightHeaderActionsComponent={RightComponent}
|
||||||
|
disableFloatingGroups={disableFloatingGroups}
|
||||||
|
floatingGroupBounds={options}
|
||||||
|
className={`${props.theme || 'dockview-theme-abyss'}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const LeftComponent = (props: IDockviewHeaderActionsProps) => {
|
||||||
|
const onClick = () => {
|
||||||
|
props.containerApi.addPanel({
|
||||||
|
id: (++panelCount).toString(),
|
||||||
|
title: `Tab ${panelCount}`,
|
||||||
|
component: 'default',
|
||||||
|
position: { referenceGroup: props.group },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div style={{ height: '100%', color: 'white', padding: '0px 4px' }}>
|
||||||
|
<Icon onClick={onClick} icon={'add'} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const RightComponent = (props: IDockviewHeaderActionsProps) => {
|
||||||
|
const [popout, setPopout] = React.useState<boolean>(
|
||||||
|
props.api.location.type === 'popout'
|
||||||
|
);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const disposable = props.group.api.onDidLocationChange((event) => [
|
||||||
|
setPopout(event.location.type === 'popout'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
disposable.dispose();
|
||||||
|
};
|
||||||
|
}, [props.group.api]);
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
if (popout) {
|
||||||
|
const group = props.containerApi.addGroup();
|
||||||
|
props.group.api.moveTo({ group });
|
||||||
|
} else {
|
||||||
|
const window = props.containerApi.addPopoutGroup(props.group, {
|
||||||
|
popoutUrl: '/popout/index.html',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: '100%', color: 'white', padding: '0px 4px' }}>
|
||||||
|
<Icon
|
||||||
|
onClick={onClick}
|
||||||
|
icon={popout ? 'jump_to_element' : 'back_to_tab'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
||||||
|
|
||||||
|
const Watermark = () => {
|
||||||
|
return <div style={{ color: 'white', padding: '8px' }}>watermark</div>;
|
||||||
|
};
|
@ -0,0 +1,20 @@
|
|||||||
|
import { StrictMode } from 'react';
|
||||||
|
import * as ReactDOMClient from 'react-dom/client';
|
||||||
|
import './styles.css';
|
||||||
|
import 'dockview/dist/styles/dockview.css';
|
||||||
|
|
||||||
|
import App from './app';
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root');
|
||||||
|
|
||||||
|
if (rootElement) {
|
||||||
|
const root = ReactDOMClient.createRoot(rootElement);
|
||||||
|
|
||||||
|
root.render(
|
||||||
|
<StrictMode>
|
||||||
|
<div className="app">
|
||||||
|
<App />
|
||||||
|
</div>
|
||||||
|
</StrictMode>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
import { useLayer, Arrow } from 'react-laag';
|
||||||
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { DockviewPanelApi } from 'dockview';
|
||||||
|
|
||||||
|
export function PopoverMenu(props: { window: Window }) {
|
||||||
|
const [isOpen, setOpen] = React.useState(false);
|
||||||
|
|
||||||
|
// helper function to close the menu
|
||||||
|
function close() {
|
||||||
|
setOpen(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { renderLayer, triggerProps, layerProps, arrowProps } = useLayer({
|
||||||
|
isOpen,
|
||||||
|
onOutsideClick: close, // close the menu when the user clicks outside
|
||||||
|
onDisappear: close, // close the menu when the menu gets scrolled out of sight
|
||||||
|
overflowContainer: false, // keep the menu positioned inside the container
|
||||||
|
auto: true, // automatically find the best placement
|
||||||
|
placement: 'top-end', // we prefer to place the menu "top-end"
|
||||||
|
triggerOffset: 12, // keep some distance to the trigger
|
||||||
|
containerOffset: 16, // give the menu some room to breath relative to the container
|
||||||
|
arrowOffset: 16, // let the arrow have some room to breath also,
|
||||||
|
environment: props.window,
|
||||||
|
container: props.window
|
||||||
|
? () => {
|
||||||
|
const el = props.window.document.body;
|
||||||
|
Object.setPrototypeOf(el, HTMLElement.prototype);
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Again, we're using framer-motion for the transition effect
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<button {...triggerProps} onClick={() => setOpen(!isOpen)}>
|
||||||
|
{isOpen ? 'Hide' : 'Show'}
|
||||||
|
</button>
|
||||||
|
{renderLayer(
|
||||||
|
<AnimatePresence>
|
||||||
|
{isOpen && (
|
||||||
|
<motion.ul {...layerProps}>
|
||||||
|
<li>Item 1</li>
|
||||||
|
<li>Item 2</li>
|
||||||
|
<li>Item 3</li>
|
||||||
|
<li>Item 4</li>
|
||||||
|
<Arrow {...arrowProps} />
|
||||||
|
</motion.ul>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
color: white;
|
||||||
|
font-family: sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
export const Icon = (props: {
|
||||||
|
icon: string;
|
||||||
|
title?: string;
|
||||||
|
onClick?: (event: React.MouseEvent) => void;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
title={props.title}
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
width: '30px',
|
||||||
|
height: '100%',
|
||||||
|
|
||||||
|
fontSize: '18px',
|
||||||
|
}}
|
||||||
|
onClick={props.onClick}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{ fontSize: 'inherit', cursor: 'pointer' }}
|
||||||
|
className="material-symbols-outlined"
|
||||||
|
>
|
||||||
|
{props.icon}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "build/dist",
|
||||||
|
"module": "esnext",
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
|
"sourceMap": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"rootDir": "src",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strictNullChecks": true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "dockview.render-mode",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [
|
||||||
|
"dockview"
|
||||||
|
],
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "src/index.tsx",
|
||||||
|
"dependencies": {
|
||||||
|
"dockview": "*",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.0.28",
|
||||||
|
"@types/react-dom": "^18.0.11",
|
||||||
|
"@types/uuid": "^9.0.0",
|
||||||
|
"typescript": "^4.9.5",
|
||||||
|
"react-scripts": "*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test --env=jsdom",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not ie <= 11",
|
||||||
|
"not op_mini all"
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is added to the
|
||||||
|
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||||
|
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<title>React App</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
You need to enable JavaScript to run this app.
|
||||||
|
</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
160
packages/docs/sandboxes/react/dockview/render-mode/src/app.tsx
Normal file
160
packages/docs/sandboxes/react/dockview/render-mode/src/app.tsx
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
import {
|
||||||
|
DockviewReact,
|
||||||
|
DockviewReadyEvent,
|
||||||
|
IDockviewPanelProps,
|
||||||
|
DockviewPanelApi,
|
||||||
|
DockviewPanelRenderer,
|
||||||
|
DockviewApi,
|
||||||
|
SerializedDockview,
|
||||||
|
} from 'dockview';
|
||||||
|
import * as React from 'react';
|
||||||
|
import './app.scss';
|
||||||
|
|
||||||
|
const useRenderer = (
|
||||||
|
api: DockviewPanelApi
|
||||||
|
): [DockviewPanelRenderer, (value: DockviewPanelRenderer) => void] => {
|
||||||
|
const [mode, setMode] = React.useState<DockviewPanelRenderer>(api.renderer);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const disposable = api.onDidRendererChange((event) => {
|
||||||
|
setMode(event.renderer);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
disposable.dispose();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const _setMode = React.useCallback(
|
||||||
|
(mode: DockviewPanelRenderer) => {
|
||||||
|
api.setRenderer(mode);
|
||||||
|
},
|
||||||
|
[api]
|
||||||
|
);
|
||||||
|
|
||||||
|
return [mode, _setMode];
|
||||||
|
};
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
default: (props: IDockviewPanelProps<{ title: string }>) => {
|
||||||
|
const [mode, setMode] = useRenderer(props.api);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: '100%', overflow: 'auto', color: 'white' }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: '1000px',
|
||||||
|
padding: '20px',
|
||||||
|
overflow: 'auto',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>{props.api.title}</div>
|
||||||
|
<input />
|
||||||
|
<div>
|
||||||
|
{mode}
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setMode(
|
||||||
|
mode === 'onlyWhenVisible'
|
||||||
|
? 'always'
|
||||||
|
: 'onlyWhenVisible'
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Toggle render mode
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const DockviewDemo = (props: { theme?: string }) => {
|
||||||
|
const [value, setValue] = React.useState<string>('100');
|
||||||
|
const [api, setApi] = React.useState<DockviewApi | null>(null);
|
||||||
|
|
||||||
|
const onReady = (event: DockviewReadyEvent) => {
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_1',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 1',
|
||||||
|
});
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_2',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 2',
|
||||||
|
position: { referencePanel: 'panel_1', direction: 'within' },
|
||||||
|
});
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_3',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 3',
|
||||||
|
});
|
||||||
|
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_4',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 4',
|
||||||
|
position: { referencePanel: 'panel_3', direction: 'below' },
|
||||||
|
});
|
||||||
|
|
||||||
|
setApi(event.api);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSave = () => {
|
||||||
|
if (!api) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem(
|
||||||
|
'dv_rendermode_state',
|
||||||
|
JSON.stringify({ size: value, state: api.toJSON() })
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onLoad = () => {
|
||||||
|
if (!api) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = localStorage.getItem('dv_rendermode_state');
|
||||||
|
if (typeof state !== 'string') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const json = JSON.parse(state) as {
|
||||||
|
size: string;
|
||||||
|
state: SerializedDockview;
|
||||||
|
};
|
||||||
|
setValue(json.size);
|
||||||
|
api.fromJSON(json.state);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{ height: '100%', display: 'flex', flexDirection: 'column' }}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<button onClick={onSave}>Save</button>
|
||||||
|
<button onClick={onLoad}>Load</button>
|
||||||
|
<input
|
||||||
|
onChange={(event) => setValue(event.target.value)}
|
||||||
|
type="range"
|
||||||
|
min="1"
|
||||||
|
max="100"
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style={{ height: `${value}%`, width: `${value}%` }}>
|
||||||
|
<DockviewReact
|
||||||
|
components={components}
|
||||||
|
onReady={onReady}
|
||||||
|
className={props.theme || 'dockview-theme-abyss'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DockviewDemo;
|
@ -0,0 +1,20 @@
|
|||||||
|
import { StrictMode } from 'react';
|
||||||
|
import * as ReactDOMClient from 'react-dom/client';
|
||||||
|
import './styles.css';
|
||||||
|
import 'dockview/dist/styles/dockview.css';
|
||||||
|
|
||||||
|
import App from './app';
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root');
|
||||||
|
|
||||||
|
if (rootElement) {
|
||||||
|
const root = ReactDOMClient.createRoot(rootElement);
|
||||||
|
|
||||||
|
root.render(
|
||||||
|
<StrictMode>
|
||||||
|
<div className="app">
|
||||||
|
<App />
|
||||||
|
</div>
|
||||||
|
</StrictMode>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
color: white;
|
||||||
|
font-family: sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "build/dist",
|
||||||
|
"module": "esnext",
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
|
"sourceMap": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"rootDir": "src",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strictNullChecks": true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "dockview.resize-container",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [
|
||||||
|
"dockview"
|
||||||
|
],
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "src/index.tsx",
|
||||||
|
"dependencies": {
|
||||||
|
"dockview": "*",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.0.28",
|
||||||
|
"@types/react-dom": "^18.0.11",
|
||||||
|
"typescript": "^4.9.5",
|
||||||
|
"react-scripts": "*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test --env=jsdom",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not ie <= 11",
|
||||||
|
"not op_mini all"
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is added to the
|
||||||
|
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||||
|
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<title>React App</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
You need to enable JavaScript to run this app.
|
||||||
|
</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -0,0 +1,117 @@
|
|||||||
|
import {
|
||||||
|
DockviewReact,
|
||||||
|
DockviewReadyEvent,
|
||||||
|
IDockviewPanelProps,
|
||||||
|
} from 'dockview';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
default: (props: IDockviewPanelProps<{ title: string }>) => {
|
||||||
|
return (
|
||||||
|
<div style={{ padding: '20px', color: 'white' }}>
|
||||||
|
{props.params.title}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const App: React.FC = (props: { theme?: string }) => {
|
||||||
|
const onReady = (event: DockviewReadyEvent) => {
|
||||||
|
const panel = event.api.addPanel({
|
||||||
|
id: 'panel_1',
|
||||||
|
component: 'default',
|
||||||
|
params: {
|
||||||
|
title: 'Panel 1',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
panel.group.locked = true;
|
||||||
|
panel.group.header.hidden = true;
|
||||||
|
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_2',
|
||||||
|
component: 'default',
|
||||||
|
params: {
|
||||||
|
title: 'Panel 2',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_3',
|
||||||
|
component: 'default',
|
||||||
|
params: {
|
||||||
|
title: 'Panel 3',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_4',
|
||||||
|
component: 'default',
|
||||||
|
params: {
|
||||||
|
title: 'Panel 4',
|
||||||
|
},
|
||||||
|
position: { referencePanel: 'panel_1', direction: 'right' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const panel5 = event.api.addPanel({
|
||||||
|
id: 'panel_5',
|
||||||
|
component: 'default',
|
||||||
|
params: {
|
||||||
|
title: 'Panel 5',
|
||||||
|
},
|
||||||
|
position: { referencePanel: 'panel_3', direction: 'right' },
|
||||||
|
});
|
||||||
|
|
||||||
|
// panel5.group!.model.header.hidden = true;
|
||||||
|
// panel5.group!.model.locked = true;
|
||||||
|
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_6',
|
||||||
|
component: 'default',
|
||||||
|
params: {
|
||||||
|
title: 'Panel 6',
|
||||||
|
},
|
||||||
|
position: { referencePanel: 'panel_5', direction: 'below' },
|
||||||
|
});
|
||||||
|
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_7',
|
||||||
|
component: 'default',
|
||||||
|
params: {
|
||||||
|
title: 'Panel 7',
|
||||||
|
},
|
||||||
|
position: { referencePanel: 'panel_6', direction: 'right' },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DockviewReact
|
||||||
|
components={components}
|
||||||
|
onReady={onReady}
|
||||||
|
className={`${props.theme || 'dockview-theme-abyss'}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Container = (props: any) => {
|
||||||
|
const [value, setValue] = React.useState<string>('50');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{ height: '100%', display: 'flex', flexDirection: 'column' }}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
onChange={(event) => setValue(event.target.value)}
|
||||||
|
type="range"
|
||||||
|
min="1"
|
||||||
|
max="100"
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
<div style={{ height: `${value}%`, width: `${value}%` }}>
|
||||||
|
<App {...props} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Container;
|
@ -0,0 +1,20 @@
|
|||||||
|
import { StrictMode } from 'react';
|
||||||
|
import * as ReactDOMClient from 'react-dom/client';
|
||||||
|
import './styles.css';
|
||||||
|
import 'dockview/dist/styles/dockview.css';
|
||||||
|
|
||||||
|
import App from './app';
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root');
|
||||||
|
|
||||||
|
if (rootElement) {
|
||||||
|
const root = ReactDOMClient.createRoot(rootElement);
|
||||||
|
|
||||||
|
root.render(
|
||||||
|
<StrictMode>
|
||||||
|
<div className="app">
|
||||||
|
<App />
|
||||||
|
</div>
|
||||||
|
</StrictMode>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
color: white;
|
||||||
|
font-family: sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "build/dist",
|
||||||
|
"module": "esnext",
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
|
"sourceMap": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"rootDir": "src",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strictNullChecks": true
|
||||||
|
}
|
||||||
|
}
|
32
packages/docs/sandboxes/react/dockview/resize/package.json
Normal file
32
packages/docs/sandboxes/react/dockview/resize/package.json
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "dockview.resize",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [
|
||||||
|
"dockview"
|
||||||
|
],
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "src/index.tsx",
|
||||||
|
"dependencies": {
|
||||||
|
"dockview": "*",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.0.28",
|
||||||
|
"@types/react-dom": "^18.0.11",
|
||||||
|
"typescript": "^4.9.5",
|
||||||
|
"react-scripts": "*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test --env=jsdom",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not ie <= 11",
|
||||||
|
"not op_mini all"
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is added to the
|
||||||
|
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||||
|
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<title>React App</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
You need to enable JavaScript to run this app.
|
||||||
|
</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
127
packages/docs/sandboxes/react/dockview/resize/src/app.tsx
Normal file
127
packages/docs/sandboxes/react/dockview/resize/src/app.tsx
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import {
|
||||||
|
DockviewReact,
|
||||||
|
DockviewReadyEvent,
|
||||||
|
IDockviewPanelProps,
|
||||||
|
} from 'dockview';
|
||||||
|
import * as React from 'react';
|
||||||
|
import './resize.scss';
|
||||||
|
|
||||||
|
const Default = (props: IDockviewPanelProps) => {
|
||||||
|
const [width, setWidth] = React.useState<number>(100);
|
||||||
|
const [height, setHeight] = React.useState<number>(100);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="resize-panel">
|
||||||
|
<div style={{ height: '25px' }}>{props.api.title}</div>
|
||||||
|
<div className="resize-control">
|
||||||
|
<span>Width:</span>
|
||||||
|
<input
|
||||||
|
value={width}
|
||||||
|
onChange={(e) => setWidth(Number(e.target.value))}
|
||||||
|
type="number"
|
||||||
|
min={50}
|
||||||
|
step={1}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
onClick={() => {
|
||||||
|
props.api.group.api.setSize({
|
||||||
|
width,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Resize Group
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
onClick={() => {
|
||||||
|
props.api.setSize({
|
||||||
|
width,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Resize panel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="resize-control">
|
||||||
|
<span>Height:</span>
|
||||||
|
<input
|
||||||
|
value={height}
|
||||||
|
onChange={(e) => setHeight(Number(e.target.value))}
|
||||||
|
type="number"
|
||||||
|
min={50}
|
||||||
|
step={1}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
onClick={() => {
|
||||||
|
props.api.group.api.setSize({
|
||||||
|
height,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Resize Group
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
onClick={() => {
|
||||||
|
props.api.setSize({
|
||||||
|
height,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Resize Panel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
default: Default,
|
||||||
|
};
|
||||||
|
|
||||||
|
const ResizeDockview = (props: { theme?: string }) => {
|
||||||
|
const onReady = (event: DockviewReadyEvent) => {
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_1',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_2',
|
||||||
|
component: 'default',
|
||||||
|
position: {
|
||||||
|
direction: 'right',
|
||||||
|
referencePanel: 'panel_1',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_3',
|
||||||
|
component: 'default',
|
||||||
|
position: {
|
||||||
|
direction: 'below',
|
||||||
|
referencePanel: 'panel_1',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_4',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
event.api.addPanel({
|
||||||
|
id: 'panel_5',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DockviewReact
|
||||||
|
className={`${props.theme || 'dockview-theme-abyss'}`}
|
||||||
|
onReady={onReady}
|
||||||
|
components={components}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ResizeDockview;
|
20
packages/docs/sandboxes/react/dockview/resize/src/index.tsx
Normal file
20
packages/docs/sandboxes/react/dockview/resize/src/index.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { StrictMode } from 'react';
|
||||||
|
import * as ReactDOMClient from 'react-dom/client';
|
||||||
|
import './styles.css';
|
||||||
|
import 'dockview/dist/styles/dockview.css';
|
||||||
|
|
||||||
|
import App from './app';
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root');
|
||||||
|
|
||||||
|
if (rootElement) {
|
||||||
|
const root = ReactDOMClient.createRoot(rootElement);
|
||||||
|
|
||||||
|
root.render(
|
||||||
|
<StrictMode>
|
||||||
|
<div className="app">
|
||||||
|
<App />
|
||||||
|
</div>
|
||||||
|
</StrictMode>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
.resize-panel {
|
||||||
|
padding: 10px;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
.resize-control {
|
||||||
|
display: flex;
|
||||||
|
height: 18px;
|
||||||
|
line-height: 18px;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 75px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
packages/docs/sandboxes/react/dockview/resize/src/styles.css
Normal file
16
packages/docs/sandboxes/react/dockview/resize/src/styles.css
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
color: white;
|
||||||
|
font-family: sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
}
|
18
packages/docs/sandboxes/react/dockview/resize/tsconfig.json
Normal file
18
packages/docs/sandboxes/react/dockview/resize/tsconfig.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "build/dist",
|
||||||
|
"module": "esnext",
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
|
"sourceMap": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"rootDir": "src",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strictNullChecks": true
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user