Skip to content

Commit f2d1a79

Browse files
committed
Refresh the dashboard layout
1 parent 29f7bc6 commit f2d1a79

File tree

1 file changed

+105
-205
lines changed

1 file changed

+105
-205
lines changed

layouts/dashboard.vue

Lines changed: 105 additions & 205 deletions
Original file line numberDiff line numberDiff line change
@@ -1,233 +1,125 @@
1-
<script lang="ts">
2-
import { CalendarIcon, Cog8ToothIcon, DocumentPlusIcon, MoonIcon, SunIcon, UserCircleIcon, XMarkIcon } from '@heroicons/vue/24/solid'
3-
import { computed, defineComponent, onMounted, onUnmounted } from 'vue'
4-
import GraphIcon from '#root/assets/graph.svg?component'
5-
import LogoIcon from '#root/assets/logo-icon.svg?component'
1+
<script lang="ts" setup>
2+
import { IconDotsVertical, IconMenu2, IconMoon, IconPencilPlus, IconSun, IconSunMoon } from '@tabler/icons-vue'
3+
import { computed, onMounted, onUnmounted } from 'vue'
64
import CoreButton from '#root/components/CoreButton.vue'
75
import CoreDivider from '#root/components/CoreDivider.vue'
86
import CoreLink from '#root/components/CoreLink.vue'
9-
import Key from '#root/components/Key.vue'
107
import TheLeftSidebar from '#root/pages/menu.vue'
118
import TheRightSidebar from '#root/pages/docs/[docId]/meta.vue'
129
import { bindGlobal } from '#root/src/common/keybindings'
1310
14-
export default defineComponent({
15-
components: {
16-
CalendarIcon,
17-
Cog8ToothIcon,
18-
CoreButton,
19-
CoreDivider,
20-
CoreLink,
21-
DocumentPlusIcon,
22-
GraphIcon,
23-
Key,
24-
LogoIcon,
25-
MoonIcon,
26-
SunIcon,
27-
TheLeftSidebar,
28-
TheRightSidebar,
29-
UserCircleIcon,
30-
XMarkIcon,
31-
},
32-
setup() {
33-
const { isNuxt } = useIsNuxt()
34-
const router = useRouter()
35-
const { doc } = useDocs()
36-
const { showMenu, showMeta, toggleMenu, toggleMeta } = useLayout()
37-
const { pinnedDocs, unpinDoc } = usePinnedDocs()
38-
const { isDesktop, isMobile, modKey } = useDevice()
39-
const isDoc = computed(() => router.currentRoute.value.name === 'docs-docId')
40-
const isNew = computed(() => router.currentRoute.value.path === '/docs/new')
41-
const { store: appearance, isAuto, isDark, isLight } = useAppearance()
11+
const { doc } = useDocs()
12+
const { isZen, toggleZen } = useZen()
13+
const { showMenu, showMeta, toggleMenu, toggleMeta } = useLayout()
14+
const { store: appearance, isAuto, isDark, isLight } = useAppearance()
15+
const isPrimaryGutterShowing = computed(() => !isZen.value && showMenu.value)
16+
const isSecondaryGutterShowing = computed(() => !isZen.value && showMeta.value && doc.value)
4217
43-
const handleQuickActionClose = () => {
44-
if (doc.value) {
45-
return router.push({ path: `/docs/${doc.value.id}` })
46-
}
18+
const scrollListener = (event: Event) => {
19+
event.preventDefault()
20+
event.stopImmediatePropagation()
21+
event.stopPropagation()
4722
48-
router.push({ path: '/docs/new' })
49-
}
50-
51-
const handleLayoutChange = () => {
52-
toggleMenu()
53-
toggleMeta()
54-
}
55-
56-
onMounted(() => {
57-
// Todo: Migrate keybindings to composables.
58-
bindGlobal('mod+\\', () => {
59-
toggleMenu()
60-
toggleMeta()
61-
})
62-
})
63-
64-
const handleTabClose = async (id: string) => {
65-
if (doc.value?.id === id) {
66-
await router.push({ path: '/docs/new' })
67-
}
68-
69-
unpinDoc(id)
70-
}
71-
72-
const scrollListener = (event: Event) => {
73-
event.preventDefault()
74-
event.stopImmediatePropagation()
75-
event.stopPropagation()
23+
window.scrollTo(0, 0)
24+
}
7625
77-
window.scrollTo(0, 0)
78-
}
26+
const toggleAppearance = () => {
27+
appearance.value = isAuto.value ? 'dark' : isDark.value ? 'light' : 'auto'
28+
}
7929
80-
const toggleAppearance = () => {
81-
appearance.value = isAuto.value ? 'dark' : isDark.value ? 'light' : 'auto'
82-
}
30+
onMounted(() => {
31+
window.addEventListener('scroll', scrollListener)
8332
84-
onMounted(() => {
85-
window.addEventListener('scroll', scrollListener)
86-
})
33+
// Todo: Migrate keybindings to composables.
34+
bindGlobal('mod+\\', () => {
35+
toggleZen()
36+
})
8737
88-
onUnmounted(() => {
89-
window.removeEventListener('scroll', scrollListener)
90-
})
38+
// Todo: Migrate keybindings to composables.
39+
bindGlobal('mod+shift+,', () => {
40+
toggleMenu()
41+
})
42+
})
9143
92-
return {
93-
CoreDivider,
94-
CoreLink,
95-
appearance,
96-
doc,
97-
handleLayoutChange,
98-
handleQuickActionClose,
99-
handleTabClose,
100-
isAuto,
101-
isDark,
102-
isDesktop,
103-
isDoc,
104-
isLight,
105-
isMobile,
106-
isNew,
107-
isNuxt,
108-
modKey,
109-
pinnedDocs,
110-
showMenu,
111-
showMeta,
112-
toggleAppearance,
113-
}
114-
},
44+
onUnmounted(() => {
45+
window.removeEventListener('scroll', scrollListener)
11546
})
11647
</script>
11748

11849
<template>
119-
<div class="dashboard flex h-screen w-screen min-h-0 min-w-0 overflow-hidden">
120-
<CoreLayer v-if="isDesktop" as="section" class="flex flex-col items-center justify-between gap-4 h-full bg-layer md:flex">
121-
<div class="flex flex-col">
122-
<div class="flex flex-col flex-shrink-0 items-center justify-center p-1">
123-
<CoreButton
124-
class="flex items-center justify-center p-1"
125-
data-test-id="toggle-sidebars"
126-
data-test-toggle-sidebars
127-
@click="handleLayoutChange"
128-
>
129-
<LogoIcon class="h-8 text-brand" />
50+
<div class="dashboard flex flex-col lg:flex-row h-screen w-screen min-h-0 min-w-0 overflow-hidden">
51+
<Gutter :show="isPrimaryGutterShowing" :size="256" class="hidden lg:flex">
52+
<TheLeftSidebar class="flex flex-grow flex-shrink overflow-hidden w-full" />
53+
</Gutter>
54+
<FlexDivider class="hidden lg:block" />
55+
<CoreLayer class="bg-layer basis-full flex flex-col flex-grow flex-shrink min-h-0">
56+
<div class="flex p-1 items-center">
57+
<div class="flex gap-1 flex-row-reverse lg:flex-row items-center basis-full justify-start">
58+
<CoreButton class="hidden lg:flex" @click="toggleMenu">
59+
<IconMenu2 :stroke-width="1.25" class="h-6" />
60+
<span>Menu</span>
61+
</CoreButton>
62+
<CoreButton :as="CoreLink" :to="{ path: '/menu' }" class="lg:hidden">
63+
<IconMenu2 :stroke-width="1.25" class="h-6" />
64+
<span>Menu</span>
65+
</CoreButton>
66+
<CoreButton :as="CoreLink" :to="{ path: '/docs/new' }">
67+
<IconPencilPlus :stroke-width="1.25" class="h-6" />
68+
<span>New</span>
13069
</CoreButton>
13170
</div>
132-
<CoreDivider />
133-
<div class="flex flex-col gap-1 p-1 items-center">
134-
<CoreButtonLink :to="{ path: '/docs/new' }" :layer="1" :flat="true">
135-
<DocumentPlusIcon class="w-6" />
136-
</CoreButtonLink>
137-
<CoreButtonLink :to="{ path: '/notepad' }" :layer="1" :flat="true">
138-
<CalendarIcon class="w-6" />
139-
</CoreButtonLink>
140-
<CoreButtonLink :to="{ path: '/force-graph' }" :layer="1" :flat="true">
141-
<GraphIcon class="w-6" />
142-
</CoreButtonLink>
71+
<div class="flex -order-1 lg:order-none items-center justify-center">
72+
<CoreLayer class="flex text-xl gap-4">
73+
<svg class="sq-7 transition-colors duration-300 cursor-pointer" :class="isZen ? 'text-layer-bg' : 'text-brand'" viewBox="0 0 250 250" fill="none" xmlns="http://www.w3.org/2000/svg" @click="toggleZen">
74+
<path d="M125 250C194.036 250 250 194.036 250 125C250 55.9644 194.036 0 125 0C55.9644 0 0 55.9644 0 125C0 194.036 55.9644 250 125 250Z" fill="currentColor" />
75+
<template v-if="isZen">
76+
<path class="fill-layer-text-muted stroke-layer-text-muted" d="M212.962 153.171C212.962 153.171 192.949 159.5 179.472 159.5C165.995 159.5 145.981 153.171 145.981 153.171C145.981 153.171 164.921 167.5 179.472 167.5C194.022 167.5 212.962 153.171 212.962 153.171Z" stroke-width="2" stroke-linejoin="round" />
77+
<path class="fill-layer-text-muted stroke-layer-text-muted" d="M37 153.171C37 153.171 57.0133 159.5 70.4906 159.5C83.9679 159.5 103.981 153.171 103.981 153.171C103.981 153.171 85.0412 167.5 70.4906 167.5C55.94 167.5 37 153.171 37 153.171Z" stroke-width="2" stroke-linejoin="round" />
78+
</template>
79+
<template v-else>
80+
<mask id="mask0_508_218" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="145" y="117" width="68" height="85">
81+
<ellipse cx="179.378" cy="159.462" rx="33.4292" ry="42.3835" fill="#D9D9D9" />
82+
</mask>
83+
<g mask="url(#mask0_508_218)">
84+
<ellipse cx="179.378" cy="159.462" rx="33.4292" ry="42.3835" fill="white" />
85+
<ellipse cx="163.865" cy="162.805" rx="23.3666" ry="29.6255" fill="black" />
86+
</g>
87+
<mask id="mask1_508_218" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="37" y="117" width="68" height="85">
88+
<ellipse cx="70.7535" cy="159.462" rx="33.4292" ry="42.3836" fill="#D9D9D9" />
89+
</mask>
90+
<g mask="url(#mask1_508_218)">
91+
<ellipse cx="70.7535" cy="159.462" rx="33.4292" ry="42.3836" fill="white" />
92+
<ellipse cx="86.0505" cy="162.271" rx="23.3664" ry="29.6253" fill="black" />
93+
</g>
94+
</template>
95+
</svg>
96+
</CoreLayer>
14397
</div>
144-
</div>
145-
<div class="flex flex-col gap-1 p-1 items-center">
146-
<div class="flex flex-col gap-1 pb-2 items-center text-layer-muted">
147-
<CoreButton @click="toggleAppearance">
148-
<MoonIcon v-if="isDark" class="w-6" />
149-
<SunIcon v-else-if="isLight" class="w-6" />
150-
<div v-else class="relative sq-6">
151-
<SunIcon class="w-4 absolute top-0 right-0" />
152-
<MoonIcon class="w-4 absolute bottom-0 left-0" />
153-
</div>
98+
<div class="flex items-center gap-1 lg:basis-full justify-end">
99+
<CoreButton class="hidden lg:flex" @click="toggleAppearance">
100+
<IconMoon v-if="isDark" :stroke-width="1.25" class="w-6" />
101+
<IconSun v-else-if="isLight" :stroke-width="1.25" class="w-6" />
102+
<IconSunMoon v-else :stroke-width="1.25" class="w-6" />
103+
</CoreButton>
104+
<CoreButton v-if="doc" :as="CoreLink" :to="{ path: `/docs/${doc.id}/meta` }" class="lg:hidden">
105+
<IconDotsVertical :stroke-width="1.25" class="h-6" />
106+
</CoreButton>
107+
<CoreButton v-if="doc" class="hidden lg:flex" @click="toggleMeta">
108+
<IconDotsVertical :stroke-width="1.25" class="h-6" />
154109
</CoreButton>
155110
</div>
156-
<CoreButtonLink :to="{ path: '/settings' }" :layer="1" :flat="true">
157-
<Cog8ToothIcon class="w-6" />
158-
</CoreButtonLink>
159-
<CoreButtonLink :to="{ path: '/account' }" :layer="1" :flat="true">
160-
<UserCircleIcon class="w-6" />
161-
</CoreButtonLink>
111+
</div>
112+
<CoreDivider />
113+
<div class="flex flex-grow flex-shrink min-h-0 overflow-hidden min-w-0">
114+
<section class="flex flex-grow flex-shrink min-h-0 overflow-hidden min-w-0">
115+
<slot />
116+
</section>
162117
</div>
163118
</CoreLayer>
164-
<CoreLayer v-if="isDesktop" :as="CoreDivider" :vertical="true" />
165-
<section class="flex flex-col flex-grow flex-shrink min-h-0 min-w-0">
166-
<CoreLayer as="nav" class="flex items-center justify-between bg-layer">
167-
<div v-if="isMobile" class="flex items-center justify-between flex-grow p-1">
168-
<CoreLink :to="{ path: '/docs/new' }" class="flex items-center justify-center p-1">
169-
<LogoIcon class="h-8 text-brand" />
170-
</CoreLink>
171-
<div class="flex items-center gap-1">
172-
<CoreButton :as="CoreLink" :to="{ path: '/menu' }" role="button" aria-haspopup="true" aria-expanded="false">
173-
<svg height="1.25em" width="1.25em" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
174-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
175-
</svg>
176-
<span class="ml-2">Menu</span>
177-
</CoreButton>
178-
<CoreButton v-if="isNew" :as="CoreLink" :to="{ path: '/quick-action' }">
179-
<svg height="1.25em" width="1.25em" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
180-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
181-
</svg>
182-
</CoreButton>
183-
<CoreButton v-else-if="isDoc" :as="CoreLink" :to="{ path: `/docs/${doc?.id}/meta` }">
184-
<svg height="1.25em" width="1.25em" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
185-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
186-
</svg>
187-
</CoreButton>
188-
<CoreButton v-else @click="handleQuickActionClose">
189-
<svg height="1.25em" width="1.25em" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
190-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
191-
</svg>
192-
</CoreButton>
193-
</div>
194-
</div>
195-
<CoreNavPanel v-if="isDesktop" class="flex-shrink-0 w-64">
196-
<CoreLink :to="{ path: '/docs' }" class="sidebar-link justify-between w-full">
197-
<div class="flex gap-3 items-center">
198-
<svg class="w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
199-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
200-
</svg>
201-
<span>Search</span>
202-
</div>
203-
<span class="hidden md:flex text-layer-muted">
204-
<Key>{{ modKey }}</Key>
205-
<Key>⇧</Key>
206-
<Key>f</Key>
207-
</span>
208-
</CoreLink>
209-
</CoreNavPanel>
210-
<CoreDivider v-if="isDesktop" vertical />
211-
<CoreNavPanel v-if="isDesktop" class="flex flex-grow flex-shrink gap-2 min-w-0" horizontal>
212-
<CoreButton v-for="pinnedDoc in pinnedDocs" :key="pinnedDoc.id" :as="CoreLink" :to="{ path: `/docs/${pinnedDoc.id}` }" class="allow-link-active flex flex-shrink justify-between min-w-[4rem] max-w-[20rem]">
213-
<span class="text-ellipsis overflow-hidden">{{ pinnedDoc.label }}</span>
214-
<XMarkIcon class="w-4 transition hover:scale-125" @click.prevent.stop="() => handleTabClose(pinnedDoc.id)" />
215-
</CoreButton>
216-
</CoreNavPanel>
217-
</CoreLayer>
218-
<CoreLayer :as="CoreDivider" />
219-
<section class="flex flex-grow flex-shrink overflow-hidden min-w-0">
220-
<CoreLayer v-if="(isDesktop && showMenu)" v-slot="{ layer }" template>
221-
<TheLeftSidebar class="hidden w-64 md:flex flex-shrink-0 bg-layer" :class="layer.class" />
222-
</CoreLayer>
223-
<CoreLayer v-if="(isDesktop && showMenu)" :as="CoreDivider" :vertical="true" />
224-
<slot />
225-
<CoreLayer v-if="(isDesktop && showMeta && doc && isDoc)" v-slot="{ layer }" template>
226-
<CoreDivider vertical />
227-
<TheRightSidebar class="hidden w-64 bg-layer md:flex flex-shrink-0" :class="layer.class" />
228-
</CoreLayer>
229-
</section>
230-
</section>
119+
<FlexDivider class="hidden lg:block" />
120+
<Gutter :show="isSecondaryGutterShowing" :size="256" class="hidden lg:flex">
121+
<TheRightSidebar class="hidden lg:flex flex-grow flex-shrink-0 w-full" />
122+
</Gutter>
231123
<ToastList class="fixed bottom-8 right-8 m-auto" />
232124
</div>
233125
</template>
@@ -236,4 +128,12 @@ export default defineComponent({
236128
.dashboard {
237129
height: var(--app-height, 100vh);
238130
}
131+
132+
.gutter-right {
133+
direction: rtl;
134+
}
135+
136+
:deep(.gutter-right > *) {
137+
direction: ltr;
138+
}
239139
</style>

0 commit comments

Comments
 (0)