Software: Apache/2.4.41 (Ubuntu). PHP/8.0.30 uname -a: Linux apirnd 5.4.0-204-generic #224-Ubuntu SMP Thu Dec 5 13:38:28 UTC 2024 x86_64 uid=33(www-data) gid=33(www-data) groups=33(www-data) Safe-mode: OFF (not secure) /var/www/html/wincloud_gateway/node_modules/@buffetjs/core/src/components/TimePicker/ drwxr-xr-x | |
| Viewing file: Select action/file-type: /**
*
* TimePicker
*
*/
import React, {
useCallback,
useEffect,
useState,
useRef,
useMemo,
} from 'react';
import { isInteger, toNumber } from 'lodash';
import PropTypes from 'prop-types';
import {
IconWrapper,
TimePicker as StyledTimePicker,
TimePickerWrapper,
TimeList,
} from '@buffetjs/styles';
import { useEventListener, useShortcutEffect } from '@buffetjs/hooks';
import Icon from '../Icon';
const MINUTES_IN_HOUR = 60;
// Returns string with two digits padded at start with 0
const pad = num => `0${num}`.substr(-2);
// Convert time array to formatted time string
export const timeFormatter = time => {
const newTime = Array(3)
.fill('00')
.concat(splitArray(time))
.reverse();
newTime.length = 3;
return format(newTime).join(':');
};
// Convert time string to time array
const splitArray = string => {
if (isInteger(toNumber(string)) && string) {
const stringFormat = string.length === 3 ? `0${string}` : string;
return stringFormat.match(/.{1,2}/g).reverse();
}
const lowercase = string ? string.toLowerCase() : '0';
const array = lowercase.includes('h')
? lowercase.split('h')
: lowercase.split(':');
return array.reverse().filter(v => !!v);
};
// Ensure two-digit format for minutes and seconds
const format = array =>
array.map((string, i) => {
if (string.length < 2) {
return i === 0 ? `0${string}` : `${string}0`;
}
return string;
});
// Hide seconds if needed
const short = hour => {
const array = hour.split(':');
if (array.length > 2) {
return array.slice(0, -1).join(':');
}
return hour;
};
// return array of minutes in hours with current step
const getMinutesArr = step => {
const length = MINUTES_IN_HOUR / step;
return Array.from({ length }, (_v, i) => step * i);
};
// Generate options for TimeList display
const getOptions = step => {
const hours = Array.from({ length: 24 }, (_, i) => i);
const minutes = getMinutesArr(step);
const options = hours.reduce((acc, cur) => {
const hour = pad(cur);
const hourOptions = minutes.map(minute => {
const label = `${hour}:${pad(minute)}`;
return { value: `${label}:00`, label };
});
return acc.concat(hourOptions);
}, []);
return options;
};
// Find the nearest time option to select a TimeList value
const roundHour = (time, step) => {
const arr = splitArray(time);
const minutesArr = getMinutesArr(step);
const nearMin = nearest(
minutesArr.concat(MINUTES_IN_HOUR),
parseInt(arr[1], 10)
);
arr[1] = minutesArr.includes(arr[1]) ? '00' : pad(nearMin);
arr[2] = nearMin === 60 ? `${parseInt(arr[2], 10) + 1}` : arr[2];
return format(arr.reverse()).join(':');
};
// Set the nearest option to select a TimeList value
const nearest = (arr, val) =>
arr.reduce(
(p, n) => (Math.abs(p) > Math.abs(n - val) ? n - val : p),
Infinity
) + val;
function TimePicker(props) {
const { name, onChange, seconds, tabIndex, value, step } = props;
const [inputVal, setInputVal] = useState('');
const [isOpen, setIsOpen] = useState(false);
const options = useMemo(() => getOptions(step), [step]);
const inputRef = useRef();
const wrapperRef = useRef();
const listRef = useRef();
const listRefs = options.reduce((acc, curr) => {
acc[curr.value] = useRef();
return acc;
}, {});
const currentTimeSelected = useMemo(
() => roundHour(timeFormatter(inputVal), step),
[inputVal, step]
);
// Effect to set the time
useEffect(() => {
if (!isOpen) {
const time = seconds ? value : short(value);
setInputVal(time);
}
}, [value, seconds, isOpen]);
// Effect to enable scrolling
useEffect(() => {
const currentRef = currentTimeSelected;
if (isOpen && listRefs[currentRef]) {
listRef.current.scrollTop = listRefs[currentRef].current.offsetTop;
}
}, [isOpen, currentTimeSelected, listRefs]);
// Custom hook to close the TimeList
useEventListener(
'click',
event => {
if (!wrapperRef.current.contains(event.target)) {
setIsOpen(false);
}
},
isOpen
);
// Custom hook to select a time using the keyboard's up arrow
useShortcutEffect(
'arrowUp',
() => {
if (isOpen) {
const currentIndex = options.findIndex(
o => o.value === currentTimeSelected
);
if (!currentIndex) return;
const nextIndex = currentIndex - 1;
const nextTime = options[nextIndex] || options[currentIndex];
updateTime(nextTime.value);
}
},
isOpen
);
// Custom hook to select a time using the keyboard's down arrow
useShortcutEffect(
'arrowDown',
() => {
if (isOpen) {
const currentIndex = options.findIndex(
o => o.value === currentTimeSelected
);
const lastIndex = options.length - 1;
if (currentIndex >= lastIndex) return;
const nextIndex = currentIndex + 1;
const nextTime = options[nextIndex] || options[lastIndex];
updateTime(nextTime.value);
}
},
isOpen
);
// Custom hook to close the time list
useShortcutEffect(
'enter',
() => {
if (isOpen) {
setIsOpen(false);
inputRef.current.blur();
}
},
isOpen
);
useShortcutEffect(
'tab',
() => {
if (isOpen) {
setIsOpen(false);
inputRef.current.blur();
}
},
isOpen
);
const handleChange = ({ target }) => {
updateTime(target.value);
};
const handleChangeRadio = useCallback(() => {}, []);
const formatInputValue = time => {
if (!seconds) {
setInputVal(short(time));
} else {
setInputVal(time);
}
};
const handleClick = ({ target }) => {
updateTime(target.value);
setIsOpen(false);
};
const updateTime = time => {
formatInputValue(time);
onChange({
target: {
name,
type: 'time',
value: timeFormatter(time),
},
});
};
return (
<TimePickerWrapper ref={wrapperRef} className={props.className}>
<StyledTimePicker
{...props}
autoComplete="off"
onChange={handleChange}
onFocus={() => setIsOpen(true)}
ref={inputRef}
type="text"
value={inputVal}
tabIndex={tabIndex}
/>
<IconWrapper>
<Icon icon="time" />
</IconWrapper>
<TimeList className={isOpen && 'displayed'} ref={listRef}>
{isOpen &&
options.map(option => (
<li key={option.value} ref={listRefs[option.value]}>
<input
type="radio"
onChange={handleChangeRadio}
onClick={handleClick}
value={option.value}
id={option.value}
name="time"
checked={option.value === currentTimeSelected}
tabIndex="0"
/>
<label htmlFor={option.value}>{option.label}</label>
</li>
))}
</TimeList>
</TimePickerWrapper>
);
}
TimePicker.defaultProps = {
className: null,
onChange: () => {},
tabIndex: '0',
seconds: false,
value: '',
step: 30,
};
TimePicker.propTypes = {
className: PropTypes.string,
name: PropTypes.string.isRequired,
onChange: PropTypes.func,
seconds: PropTypes.bool,
step: (props, propName) =>
MINUTES_IN_HOUR % props[propName] > 0 &&
new Error('step should be divisible by 60'),
tabIndex: PropTypes.string,
value: PropTypes.string,
};
export default TimePicker;
|
:: Command execute :: | |
--[ c99shell v. 2.5 [PHP 8 Update] [24.05.2025] | Generation time: 0.0214 ]-- |