diff --git a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts index d8d887de5..ae45adf8f 100644 --- a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts +++ b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts @@ -8,9 +8,9 @@ import { PanelUpdateEvent } from '../../panel/types'; import { Orientation } from '../../splitview/splitview'; import { CompositeDisposable } from '../../lifecycle'; import { Emitter } from '../../events'; -import { DockviewPanel, IDockviewPanel } from '../../dockview/dockviewPanel'; +import { IDockviewPanel } from '../../dockview/dockviewPanel'; import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel'; -import { fireEvent, getByTestId, queryByTestId } from '@testing-library/dom'; +import { fireEvent, queryByTestId } from '@testing-library/dom'; import { getPanelData } from '../../dnd/dataTransfer'; import { GroupDragEvent, diff --git a/packages/dockview-core/src/__tests__/gridview/baseComponentGridview.spec.ts b/packages/dockview-core/src/__tests__/gridview/baseComponentGridview.spec.ts index 6371488ee..d8ed45209 100644 --- a/packages/dockview-core/src/__tests__/gridview/baseComponentGridview.spec.ts +++ b/packages/dockview-core/src/__tests__/gridview/baseComponentGridview.spec.ts @@ -68,8 +68,8 @@ class TestPanel implements IGridPanelView { class ClassUnderTest extends BaseGrid { readonly gridview = this.gridview; - constructor(options: BaseGridOptions) { - super(options); + constructor(parentElement: HTMLElement, options: BaseGridOptions) { + super(parentElement, options); } doRemoveGroup( @@ -106,8 +106,7 @@ class ClassUnderTest extends BaseGrid { describe('baseComponentGridview', () => { test('that .layout(...) force flag works', () => { - const cut = new ClassUnderTest({ - parentElement: document.createElement('div'), + const cut = new ClassUnderTest(document.createElement('div'), { orientation: Orientation.HORIZONTAL, proportionalLayout: true, }); @@ -131,8 +130,7 @@ describe('baseComponentGridview', () => { }); test('can add group', () => { - const cut = new ClassUnderTest({ - parentElement: document.createElement('div'), + const cut = new ClassUnderTest(document.createElement('div'), { orientation: Orientation.HORIZONTAL, proportionalLayout: true, }); diff --git a/packages/dockview-core/src/dockview/dockviewComponent.ts b/packages/dockview-core/src/dockview/dockviewComponent.ts index cf1c267ee..2a72db7fe 100644 --- a/packages/dockview-core/src/dockview/dockviewComponent.ts +++ b/packages/dockview-core/src/dockview/dockviewComponent.ts @@ -51,7 +51,12 @@ import { DockviewPanelModel } from './dockviewPanelModel'; import { getPanelData } from '../dnd/dataTransfer'; import { Parameters } from '../panel/types'; import { Overlay } from '../overlay/overlay'; -import { addTestId, toggleClass, watchElementResize } from '../dom'; +import { + addTestId, + getDockviewTheme, + toggleClass, + watchElementResize, +} from '../dom'; import { DockviewFloatingGroupPanel } from './dockviewFloatingGroupPanel'; import { GroupDragEvent, @@ -92,33 +97,6 @@ function moveGroupWithoutDestroying(options: { }); } -function getDockviewTheme(element: HTMLElement): string | undefined { - function toClassList(element: HTMLElement) { - const list: string[] = []; - - for (let i = 0; i < element.classList.length; i++) { - list.push(element.classList.item(i)!); - } - - return list; - } - - let theme: string | undefined = undefined; - let parent: HTMLElement | null = element; - - while (parent !== null) { - theme = toClassList(parent).find((cls) => - cls.startsWith('dockview-theme-') - ); - if (typeof theme === 'string') { - break; - } - parent = parent.parentElement; - } - - return theme; -} - export interface PanelReference { update: (event: { params: { [key: string]: any } }) => void; remove: () => void; @@ -362,23 +340,18 @@ export class DockviewComponent } constructor(parentElement: HTMLElement, options: DockviewComponentOptions) { - super({ + super(parentElement, { proportionalLayout: true, orientation: Orientation.HORIZONTAL, styles: options.hideBorders ? { separatorBorder: 'transparent' } : undefined, - parentElement: parentElement, disableAutoResizing: options.disableAutoResizing, locked: options.locked, margin: options.gap, className: options.className, }); - // const gready = document.createElement('div'); - // gready.className = 'dv-overlay-render-container'; - // this.gridview.element.appendChild(gready); - this.overlayRenderContainer = new OverlayRenderContainer( this.gridview.element, this @@ -1022,19 +995,9 @@ export class DockviewComponent override updateOptions(options: Partial): void { super.updateOptions(options); - const changed_floatingGroupBounds = - 'floatingGroupBounds' in options && - options.floatingGroupBounds !== this.options.floatingGroupBounds; - - const changed_rootOverlayOptions = - 'rootOverlayModel' in options && - options.rootOverlayModel !== this.options.rootOverlayModel; - - this._options = { ...this.options, ...options }; - - if (changed_floatingGroupBounds) { + if ('floatingGroupBounds' in options) { for (const group of this._floatingGroups) { - switch (this.options.floatingGroupBounds) { + switch (options.floatingGroupBounds) { case 'boundedWithinViewport': group.overlay.minimumInViewportHeight = undefined; group.overlay.minimumInViewportWidth = undefined; @@ -1047,30 +1010,26 @@ export class DockviewComponent break; default: group.overlay.minimumInViewportHeight = - this.options.floatingGroupBounds?.minimumHeightWithinViewport; + options.floatingGroupBounds?.minimumHeightWithinViewport; group.overlay.minimumInViewportWidth = - this.options.floatingGroupBounds?.minimumWidthWithinViewport; + options.floatingGroupBounds?.minimumWidthWithinViewport; } group.overlay.setBounds(); } } - if (changed_rootOverlayOptions) { - this._rootDropTarget.setOverlayModel(options.rootOverlayModel!); + if ('rootOverlayModel' in options) { + this._rootDropTarget.setOverlayModel( + options.rootOverlayModel ?? DEFAULT_ROOT_OVERLAY_MODEL + ); } - if ( - // if explicitly set as `undefined` - 'gap' in options && - options.gap === undefined - ) { - this.gridview.margin = 0; + if ('gap' in options) { + this.gridview.margin = options.gap ?? 0; } - if (typeof options.gap === 'number') { - this.gridview.margin = options.gap; - } + this._options = { ...this.options, ...options }; this.layout(this.gridview.width, this.gridview.height, true); } @@ -1108,7 +1067,7 @@ export class DockviewComponent if (!this.activeGroup) { return; } - options.group = this.activeGroup; + options.group = this.activeGroup; } if (options.includePanel && options.group) { diff --git a/packages/dockview-core/src/dom.ts b/packages/dockview-core/src/dom.ts index 249b84181..bcc82a496 100644 --- a/packages/dockview-core/src/dom.ts +++ b/packages/dockview-core/src/dom.ts @@ -280,3 +280,50 @@ export function disableIframePointEvents() { }, }; } + +export function getDockviewTheme(element: HTMLElement): string | undefined { + function toClassList(element: HTMLElement) { + const list: string[] = []; + + for (let i = 0; i < element.classList.length; i++) { + list.push(element.classList.item(i)!); + } + + return list; + } + + let theme: string | undefined = undefined; + let parent: HTMLElement | null = element; + + while (parent !== null) { + theme = toClassList(parent).find((cls) => + cls.startsWith('dockview-theme-') + ); + if (typeof theme === 'string') { + break; + } + parent = parent.parentElement; + } + + return theme; +} + +export class Classnames { + private _classNames: string[] = []; + + constructor(private readonly element: HTMLElement) {} + + setClassNames(classNames: string) { + for (const className of this._classNames) { + toggleClass(this.element, className, false); + } + + this._classNames = classNames + .split(' ') + .filter((v) => v.trim().length > 0); + + for (const className of this._classNames) { + toggleClass(this.element, className, true); + } + } +} diff --git a/packages/dockview-core/src/gridview/baseComponentGridview.ts b/packages/dockview-core/src/gridview/baseComponentGridview.ts index 6bed2c62c..332cef90c 100644 --- a/packages/dockview-core/src/gridview/baseComponentGridview.ts +++ b/packages/dockview-core/src/gridview/baseComponentGridview.ts @@ -7,7 +7,7 @@ import { ISplitviewStyles, Orientation, Sizing } from '../splitview/splitview'; import { IPanel } from '../panel/types'; import { MovementOptions2 } from '../dockview/options'; import { Resizable } from '../resizable'; -import { toggleClass } from '../dom'; +import { Classnames, toggleClass } from '../dom'; const nextLayoutId = sequentialNumberGenerator(); @@ -33,7 +33,6 @@ export interface BaseGridOptions { readonly proportionalLayout: boolean; readonly orientation: Orientation; readonly styles?: ISplitviewStyles; - readonly parentElement: HTMLElement; readonly disableAutoResizing?: boolean; readonly locked?: boolean; readonly margin?: number; @@ -100,7 +99,7 @@ export abstract class BaseGrid readonly onDidViewVisibilityChangeMicroTaskQueue = this._onDidViewVisibilityChangeMicroTaskQueue.onEvent; - private classNames: string[] = []; + private readonly _classNames: Classnames; get id(): string { return this._id; @@ -147,18 +146,15 @@ export abstract class BaseGrid this.gridview.locked = value; } - constructor(options: BaseGridOptions) { + constructor(parentElement: HTMLElement, options: BaseGridOptions) { super(document.createElement('div'), options.disableAutoResizing); this.element.style.height = '100%'; this.element.style.width = '100%'; - this.classNames = options.className?.split(' ') ?? []; + this._classNames = new Classnames(this.element); + this._classNames.setClassNames(options.className ?? ''); - for (const className of this.classNames) { - toggleClass(this.element, className, true); - } - - options.parentElement.appendChild(this.element); + parentElement.appendChild(this.element); this.gridview = new Gridview( !!options.proportionalLayout, @@ -214,14 +210,26 @@ export abstract class BaseGrid } updateOptions(options: Partial) { + if (typeof options.proportionalLayout === 'boolean') { + // this.gridview.proportionalLayout = options.proportionalLayout; // not supported + } + if (options.orientation) { + this.gridview.orientation = options.orientation; + } + if ('styles' in options) { + // this.gridview.styles = options.styles; // not supported + } + if ('disableResizing' in options) { + this.disableResizing = options.disableAutoResizing ?? false; + } + if ('locked' in options) { + this.locked = options.locked ?? false; + } + if ('margin' in options) { + this.gridview.margin = options.margin ?? 0; + } if ('className' in options) { - for (const className of this.classNames) { - toggleClass(this.element, className, false); - } - this.classNames = options.className?.split(' ') ?? []; - for (const className of this.classNames) { - toggleClass(this.element, className, true); - } + this._classNames.setClassNames(options.className ?? ''); } } diff --git a/packages/dockview-core/src/gridview/gridviewComponent.ts b/packages/dockview-core/src/gridview/gridviewComponent.ts index c342d3a50..a0ea22288 100644 --- a/packages/dockview-core/src/gridview/gridviewComponent.ts +++ b/packages/dockview-core/src/gridview/gridviewComponent.ts @@ -115,8 +115,7 @@ export class GridviewComponent } constructor(parentElement: HTMLElement, options: GridviewComponentOptions) { - super({ - parentElement: parentElement, + super(parentElement, { proportionalLayout: options.proportionalLayout, orientation: options.orientation, styles: options.styles, diff --git a/packages/dockview-core/src/paneview/paneviewComponent.ts b/packages/dockview-core/src/paneview/paneviewComponent.ts index 944378c31..2920cb802 100644 --- a/packages/dockview-core/src/paneview/paneviewComponent.ts +++ b/packages/dockview-core/src/paneview/paneviewComponent.ts @@ -24,7 +24,7 @@ import { sequentialNumberGenerator } from '../math'; import { PaneTransfer } from '../dnd/dataTransfer'; import { Resizable } from '../resizable'; import { Parameters } from '../panel/types'; -import { toggleClass } from '../dom'; +import { Classnames, toggleClass } from '../dom'; const nextLayoutId = sequentialNumberGenerator(); @@ -152,7 +152,7 @@ export class PaneviewComponent extends Resizable implements IPaneviewComponent { private readonly _onDidRemoveView = new Emitter(); readonly onDidRemoveView = this._onDidRemoveView.event; - private classNames: string[] = []; + private readonly _classNames: Classnames; get id(): string { return this._id; @@ -213,11 +213,8 @@ export class PaneviewComponent extends Resizable implements IPaneviewComponent { this._onDidRemoveView ); - this.classNames = options.className?.split(' ') ?? []; - - for (const className of this.classNames) { - toggleClass(this.element, className, true); - } + this._classNames = new Classnames(this.element); + this._classNames.setClassNames(options.className ?? ''); this._options = options; @@ -247,13 +244,11 @@ export class PaneviewComponent extends Resizable implements IPaneviewComponent { updateOptions(options: Partial): void { if ('className' in options) { - for (const className of this.classNames) { - toggleClass(this.element, className, false); - } - this.classNames = options.className?.split(' ') ?? []; - for (const className of this.classNames) { - toggleClass(this.element, className, true); - } + this._classNames.setClassNames(options.className ?? ''); + } + + if ('disableResizing' in options) { + this.disableResizing = options.disableAutoResizing ?? false; } this._options = { ...this.options, ...options }; diff --git a/packages/dockview-core/src/splitview/splitviewComponent.ts b/packages/dockview-core/src/splitview/splitviewComponent.ts index dc770d9a9..aa666fbfe 100644 --- a/packages/dockview-core/src/splitview/splitviewComponent.ts +++ b/packages/dockview-core/src/splitview/splitviewComponent.ts @@ -17,7 +17,7 @@ import { Emitter, Event } from '../events'; import { SplitviewPanel, ISplitviewPanel } from './splitviewPanel'; import { createComponent } from '../panel/componentFactory'; import { Resizable } from '../resizable'; -import { toggleClass } from '../dom'; +import { Classnames, toggleClass } from '../dom'; export interface SerializedSplitviewPanelData { id: string; @@ -100,7 +100,7 @@ export class SplitviewComponent private readonly _onDidLayoutChange = new Emitter(); readonly onDidLayoutChange: Event = this._onDidLayoutChange.event; - private classNames: string[] = []; + private readonly _classNames: Classnames; get panels(): SplitviewPanel[] { return this.splitview.getViews(); @@ -162,11 +162,8 @@ export class SplitviewComponent ) { super(parentElement, options.disableAutoResizing); - this.classNames = options.className?.split(' ') ?? []; - - for (const className of this.classNames) { - toggleClass(this.element, className, true); - } + this._classNames = new Classnames(this.element); + this._classNames.setClassNames(options.className ?? ''); this._options = options; @@ -189,25 +186,19 @@ export class SplitviewComponent updateOptions(options: Partial): void { if ('className' in options) { - for (const className of this.classNames) { - toggleClass(this.element, className, false); - } - this.classNames = options.className?.split(' ') ?? []; - for (const className of this.classNames) { - toggleClass(this.element, className, true); - } + this._classNames.setClassNames(options.className ?? ''); } - const hasOrientationChanged = - typeof options.orientation === 'string' && - this.options.orientation !== options.orientation; + if ('disableResizing' in options) { + this.disableResizing = options.disableAutoResizing ?? false; + } - this._options = { ...this.options, ...options }; - - if (hasOrientationChanged) { + if (typeof options.orientation === 'string') { this.splitview.orientation = options.orientation!; } + this._options = { ...this.options, ...options }; + this.splitview.layout( this.splitview.size, this.splitview.orthogonalSize