@propelinc/citrus-ui 0.5.1 → 1.0.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 +56 -86
- package/dist/colors/colors.d.ts +31 -0
- package/dist/colors/theme.d.ts +2 -11
- package/dist/colors/util-classes.d.ts +11 -0
- package/dist/components/CAccordion.vue.d.ts +21 -0
- package/dist/components/CAccordionItem.vue.d.ts +41 -0
- package/dist/components/CAppBar.vue.d.ts +156 -0
- package/dist/components/CBadge.vue.d.ts +52 -0
- package/dist/components/CBottomSheet.vue.d.ts +226 -0
- package/dist/components/CButton/CButton.vue.d.ts +231 -0
- package/dist/components/CButton/types.d.ts +5 -0
- package/dist/components/CButtonStack.vue.d.ts +24 -0
- package/dist/components/CCard.vue.d.ts +107 -0
- package/dist/components/CCardFooter.vue.d.ts +26 -0
- package/dist/components/CCardHeader.vue.d.ts +3 -0
- package/dist/components/CCardSection.vue.d.ts +17 -0
- package/dist/components/CCheckbox.vue.d.ts +145 -0
- package/dist/components/CCol.vue.d.ts +21 -0
- package/dist/components/CDivider.vue.d.ts +17 -0
- package/dist/components/CDobField.vue.d.ts +2109 -0
- package/dist/components/CDobSelect.vue.d.ts +398 -0
- package/dist/components/CEmailField.vue.d.ts +699 -0
- package/dist/components/CExpandTransition.vue.d.ts +19 -0
- package/dist/components/CFadeTransition.vue.d.ts +3 -0
- package/dist/components/CFileInput.vue.d.ts +98 -0
- package/dist/components/CFixedPageFooter.vue.d.ts +106 -0
- package/dist/components/CForm.vue.d.ts +29 -0
- package/dist/components/CFormFieldCounter.vue.d.ts +42 -0
- package/dist/components/CIconButton.vue.d.ts +390 -0
- package/dist/components/CLabel.vue.d.ts +32 -0
- package/dist/components/CListItem.vue.d.ts +208 -0
- package/dist/components/CListItemContent.vue.d.ts +27 -0
- package/dist/components/CListItemIcon.vue.d.ts +54 -0
- package/dist/components/CLoader.vue.d.ts +73 -0
- package/dist/components/CLogo.vue.d.ts +19 -0
- package/dist/components/CMaskedTextField.vue.d.ts +2012 -0
- package/dist/components/CMenu.vue.d.ts +6 -0
- package/dist/components/CMenuItem.vue.d.ts +170 -0
- package/dist/components/CMenuLabel.vue.d.ts +3 -0
- package/dist/components/CModal.vue.d.ts +206 -0
- package/dist/components/CModalLoading.vue.d.ts +230 -0
- package/dist/components/CNotification.vue.d.ts +589 -0
- package/dist/components/CPhoneField.vue.d.ts +2088 -0
- package/dist/components/CPill.vue.d.ts +42 -0
- package/dist/components/CPillGroup.vue.d.ts +70 -0
- package/dist/components/CPopup.vue.d.ts +21 -0
- package/dist/components/CProgressLinear.vue.d.ts +61 -0
- package/dist/components/CProgressRing.vue.d.ts +103 -0
- package/dist/components/CRadio.vue.d.ts +73 -0
- package/dist/components/CRadioGroup.vue.d.ts +123 -0
- package/dist/components/CRebrand.vue.d.ts +28 -0
- package/dist/components/CRow.vue.d.ts +67 -0
- package/dist/components/CSafeArea.vue.d.ts +18 -0
- package/dist/components/CSectionHeader.vue.d.ts +28 -0
- package/dist/components/CSelect.vue.d.ts +293 -0
- package/dist/components/CSkeleton.vue.d.ts +3 -0
- package/dist/components/CSkeletonLoaderCard.vue.d.ts +21 -0
- package/dist/components/CSkeletonLoaderCircle.vue.d.ts +5 -0
- package/dist/components/CSkeletonLoaderText.vue.d.ts +44 -0
- package/dist/components/CSlideFadeTransition.vue.d.ts +58 -0
- package/dist/components/CSplitInput.vue.d.ts +2131 -0
- package/dist/components/CSquaredIcon.vue.d.ts +47 -0
- package/dist/components/CSsnField.vue.d.ts +2083 -0
- package/dist/components/CStatusDot.vue.d.ts +27 -0
- package/dist/components/CSwitch.vue.d.ts +54 -0
- package/dist/components/CSwitchListItem.vue.d.ts +392 -0
- package/dist/components/CTextArea.vue.d.ts +240 -0
- package/dist/components/CTextField.vue.d.ts +647 -0
- package/dist/components/CTextLink.vue.d.ts +55 -0
- package/dist/components/CThirdPartyLogo.vue.d.ts +128 -0
- package/dist/components/CTimeago.vue.d.ts +12 -0
- package/dist/components/CToast.vue.d.ts +458 -0
- package/dist/components/CToastsList.vue.d.ts +430 -0
- package/dist/components/CValidationMessage.vue.d.ts +45 -0
- package/dist/components/CZipcodeField.vue.d.ts +2080 -0
- package/dist/components/index.d.ts +66 -25
- package/dist/components/internal/CCloseButton.vue.d.ts +14 -0
- package/dist/composables/accessibility.d.ts +1 -0
- package/dist/composables/animation.d.ts +12 -0
- package/dist/composables/binding.d.ts +19 -0
- package/dist/composables/colors.d.ts +13 -0
- package/dist/composables/elements.d.ts +3 -0
- package/dist/composables/fields.d.ts +9 -0
- package/dist/composables/gestures.d.ts +53 -0
- package/dist/composables/i18n.d.ts +3 -0
- package/dist/composables/id.d.ts +11 -0
- package/dist/composables/input-mask.d.ts +18 -0
- package/dist/composables/router.d.ts +30 -0
- package/dist/composables/slots.d.ts +2 -0
- package/dist/composables/toast.d.ts +21 -0
- package/dist/composables/validations.d.ts +77 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.mjs +11738 -0
- package/dist/index.mjs.map +1 -0
- package/dist/plugin.d.ts +2 -2
- package/dist/services/animation.d.ts +17 -0
- package/dist/services/directives/index.d.ts +2 -0
- package/dist/services/directives/scroll-into-view.d.ts +7 -0
- package/dist/services/directives/tap-animation.d.ts +6 -0
- package/dist/services/id.d.ts +22 -0
- package/dist/services/injections/accordions.d.ts +3 -0
- package/dist/services/injections/animations.d.ts +2 -0
- package/dist/services/injections/buttons.d.ts +4 -0
- package/dist/services/injections/forms.d.ts +6 -0
- package/dist/services/injections/icon-buttons.d.ts +3 -0
- package/dist/services/injections/pills.d.ts +4 -0
- package/dist/services/injections/radio.d.ts +10 -0
- package/dist/styles/main.css +3002 -0
- package/dist/styles/utils.css +2319 -0
- package/dist/theme/icons.d.ts +35 -2
- package/dist/types/CForm.d.ts +12 -0
- package/dist/types/font-awesome.d.ts +5 -0
- package/dist/types.d.ts +12 -0
- package/index.ts +2 -0
- package/package.json +63 -77
- package/src/assets/fonts/grenette-regular.woff2 +0 -0
- package/src/assets/fonts/grenette-semibold.woff2 +0 -0
- package/src/assets/fonts/polymath.woff2 +0 -0
- package/src/assets/logos/propel/icon.svg +15 -0
- package/src/assets/logos/propel/lockup.svg +11 -0
- package/src/colors/colors.ts +173 -0
- package/src/colors/theme.ts +8 -14
- package/src/colors/util-classes.ts +49 -0
- package/src/componentResolver.js +33 -0
- package/src/components/CAccordion.vue +32 -7
- package/src/components/CAccordionItem.vue +109 -36
- package/src/components/CAppBar.vue +237 -0
- package/src/components/CBadge.vue +74 -0
- package/src/components/CBottomSheet.vue +430 -0
- package/src/components/CButton/CButton.vue +347 -0
- package/src/components/CButton/types.ts +5 -0
- package/src/components/CButtonStack.vue +36 -0
- package/src/components/CCard.vue +149 -41
- package/src/components/CCardFooter.vue +11 -27
- package/src/components/CCardHeader.vue +30 -21
- package/src/components/CCardSection.vue +23 -12
- package/src/components/CCheckbox.vue +191 -21
- package/src/components/CCol.vue +55 -0
- package/src/components/CDivider.vue +46 -0
- package/src/components/CDobField.vue +153 -0
- package/src/components/CDobSelect.vue +274 -0
- package/src/components/CEmailField.vue +61 -0
- package/src/components/CExpandTransition.vue +55 -0
- package/src/components/CFadeTransition.vue +23 -0
- package/src/components/CFileInput.vue +186 -0
- package/src/components/CFixedPageFooter.vue +76 -0
- package/src/components/CForm.vue +86 -0
- package/src/components/CFormFieldCounter.vue +40 -0
- package/src/components/CIconButton.vue +175 -59
- package/src/components/CLabel.vue +52 -0
- package/src/components/CListItem.vue +149 -45
- package/src/components/CListItemContent.vue +60 -0
- package/src/components/CListItemIcon.vue +27 -31
- package/src/components/CLoader.vue +156 -0
- package/src/components/CLogo.vue +23 -0
- package/src/components/CMaskedTextField.vue +118 -0
- package/src/components/CMenu.vue +24 -0
- package/src/components/CMenuItem.vue +106 -0
- package/src/components/CMenuLabel.vue +26 -0
- package/src/components/CModal.vue +198 -79
- package/src/components/CModalLoading.vue +27 -9
- package/src/components/CNotification.vue +86 -53
- package/src/components/CPhoneField.vue +69 -0
- package/src/components/CPill.vue +162 -0
- package/src/components/CPillGroup.vue +73 -0
- package/src/components/CPopup.vue +66 -0
- package/src/components/CProgressLinear.vue +52 -0
- package/src/components/CProgressRing.vue +126 -0
- package/src/components/CRadio.vue +138 -0
- package/src/components/CRadioGroup.vue +142 -0
- package/src/components/CRebrand.vue +28 -0
- package/src/components/CRow.vue +62 -0
- package/src/components/CSafeArea.vue +23 -0
- package/src/components/CSectionHeader.vue +50 -0
- package/src/components/CSelect.vue +223 -74
- package/src/components/CSkeleton.vue +65 -0
- package/src/components/CSkeletonLoaderCard.vue +29 -0
- package/src/components/CSkeletonLoaderCircle.vue +18 -14
- package/src/components/CSkeletonLoaderText.vue +127 -17
- package/src/components/CSlideFadeTransition.vue +100 -0
- package/src/components/CSplitInput.vue +111 -0
- package/src/components/CSquaredIcon.vue +83 -0
- package/src/components/CSsnField.vue +86 -0
- package/src/components/CStatusDot.vue +70 -0
- package/src/components/CSwitch.vue +125 -0
- package/src/components/CSwitchListItem.vue +110 -0
- package/src/components/CTextArea.vue +193 -47
- package/src/components/CTextField.vue +450 -93
- package/src/components/CTextLink.vue +48 -38
- package/src/components/CThirdPartyLogo.vue +127 -0
- package/src/components/CTimeago.vue +63 -0
- package/src/components/CToast.vue +259 -0
- package/src/components/CToastsList.vue +32 -0
- package/src/components/CValidationMessage.vue +70 -0
- package/src/components/CZipcodeField.vue +69 -0
- package/src/components/index.ts +66 -25
- package/src/components/internal/CCloseButton.vue +57 -0
- package/src/composables/accessibility.ts +29 -0
- package/src/composables/animation.ts +95 -0
- package/src/composables/binding.ts +34 -0
- package/src/composables/colors.ts +59 -0
- package/src/composables/elements.ts +72 -0
- package/src/composables/fields.ts +19 -0
- package/src/composables/gestures.ts +197 -0
- package/src/composables/i18n.ts +13 -0
- package/src/composables/id.ts +23 -0
- package/src/composables/input-mask.ts +139 -0
- package/src/composables/router.ts +64 -0
- package/src/composables/slots.ts +57 -0
- package/src/composables/toast.ts +64 -0
- package/src/composables/validations.ts +214 -0
- package/src/index.ts +7 -7
- package/src/plugin.ts +13 -6
- package/src/services/animation.ts +101 -0
- package/src/services/directives/index.ts +2 -0
- package/src/services/directives/scroll-into-view.ts +86 -0
- package/src/services/directives/tap-animation.ts +71 -0
- package/src/services/id.ts +31 -0
- package/src/services/injections/accordions.ts +4 -0
- package/src/services/injections/animations.ts +3 -0
- package/src/services/injections/buttons.ts +5 -0
- package/src/services/injections/forms.ts +8 -0
- package/src/services/injections/icon-buttons.ts +4 -0
- package/src/services/injections/pills.ts +7 -0
- package/src/services/injections/radio.ts +12 -0
- package/src/shims-vue.d.ts +6 -3
- package/src/styles/_animation.scss +19 -0
- package/src/styles/_button.scss +61 -0
- package/src/styles/_colors.scss +58 -11
- package/src/styles/_core.scss +280 -119
- package/src/styles/_form-fields.scss +69 -16
- package/src/styles/_grenette.scss +13 -0
- package/src/styles/_polymath.scss +14 -0
- package/src/styles/_reset.scss +105 -0
- package/src/styles/_shoelace.scss +46 -0
- package/src/styles/_typography.scss +39 -10
- package/src/styles/main.scss +6 -3
- package/src/styles/utils/a11y.scss +18 -0
- package/src/styles/utils/typography.scss +13 -0
- package/src/styles/utils.scss +560 -0
- package/src/styles/variables.scss +57 -45
- package/src/theme/icons.ts +16 -5
- package/src/types/CForm.ts +15 -0
- package/src/types/font-awesome.ts +6 -0
- package/src/types.ts +15 -0
- package/.browserslistrc +0 -3
- package/.eslintrc.js +0 -4
- package/.nvmrc +0 -1
- package/.stylelintrc.js +0 -3
- package/babel.config.js +0 -3
- package/dist/citrus-ui.common.js +0 -42228
- package/dist/citrus-ui.common.js.map +0 -1
- package/dist/citrus-ui.css +0 -1
- package/dist/citrus-ui.umd.js +0 -42238
- package/dist/citrus-ui.umd.js.map +0 -1
- package/dist/citrus-ui.umd.min.js +0 -27
- package/dist/citrus-ui.umd.min.js.map +0 -1
- package/dist/demo.html +0 -10
- package/dist/fonts/Blitz-Script.85ed9abe.woff2 +0 -0
- package/dist/fonts/ObjectSans-Bold.5492f3d5.woff2 +0 -0
- package/dist/fonts/ObjectSans-BoldSlanted.29e2a87e.woff2 +0 -0
- package/dist/fonts/ObjectSans-Heavy.d0b2f035.woff2 +0 -0
- package/dist/fonts/ObjectSans-HeavySlanted.45e9c063.woff2 +0 -0
- package/dist/fonts/ObjectSans-Light.f885dec3.woff2 +0 -0
- package/dist/fonts/ObjectSans-LightSlanted.b8eb7c12.woff2 +0 -0
- package/dist/fonts/ObjectSans-Regular.e4ea0b90.woff2 +0 -0
- package/dist/fonts/ObjectSans-Slanted.57a90be9.woff2 +0 -0
- package/dist/fonts/ObjectSans-Thin.86d44227.woff2 +0 -0
- package/dist/fonts/ObjectSans-ThinSlanted.20342160.woff2 +0 -0
- package/jest.config.js +0 -9
- package/plopfile.js +0 -67
- package/postcss.config.js +0 -5
- package/src/assets/fonts/Blitz-Script.woff2 +0 -0
- package/src/assets/fonts/ObjectSans-Bold.woff2 +0 -0
- package/src/assets/fonts/ObjectSans-BoldSlanted.woff2 +0 -0
- package/src/assets/fonts/ObjectSans-Heavy.woff2 +0 -0
- package/src/assets/fonts/ObjectSans-HeavySlanted.woff2 +0 -0
- package/src/assets/fonts/ObjectSans-Light.woff2 +0 -0
- package/src/assets/fonts/ObjectSans-LightSlanted.woff2 +0 -0
- package/src/assets/fonts/ObjectSans-Regular.woff2 +0 -0
- package/src/assets/fonts/ObjectSans-Slanted.woff2 +0 -0
- package/src/assets/fonts/ObjectSans-Thin.woff2 +0 -0
- package/src/assets/fonts/ObjectSans-ThinSlanted.woff2 +0 -0
- package/src/components/CAlert.vue +0 -73
- package/src/components/CBanner.vue +0 -47
- package/src/components/CButton.vue +0 -146
- package/src/components/CListItemAction.vue +0 -29
- package/src/components/CSegmentedButton.vue +0 -47
- package/src/components/CSegmentedButtonOption.vue +0 -42
- package/src/components/helpers/FormField.vue +0 -48
- package/src/components/helpers/SelectInput.vue +0 -115
- package/src/shims-scss.d.ts +0 -4
- package/src/shims-vuetify.d.ts +0 -4
- package/src/styles/_blitz.scss +0 -8
- package/src/styles/_object-sans.scss +0 -23
- package/tsconfig.dist.json +0 -9
- package/tsconfig.json +0 -42
- package/vue.config.js +0 -5
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<form ref="form" :data-test="dataTest" novalidate @submit.prevent="$emit('submit', $event)">
|
|
3
|
+
<slot />
|
|
4
|
+
</form>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
import scrollIntoViewIfNeeded from 'scroll-into-view-if-needed';
|
|
9
|
+
import { computed, defineComponent, onMounted, provide, ref, watch } from 'vue';
|
|
10
|
+
|
|
11
|
+
import { VALIDATIONS } from '@propelinc/citrus-ui/src/services/injections/forms';
|
|
12
|
+
import type { ValidationState } from '@propelinc/citrus-ui/src/types/CForm';
|
|
13
|
+
|
|
14
|
+
interface FormValidation {
|
|
15
|
+
id: string;
|
|
16
|
+
validate: () => boolean;
|
|
17
|
+
valid: boolean;
|
|
18
|
+
field?: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default defineComponent({
|
|
22
|
+
name: 'CForm',
|
|
23
|
+
props: {
|
|
24
|
+
dataTest: { type: String, default: 'form' },
|
|
25
|
+
// eslint-disable-next-line vue/no-unused-properties
|
|
26
|
+
value: { type: Boolean, default: false },
|
|
27
|
+
},
|
|
28
|
+
emits: ['submit', 'input'],
|
|
29
|
+
setup(_, { expose, emit }) {
|
|
30
|
+
const form = ref<HTMLFormElement | null>(null);
|
|
31
|
+
const inputs = ref<Record<string, FormValidation>>({});
|
|
32
|
+
|
|
33
|
+
provide(VALIDATIONS, {
|
|
34
|
+
register: (input: ValidationState) => {
|
|
35
|
+
// NOTE(ram): Vue collapses the ref to input.field automatically so we need to adjust types.
|
|
36
|
+
inputs.value = { ...inputs.value, [input.id]: input as unknown as FormValidation };
|
|
37
|
+
},
|
|
38
|
+
unregister: (id: string) => {
|
|
39
|
+
const filteredInputs = { ...inputs.value };
|
|
40
|
+
delete filteredInputs[id];
|
|
41
|
+
inputs.value = filteredInputs;
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const inputArray = computed(() => Object.values(inputs.value));
|
|
46
|
+
const getInvalidInputs = (startValidating?: boolean): FormValidation[] => {
|
|
47
|
+
return inputArray.value.filter((input) => {
|
|
48
|
+
return startValidating ? !input.validate() : !input.valid;
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// NOTE(mohan): I would love for this to be a computed property, but Vue 2's
|
|
53
|
+
// the reactivity system can't seem to track refs inside of refs, though it
|
|
54
|
+
// should work in Vue 3.
|
|
55
|
+
watch(getInvalidInputs, (newInvalidInputs, oldInvalidInputs) => {
|
|
56
|
+
const newValid = newInvalidInputs.length === 0;
|
|
57
|
+
const oldValid = oldInvalidInputs?.length === 0;
|
|
58
|
+
if (newValid !== oldValid) {
|
|
59
|
+
emit('input', newValid);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
onMounted(() => emit('input', getInvalidInputs().length === 0));
|
|
63
|
+
|
|
64
|
+
const validate = (): boolean => {
|
|
65
|
+
if (!form.value) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const invalidInputs = getInvalidInputs(true);
|
|
70
|
+
const firstInvalidField = invalidInputs.find((input) => input.field)?.field;
|
|
71
|
+
if (firstInvalidField) {
|
|
72
|
+
scrollIntoViewIfNeeded(firstInvalidField, {
|
|
73
|
+
behavior: 'smooth',
|
|
74
|
+
block: 'center',
|
|
75
|
+
scrollMode: 'if-needed',
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return invalidInputs.length === 0;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
expose({ validate, inputs: inputArray });
|
|
83
|
+
return { form };
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
</script>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>{{ displayText }}</div>
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import type { PropType } from 'vue';
|
|
7
|
+
import { computed, defineComponent } from 'vue';
|
|
8
|
+
|
|
9
|
+
export default defineComponent({
|
|
10
|
+
name: 'CFormFieldCounter',
|
|
11
|
+
props: {
|
|
12
|
+
/** Sets the denominator of the counter. */
|
|
13
|
+
counter: { type: Number as PropType<number | null>, default: null },
|
|
14
|
+
/** Function or number that determines the numerator of the counter. */
|
|
15
|
+
counterValue: {
|
|
16
|
+
type: [Number, Function] as PropType<number | (() => number | null)>,
|
|
17
|
+
default: null,
|
|
18
|
+
},
|
|
19
|
+
/** Input value of the parent form field */
|
|
20
|
+
value: { type: String, default: '' },
|
|
21
|
+
},
|
|
22
|
+
setup(props) {
|
|
23
|
+
const numerator = computed(() => {
|
|
24
|
+
if (typeof props.counterValue === 'function') {
|
|
25
|
+
return props.counterValue();
|
|
26
|
+
} else if (typeof props.counterValue === 'number') {
|
|
27
|
+
return props.counterValue;
|
|
28
|
+
} else {
|
|
29
|
+
return props.value.length;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const displayText = computed(() => {
|
|
34
|
+
return `${numerator.value}/${props.counter}`;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return { numerator, displayText };
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
</script>
|
|
@@ -1,73 +1,189 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
2
|
+
<CButton
|
|
3
|
+
v-bind="$attrs"
|
|
4
|
+
class="c-icon-button"
|
|
5
|
+
:class="{
|
|
6
|
+
'c-icon-button--inherit': inheritColor,
|
|
7
|
+
[`c-icon-button-size--${computedSize}`]: computedSize,
|
|
8
|
+
}"
|
|
9
|
+
:data-inherit-color="inheritColor"
|
|
10
|
+
:data-size="computedSize"
|
|
4
11
|
data-test="icon-button"
|
|
5
|
-
:
|
|
6
|
-
icon
|
|
7
|
-
:color="colorScheme.icon"
|
|
8
|
-
:ripple="false"
|
|
9
|
-
:to="to"
|
|
12
|
+
:disabled="disabled"
|
|
10
13
|
:href="href"
|
|
14
|
+
:level="level"
|
|
15
|
+
:loading="loading"
|
|
16
|
+
:size="computedSize"
|
|
11
17
|
:target="target"
|
|
12
|
-
|
|
18
|
+
:to="to"
|
|
19
|
+
:variant="computedVariant"
|
|
20
|
+
@blur="$emit('blur', $event)"
|
|
21
|
+
@click="$emit('click', $event)"
|
|
22
|
+
@focus="$emit('focus', $event)"
|
|
13
23
|
>
|
|
14
|
-
<
|
|
15
|
-
|
|
24
|
+
<div
|
|
25
|
+
role="img"
|
|
26
|
+
data-test="icon-button-a11y"
|
|
27
|
+
:aria-label="ariaLabel"
|
|
28
|
+
:aria-labelledby="ariaLabelledby"
|
|
29
|
+
>
|
|
30
|
+
<slot>
|
|
31
|
+
<font-awesome-icon
|
|
32
|
+
v-if="icon"
|
|
33
|
+
class="c-icon-button__icon"
|
|
34
|
+
:class="{ 'c-icon-button__icon--spin': spinIcon }"
|
|
35
|
+
data-test="icon-button-icon"
|
|
36
|
+
:icon="icon"
|
|
37
|
+
/>
|
|
38
|
+
</slot>
|
|
39
|
+
</div>
|
|
40
|
+
</CButton>
|
|
16
41
|
</template>
|
|
17
42
|
|
|
18
43
|
<script lang="ts">
|
|
19
|
-
import { IconDefinition } from '@fortawesome/
|
|
44
|
+
import type { IconDefinition } from '@fortawesome/pro-light-svg-icons';
|
|
20
45
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
46
|
+
import type { PropType } from 'vue';
|
|
47
|
+
import { computed, defineComponent, inject, provide } from 'vue';
|
|
48
|
+
import type { RouteLocationRaw } from 'vue-router';
|
|
23
49
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
50
|
+
import CButton from '@propelinc/citrus-ui/src/components/CButton/CButton.vue';
|
|
51
|
+
import type {
|
|
52
|
+
ButtonLevel,
|
|
53
|
+
ButtonSize,
|
|
54
|
+
ButtonVariant,
|
|
55
|
+
} from '@propelinc/citrus-ui/src/components/CButton/types';
|
|
56
|
+
import { useA11yLabelCheck } from '@propelinc/citrus-ui/src/composables/accessibility';
|
|
57
|
+
import { BOUNCE_AMOUNT } from '@propelinc/citrus-ui/src/services/injections/buttons';
|
|
58
|
+
import { INHERIT_COLOR, TERTIARY } from '@propelinc/citrus-ui/src/services/injections/icon-buttons';
|
|
28
59
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
export default defineComponent({
|
|
61
|
+
components: { FontAwesomeIcon, CButton },
|
|
62
|
+
props: {
|
|
63
|
+
/** ARIA label for the button. You must use either this or ariaLabelledby. */
|
|
64
|
+
ariaLabel: { type: String, default: undefined },
|
|
65
|
+
/** ARIA labelledby for the button. You must use either this or ariaLabel. */
|
|
66
|
+
ariaLabelledby: { type: String, default: undefined },
|
|
67
|
+
/** Controls whether button is disabled */
|
|
68
|
+
disabled: { type: Boolean, default: false },
|
|
69
|
+
/** Designates the button as an anchor and applies the href attribute */
|
|
70
|
+
href: { type: String, default: undefined },
|
|
71
|
+
/** Specifies a Font Awesome icon */
|
|
72
|
+
icon: {
|
|
73
|
+
type: [String, Array, Object] as PropType<string | string[] | IconDefinition>,
|
|
74
|
+
default: undefined,
|
|
75
|
+
},
|
|
76
|
+
/**
|
|
77
|
+
* Controls whether button uses the large variant
|
|
78
|
+
* @deprecated Use `size="large"` instead
|
|
79
|
+
*/
|
|
80
|
+
large: { type: Boolean, default: false, deprecated: true },
|
|
81
|
+
/**
|
|
82
|
+
* Controls the level of the button
|
|
83
|
+
* @default 'normal'
|
|
84
|
+
*/
|
|
85
|
+
level: { type: String as PropType<ButtonLevel>, default: 'normal' },
|
|
86
|
+
/** Controls whether button is loading */
|
|
87
|
+
loading: { type: Boolean, default: false },
|
|
88
|
+
/**
|
|
89
|
+
* Controls whether button uses the secondary variant
|
|
90
|
+
* @deprecated Use `variant="secondary"` instead
|
|
91
|
+
*/
|
|
92
|
+
secondary: { type: Boolean, default: false, deprecated: true },
|
|
93
|
+
/**
|
|
94
|
+
* Controls the size of the button
|
|
95
|
+
* @default 'medium'
|
|
96
|
+
*/
|
|
97
|
+
size: { type: String as PropType<ButtonSize>, default: 'medium' },
|
|
98
|
+
/** Spins the icon for loading states */
|
|
99
|
+
spinIcon: { type: Boolean, default: false },
|
|
100
|
+
/** Designates the target attribute. Only use with the href prop. */
|
|
101
|
+
target: { type: String, default: undefined },
|
|
102
|
+
/**
|
|
103
|
+
* Controls whether button uses the tertiary variant
|
|
104
|
+
* @deprecated Use `variant="tertiary"` instead
|
|
105
|
+
*/
|
|
106
|
+
tertiary: { type: Boolean, default: false, deprecated: true },
|
|
107
|
+
/** Controls whether button functions as a router-link */
|
|
108
|
+
to: { type: [Object, String] as PropType<RouteLocationRaw>, default: undefined },
|
|
109
|
+
/**
|
|
110
|
+
* Controls the variant of the button
|
|
111
|
+
* @default 'primary'
|
|
112
|
+
*/
|
|
113
|
+
variant: { type: String as PropType<ButtonVariant>, default: 'primary' },
|
|
114
|
+
},
|
|
115
|
+
emits: ['blur', 'click', 'focus'],
|
|
116
|
+
setup(props) {
|
|
117
|
+
useA11yLabelCheck('CIconButton', props);
|
|
118
|
+
|
|
119
|
+
const injectedTertiary = inject(TERTIARY, undefined);
|
|
120
|
+
const injectedOrPropTertiary = computed(() => injectedTertiary?.value ?? props.tertiary);
|
|
121
|
+
/**
|
|
122
|
+
* Handle the `variant` prop. `secondary` and `tertiary` are deprecated, but because we set a
|
|
123
|
+
* default `variant`, we need to give them precedence.
|
|
124
|
+
*
|
|
125
|
+
* This is slightly different than the implementation of `computedVariant` in CButton, as
|
|
126
|
+
* `CButton` does not make use of an injected `TERTIARY` value.
|
|
127
|
+
*/
|
|
128
|
+
const computedVariant = computed((): ButtonVariant => {
|
|
129
|
+
if (props.secondary) {
|
|
130
|
+
return 'secondary';
|
|
61
131
|
}
|
|
62
|
-
|
|
63
|
-
if (
|
|
64
|
-
return
|
|
65
|
-
} else if (this.dark) {
|
|
66
|
-
return { icon: 'navy' };
|
|
67
|
-
} else {
|
|
68
|
-
return { icon: 'primary' };
|
|
132
|
+
|
|
133
|
+
if (injectedOrPropTertiary.value) {
|
|
134
|
+
return 'tertiary';
|
|
69
135
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
136
|
+
|
|
137
|
+
return props.variant;
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Handle the `size` prop. `large` is deprecated, but because we set a default `size`, we need
|
|
142
|
+
* to give it precedence.
|
|
143
|
+
*/
|
|
144
|
+
const computedSize = computed((): ButtonSize => {
|
|
145
|
+
if (props.large) {
|
|
146
|
+
return 'large';
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return props.size;
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const inheritColor = inject(INHERIT_COLOR, undefined);
|
|
153
|
+
|
|
154
|
+
provide(BOUNCE_AMOUNT, 0.9);
|
|
155
|
+
|
|
156
|
+
return { computedSize, computedVariant, inheritColor };
|
|
157
|
+
},
|
|
158
|
+
});
|
|
73
159
|
</script>
|
|
160
|
+
|
|
161
|
+
<style lang="scss" scoped>
|
|
162
|
+
@import '@propelinc/citrus-ui/src/styles/core';
|
|
163
|
+
|
|
164
|
+
.c-icon-button::part(base) {
|
|
165
|
+
font-size: $font-size-icon-medium;
|
|
166
|
+
line-height: 0;
|
|
167
|
+
padding: 0;
|
|
168
|
+
width: $button-size-medium;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.c-icon-button--inherit::part(base) {
|
|
172
|
+
color: inherit;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.c-icon-button::part(label) {
|
|
176
|
+
align-items: center;
|
|
177
|
+
display: flex;
|
|
178
|
+
justify-content: center;
|
|
179
|
+
padding: 0;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.c-icon-button-size--large::part(base) {
|
|
183
|
+
width: $button-size-large;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.c-icon-button__icon--spin {
|
|
187
|
+
animation: $loader-spin-animation;
|
|
188
|
+
}
|
|
189
|
+
</style>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="c-label" :data-test="dataTest">
|
|
3
|
+
<slot name="icon">
|
|
4
|
+
<FontAwesomeIcon v-if="icon" data-test="label-icon" :icon="icon" size="xs" />
|
|
5
|
+
</slot>
|
|
6
|
+
<div class="c-label__text" data-test="label-text">
|
|
7
|
+
<slot />
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script lang="ts">
|
|
13
|
+
import type { IconDefinition } from '@fortawesome/fontawesome-svg-core';
|
|
14
|
+
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
|
15
|
+
import type { PropType } from 'vue';
|
|
16
|
+
import { defineComponent } from 'vue';
|
|
17
|
+
|
|
18
|
+
export default defineComponent({
|
|
19
|
+
components: { FontAwesomeIcon },
|
|
20
|
+
props: {
|
|
21
|
+
/** Specifies the label's icon, overwritten if icon slot is used */
|
|
22
|
+
icon: {
|
|
23
|
+
type: [Array, String, Object] as PropType<string | string[] | IconDefinition>,
|
|
24
|
+
default: null,
|
|
25
|
+
},
|
|
26
|
+
/** A custom data test string */
|
|
27
|
+
dataTest: { type: String, default: 'label' },
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<style lang="scss" scoped>
|
|
33
|
+
@import '@propelinc/citrus-ui/src/styles/core';
|
|
34
|
+
|
|
35
|
+
.c-label {
|
|
36
|
+
align-items: center;
|
|
37
|
+
background-color: $color-gray-100;
|
|
38
|
+
border-radius: 4px;
|
|
39
|
+
color: $color-text-secondary;
|
|
40
|
+
display: inline-flex;
|
|
41
|
+
font-size: $font-size-small;
|
|
42
|
+
font-weight: $font-weight-semibold;
|
|
43
|
+
gap: 4px;
|
|
44
|
+
height: 24px;
|
|
45
|
+
margin-bottom: 6px;
|
|
46
|
+
padding: 0 12px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.c-label:not(:last-child) {
|
|
50
|
+
margin-right: 8px;
|
|
51
|
+
}
|
|
52
|
+
</style>
|
|
@@ -1,69 +1,173 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
:
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
<component
|
|
3
|
+
:is="tag"
|
|
4
|
+
v-bind="allAttrs"
|
|
5
|
+
class="c-list-item d-flex flex-col justify-center w-full"
|
|
6
|
+
:class="{ 'c-list-item--is-tappable': isTappable }"
|
|
7
|
+
:href="routerDestinationOrHref"
|
|
6
8
|
:target="target"
|
|
7
|
-
|
|
8
|
-
class="c-list-item"
|
|
9
|
-
v-on="$listeners"
|
|
9
|
+
@click="navigate"
|
|
10
10
|
>
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
11
|
+
<div
|
|
12
|
+
class="d-flex flex-row gap-3 justify-between w-full p-4"
|
|
13
|
+
:class="{
|
|
14
|
+
'align-start': align === 'top',
|
|
15
|
+
'align-center': align !== 'top',
|
|
16
|
+
}"
|
|
17
|
+
>
|
|
18
|
+
<slot name="prepend">
|
|
19
|
+
<CListItemIcon v-if="icon || iconSlotHasContent" :icon="icon" :icon-color="iconColor">
|
|
20
|
+
<template #icon>
|
|
21
|
+
<slot name="icon" />
|
|
22
|
+
</template>
|
|
23
|
+
</CListItemIcon>
|
|
24
|
+
</slot>
|
|
25
|
+
|
|
26
|
+
<div class="flex-1 d-flex flex-row gap-3 justify-between align-center">
|
|
27
|
+
<CListItemContent :title="title" :label="label">
|
|
28
|
+
<template v-for="slotName in ['title', 'label', 'default']" #[slotName]>
|
|
29
|
+
<slot :name="slotName" />
|
|
30
|
+
</template>
|
|
31
|
+
</CListItemContent>
|
|
32
|
+
|
|
33
|
+
<slot name="append">
|
|
34
|
+
<div
|
|
35
|
+
v-if="renderActionSlot"
|
|
36
|
+
data-test="list-item-action"
|
|
37
|
+
class="c-list-item__append__action flex-shrink-0"
|
|
38
|
+
>
|
|
39
|
+
<slot name="action">
|
|
40
|
+
<FontAwesomeIcon
|
|
41
|
+
data-test="list-item-action-icon"
|
|
42
|
+
class="c-list-item__append__action-icon"
|
|
43
|
+
fixed-width
|
|
44
|
+
:icon="actionIcon ? actionIcon : faChevronRight"
|
|
45
|
+
/>
|
|
46
|
+
</slot>
|
|
47
|
+
</div>
|
|
48
|
+
</slot>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</component>
|
|
25
52
|
</template>
|
|
26
53
|
|
|
27
54
|
<script lang="ts">
|
|
28
|
-
import {
|
|
55
|
+
import { faChevronRight } from '@fortawesome/pro-regular-svg-icons';
|
|
29
56
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
57
|
+
import { type PropType, computed, defineComponent, mergeProps, toRefs } from 'vue';
|
|
58
|
+
import type { RouteLocationRaw } from 'vue-router';
|
|
32
59
|
|
|
33
|
-
import
|
|
34
|
-
import CListItemIcon from '
|
|
60
|
+
import CListItemContent from '@propelinc/citrus-ui/src/components/CListItemContent.vue';
|
|
61
|
+
import CListItemIcon from '@propelinc/citrus-ui/src/components/CListItemIcon.vue';
|
|
62
|
+
import { useRouterLink } from '@propelinc/citrus-ui/src/composables/router';
|
|
63
|
+
import { useSlotHasContent } from '@propelinc/citrus-ui/src/composables/slots';
|
|
64
|
+
import type { FaPropType } from '@propelinc/citrus-ui/src/types/font-awesome';
|
|
35
65
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
66
|
+
export default defineComponent({
|
|
67
|
+
components: { FontAwesomeIcon, CListItemIcon, CListItemContent },
|
|
68
|
+
props: {
|
|
69
|
+
/** The Font Awesome icon to display */
|
|
70
|
+
icon: { type: [String, Array, Object] as FaPropType, default: null },
|
|
71
|
+
/** The icon to display on the right side for clickable items, defaulting to right arrow */
|
|
72
|
+
actionIcon: { type: [String, Array, Object] as FaPropType, default: null },
|
|
73
|
+
/** Color of the icon background */
|
|
74
|
+
iconColor: { type: String, default: 'gray-100' },
|
|
75
|
+
/** Emphasized text above body text. Overridden by the default slot. */
|
|
76
|
+
title: { type: String, default: undefined },
|
|
77
|
+
/** Plain body copy. Overridden by the default slot. */
|
|
78
|
+
label: { type: String, default: undefined },
|
|
79
|
+
/** Router location for links within the app */
|
|
80
|
+
to: { type: [String, Object] as PropType<RouteLocationRaw>, default: undefined },
|
|
81
|
+
/** HTML target attribute for links to outside the app */
|
|
82
|
+
target: { type: String, default: undefined },
|
|
83
|
+
/** Location for links to outside the app */
|
|
84
|
+
href: { type: String, default: undefined },
|
|
85
|
+
/** Changes alignment of icon and action icon */
|
|
86
|
+
align: { type: String as PropType<'top' | 'center'>, default: 'center' },
|
|
87
|
+
/** Overrides default behavior to display a chevron in the action slot */
|
|
88
|
+
hideAction: { type: Boolean, default: false },
|
|
89
|
+
},
|
|
90
|
+
setup(props, { attrs }) {
|
|
91
|
+
const { to } = toRefs(props);
|
|
92
|
+
const { href: routerDestination, navigate } = useRouterLink(to);
|
|
93
|
+
const routerDestinationOrHref = computed(() => routerDestination.value ?? props.href);
|
|
42
94
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
95
|
+
const isLink = computed(() => !!routerDestinationOrHref.value);
|
|
96
|
+
const isButton = computed(() => !!attrs.onClick);
|
|
97
|
+
const isTappable = computed(() => isLink.value || isButton.value);
|
|
46
98
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
99
|
+
const tag = computed(() => {
|
|
100
|
+
if (isLink.value) {
|
|
101
|
+
return 'a';
|
|
102
|
+
} else if (isButton.value) {
|
|
103
|
+
return 'button';
|
|
104
|
+
} else {
|
|
105
|
+
return 'div';
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
const tagAttrs = computed(() => {
|
|
109
|
+
if (isButton.value) {
|
|
110
|
+
return { type: 'button' };
|
|
111
|
+
}
|
|
112
|
+
return {};
|
|
113
|
+
});
|
|
50
114
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
115
|
+
const actionSlotHasContent = useSlotHasContent('action');
|
|
116
|
+
|
|
117
|
+
const renderActionSlot = computed(() => {
|
|
118
|
+
if (props.hideAction) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return isTappable.value || actionSlotHasContent.value;
|
|
123
|
+
});
|
|
124
|
+
const iconSlotHasContent = useSlotHasContent('icon');
|
|
125
|
+
|
|
126
|
+
const allAttrs = computed(() => mergeProps(attrs, tagAttrs.value));
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
allAttrs,
|
|
130
|
+
faChevronRight,
|
|
131
|
+
iconSlotHasContent,
|
|
132
|
+
isTappable,
|
|
133
|
+
navigate,
|
|
134
|
+
renderActionSlot,
|
|
135
|
+
routerDestinationOrHref,
|
|
136
|
+
tag,
|
|
137
|
+
};
|
|
138
|
+
},
|
|
139
|
+
});
|
|
55
140
|
</script>
|
|
56
141
|
|
|
57
142
|
<style lang="scss" scoped>
|
|
58
|
-
@import '
|
|
143
|
+
@import '@propelinc/citrus-ui/src/styles/core';
|
|
144
|
+
@import '@propelinc/citrus-ui/src/styles/utils/a11y';
|
|
59
145
|
|
|
60
146
|
.c-list-item {
|
|
147
|
+
background: transparent;
|
|
148
|
+
color: $color-text-primary;
|
|
149
|
+
min-height: 64px;
|
|
150
|
+
outline: none;
|
|
151
|
+
text-align: left;
|
|
152
|
+
text-decoration: none;
|
|
153
|
+
|
|
61
154
|
& + & {
|
|
62
155
|
border-top: $border;
|
|
63
156
|
}
|
|
157
|
+
|
|
158
|
+
&:focus-visible {
|
|
159
|
+
@include focus-ring;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.c-list-item--is-tappable {
|
|
164
|
+
appearance: none; // Needed to avoid browser default styles for buttons
|
|
165
|
+
border: none; // Needed to avoid browser default styles for buttons
|
|
166
|
+
cursor: pointer;
|
|
64
167
|
}
|
|
65
168
|
|
|
66
|
-
.c-list-
|
|
67
|
-
|
|
169
|
+
.c-list-item__append__action-icon {
|
|
170
|
+
color: $color-text-secondary;
|
|
171
|
+
font-size: $font-size-medium;
|
|
68
172
|
}
|
|
69
173
|
</style>
|