Skip to content

Commit

Permalink
feat: add gallery page
Browse files Browse the repository at this point in the history
  • Loading branch information
BHznJNs committed Jul 5, 2024
1 parent 0547485 commit 315e18e
Show file tree
Hide file tree
Showing 13 changed files with 575 additions and 8 deletions.
14 changes: 14 additions & 0 deletions packages/playground/gallery.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en" class="h-full bg-white">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/assets/kanaries.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Graphic Walker Gallery</title>
<meta name="description" content="An easy to use Exploratory Data Analysis tool">
</head>
<body class="h-full">
<div id="root"></div>
<script type="module" src="/src/gallery/index.tsx"></script>
</body>
</html>
1 change: 1 addition & 0 deletions packages/playground/src/gallery/components/ErrorPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "../../examples/components/errorPage";
151 changes: 151 additions & 0 deletions packages/playground/src/gallery/components/Example.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import React, { useContext, useState, Fragment } from 'react';
import { Link } from 'react-router-dom';
import { Menu, Transition } from '@headlessui/react'
import { ChevronDownIcon } from '@heroicons/react/20/solid'
import { ArrowUturnLeftIcon } from '@heroicons/react/24/outline';
import { IChart, PureRenderer, GraphicWalker } from '@kanaries/graphic-walker';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { a11yDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
import { themeContext } from '../context';
import { GalleryItemOption } from './GalleryGroup';
import { specDict } from '../resources';
import { IDataSource, useFetch } from '../util';

function getResourceURL(type: "datasets" | "specs", name: string): string {
if (type === "specs") {
return specDict[name];
} else if (type === "datasets") {
return `https://pub-2422ed4100b443659f588f2382cfc7b1.r2.dev/datasets/${name}.json`;
} else {
throw new Error(`Unknown fetch type: "${type}".`);
}
}

interface ExampleProps {
title: string,
specName: string,
datasetName: string,
}

function classNames(...classes: string[]) {
return classes.filter(Boolean).join(' ')
}

function Dropdown({ showMode, setShowMode }: {
showMode: 'preview' | 'editor',
setShowMode: (mode: 'preview' | 'editor') => void
}) {
return (
<div className="absolute top-4 right-4 w-56 text-right z-10">
<Menu as="div" className="relative inline-block text-left">
<div>
<Menu.Button className="inline-flex w-full justify-center rounded-md bg-gray-950 px-4 py-2 text-sm font-medium text-white hover:bg-gray-800 focus:outline-none focus-visible:ring-2 focus-visible:ring-white/75">
{showMode === 'preview' ? 'Preview' : 'Editor'}
<ChevronDownIcon className="-mr-1 ml-2 h-5 w-5" aria-hidden="true"/>
</Menu.Button>
</div>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="absolute right-0 mt-2 w-56 origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black/5 focus:outline-none">
<div className="px-1 py-1 ">
<Menu.Item>
{({ active }) => (
<button
onClick={() => setShowMode("preview")}
className={`${active ? 'bg-gray-100 text-gray-900' : 'text-gray-700'
} group flex w-full items-center rounded-md px-2 py-2 text-sm`}
>
Preview
</button>
)}
</Menu.Item>
<Menu.Item>
{({ active }) => (
<button
onClick={() => setShowMode("editor")}
className={`${active ? 'bg-gray-100 text-gray-900' : 'text-gray-700'
} group flex w-full items-center rounded-md px-2 py-2 text-sm`}
>
Editor
</button>
)}
</Menu.Item>
</div>
</Menu.Items>
</Transition>
</Menu>
</div>
)
}

function Example(props: ExampleProps) {
const {
title,
specName,
datasetName,
} = props;
const { theme } = useContext(themeContext);
const [showMode, setShowMode] = useState<'preview' | 'editor'>('preview');
const datasetURL = getResourceURL("datasets", datasetName);
const specsURL = getResourceURL("specs", specName);
const { dataSource, fields } = useFetch<IDataSource>(datasetURL);
const spec = useFetch<IChart[]>(specsURL);
const chart = spec[0] as IChart;

return (
<div>
<div className="flex gap-2 items-center">
<Link to={".."}>
<button type="button" className="flex items-center justify-center rounded-md px-1 py-1 transition hover:bg-zinc-900/5 dark:text-white dark:hover:bg-white/5">
<ArrowUturnLeftIcon className="h-5 w-5" />
</button>
</Link>
<h2 className="text-2xl font-bold tracking-tight text-gray-900">{title}</h2>
</div>

<div className={classNames("relative", showMode === 'editor' ? "mt-6 border rounded-md overflow-hidden" : "")}>
<Dropdown showMode={showMode} setShowMode={setShowMode} />
{showMode === 'preview' && <>
<PureRenderer
className="mt-6 border rounded-md overflow-hidden"
rawData={dataSource}
visualConfig={chart.config}
visualState={chart.encodings}
visualLayout={chart.layout}
appearance={theme}
/>
<h2 className="mt-16 mb-6 text-2xl font-bold tracking-tight text-gray-900">Graphic Walker JSON Specification</h2>
<SyntaxHighlighter language="tsx" style={a11yDark}>
{JSON.stringify(spec, null, 2)}
</SyntaxHighlighter>
</>}

{showMode === "editor" &&
<GraphicWalker fields={fields} data={dataSource} chart={spec} appearance={theme} />
}
</div>
</div>
)
}

export default function ExampleWrapper(props: {
options: GalleryItemOption,
}) {
const { options } = props;
return (
<React.Suspense fallback={<p>Loading component...</p>}>
<Example
title={options.title}
specName={options.id}
datasetName={options.datasetName}
/>
</React.Suspense>
);
}
56 changes: 56 additions & 0 deletions packages/playground/src/gallery/components/GalleryGroup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { useMemo } from 'react';
import { Link, Outlet, useLocation } from 'react-router-dom';
import { imageDict } from '../resources';

interface GalleryItemProps {
id: string,
title: string,
}
export type GalleryItemOption = GalleryItemProps & { datasetName: string };

function GalleryItem(props: GalleryItemProps) {
const { id, title } = props;
const imageURL = imageDict[id];

return (
<Link to={id}>
<div className="divide-y divide-gray-200 h-full overflow-hidden rounded-lg bg-white shadow group">
<div
className="w-full h-36 bg-cover bg-no-repeat overflow-hidden hover:bg-right-bottom"
style={{
backgroundImage: `url(${imageURL})`,
transition: "background-position 2s",
}}
></div>
<div className="px-4 pt-2 pb-4 sm:px-6 text-sm text-gray-700 group-hover:underline">{title}</div>
</div>
</Link>
);
}

export default function GalleryGroup(props: {
title: string,
path: string,
items: GalleryItemProps[],
}) {
const { title, path, items } = props;
const location = useLocation();
const isActive = useMemo(() => {
return location.pathname.endsWith(path) || location.pathname.endsWith(path + '/');
}, [location.pathname, path]);

return (
<>
{isActive && <>
<h2 className="mb-6 text-2xl font-bold tracking-tight text-gray-900">{title}</h2>
<div className="grid justify-between gap-y-5 gap-x-4 grid-cols-[repeat(auto-fill,minmax(16rem,1fr))] my-7">
{
items.map(({ id, title }, index) =>
<GalleryItem key={index} id={id} title={title} />)
}
</div>
</>}
<Outlet />
</>
)
}
1 change: 1 addition & 0 deletions packages/playground/src/gallery/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { themeContext, useTheme } from '../examples/context';
3 changes: 3 additions & 0 deletions packages/playground/src/gallery/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
26 changes: 26 additions & 0 deletions packages/playground/src/gallery/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import Layout from './layout';
import { pages } from './pages';
import ErrorPage from './components/ErrorPage';
import './index.css';

export const homeRoute = "/gallery"

const root = createRoot(document.getElementById("root") as HTMLElement);

const router = createBrowserRouter([
{
path: homeRoute,
element: <Layout />,
errorElement: <ErrorPage />,
children: pages,
},
]);

root.render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);
Loading

0 comments on commit 315e18e

Please sign in to comment.