Browse Source

fix highlights

imwald
Silberengel 2 weeks ago
parent
commit
1eb9e867cc
  1. 72
      src/components/Note/SelectionHighlightTrigger.tsx

72
src/components/Note/SelectionHighlightTrigger.tsx

@ -1,5 +1,6 @@
import { buildHighlightDataFromEvent } from '@/lib/build-highlight-data' import { buildHighlightDataFromEvent } from '@/lib/build-highlight-data'
import { useCreateHighlight } from './CreateHighlightContext' import { useCreateHighlight } from './CreateHighlightContext'
import { Drawer, DrawerContent, DrawerHeader, DrawerTitle } from '@/components/ui/drawer'
import { useScreenSize } from '@/providers/ScreenSizeProvider' import { useScreenSize } from '@/providers/ScreenSizeProvider'
import { Event } from 'nostr-tools' import { Event } from 'nostr-tools'
import { Highlighter } from 'lucide-react' import { Highlighter } from 'lucide-react'
@ -7,7 +8,9 @@ import { useCallback, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
/** After finger lift, wait before treating the gesture as finished (handle drag may still run). */
const MOBILE_TOUCH_END_SETTLE_MS = 600 const MOBILE_TOUCH_END_SETTLE_MS = 600
/** After selection stops changing, wait before opening the drawer so handles can extend the range. */
const MOBILE_SELECTION_STABLE_MS = 1600 const MOBILE_SELECTION_STABLE_MS = 1600
const DESKTOP_SELECTION_DELAY_MS = 50 const DESKTOP_SELECTION_DELAY_MS = 50
@ -79,6 +82,7 @@ export default function SelectionHighlightTrigger({
const [selectedText, setSelectedText] = useState('') const [selectedText, setSelectedText] = useState('')
const [paragraphContext, setParagraphContext] = useState('') const [paragraphContext, setParagraphContext] = useState('')
const [toolbarPos, setToolbarPos] = useState<{ top: number; left: number } | null>(null) const [toolbarPos, setToolbarPos] = useState<{ top: number; left: number } | null>(null)
const [showMobileDrawer, setShowMobileDrawer] = useState(false)
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null) const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null)
const touchEndTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null) const touchEndTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
@ -90,6 +94,7 @@ export default function SelectionHighlightTrigger({
setSelectedText('') setSelectedText('')
setParagraphContext('') setParagraphContext('')
setToolbarPos(null) setToolbarPos(null)
setShowMobileDrawer(false)
}, []) }, [])
const applySelection = useCallback( const applySelection = useCallback(
@ -101,19 +106,25 @@ export default function SelectionHighlightTrigger({
return return
} }
if (isSmallScreen && isSelectingRef.current && !forceShow) return
setSelectedText(hit.selectedText) setSelectedText(hit.selectedText)
setParagraphContext(hit.paragraphContext) setParagraphContext(hit.paragraphContext)
const toolbarHeight = isSmallScreen ? 48 : 44 if (isSmallScreen) {
const toolbarWidth = isSmallScreen ? 200 : 176 if (forceShow || !isSelectingRef.current) {
setShowMobileDrawer(true)
setToolbarPos(null)
}
return
}
const toolbarHeight = 44
const margin = 8 const margin = 8
const top = const top =
hit.rect.top - toolbarHeight < margin ? hit.rect.bottom + margin : hit.rect.top - toolbarHeight hit.rect.top - toolbarHeight < margin ? hit.rect.bottom + margin : hit.rect.top - toolbarHeight
const rawLeft = hit.rect.left + hit.rect.width / 2 - toolbarWidth / 2 const rawLeft = hit.rect.left + hit.rect.width / 2 - 80
const left = Math.max(margin, Math.min(rawLeft, window.innerWidth - toolbarWidth - margin)) const left = Math.max(margin, Math.min(rawLeft, window.innerWidth - 176 - margin))
setToolbarPos({ top, left }) setToolbarPos({ top, left })
setShowMobileDrawer(false)
}, },
[clearUi, isSmallScreen, openHighlight] [clearUi, isSmallScreen, openHighlight]
) )
@ -149,14 +160,14 @@ export default function SelectionHighlightTrigger({
if (!isSmallScreen) return if (!isSmallScreen) return
isSelectingRef.current = true isSelectingRef.current = true
if (selectionStableTimeoutRef.current) clearTimeout(selectionStableTimeoutRef.current) if (selectionStableTimeoutRef.current) clearTimeout(selectionStableTimeoutRef.current)
setToolbarPos(null) setShowMobileDrawer(false)
} }
const onTouchMove = () => { const onTouchMove = () => {
if (!isSmallScreen) return if (!isSmallScreen) return
isSelectingRef.current = true isSelectingRef.current = true
if (selectionStableTimeoutRef.current) clearTimeout(selectionStableTimeoutRef.current) if (selectionStableTimeoutRef.current) clearTimeout(selectionStableTimeoutRef.current)
setToolbarPos(null) setShowMobileDrawer(false)
} }
const onTouchEnd = () => { const onTouchEnd = () => {
@ -186,7 +197,6 @@ export default function SelectionHighlightTrigger({
return return
} }
setToolbarPos(null)
scheduleMobileStableSelection() scheduleMobileStableSelection()
return return
} }
@ -243,12 +253,12 @@ export default function SelectionHighlightTrigger({
if (!openHighlight) return <>{children}</> if (!openHighlight) return <>{children}</>
const showToolbar = selectedText && toolbarPos const showDesktopToolbar = !isSmallScreen && selectedText && toolbarPos
return ( return (
<div ref={containerRef} className="relative select-text"> <div ref={containerRef} className="relative select-text">
{children} {children}
{showToolbar ? ( {showDesktopToolbar ? (
<> <>
<div <div
className="highlight-button-container fixed z-[150] flex items-center gap-1 rounded-md border bg-background px-2 py-1.5 shadow-lg" className="highlight-button-container fixed z-[150] flex items-center gap-1 rounded-md border bg-background px-2 py-1.5 shadow-lg"
@ -258,8 +268,8 @@ export default function SelectionHighlightTrigger({
<Button <Button
type="button" type="button"
variant="ghost" variant="ghost"
size={isSmallScreen ? 'default' : 'sm'} size="sm"
className={isSmallScreen ? 'h-10 gap-1.5' : 'h-8 gap-1.5'} className="h-8 gap-1.5"
onClick={(e) => { onClick={(e) => {
e.stopPropagation() e.stopPropagation()
handleCreateHighlight() handleCreateHighlight()
@ -271,8 +281,8 @@ export default function SelectionHighlightTrigger({
<Button <Button
type="button" type="button"
variant="ghost" variant="ghost"
size={isSmallScreen ? 'default' : 'sm'} size="sm"
className={isSmallScreen ? 'h-10 px-3' : 'h-8 px-2'} className="h-8 px-2"
onClick={(e) => { onClick={(e) => {
e.stopPropagation() e.stopPropagation()
handleDismiss() handleDismiss()
@ -281,16 +291,44 @@ export default function SelectionHighlightTrigger({
{t('Cancel')} {t('Cancel')}
</Button> </Button>
</div> </div>
{!isSmallScreen ? (
<div <div
className="fixed inset-0 z-[149]" className="fixed inset-0 z-[149]"
aria-hidden aria-hidden
data-selection-highlight-ui data-selection-highlight-ui
onClick={handleDismiss} onClick={handleDismiss}
/> />
) : null}
</> </>
) : null} ) : null}
{isSmallScreen ? (
<Drawer
open={showMobileDrawer && selectedText.length > 0}
onOpenChange={(open) => {
setShowMobileDrawer(open)
if (!open) handleDismiss()
}}
>
<DrawerContent data-selection-highlight-ui>
<DrawerHeader>
<DrawerTitle>{t('Create Highlight')}</DrawerTitle>
</DrawerHeader>
<div className="space-y-4 p-4 pb-8">
<div className="text-sm text-muted-foreground">{t('Selected text')}:</div>
<div className="break-words rounded-lg bg-muted p-3 text-sm">&ldquo;{selectedText}&rdquo;</div>
<Button
className="w-full"
onClick={(e) => {
e.stopPropagation()
handleCreateHighlight()
}}
>
<Highlighter className="mr-2 h-4 w-4" />
{t('Create Highlight')}
</Button>
</div>
</DrawerContent>
</Drawer>
) : null}
</div> </div>
) )
} }

Loading…
Cancel
Save