213 lines
4.9 KiB
Vue
213 lines
4.9 KiB
Vue
<script setup lang="ts">
|
|
import { RouterLink } from 'vue-router'
|
|
import {
|
|
ref, computed, reactive, watch,
|
|
onMounted, onUnmounted,
|
|
} from "vue";
|
|
|
|
import * as backend from '../../backend';
|
|
import * as ui from '../../ui';
|
|
|
|
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};
|
|
});
|
|
agents.value = (await backend.getAgents()).map(it => {
|
|
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>
|
|
<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>
|
|
</nav>
|
|
<nav v-if="mode=='agents'">
|
|
<RouterLink v-for="it in agents" :to="'/agent/' + it.id" class="nav-item">{{it.name}}</RouterLink>
|
|
<RouterLink to="/agent/create" class="nav-item">Create Agent</RouterLink>
|
|
</nav>
|
|
</aside>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.sidebar {
|
|
display: flex;
|
|
flex-direction: column;
|
|
position: relative;
|
|
z-index: var(--z-index-sidebar);
|
|
background-color: var(--color-background);
|
|
}
|
|
|
|
.tab-bar, .tab-group {
|
|
display: flex;
|
|
flex-direction: row;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
.tab-bar {
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.tab {
|
|
display: flex;
|
|
justify-content: space-around;
|
|
align-items: center;
|
|
list-style-type: none;
|
|
height: 3em;
|
|
width: 3em;
|
|
|
|
padding-top: 3px;
|
|
color: var(--color-text-mute);
|
|
/*flex-grow: 1;*/
|
|
}
|
|
|
|
.tab:hover {
|
|
background-color: var(--color-background-mute);
|
|
}
|
|
|
|
.tab.active {
|
|
color: var(--color-text-heading);
|
|
background-color: var(--color-background);
|
|
|
|
padding-top: 0;
|
|
border-top: 3px solid var(--color-active);
|
|
}
|
|
|
|
.nav-item {
|
|
display: block;
|
|
|
|
color: inherit;
|
|
background-color: transparent;
|
|
border: none;
|
|
|
|
border-radius: 0;
|
|
padding: 0.2em 0.4em;
|
|
width: 100%;
|
|
|
|
text-align: left;
|
|
vertical-align: middle;
|
|
|
|
font-size: inherit;
|
|
}
|
|
|
|
.nav-item:hover {
|
|
background-color: rgba(255, 255, 255, 0.1);
|
|
}
|
|
|
|
.nav-item i {
|
|
font-size: 80%;
|
|
padding-left: 0.5em;
|
|
padding-right: 0.5em;
|
|
}
|
|
|
|
.drag-bar {
|
|
height: 100%;
|
|
border-right: 1px solid var(--color-border);
|
|
width: 0.5em;
|
|
position: absolute;
|
|
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> |