react-js-cron-mantine
Version:
Fork of [react-js-cron](https://github.com/xrutayisire/react-js-cron), made to work with [mantine](https://mantine.dev)
278 lines (271 loc) • 10.7 kB
JavaScript
import React, { useState, useCallback, useEffect, useRef, useMemo } from 'react';
import Period from './fields/Period';
import MonthDays from './fields/MonthDays';
import Months from './fields/Months';
import Hours from './fields/Hours';
import Minutes from './fields/Minutes';
import WeekDays from './fields/WeekDays';
import { classNames, usePrevious } from './utils';
import { DEFAULT_LOCALE_EN } from './locale';
import { setValuesFromCronString, getCronStringFromValues } from './converter';
import { SimpleGrid } from '@mantine/core';
import { Button } from '@mantine/core';
import dayjs from 'dayjs';
import RelativeTime from 'dayjs/plugin/relativeTime';
import tz from 'dayjs/plugin/timezone';
import utc from 'dayjs/plugin/utc';
export default function Cron(props) {
const {
timezone_value = 'UTC',
clearButton = true,
convertToTimezone = false,
clearButtonAction = 'fill-with-every',
locale = DEFAULT_LOCALE_EN,
value = '',
setValue,
setValueObj,
displayError = true,
onError,
className,
defaultPeriod = 'day',
allowEmpty = 'for-default-value',
humanizeLabels = true,
humanizeValue = false,
disabled = false,
readOnly = false,
leadingZero = false,
shortcuts = ['@yearly', '@annually', '@monthly', '@weekly', '@daily', '@midnight', '@hourly'],
clockFormat,
periodicityOnDoubleClick = true,
mode = 'multiple',
allowedDropdowns = ['period', 'months', 'month-days', 'week-days', 'hours', 'minutes', 'switch'],
allowedPeriods = ['year', 'month', 'week', 'day', 'hour', 'minute', 'reboot']
} = props;
const internalValueRef = useRef(value);
const defaultPeriodRef = useRef(defaultPeriod);
const [period, setPeriod] = useState();
const [monthDays, setMonthDays] = useState();
const [months, setMonths] = useState();
const [weekDays, setWeekDays] = useState();
const [hours, setHours] = useState();
const [minutes, setMinutes] = useState();
const [tzhours, setTzHours] = useState();
const [tzminutes, setTzMinutes] = useState();
const [utchours, utcsetHours] = useState();
const [utcminutes, utcsetMinutes] = useState();
const [error, setInternalError] = useState(false);
const [tzerror, setTzError] = useState('');
const [valueCleared, setValueCleared] = useState(false);
const previousValueCleared = usePrevious(valueCleared);
const localeJSON = JSON.stringify(locale);
const [checked, setChecked] = useState(false);
useEffect(() => {
setValuesFromCronString(value, setInternalError, onError, allowEmpty, internalValueRef, true, locale, shortcuts, setMinutes, setHours, setMonthDays, setMonths, setWeekDays, setPeriod);
}, []);
useEffect(() => {
if (value !== internalValueRef.current) {
setValuesFromCronString(value, setInternalError, onError, allowEmpty, internalValueRef, false, locale, shortcuts, setMinutes, setHours, setMonthDays, setMonths, setWeekDays, setPeriod);
}
}, [value, internalValueRef, localeJSON, allowEmpty, shortcuts]);
useEffect(() => {
if ((period || minutes || months || monthDays || weekDays || hours) && !valueCleared && !previousValueCleared) {
const selectedPeriod = period || defaultPeriodRef.current;
const cron = getCronStringFromValues(selectedPeriod, months, monthDays, weekDays, convertToTimezone ? tzhours : hours, convertToTimezone ? tzminutes : minutes, setValueObj, humanizeValue);
setValue(cron, {
selectedPeriod
});
internalValueRef.current = cron;
setInternalError(false);
} else if (valueCleared) {
setValueCleared(false);
}
}, [period, monthDays, months, weekDays, tzhours, tzminutes, minutes, hours, humanizeValue, timezone_value, valueCleared]);
const handleClear = useCallback(() => {
setMonthDays([]);
setMonths([]);
setWeekDays([]);
setHours([]);
setMinutes([]);
let newValue = '';
const newPeriod = period !== 'reboot' && period ? period : defaultPeriodRef.current;
if (newPeriod !== period) {
setPeriod(newPeriod);
}
if (clearButtonAction === 'fill-with-every') {
const cron = getCronStringFromValues(newPeriod, undefined, undefined, undefined, undefined, undefined, setValueObj);
newValue = cron;
}
setValue(newValue, {
selectedPeriod: newPeriod
});
internalValueRef.current = newValue;
setValueCleared(true);
if (allowEmpty === 'never' && clearButtonAction === 'empty') {
setInternalError(true);
} else {
setInternalError(false);
}
}, [period, setValue, onError, clearButtonAction]);
const internalClassName = useMemo(() => classNames({
'react-js-cron': true,
'react-js-cron-error': error && displayError,
'react-js-cron-disabled': disabled,
'react-js-cron-read-only': readOnly,
[`${className}`]: !!className,
[`${className}-error`]: error && displayError && !!className,
[`${className}-disabled`]: disabled && !!className,
[`${className}-read-only`]: readOnly && !!className
}), [className, error, displayError, disabled, readOnly]);
const clearButtonClassName = useMemo(() => classNames({
'react-js-cron-clear-button': true,
[`${className}-clear-button`]: !!className
}), [className]);
const clearButtonNode = useMemo(() => {
if (clearButton && !readOnly) {
return React.createElement(Button, {
disabled: disabled,
ml: 5,
onClick: handleClear,
color: "red",
size: "xs"
}, "Clear");
}
return null;
}, [clearButton, readOnly, localeJSON, clearButtonClassName, disabled, handleClear]);
const periodForRender = period || defaultPeriodRef.current;
dayjs.extend(RelativeTime);
dayjs.extend(utc);
dayjs.extend(tz);
useEffect(() => {
const hourarr = [];
const minutearr = [];
if (convertToTimezone == true) {
try {
if (hours && hours.length > 0) {
const newh = hours?.map(h => {
if (minutes && minutes.length > 0) {
minutes?.map(m => {
hourarr.includes(Number(dayjs().hour(h).minute(m).tz(timezone_value).format('HH'))) ? null : hourarr.push(Number(dayjs().hour(h).minute(m).tz(timezone_value).format('HH')));
minutearr.includes(Number(dayjs().hour(h).minute(m).tz(timezone_value).format('mm'))) ? null : minutearr.push(Number(dayjs().hour(h).minute(m).tz(timezone_value).format('mm')));
});
} else {
if (Number(dayjs().hour(h).minute(0).tz(timezone_value).format('mm')) !== 0) {
hourarr.includes(Number(dayjs().hour(h).minute(0).tz(timezone_value).format('HH'))) ? null : hourarr.push(Number(dayjs().hour(h).minute(0).tz(timezone_value).format('HH')));
minutearr.includes(Number(dayjs().hour(h).minute(0).tz(timezone_value).format('mm'))) ? null : minutearr.push(Number(dayjs().hour(h).minute(0).tz(timezone_value).format('mm')));
} else {
hourarr.includes(Number(dayjs().hour(h).minute(0).tz(timezone_value).format('HH'))) ? null : hourarr.push(Number(dayjs().hour(h).minute(0).tz(timezone_value).format('HH')));
}
}
});
}
setTzHours(hourarr);
setTzMinutes(minutearr);
setInternalError(false);
onError && onError(undefined);
setTzError('');
} catch (err) {
setTzError(err.message);
onError && onError({
type: 'invalid_cron',
description: err.message
});
setInternalError(true);
}
} else {
setTzError('');
setInternalError(false);
onError && onError(undefined);
setTzHours([]);
setTzMinutes([]);
}
}, [hours, minutes, timezone_value, convertToTimezone]);
return React.createElement(React.Fragment, null, React.createElement(SimpleGrid, {
breakpoints: [{
maxWidth: 980,
cols: 2,
spacing: 'md'
}, {
maxWidth: 755,
cols: 2,
spacing: 'sm'
}, {
maxWidth: 600,
cols: 1,
spacing: 'sm'
}],
mb: 10,
cols: 2
}, ' ', allowedDropdowns.includes('period') && React.createElement(Period, {
value: periodForRender,
setValue: setPeriod,
locale: locale,
className: className,
disabled: disabled,
readOnly: readOnly,
shortcuts: shortcuts,
allowedPeriods: allowedPeriods
}), periodForRender === 'reboot' ? clearButtonNode : React.createElement(React.Fragment, null, periodForRender === 'year' && allowedDropdowns.includes('months') && React.createElement(Months, {
value: months,
setValue: setMonths,
locale: locale,
className: className,
humanizeLabels: humanizeLabels,
disabled: disabled,
readOnly: readOnly,
period: periodForRender,
periodicityOnDoubleClick: periodicityOnDoubleClick,
mode: mode
}), (periodForRender === 'year' || periodForRender === 'month') && allowedDropdowns.includes('month-days') && React.createElement(MonthDays, {
value: monthDays,
setValue: setMonthDays,
locale: locale,
className: className,
weekDays: weekDays,
disabled: disabled,
readOnly: readOnly,
leadingZero: leadingZero,
period: periodForRender,
periodicityOnDoubleClick: periodicityOnDoubleClick,
mode: mode
}), (periodForRender === 'year' || periodForRender === 'month' || periodForRender === 'week') && allowedDropdowns.includes('week-days') && React.createElement(WeekDays, {
value: weekDays,
setValue: setWeekDays,
locale: locale,
className: className,
humanizeLabels: humanizeLabels,
monthDays: monthDays,
disabled: disabled,
readOnly: readOnly,
period: periodForRender,
periodicityOnDoubleClick: periodicityOnDoubleClick,
mode: mode
}), periodForRender !== 'minute' && periodForRender !== 'hour' && allowedDropdowns.includes('hours') && React.createElement(Hours, {
value: hours,
setValue: setHours,
locale: locale,
className: className,
disabled: disabled,
readOnly: readOnly,
leadingZero: leadingZero,
clockFormat: clockFormat,
period: periodForRender,
periodicityOnDoubleClick: periodicityOnDoubleClick,
mode: mode
}), periodForRender !== 'minute' && allowedDropdowns.includes('minutes') && React.createElement(Minutes, {
value: minutes,
setValue: setMinutes,
locale: locale,
period: periodForRender,
className: className,
disabled: disabled,
readOnly: readOnly,
leadingZero: leadingZero,
clockFormat: clockFormat,
periodicityOnDoubleClick: periodicityOnDoubleClick,
mode: mode
}))), React.createElement("div", {
style: {
float: 'right'
}
}, clearButtonNode));
}