feat: add better provider support, icons, regen, and a lot more
This commit is contained in:
@@ -6,8 +6,34 @@ const isResizing = ref(false);
|
||||
const startX = ref(0);
|
||||
const initialWidth = ref(0);
|
||||
|
||||
const isMouseOver = ref(false);
|
||||
const isFocused = ref(false);
|
||||
const lastInteraction = ref<'mouse' | 'keyboard' | null>(null);
|
||||
|
||||
const isHovering = computed(() =>
|
||||
isMouseOver.value || (isFocused.value && lastInteraction.value === 'keyboard')
|
||||
);
|
||||
const sidenavRef = ref<HTMLElement | null>(null);
|
||||
const closeSidenavRef = ref<HTMLElement | null>(null);
|
||||
|
||||
provideSidenavContext({
|
||||
isHovered: isHovering,
|
||||
sidebarWidth: sidebarWidth,
|
||||
isOpen: open,
|
||||
close: () => {
|
||||
closeSidebar();
|
||||
},
|
||||
});
|
||||
|
||||
const trackInteraction = (interaction: 'mouse' | 'keyboard') => {
|
||||
lastInteraction.value = interaction;
|
||||
};
|
||||
|
||||
const onFocusOut = (e: FocusEvent) => {
|
||||
const isMovingOutside = sidenavRef.value && !sidenavRef.value.contains(e.relatedTarget as Node);
|
||||
if (isMovingOutside) {
|
||||
isFocused.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const { toggle: toggleSettings } = useSettings();
|
||||
|
||||
@@ -46,63 +72,55 @@ onMounted(() => {
|
||||
document.addEventListener('mousemove', onResizeMove);
|
||||
document.addEventListener('mouseup', onResizeEnd);
|
||||
|
||||
watch(hovering, (value) => {
|
||||
if (!closeSidenavRef.value) return;
|
||||
|
||||
if (value) {
|
||||
const width = closeSidenavRef.value.scrollWidth;
|
||||
closeSidenavRef.value.style.width = `${width}px`;
|
||||
} else {
|
||||
closeSidenavRef.value.style.width = '0';
|
||||
}
|
||||
// NEW: Track global interactions
|
||||
document.addEventListener('mousedown', () => trackInteraction('mouse'));
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Tab') trackInteraction('keyboard');
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('mousemove', onResizeMove);
|
||||
document.removeEventListener('mouseup', onResizeEnd);
|
||||
});
|
||||
|
||||
const hovering = ref(false);
|
||||
// NEW: Cleanup listeners
|
||||
document.removeEventListener('mousedown', () => trackInteraction('mouse'));
|
||||
document.removeEventListener('keydown', (e) => {
|
||||
if (e.key === 'Tab') trackInteraction('keyboard');
|
||||
});
|
||||
});
|
||||
|
||||
const navKind = computed(() => {
|
||||
if (route.path === '/') return 'home';
|
||||
if (route.path.startsWith('/agent/')) return 'agent';
|
||||
return null;
|
||||
});
|
||||
|
||||
const onFocusOut = (e: FocusEvent) => {
|
||||
const isMovingOutside = sidenavRef.value && !sidenavRef.value.contains(e.relatedTarget as Node);
|
||||
if (isMovingOutside) {
|
||||
hovering.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative">
|
||||
<aside ref="sidenavRef" :class="[
|
||||
'h-full max-w-fit bg-[var(--color-base)] overflow-hidden will-change-width text-[var(--color-muted)] select-none',
|
||||
open ? 'w-full mr-2' : 'w-0 mr-0',
|
||||
isResizing ? '' : 'transition-[width,margin] duration-250 ease-[cubic-bezier(0,0.55,0.45,1)]'
|
||||
]" :style="open ? { width: `${sidebarWidth}px` } : {}" @mouseenter="hovering = true"
|
||||
@mouseleave="hovering = false" @focusin="hovering = true" @focusout="onFocusOut">
|
||||
'sidenav',
|
||||
open ? 'sidenav--open' : 'sidenav--closed',
|
||||
isResizing ? 'sidenav--resizing' : ''
|
||||
]" :style="open ? { width: `${sidebarWidth}px` } : {}" @mouseenter="isMouseOver = true"
|
||||
@mouseleave="isMouseOver = false" @focusin="isFocused = true" @focusout="onFocusOut">
|
||||
<div :style="{ minWidth: `${sidebarWidth}px` }" class="flex flex-col h-full justify-between">
|
||||
<div class="flex flex-col h-full max-h-full overflow-y-hidden">
|
||||
<!-- Header -->
|
||||
<div class="relative flex flex-row gap-2 justify-between items-center mb-1.5">
|
||||
|
||||
<SidenavHeader v-if="navKind === 'home'" v-model="hovering" />
|
||||
<SidenavHeaderAgent v-else-if="navKind === 'agent'" v-model="hovering" />
|
||||
<SidenavHeader v-if="navKind === 'home'" />
|
||||
<SidenavHeaderAgent v-else-if="navKind === 'agent'" />
|
||||
|
||||
<div class="flex items-center justify-end text-[var(--color-muted)] gap-0.5">
|
||||
<div ref="closeSidenavRef" style="width: 0;"
|
||||
<div :style="isHovering ? 'width: 32px;' : 'width: 0px;'"
|
||||
:class="['flex-shrink-0 overflow-hidden rounded-lg transition-all duration-150 ease-[cubic-bezier(0.5,_1,_0.89,_1)] transform-origin-center-right']">
|
||||
<button aria-label="close sidebar" @click="closeSidebar" :class="[
|
||||
'flex text-5 h-8 w-8 items-center justify-center hover:bg-[var(--color-highlight)] focus-visible:bg-[var(--color-highlight)] scale-100 bg-transparent transition-inherit',
|
||||
'flex text-5 h-8 w-8 items-center justify-center hover:bg-[var(--color-highlight)] focus-visible:bg-[var(--color-highlight)] bg-transparent transition-inherit',
|
||||
]">
|
||||
<Icon name="mynaui:panel-left-close"
|
||||
:class="['transition-inherit', hovering ? 'opacity-100 scale-100' : 'opacity-0 scale-95']" />
|
||||
:class="['transition-inherit transform-origin-right-center', isHovering ? 'opacity-100 scale-100' : 'opacity-0 scale-95']" />
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="navKind === 'agent'" class="flex-shrink-0 overflow-hidden rounded-lg">
|
||||
@@ -143,3 +161,31 @@ const onFocusOut = (e: FocusEvent) => {
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.sidenav {
|
||||
height: 100%;
|
||||
max-width: fit-content;
|
||||
background: var(--color-base);
|
||||
overflow: hidden;
|
||||
will-change: width;
|
||||
color: var(--color-muted);
|
||||
user-select: none;
|
||||
transition: width 250ms cubic-bezier(0, 0.55, 0.45, 1),
|
||||
margin 250ms cubic-bezier(0, 0.55, 0.45, 1);
|
||||
}
|
||||
|
||||
.sidenav--open {
|
||||
width: 100%;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.sidenav--closed {
|
||||
width: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.sidenav--resizing {
|
||||
transition: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user