Prototype

This commit is contained in:
He4eT 2023-08-16 04:01:43 +03:00
commit 88b35a75cb
4 changed files with 122 additions and 66 deletions

View file

@ -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);
}

View file

@ -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;

View file

@ -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&nbsp;<span id='tabNumber'>
0
</span>&nbsp;of&nbsp;<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>

View file

@ -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()