From 2f8f6a0652fbecc60604584701fab16ad9f42494 Mon Sep 17 00:00:00 2001 From: Gigi Date: Mon, 13 Oct 2025 14:01:51 +0200 Subject: [PATCH] feat: introduce CompactButton component for highlight cards - Create reusable CompactButton component for small, borderless buttons - Refactor relay indicator to use CompactButton - Refactor menu toggle button to use CompactButton - Make timestamp clickable with CompactButton (shows full date on hover) - Simplify CSS by removing duplicate button styles - Improve mobile touch targets for all compact buttons --- src/components/CompactButton.tsx | 41 ++++++++++++++++++++++++++++++++ src/components/HighlightItem.tsx | 32 ++++++++++++++----------- src/styles/layout/highlights.css | 25 ++++++++++--------- 3 files changed, 71 insertions(+), 27 deletions(-) create mode 100644 src/components/CompactButton.tsx diff --git a/src/components/CompactButton.tsx b/src/components/CompactButton.tsx new file mode 100644 index 00000000..1d611d77 --- /dev/null +++ b/src/components/CompactButton.tsx @@ -0,0 +1,41 @@ +import React from 'react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import type { IconDefinition } from '@fortawesome/fontawesome-svg-core' + +interface CompactButtonProps { + icon?: IconDefinition + onClick?: (e: React.MouseEvent) => void + title?: string + ariaLabel?: string + disabled?: boolean + spin?: boolean + className?: string + children?: React.ReactNode +} + +const CompactButton: React.FC = ({ + icon, + onClick, + title, + ariaLabel, + disabled = false, + spin = false, + className = '', + children +}) => { + return ( + + ) +} + +export default CompactButton + diff --git a/src/components/HighlightItem.tsx b/src/components/HighlightItem.tsx index d9514d91..02cfb47f 100644 --- a/src/components/HighlightItem.tsx +++ b/src/components/HighlightItem.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useRef, useState } from 'react' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faQuoteLeft, faExternalLinkAlt, faPlane, faSpinner, faServer, faTrash, faEllipsisH } from '@fortawesome/free-solid-svg-icons' +import { faQuoteLeft, faExternalLinkAlt, faPlane, faSpinner, faServer, faTrash, faEllipsisH, faClock } from '@fortawesome/free-solid-svg-icons' import { Highlight } from '../types/highlights' import { useEventModel } from 'applesauce-react/hooks' import { Models, IEventStore } from 'applesauce-core' @@ -14,6 +14,7 @@ import { formatDateCompact } from '../utils/bookmarkUtils' import { createDeletionRequest } from '../services/deletionService' import ConfirmDialog from './ConfirmDialog' import { getNostrUrl } from '../config/nostrGateways' +import CompactButton from './CompactButton' interface HighlightWithLevel extends Highlight { level?: 'mine' | 'friends' | 'nostrverse' @@ -303,21 +304,26 @@ export const HighlightItem: React.FC = ({ onClick={handleItemClick} style={{ cursor: onHighlightClick ? 'pointer' : 'default' }} > - + e.stopPropagation()} + > {formatDateCompact(highlight.created_at)} - +
{relayIndicator && ( -
- -
+ disabled={!relayPool || !eventStore} + /> )}
@@ -339,13 +345,11 @@ export const HighlightItem: React.FC = ({
- + /> {showMenu && (
diff --git a/src/styles/layout/highlights.css b/src/styles/layout/highlights.css index f4269142..5e887ba2 100644 --- a/src/styles/layout/highlights.css +++ b/src/styles/layout/highlights.css @@ -104,7 +104,14 @@ .highlight-item:hover { border-color: #646cff; } .highlight-item.selected { border-color: #646cff; background: #252525; box-shadow: 0 0 0 2px rgba(100, 108, 255, 0.3); } -.highlight-timestamp { position: absolute; top: 0.5rem; right: 0.75rem; font-size: 0.75rem; color: #888; font-weight: 500; white-space: nowrap; pointer-events: none; } +/* Compact button for highlight cards */ +.compact-button { background: none; border: none; color: #888; cursor: pointer; padding: 0.25rem; font-size: 0.75rem; display: flex; align-items: center; justify-content: center; gap: 0.25rem; transition: all 0.2s ease; border-radius: 4px; min-width: 20px; min-height: 20px; } +.compact-button:hover { color: #aaa; background: rgba(255, 255, 255, 0.05); } +.compact-button:active { transform: scale(0.95); } +.compact-button:disabled { opacity: 0.5; cursor: not-allowed; } +.compact-button:disabled:hover { background: none; color: #888; transform: none; } + +.highlight-timestamp { position: absolute; top: 0.5rem; right: 0.75rem; font-size: 0.75rem; font-weight: 500; white-space: nowrap; } /* Level colors in sidebar items */ .highlight-item.level-mine { border-color: color-mix(in srgb, var(--highlight-color-mine, #ffff00) 60%, #333); box-shadow: 0 0 0 1px color-mix(in srgb, var(--highlight-color-mine, #ffff00) 25%, transparent); } @@ -112,18 +119,14 @@ .highlight-item.level-nostrverse { border-color: color-mix(in srgb, var(--highlight-color-nostrverse, #9333ea) 60%, #333); box-shadow: 0 0 0 1px color-mix(in srgb, var(--highlight-color-nostrverse, #9333ea) 25%, transparent); } .highlight-quote-icon { color: #646cff; font-size: 1.2rem; flex-shrink: 0; margin-top: 0.25rem; position: relative; } -.highlight-relay-indicator { position: absolute; bottom: -4px; left: -6px; font-size: 0.7rem; color: #888; opacity: 0.7; transition: all 0.2s ease; cursor: pointer; padding: 4px; min-width: 20px; min-height: 20px; display: flex; align-items: center; justify-content: center; } -.highlight-relay-indicator:hover { opacity: 1; color: #aaa; transform: scale(1.1); } -.highlight-relay-indicator:active { transform: scale(0.95); } -.highlight-delete-btn { position: absolute; bottom: -4px; right: -6px; font-size: 0.7rem; color: #888; opacity: 0.7; transition: all 0.2s ease; cursor: pointer; padding: 4px; min-width: 20px; min-height: 20px; display: flex; align-items: center; justify-content: center; } -.highlight-delete-btn:hover { opacity: 1; color: #ff4444; transform: scale(1.1); } -.highlight-delete-btn:active { transform: scale(0.95); } +.highlight-relay-indicator { position: absolute; bottom: -4px; left: -6px; opacity: 0.7; } +.highlight-relay-indicator:hover { opacity: 1; } /* Mobile: Larger touch targets and better spacing */ @media (max-width: 768px) { .highlight-quote-icon { min-width: 100px; } - .highlight-relay-indicator { bottom: -8px; left: -8px; padding: 8px; min-width: var(--min-touch-target); min-height: var(--min-touch-target); font-size: 0.85rem; } - .highlight-delete-btn { bottom: -8px; right: -8px; padding: 8px; min-width: var(--min-touch-target); min-height: var(--min-touch-target); font-size: 0.85rem; } + .highlight-relay-indicator { bottom: -8px; left: -8px; padding: 8px; min-width: var(--min-touch-target); min-height: var(--min-touch-target); } + .compact-button { padding: 0.5rem; min-width: var(--min-touch-target); min-height: var(--min-touch-target); } } /* Level-colored quote icon */ @@ -137,11 +140,7 @@ .highlight-meta { display: flex; align-items: center; gap: 0.5rem; font-size: 0.8rem; color: #888; flex-wrap: nowrap; min-height: 20px; } .highlight-author { color: #aaa; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 150px; line-height: 1; } -.highlight-meta-separator { color: #666; flex-shrink: 0; line-height: 1; } -.highlight-time { color: #888; white-space: nowrap; flex-shrink: 0; line-height: 1; } .highlight-menu-wrapper { position: relative; margin-left: auto; flex-shrink: 0; } -.highlight-menu-btn { background: none; border: none; color: #888; cursor: pointer; padding: 0.25rem 0.5rem; font-size: 0.875rem; display: flex; align-items: center; transition: color 0.2s ease; border-radius: 4px; } -.highlight-menu-btn:hover { color: #646cff; background: rgba(100, 108, 255, 0.1); } .highlight-menu { position: absolute; right: 0; top: calc(100% + 4px); background: #2a2a2a; border: 1px solid #444; border-radius: 6px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); z-index: 1000; min-width: 160px; overflow: hidden; } .highlight-menu-item { width: 100%; background: none; border: none; color: #ddd; padding: 0.625rem 0.875rem; font-size: 0.875rem; display: flex; align-items: center; gap: 0.625rem; cursor: pointer; transition: all 0.15s ease; text-align: left; white-space: nowrap; } .highlight-menu-item:hover { background: rgba(100, 108, 255, 0.15); color: #fff; }