Skip to content

Commit 7f975cf

Browse files
committed
Bootstrap react@18 - Initial commit
0 parents  commit 7f975cf

24 files changed

+5639
-0
lines changed

.eslintrc.js

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
module.exports = {
2+
parser: "@typescript-eslint/parser",
3+
plugins: [
4+
"@typescript-eslint",
5+
"react",
6+
"unicorn",
7+
"prettier",
8+
],
9+
extends: [
10+
"eslint:recommended",
11+
"plugin:@typescript-eslint/eslint-recommended",
12+
"plugin:@typescript-eslint/recommended",
13+
"plugin:react/recommended",
14+
"plugin:unicorn/recommended",
15+
"plugin:prettier/recommended"
16+
],
17+
parserOptions: {
18+
project: "./tsconfig.json",
19+
tsconfigRootDir: __dirname,
20+
},
21+
ignorePatterns: [
22+
],
23+
settings: {
24+
react: {
25+
version: "detect",
26+
},
27+
},
28+
rules: {
29+
"prettier/prettier": [
30+
"error",
31+
{
32+
printWidth: 80,
33+
semi: true,
34+
singleQuote: true,
35+
quoteProps: 'as-needed',
36+
tabWidth: 2,
37+
jsxSingleQuote: false,
38+
trailingComma: 'all',
39+
bracketSpacing: true,
40+
bracketSameLine: false,
41+
arrowParens: 'always',
42+
htmlWhitespaceSensitivity: 'css',
43+
endOfLine: 'lf',
44+
embeddedLanguageFormatting: 'auto',
45+
useTabs: false,
46+
}
47+
],
48+
},
49+
};

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules/
2+
dist/

package.json

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"name": "bootstrap",
3+
"version": "0.0.1",
4+
"private": true,
5+
"contributors": [
6+
"Jean Grizet <[email protected]>"
7+
],
8+
"scripts": {
9+
"build": "webpack build --config-name build",
10+
"lint": "eslint ./src/",
11+
"lint:fix": "yarn lint --fix",
12+
"lint:i18": "node tools/i18next/main.js -u -m --config tools/i18next/i18next-scanner.config.js",
13+
"start": "webpack serve --config-name dev"
14+
},
15+
"dependencies": {
16+
"@babel/core": "^7.17.9",
17+
"@date-io/date-fns": "^2.13.1",
18+
"@emotion/react": "^11.8.1",
19+
"@emotion/styled": "^11.8.1",
20+
"@mui/icons-material": "^5.6.2",
21+
"@mui/material": "^5.6.2",
22+
"@mui/system": "^5.6.2",
23+
"@mui/x-date-pickers": "^5.0.0-alpha.1",
24+
"date-fns": "^2.28.0",
25+
"i18next": "^21.6.12",
26+
"react": "^18.0.0",
27+
"react-dom": "^18.0.0",
28+
"react-i18next": "^11.16.7",
29+
"react-router-dom": "^6.3.0"
30+
},
31+
"devDependencies": {
32+
"@types/react": "18.0.5",
33+
"@types/react-dom": "^18.0.1",
34+
"@typescript-eslint/eslint-plugin": "^5.20.0",
35+
"@typescript-eslint/parser": "^5.20.0",
36+
"argparse": "^2.0.1",
37+
"copy-webpack-plugin": "^10.2.4",
38+
"eslint": "^8.13.0",
39+
"eslint-config-prettier": "^8.5.0",
40+
"eslint-plugin-prettier": "^4.0.0",
41+
"eslint-plugin-react": "^7.29.2",
42+
"eslint-plugin-unicorn": "^42.0.0",
43+
"eslint-webpack-plugin": "^3.1.1",
44+
"html-webpack-plugin": "^5.5.0",
45+
"i18next-scanner": "^3.0.1",
46+
"prettier": "^2.6.2",
47+
"ts-loader": "9.2.8",
48+
"typescript": "4.6.3",
49+
"webpack": "^5.72.0",
50+
"webpack-cli": "^4.9.2",
51+
"webpack-dev-server": "^4.8.1"
52+
}
53+
}

