Skip to content

Commit

Permalink
Merge branch 'feature/list-sidebar-kb-nav' into release/3.20.0
Browse files Browse the repository at this point in the history
  • Loading branch information
poltak committed Jul 12, 2024
2 parents 2af364e + bc5602c commit fe1c196
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 79 deletions.
36 changes: 24 additions & 12 deletions src/dashboard-refactor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ import SidebarToggle from './header/sidebar-toggle'
import { Rnd } from 'react-rnd'
import { AnnotationsSidebarInDashboardResults as NotesSidebar } from 'src/sidebar/annotations-sidebar/containers/AnnotationsSidebarInDashboardResults'
import { AnnotationsSidebarContainer as NotesSidebarContainer } from 'src/sidebar/annotations-sidebar/containers/AnnotationsSidebarContainer'
import { updatePickerValues, stateToSearchParams, getListData } from './util'
import {
updatePickerValues,
stateToSearchParams,
getListData,
getOwnLists,
} from './util'
import analytics from 'src/analytics'
import { copyToClipboard } from 'src/annotations/content_script/utils'
import { deriveStatusIconColor } from './header/sync-status-menu/util'
Expand Down Expand Up @@ -72,6 +77,7 @@ import { UpdateNotifBanner } from 'src/common-ui/containers/UpdateNotifBanner'
import { defaultOrderableSorter } from '@worldbrain/memex-common/lib/utils/item-ordering'
import type { HighlightColor } from '@worldbrain/memex-common/lib/common-ui/components/highlightColorPicker/types'
import ImagePreviewModal from '@worldbrain/memex-common/lib/common-ui/image-preview-modal'
import type { ListTrees } from 'src/custom-lists/ui/list-trees'

export type Props = DashboardDependencies & {
getRootElement: () => HTMLElement
Expand Down Expand Up @@ -166,6 +172,7 @@ export class DashboardContainer extends StatefulUIElement<
imageSupportBG: runInBackground(),
}

private listTreesRef = React.createRef<ListTrees>()
private notesSidebarRef = React.createRef<NotesSidebarContainer>()
private syncStatusButtonRef = React.createRef<HTMLDivElement>()
youtubeService: YoutubeService
Expand All @@ -175,7 +182,13 @@ export class DashboardContainer extends StatefulUIElement<
}

