@carbon/vue 3.0.5-alpha.0 → 3.0.7-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 +16 -0
- package/dist/carbon-vue-3.common.js +22859 -11008
- package/dist/carbon-vue-3.common.js.map +1 -1
- package/dist/carbon-vue-3.css +1 -0
- package/dist/carbon-vue-3.umd.js +22859 -11008
- package/dist/carbon-vue-3.umd.js.map +1 -1
- package/dist/carbon-vue-3.umd.min.js +30 -2
- package/dist/carbon-vue-3.umd.min.js.map +1 -1
- package/package.json +5 -4
- 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/CvDatePicker/CvDatePicker-notes.md +86 -0
- package/src/components/CvDatePicker/CvDatePicker.stories.js +241 -0
- package/src/components/CvDatePicker/CvDatePicker.vue +402 -0
- package/src/components/CvDatePicker/CvDatePickerSkeleton.vue +106 -0
- package/src/components/CvDatePicker/index.js +4 -0
- package/src/components/CvDatePicker/plugins/appendToPlugin.js +64 -0
- package/src/components/CvDatePicker/plugins/fixEventsPlugin.js +79 -0
- package/src/components/CvDatePicker/plugins/monthSelectPlugin.js +86 -0
- package/src/components/CvDatePicker/plugins/rangePlugin.js +47 -0
- package/src/components/CvDropdown/CvDropdown.stories.mdx +407 -0
- package/src/components/CvDropdown/CvDropdown.vue +418 -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/CvForm/CvForm.stories.mdx +193 -0
- package/src/components/CvForm/CvForm.vue +9 -0
- package/src/components/CvForm/CvFormGroup.vue +27 -0
- package/src/components/CvForm/CvFormItem.vue +9 -0
- package/src/components/CvForm/index.js +5 -0
- 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 +236 -0
- package/src/components/CvModal/CvModal.vue +401 -0
- package/src/components/CvModal/index.js +3 -0
- 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/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/CvToggle/CvToggle-Skeleton.vue +29 -0
- package/src/components/CvToggle/CvToggle.stories.js +232 -0
- package/src/components/CvToggle/CvToggle.vue +70 -0
- package/src/components/CvToggle/__tests__/__snapshots__/CvToggle.spec.js.snap +7 -0
- package/src/components/CvToggle/index.js +4 -0
- package/src/global/storybook-utils/story-source-code.js +1 -0
- package/src/use/cvInput.js +34 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { Canvas, Meta, Story } from '@storybook/addon-docs';
|
|
2
|
+
import { sbCompPrefix } from '../../global/storybook-utils';
|
|
3
|
+
import CvModal from './CvModal.vue';
|
|
4
|
+
import { action } from '@storybook/addon-actions';
|
|
5
|
+
import { nextTick, ref } from 'vue';
|
|
6
|
+
|
|
7
|
+
<Meta title={`${sbCompPrefix}/CvModal`} component={CvModal} />
|
|
8
|
+
|
|
9
|
+
export const Template = args => ({
|
|
10
|
+
// Components used in your story `template` are defined in the `components` object
|
|
11
|
+
components: {
|
|
12
|
+
CvModal,
|
|
13
|
+
},
|
|
14
|
+
// The story's `args` need to be mapped into the template through the `setup()` method
|
|
15
|
+
setup() {
|
|
16
|
+
const visible = ref(false);
|
|
17
|
+
const visible3btn = ref(false);
|
|
18
|
+
return {
|
|
19
|
+
...args,
|
|
20
|
+
props: args.props,
|
|
21
|
+
onModalHidden: action('modal-hidden'),
|
|
22
|
+
onModalHideRequest: action('modal-hide-request'),
|
|
23
|
+
onModalShown: action('modal-shown'),
|
|
24
|
+
onOtherBtnClick: action('other-btn-click'),
|
|
25
|
+
onPrimaryClick: action('primary-click'),
|
|
26
|
+
onSecondaryClick: action('secondary-click'),
|
|
27
|
+
visible,
|
|
28
|
+
visible3btn,
|
|
29
|
+
kind: args.kind,
|
|
30
|
+
size: args.size,
|
|
31
|
+
autoHideOff: args.autoHideOff,
|
|
32
|
+
primaryButtonDisabled: args.primaryButtonDisabled,
|
|
33
|
+
disableTeleport: args.disableTeleport,
|
|
34
|
+
show: () => {
|
|
35
|
+
if (visible3btn.value) visible3btn.value = false;
|
|
36
|
+
if (visible.value) visible.value = false;
|
|
37
|
+
nextTick(() => {
|
|
38
|
+
visible.value = true;
|
|
39
|
+
}).catch(() => console.warn('cannot show'));
|
|
40
|
+
},
|
|
41
|
+
show3btn: () => {
|
|
42
|
+
if (visible.value) visible.value = false;
|
|
43
|
+
if (visible3btn.value) visible3btn.value = false;
|
|
44
|
+
nextTick(() => {
|
|
45
|
+
visible3btn.value = true;
|
|
46
|
+
}).catch(() => console.warn('cannot show'));
|
|
47
|
+
},
|
|
48
|
+
onAfterModalHidden() {
|
|
49
|
+
action('after-modal-hidden')();
|
|
50
|
+
visible.value = false;
|
|
51
|
+
visible3btn.value = false;
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
// And then the `args` are bound to your component with `v-bind="args"`
|
|
56
|
+
template: args.template,
|
|
57
|
+
});
|
|
58
|
+
const defaultTemplate = `
|
|
59
|
+
<div>
|
|
60
|
+
<cv-modal v-bind="props"
|
|
61
|
+
:visible="visible"
|
|
62
|
+
:kind="kind"
|
|
63
|
+
:size="size"
|
|
64
|
+
:autoHideOff="autoHideOff"
|
|
65
|
+
:primaryButtonDisabled="primaryButtonDisabled"
|
|
66
|
+
:disableTeleport="disableTeleport"
|
|
67
|
+
@after-modal-hidden="onAfterModalHidden"
|
|
68
|
+
@modal-hidden="onModalHidden"
|
|
69
|
+
@modal-hide-request="onModalHideRequest"
|
|
70
|
+
@modal-shown="onModalShown"
|
|
71
|
+
@other-btn-click="onOtherBtnClick"
|
|
72
|
+
@primary-click="onPrimaryClick"
|
|
73
|
+
@secondary-click="onSecondaryClick">
|
|
74
|
+
<template v-slot:label>Label of modal</template>
|
|
75
|
+
<template v-slot:title>Title of modal</template>
|
|
76
|
+
<template v-slot:content><p>Space: the final frontier. These are the voyages of the starship Enterprise. Its five-year mission: to explore strange new worlds; to seek out new life and new civilizations; to boldly go where no one has gone before!</p></template>
|
|
77
|
+
</cv-modal>
|
|
78
|
+
<!-- The show function sets the visible property to true -->
|
|
79
|
+
<button @click="show">show</button>
|
|
80
|
+
</div>
|
|
81
|
+
`;
|
|
82
|
+
const buttonTemplate = `
|
|
83
|
+
<div>
|
|
84
|
+
<cv-modal v-bind="props"
|
|
85
|
+
:visible="visible3btn"
|
|
86
|
+
:kind="kind"
|
|
87
|
+
:size="size"
|
|
88
|
+
:autoHideOff="autoHideOff"
|
|
89
|
+
:primaryButtonDisabled="primaryButtonDisabled"
|
|
90
|
+
:disableTeleport="disableTeleport"
|
|
91
|
+
@after-modal-hidden="onAfterModalHidden"
|
|
92
|
+
@modal-hidden="onModalHidden"
|
|
93
|
+
@modal-hide-request="onModalHideRequest"
|
|
94
|
+
@modal-shown="onModalShown"
|
|
95
|
+
@other-btn-click="onOtherBtnClick"
|
|
96
|
+
@primary-click="onPrimaryClick"
|
|
97
|
+
@secondary-click="onSecondaryClick">
|
|
98
|
+
<template v-slot:label>Label of modal</template>
|
|
99
|
+
<template v-slot:title>Title of modal</template>
|
|
100
|
+
<template v-slot:content>
|
|
101
|
+
<p>The term 'shields' first appears in the Star Trek episode "Balance of Terror", in which they were deployed, albeit to little effect, by a Federation outpost under attack from a Romulan Warbird. The first depicted use by a starship was in the Star Trek episode "Arena", in which the USS Enterprise raises its 'screens' after being attacked by an alien warship although the term 'shields' is not used in this episode. Shields are not mentioned during the earlier seasons of the Star Trek: Enterprise series, starships instead using "polarized hull plating" to make their hull more resistant to damage.</p>
|
|
102
|
+
<br />
|
|
103
|
+
<p>Like most technologies in the Star Trek fictional universe, the exact operation of shield technology is never precisely described. Characters discuss its existence and manipulation, while only superficially describing its exact physics, which result in a field being projected around a ship or other body, deflecting or dispersing projectiles and energy weapons. Shields are not shown unless struck, and are then often shown briefly for dramatic effect as a translucent bubble of energy.</p>
|
|
104
|
+
<br />
|
|
105
|
+
<p>Shields are said to be made of a screen of gravitons that can deflect beam and projectile weaponry.</p>
|
|
106
|
+
<br />
|
|
107
|
+
<p>https://en.wikipedia.org/wiki/Shields_(Star_Trek)</p>
|
|
108
|
+
</template>
|
|
109
|
+
<template v-slot:other-button>other</template>
|
|
110
|
+
<template v-slot:secondary-button>secondary</template>
|
|
111
|
+
<template v-slot:primary-button>primary</template>
|
|
112
|
+
</cv-modal>
|
|
113
|
+
<!-- The show function sets the visible property to true -->
|
|
114
|
+
<button @click="show3btn">show</button>
|
|
115
|
+
</div>
|
|
116
|
+
`;
|
|
117
|
+
|
|
118
|
+
# CvModal
|
|
119
|
+
|
|
120
|
+
Migration notes:
|
|
121
|
+
|
|
122
|
+
- The modal uses the Vue [teleport](https://vuejs.org/guide/built-ins/teleport.html#basic-usage) to move the modal to the document body. This resolves some scrolling issues seen in the Vue 2 implementation.
|
|
123
|
+
Set `disableTeleport` to true to revert to the Vue 2 behaviour.
|
|
124
|
+
- The `v-model` is different in Vue 3 than Vue 2. You can still specify the `v-model=something` to control the visibility but
|
|
125
|
+
if you specify it like this you will see a deprecation message in the log. Please use `v-model:visible=something` instead.
|
|
126
|
+
- For the `size` attribute please use of the standard sizes 'xs', 'sm', 'md', or 'lg'. The 'large' and 'small' values still
|
|
127
|
+
work but avoid using them.
|
|
128
|
+
- The `arial-label` was derived from the title slot but in this version it is an additional property with a default of 'Information modal'
|
|
129
|
+
- The `show` and `hide` methods can no longer be called directly. Use the `visible` property to control visability.
|
|
130
|
+
- The `modal-hide-request` no longer emits a `PointerEvent` instead just the reason is emitted. i.e. `{cv:reason: "external-click"}`
|
|
131
|
+
|
|
132
|
+
<Canvas>
|
|
133
|
+
<Story
|
|
134
|
+
name="Default"
|
|
135
|
+
parameters={{
|
|
136
|
+
controls: {
|
|
137
|
+
exclude: [
|
|
138
|
+
'props',
|
|
139
|
+
'default',
|
|
140
|
+
'template',
|
|
141
|
+
'content',
|
|
142
|
+
'label',
|
|
143
|
+
'other-button',
|
|
144
|
+
'primary-button',
|
|
145
|
+
'secondary-button',
|
|
146
|
+
'title',
|
|
147
|
+
'after-modal-hidden',
|
|
148
|
+
'modal-hidden',
|
|
149
|
+
'modal-hide-request',
|
|
150
|
+
'modal-shown',
|
|
151
|
+
'other-btn-click',
|
|
152
|
+
'primary-click',
|
|
153
|
+
'secondary-click',
|
|
154
|
+
'update:modelValue',
|
|
155
|
+
'update:visible',
|
|
156
|
+
'modelValue',
|
|
157
|
+
'visible',
|
|
158
|
+
'onOtherBtnClick',
|
|
159
|
+
'onPrimaryClick',
|
|
160
|
+
'onSecondaryClick',
|
|
161
|
+
],
|
|
162
|
+
},
|
|
163
|
+
docs: { source: { code: defaultTemplate } },
|
|
164
|
+
}}
|
|
165
|
+
args={{
|
|
166
|
+
template: defaultTemplate,
|
|
167
|
+
props: {
|
|
168
|
+
modelValue: undefined,
|
|
169
|
+
},
|
|
170
|
+
}}
|
|
171
|
+
argTypes={{
|
|
172
|
+
kind: { control: 'select', default: '', options: ['', 'danger'] },
|
|
173
|
+
size: {
|
|
174
|
+
control: 'select',
|
|
175
|
+
default: 'md',
|
|
176
|
+
options: ['xs', 'sm', 'md', 'lg'],
|
|
177
|
+
},
|
|
178
|
+
}}
|
|
179
|
+
>
|
|
180
|
+
{Template.bind({})}
|
|
181
|
+
</Story>
|
|
182
|
+
</Canvas>
|
|
183
|
+
|
|
184
|
+
# CvModal with three buttons
|
|
185
|
+
|
|
186
|
+
<Canvas>
|
|
187
|
+
<Story
|
|
188
|
+
name="Three buttons"
|
|
189
|
+
parameters={{
|
|
190
|
+
controls: {
|
|
191
|
+
exclude: [
|
|
192
|
+
'props',
|
|
193
|
+
'default',
|
|
194
|
+
'template',
|
|
195
|
+
'content',
|
|
196
|
+
'label',
|
|
197
|
+
'other-button',
|
|
198
|
+
'primary-button',
|
|
199
|
+
'secondary-button',
|
|
200
|
+
'title',
|
|
201
|
+
'after-modal-hidden',
|
|
202
|
+
'modal-hidden',
|
|
203
|
+
'modal-hide-request',
|
|
204
|
+
'modal-shown',
|
|
205
|
+
'other-btn-click',
|
|
206
|
+
'primary-click',
|
|
207
|
+
'secondary-click',
|
|
208
|
+
'update:modelValue',
|
|
209
|
+
'update:visible',
|
|
210
|
+
'modelValue',
|
|
211
|
+
'visible',
|
|
212
|
+
'onOtherBtnClick',
|
|
213
|
+
'onPrimaryClick',
|
|
214
|
+
'onSecondaryClick',
|
|
215
|
+
],
|
|
216
|
+
},
|
|
217
|
+
docs: { source: { code: buttonTemplate } },
|
|
218
|
+
}}
|
|
219
|
+
args={{
|
|
220
|
+
template: buttonTemplate,
|
|
221
|
+
props: {
|
|
222
|
+
modelValue: undefined,
|
|
223
|
+
},
|
|
224
|
+
}}
|
|
225
|
+
argTypes={{
|
|
226
|
+
kind: { control: 'select', default: '', options: ['', 'danger'] },
|
|
227
|
+
size: {
|
|
228
|
+
control: 'select',
|
|
229
|
+
default: 'md',
|
|
230
|
+
options: ['xs', 'sm', 'md', 'lg'],
|
|
231
|
+
},
|
|
232
|
+
}}
|
|
233
|
+
>
|
|
234
|
+
{Template.bind({})}
|
|
235
|
+
</Story>
|
|
236
|
+
</Canvas>
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Teleport to="body" :disabled="disableTeleport">
|
|
3
|
+
<div
|
|
4
|
+
ref="el"
|
|
5
|
+
data-modal
|
|
6
|
+
:id="uid"
|
|
7
|
+
:class="[
|
|
8
|
+
`cv-modal ${carbonPrefix}--modal`,
|
|
9
|
+
{
|
|
10
|
+
'is-visible': dataVisible,
|
|
11
|
+
[`${carbonPrefix}--modal--danger`]: kind === 'danger',
|
|
12
|
+
},
|
|
13
|
+
]"
|
|
14
|
+
tabindex="-1"
|
|
15
|
+
@keydown.esc.prevent="onEsc"
|
|
16
|
+
@click.self="onExternalClick"
|
|
17
|
+
v-bind="$attrs"
|
|
18
|
+
>
|
|
19
|
+
<div
|
|
20
|
+
:class="[
|
|
21
|
+
`${carbonPrefix}--modal-container`,
|
|
22
|
+
{
|
|
23
|
+
[`${carbonPrefix}--modal-container--${internalSize}`]: internalSize,
|
|
24
|
+
},
|
|
25
|
+
]"
|
|
26
|
+
v-bind="dialogAttrs"
|
|
27
|
+
:aria-label="ariaLabel"
|
|
28
|
+
ref="modalDialog"
|
|
29
|
+
>
|
|
30
|
+
<div
|
|
31
|
+
class="cv-modal__before-content"
|
|
32
|
+
ref="beforeContent"
|
|
33
|
+
tabindex="0"
|
|
34
|
+
style="position: absolute; height: 1px; width: 1px; left: -9999px"
|
|
35
|
+
role="separator"
|
|
36
|
+
aria-valuenow="0"
|
|
37
|
+
@focus="focusBeforeContent"
|
|
38
|
+
/>
|
|
39
|
+
<div :class="`${carbonPrefix}--modal-header`">
|
|
40
|
+
<p :class="`${carbonPrefix}--modal-header__label`">
|
|
41
|
+
<slot name="label"></slot>
|
|
42
|
+
</p>
|
|
43
|
+
<p :class="`${carbonPrefix}--modal-header__heading`">
|
|
44
|
+
<slot name="title">Modal Title</slot>
|
|
45
|
+
</p>
|
|
46
|
+
<button
|
|
47
|
+
:class="`${carbonPrefix}--modal-close`"
|
|
48
|
+
type="button"
|
|
49
|
+
@click="onClose"
|
|
50
|
+
ref="closeBtn"
|
|
51
|
+
:aria-label="closeAriaLabel"
|
|
52
|
+
>
|
|
53
|
+
<Close16 :class="`${carbonPrefix}--modal-close__icon`" />
|
|
54
|
+
</button>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<div
|
|
58
|
+
:class="[
|
|
59
|
+
`${carbonPrefix}--modal-content`,
|
|
60
|
+
{ [`${carbonPrefix}--modal-content--with-form`]: hasFormContent },
|
|
61
|
+
]"
|
|
62
|
+
ref="content"
|
|
63
|
+
:tabindex="data.scrollable ? 0 : undefined"
|
|
64
|
+
>
|
|
65
|
+
<slot name="content"></slot>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<cv-button-set
|
|
69
|
+
:class="[
|
|
70
|
+
`${carbonPrefix}--modal-footer`,
|
|
71
|
+
{
|
|
72
|
+
[`${carbonPrefix}--modal-footer--three-button`]:
|
|
73
|
+
data.hasPrimary && data.hasSecondary && data.hasOtherBtn,
|
|
74
|
+
},
|
|
75
|
+
]"
|
|
76
|
+
v-if="data.hasFooter"
|
|
77
|
+
>
|
|
78
|
+
<cv-button
|
|
79
|
+
type="button"
|
|
80
|
+
kind="secondary"
|
|
81
|
+
@click="onOtherBtnClick"
|
|
82
|
+
v-if="data.hasOtherBtn"
|
|
83
|
+
ref="otherBtn"
|
|
84
|
+
>
|
|
85
|
+
<slot name="other-button">Other button</slot>
|
|
86
|
+
</cv-button>
|
|
87
|
+
<cv-button
|
|
88
|
+
type="button"
|
|
89
|
+
kind="secondary"
|
|
90
|
+
@click="onSecondaryClick"
|
|
91
|
+
v-if="data.hasSecondary"
|
|
92
|
+
ref="secondaryBtn"
|
|
93
|
+
>
|
|
94
|
+
<slot name="secondary-button">Secondary button</slot>
|
|
95
|
+
</cv-button>
|
|
96
|
+
<cv-button
|
|
97
|
+
:disabled="primaryButtonDisabled"
|
|
98
|
+
type="button"
|
|
99
|
+
:kind="primaryKind"
|
|
100
|
+
@click="onPrimaryClick"
|
|
101
|
+
v-if="data.hasPrimary"
|
|
102
|
+
ref="primary"
|
|
103
|
+
>
|
|
104
|
+
<slot name="primary-button">Primary button</slot>
|
|
105
|
+
</cv-button>
|
|
106
|
+
</cv-button-set>
|
|
107
|
+
<div
|
|
108
|
+
class="cv-modal__after-content"
|
|
109
|
+
ref="afterContent"
|
|
110
|
+
tabindex="0"
|
|
111
|
+
style="position: absolute; height: 1px; width: 1px; left: -9999px"
|
|
112
|
+
role="separator"
|
|
113
|
+
aria-valuenow="1"
|
|
114
|
+
@focus="focusAfterContent"
|
|
115
|
+
/>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
</Teleport>
|
|
119
|
+
</template>
|
|
120
|
+
|
|
121
|
+
<script setup>
|
|
122
|
+
import CvButton from '../CvButton/CvButton.vue';
|
|
123
|
+
import { carbonPrefix } from '../../global/settings';
|
|
124
|
+
import { props as propsCvId, useCvId } from '../../use/cvId';
|
|
125
|
+
import Close16 from '@carbon/icons-vue/es/close/16';
|
|
126
|
+
import CvButtonSet from '../CvButton/CvButtonSet.vue';
|
|
127
|
+
import {
|
|
128
|
+
computed,
|
|
129
|
+
onBeforeMount,
|
|
130
|
+
onBeforeUnmount,
|
|
131
|
+
onBeforeUpdate,
|
|
132
|
+
onMounted,
|
|
133
|
+
reactive,
|
|
134
|
+
ref,
|
|
135
|
+
useSlots,
|
|
136
|
+
watch,
|
|
137
|
+
} from 'vue';
|
|
138
|
+
|
|
139
|
+
const props = defineProps({
|
|
140
|
+
/**
|
|
141
|
+
* When set to true, the aria role will be either "alert" or "alertdialog"
|
|
142
|
+
*/
|
|
143
|
+
alert: { type: Boolean, default: false },
|
|
144
|
+
/**
|
|
145
|
+
* optional label for close button, default 'Close modal'
|
|
146
|
+
*/
|
|
147
|
+
closeAriaLabel: { type: String, default: 'Close modal' },
|
|
148
|
+
/**
|
|
149
|
+
* aria label for the dialog, default 'Information modal'
|
|
150
|
+
*/
|
|
151
|
+
ariaLabel: { type: String, default: 'Information modal' },
|
|
152
|
+
/**
|
|
153
|
+
* 'danger' otherwise default modal.
|
|
154
|
+
*/
|
|
155
|
+
kind: {
|
|
156
|
+
type: String,
|
|
157
|
+
default: '',
|
|
158
|
+
validator: val => ['', 'danger'].includes(val),
|
|
159
|
+
},
|
|
160
|
+
/**
|
|
161
|
+
* boolean value if true the component user is expected to close the modal via visible property.
|
|
162
|
+
*/
|
|
163
|
+
autoHideOff: { type: Boolean, default: false },
|
|
164
|
+
/**
|
|
165
|
+
* visibility of modal dialog - available as v-model
|
|
166
|
+
*/
|
|
167
|
+
visible: { type: Boolean, default: false },
|
|
168
|
+
/**
|
|
169
|
+
* @deprecated - use v-model:visible
|
|
170
|
+
*/
|
|
171
|
+
modelValue: {
|
|
172
|
+
type: Boolean,
|
|
173
|
+
validator: val => {
|
|
174
|
+
console.warn(
|
|
175
|
+
`v-model for modal is deprecated. Specify "v-model:visible" instead [${val}]`
|
|
176
|
+
);
|
|
177
|
+
return true;
|
|
178
|
+
},
|
|
179
|
+
default: undefined,
|
|
180
|
+
},
|
|
181
|
+
/**
|
|
182
|
+
* boolean value used to enable/disable the primary button
|
|
183
|
+
*/
|
|
184
|
+
primaryButtonDisabled: { type: Boolean, default: false },
|
|
185
|
+
/**
|
|
186
|
+
* default size is "medium"
|
|
187
|
+
*/
|
|
188
|
+
size: {
|
|
189
|
+
type: String,
|
|
190
|
+
validator: val =>
|
|
191
|
+
['', 'xs', 'sm', 'small', 'md', 'large', 'lg'].includes(val),
|
|
192
|
+
default: '',
|
|
193
|
+
},
|
|
194
|
+
/**
|
|
195
|
+
* Adds styling specific to modals with form content
|
|
196
|
+
*/
|
|
197
|
+
hasFormContent: { type: Boolean, default: false },
|
|
198
|
+
/**
|
|
199
|
+
* Do not move the modal to the document body
|
|
200
|
+
*/
|
|
201
|
+
disableTeleport: { type: Boolean, default: false },
|
|
202
|
+
|
|
203
|
+
// Listeners
|
|
204
|
+
onPrimaryClick: { type: Function },
|
|
205
|
+
onSecondaryClick: { type: Function },
|
|
206
|
+
onOtherBtnClick: { type: Function },
|
|
207
|
+
|
|
208
|
+
...propsCvId,
|
|
209
|
+
});
|
|
210
|
+
const uid = useCvId(props, true);
|
|
211
|
+
|
|
212
|
+
const dataVisible = ref(false);
|
|
213
|
+
const data = reactive({
|
|
214
|
+
scrollable: false,
|
|
215
|
+
hasFooter: false,
|
|
216
|
+
hasHeaderLabel: false,
|
|
217
|
+
hasPrimary: false,
|
|
218
|
+
hasSecondary: false,
|
|
219
|
+
hasOtherBtn: false,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
const slots = useSlots();
|
|
223
|
+
function checkSlots() {
|
|
224
|
+
// NOTE: $slots is not reactive so needs to be managed on updated
|
|
225
|
+
data.hasFooter = !!(
|
|
226
|
+
slots['primary-button'] ||
|
|
227
|
+
slots['secondary-button'] ||
|
|
228
|
+
slots['other-button']
|
|
229
|
+
);
|
|
230
|
+
data.hasHeaderLabel = !!slots['label'];
|
|
231
|
+
data.hasPrimary = !!slots['primary-button'];
|
|
232
|
+
data.hasSecondary = !!slots['secondary-button'];
|
|
233
|
+
data.hasOtherBtn = !!slots['other-button'];
|
|
234
|
+
}
|
|
235
|
+
const emit = defineEmits([
|
|
236
|
+
'modal-shown',
|
|
237
|
+
'modal-hidden',
|
|
238
|
+
'after-modal-hidden',
|
|
239
|
+
'modal-hide-request',
|
|
240
|
+
'primary-click',
|
|
241
|
+
'secondary-click',
|
|
242
|
+
'other-btn-click',
|
|
243
|
+
'update:modelValue',
|
|
244
|
+
'update:visible',
|
|
245
|
+
]);
|
|
246
|
+
onBeforeMount(checkSlots);
|
|
247
|
+
onBeforeUpdate(checkSlots);
|
|
248
|
+
|
|
249
|
+
const el = ref(null);
|
|
250
|
+
function show() {
|
|
251
|
+
// prevent body scrolling
|
|
252
|
+
document.body.classList.add(`${carbonPrefix}--body--with-modal-open`);
|
|
253
|
+
|
|
254
|
+
el.value?.addEventListener('transitionend', onShown);
|
|
255
|
+
dataVisible.value = true;
|
|
256
|
+
}
|
|
257
|
+
const content = ref(null);
|
|
258
|
+
function onShown() {
|
|
259
|
+
const focusEl = content.value?.querySelector('[data-modal-primary-focus]');
|
|
260
|
+
if (focusEl) {
|
|
261
|
+
focusEl.focus();
|
|
262
|
+
} else {
|
|
263
|
+
focusBeforeContent();
|
|
264
|
+
}
|
|
265
|
+
emit('modal-shown', uid.value);
|
|
266
|
+
|
|
267
|
+
// check to see if content scrollable
|
|
268
|
+
data.scrollable = content.value?.scrollHeight > content.value?.clientHeight;
|
|
269
|
+
|
|
270
|
+
el.value?.removeEventListener('transitionend', onShown);
|
|
271
|
+
}
|
|
272
|
+
const secondaryBtn = ref(null);
|
|
273
|
+
const otherBtn = ref(null);
|
|
274
|
+
const closeBtn = ref(null);
|
|
275
|
+
function focusBeforeContent() {
|
|
276
|
+
if (slots['primary-button']) {
|
|
277
|
+
el.value?.focus();
|
|
278
|
+
} else if (slots['secondary-button']) {
|
|
279
|
+
secondaryBtn.value?.$el.focus();
|
|
280
|
+
} else if (slots['other-button']) {
|
|
281
|
+
otherBtn.value?.$el.focus();
|
|
282
|
+
} else {
|
|
283
|
+
closeBtn.value?.focus();
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
onMounted(() => {
|
|
288
|
+
if (props.visible) show();
|
|
289
|
+
else if (props.modelValue) show();
|
|
290
|
+
});
|
|
291
|
+
watch(
|
|
292
|
+
() => props.visible,
|
|
293
|
+
val => {
|
|
294
|
+
if (val) show();
|
|
295
|
+
else hide();
|
|
296
|
+
}
|
|
297
|
+
);
|
|
298
|
+
watch(dataVisible, val => {
|
|
299
|
+
emit('update:modelValue', val);
|
|
300
|
+
emit('update:visible', val);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
function hide() {
|
|
304
|
+
//restore any previous scroll
|
|
305
|
+
document.body.classList.remove(`${carbonPrefix}--body--with-modal-open`);
|
|
306
|
+
|
|
307
|
+
if (dataVisible.value) {
|
|
308
|
+
el.value?.addEventListener('transitionend', afterHide);
|
|
309
|
+
emit('modal-hidden', uid.value);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
dataVisible.value = false;
|
|
313
|
+
}
|
|
314
|
+
function afterHide(event) {
|
|
315
|
+
if (event.propertyName === 'opacity') {
|
|
316
|
+
emit('after-modal-hidden');
|
|
317
|
+
el.value?.removeEventListener('transitionend', afterHide);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const dialogAttrs = computed(() => {
|
|
322
|
+
const passive = !data.hasFooter;
|
|
323
|
+
const attrs = {};
|
|
324
|
+
if (props.alert) {
|
|
325
|
+
if (passive) {
|
|
326
|
+
attrs.role = 'alert';
|
|
327
|
+
} else {
|
|
328
|
+
attrs.role = 'alertdialog';
|
|
329
|
+
}
|
|
330
|
+
} else {
|
|
331
|
+
attrs.role = 'dialog';
|
|
332
|
+
}
|
|
333
|
+
return attrs;
|
|
334
|
+
});
|
|
335
|
+
const primaryKind = computed(() => {
|
|
336
|
+
if (props.kind === 'danger') {
|
|
337
|
+
return 'danger';
|
|
338
|
+
} else {
|
|
339
|
+
return 'primary';
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
const internalSize = computed(() => {
|
|
343
|
+
switch (props.size) {
|
|
344
|
+
case 'xs':
|
|
345
|
+
return 'xs';
|
|
346
|
+
case 'sm':
|
|
347
|
+
case 'small':
|
|
348
|
+
return 'sm';
|
|
349
|
+
case 'lg':
|
|
350
|
+
case 'large':
|
|
351
|
+
return 'lg';
|
|
352
|
+
default:
|
|
353
|
+
return '';
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
function focusAfterContent() {
|
|
358
|
+
closeBtn.value?.focus();
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function onExternalClick(ev) {
|
|
362
|
+
if (ev.target === el.value) {
|
|
363
|
+
_maybeHide(ev, 'external-click');
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
function onEsc(ev) {
|
|
367
|
+
_maybeHide(ev, 'escape-press');
|
|
368
|
+
}
|
|
369
|
+
function onClose(ev) {
|
|
370
|
+
_maybeHide(ev, 'close-click');
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function _maybeHide(event, reason) {
|
|
374
|
+
if (!props.autoHideOff) {
|
|
375
|
+
hide();
|
|
376
|
+
} else {
|
|
377
|
+
emit('modal-hide-request', { 'cv:reason': reason });
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
function onFooterButtonClick(buttonId, ev) {
|
|
381
|
+
emit(buttonId);
|
|
382
|
+
if (buttonId === 'primary-click' && !props.onPrimaryClick)
|
|
383
|
+
_maybeHide(ev, buttonId);
|
|
384
|
+
if (buttonId === 'secondary-click' && !props.onSecondaryClick)
|
|
385
|
+
_maybeHide(ev, buttonId);
|
|
386
|
+
if (buttonId === 'other-btn-click' && !props.onOtherBtnClick)
|
|
387
|
+
_maybeHide(ev, buttonId);
|
|
388
|
+
}
|
|
389
|
+
function onPrimaryClick(ev) {
|
|
390
|
+
onFooterButtonClick('primary-click', ev);
|
|
391
|
+
}
|
|
392
|
+
function onSecondaryClick(ev) {
|
|
393
|
+
onFooterButtonClick('secondary-click', ev);
|
|
394
|
+
}
|
|
395
|
+
function onOtherBtnClick(ev) {
|
|
396
|
+
onFooterButtonClick('other-btn-click', ev);
|
|
397
|
+
}
|
|
398
|
+
onBeforeUnmount(() => {
|
|
399
|
+
if (dataVisible.value) hide();
|
|
400
|
+
});
|
|
401
|
+
</script>
|