@ember-eui/core 5.8.5 → 5.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/addon/components/eui-checkbox-group/index.hbs +8 -0
- package/addon/components/eui-combo-box/index.hbs +1 -1
- package/addon/components/eui-combo-box/options/index.hbs +1 -0
- package/addon/components/eui-dual-range/index.ts +2 -2
- package/addon/components/eui-field-text/index.hbs +1 -0
- package/addon/components/eui-global-toast-list/index.ts +1 -1
- package/addon/components/eui-i18n/index.ts +9 -41
- package/addon/components/eui-icon/index.ts +2 -2
- package/addon/components/eui-markdown-editor/index.ts +2 -2
- package/addon/components/eui-portal/index.ts +2 -2
- package/addon/components/eui-range/index.ts +2 -2
- package/addon/components/eui-super-date-picker/date-popover/absolute-tab.hbs +42 -0
- package/addon/components/eui-super-date-picker/date-popover/absolute-tab.ts +79 -0
- package/addon/components/eui-super-date-picker/date-popover/datetime-picker.hbs +125 -0
- package/addon/components/eui-super-date-picker/date-popover/datetime-picker.ts +258 -0
- package/addon/components/eui-super-date-picker/date-popover/eui-date-popover-button.hbs +49 -0
- package/addon/components/eui-super-date-picker/date-popover/eui-date-popover-button.ts +49 -0
- package/addon/components/eui-super-date-picker/date-popover/eui-date-popover-content.hbs +42 -0
- package/addon/components/eui-super-date-picker/date-popover/eui-date-popover-content.ts +73 -0
- package/addon/components/eui-super-date-picker/date-popover/now-tab.hbs +45 -0
- package/addon/components/eui-super-date-picker/date-popover/relative-tab.hbs +47 -0
- package/addon/components/eui-super-date-picker/date-popover/relative-tab.ts +89 -0
- package/addon/components/eui-super-date-picker/eui-date-picker-range.hbs +22 -0
- package/addon/components/eui-super-date-picker/eui-quick-select-popover/eui-commonly-used-time-ranges.hbs +42 -0
- package/addon/components/eui-super-date-picker/eui-quick-select-popover/eui-quick-select.hbs +154 -0
- package/addon/components/eui-super-date-picker/eui-quick-select-popover/eui-quick-select.ts +103 -0
- package/addon/components/eui-super-date-picker/eui-quick-select-popover/eui-recently-used.hbs +48 -0
- package/addon/components/eui-super-date-picker/eui-quick-select-popover/index.hbs +60 -0
- package/addon/components/eui-super-date-picker/eui-quick-select-popover/index.ts +34 -0
- package/addon/components/eui-super-date-picker/eui-super-update-button.hbs +43 -0
- package/addon/components/eui-super-date-picker/index.hbs +123 -0
- package/addon/components/eui-super-date-picker/index.ts +220 -0
- package/addon/components/eui-super-date-picker/types/global.d.ts +52 -0
- package/addon/components/eui-super-date-picker/utils/date-utils.ts +59 -0
- package/addon/components/eui-super-date-picker/utils/index.ts +181 -0
- package/addon/components/eui-super-date-picker/utils/pretty-duration.ts +131 -0
- package/addon/components/eui-super-date-picker/utils/quick-select.ts +75 -0
- package/addon/components/eui-super-date-picker/utils/time-options.ts +216 -0
- package/addon/components/eui-tool-tip/index.ts +2 -2
- package/addon/components/text-block/index.hbs +25 -21
- package/addon/{components/eui-i18n → i18n}/util.ts +1 -6
- package/addon/modifiers/outside-click-detector.ts +2 -2
- package/addon/services/eui-i18n.ts +89 -0
- package/addon/utils/markdown/markdown-unified-plugins.d.ts +0 -14
- package/app/components/eui-super-date-picker/date-popover/absolute-tab.js +1 -0
- package/app/components/eui-super-date-picker/date-popover/datetime-picker.js +1 -0
- package/app/components/eui-super-date-picker/date-popover/eui-date-popover-button.js +1 -0
- package/app/components/eui-super-date-picker/date-popover/eui-date-popover-content.js +1 -0
- package/app/components/eui-super-date-picker/date-popover/now-tab.js +1 -0
- package/app/components/eui-super-date-picker/date-popover/relative-tab.js +1 -0
- package/app/components/eui-super-date-picker/eui-date-picker-range.js +1 -0
- package/app/components/eui-super-date-picker/eui-quick-select-popover/eui-commonly-used-time-ranges.js +1 -0
- package/app/components/eui-super-date-picker/eui-quick-select-popover/eui-quick-select.js +1 -0
- package/app/components/eui-super-date-picker/eui-quick-select-popover/eui-recently-used.js +1 -0
- package/app/components/eui-super-date-picker/eui-quick-select-popover/index.js +1 -0
- package/app/components/eui-super-date-picker/eui-super-update-button.js +1 -0
- package/app/components/eui-super-date-picker/index.js +1 -0
- package/app/services/eui-i18n.js +1 -0
- package/docs/forms/super-date-picker/demo/d01-picker.md +90 -0
- package/docs/forms/super-date-picker/index.md +16 -0
- package/package.json +7 -3
- package/tsconfig.json +1 -0
- package/addon/helpers/unique-id.ts +0 -23
- package/addon/i18n/index.ts +0 -8
- package/app/helpers/unique-id.js +0 -1
- package/app/i18n/index.js +0 -1
- package/docs/templates/super-date-picker/demo/d01-update-button.md +0 -10
- package/docs/templates/super-date-picker/demo/d02-quick-select-panels.md +0 -10
- package/docs/templates/super-date-picker/demo/d03-sizing.md +0 -10
- package/docs/templates/super-date-picker/demo/d04-auto-refresh.md +0 -11
- package/docs/templates/super-date-picker/demo/d05-elastic-pattern-with-kql.md +0 -10
- package/docs/templates/super-date-picker/index.md +0 -12
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { action } from '@ember/object';
|
|
2
|
+
import Component from '@glimmer/component';
|
|
3
|
+
import { tracked } from '@glimmer/tracking';
|
|
4
|
+
import { argOrDefaultDecorator as argOrDefault } from '../../helpers/arg-or-default';
|
|
5
|
+
import { LocaleSpecifier } from 'moment';
|
|
6
|
+
import {
|
|
7
|
+
ApplyRefreshInterval,
|
|
8
|
+
DurationRange,
|
|
9
|
+
Milliseconds,
|
|
10
|
+
ShortDate
|
|
11
|
+
} from './types/global';
|
|
12
|
+
import { isRangeInvalid } from './utils';
|
|
13
|
+
import { useI18nTimeOptions } from './utils/time-options';
|
|
14
|
+
import { OnRefreshProps, OnTimeChangeProps } from '@elastic/eui';
|
|
15
|
+
import type EuiI18n from '../../services/eui-i18n';
|
|
16
|
+
import { inject as service } from '@ember/service';
|
|
17
|
+
|
|
18
|
+
interface EuiSuperDatePickerArgs {
|
|
19
|
+
commonlyUsedRanges?: DurationRange[];
|
|
20
|
+
// customQuickSelectPanels?: QuickSelectPanel[];
|
|
21
|
+
/**
|
|
22
|
+
* Specifies the formatted used when displaying dates and/or datetimes
|
|
23
|
+
*/
|
|
24
|
+
dateFormat?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Set isAutoRefreshOnly to true to limit the component to only display auto refresh content.
|
|
27
|
+
*/
|
|
28
|
+
isAutoRefreshOnly?: boolean;
|
|
29
|
+
isDisabled?: boolean;
|
|
30
|
+
isLoading?: boolean;
|
|
31
|
+
isPaused?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Sets the overall width by adding sensible min and max widths.
|
|
34
|
+
* - `auto`: fits width to internal content / time string.
|
|
35
|
+
* - `restricted`: static width that fits the longest possible time string.
|
|
36
|
+
* - `full`: expands to 100% of the container.
|
|
37
|
+
*/
|
|
38
|
+
width?: 'restricted' | 'full' | 'auto';
|
|
39
|
+
/**
|
|
40
|
+
* Reduces overall height to compressed form size
|
|
41
|
+
*/
|
|
42
|
+
compressed?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Used to localize e.g. month names, passed to `moment`
|
|
45
|
+
*/
|
|
46
|
+
locale?: LocaleSpecifier;
|
|
47
|
+
/**
|
|
48
|
+
* Callback for when the refresh interval is fired.
|
|
49
|
+
* EuiSuperDatePicker will only manage a refresh interval timer when onRefresh callback is supplied
|
|
50
|
+
* If a promise is returned, the next refresh interval will not start until the promise has resolved.
|
|
51
|
+
* If the promise rejects the refresh interval will stop and the error thrown
|
|
52
|
+
*/
|
|
53
|
+
onRefresh?: (props: OnRefreshProps) => void;
|
|
54
|
+
/**
|
|
55
|
+
* Callback for when the refresh interval changes.
|
|
56
|
+
* Supply onRefreshChange to show refresh interval inputs in quick select popover
|
|
57
|
+
*/
|
|
58
|
+
onRefreshChange?: ApplyRefreshInterval;
|
|
59
|
+
/**
|
|
60
|
+
* Callback for when the time changes.
|
|
61
|
+
*/
|
|
62
|
+
onTimeChange: (props: OnTimeChangeProps) => void;
|
|
63
|
+
// recentlyUsedRanges?: DurationRange[];
|
|
64
|
+
/**
|
|
65
|
+
* Refresh interval in milliseconds
|
|
66
|
+
*/
|
|
67
|
+
refreshInterval?: Milliseconds;
|
|
68
|
+
start?: ShortDate;
|
|
69
|
+
end?: ShortDate;
|
|
70
|
+
/**
|
|
71
|
+
* Specifies the formatted used when displaying times
|
|
72
|
+
*/
|
|
73
|
+
timeFormat?: string;
|
|
74
|
+
utcOffset?: number;
|
|
75
|
+
/**
|
|
76
|
+
* Set showUpdateButton to false to immediately invoke onTimeChange for all start and end changes.
|
|
77
|
+
*/
|
|
78
|
+
showUpdateButton?: boolean | 'iconOnly';
|
|
79
|
+
/**
|
|
80
|
+
* Hides the actual input reducing to just the quick select button.
|
|
81
|
+
*/
|
|
82
|
+
isQuickSelectOnly?: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Props passed to the update button #EuiSuperUpdateButtonProps
|
|
85
|
+
*/
|
|
86
|
+
// updateButtonProps?: EuiSuperUpdateButtonProps;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export default class EuiSuperDatePicker extends Component<EuiSuperDatePickerArgs> {
|
|
90
|
+
@argOrDefault('MMM D, YYYY @ HH:mm:ss.SSS') dateFormat!: string;
|
|
91
|
+
@argOrDefault('HH:mm') timeFormat!: string;
|
|
92
|
+
// @argOrDefault('now-15m') start!: ShortDate;
|
|
93
|
+
// @argOrDefault('now') end!: ShortDate;
|
|
94
|
+
@argOrDefault(false) isAutoRefreshOnly!: boolean;
|
|
95
|
+
@argOrDefault(false) isDisabled!: boolean;
|
|
96
|
+
@argOrDefault(true) isPaused!: boolean;
|
|
97
|
+
@argOrDefault(true) showUpdateButton!: boolean;
|
|
98
|
+
@argOrDefault('restricted') width!: string;
|
|
99
|
+
// recentlyUsedRanges: [],
|
|
100
|
+
@argOrDefault(1000) refreshInterval!: Milliseconds;
|
|
101
|
+
|
|
102
|
+
@service declare euiI18n: EuiI18n;
|
|
103
|
+
|
|
104
|
+
@tracked start: ShortDate;
|
|
105
|
+
@tracked end: ShortDate;
|
|
106
|
+
@tracked isInvalid = false;
|
|
107
|
+
@tracked hasChanged = false;
|
|
108
|
+
|
|
109
|
+
constructor(owner: any, args: EuiSuperDatePickerArgs) {
|
|
110
|
+
super(owner, args);
|
|
111
|
+
|
|
112
|
+
this.start = this.args.start ?? 'now-15m';
|
|
113
|
+
this.end = this.args.end ?? 'now';
|
|
114
|
+
|
|
115
|
+
// if (this.args.start === undefined || this.args.end === undefined) {
|
|
116
|
+
// this.setTime({
|
|
117
|
+
// start: this.args.start ?? 'now-15m',
|
|
118
|
+
// end: this.args.end ?? 'now'
|
|
119
|
+
// });
|
|
120
|
+
// }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
get timeOptions() {
|
|
124
|
+
return useI18nTimeOptions(this.euiI18n);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
setTime({ start, end }: DurationRange) {
|
|
128
|
+
this.hasChanged = !(this.start === start && this.end === end);
|
|
129
|
+
this.start = start;
|
|
130
|
+
this.end = end;
|
|
131
|
+
this.isInvalid = isRangeInvalid(start, end);
|
|
132
|
+
|
|
133
|
+
if (!this.showUpdateButton) {
|
|
134
|
+
this.args.onTimeChange({
|
|
135
|
+
start,
|
|
136
|
+
end,
|
|
137
|
+
isQuickSelection: false,
|
|
138
|
+
isInvalid: this.isInvalid
|
|
139
|
+
} as OnTimeChangeProps);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
applyTime() {
|
|
144
|
+
this.args.onTimeChange({
|
|
145
|
+
start: this.start,
|
|
146
|
+
end: this.end,
|
|
147
|
+
isQuickSelection: false,
|
|
148
|
+
isInvalid: false
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// stopInterval() {
|
|
153
|
+
// if (this.asyncInterval) {
|
|
154
|
+
// this.asyncInterval.stop();
|
|
155
|
+
// }
|
|
156
|
+
// }
|
|
157
|
+
|
|
158
|
+
// startInterval(refreshInterval: number) {
|
|
159
|
+
// if (this.args.onRefresh) {
|
|
160
|
+
// const handler = () => {
|
|
161
|
+
// const { start, end } = this.props;
|
|
162
|
+
// this.args.onRefresh({ start, end, refreshInterval });
|
|
163
|
+
// };
|
|
164
|
+
// this.asyncInterval = new AsyncInterval(handler, refreshInterval);
|
|
165
|
+
// }
|
|
166
|
+
// }
|
|
167
|
+
|
|
168
|
+
@action applyQuickTime({ start, end }: DurationRange) {
|
|
169
|
+
// this.setState({
|
|
170
|
+
// showPrettyDuration: showPrettyDuration(
|
|
171
|
+
// start,
|
|
172
|
+
// end,
|
|
173
|
+
// this.props.commonlyUsedRanges
|
|
174
|
+
// )
|
|
175
|
+
// });
|
|
176
|
+
|
|
177
|
+
// Update the internal state
|
|
178
|
+
this.start = start;
|
|
179
|
+
this.end = end;
|
|
180
|
+
|
|
181
|
+
this.args.onTimeChange({
|
|
182
|
+
start,
|
|
183
|
+
end,
|
|
184
|
+
isQuickSelection: true,
|
|
185
|
+
isInvalid: false
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
@action setStart(start: ShortDate) {
|
|
190
|
+
this.setTime({ start, end: this.end });
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
@action setEnd(end: ShortDate) {
|
|
194
|
+
this.setTime({ start: this.start, end });
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
@action handleClickUpdateButton() {
|
|
198
|
+
if (!this.hasChanged && this.args.onRefresh) {
|
|
199
|
+
// const { start, end, refreshInterval } = this.args;
|
|
200
|
+
this.args.onRefresh({
|
|
201
|
+
start: this.start,
|
|
202
|
+
end: this.end,
|
|
203
|
+
refreshInterval: this.refreshInterval
|
|
204
|
+
});
|
|
205
|
+
} else {
|
|
206
|
+
this.applyTime();
|
|
207
|
+
}
|
|
208
|
+
this.hasChanged = false;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// @action onRefreshChange({ refreshInterval, isPaused }) {
|
|
212
|
+
// this.stopInterval();
|
|
213
|
+
// if (!isPaused) {
|
|
214
|
+
// this.startInterval(refreshInterval);
|
|
215
|
+
// }
|
|
216
|
+
// if (this.props.onRefreshChange) {
|
|
217
|
+
// this.props.onRefreshChange({ refreshInterval, isPaused });
|
|
218
|
+
// }
|
|
219
|
+
// }
|
|
220
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Moment } from 'moment';
|
|
2
|
+
|
|
3
|
+
export interface DurationRange {
|
|
4
|
+
end: ShortDate;
|
|
5
|
+
label?: string;
|
|
6
|
+
start: ShortDate;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type ShortDate = NowDateMode | string;
|
|
10
|
+
|
|
11
|
+
export type NowDateMode = 'now';
|
|
12
|
+
|
|
13
|
+
export type TimeUnitId = 's' | 'm' | 'h' | 'd' | 'w' | 'M' | 'y';
|
|
14
|
+
|
|
15
|
+
export type TimeUnitFromNowId = 's+' | 'm+' | 'h+' | 'd+' | 'w+' | 'M+' | 'y+';
|
|
16
|
+
|
|
17
|
+
export interface RelativeParts {
|
|
18
|
+
count: number;
|
|
19
|
+
round: boolean;
|
|
20
|
+
roundUnit?: TimeUnitId;
|
|
21
|
+
unit: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type Milliseconds = number;
|
|
25
|
+
|
|
26
|
+
export interface RelativeOption {
|
|
27
|
+
text: string;
|
|
28
|
+
value: TimeUnitAllId;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type TimeUnitAllId = TimeUnitId | TimeUnitFromNowId;
|
|
32
|
+
|
|
33
|
+
export type OnRefreshChangeArgs = {
|
|
34
|
+
isPaused: boolean;
|
|
35
|
+
refreshInterval: number;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type ApplyRefreshInterval = (args: OnRefreshChangeArgs) => void;
|
|
39
|
+
|
|
40
|
+
interface ApplyTimeArgs extends DurationRange {
|
|
41
|
+
start: string;
|
|
42
|
+
end: string;
|
|
43
|
+
keepPopoverOpen?: boolean;
|
|
44
|
+
quickSelect?: QuickSelect;
|
|
45
|
+
}
|
|
46
|
+
export type ApplyTime = (args: ApplyTimeArgs) => void;
|
|
47
|
+
|
|
48
|
+
export interface QuickSelect {
|
|
49
|
+
timeTense: string;
|
|
50
|
+
timeValue: number;
|
|
51
|
+
timeUnits: TimeUnitId;
|
|
52
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Moment } from 'moment';
|
|
2
|
+
|
|
3
|
+
export function isSameDay(moment1: Moment, moment2: Moment) {
|
|
4
|
+
if (moment1 && moment2) {
|
|
5
|
+
return moment1.isSame(moment2, 'day');
|
|
6
|
+
} else {
|
|
7
|
+
return !moment1 && !moment2;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function isSameTime(moment1: Moment, moment2: Moment) {
|
|
12
|
+
if (moment1 && moment2) {
|
|
13
|
+
return moment1.isSame(moment2, 'second');
|
|
14
|
+
} else {
|
|
15
|
+
return !moment1 && !moment2;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function isDayDisabled(
|
|
20
|
+
day: Moment,
|
|
21
|
+
{
|
|
22
|
+
minDate,
|
|
23
|
+
maxDate,
|
|
24
|
+
excludeDates,
|
|
25
|
+
includeDates,
|
|
26
|
+
filterDate
|
|
27
|
+
}: {
|
|
28
|
+
minDate?: Moment;
|
|
29
|
+
maxDate?: Moment;
|
|
30
|
+
excludeDates?: Moment[];
|
|
31
|
+
includeDates?: Moment[];
|
|
32
|
+
filterDate?: (day: Moment) => boolean;
|
|
33
|
+
} = {}
|
|
34
|
+
) {
|
|
35
|
+
return (
|
|
36
|
+
(minDate && day.isBefore(minDate, 'day')) ||
|
|
37
|
+
(maxDate && day.isAfter(maxDate, 'day')) ||
|
|
38
|
+
(excludeDates &&
|
|
39
|
+
excludeDates.some((excludeDate: Moment) =>
|
|
40
|
+
isSameDay(day, excludeDate)
|
|
41
|
+
)) ||
|
|
42
|
+
(includeDates &&
|
|
43
|
+
!includeDates.some((includeDate: Moment) =>
|
|
44
|
+
isSameDay(day, includeDate)
|
|
45
|
+
)) ||
|
|
46
|
+
(filterDate && !filterDate(day.clone())) ||
|
|
47
|
+
false
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function isOutOfBounds(
|
|
52
|
+
day: Moment,
|
|
53
|
+
{ minDate, maxDate }: { minDate?: Moment; maxDate?: Moment } = {}
|
|
54
|
+
) {
|
|
55
|
+
return (
|
|
56
|
+
(minDate && day.isBefore(minDate, 'day')) ||
|
|
57
|
+
(maxDate && day.isAfter(maxDate, 'day'))
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
3
|
+
* or more contributor license agreements. Licensed under the Elastic License
|
|
4
|
+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
|
5
|
+
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
|
6
|
+
* Side Public License, v 1.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import moment, { LocaleSpecifier } from 'moment';
|
|
10
|
+
import dateMath from '@elastic/datemath';
|
|
11
|
+
import { RelativeParts, ShortDate } from '../types/global';
|
|
12
|
+
import type EuiI18n from '../../../services/eui-i18n';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Reusable format time string util
|
|
16
|
+
*/
|
|
17
|
+
export const ISO_FORMAT = 'YYYY-MM-DDTHH:mm:ss.SSSZ';
|
|
18
|
+
export const INVALID_DATE = 'invalid_date';
|
|
19
|
+
export const ROUND_DELIMETER = '/';
|
|
20
|
+
export const relativeUnitsFromLargestToSmallest = [
|
|
21
|
+
'y',
|
|
22
|
+
'M',
|
|
23
|
+
'w',
|
|
24
|
+
'd',
|
|
25
|
+
'h',
|
|
26
|
+
'm',
|
|
27
|
+
's'
|
|
28
|
+
];
|
|
29
|
+
export type AbsoluteDateMode = 'absolute';
|
|
30
|
+
export type RelativeDateMode = 'relative';
|
|
31
|
+
export type NowDateMode = 'now';
|
|
32
|
+
export type DateMode = AbsoluteDateMode | RelativeDateMode | NowDateMode;
|
|
33
|
+
export const DATE_MODES: {
|
|
34
|
+
ABSOLUTE: AbsoluteDateMode;
|
|
35
|
+
RELATIVE: RelativeDateMode;
|
|
36
|
+
NOW: NowDateMode;
|
|
37
|
+
} = {
|
|
38
|
+
ABSOLUTE: 'absolute',
|
|
39
|
+
RELATIVE: 'relative',
|
|
40
|
+
NOW: 'now'
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const useFormatTimeString = (
|
|
44
|
+
timeString: string,
|
|
45
|
+
dateFormat: string,
|
|
46
|
+
roundUp = false,
|
|
47
|
+
locale: LocaleSpecifier = 'en',
|
|
48
|
+
euiI18nService: EuiI18n
|
|
49
|
+
): string => {
|
|
50
|
+
let lookupToken = euiI18nService.lookupToken;
|
|
51
|
+
|
|
52
|
+
// i18n'd strings
|
|
53
|
+
const nowDisplay = lookupToken('euiPrettyDuration.now', 'now');
|
|
54
|
+
const invalidDateDisplay = lookupToken(
|
|
55
|
+
'euiPrettyDuration.invalid',
|
|
56
|
+
'Invalid date'
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const timeAsMoment = moment(timeString, ISO_FORMAT, true);
|
|
60
|
+
if (timeAsMoment.isValid()) {
|
|
61
|
+
return timeAsMoment.locale(locale).format(dateFormat);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (timeString === 'now') {
|
|
65
|
+
return nowDisplay as string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const tryParse = dateMath.parse(timeString, { roundUp: roundUp });
|
|
69
|
+
if (!moment(tryParse).isValid()) {
|
|
70
|
+
return invalidDateDisplay as string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (moment.isMoment(tryParse)) {
|
|
74
|
+
return `~ ${tryParse.locale(locale).fromNow()}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return timeString;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export function toAbsoluteString(value: string, roundUp: boolean = false) {
|
|
81
|
+
const valueAsMoment = dateMath.parse(value, { roundUp });
|
|
82
|
+
if (!valueAsMoment) {
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
if (!moment(valueAsMoment).isValid()) {
|
|
86
|
+
return INVALID_DATE;
|
|
87
|
+
}
|
|
88
|
+
return valueAsMoment.toISOString();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function parseRelativeParts(value: string): {
|
|
92
|
+
count: number;
|
|
93
|
+
unit: string;
|
|
94
|
+
round: boolean;
|
|
95
|
+
roundUnit?: string;
|
|
96
|
+
} {
|
|
97
|
+
const matches =
|
|
98
|
+
typeof value === 'string' &&
|
|
99
|
+
value.match(/now(([-+])([0-9]+)([smhdwMy])(\/[smhdwMy])?)?/);
|
|
100
|
+
|
|
101
|
+
const operator = matches && matches[2];
|
|
102
|
+
const count = matches && matches[3];
|
|
103
|
+
const unit = matches && matches[4];
|
|
104
|
+
const roundBy = matches && matches[5];
|
|
105
|
+
|
|
106
|
+
if (count && unit) {
|
|
107
|
+
const isRounded = roundBy ? true : false;
|
|
108
|
+
const roundUnit =
|
|
109
|
+
isRounded && roundBy ? roundBy.replace(ROUND_DELIMETER, '') : undefined;
|
|
110
|
+
return {
|
|
111
|
+
count: parseInt(count, 10),
|
|
112
|
+
unit: operator === '+' ? `${unit}+` : unit,
|
|
113
|
+
round: isRounded,
|
|
114
|
+
...(roundUnit ? { roundUnit } : {})
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const results = { count: 0, unit: 's', round: false };
|
|
119
|
+
const duration = moment.duration(moment().diff(dateMath.parse(value)));
|
|
120
|
+
let unitOp = '';
|
|
121
|
+
for (let i = 0; i < relativeUnitsFromLargestToSmallest.length; i++) {
|
|
122
|
+
// @ts-expect-error this is a string with the accepted time units
|
|
123
|
+
const asRelative = duration.as(relativeUnitsFromLargestToSmallest[i]);
|
|
124
|
+
if (asRelative < 0) unitOp = '+';
|
|
125
|
+
if (Math.abs(asRelative) > 1) {
|
|
126
|
+
results.count = Math.round(Math.abs(asRelative));
|
|
127
|
+
results.unit = relativeUnitsFromLargestToSmallest[i] + unitOp;
|
|
128
|
+
results.round = false;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return results;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function toRelativeStringFromParts(relativeParts: RelativeParts) {
|
|
136
|
+
const count = relativeParts.count ?? 0;
|
|
137
|
+
const isRounded = relativeParts.round ?? false;
|
|
138
|
+
|
|
139
|
+
if (count === 0 && !isRounded) {
|
|
140
|
+
return 'now';
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const matches = (relativeParts.unit ?? 's').match(/([smhdwMy])(\+)?/);
|
|
144
|
+
const unit = matches?.[1];
|
|
145
|
+
const operator = matches && matches[2] ? matches[2] : '-';
|
|
146
|
+
const round = isRounded ? `${ROUND_DELIMETER}${unit}` : '';
|
|
147
|
+
|
|
148
|
+
return `now${operator}${count}${unit}${round}`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function isRangeInvalid(start: ShortDate, end: ShortDate) {
|
|
152
|
+
if (start === 'now' && end === 'now') {
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const startMoment = dateMath.parse(start);
|
|
157
|
+
const endMoment = dateMath.parse(end, { roundUp: true });
|
|
158
|
+
|
|
159
|
+
const isInvalid =
|
|
160
|
+
!startMoment ||
|
|
161
|
+
!endMoment ||
|
|
162
|
+
!startMoment.isValid() ||
|
|
163
|
+
!endMoment.isValid() ||
|
|
164
|
+
!moment(startMoment).isValid() ||
|
|
165
|
+
!moment(endMoment).isValid() ||
|
|
166
|
+
startMoment.isAfter(endMoment);
|
|
167
|
+
|
|
168
|
+
return isInvalid;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function getDateMode(value: ShortDate) {
|
|
172
|
+
if (value === 'now') {
|
|
173
|
+
return DATE_MODES.NOW;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (value.includes('now')) {
|
|
177
|
+
return DATE_MODES.RELATIVE;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return DATE_MODES.ABSOLUTE;
|
|
181
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type EuiI18n from '../../../services/eui-i18n';
|
|
2
|
+
import { DATE_MODES, getDateMode } from '.';
|
|
3
|
+
import { DurationRange, ShortDate } from '../types/global';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Pretty duration i18n strings
|
|
7
|
+
* Units should not be simply concatenated because different languages
|
|
8
|
+
* will have different grammar/positions for time than English
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const useRelativeDurationI18n = <T extends number = number>(
|
|
12
|
+
duration: T,
|
|
13
|
+
euiI18n: EuiI18n
|
|
14
|
+
) => {
|
|
15
|
+
const lookupToken = euiI18n.lookupToken;
|
|
16
|
+
return {
|
|
17
|
+
s: lookupToken(
|
|
18
|
+
'euiPrettyDuration.lastDurationSeconds',
|
|
19
|
+
({ duration }: { duration: T }) =>
|
|
20
|
+
`Last ${duration} second${duration === 1 ? '' : 's'}`,
|
|
21
|
+
{ duration }
|
|
22
|
+
),
|
|
23
|
+
's+': lookupToken(
|
|
24
|
+
'euiPrettyDuration.nextDurationSeconds',
|
|
25
|
+
({ duration }: { duration: T }) =>
|
|
26
|
+
`Next ${duration} second${duration === 1 ? '' : 's'}`,
|
|
27
|
+
{ duration }
|
|
28
|
+
),
|
|
29
|
+
m: lookupToken(
|
|
30
|
+
'euiPrettyDuration.lastDurationMinutes',
|
|
31
|
+
({ duration }: { duration: T }) =>
|
|
32
|
+
`Last ${duration} minute${duration === 1 ? '' : 's'}`,
|
|
33
|
+
{ duration }
|
|
34
|
+
),
|
|
35
|
+
'm+': lookupToken(
|
|
36
|
+
'euiPrettyDuration.nextDurationMinutes',
|
|
37
|
+
({ duration }: { duration: T }) =>
|
|
38
|
+
`Next ${duration} minute${duration === 1 ? '' : 's'}`,
|
|
39
|
+
{ duration }
|
|
40
|
+
),
|
|
41
|
+
h: lookupToken(
|
|
42
|
+
'euiPrettyDuration.lastDurationHours',
|
|
43
|
+
({ duration }: { duration: T }) =>
|
|
44
|
+
`Last ${duration} hour${duration === 1 ? '' : 's'}`,
|
|
45
|
+
{ duration }
|
|
46
|
+
),
|
|
47
|
+
'h+': lookupToken(
|
|
48
|
+
'euiPrettyDuration.nextDurationHours',
|
|
49
|
+
({ duration }: { duration: T }) =>
|
|
50
|
+
`Next ${duration} hour${duration === 1 ? '' : 's'}`,
|
|
51
|
+
{ duration }
|
|
52
|
+
),
|
|
53
|
+
d: lookupToken(
|
|
54
|
+
'euiPrettyDuration.lastDurationDays',
|
|
55
|
+
({ duration }: { duration: T }) =>
|
|
56
|
+
`Last ${duration} day${duration === 1 ? '' : 's'}`,
|
|
57
|
+
{ duration }
|
|
58
|
+
),
|
|
59
|
+
'd+': lookupToken(
|
|
60
|
+
'euiPrettyDuration.nexttDurationDays',
|
|
61
|
+
({ duration }: { duration: T }) =>
|
|
62
|
+
`Next ${duration} day${duration === 1 ? '' : 's'}`,
|
|
63
|
+
{ duration }
|
|
64
|
+
),
|
|
65
|
+
w: lookupToken(
|
|
66
|
+
'euiPrettyDuration.lastDurationWeeks',
|
|
67
|
+
({ duration }: { duration: T }) =>
|
|
68
|
+
`Last ${duration} week${duration === 1 ? '' : 's'}`,
|
|
69
|
+
{ duration }
|
|
70
|
+
),
|
|
71
|
+
'w+': lookupToken(
|
|
72
|
+
'euiPrettyDuration.nextDurationWeeks',
|
|
73
|
+
({ duration }: { duration: T }) =>
|
|
74
|
+
`Next ${duration} week${duration === 1 ? '' : 's'}`,
|
|
75
|
+
{ duration }
|
|
76
|
+
),
|
|
77
|
+
M: lookupToken(
|
|
78
|
+
'euiPrettyDuration.lastDurationMonths',
|
|
79
|
+
({ duration }: { duration: T }) =>
|
|
80
|
+
`Last ${duration} month${duration === 1 ? '' : 's'}`,
|
|
81
|
+
{ duration }
|
|
82
|
+
),
|
|
83
|
+
'M+': lookupToken(
|
|
84
|
+
'euiPrettyDuration.nextDurationMonths',
|
|
85
|
+
({ duration }: { duration: T }) =>
|
|
86
|
+
`Next ${duration} month${duration === 1 ? '' : 's'}`,
|
|
87
|
+
{ duration }
|
|
88
|
+
),
|
|
89
|
+
y: lookupToken(
|
|
90
|
+
'euiPrettyDuration.lastDurationYears',
|
|
91
|
+
({ duration }: { duration: T }) =>
|
|
92
|
+
`Last ${duration} year${duration === 1 ? '' : 's'}`,
|
|
93
|
+
{ duration }
|
|
94
|
+
),
|
|
95
|
+
'y+': lookupToken(
|
|
96
|
+
'euiPrettyDuration.nextDurationYears',
|
|
97
|
+
({ duration }: { duration: T }) =>
|
|
98
|
+
`Next ${duration} year${duration === 1 ? '' : 's'}`,
|
|
99
|
+
{ duration }
|
|
100
|
+
)
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const hasRangeMatch = (
|
|
105
|
+
timeFrom: ShortDate,
|
|
106
|
+
timeTo: ShortDate,
|
|
107
|
+
ranges: DurationRange[]
|
|
108
|
+
) => {
|
|
109
|
+
return ranges.find(({ start, end }) => timeFrom === start && timeTo === end);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const isRelativeToNow = (timeFrom: ShortDate, timeTo: ShortDate): boolean => {
|
|
113
|
+
const fromDateMode = getDateMode(timeFrom);
|
|
114
|
+
const toDateMode = getDateMode(timeTo);
|
|
115
|
+
const isLast =
|
|
116
|
+
fromDateMode === DATE_MODES.RELATIVE && toDateMode === DATE_MODES.NOW;
|
|
117
|
+
const isNext =
|
|
118
|
+
fromDateMode === DATE_MODES.NOW && toDateMode === DATE_MODES.RELATIVE;
|
|
119
|
+
return isLast || isNext;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const showPrettyDuration = (
|
|
123
|
+
timeFrom: ShortDate,
|
|
124
|
+
timeTo: ShortDate,
|
|
125
|
+
quickRanges: DurationRange[]
|
|
126
|
+
): boolean => {
|
|
127
|
+
if (hasRangeMatch(timeFrom, timeTo, quickRanges)) {
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
return isRelativeToNow(timeFrom, timeTo);
|
|
131
|
+
};
|