feat: further enhance view events

This commit is contained in:
mathuo 2021-10-20 23:16:11 +01:00
parent 0ef3df76f9
commit ee22a08b2e
No known key found for this signature in database
GPG Key ID: C6EEDEFD6CA07281
11 changed files with 193 additions and 104 deletions

View File

@ -1,5 +1,6 @@
import { GridviewComponent } from '../../gridview/gridviewComponent';
import { GridviewPanel } from '../../gridview/gridviewPanel';
import { GroupChangeEvent, GroupChangeKind } from '../../groupview/groupview';
import { IFrameworkPart } from '../../panel/types';
import { Orientation } from '../../splitview/core/splitview';
@ -276,4 +277,94 @@ describe('gridview', () => {
disposable.dispose();
});
test('gridview events', () => {
gridview.layout(800, 400);
let events: GroupChangeEvent[] = [];
const disposable = gridview.onGridEvent((e) => events.push(e));
gridview.addPanel({
id: 'panel_1',
component: 'default',
});
expect(events).toEqual([
{
kind: GroupChangeKind.ADD_GROUP,
},
{
kind: GroupChangeKind.GROUP_ACTIVE,
},
]);
events = [];
gridview.addPanel({
id: 'panel_2',
component: 'default',
});
expect(events).toEqual([
{
kind: GroupChangeKind.ADD_GROUP,
},
{
kind: GroupChangeKind.GROUP_ACTIVE,
},
]);
events = [];
gridview.addPanel({
id: 'panel_3',
component: 'default',
});
expect(events).toEqual([
{
kind: GroupChangeKind.ADD_GROUP,
},
{
kind: GroupChangeKind.GROUP_ACTIVE,
},
]);
events = [];
const panel1 = gridview.getPanel('panel_1');
const panel2 = gridview.getPanel('panel_2');
const panel3 = gridview.getPanel('panel_3');
gridview.removePanel(panel2);
expect(events).toEqual([
{
kind: GroupChangeKind.REMOVE_GROUP,
},
]);
events = [];
gridview.removePanel(panel3);
expect(events).toEqual([
{
kind: GroupChangeKind.REMOVE_GROUP,
},
{
kind: GroupChangeKind.GROUP_ACTIVE,
},
]);
events = [];
gridview.removePanel(panel1);
expect(events).toEqual([
{
kind: GroupChangeKind.REMOVE_GROUP,
},
{
kind: GroupChangeKind.GROUP_ACTIVE,
},
]);
events = [];
disposable.dispose();
});
});

View File

