@purpurds/drawer 8.3.1 → 8.4.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/dist/styles.css CHANGED
@@ -1 +1 @@
1
- ._purpur-drawer-content_khfax_1{bottom:0;height:90vh;height:90dvh;max-width:100%;position:absolute;width:100%}@media (prefers-reduced-motion: no-preference){._purpur-drawer-content_khfax_1{animation:_drawerSmallScreenAnimation_khfax_1 var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out);transition:transform var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out)}}._purpur-drawer-content--left_khfax_15{left:0}._purpur-drawer-content--right_khfax_18{right:0}._purpur-drawer-content--fit-to-content_khfax_21{display:flex;flex-direction:column;height:auto;max-height:90vh;max-height:90dvh}@media screen and (min-width: 600px){._purpur-drawer-content_khfax_1{height:100vh;height:100dvh;max-width:calc(30rem * var(--purpur-rescale));top:0}._purpur-drawer-content--fit-to-content_khfax_21{max-height:100vh;max-height:100dvh}}@media screen and (min-width: 600px) and (prefers-reduced-motion: no-preference){._purpur-drawer-content--right_khfax_18{animation:_drawerLargeScreenAnimationRight_khfax_1 var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out)}._purpur-drawer-content--left_khfax_15{animation:_drawerLargeScreenAnimationLeft_khfax_1 var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out)}}._purpur-drawer-content_khfax_1:focus{outline:none}._purpur-drawer-content__content-container_khfax_51{display:flex;flex-direction:column;gap:var(--purpur-spacing-400)}._purpur-drawer-content__drawer-frame_khfax_56[data-swipe=cancel]{transform:translateY(0);transition:transform var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out)}._purpur-drawer-content__drawer-frame_khfax_56[data-swipe=move]{transform:translateY(var(--purpur-drawer-swipe-move-y))}._purpur-drawer-content__drawer-frame_khfax_56[data-swipe=end]{animation:_slideDown_khfax_1 var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out) forwards}._purpur-drawer-content__description_khfax_66{color:var(--purpur-color-text-default);display:block;font-family:var(--purpur-typography-family-default);font-size:var(--purpur-typography-scale-100);font-weight:var(--purpur-typography-weight-normal);-webkit-hyphens:none;hyphens:none;line-height:var(--purpur-typography-line-height-loose);margin:0}._purpur-drawer-overlay_khfax_77{background:var(--purpur-color-overlay-default);top:0;right:0;bottom:0;left:0;position:fixed}@media (prefers-reduced-motion: no-preference){._purpur-drawer-overlay_khfax_77{animation:_overlayAnimation_khfax_1 var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out);transition:opacity var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out)}}@keyframes _slideDown_khfax_1{0%{transform:translateY(var(--purpur-drawer-swipe-end-y))}to{transform:translateY(100%)}}@keyframes _overlayAnimation_khfax_1{0%{opacity:0}to{opacity:1}}@keyframes _drawerLargeScreenAnimationRight_khfax_1{0%{transform:translate(100%)}to{transform:translate(0)}}@keyframes _drawerLargeScreenAnimationLeft_khfax_1{0%{transform:translate(-100%)}to{transform:translate(0)}}@keyframes _drawerSmallScreenAnimation_khfax_1{0%{transform:translateY(100%)}to{transform:translateY(0)}}._purpur-drawer-container--header_1u45h_1{padding:var(--purpur-spacing-300) var(--purpur-spacing-page-padding-sm) var(--purpur-spacing-150);border-bottom:var(--purpur-border-width-xs) solid var(--purpur-color-border-weak)}@media screen and (min-width: 600px){._purpur-drawer-container--header_1u45h_1{padding:var(--purpur-spacing-300) var(--purpur-spacing-300) var(--purpur-spacing-200)}}._purpur-drawer-container--body_1u45h_10{padding:var(--purpur-spacing-300) var(--purpur-spacing-page-padding-sm) 0}@media screen and (min-width: 600px){._purpur-drawer-container--body_1u45h_10{padding:var(--purpur-spacing-400) var(--purpur-spacing-300) 0}}._purpur-drawer-container--body_1u45h_10._purpur-drawer-container--sticky_1u45h_18{padding:var(--purpur-spacing-300) var(--purpur-spacing-page-padding-sm)}@media screen and (min-width: 600px){._purpur-drawer-container--body_1u45h_10._purpur-drawer-container--sticky_1u45h_18{padding:var(--purpur-spacing-400) var(--purpur-spacing-300)}}._purpur-drawer-container--footer_1u45h_26{padding:0 var(--purpur-spacing-page-padding-sm) var(--purpur-spacing-200)}@media screen and (min-width: 600px){._purpur-drawer-container--footer_1u45h_26{padding:0 var(--purpur-spacing-300) var(--purpur-spacing-200)}}._purpur-drawer-container--footer_1u45h_26._purpur-drawer-container--sticky_1u45h_18{border-top:var(--purpur-border-width-xs) solid var(--purpur-color-border-weak);padding:var(--purpur-spacing-200) var(--purpur-spacing-page-padding-sm)}@media screen and (min-width: 600px){._purpur-drawer-container--footer_1u45h_26._purpur-drawer-container--sticky_1u45h_18{padding:var(--purpur-spacing-200) var(--purpur-spacing-300)}}._purpur-drawer-frame_1s8hq_1{background-color:var(--purpur-color-background-primary);border-top-left-radius:var(--purpur-border-radius-lg);border-top-right-radius:var(--purpur-border-radius-lg);box-shadow:var(--purpur-shadow-lg);display:flex;flex-direction:column;height:100%;position:relative}@media screen and (min-width: 600px){._purpur-drawer-frame--left_1s8hq_12{border-bottom-right-radius:var(--purpur-border-radius-lg);border-top-left-radius:0}._purpur-drawer-frame--right_1s8hq_16{border-bottom-left-radius:var(--purpur-border-radius-lg);border-top-right-radius:0}}._purpur-drawer-frame--sticky-footer_1s8hq_21{gap:0}._purpur-drawer-frame__header_1s8hq_24{flex:0 0 auto}._purpur-drawer-frame__body_1s8hq_27{flex:1 1 auto;overflow:hidden}._purpur-drawer-frame--fit-to-content_1s8hq_31{flex:1 1 auto;height:auto;overflow:hidden}._purpur-drawer-frame--fit-to-content_1s8hq_31 ._purpur-drawer-frame__body_1s8hq_27{display:flex;flex-direction:column}._purpur-drawer-frame__footer_1s8hq_40{flex:0 0 auto}._purpur-drawer-frame__content-container_1s8hq_43{display:flex;flex-direction:column;gap:var(--purpur-spacing-400)}._purpur-drawer-frame__content-container--no-footer_1s8hq_48{margin-bottom:var(--purpur-spacing-400)}._purpur-drawer-handle_3n0ew_1{align-items:center;display:flex;height:var(--purpur-spacing-250);justify-content:center;position:absolute;top:0;width:100%}@media screen and (min-width: 600px){._purpur-drawer-handle_3n0ew_1{display:none}}._purpur-drawer-handle_3n0ew_1:before{content:"";background:var(--purpur-color-border-weak);border-radius:var(--purpur-border-radius-full);height:var(--purpur-spacing-50);width:var(--purpur-spacing-600)}._purpur-drawer-header__row_1yg5w_1{display:flex;align-items:center;gap:var(--purpur-spacing-100)}._purpur-drawer-header__row--with-back-button_1yg5w_6{margin-bottom:var(--purpur-spacing-100)}._purpur-drawer-header__left_1yg5w_9{flex:1 1 auto}._purpur-drawer-header__right_1yg5w_12{flex:0 0 auto}._purpur-drawer-header__close-button_1yg5w_15{margin-right:calc(-1 * var(--purpur-spacing-100))}._purpur-drawer-header__back-button--only-icon_1yg5w_18{margin-left:calc(-1 * var(--purpur-spacing-100))}._purpur-drawer-scroll-area__root_1r0fa_1{height:100%}._purpur-drawer-scroll-area__root--fit-to-content_1r0fa_4{display:flex;flex-direction:column;height:auto;overflow:hidden}._purpur-drawer-scroll-area__viewport_1r0fa_10{height:100%}._purpur-drawer-scroll-area__scrollbar_1r0fa_13{display:flex;-webkit-user-select:none;user-select:none;touch-action:none;padding:var(--purpur-spacing-25);background:var(--purpur-color-functional-white);transition:background var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out);width:var(--purpur-spacing-100)}._purpur-drawer-scroll-area__thumb_1r0fa_24{background:var(--purpur-color-gray-200);border-radius:var(--purpur-spacing-200);flex:1;position:relative}._purpur-drawer-scroll-area__thumb_1r0fa_24:before{content:"";height:100%;left:50%;min-height:var(--purpur-spacing-300);min-width:var(--purpur-spacing-300);position:absolute;top:50%;transform:translate(-50%,-50%);width:100%}
1
+ ._purpur-drawer-content_71yig_1{bottom:0;height:95.5vh;height:95.5dvh;max-width:100%;position:absolute;width:100%;transform:translateY(var(--purpur-drawer-swipe-move-y))}@media (prefers-reduced-motion: no-preference){._purpur-drawer-content_71yig_1:not([data-swipe=move]){transition:transform var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out)}._purpur-drawer-content_71yig_1[data-state=open]{animation:_slideUp_71yig_1 var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out)}._purpur-drawer-content_71yig_1[data-state=closed]{animation:_slideDown_71yig_1 var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out)}}._purpur-drawer-content--left_71yig_21{left:0}._purpur-drawer-content--right_71yig_24{right:0}._purpur-drawer-content--fit-to-content_71yig_27{display:flex;flex-direction:column;height:auto;max-height:95.5vh;max-height:95.5dvh}@media screen and (min-width: 600px){._purpur-drawer-content_71yig_1{height:100vh;height:100dvh;max-width:calc(30rem * var(--purpur-rescale));top:0}._purpur-drawer-content--fit-to-content_71yig_27{max-height:100vh;max-height:100dvh}}@media screen and (min-width: 600px) and (prefers-reduced-motion: no-preference){._purpur-drawer-content--right_71yig_24[data-state=open]{animation:_slideInRight_71yig_1 var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out)}._purpur-drawer-content--left_71yig_21[data-state=open]{animation:_slideInLeft_71yig_1 var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out)}._purpur-drawer-content--right_71yig_24[data-state=closed]{animation:_slideOutRight_71yig_1 var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out)}._purpur-drawer-content--left_71yig_21[data-state=closed]{animation:_slideOutLeft_71yig_1 var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out)}}._purpur-drawer-content_71yig_1:focus{outline:none}._purpur-drawer-content__content-container_71yig_63{display:flex;flex-direction:column;gap:var(--purpur-spacing-400)}._purpur-drawer-content__description_71yig_68{color:var(--purpur-color-text-default);display:block;font-family:var(--purpur-typography-family-default);font-size:var(--purpur-typography-scale-100);font-weight:var(--purpur-typography-weight-normal);-webkit-hyphens:none;hyphens:none;line-height:var(--purpur-typography-line-height-loose);margin:0}._purpur-drawer-overlay_71yig_79{background:var(--purpur-color-overlay-default);top:0;right:0;bottom:0;left:0;position:fixed}@media (prefers-reduced-motion: no-preference){._purpur-drawer-overlay_71yig_79{transition:opacity var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out)}._purpur-drawer-overlay_71yig_79[data-state=open]{animation:_fadeIn_71yig_1 var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out)}._purpur-drawer-overlay_71yig_79[data-state=closed]{animation:_fadeOut_71yig_1 var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out)}}@keyframes _fadeIn_71yig_1{0%{opacity:0}to{opacity:1}}@keyframes _fadeOut_71yig_1{0%{opacity:1}to{opacity:0}}@keyframes _slideInRight_71yig_1{0%{transform:translate(100%)}to{transform:translate(0)}}@keyframes _slideOutRight_71yig_1{0%{transform:translate(0)}to{transform:translate(100%)}}@keyframes _slideInLeft_71yig_1{0%{transform:translate(-100%)}to{transform:translate(0)}}@keyframes _slideOutLeft_71yig_1{0%{transform:translate(0)}to{transform:translate(-100%)}}@keyframes _slideUp_71yig_1{0%{transform:translateY(100%)}to{transform:translateY(0)}}@keyframes _slideDown_71yig_1{0%{transform:translateY(var(--purpur-drawer-swipe-end-y))}to{transform:translateY(100%)}}._purpur-drawer-container--header_1bcla_1{border-bottom:var(--purpur-border-width-xs) solid var(--purpur-color-border-weak)}._purpur-drawer-container--body_1bcla_4{padding:var(--purpur-spacing-300) var(--purpur-spacing-page-padding-sm) 0}@media screen and (min-width: 600px){._purpur-drawer-container--body_1bcla_4{padding:var(--purpur-spacing-400) var(--purpur-spacing-300) 0}}._purpur-drawer-container--body_1bcla_4._purpur-drawer-container--sticky_1bcla_12{padding:var(--purpur-spacing-300) var(--purpur-spacing-page-padding-sm)}@media screen and (min-width: 600px){._purpur-drawer-container--body_1bcla_4._purpur-drawer-container--sticky_1bcla_12{padding:var(--purpur-spacing-400) var(--purpur-spacing-300)}}._purpur-drawer-container--footer_1bcla_20{padding:0 var(--purpur-spacing-page-padding-sm) var(--purpur-spacing-200)}@media screen and (min-width: 600px){._purpur-drawer-container--footer_1bcla_20{padding:0 var(--purpur-spacing-300) var(--purpur-spacing-200)}}._purpur-drawer-container--footer_1bcla_20._purpur-drawer-container--sticky_1bcla_12{border-top:var(--purpur-border-width-xs) solid var(--purpur-color-border-weak);padding:var(--purpur-spacing-200) var(--purpur-spacing-page-padding-sm)}@media screen and (min-width: 600px){._purpur-drawer-container--footer_1bcla_20._purpur-drawer-container--sticky_1bcla_12{padding:var(--purpur-spacing-200) var(--purpur-spacing-300)}}._purpur-drawer-frame_1didd_1{background-color:var(--purpur-color-background-primary);border-top-left-radius:var(--purpur-border-radius-lg);border-top-right-radius:var(--purpur-border-radius-lg);box-shadow:var(--purpur-shadow-lg);display:flex;flex-direction:column;height:100%;position:relative}._purpur-drawer-frame--without-back-button_1didd_11 ._purpur-drawer-frame__header_1didd_11{padding:var(--purpur-spacing-300) calc(56 * var(--purpur-spacing-10)) var(--purpur-spacing-150) var(--purpur-spacing-200)}._purpur-drawer-frame--with-back-button_1didd_14 ._purpur-drawer-frame__header_1didd_11{padding:calc(14 * var(--purpur-spacing-10)) var(--purpur-spacing-200) var(--purpur-spacing-150)}@media screen and (min-width: 600px){._purpur-drawer-frame--left_1didd_18{border-bottom-right-radius:var(--purpur-border-radius-lg);border-top-left-radius:0}._purpur-drawer-frame--right_1didd_22{border-bottom-left-radius:var(--purpur-border-radius-lg);border-top-right-radius:0}._purpur-drawer-frame--without-back-button_1didd_11 ._purpur-drawer-frame__header_1didd_11{padding:var(--purpur-spacing-300) calc(64 * var(--purpur-spacing-10)) var(--purpur-spacing-200) var(--purpur-spacing-300)}._purpur-drawer-frame--with-back-button_1didd_14 ._purpur-drawer-frame__header_1didd_11{padding:calc(14 * var(--purpur-spacing-10)) var(--purpur-spacing-300) var(--purpur-spacing-200)}}._purpur-drawer-frame--sticky-footer_1didd_33{gap:0}._purpur-drawer-frame__header_1didd_11{flex:0 0 auto}._purpur-drawer-frame__body_1didd_39{flex:1 1 auto;overflow:hidden}._purpur-drawer-frame--fit-to-content_1didd_43{flex:1 1 auto;height:auto;overflow:hidden}._purpur-drawer-frame--fit-to-content_1didd_43 ._purpur-drawer-frame__body_1didd_39{display:flex;flex-direction:column}._purpur-drawer-frame__footer_1didd_52{flex:0 0 auto}._purpur-drawer-frame__content-container_1didd_55{display:flex;flex-direction:column;gap:var(--purpur-spacing-400)}._purpur-drawer-frame__content-container--no-footer_1didd_60{margin-bottom:var(--purpur-spacing-400)}._purpur-drawer-handle_3n0ew_1{align-items:center;display:flex;height:var(--purpur-spacing-250);justify-content:center;position:absolute;top:0;width:100%}@media screen and (min-width: 600px){._purpur-drawer-handle_3n0ew_1{display:none}}._purpur-drawer-handle_3n0ew_1:before{content:"";background:var(--purpur-color-border-weak);border-radius:var(--purpur-border-radius-full);height:var(--purpur-spacing-50);width:var(--purpur-spacing-600)}._purpur-drawer-header__row_12p9v_1{display:flex}._purpur-drawer-header__row--with-back-button_12p9v_4{margin-bottom:var(--purpur-spacing-100)}._purpur-drawer-header__left_12p9v_7{flex:1 1 auto}._purpur-drawer-header__right_12p9v_10{flex:0 0 auto}._purpur-drawer-header_12p9v_1 ._purpur-drawer-header__close-button_12p9v_13{position:absolute;top:calc(14 * var(--purpur-spacing-10));right:calc(6 * var(--purpur-spacing-10));padding:calc(10 * var(--purpur-spacing-10))}@media screen and (min-width: 600px){._purpur-drawer-header_12p9v_1 ._purpur-drawer-header__close-button_12p9v_13{right:calc(14 * var(--purpur-spacing-10))}}._purpur-drawer-header__back-button--only-icon_12p9v_24{margin-left:calc(-1 * var(--purpur-spacing-100))}._purpur-drawer-scroll-area__root_1hyos_1{height:100%}._purpur-drawer-scroll-area__root--fit-to-content_1hyos_4{display:flex;flex-direction:column;height:auto;overflow:hidden}._purpur-drawer-scroll-area__viewport_1hyos_10{position:relative;height:100%}._purpur-drawer-scroll-area__scrollbar_1hyos_14{display:flex;-webkit-user-select:none;user-select:none;touch-action:none;padding:var(--purpur-spacing-25);background:var(--purpur-color-functional-white);transition:background var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out);width:var(--purpur-spacing-100)}._purpur-drawer-scroll-area__thumb_1hyos_25{background:var(--purpur-color-gray-200);border-radius:var(--purpur-spacing-200);flex:1;position:relative}._purpur-drawer-scroll-area__thumb_1hyos_25:before{content:"";height:100%;left:50%;min-height:var(--purpur-spacing-300);min-width:var(--purpur-spacing-300);position:absolute;top:50%;transform:translate(-50%,-50%);width:100%}
@@ -1,13 +1,12 @@
1
- import { AnimationEvent, RefObject } from 'react';
1
+ import { RefObject } from 'react';
2
2
  import { SwipeEvent } from './types';
