@hypothesis/frontend-shared
Version:
Shared components, styles and utilities for Hypothesis projects
406 lines (384 loc) • 11.2 kB
JavaScript
var _jsxFileName = "/home/runner/work/frontend-shared/frontend-shared/src/pattern-library/components/Library.js";
import classnames from 'classnames';
import { toChildArray } from 'preact';
import { useState } from 'preact/hooks';
import { Frame } from '../../components/containers';
import { jsxToHTML } from '../util/jsx-to-string';
/**
* @typedef LibraryBaseProps
* @prop {import("preact").ComponentChildren} [intro] - Optional
* introductory content
* @prop {import("preact").ComponentChildren} [children]
* @prop {string} [title]
*
*/
/**
* Components for rendering patterns, examples and demos in the pattern-library
* page. A pattern-library Page contains Patterns, which in turn contain
* Examples. An Example _may_ contain one or more Demos. Child content (markup)
* may also be rendered in these components, as desired.
*
* Example of structure:
*
* <Library.Page intro={<p>Some introductory content</p>} title="Elephants">
* <p>Any content you want on the page.</p>
* More content: it can be any valid `ComponentChildren`
*
* <Library.Pattern title="Elephant">
* <p>The `Elephant` component is used to render information about elephant
* personalities.</p>
* <Library.Example title="Colored elephants">
* <p>You can change the color of your elephant.</p>
* <Library.Demo withSource>
* <Elephant color="pink" />
* </Library.Demo>
* </Library.Example>
* // More Examples if desired
* </Library.Pattern>
*
* // more Patterns if desired...
* </Library.Page>
*/
/**
* Render a pattern-library page.
*
* @param {LibraryBaseProps} props
*/
import { jsxDEV as _jsxDEV } from "preact/jsx-dev-runtime";
import { Fragment as _Fragment } from "preact/jsx-dev-runtime";
function Page({
children,
intro,
title
}) {
return _jsxDEV("section", {
className: "max-w-6xl pb-16 space-y-8 text-slate-7",
children: [_jsxDEV(PageTitle, {
title: title
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 54,
columnNumber: 7
}, this), intro && _jsxDEV(PageIntro, {
children: intro
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 55,
columnNumber: 17
}, this), _jsxDEV("div", {
className: "px-4 space-y-16 styled-text",
children: children
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 56,
columnNumber: 7
}, this)]
}, void 0, true, {
fileName: _jsxFileName,
lineNumber: 53,
columnNumber: 5
}, this);
}
/**
* Sticky pattern-library page header
*/
function PageTitle({
title
}) {
return _jsxDEV("div", {
className: "sticky top-0 z-4 h-16 flex items-center bg-slate-0 border-b",
children: _jsxDEV("h1", {
className: "px-4 text-4xl font-light",
children: title
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 67,
columnNumber: 7
}, this)
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 66,
columnNumber: 5
}, this);
}
/**
* Page introductory text
*/
function PageIntro({
children
}) {
return _jsxDEV("div", {
className: "styled-text px-4 text-xl font-light space-y-4 leading-relaxed",
children: children
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 77,
columnNumber: 5
}, this);
}
/**
* Render info about a single pattern (or component) on a pattern-library page.
*
* @param {LibraryBaseProps} props
*/
function Pattern({
children,
title
}) {
return _jsxDEV("section", {
className: "space-y-8",
children: [_jsxDEV("h2", {
className: "text-2xl text-slate-7",
children: title
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 91,
columnNumber: 7
}, this), _jsxDEV("div", {
className: "space-y-8 px-4",
children: children
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 92,
columnNumber: 7
}, this)]
}, void 0, true, {
fileName: _jsxFileName,
lineNumber: 90,
columnNumber: 5
}, this);
}
/**
* @typedef LibraryExampleProps
* @prop {import("preact").ComponentChildren} [children]
* @prop {string} [title]
* @prop {'split'|'wide'} [variant='split'] - Layout variant. Applies
* appropriate className.
* - Split (default) lays out in a row. Non-demo example content is rendered
* left, with demos right. Demos in this variant stack vertically.
* - Wide lays out in a full-width column. Non-example is rendered first,
* then a row to contain demos. Demos in this variant render next to each
* other in a single row.
*/
/**
* Render example content and optional Demo(s) for a pattern.
*
* @param {LibraryExampleProps} props
*/
function Example({
children,
title,
variant = 'split'
}) {
const kids = toChildArray(children); // Extract Demo components out of any children
const demos = kids.filter(kid => typeof kid === 'object' && (kid === null || kid === void 0 ? void 0 : kid.type) === Demo); // And everything else that is not a demo...
const notDemos = kids.filter(kid => !demos.includes(kid));
return _jsxDEV("div", {
className: "space-y-6",
children: [title && _jsxDEV("h3", {
className: "text-xl text-slate-9 font-light",
children: title
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 127,
columnNumber: 17
}, this), _jsxDEV("div", {
className: "space-y-6 px-4",
children: notDemos
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 129,
columnNumber: 7
}, this), _jsxDEV("div", {
className: classnames({
'space-y-16 px-4': variant === 'split',
'flex flex-row gap-16 flex-wrap': variant === 'wide'
}),
children: demos
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 130,
columnNumber: 7
}, this)]
}, void 0, true, {
fileName: _jsxFileName,
lineNumber: 126,
columnNumber: 5
}, this);
}
/**
* @typedef DemoButtonProps
* @prop {import("preact").ComponentChildren} [children]
* @prop {() => void} [onClick]
* @prop {boolean} pressed
*/
/**
*
* @param {DemoButtonProps} props
*/
function DemoButton({
children,
onClick,
pressed
}) {
return _jsxDEV("button", {
className: classnames('flex items-center gap-x-1.5 rounded-sm shadow py-1 px-2', 'text-sm text-color-text-light hover:bg-grey-0 hover:text-grey-7 hover:shadow-md', {
'bg-grey-2': pressed
}),
onClick: onClick,
"aria-pressed": pressed,
children: children
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 155,
columnNumber: 5
}, this);
}
/**
* @typedef DemoProps
* @prop {import("preact").ComponentChildren} [children]
* @prop {string} [classes] - Extra CSS classes for the demo content's immediate
* parent container
* @prop {boolean} [withSource=false] - Should the demo also render the source?
* When true, a "Source" tab will be rendered, which will display the JSX
* source of the Demo's children
* @prop {object} [style] - Inline styles to apply to the demo container
* @prop {string} [title]
*/
/**
* Render a "Demo", with optional source. This will render the children as
* provided in a tabbed container. If `withSource` is `true`, the JSX source
* of the children will be provided in a separate "Source" tab from the
* rendered Demo content.
*
* @param {DemoProps} props
*/
function Demo({
children,
classes,
withSource = false,
style = {},
title
}) {
const [visibleTab, setVisibleTab] = useState('demo');
const source = toChildArray(children).map((child, idx) => {
return _jsxDEV("li", {
children: _jsxDEV("code", {
children: _jsxDEV("pre", {
className: "font-pre whitespace-pre-wrap break-words text-sm",
dangerouslySetInnerHTML: {
__html: jsxToHTML(child)
}
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 197,
columnNumber: 11
}, this)
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 196,
columnNumber: 9
}, this)
}, idx, false, {
fileName: _jsxFileName,
lineNumber: 195,
columnNumber: 7
}, this);
});
return _jsxDEV("div", {
className: "space-y-2 p-4",
children: [_jsxDEV("div", {
className: "flex items-center",
children: [_jsxDEV("div", {
className: "py-2 grow",
children: _jsxDEV("h4", {
className: "text-lg italic text-slate-7 font-light",
children: title
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 209,
columnNumber: 11
}, this)
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 208,
columnNumber: 9
}, this), _jsxDEV("div", {
className: "flex flex-row items-center justify-end gap-x-4",
children: withSource && _jsxDEV(_Fragment, {
children: [_jsxDEV(DemoButton, {
onClick: () => setVisibleTab('demo'),
pressed: visibleTab === 'demo',
children: "Demo"
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 214,
columnNumber: 15
}, this), _jsxDEV(DemoButton, {
onClick: () => setVisibleTab('source'),
pressed: visibleTab === 'source',
children: "Source"
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 220,
columnNumber: 15
}, this)]
}, void 0, true)
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 211,
columnNumber: 9
}, this)]
}, void 0, true, {
fileName: _jsxFileName,
lineNumber: 207,
columnNumber: 7
}, this), _jsxDEV("div", {
className: "bg-slate-0 p-2 rounded-md unstyled-text",
children: [visibleTab === 'demo' && _jsxDEV("div", {
className: "w-full bg-white p-8 rounded-md",
style: style,
children: _jsxDEV("div", {
className: classnames('h-full flex flex-row items-center justify-center gap-2', classes),
children: children
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 233,
columnNumber: 13
}, this)
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 232,
columnNumber: 11
}, this), visibleTab === 'source' && _jsxDEV(Frame, {
classes: "w-full rounded-md bg-slate-7 text-color-text-inverted p-4",
children: _jsxDEV("ul", {
children: source
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 245,
columnNumber: 13
}, this)
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 244,
columnNumber: 11
}, this)]
}, void 0, true, {
fileName: _jsxFileName,
lineNumber: 230,
columnNumber: 7
}, this)]
}, void 0, true, {
fileName: _jsxFileName,
lineNumber: 206,
columnNumber: 5
}, this);
}
export default {
Page,
Pattern,
Example,
Demo
};
//# sourceMappingURL=Library.js.map