@propelinc/citrus-ui 1.0.4 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/README.md +51 -14
  2. package/dist/citrus-ui.cdn.css +1 -0
  3. package/dist/citrus-ui.css +1 -0
  4. package/dist/colors/colors.d.ts +31 -0
  5. package/dist/colors/theme.d.ts +3 -0
  6. package/dist/colors/util-classes.d.ts +11 -0
  7. package/dist/components/CAccordion.vue.d.ts +34 -0
  8. package/dist/components/CAccordionItem.vue.d.ts +39 -0
  9. package/dist/components/CAppBar.vue.d.ts +59 -0
  10. package/dist/components/CBadge.vue.d.ts +35 -0
  11. package/dist/components/CBottomSheet.vue.d.ts +90 -0
  12. package/dist/components/CButton/CButton.vue.d.ts +97 -0
  13. package/dist/components/CButton/types.d.ts +5 -0
  14. package/dist/components/CButtonStack.vue.d.ts +27 -0
  15. package/dist/components/CCard.vue.d.ts +53 -0
  16. package/dist/components/CCardFooter.vue.d.ts +20 -0
  17. package/dist/components/CCardHeader.vue.d.ts +22 -0
  18. package/dist/components/CCardSection.vue.d.ts +26 -0
  19. package/dist/components/CCheckbox.vue.d.ts +62 -0
  20. package/dist/components/CCol.vue.d.ts +30 -0
  21. package/dist/components/CDivider.vue.d.ts +9 -0
  22. package/dist/components/CDobField.vue.d.ts +60 -0
  23. package/dist/components/CDobSelect.vue.d.ts +50 -0
  24. package/dist/components/CEmailField.vue.d.ts +48 -0
  25. package/dist/components/CExpandTransition.vue.d.ts +29 -0
  26. package/dist/components/CFadeTransition.vue.d.ts +20 -0
  27. package/dist/components/CFileInput.vue.d.ts +50 -0
  28. package/dist/components/CFixedPageFooter.vue.d.ts +153 -0
  29. package/dist/components/CForm.vue.d.ts +44 -0
  30. package/dist/components/CFormFieldCounter.vue.d.ts +15 -0
  31. package/dist/components/CIconButton.vue.d.ts +97 -0
  32. package/dist/components/CLabel.vue.d.ts +36 -0
  33. package/dist/components/CListItem.vue.d.ts +56 -0
  34. package/dist/components/CListItemContent.vue.d.ts +27 -0
  35. package/dist/components/CListItemIcon.vue.d.ts +28 -0
  36. package/dist/components/CLoader.vue.d.ts +23 -0
  37. package/dist/components/CLogo.vue.d.ts +9 -0
  38. package/dist/components/CMaskedTextField.vue.d.ts +511 -0
  39. package/dist/components/CMenu.vue.d.ts +17 -0
  40. package/dist/components/CMenuItem.vue.d.ts +37 -0
  41. package/dist/components/CMenuLabel.vue.d.ts +20 -0
  42. package/dist/components/CModal.vue.d.ts +59 -0
  43. package/dist/components/CModalLoading.vue.d.ts +36 -0
  44. package/dist/components/CNotification.vue.d.ts +64 -0
  45. package/dist/components/CPhoneField.vue.d.ts +792 -0
  46. package/dist/components/CPill.vue.d.ts +41 -0
  47. package/dist/components/CPillGroup.vue.d.ts +39 -0
  48. package/dist/components/CPopup.vue.d.ts +37 -0
  49. package/dist/components/CProgressLinear.vue.d.ts +21 -0
  50. package/dist/components/CProgressRing.vue.d.ts +48 -0
  51. package/dist/components/CRadio.vue.d.ts +40 -0
  52. package/dist/components/CRadioGroup.vue.d.ts +54 -0
  53. package/dist/components/CRebrand.vue.d.ts +28 -0
  54. package/dist/components/CRow.vue.d.ts +41 -0
  55. package/dist/components/CSafeArea.vue.d.ts +18 -0
  56. package/dist/components/CSectionHeader.vue.d.ts +29 -0
  57. package/dist/components/CSelect.vue.d.ts +96 -0
  58. package/dist/components/CSkeleton.vue.d.ts +3 -0
  59. package/dist/components/CSkeletonLoaderCard.vue.d.ts +9 -0
  60. package/dist/components/CSkeletonLoaderCircle.vue.d.ts +3 -0
  61. package/dist/components/CSkeletonLoaderText.vue.d.ts +16 -0
  62. package/dist/components/CSlideFadeTransition.vue.d.ts +36 -0
  63. package/dist/components/CSplitInput.vue.d.ts +56 -0
  64. package/dist/components/CSquaredIcon.vue.d.ts +33 -0
  65. package/dist/components/CSsnField.vue.d.ts +798 -0
  66. package/dist/components/CStatusDot.vue.d.ts +10 -0
  67. package/dist/components/CSwitch.vue.d.ts +39 -0
  68. package/dist/components/CSwitchListItem.vue.d.ts +48 -0
  69. package/dist/components/CTextArea.vue.d.ts +96 -0
  70. package/dist/components/CTextField.vue.d.ts +129 -0
  71. package/dist/components/CTextLink.vue.d.ts +36 -0
  72. package/dist/components/CThirdPartyLogo.vue.d.ts +22 -0
  73. package/dist/components/CTimeago.vue.d.ts +12 -0
  74. package/dist/components/CToast.vue.d.ts +69 -0
  75. package/dist/components/CToastsList.vue.d.ts +3 -0
  76. package/dist/components/CValidationMessage.vue.d.ts +37 -0
  77. package/dist/components/CZipcodeField.vue.d.ts +796 -0
  78. package/dist/components/index.d.ts +66 -0
  79. package/dist/components/internal/CCloseButton.vue.d.ts +14 -0
  80. package/dist/composables/accessibility.d.ts +1 -0
  81. package/dist/composables/animation.d.ts +12 -0
  82. package/dist/composables/binding.d.ts +19 -0
  83. package/dist/composables/colors.d.ts +13 -0
  84. package/dist/composables/elements.d.ts +3 -0
  85. package/dist/composables/fields.d.ts +10 -0
  86. package/dist/composables/gestures.d.ts +53 -0
  87. package/dist/composables/i18n.d.ts +3 -0
  88. package/dist/composables/id.d.ts +11 -0
  89. package/dist/composables/input-mask.d.ts +18 -0
  90. package/dist/composables/router.d.ts +30 -0
  91. package/dist/composables/slots.d.ts +2 -0
  92. package/dist/composables/toast.d.ts +21 -0
  93. package/dist/composables/validations.d.ts +77 -0
  94. package/dist/icons.cdn.mjs +3 -0
  95. package/dist/icons.cdn.mjs.map +1 -0
  96. package/dist/icons.d.ts +1 -0
  97. package/dist/icons.mjs +6 -0
  98. package/dist/icons.mjs.map +1 -0
  99. package/dist/index.cdn.mjs +9328 -12875
  100. package/dist/index.cdn.mjs.map +1 -1
  101. package/dist/index.cdn2.mjs +55255 -0
  102. package/dist/index.cdn2.mjs.map +1 -0
  103. package/dist/index.d.ts +8 -0
  104. package/dist/index.mjs +3946 -0
  105. package/dist/index.mjs.map +1 -0
  106. package/dist/plugin.d.ts +3 -0
  107. package/dist/services/animation.d.ts +17 -0
  108. package/dist/services/directives/index.d.ts +2 -0
  109. package/dist/services/directives/scroll-into-view.d.ts +7 -0
  110. package/dist/services/directives/tap-animation.d.ts +6 -0
  111. package/dist/services/id.d.ts +22 -0
  112. package/dist/services/injections/accordions.d.ts +3 -0
  113. package/dist/services/injections/animations.d.ts +2 -0
  114. package/dist/services/injections/buttons.d.ts +4 -0
  115. package/dist/services/injections/forms.d.ts +6 -0
  116. package/dist/services/injections/icon-buttons.d.ts +3 -0
  117. package/dist/services/injections/pills.d.ts +4 -0
  118. package/dist/services/injections/radio.d.ts +10 -0
  119. package/dist/{styles/main.css → styles.css} +40 -2
  120. package/dist/theme/icons.d.ts +36 -0
  121. package/dist/types/CForm.d.ts +12 -0
  122. package/dist/types/font-awesome.d.ts +5 -0
  123. package/dist/types.d.ts +13 -0
  124. package/package.json +11 -4
  125. package/src/colors/colors.ts +8 -3
  126. package/src/components/CAccordion.vue +31 -24
  127. package/src/components/CAccordionItem.vue +46 -45
  128. package/src/components/CAppBar.vue +108 -101
  129. package/src/components/CBadge.vue +33 -25
  130. package/src/components/CBottomSheet.vue +212 -199
  131. package/src/components/CButton/CButton.vue +135 -147
  132. package/src/components/CButtonStack.vue +21 -13
  133. package/src/components/CCard.vue +72 -69
  134. package/src/components/CCardFooter.vue +5 -5
  135. package/src/components/CCardHeader.vue +9 -7
  136. package/src/components/CCardSection.vue +15 -8
  137. package/src/components/CCheckbox.vue +68 -69
  138. package/src/components/CCol.vue +21 -22
  139. package/src/components/CDivider.vue +9 -8
  140. package/src/components/CDobField.vue +114 -105
  141. package/src/components/CDobSelect.vue +162 -164
  142. package/src/components/CEmailField.vue +39 -27
  143. package/src/components/CExpandTransition.vue +14 -17
  144. package/src/components/CFadeTransition.vue +3 -3
  145. package/src/components/CFileInput.vue +57 -50
  146. package/src/components/CFixedPageFooter.vue +23 -17
  147. package/src/components/CForm.vue +67 -60
  148. package/src/components/CFormFieldCounter.vue +25 -28
  149. package/src/components/CIconButton.vue +84 -65
  150. package/src/components/CLabel.vue +19 -13
  151. package/src/components/CListItem.vue +67 -66
  152. package/src/components/CListItemContent.vue +14 -16
  153. package/src/components/CListItemIcon.vue +18 -14
  154. package/src/components/CLoader.vue +47 -56
  155. package/src/components/CLogo.vue +13 -12
  156. package/src/components/CMaskedTextField.vue +80 -64
  157. package/src/components/CMenu.vue +14 -6
  158. package/src/components/CMenuItem.vue +28 -22
  159. package/src/components/CMenuLabel.vue +6 -5
  160. package/src/components/CModal.vue +76 -71
  161. package/src/components/CModalLoading.vue +24 -15
  162. package/src/components/CNotification.vue +77 -28
  163. package/src/components/CPhoneField.vue +34 -25
  164. package/src/components/CPill.vue +92 -88
  165. package/src/components/CPillGroup.vue +30 -21
  166. package/src/components/CPopup.vue +46 -37
  167. package/src/components/CProgressLinear.vue +17 -11
  168. package/src/components/CProgressRing.vue +33 -33
  169. package/src/components/CRadio.vue +57 -57
  170. package/src/components/CRadioGroup.vue +85 -72
  171. package/src/components/CRow.vue +22 -20
  172. package/src/components/CSectionHeader.vue +20 -12
  173. package/src/components/CSelect.vue +89 -73
  174. package/src/components/CSkeletonLoaderCard.vue +9 -15
  175. package/src/components/CSkeletonLoaderCircle.vue +1 -9
  176. package/src/components/CSkeletonLoaderText.vue +17 -18
  177. package/src/components/CSlideFadeTransition.vue +12 -34
  178. package/src/components/CSplitInput.vue +46 -45
  179. package/src/components/CSquaredIcon.vue +39 -29
  180. package/src/components/CSsnField.vue +48 -36
  181. package/src/components/CStatusDot.vue +16 -16
  182. package/src/components/CSwitch.vue +31 -22
  183. package/src/components/CSwitchListItem.vue +27 -28
  184. package/src/components/CTextArea.vue +116 -83
  185. package/src/components/CTextField.vue +194 -198
  186. package/src/components/CTextLink.vue +28 -25
  187. package/src/components/CThirdPartyLogo.vue +30 -59
  188. package/src/components/CToast.vue +135 -132
  189. package/src/components/CToastsList.vue +2 -15
  190. package/src/components/CValidationMessage.vue +31 -24
  191. package/src/components/CZipcodeField.vue +40 -27
  192. package/src/composables/elements.ts +1 -1
  193. package/src/composables/fields.ts +4 -4
  194. package/src/composables/router.ts +6 -5
  195. package/src/icons.ts +6 -0
  196. package/src/services/injections/buttons.ts +1 -1
  197. package/src/styles/_core.scss +1 -2
  198. package/src/styles/_reset.scss +1 -1
  199. package/src/styles/main.scss +2 -0
  200. package/src/types.ts +2 -0
  201. package/dist/index.cdn.css +0 -1
  202. package/dist/styles/utils.css +0 -2709
