@propelinc/citrus-ui 1.0.4 → 1.3.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 (202) hide show
  1. package/README.md +51 -14
  2. package/dist/citrus-ui.cdn.css +1 -0
  3. package/dist/citrus-ui.css +1 -0
  4. package/dist/colors/colors.d.ts +31 -0
  5. package/dist/colors/theme.d.ts +3 -0
  6. package/dist/colors/util-classes.d.ts +11 -0
  7. package/dist/components/CAccordion.vue.d.ts +34 -0
  8. package/dist/components/CAccordionItem.vue.d.ts +39 -0
  9. package/dist/components/CAppBar.vue.d.ts +59 -0
  10. package/dist/components/CBadge.vue.d.ts +35 -0
  11. package/dist/components/CBottomSheet.vue.d.ts +90 -0
  12. package/dist/components/CButton/CButton.vue.d.ts +97 -0
  13. package/dist/components/CButton/types.d.ts +5 -0
  14. package/dist/components/CButtonStack.vue.d.ts +27 -0
  15. package/dist/components/CCard.vue.d.ts +53 -0
  16. package/dist/components/CCardFooter.vue.d.ts +20 -0
  17. package/dist/components/CCardHeader.vue.d.ts +22 -0
  18. package/dist/components/CCardSection.vue.d.ts +26 -0
  19. package/dist/components/CCheckbox.vue.d.ts +62 -0
  20. package/dist/components/CCol.vue.d.ts +30 -0
  21. package/dist/components/CDivider.vue.d.ts +9 -0
  22. package/dist/components/CDobField.vue.d.ts +60 -0
  23. package/dist/components/CDobSelect.vue.d.ts +50 -0
  24. package/dist/components/CEmailField.vue.d.ts +48 -0
  25. package/dist/components/CExpandTransition.vue.d.ts +29 -0
  26. package/dist/components/CFadeTransition.vue.d.ts +20 -0
  27. package/dist/components/CFileInput.vue.d.ts +50 -0
  28. package/dist/components/CFixedPageFooter.vue.d.ts +153 -0
  29. package/dist/components/CForm.vue.d.ts +44 -0
  30. package/dist/components/CFormFieldCounter.vue.d.ts +15 -0
  31. package/dist/components/CIconButton.vue.d.ts +97 -0
  32. package/dist/components/CLabel.vue.d.ts +36 -0
  33. package/dist/components/CListItem.vue.d.ts +56 -0
  34. package/dist/components/CListItemContent.vue.d.ts +27 -0
  35. package/dist/components/CListItemIcon.vue.d.ts +28 -0
  36. package/dist/components/CLoader.vue.d.ts +23 -0
  37. package/dist/components/CLogo.vue.d.ts +9 -0
  38. package/dist/components/CMaskedTextField.vue.d.ts +511 -0
  39. package/dist/components/CMenu.vue.d.ts +17 -0
  40. package/dist/components/CMenuItem.vue.d.ts +37 -0
  41. package/dist/components/CMenuLabel.vue.d.ts +20 -0
  42. package/dist/components/CModal.vue.d.ts +59 -0
  43. package/dist/components/CModalLoading.vue.d.ts +36 -0
  44. package/dist/components/CNotification.vue.d.ts +64 -0
  45. package/dist/components/CPhoneField.vue.d.ts +792 -0
  46. package/dist/components/CPill.vue.d.ts +41 -0
  47. package/dist/components/CPillGroup.vue.d.ts +39 -0
  48. package/dist/components/CPopup.vue.d.ts +37 -0
  49. package/dist/components/CProgressLinear.vue.d.ts +21 -0
  50. package/dist/components/CProgressRing.vue.d.ts +48 -0
  51. package/dist/components/CRadio.vue.d.ts +40 -0
  52. package/dist/components/CRadioGroup.vue.d.ts +54 -0
  53. package/dist/components/CRebrand.vue.d.ts +28 -0
  54. package/dist/components/CRow.vue.d.ts +41 -0
  55. package/dist/components/CSafeArea.vue.d.ts +18 -0
  56. package/dist/components/CSectionHeader.vue.d.ts +29 -0
  57. package/dist/components/CSelect.vue.d.ts +96 -0
  58. package/dist/components/CSkeleton.vue.d.ts +3 -0
  59. package/dist/components/CSkeletonLoaderCard.vue.d.ts +9 -0
  60. package/dist/components/CSkeletonLoaderCircle.vue.d.ts +3 -0
  61. package/dist/components/CSkeletonLoaderText.vue.d.ts +16 -0
  62. package/dist/components/CSlideFadeTransition.vue.d.ts +36 -0
  63. package/dist/components/CSplitInput.vue.d.ts +56 -0
  64. package/dist/components/CSquaredIcon.vue.d.ts +33 -0
  65. package/dist/components/CSsnField.vue.d.ts +798 -0
  66. package/dist/components/CStatusDot.vue.d.ts +10 -0
  67. package/dist/components/CSwitch.vue.d.ts +39 -0
  68. package/dist/components/CSwitchListItem.vue.d.ts +48 -0
  69. package/dist/components/CTextArea.vue.d.ts +96 -0
  70. package/dist/components/CTextField.vue.d.ts +129 -0
  71. package/dist/components/CTextLink.vue.d.ts +36 -0
  72. package/dist/components/CThirdPartyLogo.vue.d.ts +22 -0
  73. package/dist/components/CTimeago.vue.d.ts +12 -0
  74. package/dist/components/CToast.vue.d.ts +69 -0
  75. package/dist/components/CToastsList.vue.d.ts +3 -0
  76. package/dist/components/CValidationMessage.vue.d.ts +37 -0
  77. package/dist/components/CZipcodeField.vue.d.ts +796 -0
  78. package/dist/components/index.d.ts +66 -0
  79. package/dist/components/internal/CCloseButton.vue.d.ts +14 -0
  80. package/dist/composables/accessibility.d.ts +1 -0
  81. package/dist/composables/animation.d.ts +12 -0
  82. package/dist/composables/binding.d.ts +19 -0
  83. package/dist/composables/colors.d.ts +13 -0
  84. package/dist/composables/elements.d.ts +3 -0
  85. package/dist/composables/fields.d.ts +10 -0
  86. package/dist/composables/gestures.d.ts +53 -0
  87. package/dist/composables/i18n.d.ts +3 -0
  88. package/dist/composables/id.d.ts +11 -0
  89. package/dist/composables/input-mask.d.ts +18 -0
  90. package/dist/composables/router.d.ts +30 -0
  91. package/dist/composables/slots.d.ts +2 -0
  92. package/dist/composables/toast.d.ts +21 -0
  93. package/dist/composables/validations.d.ts +77 -0
  94. package/dist/icons.cdn.mjs +3 -0
  95. package/dist/icons.cdn.mjs.map +1 -0
  96. package/dist/icons.d.ts +1 -0
  97. package/dist/icons.mjs +6 -0
  98. package/dist/icons.mjs.map +1 -0
  99. package/dist/index.cdn.mjs +9328 -12875
  100. package/dist/index.cdn.mjs.map +1 -1
  101. package/dist/index.cdn2.mjs +55255 -0
  102. package/dist/index.cdn2.mjs.map +1 -0
  103. package/dist/index.d.ts +8 -0
  104. package/dist/index.mjs +3946 -0
  105. package/dist/index.mjs.map +1 -0
  106. package/dist/plugin.d.ts +3 -0
  107. package/dist/services/animation.d.ts +17 -0
  108. package/dist/services/directives/index.d.ts +2 -0
  109. package/dist/services/directives/scroll-into-view.d.ts +7 -0
  110. package/dist/services/directives/tap-animation.d.ts +6 -0
  111. package/dist/services/id.d.ts +22 -0
  112. package/dist/services/injections/accordions.d.ts +3 -0
  113. package/dist/services/injections/animations.d.ts +2 -0
  114. package/dist/services/injections/buttons.d.ts +4 -0
  115. package/dist/services/injections/forms.d.ts +6 -0
  116. package/dist/services/injections/icon-buttons.d.ts +3 -0
  117. package/dist/services/injections/pills.d.ts +4 -0
  118. package/dist/services/injections/radio.d.ts +10 -0
  119. package/dist/{styles/main.css → styles.css} +40 -2
  120. package/dist/theme/icons.d.ts +36 -0
  121. package/dist/types/CForm.d.ts +12 -0
  122. package/dist/types/font-awesome.d.ts +5 -0
  123. package/dist/types.d.ts +13 -0
  124. package/package.json +11 -4
  125. package/src/colors/colors.ts +8 -3
  126. package/src/components/CAccordion.vue +31 -24
  127. package/src/components/CAccordionItem.vue +46 -45
  128. package/src/components/CAppBar.vue +108 -101
  129. package/src/components/CBadge.vue +33 -25
  130. package/src/components/CBottomSheet.vue +212 -199
  131. package/src/components/CButton/CButton.vue +135 -147
  132. package/src/components/CButtonStack.vue +21 -13
  133. package/src/components/CCard.vue +72 -69
  134. package/src/components/CCardFooter.vue +5 -5
  135. package/src/components/CCardHeader.vue +9 -7
  136. package/src/components/CCardSection.vue +15 -8
  137. package/src/components/CCheckbox.vue +68 -69
  138. package/src/components/CCol.vue +21 -22
  139. package/src/components/CDivider.vue +9 -8
  140. package/src/components/CDobField.vue +114 -105
  141. package/src/components/CDobSelect.vue +162 -164
  142. package/src/components/CEmailField.vue +39 -27
  143. package/src/components/CExpandTransition.vue +14 -17
  144. package/src/components/CFadeTransition.vue +3 -3
  145. package/src/components/CFileInput.vue +57 -50
  146. package/src/components/CFixedPageFooter.vue +23 -17
  147. package/src/components/CForm.vue +67 -60
  148. package/src/components/CFormFieldCounter.vue +25 -28
  149. package/src/components/CIconButton.vue +84 -65
  150. package/src/components/CLabel.vue +19 -13
  151. package/src/components/CListItem.vue +67 -66
  152. package/src/components/CListItemContent.vue +14 -16
  153. package/src/components/CListItemIcon.vue +18 -14
  154. package/src/components/CLoader.vue +47 -56
  155. package/src/components/CLogo.vue +13 -12
  156. package/src/components/CMaskedTextField.vue +80 -64
  157. package/src/components/CMenu.vue +14 -6
  158. package/src/components/CMenuItem.vue +28 -22
  159. package/src/components/CMenuLabel.vue +6 -5
  160. package/src/components/CModal.vue +76 -71
  161. package/src/components/CModalLoading.vue +24 -15
  162. package/src/components/CNotification.vue +77 -28
  163. package/src/components/CPhoneField.vue +34 -25
  164. package/src/components/CPill.vue +92 -88
  165. package/src/components/CPillGroup.vue +30 -21
  166. package/src/components/CPopup.vue +46 -37
  167. package/src/components/CProgressLinear.vue +17 -11
  168. package/src/components/CProgressRing.vue +33 -33
  169. package/src/components/CRadio.vue +57 -57
  170. package/src/components/CRadioGroup.vue +85 -72
  171. package/src/components/CRow.vue +22 -20
  172. package/src/components/CSectionHeader.vue +20 -12
  173. package/src/components/CSelect.vue +89 -73
  174. package/src/components/CSkeletonLoaderCard.vue +9 -15
  175. package/src/components/CSkeletonLoaderCircle.vue +1 -9
  176. package/src/components/CSkeletonLoaderText.vue +17 -18
  177. package/src/components/CSlideFadeTransition.vue +12 -34
  178. package/src/components/CSplitInput.vue +46 -45
  179. package/src/components/CSquaredIcon.vue +39 -29
  180. package/src/components/CSsnField.vue +48 -36
  181. package/src/components/CStatusDot.vue +16 -16
  182. package/src/components/CSwitch.vue +31 -22
  183. package/src/components/CSwitchListItem.vue +27 -28
  184. package/src/components/CTextArea.vue +116 -83
  185. package/src/components/CTextField.vue +194 -198
  186. package/src/components/CTextLink.vue +28 -25
  187. package/src/components/CThirdPartyLogo.vue +30 -59
  188. package/src/components/CToast.vue +135 -132
  189. package/src/components/CToastsList.vue +2 -15
  190. package/src/components/CValidationMessage.vue +31 -24
  191. package/src/components/CZipcodeField.vue +40 -27
  192. package/src/composables/elements.ts +1 -1
  193. package/src/composables/fields.ts +4 -4
  194. package/src/composables/router.ts +6 -5
  195. package/src/icons.ts +6 -0
  196. package/src/services/injections/buttons.ts +1 -1
  197. package/src/styles/_core.scss +1 -2
  198. package/src/styles/_reset.scss +1 -1
  199. package/src/styles/main.scss +2 -0
  200. package/src/types.ts +2 -0
  201. package/dist/index.cdn.css +0 -1
  202. package/dist/styles/utils.css +0 -2709
