@purpurds/drawer 8.3.0 → 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/LICENSE.txt +4 -4
- package/dist/drawer-content.d.ts.map +1 -1
- package/dist/drawer-frame.d.ts +0 -1
- package/dist/drawer-frame.d.ts.map +1 -1
- package/dist/drawer-header.d.ts.map +1 -1
- package/dist/drawer.cjs.js +14 -14
- package/dist/drawer.cjs.js.map +1 -1
- package/dist/drawer.d.ts.map +1 -1
- package/dist/drawer.es.js +1173 -1175
- package/dist/drawer.es.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/use-swipe-to-dismiss.hook.d.ts +2 -3
- package/dist/use-swipe-to-dismiss.hook.d.ts.map +1 -1
- package/package.json +8 -8
- package/src/drawer-container.module.scss +1 -5
- package/src/drawer-content.module.scss +71 -35
- package/src/drawer-content.tsx +17 -5
- package/src/drawer-frame.module.scss +28 -0
- package/src/drawer-frame.tsx +4 -9
- package/src/drawer-header.module.scss +11 -4
- package/src/drawer-header.tsx +4 -6
- package/src/drawer-scroll-area.module.scss +1 -0
- package/src/drawer.stories.tsx +37 -5
- package/src/drawer.tsx +1 -8
- package/src/use-swipe-to-dismiss.hook.ts +4 -9
- package/src/drawer.module.scss +0 -2
package/dist/styles.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
._purpur-drawer-
|
|
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 {
|
|
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,
|
|
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
|
+
"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/
|
|
22
|
-
"@purpurds/
|
|
23
|
-
"@purpurds/
|
|
24
|
-
"@purpurds/
|
|
25
|
-
"@purpurds/
|
|
26
|
-
"@purpurds/visually-hidden": "8.
|
|
27
|
-
"@purpurds/
|
|
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:
|
|
8
|
-
height:
|
|
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
|
-
|
|
15
|
-
|
|
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:
|
|
31
|
-
max-height:
|
|
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:
|
|
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:
|
|
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
|
|
113
|
+
@keyframes fadeIn {
|
|
105
114
|
from {
|
|
106
|
-
|
|
115
|
+
opacity: 0;
|
|
107
116
|
}
|
|
108
117
|
to {
|
|
109
|
-
|
|
118
|
+
opacity: 1;
|
|
110
119
|
}
|
|
111
120
|
}
|
|
112
121
|
|
|
113
|
-
@keyframes
|
|
122
|
+
@keyframes fadeOut {
|
|
114
123
|
from {
|
|
115
|
-
opacity:
|
|
124
|
+
opacity: 1;
|
|
116
125
|
}
|
|
117
126
|
to {
|
|
118
|
-
opacity:
|
|
127
|
+
opacity: 0;
|
|
119
128
|
}
|
|
120
129
|
}
|
|
121
130
|
|
|
122
|
-
@keyframes
|
|
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
|
|
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
|
|
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
|
+
}
|
package/src/drawer-content.tsx
CHANGED
|
@@ -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
|
|
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 {
|
|
82
|
-
|
|
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={
|
|
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 {
|
package/src/drawer-frame.tsx
CHANGED
|
@@ -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
|
-
|
|
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 {
|
package/src/drawer-header.tsx
CHANGED
|
@@ -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
|
-
{
|
|
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
|
-
{
|
|
112
|
-
backButtonText &&
|
|
113
|
-
onBackButtonClick &&
|
|
111
|
+
{hasBackButton &&
|
|
114
112
|
(headerContent ? (
|
|
115
113
|
<>
|
|
116
114
|
{headerContent}
|
package/src/drawer.stories.tsx
CHANGED
|
@@ -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
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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={
|
|
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 {
|
|
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
|
-
|
|
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,
|
package/src/drawer.module.scss
DELETED