@pantoia/ui 0.12.1 → 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/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/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);
|