From d51fc36b56a5cb922aa60bbd912255e4c699a967 Mon Sep 17 00:00:00 2001 From: NGPixel Date: Mon, 11 Sep 2023 05:01:21 +0000 Subject: [PATCH] feat: block-index feature complete + dark theme fixes --- blocks/block-index/component.js | 128 ++++++++++++++++----- blocks/block-index/folderByPath.graphql | 14 --- blocks/block-index/tree.graphql | 27 +++++ server/db/migrations/3.0.0.mjs | 7 +- server/graph/resolvers/tree.mjs | 6 + server/graph/schemas/tree.graphql | 1 + server/locales/en.json | 4 +- server/models/pages.mjs | 2 + server/models/tree.mjs | 8 +- ux/quasar.config.js | 28 ++--- ux/src/boot/externals.js | 11 +- ux/src/components/AccountMenu.vue | 6 +- ux/src/components/HeaderSearch.vue | 8 +- ux/src/components/NavEditOverlay.vue | 3 +- ux/src/components/NavSidebar.vue | 6 +- ux/src/components/PageHeader.vue | 3 +- ux/src/components/PagePropertiesDialog.vue | 5 +- ux/src/layouts/MainLayout.vue | 17 ++- 18 files changed, 208 insertions(+), 76 deletions(-) delete mode 100644 blocks/block-index/folderByPath.graphql create mode 100644 blocks/block-index/tree.graphql diff --git a/blocks/block-index/component.js b/blocks/block-index/component.js index 5e4c4e60e7..dfa44e5b42 100644 --- a/blocks/block-index/component.js +++ b/blocks/block-index/component.js @@ -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; + } ` } @@ -55,52 +71,108 @@ export class BlockIndexElement extends LitElement { * The base path to fetch pages from * @type {string} */ - path: {type: String}, + path: { type: String }, /** * A comma-separated list of tags to filter with * @type {string} */ - tags: {type: String}, + tags: { type: String }, /** * 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() - const resp = await APOLLO_CLIENT.query({ - query: treeQuery, - variables: { - siteId: WIKI_STORES.site.id, - locale: 'en', - parentPath: '' - } - }) - this.pages = resp.data.tree - this.requestUpdate() + try { + const resp = await APOLLO_CLIENT.query({ + query: treeQuery, + variables: { + 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.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` + ` : html` + + ` } + _navigate (e) { + e.preventDefault() + WIKI_ROUTER.push(e.target.getAttribute('href')) + } + // createRenderRoot() { // return this; // } diff --git a/blocks/block-index/folderByPath.graphql b/blocks/block-index/folderByPath.graphql deleted file mode 100644 index d9257455ec..0000000000 --- a/blocks/block-index/folderByPath.graphql +++ /dev/null @@ -1,14 +0,0 @@ -query blockIndexFetchPages ( - $siteId: UUID! - $locale: String! - $parentPath: String! - ) { - tree( - siteId: $siteId, - locale: $locale, - parentPath: $parentPath - ) { - id - title - } -} diff --git a/blocks/block-index/tree.graphql b/blocks/block-index/tree.graphql new file mode 100644 index 0000000000..6c21d3e5c1 --- /dev/null +++ b/blocks/block-index/tree.graphql @@ -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 + } +} diff --git a/server/db/migrations/3.0.0.mjs b/server/db/migrations/3.0.0.mjs index 81ce33d489..406f851478 100644 --- a/server/db/migrations/3.0.0.mjs +++ b/server/db/migrations/3.0.0.mjs @@ -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()) diff --git a/server/graph/resolvers/tree.mjs b/server/graph/resolvers/tree.mjs index bef2228aad..debe757bba 100644 --- a/server/graph/resolvers/tree.mjs +++ b/server/graph/resolvers/tree.mjs @@ -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') && { diff --git a/server/graph/schemas/tree.graphql b/server/graph/schemas/tree.graphql index b38afb807f..4c4753f0b1 100644 --- a/server/graph/schemas/tree.graphql +++ b/server/graph/schemas/tree.graphql @@ -13,6 +13,7 @@ extend type Query { parentPath: String locale: String types: [TreeItemType] + tags: [String] limit: Int offset: Int orderBy: TreeOrderBy diff --git a/server/locales/en.json b/server/locales/en.json index 956e3b203e..c5800a5c2d 100644 --- a/server/locales/en.json +++ b/server/locales/en.json @@ -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.", diff --git a/server/models/pages.mjs b/server/models/pages.mjs index 1be210cf2f..6182235217 100644 --- a/server/models/pages.mjs +++ b/server/models/pages.mjs @@ -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, diff --git a/server/models/tree.mjs b/server/models/tree.mjs index 4bb02c90a0..005f261db6 100644 --- a/server/models/tree.mjs +++ b/server/models/tree.mjs @@ -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('*') diff --git a/ux/quasar.config.js b/ux/quasar.config.js index 931f5302d7..741100f4d2 100644 --- a/ux/quasar.config.js +++ b/ux/quasar.config.js @@ -90,22 +90,24 @@ module.exports = configure(function (ctx) { distDir: '../assets', extendViteConf (viteConf) { - viteConf.build.assetsDir = '_assets' - viteConf.build.rollupOptions = { - ...viteConf.build.rollupOptions ?? {}, - output: { - manualChunks: { - lodash: ['lodash-es', 'lodash'], - quasar: ['quasar', 'quasar/src/components'] + if (ctx.prod) { + viteConf.build.assetsDir = '_assets' + viteConf.build.rollupOptions = { + ...viteConf.build.rollupOptions ?? {}, + output: { + manualChunks: { + lodash: ['lodash-es', 'lodash'], + quasar: ['quasar', 'quasar/src/components'] + } } } + viteConf.optimizeDeps.include = [ + 'prosemirror-state', + 'prosemirror-transform', + 'prosemirror-model', + 'prosemirror-view' + ] } - viteConf.optimizeDeps.include = [ - 'prosemirror-state', - 'prosemirror-transform', - 'prosemirror-model', - 'prosemirror-view' - ] }, // viteVuePluginOptions: {}, diff --git a/ux/src/boot/externals.js b/ux/src/boot/externals.js index f13a5819fc..d850fe541a 100644 --- a/ux/src/boot/externals.js +++ b/ux/src/boot/externals.js @@ -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 } }) diff --git a/ux/src/components/AccountMenu.vue b/ux/src/components/AccountMenu.vue index 8741f312d3..2fa57b4046 100644 --- a/ux/src/components/AccountMenu.vue +++ b/ux/src/components/AccountMenu.vue @@ -1,5 +1,5 @@