@pantoia/ui 0.12.0 → 0.13.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/alert-dialog/index.tsx +2 -2
- package/bottom-sheet/index.tsx +2 -2
- package/dialog/index.tsx +2 -2
- package/drawer/index.tsx +2 -2
- package/package.json +1 -1
- package/registry.json +1 -3
- package/toast/index.module.css +11 -8
- package/toast/index.tsx +64 -37
- package/tokens.css +9 -0
package/alert-dialog/index.tsx
CHANGED
|
@@ -109,11 +109,11 @@ export function AlertDialogHeader({ lead, trail, className, children }: AlertDia
|
|
|
109
109
|
)
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
const alertDialogBody = tv({ base: '-m-1 min-h-0 overflow-y-auto p-1' })
|
|
112
|
+
const alertDialogBody = tv({ base: '-m-1 min-h-0 overflow-y-auto p-1 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-line-focus-ring' })
|
|
113
113
|
|
|
114
114
|
export function AlertDialogBody({ className, children }: { className?: string, children?: ReactNode }) {
|
|
115
115
|
return (
|
|
116
|
-
<div className={alertDialogBody({ className })}>
|
|
116
|
+
<div tabIndex={0} className={alertDialogBody({ className })}>
|
|
117
117
|
{children}
|
|
118
118
|
</div>
|
|
119
119
|
)
|
package/bottom-sheet/index.tsx
CHANGED
|
@@ -100,11 +100,11 @@ export function BottomSheetDescription({ className, ...rest }: BottomSheetDescri
|
|
|
100
100
|
)
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
const bottomSheetBody = tv({ base: '-m-1 min-h-0 overflow-y-auto p-1' })
|
|
103
|
+
const bottomSheetBody = tv({ base: '-m-1 min-h-0 overflow-y-auto p-1 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-line-focus-ring' })
|
|
104
104
|
|
|
105
105
|
export function BottomSheetBody({ className, children }: { className?: string, children?: ReactNode }) {
|
|
106
106
|
return (
|
|
107
|
-
<div className={bottomSheetBody({ className })}>
|
|
107
|
+
<div tabIndex={0} className={bottomSheetBody({ className })}>
|
|
108
108
|
{children}
|
|
109
109
|
</div>
|
|
110
110
|
)
|
package/dialog/index.tsx
CHANGED
|
@@ -108,11 +108,11 @@ export function DialogHeader({ lead, trail, className, children }: DialogHeaderP
|
|
|
108
108
|
)
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
const dialogBody = tv({ base: '-m-1 min-h-0 overflow-y-auto p-1' })
|
|
111
|
+
const dialogBody = tv({ base: '-m-1 min-h-0 overflow-y-auto p-1 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-line-focus-ring' })
|
|
112
112
|
|
|
113
113
|
export function DialogBody({ className, children }: { className?: string, children?: ReactNode }) {
|
|
114
114
|
return (
|
|
115
|
-
<div className={dialogBody({ className })}>
|
|
115
|
+
<div tabIndex={0} className={dialogBody({ className })}>
|
|
116
116
|
{children}
|
|
117
117
|
</div>
|
|
118
118
|
)
|
package/drawer/index.tsx
CHANGED
|
@@ -123,11 +123,11 @@ export function DrawerDescription({ className, ...rest }: DrawerDescriptionProps
|
|
|
123
123
|
)
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
const drawerBody = tv({ base: '-m-1 min-h-0 overflow-y-auto p-1' })
|
|
126
|
+
const drawerBody = tv({ base: '-m-1 min-h-0 overflow-y-auto p-1 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-line-focus-ring' })
|
|
127
127
|
|
|
128
128
|
export function DrawerBody({ className, children }: { className?: string, children?: ReactNode }) {
|
|
129
129
|
return (
|
|
130
|
-
<div className={drawerBody({ className })}>
|
|
130
|
+
<div tabIndex={0} className={drawerBody({ className })}>
|
|
131
131
|
{children}
|
|
132
132
|
</div>
|
|
133
133
|
)
|
package/package.json
CHANGED
package/registry.json
CHANGED
package/toast/index.module.css
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
--toast-inset: 16px;
|
|
24
24
|
position: fixed;
|
|
25
25
|
box-sizing: border-box;
|
|
26
|
-
width:
|
|
26
|
+
width: 352px;
|
|
27
27
|
margin: 0;
|
|
28
28
|
padding: 0;
|
|
29
29
|
outline: none;
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
touch-action: none;
|
|
49
49
|
opacity: 1;
|
|
50
50
|
transform: var(--toast-y);
|
|
51
|
+
backdrop-filter: blur(12px);
|
|
51
52
|
transition: transform 400ms, opacity 400ms, height 400ms, box-shadow 200ms;
|
|
52
53
|
}
|
|
53
54
|
|
|
@@ -62,6 +63,7 @@
|
|
|
62
63
|
.viewport[data-position^='top'] .toast { --toast-lift: 1; top: 0; bottom: auto; }
|
|
63
64
|
.viewport[data-position$='right'] .toast { right: 0; }
|
|
64
65
|
.viewport[data-position$='left'] .toast { left: 0; }
|
|
66
|
+
.viewport[data-position$='center'] .toast { left: 0; right: 0; margin-inline: auto; }
|
|
65
67
|
|
|
66
68
|
.toast:not([data-expanded]) {
|
|
67
69
|
--toast-y: translateY(calc(var(--toast-lift) * var(--toast-gap) * var(--toast-index))) scale(calc(1 - var(--toast-index) * 0.05));
|
|
@@ -69,6 +71,13 @@
|
|
|
69
71
|
|
|
70
72
|
.toast:not([data-expanded]):not([data-front]) {
|
|
71
73
|
height: var(--toast-frontmost-height);
|
|
74
|
+
width: var(--toast-frontmost-width, auto);
|
|
75
|
+
border-radius: 20px;
|
|
76
|
+
overflow: hidden;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.viewport:not([data-expand]) .toast:not([data-expanded]):not([data-front]) > * {
|
|
80
|
+
opacity: 0;
|
|
72
81
|
}
|
|
73
82
|
|
|
74
83
|
.toast[data-expanded],
|
|
@@ -162,13 +171,7 @@
|
|
|
162
171
|
.viewport {
|
|
163
172
|
right: var(--toast-inset);
|
|
164
173
|
left: var(--toast-inset);
|
|
165
|
-
width:
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
.viewport .toast {
|
|
169
|
-
right: 0;
|
|
170
|
-
left: 0;
|
|
171
|
-
width: calc(100% - var(--toast-inset) * 2);
|
|
174
|
+
width: auto;
|
|
172
175
|
}
|
|
173
176
|
|
|
174
177
|
.viewport[data-position$='center'] {
|
package/toast/index.tsx
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import { Toast as ToastPrimitive, type ToastManager, type ToastManagerAddOptions } from '@base-ui/react/toast'
|
|
4
|
-
import type
|
|
4
|
+
import { type MouseEvent, type ReactNode, useLayoutEffect, useRef, useState } from 'react'
|
|
5
5
|
|
|
6
|
-
import { button } from '../button'
|
|
7
6
|
import { tv } from '../tv'
|
|
8
7
|
|
|
9
8
|
import s from './index.module.css'
|
|
@@ -52,41 +51,41 @@ export const toast = createToast()
|
|
|
52
51
|
const toastViewport = tv({ base: 'z-toast' })
|
|
53
52
|
|
|
54
53
|
const toastRoot = tv({
|
|
55
|
-
base: '
|
|
56
|
-
|
|
57
|
-
type: {
|
|
58
|
-
success: 'text-fg-success',
|
|
59
|
-
error: 'text-fg-danger',
|
|
60
|
-
warning: 'text-fg-warning',
|
|
61
|
-
info: 'text-fg-info',
|
|
62
|
-
},
|
|
63
|
-
},
|
|
54
|
+
base: 'bg-bg-neutral-toast text-fg-neutral-toast-contrast inset-ring inset-ring-line-neutral-toast flex min-h-10 w-fit max-w-full items-center gap-2.5 rounded-full px-4.5 py-2.5'
|
|
55
|
+
+ ' data-multiline:rounded-r20 data-multiline:w-full data-multiline:items-start data-multiline:px-4 data-multiline:py-3',
|
|
64
56
|
})
|
|
65
57
|
|
|
58
|
+
const toastIcon = tv({ base: 'shrink-0 in-data-multiline:pt-px' })
|
|
59
|
+
|
|
66
60
|
const toastContent = tv({ base: 'min-w-0 flex-1' })
|
|
67
61
|
|
|
68
|
-
const toastTitle = tv({ base: 'text-
|
|
62
|
+
const toastTitle = tv({ base: 'text-t14 in-data-multiline:font-medium' })
|
|
63
|
+
|
|
64
|
+
const toastDescription = tv({ base: 'text-t13 text-fg-neutral-toast-contrast/72 line-clamp-2' })
|
|
69
65
|
|
|
70
|
-
const
|
|
66
|
+
const toastAction = tv({
|
|
67
|
+
base: 'text-t14 rounded-r4 shrink-0 cursor-pointer font-bold underline underline-offset-3 in-data-multiline:self-center'
|
|
68
|
+
+ ' focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-line-focus-ring',
|
|
69
|
+
})
|
|
71
70
|
|
|
72
71
|
const icons: Record<Kind, ReactNode> = {
|
|
73
72
|
success: (
|
|
74
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" height="
|
|
73
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" height="18" width="18" aria-hidden="true" className="size-4.5">
|
|
75
74
|
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clipRule="evenodd" />
|
|
76
75
|
</svg>
|
|
77
76
|
),
|
|
78
77
|
warning: (
|
|
79
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" height="
|
|
78
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" height="18" width="18" aria-hidden="true" className="size-4.5">
|
|
80
79
|
<path fillRule="evenodd" d="M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003zM12 8.25a.75.75 0 01.75.75v3.75a.75.75 0 01-1.5 0V9a.75.75 0 01.75-.75zm0 8.25a.75.75 0 100-1.5.75.75 0 000 1.5z" clipRule="evenodd" />
|
|
81
80
|
</svg>
|
|
82
81
|
),
|
|
83
82
|
info: (
|
|
84
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" height="
|
|
83
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" height="18" width="18" aria-hidden="true" className="size-4.5">
|
|
85
84
|
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clipRule="evenodd" />
|
|
86
85
|
</svg>
|
|
87
86
|
),
|
|
88
87
|
error: (
|
|
89
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" height="
|
|
88
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" height="18" width="18" aria-hidden="true" className="size-4.5">
|
|
90
89
|
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z" clipRule="evenodd" />
|
|
91
90
|
</svg>
|
|
92
91
|
),
|
|
@@ -101,30 +100,58 @@ function swipeOf(position: ToastPosition): Swipe[] {
|
|
|
101
100
|
return out
|
|
102
101
|
}
|
|
103
102
|
|
|
103
|
+
const ONE_LINE = 28
|
|
104
|
+
|
|
105
|
+
type Item = ReturnType<typeof ToastPrimitive.useToastManager>['toasts'][number]
|
|
106
|
+
|
|
107
|
+
function ToastItem({ item, front, swipe }: { item: Item, front: boolean, swipe: Swipe[] }) {
|
|
108
|
+
const title = useRef<HTMLHeadingElement>(null)
|
|
109
|
+
const root = useRef<HTMLDivElement>(null)
|
|
110
|
+
const [wrapped, setWrapped] = useState(false)
|
|
111
|
+
const icon = item.type ? icons[item.type as Kind] : undefined
|
|
112
|
+
const multiline = wrapped || Boolean(item.description)
|
|
113
|
+
|
|
114
|
+
useLayoutEffect(() => {
|
|
115
|
+
if (multiline || !title.current) return
|
|
116
|
+
if (title.current.offsetHeight > ONE_LINE) setWrapped(true)
|
|
117
|
+
}, [multiline])
|
|
118
|
+
|
|
119
|
+
useLayoutEffect(() => {
|
|
120
|
+
const el = root.current
|
|
121
|
+
const viewport = el?.parentElement
|
|
122
|
+
if (!front || !el || !viewport) return
|
|
123
|
+
const sync = () => viewport.style.setProperty('--toast-frontmost-width', `${el.offsetWidth}px`)
|
|
124
|
+
sync()
|
|
125
|
+
if (typeof ResizeObserver === 'undefined') return
|
|
126
|
+
const observer = new ResizeObserver(sync)
|
|
127
|
+
observer.observe(el)
|
|
128
|
+
return () => observer.disconnect()
|
|
129
|
+
}, [front])
|
|
130
|
+
|
|
131
|
+
return (
|
|
132
|
+
<ToastPrimitive.Root
|
|
133
|
+
ref={root}
|
|
134
|
+
toast={item}
|
|
135
|
+
swipeDirection={swipe}
|
|
136
|
+
data-front={front ? '' : undefined}
|
|
137
|
+
data-multiline={multiline ? '' : undefined}
|
|
138
|
+
className={toastRoot({ className: s.toast })}
|
|
139
|
+
>
|
|
140
|
+
{icon && <div className={toastIcon()}>{icon}</div>}
|
|
141
|
+
<ToastPrimitive.Content className={toastContent()}>
|
|
142
|
+
<ToastPrimitive.Title ref={title} className={toastTitle()} />
|
|
143
|
+
<ToastPrimitive.Description className={toastDescription()} />
|
|
144
|
+
</ToastPrimitive.Content>
|
|
145
|
+
<ToastPrimitive.Action className={toastAction()} />
|
|
146
|
+
</ToastPrimitive.Root>
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
|
|
104
150
|
function ToastList({ position }: { position: ToastPosition }) {
|
|
105
151
|
const { toasts } = ToastPrimitive.useToastManager()
|
|
106
152
|
const swipe = swipeOf(position)
|
|
107
153
|
|
|
108
|
-
return toasts.map((item, index) => {
|
|
109
|
-
const icon = item.type ? icons[item.type as Kind] : undefined
|
|
110
|
-
|
|
111
|
-
return (
|
|
112
|
-
<ToastPrimitive.Root
|
|
113
|
-
key={item.id}
|
|
114
|
-
toast={item}
|
|
115
|
-
swipeDirection={swipe}
|
|
116
|
-
data-front={index === 0 ? '' : undefined}
|
|
117
|
-
className={toastRoot({ type: item.type as Kind | undefined, className: s.toast })}
|
|
118
|
-
>
|
|
119
|
-
{icon && <div className="shrink-0">{icon}</div>}
|
|
120
|
-
<ToastPrimitive.Content className={toastContent()}>
|
|
121
|
-
<ToastPrimitive.Title className={toastTitle()} />
|
|
122
|
-
<ToastPrimitive.Description className={toastDescription()} />
|
|
123
|
-
</ToastPrimitive.Content>
|
|
124
|
-
<ToastPrimitive.Action className={button({ color: 'neutral', emphasis: 'weak', size: 'sm' })} />
|
|
125
|
-
</ToastPrimitive.Root>
|
|
126
|
-
)
|
|
127
|
-
})
|
|
154
|
+
return toasts.map((item, index) => <ToastItem key={item.id} item={item} front={index === 0} swipe={swipe} />)
|
|
128
155
|
}
|
|
129
156
|
|
|
130
157
|
export type ToastProps = Omit<ToastPrimitive.Provider.Props, 'children'> & {
|
package/tokens.css
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
--pds-bg-neutral-knob: #ffffff;
|
|
18
18
|
--pds-bg-neutral-knob-off: #ffffff;
|
|
19
19
|
--pds-bg-neutral-thumb: #ffffff;
|
|
20
|
+
--pds-bg-neutral-toast: rgba(25,31,40,0.94);
|
|
20
21
|
--pds-bg-neutral-track: var(--pds-gray-300);
|
|
21
22
|
--pds-bg-neutral-selected: rgba(2,32,71,0.05);
|
|
22
23
|
--pds-bg-neutral-selected-hover: rgba(0,27,55,0.1);
|
|
@@ -30,6 +31,7 @@
|
|
|
30
31
|
|
|
31
32
|
--pds-fg-brand-contrast: var(--pds-brand-700);
|
|
32
33
|
--pds-fg-brand-solid-contrast: #ffffff;
|
|
34
|
+
--pds-fg-neutral-toast-contrast: var(--pds-white);
|
|
33
35
|
--pds-fg-danger: var(--pds-red-500);
|
|
34
36
|
--pds-fg-danger-contrast: var(--pds-red-700);
|
|
35
37
|
--pds-fg-info: var(--pds-blue-500);
|
|
@@ -48,6 +50,7 @@
|
|
|
48
50
|
--pds-line-info: var(--pds-blue-500);
|
|
49
51
|
--pds-line-info-weak: var(--pds-blue-200);
|
|
50
52
|
--pds-line-neutral-solid: var(--pds-gray-900);
|
|
53
|
+
--pds-line-neutral-toast: transparent;
|
|
51
54
|
--pds-line-strong: var(--pds-gray-300);
|
|
52
55
|
--pds-line-success: var(--pds-green-500);
|
|
53
56
|
--pds-line-success-weak: var(--pds-green-200);
|
|
@@ -83,6 +86,7 @@
|
|
|
83
86
|
--pds-bg-neutral-knob: #2c2c35;
|
|
84
87
|
--pds-bg-neutral-knob-off: var(--pds-gray-800);
|
|
85
88
|
--pds-bg-neutral-thumb: #3c3c47;
|
|
89
|
+
--pds-bg-neutral-toast: rgba(0,0,0,0.92);
|
|
86
90
|
--pds-bg-neutral-track: var(--pds-gray-300);
|
|
87
91
|
--pds-bg-neutral-selected: rgba(217,217,255,0.11);
|
|
88
92
|
--pds-bg-neutral-selected-hover: rgba(222,222,255,0.19);
|
|
@@ -96,6 +100,7 @@
|
|
|
96
100
|
|
|
97
101
|
--pds-fg-brand-contrast: var(--pds-brand-700);
|
|
98
102
|
--pds-fg-brand-solid-contrast: #ffffff;
|
|
103
|
+
--pds-fg-neutral-toast-contrast: var(--pds-white);
|
|
99
104
|
--pds-fg-danger: var(--pds-red-500);
|
|
100
105
|
--pds-fg-danger-contrast: var(--pds-red-700);
|
|
101
106
|
--pds-fg-info: var(--pds-blue-500);
|
|
@@ -114,6 +119,7 @@
|
|
|
114
119
|
--pds-line-info: var(--pds-blue-500);
|
|
115
120
|
--pds-line-info-weak: var(--pds-blue-200);
|
|
116
121
|
--pds-line-neutral-solid: var(--pds-gray-900);
|
|
122
|
+
--pds-line-neutral-toast: rgba(255,255,255,0.1);
|
|
117
123
|
--pds-line-strong: var(--pds-gray-300);
|
|
118
124
|
--pds-line-success: var(--pds-green-500);
|
|
119
125
|
--pds-line-success-weak: var(--pds-green-200);
|
|
@@ -148,6 +154,7 @@
|
|
|
148
154
|
--color-bg-neutral-knob: var(--pds-bg-neutral-knob);
|
|
149
155
|
--color-bg-neutral-knob-off: var(--pds-bg-neutral-knob-off);
|
|
150
156
|
--color-bg-neutral-thumb: var(--pds-bg-neutral-thumb);
|
|
157
|
+
--color-bg-neutral-toast: var(--pds-bg-neutral-toast);
|
|
151
158
|
--color-bg-neutral-track: var(--pds-bg-neutral-track);
|
|
152
159
|
--color-bg-neutral-selected: var(--pds-bg-neutral-selected);
|
|
153
160
|
--color-bg-neutral-selected-hover: var(--pds-bg-neutral-selected-hover);
|
|
@@ -160,6 +167,7 @@
|
|
|
160
167
|
--color-bg-warning-weak: var(--pds-bg-warning-weak);
|
|
161
168
|
--color-fg-brand-contrast: var(--pds-fg-brand-contrast);
|
|
162
169
|
--color-fg-brand-solid-contrast: var(--pds-fg-brand-solid-contrast);
|
|
170
|
+
--color-fg-neutral-toast-contrast: var(--pds-fg-neutral-toast-contrast);
|
|
163
171
|
--color-fg-danger: var(--pds-fg-danger);
|
|
164
172
|
--color-fg-danger-contrast: var(--pds-fg-danger-contrast);
|
|
165
173
|
--color-fg-info: var(--pds-fg-info);
|
|
@@ -177,6 +185,7 @@
|
|
|
177
185
|
--color-line-info: var(--pds-line-info);
|
|
178
186
|
--color-line-info-weak: var(--pds-line-info-weak);
|
|
179
187
|
--color-line-neutral-solid: var(--pds-line-neutral-solid);
|
|
188
|
+
--color-line-neutral-toast: var(--pds-line-neutral-toast);
|
|
180
189
|
--color-line-strong: var(--pds-line-strong);
|
|
181
190
|
--color-line-success: var(--pds-line-success);
|
|
182
191
|
--color-line-success-weak: var(--pds-line-success-weak);
|