From ab0b9035e578534409cf1fd08238364cf0301f37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?La=C3=ADs=20Fray=20Ribeiro?= Date: Fri, 27 Sep 2024 09:37:41 -0300 Subject: [PATCH] feat(clickable): add only immediate prop This new property will define whether the ClickableBubble will allow the bubble of only its immediate child or of all its internal children. fix #1963 --- .../components/clickable/clickable-bubble.tsx | 33 ++++++++++++++++++- .../src/components/clickable/clickable.tsx | 10 ++++-- .../clickable/stories/examples.stories.tsx | 32 ++++++++++++++++++ 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/packages/shoreline/src/components/clickable/clickable-bubble.tsx b/packages/shoreline/src/components/clickable/clickable-bubble.tsx index 8a2f29332d..25299659fb 100644 --- a/packages/shoreline/src/components/clickable/clickable-bubble.tsx +++ b/packages/shoreline/src/components/clickable/clickable-bubble.tsx @@ -2,6 +2,7 @@ import type { ComponentPropsWithoutRef, ReactNode } from 'react' import { forwardRef } from 'react' import { Compose } from '../compose' +import type { ExtendedMouseEvent } from './clickable' /** * Bubbles events to Clickable @@ -14,7 +15,30 @@ import { Compose } from '../compose' */ export const ClickableBubble = forwardRef( function Clickable(props, ref) { - return + const { onlyImmediateChild = false, onClick, ...otherProps } = props + const clickEvent = (event: ExtendedMouseEvent): void => { + const targetIsImmediateChild = !!event.target.getAttribute( + 'data-sl-clickable-bubble' + ) + + if ( + (onlyImmediateChild && targetIsImmediateChild) || + !onlyImmediateChild + ) { + event.target.setAttribute('data-should-bubble', 'true') + } + + onClick?.(event) + } + + return ( + + ) } ) @@ -23,6 +47,13 @@ export interface ClickableBubbleOptions { * Children to bubble event */ children: ReactNode + /** + * if true only immediate children will bubble the event + * if false the immediate children and their children will bubble the event + * + * @default false + */ + onlyImmediateChild?: boolean } export type ClickableBubbleProps = ClickableBubbleOptions & diff --git a/packages/shoreline/src/components/clickable/clickable.tsx b/packages/shoreline/src/components/clickable/clickable.tsx index cc0fe20801..8557213e7f 100644 --- a/packages/shoreline/src/components/clickable/clickable.tsx +++ b/packages/shoreline/src/components/clickable/clickable.tsx @@ -45,12 +45,18 @@ export const Clickable = forwardRef( ) function shouldBubble(event: ExtendedMouseEvent): boolean { - return !!event?.target?.getAttribute('data-sl-clickable-bubble') + const shouldBubbleEvent = !!event.target.getAttribute('data-should-bubble') + event.target.removeAttribute('data-should-bubble') + + return shouldBubbleEvent } -interface ExtendedMouseEvent extends Omit, 'target'> { +export interface ExtendedMouseEvent + extends Omit, 'target'> { target: React.MouseEvent['target'] & { + setAttribute: (qualifiedName: string, value: string) => void getAttribute: (attribute: string) => any + removeAttribute: (attribute: string) => void } } diff --git a/packages/shoreline/src/components/clickable/stories/examples.stories.tsx b/packages/shoreline/src/components/clickable/stories/examples.stories.tsx index 83e0016347..e406c96bf7 100644 --- a/packages/shoreline/src/components/clickable/stories/examples.stories.tsx +++ b/packages/shoreline/src/components/clickable/stories/examples.stories.tsx @@ -1,3 +1,4 @@ +import { Stack } from '../../stack' import { Clickable, ClickableBubble } from '../index' export default { @@ -17,3 +18,34 @@ export function ClickBubble() { ) } + +export function ClickBubbleAllChildren() { + return ( + + alert('Parent click')}> + +

+ Click the text will bubble the click event to the parent{' '} + + and click in their child too + {' '} + because the ClickableBubble is not set to onlyImmediateChild +

+
+ +
+ alert('Parent click')}> + +

+ Click the text will bubble the click event to the parent{' '} + + and click in their child will not + {' '} + because the ClickableBubble is set to onlyImmediateChild +

+
+ +
+
+ ) +}