3
3
 
4
4
  type UseSwipeToDismiss = {
5
- onAnimationEnd(event: AnimationEvent<HTMLDivElement>): void;
6
5
  onSwipeStart(): void;
7
6
  onSwipeMove(event: SwipeEvent): void;
8
7
  onSwipeCancel(): void;
9
8
  onSwipeEnd(event: SwipeEvent): void;
10
9
  };
11
- export declare const useSwipeToDismiss: <T extends HTMLElement>(containerRef: RefObject<T | null>, handleOpenChange: (open: boolean) => void) => UseSwipeToDismiss;
10
+ export declare const useSwipeToDismiss: <T extends HTMLElement>(containerRef: RefObject<T | null>, handleOpenChange: ((open: boolean) => void) | null) => UseSwipeToDismiss;
12
11
  export {};
13
12
  //# sourceMappingURL=use-swipe-to-dismiss.hook.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-swipe-to-dismiss.hook.d.ts","sourceRoot":"","sources":["../src/use-swipe-to-dismiss.hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,KAAK,iBAAiB,GAAG;IACvB,cAAc,CAAC,KAAK,EAAE,cAAc,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC;IAC5D,YAAY,IAAI,IAAI,CAAC;IACrB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACrC,aAAa,IAAI,IAAI,CAAC;IACtB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;CACrC,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,CAAC,SAAS,WAAW,EACrD,cAAc,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,EACjC,kBAAkB,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,KACxC,iBA4CF,CAAC"}
1
+ {"version":3,"file":"use-swipe-to-dismiss.hook.d.ts","sourceRoot":"","sources":["../src/use-swipe-to-dismiss.hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,KAAK,iBAAiB,GAAG;IACvB,YAAY,IAAI,IAAI,CAAC;IACrB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACrC,aAAa,IAAI,IAAI,CAAC;IACtB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;CACrC,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,CAAC,SAAS,WAAW,EACrD,cAAc,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,EACjC,kBAAkB,CAAC,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,KACjD,iBAwCF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@purpurds/drawer",
3
- "version": "8.3.1",
3
+ "version": "8.4.0",
4
4
  "license": "AGPL-3.0-only",
5
5
  "main": "./dist/drawer.cjs.js",
6
6
  "types": "./dist/drawer.d.ts",
@@ -18,13 +18,13 @@
18
18
  "@radix-ui/react-dialog": "~1.1.6",
19
19
  "@radix-ui/react-scroll-area": "~1.2.3",
20
20
  "classnames": "~2.5.0",
21
- "@purpurds/button": "8.3.1",
22
- "@purpurds/common-types": "8.3.1",
23
- "@purpurds/heading": "8.3.1",
24
- "@purpurds/icon": "8.3.1",
25
- "@purpurds/paragraph": "8.3.1",
26
- "@purpurds/tokens": "8.3.1",
27
- "@purpurds/visually-hidden": "8.3.1"
21
+ "@purpurds/button": "8.4.0",
22
+ "@purpurds/heading": "8.4.0",
23
+ "@purpurds/icon": "8.4.0",
24
+ "@purpurds/common-types": "8.4.0",
25
+ "@purpurds/paragraph": "8.4.0",
26
+ "@purpurds/visually-hidden": "8.4.0",
27
+ "@purpurds/tokens": "8.4.0"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@storybook/react-vite": "^9.0.18",
@@ -2,13 +2,9 @@
2
2
 
3
3
  .purpur-drawer-container {
4
4
  $root: &;
5
+
5
6
  &--header {
6
- padding: var(--purpur-spacing-300) var(--purpur-spacing-page-padding-sm) var(--purpur-spacing-150);
7
7
  border-bottom: var(--purpur-border-width-xs) solid var(--purpur-color-border-weak);
8
-
9
- @media screen and (min-width: $purpur-breakpoint-md) {
10
- padding: var(--purpur-spacing-300) var(--purpur-spacing-300) var(--purpur-spacing-200);
11
- }
12
8
  }
13
9
 
14
10
  &--body {
@@ -4,15 +4,25 @@ $animation-settings: var(--purpur-motion-duration-200) var(--purpur-motion-easin
4
4
 
5
5
  .purpur-drawer-content {
6
6
  bottom: 0;
7
- height: 90vh; /* Fallback for older browsers */
8
- height: 90dvh;
7
+ height: 95.5vh; /* Fallback for older browsers */
8
+ height: 95.5dvh;
9
9
  max-width: 100%;
10
10
  position: absolute;
11
11
  width: 100%;
12
+ transform: translateY(var(--purpur-drawer-swipe-move-y));
12
13
 
13
14
  @media (prefers-reduced-motion: no-preference) {
14
- animation: drawerSmallScreenAnimation $animation-settings;
15
- transition: transform $animation-settings;
15
+ &:not([data-swipe="move"]) {
16
+ transition: transform $animation-settings;
17
+ }
18
+
19
+ &[data-state="open"] {
20
+ animation: slideUp $animation-settings;
21
+ }
22
+
23
+ &[data-state="closed"] {
24
+ animation: slideDown $animation-settings;
25
+ }
16
26
  }
17
27
 
18
28
  &--left {
@@ -27,8 +37,8 @@ $animation-settings: var(--purpur-motion-duration-200) var(--purpur-motion-easin
27
37
  display: flex;
28
38
  flex-direction: column;
29
39
  height: auto;
30
- max-height: 90vh; /* Fallback for older browsers */
31
- max-height: 90dvh;
40
+ max-height: 95.5vh; /* Fallback for older browsers */
41
+ max-height: 95.5dvh;
32
42
  }
33
43
 
34
44
  @media screen and (min-width: $purpur-breakpoint-md) {
@@ -43,12 +53,20 @@ $animation-settings: var(--purpur-motion-duration-200) var(--purpur-motion-easin
43
53
  }
44
54
 
45
55
  @media (prefers-reduced-motion: no-preference) {
46
- &--right {
47
- animation: drawerLargeScreenAnimationRight $animation-settings;
56
+ &--right[data-state="open"] {
57
+ animation: slideInRight $animation-settings;
58
+ }
59
+
60
+ &--left[data-state="open"] {
61
+ animation: slideInLeft $animation-settings;
62
+ }
63
+
64
+ &--right[data-state="closed"] {
65
+ animation: slideOutRight $animation-settings;
48
66
  }
49
67
 
50
- &--left {
51
- animation: drawerLargeScreenAnimationLeft $animation-settings;
68
+ &--left[data-state="closed"] {
69
+ animation: slideOutLeft $animation-settings;
52
70
  }
53
71
  }
54
72
  }
@@ -63,21 +81,6 @@ $animation-settings: var(--purpur-motion-duration-200) var(--purpur-motion-easin
63
81
  gap: var(--purpur-spacing-400);
64
82
  }
65
83
 
66
- &__drawer-frame {
67
- &[data-swipe="cancel"] {
68
- transform: translateY(0);
69
- transition: transform $animation-settings;
70
- }
71
-
72
- &[data-swipe="move"] {
73
- transform: translateY(var(--purpur-drawer-swipe-move-y));
74
- }
75
-
76
- &[data-swipe="end"] {
77
- animation: slideDown $animation-settings forwards;
78
- }
79
- }
80
-
81
84
  &__description {
82
85
  color: var(--purpur-color-text-default);
83
86
  display: block;
@@ -96,30 +99,36 @@ $animation-settings: var(--purpur-motion-duration-200) var(--purpur-motion-easin
96
99
  position: fixed;
97
100
 
98
101
  @media (prefers-reduced-motion: no-preference) {
99
- animation: overlayAnimation $animation-settings;
100
102
  transition: opacity $animation-settings;
103
+ &[data-state="open"] {
104
+ animation: fadeIn $animation-settings;
105
+ }
106
+
107
+ &[data-state="closed"] {
108
+ animation: fadeOut $animation-settings;
109
+ }
101
110
  }
102
111
  }
103
112
 
104
- @keyframes slideDown {
113
+ @keyframes fadeIn {
105
114
  from {
106
- transform: translateY(var(--purpur-drawer-swipe-end-y));
115
+ opacity: 0;
107
116
  }
108
117
  to {
109
- transform: translateY(100%);
118
+ opacity: 1;
110
119
  }
111
120
  }
112
121
 
113
- @keyframes overlayAnimation {
122
+ @keyframes fadeOut {
114
123
  from {
115
- opacity: 0;
124
+ opacity: 1;
116
125
  }
117
126
  to {
118
- opacity: 1;
127
+ opacity: 0;
119
128
  }
120
129
  }
121
130
 
122
- @keyframes drawerLargeScreenAnimationRight {
131
+ @keyframes slideInRight {
123
132
  from {
124
133
  transform: translateX(100%);
125
134
  }
@@ -128,7 +137,16 @@ $animation-settings: var(--purpur-motion-duration-200) var(--purpur-motion-easin
128
137
  }
129
138
  }
130
139
 
131
- @keyframes drawerLargeScreenAnimationLeft {
140
+ @keyframes slideOutRight {
141
+ from {
142
+ transform: translateX(0%);
143
+ }
144
+ to {
145
+ transform: translateX(100%);
146
+ }
147
+ }
148
+
149
+ @keyframes slideInLeft {
132
150
  from {
133
151
  transform: translateX(-100%);
134
152
  }
@@ -137,7 +155,16 @@ $animation-settings: var(--purpur-motion-duration-200) var(--purpur-motion-easin
137
155
  }
138
156
  }
139
157
 
140
- @keyframes drawerSmallScreenAnimation {
158
+ @keyframes slideOutLeft {
159
+ from {
160
+ transform: translateX(0%);
161
+ }
162
+ to {
163
+ transform: translateX(-100%);
164
+ }
165
+ }
166
+
167
+ @keyframes slideUp {
141
168
  from {
142
169
  transform: translateY(100%);
143
170
  }
@@ -145,3 +172,12 @@ $animation-settings: var(--purpur-motion-duration-200) var(--purpur-motion-easin
145
172
  transform: translateY(0%);
146
173
  }
147
174
  }
175
+
176
+ @keyframes slideDown {
177
+ from {
178
+ transform: translateY(var(--purpur-drawer-swipe-end-y));
179
+ }
180
+ to {
181
+ transform: translateY(100%);
182
+ }
183
+ }
@@ -6,7 +6,7 @@ import c from "classnames/bind";
6
6
  import { DrawerContext } from "./drawer.context";
7
7
  import styles from "./drawer-content.module.scss";
8
8
  import { DrawerFrame } from "./drawer-frame";
9
- import { type OpenHandlerFunction, type Position } from "./types";
9
+ import { type Position } from "./types";
10
10
  import { useSwipeToDismiss } from "./use-swipe-to-dismiss.hook";
11
11
 
12
12
  const cx = c.bind(styles);
@@ -77,9 +77,22 @@ export const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>(
77
77
  ]);
78
78
 
79
79
  const drawerFrameRef = useRef<HTMLDivElement>(null);
80
+ const internalDrawerContentRef = useRef<HTMLDivElement>(null);
81
+
82
+ const setRef = (node: HTMLDivElement | null) => {
83
+ internalDrawerContentRef.current = node;
84
+ if (typeof ref === "function") {
85
+ ref(node);
86
+ } else if (ref) {
87
+ ref.current = node;
88
+ }
89
+ };
90
+
80
91
  const onOpenChange = useContext(DrawerContext);
81
- const { onAnimationEnd, onSwipeStart, onSwipeMove, onSwipeCancel, onSwipeEnd } =
82
- useSwipeToDismiss(drawerFrameRef, onOpenChange as OpenHandlerFunction);
92
+ const { onSwipeStart, onSwipeMove, onSwipeCancel, onSwipeEnd } = useSwipeToDismiss(
93
+ internalDrawerContentRef,
94
+ onOpenChange
95
+ );
83
96
 
84
97
  const handlePointerDownOutside = (event: CustomEvent<{ originalEvent: PointerEvent }>) => {
85
98
  if (disableCloseOnClickOutside) {
@@ -111,7 +124,7 @@ export const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>(
111
124
  onOpenAutoFocus={handleInitialFocus}
112
125
  className={classes}
113
126
  data-testid={dataTestId}
114
- ref={ref}
127
+ ref={setRef}
115
128
  {...(!bodyText && { ["aria-describedby"]: undefined })}
116
129
  {...props}
117
130
  >
@@ -125,7 +138,6 @@ export const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>(
125
138
  footerContent={footerContent}
126
139
  headerContent={headerContent}
127
140
  ref={drawerFrameRef}
128
- onAnimationEnd={onAnimationEnd}
129
141
  onBackButtonClick={onBackButtonClick}
130
142
  onSwipeStart={onSwipeStart}
131
143
  onSwipeMove={onSwipeMove}
@@ -12,6 +12,20 @@
12
12
  height: 100%;
13
13
  position: relative;
14
14
 
15
+ &--without-back-button {
16
+ #{$root}__header {
17
+ padding: var(--purpur-spacing-300) calc(56 * var(--purpur-spacing-10))
18
+ var(--purpur-spacing-150) var(--purpur-spacing-200);
19
+ }
20
+ }
21
+
22
+ &--with-back-button {
23
+ #{$root}__header {
24
+ padding: calc(14 * var(--purpur-spacing-10)) var(--purpur-spacing-200)
25
+ var(--purpur-spacing-150);
26
+ }
27
+ }
28
+
15
29
  @media screen and (min-width: $purpur-breakpoint-md) {
16
30
  &--left {
17
31
  border-bottom-right-radius: var(--purpur-border-radius-lg);
@@ -22,6 +36,20 @@
22
36
  border-bottom-left-radius: var(--purpur-border-radius-lg);
23
37
  border-top-right-radius: 0;
24
38
  }
39
+
40
+ &--without-back-button {
41
+ #{$root}__header {
42
+ padding: var(--purpur-spacing-300) calc(64 * var(--purpur-spacing-10))
43
+ var(--purpur-spacing-200) var(--purpur-spacing-300);
44
+ }
45
+ }
46
+
47
+ &--with-back-button {
48
+ #{$root}__header {
49
+ padding: calc(14 * var(--purpur-spacing-10)) var(--purpur-spacing-300)
50
+ var(--purpur-spacing-200);
51
+ }
52
+ }
25
53
  }
26
54
 
27
55
  &--sticky-footer {
@@ -20,7 +20,6 @@ export type DrawerFrameProps = Omit<BaseProps, "children"> & {
20
20
  fitToContent?: boolean;
21
21
  footerContent?: ReactNode;
22
22
  headerContent?: ReactNode;
23
- onAnimationEnd: (event: React.AnimationEvent<HTMLDivElement>) => void;
24
23
  onBackButtonClick?: () => void;
25
24
  onSwipeStart(): void;
26
25
  onSwipeMove(event: SwipeEvent): void;
@@ -46,7 +45,6 @@ export const DrawerFrame = forwardRef<HTMLDivElement, DrawerFrameProps>(
46
45
  fitToContent = false,
47
46
  footerContent,
48
47
  headerContent,
49
- onAnimationEnd,
50
48
  onBackButtonClick,
51
49
  onSwipeStart,
52
50
  onSwipeMove,
@@ -59,6 +57,7 @@ export const DrawerFrame = forwardRef<HTMLDivElement, DrawerFrameProps>(
59
57
  },
60
58
  ref
61
59
  ) => {
60
+ const hasBackButton = !!(backButton && backButtonText && onBackButtonClick);
62
61
  const classes = cx([
63
62
  className,
64
63
  rootClassName,
@@ -67,17 +66,13 @@ export const DrawerFrame = forwardRef<HTMLDivElement, DrawerFrameProps>(
67
66
  [`${rootClassName}--sticky-footer`]: stickyFooter,
68
67
  [`${rootClassName}--left`]: position === "left",
69
68
  [`${rootClassName}--right`]: position === "right",
69
+ [`${rootClassName}--without-back-button`]: !hasBackButton,
70
+ [`${rootClassName}--with-back-button`]: hasBackButton,
70
71
  },
71
72
  ]);
72
73
 
73
74
  return (
74
- <div
75
- className={classes}
76
- data-testid={dataTestId}
77
- ref={ref}
78
- onAnimationEnd={onAnimationEnd}
79
- {...props}
80
- >
75
+ <div className={classes} data-testid={dataTestId} ref={ref} {...props}>
81
76
  <DrawerHandle
82
77
  onSwipeStart={onSwipeStart}
83
78
  onSwipeMove={onSwipeMove}
@@ -1,8 +1,8 @@
1
+ @use "@purpurds/tokens/breakpoint/variables" as *;
2
+
1
3
  .purpur-drawer-header {
2
4
  &__row {
3
5
  display: flex;
4
- align-items: center;
5
- gap: var(--purpur-spacing-100);
6
6
 
7
7
  &--with-back-button {
8
8
  margin-bottom: var(--purpur-spacing-100);
@@ -17,8 +17,15 @@
17
17
  flex: 0 0 auto;
18
18
  }
19
19
 
20
- &__close-button {
21
- margin-right: calc(-1 * var(--purpur-spacing-100));
20
+ #{&}__close-button {
21
+ position: absolute;
22
+ top: calc(14 * var(--purpur-spacing-10));
23
+ right: calc(6 * var(--purpur-spacing-10));
24
+ padding: calc(10 * var(--purpur-spacing-10));
25
+
26
+ @media screen and (min-width: $purpur-breakpoint-md) {
27
+ right: calc(14 * var(--purpur-spacing-10));
28
+ }
22
29
  }
23
30
 
24
31
  &__back-button {
@@ -41,6 +41,7 @@ export const DrawerHeader = forwardRef<HTMLDivElement, DrawerHeaderProps>(
41
41
  ref
42
42
  ) => {
43
43
  const classes = cx([className, rootClassName]);
44
+ const hasBackButton = !!(backButton && backButtonText && onBackButtonClick);
44
45
 
45
46
  return (
46
47
  <div className={classes} data-testid={dataTestId} ref={ref} {...props}>
@@ -48,14 +49,13 @@ export const DrawerHeader = forwardRef<HTMLDivElement, DrawerHeaderProps>(
48
49
  className={cx([
49
50
  `${rootClassName}__row`,
50
51
  {
51
- [`${rootClassName}__row--with-back-button`]:
52
- backButton && backButtonText && onBackButtonClick,
52
+ [`${rootClassName}__row--with-back-button`]: hasBackButton,
53
53
  },
54
54
  ])}
55
55
  data-testid={`${dataTestId}-row`}
56
56
  >
57
57
  <div className={cx(`${rootClassName}__left`)}>
58
- {backButton && backButtonText && onBackButtonClick ? (
58
+ {hasBackButton ? (
59
59
  <Button
60
60
  aria-label={backButtonOnlyIcon ? backButtonText : ""}
61
61
  className={cx([
@@ -108,9 +108,7 @@ export const DrawerHeader = forwardRef<HTMLDivElement, DrawerHeaderProps>(
108
108
  </RadixDialog.Close>
109
109
  </div>
110
110
  </div>
111
- {backButton &&
112
- backButtonText &&
113
- onBackButtonClick &&
111
+ {hasBackButton &&
114
112
  (headerContent ? (
115
113
  <>
116
114
  {headerContent}
@@ -13,6 +13,7 @@ $animation-settings: var(--purpur-motion-duration-200) var(--purpur-motion-easin
13
13
  }
14
14
 
15
15
  &__viewport {
16
+ position: relative;
16
17
  height: 100%;
17
18
  }
18
19
 
@@ -66,6 +66,7 @@ const StorybookMultiDrawerTestComponent = () => {
66
66
  const [open1, setOpen1] = React.useState(false);
67
67
  const [open2, setOpen2] = React.useState(false);
68
68
  const [open3, setOpen3] = React.useState(false);
69
+ const [open4, setOpen4] = React.useState(false);
69
70
  return (
70
71
  <div>
71
72
  <div
@@ -160,11 +161,40 @@ const StorybookMultiDrawerTestComponent = () => {
160
161
  </Drawer.Content>
161
162
  </Drawer>
162
163
  </div>
164
+
165
+ <div
166
+ style={{
167
+ padding: "20px",
168
+ border: "1px solid var(--purpur-color-border-interactive-primary)",
169
+ }}
170
+ >
171
+ <Drawer open={open4} onOpenChange={setOpen4}>
172
+ <Drawer.Trigger>
173
+ <Button variant="primary">Open drawer 4</Button>
174
+ </Drawer.Trigger>
175
+ <Drawer.Content
176
+ bodyText="This is the body text"
177
+ closeButtonAriaLabel="Close drawer"
178
+ fitToContent
179
+ footerContent={<StorybookTestFooterContent numberOfButtons={2} />}
180
+ title="This is drawer 4"
181
+ stickyFooter
182
+ zIndex={20}
183
+ >
184
+ <Heading variant="subsection-100" tag="h3">
185
+ The standard Lorem Ipsum passage, used since the 1500s
186
+ </Heading>
187
+ <Paragraph variant="paragraph-100" style={{ marginBottom: "32px" }}>
188
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit.
189
+ </Paragraph>
190
+ </Drawer.Content>
191
+ </Drawer>
192
+ </div>
163
193
  </div>
164
194
  );
165
195
  };
166
196
 
167
- const StorybookTestFooterContent = () => {
197
+ const StorybookTestFooterContent = ({ numberOfButtons = 3 }: { numberOfButtons?: 2 | 3 }) => {
168
198
  return (
169
199
  <div style={{ textAlign: "center" }}>
170
200
  <div
@@ -172,7 +202,7 @@ const StorybookTestFooterContent = () => {
172
202
  display: "flex",
173
203
  gap: "16px",
174
204
  width: "100%",
175
- marginBottom: "16px",
205
+ marginBottom: numberOfButtons > 2 ? "16px" : 0,
176
206
  }}
177
207
  >
178
208
  <Button variant="secondary" style={{ flex: "1 1 50%" }}>
@@ -182,9 +212,11 @@ const StorybookTestFooterContent = () => {
182
212
  Primary button
183
213
  </Button>
184
214
  </div>
185
- <Button variant="tertiary-purple" style={{ minWidth: "50%" }}>
186
- Tertiary button
187
- </Button>
215
+ {numberOfButtons === 3 && (
216
+ <Button variant="tertiary-purple" style={{ minWidth: "50%" }}>
217
+ Tertiary button
218
+ </Button>
219
+ )}
188
220
  </div>
189
221
  );
190
222
  };
package/src/drawer.tsx CHANGED
@@ -1,23 +1,17 @@
1
1
  import React, { type ReactNode, useEffect } from "react";
2
2
  import type { BaseProps } from "@purpurds/common-types";
3
3
  import * as RadixDialog from "@radix-ui/react-dialog";
4
- import c from "classnames/bind";
5
4
 
6
5
  import { DrawerContext } from "./drawer.context";
7
- import styles from "./drawer.module.scss";
8
6
  import { DrawerContent } from "./drawer-content";
9
7
  import { DrawerTrigger } from "./drawer-trigger";
10
8
 
11
- const cx = c.bind(styles);
12
-
13
9
  export type DrawerProps = Omit<BaseProps, "children"> & {
14
10
  children: ReactNode;
15
11
  onOpenChange?: (open: boolean) => void;
16
12
  open: boolean;
17
13
  };
18
14
 
19
- const rootClassName = "purpur-drawer";
20
-
21
15
  export type DrawerComponent<P> = React.FunctionComponent<P> & {
22
16
  Trigger: typeof DrawerTrigger;
23
17
  Content: typeof DrawerContent;
@@ -32,7 +26,6 @@ export const Drawer: DrawerComponent<DrawerProps> = ({
32
26
  ...props
33
27
  }: DrawerProps) => {
34
28
  const [_open, _setOpen] = React.useState(open);
35
- const classes = cx([className, rootClassName]);
36
29
 
37
30
  const handleOpenChange = (newOpen: boolean) => {
38
31
  _setOpen(newOpen);
@@ -45,7 +38,7 @@ export const Drawer: DrawerComponent<DrawerProps> = ({
45
38
 
46
39
  return (
47
40
  <DrawerContext.Provider value={handleOpenChange}>
48
- <div className={classes} data-testid={dataTestId} {...props}>
41
+ <div className={className} data-testid={dataTestId} {...props}>
49
42
  <RadixDialog.Root open={_open} onOpenChange={handleOpenChange}>
50
43
  {children}
51
44
  </RadixDialog.Root>
@@ -1,9 +1,8 @@
1
- import type { AnimationEvent, RefObject } from "react";
1
+ import type { RefObject } from "react";
2
2
 
3
3
  import { type SwipeEvent } from "./types";
4
4
 
5
5
  type UseSwipeToDismiss = {
6
- onAnimationEnd(event: AnimationEvent<HTMLDivElement>): void;
7
6
  onSwipeStart(): void;
8
7
  onSwipeMove(event: SwipeEvent): void;
9
8
  onSwipeCancel(): void;
@@ -12,7 +11,7 @@ type UseSwipeToDismiss = {
12
11
 
13
12
  export const useSwipeToDismiss = <T extends HTMLElement>(
14
13
  containerRef: RefObject<T | null>,
15
- handleOpenChange: (open: boolean) => void
14
+ handleOpenChange: ((open: boolean) => void) | null
16
15
  ): UseSwipeToDismiss => {
17
16
  const onSwipeStart = () => {
18
17
  if (!containerRef.current) {
@@ -44,14 +43,10 @@ export const useSwipeToDismiss = <T extends HTMLElement>(
44
43
  containerRef.current.setAttribute("data-swipe", "end");
45
44
  containerRef.current.style.removeProperty("--purpur-drawer-swipe-move-y");
46
45
  containerRef.current.style.setProperty("--purpur-drawer-swipe-end-y", `${y}px`);
46
+ handleOpenChange?.(false);
47
47
  };
48
- const onAnimationEnd = (event: AnimationEvent<HTMLDivElement>) => {
49
- if (event.currentTarget.getAttribute("data-swipe") === "end") {
50
- handleOpenChange(false);
51
- }
52
- };
48
+
53
49
  return {
54
- onAnimationEnd,
55
50
  onSwipeStart,
56
51
  onSwipeMove,
57
52
  onSwipeCancel,
@@ -1,2 +0,0 @@
1
- .purpur-drawer {
2
- }