feat: customize sidebar navi (#1336)
This commit is contained in:
parent
e6505fe55a
commit
a3316eb23b
@ -80,6 +80,7 @@ import SettingsDashboardTab from '@/components/settings/SettingsDashboardTab.vue
|
|||||||
import SettingsGCodeViewerTab from '@/components/settings/SettingsGCodeViewerTab.vue'
|
import SettingsGCodeViewerTab from '@/components/settings/SettingsGCodeViewerTab.vue'
|
||||||
import SettingsEditorTab from '@/components/settings/SettingsEditorTab.vue'
|
import SettingsEditorTab from '@/components/settings/SettingsEditorTab.vue'
|
||||||
import SettingsTimelapseTab from '@/components/settings/SettingsTimelapseTab.vue'
|
import SettingsTimelapseTab from '@/components/settings/SettingsTimelapseTab.vue'
|
||||||
|
import SettingsNavigationTab from '@/components/settings/SettingsNavigationTab.vue'
|
||||||
|
|
||||||
import Panel from '@/components/ui/Panel.vue'
|
import Panel from '@/components/ui/Panel.vue'
|
||||||
import {
|
import {
|
||||||
@ -98,6 +99,7 @@ import {
|
|||||||
mdiVideo3d,
|
mdiVideo3d,
|
||||||
mdiWebcam,
|
mdiWebcam,
|
||||||
mdiDipSwitch,
|
mdiDipSwitch,
|
||||||
|
mdiMenu,
|
||||||
} from '@mdi/js'
|
} from '@mdi/js'
|
||||||
import SettingsMiscellaneousTab from '@/components/settings/SettingsMiscellaneousTab.vue'
|
import SettingsMiscellaneousTab from '@/components/settings/SettingsMiscellaneousTab.vue'
|
||||||
@Component({
|
@Component({
|
||||||
@ -116,6 +118,7 @@ import SettingsMiscellaneousTab from '@/components/settings/SettingsMiscellaneou
|
|||||||
SettingsEditorTab,
|
SettingsEditorTab,
|
||||||
SettingsTimelapseTab,
|
SettingsTimelapseTab,
|
||||||
SettingsMiscellaneousTab,
|
SettingsMiscellaneousTab,
|
||||||
|
SettingsNavigationTab,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
export default class TheSettingsMenu extends Mixins(BaseMixin) {
|
export default class TheSettingsMenu extends Mixins(BaseMixin) {
|
||||||
@ -194,6 +197,11 @@ export default class TheSettingsMenu extends Mixins(BaseMixin) {
|
|||||||
name: 'miscellaneous',
|
name: 'miscellaneous',
|
||||||
title: this.$t('Settings.MiscellaneousTab.Miscellaneous'),
|
title: this.$t('Settings.MiscellaneousTab.Miscellaneous'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: mdiMenu,
|
||||||
|
name: 'navigation',
|
||||||
|
title: this.$t('Settings.NavigationTab.Navigation'),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
if (this.moonrakerComponents.includes('timelapse')) {
|
if (this.moonrakerComponents.includes('timelapse')) {
|
||||||
|
@ -1,46 +1,3 @@
|
|||||||
<style>
|
|
||||||
.nav-logo {
|
|
||||||
height: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.small-list-item {
|
|
||||||
height: var(--sidebar-menu-item-height);
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-text-decoration {
|
|
||||||
text-decoration: none;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-background:before {
|
|
||||||
background-color: rgba(255, 255, 255, 0) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-border {
|
|
||||||
border: 0 !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<style scoped>
|
|
||||||
.active-nav-item {
|
|
||||||
border-right: 4px solid var(--v-primary-base);
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-item-icon {
|
|
||||||
opacity: 0.85;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-item-title {
|
|
||||||
line-height: 30px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 600;
|
|
||||||
text-transform: uppercase;
|
|
||||||
opacity: 0.85;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-scrollbar {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template>
|
<template>
|
||||||
<v-navigation-drawer
|
<v-navigation-drawer
|
||||||
:key="navigationStyle"
|
:key="navigationStyle"
|
||||||
@ -59,78 +16,27 @@
|
|||||||
v-if="isMobile"
|
v-if="isMobile"
|
||||||
router
|
router
|
||||||
to="/"
|
to="/"
|
||||||
:class="
|
:class="mobileLogoClass"
|
||||||
'sidebar-logo no-text-decoration no-background no-border ' +
|
|
||||||
(navigationStyle === 'iconsOnly' ? 'pa-0 justify-center' : '')
|
|
||||||
"
|
|
||||||
:style="'height: ' + topbarHeight + 'px'"
|
:style="'height: ' + topbarHeight + 'px'"
|
||||||
:ripple="false">
|
:ripple="false">
|
||||||
<template v-if="sidebarLogo">
|
<template v-if="sidebarLogo">
|
||||||
<img :src="sidebarLogo" :style="logoCssVars" class="nav-logo" alt="Logo" />
|
<img :src="sidebarLogo" :style="logoCssVars" class="nav-logo" alt="Logo" />
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<mainsail-logo
|
<mainsail-logo :color="logoColor" :style="logoCssVars" class="nav-logo" :ripple="false" />
|
||||||
:color="logoColor"
|
|
||||||
:style="logoCssVars"
|
|
||||||
class="nav-logo"
|
|
||||||
:ripple="false"></mainsail-logo>
|
|
||||||
</template>
|
</template>
|
||||||
<template v-if="navigationStyle !== 'iconsOnly'">
|
<template v-if="navigationStyle !== 'iconsOnly'">
|
||||||
<span class="text-h6 font-weight-regular text-truncate">{{ printerName }}</span>
|
<span class="text-h6 font-weight-regular text-truncate">{{ printerName }}</span>
|
||||||
</template>
|
</template>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
<template v-if="countPrinters">
|
<sidebar-item v-for="(category, index) in visibleNaviPoints" :key="index" :item="category" />
|
||||||
<v-tooltip right :open-delay="500" :disabled="navigationStyle !== 'iconsOnly'">
|
|
||||||
<template #activator="{ on, attrs }">
|
|
||||||
<v-list-item
|
|
||||||
router
|
|
||||||
to="/allPrinters"
|
|
||||||
class="small-list-item mt-1"
|
|
||||||
v-bind="attrs"
|
|
||||||
v-on="on">
|
|
||||||
<v-list-item-icon class="my-3 mr-3 menu-item-icon">
|
|
||||||
<v-icon>{{ mdiViewDashboardOutline }}</v-icon>
|
|
||||||
</v-list-item-icon>
|
|
||||||
<v-list-item-content>
|
|
||||||
<v-list-item-title tile class="menu-item-title">
|
|
||||||
{{ $t('App.Printers') }}
|
|
||||||
</v-list-item-title>
|
|
||||||
</v-list-item-content>
|
|
||||||
</v-list-item>
|
|
||||||
</template>
|
|
||||||
<span>{{ $t('App.Printers') }}</span>
|
|
||||||
</v-tooltip>
|
|
||||||
<v-divider class="my-1"></v-divider>
|
|
||||||
</template>
|
|
||||||
<div v-for="(category, index) in naviPoints" :key="index">
|
|
||||||
<v-tooltip right :open-delay="500" :disabled="navigationStyle !== 'iconsOnly'">
|
|
||||||
<template #activator="{ on, attrs }">
|
|
||||||
<v-list-item
|
|
||||||
router
|
|
||||||
:to="category.path"
|
|
||||||
class="small-list-item"
|
|
||||||
v-bind="attrs"
|
|
||||||
v-on="on">
|
|
||||||
<v-list-item-icon class="my-3 mr-3 menu-item-icon">
|
|
||||||
<v-icon>{{ category.icon }}</v-icon>
|
|
||||||
</v-list-item-icon>
|
|
||||||
<v-list-item-content>
|
|
||||||
<v-list-item-title tile class="menu-item-title">
|
|
||||||
{{ $t(`Router.${category.title}`) }}
|
|
||||||
</v-list-item-title>
|
|
||||||
</v-list-item-content>
|
|
||||||
</v-list-item>
|
|
||||||
</template>
|
|
||||||
<span>{{ $t(`Router.${category.title}`) }}</span>
|
|
||||||
</v-tooltip>
|
|
||||||
</div>
|
|
||||||
</v-list-item-group>
|
</v-list-item-group>
|
||||||
</v-list>
|
</v-list>
|
||||||
</overlay-scrollbars>
|
</overlay-scrollbars>
|
||||||
<template #append>
|
<template #append>
|
||||||
<v-list-item class="small-list-item mb-2">
|
<v-list-item class="small-list-item mb-2">
|
||||||
<v-list-item-icon class="menu-item-icon">
|
<v-list-item-icon class="menu-item-icon">
|
||||||
<about-dialog></about-dialog>
|
<about-dialog />
|
||||||
</v-list-item-icon>
|
</v-list-item-icon>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</template>
|
</template>
|
||||||
@ -139,32 +45,27 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Component from 'vue-class-component'
|
import Component from 'vue-class-component'
|
||||||
import routes, { AppRoute } from '@/routes'
|
|
||||||
import { Mixins } from 'vue-property-decorator'
|
import { Mixins } from 'vue-property-decorator'
|
||||||
import BaseMixin from '@/components/mixins/base'
|
import BaseMixin from '@/components/mixins/base'
|
||||||
import { PrinterStateKlipperConfig } from '@/store/printer/types'
|
|
||||||
import TheSelectPrinterDialog from '@/components/TheSelectPrinterDialog.vue'
|
import TheSelectPrinterDialog from '@/components/TheSelectPrinterDialog.vue'
|
||||||
import AboutDialog from '@/components/dialogs/AboutDialog.vue'
|
import AboutDialog from '@/components/dialogs/AboutDialog.vue'
|
||||||
import { navigationWidth, topbarHeight } from '@/store/variables'
|
import { navigationWidth, topbarHeight } from '@/store/variables'
|
||||||
import MainsailLogo from '@/components/ui/MainsailLogo.vue'
|
import MainsailLogo from '@/components/ui/MainsailLogo.vue'
|
||||||
import { mdiViewDashboardOutline } from '@mdi/js'
|
import SidebarItem from '@/components/ui/SidebarItem.vue'
|
||||||
|
import NavigationMixin from '@/components/mixins/navigation'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {
|
components: {
|
||||||
|
SidebarItem,
|
||||||
TheSelectPrinterDialog,
|
TheSelectPrinterDialog,
|
||||||
AboutDialog,
|
AboutDialog,
|
||||||
MainsailLogo,
|
MainsailLogo,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
export default class TheSidebar extends Mixins(BaseMixin) {
|
export default class TheSidebar extends Mixins(NavigationMixin, BaseMixin) {
|
||||||
navigationWidth = navigationWidth
|
navigationWidth = navigationWidth
|
||||||
topbarHeight = topbarHeight
|
topbarHeight = topbarHeight
|
||||||
|
|
||||||
/**
|
|
||||||
* Icons
|
|
||||||
*/
|
|
||||||
mdiViewDashboardOutline = mdiViewDashboardOutline
|
|
||||||
|
|
||||||
get naviDrawer(): boolean {
|
get naviDrawer(): boolean {
|
||||||
return this.$store.state.naviDrawer
|
return this.$store.state.naviDrawer
|
||||||
}
|
}
|
||||||
@ -181,45 +82,12 @@ export default class TheSidebar extends Mixins(BaseMixin) {
|
|||||||
return this.$store.getters['files/getSidebarBackground']
|
return this.$store.getters['files/getSidebarBackground']
|
||||||
}
|
}
|
||||||
|
|
||||||
get naviPoints(): AppRoute[] {
|
|
||||||
return routes.filter((element) => {
|
|
||||||
return element.showInNavi && this.showInNavi(element)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
get klippy_state(): string {
|
|
||||||
return this.$store.state.server.klippy_state
|
|
||||||
}
|
|
||||||
|
|
||||||
get boolNaviWebcam(): boolean {
|
|
||||||
return this.$store.state.gui.uiSettings.boolWebcamNavi
|
|
||||||
}
|
|
||||||
|
|
||||||
get moonrakerComponents(): string[] {
|
|
||||||
return this.$store.state.server.components
|
|
||||||
}
|
|
||||||
|
|
||||||
get registeredDirectories(): string[] {
|
|
||||||
return this.$store.state.server.registered_directories
|
|
||||||
}
|
|
||||||
|
|
||||||
get klipperConfigfileSettings(): PrinterStateKlipperConfig[] {
|
|
||||||
return this.$store.state.printer.configfile?.settings ?? {}
|
|
||||||
}
|
|
||||||
|
|
||||||
get currentPage(): string {
|
get currentPage(): string {
|
||||||
return this.$route.fullPath
|
return this.$route.fullPath
|
||||||
}
|
}
|
||||||
|
|
||||||
get countPrinters() {
|
|
||||||
return this.$store.getters['farm/countPrinters']
|
|
||||||
}
|
|
||||||
|
|
||||||
get boolNaviTemp(): boolean {
|
get boolNaviTemp(): boolean {
|
||||||
if (!this.isMobile && this.$vuetify.breakpoint.mdAndDown) {
|
return !this.isMobile && this.$vuetify.breakpoint.mdAndDown
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get sidebarCssVars(): any {
|
get sidebarCssVars(): any {
|
||||||
@ -255,16 +123,15 @@ export default class TheSidebar extends Mixins(BaseMixin) {
|
|||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
showInNavi(route: AppRoute): boolean {
|
get mobileLogoClass() {
|
||||||
if (['shutdown', 'error', 'disconnected'].includes(this.klippy_state) && !route.alwaysShow) return false
|
const output = ['sidebar-logo', 'no-text-decoration', 'no-background', 'no-border']
|
||||||
else if (route.title === 'Webcam' && !this.boolNaviWebcam) return false
|
|
||||||
else if (route.moonrakerComponent && !this.moonrakerComponents.includes(route.moonrakerComponent)) return false
|
|
||||||
else if (route.registeredDirectory && !this.registeredDirectories.includes(route.registeredDirectory))
|
|
||||||
return false
|
|
||||||
else if (route.klipperComponent && !(route.klipperComponent in this.klipperConfigfileSettings)) return false
|
|
||||||
else if (route.klipperIsConnected && !this.klippyIsConnected) return false
|
|
||||||
|
|
||||||
return true
|
if (this.navigationStyle === 'iconsOnly') {
|
||||||
|
output.push('pa-0')
|
||||||
|
output.push('justify-center')
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -272,3 +139,30 @@ export default class TheSidebar extends Mixins(BaseMixin) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.no-text-decoration {
|
||||||
|
text-decoration: none;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-background:before {
|
||||||
|
background-color: rgba(255, 255, 255, 0) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-border {
|
||||||
|
border: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-logo {
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item-icon {
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-scrollbar {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
172
src/components/mixins/navigation.ts
Normal file
172
src/components/mixins/navigation.ts
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
import Component from 'vue-class-component'
|
||||||
|
import routes, { AppRoute } from '@/routes'
|
||||||
|
import { Mixins, Watch } from 'vue-property-decorator'
|
||||||
|
import { mdiLinkVariant, mdiViewDashboardOutline } from '@mdi/js'
|
||||||
|
import BaseMixin from '@/components/mixins/base'
|
||||||
|
import { PrinterStateKlipperConfig } from '@/store/printer/types'
|
||||||
|
import { GuiNavigationStateEntry } from '@/store/gui/navigation/types'
|
||||||
|
|
||||||
|
export interface NaviPoint {
|
||||||
|
type: 'link' | 'route'
|
||||||
|
title: string
|
||||||
|
to?: string
|
||||||
|
href?: string
|
||||||
|
target?: string
|
||||||
|
icon: string
|
||||||
|
position: number
|
||||||
|
visible: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
export default class NavigationMixin extends Mixins(BaseMixin) {
|
||||||
|
private customNaviLinks: NaviPoint[] = []
|
||||||
|
|
||||||
|
get countPrinters() {
|
||||||
|
return this.$store.getters['farm/countPrinters']
|
||||||
|
}
|
||||||
|
|
||||||
|
get routesNaviPoints(): NaviPoint[] {
|
||||||
|
const points: NaviPoint[] = []
|
||||||
|
|
||||||
|
if (this.countPrinters) {
|
||||||
|
points.push({
|
||||||
|
title: this.$t('App.Printers'),
|
||||||
|
icon: mdiViewDashboardOutline,
|
||||||
|
to: '/allPrinters',
|
||||||
|
position: 0,
|
||||||
|
} as NaviPoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
routes
|
||||||
|
.filter((element) => {
|
||||||
|
return element.showInNavi && this.showInNavi(element)
|
||||||
|
})
|
||||||
|
.forEach((element) => {
|
||||||
|
const [position, visible] = this.getUiSettings({
|
||||||
|
type: 'route',
|
||||||
|
title: element.title ?? 'unknown',
|
||||||
|
visible: true,
|
||||||
|
position: element.position ?? 999,
|
||||||
|
})
|
||||||
|
|
||||||
|
points.push({
|
||||||
|
type: 'route',
|
||||||
|
title: this.$t(`Router.${element.title}`),
|
||||||
|
icon: element.icon,
|
||||||
|
to: element.path,
|
||||||
|
position,
|
||||||
|
visible,
|
||||||
|
} as NaviPoint)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (this.customNaviLinks.length) {
|
||||||
|
this.customNaviLinks.forEach((element) => {
|
||||||
|
const [position, visible] = this.getUiSettings({
|
||||||
|
type: 'link',
|
||||||
|
title: element.title ?? 'unknown',
|
||||||
|
visible: element.visible ?? true,
|
||||||
|
position: element.position ?? 999,
|
||||||
|
})
|
||||||
|
|
||||||
|
points.push({
|
||||||
|
type: 'link',
|
||||||
|
title: element.title,
|
||||||
|
icon: element.icon,
|
||||||
|
href: element.href,
|
||||||
|
target: element.target,
|
||||||
|
position,
|
||||||
|
visible,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return points
|
||||||
|
}
|
||||||
|
|
||||||
|
get naviPoints(): NaviPoint[] {
|
||||||
|
return this.routesNaviPoints.sort((a, b) => a.position - b.position)
|
||||||
|
}
|
||||||
|
|
||||||
|
get visibleNaviPoints(): NaviPoint[] {
|
||||||
|
return this.naviPoints.filter((entry) => entry.visible)
|
||||||
|
}
|
||||||
|
|
||||||
|
get uiSettings(): GuiNavigationStateEntry[] {
|
||||||
|
return this.$store.state.gui.navigation.entries
|
||||||
|
}
|
||||||
|
|
||||||
|
get klippy_state(): string {
|
||||||
|
return this.$store.state.server.klippy_state
|
||||||
|
}
|
||||||
|
|
||||||
|
get boolNaviWebcam(): boolean {
|
||||||
|
return this.$store.state.gui.uiSettings.boolWebcamNavi
|
||||||
|
}
|
||||||
|
|
||||||
|
get moonrakerComponents(): string[] {
|
||||||
|
return this.$store.state.server.components
|
||||||
|
}
|
||||||
|
|
||||||
|
get registeredDirectories(): string[] {
|
||||||
|
return this.$store.state.server.registered_directories
|
||||||
|
}
|
||||||
|
|
||||||
|
get klipperConfigfileSettings(): PrinterStateKlipperConfig[] {
|
||||||
|
return this.$store.state.printer.configfile?.settings ?? {}
|
||||||
|
}
|
||||||
|
|
||||||
|
get sidebarNaviFile(): string {
|
||||||
|
return this.$store.getters['files/getCustomNaviPoints']
|
||||||
|
}
|
||||||
|
|
||||||
|
get webcamCount(): number {
|
||||||
|
return this.$store.getters['gui/webcams/getWebcams'].length
|
||||||
|
}
|
||||||
|
|
||||||
|
@Watch('sidebarNaviFile', { immediate: true })
|
||||||
|
async sidebarNaviFileChanged(newVal: string) {
|
||||||
|
this.customNaviLinks = []
|
||||||
|
|
||||||
|
// stop if no file is set
|
||||||
|
if (!newVal) return
|
||||||
|
|
||||||
|
const content = await fetch(newVal)
|
||||||
|
.then((res) => res.json())
|
||||||
|
.catch((err) => {
|
||||||
|
window.console.error('Unable to parse .theme/navi.json.')
|
||||||
|
throw err
|
||||||
|
})
|
||||||
|
|
||||||
|
content.forEach((item: NaviPoint) => {
|
||||||
|
this.customNaviLinks.push({
|
||||||
|
title: item.title ?? 'Unknown',
|
||||||
|
icon: item.icon ?? mdiLinkVariant,
|
||||||
|
href: item.href ?? '#',
|
||||||
|
target: item.target ?? undefined,
|
||||||
|
position: item.position ?? 999,
|
||||||
|
} as NaviPoint)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
showInNavi(route: AppRoute): boolean {
|
||||||
|
if (['shutdown', 'error', 'disconnected'].includes(this.klippy_state) && !route.alwaysShow) return false
|
||||||
|
else if (route.title === 'Webcam' && this.webcamCount === 0) return false
|
||||||
|
else if (route.moonrakerComponent && !this.moonrakerComponents.includes(route.moonrakerComponent)) return false
|
||||||
|
else if (route.registeredDirectory && !this.registeredDirectories.includes(route.registeredDirectory))
|
||||||
|
return false
|
||||||
|
else if (route.klipperComponent && !(route.klipperComponent in this.klipperConfigfileSettings)) return false
|
||||||
|
else if (route.klipperIsConnected && !this.klippyIsConnected) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
getUiSettings(entry: GuiNavigationStateEntry): [number, boolean] {
|
||||||
|
const index = this.uiSettings.findIndex((point) => {
|
||||||
|
return point.title === entry.title && point.type === entry.type
|
||||||
|
})
|
||||||
|
|
||||||
|
if (index === -1) return [entry.position, entry.visible]
|
||||||
|
|
||||||
|
return [this.uiSettings[index].position, this.uiSettings[index].visible]
|
||||||
|
}
|
||||||
|
}
|
53
src/components/settings/SettingsNavigationTab.vue
Normal file
53
src/components/settings/SettingsNavigationTab.vue
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<v-card-text>
|
||||||
|
<h3 class="text-h5 mb-3">{{ $t('Settings.NavigationTab.Navigation') }}</h3>
|
||||||
|
<draggable v-model="sortableNaviPoints" handle=".handle" ghost-class="ghost" group="navigation-points">
|
||||||
|
<settings-navigation-tab-item
|
||||||
|
v-for="(naviPoint, index) in sortableNaviPoints"
|
||||||
|
:key="index"
|
||||||
|
class="dragable-item my-2 mx-0"
|
||||||
|
:navi-point="naviPoint" />
|
||||||
|
</draggable>
|
||||||
|
</v-card-text>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Mixins } from 'vue-property-decorator'
|
||||||
|
import BaseMixin from '@/components/mixins/base'
|
||||||
|
import NavigationMixin, { NaviPoint } from '@/components/mixins/navigation'
|
||||||
|
import SettingsRow from '@/components/settings/SettingsRow.vue'
|
||||||
|
import draggable from 'vuedraggable'
|
||||||
|
import SettingsNavigationTabItem from '@/components/settings/SettingsNavigationTabItem.vue'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: { SettingsNavigationTabItem, SettingsRow, draggable },
|
||||||
|
})
|
||||||
|
export default class SettingsNavigationTab extends Mixins(NavigationMixin, BaseMixin) {
|
||||||
|
get sortableNaviPoints() {
|
||||||
|
return this.naviPoints.filter((naviPoint) => naviPoint.position > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
set sortableNaviPoints(newVal: NaviPoint[]) {
|
||||||
|
// update store with new positions
|
||||||
|
newVal.forEach((naviPoint, index) => {
|
||||||
|
this.$store.dispatch('gui/navigation/updatePos', {
|
||||||
|
type: naviPoint.type,
|
||||||
|
title: naviPoint.title,
|
||||||
|
visible: naviPoint.visible,
|
||||||
|
position: index + 1,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// upload to moonraker db
|
||||||
|
this.$store.dispatch('gui/navigation/upload')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.dragable-item {
|
||||||
|
background-color: #282828;
|
||||||
|
}
|
||||||
|
</style>
|
62
src/components/settings/SettingsNavigationTabItem.vue
Normal file
62
src/components/settings/SettingsNavigationTabItem.vue
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<template>
|
||||||
|
<v-row class="dragable-item my-2 mx-0">
|
||||||
|
<v-col class="col-auto pr-0 d-flex py-2">
|
||||||
|
<v-icon class="handle">{{ mdiDragVertical }}</v-icon>
|
||||||
|
</v-col>
|
||||||
|
<v-col class="py-2">
|
||||||
|
<settings-row :title="title" :sub-title="subtitle" :dynamic-slot-width="true">
|
||||||
|
<v-icon :color="checkboxColor" v-html="checkboxIcon" @click="changeVisibility" />
|
||||||
|
</settings-row>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Mixins, Prop } from 'vue-property-decorator'
|
||||||
|
import BaseMixin from '@/components/mixins/base'
|
||||||
|
import NavigationMixin, { NaviPoint } from '@/components/mixins/navigation'
|
||||||
|
import SettingsRow from '@/components/settings/SettingsRow.vue'
|
||||||
|
import draggable from 'vuedraggable'
|
||||||
|
import { mdiDragVertical, mdiCheckboxMarked, mdiCheckboxBlankOutline } from '@mdi/js'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: { SettingsRow, draggable },
|
||||||
|
})
|
||||||
|
export default class SettingsNavigationTab extends Mixins(NavigationMixin, BaseMixin) {
|
||||||
|
mdiDragVertical = mdiDragVertical
|
||||||
|
|
||||||
|
@Prop({ type: Object, required: true }) naviPoint!: NaviPoint
|
||||||
|
|
||||||
|
get title() {
|
||||||
|
return this.naviPoint.title
|
||||||
|
}
|
||||||
|
|
||||||
|
get subtitle() {
|
||||||
|
if (this.naviPoint.type === 'link') return `URL: ${this.naviPoint.href ?? 'Unknown'}`
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
get checkboxColor() {
|
||||||
|
if (this.naviPoint.visible) return 'primary'
|
||||||
|
|
||||||
|
return 'grey lighten-1'
|
||||||
|
}
|
||||||
|
|
||||||
|
get checkboxIcon() {
|
||||||
|
if (this.naviPoint.visible) return mdiCheckboxMarked
|
||||||
|
|
||||||
|
return mdiCheckboxBlankOutline
|
||||||
|
}
|
||||||
|
|
||||||
|
changeVisibility() {
|
||||||
|
this.$store.dispatch('gui/navigation/changeVisibility', this.naviPoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.dragable-item {
|
||||||
|
background-color: #282828;
|
||||||
|
}
|
||||||
|
</style>
|
@ -65,10 +65,6 @@
|
|||||||
<v-switch v-model="boolBigThumbnail" hide-details class="mt-0"></v-switch>
|
<v-switch v-model="boolBigThumbnail" hide-details class="mt-0"></v-switch>
|
||||||
</settings-row>
|
</settings-row>
|
||||||
<v-divider class="my-2"></v-divider>
|
<v-divider class="my-2"></v-divider>
|
||||||
<settings-row :title="$t('Settings.UiSettingsTab.ShowWebcamInNavigation').toString()">
|
|
||||||
<v-switch v-model="boolWebcamInNavigation" hide-details class="mt-0"></v-switch>
|
|
||||||
</settings-row>
|
|
||||||
<v-divider class="my-2"></v-divider>
|
|
||||||
<settings-row
|
<settings-row
|
||||||
:title="$t('Settings.UiSettingsTab.DisplayCANCEL_PRINT').toString()"
|
:title="$t('Settings.UiSettingsTab.DisplayCANCEL_PRINT').toString()"
|
||||||
:sub-title="$t('Settings.UiSettingsTab.DisplayCANCEL_PRINTDescription').toString()"
|
:sub-title="$t('Settings.UiSettingsTab.DisplayCANCEL_PRINTDescription').toString()"
|
||||||
@ -215,14 +211,6 @@ export default class SettingsUiSettingsTab extends Mixins(BaseMixin) {
|
|||||||
this.$store.dispatch('gui/saveSetting', { name: 'uiSettings.boolBigThumbnail', value: newVal })
|
this.$store.dispatch('gui/saveSetting', { name: 'uiSettings.boolBigThumbnail', value: newVal })
|
||||||
}
|
}
|
||||||
|
|
||||||
get boolWebcamInNavigation() {
|
|
||||||
return this.$store.state.gui.uiSettings.boolWebcamNavi ?? false
|
|
||||||
}
|
|
||||||
|
|
||||||
set boolWebcamInNavigation(newVal) {
|
|
||||||
this.$store.dispatch('gui/saveSetting', { name: 'uiSettings.boolWebcamNavi', value: newVal })
|
|
||||||
}
|
|
||||||
|
|
||||||
get displayCancelPrint() {
|
get displayCancelPrint() {
|
||||||
return this.$store.state.gui.uiSettings.displayCancelPrint
|
return this.$store.state.gui.uiSettings.displayCancelPrint
|
||||||
}
|
}
|
||||||
|
89
src/components/ui/SidebarItem.vue
Normal file
89
src/components/ui/SidebarItem.vue
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<v-tooltip right :open-delay="500" :disabled="navigationStyle !== 'iconsOnly'">
|
||||||
|
<template #activator="{ on, attrs }">
|
||||||
|
<v-list-item
|
||||||
|
router
|
||||||
|
:to="to"
|
||||||
|
:href="href"
|
||||||
|
:target="target"
|
||||||
|
class="small-list-item"
|
||||||
|
v-bind="attrs"
|
||||||
|
v-on="on">
|
||||||
|
<v-list-item-icon class="my-3 mr-3 menu-item-icon">
|
||||||
|
<v-icon>{{ icon }}</v-icon>
|
||||||
|
</v-list-item-icon>
|
||||||
|
<v-list-item-content>
|
||||||
|
<v-list-item-title tile class="menu-item-title">
|
||||||
|
{{ title }}
|
||||||
|
</v-list-item-title>
|
||||||
|
</v-list-item-content>
|
||||||
|
</v-list-item>
|
||||||
|
</template>
|
||||||
|
<span>{{ title }}</span>
|
||||||
|
</v-tooltip>
|
||||||
|
<v-divider v-if="borderBottom" class="my-1" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Component from 'vue-class-component'
|
||||||
|
import { Mixins, Prop } from 'vue-property-decorator'
|
||||||
|
import BaseMixin from '@/components/mixins/base'
|
||||||
|
import { NaviPoint } from '@/components/mixins/navigation'
|
||||||
|
|
||||||
|
@Component
|
||||||
|
export default class SidebarItem extends Mixins(BaseMixin) {
|
||||||
|
@Prop({ type: Object, required: true }) item!: NaviPoint
|
||||||
|
|
||||||
|
get navigationStyle() {
|
||||||
|
return this.$store.state.gui.uiSettings.navigationStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
get icon() {
|
||||||
|
return this.item.icon
|
||||||
|
}
|
||||||
|
|
||||||
|
get title() {
|
||||||
|
return this.item.title
|
||||||
|
}
|
||||||
|
|
||||||
|
get to() {
|
||||||
|
return this.item.to ?? undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
get href() {
|
||||||
|
return this.item.href ?? undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
get target() {
|
||||||
|
return this.item.target ?? undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
get borderBottom() {
|
||||||
|
return this.item.to === '/allPrinters'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.small-list-item {
|
||||||
|
height: var(--sidebar-menu-item-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-nav-item {
|
||||||
|
border-right: 4px solid var(--v-primary-base);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item-icon {
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item-title {
|
||||||
|
line-height: 30px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
</style>
|
@ -911,6 +911,9 @@
|
|||||||
"UnableToLoadLight": "Unable to load light",
|
"UnableToLoadLight": "Unable to load light",
|
||||||
"UnableToLoadPreset": "Unable to load preset"
|
"UnableToLoadPreset": "Unable to load preset"
|
||||||
},
|
},
|
||||||
|
"NavigationTab": {
|
||||||
|
"Navigation": "Navigation"
|
||||||
|
},
|
||||||
"PresetsTab": {
|
"PresetsTab": {
|
||||||
"AddPreset": "add preset",
|
"AddPreset": "add preset",
|
||||||
"Cooldown": "Cooldown",
|
"Cooldown": "Cooldown",
|
||||||
@ -1036,7 +1039,6 @@
|
|||||||
"PowerDeviceName": "Printer power device",
|
"PowerDeviceName": "Printer power device",
|
||||||
"PowerDeviceNameDescription": "Select which Moonraker power device should be used to power on the printer.",
|
"PowerDeviceNameDescription": "Select which Moonraker power device should be used to power on the printer.",
|
||||||
"Primary": "Primary",
|
"Primary": "Primary",
|
||||||
"ShowWebcamInNavigation": "Show Webcam in navigation",
|
|
||||||
"UiSettings": "UI-Settings"
|
"UiSettings": "UI-Settings"
|
||||||
},
|
},
|
||||||
"Update": "update",
|
"Update": "update",
|
||||||
|
@ -28,6 +28,7 @@ const routes: AppRoute[] = [
|
|||||||
component: Dashboard,
|
component: Dashboard,
|
||||||
alwaysShow: true,
|
alwaysShow: true,
|
||||||
showInNavi: true,
|
showInNavi: true,
|
||||||
|
position: 10,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Printers',
|
title: 'Printers',
|
||||||
@ -43,6 +44,7 @@ const routes: AppRoute[] = [
|
|||||||
component: Webcam,
|
component: Webcam,
|
||||||
alwaysShow: true,
|
alwaysShow: true,
|
||||||
showInNavi: true,
|
showInNavi: true,
|
||||||
|
position: 20,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Console',
|
title: 'Console',
|
||||||
@ -52,6 +54,7 @@ const routes: AppRoute[] = [
|
|||||||
alwaysShow: true,
|
alwaysShow: true,
|
||||||
showInNavi: true,
|
showInNavi: true,
|
||||||
klipperIsConnected: true,
|
klipperIsConnected: true,
|
||||||
|
position: 30,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Heightmap',
|
title: 'Heightmap',
|
||||||
@ -61,6 +64,7 @@ const routes: AppRoute[] = [
|
|||||||
alwaysShow: false,
|
alwaysShow: false,
|
||||||
showInNavi: true,
|
showInNavi: true,
|
||||||
klipperComponent: 'bed_mesh',
|
klipperComponent: 'bed_mesh',
|
||||||
|
position: 40,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'G-Code Files',
|
title: 'G-Code Files',
|
||||||
@ -70,6 +74,7 @@ const routes: AppRoute[] = [
|
|||||||
alwaysShow: true,
|
alwaysShow: true,
|
||||||
showInNavi: true,
|
showInNavi: true,
|
||||||
registeredDirectory: 'gcodes',
|
registeredDirectory: 'gcodes',
|
||||||
|
position: 50,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'G-Code Viewer',
|
title: 'G-Code Viewer',
|
||||||
@ -78,6 +83,7 @@ const routes: AppRoute[] = [
|
|||||||
component: () => import('../pages/Viewer.vue'),
|
component: () => import('../pages/Viewer.vue'),
|
||||||
alwaysShow: true,
|
alwaysShow: true,
|
||||||
showInNavi: true,
|
showInNavi: true,
|
||||||
|
position: 60,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'History',
|
title: 'History',
|
||||||
@ -87,6 +93,7 @@ const routes: AppRoute[] = [
|
|||||||
alwaysShow: true,
|
alwaysShow: true,
|
||||||
showInNavi: true,
|
showInNavi: true,
|
||||||
moonrakerComponent: 'history',
|
moonrakerComponent: 'history',
|
||||||
|
position: 70,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Timelapse',
|
title: 'Timelapse',
|
||||||
@ -96,6 +103,7 @@ const routes: AppRoute[] = [
|
|||||||
alwaysShow: true,
|
alwaysShow: true,
|
||||||
showInNavi: true,
|
showInNavi: true,
|
||||||
moonrakerComponent: 'timelapse',
|
moonrakerComponent: 'timelapse',
|
||||||
|
position: 80,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Machine',
|
title: 'Machine',
|
||||||
@ -104,6 +112,7 @@ const routes: AppRoute[] = [
|
|||||||
component: Machine,
|
component: Machine,
|
||||||
alwaysShow: true,
|
alwaysShow: true,
|
||||||
showInNavi: true,
|
showInNavi: true,
|
||||||
|
position: 90,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: null,
|
title: null,
|
||||||
@ -130,4 +139,5 @@ export interface AppRoute {
|
|||||||
klipperComponent?: string
|
klipperComponent?: string
|
||||||
klipperIsConnected?: boolean
|
klipperIsConnected?: boolean
|
||||||
children?: AppRoute[]
|
children?: AppRoute[]
|
||||||
|
position?: number
|
||||||
}
|
}
|
||||||
|
@ -199,11 +199,14 @@ export const getters: GetterTree<FileState, any> = {
|
|||||||
|
|
||||||
const file = directory?.childrens?.find(
|
const file = directory?.childrens?.find(
|
||||||
(element: FileStateFile) =>
|
(element: FileStateFile) =>
|
||||||
element.filename !== undefined &&
|
element.filename?.slice(0, element.filename?.lastIndexOf('.')) === acceptName &&
|
||||||
element.filename.substr(0, element.filename.lastIndexOf('.')) === acceptName &&
|
acceptExtensions.includes(element.filename?.slice(element.filename?.lastIndexOf('.') + 1))
|
||||||
acceptExtensions.includes(element.filename.substr(element.filename.lastIndexOf('.') + 1))
|
|
||||||
)
|
)
|
||||||
return file ? rootGetters['socket/getUrl'] + '/server/files/config/' + themeDir + '/' + file.filename : null
|
if (!file) return null
|
||||||
|
|
||||||
|
return `${rootGetters['socket/getUrl']}/server/files/config/${themeDir}/${
|
||||||
|
file.filename
|
||||||
|
}?timestamp=${file.modified.getTime()}`
|
||||||
},
|
},
|
||||||
|
|
||||||
getSidebarLogo: (state, getters) => {
|
getSidebarLogo: (state, getters) => {
|
||||||
@ -234,6 +237,13 @@ export const getters: GetterTree<FileState, any> = {
|
|||||||
return getters['getThemeFileUrl'](acceptName, acceptExtensions) ?? null
|
return getters['getThemeFileUrl'](acceptName, acceptExtensions) ?? null
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getCustomNaviPoints: (state, getters) => {
|
||||||
|
const acceptName = 'navi'
|
||||||
|
const acceptExtensions = ['json']
|
||||||
|
|
||||||
|
return getters['getThemeFileUrl'](acceptName, acceptExtensions) ?? null
|
||||||
|
},
|
||||||
|
|
||||||
getCustomFavicons: (state, getters) => {
|
getCustomFavicons: (state, getters) => {
|
||||||
const acceptName16 = 'favicon-32x32'
|
const acceptName16 = 'favicon-32x32'
|
||||||
const acceptName32 = 'favicon-32x32'
|
const acceptName32 = 'favicon-32x32'
|
||||||
|
@ -10,10 +10,11 @@ import { console } from '@/store/gui/console'
|
|||||||
import { gcodehistory } from '@/store/gui/gcodehistory'
|
import { gcodehistory } from '@/store/gui/gcodehistory'
|
||||||
import { macros } from '@/store/gui/macros'
|
import { macros } from '@/store/gui/macros'
|
||||||
import { miscellaneous } from '@/store/gui/miscellaneous'
|
import { miscellaneous } from '@/store/gui/miscellaneous'
|
||||||
|
import { navigation } from '@/store/gui/navigation'
|
||||||
|
import { notifications } from '@/store/gui/notifications'
|
||||||
import { presets } from '@/store/gui/presets'
|
import { presets } from '@/store/gui/presets'
|
||||||
import { remoteprinters } from '@/store/gui/remoteprinters'
|
import { remoteprinters } from '@/store/gui/remoteprinters'
|
||||||
import { webcams } from '@/store/gui/webcams'
|
import { webcams } from '@/store/gui/webcams'
|
||||||
import { notifications } from '@/store/gui/notifications'
|
|
||||||
|
|
||||||
export const getDefaultState = (): GuiState => {
|
export const getDefaultState = (): GuiState => {
|
||||||
return {
|
return {
|
||||||
@ -141,6 +142,9 @@ export const getDefaultState = (): GuiState => {
|
|||||||
showGCodePanel: false,
|
showGCodePanel: false,
|
||||||
cncMode: false,
|
cncMode: false,
|
||||||
},
|
},
|
||||||
|
navigation: {
|
||||||
|
entries: [],
|
||||||
|
},
|
||||||
uiSettings: {
|
uiSettings: {
|
||||||
logo: defaultLogoColor,
|
logo: defaultLogoColor,
|
||||||
primary: defaultPrimaryColor,
|
primary: defaultPrimaryColor,
|
||||||
@ -152,7 +156,6 @@ export const getDefaultState = (): GuiState => {
|
|||||||
boolBigThumbnail: true,
|
boolBigThumbnail: true,
|
||||||
boolWideNavDrawer: false,
|
boolWideNavDrawer: false,
|
||||||
boolHideUploadAndPrintButton: false,
|
boolHideUploadAndPrintButton: false,
|
||||||
boolWebcamNavi: false,
|
|
||||||
navigationStyle: 'iconsAndText',
|
navigationStyle: 'iconsAndText',
|
||||||
powerDeviceName: null,
|
powerDeviceName: null,
|
||||||
hideSaveConfigForBedMash: false,
|
hideSaveConfigForBedMash: false,
|
||||||
@ -263,6 +266,7 @@ export const gui: Module<GuiState, any> = {
|
|||||||
gcodehistory,
|
gcodehistory,
|
||||||
macros,
|
macros,
|
||||||
miscellaneous,
|
miscellaneous,
|
||||||
|
navigation,
|
||||||
notifications,
|
notifications,
|
||||||
presets,
|
presets,
|
||||||
remoteprinters,
|
remoteprinters,
|
||||||
|
27
src/store/gui/navigation/actions.ts
Normal file
27
src/store/gui/navigation/actions.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { ActionTree } from 'vuex'
|
||||||
|
import { RootState } from '@/store/types'
|
||||||
|
import Vue from 'vue'
|
||||||
|
import { GuiNavigationState, GuiNavigationStateEntry } from '@/store/gui/navigation/types'
|
||||||
|
|
||||||
|
export const actions: ActionTree<GuiNavigationState, RootState> = {
|
||||||
|
reset({ commit }) {
|
||||||
|
commit('reset')
|
||||||
|
},
|
||||||
|
|
||||||
|
upload({ state }) {
|
||||||
|
Vue.$socket.emit('server.database.post_item', {
|
||||||
|
namespace: 'mainsail',
|
||||||
|
key: 'navigation.entries',
|
||||||
|
value: state.entries,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
updatePos({ commit }, payload: GuiNavigationStateEntry) {
|
||||||
|
commit('updatePos', payload)
|
||||||
|
},
|
||||||
|
|
||||||
|
changeVisibility({ commit, dispatch }, payload: GuiNavigationStateEntry) {
|
||||||
|
commit('changeVisibility', payload)
|
||||||
|
dispatch('upload')
|
||||||
|
},
|
||||||
|
}
|
5
src/store/gui/navigation/getters.ts
Normal file
5
src/store/gui/navigation/getters.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { GetterTree } from 'vuex'
|
||||||
|
import { GuiNavigationState } from './types'
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
export const getters: GetterTree<GuiNavigationState, any> = {}
|
23
src/store/gui/navigation/index.ts
Normal file
23
src/store/gui/navigation/index.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { Module } from 'vuex'
|
||||||
|
import { actions } from '@/store/gui/navigation/actions'
|
||||||
|
import { mutations } from '@/store/gui/navigation/mutations'
|
||||||
|
import { getters } from '@/store/gui/navigation/getters'
|
||||||
|
import { GuiNavigationState } from '@/store/gui/navigation/types'
|
||||||
|
|
||||||
|
export const getDefaultState = (): GuiNavigationState => {
|
||||||
|
return {
|
||||||
|
entries: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// initial state
|
||||||
|
const state = getDefaultState()
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
export const navigation: Module<GuiNavigationState, any> = {
|
||||||
|
namespaced: true,
|
||||||
|
state,
|
||||||
|
getters,
|
||||||
|
actions,
|
||||||
|
mutations,
|
||||||
|
}
|
62
src/store/gui/navigation/mutations.ts
Normal file
62
src/store/gui/navigation/mutations.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { getDefaultState } from './index'
|
||||||
|
import { MutationTree } from 'vuex'
|
||||||
|
import { GuiNavigationState, GuiNavigationStateEntry } from './types'
|
||||||
|
import Vue from 'vue'
|
||||||
|
|
||||||
|
export const mutations: MutationTree<GuiNavigationState> = {
|
||||||
|
reset(state) {
|
||||||
|
Object.assign(state, getDefaultState())
|
||||||
|
},
|
||||||
|
|
||||||
|
updatePos(state, payload: GuiNavigationStateEntry) {
|
||||||
|
const index = state.entries.findIndex((entry) => {
|
||||||
|
return entry.type === payload.type && entry.title === payload.title
|
||||||
|
})
|
||||||
|
|
||||||
|
// update existing entry
|
||||||
|
if (index !== -1) {
|
||||||
|
state.entries[index].position = payload.position
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new entry
|
||||||
|
const newEntry: GuiNavigationStateEntry = {
|
||||||
|
type: payload.type,
|
||||||
|
title: payload.title,
|
||||||
|
visible: payload.visible,
|
||||||
|
position: payload.position,
|
||||||
|
}
|
||||||
|
// copy old array
|
||||||
|
const entries = [...state.entries]
|
||||||
|
// add new entry
|
||||||
|
entries.push(newEntry)
|
||||||
|
// set new array
|
||||||
|
Vue.set(state, 'entries', entries)
|
||||||
|
},
|
||||||
|
|
||||||
|
changeVisibility(state, payload: GuiNavigationStateEntry) {
|
||||||
|
const index = state.entries.findIndex((entry) => {
|
||||||
|
return entry.type === payload.type && entry.title === payload.title
|
||||||
|
})
|
||||||
|
|
||||||
|
// update existing entry
|
||||||
|
if (index !== -1) {
|
||||||
|
state.entries[index].visible = !payload.visible
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new entry
|
||||||
|
const newEntry: GuiNavigationStateEntry = {
|
||||||
|
type: payload.type,
|
||||||
|
title: payload.title,
|
||||||
|
visible: !payload.visible,
|
||||||
|
position: payload.position,
|
||||||
|
}
|
||||||
|
// copy old array
|
||||||
|
const entries = [...state.entries]
|
||||||
|
// add new entry
|
||||||
|
entries.push(newEntry)
|
||||||
|
// set new array
|
||||||
|
Vue.set(state, 'entries', entries)
|
||||||
|
},
|
||||||
|
}
|
10
src/store/gui/navigation/types.ts
Normal file
10
src/store/gui/navigation/types.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export interface GuiNavigationState {
|
||||||
|
entries: GuiNavigationStateEntry[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GuiNavigationStateEntry {
|
||||||
|
type: 'route' | 'link'
|
||||||
|
title: string
|
||||||
|
visible: boolean
|
||||||
|
position: number
|
||||||
|
}
|
@ -5,6 +5,7 @@ import { GuiRemoteprintersState } from '@/store/gui/remoteprinters/types'
|
|||||||
import { ServerHistoryStateJob } from '@/store/server/history/types'
|
import { ServerHistoryStateJob } from '@/store/server/history/types'
|
||||||
import { GuiNotificationState } from '@/store/gui/notifications/types'
|
import { GuiNotificationState } from '@/store/gui/notifications/types'
|
||||||
import { FileStateFile, FileStateGcodefile } from '@/store/files/types'
|
import { FileStateFile, FileStateGcodefile } from '@/store/files/types'
|
||||||
|
import { GuiNavigationState } from '@/store/gui/navigation/types'
|
||||||
|
|
||||||
export interface GuiState {
|
export interface GuiState {
|
||||||
general: {
|
general: {
|
||||||
@ -90,6 +91,7 @@ export interface GuiState {
|
|||||||
cncMode: boolean
|
cncMode: boolean
|
||||||
}
|
}
|
||||||
macros?: GuiMacrosState
|
macros?: GuiMacrosState
|
||||||
|
navigation: GuiNavigationState
|
||||||
notifications?: GuiNotificationState
|
notifications?: GuiNotificationState
|
||||||
presets?: GuiPresetsState
|
presets?: GuiPresetsState
|
||||||
remoteprinters?: GuiRemoteprintersState
|
remoteprinters?: GuiRemoteprintersState
|
||||||
@ -104,7 +106,6 @@ export interface GuiState {
|
|||||||
boolBigThumbnail: boolean
|
boolBigThumbnail: boolean
|
||||||
boolWideNavDrawer: boolean
|
boolWideNavDrawer: boolean
|
||||||
boolHideUploadAndPrintButton: boolean
|
boolHideUploadAndPrintButton: boolean
|
||||||
boolWebcamNavi: boolean
|
|
||||||
navigationStyle: 'iconsAndText' | 'iconsOnly'
|
navigationStyle: 'iconsAndText' | 'iconsOnly'
|
||||||
powerDeviceName: string | null
|
powerDeviceName: string | null
|
||||||
hideSaveConfigForBedMash: boolean
|
hideSaveConfigForBedMash: boolean
|
||||||
|
Loading…
x
Reference in New Issue
Block a user