pkg-components
Version:
155 lines (144 loc) • 5.48 kB
text/typescript
export const initialState = {
time: null, // Time obj 'hours', 'minutes', 'meridiem'
selected: 'none',
redrawCounter: 0,
SELECTED_RANGE: {
none: { start: 0, end: 0 },
hours: { start: 0, end: 2, previous: 'meridiem', next: 'minutes', pattern: /^[0-9]+$/ },
minutes: { start: 3, end: 5, previous: 'hours', next: 'meridiem', pattern: /^[0-9]+$/ },
meridiem: { start: 6, end: 8, previous: 'minutes', next: 'hours', pattern: /^[AMP]/ }
}
}
export const reducer = (state, action) => {
let _time; let _selected
switch (action.type) {
// DETERMINE WHICH ELEMENT OF TIME INPUT TO SELECT/HIGHLIGHT (i.e. HOURS, MINS, AM/PM)
case 'clickToSelect':
const selectionStart = { action }
_selected = 'meridiem'
if (selectionStart < state.SELECTED_RANGE.hours.end + 1) {
_selected = 'hours'
} else if (selectionStart < state.SELECTED_RANGE.minutes.end) {
_selected = 'minutes'
}
return {
...state,
selected: _selected,
redrawCounter: state.redrawCounter + 1
}
// DETERMIN IF INPUT IS VALID RELATIVE TO 'hour', 'minutes', 'meridiem'
case 'validateInput':
const { value, keyDownTimer } = action
let [_hours, _minutes, _meridiem] = (value.trim()).split(/[:\s]/)
const _timeNow = Date.now()
// UPDATE HOURS
if (state.selected === 'hours') {
if (state.SELECTED_RANGE.hours.pattern.test(_hours) === false) break
_hours = Number(_hours)
const _prevHours = Number(state.time.hours)
let _hoursFormatted
if (_timeNow - keyDownTimer.current < 500) {
_hoursFormatted = (_prevHours === 1 && _hours < 3) ? '1' + _hours : _hours === 0 ? state.time.hours : ' ' + _hours
} else {
_hoursFormatted = _hours === 0 ? state.time.hours : ' ' + _hours
}
return {
...state,
time: {...state.time, hours: _hoursFormatted },
redrawCounter: state.redrawCounter + 1
}
}
// UPDATE MINUTES
if (state.selected === 'minutes') {
if (state.SELECTED_RANGE.minutes.pattern.test(_minutes) === false) break
_minutes = Number(_minutes)
let _minutesFormatted
const _prevMinutes = Number(state.time.minutes)
if (_timeNow - keyDownTimer.current < 500) {
_minutesFormatted = _prevMinutes < 6 ? `${_prevMinutes}${_minutes}` : '0' + _minutes
} else {
_minutesFormatted = '0' + _minutes
}
return {
...state,
time: {...state.time, minutes: _minutesFormatted },
redrawCounter: state.redrawCounter + 1
}
}
// UPDATE MERIDIEM
if (state.selected === 'meridiem') {
let _meridiemFormatted
const _prevMeridiem = state.time.meridiem
if (_meridiem[0].toUpperCase() === 'A' || _meridiem[0].toUpperCase() === 'P') {
_meridiemFormatted = `${_meridiem[0].toUpperCase()}M`
} else {
_meridiemFormatted = _prevMeridiem
}
return {
...state,
time: {...state.time, meridiem: _meridiemFormatted },
redrawCounter: state.redrawCounter + 1
}
}
return {
...state,
redrawCounter: state.redrawCounter + 1
}
// DETERMINE IF 'Enter', 'Tab' OR ARROW KEYS HAVE BEEN PRESSED
case 'keyDown':
const { event, textFieldRef } = action
const { code } = event
if (code === 'Enter') {
textFieldRef.current.blur()
_selected = 'none'
}
if (['Tab', 'ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown'].includes(code)) {
event.preventDefault()
if (code === 'Tab' || code === 'ArrowRight') _selected = state.SELECTED_RANGE[state.selected].next
if (code === 'ArrowLeft') _selected = state.SELECTED_RANGE[state.selected].previous
if (code === 'ArrowUp') _time = bumpSelected({ direction: 'up', time: state.time, selected: state.selected })
if (code === 'ArrowDown') _time = bumpSelected({ direction: 'down', time: state.time, selected: state.selected })
}
return {
...state,
selected: _selected ?? state.selected,
time: _time ?? state.time
}
case 'setSelected':
const { selected } = action
return {
...state,
selected
}
case 'setTime':
const { time } = action
return {
...state,
time
}
default:
console.log(`reducer > no matching case for ${action.type}`)
}
}
// Helper
const bumpSelected = ({ direction, time, selected }) => {
let { hours, minutes, meridiem } = time
if (selected === 'hours') {
if (direction === 'up') {
hours = hours === '12' ? ' 1' : String(Number(hours) + 1).padStart(2, ' ')
} else {
hours = hours === ' 1' ? '12' : String(Number(hours) - 1).padStart(2, ' ')
}
}
if (selected === 'minutes') {
if (direction === 'up') {
minutes = minutes === '59' ? '00' : String(Number(minutes) + 1).padStart(2, '0')
} else {
minutes = minutes === '00' ? '59' : String(Number(minutes) - 1).padStart(2, '0')
}
}
if (selected === 'meridiem') {
meridiem = meridiem === 'AM' ? 'PM' : 'AM'
}
return { hours, minutes, meridiem }
}