feat(core): add shortcut for multi select docs (#6318)

close TOV-701
In All Doc, `Shift + Click` has been added to activate the multiple selection state.
This commit is contained in:
JimmFly
2024-03-29 01:40:23 +00:00
parent 0ce6401a6f
commit 1a873ecf3c
10 changed files with 63 additions and 15 deletions

View File

@@ -106,6 +106,7 @@ export const titleCell = style({
maxWidth: 'calc(100% - 64px)', maxWidth: 'calc(100% - 64px)',
flex: 1, flex: 1,
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
userSelect: 'none',
}); });
export const titleCellMain = style({ export const titleCellMain = style({
overflow: 'hidden', overflow: 'hidden',

View File

@@ -5,6 +5,7 @@ import type { PropsWithChildren } from 'react';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { selectionStateAtom, useAtom } from '../scoped-atoms';
import type { import type {
CollectionListItemProps, CollectionListItemProps,
DraggableTitleCellData, DraggableTitleCellData,
@@ -172,14 +173,28 @@ function CollectionListItemWrapper({
children, children,
draggable, draggable,
}: collectionListWrapperProps) { }: collectionListWrapperProps) {
const [selectionState, setSelectionActive] = useAtom(selectionStateAtom);
const handleClick = useCallback( const handleClick = useCallback(
(e: React.MouseEvent) => { (e: React.MouseEvent) => {
if (onClick) { if (!selectionState.selectable) {
return;
}
if (e.shiftKey) {
stopPropagation(e); stopPropagation(e);
onClick(); setSelectionActive(true);
onClick?.();
return;
}
if (selectionState.selectionActive) {
return onClick?.();
} }
}, },
[onClick] [
onClick,
selectionState.selectable,
selectionState.selectionActive,
setSelectionActive,
]
); );
const commonProps = useMemo( const commonProps = useMemo(

View File

@@ -106,6 +106,7 @@ export const titleCell = style({
maxWidth: 'calc(100% - 64px)', maxWidth: 'calc(100% - 64px)',
flex: 1, flex: 1,
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
userSelect: 'none',
}); });
export const titleCellMain = style({ export const titleCellMain = style({
overflow: 'hidden', overflow: 'hidden',

View File

@@ -6,6 +6,7 @@ import type { PropsWithChildren } from 'react';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import { WorkbenchLink } from '../../../modules/workbench/view/workbench-link'; import { WorkbenchLink } from '../../../modules/workbench/view/workbench-link';
import { selectionStateAtom, useAtom } from '../scoped-atoms';
import type { DraggableTitleCellData, PageListItemProps } from '../types'; import type { DraggableTitleCellData, PageListItemProps } from '../types';
import { usePageDisplayProperties } from '../use-page-display-properties'; import { usePageDisplayProperties } from '../use-page-display-properties';
import { ColWrapper, formatDate, stopPropagation } from '../utils'; import { ColWrapper, formatDate, stopPropagation } from '../utils';
@@ -237,14 +238,22 @@ function PageListItemWrapper({
children, children,
draggable, draggable,
}: PageListWrapperProps) { }: PageListWrapperProps) {
const [selectionState, setSelectionActive] = useAtom(selectionStateAtom);
const handleClick = useCallback( const handleClick = useCallback(
(e: React.MouseEvent) => { (e: React.MouseEvent) => {
if (onClick) { if (!selectionState.selectable) {
stopPropagation(e); return false;
onClick();
} }
stopPropagation(e);
if (e.shiftKey) {
setSelectionActive(true);
onClick?.();
return true;
}
onClick?.();
return false;
}, },
[onClick] [onClick, selectionState.selectable, setSelectionActive]
); );
const commonProps = useMemo( const commonProps = useMemo(
@@ -255,7 +264,7 @@ function PageListItemWrapper({
className: styles.root, className: styles.root,
'data-clickable': !!onClick || !!to, 'data-clickable': !!onClick || !!to,
'data-dragging': isDragging, 'data-dragging': isDragging,
onClick: handleClick, onClick: onClick ? handleClick : undefined,
}), }),
[pageId, draggable, onClick, to, isDragging, handleClick] [pageId, draggable, onClick, to, isDragging, handleClick]
); );

View File

@@ -25,6 +25,7 @@ export const pageCount = style({
fontSize: cssVar('fontBase'), fontSize: cssVar('fontBase'),
lineHeight: '1.6em', lineHeight: '1.6em',
color: cssVar('textSecondaryColor'), color: cssVar('textSecondaryColor'),
marginRight: '12px',
}); });
export const favouritedIcon = style({ export const favouritedIcon = style({

View File

@@ -67,6 +67,7 @@ const useItemSelectionStateEffect = () => {
return; return;
} }
setSelectionActive(false); setSelectionActive(false);
selectionState.onSelectedIdsChange?.([]);
}; };
const escHandler = (e: KeyboardEvent) => { const escHandler = (e: KeyboardEvent) => {
@@ -75,6 +76,7 @@ const useItemSelectionStateEffect = () => {
} }
if (e.key === 'Escape') { if (e.key === 'Escape') {
setSelectionActive(false); setSelectionActive(false);
selectionState.onSelectedIdsChange?.([]);
} }
}; };
@@ -88,6 +90,7 @@ const useItemSelectionStateEffect = () => {
} }
return; return;
}, [ }, [
selectionState,
selectionState.selectable, selectionState.selectable,
selectionState.selectionActive, selectionState.selectionActive,
setSelectionActive, setSelectionActive,

View File

@@ -329,7 +329,7 @@ function pageMetaToListItemProp(
createDate: new Date(item.createDate), createDate: new Date(item.createDate),
updatedDate: item.updatedDate ? new Date(item.updatedDate) : undefined, updatedDate: item.updatedDate ? new Date(item.updatedDate) : undefined,
to: props.rowAsLink && !props.selectable ? `/${item.id}` : undefined, to: props.rowAsLink && !props.selectable ? `/${item.id}` : undefined,
onClick: props.selectable ? toggleSelection : undefined, onClick: toggleSelection,
icon: ( icon: (
<UnifiedPageIcon <UnifiedPageIcon
id={item.id} id={item.id}
@@ -378,7 +378,7 @@ function collectionMetaToListItemProp(
props.rowAsLink && !props.selectable props.rowAsLink && !props.selectable
? `/collection/${item.id}` ? `/collection/${item.id}`
: undefined, : undefined,
onClick: props.selectable ? toggleSelection : undefined, onClick: toggleSelection,
icon: <ViewLayersIcon />, icon: <ViewLayersIcon />,
operations: props.operationsRenderer?.(item), operations: props.operationsRenderer?.(item),
selectable: props.selectable, selectable: props.selectable,
@@ -412,7 +412,7 @@ function tagMetaToListItemProp(
tagId: item.id, tagId: item.id,
title: item.title, title: item.title,
to: props.rowAsLink && !props.selectable ? `/tag/${item.id}` : undefined, to: props.rowAsLink && !props.selectable ? `/tag/${item.id}` : undefined,
onClick: props.selectable ? toggleSelection : undefined, onClick: toggleSelection,
color: item.color, color: item.color,
pageCount: item.pageCount, pageCount: item.pageCount,
operations: props.operationsRenderer?.(item), operations: props.operationsRenderer?.(item),

View File

@@ -105,6 +105,7 @@ export const titleCell = style({
maxWidth: 'calc(100% - 64px)', maxWidth: 'calc(100% - 64px)',
flex: 1, flex: 1,
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
userSelect: 'none',
}); });
export const titleCellMain = style({ export const titleCellMain = style({
overflow: 'hidden', overflow: 'hidden',

View File

@@ -5,6 +5,7 @@ import type { PropsWithChildren } from 'react';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { selectionStateAtom, useAtom } from '../scoped-atoms';
import type { DraggableTitleCellData, TagListItemProps } from '../types'; import type { DraggableTitleCellData, TagListItemProps } from '../types';
import { ColWrapper, stopPropagation } from '../utils'; import { ColWrapper, stopPropagation } from '../utils';
import * as styles from './tag-list-item.css'; import * as styles from './tag-list-item.css';
@@ -165,14 +166,28 @@ function TagListItemWrapper({
children, children,
draggable, draggable,
}: TagListWrapperProps) { }: TagListWrapperProps) {
const [selectionState, setSelectionActive] = useAtom(selectionStateAtom);
const handleClick = useCallback( const handleClick = useCallback(
(e: React.MouseEvent) => { (e: React.MouseEvent) => {
if (onClick) { if (!selectionState.selectable) {
return;
}
if (e.shiftKey) {
stopPropagation(e); stopPropagation(e);
onClick(); setSelectionActive(true);
onClick?.();
return;
}
if (selectionState.selectionActive) {
return onClick?.();
} }
}, },
[onClick] [
onClick,
selectionState.selectable,
selectionState.selectionActive,
setSelectionActive,
]
); );
const commonProps = useMemo( const commonProps = useMemo(

View File

@@ -20,6 +20,9 @@ export const WorkbenchLink = ({
(event: React.MouseEvent<HTMLAnchorElement>) => { (event: React.MouseEvent<HTMLAnchorElement>) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
if (onClick?.(event)) {
return;
}
if (event.ctrlKey || event.metaKey) { if (event.ctrlKey || event.metaKey) {
if (appSettings.enableMultiView && environment.isDesktop) { if (appSettings.enableMultiView && environment.isDesktop) {
@@ -34,7 +37,6 @@ export const WorkbenchLink = ({
} else { } else {
workbench.open(to); workbench.open(to);
} }
onClick?.(event);
}, },
[appSettings.enableMultiView, basename, onClick, to, workbench] [appSettings.enableMultiView, basename, onClick, to, workbench]
); );