@ -1,158 +1,27 @@
< template lang = 'pug' >
q - page . column
. page - breadcrumbs . q - py - sm . q - px - md . row (
v - if = '!editorStore.isActive'
)
. col
q - breadcrumbs (
active - color = 'grey-7'
separator - color = 'grey'
)
template ( v - slot : separator )
q - icon (
name = 'las la-angle-right'
)
q - breadcrumbs - el ( icon = 'las la-home' , to = '/' , aria - label = 'Home' )
q - tooltip Home
q - breadcrumbs - el (
v - for = 'brd of pageStore.breadcrumbs'
: key = 'brd.id'
: icon = 'brd.icon'
: label = 'brd.title'
: aria - label = 'brd.title'
: to = 'brd.path'
)
. col - auto . flex . items - center . justify - end
template ( v - if = '!pageStore.publishState === `draft`' )
. text - caption . text - accent : strong Unpublished
q - separator . q - mx - sm ( vertical )
. text - caption . text - grey - 6 Last modified on # [ strong { { lastModified } } ]
page - header
. page - container . row . no - wrap . items - stretch ( style = 'flex: 1 1 100%;' )
. col (
: style = 'siteStore.theme.tocPosition === `left` ? `order: 2;` : `order: 1;`'
)
q - no - ssr (
v - if = 'editorStore.isActive'
)
. col
q - no - ssr ( v - if = 'editorStore.isActive' )
component ( : is = 'editorComponents[editorStore.editor]' )
q - scroll - area . page - container - scrl (
v - else
: thumb - style = 'thumbStyle'
: bar - style = 'barStyle'
style = 'height: 100%;'
)
. q - pa - md
. page - contents ( ref = 'pageContents' , v - html = 'pageStore.render' )
template ( v - if = 'pageStore.relations && pageStore.relations.length > 0' )
q - separator . q - my - lg
. row . align - center
. col . text - left ( v - if = 'relationsLeft.length > 0' )
q - btn . q - mr - sm . q - mb - sm (
padding = 'sm md'
outline
: icon = 'rel.icon'
no - caps
color = 'primary'
v - for = 'rel of relationsLeft'
: key = '`rel-id-` + rel.id'
)
. column . text - left . q - pl - md
. text - body2 : strong { { rel . label } }
. text - caption { { rel . caption } }
. col . text - center ( v - if = 'relationsCenter.length > 0' )
. column
q - btn (
: label = 'rel.label'
color = 'primary'
flat
no - caps
: icon = 'rel.icon'
v - for = 'rel of relationsCenter'
: key = '`rel-id-` + rel.id'
)
. col . text - right ( v - if = 'relationsRight.length > 0' )
q - btn . q - ml - sm . q - mb - sm (
padding = 'sm md'
outline
: icon - right = 'rel.icon'
no - caps
color = 'primary'
v - for = 'rel of relationsRight'
: key = '`rel-id-` + rel.id'
)
. column . text - left . q - pr - md
. text - body2 : strong { { rel . label } }
. text - caption { { rel . caption } }
. page - sidebar (
v - if = 'showSidebar'
: style = 'siteStore.theme.tocPosition === `left` ? `order: 1;` : `order: 2;`'
)
template ( v - if = 'pageStore.showToc' )
/ / - T O C
. q - pa - md . flex . items - center
q - icon . q - mr - sm ( name = 'las la-stream' , color = 'grey' )
. text - caption . text - grey - 7 Contents
. q - px - md . q - pb - sm
q - tree . page - toc (
: nodes = 'pageStore.toc'
icon = 'las la-caret-right'
node - key = 'key'
dense
v - model : expanded = 'state.tocExpanded'
v - model : selected = 'state.tocSelected'
)
/ / - T a g s
template ( v - if = 'pageStore.showTags' )
q - separator ( v - if = 'pageStore.showToc' )
. q - pa - md (
@ mouseover = 'state.showTagsEditBtn = true'
@ mouseleave = 'state.showTagsEditBtn = false'
)
. flex . items - center
q - icon . q - mr - sm ( name = 'las la-tags' , color = 'grey' )
. text - caption . text - grey - 7 Tags
q - space
transition ( name = 'fade' )
q - btn (
v - show = 'state.showTagsEditBtn'
size = 'sm'
padding = 'none xs'
icon = 'las la-pen'
color = 'deep-orange-9'
flat
label = 'Edit'
no - caps
@ click = 'state.tagEditMode = !state.tagEditMode'
. page - content - wrap ( v - else )
. page - contents ( ref = 'pageContents' , v - html = 'pageStore.render' )
template ( v - if = 'pageStore.relations && pageStore.relations.length > 0' )
. page - relations
q - btn . q - mr - sm . q - mb - sm (
v - for = 'rel of pageStore.relations'
: key = '`rel-` + rel.id'
padding = 'sm md'
outline
: icon = 'rel.icon'
: label = 'rel.label'
no - caps
color = 'primary'
size = 'sm'
)
page - tags . q - mt - sm ( : edit = 'state.tagEditMode' )
template ( v - if = 'siteStore.features.ratingsMode !== `off` && pageStore.allowRatings' )
q - separator ( v - if = 'pageStore.showToc || pageStore.showTags' )
/ / - R a t i n g
. q - pa - md . flex . items - center
q - icon . q - mr - sm ( name = 'las la-star-half-alt' , color = 'grey' )
. text - caption . text - grey - 7 Rate this page
. q - px - md
q - rating (
v - if = 'siteStore.features.ratingsMode === `stars`'
v - model = 'state.currentRating'
icon = 'las la-star'
color = 'secondary'
size = 'sm'
)
. flex . items - center ( v - else - if = 'siteStore.features.ratingsMode === `thumbs`' )
q - btn . acrylic - btn (
flat
icon = 'las la-thumbs-down'
color = 'secondary'
)
q - btn . acrylic - btn . q - ml - sm (
flat
icon = 'las la-thumbs-up'
color = 'secondary'
)
page - actions - col
/ / - E d i t a c t i o n s ( o n l y i n e d i t o r m o d e )
page - actions - col ( v - if = 'editorStore.isActive' )
side - dialog
< / template >
@ -170,12 +39,9 @@ import { usePageStore } from '@/stores/page'
import { useSiteStore } from '@/stores/site'
import { useUserStore } from '@/stores/user'
/ / C O M P O N E N T S
import LoadingGeneric from '@/components/LoadingGeneric.vue'
import PageActionsCol from '@/components/PageActionsCol.vue'
import PageHeader from '@/components/PageHeader.vue'
import PageTags from '@/components/PageTags.vue'
import SideDialog from '@/components/SideDialog.vue'
const editorComponents = {
@ -189,137 +55,59 @@ const editorComponents = {
} )
}
/ / Q U A S A R
const $q = useQuasar ( )
/ / S T O R E S
const commonStore = useCommonStore ( )
const editorStore = useEditorStore ( )
const flagsStore = useFlagsStore ( )
const pageStore = usePageStore ( )
const siteStore = useSiteStore ( )
const userStore = useUserStore ( )
/ / R O U T E R
const router = useRouter ( )
const route = useRoute ( )
/ / I 1 8 N
const { t } = useI18n ( )
/ / M E T A
useMeta ( {
title : pageStore . title
} )
/ / D A T A
const state = reactive ( {
showSideDialog : false ,
sideDialogComponent : null ,
showGlobalDialog : false ,
globalDialogComponent : null ,
showTagsEditBtn : false ,
tagEditMode : false ,
tocExpanded : [ 'h1-0' , 'h1-1' ] ,
tocSelected : [ ] ,
currentRating : 3
} )
const thumbStyle = {
right : '2px' ,
borderRadius : '5px' ,
backgroundColor : $q . dark . isActive ? '#FFF' : '#000' ,
width : '5px' ,
opacity : 0.15
}
const barStyle = {
backgroundColor : $q . dark . isActive ? '#161b22' : '#FAFAFA' ,
width : '9px' ,
opacity : 1
}
useMeta ( { title : pageStore . title } )
const pageContents = ref ( null )
/ / C O M P U T E D
const showSidebar = computed ( ( ) => {
return pageStore . showSidebar && siteStore . showSidebar && siteStore . theme . tocPosition !== 'off' && ! editorStore . isActive
} )
const relationsLeft = computed ( ( ) => {
return pageStore . relations ? pageStore . relations . filter ( r => r . position === 'left' ) : [ ]
} )
const relationsCenter = computed ( ( ) => {
return pageStore . relations ? pageStore . relations . filter ( r => r . position === 'center' ) : [ ]
} )
const relationsRight = computed ( ( ) => {
return pageStore . relations ? pageStore . relations . filter ( r => r . position === 'right' ) : [ ]
} )
const lastModified = computed ( ( ) => {
return pageStore . updatedAt ? DateTime . fromISO ( pageStore . updatedAt ) . toLocaleString ( DateTime . DATETIME _MED ) : 'N/A'
} )
/ / W A T C H E R S
watch ( ( ) => route . path , async ( newValue ) => {
/ / - > I g n o r e r o u t e c h a n g e ( e . g . f r o m p a g e c r e a t e r o u t e f i x )
if ( editorStore . ignoreRouteChange ) {
editorStore . $patch ( { ignoreRouteChange : false } )
return
}
/ / - > E n t e r C r e a t e M o d e ?
if ( newValue . startsWith ( '/_create' ) ) {
if ( ! route . params . editor ) {
$q . notify ( {
type : 'negative' ,
message : 'No editor specified!'
} )
$q . notify ( { type : 'negative' , message : 'No editor specified!' } )
return router . replace ( '/' )
}
$q . loading . show ( )
const pageCreateArgs = { editor : route . params . editor , fromNavigate : true }
if ( route . query . path ) {
pageCreateArgs . path = route . query . path
}
if ( route . query . locale ) {
pageCreateArgs . locale = route . query . locale
}
if ( route . query . path ) { pageCreateArgs . path = route . query . path }
if ( route . query . locale ) { pageCreateArgs . locale = route . query . locale }
await pageStore . pageCreate ( pageCreateArgs )
$q . loading . hide ( )
return
}
/ / - > E n t e r E d i t M o d e ?
if ( newValue . startsWith ( '/_edit' ) ) {
if ( ! route . params . pagePath ) {
return router . replace ( '/' )
}
if ( ! route . params . pagePath ) { return router . replace ( '/' ) }
$q . loading . show ( )
await pageStore . pageEdit ( { path : route . params . pagePath , fromNavigate : true } )
$q . loading . hide ( )
return
}
/ / - > M o v i n g t o a n o n - p a g e p a t h ? I g n o r e
if ( newValue . startsWith ( '/_' ) ) { return }
/ / - > L o a d P a g e
try {
await pageStore . pageLoad ( { path : newValue } )
if ( editorStore . isActive ) {
editorStore . $patch ( {
isActive : false
} )
}
/ / - > L o a d B l o c k s
if ( editorStore . isActive ) { editorStore . $patch ( { isActive : false } ) }
nextTick ( ( ) => {
for ( const block of pageContents . value . querySelectorAll ( ':not(:defined)' ) ) {
commonStore . loadBlocks ( [ block . tagName . toLowerCase ( ) ] )
if ( pageContents . value ) {
for ( const block of pageContents . value . querySelectorAll ( ':not(:defined)' ) ) {
commonStore . loadBlocks ( [ block . tagName . toLowerCase ( ) ] )
}
}
} )
} catch ( err ) {
@ -333,167 +121,39 @@ watch(() => route.path, async (newValue) => {
siteStore . overlay = 'Welcome'
}
} else {
$q . notify ( {
type : 'negative' ,
message : 'This page does not exist (yet)!'
} )
$q . notify ( { type : 'negative' , message : 'This page does not exist (yet)!' } )
}
} else {
$q . notify ( {
type : 'negative' ,
message : err . message
} )
$q . notify ( { type : 'negative' , message : err . message } )
}
}
} , { immediate : true } )
watch ( ( ) => pageStore . toc , ( ) => { refreshTocExpanded ( ) } , { immediate : true } )
watch ( ( ) => pageStore . tocDepth , ( ) => { refreshTocExpanded ( ) } )
/ / M E T H O D S
function refreshTocExpanded ( baseToc , lvl ) {
const toExpand = [ ]
let isRootNode = false
if ( ! baseToc ) {
baseToc = pageStore . toc
isRootNode = true
lvl = 1
}
if ( baseToc . length > 0 ) {
for ( const node of baseToc ) {
if ( lvl >= pageStore . tocDepth . min && lvl < pageStore . tocDepth . max ) {
toExpand . push ( node . key )
}
if ( node . children ? . length && lvl < pageStore . tocDepth . max - 1 ) {
toExpand . push ( ... refreshTocExpanded ( node . children , lvl + 1 ) )
}
}
}
if ( isRootNode ) {
state . tocExpanded = toExpand
} else {
return toExpand
}
}
< / script >
< style lang = "scss" >
. page - breadcrumbs {
@ at - root . body -- light & {
background : linear - gradient ( to bottom , $grey - 1 0 % , $grey - 3 100 % ) ;
border - bottom : 1 px solid $grey - 4 ;
}
@ at - root . body -- dark & {
background : linear - gradient ( to bottom , $dark - 3 0 % , $dark - 4 100 % ) ;
border - bottom : 1 px solid $dark - 3 ;
}
}
. page - header {
height : 95 px ;
@ at - root . body -- light & {
background : linear - gradient ( to bottom , $grey - 2 0 % , $grey - 1 100 % ) ;
border - bottom : 1 px solid $grey - 4 ;
border - top : 1 px solid # FFF ;
}
@ at - root . body -- dark & {
background : linear - gradient ( to bottom , $dark - 4 0 % , $dark - 3 100 % ) ;
/ / b o r d e r - b o t t o m : 1 p x s o l i d $ d a r k - 5 ;
border - top : 1 px solid $dark - 6 ;
}
. no - height . q - field _ _control {
height : auto ;
}
& - title {
@ at - root . body -- light & {
color : $grey - 9 ;
}
@ at - root . body -- dark & {
color : # FFF ;
}
}
& - subtitle {
@ at - root . body -- light & {
color : $grey - 7 ;
}
@ at - root . body -- dark & {
color : rgba ( 255 , 255 , 255 , .6 ) ;
}
}
}
. page - container {
@ at - root . body -- light & {
border - top : 1 px solid # FFF ;
}
/ / @ a t - r o o t . b o d y - - d a r k & {
/ / b o r d e r - t o p : 1 p x s o l i d $ d a r k - 6 ;
/ / }
. page - content - wrap {
max - width : 860 px ;
margin : 0 auto ;
padding : 0 32 px 48 px ;
width : 100 % ;
. page - container - scrl > . q - scrollarea _ _container > . q - scrollarea _ _content {
width : 100 % ;
}
}
. page - sidebar {
flex : 0 0 300 px ;
@ at - root . body -- light & {
background - color : $grey - 2 ;
}
@ at - root . body -- dark & {
background - color : $dark - 5 ;
}
. q - separator {
background - color : rgba ( 0 , 0 , 0 , .05 ) ;
border - bottom : 1 px solid ;
@ at - root . body -- light & {
background - color : rgba ( 0 , 0 , 0 , .05 ) ;
border - bottom - color : # FFF ;
}
@ at - root . body -- dark & {
background - color : rgba ( 255 , 255 , 255 , .04 ) ;
border - bottom - color : # 070 a0d ;
}
}
. page - relations {
margin - top : 32 px ;
padding - top : 24 px ;
border - top : 1 px solid # E2E8F0 ;
}
. floating - syncpanel {
. q - dialog _ _inner {
margin - top : 14 px ;
right : 140 px ;
left : auto ;
. q - card {
border - radius : 17 px ;
}
}
& - msg {
padding - top : 1 px ;
font - weight : 500 ;
font - size : .75 rem ;
padding - right : 16 px ;
display : flex ;
align - items : center ;
}
. page - container {
border - top : none ;
}
. q - card {
@ at - root . body -- light & {
background - color : # FFF ;
}
@ at - root . body -- dark & {
background - color : $dark - 3 ;
}
}
. page - toc {
& . q - tree -- dense . q - tree _ _node {
padding - bottom : 5 px ;
}
@ at - root . body -- light & { background - color : # FFF ; }
@ at - root . body -- dark & { background - color : $dark - 3 ; }
}
< / style >