@gitlab/ui 137.3.1 → 138.1.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/README.md +3 -12
- package/dist/components/base/alert/alert.js +0 -1
- package/dist/components/base/drawer/drawer.js +0 -1
- package/dist/components/base/filtered_search/filtered_search_suggestion_list.js +1 -1
- package/dist/components/base/filtered_search/filtered_search_token_segment.js +0 -1
- package/dist/components/base/form/form_group/form_group.js +0 -1
- package/dist/components/base/form/form_select/form_select.js +322 -6
- package/dist/components/base/modal/modal.js +1 -5
- package/dist/components/base/new_dropdowns/base_dropdown/dropdown_container.js +1 -1
- package/dist/components/base/new_dropdowns/disclosure/disclosure_dropdown.js +1 -3
- package/dist/components/base/toast/index.js +1 -1
- package/dist/components/base/toggle/toggle.js +0 -1
- package/dist/components/mixins/tooltip_mixin.js +1 -1
- package/dist/index.css +2 -2
- package/dist/index.css.map +1 -1
- package/dist/tailwind.css +1 -1
- package/dist/tailwind.css.map +1 -1
- package/dist/tokens/build/js/tokens.dark.js +299 -299
- package/dist/tokens/build/js/tokens.js +302 -302
- package/dist/utils/confetti/confetti.js +189 -0
- package/dist/utils/confetti/constants.js +36 -0
- package/dist/utils/confetti/field.js +114 -0
- package/dist/utils/confetti/palette.js +6 -0
- package/dist/utils/confetti/particles.js +92 -0
- package/dist/utils/matrix/columns.js +54 -0
- package/dist/utils/matrix/constants.js +19 -0
- package/dist/utils/matrix/matrix.js +157 -0
- package/dist/utils/matrix/palette.js +9 -0
- package/dist/utils.js +2 -0
- package/dist/vendor/bootstrap-vue/src/components/popover/helpers/bv-popover-template.js +4 -2
- package/dist/vendor/bootstrap-vue/src/components/tooltip/helpers/bv-tooltip-template.js +3 -1
- package/dist/vendor/bootstrap-vue/src/constants/components.js +1 -4
- package/package.json +4 -11
- package/src/components/base/alert/alert.vue +0 -1
- package/src/components/base/drawer/drawer.vue +0 -1
- package/src/components/base/filtered_search/filtered_search_suggestion_list.vue +2 -1
- package/src/components/base/filtered_search/filtered_search_token_segment.vue +0 -1
- package/src/components/base/form/form_group/form_group.vue +0 -2
- package/src/components/base/form/form_select/form_select.vue +389 -10
- package/src/components/base/modal/modal.vue +0 -3
- package/src/components/base/new_dropdowns/base_dropdown/dropdown_container.js +1 -1
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown.vue +0 -1
- package/src/components/base/tabs/tabs/scrollable_tabs.vue +0 -1
- package/src/components/base/tabs/tabs/tabs.vue +0 -1
- package/src/components/base/toast/index.js +1 -1
- package/src/components/base/toggle/toggle.vue +0 -1
- package/src/components/charts/heatmap/index.js +1 -1
- package/src/components/mixins/tooltip_mixin.js +1 -1
- package/src/tokens/build/css/tokens.css +74 -74
- package/src/tokens/build/css/tokens.dark.css +70 -70
- package/src/tokens/build/docs/tokens-tailwind-docs.dark.json +303 -303
- package/src/tokens/build/docs/tokens-tailwind-docs.json +323 -323
- package/src/tokens/build/figma/constants.dark.json +207 -207
- package/src/tokens/build/figma/constants.json +207 -207
- package/src/tokens/build/figma/mode.dark.json +37 -37
- package/src/tokens/build/figma/mode.json +35 -35
- package/src/tokens/build/js/tokens.dark.js +299 -300
- package/src/tokens/build/js/tokens.js +302 -303
- package/src/tokens/build/json/tokens.dark.json +369 -369
- package/src/tokens/build/json/tokens.json +376 -376
- package/src/tokens/build/scss/_tokens.dark.scss +70 -70
- package/src/tokens/build/scss/_tokens.scss +74 -74
- package/src/tokens/constant/color.blue.tokens.json +39 -39
- package/src/tokens/constant/color.green.tokens.json +41 -41
- package/src/tokens/constant/color.orange.tokens.json +43 -43
- package/src/tokens/constant/color.purple.tokens.json +40 -40
- package/src/tokens/constant/color.red.tokens.json +44 -44
- package/src/tokens/contextual/avatar.tokens.json +13 -13
- package/src/tokens/contextual/button.tokens.json +22 -22
- package/src/tokens/contextual/chart.tokens.json +3 -3
- package/src/tokens/semantic/action.tokens.json +18 -18
- package/src/utils/confetti/confetti.js +191 -0
- package/src/utils/confetti/constants.js +42 -0
- package/src/utils/confetti/field.js +134 -0
- package/src/utils/confetti/palette.js +15 -0
- package/src/utils/confetti/particles.js +98 -0
- package/src/utils/matrix/columns.js +53 -0
- package/src/utils/matrix/constants.js +19 -0
- package/src/utils/matrix/matrix.js +185 -0
- package/src/utils/matrix/palette.js +18 -0
- package/src/utils.js +2 -0
- package/src/vendor/bootstrap-vue/src/components/popover/helpers/bv-popover-template.js +7 -2
- package/src/vendor/bootstrap-vue/src/components/tooltip/helpers/bv-tooltip-template.js +2 -1
- package/src/vendor/bootstrap-vue/src/constants/components.js +0 -3
- package/translations.js +1 -1
- package/dist/vendor/bootstrap-vue/src/components/form-select/form-select-option-group.js +0 -51
- package/dist/vendor/bootstrap-vue/src/components/form-select/form-select-option.js +0 -41
- package/dist/vendor/bootstrap-vue/src/components/form-select/form-select.js +0 -153
- package/dist/vendor/bootstrap-vue/src/components/form-select/helpers/mixin-options.js +0 -63
- package/dist/vendor/bootstrap-vue/src/components/form-select/index.js +0 -3
- package/src/vendor/bootstrap-vue/src/components/form-select/form-select-option-group.js +0 -46
- package/src/vendor/bootstrap-vue/src/components/form-select/form-select-option.js +0 -35
- package/src/vendor/bootstrap-vue/src/components/form-select/form-select.js +0 -156
- package/src/vendor/bootstrap-vue/src/components/form-select/helpers/mixin-options.js +0 -60
- package/src/vendor/bootstrap-vue/src/components/form-select/index.js +0 -5
|
@@ -42,10 +42,12 @@ const BVPopoverTemplate = /*#__PURE__*/extend({
|
|
|
42
42
|
attrs: {
|
|
43
43
|
role: 'presentation'
|
|
44
44
|
}
|
|
45
|
-
},
|
|
45
|
+
},
|
|
46
|
+
// `innerHTML` overrides children, so avoid passing both — Vue 3 warns otherwise
|
|
47
|
+
titleDomProps.innerHTML ? [] : [$title]), isUndefinedOrNull($content) || $content === '' ? /* istanbul ignore next */h() : h('div', {
|
|
46
48
|
staticClass: 'popover-body',
|
|
47
49
|
domProps: contentDomProps
|
|
48
|
-
}, [$content])]);
|
|
50
|
+
}, contentDomProps.innerHTML ? [] : [$content])]);
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
});
|
|
@@ -113,7 +113,9 @@ const BVTooltipTemplate = /*#__PURE__*/extend({
|
|
|
113
113
|
}), h('div', {
|
|
114
114
|
staticClass: 'tooltip-inner',
|
|
115
115
|
domProps
|
|
116
|
-
},
|
|
116
|
+
},
|
|
117
|
+
// `innerHTML` overrides children, so avoid passing both — Vue 3 warns otherwise
|
|
118
|
+
domProps.innerHTML ? [] : [$title])]);
|
|
117
119
|
}
|
|
118
120
|
}
|
|
119
121
|
});
|
|
@@ -14,9 +14,6 @@ const NAME_FORM = 'BForm';
|
|
|
14
14
|
const NAME_FORM_GROUP = 'BFormGroup';
|
|
15
15
|
const NAME_FORM_INVALID_FEEDBACK = 'BFormInvalidFeedback';
|
|
16
16
|
const NAME_FORM_ROW = 'BFormRow';
|
|
17
|
-
const NAME_FORM_SELECT = 'BFormSelect';
|
|
18
|
-
const NAME_FORM_SELECT_OPTION = 'BFormSelectOption';
|
|
19
|
-
const NAME_FORM_SELECT_OPTION_GROUP = 'BFormSelectOptionGroup';
|
|
20
17
|
const NAME_FORM_TEXT = 'BFormText';
|
|
21
18
|
const NAME_FORM_VALID_FEEDBACK = 'BFormValidFeedback';
|
|
22
19
|
const NAME_LINK = 'BLink';
|
|
@@ -51,4 +48,4 @@ const NAME_TRANSITION = 'BVTransition';
|
|
|
51
48
|
const NAME_TRANSPORTER = 'BVTransporter';
|
|
52
49
|
const NAME_TRANSPORTER_TARGET = 'BVTransporterTarget';
|
|
53
50
|
|
|
54
|
-
export { NAME_BUTTON, NAME_BUTTON_CLOSE, NAME_COL, NAME_DROPDOWN, NAME_DROPDOWN_DIVIDER, NAME_DROPDOWN_FORM, NAME_DROPDOWN_GROUP, NAME_DROPDOWN_HEADER, NAME_DROPDOWN_ITEM, NAME_DROPDOWN_ITEM_BUTTON, NAME_DROPDOWN_TEXT, NAME_FORM, NAME_FORM_BUTTON_LABEL_CONTROL, NAME_FORM_GROUP, NAME_FORM_INVALID_FEEDBACK, NAME_FORM_RATING_STAR, NAME_FORM_ROW,
|
|
51
|
+
export { NAME_BUTTON, NAME_BUTTON_CLOSE, NAME_COL, NAME_DROPDOWN, NAME_DROPDOWN_DIVIDER, NAME_DROPDOWN_FORM, NAME_DROPDOWN_GROUP, NAME_DROPDOWN_HEADER, NAME_DROPDOWN_ITEM, NAME_DROPDOWN_ITEM_BUTTON, NAME_DROPDOWN_TEXT, NAME_FORM, NAME_FORM_BUTTON_LABEL_CONTROL, NAME_FORM_GROUP, NAME_FORM_INVALID_FEEDBACK, NAME_FORM_RATING_STAR, NAME_FORM_ROW, NAME_FORM_TEXT, NAME_FORM_VALID_FEEDBACK, NAME_LINK, NAME_MODAL, NAME_POPOVER, NAME_POPOVER_HELPER, NAME_POPOVER_TEMPLATE, NAME_POPPER, NAME_TAB, NAME_TABLE, NAME_TABLE_CELL, NAME_TABLE_LITE, NAME_TABLE_SIMPLE, NAME_TABS, NAME_TAB_BUTTON_HELPER, NAME_TBODY, NAME_TFOOT, NAME_TH, NAME_THEAD, NAME_TOAST, NAME_TOASTER, NAME_TOAST_POP, NAME_TOOLTIP, NAME_TOOLTIP_HELPER, NAME_TOOLTIP_TEMPLATE, NAME_TR, NAME_TRANSITION, NAME_TRANSPORTER, NAME_TRANSPORTER_TARGET };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gitlab/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "138.1.0",
|
|
4
4
|
"description": "GitLab UI Components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -89,14 +89,6 @@
|
|
|
89
89
|
"tailwindcss": "^3",
|
|
90
90
|
"vue": "^2.7.16"
|
|
91
91
|
},
|
|
92
|
-
"resolutions": {
|
|
93
|
-
"chokidar": "^4.0.3",
|
|
94
|
-
"sane": "^5.0.1",
|
|
95
|
-
"jackspeak": "2.3.6",
|
|
96
|
-
"postcss": "8.5.28",
|
|
97
|
-
"json5": "2.2.3",
|
|
98
|
-
"rollup-plugin-vue/@vue/component-compiler/postcss-modules-sync/generic-names/loader-utils": "3.3.1"
|
|
99
|
-
},
|
|
100
92
|
"devDependencies": {
|
|
101
93
|
"@adobe/leonardo-contrast-colors": "^1.1.0",
|
|
102
94
|
"@babel/core": "^7.29.7",
|
|
@@ -107,8 +99,9 @@
|
|
|
107
99
|
"@cypress/grep": "^4.1.1",
|
|
108
100
|
"@figma/code-connect": "^1.5.3",
|
|
109
101
|
"@gitlab/fonts": "^1.3.1",
|
|
110
|
-
"@gitlab/hybrid-vue": "npm:@vue/compat@3.5.
|
|
102
|
+
"@gitlab/hybrid-vue": "npm:@vue/compat@3.5.42",
|
|
111
103
|
"@gitlab/svgs": "*",
|
|
104
|
+
"@gitlab/vue-router-vue3": "npm:vue-router@^4.6.4",
|
|
112
105
|
"@jest/test-sequencer": "30.5.2",
|
|
113
106
|
"@rollup/plugin-commonjs": "^28.0.9",
|
|
114
107
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
@@ -169,7 +162,7 @@
|
|
|
169
162
|
"rollup-plugin-string": "^3.0.0",
|
|
170
163
|
"rollup-plugin-svg": "^2.0.0",
|
|
171
164
|
"rollup-plugin-vue": "^5.1.9",
|
|
172
|
-
"sass": "^1.
|
|
165
|
+
"sass": "^1.105.1",
|
|
173
166
|
"sass-loader": "^10.5.2",
|
|
174
167
|
"sass-true": "^9",
|
|
175
168
|
"start-server-and-test": "^2.1.5",
|
|
@@ -147,7 +147,6 @@ export default {
|
|
|
147
147
|
return alertVariantIconMap[this.variant];
|
|
148
148
|
},
|
|
149
149
|
shouldRenderActions() {
|
|
150
|
-
// eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots
|
|
151
150
|
return Boolean(this.$slots.actions || this.actionButtons.length);
|
|
152
151
|
},
|
|
153
152
|
actionButtons() {
|
|
@@ -117,7 +117,8 @@ export default {
|
|
|
117
117
|
</script>
|
|
118
118
|
<template>
|
|
119
119
|
<ul :class="listClasses">
|
|
120
|
-
|
|
120
|
+
<!-- Sentinel used by stepItem() to reset scroll position; not a real suggestion. -->
|
|
121
|
+
<li ref="top" aria-hidden="true" role="presentation"></li>
|
|
121
122
|
<!-- @slot The suggestions (implemented with GlFilteredSearchSuggestion). -->
|
|
122
123
|
<slot></slot>
|
|
123
124
|
</ul>
|
|
@@ -70,7 +70,6 @@ export default {
|
|
|
70
70
|
return defaultClass;
|
|
71
71
|
},
|
|
72
72
|
hasLabelDescription() {
|
|
73
|
-
// eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots
|
|
74
73
|
return Boolean(this.labelDescription || this.$slots['label-description']);
|
|
75
74
|
},
|
|
76
75
|
},
|
|
@@ -95,7 +94,6 @@ export default {
|
|
|
95
94
|
</div>
|
|
96
95
|
</template>
|
|
97
96
|
|
|
98
|
-
<!-- eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots -->
|
|
99
97
|
<template v-for="slot in Object.keys($slots)" #[slot]>
|
|
100
98
|
<slot :name="slot"></slot>
|
|
101
99
|
</template>
|
|
@@ -1,17 +1,185 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import { isObject } from 'lodash-es';
|
|
3
|
-
import { BFormSelect } from '../../../../vendor/bootstrap-vue/src/components/form-select/form-select';
|
|
2
|
+
import { isObject, uniqueId, isPlainObject, isBoolean, isUndefined, isNull, get } from 'lodash-es';
|
|
4
3
|
import { formInputWidths } from '../../../../utils/constants';
|
|
4
|
+
import { isVisible } from '../../../../utils/utils';
|
|
5
|
+
import { SafeHtmlDirective as SafeHtml } from '../../../../directives/safe_html/safe_html';
|
|
5
6
|
|
|
6
7
|
export default {
|
|
7
8
|
name: 'GlFormSelect',
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
directives: {
|
|
10
|
+
SafeHtml,
|
|
10
11
|
},
|
|
11
12
|
inheritAttrs: false,
|
|
12
13
|
props: {
|
|
13
14
|
/**
|
|
14
|
-
*
|
|
15
|
+
* Current value of the select. Should be set to an array when the 'multiple' prop is set
|
|
16
|
+
*/
|
|
17
|
+
value: {
|
|
18
|
+
// Value can be any type!
|
|
19
|
+
type: undefined,
|
|
20
|
+
required: false,
|
|
21
|
+
default: undefined,
|
|
22
|
+
},
|
|
23
|
+
/**
|
|
24
|
+
* Array of items to render in the component
|
|
25
|
+
*/
|
|
26
|
+
options: {
|
|
27
|
+
type: [Array, Object],
|
|
28
|
+
required: false,
|
|
29
|
+
default: undefined,
|
|
30
|
+
},
|
|
31
|
+
/**
|
|
32
|
+
* When set to a number larger than 0, will set the number of display option rows. Note not all browser will respect this setting
|
|
33
|
+
* @deprecated Only useful with `multiple`, which is also deprecated.
|
|
34
|
+
*/
|
|
35
|
+
selectSize: {
|
|
36
|
+
type: Number,
|
|
37
|
+
required: false,
|
|
38
|
+
default: 0,
|
|
39
|
+
},
|
|
40
|
+
/**
|
|
41
|
+
* Used to set the `id` attribute on the rendered content, and used as the base to generate any additional element IDs as needed
|
|
42
|
+
*/
|
|
43
|
+
id: {
|
|
44
|
+
type: String,
|
|
45
|
+
required: false,
|
|
46
|
+
default: undefined,
|
|
47
|
+
},
|
|
48
|
+
/**
|
|
49
|
+
* Render the form control in plain mode, rather than custom styled mode
|
|
50
|
+
* @deprecated Use a native <select> element instead.
|
|
51
|
+
*/
|
|
52
|
+
plain: {
|
|
53
|
+
type: Boolean,
|
|
54
|
+
required: false,
|
|
55
|
+
default: false,
|
|
56
|
+
},
|
|
57
|
+
/**
|
|
58
|
+
* Set the size of the component's appearance. 'sm', 'md' (default), or 'lg'
|
|
59
|
+
* @deprecated Has no effect. Use the `width` prop instead.
|
|
60
|
+
*/
|
|
61
|
+
size: {
|
|
62
|
+
type: String,
|
|
63
|
+
required: false,
|
|
64
|
+
default: undefined,
|
|
65
|
+
},
|
|
66
|
+
/**
|
|
67
|
+
* Optional value to set for the 'aria-invalid' attribute. Supported values are 'true' and 'false'. If not set, the 'state' prop will dictate the value
|
|
68
|
+
*/
|
|
69
|
+
ariaInvalid: {
|
|
70
|
+
type: [Boolean, String],
|
|
71
|
+
required: false,
|
|
72
|
+
default: false,
|
|
73
|
+
},
|
|
74
|
+
/**
|
|
75
|
+
* Controls the validation state appearance of the component. `true` for valid, `false` for invalid, or `null` for no validation state
|
|
76
|
+
*/
|
|
77
|
+
state: {
|
|
78
|
+
// Tri-state prop: true, false, null (or undefined)
|
|
79
|
+
type: Boolean,
|
|
80
|
+
required: false,
|
|
81
|
+
default: null,
|
|
82
|
+
},
|
|
83
|
+
/**
|
|
84
|
+
* Field name in the `options` array that should be used for the value
|
|
85
|
+
*/
|
|
86
|
+
valueField: {
|
|
87
|
+
type: String,
|
|
88
|
+
required: false,
|
|
89
|
+
default: 'value',
|
|
90
|
+
},
|
|
91
|
+
/**
|
|
92
|
+
* The key to use from the option object to get the label
|
|
93
|
+
*/
|
|
94
|
+
labelField: {
|
|
95
|
+
type: String,
|
|
96
|
+
required: false,
|
|
97
|
+
default: 'label',
|
|
98
|
+
},
|
|
99
|
+
/**
|
|
100
|
+
* The key to use from the option object to get the options
|
|
101
|
+
*/
|
|
102
|
+
optionsField: {
|
|
103
|
+
type: String,
|
|
104
|
+
required: false,
|
|
105
|
+
default: 'options',
|
|
106
|
+
},
|
|
107
|
+
/**
|
|
108
|
+
* Field name in the `options` array that should be used for the text label
|
|
109
|
+
*/
|
|
110
|
+
textField: {
|
|
111
|
+
type: String,
|
|
112
|
+
required: false,
|
|
113
|
+
default: 'text',
|
|
114
|
+
},
|
|
115
|
+
/**
|
|
116
|
+
* Field name in the `options` array that should be used for the disabled state
|
|
117
|
+
*/
|
|
118
|
+
disabledField: {
|
|
119
|
+
type: String,
|
|
120
|
+
required: false,
|
|
121
|
+
default: 'disabled',
|
|
122
|
+
},
|
|
123
|
+
/**
|
|
124
|
+
* Field name in the `options` array that should be used for the html label instead of text field
|
|
125
|
+
* @deprecated Using the `.html` field (whatever its name) is potentially unsafe.
|
|
126
|
+
*/
|
|
127
|
+
htmlField: {
|
|
128
|
+
type: String,
|
|
129
|
+
required: false,
|
|
130
|
+
default: 'html',
|
|
131
|
+
},
|
|
132
|
+
/**
|
|
133
|
+
* When set, allows multiple options to be selected (multi-select)
|
|
134
|
+
* @deprecated Use GlCollapsibleListbox instead.
|
|
135
|
+
*/
|
|
136
|
+
multiple: {
|
|
137
|
+
type: Boolean,
|
|
138
|
+
required: false,
|
|
139
|
+
default: false,
|
|
140
|
+
},
|
|
141
|
+
/**
|
|
142
|
+
* When set to `true`, attempts to auto-focus the control when it is mounted, or re-activated when in a keep-alive. Does not set the `autofocus` attribute on the control
|
|
143
|
+
*/
|
|
144
|
+
autofocus: {
|
|
145
|
+
type: Boolean,
|
|
146
|
+
required: false,
|
|
147
|
+
default: false,
|
|
148
|
+
},
|
|
149
|
+
/**
|
|
150
|
+
* When set to `true`, disables the component's functionality and places it in a disabled state
|
|
151
|
+
*/
|
|
152
|
+
disabled: {
|
|
153
|
+
type: Boolean,
|
|
154
|
+
required: false,
|
|
155
|
+
default: false,
|
|
156
|
+
},
|
|
157
|
+
/**
|
|
158
|
+
* ID of the form that the form control belongs to. Sets the `form` attribute on the control
|
|
159
|
+
*/
|
|
160
|
+
form: {
|
|
161
|
+
type: String,
|
|
162
|
+
required: false,
|
|
163
|
+
default: undefined,
|
|
164
|
+
},
|
|
165
|
+
/**
|
|
166
|
+
* Sets the value of the `name` attribute on the form control
|
|
167
|
+
*/
|
|
168
|
+
name: {
|
|
169
|
+
type: String,
|
|
170
|
+
required: false,
|
|
171
|
+
default: undefined,
|
|
172
|
+
},
|
|
173
|
+
/**
|
|
174
|
+
* Adds the `required` attribute to the form control
|
|
175
|
+
*/
|
|
176
|
+
required: {
|
|
177
|
+
type: Boolean,
|
|
178
|
+
required: false,
|
|
179
|
+
default: false,
|
|
180
|
+
},
|
|
181
|
+
/**
|
|
182
|
+
* Maximum width of the Select
|
|
15
183
|
*/
|
|
16
184
|
width: {
|
|
17
185
|
type: [String, Object],
|
|
@@ -32,7 +200,32 @@ export default {
|
|
|
32
200
|
default: '',
|
|
33
201
|
},
|
|
34
202
|
},
|
|
203
|
+
data() {
|
|
204
|
+
return {
|
|
205
|
+
localValue: this.value,
|
|
206
|
+
localId: null,
|
|
207
|
+
};
|
|
208
|
+
},
|
|
35
209
|
computed: {
|
|
210
|
+
computedId() {
|
|
211
|
+
return this.id || this.localId;
|
|
212
|
+
},
|
|
213
|
+
computedSelectSize() {
|
|
214
|
+
// Custom selects with a size of zero causes the arrows to be hidden,
|
|
215
|
+
// so dont render the size attribute in this case
|
|
216
|
+
return !this.plain && this.selectSize === 0 ? null : this.selectSize;
|
|
217
|
+
},
|
|
218
|
+
computedAriaInvalid() {
|
|
219
|
+
const { ariaInvalid } = this;
|
|
220
|
+
if (ariaInvalid === true || ariaInvalid === 'true' || ariaInvalid === '') {
|
|
221
|
+
return 'true';
|
|
222
|
+
}
|
|
223
|
+
return this.computedState === false ? 'true' : ariaInvalid;
|
|
224
|
+
},
|
|
225
|
+
computedState() {
|
|
226
|
+
// If not a boolean, ensure that value is null
|
|
227
|
+
return isBoolean(this.state) ? this.state : null;
|
|
228
|
+
},
|
|
36
229
|
cssClasses() {
|
|
37
230
|
if (this.width === null) {
|
|
38
231
|
return [];
|
|
@@ -54,16 +247,202 @@ export default {
|
|
|
54
247
|
// eslint-disable-next-line @gitlab/tailwind-no-interpolation -- Not a CSS utility
|
|
55
248
|
return [`gl-form-select-${this.width}`];
|
|
56
249
|
},
|
|
250
|
+
stateClass() {
|
|
251
|
+
if (this.computedState === true) return 'is-valid';
|
|
252
|
+
if (this.computedState === false) return 'is-invalid';
|
|
253
|
+
return null;
|
|
254
|
+
},
|
|
255
|
+
inputClass() {
|
|
256
|
+
return [
|
|
257
|
+
'gl-form-select',
|
|
258
|
+
this.plain ? 'form-control' : 'custom-select',
|
|
259
|
+
this.size && this.plain ? `form-control-${this.size}` : null,
|
|
260
|
+
this.size && !this.plain ? `custom-select-${this.size}` : null,
|
|
261
|
+
this.stateClass,
|
|
262
|
+
this.selectClass,
|
|
263
|
+
];
|
|
264
|
+
},
|
|
265
|
+
normalizedOptions() {
|
|
266
|
+
return this.normalizeOptions(this.options);
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
watch: {
|
|
270
|
+
value(newValue) {
|
|
271
|
+
this.localValue = newValue;
|
|
272
|
+
},
|
|
273
|
+
localValue(newValue) {
|
|
274
|
+
/**
|
|
275
|
+
* Emitted when the select value changes
|
|
276
|
+
*
|
|
277
|
+
* @event input
|
|
278
|
+
* @type {any}
|
|
279
|
+
*/
|
|
280
|
+
this.$emit('input', newValue);
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
activated() {
|
|
284
|
+
this.handleAutofocus();
|
|
285
|
+
},
|
|
286
|
+
async mounted() {
|
|
287
|
+
this.handleAutofocus();
|
|
288
|
+
await this.$nextTick();
|
|
289
|
+
// Update DOM with auto-generated ID after mount
|
|
290
|
+
// to prevent SSR hydration errors
|
|
291
|
+
this.localId = uniqueId('gl-form-select-');
|
|
292
|
+
},
|
|
293
|
+
methods: {
|
|
294
|
+
focus() {
|
|
295
|
+
this.$refs.select?.focus();
|
|
296
|
+
},
|
|
297
|
+
blur() {
|
|
298
|
+
this.$refs.select?.blur();
|
|
299
|
+
},
|
|
300
|
+
normalizeOptions(options) {
|
|
301
|
+
// Normalize the given options array
|
|
302
|
+
if (Array.isArray(options)) return options.map((option) => this.normalizeOption(option));
|
|
303
|
+
|
|
304
|
+
if (isPlainObject(options)) {
|
|
305
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
306
|
+
// Deprecate the object options format
|
|
307
|
+
// eslint-disable-next-line no-console
|
|
308
|
+
console.warn(
|
|
309
|
+
'[@gitlab/ui] GlFormSelect - Setting prop "options" to an object is deprecated. Use the array format instead.',
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Normalize a `options` object to an array of options
|
|
314
|
+
return Object.keys(options).map((key) => this.normalizeOption(options[key] || {}, key));
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// If not an array or object, return an empty array
|
|
318
|
+
return [];
|
|
319
|
+
},
|
|
320
|
+
normalizeOption(option, key = null) {
|
|
321
|
+
// When the option is an object, normalize it
|
|
322
|
+
if (isPlainObject(option)) {
|
|
323
|
+
const value = get(option, this.valueField);
|
|
324
|
+
const text = get(option, this.textField);
|
|
325
|
+
const options = get(option, this.optionsField, null);
|
|
326
|
+
// When it has options, create an <optgroup> object
|
|
327
|
+
if (!isNull(options)) {
|
|
328
|
+
return {
|
|
329
|
+
label: String(get(option, this.labelField) || text),
|
|
330
|
+
options: this.normalizeOptions(options),
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
// Otherwise create an <option> object
|
|
334
|
+
return {
|
|
335
|
+
value: isUndefined(value) ? key || text : value,
|
|
336
|
+
text: String(isUndefined(text) ? key : text),
|
|
337
|
+
html: get(option, this.htmlField),
|
|
338
|
+
disabled: Boolean(get(option, this.disabledField)),
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
// Otherwise create an <option> object from the given value
|
|
342
|
+
return {
|
|
343
|
+
value: key || option,
|
|
344
|
+
text: String(option),
|
|
345
|
+
disabled: false,
|
|
346
|
+
};
|
|
347
|
+
},
|
|
348
|
+
isOptionGroup(option) {
|
|
349
|
+
return Array.isArray(option.options);
|
|
350
|
+
},
|
|
351
|
+
async onChange(event) {
|
|
352
|
+
const { target } = event;
|
|
353
|
+
const selectedValue = Array.from(target.options)
|
|
354
|
+
.filter((option) => option.selected)
|
|
355
|
+
// NOTE: the _value property is set up by both Vue 2 and 3 explicitly for the
|
|
356
|
+
// purpose of handling non-string values on form elements.
|
|
357
|
+
//
|
|
358
|
+
// See:
|
|
359
|
+
// - https://github.com/vuejs/vue/blob/v2.7.16/src/platforms/web/runtime/modules/dom-props.ts#L42-L44
|
|
360
|
+
// - https://github.com/vuejs/vue/blob/v2.7.16/test/unit/modules/vdom/modules/dom-props.spec.ts#L38
|
|
361
|
+
// - https://github.com/vuejs/core/blob/v3.4.27/packages/runtime-dom/src/modules/props.ts#L48-L50
|
|
362
|
+
// - https://github.com/vuejs/core/blob/v3.4.27/packages/runtime-dom/__tests__/patchProps.spec.ts#L25
|
|
363
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
364
|
+
.map((option) => ('_value' in option ? option._value : option.value));
|
|
365
|
+
|
|
366
|
+
this.localValue = target.multiple ? selectedValue : selectedValue[0];
|
|
367
|
+
|
|
368
|
+
await this.$nextTick();
|
|
369
|
+
/**
|
|
370
|
+
* Emitted when the select value changes via user interaction
|
|
371
|
+
*
|
|
372
|
+
* @event change
|
|
373
|
+
* @type {any}
|
|
374
|
+
*/
|
|
375
|
+
this.$emit('change', this.localValue);
|
|
376
|
+
},
|
|
377
|
+
async handleAutofocus() {
|
|
378
|
+
await this.$nextTick();
|
|
379
|
+
window.requestAnimationFrame(() => {
|
|
380
|
+
if (this.autofocus && isVisible(this.$refs.select)) this.focus();
|
|
381
|
+
});
|
|
382
|
+
},
|
|
57
383
|
},
|
|
58
384
|
};
|
|
59
385
|
</script>
|
|
60
386
|
<template>
|
|
61
387
|
<span class="gl-form-select-wrapper" :class="cssClasses">
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
388
|
+
<select
|
|
389
|
+
:id="computedId"
|
|
390
|
+
ref="select"
|
|
391
|
+
v-model="localValue"
|
|
392
|
+
:aria-invalid="computedAriaInvalid"
|
|
393
|
+
:aria-required="required ? 'true' : null"
|
|
394
|
+
:class="inputClass"
|
|
395
|
+
:disabled="disabled"
|
|
396
|
+
:form="form"
|
|
397
|
+
:multiple="multiple"
|
|
398
|
+
:name="name"
|
|
399
|
+
:required="required"
|
|
400
|
+
:size="computedSelectSize"
|
|
401
|
+
v-bind="$attrs"
|
|
402
|
+
@change="onChange"
|
|
403
|
+
>
|
|
404
|
+
<!-- @slot Slot to place options or option groups above options provided via the 'options' prop -->
|
|
405
|
+
<slot name="first"></slot>
|
|
406
|
+
|
|
407
|
+
<template v-for="(option, index) of normalizedOptions">
|
|
408
|
+
<template v-if="isOptionGroup(option)">
|
|
409
|
+
<optgroup :key="index" :label="option.label">
|
|
410
|
+
<template v-for="(childOption, childIndex) of option.options">
|
|
411
|
+
<option
|
|
412
|
+
v-if="childOption.html"
|
|
413
|
+
:key="`${childIndex}-html`"
|
|
414
|
+
v-safe-html="childOption.html"
|
|
415
|
+
:value="childOption.value"
|
|
416
|
+
:disabled="childOption.disabled"
|
|
417
|
+
></option>
|
|
418
|
+
<option
|
|
419
|
+
v-else
|
|
420
|
+
:key="childIndex"
|
|
421
|
+
:value="childOption.value"
|
|
422
|
+
:disabled="childOption.disabled"
|
|
423
|
+
>
|
|
424
|
+
{{ childOption.text }}
|
|
425
|
+
</option>
|
|
426
|
+
</template>
|
|
427
|
+
</optgroup>
|
|
428
|
+
</template>
|
|
429
|
+
|
|
430
|
+
<template v-else>
|
|
431
|
+
<option
|
|
432
|
+
v-if="option.html"
|
|
433
|
+
:key="`${index}-html`"
|
|
434
|
+
v-safe-html="option.html"
|
|
435
|
+
:value="option.value"
|
|
436
|
+
:disabled="option.disabled"
|
|
437
|
+
></option>
|
|
438
|
+
<option v-else :key="index" :value="option.value" :disabled="option.disabled">
|
|
439
|
+
{{ option.text }}
|
|
440
|
+
</option>
|
|
441
|
+
</template>
|
|
66
442
|
</template>
|
|
67
|
-
|
|
443
|
+
|
|
444
|
+
<!-- @slot Content to place in the form select after 'options' prop options -->
|
|
445
|
+
<slot></slot>
|
|
446
|
+
</select>
|
|
68
447
|
</span>
|
|
69
448
|
</template>
|
|
@@ -146,11 +146,9 @@ export default {
|
|
|
146
146
|
},
|
|
147
147
|
computed: {
|
|
148
148
|
shouldRenderModalOk() {
|
|
149
|
-
// eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots
|
|
150
149
|
return Boolean(this.$slots['modal-ok']);
|
|
151
150
|
},
|
|
152
151
|
shouldRenderModalCancel() {
|
|
153
|
-
// eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots
|
|
154
152
|
return Boolean(this.$slots['modal-cancel']);
|
|
155
153
|
},
|
|
156
154
|
shouldRenderModalFooter() {
|
|
@@ -158,7 +156,6 @@ export default {
|
|
|
158
156
|
this.actionCancel ||
|
|
159
157
|
this.actionSecondary ||
|
|
160
158
|
this.actionPrimary ||
|
|
161
|
-
// eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots
|
|
162
159
|
this.$slots['modal-footer'],
|
|
163
160
|
);
|
|
164
161
|
},
|
|
@@ -125,7 +125,6 @@ export default {
|
|
|
125
125
|
v-bind="passthroughAttrs"
|
|
126
126
|
v-on="$listeners"
|
|
127
127
|
>
|
|
128
|
-
<!-- eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots -->
|
|
129
128
|
<template v-for="slot in Object.keys($slots)" #[slot]>
|
|
130
129
|
<slot :name="slot"></slot>
|
|
131
130
|
</template>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* eslint-disable import/no-default-export */
|
|
1
|
+
/* eslint-disable import-x/no-default-export */
|
|
2
2
|
import Vue from 'vue';
|
|
3
3
|
import { createNewChildComponent } from '../../../vendor/bootstrap-vue/src/utils/create-new-child-component';
|
|
4
4
|
import GlToaster from './toaster.vue';
|
|
@@ -97,7 +97,6 @@ export default {
|
|
|
97
97
|
return Boolean(this.$scopedSlots.description || this.description) && this.isVerticalLayout;
|
|
98
98
|
},
|
|
99
99
|
shouldRenderHelp() {
|
|
100
|
-
// eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots
|
|
101
100
|
return Boolean(this.$slots.help || this.help) && this.isVerticalLayout;
|
|
102
101
|
},
|
|
103
102
|
toggleClasses() {
|