Skip to content

Commit

Permalink
feat(clickable): add only immediate prop (#1983)
Browse files Browse the repository at this point in the history
  • Loading branch information
matheusps authored Oct 15, 2024
2 parents a43d4ad + ab0b903 commit 976c9c6
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 3 deletions.
33 changes: 32 additions & 1 deletion packages/shoreline/src/components/clickable/clickable-bubble.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -14,7 +15,30 @@ import { Compose } from '../compose'
*/
export const ClickableBubble = forwardRef<HTMLDivElement, ClickableBubbleProps>(
function Clickable(props, ref) {
return <Compose data-sl-clickable-bubble ref={ref} {...props} />
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 (
<Compose
data-sl-clickable-bubble
onClick={clickEvent}
ref={ref}
{...otherProps}
/>
)
}
)

Expand All @@ -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 &
Expand Down
10 changes: 8 additions & 2 deletions packages/shoreline/src/components/clickable/clickable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,18 @@ export const Clickable = forwardRef<HTMLDivElement, ClickableProps>(
)

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<React.MouseEvent<any>, 'target'> {
export interface ExtendedMouseEvent
extends Omit<React.MouseEvent<any>, 'target'> {
target: React.MouseEvent<any>['target'] & {
setAttribute: (qualifiedName: string, value: string) => void
getAttribute: (attribute: string) => any
removeAttribute: (attribute: string) => void
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Stack } from '../../stack'
import { Clickable, ClickableBubble } from '../index'

export default {
Expand All @@ -17,3 +18,34 @@ export function ClickBubble() {
</Clickable>
)
}

export function ClickBubbleAllChildren() {
return (
<Stack>
<Clickable onClick={() => alert('Parent click')}>
<ClickableBubble>
<p>
Click the text will bubble the click event to the parent{' '}
<span style={{ fontWeight: 600 }}>
and click in their child too
</span>{' '}
because the ClickableBubble is not set to onlyImmediateChild
</p>
</ClickableBubble>
<button onClick={() => alert('Child click')}>Child</button>
</Clickable>
<Clickable onClick={() => alert('Parent click')}>
<ClickableBubble onlyImmediateChild>
<p>
Click the text will bubble the click event to the parent{' '}
<span style={{ fontWeight: 600 }}>
and click in their child will not
</span>{' '}
because the ClickableBubble is set to onlyImmediateChild
</p>
</ClickableBubble>
<button onClick={() => alert('Child click')}>Child</button>
</Clickable>
</Stack>
)
}

0 comments on commit 976c9c6

Please sign in to comment.