Merge pull request #93 from brrd/develop

Version 0.12.0
This commit is contained in:
Thomas Brouard 2020-02-04 13:39:04 +01:00 committed by GitHub
commit b20f757c5d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 503 additions and 480 deletions

11
.editorconfig Normal file
View file

@ -0,0 +1,11 @@
# https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

View file

@ -18,12 +18,23 @@ $ npm run demo
## Usage ## Usage
Add the following elements to the app page: Electron-tabs uses webviews, so you first need to use the following `webPreferences` options in the main process:
```js
const mainWindow = new electron.BrowserWindow({
webPreferences: {
nodeIntegration: true,
webviewTag: true
}
});
```
Then add the following elements to the app page:
```html ```html
<div class="etabs-tabgroup"> <div class="etabs-tabgroup">
<div class="etabs-tabs"></div> <div class="etabs-tabs"></div>
<div class="etabs-buttons"></div> <div class="etabs-buttons"></div>
</div> </div>
<div class="etabs-views"></div> <div class="etabs-views"></div>
``` ```
@ -34,14 +45,14 @@ And call the module in the renderer process:
const TabGroup = require("electron-tabs"); const TabGroup = require("electron-tabs");
``` ```
Then you can initialize a tab group and add tabs to it: Now you can initialize a tab group and add tabs to it:
```javascript ```javascript
let tabGroup = new TabGroup(); let tabGroup = new TabGroup();
let tab = tabGroup.addTab({ let tab = tabGroup.addTab({
title: "Electron", title: "Electron",
src: "http://electron.atom.io", src: "http://electron.atom.io",
visible: true visible: true
}); });
``` ```
@ -52,6 +63,7 @@ If you don't want to write your own styles, you can also insert the sample elect
``` ```
### Note ### Note
Please note, there is a known issue in some versions of Electron that prevents the process to completely shut down and it remains hanging in Background Processes (Windows 10). If you encounter that issue please use the workaround provided at https://github.com/electron/electron/issues/13939 Please note, there is a known issue in some versions of Electron that prevents the process to completely shut down and it remains hanging in Background Processes (Windows 10). If you encounter that issue please use the workaround provided at https://github.com/electron/electron/issues/13939
## API ## API
@ -238,11 +250,11 @@ const TabGroup = require("electron-tabs");
const dragula = require("dragula"); const dragula = require("dragula");
var tabGroup = new TabGroup({ var tabGroup = new TabGroup({
ready: function (tabGroup) { ready: function (tabGroup) {
dragula([tabGroup.tabContainer], { dragula([tabGroup.tabContainer], {
direction: "horizontal" direction: "horizontal"
}); });
} }
}); });
``` ```

View file

@ -3,19 +3,16 @@ const app = electron.app;
app.setName('electron-tabs-demo'); app.setName('electron-tabs-demo');
app.on('ready', function () { app.on('ready', function () {
const mainWindow = new electron.BrowserWindow({
const mainWindow = new electron.BrowserWindow({ webPreferences: {
webPreferences: { nodeIntegration: true,
nodeIntegration: true, webviewTag: true
webviewTag: true }
} });
}); mainWindow.loadURL('file://' + __dirname + '/electron-tabs.html');
mainWindow.loadURL('file://' + __dirname + '/electron-tabs.html'); mainWindow.on('ready-to-show', function () {
mainWindow.on('ready-to-show', function () { mainWindow.show();
mainWindow.show(); mainWindow.focus();
mainWindow.focus(); });
});
}); });

View file

@ -2,38 +2,38 @@
<html> <html>
<head> <head>
<link rel="stylesheet" href="../electron-tabs.css"> <link rel="stylesheet" href="../electron-tabs.css">
</head> </head>
<body style="margin:0"> <body style="margin:0">
<div class="etabs-tabgroup"> <div class="etabs-tabgroup">
<div class="etabs-tabs"></div> <div class="etabs-tabs"></div>
<div class="etabs-buttons"></div> <div class="etabs-buttons"></div>
</div> </div>
<div class="etabs-views"></div> <div class="etabs-views"></div>
<script> <script>
//const TabGroup = require('electron-tabs') normally but for demo : //const TabGroup = require('electron-tabs') normally but for demo :
const TabGroup = require("../index"); const TabGroup = require("../index");
let tabGroup = new TabGroup({ let tabGroup = new TabGroup({
newTab: { newTab: {
title: 'New Tab' title: 'New Tab'
} }
}); });
tabGroup.addTab({ tabGroup.addTab({
title: 'Google', title: 'Google',
src: 'http://google.com', src: 'http://google.com',
}); });
tabGroup.addTab({ tabGroup.addTab({
title: "Electron", title: "Electron",
src: "http://electron.atom.io", src: "http://electron.atom.io",
visible: true, visible: true,
active: true active: true
}); });
</script> </script>
</body> </body>

