@gitlab/ui 101.4.0 → 101.6.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/CHANGELOG.md +14 -0
- package/dist/components/base/segmented_control/segmented_control.js +23 -59
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/package.json +1 -1
- package/src/components/base/drawer/drawer.scss +1 -1
- package/src/components/base/segmented_control/segmented_control.md +26 -1
- package/src/components/base/segmented_control/segmented_control.vue +37 -59
- package/src/components/base/table/table.scss +1 -2
- package/src/scss/components.scss +0 -1
- package/src/scss/mixins.scss +1 -0
- package/src/components/base/segmented_control/segmented_control.scss +0 -189
package/package.json
CHANGED
|
@@ -1 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
A customizable button group that displays a set of equal options, where only one
|
|
2
|
+
option can be active at a time. This component includes the ability to disable
|
|
3
|
+
specific options and dynamically modify button content using slots.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Displays a group of selectable buttons.
|
|
8
|
+
- Allows only one active selection at a time.
|
|
9
|
+
- Supports content customization through the button-content slot.
|
|
10
|
+
- Options can be disabled individually.
|
|
11
|
+
|
|
12
|
+
## Props Validation
|
|
13
|
+
|
|
14
|
+
The `options` prop is validated against a specific structure to ensure consistent
|
|
15
|
+
data. Each option must include:
|
|
16
|
+
|
|
17
|
+
- `value`: A `string`, `number`, or `boolean` to identify the option.
|
|
18
|
+
- `disabled`: A `boolean` (or `undefined`) indicating whether the option is disabled.
|
|
19
|
+
|
|
20
|
+
Optionally it can include:
|
|
21
|
+
|
|
22
|
+
- `text`: A `string` which gets displayed in the slot content.
|
|
23
|
+
|
|
24
|
+
## Notes
|
|
25
|
+
|
|
26
|
+
- Ensure each value is unique within the options array for consistent behavior.
|
|
@@ -1,77 +1,55 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import
|
|
2
|
+
import GlButtonGroup from '../button_group/button_group.vue';
|
|
3
|
+
import GlButton from '../button/button.vue';
|
|
3
4
|
|
|
4
|
-
const
|
|
5
|
+
const validateOptionsProp = (options) => {
|
|
6
|
+
const requiredOptionPropType = {
|
|
7
|
+
value: ['string', 'number', 'boolean'],
|
|
8
|
+
disabled: ['boolean', 'undefined'],
|
|
9
|
+
};
|
|
10
|
+
const optionProps = Object.keys(requiredOptionPropType);
|
|
11
|
+
|
|
12
|
+
return options.every((option) => {
|
|
13
|
+
if (!option) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
return optionProps.every((name) => requiredOptionPropType[name].includes(typeof option[name]));
|
|
17
|
+
});
|
|
18
|
+
};
|
|
5
19
|
|
|
6
20
|
export default {
|
|
7
21
|
name: 'GlSegmentedControl',
|
|
8
22
|
components: {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
inheritAttrs: false,
|
|
12
|
-
model: {
|
|
13
|
-
prop: 'checked',
|
|
14
|
-
event: 'input',
|
|
23
|
+
GlButtonGroup,
|
|
24
|
+
GlButton,
|
|
15
25
|
},
|
|
16
26
|
props: {
|
|
17
|
-
checked: {
|
|
18
|
-
required: true,
|
|
19
|
-
validator: () => true,
|
|
20
|
-
},
|
|
21
27
|
options: {
|
|
22
28
|
type: Array,
|
|
23
29
|
required: true,
|
|
30
|
+
validator: validateOptionsProp,
|
|
24
31
|
},
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return this.options.filter((option) => !option.disabled);
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
watch: {
|
|
32
|
-
checked: {
|
|
33
|
-
handler(newValue, oldValue) {
|
|
34
|
-
this.checkValue(newValue, oldValue);
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
options: {
|
|
38
|
-
handler() {
|
|
39
|
-
this.checkValue(this.checked);
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
created() {
|
|
44
|
-
this.checkValue(this.checked);
|
|
45
|
-
},
|
|
46
|
-
methods: {
|
|
47
|
-
checkValue(newValue, oldValue = null) {
|
|
48
|
-
if (!this.isValidValue(newValue)) {
|
|
49
|
-
// eslint-disable-next-line no-console
|
|
50
|
-
console.warn(genericErrorMessage);
|
|
51
|
-
if (this.enabledOptions.length) {
|
|
52
|
-
const suggestion =
|
|
53
|
-
oldValue && this.isValidValue(oldValue) ? oldValue : this.enabledOptions[0].value;
|
|
54
|
-
/**
|
|
55
|
-
* Emitted when the selection changes
|
|
56
|
-
* @event input
|
|
57
|
-
* @argument checked The selected option
|
|
58
|
-
*/
|
|
59
|
-
this.$emit('input', suggestion);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
isValidValue(val) {
|
|
64
|
-
return this.enabledOptions.some(({ value }) => value === val);
|
|
32
|
+
value: {
|
|
33
|
+
type: [String, Number, Boolean],
|
|
34
|
+
required: true,
|
|
65
35
|
},
|
|
66
36
|
},
|
|
67
37
|
};
|
|
68
38
|
</script>
|
|
39
|
+
|
|
69
40
|
<template>
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
41
|
+
<gl-button-group>
|
|
42
|
+
<gl-button
|
|
43
|
+
v-for="option in options"
|
|
44
|
+
:key="option.value"
|
|
45
|
+
:disabled="!!option.disabled"
|
|
46
|
+
:selected="value === option.value"
|
|
47
|
+
v-bind="option.props"
|
|
48
|
+
@click="$emit('input', option.value)"
|
|
49
|
+
>
|
|
50
|
+
<slot name="button-content" v-bind="option">
|
|
51
|
+
{{ option.text }}
|
|
52
|
+
</slot>
|
|
53
|
+
</gl-button>
|
|
54
|
+
</gl-button-group>
|
|
77
55
|
</template>
|
package/src/scss/components.scss
CHANGED
|
@@ -61,7 +61,6 @@
|
|
|
61
61
|
@import '../components/base/progress_bar/progress_bar';
|
|
62
62
|
@import '../components/base/search_box_by_type/search_box_by_type';
|
|
63
63
|
@import '../components/base/search_box_by_click/search_box_by_click';
|
|
64
|
-
@import '../components/base/segmented_control/segmented_control';
|
|
65
64
|
@import '../components/base/skeleton_loader/skeleton_loader';
|
|
66
65
|
@import '../components/base/tabs/tabs/tabs';
|
|
67
66
|
@import '../components/base/toast/toast';
|
package/src/scss/mixins.scss
CHANGED
|
@@ -182,6 +182,7 @@
|
|
|
182
182
|
font-weight: $gl-font-weight-heading;
|
|
183
183
|
margin-top: 0; // override bootstrap reset in GitLab
|
|
184
184
|
font-size: get-font-size-variable($size, $fixed);
|
|
185
|
+
@apply gl-text-heading;
|
|
185
186
|
|
|
186
187
|
// Larger headings have reduced letter spacing
|
|
187
188
|
@if ($size <= 500) {
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Segmented-control-specific utilities
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
@mixin gl-btn-gl-segmented-button-first {
|
|
6
|
-
box-shadow:
|
|
7
|
-
inset 0 #{$gl-border-size-1} 0 0 $gray-200,
|
|
8
|
-
inset 0 -#{$gl-border-size-1} 0 0 $gray-200,
|
|
9
|
-
inset #{$gl-border-size-1} 0 0 0 $gray-200;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
@mixin gl-btn-gl-segmented-button-last {
|
|
13
|
-
box-shadow:
|
|
14
|
-
inset 0 #{$gl-border-size-1} 0 0 $gray-200,
|
|
15
|
-
inset 0 -#{$gl-border-size-1} 0 0 $gray-200,
|
|
16
|
-
inset -#{$gl-border-size-1} 0 0 0 $gray-200;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
@mixin gl-btn-gl-segmented-button-focus($color) {
|
|
20
|
-
box-shadow:
|
|
21
|
-
$focus-ring,
|
|
22
|
-
inset 0 0 0 $gl-border-size-2 $color;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
.btn-group.gl-segmented-control {
|
|
26
|
-
.btn:not(:first-child),
|
|
27
|
-
.btn:not(:last-child) {
|
|
28
|
-
&.active,
|
|
29
|
-
&.focus,
|
|
30
|
-
&:hover:not(.disabled) {
|
|
31
|
-
@apply gl-rounded-base;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
@if $feature-button-border {
|
|
37
|
-
.gl-segmented-control {
|
|
38
|
-
label:not(.disabled) {
|
|
39
|
-
@apply gl-cursor-pointer;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
.btn-gl-segmented-button {
|
|
43
|
-
@apply gl-text-base;
|
|
44
|
-
@apply gl-leading-normal;
|
|
45
|
-
@apply gl-text-gray-900;
|
|
46
|
-
@apply gl-fill-current;
|
|
47
|
-
@apply gl-bg-gray-10;
|
|
48
|
-
border-color: $gray-200;
|
|
49
|
-
|
|
50
|
-
&:not(:first-child) {
|
|
51
|
-
border-left-color: transparent;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
&:not(:last-child) {
|
|
55
|
-
border-right-color: transparent;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
&:hover {
|
|
59
|
-
border-color: $gray-400;
|
|
60
|
-
@apply gl-bg-gray-50;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
&.focus {
|
|
64
|
-
@apply gl-z-1;
|
|
65
|
-
border-color: $gray-400;
|
|
66
|
-
@apply gl-focus;
|
|
67
|
-
@apply gl-bg-gray-50;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
&.active {
|
|
71
|
-
@apply gl-z-2;
|
|
72
|
-
border-color: $gray-400;
|
|
73
|
-
box-shadow: inset 0 0 0 $gl-border-size-1 $gray-400;
|
|
74
|
-
@apply gl-bg-white;
|
|
75
|
-
|
|
76
|
-
&:hover {
|
|
77
|
-
box-shadow: inset 0 0 0 $gl-border-size-1 $gray-400;
|
|
78
|
-
@apply gl-bg-gray-50;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
&.focus,
|
|
82
|
-
&.focus:hover {
|
|
83
|
-
border-color: $gray-400;
|
|
84
|
-
@apply gl-focus;
|
|
85
|
-
@apply gl-bg-gray-50;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
&:focus-within {
|
|
90
|
-
@apply gl-focus;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
&.disabled,
|
|
94
|
-
&[disabled],
|
|
95
|
-
&.disabled:hover,
|
|
96
|
-
&[disabled]:hover {
|
|
97
|
-
@apply gl-text-gray-400;
|
|
98
|
-
border-color: $gray-200;
|
|
99
|
-
@apply gl-z-0;
|
|
100
|
-
@apply gl-cursor-not-allowed;
|
|
101
|
-
|
|
102
|
-
&:first-child {
|
|
103
|
-
border-right-color: transparent;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
&:last-child {
|
|
107
|
-
border-left-color: transparent;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
} @else {
|
|
113
|
-
.gl-segmented-control {
|
|
114
|
-
label:not(.disabled) {
|
|
115
|
-
@apply gl-cursor-pointer;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
.btn-gl-segmented-button {
|
|
119
|
-
@apply gl-text-base;
|
|
120
|
-
@apply gl-leading-normal;
|
|
121
|
-
@apply gl-text-gray-900;
|
|
122
|
-
@apply gl-fill-current;
|
|
123
|
-
@apply gl-bg-gray-10;
|
|
124
|
-
box-shadow:
|
|
125
|
-
inset 0 #{$gl-border-size-1} 0 0 $gray-200,
|
|
126
|
-
inset 0 -#{$gl-border-size-1} 0 0 $gray-200;
|
|
127
|
-
|
|
128
|
-
&:first-child {
|
|
129
|
-
@include gl-btn-gl-segmented-button-first;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
&:last-child {
|
|
133
|
-
@include gl-btn-gl-segmented-button-last;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
&:hover {
|
|
137
|
-
box-shadow: inset 0 0 0 $gl-border-size-2 $gray-400;
|
|
138
|
-
@apply gl-bg-gray-50;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
&.focus {
|
|
142
|
-
@apply gl-z-1;
|
|
143
|
-
@include gl-btn-gl-segmented-button-focus($gray-400);
|
|
144
|
-
@apply gl-bg-gray-50;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
&.active {
|
|
148
|
-
@apply gl-z-2;
|
|
149
|
-
box-shadow: inset 0 0 0 $gl-border-size-2 $gray-300;
|
|
150
|
-
@apply gl-bg-white;
|
|
151
|
-
|
|
152
|
-
&:hover {
|
|
153
|
-
box-shadow: inset 0 0 0 $gl-border-size-2 $gray-400;
|
|
154
|
-
@apply gl-bg-gray-50;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
&.focus,
|
|
158
|
-
&.focus:hover {
|
|
159
|
-
@include gl-btn-gl-segmented-button-focus($gray-400);
|
|
160
|
-
@apply gl-bg-gray-50;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
&:focus-within {
|
|
165
|
-
@apply gl-focus;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
&.disabled,
|
|
169
|
-
&[disabled],
|
|
170
|
-
&.disabled:hover,
|
|
171
|
-
&[disabled]:hover {
|
|
172
|
-
@apply gl-text-gray-400;
|
|
173
|
-
box-shadow:
|
|
174
|
-
inset 0 #{$gl-border-size-1} 0 0 $gray-200,
|
|
175
|
-
inset 0 -#{$gl-border-size-1} 0 0 $gray-200;
|
|
176
|
-
@apply gl-z-0;
|
|
177
|
-
@apply gl-cursor-not-allowed;
|
|
178
|
-
|
|
179
|
-
&:first-child {
|
|
180
|
-
@include gl-btn-gl-segmented-button-first;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
&:last-child {
|
|
184
|
-
@include gl-btn-gl-segmented-button-last;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|