@@ -12,27 +12,27 @@
12
12
  </div>
13
13
  </template>
14
14
 
15
- <script lang="ts">
16
- import type { PropType } from 'vue';
17
- import { computed, defineComponent } from 'vue';
15
+ <script setup lang="ts">
16
+ import { computed } from 'vue';
18
17
 
19
18
  import { useA11yLabelCheck } from '@propelinc/citrus-ui/src/composables/accessibility';
20
19
 
21
- export default defineComponent({
22
- props: {
23
- count: { type: Number as PropType<number | null>, default: null },
24
- bordered: { type: Boolean, default: false },
25
- },
26
- setup(props) {
27
- useA11yLabelCheck('CStatusDot');
20
+ const props = withDefaults(
21
+ defineProps<{
22
+ count?: number | null;
23
+ bordered?: boolean;
24
+ }>(),
25
+ {
26
+ count: null,
27
+ bordered: false,
28
+ }
29
+ );
28
30
 
29
- const hasCount = computed(() => props.count !== null);
30
- const displayCount = computed(() => {
31
- return props.count && props.count > 9 ? '9+' : props.count;
32
- });
31
+ useA11yLabelCheck('CStatusDot');
33
32
 
34
- return { displayCount, hasCount };
35
- },
33
+ const hasCount = computed(() => props.count !== null);
34
+ const displayCount = computed(() => {
35
+ return props.count && props.count > 9 ? '9+' : props.count;
36
36
  });
37
37
  </script>
38
38
 
@@ -18,34 +18,43 @@
18
18
  </sl-switch>
19
19
  </template>
20
20
 
21
- <script lang="ts">
21
+ <script setup lang="ts">
22
22
  import '@shoelace-style/shoelace/dist/components/switch/switch.js';
23
- import { defineComponent, toRefs } from 'vue';
23
+ import type { VNode } from 'vue';
24
+ import { toRefs } from 'vue';
24
25
 
25
26
  import { useInternalValue } from '@propelinc/citrus-ui/src/composables/binding';
26
27
 
27
- export default defineComponent({
28
- props: {
28
+ const props = withDefaults(
29
+ defineProps<{
30
+ /** Label for screen-readers. This hides and overrides the normal label. */
31
+ ariaLabel?: string;
32
+ /** Prevents the user from interacting with the switch */
33
+ disabled?: boolean;
29
34
  /** When true, the slot label is visually rendered before the control (label left, switch right) */
30
- labelFirst: { type: Boolean, default: false },
35
+ labelFirst?: boolean;
31
36
  /** Whether the switch is in an "on" or "off" state */
32
- value: { type: Boolean, default: false },
33
- /** Prevents the user from interacting with the switch */
34
- disabled: { type: Boolean, default: false },
35
- /** Label for screen-readers. This hides and overrides the normal label. */
36
- ariaLabel: { type: String, default: '' },
37
- },
38
- emits: ['input'],
39
- setup(props, { emit }) {
40
- const { value } = toRefs(props);
41
- const internalValue = useInternalValue(value, {
42
- onChange: (newValue) => emit('input', newValue),
43
- });
44
-
45
- return {
46
- internalValue,
47
- };
48
- },
37
+ value?: boolean;
38
+ }>(),
39
+ {
40
+ ariaLabel: '',
41
+ disabled: false,
42
+ labelFirst: false,
43
+ value: false,
44
+ }
45
+ );
46
+
47
+ const emit = defineEmits<{
48
+ (e: 'input', value: boolean): void;
49
+ }>();
50
+
51
+ defineSlots<{
52
+ default?: () => VNode[];
53
+ }>();
54
+
55
+ const { value } = toRefs(props);
56
+ const internalValue = useInternalValue(value, {
57
+ onChange: (newValue) => emit('input', newValue),
49
58
  });
50
59
  </script>
51
60
 
@@ -32,50 +32,49 @@
32
32
  </CListItem>
33
33
  </template>
34
34
 
35
- <script lang="ts">
36
- import { type PropType, defineComponent } from 'vue';
35
+ <script setup lang="ts">
36
+ import type { IconDefinition } from '@fortawesome/fontawesome-svg-core';
37
37
 
38
38
  import CListItem from '@propelinc/citrus-ui/src/components/CListItem.vue';
39
39
  import CListItemContent from '@propelinc/citrus-ui/src/components/CListItemContent.vue';
40
40
  import CListItemIcon from '@propelinc/citrus-ui/src/components/CListItemIcon.vue';
41
41
  import CSwitch from '@propelinc/citrus-ui/src/components/CSwitch.vue';
42
- import type { FaPropType } from '@propelinc/citrus-ui/src/types/font-awesome';
43
42
 
44
43
  import { useSlotHasContent } from '../composables/slots';
45
44
 
46
- export default defineComponent({
47
- name: 'CSwitchListItem',
48
- components: {
49
- CSwitch,
50
- CListItem,
51
- CListItemContent,
52
- CListItemIcon,
53
- },
54
- props: {
45
+ withDefaults(
46
+ defineProps<{
55
47
  /** Alignment of the content */
56
- align: { type: String as PropType<'top' | 'center'>, default: 'center' },
48
+ align?: 'top' | 'center';
57
49
  /** Prevents the user from interacting with the switch */
58
- disabled: { type: Boolean, default: false },
50
+ disabled?: boolean;
59
51
  /** Font Awesome icon to render on the left side of the component */
60
- icon: { type: [String, Array, Object] as FaPropType, default: null },
52
+ icon?: string | string[] | IconDefinition | null;
61
53
  /** Color of the icon background */
62
- iconColor: { type: String, default: 'gray-100' },
54
+ iconColor?: string;
63
55
  /** Plain body copy. Overridden by the default slot. */
64
- label: { type: String, default: undefined },
56
+ label?: string;
65
57
  /** Emphasized text above body text. Overridden by the default slot. */
66
- title: { type: String, default: undefined },
58
+ title?: string;
67
59
  /** Whether the switch is in an "on" or "off" state */
68
- value: { type: Boolean, default: false },
69
- },
70
- emits: ['input'],
71
- setup() {
72
- const iconSlotHasContent = useSlotHasContent('icon');
60
+ value?: boolean;
61
+ }>(),
62
+ {
63
+ align: 'center',
64
+ disabled: false,
65
+ icon: null,
66
+ iconColor: 'gray-100',
67
+ label: undefined,
68
+ title: undefined,
69
+ value: false,
70
+ }
71
+ );
72
+
73
+ defineEmits<{
74
+ (e: 'input', value: boolean): void;
75
+ }>();
73
76
 
74
- return {
75
- iconSlotHasContent,
76
- };
77
- },
78
- });
77
+ const iconSlotHasContent = useSlotHasContent('icon');
79
78
  </script>
80
79
 
81
80
  <style lang="scss" scoped>
@@ -3,7 +3,7 @@
3
3
  class="d-flex flex-column gap-1"
4
4
  :class="{
5
5
  'c-text-area--disabled': disabled,
6
- 'c-text-area--invalid': !isValidationValid,
6
+ 'c-text-area--invalid': isInvalid,
7
7
  'c-text-area--hide-label': hideLabel,
8
8
  }"
9
9
  :data-test="dataTest"
@@ -21,7 +21,7 @@
21
21
  rows="4"
22
22
  :aria-label="label || undefined"
23
23
  :aria-describedby="`${idWithFallback}-message ${idWithFallback}-validation-message`"
24
- :aria-invalid="isValidationValid ? 'false' : 'true'"
24
+ :aria-invalid="isInvalid ? 'true' : 'false'"
25
25
  :data-test="`${dataTest}-textarea`"
26
26
  :disabled="disabled"
27
27
  :placeholder="placeholder"
@@ -37,7 +37,7 @@
37
37
 
38
38
  <CValidationMessage
39
39
  :data-test="`${dataTest}-validation-message`"
40
- :validation-message="validationMessage"
40
+ :validation-message="errorMessage || validationMessage"
41
41
  :hide="hideDetails"
42
42
  >
43
43
  <template #append>
@@ -62,9 +62,9 @@
62
62
  </div>
63
63
  </template>
64
64
 
65
- <script lang="ts">
66
- import type { PropType } from 'vue';
67
- import { defineComponent, ref, toRefs } from 'vue';
65
+ <script setup lang="ts">
66
+ import type { VNode } from 'vue';
67
+ import { computed, ref, toRefs } from 'vue';
68
68
 
69
69
  import CFormFieldCounter from '@propelinc/citrus-ui/src/components/CFormFieldCounter.vue';
70
70
  import CValidationMessage from '@propelinc/citrus-ui/src/components/CValidationMessage.vue';
@@ -74,86 +74,110 @@ import { useSlotHasContent } from '@propelinc/citrus-ui/src/composables/slots';
74
74
  import type { Rules } from '@propelinc/citrus-ui/src/composables/validations';
75
75
  import { useValidations } from '@propelinc/citrus-ui/src/composables/validations';
76
76
 
77
- export default defineComponent({
78
- name: 'CTextArea',
79
- components: { CValidationMessage, CFormFieldCounter },
80
- props: {
81
- /** The textarea's label, both visual and screenreader-only */
82
- label: { type: String as PropType<string | null>, default: null },
83
- /** The textarea's placeholder */
84
- placeholder: { type: String, default: '' },
85
- /** The textarea element's id */
86
- id: { type: String as PropType<string | null>, default: null },
77
+ const props = withDefaults(
78
+ defineProps<{
79
+ /** Shows a character counter and sets its denominator. */
80
+ counter?: number | null;
81
+ /**
82
+ * Function or number that determines the numerator of the counter. If not
83
+ * set, the counter will be the length of the input value.
84
+ */
85
+ counterValue?: number | (() => number | null) | null;
86
+ /** Adds a custom data test string to the form field and textarea elements */
87
+ dataTest?: string;
87
88
  /** Controls whether the input is disabled */
88
- disabled: { type: Boolean, default: false },
89
- /** The textarea's value */
90
- value: { type: String, default: '' },
89
+ disabled?: boolean;
90
+ /**
91
+ * Overrides the default validation message. If provided, this error message will always
92
+ * be shown, regardless of any other failing validation rules.
93
+ */
94
+ errorMessage?: string | null;
91
95
  /** Hides the label and instead labels the field with `aria-label` */
92
- hideLabel: { type: Boolean, default: false },
93
- /** Hides the validation message area. Defaults to `false` (show message area even when valid), so validations transition in gracefully. */
94
- hideDetails: { type: [Boolean, String] as PropType<boolean | 'auto'>, default: false },
95
- /** Adds a custom data test string to the form field and textarea elements */
96
- dataTest: { type: String, default: 'text-area' },
97
- /** Shows a character counter and sets its denominator. */
98
- counter: { type: Number as PropType<number | null>, default: null },
99
- /** Function or number that determines the numerator of the counter. If not
100
- * set, the counter will be the length of the input value. */
101
- counterValue: {
102
- type: [Number, Function] as PropType<number | (() => number | null)>,
103
- default: null,
104
- },
105
- maxlength: { type: Number, default: undefined },
106
- /** Accepts a mixed array of types function, boolean and string.
96
+ hideLabel?: boolean;
97
+ /**
98
+ * Hides the validation message area. Defaults to `false` (show message area even when valid),
99
+ * so validations transition in gracefully.
100
+ */
101
+ hideDetails?: boolean | 'auto';
102
+ /** The textarea element's id */
103
+ id?: string | null;
104
+ /** The textarea's label, both visual and screenreader-only */
105
+ label?: string | null;
106
+ /** Sets the maxlength attribute of the textarea */
107
+ maxlength?: number;
108
+ /** The textarea's placeholder */
109
+ placeholder?: string;
110
+ /** If true, the field is required */
111
+ required?: boolean;
112
+ /**
113
+ * Accepts a mixed array of types function, boolean and string.
107
114
  * Functions pass an input value as an argument and must return either true / false or
108
115
  * a string containing an error message. The input field will enter an error state if a
109
- * function returns (or any value in the array contains) false or is a string */
110
- rules: { type: Array as PropType<Rules<string>>, default: () => [] },
111
- /** If true, the field is required */
112
- required: { type: Boolean, default: false },
113
- },
114
- emits: ['input', 'change', 'blur', 'focus'],
115
- setup(props, { emit }) {
116
- const { value, rules, required, id } = toRefs(props);
117
- const idWithFallback = useId(id);
118
-
119
- const internalValue = useInternalValue(value, {
120
- onChange: (value) => emit('input', value),
121
- });
122
-
123
- const messageSlotHasContent = useSlotHasContent('message');
124
-
125
- const textarea = ref<HTMLTextAreaElement | null>(null);
126
-
127
- const {
128
- message: validationMessage,
129
- valid: isValidationValid,
130
- listeners: validationListeners,
131
- } = useValidations({
132
- id: idWithFallback,
133
- value: internalValue,
134
- rules,
135
- required,
136
- field: textarea,
137
- });
138
-
139
- const onInput = (event: Event): void => {
140
- const target = event.target as HTMLTextAreaElement;
141
- internalValue.value = target.value;
142
- };
143
-
144
- return {
145
- textarea,
146
- idWithFallback,
147
- internalValue,
148
- validationMessage,
149
- isValidationValid,
150
- validationListeners,
151
- messageSlotHasContent,
152
-
153
- onInput,
154
- };
155
- },
116
+ * function returns (or any value in the array contains) false or is a string
117
+ */
118
+ rules?: Rules<string>;
119
+ /** The textarea's value */
120
+ value?: string;
121
+ }>(),
122
+ {
123
+ counter: null,
124
+ counterValue: null,
125
+ dataTest: 'text-area',
126
+ disabled: false,
127
+ errorMessage: null,
128
+ hideDetails: false,
129
+ hideLabel: false,
130
+ id: null,
131
+ label: null,
132
+ maxlength: undefined,
133
+ placeholder: '',
134
+ required: false,
135
+ rules: () => [],
136
+ value: '',
137
+ }
138
+ );
139
+
140
+ const emit = defineEmits<{
141
+ input: [value: string];
142
+ change: [value: string];
143
+ blur: [];
144
+ focus: [];
145
+ }>();
146
+
147
+ defineSlots<{
148
+ label?: () => VNode[];
149
+ message?: () => VNode[];
150
+ }>();
151
+
152
+ const { value, rules, required, id } = toRefs(props);
153
+ const idWithFallback = useId(id);
154
+
155
+ const internalValue = useInternalValue(value, {
156
+ onChange: (value) => emit('input', value),
157
+ });
158
+
159
+ const messageSlotHasContent = useSlotHasContent('message');
160
+
161
+ const textarea = ref<HTMLTextAreaElement | null>(null);
162
+
163
+ const {
164
+ message: validationMessage,
165
+ valid: isValidationValid,
166
+ listeners: validationListeners,
167
+ } = useValidations({
168
+ id: idWithFallback,
169
+ value: internalValue,
170
+ rules,
171
+ required,
172
+ field: textarea,
156
173
  });
174
+
175
+ const isInvalid = computed(() => !isValidationValid.value || !!props.errorMessage);
176
+
177
+ const onInput = (event: Event): void => {
178
+ const target = event.target as HTMLTextAreaElement;
179
+ internalValue.value = target.value;
180
+ };
157
181
  </script>
158
182
 
159
183
  <style lang="scss" scoped>
@@ -168,6 +192,10 @@ export default defineComponent({
168
192
  margin-bottom: 4px;
169
193
  transition: color $citrus-field-transition-duration ease;
170
194
 
195
+ .c-text-area--invalid & {
196
+ color: $color-status-error;
197
+ }
198
+
171
199
  .c-text-area--disabled & {
172
200
  color: $color-text-disabled;
173
201
  }
@@ -186,12 +214,17 @@ export default defineComponent({
186
214
  transition: border-color $citrus-field-transition-duration ease;
187
215
 
188
216
  &:focus,
189
- &:focus-within,
190
- .c-text-area--invalid & {
217
+ &:focus-within {
191
218
  border-color: $color-black;
192
219
  outline: none;
193
220
  }
194
221
 
222
+ .c-text-area--invalid & {
223
+ border-color: $color-status-error;
224
+ color: $color-status-error;
225
+ outline: none;
226
+ }
227
+
195
228
  &:disabled {
196
229
  background-color: $color-gray-100;
197
230
  color: $color-text-disabled;