@carbon/vue 3.0.6-alpha.0 → 3.0.8-alpha.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/README.md +7 -0
- package/dist/carbon-vue-3.common.js +16111 -9105
- package/dist/carbon-vue-3.common.js.map +1 -1
- package/dist/carbon-vue-3.umd.js +16111 -9105
- package/dist/carbon-vue-3.umd.js.map +1 -1
- package/dist/carbon-vue-3.umd.min.js +4 -4
- package/dist/carbon-vue-3.umd.min.js.map +1 -1
- package/package.json +9 -6
- package/src/components/CvAccordion/CvAccordion.stories.js +1 -0
- package/src/components/CvButton/CvButton.stories.js +3 -3
- package/src/components/CvButton/CvButtonSkeleton.stories.js +79 -0
- package/src/components/CvButton/CvButtonSkeleton.vue +19 -0
- package/src/components/CvButton/index.js +8 -1
- package/src/components/CvCheckbox/CvCheckbox.stories.js +18 -7
- package/src/components/CvCheckbox/CvCheckbox.vue +19 -3
- package/src/components/CvCheckbox/CvCheckboxSkeleton.vue +16 -0
- package/src/components/CvCheckbox/__tests__/__snapshots__/CvCheckboxSkeleton.spec.js.snap +3 -0
- package/src/components/CvCheckbox/index.js +2 -1
- package/src/components/CvDatePicker/CvDatePicker.vue +1 -1
- package/src/components/CvDropdown/CvDropdown.stories.mdx +407 -0
- package/src/components/CvDropdown/CvDropdown.vue +416 -0
- package/src/components/CvDropdown/CvDropdownItem.vue +86 -0
- package/src/components/CvDropdown/CvDropdownSkeleton.vue +17 -0
- package/src/components/CvDropdown/index.js +5 -0
- package/src/components/CvFileUploader/CvFileUploader.stories.mdx +333 -0
- package/src/components/CvFileUploader/CvFileUploader.vue +286 -0
- package/src/components/CvFileUploader/CvFileUploaderItem.vue +92 -0
- package/src/components/CvFileUploader/CvFileUploaderSkeleton.vue +16 -0
- package/src/components/CvFileUploader/const.js +10 -0
- package/src/components/CvFileUploader/index.js +5 -0
- package/src/components/CvForm/CvFormGroup.vue +1 -1
- package/src/components/CvInlineLoading/CvInlineLoading.stories.mdx +87 -0
- package/src/components/CvInlineLoading/CvInlineLoading.vue +144 -0
- package/src/components/CvInlineLoading/consts.js +9 -0
- package/src/components/CvInlineLoading/index.js +4 -0
- package/src/components/CvModal/CvModal.stories.mdx +4 -2
- package/src/components/CvModal/CvModal.vue +3 -5
- package/src/components/CvMultiSelect/CvMultiSelect.stories.mdx +333 -0
- package/src/components/CvMultiSelect/CvMultiSelect.vue +705 -0
- package/src/components/CvMultiSelect/consts.js +16 -0
- package/src/components/CvMultiSelect/index.js +3 -0
- package/src/components/CvNumberInput/CvNumberInput.stories.js +265 -0
- package/src/components/CvNumberInput/CvNumberInput.vue +227 -0
- package/src/components/CvNumberInput/CvNumberInputSkeleton.stories.js +30 -0
- package/src/components/CvNumberInput/CvNumberInputSkeleton.vue +10 -0
- package/src/components/CvNumberInput/StorybookGroupConst.js +1 -0
- package/src/components/CvNumberInput/__tests__/__snapshots__/CvNumberInput.spec.js.snap +21 -0
- package/src/components/CvNumberInput/index.js +5 -0
- package/src/components/CvProgress/CvProgress.stories.mdx +202 -0
- package/src/components/CvProgress/CvProgress.vue +78 -0
- package/src/components/CvProgress/CvProgressStep.vue +124 -0
- package/src/components/CvProgress/index.js +4 -0
- package/src/components/CvRadioButton/CvRadioButton.stories.js +166 -0
- package/src/components/CvRadioButton/CvRadioButton.vue +57 -0
- package/src/components/CvRadioButton/CvRadioGroup.vue +27 -0
- package/src/components/CvRadioButton/__tests__/__snapshots__/CvRadioButton.spec.js.snap +27 -0
- package/src/components/CvRadioButton/index.js +6 -0
- package/src/components/CvSearch/CvSearch.stories.mdx +168 -0
- package/src/components/CvSearch/CvSearch.vue +250 -0
- package/src/components/CvSearch/index.js +3 -0
- package/src/components/CvSelect/CvSelect.vue +27 -6
- package/src/components/CvSlider/CvSlider.stories.js +166 -0
- package/src/components/CvSlider/CvSlider.vue +324 -0
- package/src/components/CvSlider/CvSliderSkeleton.stories.js +26 -0
- package/src/components/CvSlider/CvSliderSkeleton.vue +18 -0
- package/src/components/CvSlider/__tests__/__snapshots__/CvSlider.spec.js.snap +13 -0
- package/src/components/CvSlider/__tests__/__snapshots__/CvSliderSkeleton.spec.js.snap +13 -0
- package/src/components/CvSlider/index.js +5 -0
- package/src/components/CvSlider/sbSliderPrefix.js +2 -0
- package/src/components/CvTabs/CvTab.vue +77 -0
- package/src/components/CvTabs/CvTabs.stories.mdx +175 -0
- package/src/components/CvTabs/CvTabs.vue +406 -0
- package/src/components/CvTabs/CvTabsSkeleton.vue +24 -0
- package/src/components/CvTabs/_SbContainer.vue +22 -0
- package/src/components/CvTabs/index.js +5 -0
- package/src/components/CvTextArea/CvTextArea.stories.js +2 -2
- package/src/components/CvTextArea/CvTextAreaSkeleton.stories.js +29 -0
- package/src/components/CvTextArea/CvTextAreaSkeleton.vue +10 -0
- package/src/components/CvTextArea/StorybookGroupConst.js +1 -0
- package/src/components/CvTextArea/__tests__/__snapshots__/CvTextAreaSkeleton.spec.js.snap +7 -0
- package/src/components/CvTextArea/index.js +2 -1
- package/src/components/CvTimePicker/CvTimePicker.stories.mdx +325 -0
- package/src/components/CvTimePicker/CvTimePicker.vue +217 -0
- package/src/components/CvTimePicker/index.js +21 -0
- package/src/components/CvToggle/CvToggle-Skeleton.vue +44 -0
- package/src/components/CvToggle/CvToggle.stories.js +249 -0
- package/src/components/CvToggle/CvToggle.vue +87 -0
- package/src/components/CvToggle/__tests__/__snapshots__/CvToggle.spec.js.snap +7 -0
- package/src/components/CvToggle/index.js +4 -0
- package/src/components/CvTooltip/CvDefinitionTooltip.stories.js +80 -0
- package/src/components/CvTooltip/CvDefinitionTooltip.vue +61 -0
- package/src/components/CvTooltip/CvInteractiveTooltip.stories.js +90 -0
- package/src/components/CvTooltip/CvInteractiveTooltip.vue +267 -0
- package/src/components/CvTooltip/CvTooltip.stories.js +128 -0
- package/src/components/CvTooltip/CvTooltip.vue +44 -0
- package/src/components/CvTooltip/__tests__/__snapshots__/CvTooltip.spec.js.snap +525 -0
- package/src/components/CvTooltip/consts.js +15 -0
- package/src/components/CvTooltip/index.js +6 -0
- package/src/components/CvTooltip/sbTooltipPrefix.js +2 -0
- package/src/components/CvUIShell/CvSideNav.vue +10 -1
- package/src/components/CvUIShell/CvUIShell.stories.mdx +116 -50
- package/src/global/storybook-utils/story-source-code.js +1 -0
- package/src/index.d.ts +6 -0
- package/src/index.js +11 -0
- package/src/use/cvCheck.js +3 -0
- package/src/use/cvInput.js +34 -0
- package/src/use/index.js +2 -0
- package/src/use/useMethods.js +18 -0
- package/src/use/useRadio.js +25 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="`cv-slider ${carbonPrefix}--form-item`">
|
|
3
|
+
<label
|
|
4
|
+
v-show="label"
|
|
5
|
+
:for="cvId"
|
|
6
|
+
:class="[
|
|
7
|
+
`${carbonPrefix}--label`,
|
|
8
|
+
{ [`${carbonPrefix}--label--disabled`]: disabled },
|
|
9
|
+
]"
|
|
10
|
+
:id="labelId"
|
|
11
|
+
>
|
|
12
|
+
{{ label }}
|
|
13
|
+
</label>
|
|
14
|
+
|
|
15
|
+
<div :class="`${carbonPrefix}--slider-container`">
|
|
16
|
+
<span :class="`${carbonPrefix}--slider__range-label`">
|
|
17
|
+
{{ internalMinLabel }}
|
|
18
|
+
</span>
|
|
19
|
+
|
|
20
|
+
<div
|
|
21
|
+
:class="[
|
|
22
|
+
`${carbonPrefix}--slider`,
|
|
23
|
+
{ [`${carbonPrefix}--slider--disabled`]: disabled },
|
|
24
|
+
]"
|
|
25
|
+
data-slider
|
|
26
|
+
data-slider-input-box="#slider-input-box"
|
|
27
|
+
>
|
|
28
|
+
<div
|
|
29
|
+
:class="`${carbonPrefix}--slider__track`"
|
|
30
|
+
@click="onTrackClick"
|
|
31
|
+
ref="track"
|
|
32
|
+
></div>
|
|
33
|
+
|
|
34
|
+
<div
|
|
35
|
+
:class="`${carbonPrefix}--slider__filled-track`"
|
|
36
|
+
:style="`width: ${percentage};`"
|
|
37
|
+
></div>
|
|
38
|
+
|
|
39
|
+
<div
|
|
40
|
+
:class="[
|
|
41
|
+
`${carbonPrefix}--slider__thumb`,
|
|
42
|
+
{
|
|
43
|
+
[`${carbonPrefix}--slider__thumb--clicked`]: animateClick,
|
|
44
|
+
},
|
|
45
|
+
]"
|
|
46
|
+
tabindex="0"
|
|
47
|
+
:aria-labelledby="labelId"
|
|
48
|
+
:style="`left: ${percentage};`"
|
|
49
|
+
ref="thumb"
|
|
50
|
+
@keydown.up.right.prevent="onUp"
|
|
51
|
+
@keydown.down.left.prevent="onDown"
|
|
52
|
+
@mousedown="onStartDrag"
|
|
53
|
+
></div>
|
|
54
|
+
|
|
55
|
+
<input
|
|
56
|
+
:id="cvId"
|
|
57
|
+
:class="`${carbonPrefix}--slider__input`"
|
|
58
|
+
type="range"
|
|
59
|
+
:step="step"
|
|
60
|
+
:min="min"
|
|
61
|
+
:max="max"
|
|
62
|
+
ref="range"
|
|
63
|
+
/>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<span :class="`${carbonPrefix}--slider__range-label`">
|
|
67
|
+
{{ internalMaxLabel }}
|
|
68
|
+
</span>
|
|
69
|
+
|
|
70
|
+
<input
|
|
71
|
+
type="string"
|
|
72
|
+
:class="[
|
|
73
|
+
`${carbonPrefix}--text-input ${carbonPrefix}--slider-text-input`,
|
|
74
|
+
{ [`${carbonPrefix}--text-input--light`]: isLight },
|
|
75
|
+
]"
|
|
76
|
+
:placeholder="min"
|
|
77
|
+
v-model="internalValue"
|
|
78
|
+
@change="onChange"
|
|
79
|
+
ref="inputBox"
|
|
80
|
+
@keydown.up.prevent="onUp"
|
|
81
|
+
@keydown.down.prevent="onDown"
|
|
82
|
+
:disabled="disabled"
|
|
83
|
+
/>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
</template>
|
|
87
|
+
|
|
88
|
+
<script setup>
|
|
89
|
+
import { computed, ref, onMounted, nextTick, watch } from 'vue';
|
|
90
|
+
import { carbonPrefix } from '../../global/settings';
|
|
91
|
+
import { props as propsCvId, useCvId } from '../../use/cvId';
|
|
92
|
+
import { useIsLight, props as propsTheme } from '../../use/cvTheme';
|
|
93
|
+
|
|
94
|
+
const props = defineProps({
|
|
95
|
+
modelValue: {
|
|
96
|
+
type: String,
|
|
97
|
+
default: undefined,
|
|
98
|
+
},
|
|
99
|
+
value: {
|
|
100
|
+
type: String,
|
|
101
|
+
default: undefined,
|
|
102
|
+
},
|
|
103
|
+
disabled: Boolean,
|
|
104
|
+
label: {
|
|
105
|
+
type: String,
|
|
106
|
+
default: undefined,
|
|
107
|
+
},
|
|
108
|
+
// NOTE: It is not safe to rely on Numbers for non-integer steps
|
|
109
|
+
min: {
|
|
110
|
+
type: String,
|
|
111
|
+
default: '0',
|
|
112
|
+
},
|
|
113
|
+
max: {
|
|
114
|
+
type: String,
|
|
115
|
+
default: '100',
|
|
116
|
+
},
|
|
117
|
+
minLabel: {
|
|
118
|
+
type: String,
|
|
119
|
+
default: undefined,
|
|
120
|
+
},
|
|
121
|
+
maxLabel: {
|
|
122
|
+
type: String,
|
|
123
|
+
default: undefined,
|
|
124
|
+
},
|
|
125
|
+
step: {
|
|
126
|
+
type: String,
|
|
127
|
+
default: '1',
|
|
128
|
+
},
|
|
129
|
+
stepMultiplier: {
|
|
130
|
+
type: String,
|
|
131
|
+
default: '4',
|
|
132
|
+
validator(val) {
|
|
133
|
+
if (val.length) {
|
|
134
|
+
let intMultiplier = parseInt(val);
|
|
135
|
+
if (isNaN(intMultiplier) || intMultiplier < 1) {
|
|
136
|
+
console.warn('cv-slider: multiplier must be >= 1');
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return true;
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
...propsTheme,
|
|
144
|
+
...propsCvId,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const range = ref(null);
|
|
148
|
+
const thumb = ref(null);
|
|
149
|
+
const track = ref(null);
|
|
150
|
+
const DefaultRangeMin = 0;
|
|
151
|
+
const DefaultRangeMax = 100;
|
|
152
|
+
const DefaultValue = 0;
|
|
153
|
+
const StepNaNReplacement = 0;
|
|
154
|
+
const StepDefaultValue = 1;
|
|
155
|
+
const DefaultMultiplier = 4;
|
|
156
|
+
|
|
157
|
+
const emit = defineEmits(['update:modelValue', 'change', 'update:value']);
|
|
158
|
+
|
|
159
|
+
const cvId = useCvId(props, true);
|
|
160
|
+
const isLight = useIsLight(props);
|
|
161
|
+
const internalValue = ref('');
|
|
162
|
+
const animateClick = ref(false);
|
|
163
|
+
const isDragging = ref(false);
|
|
164
|
+
const dragStartX = ref(0);
|
|
165
|
+
const dragStartValue = ref(0);
|
|
166
|
+
const percentage = ref('0%');
|
|
167
|
+
|
|
168
|
+
const labelId = computed(() => `${cvId.value}-label`);
|
|
169
|
+
|
|
170
|
+
const internalMinLabel = computed(() => props.minLabel ?? getMin());
|
|
171
|
+
|
|
172
|
+
const internalMaxLabel = computed(() => props.maxLabel ?? getMax());
|
|
173
|
+
|
|
174
|
+
const rangePropsObserver = computed(() => ({
|
|
175
|
+
min: props.min,
|
|
176
|
+
max: props.max,
|
|
177
|
+
step: props.step,
|
|
178
|
+
}));
|
|
179
|
+
|
|
180
|
+
const internalMultiplier = computed(() => {
|
|
181
|
+
let intMultiplier = parseInt(props.stepMultiplier);
|
|
182
|
+
// default to 4 fro multiplier
|
|
183
|
+
return isNaN(intMultiplier) ? DefaultMultiplier : Math.max(intMultiplier, 1);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const getRangeAttributeValue = (
|
|
187
|
+
attributeName,
|
|
188
|
+
defaultForNaN,
|
|
189
|
+
defaultReturnValue
|
|
190
|
+
) => {
|
|
191
|
+
if (range?.value) {
|
|
192
|
+
const val = parseFloat(range.value[attributeName]);
|
|
193
|
+
return isNaN(val) ? defaultForNaN : val;
|
|
194
|
+
}
|
|
195
|
+
return defaultReturnValue ?? defaultForNaN;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const getMin = () => {
|
|
199
|
+
return getRangeAttributeValue('min', DefaultRangeMin);
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const getMax = () => {
|
|
203
|
+
return getRangeAttributeValue('max', DefaultRangeMax);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const getValue = () => {
|
|
207
|
+
const middleValue = (getMax() + getMin()) / 2;
|
|
208
|
+
return getRangeAttributeValue('value', DefaultValue, middleValue);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const getStep = () => {
|
|
212
|
+
return getRangeAttributeValue('step', StepNaNReplacement, StepDefaultValue);
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const setValue = newValue => {
|
|
216
|
+
if (props.disabled) return;
|
|
217
|
+
|
|
218
|
+
range.value.value = newValue;
|
|
219
|
+
internalValue.value = range.value.value;
|
|
220
|
+
|
|
221
|
+
percentage.value = `${
|
|
222
|
+
((internalValue.value - getMin()) * 100) / (getMax() - getMin())
|
|
223
|
+
}%`;
|
|
224
|
+
|
|
225
|
+
const { value: rangeValue } = range.value;
|
|
226
|
+
emit('update:modelValue', rangeValue);
|
|
227
|
+
emit('update:value', rangeValue);
|
|
228
|
+
emit('change', rangeValue);
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const onChange = () => {
|
|
232
|
+
let newValue = internalValue.value.length
|
|
233
|
+
? parseFloat(internalValue.value)
|
|
234
|
+
: getMin();
|
|
235
|
+
setValue(newValue);
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const onStartDrag = ev => {
|
|
239
|
+
document.body.addEventListener('mousemove', onDrag);
|
|
240
|
+
document.body.addEventListener('mouseup', onStopDrag);
|
|
241
|
+
|
|
242
|
+
dragStartX.value = ev.clientX;
|
|
243
|
+
dragStartValue.value = getValue();
|
|
244
|
+
isDragging.value = true;
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
const onDrag = ev => {
|
|
248
|
+
if (isDragging.value) {
|
|
249
|
+
// percentage change
|
|
250
|
+
let newValue = (ev.clientX - dragStartX.value) / track.value.offsetWidth;
|
|
251
|
+
// uncapped new value
|
|
252
|
+
newValue = dragStartValue.value + (getMax() - getMin()) * newValue;
|
|
253
|
+
setValue(newValue);
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const onStopDrag = () => {
|
|
258
|
+
isDragging.value = false;
|
|
259
|
+
document.body.removeEventListener('mousemove', onDrag);
|
|
260
|
+
document.body.removeEventListener('mouseup', onStopDrag);
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const onTrackClick = ev => {
|
|
264
|
+
const afterAnimate = ev => {
|
|
265
|
+
if (ev.propertyName === 'left') {
|
|
266
|
+
animateClick.value = false;
|
|
267
|
+
thumb.value.removeEventListener('transitionend', afterAnimate);
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
let newValue = ev.offsetX / track.value.offsetWidth;
|
|
272
|
+
const min = getMin();
|
|
273
|
+
newValue = (getMax() - min) * newValue + min;
|
|
274
|
+
thumb.value.addEventListener('transitionend', afterAnimate);
|
|
275
|
+
animateClick.value = true;
|
|
276
|
+
setValue(newValue);
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const onUpDown = (ev, isUp = true) => {
|
|
280
|
+
let curValue =
|
|
281
|
+
ev.target.type === 'number' ? parseFloat(ev.target.value) : getValue();
|
|
282
|
+
const step = getStep();
|
|
283
|
+
const progressValue = ev.shiftKey ? internalMultiplier.value * step : step;
|
|
284
|
+
const direction = isUp ? 1 : -1;
|
|
285
|
+
let newValue = curValue + progressValue * direction;
|
|
286
|
+
setValue(newValue);
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const onUp = ev => {
|
|
290
|
+
onUpDown(ev);
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
const onDown = ev => {
|
|
294
|
+
onUpDown(ev, false);
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
onMounted(() => {
|
|
298
|
+
range.value.value = props.value ?? props.modelValue;
|
|
299
|
+
internalValue.value = range.value.value;
|
|
300
|
+
percentage.value = `${
|
|
301
|
+
((internalValue.value - getMin()) * 100) / (getMax() - getMin())
|
|
302
|
+
}%`;
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
watch(
|
|
306
|
+
() => props.modelValue,
|
|
307
|
+
modelValue => {
|
|
308
|
+
setValue(modelValue);
|
|
309
|
+
}
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
watch(
|
|
313
|
+
() => props.value,
|
|
314
|
+
value => {
|
|
315
|
+
setValue(value);
|
|
316
|
+
}
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
watch(rangePropsObserver, () => {
|
|
320
|
+
nextTick(() => {
|
|
321
|
+
setValue(internalValue.value);
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
</script>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {
|
|
2
|
+
sbCompPrefix,
|
|
3
|
+
storyParametersObject,
|
|
4
|
+
} from '../../global/storybook-utils';
|
|
5
|
+
import { sbSliderPrefix } from './sbSliderPrefix';
|
|
6
|
+
import { CvSliderSkeleton } from '.';
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
title: `${sbCompPrefix}/${sbSliderPrefix}/CvSliderSkeleton`,
|
|
10
|
+
component: CvSliderSkeleton,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const template = `<cv-slider-skeleton />`;
|
|
14
|
+
const Template = () => {
|
|
15
|
+
return {
|
|
16
|
+
components: { CvSliderSkeleton },
|
|
17
|
+
template,
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const Default = Template.bind({});
|
|
22
|
+
Default.parameters = storyParametersObject(
|
|
23
|
+
Default.parameters,
|
|
24
|
+
template,
|
|
25
|
+
Default.args
|
|
26
|
+
);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="`cv-slider ${carbonPrefix}--form-item`">
|
|
3
|
+
<label :class="`${carbonPrefix}--label ${carbonPrefix}--skeleton`"></label>
|
|
4
|
+
<div :class="`${carbonPrefix}--slider-container ${carbonPrefix}--skeleton`">
|
|
5
|
+
<span :class="`${carbonPrefix}--slider__range-label`"></span>
|
|
6
|
+
<div :class="`${carbonPrefix}--slider`">
|
|
7
|
+
<div :class="`${carbonPrefix}--slider__track`"></div>
|
|
8
|
+
<div :class="`${carbonPrefix}--slider__filled-track`"></div>
|
|
9
|
+
<div :class="`${carbonPrefix}--slider__thumb`"></div>
|
|
10
|
+
</div>
|
|
11
|
+
<span :class="`${carbonPrefix}--slider__range-label`"></span>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script setup>
|
|
17
|
+
import { carbonPrefix } from '../../global/settings';
|
|
18
|
+
</script>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`CvSlider should match snapshot 1`] = `
|
|
4
|
+
<div class="cv-slider bx--form-item"><label for="abc" class="bx--label" id="abc-label" style="display: none;"></label>
|
|
5
|
+
<div class="bx--slider-container"><span class="bx--slider__range-label">0</span>
|
|
6
|
+
<div class="bx--slider" data-slider="" data-slider-input-box="#slider-input-box">
|
|
7
|
+
<div class="bx--slider__track"></div>
|
|
8
|
+
<div class="bx--slider__filled-track" style="width: 0%;"></div>
|
|
9
|
+
<div class="bx--slider__thumb" tabindex="0" aria-labelledby="abc-label" style="left: 0%;"></div><input id="abc" class="bx--slider__input" type="range" step="1" min="0" max="100">
|
|
10
|
+
</div><span class="bx--slider__range-label">100</span><input type="string" class="bx--text-input bx--slider-text-input" placeholder="0">
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
`;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`CvSliderSkeleton should match snapshot 1`] = `
|
|
4
|
+
<div class="cv-slider bx--form-item"><label class="bx--label bx--skeleton"></label>
|
|
5
|
+
<div class="bx--slider-container bx--skeleton"><span class="bx--slider__range-label"></span>
|
|
6
|
+
<div class="bx--slider">
|
|
7
|
+
<div class="bx--slider__track"></div>
|
|
8
|
+
<div class="bx--slider__filled-track"></div>
|
|
9
|
+
<div class="bx--slider__thumb"></div>
|
|
10
|
+
</div><span class="bx--slider__range-label"></span>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
`;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
:class="`cv-tab ${carbonPrefix}--tab-content`"
|
|
4
|
+
:id="uid"
|
|
5
|
+
role="tabpanel"
|
|
6
|
+
:aria-labelledby="`${uid}-link`"
|
|
7
|
+
:aria-hidden="!dataSelected ? 'true' : 'false'"
|
|
8
|
+
:hidden="!dataSelected"
|
|
9
|
+
>
|
|
10
|
+
<slot>
|
|
11
|
+
<!-- Content for tab goes here. -->
|
|
12
|
+
</slot>
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script setup>
|
|
17
|
+
import { carbonPrefix } from '../../global/settings';
|
|
18
|
+
import { useCvId, props as propsCvId } from '../../use/cvId';
|
|
19
|
+
import { inject, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
|
20
|
+
|
|
21
|
+
const props = defineProps({
|
|
22
|
+
selected: { type: Boolean, default: false },
|
|
23
|
+
disabled: { type: Boolean, default: false },
|
|
24
|
+
label: { type: String, required: true },
|
|
25
|
+
...propsCvId,
|
|
26
|
+
});
|
|
27
|
+
const uid = useCvId(props, true);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Is this tab selected?
|
|
31
|
+
* @type {Ref<UnwrapRef<string>>}
|
|
32
|
+
*/
|
|
33
|
+
const tabSelected = inject('tab-selected');
|
|
34
|
+
const dataSelected = ref(props.selected);
|
|
35
|
+
onMounted(() => {
|
|
36
|
+
if (props.selected) tabSelected.value = uid.value;
|
|
37
|
+
});
|
|
38
|
+
watch(
|
|
39
|
+
() => props.selected,
|
|
40
|
+
() => {
|
|
41
|
+
if (props.selected) tabSelected.value = uid.value;
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
watch(tabSelected, () => {
|
|
45
|
+
dataSelected.value = tabSelected.value === uid.value;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Is this tab disabled?
|
|
50
|
+
* @type {Ref<UnwrapRef<Set>>}
|
|
51
|
+
*/
|
|
52
|
+
const tabDisabledIds = inject('tab-disabled-ids');
|
|
53
|
+
function updateTabDisabled() {
|
|
54
|
+
if (props.disabled) tabDisabledIds.value?.add(uid.value);
|
|
55
|
+
else tabDisabledIds.value?.delete(uid.value);
|
|
56
|
+
}
|
|
57
|
+
onMounted(updateTabDisabled);
|
|
58
|
+
watch(() => props.disabled, updateTabDisabled);
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Keep this tab's data up to date
|
|
62
|
+
* @type {Ref<UnwrapRef<Array<TabData>>>}
|
|
63
|
+
*/
|
|
64
|
+
const tabData = inject('tab-data');
|
|
65
|
+
function updateTabData() {
|
|
66
|
+
const index = tabData.value?.findIndex(tab => tab.uid === uid.value);
|
|
67
|
+
if (index > -1)
|
|
68
|
+
tabData.value.splice(index, 1, { uid: uid.value, label: props.label });
|
|
69
|
+
else tabData.value.push({ uid: uid.value, label: props.label });
|
|
70
|
+
}
|
|
71
|
+
onMounted(updateTabData);
|
|
72
|
+
watch(() => props.label, updateTabData);
|
|
73
|
+
onBeforeUnmount(() => {
|
|
74
|
+
const index = tabData.value?.findIndex(tab => tab.uid === uid.value);
|
|
75
|
+
if (index > -1) tabData.value.splice(index, 1);
|
|
76
|
+
});
|
|
77
|
+
</script>
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { Canvas, Meta, Story } from '@storybook/addon-docs';
|
|
2
|
+
import { sbCompPrefix } from '../../global/storybook-utils';
|
|
3
|
+
import CvTabs from './CvTabs.vue';
|
|
4
|
+
import CvTab from './CvTab.vue';
|
|
5
|
+
import CvTabsSkeleton from './CvTabsSkeleton.vue';
|
|
6
|
+
import SbContainer from './_SbContainer.vue';
|
|
7
|
+
import { action } from '@storybook/addon-actions';
|
|
8
|
+
|
|
9
|
+
<Meta
|
|
10
|
+
title={`${sbCompPrefix}/CvTabs`}
|
|
11
|
+
component={CvTabs}
|
|
12
|
+
decorators={[
|
|
13
|
+
() => ({
|
|
14
|
+
components: { SbContainer, Story },
|
|
15
|
+
template: `<SbContainer><story/></SbContainer>`,
|
|
16
|
+
}),
|
|
17
|
+
]}
|
|
18
|
+
/>
|
|
19
|
+
|
|
20
|
+
export const Template = args => ({
|
|
21
|
+
// Components used in your story `template` are defined in the `components` object
|
|
22
|
+
components: {
|
|
23
|
+
CvTabs,
|
|
24
|
+
CvTab,
|
|
25
|
+
CvTabsSkeleton,
|
|
26
|
+
},
|
|
27
|
+
// The story's `args` need to be mapped into the template through the `setup()` method
|
|
28
|
+
setup() {
|
|
29
|
+
return {
|
|
30
|
+
container: args.container,
|
|
31
|
+
noDefaultToFirst: args.noDefaultToFirst,
|
|
32
|
+
selectedId: args.selectedId,
|
|
33
|
+
disabledIds: args.disabledIds,
|
|
34
|
+
onTabSelected: action('tab-selected'),
|
|
35
|
+
onTabSelectedId: action('tab-selected-id'),
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
// And then the `args` are bound to your component with `v-bind="args"`
|
|
39
|
+
template: args.template,
|
|
40
|
+
});
|
|
41
|
+
const defaultTemplate = `
|
|
42
|
+
<cv-tabs
|
|
43
|
+
:container="container"
|
|
44
|
+
:no-default-to-first="noDefaultToFirst"
|
|
45
|
+
@tab-selected="onTabSelected"
|
|
46
|
+
@tab-selected-id="onTabSelectedId"
|
|
47
|
+
aria-label="navigation tab label">
|
|
48
|
+
<cv-tab id="tab-1" label="House" :selected="selectedId === 'tab-1'" :disabled="disabledIds.includes('tab-1')">
|
|
49
|
+
<p>
|
|
50
|
+
29 Arlington Avenue is a house in Islington, London. Douglas Adams shared the house for a time
|
|
51
|
+
with Jonny Brock and Clare Gorst. It is mentioned in the dedication of The Hitchhiker's Guide to
|
|
52
|
+
the Galaxy, which is dedicated to the "Arlingtonians for tea, sympathy, and a sofa."
|
|
53
|
+
</p>
|
|
54
|
+
</cv-tab>
|
|
55
|
+
<cv-tab id="tab-2" label="Bar" :selected="selectedId === 'tab-2'" :disabled="disabledIds.includes('tab-2')">
|
|
56
|
+
<p>
|
|
57
|
+
The Old Pink Dog Bar was the bar in Han Dold City that Ford Prefect was in when he discovered
|
|
58
|
+
that his Guide had his full entry on Earth on it.
|
|
59
|
+
</p>
|
|
60
|
+
</cv-tab>
|
|
61
|
+
<cv-tab id="tab-3" label="Reach" :selected="selectedId === 'tab-3'" :disabled="disabledIds.includes('tab-3')">
|
|
62
|
+
<p>
|
|
63
|
+
The Third Reach of the Unknown is what might be loosely described as a "place" which exists only
|
|
64
|
+
under high improbability conditions and was created on the starship Heart of Gold due to the
|
|
65
|
+
Infinite Improbability Drive. During Arthur Dent and Ford Prefect's first trip on board the Heart
|
|
66
|
+
of Gold they witnessed many strange things happening.
|
|
67
|
+
</p>
|
|
68
|
+
</cv-tab>
|
|
69
|
+
<cv-tab id="tab-4" label="Zone" :selected="selectedId === 'tab-4'" :disabled="disabledIds.includes('tab-4')">
|
|
70
|
+
<p>Plural Zones are instabilities in space-time.
|
|
71
|
+
The effects of the Plural Zones on the beings and objects that inhabit and originate from them,
|
|
72
|
+
for the most part, remain largely unnoticed.
|
|
73
|
+
</p>
|
|
74
|
+
</cv-tab>
|
|
75
|
+
<cv-tab id="tab-5" label="Planet" :selected="selectedId === 'tab-5'" :disabled="disabledIds.includes('tab-5')">
|
|
76
|
+
<p>
|
|
77
|
+
Ursa Minor Beta is a rich and sunny planet and one of the most popular holiday destinations in
|
|
78
|
+
the Universe. It is always Saturday afternoon on this planet, just before the beach bars close -
|
|
79
|
+
except for the few places where it is eternally Saturday evening. This is why the locals always
|
|
80
|
+
invite visitors to "Have a nice diurnal anomaly!"
|
|
81
|
+
<a target='_blank' href='https://hitchhikers.fandom.com/wiki/Main_Page'>credits</a>
|
|
82
|
+
</p>
|
|
83
|
+
</cv-tab>
|
|
84
|
+
</cv-tabs>
|
|
85
|
+
`;
|
|
86
|
+
const skeletonTemplate = `<cv-tabs-skeleton></cv-tabs-skeleton>`;
|
|
87
|
+
|
|
88
|
+
# CvTabs
|
|
89
|
+
|
|
90
|
+
Migration notes:
|
|
91
|
+
|
|
92
|
+
- The property `leftOverflowIconButtonProps` is deprecated. It does not seem to be used at all in Vue 2 and thus does
|
|
93
|
+
nothing when specified.
|
|
94
|
+
- The property `rightOverflowIconButtonProps` is deprecated. It does not seem to be used at all in Vue 2 and thus does
|
|
95
|
+
nothing when specified.
|
|
96
|
+
- The property `scrollIntoView` is deprecated. It does not seem to be used at all in Vue 2 and thus does nothing when
|
|
97
|
+
specified.
|
|
98
|
+
- The `container` property in Vue 2 acts differently as compared to the React story book. Here the class/styles are
|
|
99
|
+
applied as is shown in the React storybook. Also note that the background color applied when this property is
|
|
100
|
+
specified is not from Carbon. The selector `.bx--tabs--container ~ div` can be used to set the background color and
|
|
101
|
+
other properties but that is up to the user.
|
|
102
|
+
- A new event emits `tab-selected-id` with the selected tab's id attribute.
|
|
103
|
+
|
|
104
|
+
## Mobile
|
|
105
|
+
|
|
106
|
+
In my opinion, this component is not suitable for mobile formats. The navigation of the tabs becomes awkward on smaller
|
|
107
|
+
screens, and so I suggest using a different component for mobile cases.
|
|
108
|
+
|
|
109
|
+
<Canvas>
|
|
110
|
+
<Story
|
|
111
|
+
name="Default"
|
|
112
|
+
parameters={{
|
|
113
|
+
controls: {
|
|
114
|
+
exclude: [
|
|
115
|
+
'default',
|
|
116
|
+
'template',
|
|
117
|
+
'leftOverflowIconButtonProps',
|
|
118
|
+
'rightOverflowIconButtonProps',
|
|
119
|
+
'scrollIntoView',
|
|
120
|
+
'tab-selected',
|
|
121
|
+
'tab-selected-id',
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
docs: { source: { code: defaultTemplate } },
|
|
125
|
+
}}
|
|
126
|
+
args={{
|
|
127
|
+
template: defaultTemplate,
|
|
128
|
+
selectedId: '',
|
|
129
|
+
disabledIds: [],
|
|
130
|
+
}}
|
|
131
|
+
argTypes={{
|
|
132
|
+
selectedId: {
|
|
133
|
+
control: 'select',
|
|
134
|
+
default: '',
|
|
135
|
+
options: ['', 'tab-1', 'tab-2', 'tab-3', 'tab-4', 'tab-5'],
|
|
136
|
+
},
|
|
137
|
+
disabledIds: {
|
|
138
|
+
control: 'multi-select',
|
|
139
|
+
options: ['tab-1', 'tab-2', 'tab-3', 'tab-4', 'tab-5'],
|
|
140
|
+
},
|
|
141
|
+
}}
|
|
142
|
+
>
|
|
143
|
+
{Template.bind({})}
|
|
144
|
+
</Story>
|
|
145
|
+
</Canvas>
|
|
146
|
+
|
|
147
|
+
# Skeleton
|
|
148
|
+
|
|
149
|
+
<Canvas>
|
|
150
|
+
<Story
|
|
151
|
+
name="skeleton"
|
|
152
|
+
parameters={{
|
|
153
|
+
controls: {
|
|
154
|
+
exclude: [
|
|
155
|
+
'default',
|
|
156
|
+
'template',
|
|
157
|
+
'leftOverflowIconButtonProps',
|
|
158
|
+
'rightOverflowIconButtonProps',
|
|
159
|
+
'scrollIntoView',
|
|
160
|
+
'tab-selected',
|
|
161
|
+
'tab-selected-id',
|
|
162
|
+
'container',
|
|
163
|
+
'noDefaultToFirst',
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
docs: { source: { code: skeletonTemplate } },
|
|
167
|
+
}}
|
|
168
|
+
args={{
|
|
169
|
+
template: skeletonTemplate,
|
|
170
|
+
}}
|
|
171
|
+
argTypes={{}}
|
|
172
|
+
>
|
|
173
|
+
{Template.bind({})}
|
|
174
|
+
</Story>
|
|
175
|
+
</Canvas>
|