diff --git a/packages/dockview-core/src/__tests__/array.spec.ts b/packages/dockview-core/src/__tests__/array.spec.ts index 46e5dcb5c..9e3f0055a 100644 --- a/packages/dockview-core/src/__tests__/array.spec.ts +++ b/packages/dockview-core/src/__tests__/array.spec.ts @@ -3,7 +3,6 @@ import { last, pushToEnd, pushToStart, - range, sequenceEquals, tail, } from '../array'; @@ -37,12 +36,6 @@ describe('array', () => { expect(arr1).toEqual([3, 1, 2, 4]); }); - test('range', () => { - expect(range(0, 5)).toEqual([0, 1, 2, 3, 4]); - expect(range(5, 0)).toEqual([5, 4, 3, 2, 1]); - expect(range(5)).toEqual([0, 1, 2, 3, 4]); - }); - test('firstIndex', () => { expect(firstIndex([1, 2, 3, 4, 3], (item) => item === 3)).toBe(2); expect(firstIndex([1, 2, 3, 4, 3], (item) => item === 5)).toBe(-1); diff --git a/packages/dockview-core/src/__tests__/math.spec.ts b/packages/dockview-core/src/__tests__/math.spec.ts index 6b1e26d4b..47c47587d 100644 --- a/packages/dockview-core/src/__tests__/math.spec.ts +++ b/packages/dockview-core/src/__tests__/math.spec.ts @@ -1,4 +1,4 @@ -import { clamp } from '../math'; +import { clamp, range } from '../math'; describe('math', () => { describe('clamp', () => { @@ -14,4 +14,10 @@ describe('math', () => { ); }); }); + + test('range', () => { + expect(range(0, 5)).toEqual([0, 1, 2, 3, 4]); + expect(range(5, 0)).toEqual([5, 4, 3, 2, 1]); + expect(range(5)).toEqual([0, 1, 2, 3, 4]); + }); }); diff --git a/packages/dockview-core/src/array.ts b/packages/dockview-core/src/array.ts index b2e0a7e34..227744927 100644 --- a/packages/dockview-core/src/array.ts +++ b/packages/dockview-core/src/array.ts @@ -47,27 +47,6 @@ export function pushToEnd(arr: T[], value: T): void { } } -export const range = (from: number, to?: number): number[] => { - const result: number[] = []; - - if (typeof to !== 'number') { - to = from; - from = 0; - } - - if (from <= to) { - for (let i = from; i < to; i++) { - result.push(i); - } - } else { - for (let i = from; i > to; i--) { - result.push(i); - } - } - - return result; -}; - export function firstIndex( array: T[] | ReadonlyArray, fn: (item: T) => boolean diff --git a/packages/dockview-core/src/dnd/droptarget.ts b/packages/dockview-core/src/dnd/droptarget.ts index 8e2c62ad7..8a1901484 100644 --- a/packages/dockview-core/src/dnd/droptarget.ts +++ b/packages/dockview-core/src/dnd/droptarget.ts @@ -4,7 +4,6 @@ import { CompositeDisposable } from '../lifecycle'; import { DragAndDropObserver } from './dnd'; import { clamp } from '../math'; import { Direction } from '../gridview/baseComponentGridview'; -import { isBooleanValue } from '../types'; function numberOrFallback(maybeNumber: any, fallback: number): number { return typeof maybeNumber === 'number' ? maybeNumber : fallback; @@ -120,7 +119,7 @@ export class Droptarget extends CompositeDisposable { return; } - if (isBooleanValue(this.options.canDisplayOverlay)) { + if (typeof this.options.canDisplayOverlay === 'boolean') { if (!this.options.canDisplayOverlay) { return; } diff --git a/packages/dockview-core/src/dockview/components/panel/content.ts b/packages/dockview-core/src/dockview/components/panel/content.ts index 0b294abe2..75859e4d2 100644 --- a/packages/dockview-core/src/dockview/components/panel/content.ts +++ b/packages/dockview-core/src/dockview/components/panel/content.ts @@ -32,7 +32,7 @@ export class ContentContainer private readonly _onDidBlur = new Emitter(); readonly onDidBlur: Event = this._onDidBlur.event; - get element() { + get element(): HTMLElement { return this._element; } @@ -51,15 +51,15 @@ export class ContentContainer // 4) register mouseMove events (if no buttons are present we take this as a dragEnd event) } - show() { + show(): void { this.element.style.display = ''; } - hide() { + hide(): void { this.element.style.display = 'none'; } - public openPanel(panel: IDockviewPanel) { + public openPanel(panel: IDockviewPanel): void { if (this.panel === panel) { return; } @@ -105,14 +105,14 @@ export class ContentContainer // noop } - public closePanel() { + public closePanel(): void { if (this.panel?.view?.content?.element) { this._element.removeChild(this.panel.view.content.element); this.panel = undefined; } } - public dispose() { + public dispose(): void { this.disposable.dispose(); super.dispose(); } diff --git a/packages/dockview-core/src/dockview/components/titlebar/tabsContainer.ts b/packages/dockview-core/src/dockview/components/titlebar/tabsContainer.ts index bb73409ad..2472945a4 100644 --- a/packages/dockview-core/src/dockview/components/titlebar/tabsContainer.ts +++ b/packages/dockview-core/src/dockview/components/titlebar/tabsContainer.ts @@ -12,7 +12,7 @@ import { toggleClass } from '../../../dom'; import { IDockviewPanel } from '../../dockviewPanel'; export interface TabDropIndexEvent { - event: DragEvent; + readonly event: DragEvent; readonly index: number; } @@ -23,7 +23,6 @@ export interface ITabsContainer extends IDisposable { height: number | undefined; delete: (id: string) => void; indexOf: (id: string) => number; - at: (index: number) => ITab; onDrop: Event; setActive: (isGroupActive: boolean) => void; setActivePanel: (panel: IDockviewPanel) => void; @@ -55,11 +54,11 @@ export class TabsContainer private readonly _onDrop = new Emitter(); readonly onDrop: Event = this._onDrop.event; - get panels() { + get panels(): string[] { return this.tabs.map((_) => _.value.panelId); } - get size() { + get size(): number { return this.tabs.length; } @@ -114,21 +113,17 @@ export class TabsContainer } } - public get element() { + get element(): HTMLElement { return this._element; } - public isActive(tab: ITab) { + public isActive(tab: ITab): boolean { return ( this.selectedIndex > -1 && this.tabs[this.selectedIndex].value === tab ); } - public at(index: number) { - return this.tabs[index]?.value; - } - public indexOf(id: string): number { return this.tabs.findIndex((tab) => tab.value.panelId === id); } @@ -214,7 +209,7 @@ export class TabsContainer private addTab( tab: IValueDisposable, index: number = this.tabs.length - ) { + ): void { if (index < 0 || index > this.tabs.length) { throw new Error('invalid location'); } @@ -235,7 +230,7 @@ export class TabsContainer } } - public delete(id: string) { + public delete(id: string): void { const index = this.tabs.findIndex((tab) => tab.value.panelId === id); const tabToRemove = this.tabs.splice(index, 1)[0]; @@ -246,14 +241,17 @@ export class TabsContainer value.element.remove(); } - public setActivePanel(panel: IDockviewPanel) { + public setActivePanel(panel: IDockviewPanel): void { this.tabs.forEach((tab) => { const isActivePanel = panel.id === tab.value.panelId; tab.value.setActive(isActivePanel); }); } - public openPanel(panel: IDockviewPanel, index: number = this.tabs.length) { + public openPanel( + panel: IDockviewPanel, + index: number = this.tabs.length + ): void { if (this.tabs.find((tab) => tab.value.panelId === panel.id)) { return; } @@ -292,11 +290,11 @@ export class TabsContainer this.addTab(value, index); } - public closePanel(panel: IDockviewPanel) { + public closePanel(panel: IDockviewPanel): void { this.delete(panel.id); } - public dispose() { + public dispose(): void { super.dispose(); this.tabs.forEach((tab) => { diff --git a/packages/dockview-core/src/dockview/components/titlebar/voidContainer.ts b/packages/dockview-core/src/dockview/components/titlebar/voidContainer.ts index 992dcf2f4..1737c87aa 100644 --- a/packages/dockview-core/src/dockview/components/titlebar/voidContainer.ts +++ b/packages/dockview-core/src/dockview/components/titlebar/voidContainer.ts @@ -15,7 +15,7 @@ export class VoidContainer extends CompositeDisposable { private readonly _onDrop = new Emitter(); readonly onDrop: Event = this._onDrop.event; - get element() { + get element(): HTMLElement { return this._element; } diff --git a/packages/dockview-core/src/dockview/components/watermark/watermark.ts b/packages/dockview-core/src/dockview/components/watermark/watermark.ts index 48664bec7..adeed029a 100644 --- a/packages/dockview-core/src/dockview/components/watermark/watermark.ts +++ b/packages/dockview-core/src/dockview/components/watermark/watermark.ts @@ -18,7 +18,7 @@ export class Watermark private group: DockviewGroupPanel | undefined; private params: GroupPanelPartInitParameters | undefined; - get element() { + get element(): HTMLElement { return this._element; } @@ -61,19 +61,19 @@ export class Watermark ); } - update(_event: PanelUpdateEvent) { + update(_event: PanelUpdateEvent): void { // noop } - focus() { + focus(): void { // noop } - layout(_width: number, _height: number) { + layout(_width: number, _height: number): void { // noop } - init(_params: WatermarkRendererInitParameters) { + init(_params: WatermarkRendererInitParameters): void { this.render(); } @@ -82,11 +82,11 @@ export class Watermark this.render(); } - dispose() { + dispose(): void { super.dispose(); } - private render() { + private render(): void { const isOneGroup = !!( this.params && this.params.containerApi.size <= 1 ); diff --git a/packages/dockview-core/src/dockview/options.ts b/packages/dockview-core/src/dockview/options.ts index 1594249bf..6e83797d7 100644 --- a/packages/dockview-core/src/dockview/options.ts +++ b/packages/dockview-core/src/dockview/options.ts @@ -13,11 +13,11 @@ import { DockviewGroupPanelApi, } from './dockviewGroupPanel'; import { ISplitviewStyles, Orientation } from '../splitview/splitview'; -import { FrameworkFactory } from '../types'; import { PanelTransfer } from '../dnd/dataTransfer'; import { IDisposable } from '../lifecycle'; import { Position } from '../dnd/droptarget'; import { IDockviewPanel } from './dockviewPanel'; +import { FrameworkFactory } from '../panel/componentFactory'; export interface IGroupControlRenderer extends IDisposable { readonly element: HTMLElement; diff --git a/packages/dockview-core/src/gridview/basePanelView.ts b/packages/dockview-core/src/gridview/basePanelView.ts index d10a27be7..375b37a47 100644 --- a/packages/dockview-core/src/gridview/basePanelView.ts +++ b/packages/dockview-core/src/gridview/basePanelView.ts @@ -9,9 +9,9 @@ import { import { PanelApi, PanelApiImpl } from '../api/panelApi'; export interface BasePanelViewState { - id: string; - component: string; - params?: Record; + readonly id: string; + readonly component: string; + readonly params?: Record; } export interface BasePanelViewExported { @@ -35,9 +35,7 @@ export abstract class BasePanelView protected part?: IFrameworkPart; protected _params?: PanelInitParameters; - /** - * Provide an IFrameworkPart that will determine the rendered UI of this view piece. - */ + // provide an IFrameworkPart that will determine the rendered UI of this view piece. protected abstract getComponent(): IFrameworkPart; get element(): HTMLElement { diff --git a/packages/dockview-core/src/gridview/options.ts b/packages/dockview-core/src/gridview/options.ts index c224a73b4..8125f9127 100644 --- a/packages/dockview-core/src/gridview/options.ts +++ b/packages/dockview-core/src/gridview/options.ts @@ -1,6 +1,6 @@ import { GridviewPanel } from './gridviewPanel'; import { ISplitviewStyles, Orientation } from '../splitview/splitview'; -import { FrameworkFactory } from '../types'; +import { FrameworkFactory } from '../panel/componentFactory'; export interface GridviewComponentOptions { proportionalLayout: boolean; diff --git a/packages/dockview-core/src/lifecycle.ts b/packages/dockview-core/src/lifecycle.ts index 20f1eb425..d71947174 100644 --- a/packages/dockview-core/src/lifecycle.ts +++ b/packages/dockview-core/src/lifecycle.ts @@ -18,7 +18,7 @@ export namespace Disposable { export class CompositeDisposable { private readonly disposables: IDisposable[]; - public static from(...args: IDisposable[]) { + public static from(...args: IDisposable[]): CompositeDisposable { return new CompositeDisposable(...args); } diff --git a/packages/dockview-core/src/math.ts b/packages/dockview-core/src/math.ts index dea86579a..4ee363902 100644 --- a/packages/dockview-core/src/math.ts +++ b/packages/dockview-core/src/math.ts @@ -1,4 +1,4 @@ -export const clamp = (value: number, min: number, max: number) => { +export const clamp = (value: number, min: number, max: number): number => { if (min > max) { throw new Error(`${min} > ${max} is an invalid condition`); } @@ -9,3 +9,24 @@ export const sequentialNumberGenerator = () => { let value = 1; return { next: () => (value++).toString() }; }; + +export const range = (from: number, to?: number): number[] => { + const result: number[] = []; + + if (typeof to !== 'number') { + to = from; + from = 0; + } + + if (from <= to) { + for (let i = from; i < to; i++) { + result.push(i); + } + } else { + for (let i = from; i > to; i--) { + result.push(i); + } + } + + return result; +}; diff --git a/packages/dockview-core/src/panel/componentFactory.ts b/packages/dockview-core/src/panel/componentFactory.ts index 58ae86356..d88fb64ad 100644 --- a/packages/dockview-core/src/panel/componentFactory.ts +++ b/packages/dockview-core/src/panel/componentFactory.ts @@ -1,4 +1,6 @@ -import { FrameworkFactory } from '../types'; +export interface FrameworkFactory { + createComponent: (id: string, componentId: string, component: any) => T; +} export function createComponent( id: string, diff --git a/packages/dockview-core/src/paneview/options.ts b/packages/dockview-core/src/paneview/options.ts index 72d4e0be7..91fd9c6fe 100644 --- a/packages/dockview-core/src/paneview/options.ts +++ b/packages/dockview-core/src/paneview/options.ts @@ -1,4 +1,4 @@ -import { FrameworkFactory } from '../types'; +import { FrameworkFactory } from '../panel/componentFactory'; import { PaneviewDndOverlayEvent } from './paneviewComponent'; import { IPaneBodyPart, IPaneHeaderPart, PaneviewPanel } from './paneviewPanel'; diff --git a/packages/dockview-core/src/splitview/options.ts b/packages/dockview-core/src/splitview/options.ts index 05c492e4c..e79eb0b0a 100644 --- a/packages/dockview-core/src/splitview/options.ts +++ b/packages/dockview-core/src/splitview/options.ts @@ -1,8 +1,8 @@ import { IPanel, PanelInitParameters } from '../panel/types'; import { IView, SplitViewOptions, LayoutPriority } from './splitview'; -import { FrameworkFactory } from '../types'; import { SplitviewPanel } from './splitviewPanel'; import { SplitviewComponent } from './splitviewComponent'; +import { FrameworkFactory } from '../panel/componentFactory'; export interface PanelViewInitParameters extends PanelInitParameters { minimumSize?: number; diff --git a/packages/dockview-core/src/splitview/splitview.scss b/packages/dockview-core/src/splitview/splitview.scss index 7d329cf12..99b91b925 100644 --- a/packages/dockview-core/src/splitview/splitview.scss +++ b/packages/dockview-core/src/splitview/splitview.scss @@ -120,6 +120,7 @@ position: relative; height: 100%; width: 100%; + background-color: var(--dv-background-color); .view { height: 100%; diff --git a/packages/dockview-core/src/splitview/splitview.ts b/packages/dockview-core/src/splitview/splitview.ts index 7925bd578..88db24df3 100644 --- a/packages/dockview-core/src/splitview/splitview.ts +++ b/packages/dockview-core/src/splitview/splitview.ts @@ -11,7 +11,8 @@ import { } from '../dom'; import { clamp } from '../math'; import { Event, Emitter } from '../events'; -import { pushToStart, pushToEnd, range, firstIndex } from '../array'; +import { pushToStart, pushToEnd, firstIndex } from '../array'; +import { range } from '../math'; import { ViewItem } from './viewItem'; export enum Orientation { diff --git a/packages/dockview-core/src/svg.ts b/packages/dockview-core/src/svg.ts index 6b51350b6..bf0360191 100644 --- a/packages/dockview-core/src/svg.ts +++ b/packages/dockview-core/src/svg.ts @@ -3,7 +3,7 @@ const createSvgElementFromPath = (params: { width: string; viewbox: string; path: string; -}) => { +}): SVGSVGElement => { const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.setAttributeNS(null, 'height', params.height); svg.setAttributeNS(null, 'width', params.width); @@ -17,7 +17,7 @@ const createSvgElementFromPath = (params: { return svg; }; -export const createCloseButton = () => +export const createCloseButton = (): SVGSVGElement => createSvgElementFromPath({ width: '11', height: '11', @@ -25,7 +25,7 @@ export const createCloseButton = () => path: 'M2.1 27.3L0 25.2L11.55 13.65L0 2.1L2.1 0L13.65 11.55L25.2 0L27.3 2.1L15.75 13.65L27.3 25.2L25.2 27.3L13.65 15.75L2.1 27.3Z', }); -export const createExpandMoreButton = () => +export const createExpandMoreButton = (): SVGSVGElement => createSvgElementFromPath({ width: '11', height: '11', @@ -33,7 +33,7 @@ export const createExpandMoreButton = () => path: 'M12 14.15L0 2.15L2.15 0L12 9.9L21.85 0.0499992L24 2.2L12 14.15Z', }); -export const createChevronRightButton = () => +export const createChevronRightButton = (): SVGSVGElement => createSvgElementFromPath({ width: '11', height: '11', diff --git a/packages/dockview-core/src/theme.scss b/packages/dockview-core/src/theme.scss index c2581f7b6..daa0ffaef 100644 --- a/packages/dockview-core/src/theme.scss +++ b/packages/dockview-core/src/theme.scss @@ -1,4 +1,5 @@ @mixin dockview-theme-core-mixin { + --dv-background-color: black; --dv-paneview-active-outline-color: dodgerblue; --dv-tabs-and-actions-container-font-size: 13px; --dv-tabs-and-actions-container-height: 35px; diff --git a/packages/dockview-core/src/types.ts b/packages/dockview-core/src/types.ts index e5bd8f0d3..935d54b21 100644 --- a/packages/dockview-core/src/types.ts +++ b/packages/dockview-core/src/types.ts @@ -1,11 +1,3 @@ -export interface FrameworkFactory { - createComponent: (id: string, componentId: string, component: any) => T; -} - -export function isBooleanValue(value: any): value is boolean { - return typeof value === 'boolean'; -} - export type FunctionOrValue = (() => T) | T; export type Optional = Pick, K> & Omit; diff --git a/packages/dockview/src/splitview/view.ts b/packages/dockview/src/splitview/view.ts index 51a7aa53b..926e3ed9f 100644 --- a/packages/dockview/src/splitview/view.ts +++ b/packages/dockview/src/splitview/view.ts @@ -16,7 +16,7 @@ export class ReactPanelView extends SplitviewPanel { super(id, component); } - getComponent() { + getComponent(): ReactPart { return new ReactPart( this.element, this.reactPortalStore, diff --git a/packages/docs/docs/components/dockview.mdx b/packages/docs/docs/components/dockview.mdx index a61471905..727fd5abf 100644 --- a/packages/docs/docs/components/dockview.mdx +++ b/packages/docs/docs/components/dockview.mdx @@ -24,6 +24,7 @@ import CustomHeadersDockview from '@site/sandboxes/customheader-dockview/src/app import DockviewNative from '@site/sandboxes/fullwidthtab-dockview/src/app'; import DockviewNative2 from '@site/sandboxes/nativeapp-dockview/src/app'; import DockviewSetTitle from '@site/sandboxes/updatetitle-dockview/src/app'; +// import { attach as attachDockviewVanilla } from '@site/sandboxes/vanilla-dockview/src/app'; # Dockview @@ -656,6 +657,22 @@ const GroupControlComponent = (props: IDockviewGroupControlProps) => { +### Constraints + +You may wish to specify a minimum or maximum height or width for a group which can be done through the group api. + +```tsx +api.group.api.setConstraints(...) +``` + +> Constraints are currently only supported for groups and not individual panels. +> If you specific a constraint on a group and move a panel within that group to another group it will no +> longer be subject to those constraints since those constraints were on the group and not on the individual panel. + + + + + ## Events @@ -685,8 +702,14 @@ hello 2 -## Contraints + diff --git a/packages/docs/src/components/container.tsx b/packages/docs/src/components/container.tsx index 1dd825811..09be369e9 100644 --- a/packages/docs/src/components/container.tsx +++ b/packages/docs/src/components/container.tsx @@ -30,12 +30,24 @@ export const CreateCloseButton = () => }); export const Container = (props: { - children: React.ReactNode; + children?: React.ReactNode; height?: number; + injectVanillaJS?: (parent: HTMLElement) => void; }) => { + const ref = React.useRef(null); + + React.useEffect(() => { + if (!props.injectVanillaJS) { + return; + } + + props.injectVanillaJS(ref.current); + }, [props.injectVanillaJS]); + return ( <>