Skip to content

Commit

Permalink
Merge pull request #112 from nmichaud/feature/tabbar-edit-title
Browse files Browse the repository at this point in the history
Double-click to edit tab title in TabBars
  • Loading branch information
Steven Silvester authored Aug 20, 2020
2 parents a09a1ba + 4f6e081 commit 649ef27
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 0 deletions.
7 changes: 7 additions & 0 deletions packages/default-theme/style/tabbar.css
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@
}


.lm-TabBar-tabLabel .lm-TabBar-tabInput {
padding: 0px;
border: 0px;
font: 12px Helvetica, Arial, sans-serif
}


/* <DEPRECATED> */ .p-TabBar-tab.p-mod-current, /* </DEPRECATED> */
.lm-TabBar-tab.lm-mod-current {
background: white;
Expand Down
93 changes: 93 additions & 0 deletions packages/widgets/src/tabbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class TabBar<T> extends Widget {
/* </DEPRECATED> */
this.setFlag(Widget.Flag.DisallowLayout);
this.tabsMovable = options.tabsMovable || false;
this.titlesEditable = options.titlesEditable || false;
this.allowDeselect = options.allowDeselect || false;
this.insertBehavior = options.insertBehavior || 'select-tab-if-needed';
this.removeBehavior = options.removeBehavior || 'select-tab-after';
Expand Down Expand Up @@ -164,6 +165,22 @@ class TabBar<T> extends Widget {
*/
tabsMovable: boolean;

/**
* Whether the titles can be user-edited.
*
*/
get titlesEditable(): boolean {
return this._titlesEditable;
}

/**
* Set whether titles can be user edited.
*
*/
set titlesEditable(value: boolean) {
this._titlesEditable = value;
}

/**
* Whether a tab can be deselected by the user.
*
Expand Down Expand Up @@ -509,6 +526,9 @@ class TabBar<T> extends Widget {
case 'mouseup':
this._evtMouseUp(event as MouseEvent);
break;
case 'dblclick':
this._evtDblClick(event as MouseEvent);
break;
case 'keydown':
this._evtKeyDown(event as KeyboardEvent);
break;
Expand All @@ -524,13 +544,15 @@ class TabBar<T> extends Widget {
*/
protected onBeforeAttach(msg: Message): void {
this.node.addEventListener('mousedown', this);
this.node.addEventListener('dblclick', this);
}

/**
* A message handler invoked on an `'after-detach'` message.
*/
protected onAfterDetach(msg: Message): void {
this.node.removeEventListener('mousedown', this);
this.node.removeEventListener('dblclick', this);
this._releaseMouse();
}

Expand All @@ -551,6 +573,69 @@ class TabBar<T> extends Widget {
VirtualDOM.render(content, this.contentNode);
}

/**
* Handle the `'dblclick'` event for the tab bar.
*/
private _evtDblClick(event: MouseEvent): void {

// Do nothing if titles are not editable
if (!this.titlesEditable) {
return;
}

let tabs = this.contentNode.children;

// Find the index of the released tab.
let index = ArrayExt.findFirstIndex(tabs, tab => {
return ElementExt.hitTest(tab, event.clientX, event.clientY);
});

// Do nothing if the press is not on a tab.
if (index === -1) {
return;
}

let title = this.titles[index];
let label = tabs[index].querySelector('.lm-TabBar-tabLabel') as HTMLElement;
if (label && label.contains(event.target as HTMLElement)) {

let value = title.label || '';

// Clear the label element
let oldValue = label.innerHTML;
label.innerHTML = "";

let input = document.createElement('input');
input.classList.add('lm-TabBar-tabInput');
input.value = value;
label.appendChild(input);

let onblur = () => {
input.removeEventListener('blur', onblur);
label.innerHTML = oldValue;
}

input.addEventListener('dblclick', (event: Event) => event.stopPropagation());
input.addEventListener('blur', onblur);
input.addEventListener('keydown', (event: KeyboardEvent) => {
if (event.key === 'Enter') {
if (input.value !== '') {
title.label = title.caption = input.value;
}
onblur();
} else if (event.key === 'Escape') {
onblur();
}
});
input.select();
input.focus();

if (label.children.length > 0) {
(label.children[0] as HTMLElement).focus();
}
}
}

/**
* Handle the `'keydown'` event for the tab bar.
*/
Expand Down Expand Up @@ -1028,6 +1113,7 @@ class TabBar<T> extends Widget {
private _currentIndex = -1;
private _titles: Title<T>[] = [];
private _orientation: TabBar.Orientation;
private _titlesEditable: boolean = false;
private _previousTitle: Title<T> | null = null;
private _dragData: Private.IDragData | null = null;
private _tabMoved = new Signal<this, TabBar.ITabMovedArgs<T>>(this);
Expand Down Expand Up @@ -1136,6 +1222,13 @@ namespace TabBar {
*/
allowDeselect?: boolean;

/**
* Whether the titles can be directly edited by the user.
*
* The default is `false`.
*/
titlesEditable?: boolean;

/**
* The selection behavior when inserting a tab.
*
Expand Down
7 changes: 7 additions & 0 deletions packages/widgets/style/tabbar.css
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@
}


.lm-TabBar-tabInput {
user-select: all;
width: 100%;
box-sizing : border-box;
}


/* <DEPRECATED> */ .p-TabBar-tab.p-mod-hidden, /* </DEPRECATED> */
.lm-TabBar-tab.lm-mod-hidden {
display: none !important;
Expand Down

0 comments on commit 649ef27

Please sign in to comment.