Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(catalogue): dark mode #117

Merged
merged 3 commits into from
Aug 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions client/src/app/App.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.dark {
background-color: #121212;
}
4 changes: 4 additions & 0 deletions client/src/app/App.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
box-sizing: border-box;
}

.dark {
background-color: #121212;
}

body {
margin: 0;
}
Expand Down
16 changes: 11 additions & 5 deletions client/src/app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react";
import "./App.scss";
import styles from "./App.module.scss";
import {
createBrowserRouter,
createRoutesFromElements,
Expand Down Expand Up @@ -45,6 +46,7 @@ import {
action as registrationAction,
} from "pages/registration";
import { FavoritePage, loader as favoriteLoader } from "pages/favorite";
import { useDarkMode } from "shared/dark-mode/use-dark-mode";

const routes = createRoutesFromElements(
<Route>
Expand Down Expand Up @@ -85,10 +87,14 @@ const routes = createRoutesFromElements(

const router = createBrowserRouter(routes);

const App = () => (
<div id="app">
<RouterProvider router={router} />
</div>
);
const App = () => {
const { darkMode } = useDarkMode();

return (
<div id="app" className={`${darkMode && styles.dark}`}>
<RouterProvider router={router} />
</div>
);
};

export default App;
16 changes: 7 additions & 9 deletions client/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import React from "react";
import ReactDOM from "react-dom/client";
import reportWebVitals from "./reportWebVitals";
import App from "./app/App";
import { ThemeProvider } from "shared/dark-mode/use-dark-mode";

const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement,
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>,
<ThemeProvider>
<React.StrictMode>
<App />
</React.StrictMode>
,
</ThemeProvider>,
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
17 changes: 15 additions & 2 deletions client/src/pages/catalogue/ui/Catalogue.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ import { GetInstrumentsByCriteriaPaginatedApi } from "generated/api";
import { CatalogueLoader } from "pages/catalogue";
import { NavigationBarWidget } from "widgets/catalogue-navbar";
import { SearchBarInputField } from "pages/catalogue/ui/SearchBarInput.field";
import { useDarkMode } from "shared/dark-mode/use-dark-mode";

const getInstrumentsByCriteriaPaginated =
new GetInstrumentsByCriteriaPaginatedApi();

export function CataloguePage() {
const { darkMode } = useDarkMode();

const loader = useLoaderData() as CatalogueLoader; // https://github.com/remix-run/react-router/discussions/9792

const [instruments, setInstruments] = useState<InstrumentDetail[]>(
Expand Down Expand Up @@ -57,11 +60,21 @@ export function CataloguePage() {
<>
<HeaderWidget />

<div className={styles.catalogue__wrapper}>
<div
className={`
${styles.catalogue__wrapper}
${darkMode ? styles.catalogue__wrapper__dark : styles.catalogue__wrapper__light}
`}
>
<SearchBarInputField filters={filters} setFilters={setFilters} />

<div className={styles.catalogue__filters__serp__navbar__wrapper}>
<div className={styles.catalogue__filters__wrapper}>
<div
className={`
${styles.catalogue__filters__wrapper}
${darkMode ? styles.catalogue__filters__wrapper__dark : styles.catalogue__filters__wrapper__light}
`}
>
<SidebarFilterWidget onFilterChange={setFilters} />
</div>

Expand Down
10 changes: 8 additions & 2 deletions client/src/pages/catalogue/ui/SearchBarInput.field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@ import React from "react";
import styles from "./styles/SearchBarInput.field.module.scss";
import { InstrumentName } from "generated/model";
import { Filters } from "widgets/catalogue-filter";
import { useDarkMode } from "shared/dark-mode/use-dark-mode";

interface Props {
filters: Filters;
setFilters: (filter: Filters) => void;
}

export const SearchBarInputField = (props: Props) => {
const { darkMode } = useDarkMode();

return (
<input
type="text"
placeholder={"Search by instrument name"}
placeholder={"Search by instrument name..."}
onChange={(e) => {
props.setFilters({
...props.filters,
Expand All @@ -21,7 +24,10 @@ export const SearchBarInputField = (props: Props) => {
} as InstrumentName,
});
}}
className={styles.search_bar__input}
className={`
${styles.search_bar__input}
${darkMode ? styles.dark : styles.light}
`}
/>
);
};
13 changes: 13 additions & 0 deletions client/src/pages/catalogue/ui/styles/Catalogue.page.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,28 @@
color: #212529;
}

.catalogue__wrapper__dark {
background-color: #000000;
}

.catalogue__wrapper__light {
}

.catalogue__filters__serp__navbar__wrapper {
display: flex;
}

.catalogue__filters__wrapper {
flex: 1;
}
.catalogue__filters__wrapper__light {
background-color: #e0e0e0;
}

.catalogue__filters__wrapper__dark {
background-color: #212529;
}

.catalogue__serp__navbar__wrapper {
flex: 3;
display: flex;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@
border: 1px #dee2e6 solid;
}

.light {
background-color: #ffffff;
}

.dark {
background-color: #333333;
color: #ffd700;

&::placeholder {
color: #ffd700;
}
}

@media (max-width: vars.$media_threshold) {
.search_bar__input {
font-size: 1.5em;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@
flex-basis: 70%;
}

.edit_instrument__button {
.btn__light {
flex: 1;
cursor: pointer;
background-color: #28a745;
color: white;
border: 1px #cccccc solid;
}

.edit_instrument__button:hover {
.btn__light:hover {
background-color: #007bff;
}
5 changes: 5 additions & 0 deletions client/src/pages/profile/ui/Profile.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import Jwt from "domain/model/jwt";
import { LOGIN } from "shared/config/paths";
import { deleteCookie } from "shared/cookie/cookie";
import { COOKIE_JWT_KEY, COOKIE_SESSIONID } from "shared/config/frontend";
import { useDarkMode } from "shared/dark-mode/use-dark-mode";

const logout = new LogoutApi();

export function ProfilePage() {
useJwt();
const { toggleTheme } = useDarkMode();
const navigate = useNavigate();
const profile = useLoaderData() as ProfileDetails;

Expand Down Expand Up @@ -52,6 +54,9 @@ export function ProfilePage() {
<div>
<b>Role</b>: <span>{profile?.role}</span>
</div>
<button onClick={toggleTheme} className={styles.dark_mode__button}>
Dark Mode
</button>
<button onClick={onLogoutHandler} className={styles.logout__button}>
Logout
</button>
Expand Down
4 changes: 4 additions & 0 deletions client/src/pages/profile/ui/styles/Profile.page.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
margin: 2em auto;
}

.dark_mode__button {
background-color: darkgray;
}

.logout__button {
background-color: #ff0000;
}
2 changes: 2 additions & 0 deletions client/src/shared/config/frontend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ export const MINIMAL_PASSWORD_LENGTH = 2;

export const COOKIE_SESSIONID = "SESSIONID";
export const COOKIE_JWT_KEY = "jwt";

export const DARK_MODE = "muse_dark_mode";
76 changes: 76 additions & 0 deletions client/src/shared/dark-mode/use-dark-mode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import {
createContext,
useContext,
useState,
ReactNode,
FC,
useEffect,
} from "react";
import { DARK_MODE } from "shared/config/frontend";

interface ThemeContextProps {
darkMode: boolean;
toggleTheme: () => void;
}

const ThemeContext = createContext<ThemeContextProps | undefined>(undefined);

export const ThemeProvider: FC<{ children: ReactNode }> = ({ children }) => {
const [darkMode, setDarkMode] = useState<boolean>(
window.localStorage.getItem(DARK_MODE) === "true",
);

useEffect(() => {
if (darkMode) {
document.documentElement.classList.add("dark");
} else {
document.documentElement.classList.remove("dark");
}
}, [darkMode]);

const toggleTheme = () => {
setDarkMode((prevMode) => {
const newMode = !prevMode;
window.localStorage.setItem(DARK_MODE, newMode ? "true" : "false");
document.documentElement.classList.toggle("dark", newMode);
return newMode;
});
};

return (
<ThemeContext.Provider value={{ darkMode, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};

export const useDarkMode = (): ThemeContextProps => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error("useDarkMode must be used within a ThemeProvider");
}
return context;
};

// export const useDarkMode = () => {
// const [darkMode, setDarkMode] = useState<boolean>(
// window.localStorage.getItem(DARK_MODE) == "true" ? true : false,
// );
//
// const toggleDarkMode = () => {
// setDarkMode((prevMode) => {
// const newMode = !prevMode;
// window.localStorage.setItem(DARK_MODE, newMode ? "true" : "false");
// document.documentElement.classList.toggle("dark", newMode);
// window.location.reload(); // need to rerender header and other components
// return newMode;
// });
// };
//
// useEffect(() => {
// window.localStorage.setItem(DARK_MODE, darkMode ? "true" : "false");
// document.documentElement.classList.toggle("dark", darkMode);
// }, [darkMode]);
//
// return { darkMode, toggleDarkMode };
// };
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@ import styles from "./styles/EditInstrument.button.module.css";
import actionBtnStyle from "./styles/Action.button.module.css";
import { useNavigate } from "react-router-dom";
import { InstrumentDetail } from "generated/model";
import { useDarkMode } from "shared/dark-mode/use-dark-mode";

interface Props {
instrument: InstrumentDetail;
}

export const EditInstrumentButton = (props: Props) => {
const { darkMode } = useDarkMode();
const navigate = useNavigate();

return (
<button
className={`
${actionBtnStyle.action__button}
${styles.edit_instrument__button}
${darkMode ? styles.btn__dark : styles.btn__light}
`}
/* eslint-disable */
onClick={(_) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import styles from "./styles/RemoveInstrument.button.module.css";
import actionBtnStyle from "./styles/Action.button.module.css";
import { InstrumentDetail } from "generated/model";
import { RemoveFavoriteApi } from "generated/api/remove-favorite-api";
import { useDarkMode } from "shared/dark-mode/use-dark-mode";

interface Props {
instrument: InstrumentDetail;
Expand All @@ -13,6 +14,8 @@ interface Props {
const removeFavoriteApi = new RemoveFavoriteApi();

export const RemoveInstrumentButton = (props: Props) => {
const { darkMode } = useDarkMode();

const handleOnDeleteInstrument = () => {
const removeFavorite = async () => {
const response = await removeFavoriteApi.removeFavorite(
Expand All @@ -35,11 +38,12 @@ export const RemoveInstrumentButton = (props: Props) => {

return (
<button
onClick={handleOnDeleteInstrument}
className={`
${actionBtnStyle.action__button}
${styles.remove_instrument__button}
${styles.btn}
${darkMode && styles.btn__dark}
`}
onClick={handleOnDeleteInstrument}
>
Remove
</button>
Expand Down
Loading
Loading