mirror of
https://github.com/He4eT/tabswiper.git
synced 2026-05-05 00:57:23 +00:00
Prototype
This commit is contained in:
parent
9306068d2a
commit
88b35a75cb
4 changed files with 122 additions and 66 deletions
|
|
@ -6,8 +6,10 @@ html, body {
|
||||||
|
|
||||||
.popup {
|
.popup {
|
||||||
--color-bg: #eeeeee;
|
--color-bg: #eeeeee;
|
||||||
|
--color-bg: #ffffff;
|
||||||
--color-text: #333333;
|
--color-text: #333333;
|
||||||
--color-accent: #666666;
|
--color-accent: #666666;
|
||||||
|
--color-accent: #777777;
|
||||||
|
|
||||||
--step: 8px;
|
--step: 8px;
|
||||||
}
|
}
|
||||||
|
|
@ -32,26 +34,15 @@ html, body {
|
||||||
/* Button */
|
/* Button */
|
||||||
|
|
||||||
button {
|
button {
|
||||||
background: var(--color-bg);
|
border: none;
|
||||||
|
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
border: 1px solid var(--color-text);
|
background: var(--color-bg);
|
||||||
|
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font: inherit;
|
font: inherit;
|
||||||
|
|
||||||
padding: var(--step);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
button:active {
|
button:active {
|
||||||
border-color: var(--color-accent);
|
border-color: var(--color-accent);
|
||||||
color: var(--color-accent);
|
color: var(--color-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Link */
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: var(--color-accent);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -3,50 +3,62 @@
|
||||||
border: 1px solid var(--color-accent);
|
border: 1px solid var(--color-accent);
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
width: 640px;
|
width: 640px;
|
||||||
|
padding: calc(2 * var(--step));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Header */
|
/* Header */
|
||||||
|
|
||||||
.tabswiper header {
|
.tabswiper header {
|
||||||
padding: var(--step);
|
padding-block-end: calc(2 * var(--step));
|
||||||
border-block-end: 1px solid var(--color-accent);
|
/* border-block-end: 1px solid var(--color-accent); */
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
aligh-items: center;
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabswiper .favicon {
|
||||||
|
width: calc(3 * var(--step));
|
||||||
|
height: calc(3 * var(--step));
|
||||||
|
margin-inline-end: calc(1 * var(--step));
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabswiper .tabCounter {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabswiper .actions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: var(--step);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Current Tab */
|
/* Current Tab */
|
||||||
|
|
||||||
.tabswiper .currentTab {
|
.tabswiper .currentTab {
|
||||||
padding: calc(1 * var(--step)) var(--step);
|
/* padding-block-start: calc(2 * var(--step)); */
|
||||||
|
/* padding-block-end: calc(3 * var(--step)); */
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
/* line-height: 1.5; */
|
/* line-height: 1.5; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabswiper .currentTab .title {
|
.tabswiper .currentTab .title {
|
||||||
/* font-size: 1.2em; */
|
font-size: 1.25em;
|
||||||
margin-block-end: calc(1 * var(--step));
|
margin-block-end: calc(1 * var(--step));
|
||||||
font-weight: bold;
|
/* font-weight: bold; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabswiper .currentTab .url {
|
.tabswiper .currentTab .url {
|
||||||
/* font-size: 0.8em; */
|
/* font-size: 0.8em; */
|
||||||
color: var(--color-accent);
|
color: var(--color-accent);
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Footer */
|
/* Footer */
|
||||||
|
|
||||||
.tabswiper footer {
|
|
||||||
padding: var(--step);
|
|
||||||
border-block-start: 1px solid var(--color-accent);
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
gap: var(--step);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabswiper footer .actionButton {
|
.tabswiper footer .actionButton {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
|
|
||||||
|
|
@ -12,16 +12,32 @@
|
||||||
<article class='tabswiper'>
|
<article class='tabswiper'>
|
||||||
<header>
|
<header>
|
||||||
<section class='tabCounter'>
|
<section class='tabCounter'>
|
||||||
Tab 1 of 20
|
<img id='favicon' class='favicon' alt='Favicon' />
|
||||||
|
Tab <span id='tabNumber'>
|
||||||
|
0
|
||||||
|
</span> of <span id='tabTotalNumber'>
|
||||||
|
0
|
||||||
|
</span>
|
||||||
|
</section>
|
||||||
|
<section class='actions'>
|
||||||
|
<button
|
||||||
|
id='buttonClose'
|
||||||
|
class='actionButton close'
|
||||||
|
title='Close this tab. Shortcuts: [J], [Left Arrow]'
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
id='buttonKeep'
|
||||||
|
class='actionButton keep'
|
||||||
|
title='Keep this tab. Shortcuts: [K], [Right Arrow]'
|
||||||
|
>
|
||||||
|
Keep
|
||||||
|
</button>
|
||||||
</section>
|
</section>
|
||||||
<a
|
|
||||||
class='infoLink'
|
|
||||||
target='_blank'
|
|
||||||
href='#'
|
|
||||||
>
|
|
||||||
Info
|
|
||||||
</a>
|
|
||||||
</header>
|
</header>
|
||||||
|
<footer>
|
||||||
|
</footer>
|
||||||
<section class='currentTab'>
|
<section class='currentTab'>
|
||||||
<section id='titleTab' class='title'>
|
<section id='titleTab' class='title'>
|
||||||
Some title here
|
Some title here
|
||||||
|
|
@ -34,22 +50,6 @@
|
||||||
https://some.url/here
|
https://some.url/here
|
||||||
</a>
|
</a>
|
||||||
</section>
|
</section>
|
||||||
<footer>
|
|
||||||
<button
|
|
||||||
id='buttonClose'
|
|
||||||
class='actionButton close'
|
|
||||||
title='Close this tab. Shortcuts: [J], [Left Arrow]'
|
|
||||||
>
|
|
||||||
Close
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
id='buttonKeep'
|
|
||||||
class='actionButton keep'
|
|
||||||
title='Keep this tab. Shortcuts: [K], [Right Arrow]'
|
|
||||||
>
|
|
||||||
Keep
|
|
||||||
</button>
|
|
||||||
</footer>
|
|
||||||
</article>
|
</article>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
/* https://png-pixel.com/ */
|
||||||
|
const defaultFavicon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkqAcAAIUAgUW0RjgAAAAASUVORK5CYII='
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
|
|
@ -6,22 +9,28 @@ const state = {
|
||||||
currentTab: null,
|
currentTab: null,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getCurrentState = () => state
|
||||||
|
|
||||||
|
/* */
|
||||||
|
|
||||||
const updateState = () =>
|
const updateState = () =>
|
||||||
updateTabs()
|
updateTabs()
|
||||||
.then(updateCurrent)
|
.then(updateCurrent)
|
||||||
.then(updateInterface)
|
.then(updateInterface)
|
||||||
|
|
||||||
const updateTabs = () =>
|
const updateTabs = () =>
|
||||||
browser.tabs.query({currentWindow: true})
|
browser.tabs.query({ currentWindow: true })
|
||||||
.then((tabs) => tabs.reverse())
|
.then((tabs) => tabs.reverse())
|
||||||
.then((tabs) => void (state.tabs = tabs))
|
.then((tabs) => void (state.tabs = tabs))
|
||||||
|
|
||||||
const updateCurrent = () => {
|
const updateCurrent = () => {
|
||||||
const filteredTabs = state.tabs
|
const untouchedTabs = state.tabs
|
||||||
.filter(({ id }) => state.skipped.includes(id) === false)
|
.filter(({ id }) => state.skipped.includes(id) === false)
|
||||||
state.currentTab = filteredTabs[0] ?? null
|
state.currentTab = untouchedTabs[0] ?? null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* */
|
||||||
|
|
||||||
const keepTab = (tab) => {
|
const keepTab = (tab) => {
|
||||||
state.skipped.push(tab.id)
|
state.skipped.push(tab.id)
|
||||||
updateState()
|
updateState()
|
||||||
|
|
@ -29,44 +38,86 @@ const keepTab = (tab) => {
|
||||||
|
|
||||||
const goToTab = (tab) => {
|
const goToTab = (tab) => {
|
||||||
browser.tabs.update(tab.id, { active: true })
|
browser.tabs.update(tab.id, { active: true })
|
||||||
updateState()
|
.then(updateState)
|
||||||
}
|
}
|
||||||
|
|
||||||
const closeTab = (tab) => {
|
const closeTab = (tab) => {
|
||||||
browser.tabs.remove(tab.id)
|
browser.tabs.remove(tab.id)
|
||||||
updateState()
|
.then(updateState)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
const setDOMListeners = () => {
|
const setDOMListeners = (getCurrentState) => {
|
||||||
const pairs = [
|
/* UI controls with handlers. */
|
||||||
|
|
||||||
|
const controls = [
|
||||||
['buttonClose', closeTab],
|
['buttonClose', closeTab],
|
||||||
['buttonKeep', keepTab],
|
['buttonKeep', keepTab],
|
||||||
['linkTab', goToTab],
|
['linkTab', goToTab],
|
||||||
]
|
]
|
||||||
|
|
||||||
pairs.forEach(([elementId, handler]) => {
|
controls.forEach(([elementId, handler]) => {
|
||||||
document.getElementById(elementId).addEventListener('click', (e) => {
|
document.getElementById(elementId).addEventListener('click', (e) => {
|
||||||
console.log('Element:', elementId, 'Handler:', handler)
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
handler(state.currentTab)
|
handler(getCurrentState().currentTab)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/* Keyboard handlers. */
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
switch (e.key) {
|
||||||
|
case 'j':
|
||||||
|
case 'ArrowLeft':
|
||||||
|
closeTab(getCurrentState().currentTab)
|
||||||
|
return
|
||||||
|
case 'k':
|
||||||
|
case 'ArrowRight':
|
||||||
|
keepTab(getCurrentState().currentTab)
|
||||||
|
return
|
||||||
|
case 'f':
|
||||||
|
goToTab(getCurrentState().currentTab)
|
||||||
|
return
|
||||||
|
case 'r':
|
||||||
|
location.reload()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
/* Replace unavalible favicon with default one. */
|
||||||
|
document.getElementById('favicon').addEventListener('error', (e) => {
|
||||||
|
e.currentTarget.src = defaultFavicon
|
||||||
|
})
|
||||||
|
|
||||||
|
/* Close popup when tab switched. */
|
||||||
|
browser.tabs.onActivated.addListener(() => {
|
||||||
|
window.close()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateInterface = () => {
|
const updateInterface = () => {
|
||||||
console.log(state)
|
/* Close popup if there ara no tabs. */
|
||||||
|
if (state.tabs.length === 0) {
|
||||||
|
window.close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start over when all tabs skipped. */
|
||||||
if (state.currentTab === null) {
|
if (state.currentTab === null) {
|
||||||
location.reload()
|
location.reload()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Update UI. */
|
||||||
const items = [
|
const items = [
|
||||||
|
['favicon', 'src', state.currentTab.favIconUrl ?? defaultFavicon],
|
||||||
['titleTab', 'textContent', state.currentTab.title],
|
['titleTab', 'textContent', state.currentTab.title],
|
||||||
['linkTab', 'textContent', state.currentTab.url],
|
['linkTab', 'textContent', state.currentTab.url],
|
||||||
['linkTab', 'href', state.currentTab.url],
|
['linkTab', 'href', state.currentTab.url],
|
||||||
|
['tabTotalNumber', 'textContent', state.tabs.length],
|
||||||
|
['tabNumber', 'textContent', state.tabs.length - state.tabs.findIndex(({ id }) =>
|
||||||
|
id === state.currentTab.id
|
||||||
|
)],
|
||||||
]
|
]
|
||||||
|
|
||||||
items.forEach(([elementId, property, value]) => {
|
items.forEach(([elementId, property, value]) => {
|
||||||
|
|
@ -76,10 +127,12 @@ const updateInterface = () => {
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
const init = () =>
|
// const state = initState({onUpdate: updateInterface})
|
||||||
|
// setDomListeners(state)
|
||||||
|
|
||||||
|
const init = () => {
|
||||||
updateState()
|
updateState()
|
||||||
.then(setDOMListeners)
|
setDOMListeners(getCurrentState)
|
||||||
// .then(setKeyboardListeners)
|
}
|
||||||
// .then(setBrowserListeners)
|
|
||||||
|
|
||||||
init()
|
init()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue