UNPKG

@renderlesskit/react

Version:

Collection of headless components/hooks that are accessible, composable, customizable from low level to build your own UI & Design System powered by Reakit

125 lines (103 loc) 5.29 kB
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; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } /** * All credit goes to [React Spectrum](https://github.com/adobe/react-spectrum) * We improved the Calendar from Stately [useCalendarState](https://github.com/adobe/react-spectrum/tree/main/packages/%40react-stately/calendar) * to work with Reakit System */ import { useMemo, useState } from "react"; import { useCompositeState } from "reakit"; import { useDateFormatter } from "@react-aria/i18n"; import { useControllableState } from "../utils"; import { add, convertValue, getSegmentLimits, setSegment } from "./helpers"; var EDITABLE_SEGMENTS = { year: true, month: true, day: true, hour: true, minute: true, second: true, dayPeriod: true }; var PAGE_STEP = { year: 5, month: 2, day: 7, hour: 2, minute: 15, second: 15 }; // Node seems to convert everything to lowercase... var TYPE_MAPPING = { dayperiod: "dayPeriod" }; export function useSegmentState(props) { var [validSegments, setValidSegments] = useState(props.value || props.defaultValue ? _objectSpread({}, EDITABLE_SEGMENTS) : {}); // https://stackoverflow.com/a/9893752/10629172 var dateFormatter = useDateFormatter(props.formatOptions); var resolvedOptions = useMemo(() => dateFormatter.resolvedOptions(), [dateFormatter]); // Determine how many editable segments there are for validation purposes. // The result is cached for performance. var numSegments = useMemo(() => dateFormatter.formatToParts(new Date()).filter(seg => EDITABLE_SEGMENTS[seg.type]).length, [dateFormatter]); // If there is a value prop, and some segments were previously placeholders, mark them all as valid. if (props.value && Object.keys(validSegments).length < numSegments) { setValidSegments(_objectSpread({}, EDITABLE_SEGMENTS)); } // We keep track of the placeholder date separately in state so that onChange is not called // until all segments are set. If the value === null (not undefined), then assume the component // is controlled, so use the placeholder as the value until all segments are entered so it doesn't // change from uncontrolled to controlled and emit a warning. var [placeholderDate, setPlaceholderDate] = useState(convertValue(props.placeholderDate) || new Date(new Date().getFullYear(), 0, 1)); var [date, setDate] = useControllableState({ value: props.value === null ? convertValue(placeholderDate) : convertValue(props.value), defaultValue: convertValue(props.defaultValue), onChange: props.onChange }); // If all segments are valid, use the date from state, otherwise use the placeholder date. var value = Object.keys(validSegments).length >= numSegments ? date : placeholderDate; var setValue = value => { if (Object.keys(validSegments).length >= numSegments) { setDate(value); } else { setPlaceholderDate(value); } }; var segments = dateFormatter.formatToParts(value).map(segment => _objectSpread(_objectSpread({ type: TYPE_MAPPING[segment.type] || segment.type, text: segment.value }, getSegmentLimits(value, segment.type, resolvedOptions)), {}, { isPlaceholder: !validSegments[segment.type] })); var adjustSegment = (type, amount) => { validSegments[type] = true; setValidSegments(_objectSpread({}, validSegments)); setValue(add(value, type, amount, resolvedOptions)); }; var segmentComposite = useCompositeState({ orientation: "horizontal" }); return _objectSpread(_objectSpread({}, segmentComposite), {}, { fieldValue: value, setFieldValue: setValue, segments, dateFormatter, increment(part) { adjustSegment(part, 1); }, decrement(part) { adjustSegment(part, -1); }, incrementPage(part) { adjustSegment(part, PAGE_STEP[part] || 1); }, decrementPage(part) { adjustSegment(part, -(PAGE_STEP[part] || 1)); }, setSegment(part, v) { validSegments[part] = true; setValidSegments(_objectSpread({}, validSegments)); // @ts-ignore setValue(setSegment(value, part, v, resolvedOptions)); }, confirmPlaceholder(part) { validSegments[part] = true; setValidSegments(_objectSpread({}, validSegments)); setValue(new Date(value)); } }); } //# sourceMappingURL=SegmentState.js.map