added top bar menus + multiple fixes/tweaks

This commit is contained in:
Elvanos 2023-09-28 23:45:18 +02:00
parent f08d119bca
commit d56819ddd5
20 changed files with 666 additions and 9 deletions

View file

@ -0,0 +1,55 @@
export interface I_appMenusDataList {
/**
* Title of the main menu button
*/
title: string
/**
* Data contents of the menu dropdown
*/
data:{
/**
* Determines if the item shows as a separator or full-fledged menu item
*/
mode: 'separator' | 'item'
/**
* Title of the menu item
*/
text?: string
/**
* Icon/Avatar of the menu item
*/
icon?: string
/**
* Trigger functionality of the item on click
*/
trigger?: (() => unknown)
/**
* Conditions to show the menu item as active
*/
conditions?: boolean
/**
*Special color class for the item
*/
specialColor?: string
/**
* Determined if the item contains a submenu and its inner data
*/
submenu?: {
mode: 'separator' | 'item'
text?: string
icon?: string
trigger?: (() => unknown)
conditions?: boolean
specialColor?: string
}[]
}[]
}

View file

@ -101,6 +101,7 @@ module.exports = configure(function (/* ctx */) {
framework: {
config: {
ripple: false,
dark: true,
},
// iconSet: 'material-icons', // Quasar icon set
@ -114,7 +115,7 @@ module.exports = configure(function (/* ctx */) {
// directives: [],
// Quasar plugins
plugins: [],
plugins: ["Dialog"],
},
// animations: 'all', // --- includes all animations

View file

@ -0,0 +1,37 @@
<template>
<div
class="appControlMenus bg-dark"
>
<q-btn-group
flat
class="appControlMenus__inner"
>
<!-- Project menu-->
<AppControlSingleMenu :data-input="project" />
<!-- Tools menu-->
<AppControlSingleMenu :data-input="tools" />
<!-- Help & Info Menu-->
<AppControlSingleMenu :data-input="helpInfo" />
</q-btn-group>
</div>
</template>
<script setup lang="ts">
import AppControlSingleMenu from 'src/components/AppControlMenus/AppControlSingleMenu/AppControlSingleMenu.vue'
import { project } from 'app/src/components/AppControlMenus/_data/project'
import { tools } from 'app/src/components/AppControlMenus/_data/tools'
import { helpInfo } from 'app/src/components/AppControlMenus/_data/helpInfo'
</script>
<style lang="scss" scoped>
.appControlMenus {
&__inner{
-webkit-app-region: no-drag;
}
}
</style>

View file

@ -0,0 +1,130 @@
<template>
<q-btn
v-if="hasProperDataInput"
flat
class="appControlSingleMenu non-selectable"
dark
size="md"
no-caps
>
{{ menuTitle }}
<!-- Button menu-->
<q-menu
anchor="bottom left"
square
dark
transition-show="jump-down"
transition-hide="jump-up"
>
<q-list
class="appControlSingleMenu__list"
dark
>
<template
v-for="(menuItem,index) in menuData"
:key="index"
>
<q-separator
v-if="menuItem.mode === 'separator'"
class="appControlSingleMenu__separator"
dark
/>
<q-item
v-if="menuItem.mode === 'item'"
v-close-popup="menuItem.submenu === undefined ? true : false"
clickable
:class="['appControlSingleMenu__item', `text-${menuItem.specialColor}`, 'non-selectable']"
:disable="(!menuItem.conditions)"
@click="(menuItem.trigger) ? menuItem.trigger() : false"
>
<q-item-section>{{ menuItem.text }}</q-item-section>
<q-item-section avatar>
<q-icon :name="menuItem.icon" />
</q-item-section>
<!-- Sub-menu-->
<q-menu
v-if="menuItem.submenu !== undefined"
anchor="top end"
self="top start"
square
dark
transition-show="jump-right"
transition-hide="jump-left"
>
<q-list
class="appControlSingleMenu__list"
dark
>
<template
v-for="(submenuItem,subIndex) in menuItem.submenu"
:key="subIndex"
>
<q-separator
v-if="submenuItem.mode === 'separator'"
class="appControlSingleMenu__separator"
dark
/>
<q-item
v-if="submenuItem.mode === 'item'"
v-close-popup
clickable
:class="['appControlSingleMenu__item', `text-${submenuItem.specialColor}`, 'non-selectable']"
:disable="(!submenuItem.conditions)"
@click="(submenuItem.trigger) ? submenuItem.trigger() : false"
>
<q-item-section>{{ submenuItem.text }}</q-item-section>
<q-item-section avatar>
<q-icon :name="submenuItem.icon" />
</q-item-section>
</q-item>
</template>
</q-list>
</q-menu>
<!-- Sub-menu end -->
</q-item>
</template>
</q-list>
</q-menu>
<!-- Main menu end -->
</q-btn>
</template>
<script setup lang="ts">
import { I_appMenusDataList } from 'app/interfaces/I_appMenusDataList'
const props = defineProps<{
dataInput: I_appMenusDataList
}>()
const hasProperDataInput = (props.dataInput.title && props.dataInput.data)
const menuTitle = props.dataInput.title
const menuData = props.dataInput.data
</script>
<style lang="scss" scoped>
.appControlSingleMenu {
&__list{
background-color: $appControlMenus_bgColor;
color: $appControlMenus_color;
}
&__item{
min-height: 42px;
&:hover{
color: $appControlMenus_singleHover;
}
}
&__separator{
background-color: $appControlMenus_separatorColor;
height: 0.5px !important;
}
}
</style>

View file

@ -0,0 +1,81 @@
import { i18n } from 'app/src/i18n/externalFileLoader'
import { I_appMenusDataList } from 'app/interfaces/I_appMenusDataList'
// TODO - add functionality for all buttons and conditions
import { toggleDevTools } from 'app/src/scripts/appInfo/toggleDevTools'
export const helpInfo: I_appMenusDataList = {
title: i18n.global.t('AppControlMenus.helpInfo.title'),
data: [
{
mode: 'item',
text: i18n.global.t('AppControlMenus.helpInfo.items.showKeybindsCheatsheet'),
icon: 'mdi-keyboard-settings',
submenu: undefined,
trigger: undefined,
conditions: true,
specialColor: undefined
},
{
mode: 'item',
text: i18n.global.t('AppControlMenus.helpInfo.items.advancedSearchGuide'),
icon: 'mdi-file-question',
submenu: undefined,
trigger: undefined,
conditions: true,
specialColor: undefined
},
{
mode: 'item',
text: i18n.global.t('AppControlMenus.helpInfo.items.tipsTricksTrivia'),
icon: 'mdi-fire-alert',
submenu: undefined,
trigger: undefined,
conditions: true,
specialColor: undefined
},
{
mode: 'separator'
},
{
mode: 'item',
text: i18n.global.t('AppControlMenus.helpInfo.items.changelog'),
icon: 'mdi-clipboard-text',
submenu: undefined,
trigger: undefined,
conditions: true,
specialColor: undefined
},
{
mode: 'item',
text: i18n.global.t('AppControlMenus.helpInfo.items.aboutFantsiaArchive'),
icon: 'mdi-information-variant',
submenu: undefined,
trigger: undefined,
conditions: true,
specialColor: undefined
},
{
mode: 'item',
text: i18n.global.t('AppControlMenus.helpInfo.items.license'),
icon: 'mdi-script-text-outline',
submenu: undefined,
trigger: undefined,
conditions: true,
specialColor: undefined
},
{
mode: 'separator'
},
{
mode: 'item',
text: i18n.global.t('AppControlMenus.helpInfo.items.toggleDeveloperTools'),
icon: 'mdi-code-tags',
submenu: undefined,
trigger: toggleDevTools,
conditions: true,
specialColor: undefined
}
]
}

View file

@ -0,0 +1,116 @@
import { i18n } from 'app/src/i18n/externalFileLoader'
import { I_appMenusDataList } from 'app/interfaces/I_appMenusDataList'
// TODO - add functionality for all buttons and conditions
export const project: I_appMenusDataList = {
title: i18n.global.t('AppControlMenus.project.title'),
data: [
{
mode: 'item',
text: i18n.global.t('AppControlMenus.project.items.newProject'),
icon: 'mdi-plus',
submenu: undefined,
trigger: undefined,
conditions: true,
specialColor: undefined
},
{
mode: 'separator'
},
{
mode: 'item',
text: i18n.global.t('AppControlMenus.project.items.saveProject'),
icon: 'mdi-package-variant-closed',
submenu: undefined,
trigger: undefined,
conditions: true,
specialColor: undefined
},
{
mode: 'item',
text: i18n.global.t('AppControlMenus.project.items.loadProject'),
icon: 'mdi-package-variant',
submenu: undefined,
trigger: undefined,
conditions: true,
specialColor: undefined
},
{
mode: 'separator'
},
{
mode: 'item',
text: i18n.global.t('AppControlMenus.project.items.exportProjectDocuments'),
icon: 'mdi-database-export-outline',
submenu: undefined,
trigger: undefined,
conditions: true,
specialColor: undefined
},
{
mode: 'separator'
},
{
mode: 'item',
text: i18n.global.t('AppControlMenus.project.items.showProjectDashboard'),
icon: 'mdi-chart-bar',
submenu: undefined,
trigger: undefined,
conditions: true,
specialColor: undefined
},
{
mode: 'item',
text: i18n.global.t('AppControlMenus.project.items.projectSettings'),
icon: 'mdi-book-cog-outline',
submenu: undefined,
trigger: undefined,
conditions: true,
specialColor: undefined
},
{
mode: 'item',
text: i18n.global.t('AppControlMenus.project.items.closeProject'),
icon: 'mdi-exit-to-app',
submenu: undefined,
trigger: undefined,
conditions: true,
specialColor: undefined
},
{
mode: 'separator'
},
{
mode: 'item',
text: i18n.global.t('AppControlMenus.project.items.advancedProjectTools'),
icon: 'keyboard_arrow_right',
trigger: undefined,
conditions: true,
specialColor: 'accent',
submenu: [
{
mode: 'item',
text: i18n.global.t('AppControlMenus.project.items.aptMerge'),
icon: 'mdi-folder-plus-outline',
trigger: undefined,
conditions: true,
specialColor: undefined
},
{
mode: 'separator'
},
{
mode: 'item',
text: i18n.global.t('AppControlMenus.project.items.aptConvertOld'),
icon: 'mdi-wrench',
trigger: undefined,
conditions: true,
specialColor: undefined
}
]
}
]
}

View file

@ -0,0 +1,75 @@
import { i18n } from 'app/src/i18n/externalFileLoader'
import { I_appMenusDataList } from 'app/interfaces/I_appMenusDataList'
// TODO - add functionality for all buttons and conditions
export const tools: I_appMenusDataList = {
title: i18n.global.t('AppControlMenus.tools.title'),
data: [
{
mode: 'item',
text: i18n.global.t('AppControlMenus.tools.items.quickAddNewDocument'),
icon: 'mdi-text-box-plus-outline',
submenu: undefined,
trigger: undefined,
conditions: true,
specialColor: undefined
},
{
mode: 'item',
text: i18n.global.t('AppControlMenus.tools.items.quickSearchDocument'),
icon: 'mdi-database-search',
submenu: undefined,
trigger: undefined,
conditions: true,
specialColor: undefined
},
{
mode: 'separator'
},
{
mode: 'item',
text: i18n.global.t('AppControlMenus.tools.items.massDeleteDocument'),
icon: 'mdi-text-box-remove-outline',
submenu: undefined,
trigger: undefined,
conditions: true,
specialColor: 'secondary'
},
{
mode: 'separator'
},
{
mode: 'item',
text: i18n.global.t('AppControlMenus.tools.items.toggleTree'),
icon: 'mdi-page-layout-sidebar-left',
submenu: undefined,
trigger: undefined,
conditions: true,
specialColor: undefined
},
{
mode: 'item',
text: i18n.global.t('AppControlMenus.tools.items.showNoteBoard'),
icon: 'mdi-clipboard-text-outline',
submenu: undefined,
trigger: undefined,
conditions: true,
specialColor: undefined
},
{
mode: 'separator'
},
{
mode: 'item',
text: i18n.global.t('AppControlMenus.tools.items.programSettings'),
icon: 'mdi-tune',
submenu: undefined,
trigger: undefined,
conditions: true,
specialColor: undefined
}
]
}

View file

@ -47,6 +47,7 @@ const props = defineProps({
type: String,
default: ''
},
/**
* Custom CSS string for the "height" attribute.
*/
@ -54,6 +55,7 @@ const props = defineProps({
type: String,
default: 'initial'
},
/**
* Custom CSS string for the "width" attribute.
*/
@ -103,6 +105,7 @@ const currentMascotImage = determineCurrentImage(fantasiaImageList)
&__inner{
height: v-bind(height);
width: v-bind(width);
user-select: none;
}
}

View file

@ -61,7 +61,7 @@
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { onMounted, onUnmounted, ref } from 'vue'
import type { Ref } from 'vue'
/**
@ -114,10 +114,25 @@ const checkIfWindowMaximized = () => {
const isMaximized: Ref<boolean> = ref(false)
/**
* Check on component mount if the windows if maximized or not
* Window interval checker variable
*/
let checkerInterval: number
/**
* Hook up a interval timer on mount for continuous checking
* This done due to the fact that dragging via the top header bar doesn't properly fire "drag" event
*/
onMounted(() => {
checkIfWindowMaximized()
checkerInterval = window.setInterval(() => {
checkIfWindowMaximized()
}, 300)
})
/**
*Unhook the interval timer on unmounting in order to prevent left-over intervals ticking in the app
*/
onUnmounted(() => {
window.clearInterval(checkerInterval)
})
</script>

View file

@ -2,3 +2,33 @@ html,
body{
overflow: hidden !important;
}
body{
&.body--light{
/* WebKit/Blink Browsers */
::selection {
color: $selectTextColor_lightMode;
background: $selectBackgroundColor_lightMode;
}
/* Gecko Browsers */
::-moz-selection {
color: $selectTextColor_lightMode;
background: $selectBackgroundColor_lightMode;
}
}
&.body--dark{
/* WebKit/Blink Browsers */
::selection {
color: $selectTextColor_darkMode;
background: $selectBackgroundColor_darkMode;
}
/* Gecko Browsers */
::-moz-selection {
color: $selectTextColor_darkMode;
background: $selectBackgroundColor_darkMode;
}
}
}

View file

@ -11,3 +11,13 @@
background-color: $qCard-background;
color: $qCard-text;
}
.q-menu--dark{
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2), 0 2px 2px rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.12) !important;
}
.q-transition--slide-right-enter-active, .q-transition--slide-right-leave-active, .q-transition--slide-left-enter-active, .q-transition--slide-left-leave-active, .q-transition--slide-up-enter-active, .q-transition--slide-up-leave-active, .q-transition--slide-down-enter-active, .q-transition--slide-down-leave-active, .q-transition--jump-right-enter-active, .q-transition--jump-right-leave-active, .q-transition--jump-left-enter-active, .q-transition--jump-left-leave-active, .q-transition--jump-up-enter-active, .q-transition--jump-up-leave-active, .q-transition--jump-down-enter-active, .q-transition--jump-down-leave-active, .q-transition--fade-enter-active, .q-transition--fade-leave-active, .q-transition--scale-enter-active, .q-transition--scale-leave-active, .q-transition--rotate-enter-active, .q-transition--rotate-leave-active, .q-transition--flip-enter-active, .q-transition--flip-leave-active{
user-select: none;
pointer-events: none;
}

View file

@ -4,16 +4,24 @@
$primary : #d7ac47;
$secondary : #f75746;
$accent : #dde4e4;
$accent : #f5f5f5;
$dark : #18303a;
$dark-page : #303742;
$dark-lighter: #234655;
$positive : #35a14e;
$negative : #c10015;
$info : lighten(#d7ac47, 35);
$warning : #f2c037;
// GLOBALS - GENERAL SELECT
$selectTextColor_darkMode: #eedbb0;
$selectBackgroundColor_darkMode: #bb3b32;
$selectTextColor_lightMode: #eedbb0;
$selectBackgroundColor_lightMode: #aa2a21;
// QUASAR COMPONENT - Q-CARD
$qCard-background: #f7eeda;
$qCard-text: $dark;
@ -24,3 +32,9 @@ $qCard-border-radius: 5px;
// COMPONENT - GLOBAL WINDOW BUTTONS
$globalWindowButtons_height: 35px;
$globalWindowButtons_color: $accent;
// COMPONENT - APP CONTROL MENUS
$appControlMenus_bgColor: $dark-lighter;
$appControlMenus_color: $accent;
$appControlMenus_separatorColor: $primary;
$appControlMenus_singleHover: $primary;

View file

@ -0,0 +1,12 @@
export default {
title: 'Help & Info',
items: {
showKeybindsCheatsheet: 'Show keybind cheatsheet',
advancedSearchGuide: 'Advanced search guide',
tipsTricksTrivia: 'Tips, Tricks & Trivia',
changelog: 'Changelog',
aboutFantsiaArchive: 'About Fantasia Archive',
license: 'License',
toggleDeveloperTools: 'Toggle developer tools'
}
}

View file

@ -0,0 +1,15 @@
export default {
title: 'Project',
items: {
newProject: 'New project',
saveProject: 'Save current project',
loadProject: 'Load existing project',
exportProjectDocuments: 'Export project/documents',
showProjectDashboard: 'Show project dashboard',
closeProject: 'Close project',
projectSettings: 'Project settings',
advancedProjectTools: 'Advanced project tools',
aptMerge: 'Merge another project into the current one',
aptConvertOld: 'Convert legacy project to FA 2.0'
}
}

View file

@ -0,0 +1,11 @@
export default {
title: 'Tools',
items: {
quickAddNewDocument: 'Quick-add new document',
quickSearchDocument: 'Quick-search existing document',
massDeleteDocument: 'Mass delete documents',
toggleTree: 'Toggle hierarchical tree',
showNoteBoard: 'Show note board',
programSettings: 'Program settings'
}
}

View file

@ -1,3 +1,7 @@
import T_helpInfo from './components/AppControlMenus/T_helpInfo'
import T_project from './components/AppControlMenus/T_project'
import T_tools from './components/AppControlMenus/T_tools'
export default {
// GLOBAL - APP TEXTS
app: {
@ -18,5 +22,13 @@ export default {
resizeButton: 'Resize Down',
maximizeButton: 'Maximize',
close: 'Close'
},
// COMPONENT - APP CONTROL MENUS
AppControlMenus: {
project: T_project,
tools: T_tools,
helpInfo: T_helpInfo
}
}

View file

@ -0,0 +1,9 @@
import { createI18n } from 'vue-i18n'
import messages from 'src/i18n'
export const i18n = createI18n({
locale: 'en-US',
fallbackLocale: 'en-US',
legacy: false,
messages
})

View file

@ -1,13 +1,15 @@
<template>
<q-layout view="hHh Lpr lFf">
<GlobalWindowButtons />
<q-header
elevated
class="bg-dark"
dark
class="bg-dark appHeader"
>
<AppControlMenus />
</q-header>
<GlobalWindowButtons />
<q-drawer
show-if-above
dark
@ -32,3 +34,23 @@ import GlobalWindowButtons from 'components/GlobalWindowButtons/GlobalWindowButt
import AppControlMenus from 'components/AppControlMenus/AppControlMenus.vue'
</script>
<style lang="scss" scoped>
.appHeader {
-webkit-app-region: drag;
// Tweak so the menus will slide nicely behind the buttons
z-index: 7000;
}
</style>
<style lang="scss">
// Unscoped due to impossibility of being matched with the rendered elements otherwise
.appHeader {
// Tweak for default white shadow rendering in the dark-mode variation of the element
.q-layout__shadow:after{
box-shadow: 0 0 10px 2px rgba(0, 0, 0, 0.2), 0 0px 10px rgba(0, 0, 0, 0.24) !important;
}
}
</style>

View file

@ -1,7 +1,13 @@
<template>
<q-page class="row">
<FantasiaMascotImage width="400px" />
<div>
<button
@click="toggleDevTools()"
>
test
</button>
</div>
<div>
<div
v-for="n in 1000"
@ -15,5 +21,5 @@
<script lang="ts" setup>
import FantasiaMascotImage from 'src/components/FantasiaMascotImage/FantasiaMascotImage.vue'
import { toggleDevTools } from 'src/scripts/appInfo/toggleDevTools'
</script>

View file

@ -0,0 +1,3 @@
export const toggleDevTools = () => {
window.faDevToolsControlAPI.toggleDevTools()
}