Merge remote-tracking branch 'upstream/master' into tab-accessibility

This commit is contained in:
Ademola Adedeji 2025-02-19 09:35:57 -04:00
commit 86d1ea7f01
19 changed files with 345 additions and 50 deletions

View File

@ -2,7 +2,7 @@
"packages": [ "packages": [
"packages/*" "packages/*"
], ],
"version": "3.1.0", "version": "3.2.0",
"npmClient": "yarn", "npmClient": "yarn",
"command": { "command": {
"publish": { "publish": {

View File

@ -1,6 +1,6 @@
{ {
"name": "dockview-angular", "name": "dockview-angular",
"version": "3.1.0", "version": "3.2.0",
"description": "Zero dependency layout manager supporting tabs, grids and splitviews", "description": "Zero dependency layout manager supporting tabs, grids and splitviews",
"keywords": [ "keywords": [
"splitview", "splitview",
@ -54,6 +54,6 @@
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview --coverage" "test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview --coverage"
}, },
"dependencies": { "dependencies": {
"dockview-core": "^3.1.0" "dockview-core": "^3.2.0"
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "dockview-core", "name": "dockview-core",
"version": "3.1.0", "version": "3.2.0",
"description": "Zero dependency layout manager supporting tabs, grids and splitviews", "description": "Zero dependency layout manager supporting tabs, grids and splitviews",
"keywords": [ "keywords": [
"splitview", "splitview",

View File

@ -49,8 +49,8 @@ export class Tab extends CompositeDisposable {
private readonly dropTarget: Droptarget; private readonly dropTarget: Droptarget;
private content: ITabRenderer | undefined = undefined; private content: ITabRenderer | undefined = undefined;
private readonly _onChanged = new Emitter<MouseEvent>(); private readonly _onPointDown = new Emitter<MouseEvent>();
readonly onChanged: Event<MouseEvent> = this._onChanged.event; readonly onPointerDown: Event<MouseEvent> = this._onPointDown.event;
private readonly _onDropped = new Emitter<DroptargetEvent>(); private readonly _onDropped = new Emitter<DroptargetEvent>();
readonly onDrop: Event<DroptargetEvent> = this._onDropped.event; readonly onDrop: Event<DroptargetEvent> = this._onDropped.event;
@ -123,7 +123,7 @@ export class Tab extends CompositeDisposable {
this.onWillShowOverlay = this.dropTarget.onWillShowOverlay; this.onWillShowOverlay = this.dropTarget.onWillShowOverlay;
this.addDisposables( this.addDisposables(
this._onChanged, this._onPointDown,
this._onDropped, this._onDropped,
this._onDragStart, this._onDragStart,
dragHandler.onDragStart((event) => { dragHandler.onDragStart((event) => {
@ -131,11 +131,7 @@ export class Tab extends CompositeDisposable {
}), }),
dragHandler, dragHandler,
addDisposableListener(this._element, 'pointerdown', (event) => { addDisposableListener(this._element, 'pointerdown', (event) => {
if (event.defaultPrevented) { this._onPointDown.fire(event);
return;
}
this._onChanged.fire(event);
}), }),
this.dropTarget.onDrop((event) => { this.dropTarget.onDrop((event) => {
this._onDropped.fire(event); this._onDropped.fire(event);

View File

@ -360,7 +360,7 @@ export class TabsContainer
tab.onDragStart((event) => { tab.onDragStart((event) => {
this._onTabDragStart.fire({ nativeEvent: event, panel }); this._onTabDragStart.fire({ nativeEvent: event, panel });
}), }),
tab.onChanged((event) => { tab.onPointerDown((event) => {
if (event.defaultPrevented) { if (event.defaultPrevented) {
return; return;
} }
@ -399,9 +399,6 @@ export class TabsContainer
this.group.model.openPanel(panel); this.group.model.openPanel(panel);
} }
break; break;
case 1: // middle click
panel.api.close();
break;
} }
}), }),
tab.onDrop((event) => { tab.onDrop((event) => {

View File

@ -117,15 +117,16 @@
-ms-user-select: none; // IE 10 and IE 11 -ms-user-select: none; // IE 10 and IE 11
touch-action: none; touch-action: none;
&:not(.disabled):active { &:not(.disabled):active,
transition: background-color 0.1s ease-in-out;
background-color: var(--dv-active-sash-color, transparent);
}
&:not(.disabled):hover { &:not(.disabled):hover {
background-color: var(--dv-active-sash-color, transparent); background-color: var(--dv-active-sash-color, transparent);
transition: background-color 0.1s ease-in-out; transition-property: background-color;
transition-delay: 0.5s; transition-timing-function: ease-in-out;
transition-duration: var(
--dv-active-sash-transition-duration,
0.1s
);
transition-delay: var(--dv-active-sash-transition-delay, 0.5s);
} }
} }
} }

View File

@ -8,6 +8,8 @@
--dv-icon-hover-background-color: rgba(90, 93, 94, 0.31); --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31);
--dv-floating-box-shadow: 8px 8px 8px 0px rgba(83, 89, 93, 0.5); --dv-floating-box-shadow: 8px 8px 8px 0px rgba(83, 89, 93, 0.5);
--dv-overlay-z-index: 999; --dv-overlay-z-index: 999;
--dv-active-sash-transition-duration: 0.1s;
--dv-active-sash-transition-delay: 0.5s;
} }
@mixin dockview-theme-dark-mixin { @mixin dockview-theme-dark-mixin {

View File

@ -1,6 +1,6 @@
{ {
"name": "dockview-react", "name": "dockview-react",
"version": "3.1.0", "version": "3.2.0",
"description": "Zero dependency layout manager supporting tabs, grids and splitviews", "description": "Zero dependency layout manager supporting tabs, grids and splitviews",
"keywords": [ "keywords": [
"splitview", "splitview",
@ -54,6 +54,6 @@
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview-react --coverage" "test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview-react --coverage"
}, },
"dependencies": { "dependencies": {
"dockview": "^3.1.0" "dockview": "^3.2.0"
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "dockview-vue", "name": "dockview-vue",
"version": "3.1.0", "version": "3.2.0",
"description": "Zero dependency layout manager supporting tabs, grids and splitviews", "description": "Zero dependency layout manager supporting tabs, grids and splitviews",
"keywords": [ "keywords": [
"splitview", "splitview",
@ -52,7 +52,7 @@
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview-vue --coverage" "test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview-vue --coverage"
}, },
"dependencies": { "dependencies": {
"dockview-core": "^3.1.0" "dockview-core": "^3.2.0"
}, },
"peerDependencies": { "peerDependencies": {
"vue": "^3.4.0" "vue": "^3.4.0"

View File

@ -1,6 +1,6 @@
{ {
"name": "dockview", "name": "dockview",
"version": "3.1.0", "version": "3.2.0",
"description": "Zero dependency layout manager supporting tabs, grids and splitviews", "description": "Zero dependency layout manager supporting tabs, grids and splitviews",
"keywords": [ "keywords": [
"splitview", "splitview",
@ -54,7 +54,7 @@
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview --coverage" "test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview --coverage"
}, },
"dependencies": { "dependencies": {
"dockview-core": "^3.1.0" "dockview-core": "^3.2.0"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"

View File

@ -32,10 +32,15 @@ export const DockviewDefaultTab: React.FunctionComponent<
params: _params, params: _params,
hideClose, hideClose,
closeActionOverride, closeActionOverride,
onPointerDown,
onPointerUp,
onPointerLeave,
...rest ...rest
}) => { }) => {
const title = useTitle(api); const title = useTitle(api);
const isMiddleMouseButton = React.useRef<boolean>(false);
const onClose = React.useCallback( const onClose = React.useCallback(
(event: React.MouseEvent<HTMLSpanElement>) => { (event: React.MouseEvent<HTMLSpanElement>) => {
event.preventDefault(); event.preventDefault();
@ -49,21 +54,52 @@ export const DockviewDefaultTab: React.FunctionComponent<
[api, closeActionOverride] [api, closeActionOverride]
); );
const onPointerDown = React.useCallback((e: React.MouseEvent) => { const onBtnPointerDown = React.useCallback((event: React.MouseEvent) => {
e.preventDefault(); event.preventDefault();
}, []); }, []);
const _onPointerDown = React.useCallback(
(event: React.PointerEvent<HTMLDivElement>) => {
isMiddleMouseButton.current = event.button === 1;
onPointerDown?.(event);
},
[onPointerDown]
);
const _onPointerUp = React.useCallback(
(event: React.PointerEvent<HTMLDivElement>) => {
if (isMiddleMouseButton && event.button === 1 && !hideClose) {
isMiddleMouseButton.current = false;
onClose(event);
}
onPointerUp?.(event);
},
[onPointerUp, onClose, hideClose]
);
const _onPointerLeave = React.useCallback(
(event: React.PointerEvent<HTMLDivElement>) => {
isMiddleMouseButton.current = false;
onPointerLeave?.(event);
},
[onPointerLeave]
);
return ( return (
<div <div
data-testid="dockview-dv-default-tab" data-testid="dockview-dv-default-tab"
{...rest} {...rest}
onPointerDown={_onPointerDown}
onPointerUp={_onPointerUp}
onPointerLeave={_onPointerLeave}
className="dv-default-tab" className="dv-default-tab"
> >
<span className="dv-default-tab-content">{title}</span> <span className="dv-default-tab-content">{title}</span>
{!hideClose && ( {!hideClose && (
<div <div
className="dv-default-tab-action" className="dv-default-tab-action"
onPointerDown={onPointerDown} onPointerDown={onBtnPointerDown}
onClick={onClose} onClick={onClose}
> >
<CloseButton /> <CloseButton />

View File

@ -0,0 +1,18 @@
---
slug: dockview-3.1.1-release
title: Dockview 3.1.1
tags: [release]
---
# Release Notes
Please reference docs @ [dockview.dev](https://dockview.dev).
## 🚀 Features
## 🛠 Miscs
- Bug: Fix Middle mouse button to close tab [#835](https://github.com/mathuo/dockview/issues/853)
## 🔥 Breaking changes

View File

@ -0,0 +1,18 @@
---
slug: dockview-3.2.0-release
title: Dockview 3.2.0
tags: [release]
---
# Release Notes
Please reference docs @ [dockview.dev](https://dockview.dev).
## 🚀 Features
- Add CSS properties `--dv-active-sash-transition-duration` and `--dv-active-sash-transition-delay` [#835](https://github.com/mathuo/dockview/issues/859)
## 🛠 Miscs
## 🔥 Breaking changes

View File

@ -184,17 +184,8 @@ const config = {
label: 'API', label: 'API',
}, },
{ to: '/blog', label: 'Blog', position: 'left' }, { to: '/blog', label: 'Blog', position: 'left' },
{ href: '/templates', target:"_blank", label: 'Examples', position: 'left' },
{ to: '/demo', label: 'Demo', position: 'left' }, { to: '/demo', label: 'Demo', position: 'left' },
// {
// to: 'https://dockview.dev/typedocs',
// label: 'TSDoc',
// position: 'left',
// },
// {
// type: 'docsVersionDropdown',
// position: 'right',
// },
{ {
href: 'https://github.com/mathuo/dockview', href: 'https://github.com/mathuo/dockview',
position: 'right', position: 'right',

View File

@ -1,6 +1,6 @@
{ {
"name": "dockview-docs", "name": "dockview-docs",
"version": "3.1.0", "version": "3.2.0",
"private": true, "private": true,
"scripts": { "scripts": {
"build": "npm run build-templates && docusaurus build", "build": "npm run build-templates && docusaurus build",
@ -38,7 +38,7 @@
"ag-grid-react": "^31.0.2", "ag-grid-react": "^31.0.2",
"axios": "^1.6.3", "axios": "^1.6.3",
"clsx": "^2.1.0", "clsx": "^2.1.0",
"dockview": "^3.1.0", "dockview": "^3.2.0",
"prism-react-renderer": "^2.3.1", "prism-react-renderer": "^2.3.1",
"react-dnd": "^16.0.1", "react-dnd": "^16.0.1",
"react-laag": "^2.0.5", "react-laag": "^2.0.5",

View File

@ -1,7 +1,6 @@
import fs from 'fs-extra'; import fs from 'fs-extra';
import * as path from 'path'; import * as path from 'path';
import { argv } from 'process'; import { argv } from 'process';
import { execSync } from 'child_process';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
@ -86,7 +85,8 @@ function createIndexHTML(options) {
.map(([key, value]) => `"${key}": "${value}"`) .map(([key, value]) => `"${key}": "${value}"`)
.join(',\n')}` .join(',\n')}`
) )
.replace('{{app}}', options.app); .replace('{{app}}', options.app)
.replace('{{githubLink}}', options.githubUrl)
} }
const input_dir = path.join(__dirname, '../templates'); const input_dir = path.join(__dirname, '../templates');
@ -97,6 +97,8 @@ const FRAMEWORKS = ['react', 'vue', 'typescript'];
const list = []; const list = [];
const githubUrl = "https://github.com/mathuo/dockview/tree/master/packages/docs/templates"
for (const component of COMPONENTS) { for (const component of COMPONENTS) {
const componentDir = path.join(input_dir, component); const componentDir = path.join(input_dir, component);
@ -115,6 +117,9 @@ for (const component of COMPONENTS) {
path.join(componentDir, folder, framework, 'src'), path.join(componentDir, folder, framework, 'src'),
path.join(output, component, folder, framework, 'src') path.join(output, component, folder, framework, 'src')
); );
const templateGithubUrl = `${githubUrl}/${component}/${folder}/${framework}/src`
const template = createIndexHTML({ const template = createIndexHTML({
title: `Dockview | ${folder} ${framework}`, title: `Dockview | ${folder} ${framework}`,
app: app:
@ -127,6 +132,7 @@ for (const component of COMPONENTS) {
USE_LOCAL_CDN ? 'local' : 'remote' USE_LOCAL_CDN ? 'local' : 'remote'
], ],
}, },
githubUrl: templateGithubUrl
}); });
fs.writeFileSync( fs.writeFileSync(
path.join(output, component, folder, framework, 'index.html'), path.join(output, component, folder, framework, 'index.html'),

View File

@ -12,7 +12,7 @@
<style media="only screen"> <style media="only screen">
html, html,
body, body,
#app { #root {
height: 100%; height: 100%;
width: 100%; width: 100%;
margin: 0; margin: 0;
@ -22,6 +22,26 @@
BlinkMacSystemFont, Segoe UI, Roboto; BlinkMacSystemFont, Segoe UI, Roboto;
} }
#header {
height: 25px;
display: flex;
justify-content: flex-end;
align-items: center;
}
#header-btn {
height: 22px;
}
#gh-logo {
height: 22px;
width: 22px;
}
#app {
height: calc(100% - 25px);
}
html { html {
position: absolute; position: absolute;
top: 0; top: 0;
@ -31,7 +51,7 @@
} }
body { body {
padding: 16px; padding: 8px;
overflow: auto; overflow: auto;
} }
</style> </style>
@ -62,9 +82,18 @@
</head> </head>
<body> <body>
<div id="app"> <div id="root">
<script type="systemjs-module" src="import:{{app}}"></script> <div id="header">
<a target="_blank" rel="noopener noreferrer" href="{{githubLink}}">
<button id="header-btn">
View Source
</button>
</a>
<img id="gh-logo" src="https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png"/>
</div>
<div id="app"></div>
</div> </div>
<script type="systemjs-module" src="import:{{app}}"></script>
<object <object
id="loading-spinner" id="loading-spinner"
style=" style="

View File

@ -0,0 +1,23 @@
.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 .dockview-groupcontrol-demo-group-active {
padding: 0px 8px;
}
.dockview-groupcontrol-demo .dockview-groupcontrol-demo-active-panel {
color: yellow;
padding: 0px 8px;
}

View File

@ -0,0 +1,178 @@
import 'dockview-core/dist/styles/dockview.css';
import {
createDockview,
DockviewGroupPanel,
GroupPanelPartInitParameters,
IContentRenderer,
IGroupHeaderProps,
IHeaderActionsRenderer,
} from 'dockview-core';
import './index.css';
class Panel implements IContentRenderer {
private readonly _element: HTMLElement;
get element(): HTMLElement {
return this._element;
}
constructor() {
this._element = document.createElement('div');
this._element.style.display = 'flex';
this._element.style.justifyContent = 'center';
this._element.style.alignItems = 'center';
this._element.style.color = 'gray';
this._element.style.height = '100%';
}
init(parameters: GroupPanelPartInitParameters): void {
//
}
}
class PrefixHeader implements IHeaderActionsRenderer {
private readonly _element: HTMLElement;
get element(): HTMLElement {
return this._element;
}
constructor(group: DockviewGroupPanel) {
this._element = document.createElement('div');
this._element.className = 'dockview-groupcontrol-demo';
this._element.innerText = '🌲';
}
init(parameters: IGroupHeaderProps): void {
//
}
dispose(): void {
//
}
}
class RightHeaderActions implements IHeaderActionsRenderer {
private readonly _element: HTMLElement;
private readonly _disposables: (() => void)[] = [];
get element(): HTMLElement {
return this._element;
}
constructor(group: DockviewGroupPanel) {
this._element = document.createElement('div');
this._element.className = 'dockview-groupcontrol-demo';
}
init(parameters: IGroupHeaderProps): void {
const group = parameters.group;
const span = document.createElement('span');
span.className = 'dockview-groupcontrol-demo-group-active';
this._element.appendChild(span);
const d1 = group.api.onDidActiveChange(() => {
span.style.background = group.api.isActive ? 'green' : 'red';
span.innerText = `${
group.api.isActive ? 'Group Active' : 'Group Inactive'
}`;
});
span.style.background = group.api.isActive ? 'green' : 'red';
span.innerText = `${
group.api.isActive ? 'Group Active' : 'Group Inactive'
}`;
this._disposables.push(() => d1.dispose());
}
dispose(): void {
this._disposables.forEach((dispose) => dispose());
}
}
class LeftHeaderActions implements IHeaderActionsRenderer {
private readonly _element: HTMLElement;
private readonly _disposables: (() => void)[] = [];
get element(): HTMLElement {
return this._element;
}
constructor(group: DockviewGroupPanel) {
console.log('group', group);
this._element = document.createElement('div');
this._element.className = 'dockview-groupcontrol-demo';
}
init(parameters: IGroupHeaderProps): void {
const group = parameters.group;
const span = document.createElement('span');
span.className = 'dockview-groupcontrol-demo-active-panel';
this._element.appendChild(span);
const d1 = group.api.onDidActivePanelChange((event) => {
console.log('event', event);
span.innerText = `activePanel: ${event.panel?.id || 'null'}`;
});
console.log('group.activePanel', group.activePanel);
span.innerText = `activePanel: ${group.activePanel?.id || 'null'}`;
this._disposables.push(() => d1.dispose());
}
dispose(): void {
this._disposables.forEach((dispose) => dispose());
}
}
const api = createDockview(document.getElementById('app'), {
className: 'dockview-theme-abyss',
createComponent: (options): IContentRenderer => {
switch (options.name) {
case 'default':
return new Panel();
default:
throw new Error('Panel not found');
}
},
createPrefixHeaderActionComponent: (group): IHeaderActionsRenderer => {
return new PrefixHeader(group);
},
createLeftHeaderActionComponent: (group): IHeaderActionsRenderer => {
return new LeftHeaderActions(group);
},
createRightHeaderActionComponent: (group): IHeaderActionsRenderer => {
return new RightHeaderActions(group);
},
});
api.addPanel({
id: 'panel_1',
component: 'default',
title: 'Panel 1',
});
api.addPanel({
id: 'panel_2',
component: 'default',
title: 'Panel 2',
position: {
direction: 'right',
},
});
api.addPanel({
id: 'panel_3',
component: 'default',
title: 'Panel 3',
position: {
direction: 'below',
},
});