Big comeback to DaisyUI, removed other stuff

This commit is contained in:
Igor Barcik 2023-11-14 13:53:59 +01:00
parent 82e0b21c06
commit 8ea78084c9
Signed by: biggy
GPG Key ID: EA4CE0D1E2A6DC98
15 changed files with 3165 additions and 103 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en" data-theme="light">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />

View File

@ -10,9 +10,14 @@
"preview": "vite preview --host" "preview": "vite preview --host"
}, },
"dependencies": { "dependencies": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@hookform/resolvers": "^3.3.2", "@hookform/resolvers": "^3.3.2",
"@tailwindcss/forms": "^0.5.7",
"@tanstack/react-table": "^8.10.7", "@tanstack/react-table": "^8.10.7",
"appwrite": "^13.0.0",
"axios": "^1.6.1", "axios": "^1.6.1",
"daisyui": "latest",
"echarts": "^5.4.3", "echarts": "^5.4.3",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
@ -38,7 +43,7 @@
"eslint-plugin-react-refresh": "^0.4.4", "eslint-plugin-react-refresh": "^0.4.4",
"postcss": "^8.4.31", "postcss": "^8.4.31",
"postcss-simple-vars": "^7.0.1", "postcss-simple-vars": "^7.0.1",
"prettier": "^3.0.3", "prettier": "^3.1.0",
"tailwindcss": "^3.3.5", "tailwindcss": "^3.3.5",
"typescript": "^5.2.2", "typescript": "^5.2.2",
"vite": "^4.5.0" "vite": "^4.5.0"

3000
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,7 @@
export default { export default {
plugins: { plugins: {
"postcss-import": {},
"tailwindcss/nesting": {},
tailwindcss: {}, tailwindcss: {},
autoprefixer: {}, autoprefixer: {},
}, },

BIN
public/pattern.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 KiB

View File

@ -0,0 +1,33 @@
import useTheme from "../hooks/useTheme";
import inDev from "../utils/inDebug";
const ThemeButton = () => {
const [isDark, toggleTheme] = useTheme();
return (
<label className="swap swap-rotate btn btn-outline btn-circle">
<input
checked={isDark}
className="hidden"
onChange={() => inDev(() => console.log("Theme changed"))}
onClick={toggleTheme}
type="checkbox"
/>
<svg
className="swap-on fill-current w-6 h-6"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z" />
</svg>
<svg
className="swap-off fill-current w-6 h-6"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z" />
</svg>
</label>
);
};
export default ThemeButton;

View File

@ -1,3 +1,4 @@
// -------- ENVIRONMENT VARIABLES --------
export const viteEnv = import.meta.env; export const viteEnv = import.meta.env;
//Main configuration, static data, mostly used here //Main configuration, static data, mostly used here
export const main = { export const main = {
@ -11,3 +12,7 @@ export const about = {
program_description: `This is a ${main.program_name} for <purpose>.`, program_description: `This is a ${main.program_name} for <purpose>.`,
program_authors: [{ name: "Author Name", email: "email@example.com" }], program_authors: [{ name: "Author Name", email: "email@example.com" }],
}; };
// -------- FRONTEND CONFIGURATION --------
// sizes in pixels
export const topbarSize = 64;
export const sidebarSize = 256;

View File

@ -1,15 +1,19 @@
import ThemeButton from "../components/ThemeButton";
import { main } from "../configure"; import { main } from "../configure";
function App() { function App() {
return ( return (
<> <div className="space-x-4 space-y-6 m-4">
{main.program_name} v{main.program_version} <div className="px-5 mt-6">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc magna massa, <h1 className="text-center ">
ornare quis interdum a, cursus in quam. Quisque risus libero, cursus eget {main.program_name} v{main.program_version}
eros vitae, aliquam placerat velit. Vivamus luctus eros id sagittis luctus. </h1>
Pellentesque felis nulla, rhoncus viverra nunc vitae, viverra aliquam ante. <div>
Ut feugiat mattis tempor. <ThemeButton />
</> </div>
</div>
<hr className="my-12 h-[2px] border-t-0 bg-transparent bg-gradient-to-r from-transparent via-current to-transparent opacity-25 dark:opacity-100" />
</div>
); );
} }

View File

@ -0,0 +1,35 @@
import { useState } from "react";
/**
* Works like useState but stores the value in local storage
* @param key Create a key to store the value in local storage
* @param initialValue Assign an initial value to the key
* @returns [storedValue, setValue] Returns the stored value and a function to set the value
*/
const useLocalStorage = <T>(
key: string,
initialValue: T,
): [T, (value: T) => void] => {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.log(error);
return initialValue;
}
});
const setValue = (value: T) => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.log(error);
}
};
return [storedValue, setValue];
};
export default useLocalStorage;

30
src/hooks/useTheme.ts Normal file
View File

@ -0,0 +1,30 @@
import { useEffect, useState } from "react";
import useLocalStorage from "./useLocalStorage";
/**
* A hook to toggle between light and dark theme and store the value in local storage.
* @returns {isDark, toggleTheme} - Returns a boolean indicating if dark mode is active and a function to toggle the theme.
*/
const useTheme = (): [boolean, () => void] => {
// Initialize theme from local storage or default value
const [theme, setTheme] = useLocalStorage<string>("theme", "light");
// Determine if the current theme is dark
const [isDark, setIsDark] = useState<boolean>(theme === "dark");
const toggleTheme = () => {
const newTheme = theme === "light" ? "dark" : "light";
setTheme(newTheme);
setIsDark(newTheme === "dark");
document.documentElement.dataset.theme = newTheme;
};
// Apply the theme on component mount
useEffect(() => {
document.documentElement.dataset.theme = theme;
}, [theme]); // Re-run effect when theme changes
return [isDark, toggleTheme];
};
export default useTheme;

