1
0
Fork 0
mirror of https://github.com/codex-team/codex.docs.git synced 2025-08-09 23:45:25 +02:00

Merge branch 'main' into feat/styles

This commit is contained in:
Peter Savchenko 2022-09-05 23:47:33 +03:00
commit 2ff1f0729a
No known key found for this signature in database
GPG key ID: E68306B1AB0F727C
12 changed files with 174 additions and 21 deletions

View file

@ -18,6 +18,7 @@
"editor-upgrade": "yarn add -D @editorjs/{editorjs,header,code,delimiter,list,link,image,table,inline-code,marker,warning,checklist,raw}@latest"
},
"dependencies": {
"@codexteam/shortcuts": "^1.2.0",
"@hawk.so/javascript": "^3.0.1",
"@hawk.so/nodejs": "^3.1.2",
"config": "^3.3.6",

View file

@ -14,10 +14,6 @@ class Aliases {
public static async get(aliasName: string): Promise<Alias> {
const alias = await Alias.get(aliasName);
if (!alias.id) {
throw new Error('Entity with given alias does not exist');
}
return alias;
}
}

View file

@ -43,7 +43,7 @@ export interface PagesFlatArrayData {
}
/**
* @class PagesFlatArray model - flat array of pages, which are ordered like in sidebar
* @class PagesFlatArray model - flat array of pages, which are ordered like in sidebar
*/
class PagesFlatArray {
/**

View file

@ -1,9 +1,11 @@
import express, { Request, Response } from 'express';
import Aliases from '../controllers/aliases.js';
import Pages from '../controllers/pages.js';
import Alias from '../models/alias.js';
import verifyToken from './middlewares/token.js';
import PagesFlatArray from '../models/pagesFlatArray.js';
import Aliases from '../controllers/aliases';
import Pages from '../controllers/pages';
import Alias from '../models/alias';
import verifyToken from './middlewares/token';
import PagesFlatArray from '../models/pagesFlatArray';
import HttpException from '../exceptions/httpException';
const router = express.Router();
@ -24,7 +26,7 @@ router.get('*', verifyToken, async (req: Request, res: Response) => {
const alias = await Aliases.get(url);
if (alias.id === undefined) {
throw new Error('Alias not found');
throw new HttpException(404, 'Alias not found');
}
switch (alias.type) {
@ -46,11 +48,18 @@ router.get('*', verifyToken, async (req: Request, res: Response) => {
}
}
} catch (err) {
res.status(400).json({
success: false,
error: err,
});
if (err instanceof HttpException && err.status === 404) {
res.status(404).render('error', {
message: 'Page not found',
status: 404,
});
} else {
res.status(500).json({
success: false,
error: err,
});
}
}
});
export default router;
export default router;

View file

@ -52,4 +52,8 @@
</div>
</aside>
<div class="docs-sidebar__slider">
{{ svg('arrow-left') }}
</div>
</div>

View file

@ -1,7 +1,10 @@
{% extends 'layout.twig' %}
{% block body %}
<h1>{{message}}</h1>
<h2>{{error.status}}</h2>
<pre>{{error.stack}}</pre>
<div class="error-page">
<h1>
┬┴┬┴┤ {{status}} ├┬┴┬┴
</h1>
<h1>{{message}}</h1>
</div>
{% endblock %}

View file

@ -1,10 +1,11 @@
import { Storage } from '../utils/storage';
import Shortcut from '@codexteam/shortcuts';
/**
* Local storage key
*/
const LOCAL_STORAGE_KEY = 'docs_sidebar_state';
const SIDEBAR_VISIBILITY_KEY = 'docs_sidebar_visibility';
/**
* Section list item height in px
@ -31,6 +32,9 @@ export default class Sidebar {
sectionList: 'docs-sidebar__section-list',
sectionListItemActive: 'docs-sidebar__section-list-item--active',
sidebarToggler: 'docs-sidebar__toggler',
sidebarSlider: 'docs-sidebar__slider',
sidebarCollapsed: 'docs-sidebar--collapsed',
sidebarAnimated: 'docs-sidebar--animated',
sidebarContent: 'docs-sidebar__content',
sidebarContentHidden: 'docs-sidebar__content--hidden',
sidebarContentInvisible: 'docs-sidebar__content--invisible',
@ -45,14 +49,24 @@ export default class Sidebar {
* Stores refs to HTML elements needed for correct sidebar work
*/
this.nodes = {
sidebar: null,
sections: [],
sidebarContent: null,
toggler: null,
slider: null,
};
this.sidebarStorage = new Storage(LOCAL_STORAGE_KEY);
const storedState = this.sidebarStorage.get();
this.sectionsState = storedState ? JSON.parse(storedState) : {};
// Initialize localStorage that contains sidebar visibility
this.sidebarVisibilityStorage = new Storage(SIDEBAR_VISIBILITY_KEY);
// Get current sidebar visibility from storage
const storedVisibility = this.sidebarVisibilityStorage.get();
// Sidebar visibility
this.isVisible = storedVisibility !== 'false';
}
/**
@ -62,11 +76,14 @@ export default class Sidebar {
* @param {HTMLElement} moduleEl - module element
*/
init(settings, moduleEl) {
this.nodes.sidebar = moduleEl;
this.nodes.sections = Array.from(moduleEl.querySelectorAll('.' + Sidebar.CSS.section));
this.nodes.sections.forEach(section => this.initSection(section));
this.nodes.sidebarContent = moduleEl.querySelector('.' + Sidebar.CSS.sidebarContent);
this.nodes.toggler = moduleEl.querySelector('.' + Sidebar.CSS.sidebarToggler);
this.nodes.toggler.addEventListener('click', () => this.toggleSidebar());
this.nodes.slider = moduleEl.querySelector('.' + Sidebar.CSS.sidebarSlider);
this.nodes.slider.addEventListener('click', () => this.handleSliderClick());
this.ready();
}
@ -104,7 +121,7 @@ export default class Sidebar {
const itemsCount = sectionList.children.length;
sectionList.style.maxHeight = `${ itemsCount * ITEM_HEIGHT }px`;
sectionList.style.maxHeight = `${itemsCount * ITEM_HEIGHT}px`;
}
/**
@ -168,12 +185,52 @@ export default class Sidebar {
this.nodes.sidebarContent.classList.toggle(Sidebar.CSS.sidebarContentHidden);
}
/**
* Initializes sidebar
*
* @returns {void}
*/
initSidebar() {
if (!this.isVisible) {
this.nodes.sidebar.classList.add(Sidebar.CSS.sidebarCollapsed);
}
/**
* prevent sidebar animation on page load
* Since animated class contains transition, hiding will be animated with it
* To prevent awkward animation when visibility is set to false, we need to remove animated class
*/
setTimeout(() => {
this.nodes.sidebar.classList.add(Sidebar.CSS.sidebarAnimated);
}, 200);
// add event listener to execute keyboard shortcut
// eslint-disable-next-line no-new
new Shortcut({
name: 'CMD+.',
on: document.body,
callback: () => this.handleSliderClick(),
});
}
/**
* Slides sidebar
*
* @returns {void}
*/
handleSliderClick() {
this.isVisible = !this.isVisible;
this.sidebarVisibilityStorage.set(this.isVisible);
this.nodes.sidebar.classList.toggle(Sidebar.CSS.sidebarCollapsed);
}
/**
* Displays sidebar when ready
*
* @returns {void}
*/
ready() {
this.initSidebar();
this.nodes.sidebarContent.classList.remove(Sidebar.CSS.sidebarContentInvisible);
}
}

View file

@ -0,0 +1,29 @@
.error-page {
font-size: 15px;
text-align: center;
position: absolute;
top: 45%;
left: 50%;
@media (--mobile) {
position: relative;
top: 30vh;
left: 0;
}
@media (--tablet) {
position: relative;
top: 30vh;
left: 0;
}
h1 {
@media (--mobile) {
font-size: 20px;
}
}
p {
margin: 40px 0 20px;
}
}

View file

@ -1,6 +1,34 @@
.docs-sidebar {
width: 100vw;
&--animated {
.docs-sidebar__content {
transition: transform 200ms ease-in-out;
will-change: transform;
}
.docs-sidebar__slider {
transition: transform 200ms ease-in-out;
will-change: transform;
}
}
&--collapsed {
@media (--desktop) {
.docs-sidebar__content {
transform: translateX(-100%);
}
}
.docs-sidebar__slider {
transform: translateX(20px);
svg {
transform: rotate(180deg);
}
}
}
@media (--desktop) {
width: var(--layout-sidebar-width);
}
@ -196,6 +224,24 @@
}
}
&__slider {
display: none;
position: fixed;
transform: translateX(calc(var(--layout-sidebar-width) + 20px));
bottom: 20px;
width: 32px;
height: 32px;
border-radius: 8px;
cursor: pointer;
background-color: var(--color-link-hover);
@media (--desktop) {
display: flex;
justify-content: center;
align-items: center;
}
}
&__logo {
display: none;
margin-top: auto;

View file

@ -8,6 +8,7 @@
@import './components/page.pcss';
@import './components/landing.pcss';
@import './components/auth.pcss';
@import './components/error.pcss';
@import './components/button.pcss';
@import './components/sidebar.pcss';
@import './components/navigator.pcss';

View file

@ -0,0 +1,3 @@
<svg width="7" height="11" viewBox="0 0 7 11" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.95579 10.2458C6.14204 10.0584 6.24658 9.80498 6.24658 9.5408C6.24658 9.27661 6.14204 9.02316 5.95579 8.8358L2.41579 5.2458L5.95579 1.7058C6.14204 1.51844 6.24658 1.26498 6.24658 1.0008C6.24658 0.736612 6.14204 0.483161 5.95579 0.295798C5.86283 0.20207 5.75223 0.127675 5.63037 0.0769067C5.50851 0.026138 5.3778 0 5.24579 0C5.11378 0 4.98307 0.026138 4.86121 0.0769067C4.73935 0.127675 4.62875 0.20207 4.53579 0.295798L0.295789 4.5358C0.202061 4.62876 0.127667 4.73936 0.0768978 4.86122C0.0261292 4.98308 -9.32772e-06 5.11379 -9.32772e-06 5.2458C-9.32772e-06 5.37781 0.0261292 5.50852 0.0768978 5.63037C0.127667 5.75223 0.202061 5.86284 0.295789 5.9558L4.53579 10.2458C4.62875 10.3395 4.73935 10.4139 4.86121 10.4647C4.98307 10.5155 5.11378 10.5416 5.24579 10.5416C5.3778 10.5416 5.50851 10.5155 5.63037 10.4647C5.75223 10.4139 5.86283 10.3395 5.95579 10.2458Z" fill="#060C26"/>
</svg>

After

Width:  |  Height:  |  Size: 991 B

View file

@ -817,6 +817,10 @@
version "1.0.0"
resolved "https://registry.yarnpkg.com/@codexteam/misprints/-/misprints-1.0.0.tgz#e5a7dec7389fe0f176cd51a040d6dc9bdc252086"
"@codexteam/shortcuts@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@codexteam/shortcuts/-/shortcuts-1.2.0.tgz#b8dd7396962b0bd845a5c8f8f19bc6119b520e19"
"@cspotcode/source-map-support@^0.8.0":
version "0.8.1"
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"