buttons
This commit is contained in:
parent
abd7c2f073
commit
384da1e832
18 changed files with 150 additions and 116 deletions
|
@ -9,7 +9,6 @@ import { AuthService } from './app/auth/authService.ts'
|
||||||
import { useUser } from './app/user/userStore.ts'
|
import { useUser } from './app/user/userStore.ts'
|
||||||
import LogoutPage from './app/auth/pages/LogoutPage.tsx'
|
import LogoutPage from './app/auth/pages/LogoutPage.tsx'
|
||||||
import UnauthorizedHandler from './app/auth/components/UnauthorizedHandler.tsx'
|
import UnauthorizedHandler from './app/auth/components/UnauthorizedHandler.tsx'
|
||||||
import Protected from './app/auth/components/Protected.tsx'
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const { user } = useUser()
|
const { user } = useUser()
|
||||||
|
@ -21,13 +20,11 @@ function App() {
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<UnauthorizedHandler>
|
<UnauthorizedHandler>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route element={<Protected />}>
|
|
||||||
<Route
|
<Route
|
||||||
path={'/'}
|
path={'/'}
|
||||||
element={<HomePage postsService={postService} mediaService={mediaService} />}
|
element={<HomePage postsService={postService} mediaService={mediaService} />}
|
||||||
/>
|
/>
|
||||||
<Route path="/u/:username" element={<AuthorPage postsService={postService} />} />
|
<Route path="/u/:username" element={<AuthorPage postsService={postService} />} />
|
||||||
</Route>
|
|
||||||
<Route path="/login" element={<LoginPage authService={authService} />} />
|
<Route path="/login" element={<LoginPage authService={authService} />} />
|
||||||
<Route path="/logout" element={<LogoutPage authService={authService} />} />
|
<Route path="/logout" element={<LogoutPage authService={authService} />} />
|
||||||
<Route path="/signup/:code?" element={<SignupPage authService={authService} />} />
|
<Route path="/signup/:code?" element={<SignupPage authService={authService} />} />
|
||||||
|
|
|
@ -275,6 +275,7 @@ export interface components {
|
||||||
authorId: string
|
authorId: string
|
||||||
content: string
|
content: string
|
||||||
media: components['schemas']['CreatePostRequestMedia'][]
|
media: components['schemas']['CreatePostRequestMedia'][]
|
||||||
|
isPublic: boolean | null
|
||||||
}
|
}
|
||||||
CreatePostRequestMedia: {
|
CreatePostRequestMedia: {
|
||||||
/** Format: uuid */
|
/** Format: uuid */
|
||||||
|
@ -292,7 +293,7 @@ export interface components {
|
||||||
postId: string
|
postId: string
|
||||||
}
|
}
|
||||||
GetAllPublicPostsResponse: {
|
GetAllPublicPostsResponse: {
|
||||||
posts: components['schemas']['PublicPostDto'][]
|
posts: components['schemas']['PostDto'][]
|
||||||
/** Format: uuid */
|
/** Format: uuid */
|
||||||
next: string | null
|
next: string | null
|
||||||
}
|
}
|
||||||
|
@ -305,21 +306,21 @@ export interface components {
|
||||||
userId: string
|
userId: string
|
||||||
username: string
|
username: string
|
||||||
}
|
}
|
||||||
PublicPostAuthorDto: {
|
PostAuthorDto: {
|
||||||
/** Format: uuid */
|
/** Format: uuid */
|
||||||
authorId: string
|
authorId: string
|
||||||
username: string
|
username: string
|
||||||
}
|
}
|
||||||
PublicPostDto: {
|
PostDto: {
|
||||||
author: components['schemas']['PublicPostAuthorDto']
|
author: components['schemas']['PostAuthorDto']
|
||||||
/** Format: uuid */
|
/** Format: uuid */
|
||||||
postId: string
|
postId: string
|
||||||
content: string
|
content: string
|
||||||
media: components['schemas']['PublicPostMediaDto'][]
|
media: components['schemas']['PostMediaDto'][]
|
||||||
/** Format: date-time */
|
/** Format: date-time */
|
||||||
createdAt: string
|
createdAt: string
|
||||||
}
|
}
|
||||||
PublicPostMediaDto: {
|
PostMediaDto: {
|
||||||
/** Format: uri */
|
/** Format: uri */
|
||||||
url: string
|
url: string
|
||||||
/** Format: int32 */
|
/** Format: int32 */
|
||||||
|
|
30
src/app/auth/components/AuthNavButtons.tsx
Normal file
30
src/app/auth/components/AuthNavButtons.tsx
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { useUser } from '../../user/userStore.ts'
|
||||||
|
import NavButton from '../../../components/buttons/NavButton.tsx'
|
||||||
|
import { useLocation } from 'react-router-dom'
|
||||||
|
|
||||||
|
export default function AuthNavButtons() {
|
||||||
|
const { user } = useUser()
|
||||||
|
|
||||||
|
const { pathname } = useLocation()
|
||||||
|
|
||||||
|
const redirectQuery = new URLSearchParams()
|
||||||
|
redirectQuery.set('t', pathname)
|
||||||
|
|
||||||
|
const loggedIn = user != null
|
||||||
|
|
||||||
|
if (loggedIn) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<NavButton to="/logout">logout</NavButton>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
const search = redirectQuery.toString()
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<NavButton to={{ pathname: '/login', search }}>login</NavButton>
|
||||||
|
<NavButton to={{ pathname: '/signup', search }}>register</NavButton>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,13 @@
|
||||||
import { useRef, useState, FormEvent, useEffect } from 'react'
|
import { useRef, useState, FormEvent, useEffect } from 'react'
|
||||||
import SingleColumnLayout from '../../../layouts/SingleColumnLayout.tsx'
|
import SingleColumnLayout from '../../../layouts/SingleColumnLayout.tsx'
|
||||||
import TextInput from '../../../components/TextInput.tsx'
|
import TextInput from '../../../components/inputs/TextInput.tsx'
|
||||||
import PrimaryButton from '../../../components/PrimaryButton.tsx'
|
import Button from '../../../components/buttons/Button.tsx'
|
||||||
import { AuthService } from '../authService.ts'
|
import { AuthService } from '../authService.ts'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import SecondaryNavButton from '../../../components/SecondaryNavButton.tsx'
|
|
||||||
import { useUser } from '../../user/userStore.ts'
|
import { useUser } from '../../user/userStore.ts'
|
||||||
|
import NavBar from '../../../components/NavBar.tsx'
|
||||||
|
import NavButton from '../../../components/buttons/NavButton.tsx'
|
||||||
|
import LinkButton from '../../../components/buttons/LinkButton.tsx'
|
||||||
|
|
||||||
interface LoginPageProps {
|
interface LoginPageProps {
|
||||||
authService: AuthService
|
authService: AuthService
|
||||||
|
@ -24,7 +26,8 @@ export default function LoginPage({ authService }: LoginPageProps) {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (user) {
|
if (user) {
|
||||||
navigate('/')
|
const search = new URLSearchParams(window.location.search)
|
||||||
|
navigate(search.get('t') || '/')
|
||||||
}
|
}
|
||||||
}, [user, navigate])
|
}, [user, navigate])
|
||||||
|
|
||||||
|
@ -55,7 +58,13 @@ export default function LoginPage({ authService }: LoginPageProps) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SingleColumnLayout>
|
<SingleColumnLayout
|
||||||
|
navbar={
|
||||||
|
<NavBar>
|
||||||
|
<NavButton to={'/'}>home</NavButton>
|
||||||
|
</NavBar>
|
||||||
|
}
|
||||||
|
>
|
||||||
<main className="w-full mx-auto p-4">
|
<main className="w-full mx-auto p-4">
|
||||||
<div className="mt-12">
|
<div className="mt-12">
|
||||||
<form className="flex flex-col gap-4 max-w-md" onSubmit={onSubmit}>
|
<form className="flex flex-col gap-4 max-w-md" onSubmit={onSubmit}>
|
||||||
|
@ -86,11 +95,13 @@ export default function LoginPage({ authService }: LoginPageProps) {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PrimaryButton className="mt-4" disabled={isSubmitting} type="submit">
|
<Button className="mt-4" disabled={isSubmitting} type="submit">
|
||||||
{isSubmitting ? 'wait...' : 'make login pls'}
|
{isSubmitting ? 'wait...' : 'make login pls'}
|
||||||
</PrimaryButton>
|
</Button>
|
||||||
|
|
||||||
<SecondaryNavButton to={'/signup'}>register instead?</SecondaryNavButton>
|
<LinkButton secondary to={{ pathname: '/signup', search: window.location.search }}>
|
||||||
|
register instead?
|
||||||
|
</LinkButton>
|
||||||
|
|
||||||
<span className="text-xs h-3 text-red-500">{error}</span>
|
<span className="text-xs h-3 text-red-500">{error}</span>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import { useNavigate, useParams } from 'react-router-dom'
|
import { useNavigate, useParams } from 'react-router-dom'
|
||||||
import { useEffect, useRef, useState, FormEvent, useCallback, Ref } from 'react'
|
import { useEffect, useRef, useState, FormEvent, useCallback, Ref } from 'react'
|
||||||
import SingleColumnLayout from '../../../layouts/SingleColumnLayout.tsx'
|
import SingleColumnLayout from '../../../layouts/SingleColumnLayout.tsx'
|
||||||
import TextInput from '../../../components/TextInput.tsx'
|
import TextInput from '../../../components/inputs/TextInput.tsx'
|
||||||
import PrimaryButton from '../../../components/PrimaryButton.tsx'
|
import Button from '../../../components/buttons/Button.tsx'
|
||||||
import PrimaryLinkButton from '../../../components/PrimaryLinkButton.tsx'
|
import AnchorButton from '../../../components/buttons/AnchorButton.tsx'
|
||||||
import { invalid, valid, Validation } from '../../../utils/validation.ts'
|
import { invalid, valid, Validation } from '../../../utils/validation.ts'
|
||||||
import { AuthService } from '../authService.ts'
|
import { AuthService } from '../authService.ts'
|
||||||
import SecondaryNavButton from '../../../components/SecondaryNavButton.tsx'
|
import LinkButton from '../../../components/buttons/LinkButton.tsx'
|
||||||
|
import NavBar from '../../../components/NavBar.tsx'
|
||||||
|
import NavButton from '../../../components/buttons/NavButton.tsx'
|
||||||
|
|
||||||
const SignupCodeKey = 'signupCode'
|
const SignupCodeKey = 'signupCode'
|
||||||
|
|
||||||
|
@ -82,7 +84,13 @@ export default function SignupPage({ authService }: SignupPageProps) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SingleColumnLayout>
|
<SingleColumnLayout
|
||||||
|
navbar={
|
||||||
|
<NavBar>
|
||||||
|
<NavButton to={'/'}>home</NavButton>
|
||||||
|
</NavBar>
|
||||||
|
}
|
||||||
|
>
|
||||||
<main className="w-full mx-auto p-4">
|
<main className="w-full mx-auto p-4">
|
||||||
<div className="mt-12">
|
<div className="mt-12">
|
||||||
<form className="flex flex-col gap-4 max-w-md" onSubmit={onSubmit}>
|
<form className="flex flex-col gap-4 max-w-md" onSubmit={onSubmit}>
|
||||||
|
@ -102,14 +110,16 @@ export default function SignupPage({ authService }: SignupPageProps) {
|
||||||
type="password"
|
type="password"
|
||||||
ref={passwordInputRef}
|
ref={passwordInputRef}
|
||||||
/>
|
/>
|
||||||
<PrimaryButton
|
<Button
|
||||||
className="mt-4"
|
className="mt-4"
|
||||||
disabled={isSubmitting || !!usernameError || !!passwordError}
|
disabled={isSubmitting || !!usernameError || !!passwordError}
|
||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
{isSubmitting ? 'wait...' : 'give me an account pls'}
|
{isSubmitting ? 'wait...' : 'give me an account pls'}
|
||||||
</PrimaryButton>
|
</Button>
|
||||||
<SecondaryNavButton to={'/login'}>login instead?</SecondaryNavButton>
|
<LinkButton secondary to={'/login'}>
|
||||||
|
login instead?
|
||||||
|
</LinkButton>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
@ -130,9 +140,9 @@ export default function SignupPage({ authService }: SignupPageProps) {
|
||||||
If you <span className="italic">do</span> want to create an account, you should know who
|
If you <span className="italic">do</span> want to create an account, you should know who
|
||||||
to contact
|
to contact
|
||||||
</p>
|
</p>
|
||||||
<PrimaryLinkButton className={`mt-4`} href="https://en.wikipedia.org/wiki/Special:Random">
|
<AnchorButton className={`mt-4`} href="https://en.wikipedia.org/wiki/Special:Random">
|
||||||
I'm sorry I'll go somewhere else :(
|
I'm sorry I'll go somewhere else :(
|
||||||
</PrimaryLinkButton>
|
</AnchorButton>
|
||||||
</div>
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
</SingleColumnLayout>
|
</SingleColumnLayout>
|
||||||
|
|
|
@ -5,7 +5,8 @@ import { useParams } from 'react-router-dom'
|
||||||
import SingleColumnLayout from '../../../layouts/SingleColumnLayout.tsx'
|
import SingleColumnLayout from '../../../layouts/SingleColumnLayout.tsx'
|
||||||
import NavBar from '../../../components/NavBar.tsx'
|
import NavBar from '../../../components/NavBar.tsx'
|
||||||
import { useFeedViewModel } from '../components/FeedView.ts'
|
import { useFeedViewModel } from '../components/FeedView.ts'
|
||||||
import NavLinkButton from '../../../components/NavLinkButton.tsx'
|
import NavButton from '../../../components/buttons/NavButton.tsx'
|
||||||
|
import AuthNavButtons from '../../auth/components/AuthNavButtons.tsx'
|
||||||
|
|
||||||
interface AuthorPageParams {
|
interface AuthorPageParams {
|
||||||
postsService: PostsService
|
postsService: PostsService
|
||||||
|
@ -27,8 +28,8 @@ export default function AuthorPage({ postsService }: AuthorPageParams) {
|
||||||
<SingleColumnLayout
|
<SingleColumnLayout
|
||||||
navbar={
|
navbar={
|
||||||
<NavBar>
|
<NavBar>
|
||||||
<NavLinkButton to={'/'}>home</NavLinkButton>
|
<NavButton to={'/'}>home</NavButton>
|
||||||
<NavLinkButton to="/logout">logout</NavLinkButton>
|
<AuthNavButtons />
|
||||||
</NavBar>
|
</NavBar>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { Post } from '../posts/posts.ts'
|
||||||
import { Temporal } from '@js-temporal/polyfill'
|
import { Temporal } from '@js-temporal/polyfill'
|
||||||
import SingleColumnLayout from '../../../layouts/SingleColumnLayout.tsx'
|
import SingleColumnLayout from '../../../layouts/SingleColumnLayout.tsx'
|
||||||
import NavBar from '../../../components/NavBar.tsx'
|
import NavBar from '../../../components/NavBar.tsx'
|
||||||
import NavLinkButton from '../../../components/NavLinkButton.tsx'
|
import AuthNavButtons from '../../auth/components/AuthNavButtons.tsx'
|
||||||
|
|
||||||
interface HomePageProps {
|
interface HomePageProps {
|
||||||
postsService: PostsService
|
postsService: PostsService
|
||||||
|
@ -18,7 +18,6 @@ interface HomePageProps {
|
||||||
|
|
||||||
export default function HomePage({ postsService, mediaService }: HomePageProps) {
|
export default function HomePage({ postsService, mediaService }: HomePageProps) {
|
||||||
const { user } = useUser()
|
const { user } = useUser()
|
||||||
|
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||||
|
|
||||||
const fetchPosts = useCallback(
|
const fetchPosts = useCallback(
|
||||||
|
@ -59,16 +58,18 @@ export default function HomePage({ postsService, mediaService }: HomePageProps)
|
||||||
[mediaService, postsService, setPages, user],
|
[mediaService, postsService, setPages, user],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const isLoggedIn = user != null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SingleColumnLayout
|
<SingleColumnLayout
|
||||||
navbar={
|
navbar={
|
||||||
<NavBar>
|
<NavBar>
|
||||||
<NavLinkButton to="/logout">logout</NavLinkButton>
|
<AuthNavButtons />
|
||||||
</NavBar>
|
</NavBar>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<main className={`w-full max-w-3xl mx-auto`}>
|
<main className={`w-full max-w-3xl mx-auto`}>
|
||||||
<NewPostWidget onSubmit={onCreatePost} isSubmitting={isSubmitting} />
|
{isLoggedIn && <NewPostWidget onSubmit={onCreatePost} isSubmitting={isSubmitting} />}
|
||||||
<FeedView pages={pages} onLoadMore={loadNextPage} />
|
<FeedView pages={pages} onLoadMore={loadNextPage} />
|
||||||
</main>
|
</main>
|
||||||
</SingleColumnLayout>
|
</SingleColumnLayout>
|
||||||
|
|
|
@ -4,7 +4,12 @@ import client from '../../api/client.ts'
|
||||||
export class PostsService {
|
export class PostsService {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
async createNew(authorId: string, content: string, media: CreatePostMedia[]): Promise<string> {
|
async createNew(
|
||||||
|
authorId: string,
|
||||||
|
content: string,
|
||||||
|
media: CreatePostMedia[],
|
||||||
|
isPublic: boolean,
|
||||||
|
): Promise<string> {
|
||||||
const response = await client.POST('/posts', {
|
const response = await client.POST('/posts', {
|
||||||
body: {
|
body: {
|
||||||
authorId,
|
authorId,
|
||||||
|
@ -12,6 +17,7 @@ export class PostsService {
|
||||||
media: media.map((m) => {
|
media: media.map((m) => {
|
||||||
return { ...m, type: null, url: m.url.toString() }
|
return { ...m, type: null, url: m.url.toString() }
|
||||||
}),
|
}),
|
||||||
|
isPublic,
|
||||||
},
|
},
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import FancyTextEditor, { TextInputKeyDownEvent } from './FancyTextEditor.tsx'
|
import FancyTextEditor, { TextInputKeyDownEvent } from './inputs/FancyTextEditor.tsx'
|
||||||
import PrimaryButton from './PrimaryButton.tsx'
|
import Button from './buttons/Button.tsx'
|
||||||
import SecondaryButton from './SecondaryButton.tsx'
|
|
||||||
import { openFileDialog } from '../utils/openFileDialog.ts'
|
import { openFileDialog } from '../utils/openFileDialog.ts'
|
||||||
|
|
||||||
interface NewPostWidgetProps {
|
interface NewPostWidgetProps {
|
||||||
|
@ -86,13 +85,15 @@ export default function NewPostWidget({ onSubmit, isSubmitting = false }: NewPos
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex justify-between items-center pt-2">
|
<div className="flex justify-between items-center pt-2">
|
||||||
<SecondaryButton onClick={onAddMediaClicked}>+ add media</SecondaryButton>
|
<Button secondary onClick={onAddMediaClicked}>
|
||||||
<PrimaryButton
|
+ add media
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
disabled={isSubmitting || (content.trim() === '' && attachments.length === 0)}
|
disabled={isSubmitting || (content.trim() === '' && attachments.length === 0)}
|
||||||
>
|
>
|
||||||
post
|
post
|
||||||
</PrimaryButton>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
import { PropsWithChildren } from 'react'
|
|
||||||
interface PrimaryButtonProps {
|
|
||||||
disabled?: boolean
|
|
||||||
type?: 'submit' | 'button'
|
|
||||||
onClick?: () => void
|
|
||||||
className?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function SecondaryButton({
|
|
||||||
disabled = false,
|
|
||||||
type = 'button',
|
|
||||||
onClick = () => {},
|
|
||||||
className: extraClasses = '',
|
|
||||||
children,
|
|
||||||
}: PropsWithChildren<PrimaryButtonProps>) {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
type={type}
|
|
||||||
disabled={disabled}
|
|
||||||
onClick={onClick}
|
|
||||||
className={`secondary-button ${extraClasses}
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
import { PropsWithChildren } from 'react'
|
|
||||||
|
|
||||||
interface SecondaryLinkButtonProps {
|
|
||||||
href: string
|
|
||||||
className?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function SecondaryLinkButton({
|
|
||||||
href,
|
|
||||||
className: extraClasses = '',
|
|
||||||
children,
|
|
||||||
}: PropsWithChildren<SecondaryLinkButtonProps>) {
|
|
||||||
return (
|
|
||||||
<a href={href} className={`secondary-button text-center ${extraClasses}`}>
|
|
||||||
{children}
|
|
||||||
</a>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
import { PropsWithChildren } from 'react'
|
|
||||||
import { Link } from 'react-router-dom'
|
|
||||||
|
|
||||||
interface SecondaryNavButtonProps {
|
|
||||||
to: string
|
|
||||||
className?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function SecondaryNavButton({
|
|
||||||
to,
|
|
||||||
className: extraClasses = '',
|
|
||||||
children,
|
|
||||||
}: PropsWithChildren<SecondaryNavButtonProps>) {
|
|
||||||
return (
|
|
||||||
<Link to={to} className={`secondary-button text-center ${extraClasses}`}>
|
|
||||||
{children}
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -3,15 +3,18 @@ import { PropsWithChildren } from 'react'
|
||||||
interface PrimaryLinkButtonProps {
|
interface PrimaryLinkButtonProps {
|
||||||
href: string
|
href: string
|
||||||
className?: string
|
className?: string
|
||||||
|
secondary?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PrimaryLinkButton({
|
export default function AnchorButton({
|
||||||
href,
|
href,
|
||||||
className: extraClasses = '',
|
className: extraClasses = '',
|
||||||
children,
|
children,
|
||||||
|
secondary = false,
|
||||||
}: PropsWithChildren<PrimaryLinkButtonProps>) {
|
}: PropsWithChildren<PrimaryLinkButtonProps>) {
|
||||||
|
const klass = secondary ? 'secondary-button' : 'primary-button'
|
||||||
return (
|
return (
|
||||||
<a href={href} className={`primary-button text-center ${extraClasses}`}>
|
<a href={href} className={`${klass} text-center ${extraClasses}`}>
|
||||||
{children}
|
{children}
|
||||||
</a>
|
</a>
|
||||||
)
|
)
|
|
@ -5,21 +5,24 @@ interface PrimaryButtonProps {
|
||||||
type?: 'submit' | 'button'
|
type?: 'submit' | 'button'
|
||||||
onClick?: () => void
|
onClick?: () => void
|
||||||
className?: string
|
className?: string
|
||||||
|
secondary?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PrimaryButton({
|
export default function Button({
|
||||||
disabled = false,
|
disabled = false,
|
||||||
type = 'button',
|
type = 'button',
|
||||||
onClick = () => {},
|
onClick = () => {},
|
||||||
className: extraClasses = '',
|
className: extraClasses = '',
|
||||||
children,
|
children,
|
||||||
|
secondary = false,
|
||||||
}: PropsWithChildren<PrimaryButtonProps>) {
|
}: PropsWithChildren<PrimaryButtonProps>) {
|
||||||
|
const klass = secondary ? 'secondary-button' : 'primary-button'
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
type={type}
|
type={type}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className={`primary-button ${extraClasses}`}
|
className={`${klass} ${extraClasses}`}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</button>
|
</button>
|
28
src/components/buttons/LinkButton.tsx
Normal file
28
src/components/buttons/LinkButton.tsx
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { PropsWithChildren } from 'react'
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
|
interface LinkButtonProps {
|
||||||
|
to: string | To
|
||||||
|
className?: string
|
||||||
|
secondary?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
interface To {
|
||||||
|
pathname: string
|
||||||
|
search?: string
|
||||||
|
hash?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function LinkButton({
|
||||||
|
to,
|
||||||
|
className: extraClasses = '',
|
||||||
|
secondary = false,
|
||||||
|
children,
|
||||||
|
}: PropsWithChildren<LinkButtonProps>) {
|
||||||
|
const klass = secondary ? 'secondary-button' : 'primary-button'
|
||||||
|
return (
|
||||||
|
<Link to={to} className={`${klass} text-center ${extraClasses}`}>
|
||||||
|
{children}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,10 +1,16 @@
|
||||||
import { PropsWithChildren } from 'react'
|
import { PropsWithChildren } from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
interface NavLinkButtonProps {
|
interface NavLinkButtonProps {
|
||||||
to: string
|
to: string | To
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NavLinkButton({ to, children }: PropsWithChildren<NavLinkButtonProps>) {
|
interface To {
|
||||||
|
pathname: string
|
||||||
|
search?: string
|
||||||
|
hash?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function NavButton({ to, children }: PropsWithChildren<NavLinkButtonProps>) {
|
||||||
return (
|
return (
|
||||||
<Link className={`text-primary-500`} to={to}>
|
<Link className={`text-primary-500`} to={to}>
|
||||||
{children}
|
{children}
|
Loading…
Add table
Add a link
Reference in a new issue