Line data Source code
1 : import * as React from 'react';
2 : import { createRoot } from 'react-dom/client';
3 : import { HashRouter, Link, Navigate, Route, Routes, useParams } from 'react-router-dom';
4 :
5 : import { LoggerProvider } from '../platform/logger/logger_provider.js';
6 : import { EmbeddedDatabaseProvider } from '../platform/database/embedded_database_provider.js';
7 : import { HttpClientProvider } from '../platform/http/http_client_provider.js';
8 : import { GitHubTheme } from '../styles/github_theme.js';
9 : import { LoggerToast } from '../ui/logger/logger_toast.js';
10 : import { FlavorProvider } from '../config/flavor_provider.js';
11 : import { FLAVOR_QUERY_PARAMETER, getRequestedFlavor, resolveFlavor } from '../config/flavor_resolution.js';
12 : import { DASHQL_FLAVOR, type AppFlavor } from '../config/flavors.js';
13 : import { DEMOS, type DemoEntry } from './demo_registry.js';
14 : import * as styles from './demos.module.css';
15 :
16 : import '@ankoh/dashql-ui/fonts/fonts.css';
17 : import '@ankoh/dashql-ui/styles/colors.css';
18 : import '@ankoh/dashql-ui/styles/tokens.css';
19 : import '../styles/globals.css';
20 : import '@ankoh/dashql-ui/styles/flavors/sidb.css';
21 :
22 : /// The pages of the demos, loaded when they are opened
23 1 : const PAGES = new Map(DEMOS.map(demo => [demo.id, React.lazy(demo.load)]));
24 :
25 : /// The list of the demos
26 1 : const DemoIndex: React.FC = () => (
27 1 : <div className={styles.index}>
28 1 : <h1 className={styles.index_title}>DashQL Demos</h1>
29 1 : <ul className={styles.index_list}>
30 1 : {DEMOS.map((demo: DemoEntry) => (
31 1 : <li key={demo.id}>
32 1 : <Link className={styles.index_entry} to={`/${demo.id}`}>
33 1 : <span className={styles.index_entry_title}>{demo.title}</span>
34 1 : <span className={styles.index_entry_description}>{demo.description}</span>
35 1 : </Link>
36 1 : </li>
37 1 : ))}
38 1 : </ul>
39 1 : </div>
40 : );
41 :
42 : /// The page of the demo of the route
43 1 : const DemoPage: React.FC = () => {
44 1 : const { demoId } = useParams();
45 1 : const Page = demoId == null ? undefined : PAGES.get(demoId);
46 1 : if (Page == null) return <Navigate to="/" replace />;
47 1 : return (
48 1 : <React.Suspense fallback={<div className={styles.loading}>Loading…</div>}>
49 1 : <Page />
50 1 : </React.Suspense>
51 : );
52 1 : };
53 :
54 : /// Demos of DashQL on Hyper, every demo is a page of its own.
55 : /// The routes are hashes, `demos.html#/<id>`, so that every host serves the page without rewrites.
56 : /// A flavor is requested in front of the hash, `demos.html?flavor=sidb#/<id>`, or in the query of the route.
57 1 : export const Demos: React.FC = () => (
58 1 : <HashRouter>
59 1 : <LoggerProvider>
60 1 : <>
61 1 : <LoggerToast />
62 1 : <HttpClientProvider>
63 1 : <EmbeddedDatabaseProvider>
64 1 : <GitHubTheme>
65 1 : <div className={styles.root}>
66 1 : <Routes>
67 1 : <Route index element={<DemoIndex />} />
68 1 : <Route path="/:demoId" element={<DemoPage />} />
69 1 : </Routes>
70 1 : </div>
71 1 : </GitHubTheme>
72 1 : </EmbeddedDatabaseProvider>
73 1 : </HttpClientProvider>
74 1 : </>
75 1 : </LoggerProvider>
76 1 : </HashRouter>
77 : );
78 :
79 : /// The flavor requested by the query of the route, `#/<id>?flavor=sidb`, or else by the query in front of the hash
80 1 : function getRequestedDemoFlavor(): AppFlavor | null {
81 1 : const hash = globalThis.location?.hash ?? '';
82 1 : const query = hash.indexOf('?');
83 1 : const requested = query < 0 ? null : new URLSearchParams(hash.slice(query + 1)).get(FLAVOR_QUERY_PARAMETER);
84 1 : if (requested == null) return getRequestedFlavor();
85 0 : const resolution = resolveFlavor(requested);
86 0 : if (resolution.unknownId !== undefined) console.warn(`unknown app flavor: ${resolution.unknownId}`);
87 0 : return resolution.flavor;
88 1 : }
89 :
90 : // The demos are branded as DashQL, only a flavor other than the default one rebrands them, e.g. their chart colors
91 1 : const requested = getRequestedDemoFlavor();
92 1 : const flavor = requested?.id === DASHQL_FLAVOR.id ? null : requested;
93 :
94 1 : const element = document.getElementById('root');
95 1 : createRoot(element!).render(flavor == null ? <Demos /> : (
96 0 : <FlavorProvider requested={flavor}>
97 0 : <Demos />
98 0 : </FlavorProvider>
99 1 : ));
|