mirror of
https://github.com/codex-team/codex.docs.git
synced 2025-08-10 07:55:24 +02:00
fix scroll issues, resolve eslit ts/js conflicts
This commit is contained in:
parent
23be283421
commit
1a100938b0
9 changed files with 251 additions and 1105 deletions
13
src/frontend/.eslintrc
Normal file
13
src/frontend/.eslintrc
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"codex"
|
||||||
|
],
|
||||||
|
"parser": "babel-eslint",
|
||||||
|
"parserOptions": {
|
||||||
|
"sourceType": "module",
|
||||||
|
"allowImportExportEverywhere": true
|
||||||
|
},
|
||||||
|
"globals": {
|
||||||
|
"HTMLElement": true
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
import { Decorators } from '../utils/decorators';
|
import * as Decorators from '../utils/decorators';
|
||||||
|
import * as $ from '../utils/dom';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate dynamic table of content
|
* Generate dynamic table of content
|
||||||
|
@ -7,10 +8,11 @@ export default class TableOfContent {
|
||||||
/**
|
/**
|
||||||
* Initialize table of content
|
* Initialize table of content
|
||||||
*
|
*
|
||||||
* @param {string} tagSelector - selector for tags to observe
|
* @param {object} options - constructor params
|
||||||
* @param {string} tocParentElement - selector for table of content wrapper
|
* @param {string} options.tagSelector - selector for tags to observe
|
||||||
|
* @param {HTMLElement} options.appendTo - element for appending of the table of content
|
||||||
*/
|
*/
|
||||||
constructor({ tagSelector, tocParentElement}) {
|
constructor({ tagSelector, appendTo }) {
|
||||||
/**
|
/**
|
||||||
* Array of tags to observe
|
* Array of tags to observe
|
||||||
*/
|
*/
|
||||||
|
@ -23,9 +25,30 @@ export default class TableOfContent {
|
||||||
this.tagSelector = tagSelector || 'h2,h3,h4';
|
this.tagSelector = tagSelector || 'h2,h3,h4';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Selector for table of content wrapper
|
* Element to append the Table of Content
|
||||||
*/
|
*/
|
||||||
this.tocParentElement = tocParentElement;
|
this.tocParentElement = appendTo;
|
||||||
|
|
||||||
|
if (!this.tocParentElement) {
|
||||||
|
throw new Error('Table of Content wrapper not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.nodes = {
|
||||||
|
/**
|
||||||
|
* Main Table of Content element
|
||||||
|
*/
|
||||||
|
wrapper: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of Table of Content links
|
||||||
|
*/
|
||||||
|
items: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Currently highlighted element of ToC
|
||||||
|
*/
|
||||||
|
this.activeItem = null;
|
||||||
|
|
||||||
this.CSS = {
|
this.CSS = {
|
||||||
tocContainer: 'table-of-content',
|
tocContainer: 'table-of-content',
|
||||||
|
@ -59,16 +82,16 @@ export default class TableOfContent {
|
||||||
this.addTableOfContent();
|
this.addTableOfContent();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate boundings for each tag and watch active section
|
* Calculate bounds for each tag and watch active section
|
||||||
*/
|
*/
|
||||||
this.calculateBoundings();
|
this.calculateBounds();
|
||||||
this.watchActiveSection();
|
this.watchActiveSection();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find all section tags on the page
|
* Find all section tags on the page
|
||||||
*
|
*
|
||||||
* @return {HTMLElement[]}
|
* @returns {HTMLElement[]}
|
||||||
*/
|
*/
|
||||||
getSectionTagsOnThePage() {
|
getSectionTagsOnThePage() {
|
||||||
return Array.from(document.querySelectorAll(this.tagSelector));
|
return Array.from(document.querySelectorAll(this.tagSelector));
|
||||||
|
@ -77,7 +100,7 @@ export default class TableOfContent {
|
||||||
/**
|
/**
|
||||||
* Calculate top line position for each tag
|
* Calculate top line position for each tag
|
||||||
*/
|
*/
|
||||||
calculateBoundings() {
|
calculateBounds() {
|
||||||
this.tagsSectionsMap = this.tags.map((tag) => {
|
this.tagsSectionsMap = this.tags.map((tag) => {
|
||||||
const rect = tag.getBoundingClientRect();
|
const rect = tag.getBoundingClientRect();
|
||||||
const top = Math.floor(rect.top + window.scrollY);
|
const top = Math.floor(rect.top + window.scrollY);
|
||||||
|
@ -93,13 +116,24 @@ export default class TableOfContent {
|
||||||
* Watch active section while scrolling
|
* Watch active section while scrolling
|
||||||
*/
|
*/
|
||||||
watchActiveSection() {
|
watchActiveSection() {
|
||||||
|
/**
|
||||||
|
* Where content zone starts in document
|
||||||
|
*/
|
||||||
|
const contentTopOffset = this.getScrollPadding();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Treat section as active if it reaches the 1/5 of viewport from top
|
||||||
|
* For example, for a window with 1006px height it will be 219px
|
||||||
|
*/
|
||||||
|
const activationOffset = window.innerHeight / 5;
|
||||||
|
|
||||||
const detectSection = () => {
|
const detectSection = () => {
|
||||||
/**
|
/**
|
||||||
* Calculate scroll position
|
* Calculate scroll position
|
||||||
*
|
*
|
||||||
* @todo research how not to use magic number
|
* @todo research how not to use magic number
|
||||||
*/
|
*/
|
||||||
let scrollPosition = this.getScrollPadding() + window.scrollY + 1;
|
const scrollPosition = contentTopOffset + window.scrollY + activationOffset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the nearest section above the scroll position
|
* Find the nearest section above the scroll position
|
||||||
|
@ -114,12 +148,12 @@ export default class TableOfContent {
|
||||||
if (section) {
|
if (section) {
|
||||||
const targetLink = section.tag.querySelector('a').getAttribute('href');
|
const targetLink = section.tag.querySelector('a').getAttribute('href');
|
||||||
|
|
||||||
this.setActiveLink(targetLink);
|
this.setActiveItem(targetLink);
|
||||||
} else {
|
} else {
|
||||||
/**
|
/**
|
||||||
* Otherwise no active link will be highlighted
|
* Otherwise no active link will be highlighted
|
||||||
*/
|
*/
|
||||||
this.setActiveLink();
|
this.setActiveItem(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -128,12 +162,14 @@ export default class TableOfContent {
|
||||||
*/
|
*/
|
||||||
const throttledDetectSectionFunction = Decorators.throttle(() => {
|
const throttledDetectSectionFunction = Decorators.throttle(() => {
|
||||||
detectSection();
|
detectSection();
|
||||||
}, 200);
|
}, 400);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scroll listener
|
* Scroll listener
|
||||||
*/
|
*/
|
||||||
document.addEventListener('scroll', throttledDetectSectionFunction);
|
document.addEventListener('scroll', throttledDetectSectionFunction, {
|
||||||
|
passive: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -148,19 +184,16 @@ export default class TableOfContent {
|
||||||
* </section>
|
* </section>
|
||||||
*/
|
*/
|
||||||
createTableOfContent() {
|
createTableOfContent() {
|
||||||
this.tocElement = document.createElement('section');
|
this.tocElement = $.make('section', this.CSS.tocElement);
|
||||||
this.tocElement.classList.add(this.CSS.tocElement);
|
|
||||||
|
|
||||||
this.tags.forEach((tag) => {
|
this.tags.forEach((tag) => {
|
||||||
const linkTarget = tag.querySelector('a').getAttribute('href');
|
const linkTarget = tag.querySelector('a').getAttribute('href');
|
||||||
|
|
||||||
const linkWrapper = document.createElement('li');
|
const linkWrapper = $.make('li', this.CSS.tocElementItem);
|
||||||
const linkBlock = document.createElement('a');
|
const linkBlock = $.make('a', null, {
|
||||||
|
innerText: tag.innerText,
|
||||||
linkBlock.innerText = tag.innerText;
|
href: `${linkTarget}`,
|
||||||
linkBlock.href = `${linkTarget}`;
|
});
|
||||||
|
|
||||||
linkWrapper.classList.add(this.CSS.tocElementItem);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Additional indent for h3-h6 headers
|
* Additional indent for h3-h6 headers
|
||||||
|
@ -182,6 +215,8 @@ export default class TableOfContent {
|
||||||
|
|
||||||
linkWrapper.appendChild(linkBlock);
|
linkWrapper.appendChild(linkBlock);
|
||||||
this.tocElement.appendChild(linkWrapper);
|
this.tocElement.appendChild(linkWrapper);
|
||||||
|
|
||||||
|
this.nodes.items.push(linkWrapper);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,74 +224,110 @@ export default class TableOfContent {
|
||||||
* Add table of content to the page
|
* Add table of content to the page
|
||||||
*/
|
*/
|
||||||
addTableOfContent() {
|
addTableOfContent() {
|
||||||
const header = document.createElement('header');
|
this.nodes.wrapper = $.make('section', this.CSS.tocContainer);
|
||||||
const container = document.createElement('section');
|
|
||||||
|
|
||||||
header.innerText = 'On this page';
|
const header = $.make('header', this.CSS.tocHeader, {
|
||||||
header.classList.add(this.CSS.tocHeader);
|
textContent: 'On this page',
|
||||||
container.appendChild(header);
|
});
|
||||||
|
|
||||||
container.classList.add(this.CSS.tocContainer);
|
this.nodes.wrapper.appendChild(header);
|
||||||
container.appendChild(this.tocElement);
|
this.nodes.wrapper.appendChild(this.tocElement);
|
||||||
|
|
||||||
const tocWrapper = document.querySelector(this.tocParentElement);
|
this.tocParentElement.appendChild(this.nodes.wrapper);
|
||||||
|
|
||||||
if (!tocWrapper) {
|
|
||||||
throw new Error('Table of content wrapper not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
tocWrapper.appendChild(container);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Highlight link's item with a given href
|
* Highlight link's item with a given href
|
||||||
*
|
*
|
||||||
* @param {string} targetLink - href of the link
|
* @param {string|null} targetLink - href of the link. Null if we need to clear all highlights
|
||||||
* @param {boolean} [needHighlightPrevious=false] - need to highlight previous link instead of current
|
|
||||||
*/
|
*/
|
||||||
setActiveLink(targetLink, needHighlightPrevious = false) {
|
setActiveItem(targetLink) {
|
||||||
/**
|
/**
|
||||||
* Clear all links
|
* Clear current highlight
|
||||||
*/
|
*/
|
||||||
this.tocElement.querySelectorAll(`.${this.CSS.tocElementItem}`).forEach((link) => {
|
if (this.activeItem) {
|
||||||
link.classList.remove(this.CSS.tocElementItemActive);
|
this.activeItem.classList.remove(this.CSS.tocElementItemActive);
|
||||||
});
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If targetLink is not defined then do nothing
|
* If targetLink is null, that means we reached top, nothing to highlight
|
||||||
*/
|
*/
|
||||||
if (!targetLink) {
|
if (targetLink === null) {
|
||||||
/**
|
|
||||||
* Show the top of table of content
|
|
||||||
*/
|
|
||||||
document.querySelector(`.${this.CSS.tocHeader}`).scrollIntoViewIfNeeded();
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Looking for a target link
|
* Looking for a target link
|
||||||
|
*
|
||||||
|
* @todo do not fire DOM search, use saved map instead
|
||||||
*/
|
*/
|
||||||
const targetElement = this.tocElement.querySelector(`a[href="${targetLink}"]`);
|
const targetElement = this.tocElement.querySelector(`a[href="${targetLink}"]`);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getting link's wrapper
|
* Getting link's wrapper
|
||||||
*/
|
*/
|
||||||
let listItem = targetElement.parentNode;
|
const listItem = targetElement.parentNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change target list item if it is needed
|
* Highlight and save current item
|
||||||
*/
|
*/
|
||||||
if (needHighlightPrevious) {
|
listItem.classList.add(this.CSS.tocElementItemActive);
|
||||||
listItem = listItem.previousSibling;
|
this.activeItem = listItem;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If target list item is found then highlight it
|
* If need, scroll parent to active item
|
||||||
*/
|
*/
|
||||||
if (listItem) {
|
this.scrollToActiveItemIfNeeded();
|
||||||
listItem.classList.add(this.CSS.tocElementItemActive);
|
}
|
||||||
listItem.scrollIntoViewIfNeeded();
|
|
||||||
|
/**
|
||||||
|
* Document scroll ending callback
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
scrollToActiveItemIfNeeded() {
|
||||||
|
console.log('computations! )))');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If some item is highlighted, check whether we need to scroll to it or not
|
||||||
|
*/
|
||||||
|
if (this.activeItem) {
|
||||||
|
/**
|
||||||
|
* First, check do we need to scroll to item?
|
||||||
|
* We need to scroll in case when:
|
||||||
|
* item bottom coord is bigger than parent height + current parent scroll
|
||||||
|
*/
|
||||||
|
const itemOffsetTop = this.activeItem.offsetTop;
|
||||||
|
const itemHeight = this.activeItem.offsetHeight;
|
||||||
|
const itemBottomCoord = itemOffsetTop + itemHeight;
|
||||||
|
const additionalOffsetBelowItem = 10; // padding below item
|
||||||
|
const itemBottomCoordWithPadding = itemBottomCoord + additionalOffsetBelowItem;
|
||||||
|
|
||||||
|
const scrollableParentHeight = this.nodes.wrapper.offsetHeight;
|
||||||
|
const scrollableParentScrolledDistance = this.nodes.wrapper.scrollTop;
|
||||||
|
|
||||||
|
const isScrollRequired = itemBottomCoordWithPadding > scrollableParentHeight + scrollableParentScrolledDistance;
|
||||||
|
|
||||||
|
if (isScrollRequired === false) {
|
||||||
|
/**
|
||||||
|
* Item is visible, scroll is not needed
|
||||||
|
*/
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Now compute the scroll distance to make item visible
|
||||||
|
*/
|
||||||
|
|
||||||
|
const distanceToMakeItemFullyVisible = itemBottomCoordWithPadding - scrollableParentHeight;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the scroll
|
||||||
|
* Using RAF to prevent overloading of regular scroll animation FPS
|
||||||
|
*/
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
this.nodes.wrapper.scrollTop = distanceToMakeItemFullyVisible;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,7 +351,7 @@ export default class TableOfContent {
|
||||||
/**
|
/**
|
||||||
* Getting css scroll padding value
|
* Getting css scroll padding value
|
||||||
*/
|
*/
|
||||||
const scrollPaddingTopValue = getComputedStyle(htmlElement)
|
const scrollPaddingTopValue = window.getComputedStyle(htmlElement)
|
||||||
.getPropertyValue('scroll-padding-top');
|
.getPropertyValue('scroll-padding-top');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
/**
|
|
||||||
* @typedef {object} pageModuleSettings
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class Page
|
* @class Page
|
||||||
* @classdesc Class for page module
|
* @classdesc Class for page module
|
||||||
|
@ -17,10 +13,8 @@ export default class Page {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by ModuleDispatcher to initialize module from DOM
|
* Called by ModuleDispatcher to initialize module from DOM
|
||||||
* @param {pageModuleSettings} settings - module settings
|
|
||||||
* @param {HTMLElement} moduleEl - module element
|
|
||||||
*/
|
*/
|
||||||
init(settings = {}, moduleEl) {
|
init() {
|
||||||
this.codeStyler = this.createCodeStyling();
|
this.codeStyler = this.createCodeStyling();
|
||||||
this.tableOfContent = this.createTableOfContent();
|
this.tableOfContent = this.createTableOfContent();
|
||||||
}
|
}
|
||||||
|
@ -31,24 +25,35 @@ export default class Page {
|
||||||
async createCodeStyling() {
|
async createCodeStyling() {
|
||||||
const { default: CodeStyler } = await import(/* webpackChunkName: "code-styling" */ './../classes/codeStyler');
|
const { default: CodeStyler } = await import(/* webpackChunkName: "code-styling" */ './../classes/codeStyler');
|
||||||
|
|
||||||
return new CodeStyler({
|
try {
|
||||||
selector: '.block-code__content',
|
// eslint-disable-next-line no-new
|
||||||
});
|
new CodeStyler({
|
||||||
|
selector: '.block-code__content',
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error); // @todo send to Hawk
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init table of content
|
* Init table of content
|
||||||
* @return {Promise<TableOfContent>}
|
*
|
||||||
|
* @returns {Promise<TableOfContent>}
|
||||||
*/
|
*/
|
||||||
async createTableOfContent() {
|
async createTableOfContent() {
|
||||||
const { default: TableOfContent } = await import(/* webpackChunkName: "table-of-content" */ '../classes/table-of-content');
|
const { default: TableOfContent } = await import(/* webpackChunkName: "table-of-content" */ '../classes/table-of-content');
|
||||||
|
|
||||||
return new TableOfContent({
|
try {
|
||||||
tagSelector:
|
// eslint-disable-next-line no-new
|
||||||
'h2.block-header--anchor,' +
|
new TableOfContent({
|
||||||
'h3.block-header--anchor,' +
|
tagSelector:
|
||||||
'h4.block-header--anchor',
|
'h2.block-header--anchor,' +
|
||||||
tocParentElement: '#layout-sidebar-right'
|
'h3.block-header--anchor,' +
|
||||||
});
|
'h4.block-header--anchor',
|
||||||
|
appendTo: document.getElementById('layout-sidebar-right'),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error); // @todo send to Hawk
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,63 +1,62 @@
|
||||||
/**
|
/**
|
||||||
* A few useful utility functions
|
* A few useful utility functions
|
||||||
*/
|
*/
|
||||||
export class Decorators {
|
|
||||||
/**
|
|
||||||
* Throttle decorator function
|
|
||||||
*
|
|
||||||
* @param {Function} func - function to throttle
|
|
||||||
* @param {number} ms - milliseconds to throttle
|
|
||||||
*
|
|
||||||
* @returns {wrapper}
|
|
||||||
*/
|
|
||||||
static throttle(func, ms) {
|
|
||||||
let isThrottled = false,
|
|
||||||
savedArgs,
|
|
||||||
savedThis;
|
|
||||||
|
|
||||||
function wrapper() {
|
/**
|
||||||
if (isThrottled) {
|
* Throttle decorator function
|
||||||
savedArgs = arguments;
|
*
|
||||||
savedThis = this;
|
* @param {Function} func - function to throttle
|
||||||
return;
|
* @param {number} ms - milliseconds to throttle
|
||||||
}
|
*
|
||||||
|
* @returns {Function}
|
||||||
|
*/
|
||||||
|
export function throttle(func, ms) {
|
||||||
|
let isThrottled = false,
|
||||||
|
savedArgs,
|
||||||
|
savedThis;
|
||||||
|
|
||||||
func.apply(this, arguments);
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||||
|
function wrapper() {
|
||||||
|
if (isThrottled) {
|
||||||
|
savedArgs = arguments;
|
||||||
|
savedThis = this;
|
||||||
|
|
||||||
isThrottled = true;
|
return;
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
isThrottled = false;
|
|
||||||
|
|
||||||
if (savedArgs) {
|
|
||||||
wrapper.apply(savedThis, savedArgs);
|
|
||||||
savedArgs = savedThis = null;
|
|
||||||
}
|
|
||||||
}, ms);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return wrapper;
|
func.apply(this, arguments);
|
||||||
|
|
||||||
|
isThrottled = true;
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
isThrottled = false;
|
||||||
|
|
||||||
|
if (savedArgs) {
|
||||||
|
wrapper.apply(savedThis, savedArgs);
|
||||||
|
savedArgs = savedThis = null;
|
||||||
|
}
|
||||||
|
}, ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
return wrapper;
|
||||||
* Debounce decorator function
|
}
|
||||||
*
|
|
||||||
* @param {Function} f - function to debounce
|
/**
|
||||||
* @param {number} ms - milliseconds to debounce
|
* Debounce decorator function
|
||||||
*
|
*
|
||||||
* @returns {(function(): void)|*}
|
* @param {Function} f - function to debounce
|
||||||
*/
|
* @param {number} ms - milliseconds to debounce
|
||||||
static debounce(f, ms) {
|
*
|
||||||
let isCooldown = false;
|
* @returns {(function(): void)|*}
|
||||||
|
*/
|
||||||
return function () {
|
export function debounce(f, ms) {
|
||||||
if (isCooldown) return;
|
let timeoutId = null;
|
||||||
|
|
||||||
f.apply(this, arguments);
|
return function () {
|
||||||
|
if (timeoutId) {
|
||||||
isCooldown = true;
|
clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
setTimeout(() => isCooldown = false, ms);
|
|
||||||
};
|
timeoutId = setTimeout(() => f.apply(this, arguments), ms);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
23
src/frontend/js/utils/dom.js
Normal file
23
src/frontend/js/utils/dom.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/**
|
||||||
|
* Helper method for elements creation
|
||||||
|
*
|
||||||
|
* @param {string} tagName - name of tag to create
|
||||||
|
* @param {string | string[]} classNames - list of CSS classes
|
||||||
|
* @param {object} attributes - any properties to add
|
||||||
|
* @returns {HTMLElement}
|
||||||
|
*/
|
||||||
|
export function make(tagName, classNames = null, attributes = {}) {
|
||||||
|
const el = document.createElement(tagName);
|
||||||
|
|
||||||
|
if (Array.isArray(classNames)) {
|
||||||
|
el.classList.add(...classNames);
|
||||||
|
} else if (classNames) {
|
||||||
|
el.classList.add(classNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const attrName in attributes) {
|
||||||
|
el[attrName] = attributes[attrName];
|
||||||
|
}
|
||||||
|
|
||||||
|
return el;
|
||||||
|
}
|
|
@ -2,6 +2,11 @@
|
||||||
border-left: 1px solid var(--color-line-gray);
|
border-left: 1px solid var(--color-line-gray);
|
||||||
padding-left: var(--layout-padding-horizontal);
|
padding-left: var(--layout-padding-horizontal);
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
padding: var(--layout-padding-vertical) var(--layout-padding-horizontal);
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|
|
@ -20,15 +20,15 @@
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
|
||||||
@media (--desktop) {
|
@media (--desktop) {
|
||||||
margin-right: var(--layout-padding-horizontal);
|
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
|
padding: var(--layout-padding-vertical) var(--layout-padding-horizontal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
--max-space-between-cols: 160px;
|
--max-space-between-cols: 160px;
|
||||||
padding: var(--layout-padding-vertical) var(--layout-padding-horizontal);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
max-width: calc(var(--layout-width-main-col) + var(--max-space-between-cols) + var(--layout-sidebar-width));
|
max-width: calc(var(--layout-width-main-col) + var(--max-space-between-cols) + var(--layout-sidebar-width));
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
height: calc(100vh - var(--layout-height-header));
|
height: calc(100vh - var(--layout-height-header));
|
||||||
|
|
||||||
top: calc(var(--layout-height-header) + var(--layout-padding-vertical));
|
top: calc(var(--layout-height-header));
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
|
|
||||||
@media (--desktop) {
|
@media (--desktop) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue