alinea
Version:
Headless git-based CMS
140 lines (138 loc) • 6.51 kB
JavaScript
import {
useAtom,
useAtomValue
} from "../chunks/chunk-TOJF2G3X.js";
import {
atom
} from "../chunks/chunk-WJ67RR7S.js";
import "../chunks/chunk-NZLE2WMY.js";
// src/dashboard/App.tsx
import { Config } from "alinea/core/Config";
import { Root } from "alinea/core/Root";
import { Icon, Loader, px } from "alinea/ui";
import { FavIcon } from "alinea/ui/branding/FavIcon";
import { IcRoundCheck } from "alinea/ui/icons/IcRoundCheck";
import { IcRoundDescription } from "alinea/ui/icons/IcRoundDescription";
import { IcRoundSync } from "alinea/ui/icons/IcRoundSync";
import { MaterialSymbolsDatabase } from "alinea/ui/icons/MaterialSymbolsDatabase";
import { Statusbar } from "alinea/ui/Statusbar";
import { useEffect } from "react";
import { sessionAtom } from "./atoms/DashboardAtoms.js";
import { dbMetaAtom, pendingAtom, useDbUpdater } from "./atoms/DbAtoms.js";
import { errorAtom } from "./atoms/ErrorAtoms.js";
import { locationAtom, matchAtoms } from "./atoms/LocationAtoms.js";
import { usePreferredLanguage } from "./atoms/NavigationAtoms.js";
import { RouterProvider, RouteView } from "./atoms/RouterAtoms.js";
import { navMatchers } from "./DashboardNav.js";
import { DashboardProvider } from "./DashboardProvider.js";
import { useDashboard } from "./hook/UseDashboard.js";
import { useEntryLocation } from "./hook/UseEntryLocation.js";
import { useLocale } from "./hook/UseLocale.js";
import { useNav } from "./hook/UseNav.js";
import { useRoot } from "./hook/UseRoot.js";
import { useWorkspace } from "./hook/UseWorkspace.js";
import { router } from "./Routes.js";
import { Head } from "./util/Head.js";
import { SuspenseBoundary } from "./util/SuspenseBoundary.js";
import { ErrorBoundary } from "./view/ErrorBoundary.js";
import { Modal } from "./view/Modal.js";
import { Sidebar } from "./view/Sidebar.js";
import { SidebarSettings } from "./view/sidebar/SidebarSettings.js";
import { Toolbar } from "./view/Toolbar.js";
import { Viewport } from "./view/Viewport.js";
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
var isEntryAtom = atom((get) => {
const location = get(locationAtom);
const match = get(matchAtoms({ route: navMatchers.matchEntry }));
return Boolean(match) || location.pathname === "/";
});
function AppAuthenticated() {
useDbUpdater();
const { alineaDev, fullPage } = useDashboard();
const nav = useNav();
const isEntry = useAtomValue(isEntryAtom);
const { name: workspace, color, roots } = useWorkspace();
const { name: currentRoot } = useRoot();
const entryLocation = useEntryLocation();
const locale = useLocale();
const [preferredLanguage, setPreferredLanguage] = usePreferredLanguage();
const [errorMessage, setErrorMessage] = useAtom(errorAtom);
const sha = useAtomValue(dbMetaAtom);
const pending = useAtomValue(pendingAtom);
useEffect(() => {
setPreferredLanguage(locale);
}, [locale]);
return /* @__PURE__ */ jsxs(Fragment, { children: [
errorMessage && /* @__PURE__ */ jsx(Modal, { open: true, onClose: () => setErrorMessage(null), children: /* @__PURE__ */ jsx("div", { style: { padding: px(16) }, children: errorMessage }) }),
/* @__PURE__ */ jsx(Statusbar.Provider, { children: /* @__PURE__ */ jsx(Toolbar.Provider, { children: /* @__PURE__ */ jsxs(Sidebar.Provider, { children: [
/* @__PURE__ */ jsx(Head, { children: /* @__PURE__ */ jsx(FavIcon, { color }) }),
/* @__PURE__ */ jsxs(
"div",
{
style: {
flex: "1",
display: "flex",
minHeight: 0,
position: "relative"
},
children: [
/* @__PURE__ */ jsxs(Sidebar.Nav, { children: [
Object.entries(roots).map(([key, root], i) => {
const isSelected = key === currentRoot;
const { id, ...location } = entryLocation;
const link = location.root === key ? nav.entry(location) : nav.root({
workspace,
root: key,
locale: preferredLanguage
});
const { label, icon } = Root.data(root);
return /* @__PURE__ */ jsx(
Sidebar.Nav.Item,
{
selected: isEntry && isSelected,
href: link,
"aria-label": label,
children: /* @__PURE__ */ jsx(Icon, { icon: icon ?? IcRoundDescription })
},
key
);
}),
/* @__PURE__ */ jsx(SidebarSettings, {})
] }),
/* @__PURE__ */ jsx(ErrorBoundary, { children: /* @__PURE__ */ jsx(SuspenseBoundary, { name: "main", fallback: /* @__PURE__ */ jsx(Loader, { absolute: true }), children: /* @__PURE__ */ jsx(RouteView, { fallback: null }) }) })
]
}
),
alineaDev && /* @__PURE__ */ jsxs(Statusbar.Root, { children: [
/* @__PURE__ */ jsx(
Statusbar.Status,
{
icon: pending === 0 ? IcRoundCheck : IcRoundSync,
children: pending === 0 ? "Synced" : "Saving\u2026"
}
),
sha ? /* @__PURE__ */ jsx(Statusbar.Status, { icon: MaterialSymbolsDatabase, children: sha.slice(0, 7) }) : /* @__PURE__ */ jsx(Statusbar.Status, { icon: MaterialSymbolsDatabase, children: "Syncing" })
] })
] }) }) })
] });
}
function AppRoot() {
const [session, setSession] = useAtom(sessionAtom);
const { fullPage, config } = useDashboard();
const { color } = Config.mainWorkspace(config);
const Auth = config.auth;
if (!session)
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(Head, { children: /* @__PURE__ */ jsx(FavIcon, { color }) }),
Auth && /* @__PURE__ */ jsx(SuspenseBoundary, { name: "auth", fallback: /* @__PURE__ */ jsx(Loader, { absolute: true }), children: /* @__PURE__ */ jsx(Auth, { setSession }) })
] });
return /* @__PURE__ */ jsx(SuspenseBoundary, { name: "router", fallback: /* @__PURE__ */ jsx(Loader, { absolute: true }), children: /* @__PURE__ */ jsx(RouterProvider, { router, children: /* @__PURE__ */ jsx(AppAuthenticated, {}) }) });
}
function App(props) {
const fullPage = props.fullPage !== false;
const { color } = Config.mainWorkspace(props.config);
return /* @__PURE__ */ jsx(DashboardProvider, { ...props, children: /* @__PURE__ */ jsx(Viewport, { attachToBody: fullPage, contain: true, color, children: /* @__PURE__ */ jsx(AppRoot, {}) }) });
}
export {
App
};