diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx
index 1bee34c3..93c9211d 100644
--- a/packages/app/src/App.tsx
+++ b/packages/app/src/App.tsx
@@ -7,9 +7,10 @@ import "@malloy-publisher/sdk/styles.css";
import "@malloydata/malloy-explorer/styles.css";
import { CssBaseline } from "@mui/material";
import { ThemeProvider } from "@mui/material/styles";
-import * as React from "react";
+import { lazy, Suspense, useMemo } from "react";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
-import { HeaderProps } from "./components/Header";
+import { Loading } from "./components/common/Loading";
+import { HeaderProps } from "./components/layout/Header/Header";
import theme from "./theme";
/**
@@ -17,13 +18,21 @@ import theme from "./theme";
* React.lazy and dynamic import() statements for lazy loading React
* components.
*/
-const HomePage = React.lazy(() => import("./components/HomePage"));
-const MainPage = React.lazy(() => import("./components/MainPage"));
-const ModelPage = React.lazy(() => import("./components/ModelPage"));
-const PackagePage = React.lazy(() => import("./components/PackagePage"));
-const ProjectPage = React.lazy(() => import("./components/ProjectPage"));
-const RouteError = React.lazy(() => import("./components/RouteError"));
-const WorkbookPage = React.lazy(() => import("./components/WorkbookPage"));
+const HomePage = lazy(() => import("./components/pages/HomePage/HomePage"));
+const MainPage = lazy(() => import("./components/layout/MainPage/MainPage"));
+const ModelPage = lazy(() => import("./components/pages/ModelPage/ModelPage"));
+const PackagePage = lazy(
+ () => import("./components/pages/PackagePage/PackagePage"),
+);
+const ProjectPage = lazy(
+ () => import("./components/pages/ProjectPage/ProjectPage"),
+);
+const RouteError = lazy(
+ () => import("./components/common/RouteError/RouteError"),
+);
+const WorkbookPage = lazy(
+ () => import("./components/pages/WorkbookPage/WorkbookPage"),
+);
// Create router configuration function
export const createMalloyRouter = (
@@ -39,7 +48,9 @@ export const createMalloyRouter = (
-
+ }>
+
+
@@ -47,24 +58,44 @@ export const createMalloyRouter = (
errorElement: ,
children: [
{
- path: "",
- element: ,
+ index: true,
+ element: (
+ }>
+
+
+ ),
},
{
path: ":projectName",
- element: ,
+ element: (
+ }>
+
+
+ ),
},
{
path: ":projectName/:packageName",
- element: ,
+ element: (
+ }>
+
+
+ ),
},
{
path: ":projectName/:packageName/*",
- element: ,
+ element: (
+ }>
+
+
+ ),
},
{
path: ":projectName/:packageName/workbook/:workspace/:workbookPath",
- element: ,
+ element: (
+ }>
+
+
+ ),
},
],
},
@@ -77,11 +108,15 @@ export interface MalloyPublisherAppProps {
workbookStorage: WorkbookStorage;
}
-export const MalloyPublisherApp: React.FC = ({
- workbookStorage,
+export const MalloyPublisherApp = ({
basePath = "/",
+ workbookStorage,
headerProps,
-}) => {
- const router = createMalloyRouter(basePath, workbookStorage, headerProps);
+}: MalloyPublisherAppProps) => {
+ const router = useMemo(
+ () => createMalloyRouter(basePath, workbookStorage, headerProps),
+ [basePath, workbookStorage, headerProps],
+ );
+
return ;
};
diff --git a/packages/app/src/components/common/Loading/Loading.tsx b/packages/app/src/components/common/Loading/Loading.tsx
new file mode 100644
index 00000000..c78269ed
--- /dev/null
+++ b/packages/app/src/components/common/Loading/Loading.tsx
@@ -0,0 +1,102 @@
+import Box from "@mui/material/Box";
+import CircularProgress from "@mui/material/CircularProgress";
+import Typography from "@mui/material/Typography";
+
+export interface LoadingProps {
+ text?: string;
+ /**
+ * The size of the CircularProgress component
+ * @default 20
+ */
+ size?: number | string;
+ /**
+ * The color of the CircularProgress component
+ * @default "primary"
+ */
+ color?:
+ | "primary"
+ | "secondary"
+ | "error"
+ | "info"
+ | "success"
+ | "warning"
+ | "inherit";
+ /**
+ * The thickness of the circular progress
+ * @default 3.6
+ */
+ thickness?: number;
+ /**
+ * Whether to center the component
+ * @default true
+ */
+ centered?: boolean;
+ /**
+ * Custom spacing between spinner and text
+ * @default 2
+ */
+ spacing?: number;
+ /**
+ * Typography variant for the text
+ * @default "body1"
+ */
+ textVariant?:
+ | "h1"
+ | "h2"
+ | "h3"
+ | "h4"
+ | "h5"
+ | "h6"
+ | "subtitle1"
+ | "subtitle2"
+ | "body1"
+ | "body2"
+ | "caption"
+ | "overline";
+}
+
+export function Loading({
+ text,
+ size = 20,
+ color = "primary",
+ thickness = 3.6,
+ centered = true,
+ spacing = 2,
+ textVariant = "body2",
+}: LoadingProps) {
+ const content = (
+
+
+ {text && (
+
+ {text}
+
+ )}
+
+ );
+
+ if (centered) {
+ return (
+
+ {content}
+
+ );
+ }
+
+ return content;
+}
diff --git a/packages/app/src/components/common/Loading/index.ts b/packages/app/src/components/common/Loading/index.ts
new file mode 100644
index 00000000..a9f097d8
--- /dev/null
+++ b/packages/app/src/components/common/Loading/index.ts
@@ -0,0 +1,2 @@
+export { Loading } from "./Loading";
+export type { LoadingProps } from "./Loading";
diff --git a/packages/app/src/components/RouteError.tsx b/packages/app/src/components/common/RouteError/RouteError.tsx
similarity index 100%
rename from packages/app/src/components/RouteError.tsx
rename to packages/app/src/components/common/RouteError/RouteError.tsx
diff --git a/packages/app/src/components/common/index.ts b/packages/app/src/components/common/index.ts
new file mode 100644
index 00000000..d7318b3c
--- /dev/null
+++ b/packages/app/src/components/common/index.ts
@@ -0,0 +1,2 @@
+export * from "./RouteError/RouteError";
+export * from "./Loading";
diff --git a/packages/app/src/components/BreadcrumbNav.tsx b/packages/app/src/components/layout/BreadcrumNav/BreadcrumbNav.tsx
similarity index 97%
rename from packages/app/src/components/BreadcrumbNav.tsx
rename to packages/app/src/components/layout/BreadcrumNav/BreadcrumbNav.tsx
index 3a5e0564..749b5356 100644
--- a/packages/app/src/components/BreadcrumbNav.tsx
+++ b/packages/app/src/components/layout/BreadcrumNav/BreadcrumbNav.tsx
@@ -1,6 +1,7 @@
import { useRouterClickHandler } from "@malloy-publisher/sdk";
import { ChevronRight } from "@mui/icons-material";
-import { Box, Chip } from "@mui/material";
+import Box from "@mui/material/Box";
+import Chip from "@mui/material/Chip";
import Breadcrumbs from "@mui/material/Breadcrumbs";
import { useParams } from "react-router-dom";
diff --git a/packages/app/src/components/Header.tsx b/packages/app/src/components/layout/Header/Header.tsx
similarity index 88%
rename from packages/app/src/components/Header.tsx
rename to packages/app/src/components/layout/Header/Header.tsx
index 2d1e758c..2dbd14be 100644
--- a/packages/app/src/components/Header.tsx
+++ b/packages/app/src/components/layout/Header/Header.tsx
@@ -1,20 +1,18 @@
import MenuIcon from "@mui/icons-material/Menu";
-import {
- AppBar,
- Box,
- Button,
- IconButton,
- Menu,
- MenuItem,
- Stack,
- Toolbar,
- Typography,
- useMediaQuery,
- useTheme,
-} from "@mui/material";
+import AppBar from "@mui/material/AppBar";
+import MenuItem from "@mui/material/MenuItem";
+import Box from "@mui/material/Box";
+import Button from "@mui/material/Button";
+import IconButton from "@mui/material/IconButton";
+import Menu from "@mui/material/Menu";
+import Stack from "@mui/material/Stack";
+import Toolbar from "@mui/material/Toolbar";
+import Typography from "@mui/material/Typography";
+import useMediaQuery from "@mui/material/useMediaQuery";
+import { useTheme } from "@mui/material/styles";
import { useState } from "react";
import { useNavigate } from "react-router-dom";
-import BreadcrumbNav from "./BreadcrumbNav";
+import BreadcrumbNav from "../BreadcrumNav/BreadcrumbNav";
export interface HeaderProps {
logoHeader?: React.ReactElement;
diff --git a/packages/app/src/components/index.ts b/packages/app/src/components/layout/Header/index.ts
similarity index 100%
rename from packages/app/src/components/index.ts
rename to packages/app/src/components/layout/Header/index.ts
diff --git a/packages/app/src/components/MainPage.tsx b/packages/app/src/components/layout/MainPage/MainPage.tsx
similarity index 93%
rename from packages/app/src/components/MainPage.tsx
rename to packages/app/src/components/layout/MainPage/MainPage.tsx
index 83433b73..97a0008a 100644
--- a/packages/app/src/components/MainPage.tsx
+++ b/packages/app/src/components/layout/MainPage/MainPage.tsx
@@ -1,7 +1,7 @@
import { Box } from "@mui/material";
import Container from "@mui/material/Container";
import { Outlet } from "react-router-dom";
-import Header, { HeaderProps } from "./Header";
+import Header, { HeaderProps } from "../Header/Header";
interface PublisherConfigProps {
headerProps?: HeaderProps;
diff --git a/packages/app/src/components/layout/index.ts b/packages/app/src/components/layout/index.ts
new file mode 100644
index 00000000..dbb58c49
--- /dev/null
+++ b/packages/app/src/components/layout/index.ts
@@ -0,0 +1,4 @@
+export * from "./Header";
+export type { HeaderProps } from "./Header";
+export { default as MainPage } from "./MainPage/MainPage";
+export { default as BreadcrumbNav } from "./BreadcrumNav/BreadcrumbNav";
diff --git a/packages/app/src/components/HomePage.tsx b/packages/app/src/components/pages/HomePage/HomePage.tsx
similarity index 100%
rename from packages/app/src/components/HomePage.tsx
rename to packages/app/src/components/pages/HomePage/HomePage.tsx
diff --git a/packages/app/src/components/ModelPage.tsx b/packages/app/src/components/pages/ModelPage/ModelPage.tsx
similarity index 100%
rename from packages/app/src/components/ModelPage.tsx
rename to packages/app/src/components/pages/ModelPage/ModelPage.tsx
diff --git a/packages/app/src/components/PackagePage.tsx b/packages/app/src/components/pages/PackagePage/PackagePage.tsx
similarity index 100%
rename from packages/app/src/components/PackagePage.tsx
rename to packages/app/src/components/pages/PackagePage/PackagePage.tsx
diff --git a/packages/app/src/components/ProjectPage.tsx b/packages/app/src/components/pages/ProjectPage/ProjectPage.tsx
similarity index 100%
rename from packages/app/src/components/ProjectPage.tsx
rename to packages/app/src/components/pages/ProjectPage/ProjectPage.tsx
diff --git a/packages/app/src/components/WorkbookPage.tsx b/packages/app/src/components/pages/WorkbookPage/WorkbookPage.tsx
similarity index 100%
rename from packages/app/src/components/WorkbookPage.tsx
rename to packages/app/src/components/pages/WorkbookPage/WorkbookPage.tsx
diff --git a/packages/app/src/components/pages/index.ts b/packages/app/src/components/pages/index.ts
new file mode 100644
index 00000000..b7a7fe27
--- /dev/null
+++ b/packages/app/src/components/pages/index.ts
@@ -0,0 +1,5 @@
+export { default as HomePage } from "./HomePage/HomePage";
+export { default as ProjectPage } from "./ProjectPage/ProjectPage";
+export { default as PackagePage } from "./PackagePage/PackagePage";
+export { default as ModelPage } from "./ModelPage/ModelPage";
+export { default as WorkbookPage } from "./WorkbookPage/WorkbookPage";
diff --git a/packages/app/src/components/ToggleColorMode.tsx b/packages/app/src/components/theme/ToggleColorMode/ToggleColorMode.tsx
similarity index 100%
rename from packages/app/src/components/ToggleColorMode.tsx
rename to packages/app/src/components/theme/ToggleColorMode/ToggleColorMode.tsx
diff --git a/packages/app/src/index.ts b/packages/app/src/index.ts
index c8b5fdec..8dbdc9d7 100644
--- a/packages/app/src/index.ts
+++ b/packages/app/src/index.ts
@@ -1,10 +1,10 @@
// Main App exports
-export { MalloyPublisherApp, createMalloyRouter } from "./App";
+export { createMalloyRouter, MalloyPublisherApp } from "./App";
export type { MalloyPublisherAppProps } from "./App";
// Theme exports
+export * from "./components/layout/Header";
export { default as theme } from "./theme";
-export * from "./components";
// Additional component exports for advanced usage
-export { default as BreadcrumbNav } from "./components/BreadcrumbNav";
-export { default as ToggleColorMode } from "./components/ToggleColorMode";
+export { default as BreadcrumbNav } from "./components/layout/BreadcrumNav/BreadcrumbNav";
+export { default as ToggleColorMode } from "./components/theme/ToggleColorMode/ToggleColorMode";