mirror of
https://github.com/Sonny93/my-links.git
synced 2025-12-11 08:43:04 +00:00
chore: init unocss
This commit is contained in:
138
inertia/components/common/theme_toggle.tsx
Normal file
138
inertia/components/common/theme_toggle.tsx
Normal file
@@ -0,0 +1,138 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
type Theme = 'light' | 'dark' | 'system';
|
||||
|
||||
export function ThemeToggle() {
|
||||
const [theme, setTheme] = useState<Theme>('system');
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
|
||||
const savedTheme = localStorage.getItem('theme') as Theme;
|
||||
if (savedTheme) {
|
||||
setTheme(savedTheme);
|
||||
applyTheme(savedTheme);
|
||||
} else {
|
||||
setTheme('system');
|
||||
applyTheme('system');
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
|
||||
const handleSystemThemeChange = () => {
|
||||
if (theme === 'system') {
|
||||
applyTheme('system');
|
||||
}
|
||||
};
|
||||
|
||||
mediaQuery.addEventListener('change', handleSystemThemeChange);
|
||||
|
||||
return () => {
|
||||
mediaQuery.removeEventListener('change', handleSystemThemeChange);
|
||||
};
|
||||
}, [theme]);
|
||||
|
||||
const applyTheme = (newTheme: Theme) => {
|
||||
const root = document.documentElement;
|
||||
|
||||
if (newTheme === 'system') {
|
||||
const systemPrefersDark = window.matchMedia(
|
||||
'(prefers-color-scheme: dark)'
|
||||
).matches;
|
||||
if (systemPrefersDark) {
|
||||
root.classList.add('dark');
|
||||
} else {
|
||||
root.classList.remove('dark');
|
||||
}
|
||||
} else if (newTheme === 'dark') {
|
||||
root.classList.add('dark');
|
||||
} else {
|
||||
root.classList.remove('dark');
|
||||
}
|
||||
};
|
||||
|
||||
const toggleTheme = () => {
|
||||
let newTheme: Theme;
|
||||
|
||||
if (theme === 'light') {
|
||||
newTheme = 'dark';
|
||||
} else if (theme === 'dark') {
|
||||
newTheme = 'system';
|
||||
} else {
|
||||
newTheme = 'light';
|
||||
}
|
||||
|
||||
setTheme(newTheme);
|
||||
applyTheme(newTheme);
|
||||
localStorage.setItem('theme', newTheme);
|
||||
};
|
||||
|
||||
if (!mounted) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const getIcon = () => {
|
||||
if (theme === 'light') {
|
||||
return (
|
||||
<svg
|
||||
className="w-5 h-5 text-yellow-500"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
} else if (theme === 'dark') {
|
||||
return (
|
||||
<svg
|
||||
className="w-5 h-5 text-gray-700 dark:text-yellow-500"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<svg
|
||||
className="w-5 h-5 text-blue-500"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={toggleTheme}
|
||||
className="fixed top-4 right-4 p-3 rounded-full bg-white dark:bg-gray-800 shadow-lg hover:shadow-xl transition-all duration-200 border border-gray-200 dark:border-gray-700"
|
||||
aria-label={`Thème actuel: ${theme}`}
|
||||
>
|
||||
{getIcon()}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -15,8 +15,9 @@ import { TuyauProvider } from '@tuyau/inertia/react';
|
||||
import dayjs from 'dayjs';
|
||||
import { ReactNode, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import 'virtual:uno.css';
|
||||
import '~/css/app.css';
|
||||
import { useAppUrl } from '~/hooks/use_app_url';
|
||||
import '../styles/index.css';
|
||||
|
||||
const TRANSITION_IN_CLASS = '__transition_fadeIn';
|
||||
const TRANSITION_OUT_CLASS = '__transition_fadeOut';
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { BaseLayout } from '~/layouts/_base_layout';
|
||||
import { BaseLayout } from '~/layouts/base_layout';
|
||||
|
||||
const LayoutWrapper = ({ children }: PropsWithChildren) => (
|
||||
<BaseLayout>{children}</BaseLayout>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Box, rem } from '@mantine/core';
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { FloatingNavbar } from '~/components/common/floating_navbar/floating_navbar';
|
||||
import { Footer } from '~/components/common/footer/footer';
|
||||
import { BaseLayout } from './_base_layout';
|
||||
import { BaseLayout } from './base_layout';
|
||||
|
||||
const DefaultLayout = ({ children }: PropsWithChildren) => (
|
||||
<BaseLayout>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Anchor, Button, Container, Group, rem, Title } from '@mantine/core';
|
||||
import { FormEvent, PropsWithChildren } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import i18n from '~/i18n';
|
||||
import { BaseLayout } from '~/layouts/_base_layout';
|
||||
import { BaseLayout } from '~/layouts/base_layout';
|
||||
import { appendCollectionId } from '~/lib/navigation';
|
||||
|
||||
export interface FormLayoutProps extends PropsWithChildren {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Box, rem } from '@mantine/core';
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { FloatingNavbar } from '~/components/common/floating_navbar/floating_navbar';
|
||||
import { Footer } from '~/components/common/footer/footer';
|
||||
import { BaseLayout } from './_base_layout';
|
||||
import { BaseLayout } from './base_layout';
|
||||
|
||||
const SmallContentLayout = ({ children }: PropsWithChildren) => (
|
||||
<BaseLayout>
|
||||
|
||||
@@ -54,6 +54,8 @@
|
||||
"@types/react": "^19.2.2",
|
||||
"@types/react-dom": "^19.2.2",
|
||||
"@typescript-eslint/eslint-plugin": "^8.46.3",
|
||||
"@unocss/preset-icons": "^66.5.10",
|
||||
"@unocss/preset-web-fonts": "^66.5.10",
|
||||
"@vite-pwa/assets-generator": "^1.0.2",
|
||||
"eslint": "^9.39.1",
|
||||
"hot-hook": "^0.4.0",
|
||||
@@ -67,6 +69,7 @@
|
||||
"release-it": "^19.0.6",
|
||||
"ts-node-maintained": "^10.9.6",
|
||||
"typescript": "~5.9.3",
|
||||
"unocss": "^66.5.10",
|
||||
"vite": "^7.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
1940
pnpm-lock.yaml
generated
1940
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -2,14 +2,6 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta
|
||||
name='theme-color'
|
||||
content='#f0eef6'
|
||||
/>
|
||||
<link
|
||||
href='https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&family=Rubik:ital,wght@0,400;0,700;1,400;1,700&display=swap'
|
||||
rel='stylesheet'
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta charSet='UTF-8' />
|
||||
<link
|
||||
@@ -26,6 +18,7 @@
|
||||
href='/favicon.png'
|
||||
/>
|
||||
<title inertia>MyLinks</title>
|
||||
|
||||
@if(nodeEnv === 'production')
|
||||
<link
|
||||
rel='manifest'
|
||||
@@ -40,9 +33,35 @@
|
||||
</script>
|
||||
@endif
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const savedTheme = localStorage.getItem('theme');
|
||||
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
let theme = 'system';
|
||||
|
||||
if (savedTheme) {
|
||||
theme = savedTheme;
|
||||
}
|
||||
|
||||
if (theme === 'system') {
|
||||
if (systemPrefersDark) {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
} else if (theme === 'dark') {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
@routes()
|
||||
@inertiaHead()
|
||||
@stack('dumper')
|
||||
@viteReactRefresh()
|
||||
@inertiaHead()
|
||||
@vite(['inertia/app/app.tsx', `inertia/pages/${page.component}.tsx`])
|
||||
</head>
|
||||
|
||||
|
||||
17
uno.config.ts
Normal file
17
uno.config.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import presetIcons from '@unocss/preset-icons';
|
||||
import presetWebFonts from '@unocss/preset-web-fonts';
|
||||
import { defineConfig, presetWind4 } from 'unocss';
|
||||
|
||||
export default defineConfig({
|
||||
presets: [
|
||||
presetWind4({
|
||||
dark: 'class',
|
||||
}),
|
||||
presetIcons({
|
||||
cdn: 'https://esm.sh/',
|
||||
}),
|
||||
presetWebFonts({
|
||||
provider: 'bunny',
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -7,11 +7,13 @@ import { getDirname } from '@adonisjs/core/helpers';
|
||||
import inertia from '@adonisjs/inertia/client';
|
||||
import adonisjs from '@adonisjs/vite/client';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import UnoCSS from 'unocss/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
import { VitePWA } from 'vite-plugin-pwa';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
UnoCSS(),
|
||||
VitePWA({
|
||||
registerType: 'autoUpdate',
|
||||
injectRegister: false,
|
||||
|
||||
Reference in New Issue
Block a user