refactor: internal refactoring

This commit is contained in:
mathuo 2023-03-21 17:25:37 +01:00
parent 5b1aa4221f
commit 6aca089685
No known key found for this signature in database
GPG Key ID: C6EEDEFD6CA07281
24 changed files with 119 additions and 93 deletions

View File

@ -3,7 +3,6 @@ import {
last, last,
pushToEnd, pushToEnd,
pushToStart, pushToStart,
range,
sequenceEquals, sequenceEquals,
tail, tail,
} from '../array'; } from '../array';
@ -37,12 +36,6 @@ describe('array', () => {
expect(arr1).toEqual([3, 1, 2, 4]); 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', () => { test('firstIndex', () => {
expect(firstIndex([1, 2, 3, 4, 3], (item) => item === 3)).toBe(2); expect(firstIndex([1, 2, 3, 4, 3], (item) => item === 3)).toBe(2);
expect(firstIndex([1, 2, 3, 4, 3], (item) => item === 5)).toBe(-1); expect(firstIndex([1, 2, 3, 4, 3], (item) => item === 5)).toBe(-1);

View File

@ -1,4 +1,4 @@
import { clamp } from '../math'; import { clamp, range } from '../math';
describe('math', () => { describe('math', () => {
describe('clamp', () => { 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]);
});
}); });

View File

