diff options
author | 2023-03-30 23:35:34 +0200 | |
---|---|---|
committer | 2023-03-30 23:48:47 +0200 | |
commit | 24ba0ff5faf71a8f1a8518705b4292aab7d41244 (patch) | |
tree | 5ae74c6ec06bc0ba21428fa9f53948f4d4a4b755 /src/components/CollapsibleToolMenu.vue | |
parent | 849981d1ec4903f26106f2df38b75e37654076ca (diff) | |
download | it-tools-24ba0ff5faf71a8f1a8518705b4292aab7d41244.tar.gz it-tools-24ba0ff5faf71a8f1a8518705b4292aab7d41244.tar.zst it-tools-24ba0ff5faf71a8f1a8518705b4292aab7d41244.zip |
feat(menu): collapsible category
Diffstat (limited to 'src/components/CollapsibleToolMenu.vue')
-rw-r--r-- | src/components/CollapsibleToolMenu.vue | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/src/components/CollapsibleToolMenu.vue b/src/components/CollapsibleToolMenu.vue new file mode 100644 index 0000000..ac54fde --- /dev/null +++ b/src/components/CollapsibleToolMenu.vue @@ -0,0 +1,136 @@ +<template> + <div v-for="{ name, tools, isCollapsed } of menuOptions" :key="name"> + <n-text tag="div" depth="3" class="category-name" @click="toggleCategoryCollapse({ name })"> + <n-icon :component="ChevronRight" :class="{ rotated: isCollapsed }" size="16" /> + + <span> + {{ name }} + </span> + </n-text> + + <n-collapse-transition :show="!isCollapsed"> + <div class="menu-wrapper"> + <div class="toggle-bar" @click="toggleCategoryCollapse({ name })" /> + + <n-menu + class="menu" + :value="(route.name as string)" + :collapsed-width="64" + :collapsed-icon-size="22" + :options="tools" + :indent="8" + :default-expand-all="true" + /> + </div> + </n-collapse-transition> + </div> +</template> + +<script setup lang="ts"> +import type { Tool, ToolCategory } from '@/tools/tools.types'; +import { ChevronRight } from '@vicons/tabler'; +import { useStorage } from '@vueuse/core'; +import { useThemeVars } from 'naive-ui'; +import { toRefs, computed, h } from 'vue'; +import { RouterLink, useRoute } from 'vue-router'; +import MenuIconItem from './MenuIconItem.vue'; + +const props = withDefaults(defineProps<{ toolsByCategory?: ToolCategory[] }>(), { toolsByCategory: () => [] }); +const { toolsByCategory } = toRefs(props); +const route = useRoute(); + +const makeLabel = (tool: Tool) => () => h(RouterLink, { to: tool.path }, { default: () => tool.name }); +const makeIcon = (tool: Tool) => () => h(MenuIconItem, { tool }); + +const collapsedCategories = useStorage<Record<string, boolean>>( + 'menu-tool-option:collapsed-categories', + {}, + undefined, + { + deep: true, + serializer: { + read: (v) => (v ? JSON.parse(v) : null), + write: (v) => JSON.stringify(v), + }, + }, +); + +function toggleCategoryCollapse({ name }: { name: string }) { + collapsedCategories.value[name] = !collapsedCategories.value[name]; +} + +const menuOptions = computed(() => + toolsByCategory.value.map(({ name, components }) => ({ + name: name, + isCollapsed: collapsedCategories.value[name], + tools: components.map((tool) => ({ + label: makeLabel(tool), + icon: makeIcon(tool), + key: tool.name, + })), + })), +); + +const themeVars = useThemeVars(); + +console.log(themeVars.value); +</script> + +<style scoped lang="less"> +.category-name { + font-size: 0.93em; + padding: 12px 0 0px 0; + cursor: pointer; + + display: flex; + flex-direction: row; + align-items: center; + .n-icon { + transition: transform ease 0.5s; + transform: rotate(90deg); + margin: 0 10px 0 7px; + opacity: 0.5; + + &.rotated { + transform: rotate(0deg); + } + } +} + +.menu-wrapper { + display: flex; + flex-direction: row; + .menu { + flex: 1; + margin-bottom: 5px; + + ::v-deep(.n-menu-item-content::before) { + left: 0; + right: 13px; + } + } + + .toggle-bar { + width: 25px; + opacity: 0.1; + transition: opacity ease 0.2s; + position: relative; + cursor: pointer; + + &::before { + width: 2px; + height: 100%; + content: ' '; + background-color: v-bind('themeVars.textColor3'); + border-radius: 2px; + position: absolute; + top: 0; + left: 14.5px; + } + + &:hover { + opacity: 0.5; + } + } +} +</style> |