mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-10 04:30:25 +02:00
implement double click handler on default controls
This commit is contained in:
@@ -176,6 +176,7 @@ export const ItemCardControls = ({
|
|||||||
icon="ellipsisHorizontal"
|
icon="ellipsisHorizontal"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
controls?.onMore?.({
|
controls?.onMore?.({
|
||||||
event: e,
|
event: e,
|
||||||
internalState,
|
internalState,
|
||||||
@@ -183,6 +184,10 @@ export const ItemCardControls = ({
|
|||||||
itemType,
|
itemType,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
onDoubleClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{controls?.onExpand && (
|
{controls?.onExpand && (
|
||||||
@@ -260,14 +265,25 @@ interface SecondaryButtonProps {
|
|||||||
onClick?: (e: MouseEvent<HTMLButtonElement>) => void;
|
onClick?: (e: MouseEvent<HTMLButtonElement>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SecondaryButton = ({ className, icon, onClick }: SecondaryButtonProps) => {
|
const SecondaryButton = ({
|
||||||
|
className,
|
||||||
|
icon,
|
||||||
|
onClick,
|
||||||
|
onDoubleClick,
|
||||||
|
}: SecondaryButtonProps & { onDoubleClick?: (e: MouseEvent<HTMLButtonElement>) => void }) => {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className={clsx(styles.secondaryButton, className)}
|
className={clsx(styles.secondaryButton, className)}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
onClick?.(e);
|
onClick?.(e);
|
||||||
}}
|
}}
|
||||||
|
onDoubleClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
onDoubleClick?.(e);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Icon icon={icon} size="lg" />
|
<Icon icon={icon} size="lg" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { Image } from '/@/shared/components/image/image';
|
|||||||
import { Separator } from '/@/shared/components/separator/separator';
|
import { Separator } from '/@/shared/components/separator/separator';
|
||||||
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
|
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
|
||||||
import { Text } from '/@/shared/components/text/text';
|
import { Text } from '/@/shared/components/text/text';
|
||||||
|
import { useDoubleClick } from '/@/shared/hooks/use-double-click';
|
||||||
import {
|
import {
|
||||||
Album,
|
Album,
|
||||||
AlbumArtist,
|
AlbumArtist,
|
||||||
@@ -138,22 +139,50 @@ const CompactItemCard = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
const handleClick = useDoubleClick({
|
||||||
|
onSingleClick: (e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
if (!data || !controls || !internalState) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't trigger selection if clicking on interactive elements
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
const isInteractiveElement = target.closest(
|
||||||
|
'button, a, input, select, textarea, [role="button"]',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isInteractiveElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
controls.onClick?.({
|
||||||
|
event: e,
|
||||||
|
internalState,
|
||||||
|
item: data as any,
|
||||||
|
itemType,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onDoubleClick: (e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
if (!data || !controls || !internalState) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
controls.onDoubleClick?.({
|
||||||
|
event: e,
|
||||||
|
internalState,
|
||||||
|
item: data as any,
|
||||||
|
itemType,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleContextMenu = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
if (!data || !controls || !internalState) {
|
if (!data || !controls || !internalState) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't trigger selection if clicking on interactive elements
|
e.preventDefault();
|
||||||
const target = e.target as HTMLElement;
|
controls.onMore?.({
|
||||||
const isInteractiveElement = target.closest(
|
|
||||||
'button, a, input, select, textarea, [role="button"]',
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isInteractiveElement) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
controls.onClick?.({
|
|
||||||
event: e,
|
event: e,
|
||||||
internalState,
|
internalState,
|
||||||
item: data as any,
|
item: data as any,
|
||||||
@@ -170,6 +199,7 @@ const CompactItemCard = ({
|
|||||||
<div
|
<div
|
||||||
className={clsx(styles.imageContainer, { [styles.isRound]: isRound })}
|
className={clsx(styles.imageContainer, { [styles.isRound]: isRound })}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
|
onContextMenu={handleContextMenu}
|
||||||
onMouseEnter={handleMouseEnter}
|
onMouseEnter={handleMouseEnter}
|
||||||
onMouseLeave={handleMouseLeave}
|
onMouseLeave={handleMouseLeave}
|
||||||
>
|
>
|
||||||
@@ -242,22 +272,50 @@ const DefaultItemCard = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
const handleClick = useDoubleClick({
|
||||||
|
onSingleClick: (e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
if (!data || !controls || !internalState) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't trigger selection if clicking on interactive elements
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
const isInteractiveElement = target.closest(
|
||||||
|
'button, a, input, select, textarea, [role="button"]',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isInteractiveElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
controls.onClick?.({
|
||||||
|
event: e,
|
||||||
|
internalState,
|
||||||
|
item: data as any,
|
||||||
|
itemType,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onDoubleClick: (e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
if (!data || !controls || !internalState) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
controls.onDoubleClick?.({
|
||||||
|
event: e,
|
||||||
|
internalState,
|
||||||
|
item: data as any,
|
||||||
|
itemType,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleContextMenu = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
if (!data || !controls || !internalState) {
|
if (!data || !controls || !internalState) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't trigger selection if clicking on interactive elements
|
e.preventDefault();
|
||||||
const target = e.target as HTMLElement;
|
controls.onMore?.({
|
||||||
const isInteractiveElement = target.closest(
|
|
||||||
'button, a, input, select, textarea, [role="button"]',
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isInteractiveElement) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
controls.onClick?.({
|
|
||||||
event: e,
|
event: e,
|
||||||
internalState,
|
internalState,
|
||||||
item: data as any,
|
item: data as any,
|
||||||
@@ -274,6 +332,7 @@ const DefaultItemCard = ({
|
|||||||
<div
|
<div
|
||||||
className={clsx(styles.imageContainer, { [styles.isRound]: isRound })}
|
className={clsx(styles.imageContainer, { [styles.isRound]: isRound })}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
|
onContextMenu={handleContextMenu}
|
||||||
onMouseEnter={handleMouseEnter}
|
onMouseEnter={handleMouseEnter}
|
||||||
onMouseLeave={handleMouseLeave}
|
onMouseLeave={handleMouseLeave}
|
||||||
>
|
>
|
||||||
@@ -390,22 +449,50 @@ const PosterItemCard = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
const handleClick = useDoubleClick({
|
||||||
|
onSingleClick: (e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
if (!data || !controls || !internalState) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't trigger selection if clicking on interactive elements
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
const isInteractiveElement = target.closest(
|
||||||
|
'button, a, input, select, textarea, [role="button"]',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isInteractiveElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
controls.onClick?.({
|
||||||
|
event: e,
|
||||||
|
internalState,
|
||||||
|
item: data as any,
|
||||||
|
itemType,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onDoubleClick: (e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
if (!data || !controls || !internalState) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
controls.onDoubleClick?.({
|
||||||
|
event: e,
|
||||||
|
internalState,
|
||||||
|
item: data as any,
|
||||||
|
itemType,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleContextMenu = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
if (!data || !controls || !internalState) {
|
if (!data || !controls || !internalState) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't trigger selection if clicking on interactive elements
|
e.preventDefault();
|
||||||
const target = e.target as HTMLElement;
|
controls.onMore?.({
|
||||||
const isInteractiveElement = target.closest(
|
|
||||||
'button, a, input, select, textarea, [role="button"]',
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isInteractiveElement) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
controls.onClick?.({
|
|
||||||
event: e,
|
event: e,
|
||||||
internalState,
|
internalState,
|
||||||
item: data as any,
|
item: data as any,
|
||||||
@@ -424,6 +511,7 @@ const PosterItemCard = ({
|
|||||||
<div
|
<div
|
||||||
className={clsx(styles.imageContainer, { [styles.isRound]: isRound })}
|
className={clsx(styles.imageContainer, { [styles.isRound]: isRound })}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
|
onContextMenu={handleContextMenu}
|
||||||
onMouseEnter={handleMouseEnter}
|
onMouseEnter={handleMouseEnter}
|
||||||
onMouseLeave={handleMouseLeave}
|
onMouseLeave={handleMouseLeave}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -2,11 +2,30 @@ import {
|
|||||||
ItemTableListInnerColumn,
|
ItemTableListInnerColumn,
|
||||||
TableColumnContainer,
|
TableColumnContainer,
|
||||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||||
|
import { ItemListItem } from '/@/renderer/components/item-list/types';
|
||||||
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
||||||
|
|
||||||
export const ActionsColumn = (props: ItemTableListInnerColumn) => {
|
export const ActionsColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: any = (props.data as (any | undefined)[])[props.rowIndex];
|
const row: any = (props.data as (any | undefined)[])[props.rowIndex];
|
||||||
|
|
||||||
|
const handleActionClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
if (row !== undefined) {
|
||||||
|
props.controls.onMore?.({
|
||||||
|
event,
|
||||||
|
internalState: props.internalState,
|
||||||
|
item: row as ItemListItem,
|
||||||
|
itemType: props.itemType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleActionDoubleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
if (row !== undefined) {
|
if (row !== undefined) {
|
||||||
return (
|
return (
|
||||||
<TableColumnContainer {...props}>
|
<TableColumnContainer {...props}>
|
||||||
@@ -17,6 +36,8 @@ export const ActionsColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
color: 'muted',
|
color: 'muted',
|
||||||
size: 'md',
|
size: 'md',
|
||||||
}}
|
}}
|
||||||
|
onDoubleClick={handleActionDoubleClick}
|
||||||
|
onClick={handleActionClick}
|
||||||
size="xs"
|
size="xs"
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ export const FavoriteColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
size: 'md',
|
size: 'md',
|
||||||
}}
|
}}
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
props.controls.onFavorite?.({
|
props.controls.onFavorite?.({
|
||||||
event,
|
event,
|
||||||
favorite: !row,
|
favorite: !row,
|
||||||
@@ -30,6 +32,10 @@ export const FavoriteColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
itemType: props.itemType,
|
itemType: props.itemType,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
onDoubleClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
}}
|
||||||
size="xs"
|
size="xs"
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import { Flex } from '/@/shared/components/flex/flex';
|
|||||||
import { Icon } from '/@/shared/components/icon/icon';
|
import { Icon } from '/@/shared/components/icon/icon';
|
||||||
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
|
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
|
||||||
import { Text } from '/@/shared/components/text/text';
|
import { Text } from '/@/shared/components/text/text';
|
||||||
|
import { useDoubleClick } from '/@/shared/hooks/use-double-click';
|
||||||
import { LibraryItem, QueueSong, Song } from '/@/shared/types/domain-types';
|
import { LibraryItem, QueueSong, Song } from '/@/shared/types/domain-types';
|
||||||
import { DragOperation, DragTarget, DragTargetMap } from '/@/shared/types/drag-and-drop';
|
import { DragOperation, DragTarget, DragTargetMap } from '/@/shared/types/drag-and-drop';
|
||||||
import { TableColumn } from '/@/shared/types/types';
|
import { TableColumn } from '/@/shared/types/types';
|
||||||
@@ -426,19 +427,43 @@ export const TableColumnTextContainer = (
|
|||||||
}
|
}
|
||||||
}, [isDataRow, props.rowIndex, props.isDraggedOver, props.tableId]);
|
}, [isDataRow, props.rowIndex, props.isDraggedOver, props.tableId]);
|
||||||
|
|
||||||
const handleCellClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
const handleClick = useDoubleClick({
|
||||||
// Don't trigger row selection if clicking on interactive elements
|
onDoubleClick: (event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
const target = event.target as HTMLElement;
|
if (isDataRow && item) {
|
||||||
const isInteractiveElement = target.closest(
|
props.controls.onDoubleClick?.({
|
||||||
'button, a, input, select, textarea, [role="button"]',
|
event,
|
||||||
);
|
internalState: props.internalState,
|
||||||
|
item: item as ItemListItem,
|
||||||
|
itemType: props.itemType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSingleClick: (event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
// Don't trigger row selection if clicking on interactive elements
|
||||||
|
const target = event.target as HTMLElement;
|
||||||
|
const isInteractiveElement = target.closest(
|
||||||
|
'button, a, input, select, textarea, [role="button"]',
|
||||||
|
);
|
||||||
|
|
||||||
if (isInteractiveElement) {
|
if (isInteractiveElement) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDataRow && item && props.enableSelection) {
|
if (isDataRow && item && props.enableSelection) {
|
||||||
props.controls.onClick?.({
|
props.controls.onClick?.({
|
||||||
|
event,
|
||||||
|
internalState: props.internalState,
|
||||||
|
item: item as ItemListItem,
|
||||||
|
itemType: props.itemType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleContextMenu = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
if (isDataRow && item) {
|
||||||
|
event.preventDefault();
|
||||||
|
props.controls.onMore?.({
|
||||||
event,
|
event,
|
||||||
internalState: props.internalState,
|
internalState: props.internalState,
|
||||||
item: item as ItemListItem,
|
item: item as ItemListItem,
|
||||||
@@ -478,7 +503,8 @@ export const TableColumnTextContainer = (
|
|||||||
[styles.withVerticalBorder]: props.enableVerticalBorders && !isLastColumn,
|
[styles.withVerticalBorder]: props.enableVerticalBorders && !isLastColumn,
|
||||||
})}
|
})}
|
||||||
data-row-index={isDataRow ? `${props.tableId}-${props.rowIndex}` : undefined}
|
data-row-index={isDataRow ? `${props.tableId}-${props.rowIndex}` : undefined}
|
||||||
onClick={handleCellClick}
|
onClick={handleClick}
|
||||||
|
onContextMenu={handleContextMenu}
|
||||||
ref={mergedRef}
|
ref={mergedRef}
|
||||||
style={props.style}
|
style={props.style}
|
||||||
>
|
>
|
||||||
@@ -604,19 +630,43 @@ export const TableColumnContainer = (
|
|||||||
}
|
}
|
||||||
}, [isDataRow, props.rowIndex, props.isDraggedOver, props.tableId]);
|
}, [isDataRow, props.rowIndex, props.isDraggedOver, props.tableId]);
|
||||||
|
|
||||||
const handleCellClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
const handleClick = useDoubleClick({
|
||||||
// Don't trigger row selection if clicking on interactive elements
|
onDoubleClick: (event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
const target = event.target as HTMLElement;
|
if (isDataRow && item) {
|
||||||
const isInteractiveElement = target.closest(
|
props.controls.onDoubleClick?.({
|
||||||
'button, a, input, select, textarea, [role="button"]',
|
event,
|
||||||
);
|
internalState: props.internalState,
|
||||||
|
item: item as ItemListItem,
|
||||||
|
itemType: props.itemType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSingleClick: (event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
// Don't trigger row selection if clicking on interactive elements
|
||||||
|
const target = event.target as HTMLElement;
|
||||||
|
const isInteractiveElement = target.closest(
|
||||||
|
'button, a, input, select, textarea, [role="button"]',
|
||||||
|
);
|
||||||
|
|
||||||
if (isInteractiveElement) {
|
if (isInteractiveElement) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDataRow && item && props.enableSelection) {
|
if (isDataRow && item && props.enableSelection) {
|
||||||
props.controls.onClick?.({
|
props.controls.onClick?.({
|
||||||
|
event,
|
||||||
|
internalState: props.internalState,
|
||||||
|
item: item as ItemListItem,
|
||||||
|
itemType: props.itemType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleContextMenu = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
if (isDataRow && item) {
|
||||||
|
event.preventDefault();
|
||||||
|
props.controls.onMore?.({
|
||||||
event,
|
event,
|
||||||
internalState: props.internalState,
|
internalState: props.internalState,
|
||||||
item: item as ItemListItem,
|
item: item as ItemListItem,
|
||||||
@@ -656,7 +706,8 @@ export const TableColumnContainer = (
|
|||||||
[styles.withVerticalBorder]: props.enableVerticalBorders && !isLastColumn,
|
[styles.withVerticalBorder]: props.enableVerticalBorders && !isLastColumn,
|
||||||
})}
|
})}
|
||||||
data-row-index={isDataRow ? `${props.tableId}-${props.rowIndex}` : undefined}
|
data-row-index={isDataRow ? `${props.tableId}-${props.rowIndex}` : undefined}
|
||||||
onClick={handleCellClick}
|
onClick={handleClick}
|
||||||
|
onContextMenu={handleContextMenu}
|
||||||
ref={mergedRef}
|
ref={mergedRef}
|
||||||
style={{ ...props.containerStyle, ...props.style }}
|
style={{ ...props.containerStyle, ...props.style }}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { useCallback, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
export const useDoubleClick = ({
|
||||||
|
latency = 160,
|
||||||
|
onDoubleClick = () => null,
|
||||||
|
onSingleClick = () => null,
|
||||||
|
}: {
|
||||||
|
latency?: number;
|
||||||
|
onDoubleClick?: (e: any) => void;
|
||||||
|
onSingleClick?: (e: any) => void;
|
||||||
|
}) => {
|
||||||
|
const clickCountRef = useRef(0);
|
||||||
|
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
|
const handleClick = useCallback(
|
||||||
|
(e: any) => {
|
||||||
|
clickCountRef.current += 1;
|
||||||
|
|
||||||
|
// Clear any existing timeout
|
||||||
|
if (timeoutRef.current) {
|
||||||
|
clearTimeout(timeoutRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a new timeout to determine if it's a single or double click
|
||||||
|
timeoutRef.current = setTimeout(() => {
|
||||||
|
if (clickCountRef.current === 1) {
|
||||||
|
onSingleClick(e);
|
||||||
|
} else if (clickCountRef.current === 2) {
|
||||||
|
onDoubleClick(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
clickCountRef.current = 0;
|
||||||
|
}, latency);
|
||||||
|
},
|
||||||
|
[latency, onDoubleClick, onSingleClick],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Cleanup timeout on unmount
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (timeoutRef.current) {
|
||||||
|
clearTimeout(timeoutRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return handleClick;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user