@itwin/itwinui-react
Version:
A react component library for iTwinUI
475 lines (474 loc) • 14.3 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', {
value: true,
});
Object.defineProperty(exports, 'TimePicker', {
enumerable: true,
get: function () {
return TimePicker;
},
});
const _interop_require_default = require('@swc/helpers/_/_interop_require_default');
const _interop_require_wildcard = require('@swc/helpers/_/_interop_require_wildcard');
const _classnames = /*#__PURE__*/ _interop_require_default._(
require('classnames'),
);
const _react = /*#__PURE__*/ _interop_require_wildcard._(require('react'));
const _index = require('../../utils/index.js');
const isSameHour = (date1, date2, meridiem) => {
let adjustedHours = meridiem
? formatHourFrom12(date1.getHours(), meridiem)
: date1.getHours();
if (!!meridiem)
return !!date2 && adjustedHours % 12 === date2.getHours() % 12;
return !!date2 && adjustedHours === date2.getHours();
};
const isSameMinute = (date1, date2) =>
!!date2 && date1.getMinutes() === date2.getMinutes();
const isSameSecond = (date1, date2) =>
!!date2 && date1.getSeconds() === date2.getSeconds();
const isSameTime = (date1, date2, precision, meridiem) => {
let isSameTime = true;
switch (precision) {
case 'seconds':
isSameTime = isSameSecond(date1, date2);
if (!isSameTime) break;
case 'minutes':
isSameTime = isSameMinute(date1, date2);
if (!isSameTime) break;
case 'hours':
isSameTime = isSameHour(date1, date2, meridiem);
}
return isSameTime;
};
const isSameMeridiem = (meridiem, date) =>
!!date && ('AM' === meridiem ? date.getHours() < 12 : date.getHours() >= 12);
const formatHourFrom12 = (hour, meridiem) => {
let adjustedHour = hour % 12;
return 'PM' === meridiem ? adjustedHour + 12 : adjustedHour;
};
const setHours = (hour, date) =>
new Date(
date.getFullYear(),
date.getMonth(),
date.getDate(),
hour,
date.getMinutes(),
date.getSeconds(),
);
const defaultCombinedRenderer = (date, precision) => {
let dateString = '';
switch (precision) {
case 'seconds':
dateString =
':' +
date.getSeconds().toLocaleString(void 0, {
minimumIntegerDigits: 2,
});
case 'minutes':
dateString =
':' +
date.getMinutes().toLocaleString(void 0, {
minimumIntegerDigits: 2,
}) +
dateString;
case 'hours':
dateString =
date.getHours().toLocaleString(void 0, {
minimumIntegerDigits: 2,
}) + dateString;
}
return dateString;
};
const TimePicker = _react.forwardRef((props, forwardedRef) => {
let {
date,
onChange,
use12Hours = false,
precision = 'minutes',
hourStep = 1,
minuteStep = 1,
secondStep = 1,
setFocusHour = false,
hourRenderer = (date) =>
date.getHours().toLocaleString(void 0, {
minimumIntegerDigits: 2,
}),
minuteRenderer = (date) =>
date.getMinutes().toLocaleString(void 0, {
minimumIntegerDigits: 2,
}),
secondRenderer = (date) =>
date.getSeconds().toLocaleString(void 0, {
minimumIntegerDigits: 2,
}),
meridiemRenderer = (meridiem) => meridiem,
useCombinedRenderer = false,
combinedRenderer = defaultCombinedRenderer,
className,
...rest
} = props;
let [selectedTime, setSelectedTime] = _react.useState(date);
let [focusedTime, setFocusedTime] = _react.useState(
selectedTime ?? new Date(),
);
let [meridiem, setMeridiem] = _react.useState(
use12Hours ? (focusedTime?.getHours() > 11 ? 'PM' : 'AM') : void 0,
);
_react.useEffect(() => {
setFocusedTime(date ?? new Date());
setSelectedTime(date);
}, [date]);
let onHourClick = (date) => {
let adjustedHour = use12Hours
? formatHourFrom12(date.getHours(), meridiem)
: date.getHours();
let adjustedSelectedTime = setHours(
adjustedHour,
selectedTime ?? new Date(),
);
updateCurrentTime(adjustedSelectedTime);
};
let onTimeClick = (date) => {
let adjustedHour = use12Hours
? formatHourFrom12(date.getHours(), meridiem)
: date.getHours();
let adjustedSelectedTime = setHours(adjustedHour, date);
updateCurrentTime(adjustedSelectedTime);
};
let onMeridiemClick = (value) => {
let adjustedSelectedTime = selectedTime ?? new Date();
let currentHours = adjustedSelectedTime.getHours();
setMeridiem(value);
if ('AM' === value && currentHours > 11)
adjustedSelectedTime = setHours(currentHours - 12, adjustedSelectedTime);
if ('PM' === value && currentHours <= 12)
adjustedSelectedTime = setHours(currentHours + 12, adjustedSelectedTime);
updateCurrentTime(adjustedSelectedTime);
};
let updateCurrentTime = (time) => {
let adjustedTime = time;
if ('hours' === precision)
adjustedTime = new Date(
time.getFullYear(),
time.getMonth(),
time.getDate(),
time.getHours(),
0,
0,
);
if ('minutes' === precision)
adjustedTime = new Date(
time.getFullYear(),
time.getMonth(),
time.getDate(),
time.getHours(),
time.getMinutes(),
0,
);
setFocusedTime(adjustedTime);
setSelectedTime(adjustedTime);
onChange?.(adjustedTime);
};
let onHourFocus = (date) => {
let adjustedHour = use12Hours
? formatHourFrom12(date.getHours(), meridiem)
: date.getHours();
setFocusedTime(setHours(adjustedHour, focusedTime));
};
let onTimeFocus = (date) => {
let adjustedHour = use12Hours
? formatHourFrom12(date.getHours(), meridiem)
: date.getHours();
setFocusedTime(setHours(adjustedHour, date));
};
let onMeridiemFocus = (value) => {
let adjustedSelectedTime = selectedTime ?? new Date();
let currentHours = adjustedSelectedTime.getHours();
if ('AM' === value && currentHours > 11) {
setMeridiem(value);
adjustedSelectedTime = setHours(currentHours - 12, adjustedSelectedTime);
}
if ('PM' === value && currentHours <= 12) {
setMeridiem(value);
adjustedSelectedTime = setHours(currentHours + 12, adjustedSelectedTime);
}
setFocusedTime(adjustedSelectedTime);
};
let generateDataList = (size, value, step) => {
let data = [];
for (let i = 0; i < size; i++) if (i % step === 0) data.push(value(i));
return data;
};
let time = _react.useMemo(() => {
let time = selectedTime ?? new Date();
let data = [];
let hoursArray = Array.from(Array(use12Hours ? 12 : 24).keys())
.filter((i) => i % hourStep === 0)
.map((i) => (use12Hours && 0 === i ? 12 : i));
let minutesArray = Array.from(Array(60).keys()).filter(
(i) => i % minuteStep === 0,
);
let secondsArray = Array.from(Array(60).keys()).filter(
(i) => i % secondStep === 0,
);
hoursArray.forEach((hour) => {
if ('hours' === precision)
data.push(
new Date(
time.getFullYear(),
time.getMonth(),
time.getDate(),
hour,
time.getMinutes(),
time.getSeconds(),
),
);
else
minutesArray.forEach((minute) => {
if ('minutes' === precision)
data.push(
new Date(
time.getFullYear(),
time.getMonth(),
time.getDate(),
hour,
minute,
time.getSeconds(),
),
);
else
secondsArray.forEach((second) => {
data.push(
new Date(
time.getFullYear(),
time.getMonth(),
time.getDate(),
hour,
minute,
second,
),
);
});
});
});
return data;
}, [hourStep, minuteStep, secondStep, selectedTime, use12Hours, precision]);
let hours = _react.useMemo(() => {
let time = selectedTime ?? new Date();
return generateDataList(
use12Hours ? 12 : 24,
(i) =>
new Date(
time.getFullYear(),
time.getMonth(),
time.getDate(),
use12Hours && 0 === i ? 12 : i,
time.getMinutes(),
time.getSeconds(),
),
hourStep,
);
}, [hourStep, selectedTime, use12Hours]);
let minutes = _react.useMemo(() => {
let time = selectedTime ?? new Date();
return generateDataList(
60,
(i) =>
new Date(
time.getFullYear(),
time.getMonth(),
time.getDate(),
time.getHours(),
i,
time.getSeconds(),
),
minuteStep,
);
}, [minuteStep, selectedTime]);
let seconds = _react.useMemo(() => {
let time = selectedTime ?? new Date();
return generateDataList(
60,
(i) =>
new Date(
time.getFullYear(),
time.getMonth(),
time.getDate(),
time.getHours(),
time.getMinutes(),
i,
),
secondStep,
);
}, [secondStep, selectedTime]);
return _react.createElement(
_index.Box,
{
className: (0, _classnames.default)('iui-time-picker', className),
ref: forwardedRef,
...rest,
},
useCombinedRenderer
? _react.createElement(TimePickerColumn, {
data: time,
isSameFocused: (val) =>
isSameTime(
val,
focusedTime,
precision,
use12Hours ? meridiem : void 0,
),
isSameSelected: (val) =>
isSameTime(
val,
selectedTime,
precision,
use12Hours ? meridiem : void 0,
),
onFocusChange: onTimeFocus,
onSelectChange: onTimeClick,
setFocus: setFocusHour,
precision: precision,
valueRenderer: combinedRenderer,
})
: _react.createElement(
_react.Fragment,
null,
_react.createElement(TimePickerColumn, {
data: hours,
isSameFocused: (val) =>
isSameHour(val, focusedTime, use12Hours ? meridiem : void 0),
isSameSelected: (val) =>
isSameHour(val, selectedTime, use12Hours ? meridiem : void 0),
onFocusChange: onHourFocus,
onSelectChange: onHourClick,
setFocus: setFocusHour,
valueRenderer: hourRenderer,
}),
'hours' !== precision &&
_react.createElement(TimePickerColumn, {
data: minutes,
isSameFocused: (val) => isSameMinute(val, focusedTime),
isSameSelected: (val) => isSameMinute(val, selectedTime),
onFocusChange: (date) => setFocusedTime(date),
onSelectChange: (date) => updateCurrentTime(date),
valueRenderer: minuteRenderer,
}),
'seconds' === precision &&
_react.createElement(TimePickerColumn, {
data: seconds,
isSameFocused: (val) => isSameSecond(val, focusedTime),
isSameSelected: (val) => isSameSecond(val, selectedTime),
onFocusChange: (date) => setFocusedTime(date),
onSelectChange: (date) => updateCurrentTime(date),
valueRenderer: secondRenderer,
}),
),
use12Hours &&
_react.createElement(TimePickerColumn, {
data: ['AM', 'PM'],
isSameFocused: (val) => isSameMeridiem(val, focusedTime),
isSameSelected: (val) => isSameMeridiem(val, selectedTime),
onFocusChange: (date) => onMeridiemFocus(date),
onSelectChange: (value) => onMeridiemClick(value),
valueRenderer: meridiemRenderer,
className: 'iui-period',
}),
);
});
if ('development' === process.env.NODE_ENV)
TimePicker.displayName = 'TimePicker';
const TimePickerColumn = (props) => {
let {
data,
onFocusChange,
onSelectChange,
isSameFocused,
isSameSelected,
setFocus = false,
valueRenderer,
precision = 'minutes',
className = 'iui-time',
} = props;
let needFocus = _react.useRef(setFocus);
let handleTimeKeyDown = (
event,
maxValue,
onFocus,
onSelect,
currentValue,
) => {
if (event.altKey) return;
switch (event.key) {
case 'ArrowDown':
if (currentValue + 1 > maxValue) break;
onFocus(currentValue + 1);
needFocus.current = true;
event.preventDefault();
break;
case 'ArrowUp':
if (currentValue - 1 < 0) break;
onFocus(currentValue - 1);
needFocus.current = true;
event.preventDefault();
break;
case 'Enter':
case ' ':
case 'Spacebar':
onSelect(currentValue);
event.preventDefault();
break;
}
};
return _react.createElement(
_index.Box,
{
className: `${className}`,
},
_react.createElement(
'ol',
null,
data.map((value, index) => {
let isSameFocus = isSameFocused(value);
return _react.createElement(
_index.Box,
{
as: 'li',
onKeyDown: (event) => {
handleTimeKeyDown(
event,
data.length - 1,
(index) => onFocusChange(data[index]),
(index) => onSelectChange(data[index]),
index,
);
},
className: (0, _classnames.default)({
'iui-selected': isSameSelected(value),
}),
key: index,
tabIndex: isSameFocus ? 0 : void 0,
ref: (ref) => {
if (!ref || !isSameFocus) return;
setTimeout(() => {
ref.scrollIntoView({
block: 'nearest',
inline: 'nearest',
});
if (needFocus.current) {
ref.focus();
needFocus.current = false;
}
});
},
onClick: () => {
onSelectChange(value);
},
},
valueRenderer(value, precision),
);
}),
),
);
};