1
0
mirror of https://github.com/garraflavatra/rolens.git synced 2025-07-13 12:14:06 +00:00
Files
rolens/frontend/src/components/tabbar.svelte

170 lines
3.6 KiB
Svelte
Raw Normal View History

2023-01-10 17:28:27 +01:00
<script>
2023-12-22 14:02:58 +01:00
import { createEventDispatcher, onMount } from 'svelte';
import { tweened } from 'svelte/motion';
2023-12-23 11:54:10 +01:00
import { cubicOut } from 'svelte/easing';
2023-01-23 20:47:43 +01:00
import Icon from './icon.svelte';
2023-01-10 17:28:27 +01:00
export let tabs = [];
2023-07-24 20:07:29 +02:00
export let selectedKey = '';
2023-01-23 20:47:43 +01:00
export let canAddTab = false;
2023-12-22 13:30:03 +01:00
export let compact = true;
2023-01-10 17:28:27 +01:00
const dispatch = createEventDispatcher();
2023-12-23 11:54:10 +01:00
const activeIndicatorLeft = tweened(0, { easing: cubicOut, duration: 400 });
const activeIndicatorRight = tweened(0, { easing: cubicOut, duration: 400 });
2023-12-22 14:02:58 +01:00
const liElements = {};
2023-06-23 14:33:39 +02:00
let navEl;
2023-01-10 17:28:27 +01:00
2023-12-22 14:04:38 +01:00
function select(tabKey) {
2023-01-10 17:28:27 +01:00
selectedKey = tabKey;
dispatch('select', tabKey);
}
2023-12-22 14:02:58 +01:00
2023-12-22 14:04:38 +01:00
function moveActiveIndicator(target = liElements[selectedKey]) {
2023-12-22 14:02:58 +01:00
if (!compact) {
return;
}
const navRect = navEl.getBoundingClientRect();
const itemRect = target.getBoundingClientRect();
$activeIndicatorLeft = itemRect.x - navRect.x;
$activeIndicatorRight = navRect.right - itemRect.right;
}
onMount(() => {
if (selectedKey) {
moveActiveIndicator(liElements[selectedKey]);
}
});
2023-01-10 17:28:27 +01:00
</script>
2023-12-23 11:54:10 +01:00
<svelte:window on:resize={() => moveActiveIndicator()} />
2023-12-22 13:30:03 +01:00
<nav class="tabs" class:compact bind:this={navEl}>
2023-01-10 17:28:27 +01:00
<ul>
{#each tabs as tab (tab.key)}
2023-12-22 14:02:58 +01:00
<li
class="tab"
class:active={tab.key === selectedKey}
class:closable={tab.closable}
bind:this={liElements[tab.key]}
on:mouseenter={event => moveActiveIndicator(event.target)}
on:mouseleave={() => moveActiveIndicator()}
>
2023-12-22 14:04:38 +01:00
<button class="tab" on:click={() => select(tab.key)}>
2023-02-18 15:41:53 +01:00
{#if tab.icon} <Icon name={tab.icon} /> {/if}
2023-06-23 14:33:39 +02:00
<span class="label">{tab.title}</span>
2023-02-18 15:41:53 +01:00
</button>
2023-12-22 13:30:03 +01:00
2023-01-23 20:47:43 +01:00
{#if tab.closable}
<button class="button-small" on:click={() => dispatch('closeTab', tab.key)}>
2023-01-23 20:47:43 +01:00
<Icon name="x" />
</button>
{/if}
2023-01-10 17:28:27 +01:00
</li>
{/each}
2023-01-23 20:47:43 +01:00
{#if canAddTab}
2023-12-22 13:30:03 +01:00
<li>
<button class="button-small" on:click={() => dispatch('addTab')}>
2023-01-23 20:47:43 +01:00
<Icon name="+" />
</button>
</li>
{/if}
2023-01-10 17:28:27 +01:00
</ul>
2023-12-22 14:02:58 +01:00
{#if compact}
<span
class="activeindicator"
style:left="{$activeIndicatorLeft}px"
style:right="{$activeIndicatorRight}px"
/>
{/if}
2023-01-10 17:28:27 +01:00
</nav>
<style>
2023-12-22 14:02:58 +01:00
nav {
position: relative;
}
2023-01-23 20:47:43 +01:00
ul {
2023-06-24 22:10:47 +02:00
overflow-x: auto;
2023-01-10 17:28:27 +01:00
display: flex;
list-style: none;
}
2023-12-22 13:30:03 +01:00
2023-01-23 20:47:43 +01:00
li {
2023-01-10 17:28:27 +01:00
display: inline-block;
2023-01-23 20:47:43 +01:00
position: relative;
}
2023-12-22 13:30:03 +01:00
nav.tabs :global(svg) {
2023-01-23 20:47:43 +01:00
vertical-align: bottom;
}
button.tab {
2023-01-10 17:28:27 +01:00
width: 100%;
padding: 0.7rem 1rem;
border: 1px solid #ccc;
border-right: none;
2023-01-10 17:28:27 +01:00
cursor: pointer;
2023-01-13 16:56:48 +01:00
background-color: #fff;
2023-01-10 17:28:27 +01:00
}
2023-01-23 20:47:43 +01:00
button.tab:hover {
background-color: #eee;
}
button.tab:active {
background-color: #ddd;
}
li:last-child button.tab {
border-right: 1px solid #ccc;
}
2023-01-23 20:47:43 +01:00
li.active button.tab {
2023-12-22 13:30:03 +01:00
background-color: var(--selection);
border-color: var(--primary);
2023-01-10 17:28:27 +01:00
cursor: not-allowed;
}
2023-06-23 21:27:55 +02:00
2023-12-22 13:30:03 +01:00
button.tab .label {
margin-top: 5px;
display: inline-block;
2023-06-23 14:33:39 +02:00
}
2023-01-23 20:47:43 +01:00
2023-12-22 13:30:03 +01:00
nav.tabs .button-small {
margin: 12px 0 0 4px;
}
li.closable .button-small {
display: none;
2023-01-23 20:47:43 +01:00
position: absolute;
2023-12-22 13:30:03 +01:00
top: 0;
2023-01-23 20:47:43 +01:00
right: 7px;
2023-12-22 13:30:03 +01:00
}
li.closable.active {
padding-right: 20px;
}
li.closable.active .button-small {
display: block;
}
nav.tabs.compact {
border-bottom: 1px solid #aaa;
}
nav.tabs.compact li {
border-bottom: 2px solid transparent;
}
nav.tabs.compact button.tab {
border: none;
color: inherit;
background-color: transparent;
}
2023-12-22 14:02:58 +01:00
.activeindicator {
display: block;
position: absolute;
bottom: -1px;
height: 2px;
background-color: var(--primary);
2023-01-23 20:47:43 +01:00
}
2023-01-10 17:28:27 +01:00
</style>