Skip to content

Commit

Permalink
feat: add theme changer
Browse files Browse the repository at this point in the history
  • Loading branch information
theodorusclarence committed Feb 6, 2022
1 parent a4e64b2 commit fbdacd0
Show file tree
Hide file tree
Showing 18 changed files with 192 additions and 122 deletions.
3 changes: 3 additions & 0 deletions notiolink.config.js
Expand Up @@ -8,4 +8,7 @@ module.exports = {

/** Without additional '/' on the end, e.g. https://theodorusclarence.com */
deployUrl: 'https://notiolink.thcl.dev',

/** Make this to false */
demoMode: 'true',
};
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -33,7 +33,8 @@
"react-icons": "^4.3.1",
"react-qr-code": "^2.0.3",
"react-query": "^3.34.12",
"tailwind-merge": "^1.2.0"
"tailwind-merge": "^1.2.0",
"tailwindcss-multi-theme": "^1.0.4"
},
"devDependencies": {
"@commitlint/cli": "^13.2.1",
Expand Down
8 changes: 2 additions & 6 deletions src/components/buttons/Button.tsx
Expand Up @@ -13,7 +13,6 @@ enum ButtonVariant {

type ButtonProps = {
isLoading?: boolean;
isDarkBg?: boolean;
variant?: keyof typeof ButtonVariant;
} & React.ComponentPropsWithRef<'button'>;

Expand All @@ -25,7 +24,6 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
disabled: buttonDisabled,
isLoading,
variant = 'primary',
isDarkBg = false,
...rest
},
ref
Expand Down Expand Up @@ -55,15 +53,13 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
'text-primary-500',
'border border-primary-500',
'hover:bg-primary-50 active:bg-primary-100 disabled:bg-primary-100',
isDarkBg &&
'hover:bg-gray-900 active:bg-gray-800 disabled:bg-gray-800',
'dark:hover:bg-gray-900 dark:active:bg-gray-800 dark:disabled:bg-gray-800',
],
variant === 'ghost' && [
'text-primary-500',
'shadow-none',
'hover:bg-primary-50 active:bg-primary-100 disabled:bg-primary-100',
isDarkBg &&
'hover:bg-gray-900 active:bg-gray-800 disabled:bg-gray-800',
'dark:hover:bg-gray-900 dark:active:bg-gray-800 dark:disabled:bg-gray-800',
],
variant === 'light' && [
'bg-white text-dark ',
Expand Down
39 changes: 27 additions & 12 deletions src/components/forms/Input.tsx
@@ -1,8 +1,9 @@
import clsx from 'clsx';
import * as React from 'react';
import { RegisterOptions, useFormContext } from 'react-hook-form';
import { HiExclamationCircle } from 'react-icons/hi';

import clsxm from '@/lib/clsxm';

export type InputProps = {
/** Input label */
label: string;
Expand Down Expand Up @@ -46,7 +47,10 @@ export default function Input({

return (
<div className='w-full'>
<label htmlFor={id} className='block text-sm font-normal text-gray-200'>
<label
htmlFor={id}
className='block text-sm font-normal text-gray-700 dark:text-gray-200'
>
{label}
</label>
<div className='relative mt-1'>
Expand All @@ -57,28 +61,39 @@ export default function Input({
name={id}
id={id}
readOnly={readOnly}
className={clsx(
readOnly
? 'cursor-not-allowed border-gray-600 bg-gray-700 focus:border-gray-600 focus:ring-0'
: errors[id]
? 'border-red-400 focus:border-red-400 focus:ring-red-400'
: 'border-gray-600 focus:border-primary-500 focus:ring-primary-500',
'block w-full rounded-md bg-dark text-white shadow-sm'
className={clsxm(
'bg-transparent',
'focus:border-primary-500 focus:ring-primary-500',
readOnly &&
'cursor-not-allowed bg-gray-700 focus:border-gray-600 focus:ring-0',
errors[id]
? [
'border-red-600 focus:border-red-600 focus:ring-red-600',
'dark:border-red-400 dark:focus:border-red-400 dark:focus:ring-red-400',
]
: ['border-gray-400', 'dark:border-gray-600'],
'block w-full rounded-md shadow-sm dark:text-white'
)}
placeholder={placeholder}
aria-describedby={id}
/>

{!hideError && errors[id] && (
<div className='pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3'>
<HiExclamationCircle className='text-xl text-red-400' />
<HiExclamationCircle className='text-xl text-red-600 dark:text-red-400' />
</div>
)}
</div>
<div className='mt-1'>
{helperText && <p className='text-xs text-gray-300'>{helperText}</p>}
{helperText && (
<p className='text-xs text-gray-600 dark:text-gray-300'>
{helperText}
</p>
)}
{!hideError && errors[id] && (
<span className='text-sm text-red-400'>{errors[id].message}</span>
<span className='text-sm text-red-600 dark:text-red-400'>
{errors[id].message}
</span>
)}
</div>
</div>
Expand Down
28 changes: 26 additions & 2 deletions src/components/layout/Layout.tsx
@@ -1,6 +1,30 @@
import clsx from 'clsx';
import * as React from 'react';

import { demoMode } from '@/lib/config';

export default function Layout({ children }: { children: React.ReactNode }) {
// Put Header or Footer Here
return <div className='default'>{children}</div>;
const [theme, setTheme] = React.useState('dark');

return (
<div className={theme}>
<div
className={clsx('dark:bg-dark dark:text-white', 'milky:bg-[#fff5e3]')}
>
{demoMode && (
<select
name='theme'
className='fixed top-4 left-4 rounded bg-transparent'
value={theme}
onChange={(e) => setTheme(e.target.value)}
>
<option value='light'>light</option>
<option value='dark'>dark</option>
<option value='theme-milky'>milky</option>
</select>
)}
{children}
</div>
</div>
);
}
4 changes: 2 additions & 2 deletions src/components/links/PrimaryLink.tsx
Expand Up @@ -14,8 +14,8 @@ const PrimaryLink = React.forwardRef<HTMLAnchorElement, UnstyledLinkProps>(
{...rest}
className={clsxm(
'inline-flex items-center',
'font-medium text-primary-400 hover:text-primary-500',
'focus:outline-none focus-visible:rounded focus-visible:ring focus-visible:ring-primary-500 focus-visible:ring-offset-2',
'font-medium text-primary-500 hover:text-primary-400',
'focus:outline-none focus-visible:rounded focus-visible:ring focus-visible:ring-primary-400 focus-visible:ring-offset-2',
className
)}
>
Expand Down
67 changes: 67 additions & 0 deletions src/components/links/TreeLink.tsx
@@ -0,0 +1,67 @@
/* eslint-disable @next/next/no-img-element */
import clsx from 'clsx';
import * as React from 'react';

import clsxm from '@/lib/clsxm';
import { Tree } from '@/lib/notion';

type TreeLinkProps = {
link: Tree;
} & React.ComponentPropsWithoutRef<'div'>;

export default function TreeLink({
className,
link: { display, link, icon },
...rest
}: TreeLinkProps) {
return (
<div className={clsxm('group relative', className)} {...rest}>
<div
className={clsx(
'opacity-0 group-hover:opacity-100',
'absolute -inset-0.5 z-0 animate-tilt rounded blur',
'accent-gradient bg-gradient-to-r',
'transition duration-300 group-hover:duration-200',
'milky:rounded-full',
'pointer-events-none'
)}
/>

<a
href={link}
className={clsx(
'relative flex items-center justify-center gap-2',
'px-4 py-4 font-medium transition-colors md:text-lg',
'border',
'focus:outline-none focus-visible:ring focus-visible:ring-primary-500',
'border-gray-400 bg-white',
'dark:border-gray-600 dark:bg-dark',
'milky:rounded-full milky:bg-milky-100 milky:text-milky-500'
)}
>
{icon ? (
icon.type === 'emoji' ? (
icon.emoji + ' '
) : icon.type === 'external' ? (
<img
src={icon.external.url}
width={20}
height={20}
className='text-transparent'
alt={`${display} Icon`}
/>
) : (
<img
src={icon.file.url}
width={20}
height={20}
className='text-transparent'
alt={`${display} Icon`}
/>
)
) : null}
{display}
</a>
</div>
);
}
4 changes: 2 additions & 2 deletions src/container/CopyBox.tsx
Expand Up @@ -16,7 +16,7 @@ export default function CopyBox({ className, link, ...rest }: CopyBoxProps) {
return (
<div
className={clsxm(
'flex max-w-sm items-center gap-2 rounded border border-gray-600 p-2 pl-4',
'flex max-w-sm items-center gap-2 rounded border border-gray-300 p-2 pl-4 dark:border-gray-600',
className
)}
{...rest}
Expand All @@ -31,7 +31,7 @@ export default function CopyBox({ className, link, ...rest }: CopyBoxProps) {
text={link}
onCopy={() => toast.success(`${trimHttps(link)} copied to clipboard`)}
>
<button className='rounded-full p-2 hover:text-primary-400 focus:outline-none focus:ring focus:ring-primary-400'>
<button className='rounded-full p-2 text-gray-600 hover:text-primary-400 focus:outline-none focus:ring focus:ring-primary-400 dark:text-white'>
<HiClipboard className='text-lg' />
</button>
</CopyToClipboard>
Expand Down
1 change: 1 addition & 0 deletions src/lib/config.ts
Expand Up @@ -4,3 +4,4 @@ import { getSiteConfig } from '@/lib/get-config-value';
export const appName = getSiteConfig('appName');
export const seoDescription = getSiteConfig('seoDescription');
export const deployUrl = getSiteConfig('deployUrl');
export const demoMode = getSiteConfig('demoMode');
4 changes: 2 additions & 2 deletions src/pages/[slug]/detail.tsx
Expand Up @@ -73,13 +73,13 @@ export default function DetailPage() {
) : (
<Skeleton className='h-5 w-5' />
)}
<div className='w-full max-w-sm break-all font-medium text-gray-300'>
<div className='w-full max-w-sm break-all font-medium text-gray-600 dark:text-gray-300'>
{url?.link ? url.link : <Skeleton className='h-5 w-64' />}
</div>
</div>
<div className='mt-2 flex items-center gap-4'>
<HiCursorClick className='h-5 w-5' />
<span className='font-medium text-gray-300'>
<span className='font-medium text-gray-600 dark:text-gray-300'>
{url?.count ?? '—'} click{(url?.count ?? 0) > 1 && 's'}
</span>
</div>
Expand Down
5 changes: 1 addition & 4 deletions src/pages/_app.tsx
Expand Up @@ -7,7 +7,6 @@ import '@/styles/globals.css';
import axiosClient from '@/lib/axios';

import DismissableToast from '@/components/DismissableToast';
import Layout from '@/components/layout/Layout';

const defaultQueryFn = async ({ queryKey }: QueryOptions) => {
const { data } = await axiosClient.get(`${queryKey?.[0]}`);
Expand All @@ -26,9 +25,7 @@ function MyApp({ Component, pageProps }: AppProps) {
return (
<QueryClientProvider client={queryClient}>
<DismissableToast />
<Layout>
<Component {...pageProps} />
</Layout>
<Component {...pageProps} />
<ReactQueryDevtools initialIsOpen={false} position='bottom-right' />
</QueryClientProvider>
);
Expand Down
2 changes: 1 addition & 1 deletion src/pages/_document.tsx
Expand Up @@ -24,7 +24,7 @@ class MyDocument extends Document {
crossOrigin='anonymous'
/>
</Head>
<body className='bg-dark text-white'>
<body className='dark:bg-dark dark:text-white'>
<Main />
<NextScript />
</body>
Expand Down
60 changes: 9 additions & 51 deletions src/pages/index.tsx
@@ -1,5 +1,4 @@
/* eslint-disable @next/next/no-img-element */
import clsx from 'clsx';
import { InferGetStaticPropsType } from 'next';
import * as React from 'react';

Expand All @@ -8,7 +7,8 @@ import { getSocialTree } from '@/lib/notion';

import Accent from '@/components/Accent';
import Layout from '@/components/layout/Layout';
import UnderlineLink from '@/components/links/UnderlineLink';
import PrimaryLink from '@/components/links/PrimaryLink';
import TreeLink from '@/components/links/TreeLink';
import Seo from '@/components/Seo';

export default function IndexPage({
Expand All @@ -19,64 +19,22 @@ export default function IndexPage({
<Seo />

<main>
<section className='bg-dark'>
<section className=''>
<div className='layout flex min-h-screen flex-col items-center justify-center py-20'>
<h1 className='text-5xl md:text-7xl'>
<h1 className='text-center text-5xl md:text-7xl'>
<Accent>{appName}</Accent>
</h1>
<div className='mx-auto mt-8 grid w-full max-w-sm gap-4 text-center'>
{links.map(({ id, display, link, icon }) => (
<div className='group relative' key={id}>
<div
className={clsx(
'opacity-0 group-hover:opacity-100',
'absolute -inset-0.5 z-0 animate-tilt rounded blur',
'accent-gradient bg-gradient-to-r',
'transition duration-300 group-hover:duration-200',
'pointer-events-none'
)}
/>

<a
href={link}
className={clsx(
'relative flex items-center justify-center gap-2',
'px-4 py-4 font-medium transition-colors md:text-lg ',
'bg-dark',
'border border-gray-600',
'focus:outline-none focus-visible:ring focus-visible:ring-primary-500'
)}
>
{icon ? (
icon.type === 'emoji' ? (
icon.emoji + ' '
) : icon.type === 'external' ? (
<img
src={icon.external.url}
width={20}
height={20}
alt={`${display} Icon`}
/>
) : (
<img
src={icon.file.url}
width={20}
height={20}
alt={`${display} Icon`}
/>
)
) : null}
{display}
</a>
</div>
{links.map((link) => (
<TreeLink key={link.id} link={link} />
))}
</div>
{/* Thank you for not removing this as an attribution 🙏 */}
<p className='mt-10 text-gray-300'>
<p className='mt-10 dark:text-gray-300'>
Built using{' '}
<UnderlineLink href='https://github.com/theodorusclarence/notiolink'>
<PrimaryLink href='https://github.com/theodorusclarence/notiolink'>
<Accent>Notiolink</Accent>
</UnderlineLink>
</PrimaryLink>
</p>
</div>
</section>
Expand Down
1 change: 0 additions & 1 deletion src/pages/login.tsx
Expand Up @@ -76,7 +76,6 @@ export default function NewLinkPage() {
isLoading={isLoading}
className='w-full justify-center md:ml-auto md:w-auto'
variant='outline'
isDarkBg
type='submit'
>
Login!
Expand Down

0 comments on commit fbdacd0

Please sign in to comment.