@@ -66,14 +66,13 @@
66
66
  </sl-drawer>
67
67
  </template>
68
68
 
69
- <script lang="ts">
70
- import { faXmark } from '@fortawesome/pro-regular-svg-icons';
69
+ <script setup lang="ts">
71
70
  import '@shoelace-style/shoelace/dist/components/drawer/drawer.js';
72
71
  import type SlDrawer from '@shoelace-style/shoelace/dist/components/drawer/drawer.js';
73
72
  import type { ElementAnimation } from '@shoelace-style/shoelace/dist/utilities/animation-registry.js';
74
73
  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';
74
+ import type { Ref, VNode } from 'vue';
75
+ import { computed, nextTick, onMounted, ref, toRef } from 'vue';
77
76
 
78
77
  import CButtonStack from '@propelinc/citrus-ui/src/components/CButtonStack.vue';
79
78
  import CCloseButton from '@propelinc/citrus-ui/src/components/internal/CCloseButton.vue';
@@ -122,217 +121,231 @@ function setCustomAnimation(
122
121
  setAnimation(el.value, name, animation);
123
122
  }
124
123
 
125
- export default defineComponent({
126
- components: { CButtonStack, CCloseButton },
127
- props: {
124
+ const props = withDefaults(
125
+ defineProps<{
128
126
  /** Aria label for the bottom sheet. This is required if title is hidden */
129
- ariaLabel: { type: String, default: '' },
127
+ ariaLabel?: string;
130
128
  /** Background color of the bottom sheet */
131
- backgroundColor: { type: String, default: '' },
129
+ backgroundColor?: string;
130
+ /** If true, the bottom sheet will be contained within the viewport. */
131
+ contained?: boolean;
132
132
  /** Prefix for test selectors */
133
- dataTest: { type: String, default: 'bottom-sheet' },
133
+ dataTest?: string;
134
134
  /** Disables the dismiss button */
135
- disableDismiss: { type: Boolean, default: false },
135
+ disableDismiss?: boolean;
136
136
  /** Divided into header, body, and footer slots */
137
- divided: { type: Boolean, default: false },
137
+ divided?: boolean;
138
138
  /** Fixed size, in CSS units. By default the drawer sizes itself to its contents. */
139
- fixedSize: { type: String, default: '' },
139
+ fixedSize?: string;
140
140
  /** Hides the dismiss 'x' button */
141
- hideDismiss: { type: Boolean, default: false },
141
+ hideDismiss?: boolean;
142
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
143
+ hideTitle?: boolean;
144
+ /**
145
+ * Toggles the background behind the sheet. If there's no overlay, the sheet
146
+ * stays open as the user interacts with background content.
147
+ */
148
+ overlay?: boolean;
149
+ /**
150
+ * Prevents the sheet from being dismissed when tapping on the overlay. If
148
151
  * 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 },
152
+ * stays open.
153
+ */
154
+ persistent?: boolean;
153
155
  /** Do not animate in the bottom sheet if it starts open. */
154
- skipInitialAnimation: { type: Boolean, default: false },
156
+ skipInitialAnimation?: boolean;
155
157
  /** The title of the bottom sheet */
156
- title: { type: String, default: '' },
158
+ title?: string;
157
159
  /** 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
- };
160
+ value?: boolean;
161
+ }>(),
162
+ {
163
+ ariaLabel: '',
164
+ backgroundColor: '',
165
+ dataTest: 'bottom-sheet',
166
+ disableDismiss: false,
167
+ divided: false,
168
+ fixedSize: '',
169
+ hideDismiss: false,
170
+ hideTitle: false,
171
+ overlay: true,
172
+ persistent: false,
173
+ contained: false,
174
+ skipInitialAnimation: false,
175
+ title: '',
176
+ value: false,
177
+ }
178
+ );
179
+
180
+ const emit = defineEmits<{
181
+ input: [value: boolean];
182
+ opened: [];
183
+ closed: [];
184
+ }>();
185
+
186
+ defineSlots<{
187
+ header?: () => VNode[];
188
+ image?: () => VNode[];
189
+ body?: () => VNode[];
190
+ footer?: () => VNode[];
191
+ }>();
192
+
193
+ // NOTE(slanden): Temporarily disabling because it's causing a lot of errors
194
+ // in the console - uses from CMS aren't specifying aria-label.
195
+ // watchEffect(() => {
196
+ // if (props.hideTitle && !props.ariaLabel) {
197
+ // console.error('CBottomSheet: aria-label is required when title is hidden');
198
+ // }
199
+ // });
200
+
201
+ // value: Value provided using props which can open/close the bottom sheet.
202
+ // internalValue: Tracks the open/closed state and lets the bottom sheet open and close even if
203
+ // the external value doesn't change.
204
+ // animatedValue: Same as internalValue but starts at false so the bottom sheet animates if it
205
+ // starts in an open state.
206
+ const valueRef = toRef(props, 'value');
207
+ const internalValue = useInternalValue(valueRef, { onChange: (value) => emit('input', value) });
208
+ const mounted = ref(false);
209
+ const animatedValue = computed(() => {
210
+ // To animate in the bottom sheet we mount the component with the sheet closed.
211
+ if (!props.skipInitialAnimation && !mounted.value) {
212
+ return false;
213
+ }
214
+ return internalValue.value;
215
+ });
216
+ const { cssColor: backgroundCssColor } = useCssColor(() => props.backgroundColor);
217
+ onMounted(async () => {
218
+ // The bottom sheet does not animate unless we wait a tick. We don't know exactly why this
219
+ // is necessary.
220
+ await nextTick();
221
+ mounted.value = true;
222
+ });
227
223
 
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
- });
224
+ const sheet = ref<SlDrawer | null>(null);
225
+ const {
226
+ panel,
227
+ overlay: overlayPart,
228
+ body: bodyPart,
229
+ } = useShoelaceShadowParts(sheet, ['panel', 'overlay', 'body']);
230
+ useScrollBoundary(bodyPart);
231
+
232
+ // NOTE(mohan): There are three parts to the bottom sheet animation:
233
+ // 1. CSS variables. These are used when we want to set the exact height and
234
+ // opacity, i.e. when the user is actively moving the sheet.
235
+ // 2. The built-in Shoelace show and hide animations. If a user has swiped
236
+ // the sheet away, we calculate the exact animation that will smoothly
237
+ // continue the swipe and then dismiss the shoelace drawer.
238
+ // 3. Manually-called web animations. These are used when the sheet is being
239
+ // restored to its original position after a drag that was not fast or
240
+ // long enough to count as a swipe.
241
+ setCustomAnimation(sheet, 'drawer.showBottom', slideUp());
242
+ setCustomAnimation(sheet, 'drawer.overlay.show', fadeIn());
243
+ setCustomAnimation(sheet, 'drawer.hideBottom', slideDown());
244
+ setCustomAnimation(sheet, 'drawer.overlay.hide', fadeOut());
245
+
246
+ const handleDismiss = (): void => {
247
+ internalValue.value = false;
248
+ };
249
+
250
+ const onRequestClose = (event: Event): void => {
251
+ if (props.persistent) {
252
+ event.preventDefault();
253
+ }
254
+ };
276
255
 
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
- };
256
+ const onAfterClose = (): void => {
257
+ // Reset the exit animations in case they were changed in a drag event
258
+ setCustomAnimation(sheet, 'drawer.hideBottom', slideDown());
259
+ setCustomAnimation(sheet, 'drawer.overlay.hide', fadeOut());
260
+ emit('closed');
261
+ };
262
+
263
+ /**
264
+ * Helper function to calculate the distance ratio of the sheet's transform.
265
+ */
266
+ const getTransformDistanceRatio = (distance: number): number => {
267
+ return distance / (panel.value?.clientHeight ?? SHEET_HEIGHT_FALLBACK);
268
+ };
269
+
270
+ const { dragDistance } = useDragListener(panel, {
271
+ onDragEnd: async ({ distance, velocity }) => {
272
+ const shouldDismiss =
273
+ !props.persistent && (distance.y > MIN_SWIPE_DISTANCE || velocity.y > MIN_SWIPE_VELOCITY);
274
+
275
+ if (shouldDismiss) {
276
+ const remainingDismissDistance = panel.value!.clientHeight - dragDistance.value!.y;
277
+ const dismissDuration = Math.max(
278
+ remainingDismissDistance / Math.max(velocity.y, MIN_DISMISS_VELOCITY),
279
+ MIN_DISMISS_DURATION
280
+ );
281
+ const dismissStart = getTransformDistanceRatio(distance.y);
282
+
283
+ // Here we set the custom animations to complete the dismissal animation smoothly
284
+ // from the point where the user ended their drag event.
285
+ setCustomAnimation(
286
+ sheet,
287
+ 'drawer.hideBottom',
288
+ slideDown({ start: dismissStart, duration: dismissDuration })
289
+ );
290
+ setCustomAnimation(
291
+ sheet,
292
+ 'drawer.overlay.hide',
293
+ fadeOut({ start: dismissStart, duration: dismissDuration })
294
+ );
295
+ handleDismiss();
296
+ } else {
297
+ /**
298
+ * This is when the user's drag event fails to trigger a dismissal
299
+ * We need to restore the sheet to its original position
300
+ */
301
+ const start = 1 - getTransformDistanceRatio(dragDistance.value!.y);
302
+ const slideUpAnimation = slideUp({ start });
303
+ const fadeInAnimation = fadeIn({ start });
304
+
305
+ // Here we imperatively animate the sheet and overlay back to their original positions
306
+ panel.value?.animate(slideUpAnimation.keyframes, slideUpAnimation.options);
307
+ overlayPart.value?.animate(fadeInAnimation.keyframes, fadeInAnimation.options);
308
+ }
334
309
  },
335
310
  });
311
+
312
+ /**
313
+ * The distance that the sheet has been currently dragged
314
+ */
315
+ const transformDistance = useElasticClamp(
316
+ computed(() => dragDistance.value?.y ?? 0),
317
+ computed(() => ({
318
+ min: -DRAG_CLAMP_DISTANCE,
319
+ max: props.persistent ? DRAG_CLAMP_DISTANCE : undefined,
320
+ }))
321
+ );
322
+
323
+ const transformDistanceRatio = computed(() => getTransformDistanceRatio(transformDistance.value));
324
+
325
+ const panelTransform = computed(() => `translateY(${transformDistance.value}px)`);
326
+
327
+ const overlayOpacity = computed(() => {
328
+ const result = 1 - transformDistanceRatio.value;
329
+
330
+ if (isNaN(result)) {
331
+ return 1;
332
+ }
333
+
334
+ return result;
335
+ });
336
+
337
+ // NOTE(mohan): We show data-test attributes only when the sheet is open.
338
+ // This helps simplify testing assertions.
339
+ const getDataTestAttr = (suffix?: string): string | null => {
340
+ const dataTest = suffix ? `${props.dataTest}-${suffix}` : props.dataTest;
341
+ return internalValue.value ? dataTest : null;
342
+ };
343
+
344
+ const footerHasContent = useSlotHasContent('footer');
345
+
346
+ const isDismissVisible = computed(
347
+ () => (!props.persistent && !props.hideDismiss) || props.disableDismiss
348
+ );
336
349
  </script>
337
350
 
338
351
  <style lang="scss" scoped>