Ensure the double-click event is not ignored in the browser tree.
parent
a410b15e30
commit
98f6b1ff12
|
@ -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
|
||||||
|
};
|
|
@ -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>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(() => {
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue