diff --git a/packages/web/components/elements/Button.tsx b/packages/web/components/elements/Button.tsx index dbd64c75a..e9085ad81 100644 --- a/packages/web/components/elements/Button.tsx +++ b/packages/web/components/elements/Button.tsx @@ -263,6 +263,7 @@ export const Button = styled('button', { padding: '4px', height: '100%', pt: '6px', + minWidth: '25px', '&:hover': { bg: '$grayBgHover', diff --git a/packages/web/components/patterns/HighlightHoverActions.tsx b/packages/web/components/patterns/HighlightHoverActions.tsx new file mode 100644 index 000000000..4d3c9ae5e --- /dev/null +++ b/packages/web/components/patterns/HighlightHoverActions.tsx @@ -0,0 +1,113 @@ +import { useState } from 'react' +import { Box, SpanBox } from '../elements/LayoutPrimitives' +import { LibraryItemNode } from '../../lib/networking/queries/useGetLibraryItemsQuery' +import { Button } from '../elements/Button' +import { theme } from '../tokens/stitches.config' +import { + ArchiveBox, + Book, + BookOpen, + Copy, + DotsThree, + Notebook, + Tag, + Trash, + Tray, +} from 'phosphor-react' +//import { CardMenu } from '../CardMenu' +import { UserBasicData } from '../../lib/networking/queries/useGetViewerQuery' +import { Highlight } from '../../lib/networking/fragments/highlightFragment' +import { showErrorToast, showSuccessToast } from '../../lib/toastHelpers' + +type HighlightHoverActionsProps = { + viewer: UserBasicData + highlight: Highlight + + isHovered: boolean + + viewInReader: (highlightId: string) => void + + setLabelsTarget: (target: Highlight) => void + setShowConfirmDeleteHighlightId: (set: string) => void +} + +export const HighlightHoverActions = (props: HighlightHoverActionsProps) => { + const [menuOpen, setMenuOpen] = useState(false) + + return ( + + + + + + + ) +} diff --git a/packages/web/components/patterns/HighlightNotes.tsx b/packages/web/components/patterns/HighlightNotes.tsx index 298437d16..c24df1193 100644 --- a/packages/web/components/patterns/HighlightNotes.tsx +++ b/packages/web/components/patterns/HighlightNotes.tsx @@ -145,6 +145,7 @@ export function MarkdownNote(props: MarkdownNote): JSX.Element { {props.mode == 'edit' ? ( ('preview') + const [isHovered, setIsHovered] = useState(false) + const [isOpen, setIsOpen] = useState(false) + + const { refs, floatingStyles, context } = useFloating({ + open: isOpen, + onOpenChange: setIsOpen, + middleware: [ + offset({ + mainAxis: -25, + }), + size(), + ], + placement: 'top-end', + whileElementsMounted: autoUpdate, + }) + + const hover = useHover(context) + + const { getReferenceProps, getFloatingProps } = useInteractions([hover]) + + console.log( + 'ref={refs.setFloating, style={floatingStyles}', + refs.setFloating, + floatingStyles + ) return ( - - {props.highlight.annotation ? ( - - ) : ( - - )} - - - - - - {/* - - */} - + *': { m: '0px', + display: 'inline', + padding: '2px', + backgroundColor: + 'rgba(var(--colors-highlightBackground), 0.35)', + boxShadow: + '1px 0 0 rgba(var(--colors-highlightBackground), 0.35), -1px 0 0 rgba(var(--colors-highlightBackground), 0.35)', + boxDecorationBreak: 'clone', + borderRadius: '2px', }, fontSize: '15px', lineHeight: 1.5, diff --git a/packages/web/components/patterns/RcEditorStyles.tsx b/packages/web/components/patterns/RcEditorStyles.tsx index 058cc6bbe..b53cba22a 100644 --- a/packages/web/components/patterns/RcEditorStyles.tsx +++ b/packages/web/components/patterns/RcEditorStyles.tsx @@ -8,7 +8,6 @@ export const RcEditorStyles = (isDark: boolean, shadow: boolean) => { borderRadius: '5px', backgroundColor: isDark ? '#2A2A2A' : 'white', border: '1px solid $thBorderSubtle', - boxShadow: shadow ? '0px 4px 4px rgba(33, 33, 33, 0.1)' : 'unset', }, '.rc-md-navigation': { borderRadius: '5px', diff --git a/packages/web/components/templates/article/HighlightViewItem.tsx b/packages/web/components/templates/article/HighlightViewItem.tsx index ac3cfab5c..df2440929 100644 --- a/packages/web/components/templates/article/HighlightViewItem.tsx +++ b/packages/web/components/templates/article/HighlightViewItem.tsx @@ -4,7 +4,6 @@ import { ReadableItem } from '../../../lib/networking/queries/useGetLibraryItems import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery' import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives' import { HighlightView } from '../../patterns/HighlightView' -import { HighlightsMenu } from '../homeFeed/HighlightItem' type HighlightViewItemProps = { viewer: UserBasicData @@ -27,8 +26,8 @@ export function HighlightViewItem(props: HighlightViewItemProps): JSX.Element { setHover(true)} onMouseLeave={() => setHover(false)} diff --git a/packages/web/components/templates/article/HighlightsLayer.tsx b/packages/web/components/templates/article/HighlightsLayer.tsx index 8d147f362..2914a08c0 100644 --- a/packages/web/components/templates/article/HighlightsLayer.tsx +++ b/packages/web/components/templates/article/HighlightsLayer.tsx @@ -26,6 +26,10 @@ import { isTouchScreenDevice } from '../../../lib/deviceType' import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery' import { ReadableItem } from '../../../lib/networking/queries/useGetLibraryItemsQuery' import { SetHighlightLabelsModalPresenter } from './SetLabelsModalPresenter' +import SlidingPane from 'react-sliding-pane' +import 'react-sliding-pane/dist/react-sliding-pane.css' +import { NotebookContent } from './Notebook' +import { NotebookHeader } from './NotebookHeader' type HighlightsLayerProps = { viewer: UserBasicData @@ -74,15 +78,13 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { const focusedHighlightMousePos = useRef({ pageX: 0, pageY: 0 }) const [currentHighlightIdx, setCurrentHighlightIdx] = useState(0) - const [focusedHighlight, setFocusedHighlight] = useState< - Highlight | undefined - >(undefined) + const [focusedHighlight, setFocusedHighlight] = + useState(undefined) const [selectionData, setSelectionData] = useSelection(highlightLocations) - const [labelsTarget, setLabelsTarget] = useState( - undefined - ) + const [labelsTarget, setLabelsTarget] = + useState(undefined) const createHighlightFromSelection = useCallback( async ( @@ -746,6 +748,50 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { if (props.showHighlightsModal) { return ( + { + props.setShowHighlightsModal(false) + }} + > + <> + + { + // The timeout here is a bit of a hack to work around rerendering + setTimeout(() => { + const target = document.querySelector( + `[omnivore-highlight-id="${highlightId}"]` + ) + target?.scrollIntoView({ + block: 'center', + behavior: 'auto', + }) + }, 1) + history.replaceState( + undefined, + window.location.href, + `#${highlightId}` + ) + + // props.setShowHighlightsModal(false) + }} + /> + + + ) + + { + /* return ( - ) + ) */ + } } return <> diff --git a/packages/web/components/templates/article/Notebook.tsx b/packages/web/components/templates/article/Notebook.tsx index 118b1d050..86cf96573 100644 --- a/packages/web/components/templates/article/Notebook.tsx +++ b/packages/web/components/templates/article/Notebook.tsx @@ -60,9 +60,8 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { const [noteText, setNoteText] = useState('') const [showConfirmDeleteHighlightId, setShowConfirmDeleteHighlightId] = useState(undefined) - const [labelsTarget, setLabelsTarget] = useState( - undefined - ) + const [labelsTarget, setLabelsTarget] = + useState(undefined) const noteState = useRef({ isCreating: false, note: undefined, @@ -174,12 +173,9 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { return } if (noteState.current.isCreating) { - console.log('note is being created, deferring') - if (noteState.current.createStarted) { const timeSinceStart = new Date().getTime() - noteState.current.createStarted.getTime() - console.log(' -- timeSinceStart: ', timeSinceStart) if (timeSinceStart > 4000) { createNote(text) @@ -208,8 +204,8 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { setNoteText('') }, [noteState, highlights]) - const [articleNotesCollapsed, setArticleNotesCollapsed] = useState(false) - const [highlightsCollapsed, setHighlightsCollapsed] = useState(false) + const [tabSelected, setTabSelected] = useState<'note' | 'highlights'>('note') + const [errorSaving, setErrorSaving] = useState(undefined) const [lastChanged, setLastChanged] = useState(undefined) const [lastSaved, setLastSaved] = useState(undefined) @@ -221,120 +217,118 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { height: '100%', width: '100%', p: '20px', + + bg: '$thLibrarySearchbox', '@mdDown': { p: '15px' }, }} > - - {!articleNotesCollapsed && ( - <> - - - - - {errorSaving && ( - - {errorSaving} - - )} - {lastSaved !== undefined ? ( - <> - {lastChanged === lastSaved - ? 'Saved' - : `Last saved ${formattedShortTime(lastSaved.toISOString())}`} - - ) : null} - - - )} - - - + {/* + setTabSelected('note')} + /> setTabSelected('highlights')} /> + */} - {!highlightsCollapsed && ( - <> - {sortedHighlights.map((highlight) => ( - { - mutate() - }} - /> - ))} - {sortedHighlights.length === 0 && ( - - You have not added any highlights to this document. - - )} - - )} - {/* + + + + */} - + alignment="start" + distribution="start" + > + {errorSaving && ( + + {errorSaving} + + )} + {lastSaved !== undefined ? ( + <> + {lastChanged === lastSaved + ? 'Saved' + : `Last saved ${formattedShortTime(lastSaved.toISOString())}`} + + ) : null} + + + {/* )} */} + + {/* {tabSelected == 'highlights' && ( */} + + {sortedHighlights.map((highlight) => ( + { + mutate() + }} + /> + ))} + {sortedHighlights.length === 0 && ( + + You have not added any highlights to this document. + + )} + + {/* )} */} {showConfirmDeleteHighlightId && ( void + selected: boolean + setSelected: (set: boolean) => void } function SectionTitle(props: SectionTitleProps): JSX.Element { @@ -408,30 +402,25 @@ function SectionTitle(props: SectionTitleProps): JSX.Element { display: 'flex', alignItems: 'center', gap: '5px', + color: props.selected ? '$thTextContrast' : '$thTextSubtle', + borderBottom: props.selected + ? '1px solid $thTextContrast' + : '1px solid transparent', }} onClick={(event) => { - props.setCollapsed(!props.collapsed) + props.setSelected(true) event.stopPropagation() }} > - {props.collapsed ? ( - - ) : ( - - )} diff --git a/packages/web/components/templates/article/NotebookHeader.tsx b/packages/web/components/templates/article/NotebookHeader.tsx index 0b19bbf3d..187664f5a 100644 --- a/packages/web/components/templates/article/NotebookHeader.tsx +++ b/packages/web/components/templates/article/NotebookHeader.tsx @@ -31,7 +31,9 @@ export const NotebookHeader = (props: NotebookHeaderProps) => { top: '0px', height: '50px', p: '20px', - background: '#F8FAFB', + borderTopLeftRadius: '10px', + overflow: 'clip', + background: '$thLibrarySearchbox', zIndex: 10, }} > @@ -49,18 +51,18 @@ export const NotebookHeader = (props: NotebookHeaderProps) => { alignment="center" > }> - {/* { - exportHighlights() + // exportHighlights() }} title="Export Notebook" /> { - setShowConfirmDeleteNote(true) + // setShowConfirmDeleteNote(true) }} title="Delete Article Note" - /> */} + /> diff --git a/packages/web/components/templates/article/SetLabelsModal.tsx b/packages/web/components/templates/article/SetLabelsModal.tsx index 9b484e015..06f021591 100644 --- a/packages/web/components/templates/article/SetLabelsModal.tsx +++ b/packages/web/components/templates/article/SetLabelsModal.tsx @@ -14,6 +14,7 @@ import { useGetLabelsQuery } from '../../../lib/networking/queries/useGetLabelsQ import { v4 as uuidv4 } from 'uuid' import { randomLabelColorHex } from '../../../utils/settings-page/labels/labelColorObjects' import { LabelsDispatcher } from '../../../lib/hooks/useSetPageLabels' +import * as Dialog from '@radix-ui/react-dialog' type SetLabelsModalProps = { provider: LabelsProvider @@ -30,9 +31,8 @@ export function SetLabelsModal(props: SetLabelsModalProps): JSX.Element { const availableLabels = useGetLabelsQuery() const [tabCount, setTabCount] = useState(-1) const [tabStartValue, setTabStartValue] = useState('') - const [errorMessage, setErrorMessage] = useState( - undefined - ) + const [errorMessage, setErrorMessage] = + useState(undefined) const errorTimeoutRef = useRef() const [highlightLastLabel, setHighlightLastLabel] = useState(false) @@ -171,44 +171,46 @@ export function SetLabelsModal(props: SetLabelsModalProps): JSX.Element { return ( - - { - event.preventDefault() - props.onOpenChange(false) - }} - onEscapeKeyDown={(event) => { - props.onOpenChange(false) - event.preventDefault() - }} - > - - - - - - - + + + { + event.preventDefault() + props.onOpenChange(false) + }} + onEscapeKeyDown={(event) => { + props.onOpenChange(false) + event.preventDefault() + }} + > + + + + + + + + ) } diff --git a/packages/web/package.json b/packages/web/package.json index bbce82d83..92050b88b 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -62,6 +62,7 @@ "react-markdown-editor-lite": "^1.3.4", "react-masonry-css": "^1.0.16", "react-pro-sidebar": "^0.7.1", + "react-sliding-pane": "^7.3.0", "react-spinners": "^0.13.7", "react-super-responsive-table": "^5.2.1", "react-topbar-progress-indicator": "^4.1.1", diff --git a/packages/web/styles/globals.css b/packages/web/styles/globals.css index 569bddf90..df1934426 100644 --- a/packages/web/styles/globals.css +++ b/packages/web/styles/globals.css @@ -419,20 +419,22 @@ button { margin: 0px; } -.omnivore-masonry-grid { - display: -webkit-box; /* Not needed if autoprefixing */ - display: -ms-flexbox; /* Not needed if autoprefixing */ - display: flex; - margin-left: -16px; /* gutter size offset */ - margin-right: 14px; - width: auto; +.slide-panel-overlay { + z-index: 100 !important; + background: transparent !important; + pointer-events: none; } -.omnivore-masonry-grid_column { - padding-left: 16px; /* gutter size */ - background-clip: padding-box; + +.slide-pane__content { + padding: 0px !important; + pointer-events: all; + border-top-left-radius: 10px; + border-bottom-right-radius: 10px; + border-left: 1px solid var(--colors-thNotebookSubtle); + background: var(--colors-thLibrarySearchbox); } -/* .omnivore-masonry-grid_column > div { - background: grey; - margin-bottom: 16px; -} */ +.slide-pane { + background: transparent !important; + box-shadow: 0px 4px 4px rgba(33, 33, 33, 0.1) !important; +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 36db67d18..eb21f53dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14467,6 +14467,11 @@ executable@^4.1.1: dependencies: pify "^2.2.0" +exenv@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d" + integrity sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw== + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -23841,6 +23846,11 @@ react-is@^18.0.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== +react-lifecycles-compat@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + react-markdown-editor-lite@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/react-markdown-editor-lite/-/react-markdown-editor-lite-1.3.4.tgz#77992d2389b9427a06595c63d95f52be66e5fea9" @@ -23877,6 +23887,16 @@ react-masonry-css@^1.0.16: resolved "https://registry.yarnpkg.com/react-masonry-css/-/react-masonry-css-1.0.16.tgz#72b28b4ae3484e250534700860597553a10f1a2c" integrity sha512-KSW0hR2VQmltt/qAa3eXOctQDyOu7+ZBevtKgpNDSzT7k5LA/0XntNa9z9HKCdz3QlxmJHglTZ18e4sX4V8zZQ== +react-modal@^3.14.3: + version "3.16.1" + resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.16.1.tgz#34018528fc206561b1a5467fc3beeaddafb39b2b" + integrity sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg== + dependencies: + exenv "^1.2.0" + prop-types "^15.7.2" + react-lifecycles-compat "^3.0.0" + warning "^4.0.3" + react-popper-tooltip@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/react-popper-tooltip/-/react-popper-tooltip-3.1.1.tgz#329569eb7b287008f04fcbddb6370452ad3f9eac" @@ -23960,6 +23980,14 @@ react-slidedown@^2.4.5: dependencies: tslib "^2.0.0" +react-sliding-pane@^7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/react-sliding-pane/-/react-sliding-pane-7.3.0.tgz#a6a03b90db216e7ec6f746c7e649d19ba03ff4e0" + integrity sha512-KCyxw2BBvXjwYm1UX83Vk67D4kxec2icJxrSPidNus8voh1yB1K6bluwShAe3OvN5zk8H9InL22jGomTUOOudw== + dependencies: + prop-types "^15.7.2" + react-modal "^3.14.3" + react-spinners@^0.13.7: version "0.13.7" resolved "https://registry.yarnpkg.com/react-spinners/-/react-spinners-0.13.7.tgz#0f423c415bfa56765ce9fb36ff604e52a92b37a9" @@ -27779,7 +27807,7 @@ walker@~1.0.5: dependencies: makeerror "1.0.12" -warning@^4.0.2: +warning@^4.0.2, warning@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==