Files
next.orly.dev/app/web/src/Sidebar.svelte
mleku 8ef3114f5c Refactor project to modularize constants and utilities.
Moved reusable constants and helper functions to dedicated modules for improved maintainability and reusability. Improved build configuration to differentiate output directories for development and production. Enhanced server error handling and added safeguards for disabled web UI scenarios.
2025-12-05 19:25:13 +00:00

156 lines
3.4 KiB
Svelte

<script>
export let isDarkTheme = false;
export let tabs = [];
export let selectedTab = "";
import { createEventDispatcher } from "svelte";
const dispatch = createEventDispatcher();
function selectTab(tabId) {
dispatch("selectTab", tabId);
}
function closeSearchTab(tabId) {
dispatch("closeSearchTab", tabId);
}
</script>
<aside class="sidebar" class:dark-theme={isDarkTheme}>
<div class="sidebar-content">
<div class="tabs">
{#each tabs as tab}
<button
class="tab"
class:active={selectedTab === tab.id}
on:click={() => selectTab(tab.id)}
>
<span class="tab-icon">{tab.icon}</span>
<span class="tab-label">{tab.label}</span>
{#if tab.isSearchTab}
<span
class="tab-close-icon"
on:click|stopPropagation={() =>
closeSearchTab(tab.id)}
on:keydown={(e) =>
e.key === "Enter" && closeSearchTab(tab.id)}
role="button"
tabindex="0"></span
>
{/if}
</button>
{/each}
</div>
</div>
</aside>
<style>
.sidebar {
position: fixed;
left: 0;
top: 2.5em;
width: 200px;
bottom: 0;
background: var(--sidebar-bg);
overflow-y: auto;
z-index: 100;
}
.sidebar-content {
padding: 0;
background: var(--sidebar-bg);
}
.tabs {
display: flex;
flex-direction: column;
padding: 0;
}
.tab {
display: flex;
align-items: center;
padding: 0.75em;
padding-left: 1em;
background: transparent;
color: var(--text-color);
border: none;
cursor: pointer;
transition: background-color 0.2s;
gap: 0.75rem;
text-align: left;
width: 100%;
}
.tab:hover {
background-color: var(--bg-color);
}
.tab.active {
background-color: var(--bg-color);
}
.tab-icon {
font-size: 1.2em;
flex-shrink: 0;
width: 1.5em;
text-align: center;
}
.tab-label {
font-size: 0.9em;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1;
}
.tab-close-icon {
cursor: pointer;
transition: opacity 0.2s;
font-size: 0.8em;
margin-left: auto;
padding: 0.25rem;
flex-shrink: 0;
}
.tab-close-icon:hover {
opacity: 0.7;
background-color: var(--warning);
color: var(--text-color);
}
@media (max-width: 1280px) {
.sidebar {
width: 60px;
}
.tab-label {
display: none;
}
.tab-close-icon {
display: none;
}
.tab {
/* Keep left alignment so icons stay in same position */
justify-content: flex-start;
}
}
@media (max-width: 640px) {
.sidebar {
width: 160px;
}
.tab-label {
display: block;
}
.tab {
justify-content: flex-start;
}
}
</style>