@ -47,27 +47,6 @@ export function pushToEnd<T>(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<T>( export function firstIndex<T>(
array: T[] | ReadonlyArray<T>, array: T[] | ReadonlyArray<T>,
fn: (item: T) => boolean fn: (item: T) => boolean

View File

@ -4,7 +4,6 @@ import { CompositeDisposable } from '../lifecycle';
import { DragAndDropObserver } from './dnd'; import { DragAndDropObserver } from './dnd';
import { clamp } from '../math'; import { clamp } from '../math';
import { Direction } from '../gridview/baseComponentGridview'; import { Direction } from '../gridview/baseComponentGridview';
import { isBooleanValue } from '../types';
function numberOrFallback(maybeNumber: any, fallback: number): number { function numberOrFallback(maybeNumber: any, fallback: number): number {
return typeof maybeNumber === 'number' ? maybeNumber : fallback; return typeof maybeNumber === 'number' ? maybeNumber : fallback;
@ -120,7 +119,7 @@ export class Droptarget extends CompositeDisposable {
return; return;
} }
if (isBooleanValue(this.options.canDisplayOverlay)) { if (typeof this.options.canDisplayOverlay === 'boolean') {
if (!this.options.canDisplayOverlay) { if (!this.options.canDisplayOverlay) {
return; return;
} }

View File

@ -32,7 +32,7 @@ export class ContentContainer
private readonly _onDidBlur = new Emitter<void>(); private readonly _onDidBlur = new Emitter<void>();
readonly onDidBlur: Event<void> = this._onDidBlur.event; readonly onDidBlur: Event<void> = this._onDidBlur.event;
get element() { get element(): HTMLElement {
return this._element; 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) // 4) register mouseMove events (if no buttons are present we take this as a dragEnd event)
} }
show() { show(): void {
this.element.style.display = ''; this.element.style.display = '';
} }
hide() { hide(): void {
this.element.style.display = 'none'; this.element.style.display = 'none';
} }
public openPanel(panel: IDockviewPanel) { public openPanel(panel: IDockviewPanel): void {
if (this.panel === panel) { if (this.panel === panel) {
return; return;
} }
@ -105,14 +105,14 @@ export class ContentContainer
// noop // noop
} }
public closePanel() { public closePanel(): void {
if (this.panel?.view?.content?.element) { if (this.panel?.view?.content?.element) {
this._element.removeChild(this.panel.view.content.element); this._element.removeChild(this.panel.view.content.element);
this.panel = undefined; this.panel = undefined;
} }
} }
public dispose() { public dispose(): void {
this.disposable.dispose(); this.disposable.dispose();
super.dispose(); super.dispose();
} }

View File

@ -12,7 +12,7 @@ import { toggleClass } from '../../../dom';
import { IDockviewPanel } from '../../dockviewPanel'; import { IDockviewPanel } from '../../dockviewPanel';
export interface TabDropIndexEvent { export interface TabDropIndexEvent {
event: DragEvent; readonly event: DragEvent;
readonly index: number; readonly index: number;
} }
@ -23,7 +23,6 @@ export interface ITabsContainer extends IDisposable {
height: number | undefined; height: number | undefined;
delete: (id: string) => void; delete: (id: string) => void;
indexOf: (id: string) => number; indexOf: (id: string) => number;
at: (index: number) => ITab;
onDrop: Event<TabDropIndexEvent>; onDrop: Event<TabDropIndexEvent>;
setActive: (isGroupActive: boolean) => void; setActive: (isGroupActive: boolean) => void;
setActivePanel: (panel: IDockviewPanel) => void; setActivePanel: (panel: IDockviewPanel) => void;
@ -55,11 +54,11 @@ export class TabsContainer
private readonly _onDrop = new Emitter<TabDropIndexEvent>(); private readonly _onDrop = new Emitter<TabDropIndexEvent>();
readonly onDrop: Event<TabDropIndexEvent> = this._onDrop.event; readonly onDrop: Event<TabDropIndexEvent> = this._onDrop.event;
get panels() { get panels(): string[] {
return this.tabs.map((_) => _.value.panelId); return this.tabs.map((_) => _.value.panelId);
} }
get size() { get size(): number {
return this.tabs.length; return this.tabs.length;
} }
@ -114,21 +113,17 @@ export class TabsContainer
} }
} }
public get element() { get element(): HTMLElement {
return this._element; return this._element;
} }
public isActive(tab: ITab) { public isActive(tab: ITab): boolean {
return ( return (
this.selectedIndex > -1 && this.selectedIndex > -1 &&
this.tabs[this.selectedIndex].value === tab this.tabs[this.selectedIndex].value === tab
); );
} }
public at(index: number) {
return this.tabs[index]?.value;
}
public indexOf(id: string): number { public indexOf(id: string): number {
return this.tabs.findIndex((tab) => tab.value.panelId === id); return this.tabs.findIndex((tab) => tab.value.panelId === id);
} }
@ -214,7 +209,7 @@ export class TabsContainer
private addTab( private addTab(
tab: IValueDisposable<ITab>, tab: IValueDisposable<ITab>,
index: number = this.tabs.length index: number = this.tabs.length
) { ): void {
if (index < 0 || index > this.tabs.length) { if (index < 0 || index > this.tabs.length) {
throw new Error('invalid location'); 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 index = this.tabs.findIndex((tab) => tab.value.panelId === id);
const tabToRemove = this.tabs.splice(index, 1)[0]; const tabToRemove = this.tabs.splice(index, 1)[0];
@ -246,14 +241,17 @@ export class TabsContainer
value.element.remove(); value.element.remove();
} }
public setActivePanel(panel: IDockviewPanel) { public setActivePanel(panel: IDockviewPanel): void {
this.tabs.forEach((tab) => { this.tabs.forEach((tab) => {
const isActivePanel = panel.id === tab.value.panelId; const isActivePanel = panel.id === tab.value.panelId;
tab.value.setActive(isActivePanel); 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)) { if (this.tabs.find((tab) => tab.value.panelId === panel.id)) {
return; return;
} }
@ -292,11 +290,11 @@ export class TabsContainer
this.addTab(value, index); this.addTab(value, index);
} }
public closePanel(panel: IDockviewPanel) { public closePanel(panel: IDockviewPanel): void {
this.delete(panel.id); this.delete(panel.id);
} }
public dispose() { public dispose(): void {
super.dispose(); super.dispose();
this.tabs.forEach((tab) => { this.tabs.forEach((tab) => {

View File

@ -15,7 +15,7 @@ export class VoidContainer extends CompositeDisposable {
private readonly _onDrop = new Emitter<DroptargetEvent>(); private readonly _onDrop = new Emitter<DroptargetEvent>();
readonly onDrop: Event<DroptargetEvent> = this._onDrop.event; readonly onDrop: Event<DroptargetEvent> = this._onDrop.event;
get element() { get element(): HTMLElement {
return this._element; return this._element;
} }

View File

@ -18,7 +18,7 @@ export class Watermark
private group: DockviewGroupPanel | undefined; private group: DockviewGroupPanel | undefined;
private params: GroupPanelPartInitParameters | undefined; private params: GroupPanelPartInitParameters | undefined;
get element() { get element(): HTMLElement {
return this._element; return this._element;
} }
@ -61,19 +61,19 @@ export class Watermark
); );
} }
update(_event: PanelUpdateEvent) { update(_event: PanelUpdateEvent): void {
// noop // noop
} }
focus() { focus(): void {
// noop // noop
} }
layout(_width: number, _height: number) { layout(_width: number, _height: number): void {
// noop // noop
} }
init(_params: WatermarkRendererInitParameters) { init(_params: WatermarkRendererInitParameters): void {
this.render(); this.render();
} }
@ -82,11 +82,11 @@ export class Watermark
this.render(); this.render();
} }
dispose() { dispose(): void {
super.dispose(); super.dispose();
} }
private render() { private render(): void {
const isOneGroup = !!( const isOneGroup = !!(
this.params && this.params.containerApi.size <= 1 this.params && this.params.containerApi.size <= 1
); );

View File

@ -13,11 +13,11 @@ import {
DockviewGroupPanelApi, DockviewGroupPanelApi,
} from './dockviewGroupPanel'; } from './dockviewGroupPanel';
import { ISplitviewStyles, Orientation } from '../splitview/splitview'; import { ISplitviewStyles, Orientation } from '../splitview/splitview';
import { FrameworkFactory } from '../types';
import { PanelTransfer } from '../dnd/dataTransfer'; import { PanelTransfer } from '../dnd/dataTransfer';
import { IDisposable } from '../lifecycle'; import { IDisposable } from '../lifecycle';
import { Position } from '../dnd/droptarget'; import { Position } from '../dnd/droptarget';
import { IDockviewPanel } from './dockviewPanel'; import { IDockviewPanel } from './dockviewPanel';
import { FrameworkFactory } from '../panel/componentFactory';
export interface IGroupControlRenderer extends IDisposable { export interface IGroupControlRenderer extends IDisposable {
readonly element: HTMLElement; readonly element: HTMLElement;

View File

@ -9,9 +9,9 @@ import {
import { PanelApi, PanelApiImpl } from '../api/panelApi'; import { PanelApi, PanelApiImpl } from '../api/panelApi';
export interface BasePanelViewState { export interface BasePanelViewState {
id: string; readonly id: string;
component: string; readonly component: string;
params?: Record<string, any>; readonly params?: Record<string, any>;
} }
export interface BasePanelViewExported<T extends PanelApi> { export interface BasePanelViewExported<T extends PanelApi> {
@ -35,9 +35,7 @@ export abstract class BasePanelView<T extends PanelApiImpl>
protected part?: IFrameworkPart; protected part?: IFrameworkPart;
protected _params?: PanelInitParameters; 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; protected abstract getComponent(): IFrameworkPart;
get element(): HTMLElement { get element(): HTMLElement {

View File

@ -1,6 +1,6 @@
import { GridviewPanel } from './gridviewPanel'; import { GridviewPanel } from './gridviewPanel';
import { ISplitviewStyles, Orientation } from '../splitview/splitview'; import { ISplitviewStyles, Orientation } from '../splitview/splitview';
import { FrameworkFactory } from '../types'; import { FrameworkFactory } from '../panel/componentFactory';
export interface GridviewComponentOptions { export interface GridviewComponentOptions {
proportionalLayout: boolean; proportionalLayout: boolean;

View File

@ -18,7 +18,7 @@ export namespace Disposable {
export class CompositeDisposable { export class CompositeDisposable {
private readonly disposables: IDisposable[]; private readonly disposables: IDisposable[];
public static from(...args: IDisposable[]) { public static from(...args: IDisposable[]): CompositeDisposable {
return new CompositeDisposable(...args); return new CompositeDisposable(...args);
} }

View File

@ -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) { if (min > max) {
throw new Error(`${min} > ${max} is an invalid condition`); throw new Error(`${min} > ${max} is an invalid condition`);
} }
@ -9,3 +9,24 @@ export const sequentialNumberGenerator = () => {
let value = 1; let value = 1;
return { next: () => (value++).toString() }; 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;
};

View File

@ -1,4 +1,6 @@
import { FrameworkFactory } from '../types'; export interface FrameworkFactory<T> {
createComponent: (id: string, componentId: string, component: any) => T;
}
export function createComponent<T>( export function createComponent<T>(
id: string, id: string,

View File

@ -1,4 +1,4 @@
import { FrameworkFactory } from '../types'; import { FrameworkFactory } from '../panel/componentFactory';
import { PaneviewDndOverlayEvent } from './paneviewComponent'; import { PaneviewDndOverlayEvent } from './paneviewComponent';
import { IPaneBodyPart, IPaneHeaderPart, PaneviewPanel } from './paneviewPanel'; import { IPaneBodyPart, IPaneHeaderPart, PaneviewPanel } from './paneviewPanel';

View File

@ -1,8 +1,8 @@
import { IPanel, PanelInitParameters } from '../panel/types'; import { IPanel, PanelInitParameters } from '../panel/types';
import { IView, SplitViewOptions, LayoutPriority } from './splitview'; import { IView, SplitViewOptions, LayoutPriority } from './splitview';
import { FrameworkFactory } from '../types';
import { SplitviewPanel } from './splitviewPanel'; import { SplitviewPanel } from './splitviewPanel';
import { SplitviewComponent } from './splitviewComponent'; import { SplitviewComponent } from './splitviewComponent';
import { FrameworkFactory } from '../panel/componentFactory';
export interface PanelViewInitParameters extends PanelInitParameters { export interface PanelViewInitParameters extends PanelInitParameters {
minimumSize?: number; minimumSize?: number;

View File

@ -120,6 +120,7 @@
position: relative; position: relative;
height: 100%; height: 100%;
width: 100%; width: 100%;
background-color: var(--dv-background-color);
.view { .view {
height: 100%; height: 100%;

View File

@ -11,7 +11,8 @@ import {
} from '../dom'; } from '../dom';
import { clamp } from '../math'; import { clamp } from '../math';
import { Event, Emitter } from '../events'; 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'; import { ViewItem } from './viewItem';
export enum Orientation { export enum Orientation {

View File

@ -3,7 +3,7 @@ const createSvgElementFromPath = (params: {
width: string; width: string;
viewbox: string; viewbox: string;
path: string; path: string;
}) => { }): SVGSVGElement => {
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttributeNS(null, 'height', params.height); svg.setAttributeNS(null, 'height', params.height);
svg.setAttributeNS(null, 'width', params.width); svg.setAttributeNS(null, 'width', params.width);
@ -17,7 +17,7 @@ const createSvgElementFromPath = (params: {
return svg; return svg;
}; };
export const createCloseButton = () => export const createCloseButton = (): SVGSVGElement =>
createSvgElementFromPath({ createSvgElementFromPath({
width: '11', width: '11',
height: '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', 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({ createSvgElementFromPath({
width: '11', width: '11',
height: '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', 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({ createSvgElementFromPath({
width: '11', width: '11',
height: '11', height: '11',

View File

@ -1,4 +1,5 @@
@mixin dockview-theme-core-mixin { @mixin dockview-theme-core-mixin {
--dv-background-color: black;
--dv-paneview-active-outline-color: dodgerblue; --dv-paneview-active-outline-color: dodgerblue;
--dv-tabs-and-actions-container-font-size: 13px; --dv-tabs-and-actions-container-font-size: 13px;
--dv-tabs-and-actions-container-height: 35px; --dv-tabs-and-actions-container-height: 35px;

View File

@ -1,11 +1,3 @@
export interface FrameworkFactory<T> {
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) | T; export type FunctionOrValue<T> = (() => T) | T;
export type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>; export type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;

View File

@ -16,7 +16,7 @@ export class ReactPanelView extends SplitviewPanel {
super(id, component); super(id, component);
} }
getComponent() { getComponent(): ReactPart<ISplitviewPanelProps> {
return new ReactPart( return new ReactPart(
this.element, this.element,
this.reactPortalStore, this.reactPortalStore,

View File

@ -24,6 +24,7 @@ import CustomHeadersDockview from '@site/sandboxes/customheader-dockview/src/app
import DockviewNative from '@site/sandboxes/fullwidthtab-dockview/src/app'; import DockviewNative from '@site/sandboxes/fullwidthtab-dockview/src/app';
import DockviewNative2 from '@site/sandboxes/nativeapp-dockview/src/app'; import DockviewNative2 from '@site/sandboxes/nativeapp-dockview/src/app';
import DockviewSetTitle from '@site/sandboxes/updatetitle-dockview/src/app'; import DockviewSetTitle from '@site/sandboxes/updatetitle-dockview/src/app';
// import { attach as attachDockviewVanilla } from '@site/sandboxes/vanilla-dockview/src/app';
# Dockview # Dockview
@ -656,6 +657,22 @@ const GroupControlComponent = (props: IDockviewGroupControlProps) => {
<DockviewGroupControl /> <DockviewGroupControl />
</Container> </Container>
### 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.
<Container height={500}>
<DockviewConstraints />
</Container>
## Events ## Events
<Container height={600}> <Container height={600}>
@ -685,8 +702,14 @@ hello 2
<App /> <App />
</div> </div>
## Contraints <!-- ## VanillaJS
<Container> > Note: This section is experimental and support for Vanilla JS is a work in progress.
<DockviewConstraints />
</Container> The `dockview` package contains `ReactJS` wrappers for the core library.
The core library is published as an independant package under the name `dockview-core` which you can install standalone.
> When using `dockview` there is no need to also install `dockview-core`.
> `dockview-core` is a dependency of `dockview` and automatically installed during the installation process of `dockview` via `npm install dockview`.
<Container injectVanillaJS={attachDockviewVanilla} /> -->

View File

@ -30,12 +30,24 @@ export const CreateCloseButton = () =>
}); });
export const Container = (props: { export const Container = (props: {
children: React.ReactNode; children?: React.ReactNode;
height?: number; height?: number;
injectVanillaJS?: (parent: HTMLElement) => void;
}) => { }) => {
const ref = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
if (!props.injectVanillaJS) {
return;
}
props.injectVanillaJS(ref.current);
}, [props.injectVanillaJS]);
return ( return (
<> <>
<div <div
ref={ref}
style={{ style={{
height: props.height ? `${props.height}px` : '300px', height: props.height ? `${props.height}px` : '300px',
}} }}