Browse Source

タグ補完ウィンドウ(#103) (#269)

#103

Merge remote-tracking branch 'origin/main' into feature/103

#103

#103 タグ補完からニコタグ除外

Co-authored-by: miteruzo <miteruzo@naver.com>
Reviewed-on: https://git.miteruzo.com/miteruzo/btrc-hub/pulls/269
feature/194
みてるぞ 1 week ago
parent
commit
a0d6aeb91e
2 changed files with 33 additions and 15 deletions
  1. +28
    -11
      frontend/src/components/PostFormTagsArea.tsx
  2. +5
    -4
      frontend/src/lib/utils.ts

+ 28
- 11
frontend/src/components/PostFormTagsArea.tsx View File

@@ -25,8 +25,8 @@ const getTokenAt = (value: string, pos: number) => {
}


const replaceToken = (value: string, start: number, end: number, text: string) => (
`${ value.slice (0, start) }${ text }${ value.slice (end) }`)
const replaceToken = (value: string, start: number, end: number, text: string) =>
`${ value.slice (0, start) }${ text }${ value.slice (end) }`


type Props = {
@@ -38,16 +38,17 @@ export default (({ tags, setTags }: Props) => {
const ref = useRef<HTMLTextAreaElement> (null)

const [bounds, setBounds] = useState<{ start: number; end: number }> ({ start: 0, end: 0 })
const [focused, setFocused] = useState (false)
const [suggestions, setSuggestions] = useState<Tag[]> ([])
const [suggestionsVsbl, setSuggestionsVsbl] = useState (false)

const handleTagSelect = (tag: Tag) => {
setSuggestionsVsbl (false)
const textarea = ref.current!
const newValue = replaceToken (tags, bounds.start, bounds.end, tag.name)
const newValue = replaceToken (tags, bounds.start, bounds.end, tag.name + ' ')
setTags (newValue)
requestAnimationFrame (async () => {
const p = bounds.start + tag.name.length
const p = bounds.start + tag.name.length + 1
textarea.selectionStart = textarea.selectionEnd = p
textarea.focus ()
await recompute (p, newValue)
@@ -56,14 +57,21 @@ export default (({ tags, setTags }: Props) => {

const recompute = async (pos: number, v: string = tags) => {
const { start, end, token } = getTokenAt (v, pos)
if (!(token.trim ()))
{
setSuggestionsVsbl (false)
return
}

setBounds ({ start, end })
const data = await apiGet<Tag[]> ('/tags/autocomplete', { params: { q: token } })

const data = await apiGet<Tag[]> ('/tags/autocomplete', { params: { q: token, nico: '0' } })
setSuggestions (data.filter (t => t.postCount > 0))
setSuggestionsVsbl (suggestions.length > 0)
}

return (
<div>
<div className="relative w-full">
<Label>タグ</Label>
<TextArea
ref={ref}
@@ -72,11 +80,20 @@ export default (({ tags, setTags }: Props) => {
onSelect={async (ev: SyntheticEvent<HTMLTextAreaElement>) => {
const pos = (ev.target as HTMLTextAreaElement).selectionStart
await recompute (pos)
}}
onFocus={() => {
setFocused (true)
}}
onBlur={() => {
setFocused (false)
setSuggestionsVsbl (false)
}}/>
<TagSearchBox suggestions={suggestionsVsbl && suggestions.length
? suggestions
: [] as Tag[]}
activeIndex={-1}
onSelect={handleTagSelect}/>
{focused && (
<TagSearchBox
suggestions={suggestionsVsbl && suggestions.length > 0
? suggestions
: [] as Tag[]}
activeIndex={-1}
onSelect={handleTagSelect}/>)}
</div>)
}) satisfies FC<Props>

+ 5
- 4
frontend/src/lib/utils.ts View File

@@ -1,6 +1,7 @@
import { clsx, type ClassValue } from 'clsx'
import { clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'

export function cn (...inputs: ClassValue[]) {
return twMerge(clsx(...inputs))
}
import type { ClassValue } from 'clsx'


export const cn = (...inputs: ClassValue[]) => twMerge (clsx (...inputs))

Loading…
Cancel
Save