Ensure the double-click event is not ignored in the browser tree.

pull/8374/head
Yogesh Mahajan 2025-01-20 11:21:21 +05:30 committed by GitHub
parent a410b15e30
commit 98f6b1ff12
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 72 additions and 28 deletions

View File

@ -0,0 +1,18 @@
import { useSingleAndDoubleClick } from '../../../custom_hooks';
import * as React from 'react';
import PropTypes from 'prop-types';
import CustomPropTypes from '../../../../js/custom_prop_types';
export default function DoubleClickHandler({onSingleClick, onDoubleClick, children}){
const onClick = useSingleAndDoubleClick(onSingleClick, onDoubleClick) ;
return(
<div onClick={(e)=>onClick(e)}>
{children}
</div>
);
}
DoubleClickHandler.propTypes = {
onSingleClick: PropTypes.func,
onDoubleClick: PropTypes.func,
children: CustomPropTypes.children
};

View File

@ -12,9 +12,9 @@ import * as React from 'react';
import { ClasslistComposite } from 'aspen-decorations';
import { Directory, FileEntry, IItemRendererProps, ItemType, RenamePromptHandle, FileType, FileOrDir} from 'react-aspen';
import {IFileTreeXTriggerEvents, FileTreeXEvent } from '../types';
import _ from 'lodash';
import { Notificar } from 'notificar';
import _ from 'lodash';
import DoubleClickHandler from './DoubleClickHandler';
interface IItemRendererXProps {
/**
* In this implementation, decoration are null when item is `PromptHandle`
@ -58,7 +58,6 @@ export class FileTreeItem extends React.Component<IItemRendererXProps & IItemRen
public render() {
const { item, itemType, decorations } = this.props;
const isRenamePrompt = itemType === ItemType.RenamePrompt;
const isNewPrompt = itemType === ItemType.NewDirectoryPrompt || itemType === ItemType.NewFilePrompt;
const isDirExpanded = itemType === ItemType.Directory
@ -93,7 +92,6 @@ export class FileTreeItem extends React.Component<IItemRendererXProps & IItemRen
data-depth={item.depth}
onContextMenu={this.handleContextMenu}
onClick={this.handleClick}
onDoubleClick={this.handleDoubleClick}
onDragStart={this.handleDragStartItem}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
@ -107,8 +105,8 @@ export class FileTreeItem extends React.Component<IItemRendererXProps & IItemRen
: null
}
<span className='file-label'>
{
<DoubleClickHandler onDoubleClick={this.handleDoubleClick} onSingleClick={this.handleClick} >
<span className='file-label'>{
item._metadata?.data?.icon ?
<i className={cn('file-icon', item._metadata?.data?.icon ? item._metadata.data.icon : fileOrDir)} /> : null
}
@ -121,7 +119,8 @@ export class FileTreeItem extends React.Component<IItemRendererXProps & IItemRen
{tag.text}
</div>
))}
</span>
</span>
</DoubleClickHandler>
</div>);
}

View File

@ -29,6 +29,32 @@ export function useInterval(callback, delay) {
}, [delay]);
}
/* React hook for handling double and single click events */
export function useSingleAndDoubleClick(handleSingleClick, handleDoubleClick, delay = 250) {
const clickCountRef = useRef(0);
const timerRef = useRef(null);
const handleClick = (e) => {
// Handle the logic here, no need to pass the event
clickCountRef.current += 1;
// Clear any previous timeout to ensure the double-click logic is triggered only once
clearTimeout(timerRef.current);
// Set the timeout to handle click logic after the delay
timerRef.current = setTimeout(() => {
if (clickCountRef.current === 1) handleSingleClick(e);
else if (clickCountRef.current === 2) handleDoubleClick(e);
// Reset the click count and props after handling
clickCountRef.current = 0;
}, delay);
};
return handleClick;
}
export function useDelayedCaller(callback) {
let timer;
useEffect(() => {

View File

@ -19,7 +19,7 @@ class TreeAreaLocators:
@staticmethod
def server_group_node_exp_status(server_group_name):
return "//i[@class='directory-toggle open']/following-sibling::" \
"span//span[starts-with(text(),'%s')]" % server_group_name
"div//span[starts-with(text(),'%s')]" % server_group_name
# Server Node
@staticmethod
@ -31,7 +31,7 @@ class TreeAreaLocators:
@staticmethod
def server_node_exp_status(server_name):
return "//i[@class='directory-toggle open']/following-sibling::" \
"span//span[starts-with(text(),'%s')]" % server_name
"div//span[starts-with(text(),'%s')]" % server_name
# Server Connection
@staticmethod
@ -43,36 +43,37 @@ class TreeAreaLocators:
# Databases Node
@staticmethod
def databases_node(server_name):
return "//div[div[span[span[starts-with(text(),'%s')]]]]/" \
return "//div[div[div[span[span[starts-with(text(),'%s')]]]]]/" \
"following-sibling::div//span[text()='Databases']" % server_name
@staticmethod
def databases_node_exp_status(server_name):
return "//div[div[span[span[starts-with(text(),'%s')]]]]/" \
"following-sibling::div//span[span[text()='Databases']]/" \
return "//div[div[div[span[span[starts-with(text(),'%s')]]]]]/" \
"following-sibling::div//div[span[span[text()='Databases']]]/" \
"preceding-sibling::i[@class='directory-toggle open']" \
% server_name
# Database Node
@staticmethod
def database_node(database_name):
return "//div[@data-depth='4']/span/span[text()='%s']" % database_name
return "//div[@data-depth='4']/div/span/span[text()='%s']" \
% database_name
@staticmethod
def database_node_exp_status(database_name):
return "//i[@class='directory-toggle open']/following-sibling::" \
"span//span[text()='%s']" % database_name
"div//span[text()='%s']" % database_name
# Schemas Node
@staticmethod
def schemas_node(database_name):
return "//div[div[span[span[starts-with(text(),'%s')]]]]/" \
return "//div[div[div[span[span[starts-with(text(),'%s')]]]]]/" \
"following-sibling::div//span[text()='Schemas']" % database_name
@staticmethod
def schemas_node_exp_status(database_name):
return "//div[div[span[span[starts-with(text(),'%s')]]]]/" \
"following-sibling::div//span[span[text()='Schemas']]/" \
return "//div[div[div[span[span[starts-with(text(),'%s')]]]]]/" \
"following-sibling::div//div[span[span[text()='Schemas']]]/" \
"preceding-sibling::i[@class='directory-toggle open']" \
% database_name
@ -85,28 +86,28 @@ class TreeAreaLocators:
@staticmethod
def schema_node_exp_status(schema_name):
return "//i[@class='directory-toggle open']/" \
"following-sibling::span//span[text()='%s']" % schema_name
"following-sibling::div//span[text()='%s']" % schema_name
# Tables Node
@staticmethod
def tables_node(schema_name):
return "//div[div[span[span[starts-with(text(),'%s')]]]]/" \
return "//div[divdiv[[span[span[starts-with(text(),'%s')]]]]]/" \
"following-sibling::div//span[text()='Tables']" % schema_name
@staticmethod
def tables_node_exp_status(schema_name):
return "//div[div[span[span[starts-with(text(),'%s')]]]]/" \
"following-sibling::div//span[span[text()='Tables']]/" \
"following-sibling::div//div[span[span[text()='Tables']]]/" \
"preceding-sibling::i[@class='directory-toggle open']"\
% schema_name
# Schema child
child_node_exp_status = \
"//div[div[span[span[starts-with(text(),'%s')]]]]/" \
"following-sibling::div//span[span[text()='%s']]/" \
"//div[div[div[span[span[starts-with(text(),'%s')]]]]]/" \
"following-sibling::div//div[span[span[text()='%s']]]/" \
"preceding-sibling::i[@class='directory-toggle open']"
child_node = "//div[div[span[span[starts-with(text(),'%s')]]]]/" \
child_node = "//div[div[div[span[span[starts-with(text(),'%s')]]]]]/" \
"following-sibling::div//span[text()='%s']"
@staticmethod
@ -120,8 +121,8 @@ class TreeAreaLocators:
@staticmethod
def schema_child_node_expand_icon_xpath(schema_name, child_node_name):
return "//div[div[span[span[starts-with(text(),'%s')]]]]/" \
"following-sibling::div//span[text()='%s']/../" \
return "//div[div[div[span[span[starts-with(text(),'%s')]]]]]/" \
"following-sibling::div//div[span[text()='%s']]/../" \
"preceding-sibling::i" % (schema_name, child_node_name)
# Database child
@ -147,17 +148,17 @@ class TreeAreaLocators:
# Table Node
@staticmethod
def table_node(table_name):
return "//div[@data-depth='8']/span/span[text()='%s']" % table_name
return "//div[@data-depth='8']/div/span/span[text()='%s']" % table_name
# Function Node
@staticmethod
def function_node(table_name):
return "//div[@data-depth='8']/span/span[text()='%s']" % table_name
return "//div[@data-depth='8']/div/span/span[text()='%s']" % table_name
# Role Node
@staticmethod
def role_node(role_name):
return "//div[@data-depth='4']/span/span[text()='%s']" % role_name
return "//div[@data-depth='4']/div/span/span[text()='%s']" % role_name
# Context element option
@staticmethod