diff --git a/frontend/src/components/layout/SidebarComponent.tsx b/frontend/src/components/layout/SidebarComponent.tsx
index 779755e..922b787 100644
--- a/frontend/src/components/layout/SidebarComponent.tsx
+++ b/frontend/src/components/layout/SidebarComponent.tsx
@@ -1,19 +1,307 @@
import { motion } from 'framer-motion'
import { Helmet } from 'react-helmet-async'
-import type { FC, ReactNode } from 'react'
+import { cn } from '@/lib/utils'
-type Props = { children: ReactNode }
+import type { CSSProperties, FC, MouseEvent, PointerEvent, ReactNode } from 'react'
+import { useCallback, useEffect, useRef, useState } from 'react'
+
+type SidebarSide = 'left' | 'right'
+
+type Props = {
+ children: ReactNode
+ className?: string
+ maxWidth?: number
+ onWidthChange?: (width: number) => void
+ sidebarKey: string
+ side?: SidebarSide }
+
+type ResizeState = {
+ pointerId: number
+ startWidth: number
+ startX: number
+ side: SidebarSide }
+
+type PendingPressState = ResizeState & { startY: number }
+
+const DEFAULT_SIDEBAR_WIDTH = 256
+const MIN_SIDEBAR_WIDTH = 192
+const MIN_MAIN_WIDTH = 360
+const MAX_VIEWPORT_WIDTH_RATIO = .8
+const LONG_PRESS_DELAY_MS = 350
+const TOUCH_MOVE_THRESHOLD_PX = 8
-const SidebarComponent: FC
= ({ children }) => (
-
-
-
-
+
+
- {children}
- )
+
+ {children}
+
-export default SidebarComponent
\ No newline at end of file
+
+ )
+}
+
+export default SidebarComponent
diff --git a/frontend/src/pages/theatres/TheatreDetailPage.tsx b/frontend/src/pages/theatres/TheatreDetailPage.tsx
index e4db3d2..6efb2cc 100644
--- a/frontend/src/pages/theatres/TheatreDetailPage.tsx
+++ b/frontend/src/pages/theatres/TheatreDetailPage.tsx
@@ -10,6 +10,7 @@ import PrefetchLink from '@/components/PrefetchLink'
import TagLink from '@/components/TagLink'
import FieldError from '@/components/common/FieldError'
import { useDialogue } from '@/components/dialogues/DialogueProvider'
+import SidebarComponent from '@/components/layout/SidebarComponent'
import { Button } from '@/components/ui/button'
import { SITE_TITLE } from '@/config'
import { CATEGORIES, CATEGORY_NAMES } from '@/consts'
@@ -52,6 +53,8 @@ const INITIAL_WEIGHTS: TheatrePostSelectionWeights =
const LAYOUT_STORAGE_KEY = 'theatre-layout-mode'
const TAG_FLOW_STORAGE_KEY = 'theatre-tag-flow'
+const MIN_MAIN_WIDTH = 360
+const SIDEBAR_GAP_WIDTH = 16
const LAYOUT_LABELS: Record = {
threeColumns: '3 列',
@@ -211,11 +214,16 @@ const TheatreDetailPage: FC = ({ user }: Props) => {
const commentsRef = useRef (null)
const embedRef = useRef (null)
const loadingRef = useRef (false)
+ const sidebarContainerRef = useRef (null)
const theatreInfoRef = useRef (INITIAL_THEATRE_INFO)
const theatreInfoReceivedAtRef = useRef (performance.now ())
const videoLengthRef = useRef (0)
const lastCommentNoRef = useRef (0)
+ const [leftSidebarWidth, setLeftSidebarWidth] = useState (256)
+ const [rightSidebarWidth, setRightSidebarWidth] = useState (256)
+ const [sidebarContainerWidth, setSidebarContainerWidth] = useState (
+ () => typeof window === 'undefined' ? 0 : window.innerWidth)
const [comments, setComments] = useState ([])
const [content, setContent] = useState ('')
const [editingPost, setEditingPost] = useState (null)
@@ -324,6 +332,17 @@ const TheatreDetailPage: FC = ({ user }: Props) => {
lastCommentNoRef.current = comments[0]?.no ?? 0
}, [comments])
+ useEffect (() => {
+ const updateSidebarContainerWidth = () => {
+ const nextWidth = sidebarContainerRef.current?.clientWidth ?? window.innerWidth
+ setSidebarContainerWidth (nextWidth)
+ }
+
+ updateSidebarContainerWidth ()
+ window.addEventListener ('resize', updateSidebarContainerWidth)
+ return () => window.removeEventListener ('resize', updateSidebarContainerWidth)
+ }, [])
+
useEffect (() => {
if (!(id))
return
@@ -639,6 +658,26 @@ const TheatreDetailPage: FC = ({ user }: Props) => {
if (status >= 400)
return
+ const singleSidebarMaxWidth = Math.max (
+ 192,
+ sidebarContainerWidth - MIN_MAIN_WIDTH - SIDEBAR_GAP_WIDTH)
+ const leftSidebarMaxWidth = layoutMode === 'threeColumns'
+ ? Math.max (
+ 192,
+ sidebarContainerWidth
+ - rightSidebarWidth
+ - MIN_MAIN_WIDTH
+ - SIDEBAR_GAP_WIDTH * 2)
+ : singleSidebarMaxWidth
+ const rightSidebarMaxWidth = layoutMode === 'threeColumns'
+ ? Math.max (
+ 192,
+ sidebarContainerWidth
+ - leftSidebarWidth
+ - MIN_MAIN_WIDTH
+ - SIDEBAR_GAP_WIDTH * 2)
+ : singleSidebarMaxWidth
+
const tagPanel = (
@@ -806,30 +845,26 @@ const TheatreDetailPage: FC
= ({ user }: Props) => {
{theatre && {`${ theatreTitle } | ${ SITE_TITLE }`}}
-
+
{layoutMode !== 'tagsBottom' && (
-
-
- {tagPanel}
-
- )}
+
+ {tagPanel}
+ )}
-
+ className="order-1 min-w-0 flex-1 space-y-4 md:order-none
+ md:min-w-[360px] md:overflow-y-auto">
+
- {commentsPanel}
- {participantsPanel}
- )}
+
+
+ {commentsPanel}
+ {participantsPanel}
+
+ )}
)
}