@gitlab/ui 128.14.3 → 128.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/base/new_dropdowns/listbox/listbox.js +43 -24
- package/dist/components/base/new_dropdowns/listbox/listbox_item.js +17 -1
- package/dist/components/base/new_dropdowns/listbox/mock_data.js +15 -5
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/package.json +2 -2
- package/src/components/base/new_dropdowns/dropdown_item.scss +31 -11
- package/src/components/base/new_dropdowns/listbox/listbox.vue +46 -20
- package/src/components/base/new_dropdowns/listbox/listbox_item.vue +20 -1
- package/src/components/base/new_dropdowns/listbox/mock_data.js +11 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gitlab/ui",
|
|
3
|
-
"version": "128.
|
|
3
|
+
"version": "128.15.0",
|
|
4
4
|
"description": "GitLab UI Components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -129,7 +129,7 @@
|
|
|
129
129
|
"@yarnpkg/lockfile": "^1.1.0",
|
|
130
130
|
"acorn": "^8.16.0",
|
|
131
131
|
"acorn-walk": "^8.3.5",
|
|
132
|
-
"autoprefixer": "10.4.
|
|
132
|
+
"autoprefixer": "10.4.27",
|
|
133
133
|
"axe-playwright": "^2.2.2",
|
|
134
134
|
"babel-loader": "^9.2.1",
|
|
135
135
|
"cypress": "15.10.0",
|
|
@@ -16,22 +16,22 @@
|
|
|
16
16
|
@apply gl-mb-0;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
&:hover {
|
|
19
|
+
&:not(.disabled):hover {
|
|
20
20
|
.gl-new-dropdown-item-content {
|
|
21
21
|
color: var(--gl-dropdown-option-text-color-hover);
|
|
22
22
|
background-color: var(--gl-dropdown-option-background-color-unselected-hover);
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
&:focus {
|
|
26
|
+
&:not(.disabled):focus {
|
|
27
27
|
.gl-new-dropdown-item-content {
|
|
28
28
|
color: var(--gl-dropdown-option-text-color-focus);
|
|
29
29
|
background-color: var(--gl-dropdown-option-background-color-unselected-focus);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
&:active,
|
|
34
|
-
&:focus:active {
|
|
33
|
+
&:not(.disabled):active,
|
|
34
|
+
&:not(.disabled):focus:active {
|
|
35
35
|
.gl-new-dropdown-item-content {
|
|
36
36
|
color: var(--gl-dropdown-option-text-color-active);
|
|
37
37
|
background-color: var(--gl-dropdown-option-background-color-unselected-active);
|
|
@@ -43,7 +43,13 @@
|
|
|
43
43
|
background-color: var(--gl-dropdown-option-background-color-selected-default);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
&.disabled {
|
|
47
|
+
.gl-new-dropdown-item-content {
|
|
48
|
+
background-color: var(--gl-background-color-disabled);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
&:not(.disabled):hover {
|
|
47
53
|
.gl-new-dropdown-item-content {
|
|
48
54
|
background-color: var(--gl-dropdown-option-background-color-selected-hover);
|
|
49
55
|
}
|
|
@@ -53,7 +59,7 @@
|
|
|
53
59
|
}
|
|
54
60
|
}
|
|
55
61
|
|
|
56
|
-
&:focus {
|
|
62
|
+
&:not(.disabled):focus {
|
|
57
63
|
.gl-new-dropdown-item-content {
|
|
58
64
|
background-color: var(--gl-dropdown-option-background-color-selected-focus);
|
|
59
65
|
}
|
|
@@ -63,8 +69,8 @@
|
|
|
63
69
|
}
|
|
64
70
|
}
|
|
65
71
|
|
|
66
|
-
&:active,
|
|
67
|
-
&:focus:active {
|
|
72
|
+
&:not(.disabled):active,
|
|
73
|
+
&:not(.disabled):focus:active {
|
|
68
74
|
.gl-new-dropdown-item-content {
|
|
69
75
|
background-color: var(--gl-dropdown-option-background-color-selected-active);
|
|
70
76
|
}
|
|
@@ -79,9 +85,9 @@
|
|
|
79
85
|
outline: none;
|
|
80
86
|
}
|
|
81
87
|
|
|
82
|
-
&:active,
|
|
83
|
-
&:focus,
|
|
84
|
-
&:focus:active {
|
|
88
|
+
&:not(.disabled):active,
|
|
89
|
+
&:not(.disabled):focus,
|
|
90
|
+
&:not(.disabled):focus:active {
|
|
85
91
|
.gl-new-dropdown-item-content {
|
|
86
92
|
@include gl-focus($inset: true);
|
|
87
93
|
}
|
|
@@ -100,6 +106,20 @@
|
|
|
100
106
|
@include gl-focus($inset: true);
|
|
101
107
|
}
|
|
102
108
|
|
|
109
|
+
&.disabled {
|
|
110
|
+
cursor: not-allowed;
|
|
111
|
+
user-select: none;
|
|
112
|
+
|
|
113
|
+
.gl-new-dropdown-item-content {
|
|
114
|
+
color: var(--gl-text-color-disabled);
|
|
115
|
+
pointer-events: none;
|
|
116
|
+
|
|
117
|
+
.gl-new-dropdown-item-check-icon {
|
|
118
|
+
color: var(--gl-icon-color-disabled);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
103
123
|
.gl-new-dropdown-item-content {
|
|
104
124
|
transition:
|
|
105
125
|
background-color $gl-transition-duration-fast $gl-easing-out-cubic,
|
|
@@ -603,10 +603,10 @@ export default {
|
|
|
603
603
|
|
|
604
604
|
/**
|
|
605
605
|
* Every time the list of items changes, and there is a search string,
|
|
606
|
-
* we want to visually highlight the first item
|
|
606
|
+
* we want to visually highlight the first enabled item
|
|
607
607
|
*/
|
|
608
608
|
if (this.searchHasOptions) {
|
|
609
|
-
this.nextFocusedItemIndex =
|
|
609
|
+
this.nextFocusedItemIndex = this.getFirstEnabledIndex();
|
|
610
610
|
} else {
|
|
611
611
|
this.nextFocusedItemIndex = null;
|
|
612
612
|
}
|
|
@@ -677,16 +677,22 @@ export default {
|
|
|
677
677
|
this.focusSearchInput();
|
|
678
678
|
|
|
679
679
|
/**
|
|
680
|
-
* If the search string is not empty, highlight the first item
|
|
680
|
+
* If the search string is not empty, highlight the first enabled item
|
|
681
681
|
*/
|
|
682
682
|
if (this.searchHasOptions) {
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
683
|
+
const firstEnabledIndex = this.getFirstEnabledIndex();
|
|
684
|
+
this.nextFocusedItemIndex = firstEnabledIndex;
|
|
685
|
+
// Set activeItemId for the first enabled item
|
|
686
|
+
const firstItem = this.flattenedOptions[firstEnabledIndex];
|
|
686
687
|
this.activeItemId = this.generateItemId(firstItem);
|
|
687
688
|
}
|
|
688
689
|
} else {
|
|
689
|
-
this.
|
|
690
|
+
const selectedIndex = this.selectedIndices[0];
|
|
691
|
+
const initialIndex =
|
|
692
|
+
selectedIndex !== undefined && !this.flattenedOptions[selectedIndex]?.disabled
|
|
693
|
+
? selectedIndex
|
|
694
|
+
: this.getFirstEnabledIndex();
|
|
695
|
+
this.focusItem(initialIndex, this.getFocusableListItemElements());
|
|
690
696
|
}
|
|
691
697
|
/**
|
|
692
698
|
* Emitted when dropdown is shown
|
|
@@ -707,13 +713,26 @@ export default {
|
|
|
707
713
|
this.$emit('blur', event);
|
|
708
714
|
},
|
|
709
715
|
getNextIndex(currentIndex, keyCode, totalLength) {
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
716
|
+
const direction = keyCode === ARROW_UP ? -1 : 1;
|
|
717
|
+
let nextIndex = currentIndex;
|
|
718
|
+
|
|
719
|
+
for (let i = 0; i < totalLength; i += 1) {
|
|
720
|
+
nextIndex += direction;
|
|
721
|
+
if (nextIndex < 0) nextIndex = totalLength - 1;
|
|
722
|
+
if (nextIndex >= totalLength) nextIndex = 0;
|
|
723
|
+
|
|
724
|
+
if (!this.flattenedOptions[nextIndex]?.disabled) {
|
|
725
|
+
return nextIndex;
|
|
726
|
+
}
|
|
713
727
|
}
|
|
714
728
|
|
|
715
|
-
|
|
716
|
-
|
|
729
|
+
return currentIndex;
|
|
730
|
+
},
|
|
731
|
+
getFirstEnabledIndex() {
|
|
732
|
+
return this.flattenedOptions.findIndex((item) => !item.disabled);
|
|
733
|
+
},
|
|
734
|
+
getLastEnabledIndex() {
|
|
735
|
+
return this.flattenedOptions.findLastIndex((item) => !item.disabled);
|
|
717
736
|
},
|
|
718
737
|
handleListNavigation(keyCode, elements) {
|
|
719
738
|
const currentIndex = this.nextFocusedItemIndex ?? -1;
|
|
@@ -731,16 +750,16 @@ export default {
|
|
|
731
750
|
|
|
732
751
|
switch (code) {
|
|
733
752
|
case HOME:
|
|
734
|
-
// Jump to first item if searchable or not in search input
|
|
753
|
+
// Jump to first enabled item if searchable or not in search input
|
|
735
754
|
if (this.searchable || !isSearchInput) {
|
|
736
|
-
this.focusItem(
|
|
755
|
+
this.focusItem(this.getFirstEnabledIndex(), elements, this.searchable);
|
|
737
756
|
}
|
|
738
757
|
break;
|
|
739
758
|
|
|
740
759
|
case END:
|
|
741
|
-
// Jump to last item if searchable or not in search input
|
|
760
|
+
// Jump to last enabled item if searchable or not in search input
|
|
742
761
|
if (this.searchable || !isSearchInput) {
|
|
743
|
-
this.focusItem(
|
|
762
|
+
this.focusItem(this.getLastEnabledIndex(), elements, this.searchable);
|
|
744
763
|
}
|
|
745
764
|
break;
|
|
746
765
|
|
|
@@ -753,9 +772,9 @@ export default {
|
|
|
753
772
|
break;
|
|
754
773
|
|
|
755
774
|
case ARROW_DOWN:
|
|
756
|
-
// Focus first item from search input, otherwise navigate down
|
|
775
|
+
// Focus first enabled item from search input, otherwise navigate down
|
|
757
776
|
if (isSearchInput && !this.searchable) {
|
|
758
|
-
this.focusItem(
|
|
777
|
+
this.focusItem(this.getFirstEnabledIndex(), elements);
|
|
759
778
|
} else {
|
|
760
779
|
this.handleListNavigation(ARROW_DOWN, elements);
|
|
761
780
|
}
|
|
@@ -763,10 +782,12 @@ export default {
|
|
|
763
782
|
|
|
764
783
|
case ENTER:
|
|
765
784
|
if (isSearchInput) {
|
|
766
|
-
// Toggle selection of highlighted item if one exists
|
|
785
|
+
// Toggle selection of highlighted item if one exists and is not disabled
|
|
767
786
|
if (elements.length > 0 && this.nextFocusedItemIndex !== null) {
|
|
768
787
|
const highlightedItem = this.flattenedOptions[this.nextFocusedItemIndex];
|
|
769
|
-
|
|
788
|
+
if (!highlightedItem?.disabled) {
|
|
789
|
+
this.onSelect(highlightedItem, !this.isSelected(highlightedItem));
|
|
790
|
+
}
|
|
770
791
|
}
|
|
771
792
|
} else {
|
|
772
793
|
stop = false;
|
|
@@ -834,6 +855,9 @@ export default {
|
|
|
834
855
|
isFocused(item) {
|
|
835
856
|
return this.nextFocusedItemIndex === this.flattenedOptions.indexOf(item);
|
|
836
857
|
},
|
|
858
|
+
isDisabled(item) {
|
|
859
|
+
return item.disabled;
|
|
860
|
+
},
|
|
837
861
|
onSingleSelect(value, isSelected) {
|
|
838
862
|
if (isSelected) {
|
|
839
863
|
/**
|
|
@@ -1109,6 +1133,7 @@ export default {
|
|
|
1109
1133
|
:is-selected="isSelected(item)"
|
|
1110
1134
|
:is-focused="isFocused(item)"
|
|
1111
1135
|
:is-check-centered="isCheckCentered"
|
|
1136
|
+
:is-disabled="isDisabled(item)"
|
|
1112
1137
|
v-bind="listboxItemMoreItemsAriaAttributes(index)"
|
|
1113
1138
|
@select="onSelect(item, $event)"
|
|
1114
1139
|
>
|
|
@@ -1140,6 +1165,7 @@ export default {
|
|
|
1140
1165
|
:is-selected="isSelected(option)"
|
|
1141
1166
|
:is-focused="isFocused(option)"
|
|
1142
1167
|
:is-check-centered="isCheckCentered"
|
|
1168
|
+
:is-disabled="isDisabled(option)"
|
|
1143
1169
|
@select="onSelect(option, $event)"
|
|
1144
1170
|
>
|
|
1145
1171
|
<!-- @slot Custom template of the listbox item -->
|
|
@@ -29,6 +29,11 @@ export default {
|
|
|
29
29
|
default: false,
|
|
30
30
|
required: false,
|
|
31
31
|
},
|
|
32
|
+
isDisabled: {
|
|
33
|
+
type: Boolean,
|
|
34
|
+
default: false,
|
|
35
|
+
required: false,
|
|
36
|
+
},
|
|
32
37
|
},
|
|
33
38
|
computed: {
|
|
34
39
|
checkedClasses() {
|
|
@@ -40,7 +45,13 @@ export default {
|
|
|
40
45
|
},
|
|
41
46
|
},
|
|
42
47
|
methods: {
|
|
48
|
+
onMousedown(event) {
|
|
49
|
+
if (this.isDisabled) {
|
|
50
|
+
stopEvent(event);
|
|
51
|
+
}
|
|
52
|
+
},
|
|
43
53
|
toggleSelection() {
|
|
54
|
+
if (this.isDisabled) return;
|
|
44
55
|
this.$emit('select', !this.isSelected);
|
|
45
56
|
},
|
|
46
57
|
onKeydown(event) {
|
|
@@ -57,10 +68,18 @@ export default {
|
|
|
57
68
|
|
|
58
69
|
<template>
|
|
59
70
|
<li
|
|
60
|
-
:class="[
|
|
71
|
+
:class="[
|
|
72
|
+
'gl-new-dropdown-item',
|
|
73
|
+
{
|
|
74
|
+
'gl-new-dropdown-item-highlighted': isHighlighted,
|
|
75
|
+
disabled: isDisabled,
|
|
76
|
+
},
|
|
77
|
+
]"
|
|
61
78
|
role="option"
|
|
62
79
|
:tabindex="isFocused ? 0 : -1"
|
|
63
80
|
:aria-selected="isSelected"
|
|
81
|
+
:aria-disabled="isDisabled"
|
|
82
|
+
@mousedown="onMousedown"
|
|
64
83
|
@click="toggleSelection"
|
|
65
84
|
@keydown="onKeydown"
|
|
66
85
|
>
|
|
@@ -14,14 +14,17 @@ export const mockOptions = [
|
|
|
14
14
|
{
|
|
15
15
|
value: 'leg',
|
|
16
16
|
text: 'Legal',
|
|
17
|
+
disabled: true,
|
|
17
18
|
},
|
|
18
19
|
{
|
|
19
20
|
value: 'eng',
|
|
20
21
|
text: 'Engineering',
|
|
22
|
+
disabled: true,
|
|
21
23
|
},
|
|
22
24
|
{
|
|
23
25
|
value: 'sales',
|
|
24
26
|
text: 'Sales',
|
|
27
|
+
disabled: false,
|
|
25
28
|
},
|
|
26
29
|
{
|
|
27
30
|
value: 'marketing',
|
|
@@ -30,6 +33,7 @@ export const mockOptions = [
|
|
|
30
33
|
{
|
|
31
34
|
value: 'acc',
|
|
32
35
|
text: 'Accounting',
|
|
36
|
+
disabled: true,
|
|
33
37
|
},
|
|
34
38
|
{
|
|
35
39
|
value: 'hr',
|
|
@@ -50,6 +54,7 @@ export const mockOptions = [
|
|
|
50
54
|
{
|
|
51
55
|
value: null,
|
|
52
56
|
text: 'None',
|
|
57
|
+
disabled: true,
|
|
53
58
|
},
|
|
54
59
|
];
|
|
55
60
|
|
|
@@ -134,6 +139,12 @@ export const mockUsers = [
|
|
|
134
139
|
secondaryText: '@ohoral',
|
|
135
140
|
icon: 'bar',
|
|
136
141
|
},
|
|
142
|
+
{
|
|
143
|
+
value: 'jdoe',
|
|
144
|
+
text: 'Jane Doe',
|
|
145
|
+
secondaryText: '@jdoe',
|
|
146
|
+
disabled: true,
|
|
147
|
+
},
|
|
137
148
|
{
|
|
138
149
|
value: 'markian',
|
|
139
150
|
text: 'Mark Florian',
|