Skip to content

Commit

Permalink
refactor: Integrate Joy UI and Jotai
Browse files Browse the repository at this point in the history
  • Loading branch information
koistya committed Dec 3, 2023
1 parent 0be37c5 commit 310e989
Show file tree
Hide file tree
Showing 75 changed files with 1,137 additions and 2,362 deletions.
10 changes: 6 additions & 4 deletions env/.local.env → .env
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# Environment variables for the local development and testing
# Environment variables
# https://vitejs.dev/guide/env-and-mode.html#env-files
#
# NOTE: You can place secrets and local overrides into the
# ".local.override.env" file which is excluded from this Git repo
# TIP: Feel free to personalize these settings in your `.env.local` file that
# is not tracked by source control, giving you the liberty to tweak
# settings in your local environment worry-free! Happy coding! 🚀

# Web application settings
APP_ENV=local
APP_NAME=React App
APP_NAME=Acme Co.
APP_HOSTNAME=localhost
APP_ORIGIN=http://localhost:5173
API_ORIGIN=https://api-mcfytwakla-uc.a.run.app
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ yarn-error.log*
*.lcov

# Environment variables
*.override.env
.env.*.local
.env.local

# Visual Studio Code
# https://github.com/github/gitignore/blob/master/Global/VisualStudioCode.gitignore
Expand Down
30 changes: 18 additions & 12 deletions .vscode/react.code-snippets
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,34 @@
"scope": "javascriptreact,typescriptreact",
"prefix": "react",
"body": [
"import { ${1:import}, ${1:import}Props } from \"@mui/material\";",
"import * as React from \"react\";",
"import { ${2:Box}, ${2}Props } from \"@mui/joy\";",
"",
"function ${TM_FILENAME_BASE}(props: ${TM_FILENAME_BASE}Props): JSX.Element {",
" const { ...other } = props;",
"export function ${1:${TM_FILENAME_BASE/(^|-)(.)/${2:/upcase}/g}}(props: ${1}Props): JSX.Element {",
" const { sx, ...other } = props;",
"",
" return (",
" <${1:import} {...other}>$0</${1:import}>",
" <${2} sx={{ ...sx }} {...other}>$0</${2}>",
" );",
"}",
"",
"type ${TM_FILENAME_BASE}Props = Omit<${1:import}Props, \"children\">;",
"",
"export { ${TM_FILENAME_BASE} };",
"export type ${1}Props = Omit<${2}Props, \"children\">;",
""
],
"description": "React Component"
},
"Import": {
"ReactRoute": {
"scope": "javascriptreact,typescriptreact",
"prefix": "imp",
"body": ["import { ${0} } from \"${1:@material-ui/icons}\";"],
"description": "Import"
"prefix": "route",
"body": [
"import { ${2:Box}, ${2}Props } from \"@mui/joy\";",
"",
"export const Component = function ${1:${TM_FILENAME_BASE/(^|-)(.)/${2:/upcase}/g}}(): JSX.Element {",
" return (",
" <${2}>$0</${2}>",
" );",
"}",
""
],
"description": "React Route Component"
}
}
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
"typescript.tsdk": ".yarn/sdks/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"vitest.commandLine": "yarn vitest",
"files.associations": {
".env.*.local": "properties",
".env.*": "properties"
},
"files.exclude": {
"**/.cache": true,
"**/.DS_Store": true,
Expand Down Expand Up @@ -44,6 +48,7 @@
"browserslist",
"cloudfunctions",
"corejs",
"corepack",
"endregion",
"entrypoint",
"envalid",
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Be sure to join our [Discord channel](https://discord.com/invite/2nKEnKq) for as

`├──`[`.github`](.github) — GitHub configuration including CI/CD workflows<br>
`├──`[`.vscode`](.vscode) — VSCode settings including code snippets, recommended extensions etc.<br>
`├──`[`app`](./app) — Web application front-end built with [React](https://react.dev/) and [Material UI](https://mui.com/core/)<br>
`├──`[`app`](./app) — Web application front-end built with [React](https://react.dev/) and [Joy UI](https://mui.com/joy-ui/getting-started/)<br>
`├──`[`edge`](./edge) — Cloudflare Workers (CDN) edge endpoint<br>
`├──`[`env`](./env) — Application settings, API keys, etc.<br>
`├──`[`scripts`](./scripts) — Automation scripts such as `yarn deploy`<br>
Expand All @@ -39,7 +39,7 @@ Be sure to join our [Discord channel](https://discord.com/invite/2nKEnKq) for as

## Tech Stack

- [React](https://react.dev/), [React Router](https://reactrouter.com/), [Recoil](https://recoiljs.org/), [Emotion](https://emotion.sh/), [Material UI](https://next.material-ui.com/), [Firebase Authentication](https://firebase.google.com/docs/auth)
- [React](https://react.dev/), [React Router](https://reactrouter.com/), [Jotai](https://jotai.org/), [Emotion](https://emotion.sh/), [Joy UI](https://mui.com/joy-ui/getting-started/), [Firebase Authentication](https://firebase.google.com/docs/auth)
- [Cloudflare Workers](https://workers.cloudflare.com/), [Vite](https://vitejs.dev/), [Vitest](https://vitejs.dev/),
[TypeScript](https://www.typescriptlang.org/), [ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [Yarn](https://yarnpkg.com/) with PnP

Expand All @@ -59,11 +59,12 @@ environment variables found in [`env/*.env`](./env/), and start hacking:
```
$ git clone https://github.com/kriasoft/react-starter-kit.git example
$ cd ./example
$ corepack enable
$ yarn install
$ yarn start
$ yarn workspace app start
```

The app will become available at [http://localhost:5173/](http://localhost:5173/) (press `q` key to exit).
The app will become available at [http://localhost:5173/](http://localhost:5173/) (press `q` + `Enter` to exit).

**IMPORTANT**: Ensure that VSCode is using the workspace [version of TypeScript](https://code.visualstudio.com/docs/typescript/typescript-compiling#_using-newer-typescript-versions)
and ESLint.
Expand Down
7 changes: 2 additions & 5 deletions app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@

## Directory Structure

`├──`[`common`](./common)Common (shared) React components<br>
`├──`[`components`](./components)UI elements<br>
`├──`[`core`](./core) — Core modules, React hooks, customized theme, etc.<br>
`├──`[`dialogs`](./dialogs) — React components implementing modal dialogs<br>
`├──`[`icons`](./icons) — Custom icon React components<br>
`├──`[`layout`](./layout) — Layout related components<br>
`├──`[`public`](./public) — Static assets such as robots.txt, index.html etc.<br>
`├──`[`routes`](./routes) — Application routes and page (screen) components<br>
`├──`[`theme`](./theme) — Customized Material UI theme<br>
`├──`[`global.d.ts`](./global.d.ts) — Global TypeScript declarations<br>
`├──`[`index.html`](./index.html) — HTML page containing application entry point<br>
`├──`[`index.tsx`](./index.tsx) — Single-page application (SPA) entry point<br>
Expand All @@ -35,7 +32,7 @@ $ yarn workspace app start
## References

- https://react.dev/ — React.js documentation
- https://mui.com/core/Material UI library documentation
- https://mui.com/joy-ui/getting-started/Joy UI documentation
- https://www.typescriptlang.org/ — TypeScript reference
- https://vitejs.dev/ — Front-end tooling (bundler)
- https://vitest.dev/ — Unit test framework
19 changes: 0 additions & 19 deletions app/common/Link.tsx

This file was deleted.

72 changes: 72 additions & 0 deletions app/components/button-color-scheme.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/* SPDX-FileCopyrightText: 2014-present Kriasoft */
/* SPDX-License-Identifier: MIT */

import { DarkModeRounded, LightModeRounded } from "@mui/icons-material";
import {
Dropdown,
IconButton,
IconButtonProps,
ListItemContent,
ListItemDecorator,
Menu,
MenuButton,
MenuItem,
useColorScheme,
} from "@mui/joy";
import { memo } from "react";

export function ColorSchemeButton(props: ColorSchemeButtonProps): JSX.Element {
const { mode, systemMode } = useColorScheme();

return (
<Dropdown>
<MenuButton slots={{ root: IconButton }} slotProps={{ root: props }}>
{mode === "light" || (mode === "system" && systemMode === "light") ? (
<DarkModeRounded />
) : (
<LightModeRounded />
)}
</MenuButton>

<Menu size="sm">
<ModeMenuItem mode="light" />
<ModeMenuItem mode="dark" />
<ModeMenuItem mode="system" />
</Menu>
</Dropdown>
);
}

const ModeMenuItem = memo(function ModeMenuItem({
mode,
}: ModeMenuItemProps): JSX.Element {
const scheme = useColorScheme();

return (
<MenuItem
onClick={() => {
scheme.setMode(mode);
}}
selected={scheme.mode === mode}
>
<ListItemDecorator sx={{ ml: 0.5 }}>
{mode === "light" ||
(mode !== "dark" && scheme.systemMode === "light") ? (
<LightModeRounded />
) : (
<DarkModeRounded />
)}
</ListItemDecorator>
<ListItemContent sx={{ pr: 2 }}>
{mode === "light"
? "Light theme"
: mode === "dark"
? "Dark theme"
: "Device default"}
</ListItemContent>
</MenuItem>
);
});

type ColorSchemeButtonProps = Omit<IconButtonProps, "children">;
type ModeMenuItemProps = { mode: "dark" | "light" | "system" };
45 changes: 45 additions & 0 deletions app/components/button-login.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* SPDX-FileCopyrightText: 2014-present Kriasoft */
/* SPDX-License-Identifier: MIT */

import { Button, ButtonProps } from "@mui/joy";
import { SignInMethod, useSignIn } from "../core/auth";
import { AnonymousIcon, GoogleIcon } from "../icons";

export function LoginButton(props: LoginButtonProps): JSX.Element {
const { signInMethod, ...other } = props;
const [signIn, inFlight] = useSignIn(signInMethod);

const icon =
signInMethod === "google.com" ? (
<GoogleIcon />
) : signInMethod === "anonymous" ? (
<AnonymousIcon />
) : null;

return (
<Button
startDecorator={icon}
variant="outlined"
onClick={signIn}
loading={inFlight}
children={
signInMethod === "google.com"
? "Continue via Google"
: signInMethod === "anonymous"
? "Continue as anonymous"
: "unknown"
}
{...other}
/>
);
}

export type LoginButtonProps = Omit<
ButtonProps<
"button",
{
signInMethod: SignInMethod;
}
>,
"children"
>;
57 changes: 57 additions & 0 deletions app/components/button-user-avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* SPDX-FileCopyrightText: 2014-present Kriasoft */
/* SPDX-License-Identifier: MIT */

import { LogoutRounded, SettingsRounded } from "@mui/icons-material";
import {
Avatar,
Dropdown,
IconButton,
IconButtonProps,
ListItemContent,
ListItemDecorator,
Menu,
MenuButton,
MenuItem,
} from "@mui/joy";
import { getAuth, signOut } from "firebase/auth";
import { useCurrentUser } from "../core/auth";

export function UserAvatarButton(props: UserAvatarButtonProps): JSX.Element {
const { sx, ...other } = props;
const user = useCurrentUser()!;

return (
<Dropdown>
<MenuButton
slots={{ root: IconButton }}
slotProps={{
root: {
sx: { borderRadius: "50%", p: "2px", ...sx },
...other,
},
}}
>
<Avatar sx={{ width: 36, height: 36 }} src={user.photoURL ?? undefined}>
{user.displayName}
</Avatar>
</MenuButton>

<Menu size="sm">
<MenuItem>
<ListItemDecorator sx={{ ml: 0.5 }}>
<SettingsRounded />
</ListItemDecorator>
<ListItemContent sx={{ mr: 2 }}>Settings</ListItemContent>
</MenuItem>
<MenuItem onClick={() => signOut(getAuth())}>
<ListItemDecorator sx={{ ml: 0.5 }}>
<LogoutRounded />
</ListItemDecorator>
<ListItemContent sx={{ mr: 2 }}>Logout</ListItemContent>
</MenuItem>
</Menu>
</Dropdown>
);
}

export type UserAvatarButtonProps = Omit<IconButtonProps, "children">;
8 changes: 4 additions & 4 deletions app/layout/RootError.tsx → app/components/error.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* SPDX-FileCopyrightText: 2020-present Kriasoft */
/* SPDX-FileCopyrightText: 2014-present Kriasoft */
/* SPDX-License-Identifier: MIT */

import { Container, Typography } from "@mui/material";
import { Container, Typography } from "@mui/joy";
import { useRouteError } from "react-router-dom";

export function RootError(): JSX.Element {
Expand All @@ -13,10 +13,10 @@ export function RootError(): JSX.Element {
sx={{
fontSize: "2em",
fontWeight: 300,
textAlign: "center",
"& strong": { fontWeight: 400 },
}}
variant="h1"
align="center"
level="h1"
>
<strong>Error {err.status || 500}</strong>:{" "}
{err.statusText ?? err.message}
Expand Down
7 changes: 7 additions & 0 deletions app/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* SPDX-FileCopyrightText: 2014-present Kriasoft */
/* SPDX-License-Identifier: MIT */

export * from "./button-login";
export * from "./error";
export * from "./layout";
export * from "./logo";
Loading

0 comments on commit 310e989

Please sign in to comment.