Skip to content

Instantly share code, notes, and snippets.

@zmitry
Created April 20, 2021 21:09
Show Gist options
  • Select an option

  • Save zmitry/673a2d8bddaced99c825d4174e0d82b3 to your computer and use it in GitHub Desktop.

Select an option

Save zmitry/673a2d8bddaced99c825d4174e0d82b3 to your computer and use it in GitHub Desktop.
import React, { useRef, useMemo, Suspense } from "react";
import { last } from "lodash";
import { stringify } from "./url";
import { Trie, dfs } from "./trie";
function stringifyQueryHash(hash) {
return "?" + stringify(hash);
}
function isUpperCase(str) {
return str[0] === str[0].toUpperCase();
}
function StoryCase({ componentName, component, config, params }) {
const getFullscreenLink = async (addProps) => {
return (
window.location.origin +
stringifyQueryHash({
...params,
fullscreen: true,
story: params.story,
})
);
};
return (
<div
style={{
height: "100%",
}}
>
<div
className="card-title hstack"
style={
{
"--space": "0.5ch",
} as any
}
>
<a
title="fullscreen"
className="size1 icon-button"
onClick={async (e) => {
e.preventDefault();
const url = await getFullscreenLink(false);
window.open(url);
}}
target="_blank"
>
<div>{componentName} ⧉</div>
</a>
</div>
{config.decorator(component)}
</div>
);
}
function IframeControllerWrapper({ children }) {
return (
<div className="stack">
<Suspense
fallback={<div style={{ margin: "auto" }} className="storyui-loader" />}
>
{children}
</Suspense>
</div>
);
}
//#endregion
//#region controller app
const SidePanel = React.memo(function SidePanel({
stories,
story,
navigate,
}: any) {
let res = [];
dfs(stories, (node, depth, parentKey) => {
// do not render top level folder which is ".""
if (!parentKey) {
return;
}
let key = node.key;
const label = key.replace(parentKey, "").replace("/", "");
let className = story === key ? "selected" : "";
if (node.end) {
className += " side-label-item";
} else {
className += " side-label-group";
}
res.push(
<a
style={{
marginLeft: depth - 1 + "ch",
}}
title={label}
className={className}
key={key + "group"}
onClick={(e) => {
e.preventDefault();
navigate({ story: key }, { preserveParams: false });
return false;
}}
>
{label}
</a>
);
});
return (
<div style={{ overflow: "auto" }}>
<nav className="side stack" style={{ "--space": "2px" } as any}>
{res}
</nav>
</div>
);
});
function AppIframe({ story, ...params }) {
return useMemo(() => {
return (
<object
aria-label="story"
key={story}
className="embed"
data={stringifyQueryHash({ story, ...params, iframe: true })}
/>
);
}, [story]);
}
//#endregion
export function isStory(key) {
return isUpperCase(key);
}
export function LightApp({ stories, params, navigate }) {
const { story, iframe } = params;
const root = new Trie(stories);
if (iframe) {
let items = root.find(story || "")?.map((el) => [el.key, el.value]);
return (
<IframeControllerWrapper>
{items.map(([key, C]: any) => {
return (
<StoryCase
params={params}
key={key}
config={C.config}
componentName={last(key.split("/"))}
component={C}
/>
);
})}
</IframeControllerWrapper>
);
}
return (
<div className="storyRoot">
<SidePanel stories={stories} {...params} navigate={navigate} />
<AppIframe {...params} />
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment