@thisismanta/react-playbook
Version:
**Playbook** is a UI library made for listing React components.
230 lines (229 loc) • 13.5 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Index = Index;
exports.MenuItem = MenuItem;
const react_1 = __importStar(require("react"));
const lodash_1 = require("lodash");
const FuzzySearch_1 = __importDefault(require("./FuzzySearch"));
const classnames_1 = __importDefault(require("classnames"));
const ErrorBoundary_1 = __importDefault(require("./ErrorBoundary"));
require("./Playbook.css");
const previewPageName = getQueryParams().r;
if (!previewPageName) {
document.body.classList.add('playbook__theater');
}
else {
if (!document.title) {
document.title = previewPageName;
}
if (window.parent !== window.self) {
document.body.classList.add('playbook__preview');
}
}
const darkMode = window.localStorage.getItem('playbook__dark-mode') === 'true';
if (darkMode) {
document.body.classList.add('playbook__dark-mode');
}
const Playbook = function Playbook(props) {
if (previewPageName) {
const currentPage = props.pages.find(page => page.name === previewPageName);
if (!currentPage || typeof currentPage.content !== 'function') {
return null;
}
const Content = currentPage.content;
const ContentWrapper = props.contentWrapper || PassThroughContentWrapper;
return (react_1.default.createElement(ErrorBoundary_1.default, null,
react_1.default.createElement(ContentWrapper, { currentPage: currentPage },
react_1.default.createElement(react_1.default.Suspense, null,
react_1.default.createElement(Content, null)))));
}
return (react_1.default.createElement(ErrorBoundary_1.default, null,
react_1.default.createElement(Index, { ...props, pages: props.pages })));
};
Playbook.Button = Button;
function Index(props) {
const getSelectPage = (0, react_1.useCallback)(() => props.pages.find(page => page.name === getQueryParams()['p']), [props.pages]);
const getSearchText = (0, react_1.useCallback)(() => getQueryParams()['q'] || '', []);
const [selectPage, setSelectPage] = (0, react_1.useState)(getSelectPage);
const [searchText, setSearchText] = (0, react_1.useState)(getSearchText);
(0, react_1.useEffect)(() => {
const firstPage = props.pages[0];
if (selectPage === undefined && firstPage) {
setQueryParams({ p: firstPage.name }, true);
setSelectPage(firstPage);
}
}, [props.pages]);
(0, react_1.useEffect)(() => {
window.addEventListener('popstate', () => {
setSelectPage(getSelectPage());
setSearchText(getSearchText());
});
}, []);
(0, react_1.useEffect)(() => {
if (selectPage) {
document.title = selectPage.name;
}
}, [selectPage]);
(0, react_1.useEffect)(() => {
setQueryParams({ q: searchText }, true);
}, [searchText]);
(0, react_1.useLayoutEffect)(() => {
const selectedMenuItem = document.querySelector('.playbook__menu__item.--select');
if (selectedMenuItem && 'scrollIntoView' in selectedMenuItem) {
selectedMenuItem.scrollIntoView({ block: 'center' });
}
}, []);
const searcher = (0, react_1.useMemo)(() => new FuzzySearch_1.default(props.pages, ['name'], { caseSensitive: false, sort: true }), [props.pages]);
const [leftMenuVisible, setLeftMenuVisible] = (0, react_1.useState)(false);
const onMenuItemClick = (0, react_1.useCallback)((pageName) => {
setSelectPage(props.pages.find(page => page.name === pageName));
setQueryParams({ p: pageName }, false);
setLeftMenuVisible(false);
}, [props.pages]);
const menus = (0, react_1.useMemo)(() => {
const results = searcher.search(searchText);
if (results.length === 0) {
return (react_1.default.createElement("div", { className: 'playbook__no-results' },
react_1.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", height: "20px", viewBox: "0 0 24 24", width: "20px" },
react_1.default.createElement("g", null,
react_1.default.createElement("path", { d: "M0,0h24v24H0V0z", fill: "none" })),
react_1.default.createElement("g", null,
react_1.default.createElement("path", { d: "M2.81,2.81L1.39,4.22l2.27,2.27C2.61,8.07,2,9.96,2,12c0,5.52,4.48,10,10,10c2.04,0,3.93-0.61,5.51-1.66l2.27,2.27 l1.41-1.41L2.81,2.81z M12,20c-4.41,0-8-3.59-8-8c0-1.48,0.41-2.86,1.12-4.06l10.94,10.94C14.86,19.59,13.48,20,12,20z M7.94,5.12 L6.49,3.66C8.07,2.61,9.96,2,12,2c5.52,0,10,4.48,10,10c0,2.04-0.61,3.93-1.66,5.51l-1.46-1.46C19.59,14.86,20,13.48,20,12 c0-4.41-3.59-8-8-8C10.52,4,9.14,4.41,7.94,5.12z" })))));
}
return results.map(page => (react_1.default.createElement(MenuItemMemoized, { key: page.name, name: page.name, selected: page.name === selectPage?.name, onClick: onMenuItemClick })));
}, [searcher, searchText, selectPage]);
const selectLink = selectPage ? window.location.pathname + '?r=' + window.encodeURIComponent(selectPage.name) : null;
return (react_1.default.createElement("div", { className: 'playbook' },
react_1.default.createElement("link", { rel: "preconnect", href: "https://fonts.googleapis.com" }),
react_1.default.createElement("link", { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "" }),
react_1.default.createElement("link", { href: "https://fonts.googleapis.com/css2?family=Roboto+Mono&family=Roboto:wght@300;400;500&display=swap", rel: "stylesheet" }),
react_1.default.createElement("div", { className: (0, classnames_1.default)('playbook__left', leftMenuVisible && 'playbook__left-responsive'), tabIndex: -1, onKeyUp: e => {
if (e.key === 'Escape') {
setLeftMenuVisible(false);
}
} },
react_1.default.createElement("div", { className: 'playbook__search-box' },
react_1.default.createElement("svg", { className: 'playbook__search-icon', xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24" },
react_1.default.createElement("path", { d: "M0 0h24v24H0z", fill: "none" }),
react_1.default.createElement("path", { d: "M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" })),
react_1.default.createElement("input", { type: 'text', value: searchText, placeholder: 'Search', onChange: e => {
setSearchText(e.target.value);
}, onKeyUp: e => {
if (e.key === 'Escape' && searchText !== '') {
setSearchText('');
e.stopPropagation();
}
} })),
react_1.default.createElement("div", { className: 'playbook__menu', onScroll: (e) => {
if (e.currentTarget.scrollTop === 0) {
window.sessionStorage.removeItem('playbook__menu-scroll-top');
}
else {
window.sessionStorage.setItem('playbook__menu-scroll-top', String(e.currentTarget.scrollTop));
}
} }, menus)),
react_1.default.createElement("div", { className: 'playbook__right' },
react_1.default.createElement("div", { className: 'playbook__toolbar' },
react_1.default.createElement(Button, { id: 'playbook__side-menu-toggle', title: 'Open navigation menu', onClick: () => { setLeftMenuVisible(value => !value); } },
react_1.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24" },
react_1.default.createElement("path", { d: "M0 0h24v24H0z", fill: "none" }),
react_1.default.createElement("path", { d: "M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z" }))),
typeof props.contentControl === 'function'
? react_1.default.createElement(props.contentControl, { currentPage: selectPage })
: null,
react_1.default.createElement("div", { style: { flex: '1 1 auto' } }),
react_1.default.createElement(Button, { title: 'Toggle dark mode', active: darkMode, onClick: () => {
if (darkMode) {
window.localStorage.removeItem('playbook__dark-mode');
}
else {
window.localStorage.setItem('playbook__dark-mode', 'true');
}
window.location.reload();
} },
react_1.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", height: "24px", viewBox: "0 0 24 24", width: "24px" },
react_1.default.createElement("rect", { fill: "none", height: "24", width: "24" }),
react_1.default.createElement("path", { d: "M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36c-0.98,1.37-2.58,2.26-4.4,2.26 c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z" }))),
selectLink && (react_1.default.createElement(Button, { title: 'Open in a new tab', onClick: () => { window.open(selectLink, '_blank'); } },
react_1.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", height: "24px", viewBox: "0 0 24 24", width: "24px" },
react_1.default.createElement("rect", { fill: "none", height: "24", width: "24" }),
react_1.default.createElement("polygon", { points: "21,11 21,3 13,3 16.29,6.29 6.29,16.29 3,13 3,21 11,21 7.71,17.71 17.71,7.71" }))))),
selectLink && (react_1.default.createElement("iframe", { className: 'playbook__content-container', "data-playbook-content": true, src: selectLink, width: '100%', frameBorder: '0' })))));
}
const MenuItemMemoized = react_1.default.memo(MenuItem);
function MenuItem(props) {
const chunks = props.name.split(/\\|\//);
const directories = chunks.slice(0, -1);
const lastName = chunks.at(-1);
return (react_1.default.createElement("a", { className: (0, classnames_1.default)('playbook__menu__item', props.selected && '--select'), href: '?p=' + window.encodeURI(props.name), onClick: e => {
e.preventDefault();
props.onClick(props.name);
} },
directories.map((name, rank) => (react_1.default.createElement(react_1.default.Fragment, { key: rank },
name + '/',
react_1.default.createElement("wbr", null)))),
react_1.default.createElement("span", { className: 'playbook__menu__item__last' }, lastName)));
}
function PassThroughContentWrapper(props) {
return props.children;
}
function Button({ active, ...props }) {
return (react_1.default.createElement("button", { ...props, className: (0, classnames_1.default)('playbook__button', active && 'playbook__button__active', props.className) }));
}
function getQueryParams() {
return Object.fromEntries((0, lodash_1.compact)(window.location.search.replace(/^\?/, '')
.split('&')).map(part => {
const [key, value] = part.split('=');
return [key, window.decodeURIComponent(value) ?? ''];
}));
}
function setQueryParams(params, replace) {
const link = '?' +
Object.entries({ ...getQueryParams(), ...params })
.filter((pair) => !!pair[1])
.map(([key, value]) => key + '=' + window.encodeURIComponent(value))
.join('&');
if (replace) {
window.history.replaceState(null, '', link);
}
else {
window.history.pushState(null, '', link);
}
}
exports.default = Playbook;