@carbon/vue 3.0.5-alpha.0 → 3.0.6-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 +8670 -564
- 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 +8670 -564
- 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 +3 -2
- 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/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/CvModal/CvModal.stories.mdx +234 -0
- package/src/components/CvModal/CvModal.vue +403 -0
- package/src/components/CvModal/index.js +3 -0
|
@@ -0,0 +1,403 @@
|
|
|
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');
|
|
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
|
+
}
|
|
310
|
+
|
|
311
|
+
dataVisible.value = false;
|
|
312
|
+
emit('modal-hidden');
|
|
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) {
|
|
400
|
+
emit('after-modal-hidden');
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
</script>
|