From c84689b22f21798600274f2d4761311bcfb355a8 Mon Sep 17 00:00:00 2001 From: Thomas Brouard Date: Tue, 4 Feb 2020 10:43:08 +0100 Subject: [PATCH 1/9] Demo: upgrade to electron 8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 221192b..3d52adb 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,6 @@ "author": "brrd", "license": "MIT", "devDependencies": { - "electron": "^1.6.11" + "electron": "^8.0.0" } } From 0c3e547782da77a58b9e788a2cce4ad79bbd4200 Mon Sep 17 00:00:00 2001 From: Thomas Brouard Date: Tue, 4 Feb 2020 11:13:47 +0100 Subject: [PATCH 2/9] Add editorconfig --- .editorconfig | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8b6f9b1 --- /dev/null +++ b/.editorconfig @@ -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 \ No newline at end of file From 0c0a72424533752511f440e688ec7a7f6a34c2fc Mon Sep 17 00:00:00 2001 From: Thomas Brouard Date: Tue, 4 Feb 2020 11:13:57 +0100 Subject: [PATCH 3/9] Fix indent --- demo/app.js | 25 +- demo/electron-tabs.html | 40 +-- electron-tabs.css | 140 ++++---- index.js | 724 ++++++++++++++++++++-------------------- 4 files changed, 463 insertions(+), 466 deletions(-) diff --git a/demo/app.js b/demo/app.js index 504a593..148aafb 100644 --- a/demo/app.js +++ b/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(); + }); }); diff --git a/demo/electron-tabs.html b/demo/electron-tabs.html index f36c980..f4ad72d 100644 --- a/demo/electron-tabs.html +++ b/demo/electron-tabs.html @@ -2,38 +2,38 @@ - +
-
-
+
+
diff --git a/electron-tabs.css b/electron-tabs.css index d93aa6e..c7c1feb 100644 --- a/electron-tabs.css +++ b/electron-tabs.css @@ -1,133 +1,133 @@ .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); + border-top: 1px solid #aaa; + height: calc(100vh - 33px); } .etab-view { - position: relative; + position: relative; } diff --git a/index.js b/index.js index dcaca17..885a9c6 100644 --- a/index.js +++ b/index.js @@ -1,422 +1,422 @@ 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 { + 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); })(); 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 = ``; + this.emit("icon-changed", iconURL, this); + } else if (icon) { + span.innerHTML = ``; + 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 = ``; - this.emit("icon-changed", iconURL, this); - } else if (icon) { - span.innerHTML = ``; - 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.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; From 8f1794c119d8842bbe4d90d1f2dda4c3183f371d Mon Sep 17 00:00:00 2001 From: Thomas Brouard Date: Tue, 4 Feb 2020 11:57:48 +0100 Subject: [PATCH 4/9] Fix webview overlapping tabs (fix #84) --- index.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index 885a9c6..6f73126 100644 --- a/index.js +++ b/index.js @@ -8,16 +8,12 @@ if (!document) { (function () { const styles = ` webview { - width: 100%; - height: 100%; - top: 0; - right: 0; - bottom: 0; - left: 0; position: absolute; visibility: hidden; } webview.visible { + width: 100%; + height: 100%; visibility: visible; } `; From 504f36a6fdb3fac29369bca64f580e1dbfc8f1b8 Mon Sep 17 00:00:00 2001 From: Thomas Brouard Date: Tue, 4 Feb 2020 11:58:28 +0100 Subject: [PATCH 5/9] Fix double scrollbars (fix #44) --- electron-tabs.css | 1 + 1 file changed, 1 insertion(+) diff --git a/electron-tabs.css b/electron-tabs.css index c7c1feb..1202bb4 100644 --- a/electron-tabs.css +++ b/electron-tabs.css @@ -124,6 +124,7 @@ } .etabs-views { + position: relative; border-top: 1px solid #aaa; height: calc(100vh - 33px); } From 38eae5b72cb0d83e3c4b68b7db1cd853462982ad Mon Sep 17 00:00:00 2001 From: Thomas Brouard Date: Tue, 4 Feb 2020 12:00:41 +0100 Subject: [PATCH 6/9] Improve default closeButtonText --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 6f73126..3a99fcf 100644 --- a/index.js +++ b/index.js @@ -31,7 +31,7 @@ class TabGroup extends EventEmitter { viewContainerSelector: args.viewContainerSelector || ".etabs-views", tabClass: args.tabClass || "etabs-tab", viewClass: args.viewClass || "etabs-view", - closeButtonText: args.closeButtonText || "✖", + closeButtonText: args.closeButtonText || "×", newTab: args.newTab, newTabButtonText: args.newTabButtonText || "+", ready: args.ready From 8a222be7f7d1f74aadf67241537b3d0989543e74 Mon Sep 17 00:00:00 2001 From: Thomas Brouard Date: Tue, 4 Feb 2020 12:07:21 +0100 Subject: [PATCH 7/9] Fix webview not focusing when created (fix #71) --- index.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/index.js b/index.js index 3a99fcf..5a09f6f 100644 --- a/index.js +++ b/index.js @@ -403,6 +403,12 @@ const TabPrivate = { 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; From e5ada44107ffa349ccaf1e3b96346cb6514dd7dd Mon Sep 17 00:00:00 2001 From: Thomas Brouard Date: Tue, 4 Feb 2020 12:14:55 +0100 Subject: [PATCH 8/9] Documentation update (webPreferences) --- README.md | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index ab45796..561026e 100644 --- a/README.md +++ b/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
-
-
+
+
``` @@ -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" + }); + } }); ``` From 78e030d02ce6d3b170a8caf8f589f7b273a17863 Mon Sep 17 00:00:00 2001 From: Thomas Brouard Date: Tue, 4 Feb 2020 13:38:12 +0100 Subject: [PATCH 9/9] Version 0.12.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3d52adb..04dbe61 100644 --- a/package.json +++ b/package.json @@ -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": {