constructor(props: Props) {
super(props, new DashboardLogic(props))
super(
props,
new DashboardLogic({
...props,
getListTreesRef: () => this.listTreesRef.current,
}),
)

this.youtubeService = new YoutubeService(createYoutubeServiceOptions())
;(window as any)['_state'] = () => ({ ...this.state })
Expand Down Expand Up @@ -658,16 +671,9 @@ export class DashboardContainer extends StatefulUIElement<
? { type: 'user-reference', id: currentUser.id }
: undefined

const ownListsData = allLists
.filter(
(list) =>
list.type === 'user-list' &&
cacheUtils.deriveListOwnershipStatus(
list,
userReference,
) === 'Creator',
)
.sort(defaultOrderableSorter)
const ownListsData = getOwnLists(allLists, currentUser).sort(
defaultOrderableSorter,
)
const followedListsData = allLists.filter(
(list) =>
list.type === 'user-list' &&
Expand All @@ -686,6 +692,7 @@ export class DashboardContainer extends StatefulUIElement<
<ListsSidebarContainer
{...listsSidebar}
listTreesDeps={{
ref: this.listTreesRef,
lists: ownListsData,
authBG: this.props.authBG,
listsBG: this.props.listsBG,
Expand All @@ -694,6 +701,9 @@ export class DashboardContainer extends StatefulUIElement<
areListsBeingFiltered:
this.state.listsSidebar.filteredListIds.length > 0,
}}
setFocusedListId={(listId) =>
this.processEvent('setFocusedListId', { listId })
}
spaceSidebarWidth={this.state.listsSidebar.spaceSidebarWidth}
openRemoteListPage={(remoteListId) =>
this.props.openSpaceInWebUI(remoteListId)
Expand Down Expand Up @@ -726,6 +736,8 @@ export class DashboardContainer extends StatefulUIElement<
this.processEvent('confirmListCreate', { value }),
onSearchQueryChange: (query) =>
this.processEvent('setListQueryValue', { query }),
onInputKeyDown: (key) =>
this.processEvent('handleListQueryKeyPress', { key }),
onInputClear: () =>
this.processEvent('setListQueryValue', { query: '' }),
areLocalListsEmpty: !ownListsData.length,
Expand Down
10 changes: 2 additions & 8 deletions src/dashboard-refactor/lists-sidebar/components/search-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ const CreateBox = styled.div`

export interface ListsSidebarSearchBarProps {
onInputClear(): void
onInputKeyDown(key: string): void
onCreateNew(newListName: string): void
onSearchQueryChange(inputString: string): void
areLocalListsEmpty: boolean
Expand Down Expand Up @@ -164,14 +165,7 @@ export default class ListsSidebarSearchBar extends PureComponent<
e,
) => {
e.stopPropagation()
if (e.key === 'Escape') {
this.handleClearSearch()
}

if (e.key === 'Enter') {
this.props.onCreateNew(this.props.searchQuery)
this.handleClearSearch()
}
this.props.onInputKeyDown(e.key)
}

render(): JSX.Element {
Expand Down
77 changes: 40 additions & 37 deletions src/dashboard-refactor/lists-sidebar/components/sidebar-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,48 +23,54 @@ export interface Props {
spaceSidebarWidth: string
sidebarItemRef?: (el: any) => void
isMenuDisplayed?: boolean

/** This overrides `state.isHovering` if set. If setting you must also set `props.setFocused` to control it. */
isFocused?: boolean
setFocused?: (isFocused: boolean) => void
}

export interface State {
isHovering: boolean
canDisableHover: boolean
/** Don't access this directly. Use `isFocused` calculated getter. */
_isFocused: boolean
}

export default class ListsSidebarItem extends React.PureComponent<
Props,
State
> {
state: State = { isHovering: false, canDisableHover: false }
state: State = { _isFocused: false }

private setFocused = (isFocused: boolean) => {
if (this.props.setFocused) {
this.props.setFocused(isFocused)
} else {
this.setState({ _isFocused: isFocused })
}
}

private get isFocused(): boolean {
return this.props.isFocused ?? this.state._isFocused
}

private get shouldShowRightSideIcon(): boolean {
return (
this.isFocused ||
this.props.isShared ||
this.state.isHovering ||
this.props.dragNDropActions?.isDraggedOver ||
this.props.dragNDropActions?.wasPageDropped ||
this.props.forceRightSidePermanentDisplay
)
}

render() {
if (!this.props.areAnyMenusDisplayed && this.state.canDisableHover) {
this.setState({ isHovering: false, canDisableHover: false })
}

return (
<Container
onMouseEnter={() => this.setState({ isHovering: true })}
isHovering={this.state.isHovering}
isHovering={this.isFocused}
onMouseEnter={() => this.setFocused(true)}
onMouseLeave={() => {
if (!this.props.areAnyMenusDisplayed) {
this.setState({
isHovering: false,
canDisableHover: true,
})
this.setFocused(false)
}
this.setState({
canDisableHover: true,
})
}}
spaceSidebarWidth={this.props.spaceSidebarWidth}
onDragEnter={this.props.dragNDropActions?.onDragEnter}
Expand All @@ -83,13 +89,14 @@ export default class ListsSidebarItem extends React.PureComponent<
isSelected={this.props.isSelected}
dragNDropActions={this.props.dragNDropActions}
name={this.props.name} // Add this line
isFocused={this.isFocused}
>
<LeftSideIconContainer
alwaysShowRightSideIcon={
this.props.alwaysShowRightSideIcon
}
>
{(this.state.isHovering ||
{(this.isFocused ||
this.props.alwaysShowLeftSideIcon ||
this.props.alwaysShowRightSideIcon) &&
this.props.renderLeftSideIcon?.()}
Expand All @@ -102,6 +109,7 @@ export default class ListsSidebarItem extends React.PureComponent<
this.props.dragNDropActions?.onDragStart
}
onDragEnd={this.props.dragNDropActions?.onDragEnd}
isFocused={this.isFocused}
draggable
>
<ListTitle>
Expand All @@ -115,7 +123,7 @@ export default class ListsSidebarItem extends React.PureComponent<
this.props.alwaysShowRightSideIcon
}
>
<IconBox {...this.props} {...this.state}>
<IconBox {...this.props} isFocused={this.isFocused}>
{this.props.renderEditIcon?.()}
</IconBox>
{this.shouldShowRightSideIcon &&
Expand Down Expand Up @@ -197,13 +205,15 @@ const Name = styled.div`

const TitleBox = styled.div<{
draggable: boolean
isFocused: boolean
}>`
display: flex;
flex: 0 1 100%;
width: 30%;
height: 100%;
align-items: center;
color: ${(props) => props.theme.colors.greyScale5};
${(props) => (props.isFocused ? 'width: 30%;' : '')}
`

const SidebarItem = styled.div<Props>`
Expand All @@ -227,10 +237,6 @@ const SidebarItem = styled.div<Props>`
: 'transparent'};
&:hover ${TitleBox} {
width: 30%;
}
${({ isSelected }) =>
isSelected &&
css`
Expand All @@ -241,20 +247,17 @@ const SidebarItem = styled.div<Props>`
cursor: 'pointer';
&:hover {
background: ${(props) => props.theme.colors.greyScale1_5};
${(props) =>
props.isFocused &&
`
background: ${props.theme.colors.greyScale1_5};
${({ isSelected }: Props) =>
isSelected &&
css`
background: ${(props) => props.theme.colors.greyScale2};
`}
${(props) =>
${props.isSelected && `background: ${props.theme.colors.greyScale2};`}}
${
props.theme.variant === 'light' &&
css`
background: ${(props) => props.theme.colors.greyScale3};
`};
}
`background: ${props.theme.colors.greyScale3};`
}
`}
${(props) =>
props.dragNDropActions?.isDraggedOver &&
Expand Down Expand Up @@ -282,7 +285,7 @@ const ListTitle = styled.span`
pointer-events: none;
`

const IconBox = styled.div<Props & State>`
const IconBox = styled.div<Props>`
display: none;
height: 100%;
align-items: center;
Expand All @@ -294,7 +297,7 @@ const IconBox = styled.div<Props & State>`
// List all states in which to display
${(props) =>
(props.alwaysShowRightSideIcon ||
props.isHovering ||
props.isFocused ||
props.dragNDropActions?.isDraggedOver ||
props.dragNDropActions?.wasPageDropped ||
props.isMenuDisplayed) &&
Expand Down
14 changes: 13 additions & 1 deletion src/dashboard-refactor/lists-sidebar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export interface ListsSidebarProps extends ListsSidebarState {
onConfirmAddList: (value: string) => void
setSidebarPeekState: (isPeeking: boolean) => () => void
initDNDActions: (listId: string) => DragNDropActions
setFocusedListId: (listId: UnifiedList['unifiedId'] | null) => void
initContextMenuBtnProps: (
listId: string,
) => Omit<
Expand All @@ -70,7 +71,9 @@ export interface ListsSidebarProps extends ListsSidebarState {
spaceSidebarWidth: string
getRootElement: () => HTMLElement
isInPageMode: boolean
listTreesDeps: Omit<ListTreesDeps, 'children'>
listTreesDeps: Omit<ListTreesDeps, 'children'> & {
ref: React.RefObject<ListTrees>
}
}

export default class ListsSidebar extends PureComponent<ListsSidebarProps> {
Expand Down Expand Up @@ -225,6 +228,15 @@ export default class ListsSidebar extends PureComponent<ListsSidebarProps> {
key={list.unifiedId}
indentSteps={list.pathUnifiedIds.length}
name={`${list.name}`}
isFocused={
this.props.focusedListId ===
list.unifiedId
}
setFocused={(isFocused) =>
this.props.setFocusedListId(
isFocused ? list.unifiedId : null,
)
}
isSelected={
this.props.selectedListId ===
list.unifiedId
Expand Down
3 changes: 3 additions & 0 deletions src/dashboard-refactor/lists-sidebar/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export type RootState = Pick<ListsSidebarSearchBarProps, 'searchQuery'> & {
dragOverListId?: string
editingListId?: string
selectedListId: string | null
focusedListId: UnifiedList['unifiedId'] | null
showMoreMenuListId?: string
editMenuListId?: string
isSidebarToggleHovered?: boolean
Expand All @@ -46,6 +47,8 @@ export type Events = UIEvent<{
setSidebarPeeking: { isPeeking: boolean }
setSidebarToggleHovered: { isHovered: boolean }
setListQueryValue: { query: string }
handleListQueryKeyPress: { key: string }
setFocusedListId: { listId: UnifiedList['unifiedId'] | null }

setAddListInputShown: { isShown: boolean }
cancelListCreate: null
Expand Down
2 changes: 2 additions & 0 deletions src/dashboard-refactor/logic.test.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
} from 'src/content-sharing/background/types'
import { PageAnnotationsCache } from 'src/annotations/cache'
import { RemoteCopyPasterInterface } from 'src/copy-paster/background/types'
import { initNormalizedState } from '@worldbrain/memex-common/lib/common-ui/utils/normalized-state'

type DataSeeder = (
logic: TestLogicContainer<RootState, Events>,
Expand Down Expand Up @@ -209,6 +210,7 @@ export async function setupTest(
device.backgroundModules.bgScript.remoteFunctions,
) as any,
browserAPIs: device.browserAPIs,
getListTreesRef: () => undefined,
})

if (args.overrideSearchTrigger) {
Expand Down
Loading

0 comments on commit fe1c196

Please sign in to comment.