src/app.tsx

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React, { Suspense } from 'react';
2+
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
3+
import { LocalizationProvider } from '@mui/x-date-pickers';
4+
import MuiThemeProvider from './mui-theme-provider';
5+
import { BrowserRouter, Route, Routes } from 'react-router-dom';
6+
7+
const RootPage = React.lazy(() => import('./pages/root-page'));
8+
const HomePage = React.lazy(() => import('./pages/home-page'));
9+
const NotFoundPage = React.lazy(() => import('./pages/not-found-page'));
10+
const ExamplePage = React.lazy(() => import('./pages/example-page'));
11+
12+
function App(): JSX.Element {
13+
return (
14+
<MuiThemeProvider>
15+
<Suspense fallback={<div></div>}>
16+
<LocalizationProvider dateAdapter={AdapterDateFns}>
17+
<BrowserRouter>
18+
<Routes>
19+
<Route path="/" element={<RootPage />}>
20+
<Route index element={<HomePage />} />
21+
<Route path="example" element={<ExamplePage />} />
22+
<Route path="*" element={<NotFoundPage />} />
23+
</Route>
24+
</Routes>
25+
</BrowserRouter>
26+
</LocalizationProvider>
27+
</Suspense>
28+
</MuiThemeProvider>
29+
);
30+
}
31+
32+
export default App;

src/i18n.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import i18next, { ReadCallback } from 'i18next';
2+
import { initReactI18next } from 'react-i18next';
3+
4+
i18next
5+
.use(initReactI18next)
6+
.use({
7+
type: 'backend',
8+
read: (language: string, namespace: string, callback: ReadCallback) => {
9+
import(`./locales/${language}_${namespace}.json`)
10+
.then((resources) => {
11+
callback(undefined, resources);
12+
})
13+
.catch((error) => {
14+
callback(error, false);
15+
});
16+
},
17+
})
18+
.init({
19+
fallbackLng: ['fr', 'en'],
20+
});

src/index.html

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<html lang="fr">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
6+
<meta name="description" content="Bootstrap template">
7+
<title>Bootstrap</title>
8+
<link rel="shortcut icon" href="static/favicon.ico" type="image/vnd.microsoft.icon">
9+
</head>
10+
<body>
11+
<div id="root" />
12+
</body>
13+
</html>

src/index.tsx

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import React from 'react';
2+
import { createRoot } from 'react-dom/client';
3+
4+
import App from './app';
5+
import './i18n';
6+
7+
const container = document.querySelector('#root');
8+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
9+
const root = createRoot(container!);
10+
root.render(<App />);

src/locales/en_translation.json

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"pages": {
3+
"home": {
4+
"text": "Bootstrap home page"
5+
},
6+
"404": {
7+
"text": "404 - Page not found"
8+
},
9+
"example": {
10+
"text": "Example page"
11+
}
12+
}
13+
}

src/locales/fr_translation.json

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"pages": {
3+
"home": {
4+
"text": "Amorce page d'accueil"
5+
},
6+
"404": {
7+
"text": "404 - Cette page n'existe pas"
8+
},
9+
"example": {
10+
"text": "Page d'exemple"
11+
}
12+
}
13+
}