View file

@ -1,133 +1,134 @@
.etabs-tabgroup { .etabs-tabgroup {
width: 100%; width: 100%;
height: 32px; height: 32px;
background-color: #ccc; background-color: #ccc;
cursor: default; cursor: default;
font: caption; font: caption;
font-size: 14px; font-size: 14px;
-webkit-user-select: none; -webkit-user-select: none;
user-select: none; user-select: none;
} }
.etabs-tabs { .etabs-tabs {
} }
.etabs-tab { .etabs-tab {
display: none; display: none;
position: relative; position: relative;
color: #333; color: #333;
height: 22px; height: 22px;
padding: 6px 8px 4px; padding: 6px 8px 4px;
border: 1px solid #aaa; border: 1px solid #aaa;
border-bottom: none; border-bottom: none;
border-left: none; border-left: none;
background: linear-gradient(to bottom, rgba(234,234,234,1) 0%,rgba(204,204,204,1) 100%); background: linear-gradient(to bottom, rgba(234,234,234,1) 0%,rgba(204,204,204,1) 100%);
font: caption; font: caption;
font-size: 14px; font-size: 14px;
background-color: #ccc; background-color: #ccc;
cursor: default; cursor: default;
} }
/* Dragula */ /* Dragula */
.etabs-tab.gu-mirror { .etabs-tab.gu-mirror {
padding-bottom: 0; padding-bottom: 0;
} }
.etabs-tab:first-child { .etabs-tab:first-child {
border-left: none; border-left: none;
} }
.etabs-tab.visible { .etabs-tab.visible {
display: inline-block; display: inline-block;
float: left; float: left;
} }
.etabs-tab.active { .etabs-tab.active {
background: #fff; background: #fff;
} }
.etabs-tab.flash { .etabs-tab.flash {
background: linear-gradient(to bottom, rgba(255,243,170,1) 0%,rgba(255,227,37,1) 100%); background: linear-gradient(to bottom, rgba(255,243,170,1) 0%,rgba(255,227,37,1) 100%);
} }
.etabs-buttons { .etabs-buttons {
float: left; float: left;
} }
.etabs-buttons button { .etabs-buttons button {
float: left; float: left;
color: #333; color: #333;
background: none; background: none;
border: none; border: none;
font-size: 12px; font-size: 12px;
margin-top: 6px; margin-top: 6px;
border-radius: 2px; border-radius: 2px;
margin-left: 4px; margin-left: 4px;
width: 20px; width: 20px;
text-align: center; text-align: center;
padding: 4px 0; padding: 4px 0;
} }
.etabs-buttons button:hover { .etabs-buttons button:hover {
color: #eee; color: #eee;
background-color: #aaa; background-color: #aaa;
} }
.etabs-tab-badge { .etabs-tab-badge {
position: absolute; position: absolute;
right: 0; right: 0;
top: -7px; top: -7px;
background: red; background: red;
border-radius: 100%; border-radius: 100%;
text-align: center; text-align: center;
font-size: 10px; font-size: 10px;
padding: 0 5px; padding: 0 5px;
} }
.etabs-tab-badge.hidden { .etabs-tab-badge.hidden {
display: none; display: none;
} }
.etabs-tab-icon { .etabs-tab-icon {
display: inline-block; display: inline-block;
height: 16px; height: 16px;
} }
.etabs-tab-icon img { .etabs-tab-icon img {
max-width: 16px; max-width: 16px;
max-height: 16px; max-height: 16px;
} }
.etabs-tab-title { .etabs-tab-title {
display: inline-block; display: inline-block;
margin-left: 10px; margin-left: 10px;
} }
.etabs-tab-buttons { .etabs-tab-buttons {
display: inline-block; display: inline-block;
margin-left: 10px; margin-left: 10px;
} }
.etabs-tab-buttons button { .etabs-tab-buttons button {
display: inline-block; display: inline-block;
color: #333; color: #333;
background: none; background: none;
border: none; border: none;
width: 20px; width: 20px;
text-align: center; text-align: center;
border-radius: 2px; border-radius: 2px;
} }
.etabs-tab-buttons button:hover { .etabs-tab-buttons button:hover {
color: #eee; color: #eee;
background-color: #aaa; background-color: #aaa;
} }
.etabs-views { .etabs-views {
border-top: 1px solid #aaa; position: relative;
height: calc(100vh - 33px); border-top: 1px solid #aaa;
height: calc(100vh - 33px);
} }
.etab-view { .etab-view {
position: relative; position: relative;
} }

