@gitlab/ui 132.2.1 → 133.0.0
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/dist/components/base/dropdown/dropdown_item.js +1 -1
- package/dist/components/base/form/form_input/form_input.js +2 -60
- package/dist/components/base/form/form_textarea/form_textarea.js +421 -45
- package/dist/components/base/new_dropdowns/listbox/listbox_item.js +1 -1
- package/dist/vendor/bootstrap-vue/src/constants/components.js +1 -2
- package/package.json +1 -1
- package/src/components/base/dropdown/dropdown_item.vue +1 -1
- package/src/components/base/form/form_input/form_input.vue +2 -60
- package/src/components/base/form/form_textarea/form_textarea.vue +462 -49
- package/src/components/base/new_dropdowns/listbox/listbox_item.vue +1 -1
- package/src/vendor/bootstrap-vue/src/constants/components.js +0 -1
- package/dist/vendor/bootstrap-vue/src/components/form-textarea/form-textarea.js +0 -233
- package/dist/vendor/bootstrap-vue/src/components/form-textarea/index.js +0 -1
- package/dist/vendor/bootstrap-vue/src/mixins/form-selection.js +0 -62
- package/dist/vendor/bootstrap-vue/src/mixins/form-text.js +0 -279
- package/dist/vendor/bootstrap-vue/src/mixins/form-validity.js +0 -50
- package/src/vendor/bootstrap-vue/src/components/form-textarea/form-textarea.js +0 -241
- package/src/vendor/bootstrap-vue/src/components/form-textarea/index.js +0 -3
- package/src/vendor/bootstrap-vue/src/mixins/form-selection.js +0 -60
- package/src/vendor/bootstrap-vue/src/mixins/form-text.js +0 -280
- package/src/vendor/bootstrap-vue/src/mixins/form-validity.js +0 -48
|
@@ -352,57 +352,6 @@ export default {
|
|
|
352
352
|
noWheel() {
|
|
353
353
|
return this.type === 'number';
|
|
354
354
|
},
|
|
355
|
-
selectionStart: {
|
|
356
|
-
// Expose selectionStart for formatters, etc
|
|
357
|
-
cache: false,
|
|
358
|
-
get() {
|
|
359
|
-
return this.$refs.input.selectionStart;
|
|
360
|
-
},
|
|
361
|
-
set(val) {
|
|
362
|
-
this.$refs.input.selectionStart = val;
|
|
363
|
-
},
|
|
364
|
-
},
|
|
365
|
-
selectionEnd: {
|
|
366
|
-
// Expose selectionEnd for formatters, etc
|
|
367
|
-
cache: false,
|
|
368
|
-
get() {
|
|
369
|
-
return this.$refs.input.selectionEnd;
|
|
370
|
-
},
|
|
371
|
-
set(val) {
|
|
372
|
-
this.$refs.input.selectionEnd = val;
|
|
373
|
-
},
|
|
374
|
-
},
|
|
375
|
-
selectionDirection: {
|
|
376
|
-
// Expose selectionDirection for formatters, etc
|
|
377
|
-
cache: false,
|
|
378
|
-
get() {
|
|
379
|
-
return this.$refs.input.selectionDirection;
|
|
380
|
-
},
|
|
381
|
-
set(val) {
|
|
382
|
-
this.$refs.input.selectionDirection = val;
|
|
383
|
-
},
|
|
384
|
-
},
|
|
385
|
-
validity: {
|
|
386
|
-
// Expose validity property
|
|
387
|
-
cache: false,
|
|
388
|
-
get() {
|
|
389
|
-
return this.$refs.input.validity;
|
|
390
|
-
},
|
|
391
|
-
},
|
|
392
|
-
validationMessage: {
|
|
393
|
-
// Expose validationMessage property
|
|
394
|
-
cache: false,
|
|
395
|
-
get() {
|
|
396
|
-
return this.$refs.input.validationMessage;
|
|
397
|
-
},
|
|
398
|
-
},
|
|
399
|
-
willValidate: {
|
|
400
|
-
// Expose willValidate property
|
|
401
|
-
cache: false,
|
|
402
|
-
get() {
|
|
403
|
-
return this.$refs.input.willValidate;
|
|
404
|
-
},
|
|
405
|
-
},
|
|
406
355
|
},
|
|
407
356
|
watch: {
|
|
408
357
|
value(newValue) {
|
|
@@ -510,21 +459,14 @@ export default {
|
|
|
510
459
|
};
|
|
511
460
|
// Only debounce the value update when a value greater than `0`
|
|
512
461
|
// is set and we are not in lazy mode or this is a forced update
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
this.$_inputDebounceTimer = setTimeout(doUpdate, debounce);
|
|
462
|
+
if (this.computedDebounce > 0 && !lazy && !force) {
|
|
463
|
+
this.$_inputDebounceTimer = setTimeout(doUpdate, this.computedDebounce);
|
|
516
464
|
} else {
|
|
517
465
|
// Immediately update the v-model
|
|
518
466
|
doUpdate();
|
|
519
467
|
}
|
|
520
468
|
},
|
|
521
469
|
onInput(event) {
|
|
522
|
-
// `event.target.composing` is set by Vue
|
|
523
|
-
// https://github.com/vuejs/vue/blob/dev/src/platforms/web/runtime/directives/model.js
|
|
524
|
-
// TODO: Is this needed now with the latest Vue?
|
|
525
|
-
if (event.target.composing) {
|
|
526
|
-
return;
|
|
527
|
-
}
|
|
528
470
|
const { value } = event.target;
|
|
529
471
|
const formattedValue = this.formatValue(value, event);
|
|
530
472
|
// Exit when the `formatter` function strictly returned `false`
|
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import { uniqueId } from 'lodash-es';
|
|
3
|
-
import {
|
|
2
|
+
import { uniqueId, isBoolean, toInteger, toString } from 'lodash-es';
|
|
3
|
+
import { toFloat } from '../../../../utils/number_utils';
|
|
4
|
+
import { isVisible, stopEvent } from '../../../../utils/utils';
|
|
5
|
+
import { VBVisible } from '../../../../vendor/bootstrap-vue/src/directives/visible/visible';
|
|
4
6
|
import GlFormCharacterCount from '../form_character_count/form_character_count.vue';
|
|
5
7
|
|
|
6
|
-
const model = {
|
|
7
|
-
prop: 'value',
|
|
8
|
-
event: 'input',
|
|
9
|
-
};
|
|
10
|
-
|
|
11
8
|
export default {
|
|
12
9
|
name: 'GlFormTextarea',
|
|
13
10
|
components: {
|
|
14
|
-
BFormTextarea,
|
|
15
11
|
GlFormCharacterCount,
|
|
16
12
|
},
|
|
13
|
+
directives: {
|
|
14
|
+
'b-visible': VBVisible,
|
|
15
|
+
},
|
|
17
16
|
inheritAttrs: false,
|
|
18
|
-
model
|
|
17
|
+
model: {
|
|
18
|
+
prop: 'value',
|
|
19
|
+
event: 'input',
|
|
20
|
+
},
|
|
19
21
|
props: {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
/**
|
|
23
|
+
* The current value of the textarea.
|
|
24
|
+
*/
|
|
22
25
|
value: {
|
|
23
26
|
type: String,
|
|
24
27
|
required: false,
|
|
@@ -64,32 +67,229 @@ export default {
|
|
|
64
67
|
required: false,
|
|
65
68
|
default: 4,
|
|
66
69
|
},
|
|
70
|
+
/**
|
|
71
|
+
* Used to set the `id` attribute on the rendered content.
|
|
72
|
+
*/
|
|
73
|
+
id: {
|
|
74
|
+
type: String,
|
|
75
|
+
required: false,
|
|
76
|
+
default: undefined,
|
|
77
|
+
},
|
|
78
|
+
/**
|
|
79
|
+
* When set to `true`, attempts to auto-focus the control when it is mounted.
|
|
80
|
+
*/
|
|
81
|
+
autofocus: {
|
|
82
|
+
type: Boolean,
|
|
83
|
+
required: false,
|
|
84
|
+
default: false,
|
|
85
|
+
},
|
|
86
|
+
/**
|
|
87
|
+
* When set to `true`, disables the component's functionality.
|
|
88
|
+
*/
|
|
89
|
+
disabled: {
|
|
90
|
+
type: Boolean,
|
|
91
|
+
required: false,
|
|
92
|
+
default: false,
|
|
93
|
+
},
|
|
94
|
+
/**
|
|
95
|
+
* ID of the form that the form control belongs to. Sets the `form` attribute on the control.
|
|
96
|
+
*/
|
|
97
|
+
form: {
|
|
98
|
+
type: String,
|
|
99
|
+
required: false,
|
|
100
|
+
default: undefined,
|
|
101
|
+
},
|
|
102
|
+
/**
|
|
103
|
+
* Sets the value of the `name` attribute on the form control.
|
|
104
|
+
*/
|
|
105
|
+
name: {
|
|
106
|
+
type: String,
|
|
107
|
+
required: false,
|
|
108
|
+
default: undefined,
|
|
109
|
+
},
|
|
110
|
+
/**
|
|
111
|
+
* Adds the `required` attribute to the form control.
|
|
112
|
+
*/
|
|
113
|
+
required: {
|
|
114
|
+
type: Boolean,
|
|
115
|
+
required: false,
|
|
116
|
+
default: false,
|
|
117
|
+
},
|
|
118
|
+
/**
|
|
119
|
+
* Controls the validation state appearance of the component.
|
|
120
|
+
* `true` for valid, `false` for invalid, or `null` for no validation state.
|
|
121
|
+
*/
|
|
122
|
+
state: {
|
|
123
|
+
type: Boolean,
|
|
124
|
+
required: false,
|
|
125
|
+
default: null,
|
|
126
|
+
},
|
|
127
|
+
/**
|
|
128
|
+
* Optional value to set for the 'aria-invalid' attribute.
|
|
129
|
+
*/
|
|
130
|
+
ariaInvalid: {
|
|
131
|
+
type: [Boolean, String],
|
|
132
|
+
required: false,
|
|
133
|
+
default: false,
|
|
134
|
+
},
|
|
135
|
+
/**
|
|
136
|
+
* Sets the 'autocomplete' attribute value on the form control.
|
|
137
|
+
*/
|
|
138
|
+
autocomplete: {
|
|
139
|
+
type: String,
|
|
140
|
+
required: false,
|
|
141
|
+
default: undefined,
|
|
142
|
+
},
|
|
143
|
+
/**
|
|
144
|
+
* When set to a number of milliseconds greater than zero, will debounce the user input.
|
|
145
|
+
*/
|
|
146
|
+
debounce: {
|
|
147
|
+
type: [Number, String],
|
|
148
|
+
required: false,
|
|
149
|
+
default: 0,
|
|
150
|
+
},
|
|
151
|
+
/**
|
|
152
|
+
* Reference to a function for formatting the input.
|
|
153
|
+
*/
|
|
154
|
+
formatter: {
|
|
155
|
+
type: Function,
|
|
156
|
+
required: false,
|
|
157
|
+
default: undefined,
|
|
158
|
+
},
|
|
159
|
+
/**
|
|
160
|
+
* Sets the `placeholder` attribute value on the form control.
|
|
161
|
+
*/
|
|
162
|
+
placeholder: {
|
|
163
|
+
type: String,
|
|
164
|
+
required: false,
|
|
165
|
+
default: undefined,
|
|
166
|
+
},
|
|
167
|
+
/**
|
|
168
|
+
* Sets the `readonly` attribute on the form control.
|
|
169
|
+
*/
|
|
170
|
+
readonly: {
|
|
171
|
+
type: Boolean,
|
|
172
|
+
required: false,
|
|
173
|
+
default: false,
|
|
174
|
+
},
|
|
175
|
+
/**
|
|
176
|
+
* Set the size of the component's appearance. 'sm' or 'lg'. Defaults to medium size when omitted.
|
|
177
|
+
*/
|
|
178
|
+
size: {
|
|
179
|
+
type: String,
|
|
180
|
+
required: false,
|
|
181
|
+
default: undefined,
|
|
182
|
+
},
|
|
183
|
+
/**
|
|
184
|
+
* The maximum number of rows to show. When set, enables auto-height.
|
|
185
|
+
*/
|
|
186
|
+
maxRows: {
|
|
187
|
+
type: [Number, String],
|
|
188
|
+
required: false,
|
|
189
|
+
default: undefined,
|
|
190
|
+
},
|
|
67
191
|
},
|
|
68
192
|
data() {
|
|
69
193
|
return {
|
|
194
|
+
localValue: toString(this.value),
|
|
195
|
+
vModelValue: this.value,
|
|
196
|
+
localId: null,
|
|
197
|
+
heightInPx: null,
|
|
70
198
|
characterCountTextId: uniqueId('form-textarea-character-count-'),
|
|
71
199
|
};
|
|
72
200
|
},
|
|
73
201
|
computed: {
|
|
74
|
-
|
|
202
|
+
computedId() {
|
|
203
|
+
return this.id || this.localId;
|
|
204
|
+
},
|
|
205
|
+
computedState() {
|
|
206
|
+
return isBoolean(this.state) ? this.state : null;
|
|
207
|
+
},
|
|
208
|
+
stateClass() {
|
|
209
|
+
if (this.computedState === true) return 'is-valid';
|
|
210
|
+
if (this.computedState === false) return 'is-invalid';
|
|
211
|
+
return null;
|
|
212
|
+
},
|
|
213
|
+
computedAriaInvalid() {
|
|
214
|
+
const { ariaInvalid } = this;
|
|
215
|
+
if (ariaInvalid === true || ariaInvalid === 'true' || ariaInvalid === '') {
|
|
216
|
+
return 'true';
|
|
217
|
+
}
|
|
218
|
+
return this.computedState === false ? 'true' : ariaInvalid;
|
|
219
|
+
},
|
|
220
|
+
sizeClass() {
|
|
221
|
+
return this.size ? `form-control-${this.size}` : null;
|
|
222
|
+
},
|
|
223
|
+
computedDebounce() {
|
|
224
|
+
return Math.max(toInteger(this.debounce), 0);
|
|
225
|
+
},
|
|
226
|
+
hasFormatter() {
|
|
227
|
+
return typeof this.formatter === 'function';
|
|
228
|
+
},
|
|
229
|
+
computedMinRows() {
|
|
230
|
+
// Ensure rows is at least 2 and positive (2 is the native textarea value)
|
|
231
|
+
// A value of 1 can cause issues in some browsers, and most browsers
|
|
232
|
+
// only support 2 as the smallest value
|
|
233
|
+
return Math.max(toInteger(this.rows), 2);
|
|
234
|
+
},
|
|
235
|
+
computedMaxRows() {
|
|
236
|
+
return Math.max(this.computedMinRows, toInteger(this.maxRows));
|
|
237
|
+
},
|
|
238
|
+
computedRows() {
|
|
239
|
+
// This is used to set the attribute 'rows' on the textarea
|
|
240
|
+
// If auto-height is enabled, then we return `null` as we use CSS to control height
|
|
241
|
+
return this.computedMinRows === this.computedMaxRows ? this.computedMinRows : null;
|
|
242
|
+
},
|
|
243
|
+
computedStyle() {
|
|
244
|
+
const styles = {
|
|
245
|
+
// Setting `noResize` to true will disable the ability for the user to
|
|
246
|
+
// manually resize the textarea. We also disable when in auto height mode
|
|
247
|
+
resize: !this.computedRows || this.noResize ? 'none' : null,
|
|
248
|
+
};
|
|
249
|
+
if (!this.computedRows) {
|
|
250
|
+
// Conditionally set the computed CSS height when auto rows/height is enabled
|
|
251
|
+
// We avoid setting the style to `null`, which can override user manual resize handle
|
|
252
|
+
styles.height = this.heightInPx;
|
|
253
|
+
// We always add a vertical scrollbar to the textarea when auto-height is
|
|
254
|
+
// enabled so that the computed height calculation returns a stable value
|
|
255
|
+
styles.overflowY = 'scroll';
|
|
256
|
+
}
|
|
257
|
+
return styles;
|
|
258
|
+
},
|
|
259
|
+
computedAttrs() {
|
|
260
|
+
const { disabled, required, readonly } = this;
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
...this.$attrs,
|
|
264
|
+
id: this.computedId,
|
|
265
|
+
name: this.name || null,
|
|
266
|
+
form: this.form || null,
|
|
267
|
+
disabled,
|
|
268
|
+
placeholder: this.placeholder || null,
|
|
269
|
+
required,
|
|
270
|
+
autocomplete: this.autocomplete || null,
|
|
271
|
+
readonly,
|
|
272
|
+
rows: this.computedRows,
|
|
273
|
+
'aria-required': required ? 'true' : null,
|
|
274
|
+
'aria-invalid': this.computedAriaInvalid,
|
|
275
|
+
};
|
|
276
|
+
},
|
|
277
|
+
computedClass() {
|
|
278
|
+
return [
|
|
279
|
+
'gl-form-input',
|
|
280
|
+
'gl-form-textarea',
|
|
281
|
+
'form-control',
|
|
282
|
+
this.textareaClasses,
|
|
283
|
+
this.sizeClass,
|
|
284
|
+
this.stateClass,
|
|
285
|
+
];
|
|
286
|
+
},
|
|
287
|
+
computedListeners() {
|
|
75
288
|
return {
|
|
76
289
|
...this.$listeners,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Emitted to update the v-model
|
|
82
|
-
*/
|
|
83
|
-
this.$emit('update', ...args);
|
|
84
|
-
},
|
|
85
|
-
update: (...args) => {
|
|
86
|
-
/**
|
|
87
|
-
* Triggered by user interaction.
|
|
88
|
-
* Emitted after any formatting (not including 'trim' or 'number' props).
|
|
89
|
-
* Useful for getting the currently entered value when the 'debounce' or 'lazy' props are set.
|
|
90
|
-
*/
|
|
91
|
-
this.$emit(model.event, ...args);
|
|
92
|
-
},
|
|
290
|
+
input: this.onInput,
|
|
291
|
+
change: this.onChange,
|
|
292
|
+
blur: this.onBlur,
|
|
93
293
|
};
|
|
94
294
|
},
|
|
95
295
|
keypressEvent() {
|
|
@@ -98,34 +298,230 @@ export default {
|
|
|
98
298
|
showCharacterCount() {
|
|
99
299
|
return this.characterCountLimit !== null;
|
|
100
300
|
},
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
301
|
+
},
|
|
302
|
+
watch: {
|
|
303
|
+
value(newValue) {
|
|
304
|
+
const stringifyValue = toString(newValue);
|
|
305
|
+
if (stringifyValue !== this.localValue || newValue !== this.vModelValue) {
|
|
306
|
+
this.clearDebounce();
|
|
307
|
+
this.localValue = stringifyValue;
|
|
308
|
+
this.vModelValue = newValue;
|
|
309
|
+
}
|
|
109
310
|
},
|
|
311
|
+
localValue() {
|
|
312
|
+
this.setHeight();
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
created() {
|
|
316
|
+
this.$_inputDebounceTimer = null;
|
|
317
|
+
},
|
|
318
|
+
mounted() {
|
|
319
|
+
this.handleAutofocus();
|
|
320
|
+
this.setHeight();
|
|
321
|
+
this.$nextTick(() => {
|
|
322
|
+
this.localId = uniqueId('gl-form-textarea-');
|
|
323
|
+
});
|
|
324
|
+
},
|
|
325
|
+
beforeDestroy() {
|
|
326
|
+
this.clearDebounce();
|
|
110
327
|
},
|
|
111
328
|
methods: {
|
|
329
|
+
focus() {
|
|
330
|
+
if (!this.disabled) {
|
|
331
|
+
this.$refs.input?.focus();
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
blur() {
|
|
335
|
+
if (!this.disabled) {
|
|
336
|
+
this.$refs.input?.blur();
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
clearDebounce() {
|
|
340
|
+
clearTimeout(this.$_inputDebounceTimer);
|
|
341
|
+
this.$_inputDebounceTimer = null;
|
|
342
|
+
},
|
|
343
|
+
formatValue(value, event) {
|
|
344
|
+
let newValue = toString(value);
|
|
345
|
+
if (this.hasFormatter) {
|
|
346
|
+
newValue = this.formatter(newValue, event);
|
|
347
|
+
}
|
|
348
|
+
return newValue;
|
|
349
|
+
},
|
|
350
|
+
updateValue(value, force = false) {
|
|
351
|
+
this.clearDebounce();
|
|
352
|
+
|
|
353
|
+
const doUpdate = () => {
|
|
354
|
+
if (value !== this.vModelValue) {
|
|
355
|
+
this.vModelValue = value;
|
|
356
|
+
/**
|
|
357
|
+
* Triggered by user interaction.
|
|
358
|
+
* Emitted after any formatting (not including 'trim' or 'number' props).
|
|
359
|
+
* Useful for getting the currently entered value when the 'debounce'is set.
|
|
360
|
+
*
|
|
361
|
+
* @event input
|
|
362
|
+
*/
|
|
363
|
+
this.$emit('input', value);
|
|
364
|
+
} else if (this.hasFormatter) {
|
|
365
|
+
const { input } = this.$refs;
|
|
366
|
+
if (input && value !== input.value) {
|
|
367
|
+
input.value = value;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
if (this.computedDebounce > 0 && !force) {
|
|
373
|
+
this.$_inputDebounceTimer = setTimeout(doUpdate, this.computedDebounce);
|
|
374
|
+
} else {
|
|
375
|
+
doUpdate();
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
onInput(event) {
|
|
379
|
+
const { value } = event.target;
|
|
380
|
+
const formattedValue = this.formatValue(value, event);
|
|
381
|
+
if (formattedValue === false || event.defaultPrevented) {
|
|
382
|
+
stopEvent(event, { propagation: false });
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
this.localValue = formattedValue;
|
|
386
|
+
this.updateValue(formattedValue);
|
|
387
|
+
/**
|
|
388
|
+
* The `input` and `update` events are swapped
|
|
389
|
+
* see https://gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/1628.
|
|
390
|
+
*
|
|
391
|
+
* @event update
|
|
392
|
+
*/
|
|
393
|
+
this.$emit('update', formattedValue);
|
|
394
|
+
},
|
|
395
|
+
onChange(event) {
|
|
396
|
+
const { value } = event.target;
|
|
397
|
+
const formattedValue = this.formatValue(value, event);
|
|
398
|
+
if (formattedValue === false || event.defaultPrevented) {
|
|
399
|
+
stopEvent(event, { propagation: false });
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
this.localValue = formattedValue;
|
|
403
|
+
this.updateValue(formattedValue, true);
|
|
404
|
+
/**
|
|
405
|
+
* Change event triggered by user interaction.
|
|
406
|
+
* Emitted after any formatting (not including 'trim' or 'number' props)
|
|
407
|
+
* and after the v-model is updated. The `input` and `update` events are swapped
|
|
408
|
+
* see https://gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/1628.
|
|
409
|
+
*
|
|
410
|
+
* @event change
|
|
411
|
+
*/
|
|
412
|
+
this.$emit('change', formattedValue);
|
|
413
|
+
},
|
|
414
|
+
onBlur(event) {
|
|
415
|
+
const { value } = event.target;
|
|
416
|
+
const formattedValue = this.formatValue(value, event);
|
|
417
|
+
if (formattedValue !== false) {
|
|
418
|
+
this.localValue = toString(formattedValue);
|
|
419
|
+
this.updateValue(formattedValue, true);
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Emitted after the textarea loses focus
|
|
423
|
+
*
|
|
424
|
+
* @event blur
|
|
425
|
+
*/
|
|
426
|
+
this.$emit('blur', event);
|
|
427
|
+
},
|
|
428
|
+
handleAutofocus() {
|
|
429
|
+
this.$nextTick(() => {
|
|
430
|
+
window.requestAnimationFrame(() => {
|
|
431
|
+
if (this.autofocus && isVisible(this.$refs.input)) {
|
|
432
|
+
this.focus();
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
},
|
|
112
437
|
handleKeyPress(e) {
|
|
113
|
-
if (e.keyCode === 13 && (e.metaKey || e.ctrlKey)) {
|
|
438
|
+
if (this.submitOnEnter && e.keyCode === 13 && (e.metaKey || e.ctrlKey)) {
|
|
439
|
+
/**
|
|
440
|
+
* Emitted after enter is pressed in textarea
|
|
441
|
+
*
|
|
442
|
+
* @event submit
|
|
443
|
+
*/
|
|
114
444
|
this.$emit('submit');
|
|
115
445
|
}
|
|
116
446
|
},
|
|
447
|
+
visibleCallback(visible) {
|
|
448
|
+
if (visible) {
|
|
449
|
+
this.$nextTick(this.setHeight);
|
|
450
|
+
}
|
|
451
|
+
},
|
|
452
|
+
setHeight() {
|
|
453
|
+
this.$nextTick(() => {
|
|
454
|
+
window.requestAnimationFrame(() => {
|
|
455
|
+
this.heightInPx = this.computeHeight();
|
|
456
|
+
});
|
|
457
|
+
});
|
|
458
|
+
},
|
|
459
|
+
computeHeight() {
|
|
460
|
+
if (this.computedRows !== null) {
|
|
461
|
+
return null;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const el = this.$refs.input;
|
|
465
|
+
|
|
466
|
+
if (!el || !isVisible(el)) {
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const computedStyle = getComputedStyle(el);
|
|
471
|
+
const lineHeight = toFloat(computedStyle.lineHeight, 1);
|
|
472
|
+
const border =
|
|
473
|
+
toFloat(computedStyle.borderTopWidth, 0) + toFloat(computedStyle.borderBottomWidth, 0);
|
|
474
|
+
const padding =
|
|
475
|
+
toFloat(computedStyle.paddingTop, 0) + toFloat(computedStyle.paddingBottom, 0);
|
|
476
|
+
const offset = border + padding;
|
|
477
|
+
const minHeight = lineHeight * this.computedMinRows + offset;
|
|
478
|
+
|
|
479
|
+
const oldHeight = el.style.height || computedStyle.height;
|
|
480
|
+
el.style.height = 'auto';
|
|
481
|
+
const { scrollHeight } = el;
|
|
482
|
+
el.style.height = oldHeight;
|
|
483
|
+
|
|
484
|
+
const contentRows = Math.max((scrollHeight - padding) / lineHeight, 2);
|
|
485
|
+
const rows = Math.min(Math.max(contentRows, this.computedMinRows), this.computedMaxRows);
|
|
486
|
+
const height = Math.max(Math.ceil(rows * lineHeight + offset), minHeight);
|
|
487
|
+
|
|
488
|
+
return `${height}px`;
|
|
489
|
+
},
|
|
490
|
+
select(...args) {
|
|
491
|
+
this.$refs.input.select(...args);
|
|
492
|
+
},
|
|
493
|
+
setSelectionRange(...args) {
|
|
494
|
+
this.$refs.input.setSelectionRange(...args);
|
|
495
|
+
},
|
|
496
|
+
setRangeText(...args) {
|
|
497
|
+
this.$refs.input.setRangeText(...args);
|
|
498
|
+
},
|
|
499
|
+
setCustomValidity(...args) {
|
|
500
|
+
return this.$refs.input.setCustomValidity(...args);
|
|
501
|
+
},
|
|
502
|
+
checkValidity(...args) {
|
|
503
|
+
return this.$refs.input.checkValidity(...args);
|
|
504
|
+
},
|
|
505
|
+
reportValidity(...args) {
|
|
506
|
+
return this.$refs.input.reportValidity(...args);
|
|
507
|
+
},
|
|
117
508
|
},
|
|
118
509
|
};
|
|
119
510
|
</script>
|
|
120
511
|
|
|
121
512
|
<template>
|
|
122
513
|
<div v-if="showCharacterCount">
|
|
123
|
-
<
|
|
124
|
-
|
|
514
|
+
<textarea
|
|
515
|
+
ref="input"
|
|
516
|
+
v-b-visible.640="visibleCallback"
|
|
517
|
+
:value="localValue"
|
|
518
|
+
:class="computedClass"
|
|
519
|
+
:style="computedStyle"
|
|
520
|
+
v-bind="computedAttrs"
|
|
125
521
|
:aria-describedby="characterCountTextId"
|
|
126
|
-
v-on="
|
|
127
|
-
@
|
|
128
|
-
|
|
522
|
+
v-on="computedListeners"
|
|
523
|
+
@keyup.enter="handleKeyPress"
|
|
524
|
+
></textarea>
|
|
129
525
|
<gl-form-character-count
|
|
130
526
|
:value="value"
|
|
131
527
|
:limit="characterCountLimit"
|
|
@@ -133,7 +529,13 @@ export default {
|
|
|
133
529
|
>
|
|
134
530
|
<template #over-limit-text="{ count }">
|
|
135
531
|
<!--
|
|
136
|
-
@slot Internationalized over character count text.
|
|
532
|
+
@slot Internationalized over character count text.
|
|
533
|
+
Example:
|
|
534
|
+
```
|
|
535
|
+
<template #character-count-over-limit-text="{ count }">
|
|
536
|
+
{{ n__('%d character over limit.', '%d characters over limit.', count) }}
|
|
537
|
+
</template>
|
|
538
|
+
```
|
|
137
539
|
@binding {number} count
|
|
138
540
|
-->
|
|
139
541
|
<slot name="character-count-over-limit-text" :count="count"></slot>
|
|
@@ -141,7 +543,13 @@ export default {
|
|
|
141
543
|
|
|
142
544
|
<template #remaining-count-text="{ count }">
|
|
143
545
|
<!--
|
|
144
|
-
@slot Internationalized character count text.
|
|
546
|
+
@slot Internationalized character count text.
|
|
547
|
+
Example:
|
|
548
|
+
```
|
|
549
|
+
<template #remaining-character-count-text="{ count }">
|
|
550
|
+
{{ n__('%d character remaining.', '%d characters remaining.', count) }}
|
|
551
|
+
</template>
|
|
552
|
+
```
|
|
145
553
|
@binding {number} count
|
|
146
554
|
-->
|
|
147
555
|
|
|
@@ -149,10 +557,15 @@ export default {
|
|
|
149
557
|
></template>
|
|
150
558
|
</gl-form-character-count>
|
|
151
559
|
</div>
|
|
152
|
-
<
|
|
560
|
+
<textarea
|
|
153
561
|
v-else
|
|
154
|
-
|
|
155
|
-
v-
|
|
156
|
-
|
|
157
|
-
|
|
562
|
+
ref="input"
|
|
563
|
+
v-b-visible.640="visibleCallback"
|
|
564
|
+
:value="localValue"
|
|
565
|
+
:class="computedClass"
|
|
566
|
+
:style="computedStyle"
|
|
567
|
+
v-bind="computedAttrs"
|
|
568
|
+
v-on="computedListeners"
|
|
569
|
+
@keyup.enter="handleKeyPress"
|
|
570
|
+
></textarea>
|
|
158
571
|
</template>
|
|
@@ -18,7 +18,6 @@ export const NAME_FORM_SELECT = 'BFormSelect'
|
|
|
18
18
|
export const NAME_FORM_SELECT_OPTION = 'BFormSelectOption'
|
|
19
19
|
export const NAME_FORM_SELECT_OPTION_GROUP = 'BFormSelectOptionGroup'
|
|
20
20
|
export const NAME_FORM_TEXT = 'BFormText'
|
|
21
|
-
export const NAME_FORM_TEXTAREA = 'BFormTextarea'
|
|
22
21
|
export const NAME_FORM_VALID_FEEDBACK = 'BFormValidFeedback'
|
|
23
22
|
export const NAME_LINK = 'BLink'
|
|
24
23
|
export const NAME_MODAL = 'BModal'
|