Fonts & theme toggle component
This commit is contained in:
@ -6,7 +6,7 @@
|
|||||||
# https://redwoodjs.com/docs/app-configuration-redwood-toml
|
# https://redwoodjs.com/docs/app-configuration-redwood-toml
|
||||||
|
|
||||||
[web]
|
[web]
|
||||||
title = "Redwood App"
|
title = "Ahmed Al-Taiar"
|
||||||
port = 8910
|
port = 8910
|
||||||
apiUrl = "/.redwood/functions" # You can customize graphql and dbauth urls individually too: see https://redwoodjs.com/docs/app-configuration-redwood-toml#api-paths
|
apiUrl = "/.redwood/functions" # You can customize graphql and dbauth urls individually too: see https://redwoodjs.com/docs/app-configuration-redwood-toml#api-paths
|
||||||
includeEnvironmentVariables = [
|
includeEnvironmentVariables = [
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
export const content = ['src/**/*.{js,jsx,ts,tsx}']
|
export const content = ['src/**/*.{js,jsx,ts,tsx}']
|
||||||
|
|
||||||
export const theme = {
|
export const theme = {
|
||||||
extend: {},
|
extend: {
|
||||||
|
fontFamily: {
|
||||||
|
syne: ['Syne', 'sans-serif'],
|
||||||
|
inter: ['Inter', 'sans-serif'],
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const plugins = [require('daisyui')]
|
export const plugins = [require('daisyui')]
|
||||||
|
|
||||||
export const daisyui = { themes: ['light', 'dark'] }
|
export const daisyui = { themes: ['light', 'dark'] }
|
||||||
|
@ -12,6 +12,7 @@ import { Router, Route } from '@redwoodjs/router'
|
|||||||
const Routes = () => {
|
const Routes = () => {
|
||||||
return (
|
return (
|
||||||
<Router>
|
<Router>
|
||||||
|
<Route path="/" page={HomePage} name="home" />
|
||||||
<Route notfound page={NotFoundPage} />
|
<Route notfound page={NotFoundPage} />
|
||||||
</Router>
|
</Router>
|
||||||
)
|
)
|
||||||
|
26
web/src/components/ThemeToggle/ThemeToggle.stories.tsx
Normal file
26
web/src/components/ThemeToggle/ThemeToggle.stories.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Pass props to your component by passing an `args` object to your story
|
||||||
|
//
|
||||||
|
// ```tsx
|
||||||
|
// export const Primary: Story = {
|
||||||
|
// args: {
|
||||||
|
// propName: propValue
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// See https://storybook.js.org/docs/react/writing-stories/args.
|
||||||
|
|
||||||
|
import type { Meta, StoryObj } from '@storybook/react'
|
||||||
|
|
||||||
|
import ThemeToggle from './ThemeToggle'
|
||||||
|
|
||||||
|
const meta: Meta<typeof ThemeToggle> = {
|
||||||
|
component: ThemeToggle,
|
||||||
|
tags: ['autodocs'],
|
||||||
|
}
|
||||||
|
|
||||||
|
export default meta
|
||||||
|
|
||||||
|
type Story = StoryObj<typeof ThemeToggle>
|
||||||
|
|
||||||
|
export const Primary: Story = {}
|
14
web/src/components/ThemeToggle/ThemeToggle.test.tsx
Normal file
14
web/src/components/ThemeToggle/ThemeToggle.test.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { render } from '@redwoodjs/testing/web'
|
||||||
|
|
||||||
|
import ThemeToggle from './ThemeToggle'
|
||||||
|
|
||||||
|
// Improve this test with help from the Redwood Testing Doc:
|
||||||
|
// https://redwoodjs.com/docs/testing#testing-components
|
||||||
|
|
||||||
|
describe('ThemeToggle', () => {
|
||||||
|
it('renders successfully', () => {
|
||||||
|
expect(() => {
|
||||||
|
render(<ThemeToggle />)
|
||||||
|
}).not.toThrow()
|
||||||
|
})
|
||||||
|
})
|
47
web/src/components/ThemeToggle/ThemeToggle.tsx
Normal file
47
web/src/components/ThemeToggle/ThemeToggle.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
|
||||||
|
import { mdiWeatherSunny, mdiWeatherNight } from '@mdi/js'
|
||||||
|
import Icon from '@mdi/react'
|
||||||
|
|
||||||
|
const ThemeToggle = () => {
|
||||||
|
const [theme, setTheme] = useState(
|
||||||
|
localStorage.getItem('theme') ? localStorage.getItem('theme') : 'light'
|
||||||
|
)
|
||||||
|
|
||||||
|
const handleToggle = (e) => {
|
||||||
|
if (e.target.checked) {
|
||||||
|
setTheme('dark')
|
||||||
|
} else {
|
||||||
|
setTheme('light')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
localStorage.setItem('theme', theme)
|
||||||
|
|
||||||
|
document
|
||||||
|
.querySelector('html')
|
||||||
|
.setAttribute('data-theme', localStorage.getItem('theme'))
|
||||||
|
}, [theme])
|
||||||
|
|
||||||
|
return (
|
||||||
|
// eslint-disable-next-line jsx-a11y/label-has-associated-control
|
||||||
|
<label className="btn btn-ghost swap swap-rotate w-12">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="theme-controller"
|
||||||
|
checked={theme === 'dark'}
|
||||||
|
onChange={handleToggle}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Icon
|
||||||
|
path={mdiWeatherSunny}
|
||||||
|
className="swap-off h-8 w-8 text-yellow-500"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Icon path={mdiWeatherNight} className="swap-on h-8 w-8 text-blue-500" />
|
||||||
|
</label>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ThemeToggle
|
@ -1,15 +1,21 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="icon" type="image/png" href="/favicon.png" />
|
||||||
|
|
||||||
<head>
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
<meta charset="UTF-8" />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<link rel="icon" type="image/png" href="/favicon.png" />
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
<link
|
||||||
<!-- Please keep this div empty -->
|
href="https://fonts.googleapis.com/css2?family=Inter&family=Syne"
|
||||||
<div id="redwood-app"></div>
|
rel="stylesheet"
|
||||||
</body>
|
/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<!-- Please keep this div empty -->
|
||||||
|
<div id="redwood-app"></div>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
13
web/src/pages/HomePage/HomePage.stories.tsx
Normal file
13
web/src/pages/HomePage/HomePage.stories.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import type { Meta, StoryObj } from '@storybook/react'
|
||||||
|
|
||||||
|
import HomePage from './HomePage'
|
||||||
|
|
||||||
|
const meta: Meta<typeof HomePage> = {
|
||||||
|
component: HomePage,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default meta
|
||||||
|
|
||||||
|
type Story = StoryObj<typeof HomePage>
|
||||||
|
|
||||||
|
export const Primary: Story = {}
|
14
web/src/pages/HomePage/HomePage.test.tsx
Normal file
14
web/src/pages/HomePage/HomePage.test.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { render } from '@redwoodjs/testing/web'
|
||||||
|
|
||||||
|
import HomePage from './HomePage'
|
||||||
|
|
||||||
|
// Improve this test with help from the Redwood Testing Doc:
|
||||||
|
// https://redwoodjs.com/docs/testing#testing-pages-layouts
|
||||||
|
|
||||||
|
describe('HomePage', () => {
|
||||||
|
it('renders successfully', () => {
|
||||||
|
expect(() => {
|
||||||
|
render(<HomePage />)
|
||||||
|
}).not.toThrow()
|
||||||
|
})
|
||||||
|
})
|
21
web/src/pages/HomePage/HomePage.tsx
Normal file
21
web/src/pages/HomePage/HomePage.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { Link, routes } from '@redwoodjs/router'
|
||||||
|
import { Metadata } from '@redwoodjs/web'
|
||||||
|
|
||||||
|
const HomePage = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Metadata title="Home" description="Home page" />
|
||||||
|
|
||||||
|
<h1>HomePage</h1>
|
||||||
|
<p>
|
||||||
|
Find me in <code>./web/src/pages/HomePage/HomePage.tsx</code>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
My default route is named <code>home</code>, link to me with `
|
||||||
|
<Link to={routes.home()}>Home</Link>`
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HomePage
|
Reference in New Issue
Block a user