react-native-form-model
Version:
An easily testable and opinionated React Native form model builder written in pure JavaScript.
1,271 lines (1,247 loc) • 158 kB
JavaScript
import React, { Component } from 'react';
import { View, TouchableOpacity, StyleSheet, Platform, TextInput, InteractionManager, ScrollView } from 'react-native';
import { useTheme, Text, ActivityIndicator, TouchableRipple, Caption, withTheme, Surface, Portal, Modal, Divider as Divider$1, Colors, Switch, IconButton, ToggleButton } from 'react-native-paper';
import _, { isNaN as isNaN$1 } from 'lodash';
import moment from 'moment';
import { LocaleConfig, Calendar } from 'react-native-calendars';
import semverCompare from 'semver-compare';
import i18n from 'i18n-js';
import { Subject, bindCallback, BehaviorSubject, Observable, of, combineLatest as combineLatest$1 } from 'rxjs';
import { finalize, combineLatest, map, shareReplay, take, distinctUntilChanged, skip } from 'rxjs/operators';
import { WeakRef } from '@ungap/weakrefs';
import { Picker as Picker$1 } from '@react-native-picker/picker';
import { parseTimeOfDay } from '@diatche/parse-time';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise */
var extendStatics = function(d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
function __extends(d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __rest(s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
}
function __generator(thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
}
function __values(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
}
function __read(o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
}
function __spreadArray(to, from) {
for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
to[j] = from[i];
return to;
}
var Button = function (_a) {
var _b = _a.title, title = _b === void 0 ? '' : _b, icon = _a.icon, _c = _a.mode, mode = _c === void 0 ? 'text' : _c, _d = _a.compact, compact = _d === void 0 ? false : _d, style = _a.style, contentContainerStyle = _a.contentContainerStyle, textStyle = _a.textStyle, numberOfLines = _a.numberOfLines, disabled = _a.disabled, _e = _a.loading, loading = _e === void 0 ? false : _e, color = _a.color, _f = _a.activityIndicatorSize, activityIndicatorSize = _f === void 0 ? 20 : _f, otherProps = __rest(_a, ["title", "icon", "mode", "compact", "style", "contentContainerStyle", "textStyle", "numberOfLines", "disabled", "loading", "color", "activityIndicatorSize"]);
var theme = useTheme();
var primaryColor = color || theme.colors.primary;
var stateColor = disabled ? theme.colors.disabled : primaryColor;
var containerStyle = {
borderRadius: theme.roundness,
};
var accessoryStyle = undefined;
if (compact) {
if (typeof numberOfLines === 'undefined') {
numberOfLines = 1;
}
}
else {
accessoryStyle = styles$c.nonCompactAccessory;
}
switch (mode) {
case 'contained':
containerStyle.backgroundColor = stateColor;
stateColor = theme.colors.background;
break;
case 'outline':
containerStyle.borderColor = stateColor;
containerStyle.borderWidth = 1;
break;
}
var titleProps = {
selectable: false,
color: stateColor,
style: [styles$c.buttonText, { color: stateColor }, textStyle],
adjustsFontSizeToFit: true,
minimumFontScale: 0.6,
numberOfLines: numberOfLines,
};
var titleView = typeof title === 'string' ? (React.createElement(Text, __assign({}, titleProps), title || '')) : (title(titleProps));
var activity = function () { return (React.createElement(ActivityIndicator, { hidesWhenStopped: true, animating: loading, size: activityIndicatorSize, style: [styles$c.accessory, accessoryStyle], color: primaryColor })); };
var buttonContent = null;
if (compact) {
buttonContent = (React.createElement(View, { style: [styles$c.compactInnerContainer, contentContainerStyle] }, loading
? activity()
: icon
? icon({ color: stateColor })
: titleView));
}
else {
buttonContent = (React.createElement(View, { style: [styles$c.innerContainer, contentContainerStyle] },
React.createElement(View, { style: [styles$c.accessory, accessoryStyle] }),
React.createElement(Flex, null),
icon ? icon({ color: stateColor }) : null,
titleView,
React.createElement(Flex, null),
activity()));
}
return (React.createElement(TouchableOpacity, __assign({}, otherProps, { style: [
compact ? styles$c.compactContainer : styles$c.container,
containerStyle,
style,
], disabled: !!disabled }), buttonContent));
};
var Flex = function () { return React.createElement(View, { style: styles$c.flex }); };
var styles$c = StyleSheet.create({
container: {
paddingHorizontal: 8,
overflow: 'hidden',
},
compactContainer: {
paddingHorizontal: 8,
marginVertical: 4,
overflow: 'hidden',
},
accessory: {
height: '100%',
alignItems: 'center',
justifyContent: 'center',
},
nonCompactAccessory: {
width: 40,
},
compactInnerContainer: {
flexShrink: 1,
flexGrow: 1,
flexBasis: 40,
justifyContent: 'center',
},
innerContainer: {
flex: 1,
minHeight: 40,
flexDirection: 'row',
alignItems: 'center',
},
buttonText: {
flexShrink: 1,
fontSize: 16,
lineHeight: 24,
paddingHorizontal: 4,
textAlign: 'center',
},
flex: {
flex: 1,
},
});
var FormAssets = /** @class */ (function () {
function FormAssets(config) {
this.ClearIcon = config.ClearIcon;
this.CheckmarkIcon = config.CheckmarkIcon;
this.LeftArrowIcon = config.LeftArrowIcon;
this.RightArrowIcon = config.RightArrowIcon;
}
Object.defineProperty(FormAssets, "shared", {
get: function () {
if (!this._shared) {
throw new Error('You must set a shared FormAssets instance');
}
return this._shared;
},
set: function (shared) {
if (!(shared instanceof FormAssets)) {
throw new Error('Invalid FormAssets instance');
}
this._shared = shared;
},
enumerable: false,
configurable: true
});
return FormAssets;
}());
var isIOS$1 = Platform.OS === 'ios';
function ClearButton(_a) {
var color = _a.color, disabled = _a.disabled, onPress = _a.onPress;
var theme = useTheme();
color = color || theme.form.colors.input;
var rippleColor = color;
if (rippleColor) {
rippleColor += '60';
}
React.useEffect(function () {
if (!FormAssets.shared.ClearIcon) {
console.warn('Missing FormAssets.shared.ClearIcon');
}
}, []);
return (React.createElement(TouchableRipple, { disabled: disabled, style: [
styles$b.ripple,
{
borderRadius: theme.roundness,
},
], rippleColor: rippleColor, underlayColor: rippleColor, onPress: onPress }, FormAssets.shared.ClearIcon && (React.createElement(FormAssets.shared.ClearIcon, { size: 22, color: color }))));
}
function shouldShowClearButtonOnTextField(clearButtonMode, editing) {
return isIOS$1 || clearButtonMode === 'never'
? false
: clearButtonMode === 'always' ||
(clearButtonMode === 'while-editing' && editing) ||
(clearButtonMode === 'unless-editing' && !editing);
}
var styles$b = StyleSheet.create({
ripple: {
marginLeft: 6,
overflow: 'hidden',
},
});
var kJustifyTextAlignMap = {
left: 'flex-start',
center: 'space-between',
right: 'flex-end',
};
/** @deprecated */
var LabelField = /** @class */ (function (_super) {
__extends(LabelField, _super);
function LabelField(props) {
var _this = _super.call(this, props) || this;
_this.initialValue = props.value;
_this.state = {
value: props.value,
};
return _this;
}
LabelField.prototype.componentDidUpdate = function (prevProps, prevState, snapshot) {
if (!_.isEqual(this.props.value, prevProps.value)) {
// eslint-disable-next-line react/no-did-update-set-state
this.setState({
value: this.props.value,
});
}
};
Object.defineProperty(LabelField.prototype, "isCustom", {
get: function () {
return !!this.props.custom;
},
enumerable: false,
configurable: true
});
LabelField.prototype.format = function (value) {
if (this.props.format) {
try {
return this.props.format(value);
}
catch (err) {
console.error('Error during formatting: ' + ((err === null || err === void 0 ? void 0 : err.message) || err));
return '';
}
}
else if (typeof value === 'undefined' || value === null) {
return '';
}
else {
return String(value);
}
};
LabelField.prototype.handlePress = function (event) {
var _a, _b;
(_b = (_a = this.props).onPress) === null || _b === void 0 ? void 0 : _b.call(_a, event);
};
LabelField.prototype.renderField = function (content) {
var _this = this;
var theme = this.props.theme;
var _a = this.props, onPress = _a.onPress, disabled = _a.disabled, _b = _a.style, style = _b === void 0 ? {} : _b, _c = _a.align, align = _c === void 0 ? 'left' : _c, _d = _a.rippleColor, rippleColor = _d === void 0 ? theme.colors.primary + '60' : _d;
var styles = [
{
flexDirection: 'row',
alignItems: 'center',
justifyContent: kJustifyTextAlignMap[align],
borderRadius: theme.roundness,
overflow: 'hidden',
},
style,
];
if (onPress) {
return (React.createElement(TouchableRipple, { onPress: function (event) {
_this.handlePress(event);
}, rippleColor: rippleColor, underlayColor: rippleColor, style: styles, disabled: disabled, theme: theme }, content));
}
else {
return React.createElement(View, { style: styles }, content);
}
};
LabelField.prototype.renderTitleAndValue = function () {
var _a = this.props, _b = _a.label, label = _b === void 0 ? '' : _b, _c = _a.placeholder, placeholder = _c === void 0 ? '' : _c, theme = _a.theme, textStyle = _a.textStyle;
var value = this.state.value;
var valueStr = this.format(value);
return (React.createElement(View, { style: { flex: 1 } },
!!label && (React.createElement(Caption, { selectable: false, style: [
{
lineHeight: 12,
color: theme.colors.placeholder,
},
textStyle,
] }, label)),
!!(valueStr || placeholder) && (React.createElement(Text, { selectable: false, style: [
{
flex: 1,
color: valueStr
? theme.colors.text
: theme.colors.placeholder,
},
textStyle,
], theme: theme }, valueStr || placeholder || ''))));
};
LabelField.prototype.renderContent = function () {
return this.renderTitleAndValue();
};
LabelField.prototype.renderCustom = function () {
return React.createElement(View, null);
};
LabelField.prototype.render = function () {
if (this.isCustom) {
return this.renderCustom();
}
return this.renderField(this.renderContent());
};
return LabelField;
}(React.PureComponent));
/** @deprecated */
var ControlField = /** @class */ (function (_super) {
__extends(ControlField, _super);
function ControlField(props) {
var _this = _super.call(this, props) || this;
_this.editing = false;
_this.didChangeValue = false;
_this.onValueChange = function (_a) {
var _b, _c, _d, _e;
var value = _a.value, _f = _a.error, error = _f === void 0 ? '' : _f;
var v = _this.validation(value);
(_c = (_b = _this.props).onValueChange) === null || _c === void 0 ? void 0 : _c.call(_b, {
value: v.valid ? value : _this.state.value,
error: error,
});
(_e = (_d = _this.props).onValidation) === null || _e === void 0 ? void 0 : _e.call(_d, v);
};
var state = { value: props.value };
_this.state = __assign(__assign({}, _this.setValueMutation(state)), _this.encodeMutation(state));
return _this;
}
ControlField.prototype.componentDidMount = function () {
this.validate();
};
ControlField.prototype.getSnapshotBeforeUpdate = function (prevProps, prevState) {
if (!this.editing &&
!_.isEqual(this.props.value, prevProps.value) &&
!_.isEqual(this.props.value, this.state.value)) {
return { value: this.props.value };
}
return null;
};
ControlField.prototype.componentDidUpdate = function (prevProps, prevState, snapshot) {
if (snapshot) {
this.setValue(snapshot.value);
}
};
ControlField.prototype.setValue = function (value) {
var state = { value: value, parsable: true };
state = __assign(__assign({}, this.setValueMutation(state)), this.encodeMutation(state));
this.setState(state);
};
ControlField.prototype.submit = function () {
if (this.state.parsable) {
this.setState(this.encodeMutation(this.state));
}
};
ControlField.prototype.reset = function () {
this.setValue(this.props.value);
};
ControlField.prototype.validate = function () {
var _a, _b;
var v = this.validation(this.state.value);
(_b = (_a = this.props).onValidation) === null || _b === void 0 ? void 0 : _b.call(_a, v);
return v;
};
ControlField.prototype.parse = function (userInput) {
var parse = this.props.parse;
if (!parse) {
return { value: userInput, parsable: true };
}
var data = {};
try {
data = parse(userInput);
}
catch (err) {
data = { value: undefined, error: err, parsable: false };
}
if (!('value' in data)) {
throw new Error('Expected an object with the value, instead got: ' + data);
}
return __assign(__assign({}, data), { parsable: !data.error });
};
ControlField.prototype.validation = function (value) {
var _a, _b;
return ((_b = (_a = this.props).validate) === null || _b === void 0 ? void 0 : _b.call(_a, value)) || { valid: true };
};
ControlField.prototype.handleFocus = function (event) {
var _a, _b;
if (this.editing) {
return;
}
this.editing = true;
this.didChangeValue = false;
(_b = (_a = this.props).onFocus) === null || _b === void 0 ? void 0 : _b.call(_a, event);
};
ControlField.prototype.handleBlur = function (event) {
var _a, _b;
if (!this.editing) {
return;
}
this.editing = false;
if (this.didChangeValue) {
this.didChangeValue = false;
this.handleUserInput(this.state);
}
this.submit();
(_b = (_a = this.props).onBlur) === null || _b === void 0 ? void 0 : _b.call(_a, event);
};
ControlField.prototype.handleSubmit = function (event) {
var _a, _b;
(_b = (_a = this.props).onSubmit) === null || _b === void 0 ? void 0 : _b.call(_a, event);
};
ControlField.prototype.handleUserInput = function (_a) {
var userInput = _a.userInput;
var data = this.parse(userInput);
var newState = __assign(__assign({}, this.setValueMutation(data)), { userInput: userInput });
this.setState(newState);
this.onValueChange(newState);
return newState;
};
ControlField.prototype.renderContent = function () {
return [this.renderTitleAndValue(), this.renderControl()];
};
ControlField.prototype.renderControl = function () {
return React.createElement(View, { key: 'control' });
};
ControlField.prototype.setValueMutation = function (newState) {
var _a = newState.parsable, parsable = _a === void 0 ? !newState.error : _a;
var validation = this.validationMutation(newState);
var mutation = __assign(__assign({}, newState), { valid: !newState.error && validation.valid, error: newState.error || validation.error, parsable: parsable });
return mutation;
};
ControlField.prototype.validationMutation = function (newState) {
if (newState.error) {
return {
error: newState.error,
valid: false,
};
}
var v = this.validation('value' in newState ? newState.value : this.state.value);
return {
error: v.error,
valid: v.valid,
};
};
ControlField.prototype.encodeMutation = function (_a) {
var _this = this;
var value = _a.value;
var _b = this.props.encode, encode = _b === void 0 ? function (value) { return _this.format(value); } : _b;
return {
userInput: encode(value),
};
};
return ControlField;
}(LabelField));
function lz(key, options) {
return i18n.t(key, options);
}
function getCurrentLocale() {
return i18n.locale;
}
var locale = getCurrentLocale();
// TODO: iOS 14 date picker ignores textColor, so it is not usable in dark mode. See [issue](https://trello.com/c/L5uiBiTw)
var iOSDisplay = 'spinner';
/**
* Default picker behaviour was changed in iOS 14.
*
* In iOS 14, the picker is both the field, which
* automatically manages the picker modal.
*
* Prior to iOS 14, the picker view was separate
* from the picker field and was managed separately.
*/
var isIOS14Like = Platform.OS === 'ios' &&
iOSDisplay !== 'spinner' &&
semverCompare(Platform.constants.osVersion, '14.0') >= 0;
LocaleConfig.locales[locale] = {
monthNames: moment.months(),
monthNamesShort: moment.monthsShort(),
dayNames: moment.weekdays(),
dayNamesShort: moment.weekdaysShort(),
today: lz('today'),
};
LocaleConfig.defaultLocale = locale;
// Choose internal date picker
var NativeDatePicker;
switch (Platform.OS) {
case 'ios':
case 'android': {
NativeDatePicker =
require('@react-native-community/datetimepicker').default;
break;
}
default: {
console.warn('No native date picker, using calendar.');
break;
}
}
/** Cross-platform date picker wrapper. */
var DatePicker = /** @class */ (function (_super) {
__extends(DatePicker, _super);
function DatePicker(props) {
var _this = _super.call(this, props) || this;
_this.state = {
date: _this.cleanDate(props.value || moment()),
};
return _this;
}
/**
* Whether the modal is managed automatically and
* the view should be displayed as a field.
*/
DatePicker.isIntegrated = function (options) {
return isIOS14Like;
};
DatePicker.prototype.componentDidMount = function () {
if (!FormAssets.shared.LeftArrowIcon) {
console.warn('Missing FormAssets.shared.LeftArrowIcon');
}
if (!FormAssets.shared.RightArrowIcon) {
console.warn('Missing FormAssets.shared.RightArrowIcon');
}
};
DatePicker.prototype.getSnapshotBeforeUpdate = function () {
return { visible: this.props.visible };
};
DatePicker.prototype.componentDidUpdate = function (prevProps) {
// Set selected date value to specified date value
// when becoming visible.
if (this.props.visible && !prevProps.visible) {
this._setDate(this.cleanDate(this.props.value || moment()), {
willShow: true,
});
}
};
DatePicker.prototype._setDate = function (date, options) {
if (Platform.OS === 'android' && !(options === null || options === void 0 ? void 0 : options.willShow)) {
// Modal handles state. Changing the date before hiding the modal
// makes the modal reappear a second time after being dismissed. See issue:
// https://github.com/react-native-datetimepicker/datetimepicker/issues/54
return;
}
this.setState({ date: date });
};
DatePicker.prototype.onChange = function (value) {
var cleanDate = this.cleanDate(value);
var newState = { date: cleanDate };
this._setDate(newState.date);
this.props.onChange && this.props.onChange(cleanDate);
if (DatePicker.submitOnChangeSupported && this.props.submitOnChange) {
this.onSubmit(newState);
}
return newState;
};
DatePicker.prototype.onSubmit = function (newState) {
var date = (newState && newState.date) || this.state.date;
this.props.onSubmit(date);
};
DatePicker.prototype.onCancel = function () {
this.props.onCancel();
};
DatePicker.prototype.handleAndroidModalEvent = function (event, date) {
event = __assign({ type: 'dismissed', nativeEvent: {} }, (event || {}));
switch (event.type) {
case 'set': {
var newState = __assign(__assign({}, this.state), this.onChange(moment(date)));
this.onSubmit(newState);
break;
}
case 'dismissed':
this.onCancel();
break;
default:
console.warn('Received unknown DatePicker event:', event);
}
};
DatePicker.prototype.render = function () {
var _this = this;
var _a = this.props, visible = _a.visible, theme = _a.theme;
if (Platform.OS === 'android') {
return this.renderAndroidPickerModal();
}
var modalStyle = {
borderRadius: theme.roundness,
overflow: 'hidden',
};
if (DatePicker.isIntegrated({ mode: this.props.mode })) {
return Platform.OS === 'ios'
? this.renderIOSPickerView()
: this.renderFallbackPickerView();
}
var modalContent = Platform.OS === 'ios' ? (React.createElement(Surface, { style: [styles$a.iosModalContainer, modalStyle] },
React.createElement(View, { style: styles$a.iosDatePickerContainer }, this.renderIOSPickerView()),
this.renderButtons())) : (React.createElement(Surface, { style: [styles$a.modalContainer, modalStyle] },
this.renderFallbackPickerView(),
this.renderButtons()));
return (React.createElement(Portal, { theme: theme },
React.createElement(Modal, { visible: visible, onDismiss: function () { return _this.onCancel(); }, contentContainerStyle: this.modalContentStyle(), theme: theme }, modalContent)));
};
DatePicker.prototype.renderAndroidPickerModal = function () {
var _this = this;
var _a = this.props, futureDisabled = _a.futureDisabled, mode = _a.mode, visible = _a.visible;
var date = this.state.date;
return (visible && (React.createElement(NativeDatePicker, { value: date.toDate(), display: 'default', disabled: this.props.disabled, mode: mode, onChange: function (event, date) {
return _this.handleAndroidModalEvent(event, date);
}, maximumDate: futureDisabled && this.maximumDate().toDate() })));
};
DatePicker.prototype.renderIOSPickerView = function (props) {
var _this = this;
var _a = this.props, futureDisabled = _a.futureDisabled, mode = _a.mode, theme = _a.theme;
var date = this.state.date;
return (React.createElement(NativeDatePicker, __assign({}, props, { value: date.toDate(), display: iOSDisplay, disabled: this.props.disabled, mode: mode, onChange: function (event, date) { return _this.onChange(date); }, maximumDate: futureDisabled && this.maximumDate().toDate(), textColor: theme.colors.text, locale: getCurrentLocale() })));
};
DatePicker.prototype.renderFallbackPickerView = function () {
switch (this.props.mode) {
case 'date':
return this.renderCalendar();
default:
throw new Error("Unsupported fallback date picker mode: " + this.props.mode);
}
};
DatePicker.prototype.renderCalendar = function () {
var _a;
var _this = this;
var _b = this.props, futureDisabled = _b.futureDisabled, theme = _b.theme;
var calendarValue = this.calendarDate(this.state.date);
return (React.createElement(Calendar, { current: calendarValue, markedDates: (_a = {},
_a[calendarValue] = { selected: true },
_a), onDayPress: function (day) { return _this.onChange(moment(day.timestamp)); }, onDayLongPress: function (day) {
return _this.onChange(moment(day.timestamp));
}, maxDate: futureDisabled ? this.calendarDate(this.today()) : undefined, renderArrow: function (direction) { return _this.renderArrow(direction); }, style: styles$a.calendar, theme: {
backgroundColor: theme.colors.background,
calendarBackground: theme.colors.surface,
textSectionTitleColor: theme.colors.text,
selectedDayBackgroundColor: theme.colors.accent,
selectedDayTextColor: theme.colors.surface,
todayTextColor: theme.colors.accent,
dayTextColor: theme.colors.text,
textDisabledColor: theme.colors.disabled,
dotColor: theme.colors.primary,
selectedDotColor: theme.colors.surface,
arrowColor: theme.colors.accent,
// disabledArrowColor: theme.colors.disabled,
monthTextColor: theme.colors.text,
indicatorColor: theme.colors.primary,
// textDayFontFamily: 'monospace',
// textMonthFontFamily: 'monospace',
// textDayHeaderFontFamily: 'monospace',
// textDayFontWeight: '300',
textMonthFontWeight: 'medium',
// textDayHeaderFontWeight: '300',
// textDayFontSize: 16,
// textMonthFontSize: 16,
// textDayHeaderFontSize: 16
} }));
};
DatePicker.prototype.renderButtons = function () {
var _this = this;
if (DatePicker.submitOnChangeSupported && this.props.submitOnChange) {
return null;
}
return (React.createElement(View, { style: styles$a.calendarButtons },
this.props.mode === 'date' && (React.createElement(Button, { title: lz('today'), onPress: function () { return _this._setDate(_this.today()); }, compact: true, color: this.props.theme.colors.accent, style: styles$a.calendarButton })),
React.createElement(Button, { title: lz('cancel'), onPress: function () { return _this.onCancel(); }, compact: true, style: styles$a.calendarButton }),
React.createElement(Button, { title: lz('done'), mode: 'contained', onPress: function () { return _this.onSubmit(); }, compact: true, style: styles$a.calendarButton })));
};
DatePicker.prototype.renderArrow = function (direction) {
var size = 18;
var color = this.props.theme.colors.accent;
switch (direction) {
case 'left':
return (FormAssets.shared.LeftArrowIcon && (React.createElement(FormAssets.shared.LeftArrowIcon, { size: size, color: color })));
case 'right':
return (FormAssets.shared.RightArrowIcon && (React.createElement(FormAssets.shared.RightArrowIcon, { size: size, color: color })));
default:
throw new Error("Bad arrow direction: " + direction);
}
};
DatePicker.prototype.today = function () {
return this.cleanDate(moment());
};
DatePicker.prototype.maximumDate = function () {
switch (this.props.mode) {
case 'date':
return this.today();
case 'time':
return this.cleanDate(moment().add(1, 'minute'));
default:
throw new Error("Unsupported date picker mode: " + this.props.mode);
}
};
DatePicker.prototype.cleanDate = function (date) {
switch (this.props.mode) {
case 'date':
return moment(date).clone().startOf('day');
case 'time':
return moment(date).clone().startOf('minute');
default:
throw new Error("Unsupported date picker mode: " + this.props.mode);
}
};
DatePicker.prototype.calendarDate = function (date) {
return moment(date).format('YYYY-MM-DD');
};
DatePicker.prototype.modalContentStyle = function () {
var theme = this.props.theme;
return [
styles$a.modal,
{
backgroundColor: theme.colors.surface,
borderRadius: theme.roundness,
},
];
};
DatePicker.submitOnChangeSupported = Platform.OS === 'web' || isIOS14Like;
DatePicker.isTimePickerSupported = (function () {
switch (Platform.OS) {
case 'ios':
case 'android':
return true;
default: {
return false;
}
}
})();
return DatePicker;
}(Component));
var styles$a = StyleSheet.create({
modal: {
maxWidth: 400,
alignSelf: 'center',
},
iosModalContainer: isIOS14Like
? {
justifyContent: 'space-between',
padding: 8,
}
: {
justifyContent: 'space-between',
paddingBottom: 8,
},
iosDatePickerContainer: isIOS14Like
? {
height: 300,
width: 320,
}
: {
height: 216,
width: 320,
},
modalContainer: {
flex: 1,
},
calendar: {
minWidth: 320,
height: 360,
},
calendarButtons: {
flex: 1,
flexDirection: 'row',
marginRight: 12,
maxHeight: 45,
},
calendarButton: {
flex: 1,
marginLeft: 12,
},
});
var DatePicker$1 = withTheme(DatePicker);
var Divider = React.memo(function (_a) {
var style = _a.style, props = __rest(_a, ["style"]);
var theme = useTheme();
return (React.createElement(Divider$1, __assign({}, props, { style: [{ backgroundColor: theme.form.colors.divider }, style] })));
});
var isWeb$1 = Platform.OS === 'web';
var kOmitTextInputProps = [
'value',
'parse',
'validate',
'format',
'onChangeText',
'onSubmitEditing',
'onEndEditing',
'onBlur',
'onFocus',
'onValueChange',
'onValidation',
];
/** @deprecated */
var TextInputField = /** @class */ (function (_super) {
__extends(TextInputField, _super);
function TextInputField() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.textInputRef = React.createRef();
return _this;
}
Object.defineProperty(TextInputField.prototype, "isCustom", {
get: function () {
return true;
},
enumerable: false,
configurable: true
});
TextInputField.prototype.focus = function () {
var _a;
(_a = this.textInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
};
TextInputField.prototype.blur = function () {
var _a;
(_a = this.textInputRef.current) === null || _a === void 0 ? void 0 : _a.blur();
};
TextInputField.prototype.renderCustom = function () {
var _this = this;
var _a = this.props, theme = _a.theme, _b = _a.secure, secure = _b === void 0 ? false : _b, _c = _a.clearTextOnFocus, clearTextOnFocus = _c === void 0 ? false : _c, _d = _a.style, style = _d === void 0 ? {} : _d, _e = _a.textStyle, textStyle = _e === void 0 ? {} : _e, formStyle = _a.formStyle, _f = _a.multiline, multiline = _f === void 0 ? false : _f, _g = _a.mode, mode = _g === void 0 ? 'plain' : _g;
var _h = this.state.userInput, userInput = _h === void 0 ? '' : _h;
var _j = this.props.textAlignVertical, textAlignVertical = _j === void 0 ? multiline ? 'top' : 'auto' : _j;
var modeStyle = {};
switch (mode) {
case 'plain':
break;
case 'contained':
modeStyle = getContainedTextFieldStyle(formStyle);
break;
default:
console.warn('Unrecognized TextInputField mode:', mode);
}
var forwardProps = _.omit(this.props, kOmitTextInputProps);
var commonProps = {
onChangeText: function (userInput) { return _this.handleUserInput({ userInput: userInput }); },
onSubmitEditing: function (event) {
_this.handleBlur(event);
if (!multiline) {
_this.handleSubmit(event);
}
},
onEndEditing: function (event) { return _this.handleBlur(event); },
onBlur: function (event) { return _this.handleBlur(event); },
onFocus: function (event) {
return _this.handleFocus(event);
},
clearTextOnFocus: clearTextOnFocus,
secureTextEntry: secure,
style: [
isWeb$1 ? kTextFieldWebStyle : undefined,
modeStyle,
style,
textStyle,
],
selectionColor: theme.colors.primary,
textAlignVertical: textAlignVertical,
};
var combinedProps = __assign(__assign({}, forwardProps), commonProps);
if (combinedProps.disabled) {
combinedProps.editable = false;
}
return (React.createElement(TextInput, __assign({ ref: this.textInputRef }, combinedProps, { value: userInput })));
};
return TextInputField;
}(ControlField));
var getContainedTextFieldStyle = function (style) {
return {
backgroundColor: style === null || style === void 0 ? void 0 : style.colors.containedTextBackground,
borderWidth: style === null || style === void 0 ? void 0 : style.containedTextBorderWidth,
borderColor: style === null || style === void 0 ? void 0 : style.colors.containedTextBorder,
};
};
var kTextFieldWebStyle = {
/** Remove browser specific styling. */
appearance: 'none',
borderRadius: 0,
};
var FloatInputField = /** @class */ (function (_super) {
__extends(FloatInputField, _super);
function FloatInputField() {
return _super !== null && _super.apply(this, arguments) || this;
}
FloatInputField.prototype.parse = function (userInput) {
if (!FloatInputField.INPUT_REGEX.test(userInput)) {
return {
value: undefined,
error: new Error(),
};
}
var value = Number.parseFloat(userInput);
return {
value: value,
error: typeof value !== 'number' || Number.isNaN(value)
? new Error(lz('invalidValue'))
: undefined,
};
};
FloatInputField.prototype.validate = function (value) {
if (!_.isNumber(value) || !Number.isFinite(value)) {
return {
valid: false,
error: new Error(lz('invalidValue')),
};
}
if (typeof this.props.gte === 'number' && value < this.props.gte) {
return {
valid: false,
error: new Error(lz('valueMustBeGTE', {
value: this.props.gte,
})),
};
}
if (typeof this.props.gt === 'number' && value <= this.props.gt) {
return {
valid: false,
error: new Error(lz('valueMustBeGT', {
value: this.props.gt,
})),
};
}
if (typeof this.props.lte === 'number' && value > this.props.lte) {
return {
valid: false,
error: new Error(lz('valueMustBeLTE', {
value: this.props.lte,
})),
};
}
if (typeof this.props.lt === 'number' && value >= this.props.lt) {
return {
valid: false,
error: new Error(lz('valueMustBeLT', {
value: this.props.lt,
})),
};
}
return { valid: true };
};
FloatInputField.prototype.format = function (value) {
if (typeof value !== 'number')
return '';
if (Number.isNaN(value))
return '';
return value.toString();
};
FloatInputField.prototype.render = function () {
var _this = this;
return (React.createElement(TextInputField, __assign({}, this.props, { parse: function (args) { return _this.parse(args); }, validate: function (args) { return _this.validate(args); }, format: function (args) { return _this.format(args); }, keyboardType: 'numeric' })));
};
FloatInputField.INPUT_REGEX = /[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)/;
return FloatInputField;
}(Component));
var FloatInputField$1 = withTheme(FloatInputField);
/**
* Returns a list of typed keys of the object.
*
* Explicitly set the type `Keys` to ensure type safety works.
* If you have an interface, use `safeKeyList<keyof YourInterface>({ ...keys })`.
*
* Using a typed object ensures type safety.
* Any changes to keys are caught by static type analysis.
*
* @param input An object with all the keys of `Keys`. The values do not matter.
*/
function safeKeyList(input) {
return Object.keys(input);
}
var kDateUnitsAsc = safeKeyList({
millisecond: 1,
second: 1,
minute: 1,
hour: 1,
day: 1,
month: 1,
year: 1,
});
var kDateUnitsDes = kDateUnitsAsc
.slice()
.reverse();
var isDateUnit = function (unit) {
return kDateUnitsAsc.indexOf(unit) >= 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
*/
var significantTimeChanges = function (options) {
if (options === void 0) { options = {
significantUnit: 'day',
}; }
var significantUnit = options.significantUnit;
if (!moment.normalizeUnits(significantUnit)) {
throw new Error("Invalid date unit: " + significantUnit);
}
var periodTimer;
var stream = new Subject();
var waitForNext = function () {
var now = moment();
var periodEnd = now
.clone()
.startOf(significantUnit)
.add(1, significantUnit);
var msLeft = periodEnd.valueOf() - now.valueOf();
periodTimer = setTimeout(function () {
periodTimer = 0;
stream.next(periodEnd);
waitForNext();
}, msLeft);
};
waitForNext();
var cleanup = function () {
periodTimer && clearTimeout(periodTimer);
periodTimer = 0;
waitForNext = undefined;
};
return stream.pipe(finalize(function () { return cleanup(); }));
};
var destructureDuration = function (duration) {
var e_1, _a;
var dateUnit;
var unitValue = 0;
try {
for (var kDateUnitsDes_1 = __values(kDateUnitsDes), kDateUnitsDes_1_1 = kDateUnitsDes_1.next(); !kDateUnitsDes_1_1.done; kDateUnitsDes_1_1 = kDateUnitsDes_1.next()) {
var calUnit = kDateUnitsDes_1_1.value;
var 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");
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (kDateUnitsDes_1_1 && !kDateUnitsDes_1_1.done && (_a = kDateUnitsDes_1.return)) _a.call(kDateUnitsDes_1);
}
finally { if (e_1) throw e_1.error; }
}
return [unitValue, dateUnit || 'millisecond'];
};
/**
* Returns the component layout.
*
* Usage:
* ```
* const Component = () => {
* const [layout, onLayout] = useLayout();
* return <View onLayout={onLayout} />;
* };
* ```
*
* Source: https://stackoverflow.com/a/57792001/328356
*/
var useLayout = function (options) {
var layoutState;
var layoutRef = React.useRef();
if (options === null || options === void 0 ? void 0 : options.updateOnChange) {
// eslint-disable-next-line react-hooks/rules-of-hooks
layoutState = React.useState();
}
var onLayout = React.useCallback(function (event) {
var newLayout = event.nativeEvent.layout;
var layoutWithPrevious = __assign(__assign({}, newLayout), { previous: layoutRef.current });
if ((options === null || options === void 0 ? void 0 : options.dedupe) && _.isEqual(layoutRef.current, newLayout)) {
return;
}
if (!(options === null || options === void 0 ? void 0 : options.filter) || options.filter(layoutWithPrevious)) {
layoutRef.current = newLayout;
layoutState === null || layoutState === void 0 ? void 0 : layoutState[1](newLayout);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return [layoutRef.current, onLayout];
};
function usePrevious(value) {
var ref = React.useRef(value);
React.useEffect(function () {
ref.current = value;
}, [value]);
return ref.current;
}
function latestAfterInteractions() {
return function (input$) {
return input$.pipe(combineLatest(onInteractionsEnd()), map(function (x) { return x[0]; }));
};
// return input$ => onInteractionsEnd().pipe(flatMap(() => input$));
}
var onInteractionsEnd = function () {
return bindCallback(InteractionManager.runAfterInteractions)();
};
function animatedObservable(value) {
// TODO: Only add listener after subscription. See [task](https://trello.com/c/zt7mL5Nh)
var subject;
// @ts-ignore: _value is private
if (typeof value._value !== 'undefined') {
// @ts-ignore: _value is private
subject = new BehaviorSubject(value._value);
}
else {
subject = new Subject();
}
var animatedSub = value.addListener(function (_a) {
var value = _a.value;
subject.next(value);
});
return subject.pipe(finalize(function () {
value.removeListener(animatedSub);
}), shareReplay(1));
}
function usePromise(promise, dependencies, options) {
if (dependencies === void 0) { dependencies = []; }
var _a = __read(React.useState(promise), 2), _promise = _a[0], setPromise = _a[1];
var _b = __read(React.useState({
loading: !!_promise,
complete: false,
}), 2), res = _b[0], setRes = _b[1];
var setResAndCallback = function (result) {
var _a;
setRes(result);
if (result.complete) {
(_a = options === null || options === void 0 ? void 0 : options.onComplete) === null || _a === void 0 ? void 0 : _a.call(options, __assign({}, result));
}
};
if (typeof promise !== 'function') {
if (promise !== _promise) {
setPromise(promise);
if (promise instanceof Promise) {
setResAndCallback({ loading: !!promise, complete: false });
}
else {
setResAndCallback({
value: promise,
loading: false,
complete: true,
});
}
}
}
React.useEffect(function () {
var _a;
var active = true;
if (_promise && _promise instanceof Promise) {
(_a = _promise) === null || _a === void 0 ? void 0 : _a.then(function (value) {
return active &&
setResAndCallback({
value: value,
loading: false,
complete: true,
});
}).catch(function (error) {
return active &&
setResAndCallback({
error: error,
loading: false,
complete: true,
});
});
}
ret