diff --git a/client/packages/lowcoder/src/comps/comps/avatar.tsx b/client/packages/lowcoder/src/comps/comps/avatar.tsx index bbd39f73e8..94e24d59a4 100644 --- a/client/packages/lowcoder/src/comps/comps/avatar.tsx +++ b/client/packages/lowcoder/src/comps/comps/avatar.tsx @@ -25,6 +25,7 @@ import { IconControl } from "comps/controls/iconControl"; import { clickEvent, eventHandlerControl, + doubleClickEvent, } from "../controls/eventHandlerControl"; import { Avatar, AvatarProps, Badge, Dropdown, Menu } from "antd"; import { LeftRightControl, dropdownControl } from "../controls/dropdownControl"; @@ -34,6 +35,8 @@ import { BadgeBasicSection, badgeChildren } from "./badgeComp/badgeConstants"; import { DropdownOptionControl } from "../controls/optionsControl"; import { ReactElement, useContext, useEffect } from "react"; import { CompNameContext, EditorContext } from "../editorState"; +import { useCompClickEventHandler } from "@lowcoder-ee/comps/utils/useCompClickEventHandler"; + const AvatarWrapper = styled(Avatar) ` background: ${(props) => props.$style.background}; @@ -106,7 +109,7 @@ padding: ${props=>props.$style.padding}; background: ${props=>props.$style.background}; text-decoration: ${props => props.$style.textDecoration}; ` -const EventOptions = [clickEvent] as const; +const EventOptions = [clickEvent, doubleClickEvent] as const; const sharpOptions = [ { label: trans("avatarComp.square"), value: "square" }, { label: trans("avatarComp.circle"), value: "circle" }, @@ -140,6 +143,8 @@ const childrenMap = { const AvatarView = (props: RecordConstructorToView) => { const { shape, title, src, iconSize } = props; const comp = useContext(EditorContext).getUICompByName(useContext(CompNameContext)); + const handleClickEvent = useCompClickEventHandler({onEvent: props.onEvent}) + // const eventsCount = comp ? Object.keys(comp?.children.comp.children.onEvent.children).length : 0; const hasIcon = props.options.findIndex((option) => (option.prefixIcon as ReactElement)?.props.value) > -1; const items = props.options @@ -181,8 +186,7 @@ const AvatarView = (props: RecordConstructorToView) => { shape={shape} $style={props.avatarStyle} src={src.value} - // $cursorPointer={eventsCount > 0} - onClick={() => props.onEvent("click")} + onClick={handleClickEvent} > {title.value} diff --git a/client/packages/lowcoder/src/comps/comps/avatarGroup.tsx b/client/packages/lowcoder/src/comps/comps/avatarGroup.tsx index 4cc2567c64..f370a4ef99 100644 --- a/client/packages/lowcoder/src/comps/comps/avatarGroup.tsx +++ b/client/packages/lowcoder/src/comps/comps/avatarGroup.tsx @@ -8,7 +8,7 @@ import { hiddenPropertyView } from "comps/utils/propertyUtils"; import { trans } from "i18n"; import { NumberControl, StringControl } from "comps/controls/codeControl"; import { Avatar, Tooltip } from "antd"; -import { clickEvent, eventHandlerControl, refreshEvent } from "../controls/eventHandlerControl"; +import { clickEvent, doubleClickEvent, eventHandlerControl, refreshEvent } from "../controls/eventHandlerControl"; import styled from "styled-components"; import { useContext, ReactElement, useEffect } from "react"; import { MultiCompBuilder, stateComp, withDefault } from "../generators"; @@ -19,6 +19,7 @@ import { optionsControl } from "../controls/optionsControl"; import { BoolControl } from "../controls/boolControl"; import { dropdownControl } from "../controls/dropdownControl"; import { JSONObject } from "util/jsonTypes"; +import { useCompClickEventHandler } from "../utils/useCompClickEventHandler"; const MacaroneList = [ '#fde68a', @@ -77,7 +78,7 @@ const DropdownOption = new MultiCompBuilder( )) .build(); -const EventOptions = [clickEvent, refreshEvent] as const; +const EventOptions = [clickEvent, refreshEvent, doubleClickEvent] as const; export const alignOptions = [ { label: , value: "flex-start" }, @@ -105,6 +106,8 @@ const childrenMap = { }; const AvatarGroupView = (props: RecordConstructorToView & { dispatch: (action: CompAction) => void; }) => { + const handleClickEvent = useCompClickEventHandler({onEvent: props.onEvent}) + return ( & { }} size={props.avatarSize} onClick={() => { - props.onEvent("click") + handleClickEvent(); props.dispatch(changeChildAction("currentAvatar", item as JSONObject, false)); }} > diff --git a/client/packages/lowcoder/src/comps/comps/buttonComp/buttonComp.tsx b/client/packages/lowcoder/src/comps/comps/buttonComp/buttonComp.tsx index 6f657c1e84..70a8de5d83 100644 --- a/client/packages/lowcoder/src/comps/comps/buttonComp/buttonComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/buttonComp/buttonComp.tsx @@ -29,6 +29,7 @@ import { AnimationStyle } from "@lowcoder-ee/comps/controls/styleControlConstant import { styleControl } from "@lowcoder-ee/comps/controls/styleControl"; import { RecordConstructorToComp } from "lowcoder-core"; import { ToViewReturn } from "@lowcoder-ee/comps/generators/multi"; +import { useCompClickEventHandler } from "@lowcoder-ee/comps/utils/useCompClickEventHandler"; const FormLabel = styled(CommonBlueLabel)` font-size: 13px; @@ -181,6 +182,7 @@ const ButtonPropertyView = React.memo((props: { const ButtonView = React.memo((props: ToViewReturn) => { const editorState = useContext(EditorContext); const mountedRef = useRef(true); + const handleClickEvent = useCompClickEventHandler({onEvent: props.onEvent}); useEffect(() => { return () => { @@ -193,7 +195,7 @@ const ButtonView = React.memo((props: ToViewReturn) => { try { if (isDefault(props.type)) { - props.onEvent("click"); + handleClickEvent(); } else { submitForm(editorState, props.form); } diff --git a/client/packages/lowcoder/src/comps/comps/buttonComp/floatButtonComp.tsx b/client/packages/lowcoder/src/comps/comps/buttonComp/floatButtonComp.tsx index 223650ef48..358a1e6ff2 100644 --- a/client/packages/lowcoder/src/comps/comps/buttonComp/floatButtonComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/buttonComp/floatButtonComp.tsx @@ -1,3 +1,4 @@ +import React from "react"; import { RecordConstructorToView } from "lowcoder-core"; import { BoolControl } from "comps/controls/boolControl"; import { stringExposingStateControl } from "comps/controls/codeStateControl"; @@ -16,7 +17,7 @@ import { IconControl } from "comps/controls/iconControl"; import styled from "styled-components"; import { ButtonEventHandlerControl } from "comps/controls/eventHandlerControl"; import { manualOptionsControl } from "comps/controls/optionsControl"; -import { useContext, useEffect } from "react"; +import { useCompClickEventHandler } from "@lowcoder-ee/comps/utils/useCompClickEventHandler"; const StyledFloatButton = styled(FloatButton)<{ $animationStyle: AnimationStyleType; @@ -98,21 +99,51 @@ const childrenMap = { dot: BoolControl, }; +const FloatButtonItem = React.memo(({ + button, + animationStyle, + badgeStyle, + buttonTheme, + shape, + dot +}: { + button: any; + animationStyle: AnimationStyleType; + badgeStyle: BadgeStyleType; + buttonTheme: 'primary' | 'default'; + shape: 'circle' | 'square'; + dot: boolean; +}) => { + const handleClickEvent = useCompClickEventHandler({ onEvent: button.onEvent }); + + return ( + + ); +}); + const FloatButtonView = (props: RecordConstructorToView) => { const renderButton = (button: any, onlyOne?: boolean) => { return !button?.hidden ? ( - button.onEvent("click")} - tooltip={button?.label} - description={button?.description} - badge={{ count: button?.badge, color: props.badgeStyle.badgeColor, dot: props?.dot }} - type={onlyOne ? props.buttonTheme : 'default'} + button={button} + animationStyle={props.animationStyle} + badgeStyle={props.badgeStyle} + buttonTheme={onlyOne ? props.buttonTheme : 'default'} shape={props.shape} - />) - : '' + dot={props.dot} + /> + ) : ''; } return ( diff --git a/client/packages/lowcoder/src/comps/comps/commentComp/commentComp.tsx b/client/packages/lowcoder/src/comps/comps/commentComp/commentComp.tsx index 4fb21b69f5..f3b14959c9 100644 --- a/client/packages/lowcoder/src/comps/comps/commentComp/commentComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/commentComp/commentComp.tsx @@ -25,10 +25,11 @@ import { eventHandlerControl, deleteEvent, mentionEvent, -} from "comps/controls/eventHandlerControl"; - + doubleClickEvent, +} from "comps/controls/eventHandlerControl"; import { EditorContext } from "comps/editorState"; + // Introducing styles import { AnimationStyle, @@ -66,6 +67,7 @@ import dayjs from "dayjs"; // import "dayjs/locale/zh-cn"; import { getInitialsAndColorCode } from "util/stringUtils"; import { default as CloseOutlined } from "@ant-design/icons/CloseOutlined"; +import { useCompClickEventHandler } from "@lowcoder-ee/comps/utils/useCompClickEventHandler"; dayjs.extend(relativeTime); // dayjs.locale("zh-cn"); @@ -80,6 +82,7 @@ dayjs.extend(relativeTime); const EventOptions = [ clickEvent, + doubleClickEvent, submitEvent, deleteEvent, mentionEvent, @@ -133,6 +136,8 @@ const CommentCompBase = ( const [commentListData, setCommentListData] = useState([]); const [prefix, setPrefix] = useState("@"); const [context, setContext] = useState(""); + const handleClickEvent = useCompClickEventHandler({onEvent: props.onEvent}) + // Integrate the comment list with the names in the original mention list const mergeAllMentionList = (mentionList: any) => { setMentionList( @@ -174,7 +179,7 @@ const CommentCompBase = ( const generateCommentAvatar = (item: commentDataTYPE) => { return ( props.onEvent("click")} + onClick={handleClickEvent} // If there is an avatar, no background colour is set, and if displayName is not null, displayName is called using getInitialsAndColorCode style={{ backgroundColor: item?.user?.avatar @@ -290,7 +295,9 @@ const CommentCompBase = ( props.onEvent("click")}> +
{item?.user?.name} { props.container.showHeader = false; - // 注入容器参数 props.container.style = Object.assign(props.container.style, { CONTAINER_BODY_PADDING: props.style.containerBodyPadding, border: '#00000000', @@ -205,6 +205,12 @@ export const ContainerBaseComp = (function () { const conRef = useRef(null); const [width, setWidth] = useState(0); const [height, setHeight] = useState(0); + const handleClickEvent = useCompClickEventHandler({onEvent: props.onEvent}) + const actionHandlers = props.actionOptions.map(item => ({ + ...item, + clickHandler: useCompClickEventHandler({onEvent: item.onEvent}) + })); + useEffect(() => { if (height && width) { onResize(); @@ -233,7 +239,7 @@ export const ContainerBaseComp = (function () { $cardType={props.cardType} onMouseEnter={() => props.onEvent('focus')} onMouseLeave={() => props.onEvent('blur')} - onClick={() => props.onEvent('click')} + onClick={handleClickEvent} > } actions={props.cardType == 'common' && props.showActionIcon ? - props.actionOptions.filter(item => !item.hidden).map(item => { + actionHandlers.filter(item => !item.hidden).map(item => { return ( item.onEvent('click')} + onClick={(e) => { + e.stopPropagation() + item.clickHandler() + }} disabled={item.disabled} $style={props.style} > diff --git a/client/packages/lowcoder/src/comps/comps/iconComp.tsx b/client/packages/lowcoder/src/comps/comps/iconComp.tsx index 4ae9dcdd98..8cc3716e16 100644 --- a/client/packages/lowcoder/src/comps/comps/iconComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/iconComp.tsx @@ -27,11 +27,13 @@ import { AutoHeightControl } from "../controls/autoHeightControl"; import { clickEvent, eventHandlerControl, + doubleClickEvent, } from "../controls/eventHandlerControl"; import { useContext } from "react"; import { EditorContext } from "comps/editorState"; import { AssetType, IconscoutControl } from "@lowcoder-ee/comps/controls/iconscoutControl"; import { dropdownControl } from "../controls/dropdownControl"; +import { useCompClickEventHandler } from "../utils/useCompClickEventHandler"; const Container = styled.div<{ $sourceMode: string; @@ -72,7 +74,7 @@ const Container = styled.div<{ `} `; -const EventOptions = [clickEvent] as const; +const EventOptions = [clickEvent, doubleClickEvent] as const; const ModeOptions = [ { label: "Standard", value: "standard" }, @@ -94,6 +96,7 @@ const IconView = (props: RecordConstructorToView) => { const conRef = useRef(null); const [width, setWidth] = useState(0); const [height, setHeight] = useState(0); + const handleClickEvent = useCompClickEventHandler({onEvent: props.onEvent}) useEffect(() => { if (height && width) { @@ -134,7 +137,7 @@ const IconView = (props: RecordConstructorToView) => { $sourceMode={props.sourceMode} $animationStyle={props.animationStyle} style={style} - onClick={() => props.onEvent("click")} + onClick={handleClickEvent} > { props.sourceMode === 'standard' ? (props.icon || '') diff --git a/client/packages/lowcoder/src/comps/comps/imageComp.tsx b/client/packages/lowcoder/src/comps/comps/imageComp.tsx index ec4190bc6e..8bc246a2b1 100644 --- a/client/packages/lowcoder/src/comps/comps/imageComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/imageComp.tsx @@ -3,6 +3,7 @@ import { Section, sectionNames } from "lowcoder-design"; import { clickEvent, eventHandlerControl, + doubleClickEvent, } from "../controls/eventHandlerControl"; import { StringStateControl } from "../controls/codeStateControl"; import { UICompBuilder, withDefault } from "../generators"; @@ -37,6 +38,7 @@ import { StringControl } from "../controls/codeControl"; import { PositionControl } from "comps/controls/dropdownControl"; import { dropdownControl } from "../controls/dropdownControl"; import { AssetType, IconscoutControl } from "../controls/iconscoutControl"; +import { useCompClickEventHandler } from "../utils/useCompClickEventHandler"; const Container = styled.div<{ $style: ImageStyleType | undefined, @@ -112,7 +114,7 @@ const getStyle = (style: ImageStyleType) => { `; }; -const EventOptions = [clickEvent] as const; +const EventOptions = [clickEvent, doubleClickEvent] as const; const ModeOptions = [ { label: "URL", value: "standard" }, { label: "Asset Library", value: "asset-library" }, @@ -123,6 +125,8 @@ const ContainerImg = (props: RecordConstructorToView) => { const conRef = useRef(null); const [width, setWidth] = useState(0); const [height, setHeight] = useState(0); + const handleClickEvent = useCompClickEventHandler({onEvent: props.onEvent}) + const imgOnload = (img: HTMLImageElement) => { img.onload = function () { @@ -211,7 +215,7 @@ const ContainerImg = (props: RecordConstructorToView) => { draggable={false} preview={props.supportPreview ? {src: props.previewSrc || props.src.value } : false} fallback={DEFAULT_IMG_URL} - onClick={() => props.onEvent("click")} + onClick={handleClickEvent} />
diff --git a/client/packages/lowcoder/src/comps/comps/meetingComp/controlButton.tsx b/client/packages/lowcoder/src/comps/comps/meetingComp/controlButton.tsx index 313358815a..0445c94039 100644 --- a/client/packages/lowcoder/src/comps/comps/meetingComp/controlButton.tsx +++ b/client/packages/lowcoder/src/comps/comps/meetingComp/controlButton.tsx @@ -41,6 +41,7 @@ import { useResizeDetector } from "react-resize-detector"; import { useContext } from "react"; import { Tooltip } from "antd"; import { AssetType, IconscoutControl } from "@lowcoder-ee/comps/controls/iconscoutControl"; +import { useCompClickEventHandler } from "@lowcoder-ee/comps/utils/useCompClickEventHandler"; const Container = styled.div<{ $style: any }>` height: 100%; @@ -212,6 +213,9 @@ let ButtonTmpComp = (function () { const imgRef = useRef(null); const conRef = useRef(null); + + const handleClickEvent = useCompClickEventHandler({onEvent: props.onEvent}) + useEffect(() => { if (height && width) { onResize(); @@ -285,7 +289,7 @@ let ButtonTmpComp = (function () { } onClick={() => isDefault(props.type) - ? props.onEvent("click") + ? handleClickEvent() : submitForm(editorState, props.form) } > diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/ColumnNumberComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/ColumnNumberComp.tsx index 78bba93807..619b42674f 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/ColumnNumberComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/ColumnNumberComp.tsx @@ -9,7 +9,8 @@ import { withDefault } from "comps/generators"; import styled from "styled-components"; import { IconControl } from "comps/controls/iconControl"; import { hasIcon } from "comps/utils"; -import { clickEvent, eventHandlerControl } from "comps/controls/eventHandlerControl"; +import { clickEvent, eventHandlerControl, doubleClickEvent } from "comps/controls/eventHandlerControl"; +import { useCompClickEventHandler } from "@lowcoder-ee/comps/utils/useCompClickEventHandler"; const InputNumberWrapper = styled.div` .ant-input-number { @@ -33,7 +34,7 @@ const NumberViewWrapper = styled.div` gap: 4px; `; -const NumberEventOptions = [clickEvent] as const; +const NumberEventOptions = [clickEvent, doubleClickEvent] as const; const childrenMap = { text: NumberControl, @@ -70,6 +71,8 @@ type NumberEditProps = { }; const ColumnNumberView = React.memo((props: NumberViewProps) => { + const handleClickEvent = useCompClickEventHandler({onEvent: props.onEvent ?? (() => {})}) + const formattedValue = useMemo(() => { let result = !props.float ? Math.floor(props.value) : props.value; if (props.float) { @@ -79,9 +82,7 @@ const ColumnNumberView = React.memo((props: NumberViewProps) => { }, [props.value, props.float, props.precision]); const handleClick = useCallback(() => { - if (props.onEvent) { - props.onEvent("click"); - } + handleClickEvent() }, [props.onEvent]); return ( diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnAvatarsComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnAvatarsComp.tsx index a62704ff6d..f02ee19943 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnAvatarsComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnAvatarsComp.tsx @@ -9,7 +9,7 @@ import { avatarGroupStyle, AvatarGroupStyleType } from "comps/controls/styleCont import { AlignCenter, AlignLeft, AlignRight } from "lowcoder-design"; import { NumberControl } from "comps/controls/codeControl"; import { Avatar, Tooltip } from "antd"; -import { clickEvent, eventHandlerControl, refreshEvent } from "comps/controls/eventHandlerControl"; +import { clickEvent, eventHandlerControl, refreshEvent, doubleClickEvent } from "comps/controls/eventHandlerControl"; import React, { ReactElement, useCallback, useEffect, useRef } from "react"; import { IconControl } from "comps/controls/iconControl"; import { ColorControl } from "comps/controls/colorControl"; @@ -17,6 +17,7 @@ import { optionsControl } from "comps/controls/optionsControl"; import { BoolControl } from "comps/controls/boolControl"; import { dropdownControl } from "comps/controls/dropdownControl"; import { JSONObject } from "util/jsonTypes"; +import { useCompClickEventHandler } from "@lowcoder-ee/comps/utils/useCompClickEventHandler"; const MacaroneList = [ '#fde68a', @@ -72,7 +73,7 @@ const DropdownOption = new MultiCompBuilder( }) .build(); -const EventOptions = [clickEvent, refreshEvent] as const; +const EventOptions = [clickEvent, refreshEvent, doubleClickEvent] as const; export const alignOptions = [ { label: , value: "flex-start" }, @@ -99,6 +100,8 @@ const MemoizedAvatar = React.memo(({ onItemEvent?: (event: string) => void; }) => { const mountedRef = useRef(true); + const handleClickEvent = useCompClickEventHandler({onEvent}) + // Cleanup on unmount useEffect(() => { @@ -116,8 +119,8 @@ const MemoizedAvatar = React.memo(({ } // Then trigger main component event - onEvent("click"); - }, [onEvent, onItemEvent]); + handleClickEvent() + }, [onItemEvent, handleClickEvent]); return ( diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnDropdownComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnDropdownComp.tsx index 9055413de1..b78601a5fa 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnDropdownComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnDropdownComp.tsx @@ -16,6 +16,7 @@ import { Button100 } from "comps/comps/buttonComp/buttonCompConstants"; import styled from "styled-components"; import { ButtonType } from "antd/es/button"; import { clickEvent, eventHandlerControl } from "comps/controls/eventHandlerControl"; +import { useCompClickEventHandler } from "@lowcoder-ee/comps/utils/useCompClickEventHandler"; const StyledButton = styled(Button100)` display: flex; @@ -43,8 +44,9 @@ const childrenMap = { const getBaseValue: ColumnTypeViewFn = (props) => props.label; // Memoized dropdown menu component -const DropdownMenu = React.memo(({ items, options, onEvent }: { items: any[]; options: any[]; onEvent?: (eventName: string) => void }) => { +const DropdownMenu = React.memo(({ items, options, onEvent }: { items: any[]; options: any[]; onEvent: (eventName: string) => void }) => { const mountedRef = useRef(true); + const handleClickEvent = useCompClickEventHandler({onEvent}) // Cleanup on unmount useEffect(() => { @@ -59,8 +61,8 @@ const DropdownMenu = React.memo(({ items, options, onEvent }: { items: any[]; op const itemIndex = options.findIndex(option => option.label === item?.label); item && options[itemIndex]?.onEvent("click"); // Also trigger the dropdown's main event handler - onEvent?.("click"); - }, [items, options, onEvent]); + handleClickEvent(); + }, [items, options, handleClickEvent]); const handleMouseDown = useCallback((e: React.MouseEvent) => { e.stopPropagation(); @@ -127,7 +129,7 @@ const DropdownView = React.memo((props: { const buttonStyle = useStyle(ButtonStyle); const menu = useMemo(() => ( - + {})} /> ), [items, props.options, props.onEvent]); return ( diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnLinkComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnLinkComp.tsx index a82a760e7f..e93b3082a6 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnLinkComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnLinkComp.tsx @@ -10,13 +10,14 @@ import { disabledPropertyView } from "comps/utils/propertyUtils"; import styled, { css } from "styled-components"; import { styleControl } from "comps/controls/styleControl"; import { TableColumnLinkStyle } from "comps/controls/styleControlConstants"; -import { clickEvent, eventHandlerControl } from "comps/controls/eventHandlerControl"; +import { clickEvent, eventHandlerControl, doubleClickEvent } from "comps/controls/eventHandlerControl"; +import { useCompClickEventHandler } from "@lowcoder-ee/comps/utils/useCompClickEventHandler"; import { migrateOldData } from "@lowcoder-ee/comps/generators/simpleGenerators"; import { fixOldActionData } from "comps/comps/tableComp/column/simpleColumnTypeComps"; export const ColumnValueTooltip = trans("table.columnValueTooltip"); -const LinkEventOptions = [clickEvent] as const; +const LinkEventOptions = [clickEvent, doubleClickEvent] as const; const childrenMap = { text: StringControl, @@ -38,10 +39,12 @@ const StyledLink = styled.a<{ $disabled: boolean }>` `; // Memoized link component -export const ColumnLink = React.memo(({ disabled, label, onClick }: { disabled: boolean; label: string; onClick?: (eventName: string) => void }) => { +export const ColumnLink = React.memo(({ disabled, label, onClick }: { disabled: boolean; label: string; onClick: (eventName: string) => void }) => { + const handleClickEvent = useCompClickEventHandler({onEvent: onClick}) const handleClick = useCallback(() => { - if (disabled) return; - onClick?.("click"); + if (!disabled) { + handleClickEvent(); + } }, [disabled, onClick]); return ( diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnLinksComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnLinksComp.tsx index e707eab432..5a7fae3d3e 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnLinksComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnLinksComp.tsx @@ -9,7 +9,8 @@ import { trans } from "i18n"; import styled from "styled-components"; import { ColumnLink } from "comps/comps/tableComp/column/columnTypeComps/columnLinkComp"; import { LightActiveTextColor, PrimaryColor } from "constants/style"; -import { clickEvent, eventHandlerControl } from "comps/controls/eventHandlerControl"; +import { clickEvent, eventHandlerControl, doubleClickEvent } from "comps/controls/eventHandlerControl"; +import { useCompClickEventHandler } from "@lowcoder-ee/comps/utils/useCompClickEventHandler"; import { migrateOldData } from "@lowcoder-ee/comps/generators/simpleGenerators"; import { fixOldActionData } from "comps/comps/tableComp/column/simpleColumnTypeComps"; @@ -39,22 +40,16 @@ const MenuWrapper = styled.div` } `; -const LinkEventOptions = [clickEvent] as const; +const LinkEventOptions = [clickEvent, doubleClickEvent] as const; // Memoized menu item component const MenuItem = React.memo(({ option, index }: { option: any; index: number }) => { - const handleClick = useCallback(() => { - if (!option.disabled && option.onClick) { - option.onClick("click"); - } - }, [option.disabled, option.onClick]); - return ( ); diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnSelectComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnSelectComp.tsx index ee15dda648..b54be87997 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnSelectComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnSelectComp.tsx @@ -6,12 +6,11 @@ import { IconControl } from "comps/controls/iconControl"; import { MultiCompBuilder } from "comps/generators"; import { optionsControl } from "comps/controls/optionsControl"; import { disabledPropertyView, hiddenPropertyView } from "comps/utils/propertyUtils"; - import { trans } from "i18n"; import { ColumnTypeCompBuilder, ColumnTypeViewFn } from "../columnTypeCompBuilder"; import { ColumnValueTooltip } from "../simpleColumnTypeComps"; import { styled } from "styled-components"; -import { clickEvent, eventHandlerControl } from "comps/controls/eventHandlerControl"; +import { clickEvent, eventHandlerControl, doubleClickEvent } from "comps/controls/eventHandlerControl"; const Wrapper = styled.div` display: inline-flex; @@ -79,7 +78,7 @@ const Wrapper = styled.div` } `; -const SelectOptionEventOptions = [clickEvent] as const; +const SelectOptionEventOptions = [clickEvent, doubleClickEvent] as const; // Create a new option type with event handlers for each option const SelectOptionWithEvents = new MultiCompBuilder( @@ -149,7 +148,7 @@ const SelectEdit = React.memo((props: SelectEditProps) => { // Trigger the specific option's event handler const selectedOption = props.options.find(option => option.value === val); - if (selectedOption && selectedOption.onEvent) { + if (selectedOption?.onEvent) { selectedOption.onEvent("click"); } diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/simpleTextComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/simpleTextComp.tsx index aba5052526..dcdffe3907 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/simpleTextComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/simpleTextComp.tsx @@ -7,10 +7,11 @@ import { IconControl } from "comps/controls/iconControl"; import { hasIcon } from "comps/utils"; import React, { useCallback, useMemo } from "react"; import { RecordConstructorToComp } from "lowcoder-core"; -import { clickEvent, eventHandlerControl } from "comps/controls/eventHandlerControl"; +import { clickEvent, doubleClickEvent, eventHandlerControl } from "comps/controls/eventHandlerControl"; import styled from "styled-components"; +import { useCompClickEventHandler } from "@lowcoder-ee/comps/utils/useCompClickEventHandler"; -const TextEventOptions = [clickEvent] as const; +const TextEventOptions = [clickEvent, doubleClickEvent] as const; const TextWrapper = styled.div` cursor: pointer; @@ -49,11 +50,11 @@ interface SimpleTextEditViewProps { } const SimpleTextContent = React.memo(({ value, prefixIcon, suffixIcon, onEvent }: SimpleTextContentProps) => { + const handleClickEvent = useCompClickEventHandler({onEvent: onEvent ?? (() => {})}) + const handleClick = useCallback(() => { - if (onEvent) { - onEvent("click"); - } - }, [onEvent]); + handleClickEvent() + }, [handleClickEvent]); return ( diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/simpleColumnTypeComps.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/simpleColumnTypeComps.tsx index 0abadf38f2..8ec51c6a1a 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/simpleColumnTypeComps.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/simpleColumnTypeComps.tsx @@ -13,8 +13,9 @@ import React, { useCallback, useEffect, useMemo } from "react"; import { CSSProperties } from "react"; import { RecordConstructorToComp } from "lowcoder-core"; import { ToViewReturn } from "@lowcoder-ee/comps/generators/multi"; -import { clickEvent, eventHandlerControl } from "comps/controls/eventHandlerControl"; +import { clickEvent, eventHandlerControl, doubleClickEvent } from "comps/controls/eventHandlerControl"; import { migrateOldData } from "@lowcoder-ee/comps/generators/simpleGenerators"; +import { useCompClickEventHandler } from "@lowcoder-ee/comps/utils/useCompClickEventHandler"; export const fixOldActionData = (oldData: any) => { if (!oldData) return oldData; @@ -46,7 +47,7 @@ export const ButtonTypeOptions = [ }, ] as const; -const ButtonEventOptions = [clickEvent] as const; +const ButtonEventOptions = [clickEvent, doubleClickEvent] as const; const childrenMap = { text: StringControl, @@ -64,10 +65,11 @@ const ButtonStyled = React.memo(({ props }: { props: ToViewReturn { - props.onClick?.("click"); - }, [props.onClick]); + handleClickEvent() + }, [handleClickEvent]); const buttonStyle = useMemo(() => ({ margin: 0, diff --git a/client/packages/lowcoder/src/comps/comps/textComp.tsx b/client/packages/lowcoder/src/comps/comps/textComp.tsx index 93b3d79ae0..dcc5ccdb2b 100644 --- a/client/packages/lowcoder/src/comps/comps/textComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/textComp.tsx @@ -20,13 +20,14 @@ import { PaddingControl } from "../controls/paddingControl"; import React, { useContext, useEffect, useRef, useMemo } from "react"; import { EditorContext } from "comps/editorState"; -import { clickEvent, eventHandlerControl } from "../controls/eventHandlerControl"; +import { clickEvent, doubleClickEvent, eventHandlerControl } from "../controls/eventHandlerControl"; import { NewChildren } from "../generators/uiCompBuilder"; import { RecordConstructorToComp } from "lowcoder-core"; import { ToViewReturn } from "../generators/multi"; import { BoolControl } from "../controls/boolControl"; +import { useCompClickEventHandler } from "../utils/useCompClickEventHandler"; -const EventOptions = [clickEvent] as const; +const EventOptions = [clickEvent, doubleClickEvent] as const; const getStyle = (style: TextStyleType) => { return css` @@ -224,9 +225,11 @@ const TextPropertyView = React.memo((props: { const TextView = React.memo((props: ToViewReturn) => { const value = props.text.value; + const handleClickEvent = useCompClickEventHandler({onEvent: props.onEvent}) + const handleClick = React.useCallback(() => { - props.onEvent("click"); - }, [props.onEvent]); + handleClickEvent() + }, [handleClickEvent]); const containerStyle = useMemo(() => ({ justifyContent: props.horizontalAlignment, diff --git a/client/packages/lowcoder/src/comps/comps/timelineComp/timelineComp.tsx b/client/packages/lowcoder/src/comps/comps/timelineComp/timelineComp.tsx index db45ba023b..06e1ff1a4e 100644 --- a/client/packages/lowcoder/src/comps/comps/timelineComp/timelineComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/timelineComp/timelineComp.tsx @@ -30,6 +30,7 @@ import { import { clickEvent, eventHandlerControl, + doubleClickEvent, } from "comps/controls/eventHandlerControl"; import { TimeLineStyle, @@ -49,6 +50,7 @@ import { convertTimeLineData } from "./timelineUtils"; import { default as Timeline } from "antd/es/timeline"; import { EditorContext } from "comps/editorState"; import { styled } from "styled-components"; +import { useCompClickEventHandler } from "@lowcoder-ee/comps/utils/useCompClickEventHandler"; const TimelineWrapper = styled.div<{ $style: TimeLineStyleType @@ -69,6 +71,7 @@ const TimelineWrapper = styled.div<{ const EventOptions = [ clickEvent, + doubleClickEvent, ] as const; const modeOptions = [ @@ -110,6 +113,8 @@ const TimelineComp = ( ) => { const { value, dispatch, style, mode, reverse, onEvent } = props; const [icons, setIcons] = useState([]); + const handleClickEvent = useCompClickEventHandler({onEvent}) + useEffect(() => { const loadIcons = async () => { const iconComponents = await Promise.all( @@ -140,7 +145,7 @@ const TimelineComp = ( e.preventDefault(); dispatch(changeChildAction("clickedObject", value, false)); dispatch(changeChildAction("clickedIndex", index, false)); - onEvent("click"); + handleClickEvent() }} // for responsiveness style={{ diff --git a/client/packages/lowcoder/src/comps/controls/eventHandlerControl.tsx b/client/packages/lowcoder/src/comps/controls/eventHandlerControl.tsx index d8c26d7ad8..b4b19d5228 100644 --- a/client/packages/lowcoder/src/comps/controls/eventHandlerControl.tsx +++ b/client/packages/lowcoder/src/comps/controls/eventHandlerControl.tsx @@ -704,6 +704,7 @@ export const InputEventHandlerControl = eventHandlerControl([ export const ButtonEventHandlerControl = eventHandlerControl([ clickEvent, + doubleClickEvent, ] as const); export const ChangeEventHandlerControl = eventHandlerControl([ @@ -818,4 +819,5 @@ export const CardEventHandlerControl = eventHandlerControl([ clickExtraEvent, focusEvent, blurEvent, + doubleClickEvent ] as const); \ No newline at end of file diff --git a/client/packages/lowcoder/src/comps/utils/useCompClickEventHandler.tsx b/client/packages/lowcoder/src/comps/utils/useCompClickEventHandler.tsx new file mode 100644 index 0000000000..e8f64cc5a4 --- /dev/null +++ b/client/packages/lowcoder/src/comps/utils/useCompClickEventHandler.tsx @@ -0,0 +1,47 @@ +import React, { useCallback, useRef } from "react"; + +export enum ClickEventType { + CLICK = "click", + DOUBLE_CLICK = "doubleClick" +} + +interface Props { + onEvent: (event: ClickEventType) => void; +} + +const DOUBLE_CLICK_THRESHOLD = 300; // ms + +export const useCompClickEventHandler = (props: Props) => { + const lastClickTimeRef = useRef(0); + const clickTimerRef = useRef>(); + + const handleClick = useCallback(() => { + const now = Date.now(); + + // Clear any existing timeout + if (clickTimerRef.current) { + clearTimeout(clickTimerRef.current); + } + + if ((now - lastClickTimeRef.current) < DOUBLE_CLICK_THRESHOLD) { + props.onEvent(ClickEventType.DOUBLE_CLICK); + } else { + clickTimerRef.current = setTimeout(() => { + props.onEvent(ClickEventType.CLICK); + }, DOUBLE_CLICK_THRESHOLD); + } + + lastClickTimeRef.current = now; + }, [props.onEvent]); + + // Cleanup on unmount + React.useEffect(() => { + return () => { + if (clickTimerRef.current) { + clearTimeout(clickTimerRef.current); + } + }; + }, []); + + return handleClick; +}; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy