@gitlab/ui 87.2.1 → 87.3.1
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/CHANGELOG.md +14 -0
- package/bin/migrate_custom_utils_to_tw.bundled.mjs +1 -0
- package/dist/components/base/token_selector/token_selector.js +19 -16
- package/dist/components/base/token_selector/token_selector_dropdown.js +29 -5
- package/package.json +4 -4
- package/src/components/base/token_selector/token_selector.md +33 -0
- package/src/components/base/token_selector/token_selector.vue +21 -19
- package/src/components/base/token_selector/token_selector_dropdown.vue +74 -43
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [87.3.1](https://gitlab.com/gitlab-org/gitlab-ui/compare/v87.3.0...v87.3.1) (2024-07-30)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* add missing Tailwind equivalent for `gl-flex-shrink-0` ([5e20414](https://gitlab.com/gitlab-org/gitlab-ui/commit/5e20414a18ddd14247862baaf3eedc5390683e82))
|
|
7
|
+
|
|
8
|
+
# [87.3.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v87.2.1...v87.3.0) (2024-07-30)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **GlTokenSelector:** Add `showAddNewAlways` prop ([89ae31c](https://gitlab.com/gitlab-org/gitlab-ui/commit/89ae31c903054f2b1866a692772799d94299e76c))
|
|
14
|
+
|
|
1
15
|
## [87.2.1](https://gitlab.com/gitlab-org/gitlab-ui/compare/v87.2.0...v87.2.1) (2024-07-28)
|
|
2
16
|
|
|
3
17
|
|
|
@@ -183900,6 +183900,7 @@ var tailwindEquivalents = {
|
|
|
183900
183900
|
"gl-md-flex-direction-row-reverse": "md:gl-flex-row-reverse",
|
|
183901
183901
|
"gl-flex-grow-0!": "!gl-grow-0",
|
|
183902
183902
|
"gl-flex-grow-1": "gl-grow",
|
|
183903
|
+
"gl-flex-shrink-0": "gl-shrink-0",
|
|
183903
183904
|
"gl-md-flex-grow-0": "md:gl-grow-0",
|
|
183904
183905
|
"gl-flex-basis-0": "gl-basis-0",
|
|
183905
183906
|
"gl-flex-basis-quarter": "gl-basis-1/4",
|
|
@@ -34,6 +34,14 @@ var script = {
|
|
|
34
34
|
required: false,
|
|
35
35
|
default: false
|
|
36
36
|
},
|
|
37
|
+
/**
|
|
38
|
+
* Shows 'Add new token option' in dropdown even if results are present, requires allowUserDefinedTokens to be true
|
|
39
|
+
*/
|
|
40
|
+
showAddNewAlways: {
|
|
41
|
+
type: Boolean,
|
|
42
|
+
required: false,
|
|
43
|
+
default: false
|
|
44
|
+
},
|
|
37
45
|
/**
|
|
38
46
|
* Dropdown items are loading, can be used when requesting new dropdown items
|
|
39
47
|
*/
|
|
@@ -158,7 +166,10 @@ var script = {
|
|
|
158
166
|
return !this.filteredDropdownItems.length;
|
|
159
167
|
},
|
|
160
168
|
userDefinedTokenCanBeAdded() {
|
|
161
|
-
|
|
169
|
+
if (!this.allowUserDefinedTokens || !this.inputText) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
return this.showAddNewAlways || this.dropdownHasNoItems;
|
|
162
173
|
},
|
|
163
174
|
hideDropdown() {
|
|
164
175
|
if (this.userDefinedTokenCanBeAdded) {
|
|
@@ -250,9 +261,7 @@ var script = {
|
|
|
250
261
|
});
|
|
251
262
|
},
|
|
252
263
|
handleEnter() {
|
|
253
|
-
if (this.
|
|
254
|
-
this.addToken();
|
|
255
|
-
} else if (this.focusedDropdownItem && this.dropdownIsOpen) {
|
|
264
|
+
if (this.focusedDropdownItem && this.dropdownIsOpen) {
|
|
256
265
|
this.addToken(this.focusedDropdownItem);
|
|
257
266
|
}
|
|
258
267
|
},
|
|
@@ -290,19 +299,13 @@ var script = {
|
|
|
290
299
|
}
|
|
291
300
|
this.focusTextInput();
|
|
292
301
|
},
|
|
293
|
-
addToken() {
|
|
294
|
-
let dropdownItem = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
|
|
295
|
-
const token = dropdownItem !== null ? dropdownItem : {
|
|
296
|
-
id: uniqueId('user-defined-token'),
|
|
297
|
-
name: this.inputText
|
|
298
|
-
};
|
|
299
|
-
|
|
302
|
+
addToken(dropdownItem) {
|
|
300
303
|
/**
|
|
301
304
|
* Fired when a token is added or removed
|
|
302
305
|
*
|
|
303
306
|
* @property {array} selectedTokens
|
|
304
307
|
*/
|
|
305
|
-
this.$emit('input', [...this.selectedTokens,
|
|
308
|
+
this.$emit('input', [...this.selectedTokens, dropdownItem]);
|
|
306
309
|
this.inputText = '';
|
|
307
310
|
this.closeDropdown();
|
|
308
311
|
|
|
@@ -311,13 +314,13 @@ var script = {
|
|
|
311
314
|
*
|
|
312
315
|
* @property {object} token
|
|
313
316
|
*/
|
|
314
|
-
this.$emit('token-add',
|
|
317
|
+
this.$emit('token-add', dropdownItem);
|
|
315
318
|
},
|
|
316
319
|
removeToken(token) {
|
|
317
320
|
/**
|
|
318
|
-
* Fired when
|
|
321
|
+
* Fired when a token is added or removed
|
|
319
322
|
*
|
|
320
|
-
* @property {
|
|
323
|
+
* @property {array} selectedTokens
|
|
321
324
|
*/
|
|
322
325
|
this.$emit('input', this.selectedTokens.filter(selectedToken => selectedToken.id !== token.id));
|
|
323
326
|
/**
|
|
@@ -366,7 +369,7 @@ const __vue_script__ = script;
|
|
|
366
369
|
/* template */
|
|
367
370
|
var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('div',{ref:"container",staticClass:"gl-token-selector gl-form-input form-control form-control-plaintext gl-flex !gl-cursor-text gl-items-center !gl-px-3 !gl-py-2",class:[_vm.inputFocused ? 'gl-token-selector-focus-glow' : '', _vm.containerClass, _vm.stateClass],on:{"click":_vm.handleContainerClick}},[(_vm.showEmptyPlaceholder)?_vm._t("empty-placeholder"):_vm._e(),_vm._v(" "),_c('gl-token-container',{attrs:{"tokens":_vm.selectedTokens,"state":_vm.state,"register-focus-on-token":_vm.registerFocusOnToken,"view-only":_vm.viewOnly,"show-clear-all-button":_vm.showClearAllButton},on:{"token-remove":_vm.removeToken,"cancel-focus":_vm.cancelTokenFocus,"clear-all":_vm.clearAll},scopedSlots:_vm._u([{key:"token-content",fn:function(ref){
|
|
368
371
|
var token = ref.token;
|
|
369
|
-
return [_vm._t("token-content",null,{"token":token})]}},{key:"text-input",fn:function(){return [_c('input',_vm._b({ref:"textInput",staticClass:"gl-token-selector-input gl-h-auto gl-w-4/10 gl-grow gl-border-none gl-bg-transparent gl-px-1 gl-font-regular gl-text-base gl-leading-normal gl-text-gray-900 gl-outline-none",attrs:{"type":"text","autocomplete":_vm.autocomplete,"aria-labelledby":_vm.ariaLabelledby,"placeholder":_vm.placeholder,"disabled":_vm.viewOnly},domProps:{"value":_vm.inputText},on:{"input":function($event){_vm.inputText = $event.target.value;},"focus":_vm.handleFocus,"blur":_vm.handleBlur,"keydown":[function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"enter",13,$event.key,"Enter")){ return null; }return _vm.handleEnter.apply(null, arguments)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"esc",27,$event.key,["Esc","Escape"])){ return null; }return _vm.handleEscape.apply(null, arguments)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"delete",[8,46],$event.key,["Backspace","Delete","Del"])){ return null; }return _vm.handleBackspace.apply(null, arguments)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"up",38,$event.key,["Up","ArrowUp"])){ return null; }$event.preventDefault();return _vm.dropdownEventHandlers.handleUpArrow.apply(null, arguments)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"down",40,$event.key,["Down","ArrowDown"])){ return null; }$event.preventDefault();return _vm.dropdownEventHandlers.handleDownArrow.apply(null, arguments)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"home",undefined,$event.key,undefined)){ return null; }return _vm.dropdownEventHandlers.handleHomeKey.apply(null, arguments)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"end",undefined,$event.key,undefined)){ return null; }return _vm.dropdownEventHandlers.handleEndKey.apply(null, arguments)},function($event){$event.stopPropagation();return _vm.$emit('keydown', $event)}],"click":_vm.handleInputClick}},'input',_vm.textInputAttrs,false))]},proxy:true}],null,true)})],2),_vm._v(" "),_c('gl-token-selector-dropdown',{attrs:{"menu-class":_vm.menuClass,"show":_vm.dropdownIsOpen,"loading":_vm.loading,"dropdown-items":_vm.filteredDropdownItems,"selected-tokens":_vm.selectedTokens,"input-text":_vm.inputText,"
|
|
372
|
+
return [_vm._t("token-content",null,{"token":token})]}},{key:"text-input",fn:function(){return [_c('input',_vm._b({ref:"textInput",staticClass:"gl-token-selector-input gl-h-auto gl-w-4/10 gl-grow gl-border-none gl-bg-transparent gl-px-1 gl-font-regular gl-text-base gl-leading-normal gl-text-gray-900 gl-outline-none",attrs:{"type":"text","autocomplete":_vm.autocomplete,"aria-labelledby":_vm.ariaLabelledby,"placeholder":_vm.placeholder,"disabled":_vm.viewOnly},domProps:{"value":_vm.inputText},on:{"input":function($event){_vm.inputText = $event.target.value;},"focus":_vm.handleFocus,"blur":_vm.handleBlur,"keydown":[function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"enter",13,$event.key,"Enter")){ return null; }return _vm.handleEnter.apply(null, arguments)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"esc",27,$event.key,["Esc","Escape"])){ return null; }return _vm.handleEscape.apply(null, arguments)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"delete",[8,46],$event.key,["Backspace","Delete","Del"])){ return null; }return _vm.handleBackspace.apply(null, arguments)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"up",38,$event.key,["Up","ArrowUp"])){ return null; }$event.preventDefault();return _vm.dropdownEventHandlers.handleUpArrow.apply(null, arguments)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"down",40,$event.key,["Down","ArrowDown"])){ return null; }$event.preventDefault();return _vm.dropdownEventHandlers.handleDownArrow.apply(null, arguments)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"home",undefined,$event.key,undefined)){ return null; }return _vm.dropdownEventHandlers.handleHomeKey.apply(null, arguments)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"end",undefined,$event.key,undefined)){ return null; }return _vm.dropdownEventHandlers.handleEndKey.apply(null, arguments)},function($event){$event.stopPropagation();return _vm.$emit('keydown', $event)}],"click":_vm.handleInputClick}},'input',_vm.textInputAttrs,false))]},proxy:true}],null,true)})],2),_vm._v(" "),_c('gl-token-selector-dropdown',{attrs:{"menu-class":_vm.menuClass,"show":_vm.dropdownIsOpen,"loading":_vm.loading,"dropdown-items":_vm.filteredDropdownItems,"selected-tokens":_vm.selectedTokens,"input-text":_vm.inputText,"user-defined-token-can-be-added":_vm.userDefinedTokenCanBeAdded,"component-id":_vm.$options.componentId,"register-dropdown-event-handlers":_vm.registerDropdownEventHandlers,"register-reset-focused-dropdown-item":_vm.registerResetFocusedDropdownItem},on:{"dropdown-item-click":_vm.addToken,"show":_vm.openDropdown},scopedSlots:_vm._u([{key:"loading-content",fn:function(){return [_vm._t("loading-content")]},proxy:true},{key:"user-defined-token-content",fn:function(){return [_vm._t("user-defined-token-content",null,{"inputText":_vm.inputText})]},proxy:true},{key:"no-results-content",fn:function(){return [_vm._t("no-results-content")]},proxy:true},{key:"dropdown-item-content",fn:function(ref){
|
|
370
373
|
var dropdownItem = ref.dropdownItem;
|
|
371
374
|
return [_vm._t("dropdown-item-content",null,{"dropdownItem":dropdownItem})]}},{key:"dropdown-footer",fn:function(){return [_vm._t("dropdown-footer")]},proxy:true}],null,true),model:{value:(_vm.focusedDropdownItem),callback:function ($$v) {_vm.focusedDropdownItem=$$v;},expression:"focusedDropdownItem"}})],1)};
|
|
372
375
|
var __vue_staticRenderFns__ = [];
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import uniqueId from 'lodash/uniqueId';
|
|
1
2
|
import GlDropdownItem from '../dropdown/dropdown_item';
|
|
2
3
|
import { tokensValidator } from './helpers';
|
|
3
4
|
import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
|
|
@@ -32,7 +33,7 @@ var script = {
|
|
|
32
33
|
type: String,
|
|
33
34
|
required: true
|
|
34
35
|
},
|
|
35
|
-
|
|
36
|
+
userDefinedTokenCanBeAdded: {
|
|
36
37
|
type: Boolean,
|
|
37
38
|
required: true
|
|
38
39
|
},
|
|
@@ -55,7 +56,27 @@ var script = {
|
|
|
55
56
|
};
|
|
56
57
|
},
|
|
57
58
|
computed: {
|
|
59
|
+
userDefinedToken() {
|
|
60
|
+
return {
|
|
61
|
+
id: uniqueId('user-defined-token'),
|
|
62
|
+
name: this.inputText
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
dropdownLength() {
|
|
66
|
+
// Adds an additional dropdown item for the 'Add ... dropdown' item
|
|
67
|
+
return this.userDefinedTokenCanBeAdded ? this.dropdownItems.length : this.dropdownItems.length - 1;
|
|
68
|
+
},
|
|
69
|
+
focusedLastDropdownItem() {
|
|
70
|
+
return this.focusedDropdownItemIndex === this.dropdownLength;
|
|
71
|
+
},
|
|
72
|
+
focusedUserDefinedToken() {
|
|
73
|
+
// User defined tokens are always the last in the list
|
|
74
|
+
return this.userDefinedTokenCanBeAdded && this.focusedLastDropdownItem;
|
|
75
|
+
},
|
|
58
76
|
focusedDropdownItem() {
|
|
77
|
+
if (this.focusedUserDefinedToken) {
|
|
78
|
+
return this.userDefinedToken;
|
|
79
|
+
}
|
|
59
80
|
return this.dropdownItems[this.focusedDropdownItemIndex];
|
|
60
81
|
}
|
|
61
82
|
},
|
|
@@ -93,7 +114,7 @@ var script = {
|
|
|
93
114
|
handleMousedown(dropdownItem) {
|
|
94
115
|
// `event.relatedTarget` returns `null` on Safari because buttons are not focused on click (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus)
|
|
95
116
|
// Because of this we need to manually focus on the button. We do this in `mousedown` because it is fired before the `blur` event
|
|
96
|
-
const dropdownItemRef =
|
|
117
|
+
const dropdownItemRef = this.getDropdownItemRef(dropdownItem);
|
|
97
118
|
if (dropdownItemRef !== null && dropdownItemRef !== void 0 && dropdownItemRef.$el) {
|
|
98
119
|
dropdownItemRef.$el.querySelector('button').focus();
|
|
99
120
|
}
|
|
@@ -116,7 +137,7 @@ var script = {
|
|
|
116
137
|
}
|
|
117
138
|
|
|
118
139
|
// Last dropdown item has been reached
|
|
119
|
-
if (this.
|
|
140
|
+
if (this.focusedLastDropdownItem) {
|
|
120
141
|
return;
|
|
121
142
|
}
|
|
122
143
|
this.focusNextDropdownItem();
|
|
@@ -130,7 +151,7 @@ var script = {
|
|
|
130
151
|
this.focusLastDropdownItem();
|
|
131
152
|
},
|
|
132
153
|
focusLastDropdownItem() {
|
|
133
|
-
this.focusedDropdownItemIndex = this.
|
|
154
|
+
this.focusedDropdownItemIndex = this.dropdownLength;
|
|
134
155
|
},
|
|
135
156
|
focusFirstDropdownItem() {
|
|
136
157
|
this.focusedDropdownItemIndex = 0;
|
|
@@ -152,6 +173,9 @@ var script = {
|
|
|
152
173
|
},
|
|
153
174
|
getDropdownItemRef(dropdownItem) {
|
|
154
175
|
var _this$$refs$dropdownI;
|
|
176
|
+
if (this.focusedUserDefinedToken) {
|
|
177
|
+
return this.$refs[this.userDefinedToken.id];
|
|
178
|
+
}
|
|
155
179
|
return (_this$$refs$dropdownI = this.$refs.dropdownItems) === null || _this$$refs$dropdownI === void 0 ? void 0 : _this$$refs$dropdownI.find(ref => ref.$attrs['data-dropdown-item-id'] === dropdownItem.id);
|
|
156
180
|
},
|
|
157
181
|
dropdownItemIdAttribute(dropdownItem) {
|
|
@@ -164,7 +188,7 @@ var script = {
|
|
|
164
188
|
const __vue_script__ = script;
|
|
165
189
|
|
|
166
190
|
/* template */
|
|
167
|
-
var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"dropdown b-dropdown gl-dropdown gl-relative",class:{ show: _vm.show }},[_c('ul',{ref:"dropdownMenu",staticClass:"dropdown-menu gl-absolute",class:[{ show: _vm.show }, _vm.menuClass],attrs:{"role":"menu","aria-activedescendant":_vm.dropdownItemIdAttribute(_vm.focusedDropdownItem)}},[(_vm.loading)?_c('gl-dropdown-item',{attrs:{"disabled":""}},[_vm._t("loading-content",function(){return [_vm._v("Searching...")]})],2):(_vm.
|
|
191
|
+
var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"dropdown b-dropdown gl-dropdown gl-relative",class:{ show: _vm.show }},[_c('ul',{ref:"dropdownMenu",staticClass:"dropdown-menu gl-absolute",class:[{ show: _vm.show }, _vm.menuClass],attrs:{"role":"menu","aria-activedescendant":_vm.dropdownItemIdAttribute(_vm.focusedDropdownItem)}},[(_vm.loading)?_c('gl-dropdown-item',{attrs:{"disabled":""}},[_vm._t("loading-content",function(){return [_vm._v("Searching...")]})],2):_vm._e(),_vm._v(" "),_vm._l((_vm.dropdownItems),function(dropdownItem){return _c('gl-dropdown-item',{key:dropdownItem.id,ref:"dropdownItems",refInFor:true,attrs:{"id":_vm.dropdownItemIdAttribute(dropdownItem),"data-dropdown-item-id":dropdownItem.id,"active":_vm.dropdownItemIsFocused(dropdownItem),"active-class":"is-focused","tabindex":"-1"},on:{"click":function($event){return _vm.handleDropdownItemClick(dropdownItem)}}},[_c('div',{staticClass:"-gl-mx-4 -gl-my-3 gl-px-4 gl-py-3",on:{"mousedown":function($event){return _vm.handleMousedown(dropdownItem)}}},[_vm._t("dropdown-item-content",function(){return [_vm._v("\n "+_vm._s(dropdownItem.name)+"\n ")]},{"dropdownItem":dropdownItem})],2)])}),_vm._v(" "),(_vm.userDefinedTokenCanBeAdded)?_c('gl-dropdown-item',{ref:_vm.userDefinedToken.id,attrs:{"id":_vm.dropdownItemIdAttribute(_vm.userDefinedToken),"data-dropdown-item-id":_vm.userDefinedToken.id,"active":_vm.dropdownItemIsFocused(_vm.userDefinedToken),"active-class":"is-focused","tabindex":"-1"},on:{"click":function($event){return _vm.handleDropdownItemClick(_vm.userDefinedToken)}}},[_c('div',{staticClass:"-gl-mx-4 -gl-my-3 gl-px-4 gl-py-3",on:{"mousedown":function($event){return _vm.handleMousedown(_vm.userDefinedToken)}}},[_vm._t("user-defined-token-content",function(){return [_vm._v("\n Add \""+_vm._s(_vm.inputText)+"\"\n ")]},{"inputText":_vm.inputText})],2)]):(!_vm.dropdownItems.length)?_c('gl-dropdown-item',{attrs:{"disabled":""}},[_vm._t("no-results-content",function(){return [_vm._v("No matches found")]})],2):_vm._e(),_vm._v(" "),_vm._t("dropdown-footer")],2)])};
|
|
168
192
|
var __vue_staticRenderFns__ = [];
|
|
169
193
|
|
|
170
194
|
/* style */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gitlab/ui",
|
|
3
|
-
"version": "87.
|
|
3
|
+
"version": "87.3.1",
|
|
4
4
|
"description": "GitLab UI Components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -108,13 +108,13 @@
|
|
|
108
108
|
"@babel/core": "^7.24.9",
|
|
109
109
|
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
|
|
110
110
|
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
|
|
111
|
-
"@babel/preset-env": "^7.
|
|
111
|
+
"@babel/preset-env": "^7.25.0",
|
|
112
112
|
"@babel/preset-react": "^7.24.7",
|
|
113
113
|
"@cypress/grep": "^4.0.1",
|
|
114
114
|
"@gitlab/eslint-plugin": "19.6.0",
|
|
115
115
|
"@gitlab/fonts": "^1.3.0",
|
|
116
116
|
"@gitlab/stylelint-config": "6.1.0",
|
|
117
|
-
"@gitlab/svgs": "3.
|
|
117
|
+
"@gitlab/svgs": "3.110.0",
|
|
118
118
|
"@jest/test-sequencer": "^29.7.0",
|
|
119
119
|
"@rollup/plugin-commonjs": "^11.1.0",
|
|
120
120
|
"@rollup/plugin-node-resolve": "^7.1.3",
|
|
@@ -156,7 +156,7 @@
|
|
|
156
156
|
"esbuild": "^0.18.0",
|
|
157
157
|
"eslint": "8.57.0",
|
|
158
158
|
"eslint-import-resolver-jest": "3.0.2",
|
|
159
|
-
"eslint-plugin-cypress": "3.
|
|
159
|
+
"eslint-plugin-cypress": "3.4.0",
|
|
160
160
|
"eslint-plugin-storybook": "0.8.0",
|
|
161
161
|
"gitlab-api-async-iterator": "^1.3.1",
|
|
162
162
|
"glob": "10.3.3",
|
|
@@ -43,3 +43,36 @@ export default {
|
|
|
43
43
|
</div>
|
|
44
44
|
</template>
|
|
45
45
|
```
|
|
46
|
+
|
|
47
|
+
## User created tokens
|
|
48
|
+
|
|
49
|
+
This component allows for users to create their own tokens when configured to do so.
|
|
50
|
+
There are two props that support this functionality: `allowUserDefinedTokens` and `showAddNewAlways`.
|
|
51
|
+
|
|
52
|
+
`allowUserDefinedTokens` is required to enable the functionality
|
|
53
|
+
|
|
54
|
+
When set to `true` and a user's search text returns nothing,
|
|
55
|
+
they will be presented with an additional dropdown item `Add ...`
|
|
56
|
+
that takes their current search input and emits `@input`.
|
|
57
|
+
The parent component can then handle the event accordingly.
|
|
58
|
+
|
|
59
|
+
Additionally, there are scenarios where the user may want the ability to add a new token
|
|
60
|
+
even if some search results are returned. This functionality can be enabled by additionally
|
|
61
|
+
setting `showAddNewAlways` to `true`.
|
|
62
|
+
This will allow for the `Add ...` dropdown item to appear at all times
|
|
63
|
+
whenever a user has inputted text, regardless if results are found.
|
|
64
|
+
|
|
65
|
+
```html
|
|
66
|
+
<template>
|
|
67
|
+
<div>
|
|
68
|
+
<gl-token-selector
|
|
69
|
+
v-model="selectedTokens"
|
|
70
|
+
:dropdown-items="dropdownItems"
|
|
71
|
+
allow-user-defined-items
|
|
72
|
+
show-ad-new-always
|
|
73
|
+
@input="onTokenUpdate"
|
|
74
|
+
/>
|
|
75
|
+
{{ selectedTokens }}
|
|
76
|
+
</div>
|
|
77
|
+
</template>
|
|
78
|
+
```
|
|
@@ -34,6 +34,14 @@ export default {
|
|
|
34
34
|
required: false,
|
|
35
35
|
default: false,
|
|
36
36
|
},
|
|
37
|
+
/**
|
|
38
|
+
* Shows 'Add new token option' in dropdown even if results are present, requires allowUserDefinedTokens to be true
|
|
39
|
+
*/
|
|
40
|
+
showAddNewAlways: {
|
|
41
|
+
type: Boolean,
|
|
42
|
+
required: false,
|
|
43
|
+
default: false,
|
|
44
|
+
},
|
|
37
45
|
/**
|
|
38
46
|
* Dropdown items are loading, can be used when requesting new dropdown items
|
|
39
47
|
*/
|
|
@@ -161,7 +169,11 @@ export default {
|
|
|
161
169
|
return !this.filteredDropdownItems.length;
|
|
162
170
|
},
|
|
163
171
|
userDefinedTokenCanBeAdded() {
|
|
164
|
-
|
|
172
|
+
if (!this.allowUserDefinedTokens || !this.inputText) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return this.showAddNewAlways || this.dropdownHasNoItems;
|
|
165
177
|
},
|
|
166
178
|
hideDropdown() {
|
|
167
179
|
if (this.userDefinedTokenCanBeAdded) {
|
|
@@ -262,9 +274,7 @@ export default {
|
|
|
262
274
|
});
|
|
263
275
|
},
|
|
264
276
|
handleEnter() {
|
|
265
|
-
if (this.
|
|
266
|
-
this.addToken();
|
|
267
|
-
} else if (this.focusedDropdownItem && this.dropdownIsOpen) {
|
|
277
|
+
if (this.focusedDropdownItem && this.dropdownIsOpen) {
|
|
268
278
|
this.addToken(this.focusedDropdownItem);
|
|
269
279
|
}
|
|
270
280
|
},
|
|
@@ -302,21 +312,13 @@ export default {
|
|
|
302
312
|
|
|
303
313
|
this.focusTextInput();
|
|
304
314
|
},
|
|
305
|
-
addToken(dropdownItem
|
|
306
|
-
const token =
|
|
307
|
-
dropdownItem !== null
|
|
308
|
-
? dropdownItem
|
|
309
|
-
: {
|
|
310
|
-
id: uniqueId('user-defined-token'),
|
|
311
|
-
name: this.inputText,
|
|
312
|
-
};
|
|
313
|
-
|
|
315
|
+
addToken(dropdownItem) {
|
|
314
316
|
/**
|
|
315
317
|
* Fired when a token is added or removed
|
|
316
318
|
*
|
|
317
319
|
* @property {array} selectedTokens
|
|
318
320
|
*/
|
|
319
|
-
this.$emit('input', [...this.selectedTokens,
|
|
321
|
+
this.$emit('input', [...this.selectedTokens, dropdownItem]);
|
|
320
322
|
|
|
321
323
|
this.inputText = '';
|
|
322
324
|
this.closeDropdown();
|
|
@@ -326,13 +328,13 @@ export default {
|
|
|
326
328
|
*
|
|
327
329
|
* @property {object} token
|
|
328
330
|
*/
|
|
329
|
-
this.$emit('token-add',
|
|
331
|
+
this.$emit('token-add', dropdownItem);
|
|
330
332
|
},
|
|
331
333
|
removeToken(token) {
|
|
332
334
|
/**
|
|
333
|
-
* Fired when
|
|
335
|
+
* Fired when a token is added or removed
|
|
334
336
|
*
|
|
335
|
-
* @property {
|
|
337
|
+
* @property {array} selectedTokens
|
|
336
338
|
*/
|
|
337
339
|
this.$emit(
|
|
338
340
|
'input',
|
|
@@ -446,7 +448,7 @@ export default {
|
|
|
446
448
|
:dropdown-items="filteredDropdownItems"
|
|
447
449
|
:selected-tokens="selectedTokens"
|
|
448
450
|
:input-text="inputText"
|
|
449
|
-
:
|
|
451
|
+
:user-defined-token-can-be-added="userDefinedTokenCanBeAdded"
|
|
450
452
|
:component-id="$options.componentId"
|
|
451
453
|
:register-dropdown-event-handlers="registerDropdownEventHandlers"
|
|
452
454
|
:register-reset-focused-dropdown-item="registerResetFocusedDropdownItem"
|
|
@@ -466,7 +468,7 @@ export default {
|
|
|
466
468
|
</template>
|
|
467
469
|
<template #no-results-content>
|
|
468
470
|
<!-- @slot Content to display when `dropdown-items` is empty and
|
|
469
|
-
`allow-user-defined-tokens` is `false`. Default content is "No matches found". -->
|
|
471
|
+
both `allow-user-defined-tokens` and `show-add-new-always` is `false`. Default content is "No matches found". -->
|
|
470
472
|
<slot name="no-results-content"></slot>
|
|
471
473
|
</template>
|
|
472
474
|
<template #dropdown-item-content="{ dropdownItem }">
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script>
|
|
2
|
+
import uniqueId from 'lodash/uniqueId';
|
|
2
3
|
import GlDropdownItem from '../dropdown/dropdown_item.vue';
|
|
3
4
|
import { tokensValidator } from './helpers';
|
|
4
5
|
|
|
@@ -30,7 +31,7 @@ export default {
|
|
|
30
31
|
type: String,
|
|
31
32
|
required: true,
|
|
32
33
|
},
|
|
33
|
-
|
|
34
|
+
userDefinedTokenCanBeAdded: {
|
|
34
35
|
type: Boolean,
|
|
35
36
|
required: true,
|
|
36
37
|
},
|
|
@@ -53,7 +54,30 @@ export default {
|
|
|
53
54
|
};
|
|
54
55
|
},
|
|
55
56
|
computed: {
|
|
57
|
+
userDefinedToken() {
|
|
58
|
+
return {
|
|
59
|
+
id: uniqueId('user-defined-token'),
|
|
60
|
+
name: this.inputText,
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
dropdownLength() {
|
|
64
|
+
// Adds an additional dropdown item for the 'Add ... dropdown' item
|
|
65
|
+
return this.userDefinedTokenCanBeAdded
|
|
66
|
+
? this.dropdownItems.length
|
|
67
|
+
: this.dropdownItems.length - 1;
|
|
68
|
+
},
|
|
69
|
+
focusedLastDropdownItem() {
|
|
70
|
+
return this.focusedDropdownItemIndex === this.dropdownLength;
|
|
71
|
+
},
|
|
72
|
+
focusedUserDefinedToken() {
|
|
73
|
+
// User defined tokens are always the last in the list
|
|
74
|
+
return this.userDefinedTokenCanBeAdded && this.focusedLastDropdownItem;
|
|
75
|
+
},
|
|
56
76
|
focusedDropdownItem() {
|
|
77
|
+
if (this.focusedUserDefinedToken) {
|
|
78
|
+
return this.userDefinedToken;
|
|
79
|
+
}
|
|
80
|
+
|
|
57
81
|
return this.dropdownItems[this.focusedDropdownItemIndex];
|
|
58
82
|
},
|
|
59
83
|
},
|
|
@@ -92,10 +116,7 @@ export default {
|
|
|
92
116
|
handleMousedown(dropdownItem) {
|
|
93
117
|
// `event.relatedTarget` returns `null` on Safari because buttons are not focused on click (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus)
|
|
94
118
|
// Because of this we need to manually focus on the button. We do this in `mousedown` because it is fired before the `blur` event
|
|
95
|
-
const dropdownItemRef =
|
|
96
|
-
dropdownItem === null
|
|
97
|
-
? this.$refs.userDefinedTokenDropdownItem
|
|
98
|
-
: this.getDropdownItemRef(dropdownItem);
|
|
119
|
+
const dropdownItemRef = this.getDropdownItemRef(dropdownItem);
|
|
99
120
|
|
|
100
121
|
if (dropdownItemRef?.$el) {
|
|
101
122
|
dropdownItemRef.$el.querySelector('button').focus();
|
|
@@ -121,7 +142,7 @@ export default {
|
|
|
121
142
|
}
|
|
122
143
|
|
|
123
144
|
// Last dropdown item has been reached
|
|
124
|
-
if (this.
|
|
145
|
+
if (this.focusedLastDropdownItem) {
|
|
125
146
|
return;
|
|
126
147
|
}
|
|
127
148
|
|
|
@@ -138,7 +159,7 @@ export default {
|
|
|
138
159
|
this.focusLastDropdownItem();
|
|
139
160
|
},
|
|
140
161
|
focusLastDropdownItem() {
|
|
141
|
-
this.focusedDropdownItemIndex = this.
|
|
162
|
+
this.focusedDropdownItemIndex = this.dropdownLength;
|
|
142
163
|
},
|
|
143
164
|
focusFirstDropdownItem() {
|
|
144
165
|
this.focusedDropdownItemIndex = 0;
|
|
@@ -160,6 +181,10 @@ export default {
|
|
|
160
181
|
return dropdownItem.id === this.focusedDropdownItem.id;
|
|
161
182
|
},
|
|
162
183
|
getDropdownItemRef(dropdownItem) {
|
|
184
|
+
if (this.focusedUserDefinedToken) {
|
|
185
|
+
return this.$refs[this.userDefinedToken.id];
|
|
186
|
+
}
|
|
187
|
+
|
|
163
188
|
return this.$refs.dropdownItems?.find(
|
|
164
189
|
(ref) => ref.$attrs['data-dropdown-item-id'] === dropdownItem.id
|
|
165
190
|
);
|
|
@@ -183,43 +208,49 @@ export default {
|
|
|
183
208
|
<gl-dropdown-item v-if="loading" disabled>
|
|
184
209
|
<slot name="loading-content">Searching...</slot>
|
|
185
210
|
</gl-dropdown-item>
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
<
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
211
|
+
|
|
212
|
+
<gl-dropdown-item
|
|
213
|
+
v-for="dropdownItem in dropdownItems"
|
|
214
|
+
:id="dropdownItemIdAttribute(dropdownItem)"
|
|
215
|
+
ref="dropdownItems"
|
|
216
|
+
:key="dropdownItem.id"
|
|
217
|
+
:data-dropdown-item-id="dropdownItem.id"
|
|
218
|
+
:active="dropdownItemIsFocused(dropdownItem)"
|
|
219
|
+
active-class="is-focused"
|
|
220
|
+
tabindex="-1"
|
|
221
|
+
@click="handleDropdownItemClick(dropdownItem)"
|
|
222
|
+
>
|
|
223
|
+
<div class="-gl-mx-4 -gl-my-3 gl-px-4 gl-py-3" @mousedown="handleMousedown(dropdownItem)">
|
|
224
|
+
<slot name="dropdown-item-content" :dropdown-item="dropdownItem">
|
|
225
|
+
{{ dropdownItem.name }}
|
|
226
|
+
</slot>
|
|
227
|
+
</div>
|
|
228
|
+
</gl-dropdown-item>
|
|
229
|
+
|
|
230
|
+
<gl-dropdown-item
|
|
231
|
+
v-if="userDefinedTokenCanBeAdded"
|
|
232
|
+
:id="dropdownItemIdAttribute(userDefinedToken)"
|
|
233
|
+
:ref="userDefinedToken.id"
|
|
234
|
+
:data-dropdown-item-id="userDefinedToken.id"
|
|
235
|
+
:active="dropdownItemIsFocused(userDefinedToken)"
|
|
236
|
+
active-class="is-focused"
|
|
237
|
+
tabindex="-1"
|
|
238
|
+
@click="handleDropdownItemClick(userDefinedToken)"
|
|
239
|
+
>
|
|
240
|
+
<div
|
|
241
|
+
class="-gl-mx-4 -gl-my-3 gl-px-4 gl-py-3"
|
|
242
|
+
@mousedown="handleMousedown(userDefinedToken)"
|
|
212
243
|
>
|
|
213
|
-
<
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
244
|
+
<slot name="user-defined-token-content" :input-text="inputText">
|
|
245
|
+
Add "{{ inputText }}"
|
|
246
|
+
</slot>
|
|
247
|
+
</div>
|
|
248
|
+
</gl-dropdown-item>
|
|
249
|
+
|
|
250
|
+
<gl-dropdown-item v-else-if="!dropdownItems.length" disabled>
|
|
251
|
+
<slot name="no-results-content">No matches found</slot>
|
|
252
|
+
</gl-dropdown-item>
|
|
253
|
+
|
|
223
254
|
<slot name="dropdown-footer"></slot>
|
|
224
255
|
</ul>
|
|
225
256
|
</div>
|