@gitlab/ui 60.2.0 → 61.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.
Files changed (31) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/components/base/banner/banner.js +11 -1
  3. package/dist/components/base/filtered_search/filtered_search.js +1 -4
  4. package/dist/components/base/filtered_search/filtered_search_suggestion.js +1 -1
  5. package/dist/components/base/filtered_search/filtered_search_suggestion_list.js +30 -16
  6. package/dist/components/base/filtered_search/filtered_search_term.js +4 -6
  7. package/dist/components/base/filtered_search/filtered_search_token.js +16 -17
  8. package/dist/components/base/filtered_search/filtered_search_token_segment.js +23 -14
  9. package/dist/components/base/filtered_search/filtered_search_utils.js +78 -1
  10. package/dist/utils/number_utils.js +22 -1
  11. package/package.json +1 -1
  12. package/src/components/base/banner/banner.vue +11 -9
  13. package/src/components/base/filtered_search/__snapshots__/filtered_search_term.spec.js.snap +2 -0
  14. package/src/components/base/filtered_search/filtered_search.md +5 -3
  15. package/src/components/base/filtered_search/filtered_search.stories.js +8 -5
  16. package/src/components/base/filtered_search/filtered_search.vue +1 -11
  17. package/src/components/base/filtered_search/filtered_search_suggestion.md +8 -2
  18. package/src/components/base/filtered_search/filtered_search_suggestion.vue +1 -0
  19. package/src/components/base/filtered_search/filtered_search_suggestion_list.spec.js +61 -64
  20. package/src/components/base/filtered_search/filtered_search_suggestion_list.vue +39 -20
  21. package/src/components/base/filtered_search/filtered_search_term.spec.js +11 -28
  22. package/src/components/base/filtered_search/filtered_search_term.vue +6 -17
  23. package/src/components/base/filtered_search/filtered_search_token.spec.js +3 -22
  24. package/src/components/base/filtered_search/filtered_search_token.vue +8 -16
  25. package/src/components/base/filtered_search/filtered_search_token_segment.spec.js +18 -1
  26. package/src/components/base/filtered_search/filtered_search_token_segment.stories.js +9 -0
  27. package/src/components/base/filtered_search/filtered_search_token_segment.vue +35 -12
  28. package/src/components/base/filtered_search/filtered_search_utils.js +69 -0
  29. package/src/components/base/filtered_search/filtered_search_utils.spec.js +32 -1
  30. package/src/utils/number_utils.js +21 -0
  31. package/src/utils/number_utils.spec.js +42 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,34 @@
