@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
@@ -125,17 +125,24 @@
125
125
  </template>
126
126
 
127
127
  <script lang="ts">
128
+ // NOTE(slanden): These rules do not properly handle multiple script tags in Vue
129
+ // eslint-disable-next-line import/order, import/no-duplicates
130
+ import type { Ref } from 'vue';
131
+
132
+ export interface CTextFieldExposed {
133
+ input: Ref<HTMLInputElement | null>;
134
+ focus: () => void;
135
+ blur: () => void;
136
+ startValidating: () => void;
137
+ }
138
+ </script>
139
+
140
+ <script setup lang="ts">
141
+ // eslint-disable-next-line import/order
128
142
  import { faEye, faEyeSlash, faXmark } from '@fortawesome/pro-regular-svg-icons';
129
- import {
130
- type PropType,
131
- type Ref,
132
- type StyleValue,
133
- computed,
134
- defineComponent,
135
- onMounted,
136
- ref,
137
- toRefs,
138
- } from 'vue';
143
+ // eslint-disable-next-line import/no-duplicates
144
+ import type { StyleValue, VNode } from 'vue';
145
+ import { computed, onMounted, ref, toRefs, useAttrs, useSlots } from 'vue';
139
146
 
140
147
  import CFormFieldCounter from '@propelinc/citrus-ui/src/components/CFormFieldCounter.vue';
141
148
  import CIconButton from '@propelinc/citrus-ui/src/components/CIconButton.vue';
@@ -147,203 +154,192 @@ import { useSlotHasContent } from '@propelinc/citrus-ui/src/composables/slots';
147
154
  import type { Rules } from '@propelinc/citrus-ui/src/composables/validations';
148
155
  import { useValidations } from '@propelinc/citrus-ui/src/composables/validations';
149
156
 
