@instawork/design-system
Version:
The design system for Instawork's web apps
183 lines • 7.04 kB
JavaScript
import { DateTime } from 'luxon';
import { DateUtil, hasFlag } from '../common';
import { NestedInputComponent } from '../nested-input';
const ATTR_BASE_DATE = 'base-date';
const ATTR_TIME_ZONE = 'time-zone';
const DATA_LUXON_VALUE = 'luxon-value';
const DATA_LUXON_ORIGINAL_VALUE = 'luxon-original-value';
const VALIDITY_INVALID_DATETIME = 'invalidDateTimeInput';
export var SyncValueAttribute;
(function (SyncValueAttribute) {
SyncValueAttribute[SyncValueAttribute["none"] = 1] = "none";
SyncValueAttribute[SyncValueAttribute["host"] = 2] = "host";
SyncValueAttribute[SyncValueAttribute["value"] = 4] = "value";
SyncValueAttribute[SyncValueAttribute["hostAndValue"] = 6] = "hostAndValue";
})(SyncValueAttribute || (SyncValueAttribute = {}));
export class TemporalComponent extends NestedInputComponent {
/**
* Used to optionally set the base date used when constructing a new date/time value from the selected time.
*/
get baseDate() {
return this.$el.attr(ATTR_BASE_DATE);
}
get timeZone() {
return this.$el.attr(ATTR_TIME_ZONE);
}
get isValid() {
var _a;
return ((_a = this.valueEl.validity) === null || _a === void 0 ? void 0 : _a.valid) || false;
}
/**
* Returns the Luxon {@link DateTime} object representation of the input's value.
*/
get luxonValue() {
return this.$el.data(DATA_LUXON_VALUE);
}
get luxonOriginalValue() {
return this.$el.data(DATA_LUXON_ORIGINAL_VALUE);
}
get isModified() {
var _a, _b;
return ((_a = this.luxonOriginalValue) === null || _a === void 0 ? void 0 : _a.valueOf()) !== ((_b = this.luxonValue) === null || _b === void 0 ? void 0 : _b.valueOf());
}
get value() {
return this.$value.val();
}
set value(value) {
this.importValue(value);
}
/**
* Determines which elements get their "value" attribute updated when the value changes
*/
get syncValueAttr() { return SyncValueAttribute.hostAndValue; }
;
// note: this is required to ensure that inheritance works correctly
constructor($el) {
super($el);
}
/**
* Returns a {@link DateTime} parsed from the provided input. Returns `undefined` if the input is a `null`,
* `undefined`, or empty string value.
*
* @param input - A string representation of a date/time
*/
parseValue(input) {
if (!input) {
return undefined;
}
return DateUtil.tryParseDateTime(input, {
baseDate: this.getBaseDate(),
timeZone: this.timeZone,
});
}
/**
* Attempts to parse the specified input into a {@link DateTime} value and set it as the value for the component.
*/
importValue(input) {
const value = this.parseValue(input);
return this.syncValue(value);
}
/**
* Convenience method for handling all logic around value updates: storing the parsed {@link DateTime} value, setting
* DOM attribute and value properties, emitting change events, validation
*/
syncValue(value) {
const prevValue = this.luxonValue;
const isChanged = this.isValueChanged(prevValue, value);
if (isChanged) {
// called before updateValidityState so that the value can be displayed correctly, even though if it is invalid
// invalid can be a valid date/time, but not valid for the input due to validation constraints (see min/max/step
// in TimeSelectorComponent)
this.setComponentValue(value);
value = this.updateValidityState(value);
if (value === null || value === void 0 ? void 0 : value.isValid) {
this.valueEl.setCustomValidity('');
}
// called after updateValidityState so that invalid value is exposed via the luxonValue property
this.setLuxonValue(value);
}
this.onValue(value, isChanged, this.isValid);
return value;
}
/**
* Stores the {@link DateTime} representation of the value for access by {@link luxonValue}
*/
setLuxonValue(value) {
this.$el.data(DATA_LUXON_VALUE, value);
}
/**
* Stores the {@link DateTime} representation of the original value for access by {@link luxonOriginalValue}
*/
setLuxonOriginalValue(value) {
this.$el.data(DATA_LUXON_ORIGINAL_VALUE, value);
}
initValue() {
super.initValue();
this.importValue(this.originalValue);
if (this.luxonValue) {
this.setLuxonOriginalValue(this.luxonValue);
}
}
getBaseDate() {
const baseDateSrc = this.baseDate || this.originalValue;
const baseDate = baseDateSrc ? DateUtil.tryParseDateTime(baseDateSrc) : DateTime.local();
if (this.timeZone) {
return baseDate.setZone(this.timeZone);
}
return baseDate;
}
isValueChanged(prev, next) {
const changedFromFalsy = !prev && !!next;
const changedToFalsy = !!prev && !next;
const valueChanged = !!prev && !!next && !prev.equals(next);
return changedFromFalsy || changedToFalsy || valueChanged;
}
setComponentValue(value) {
const formattedValue = this.formatValue(value);
const syncValueEl = hasFlag(this.syncValueAttr, SyncValueAttribute.value);
if (syncValueEl) {
this.$value.val(formattedValue); // for the backing hidden form input value
}
const hostAndValueAreDifferentElements = this.valueEl !== this.el;
const syncHostEl = hasFlag(this.syncValueAttr, SyncValueAttribute.host);
if (syncHostEl && (hostAndValueAreDifferentElements || !syncValueEl)) {
this.$el.attr('value', formattedValue); // so the attribute updates in the DOM
}
}
formatValue(value) {
if (!(value === null || value === void 0 ? void 0 : value.isValid)) {
return '';
}
return value.toFormat(DateUtil.DATE_FORMAT_PYTHON_AWARE);
}
updateValidityState(value) {
if (!value) {
if (this.required) {
this.valueEl.setCustomValidity('required');
}
return value;
}
if (!value.isValid) {
this.valueEl.setCustomValidity(VALIDITY_INVALID_DATETIME);
return value;
}
return value;
}
/**
* Handles UI side effects of value updates, such as validation display and change events
*/
onValue(value, isChanged, isValid) {
this.syncPlaceholder(this.disabled);
this.$el.toggleClass('is-invalid', !isValid);
if (isChanged) {
this.$el.trigger('change');
}
}
invalidDateTimeAttributeError(attrName, value) {
if (value) {
return this.attributeInvalidError(attrName, value.invalidExplanation, value);
}
return this.requiredAttributeError(attrName);
}
}
//# sourceMappingURL=temporal-component.js.map