-
-
Save nmichaud/10495747fe5b099926b81cf1373673ac to your computer and use it in GitHub Desktop.
DockPanel subclass with maximize/minimize button and menu button.>....
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
Message | |
} from '@phosphor/messaging'; | |
import { | |
VirtualElement, h | |
} from '@phosphor/virtualdom'; | |
import { | |
ISignal, Signal | |
} from '@phosphor/signaling'; | |
import { | |
ArrayExt, each | |
} from '@phosphor/algorithm'; | |
import { | |
ElementExt | |
} from '@phosphor/domutils'; | |
import { | |
CommandRegistry | |
} from '@phosphor/commands'; | |
import { | |
DockPanel as DockPanel_, TabBar as TabBar_, Menu, Widget, Title, | |
} from '@phosphor/widgets'; | |
function createMenu(commands: CommandRegistry): Menu { | |
let root = new Menu({ commands }); | |
root.addItem({ command: 'example:lock' }); | |
root.addItem({ type: 'separator' }); | |
root.addItem({ command: 'example:new-tab' }); | |
return root; | |
} | |
class TabBarRenderer extends TabBar_.Renderer { | |
/** | |
* A selector which matches the close icon node in a tab. | |
*/ | |
readonly maximizeIconSelector = '.p-TabBar-tabMaximizeIcon'; | |
/** | |
* A selector which matches the close icon node in a tab. | |
*/ | |
readonly configureIconSelector = '.p-TabBar-tabConfigureIcon'; | |
/** | |
* Render tabs with the default DOM structure, but additionally register a context | |
* menu listener. | |
*/ | |
renderTab(data: TabBar_.IRenderData<any>): VirtualElement { | |
let title = data.title.caption; | |
let key = this.createTabKey(data); | |
let style = this.createTabStyle(data); | |
let className = this.createTabClass(data); | |
let dataset = this.createTabDataset(data); | |
return ( | |
h.li({ key, className, title, style, dataset }, | |
this.renderIcon(data), | |
this.renderLabel(data), | |
this.renderConfigureIcon(data), | |
this.renderMaximizeIcon(data), | |
this.renderCloseIcon(data) | |
) | |
); | |
} | |
/** | |
* Render the maximize icon element for a tab. | |
* | |
* @param data - The data to use for rendering the tab. | |
* | |
* @returns A virtual element representing the tab maximize icon. | |
*/ | |
renderMaximizeIcon(data: TabBar_.IRenderData<any>): VirtualElement { | |
let name = 'p-TabBar-tabMaximizeIcon'; | |
if (!this.maximized) { | |
name += ` p-mod-maximize`; | |
} else { | |
name += ` p-mod-minimize`; | |
} | |
return h.div({ className: name }); | |
} | |
/** | |
* Render the configure icon element for a tab. | |
* | |
* @param data - The data to use for rendering the tab. | |
* | |
* @returns A virtual element representing the tab configure icon. | |
*/ | |
renderConfigureIcon(data: TabBar_.IRenderData<any>): VirtualElement { | |
return h.div({ className: 'p-TabBar-tabConfigureIcon' }); | |
} | |
maximized: boolean = false; | |
} | |
/** | |
* The arguments object for the `tabMaximizeRequested` signal. | |
*/ | |
export | |
interface ITabMaximizeRequestedArgs<T> { | |
/** | |
* The index of the tab to maximize. | |
*/ | |
readonly index: number; | |
/** | |
* The title for the tab. | |
*/ | |
readonly title: Title<T>; | |
}; | |
/** | |
* The arguments object for the `tabConfigureRequested` signal. | |
*/ | |
export | |
interface ITabConfigureRequestedArgs<T> { | |
/** | |
* The index of the tab to maximize. | |
*/ | |
readonly index: number; | |
/** | |
* The title for the tab. | |
*/ | |
readonly title: Title<T>; | |
/** | |
* The current client X position of the mouse. | |
*/ | |
readonly clientX: number; | |
/** | |
* The current client Y position of the mouse. | |
*/ | |
readonly clientY: number; | |
}; | |
export | |
class TabBar<T> extends TabBar_<T> { | |
/** | |
* A signal emitted when a tab maximize icon is clicked. | |
* | |
*/ | |
get tabMaximizeRequested(): ISignal<this, ITabMaximizeRequestedArgs<T>> { | |
return this._tabMaximizeRequested; | |
} | |
private _tabMaximizeRequested = new Signal<this, ITabMaximizeRequestedArgs<T>>(this); | |
/** | |
* A signal emitted when a tab configure icon is clicked. | |
* | |
*/ | |
get tabConfigureRequested(): ISignal<this, ITabConfigureRequestedArgs<T>> { | |
return this._tabConfigureRequested; | |
} | |
private _tabConfigureRequested = new Signal<this, ITabConfigureRequestedArgs<T>>(this); | |
/** | |
* Handle the DOM events for the tab bar. | |
* | |
* @param event - The DOM event sent to the tab bar. | |
* | |
* #### Notes | |
* This method implements the DOM `EventListener` interface and is | |
* called in response to events on the tab bar's DOM node. | |
* | |
* This should not be called directly by user code. | |
*/ | |
handleEvent(event: Event): void { | |
switch (event.type) { | |
case 'mouseup': | |
// Lookup the tab nodes. | |
let tabs = this.contentNode.children; | |
let ev = event as MouseEvent; | |
// Note this is broken, since it only checks on mouse up so you could | |
// click outside the button and release inside and it would fire | |
// Find the index of the released tab. | |
let index = ArrayExt.findFirstIndex(tabs, tab => { | |
return ElementExt.hitTest(tab, ev.clientX, ev.clientY); | |
}); | |
let title = this.titles[index]; | |
// Emit the close requested signal if the maximize icon was released. | |
let icon = tabs[index].querySelector((this.renderer as TabBarRenderer).maximizeIconSelector); | |
if (icon && icon.contains(ev.target as HTMLElement)) { | |
this._tabMaximizeRequested.emit({ index, title }); | |
} | |
icon = tabs[index].querySelector((this.renderer as TabBarRenderer).configureIconSelector); | |
if (icon && icon.contains(ev.target as HTMLElement)) { | |
let er = (ev.target as HTMLElement).getBoundingClientRect(); | |
let clientX = er.left; | |
let clientY = er.bottom; | |
this._tabConfigureRequested.emit({ index, title, clientX, clientY }); | |
} | |
} | |
super.handleEvent(event); | |
} | |
} | |
class DockPanelRenderer extends DockPanel_.Renderer { | |
createTabBar(): TabBar<Widget> { | |
let bar = new TabBar<Widget>({renderer: new TabBarRenderer()}); | |
bar.addClass('p-DockPanel-tabBar'); | |
return bar; | |
} | |
} | |
export | |
class DockPanel extends DockPanel_ { | |
constructor(commands: CommandRegistry, options: DockPanel_.IOptions = {}) { | |
super({...options, | |
renderer: new DockPanelRenderer() | |
}); | |
this._commands = commands; | |
} | |
/** | |
* Get the locked state for the dock panel. | |
*/ | |
get locked(): boolean { | |
return this._locked; | |
} | |
/** | |
* Set the locked state for the dock panel. | |
* | |
*/ | |
set locked(value: boolean) { | |
// Bail early if the locked state does not change. | |
if (this._locked === value) { | |
return; | |
} | |
this._locked = value; | |
each(this.tabBars(), tabBar => | |
{ | |
tabBar.tabsMovable = !value; | |
each(tabBar.titles, title => { title.closable = !value;}); | |
}); | |
} | |
protected _createTabBar(): TabBar<Widget> { | |
let tabBar = super._createTabBar() as TabBar<Widget>; | |
tabBar.tabMaximizeRequested.connect(this._onTabMaximizeRequested, this); | |
tabBar.tabConfigureRequested.connect(this._onTabConfigureRequested, this); | |
tabBar.tabsMovable = !this._locked; | |
let renderer = tabBar.renderer as TabBarRenderer; | |
renderer.maximized = this._maximized; | |
return tabBar; | |
} | |
/** | |
* Handle the `tabMaximizeRequested` signal from a tab bar. | |
*/ | |
private _onTabConfigureRequested(sender: TabBar<Widget>, args: ITabConfigureRequestedArgs<Widget>): void { | |
let menu = createMenu(this._commands); | |
menu.open(args.clientX, args.clientY); | |
} | |
/** | |
* Handle the `tabMaximizeRequested` signal from a tab bar. | |
*/ | |
private _onTabMaximizeRequested(sender: TabBar<Widget>, args: ITabMaximizeRequestedArgs<Widget>): void { | |
this._maximized = !this._maximized; | |
if (this._maximized) { | |
this._minimizedLayout = this.saveLayout(); | |
this.restoreLayout({ | |
main: { | |
type: 'tab-area', | |
widgets: [args.title.owner], | |
currentIndex: 0 | |
} | |
}); | |
} else { | |
this.restoreLayout(this._minimizedLayout as DockPanel_.ILayoutConfig); | |
this._minimizedLayout = null; | |
} | |
} | |
private _locked: boolean = false; | |
private _maximized: boolean = false; | |
private _minimizedLayout: DockPanel_.ILayoutConfig | null = null; | |
private _commands: CommandRegistry; | |
} | |
export | |
class ContentWidget extends Widget { | |
static createNode(): HTMLElement { | |
let node = document.createElement('div'); | |
let content = document.createElement('div'); | |
let input = document.createElement('input'); | |
input.placeholder = 'Placeholder...'; | |
content.appendChild(input); | |
node.appendChild(content); | |
return node; | |
} | |
constructor(name: string) { | |
super({ node: ContentWidget.createNode() }); | |
this.setFlag(Widget.Flag.DisallowLayout); | |
this.addClass('content'); | |
this.title.label = name; | |
this.title.closable = true; | |
this.title.caption = `Long description for: ${name}`; | |
} | |
get inputNode(): HTMLInputElement { | |
return this.node.getElementsByTagName('input')[0] as HTMLInputElement; | |
} | |
protected onActivateRequest(msg: Message): void { | |
if (this.isAttached) { | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment