diff --git a/.eslintignore b/.eslintignore index e061d4c1b47..ce46a7f8c51 100644 --- a/.eslintignore +++ b/.eslintignore @@ -8,6 +8,7 @@ pnpm-lock.yaml /packages/toolpad-studio/public build +dist /examples/*/toolpad.yml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1974a40fe06..3766b6eca27 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -71,7 +71,11 @@ You can also test the example apps to make sure they work as expected. pnpm --filter @toolpad/core dev ``` -3. Run the example app +3. Install then run the example app + + ```bash + pnpm --filter core-nextjs install + ``` ```bash pnpm --filter core-nextjs dev diff --git a/docs/data/toolpad/core/introduction/ReactRouter.js b/docs/data/toolpad/core/introduction/ReactRouter.js new file mode 100644 index 00000000000..7b8059c1283 --- /dev/null +++ b/docs/data/toolpad/core/introduction/ReactRouter.js @@ -0,0 +1,130 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { createTheme } from '@mui/material/styles'; +import Typography from '@mui/material/Typography'; +import DashboardIcon from '@mui/icons-material/Dashboard'; +import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; +import { createMemoryRouter, RouterProvider, Outlet } from 'react-router-dom'; +import { AppProvider } from '@toolpad/core/react-router-dom'; +import { DashboardLayout } from '@toolpad/core/DashboardLayout'; +import { PageContainer } from '@toolpad/core/PageContainer'; + +function Layout() { + return ( + + + + + + ); +} + +function DashboardPage() { + return Welcome to Toolpad!; +} + +function OrdersPage() { + return Welcome to the orders page!; +} + +const NAVIGATION = [ + { + kind: 'header', + title: 'Main items', + }, + { + title: 'Dashboard', + icon: , + }, + { + segment: 'orders', + title: 'Orders', + icon: , + }, +]; + +const BRANDING = { + title: 'My Toolpad Core App', +}; + +const demoTheme = createTheme({ + cssVariables: { + colorSchemeSelector: 'data-toolpad-color-scheme', + }, + colorSchemes: { light: true, dark: true }, + breakpoints: { + values: { + xs: 0, + sm: 600, + md: 600, + lg: 1200, + xl: 1536, + }, + }, +}); + +function App(props) { + const { window } = props; + + return ( + + + + ); +} + +App.propTypes = { + window: PropTypes.object, +}; + +function ReactRouter(props) { + const { window } = props; + + // Remove this const when copying and pasting into your project. + const demoWindow = window !== undefined ? window() : undefined; + + // preview-start + const router = React.useMemo( + () => + createMemoryRouter([ + { + element: , // root layout route + children: [ + { + path: '/', + Component: Layout, + children: [ + { + path: '/', + Component: DashboardPage, + }, + { + path: '/orders', + Component: OrdersPage, + }, + ], + }, + ], + }, + ]), + [demoWindow], + ); + + return ; + // preview-end +} + +ReactRouter.propTypes = { + /** + * Injected by the documentation to work in an iframe. + * Remove this when copying and pasting into your project. + */ + window: PropTypes.func.isRequired, +}; + +export default ReactRouter; diff --git a/docs/data/toolpad/core/introduction/ReactRouter.tsx b/docs/data/toolpad/core/introduction/ReactRouter.tsx new file mode 100644 index 00000000000..606eea7f552 --- /dev/null +++ b/docs/data/toolpad/core/introduction/ReactRouter.tsx @@ -0,0 +1,124 @@ +import * as React from 'react'; +import { createTheme } from '@mui/material/styles'; +import Typography from '@mui/material/Typography'; +import DashboardIcon from '@mui/icons-material/Dashboard'; +import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; +import { createMemoryRouter, RouterProvider, Outlet } from 'react-router-dom'; +import { AppProvider } from '@toolpad/core/react-router-dom'; +import { DashboardLayout } from '@toolpad/core/DashboardLayout'; +import { PageContainer } from '@toolpad/core/PageContainer'; +import type { Navigation } from '@toolpad/core'; + +function Layout() { + return ( + + + + + + ); +} + +function DashboardPage() { + return Welcome to Toolpad!; +} + +function OrdersPage() { + return Welcome to the orders page!; +} + +const NAVIGATION: Navigation = [ + { + kind: 'header', + title: 'Main items', + }, + { + title: 'Dashboard', + icon: , + }, + { + segment: 'orders', + title: 'Orders', + icon: , + }, +]; + +const BRANDING = { + title: 'My Toolpad Core App', +}; + +const demoTheme = createTheme({ + cssVariables: { + colorSchemeSelector: 'data-toolpad-color-scheme', + }, + colorSchemes: { light: true, dark: true }, + breakpoints: { + values: { + xs: 0, + sm: 600, + md: 600, + lg: 1200, + xl: 1536, + }, + }, +}); + +function App(props: { window?: Window }) { + const { window } = props; + + return ( + + + + ); +} + +interface DemoProps { + /** + * Injected by the documentation to work in an iframe. + * Remove this when copying and pasting into your project. + */ + window: () => Window; +} + +export default function ReactRouter(props: DemoProps) { + const { window } = props; + + // Remove this const when copying and pasting into your project. + const demoWindow = window !== undefined ? window() : undefined; + + // preview-start + const router = React.useMemo( + () => + createMemoryRouter([ + { + element: , // root layout route + children: [ + { + path: '/', + Component: Layout, + children: [ + { + path: '/', + Component: DashboardPage, + }, + { + path: '/orders', + Component: OrdersPage, + }, + ], + }, + ], + }, + ]), + [demoWindow], + ); + + return ; + // preview-end +} diff --git a/docs/data/toolpad/core/introduction/ReactRouter.tsx.preview b/docs/data/toolpad/core/introduction/ReactRouter.tsx.preview new file mode 100644 index 00000000000..3fd158b2d3e --- /dev/null +++ b/docs/data/toolpad/core/introduction/ReactRouter.tsx.preview @@ -0,0 +1,27 @@ +const router = React.useMemo( + () => + createMemoryRouter([ + { + element: , // root layout route + children: [ + { + path: '/', + Component: Layout, + children: [ + { + path: '/', + Component: DashboardPage, + }, + { + path: '/orders', + Component: OrdersPage, + }, + ], + }, + ], + }, + ]), + [demoWindow], +); + +return ; \ No newline at end of file diff --git a/docs/data/toolpad/core/introduction/base-concepts.md b/docs/data/toolpad/core/introduction/base-concepts.md index 72e52f80e16..96409572c4a 100644 --- a/docs/data/toolpad/core/introduction/base-concepts.md +++ b/docs/data/toolpad/core/introduction/base-concepts.md @@ -69,7 +69,7 @@ You can pass the router implementation to the `AppProvider` component using the :::success If you are using Next.js, use the `AppProvider` exported from `@toolpad/core/nextjs`. -If you are building a single-page application with React Router for routing, use the `AppProvider` exported from `@toolpad/core/react-router-dom`. +If you are building a single-page application (with [Vite](https://vite.dev/), for example) using React Router for routing, use the `AppProvider` exported from `@toolpad/core/react-router-dom`. This automatically sets up the router for you, so that you don't need to pass the `router` prop. ::: diff --git a/docs/data/toolpad/core/introduction/integration.md b/docs/data/toolpad/core/introduction/integration.md index fda56d5534b..b8e21e4f315 100644 --- a/docs/data/toolpad/core/introduction/integration.md +++ b/docs/data/toolpad/core/introduction/integration.md @@ -666,3 +666,175 @@ That's it! You now have Toolpad Core integrated into your Next.js Pages Router a :::info For a full working example with authentication included, see the [Toolpad Core Next.js Pages app with Auth.js example](https://github.com/mui/toolpad/tree/master/examples/core-auth-nextjs-pages) ::: + +## React Router + +To integrate Toolpad Core into a single-page app (with [Vite](https://vite.dev/), for example) using **React Router**, follow these steps: + +### 1. Wrap all your pages in an `AppProvider` + +In your router configuration (e.g.: `src/main.tsx`), use a shared component or element (e.g.: `src/App.tsx`) as a root **layout route** that will wrap the whole application with the `AppProvider` from `@toolpad/core/react-router-dom`. + +You must use the `` component from `react-router-dom` in this root layout element or component. + +```tsx title="src/main.tsx" +import * as React from 'react'; +import * as ReactDOM from 'react-dom/client'; +import { createBrowserRouter, RouterProvider } from 'react-router-dom'; +import App from './App'; +import DashboardPage from './pages'; +import OrdersPage from './pages/orders'; + +const router = createBrowserRouter([ + { + Component: App, // root layout route + }, +]); + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +); +``` + +```tsx title="src/App.tsx" +import * as React from 'react'; +import DashboardIcon from '@mui/icons-material/Dashboard'; +import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; +import { AppProvider } from '@toolpad/core/react-router-dom'; +import { Outlet } from 'react-router-dom'; +import type { Navigation } from '@toolpad/core'; + +const NAVIGATION: Navigation = [ + { + kind: 'header', + title: 'Main items', + }, + { + title: 'Dashboard', + icon: , + }, + { + segment: 'orders', + title: 'Orders', + icon: , + }, +]; + +const BRANDING = { + title: 'My Toolpad Core App', +}; + +export default function App() { + return ( + + + + ); +} +``` + +### 2. Create a dashboard layout + +Create a layout file for your dashboard pages (e.g.: `src/layouts/dashboard.tsx`), to also be used as a layout route with the `` component from `react-router-dom`: + +```tsx title="src/layouts/dashboard.tsx" +import * as React from 'react'; +import { Outlet } from 'react-router-dom'; +import { DashboardLayout } from '@toolpad/core/DashboardLayout'; +import { PageContainer } from '@toolpad/core/PageContainer'; + +export default function Layout() { + return ( + + + + + + ); +} +``` + +The [`DashboardLayout`](/toolpad/core/react-dashboard-layout/) component provides a consistent layout for your dashboard pages, including a sidebar, navigation, and header. The [`PageContainer`](/toolpad/core/react-page-container/) component is used to wrap the page content, and provides breadcrumbs for navigation. + +You can then add this layout component to your React Router configuration (e.g.: `src/main.tsx`), as a child of the root layout route created in step 1. + +```tsx title="src/main.tsx" +import Layout from './layouts/dashboard'; + +//... +const router = createBrowserRouter([ + { + Component: App, // root layout route + children: [ + { + path: '/', + Component: Layout, + }, + ], + }, +]); +//... +``` + +### 3. Create pages + +Create a dashboard page (e.g.: `src/pages/index.tsx`) and an orders page (`src/pages/orders.tsx`). + +```tsx title="src/pages/index.tsx" +import * as React from 'react'; +import Typography from '@mui/material/Typography'; + +export default function DashboardPage() { + return Welcome to Toolpad!; +} +``` + +```tsx title="src/pages/orders.tsx" +import * as React from 'react'; +import Typography from '@mui/material/Typography'; + +export default function OrdersPage() { + return Welcome to the Toolpad orders!; +} +``` + +You can then add these page components as routes to your React Router configuration (e.g.: `src/main.tsx`). By adding them as children of the layout route created in step 2, they will automatically be wrapped with that dashboard layout: + +```tsx title="src/main.tsx" +import DashboardPage from './pages'; +import OrdersPage from './pages/orders'; + +//... +const router = createBrowserRouter([ + { + Component: App, // root layout route + children: [ + { + path: '/', + Component: Layout, + children: [ + { + path: '/', + Component: DashboardPage, + }, + { + path: '/orders', + Component: OrdersPage, + }, + ], + }, + ], + }, +]); +//... +``` + +That's it! You now have Toolpad Core integrated into your single-page app with React Router! + +{{"demo": "ReactRouter.js", "height": 500, "iframe": true, "hideToolbar": true}} + +:::info +For a full working example, see the [Toolpad Core Vite app with React Router example](https://github.com/mui/toolpad/tree/master/examples/core-vite) +::: diff --git a/docs/public/static/toolpad/docs/core/vite-react-router.png b/docs/public/static/toolpad/docs/core/vite-react-router.png new file mode 100644 index 00000000000..d7e6fa2935e Binary files /dev/null and b/docs/public/static/toolpad/docs/core/vite-react-router.png differ diff --git a/docs/src/modules/components/ExamplesGrid/core-examples.ts b/docs/src/modules/components/ExamplesGrid/core-examples.ts index d1eec5beb56..b99860b01a8 100644 --- a/docs/src/modules/components/ExamplesGrid/core-examples.ts +++ b/docs/src/modules/components/ExamplesGrid/core-examples.ts @@ -22,5 +22,12 @@ export default function examples() { src: '/static/toolpad/docs/core/auth-next.png', source: 'https://github.com/mui/toolpad/tree/master/examples/core-auth-nextjs-pages', }, + { + title: 'Vite with React Router', + description: + 'This app shows you to how to get started using Toolpad Core with Vite and React Router', + src: '/static/toolpad/docs/core/vite-react-router.png', + source: 'https://github.com/mui/toolpad/tree/master/examples/core-vite', + }, ]; } diff --git a/examples/core-auth-nextjs-pages-nextauth-4/README.md b/examples/core-auth-nextjs-pages-nextauth-4/README.md index 713d86babaf..d33f0dc405c 100644 --- a/examples/core-auth-nextjs-pages-nextauth-4/README.md +++ b/examples/core-auth-nextjs-pages-nextauth-4/README.md @@ -1,4 +1,4 @@ -# Toolpad Core Playground - Next.js Pages Router with Next Auth 4 +# Toolpad Core - Next.js Pages Router with Next Auth 4 This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). diff --git a/examples/core-auth-nextjs-pages/README.md b/examples/core-auth-nextjs-pages/README.md index 18729facd52..e92d004f1c3 100644 --- a/examples/core-auth-nextjs-pages/README.md +++ b/examples/core-auth-nextjs-pages/README.md @@ -1,4 +1,4 @@ -# Toolpad Core Playground - Next.js Pages Router +# Toolpad Core - Next.js Pages Router This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). diff --git a/examples/core-auth-nextjs/README.md b/examples/core-auth-nextjs/README.md index 38b913c6863..be121f349a6 100644 --- a/examples/core-auth-nextjs/README.md +++ b/examples/core-auth-nextjs/README.md @@ -1,4 +1,4 @@ -# Toolpad Core Playground - Next.js App Router +# Toolpad Core - Next.js App Router This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). diff --git a/examples/core-vite/.gitignore b/examples/core-vite/.gitignore new file mode 100644 index 00000000000..a547bf36d8d --- /dev/null +++ b/examples/core-vite/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/examples/core-vite/README.md b/examples/core-vite/README.md new file mode 100644 index 00000000000..fdcc372ab1e --- /dev/null +++ b/examples/core-vite/README.md @@ -0,0 +1,23 @@ +# Toolpad Core - Vite & React Router + +This example provides a minimal setup to get Toolpad Core working in Vite with HMR and some ESLint rules, as well as routing with React Router. + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:5173](http://localhost:5173) with your browser to see the result. + +## The source + +[Check out the source code](https://github.com/mui/toolpad/tree/master/examples/core-vite) diff --git a/examples/core-vite/index.html b/examples/core-vite/index.html new file mode 100644 index 00000000000..1f790b03947 --- /dev/null +++ b/examples/core-vite/index.html @@ -0,0 +1,20 @@ + + + + + + + + + + + Toolpad Core Vite + + +
+ + + diff --git a/examples/core-vite/package.json b/examples/core-vite/package.json new file mode 100644 index 00000000000..afe44cc4cf7 --- /dev/null +++ b/examples/core-vite/package.json @@ -0,0 +1,27 @@ +{ + "name": "core-vite", + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@emotion/react": "^11", + "@emotion/styled": "^11", + "@mui/icons-material": "^6", + "@mui/material": "^6", + "@toolpad/core": "latest", + "react": "^18", + "react-dom": "^18", + "react-router-dom": "^6" + }, + "devDependencies": { + "@types/react": "^18", + "@types/react-dom": "^18", + "@vitejs/plugin-react": "^4.3.2", + "typescript": "^5", + "vite": "^5.4.8" + } +} diff --git a/examples/core-vite/public/vite.svg b/examples/core-vite/public/vite.svg new file mode 100644 index 00000000000..e7b8dfb1b2a --- /dev/null +++ b/examples/core-vite/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/core-vite/src/App.tsx b/examples/core-vite/src/App.tsx new file mode 100644 index 00000000000..254413daced --- /dev/null +++ b/examples/core-vite/src/App.tsx @@ -0,0 +1,34 @@ +import * as React from 'react'; +import DashboardIcon from '@mui/icons-material/Dashboard'; +import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; +import { Outlet } from 'react-router-dom'; +import { AppProvider } from '@toolpad/core/react-router-dom'; +import type { Navigation } from '@toolpad/core'; + +const NAVIGATION: Navigation = [ + { + kind: 'header', + title: 'Main items', + }, + { + title: 'Dashboard', + icon: , + }, + { + segment: 'orders', + title: 'Orders', + icon: , + }, +]; + +const BRANDING = { + title: 'My Toolpad Core App', +}; + +export default function App() { + return ( + + + + ); +} diff --git a/examples/core-vite/src/assets/.gitkeep b/examples/core-vite/src/assets/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/examples/core-vite/src/layouts/dashboard.tsx b/examples/core-vite/src/layouts/dashboard.tsx new file mode 100644 index 00000000000..c540feb6e0f --- /dev/null +++ b/examples/core-vite/src/layouts/dashboard.tsx @@ -0,0 +1,14 @@ +import * as React from 'react'; +import { Outlet } from 'react-router-dom'; +import { DashboardLayout } from '@toolpad/core/DashboardLayout'; +import { PageContainer } from '@toolpad/core/PageContainer'; + +export default function Layout() { + return ( + + + + + + ); +} diff --git a/examples/core-vite/src/main.tsx b/examples/core-vite/src/main.tsx new file mode 100644 index 00000000000..cda00abd9f2 --- /dev/null +++ b/examples/core-vite/src/main.tsx @@ -0,0 +1,35 @@ +import * as React from 'react'; +import * as ReactDOM from 'react-dom/client'; +import { createBrowserRouter, RouterProvider } from 'react-router-dom'; +import App from './App'; +import Layout from './layouts/dashboard'; +import DashboardPage from './pages'; +import OrdersPage from './pages/orders'; + +const router = createBrowserRouter([ + { + Component: App, + children: [ + { + path: '/', + Component: Layout, + children: [ + { + path: '/', + Component: DashboardPage, + }, + { + path: '/orders', + Component: OrdersPage, + }, + ], + }, + ], + }, +]); + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/examples/core-vite/src/pages/index.tsx b/examples/core-vite/src/pages/index.tsx new file mode 100644 index 00000000000..e4581fc26bf --- /dev/null +++ b/examples/core-vite/src/pages/index.tsx @@ -0,0 +1,6 @@ +import * as React from 'react'; +import Typography from '@mui/material/Typography'; + +export default function DashboardPage() { + return Welcome to Toolpad!; +} diff --git a/examples/core-vite/src/pages/orders.tsx b/examples/core-vite/src/pages/orders.tsx new file mode 100644 index 00000000000..de4948afd88 --- /dev/null +++ b/examples/core-vite/src/pages/orders.tsx @@ -0,0 +1,6 @@ +import * as React from 'react'; +import Typography from '@mui/material/Typography'; + +export default function OrdersPage() { + return Welcome to the Toolpad orders!; +} diff --git a/examples/core-vite/src/vite-env.d.ts b/examples/core-vite/src/vite-env.d.ts new file mode 100644 index 00000000000..11f02fe2a00 --- /dev/null +++ b/examples/core-vite/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/core-vite/tsconfig.json b/examples/core-vite/tsconfig.json new file mode 100644 index 00000000000..3d0a51a86e2 --- /dev/null +++ b/examples/core-vite/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/examples/core-vite/tsconfig.node.json b/examples/core-vite/tsconfig.node.json new file mode 100644 index 00000000000..9d31e2aed93 --- /dev/null +++ b/examples/core-vite/tsconfig.node.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "composite": true, + "module": "ESNext", + "moduleResolution": "Node", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/examples/core-vite/vite.config.ts b/examples/core-vite/vite.config.ts new file mode 100644 index 00000000000..627a3196243 --- /dev/null +++ b/examples/core-vite/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}); diff --git a/package.json b/package.json index 407eb30a898..5fa3ec6a490 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "markdownlint": "markdownlint-cli2 \"**/*.md\"", "prettier": "pretty-quick --ignore-path .eslintignore", "prettier:all": "prettier --write . --ignore-path .eslintignore", - "dev": "dotenv cross-env FORCE_COLOR=1 lerna -- run dev --stream --parallel --ignore docs --ignore playground-nextjs --ignore playground-nextjs-pages", + "dev": "dotenv cross-env FORCE_COLOR=1 lerna -- run dev --stream --parallel --ignore docs --ignore playground-nextjs --ignore playground-nextjs-pages --ignore playground-vite", "docs:dev": "pnpm --filter docs dev", "docs:build": "pnpm --filter docs build", "docs:build:api:core": "tsx --tsconfig ./scripts/tsconfig.json ./scripts/docs/buildCoreApiDocs/index.ts", diff --git a/playground/vite/.gitignore b/playground/vite/.gitignore new file mode 100644 index 00000000000..a547bf36d8d --- /dev/null +++ b/playground/vite/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/playground/vite/README.md b/playground/vite/README.md new file mode 100644 index 00000000000..77643b462dd --- /dev/null +++ b/playground/vite/README.md @@ -0,0 +1,8 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh diff --git a/playground/vite/index.html b/playground/vite/index.html new file mode 100644 index 00000000000..1f790b03947 --- /dev/null +++ b/playground/vite/index.html @@ -0,0 +1,20 @@ + + + + + + + + + + + Toolpad Core Vite + + +
+ + + diff --git a/playground/vite/package.json b/playground/vite/package.json new file mode 100644 index 00000000000..449b8b3c90e --- /dev/null +++ b/playground/vite/package.json @@ -0,0 +1,24 @@ +{ + "name": "playground-vite", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "preview": "vite preview" + }, + "devDependencies": { + "@emotion/react": "11.13.3", + "@emotion/styled": "11.13.0", + "@mui/icons-material": "6.1.2", + "@mui/material": "6.1.2", + "@toolpad/core": "workspace:*", + "@types/react": "18.3.11", + "@types/react-dom": "18.3.0", + "@vitejs/plugin-react": "4.3.2", + "react": "18.3.1", + "react-dom": "18.3.1", + "react-router-dom": "6.26.2", + "vite": "5.4.8" + } +} diff --git a/playground/vite/public/vite.svg b/playground/vite/public/vite.svg new file mode 100644 index 00000000000..e7b8dfb1b2a --- /dev/null +++ b/playground/vite/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/playground/vite/src/App.tsx b/playground/vite/src/App.tsx new file mode 100644 index 00000000000..254413daced --- /dev/null +++ b/playground/vite/src/App.tsx @@ -0,0 +1,34 @@ +import * as React from 'react'; +import DashboardIcon from '@mui/icons-material/Dashboard'; +import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; +import { Outlet } from 'react-router-dom'; +import { AppProvider } from '@toolpad/core/react-router-dom'; +import type { Navigation } from '@toolpad/core'; + +const NAVIGATION: Navigation = [ + { + kind: 'header', + title: 'Main items', + }, + { + title: 'Dashboard', + icon: , + }, + { + segment: 'orders', + title: 'Orders', + icon: , + }, +]; + +const BRANDING = { + title: 'My Toolpad Core App', +}; + +export default function App() { + return ( + + + + ); +} diff --git a/playground/vite/src/assets/.gitkeep b/playground/vite/src/assets/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/playground/vite/src/layouts/dashboard.tsx b/playground/vite/src/layouts/dashboard.tsx new file mode 100644 index 00000000000..c540feb6e0f --- /dev/null +++ b/playground/vite/src/layouts/dashboard.tsx @@ -0,0 +1,14 @@ +import * as React from 'react'; +import { Outlet } from 'react-router-dom'; +import { DashboardLayout } from '@toolpad/core/DashboardLayout'; +import { PageContainer } from '@toolpad/core/PageContainer'; + +export default function Layout() { + return ( + + + + + + ); +} diff --git a/playground/vite/src/main.tsx b/playground/vite/src/main.tsx new file mode 100644 index 00000000000..cda00abd9f2 --- /dev/null +++ b/playground/vite/src/main.tsx @@ -0,0 +1,35 @@ +import * as React from 'react'; +import * as ReactDOM from 'react-dom/client'; +import { createBrowserRouter, RouterProvider } from 'react-router-dom'; +import App from './App'; +import Layout from './layouts/dashboard'; +import DashboardPage from './pages'; +import OrdersPage from './pages/orders'; + +const router = createBrowserRouter([ + { + Component: App, + children: [ + { + path: '/', + Component: Layout, + children: [ + { + path: '/', + Component: DashboardPage, + }, + { + path: '/orders', + Component: OrdersPage, + }, + ], + }, + ], + }, +]); + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/playground/vite/src/pages/index.tsx b/playground/vite/src/pages/index.tsx new file mode 100644 index 00000000000..e4581fc26bf --- /dev/null +++ b/playground/vite/src/pages/index.tsx @@ -0,0 +1,6 @@ +import * as React from 'react'; +import Typography from '@mui/material/Typography'; + +export default function DashboardPage() { + return Welcome to Toolpad!; +} diff --git a/playground/vite/src/pages/orders.tsx b/playground/vite/src/pages/orders.tsx new file mode 100644 index 00000000000..de4948afd88 --- /dev/null +++ b/playground/vite/src/pages/orders.tsx @@ -0,0 +1,6 @@ +import * as React from 'react'; +import Typography from '@mui/material/Typography'; + +export default function OrdersPage() { + return Welcome to the Toolpad orders!; +} diff --git a/playground/vite/src/vite-env.d.ts b/playground/vite/src/vite-env.d.ts new file mode 100644 index 00000000000..11f02fe2a00 --- /dev/null +++ b/playground/vite/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/playground/vite/tsconfig.json b/playground/vite/tsconfig.json new file mode 100644 index 00000000000..251a83f8a97 --- /dev/null +++ b/playground/vite/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": ["src"] +} diff --git a/playground/vite/vite.config.ts b/playground/vite/vite.config.ts new file mode 100644 index 00000000000..627a3196243 --- /dev/null +++ b/playground/vite/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8b9772e8ac8..bdaed45ae50 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1289,6 +1289,45 @@ importers: specifier: 18.3.1 version: 18.3.1(react@18.3.1) + playground/vite: + devDependencies: + '@emotion/react': + specifier: 11.13.3 + version: 11.13.3(@types/react@18.3.11)(react@18.3.1) + '@emotion/styled': + specifier: 11.13.0 + version: 11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1) + '@mui/icons-material': + specifier: 6.1.2 + version: 6.1.2(@mui/material@6.1.2(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.11)(react@18.3.1) + '@mui/material': + specifier: 6.1.2 + version: 6.1.2(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@toolpad/core': + specifier: workspace:* + version: link:../../packages/toolpad-core/build + '@types/react': + specifier: 18.3.11 + version: 18.3.11 + '@types/react-dom': + specifier: 18.3.0 + version: 18.3.0 + '@vitejs/plugin-react': + specifier: 4.3.2 + version: 4.3.2(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1)) + react: + specifier: 18.3.1 + version: 18.3.1 + react-dom: + specifier: 18.3.1 + version: 18.3.1(react@18.3.1) + react-router-dom: + specifier: 6.26.2 + version: 6.26.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + vite: + specifier: 5.4.8 + version: 5.4.8(@types/node@20.16.11)(terser@5.34.1) + test: devDependencies: '@mui/material': @@ -2929,6 +2968,17 @@ packages: '@types/react': optional: true + '@mui/icons-material@6.1.2': + resolution: {integrity: sha512-7NNcjW5JoT9jHagrVbARA1o41vQY2xezDamtke+mEKKZmsJyejfRBOacSrPDfjZQ//lyhIjNKyzAwisxYJR47w==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@mui/material': ^6.1.2 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@mui/icons-material@6.1.3': resolution: {integrity: sha512-QBQCCIMSAv6IkArTg4Hg8q2sJRhHOci8oPAlkHWFlt2ghBdy3EqyLbIELLE/bhpqhX+E/ZkPYGIUQCd5/L0owA==} engines: {node: '>=14.0.0'} @@ -3010,6 +3060,26 @@ packages: '@types/react': optional: true + '@mui/material@6.1.2': + resolution: {integrity: sha512-5TtHeAVX9D5d2LYfB1GAUn29BcVETVsrQ76Dwb2SpAfQGW3JVy4deJCAd0RrIkI3eEUrsl0E4xuBdreszxdTTg==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@mui/material-pigment-css': ^6.1.2 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@mui/material-pigment-css': + optional: true + '@types/react': + optional: true + '@mui/material@6.1.3': resolution: {integrity: sha512-loV5MBoMKLrK80JeWINmQ1A4eWoLv51O2dBPLJ260IAhupkB3Wol8lEQTEvvR2vO3o6xRHuXe1WaQEP6N3riqg==} engines: {node: '>=14.0.0'} @@ -4226,6 +4296,9 @@ packages: '@types/react-dev-utils@9.0.15': resolution: {integrity: sha512-JSZZtC8f5FD3FvlT8F6dPojPH6u0f+E/+g9DqXgQo/7GvCdPYiK+Q+mMajZClqH/f5SjyYop7v6uiLyfgnyLQA==} + '@types/react-dom@18.3.0': + resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + '@types/react-dom@18.3.1': resolution: {integrity: sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==} @@ -12177,6 +12250,14 @@ snapshots: optionalDependencies: '@types/react': 18.3.11 + '@mui/icons-material@6.1.2(@mui/material@6.1.2(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.7 + '@mui/material': 6.1.2(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.11 + '@mui/icons-material@6.1.3(@mui/material@6.1.3(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)': dependencies: '@babel/runtime': 7.25.7 @@ -12262,6 +12343,27 @@ snapshots: '@emotion/server': 11.11.0 '@types/react': 18.3.11 + '@mui/material@6.1.2(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.7 + '@mui/core-downloads-tracker': 6.1.3 + '@mui/system': 6.1.3(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1) + '@mui/types': 7.2.18(@types/react@18.3.11) + '@mui/utils': 6.1.3(@types/react@18.3.11)(react@18.3.1) + '@popperjs/core': 2.11.8 + '@types/react-transition-group': 4.4.11 + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 18.3.1 + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + optionalDependencies: + '@emotion/react': 11.13.3(@types/react@18.3.11)(react@18.3.1) + '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1) + '@types/react': 18.3.11 + '@mui/material@6.1.3(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.7 @@ -13595,6 +13697,10 @@ snapshots: transitivePeerDependencies: - debug + '@types/react-dom@18.3.0': + dependencies: + '@types/react': 18.3.11 + '@types/react-dom@18.3.1': dependencies: '@types/react': 18.3.11