1
+ # [61.1.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v61.0.0...v61.1.0) (2023-04-13)
2
+
3
+
4
+ ### Features
5
+
6
+ * **Banner:** Use CloseButton ([f4ddd66](https://gitlab.com/gitlab-org/gitlab-ui/commit/f4ddd66771f46607ff657f2bf4bc268f666c51ec))
7
+ * **Banner:** Use CloseButton ([d173aed](https://gitlab.com/gitlab-org/gitlab-ui/commit/d173aed03b4400196aa9711e118b57047e707a14))
8
+
9
+ # [61.0.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v60.2.0...v61.0.0) (2023-04-12)
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * **GlFilteredSearch:** Don't prevent tabbing past component ([27cdbf3](https://gitlab.com/gitlab-org/gitlab-ui/commit/27cdbf3d67cebebc9b6a2177d3ed664ac44ddee7))
15
+
16
+
17
+ ### Features
18
+
19
+ * **GlFilteredSearch:** Improve option interactions ([d2ada9d](https://gitlab.com/gitlab-org/gitlab-ui/commit/d2ada9d7c179d255e4845e7b237740d1471166b4))
20
+
21
+
22
+ ### BREAKING CHANGES
23
+
24
+ * **GlFilteredSearch:** `GlFilteredSearch`/`GlFilteredSearchTerm`'s `title`
25
+ slot and `GlFilteredSearchToken`'s `title-option` slot have been
26
+ removed. Similar functionality is available by setting the
27
+ `optionComponent` property on the given token object passed to
28
+ `GlFilteredSearch`'s `available-tokens` prop. The component will be
29
+ given an `option` prop, which will be the option object representing the
30
+ token.
31
+
1
32
  # [60.2.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v60.1.0...v60.2.0) (2023-04-12)
2
33
 
3
34
 
@@ -1,4 +1,5 @@
1
1
  import { bannerVariants } from '../../../utils/constants';
2
+ import CloseButton from '../../shared_components/close_button/close_button';
2
3
  import GlButton from '../button/button';
3
4
  import GlCard from '../card/card';
4
5
  import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
@@ -6,6 +7,7 @@ import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
6
7
  var script = {
7
8
  name: 'GlBanner',
8
9
  components: {
10
+ CloseButton,
9
11
  GlButton,
10
12
  GlCard
11
13
  },
@@ -51,6 +53,14 @@ var script = {
51
53
  return bannerVariants.includes(value);
52
54
  }
53
55
  },
56
+ /**
57
+ * Dismiss button's aria-label.
58
+ */
59
+ dismissLabel: {
60
+ type: String,
61
+ required: false,
62
+ default: 'Dismiss'
63
+ },
54
64
  /**
55
65
  * Removes the border for banners embedded in content.
56
66
  */
@@ -91,7 +101,7 @@ var script = {
91
101
  const __vue_script__ = script;
92
102
 
93
103
  /* template */
94
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('gl-card',{staticClass:"gl-px-8 gl-py-6 gl-line-height-20",class:{ 'gl-banner-introduction': _vm.isIntroducing, 'gl-border-none!': _vm.embedded },attrs:{"body-class":"gl-display-flex gl-p-0!"}},[(_vm.svgPath)?_c('div',{staticClass:"gl-banner-illustration"},[_c('img',{attrs:{"src":_vm.svgPath,"alt":""}})]):_vm._e(),_vm._v(" "),_c('div',{staticClass:"gl-banner-content"},[_c('h2',{staticClass:"gl-banner-title"},[_vm._v(_vm._s(_vm.title))]),_vm._v(" "),_vm._t("default"),_vm._v(" "),_c('gl-button',{attrs:{"variant":"confirm","category":"primary","data-testid":"gl-banner-primary-button","href":_vm.buttonLink},on:{"click":_vm.primaryButtonClicked}},[_vm._v(_vm._s(_vm.buttonText))]),_vm._v(" "),_vm._t("actions")],2),_vm._v(" "),_c('gl-button',{staticClass:"gl-banner-close",attrs:{"variant":_vm.isIntroducing ? 'confirm' : 'default',"category":"tertiary","size":"small","icon":"close","aria-label":"Close banner"},on:{"click":_vm.handleClose}})],1)};
104
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('gl-card',{staticClass:"gl-px-8 gl-py-6 gl-line-height-20",class:{ 'gl-banner-introduction': _vm.isIntroducing, 'gl-border-none!': _vm.embedded },attrs:{"body-class":"gl-display-flex gl-p-0!"}},[(_vm.svgPath)?_c('div',{staticClass:"gl-banner-illustration"},[_c('img',{attrs:{"src":_vm.svgPath,"alt":""}})]):_vm._e(),_vm._v(" "),_c('div',{staticClass:"gl-banner-content"},[_c('h2',{staticClass:"gl-banner-title"},[_vm._v(_vm._s(_vm.title))]),_vm._v(" "),_vm._t("default"),_vm._v(" "),_c('gl-button',{attrs:{"variant":"confirm","category":"primary","data-testid":"gl-banner-primary-button","href":_vm.buttonLink},on:{"click":_vm.primaryButtonClicked}},[_vm._v(_vm._s(_vm.buttonText))]),_vm._v(" "),_vm._t("actions")],2),_vm._v(" "),_c('close-button',{staticClass:"gl-banner-close",attrs:{"label":_vm.dismissLabel},on:{"click":_vm.handleClose}})],1)};
95
105
  var __vue_staticRenderFns__ = [];
96
106
 
97
107
  /* style */
@@ -323,9 +323,6 @@ var script = {
323
323
  * @property {array} tokens
324
324
  */
325
325
  this.$emit('submit', normalizeTokens(cloneDeep(this.tokens)));
326
- },
327
- hasTitleSlot() {
328
- return Boolean(this.$scopedSlots.title);
329
326
  }
330
327
  }
331
328
  };
@@ -334,7 +331,7 @@ var script = {
334
331
  const __vue_script__ = script;
335
332
 
336
333
  /* template */
337
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('gl-search-box-by-click',_vm._b({attrs:{"value":_vm.tokens,"history-items":_vm.historyItems,"clearable":_vm.hasValue,"search-button-attributes":_vm.searchButtonAttributes,"disabled":_vm.viewOnly,"data-testid":"filtered-search-input"},on:{"submit":_vm.submit,"input":_vm.applyNewValue,"history-item-selected":function($event){return _vm.$emit('history-item-selected', $event)},"clear":function($event){return _vm.$emit('clear')},"clear-history":function($event){return _vm.$emit('clear-history')}},scopedSlots:_vm._u([{key:"history-item",fn:function(slotScope){return [_vm._t("history-item",null,null,slotScope)]}},{key:"input",fn:function(){return [_c('div',{staticClass:"gl-filtered-search-scrollable",class:{ 'gl-bg-gray-10! gl-inset-border-1-gray-100!': _vm.viewOnly }},_vm._l((_vm.tokens),function(token,idx){return _c(_vm.getTokenComponent(token.type),{key:token.id,ref:"tokens",refInFor:true,tag:"component",class:_vm.getTokenClassList(idx),attrs:{"config":_vm.getTokenEntry(token.type),"active":_vm.activeTokenIdx === idx,"cursor-position":_vm.intendedCursorPosition,"available-tokens":_vm.currentAvailableTokens,"current-value":_vm.tokens,"index":idx,"placeholder":_vm.termPlaceholder,"show-friendly-text":_vm.showFriendlyText,"search-input-attributes":_vm.searchInputAttributes,"view-only":_vm.viewOnly,"is-last-token":_vm.isLastToken(idx)},on:{"activate":function($event){return _vm.activate(idx)},"deactivate":function($event){return _vm.deactivate(token)},"destroy":function($event){return _vm.destroyToken(idx, $event)},"replace":function($event){return _vm.replaceToken(idx, $event)},"complete":_vm.completeToken,"submit":_vm.submit,"split":function($event){return _vm.createTokens(idx, $event)},"previous":_vm.activatePreviousToken,"next":_vm.activateNextToken},scopedSlots:_vm._u([(_vm.hasTitleSlot() && _vm.getTokenComponent(token.type).name === 'GlFilteredSearchTerm')?{key:"title",fn:function(title){return [_vm._t("title",null,null,title)]}}:null],null,true),model:{value:(token.value),callback:function ($$v) {_vm.$set(token, "value", $$v);},expression:"token.value"}})}),1),_vm._v(" "),_c('portal-target',{key:_vm.activeTokenIdx,ref:"menu",style:(_vm.suggestionsStyle),attrs:{"name":_vm.portalName,"slim":""}})]},proxy:true}],null,true)},'gl-search-box-by-click',_vm.$attrs,false))};
334
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('gl-search-box-by-click',_vm._b({attrs:{"value":_vm.tokens,"history-items":_vm.historyItems,"clearable":_vm.hasValue,"search-button-attributes":_vm.searchButtonAttributes,"disabled":_vm.viewOnly,"data-testid":"filtered-search-input"},on:{"submit":_vm.submit,"input":_vm.applyNewValue,"history-item-selected":function($event){return _vm.$emit('history-item-selected', $event)},"clear":function($event){return _vm.$emit('clear')},"clear-history":function($event){return _vm.$emit('clear-history')}},scopedSlots:_vm._u([{key:"history-item",fn:function(slotScope){return [_vm._t("history-item",null,null,slotScope)]}},{key:"input",fn:function(){return [_c('div',{staticClass:"gl-filtered-search-scrollable",class:{ 'gl-bg-gray-10! gl-inset-border-1-gray-100!': _vm.viewOnly }},_vm._l((_vm.tokens),function(token,idx){return _c(_vm.getTokenComponent(token.type),{key:token.id,ref:"tokens",refInFor:true,tag:"component",class:_vm.getTokenClassList(idx),attrs:{"config":_vm.getTokenEntry(token.type),"active":_vm.activeTokenIdx === idx,"cursor-position":_vm.intendedCursorPosition,"available-tokens":_vm.currentAvailableTokens,"current-value":_vm.tokens,"index":idx,"placeholder":_vm.termPlaceholder,"show-friendly-text":_vm.showFriendlyText,"search-input-attributes":_vm.searchInputAttributes,"view-only":_vm.viewOnly,"is-last-token":_vm.isLastToken(idx)},on:{"activate":function($event){return _vm.activate(idx)},"deactivate":function($event){return _vm.deactivate(token)},"destroy":function($event){return _vm.destroyToken(idx, $event)},"replace":function($event){return _vm.replaceToken(idx, $event)},"complete":_vm.completeToken,"submit":_vm.submit,"split":function($event){return _vm.createTokens(idx, $event)},"previous":_vm.activatePreviousToken,"next":_vm.activateNextToken},model:{value:(token.value),callback:function ($$v) {_vm.$set(token, "value", $$v);},expression:"token.value"}})}),1),_vm._v(" "),_c('portal-target',{key:_vm.activeTokenIdx,ref:"menu",style:(_vm.suggestionsStyle),attrs:{"name":_vm.portalName,"slim":""}})]},proxy:true}],null,true)},'gl-search-box-by-click',_vm.$attrs,false))};
338
335
  var __vue_staticRenderFns__ = [];
339
336
 
340
337
  /* style */
@@ -54,7 +54,7 @@ var script = {
54
54
  const __vue_script__ = script;
55
55
 
56
56
  /* template */
57
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('gl-dropdown-item',_vm._b({ref:"item",staticClass:"gl-filtered-search-suggestion",class:{ 'gl-filtered-search-suggestion-active': _vm.isActive },attrs:{"data-testid":"filtered-search-suggestion","href":"#"},nativeOn:{"mousedown":function($event){$event.preventDefault();return _vm.emitValue.apply(null, arguments)}}},'gl-dropdown-item',_vm.$attrs,false),[_vm._t("default")],2)};
57
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('gl-dropdown-item',_vm._b({ref:"item",staticClass:"gl-filtered-search-suggestion",class:{ 'gl-filtered-search-suggestion-active': _vm.isActive },attrs:{"data-testid":"filtered-search-suggestion","tabindex":"-1","href":"#"},nativeOn:{"mousedown":function($event){$event.preventDefault();return _vm.emitValue.apply(null, arguments)}}},'gl-dropdown-item',_vm.$attrs,false),[_vm._t("default")],2)};
58
58
  var __vue_staticRenderFns__ = [];
59
59
 
60
60
  /* style */
@@ -1,5 +1,8 @@
1
+ import { stepIndexAndWrap } from './filtered_search_utils';
1
2
  import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
2
3
 
4
+ const DEFER_TO_INITIAL_VALUE = -1;
5
+ const NO_ACTIVE_ITEM = -2;
3
6
  var script = {
4
7
  name: 'GlFilteredSearchSuggestionList',
5
8
  inject: ['suggestionsListClass'],
@@ -20,21 +23,29 @@ var script = {
20
23
  },
21
24
  data() {
22
25
  return {
23
- activeIdx: -1,
26
+ activeIdx: DEFER_TO_INITIAL_VALUE,
24
27
  registeredItems: []
25
28
  };
26
29
  },
27
30
  computed: {
31
+ initialActiveIdx() {
32
+ return this.registeredItems.findIndex(item => this.valuesMatch(item.value, this.initialValue));
33
+ },
34
+ initialActiveItem() {
35
+ return this.registeredItems[this.initialActiveIdx];
36
+ },
28
37
  activeItem() {
29
- return this.activeIdx > -1 && this.activeIdx < this.registeredItems.length ? this.registeredItems[this.activeIdx] : null;
38
+ if (this.activeIdx === NO_ACTIVE_ITEM) return null;
39
+ if (this.activeIdx === DEFER_TO_INITIAL_VALUE) return this.initialActiveItem;
40
+ return this.registeredItems[this.activeIdx];
30
41
  },
31
42
  listClasses() {
32
43
  return [this.suggestionsListClass(), 'dropdown-menu gl-filtered-search-suggestion-list'];
33
44
  }
34
45
  },
35
46
  watch: {
36
- initialValue(newValue) {
37
- this.activeIdx = this.registeredItems.findIndex(item => this.valuesMatch(item.value, newValue));
47
+ initialValue() {
48
+ this.activeIdx = DEFER_TO_INITIAL_VALUE;
38
49
  }
39
50
  },
40
51
  methods: {
@@ -44,31 +55,34 @@ var script = {
44
55
  },
45
56
  register(item) {
46
57
  this.registeredItems.push(item);
47
- if (this.valuesMatch(item.value, this.initialValue)) {
48
- this.activeIdx = this.registeredItems.length - 1;
49
- }
50
58
  },
51
59
  unregister(item) {
52
60
  const idx = this.registeredItems.indexOf(item);
53
61
  if (idx !== -1) {
54
62
  this.registeredItems.splice(idx, 1);
55
63
  if (idx === this.activeIdx) {
56
- this.activeIdx = -1;
64
+ this.activeIdx = DEFER_TO_INITIAL_VALUE;
57
65
  }
58
66
  }
59
67
  },
60
68
  nextItem() {
61
- if (this.activeIdx < this.registeredItems.length) {
62
- this.activeIdx += 1;
63
- } else {
64
- this.activeIdx = 0;
65
- }
69
+ this.stepItem(1, this.registeredItems.length - 1);
66
70
  },
67
71
  prevItem() {
68
- if (this.activeIdx >= 0) {
69
- this.activeIdx -= 1;
72
+ this.stepItem(-1, 0);
73
+ },
74
+ stepItem(direction, endIdx) {
75
+ if (this.activeIdx === endIdx || this.activeIdx === DEFER_TO_INITIAL_VALUE && this.initialActiveIdx === endIdx) {
76
+ // The user wants to move past the end of the list, so ensure nothing is selected.
77
+ this.activeIdx = NO_ACTIVE_ITEM;
70
78
  } else {
71
- this.activeIdx = this.registeredItems.length - 1;
79
+ const index = this.activeIdx === DEFER_TO_INITIAL_VALUE ?
80
+ // Currently active item is set by initialValue (i.e., text input matching),
81
+ // so step relative to that.
82
+ this.initialActiveIdx :
83
+ // Otherwise, step relative to the explicitly (via up/down arrows) activated item.
84
+ this.activeIdx;
85
+ this.activeIdx = stepIndexAndWrap(index, direction, this.registeredItems.length);
72
86
  }
73
87
  },
74
88
  getValue() {
@@ -1,13 +1,11 @@
1
- import GlFilteredSearchSuggestion from './filtered_search_suggestion';
2
1
  import GlFilteredSearchTokenSegment from './filtered_search_token_segment';
3
- import { INTENT_ACTIVATE_PREVIOUS } from './filtered_search_utils';
2
+ import { match, tokenToOption, INTENT_ACTIVATE_PREVIOUS } from './filtered_search_utils';
4
3
  import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
5
4
 
6
5
  var script = {
7
6
  name: 'GlFilteredSearchTerm',
8
7
  components: {
9
- GlFilteredSearchTokenSegment,
10
- GlFilteredSearchSuggestion
8
+ GlFilteredSearchTokenSegment
11
9
  },
12
10
  inheritAttrs: false,
13
11
  props: {
@@ -79,7 +77,7 @@ var script = {
79
77
  },
80
78
  computed: {
81
79
  suggestedTokens() {
82
- return this.availableTokens.filter(item => item.title.toLowerCase().includes(this.value.data.toLowerCase()));
80
+ return this.availableTokens.filter(token => match(token.title, this.value.data)).map(tokenToOption);
83
81
  },
84
82
  internalValue: {
85
83
  get() {
@@ -118,7 +116,7 @@ var script = {
118
116
  const __vue_script__ = script;
119
117
 
120
118
  /* template */
121
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"gl-h-auto gl-filtered-search-term",attrs:{"data-testid":"filtered-search-term"}},[_c('gl-filtered-search-token-segment',{ref:"segment",staticClass:"gl-filtered-search-term-token",attrs:{"active":_vm.active,"cursor-position":_vm.cursorPosition,"search-input-attributes":_vm.searchInputAttributes,"is-last-token":_vm.isLastToken,"current-value":_vm.currentValue,"view-only":_vm.viewOnly},on:{"activate":function($event){return _vm.$emit('activate')},"deactivate":function($event){return _vm.$emit('deactivate')},"complete":function($event){return _vm.$emit('replace', { type: $event })},"backspace":_vm.onBackspace,"submit":function($event){return _vm.$emit('submit')},"split":function($event){return _vm.$emit('split', $event)},"previous":function($event){return _vm.$emit('previous')},"next":function($event){return _vm.$emit('next')}},scopedSlots:_vm._u([{key:"suggestions",fn:function(){return _vm._l((_vm.suggestedTokens),function(item,idx){return _c('gl-filtered-search-suggestion',{key:idx,attrs:{"value":item.type,"icon-name":item.icon}},[_vm._t("title",function(){return [_vm._v(" "+_vm._s(item.title)+" ")]},null,{ value: item.title })],2)})},proxy:true},{key:"view",fn:function(){return [(_vm.placeholder)?_c('input',_vm._b({staticClass:"gl-filtered-search-term-input",class:{ 'gl-bg-gray-10': _vm.viewOnly },attrs:{"placeholder":_vm.placeholder,"aria-label":_vm.placeholder,"readonly":_vm.viewOnly,"data-testid":"filtered-search-term-input"},on:{"focusin":function($event){return _vm.$emit('activate')},"focusout":function($event){return _vm.$emit('deactivate')}}},'input',_vm.searchInputAttributes,false)):[_vm._v(_vm._s(_vm.value.data))]]},proxy:true}],null,true),model:{value:(_vm.internalValue),callback:function ($$v) {_vm.internalValue=$$v;},expression:"internalValue"}})],1)};
119
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"gl-h-auto gl-filtered-search-term",attrs:{"data-testid":"filtered-search-term"}},[_c('gl-filtered-search-token-segment',{ref:"segment",staticClass:"gl-filtered-search-term-token",attrs:{"is-term":"","active":_vm.active,"cursor-position":_vm.cursorPosition,"search-input-attributes":_vm.searchInputAttributes,"is-last-token":_vm.isLastToken,"current-value":_vm.currentValue,"view-only":_vm.viewOnly,"options":_vm.suggestedTokens},on:{"activate":function($event){return _vm.$emit('activate')},"deactivate":function($event){return _vm.$emit('deactivate')},"complete":function($event){return _vm.$emit('replace', { type: $event })},"backspace":_vm.onBackspace,"submit":function($event){return _vm.$emit('submit')},"split":function($event){return _vm.$emit('split', $event)},"previous":function($event){return _vm.$emit('previous')},"next":function($event){return _vm.$emit('next')}},scopedSlots:_vm._u([{key:"view",fn:function(){return [(_vm.placeholder)?_c('input',_vm._b({staticClass:"gl-filtered-search-term-input",class:{ 'gl-bg-gray-10': _vm.viewOnly },attrs:{"placeholder":_vm.placeholder,"aria-label":_vm.placeholder,"readonly":_vm.viewOnly,"data-testid":"filtered-search-term-input"},on:{"focusin":function($event){return _vm.$emit('activate')},"focusout":function($event){return _vm.$emit('deactivate')}}},'input',_vm.searchInputAttributes,false)):[_vm._v(_vm._s(_vm.value.data))]]},proxy:true}]),model:{value:(_vm.internalValue),callback:function ($$v) {_vm.internalValue=$$v;},expression:"internalValue"}})],1)};
122
120
  var __vue_staticRenderFns__ = [];
123
121
 
124
122
  /* style */
@@ -2,7 +2,7 @@ import cloneDeep from 'lodash/cloneDeep';
2
2
  import { COMMA } from '../../../utils/constants';
3
3
  import GlToken from '../token/token';
4
4
  import GlFilteredSearchTokenSegment from './filtered_search_token_segment';
5
- import { createTerm } from './filtered_search_utils';
5
+ import { tokenToOption, createTerm } from './filtered_search_utils';
6
6
  import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
7
7
 
8
8
  const SEGMENT_TITLE = 'TYPE';
@@ -12,7 +12,7 @@ const TOKEN_CLOSE_SELECTOR = '.gl-token-close';
12
12
  const DEFAULT_OPERATORS = [{
13
13
  value: '=',
14
14
  description: 'is',
15
- default: 'true'
15
+ default: true
16
16
  }, {
17
17
  value: '!=',
18
18
  description: 'is not'
@@ -99,10 +99,7 @@ var script = {
99
99
  return this.tokenValue.data !== '' || this.isSegmentActive(SEGMENT_DATA);
100
100
  },
101
101
  availableTokensWithSelf() {
102
- return [this.config, ...this.availableTokens.filter(t => t !== this.config)].map(t => ({
103
- ...t,
104
- value: t.title
105
- }));
102
+ return [this.config, ...this.availableTokens.filter(token => token !== this.config)].map(tokenToOption);
106
103
  },
107
104
  operatorDescription() {
108
105
  const operator = this.operators.find(op => op.value === this.tokenValue.operator);
@@ -210,8 +207,13 @@ var script = {
210
207
  this.$emit('replace', createTerm(this.config.title));
211
208
  }
212
209
  },
213
- replaceToken(newTitle) {
214
- const newTokenConfig = this.availableTokens.find(t => t.title === newTitle);
210
+ replaceToken(newType) {
211
+ const newTokenConfig = this.availableTokens.find(_ref => {
212
+ let {
213
+ type
214
+ } = _ref;
215
+ return type === newType;
216
+ });
215
217
  if (newTokenConfig === this.config) {
216
218
  this.$nextTick(() => {
217
219
  /**
@@ -233,12 +235,12 @@ var script = {
233
235
  });
234
236
  }
235
237
  },
236
- handleOperatorKeydown(evt, _ref) {
238
+ handleOperatorKeydown(evt, _ref2) {
237
239
  let {
238
240
  inputValue,
239
241
  suggestedValue,
240
242
  applySuggestion
241
- } = _ref;
243
+ } = _ref2;
242
244
  const {
243
245
  key
244
246
  } = evt;
@@ -247,10 +249,10 @@ var script = {
247
249
  return;
248
250
  }
249
251
  const potentialValue = `${inputValue}${key}`;
250
- if (key.length === 1 && !this.operators.find(_ref2 => {
252
+ if (key.length === 1 && !this.operators.find(_ref3 => {
251
253
  let {
252
254
  value
253
- } = _ref2;
255
+ } = _ref3;
254
256
  return value.startsWith(potentialValue);
255
257
  })) {
256
258
  if (this.tokenValue.data === '') {
@@ -305,9 +307,6 @@ var script = {
305
307
  event.stopPropagation();
306
308
  this.$emit('destroy');
307
309
  }
308
- },
309
- hasTitleOptionSlot() {
310
- return Boolean(this.$scopedSlots['title-option']);
311
310
  }
312
311
  }
313
312
  };
@@ -322,9 +321,9 @@ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=
322
321
  'gl-cursor-default': _vm.viewOnly,
323
322
  },attrs:{"data-testid":"filtered-search-token"}},[_c('gl-filtered-search-token-segment',{key:"title-segment",attrs:{"value":_vm.config.title,"active":_vm.isSegmentActive(_vm.$options.segments.SEGMENT_TITLE),"cursor-position":_vm.intendedCursorPosition,"options":_vm.availableTokensWithSelf,"view-only":_vm.viewOnly},on:{"activate":function($event){return _vm.activateSegment(_vm.$options.segments.SEGMENT_TITLE)},"deactivate":function($event){return _vm.$emit('deactivate')},"complete":_vm.replaceToken,"backspace":function($event){return _vm.$emit('destroy')},"submit":function($event){return _vm.$emit('submit')},"previous":function($event){return _vm.$emit('previous')},"next":_vm.activateNextOperatorSegment},scopedSlots:_vm._u([{key:"view",fn:function(ref){
324
323
  var inputValue = ref.inputValue;
325
- return [_c('gl-token',{staticClass:"gl-filtered-search-token-type",class:_vm.getAdditionalSegmentClasses(_vm.$options.segments.SEGMENT_TITLE),attrs:{"view-only":""}},[_vm._v("\n "+_vm._s(inputValue)+"\n ")])]}},(_vm.hasTitleOptionSlot())?{key:"option",fn:function(){return [_vm._t("title-option")]},proxy:true}:null],null,true)}),_vm._v(" "),_c('gl-filtered-search-token-segment',{key:"operator-segment",attrs:{"active":_vm.isSegmentActive(_vm.$options.segments.SEGMENT_OPERATOR),"cursor-position":_vm.intendedCursorPosition,"options":_vm.operators,"custom-input-keydown-handler":_vm.handleOperatorKeydown,"view-only":_vm.viewOnly},on:{"activate":function($event){return _vm.activateSegment(_vm.$options.segments.SEGMENT_OPERATOR)},"backspace":_vm.replaceWithTermIfEmpty,"complete":function($event){return _vm.activateSegment(_vm.$options.segments.SEGMENT_DATA)},"deactivate":function($event){return _vm.$emit('deactivate')},"previous":_vm.activatePreviousTitleSegment,"next":_vm.activateNextDataSegment},scopedSlots:_vm._u([{key:"view",fn:function(){return [_c('gl-token',{staticClass:"gl-filtered-search-token-operator",class:_vm.getAdditionalSegmentClasses(_vm.$options.segments.SEGMENT_OPERATOR),attrs:{"variant":"search-value","view-only":""}},[_vm._v("\n "+_vm._s(_vm.operatorDescription)+"\n ")])]},proxy:true},{key:"option",fn:function(ref){
324
+ return [_c('gl-token',{staticClass:"gl-filtered-search-token-type",class:_vm.getAdditionalSegmentClasses(_vm.$options.segments.SEGMENT_TITLE),attrs:{"view-only":""}},[_vm._v("\n "+_vm._s(inputValue)+"\n ")])]}}])}),_vm._v(" "),_c('gl-filtered-search-token-segment',{key:"operator-segment",attrs:{"active":_vm.isSegmentActive(_vm.$options.segments.SEGMENT_OPERATOR),"cursor-position":_vm.intendedCursorPosition,"options":_vm.operators,"option-text-field":"value","custom-input-keydown-handler":_vm.handleOperatorKeydown,"view-only":_vm.viewOnly},on:{"activate":function($event){return _vm.activateSegment(_vm.$options.segments.SEGMENT_OPERATOR)},"backspace":_vm.replaceWithTermIfEmpty,"complete":function($event){return _vm.activateSegment(_vm.$options.segments.SEGMENT_DATA)},"deactivate":function($event){return _vm.$emit('deactivate')},"previous":_vm.activatePreviousTitleSegment,"next":_vm.activateNextDataSegment},scopedSlots:_vm._u([{key:"view",fn:function(){return [_c('gl-token',{staticClass:"gl-filtered-search-token-operator",class:_vm.getAdditionalSegmentClasses(_vm.$options.segments.SEGMENT_OPERATOR),attrs:{"variant":"search-value","view-only":""}},[_vm._v("\n "+_vm._s(_vm.operatorDescription)+"\n ")])]},proxy:true},{key:"option",fn:function(ref){
326
325
  var option = ref.option;
327
- return [_c('div',{staticClass:"gl-display-flex"},[_vm._v("\n "+_vm._s(_vm.showFriendlyText ? option.description : option.value)+"\n "),(option.description)?_c('span',{staticClass:"gl-filtered-search-token-operator-description"},[_vm._v("\n "+_vm._s(_vm.showFriendlyText ? option.value : option.description)+"\n ")]):_vm._e()])]}}]),model:{value:(_vm.tokenValue.operator),callback:function ($$v) {_vm.$set(_vm.tokenValue, "operator", $$v);},expression:"tokenValue.operator"}}),_vm._v(" "),(_vm.hasDataOrDataSegmentIsCurrentlyActive)?_c('gl-filtered-search-token-segment',{key:"data-segment",attrs:{"active":_vm.isSegmentActive(_vm.$options.segments.SEGMENT_DATA),"cursor-position":_vm.intendedCursorPosition,"multi-select":_vm.config.multiSelect,"options":_vm.config.options,"view-only":_vm.viewOnly,"option-text-field":"title"},on:{"activate":_vm.activateDataSegment,"backspace":function($event){return _vm.activateSegment(_vm.$options.segments.SEGMENT_OPERATOR)},"complete":_vm.handleComplete,"select":function($event){return _vm.$emit('select', $event)},"submit":function($event){return _vm.$emit('submit')},"deactivate":function($event){return _vm.$emit('deactivate')},"split":function($event){return _vm.$emit('split', $event)},"previous":_vm.activatePreviousOperatorSegment,"next":function($event){return _vm.$emit('next')}},scopedSlots:_vm._u([{key:"suggestions",fn:function(){return [_vm._t("suggestions")]},proxy:true},{key:"view",fn:function(ref){
326
+ return [_c('div',{staticClass:"gl-display-flex"},[_vm._v("\n "+_vm._s(_vm.showFriendlyText ? option.description : option.value)+"\n "),(option.description)?_c('span',{staticClass:"gl-filtered-search-token-operator-description"},[_vm._v("\n "+_vm._s(_vm.showFriendlyText ? option.value : option.description)+"\n ")]):_vm._e()])]}}]),model:{value:(_vm.tokenValue.operator),callback:function ($$v) {_vm.$set(_vm.tokenValue, "operator", $$v);},expression:"tokenValue.operator"}}),_vm._v(" "),(_vm.hasDataOrDataSegmentIsCurrentlyActive)?_c('gl-filtered-search-token-segment',{key:"data-segment",attrs:{"active":_vm.isSegmentActive(_vm.$options.segments.SEGMENT_DATA),"cursor-position":_vm.intendedCursorPosition,"multi-select":_vm.config.multiSelect,"options":_vm.config.options,"view-only":_vm.viewOnly},on:{"activate":_vm.activateDataSegment,"backspace":function($event){return _vm.activateSegment(_vm.$options.segments.SEGMENT_OPERATOR)},"complete":_vm.handleComplete,"select":function($event){return _vm.$emit('select', $event)},"submit":function($event){return _vm.$emit('submit')},"deactivate":function($event){return _vm.$emit('deactivate')},"split":function($event){return _vm.$emit('split', $event)},"previous":_vm.activatePreviousOperatorSegment,"next":function($event){return _vm.$emit('next')}},scopedSlots:_vm._u([{key:"suggestions",fn:function(){return [_vm._t("suggestions")]},proxy:true},{key:"view",fn:function(ref){
328
327
  var inputValue = ref.inputValue;
329
328
  return [_vm._t("view-token",function(){return [_c('gl-token',_vm._g({staticClass:"gl-filtered-search-token-data",class:_vm.getAdditionalSegmentClasses(_vm.$options.segments.SEGMENT_DATA),attrs:{"variant":"search-value","view-only":_vm.viewOnly}},_vm.eventListeners),[_c('span',{staticClass:"gl-filtered-search-token-data-content"},[_vm._t("view",function(){return [_vm._v(_vm._s(inputValue))]},null,{ inputValue: inputValue })],2)])]},null,{
330
329
  inputValue: inputValue,
@@ -3,7 +3,7 @@ import { Portal } from 'portal-vue';
3
3
  import { COMMA, LEFT_MOUSE_BUTTON } from '../../../utils/constants';
4
4
  import GlFilteredSearchSuggestion from './filtered_search_suggestion';
5
5
  import GlFilteredSearchSuggestionList from './filtered_search_suggestion_list';
6
- import { splitOnQuotes, wrapTokenInQuotes } from './filtered_search_utils';
6
+ import { splitOnQuotes, match, wrapTokenInQuotes } from './filtered_search_utils';
7
7
  import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
8
8
 
9
9
  // We need some helpers to ensure @vue/compat compatibility
@@ -60,6 +60,11 @@ var script = {
60
60
  required: false,
61
61
  default: false
62
62
  },
63
+ isTerm: {
64
+ type: Boolean,
65
+ required: false,
66
+ default: false
67
+ },
63
68
  label: {
64
69
  type: String,
65
70
  required: false,
@@ -78,7 +83,7 @@ var script = {
78
83
  optionTextField: {
79
84
  type: String,
80
85
  required: false,
81
- default: 'value'
86
+ default: 'title'
82
87
  },
83
88
  customInputKeydownHandler: {
84
89
  type: Function,
@@ -140,16 +145,18 @@ var script = {
140
145
  },
141
146
  inputValue: {
142
147
  get() {
148
+ if (this.isTerm) {
149
+ return this.nonMultipleValue;
150
+ }
143
151
  return this.matchingOption ? this.matchingOption[this.optionTextField] : this.nonMultipleValue;
144
152
  },
145
- set(v) {
146
- var _this$getMatchingOpti, _this$getMatchingOpti2;
153
+ set(inputValue) {
147
154
  /**
148
155
  * Emitted when this token segment's value changes.
149
156
  *
150
157
  * @type {object} option The current option.
151
158
  */
152
- this.$emit('input', (_this$getMatchingOpti = (_this$getMatchingOpti2 = this.getMatchingOptionForInputValue(v)) === null || _this$getMatchingOpti2 === void 0 ? void 0 : _this$getMatchingOpti2.value) !== null && _this$getMatchingOpti !== void 0 ? _this$getMatchingOpti : v);
159
+ this.$emit('input', inputValue);
153
160
  }
154
161
  },
155
162
  hasOptionsOrSuggestions() {
@@ -158,7 +165,7 @@ var script = {
158
165
  return ((_this$options2 = this.options) === null || _this$options2 === void 0 ? void 0 : _this$options2.length) || isSlotNotEmpty(this.$slots.suggestions);
159
166
  },
160
167
  defaultSuggestedValue() {
161
- var _ref;
168
+ var _this$options$;
162
169
  if (!this.options) {
163
170
  return this.nonMultipleValue;
164
171
  }
@@ -168,8 +175,11 @@ var script = {
168
175
  });
169
176
  return option === null || option === void 0 ? void 0 : option.value;
170
177
  }
171
- const defaultSuggestion = this.options.find(op => op.default);
172
- return (_ref = defaultSuggestion !== null && defaultSuggestion !== void 0 ? defaultSuggestion : this.options[0]) === null || _ref === void 0 ? void 0 : _ref.value;
178
+ const defaultOption = this.options.find(op => op.default);
179
+ if (defaultOption) {
180
+ return defaultOption.value;
181
+ }
182
+ return this.isTerm ? undefined : (_this$options$ = this.options[0]) === null || _this$options$ === void 0 ? void 0 : _this$options$.value;
173
183
  },
174
184
  containerAttributes() {
175
185
  return this.isLastToken && !this.active && this.currentValue.length > 1 && this.searchInputAttributes;
@@ -187,13 +197,12 @@ var script = {
187
197
  }
188
198
  },
189
199
  inputValue(newValue) {
190
- var _this$getMatchingOpti3, _this$getMatchingOpti4;
191
200
  const hasUnclosedQuote = newValue.split('"').length % 2 === 0;
192
201
  if (newValue.indexOf(' ') === -1 || hasUnclosedQuote) {
193
202
  return;
194
203
  }
195
204
  const [firstWord, ...otherWords] = splitOnQuotes(newValue).filter((w, idx, arr) => Boolean(w) || idx === arr.length - 1);
196
- this.$emit('input', (_this$getMatchingOpti3 = (_this$getMatchingOpti4 = this.getMatchingOptionForInputValue(firstWord)) === null || _this$getMatchingOpti4 === void 0 ? void 0 : _this$getMatchingOpti4.value) !== null && _this$getMatchingOpti3 !== void 0 ? _this$getMatchingOpti3 : firstWord);
205
+ this.$emit('input', firstWord);
197
206
  if (otherWords.length) {
198
207
  /**
199
208
  * Emitted when Space appears in token segment value
@@ -213,14 +222,14 @@ var script = {
213
222
  e.preventDefault();
214
223
  }
215
224
  },
216
- getMatchingOptionForInputValue(v) {
225
+ getMatchingOptionForInputValue(inputValue) {
217
226
  var _this$options3;
218
227
  let {
219
228
  loose
220
229
  } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
221
230
  loose: false
222
231
  };
223
- return (_this$options3 = this.options) === null || _this$options3 === void 0 ? void 0 : _this$options3.find(o => loose ? o[this.optionTextField].startsWith(v) : [this.optionTextField] === v);
232
+ return (_this$options3 = this.options) === null || _this$options3 === void 0 ? void 0 : _this$options3.find(option => loose ? match(option[this.optionTextField], inputValue) : option[this.optionTextField] === inputValue);
224
233
  },
225
234
  activate() {
226
235
  this.fallbackValue = this.value;
@@ -243,7 +252,7 @@ var script = {
243
252
  },
244
253
  deactivate() {
245
254
  var _this$matchingOption;
246
- if (!this.options) {
255
+ if (!this.options || this.isTerm) {
247
256
  return;
248
257
  }
249
258
  if (((_this$matchingOption = this.matchingOption) === null || _this$matchingOption === void 0 ? void 0 : _this$matchingOption.value) !== this.value) {
@@ -359,7 +368,7 @@ const __vue_script__ = script;
359
368
  var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',_vm._g(_vm._b({staticClass:"gl-filtered-search-token-segment",class:{
360
369
  'gl-filtered-search-token-segment-active': _vm.active,
361
370
  'gl-cursor-text!': _vm.viewOnly,
362
- },attrs:{"data-testid":"filtered-search-token-segment"}},'div',_vm.containerAttributes,false),_vm.viewOnly ? {} : { mousedown: _vm.emitIfInactive }),[(_vm.active)?[(((_vm.searchInputAttributes).type)==='checkbox')?_c('input',_vm._b({directives:[{name:"model",rawName:"v-model",value:(_vm.inputValue),expression:"inputValue"}],ref:"input",staticClass:"gl-filtered-search-token-segment-input",attrs:{"data-testid":"filtered-search-token-segment-input","aria-label":_vm.label,"readonly":_vm.viewOnly,"type":"checkbox"},domProps:{"checked":Array.isArray(_vm.inputValue)?_vm._i(_vm.inputValue,null)>-1:(_vm.inputValue)},on:{"keydown":_vm.handleInputKeydown,"blur":_vm.handleBlur,"change":function($event){var $$a=_vm.inputValue,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.inputValue=$$a.concat([$$v]));}else {$$i>-1&&(_vm.inputValue=$$a.slice(0,$$i).concat($$a.slice($$i+1)));}}else {_vm.inputValue=$$c;}}}},'input',_vm.searchInputAttributes,false)):(((_vm.searchInputAttributes).type)==='radio')?_c('input',_vm._b({directives:[{name:"model",rawName:"v-model",value:(_vm.inputValue),expression:"inputValue"}],ref:"input",staticClass:"gl-filtered-search-token-segment-input",attrs:{"data-testid":"filtered-search-token-segment-input","aria-label":_vm.label,"readonly":_vm.viewOnly,"type":"radio"},domProps:{"checked":_vm._q(_vm.inputValue,null)},on:{"keydown":_vm.handleInputKeydown,"blur":_vm.handleBlur,"change":function($event){_vm.inputValue=null;}}},'input',_vm.searchInputAttributes,false)):_c('input',_vm._b({directives:[{name:"model",rawName:"v-model",value:(_vm.inputValue),expression:"inputValue"}],ref:"input",staticClass:"gl-filtered-search-token-segment-input",attrs:{"data-testid":"filtered-search-token-segment-input","aria-label":_vm.label,"readonly":_vm.viewOnly,"type":(_vm.searchInputAttributes).type},domProps:{"value":(_vm.inputValue)},on:{"keydown":_vm.handleInputKeydown,"blur":_vm.handleBlur,"input":function($event){if($event.target.composing){ return; }_vm.inputValue=$event.target.value;}}},'input',_vm.searchInputAttributes,false)),_vm._v(" "),_c('portal',{key:("operator-" + _vm._uid),attrs:{"to":_vm.portalName}},[(_vm.hasOptionsOrSuggestions)?_c('gl-filtered-search-suggestion-list',{key:("operator-" + _vm._uid),ref:"suggestions",attrs:{"initial-value":_vm.defaultSuggestedValue},on:{"suggestion":_vm.applySuggestion}},[(_vm.options)?_vm._l((_vm.options),function(option,idx){return _c('gl-filtered-search-suggestion',{key:((option.value) + "-" + idx),attrs:{"value":option.value,"icon-name":option.icon}},[_vm._t("option",function(){return [_vm._v(" "+_vm._s(option[_vm.optionTextField])+" ")]},null,{ option: option })],2)}):_vm._t("suggestions")],2):_vm._e()],1)]:_vm._t("view",function(){return [_vm._v(_vm._s(_vm.inputValue))]},null,{ inputValue: _vm.inputValue })],2)};
371
+ },attrs:{"data-testid":"filtered-search-token-segment"}},'div',_vm.containerAttributes,false),_vm.viewOnly ? {} : { mousedown: _vm.emitIfInactive }),[(_vm.active)?[(((_vm.searchInputAttributes).type)==='checkbox')?_c('input',_vm._b({directives:[{name:"model",rawName:"v-model",value:(_vm.inputValue),expression:"inputValue"}],ref:"input",staticClass:"gl-filtered-search-token-segment-input",attrs:{"data-testid":"filtered-search-token-segment-input","aria-label":_vm.label,"readonly":_vm.viewOnly,"type":"checkbox"},domProps:{"checked":Array.isArray(_vm.inputValue)?_vm._i(_vm.inputValue,null)>-1:(_vm.inputValue)},on:{"keydown":_vm.handleInputKeydown,"blur":_vm.handleBlur,"change":function($event){var $$a=_vm.inputValue,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.inputValue=$$a.concat([$$v]));}else {$$i>-1&&(_vm.inputValue=$$a.slice(0,$$i).concat($$a.slice($$i+1)));}}else {_vm.inputValue=$$c;}}}},'input',_vm.searchInputAttributes,false)):(((_vm.searchInputAttributes).type)==='radio')?_c('input',_vm._b({directives:[{name:"model",rawName:"v-model",value:(_vm.inputValue),expression:"inputValue"}],ref:"input",staticClass:"gl-filtered-search-token-segment-input",attrs:{"data-testid":"filtered-search-token-segment-input","aria-label":_vm.label,"readonly":_vm.viewOnly,"type":"radio"},domProps:{"checked":_vm._q(_vm.inputValue,null)},on:{"keydown":_vm.handleInputKeydown,"blur":_vm.handleBlur,"change":function($event){_vm.inputValue=null;}}},'input',_vm.searchInputAttributes,false)):_c('input',_vm._b({directives:[{name:"model",rawName:"v-model",value:(_vm.inputValue),expression:"inputValue"}],ref:"input",staticClass:"gl-filtered-search-token-segment-input",attrs:{"data-testid":"filtered-search-token-segment-input","aria-label":_vm.label,"readonly":_vm.viewOnly,"type":(_vm.searchInputAttributes).type},domProps:{"value":(_vm.inputValue)},on:{"keydown":_vm.handleInputKeydown,"blur":_vm.handleBlur,"input":function($event){if($event.target.composing){ return; }_vm.inputValue=$event.target.value;}}},'input',_vm.searchInputAttributes,false)),_vm._v(" "),_c('portal',{key:("operator-" + _vm._uid),attrs:{"to":_vm.portalName}},[(_vm.hasOptionsOrSuggestions)?_c('gl-filtered-search-suggestion-list',{key:("operator-" + _vm._uid),ref:"suggestions",attrs:{"initial-value":_vm.defaultSuggestedValue},on:{"suggestion":_vm.applySuggestion}},[(_vm.options)?_vm._l((_vm.options),function(option,idx){return _c('gl-filtered-search-suggestion',{key:((option.value) + "-" + idx),attrs:{"value":option.value,"icon-name":option.icon}},[_vm._t("option",function(){return [(option.component)?[_c(option.component,{tag:"component",attrs:{"option":option}})]:[_vm._v("\n "+_vm._s(option[_vm.optionTextField])+"\n ")]]},null,{ option: option })],2)}):_vm._t("suggestions")],2):_vm._e()],1)]:_vm._t("view",function(){return [_vm._v(_vm._s(_vm.inputValue))]},null,{ inputValue: _vm.inputValue })],2)};
363
372
  var __vue_staticRenderFns__ = [];
364
373
 
365
374
  /* style */
@@ -1,6 +1,7 @@
1
1
  import first from 'lodash/first';
2
2
  import last from 'lodash/last';
3
3
  import isString from 'lodash/isString';
4
+ import { modulo } from '../../../utils/number_utils';
4
5
 
5
6
  const TERM_TOKEN_TYPE = 'filtered-search-term';
6
7
  const INTENT_ACTIVATE_PREVIOUS = 'intent-activate-previous';
@@ -37,6 +38,69 @@ function needDenormalization(tokens) {
37
38
  assertValidTokens(tokens);
38
39
  return tokens.some(t => typeof t === 'string' || !t.id);
39
40
  }
41
+
42
+ /**
43
+ * Given an initial index, step size and array length, returns an index that is
44
+ * within the array bounds (unless step is 0; see † below).
45
+ *
46
+ * The step can be any positive or negative integer, including zero.
47
+ *
48
+ * An out-of-bounds index is considered 'uninitialised', and is handled
49
+ * specially. For instance, the 'next' index of 'uninitialised' is the first
50
+ * index:
51
+ *
52
+ * stepIndexAndWrap(-1, 1, 5) === 0
53
+ *
54
+ * The 'previous' index of 'uninitialised' is the last index:
55
+ *
56
+ * stepIndexAndWrap(-1, -1, 5) === 4
57
+ *
58
+ * †: If step is 0, the index is returned as-is, which may be out-of-bounds.
59
+ *
60
+ * @param {number} index The initial index.
61
+ * @param {number} step The amount to step by (positive or negative).
62
+ * @param {number} length The length of the array.
63
+ * @returns {number}
64
+ */
65
+ function stepIndexAndWrap(index, step, length) {
66
+ if (step === 0) return index;
67
+ let start;
68
+ const indexInRange = index >= 0 && index < length;
69
+ if (indexInRange) {
70
+ // Step from the valid index.
71
+ start = index;
72
+ } else if (step > 0) {
73
+ // Step forwards from the beginning of the array.
74
+ start = -1;
75
+ } else {
76
+ // Step backwards from the end of the array.
77
+ start = length;
78
+ }
79
+ return modulo(start + step, length);
80
+ }
81
+
82
+ /**
83
+ * Transforms a given token definition to an option definition.
84
+ *
85
+ * @param {Object} token A token definition (see GlFilteredSearch's
86
+ * availableTokens prop).
87
+ * @returns {Object} A option definition (see GlFilteredSearchTokenSegment's
88
+ * options prop).
89
+ */
90
+ function tokenToOption(_ref) {
91
+ let {
92
+ icon,
93
+ title,
94
+ type,
95
+ optionComponent
96
+ } = _ref;
97
+ return {
98
+ icon,
99
+ title,
100
+ value: type,
101
+ component: optionComponent
102
+ };
103
+ }
40
104
  let tokenIdCounter = 0;
41
105
  const getTokenId = () => {
42
106
  const tokenId = `token-${tokenIdCounter}`;
@@ -86,6 +150,19 @@ function denormalizeTokens(inputTokens) {
86
150
  });
87
151
  return result;
88
152
  }
153
+
154
+ /**
155
+ * Returns `true` if `text` contains `query` (case insensitive).
156
+ *
157
+ * This is used in `filter` and `find` array methods for token segment options.
158
+ *
159
+ * @param {string} text The string to look within.
160
+ * @param {string} query The string to find inside the text.
161
+ * @returns {boolean}
162
+ */
163
+ function match(text, query) {
164
+ return text.toLowerCase().includes(query.toLowerCase());
165
+ }
89
166
  function splitOnQuotes(str) {
90
167
  if (first(str) === "'" && last(str) === "'") {
91
168
  return [str];
@@ -151,4 +228,4 @@ function wrapTokenInQuotes(token) {
151
228
  return `"${token}"`;
152
229
  }
153
230
 
154
- export { INTENT_ACTIVATE_PREVIOUS, TERM_TOKEN_TYPE, createTerm, denormalizeTokens, ensureTokenId, isEmptyTerm, needDenormalization, normalizeTokens, splitOnQuotes, wrapTokenInQuotes };
231
+ export { INTENT_ACTIVATE_PREVIOUS, TERM_TOKEN_TYPE, createTerm, denormalizeTokens, ensureTokenId, isEmptyTerm, match, needDenormalization, normalizeTokens, splitOnQuotes, stepIndexAndWrap, tokenToOption, wrapTokenInQuotes };
@@ -24,6 +24,27 @@ const average = function () {
24
24
  return sum(...arguments) / arguments.length;
25
25
  };
26
26
 
27
+ /**
28
+ * Returns the modulo of n for a divisor.
29
+ *
30
+ * Maps the integer n into the range [0, divisor) when the divisor is positive,
31
+ * and (divisor, 0] when the divisor is negative.
32
+ *
33
+ * This is useful when indexing into an array, to ensure you always stay within
34
+ * the array bounds.
35
+ *
36
+ * See https://2ality.com/2019/08/remainder-vs-modulo.html.
37
+ *
38
+ * @param {number} n The number to mod.
39
+ * @param {number} divisor The divisor (e.g., the length of an array).
40
+ * @returns {number}
41
+ */
42
+ function modulo(n, divisor) {
43
+ const result = (n % divisor + divisor) % divisor;
44
+ // Never return -0.
45
+ return result === 0 ? 0 : result;
46
+ }
47
+
27
48
  /**
28
49
  * Convert number to engineering format, using SI suffix
29
50
  * @param {Number|String} value - Number or Number-convertible String
@@ -75,4 +96,4 @@ const engineeringNotation = function (value) {
75
96
  return `${scaledMantissa}${allYourBase[scaledPower]}`;
76
97
  };
77
98
 
78
- export { addition, average, engineeringNotation, sum };
99
+ export { addition, average, engineeringNotation, modulo, sum };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "60.2.0",
3
+ "version": "61.1.0",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",