feat: sidebar nav editor (wip)

pull/6775/head
NGPixel 2 years ago
parent fa4b75753a
commit 269040ed7f
No known key found for this signature in database
GPG Key ID: B755FB6870B30F63

@ -1683,6 +1683,25 @@
"history.restore.confirmText": "Are you sure you want to restore this page content as it was on {date}? This version will be copied on top of the current history. As such, newer versions will still be preserved.",
"history.restore.confirmTitle": "Restore page version?",
"history.restore.success": "Page version restored succesfully!",
"navEdit.editMenuItems": "Edit Menu Items",
"navEdit.header": "Header",
"navEdit.icon": "Icon",
"navEdit.iconHint": "Icon to display to the left of the menu item.",
"navEdit.label": "Label",
"navEdit.labelHint": "Text to display on the menu item.",
"navEdit.link": "Link",
"navEdit.nestItem": "Nest Item",
"navEdit.openInNewWindow": "Open in New Window",
"navEdit.openInNewWindowHint": "Whether the link should open in a new window / tab.",
"navEdit.separator": "Separator",
"navEdit.target": "Target",
"navEdit.targetHint": "Target path or external link to point to.",
"navEdit.title": "Edit Navigation",
"navEdit.unnestItem": "Unnest Item",
"navEdit.visibility": "Visibility",
"navEdit.visibilityAll": "Everyone",
"navEdit.visibilityHint": "Whether to show the menu item to everyone or just selected groups.",
"navEdit.visibilityLimited": "Selected Groups",
"pageDeleteDialog.confirm": "Are you sure you want to delete the page {name}?",
"pageDeleteDialog.deleteSuccess": "Page deleted successfully.",
"pageDeleteDialog.pageId": "Page ID {id}",

29
ux/package-lock.json generated

