Collapsable+resizable sidebar. Standard view header component.

This commit is contained in:
Shivering Ocelot committed 2025-07-20 21:44:20 -05:00
1 parent 69e8614a98
commit 9b30473c40
9 files changed
+202 -41

No files matched your search

+118 -26
View File
@@ -1,19 +1,40 @@
<script setup lang="ts">
import { RouterLink } from 'vue-router'
import {
ref, computed, reactive,
ref, computed, reactive, watch,
onMounted, onUnmounted,
} from "vue";
import * as backend from '../../backend';
import * as ui from '../../ui';
const collapsed = ref(false);
const mode = ref('chats');
const chats = ref([]);
const agents = ref([]);
const mode = ref('chats');
const sidebarExpanded = ref(true);
const MIN_WIDTH = 150;
const MAX_WIDTH = 400;
const sidebarWidth = ref(200);
let dragState = null;
watch(sidebarExpanded, newValue => {
if (newValue) {
document.body.classList.add('with-sidebar-expanded');
} else {
document.body.classList.remove('with-sidebar-expanded');
}
}, {immediate: true});
ui.listen('sidebar-expand', ev => {
sidebarExpanded.value = true;
});
onMounted(async () => {
document.body.addEventListener('mousemove', documentMouseMove);
document.body.addEventListener('mouseup', documentMouseUp);
chats.value = (await backend.getChats()).map(it => {
return {id: it.id, name: it.name};
});
@@ -21,23 +42,61 @@
return {id: it.id, name: it.name};
});
});
onUnmounted(() => {
document.body.removeEventListener('mousemove', documentMouseMove);
document.body.removeEventListener('mouseup', documentMouseUp);
});
function dragBarMouseDown (event) {
dragState = {x: event.clientX, y: event.clientY, width: sidebarWidth.value};
event.preventDefault();
event.stopPropagation();
console.log("Set Drag State", dragState);
}
function documentMouseMove (event) {
if (dragState) {
let widthDiff = event.clientX - dragState.x;
let newWidth = dragState.width + widthDiff;
newWidth = Math.min(MAX_WIDTH, Math.max(MIN_WIDTH, newWidth));
sidebarWidth.value = newWidth;
}
}
function documentMouseUp (event) {
if (dragState) {
dragState = null;
}
}
function sidebarStyle () {
return {
flex: `0 0 ${sidebarWidth.value}px`
}
}
</script>
<template>
<button class="nav-button expand-navbar" v-if="collapsed" @click="collapsed=false;">
</button>
<aside class="sidebar" style="min-width:8em" v-else>
<div class="drag-bar"></div>
<ul class="tab-bar">
<li @click="mode='chats'" :class="{active: mode=='chats'}">
<i class="fa-solid fa-message"></i>
</li>
<li @click="mode='agents'" :class="{active: mode=='agents'}">
<i class="fa-solid fa-user-group"></i>
</li>
<li><i class="fa-solid fa-angles-left"></i></li>
</ul>
<aside class="sidebar" :style="sidebarStyle()" v-if="sidebarExpanded">
<div class="drag-bar" @mousedown="dragBarMouseDown($event)"></div>
<div class="tab-bar">
<div class="tab-group">
<div @click="mode='chats'" class="tab" :class="{active: mode=='chats'}">
<i class="fa-solid fa-message"></i>
</div>
<div @click="mode='agents'" class="tab" :class="{active: mode=='agents'}">
<i class="fa-solid fa-user-group"></i>
</div>
</div>
<div class="tab-group">
<button class="header-button">
<i class="fa-solid fa-angles-left" @click="sidebarExpanded=false;"></i>
</button>
</div>
</div>
<nav v-if="mode=='chats'">
<RouterLink v-for="it in chats" :to="'/chat/' + it.id" class="nav-item">{{it.name}}</RouterLink>
<RouterLink to="/chat/create" class="nav-item">Create Chat</RouterLink>
@@ -54,33 +113,43 @@
display: flex;
flex-direction: column;
position: relative;
z-index: var(--z-index-sidebar);
background-color: var(--color-background);
}
.tab-bar {
.tab-bar, .tab-group {
display: flex;
flex-direction: row;
justify-content: space-around;
padding: 0;
margin: 0;
}
.tab-bar {
justify-content: space-between;
}
.tab-bar li {
.tab {
display: flex;
justify-content: space-around;
align-items: center;
list-style-type: none;
border-radius: var(--border-radius);
width: 3em;
height: 3em;
transition: background-color 0.5s;
width: 3em;
padding-top: 3px;
color: var(--color-text-mute);
/*flex-grow: 1;*/
}
.tab-bar li:hover {
.tab:hover {
background-color: var(--color-background-mute);
}
.tab-bar li.active {
background-color: var(--color-background-soft);
.tab.active {
color: var(--color-text-heading);
background-color: var(--color-background);
padding-top: 0;
border-top: 3px solid var(--color-active);
}
.nav-item {
@@ -118,4 +187,27 @@
right: 0;
cursor: ew-resize;
}
/* Adapt sidebar to small screens */
@media (max-width: 480px) {
.sidebar {
position: absolute;
width: 100%;
height: 100%;
}
.drag-bar {
display: none;
}
}
.header-button {
height: 3em;
width: 3em;
border-radius: 1.5em;
background-color: none;
border: none;
}
.header-button:hover {
background-color: var(--color-background-hover);
}
</style>