@carbon/vue 3.0.4-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.
Files changed (41) hide show
  1. package/README.md +16 -0
  2. package/dist/carbon-vue-3.common.js +11782 -2709
  3. package/dist/carbon-vue-3.common.js.map +1 -1
  4. package/dist/carbon-vue-3.css +1 -0
  5. package/dist/carbon-vue-3.umd.js +11782 -2709
  6. package/dist/carbon-vue-3.umd.js.map +1 -1
  7. package/dist/carbon-vue-3.umd.min.js +30 -2
  8. package/dist/carbon-vue-3.umd.min.js.map +1 -1
  9. package/package.json +3 -2
  10. package/src/components/CvCheckbox/CvCheckbox.vue +0 -2
  11. package/src/components/CvDatePicker/CvDatePicker-notes.md +86 -0
  12. package/src/components/CvDatePicker/CvDatePicker.stories.js +241 -0
  13. package/src/components/CvDatePicker/CvDatePicker.vue +402 -0
  14. package/src/components/CvDatePicker/CvDatePickerSkeleton.vue +106 -0
  15. package/src/components/CvDatePicker/index.js +4 -0
  16. package/src/components/CvDatePicker/plugins/appendToPlugin.js +64 -0
  17. package/src/components/CvDatePicker/plugins/fixEventsPlugin.js +79 -0
  18. package/src/components/CvDatePicker/plugins/monthSelectPlugin.js +86 -0
  19. package/src/components/CvDatePicker/plugins/rangePlugin.js +47 -0
  20. package/src/components/CvForm/CvForm.stories.mdx +193 -0
  21. package/src/components/CvForm/CvForm.vue +9 -0
  22. package/src/components/CvForm/CvFormGroup.vue +27 -0
  23. package/src/components/CvForm/CvFormItem.vue +9 -0
  24. package/src/components/CvForm/index.js +5 -0
  25. package/src/components/CvModal/CvModal.stories.mdx +234 -0
  26. package/src/components/CvModal/CvModal.vue +403 -0
  27. package/src/components/CvModal/index.js +3 -0
  28. package/src/components/CvTextArea/CvTextArea.stories.js +174 -0
  29. package/src/components/CvTextArea/CvTextArea.vue +107 -0
  30. package/src/components/CvTextArea/index.js +4 -0
  31. package/src/components/CvTextInput/CvTextInput.stories.js +245 -0
  32. package/src/components/CvTextInput/CvTextInput.vue +217 -0
  33. package/src/components/CvTextInput/const.js +2 -0
  34. package/src/components/CvTextInput/index.js +4 -0
  35. package/src/components/CvTile/CvTile.stories.mdx +156 -0
  36. package/src/components/CvTile/CvTile.vue +67 -0
  37. package/src/components/CvTile/CvTileClickable.vue +23 -0
  38. package/src/components/CvTile/CvTileExpandable.vue +103 -0
  39. package/src/components/CvTile/CvTileSelectable.vue +52 -0
  40. package/src/components/CvTile/CvTileStandard.vue +7 -0
  41. package/src/components/CvTile/index.js +4 -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>
