@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.
@@ -1,233 +0,0 @@
1
- import { extend } from '../../vue';
2
- import { NAME_FORM_TEXTAREA } from '../../constants/components';
3
- import { requestAF, isVisible, getCS, getStyle, setStyle } from '../../utils/dom';
4
- import { isNull } from '../../utils/inspect';
5
- import { mathMax, mathMin, mathCeil } from '../../utils/math';
6
- import { toInteger, toFloat } from '../../utils/number';
7
- import { sortKeys } from '../../utils/object';
8
- import { props as props$2, formControlMixin } from '../../mixins/form-control';
9
- import { formSelectionMixin } from '../../mixins/form-selection';
10
- import { props as props$3, formSizeMixin } from '../../mixins/form-size';
11
- import { props as props$4, formStateMixin } from '../../mixins/form-state';
12
- import { props as props$5, formTextMixin } from '../../mixins/form-text';
13
- import { formValidityMixin } from '../../mixins/form-validity';
14
- import { props as props$1, idMixin } from '../../mixins/id';
15
- import { listenOnRootMixin } from '../../mixins/listen-on-root';
16
- import { listenersMixin } from '../../mixins/listeners';
17
- import { VBVisible } from '../../directives/visible/visible';
18
-
19
- // --- Props ---
20
-
21
- const props = sortKeys({
22
- ...props$1,
23
- ...props$2,
24
- ...props$3,
25
- ...props$4,
26
- ...props$5,
27
- maxRows: {
28
- type: [Number, String],
29
- required: false,
30
- default: undefined
31
- },
32
- // When in auto resize mode, disable shrinking to content height
33
- noAutoShrink: {
34
- type: Boolean,
35
- required: false,
36
- default: false
37
- },
38
- // Disable the resize handle of textarea
39
- noResize: {
40
- type: Boolean,
41
- required: false,
42
- default: false
43
- },
44
- rows: {
45
- type: [Number, String],
46
- required: false,
47
- default: 2
48
- },
49
- // 'soft', 'hard' or 'off'
50
- // Browser default is 'soft'
51
- wrap: {
52
- type: String,
53
- required: false,
54
- default: 'soft'
55
- }
56
- });
57
-
58
- // --- Main component ---
59
-
60
- // @vue/component
61
- const BFormTextarea = /*#__PURE__*/extend({
62
- name: NAME_FORM_TEXTAREA,
63
- directives: {
64
- 'b-visible': VBVisible
65
- },
66
- // Mixin order is important!
67
- mixins: [listenersMixin, idMixin, listenOnRootMixin, formControlMixin, formSizeMixin, formStateMixin, formTextMixin, formSelectionMixin, formValidityMixin],
68
- props,
69
- data() {
70
- return {
71
- heightInPx: null
72
- };
73
- },
74
- computed: {
75
- type() {
76
- return null;
77
- },
78
- computedStyle() {
79
- const styles = {
80
- // Setting `noResize` to true will disable the ability for the user to
81
- // manually resize the textarea. We also disable when in auto height mode
82
- resize: !this.computedRows || this.noResize ? 'none' : null
83
- };
84
- if (!this.computedRows) {
85
- // Conditionally set the computed CSS height when auto rows/height is enabled
86
- // We avoid setting the style to `null`, which can override user manual resize handle
87
- styles.height = this.heightInPx;
88
- // We always add a vertical scrollbar to the textarea when auto-height is
89
- // enabled so that the computed height calculation returns a stable value
90
- styles.overflowY = 'scroll';
91
- }
92
- return styles;
93
- },
94
- computedMinRows() {
95
- // Ensure rows is at least 2 and positive (2 is the native textarea value)
96
- // A value of 1 can cause issues in some browsers, and most browsers
97
- // only support 2 as the smallest value
98
- return mathMax(toInteger(this.rows, 2), 2);
99
- },
100
- computedMaxRows() {
101
- return mathMax(this.computedMinRows, toInteger(this.maxRows, 0));
102
- },
103
- computedRows() {
104
- // This is used to set the attribute 'rows' on the textarea
105
- // If auto-height is enabled, then we return `null` as we use CSS to control height
106
- return this.computedMinRows === this.computedMaxRows ? this.computedMinRows : null;
107
- },
108
- computedAttrs() {
109
- const disabled = this.disabled,
110
- required = this.required;
111
- return {
112
- id: this.safeId(),
113
- name: this.name || null,
114
- form: this.form || null,
115
- disabled,
116
- placeholder: this.placeholder || null,
117
- required,
118
- autocomplete: this.autocomplete || null,
119
- readonly: this.readonly || this.plaintext,
120
- rows: this.computedRows,
121
- wrap: this.wrap || null,
122
- 'aria-required': this.required ? 'true' : null,
123
- 'aria-invalid': this.computedAriaInvalid
124
- };
125
- },
126
- computedListeners() {
127
- return {
128
- ...this.bvListeners,
129
- input: this.onInput,
130
- change: this.onChange,
131
- blur: this.onBlur
132
- };
133
- }
134
- },
135
- watch: {
136
- localValue() {
137
- this.setHeight();
138
- }
139
- },
140
- mounted() {
141
- this.setHeight();
142
- },
143
- methods: {
144
- // Called by intersection observer directive
145
- /* istanbul ignore next */
146
- visibleCallback(visible) {
147
- if (visible) {
148
- // We use a `$nextTick()` here just to make sure any
149
- // transitions or portalling have completed
150
- this.$nextTick(this.setHeight);
151
- }
152
- },
153
- setHeight() {
154
- this.$nextTick(() => {
155
- requestAF(() => {
156
- this.heightInPx = this.computeHeight();
157
- });
158
- });
159
- },
160
- /* istanbul ignore next: can't test getComputedStyle in JSDOM */
161
- computeHeight() {
162
- if (this.$isServer || !isNull(this.computedRows)) {
163
- return null;
164
- }
165
- const el = this.$el;
166
-
167
- // Element must be visible (not hidden) and in document
168
- // Must be checked after above checks
169
- if (!isVisible(el)) {
170
- return null;
171
- }
172
-
173
- // Get current computed styles
174
- const computedStyle = getCS(el);
175
- // Height of one line of text in px
176
- const lineHeight = toFloat(computedStyle.lineHeight, 1);
177
- // Calculate height of border and padding
178
- const border = toFloat(computedStyle.borderTopWidth, 0) + toFloat(computedStyle.borderBottomWidth, 0);
179
- const padding = toFloat(computedStyle.paddingTop, 0) + toFloat(computedStyle.paddingBottom, 0);
180
- // Calculate offset
181
- const offset = border + padding;
182
- // Minimum height for min rows (which must be 2 rows or greater for cross-browser support)
183
- const minHeight = lineHeight * this.computedMinRows + offset;
184
-
185
- // Get the current style height (with `px` units)
186
- const oldHeight = getStyle(el, 'height') || computedStyle.height;
187
- // Probe scrollHeight by temporarily changing the height to `auto`
188
- setStyle(el, 'height', 'auto');
189
- const scrollHeight = el.scrollHeight;
190
- // Place the original old height back on the element, just in case `computedProp`
191
- // returns the same value as before
192
- setStyle(el, 'height', oldHeight);
193
-
194
- // Calculate content height in 'rows' (scrollHeight includes padding but not border)
195
- const contentRows = mathMax((scrollHeight - padding) / lineHeight, 2);
196
- // Calculate number of rows to display (limited within min/max rows)
197
- const rows = mathMin(mathMax(contentRows, this.computedMinRows), this.computedMaxRows);
198
- // Calculate the required height of the textarea including border and padding (in pixels)
199
- const height = mathMax(mathCeil(rows * lineHeight + offset), minHeight);
200
-
201
- // Computed height remains the larger of `oldHeight` and new `height`,
202
- // when height is in `sticky` mode (prop `no-auto-shrink` is true)
203
- if (this.noAutoShrink && toFloat(oldHeight, 0) > height) {
204
- return oldHeight;
205
- }
206
-
207
- // Return the new computed CSS height in px units
208
- return `${height}px`;
209
- }
210
- },
211
- render(h) {
212
- return h('textarea', {
213
- class: this.computedClass,
214
- style: this.computedStyle,
215
- directives: [{
216
- name: 'b-visible',
217
- value: this.visibleCallback,
218
- // If textarea is within 640px of viewport, consider it visible
219
- modifiers: {
220
- '640': true
221
- }
222
- }],
223
- attrs: this.computedAttrs,
224
- domProps: {
225
- value: this.localValue
226
- },
227
- on: this.computedListeners,
228
- ref: 'input'
229
- });
230
- }
231
- });
232
-
233
- export { BFormTextarea, props };
@@ -1 +0,0 @@
1
- export { BFormTextarea } from './form-textarea';
@@ -1,62 +0,0 @@
1
- import { extend } from '../vue';
2
-
3
- // @vue/component
4
- const formSelectionMixin = extend({
5
- computed: {
6
- selectionStart: {
7
- // Expose selectionStart for formatters, etc
8
- cache: false,
9
- /* istanbul ignore next */
10
- get() {
11
- return this.$refs.input.selectionStart;
12
- },
13
- /* istanbul ignore next */
14
- set(val) {
15
- this.$refs.input.selectionStart = val;
16
- }
17
- },
18
- selectionEnd: {
19
- // Expose selectionEnd for formatters, etc
20
- cache: false,
21
- /* istanbul ignore next */
22
- get() {
23
- return this.$refs.input.selectionEnd;
24
- },
25
- /* istanbul ignore next */
26
- set(val) {
27
- this.$refs.input.selectionEnd = val;
28
- }
29
- },
30
- selectionDirection: {
31
- // Expose selectionDirection for formatters, etc
32
- cache: false,
33
- /* istanbul ignore next */
34
- get() {
35
- return this.$refs.input.selectionDirection;
36
- },
37
- /* istanbul ignore next */
38
- set(val) {
39
- this.$refs.input.selectionDirection = val;
40
- }
41
- }
42
- },
43
- methods: {
44
- /* istanbul ignore next */
45
- select() {
46
- // For external handler that may want a select() method
47
- this.$refs.input.select(...arguments);
48
- },
49
- /* istanbul ignore next */
50
- setSelectionRange() {
51
- // For external handler that may want a setSelectionRange(a,b,c) method
52
- this.$refs.input.setSelectionRange(...arguments);
53
- },
54
- /* istanbul ignore next */
55
- setRangeText() {
56
- // For external handler that may want a setRangeText(a,b,c) method
57
- this.$refs.input.setRangeText(...arguments);
58
- }
59
- }
60
- });
61
-
62
- export { formSelectionMixin };
@@ -1,279 +0,0 @@
1
- import { extend } from '../vue';
2
- import { EVENT_NAME_INPUT, EVENT_NAME_CHANGE, EVENT_NAME_BLUR, EVENT_NAME_UPDATE } from '../constants/events';
3
- import { attemptFocus, attemptBlur } from '../utils/dom';
4
- import { stopEvent } from '../utils/events';
5
- import { mathMax } from '../utils/math';
6
- import { toInteger, toFloat } from '../utils/number';
7
- import { sortKeys } from '../utils/object';
8
- import { isFunction } from '../utils/inspect';
9
- import { toString } from '../utils/string';
10
-
11
- // --- Constants ---
12
-
13
- const MODEL_PROP_NAME = 'value';
14
- const MODEL_EVENT_NAME = EVENT_NAME_UPDATE;
15
-
16
- // --- Props ---
17
-
18
- const props = sortKeys({
19
- [MODEL_PROP_NAME]: {
20
- type: [Number, String],
21
- default: ''
22
- },
23
- ariaInvalid: {
24
- type: [Boolean, String],
25
- required: false,
26
- default: false
27
- },
28
- autocomplete: {
29
- type: String,
30
- required: false,
31
- default: undefined
32
- },
33
- // Debounce timeout (in ms). Not applicable with `lazy` prop
34
- debounce: {
35
- type: [Number, String],
36
- required: false,
37
- default: 0
38
- },
39
- formatter: {
40
- type: Function,
41
- required: false,
42
- default: undefined
43
- },
44
- // Only update the `v-model` on blur/change events
45
- lazy: {
46
- type: Boolean,
47
- required: false,
48
- default: false
49
- },
50
- lazyFormatter: {
51
- type: Boolean,
52
- required: false,
53
- default: false
54
- },
55
- number: {
56
- type: Boolean,
57
- required: false,
58
- default: false
59
- },
60
- placeholder: {
61
- type: String,
62
- required: false,
63
- default: undefined
64
- },
65
- plaintext: {
66
- type: Boolean,
67
- required: false,
68
- default: false
69
- },
70
- readonly: {
71
- type: Boolean,
72
- required: false,
73
- default: false
74
- },
75
- trim: {
76
- type: Boolean,
77
- required: false,
78
- default: false
79
- }
80
- });
81
-
82
- // --- Mixin ---
83
-
84
- // @vue/component
85
- const formTextMixin = extend({
86
- model: {
87
- prop: MODEL_PROP_NAME,
88
- event: MODEL_EVENT_NAME
89
- },
90
- props,
91
- data() {
92
- const value = this[MODEL_PROP_NAME];
93
- return {
94
- localValue: toString(value),
95
- vModelValue: this.modifyValue(value)
96
- };
97
- },
98
- computed: {
99
- computedClass() {
100
- const plaintext = this.plaintext,
101
- type = this.type;
102
- const isRange = type === 'range';
103
- const isColor = type === 'color';
104
- return [{
105
- // Range input needs class `custom-range`
106
- 'custom-range': isRange,
107
- // `plaintext` not supported by `type="range"` or `type="color"`
108
- 'form-control-plaintext': plaintext && !isRange && !isColor,
109
- // `form-control` not used by `type="range"` or `plaintext`
110
- // Always used by `type="color"`
111
- 'form-control': isColor || !plaintext && !isRange
112
- }, this.sizeFormClass, this.stateClass];
113
- },
114
- computedDebounce() {
115
- // Ensure we have a positive number equal to or greater than 0
116
- return mathMax(toInteger(this.debounce, 0), 0);
117
- },
118
- hasFormatter() {
119
- // `formatter` is an optional Function prop with no default
120
- return isFunction(this.formatter);
121
- }
122
- },
123
- watch: {
124
- [MODEL_PROP_NAME](newValue) {
125
- const stringifyValue = toString(newValue);
126
- const modifiedValue = this.modifyValue(newValue);
127
- if (stringifyValue !== this.localValue || modifiedValue !== this.vModelValue) {
128
- // Clear any pending debounce timeout, as we are overwriting the user input
129
- this.clearDebounce();
130
- // Update the local values
131
- this.localValue = stringifyValue;
132
- this.vModelValue = modifiedValue;
133
- }
134
- }
135
- },
136
- created() {
137
- // Create private non-reactive props
138
- this.$_inputDebounceTimer = null;
139
- },
140
- beforeDestroy() {
141
- this.clearDebounce();
142
- },
143
- methods: {
144
- clearDebounce() {
145
- clearTimeout(this.$_inputDebounceTimer);
146
- this.$_inputDebounceTimer = null;
147
- },
148
- formatValue(value, event) {
149
- let force = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
150
- value = toString(value);
151
- if (this.hasFormatter && (!this.lazyFormatter || force)) {
152
- value = this.formatter(value, event);
153
- }
154
- return value;
155
- },
156
- modifyValue(value) {
157
- value = toString(value);
158
- // Emulate `.trim` modifier behaviour
159
- if (this.trim) {
160
- value = value.trim();
161
- }
162
- // Emulate `.number` modifier behaviour
163
- if (this.number) {
164
- value = toFloat(value, value);
165
- }
166
- return value;
167
- },
168
- updateValue(value) {
169
- let force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
170
- const lazy = this.lazy;
171
- if (lazy && !force) {
172
- return;
173
- }
174
- // Make sure to always clear the debounce when `updateValue()`
175
- // is called, even when the v-model hasn't changed
176
- this.clearDebounce();
177
- // Define the shared update logic in a method to be able to use
178
- // it for immediate and debounced value changes
179
- const doUpdate = () => {
180
- value = this.modifyValue(value);
181
- if (value !== this.vModelValue) {
182
- this.vModelValue = value;
183
- this.$emit(MODEL_EVENT_NAME, value);
184
- } else if (this.hasFormatter) {
185
- // When the `vModelValue` hasn't changed but the actual input value
186
- // is out of sync, make sure to change it to the given one
187
- // Usually caused by browser autocomplete and how it triggers the
188
- // change or input event, or depending on the formatter function
189
- // https://github.com/bootstrap-vue/bootstrap-vue/issues/2657
190
- // https://github.com/bootstrap-vue/bootstrap-vue/issues/3498
191
- /* istanbul ignore next: hard to test */
192
- const $input = this.$refs.input;
193
- /* istanbul ignore if: hard to test out of sync value */
194
- if ($input && value !== $input.value) {
195
- $input.value = value;
196
- }
197
- }
198
- };
199
- // Only debounce the value update when a value greater than `0`
200
- // is set and we are not in lazy mode or this is a forced update
201
- const debounce = this.computedDebounce;
202
- if (debounce > 0 && !lazy && !force) {
203
- this.$_inputDebounceTimer = setTimeout(doUpdate, debounce);
204
- } else {
205
- // Immediately update the v-model
206
- doUpdate();
207
- }
208
- },
209
- onInput(event) {
210
- // `event.target.composing` is set by Vue
211
- // https://github.com/vuejs/vue/blob/dev/src/platforms/web/runtime/directives/model.js
212
- // TODO: Is this needed now with the latest Vue?
213
- /* istanbul ignore if: hard to test composition events */
214
- if (event.target.composing) {
215
- return;
216
- }
217
- const value = event.target.value;
218
- const formattedValue = this.formatValue(value, event);
219
- // Exit when the `formatter` function strictly returned `false`
220
- // or prevented the input event
221
- /* istanbul ignore next */
222
- if (formattedValue === false || event.defaultPrevented) {
223
- stopEvent(event, {
224
- propagation: false
225
- });
226
- return;
227
- }
228
- this.localValue = formattedValue;
229
- this.updateValue(formattedValue);
230
- this.$emit(EVENT_NAME_INPUT, formattedValue);
231
- },
232
- onChange(event) {
233
- const value = event.target.value;
234
- const formattedValue = this.formatValue(value, event);
235
- // Exit when the `formatter` function strictly returned `false`
236
- // or prevented the input event
237
- /* istanbul ignore next */
238
- if (formattedValue === false || event.defaultPrevented) {
239
- stopEvent(event, {
240
- propagation: false
241
- });
242
- return;
243
- }
244
- this.localValue = formattedValue;
245
- this.updateValue(formattedValue, true);
246
- this.$emit(EVENT_NAME_CHANGE, formattedValue);
247
- },
248
- onBlur(event) {
249
- // Apply the `localValue` on blur to prevent cursor jumps
250
- // on mobile browsers (e.g. caused by autocomplete)
251
- const value = event.target.value;
252
- const formattedValue = this.formatValue(value, event, true);
253
- if (formattedValue !== false) {
254
- // We need to use the modified value here to apply the
255
- // `.trim` and `.number` modifiers properly
256
- this.localValue = toString(this.modifyValue(formattedValue));
257
- // We pass the formatted value here since the `updateValue` method
258
- // handles the modifiers itself
259
- this.updateValue(formattedValue, true);
260
- }
261
- // Emit native blur event
262
- this.$emit(EVENT_NAME_BLUR, event);
263
- },
264
- focus() {
265
- // For external handler that may want a focus method
266
- if (!this.disabled) {
267
- attemptFocus(this.$el);
268
- }
269
- },
270
- blur() {
271
- // For external handler that may want a blur method
272
- if (!this.disabled) {
273
- attemptBlur(this.$el);
274
- }
275
- }
276
- }
277
- });
278
-
279
- export { formTextMixin, props };
@@ -1,50 +0,0 @@
1
- import { extend } from '../vue';
2
-
3
- // @vue/component
4
- const formValidityMixin = extend({
5
- computed: {
6
- validity: {
7
- // Expose validity property
8
- cache: false,
9
- /* istanbul ignore next */
10
- get() {
11
- return this.$refs.input.validity;
12
- }
13
- },
14
- validationMessage: {
15
- // Expose validationMessage property
16
- cache: false,
17
- /* istanbul ignore next */
18
- get() {
19
- return this.$refs.input.validationMessage;
20
- }
21
- },
22
- willValidate: {
23
- // Expose willValidate property
24
- cache: false,
25
- /* istanbul ignore next */
26
- get() {
27
- return this.$refs.input.willValidate;
28
- }
29
- }
30
- },
31
- methods: {
32
- /* istanbul ignore next */
33
- setCustomValidity() {
34
- // For external handler that may want a setCustomValidity(...) method
35
- return this.$refs.input.setCustomValidity(...arguments);
36
- },
37
- /* istanbul ignore next */
38
- checkValidity() {
39
- // For external handler that may want a checkValidity(...) method
40
- return this.$refs.input.checkValidity(...arguments);
41
- },
42
- /* istanbul ignore next */
43
- reportValidity() {
44
- // For external handler that may want a reportValidity(...) method
45
- return this.$refs.input.reportValidity(...arguments);
46
- }
47
- }
48
- });
49
-
50
- export { formValidityMixin };