Skip to content

Commit

Permalink
add InputPopover component & Popover autoFocus prop #252
Browse files Browse the repository at this point in the history
  • Loading branch information
joduplessis committed Jun 12, 2024
1 parent 2d6ed73 commit 5abb754
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 5 deletions.
1 change: 1 addition & 0 deletions packages/core/src/input/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './input'
export * from './input-popover'
63 changes: 63 additions & 0 deletions packages/core/src/input/input-popover.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React, { ReactNode, cloneElement, useRef } from 'react'
import { Popover, PopoverProps, getKey, renderChildren, useId, useVisibility } from '../'

export type InputPopoverProps = {
id?: string
defaultVisibility?: boolean
popoverProps?: PopoverProps
children: ReactNode
content: ReactNode
}

export const InputPopover = (props: InputPopoverProps) => {
const { id, defaultVisibility = false, popoverProps = {}, children, content } = props
const childRef = useRef(null)
const firstTimeFocus = useRef(false)
const isOpen = useRef(false)
const { show, hide, visible, delayedShow } = useVisibility(defaultVisibility)
const internalId = useId(id)

const handleFocus = (e) => {
if (!firstTimeFocus.current) {
e.stopPropagation()
e.preventDefault()
if (!isOpen.current) delayedShow(100)
}
}

const handleClick = (e) => {
if (!isOpen.current) delayedShow(100)
}

const handleKeyDown = (e) => {
const { isEnter } = getKey(e)
if (isEnter && !isOpen.current) delayedShow(100)
}

const handleDismiss = (e) => {
hide()
isOpen.current = false
firstTimeFocus.current = true
childRef.current?.focus()
}

return (
<Popover
targetId={internalId}
isVisible={visible}
content={content}
onDismiss={handleDismiss}
{...popoverProps}>
{renderChildren(children, (child) => {
return cloneElement(child, {
...child.props,
onFocus: handleFocus,
onClick: handleClick,
onKeyDown: handleKeyDown,
ref: childRef,
id: internalId,
})
})}
</Popover>
)
}
5 changes: 3 additions & 2 deletions packages/core/src/modal/modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export const Modal = (props: ModalProps) => {
const { trapFocus } = useFocus()

const handleKeyDown = (e) => {
console.log('modal')
const { isEscape } = getKey(e)
if (isEscape && dismissOnEscape) onDismiss(e)
}
Expand Down Expand Up @@ -104,14 +105,14 @@ export const Modal = (props: ModalProps) => {

return (
<div
tabIndex={0}
className={classNameOverlay}
onKeyUp={handleKeyDown}
onClick={handleBackgroundClick}>
<View
{...rest}
className={className}
aria-modal={true}
tabIndex={0}
onKeyDown={handleKeyDown}
ref={contentRef}>
{header && <div className="f-modal__header f-row">{header}</div>}
{props.children && <div className="f-modal__body">{props.children}</div>}
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/popover/popover.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
pointer-events: all;
}

.f-popover:focus {
outline: none
}

.f-popover:not(.is-ready) {
visibility: hidden;
}
Expand Down
26 changes: 23 additions & 3 deletions packages/core/src/popover/popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export const PopoverContent = forwardRef((props: CoreViewProps, ref) => (
export type PopoverAnchor = PopoutPosition

export type PopoverProps = {
__autoFocusTimeoutDelay?: number
autoFocus?: boolean
targetId?: string
fixPosition?: { top: number; left: number }
arrow?: boolean
Expand All @@ -38,6 +40,8 @@ export type PopoverProps = {

export const Popover = forwardRef((props: PopoverProps, ref) => {
const {
__autoFocusTimeoutDelay = 200,
autoFocus = true,
targetId,
fixPosition,
anchorProps = {},
Expand All @@ -54,7 +58,7 @@ export const Popover = forwardRef((props: PopoverProps, ref) => {
const [ready, setReady] = useState(false)
const [finalAnchor, setFinalAnchor] = useState('')
const id = useId(targetId)
const showPopover = isVisible && id && finalAnchor
const showPopover = isVisible && id && !!finalAnchor
const isFixed = !!fixPosition
const className = classNames(
{
Expand All @@ -66,6 +70,8 @@ export const Popover = forwardRef((props: PopoverProps, ref) => {
)

const dismissPopover = (e) => {
e.preventDefault()
e.stopPropagation()
dispatchPopoverEvent('ondismiss', e)
onDismiss(e)
setReady(false)
Expand All @@ -84,9 +90,21 @@ export const Popover = forwardRef((props: PopoverProps, ref) => {
}
}

useEvent('keydown', handleKeyDown, true)
useEvent('click', handleClick, true)

useEffect(() => {
if (autoFocus && showPopover) {
setTimeout(() => {
containerRef.current?.focus()

}, __autoFocusTimeoutDelay)
}
}, [
__autoFocusTimeoutDelay,
autoFocus,
showPopover,
])

useLayoutEffect(() => {
if (!id) return
if (!isVisible) return
Expand Down Expand Up @@ -137,7 +155,7 @@ export const Popover = forwardRef((props: PopoverProps, ref) => {
return cloneElement(child, {
...child.props,
ref: mergeRefs([child.ref, childRef]),
id,
id: child.props.id || id,
})
})}

Expand All @@ -153,6 +171,8 @@ export const Popover = forwardRef((props: PopoverProps, ref) => {
{...anchorProps}>
<View
{...rest}
tabIndex={0}
onKeyDown={handleKeyDown}
aria-describedby={id}
className={className}
ref={mergeRefs([ref, containerRef])}>
Expand Down

0 comments on commit 5abb754

Please sign in to comment.