dashboard-shell-shell 1.0.111 → 1.0.112
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/.DS_Store +0 -0
- package/package.json +1 -1
- package/rancher-components/Banner/Banner.vue +6 -4
- package/rancher-components/Card/Card.vue +6 -4
- package/rancher-components/Form/Checkbox/Checkbox.vue +20 -1
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +46 -5
- package/rancher-components/Form/Radio/RadioButton.vue +32 -8
- package/rancher-components/Form/Radio/RadioGroup.vue +31 -24
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +17 -0
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +8 -3
- package/rancher-components/LabeledTooltip/LabeledTooltip.vue +15 -3
- package/rancher-components/RcButton/RcButton.vue +1 -0
- package/rancher-components/RcButton/types.ts +1 -0
- package/rancher-components/RcDropdown/RcDropdown.vue +54 -15
- package/rancher-components/RcDropdown/RcDropdownItem.vue +5 -4
- package/rancher-components/RcDropdown/RcDropdownMenu.vue +11 -7
- package/rancher-components/RcDropdown/RcDropdownTrigger.vue +12 -2
- package/rancher-components/RcDropdown/useDropdownCollection.ts +8 -0
- package/rancher-components/RcDropdown/useDropdownContext.ts +9 -3
- package/rancher-components/StringList/StringList.vue +1 -1
- package/vue.config.js +1 -1
package/.DS_Store
ADDED
|
Binary file
|
package/package.json
CHANGED
|
@@ -125,7 +125,8 @@ $icon-size: 24px;
|
|
|
125
125
|
|
|
126
126
|
.banner {
|
|
127
127
|
display: flex;
|
|
128
|
-
margin: 15px 0;
|
|
128
|
+
/* margin: 15px 0; */
|
|
129
|
+
margin: 0px 0px 20px 0px;
|
|
129
130
|
position: relative;
|
|
130
131
|
width: 100%;
|
|
131
132
|
color: var(--body-text);
|
|
@@ -165,9 +166,9 @@ $icon-size: 24px;
|
|
|
165
166
|
}
|
|
166
167
|
|
|
167
168
|
&__content {
|
|
168
|
-
padding: 10px;
|
|
169
|
+
padding: 9px 10px;
|
|
169
170
|
transition: all 0.2s ease;
|
|
170
|
-
line-height:
|
|
171
|
+
line-height: 12px;
|
|
171
172
|
width: 100%;
|
|
172
173
|
border-left: solid $left-border-size transparent;
|
|
173
174
|
display: flex;
|
|
@@ -189,7 +190,8 @@ $icon-size: 24px;
|
|
|
189
190
|
}
|
|
190
191
|
|
|
191
192
|
.info & {
|
|
192
|
-
background: var(--info-banner-bg);
|
|
193
|
+
/* background: var(--info-banner-bg); */
|
|
194
|
+
background: #f2f2f2;
|
|
193
195
|
border-color: var(--info);
|
|
194
196
|
}
|
|
195
197
|
|
|
@@ -89,7 +89,7 @@ export default defineComponent({
|
|
|
89
89
|
{{ title }}
|
|
90
90
|
</slot>
|
|
91
91
|
</div>
|
|
92
|
-
<hr>
|
|
92
|
+
<!-- <hr role="none"> -->
|
|
93
93
|
<div
|
|
94
94
|
class="card-body"
|
|
95
95
|
data-testid="card-body-slot"
|
|
@@ -124,9 +124,9 @@ export default defineComponent({
|
|
|
124
124
|
border-radius: var(--border-radius);
|
|
125
125
|
display: flex;
|
|
126
126
|
flex-basis: 40%;
|
|
127
|
-
margin: 10px;
|
|
127
|
+
/* margin: 10px; */
|
|
128
128
|
min-height: 100px;
|
|
129
|
-
padding:
|
|
129
|
+
padding: 24px;
|
|
130
130
|
box-shadow: 0 0 20px var(--shadow);
|
|
131
131
|
&:not(.top) {
|
|
132
132
|
align-items: top;
|
|
@@ -143,14 +143,16 @@ export default defineComponent({
|
|
|
143
143
|
justify-content: center;
|
|
144
144
|
}
|
|
145
145
|
& .card-actions {
|
|
146
|
-
align-self: end;
|
|
146
|
+
/* align-self: end; */
|
|
147
147
|
display: flex;
|
|
148
|
+
justify-content: flex-end;
|
|
148
149
|
padding-top: 20px;
|
|
149
150
|
}
|
|
150
151
|
& .card-title {
|
|
151
152
|
align-items: center;
|
|
152
153
|
display: flex;
|
|
153
154
|
width: 100%;
|
|
155
|
+
margin-bottom: 15px;
|
|
154
156
|
h5 {
|
|
155
157
|
margin: 0;
|
|
156
158
|
}
|
|
@@ -124,10 +124,23 @@ export default defineComponent({
|
|
|
124
124
|
type: String,
|
|
125
125
|
default: undefined
|
|
126
126
|
},
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Inherited global identifier prefix for tests
|
|
130
|
+
* Define a term based on the parent component to avoid conflicts on multiple components
|
|
131
|
+
*/
|
|
132
|
+
componentTestid: {
|
|
133
|
+
type: String,
|
|
134
|
+
default: 'checkbox'
|
|
135
|
+
},
|
|
127
136
|
},
|
|
128
137
|
|
|
129
138
|
emits: ['update:value'],
|
|
130
139
|
|
|
140
|
+
data() {
|
|
141
|
+
return { describedById: `described-by-${ generateRandomAlphaString(12) }` };
|
|
142
|
+
},
|
|
143
|
+
|
|
131
144
|
computed: {
|
|
132
145
|
/**
|
|
133
146
|
* Determines if the checkbox is disabled.
|
|
@@ -270,6 +283,7 @@ export default defineComponent({
|
|
|
270
283
|
:aria-label="replacementLabel"
|
|
271
284
|
:aria-checked="!!value"
|
|
272
285
|
:aria-labelledby="labelKey || label ? idForLabel : undefined"
|
|
286
|
+
:aria-describedby="descriptionKey || description ? describedById : undefined"
|
|
273
287
|
role="checkbox"
|
|
274
288
|
/>
|
|
275
289
|
<span
|
|
@@ -293,6 +307,7 @@ export default defineComponent({
|
|
|
293
307
|
v-clean-tooltip="{content: t(tooltipKey), triggers: ['hover', 'touch', 'focus']}"
|
|
294
308
|
v-stripped-aria-label="t(tooltipKey)"
|
|
295
309
|
class="checkbox-info icon icon-info icon-lg"
|
|
310
|
+
:data-testid="componentTestid + '-info-icon'"
|
|
296
311
|
:tabindex="isDisabled ? -1 : 0"
|
|
297
312
|
/>
|
|
298
313
|
<i
|
|
@@ -300,6 +315,7 @@ export default defineComponent({
|
|
|
300
315
|
v-clean-tooltip="{content: tooltip, triggers: ['hover', 'touch', 'focus']}"
|
|
301
316
|
v-stripped-aria-label="tooltip"
|
|
302
317
|
class="checkbox-info icon icon-info icon-lg"
|
|
318
|
+
:data-testid="componentTestid + '-info-icon'"
|
|
303
319
|
:tabindex="isDisabled ? -1 : 0"
|
|
304
320
|
/>
|
|
305
321
|
</slot>
|
|
@@ -311,10 +327,13 @@ export default defineComponent({
|
|
|
311
327
|
>
|
|
312
328
|
<t
|
|
313
329
|
v-if="descriptionKey"
|
|
330
|
+
:id="describedById"
|
|
314
331
|
:k="descriptionKey"
|
|
315
332
|
/>
|
|
316
333
|
<template v-else-if="description">
|
|
317
|
-
|
|
334
|
+
<p :id="describedById">
|
|
335
|
+
{{ description }}
|
|
336
|
+
</p>
|
|
318
337
|
</template>
|
|
319
338
|
</div>
|
|
320
339
|
<div class="checkbox-outer-container-extra">
|
|
@@ -4,6 +4,7 @@ import TextAreaAutoGrow from '@components/Form/TextArea/TextAreaAutoGrow.vue';
|
|
|
4
4
|
import LabeledTooltip from '@components/LabeledTooltip/LabeledTooltip.vue';
|
|
5
5
|
import { escapeHtml, generateRandomAlphaString } from '@shell/utils/string';
|
|
6
6
|
import cronstrue from 'cronstrue';
|
|
7
|
+
import 'cronstrue/locales/zh_CN';
|
|
7
8
|
import { isValidCron } from 'cron-validator';
|
|
8
9
|
import { debounce } from 'lodash';
|
|
9
10
|
import { useLabeledFormElement, labeledFormElementProps } from '@shell/composables/useLabeledFormElement';
|
|
@@ -148,7 +149,8 @@ export default defineComponent({
|
|
|
148
149
|
return {
|
|
149
150
|
updated: false,
|
|
150
151
|
validationErrors: '',
|
|
151
|
-
inputId: `input-${ generateRandomAlphaString(12) }
|
|
152
|
+
inputId: `input-${ generateRandomAlphaString(12) }`,
|
|
153
|
+
describedById: `described-by-${ generateRandomAlphaString(12) }`
|
|
152
154
|
};
|
|
153
155
|
},
|
|
154
156
|
|
|
@@ -212,7 +214,8 @@ export default defineComponent({
|
|
|
212
214
|
}
|
|
213
215
|
|
|
214
216
|
try {
|
|
215
|
-
const hint = cronstrue.toString(this.value as string || '', { verbose: true });
|
|
217
|
+
// const hint = cronstrue.toString(this.value as string || '', { verbose: true });
|
|
218
|
+
const hint = cronstrue.toString(this.value as string || '', { locale: 'zh_CN' });
|
|
216
219
|
|
|
217
220
|
return hint;
|
|
218
221
|
} catch (e) {
|
|
@@ -333,6 +336,24 @@ export default defineComponent({
|
|
|
333
336
|
</script>
|
|
334
337
|
|
|
335
338
|
<template>
|
|
339
|
+
<div class="label-input-all">
|
|
340
|
+
<slot name="label">
|
|
341
|
+
<label
|
|
342
|
+
v-if="hasLabel"
|
|
343
|
+
:for="inputId"
|
|
344
|
+
>
|
|
345
|
+
<t
|
|
346
|
+
v-if="labelKey"
|
|
347
|
+
:k="labelKey"
|
|
348
|
+
/>
|
|
349
|
+
<template v-else-if="label">{{ label }}</template>
|
|
350
|
+
|
|
351
|
+
<span
|
|
352
|
+
v-if="requiredField"
|
|
353
|
+
class="required"
|
|
354
|
+
>*</span>
|
|
355
|
+
</label>
|
|
356
|
+
</slot>
|
|
336
357
|
<div
|
|
337
358
|
:class="{
|
|
338
359
|
'labeled-input': true,
|
|
@@ -347,7 +368,7 @@ export default defineComponent({
|
|
|
347
368
|
[className]: true
|
|
348
369
|
}"
|
|
349
370
|
>
|
|
350
|
-
<slot name="label">
|
|
371
|
+
<!-- <slot name="label">
|
|
351
372
|
<label
|
|
352
373
|
v-if="hasLabel"
|
|
353
374
|
:for="inputId"
|
|
@@ -363,7 +384,7 @@ export default defineComponent({
|
|
|
363
384
|
class="required"
|
|
364
385
|
>*</span>
|
|
365
386
|
</label>
|
|
366
|
-
</slot>
|
|
387
|
+
</slot> -->
|
|
367
388
|
|
|
368
389
|
<slot name="prefix" />
|
|
369
390
|
|
|
@@ -380,6 +401,7 @@ export default defineComponent({
|
|
|
380
401
|
:placeholder="_placeholder"
|
|
381
402
|
autocapitalize="off"
|
|
382
403
|
:class="{ conceal: type === 'multiline-password' }"
|
|
404
|
+
:aria-describedby="cronHint || subLabel ? describedById : undefined"
|
|
383
405
|
@update:value="onInput"
|
|
384
406
|
@focus="onFocus"
|
|
385
407
|
@blur="onBlur"
|
|
@@ -400,6 +422,7 @@ export default defineComponent({
|
|
|
400
422
|
autocomplete="off"
|
|
401
423
|
autocapitalize="off"
|
|
402
424
|
:data-lpignore="ignorePasswordManagers"
|
|
425
|
+
:aria-describedby="cronHint || subLabel ? describedById : undefined"
|
|
403
426
|
@input="onInput"
|
|
404
427
|
@focus="onFocus"
|
|
405
428
|
@blur="onBlur"
|
|
@@ -428,17 +451,20 @@ export default defineComponent({
|
|
|
428
451
|
>
|
|
429
452
|
<div
|
|
430
453
|
v-if="cronHint"
|
|
454
|
+
:id="describedById"
|
|
431
455
|
role="alert"
|
|
432
456
|
:aria-label="cronHint"
|
|
433
457
|
>
|
|
434
458
|
{{ cronHint }}
|
|
435
459
|
</div>
|
|
436
460
|
<div
|
|
437
|
-
v-if="subLabel"
|
|
461
|
+
v-else-if="subLabel"
|
|
462
|
+
:id="describedById"
|
|
438
463
|
v-clean-html="subLabel"
|
|
439
464
|
/>
|
|
440
465
|
</div>
|
|
441
466
|
</div>
|
|
467
|
+
</div>
|
|
442
468
|
</template>
|
|
443
469
|
<style scoped lang="scss">
|
|
444
470
|
.labeled-input.view {
|
|
@@ -461,6 +487,21 @@ export default defineComponent({
|
|
|
461
487
|
-moz-appearance: textfield;
|
|
462
488
|
}
|
|
463
489
|
}
|
|
490
|
+
.label-input-all{
|
|
491
|
+
display: flex;
|
|
492
|
+
label{
|
|
493
|
+
width: 160px;
|
|
494
|
+
line-height: 32px;
|
|
495
|
+
.required{
|
|
496
|
+
color: red;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.v-popper--has-tooltip INPUT, .v-popper--has-tooltip INPUT:hover, .v-popper--has-tooltip INPUT:focus{
|
|
502
|
+
padding: 0px 0px 0px 11px;
|
|
503
|
+
}
|
|
504
|
+
|
|
464
505
|
</style>
|
|
465
506
|
<style>
|
|
466
507
|
.validation-message {
|
|
@@ -251,15 +251,21 @@ $fontColor: var(--input-label);
|
|
|
251
251
|
cursor: not-allowed
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
-
|
|
255
|
-
height:
|
|
256
|
-
width:
|
|
257
|
-
min-height:
|
|
258
|
-
min-width:
|
|
254
|
+
.radio-custom {
|
|
255
|
+
height: 12px;
|
|
256
|
+
width: 12px;
|
|
257
|
+
min-height: 13px;
|
|
258
|
+
min-width: 13px;
|
|
259
259
|
background-color: var(--input-bg);
|
|
260
260
|
border-radius: 50%;
|
|
261
|
+
transition: all 0.3s ease-out;
|
|
261
262
|
border: 1.5px solid var(--border);
|
|
262
|
-
margin-top:
|
|
263
|
+
margin-top: 4px;
|
|
264
|
+
|
|
265
|
+
&:focus {
|
|
266
|
+
outline: none;
|
|
267
|
+
border-radius: 50%;
|
|
268
|
+
}
|
|
263
269
|
}
|
|
264
270
|
|
|
265
271
|
input {
|
|
@@ -268,13 +274,31 @@ $fontColor: var(--input-label);
|
|
|
268
274
|
|
|
269
275
|
.radio-custom {
|
|
270
276
|
&[aria-checked="true"] {
|
|
271
|
-
background-color:
|
|
277
|
+
background-color: #fff;
|
|
272
278
|
-webkit-transform: rotate(0deg) scale(1);
|
|
273
279
|
-ms-transform: rotate(0deg) scale(1);
|
|
274
280
|
transform: rotate(0deg) scale(1);
|
|
275
281
|
opacity:1;
|
|
276
282
|
border: 1.5px solid var(--primary);
|
|
283
|
+
display: flex;
|
|
284
|
+
align-items: center;
|
|
285
|
+
justify-content: center;
|
|
286
|
+
|
|
277
287
|
|
|
288
|
+
&::after {
|
|
289
|
+
background-color: var(--primary);
|
|
290
|
+
width: 7px;
|
|
291
|
+
height: 7px;
|
|
292
|
+
display: inline;
|
|
293
|
+
content: "";
|
|
294
|
+
/* position: absolute; */
|
|
295
|
+
/* top: 17%;
|
|
296
|
+
left: 19%;
|
|
297
|
+
margin-left: 0.4px; */
|
|
298
|
+
/* top: 1.5px;
|
|
299
|
+
left: 1.5px; */
|
|
300
|
+
border-radius: 50%;
|
|
301
|
+
}
|
|
278
302
|
// Ensure that checked radio buttons are muted but still visibly selected when muted
|
|
279
303
|
&.text-muted {
|
|
280
304
|
opacity: .25;
|
|
@@ -297,7 +321,7 @@ $fontColor: var(--input-label);
|
|
|
297
321
|
display: inline-flex;
|
|
298
322
|
flex-direction: column;
|
|
299
323
|
|
|
300
|
-
margin: 3px
|
|
324
|
+
margin: 3px 16px 0px 5px;
|
|
301
325
|
}
|
|
302
326
|
}
|
|
303
327
|
|
|
@@ -199,33 +199,35 @@ export default defineComponent({
|
|
|
199
199
|
</script>
|
|
200
200
|
|
|
201
201
|
<template>
|
|
202
|
-
<
|
|
202
|
+
<fieldset>
|
|
203
203
|
<!-- Label -->
|
|
204
204
|
<div
|
|
205
205
|
v-if="label || labelKey || tooltip || tooltipKey || $slots.label"
|
|
206
206
|
class="radio-group label"
|
|
207
207
|
>
|
|
208
|
-
<
|
|
209
|
-
<
|
|
210
|
-
<
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
208
|
+
<legend>
|
|
209
|
+
<slot name="label">
|
|
210
|
+
<span class="radio-group-title">
|
|
211
|
+
<t
|
|
212
|
+
v-if="labelKey"
|
|
213
|
+
:k="labelKey"
|
|
214
|
+
/>
|
|
215
|
+
<template v-else-if="label">
|
|
216
|
+
{{ label }}
|
|
217
|
+
</template>
|
|
218
|
+
<i
|
|
219
|
+
v-if="tooltipKey"
|
|
220
|
+
v-clean-tooltip="t(tooltipKey)"
|
|
221
|
+
class="icon icon-info icon-lg"
|
|
222
|
+
/>
|
|
223
|
+
<i
|
|
224
|
+
v-else-if="tooltip"
|
|
225
|
+
v-clean-tooltip="tooltip"
|
|
226
|
+
class="icon icon-info icon-lg"
|
|
227
|
+
/>
|
|
228
|
+
</span>
|
|
229
|
+
</slot>
|
|
230
|
+
</legend>
|
|
229
231
|
</div>
|
|
230
232
|
|
|
231
233
|
<!-- Group -->
|
|
@@ -266,7 +268,7 @@ export default defineComponent({
|
|
|
266
268
|
</slot>
|
|
267
269
|
</div>
|
|
268
270
|
</div>
|
|
269
|
-
</
|
|
271
|
+
</fieldset>
|
|
270
272
|
</template>
|
|
271
273
|
|
|
272
274
|
<style lang='scss'>
|
|
@@ -292,7 +294,12 @@ export default defineComponent({
|
|
|
292
294
|
}
|
|
293
295
|
|
|
294
296
|
.label{
|
|
295
|
-
font-size:
|
|
297
|
+
font-size: 12px !important;
|
|
296
298
|
}
|
|
297
299
|
}
|
|
300
|
+
.radio-group-title{
|
|
301
|
+
font-size: 14px;
|
|
302
|
+
margin-bottom: 15px;
|
|
303
|
+
display: inline-block;
|
|
304
|
+
}
|
|
298
305
|
</style>
|
|
@@ -91,4 +91,21 @@ describe('toggleSwitch.vue', () => {
|
|
|
91
91
|
expect(wrapper.emitted('update:value')).toHaveLength(1);
|
|
92
92
|
expect(wrapper.emitted('update:value')[0][0]).toBe(offValue);
|
|
93
93
|
});
|
|
94
|
+
|
|
95
|
+
it('adds focus class when input is focused', async() => {
|
|
96
|
+
const wrapper = shallowMount(ToggleSwitch);
|
|
97
|
+
|
|
98
|
+
await wrapper.find('input').trigger('focus');
|
|
99
|
+
|
|
100
|
+
expect(wrapper.find('.slider').classes()).toContain('focus');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('removes focus class when input is blurred', async() => {
|
|
104
|
+
const wrapper = shallowMount(ToggleSwitch);
|
|
105
|
+
|
|
106
|
+
await wrapper.find('input').trigger('focus');
|
|
107
|
+
await wrapper.find('input').trigger('blur');
|
|
108
|
+
|
|
109
|
+
expect(wrapper.find('.slider').classes()).not.toContain('focus');
|
|
110
|
+
});
|
|
94
111
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { defineComponent, onMounted, onBeforeUnmount,
|
|
2
|
+
import { defineComponent, onMounted, onBeforeUnmount, ref } from 'vue';
|
|
3
3
|
|
|
4
4
|
type StateType = boolean | 'true' | 'false' | undefined;
|
|
5
5
|
|
|
@@ -34,7 +34,7 @@ export default defineComponent({
|
|
|
34
34
|
emits: ['update:value'],
|
|
35
35
|
|
|
36
36
|
setup() {
|
|
37
|
-
const switchChrome =
|
|
37
|
+
const switchChrome = ref<HTMLElement | null>(null);
|
|
38
38
|
const focus = () => {
|
|
39
39
|
switchChrome.value?.classList.add('focus');
|
|
40
40
|
};
|
|
@@ -43,7 +43,7 @@ export default defineComponent({
|
|
|
43
43
|
switchChrome.value?.classList.remove('focus');
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
-
const switchInput =
|
|
46
|
+
const switchInput = ref<HTMLInputElement | null>(null);
|
|
47
47
|
|
|
48
48
|
onMounted(() => {
|
|
49
49
|
switchInput.value?.addEventListener('focus', focus);
|
|
@@ -54,6 +54,11 @@ export default defineComponent({
|
|
|
54
54
|
switchInput.value?.removeEventListener('focus', focus);
|
|
55
55
|
switchInput.value?.removeEventListener('blur', blur);
|
|
56
56
|
});
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
switchChrome,
|
|
60
|
+
switchInput,
|
|
61
|
+
};
|
|
57
62
|
},
|
|
58
63
|
|
|
59
64
|
data() {
|
|
@@ -26,7 +26,15 @@ export default defineComponent({
|
|
|
26
26
|
hover: {
|
|
27
27
|
type: Boolean,
|
|
28
28
|
default: true
|
|
29
|
-
}
|
|
29
|
+
},
|
|
30
|
+
/**
|
|
31
|
+
* Inherited global identifier prefix for tests
|
|
32
|
+
* Define a term based on the parent component to avoid conflicts on multiple components
|
|
33
|
+
*/
|
|
34
|
+
componentTestid: {
|
|
35
|
+
type: String,
|
|
36
|
+
default: 'labeledTooltip-info-icon'
|
|
37
|
+
},
|
|
30
38
|
},
|
|
31
39
|
computed: {
|
|
32
40
|
iconClass(): string {
|
|
@@ -64,6 +72,7 @@ export default defineComponent({
|
|
|
64
72
|
:class="{'hover':!value, [iconClass]: true}"
|
|
65
73
|
class="icon status-icon"
|
|
66
74
|
tabindex="0"
|
|
75
|
+
:data-testid="componentTestid"
|
|
67
76
|
/>
|
|
68
77
|
</template>
|
|
69
78
|
<template v-else>
|
|
@@ -99,9 +108,12 @@ export default defineComponent({
|
|
|
99
108
|
|
|
100
109
|
.status-icon {
|
|
101
110
|
position: absolute;
|
|
102
|
-
right:
|
|
111
|
+
right: 5px;
|
|
112
|
+
top: 10px;
|
|
113
|
+
z-index: 3;
|
|
114
|
+
/* right: 30px;
|
|
103
115
|
top: $input-padding-lg;
|
|
104
|
-
z-index: z-index(hoverOverContent);
|
|
116
|
+
z-index: z-index(hoverOverContent); */
|
|
105
117
|
}
|
|
106
118
|
|
|
107
119
|
@mixin tooltipColors($color) {
|
|
@@ -15,6 +15,7 @@ const buttonRoles: { role: keyof ButtonRoleProps, className: string }[] = [
|
|
|
15
15
|
{ role: 'secondary', className: 'role-secondary' },
|
|
16
16
|
{ role: 'tertiary', className: 'role-tertiary' },
|
|
17
17
|
{ role: 'link', className: 'role-link' },
|
|
18
|
+
{ role: 'multiAction', className: 'role-multi-action' },
|
|
18
19
|
{ role: 'ghost', className: 'role-ghost' },
|
|
19
20
|
];
|
|
20
21
|
|
|
@@ -20,13 +20,22 @@
|
|
|
20
20
|
* </template>
|
|
21
21
|
* </rc-dropdown>
|
|
22
22
|
*/
|
|
23
|
-
import {
|
|
23
|
+
import { ref } from 'vue';
|
|
24
24
|
import { useClickOutside } from '@shell/composables/useClickOutside';
|
|
25
25
|
import { useDropdownContext } from '@components/RcDropdown/useDropdownContext';
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
import type { Placement } from 'floating-vue';
|
|
28
|
+
|
|
29
|
+
withDefaults(
|
|
30
|
+
defineProps<{
|
|
31
|
+
// eslint-disable-next-line vue/require-default-prop
|
|
32
|
+
ariaLabel?: string;
|
|
33
|
+
// eslint-disable-next-line vue/require-default-prop
|
|
34
|
+
distance?: number;
|
|
35
|
+
placement?: Placement;
|
|
36
|
+
}>(),
|
|
37
|
+
{ placement: 'bottom-end' }
|
|
38
|
+
);
|
|
30
39
|
|
|
31
40
|
const emit = defineEmits(['update:open']);
|
|
32
41
|
|
|
@@ -42,14 +51,14 @@ const {
|
|
|
42
51
|
|
|
43
52
|
provideDropdownContext();
|
|
44
53
|
|
|
45
|
-
const popperContainer =
|
|
46
|
-
const dropdownTarget =
|
|
54
|
+
// const popperContainer = ref(null);
|
|
55
|
+
const dropdownTarget = ref(null);
|
|
47
56
|
|
|
48
57
|
useClickOutside(dropdownTarget, () => showMenu(false));
|
|
49
58
|
|
|
50
59
|
const applyShow = () => {
|
|
51
60
|
registerDropdownCollection(dropdownTarget.value);
|
|
52
|
-
setFocus();
|
|
61
|
+
setFocus('down');
|
|
53
62
|
};
|
|
54
63
|
|
|
55
64
|
</script>
|
|
@@ -60,15 +69,30 @@ const applyShow = () => {
|
|
|
60
69
|
:triggers="[]"
|
|
61
70
|
:shown="isMenuOpen"
|
|
62
71
|
:auto-hide="false"
|
|
63
|
-
|
|
64
|
-
:placement="
|
|
72
|
+
append-to-body
|
|
73
|
+
:placement="placement"
|
|
74
|
+
:distance="distance"
|
|
65
75
|
@apply-show="applyShow"
|
|
76
|
+
popper-class="custom-dropdown"
|
|
66
77
|
>
|
|
67
78
|
<slot name="default">
|
|
68
79
|
<!--Empty slot content Trigger-->
|
|
69
80
|
</slot>
|
|
70
81
|
|
|
71
82
|
<template #popper>
|
|
83
|
+
<!-- <div
|
|
84
|
+
ref="dropdownTarget"
|
|
85
|
+
class="dropdownTarget"
|
|
86
|
+
tabindex="-1"
|
|
87
|
+
role="menu"
|
|
88
|
+
aria-orientation="vertical"
|
|
89
|
+
dropdown-menu-collection
|
|
90
|
+
:aria-label="ariaLabel || 'Dropdown Menu'"
|
|
91
|
+
@keydown="handleKeydown"
|
|
92
|
+
@keydown.down.prevent="setFocus('down')"
|
|
93
|
+
@keydown.up.prevent="setFocus('up')"
|
|
94
|
+
|
|
95
|
+
> -->
|
|
72
96
|
<div
|
|
73
97
|
ref="dropdownTarget"
|
|
74
98
|
class="dropdownTarget"
|
|
@@ -78,7 +102,10 @@ const applyShow = () => {
|
|
|
78
102
|
dropdown-menu-collection
|
|
79
103
|
:aria-label="ariaLabel || 'Dropdown Menu'"
|
|
80
104
|
@keydown="handleKeydown"
|
|
81
|
-
@keydown.down="setFocus()"
|
|
105
|
+
@keydown.down.prevent="setFocus('down')"
|
|
106
|
+
@keydown.up.prevent="setFocus('up')"
|
|
107
|
+
@keydown.tab="showMenu(false)"
|
|
108
|
+
@keydown.escape="returnFocus"
|
|
82
109
|
>
|
|
83
110
|
<slot name="dropdownCollection">
|
|
84
111
|
<!--Empty slot content-->
|
|
@@ -86,14 +113,13 @@ const applyShow = () => {
|
|
|
86
113
|
</div>
|
|
87
114
|
</template>
|
|
88
115
|
</v-dropdown>
|
|
89
|
-
<div
|
|
116
|
+
<!-- <div
|
|
90
117
|
ref="popperContainer"
|
|
91
118
|
class="popperContainer"
|
|
92
119
|
@keydown.tab="showMenu(false)"
|
|
93
120
|
@keydown.escape="returnFocus"
|
|
94
121
|
>
|
|
95
|
-
|
|
96
|
-
</div>
|
|
122
|
+
</div> -->
|
|
97
123
|
</template>
|
|
98
124
|
|
|
99
125
|
<style lang="scss" scoped>
|
|
@@ -102,7 +128,8 @@ const applyShow = () => {
|
|
|
102
128
|
&:deep(.v-popper__popper) {
|
|
103
129
|
|
|
104
130
|
.v-popper__wrapper {
|
|
105
|
-
box-shadow:
|
|
131
|
+
box-shadow: 0 5px 20px var(--shadow);
|
|
132
|
+
/* box-shadow: 0px 6px 18px 0px rgba(0, 0, 0, 0.25), 0px 4px 10px 0px rgba(0, 0, 0, 0.15); */
|
|
106
133
|
border-radius: var(--border-radius-lg);
|
|
107
134
|
|
|
108
135
|
.v-popper__arrow-container {
|
|
@@ -110,7 +137,9 @@ const applyShow = () => {
|
|
|
110
137
|
}
|
|
111
138
|
|
|
112
139
|
.v-popper__inner {
|
|
113
|
-
padding: 10px 0 10px 0;
|
|
140
|
+
/* padding: 10px 0 10px 0; */
|
|
141
|
+
padding: 0px;
|
|
142
|
+
/* min-width: 145px; */
|
|
114
143
|
}
|
|
115
144
|
}
|
|
116
145
|
}
|
|
@@ -121,4 +150,14 @@ const applyShow = () => {
|
|
|
121
150
|
outline: none;
|
|
122
151
|
}
|
|
123
152
|
}
|
|
153
|
+
|
|
154
|
+
.custom-dropdown{
|
|
155
|
+
.v-popper__wrapper{
|
|
156
|
+
.v-popper__inner {
|
|
157
|
+
padding: 0px;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
124
162
|
</style>
|
|
163
|
+
|
|
@@ -83,7 +83,7 @@ const handleActivate = (e: KeyboardEvent) => {
|
|
|
83
83
|
:aria-disabled="disabled || false"
|
|
84
84
|
@click.stop="handleClick"
|
|
85
85
|
@keydown.enter.space="handleActivate"
|
|
86
|
-
@keydown.up.down.stop="handleKeydown"
|
|
86
|
+
@keydown.up.down.prevent.stop="handleKeydown"
|
|
87
87
|
>
|
|
88
88
|
<slot name="before">
|
|
89
89
|
<!--Empty slot content-->
|
|
@@ -99,9 +99,10 @@ const handleActivate = (e: KeyboardEvent) => {
|
|
|
99
99
|
display: flex;
|
|
100
100
|
gap: 8px;
|
|
101
101
|
align-items: center;
|
|
102
|
-
padding:
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
padding: 8px 10px;
|
|
103
|
+
min-width: 145px;
|
|
104
|
+
/* margin: 0 9px; */
|
|
105
|
+
/* border-radius: 4px; */
|
|
105
106
|
|
|
106
107
|
&:hover {
|
|
107
108
|
cursor: pointer;
|
|
@@ -8,8 +8,11 @@ import {
|
|
|
8
8
|
import { RcDropdownMenuComponentProps, DropdownOption } from './types';
|
|
9
9
|
import IconOrSvg from '@shell/components/IconOrSvg';
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
withDefaults(defineProps<RcDropdownMenuComponentProps>(), {
|
|
12
|
+
buttonRole: 'primary',
|
|
13
|
+
buttonSize: undefined,
|
|
14
|
+
isDetail: false,
|
|
15
|
+
});
|
|
13
16
|
|
|
14
17
|
const emit = defineEmits(['update:open', 'select']);
|
|
15
18
|
|
|
@@ -29,7 +32,8 @@ const hasOptions = (options: DropdownOption[]) => {
|
|
|
29
32
|
:data-testid="dataTestid"
|
|
30
33
|
:aria-label="buttonAriaLabel"
|
|
31
34
|
>
|
|
32
|
-
<i
|
|
35
|
+
<i style="font-style: normal;" v-if="buttonRole === 'link'">操作</i>
|
|
36
|
+
<i class="icon icon-actions" v-else/>
|
|
33
37
|
</rc-dropdown-trigger>
|
|
34
38
|
<template #dropdownCollection>
|
|
35
39
|
<template
|
|
@@ -40,7 +44,7 @@ const hasOptions = (options: DropdownOption[]) => {
|
|
|
40
44
|
v-if="!a.divider"
|
|
41
45
|
@click="(e: MouseEvent) => emit('select', e, a)"
|
|
42
46
|
>
|
|
43
|
-
<template #before>
|
|
47
|
+
<!-- <template #before>
|
|
44
48
|
<IconOrSvg
|
|
45
49
|
v-if="a.icon || a.svg"
|
|
46
50
|
:icon="a.icon"
|
|
@@ -48,12 +52,12 @@ const hasOptions = (options: DropdownOption[]) => {
|
|
|
48
52
|
class="icon"
|
|
49
53
|
color="header"
|
|
50
54
|
/>
|
|
51
|
-
</template>
|
|
55
|
+
</template> -->
|
|
52
56
|
{{ a.label }}
|
|
53
57
|
</rc-dropdown-item>
|
|
54
|
-
<rc-dropdown-separator
|
|
58
|
+
<!-- <rc-dropdown-separator
|
|
55
59
|
v-else
|
|
56
|
-
/>
|
|
60
|
+
/> -->
|
|
57
61
|
</template>
|
|
58
62
|
<rc-dropdown-item
|
|
59
63
|
v-if="!hasOptions(options)"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* A button that opens a menu. Used in conjunction with `RcDropdown.vue`.
|
|
4
4
|
*/
|
|
5
|
-
import { inject, onMounted,
|
|
5
|
+
import { inject, onMounted, ref } from 'vue';
|
|
6
6
|
import { RcButton, RcButtonType } from '@components/RcButton';
|
|
7
7
|
import { DropdownContext, defaultContext } from './types';
|
|
8
8
|
|
|
@@ -13,7 +13,7 @@ const {
|
|
|
13
13
|
handleKeydown,
|
|
14
14
|
} = inject<DropdownContext>('dropdownContext') || defaultContext;
|
|
15
15
|
|
|
16
|
-
const dropdownTrigger =
|
|
16
|
+
const dropdownTrigger = ref<RcButtonType | null>(null);
|
|
17
17
|
|
|
18
18
|
onMounted(() => {
|
|
19
19
|
registerTrigger(dropdownTrigger.value);
|
|
@@ -35,8 +35,18 @@ defineExpose({ focus });
|
|
|
35
35
|
@keydown.enter.space="handleKeydown"
|
|
36
36
|
@click="showMenu(true)"
|
|
37
37
|
>
|
|
38
|
+
<template #before>
|
|
39
|
+
<slot name="before">
|
|
40
|
+
<!-- Empty Content -->
|
|
41
|
+
</slot>
|
|
42
|
+
</template>
|
|
38
43
|
<slot name="default">
|
|
39
44
|
<!--Empty slot content-->
|
|
40
45
|
</slot>
|
|
46
|
+
<template #after>
|
|
47
|
+
<slot name="after">
|
|
48
|
+
<!-- Empty Content -->
|
|
49
|
+
</slot>
|
|
50
|
+
</template>
|
|
41
51
|
</RcButton>
|
|
42
52
|
</template>
|
|
@@ -10,6 +10,7 @@ export const useDropdownCollection = () => {
|
|
|
10
10
|
const dropdownItems = ref<Element[]>([]);
|
|
11
11
|
const dropdownContainer = ref<HTMLElement | null>(null);
|
|
12
12
|
const firstDropdownItem = ref<HTMLElement | null>(null);
|
|
13
|
+
const lastDropdownItem = ref<HTMLElement | null>(null);
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Registers the dropdown container and initializes dropdown items.
|
|
@@ -22,6 +23,12 @@ export const useDropdownCollection = () => {
|
|
|
22
23
|
if (dropdownItems.value[0] instanceof HTMLElement) {
|
|
23
24
|
firstDropdownItem.value = dropdownItems.value[0];
|
|
24
25
|
}
|
|
26
|
+
|
|
27
|
+
const lastItem = dropdownItems.value[dropdownItems.value.length - 1];
|
|
28
|
+
|
|
29
|
+
if (lastItem instanceof HTMLElement) {
|
|
30
|
+
lastDropdownItem.value = lastItem;
|
|
31
|
+
}
|
|
25
32
|
}
|
|
26
33
|
};
|
|
27
34
|
|
|
@@ -40,6 +47,7 @@ export const useDropdownCollection = () => {
|
|
|
40
47
|
return {
|
|
41
48
|
dropdownItems,
|
|
42
49
|
firstDropdownItem,
|
|
50
|
+
lastDropdownItem,
|
|
43
51
|
dropdownContainer,
|
|
44
52
|
registerDropdownCollection,
|
|
45
53
|
};
|
|
@@ -17,6 +17,7 @@ export const useDropdownContext = (emit: typeof rcDropdownEmits) => {
|
|
|
17
17
|
const {
|
|
18
18
|
dropdownItems,
|
|
19
19
|
firstDropdownItem,
|
|
20
|
+
lastDropdownItem,
|
|
20
21
|
dropdownContainer,
|
|
21
22
|
registerDropdownCollection,
|
|
22
23
|
} = useDropdownCollection();
|
|
@@ -70,7 +71,7 @@ export const useDropdownContext = (emit: typeof rcDropdownEmits) => {
|
|
|
70
71
|
/**
|
|
71
72
|
* Sets focus to the first dropdown item if a keydown event has occurred.
|
|
72
73
|
*/
|
|
73
|
-
const setFocus = () => {
|
|
74
|
+
const setFocus = (direction: 'down' | 'up') => {
|
|
74
75
|
nextTick(() => {
|
|
75
76
|
if (!didKeydown.value) {
|
|
76
77
|
dropdownContainer.value?.focus();
|
|
@@ -78,7 +79,12 @@ export const useDropdownContext = (emit: typeof rcDropdownEmits) => {
|
|
|
78
79
|
return;
|
|
79
80
|
}
|
|
80
81
|
|
|
81
|
-
|
|
82
|
+
if (direction === 'down') {
|
|
83
|
+
firstDropdownItem.value?.focus();
|
|
84
|
+
} else if (direction === 'up') {
|
|
85
|
+
lastDropdownItem.value?.focus();
|
|
86
|
+
}
|
|
87
|
+
|
|
82
88
|
didKeydown.value = false;
|
|
83
89
|
});
|
|
84
90
|
};
|
|
@@ -95,7 +101,7 @@ export const useDropdownContext = (emit: typeof rcDropdownEmits) => {
|
|
|
95
101
|
dropdownItems,
|
|
96
102
|
close: () => returnFocus(),
|
|
97
103
|
focusFirstElement: () => {
|
|
98
|
-
setFocus();
|
|
104
|
+
setFocus('down');
|
|
99
105
|
},
|
|
100
106
|
handleKeydown,
|
|
101
107
|
});
|
package/vue.config.js
CHANGED
|
@@ -37,7 +37,7 @@ const perfTest = (process.env.PERF_TEST === 'true'); // Enable performance testi
|
|
|
37
37
|
* Paths to the shell folder when it is included as a node dependency
|
|
38
38
|
*/
|
|
39
39
|
const getShellPaths = (dir) => {
|
|
40
|
-
let SHELL_ABS = path.join(dir, 'node_modules
|
|
40
|
+
let SHELL_ABS = path.join(dir, 'node_modules/dashboard-shell-shell');
|
|
41
41
|
let COMPONENTS_DIR = path.join(SHELL_ABS, 'rancher-components');
|
|
42
42
|
|
|
43
43
|
if (fs.existsSync(SHELL_ABS)) {
|