UNPKG

@instawork/design-system

Version:

The design system for Instawork's web apps

183 lines 7.04 kB
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