Add vertical navigation menu in left side

This commit is contained in:
tiny-craft 2023-06-28 00:47:44 +08:00
parent f8882d4eea
commit 79784fd109
12 changed files with 405 additions and 122 deletions

1
.prettierignore Normal file
View File

@ -0,0 +1 @@
/frontend/wailsjs/**

View File

@ -1,48 +1,18 @@
<script setup>
import { get } from 'lodash'
import { computed, nextTick, onMounted, provide, reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { GetPreferences } from '../wailsjs/go/storage/PreferencesStorage.js'
import ContentPane from './components/ContentPane.vue'
import NewConnDialog from './components/dialogs/NewConnDialog.vue'
import NewKeyDialog from './components/dialogs/NewKeyDialog.vue'
import PreferencesDialog from './components/dialogs/PreferencesDialog.vue'
import RenameKeyDialog from './components/dialogs/RenameKeyDialog.vue'
import SetTtlDialog from './components/dialogs/SetTtlDialog.vue'
import NavigationPane from './components/NavigationPane.vue'
import hljs from 'highlight.js/lib/core'
import json from 'highlight.js/lib/languages/json'
import plaintext from 'highlight.js/lib/languages/plaintext'
import { useThemeVars } from 'naive-ui'
import AddFieldsDialog from './components/dialogs/AddFieldsDialog.vue'
const themeVars = useThemeVars()
import AppContent from './AppContent.vue'
hljs.registerLanguage('json', json)
hljs.registerLanguage('plaintext', plaintext)
const data = reactive({
asideWith: 300,
hoverResize: false,
resizing: false,
})
const preferences = ref({})
provide('preferences', preferences)
const i18n = useI18n()
onMounted(async () => {
preferences.value = await GetPreferences()
await nextTick(() => {
i18n.locale.value = get(preferences.value, 'general.language', 'en')
})
})
// TODO: apply font size to all elements
const getFontSize = computed(() => {
return get(preferences.value, 'general.font_size', 'en')
})
const themeOverrides = {
common: {
// primaryColor: '#409EFF',
@ -55,56 +25,19 @@ const themeOverrides = {
Tag: {
// borderRadius: '3px'
},
Tabs: {
tabGapSmallCard: '1px',
tabGapMediumCard: '1px',
tabGapLargeCard: '1px',
},
}
const handleResize = (evt) => {
if (data.resizing) {
data.asideWith = Math.max(evt.clientX, 300)
}
}
const stopResize = () => {
data.resizing = false
document.removeEventListener('mousemove', handleResize)
document.removeEventListener('mouseup', stopResize)
// TODO: Save sidebar x-position
}
const startResize = () => {
data.resizing = true
document.addEventListener('mousemove', handleResize)
document.addEventListener('mouseup', stopResize)
}
const asideWidthVal = computed(() => {
return data.asideWith + 'px'
})
const dragging = computed(() => {
return data.hoverResize || data.resizing
})
</script>
<template>
<n-config-provider :hljs="hljs" :inline-theme-disabled="true" :theme-overrides="themeOverrides" class="fill-height">
<n-message-provider>
<n-dialog-provider>
<div id="app-container" :class="{ dragging: dragging }" class="flex-box-h">
<div id="app-side" :style="{ width: asideWidthVal }" class="flex-box-h flex-item">
<navigation-pane class="flex-item-expand"></navigation-pane>
<div
:class="{
'resize-divider-hover': data.hoverResize,
'resize-divider-drag': data.resizing,
}"
class="resize-divider"
@mousedown="startResize"
@mouseout="data.hoverResize = false"
@mouseover="data.hoverResize = true"
></div>
</div>
<content-pane class="flex-item-expand" />
</div>
<app-content />
<!-- top modal dialogs -->
<new-conn-dialog />
@ -118,43 +51,4 @@ const dragging = computed(() => {
</n-config-provider>
</template>
<style lang="scss">
#app-container {
height: 100%;
overflow: hidden;
border-top: var(--border-color) 1px solid;
box-sizing: border-box;
#app-toolbar {
height: 40px;
border-bottom: var(--border-color) 1px solid;
}
#app-side {
//overflow: hidden;
height: 100%;
.resize-divider {
//height: 100%;
width: 2px;
border-left-width: 5px;
background-color: var(--border-color);
}
.resize-divider-hover {
width: 5px;
}
.resize-divider-drag {
//background-color: rgb(0, 105, 218);
width: 5px;
//background-color: var(--el-color-primary);
background-color: v-bind('themeVars.primaryColor');
}
}
}
.dragging {
cursor: col-resize !important;
}
</style>
<style lang="scss"></style>

123
frontend/src/AppContent.vue Normal file
View File

@ -0,0 +1,123 @@
<script setup>
import ContentPane from './components/ContentPane.vue'
import NavigationPane from './components/NavigationPane.vue'
import { computed, nextTick, onMounted, provide, reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { GetPreferences } from '../wailsjs/go/storage/PreferencesStorage.js'
import { get } from 'lodash'
import { useThemeVars } from 'naive-ui'
import NavMenu from './components/NavMenu.vue'
const themeVars = useThemeVars()
const data = reactive({
asideWith: 300,
hoverResize: false,
resizing: false,
})
const preferences = ref({})
provide('preferences', preferences)
const i18n = useI18n()
onMounted(async () => {
preferences.value = await GetPreferences()
await nextTick(() => {
i18n.locale.value = get(preferences.value, 'general.language', 'en')
})
})
// TODO: apply font size to all elements
const getFontSize = computed(() => {
return get(preferences.value, 'general.font_size', 'en')
})
const handleResize = (evt) => {
if (data.resizing) {
data.asideWith = Math.max(evt.clientX, 300)
}
}
const stopResize = () => {
data.resizing = false
document.removeEventListener('mousemove', handleResize)
document.removeEventListener('mouseup', stopResize)
// TODO: Save sidebar x-position
}
const startResize = () => {
data.resizing = true
document.addEventListener('mousemove', handleResize)
document.addEventListener('mouseup', stopResize)
}
const asideWidthVal = computed(() => {
return data.asideWith + 'px'
})
const dragging = computed(() => {
return data.hoverResize || data.resizing
})
</script>
<template>
<!-- app content-->
<div id="app-container" :class="{ dragging: dragging }" class="flex-box-h">
<nav-menu />
<div id="app-side" :style="{ width: asideWidthVal }" class="flex-box-h flex-item">
<navigation-pane class="flex-item-expand"></navigation-pane>
<div
:class="{
'resize-divider-hover': data.hoverResize,
'resize-divider-drag': data.resizing,
}"
class="resize-divider"
@mousedown="startResize"
@mouseout="data.hoverResize = false"
@mouseover="data.hoverResize = true"
></div>
</div>
<content-pane class="flex-item-expand" />
</div>
</template>
<style lang="scss">
#app-container {
height: 100%;
overflow: hidden;
border-top: var(--border-color) 1px solid;
box-sizing: border-box;
#app-toolbar {
height: 40px;
border-bottom: var(--border-color) 1px solid;
}
#app-side {
//overflow: hidden;
height: 100%;
.resize-divider {
//height: 100%;
width: 2px;
border-left-width: 5px;
background-color: var(--border-color);
}
.resize-divider-hover {
width: 5px;
}
.resize-divider-drag {
//background-color: rgb(0, 105, 218);
width: 5px;
//background-color: var(--el-color-primary);
background-color: v-bind('themeVars.primaryColor');
}
}
}
.dragging {
cursor: col-resize !important;
}
</style>

View File

@ -0,0 +1,122 @@
<script setup>
import { computed, h, ref } from 'vue'
import { NIcon, useThemeVars } from 'naive-ui'
import ToggleDb from './icons/ToggleDb.vue'
import { useI18n } from 'vue-i18n'
import ToggleServer from './icons/ToggleServer.vue'
import IconButton from './IconButton.vue'
import Config from './icons/Config.vue'
import useDialogStore from '../stores/dialog.js'
import Github from './icons/Github.vue'
import { BrowserOpenURL } from '../../wailsjs/runtime/runtime.js'
const themeVars = useThemeVars()
const iconSize = 26
const selectedMenu = ref('server')
const renderIcon = (icon) => {
return () => h(NIcon, null, { default: () => h(icon) })
}
const i18n = useI18n()
const menuOptions = computed(() => {
return [
{
label: i18n.t('structure'),
key: 'structure',
icon: renderIcon(ToggleDb),
show: true,
},
{
label: i18n.t('server'),
key: 'server',
icon: renderIcon(ToggleServer),
},
]
})
const preferencesOptions = computed(() => {
return [
{
label: i18n.t('preferences'),
key: 'preferences',
icon: renderIcon(Config),
},
{
label: i18n.t('about'),
key: 'about',
},
{
label: i18n.t('check_update'),
key: 'update',
},
]
})
const renderContextLabel = (option) => {
return h('div', { class: 'context-menu-item' }, option.label)
}
const dialogStore = useDialogStore()
const onSelectPreferenceMenu = (key) => {
switch (key) {
case 'preferences':
dialogStore.openPreferencesDialog()
break
case 'update':
break
}
}
const openGithub = () => {
BrowserOpenURL('https://github.com/tiny-craft/tiny-rdm')
}
</script>
<template>
<div id="app-nav-menu" class="flex-box-v">
<n-menu
v-model:value="selectedMenu"
:collapsed="true"
:collapsed-icon-size="iconSize"
:collapsed-width="60"
:options="menuOptions"
></n-menu>
<div class="flex-item-expand"></div>
<div class="nav-menu-item flex-box-v">
<n-dropdown
:animated="false"
:keyboard="false"
:options="preferencesOptions"
:render-label="renderContextLabel"
trigger="click"
@select="onSelectPreferenceMenu"
>
<icon-button :icon="Config" :size="iconSize" class="nav-menu-button" />
</n-dropdown>
<icon-button :icon="Github" :size="iconSize" class="nav-menu-button" @click="openGithub"></icon-button>
</div>
</div>
</template>
<style lang="scss">
#app-nav-menu {
width: 60px;
height: 100vh;
border-right: var(--border-color) solid 1px;
.nav-menu-item {
align-items: center;
padding: 10px 0;
gap: 15px;
.nav-menu-button {
margin-bottom: 6px;
:hover {
color: v-bind('themeVars.primaryColor');
}
}
}
}
</style>

View File

@ -16,7 +16,7 @@ const onSort = () => {
</script>
<template>
<div class="nav-pane-container flex-box-v">
<div v-if="true" class="nav-pane-container flex-box-v">
<ConnectionsTree />
<!-- bottom function bar -->

View File

@ -10,17 +10,17 @@ const props = defineProps({
<template>
<svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<path
:stroke-width="props.strokeWidth"
d="M24 4L18 10H10V18L4 24L10 30V38H18L24 44L30 38H38V30L44 24L38 18V10H30L24 4Z"
:stroke-width="strokeWidth"
fill="none"
stroke="currentColor"
d="M18.2838 43.1713C14.9327 42.1736 11.9498 40.3213 9.58787 37.867C10.469 36.8227 11 35.4734 11 34.0001C11 30.6864 8.31371 28.0001 5 28.0001C4.79955 28.0001 4.60139 28.01 4.40599 28.0292C4.13979 26.7277 4 25.3803 4 24.0001C4 21.9095 4.32077 19.8938 4.91579 17.9995C4.94381 17.9999 4.97188 18.0001 5 18.0001C8.31371 18.0001 11 15.3138 11 12.0001C11 11.0488 10.7786 10.1493 10.3846 9.35011C12.6975 7.1995 15.5205 5.59002 18.6521 4.72314C19.6444 6.66819 21.6667 8.00013 24 8.00013C26.3333 8.00013 28.3556 6.66819 29.3479 4.72314C32.4795 5.59002 35.3025 7.1995 37.6154 9.35011C37.2214 10.1493 37 11.0488 37 12.0001C37 15.3138 39.6863 18.0001 43 18.0001C43.0281 18.0001 43.0562 17.9999 43.0842 17.9995C43.6792 19.8938 44 21.9095 44 24.0001C44 25.3803 43.8602 26.7277 43.594 28.0292C43.3986 28.01 43.2005 28.0001 43 28.0001C39.6863 28.0001 37 30.6864 37 34.0001C37 35.4734 37.531 36.8227 38.4121 37.867C36.0502 40.3213 33.0673 42.1736 29.7162 43.1713C28.9428 40.752 26.676 39.0001 24 39.0001C21.324 39.0001 19.0572 40.752 18.2838 43.1713Z"
stroke-linejoin="round"
/>
<path
:stroke-width="props.strokeWidth"
d="M24 30C27.3137 30 30 27.3137 30 24C30 20.6863 27.3137 18 24 18C20.6863 18 18 20.6863 18 24C18 27.3137 20.6863 30 24 30Z"
:stroke-width="strokeWidth"
fill="none"
stroke="currentColor"
d="M24 31C27.866 31 31 27.866 31 24C31 20.134 27.866 17 24 17C20.134 17 17 20.134 17 24C17 27.866 20.134 31 24 31Z"
stroke-linejoin="round"
/>
</svg>

View File

@ -0,0 +1,27 @@
<script setup>
// const props = defineProps({
// strokeWidth: {
// type: [Number, String],
// default: 3,
// },
// })
</script>
<template>
<svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<path
clip-rule="evenodd"
d="M24 4C12.9543 4 4 12.9543 4 24C4 35.0457 12.9543 44 24 44C35.0457 44 44 35.0457 44 24C44 12.9543 35.0457 4 24 4ZM0 24C0 10.7452 10.7452 0 24 0C37.2548 0 48 10.7452 48 24C48 37.2548 37.2548 48 24 48C10.7452 48 0 37.2548 0 24Z"
fill="currentColor"
fill-rule="evenodd"
/>
<path
clip-rule="evenodd"
d="M19.1833 45.4716C18.9898 45.2219 18.9898 42.9973 19.1833 38.798C17.1114 38.8696 15.8024 38.7258 15.2563 38.3667C14.437 37.828 13.6169 36.1667 12.8891 34.9959C12.1614 33.8251 10.5463 33.64 9.89405 33.3783C9.24182 33.1165 9.07809 32.0496 11.6913 32.8565C14.3044 33.6634 14.4319 35.8607 15.2563 36.3745C16.0806 36.8883 18.0515 36.6635 18.9448 36.2519C19.8382 35.8403 19.7724 34.3078 19.9317 33.7007C20.1331 33.134 19.4233 33.0083 19.4077 33.0037C18.5355 33.0037 13.9539 32.0073 12.6955 27.5706C11.437 23.134 13.0581 20.2341 13.9229 18.9875C14.4995 18.1564 14.4485 16.3852 13.7699 13.6737C16.2335 13.3589 18.1347 14.1343 19.4734 16.0001C19.4747 16.0108 21.2285 14.9572 24.0003 14.9572C26.772 14.9572 27.7553 15.8154 28.5142 16.0001C29.2731 16.1848 29.88 12.7341 34.5668 13.6737C33.5883 15.5969 32.7689 18.0001 33.3943 18.9875C34.0198 19.9749 36.4745 23.1147 34.9666 27.5706C33.9614 30.5413 31.9853 32.3523 29.0384 33.0037C28.7005 33.1115 28.5315 33.2855 28.5315 33.5255C28.5315 33.8856 28.9884 33.9249 29.6465 35.6117C30.0853 36.7362 30.117 39.948 29.7416 45.247C28.7906 45.4891 28.0508 45.6516 27.5221 45.7347C26.5847 45.882 25.5669 45.9646 24.5669 45.9965C23.5669 46.0284 23.2196 46.0248 21.837 45.8961C20.9154 45.8103 20.0308 45.6688 19.1833 45.4716Z"
fill="currentColor"
fill-rule="evenodd"
/>
</svg>
</template>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,64 @@
<script setup>
const props = defineProps({
strokeWidth: {
type: [Number, String],
default: 3,
},
})
</script>
<template>
<svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<path
:stroke-width="strokeWidth"
d="M38 20H18V28H38V20Z"
fill="none"
stroke="currentColor"
stroke-linejoin="round"
/>
<path
:stroke-width="strokeWidth"
d="M32 6H18V14H32V6Z"
fill="none"
stroke="currentColor"
stroke-linejoin="round"
/>
<path
:stroke-width="strokeWidth"
d="M44 34H18V42H44V34Z"
fill="none"
stroke="currentColor"
stroke-linejoin="round"
/>
<path
:stroke-width="strokeWidth"
d="M17 10H5"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
:stroke-width="strokeWidth"
d="M17 24H5"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
:stroke-width="strokeWidth"
d="M17 38H5"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
:stroke-width="strokeWidth"
d="M5 44V4"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</template>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,37 @@
<script setup>
const props = defineProps({
strokeWidth: {
type: [Number, String],
default: 3,
},
})
</script>
<template>
<svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<path
:stroke-width="strokeWidth"
d="M24 44C35.0457 44 44 35.0457 44 24C44 12.9543 35.0457 4 24 4C12.9543 4 4 12.9543 4 24C4 35.0457 12.9543 44 24 44Z"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
:stroke-width="strokeWidth"
d="M33.5424 27C32.2681 31.0571 28.4778 34 24.0002 34C19.5226 34 15.7323 31.0571 14.458 27V33"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
:stroke-width="strokeWidth"
d="M33.5424 15V21C32.2681 16.9429 28.4778 14 24.0002 14C19.5226 14 15.7323 16.9429 14.458 21"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</template>
<style lang="scss" scoped></style>

View File

@ -103,5 +103,9 @@
"spec_field_required": "\"{key}\" should not be blank",
"no_connections": "No Connection",
"empty_tab_content": "Select the key from left list to see the details of the key.",
"reload_when_succ": "Reload immediately after success"
"reload_when_succ": "Reload immediately after success",
"server": "Server",
"structure": "Structure",
"about": "About",
"check_update": "Check for Updates..."
}

View File

@ -106,5 +106,9 @@
"spec_field_required": "{key} 不能为空",
"no_connections": "空空如也",
"empty_tab_content": "可以从左边选择键来查看键的详细内容",
"reload_when_succ": "操作成功后立即重新加载"
"reload_when_succ": "操作成功后立即重新加载",
"server": "服务器",
"structure": "结构",
"about": "关于",
"check_update": "检查更新..."
}

View File

@ -47,7 +47,14 @@ func main() {
connSvc,
},
Mac: &mac.Options{
//TitleBar: mac.TitleBarHiddenInset(),
TitleBar: &mac.TitleBar{
TitlebarAppearsTransparent: false,
HideTitle: false,
HideTitleBar: false,
FullSizeContent: false,
UseToolbar: false,
HideToolbarSeparator: true,
},
//WebviewIsTransparent: true,
//WindowIsTranslucent: true,
},