Open thescientist13 opened 11 months ago
@jstockdi Had some time to play around with some of the ideas here and have a demo you can checkout on the new Greenwood website, demonstrating rich frontmatter and using it in markdown / HTML to pass data to Web Components.
So taking your playlist example
---
title: Playlist
name: "My Cool Playlist"
songs:
- title: You Can Close Your Eyes
url: http://archive.org/download/james-taylor-montreux-jazz-fest-1988-rsr/01 You Can Close Your Eyes.mp3
- title: Country Road
url: http://archive.org/download/james-taylor-montreux-jazz-fest-1988-rsr/09 Country Road.mp3
---
<!doctype html>
<html lang="en" prefix="og:http://ogp.me/ns#">
<head>
<title>Playlist</title>
<script type="module" src="../components/player.js"></script>
</head>
<body>
<app-header></app-header>
<h1>Welcome to the Player!</h1>
<app-player name="${globalThis.page.name}" playlist='${globalThis.page.songs}'></app-player>
<app-footer></app-footer>
</body>
</html>
And here's the Player
component
export default class Player extends HTMLElement {
constructor() {
super();
this.title = '';
this.playlist = [];
}
async connectedCallback() {
this.playlist = JSON.parse(this.getAttribute("playlist") || "[]");
this.title = this.getAttribute("name") || "";
if (!this.shadowRoot) {
const list = this.playlist.map((item) => `<li>${item.title}</li>`).join("\n");
const template = document.createElement('template');
template.innerHTML = `
<div style="text-align: left; width: 30%; margin:0 auto;">
<h1>${this.title}</h1>
<ul>
${list}
</ul>
<button>Start Playlist</button>
</div>
`;
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
this.shadowRoot.querySelector('button')?.addEventListener('click', this.startPlaylist.bind(this));
}
startPlaylist() {
const { title, playlist } = this;
console.log('starting playlist =>', { title, playlist });
alert(`starting playlist => ${title}`);
}
}
You can pass frontmatter right into your component, almost thinking of it like an ESI
You can see the demo running here https://deploy-preview-49--super-tapioca-5987ce.netlify.app/playlist/
The patches are pretty straightforward, just like you has posed in #1229 if you want to run with those, while I get this into Greenwood. I plan to include this in the next release line (v0.30.0) which is in progress (#1208) and something I hope to have wrapped up in the next couple months, but this will likely be available earlier via an alpha release, so will keep you posted.
<app-player name="${globalThis.page.name}" playlist='${globalThis.page.songs}'></app-player>
I dig the syntax and usage here, the patch makes sense. Thinking broadly...
Rich Frontmatter should include:
Should there be a patch to graph.js? Possibly adding the rich front-matter in...
data: { menu: 'side', index: 7, linkheadings: 0, tableOfContents: [] },
In the playlist example... the graph.json could get pretty large, but in other cases, making menus or supporting tags, it could be quite useful. Currently, tags have to be implemented using CSV and parsing... w/ rich front-matter, I am guessing GQL queries could pre-render then...
Should there be a patch to graph.js? Possibly adding the rich front-matter in...
Yeah, that should be handled automatically by the frontmatter package we're using, which is what is allowing us to JSON.stringify
it into the page atm. By default anything in fronmatter that isn't one of these "reserved" keys should end up in the generic data
property on a page in the graph, which should include GraphQL as well.
label
title
template
imports
In the playlist example... the graph.json could get pretty large, but in other cases, making menus or supporting tags, it could be quite useful.
Yeah, the one downside to the active frontmatter approach, in particular when combined with collections, is that a lot of that data would get serialized into the page, as seen in my demo
<app-player name="My Cool Playlist" playlist="[{"title":"You Can Close Your Eyes","url":"http://archive.org/download/james-taylor-montreux-jazz-fest-1988-rsr/01 You Can Close Your Eyes.mp3"},{"title":"Country Road","url":"http://archive.org/download/james-taylor-montreux-jazz-fest-1988-rsr/09 Country Road.mp3"}]">
<template shadowrootmode="open">
<div style="text-align: left; width: 30%; margin:0 auto;">
<h1>My Cool Playlist</h1>
<ol>
<li>You Can Close Your Eyes</li>
<li>Country Road</li>
</ol>
<button>Start Playlist</button>
</div>
</template>
</app-player>
Which could definitely add to the HTML payload size a bit for collections, which would be graph data, but hopefully narrowed down significantly since would just be a collection (subset) of pages. And at least it would be just HTML, so would be the least "punishing" to the browser / usage. 😅
I am guessing GQL queries could pre-render then...
Yeah, there is always the ability to do this all in client-side JavaScript either using fetch
against /graph.json or the GraphQL plugin. My hope as part of this issue is to also make a more ergonomic option between raw graph.json and having to use the GraphQL plugin, e.g.
// notice this is not a .gql file, but perhaps the logic could be shared?
import { getChildren } from '@greenwood/cli/src/queries/menu.js';
export default class BlogPostsPage extends HTMLElement {
// we can pass in the graph to component, or figure out a way to get it into `getChildren`
constructor(request, graph) {
super();
this.graph = graph;
}
async connectedCallback() {
const { graph } = this;
const blogPosts = getChildren(graph, { parent: 'blog' });
this.innerHTML = `
${
blogPosts.map((post) => {
return `<h2>${post.title}</h2>
}).join('');
}
`;
}
So that way users will now have multiple options, which I think provide a good set of pros / cons and authoring preferences:
fetch
+ graph.json (already supported)
Type of Change
RFC
Summary
Coming out of #952 (and now while working on the new Greenwood website), wanted to track some specific thoughts and ideas around accessing content as data with Greenwood and making it easier to work in HTML / markdown for happy path cases, and mostly to provide an alternative to using GraphQL as built into Greenwood.
In addition, the "graph", if that's even a good name for it, is showing its age by now and in need of some TLC.
Details
Collections
Although Greenwood has the concept of menus, which is a way to group content in the context of a navigation and whatnot, perhaps a more general purpose name going forward may a "Collection", as inspired by Astro's (Content Collections](https://docs.astro.build/en/guides/content-collections/). Ultimately it should be whatever meaning the user wants to give, be it navigation, a bunch of blog posts, or something else entirely.
Things like:
(I think Menu's may have added extra functionality on top of that like ordering and what not, but maybe that should just be considered a "type" of collection?)
For example, say listing out the table of contents for a blog using constructor props instead of the Children GraphQL query
Frontmatter
We may also want to rename it from
index
toorder
since these aren't arrays, and the starting index thing may be confusing / misleading.HTML
We already have frontmatter support for markdown and JS
But might be good to see if we can extend frontmatter support to HTML as well, assuming it doesn't break all the HTML parsers
Rich Frontmatter
As reported in #1229 , would be good to see how we can support "rich" frontmatter data, and to the use case presenting point, what more could we do with frontmatter in this use case, as it would be really nice to leverage this data somehow combined with a custom element
Rendering Strategies / Contexts
Somewhat related to #951 , one caveat around content as data is what to about CSR use cases, like when authoring HTML. How can you get content as data into a custom element using only HTML as you will be coupled to API calls and have to handle them somehow for production builds, since the plan isn't to support an actual Greenwood GraphQL server for production, and so all data is pre-generated at build time, this making it pretty much read-only.
Graph Structure
There are definitely some breaking changes to be made to the graph, to better align on terminology and overall usefulness to Greenwood and users. So given a current item in the graph
id
andlabel
?path
andfilename
? anything else worth cleaning up?imports
field toresources
in frontmatter configindex
->order
path
->url
(instance of URL or at least thehref
property for serialiazing)outputPath
/id
I wonder if there is a case to rename this to
pages
instead? A graph implies nodes, some sort of linking structure, etc which it is definitely not in its current state.Would be good to go through and evaluate all currently open and related issues