feat: block-index feature complete + dark theme fixes

pull/6775/head
NGPixel 1 year ago
parent 46fb25c1e9
commit d51fc36b56
No known key found for this signature in database
GPG Key ID: B755FB6870B30F63

@ -1,5 +1,5 @@
import { LitElement, html, css } from 'lit'
import treeQuery from './folderByPath.graphql'
import treeQuery from './tree.graphql'
/**
* Block Index
@ -11,9 +11,6 @@ export class BlockIndexElement extends LitElement {
display: block;
margin-bottom: 16px;
}
:host-context(body.body--dark) {
background-color: #F00;
}
ul {
padding: 0;
@ -23,29 +20,48 @@ export class BlockIndexElement extends LitElement {
li {
background-color: #fafafa;
background-image: linear-gradient(180deg,#fff,#fafafa);
border-right: 1px solid #eee;
border-bottom: 1px solid #eee;
border-left: 5px solid #e0e0e0;
background-image: linear-gradient(to bottom,#fff,#fafafa);
border-right: 1px solid rgba(0,0,0,.05);
border-bottom: 1px solid rgba(0,0,0,.05);
border-left: 5px solid rgba(0,0,0,.1);
box-shadow: 0 3px 8px 0 rgba(116,129,141,.1);
padding: 0;
border-radius: 5px;
font-weight: 500;
}
:host-context(body.body--dark) li {
background-color: #222;
background-image: linear-gradient(to bottom,#161b22, #0d1117);
border-right: 1px solid rgba(0,0,0,.5);
border-bottom: 1px solid rgba(0,0,0,.5);
border-left: 5px solid rgba(255,255,255,.2);
box-shadow: 0 3px 8px 0 rgba(0,0,0,.25);
}
li:hover {
background-image: linear-gradient(180deg,#fff,#f6fbfe);
border-left-color: #2196f3;
background-color: var(--q-primary);
background-image: linear-gradient(to bottom,#fff,rgba(255,255,255,.95));
border-left-color: var(--q-primary);
cursor: pointer;
}
:host-context(body.body--dark) li:hover {
background-image: linear-gradient(to bottom,#1e232a, #161b22);
border-left-color: var(--q-primary);
}
li + li {
margin-top: .5rem;
}
li a {
display: block;
color: #1976d2;
color: var(--q-primary);
padding: 1rem;
text-decoration: none;
}
.no-links {
color: var(--q-negative);
border: 1px dashed color-mix(in srgb, currentColor 50%, transparent);
border-radius: 5px;
padding: 1rem;
}
`
}
@ -67,40 +83,96 @@ export class BlockIndexElement extends LitElement {
* The maximum number of items to fetch
* @type {number}
*/
limit: {type: Number}
limit: { type: Number },
/**
* Ordering (createdAt, fileName, title, updatedAt)
* @type {string}
*/
orderBy: { type: String },
/**
* Ordering direction (asc, desc)
* @type {string}
*/
orderByDirection: { type: String },
/**
* Maximum folder depth to fetch
* @type {number}
*/
depth: { type: Number },
/**
* A fallback message if no results are returned
* @type {string}
*/
noResultMsg: { type: String },
// Internal Properties
_loading: { state: true },
_pages: { state: true }
}
}
constructor() {
super()
this.pages = []
this._loading = true
this._pages = []
this.path = ''
this.tags = ''
this.limit = 10
this.orderBy = 'title'
this.orderByDirection = 'asc'
this.depth = 0
this.noResultMsg = 'No pages matching your query.'
}
async connectedCallback() {
super.connectedCallback()
try {
const resp = await APOLLO_CLIENT.query({
query: treeQuery,
variables: {
siteId: WIKI_STORES.site.id,
locale: 'en',
parentPath: ''
siteId: WIKI_STATE.site.id,
locale: WIKI_STATE.page.locale,
parentPath: this.path,
limit: this.limit,
orderBy: this.orderBy,
orderByDirection: this.orderByDirection,
depth: this.depth,
...this.tags && { tags: this.tags.split(',').map(t => t.trim()).filter(t => t) },
}
})
this.pages = resp.data.tree
this.requestUpdate()
this._pages = resp.data.tree.map(p => ({
...p,
href: p.folderPath ? `/${p.folderPath}/${p.fileName}` : `/${p.fileName}`
}))
} catch (err) {
console.warn(err)
}
this._loading = false
}
render() {
return html`
return this._pages.length > 0 || this._loading ? html`
<ul>
${this.pages.map(p =>
html`<li><a href="#">${p.title}</a></li>`
${this._pages.map(p =>
html`<li><a href="${p.href}" @click="${this._navigate}">${p.title}</a></li>`
)}
</ul>
<slot></slot>
` : html`
<div class="no-links">${this.noResultMsg}</div>
<slot></slot>
`
}
_navigate (e) {
e.preventDefault()
WIKI_ROUTER.push(e.target.getAttribute('href'))
}
// createRenderRoot() {
// return this;
// }

@ -1,14 +0,0 @@
query blockIndexFetchPages (
$siteId: UUID!
$locale: String!
$parentPath: String!
) {
tree(
siteId: $siteId,
locale: $locale,
parentPath: $parentPath
) {
id
title
}
}

@ -0,0 +1,27 @@
query blockIndexFetchPages (
$siteId: UUID!
$locale: String
$parentPath: String
$tags: [String]
$limit: Int
$orderBy: TreeOrderBy
$orderByDirection: OrderByDirection
$depth: Int
) {
tree(
siteId: $siteId
locale: $locale
parentPath: $parentPath
tags: $tags
limit: $limit
types: [page]
orderBy: $orderBy
orderByDirection: $orderByDirection
depth: $depth
) {
id
folderPath
fileName
title
}
}

@ -244,8 +244,8 @@ export async function up (knex) {
table.text('content')
table.text('render')
table.text('searchContent')
table.specificType('ts', 'tsvector').index('ts_idx', { indexType: 'GIN' })
table.specificType('tags', 'text[]').index('tags_idx', { indexType: 'GIN' })
table.specificType('ts', 'tsvector').index('pages_ts_idx', { indexType: 'GIN' })
table.specificType('tags', 'text[]').index('pages_tags_idx', { indexType: 'GIN' })
table.jsonb('toc')
table.string('editor').notNullable()
table.string('contentType').notNullable()
@ -303,7 +303,7 @@ export async function up (knex) {
// TREE --------------------------------
.createTable('tree', table => {
table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
table.specificType('folderPath', 'ltree').index().index('tree_folderpath_gist_index', { indexType: 'GIST' })
table.specificType('folderPath', 'ltree').index().index('tree_folderpath_gist_idx', { indexType: 'GIST' })
table.string('fileName').notNullable().index()
table.string('hash').notNullable().index()
table.enu('type', ['folder', 'page', 'asset']).notNullable().index()
@ -311,6 +311,7 @@ export async function up (knex) {
table.string('title').notNullable()
table.enum('navigationMode', ['inherit', 'override', 'overrideExact', 'hide', 'hideExact']).notNullable().defaultTo('inherit').index()
table.uuid('navigationId').index()
table.specificType('tags', 'text[]').index('tree_tags_idx', { indexType: 'GIN' })
table.jsonb('meta').notNullable().defaultTo('{}')
table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())

@ -80,6 +80,11 @@ export default {
type: 'folder'
})
}
// -> Filter by tags
if (args.tags && args.tags.length > 0) {
builder.where('tags', '@>', args.tags)
}
})
.andWhere(builder => {
// -> Limit to specific types
@ -101,6 +106,7 @@ export default {
folderPath: decodeTreePath(decodeFolderPath(item.folderPath)),
fileName: item.fileName,
title: item.title,
tags: item.tags ?? [],
createdAt: item.createdAt,
updatedAt: item.updatedAt,
...(item.type === 'folder') && {

@ -13,6 +13,7 @@ extend type Query {
parentPath: String
locale: String
types: [TreeItemType]
tags: [String]
limit: Int
offset: Int
orderBy: TreeOrderBy

@ -110,8 +110,8 @@
"admin.auth.vendor": "Vendor",
"admin.auth.vendorWebsite": "Website",
"admin.blocks.add": "Add Block",
"admin.blocks.builtin": "Built-in component",
"admin.blocks.custom": "Custom component",
"admin.blocks.builtin": "Built-in",
"admin.blocks.custom": "Custom",
"admin.blocks.isEnabled": "Enabled",
"admin.blocks.saveSuccess": "Blocks state saved successfully.",
"admin.blocks.subtitle": "Manage dynamic components available for use inside pages.",

@ -366,6 +366,7 @@ export class Page extends Model {
fileName: last(pathParts),
locale: page.locale,
title: page.title,
tags,
meta: {
authorId: page.authorId,
contentType: page.contentType,
@ -649,6 +650,7 @@ export class Page extends Model {
// -> Update tree
await WIKI.db.knex('tree').where('id', page.id).update({
title: page.title,
tags: page.tags,
meta: {
authorId: page.authorId,
contentType: page.contentType,

@ -120,9 +120,10 @@ export class Tree extends Model {
* @param {string} args.title - Title of the page to add
* @param {string} args.locale - Locale code of the page to add
* @param {string} args.siteId - UUID of the site in which the page will be added
* @param {string[]} [args.tags] - Tags of the assets
* @param {Object} [args.meta] - Extra metadata
*/
static async addPage ({ id, parentId, parentPath, fileName, title, locale, siteId, meta = {} }) {
static async addPage ({ id, parentId, parentPath, fileName, title, locale, siteId, tags = [], meta = {} }) {
const folder = (parentId || parentPath) ? await WIKI.db.tree.getFolder({
id: parentId,
path: parentPath,
@ -147,6 +148,7 @@ export class Tree extends Model {
hash: generateHash(fullPath),
locale: locale,
siteId,
tags,
meta,
navigationId: siteId,
}).returning('*')
@ -164,9 +166,10 @@ export class Tree extends Model {
* @param {string} args.title - Title of the asset to add
* @param {string} args.locale - Locale code of the asset to add
* @param {string} args.siteId - UUID of the site in which the asset will be added
* @param {string[]} [args.tags] - Tags of the assets
* @param {Object} [args.meta] - Extra metadata
*/
static async addAsset ({ id, parentId, parentPath, fileName, title, locale, siteId, meta = {} }) {
static async addAsset ({ id, parentId, parentPath, fileName, title, locale, siteId, tags = [], meta = {} }) {
const folder = (parentId || parentPath) ? await WIKI.db.tree.getFolder({
id: parentId,
path: parentPath,
@ -191,6 +194,7 @@ export class Tree extends Model {
hash: generateHash(fullPath),
locale: locale,
siteId,
tags,
meta
}).returning('*')

@ -90,6 +90,7 @@ module.exports = configure(function (ctx) {
distDir: '../assets',
extendViteConf (viteConf) {
if (ctx.prod) {
viteConf.build.assetsDir = '_assets'
viteConf.build.rollupOptions = {
...viteConf.build.rollupOptions ?? {},
@ -106,6 +107,7 @@ module.exports = configure(function (ctx) {
'prosemirror-model',
'prosemirror-view'
]
}
},
// viteVuePluginOptions: {},

@ -1,17 +1,22 @@
import { boot } from 'quasar/wrappers'
import { usePageStore } from 'src/stores/page'
import { useSiteStore } from 'src/stores/site'
import { useUserStore } from 'src/stores/user'
export default boot(() => {
export default boot(({ router }) => {
if (import.meta.env.SSR) {
global.WIKI_STORES = {
global.WIKI_STATE = {
page: usePageStore(),
site: useSiteStore(),
user: useUserStore()
}
global.WIKI_ROUTER = router
} else {
window.WIKI_STORES = {
window.WIKI_STATE = {
page: usePageStore(),
site: useSiteStore(),
user: useUserStore()
}
window.WIKI_ROUTER = router
}
})

@ -1,5 +1,5 @@
<template lang='pug'>
q-btn.q-ml-md(flat, round, dense, color='grey')
q-btn.account-avbtn.q-ml-md(flat, round, dense, color='custom-color')
q-icon(
v-if='!userStore.authenticated || !userStore.hasAvatar'
name='las la-user-circle'
@ -49,5 +49,7 @@ const { t } = useI18n()
</script>
<style lang="scss">
.account-avbtn {
color: rgba(255,255,255,.6);
}
</style>

@ -28,10 +28,10 @@ q-toolbar(
)
q-icon(v-else, name='las la-search')
template(v-slot:append)
q-badge.q-mr-sm(
q-badge.search-kbdbadge.q-mr-sm(
v-if='!state.searchIsFocused'
label='Ctrl+K'
color='grey-7'
color='custom-color'
outline
@click='searchField.focus()'
)
@ -221,4 +221,8 @@ onBeforeUnmount(() => {
border-radius: 4px;
}
}
.search-kbdbadge {
color: rgba(255,255,255,.5);
}
</style>

@ -242,7 +242,8 @@ q-layout(view='hHh lpR fFf', container)
color='primary'
)
q-menu(content-class='shadow-7')
icon-picker-dialog(v-model='state.current.icon')
.q-pa-lg: em [ TODO: Icon Picker Dialog ]
// icon-picker-dialog(v-model='pageStore.icon')
q-separator.q-my-sm(inset)
q-item
blueprint-icon(icon='link')

@ -9,7 +9,7 @@ q-scroll-area.sidebar-nav(
dark
)
template(v-for='item of siteStore.nav.items', :key='item.id')
q-item-label.text-blue-2.text-caption.text-wordbreak-all(
q-item-label.sidebar-nav-header.text-caption.text-wordbreak-all(
v-if='item.type === `header`'
header
) {{ item.label }}
@ -175,5 +175,9 @@ watch(() => pageStore.navigationId, (newValue) => {
border-left: 10px solid rgba(255,255,255,.25);
}
}
&-header {
color: rgba(255,255,255,.75) !important;
}
}
</style>

@ -13,7 +13,8 @@
q-badge(color='grey' floating rounded)
q-icon(name='las la-pen', size='xs', padding='xs xs')
q-menu(content-class='shadow-7')
icon-picker-dialog(v-model='pageStore.icon')
.q-pa-lg: em [ TODO: Icon Picker Dialog ]
// icon-picker-dialog(v-model='pageStore.icon')
q-icon.rounded-borders(
v-else
:name='pageStore.icon'

@ -62,6 +62,9 @@ q-card.page-properties-dialog
name='las la-icons'
color='primary'
)
q-menu(content-class='shadow-7')
.q-pa-lg: em [ TODO: Icon Picker Dialog ]
// icon-picker-dialog(v-model='pageStore.icon')
q-input(
v-if='pageStore.path !== `home`'
v-model='pageStore.alias'

@ -57,7 +57,7 @@ q-layout(view='hHh Lpr lff')
dense
icon='las la-globe'
color='blue-7'
text-color='blue-2'
text-color='custom-color'
:label='commonStore.locale'
:aria-label='commonStore.locale'
size='sm'
@ -69,13 +69,16 @@ q-layout(view='hHh Lpr lff')
dense
icon='las la-sitemap'
color='blue-7'
text-color='blue-2'
text-color='custom-color'
label='Browse'
aria-label='Browse'
size='sm'
)
nav-sidebar
q-bar.bg-blue-9.text-white(dense, v-if='userStore.authenticated')
q-bar.sidebar-footerbtns.text-white(
v-if='userStore.authenticated'
dense
)
q-btn.col(
icon='las la-dharmachakra'
label='Edit Nav'
@ -186,12 +189,20 @@ const isSidebarMini = computed(() => {
background: linear-gradient(to bottom, rgba(255,255,255,.1) 0%, rgba(0,0,0, .05) 100%);
border-bottom: 1px solid rgba(0,0,0,.2);
height: 38px;
.q-btn {
color: rgba(255,255,255,.8);
}
}
.sidebar-mini {
height: 100%;
}
.sidebar-footerbtns {
background-color: rgba(255,255,255,.1);
}
body.body--dark {
background-color: $dark-6;
}

Loading…
Cancel
Save