Collapsable+resizable sidebar. Standard view header component.
This commit is contained in:
1 parent
69e8614a98
commit
9b30473c40
9 files changed
+202
-41
No files matched your search
@@ -0,0 +1,36 @@
|
||||
<script setup>
|
||||
import * as ui from '../ui';
|
||||
|
||||
function sidebarExpand () {
|
||||
ui.emit('sidebar-expand');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header>
|
||||
<button class="when-sidebar-collapsed" @click="sidebarExpand()">
|
||||
>>
|
||||
</button>
|
||||
|
||||
<slot>
|
||||
<h1>Title Bar</h1>
|
||||
</slot>
|
||||
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
header {
|
||||
height: 3em;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:slotted(h1) {
|
||||
font-size: 160%;
|
||||
font-weight: bold;
|
||||
padding: 0 0.4em;
|
||||
}
|
||||
</style>
|
||||
@@ -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>
|
||||
Reference in new issue
Block a user