mirror of
https://github.com/codex-team/codex.docs.git
synced 2025-08-07 06:25:21 +02:00
Added navigation on page
This commit is contained in:
parent
8c0211d7bc
commit
587062d4d1
9 changed files with 231 additions and 7 deletions
|
@ -1,4 +1,5 @@
|
||||||
import database from '../utils/database/index';
|
import database from '../utils/database/index';
|
||||||
|
import Pages from '../controllers/pages';
|
||||||
|
|
||||||
const db = database['pagesOrder'];
|
const db = database['pagesOrder'];
|
||||||
|
|
||||||
|
@ -86,6 +87,105 @@ class PageOrder {
|
||||||
return new PageOrder(docs);
|
return new PageOrder(docs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns previous page for navigation
|
||||||
|
*
|
||||||
|
* @param {string} pageId - page's id
|
||||||
|
* @returns {Promise<string | null>} - previous page id
|
||||||
|
*/
|
||||||
|
public static async getPreviousNavigationPage(pageId: string): Promise<string | null> {
|
||||||
|
const page = await Pages.get(pageId);
|
||||||
|
|
||||||
|
const pageParent = await page.getParent();
|
||||||
|
|
||||||
|
let previousPageId = null;
|
||||||
|
|
||||||
|
// Check if page has a parent
|
||||||
|
if (pageParent._id) {
|
||||||
|
// Get order by parent
|
||||||
|
const order = await this.get(pageParent._id);
|
||||||
|
|
||||||
|
// Get previous page
|
||||||
|
previousPageId = order.getSubPageBefore(pageId);
|
||||||
|
|
||||||
|
// Check if previous page consists in parent order
|
||||||
|
if (!previousPageId) {
|
||||||
|
previousPageId = pageParent._id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return previousPageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get order, which includes getting page, because it has no parent
|
||||||
|
const order = await this.getRootPageOrder();
|
||||||
|
|
||||||
|
// Get parent page before page, which was gotten
|
||||||
|
const parentPageBefore = order.getSubPageBefore(pageId);
|
||||||
|
|
||||||
|
if (parentPageBefore) {
|
||||||
|
// Get previous parent page order
|
||||||
|
const newOrder = await this.get(parentPageBefore);
|
||||||
|
|
||||||
|
// Check if order is empty
|
||||||
|
if (!newOrder._order || newOrder._order.length == 0) {
|
||||||
|
return parentPageBefore;
|
||||||
|
}
|
||||||
|
previousPageId = newOrder._order[newOrder._order.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return previousPageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns next page for navigation
|
||||||
|
*
|
||||||
|
* @param {string} pageId - page's id
|
||||||
|
* @returns {Promise<string | null>} - next page id
|
||||||
|
*/
|
||||||
|
public static async getNextNavigationPage(pageId: string): Promise<string | null> {
|
||||||
|
const page = await Pages.get(pageId);
|
||||||
|
const pageParent = await page.getParent();
|
||||||
|
|
||||||
|
let nextPageId;
|
||||||
|
|
||||||
|
// Check if page has a parent
|
||||||
|
if (pageParent._id) {
|
||||||
|
let order = await this.get(pageParent._id);
|
||||||
|
|
||||||
|
// Get next page by parent order
|
||||||
|
nextPageId = order.getSubPageAfter(pageId);
|
||||||
|
|
||||||
|
// Check if next page consists in parent order
|
||||||
|
if (nextPageId) {
|
||||||
|
return nextPageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get order, which includes parent
|
||||||
|
order = await this.getRootPageOrder();
|
||||||
|
|
||||||
|
nextPageId = order.getSubPageAfter(pageParent._id);
|
||||||
|
|
||||||
|
return nextPageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get order by page id
|
||||||
|
const childOrder = await this.get(pageId);
|
||||||
|
|
||||||
|
// Check if order is empty
|
||||||
|
if (childOrder._order && childOrder._order.length > 0) {
|
||||||
|
nextPageId = childOrder._order[0];
|
||||||
|
|
||||||
|
return nextPageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get order, which includes getting page, because it has no parent
|
||||||
|
const order = await this.getRootPageOrder();
|
||||||
|
|
||||||
|
nextPageId = order.getSubPageAfter(pageId);
|
||||||
|
|
||||||
|
return nextPageId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns only child page's order
|
* Returns only child page's order
|
||||||
*
|
*
|
||||||
|
@ -182,7 +282,7 @@ class PageOrder {
|
||||||
*
|
*
|
||||||
* @param {string} pageId - identity of page
|
* @param {string} pageId - identity of page
|
||||||
*/
|
*/
|
||||||
public getPageBefore(pageId: string): string | null {
|
public getSubPageBefore(pageId: string): string | null {
|
||||||
if (this.order === undefined) {
|
if (this.order === undefined) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -204,7 +304,7 @@ class PageOrder {
|
||||||
*
|
*
|
||||||
* @param pageId - identity of page
|
* @param pageId - identity of page
|
||||||
*/
|
*/
|
||||||
public getPageAfter(pageId: string): string | null {
|
public getSubPageAfter(pageId: string): string | null {
|
||||||
if (this.order === undefined) {
|
if (this.order === undefined) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ import Aliases from '../controllers/aliases';
|
||||||
import Pages from '../controllers/pages';
|
import Pages from '../controllers/pages';
|
||||||
import Alias from '../models/alias';
|
import Alias from '../models/alias';
|
||||||
import verifyToken from './middlewares/token';
|
import verifyToken from './middlewares/token';
|
||||||
|
import PageOrder from '../models/pageOrder';
|
||||||
|
import Page from '../models/page';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
|
@ -32,9 +34,27 @@ router.get('*', verifyToken, async (req: Request, res: Response) => {
|
||||||
|
|
||||||
const pageParent = await page.getParent();
|
const pageParent = await page.getParent();
|
||||||
|
|
||||||
|
let previousPage;
|
||||||
|
|
||||||
|
let nextPage;
|
||||||
|
|
||||||
|
const previousPageId = await PageOrder.getPreviousNavigationPage(alias.id);
|
||||||
|
|
||||||
|
const nextPageId = await PageOrder.getNextNavigationPage(alias.id);
|
||||||
|
|
||||||
|
if (previousPageId){
|
||||||
|
previousPage = await Page.get(previousPageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextPageId) {
|
||||||
|
nextPage = await Page.get(nextPageId);
|
||||||
|
}
|
||||||
|
|
||||||
res.render('pages/page', {
|
res.render('pages/page', {
|
||||||
page,
|
page,
|
||||||
pageParent,
|
pageParent,
|
||||||
|
previousPage,
|
||||||
|
nextPage,
|
||||||
config: req.app.locals.config,
|
config: req.app.locals.config,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,8 +158,8 @@ router.delete('/page/:id', async (req: Request, res: Response) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const parentPageOrder = await PagesOrder.get(page._parent);
|
const parentPageOrder = await PagesOrder.get(page._parent);
|
||||||
const pageBeforeId = parentPageOrder.getPageBefore(page._id);
|
const pageBeforeId = parentPageOrder.getSubPageBefore(page._id);
|
||||||
const pageAfterId = parentPageOrder.getPageAfter(page._id);
|
const pageAfterId = parentPageOrder.getSubPageAfter(page._id);
|
||||||
|
|
||||||
let pageToRedirect;
|
let pageToRedirect;
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@ import Pages from '../controllers/pages';
|
||||||
import PagesOrder from '../controllers/pagesOrder';
|
import PagesOrder from '../controllers/pagesOrder';
|
||||||
import verifyToken from './middlewares/token';
|
import verifyToken from './middlewares/token';
|
||||||
import allowEdit from './middlewares/locals';
|
import allowEdit from './middlewares/locals';
|
||||||
|
import PageOrder from '../models/pageOrder';
|
||||||
|
import Page from '../models/page';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
|
@ -62,10 +64,28 @@ router.get('/page/:id', verifyToken, async (req: Request, res: Response, next: N
|
||||||
|
|
||||||
const pageParent = await page.parent;
|
const pageParent = await page.parent;
|
||||||
|
|
||||||
|
let previousPage;
|
||||||
|
|
||||||
|
let nextPage;
|
||||||
|
|
||||||
|
const previousPageId = await PageOrder.getPreviousNavigationPage(pageId);
|
||||||
|
|
||||||
|
const nextPageId = await PageOrder.getNextNavigationPage(pageId);
|
||||||
|
|
||||||
|
if (previousPageId) {
|
||||||
|
previousPage = await Page.get(previousPageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextPageId) {
|
||||||
|
nextPage = await Page.get(nextPageId);
|
||||||
|
}
|
||||||
|
|
||||||
res.render('pages/page', {
|
res.render('pages/page', {
|
||||||
page,
|
page,
|
||||||
pageParent,
|
pageParent,
|
||||||
config: req.app.locals.config,
|
config: req.app.locals.config,
|
||||||
|
previousPage,
|
||||||
|
nextPage,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(404);
|
res.status(404);
|
||||||
|
|
21
src/backend/views/components/navigator.twig
Normal file
21
src/backend/views/components/navigator.twig
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{% set mainClass = 'navigator' %}
|
||||||
|
|
||||||
|
|
||||||
|
{% set tag = 'div' %}
|
||||||
|
|
||||||
|
{% if url is not empty %}
|
||||||
|
{% set tag = 'a' %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<{{tag}}
|
||||||
|
{{ name is not empty ? 'name="' ~ name ~ '"': '' }}
|
||||||
|
class="{{ mainClass }} {{ mainClass }}--{{ direction|default('previous') }} {{ class ?? '' }}"
|
||||||
|
{{ url is not empty ? 'href="' ~ url ~ '"' : '' }}
|
||||||
|
>
|
||||||
|
<div class="{{mainClass}}__direction">
|
||||||
|
{{ direction }}
|
||||||
|
</div>
|
||||||
|
<div class="{{mainClass}}__label">
|
||||||
|
{{ label }}
|
||||||
|
</div>
|
||||||
|
</{{tag}}>
|
|
@ -23,6 +23,9 @@
|
||||||
{% include 'components/button.twig' with {label: 'Edit', icon: 'pencil', size: 'small', url: '/page/edit/' ~ page._id, class: 'page__header-button'} %}
|
{% include 'components/button.twig' with {label: 'Edit', icon: 'pencil', size: 'small', url: '/page/edit/' ~ page._id, class: 'page__header-button'} %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</header>
|
</header>
|
||||||
|
<script>
|
||||||
|
console.log({{prev.uri}})
|
||||||
|
</script>
|
||||||
<h1 class="page__title">
|
<h1 class="page__title">
|
||||||
{{ page.title }}
|
{{ page.title }}
|
||||||
</h1>
|
</h1>
|
||||||
|
@ -39,7 +42,17 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</section>
|
</section>
|
||||||
<footer>
|
<footer class="page_footer">
|
||||||
|
<div class="navigator_wrapper">
|
||||||
|
{% if previousPage %}
|
||||||
|
{% include 'components/navigator.twig' with {label: previousPage.title, direction: 'previous', url: '/' ~ previousPage.uri} %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="navigator_wrapper">
|
||||||
|
{% if nextPage %}
|
||||||
|
{% include 'components/navigator.twig' with {label: nextPage.title, direction: 'next', url: '/' ~ nextPage.uri} %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
|
|
46
src/frontend/styles/components/navigator.pcss
Normal file
46
src/frontend/styles/components/navigator.pcss
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
.navigator {
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
background-color: var(--color-bg-navigation);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 12px 16px 12px 16px;
|
||||||
|
color: black;
|
||||||
|
width: max-content;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
&--previous {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--next {
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__direction {
|
||||||
|
text-transform: capitalize;
|
||||||
|
color: var(--color-direction-navigation);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigator_wrapper {
|
||||||
|
max-width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page_footer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
}
|
|
@ -9,6 +9,7 @@
|
||||||
@import './components/auth.pcss';
|
@import './components/auth.pcss';
|
||||||
@import './components/button.pcss';
|
@import './components/button.pcss';
|
||||||
@import './components/sidebar.pcss';
|
@import './components/sidebar.pcss';
|
||||||
|
@import './components/navigator.pcss';
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: system-ui, Helvetica, Arial, Verdana;
|
font-family: system-ui, Helvetica, Arial, Verdana;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
:root {
|
:root {
|
||||||
--color-text-main: #313649;
|
--color-text-main: #313649;
|
||||||
--color-text-second: #5d6068;
|
--color-text-second: #5d6068;
|
||||||
|
--color-direction-navigation: #717682;
|
||||||
--color-line-gray: #E8E8EB;
|
--color-line-gray: #E8E8EB;
|
||||||
--color-link-active: #2071cc;
|
--color-link-active: #2071cc;
|
||||||
--color-link-hover: #F3F6F8;
|
--color-link-hover: #F3F6F8;
|
||||||
|
@ -19,6 +20,8 @@
|
||||||
--color-button-warning-hover: #D65151;
|
--color-button-warning-hover: #D65151;
|
||||||
--color-button-warning-active: #BD4848;
|
--color-button-warning-active: #BD4848;
|
||||||
|
|
||||||
|
--color-bg-navigation: #F3F6F8;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Site layout sizes
|
* Site layout sizes
|
||||||
|
@ -79,7 +82,7 @@
|
||||||
|
|
||||||
--squircle {
|
--squircle {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
||||||
@supports(-webkit-mask-box-image: url('')){
|
@supports(-webkit-mask-box-image: url('')){
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
-webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 10.3872C0 1.83334 1.83334 0 10.3872 0H13.6128C22.1667 0 24 1.83334 24 10.3872V13.6128C24 22.1667 22.1667 24 13.6128 24H10.3872C1.83334 24 0 22.1667 0 13.6128V10.3872Z' fill='black'/%3E%3C/svg%3E%0A") 48% 41% 37.9% 53.3%;;
|
-webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 10.3872C0 1.83334 1.83334 0 10.3872 0H13.6128C22.1667 0 24 1.83334 24 10.3872V13.6128C24 22.1667 22.1667 24 13.6128 24H10.3872C1.83334 24 0 22.1667 0 13.6128V10.3872Z' fill='black'/%3E%3C/svg%3E%0A") 48% 41% 37.9% 53.3%;;
|
||||||
|
@ -95,4 +98,4 @@
|
||||||
@custom-media --tablet all and (min-width: 980px) and (max-width: 1050px);
|
@custom-media --tablet all and (min-width: 980px) and (max-width: 1050px);
|
||||||
@custom-media --mobile all and (max-width: 980px);
|
@custom-media --mobile all and (max-width: 980px);
|
||||||
@custom-media --retina all and (-webkit-min-device-pixel-ratio: 1.5);
|
@custom-media --retina all and (-webkit-min-device-pixel-ratio: 1.5);
|
||||||
@custom-media --can-hover all and (hover:hover)
|
@custom-media --can-hover all and (hover:hover)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue