@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,127 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="c-third-party-logo" :style="{ '--size': SIZE_TO_VALUE[size] }">
|
|
3
|
+
<img
|
|
4
|
+
v-if="imageState === 'loaded' || imageState === 'loading'"
|
|
5
|
+
class="c-third-party-logo__img transition-opacity"
|
|
6
|
+
:class="{ 'opacity-0': imageState === 'loading' }"
|
|
7
|
+
:src="src"
|
|
8
|
+
:alt="alt"
|
|
9
|
+
@error="handleImageError"
|
|
10
|
+
@load="handleImageLoad"
|
|
11
|
+
/>
|
|
12
|
+
<c-squared-icon
|
|
13
|
+
class="transition-opacity"
|
|
14
|
+
:class="{ 'opacity-0': imageState === 'loaded' }"
|
|
15
|
+
:icon="icon"
|
|
16
|
+
:color="color"
|
|
17
|
+
:large="size === 'large'"
|
|
18
|
+
/>
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script lang="ts">
|
|
23
|
+
import { type PropType, defineComponent, ref } from 'vue';
|
|
24
|
+
|
|
25
|
+
import CSquaredIcon from '@propelinc/citrus-ui/src/components/CSquaredIcon.vue';
|
|
26
|
+
|
|
27
|
+
type Size = 'medium' | 'large';
|
|
28
|
+
|
|
29
|
+
type ImageState = 'loading' | 'loaded' | 'failed';
|
|
30
|
+
|
|
31
|
+
const SIZES = new Set<Size>(['medium', 'large']);
|
|
32
|
+
|
|
33
|
+
const SIZE_TO_VALUE: Record<Size, `${number}px`> = {
|
|
34
|
+
medium: '40px',
|
|
35
|
+
large: '56px',
|
|
36
|
+
};
|
|
37
|
+
|
|
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');
|
|
79
|
+
|
|
80
|
+
const handleImageError = (): void => {
|
|
81
|
+
imageState.value = 'failed';
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const handleImageLoad = (): void => {
|
|
85
|
+
imageState.value = 'loaded';
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
handleImageError,
|
|
90
|
+
handleImageLoad,
|
|
91
|
+
imageState,
|
|
92
|
+
SIZE_TO_VALUE,
|
|
93
|
+
};
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
</script>
|
|
97
|
+
|
|
98
|
+
<style scoped>
|
|
99
|
+
.c-third-party-logo {
|
|
100
|
+
border-radius: 8px;
|
|
101
|
+
height: var(--size);
|
|
102
|
+
isolation: isolate;
|
|
103
|
+
object-fit: contain;
|
|
104
|
+
overflow: hidden;
|
|
105
|
+
position: relative;
|
|
106
|
+
width: var(--size);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.c-third-party-logo__img {
|
|
110
|
+
display: block;
|
|
111
|
+
height: 100%;
|
|
112
|
+
left: 0;
|
|
113
|
+
object-fit: contain;
|
|
114
|
+
position: absolute;
|
|
115
|
+
top: 0;
|
|
116
|
+
width: 100%;
|
|
117
|
+
z-index: 1;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.opacity-0 {
|
|
121
|
+
opacity: 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.transition-opacity {
|
|
125
|
+
transition: opacity 0.1s ease-in-out;
|
|
126
|
+
}
|
|
127
|
+
</style>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<time v-bind="$attrs" :datetime="isoString">{{ timeago }}</time>
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script setup lang="ts">
|
|
6
|
+
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
|
7
|
+
|
|
8
|
+
const props = withDefaults(
|
|
9
|
+
defineProps<{
|
|
10
|
+
datetime: string | number | Date;
|
|
11
|
+
converter: (datetime: string | number | Date, locale: string, count?: number) => string;
|
|
12
|
+
locale?: string;
|
|
13
|
+
autoUpdate?: number | false;
|
|
14
|
+
}>(),
|
|
15
|
+
{
|
|
16
|
+
locale: 'en',
|
|
17
|
+
autoUpdate: 60,
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
// NOTE(ram): Used to trigger reactivity in the computed property for auto updates.
|
|
22
|
+
const counter = ref(0);
|
|
23
|
+
let timer: number | null = null;
|
|
24
|
+
|
|
25
|
+
const isoString = computed((): string => new Date(props.datetime).toISOString());
|
|
26
|
+
const timeago = computed((): string =>
|
|
27
|
+
props.converter(props.datetime, props.locale, counter.value)
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const startAutoUpdate = (): void => {
|
|
31
|
+
if (props.autoUpdate) {
|
|
32
|
+
// TODO(ram): This is a workaround to make sure the timer is of type number.
|
|
33
|
+
// The type of setInterval is NodeJS.Timeout in Node.js and number in the browser.
|
|
34
|
+
// This is causing issues in apps/healthcare-app.
|
|
35
|
+
timer = setInterval(() => counter.value++, props.autoUpdate * 1000) as unknown as number;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const stopAutoUpdate = (): void => {
|
|
40
|
+
if (timer) {
|
|
41
|
+
clearInterval(timer);
|
|
42
|
+
timer = null;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
watch(
|
|
47
|
+
() => props.autoUpdate,
|
|
48
|
+
(value: number | false) => {
|
|
49
|
+
stopAutoUpdate();
|
|
50
|
+
if (value) {
|
|
51
|
+
startAutoUpdate();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
onMounted(() => {
|
|
57
|
+
startAutoUpdate();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
onBeforeUnmount(() => {
|
|
61
|
+
stopAutoUpdate();
|
|
62
|
+
});
|
|
63
|
+
</script>
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
<!-- eslint-disable vue/no-deprecated-slot-attribute -->
|
|
2
|
+
<template>
|
|
3
|
+
<sl-alert
|
|
4
|
+
ref="alert"
|
|
5
|
+
class="c-toast"
|
|
6
|
+
:data-test="dataTest"
|
|
7
|
+
:data-open="isOpen || undefined"
|
|
8
|
+
:data-variant="computedVariant"
|
|
9
|
+
:class="`c-toast--${computedVariant}`"
|
|
10
|
+
:duration="duration"
|
|
11
|
+
:style="{ '--toast-base-transform': transform }"
|
|
12
|
+
@sl-hide="onHide"
|
|
13
|
+
@sl-after-hide="resetAnimation"
|
|
14
|
+
>
|
|
15
|
+
<div class="flex-grow-1 d-flex flex-row align-center gap-4">
|
|
16
|
+
<slot name="icon">
|
|
17
|
+
<FontAwesomeIcon data-test="toast-icon" fixed-width class="c-toast__icon" :icon="icon" />
|
|
18
|
+
</slot>
|
|
19
|
+
|
|
20
|
+
<div class="c-toast__message flex-grow-1" data-test="toast-message">
|
|
21
|
+
<slot>{{ message }}</slot>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<CIconButton
|
|
26
|
+
data-test="toast-dismiss"
|
|
27
|
+
variant="tertiary"
|
|
28
|
+
:aria-label="$t('Close')"
|
|
29
|
+
:icon="faXmark"
|
|
30
|
+
@click="close"
|
|
31
|
+
/>
|
|
32
|
+
</sl-alert>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<script lang="ts">
|
|
36
|
+
import {
|
|
37
|
+
faCircleCheck,
|
|
38
|
+
faCircleExclamation,
|
|
39
|
+
faInfoCircle,
|
|
40
|
+
faXmark,
|
|
41
|
+
} from '@fortawesome/pro-regular-svg-icons';
|
|
42
|
+
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
|
43
|
+
import '@shoelace-style/shoelace/dist/components/alert/alert.js';
|
|
44
|
+
import type SlAlert from '@shoelace-style/shoelace/dist/components/alert/alert.js';
|
|
45
|
+
import type { ElementAnimation } from '@shoelace-style/shoelace/dist/utilities/animation-registry';
|
|
46
|
+
import type { PropType } from 'vue';
|
|
47
|
+
import { computed, defineComponent, onBeforeUnmount, ref, watchEffect } from 'vue';
|
|
48
|
+
|
|
49
|
+
import CIconButton from '@propelinc/citrus-ui/src/components/CIconButton.vue';
|
|
50
|
+
import { useShoelaceAnimation } from '@propelinc/citrus-ui/src/composables/animation';
|
|
51
|
+
import { useShoelaceShadowParts } from '@propelinc/citrus-ui/src/composables/elements';
|
|
52
|
+
import { useDragListener } from '@propelinc/citrus-ui/src/composables/gestures';
|
|
53
|
+
import { slideLeft, slideRight } from '@propelinc/citrus-ui/src/services/animation';
|
|
54
|
+
import type { ToastVariant } from '@propelinc/citrus-ui/src/types';
|
|
55
|
+
|
|
56
|
+
/** Default duration for the toast to be visible. */
|
|
57
|
+
const DEFAULT_DURATION = 5000;
|
|
58
|
+
/** The minimum distance, in pixels, the user has to drag left or right for
|
|
59
|
+
* it to be considered a successful swipe. */
|
|
60
|
+
const MIN_SWIPE_DISTANCE = 150;
|
|
61
|
+
/** The minimum speed, in pixels per millisecond, the user has to swipe for
|
|
62
|
+
* it to be considered a successful swipe. */
|
|
63
|
+
const MIN_SWIPE_VELOCITY = 0.75;
|
|
64
|
+
|
|
65
|
+
export default defineComponent({
|
|
66
|
+
name: 'CToast',
|
|
67
|
+
components: { FontAwesomeIcon, CIconButton },
|
|
68
|
+
props: {
|
|
69
|
+
/** Show/hides the toast */
|
|
70
|
+
value: { type: Boolean, default: false },
|
|
71
|
+
/** The message rendered in the toast, using the default slot overrides this value */
|
|
72
|
+
message: { type: String, default: '' },
|
|
73
|
+
/**
|
|
74
|
+
* @deprecated use `variant` prop instead
|
|
75
|
+
* Renders the error state
|
|
76
|
+
*/
|
|
77
|
+
error: { type: Boolean, default: false, deprecated: true },
|
|
78
|
+
/**
|
|
79
|
+
* @deprecated use `variant` prop instead
|
|
80
|
+
* Renders the success state
|
|
81
|
+
*/
|
|
82
|
+
success: { type: Boolean, default: false, deprecated: true },
|
|
83
|
+
/**
|
|
84
|
+
* @deprecated use `variant` prop instead
|
|
85
|
+
* Renders the warning state
|
|
86
|
+
*/
|
|
87
|
+
warning: { type: Boolean, default: false, deprecated: true },
|
|
88
|
+
/** The variant of the toast */
|
|
89
|
+
variant: { type: String as PropType<ToastVariant>, default: 'info' },
|
|
90
|
+
/** Controls (overrides) whether the toast is dismissible. */
|
|
91
|
+
dismissible: { type: Boolean as PropType<boolean | undefined>, default: undefined },
|
|
92
|
+
/** The data-test attribute for the toast. */
|
|
93
|
+
dataTest: { type: String, default: 'toast' },
|
|
94
|
+
},
|
|
95
|
+
emits: ['hide', 'input'],
|
|
96
|
+
setup(props, { emit }) {
|
|
97
|
+
const alert = ref<SlAlert | null>(null);
|
|
98
|
+
const isOpen = ref(props.value);
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Handle merging the deprecated `error`, `success`, and `warning` props
|
|
102
|
+
* with the `variant` prop.
|
|
103
|
+
*/
|
|
104
|
+
const computedVariant = computed(() => {
|
|
105
|
+
if (props.error) {
|
|
106
|
+
return 'error';
|
|
107
|
+
} else if (props.success) {
|
|
108
|
+
return 'success';
|
|
109
|
+
} else if (props.warning) {
|
|
110
|
+
return 'warning';
|
|
111
|
+
}
|
|
112
|
+
return props.variant;
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const show = async (): Promise<void> => {
|
|
116
|
+
isOpen.value = true;
|
|
117
|
+
alert.value?.toast();
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const onHide = (): void => {
|
|
121
|
+
isOpen.value = false;
|
|
122
|
+
emit('input', false);
|
|
123
|
+
emit('hide');
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const close = async (): Promise<void> => {
|
|
127
|
+
alert.value?.hide();
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// If our value changes, show or hide the toast respectively
|
|
131
|
+
watchEffect(() => (props.value ? show() : close()));
|
|
132
|
+
|
|
133
|
+
// Close the toast when the component is unmounted
|
|
134
|
+
onBeforeUnmount(() => close());
|
|
135
|
+
|
|
136
|
+
const shouldAutoDismiss = computed(() => {
|
|
137
|
+
if (props.dismissible !== undefined) {
|
|
138
|
+
return !props.dismissible;
|
|
139
|
+
}
|
|
140
|
+
return computedVariant.value === 'success';
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const duration = computed(() => {
|
|
144
|
+
return shouldAutoDismiss.value ? DEFAULT_DURATION : Infinity;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const icon = computed(() => {
|
|
148
|
+
if (computedVariant.value === 'error') {
|
|
149
|
+
return faCircleExclamation;
|
|
150
|
+
} else if (computedVariant.value === 'success') {
|
|
151
|
+
return faCircleCheck;
|
|
152
|
+
} else {
|
|
153
|
+
return faInfoCircle;
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const alertHideAnimation = ref<ElementAnimation | 'default'>('default');
|
|
158
|
+
useShoelaceAnimation(alert, 'alert.hide', alertHideAnimation);
|
|
159
|
+
|
|
160
|
+
const resetAnimation = (): void => {
|
|
161
|
+
alertHideAnimation.value = 'default';
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const { base } = useShoelaceShadowParts(alert, ['base']);
|
|
165
|
+
const { dragDistance } = useDragListener(base, {
|
|
166
|
+
onDragEnd: async ({ distance, velocity }) => {
|
|
167
|
+
const absoluteDistance = Math.abs(distance.x);
|
|
168
|
+
const absoluteVelocity = Math.abs(velocity.x);
|
|
169
|
+
const shouldDismiss =
|
|
170
|
+
absoluteDistance > MIN_SWIPE_DISTANCE || absoluteVelocity > MIN_SWIPE_VELOCITY;
|
|
171
|
+
const dismissStart = absoluteDistance / base.value!.clientWidth;
|
|
172
|
+
if (shouldDismiss) {
|
|
173
|
+
const remainingDismissDistance = Math.max(base.value!.clientWidth - absoluteDistance, 0);
|
|
174
|
+
const dismissDuration =
|
|
175
|
+
remainingDismissDistance / Math.max(absoluteVelocity, MIN_SWIPE_VELOCITY);
|
|
176
|
+
const slideDirection = velocity.x > 0 ? slideRight : slideLeft;
|
|
177
|
+
alertHideAnimation.value = slideDirection({
|
|
178
|
+
start: dismissStart,
|
|
179
|
+
duration: dismissDuration,
|
|
180
|
+
end: 1.1,
|
|
181
|
+
});
|
|
182
|
+
close();
|
|
183
|
+
} else {
|
|
184
|
+
const slideDirection = velocity.x > 0 ? slideRight : slideLeft;
|
|
185
|
+
const slideBack = slideDirection({ start: dismissStart, end: 0 });
|
|
186
|
+
base.value?.animate(slideBack.keyframes, slideBack.options);
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const transform = computed(() => {
|
|
192
|
+
return `translateX(${dragDistance.value?.x ?? 0}px)`;
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
duration,
|
|
197
|
+
alert,
|
|
198
|
+
icon,
|
|
199
|
+
faXmark,
|
|
200
|
+
isOpen,
|
|
201
|
+
computedVariant,
|
|
202
|
+
close,
|
|
203
|
+
onHide,
|
|
204
|
+
transform,
|
|
205
|
+
resetAnimation,
|
|
206
|
+
};
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
</script>
|
|
210
|
+
|
|
211
|
+
<style lang="scss" scoped>
|
|
212
|
+
@import '@propelinc/citrus-ui/src/styles/core';
|
|
213
|
+
|
|
214
|
+
.c-toast {
|
|
215
|
+
margin: 16px;
|
|
216
|
+
|
|
217
|
+
&::part(base) {
|
|
218
|
+
background-color: $color-blue-200;
|
|
219
|
+
border: none;
|
|
220
|
+
border-radius: $border-radius-next;
|
|
221
|
+
box-shadow: $box-shadow-toast;
|
|
222
|
+
color: $color-black;
|
|
223
|
+
transform: var(--toast-base-transform);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
&::part(message) {
|
|
227
|
+
@include body;
|
|
228
|
+
|
|
229
|
+
align-items: center;
|
|
230
|
+
display: flex;
|
|
231
|
+
padding: 16px 0 16px 16px;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
&::part(icon) {
|
|
235
|
+
padding-left: 12px;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.c-toast--error::part(base) {
|
|
240
|
+
background-color: $color-red-200;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.c-toast--success::part(base) {
|
|
244
|
+
background-color: $color-green-200;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.c-toast--warning::part(base) {
|
|
248
|
+
background-color: $color-gold-200;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.c-toast__message {
|
|
252
|
+
flex: auto;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.c-toast__icon {
|
|
256
|
+
color: $color-black;
|
|
257
|
+
font-size: $font-size-icon-medium;
|
|
258
|
+
}
|
|
259
|
+
</style>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<CToast
|
|
3
|
+
v-for="toast in toasts"
|
|
4
|
+
:key="toast.id"
|
|
5
|
+
:value="true"
|
|
6
|
+
:data-test="toast.dataTest || 'c-toast'"
|
|
7
|
+
:message="toast.message"
|
|
8
|
+
:variant="toast.variant"
|
|
9
|
+
@hide="dismissToast(toast.id)"
|
|
10
|
+
/>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script lang="ts">
|
|
14
|
+
import { defineComponent } from 'vue';
|
|
15
|
+
|
|
16
|
+
import CToast from '@propelinc/citrus-ui/src/components/CToast.vue';
|
|
17
|
+
|
|
18
|
+
import { useToast } from '../composables/toast';
|
|
19
|
+
|
|
20
|
+
export default defineComponent({
|
|
21
|
+
name: 'CToastsList',
|
|
22
|
+
components: { CToast },
|
|
23
|
+
setup() {
|
|
24
|
+
const { toasts, dismissToast } = useToast();
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
toasts,
|
|
28
|
+
dismissToast,
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
</script>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
v-if="!isHidden"
|
|
4
|
+
:data-test="dataTest"
|
|
5
|
+
class="c-validation-message"
|
|
6
|
+
:class="{ 'c-validation-message--invalid': !!validationMessage }"
|
|
7
|
+
>
|
|
8
|
+
<CFadeTransition aria-live="polite">
|
|
9
|
+
<span v-if="validationMessage" key="message" role="alert" :data-test="`${dataTest}-text`">
|
|
10
|
+
{{ validationMessage }}
|
|
11
|
+
</span>
|
|
12
|
+
<!-- NOTE(mohan): Always keep an empty span to make sure the flex layout is stable. -->
|
|
13
|
+
<span v-else key="empty">
|
|
14
|
+
<slot />
|
|
15
|
+
</span>
|
|
16
|
+
</CFadeTransition>
|
|
17
|
+
|
|
18
|
+
<slot name="append" />
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script lang="ts">
|
|
23
|
+
import type { PropType } from 'vue';
|
|
24
|
+
import { computed, defineComponent } from 'vue';
|
|
25
|
+
|
|
26
|
+
import CFadeTransition from '@propelinc/citrus-ui/src/components/CFadeTransition.vue';
|
|
27
|
+
import { useSlotHasContent } from '@propelinc/citrus-ui/src/composables/slots';
|
|
28
|
+
|
|
29
|
+
export default defineComponent({
|
|
30
|
+
name: 'CValidationMessage',
|
|
31
|
+
components: { CFadeTransition },
|
|
32
|
+
props: {
|
|
33
|
+
/** Test selector for the component */
|
|
34
|
+
dataTest: { type: String, default: 'validation-message' },
|
|
35
|
+
/** Validation message to display */
|
|
36
|
+
validationMessage: { type: String as PropType<string | null>, default: null },
|
|
37
|
+
/** Whether to hide the component. If "auto", the component will automatically
|
|
38
|
+
* hide itself if it has no content. */
|
|
39
|
+
hide: { type: [Boolean, String] as PropType<boolean | 'auto'>, default: false },
|
|
40
|
+
},
|
|
41
|
+
setup(props) {
|
|
42
|
+
const appendSlotHasContent = useSlotHasContent('append');
|
|
43
|
+
const isHidden = computed(() => {
|
|
44
|
+
if (props.hide === 'auto') {
|
|
45
|
+
return !props.validationMessage && !appendSlotHasContent.value;
|
|
46
|
+
} else {
|
|
47
|
+
return props.hide;
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return { isHidden };
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
</script>
|
|
55
|
+
|
|
56
|
+
<style lang="scss" scoped>
|
|
57
|
+
@import '@propelinc/citrus-ui/src/styles/core';
|
|
58
|
+
|
|
59
|
+
.c-validation-message {
|
|
60
|
+
@include caption;
|
|
61
|
+
|
|
62
|
+
display: flex;
|
|
63
|
+
justify-content: space-between;
|
|
64
|
+
margin-bottom: 8px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.c-validation-message--invalid {
|
|
68
|
+
color: $color-status-error;
|
|
69
|
+
}
|
|
70
|
+
</style>
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<CMaskedTextField
|
|
3
|
+
v-bind="$attrs"
|
|
4
|
+
:id="id"
|
|
5
|
+
ref="field"
|
|
6
|
+
:data-test="dataTest"
|
|
7
|
+
:value="value"
|
|
8
|
+
name="zip"
|
|
9
|
+
:label="label || $t('ZIP Code')"
|
|
10
|
+
mask="#####"
|
|
11
|
+
:placeholder="placeholder || '12345'"
|
|
12
|
+
:minlength="5"
|
|
13
|
+
:maxlength="5"
|
|
14
|
+
type="tel"
|
|
15
|
+
autocorrect="off"
|
|
16
|
+
autocapitalize="none"
|
|
17
|
+
:rules="computedRules"
|
|
18
|
+
@input="$emit('input', $event)"
|
|
19
|
+
@focus="$emit('focus', $event)"
|
|
20
|
+
@blur="$emit('blur', $event)"
|
|
21
|
+
@change="$emit('change', $event)"
|
|
22
|
+
>
|
|
23
|
+
<template #label>
|
|
24
|
+
<slot name="label" />
|
|
25
|
+
</template>
|
|
26
|
+
<template #message>
|
|
27
|
+
<slot name="message" />
|
|
28
|
+
</template>
|
|
29
|
+
</CMaskedTextField>
|
|
30
|
+
</template>
|
|
31
|
+
|
|
32
|
+
<script lang="ts">
|
|
33
|
+
import { type PropType, computed, defineComponent } from 'vue';
|
|
34
|
+
|
|
35
|
+
import CMaskedTextField from '@propelinc/citrus-ui/src/components/CMaskedTextField.vue';
|
|
36
|
+
import { useFocusableField } from '@propelinc/citrus-ui/src/composables/fields';
|
|
37
|
+
import { useTranslation } from '@propelinc/citrus-ui/src/composables/i18n';
|
|
38
|
+
import type { Rules } from '@propelinc/citrus-ui/src/composables/validations';
|
|
39
|
+
import { minLengthRule } from '@propelinc/shared-utils';
|
|
40
|
+
|
|
41
|
+
export default defineComponent({
|
|
42
|
+
name: 'CZipcodeField',
|
|
43
|
+
components: { CMaskedTextField },
|
|
44
|
+
props: {
|
|
45
|
+
dataTest: { type: String, default: 'zipcode-field' },
|
|
46
|
+
id: { type: String, default: undefined },
|
|
47
|
+
label: { type: String as PropType<string | null>, default: null },
|
|
48
|
+
placeholder: { type: String as PropType<string | null>, default: null },
|
|
49
|
+
rules: { type: Array as PropType<Rules<string>>, default: () => [] },
|
|
50
|
+
value: { type: String, default: '' },
|
|
51
|
+
},
|
|
52
|
+
emits: ['input', 'focus', 'blur', 'change'],
|
|
53
|
+
setup(props, { expose }) {
|
|
54
|
+
const { t } = useTranslation();
|
|
55
|
+
|
|
56
|
+
const defaultRules = [
|
|
57
|
+
minLengthRule(5, t('Please enter a valid {inputLabel}', { inputLabel: t('ZIP Code') })),
|
|
58
|
+
];
|
|
59
|
+
const computedRules = computed(() => [...defaultRules, ...props.rules]);
|
|
60
|
+
|
|
61
|
+
const { field } = useFocusableField(expose);
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
computedRules,
|
|
65
|
+
field,
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
</script>
|