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

View File

@ -29,6 +29,32 @@ export function useInterval(callback, delay) {
}, [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) { export function useDelayedCaller(callback) {
let timer; let timer;
useEffect(() => { useEffect(() => {

View File

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