UNPKG

@amaui/ui-react

Version:
667 lines (663 loc) 26.5 kB
import _extends from "@babel/runtime/helpers/extends"; import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; const _excluded = ["tonal", "color", "version", "size", "value", "valueDefault", "onChange", "calendar", "calendarDefault", "onChangeCalendar", "start", "end", "menu", "range", "now", "calendars", "min", "max", "validate", "menu_month_previous_unit", "menu_month_next_unit", "valid", "getMonths", "getYears", "belowCalendars", "disabled", "IconPrevious", "IconNext", "IconDropDown", "CalendarMonthProps", "OptionButtonProps", "PaginationItemsProps", "renderDay", "renderDayName", "className"]; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } import React from 'react'; import { clamp, is, isEnvironment, Try } from '@amaui/utils'; import { classNames, style, useAmauiTheme } from '@amaui/style-react'; import { AmauiDate, add, remove, format, set } from '@amaui/date'; import IconMaterialNavigateBefore from '@amaui/icons-material-rounded-react/IconMaterialNavigateBeforeW100'; import IconMaterialNavigateNext from '@amaui/icons-material-rounded-react/IconMaterialNavigateNextW100'; import IconMaterialArrowDropDown from '@amaui/icons-material-rounded-react/IconMaterialArrowDropDownW100'; import SurfaceElement from '../Surface'; import LineElement from '../Line'; import FadeElement from '../Fade'; import IconButtonElement from '../IconButton'; import ButtonElement from '../Button'; import CalendarMonthElement from '../CalendarMonth'; import ListElement from '../List'; import ListItemElement from '../ListItem'; import DividerElement from '../Divider'; import TypeElement from '../Type'; import CarouselElement from '../Carousel'; import PaginationItemElement from '../PaginationItem'; import { IconDoneAnimated } from '../Buttons/Buttons'; import { iconFontSize, staticClassName } from '../utils'; const useStyle = style(theme => ({ root: { borderRadius: theme.methods.shape.radius.value(4, 'px'), overflow: 'hidden' }, size_small: { width: '275px', '& .amaui-ListItem-root': { minHeight: '40px' }, '& .amaui-ListItem-inset': { paddingInlineStart: '44px' } }, size_regular: { width: '320px', '& .amaui-ListItem-root': { minHeight: '50px' }, '& .amaui-ListItem-inset': { paddingInlineStart: '58px' } }, size_large: { width: '375px', '& .amaui-ListItem-root': { minHeight: '60px' }, '& .amaui-ListItem-inset': { paddingInlineStart: '78px' } }, range: { width: 'unset' }, header: { width: '100%', padding: `${theme.methods.space.value(1.5, 'px')} ${theme.methods.space.value(1, 'px')} ${theme.methods.space.value(0.5, 'px')}` }, calendars: { width: '100%', padding: `0 ${theme.methods.space.value(1, 'px')} ${theme.methods.space.value(1, 'px')}` }, calendar: { width: '100%' }, option: { transition: theme.methods.transitions.make('opacity'), '&.amaui-Button-root': { paddingInline: `${theme.methods.space.value(1, 'px')} 0` }, '& .amaui-Button-end': { paddingInline: `${theme.methods.space.value(1, 'px')} 0` } }, option_secondary: { opacity: '0.4' }, list: { width: '100%', maxHeight: '307px', overflowY: 'auto', '&.amaui-List-root': { maxWidth: 'unset !important', boxShadow: 'none' } }, listItem: {}, list_version_year: { width: '100%', maxHeight: '299px', overflowY: 'auto', marginTop: '8px' }, day_version_year: { flex: '0 1 72px', height: '36px', '&:hover': { boxShadow: 'inset 0px 0px 0px 1px currentColor' } }, carousel: { '&.amaui-Carousel-root': { height: '100vh', maxHeight: '440px', padding: `0 ${theme.methods.space.value(1, 'px')}` } }, carousel_item: { width: '100%', marginTop: '16px', userSelect: 'none' }, carousel_item_label: { paddingInlineStart: theme.methods.space.value(2, 'px') }, arrow: { transition: theme.methods.transitions.make('transform') }, arrow_open: { transform: 'rotate(-180deg)' }, divider: { '&.amaui-Divider-root': { margin: '0px' } } }), { name: 'amaui-Calendar' }); const Calendar = /*#__PURE__*/React.forwardRef((props__, ref) => { const theme = useAmauiTheme(); const props = React.useMemo(() => _objectSpread(_objectSpread(_objectSpread({}, theme?.ui?.elements?.all?.props?.default), theme?.ui?.elements?.amauiCalendar?.props?.default), props__), [props__]); const Line = React.useMemo(() => theme?.elements?.Line || LineElement, [theme]); const Surface = React.useMemo(() => theme?.elements?.Surface || SurfaceElement, [theme]); const Fade = React.useMemo(() => theme?.elements?.Fade || FadeElement, [theme]); const IconButton = React.useMemo(() => theme?.elements?.IconButton || IconButtonElement, [theme]); const Button = React.useMemo(() => theme?.elements?.Button || ButtonElement, [theme]); const CalendarMonth = React.useMemo(() => theme?.elements?.CalendarMonth || CalendarMonthElement, [theme]); const List = React.useMemo(() => theme?.elements?.List || ListElement, [theme]); const ListItem = React.useMemo(() => theme?.elements?.ListItem || ListItemElement, [theme]); const Divider = React.useMemo(() => theme?.elements?.Divider || DividerElement, [theme]); const Type = React.useMemo(() => theme?.elements?.Type || TypeElement, [theme]); const Carousel = React.useMemo(() => theme?.elements?.Carousel || CarouselElement, [theme]); const PaginationItem = React.useMemo(() => theme?.elements?.PaginationItem || PaginationItemElement, [theme]); const { tonal = true, color = 'default', version = 'regular', size = 'regular', value: value_, valueDefault, onChange, calendar: calendar_, calendarDefault, onChangeCalendar, start, end, menu = 'month-year', range, now, calendars = props.range ? 2 : 1, min, max, validate, menu_month_previous_unit, menu_month_next_unit, valid: valid_, getMonths: getMonths_, getYears: getYears_, belowCalendars, disabled, IconPrevious = IconMaterialNavigateBefore, IconNext = IconMaterialNavigateNext, IconDropDown = IconMaterialArrowDropDown, CalendarMonthProps, OptionButtonProps, PaginationItemsProps, renderDay, renderDayName, className } = props, other = _objectWithoutProperties(props, _excluded); const { classes } = useStyle(); const refs = { root: React.useRef(undefined), month: React.useRef(undefined), year: React.useRef(undefined), inProgressTransition: React.useRef() }; const [value, setValue] = React.useState(() => { const valueResult = (valueDefault !== undefined ? valueDefault : value_) || now && (range ? [new AmauiDate(), new AmauiDate()] : [new AmauiDate()]); return (is('array', valueResult) ? valueResult : [valueResult]).filter(Boolean); }); const [calendar, setCalendar] = React.useState((calendarDefault !== undefined ? calendarDefault : calendar_) || new AmauiDate()); const [carousel, setCarousel] = React.useState(); const [open, setOpen] = React.useState(); // Value React.useEffect(() => { if (value_ !== undefined && value_ !== value) setValue((is('array', value_) ? value_ : [value_]).filter(Boolean)); }, [value_]); // Calendar React.useEffect(() => { if (calendar_ !== undefined && calendar_ !== calendar) setCalendar(calendar_); }, [calendar_]); const onUpdate = React.useCallback(valueNew => { // Inner update if (!props.hasOwnProperty('value')) setValue(valueNew); if (is('function', onChange)) onChange(!range ? valueNew[0] : valueNew); }, [range, onChange]); const onUpdateCalendar = React.useCallback(valueNew => { // Inner update if (!props.hasOwnProperty('calendar')) setCalendar(valueNew); if (is('function', onChangeCalendar)) onChangeCalendar(valueNew); }, [onChangeCalendar]); const onUpdateCalendarOption = React.useCallback(valueNew => { setOpen(null); onUpdateCalendar(valueNew); }, []); const onCalendarMonthChange = React.useCallback(valueNew_ => { const valueNew = is('array', valueNew_) ? valueNew_ : [valueNew_]; if (valueNew !== value) onUpdate(valueNew); }, [value]); const onCalendarMonthChangeCalendar = React.useCallback(valueNew => { onUpdateCalendar(valueNew); }, [calendar]); const valid = React.useCallback(function () { if (is('function', valid_)) return valid_(...arguments); return true; }, [valid_]); const move = React.useCallback(function () { let next = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; let unit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'month'; if (refs.inProgressTransition.current) return; onUpdateCalendar((next ? add : remove)(1, unit, calendar)); }, [calendar]); const onOpen = React.useCallback(function () { let valueUpdate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'month'; const valueNew = open === valueUpdate ? null : valueUpdate; setOpen(valueNew); // Scroll to the value if (valueNew) setTimeout(() => { const list = refs[valueNew]?.current; if (list) { const valueData = valueNew === 'month' ? calendar.month - 1 : calendar.year; Try(() => { const element = list.querySelector(`[data-value="${valueData}"]`); if (element) list.scrollTo({ top: clamp(element.offsetTop - element.parentElement.offsetTop + (menu === 'month' ? -104 : 51), 0), behavior: 'smooth' }); }); } }); }, [open, menu, calendar]); const getMonths = is('function', getMonths_) ? getMonths_ : React.useCallback(() => { const valueCalendar = set(14, 'day', calendar); const result = Array.from({ length: 12 }).map((item, index) => ({ value: set(index, 'month', valueCalendar) })); return result; }, [calendar]); const getYears = is('function', getYears_) ? getYears_ : React.useCallback(() => { const minYear = 1970; const length = 130; return Array.from({ length }).map((item, index) => ({ value: set(minYear + index, 'year', calendar) })); }, []); const onCarouselInit = React.useCallback(() => { // Scroll to the value setTimeout(() => { Try(() => { const rootDocument = isEnvironment('browser') ? refs.root.current?.ownerDocument || window.document : undefined; let item = rootDocument.body.querySelector('[data-month-from]'); if (item) { item = item.parentElement.parentElement; setCarousel({ y: item.offsetTop }); } }); }, 140); }, []); // Prevent multiple moves of the calendar // before the previous transition is done const onTransition = React.useCallback((element, status) => { refs.inProgressTransition.current = !['entered', 'exited', 'removed'].includes(status); }, []); const calendarMonthProps = _objectSpread({ renderDay, renderDayName, disabled }, CalendarMonthProps); const main = () => { switch (version) { case 'year': return /*#__PURE__*/React.createElement(Carousel, { color: "default", id: value[0].milliseconds + (value[1]?.milliseconds || 0) + year, value: carousel, arrows: false, progress: false, orientation: "vertical", moveBeyondEdge: false, itemSize: "auto", gap: 0, free: true, onInit: onCarouselInit, items: Array.from({ length: 12 }).map((item, index) => { const calendarAmauiDate = set(index, 'month', calendar); return /*#__PURE__*/React.createElement(Line, { key: index, gap: 1.5, direction: "column", className: classNames([classes.carousel_item]) }, /*#__PURE__*/React.createElement(Type, { version: size === 'large' ? 'l1' : size === 'regular' ? 'l2' : 'l3', className: classNames([classes.carousel_item_label]) }, format(calendarAmauiDate, 'MMMM'), " ", format(calendarAmauiDate, 'YYYY')), /*#__PURE__*/React.createElement(CalendarMonth, _extends({ color: "default", value: value, calendar: calendarAmauiDate, valid: valid, now: now, size: size, range: range, offset: index, min: min, max: max, validate: validate, outside: false, noTransition: true }, calendarMonthProps, { onChange: onCalendarMonthChange, onChangeCalendar: onCalendarMonthChangeCalendar, TransitionProps: { onTransition }, className: classNames([staticClassName('Calendar', theme) && ['amaui-Calendar-calendar-days'], calendarMonthProps?.className, classes.calendar]) }))); }), ItemWrapperProps: { style: { width: '100%' } }, className: classNames([staticClassName('Calendar', theme) && ['amaui-Calendar-carousel'], classes.carousel]) }); default: return /*#__PURE__*/React.createElement(Line, { gap: 0, direction: "column", align: "center", style: { width: '100%' } }, /*#__PURE__*/React.createElement(Line, { direction: "row", align: "center", className: classNames([staticClassName('Calendar', theme) && ['amaui-Calendar-calendars'], classes.calendars]) }, Array.from({ length: calendars }).map((item, index) => { const calendarAmauiDate = add(index, 'month', calendar); return /*#__PURE__*/React.createElement(Line, { key: index, gap: 1, direction: "column", style: { width: '100%' } }, calendars > 1 && /*#__PURE__*/React.createElement(Type, { version: size === 'large' ? 'l1' : size === 'regular' ? 'l2' : 'l3', style: { paddingInlineStart: '16px' } }, format(calendarAmauiDate, 'MMMM')), /*#__PURE__*/React.createElement(CalendarMonth, _extends({ color: "default", value: value, calendar: calendarAmauiDate, valid: valid, now: now, size: size, range: range, offset: index, min: min, max: max, validate: validate }, calendarMonthProps, { onChange: onCalendarMonthChange, onChangeCalendar: onCalendarMonthChangeCalendar, TransitionProps: { onTransition }, className: classNames([staticClassName('Calendar', theme) && ['amaui-Calendar-calendar-days'], calendarMonthProps?.className, classes.calendar]) }))); })), belowCalendars); } }; const month = format(calendar, 'MMM'); const year = format(calendar, 'YYYY'); const optionButtonProps = _objectSpread({ color: 'inherit', version: 'text' }, OptionButtonProps); return /*#__PURE__*/React.createElement(Surface, _extends({ ref: item => { if (ref) { if (is('function', ref)) ref(item);else ref.current = item; } refs.root.current = item; }, className: classNames([staticClassName('Calendar', theme) && ['amaui-Calendar-root', `amaui-Calendar-version-${version}`, `amaui-Calendar-size-${size}`], className, classes.root, classes[`size_${size}`], range && classes.range]) }, other), start, /*#__PURE__*/React.createElement(Line, { gap: 0.5, direction: "row", align: "center", justify: "space-between", className: classNames([staticClassName('Calendar', theme) && ['amaui-Calendar-header'], classes.header]) }, menu === 'month-year' ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Line, { gap: 0, direction: "row", align: "center" }, /*#__PURE__*/React.createElement(Fade, { in: !open }, /*#__PURE__*/React.createElement(IconButton, _extends({ onClick: () => move(false), size: size, "aria-label": "Previous month", disabled: open || +year <= 1970 && month === 'Jan' }, optionButtonProps), /*#__PURE__*/React.createElement(IconPrevious, { size: size }))), /*#__PURE__*/React.createElement(Button, _extends({ version: "text", onClick: () => onOpen(), fontSize: iconFontSize, size: size, end: /*#__PURE__*/React.createElement(Fade, { in: open !== 'year' }, /*#__PURE__*/React.createElement(IconDropDown, { size: size, className: classNames([staticClassName('Calendar', theme) && ['amaui-Calendar-arrow'], classes.arrow, open === 'month' && classes.arrow_open]) })), "aria-label": `Select month, current ${month}` }, optionButtonProps, { className: classNames([staticClassName('Calendar', theme) && ['amaui-Calendar-option'], optionButtonProps?.className, classes.option, open === 'year' && classes.option_secondary]) }), month), /*#__PURE__*/React.createElement(Fade, { in: !open }, /*#__PURE__*/React.createElement(IconButton, _extends({ onClick: () => move(), size: size, "aria-label": "Next month", disabled: open || +year === 2099 && month === 'Dec' }, optionButtonProps), /*#__PURE__*/React.createElement(IconNext, { size: size })))), /*#__PURE__*/React.createElement(Line, { gap: 0, direction: "row", align: "center" }, /*#__PURE__*/React.createElement(Fade, { in: !open }, /*#__PURE__*/React.createElement(IconButton, _extends({ onClick: () => move(false, 'year'), size: size, "aria-label": "Previous year", disabled: open || +year <= 1970 }, optionButtonProps), /*#__PURE__*/React.createElement(IconPrevious, { size: size }))), /*#__PURE__*/React.createElement(Button, _extends({ version: "text", onClick: () => onOpen('year'), fontSize: iconFontSize, size: size, end: /*#__PURE__*/React.createElement(Fade, { in: open !== 'month' }, /*#__PURE__*/React.createElement(IconDropDown, { size: size, className: classNames([staticClassName('Calendar', theme) && ['amaui-Calendar-arrow'], classes.arrow, open === 'year' && classes.arrow_open]) })), "aria-label": `Select year, current ${year}` }, optionButtonProps, { className: classNames([staticClassName('Calendar', theme) && ['amaui-Calendar-option'], optionButtonProps?.className, classes.option, open === 'month' && classes.option_secondary]) }), year), /*#__PURE__*/React.createElement(Fade, { in: !open }, /*#__PURE__*/React.createElement(IconButton, _extends({ onClick: () => move(true, 'year'), size: size, "aria-label": "Next year", disabled: open || +year === 2099 }, optionButtonProps), /*#__PURE__*/React.createElement(IconNext, { size: size }))))) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Button, _extends({ tonal: tonal, color: "inherit", size: size, version: "text", onClick: () => onOpen('year'), fontSize: iconFontSize, end: /*#__PURE__*/React.createElement(IconDropDown, { size: size, className: classNames([staticClassName('Calendar', theme) && ['amaui-Calendar-arrow'], classes.arrow, open === 'year' && classes.arrow_open]) }) }, optionButtonProps, { className: classNames([staticClassName('Calendar', theme) && ['amaui-Calendar-option'], optionButtonProps?.className, classes.option]) }), format(calendar, 'MMMM'), " ", format(calendar, 'YYYY')), /*#__PURE__*/React.createElement(Line, { gap: 0, direction: "row", align: "center" }, /*#__PURE__*/React.createElement(Fade, { in: !open }, /*#__PURE__*/React.createElement(IconButton, { tonal: tonal, color: "inherit", size: size, onClick: () => move(false, menu_month_previous_unit || 'month'), "aria-label": "Previous month", disabled: !!(open || +year === 1970 && month === 'Jan') }, /*#__PURE__*/React.createElement(IconPrevious, { size: size }))), /*#__PURE__*/React.createElement(Fade, { in: !open }, /*#__PURE__*/React.createElement(IconButton, { tonal: tonal, color: "inherit", size: size, onClick: () => move(true, menu_month_next_unit || 'month'), "aria-label": "Next month", disabled: !!(open || +year === 2099 && month === 'Dec') }, /*#__PURE__*/React.createElement(IconNext, { size: size })))))), !open && /*#__PURE__*/React.createElement(Fade, { in: true }, main()), open && /*#__PURE__*/React.createElement(Divider, { tonal: false, className: classNames([staticClassName('Calendar', theme) && ['amaui-Calendar-divider'], classes.divider]) }), open === 'month' && /*#__PURE__*/React.createElement(Fade, { in: true }, /*#__PURE__*/React.createElement(List, { ref: refs.month, color: "default", size: "large", menu: true, className: classNames([staticClassName('Calendar', theme) && ['amaui-Calendar-list'], classes.list]) }, getMonths(value, calendar, props).map((item, index) => { const amauiDate = item.value; const selected = calendar.month === amauiDate.month; return /*#__PURE__*/React.createElement(ListItem, { key: index, onClick: () => onUpdateCalendarOption(amauiDate), primary: format(amauiDate, 'MMMM'), inset: !selected, startAlign: "center", size: size, start: selected ? /*#__PURE__*/React.createElement(IconDoneAnimated, { in: true, add: true, simple: true, size: size }) : undefined, disabled: !valid(amauiDate, 'month'), selected: selected, button: true, PrimaryProps: { version: size === 'regular' ? 'b2' : size === 'small' ? 'b3' : 'b1' }, "data-value": index, className: classNames([staticClassName('Calendar', theme) && ['amaui-Calendar-list-item'], classes.listItem]) }); }))), open === 'year' && /*#__PURE__*/React.createElement(Surface, { color: "default" }, _ref => { let { palette } = _ref; return menu === 'month-year' ? /*#__PURE__*/React.createElement(Fade, { in: true }, /*#__PURE__*/React.createElement(List, { ref: refs.year, color: "default", size: size, menu: true, className: classNames([staticClassName('Calendar', theme) && ['amaui-Calendar-list'], classes.list]) }, getYears(value, calendar, props).map((item, index) => { const amauiDate = item.value; const yearValue = format(amauiDate, 'YYYY'); const selected = calendar.year === amauiDate.year; return /*#__PURE__*/React.createElement(ListItem, { key: index, onClick: () => onUpdateCalendarOption(amauiDate), primary: yearValue, inset: !selected, startAlign: "center", size: size, start: selected ? /*#__PURE__*/React.createElement(IconDoneAnimated, { in: true, add: true, simple: true, size: size }) : undefined, selected: selected, disabled: !valid(amauiDate, 'year'), button: true, PrimaryProps: { version: size === 'regular' ? 'b2' : size === 'small' ? 'b3' : 'b1' }, "data-value": yearValue, className: classNames([staticClassName('Calendar', theme) && ['amaui-Calendar-list-item'], classes.listItem]) }); }))) : /*#__PURE__*/React.createElement(Fade, { in: true }, /*#__PURE__*/React.createElement(Line, { ref: refs.year, direction: "row", wrap: "wrap", justify: "space-evenly", className: classNames([staticClassName('Calendar', theme) && ['amaui-Calendar-list-version-year'], classes.list_version_year]) }, getYears(value, calendar, props).map((item, index) => { const amauiDate = item.value; const yearValue = format(amauiDate, 'YYYY'); const selected = calendar.year === amauiDate.year; return /*#__PURE__*/React.createElement(PaginationItem, _extends({ key: index, tonal: tonal, color: "inherit", size: size, InteractionProps: { background: false }, TypeProps: { version: size === 'large' ? 'b1' : size === 'regular' ? 'b2' : 'b3', tone: !selected ? 'primary' : undefined }, onClick: () => onUpdateCalendarOption(amauiDate), "data-value": yearValue, disabled: !valid(amauiDate, 'year') }, PaginationItemsProps, { className: classNames([staticClassName('Calendar', theme) && ['amaui-Calendar-day-version-year'], PaginationItemsProps?.className, classes.day_version_year]), style: _objectSpread(_objectSpread({}, selected ? { color: theme.methods.palette.color.value(undefined, 90, true, palette), backgroundColor: theme.methods.palette.color.value(undefined, 40, true, palette) } : undefined), PaginationItemsProps?.style) }), yearValue); }))); }), end); }); Calendar.displayName = 'amaui-Calendar'; export default Calendar;