@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
JavaScript
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