diff --git a/external/@worldbrain/memex-common b/external/@worldbrain/memex-common index 6c6a06bb46..1f2c085088 160000 --- a/external/@worldbrain/memex-common +++ b/external/@worldbrain/memex-common @@ -1 +1 @@ -Subproject commit 6c6a06bb4654ead77903d28af37739d42ea5535d +Subproject commit 1f2c08508800c91bc61b2f2052808251fc9fd308 diff --git a/src/annotations/components/AnnotationEditable.tsx b/src/annotations/components/AnnotationEditable.tsx index 185ebaa4cf..f67cebafdd 100644 --- a/src/annotations/components/AnnotationEditable.tsx +++ b/src/annotations/components/AnnotationEditable.tsx @@ -85,6 +85,7 @@ export interface AnnotationProps { hasReplies?: boolean appendRepliesToggle?: boolean isBulkShareProtected: boolean + focusLockUntilMouseStart?: boolean repliesLoadingState?: UITaskState onReplyBtnClick?: React.MouseEventHandler isClickable?: boolean @@ -143,6 +144,7 @@ export interface AnnotationProps { spaceName: string, unifiedAnnotationId: UnifiedAnnotation['unifiedId'], ) => void + setAnnotationInFocus?: (unifiedId: string) => void isInFocus?: boolean shiftSelectItem?: () => void searchTerms?: string[] @@ -295,11 +297,16 @@ export default class AnnotationEditable extends React.Component { } if (this.props.isInFocus && !prevProps.isInFocus) { this.setupKeyListener() - const itemBox = this.itemBoxRef.current - if (itemBox && !this.props.hoverState) { - itemBox.scrollIntoView({ block: 'center' }) + if (!this.state.hoverCard) { + const itemBox = this.itemBoxRef.current + if (itemBox && !this.props.hoverState) { + itemBox.scrollIntoView({ block: 'center' }) + } } } else if (!this.props.isInFocus && prevProps.isInFocus) { + // if (!this.state.hoverCard) { + // this.props.setAnnotationInFocus(null) + // } this.removeKeyListener() } } @@ -949,6 +956,7 @@ export default class AnnotationEditable extends React.Component { this.props.unifiedId, buttonRef: this.shareMenuButtonRef, leftSideItem: this.renderAutoAddedIndicator(), + showKeyShortcut: this.props.isInFocus && 'S', }, { key: 'copy-paste-note-btn', @@ -981,6 +989,7 @@ export default class AnnotationEditable extends React.Component { this.props.copyPasterAnnotationInstanceId === this.props.unifiedId, buttonRef: this.copyPasterButtonRef, + showKeyShortcut: this.props.isInFocus && 'C', }, appendRepliesToggle && { key: 'show-replies-notes-btn', @@ -1012,7 +1021,6 @@ export default class AnnotationEditable extends React.Component { ) { return ( { return ( { hoverTimeout = null handleMouseEnter = () => { - this.hoverTimeout = setTimeout(() => { + if (!this.props.focusLockUntilMouseStart) { this.setState({ hoverCard: true }) - }, 300) + this.hoverTimeout = setTimeout(() => { + this.props.setAnnotationInFocus(this.props.unifiedId) + }, 300) + } } handleMouseLeave = () => { - if (this.hoverTimeout) { - console.log('clearing timeout') - clearTimeout(this.hoverTimeout) - this.hoverTimeout = null - } - if (!this.isAnyModalOpen()) { + if (!this.props.focusLockUntilMouseStart) { this.setState({ hoverCard: false }) + if (this.hoverTimeout) { + clearTimeout(this.hoverTimeout) + this.hoverTimeout = null + } + if (!this.isAnyModalOpen()) { + this.props.setAnnotationInFocus(null) + } } } @@ -1301,7 +1313,7 @@ export default class AnnotationEditable extends React.Component { const actionsBox = !this.props.isEditingHighlight ? ( - {this.state.hoverCard && ( + {this.props.isInFocus && ( <> {footerDeps.onDeleteIconClick && ( { firstDivProps={{ id: ANNOT_BOX_ID_PREFIX + this.props.unifiedId, }} - hoverState={this.props.isInFocus} + hoverState={ + this.props.isInFocus || this.state.hoverCard + } onRef={this.itemBoxRef} > {this.renderDeleteScreen(footerDeps)} @@ -1637,18 +1651,15 @@ const SaveActionBar = styled.div` const slideInFromBottom = keyframes` from { - opacity: 0; margin-top: -10px; } to { - opacity: 1; margin-top: 0px; } ` const DefaultFooterStyled = styled.div<{ compactVersion: boolean - hoverCard: boolean inFocus: boolean inPageMode?: boolean inEditMode?: boolean @@ -1662,27 +1673,26 @@ const DefaultFooterStyled = styled.div<{ box-sizing: border-box; border-radius: 0 0 12px 12px; position: relative; - animation: ${slideInFromBottom} 0.1s ease-out; - background: ${(props) => props.theme.colors.black}98; + background: ${(props) => props.theme.colors.black0}98; backdrop-filter: blur(5px); width: 100%; + opacity: 0; ${(props) => props.inFocus && css` - animation: none; + animation: ${slideInFromBottom} 0.1 + cubic-bezier(0.22, 0.61, 0.36, 1) forwards; position: relative; display: flex; + opacity: 1; `}; - ${(props) => - props.hoverCard && - css` - display: flex; - `} ${(props) => props.inEditMode && css` + animation: ${slideInFromBottom} 0.1 + cubic-bezier(0.22, 0.61, 0.36, 1) forwards; display: flex; position: relative; `} diff --git a/src/dashboard-refactor/index.tsx b/src/dashboard-refactor/index.tsx index 062e215d27..3ced7efb85 100644 --- a/src/dashboard-refactor/index.tsx +++ b/src/dashboard-refactor/index.tsx @@ -2,7 +2,6 @@ import React from 'react' import styled, { css, keyframes } from 'styled-components' import browser from 'webextension-polyfill' import ListShareModal from '@worldbrain/memex-common/lib/content-sharing/ui/list-share-modal' -import { createGlobalStyle } from 'styled-components' import { sizeConstants } from 'src/dashboard-refactor/constants' import { StatefulUIElement } from 'src/util/ui-logic' import { DashboardLogic } from './logic' @@ -65,12 +64,8 @@ import { ColorThemeKeys, IconKeys, } from '@worldbrain/memex-common/lib/common-ui/styles/types' -import PageCitations from 'src/citations/PageCitations' -import CopyPaster from 'src/copy-paster/components/CopyPaster' -import { PageSearchCopyPaster } from 'src/copy-paster' import BulkEditCopyPaster from 'src/copy-paster/BulkEditCopyPaster' import { OverlayModals } from '@worldbrain/memex-common/lib/common-ui/components/overlay-modals' -import IconBox from '@worldbrain/memex-common/lib/common-ui/components/icon-box' import { TooltipBox } from '@worldbrain/memex-common/lib/common-ui/components/tooltip-box' import KeyboardShortcuts from '@worldbrain/memex-common/lib/common-ui/components/keyboard-shortcuts' @@ -957,6 +952,20 @@ export class DashboardContainer extends StatefulUIElement< this.processEvent('getHighlightColorSettings', null) } highlightColorSettings={this.state.highlightColors} + setAnnotationInFocus={(unifiedId: string) => { + if (unifiedId == null) { + this.processEvent('changeFocusItem', { + item: { + id: null, + type: null, + }, + }) + } else { + this.processEvent('changeFocusItem', { + item: { id: unifiedId, type: 'note' }, + }) + } + }} getListDetailsById={this.getListDetailsById} youtubeService={this.youtubeService} toggleSortMenuShown={() => @@ -1072,6 +1081,7 @@ export class DashboardContainer extends StatefulUIElement< type: type, }) } + focusLockUntilMouseStart={this.state.focusLockUntilMouseStart} pageInteractionProps={{ onClick: (day, pageResultId) => async (event) => { this.processEvent('clickPageResult', { @@ -1179,21 +1189,11 @@ export class DashboardContainer extends StatefulUIElement< ].isShareMenuShown, }), onMainContentHover: (day, pageId) => () => { - if (this.state.focusLockUntilMouseStart) { - return - } - this.processEvent('setPageHover', { - day, + this.processEvent('onMainContentHover', { pageResultId: pageId, + day, hover: 'main-content', }) - - this.processEvent('changeFocusItem', { - item: { - id: pageId, - type: 'page', - }, - }) }, onFooterHover: (day, pageId) => () => this.processEvent('setPageHover', { @@ -1214,14 +1214,14 @@ export class DashboardContainer extends StatefulUIElement< hover: 'lists', }), onUnhover: (day, pageId) => () => { + // if (this.state.focusLockUntilMouseStart) { + // return + // } this.processEvent('setPageHover', { day, pageResultId: pageId, hover: null, }) - if (this.state.focusLockUntilMouseStart) { - return - } this.processEvent('changeFocusItem', { item: { id: null, diff --git a/src/dashboard-refactor/logic.ts b/src/dashboard-refactor/logic.ts index 5a5509e111..5170c102b3 100644 --- a/src/dashboard-refactor/logic.ts +++ b/src/dashboard-refactor/logic.ts @@ -684,7 +684,7 @@ export class DashboardLogic extends UILogic { ) { if (this.increment > 1) { const blurContainer = document.getElementById('BlurContainer') - blurContainer.style.background = '#313239' + blurContainer.style.background = this.options.theme.colors.black } else { this.emitMutation({ blurEffectReset: { $set: true }, @@ -698,7 +698,7 @@ export class DashboardLogic extends UILogic { const blurContainer = document.getElementById( 'BlurContainer', ) - blurContainer.style.background = '#313239' + blurContainer.style.background = this.options.theme.colors.black if ( // @ts-ignore !blurContainer.style.backdropFilter || @@ -1043,8 +1043,13 @@ export class DashboardLogic extends UILogic { }) } if (event.item?.id != null) { + const itemPos = event.item?.id?.split('-')[0] + if (parseFloat(itemPos) === currentFocusElementIndex) { + return + } + const itemId = event.item.id?.replace(/^[^-]*-/, '') const nextFocusItemIndex = selectedBlocksArray.findIndex( - (item) => item?.id === event.item.id, + (item) => item?.id === itemId, ) nextItem = selectedBlocksArray[nextFocusItemIndex] @@ -1053,6 +1058,16 @@ export class DashboardLogic extends UILogic { }) } + if ( + event.item?.id == null && + event.direction == undefined && + event.item?.type == null + ) { + this.emitMutation({ + focusedBlockId: { $set: null }, + }) + } + if (nextItem) { if (nextItem.type === 'page') { this.emitMutation({ @@ -2552,7 +2567,30 @@ export class DashboardLogic extends UILogic { }) } - hoverTimeout = null + onMainContentHover: EventHandler<'setPageHover'> = ({ + event, + previousState, + }) => { + this.setHoverState( + event.day, + event.pageResultId, + event.hover, + previousState, + ) + } + + setHoverState(day, pageResultId, hover, previousState) { + this.processUIEvent('setPageHover', { + event: { pageResultId, day, hover }, + previousState, + }) + this.processUIEvent('changeFocusItem', { + event: { + item: { id: pageResultId, type: 'page' }, + }, + previousState, + }) + } setPageHover: EventHandler<'setPageHover'> = ({ event, previousState }) => { const emitMutation = () => @@ -2593,22 +2631,7 @@ export class DashboardLogic extends UILogic { return } - if (event.hover === null) { - console.log('clearing timeout') - if (this.hoverTimeout) { - clearTimeout(this.hoverTimeout) - this.hoverTimeout = null - emitMutation() - return - } - } - - if (this.hoverTimeout) { - clearTimeout(this.hoverTimeout) - } - this.hoverTimeout = setTimeout(() => { - emitMutation() - }, 300) + emitMutation() } setPageNewNoteTagPickerShown: EventHandler< diff --git a/src/dashboard-refactor/search-results/components/page-result.tsx b/src/dashboard-refactor/search-results/components/page-result.tsx index 5bca4963d9..728dcef106 100644 --- a/src/dashboard-refactor/search-results/components/page-result.tsx +++ b/src/dashboard-refactor/search-results/components/page-result.tsx @@ -62,6 +62,7 @@ export interface Props searchType: SearchType hasNotes: boolean isInFocus?: boolean + focusLockUntilMouseStart?: boolean } export default class PageResultView extends PureComponent { @@ -102,12 +103,15 @@ export default class PageResultView extends PureComponent { componentDidUpdate(prevProps: Props) { if (this.props.isInFocus && !prevProps.isInFocus) { this.setupKeyListener() - const itemBox = this.itemBoxRef.current - if (itemBox && !this.props.hoverState) { - itemBox.scrollIntoView({ block: 'center' }) + if (this.props.hoverState !== 'main-content') { + const itemBox = this.itemBoxRef.current + if (itemBox && !this.props.hoverState) { + itemBox.scrollIntoView({ block: 'center' }) + } } } else if (!this.props.isInFocus && prevProps.isInFocus) { this.removeKeyListener() + // this.props.onUnhover(null) } } @@ -118,6 +122,7 @@ export default class PageResultView extends PureComponent { tutorialId: null, showFooterBar: false, searchTermMatches: null, + hoveredMouse: false, } componentDidMount() { @@ -301,7 +306,11 @@ export default class PageResultView extends PureComponent { placement={'bottom-end'} offsetX={10} strategy={'fixed'} - closeComponent={this.props.onCopyPasterBtnClick} + closeComponent={(event) => { + this.props.onCopyPasterBtnClick(event) + this.props.onUnhover(event) + this.setState({ hoveredMouse: false }) + }} getPortalRoot={this.props.getRootElement} > {this.props.renderPageCitations()} @@ -317,9 +326,11 @@ export default class PageResultView extends PureComponent { targetElementRef={this.spacePickerBarRef.current} placement={'bottom-end'} offsetX={10} - closeComponent={(event) => + closeComponent={(event) => { this.listPickerBtnClickHandler(event) - } + this.props.onUnhover(event) + this.setState({ hoveredMouse: false }) + }} strategy={'fixed'} getPortalRoot={this.props.getRootElement} > @@ -334,9 +345,11 @@ export default class PageResultView extends PureComponent { targetElementRef={this.spacePickerButtonRef.current} placement={'bottom-start'} offsetX={10} - closeComponent={(event) => + closeComponent={(event) => { this.listPickerBtnClickHandler(event) - } + this.props.onUnhover(event) + this.setState({ hoveredMouse: false }) + }} strategy={'fixed'} getPortalRoot={this.props.getRootElement} > @@ -912,12 +925,45 @@ export default class PageResultView extends PureComponent { ) } + hoverTimeout = null + + onMainContentHover = (event) => { + console.log('focuslock', this.props.focusLockUntilMouseStart) + if (!this.props.focusLockUntilMouseStart) { + this.setState({ hoveredMouse: true }) + if (this.hoverTimeout) { + clearTimeout(this.hoverTimeout) + this.hoverTimeout = null + this.props.onMainContentHover(event) + return + } + + if (this.hoverTimeout) { + clearTimeout(this.hoverTimeout) + } + this.hoverTimeout = setTimeout(() => { + this.props.onMainContentHover(event) + }, 300) + } + } + + onUnhover = (event) => { + if (!this.props.focusLockUntilMouseStart) { + this.setState({ hoveredMouse: false }) + if (this.hoverTimeout) { + clearTimeout(this.hoverTimeout) + this.hoverTimeout = null + } + this.props.onUnhover(event) + } + } + render() { return ( { onDragEnd: !this.props.isCopyPasterShown && this.props.onPageDrop, }} - hoverState={this.props.isInFocus} + hoverState={this.props.isInFocus || this.state.hoveredMouse} onRef={this.itemBoxRef} // Passing the ref as a prop > @@ -948,7 +994,7 @@ export default class PageResultView extends PureComponent { > {this.props.hoverState != null || this.props.isBulkSelected ? ( - + {this.props.hoverState != null && ( {this.renderEditButton()} @@ -1151,6 +1197,7 @@ const FooterBar = styled.div<{ inPageMode?: boolean inTitleEditMode?: boolean }>` + display: none; bottom: 0; width: 100%; z-index: 999999; @@ -1160,16 +1207,15 @@ const FooterBar = styled.div<{ backdrop-filter: unset; margin-top: -5px; position: relative; - animation: ${slideInFromBottom} 0.1s cubic-bezier(0.22, 0.61, 0.36, 1) - forwards; - display: none; + opacity: 0; background: ${(props) => props.theme.colors.black}98; backdrop-filter: blur(5px); ${(props) => props.inFocus && css` - animation: none; + animation: ${slideInFromBottom} 1s cubic-bezier(0.22, 0.61, 0.36, 1) + forwards; position: relative; display: flex; `}; @@ -1182,6 +1228,8 @@ const FooterBar = styled.div<{ ${(props) => props.shouldShow && css` + animation: ${slideInFromBottom} 0.1s + cubic-bezier(0.22, 0.61, 0.36, 1) forwards; display: flex; `}; ${(props) => @@ -1271,7 +1319,9 @@ const LoadingBox = styled.div` z-index: 10000000; ` -const PageActionBox = styled.div` +const PageActionBox = styled.div<{ + inPageMode: boolean +}>` display: flex; justify-content: space-between; grid-gap: 5px; @@ -1284,6 +1334,13 @@ const PageActionBox = styled.div` backdrop-filter: blur(5px); border-radius: 8px; align-items: center; + + ${(props) => + props.inPageMode && + css` + background: ${(props) => props.theme.colors.black}; + backdrop-filter: none; + `} ` const NotesCounterContainer = styled.div` @@ -1302,13 +1359,11 @@ const NotesCounterTitle = styled.span` ` const NoteCounter = styled.span` - color: ${(props) => props.theme.colors.black}; font-weight: 400; font-size: 12px; margin-left: 5px; border-radius: 30px; padding: 2px 10px; - background: ${(props) => props.theme.colors.headerGradient}; text-align: center; position: absolute; right: 10px; diff --git a/src/dashboard-refactor/search-results/index.tsx b/src/dashboard-refactor/search-results/index.tsx index c46a6cffbb..a4f956d309 100644 --- a/src/dashboard-refactor/search-results/index.tsx +++ b/src/dashboard-refactor/search-results/index.tsx @@ -144,6 +144,7 @@ export type Props = RootState & saveHighlightColorSettings: (newState: HighlightColor[]) => void getHighlightColorSettings: () => void highlightColorSettings: HighlightColor[] + setAnnotationInFocus: (unifiedId: string) => void getRootElement: () => HTMLElement showSpacesTab: (pageUrl) => void // onEditPageBtnClick: ( @@ -164,6 +165,7 @@ export type Props = RootState & updateSpacesSearchSuggestions?: (query: string) => void spaceSearchSuggestions?: SpaceSearchSuggestion[] shiftSelectItems: (itemId: string, type: 'notes' | 'pages') => void + focusLockUntilMouseStart: boolean } export interface State { @@ -380,6 +382,7 @@ export default class SearchResultsContainer extends React.Component< color={noteColor ?? null} body={noteData.highlight} comment={noteData.comment} + focusLockUntilMouseStart={this.props.focusLockUntilMouseStart} isShared={noteData.isShared} getListDetailsById={this.props.getListDetailsById} isBulkShareProtected={noteData.isBulkShareProtected} @@ -412,6 +415,7 @@ export default class SearchResultsContainer extends React.Component< shiftSelectItem={() => this.props.shiftSelectItems(noteId, 'notes') } + setAnnotationInFocus={this.props.setAnnotationInFocus} isInFocus={noteData.isInFocus} getHighlightColorSettings={this.props.getHighlightColorSettings} highlightColorSettings={this.props.highlightColorSettings} @@ -758,6 +762,9 @@ export default class SearchResultsContainer extends React.Component< this.props.listData.byId[this.props.selectedListId] ?.localId } + focusLockUntilMouseStart={ + this.props.focusLockUntilMouseStart + } isNotesSidebarShown={this.props.isNotesSidebarShown} isListsSidebarShown={this.props.isSpacesSidebarLocked} showPopoutsForResultBox={(show) => @@ -1587,6 +1594,7 @@ const PageTopBarBox = styled.div<{ props.inPageMode && css` position: relative; + background: ${(props) => props.theme.colors.black}; `} ` diff --git a/src/dashboard-refactor/search-results/types.ts b/src/dashboard-refactor/search-results/types.ts index 635c59bf29..1646f6134d 100644 --- a/src/dashboard-refactor/search-results/types.ts +++ b/src/dashboard-refactor/search-results/types.ts @@ -336,6 +336,7 @@ export type Events = UIEvent<{ setPageNotesSort: PageEventArgs & { sortingFn: AnnotationsSorter } setPageNotesType: PageEventArgs & { noteType: NotesType } setPageHover: PageEventArgs & { hover: ResultHoverState } + onMainContentHover: PageEventArgs & { hover: ResultHoverState } removePageFromList: PageEventArgs clearInbox: null bulkSelectItems: {