@carbon/vue 3.0.7-alpha.0 → 3.0.9-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 +3850 -217
- package/dist/carbon-vue-3.common.js.map +1 -1
- package/dist/carbon-vue-3.umd.js +3850 -217
- 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 +7 -4
- 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/CvDropdown/CvDropdown.vue +0 -2
- 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/CvMultiSelect/CvMultiSelect.stories.mdx +1 -1
- package/src/components/CvNotification/CvToastNotification.stories.js +71 -1
- package/src/components/CvNotification/CvToastNotification.vue +6 -4
- package/src/components/CvOverflowMenu/CvOverflowMenu.stories.mdx +68 -12
- package/src/components/CvOverflowMenu/CvOverflowMenu.vue +39 -45
- package/src/components/CvOverflowMenu/CvOverflowMenuItem.vue +6 -11
- package/src/components/CvOverflowMenu/index.js +2 -1
- 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/CvStructuredList/CvStructuredList.stories.js +141 -0
- package/src/components/CvStructuredList/CvStructuredList.vue +44 -0
- package/src/components/CvStructuredList/CvStructuredListData.vue +19 -0
- package/src/components/CvStructuredList/CvStructuredListHeading.vue +11 -0
- package/src/components/CvStructuredList/CvStructuredListItem.vue +47 -0
- package/src/components/CvStructuredList/CvStructuredListItemSelectable.vue +56 -0
- package/src/components/CvStructuredList/CvStructuredListItemStandard.vue +11 -0
- package/src/components/CvStructuredList/__tests__/__snapshots__/CvStructuredList.spec.js.snap +73 -0
- package/src/components/CvStructuredList/__tests__/__snapshots__/CvStructuredListData.spec.js.snap +5 -0
- package/src/components/CvStructuredList/__tests__/__snapshots__/CvStructuredListHeading.spec.js.snap +9 -0
- package/src/components/CvStructuredList/__tests__/__snapshots__/CvStructuredListItem.spec.js.snap +7 -0
- package/src/components/CvStructuredList/index.js +12 -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 +27 -12
- package/src/components/CvToggle/CvToggle.stories.js +20 -3
- package/src/components/CvToggle/CvToggle.vue +22 -5
- package/src/components/CvToggle/__tests__/__snapshots__/CvToggle.spec.js.snap +3 -3
- 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/index.d.ts +6 -0
- package/src/index.js +11 -0
- package/src/use/cvCheck.js +3 -0
- package/src/use/index.js +2 -0
- package/src/use/useMethods.js +18 -0
- package/src/use/useRadio.js +24 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { action } from '@storybook/addon-actions';
|
|
2
|
+
import { CvRadioButton, CvRadioGroup } from '.';
|
|
3
|
+
import {
|
|
4
|
+
sbCompPrefix,
|
|
5
|
+
storyParametersObject,
|
|
6
|
+
} from '../../global/storybook-utils';
|
|
7
|
+
import { ref } from 'vue';
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
title: `${sbCompPrefix}/CvRadioButton`,
|
|
11
|
+
component: CvRadioButton,
|
|
12
|
+
argTypes: {
|
|
13
|
+
checked: {
|
|
14
|
+
type: 'boolean',
|
|
15
|
+
control: false,
|
|
16
|
+
table: {
|
|
17
|
+
type: { summary: 'boolean' },
|
|
18
|
+
category: 'props',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
label: {
|
|
22
|
+
description: 'radio-button label',
|
|
23
|
+
control: false,
|
|
24
|
+
},
|
|
25
|
+
hideLabel: {
|
|
26
|
+
description:
|
|
27
|
+
'makes the label visually hidden but still labels the radio input',
|
|
28
|
+
},
|
|
29
|
+
labelLeft: {
|
|
30
|
+
type: 'boolean',
|
|
31
|
+
table: {
|
|
32
|
+
type: { summary: 'boolean' },
|
|
33
|
+
category: 'props',
|
|
34
|
+
},
|
|
35
|
+
description: 'position label on left',
|
|
36
|
+
},
|
|
37
|
+
vertical: {
|
|
38
|
+
type: 'boolean',
|
|
39
|
+
table: {
|
|
40
|
+
type: { summary: 'boolean' },
|
|
41
|
+
category: 'props',
|
|
42
|
+
},
|
|
43
|
+
description:
|
|
44
|
+
'Property of CvRadioGroup component - stacks inside radio components vertically',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const DefaultRadioItems = [
|
|
50
|
+
{
|
|
51
|
+
label: 'radio-1',
|
|
52
|
+
id: 'value-1',
|
|
53
|
+
checked: true,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
label: 'radio-2',
|
|
57
|
+
id: 'value-2',
|
|
58
|
+
disabled: true,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
label: 'radio-3',
|
|
62
|
+
id: 'value-3',
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
const DefaultStoryParameters = {
|
|
67
|
+
controls: {
|
|
68
|
+
exclude: ['change', 'update:modelValue', 'modelValue'],
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const defaultTemplate = `
|
|
73
|
+
<cv-radio-group v-bind="args" @change="onChange">
|
|
74
|
+
<cv-radio-button
|
|
75
|
+
v-for="({id, label, checked, disabled}, index) in DefaultRadioItems"
|
|
76
|
+
:key="index"
|
|
77
|
+
name="group-1"
|
|
78
|
+
:label="label"
|
|
79
|
+
:value="id"
|
|
80
|
+
:checked="checked"
|
|
81
|
+
:disabled="disabled"
|
|
82
|
+
:hide-label="hideLabel"
|
|
83
|
+
:label-left='labelLeft'
|
|
84
|
+
/>
|
|
85
|
+
</cv-radio-group>
|
|
86
|
+
`;
|
|
87
|
+
|
|
88
|
+
const Template = (args, { argTypes }) => {
|
|
89
|
+
return {
|
|
90
|
+
props: Object.keys(argTypes),
|
|
91
|
+
components: { CvRadioButton, CvRadioGroup },
|
|
92
|
+
template: defaultTemplate,
|
|
93
|
+
setup() {
|
|
94
|
+
return {
|
|
95
|
+
args,
|
|
96
|
+
hideLabel: args.hideLabel,
|
|
97
|
+
labelLeft: args.labelLeft,
|
|
98
|
+
onChange: action('change'),
|
|
99
|
+
DefaultRadioItems,
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const Default = Template.bind({});
|
|
106
|
+
Default.args = {
|
|
107
|
+
vertical: false,
|
|
108
|
+
};
|
|
109
|
+
Default.parameters = storyParametersObject(
|
|
110
|
+
DefaultStoryParameters,
|
|
111
|
+
defaultTemplate,
|
|
112
|
+
Default.args
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const vModelTemplate = `
|
|
116
|
+
<cv-radio-group v-bind="args" @change="onChange">
|
|
117
|
+
<cv-radio-button
|
|
118
|
+
v-for="({id, label, checked, disabled}, index) in DefaultRadioItems"
|
|
119
|
+
v-model="modelValue"
|
|
120
|
+
:key="index"
|
|
121
|
+
name="group-1"
|
|
122
|
+
:label="label"
|
|
123
|
+
:value="id"
|
|
124
|
+
:disabled="disabled"
|
|
125
|
+
:hide-label="hideLabel"
|
|
126
|
+
:label-left='labelLeft'
|
|
127
|
+
/>
|
|
128
|
+
</cv-radio-group>
|
|
129
|
+
|
|
130
|
+
<div style="margin-top:1rem; background-color: #888888; padding:1rem">
|
|
131
|
+
V-model:
|
|
132
|
+
<template
|
|
133
|
+
v-for="({id, label}, index) in DefaultRadioItems"
|
|
134
|
+
:key="index"
|
|
135
|
+
>
|
|
136
|
+
<input name="modelValue" :id="id" type="radio" @change="(ev) => {modelValue = ev.target.id}" :checked="id === modelValue" />
|
|
137
|
+
<label :for="id" style="margin-right: 16px;">{{label}}:</label>
|
|
138
|
+
</template>
|
|
139
|
+
|
|
140
|
+
<div style="margin-top: 16px;">Checked: <span style="font-weight: bold;">{{modelValue}}</span></div>
|
|
141
|
+
</div>
|
|
142
|
+
`;
|
|
143
|
+
|
|
144
|
+
const VModelTemplate = args => {
|
|
145
|
+
return {
|
|
146
|
+
components: { CvRadioButton, CvRadioGroup },
|
|
147
|
+
template: vModelTemplate,
|
|
148
|
+
setup() {
|
|
149
|
+
return {
|
|
150
|
+
args,
|
|
151
|
+
hideLabel: args.hideLabel,
|
|
152
|
+
labelLeft: args.labelLeft,
|
|
153
|
+
modelValue: ref(DefaultRadioItems[0].id),
|
|
154
|
+
onChange: action('change'),
|
|
155
|
+
DefaultRadioItems,
|
|
156
|
+
};
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export const VModel = VModelTemplate.bind({});
|
|
162
|
+
VModel.parameters = storyParametersObject(
|
|
163
|
+
DefaultStoryParameters,
|
|
164
|
+
vModelTemplate,
|
|
165
|
+
VModel.args
|
|
166
|
+
);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
:class="[
|
|
4
|
+
`cv-radio-button ${carbonPrefix}--radio-button-wrapper`,
|
|
5
|
+
{ [`${carbonPrefix}--radio-button-wrapper--label-left`]: labelLeft },
|
|
6
|
+
]"
|
|
7
|
+
>
|
|
8
|
+
<input
|
|
9
|
+
v-bind="$attrs"
|
|
10
|
+
:id="cvId"
|
|
11
|
+
:checked="isChecked"
|
|
12
|
+
:class="`${carbonPrefix}--radio-button`"
|
|
13
|
+
ref="input"
|
|
14
|
+
type="radio"
|
|
15
|
+
:value="value"
|
|
16
|
+
@change="onChange"
|
|
17
|
+
/>
|
|
18
|
+
<!-- symbol causes problem in codepen? -->
|
|
19
|
+
<label :for="cvId" :class="`${carbonPrefix}--radio-button__label`">
|
|
20
|
+
<span :class="`${carbonPrefix}--radio-button__appearance`"></span>
|
|
21
|
+
<span
|
|
22
|
+
v-if="label"
|
|
23
|
+
:class="{ [`${carbonPrefix}--visually-hidden`]: hideLabel }"
|
|
24
|
+
>
|
|
25
|
+
{{ label }}
|
|
26
|
+
</span>
|
|
27
|
+
</label>
|
|
28
|
+
</div>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script setup>
|
|
32
|
+
import { carbonPrefix } from '../../global/settings';
|
|
33
|
+
import { useCvId, propsCvId, useMethods, useRadio } from '../../use';
|
|
34
|
+
|
|
35
|
+
const props = defineProps({
|
|
36
|
+
modelValue: String,
|
|
37
|
+
checked: Boolean,
|
|
38
|
+
label: String,
|
|
39
|
+
value: { type: String, required: true },
|
|
40
|
+
hideLabel: Boolean,
|
|
41
|
+
labelLeft: Boolean,
|
|
42
|
+
...propsCvId,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const cvId = useCvId(props);
|
|
46
|
+
|
|
47
|
+
const emit = defineEmits(['update:modelValue', 'change']);
|
|
48
|
+
|
|
49
|
+
useMethods({ input: ['blur', 'focus'] });
|
|
50
|
+
const { isChecked, onChange } = useRadio(props, emit);
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<script>
|
|
54
|
+
export default {
|
|
55
|
+
inheritAttrs: false,
|
|
56
|
+
};
|
|
57
|
+
</script>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="`cv-radio-group ${carbonPrefix}--form-item`">
|
|
3
|
+
<div
|
|
4
|
+
:class="[
|
|
5
|
+
`${carbonPrefix}--radio-button-group`,
|
|
6
|
+
{ [`${carbonPrefix}--radio-button-group--vertical`]: vertical },
|
|
7
|
+
]"
|
|
8
|
+
>
|
|
9
|
+
<slot></slot>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script setup>
|
|
15
|
+
import { provide } from 'vue';
|
|
16
|
+
import { carbonPrefix } from '../../global/settings';
|
|
17
|
+
|
|
18
|
+
defineProps({
|
|
19
|
+
vertical: Boolean,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const emit = defineEmits(['change']);
|
|
23
|
+
|
|
24
|
+
provide('onRadioItemChange', clickedItemCvId => {
|
|
25
|
+
emit('change', clickedItemCvId);
|
|
26
|
+
});
|
|
27
|
+
</script>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`CvRadioButton matches render when checked 1`] = `<div class="cv-radio-button bx--radio-button-wrapper"><input id="1" class="bx--radio-button" type="radio" value=""><!-- symbol causes problem in codepen? --><label for="1" class="bx--radio-button__label"><span class="bx--radio-button__appearance"></span><span class="">test label</span></label></div>`;
|
|
4
|
+
|
|
5
|
+
exports[`CvRadioButton matches render when modelValue is equal to value 1`] = `<div class="cv-radio-button bx--radio-button-wrapper"><input id="1" class="bx--radio-button" type="radio" value="test value"><!-- symbol causes problem in codepen? --><label for="1" class="bx--radio-button__label"><span class="bx--radio-button__appearance"></span><span class="">test label</span></label></div>`;
|
|
6
|
+
|
|
7
|
+
exports[`CvRadioButton matches render when modelValue is not equal to value 1`] = `<div class="cv-radio-button bx--radio-button-wrapper"><input id="1" class="bx--radio-button" type="radio" value="test value"><!-- symbol causes problem in codepen? --><label for="1" class="bx--radio-button__label"><span class="bx--radio-button__appearance"></span><span class="">test label</span></label></div>`;
|
|
8
|
+
|
|
9
|
+
exports[`CvRadioButton matches render with left label 1`] = `<div class="cv-radio-button bx--radio-button-wrapper bx--radio-button-wrapper--label-left"><input id="1" class="bx--radio-button" type="radio" value=""><!-- symbol causes problem in codepen? --><label for="1" class="bx--radio-button__label"><span class="bx--radio-button__appearance"></span><span class="">left label</span></label></div>`;
|
|
10
|
+
|
|
11
|
+
exports[`CvRadioButton matches render with right label 1`] = `<div class="cv-radio-button bx--radio-button-wrapper"><input id="1" class="bx--radio-button" type="radio" value=""><!-- symbol causes problem in codepen? --><label for="1" class="bx--radio-button__label"><span class="bx--radio-button__appearance"></span><span class="">right label</span></label></div>`;
|
|
12
|
+
|
|
13
|
+
exports[`CvRadioGroup matches render when horizontal 1`] = `
|
|
14
|
+
<div class="cv-radio-group bx--form-item">
|
|
15
|
+
<div class="bx--radio-button-group">
|
|
16
|
+
<div class="cv-radio-button-stub">RadioButtonStub</div>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
exports[`CvRadioGroup matches render when vertical 1`] = `
|
|
22
|
+
<div class="cv-radio-group bx--form-item">
|
|
23
|
+
<div class="bx--radio-button-group bx--radio-button-group--vertical">
|
|
24
|
+
<div class="cv-radio-button-stub">RadioButtonStub</div>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
`;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { Canvas, Meta, Story } from '@storybook/addon-docs';
|
|
2
|
+
import CvSearch from './CvSearch.vue';
|
|
3
|
+
import { sbCompPrefix } from '../../global/storybook-utils';
|
|
4
|
+
import { SEARCH_SIZES } from './index';
|
|
5
|
+
import { action } from '@storybook/addon-actions';
|
|
6
|
+
import { ref } from 'vue';
|
|
7
|
+
const myValue = ref('searching galaxies');
|
|
8
|
+
|
|
9
|
+
<Meta title={`${sbCompPrefix}/CvSearch`} component={CvSearch} />
|
|
10
|
+
|
|
11
|
+
export const Template = args => ({
|
|
12
|
+
// Components used in your story `template` are defined in the `components` object
|
|
13
|
+
components: {
|
|
14
|
+
CvSearch,
|
|
15
|
+
},
|
|
16
|
+
// The story's `args` need to be mapped into the template through the `setup()` method
|
|
17
|
+
setup() {
|
|
18
|
+
return {
|
|
19
|
+
light: args.light,
|
|
20
|
+
label: args.label,
|
|
21
|
+
placeholder: args.placeholder,
|
|
22
|
+
disabled: args.disabled,
|
|
23
|
+
size: args.size,
|
|
24
|
+
ariaLabel: args.ariaLabel,
|
|
25
|
+
clearAriaLabel: args.clearAriaLabel,
|
|
26
|
+
formItem: args.formItem,
|
|
27
|
+
expandable: args.expandable,
|
|
28
|
+
value: args.value,
|
|
29
|
+
myValue: myValue,
|
|
30
|
+
onInput: action('input'),
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
template: args.template,
|
|
34
|
+
});
|
|
35
|
+
const defaultTemplate = `
|
|
36
|
+
<cv-search
|
|
37
|
+
:light="light"
|
|
38
|
+
:label="label"
|
|
39
|
+
:placeholder="placeholder"
|
|
40
|
+
:disabled="disabled"
|
|
41
|
+
:size="size"
|
|
42
|
+
:expandable="expandable"
|
|
43
|
+
:value="value"
|
|
44
|
+
:form-item="formItem"
|
|
45
|
+
:aria-label="ariaLabel"
|
|
46
|
+
:clear-aria-label="clearAriaLabel"
|
|
47
|
+
@input="onInput"
|
|
48
|
+
>
|
|
49
|
+
</cv-search>
|
|
50
|
+
`;
|
|
51
|
+
const vModelTemplate = `
|
|
52
|
+
<div>
|
|
53
|
+
<cv-search
|
|
54
|
+
v-model:value="myValue"
|
|
55
|
+
:light="light"
|
|
56
|
+
:label="label"
|
|
57
|
+
:placeholder="placeholder"
|
|
58
|
+
:disabled="disabled"
|
|
59
|
+
:size="size"
|
|
60
|
+
:expandable="expandable"
|
|
61
|
+
:form-item="formItem"
|
|
62
|
+
:aria-label="ariaLabel"
|
|
63
|
+
:clear-aria-label="clearAriaLabel"
|
|
64
|
+
>
|
|
65
|
+
</cv-search>
|
|
66
|
+
<div style="margin-top:1rem; background-color: #888888; padding:1rem">
|
|
67
|
+
<div style="padding:1rem">v-model value: {{ myValue }}</div>
|
|
68
|
+
<input type="search" v-model="myValue"/>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
`;
|
|
72
|
+
|
|
73
|
+
# CvSearch
|
|
74
|
+
|
|
75
|
+
Migration notes:
|
|
76
|
+
|
|
77
|
+
- The `v-model` is different in Vue 3 than Vue 2. If you specify it you will see an error deprecation message in the log.
|
|
78
|
+
Please use `v-model:value=something` instead.
|
|
79
|
+
- The "toolbar" mode is removed in this version. Please use the `expandable` mode instead.
|
|
80
|
+
|
|
81
|
+
<Canvas>
|
|
82
|
+
<Story
|
|
83
|
+
name="Default"
|
|
84
|
+
parameters={{
|
|
85
|
+
controls: {
|
|
86
|
+
exclude: [
|
|
87
|
+
'input',
|
|
88
|
+
'modelValue',
|
|
89
|
+
'template',
|
|
90
|
+
'update:modelValue',
|
|
91
|
+
'update:value',
|
|
92
|
+
'large',
|
|
93
|
+
'small',
|
|
94
|
+
'toolbarAriaLabel',
|
|
95
|
+
'kind',
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
docs: { source: { code: defaultTemplate } },
|
|
99
|
+
}}
|
|
100
|
+
args={{
|
|
101
|
+
template: defaultTemplate,
|
|
102
|
+
light: false,
|
|
103
|
+
disabled: false,
|
|
104
|
+
placeholder: 'JWST: search for light from the first stars',
|
|
105
|
+
}}
|
|
106
|
+
argTypes={{
|
|
107
|
+
light: {
|
|
108
|
+
description: '`true` to use the light version.',
|
|
109
|
+
table: { category: 'props', defaultValue: { summary: false } },
|
|
110
|
+
},
|
|
111
|
+
size: {
|
|
112
|
+
control: 'select',
|
|
113
|
+
default: 'xl',
|
|
114
|
+
options: SEARCH_SIZES,
|
|
115
|
+
table: { defaultValue: { summary: 'xl' } },
|
|
116
|
+
},
|
|
117
|
+
}}
|
|
118
|
+
>
|
|
119
|
+
{Template.bind({})}
|
|
120
|
+
</Story>
|
|
121
|
+
</Canvas>
|
|
122
|
+
|
|
123
|
+
# v-model
|
|
124
|
+
|
|
125
|
+
- The `v-model` is different in Vue 3 than Vue 2.If you specify it you will see an error deprecation message in the log.
|
|
126
|
+
Please use `v-model:value=something` instead.
|
|
127
|
+
|
|
128
|
+
<Canvas>
|
|
129
|
+
<Story
|
|
130
|
+
name="v-model"
|
|
131
|
+
parameters={{
|
|
132
|
+
controls: {
|
|
133
|
+
exclude: [
|
|
134
|
+
'input',
|
|
135
|
+
'modelValue',
|
|
136
|
+
'template',
|
|
137
|
+
'update:modelValue',
|
|
138
|
+
'update:value',
|
|
139
|
+
'large',
|
|
140
|
+
'small',
|
|
141
|
+
'toolbarAriaLabel',
|
|
142
|
+
'kind',
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
docs: { source: { code: vModelTemplate } },
|
|
146
|
+
}}
|
|
147
|
+
args={{
|
|
148
|
+
template: vModelTemplate,
|
|
149
|
+
light: false,
|
|
150
|
+
disabled: false,
|
|
151
|
+
placeholder: 'JWST: search for light from the first stars',
|
|
152
|
+
}}
|
|
153
|
+
argTypes={{
|
|
154
|
+
light: {
|
|
155
|
+
description: '`true` to use the light version.',
|
|
156
|
+
table: { category: 'props', defaultValue: { summary: false } },
|
|
157
|
+
},
|
|
158
|
+
size: {
|
|
159
|
+
control: 'select',
|
|
160
|
+
default: 'xl',
|
|
161
|
+
options: SEARCH_SIZES,
|
|
162
|
+
table: { defaultValue: { summary: 'xl' } },
|
|
163
|
+
},
|
|
164
|
+
}}
|
|
165
|
+
>
|
|
166
|
+
{Template.bind({})}
|
|
167
|
+
</Story>
|
|
168
|
+
</Canvas>
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<component
|
|
3
|
+
:is="formItem ? 'div' : CvEmpty"
|
|
4
|
+
:class="`cv-search ${carbonPrefix}--form-item`"
|
|
5
|
+
>
|
|
6
|
+
<div
|
|
7
|
+
:aria-label="internalAriaLabel"
|
|
8
|
+
:aria-labelledby="internalAriaLabelBy"
|
|
9
|
+
:class="[
|
|
10
|
+
`${carbonPrefix}--search`,
|
|
11
|
+
`${carbonPrefix}--search--${size}`,
|
|
12
|
+
{
|
|
13
|
+
[`${carbonPrefix}--search--expandable`]: expandable,
|
|
14
|
+
[`${carbonPrefix}--search--expanded`]: isExpanded,
|
|
15
|
+
[`${carbonPrefix}--search--disabled`]: disabled,
|
|
16
|
+
[`${carbonPrefix}--search--light`]: isLight,
|
|
17
|
+
'cv-search': !formItem,
|
|
18
|
+
},
|
|
19
|
+
]"
|
|
20
|
+
role="search"
|
|
21
|
+
ref="elSearch"
|
|
22
|
+
>
|
|
23
|
+
<div
|
|
24
|
+
:class="`${carbonPrefix}--search-magnifier`"
|
|
25
|
+
@click="toggleActive(true)"
|
|
26
|
+
@blur="checkFocus"
|
|
27
|
+
>
|
|
28
|
+
<search-icon :class="`${carbonPrefix}--search-magnifier-icon`" />
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<label v-show="label" :for="uid" :class="`${carbonPrefix}--label`">{{
|
|
32
|
+
label
|
|
33
|
+
}}</label>
|
|
34
|
+
|
|
35
|
+
<input
|
|
36
|
+
role="searchbox"
|
|
37
|
+
:id="uid"
|
|
38
|
+
:class="`${carbonPrefix}--search-input`"
|
|
39
|
+
v-bind="$attrs"
|
|
40
|
+
v-model="internalSearchText"
|
|
41
|
+
@input="onInput"
|
|
42
|
+
type="text"
|
|
43
|
+
ref="elInput"
|
|
44
|
+
:placeholder="placeholder"
|
|
45
|
+
:aria-labelledby="uid"
|
|
46
|
+
@blur="checkFocus"
|
|
47
|
+
@click="toggleActive"
|
|
48
|
+
@keydown.esc="onClearClick"
|
|
49
|
+
:disabled="disabled ? 'true' : undefined"
|
|
50
|
+
/>
|
|
51
|
+
|
|
52
|
+
<button
|
|
53
|
+
type="button"
|
|
54
|
+
:class="[
|
|
55
|
+
`${carbonPrefix}--search-close`,
|
|
56
|
+
{ [`${carbonPrefix}--search-close--hidden`]: !clearVisible },
|
|
57
|
+
]"
|
|
58
|
+
:title="clearAriaLabel"
|
|
59
|
+
:aria-label="clearAriaLabel"
|
|
60
|
+
@click="onClearClick"
|
|
61
|
+
>
|
|
62
|
+
<close-icon />
|
|
63
|
+
</button>
|
|
64
|
+
</div>
|
|
65
|
+
</component>
|
|
66
|
+
</template>
|
|
67
|
+
|
|
68
|
+
<script setup>
|
|
69
|
+
import { carbonPrefix } from '../../global/settings';
|
|
70
|
+
import { useIsLight, props as propsTheme } from '../../use/cvTheme';
|
|
71
|
+
import { useCvId, props as propsCvId } from '../../use/cvId';
|
|
72
|
+
import SearchIcon from '@carbon/icons-vue/es/search/16';
|
|
73
|
+
import CloseIcon from '@carbon/icons-vue/es/close/16';
|
|
74
|
+
import { computed, ref, watch } from 'vue';
|
|
75
|
+
import { SEARCH_SIZES } from '.';
|
|
76
|
+
import CvEmpty from '../CvEmpty/_CvEmpty.vue';
|
|
77
|
+
|
|
78
|
+
defineOptions({
|
|
79
|
+
inheritAttrs: false,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const props = defineProps({
|
|
83
|
+
ariaLabel: { type: String, default: 'search input' },
|
|
84
|
+
/**
|
|
85
|
+
* Specify a label to be read by screen readers on the "close" button
|
|
86
|
+
*/
|
|
87
|
+
clearAriaLabel: { type: String, default: 'Clear search input' },
|
|
88
|
+
/**
|
|
89
|
+
* Specify whether the `<input>` should be disabled
|
|
90
|
+
*/
|
|
91
|
+
disabled: { type: Boolean, default: false },
|
|
92
|
+
/**
|
|
93
|
+
* Do no show full control until it has focus. Similar to deprecated toolbar search.
|
|
94
|
+
*/
|
|
95
|
+
expandable: { type: Boolean, default: false },
|
|
96
|
+
/**
|
|
97
|
+
* Wrap search in `<div>` if part of a form
|
|
98
|
+
*/
|
|
99
|
+
formItem: { type: Boolean, default: true },
|
|
100
|
+
/**
|
|
101
|
+
* @deprecated use expandable
|
|
102
|
+
*/
|
|
103
|
+
kind: {
|
|
104
|
+
type: String,
|
|
105
|
+
default: undefined,
|
|
106
|
+
validator: () => {
|
|
107
|
+
console.warn('Deprecated: probably you want expandable=true');
|
|
108
|
+
return true;
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
/**
|
|
112
|
+
* Provide the label text for the Search icon
|
|
113
|
+
*/
|
|
114
|
+
label: String,
|
|
115
|
+
/**
|
|
116
|
+
* Specify the search size ('sm', 'md', 'lg', 'xl')
|
|
117
|
+
*/
|
|
118
|
+
size: {
|
|
119
|
+
type: String,
|
|
120
|
+
default: 'xl',
|
|
121
|
+
validator: val => SEARCH_SIZES.includes(val),
|
|
122
|
+
},
|
|
123
|
+
/**
|
|
124
|
+
* @deprecated use size
|
|
125
|
+
*/
|
|
126
|
+
small: {
|
|
127
|
+
type: Boolean,
|
|
128
|
+
default: undefined,
|
|
129
|
+
validator: val => {
|
|
130
|
+
if (val !== undefined) {
|
|
131
|
+
console.error('Error: Use size property: sm, lg or xl (default)');
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
return true;
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
/**
|
|
138
|
+
* @deprecated use size
|
|
139
|
+
*/
|
|
140
|
+
large: {
|
|
141
|
+
type: Boolean,
|
|
142
|
+
default: undefined,
|
|
143
|
+
validator: val => {
|
|
144
|
+
if (val !== undefined) {
|
|
145
|
+
console.error('Error: Use size property: sm, lg or xl (default)');
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
return true;
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
/**
|
|
152
|
+
* Optionally provide the default value of the `<input>`
|
|
153
|
+
*/
|
|
154
|
+
value: String,
|
|
155
|
+
/**
|
|
156
|
+
* @deprecated use value
|
|
157
|
+
*/
|
|
158
|
+
modelValue: {
|
|
159
|
+
type: String,
|
|
160
|
+
default: undefined,
|
|
161
|
+
validator: () => {
|
|
162
|
+
console.warn('Deprecated: use v-model:value instead');
|
|
163
|
+
return true;
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
/**
|
|
167
|
+
* Provide an optional placeholder text for the Search. Note: if the label and placeholder differ, VoiceOver on Mac will read both
|
|
168
|
+
*/
|
|
169
|
+
placeholder: { type: String, default: 'Search' },
|
|
170
|
+
/**
|
|
171
|
+
* @deprecated
|
|
172
|
+
*/
|
|
173
|
+
toolbarAriaLabel: {
|
|
174
|
+
type: String,
|
|
175
|
+
validator: () => {
|
|
176
|
+
console.warn('Deprecated: probably you want expandable=true');
|
|
177
|
+
return true;
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
...propsTheme,
|
|
181
|
+
...propsCvId,
|
|
182
|
+
});
|
|
183
|
+
const uid = useCvId(props, true);
|
|
184
|
+
const isLight = useIsLight(props);
|
|
185
|
+
|
|
186
|
+
const clearVisible = ref(props.value ? props.value.length : false);
|
|
187
|
+
const internalSearchText = ref(props.value || props.modelValue);
|
|
188
|
+
const searchActive = ref(false);
|
|
189
|
+
watch(
|
|
190
|
+
() => props.value,
|
|
191
|
+
() => {
|
|
192
|
+
clearVisible.value = props.value ? props.value.length : false;
|
|
193
|
+
internalSearchText.value = props.value;
|
|
194
|
+
}
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
const internalAriaLabel = computed(() => {
|
|
198
|
+
if (!props.label) return props.ariaLabel;
|
|
199
|
+
else return undefined;
|
|
200
|
+
});
|
|
201
|
+
const internalAriaLabelBy = computed(() => {
|
|
202
|
+
if (props.label) return uid.value;
|
|
203
|
+
else return undefined;
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const emit = defineEmits(['input', 'update:value', 'update:modelValue']);
|
|
207
|
+
watch(internalSearchText, () => {
|
|
208
|
+
emit('input', internalSearchText.value);
|
|
209
|
+
emit('update:value', internalSearchText.value);
|
|
210
|
+
if (props.modelValue) {
|
|
211
|
+
console.warn('Deprecated: use v-model:value instead');
|
|
212
|
+
emit('update:modelValue', internalSearchText.value);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
function onClearClick() {
|
|
216
|
+
internalSearchText.value = '';
|
|
217
|
+
clearVisible.value = false;
|
|
218
|
+
if (props.expandable) {
|
|
219
|
+
elInput.value?.focus();
|
|
220
|
+
}
|
|
221
|
+
checkFocus();
|
|
222
|
+
}
|
|
223
|
+
function onInput() {
|
|
224
|
+
clearVisible.value = internalSearchText.value.length > 0;
|
|
225
|
+
}
|
|
226
|
+
const elInput = ref(null);
|
|
227
|
+
const isExpanded = computed(() => {
|
|
228
|
+
return props.expandable && searchActive.value;
|
|
229
|
+
});
|
|
230
|
+
function toggleActive(force) {
|
|
231
|
+
if (props.disabled) return;
|
|
232
|
+
|
|
233
|
+
if (typeof force === 'boolean') searchActive.value = force;
|
|
234
|
+
else if (internalSearchText.value?.length > 0) searchActive.value = true;
|
|
235
|
+
else searchActive.value = !searchActive.value;
|
|
236
|
+
|
|
237
|
+
if (searchActive.value) {
|
|
238
|
+
elInput.value?.focus();
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const elSearch = ref(null);
|
|
242
|
+
function checkFocus() {
|
|
243
|
+
if (props.disabled) return;
|
|
244
|
+
|
|
245
|
+
if (props.expandable) {
|
|
246
|
+
if (elInput.value !== document.activeElement)
|
|
247
|
+
searchActive.value = internalSearchText.value?.length > 0;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
</script>
|