From 0c28f2dabe73fc14b3c7be9d75b981a222836b57 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Sun, 5 Jun 2022 20:17:59 +0100 Subject: [PATCH] feat: use svg icons and CSS color property for icons --- .../dockview/dockviewComponent.spec.ts | 294 +++++++++++++++++- .../src/actionbar/actionsContainer.scss | 23 +- .../dockview/components/tab/defaultTab.scss | 26 +- .../src/dockview/components/tab/defaultTab.ts | 8 +- .../components/watermark/watermark.ts | 4 +- .../src/dockview/dockviewComponent.scss | 24 -- .../src/dockview/dockviewComponent.ts | 3 +- packages/dockview/src/dockview/options.ts | 1 + .../src/paneview/defaultPaneviewHeader.ts | 35 ++- packages/dockview/src/paneview/paneview.scss | 6 +- packages/dockview/src/react/deserializer.ts | 35 ++- .../src/react/dockview/defaultTab.scss | 32 ++ .../src/react/dockview/defaultTab.tsx | 42 +++ .../dockview/src/react/dockview/dockview.tsx | 22 +- packages/dockview/src/react/index.ts | 1 + packages/dockview/src/react/svg.tsx | 29 ++ packages/dockview/src/svg.scss | 7 + packages/dockview/src/svg.ts | 42 +++ packages/dockview/src/theme.scss | 2 +- 19 files changed, 560 insertions(+), 76 deletions(-) create mode 100644 packages/dockview/src/react/dockview/defaultTab.scss create mode 100644 packages/dockview/src/react/dockview/defaultTab.tsx create mode 100644 packages/dockview/src/react/svg.tsx create mode 100644 packages/dockview/src/svg.scss create mode 100644 packages/dockview/src/svg.ts diff --git a/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts b/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts index 3839d56c7..89e50ec33 100644 --- a/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts +++ b/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts @@ -75,7 +75,7 @@ class PanelTabPartTest implements ITabRenderer { isDisposed: boolean = false; constructor(public readonly id: string, component: string) { - this.element.classList.add(`testpanel-${id}`); + this.element.className = `panel-tab-part-${id}`; } updateParentGroup(group: GroupPanel, isPanelVisible: boolean): void { @@ -1686,7 +1686,293 @@ describe('dockviewComponent', () => { return disposable.dispose(); }); - // group is disposed of when dockview is disposed - // watermark is disposed of when removed - // watermark is disposed of when dockview is disposed + test('load a layout with a non-existant tab id', () => { + const container = document.createElement('div'); + + const dockview = new DockviewComponent(container, { + components: { + default: PanelContentPartTest, + }, + }); + dockview.deserializer = new ReactPanelDeserialzier(dockview); + dockview.fromJSON({ + activeGroup: 'group-1', + grid: { + root: { + type: 'branch', + data: [ + { + type: 'leaf', + data: { + views: ['panel1'], + id: 'group-1', + activeView: 'panel1', + }, + size: 500, + }, + { + type: 'branch', + data: [ + { + type: 'leaf', + data: { + views: ['panel2', 'panel3'], + id: 'group-2', + }, + size: 500, + }, + { + type: 'leaf', + data: { views: ['panel4'], id: 'group-3' }, + size: 500, + }, + ], + size: 250, + }, + { + type: 'leaf', + data: { views: ['panel5'], id: 'group-4' }, + size: 250, + }, + ], + size: 1000, + }, + height: 1000, + width: 1000, + orientation: Orientation.VERTICAL, + }, + panels: { + panel1: { + id: 'panel1', + view: { content: { id: 'default' } }, + title: 'panel1', + }, + panel2: { + id: 'panel2', + view: { + content: { id: 'default' }, + tab: { id: '__non__existant_tab__' }, + }, + title: 'panel2', + }, + panel3: { + id: 'panel3', + view: { content: { id: 'default' } }, + title: 'panel3', + }, + panel4: { + id: 'panel4', + view: { content: { id: 'default' } }, + title: 'panel4', + }, + panel5: { + id: 'panel5', + view: { content: { id: 'default' } }, + title: 'panel5', + }, + }, + options: { tabHeight: 25 }, + }); + }); + + test('load and persist layout with custom tab header', () => { + const container = document.createElement('div'); + + const dockview = new DockviewComponent(container, { + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + }); + dockview.deserializer = new ReactPanelDeserialzier(dockview); + dockview.fromJSON({ + activeGroup: 'group-1', + grid: { + root: { + type: 'branch', + data: [ + { + type: 'leaf', + data: { + views: ['panel1'], + id: 'group-1', + activeView: 'panel1', + }, + size: 500, + }, + { + type: 'leaf', + data: { + views: ['panel2'], + id: 'group-2', + activeView: 'panel2', + }, + + size: 500, + }, + ], + size: 1000, + }, + height: 1000, + width: 1000, + orientation: Orientation.VERTICAL, + }, + panels: { + panel1: { + id: 'panel1', + view: { content: { id: 'default' } }, + title: 'panel1', + }, + panel2: { + id: 'panel2', + view: { + content: { id: 'default' }, + tab: { id: 'test_tab_id' }, + }, + title: 'panel2', + }, + }, + options: { tabHeight: 25 }, + }); + + expect(JSON.parse(JSON.stringify(dockview.toJSON()))).toEqual({ + activeGroup: 'group-1', + grid: { + root: { + type: 'branch', + data: [ + { + type: 'leaf', + data: { + views: ['panel1'], + id: 'group-1', + activeView: 'panel1', + }, + size: 500, + }, + { + type: 'leaf', + data: { + views: ['panel2'], + id: 'group-2', + activeView: 'panel2', + }, + size: 500, + }, + ], + size: 1000, + }, + height: 1000, + width: 1000, + orientation: Orientation.VERTICAL, + }, + panels: { + panel1: { + id: 'panel1', + view: { content: { id: 'default' } }, + title: 'panel1', + }, + panel2: { + id: 'panel2', + view: { + content: { id: 'default' }, + tab: { id: 'test_tab_id' }, + }, + title: 'panel2', + }, + }, + options: { tabHeight: 25 }, + }); + }); + + test('#2', () => { + const container = document.createElement('div'); + + const dockview = new DockviewComponent(container, { + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + }); + dockview.deserializer = new ReactPanelDeserialzier(dockview); + dockview.fromJSON({ + activeGroup: 'group-1', + grid: { + root: { + type: 'branch', + data: [ + { + type: 'leaf', + data: { + views: ['panel1'], + id: 'group-1', + activeView: 'panel1', + }, + size: 500, + }, + { + type: 'leaf', + data: { + views: ['panel2', 'panel3'], + id: 'group-2', + activeView: 'panel2', + }, + + size: 500, + }, + ], + size: 1000, + }, + height: 1000, + width: 1000, + orientation: Orientation.VERTICAL, + }, + panels: { + panel1: { + id: 'panel1', + view: { content: { id: 'default' } }, + title: 'panel1', + }, + panel2: { + id: 'panel2', + view: { + content: { id: 'default' }, + tab: { id: 'test_tab_id' }, + }, + title: 'panel2', + }, + panel3: { + id: 'panel3', + view: { content: { id: 'default' } }, + title: 'panel3', + }, + }, + options: { tabHeight: 25 }, + }); + + const group = dockview.getGroupPanel('panel2')!.api.group; + + const viewQuery = group.element.querySelectorAll( + '.groupview > .tabs-and-actions-container > .tabs-container > .tab' + ); + expect(viewQuery.length).toBe(2); + + const viewQuery2 = group.element.querySelectorAll( + '.groupview > .tabs-and-actions-container > .tabs-container > .tab > .default-tab' + ); + expect(viewQuery2.length).toBe(1); + + const viewQuery3 = group.element.querySelectorAll( + '.groupview > .tabs-and-actions-container > .tabs-container > .tab > .panel-tab-part-test_tab_id' + ); + expect(viewQuery3.length).toBe(1); + }); + + // load a layout with a default tab identifier when react default is present + + // load a layout with invialid panel identifier }); diff --git a/packages/dockview/src/actionbar/actionsContainer.scss b/packages/dockview/src/actionbar/actionsContainer.scss index 78b0a1ac5..b3dbf769e 100644 --- a/packages/dockview/src/actionbar/actionsContainer.scss +++ b/packages/dockview/src/actionbar/actionsContainer.scss @@ -11,20 +11,19 @@ margin: 0px; justify-content: flex-end; - a:active { - -webkit-mask-size: 100% 100% !important; - mask-size: 100% 100% !important; - } - .close-action { - background-color: white; - height: 16px; - width: 16px; - display: block; - -webkit-mask: var(--dv-tab-close-icon) 50% 50% / 90% 90% no-repeat; - mask: var(--dv-tab-close-icon) 50% 50% / 90% 90% no-repeat; - margin-right: '0.5em'; + padding: 4px; + display: flex; + align-items: center; + justify-content: center; + box-sizing: border-box; cursor: pointer; + color: var(--dv-activegroup-hiddenpanel-tab-color); + + &:hover { + border-radius: 2px; + background-color: var(--dv-icon-hover-background-color); + } } } } diff --git a/packages/dockview/src/dockview/components/tab/defaultTab.scss b/packages/dockview/src/dockview/components/tab/defaultTab.scss index d10afffbe..c84acdb69 100644 --- a/packages/dockview/src/dockview/components/tab/defaultTab.scss +++ b/packages/dockview/src/dockview/components/tab/defaultTab.scss @@ -38,17 +38,17 @@ display: flex; min-width: 80px; align-items: center; - padding-left: 10px; + padding: 0px 8px; white-space: nowrap; text-overflow: elipsis; .tab-content { + padding: 0px 8px; flex-grow: 1; } .action-container { text-align: right; - width: 28px; display: flex; .tab-list { @@ -57,19 +57,17 @@ margin: 0px; justify-content: flex-end; - a:active:hover { - -webkit-mask-size: 100% 100% !important; - mask-size: 100% 100% !important; - } - .tab-action { - height: 16px; - width: 16px; - display: block; - -webkit-mask: var(--dv-tab-close-icon) 50% 50% / 90% 90% - no-repeat; - mask: var(--dv-tab-close-icon) 50% 50% / 90% 90% no-repeat; - margin-right: '0.5em'; + padding: 4px; + display: flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + + &:hover { + border-radius: 2px; + background-color: var(--dv-icon-hover-background-color); + } &.disable-close { display: none; diff --git a/packages/dockview/src/dockview/components/tab/defaultTab.ts b/packages/dockview/src/dockview/components/tab/defaultTab.ts index 19d0b06be..69bfff5c6 100644 --- a/packages/dockview/src/dockview/components/tab/defaultTab.ts +++ b/packages/dockview/src/dockview/components/tab/defaultTab.ts @@ -6,6 +6,8 @@ import { import { addDisposableListener } from '../../../events'; import { PanelUpdateEvent } from '../../../panel/types'; import { GroupPanel } from '../../../groupview/groupviewPanel'; +import { createCloseButton } from '../../../svg'; +import { DEFAULT_TAB_IDENTIFIER } from '../../../react'; export class DefaultTab extends CompositeDisposable implements ITabRenderer { private _element: HTMLElement; @@ -24,7 +26,7 @@ export class DefaultTab extends CompositeDisposable implements ITabRenderer { } get id() { - return '__DEFAULT_TAB__'; + return DEFAULT_TAB_IDENTIFIER; } constructor() { @@ -42,8 +44,10 @@ export class DefaultTab extends CompositeDisposable implements ITabRenderer { this._list = document.createElement('ul'); this._list.className = 'tab-list'; // - this.action = document.createElement('a'); + this.action = document.createElement('div'); this.action.className = 'tab-action'; + this.action.appendChild(createCloseButton()); + // this._element.appendChild(this._content); this._element.appendChild(this._actionContainer); diff --git a/packages/dockview/src/dockview/components/watermark/watermark.ts b/packages/dockview/src/dockview/components/watermark/watermark.ts index 5bf338a09..3fa360a5c 100644 --- a/packages/dockview/src/dockview/components/watermark/watermark.ts +++ b/packages/dockview/src/dockview/components/watermark/watermark.ts @@ -8,6 +8,7 @@ import { toggleClass } from '../../../dom'; import { CompositeDisposable } from '../../../lifecycle'; import { GroupPanel } from '../../../groupview/groupviewPanel'; import { PanelUpdateEvent } from '../../../panel/types'; +import { createCloseButton } from '../../../svg'; export class Watermark extends CompositeDisposable @@ -42,8 +43,9 @@ export class Watermark title.appendChild(emptySpace); title.appendChild(actions.element); - const closeAnchor = document.createElement('a'); + const closeAnchor = document.createElement('div'); closeAnchor.className = 'close-action'; + closeAnchor.appendChild(createCloseButton()); actions.add(closeAnchor); diff --git a/packages/dockview/src/dockview/dockviewComponent.scss b/packages/dockview/src/dockview/dockviewComponent.scss index f6df4a85d..869a450ad 100644 --- a/packages/dockview/src/dockview/dockviewComponent.scss +++ b/packages/dockview/src/dockview/dockviewComponent.scss @@ -18,24 +18,12 @@ --dv-activegroup-visiblepanel-tab-background-color ); color: var(--dv-activegroup-visiblepanel-tab-color); - - .tab-action { - background-color: var( - --dv-activegroup-visiblepanel-tab-color - ); - } } &.inactive-tab { background-color: var( --dv-activegroup-hiddenpanel-tab-background-color ); color: var(--dv-activegroup-hiddenpanel-tab-color); - - .tab-action { - background-color: var( - --dv-activegroup-hiddenpanel-tab-color - ); - } } } } @@ -46,24 +34,12 @@ --dv-inactivegroup-visiblepanel-tab-background-color ); color: var(--dv-inactivegroup-visiblepanel-tab-color); - - .tab-action { - background-color: var( - --dv-inactivegroup-visiblepanel-tab-color - ); - } } &.inactive-tab { background-color: var( --dv-inactivegroup-hiddenpanel-tab-background-color ); color: var(--dv-inactivegroup-hiddenpanel-tab-color); - - .tab-action { - background-color: var( - --dv-inactivegroup-hiddenpanel-tab-color - ); - } } } } diff --git a/packages/dockview/src/dockview/dockviewComponent.ts b/packages/dockview/src/dockview/dockviewComponent.ts index c3b631ff9..53dfec761 100644 --- a/packages/dockview/src/dockview/dockviewComponent.ts +++ b/packages/dockview/src/dockview/dockviewComponent.ts @@ -72,6 +72,7 @@ export type DockviewComponentUpdateOptions = Pick< | 'frameworkTabComponents' | 'showDndOverlay' | 'watermarkFrameworkComponent' + | 'defaultTabComponent' >; export interface DockviewDropEvent extends GroupviewDropEvent { @@ -751,7 +752,7 @@ export class DockviewComponent private createPanel(options: AddPanelOptions, group: GroupPanel): IDockviewPanel { const view = new DefaultGroupPanelView({ content: this.createContentComponent(options.id, options.component), - tab: this.createTabComponent(options.id, options.tabComponent), + tab: this.createTabComponent(options.id, options.tabComponent || this.options.defaultTabComponent), }); const panel = new DockviewGroupPanel(options.id, this, this._api, group); diff --git a/packages/dockview/src/dockview/options.ts b/packages/dockview/src/dockview/options.ts index b53851ad9..a46f6bd23 100644 --- a/packages/dockview/src/dockview/options.ts +++ b/packages/dockview/src/dockview/options.ts @@ -62,6 +62,7 @@ export interface DockviewComponentOptions extends DockviewRenderFunctions { tabHeight?: number; orientation?: Orientation; styles?: ISplitviewStyles; + defaultTabComponent?: string; showDndOverlay?: (event: DockviewDndOverlayEvent) => boolean; } diff --git a/packages/dockview/src/paneview/defaultPaneviewHeader.ts b/packages/dockview/src/paneview/defaultPaneviewHeader.ts index 6f42b7db1..278483c9a 100644 --- a/packages/dockview/src/paneview/defaultPaneviewHeader.ts +++ b/packages/dockview/src/paneview/defaultPaneviewHeader.ts @@ -4,11 +4,14 @@ import { CompositeDisposable, MutableDisposable } from '../lifecycle'; import { PanelUpdateEvent } from '../panel/types'; import { IPaneHeaderPart, PanePanelInitParameter } from './paneviewPanel'; import { toggleClass } from '../dom'; +import { createChevronRightButton, createExpandMoreButton } from '../svg'; export class DefaultHeader extends CompositeDisposable implements IPaneHeaderPart { + private readonly _expandedIcon = createExpandMoreButton(); + private readonly _collapsedIcon = createChevronRightButton(); private readonly disposable = new MutableDisposable(); private readonly _element: HTMLElement; private readonly _content: HTMLElement; @@ -21,11 +24,13 @@ export class DefaultHeader constructor() { super(); + this._element = document.createElement('div'); this.element.className = 'default-header'; this._content = document.createElement('span'); - this._expander = document.createElement('a'); + this._expander = document.createElement('div'); + this._expander.className = 'dockview-pane-header-icon'; this.element.appendChild(this._expander); this.element.appendChild(this._content); @@ -41,15 +46,35 @@ export class DefaultHeader this.apiRef.api = params.api; this._content.textContent = params.title; - this._expander.textContent = '▼'; - toggleClass(this._expander, 'collapsed', !params.api.isExpanded); + this.updateIcon(); - this.disposable.value = params.api.onDidExpansionChange((e) => { - toggleClass(this._expander, 'collapsed', !e.isExpanded); + this.disposable.value = params.api.onDidExpansionChange(() => { + this.updateIcon(); }); } + private updateIcon() { + const isExpanded = !!this.apiRef.api?.isExpanded; + toggleClass(this._expander, 'collapsed', !isExpanded); + + if (isExpanded) { + if (this._expander.contains(this._collapsedIcon)) { + this._collapsedIcon.remove(); + } + if (!this._expander.contains(this._expandedIcon)) { + this._expander.appendChild(this._expandedIcon); + } + } else { + if (this._expander.contains(this._expandedIcon)) { + this._expandedIcon.remove(); + } + if (!this._expander.contains(this._collapsedIcon)) { + this._expander.appendChild(this._collapsedIcon); + } + } + } + update(_params: PanelUpdateEvent) { // } diff --git a/packages/dockview/src/paneview/paneview.scss b/packages/dockview/src/paneview/paneview.scss index 77ab3a40c..72a789916 100644 --- a/packages/dockview/src/paneview/paneview.scss +++ b/packages/dockview/src/paneview/paneview.scss @@ -31,8 +31,10 @@ padding: 0px 8px; cursor: pointer; - .collapsed { - transform: rotate(-90deg); + .dockview-pane-header-icon { + display: flex; + justify-content: center; + align-items: center; } > span { diff --git a/packages/dockview/src/react/deserializer.ts b/packages/dockview/src/react/deserializer.ts index f8117c456..68bbe2978 100644 --- a/packages/dockview/src/react/deserializer.ts +++ b/packages/dockview/src/react/deserializer.ts @@ -7,6 +7,7 @@ import { DockviewApi } from '../api/component.api'; import { DefaultTab } from '../dockview/components/tab/defaultTab'; import { DefaultGroupPanelView } from '../dockview/defaultGroupPanelView'; import { GroupPanel } from '../groupview/groupviewPanel'; +import { ITabRenderer } from '../groupview/types'; export class ReactPanelDeserialzier implements IPanelDeserializer { constructor(private readonly layout: DockviewComponent) {} @@ -21,6 +22,30 @@ export class ReactPanelDeserialzier implements IPanelDeserializer { const suppressClosable = panelData.suppressClosable; const viewData = panelData.view; + let tab: ITabRenderer; + + if (viewData.tab?.id) { + tab = createComponent( + viewData.tab.id, + viewData.tab.id, + this.layout.options.tabComponents, + this.layout.options.frameworkTabComponents, + this.layout.options.frameworkComponentFactory?.tab, + () => new DefaultTab() + ); + } else if (this.layout.options.defaultTabComponent) { + tab = createComponent( + this.layout.options.defaultTabComponent, + this.layout.options.defaultTabComponent, + this.layout.options.tabComponents, + this.layout.options.frameworkTabComponents, + this.layout.options.frameworkComponentFactory?.tab, + () => new DefaultTab() + ); + } else { + tab = new DefaultTab(); + } + const view = new DefaultGroupPanelView({ content: createComponent( viewData.content.id, @@ -29,15 +54,7 @@ export class ReactPanelDeserialzier implements IPanelDeserializer { this.layout.options.frameworkComponents, this.layout.options.frameworkComponentFactory?.content ), - tab: viewData.tab?.id - ? createComponent( - viewData.tab.id, - viewData.tab.id, - this.layout.options.tabComponents, - this.layout.options.frameworkTabComponents, - this.layout.options.frameworkComponentFactory?.tab - ) - : new DefaultTab(), + tab, }); const panel = new DockviewGroupPanel( diff --git a/packages/dockview/src/react/dockview/defaultTab.scss b/packages/dockview/src/react/dockview/defaultTab.scss new file mode 100644 index 000000000..b40356490 --- /dev/null +++ b/packages/dockview/src/react/dockview/defaultTab.scss @@ -0,0 +1,32 @@ +.tab { + .dockview-react-tab { + display: flex; + padding: 0px 8px; + align-items: center; + height: 100%; + + .dockview-react-tab-title { + padding: 0px 8px; + flex-grow: 1; + } + + .dockview-react-tab-action { + padding: 4px; + display: flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + + &:hover { + border-radius: 2px; + background-color: var(--dv-icon-hover-background-color); + } + } + } + + &.inactive-tab:not(:hover) { + .dockview-react-tab-action { + visibility: hidden; + } + } +} diff --git a/packages/dockview/src/react/dockview/defaultTab.tsx b/packages/dockview/src/react/dockview/defaultTab.tsx new file mode 100644 index 000000000..e42af684e --- /dev/null +++ b/packages/dockview/src/react/dockview/defaultTab.tsx @@ -0,0 +1,42 @@ +import { IDockviewPanelHeaderProps } from './dockview'; +import * as React from 'react'; +import { CloseButton } from '../svg'; + +export type IDockviewDefaultTabProps = IDockviewPanelHeaderProps & + React.DOMAttributes; + +export const DockviewDefaultTab: React.FunctionComponent = + ({ api, containerApi: _containerApi, params: _params, ...rest }) => { + const onClose = React.useCallback( + (event: React.MouseEvent) => { + event.stopPropagation(); + api.close(); + }, + [api] + ); + + const onClick = React.useCallback( + (event: React.MouseEvent) => { + api.setActive(); + + if (rest.onClick) { + rest.onClick(event); + } + }, + [api, rest.onClick] + ); + + const iconClassname = React.useMemo(() => { + const cn = ['dockview-react-tab-action']; + return cn.join(','); + }, [api.suppressClosable]); + + return ( +
+ {api.title} +
+ +
+
+ ); + }; diff --git a/packages/dockview/src/react/dockview/dockview.tsx b/packages/dockview/src/react/dockview/dockview.tsx index c5a74ad93..85ca922e5 100644 --- a/packages/dockview/src/react/dockview/dockview.tsx +++ b/packages/dockview/src/react/dockview/dockview.tsx @@ -19,6 +19,8 @@ import { PanelCollection, PanelParameters } from '../types'; import { watchElementResize } from '../../dom'; import { IContentRenderer, ITabRenderer } from '../../groupview/types'; +export const DEFAULT_TAB_IDENTIFIER = '__default__tab__'; + export interface IGroupPanelBaseProps> extends PanelParameters { api: DockviewPanelApi; @@ -47,6 +49,7 @@ export interface IDockviewReactProps { hideBorders?: boolean; className?: string; disableAutoResizing?: boolean; + defaultTabComponent?: React.FunctionComponent; } export const DockviewReact = React.forwardRef( @@ -124,9 +127,13 @@ export const DockviewReact = React.forwardRef( const dockview = new DockviewComponent(element, { frameworkComponentFactory: factory, frameworkComponents: props.components, - frameworkTabComponents: props.tabComponents, + frameworkTabComponents: { + ...(props.tabComponents || {}), + [DEFAULT_TAB_IDENTIFIER]: props.defaultTabComponent, + }, tabHeight: props.tabHeight, watermarkFrameworkComponent: props.watermarkComponent, + defaultTabComponent: DEFAULT_TAB_IDENTIFIER, styles: props.hideBorders ? { separatorBorder: 'transparent' } : undefined, @@ -222,6 +229,19 @@ export const DockviewReact = React.forwardRef( }; }, [props.onTabContextMenu]); + React.useEffect(() => { + if (!dockviewRef.current) { + return; + } + dockviewRef.current.updateOptions({ + defaultTabComponent: DEFAULT_TAB_IDENTIFIER, + frameworkTabComponents: { + ...(props.tabComponents || {}), + [DEFAULT_TAB_IDENTIFIER]: props.defaultTabComponent, + }, + }); + }, [props.defaultTabComponent]); + return (
( + + + +); + +export const ExpandMore = () => { + return ( + + + + ); +}; diff --git a/packages/dockview/src/svg.scss b/packages/dockview/src/svg.scss new file mode 100644 index 000000000..02519fae6 --- /dev/null +++ b/packages/dockview/src/svg.scss @@ -0,0 +1,7 @@ +.dockview-svg { + display: inline-block; + fill: currentcolor; + line-height: 1; + stroke: currentcolor; + stroke-width: 0; +} diff --git a/packages/dockview/src/svg.ts b/packages/dockview/src/svg.ts new file mode 100644 index 000000000..6b51350b6 --- /dev/null +++ b/packages/dockview/src/svg.ts @@ -0,0 +1,42 @@ +const createSvgElementFromPath = (params: { + height: string; + width: string; + viewbox: string; + path: string; +}) => { + const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + svg.setAttributeNS(null, 'height', params.height); + svg.setAttributeNS(null, 'width', params.width); + svg.setAttributeNS(null, 'viewBox', params.viewbox); + svg.setAttributeNS(null, 'aria-hidden', 'false'); + svg.setAttributeNS(null, 'focusable', 'false'); + svg.classList.add('dockview-svg'); + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttributeNS(null, 'd', params.path); + svg.appendChild(path); + return svg; +}; + +export const createCloseButton = () => + createSvgElementFromPath({ + width: '11', + height: '11', + viewbox: '0 0 28 28', + 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 = () => + createSvgElementFromPath({ + width: '11', + height: '11', + viewbox: '0 0 24 15', + path: 'M12 14.15L0 2.15L2.15 0L12 9.9L21.85 0.0499992L24 2.2L12 14.15Z', + }); + +export const createChevronRightButton = () => + createSvgElementFromPath({ + width: '11', + height: '11', + viewbox: '0 0 15 25', + path: 'M2.15 24.1L0 21.95L9.9 12.05L0 2.15L2.15 0L14.2 12.05L2.15 24.1Z', + }); diff --git a/packages/dockview/src/theme.scss b/packages/dockview/src/theme.scss index 38b7d208d..9ba9e8e92 100644 --- a/packages/dockview/src/theme.scss +++ b/packages/dockview/src/theme.scss @@ -2,10 +2,10 @@ --dv-paneview-active-outline-color: dodgerblue; --dv-tabs-and-actions-container-font-size: 13px; --dv-tabs-and-actions-container-height: 35px; - --dv-tab-close-icon: url('data:image/svg+xml;utf8,'); --dv-drag-over-background-color: rgba(83, 89, 93, 0.5); --dv-drag-over-border-color: white; --dv-tabs-container-scrollbar-color: #888; + --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31); } @mixin dockview-theme-dark-mixin {