#3 TS エラー修正
This commit is contained in:
Generated
+352
-367
File diff suppressed because it is too large
Load Diff
+15
-9
@@ -22,6 +22,17 @@ const colours = ['bg-fuchsia-500 dark:bg-fuchsia-900',
|
||||
'bg-yellow-500 dark:bg-yellow-900'] as const
|
||||
|
||||
|
||||
const ScrollToTop = () => {
|
||||
const { pathname } = useLocation ()
|
||||
|
||||
useEffect (() => {
|
||||
scrollTo (0, 0)
|
||||
}, [pathname])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
export default () => {
|
||||
const bgmRef = useRef<HTMLAudioElement | null> (null)
|
||||
|
||||
@@ -35,7 +46,7 @@ export default () => {
|
||||
bgmRef.current.volume = 1
|
||||
|
||||
const playBGM = async () => {
|
||||
if (playing)
|
||||
if (playing || !(bgmRef.current))
|
||||
return
|
||||
|
||||
try
|
||||
@@ -54,7 +65,7 @@ export default () => {
|
||||
document.addEventListener ('touchstart', playBGM)
|
||||
|
||||
const changeColour = () => {
|
||||
setColourIndex (idx => Math.floor (Math.random () * colours.length))
|
||||
setColourIndex (Math.floor (Math.random () * colours.length))
|
||||
}
|
||||
changeColour ()
|
||||
const colouringInterval = setInterval (changeColour, 3000)
|
||||
@@ -86,7 +97,7 @@ export default () => {
|
||||
{playing && (
|
||||
<a href="#" onClick={ev => {
|
||||
ev.preventDefault ()
|
||||
setMute (bgmRef.current.muted = !(mute))
|
||||
bgmRef.current && setMute (bgmRef.current.muted = !(mute))
|
||||
}}>
|
||||
{mute ? 'やっぱり BGM が恋しい人用' : 'BGM がうるさい人用'}
|
||||
</a>)}
|
||||
@@ -94,12 +105,7 @@ export default () => {
|
||||
</header>
|
||||
<main className="mb-8">
|
||||
<Routes>
|
||||
{() => {
|
||||
const { pathname } = useLocation ()
|
||||
useEffect (() => {
|
||||
scrollTo (0, 0)
|
||||
}, [pathname])
|
||||
}}
|
||||
<ScrollToTop />
|
||||
<Route path="/" element={<Navigate to="/threads" replace />} />
|
||||
<Route path="/threads" element={<ThreadListPage />} />
|
||||
<Route path="/threads/:id" element={<ThreadDetailPage />} />
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useEffect, useRef, useState } from 'react'
|
||||
import { FaRedo, FaUndo } from 'react-icons/fa'
|
||||
import { Layer, Line, Rect, Stage, Image } from 'react-konva'
|
||||
|
||||
import type { KonvaEventObject } from 'konva'
|
||||
import type Konva from 'konva'
|
||||
import type { ChangeEventHandler } from 'react'
|
||||
|
||||
type ImageItem = { src: string }
|
||||
@@ -23,34 +23,35 @@ type Line = {
|
||||
strokeWidth: number }
|
||||
|
||||
const Mode = {
|
||||
Pen: Symbol (),
|
||||
Rubber: Symbol (),
|
||||
Image: Symbol () } as const
|
||||
type Mode = keyof Mode
|
||||
Pen: 'Pen',
|
||||
Rubber: 'Rubber',
|
||||
Image: 'Image' } as const
|
||||
type Mode = (typeof Mode)[keyof typeof Mode]
|
||||
|
||||
|
||||
export default () => {
|
||||
const drawingRef = useRef (false)
|
||||
const fileInputRef = useRef<HTMLInputElement> (null)
|
||||
const stageRef = useRef<Stage | null> (null)
|
||||
const stageRef = useRef<Konva.Stage | null> (null)
|
||||
|
||||
const [activeLayerId, setActiveLayerId] = useState<string | null> (null)
|
||||
const [colour, setColour] = useState ('#000000')
|
||||
const [images, setImages] = useState<Record<string, window.Image[]>> ({ })
|
||||
const [layers, setLayers] = useState<Layer[]> ([])
|
||||
const [images, setImages] = useState<Record<string, HTMLImageElement>> ({ })
|
||||
const [layerCnt, setLayerCnt] = useState (0)
|
||||
const [layers, setLayers] = useState<Layer[]> ([])
|
||||
const [mode, setMode] = useState<Mode> (Mode.Pen)
|
||||
const [pointSize, setPointSize] = useState (3)
|
||||
const [stageWidth, setStageWidth] = useState (480)
|
||||
const [stageHeight, setStageHeight] = useState (480)
|
||||
const [stageWidth, setStageWidth] = useState (480)
|
||||
|
||||
const activeLayer = layers.find (l => l.id === activeLayerId)
|
||||
|
||||
const handleMouseDown = (ev: KonvaEventObject<MouseEvent>) => {
|
||||
drawingRef.current = true
|
||||
const handleMouseDown = (ev: Konva.KonvaEventObject<MouseEvent | TouchEvent>) => {
|
||||
const pos = ev.target.getStage ()?.getPointerPosition ()
|
||||
if (!(pos))
|
||||
if (!(pos) || !(activeLayer))
|
||||
return
|
||||
|
||||
drawingRef.current = true
|
||||
const lines: Line[] = [
|
||||
...activeLayer.lines,
|
||||
{ mode,
|
||||
@@ -60,9 +61,10 @@ export default () => {
|
||||
updateActiveLayerLines (lines)
|
||||
}
|
||||
|
||||
const handleMouseMove = (ev: KonvaEventObject<MouseEvent>) => {
|
||||
if (!(drawingRef.current))
|
||||
const handleMouseMove = (ev: Konva.KonvaEventObject<MouseEvent | TouchEvent>) => {
|
||||
if (!(drawingRef.current) || !(activeLayer))
|
||||
return
|
||||
|
||||
const stage = ev.target.getStage ()
|
||||
const point = stage?.getPointerPosition ()
|
||||
if (!(point))
|
||||
@@ -80,17 +82,17 @@ export default () => {
|
||||
}
|
||||
|
||||
const handleUndo = () => {
|
||||
if (activeLayer.lines.length === 0)
|
||||
if (!(activeLayer) || activeLayer.lines.length === 0)
|
||||
return
|
||||
const newLines = [...activeLayer.lines]
|
||||
const last = newLines.pop()!
|
||||
newLines.pop()
|
||||
updateActiveLayerHistory ([...activeLayer.history, activeLayer.lines])
|
||||
updateActiveLayerFuture ([])
|
||||
updateActiveLayerLines (newLines)
|
||||
}
|
||||
|
||||
const handleRedo = () => {
|
||||
if (activeLayer.history.length === 0)
|
||||
if (!(activeLayer) || activeLayer.history.length === 0)
|
||||
return
|
||||
const prev = activeLayer.history[activeLayer.history.length - 1]
|
||||
updateActiveLayerLines (prev)
|
||||
@@ -181,9 +183,14 @@ export default () => {
|
||||
useEffect (() => {
|
||||
try
|
||||
{
|
||||
const paint = JSON.parse (localStorage.getItem ('paint'))
|
||||
const paintJSON = localStorage.getItem ('paint')
|
||||
if (paintJSON == null)
|
||||
throw new Error
|
||||
|
||||
const paint = JSON.parse (paintJSON)
|
||||
if (!(paint instanceof Array))
|
||||
throw new Error
|
||||
|
||||
setLayers (paint)
|
||||
setActiveLayerId (paint[0].id)
|
||||
}
|
||||
@@ -254,7 +261,7 @@ export default () => {
|
||||
min={32}
|
||||
max={640}
|
||||
value={stageWidth}
|
||||
onChange={ev => setStageWidth (ev.target.value)} />
|
||||
onChange={ev => setStageWidth (+ev.target.value)} />
|
||||
</label>
|
||||
<label>
|
||||
高さ:
|
||||
@@ -262,7 +269,7 @@ export default () => {
|
||||
min={24}
|
||||
max={480}
|
||||
value={stageHeight}
|
||||
onChange={ev => setStageHeight (ev.target.value)} />
|
||||
onChange={ev => setStageHeight (+ev.target.value)} />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -363,7 +370,6 @@ export default () => {
|
||||
<div className="border border-black dark:border-white">
|
||||
<Stage ref={node => {
|
||||
stageRef.current = node
|
||||
return node
|
||||
}}
|
||||
width={stageWidth}
|
||||
height={stageHeight}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import './index.css'
|
||||
import App from './App.tsx'
|
||||
|
||||
@@ -7,6 +7,8 @@ import { useParams } from 'react-router-dom'
|
||||
import ThreadCanvas from '@/components/threads/ThreadCanvas'
|
||||
import { API_BASE_URL } from '@/config'
|
||||
|
||||
import type { Post, Thread } from '@/types'
|
||||
|
||||
|
||||
export default () => {
|
||||
const { id } = useParams ()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import axios from 'axios'
|
||||
import toCamel from 'camelcase-keys'
|
||||
import cn from 'classnames'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Accordion,
|
||||
AccordionItem,
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
export type Post = {
|
||||
id: number
|
||||
threadId: number
|
||||
postNo: number
|
||||
name: string | null
|
||||
message: string | null
|
||||
imageUrl: string | null
|
||||
held: boolean
|
||||
sensitive: boolean
|
||||
good: number
|
||||
bad: number
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
deletedAt: string | null }
|
||||
|
||||
export type Thread = {
|
||||
id: number
|
||||
name: string
|
||||
description: string | null
|
||||
postCount: number
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
deletedAt: string | null }
|
||||
Reference in New Issue
Block a user