@ -9,12 +9,6 @@ export abstract class DragHandler extends CompositeDisposable {
private readonly _onDragStart = new Emitter<void>();
readonly onDragStart = this._onDragStart.event;
// private activeDrag: { id: string } | undefined;
// get isDragging() {
// return !!this.activeDrag;
// }
private disposable: IDisposable | undefined;
constructor(private readonly el: HTMLElement) {
@ -39,14 +33,8 @@ export abstract class DragHandler extends CompositeDisposable {
this.el.classList.add('dragged');
setTimeout(() => this.el.classList.remove('dragged'), 0);
// this.activeDrag = this.getData();
this.disposable?.dispose();
this.disposable = this.getData();
// if (event.dataTransfer) {
// event.dataTransfer.setData(DATA_KEY, stringifiedData);
// event.dataTransfer.effectAllowed = 'move';
// }
}),
addDisposableListener(this.el, 'dragend', (ev) => {
for (const iframe of this.iframes) {
@ -56,43 +44,6 @@ export abstract class DragHandler extends CompositeDisposable {
this.disposable?.dispose();
this.disposable = undefined;
// drop events fire before dragend so we can remove this safely
// LocalSelectionTransfer.getInstance().clearData(this.activeDrag);
// this.activeDrag = undefined;
}),
addDisposableListener(this.el, 'mousedown', (event) => {
if (event.defaultPrevented) {
return;
}
/**
* TODO: alternative to stopPropagation
*
* I need to stop the event propagation here since otherwise it'll be intercepted by event handlers
* on the tabs-container. I cannot use event.preventDefault() since I need the on DragStart event to occur
*/
event.stopPropagation();
/**
* //TODO mousedown focusing with draggable element (is there a better approach?)
*
* this mousedown event wants to focus the tab itself but if we call preventDefault()
* this would also prevent the dragStart event from firing. To get around this we propagate
* the onChanged event during the next tick of the event-loop, allowing the tab element to become
* focused on this tick and ensuring the dragstart event is not interrupted
*/
const oldFocus = focusedElement.element as HTMLElement;
setTimeout(() => {
oldFocus.focus();
// this._onChanged.fire({ kind: MouseEventKind.CLICK, event });
}, 0);
}),
addDisposableListener(this.el, 'contextmenu', (event) => {
// this._onChanged.fire({
// kind: MouseEventKind.CONTEXT_MENU,
// event,
// });
})
);
}

View File

@ -380,7 +380,7 @@ export class DockviewComponent
this.gridview.layout(this.width, this.height);
this._onGridEvent.fire({ kind: GroupChangeKind.NEW_LAYOUT });
this._onGridEvent.fire({ kind: GroupChangeKind.LAYOUT_FROM_JSON });
}
async closeAllGroups(): Promise<boolean> {

View File

@ -193,9 +193,9 @@ export abstract class BaseGrid<T extends IGridPanelView>
protected doAddGroup(group: T, location: number[] = [0], size?: number) {
this.gridview.addView(group, size ?? Sizing.Distribute, location);
this.doSetGroupActive(group);
this._onGridEvent.fire({ kind: GroupChangeKind.ADD_GROUP });
this.doSetGroupActive(group);
}
protected doRemoveGroup(
@ -215,12 +215,16 @@ export abstract class BaseGrid<T extends IGridPanelView>
this._groups.delete(group.id);
}
if (!options?.skipActive && this._groups.size > 0) {
this.doSetGroupActive(Array.from(this._groups.values())[0].value);
}
this._onGridEvent.fire({ kind: GroupChangeKind.REMOVE_GROUP });
if (!options?.skipActive && this._activeGroup === group) {
const groups = Array.from(this._groups.values());
this.doSetGroupActive(
groups.length > 0 ? groups[0].value : undefined
);
}
return view as T;
}
@ -228,7 +232,7 @@ export abstract class BaseGrid<T extends IGridPanelView>
return this._groups.get(id)?.value;
}
public doSetGroupActive(group: T, skipFocus?: boolean) {
public doSetGroupActive(group: T | undefined, skipFocus?: boolean) {
if (this._activeGroup === group) {
return;
}
@ -238,17 +242,20 @@ export abstract class BaseGrid<T extends IGridPanelView>
this._activeGroup.focus();
}
}
group.setActive(true);
if (!skipFocus) {
group.focus();
if (group) {
group.setActive(true);
if (!skipFocus) {
group.focus();
}
}
this._activeGroup = group;
this._onGridEvent.fire({ kind: GroupChangeKind.GROUP_ACTIVE });
}
public removeGroup(group: T) {
if (group === this._activeGroup) {
this._activeGroup = undefined;
}
this.doRemoveGroup(group);
}

View File