726
index.js
View file

@ -1,422 +1,424 @@
const EventEmitter = require("events"); const EventEmitter = require("events");
if (!document) { if (!document) {
throw Error("electron-tabs module must be called in renderer process"); throw Error("electron-tabs module must be called in renderer process");
} }
// Inject styles // Inject styles
(function () { (function () {
const styles = ` const styles = `
webview { webview {
width: 100%; position: absolute;
height: 100%; visibility: hidden;
top: 0; }
right: 0; webview.visible {
bottom: 0; width: 100%;
left: 0; height: 100%;
position: absolute; visibility: visible;
visibility: hidden; }
} `;
webview.visible { let styleTag = document.createElement("style");
visibility: visible; styleTag.innerHTML = styles;
} document.getElementsByTagName("head")[0].appendChild(styleTag);
`;
let styleTag = document.createElement("style");
styleTag.innerHTML = styles;
document.getElementsByTagName("head")[0].appendChild(styleTag);
})(); })();
class TabGroup extends EventEmitter { class TabGroup extends EventEmitter {
constructor (args = {}) { constructor (args = {}) {
super(); super();
let options = this.options = { let options = this.options = {
tabContainerSelector: args.tabContainerSelector || ".etabs-tabs", tabContainerSelector: args.tabContainerSelector || ".etabs-tabs",
buttonsContainerSelector: args.buttonsContainerSelector || ".etabs-buttons", buttonsContainerSelector: args.buttonsContainerSelector || ".etabs-buttons",
viewContainerSelector: args.viewContainerSelector || ".etabs-views", viewContainerSelector: args.viewContainerSelector || ".etabs-views",
tabClass: args.tabClass || "etabs-tab", tabClass: args.tabClass || "etabs-tab",
viewClass: args.viewClass || "etabs-view", viewClass: args.viewClass || "etabs-view",
closeButtonText: args.closeButtonText || "&#10006;", closeButtonText: args.closeButtonText || "&#215;",
newTab: args.newTab, newTab: args.newTab,
newTabButtonText: args.newTabButtonText || "&#65291;", newTabButtonText: args.newTabButtonText || "&#65291;",
ready: args.ready ready: args.ready
}; };
this.tabContainer = document.querySelector(options.tabContainerSelector); this.tabContainer = document.querySelector(options.tabContainerSelector);
this.viewContainer = document.querySelector(options.viewContainerSelector); this.viewContainer = document.querySelector(options.viewContainerSelector);
this.tabs = []; this.tabs = [];
this.newTabId = 0; this.newTabId = 0;
TabGroupPrivate.initNewTabButton.bind(this)(); TabGroupPrivate.initNewTabButton.bind(this)();
if (typeof this.options.ready === "function") { if (typeof this.options.ready === "function") {
this.options.ready(this); this.options.ready(this);
}
} }
}
addTab (args = this.options.newTab) { addTab (args = this.options.newTab) {
if (typeof args === "function") { if (typeof args === "function") {
args = args(this); args = args(this);
}
let id = this.newTabId;
this.newTabId++;
let tab = new Tab(this, id, args);
this.tabs.push(tab);
// Don't call tab.activate() before a tab is referenced in this.tabs
if (args.active === true) {
tab.activate();
}
this.emit("tab-added", tab, this);
return tab;
} }
let id = this.newTabId;
this.newTabId++;
let tab = new Tab(this, id, args);
this.tabs.push(tab);
// Don't call tab.activate() before a tab is referenced in this.tabs
if (args.active === true) {
tab.activate();
}
this.emit("tab-added", tab, this);
return tab;
}
getTab (id) { getTab (id) {
for (let i in this.tabs) { for (let i in this.tabs) {
if (this.tabs[i].id === id) { if (this.tabs[i].id === id) {
return this.tabs[i]; return this.tabs[i];
} }
}
return null;
} }
return null;
}
getTabByPosition (position) { getTabByPosition (position) {
let fromRight = position < 0; let fromRight = position < 0;
for (let i in this.tabs) { for (let i in this.tabs) {
if (this.tabs[i].getPosition(fromRight) === position) { if (this.tabs[i].getPosition(fromRight) === position) {
return this.tabs[i]; return this.tabs[i];
} }
}
return null;
} }
return null;
}
getTabByRelPosition (position) { getTabByRelPosition (position) {
position = this.getActiveTab().getPosition() + position; position = this.getActiveTab().getPosition() + position;
if (position <= 0) { if (position <= 0) {
return null; return null;
}
return this.getTabByPosition(position);
} }
return this.getTabByPosition(position);
}
getNextTab () { getNextTab () {
return this.getTabByRelPosition(1); return this.getTabByRelPosition(1);
} }
getPreviousTab () { getPreviousTab () {
return this.getTabByRelPosition(-1); return this.getTabByRelPosition(-1);
} }
getTabs () { getTabs () {
return this.tabs.slice(); return this.tabs.slice();
} }
eachTab (fn) { eachTab (fn) {
this.getTabs().forEach(fn); this.getTabs().forEach(fn);
return this; return this;
} }
getActiveTab () { getActiveTab () {
if (this.tabs.length === 0) return null; if (this.tabs.length === 0) return null;
return this.tabs[0]; return this.tabs[0];
} }
} }
const TabGroupPrivate = { const TabGroupPrivate = {
initNewTabButton: function () { initNewTabButton: function () {
if (!this.options.newTab) return; if (!this.options.newTab) return;
let container = document.querySelector(this.options.buttonsContainerSelector); let container = document.querySelector(this.options.buttonsContainerSelector);
let button = container.appendChild(document.createElement("button")); let button = container.appendChild(document.createElement("button"));
button.classList.add(`${this.options.tabClass}-button-new`); button.classList.add(`${this.options.tabClass}-button-new`);
button.innerHTML = this.options.newTabButtonText; button.innerHTML = this.options.newTabButtonText;
button.addEventListener("click", this.addTab.bind(this, undefined), false); button.addEventListener("click", this.addTab.bind(this, undefined), false);
}, },
removeTab: function (tab, triggerEvent) { removeTab: function (tab, triggerEvent) {
let id = tab.id; let id = tab.id;
for (let i in this.tabs) { for (let i in this.tabs) {
if (this.tabs[i].id === id) { if (this.tabs[i].id === id) {
this.tabs.splice(i, 1); this.tabs.splice(i, 1);
break; break;
} }
}
if (triggerEvent) {
this.emit("tab-removed", tab, this);
}
return this;
},
setActiveTab: function (tab) {
TabGroupPrivate.removeTab.bind(this)(tab);
this.tabs.unshift(tab);
this.emit("tab-active", tab, this);
return this;
},
activateRecentTab: function (tab) {
if (this.tabs.length > 0) {
this.tabs[0].activate();
}
return this;
} }
if (triggerEvent) {
this.emit("tab-removed", tab, this);
}
return this;
},
setActiveTab: function (tab) {
TabGroupPrivate.removeTab.bind(this)(tab);
this.tabs.unshift(tab);
this.emit("tab-active", tab, this);
return this;
},
activateRecentTab: function (tab) {
if (this.tabs.length > 0) {
this.tabs[0].activate();
}
return this;
}
}; };
class Tab extends EventEmitter { class Tab extends EventEmitter {
constructor (tabGroup, id, args) { constructor (tabGroup, id, args) {
super(); super();
this.tabGroup = tabGroup; this.tabGroup = tabGroup;
this.id = id; this.id = id;
this.title = args.title; this.title = args.title;
this.badge = args.badge; this.badge = args.badge;
this.iconURL = args.iconURL; this.iconURL = args.iconURL;
this.icon = args.icon; this.icon = args.icon;
this.closable = args.closable === false ? false : true; this.closable = args.closable === false ? false : true;
this.webviewAttributes = args.webviewAttributes || {}; this.webviewAttributes = args.webviewAttributes || {};
this.webviewAttributes.src = args.src; this.webviewAttributes.src = args.src;
this.tabElements = {}; this.tabElements = {};
TabPrivate.initTab.bind(this)(); TabPrivate.initTab.bind(this)();
TabPrivate.initWebview.bind(this)(); TabPrivate.initWebview.bind(this)();
if (args.visible !== false) { if (args.visible !== false) {
this.show(); this.show();
} }
if (typeof args.ready === "function") { if (typeof args.ready === "function") {
args.ready(this); args.ready(this);
} }
}
setTitle (title) {
if (this.isClosed) return;
let span = this.tabElements.title;
span.innerHTML = title;
span.title = title;
this.title = title;
this.emit("title-changed", title, this);
return this;
}
getTitle () {
if (this.isClosed) return;
return this.title;
}
setBadge (badge) {
if (this.isClosed) return;
let span = this.tabElements.badge;
this.badge = badge;
if (badge) {
span.innerHTML = badge;
span.classList.remove('hidden');
} else {
span.classList.add('hidden');
} }
setTitle (title) { this.emit("badge-changed", badge, this);
if (this.isClosed) return; }
let span = this.tabElements.title;
span.innerHTML = title; getBadge () {
span.title = title; if (this.isClosed) return;
this.title = title; return this.badge;
this.emit("title-changed", title, this); }
return this;
setIcon (iconURL, icon) {
if (this.isClosed) return;
this.iconURL = iconURL;
this.icon = icon;
let span = this.tabElements.icon;
if (iconURL) {
span.innerHTML = `<img src="${iconURL}" />`;
this.emit("icon-changed", iconURL, this);
} else if (icon) {
span.innerHTML = `<i class="${icon}"></i>`;
this.emit("icon-changed", icon, this);
} }
getTitle () { return this;
if (this.isClosed) return; }
return this.title;
getIcon () {
if (this.isClosed) return;
if (this.iconURL) return this.iconURL;
return this.icon;
}
setPosition (newPosition) {
let tabContainer = this.tabGroup.tabContainer;
let tabs = tabContainer.children;
let oldPosition = this.getPosition() - 1;
if (newPosition < 0) {
newPosition += tabContainer.childElementCount;
if (newPosition < 0) {
newPosition = 0;
}
} else {
if (newPosition > tabContainer.childElementCount) {
newPosition = tabContainer.childElementCount;
}
// Make 1 be leftmost position
newPosition--;
} }
setBadge (badge) { if (newPosition > oldPosition) {
if (this.isClosed) return; newPosition++;
let span = this.tabElements.badge;
this.badge = badge;
if (badge) {
span.innerHTML = badge;
span.classList.remove('hidden');
} else {
span.classList.add('hidden');
}
this.emit("badge-changed", badge, this);
} }
getBadge () { tabContainer.insertBefore(tabs[oldPosition], tabs[newPosition]);
if (this.isClosed) return;
return this.badge; return this;
}
getPosition (fromRight) {
let position = 0;
let tab = this.tab;
while ((tab = tab.previousSibling) != null) position++;
if (fromRight === true) {
position -= this.tabGroup.tabContainer.childElementCount;
} }
setIcon (iconURL, icon) { if (position >= 0) {
if (this.isClosed) return; position++;
this.iconURL = iconURL;
this.icon = icon;
let span = this.tabElements.icon;
if (iconURL) {
span.innerHTML = `<img src="${iconURL}" />`;
this.emit("icon-changed", iconURL, this);
} else if (icon) {
span.innerHTML = `<i class="${icon}"></i>`;
this.emit("icon-changed", icon, this);
}
return this;
} }
getIcon () { return position;
if (this.isClosed) return; }
if (this.iconURL) return this.iconURL;
return this.icon; activate () {
if (this.isClosed) return;
let activeTab = this.tabGroup.getActiveTab();
if (activeTab) {
activeTab.tab.classList.remove("active");
activeTab.webview.classList.remove("visible");
activeTab.emit("inactive", activeTab);
} }
TabGroupPrivate.setActiveTab.bind(this.tabGroup)(this);
this.tab.classList.add("active");
this.webview.classList.add("visible");
this.webview.focus();
this.emit("active", this);
return this;
}
setPosition (newPosition) { show (flag) {
let tabContainer = this.tabGroup.tabContainer; if (this.isClosed) return;
let tabs = tabContainer.children; if (flag !== false) {
let oldPosition = this.getPosition() - 1; this.tab.classList.add("visible");
this.emit("visible", this);
if (newPosition < 0) { } else {
newPosition += tabContainer.childElementCount; this.tab.classList.remove("visible");
this.emit("hidden", this);
if (newPosition < 0) {
newPosition = 0;
}
} else {
if (newPosition > tabContainer.childElementCount) {
newPosition = tabContainer.childElementCount;
}
// Make 1 be leftmost position
newPosition--;
}
if (newPosition > oldPosition) {
newPosition++;
}
tabContainer.insertBefore(tabs[oldPosition], tabs[newPosition]);
return this;
} }
return this;
}
getPosition (fromRight) { hide () {
let position = 0; return this.show(false);
let tab = this.tab; }
while ((tab = tab.previousSibling) != null) position++;
if (fromRight === true) { flash (flag) {
position -= this.tabGroup.tabContainer.childElementCount; if (this.isClosed) return;
} if (flag !== false) {
this.tab.classList.add("flash");
if (position >= 0) { this.emit("flash", this);
position++; } else {
} this.tab.classList.remove("flash");
this.emit("unflash", this);
return position;
} }
return this;
}
activate () { unflash () {
if (this.isClosed) return; return this.flash(false);
let activeTab = this.tabGroup.getActiveTab(); }
if (activeTab) {
activeTab.tab.classList.remove("active"); close (force) {
activeTab.webview.classList.remove("visible"); this.emit("closing", this);
activeTab.emit("inactive", activeTab); if (this.isClosed || (!this.closable && !force)) return;
} this.isClosed = true;
TabGroupPrivate.setActiveTab.bind(this.tabGroup)(this); let tabGroup = this.tabGroup;
this.tab.classList.add("active"); tabGroup.tabContainer.removeChild(this.tab);
this.webview.classList.add("visible"); tabGroup.viewContainer.removeChild(this.webview);
this.webview.focus(); let activeTab = this.tabGroup.getActiveTab();
this.emit("active", this); TabGroupPrivate.removeTab.bind(tabGroup)(this, true);
return this; this.emit("close", this);
}
if (activeTab.id === this.id) {
show (flag) { TabGroupPrivate.activateRecentTab.bind(tabGroup)();
if (this.isClosed) return;
if (flag !== false) {
this.tab.classList.add("visible");
this.emit("visible", this);
} else {
this.tab.classList.remove("visible");
this.emit("hidden", this);
}
return this;
}
hide () {
return this.show(false);
}
flash (flag) {
if (this.isClosed) return;
if (flag !== false) {
this.tab.classList.add("flash");
this.emit("flash", this);
} else {
this.tab.classList.remove("flash");
this.emit("unflash", this);
}
return this;
}
unflash () {
return this.flash(false);
}
close (force) {
this.emit("closing", this);
if (this.isClosed || (!this.closable && !force)) return;
this.isClosed = true;
let tabGroup = this.tabGroup;
tabGroup.tabContainer.removeChild(this.tab);
tabGroup.viewContainer.removeChild(this.webview);
let activeTab = this.tabGroup.getActiveTab();
TabGroupPrivate.removeTab.bind(tabGroup)(this, true);
this.emit("close", this);
if (activeTab.id === this.id) {
TabGroupPrivate.activateRecentTab.bind(tabGroup)();
}
} }
}
} }
const TabPrivate = { const TabPrivate = {
initTab: function () { initTab: function () {
let tabClass = this.tabGroup.options.tabClass; let tabClass = this.tabGroup.options.tabClass;
// Create tab element // Create tab element
let tab = this.tab = document.createElement("div"); let tab = this.tab = document.createElement("div");
tab.classList.add(tabClass); tab.classList.add(tabClass);
for (let el of ["icon", "title", "buttons", "badge"]) { for (let el of ["icon", "title", "buttons", "badge"]) {
let span = tab.appendChild(document.createElement("span")); let span = tab.appendChild(document.createElement("span"));
span.classList.add(`${tabClass}-${el}`); span.classList.add(`${tabClass}-${el}`);
this.tabElements[el] = span; this.tabElements[el] = span;
}
this.setTitle(this.title);
this.setBadge(this.badge);
this.setIcon(this.iconURL, this.icon);
TabPrivate.initTabButtons.bind(this)();
TabPrivate.initTabClickHandler.bind(this)();
this.tabGroup.tabContainer.appendChild(this.tab);
},
initTabButtons: function () {
let container = this.tabElements.buttons;
let tabClass = this.tabGroup.options.tabClass;
if (this.closable) {
let button = container.appendChild(document.createElement("button"));
button.classList.add(`${tabClass}-button-close`);
button.innerHTML = this.tabGroup.options.closeButtonText;
button.addEventListener("click", this.close.bind(this, false), false);
}
},
initTabClickHandler: function () {
// Mouse up
const tabClickHandler = function (e) {
if (this.isClosed) return;
if (e.which === 2) {
this.close();
}
};
this.tab.addEventListener("mouseup", tabClickHandler.bind(this), false);
// Mouse down
const tabMouseDownHandler = function (e) {
if (this.isClosed) return;
if (e.which === 1) {
if (e.target.matches("button")) return;
this.activate();
}
};
this.tab.addEventListener("mousedown", tabMouseDownHandler.bind(this), false);
},
initWebview: function () {
this.webview = document.createElement("webview");
const tabWebviewDidFinishLoadHandler = function (e) {
this.emit("webview-ready", this);
};
this.webview.addEventListener("did-finish-load", tabWebviewDidFinishLoadHandler.bind(this), false);
this.webview.classList.add(this.tabGroup.options.viewClass);
if (this.webviewAttributes) {
let attrs = this.webviewAttributes;
for (let key in attrs) {
this.webview.setAttribute(key, attrs[key]);
}
}
this.tabGroup.viewContainer.appendChild(this.webview);
} }
this.setTitle(this.title);
this.setBadge(this.badge);
this.setIcon(this.iconURL, this.icon);
TabPrivate.initTabButtons.bind(this)();
TabPrivate.initTabClickHandler.bind(this)();
this.tabGroup.tabContainer.appendChild(this.tab);
},
initTabButtons: function () {
let container = this.tabElements.buttons;
let tabClass = this.tabGroup.options.tabClass;
if (this.closable) {
let button = container.appendChild(document.createElement("button"));
button.classList.add(`${tabClass}-button-close`);
button.innerHTML = this.tabGroup.options.closeButtonText;
button.addEventListener("click", this.close.bind(this, false), false);
}
},
initTabClickHandler: function () {
// Mouse up
const tabClickHandler = function (e) {
if (this.isClosed) return;
if (e.which === 2) {
this.close();
}
};
this.tab.addEventListener("mouseup", tabClickHandler.bind(this), false);
// Mouse down
const tabMouseDownHandler = function (e) {
if (this.isClosed) return;
if (e.which === 1) {
if (e.target.matches("button")) return;
this.activate();
}
};
this.tab.addEventListener("mousedown", tabMouseDownHandler.bind(this), false);
},
initWebview: function () {
this.webview = document.createElement("webview");
const tabWebviewDidFinishLoadHandler = function (e) {
this.emit("webview-ready", this);
};
this.webview.addEventListener("did-finish-load", tabWebviewDidFinishLoadHandler.bind(this), false);
this.webview.addEventListener("dom-ready", function () {
// Remove this once https://github.com/electron/electron/issues/14474 is fixed
tab.webview.blur();
tab.webview.focus();
});
this.webview.classList.add(this.tabGroup.options.viewClass);
if (this.webviewAttributes) {
let attrs = this.webviewAttributes;
for (let key in attrs) {
this.webview.setAttribute(key, attrs[key]);
}
}
this.tabGroup.viewContainer.appendChild(this.webview);
}
}; };
module.exports = TabGroup; module.exports = TabGroup;

View file

@ -1,6 +1,6 @@
{ {
"name": "electron-tabs", "name": "electron-tabs",
"version": "0.11.0", "version": "0.12.0",
"description": "Simple tabs for Electron applications", "description": "Simple tabs for Electron applications",
"main": "index.js", "main": "index.js",
"repository": { "repository": {
@ -20,6 +20,6 @@
"author": "brrd", "author": "brrd",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"electron": "^1.6.11" "electron": "^8.0.0"
} }
} }