-
Notifications
You must be signed in to change notification settings - Fork 4.3k
/
Copy pathttl-picker.js
171 lines (153 loc) · 5.4 KB
/
ttl-picker.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
/**
* @module TtlPicker
* TtlPicker components are used to enable and select duration values such as TTL.
* This component renders a toggle by default, and passes all relevant attributes
* to TtlForm. Please see that component for additional arguments
* - allows TTL to be enabled or disabled
* - recalculates the time when the unit is changed by the user (eg 60s -> 1m)
*
* @example
* <TtlPicker @onChange={{this.handleChange}} @initialEnabled={{@model.myAttribute}} @initialValue={{@model.myAttribute}}/>
*
* @param {function} onChange - This function will be passed a TTL object, which includes enabled{bool}, seconds{number}, timeString{string}, goSafeTimeString{string}.
* @param {boolean} initialEnabled=false - Set this value if you want the toggle on when component is mounted
* @param {string} label=Time to live (TTL) - Label is the main label that lives next to the toggle. Yielded values will replace the label
* @param {string} labelDisabled=Label to display when TTL is toggled off
* @param {string} helperTextEnabled - This helper text is shown under the label when the toggle is switched on
* @param {string} helperTextDisabled - This helper text is shown under the label when the toggle is switched off
* @param {string} initialValue=null - InitialValue is the duration value which will be shown when the component is loaded. If it can't be parsed, will default to 0.
* @param {boolean} changeOnInit=false - if true, calls the onChange hook when component is initialized
* @param {boolean} hideToggle=false - set this value if you'd like to hide the toggle and just leverage the input field
*/
import Component from '@glimmer/component';
import { typeOf } from '@ember/utils';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { guidFor } from '@ember/object/internals';
import Ember from 'ember';
import { restartableTask, timeout } from 'ember-concurrency';
import {
convertFromSeconds,
convertToSeconds,
durationToSeconds,
goSafeConvertFromSeconds,
largestUnitFromSeconds,
} from 'core/utils/duration-utils';
export default class TtlPickerComponent extends Component {
@tracked enableTTL = false;
@tracked recalculateSeconds = false;
@tracked time = ''; // if defaultValue is NOT set, then do not display a defaultValue.
@tracked unit = 's';
@tracked errorMessage = '';
/* Used internally */
recalculationTimeout = 5000;
elementId = 'ttl-' + guidFor(this);
get label() {
if (this.args.label && this.args.labelDisabled) {
return this.enableTTL ? this.args.label : this.args.labelDisabled;
}
return this.args.label || 'Time to live (TTL)';
}
get helperText() {
return this.enableTTL || this.args.hideToggle
? this.args.helperTextEnabled
: this.args.helperTextDisabled;
}
constructor() {
super(...arguments);
const enable = this.args.initialEnabled;
let setEnable = !!this.args.hideToggle;
if (!!enable || typeOf(enable) === 'boolean') {
// This allows non-boolean values passed in to be evaluated for truthiness
setEnable = !!enable;
}
this.enableTTL = setEnable;
this.initializeTtl();
}
initializeTtl() {
const initialValue = this.args.initialValue;
let seconds = 0;
if (typeof initialValue === 'number') {
// if the passed value is a number, assume unit is seconds
seconds = initialValue;
} else {
const parseDuration = durationToSeconds(initialValue);
// if parsing fails leave it empty
if (parseDuration === null) return;
seconds = parseDuration;
}
const unit = largestUnitFromSeconds(seconds);
this.time = convertFromSeconds(seconds, unit);
this.unit = unit;
if (this.args.changeOnInit) {
this.handleChange();
}
}
get seconds() {
return convertToSeconds(this.time, this.unit);
}
get unitOptions() {
return [
{ label: 'seconds', value: 's' },
{ label: 'minutes', value: 'm' },
{ label: 'hours', value: 'h' },
{ label: 'days', value: 'd' },
];
}
keepSecondsRecalculate(newUnit) {
const newTime = convertFromSeconds(this.seconds, newUnit);
if (Number.isInteger(newTime)) {
// Only recalculate if time is whole number
this.time = newTime;
}
this.unit = newUnit;
}
handleChange() {
const { time, unit, seconds, enableTTL } = this;
const ttl = {
enabled: this.args.hideToggle || enableTTL,
seconds,
timeString: time + unit,
goSafeTimeString: goSafeConvertFromSeconds(seconds, unit),
};
this.args.onChange(ttl);
}
@action
toggleEnabled() {
this.enableTTL = !this.enableTTL;
this.handleChange();
}
@restartableTask
*updateTime(newTime) {
this.errorMessage = '';
const parsedTime = parseInt(newTime, 10);
if (!newTime) {
this.errorMessage = 'This field is required';
return;
} else if (Number.isNaN(parsedTime)) {
this.errorMessage = 'Value must be a number';
return;
}
this.time = parsedTime;
this.handleChange();
if (Ember.testing) {
return;
}
this.recalculateSeconds = true;
yield timeout(this.recalculationTimeout);
this.recalculateSeconds = false;
}
@action
updateUnit(newUnit) {
if (this.recalculateSeconds) {
this.unit = newUnit;
} else {
this.keepSecondsRecalculate(newUnit);
}
this.handleChange();
}
}