react-native-form-model
Version:
An easily testable and opinionated React Native form model builder written in pure JavaScript.
103 lines (92 loc) • 2.67 kB
text/typescript
import moment, { Moment } from 'moment';
import { Observable, Subject } from 'rxjs';
import { finalize } from 'rxjs/operators';
import { safeKeyList } from './util';
export type DateUnit =
| 'millisecond'
| 'second'
| 'minute'
| 'hour'
| 'day'
| 'month'
| 'year';
export const kDateUnitsAsc = safeKeyList<DateUnit>({
millisecond: 1,
second: 1,
minute: 1,
hour: 1,
day: 1,
month: 1,
year: 1,
});
export const kDateUnitsDes: typeof kDateUnitsAsc = kDateUnitsAsc
.slice()
.reverse();
export const isDateUnit = (unit: any): unit is DateUnit => {
return kDateUnitsAsc.indexOf(unit as any) >= 0;
};
/**
* Triggers a callback on every `options.significantUnit` change
* in the local time zone.
* @param callback
* @param options.significantUnit `day` by default.
* @returns obj.cancel A cancel function
*/
export const significantTimeChanges = (
options: {
significantUnit: moment.unitOfTime.Base;
} = {
significantUnit: 'day',
}
): Observable<Moment> => {
const { significantUnit } = options;
if (!moment.normalizeUnits(significantUnit)) {
throw new Error(`Invalid date unit: ${significantUnit}`);
}
let periodTimer: any;
const stream = new Subject<Moment>();
let waitForNext = () => {
const now = moment();
const periodEnd = now
.clone()
.startOf(significantUnit)
.add(1, significantUnit);
const msLeft = periodEnd.valueOf() - now.valueOf();
periodTimer = setTimeout(() => {
periodTimer = 0;
stream.next(periodEnd);
waitForNext();
}, msLeft);
};
waitForNext();
const cleanup = () => {
periodTimer && clearTimeout(periodTimer);
periodTimer = 0;
(waitForNext as any) = undefined;
};
return stream.pipe(finalize(() => cleanup()));
};
export const destructureDuration = (
duration: moment.Duration
): [number, DateUnit] => {
let dateUnit: DateUnit | undefined;
let unitValue = 0;
for (const calUnit of kDateUnitsDes) {
const value = duration.get(calUnit);
if (value === 0 || isNaN(value)) {
continue;
}
if (isDateUnit(calUnit)) {
if (dateUnit) {
throw new Error(
`Durations with multiple units is not supported`
);
}
dateUnit = calUnit;
unitValue = value;
} else {
throw new Error(`Duration unit ${calUnit} is not supported`);
}
}
return [unitValue, dateUnit || 'millisecond'];
};