@ -248,7 +248,7 @@ export class GridviewComponent
}
}
this._onGridEvent.fire({ kind: GroupChangeKind.NEW_LAYOUT });
this._onGridEvent.fire({ kind: GroupChangeKind.LAYOUT_FROM_JSON });
}
movePanel(

View File

@ -27,11 +27,9 @@ export enum GroupChangeKind {
//
ADD_PANEL = 'ADD_PANEL',
REMOVE_PANEL = 'REMOVE_PANEL',
PANEL_OPEN = 'PANEL_OPEN',
PANEL_CLOSE = 'PANEL_CLOSE',
PANEL_ACTIVE = 'PANEL_ACTIVE',
//
NEW_LAYOUT = 'NEW_LAYOUT',
LAYOUT_FROM_JSON = 'LAYOUT_FROM_JSON',
LAYOUT = 'LAYOUT',
//
PANEL_CREATED = 'PANEL_CREATED',

View File

@ -0,0 +1,59 @@
import { addDisposableListener } from '../events';
import { PaneviewPanelApiImpl } from '../api/paneviewPanelApi';
import { CompositeDisposable } from '../lifecycle';
import { PanelUpdateEvent } from '../panel/types';
import { IPaneHeaderPart, PanePanelInitParameter } from './paneviewPanel';
import { MutableDisposable } from '../lifecycle';
export class DefaultHeader
extends CompositeDisposable
implements IPaneHeaderPart
{
private readonly disposable = new MutableDisposable();
private readonly _element: HTMLElement;
private readonly _content: HTMLElement;
private readonly _expander: HTMLElement;
private apiRef: { api: PaneviewPanelApiImpl | null } = { api: null };
get element() {
return this._element;
}
constructor() {
super();
this._element = document.createElement('div');
this.element.className = 'default-header';
this._content = document.createElement('span');
this._expander = document.createElement('a');
this.element.appendChild(this._content);
this.element.appendChild(this._expander);
this.addDisposables(
addDisposableListener(this._expander, 'click', () => {
this.apiRef.api?.setExpanded(!this.apiRef.api.isExpanded);
})
);
}
init(params: PanePanelInitParameter & { api: PaneviewPanelApiImpl }) {
this.apiRef.api = params.api;
this._content.textContent = params.title;
this._expander.textContent = params.api.isExpanded ? '<' : '>';
this.disposable.value = params.api.onDidExpansionChange((e) => {
this._expander.textContent = e.isExpanded ? '<' : '>';
});
}
update(params: PanelUpdateEvent) {
//
}
dispose() {
this.disposable.dispose();
super.dispose();
}
}

View File

@ -58,7 +58,6 @@ export abstract class DraggablePaneviewPanel extends PaneviewPanel {
private initDragFeatures() {
const id = this.id;
this.header!.draggable = true;
this.header!.tabIndex = 0;
this.handler = new (class PaneDragHandler extends DragHandler {
getData(): IDisposable {

View File

@ -27,6 +27,12 @@
.default-header {
background-color: var(--dv-group-view-background-color);
color: var(--dv-activegroup-visiblepanel-tab-color);
display: flex;
padding: 0px 8px;
> span {
flex-grow: 1;
}
}
}

View File

@ -1,13 +1,11 @@
import { PaneviewApi } from '../api/component.api';
import { PaneviewPanelApiImpl } from '../api/paneviewPanelApi';
import { createComponent } from '../panel/componentFactory';
import { addDisposableListener, Emitter, Event } from '../events';
import { Emitter, Event } from '../events';
import {
CompositeDisposable,
IDisposable,
MutableDisposable,
} from '../lifecycle';
import { PanelUpdateEvent } from '../panel/types';
import {
LayoutPriority,
Orientation,
@ -19,13 +17,13 @@ import {
IPaneBodyPart,
IPaneHeaderPart,
PaneviewPanel,
PanePanelInitParameter,
IPaneviewPanel,
} from './paneviewPanel';
import {
DraggablePaneviewPanel,
PaneviewDropEvent2,
} from './draggablePaneviewPanel';
import { DefaultHeader } from './defaultPaneviewHeader';
export interface SerializedPaneviewPanel {
snap?: boolean;
@ -49,36 +47,6 @@ export interface SerializedPaneview {
views: SerializedPaneviewPanel[];
}
class DefaultHeader extends CompositeDisposable implements IPaneHeaderPart {
private _element: HTMLElement;
private apiRef: { api: PaneviewPanelApiImpl | null } = { api: null };
get element() {
return this._element;
}
constructor() {
super();
this._element = document.createElement('div');
this._element.className = 'default-header';
this.addDisposables(
addDisposableListener(this.element, 'click', () => {
this.apiRef.api?.setExpanded(!this.apiRef.api.isExpanded);
})
);
}
init(params: PanePanelInitParameter & { api: PaneviewPanelApiImpl }) {
this.apiRef.api = params.api;
this._element.textContent = params.title;
}
update(params: PanelUpdateEvent) {
//
}
}
export class PaneFramework extends DraggablePaneviewPanel {
constructor(
private readonly options: {

View File

@ -1,5 +1,6 @@
import { PaneviewApi } from '../api/component.api';
import { PaneviewPanelApiImpl } from '../api/paneviewPanelApi';
import { addClasses, removeClasses } from '../dom';
import { addDisposableListener, Emitter, Event } from '../events';
import {
BasePanelView,
@ -180,6 +181,16 @@ export abstract class PaneviewPanel
this.api._onDidExpansionChange.fire({
isExpanded: isPanelExpanded,
});
}),
this.api.onDidFocusChange((e) => {
if (!this.header) {
return;
}
if (e.isFocused) {
addClasses(this.header, 'focused');
} else {
removeClasses(this.header, 'focused');
}
})
);
@ -270,7 +281,7 @@ export abstract class PaneviewPanel
private renderOnce() {
this.header = document.createElement('div');
this.header.tabIndex = -1;
this.header.tabIndex = 0;
this.header.className = 'pane-header';
this.header.style.height = `${this.headerSize}px`;
@ -281,7 +292,6 @@ export abstract class PaneviewPanel
this.element.appendChild(this.header);
this.body = document.createElement('div');
this.body.tabIndex = -1;
this.body.className = 'pane-body';