Skip to content

Commit

Permalink
docs: playgrounds performance improvements (#1254)
Browse files Browse the repository at this point in the history
* docs: playgrounds performance improvements

* docs: playground styles

Co-Authored-By: Steven Spriggs <[email protected]>

---------

Co-authored-by: Steven Spriggs <[email protected]>
  • Loading branch information
bennypowers and zeroedin authored Oct 11, 2023
1 parent ed6ff92 commit 1275572
Show file tree
Hide file tree
Showing 9 changed files with 15,471 additions and 198 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ docs/pfe.min.js
docs/bundle.js
docs/core
docs/components
docs/assets/playgrounds
node_modules

core/pfe-sass/docs/index.html
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# Dependencies
node_modules
.wireit
.rollup.cache

# Ignore compiled files in webroot
_site
docs/pfe.min*
docs/assets/playgrounds/

# Build artifacts
elements/*/*.js
Expand Down
52 changes: 42 additions & 10 deletions docs/_data/playgrounds.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,26 @@ function demoPaths(content, pathname) {
}
}

function isModuleScript(node) {
return (
node.tagName === 'script' &&
node.attrs.some(x => x.name === 'type' && x.value === 'module') &&
node.attrs.some(x => x.name === 'src')
);
}

function isStyleLink(node) {
return (
node.tagName === 'link' &&
node.attrs.some(x => x.name === 'rel' && x.value === 'stylesheet') &&
node.attrs.some(x => x.name === 'href')
);
}

module.exports = async function(data) {
performance.mark('playgrounds-start');
const { parseHTML } = await import('linkedom');
const { parseFragment, serialize } = await import('parse5');
const Tools = await import('@parse5/tools');

const demoManifests = groupBy('primaryElementName', data.demos);

Expand All @@ -74,13 +91,22 @@ module.exports = async function(data) {

const demoSource = await fs.readFile(demo.filePath, 'utf8');

const { document } = parseHTML(demoSource);
const fragment = parseFragment(demoSource);

const baseCssPathPrefix = demo.filePath.match(DEMO_FILEPATH_IS_MAIN_DEMO_RE) ? '' : '../';
const baseCssLink = document.createElement('link');
baseCssLink.rel = 'stylesheet';
baseCssLink.href = `${baseCssPathPrefix}rhds-demo-base.css`;
document.head.append(baseCssLink);

Tools.spliceChildren(
fragment,
Infinity,
0,
Tools.createCommentNode('playground-fold'),
Tools.createElement('link', {
rel: 'stylesheet',
href: `${baseCssPathPrefix}rhds-demo-base.css`,
}),
Tools.createTextNode('\n\n'),
Tools.createCommentNode('playground-fold-end'),
);

const filename = getDemoFilename(demo);

Expand All @@ -99,14 +125,20 @@ module.exports = async function(data) {
fileMap.set(filename, {
contentType: 'text/html',
selected: isMainDemo,
content: demoPaths(document.toString(), demo.filePath),
content: demoPaths(serialize(fragment), demo.filePath),
label: demo.title,
});

const modulesAndLinks = Tools.queryAll(fragment, node =>
Tools.isElementNode(node) &&
isModuleScript(node) ||
isStyleLink(node));

// register demo script and css resources
for (const el of document.querySelectorAll('script[type=module][src], link[rel=stylesheet][href]')) {
const isLink = el.localName === 'link';
const subresourceURL = isLink ? el.href : el.src;
for (const el of modulesAndLinks) {
const isLink = el.tagName === 'link';
const attrs = Object.fromEntries(el.attrs.map(({ name, value }) => [name, value]));
const subresourceURL = isLink ? attrs.href : attrs.src;
if (!subresourceURL.startsWith('http')) {
const subresourceFileURL = !subresourceURL.startsWith('/')
// non-tabular tern
Expand Down
15 changes: 15 additions & 0 deletions docs/_plugins/shortcodes/playground.cjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const { readFile } = require('node:fs/promises');
const { join } = require('node:path');

/** @typedef {import('@patternfly/pfe-tools/11ty/DocsPage').DocsPage} DocsPage */

Expand Down Expand Up @@ -40,4 +41,18 @@ ${content}

module.exports = function(eleventyConfig) {
eleventyConfig.addPairedShortcode('playground', playground);
eleventyConfig.on('eleventy.before', async function() {
const { rollup } = await import('rollup');
const { importMetaAssets } = await import('@web/rollup-plugin-import-meta-assets');
const { nodeResolve } = await import('@rollup/plugin-node-resolve');
const outdir = join(__dirname, `../../assets/playgrounds/`);
const bundle = await rollup({
input: join(__dirname, 'rh-playground.js'),
plugins: [
nodeResolve(),
importMetaAssets(),
],
});
await bundle.write({ dir: outdir });
});
};
146 changes: 146 additions & 0 deletions docs/_plugins/shortcodes/rh-playground.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { LitElement, html, css } from 'lit';
import { classMap } from 'lit/directives/class-map.js';

import 'playground-elements';
import '@rhds/elements/rh-button/rh-button.js';
import '@rhds/elements/rh-spinner/rh-spinner.js';

class RhPlayground extends LitElement {
static styles = css`
:host {
position: relative;
display: block;
--_max-height: 785px;
}
::slotted(pre) {
max-height: var(--_max-height);
margin: 0 !important;
}
[hidden],
div.showing {
display: none !important;
}
div {
max-height: var(--_max-height);
overflow-y: scroll;
}
rh-button {
position: absolute;
inset-block-end: 5px;
inset-inline-end: 5px;
display: block;
}
rh-spinner {
opacity: 0;
transition: opacity 0.5s ease;
position: absolute;
inset-block-start: 50%;
inset-inline-start: 50%;
transform: translateY(-50%) translateX(-50%);
}
.loading rh-spinner {
opacity: 1;
}
.loading ::slotted(pre) {
opacity: .3;
}
playground-project {
display: block;
border: var(--rh-border-width-md, 2px) solid var(--rh-color-border-subtle-on-light, #c7c7c7);
border-radius: var(--rh-border-radius-default, 3px);
overflow: hidden;
}
playground-preview {
resize: vertical;
overflow: hidden;
}
`;

static properties = {
loading: { type: Boolean, state: true },
showing: { type: Boolean, state: true },
tagName: { attribute: 'tag-name' },
};

constructor() {
super();
/** Is the demo code loading? */
this.loading = false;
/** Is the demo displayed? */
this.showing = false;
this.project; // ?: PlaygroundProject | null;
this.tabBar; // ?: PlaygroundTabBar | null;
this.fileEditor; // ?: PlaygroundFileEditor | null;
this.preview; // ?: PlaygroundPreview | null;
}

render() {
const { showing, loading } = this;
return html`
<div id="snippet" class="${classMap({ showing, loading })}">
<slot></slot>
<rh-spinner>Loading demo...</rh-spinner>
</div>
<rh-button ?hidden="${showing}" @click="${this.load}">Load Demo</rh-button>
<playground-project ?hidden="${!showing}">
<playground-tab-bar @click="${this.onChange}"></playground-tab-bar>
<playground-file-editor @click="${this.onChange}" @keydown="${this.onChange}"></playground-file-editor>
<playground-preview></playground-preview>
</playground-project>
`;
}

firstUpdated() {
this.project = this.shadowRoot?.querySelector('playground-project');
this.tabBar = this.shadowRoot?.querySelector('playground-tab-bar');
this.fileEditor = this.shadowRoot?.querySelector('playground-file-editor');
this.preview = this.shadowRoot?.querySelector('playground-preview');
if (this.project && this.tabBar && this.fileEditor && this.preview) {
this.tabBar.project = this.project;
this.fileEditor.project = this.project;
this.preview.project = this.project;
}
}

onChange(event) {
if (event.target === this.tabBar) {
// @ts-expect-error: need a better way to handle this, but works for now
this.switch((event.target)._activeFileName);
} else {
this.switch((event.target).filename);
}
}

switch(filename) {
if (filename && this.preview && this.fileEditor) {
this.preview.htmlFile = filename;
this.fileEditor.filename = filename;
}
}

async load() {
this.loading = true;
this.switch('demo/index.html');
const { configure } = await import(`/assets/playgrounds/${this.tagName}-playground.js`);
configure(this.project);
await import('playground-elements');
this.show();
}

show() {
this.loading = false;
this.showing = true;
}
}

customElements.define('rh-playground', RhPlayground);
Loading

0 comments on commit 1275572

Please sign in to comment.