mirror of
https://ark.sudovanilla.org/Korbs/electron-tabs.git
synced 2024-12-23 03:53:53 +00:00
1 line
18 KiB
Plaintext
1 line
18 KiB
Plaintext
{"mappings":"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","sources":["src/src/index.ts","src/index.ts"],"sourcesContent":[null,"import Sortable from \"sortablejs\";\n// @ts-ignore\nimport styles from \"bundle-text:./style.css\";\n\nif (!document) {\n throw Error(\"electron-tabs module must be called in renderer process\");\n}\n\ninterface TabGroupOptions {\n closeButtonText: string,\n defaultTab: TabOptions | ((tabGroup: TabGroup) => TabOptions),\n newTabButton: boolean,\n newTabButtonText: string,\n sortable: boolean,\n sortableOptions?: Sortable.Options\n visibilityThreshold: number,\n}\n\ninterface TabOptions {\n active?: boolean;\n badge?: Badge;\n closable?: boolean;\n icon?: string;\n iconURL?: string;\n ready?: ((tab: Tab) => void);\n src?: string;\n title?: string;\n visible?: boolean;\n webviewAttributes?: { [key: string]: any };\n}\n\ninterface Badge {\n text: string,\n classname: string\n}\n\nconst CLASSNAMES = {\n ROOT: \"etabs\",\n NAV: \"nav\",\n TABS: \"tabs\",\n TAB: \"tab\",\n BUTTONS: \"buttons\",\n VIEWS: \"views\",\n VIEW: \"view\"\n}\n\nfunction emit(emitter: TabGroup | Tab, type: string, args: any[]) {\n if (type === \"ready\") {\n emitter.isReady = true;\n }\n emitter.dispatchEvent(new CustomEvent(type, { detail: args }));\n}\n\nfunction on(emitter: TabGroup | Tab, type: string, fn: (detail: string) => void, options?: { [key: string]: any }) {\n if (type === \"ready\" && emitter.isReady === true) {\n fn.apply(emitter, [emitter]);\n }\n emitter.addEventListener(type, ((e: CustomEvent) => fn.apply(emitter, e.detail)) as EventListener, options);\n}\n\nclass TabGroup extends HTMLElement {\n buttonContainer: HTMLDivElement;\n isReady: boolean;\n newTabId: number;\n options: TabGroupOptions;\n shadow: ShadowRoot;\n tabContainer: HTMLDivElement;\n tabs: Array<Tab>;\n viewContainer: HTMLDivElement;\n\n constructor() {\n super();\n\n this.isReady = false;\n\n // Options\n this.options = {\n closeButtonText: this.getAttribute(\"close-button-text\") || \"×\",\n defaultTab: { title: \"New Tab\", active: true },\n newTabButton: !!this.getAttribute(\"new-tab-button\") === true || false,\n newTabButtonText: this.getAttribute(\"new-tab-button-text\") || \"+\",\n sortable: !!this.getAttribute(\"sortable\") === true || false,\n visibilityThreshold: Number(this.getAttribute(\"visibility-threshold\")) || 0\n };\n\n this.tabs = [];\n this.newTabId = 0;\n\n this.createComponent();\n this.initVisibility();\n if (this.options.sortable) {\n this.initSortable();\n }\n\n this.emit(\"ready\", this);\n }\n\n emit(type: string, ...args: any[]) {\n return emit(this, type, args);\n }\n\n on(type: string, fn: (...detail: any[]) => void) {\n return on(this, type, fn);\n }\n\n once(type: string, fn: (detail: string) => void) {\n return on(this, type, fn, { once: true });\n }\n\n connectedCallback() {\n // Support custom styles\n const style = this.querySelector(\"style\");\n if (style) {\n this.shadow.appendChild(style);\n }\n }\n\n private createComponent() {\n const shadow = this.attachShadow({mode: \"open\"});\n this.shadow = shadow;\n\n const wrapper = document.createElement(\"div\");\n wrapper.setAttribute(\"class\", CLASSNAMES.ROOT);\n\n const tabgroup = document.createElement(\"nav\");\n tabgroup.setAttribute(\"class\", CLASSNAMES.NAV);\n wrapper.appendChild(tabgroup);\n\n const tabContainer = document.createElement(\"div\");\n tabContainer.setAttribute(\"class\", CLASSNAMES.TABS);\n tabgroup.appendChild(tabContainer);\n this.tabContainer = tabContainer;\n\n const buttonContainer = document.createElement(\"div\");\n buttonContainer.setAttribute(\"class\", CLASSNAMES.BUTTONS);\n tabgroup.appendChild(buttonContainer);\n this.buttonContainer = buttonContainer;\n\n if (this.options.newTabButton) {\n const button = this.buttonContainer.appendChild(document.createElement(\"button\"));\n button.innerHTML = this.options.newTabButtonText;\n button.addEventListener(\"click\", this.addTab.bind(this, undefined), false);\n }\n\n const viewContainer = document.createElement(\"div\");\n viewContainer.setAttribute(\"class\", CLASSNAMES.VIEWS);\n wrapper.appendChild(viewContainer);\n this.viewContainer = viewContainer;\n\n const style = document.createElement(\"style\");\n style.textContent = styles;\n\n shadow.appendChild(style);\n shadow.appendChild(wrapper);\n }\n\n private initVisibility() {\n function toggleTabsVisibility(tab: Tab, tabGroup: TabGroup) {\n const visibilityThreshold = this.options.visibilityThreshold;\n const el = tabGroup.tabContainer.parentElement;\n if (this.tabs.length >= visibilityThreshold) {\n el.classList.add(\"visible\");\n } else {\n el.classList.remove(\"visible\");\n }\n }\n\n this.on(\"tab-added\", toggleTabsVisibility);\n this.on(\"tab-removed\", toggleTabsVisibility);\n }\n\n initSortable() {\n const createNewSortable = () => {\n const options = Object.assign({\n direction: \"horizontal\",\n animation: 150,\n swapThreshold: 0.20\n }, this.options.sortableOptions);\n new Sortable(this.tabContainer, options);\n };\n\n if (Sortable) {\n createNewSortable();\n } else {\n document.addEventListener(\"DOMContentLoaded\", createNewSortable);\n }\n }\n\n setDefaultTab(tab: TabOptions) {\n this.options.defaultTab = tab;\n }\n\n addTab(args = this.options.defaultTab) {\n if (typeof args === \"function\") {\n args = args(this);\n }\n const id = this.newTabId;\n this.newTabId++;\n const tab = new Tab(this, id, args);\n this.tabs.push(tab);\n // Don't call tab.activate() before a tab is referenced in this.tabs\n if (args.active === true) {\n tab.activate();\n }\n this.emit(\"tab-added\", tab, this);\n return tab;\n }\n\n getTab(id: number) {\n for (let i in this.tabs) {\n if (this.tabs[i].id === id) {\n return this.tabs[i];\n }\n }\n return null;\n }\n\n getTabByPosition(position: number) {\n const fromRight = position < 0;\n for (let i in this.tabs) {\n if (this.tabs[i].getPosition(fromRight) === position) {\n return this.tabs[i];\n }\n }\n return null;\n }\n\n getTabByRelPosition(position: number) {\n position = this.getActiveTab().getPosition() + position;\n if (position <= 0) {\n return null;\n }\n return this.getTabByPosition(position);\n }\n\n getNextTab() {\n return this.getTabByRelPosition(1);\n }\n\n getPreviousTab() {\n return this.getTabByRelPosition(-1);\n }\n\n getTabs() {\n return this.tabs.slice();\n }\n\n eachTab(fn: (tab: Tab) => void) {\n this.getTabs().forEach(fn);\n }\n\n getActiveTab() {\n if (this.tabs.length === 0) return null;\n return this.tabs[0];\n }\n\n setActiveTab(tab: Tab) {\n this.removeTab(tab);\n this.tabs.unshift(tab);\n this.emit(\"tab-active\", tab, this);\n }\n\n removeTab(tab: Tab, triggerEvent = false) {\n const id = tab.id;\n const index = this.tabs.findIndex((t: Tab) => t.id === id);\n this.tabs.splice(index, 1);\n if (triggerEvent) {\n this.emit(\"tab-removed\", tab, this);\n }\n }\n\n activateRecentTab() {\n if (this.tabs.length > 0) {\n this.tabs[0].activate();\n }\n }\n}\n\nclass Tab extends EventTarget {\n badge: Badge;\n closable: boolean;\n element: HTMLDivElement;\n icon: string;\n iconURL: string;\n id: number;\n isClosed: boolean;\n isReady: boolean;\n spans: { [key: string]: HTMLSpanElement };\n tabGroup: TabGroup;\n title: string;\n webview: HTMLElement;\n webviewAttributes: { [key: string]: any };\n\n constructor(tabGroup: TabGroup, id: number, args: TabOptions) {\n super();\n this.badge = args.badge;\n this.closable = args.closable === false ? false : true;\n this.icon = args.icon;\n this.iconURL = args.iconURL;\n this.id = id;\n this.isClosed = false;\n this.isReady = false;\n this.spans = {};\n this.tabGroup = tabGroup;\n this.title = args.title;\n this.webviewAttributes = args.webviewAttributes || {};\n this.webviewAttributes.src = args.src;\n\n this.initTab();\n this.initWebview();\n\n if (args.visible !== false) {\n this.show();\n }\n if (typeof args.ready === \"function\") {\n args.ready(this);\n } else {\n this.emit(\"ready\", this);\n }\n }\n\n emit(type: string, ...args: any[]) {\n return emit(this, type, args);\n }\n\n on(type: string, fn: (...detail: any[]) => void) {\n return on(this, type, fn);\n }\n\n once(type: string, fn: (detail: string) => void) {\n return on(this, type, fn, { once: true });\n }\n\n private initTab() {\n const tab = this.element = document.createElement(\"div\");\n tab.classList.add(CLASSNAMES.TAB);\n for (let el of [\"icon\", \"title\", \"badge\", \"close\"]) {\n const span = tab.appendChild(document.createElement(\"span\"));\n span.classList.add(`${CLASSNAMES.TAB}-${el}`);\n this.spans[el] = span;\n }\n\n this.setTitle(this.title);\n this.setBadge(this.badge);\n this.setIcon(this.iconURL, this.icon);\n this.initTabCloseButton();\n this.initTabClickHandler();\n\n this.tabGroup.tabContainer.appendChild(this.element);\n }\n\n private initTabCloseButton() {\n const container = this.spans.close;\n if (this.closable) {\n const button = container.appendChild(document.createElement(\"button\"));\n button.innerHTML = this.tabGroup.options.closeButtonText;\n button.addEventListener(\"click\", this.close.bind(this, false), false);\n }\n }\n\n private initTabClickHandler() {\n // Mouse up\n const tabClickHandler = function(e: KeyboardEvent) {\n if (this.isClosed) return;\n if (e.which === 2) {\n this.close();\n }\n };\n this.element.addEventListener(\"mouseup\", tabClickHandler.bind(this), false);\n // Mouse down\n const tabMouseDownHandler = function(e: KeyboardEvent) {\n if (this.isClosed) return;\n if (e.which === 1) {\n if ((e.target as HTMLElement).matches(\"button\")) return;\n this.activate();\n }\n };\n this.element.addEventListener(\"mousedown\", tabMouseDownHandler.bind(this), false);\n }\n\n initWebview() {\n const webview = this.webview = document.createElement(\"webview\");\n\n const tabWebviewDidFinishLoadHandler = function(e: Event) {\n this.emit(\"webview-ready\", this);\n };\n\n this.webview.addEventListener(\"did-finish-load\", tabWebviewDidFinishLoadHandler.bind(this), false);\n\n const tabWebviewDomReadyHandler = function(e: Event) {\n // Remove this once https://github.com/electron/electron/issues/14474 is fixed\n webview.blur();\n webview.focus();\n this.emit(\"webview-dom-ready\", this);\n };\n\n this.webview.addEventListener(\"dom-ready\", tabWebviewDomReadyHandler.bind(this), false);\n\n this.webview.classList.add(CLASSNAMES.VIEW);\n if (this.webviewAttributes) {\n const attrs = this.webviewAttributes;\n for (let key in attrs) {\n const attr = attrs[key];\n if (attr === false) continue;\n this.webview.setAttribute(key, attr);\n }\n }\n\n this.tabGroup.viewContainer.appendChild(this.webview);\n }\n\n setTitle(title: string) {\n if (this.isClosed) return;\n const span = this.spans.title;\n span.innerHTML = title;\n span.title = title;\n this.title = title;\n this.emit(\"title-changed\", title, this);\n return this;\n }\n\n getTitle() {\n if (this.isClosed) return;\n return this.title;\n }\n\n setBadge(badge?: Badge) {\n if (this.isClosed) return;\n const span = this.spans.badge;\n this.badge = badge;\n\n if (badge) {\n span.innerHTML = badge.text;\n span.classList.add(badge.classname);\n span.classList.remove(\"hidden\");\n } else {\n span.classList.add(\"hidden\");\n }\n\n this.emit(\"badge-changed\", badge, this);\n }\n\n getBadge() {\n if (this.isClosed) return;\n return this.badge;\n }\n\n setIcon(iconURL: string, icon: string) {\n if (this.isClosed) return;\n this.iconURL = iconURL;\n this.icon = icon;\n const span = this.spans.icon;\n if (iconURL) {\n span.innerHTML = `<img src=\"${iconURL}\" />`;\n this.emit(\"icon-changed\", iconURL, this);\n } else if (icon) {\n span.innerHTML = `<i class=\"${icon}\"></i>`;\n this.emit(\"icon-changed\", icon, this);\n }\n\n return this;\n }\n\n getIcon() {\n if (this.isClosed) return;\n if (this.iconURL) return this.iconURL;\n return this.icon;\n }\n\n setPosition(newPosition: number) {\n const tabContainer = this.tabGroup.tabContainer;\n const length = tabContainer.childElementCount;\n const thisPosition = this.getPosition();\n const tabs = Array.from(tabContainer.children)\n tabs.splice(thisPosition, 1);\n\n if (newPosition < 0) {\n newPosition += length;\n if (newPosition < 0) {\n newPosition = 0;\n }\n }\n\n if (newPosition < length) {\n tabContainer.insertBefore(this.element, tabs[newPosition]);\n } else {\n tabContainer.appendChild(this.element);\n }\n\n return this;\n }\n\n getPosition(fromRight = false) {\n let position = 0;\n let tab = this.element;\n while ((tab = tab.previousSibling as HTMLDivElement) != null) position++;\n\n if (fromRight === true) {\n position -= this.tabGroup.tabContainer.childElementCount;\n }\n\n return position;\n }\n\n activate() {\n if (this.isClosed) return;\n const activeTab = this.tabGroup.getActiveTab();\n if (activeTab) {\n activeTab.element.classList.remove(\"active\");\n activeTab.webview.classList.remove(\"visible\");\n activeTab.emit(\"inactive\", activeTab);\n }\n this.tabGroup.setActiveTab(this);\n this.element.classList.add(\"active\");\n this.webview.classList.add(\"visible\");\n this.webview.focus();\n this.emit(\"active\", this);\n return this;\n }\n\n show(flag = true) {\n if (this.isClosed) return;\n if (flag) {\n this.element.classList.add(\"visible\");\n this.emit(\"visible\", this);\n } else {\n this.element.classList.remove(\"visible\");\n this.emit(\"hidden\", this);\n }\n return this;\n }\n\n hide() {\n return this.show(false);\n }\n\n flash(flag = true) {\n if (this.isClosed) return;\n if (flag !== false) {\n this.element.classList.add(\"flash\");\n this.emit(\"flash\", this);\n } else {\n this.element.classList.remove(\"flash\");\n this.emit(\"unflash\", this);\n }\n return this;\n }\n\n unflash() {\n return this.flash(false);\n }\n\n hasClass(classname: string) {\n return this.element.classList.contains(classname);\n }\n\n close(force: boolean) {\n const abortController = new AbortController();\n const abort = () => abortController.abort();\n this.emit(\"closing\", this, abort);\n\n const abortSignal = abortController.signal;\n if (this.isClosed || (!this.closable && !force) || abortSignal.aborted) return;\n\n this.isClosed = true;\n const tabGroup = this.tabGroup;\n tabGroup.tabContainer.removeChild(this.element);\n tabGroup.viewContainer.removeChild(this.webview);\n const activeTab = this.tabGroup.getActiveTab();\n tabGroup.removeTab(this, true);\n\n this.emit(\"close\", this);\n\n if (activeTab.id === this.id) {\n tabGroup.activateRecentTab();\n }\n }\n}\n\ncustomElements.define(\"tab-group\", TabGroup);\n\nexport type { TabGroup, Tab };\n"],"names":[],"version":3,"file":"electron-tabs.d.ts.map","sourceRoot":"../"} |