From cc68e67726dd9f254334695355f1ff2be0547767 Mon Sep 17 00:00:00 2001 From: Gigi Date: Sun, 5 Oct 2025 23:09:36 +0100 Subject: [PATCH] refactor: change highlight button to FAB style - Replace floating popup button with persistent FAB in bottom-right corner - Button always visible but disabled when no text is selected - Uses user's highlight color from settings - Visual feedback: scales up and becomes opaque when text is selected - Follows Google apps design pattern for floating action buttons --- dist/index.html | 2 +- src/components/ContentPanel.tsx | 13 +++--- src/components/HighlightButton.tsx | 68 +++++++++++++----------------- 3 files changed, 37 insertions(+), 46 deletions(-) diff --git a/dist/index.html b/dist/index.html index fdc3a78d..f611a055 100644 --- a/dist/index.html +++ b/dist/index.html @@ -5,7 +5,7 @@ Boris - Nostr Bookmarks - + diff --git a/src/components/ContentPanel.tsx b/src/components/ContentPanel.tsx index 1338530e..b0353728 100644 --- a/src/components/ContentPanel.tsx +++ b/src/components/ContentPanel.tsx @@ -182,14 +182,14 @@ const ContentPanel: React.FC = ({ const handleMouseUp = useCallback(() => { // Only allow highlight creation if user is logged in if (!activeAccount || !relayPool) { - highlightButtonRef.current?.hide() + highlightButtonRef.current?.clearSelection() return } setTimeout(() => { const selection = window.getSelection() if (!selection || selection.rangeCount === 0) { - highlightButtonRef.current?.hide() + highlightButtonRef.current?.clearSelection() return } @@ -197,9 +197,9 @@ const ContentPanel: React.FC = ({ const text = selection.toString().trim() if (text.length > 0 && contentRef.current?.contains(range.commonAncestorContainer)) { - highlightButtonRef.current?.updateSelection(text, range.cloneRange()) + highlightButtonRef.current?.updateSelection(text) } else { - highlightButtonRef.current?.hide() + highlightButtonRef.current?.clearSelection() } }, 10) }, [activeAccount, relayPool]) @@ -220,7 +220,7 @@ const ContentPanel: React.FC = ({ ) onShowToast?.('Highlight created successfully!', 'success') - highlightButtonRef.current?.hide() + highlightButtonRef.current?.clearSelection() window.getSelection()?.removeAllRanges() // Trigger refresh of highlights @@ -306,7 +306,8 @@ const ContentPanel: React.FC = ({ {activeAccount && relayPool && ( )} diff --git a/src/components/HighlightButton.tsx b/src/components/HighlightButton.tsx index 89ebde7a..30059a10 100644 --- a/src/components/HighlightButton.tsx +++ b/src/components/HighlightButton.tsx @@ -1,20 +1,21 @@ -import React, { useCallback, useImperativeHandle, useRef } from 'react' +import React, { useCallback, useImperativeHandle, useRef, useState } from 'react' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faHighlighter } from '@fortawesome/free-solid-svg-icons' interface HighlightButtonProps { onHighlight: (text: string) => void + highlightColor?: string } export interface HighlightButtonRef { - updateSelection: (text: string, range: Range) => void - hide: () => void + updateSelection: (text: string) => void + clearSelection: () => void } export const HighlightButton = React.forwardRef( - ({ onHighlight }, ref) => { + ({ onHighlight, highlightColor = '#ffff00' }, ref) => { const currentSelectionRef = useRef('') - const buttonRef = useRef(null) + const [hasSelection, setHasSelection] = useState(false) const handleClick = useCallback( (e: React.MouseEvent) => { @@ -27,59 +28,48 @@ export const HighlightButton = React.forwardRef { - // Prevent the button from taking focus away from the text selection - e.preventDefault() - }, []) - - // Expose methods to update selection and hide button + // Expose methods to update selection useImperativeHandle(ref, () => ({ - updateSelection: (text: string, range: Range) => { + updateSelection: (text: string) => { currentSelectionRef.current = text - if (buttonRef.current) { - const rect = range.getBoundingClientRect() - buttonRef.current.style.display = 'flex' - // Use fixed positioning relative to viewport, so it follows the scroll - buttonRef.current.style.top = `${rect.bottom + 8}px` - buttonRef.current.style.left = `${rect.left + rect.width / 2 - 20}px` - } + setHasSelection(!!text) }, - hide: () => { + clearSelection: () => { currentSelectionRef.current = '' - if (buttonRef.current) { - buttonRef.current.style.display = 'none' - } + setHasSelection(false) } })) return ( ) }