import { createMixin, css, on, type Handle } from '@remix-run/ui' import { animateEntrance, animateExit, spring } from '@remix-run/ui/animation' // Demo const buttonExitAnimation = { opacity: 0, transform: 'scale(1.15)', duration: 100, easing: 'ease-in', } const confirmationEnterAnimation = { opacity: 0, transform: 'scale(0.9)', duration: 200, easing: 'ease-out', } export function HoldToConfirm(handle: Handle) { let confirmed = false return () => (
*': { gridArea: '1 / 1' }, }), ]} > {!confirmed && ( { confirmed = true handle.update() }} /> )} {confirmed && ( { confirmed = false handle.update() }} /> )}
) } function HoldButton(handle: Handle<{ onConfirm: () => void }>) { let confirming = false return () => ( ) } function Confirmation(handle: Handle<{ onReset: () => void }>) { return () => (
Deleted
) } function TrashIcon() { return () => ( ) } function CheckIcon() { return () => ( ) } const PRESS_CONFIRM_TIME = 2000 const pressConfirmStartEventType = 'demo:press-confirm-start' as const const pressConfirmCancelEventType = 'demo:press-confirm-cancel' as const const pressConfirmEndEventType = 'demo:press-confirm-end' as const declare global { interface HTMLElementEventMap { [pressConfirmStartEventType]: Event [pressConfirmCancelEventType]: Event [pressConfirmEndEventType]: Event } } const baseConfirmPress = createMixin((handle) => { let timer = 0 let pressing = false let clearTimer = () => { if (timer) { clearTimeout(timer) timer = 0 } } let start = (target: Element) => { clearTimer() pressing = true target.dispatchEvent(new Event(pressConfirmStartEventType, { bubbles: true })) timer = window.setTimeout(() => { timer = 0 pressing = false target.dispatchEvent(new Event(pressConfirmEndEventType, { bubbles: true })) }, PRESS_CONFIRM_TIME) } let cancel = (target: Element) => { if (!pressing) return clearTimer() pressing = false target.dispatchEvent(new Event(pressConfirmCancelEventType, { bubbles: true })) } handle.addEventListener('remove', () => { clearTimer() pressing = false }) return (props) => ( { if (event.isPrimary === false) return start(event.currentTarget) }), on('pointerup', (event) => { cancel(event.currentTarget) }), on('pointercancel', (event) => { cancel(event.currentTarget) }), on('pointerleave', (event) => { cancel(event.currentTarget) }), on('keydown', (event) => { if (!(event.key === 'Enter' || event.key === ' ') || event.repeat) return event.preventDefault() start(event.currentTarget) }), on('keyup', (event) => { if (!(event.key === 'Enter' || event.key === ' ')) return event.preventDefault() cancel(event.currentTarget) }), on('blur', (event) => { cancel(event.currentTarget) }), ]} /> ) }) type ConfirmPressMixin = typeof baseConfirmPress & { readonly start: typeof pressConfirmStartEventType readonly cancel: typeof pressConfirmCancelEventType readonly end: typeof pressConfirmEndEventType } const confirmPress: ConfirmPressMixin = Object.assign(baseConfirmPress, { start: pressConfirmStartEventType, cancel: pressConfirmCancelEventType, end: pressConfirmEndEventType, })