@propelinc/citrus-ui 0.6.0 → 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 +54 -60
- 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 +60 -45
- 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 +248 -87
- package/src/styles/_form-fields.scss +68 -15
- 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 +40 -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 +27 -15
- 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/.stylelintrc.js +0 -3
- package/babel.config.js +0 -3
- package/dist/citrus-ui.common.js +0 -43434
- package/dist/citrus-ui.common.js.map +0 -1
- package/dist/citrus-ui.css +0 -1
- package/dist/citrus-ui.umd.js +0 -43444
- 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/project.json +0 -69
- 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 -78
- 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/storybook-static/0.799c368cbe88266827ba.manager.bundle.js +0 -1
- package/storybook-static/0.a9f0a9ad.iframe.bundle.js +0 -3
- package/storybook-static/0.a9f0a9ad.iframe.bundle.js.LICENSE.txt +0 -8
- package/storybook-static/0.a9f0a9ad.iframe.bundle.js.map +0 -1
- package/storybook-static/1.0438fd8d.iframe.bundle.js +0 -3
- package/storybook-static/1.0438fd8d.iframe.bundle.js.LICENSE.txt +0 -17
- package/storybook-static/1.0438fd8d.iframe.bundle.js.map +0 -1
- package/storybook-static/1.9ebd2fb519f6726108de.manager.bundle.js +0 -1
- package/storybook-static/10.348d8814.iframe.bundle.js +0 -3
- package/storybook-static/10.348d8814.iframe.bundle.js.LICENSE.txt +0 -30
- package/storybook-static/10.348d8814.iframe.bundle.js.map +0 -1
- package/storybook-static/10.a85ea1a67689be8e19ff.manager.bundle.js +0 -1
- package/storybook-static/11.f4e922583ae35da460f3.manager.bundle.js +0 -2
- package/storybook-static/11.f4e922583ae35da460f3.manager.bundle.js.LICENSE.txt +0 -12
- package/storybook-static/12.1415460941f0bdcb8fa8.manager.bundle.js +0 -1
- package/storybook-static/2.75a17459.iframe.bundle.js +0 -3
- package/storybook-static/2.75a17459.iframe.bundle.js.LICENSE.txt +0 -26
- package/storybook-static/2.75a17459.iframe.bundle.js.map +0 -1
- package/storybook-static/5.f459d151315e6780c20f.manager.bundle.js +0 -2
- package/storybook-static/5.f459d151315e6780c20f.manager.bundle.js.LICENSE.txt +0 -8
- package/storybook-static/6.3bd64d820f3745f262ff.manager.bundle.js +0 -1
- package/storybook-static/6.ce8d99b4.iframe.bundle.js +0 -1
- package/storybook-static/7.3d04765dbf3f1dcd706c.manager.bundle.js +0 -1
- package/storybook-static/7.6633a922.iframe.bundle.js +0 -3
- package/storybook-static/7.6633a922.iframe.bundle.js.LICENSE.txt +0 -12
- package/storybook-static/7.6633a922.iframe.bundle.js.map +0 -1
- package/storybook-static/8.b541eadfcb9164835dfc.manager.bundle.js +0 -1
- package/storybook-static/8.fc5e1ebf.iframe.bundle.js +0 -1
- package/storybook-static/9.411ac8e451bbb10926c7.manager.bundle.js +0 -1
- package/storybook-static/9.724ac3ed.iframe.bundle.js +0 -3
- package/storybook-static/9.724ac3ed.iframe.bundle.js.LICENSE.txt +0 -15
- package/storybook-static/9.724ac3ed.iframe.bundle.js.map +0 -1
- package/storybook-static/css/main.95216119.css +0 -1
- package/storybook-static/css/vendors~main.02dc89bf.css +0 -1
- package/storybook-static/favicon.ico +0 -0
- package/storybook-static/fonts/Blitz-Script.85ed9abe.woff2 +0 -0
- package/storybook-static/fonts/ObjectSans-Bold.5492f3d5.woff2 +0 -0
- package/storybook-static/fonts/ObjectSans-BoldSlanted.29e2a87e.woff2 +0 -0
- package/storybook-static/fonts/ObjectSans-Heavy.d0b2f035.woff2 +0 -0
- package/storybook-static/fonts/ObjectSans-HeavySlanted.45e9c063.woff2 +0 -0
- package/storybook-static/fonts/ObjectSans-Light.f885dec3.woff2 +0 -0
- package/storybook-static/fonts/ObjectSans-LightSlanted.b8eb7c12.woff2 +0 -0
- package/storybook-static/fonts/ObjectSans-Regular.e4ea0b90.woff2 +0 -0
- package/storybook-static/fonts/ObjectSans-Slanted.57a90be9.woff2 +0 -0
- package/storybook-static/fonts/ObjectSans-Thin.86d44227.woff2 +0 -0
- package/storybook-static/fonts/ObjectSans-ThinSlanted.20342160.woff2 +0 -0
- package/storybook-static/iframe.html +0 -348
- package/storybook-static/index.html +0 -51
- package/storybook-static/main.7b4aec9c4352d4bb535b.manager.bundle.js +0 -1
- package/storybook-static/main.9e8c64c7.iframe.bundle.js +0 -1
- package/storybook-static/runtime~main.91a0c7330ab317d35c4a.manager.bundle.js +0 -1
- package/storybook-static/runtime~main.e4da100f.iframe.bundle.js +0 -1
- package/storybook-static/vendors~main.6660eda6.iframe.bundle.js +0 -7
- package/storybook-static/vendors~main.6660eda6.iframe.bundle.js.LICENSE.txt +0 -80
- package/storybook-static/vendors~main.6660eda6.iframe.bundle.js.map +0 -1
- package/storybook-static/vendors~main.f7f16cebbf3aa96a4f89.manager.bundle.js +0 -2
- package/storybook-static/vendors~main.f7f16cebbf3aa96a4f89.manager.bundle.js.LICENSE.txt +0 -110
- package/tsconfig.dist.json +0 -7
- package/tsconfig.json +0 -24
- package/vue.config.js +0 -5
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
<!-- eslint-disable vue/no-deprecated-slot-attribute -->
|
|
2
|
+
<template>
|
|
3
|
+
<!--
|
|
4
|
+
For no-header prop below:
|
|
5
|
+
See https://vuejs.org/guide/extras/web-components#passing-dom-properties
|
|
6
|
+
Shoelace does not seem to properly specify the no-header prop; otherwise
|
|
7
|
+
a false value resolves to no-header="false" which is actually truthy
|
|
8
|
+
-->
|
|
9
|
+
<sl-drawer
|
|
10
|
+
ref="sheet"
|
|
11
|
+
class="c-bottom-sheet"
|
|
12
|
+
:class="{
|
|
13
|
+
'c-bottom-sheet--hide-overlay': !overlay,
|
|
14
|
+
'c-bottom-sheet--hide-title': hideTitle,
|
|
15
|
+
'c-bottom-sheet--with-footer': footerHasContent,
|
|
16
|
+
'c-bottom-sheet--with-dismiss': isDismissVisible,
|
|
17
|
+
}"
|
|
18
|
+
:style="{
|
|
19
|
+
'--panel-transform': panelTransform,
|
|
20
|
+
'--background-color-hex-code': backgroundCssColor,
|
|
21
|
+
'--overlay-opacity': overlayOpacity,
|
|
22
|
+
'--size': fixedSize || undefined,
|
|
23
|
+
}"
|
|
24
|
+
:data-test="getDataTestAttr()"
|
|
25
|
+
:open="animatedValue || undefined"
|
|
26
|
+
:no-header="hideTitle || undefined"
|
|
27
|
+
:label="ariaLabel"
|
|
28
|
+
placement="bottom"
|
|
29
|
+
:contained="contained || undefined"
|
|
30
|
+
:divided="divided || undefined"
|
|
31
|
+
@sl-request-close="onRequestClose"
|
|
32
|
+
@sl-hide="handleDismiss"
|
|
33
|
+
@sl-after-hide="onAfterClose"
|
|
34
|
+
@sl-after-show="$emit('opened')"
|
|
35
|
+
>
|
|
36
|
+
<div slot="label" :data-test="value ? `${dataTest}-title` : null">
|
|
37
|
+
<slot name="header">{{ title }}</slot>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<slot name="image" />
|
|
41
|
+
|
|
42
|
+
<div class="c-bottom-sheet__body" :data-test="getDataTestAttr('body')">
|
|
43
|
+
<slot name="body" />
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<!-- NOTE(andrew): This renders in the body when the title is hidden because the header-actions
|
|
47
|
+
slot is not rendered when the title is hidden. Previously this only rendered in the body but
|
|
48
|
+
this caused issues on iOS where a scrollable body renders in a separate layer and the button
|
|
49
|
+
got rendered invisibly underneath the header. This is intentionally placed after the body so
|
|
50
|
+
it renders on top. -->
|
|
51
|
+
<div
|
|
52
|
+
v-if="isDismissVisible"
|
|
53
|
+
:slot="hideTitle ? undefined : 'header-actions'"
|
|
54
|
+
class="c-bottom-sheet__actions"
|
|
55
|
+
>
|
|
56
|
+
<c-close-button
|
|
57
|
+
:data-test="getDataTestAttr('dismiss')"
|
|
58
|
+
:disabled="disableDismiss"
|
|
59
|
+
@click="internalValue = false"
|
|
60
|
+
/>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<CButtonStack v-if="footerHasContent" slot="footer" :data-test="getDataTestAttr('footer')">
|
|
64
|
+
<slot name="footer" />
|
|
65
|
+
</CButtonStack>
|
|
66
|
+
</sl-drawer>
|
|
67
|
+
</template>
|
|
68
|
+
|
|
69
|
+
<script lang="ts">
|
|
70
|
+
import { faXmark } from '@fortawesome/pro-regular-svg-icons';
|
|
71
|
+
import '@shoelace-style/shoelace/dist/components/drawer/drawer.js';
|
|
72
|
+
import type SlDrawer from '@shoelace-style/shoelace/dist/components/drawer/drawer.js';
|
|
73
|
+
import type { ElementAnimation } from '@shoelace-style/shoelace/dist/utilities/animation-registry.js';
|
|
74
|
+
import { setAnimation } from '@shoelace-style/shoelace/dist/utilities/animation-registry.js';
|
|
75
|
+
import type { Ref } from 'vue';
|
|
76
|
+
import { computed, defineComponent, nextTick, onMounted, ref, toRefs } from 'vue';
|
|
77
|
+
|
|
78
|
+
import CButtonStack from '@propelinc/citrus-ui/src/components/CButtonStack.vue';
|
|
79
|
+
import CCloseButton from '@propelinc/citrus-ui/src/components/internal/CCloseButton.vue';
|
|
80
|
+
import { useInternalValue } from '@propelinc/citrus-ui/src/composables/binding';
|
|
81
|
+
import { useCssColor } from '@propelinc/citrus-ui/src/composables/colors';
|
|
82
|
+
import { useShoelaceShadowParts } from '@propelinc/citrus-ui/src/composables/elements';
|
|
83
|
+
import {
|
|
84
|
+
useDragListener,
|
|
85
|
+
useElasticClamp,
|
|
86
|
+
useScrollBoundary,
|
|
87
|
+
} from '@propelinc/citrus-ui/src/composables/gestures';
|
|
88
|
+
import { useSlotHasContent } from '@propelinc/citrus-ui/src/composables/slots';
|
|
89
|
+
import { fadeIn, fadeOut, slideDown, slideUp } from '@propelinc/citrus-ui/src/services/animation';
|
|
90
|
+
|
|
91
|
+
/** Minimum distance, in pixels, the user has to drag for it to be considered a
|
|
92
|
+
* successful swipe. */
|
|
93
|
+
const MIN_SWIPE_DISTANCE = 100;
|
|
94
|
+
/** The minimum speed, in pixels per millisecond, the user has to drag down for
|
|
95
|
+
* it to be considered a successful swipe. */
|
|
96
|
+
const MIN_SWIPE_VELOCITY = 0.75;
|
|
97
|
+
/** The minimum speed, in pixels per millisecond, to animate the sheet sliding
|
|
98
|
+
* down after a successful swipe. */
|
|
99
|
+
const MIN_DISMISS_VELOCITY = 1.5;
|
|
100
|
+
/** Minimum duration, in milliseconds, for the slide down animation after a
|
|
101
|
+
* successful drag. */
|
|
102
|
+
const MIN_DISMISS_DURATION = 100;
|
|
103
|
+
/** Maximum distance the sheet can move in a direction it can't be dragged. */
|
|
104
|
+
const DRAG_CLAMP_DISTANCE = 240;
|
|
105
|
+
/** Fallback height for the sheet if the actual height is unknown. */
|
|
106
|
+
const SHEET_HEIGHT_FALLBACK = 300;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Was having issues with the useShoelaceAnimation composable not updating the animation quickly
|
|
110
|
+
* enough using the `watch` or `watchEffect` hooks, so we're just going to set the animations
|
|
111
|
+
* directly whenever we need to change the animation.
|
|
112
|
+
*/
|
|
113
|
+
function setCustomAnimation(
|
|
114
|
+
el: Ref<Element | null>,
|
|
115
|
+
name: string,
|
|
116
|
+
animation: ElementAnimation
|
|
117
|
+
): void {
|
|
118
|
+
if (!el.value) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
setAnimation(el.value, name, animation);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export default defineComponent({
|
|
126
|
+
components: { CButtonStack, CCloseButton },
|
|
127
|
+
props: {
|
|
128
|
+
/** Aria label for the bottom sheet. This is required if title is hidden */
|
|
129
|
+
ariaLabel: { type: String, default: '' },
|
|
130
|
+
/** Background color of the bottom sheet */
|
|
131
|
+
backgroundColor: { type: String, default: '' },
|
|
132
|
+
/** Prefix for test selectors */
|
|
133
|
+
dataTest: { type: String, default: 'bottom-sheet' },
|
|
134
|
+
/** Disables the dismiss button */
|
|
135
|
+
disableDismiss: { type: Boolean, default: false },
|
|
136
|
+
/** Divided into header, body, and footer slots */
|
|
137
|
+
divided: { type: Boolean, default: false },
|
|
138
|
+
/** Fixed size, in CSS units. By default the drawer sizes itself to its contents. */
|
|
139
|
+
fixedSize: { type: String, default: '' },
|
|
140
|
+
/** Hides the dismiss 'x' button */
|
|
141
|
+
hideDismiss: { type: Boolean, default: false },
|
|
142
|
+
/** Allows for hiding the entire title bar, both the title and the dismiss 'x' button */
|
|
143
|
+
hideTitle: { type: Boolean, default: false },
|
|
144
|
+
/** Toggles the background behind the sheet. If there's no overlay, the sheet
|
|
145
|
+
* stays open as the user interacts with background content.' */
|
|
146
|
+
overlay: { type: Boolean, default: true },
|
|
147
|
+
/** Prevents the sheet from being dismissed when tapping on the overlay. If
|
|
148
|
+
* there is no overlay then this property is ignored, as the sheet always
|
|
149
|
+
* stays open. */
|
|
150
|
+
persistent: { type: Boolean, default: false },
|
|
151
|
+
/** If true, the bottom sheet will be contained within the viewport. */
|
|
152
|
+
contained: { type: Boolean, default: false },
|
|
153
|
+
/** Do not animate in the bottom sheet if it starts open. */
|
|
154
|
+
skipInitialAnimation: { type: Boolean, default: false },
|
|
155
|
+
/** The title of the bottom sheet */
|
|
156
|
+
title: { type: String, default: '' },
|
|
157
|
+
/** Controls whether or not the bottom sheet is showing */
|
|
158
|
+
value: { type: Boolean, default: false },
|
|
159
|
+
},
|
|
160
|
+
emits: ['input', 'opened', 'closed'],
|
|
161
|
+
setup(props, { emit }) {
|
|
162
|
+
// NOTE(slanden): Temporarily disabling because it's causing a lot of errors
|
|
163
|
+
// in the console - uses from CMS aren't specifying aria-label.
|
|
164
|
+
// watchEffect(() => {
|
|
165
|
+
// if (props.hideTitle && !props.ariaLabel) {
|
|
166
|
+
// console.error('CBottomSheet: aria-label is required when title is hidden');
|
|
167
|
+
// }
|
|
168
|
+
// });
|
|
169
|
+
|
|
170
|
+
// value: Value provided using props which can open/close the bottom sheet.
|
|
171
|
+
// internalValue: Tracks the open/closed state and lets the bottom sheet open and close even if
|
|
172
|
+
// the external value doesn't change.
|
|
173
|
+
// animatedValue: Same as internalValue but starts at false so the bottom sheet animates if it
|
|
174
|
+
// starts in an open state.
|
|
175
|
+
const { value } = toRefs(props);
|
|
176
|
+
const internalValue = useInternalValue(value, { onChange: (value) => emit('input', value) });
|
|
177
|
+
const mounted = ref(false);
|
|
178
|
+
const animatedValue = computed(() => {
|
|
179
|
+
// To animate in the bottom sheet we mount the component with the sheet closed.
|
|
180
|
+
if (!props.skipInitialAnimation && !mounted.value) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
return internalValue.value;
|
|
184
|
+
});
|
|
185
|
+
const { cssColor: backgroundCssColor } = useCssColor(() => props.backgroundColor);
|
|
186
|
+
onMounted(async () => {
|
|
187
|
+
// The bottom sheet does not animate unless we wait a tick. We don't know exactly why this
|
|
188
|
+
// is necessary.
|
|
189
|
+
await nextTick();
|
|
190
|
+
mounted.value = true;
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const sheet = ref<SlDrawer | null>(null);
|
|
194
|
+
const { panel, overlay, body } = useShoelaceShadowParts(sheet, ['panel', 'overlay', 'body']);
|
|
195
|
+
useScrollBoundary(body);
|
|
196
|
+
|
|
197
|
+
// NOTE(mohan): There are three parts to the bottom sheet animation:
|
|
198
|
+
// 1. CSS variables. These are used when we want to set the exact height and
|
|
199
|
+
// opacity, i.e. when the user is actively moving the sheet.
|
|
200
|
+
// 2. The built-in Shoelace show and hide animations. If a user has swiped
|
|
201
|
+
// the sheet away, we calculate the exact animation that will smoothly
|
|
202
|
+
// continue the swipe and then dismiss the shoelace drawer.
|
|
203
|
+
// 3. Manually-called web animations. These are used when the sheet is being
|
|
204
|
+
// restored to its original position after a drag that was not fast or
|
|
205
|
+
// long enough to count as a swipe.
|
|
206
|
+
setCustomAnimation(sheet, 'drawer.showBottom', slideUp());
|
|
207
|
+
setCustomAnimation(sheet, 'drawer.overlay.show', fadeIn());
|
|
208
|
+
setCustomAnimation(sheet, 'drawer.hideBottom', slideDown());
|
|
209
|
+
setCustomAnimation(sheet, 'drawer.overlay.hide', fadeOut());
|
|
210
|
+
|
|
211
|
+
const handleDismiss = (): void => {
|
|
212
|
+
internalValue.value = false;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const onRequestClose = (event: Event): void => {
|
|
216
|
+
if (props.persistent) {
|
|
217
|
+
event.preventDefault();
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const onAfterClose = (): void => {
|
|
222
|
+
// Reset the exit animations in case they were changed in a drag event
|
|
223
|
+
setCustomAnimation(sheet, 'drawer.hideBottom', slideDown());
|
|
224
|
+
setCustomAnimation(sheet, 'drawer.overlay.hide', fadeOut());
|
|
225
|
+
emit('closed');
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Helper function to calculate the distance ratio of the sheet's transform.
|
|
230
|
+
*/
|
|
231
|
+
const getTransformDistanceRatio = (distance: number): number => {
|
|
232
|
+
return distance / (panel.value?.clientHeight ?? SHEET_HEIGHT_FALLBACK);
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const { dragDistance } = useDragListener(panel, {
|
|
236
|
+
onDragEnd: async ({ distance, velocity }) => {
|
|
237
|
+
const shouldDismiss =
|
|
238
|
+
!props.persistent && (distance.y > MIN_SWIPE_DISTANCE || velocity.y > MIN_SWIPE_VELOCITY);
|
|
239
|
+
|
|
240
|
+
if (shouldDismiss) {
|
|
241
|
+
const remainingDismissDistance = panel.value!.clientHeight - dragDistance.value!.y;
|
|
242
|
+
const dismissDuration = Math.max(
|
|
243
|
+
remainingDismissDistance / Math.max(velocity.y, MIN_DISMISS_VELOCITY),
|
|
244
|
+
MIN_DISMISS_DURATION
|
|
245
|
+
);
|
|
246
|
+
const dismissStart = getTransformDistanceRatio(distance.y);
|
|
247
|
+
|
|
248
|
+
// Here we set the custom animations to complete the dismissal animation smoothly
|
|
249
|
+
// from the point where the user ended their drag event.
|
|
250
|
+
setCustomAnimation(
|
|
251
|
+
sheet,
|
|
252
|
+
'drawer.hideBottom',
|
|
253
|
+
slideDown({ start: dismissStart, duration: dismissDuration })
|
|
254
|
+
);
|
|
255
|
+
setCustomAnimation(
|
|
256
|
+
sheet,
|
|
257
|
+
'drawer.overlay.hide',
|
|
258
|
+
fadeOut({ start: dismissStart, duration: dismissDuration })
|
|
259
|
+
);
|
|
260
|
+
handleDismiss();
|
|
261
|
+
} else {
|
|
262
|
+
/**
|
|
263
|
+
* This is when the user's drag event fails to trigger a dismissal
|
|
264
|
+
* We need to restore the sheet to its original position
|
|
265
|
+
*/
|
|
266
|
+
const start = 1 - getTransformDistanceRatio(dragDistance.value!.y);
|
|
267
|
+
const slideUpAnimation = slideUp({ start });
|
|
268
|
+
const fadeInAnimation = fadeIn({ start });
|
|
269
|
+
|
|
270
|
+
// Here we imperatively animate the sheet and overlay back to their original positions
|
|
271
|
+
panel.value?.animate(slideUpAnimation.keyframes, slideUpAnimation.options);
|
|
272
|
+
overlay.value?.animate(fadeInAnimation.keyframes, fadeInAnimation.options);
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* The distance that the sheet has been currently dragged
|
|
279
|
+
*/
|
|
280
|
+
const transformDistance = useElasticClamp(
|
|
281
|
+
computed(() => dragDistance.value?.y ?? 0),
|
|
282
|
+
computed(() => ({
|
|
283
|
+
min: -DRAG_CLAMP_DISTANCE,
|
|
284
|
+
max: props.persistent ? DRAG_CLAMP_DISTANCE : undefined,
|
|
285
|
+
}))
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
const transformDistanceRatio = computed(() =>
|
|
289
|
+
getTransformDistanceRatio(transformDistance.value)
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
const panelTransform = computed(() => `translateY(${transformDistance.value}px)`);
|
|
293
|
+
|
|
294
|
+
const overlayOpacity = computed(() => {
|
|
295
|
+
const result = 1 - transformDistanceRatio.value;
|
|
296
|
+
|
|
297
|
+
if (isNaN(result)) {
|
|
298
|
+
return 1;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return result;
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// NOTE(mohan): We show data-test attributes only when the sheet is open.
|
|
305
|
+
// This helps simplify testing assertions.
|
|
306
|
+
const getDataTestAttr = (suffix?: string): string | null => {
|
|
307
|
+
const dataTest = suffix ? `${props.dataTest}-${suffix}` : props.dataTest;
|
|
308
|
+
return internalValue.value ? dataTest : null;
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
const footerHasContent = useSlotHasContent('footer');
|
|
312
|
+
|
|
313
|
+
const isDismissVisible = computed(
|
|
314
|
+
() => (!props.persistent && !props.hideDismiss) || props.disableDismiss
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
return {
|
|
318
|
+
backgroundCssColor,
|
|
319
|
+
animatedValue,
|
|
320
|
+
dragDistance,
|
|
321
|
+
faXmark,
|
|
322
|
+
footerHasContent,
|
|
323
|
+
getDataTestAttr,
|
|
324
|
+
handleDismiss,
|
|
325
|
+
internalValue,
|
|
326
|
+
isDismissVisible,
|
|
327
|
+
onRequestClose,
|
|
328
|
+
overlayOpacity,
|
|
329
|
+
panelTransform,
|
|
330
|
+
onAfterClose,
|
|
331
|
+
sheet,
|
|
332
|
+
transformDistance,
|
|
333
|
+
};
|
|
334
|
+
},
|
|
335
|
+
});
|
|
336
|
+
</script>
|
|
337
|
+
|
|
338
|
+
<style lang="scss" scoped>
|
|
339
|
+
@import '@propelinc/citrus-ui/src/styles/core';
|
|
340
|
+
@import '@propelinc/citrus-ui/src/styles/utils/a11y';
|
|
341
|
+
|
|
342
|
+
.c-bottom-sheet {
|
|
343
|
+
--size: auto;
|
|
344
|
+
--header-spacing: 24px;
|
|
345
|
+
--body-spacing: 0;
|
|
346
|
+
--bottom-padding: max(env(safe-area-inset-bottom, 0px), 16px);
|
|
347
|
+
--footer-spacing: 12px 24px var(--bottom-padding);
|
|
348
|
+
|
|
349
|
+
&::part(header-actions) {
|
|
350
|
+
padding: 0;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
&::part(close-button) {
|
|
354
|
+
@include sr-only;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
&::part(panel) {
|
|
358
|
+
background: var(--background-color-hex-code, $color-white);
|
|
359
|
+
border-radius: 12px 12px 0 0;
|
|
360
|
+
max-height: 90%;
|
|
361
|
+
overflow: visible;
|
|
362
|
+
transform: var(--panel-transform, none);
|
|
363
|
+
|
|
364
|
+
&::after {
|
|
365
|
+
background: var(--background-color-hex-code, $color-white);
|
|
366
|
+
bottom: -100px;
|
|
367
|
+
content: '';
|
|
368
|
+
height: 101px;
|
|
369
|
+
left: 0;
|
|
370
|
+
position: absolute;
|
|
371
|
+
right: 0;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
&::part(body) {
|
|
376
|
+
border-radius: 12px 12px 0 0;
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* NOTE (kyleshevlin) adding position: relative here fixed an issue where certain actions (like
|
|
380
|
+
* clicking a checkbox) would hijack the bottom sheet body up and off the screen.
|
|
381
|
+
*/
|
|
382
|
+
position: relative;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
&::part(title) {
|
|
386
|
+
@include large-headline;
|
|
387
|
+
|
|
388
|
+
padding-bottom: 16px;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Note(kayliegh): Add space to prevent long titles from colliding with dismiss button
|
|
392
|
+
&--with-dismiss::part(title) {
|
|
393
|
+
margin-right: 28px;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
&::part(overlay) {
|
|
397
|
+
opacity: var(--overlay-opacity, 1);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.c-bottom-sheet__actions {
|
|
402
|
+
position: absolute;
|
|
403
|
+
right: 16px;
|
|
404
|
+
top: 16px;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.c-bottom-sheet__body {
|
|
408
|
+
padding: 0 24px var(--bottom-padding);
|
|
409
|
+
|
|
410
|
+
// NOTE(andrew): When there is a footer the bottom inset is handled by the footer and we just add
|
|
411
|
+
// spacing above the footer.
|
|
412
|
+
.c-bottom-sheet--with-footer & {
|
|
413
|
+
padding-bottom: 12px;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
.c-bottom-sheet--hide-title {
|
|
418
|
+
// NOTE(mohan): When there is no title, we need to add some spacing to the top
|
|
419
|
+
// of the body so it doesn't touch the top of the panel.
|
|
420
|
+
.c-bottom-sheet__body {
|
|
421
|
+
padding-top: 16px;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.c-bottom-sheet--hide-overlay {
|
|
426
|
+
&::part(overlay) {
|
|
427
|
+
display: none;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
</style>
|