View File

@ -4,6 +4,7 @@ import { setupAxiosInterceptors } from "./api/AxiosService";
import { viteEnv } from "./configure"; import { viteEnv } from "./configure";
import App from "./features/App"; import App from "./features/App";
import inDev from "./utils/inDebug"; import inDev from "./utils/inDebug";
import { Global, css } from "@emotion/react";
setupAxiosInterceptors(); setupAxiosInterceptors();
inDev(() => console.log(viteEnv)); inDev(() => console.log(viteEnv));
@ -11,6 +12,11 @@ inDev(() => console.log(viteEnv));
// Hook react to the HTML element with id="root" // Hook react to the HTML element with id="root"
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode> <React.StrictMode>
<Global
styles={css`
// You can add more global styles here
`}
/>
<App /> <App />
</React.StrictMode>, </React.StrictMode>,
); );

View File

@ -1,79 +0,0 @@
import {
CustomRouteObject,
RouteObjectAdditionalProps,
flatRoutes,
} from "../configure";
import {
clearMultiplePathSlashes,
trimPathOfParameters,
} from "./StringTransformationUtils";
interface FlatternRoutingTableElement extends RouteObjectAdditionalProps {
path: string;
name: string;
}
//! WARNING: This function will generate error if paths aren't unique, disableInNavbar or disableRedirect to prevent this. It's a useful feature to prevent duplicate path in navbar
/**
* @description Convert a existing routing table as a flat array
*/
export const flatternRoutingTable = (
routes: CustomRouteObject[],
previousPath = "undefined",
): FlatternRoutingTableElement[] => {
const result: FlatternRoutingTableElement[] = [];
routes.forEach((route: CustomRouteObject) => {
if (route.additionalProps.disableRedirect) return; // Skip if disable redirect, children are skipped too
if (
typeof route.path !== "undefined" &&
typeof previousPath === "undefined" &&
!route.additionalProps.disableInNavbar
) {
result.push({
path: trimPathOfParameters(route.path),
name: route.additionalProps.name,
disableBreadcrumbBar: route.additionalProps.disableBreadcrumbBar,
disableInNavbar: route.additionalProps.disableInNavbar,
disableRedirect: route.additionalProps.disableRedirect,
});
}
if (
typeof route.path !== "undefined" &&
typeof previousPath !== "undefined" &&
!route.additionalProps.disableInNavbar
) {
result.push({
path: trimPathOfParameters(
clearMultiplePathSlashes(`/${previousPath}/${route.path}`),
),
name: route.additionalProps.name,
disableBreadcrumbBar: route.additionalProps.disableBreadcrumbBar,
disableInNavbar: route.additionalProps.disableInNavbar,
disableRedirect: route.additionalProps.disableRedirect,
});
}
if (route.children && typeof previousPath === "undefined") {
result.push(...flatternRoutingTable(route.children));
}
if (route.children && typeof previousPath !== "undefined") {
result.push(...flatternRoutingTable(route.children, route.path));
}
// Errors handling
if (typeof route.path === "undefined")
console.error(`Route ${route.additionalProps.name} is missing path`);
});
return result;
};
/**
* @description Function to find element in flattern routes array by LAST path ex: /admin/MAINTENANCE
*/
export const findElementInFlatRoutes = (
path: string,
): FlatternRoutingTableElement | undefined => {
// Split path string into array, split by '/', then get the last element
const _route = flatRoutes.find((route) => {
const pathArray = route.path.split("/");
if (pathArray[pathArray.length - 1] === path) return true;
else return false;
});
return _route;
};

View File

@ -1,15 +1,3 @@
@import url("https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,900&display=swap");
@tailwind base; @tailwind base;
@layer base {
html {
@apply text-neutral-800;
}
html.dark {
@apply text-neutral-50;
@apply bg-neutral-800;
}
}
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;

View File

@ -1,7 +1,40 @@
/** @type {import('tailwindcss').Config} */
export default { export default {
content: ["./src/**/*.{js,ts,jsx,tsx}", "./index.html"], content: ["./src/**/*.{js,ts,jsx,tsx}", "./index.html"],
// safelist is used to allow classes to not be purged by tailwind // safelist is used to allow classes to not be purged by tailwind
safelist: ["alert-info", "alert-success", "alert-warning", "alert-error"], safelist: ["alert-info", "alert-success", "alert-warning", "alert-error"],
plugins: [require("@tailwindcss/typography")], theme: {
extend: {
spacing: {
128: "32rem",
144: "36rem",
},
borderRadius: {
"4xl": "2rem",
},
},
},
darkMode: "class",
plugins: [
require("@tailwindcss/typography"),
require("@tailwindcss/forms"),
require("daisyui"),
],
daisyui: {
themes: [
{
light: {
primary: "#38bdf8",
secondary: "#10b981",
accent: "#f59e0b",
neutral: "#e5e7eb",
"base-100": "#f3f4f6",
info: "#67e8f9",
success: "#6ee7b7",
warning: "#fde047",
error: "#f87171",
},
},
"dark",
],
},
}; };