/** * Copyright (c) Paymium. * * This source code is licensed under the MIT license found in the * LICENSE file in the root of this projects source tree. */ import { composeStyles, createStyles, inlineStyle, isWeb, useTheme, } from '@crossed/styled'; import { Floating, FloatingProps, FloatingRef, Sheet } from '../../overlay'; import { ChevronDown } from '@crossed/unicons'; import { FadeIn, FadeOut } from 'react-native-reanimated'; import { ComponentProps, CSSProperties, forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useTransition, } from 'react'; import { Calendar as CalendarComponent, type CalendarProps as CalendarComponentProps, } from '../Calendar'; import { IDay } from '@crossed/use-calendar'; import { Box } from '../../layout/Box'; import { useMedia } from '../../useMedia'; import { composeEventHandlers } from '@crossed/core'; import { Focus } from './Focus'; import { ActionSheetRef } from '@crossed/sheet'; import { XBox } from '../../layout'; const duration = 100; const styles = createStyles(({ colors, space, boxShadow }) => ({ dynamic: (e: any) => { const transform = []; if (e.transform) { const [x, y] = e.transform .replace('translate(', '') .replace(')', '') .replaceAll('px', '') .replaceAll(' ', '') .split(','); transform.push({ translateX: Number(x) }, { translateY: Number(y) }); } return Object.assign({}, e, { transform }); }, calendar: { base: { backgroundColor: colors.background.secondary, borderWidth: 1, borderColor: colors.border.secondary, paddingLeft: space.xl, paddingRight: space.xl, paddingTop: space.xl, paddingBottom: space.xl, borderRadius: 16, boxShadow, position: 'absolute', width: 432, }, }, })); export type FloatingRefExtended = FloatingRef & { isOpen: () => boolean }; export type CalendarProps = CalendarComponentProps & Pick, 'shards'> & { floatingStyles: CSSProperties; setFloating: (_node: HTMLElement | null) => void; locale: string; floatingProps?: FloatingProps; }; export const Calendar = forwardRef( ( { onDateSelected, setFloating, floatingStyles, locale, events, firstDayOfWeek, maxDate, minDate, monthsToDisplay, selectedDate, availableDates, floatingProps, shards, }, ref ) => { const [, setTransition] = useTransition(); const { colors } = useTheme(); const { md } = useMedia(); const open = useRef(false); useImperativeHandle( ref, () => ({ open: () => { floatingRef.current?.open(); sheetRef.current?.show(); }, close: () => { floatingRef.current?.close(); sheetRef.current?.hide(); }, isOpen() { return open.current; }, }), [] ); const floatingRef = useRef(null); const sheetRef = useRef(null); const toggleOpen = useCallback((e) => { open.current = e; }, []); const handleDateSelected = useCallback( (e: IDay) => { setTransition(() => { onDateSelected?.(e); }); floatingRef.current?.close(); sheetRef.current?.hide(); }, [onDateSelected] ); const handleClose = useCallback((e: KeyboardEvent | PointerEvent) => { if ( e instanceof PointerEvent || (e instanceof KeyboardEvent && e.key === 'Escape') ) { floatingRef.current?.close(); } }, []); useEffect(() => { if (isWeb && open.current) { document.addEventListener('click', handleClose); document.addEventListener('keyup', handleClose); return () => { document.removeEventListener('click', handleClose); document.removeEventListener('keyup', handleClose); }; } return () => {}; }, [open.current]); const showFloating = isWeb && md; const renderCalendar = ( ); const renderIcon = ( ); return showFloating ? ( {renderIcon} ({ base: { zIndex: 100, position: 'absolute' }, web: { base: { boxShadow } }, })) )} > {renderCalendar} ) : ( {renderIcon} ({ base: { height: undefined } }))} > ({ base: { height: undefined, paddingHorizontal: space.md, paddingVertical: space.xl, }, }))} > {renderCalendar} ); } );