UNPKG

@kelvininc/ui-components

Version:
698 lines (697 loc) 33.1 kB
import { Host, h } from "@stencil/core"; import { EActionButtonType, EComponentSize, EIconName, EInputSource } from "../../types"; import dayjs from "dayjs"; import { CALENDAR_DATE_TIME_MASK, CALENDAR_INPUT_MAX_DATE, CALENDAR_INPUT_MIN_DATE, CALENDAR_MASK, DATETIME_INPUT_MASK, DATE_INPUT_MASK, DATE_INPUT_PLACEHOLDER, DEFAULT_HEADER_TITLE } from "./absolute-time-picker.config"; import { fromDateInput, fromISO, isDateBefore, isDateSame, newDate } from "../../utils/date"; import { isEmpty } from "lodash-es"; import { ERelativeTimeInputMode, EAbsoluteTimePickerMode } from "./absolute-time-picker.types"; import { buildSelectedDatesEventPayload, getFirstCalendarInitialDate, getFromDateInputState, getMaximumDateFromDayClick, getMinimumDateFromDayClick, getSecondCalendarInitialDate, getSingleDateTimeInputState, getToDateTimeInputState, isEndDateAtStartOfDay } from "./absolute-time-picker.helper"; import { DATE_FORMAT } from "../calendar/calendar.config"; export class KvAbsoluteTimePicker { constructor() { var _a; /** @inheritdoc */ this.headerTitle = DEFAULT_HEADER_TITLE; /** @inheritdoc */ this.displayBackButton = false; /** @inheritdoc */ this.mode = EAbsoluteTimePickerMode.Range; /** @inheritdoc */ this.selectedDates = []; /** @inheritdoc */ this.disabledDates = []; /** @inheritdoc */ this.calendarInputMinDate = CALENDAR_INPUT_MIN_DATE; /** @inheritdoc */ this.calendarInputMaxDate = CALENDAR_INPUT_MAX_DATE; /** Input values inserted by the user on the date and time input */ this.fromInputValue = ''; this.toInputValue = ''; this.singleInputValue = ''; /** Shared hovered date between calendars for range mode */ this.hoveredDate = ''; /** First calendar displayed month (Second calendar is displayedMonth + 1) */ this.displayedMonth = (_a = fromDateInput(this.initialDate)) !== null && _a !== void 0 ? _a : fromDateInput(new Date()); /** Used to force focus the from and to input */ this.fromInputFocused = true; this.toInputFocused = false; /** * Used to define when the input dates came from the relative time picker where the input * can be an absolute date or a text (ex: last 24 hours) */ this.inputMode = ERelativeTimeInputMode.Date; /** Calendars max and minimum dates */ this.minDate = ''; this.maxDate = ''; this.setDateLimits = (min, max) => { this.minDate = min; this.maxDate = max; }; this.setInputValues = (from, to) => { this.fromInputValue = from; this.toInputValue = to; }; this.resetInputValues = () => { this.fromInputValue = ''; this.toInputValue = ''; this.singleInputValue = ''; }; this.resetDateLimits = () => { this.minDate = ''; this.maxDate = ''; }; this.emitSelectRangeDatesChangeEvent = (dateA, dateB) => { const range = buildSelectedDatesEventPayload(dateA, dateB); this.selectedDatesChange.emit({ range }); }; this.handleBackClick = (event) => { this.backButtonClicked.emit(event); }; this.onClickDate = ({ detail }) => { var _a; const date = detail.date; const clickedDate = fromISO(date); const inputDate = newDate(date); const fromDate = getMinimumDateFromDayClick(clickedDate, this.calendarInputMinDate); const toDate = getMaximumDateFromDayClick(clickedDate, this.calendarInputMaxDate); if (this.mode === EAbsoluteTimePickerMode.Range) { const [selectedStartDate, selectedEndDate] = (_a = this.selectedDates) !== null && _a !== void 0 ? _a : []; if (!selectedStartDate) { this.displayedMonth = clickedDate.startOf('day'); this.setInputValues(fromDate.format(DATETIME_INPUT_MASK), ''); this.setDateLimits(clickedDate.format(CALENDAR_MASK), ''); this.emitSelectRangeDatesChangeEvent(fromDate); return; } if (isDateSame(clickedDate, selectedStartDate)) { if (!selectedEndDate) { this.setInputValues(fromDate.format(DATETIME_INPUT_MASK), toDate.format(DATETIME_INPUT_MASK)); this.resetDateLimits(); this.emitSelectRangeDatesChangeEvent(fromDate, toDate); return; } this.resetInputValues(); this.resetDateLimits(); this.emitSelectRangeDatesChangeEvent(); return; } if (selectedEndDate !== undefined) { this.displayedMonth = inputDate; this.setInputValues(fromDate.format(DATETIME_INPUT_MASK), ''); this.setDateLimits(inputDate.format(CALENDAR_MASK), ''); this.emitSelectRangeDatesChangeEvent(fromDate); return; } if (isDateBefore(clickedDate, selectedStartDate)) { this.displayedMonth = clickedDate; this.setInputValues(fromDate.format(DATETIME_INPUT_MASK), ''); this.setDateLimits(fromDate.format(CALENDAR_MASK), ''); this.emitSelectRangeDatesChangeEvent(fromDate); return; } this.setInputValues(newDate(selectedStartDate).format(DATETIME_INPUT_MASK), toDate.format(DATETIME_INPUT_MASK)); this.resetDateLimits(); this.emitSelectRangeDatesChangeEvent(fromISO(selectedStartDate), toDate); return; } else { this.displayedMonth = clickedDate; this.singleInputValue = fromDate.format(DATETIME_INPUT_MASK); this.emitSelectRangeDatesChangeEvent(fromDate); } }; this.handleClickBackMonth = () => { this.displayedMonth = this.displayedMonth.subtract(1, 'month'); if (!isEmpty(this.relativeTimeConfig)) { this.relativeTimeConfigReset.emit(); } }; this.handleClickForwardMonth = () => { this.displayedMonth = this.displayedMonth.add(1, 'month'); if (!isEmpty(this.relativeTimeConfig)) { this.relativeTimeConfigReset.emit(); } }; this.handleHoveredDateChange = (event) => { if (this.mode === EAbsoluteTimePickerMode.Range) this.hoveredDate = event.detail; }; this.handleInputReset = () => { this.resetInputValues(); this.inputMode = ERelativeTimeInputMode.Date; this.relativeTimeConfigReset.emit(); }; /** User date-time-input I/O event handlers */ this.handleOnFocusFromInput = () => { this.toInputFocused = false; this.fromInputFocused = true; const parsedFromInputDate = dayjs(this.fromInputValue, DATETIME_INPUT_MASK); const parsedToInputDate = dayjs(this.toInputValue, DATETIME_INPUT_MASK); if (this.relativeTimeConfig) { if (this.relativeTimeConfig.mode === ERelativeTimeInputMode.Text) { this.handleInputReset(); } else { if (!isEmpty(this.fromInputValue)) { this.displayedMonth = parsedFromInputDate; } this.emitSelectRangeDatesChangeEvent(dayjs(this.fromInputValue, DATETIME_INPUT_MASK), dayjs(this.toInputValue, DATETIME_INPUT_MASK)); } } else { if (!isEmpty(this.fromInputValue) && (parsedToInputDate.diff(parsedFromInputDate, 'month') > 0 || this.displayedMonth.diff(parsedFromInputDate, 'month') > 0)) { this.displayedMonth = parsedFromInputDate; } } }; this.handleOnFocusToInput = () => { this.toInputFocused = true; this.fromInputFocused = false; const parsedToInputDate = dayjs(this.toInputValue, DATETIME_INPUT_MASK); const parsedFromInputDate = dayjs(this.fromInputValue, DATETIME_INPUT_MASK); if (this.relativeTimeConfig) { if (this.relativeTimeConfig.mode === ERelativeTimeInputMode.Text) { this.handleInputReset(); } else { if (!isEmpty(this.toInputValue) && parsedFromInputDate.diff(parsedToInputDate, 'month') > 0) { this.displayedMonth = parsedToInputDate.subtract(1, 'month'); } this.emitSelectRangeDatesChangeEvent(dayjs(this.fromInputValue, DATETIME_INPUT_MASK), dayjs(this.toInputValue, DATETIME_INPUT_MASK)); } } else { if (!isEmpty(this.toInputValue) && (parsedToInputDate.diff(parsedFromInputDate, 'month') > 0 || this.displayedMonth.diff(parsedToInputDate, 'month') > 0)) { this.displayedMonth = parsedToInputDate.subtract(1, 'month'); } } }; this.handleDateChange = (event, inputSource) => { const date = event.detail; const parsedDate = dayjs(date, DATE_INPUT_MASK); const parsedDateTime = dayjs(date, DATETIME_INPUT_MASK); if (inputSource === EInputSource.Single) { if (parsedDate.isValid() && parsedDateTime.isValid()) { this.singleInputValue = parsedDateTime.format(DATETIME_INPUT_MASK); this.displayedMonth = parsedDate; this.emitSelectRangeDatesChangeEvent(parsedDateTime); } } else { if (parsedDateTime.isValid()) { if (inputSource === EInputSource.From) { this.handleFromInputDateChange(parsedDate, parsedDateTime); } else { this.handleToInputDateChange(parsedDate, parsedDateTime); } } else { if (isEmpty(this.fromInputValue)) { this.minDate = ''; } if (isEmpty(this.toInputValue)) { this.maxDate = ''; } } } }; this.handleFromInputDateChange = (parsedDate, parsedDateTime) => { this.fromInputValue = parsedDateTime.format(DATETIME_INPUT_MASK); this.displayedMonth = parsedDate; if (!isEmpty(this.toInputValue)) { this.emitSelectRangeDatesChangeEvent(parsedDateTime, dayjs(this.toInputValue, DATETIME_INPUT_MASK)); this.resetDateLimits(); } else { this.emitSelectRangeDatesChangeEvent(parsedDateTime); this.toInputFocused = true; this.minDate = parsedDate.format(CALENDAR_MASK); } }; this.handleToInputDateChange = (parsedDate, parsedDateTime) => { this.toInputValue = parsedDateTime.format(DATETIME_INPUT_MASK); this.displayedMonth = parsedDate.subtract(1, 'month'); if (!isEmpty(this.fromInputValue)) { this.emitSelectRangeDatesChangeEvent(dayjs(this.fromInputValue, DATETIME_INPUT_MASK), parsedDateTime); this.resetDateLimits(); } else { this.maxDate = parsedDate.format(CALENDAR_MASK); this.emitSelectRangeDatesChangeEvent(parsedDateTime); this.fromInputFocused = true; } }; this.handleEndDateLostFocus = (event) => { const date = event.detail; const parsedDate = dayjs(date, DATE_INPUT_MASK); const parsedDateTime = dayjs(date, DATETIME_INPUT_MASK); if (parsedDate.isValid() && isEndDateAtStartOfDay(parsedDateTime)) { const parsedDateFormatted = parsedDate.endOf('day').format(DATETIME_INPUT_MASK); this.toInputValue = parsedDateFormatted; this.displayedMonth = parsedDate.subtract(1, 'month'); if (!isEmpty(this.fromInputValue)) { this.emitSelectRangeDatesChangeEvent(dayjs(this.fromInputValue, DATETIME_INPUT_MASK), parsedDate.endOf('day')); } else { this.emitSelectRangeDatesChangeEvent(parsedDate.endOf('day')); this.fromInputFocused = true; } } }; /** Components config methods */ this.useInputMask = () => { return this.inputMode === ERelativeTimeInputMode.Date; }; this.getCalendarStringLimits = () => ({ minDate: !isEmpty(this.minDate) ? this.minDate : dayjs(this.calendarInputMinDate, DATETIME_INPUT_MASK).format(DATE_FORMAT), maxDate: !isEmpty(this.maxDate) ? this.maxDate : dayjs(this.calendarInputMaxDate, DATETIME_INPUT_MASK).format(DATE_FORMAT) }); this.getCalendarTimestampLimits = () => ({ minDate: !isEmpty(this.minDate) ? undefined : dayjs(this.calendarInputMinDate, DATETIME_INPUT_MASK).valueOf(), maxDate: !isEmpty(this.maxDate) ? undefined : dayjs(this.calendarInputMaxDate, DATETIME_INPUT_MASK).valueOf() }); } handleSelectedRangeDatesChange(value = []) { if (value.length === 0) { this.resetInputValues(); this.resetDateLimits(); return; } if (this.mode === EAbsoluteTimePickerMode.Range) { if (isEmpty(this.relativeTimeConfig)) { const [from, to] = value; const parsedFromDate = dayjs(from, CALENDAR_DATE_TIME_MASK).format(DATETIME_INPUT_MASK); const parsedToDate = dayjs(to, CALENDAR_DATE_TIME_MASK).format(DATETIME_INPUT_MASK); this.setInputValues(parsedFromDate, parsedToDate); } } else { const [date] = value; const parsedDate = dayjs(date, CALENDAR_DATE_TIME_MASK).format(DATETIME_INPUT_MASK); this.singleInputValue = parsedDate; } } handleRelativeTimeConfigInput(newValue) { var _a; if (isEmpty(newValue)) { this.inputMode = ERelativeTimeInputMode.Date; if (!this.selectedDates || this.selectedDates.length === 0) { this.setInputValues('', ''); this.displayedMonth = fromDateInput(new Date()); } return; } this.inputMode = newValue.mode; this.setInputValues(newValue.from, newValue.to); const [from] = (_a = this.selectedDates) !== null && _a !== void 0 ? _a : []; const date = newDate(from); if (date.isValid()) { this.displayedMonth = date; } } render() { const fromCalendarInitialDate = getFirstCalendarInitialDate(this.displayedMonth); const toCalendarInitialDate = getSecondCalendarInitialDate(this.displayedMonth); return (h(Host, { key: '0e3d605bcf63fd0051c31d97e5d86b336041b6e9' }, h("div", { key: '9442479afb6635af2aee2d11e49ceff43a923524', class: "absolute-time-picker-container" }, this.displayBackButton && (h("div", { key: '021c0b6977bd68e8533063f64662610ee0b9d061', class: "navigate-back", onClick: this.handleBackClick }, h("kv-action-button-text", { key: '4d0ab36df56ef43ef1cefa66595e7dbcf6ba61dd', text: "Back", icon: EIconName.SlimRight, type: EActionButtonType.Text, size: EComponentSize.Small }))), this.headerTitle && (h("div", { key: '9267d91699a5d825395589644ce193ec554aea16', class: "header" }, h("div", { key: '9c6c2666df41b718fa80fe315386a51039e1397a', class: "title" }, this.headerTitle))), this.mode === EAbsoluteTimePickerMode.Range ? (h("div", { class: "absolute-range-input" }, h("kv-date-time-input", Object.assign({ inputName: "from-input", useInputMask: this.useInputMask(), label: "From", value: this.fromInputValue, size: EComponentSize.Small, placeholder: DATE_INPUT_PLACEHOLDER, highlighted: isEmpty(this.fromInputValue) && !this.toInputFocused, onTextChange: ev => this.handleDateChange(ev, EInputSource.From), onInputFocus: this.handleOnFocusFromInput }, getFromDateInputState(this.error, this.getCalendarTimestampLimits()))), h("kv-date-time-input", Object.assign({ inputName: "to-input", useInputMask: this.useInputMask(), label: "To", value: this.toInputValue, size: EComponentSize.Small, placeholder: DATE_INPUT_PLACEHOLDER, highlighted: isEmpty(this.toInputValue) && !isEmpty(this.fromInputValue), onTextChange: ev => this.handleDateChange(ev, EInputSource.To), onDateTimeBlur: this.handleEndDateLostFocus, onInputFocus: this.handleOnFocusToInput }, getToDateTimeInputState(this.error, this.getCalendarTimestampLimits()))))) : (h("div", { class: "absolute-point-input" }, h("kv-date-time-input", Object.assign({ id: "single-date-input", useInputMask: true, label: "Day & Hour", value: this.singleInputValue, size: EComponentSize.Small, placeholder: DATE_INPUT_PLACEHOLDER, onTextChange: ev => this.handleDateChange(ev, EInputSource.Single) }, getSingleDateTimeInputState(this.error, this.getCalendarTimestampLimits()))))), h("div", { key: '3ea2252c5974867e2010741412c3c71bcaf1ef23', class: "calendars" }, h("kv-calendar", Object.assign({ key: '769e1ac0a896e81b072907c3e79d9b6b265817f7', mode: this.mode, displayNextMonthArrow: false, displayPreviousMonthArrow: true, selectedDates: this.selectedDates, hoveredDate: this.hoveredDate, initialDate: fromCalendarInitialDate, disabledDates: this.disabledDates, onClickDate: this.onClickDate, onChangeMonth: this.handleClickBackMonth, onHoveredDateChange: this.handleHoveredDateChange }, this.getCalendarStringLimits())), h("kv-calendar", Object.assign({ key: '04f44676e38e0cf3ca377325c1f540c3e32c3270', mode: this.mode, displayNextMonthArrow: true, displayPreviousMonthArrow: false, selectedDates: this.selectedDates, hoveredDate: this.hoveredDate, initialDate: toCalendarInitialDate, disabledDates: this.disabledDates, onClickDate: this.onClickDate, onChangeMonth: this.handleClickForwardMonth, onHoveredDateChange: this.handleHoveredDateChange }, this.getCalendarStringLimits())))))); } static get is() { return "kv-absolute-time-picker"; } static get originalStyleUrls() { return { "$": ["absolute-time-picker.scss"] }; } static get styleUrls() { return { "$": ["absolute-time-picker.css"] }; } static get properties() { return { "headerTitle": { "type": "string", "attribute": "header-title", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Title disaplayed on top of the component" }, "getter": false, "setter": false, "reflect": false, "defaultValue": "DEFAULT_HEADER_TITLE" }, "displayBackButton": { "type": "boolean", "attribute": "display-back-button", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Enables the back button displayed on top" }, "getter": false, "setter": false, "reflect": false, "defaultValue": "false" }, "mode": { "type": "string", "attribute": "mode", "mutable": false, "complexType": { "original": "EAbsoluteTimePickerMode", "resolved": "EAbsoluteTimePickerMode.Range | EAbsoluteTimePickerMode.Single", "references": { "EAbsoluteTimePickerMode": { "location": "import", "path": "./absolute-time-picker.types", "id": "src/components/absolute-time-picker/absolute-time-picker.types.ts::EAbsoluteTimePickerMode" } } }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Defines if the calendar is in single date or range mode" }, "getter": false, "setter": false, "reflect": false, "defaultValue": "EAbsoluteTimePickerMode.Range" }, "selectedDates": { "type": "unknown", "attribute": "selected-dates", "mutable": false, "complexType": { "original": "string[]", "resolved": "string[]", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Selected dates" }, "getter": false, "setter": false, "defaultValue": "[]" }, "initialDate": { "type": "string", "attribute": "initial-date", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Initial Date" }, "getter": false, "setter": false, "reflect": false }, "disabledDates": { "type": "unknown", "attribute": "disabled-dates", "mutable": false, "complexType": { "original": "string[]", "resolved": "string[]", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Disabled dates" }, "getter": false, "setter": false, "defaultValue": "[]" }, "relativeTimeConfig": { "type": "unknown", "attribute": "relative-time-config", "mutable": false, "complexType": { "original": "IRelativeTimeInput", "resolved": "IRelativeTimeInput", "references": { "IRelativeTimeInput": { "location": "import", "path": "./absolute-time-picker.types", "id": "src/components/absolute-time-picker/absolute-time-picker.types.ts::IRelativeTimeInput" } } }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) use to determine if the chart inputs have custom strings" }, "getter": false, "setter": false }, "calendarInputMinDate": { "type": "string", "attribute": "calendar-input-min-date", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) calendar minimum date to be navigated format: DD-MM-YYYY HH:mm:ss" }, "getter": false, "setter": false, "reflect": false, "defaultValue": "CALENDAR_INPUT_MIN_DATE" }, "calendarInputMaxDate": { "type": "string", "attribute": "calendar-input-max-date", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) calendar maximum date to be navigated format: DD-MM-YYYY HH:mm:ss" }, "getter": false, "setter": false, "reflect": false, "defaultValue": "CALENDAR_INPUT_MAX_DATE" }, "error": { "type": "string", "attribute": "error", "mutable": false, "complexType": { "original": "EAbsoluteTimeError", "resolved": "EAbsoluteTimeError.EndDateAfterMaximumDate | EAbsoluteTimeError.EndDateBeforeStartDate | EAbsoluteTimeError.StartDateBeforeMinimumDate", "references": { "EAbsoluteTimeError": { "location": "import", "path": "../../types", "id": "src/types.ts::EAbsoluteTimeError" } } }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) It is used to determine if the time picker as date time input error" }, "getter": false, "setter": false, "reflect": false } }; } static get states() { return { "fromInputValue": {}, "toInputValue": {}, "singleInputValue": {}, "hoveredDate": {}, "displayedMonth": {}, "fromInputFocused": {}, "toInputFocused": {}, "inputMode": {}, "minDate": {}, "maxDate": {} }; } static get events() { return [{ "method": "selectedDatesChange", "name": "selectedDatesChange", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "Selected dates change" }, "complexType": { "original": "IAbsoluteSelectedRangeDates", "resolved": "IAbsoluteSelectedRangeDates", "references": { "IAbsoluteSelectedRangeDates": { "location": "import", "path": "./absolute-time-picker.types", "id": "src/components/absolute-time-picker/absolute-time-picker.types.ts::IAbsoluteSelectedRangeDates" } } } }, { "method": "backButtonClicked", "name": "backButtonClicked", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "Emitted when the back button is clicked" }, "complexType": { "original": "MouseEvent", "resolved": "MouseEvent", "references": { "MouseEvent": { "location": "global", "id": "global::MouseEvent" } } } }, { "method": "relativeTimeConfigReset", "name": "relativeTimeConfigReset", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "Emitted when the input is clicked and it were displaying custom text" }, "complexType": { "original": "MouseEvent", "resolved": "MouseEvent", "references": { "MouseEvent": { "location": "global", "id": "global::MouseEvent" } } } }, { "method": "relativeTimeConfigChange", "name": "relativeTimeConfigChange", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "Emitted when there is a change in the relative config" }, "complexType": { "original": "IAbsoluteSelectedRangeDates", "resolved": "IAbsoluteSelectedRangeDates", "references": { "IAbsoluteSelectedRangeDates": { "location": "import", "path": "./absolute-time-picker.types", "id": "src/components/absolute-time-picker/absolute-time-picker.types.ts::IAbsoluteSelectedRangeDates" } } } }]; } static get watchers() { return [{ "propName": "selectedDates", "methodName": "handleSelectedRangeDatesChange" }, { "propName": "relativeTimeConfig", "methodName": "handleRelativeTimeConfigInput" }]; } }