diff --git a/packages/dockview-core/src/__tests__/api/dockviewPanelApi.spec.ts b/packages/dockview-core/src/__tests__/api/dockviewPanelApi.spec.ts index d4255ab22..4061c4701 100644 --- a/packages/dockview-core/src/__tests__/api/dockviewPanelApi.spec.ts +++ b/packages/dockview-core/src/__tests__/api/dockviewPanelApi.spec.ts @@ -50,6 +50,7 @@ describe('groupPanelApi', () => { const accessor = fromPartial({ onDidAddPanel: jest.fn(), onDidRemovePanel: jest.fn(), + onDidActivePanelChange: jest.fn(), options: {}, onDidOptionsChange: jest.fn(), }); @@ -83,6 +84,7 @@ describe('groupPanelApi', () => { const accessor = fromPartial({ onDidAddPanel: jest.fn(), onDidRemovePanel: jest.fn(), + onDidActivePanelChange: jest.fn(), options: {}, onDidOptionsChange: jest.fn(), }); diff --git a/packages/dockview-core/src/__tests__/dockview/components/titlebar/tabsContainer.spec.ts b/packages/dockview-core/src/__tests__/dockview/components/titlebar/tabsContainer.spec.ts index bb79949cf..116f14cf3 100644 --- a/packages/dockview-core/src/__tests__/dockview/components/titlebar/tabsContainer.spec.ts +++ b/packages/dockview-core/src/__tests__/dockview/components/titlebar/tabsContainer.spec.ts @@ -17,6 +17,7 @@ describe('tabsContainer', () => { const accessor = fromPartial({ onDidAddPanel: jest.fn(), onDidRemovePanel: jest.fn(), + onDidActivePanelChange: jest.fn(), options: {}, onDidOptionsChange: jest.fn(), }); @@ -71,6 +72,7 @@ describe('tabsContainer', () => { id: 'testcomponentid', onDidAddPanel: jest.fn(), onDidRemovePanel: jest.fn(), + onDidActivePanelChange: jest.fn(), options: {}, onDidOptionsChange: jest.fn(), }); @@ -141,6 +143,7 @@ describe('tabsContainer', () => { id: 'testcomponentid', onDidAddPanel: jest.fn(), onDidRemovePanel: jest.fn(), + onDidActivePanelChange: jest.fn(), options: {}, onDidOptionsChange: jest.fn(), }); @@ -205,6 +208,7 @@ describe('tabsContainer', () => { id: 'testcomponentid', onDidAddPanel: jest.fn(), onDidRemovePanel: jest.fn(), + onDidActivePanelChange: jest.fn(), options: {}, onDidOptionsChange: jest.fn(), }); @@ -269,6 +273,7 @@ describe('tabsContainer', () => { id: 'testcomponentid', onDidAddPanel: jest.fn(), onDidRemovePanel: jest.fn(), + onDidActivePanelChange: jest.fn(), options: {}, onDidOptionsChange: jest.fn(), }); @@ -338,6 +343,7 @@ describe('tabsContainer', () => { id: 'testcomponentid', onDidAddPanel: jest.fn(), onDidRemovePanel: jest.fn(), + onDidActivePanelChange: jest.fn(), options: {}, onDidOptionsChange: jest.fn(), }); @@ -403,6 +409,7 @@ describe('tabsContainer', () => { id: 'testcomponentid', onDidAddPanel: jest.fn(), onDidRemovePanel: jest.fn(), + onDidActivePanelChange: jest.fn(), options: {}, onDidOptionsChange: jest.fn(), }); @@ -468,6 +475,7 @@ describe('tabsContainer', () => { options: {}, onDidAddPanel: jest.fn(), onDidRemovePanel: jest.fn(), + onDidActivePanelChange: jest.fn(), element: document.createElement('div'), addFloatingGroup: jest.fn(), doSetGroupActive: jest.fn(), @@ -525,6 +533,7 @@ describe('tabsContainer', () => { options: {}, onDidAddPanel: jest.fn(), onDidRemovePanel: jest.fn(), + onDidActivePanelChange: jest.fn(), element: document.createElement('div'), addFloatingGroup: jest.fn(), doSetGroupActive: jest.fn(), @@ -577,6 +586,7 @@ describe('tabsContainer', () => { options: {}, onDidAddPanel: jest.fn(), onDidRemovePanel: jest.fn(), + onDidActivePanelChange: jest.fn(), element: document.createElement('div'), addFloatingGroup: jest.fn(), getGroupPanel: jest.fn(), @@ -634,6 +644,7 @@ describe('tabsContainer', () => { options: {}, onDidAddPanel: jest.fn(), onDidRemovePanel: jest.fn(), + onDidActivePanelChange: jest.fn(), element: document.createElement('div'), addFloatingGroup: jest.fn(), getGroupPanel: jest.fn(), @@ -702,6 +713,7 @@ describe('tabsContainer', () => { options: {}, onDidAddPanel: jest.fn(), onDidRemovePanel: jest.fn(), + onDidActivePanelChange: jest.fn(), element: document.createElement('div'), addFloatingGroup: jest.fn(), getGroupPanel: jest.fn(), @@ -770,6 +782,7 @@ describe('tabsContainer', () => { options: {}, onDidAddPanel: jest.fn(), onDidRemovePanel: jest.fn(), + onDidActivePanelChange: jest.fn(), element: document.createElement('div'), addFloatingGroup: jest.fn(), getGroupPanel: jest.fn(), diff --git a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts index c8619b8e1..6109041bc 100644 --- a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts +++ b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts @@ -6917,4 +6917,91 @@ describe('dockviewComponent', () => { dockview.layout(1000, 1000); }); }); + + test('that arrow keys should activate appropriate tabs', () => { + dockview.layout(500, 1000); + + dockview.addPanel({ + id: 'panel1', + component: 'default', + }); + + dockview.addPanel({ + id: 'panel2', + component: 'default', + position: { referencePanel: 'panel1', direction: 'within' }, + }); + + dockview.addPanel({ + id: 'panel3', + component: 'default', + }); + + dockview.addPanel({ + id: 'panel4', + component: 'default', + position: { referencePanel: 'panel3', direction: 'below' }, + }); + + const panel1 = dockview.getGroupPanel('panel1')!; + const panel2 = dockview.getGroupPanel('panel2')!; + const panel3 = dockview.getGroupPanel('panel3')!; + const panel4 = dockview.getGroupPanel('panel4')!; + + panel1.api.setActive(); + + expect(panel1.api.isActive).toBeTruthy(); + expect(panel2.api.isActive).toBeFalsy(); + expect(panel3.api.isActive).toBeFalsy(); + expect(panel4.api.isActive).toBeFalsy(); + + const tabsContainer = (panel: IDockviewPanel) => + panel.api.group.element.querySelector('.tabs-container')!; + + const event = new KeyboardEvent('keydown', { key: 'ArrowRight' }); + + fireEvent(tabsContainer(panel1), event); + expect(panel1.api.isActive).toBeFalsy(); + expect(panel2.api.isActive).toBeTruthy(); + expect(panel3.api.isActive).toBeFalsy(); + expect(panel4.api.isActive).toBeFalsy(); + + fireEvent(tabsContainer(panel1), event); + expect(panel1.api.isActive).toBeFalsy(); + expect(panel2.api.isActive).toBeFalsy(); + expect(panel3.api.isActive).toBeTruthy(); + expect(panel4.api.isActive).toBeFalsy(); + + const event2 = new KeyboardEvent('keydown', { key: 'ArrowLeft' }); + + fireEvent(tabsContainer(panel1), event2); + expect(panel1.api.isActive).toBeFalsy(); + expect(panel2.api.isActive).toBeTruthy(); + expect(panel3.api.isActive).toBeFalsy(); + expect(panel4.api.isActive).toBeFalsy(); + + fireEvent(tabsContainer(panel1), event2); + expect(panel1.api.isActive).toBeTruthy(); + expect(panel2.api.isActive).toBeFalsy(); + expect(panel3.api.isActive).toBeFalsy(); + expect(panel4.api.isActive).toBeFalsy(); + + panel4.api.setActive(); + expect(panel1.api.isActive).toBeFalsy(); + expect(panel2.api.isActive).toBeFalsy(); + expect(panel3.api.isActive).toBeFalsy(); + expect(panel4.api.isActive).toBeTruthy(); + + fireEvent(tabsContainer(panel4), event2); + expect(panel1.api.isActive).toBeFalsy(); + expect(panel2.api.isActive).toBeFalsy(); + expect(panel3.api.isActive).toBeFalsy(); + expect(panel4.api.isActive).toBeTruthy(); + + fireEvent(tabsContainer(panel4), event); + expect(panel1.api.isActive).toBeFalsy(); + expect(panel2.api.isActive).toBeFalsy(); + expect(panel3.api.isActive).toBeFalsy(); + expect(panel4.api.isActive).toBeTruthy(); + }); }); diff --git a/packages/dockview-core/src/__tests__/dockview/dockviewGroupPanelModel.spec.ts b/packages/dockview-core/src/__tests__/dockview/dockviewGroupPanelModel.spec.ts index 6e24cd53f..02e1c8f6d 100644 --- a/packages/dockview-core/src/__tests__/dockview/dockviewGroupPanelModel.spec.ts +++ b/packages/dockview-core/src/__tests__/dockview/dockviewGroupPanelModel.spec.ts @@ -174,6 +174,8 @@ export class TestPanel implements IDockviewPanel { private _group: DockviewGroupPanel | undefined; private _params: IGroupPanelInitParameters | undefined; readonly view: IDockviewPanelModel; + readonly componentElId: string; + readonly tabComponentElId: string; get title() { return ''; @@ -189,6 +191,8 @@ export class TestPanel implements IDockviewPanel { constructor(public readonly id: string, public api: DockviewPanelApi) { this.view = new TestModel(id); + this.tabComponentElId = `tab-${id}`; + this.componentElId = `tab-panel-${id}`; this.init({ title: `${id}`, params: {}, @@ -266,6 +270,7 @@ describe('dockviewGroupPanelModel', () => { removeGroup: removeGroupMock, onDidAddPanel: () => ({ dispose: jest.fn() }), onDidRemovePanel: () => ({ dispose: jest.fn() }), + onDidActivePanelChange: () => ({ dispose: jest.fn() }), overlayRenderContainer: new OverlayRenderContainer( document.createElement('div'), fromPartial({}) @@ -653,6 +658,7 @@ describe('dockviewGroupPanelModel', () => { onDidAddPanel: jest.fn(), onDidRemovePanel: jest.fn(), onDidOptionsChange: jest.fn(), + onDidActivePanelChange: jest.fn(), }); const groupviewMock = jest.fn, []>( @@ -716,6 +722,7 @@ describe('dockviewGroupPanelModel', () => { onDidAddPanel: jest.fn(), onDidRemovePanel: jest.fn(), onDidOptionsChange: jest.fn(), + onDidActivePanelChange: jest.fn(), }); const groupviewMock = jest.fn, []>( @@ -808,6 +815,7 @@ describe('dockviewGroupPanelModel', () => { doSetGroupActive: jest.fn(), onDidAddPanel: jest.fn(), onDidRemovePanel: jest.fn(), + onDidActivePanelChange: jest.fn(), overlayRenderContainer: new OverlayRenderContainer( document.createElement('div'), fromPartial({}) @@ -875,6 +883,7 @@ describe('dockviewGroupPanelModel', () => { doSetGroupActive: jest.fn(), onDidAddPanel: jest.fn(), onDidRemovePanel: jest.fn(), + onDidActivePanelChange: jest.fn(), overlayRenderContainer: new OverlayRenderContainer( document.createElement('div'), fromPartial({}) @@ -949,6 +958,7 @@ describe('dockviewGroupPanelModel', () => { doSetGroupActive: jest.fn(), onDidAddPanel: jest.fn(), onDidRemovePanel: jest.fn(), + onDidActivePanelChange: jest.fn(), overlayRenderContainer: new OverlayRenderContainer( document.createElement('div'), fromPartial({}) @@ -1030,6 +1040,7 @@ describe('dockviewGroupPanelModel', () => { return { id: 'testgroupid', model: groupView, + dispose: jest.fn() }; }); diff --git a/packages/dockview-core/src/__tests__/gridview/gridviewPanel.spec.ts b/packages/dockview-core/src/__tests__/gridview/gridviewPanel.spec.ts index 18ed7d1d6..7f9824b11 100644 --- a/packages/dockview-core/src/__tests__/gridview/gridviewPanel.spec.ts +++ b/packages/dockview-core/src/__tests__/gridview/gridviewPanel.spec.ts @@ -7,6 +7,7 @@ describe('gridviewPanel', () => { return { onDidAddPanel: jest.fn(), onDidRemovePanel: jest.fn(), + onDidActivePanelChange: jest.fn(), options: {}, onDidOptionsChange: jest.fn(), } as any; diff --git a/packages/dockview-core/src/dockview/components/panel/content.ts b/packages/dockview-core/src/dockview/components/panel/content.ts index 6c37035b0..c7de9e568 100644 --- a/packages/dockview-core/src/dockview/components/panel/content.ts +++ b/packages/dockview-core/src/dockview/components/panel/content.ts @@ -51,6 +51,7 @@ export class ContentContainer super(); this._element = document.createElement('div'); this._element.className = 'dv-content-container'; + this.element.role = 'tabpanel'; this._element.tabIndex = -1; this.addDisposables(this._onDidFocus, this._onDidBlur); diff --git a/packages/dockview-core/src/dockview/components/tab/defaultTab.ts b/packages/dockview-core/src/dockview/components/tab/defaultTab.ts index 205e4e562..82d4b4b3f 100644 --- a/packages/dockview-core/src/dockview/components/tab/defaultTab.ts +++ b/packages/dockview-core/src/dockview/components/tab/defaultTab.ts @@ -6,7 +6,7 @@ import { createCloseButton } from '../../../svg'; export class DefaultTab extends CompositeDisposable implements ITabRenderer { private readonly _element: HTMLElement; private readonly _content: HTMLElement; - private readonly action: HTMLElement; + private readonly action: HTMLButtonElement; private _title: string | undefined; get element(): HTMLElement { @@ -22,22 +22,39 @@ export class DefaultTab extends CompositeDisposable implements ITabRenderer { this._content = document.createElement('div'); this._content.className = 'dv-default-tab-content'; - this.action = document.createElement('div'); + this.action = document.createElement('button'); + this.action.type = 'button'; + this.action.className = 'dv-default-tab-action'; + // originally hide this, so only when it is focused is it read out. + // so the SR when focused on the tab, doesn't read " Close Button" + this.action.ariaHidden = 'true'; + this.action.appendChild(createCloseButton()); this._element.appendChild(this._content); this._element.appendChild(this.action); + this.addDisposables( + addDisposableListener(this.action, 'focus', (event) => { + this.action.ariaHidden = 'false'; + }), + addDisposableListener(this.action, 'blur', (event) => { + this.action.ariaHidden = 'true'; + }) + ); + this.render(); } init(params: GroupPanelPartInitParameters): void { this._title = params.title; + this.action.ariaLabel = `Close "${this._title}" tab`; this.addDisposables( params.api.onDidTitleChange((event) => { this._title = event.title; + this.action.ariaLabel = `Close "${event.title}" tab`; this.render(); }), addDisposableListener(this.action, 'pointerdown', (ev) => { @@ -50,6 +67,18 @@ export class DefaultTab extends CompositeDisposable implements ITabRenderer { ev.preventDefault(); params.api.close(); + }), + addDisposableListener(this.action, 'keydown', (ev) => { + if (ev.defaultPrevented) { + return; + } + + switch (ev.key) { + case 'Enter': + case 'Space': + params.api.close(); + break; + } }) ); diff --git a/packages/dockview-core/src/dockview/components/tab/tab.ts b/packages/dockview-core/src/dockview/components/tab/tab.ts index c56bcaccb..8e92db1f8 100644 --- a/packages/dockview-core/src/dockview/components/tab/tab.ts +++ b/packages/dockview-core/src/dockview/components/tab/tab.ts @@ -53,6 +53,9 @@ export class Tab extends CompositeDisposable { private readonly _onPointDown = new Emitter(); readonly onPointerDown: Event = this._onPointDown.event; + private readonly _onKeyDown = new Emitter(); + readonly onKeyDown: Event = this._onKeyDown.event; + private readonly _onDropped = new Emitter(); readonly onDrop: Event = this._onDropped.event; @@ -74,8 +77,16 @@ export class Tab extends CompositeDisposable { this._element = document.createElement('div'); this._element.className = 'dv-tab'; - this._element.tabIndex = 0; + this._element.role = 'tab'; + this._element.tabIndex = -1; this._element.draggable = true; + this._element.ariaSelected = 'false'; + + Object.entries(this.panel.tabComponentAttributes).forEach( + ([key, value]) => { + this._element.setAttribute(key, value); + } + ); toggleClass(this.element, 'dv-inactive-tab', true); @@ -139,6 +150,9 @@ export class Tab extends CompositeDisposable { addDisposableListener(this._element, 'pointerdown', (event) => { this._onPointDown.fire(event); }), + addDisposableListener(this._element, 'keydown', (event) => { + this._onKeyDown.fire(event); + }), this.dropTarget.onDrop((event) => { this._onDropped.fire(event); }), @@ -147,6 +161,9 @@ export class Tab extends CompositeDisposable { } public setActive(isActive: boolean): void { + this.element.tabIndex = isActive ? 0 : -1; + this.element.ariaSelected = isActive.toString(); + toggleClass(this.element, 'dv-active-tab', isActive); toggleClass(this.element, 'dv-inactive-tab', !isActive); } diff --git a/packages/dockview-core/src/dockview/components/titlebar/tabs.ts b/packages/dockview-core/src/dockview/components/titlebar/tabs.ts index 53486ed7c..a0ca25df3 100644 --- a/packages/dockview-core/src/dockview/components/titlebar/tabs.ts +++ b/packages/dockview-core/src/dockview/components/titlebar/tabs.ts @@ -98,6 +98,10 @@ export class Tabs extends CompositeDisposable { this._tabsList = document.createElement('div'); this._tabsList.className = 'dv-tabs-container dv-horizontal'; + this._tabsList.ariaOrientation = 'horizontal'; + this.element.role = 'tablist'; + this.element.ariaLabel = + 'Use the Left Arrow to select the previous tab, Right Arrow for the next tab, Home for the first tab, and End for the last tab. Press Enter to select the focused tab.'; this.showTabsOverflowControl = options.showTabsOverflowControl; @@ -115,6 +119,13 @@ export class Tabs extends CompositeDisposable { this._onWillShowOverlay, this._onDrop, this._onTabDragStart, + this.accessor.onDidActivePanelChange((e) => { + if (e?.api.group === this.group) { + this.selectedIndex = this.indexOf(e.id); + } else { + this.selectedIndex = -1; + } + }), addDisposableListener(this.element, 'pointerdown', (event) => { if (event.defaultPrevented) { return; @@ -154,6 +165,7 @@ export class Tabs extends CompositeDisposable { for (const tab of this._tabs) { const isActivePanel = panel.id === tab.value.panel.id; tab.value.setActive(isActivePanel); + tab.value.panel.runEvents(); if (isActivePanel) { const element = tab.value.element; @@ -224,6 +236,40 @@ export class Tabs extends CompositeDisposable { break; } }), + tab.onKeyDown((event) => { + if (event.defaultPrevented) { + return; + } + + const index = this.indexOf(tab.panel.id); + let nextTab: Tab | undefined = undefined; + + switch (event.key) { + case 'ArrowLeft': + nextTab = this.tabs[Math.max(0, index - 1)]; + break; + case 'ArrowRight': + nextTab = this.tabs[Math.min(this.size - 1, index + 1)]; + break; + case 'Home': + nextTab = this.tabs[0]; + break; + case 'End': + nextTab = this.tabs[this.size - 1]; + break; + case 'Enter': + case 'Space': + nextTab = tab; + } + + if ( + nextTab != null && + this.group.activePanel !== nextTab.panel + ) { + nextTab.element.focus(); + this.group.model.openPanel(nextTab.panel); + } + }), tab.onDrop((event) => { this._onDrop.fire({ event: event.nativeEvent, diff --git a/packages/dockview-core/src/dockview/dockviewComponent.scss b/packages/dockview-core/src/dockview/dockviewComponent.scss index 440f6cc5e..df182caab 100644 --- a/packages/dockview-core/src/dockview/dockviewComponent.scss +++ b/packages/dockview-core/src/dockview/dockviewComponent.scss @@ -20,6 +20,9 @@ &.dv-active-group { > .dv-tabs-and-actions-container { .dv-tabs-container > .dv-tab { + &:focus { + outline: 1px solid var(--dv-paneview-active-outline-color); + } &.dv-active-tab { background-color: var( --dv-activegroup-visiblepanel-tab-background-color @@ -38,6 +41,9 @@ &.dv-inactive-group { > .dv-tabs-and-actions-container { .dv-tabs-container > .dv-tab { + &:focus { + outline: 1px solid var(--dv-paneview-active-outline-color); + } &.dv-active-tab { background-color: var( --dv-inactivegroup-visiblepanel-tab-background-color diff --git a/packages/dockview-core/src/dockview/dockviewComponent.ts b/packages/dockview-core/src/dockview/dockviewComponent.ts index a0c4fb6f1..43320e6c6 100644 --- a/packages/dockview-core/src/dockview/dockviewComponent.ts +++ b/packages/dockview-core/src/dockview/dockviewComponent.ts @@ -2524,6 +2524,12 @@ export class DockviewComponent tabComponent ); + Object.entries(options.componentAttributes ?? {}).forEach( + ([key, value]) => { + view.content.element.setAttribute(key, value); + } + ); + const panel = new DockviewPanel( options.id, contentComponent, @@ -2538,6 +2544,8 @@ export class DockviewComponent minimumHeight: options.minimumHeight, maximumWidth: options.maximumWidth, maximumHeight: options.maximumHeight, + componentAttributes: options.componentAttributes, + tabComponentAttributes: options.tabComponentAttributes, } ); diff --git a/packages/dockview-core/src/dockview/dockviewPanel.ts b/packages/dockview-core/src/dockview/dockviewPanel.ts index bf71ca6c1..44c2f2b0c 100644 --- a/packages/dockview-core/src/dockview/dockviewPanel.ts +++ b/packages/dockview-core/src/dockview/dockviewPanel.ts @@ -19,6 +19,8 @@ export interface IDockviewPanel extends IDisposable, IPanel { readonly api: DockviewPanelApi; readonly title: string | undefined; readonly params: Parameters | undefined; + readonly componentAttributes: Record; + readonly tabComponentAttributes: Record; readonly minimumWidth?: number; readonly minimumHeight?: number; readonly maximumWidth?: number; @@ -40,6 +42,9 @@ export class DockviewPanel { readonly api: DockviewPanelApiImpl; + readonly componentAttributes: Record; + readonly tabComponentAttributes: Record; + private _group: DockviewGroupPanel; private _params?: Parameters; private _title: string | undefined; @@ -90,7 +95,10 @@ export class DockviewPanel private readonly containerApi: DockviewApi, group: DockviewGroupPanel, readonly view: IDockviewPanelModel, - options: { renderer?: DockviewPanelRenderer } & Partial + options: { renderer?: DockviewPanelRenderer } & Partial & { + componentAttributes?: Record; + tabComponentAttributes?: Record; + } ) { super(); this._renderer = options.renderer; @@ -100,6 +108,9 @@ export class DockviewPanel this._maximumWidth = options.maximumWidth; this._maximumHeight = options.maximumHeight; + this.componentAttributes = options.componentAttributes ?? {}; + this.tabComponentAttributes = options.tabComponentAttributes ?? {}; + this.api = new DockviewPanelApiImpl( this, this._group, diff --git a/packages/dockview-core/src/dockview/options.ts b/packages/dockview-core/src/dockview/options.ts index 39e66d2c3..bde41b22d 100644 --- a/packages/dockview-core/src/dockview/options.ts +++ b/packages/dockview-core/src/dockview/options.ts @@ -260,6 +260,8 @@ export type AddPanelOptions

= { inactive?: boolean; initialWidth?: number; initialHeight?: number; + componentAttributes?: Record; + tabComponentAttributes?: Record; } & Partial & Partial; diff --git a/packages/dockview-core/src/gridview/gridview.ts b/packages/dockview-core/src/gridview/gridview.ts index bda8c0638..f44f81286 100644 --- a/packages/dockview-core/src/gridview/gridview.ts +++ b/packages/dockview-core/src/gridview/gridview.ts @@ -642,7 +642,7 @@ export class Gridview implements IDisposable { } this._root = root; - this.element.appendChild(this._root.element); + this.element.prepend(this._root.element); this.disposable.value = this._root.onDidChange((e) => { this._onDidChange.fire(e); }); @@ -698,7 +698,7 @@ export class Gridview implements IDisposable { this._root.addChild(oldRoot, Sizing.Distribute, 0); } - this.element.appendChild(this._root.element); + this.element.prepend(this._root.element); this.disposable.value = this._root.onDidChange((e) => { this._onDidChange.fire(e); diff --git a/packages/dockview-core/src/overlay/overlayRenderContainer.ts b/packages/dockview-core/src/overlay/overlayRenderContainer.ts index f83f50e5e..c16af704a 100644 --- a/packages/dockview-core/src/overlay/overlayRenderContainer.ts +++ b/packages/dockview-core/src/overlay/overlayRenderContainer.ts @@ -73,12 +73,12 @@ export class OverlayRenderContainer extends CompositeDisposable { if (!this.map[panel.api.id]) { const element = createFocusableElement(); element.className = 'dv-render-overlay'; + element.tabIndex = 0; this.map[panel.api.id] = { panel, disposable: Disposable.NONE, destroy: Disposable.NONE, - element, }; } diff --git a/packages/dockview-core/src/splitview/splitview.ts b/packages/dockview-core/src/splitview/splitview.ts index c511d54f6..57d909467 100644 --- a/packages/dockview-core/src/splitview/splitview.ts +++ b/packages/dockview-core/src/splitview/splitview.ts @@ -102,7 +102,7 @@ export class Splitview { private readonly sashContainer: HTMLElement; private readonly viewItems: ViewItem[] = []; private readonly sashes: ISashItem[] = []; - private _orientation: Orientation; + private _orientation = Orientation.VERTICAL; private _size = 0; private _orthogonalSize = 0; private _contentSize = 0; @@ -159,12 +159,20 @@ export class Splitview { this.size = this.orthogonalSize; this.orthogonalSize = tmp; - removeClasses(this.element, 'dv-horizontal', 'dv-vertical'); - this.element.classList.add( - this.orientation == Orientation.HORIZONTAL - ? 'dv-horizontal' - : 'dv-vertical' + toggleClass( + this.element, + 'dv-horizontal', + this._orientation == Orientation.HORIZONTAL ); + toggleClass( + this.element, + 'dv-vertical', + this._orientation == Orientation.VERTICAL + ); + this.element.ariaOrientation = + this.orientation == Orientation.HORIZONTAL + ? 'horizontal' + : 'vertical'; } get minimumSize(): number { @@ -227,8 +235,8 @@ export class Splitview { private readonly container: HTMLElement, options: SplitViewOptions ) { - this._orientation = options.orientation ?? Orientation.VERTICAL; this.element = this.createContainer(); + this.orientation = options.orientation ?? Orientation.VERTICAL; this.margin = options.margin ?? 0; @@ -1143,11 +1151,7 @@ export class Splitview { private createContainer(): HTMLElement { const element = document.createElement('div'); - const orientationClassname = - this._orientation === Orientation.HORIZONTAL - ? 'dv-horizontal' - : 'dv-vertical'; - element.className = `dv-split-view-container ${orientationClassname}`; + element.className = `dv-split-view-container`; return element; }