@hanzo/ui 3.7.0 → 3.7.22
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/package.json +11 -9
- package/primitives/drawer.tsx +17 -11
- package/primitives/index.ts +2 -0
- package/style/drawer.css +163 -0
- package/tsconfig.json +1 -1
- package/util/index-client.ts +3 -0
- package/util/index.ts +2 -0
- package/util/step-animation.ts +90 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/ui",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.22",
|
|
4
4
|
"description": "Library that contains shared UI primitives, support for a common design system, and other boilerplate support.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/",
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
"./style/": "./style/*",
|
|
40
40
|
"./tailwind": "./tailwind/index.ts",
|
|
41
41
|
"./types": "./types/index.ts",
|
|
42
|
-
"./util": "./util/index.ts"
|
|
42
|
+
"./util": "./util/index.ts",
|
|
43
|
+
"./util-client": "./util/index-client.ts"
|
|
43
44
|
},
|
|
44
45
|
"dependencies": {
|
|
45
46
|
"@next/third-parties": "^14.1.0",
|
|
@@ -82,19 +83,20 @@
|
|
|
82
83
|
"tailwind-merge": "^2.3.0",
|
|
83
84
|
"tailwindcss-animate": "^1.0.7",
|
|
84
85
|
"tailwindcss-interaction-media": "^0.1.0",
|
|
85
|
-
"
|
|
86
|
+
"@hanzo/react-drawer": "0.9.1"
|
|
86
87
|
},
|
|
87
88
|
"peerDependencies": {
|
|
88
89
|
"@hookform/resolvers": "^3.3.2",
|
|
89
90
|
"embla-carousel": "^8.0.2",
|
|
90
91
|
"lucide-react": "^0.344.0",
|
|
92
|
+
"mobx": "^6.12.0",
|
|
91
93
|
"next": "14.1.3",
|
|
92
94
|
"next-themes": "^0.2.1",
|
|
93
|
-
"react": "^18.
|
|
94
|
-
"react-dom": "^18.
|
|
95
|
-
"react-hook-form": "^7.51.
|
|
95
|
+
"react": "^18.3.1",
|
|
96
|
+
"react-dom": "^18.3.1",
|
|
97
|
+
"react-hook-form": "^7.51.4",
|
|
96
98
|
"validator": "^13.11.0",
|
|
97
|
-
"zod": "3.
|
|
99
|
+
"zod": "3.23.8"
|
|
98
100
|
},
|
|
99
101
|
"devDependencies": {
|
|
100
102
|
"@mdx-js/loader": "^3.0.0",
|
|
@@ -103,8 +105,8 @@
|
|
|
103
105
|
"@types/gtag.js": "^0.0.19",
|
|
104
106
|
"@types/lodash.merge": "^4.6.9",
|
|
105
107
|
"@types/mdx": "^2.0.13",
|
|
106
|
-
"@types/react": "^18.
|
|
107
|
-
"@types/react-dom": "^18.
|
|
108
|
+
"@types/react": "^18.3.1",
|
|
109
|
+
"@types/react-dom": "^18.3.0",
|
|
108
110
|
"embla-carousel": "^8.0.2",
|
|
109
111
|
"tailwindcss": "^3.4.3",
|
|
110
112
|
"typescript": "5.3.3"
|
package/primitives/drawer.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
|
-
import { Drawer as DrawerPrimitive } from '
|
|
4
|
+
import { Drawer as DrawerPrimitive, useDrawerContext } from '@hanzo/react-drawer'
|
|
5
5
|
|
|
6
6
|
import { cn } from '../util'
|
|
7
7
|
|
|
@@ -18,6 +18,7 @@ Drawer.displayName = 'Drawer'
|
|
|
18
18
|
|
|
19
19
|
const DrawerTrigger = DrawerPrimitive.Trigger
|
|
20
20
|
const DrawerPortal = DrawerPrimitive.Portal
|
|
21
|
+
const DrawerHandle = DrawerPrimitive.Handle
|
|
21
22
|
const DrawerClose = DrawerPrimitive.Close
|
|
22
23
|
|
|
23
24
|
const DrawerOverlay = React.forwardRef<
|
|
@@ -36,12 +37,16 @@ const DrawerContent = React.forwardRef<
|
|
|
36
37
|
React.ElementRef<typeof DrawerPrimitive.Content>,
|
|
37
38
|
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content> & {
|
|
38
39
|
overlayClx?: string
|
|
39
|
-
|
|
40
|
-
// Hack that avoids forking their code.
|
|
41
|
-
modal?: boolean
|
|
40
|
+
defaultHandle?: boolean
|
|
42
41
|
}
|
|
43
|
-
>(({
|
|
44
|
-
|
|
42
|
+
>(({
|
|
43
|
+
className,
|
|
44
|
+
children,
|
|
45
|
+
overlayClx='',
|
|
46
|
+
defaultHandle=true,
|
|
47
|
+
...props
|
|
48
|
+
}, ref) => {
|
|
49
|
+
|
|
45
50
|
return (
|
|
46
51
|
<DrawerPortal>
|
|
47
52
|
{/* If no or same z index, overlay should precede content */}
|
|
@@ -50,15 +55,14 @@ const DrawerContent = React.forwardRef<
|
|
|
50
55
|
ref={ref}
|
|
51
56
|
className={cn('fixed left-0 right-0 bottom-0 z-modal',
|
|
52
57
|
'mt-24 flex flex-col rounded-t-[10px] pt-6 border bg-background',
|
|
53
|
-
// 'h-[80%]'
|
|
58
|
+
// 'h-[80%]'
|
|
54
59
|
className
|
|
55
60
|
)}
|
|
56
|
-
// A bug / omission in Vaul
|
|
57
|
-
onFocusOutside={(e) => { if (!modal) { e.preventDefault(); return } }}
|
|
58
|
-
onEscapeKeyDown={(e) => { if (!modal) { e.preventDefault(); return } }}
|
|
59
61
|
{...props}
|
|
60
62
|
>
|
|
61
|
-
|
|
63
|
+
{defaultHandle && (
|
|
64
|
+
<div className='absolute left-0 right-0 mx-auto top-2 h-2 w-[100px] rounded-full bg-level-3 shrink-0' />
|
|
65
|
+
)}
|
|
62
66
|
{children}
|
|
63
67
|
</DrawerPrimitive.Content>
|
|
64
68
|
</DrawerPortal>
|
|
@@ -126,8 +130,10 @@ export {
|
|
|
126
130
|
DrawerTrigger,
|
|
127
131
|
DrawerClose,
|
|
128
132
|
DrawerContent,
|
|
133
|
+
DrawerHandle,
|
|
129
134
|
DrawerHeader,
|
|
130
135
|
DrawerFooter,
|
|
131
136
|
DrawerTitle,
|
|
132
137
|
DrawerDescription,
|
|
138
|
+
useDrawerContext
|
|
133
139
|
}
|
package/primitives/index.ts
CHANGED
package/style/drawer.css
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/* This is a copy of vaul/src/style.css */
|
|
2
|
+
|
|
3
|
+
[vaul-drawer] {
|
|
4
|
+
touch-action: none;
|
|
5
|
+
transition: transform 0.5s cubic-bezier(0.32, 0.72, 0, 1);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
[vaul-drawer][vaul-drawer-direction='bottom'] {
|
|
9
|
+
transform: translate3d(0, 100%, 0);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
[vaul-drawer][vaul-drawer-direction='top'] {
|
|
13
|
+
transform: translate3d(0, -100%, 0);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
[vaul-drawer][vaul-drawer-direction='left'] {
|
|
17
|
+
transform: translate3d(-100%, 0, 0);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
[vaul-drawer][vaul-drawer-direction='right'] {
|
|
21
|
+
transform: translate3d(100%, 0, 0);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.vaul-dragging .vaul-scrollable [vault-drawer-direction='top'] {
|
|
25
|
+
overflow-y: hidden !important;
|
|
26
|
+
}
|
|
27
|
+
.vaul-dragging .vaul-scrollable [vault-drawer-direction='bottom'] {
|
|
28
|
+
overflow-y: hidden !important;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.vaul-dragging .vaul-scrollable [vault-drawer-direction='left'] {
|
|
32
|
+
overflow-x: hidden !important;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.vaul-dragging .vaul-scrollable [vault-drawer-direction='right'] {
|
|
36
|
+
overflow-x: hidden !important;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
[vaul-drawer][vaul-drawer-visible='true'][vaul-drawer-direction='top'] {
|
|
40
|
+
transform: translate3d(0, var(--snap-point-height, 0), 0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
[vaul-drawer][vaul-drawer-visible='true'][vaul-drawer-direction='bottom'] {
|
|
44
|
+
transform: translate3d(0, var(--snap-point-height, 0), 0);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
[vaul-drawer][vaul-drawer-visible='true'][vaul-drawer-direction='left'] {
|
|
48
|
+
transform: translate3d(var(--snap-point-height, 0), 0, 0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
[vaul-drawer][vaul-drawer-visible='true'][vaul-drawer-direction='right'] {
|
|
52
|
+
transform: translate3d(var(--snap-point-height, 0), 0, 0);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
[vaul-overlay] {
|
|
56
|
+
opacity: 0;
|
|
57
|
+
transition: opacity 0.5s cubic-bezier(0.32, 0.72, 0, 1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
[vaul-overlay][vaul-drawer-visible='true'] {
|
|
61
|
+
opacity: 1;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
[vaul-drawer]::after {
|
|
65
|
+
content: '';
|
|
66
|
+
position: absolute;
|
|
67
|
+
background: inherit;
|
|
68
|
+
background-color: inherit;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
[vaul-drawer][vaul-drawer-direction='top']::after {
|
|
72
|
+
top: initial;
|
|
73
|
+
bottom: 100%;
|
|
74
|
+
left: 0;
|
|
75
|
+
right: 0;
|
|
76
|
+
height: 200%;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
[vaul-drawer][vaul-drawer-direction='bottom']::after {
|
|
80
|
+
top: 100%;
|
|
81
|
+
bottom: initial;
|
|
82
|
+
left: 0;
|
|
83
|
+
right: 0;
|
|
84
|
+
height: 200%;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
[vaul-drawer][vaul-drawer-direction='left']::after {
|
|
88
|
+
left: initial;
|
|
89
|
+
right: 100%;
|
|
90
|
+
top: 0;
|
|
91
|
+
bottom: 0;
|
|
92
|
+
width: 200%;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
[vaul-drawer][vaul-drawer-direction='right']::after {
|
|
96
|
+
left: 100%;
|
|
97
|
+
right: initial;
|
|
98
|
+
top: 0;
|
|
99
|
+
bottom: 0;
|
|
100
|
+
width: 200%;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
[vaul-handle] {
|
|
104
|
+
display: block;
|
|
105
|
+
position: relative;
|
|
106
|
+
opacity: 0.8;
|
|
107
|
+
margin-left: auto;
|
|
108
|
+
margin-right: auto;
|
|
109
|
+
height: 5px;
|
|
110
|
+
width: 56px;
|
|
111
|
+
border-radius: 1rem;
|
|
112
|
+
touch-action: pan-y;
|
|
113
|
+
cursor: grab;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
[vaul-handle]:hover,
|
|
117
|
+
[vaul-handle]:active {
|
|
118
|
+
opacity: 1;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
[vaul-handle]:active {
|
|
122
|
+
cursor: grabbing;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
[vaul-handle-hitarea] {
|
|
126
|
+
position: absolute;
|
|
127
|
+
left: 50%;
|
|
128
|
+
top: 50%;
|
|
129
|
+
transform: translate(-50%, -50%);
|
|
130
|
+
width: max(100%, 2.75rem); /* 44px */
|
|
131
|
+
height: max(100%, 2.75rem); /* 44px */
|
|
132
|
+
touch-action: inherit;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
[vaul-overlay][vaul-snap-points='true']:not([vaul-snap-points-overlay='true']):not([data-state='closed']) {
|
|
136
|
+
opacity: 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
[vaul-overlay][vaul-snap-points-overlay='true']:not([vaul-drawer-visible='false']) {
|
|
140
|
+
opacity: 1;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* This will allow us to not animate via animation, but still benefit from delaying unmount via Radix. */
|
|
144
|
+
@keyframes fake-animation {
|
|
145
|
+
from {
|
|
146
|
+
}
|
|
147
|
+
to {
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@media (pointer: fine) {
|
|
152
|
+
[vaul-handle-hitarea] {
|
|
153
|
+
width: 100%;
|
|
154
|
+
height: 100%;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
@media (hover: hover) and (pointer: fine) {
|
|
159
|
+
[vaul-drawer] {
|
|
160
|
+
user-select: none;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
package/tsconfig.json
CHANGED
package/util/index.ts
CHANGED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react'
|
|
2
|
+
import { makeObservable, reaction, computed, type IReactionDisposer, observable, action } from 'mobx'
|
|
3
|
+
|
|
4
|
+
interface StepAnimation {
|
|
5
|
+
notPast(step: number): boolean
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
class MyStepAnimation implements StepAnimation {
|
|
9
|
+
|
|
10
|
+
_step: number = -1
|
|
11
|
+
_reactionDisposer: IReactionDisposer | undefined = undefined
|
|
12
|
+
|
|
13
|
+
_initialStep: () => boolean
|
|
14
|
+
_intervals: number[]
|
|
15
|
+
|
|
16
|
+
/** initialStep: false -> true: step 0
|
|
17
|
+
true -> false: step 1
|
|
18
|
+
after intervals[0] : step 2
|
|
19
|
+
after intervals[1] : step 3
|
|
20
|
+
|
|
21
|
+
initialStep must contain at least one mobx observable and return boolean
|
|
22
|
+
see: https://mobx.js.org/reactions.html#reaction
|
|
23
|
+
*/
|
|
24
|
+
constructor(initialStep: () => boolean, intervals: number[]) {
|
|
25
|
+
|
|
26
|
+
this._initialStep = initialStep
|
|
27
|
+
this._intervals = intervals
|
|
28
|
+
|
|
29
|
+
makeObservable(this, {
|
|
30
|
+
_step: observable,
|
|
31
|
+
_setStep: action
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// This is separated out because reactions have to be created
|
|
36
|
+
// once we have a valid doc / window etc. (mobx internals)
|
|
37
|
+
// Can't just do it in constructor and assign to ref
|
|
38
|
+
initialize = () => {
|
|
39
|
+
|
|
40
|
+
const fireNext = () => {
|
|
41
|
+
this._setStep(this._step + 1)
|
|
42
|
+
if (this._step <= this._intervals.length) {
|
|
43
|
+
// No need to call clearTimeout(): https://stackoverflow.com/a/7391588/11645689
|
|
44
|
+
setTimeout(() => { fireNext() }, this._intervals[this._step - 1])
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
this._reactionDisposer = reaction(
|
|
49
|
+
this._initialStep,
|
|
50
|
+
(triggered: boolean) => {
|
|
51
|
+
if (triggered && this._step === -1) {
|
|
52
|
+
this._setStep(0)
|
|
53
|
+
}
|
|
54
|
+
// extra safe
|
|
55
|
+
else if (this._step === 0) {
|
|
56
|
+
fireNext()
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
_setStep = (v: number): void => {this._step = v}
|
|
63
|
+
|
|
64
|
+
dispose = () => {
|
|
65
|
+
if (this._reactionDisposer) {
|
|
66
|
+
this._reactionDisposer()
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// https://mobx.js.org/computeds-with-args.html#2-close-over-the-arguments
|
|
71
|
+
notPast = (step: number): boolean => (
|
|
72
|
+
computed(() => (this._step > -1 && this._step <= step)).get()
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const useStepAnimation = (initialStep: () => boolean, intervals: number[]): StepAnimation => {
|
|
77
|
+
|
|
78
|
+
const animRef = useRef<MyStepAnimation>(new MyStepAnimation(initialStep, intervals))
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
animRef.current.initialize()
|
|
81
|
+
return animRef.current.dispose
|
|
82
|
+
}, [])
|
|
83
|
+
|
|
84
|
+
return animRef.current
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export {
|
|
88
|
+
type StepAnimation,
|
|
89
|
+
useStepAnimation
|
|
90
|
+
}
|