UNPKG

@thisismanta/react-playbook

Version:

**Playbook** is a UI library made for listing React components.

230 lines (229 loc) 13.5 kB
"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;