@ -84,6 +84,8 @@
"quasar": "2.12.1",
"slugify": "1.6.6",
"socket.io-client": "4.7.1",
"sortablejs": "1.15.0",
"sortablejs-vue3": "1.2.9",
"tabulator-tables": "5.5.0",
"tippy.js": "6.3.7",
"twemoji": "14.0.2",
@ -6920,8 +6922,26 @@
}
},
"node_modules/sortablejs": {
"version": "1.14.0",
"license": "MIT"
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.0.tgz",
"integrity": "sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w=="
},
"node_modules/sortablejs-vue3": {
"version": "1.2.9",
"resolved": "https://registry.npmjs.org/sortablejs-vue3/-/sortablejs-vue3-1.2.9.tgz",
"integrity": "sha512-l0IIBdu+nRIwC2+KOkiavXw5vRfsn6MIPVSVSf7ItBevcuRZ4mVzC7dgnr/Hs/VPH2Q+nF2PYP3FsrnrG+7qCw==",
"dependencies": {
"sortablejs": "^1.15.0",
"vue": "^3.2.37"
},
"funding": {
"type": "individual",
"url": "https://github.com/sponsors/MaxLeiter/"
},
"peerDependencies": {
"sortablejs": "^1.15.0",
"vue": "^3.2.25"
}
},
"node_modules/source-map": {
"version": "0.6.1",
@ -7731,6 +7751,11 @@
"vue": "^3.0.1"
}
},
"node_modules/vuedraggable/node_modules/sortablejs": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz",
"integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w=="
},
"node_modules/wcwidth": {
"version": "1.0.1",
"dev": true,

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="96px" height="96px"><path fill="#50e6ff" d="M33.755,6H8C6.895,6,6,6.895,6,8v32c0,1.105,0.895,2,2,2h28V8.245C36,7.005,34.995,6,33.755,6z"/><linearGradient id="_UG2OqS6EDhFYZ28lunJga" x1="25" x2="42" y1="24" y2="24" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#1951a8"/><stop offset=".175" stop-color="#1955b0"/><stop offset=".505" stop-color="#195ab9"/><stop offset="1" stop-color="#195bbc"/></linearGradient><path fill="url(#_UG2OqS6EDhFYZ28lunJga)" d="M25,6h15c1.105,0,2,0.895,2,2v32c0,1.105-0.895,2-2,2H25V6z"/><path fill="#057093" d="M10.667,11H9.333C9.149,11,9,10.851,9,10.667V9.333C9,9.149,9.149,9,9.333,9h1.333 C10.851,9,11,9.149,11,9.333v1.333C11,10.851,10.851,11,10.667,11z"/><path fill="#057093" d="M21.567,11h-8.134C13.194,11,13,10.806,13,10.567V9.433C13,9.194,13.194,9,13.433,9h8.134 C21.806,9,22,9.194,22,9.433v1.134C22,10.806,21.806,11,21.567,11z"/><path fill="#057093" d="M10.667,15H9.333C9.149,15,9,14.851,9,14.667v-1.333C9,13.149,9.149,13,9.333,13h1.333 C10.851,13,11,13.149,11,13.333v1.333C11,14.851,10.851,15,10.667,15z"/><path fill="#057093" d="M21.567,15h-8.134C13.194,15,13,14.806,13,14.567v-1.134C13,13.194,13.194,13,13.433,13h8.134 C21.806,13,22,13.194,22,13.433v1.134C22,14.806,21.806,15,21.567,15z"/><path fill="#057093" d="M10.667,19H9.333C9.149,19,9,18.851,9,18.667v-1.333C9,17.149,9.149,17,9.333,17h1.333 C10.851,17,11,17.149,11,17.333v1.333C11,18.851,10.851,19,10.667,19z"/><path fill="#057093" d="M21.567,19h-8.134C13.194,19,13,18.806,13,18.567v-1.134C13,17.194,13.194,17,13.433,17h8.134 C21.806,17,22,17.194,22,17.433v1.134C22,18.806,21.806,19,21.567,19z"/><path fill="#057093" d="M10.667,23H9.333C9.149,23,9,22.851,9,22.667v-1.333C9,21.149,9.149,21,9.333,21h1.333 C10.851,21,11,21.149,11,21.333v1.333C11,22.851,10.851,23,10.667,23z"/><path fill="#057093" d="M21.567,23h-8.134C13.194,23,13,22.806,13,22.567v-1.134C13,21.194,13.194,21,13.433,21h8.134 C21.806,21,22,21.194,22,21.433v1.134C22,22.806,21.806,23,21.567,23z"/><path fill="#057093" d="M10.667,27H9.333C9.149,27,9,26.851,9,26.667v-1.333C9,25.149,9.149,25,9.333,25h1.333 C10.851,25,11,25.149,11,25.333v1.333C11,26.851,10.851,27,10.667,27z"/><path fill="#057093" d="M21.567,27h-8.134C13.194,27,13,26.806,13,26.567v-1.134C13,25.194,13.194,25,13.433,25h8.134 C21.806,25,22,25.194,22,25.433v1.134C22,26.806,21.806,27,21.567,27z"/><path fill="#057093" d="M10.667,31H9.333C9.149,31,9,30.851,9,30.667v-1.333C9,29.149,9.149,29,9.333,29h1.333 C10.851,29,11,29.149,11,29.333v1.333C11,30.851,10.851,31,10.667,31z"/><path fill="#057093" d="M21.567,31h-8.134C13.194,31,13,30.806,13,30.567v-1.134C13,29.194,13.194,29,13.433,29h8.134 C21.806,29,22,29.194,22,29.433v1.134C22,30.806,21.806,31,21.567,31z"/><path fill="#057093" d="M10.667,35H9.333C9.149,35,9,34.851,9,34.667v-1.333C9,33.149,9.149,33,9.333,33h1.333 C10.851,33,11,33.149,11,33.333v1.333C11,34.851,10.851,35,10.667,35z"/><path fill="#057093" d="M21.567,35h-8.134C13.194,35,13,34.806,13,34.567v-1.134C13,33.194,13.194,33,13.433,33h8.134 C21.806,33,22,33.194,22,33.433v1.134C22,34.806,21.806,35,21.567,35z"/><path fill="#057093" d="M10.667,39H9.333C9.149,39,9,38.851,9,38.667v-1.333C9,37.149,9.149,37,9.333,37h1.333 C10.851,37,11,37.149,11,37.333v1.333C11,38.851,10.851,39,10.667,39z"/><path fill="#057093" d="M21.567,39h-8.134C13.194,39,13,38.806,13,38.567v-1.134C13,37.194,13.194,37,13.433,37h8.134 C21.806,37,22,37.194,22,37.433v1.134C22,38.806,21.806,39,21.567,39z"/></svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40" width="80px" height="80px"><path fill="#dff0fe" d="M1.5 5.5H34.5V38.5H1.5z"/><path fill="#4788c7" d="M34,6v32H2V6H34 M35,5H1v34h34V5L35,5z"/><path fill="#dff0fe" d="M32.667 11.555H35V19H32.667zM21 5H28.444V7.333H21z"/><path fill="#98ccfd" d="M18.707 16L28.707 6 24.207 1.5 38.5 1.5 38.5 15.793 34 11.293 24 21.293z"/><path fill="#4788c7" d="M38,2v12.586l-3.293-3.293L34,10.586l-0.707,0.707L24,20.586L19.414,16l9.293-9.293L29.414,6 l-0.707-0.707L25.414,2H38 M39,1H23l5,5L18,16l6,6l10-10l5,5V1L39,1z"/><path fill="#4788c7" d="M34.5 21L34.5 21c.275 0 .5-.225.5-.5v-2c0-.275-.225-.5-.5-.5l0 0c-.275 0-.5.225-.5.5v2C34 20.775 34.225 21 34.5 21zM19.5 6h2C21.775 6 22 5.775 22 5.5v0C22 5.225 21.775 5 21.5 5h-2C19.225 5 19 5.225 19 5.5v0C19 5.775 19.225 6 19.5 6z"/></svg>

After

Width:  |  Height:  |  Size: 826 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.0 KiB

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40" width="80px" height="80px"><path fill="#dff0fe" d="M28.249,35.5l-2.061-7H13.812l-2.061,7H5.702l10.721-31h7.405l10.475,31H28.249z M15.283,23.5 h9.434L20.51,9.213h-1.022L15.283,23.5z"/><path fill="#4788c7" d="M23.47,5l10.137,30h-4.984l-1.849-6.282L26.562,28h-0.748H14.185h-0.748l-0.211,0.718L11.377,35 H6.404L16.779,5H23.47 M14.615,24h1.337h8.097h1.337l-0.378-1.282L21.096,9.43l-0.211-0.718h-0.748h-0.274h-0.748L18.903,9.43 l-3.911,13.287L14.615,24 M24.187,4h-8.12L5,36h7.125l2.06-7h11.629l2.061,7H35L24.187,4L24.187,4z M15.951,23l3.911-13.287h0.274 L24.048,23H15.951L15.951,23z"/></svg>

After

Width:  |  Height:  |  Size: 646 B

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40" width="80px" height="80px"><path fill="#dff0fe" d="M32 12.5A4.5 4.5 0 1 0 32 21.5A4.5 4.5 0 1 0 32 12.5Z"/><path fill="#4788c7" d="M32,13c2.206,0,4,1.794,4,4s-1.794,4-4,4s-4-1.794-4-4S29.794,13,32,13 M32,12 c-2.761,0-5,2.239-5,5s2.239,5,5,5s5-2.239,5-5S34.761,12,32,12L32,12z"/><path fill="#98ccfd" d="M24.5,32.5V32c0-4.136,3.364-7.5,7.5-7.5s7.5,3.364,7.5,7.5v0.5H24.5z"/><path fill="#4788c7" d="M32,25c3.86,0,7,3.14,7,7v0l-14,0C25,28.14,28.14,25,32,25 M32,24c-4.418,0-8,3.582-8,8v1h16v-1 C40,27.582,36.418,24,32,24L32,24z"/><path fill="#dff0fe" d="M8 12.5A4.5 4.5 0 1 0 8 21.5A4.5 4.5 0 1 0 8 12.5Z"/><path fill="#4788c7" d="M8,13c2.206,0,4,1.794,4,4s-1.794,4-4,4s-4-1.794-4-4S5.794,13,8,13 M8,12c-2.761,0-5,2.239-5,5 s2.239,5,5,5s5-2.239,5-5S10.761,12,8,12L8,12z"/><path fill="#dff0fe" d="M14.5 3.5A4 4 0 1 0 14.5 11.5A4 4 0 1 0 14.5 3.5Z"/><path fill="#4788c7" d="M14.5,4C16.43,4,18,5.57,18,7.5S16.43,11,14.5,11S11,9.43,11,7.5S12.57,4,14.5,4 M14.5,3 C12.015,3,10,5.015,10,7.5s2.015,4.5,4.5,4.5S19,9.985,19,7.5S16.985,3,14.5,3L14.5,3z"/><g><path fill="#dff0fe" d="M25.5 3.5A4 4 0 1 0 25.5 11.5A4 4 0 1 0 25.5 3.5Z"/><path fill="#4788c7" d="M25.5,4C27.43,4,29,5.57,29,7.5S27.43,11,25.5,11S22,9.43,22,7.5S23.57,4,25.5,4 M25.5,3 C23.015,3,21,5.015,21,7.5s2.015,4.5,4.5,4.5S30,9.985,30,7.5S27.985,3,25.5,3L25.5,3z"/></g><g><path fill="#98ccfd" d="M0.5,32.5V32c0-4.136,3.364-7.5,7.5-7.5s7.5,3.364,7.5,7.5v0.5H0.5z"/><path fill="#4788c7" d="M8,25c3.86,0,7,3.14,7,7v0L1,32C1,28.14,4.14,25,8,25 M8,24c-4.418,0-8,3.582-8,8v1h16v-1 C16,27.582,12.418,24,8,24L8,24z"/></g><g><path fill="#dff0fe" d="M20 17.5A4.5 4.5 0 1 0 20 26.5A4.5 4.5 0 1 0 20 17.5Z"/><path fill="#4788c7" d="M20,18c2.206,0,4,1.794,4,4s-1.794,4-4,4s-4-1.794-4-4S17.794,18,20,18 M20,17 c-2.761,0-5,2.239-5,5s2.239,5,5,5s5-2.239,5-5S22.761,17,20,17L20,17z"/></g><g><path fill="#b6dcfe" d="M12.5,37.5V37c0-4.136,3.364-7.5,7.5-7.5s7.5,3.364,7.5,7.5v0.5H12.5z"/><path fill="#4788c7" d="M20,30c3.86,0,7,3.14,7,7v0l-14,0C13,33.14,16.14,30,20,30 M20,29c-4.418,0-8,3.582-8,8v1h16v-1 C28,32.582,24.418,29,20,29L20,29z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@ -27,6 +27,10 @@ const overlays = {
loader: () => import('./FileManager.vue'),
loadingComponent: LoadingGeneric
}),
NavEdit: defineAsyncComponent({
loader: () => import('./NavEditOverlay.vue'),
loadingComponent: LoadingGeneric
}),
TableEditor: defineAsyncComponent({
loader: () => import('./TableEditorOverlay.vue'),
loadingComponent: LoadingGeneric

@ -0,0 +1,88 @@
<template lang="pug">
q-card(style='min-width: 350px')
q-card-section.card-header
q-icon(name='img:/_assets/icons/fluent-sidebar-menu.svg', left, size='sm')
span {{t(`navEdit.title`)}}
q-card-section
q-btn.full-width(
unelevated
icon='mdi-playlist-edit'
color='deep-orange-9'
:label='t(`navEdit.editMenuItems`)'
@click='startEditing'
)
q-separator(inset)
q-card-section.q-pb-none.text-body2 Mode
q-list(padding)
q-item(tag='label')
q-item-section(side)
q-radio(v-model='state.mode', val='inherit')
q-item-section
q-item-label Inherit
q-item-label(caption) Use the menu items and settings from the parent path.
q-item(tag='label')
q-item-section(side)
q-radio(v-model='state.mode', val='starting')
q-item-section
q-item-label Override Current + Descendants
q-item-label(caption) Set menu items and settings for this path and all children.
q-item(tag='label')
q-item-section(side)
q-radio(v-model='state.mode', val='exact')
q-item-section
q-item-label Override Current Only
q-item-label(caption) Set menu items and settings only for this path.
q-card-actions.card-actions
q-space
q-btn.acrylic-btn(
flat
:label='t(`common.actions.cancel`)'
color='grey'
padding='xs md'
@click='props.menuHideHandler'
)
q-btn(
unelevated
:label='t(`common.actions.save`)'
color='positive'
padding='xs md'
)
</template>
<script setup>
import { onMounted, reactive, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useSiteStore } from 'src/stores/site'
// PROPS
const props = defineProps({
menuHideHandler: {
type: Function,
default: () => ({})
}
})
// STORES
const siteStore = useSiteStore()
// I18N
const { t } = useI18n()
// DATA
const state = reactive({
mode: 'inherit'
})
// METHODS
function startEditing () {
siteStore.$patch({ overlay: 'NavEdit' })
props.menuHideHandler()
}
</script>

@ -0,0 +1,518 @@
<template lang="pug">
q-layout(view='hHh lpR fFf', container)
q-header.card-header.q-px-md.q-py-sm
q-icon(name='img:/_assets/icons/fluent-sidebar-menu.svg', left, size='md')
span {{t(`navEdit.editMenuItems`)}}
q-space
q-btn.q-mr-sm(
flat
rounded
color='white'
:aria-label='t(`common.actions.viewDocs`)'
icon='las la-question-circle'
:href='siteStore.docsBase + `/admin/editors/markdown`'
target='_blank'
type='a'
)
q-btn-group(push)
q-btn(
push
color='white'
text-color='grey-7'
:label='t(`common.actions.cancel`)'
:aria-label='t(`common.actions.cancel`)'
icon='las la-times'
@click='close'
)
q-btn(
push
color='positive'
text-color='white'
:label='t(`common.actions.save`)'
:aria-label='t(`common.actions.save`)'
icon='las la-check'
:disabled='state.loading > 0'
)
q-drawer.bg-dark-6(:model-value='true', :width='295', dark)
q-scroll-area.nav-edit(
:thumb-style='thumbStyle'
:bar-style='barStyle'
)
sortable(
class='q-list q-list--dense q-list--dark nav-edit-list'
:list='state.items'
item-key='id'
:options='sortableOptions'
)
template(#item='{element}')
.nav-edit-item.nav-edit-item-header(
v-if='element.type === `header`'
:class='state.selected === element.id ? `is-active` : ``'
@click='setItem(element)'
)
q-item-label.text-caption(
header
) {{ element.label }}
q-space
q-item-section(side)
q-icon.handle(name='mdi-drag-horizontal', size='sm')
q-item.nav-edit-item.nav-edit-item-link(
v-else-if='element.type === `link`'
:class='{ "is-active": state.selected === element.id, "is-nested": element.isNested }'
@click='setItem(element)'
clickable
)
q-item-section(side)
q-icon(:name='element.icon', color='white')
q-item-section.text-wordbreak-all {{ element.label }}
q-item-section(side)
q-icon.handle(name='mdi-drag-horizontal', size='sm')
.nav-edit-item.nav-edit-item-separator(
v-else
:class='state.selected === element.id ? `is-active` : ``'
@click='setItem(element)'
)
q-separator(
dark
inset
style='flex: 1; margin-top: 11px;'
)
q-item-section(side)
q-icon.handle(name='mdi-drag-horizontal', size='sm')
.q-pa-md
q-btn.full-width.acrylic-btn(
flat
color='positive'
:label='t(`common.actions.add`)'
:aria-label='t(`common.actions.add`)'
icon='las la-plus-circle'
)
q-menu(fit, :offset='[0, 10]')
q-list(separator)
q-item(clickable)
q-item-section(side)
q-icon(name='las la-heading')
q-item-section
q-item-label Header
q-item(clickable)
q-item-section(side)
q-icon(name='las la-link')
q-item-section
q-item-label {{t('navEdit.link')}}
q-item(clickable)
q-item-section(side)
q-icon(name='las la-minus')
q-item-section
q-item-label Separator
q-item(clickable, style='border-top-width: 5px;')
q-item-section(side)
q-icon(name='mdi-import')
q-item-section
q-item-label Copy from...
q-page-container
q-page.q-pa-md
template(v-if='state.current.type === `header`')
q-card.q-pb-sm
q-card-section
.text-subtitle1 {{t('navEdit.header')}}
q-item
blueprint-icon(icon='typography')
q-item-section
q-item-label {{t(`navEdit.label`)}}
q-item-label(caption) {{t(`navEdit.labelHint`)}}
q-item-section
q-input(
outlined
v-model='state.current.label'
dense
hide-bottom-space
:aria-label='t(`navEdit.label`)'
)
q-card.q-pa-md.q-mt-md.flex
q-space
q-btn.acrylic-btn(
flat
:label='t(`common.actions.delete`)'
color='negative'
padding='xs md'
@click=''
)
template(v-if='state.current.type === `link`')
q-card.q-pb-sm
q-card-section
.text-subtitle1 {{t('navEdit.link')}}
q-item
blueprint-icon(icon='typography')
q-item-section
q-item-label {{t(`navEdit.label`)}}
q-item-label(caption) {{t(`navEdit.labelHint`)}}
q-item-section
q-input(
outlined
v-model='state.current.label'
dense
hide-bottom-space
:aria-label='t(`navEdit.label`)'
)
q-separator.q-my-sm(inset)
q-item
blueprint-icon(icon='spring')
q-item-section
q-item-label {{t(`navEdit.icon`)}}
q-item-label(caption) {{t(`navEdit.iconHint`)}}
q-item-section
q-input(
outlined
v-model='state.current.icon'
dense
:aria-label='t(`navEdit.icon`)'
)
template(#append)
q-icon.cursor-pointer(
name='las la-icons'
color='primary'
)
q-separator.q-my-sm(inset)
q-item
blueprint-icon(icon='link')
q-item-section
q-item-label {{t(`navEdit.target`)}}
q-item-label(caption) {{t(`navEdit.targetHint`)}}
q-item-section
q-input(
outlined
v-model='state.current.target'
dense
hide-bottom-space
:aria-label='t(`navEdit.target`)'
)
q-separator.q-my-sm(inset)
q-item(tag='label')
blueprint-icon(icon='external-link')
q-item-section
q-item-label {{t(`navEdit.openInNewWindow`)}}
q-item-label(caption) {{t(`navEdit.openInNewWindowHint`)}}
q-item-section(avatar)
q-toggle(
v-model='state.current.openInNewWindow'
color='primary'
checked-icon='las la-check'
unchecked-icon='las la-times'
:aria-label='t(`navEdit.openInNewWindow`)'
)
q-separator.q-my-sm(inset)
q-item
blueprint-icon(icon='user-groups')
q-item-section
q-item-label {{t(`navEdit.visibility`)}}
q-item-label(caption) {{t(`navEdit.visibilityHint`)}}
q-item-section(avatar)
q-btn-toggle(
v-model='state.current.visibilityLimited'
push
glossy
no-caps
toggle-color='primary'
:options='visibilityOptions'
)
q-item(v-if='state.current.visibilityLimited')
q-item-section
q-item-section
q-select(
outlined
v-model='state.current.visibility'
:options='state.groups'
option-value='value'
option-label='label'
emit-value
map-options
dense
options-dense
:virtual-scroll-slice-size='1000'
:aria-label='t(`admin.general.uploadConflictBehavior`)'
)
q-card.q-pa-md.q-mt-md.flex
q-btn.acrylic-btn(
v-if='state.current.isNested'
flat
:label='t(`navEdit.unnestItem`)'
icon='mdi-format-indent-decrease'
color='teal'
padding='xs md'
@click='state.current.isNested = false'
)
q-btn.acrylic-btn(
v-else
flat
:label='t(`navEdit.nestItem`)'
icon='mdi-format-indent-increase'
color='teal'
padding='xs md'
@click='state.current.isNested = true'
)
q-space
q-btn.acrylic-btn(
flat
:label='t(`common.actions.delete`)'
color='negative'
padding='xs md'
@click=''
)
template(v-if='state.current.type === `separator`')
q-card
q-card-section
.text-subtitle1 {{t('navEdit.separator')}}
q-card.q-pa-md.q-mt-md.flex
q-space
q-btn.acrylic-btn(
flat
:label='t(`common.actions.delete`)'
color='negative'
padding='xs md'
@click=''
)
</template>
<script setup>
import { useI18n } from 'vue-i18n'
import { useQuasar } from 'quasar'
import { onMounted, reactive, ref } from 'vue'
import gql from 'graphql-tag'
import { cloneDeep } from 'lodash-es'
import { useSiteStore } from 'src/stores/site'
import { Sortable } from 'sortablejs-vue3'
// QUASAR
const $q = useQuasar()
// STORES
const siteStore = useSiteStore()
// I18N
const { t } = useI18n()
// DATA
const state = reactive({
loading: 0,
selected: '3',
items: [
{
id: '1',
type: 'header',
label: 'General'
},
{
id: '2',
type: 'link',
label: 'Dogs',
icon: 'las la-dog'
},
{
id: '3',
type: 'link',
label: 'Cats',
icon: 'las la-cat'
},
{
id: '4',
type: 'separator'
},
{
id: '5',
type: 'header',
label: 'User Guide'
},
{
id: '6',
type: 'link',
label: 'Editing Pages',
icon: 'las la-file-alt'
},
{
id: '7',
type: 'link',
label: 'Permissions',
icon: 'las la-key',
isNested: true
},
{
id: '8',
type: 'link',
label: 'Supersuperlongtitleveryveryversupersupersupersupersuper long word',
icon: 'las la-key'
},
{
id: '9',
type: 'link',
label: 'Users',
icon: 'las la-users'
},
{
id: '10',
type: 'link',
label: 'Locales',
icon: 'las la-globe'
}
],
current: {
label: '',
icon: '',
target: '/',
openInNewWindow: false,
visibility: [],
visibilityLimited: false,
isNested: false
},
groups: []
})
const sortableOptions = {
handle: '.handle',
animation: 150
}
const visibilityOptions = [
{ value: false, label: t('navEdit.visibilityAll') },
{ value: true, label: t('navEdit.visibilityLimited') }
]
const thumbStyle = {
right: '2px',
borderRadius: '5px',
backgroundColor: '#FFF',
width: '5px',
opacity: 0.5
}
const barStyle = {
backgroundColor: '#000',
width: '9px',
opacity: 0.1
}
// METHODS
function setItem (item) {
state.selected = item.id
state.current = item
}
function close () {
siteStore.$patch({ overlay: '' })
}
onMounted(() => {
})
</script>
<style lang="scss" scoped>
.nav-edit {
height: 100%;
.handle {
cursor: grab;
}
}
.nav-edit-item {
position: relative;
&.is-active {
background-color: $blue-8;
}
&.sortable-chosen {
background-color: $blue-5;
}
}
.nav-edit-item-header {
display: flex;
cursor: pointer;
}
.nav-edit-item-link {
&.is-nested {
border-left: 10px solid $dark-1;
background-color: $dark-4;
&.is-active {
background-color: $primary;
}
& + div:not(.is-nested) {
&::before {
content: '';
display: 'block';
position: absolute;
top: 0;
left: 0;
width: 10px;
height: 10px;
border-style: solid;
border-color: $dark-1 transparent transparent $dark-1;
border-width: 10px 10px 10px 0;
}
}
}
&:not(.is-nested) + &.is-nested {
&::before {
content: '';
display: 'block';
position: absolute;
top: -10px;
left: -10px;
width: 10px;
height: 10px;
border-style: solid;
border-color: transparent transparent $dark-1 $dark-1;
border-width: 0 10px 10px 0;
}
}
}
.nav-edit-item-separator {
display: flex;
cursor: pointer;
}
.nav-edit-item-header, .nav-edit-item-separator {
& + .nav-edit-item-link.is-nested {
background-color: $negative !important;
border-left-color: darken($negative, 10%) !important;
& + div:not(.is-nested) {
&::before {
display: none !important;
}
}
}
}
.nav-edit-list {
.nav-edit-item-separator + .nav-edit-item-header > .q-item__label {
padding-top: 8px;
}
.is-nested:first-child {
background-color: $negative !important;
border-left-color: darken($negative, 10%) !important;
& + div:not(.is-nested) {
&::before {
display: none !important;
}
}
}
}
</style>

@ -0,0 +1,79 @@
<template lang="pug">
q-scroll-area.sidebar-nav(
:thumb-style='thumbStyle'
:bar-style='barStyle'
)
q-list(
clickable
dense
dark
)
q-item-label.text-blue-2.text-caption(header) Header
q-item(to='/install')
q-item-section(side)
q-icon(name='las la-dog', color='white')
q-item-section Link 1
q-item(to='/install')
q-item-section(side)
q-icon(name='las la-cat', color='white')
q-item-section Link 2
q-separator.q-my-sm(dark)
q-item(to='/install')
q-item-section(side)
q-icon(name='mdi-fruit-grapes', color='white')
q-item-section.text-wordbreak-all Link 3
</template>
<script setup>
import { useQuasar } from 'quasar'
import { computed, onMounted, reactive, ref, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { useSiteStore } from 'src/stores/site'
// QUASAR
const $q = useQuasar()
// STORES
const siteStore = useSiteStore()
// ROUTER
const router = useRouter()
const route = useRoute()
// I18N
const { t } = useI18n()
// DATA
const thumbStyle = {
right: '2px',
borderRadius: '5px',
backgroundColor: '#FFF',
width: '5px',
opacity: 0.5
}
const barStyle = {
backgroundColor: '#000',
width: '9px',
opacity: 0.1
}
</script>
<style lang="scss">
.sidebar-nav {
border-top: 1px solid rgba(255,255,255,.15);
height: calc(100% - 38px - 24px);
.q-list {
.q-separator + .q-item__label {
padding-top: 12px;
}
}
}
</style>

@ -36,6 +36,10 @@ body::-webkit-scrollbar-thumb {
font-family: 'Roboto Mono', Consolas, "Liberation Mono", Courier, monospace;
}
.text-wordbreak-all {
word-break: break-all;
}
// ------------------------------------------------------------------
// THEME COLORS
// ------------------------------------------------------------------

@ -30,40 +30,27 @@ q-layout(view='hHh Lpr lff')
aria-label='Browse'
size='sm'
)
q-scroll-area.sidebar-nav(
:thumb-style='thumbStyle'
:bar-style='barStyle'
)
q-list(
clickable
dense
dark
)
q-item-label.text-blue-2.text-caption(header) Header
q-item(to='/install')
q-item-section(side)
q-icon(name='las la-dog', color='white')
q-item-section Link 1
q-item(to='/install')
q-item-section(side)
q-icon(name='las la-cat', color='white')
q-item-section Link 2
q-separator.q-my-sm(dark)
q-item(to='/install')
q-item-section(side)
q-icon(name='mdi-fruit-grapes', color='white')
q-item-section Link 3
q-bar.bg-blue-9.text-white(dense, v-if='flagsStore.experimental && userStore.authenticated')
nav-sidebar
q-bar.bg-blue-9.text-white(dense, v-if='userStore.authenticated')
q-btn.col(
icon='las la-dharmachakra'
label='History'
label='Edit Nav'
flat
)
)
q-menu(
ref='navEditMenu'
anchor='top left'
self='bottom left'
:offset='[0, 10]'
)
nav-edit-menu(:menu-hide-handler='navEditMenu.hide')
q-separator(vertical)
q-btn.col(
icon='las la-bookmark'
label='Bookmarks'
flat
disabled
)
q-page-container
router-view
@ -99,6 +86,8 @@ import { useUserStore } from 'src/stores/user'
import FooterNav from 'src/components/FooterNav.vue'
import HeaderNav from 'src/components/HeaderNav.vue'
import LocaleSelectorMenu from 'src/components/LocaleSelectorMenu.vue'
import NavSidebar from 'src/components/NavSidebar.vue'
import NavEditMenu from 'src/components/NavEditMenu.vue'
import MainOverlayDialog from 'src/components/MainOverlayDialog.vue'
// QUASAR
@ -128,23 +117,9 @@ useMeta({
titleTemplate: title => `${title} - ${siteStore.title}`
})
// DATA
// REFS
const leftDrawerOpen = ref(true)
const search = ref('')
const thumbStyle = {
right: '2px',
borderRadius: '5px',
backgroundColor: '#FFF',
width: '5px',
opacity: 0.5
}
const barStyle = {
backgroundColor: '#000',
width: '9px',
opacity: 0.1
}
const navEditMenu = ref(null)
// COMPUTED
@ -152,12 +127,6 @@ const isSidebarShown = computed(() => {
return siteStore.showSideNav && !siteStore.sideNavIsDisabled && !(editorStore.isActive && editorStore.hideSideNav)
})
// METHODS
function openFileManager () {
siteStore.openFileManager()
}
</script>
<style lang="scss">
@ -166,10 +135,6 @@ function openFileManager () {
border-bottom: 1px solid rgba(0,0,0,.2);
height: 38px;
}
.sidebar-nav {
border-top: 1px solid rgba(255,255,255,.15);
height: calc(100% - 38px - 24px);
}
body.body--dark {
background-color: $dark-6;

@ -64,7 +64,8 @@ export const useSiteStore = defineStore('site', {
},
sideDialogShown: false,
sideDialogComponent: '',
docsBase: 'https://next.js.wiki/docs'
docsBase: 'https://next.js.wiki/docs',
nav: {}
}),
getters: {
overlayIsShown: (state) => Boolean(state.overlay),

Loading…
Cancel
Save