@@ -0,0 +1,3 @@
1
+ import CvModal from './CvModal.vue';
2
+ export { CvModal };
3
+ export default CvModal;
@@ -0,0 +1,174 @@
1
+ import {
2
+ sbCompPrefix,
3
+ storyParametersObject,
4
+ } from '../../global/storybook-utils';
5
+
6
+ import CvTextArea from '.';
7
+ import { ref } from 'vue';
8
+
9
+ export default {
10
+ title: `${sbCompPrefix}/CvTextArea`,
11
+ component: CvTextArea,
12
+ argTypes: {
13
+ // attrs
14
+ disabled: {
15
+ type: 'boolean',
16
+ table: {
17
+ type: { summary: 'boolean' },
18
+ category: 'attributes',
19
+ },
20
+ description: 'Specify whether the textarea should be disabled',
21
+ },
22
+ placeholder: {
23
+ type: 'string',
24
+ table: {
25
+ type: { summary: 'string' },
26
+ category: 'attributes',
27
+ },
28
+ description: 'Specify the placeholder attribute for the textarea',
29
+ },
30
+ // props
31
+ helperText: {
32
+ type: 'string',
33
+ table: {
34
+ type: { summary: 'string' },
35
+ category: 'props',
36
+ },
37
+ description:
38
+ 'Provide text that is used alongside the control label for additional help',
39
+ },
40
+ hideLabel: {
41
+ type: 'boolean',
42
+ table: {
43
+ type: { summary: 'boolean' },
44
+ category: 'props',
45
+ defaultValue: false,
46
+ },
47
+ description:
48
+ 'Specify whether you want the underlying label to be visually hidden',
49
+ },
50
+ invalidMessage: {
51
+ type: 'string',
52
+ table: {
53
+ type: { summary: 'string' },
54
+ category: 'props',
55
+ },
56
+ description:
57
+ 'Provide the text that is displayed when the control is in an invalid state',
58
+ },
59
+ label: {
60
+ type: 'string',
61
+ table: {
62
+ type: { summary: 'string' },
63
+ category: 'props',
64
+ },
65
+ description: "Input's label",
66
+ },
67
+ light: {
68
+ type: 'boolean',
69
+ table: {
70
+ type: { summary: 'boolean' },
71
+ category: 'props',
72
+ defaultValue: false,
73
+ },
74
+ description:
75
+ "Toggles light version. For use on `$ui-01` backgrounds only. Don't use this to make tile background color same as container background color.",
76
+ },
77
+ modelValue: {
78
+ type: 'string',
79
+ table: {
80
+ type: { summary: 'string' },
81
+ category: 'props',
82
+ },
83
+ description:
84
+ "Input's value, modelValue is the vue3 default 'prop' for two-way data binding with v-model",
85
+ },
86
+ // slots
87
+ 'helper-text': {
88
+ type: 'string',
89
+ table: {
90
+ type: { summary: 'string | html | Component' },
91
+ category: 'slots',
92
+ },
93
+ description:
94
+ "Helper text slot. It isn't shown if invalid message, either prop or slot, is available",
95
+ },
96
+ 'invalid-message': {
97
+ type: 'string',
98
+ table: {
99
+ type: { summary: 'string | html | Component' },
100
+ category: 'slots',
101
+ },
102
+ description: 'Invalid message slot.',
103
+ },
104
+ // events
105
+ 'update:modelValue': {
106
+ type: 'event',
107
+ table: {
108
+ category: 'events',
109
+ },
110
+ control: { type: null },
111
+ description:
112
+ "Triggered on textarea update. `update:modelValue` is the vue3 default 'event' for two-way data binding with v-model",
113
+ },
114
+ },
115
+ };
116
+
117
+ const template = `
118
+ <cv-text-area v-bind="args">
119
+ <template v-if="args['helper-text']" v-slot:helper-text />
120
+ <template v-if="args['invalid-message']" v-slot:invalid-message />
121
+ </cv-text-area>
122
+ `;
123
+ const Template = args => {
124
+ return {
125
+ components: { CvTextArea },
126
+ setup: () => ({ args }),
127
+ template,
128
+ };
129
+ };
130
+
131
+ export const Default = Template.bind({});
132
+ Default.args = {
133
+ label: 'Text area label',
134
+ placeholder: 'Sample placeholder',
135
+ };
136
+ Default.parameters = storyParametersObject(
137
+ Default.parameters,
138
+ template,
139
+ Default.args
140
+ );
141
+
142
+ export const Slots = Template.bind({});
143
+ Slots.args = {
144
+ ['invalid-message']: 'Invalid text area message',
145
+ label: 'Text area label',
146
+ placeholder: 'Sample placeholder',
147
+ };
148
+ Slots.parameters = storyParametersObject(
149
+ Slots.parameters,
150
+ template,
151
+ Slots.args
152
+ );
153
+
154
+ const vModelTemplate = `
155
+ <cv-text-area v-bind="args" v-model="value" />
156
+ <p style="margin-top: 1rem;">Value passed to v-model: {{ value }}</p>
157
+ `;
158
+ const VModelTemplate = args => {
159
+ return {
160
+ components: { CvTextArea },
161
+ setup: () => ({ args, value: ref('') }),
162
+ template: vModelTemplate,
163
+ };
164
+ };
165
+ export const vModel = VModelTemplate.bind({});
166
+ vModel.args = {
167
+ label: 'Text area label',
168
+ placeholder: 'Sample placeholder',
169
+ };
170
+ vModel.parameters = storyParametersObject(
171
+ vModel.parameters,
172
+ vModelTemplate,
173
+ vModel.args
174
+ );
@@ -0,0 +1,107 @@
1
+ <template>
2
+ <div :class="`cv-text-area ${carbonPrefix}--form-item`">
3
+ <label
4
+ :for="cvId"
5
+ :class="[
6
+ `${carbonPrefix}--label`,
7
+ {
8
+ [`${carbonPrefix}--label--disabled`]: $attrs.disabled,
9
+ [`${carbonPrefix}--visually-hidden`]: hideLabel,
10
+ },
11
+ ]"
12
+ >
13
+ {{ label }}
14
+ </label>
15
+ <div
16
+ :class="`${carbonPrefix}--text-area__wrapper`"
17
+ :data-invalid="isInvalid"
18
+ >
19
+ <WarningFilled16
20
+ v-if="isInvalid"
21
+ :class="`${carbonPrefix}--text-area__invalid-icon`"
22
+ />
23
+ <textarea
24
+ :aria-invalid="isInvalid"
25
+ :aria-describedby="isInvalid ? errorId : undefined"
26
+ :id="cvId"
27
+ :class="[
28
+ `${carbonPrefix}--text-area`,
29
+ {
30
+ [`${carbonPrefix}--text-area--invalid`]: isInvalid,
31
+ [`${carbonPrefix}--text-area--light`]: isLight,
32
+ },
33
+ ]"
34
+ v-bind="$attrs"
35
+ :value="modelValue"
36
+ @input="$event => $emit('update:modelValue', $event.target.value)"
37
+ ></textarea>
38
+ </div>
39
+ <div
40
+ v-if="isInvalid"
41
+ :id="errorId"
42
+ :class="`${carbonPrefix}--form-requirement`"
43
+ >
44
+ <slot name="invalid-message">{{ invalidMessage }}</slot>
45
+ </div>
46
+ <div
47
+ v-if="isHelper"
48
+ :class="[
49
+ `${carbonPrefix}--form__helper-text`,
50
+ { [`${carbonPrefix}--form__helper-text--disabled`]: $attrs.disabled },
51
+ ]"
52
+ >
53
+ <slot name="helper-text">{{ helperText }}</slot>
54
+ </div>
55
+ </div>
56
+ </template>
57
+
58
+ <script setup>
59
+ import { computed, ref, useSlots, onBeforeMount, onBeforeUpdate } from 'vue';
60
+ import { WarningFilled16 } from '@carbon/icons-vue';
61
+ import { carbonPrefix } from '../../global/settings';
62
+ import { useCvId, props as propsCvId } from '../../use/cvId';
63
+ import { useIsLight, props as propsTheme } from '../../use/cvTheme';
64
+
65
+ const props = defineProps({
66
+ helperText: { type: String, default: undefined },
67
+ hideLabel: { type: Boolean, default: false },
68
+ invalidMessage: { type: String, default: undefined },
69
+ label: String,
70
+ modelValue: String,
71
+ ...propsCvId,
72
+ ...propsTheme,
73
+ });
74
+
75
+ const cvId = useCvId(props);
76
+
77
+ // DOM
78
+ const slots = useSlots();
79
+
80
+ // Data
81
+ const isHelper = ref(false);
82
+ const isInvalid = ref(false);
83
+ const isLight = useIsLight(props);
84
+
85
+ // Computed
86
+ const errorId = computed(() => `error-${cvId.value}`);
87
+
88
+ // Methods
89
+ function checkSlots() {
90
+ // NOTE: this.$slots is not reactive so needs to be managed on updated
91
+ isInvalid.value = !!(
92
+ props.invalidMessage?.length || slots['invalid-message']
93
+ );
94
+ isHelper.value =
95
+ !isInvalid.value && !!(props.helperText?.length || slots['helper-text']);
96
+ }
97
+
98
+ // Life Hooks
99
+ onBeforeMount(checkSlots);
100
+ onBeforeUpdate(checkSlots);
101
+ </script>
102
+
103
+ <script>
104
+ export default {
105
+ inheritAttrs: false,
106
+ };
107
+ </script>
@@ -0,0 +1,4 @@
1
+ import CvTextArea from './CvTextArea.vue';
2
+
3
+ export { CvTextArea };
4
+ export default CvTextArea;