id-community-style-kit
Version:
A style kit for .id community products
340 lines (253 loc) • 7.8 kB
JavaScript
require('./prism');
/**
* Global `fabricator` object
* @namespace
*/
const fabricator = window.fabricator = {};
/**
* Default options
* @type {Object}
*/
fabricator.options = {
toggles: {
labels: true,
notes: true,
code: false,
},
menu: false,
mq: '(min-width: 60em)',
};
// open menu by default if large screen
fabricator.options.menu = window.matchMedia(fabricator.options.mq).matches;
/**
* Feature detection
* @type {Object}
*/
fabricator.test = {};
// test for sessionStorage
fabricator.test.sessionStorage = (() => {
const test = '_f';
try {
sessionStorage.setItem(test, test);
sessionStorage.removeItem(test);
return true;
} catch (e) {
return false;
}
})();
// create storage object if it doesn't exist; store options
if (fabricator.test.sessionStorage) {
sessionStorage.fabricator = sessionStorage.fabricator || JSON.stringify(fabricator.options);
}
/**
* Cache DOM
* @type {Object}
*/
fabricator.dom = {
root: document.querySelector('html'),
primaryMenu: document.querySelector('.f-menu'),
menuItems: document.querySelectorAll('.f-menu li a'),
menuToggle: document.querySelector('.f-menu-toggle'),
};
/**
* Get current option values from session storage
* @return {Object}
*/
fabricator.getOptions = () => {
return (fabricator.test.sessionStorage) ? JSON.parse(sessionStorage.fabricator) : fabricator.options;
};
/**
* Build color chips
*/
fabricator.buildColorChips = () => {
const chips = document.querySelectorAll('.f-color-chip');
for (let i = chips.length - 1; i >= 0; i--) {
const color = chips[i].querySelector('.f-color-chip__color').innerHTML;
chips[i].style.borderTopColor = color;
chips[i].style.borderBottomColor = color;
}
return fabricator;
};
/**
* Add `f-active` class to active menu item
*/
fabricator.setActiveItem = () => {
/**
* Match the window location with the menu item, set menu item as active
*/
const setActive = () => {
// get current file and hash without first slash
const loc = (window.location.pathname + window.location.hash);
const current = loc.replace(/(^\/)([^#]+)?(#[\w\-\.]+)?$/ig, (match, slash, file, hash) => {
return (file || '') + (hash || '').split('.')[0];
}) || 'index.html';
// find the current section in the items array
for (let i = fabricator.dom.menuItems.length - 1; i >= 0; i--) {
const item = fabricator.dom.menuItems[i];
// get item href without first slash
const href = item.getAttribute('href').replace(/^\//g, '');
if (href === current) {
item.classList.add('f-active');
} else {
item.classList.remove('f-active');
}
}
};
window.addEventListener('hashchange', setActive);
setActive();
return fabricator;
};
/**
* Click handler to primary menu toggle
* @return {Object} fabricator
*/
fabricator.menuToggle = () => {
// shortcut menu DOM
const toggle = fabricator.dom.menuToggle;
const options = fabricator.getOptions();
// toggle classes on certain elements
const toggleClasses = () => {
options.menu = !fabricator.dom.root.classList.contains('f-menu-active');
fabricator.dom.root.classList.toggle('f-menu-active');
if (fabricator.test.sessionStorage) {
sessionStorage.setItem('fabricator', JSON.stringify(options));
}
};
// toggle classes on ctrl + m press
document.onkeydown = (e) => {
if (e.ctrlKey && e.keyCode === 'M'.charCodeAt(0)) {
toggleClasses();
}
};
// toggle classes on click
toggle.addEventListener('click', () => {
toggleClasses();
});
// close menu when clicking on item (for collapsed menu view)
const closeMenu = () => {
if (!window.matchMedia(fabricator.options.mq).matches) {
toggleClasses();
}
};
for (let i = 0; i < fabricator.dom.menuItems.length; i++) {
fabricator.dom.menuItems[i].addEventListener('click', closeMenu);
}
return fabricator;
};
/**
* Handler for preview and code toggles
* @return {Object} fabricator
*/
fabricator.allItemsToggles = () => {
const itemCache = {
labels: document.querySelectorAll('[data-f-toggle="labels"]'),
notes: document.querySelectorAll('[data-f-toggle="notes"]'),
code: document.querySelectorAll('[data-f-toggle="code"]'),
};
const toggleAllControls = document.querySelectorAll('.f-controls [data-f-toggle-control]');
const options = fabricator.getOptions();
// toggle all
const toggleAllItems = (type, value) => {
const button = document.querySelector(`.f-controls [data-f-toggle-control=${type}]`);
const items = itemCache[type];
for (let i = 0; i < items.length; i++) {
if (value) {
items[i].classList.remove('f-item-hidden');
} else {
items[i].classList.add('f-item-hidden');
}
}
// toggle styles
if (value) {
button.classList.add('f-active');
} else {
button.classList.remove('f-active');
}
// update options
options.toggles[type] = value;
if (fabricator.test.sessionStorage) {
sessionStorage.setItem('fabricator', JSON.stringify(options));
}
};
for (let i = 0; i < toggleAllControls.length; i++) {
toggleAllControls[i].addEventListener('click', (e) => {
// extract info from target node
const type = e.currentTarget.getAttribute('data-f-toggle-control');
const value = e.currentTarget.className.indexOf('f-active') < 0;
// toggle the items
toggleAllItems(type, value);
});
}
// persist toggle options from page to page
Object.keys(options.toggles).forEach((key) => {
toggleAllItems(key, options.toggles[key]);
});
return fabricator;
};
/**
* Handler for single item code toggling
*/
fabricator.singleItemToggle = () => {
const itemToggleSingle = document.querySelectorAll('.f-item-group [data-f-toggle-control]');
// toggle single
const toggleSingleItemCode = (e) => {
const group = e.currentTarget.parentNode.parentNode.parentNode;
const type = e.currentTarget.getAttribute('data-f-toggle-control');
group.querySelector(`[data-f-toggle=${type}]`).classList.toggle('f-item-hidden');
};
for (let i = 0; i < itemToggleSingle.length; i++) {
itemToggleSingle[i].addEventListener('click', toggleSingleItemCode);
}
return fabricator;
};
/**
* Automatically select code when code block is clicked
*/
fabricator.bindCodeAutoSelect = () => {
const codeBlocks = document.querySelectorAll('.f-item-code');
const select = (block) => {
const selection = window.getSelection();
const range = document.createRange();
range.selectNodeContents(block.querySelector('code'));
selection.removeAllRanges();
selection.addRange(range);
};
for (let i = codeBlocks.length - 1; i >= 0; i--) {
codeBlocks[i].addEventListener('click', select.bind(this, codeBlocks[i]));
}
};
/**
* Open/Close menu based on session var.
* Also attach a media query listener to close the menu when resizing to smaller screen.
*/
fabricator.setInitialMenuState = () => {
// root element
const root = document.querySelector('html');
const mq = window.matchMedia(fabricator.options.mq);
// if small screen
const mediaChangeHandler = (list) => {
if (!list.matches) {
root.classList.remove('f-menu-active');
} else {
if (fabricator.getOptions().menu) {
root.classList.add('f-menu-active');
} else {
root.classList.remove('f-menu-active');
}
}
};
mq.addListener(mediaChangeHandler);
mediaChangeHandler(mq);
return fabricator;
};
/**
* Initialization
*/
fabricator
.setInitialMenuState()
.menuToggle()
.allItemsToggles()
.singleItemToggle()
.buildColorChips()
.setActiveItem()
.bindCodeAutoSelect();