mirror of
https://github.com/koreanbots/core.git
synced 2025-12-15 06:10:22 +00:00
types: improved component typing
This commit is contained in:
parent
74fa828189
commit
3b52e4c386
@ -1,7 +1,7 @@
|
||||
import { useEffect } from 'react'
|
||||
import Logger from '@utils/Logger'
|
||||
|
||||
const Advertisement = ({ size = 'short' }: AdvertisementProps): JSX.Element => {
|
||||
const Advertisement: React.FC<AdvertisementProps> = ({ size = 'short' }) => {
|
||||
useEffect(() => {
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
window.adsbygoogle = window.adsbygoogle || []
|
||||
|
||||
@ -3,15 +3,14 @@ import Link from 'next/link'
|
||||
|
||||
const DiscordAvatar = dynamic(() => import('@components/DiscordAvatar'))
|
||||
|
||||
const Application = ({ type, id, name }: ApplicationProps): JSX.Element => {
|
||||
return (
|
||||
<Link href={`/developers/applications/${type + 's'}/${id}`}>
|
||||
<div className='relative px-2 py-4 text-center dark:bg-discord-black bg-little-white rounded-lg cursor-pointer transform hover:-translate-y-1 transition duration-100 ease-in'>
|
||||
<DiscordAvatar userID={id} className='px-2 w-full rounded-xl' />
|
||||
<h2 className='pt-2 whitespace-nowrap text-xl font-medium truncate'>{name}</h2>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
const Application: React.FC<ApplicationProps> = ({ type, id, name }) => {
|
||||
return <Link href={`/developers/applications/${type + 's'}/${id}`}>
|
||||
<div className='relative px-2 py-4 text-center dark:bg-discord-black bg-little-white rounded-lg cursor-pointer transform hover:-translate-y-1 transition duration-100 ease-in'>
|
||||
<DiscordAvatar userID={id} className='px-2 w-full rounded-xl' />
|
||||
<h2 className='pt-2 whitespace-nowrap text-xl font-medium truncate'>{name}</h2>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
}
|
||||
|
||||
interface ApplicationProps {
|
||||
|
||||
@ -9,115 +9,114 @@ const Divider = dynamic(() => import('@components/Divider'))
|
||||
const Tag = dynamic(() => import('@components/Tag'))
|
||||
const DiscordAvatar = dynamic(() => import('@components/DiscordAvatar'))
|
||||
|
||||
const BotCard = ({ manage = false, bot }: BotProps): JSX.Element => {
|
||||
return (
|
||||
<div className='container mb-16 transform hover:-translate-y-1 transition duration-100 ease-in cursor-pointer'>
|
||||
<div className='relative'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='h-full'>
|
||||
<div
|
||||
className='relative mx-auto h-full text-black dark:text-white dark:bg-discord-black bg-little-white rounded-2xl shadow-xl'
|
||||
style={
|
||||
checkBotFlag(bot.flags, 'trusted') && bot.banner
|
||||
? {
|
||||
background: `linear-gradient(to right, rgba(34, 36, 38, 0.68), rgba(34, 36, 38, 0.68)), url("${bot.banner}") center top / cover no-repeat`,
|
||||
color: 'white',
|
||||
}
|
||||
: {}
|
||||
}
|
||||
>
|
||||
<Link href={makeBotURL(bot)}>
|
||||
<div>
|
||||
<div className='flex h-44'>
|
||||
<div className='w-3/5'>
|
||||
<div className='flex justify-start'>
|
||||
<DiscordAvatar
|
||||
size={128}
|
||||
userID={bot.id}
|
||||
alt='Avatar'
|
||||
className='absolute -left-2 -top-8 mx-auto w-32 h-32 bg-white rounded-full'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-28 px-4'>
|
||||
<h2 className='px-1 text-sm'>
|
||||
<i className={`fas fa-circle text-${Status[bot.status]?.color}`} />
|
||||
{Status[bot.status]?.text}
|
||||
</h2>
|
||||
<h1 className='mb-3 text-left text-2xl font-bold truncate'>{bot.name}</h1>
|
||||
</div>
|
||||
const BotCard: React.FC<BotCardProps> = ({ manage = false, bot }) => {
|
||||
return <div className='container mb-16 transform hover:-translate-y-1 transition duration-100 ease-in cursor-pointer'>
|
||||
<div className='relative'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='h-full'>
|
||||
<div
|
||||
className='relative mx-auto h-full text-black dark:text-white dark:bg-discord-black bg-little-white rounded-2xl shadow-xl'
|
||||
style={
|
||||
checkBotFlag(bot.flags, 'trusted') && bot.banner
|
||||
? {
|
||||
background: `linear-gradient(to right, rgba(34, 36, 38, 0.68), rgba(34, 36, 38, 0.68)), url("${bot.banner}") center top / cover no-repeat`,
|
||||
color: 'white',
|
||||
}
|
||||
: {}
|
||||
}
|
||||
>
|
||||
<Link href={makeBotURL(bot)}>
|
||||
<div>
|
||||
<div className='flex h-44'>
|
||||
<div className='w-3/5'>
|
||||
<div className='flex justify-start'>
|
||||
<DiscordAvatar
|
||||
size={128}
|
||||
userID={bot.id}
|
||||
alt='Avatar'
|
||||
className='absolute -left-2 -top-8 mx-auto w-32 h-32 bg-white rounded-full'
|
||||
/>
|
||||
</div>
|
||||
<div className='grid grid-cols-1 pr-5 py-5 w-2/5 h-0'>
|
||||
<Tag
|
||||
text={
|
||||
<>
|
||||
<i className='fas fa-heart text-red-600' /> {formatNumber(bot.votes)}
|
||||
</>
|
||||
}
|
||||
dark
|
||||
/>
|
||||
<Tag
|
||||
blurple
|
||||
text={bot.servers ? <>{formatNumber(bot.servers)} 서버</> : 'N/A'}
|
||||
dark
|
||||
/>
|
||||
|
||||
<div className='mt-28 px-4'>
|
||||
<h2 className='px-1 text-sm'>
|
||||
<i className={`fas fa-circle text-${Status[bot.status]?.color}`} />
|
||||
{Status[bot.status]?.text}
|
||||
</h2>
|
||||
<h1 className='mb-3 text-left text-2xl font-bold truncate'>{bot.name}</h1>
|
||||
</div>
|
||||
</div>
|
||||
<p className='mb-10 px-4 h-6 text-left text-gray-400 text-sm font-medium'>
|
||||
{bot.intro}
|
||||
</p>
|
||||
<div>
|
||||
<div className='category flex flex-wrap px-2'>
|
||||
{bot.category.slice(0, 3).map(el => (
|
||||
<Tag key={el} text={el} href={`/categories/${el}`} dark />
|
||||
))}{' '}
|
||||
{bot.category.length > 3 && <Tag text={`+${bot.category.length - 3}`} dark />}
|
||||
</div>
|
||||
<div className='grid grid-cols-1 pr-5 py-5 w-2/5 h-0'>
|
||||
<Tag
|
||||
text={
|
||||
<>
|
||||
<i className='fas fa-heart text-red-600' /> {formatNumber(bot.votes)}
|
||||
</>
|
||||
}
|
||||
dark
|
||||
/>
|
||||
<Tag
|
||||
blurple
|
||||
text={bot.servers ? <>{formatNumber(bot.servers)} 서버</> : 'N/A'}
|
||||
dark
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<Divider />
|
||||
<div className='w-full'>
|
||||
<div className='flex justify-evenly'>
|
||||
<Link href={makeBotURL(bot)}>
|
||||
<a className='py-3 w-full text-center text-koreanbots-blue hover:text-white text-sm font-bold hover:bg-koreanbots-blue rounded-bl-2xl hover:shadow-lg transition duration-100 ease-in'>
|
||||
<p className='mb-10 px-4 h-6 text-left text-gray-400 text-sm font-medium'>
|
||||
{bot.intro}
|
||||
</p>
|
||||
<div>
|
||||
<div className='category flex flex-wrap px-2'>
|
||||
{bot.category.slice(0, 3).map(el => (
|
||||
<Tag key={el} text={el} href={`/categories/${el}`} dark />
|
||||
))}{' '}
|
||||
{bot.category.length > 3 && <Tag text={`+${bot.category.length - 3}`} dark />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<Divider />
|
||||
<div className='w-full'>
|
||||
<div className='flex justify-evenly'>
|
||||
<Link href={makeBotURL(bot)}>
|
||||
<a className='py-3 w-full text-center text-koreanbots-blue hover:text-white text-sm font-bold hover:bg-koreanbots-blue rounded-bl-2xl hover:shadow-lg transition duration-100 ease-in'>
|
||||
보기
|
||||
</a>
|
||||
</Link>
|
||||
{manage ? (
|
||||
<Link href={`/manage/${bot.id}`}>
|
||||
<a className='py-3 w-full text-center text-green-500 hover:text-white text-sm font-bold hover:bg-green-500 rounded-br-2xl hover:shadow-lg transition duration-100 ease-in'>
|
||||
관리하기
|
||||
</a>
|
||||
</Link>
|
||||
{manage ? (
|
||||
<Link href={`/manage/${bot.id}`}>
|
||||
<a className='py-3 w-full text-center text-green-500 hover:text-white text-sm font-bold hover:bg-green-500 rounded-br-2xl hover:shadow-lg transition duration-100 ease-in'>
|
||||
관리하기
|
||||
</a>
|
||||
</Link>
|
||||
) : bot.state !== 'ok' ? <a
|
||||
className='py-3 w-full text-center text-discord-blurple text-sm font-bold rounded-br-2xl hover:shadow-lg transition duration-100 ease-in opacity-50 cursor-default select-none'
|
||||
>
|
||||
) : bot.state !== 'ok' ? <a
|
||||
className='py-3 w-full text-center text-discord-blurple text-sm font-bold rounded-br-2xl hover:shadow-lg transition duration-100 ease-in opacity-50 cursor-default select-none'
|
||||
>
|
||||
초대하기
|
||||
</a> :
|
||||
<a
|
||||
href={
|
||||
bot.url ||
|
||||
</a> :
|
||||
<a
|
||||
href={
|
||||
bot.url ||
|
||||
`https://discordapp.com/oauth2/authorize?client_id=${bot.id}&scope=bot&permissions=0`
|
||||
}
|
||||
rel='noopener noreferrer'
|
||||
target='_blank'
|
||||
className='py-3 w-full text-center text-discord-blurple hover:text-white text-sm font-bold hover:bg-discord-blurple rounded-br-2xl hover:shadow-lg transition duration-100 ease-in'
|
||||
>
|
||||
}
|
||||
rel='noopener noreferrer'
|
||||
target='_blank'
|
||||
className='py-3 w-full text-center text-discord-blurple hover:text-white text-sm font-bold hover:bg-discord-blurple rounded-br-2xl hover:shadow-lg transition duration-100 ease-in'
|
||||
>
|
||||
초대하기
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
</div>
|
||||
|
||||
}
|
||||
|
||||
interface BotProps {
|
||||
interface BotCardProps {
|
||||
manage?: boolean
|
||||
bot: Bot
|
||||
}
|
||||
|
||||
@ -1,25 +1,23 @@
|
||||
import Link from 'next/link'
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
const Button = ({
|
||||
const Button: React.FC<ButtonProps> = ({
|
||||
type = 'button',
|
||||
className,
|
||||
children,
|
||||
href,
|
||||
disabled=false,
|
||||
onClick,
|
||||
}: ButtonProps): JSX.Element => {
|
||||
return href ? (
|
||||
<Link href={!disabled && href}>
|
||||
<a
|
||||
className={`cursor-pointer rounded-md px-4 py-2 transition duration-300 ease select-none outline-none foucs:outline-none mr-1.5 ${className ??
|
||||
}) => {
|
||||
return href ? <Link href={!disabled && href}>
|
||||
<a
|
||||
className={`cursor-pointer rounded-md px-4 py-2 transition duration-300 ease select-none outline-none foucs:outline-none mr-1.5 ${className ??
|
||||
'bg-discord-blurple hover:opacity-80 dark:bg-very-black dark:hover:bg-discord-dark-hover text-white'}`}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
</Link>
|
||||
) : onClick ? (
|
||||
<button
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
</Link>
|
||||
: onClick ? <button
|
||||
type={disabled ? 'button' : type}
|
||||
onClick={disabled ? null : onClick}
|
||||
className={`cursor-pointer rounded-md px-4 py-2 transition duration-300 ease select-none outline-none foucs:outline-none mr-1.5 ${className ??
|
||||
@ -27,15 +25,14 @@ const Button = ({
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
type={disabled ? 'button' : type}
|
||||
className={`cursor-pointer rounded-md px-4 py-2 transition duration-300 ease select-none outline-none foucs:outline-none mr-1.5 ${className ??
|
||||
:
|
||||
<button
|
||||
type={disabled ? 'button' : type}
|
||||
className={`cursor-pointer rounded-md px-4 py-2 transition duration-300 ease select-none outline-none foucs:outline-none mr-1.5 ${className ??
|
||||
'bg-discord-blurple hover:opacity-80 dark:bg-very-black dark:hover:bg-discord-dark-hover text-white'}`}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
)
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
}
|
||||
|
||||
interface ButtonProps {
|
||||
|
||||
@ -2,7 +2,7 @@ import { Ref } from 'react'
|
||||
import HCaptcha from '@hcaptcha/react-hcaptcha'
|
||||
|
||||
|
||||
const Captcha = ({ dark, onVerify }:CaptchaProps):JSX.Element => {
|
||||
const Captcha: React.FC<CaptchaProps> = ({ dark, onVerify }) => {
|
||||
return <HCaptcha sitekey='43e556b4-cc90-494f-b100-378b906bb736' theme={dark ? 'dark' : 'light'} onVerify={onVerify}/>
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
const ColorCard = ({ header, first, second, className }: ColorCardProps): JSX.Element => {
|
||||
const ColorCard: React.FC<ColorCardProps> = ({ header, first, second, className }) => {
|
||||
return (
|
||||
<div className={`rounded-lg p-10 ${className} shadow-lg`}>
|
||||
<h2 className='text-2xl font-bold'>{header}</h2>
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
const Container = ({
|
||||
const Container: React.FC<ContainerProps> = ({
|
||||
ignoreColor,
|
||||
className,
|
||||
paddingTop = false,
|
||||
children,
|
||||
}: ContainerProps): JSX.Element => {
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={`${ignoreColor ? '' : 'text-black dark:text-gray-100'} ${
|
||||
|
||||
@ -9,7 +9,7 @@ const Container = dynamic(() => import('@components/Container'))
|
||||
const Divider = dynamic(() => import('@components/Divider'))
|
||||
const SEO = dynamic(() => import('@components/SEO'))
|
||||
|
||||
const DeveloperLayout = ({ children, enabled, docs, currentDoc }:DeveloperLayout):JSX.Element => {
|
||||
const DeveloperLayout: React.FC<DeveloperLayout> = ({ children, enabled, docs, currentDoc }:DeveloperLayout) => {
|
||||
const [ navbarEnabled, setNavbarOpen ] = useState(false)
|
||||
|
||||
return <div className='flex min-h-screen'>
|
||||
|
||||
@ -3,12 +3,7 @@ import { KoreanbotsEndPoints } from '@utils/Constants'
|
||||
import { supportsWebP } from '@utils/Tools'
|
||||
import Logger from '@utils/Logger'
|
||||
|
||||
const DiscordAvatar = (props: {
|
||||
alt?: string
|
||||
userID: string
|
||||
className?: string
|
||||
size? : 128 | 256 | 512
|
||||
}) => {
|
||||
const DiscordAvatar: React.FC<DiscordAvatarProps> = props => {
|
||||
const fallback = '/img/default.png'
|
||||
const [ webpUnavailable, setWebpUnavailable ] = useState<boolean>()
|
||||
|
||||
@ -46,7 +41,12 @@ const DiscordAvatar = (props: {
|
||||
/>
|
||||
}
|
||||
|
||||
export default DiscordAvatar
|
||||
interface DiscordAvatarProps {
|
||||
alt?: string
|
||||
userID: string
|
||||
className?: string
|
||||
size? : 128 | 256 | 512
|
||||
}
|
||||
|
||||
interface ImageEvent extends Event {
|
||||
target: ImageTarget
|
||||
@ -55,4 +55,6 @@ interface ImageEvent extends Event {
|
||||
interface ImageTarget extends EventTarget {
|
||||
src: string
|
||||
onerror: (event: SyntheticEvent<HTMLImageElement, ImageEvent>) => void
|
||||
}
|
||||
}
|
||||
|
||||
export default DiscordAvatar
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
const Divider = ({ className }: { className?: string }) => {
|
||||
const Divider: React.FC<DividerProps> = ({ className }) => {
|
||||
return (
|
||||
<div
|
||||
className={`my-2 px-5 ${className || ''}`}
|
||||
@ -10,4 +10,8 @@ const Divider = ({ className }: { className?: string }) => {
|
||||
)
|
||||
}
|
||||
|
||||
interface DividerProps {
|
||||
className?: string
|
||||
}
|
||||
|
||||
export default Divider
|
||||
|
||||
@ -2,7 +2,8 @@ import dynamic from 'next/dynamic'
|
||||
|
||||
const Container = dynamic(() => import('@components/Container'))
|
||||
const SEO = dynamic(() => import('@components/SEO'))
|
||||
const Docs = ({ title, header, description, subheader, children }: DocsProps): JSX.Element => {
|
||||
|
||||
const Docs: React.FC<DocsProps> = ({ title, header, description, subheader, children }) => {
|
||||
return (
|
||||
<>
|
||||
<SEO
|
||||
@ -32,9 +33,9 @@ const Docs = ({ title, header, description, subheader, children }: DocsProps): J
|
||||
export default Docs
|
||||
|
||||
interface DocsProps {
|
||||
header: string | JSX.Element
|
||||
header: string | React.ReactNode
|
||||
title?: string
|
||||
description?: string
|
||||
subheader?: string
|
||||
children: JSX.Element | JSX.Element[]
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ import { Theme } from '@types'
|
||||
const Container = dynamic(() => import('@components/Container'))
|
||||
const Toggle = dynamic(() => import('@components/Toggle'))
|
||||
|
||||
const Footer = ({ theme, setTheme }: FooterProps): JSX.Element => {
|
||||
const Footer: React.FC<FooterProps> = ({ theme, setTheme }) => {
|
||||
return (
|
||||
<div className='releative z-30'>
|
||||
<div className='bottom-0 text-white bg-discord-black py-24'>
|
||||
|
||||
@ -6,7 +6,7 @@ import { ErrorText } from '@utils/Constants'
|
||||
const SEO = dynamic(() => import('@components/SEO'))
|
||||
const Button = dynamic(() => import('@components/Button'))
|
||||
|
||||
const Forbidden = ():JSX.Element => {
|
||||
const Forbidden:React.FC = () => {
|
||||
const router = useRouter()
|
||||
return <>
|
||||
<SEO title='권한이 없습니다' />
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Field } from 'formik'
|
||||
|
||||
const CheckBox = ({ name, ...props }: CheckBoxProps): JSX.Element => {
|
||||
const CheckBox: React.FC<CheckBoxProps> = ({ name, ...props }) => {
|
||||
return <Field type='checkbox' name={name} className='form-checkbox text-koreanbots-blue bg-gray-300 h-4 w-4 rounded' {...props} />
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Field } from 'formik'
|
||||
|
||||
const CsrfToken = ({ token }: CsrfTokenProps): JSX.Element => {
|
||||
const CsrfToken: React.FC<CsrfTokenProps> = ({ token }) => {
|
||||
return <Field name='_csrf' hidden value={token} readOnly />
|
||||
}
|
||||
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
import { Field } from 'formik'
|
||||
|
||||
const Input = ({ name, placeholder }: InputProps): JSX.Element => {
|
||||
return (
|
||||
<Field
|
||||
name={name}
|
||||
className='border-grey-light relative px-3 w-full h-10 text-black dark:text-white dark:bg-very-black border dark:border-transparent rounded outline-none'
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
)
|
||||
const Input: React.FC<InputProps> = ({ name, placeholder, ...props }) => {
|
||||
return <Field
|
||||
{...props}
|
||||
name={name}
|
||||
className='border-grey-light relative px-3 w-full h-10 text-black dark:text-white dark:bg-very-black border dark:border-transparent rounded outline-none'
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
}
|
||||
|
||||
interface InputProps {
|
||||
name: string
|
||||
placeholder?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export default Input
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
const Label = ({
|
||||
const Label: React.FC<LabelProps> = ({
|
||||
For,
|
||||
children,
|
||||
label,
|
||||
@ -7,31 +7,27 @@ const Label = ({
|
||||
grid = true,
|
||||
short = false,
|
||||
required = false,
|
||||
}: LabelProps): JSX.Element => {
|
||||
return (
|
||||
<>
|
||||
<label
|
||||
className={grid ? 'grid grid-cols-1 xl:grid-cols-4 gap-2 my-4' : 'inline-flex items-center'}
|
||||
htmlFor={For}
|
||||
>
|
||||
{label && (
|
||||
<div className='col-span-1 text-sm'>
|
||||
<h3 className='text-koreanbots-blue text-lg font-bold'>
|
||||
{label}
|
||||
{required && (
|
||||
<span className='align-text-top text-red-500 text-base font-semibold'> *</span>
|
||||
)}
|
||||
</h3>
|
||||
{labelDesc}
|
||||
</div>
|
||||
)}
|
||||
<div className={short ? 'col-span-1' : 'col-span-3'}>
|
||||
{children}
|
||||
<div className='mt-1 text-red-500 text-xs font-light'>{error}</div>
|
||||
</div>
|
||||
</label>
|
||||
</>
|
||||
)
|
||||
}) => {
|
||||
return <label
|
||||
className={grid ? 'grid grid-cols-1 xl:grid-cols-4 gap-2 my-4' : 'inline-flex items-center'}
|
||||
htmlFor={For}
|
||||
>
|
||||
{label && (
|
||||
<div className='col-span-1 text-sm'>
|
||||
<h3 className='text-koreanbots-blue text-lg font-bold'>
|
||||
{label}
|
||||
{required && (
|
||||
<span className='align-text-top text-red-500 text-base font-semibold'> *</span>
|
||||
)}
|
||||
</h3>
|
||||
{labelDesc}
|
||||
</div>
|
||||
)}
|
||||
<div className={short ? 'col-span-1' : 'col-span-3'}>
|
||||
{children}
|
||||
<div className='mt-1 text-red-500 text-xs font-light'>{error}</div>
|
||||
</div>
|
||||
</label>
|
||||
}
|
||||
|
||||
interface LabelProps {
|
||||
|
||||
@ -1,32 +1,30 @@
|
||||
import ReactSelect from 'react-select'
|
||||
|
||||
const Select = ({ placeholder, options, handleChange, handleTouch, value }: SelectProps): JSX.Element => {
|
||||
return (
|
||||
<ReactSelect
|
||||
styles={{
|
||||
control: provided => {
|
||||
return { ...provided, border: 'none' }
|
||||
},
|
||||
option: provided => {
|
||||
return {
|
||||
...provided,
|
||||
cursor: 'pointer',
|
||||
':hover': {
|
||||
opacity: '0.7',
|
||||
},
|
||||
}
|
||||
},
|
||||
}}
|
||||
className='border-grey-light border dark:border-transparent rounded'
|
||||
classNamePrefix='outline-none text-black dark:bg-very-black dark:text-white '
|
||||
placeholder={placeholder || '선택해주세요.'}
|
||||
options={options}
|
||||
onChange={handleChange}
|
||||
onBlur={handleTouch}
|
||||
noOptionsMessage={() => '검색 결과가 없습니다.'}
|
||||
defaultValue={value}
|
||||
/>
|
||||
)
|
||||
const Select: React.FC<SelectProps> = ({ placeholder, options, handleChange, handleTouch, value }) => {
|
||||
return <ReactSelect
|
||||
styles={{
|
||||
control: provided => {
|
||||
return { ...provided, border: 'none' }
|
||||
},
|
||||
option: provided => {
|
||||
return {
|
||||
...provided,
|
||||
cursor: 'pointer',
|
||||
':hover': {
|
||||
opacity: '0.7',
|
||||
},
|
||||
}
|
||||
},
|
||||
}}
|
||||
className='border-grey-light border dark:border-transparent rounded'
|
||||
classNamePrefix='outline-none text-black dark:bg-very-black dark:text-white '
|
||||
placeholder={placeholder || '선택해주세요.'}
|
||||
options={options}
|
||||
onChange={handleChange}
|
||||
onBlur={handleTouch}
|
||||
noOptionsMessage={() => '검색 결과가 없습니다.'}
|
||||
defaultValue={value}
|
||||
/>
|
||||
}
|
||||
|
||||
interface SelectProps {
|
||||
|
||||
@ -31,7 +31,7 @@ const SortableMultiValueLabel = SortableHandle(props => (
|
||||
|
||||
const SortableSelect = SortableContainer(ReactSelect)
|
||||
|
||||
const Select = ({ placeholder, options, values, setValues, handleChange, handleTouch }:SelectProps):JSX.Element => {
|
||||
const Select: React.FC<SelectProps> = ({ placeholder, options, values, setValues, handleChange, handleTouch }) => {
|
||||
const onSortEnd = ({ oldIndex, newIndex }) => {
|
||||
const newValue = arrayMove(values, oldIndex, newIndex)
|
||||
setValues(newValue)
|
||||
|
||||
@ -10,7 +10,7 @@ import 'emoji-mart/css/emoji-mart.css'
|
||||
|
||||
|
||||
|
||||
const TextArea = ({ name, placeholder, theme='auto', setValue, value }:TextAreaProps):JSX.Element => {
|
||||
const TextArea: React.FC<TextAreaProps> = ({ name, placeholder, theme='auto', setValue, value }) => {
|
||||
const ref = useRef()
|
||||
const [ emojiPickerHidden, setEmojiPickerHidden ] = useState(true)
|
||||
useOutsideClick(ref, () => {
|
||||
|
||||
@ -6,7 +6,7 @@ const Container = dynamic(()=> import('@components/Container'))
|
||||
const Tag = dynamic(()=> import('@components/Tag'))
|
||||
const Search = dynamic(()=> import('@components/Search'))
|
||||
|
||||
const Hero = ({ header, description }:HeroProps):JSX.Element => {
|
||||
const Hero:React.FC<HeroProps> = ({ header, description }) => {
|
||||
return <>
|
||||
<div className='dark:bg-discord-black bg-discord-blurple text-gray-100 md:p-0 mb-8'>
|
||||
<Container className='pt-24 pb-16 md:pb-20' ignoreColor>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
const Loader = ({ text, visible = true }: LoaderProps): JSX.Element => {
|
||||
const Loader: React.FC<LoaderProps> = ({ text, visible = true }) => {
|
||||
return (
|
||||
<div
|
||||
className={`${
|
||||
@ -13,7 +13,7 @@ const Loader = ({ text, visible = true }: LoaderProps): JSX.Element => {
|
||||
}
|
||||
|
||||
interface LoaderProps {
|
||||
text: string | JSX.Element
|
||||
text: string | React.ReactNode
|
||||
visible?: boolean
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { redirectTo } from '@utils/Tools'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
import { redirectTo } from '@utils/Tools'
|
||||
|
||||
const Login: React.FC = ({ children }) => {
|
||||
const router = useRouter()
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||
import Link from 'next/link'
|
||||
|
||||
const LongButton = ({ children, newTab=false, href, onClick, center=false }:LongButtonProps):JSX.Element => {
|
||||
const LongButton: React.FC<LongButtonProps> = ({ children, newTab=false, href, onClick, center=false }) => {
|
||||
if(href) {
|
||||
if(newTab) return <a href={href} rel='noopener noreferrer'
|
||||
target='_blank'>
|
||||
|
||||
@ -5,7 +5,7 @@ import { FunctionComponent } from 'react'
|
||||
|
||||
import { anchorHeader, customEmoji, twemoji } from '@utils/Tools'
|
||||
|
||||
const Markdown = ({ text, options={}, allowedTag=[], components={} }: MarkdownProps): JSX.Element => {
|
||||
const Markdown: React.FC<MarkdownProps> = ({ text, options={}, allowedTag=[], components={} }) => {
|
||||
return (
|
||||
<div className='markdown-body w-full'>
|
||||
<MarkdownView
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { MessageColor } from '@utils/Constants'
|
||||
import Markdown from './Markdown'
|
||||
|
||||
const Message = ({ type, children }: MessageProps): JSX.Element => {
|
||||
const Message: React.FC<MessageProps> = ({ type, children }) => {
|
||||
return (
|
||||
<div
|
||||
className={`${MessageColor[type]} px-6 py-4 rounded-md text-base mx-auto w-full text-left`}
|
||||
|
||||
@ -2,7 +2,7 @@ import { ReactNode } from 'react'
|
||||
import { Modal as ReactModal } from 'react-responsive-modal'
|
||||
import 'react-responsive-modal/styles.css'
|
||||
|
||||
const Modal = ({ children, isOpen, onClose, closeIcon=false, dark, header, full=false }: ModalProps): JSX.Element => {
|
||||
const Modal: React.FC<ModalProps> = ({ children, isOpen, onClose, closeIcon=false, dark, header, full=false }) => {
|
||||
return (
|
||||
<ReactModal
|
||||
open={isOpen}
|
||||
|
||||
@ -3,7 +3,7 @@ import dynamic from 'next/dynamic'
|
||||
const Button = dynamic(() => import('@components/Button'))
|
||||
const Container = dynamic(() => import('@components/Container'))
|
||||
|
||||
const NSFW = ({ onClick, onDisableClick }:NSFWProps): JSX.Element => {
|
||||
const NSFW: React.FC<NSFWProps> = ({ onClick, onDisableClick }) => {
|
||||
return <Container>
|
||||
<div className='flex items-center h-screen select-none'>
|
||||
<div className='px-10'>
|
||||
|
||||
@ -3,15 +3,16 @@
|
||||
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||
import { useEffect, useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
import { redirectTo } from '@utils/Tools'
|
||||
import Fetch from '@utils/Fetch'
|
||||
import { User, UserCache } from '@types'
|
||||
|
||||
import DiscordAvatar from '@components/DiscordAvatar'
|
||||
import Fetch from '@utils/Fetch'
|
||||
const DiscordAvatar = dynamic(() => import('@components/DiscordAvatar'))
|
||||
|
||||
const Navbar = ({ token, pwa }:{ token: string, pwa: boolean }): JSX.Element => {
|
||||
const Navbar: React.FC<NavbarProps> = ({ token, pwa }) => {
|
||||
const [userCache, setUserCache] = useState<UserCache>()
|
||||
const [navbarOpen, setNavbarOpen] = useState<boolean>(false)
|
||||
const [dropdownOpen, setDropdownOpen] = useState<boolean>(false)
|
||||
@ -213,4 +214,9 @@ const Navbar = ({ token, pwa }:{ token: string, pwa: boolean }): JSX.Element =>
|
||||
)
|
||||
}
|
||||
|
||||
interface NavbarProps {
|
||||
token: string
|
||||
pwa: boolean
|
||||
}
|
||||
|
||||
export default Navbar
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
const Notice = ({ header, desc }: NoticeProps) => {
|
||||
const Notice: React.FC<NoticeProps> = ({ header, desc }) => {
|
||||
return (
|
||||
<div className='mx-auto my-auto px-10 py-48 h-screen text-center'>
|
||||
<h1 className='text-4xl font-bold'>KOREANBOTS</h1>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import Link from 'next/link'
|
||||
import DiscordAvatar from '@components/DiscordAvatar'
|
||||
|
||||
const Owner = ({ id, username, tag }: OwnerProps): JSX.Element => {
|
||||
const Owner: React.FC<OwnerProps> = ({ id, username, tag }) => {
|
||||
return (
|
||||
<Link href={`/users/${id}`}>
|
||||
<a className='dark:hover:bg-discord-dark-hover flex mb-1 px-4 py-4 text-black dark:text-gray-400 text-base dark:bg-discord-black bg-little-white hover:bg-little-white-hover rounded cursor-pointer'>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import Link from 'next/link'
|
||||
const Paginator = ({ currentPage, totalPage, pathname, searchParams }: PaginatorProps): JSX.Element => {
|
||||
const Paginator: React.FC<PaginatorProps> = ({ currentPage, totalPage, pathname, searchParams }) => {
|
||||
let pages = []
|
||||
if (currentPage < 4)
|
||||
pages = [
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
const PlatformDisplay = ({ osx, children }:PlatformDisplayProps): JSX.Element => {
|
||||
const PlatformDisplay: React.FC<PlatformDisplayProps> = ({ osx, children }:PlatformDisplayProps) => {
|
||||
const isOSX = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)
|
||||
return <>{isOSX ? osx ?? children : children}</>
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ import { redirectTo } from '@utils/Tools'
|
||||
|
||||
const Container = dynamic(() => import('@components/Container'))
|
||||
|
||||
const Redirect = ({ to, text=true, children }:RedirectProps):JSX.Element => {
|
||||
const Redirect: React.FC<RedirectProps> = ({ to, text=true, children }) => {
|
||||
const router = useRouter()
|
||||
if(!to) throw new Error('No Link')
|
||||
useEffect(() => {
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
const ResponsiveGrid = ({ children }:{ children: ReactNode }):JSX.Element => {
|
||||
const ResponsiveGrid: React.FC = ({ children }) => {
|
||||
return <div className='grid gap-x-4 grid-rows-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 mt-10 -mb-10'>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import Head from 'next/head'
|
||||
|
||||
const SEO = ({ title, description, image }: SEOProps): JSX.Element => {
|
||||
const SEO: React.FC<SEOProps> = ({ title, description, image }: SEOProps) => {
|
||||
return (
|
||||
<Head>
|
||||
<title>{title} - 한국 디스코드봇 리스트</title>
|
||||
|
||||
@ -11,7 +11,7 @@ import DiscordAvatar from '@components/DiscordAvatar'
|
||||
import Day from '@utils/Day'
|
||||
import useOutsideClick from '@utils/useOutsideClick'
|
||||
|
||||
const Search = (): JSX.Element => {
|
||||
const Search: React.FC = () => {
|
||||
const router = useRouter()
|
||||
const ref = useRef()
|
||||
const [query, setQuery] = useState('')
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
const Segment = ({ children, className = '' }: SegmentProps): JSX.Element => {
|
||||
const Segment: React.FC<SegmentProps> = ({ children, className = '' }) => {
|
||||
return (
|
||||
<div
|
||||
className={`py-3 px-7 text-black dark:text-white dark:bg-discord-black bg-little-white rounded ${className}`}
|
||||
|
||||
@ -5,7 +5,7 @@ import Tag from '@components/Tag'
|
||||
import { SubmittedBot } from '@types'
|
||||
import Link from 'next/link'
|
||||
|
||||
const SubmittedBotCard = ({ href, submit }: SubmittedBotProps): JSX.Element => {
|
||||
const SubmittedBotCard: React.FC<SubmittedBotProps> = ({ href, submit }) => {
|
||||
return (
|
||||
<Link href={href}>
|
||||
<a className='relative mx-auto px-4 py-5 w-full h-full text-black dark:text-white dark:bg-discord-black bg-little-white rounded-2xl shadow-xl transform hover:-translate-y-1 transition duration-100 ease-in'>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import Link from 'next/link'
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
const Tag = ({
|
||||
const Tag: React.FC<LabelProps> = ({
|
||||
blurple = false,
|
||||
github = false,
|
||||
href,
|
||||
@ -13,7 +13,7 @@ const Tag = ({
|
||||
newTab = false,
|
||||
bigger = false,
|
||||
...props
|
||||
}: LabelProps): JSX.Element => {
|
||||
}) => {
|
||||
return href ? (
|
||||
newTab ? (
|
||||
<a
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
const Toggle = ({ checked, onChange }: ToggleProps): JSX.Element => {
|
||||
const Toggle: React.FC<ToggleProps> = ({ checked, onChange }: ToggleProps) => {
|
||||
return (
|
||||
<button
|
||||
className='relative inline-block align-middle mr-2 w-10 outline-none select-none'
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import Link from 'next/link'
|
||||
|
||||
const Tooltip = ({
|
||||
const Tooltip: React.FC<TooltipProps> = ({
|
||||
href,
|
||||
size = 'small',
|
||||
children,
|
||||
direction = 'center',
|
||||
text,
|
||||
}: TooltipProps): JSX.Element => {
|
||||
}) => {
|
||||
return href ? (
|
||||
<Link href={href}>
|
||||
<a className='inline'>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
const Wave = ({ color, className }: WaveProps): JSX.Element => {
|
||||
const Wave: React.FC<WaveProps> = ({ color, className }) => {
|
||||
return (
|
||||
<svg viewBox='0 0 1440 320' className={className}>
|
||||
<path
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user