150
- // Export the interface for what this component exposes
151
- export interface CTextFieldExposed {
152
- input: Ref<HTMLInputElement | null>;
153
- focus: () => void;
154
- blur: () => void;
155
- startValidating: () => void;
156
- }
157
-
158
- export default defineComponent({
159
- name: 'CTextField',
160
- components: { CFormFieldCounter, CIconButton, CValidationMessage },
161
- inheritAttrs: false,
162
- props: {
163
- id: { type: String as PropType<string | null>, default: null },
164
- label: { type: String as PropType<string | null>, default: null },
165
- ariaLabel: { type: String as PropType<string | null>, default: null },
166
- placeholder: { type: String, default: '' },
167
- type: {
168
- type: String as PropType<
169
- 'text' | 'password' | 'email' | 'tel' | 'url' | 'search' | 'number' | 'decimal' | 'numeric'
170
- >,
171
- default: 'text',
172
- },
173
- clearable: { type: Boolean, default: false },
174
- size: { type: String as PropType<'medium' | 'large'>, default: 'medium' },
175
- inputmode: {
176
- type: String as PropType<
177
- 'text' | 'search' | 'none' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal'
178
- >,
179
- default: undefined,
180
- },
181
- minlength: { type: [Number, String], default: undefined },
182
- maxlength: { type: [Number, String], default: undefined },
183
- disabled: { type: Boolean, default: false },
184
- hideable: { type: Boolean, default: false },
185
- hideLabel: { type: Boolean, default: false },
186
- hideDetails: { type: [Boolean, String] as PropType<boolean | 'auto'>, default: false },
187
- value: { type: String, default: '' },
188
- dataTest: { type: String, default: 'text-field' },
189
- counter: { type: Number as PropType<number | null>, default: null },
190
- counterValue: {
191
- type: [Number, Function] as PropType<number | (() => number | null)>,
192
- default: null,
193
- },
194
- rules: { type: Array as PropType<Rules<string>>, default: () => [] },
195
- validateOnBlur: { type: Boolean, default: true },
196
- required: { type: Boolean, default: false },
157
+ defineOptions({ inheritAttrs: false });
158
+
159
+ const props = withDefaults(
160
+ defineProps<{
161
+ id?: string | null;
162
+ label?: string | null;
163
+ ariaLabel?: string | null;
164
+ placeholder?: string;
165
+ type?:
166
+ | 'text'
167
+ | 'password'
168
+ | 'email'
169
+ | 'tel'
170
+ | 'url'
171
+ | 'search'
172
+ | 'number'
173
+ | 'decimal'
174
+ | 'numeric';
175
+ clearable?: boolean;
176
+ size?: 'medium' | 'large';
177
+ inputmode?: 'text' | 'search' | 'none' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal';
178
+ minlength?: number | string;
179
+ maxlength?: number | string;
180
+ disabled?: boolean;
181
+ hideable?: boolean;
182
+ hideLabel?: boolean;
183
+ hideDetails?: boolean | 'auto';
184
+ value?: string;
185
+ dataTest?: string;
186
+ counter?: number | null;
187
+ counterValue?: number | (() => number | null) | null;
188
+ rules?: Rules<string>;
189
+ validateOnBlur?: boolean;
190
+ required?: boolean;
197
191
  /**
198
192
  * Overrides the default validation message. If provided, this error message will always
199
193
  * be shown, regardless of any other failing validation rules.
200
194
  */
201
- errorMessage: { type: String, default: null },
202
- },
203
- emits: [
204
- 'keyup',
205
- 'keydown',
206
- 'keypress',
207
- 'input',
208
- 'focus',
209
- 'blur',
210
- 'change',
211
- 'click',
212
- 'click:hide',
213
- ],
214
- setup(props, { attrs, emit, slots, expose }) {
215
- const { t } = useTranslation();
216
-
217
- const isFocused = ref(false);
218
- const inputHidden = ref(true);
219
- const inputType = computed(() =>
220
- props.hideable && inputHidden.value ? 'password' : props.type
221
- );
222
- const hideToggleAriaLabel = computed(() => {
223
- return props.hideable && inputHidden.value ? t('Show input value') : t('Hide input value');
224
- });
195
+ errorMessage?: string;
196
+ }>(),
197
+ {
198
+ id: null,
199
+ label: null,
200
+ ariaLabel: null,
201
+ placeholder: '',
202
+ type: 'text',
203
+ clearable: false,
204
+ size: 'medium',
205
+ inputmode: undefined,
206
+ minlength: undefined,
207
+ maxlength: undefined,
208
+ disabled: false,
209
+ hideable: false,
210
+ hideLabel: false,
211
+ hideDetails: false,
212
+ value: '',
213
+ dataTest: 'text-field',
214
+ counter: null,
215
+ counterValue: null,
216
+ rules: () => [],
217
+ validateOnBlur: true,
218
+ required: false,
219
+ errorMessage: undefined,
220
+ }
221
+ );
222
+
223
+ const emit = defineEmits<{
224
+ 'keyup': [event: KeyboardEvent];
225
+ 'keydown': [event: KeyboardEvent];
226
+ 'keypress': [event: KeyboardEvent];
227
+ 'input': [value: string];
228
+ 'focus': [event: Event];
229
+ 'blur': [event: Event];
230
+ 'change': [value: string];
231
+ 'click': [event: MouseEvent];
232
+ 'click:hide': [hidden: boolean];
233
+ }>();
234
+
235
+ defineSlots<{
236
+ 'label'?: () => VNode[];
237
+ 'prepend-inner'?: () => VNode[];
238
+ 'append'?: () => VNode[];
239
+ 'message'?: () => VNode[];
240
+ }>();
241
+
242
+ const attrs = useAttrs();
243
+ const slots = useSlots();
244
+ const { t } = useTranslation();
245
+
246
+ const isFocused = ref(false);
247
+ const inputHidden = ref(true);
248
+ const inputType = computed(() => (props.hideable && inputHidden.value ? 'password' : props.type));
249
+ const hideToggleAriaLabel = computed(() => {
250
+ return props.hideable && inputHidden.value ? t('Show input value') : t('Hide input value');
251
+ });
225
252
 
226
- const { id, value, rules, required } = toRefs(props);
253
+ const { id, value, rules, required } = toRefs(props);
227
254
 
228
- const idWithFallback = useId(id);
255
+ const idWithFallback = useId(id);
229
256
 
230
- const inputValue = useInternalValue(value, {
231
- onChange: (newValue) => {
232
- emit('input', newValue);
233
- },
234
- });
235
-
236
- const input: Ref<HTMLInputElement | null> = ref(null);
237
- const {
238
- message: validationMessage,
239
- valid: isValidationValid,
240
- listeners: validationListeners,
241
- startValidating,
242
- } = useValidations({
243
- id: idWithFallback,
244
- value: inputValue,
245
- rules,
246
- required,
247
- validateOn: computed(() => (props.validateOnBlur ? 'blur' : 'input')),
248
- field: input,
249
- });
250
-
251
- const isInvalid = computed(() => !isValidationValid.value || !!props.errorMessage);
252
-
253
- const prefixSlotHasContent = useSlotHasContent('prepend-inner');
254
- const suffixSlotHasContent = useSlotHasContent('append');
255
- const messageSlotHasContent = useSlotHasContent('message');
256
-
257
- const toggleHidden = (): void => {
258
- inputHidden.value = !inputHidden.value;
259
- emit('click:hide', inputHidden.value);
260
- };
261
-
262
- const onInput = (event: Event): void => {
263
- const target = event.target as HTMLInputElement;
264
- if (target.value !== inputValue.value) {
265
- inputValue.value = target.value;
266
- validationListeners.input(event);
267
- }
268
- };
269
-
270
- const onChange = (event: Event): void => {
271
- validationListeners.change(event);
272
- const target = event.target as HTMLInputElement;
273
- emit('change', target.value);
274
- };
275
-
276
- const onFocus = (event: Event): void => {
277
- isFocused.value = true;
278
- emit('focus', event);
279
- };
280
-
281
- const onBlur = (event: Event): void => {
282
- isFocused.value = false;
283
- validationListeners.blur(event);
284
- emit('blur', event);
285
- };
286
-
287
- const clearInput = (): void => {
288
- emit('input', '');
289
- };
290
-
291
- const focus = (): void => {
292
- input.value?.focus();
293
- };
294
-
295
- const blur = (): void => {
296
- input.value?.blur();
297
- };
298
-
299
- onMounted(() => {
300
- if (!props.label && !slots.label && !props.ariaLabel) {
301
- console.error(
302
- 'Missing CTextField label. Please provide a label prop, an ariaLabel prop or pass a label via the label slot.'
303
- );
304
- }
305
- });
306
-
307
- const exposedAPI: CTextFieldExposed = { input, focus, blur, startValidating };
308
- expose(exposedAPI);
309
-
310
- const fallthroughAttrs = computed(() => {
311
- const { class: _, style: __, ...rest } = attrs;
312
- return rest;
313
- });
314
-
315
- // Need to compute these to cast the type to avoid type errors
316
- const rootStyles = computed(() => attrs.style as StyleValue);
317
-
318
- return {
319
- faXmark,
320
- faEye,
321
- faEyeSlash,
322
-
323
- fallthroughAttrs,
324
- hideToggleAriaLabel,
325
- idWithFallback,
326
- input,
327
- inputHidden,
328
- inputType,
329
- inputValue,
330
- isFocused,
331
- messageSlotHasContent,
332
- prefixSlotHasContent,
333
- rootStyles,
334
- suffixSlotHasContent,
335
- isInvalid,
336
- validationMessage,
337
-
338
- clearInput,
339
- onBlur,
340
- onChange,
341
- onFocus,
342
- onInput,
343
- toggleHidden,
344
- };
257
+ const inputValue = useInternalValue(value, {
258
+ onChange: (newValue) => {
259
+ emit('input', newValue);
345
260
  },
346
261
  });
262
+
263
+ const input = ref<HTMLInputElement | null>(null);
264
+ const {
265
+ message: validationMessage,
266
+ valid: isValidationValid,
267
+ listeners: validationListeners,
268
+ startValidating,
269
+ } = useValidations({
270
+ id: idWithFallback,
271
+ value: inputValue,
272
+ rules,
273
+ required,
274
+ validateOn: computed(() => (props.validateOnBlur ? 'blur' : 'input')),
275
+ field: input,
276
+ });
277
+
278
+ const isInvalid = computed(() => !isValidationValid.value || !!props.errorMessage);
279
+
280
+ const prefixSlotHasContent = useSlotHasContent('prepend-inner');
281
+ const suffixSlotHasContent = useSlotHasContent('append');
282
+ const messageSlotHasContent = useSlotHasContent('message');
283
+
284
+ const toggleHidden = (): void => {
285
+ inputHidden.value = !inputHidden.value;
286
+ emit('click:hide', inputHidden.value);
287
+ };
288
+
289
+ const onInput = (event: Event): void => {
290
+ const target = event.target as HTMLInputElement;
291
+ if (target.value !== inputValue.value) {
292
+ inputValue.value = target.value;
293
+ validationListeners.input(event);
294
+ }
295
+ };
296
+
297
+ const onChange = (event: Event): void => {
298
+ validationListeners.change(event);
299
+ const target = event.target as HTMLInputElement;
300
+ emit('change', target.value);
301
+ };
302
+
303
+ const onFocus = (event: Event): void => {
304
+ isFocused.value = true;
305
+ emit('focus', event);
306
+ };
307
+
308
+ const onBlur = (event: Event): void => {
309
+ isFocused.value = false;
310
+ validationListeners.blur(event);
311
+ emit('blur', event);
312
+ };
313
+
314
+ const clearInput = (): void => {
315
+ emit('input', '');
316
+ };
317
+
318
+ const focus = (): void => {
319
+ input.value?.focus();
320
+ };
321
+
322
+ const blur = (): void => {
323
+ input.value?.blur();
324
+ };
325
+
326
+ onMounted(() => {
327
+ if (!props.label && !slots.label && !props.ariaLabel) {
328
+ console.error(
329
+ 'Missing CTextField label. Please provide a label prop, an ariaLabel prop or pass a label via the label slot.'
330
+ );
331
+ }
332
+ });
333
+
334
+ const exposedAPI: CTextFieldExposed = { input, focus, blur, startValidating };
335
+ defineExpose(exposedAPI);
336
+
337
+ const fallthroughAttrs = computed(() => {
338
+ const { class: _, style: __, ...rest } = attrs;
339
+ return rest;
340
+ });
341
+
342
+ const rootStyles = computed(() => attrs.style as StyleValue);
347
343
  </script>
348
344
 
349
345
  <style lang="scss" scoped>
@@ -13,39 +13,42 @@
13
13
  </a>
14
14
  </template>
15
15
 
16
- <script lang="ts">
17
- import type { PropType } from 'vue';
18
- import { computed, defineComponent } from 'vue';
16
+ <script setup lang="ts">
17
+ import type { VNode } from 'vue';
18
+ import { computed } from 'vue';
19
19
  import type { RouteLocationRaw } from 'vue-router';
20
20
 
21
21
  import { useRouterLink } from '@propelinc/citrus-ui/src/composables/router';
22
22
 
23
- export default defineComponent({
24
- props: {
23
+ const props = withDefaults(
24
+ defineProps<{
25
25
  /** Controls whether the text link functions as a router-link */
26
- to: { type: [String, Object] as PropType<RouteLocationRaw>, default: undefined },
26
+ to?: RouteLocationRaw;
27
27
  /** Designates the text link as an anchor and applies the href attribute */
28
- href: { type: String, default: undefined },
28
+ href?: string;
29
29
  /** Designates the target attribute. Only use with the href prop. */
30
- target: { type: String, default: undefined },
30
+ target?: string;
31
31
  /** Toggles whether the link should have an underline */
32
- underline: { type: Boolean, default: true },
33
- },
34
- setup(props) {
35
- const to = computed(() => props.to);
36
- const { href: routerDestination, navigate } = useRouterLink(to);
37
- const routerDestinationOrHref = computed(() => routerDestination.value ?? props.href);
38
-
39
- const isRoleButton = computed(() => {
40
- return !props.to && !props.href;
41
- });
42
-
43
- return {
44
- isRoleButton,
45
- navigate,
46
- routerDestinationOrHref,
47
- };
48
- },
32
+ underline?: boolean;
33
+ }>(),
34
+ {
35
+ to: undefined,
36
+ href: undefined,
37
+ target: undefined,
38
+ underline: true,
39
+ }
40
+ );
41
+
42
+ defineSlots<{
43
+ default?: () => VNode[];
44
+ }>();
45
+
46
+ const to = computed(() => props.to);
47
+ const { href: routerDestination, navigate } = useRouterLink(to);
48
+ const routerDestinationOrHref = computed(() => routerDestination.value ?? props.href);
49
+
50
+ const isRoleButton = computed(() => {
51
+ return !props.to && !props.href;
49
52
  });
50
53
  </script>
51
54
 
@@ -19,8 +19,9 @@
19
19
  </div>
20
20
  </template>
21
21
 
22
- <script lang="ts">
23
- import { type PropType, defineComponent, ref } from 'vue';
22
+ <script setup lang="ts">
23
+ import type { IconDefinition } from '@fortawesome/fontawesome-svg-core';
24
+ import { ref } from 'vue';
24
25
 
25
26
  import CSquaredIcon from '@propelinc/citrus-ui/src/components/CSquaredIcon.vue';
26
27
 
@@ -28,71 +29,41 @@ type Size = 'medium' | 'large';
28
29
 
29
30
  type ImageState = 'loading' | 'loaded' | 'failed';
30
31
 
31
- const SIZES = new Set<Size>(['medium', 'large']);
32
-
33
32
  const SIZE_TO_VALUE: Record<Size, `${number}px`> = {
34
33
  medium: '40px',
35
34
  large: '56px',
36
35
  };
37
36
 
38
- /**
39
- * A component that displays a third party logo with a fallback icon.
40
- *
41
- * The fallback icon is displayed while the image is loading or if the image fails to load.
42
- *
43
- * The image is rendered, but hidden, while loading, and displayed when loaded.
44
- */
45
- export default defineComponent({
46
- name: 'CThirdPartyLogo',
47
- components: {
48
- CSquaredIcon,
49
- },
50
- props: {
51
- /**
52
- * The alt text for the logo.
53
- */
54
- alt: { type: String, required: true },
55
- /**
56
- * The color of the fallback icon logo
57
- */
58
- color: CSquaredIcon.props.color,
59
- /**
60
- * The Font Awesome icon to display as a fallback
61
- */
62
- icon: CSquaredIcon.props.icon,
63
- /**
64
- * The size of the logo. Defaults to `medium`.
65
- */
66
- size: {
67
- type: String as PropType<Size>,
68
- required: false,
69
- default: 'medium',
70
- validator: (value: string) => SIZES.has(value as Size),
71
- },
72
- /**
73
- * The URL of the logo image
74
- */
75
- src: { type: String, required: false, default: '' },
76
- },
77
- setup() {
78
- const imageState = ref<ImageState>('loading');
37
+ withDefaults(
38
+ defineProps<{
39
+ /** The alt text for the logo. */
40
+ alt: string;
41
+ /** The color of the fallback icon logo */
42
+ color?: string;
43
+ /** The Font Awesome icon to display as a fallback */
44
+ icon?: string | string[] | IconDefinition | null;
45
+ /** The size of the logo. Defaults to `medium`. */
46
+ size?: Size;
47
+ /** The URL of the logo image */
48
+ src?: string;
49
+ }>(),
50
+ {
51
+ color: 'gray-100',
52
+ icon: null,
53
+ size: 'medium',
54
+ src: '',
55
+ }
56
+ );
79
57
 
80
- const handleImageError = (): void => {
81
- imageState.value = 'failed';
82
- };
58
+ const imageState = ref<ImageState>('loading');
83
59
 
84
- const handleImageLoad = (): void => {
85
- imageState.value = 'loaded';
86
- };
60
+ const handleImageError = (): void => {
61
+ imageState.value = 'failed';
62
+ };
87
63
 
88
- return {
89
- handleImageError,
90
- handleImageLoad,
91
- imageState,
92
- SIZE_TO_VALUE,
93
- };
94
- },
95
- });
64
+ const handleImageLoad = (): void => {
65
+ imageState.value = 'loaded';
66
+ };
96
67
  </script>
97
68
 
98
69
  <style scoped>