mirror of
https://ark.sudovanilla.org/Korbs/electron-tabs.git
synced 2024-12-22 19:43:53 +00:00
commit
b20f757c5d
11
.editorconfig
Normal file
11
.editorconfig
Normal 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
|
36
README.md
36
README.md
|
@ -18,12 +18,23 @@ $ npm run demo
|
|||
|
||||
## 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
|
||||
<div class="etabs-tabgroup">
|
||||
<div class="etabs-tabs"></div>
|
||||
<div class="etabs-buttons"></div>
|
||||
<div class="etabs-tabs"></div>
|
||||
<div class="etabs-buttons"></div>
|
||||
</div>
|
||||
<div class="etabs-views"></div>
|
||||
```
|
||||
|
@ -34,14 +45,14 @@ And call the module in the renderer process:
|
|||
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
|
||||
let tabGroup = new TabGroup();
|
||||
let tab = tabGroup.addTab({
|
||||
title: "Electron",
|
||||
src: "http://electron.atom.io",
|
||||
visible: true
|
||||
title: "Electron",
|
||||
src: "http://electron.atom.io",
|
||||
visible: true
|
||||
});
|
||||
```
|
||||
|
||||
|
@ -52,6 +63,7 @@ If you don't want to write your own styles, you can also insert the sample elect
|
|||
```
|
||||
|
||||
### 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
|
||||
|
||||
## API
|
||||
|
@ -238,11 +250,11 @@ const TabGroup = require("electron-tabs");
|
|||
const dragula = require("dragula");
|
||||
|
||||
var tabGroup = new TabGroup({
|
||||
ready: function (tabGroup) {
|
||||
dragula([tabGroup.tabContainer], {
|
||||
direction: "horizontal"
|
||||
});
|
||||
}
|
||||
ready: function (tabGroup) {
|
||||
dragula([tabGroup.tabContainer], {
|
||||
direction: "horizontal"
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
|
|
25
demo/app.js
25
demo/app.js
|
@ -3,19 +3,16 @@ const app = electron.app;
|
|||
|
||||
app.setName('electron-tabs-demo');
|
||||
|
||||
|
||||
app.on('ready', function () {
|
||||
|
||||
const mainWindow = new electron.BrowserWindow({
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
webviewTag: true
|
||||
}
|
||||
});
|
||||
mainWindow.loadURL('file://' + __dirname + '/electron-tabs.html');
|
||||
mainWindow.on('ready-to-show', function () {
|
||||
mainWindow.show();
|
||||
mainWindow.focus();
|
||||
});
|
||||
|
||||
const mainWindow = new electron.BrowserWindow({
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
webviewTag: true
|
||||
}
|
||||
});
|
||||
mainWindow.loadURL('file://' + __dirname + '/electron-tabs.html');
|
||||
mainWindow.on('ready-to-show', function () {
|
||||
mainWindow.show();
|
||||
mainWindow.focus();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,38 +2,38 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<link rel="stylesheet" href="../electron-tabs.css">
|
||||
<link rel="stylesheet" href="../electron-tabs.css">
|
||||
</head>
|
||||
<body style="margin:0">
|
||||
|
||||
<div class="etabs-tabgroup">
|
||||
<div class="etabs-tabs"></div>
|
||||
<div class="etabs-buttons"></div>
|
||||
<div class="etabs-tabs"></div>
|
||||
<div class="etabs-buttons"></div>
|
||||
</div>
|
||||
<div class="etabs-views"></div>
|
||||
|
||||
|
||||
<script>
|
||||
//const TabGroup = require('electron-tabs') normally but for demo :
|
||||
const TabGroup = require("../index");
|
||||
//const TabGroup = require('electron-tabs') normally but for demo :
|
||||
const TabGroup = require("../index");
|
||||
|
||||
let tabGroup = new TabGroup({
|
||||
newTab: {
|
||||
title: 'New Tab'
|
||||
}
|
||||
});
|
||||
let tabGroup = new TabGroup({
|
||||
newTab: {
|
||||
title: 'New Tab'
|
||||
}
|
||||
});
|
||||
|
||||
tabGroup.addTab({
|
||||
title: 'Google',
|
||||
src: 'http://google.com',
|
||||
});
|
||||
tabGroup.addTab({
|
||||
title: 'Google',
|
||||
src: 'http://google.com',
|
||||
});
|
||||
|
||||
tabGroup.addTab({
|
||||
title: "Electron",
|
||||
src: "http://electron.atom.io",
|
||||
visible: true,
|
||||
active: true
|
||||
});
|
||||
tabGroup.addTab({
|
||||
title: "Electron",
|
||||
src: "http://electron.atom.io",
|
||||
visible: true,
|
||||
active: true
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -1,133 +1,134 @@
|
|||
.etabs-tabgroup {
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
background-color: #ccc;
|
||||
cursor: default;
|
||||
font: caption;
|
||||
font-size: 14px;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
background-color: #ccc;
|
||||
cursor: default;
|
||||
font: caption;
|
||||
font-size: 14px;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.etabs-tabs {
|
||||
}
|
||||
|
||||
.etabs-tab {
|
||||
display: none;
|
||||
position: relative;
|
||||
color: #333;
|
||||
height: 22px;
|
||||
padding: 6px 8px 4px;
|
||||
border: 1px solid #aaa;
|
||||
border-bottom: none;
|
||||
border-left: none;
|
||||
background: linear-gradient(to bottom, rgba(234,234,234,1) 0%,rgba(204,204,204,1) 100%);
|
||||
font: caption;
|
||||
font-size: 14px;
|
||||
background-color: #ccc;
|
||||
cursor: default;
|
||||
display: none;
|
||||
position: relative;
|
||||
color: #333;
|
||||
height: 22px;
|
||||
padding: 6px 8px 4px;
|
||||
border: 1px solid #aaa;
|
||||
border-bottom: none;
|
||||
border-left: none;
|
||||
background: linear-gradient(to bottom, rgba(234,234,234,1) 0%,rgba(204,204,204,1) 100%);
|
||||
font: caption;
|
||||
font-size: 14px;
|
||||
background-color: #ccc;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* Dragula */
|
||||
.etabs-tab.gu-mirror {
|
||||
padding-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.etabs-tab:first-child {
|
||||
border-left: none;
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.etabs-tab.visible {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.etabs-tab.active {
|
||||
background: #fff;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.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 {
|
||||
float: left;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.etabs-buttons button {
|
||||
float: left;
|
||||
color: #333;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 12px;
|
||||
margin-top: 6px;
|
||||
border-radius: 2px;
|
||||
margin-left: 4px;
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
padding: 4px 0;
|
||||
float: left;
|
||||
color: #333;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 12px;
|
||||
margin-top: 6px;
|
||||
border-radius: 2px;
|
||||
margin-left: 4px;
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.etabs-buttons button:hover {
|
||||
color: #eee;
|
||||
background-color: #aaa;
|
||||
color: #eee;
|
||||
background-color: #aaa;
|
||||
}
|
||||
|
||||
.etabs-tab-badge {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: -7px;
|
||||
background: red;
|
||||
border-radius: 100%;
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
padding: 0 5px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: -7px;
|
||||
background: red;
|
||||
border-radius: 100%;
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.etabs-tab-badge.hidden {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.etabs-tab-icon {
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.etabs-tab-icon img {
|
||||
max-width: 16px;
|
||||
max-height: 16px;
|
||||
max-width: 16px;
|
||||
max-height: 16px;
|
||||
}
|
||||
|
||||
.etabs-tab-title {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.etabs-tab-buttons {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.etabs-tab-buttons button {
|
||||
display: inline-block;
|
||||
color: #333;
|
||||
background: none;
|
||||
border: none;
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
border-radius: 2px;
|
||||
display: inline-block;
|
||||
color: #333;
|
||||
background: none;
|
||||
border: none;
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.etabs-tab-buttons button:hover {
|
||||
color: #eee;
|
||||
background-color: #aaa;
|
||||
color: #eee;
|
||||
background-color: #aaa;
|
||||
}
|
||||
|
||||
.etabs-views {
|
||||
border-top: 1px solid #aaa;
|
||||
height: calc(100vh - 33px);
|
||||
position: relative;
|
||||
border-top: 1px solid #aaa;
|
||||
height: calc(100vh - 33px);
|
||||
}
|
||||
|
||||
.etab-view {
|
||||
position: relative;
|
||||
position: relative;
|
||||
}
|
||||
|
|
726
index.js
726
index.js
|
@ -1,422 +1,424 @@
|
|||
const EventEmitter = require("events");
|
||||
|
||||
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
|
||||
(function () {
|
||||
const styles = `
|
||||
webview {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
}
|
||||
webview.visible {
|
||||
visibility: visible;
|
||||
}
|
||||
`;
|
||||
let styleTag = document.createElement("style");
|
||||
styleTag.innerHTML = styles;
|
||||
document.getElementsByTagName("head")[0].appendChild(styleTag);
|
||||
const styles = `
|
||||
webview {
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
}
|
||||
webview.visible {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
visibility: visible;
|
||||
}
|
||||
`;
|
||||
let styleTag = document.createElement("style");
|
||||
styleTag.innerHTML = styles;
|
||||
document.getElementsByTagName("head")[0].appendChild(styleTag);
|
||||
})();
|
||||
|
||||
class TabGroup extends EventEmitter {
|
||||
constructor (args = {}) {
|
||||
super();
|
||||
let options = this.options = {
|
||||
tabContainerSelector: args.tabContainerSelector || ".etabs-tabs",
|
||||
buttonsContainerSelector: args.buttonsContainerSelector || ".etabs-buttons",
|
||||
viewContainerSelector: args.viewContainerSelector || ".etabs-views",
|
||||
tabClass: args.tabClass || "etabs-tab",
|
||||
viewClass: args.viewClass || "etabs-view",
|
||||
closeButtonText: args.closeButtonText || "✖",
|
||||
newTab: args.newTab,
|
||||
newTabButtonText: args.newTabButtonText || "+",
|
||||
ready: args.ready
|
||||
};
|
||||
this.tabContainer = document.querySelector(options.tabContainerSelector);
|
||||
this.viewContainer = document.querySelector(options.viewContainerSelector);
|
||||
this.tabs = [];
|
||||
this.newTabId = 0;
|
||||
TabGroupPrivate.initNewTabButton.bind(this)();
|
||||
if (typeof this.options.ready === "function") {
|
||||
this.options.ready(this);
|
||||
}
|
||||
constructor (args = {}) {
|
||||
super();
|
||||
let options = this.options = {
|
||||
tabContainerSelector: args.tabContainerSelector || ".etabs-tabs",
|
||||
buttonsContainerSelector: args.buttonsContainerSelector || ".etabs-buttons",
|
||||
viewContainerSelector: args.viewContainerSelector || ".etabs-views",
|
||||
tabClass: args.tabClass || "etabs-tab",
|
||||
viewClass: args.viewClass || "etabs-view",
|
||||
closeButtonText: args.closeButtonText || "×",
|
||||
newTab: args.newTab,
|
||||
newTabButtonText: args.newTabButtonText || "+",
|
||||
ready: args.ready
|
||||
};
|
||||
this.tabContainer = document.querySelector(options.tabContainerSelector);
|
||||
this.viewContainer = document.querySelector(options.viewContainerSelector);
|
||||
this.tabs = [];
|
||||
this.newTabId = 0;
|
||||
TabGroupPrivate.initNewTabButton.bind(this)();
|
||||
if (typeof this.options.ready === "function") {
|
||||
this.options.ready(this);
|
||||
}
|
||||
}
|
||||
|
||||
addTab (args = this.options.newTab) {
|
||||
if (typeof args === "function") {
|
||||
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;
|
||||
addTab (args = this.options.newTab) {
|
||||
if (typeof args === "function") {
|
||||
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;
|
||||
}
|
||||
|
||||
getTab (id) {
|
||||
for (let i in this.tabs) {
|
||||
if (this.tabs[i].id === id) {
|
||||
return this.tabs[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
getTab (id) {
|
||||
for (let i in this.tabs) {
|
||||
if (this.tabs[i].id === id) {
|
||||
return this.tabs[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getTabByPosition (position) {
|
||||
let fromRight = position < 0;
|
||||
for (let i in this.tabs) {
|
||||
if (this.tabs[i].getPosition(fromRight) === position) {
|
||||
return this.tabs[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
getTabByPosition (position) {
|
||||
let fromRight = position < 0;
|
||||
for (let i in this.tabs) {
|
||||
if (this.tabs[i].getPosition(fromRight) === position) {
|
||||
return this.tabs[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getTabByRelPosition (position) {
|
||||
position = this.getActiveTab().getPosition() + position;
|
||||
if (position <= 0) {
|
||||
return null;
|
||||
}
|
||||
return this.getTabByPosition(position);
|
||||
getTabByRelPosition (position) {
|
||||
position = this.getActiveTab().getPosition() + position;
|
||||
if (position <= 0) {
|
||||
return null;
|
||||
}
|
||||
return this.getTabByPosition(position);
|
||||
}
|
||||
|
||||
getNextTab () {
|
||||
return this.getTabByRelPosition(1);
|
||||
}
|
||||
getNextTab () {
|
||||
return this.getTabByRelPosition(1);
|
||||
}
|
||||
|
||||
getPreviousTab () {
|
||||
return this.getTabByRelPosition(-1);
|
||||
}
|
||||
getPreviousTab () {
|
||||
return this.getTabByRelPosition(-1);
|
||||
}
|
||||
|
||||
getTabs () {
|
||||
return this.tabs.slice();
|
||||
}
|
||||
getTabs () {
|
||||
return this.tabs.slice();
|
||||
}
|
||||
|
||||
eachTab (fn) {
|
||||
this.getTabs().forEach(fn);
|
||||
return this;
|
||||
}
|
||||
eachTab (fn) {
|
||||
this.getTabs().forEach(fn);
|
||||
return this;
|
||||
}
|
||||
|
||||
getActiveTab () {
|
||||
if (this.tabs.length === 0) return null;
|
||||
return this.tabs[0];
|
||||
}
|
||||
getActiveTab () {
|
||||
if (this.tabs.length === 0) return null;
|
||||
return this.tabs[0];
|
||||
}
|
||||
}
|
||||
|
||||
const TabGroupPrivate = {
|
||||
initNewTabButton: function () {
|
||||
if (!this.options.newTab) return;
|
||||
let container = document.querySelector(this.options.buttonsContainerSelector);
|
||||
let button = container.appendChild(document.createElement("button"));
|
||||
button.classList.add(`${this.options.tabClass}-button-new`);
|
||||
button.innerHTML = this.options.newTabButtonText;
|
||||
button.addEventListener("click", this.addTab.bind(this, undefined), false);
|
||||
},
|
||||
initNewTabButton: function () {
|
||||
if (!this.options.newTab) return;
|
||||
let container = document.querySelector(this.options.buttonsContainerSelector);
|
||||
let button = container.appendChild(document.createElement("button"));
|
||||
button.classList.add(`${this.options.tabClass}-button-new`);
|
||||
button.innerHTML = this.options.newTabButtonText;
|
||||
button.addEventListener("click", this.addTab.bind(this, undefined), false);
|
||||
},
|
||||
|
||||
removeTab: function (tab, triggerEvent) {
|
||||
let id = tab.id;
|
||||
for (let i in this.tabs) {
|
||||
if (this.tabs[i].id === id) {
|
||||
this.tabs.splice(i, 1);
|
||||
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;
|
||||
removeTab: function (tab, triggerEvent) {
|
||||
let id = tab.id;
|
||||
for (let i in this.tabs) {
|
||||
if (this.tabs[i].id === id) {
|
||||
this.tabs.splice(i, 1);
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
class Tab extends EventEmitter {
|
||||
constructor (tabGroup, id, args) {
|
||||
super();
|
||||
this.tabGroup = tabGroup;
|
||||
this.id = id;
|
||||
this.title = args.title;
|
||||
this.badge = args.badge;
|
||||
this.iconURL = args.iconURL;
|
||||
this.icon = args.icon;
|
||||
this.closable = args.closable === false ? false : true;
|
||||
this.webviewAttributes = args.webviewAttributes || {};
|
||||
this.webviewAttributes.src = args.src;
|
||||
this.tabElements = {};
|
||||
TabPrivate.initTab.bind(this)();
|
||||
TabPrivate.initWebview.bind(this)();
|
||||
if (args.visible !== false) {
|
||||
this.show();
|
||||
}
|
||||
if (typeof args.ready === "function") {
|
||||
args.ready(this);
|
||||
}
|
||||
constructor (tabGroup, id, args) {
|
||||
super();
|
||||
this.tabGroup = tabGroup;
|
||||
this.id = id;
|
||||
this.title = args.title;
|
||||
this.badge = args.badge;
|
||||
this.iconURL = args.iconURL;
|
||||
this.icon = args.icon;
|
||||
this.closable = args.closable === false ? false : true;
|
||||
this.webviewAttributes = args.webviewAttributes || {};
|
||||
this.webviewAttributes.src = args.src;
|
||||
this.tabElements = {};
|
||||
TabPrivate.initTab.bind(this)();
|
||||
TabPrivate.initWebview.bind(this)();
|
||||
if (args.visible !== false) {
|
||||
this.show();
|
||||
}
|
||||
if (typeof args.ready === "function") {
|
||||
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) {
|
||||
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;
|
||||
this.emit("badge-changed", badge, this);
|
||||
}
|
||||
|
||||
getBadge () {
|
||||
if (this.isClosed) return;
|
||||
return this.badge;
|
||||
}
|
||||
|
||||
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 () {
|
||||
if (this.isClosed) return;
|
||||
return this.title;
|
||||
return this;
|
||||
}
|
||||
|
||||
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 (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');
|
||||
}
|
||||
|
||||
this.emit("badge-changed", badge, this);
|
||||
if (newPosition > oldPosition) {
|
||||
newPosition++;
|
||||
}
|
||||
|
||||
getBadge () {
|
||||
if (this.isClosed) return;
|
||||
return this.badge;
|
||||
tabContainer.insertBefore(tabs[oldPosition], tabs[newPosition]);
|
||||
|
||||
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 (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);
|
||||
}
|
||||
|
||||
return this;
|
||||
if (position >= 0) {
|
||||
position++;
|
||||
}
|
||||
|
||||
getIcon () {
|
||||
if (this.isClosed) return;
|
||||
if (this.iconURL) return this.iconURL;
|
||||
return this.icon;
|
||||
return position;
|
||||
}
|
||||
|
||||
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) {
|
||||
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--;
|
||||
}
|
||||
|
||||
if (newPosition > oldPosition) {
|
||||
newPosition++;
|
||||
}
|
||||
|
||||
tabContainer.insertBefore(tabs[oldPosition], tabs[newPosition]);
|
||||
|
||||
return this;
|
||||
show (flag) {
|
||||
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;
|
||||
}
|
||||
|
||||
getPosition (fromRight) {
|
||||
let position = 0;
|
||||
let tab = this.tab;
|
||||
while ((tab = tab.previousSibling) != null) position++;
|
||||
hide () {
|
||||
return this.show(false);
|
||||
}
|
||||
|
||||
if (fromRight === true) {
|
||||
position -= this.tabGroup.tabContainer.childElementCount;
|
||||
}
|
||||
|
||||
if (position >= 0) {
|
||||
position++;
|
||||
}
|
||||
|
||||
return position;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
show (flag) {
|
||||
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)();
|
||||
}
|
||||
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 = {
|
||||
initTab: function () {
|
||||
let tabClass = this.tabGroup.options.tabClass;
|
||||
initTab: function () {
|
||||
let tabClass = this.tabGroup.options.tabClass;
|
||||
|
||||
// Create tab element
|
||||
let tab = this.tab = document.createElement("div");
|
||||
tab.classList.add(tabClass);
|
||||
for (let el of ["icon", "title", "buttons", "badge"]) {
|
||||
let span = tab.appendChild(document.createElement("span"));
|
||||
span.classList.add(`${tabClass}-${el}`);
|
||||
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);
|
||||
// Create tab element
|
||||
let tab = this.tab = document.createElement("div");
|
||||
tab.classList.add(tabClass);
|
||||
for (let el of ["icon", "title", "buttons", "badge"]) {
|
||||
let span = tab.appendChild(document.createElement("span"));
|
||||
span.classList.add(`${tabClass}-${el}`);
|
||||
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.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;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "electron-tabs",
|
||||
"version": "0.11.0",
|
||||
"version": "0.12.0",
|
||||
"description": "Simple tabs for Electron applications",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
|
@ -20,6 +20,6 @@
|
|||
"author": "brrd",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"electron": "^1.6.11"
|
||||
"electron": "^8.0.0"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue