このコミットが含まれているのは:
@@ -19,7 +19,7 @@ type Props = {
|
||||
sp?: boolean }
|
||||
|
||||
|
||||
export default (({ tag, nestLevel, pathKey, parentTagId, suppressClickRef, sp }: Props) => {
|
||||
const DraggableDroppableTagRow: FC<Props> = ({ tag, nestLevel, pathKey, parentTagId, suppressClickRef, sp }) => {
|
||||
const dndId = `tag-node:${ pathKey }`
|
||||
|
||||
const downPosRef = useRef<{ x: number; y: number } | null> (null)
|
||||
@@ -96,4 +96,6 @@ export default (({ tag, nestLevel, pathKey, parentTagId, suppressClickRef, sp }:
|
||||
<TagLink tag={tag} nestLevel={nestLevel}/>
|
||||
</motion.div>
|
||||
</div>)
|
||||
}) satisfies FC<Props>
|
||||
}
|
||||
|
||||
export default DraggableDroppableTagRow
|
||||
@@ -10,7 +10,7 @@ import type { FC } from 'react'
|
||||
type Props = { status: number }
|
||||
|
||||
|
||||
export default (({ status }: Props) => {
|
||||
const ErrorScreen: FC<Props> = ({ status }) => {
|
||||
const [message, rightMsg, leftMsg]: [string, string, string] = (() => {
|
||||
switch (status)
|
||||
{
|
||||
@@ -58,4 +58,6 @@ export default (({ status }: Props) => {
|
||||
<p className="mr-[-.5em]">{message}</p>
|
||||
</div>
|
||||
</MainArea>)
|
||||
}) satisfies FC<Props>
|
||||
}
|
||||
|
||||
export default ErrorScreen
|
||||
@@ -31,7 +31,7 @@ const setChildrenById = (
|
||||
}))
|
||||
|
||||
|
||||
export default (() => {
|
||||
const MaterialSidebar: FC = () => {
|
||||
const [tags, setTags] = useState<TagWithDepth[]> ([])
|
||||
const [openTags, setOpenTags] = useState<Record<number, boolean>> ({ })
|
||||
const [tagFetchedFlags, setTagFetchedFlags] = useState<Record<number, boolean>> ({ })
|
||||
@@ -94,4 +94,6 @@ export default (() => {
|
||||
{renderTags (tags)}
|
||||
</ul>
|
||||
</SidebarComponent>)
|
||||
}) satisfies FC
|
||||
}
|
||||
|
||||
export default MaterialSidebar
|
||||
@@ -1,9 +1,11 @@
|
||||
import type { FC } from 'react'
|
||||
|
||||
|
||||
export default (() => (
|
||||
const MenuSeparator: FC = () => (
|
||||
<>
|
||||
<span className="hidden md:inline flex items-center px-2">|</span>
|
||||
<hr className="block md:hidden w-full opacity-25
|
||||
border-t border-black dark:border-white"/>
|
||||
</>)) satisfies FC
|
||||
</>)
|
||||
|
||||
export default MenuSeparator
|
||||
@@ -6,6 +6,7 @@ import Label from '@/components/common/Label'
|
||||
import { useDialogue } from '@/components/dialogues/DialogueProvider'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { toast } from '@/components/ui/use-toast'
|
||||
import { isApiError } from '@/lib/api'
|
||||
import { updatePost } from '@/lib/posts'
|
||||
|
||||
import type { FC, FormEvent } from 'react'
|
||||
@@ -32,7 +33,7 @@ type Props = { post: Post
|
||||
onSave: (newPost: Post) => void }
|
||||
|
||||
|
||||
export default (({ post, onSave }: Props) => {
|
||||
const PostEditForm: FC<Props> = ({ post, onSave }) => {
|
||||
const [disabled, setDisabled] = useState (false)
|
||||
const [originalCreatedBefore, setOriginalCreatedBefore] =
|
||||
useState<string | null> (post.originalCreatedBefore)
|
||||
@@ -62,7 +63,7 @@ export default (({ post, onSave }: Props) => {
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
const response = (e as any)?.response
|
||||
const response = isApiError<{ mergeable?: boolean }> (e) ? e.response : undefined
|
||||
|
||||
if (response?.status !== 409)
|
||||
{
|
||||
@@ -164,4 +165,6 @@ export default (({ post, onSave }: Props) => {
|
||||
更新
|
||||
</Button>
|
||||
</form>)
|
||||
}) satisfies FC<Props>
|
||||
}
|
||||
|
||||
export default PostEditForm
|
||||
@@ -16,8 +16,9 @@ type Props = {
|
||||
onMetadataChange?: (meta: NiconicoMetadata) => void }
|
||||
|
||||
|
||||
export default (({ ref, post, onLoadComplete, onMetadataChange }: Props) => {
|
||||
const PostEmbed: FC<Props> = ({ ref, post, onLoadComplete, onMetadataChange }) => {
|
||||
const dialogue = useDialogue ()
|
||||
const [framed, setFramed] = useState (false)
|
||||
|
||||
const url = new URL (post.url)
|
||||
|
||||
@@ -44,7 +45,7 @@ export default (({ ref, post, onLoadComplete, onMetadataChange }: Props) => {
|
||||
case 'twitter.com':
|
||||
case 'x.com':
|
||||
{
|
||||
const mUserId = url.pathname.match (/(?<=\/)[^\/]+?(?=\/|$|\?)/)
|
||||
const mUserId = url.pathname.match (/(?<=\/)[^/]+?(?=\/|$|\?)/)
|
||||
const mStatusId = url.pathname.match (/(?<=\/status\/)\d+?(?=\/|$|\?)/)
|
||||
if (!(mUserId) || !(mStatusId))
|
||||
break
|
||||
@@ -72,8 +73,6 @@ export default (({ ref, post, onLoadComplete, onMetadataChange }: Props) => {
|
||||
}
|
||||
}
|
||||
|
||||
const [framed, setFramed] = useState (false)
|
||||
|
||||
return (
|
||||
<>
|
||||
{framed
|
||||
@@ -101,4 +100,6 @@ export default (({ ref, post, onLoadComplete, onMetadataChange }: Props) => {
|
||||
</a>
|
||||
</div>)}
|
||||
</>)
|
||||
}) satisfies FC<Props>
|
||||
}
|
||||
|
||||
export default PostEmbed
|
||||
|
||||
@@ -36,7 +36,7 @@ type Props = Omit<ComponentPropsWithoutRef<'textarea'>, 'value' | 'onChange' | '
|
||||
setTags: (tags: string) => void }
|
||||
|
||||
|
||||
export default (({ tags, setTags, ...rest }: Props) => {
|
||||
const PostFormTagsArea: FC<Props> = ({ tags, setTags, ...rest }) => {
|
||||
const ref = useRef<HTMLTextAreaElement> (null)
|
||||
|
||||
const [bounds, setBounds] = useState<{ start: number; end: number }> ({ start: 0, end: 0 })
|
||||
@@ -97,4 +97,6 @@ export default (({ tags, setTags, ...rest }: Props) => {
|
||||
activeIndex={-1}
|
||||
onSelect={handleTagSelect}/>)}
|
||||
</div>)
|
||||
}) satisfies FC<Props>
|
||||
}
|
||||
|
||||
export default PostFormTagsArea
|
||||
@@ -14,7 +14,7 @@ type Props = { posts: Post[]
|
||||
onClick?: (event: MouseEvent<HTMLElement>) => void }
|
||||
|
||||
|
||||
export default (({ posts, onClick }: Props) => {
|
||||
const PostList: FC<Props> = ({ posts, onClick }) => {
|
||||
const location = useLocation ()
|
||||
|
||||
const setForLocationKey = useSharedTransitionStore (s => s.setForLocationKey)
|
||||
@@ -70,4 +70,6 @@ export default (({ posts, onClick }: Props) => {
|
||||
</PrefetchLink>)
|
||||
})}
|
||||
</div>)
|
||||
}) satisfies FC<Props>
|
||||
}
|
||||
|
||||
export default PostList
|
||||
@@ -12,11 +12,11 @@ type Props = {
|
||||
setOriginalCreatedBefore: (x: string | null) => void }
|
||||
|
||||
|
||||
export default (({ disabled,
|
||||
const PostOriginalCreatedTimeField: FC<Props> = ({ disabled,
|
||||
originalCreatedFrom,
|
||||
setOriginalCreatedFrom,
|
||||
originalCreatedBefore,
|
||||
setOriginalCreatedBefore }: Props) => (
|
||||
setOriginalCreatedBefore }) => (
|
||||
<div>
|
||||
<Label>オリジナルの作成日時</Label>
|
||||
<div className="my-1 flex">
|
||||
@@ -71,4 +71,6 @@ export default (({ disabled,
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>)) satisfies FC<Props>
|
||||
</div>)
|
||||
|
||||
export default PostOriginalCreatedTimeField
|
||||
@@ -13,7 +13,7 @@ export const useOverlayStore = create<OverlayStore> (set => ({
|
||||
setActive: v => set ({ active: v }) }))
|
||||
|
||||
|
||||
export default (() => {
|
||||
const RouteBlockerOverlay: FC = () => {
|
||||
const active = useOverlayStore (s => s.active)
|
||||
|
||||
useEffect (() => {
|
||||
@@ -43,4 +43,6 @@ export default (() => {
|
||||
</div>
|
||||
</div>
|
||||
</div>)
|
||||
}) satisfies FC
|
||||
}
|
||||
|
||||
export default RouteBlockerOverlay
|
||||
@@ -151,7 +151,7 @@ const DropSlot = ({ cat }: { cat: Category }) => {
|
||||
type Props = { post: Post; sp?: boolean }
|
||||
|
||||
|
||||
export default (({ post, sp }: Props) => {
|
||||
const TagDetailSidebar: FC<Props> = ({ post, sp }) => {
|
||||
sp = Boolean (sp)
|
||||
|
||||
const qc = useQueryClient ()
|
||||
@@ -376,4 +376,6 @@ export default (({ post, sp }: Props) => {
|
||||
</DragOverlay>
|
||||
</DndContext>
|
||||
</SidebarComponent>)
|
||||
}) satisfies FC<Props>
|
||||
}
|
||||
|
||||
export default TagDetailSidebar
|
||||
@@ -27,12 +27,12 @@ type Props =
|
||||
| PropsWithoutLink
|
||||
|
||||
|
||||
export default (({ tag,
|
||||
const TagLink: FC<Props> = ({ tag,
|
||||
nestLevel = 0,
|
||||
linkFlg = true,
|
||||
withWiki = true,
|
||||
withCount = true,
|
||||
...props }: Props) => {
|
||||
...props }) => {
|
||||
const spanClass = cn (
|
||||
`text-${ TAG_COLOUR[tag.category] }-${ LIGHT_COLOUR_SHADE }`,
|
||||
`dark:text-${ TAG_COLOUR[tag.category] }-${ DARK_COLOUR_SHADE }`)
|
||||
@@ -126,4 +126,6 @@ export default (({ tag,
|
||||
{withCount && (
|
||||
<span className="ml-1">{tag.postCount}</span>)}
|
||||
</>)
|
||||
}) satisfies FC<Props>
|
||||
}
|
||||
|
||||
export default TagLink
|
||||
@@ -12,7 +12,7 @@ import type { ChangeEvent, FC, KeyboardEvent } from 'react'
|
||||
import type { Tag } from '@/types'
|
||||
|
||||
|
||||
export default (() => {
|
||||
const TagSearch: FC = () => {
|
||||
const location = useLocation ()
|
||||
const navigate = useNavigate ()
|
||||
|
||||
@@ -115,4 +115,6 @@ export default (() => {
|
||||
activeIndex={activeIndex}
|
||||
onSelect={handleTagSelect}/>
|
||||
</div>)
|
||||
}) satisfies FC
|
||||
}
|
||||
|
||||
export default TagSearch
|
||||
@@ -10,7 +10,7 @@ type Props = { suggestions: Tag[]
|
||||
onSelect: (tag: Tag) => void }
|
||||
|
||||
|
||||
export default (({ suggestions, activeIndex, onSelect }: Props) => {
|
||||
const TagSearchBox: FC<Props> = ({ suggestions, activeIndex, onSelect }) => {
|
||||
if (suggestions.length === 0)
|
||||
return
|
||||
|
||||
@@ -26,4 +26,6 @@ export default (({ suggestions, activeIndex, onSelect }: Props) => {
|
||||
<TagLink tag={tag} linkFlg={false} withWiki={false}/>
|
||||
</li>))}
|
||||
</ul>)
|
||||
}) satisfies FC<Props>
|
||||
}
|
||||
|
||||
export default TagSearchBox
|
||||
@@ -19,7 +19,7 @@ type Props = { posts: Post[]
|
||||
onClick?: (event: MouseEvent<HTMLElement>) => void }
|
||||
|
||||
|
||||
export default (({ posts, onClick }: Props) => {
|
||||
const TagSidebar: FC<Props> = ({ posts, onClick }) => {
|
||||
const navigate = useNavigate ()
|
||||
|
||||
const [tagsVsbl, setTagsVsbl] = useState (false)
|
||||
@@ -126,4 +126,6 @@ export default (({ posts, onClick }: Props) => {
|
||||
{tagsVsbl ? '▲▲▲ タグ一覧を閉じる ▲▲▲' : '▼▼▼ タグ一覧を表示 ▼▼▼'}
|
||||
</a>
|
||||
</SidebarComponent>)
|
||||
}) satisfies FC<Props>
|
||||
}
|
||||
|
||||
export default TagSidebar
|
||||
@@ -26,7 +26,7 @@ export const menuOutline = ({ tag, wikiId, user, pathName }: {
|
||||
pathName: string }): Menu => {
|
||||
const postCount = tag?.postCount ?? 0
|
||||
|
||||
const wikiPageFlg = Boolean (/^\/wiki\/(?!new|changes)[^\/]+/.test (pathName) && wikiId)
|
||||
const wikiPageFlg = Boolean (/^\/wiki\/(?!new|changes)[^/]+/.test (pathName) && wikiId)
|
||||
const wikiTitle = pathName.split ('/')[2] ?? ''
|
||||
|
||||
const tagFlg = /^\/tags\/\d+/.test (pathName)
|
||||
@@ -80,7 +80,7 @@ export const menuOutline = ({ tag, wikiId, user, pathName }: {
|
||||
}
|
||||
|
||||
|
||||
export default (({ user }: Props) => {
|
||||
const TopNav: FC<Props> = ({ user }) => {
|
||||
const location = useLocation ()
|
||||
|
||||
const dirRef = useRef<(-1) | 1> (1)
|
||||
@@ -159,12 +159,12 @@ export default (({ user }: Props) => {
|
||||
useEffect (() => {
|
||||
const unsubscribe = WikiIdBus.subscribe (setWikiId)
|
||||
return () => unsubscribe ()
|
||||
}, [activeIdx])
|
||||
}, [])
|
||||
|
||||
useEffect (() => {
|
||||
setMenuOpen (false)
|
||||
setOpenItemIdx (activeIdx)
|
||||
}, [location])
|
||||
}, [activeIdx, location])
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -433,4 +433,6 @@ export default (({ user }: Props) => {
|
||||
</motion.div>)}
|
||||
</AnimatePresence>
|
||||
</>)
|
||||
}) satisfies FC<Props>
|
||||
}
|
||||
|
||||
export default TopNav
|
||||
|
||||
@@ -10,7 +10,7 @@ type Props = { user: User | null,
|
||||
sp?: boolean }
|
||||
|
||||
|
||||
export default (({ user, sp }: Props) => {
|
||||
const TopNavUser: FC<Props> = ({ user, sp }) => {
|
||||
if (!(user))
|
||||
return
|
||||
|
||||
@@ -28,4 +28,6 @@ export default (({ user, sp }: Props) => {
|
||||
{user.name || '名もなきニジラー'}
|
||||
</PrefetchLink>
|
||||
</>)
|
||||
}) satisfies FC<Props>
|
||||
}
|
||||
|
||||
export default TopNavUser
|
||||
@@ -5,7 +5,7 @@ type Props = {
|
||||
statusId: string }
|
||||
|
||||
|
||||
export default (({ userId, statusId }: Props) => {
|
||||
const TwitterEmbed: FC<Props> = ({ userId, statusId }) => {
|
||||
const now = (new Date).toLocaleDateString ()
|
||||
|
||||
return (
|
||||
@@ -18,4 +18,6 @@ export default (({ userId, statusId }: Props) => {
|
||||
</blockquote>
|
||||
<script async src="https://platform.twitter.com/widgets.js" charSet="utf-8"/>
|
||||
</div>)
|
||||
}) satisfies FC<Props>
|
||||
}
|
||||
|
||||
export default TwitterEmbed
|
||||
@@ -25,7 +25,7 @@ const mdComponents = { a: (({ href, children }) => (
|
||||
</a>))) } as const satisfies Components
|
||||
|
||||
|
||||
export default (({ title, body }: Props) => {
|
||||
const WikiBody: FC<Props> = ({ title, body }) => {
|
||||
const { data } = useQuery ({
|
||||
enabled: Boolean (body),
|
||||
queryKey: wikiKeys.index ({ }),
|
||||
@@ -39,4 +39,6 @@ export default (({ title, body }: Props) => {
|
||||
<ReactMarkdown components={mdComponents} remarkPlugins={remarkPlugins}>
|
||||
{body || `このページは存在しません。[新規作成してください](/wiki/new?title=${ encodeURIComponent (title) })。`}
|
||||
</ReactMarkdown>)
|
||||
}) satisfies FC<Props>
|
||||
}
|
||||
|
||||
export default WikiBody
|
||||
@@ -25,7 +25,7 @@ type Props = Omit<ComponentPropsWithoutRef<'input'>, 'onChange'> & {
|
||||
onBlur?: (ev: FocusEvent<HTMLInputElement>) => void }
|
||||
|
||||
|
||||
export default (({ value, onChange, className, onBlur, ...rest }: Props) => {
|
||||
const DateTimeField: FC<Props> = ({ value, onChange, className, onBlur, ...rest }) => {
|
||||
const [local, setLocal] = useState ('')
|
||||
|
||||
useEffect (() => {
|
||||
@@ -44,4 +44,6 @@ export default (({ value, onChange, className, onBlur, ...rest }: Props) => {
|
||||
onChange?.(v ? (new Date (v)).toISOString () : null)
|
||||
}}
|
||||
onBlur={onBlur}/>)
|
||||
}) satisfies FC<Props>
|
||||
}
|
||||
|
||||
export default DateTimeField
|
||||
@@ -3,7 +3,9 @@ import type { FC, ReactNode } from 'react'
|
||||
type Props = { children: ReactNode }
|
||||
|
||||
|
||||
export default (({ children }: Props) => (
|
||||
const Form: FC<Props> = ({ children }) => (
|
||||
<div className="max-w-xl mx-auto p-4 space-y-4">
|
||||
{children}
|
||||
</div>)) satisfies FC<Props>
|
||||
</div>)
|
||||
|
||||
export default Form
|
||||
@@ -1,12 +1,14 @@
|
||||
import React from 'react'
|
||||
|
||||
import type { FC } from 'react'
|
||||
|
||||
type Props = { children: React.ReactNode
|
||||
checkBox?: { label: string
|
||||
checked: boolean
|
||||
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void } }
|
||||
|
||||
|
||||
export default ({ children, checkBox }: Props) => {
|
||||
const Label: FC<Props> = ({ children, checkBox }) => {
|
||||
if (!(checkBox))
|
||||
{
|
||||
return (
|
||||
@@ -26,3 +28,5 @@ export default ({ children, checkBox }: Props) => {
|
||||
</label>
|
||||
</div>)
|
||||
}
|
||||
|
||||
export default Label
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import PageTitle from '@/components/common/PageTitle'
|
||||
|
||||
|
||||
describe ('PageTitle', () => {
|
||||
it ('renders children as a level 1 heading', () => {
|
||||
render (<PageTitle>Test title</PageTitle>)
|
||||
|
||||
const heading = screen.getByRole ('heading', { level: 1 })
|
||||
|
||||
expect (heading.textContent).toBe ('Test title')
|
||||
})
|
||||
})
|
||||
@@ -1,9 +1,13 @@
|
||||
import React from 'react'
|
||||
|
||||
import type { FC } from 'react'
|
||||
|
||||
type Props = { children: React.ReactNode }
|
||||
|
||||
|
||||
export default ({ children }: Props) => (
|
||||
const PageTitle: FC<Props> = ({ children }) => (
|
||||
<h1 className="text-2xl font-bold mb-2">
|
||||
{children}
|
||||
</h1>)
|
||||
|
||||
export default PageTitle
|
||||
|
||||
@@ -48,7 +48,7 @@ const getPages = (
|
||||
}
|
||||
|
||||
|
||||
export default (({ page, totalPages, siblingCount = 3 }) => {
|
||||
const Pagination: FC<Props> = ({ page, totalPages, siblingCount = 3 }) => {
|
||||
const location = useLocation ()
|
||||
|
||||
const buildTo = (p: number) => {
|
||||
@@ -124,4 +124,6 @@ export default (({ page, totalPages, siblingCount = 3 }) => {
|
||||
</>)}
|
||||
</div>
|
||||
</nav>)
|
||||
}) satisfies FC<Props>
|
||||
}
|
||||
|
||||
export default Pagination
|
||||
|
||||
@@ -5,7 +5,9 @@ import type { ComponentPropsWithoutRef, FC } from 'react'
|
||||
type Props = ComponentPropsWithoutRef<'h2'>
|
||||
|
||||
|
||||
export default (({ children, className, ...rest }: Props) => (
|
||||
const SectionTitle: FC<Props> = ({ children, className, ...rest }) => (
|
||||
<h2 {...rest} className={cn ('text-xl my-4', className)}>
|
||||
{children}
|
||||
</h2>)) satisfies FC<Props>
|
||||
</h2>)
|
||||
|
||||
export default SectionTitle
|
||||
@@ -1,9 +1,13 @@
|
||||
import React from 'react'
|
||||
|
||||
import type { FC } from 'react'
|
||||
|
||||
type Props = { children: React.ReactNode }
|
||||
|
||||
|
||||
export default ({ children }: Props) => (
|
||||
const SubsectionTitle: FC<Props> = ({ children }) => (
|
||||
<h3 className="my-2">
|
||||
{children}
|
||||
</h3>)
|
||||
|
||||
export default SubsectionTitle
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { FC } from 'react'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
@@ -10,7 +12,7 @@ type Props = { children: React.ReactNode }
|
||||
export const Tab = ({ children }: TabProps) => <>{children}</>
|
||||
|
||||
|
||||
export default ({ children }: Props) => {
|
||||
const TabGroup: FC<Props> = ({ children }) => {
|
||||
const tabs = React.Children.toArray (children) as React.ReactElement<TabProps>[]
|
||||
|
||||
const [current, setCurrent] = useState<number> (() => {
|
||||
@@ -37,3 +39,5 @@ export default ({ children }: Props) => {
|
||||
</div>
|
||||
</div>)
|
||||
}
|
||||
|
||||
export default TabGroup
|
||||
|
||||
@@ -12,7 +12,7 @@ type Props = {
|
||||
value: string
|
||||
setValue: (value: string) => void }
|
||||
|
||||
export default (({ value, setValue }: Props) => {
|
||||
const TagInput: FC<Props> = ({ value, setValue }) => {
|
||||
const [activeIndex, setActiveIndex] = useState (-1)
|
||||
const [suggestions, setSuggestions] = useState<Tag[]> ([])
|
||||
const [suggestionsVsbl, setSuggestionsVsbl] = useState (false)
|
||||
@@ -62,9 +62,12 @@ export default (({ value, setValue }: Props) => {
|
||||
case 'Enter':
|
||||
if (activeIndex < 0)
|
||||
break
|
||||
ev.preventDefault ()
|
||||
const selected = suggestions[activeIndex]
|
||||
selected && handleTagSelect (selected)
|
||||
{
|
||||
ev.preventDefault ()
|
||||
const selected = suggestions[activeIndex]
|
||||
if (selected)
|
||||
handleTagSelect (selected)
|
||||
}
|
||||
break
|
||||
|
||||
case 'Escape':
|
||||
@@ -94,4 +97,6 @@ export default (({ value, setValue }: Props) => {
|
||||
activeIndex={activeIndex}
|
||||
onSelect={handleTagSelect}/>
|
||||
</div>)
|
||||
}) satisfies FC<Props>
|
||||
}
|
||||
|
||||
export default TagInput
|
||||
@@ -57,7 +57,7 @@ let nextDialogueId = 1
|
||||
type Props = { children: ReactNode }
|
||||
|
||||
|
||||
export default (({ children }: Props) => {
|
||||
const DialogueProvider: FC<Props> = ({ children }) => {
|
||||
const [queue, setQueue] = useState<DialogueRequest[]> ([])
|
||||
|
||||
const push = useCallback ((request: Omit<DialogueRequest, 'id'>) => {
|
||||
@@ -174,7 +174,7 @@ export default (({ children }: Props) => {
|
||||
</DialogContent>)}
|
||||
</Dialog>
|
||||
</DialogueContext.Provider>)
|
||||
}) satisfies FC<Props>
|
||||
}
|
||||
|
||||
|
||||
export const useDialogue = () => {
|
||||
@@ -185,3 +185,5 @@ export const useDialogue = () => {
|
||||
|
||||
return dialogue
|
||||
}
|
||||
|
||||
export default DialogueProvider
|
||||
|
||||
@@ -9,10 +9,12 @@ type Props = {
|
||||
className?: string }
|
||||
|
||||
|
||||
export default (({ children, className }: Props) => (
|
||||
const MainArea: FC<Props> = ({ children, className }) => (
|
||||
<motion.main
|
||||
transition={{ layout: { duration: .2, ease: 'easeOut' } }}
|
||||
className={cn ('flex-1 overflow-y-auto p-4', className)}
|
||||
layout="position">
|
||||
{children}
|
||||
</motion.main>)) satisfies FC<Props>
|
||||
</motion.main>)
|
||||
|
||||
export default MainArea
|
||||
@@ -6,7 +6,7 @@ import type { FC, ReactNode } from 'react'
|
||||
type Props = { children: ReactNode }
|
||||
|
||||
|
||||
export default (({ children }: Props) => (
|
||||
const SidebarComponent: FC<Props> = ({ children }) => (
|
||||
<motion.div
|
||||
layout="position"
|
||||
transition={{ layout: { duration: .2, ease: 'easeOut' } }}
|
||||
@@ -27,4 +27,6 @@ export default (({ children }: Props) => (
|
||||
</Helmet>
|
||||
|
||||
{children}
|
||||
</motion.div>)) satisfies FC<Props>
|
||||
</motion.div>)
|
||||
|
||||
export default SidebarComponent
|
||||
@@ -18,13 +18,6 @@ type ToasterToast = ToastProps & {
|
||||
action?: ToastActionElement
|
||||
}
|
||||
|
||||
const actionTypes = {
|
||||
ADD_TOAST: "ADD_TOAST",
|
||||
UPDATE_TOAST: "UPDATE_TOAST",
|
||||
DISMISS_TOAST: "DISMISS_TOAST",
|
||||
REMOVE_TOAST: "REMOVE_TOAST",
|
||||
} as const
|
||||
|
||||
let count = 0
|
||||
|
||||
function genId() {
|
||||
@@ -32,23 +25,21 @@ function genId() {
|
||||
return count.toString()
|
||||
}
|
||||
|
||||
type ActionType = typeof actionTypes
|
||||
|
||||
type Action =
|
||||
| {
|
||||
type: ActionType["ADD_TOAST"]
|
||||
type: "ADD_TOAST"
|
||||
toast: ToasterToast
|
||||
}
|
||||
| {
|
||||
type: ActionType["UPDATE_TOAST"]
|
||||
type: "UPDATE_TOAST"
|
||||
toast: Partial<ToasterToast>
|
||||
}
|
||||
| {
|
||||
type: ActionType["DISMISS_TOAST"]
|
||||
type: "DISMISS_TOAST"
|
||||
toastId?: ToasterToast["id"]
|
||||
}
|
||||
| {
|
||||
type: ActionType["REMOVE_TOAST"]
|
||||
type: "REMOVE_TOAST"
|
||||
toastId?: ToasterToast["id"]
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { FC } from 'react'
|
||||
|
||||
import { useState } from 'react'
|
||||
|
||||
import { useDialogue } from '@/components/dialogues/DialogueProvider'
|
||||
@@ -18,7 +20,7 @@ type Props = { visible: boolean
|
||||
setUser: (user: User) => void }
|
||||
|
||||
|
||||
export default ({ visible, onVisibleChange, setUser }: Props) => {
|
||||
const InheritDialogue: FC<Props> = ({ visible, onVisibleChange, setUser }) => {
|
||||
const dialogue = useDialogue ()
|
||||
|
||||
const [inputCode, setInputCode] = useState ('')
|
||||
@@ -68,3 +70,5 @@ export default ({ visible, onVisibleChange, setUser }: Props) => {
|
||||
</DialogContent>
|
||||
</Dialog>)
|
||||
}
|
||||
|
||||
export default InheritDialogue
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { FC } from 'react'
|
||||
|
||||
import { useDialogue } from '@/components/dialogues/DialogueProvider'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Dialog,
|
||||
@@ -17,7 +19,7 @@ type Props = { visible: boolean
|
||||
setUser: React.Dispatch<React.SetStateAction<User | null>> }
|
||||
|
||||
|
||||
export default ({ visible, onVisibleChange, user, setUser }: Props) => {
|
||||
const UserCodeDialogue: FC<Props> = ({ visible, onVisibleChange, user, setUser }) => {
|
||||
const dialogue = useDialogue ()
|
||||
|
||||
const handleChange = async () => {
|
||||
@@ -69,3 +71,5 @@ export default ({ visible, onVisibleChange, user, setUser }: Props) => {
|
||||
</DialogContent>
|
||||
</Dialog>)
|
||||
}
|
||||
|
||||
export default UserCodeDialogue
|
||||
|
||||
新しい課題から参照
ユーザをブロックする