src/mui-theme-provider.tsx

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import React, {
2+
ReactNode,
3+
ReactElement,
4+
useMemo,
5+
useState,
6+
useContext,
7+
useEffect,
8+
} from 'react';
9+
import CssBaseline from '@mui/material/CssBaseline';
10+
import {
11+
createTheme,
12+
ThemeProvider,
13+
StyledEngineProvider,
14+
useMediaQuery,
15+
PaletteMode,
16+
Theme,
17+
} from '@mui/material';
18+
19+
const baseTheme = {
20+
typography: {
21+
fontFamily: ['Montserrat', 'sans-serif'].join(','),
22+
fontSize: 12,
23+
fontWeightLight: 400,
24+
fontWeightRegular: 500,
25+
fontWeightMedium: 550,
26+
fontWeightBold: 600,
27+
},
28+
};
29+
30+
const lightPalette = {
31+
mode: 'light' as PaletteMode,
32+
};
33+
34+
const darkPalette = {
35+
mode: 'dark' as PaletteMode,
36+
};
37+
38+
type MuiThemeProviderProperties = {
39+
children: ReactNode;
40+
};
41+
42+
type MuiThemeContextProperties = {
43+
theme?: Theme;
44+
toggleTheme: () => void;
45+
};
46+
47+
export const MuiThemeContext = React.createContext<MuiThemeContextProperties>({
48+
theme: undefined,
49+
// eslint-disable-next-line @typescript-eslint/no-empty-function
50+
toggleTheme: () => {},
51+
});
52+
53+
export const useMuiThemeContext = () => useContext(MuiThemeContext);
54+
55+
function MuiThemeProvider({
56+
children,
57+
}: MuiThemeProviderProperties): ReactElement {
58+
const localStorageModeKey = 'theme.palette.mode';
59+
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
60+
const localMode = window.localStorage.getItem(localStorageModeKey);
61+
const [darkMode, setDarkMode] = useState<boolean>(
62+
localMode ? localMode === 'dark' : prefersDarkMode,
63+
);
64+
65+
function toggleTheme() {
66+
window.localStorage.setItem(
67+
localStorageModeKey,
68+
darkMode ? 'light' : 'dark',
69+
);
70+
setDarkMode(!darkMode);
71+
}
72+
73+
const theme: Theme = useMemo(
74+
() =>
75+
createTheme({
76+
...baseTheme,
77+
palette: darkMode ? darkPalette : lightPalette,
78+
}),
79+
[darkMode],
80+
);
81+
82+
useEffect(() => {
83+
if (darkMode) {
84+
document.querySelector('body')?.classList.add('dark-theme');
85+
} else {
86+
document.querySelector('body')?.classList.remove('dark-theme');
87+
}
88+
}, [darkMode]);
89+
90+
return (
91+
<MuiThemeContext.Provider value={{ theme, toggleTheme }}>
92+
<StyledEngineProvider injectFirst>
93+
<ThemeProvider theme={theme}>
94+
<CssBaseline />
95+
{children}
96+
</ThemeProvider>
97+
</StyledEngineProvider>
98+
</MuiThemeContext.Provider>
99+
);
100+
}
101+
102+
export default MuiThemeProvider;

src/pages/example-page.tsx

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { TextField } from '@mui/material';
2+
import { DateTimePicker } from '@mui/x-date-pickers';
3+
import React, { useState } from 'react';
4+
import { useTranslation } from 'react-i18next';
5+
6+
function ExamplePage(): JSX.Element {
7+
const { t } = useTranslation();
8+
const [value, setValue] = useState<Date | null>(new Date());
9+
10+
const handleChange = (newValue: Date | null) => {
11+
setValue(newValue);
12+
};
13+
14+
return (
15+
<div>
16+
<p>{t('pages.example.text')}</p>
17+
<DateTimePicker
18+
value={value}
19+
onChange={handleChange}
20+
renderInput={(parameters) => <TextField {...parameters} />}
21+
/>
22+
</div>
23+
);
24+
}
25+
26+
export default ExamplePage;

src/pages/home-page.tsx

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from 'react';
2+
import { useTranslation } from 'react-i18next';
3+
4+
function HomePage(): JSX.Element {
5+
const { t } = useTranslation();
6+
7+
return (
8+
<div>
9+
<p>{t('pages.home.text')}</p>
10+
</div>
11+
);
12+
}
13+
14+
export default HomePage;

src/pages/not-found-page.tsx

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from 'react';
2+
import { useTranslation } from 'react-i18next';
3+
4+
function HomePage(): JSX.Element {
5+
const { t } = useTranslation();
6+
7+
return (
8+
<div>
9+
<p>{t('pages.404.text')}</p>
10+
</div>
11+
);
12+
}
13+
14+
export default HomePage;

0 commit comments

Comments
 (0)