@pantoia/ui 0.3.0 → 0.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/accordion/index.module.css +4 -4
- package/accordion/index.tsx +21 -51
- package/alert-dialog/index.tsx +36 -42
- package/badge/index.tsx +1 -1
- package/button/index.tsx +31 -30
- package/callout/index.tsx +4 -4
- package/checkbox/index.tsx +20 -85
- package/combobox/index.tsx +122 -316
- package/command-palette/index.tsx +51 -32
- package/dialog/index.tsx +28 -24
- package/dist/index.js +5 -5
- package/drawer/index.module.css +2 -2
- package/drawer/index.tsx +52 -130
- package/dropdown-menu/index.tsx +87 -95
- package/field/index.tsx +27 -86
- package/input/index.tsx +7 -3
- package/kbd/index.tsx +2 -2
- package/label/index.tsx +6 -8
- package/number-field/index.tsx +122 -173
- package/package.json +2 -6
- package/popover/index.tsx +61 -32
- package/radio-group/index.tsx +35 -38
- package/registry.json +64 -154
- package/select/index.tsx +141 -70
- package/switch/index.tsx +13 -6
- package/table/index.tsx +1 -2
- package/tabs/index.module.css +3 -3
- package/tabs/index.tsx +22 -76
- package/textarea/index.tsx +19 -7
- package/toast/index.module.css +187 -0
- package/toast/index.tsx +141 -46
- package/toggle/index.tsx +4 -3
- package/tokens.css +7 -1
- package/tooltip/index.tsx +61 -30
- package/utilities.css +11 -6
- package/accordion/AGENTS.md +0 -60
- package/alert-dialog/AGENTS.md +0 -78
- package/badge/AGENTS.md +0 -35
- package/button/AGENTS.md +0 -39
- package/callout/AGENTS.md +0 -37
- package/checkbox/AGENTS.md +0 -54
- package/combobox/AGENTS.md +0 -150
- package/command-palette/AGENTS.md +0 -41
- package/dialog/AGENTS.md +0 -81
- package/drawer/AGENTS.md +0 -66
- package/dropdown-menu/AGENTS.md +0 -133
- package/field/AGENTS.md +0 -92
- package/input/AGENTS.md +0 -35
- package/kbd/AGENTS.md +0 -17
- package/label/AGENTS.md +0 -21
- package/number-field/AGENTS.md +0 -49
- package/popover/AGENTS.md +0 -64
- package/radio-group/AGENTS.md +0 -81
- package/select/AGENTS.md +0 -90
- package/switch/AGENTS.md +0 -37
- package/table/AGENTS.md +0 -82
- package/tabs/AGENTS.md +0 -61
- package/test-utils.ts +0 -10
- package/textarea/AGENTS.md +0 -35
- package/toast/AGENTS.md +0 -34
- package/toggle/AGENTS.md +0 -37
- package/tooltip/AGENTS.md +0 -50
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
@keyframes swipe-out-left {
|
|
2
|
+
from { transform: var(--toast-y) translateX(var(--toast-swipe-movement-x)); opacity: 1; }
|
|
3
|
+
to { transform: var(--toast-y) translateX(calc(var(--toast-swipe-movement-x) - 100%)); opacity: 0; }
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
@keyframes swipe-out-right {
|
|
7
|
+
from { transform: var(--toast-y) translateX(var(--toast-swipe-movement-x)); opacity: 1; }
|
|
8
|
+
to { transform: var(--toast-y) translateX(calc(var(--toast-swipe-movement-x) + 100%)); opacity: 0; }
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
@keyframes swipe-out-up {
|
|
12
|
+
from { transform: var(--toast-y) translateY(var(--toast-swipe-movement-y)); opacity: 1; }
|
|
13
|
+
to { transform: var(--toast-y) translateY(calc(var(--toast-swipe-movement-y) - 100%)); opacity: 0; }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@keyframes swipe-out-down {
|
|
17
|
+
from { transform: var(--toast-y) translateY(var(--toast-swipe-movement-y)); opacity: 1; }
|
|
18
|
+
to { transform: var(--toast-y) translateY(calc(var(--toast-swipe-movement-y) + 100%)); opacity: 0; }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.viewport {
|
|
22
|
+
--toast-gap: 8px;
|
|
23
|
+
--toast-inset: 16px;
|
|
24
|
+
position: fixed;
|
|
25
|
+
box-sizing: border-box;
|
|
26
|
+
width: 356px;
|
|
27
|
+
margin: 0;
|
|
28
|
+
padding: 0;
|
|
29
|
+
outline: none;
|
|
30
|
+
transition: transform 400ms ease;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.viewport[data-position$='right'] { right: var(--toast-inset); }
|
|
34
|
+
.viewport[data-position$='left'] { left: var(--toast-inset); }
|
|
35
|
+
.viewport[data-position$='center'] { left: 50%; transform: translateX(-50%); }
|
|
36
|
+
.viewport[data-position^='top'] { top: var(--toast-inset); }
|
|
37
|
+
.viewport[data-position^='bottom'] { bottom: var(--toast-inset); }
|
|
38
|
+
|
|
39
|
+
.toast {
|
|
40
|
+
--toast-lift: -1;
|
|
41
|
+
--toast-y: translateY(0);
|
|
42
|
+
position: absolute;
|
|
43
|
+
bottom: 0;
|
|
44
|
+
z-index: calc(1000 - var(--toast-index));
|
|
45
|
+
box-sizing: border-box;
|
|
46
|
+
outline: none;
|
|
47
|
+
overflow-wrap: anywhere;
|
|
48
|
+
touch-action: none;
|
|
49
|
+
opacity: 1;
|
|
50
|
+
transform: var(--toast-y);
|
|
51
|
+
transition: transform 400ms, opacity 400ms, height 400ms, box-shadow 200ms;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.toast:focus-visible {
|
|
55
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(0, 0, 0, 0.2);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.toast > * {
|
|
59
|
+
transition: opacity 400ms;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.viewport[data-position^='top'] .toast { --toast-lift: 1; top: 0; bottom: auto; }
|
|
63
|
+
.viewport[data-position$='right'] .toast { right: 0; }
|
|
64
|
+
.viewport[data-position$='left'] .toast { left: 0; }
|
|
65
|
+
|
|
66
|
+
.toast:not([data-expanded]) {
|
|
67
|
+
--toast-y: translateY(calc(var(--toast-lift) * var(--toast-gap) * var(--toast-index))) scale(calc(1 - var(--toast-index) * 0.05));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.toast:not([data-expanded]):not([data-front]) {
|
|
71
|
+
height: var(--toast-frontmost-height);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.toast[data-expanded],
|
|
75
|
+
.viewport[data-expand] .toast {
|
|
76
|
+
--toast-y: translateY(calc(var(--toast-lift) * (var(--toast-offset-y) + var(--toast-index) * var(--toast-gap))));
|
|
77
|
+
height: var(--toast-height);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.viewport[data-expand] .toast {
|
|
81
|
+
height: auto;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.toast[data-expanded]::after,
|
|
85
|
+
.viewport[data-expand] .toast::after {
|
|
86
|
+
content: '';
|
|
87
|
+
position: absolute;
|
|
88
|
+
bottom: 100%;
|
|
89
|
+
left: 0;
|
|
90
|
+
width: 100%;
|
|
91
|
+
height: calc(var(--toast-gap) + 1px);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.viewport .toast[data-starting-style] {
|
|
95
|
+
--toast-y: translateY(calc(var(--toast-lift) * -100%));
|
|
96
|
+
opacity: 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.toast[data-limited] {
|
|
100
|
+
opacity: 0;
|
|
101
|
+
pointer-events: none;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.viewport .toast[data-ending-style]:not([data-swipe-direction]) {
|
|
105
|
+
opacity: 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.viewport .toast[data-ending-style][data-front]:not([data-swipe-direction]) {
|
|
109
|
+
--toast-y: translateY(calc(var(--toast-lift) * -100%));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.viewport .toast[data-ending-style][data-expanded]:not([data-front]):not([data-swipe-direction]),
|
|
113
|
+
.viewport[data-expand] .toast[data-ending-style]:not([data-front]):not([data-swipe-direction]) {
|
|
114
|
+
--toast-y: translateY(calc(var(--toast-lift) * (var(--toast-offset-y) + var(--toast-index) * var(--toast-gap)) + var(--toast-lift) * -100%));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.viewport:not([data-expand]) .toast[data-ending-style]:not([data-expanded]):not([data-front]):not([data-swipe-direction]) {
|
|
118
|
+
--toast-y: translateY(40%);
|
|
119
|
+
transition: transform 500ms, opacity 200ms;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.toast[data-swiping] {
|
|
123
|
+
transition: none;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.toast[data-swiping]::before {
|
|
127
|
+
content: '';
|
|
128
|
+
position: absolute;
|
|
129
|
+
left: -100%;
|
|
130
|
+
right: -100%;
|
|
131
|
+
z-index: -1;
|
|
132
|
+
height: 100%;
|
|
133
|
+
top: 50%;
|
|
134
|
+
transform: scaleY(3) translateY(-50%);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.viewport[data-position^='top'] .toast[data-swiping]::before {
|
|
138
|
+
top: auto;
|
|
139
|
+
bottom: 50%;
|
|
140
|
+
transform: scaleY(3) translateY(50%);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.toast[data-ending-style]:not([data-swiping])::before {
|
|
144
|
+
content: '';
|
|
145
|
+
position: absolute;
|
|
146
|
+
inset: 0;
|
|
147
|
+
transform: scaleY(2);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.toast[data-ending-style][data-swipe-direction] {
|
|
151
|
+
animation-duration: 200ms;
|
|
152
|
+
animation-timing-function: ease-out;
|
|
153
|
+
animation-fill-mode: forwards;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.toast[data-ending-style][data-swipe-direction='left'] { animation-name: swipe-out-left; }
|
|
157
|
+
.toast[data-ending-style][data-swipe-direction='right'] { animation-name: swipe-out-right; }
|
|
158
|
+
.toast[data-ending-style][data-swipe-direction='up'] { animation-name: swipe-out-up; }
|
|
159
|
+
.toast[data-ending-style][data-swipe-direction='down'] { animation-name: swipe-out-down; }
|
|
160
|
+
|
|
161
|
+
@media (max-width: 600px) {
|
|
162
|
+
.viewport {
|
|
163
|
+
right: var(--toast-inset);
|
|
164
|
+
left: var(--toast-inset);
|
|
165
|
+
width: 100%;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.viewport .toast {
|
|
169
|
+
right: 0;
|
|
170
|
+
left: 0;
|
|
171
|
+
width: calc(100% - var(--toast-inset) * 2);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.viewport[data-position$='center'] {
|
|
175
|
+
left: var(--toast-inset);
|
|
176
|
+
right: var(--toast-inset);
|
|
177
|
+
transform: none;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@media (prefers-reduced-motion) {
|
|
182
|
+
.toast,
|
|
183
|
+
.toast > * {
|
|
184
|
+
transition: none !important;
|
|
185
|
+
animation: none !important;
|
|
186
|
+
}
|
|
187
|
+
}
|
package/toast/index.tsx
CHANGED
|
@@ -1,65 +1,160 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import type
|
|
4
|
-
import {
|
|
3
|
+
import { Toast as ToastPrimitive, type ToastManager, type ToastManagerAddOptions } from '@base-ui/react/toast'
|
|
4
|
+
import type { MouseEvent, ReactNode } from 'react'
|
|
5
5
|
|
|
6
|
+
import { button } from '../button'
|
|
6
7
|
import { tv } from '../tv'
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
import s from './index.module.css'
|
|
9
10
|
|
|
10
|
-
export
|
|
11
|
+
export type ToastPosition = 'top-left' | 'top-center' | 'top-right'
|
|
12
|
+
| 'bottom-left' | 'bottom-center' | 'bottom-right'
|
|
11
13
|
|
|
12
|
-
export
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
export type ToastOptions = Omit<ToastManagerAddOptions<object>, 'title' | 'type' | 'actionProps' | 'transitionStatus'> & {
|
|
15
|
+
action?: {
|
|
16
|
+
label: ReactNode
|
|
17
|
+
onClick: (event: MouseEvent<HTMLButtonElement>) => void
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type Kind = 'success' | 'error' | 'warning' | 'info'
|
|
22
|
+
|
|
23
|
+
export function createToast(manager: ToastManager = ToastPrimitive.createToastManager()) {
|
|
24
|
+
const show = (type?: Kind) => (message: ReactNode, { action, ...options }: ToastOptions = {}) => {
|
|
25
|
+
const id: string = manager.add({
|
|
26
|
+
...options,
|
|
27
|
+
type,
|
|
28
|
+
title: message,
|
|
29
|
+
actionProps: action && {
|
|
30
|
+
children: action.label,
|
|
31
|
+
onClick: event => {
|
|
32
|
+
action.onClick(event)
|
|
33
|
+
if (!event.defaultPrevented) manager.close(id)
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
})
|
|
37
|
+
return id
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return Object.assign(show(), {
|
|
41
|
+
success: show('success'),
|
|
42
|
+
error: show('error'),
|
|
43
|
+
warning: show('warning'),
|
|
44
|
+
info: show('info'),
|
|
45
|
+
dismiss: (id?: string) => manager.close(id),
|
|
46
|
+
manager,
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const toast = createToast()
|
|
51
|
+
|
|
52
|
+
const toastViewport = tv({ base: 'z-toast' })
|
|
53
|
+
|
|
54
|
+
const toastRoot = tv({
|
|
55
|
+
base: 'float rounded-r8 flex w-full items-center gap-stack p-4',
|
|
56
|
+
variants: {
|
|
57
|
+
type: {
|
|
58
|
+
success: 'text-fg-success',
|
|
59
|
+
error: 'text-fg-danger',
|
|
60
|
+
warning: 'text-fg-warning',
|
|
61
|
+
info: 'text-fg-info',
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
const toastContent = tv({ base: 'min-w-0 flex-1' })
|
|
67
|
+
|
|
68
|
+
const toastTitle = tv({ base: 'text-label text-fg' })
|
|
69
|
+
|
|
70
|
+
const toastDescription = tv({ base: 'text-caption text-fg-muted' })
|
|
71
|
+
|
|
72
|
+
const icons: Record<Kind, ReactNode> = {
|
|
73
|
+
success: (
|
|
74
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" height="20" width="20" aria-hidden="true" className="size-5">
|
|
75
|
+
<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
|
+
</svg>
|
|
77
|
+
),
|
|
78
|
+
warning: (
|
|
79
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" height="20" width="20" aria-hidden="true" className="size-5">
|
|
80
|
+
<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
|
+
</svg>
|
|
82
|
+
),
|
|
83
|
+
info: (
|
|
84
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" height="20" width="20" aria-hidden="true" className="size-5">
|
|
85
|
+
<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
|
+
</svg>
|
|
87
|
+
),
|
|
88
|
+
error: (
|
|
89
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" height="20" width="20" aria-hidden="true" className="size-5">
|
|
90
|
+
<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
|
+
</svg>
|
|
92
|
+
),
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
type Swipe = 'up' | 'down' | 'left' | 'right'
|
|
96
|
+
|
|
97
|
+
function swipeOf(position: ToastPosition): Swipe[] {
|
|
98
|
+
const [y, x] = position.split('-')
|
|
99
|
+
const out: Swipe[] = [y === 'top' ? 'up' : 'down']
|
|
100
|
+
if (x === 'left' || x === 'right') out.push(x)
|
|
101
|
+
return out
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function ToastList({ position }: { position: ToastPosition }) {
|
|
105
|
+
const { toasts } = ToastPrimitive.useToastManager()
|
|
106
|
+
const swipe = swipeOf(position)
|
|
107
|
+
|
|
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
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export type ToastProps = Omit<ToastPrimitive.Provider.Props, 'children'> & {
|
|
131
|
+
position?: ToastPosition
|
|
18
132
|
expand?: boolean
|
|
133
|
+
className?: string
|
|
134
|
+
'aria-label'?: string
|
|
19
135
|
}
|
|
20
136
|
|
|
21
137
|
export function Toast({
|
|
22
|
-
|
|
138
|
+
toastManager = toast.manager,
|
|
139
|
+
timeout = 4000,
|
|
23
140
|
position = 'bottom-center',
|
|
24
|
-
duration = 4000,
|
|
25
141
|
expand = false,
|
|
26
|
-
offset = 16,
|
|
27
|
-
gap = 8,
|
|
28
142
|
className,
|
|
29
|
-
|
|
143
|
+
'aria-label': label,
|
|
30
144
|
...rest
|
|
31
145
|
}: ToastProps) {
|
|
32
146
|
return (
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
classNames: {
|
|
46
|
-
toast: 'float rounded-r8'
|
|
47
|
-
+ ' flex w-full items-center gap-stack p-4',
|
|
48
|
-
content: 'min-w-0 flex-1',
|
|
49
|
-
title: 'text-label text-fg',
|
|
50
|
-
description: 'text-caption text-fg-muted',
|
|
51
|
-
icon: 'shrink-0',
|
|
52
|
-
actionButton: 'text-label text-fg shrink-0 rounded-r8 px-3 h-8'
|
|
53
|
-
+ ' bg-bg-well transition-state press press-shrink enabled:hover:bg-bg-neutral-solid/10'
|
|
54
|
-
+ ' focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-line-focus-ring'
|
|
55
|
-
+ ' disabled:text-fg-disabled disabled:cursor-not-allowed',
|
|
56
|
-
success: 'text-fg-success',
|
|
57
|
-
error: 'text-fg-danger',
|
|
58
|
-
warning: 'text-fg-warning',
|
|
59
|
-
info: 'text-fg-info',
|
|
60
|
-
...toastOptions?.classNames,
|
|
61
|
-
},
|
|
62
|
-
}}
|
|
63
|
-
/>
|
|
147
|
+
<ToastPrimitive.Provider {...rest} toastManager={toastManager} timeout={timeout}>
|
|
148
|
+
<ToastPrimitive.Portal>
|
|
149
|
+
<ToastPrimitive.Viewport
|
|
150
|
+
{...(label && { 'aria-label': label })}
|
|
151
|
+
data-position={position}
|
|
152
|
+
data-expand={expand ? '' : undefined}
|
|
153
|
+
className={toastViewport({ className: [s.viewport, className] })}
|
|
154
|
+
>
|
|
155
|
+
<ToastList position={position} />
|
|
156
|
+
</ToastPrimitive.Viewport>
|
|
157
|
+
</ToastPrimitive.Portal>
|
|
158
|
+
</ToastPrimitive.Provider>
|
|
64
159
|
)
|
|
65
160
|
}
|
package/toggle/index.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { Toggle as TogglePrimitive } from '
|
|
3
|
+
import { Toggle as TogglePrimitive } from '@base-ui/react/toggle'
|
|
4
4
|
import type { ComponentProps } from 'react'
|
|
5
5
|
|
|
6
6
|
import { tv } from '../tv'
|
|
@@ -28,10 +28,11 @@ export const toggle = tv({
|
|
|
28
28
|
defaultVariants: { surface: 'bare' },
|
|
29
29
|
})
|
|
30
30
|
|
|
31
|
-
export
|
|
31
|
+
export type ToggleProps = Omit<ComponentProps<typeof TogglePrimitive>, 'className'> & {
|
|
32
32
|
surface?: 'bare' | 'well' | 'panel'
|
|
33
|
+
className?: string
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
export function Toggle({ surface, className, ...rest }: ToggleProps) {
|
|
36
|
-
return <TogglePrimitive
|
|
37
|
+
return <TogglePrimitive {...rest} className={toggle({ surface, className })} />
|
|
37
38
|
}
|
package/tokens.css
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
--pds-bg-neutral-disabled: var(--pds-gray-200);
|
|
16
16
|
--pds-bg-neutral-hover: rgba(0,23,51,0.02);
|
|
17
17
|
--pds-bg-neutral-knob: #ffffff;
|
|
18
|
+
--pds-bg-neutral-knob-off: #ffffff;
|
|
19
|
+
--pds-bg-neutral-track: var(--pds-gray-300);
|
|
18
20
|
--pds-bg-neutral-selected: rgba(2,32,71,0.05);
|
|
19
21
|
--pds-bg-neutral-selected-hover: rgba(0,27,55,0.1);
|
|
20
22
|
--pds-bg-neutral-solid: var(--pds-gray-900);
|
|
@@ -64,7 +66,7 @@
|
|
|
64
66
|
--pds-bg: #17171c;
|
|
65
67
|
--pds-bg-overlay: rgba(0,0,0,0.56);
|
|
66
68
|
--pds-bg-panel: #202027;
|
|
67
|
-
--pds-bg-well: #
|
|
69
|
+
--pds-bg-well: #2c2c35;
|
|
68
70
|
|
|
69
71
|
--pds-bg-brand-solid: var(--pds-brand-500);
|
|
70
72
|
--pds-bg-brand-solid-hover: var(--pds-brand-600);
|
|
@@ -77,6 +79,8 @@
|
|
|
77
79
|
--pds-bg-neutral-disabled: var(--pds-gray-200);
|
|
78
80
|
--pds-bg-neutral-hover: rgba(209,209,253,0.05);
|
|
79
81
|
--pds-bg-neutral-knob: #2c2c35;
|
|
82
|
+
--pds-bg-neutral-knob-off: var(--pds-gray-800);
|
|
83
|
+
--pds-bg-neutral-track: var(--pds-gray-300);
|
|
80
84
|
--pds-bg-neutral-selected: rgba(217,217,255,0.11);
|
|
81
85
|
--pds-bg-neutral-selected-hover: rgba(222,222,255,0.19);
|
|
82
86
|
--pds-bg-neutral-solid: var(--pds-gray-900);
|
|
@@ -138,6 +142,8 @@
|
|
|
138
142
|
--color-bg-neutral-disabled: var(--pds-bg-neutral-disabled);
|
|
139
143
|
--color-bg-neutral-hover: var(--pds-bg-neutral-hover);
|
|
140
144
|
--color-bg-neutral-knob: var(--pds-bg-neutral-knob);
|
|
145
|
+
--color-bg-neutral-knob-off: var(--pds-bg-neutral-knob-off);
|
|
146
|
+
--color-bg-neutral-track: var(--pds-bg-neutral-track);
|
|
141
147
|
--color-bg-neutral-selected: var(--pds-bg-neutral-selected);
|
|
142
148
|
--color-bg-neutral-selected-hover: var(--pds-bg-neutral-selected-hover);
|
|
143
149
|
--color-bg-neutral-solid: var(--pds-bg-neutral-solid);
|
package/tooltip/index.tsx
CHANGED
|
@@ -1,35 +1,55 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { Tooltip as TooltipPrimitive } from '
|
|
4
|
-
import
|
|
3
|
+
import { Tooltip as TooltipPrimitive } from '@base-ui/react/tooltip'
|
|
4
|
+
import { createContext, useContext, useId, useState } from 'react'
|
|
5
|
+
import type { ComponentProps } from 'react'
|
|
5
6
|
|
|
6
7
|
import { tv } from '../tv'
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
const TooltipContext = createContext<{ id?: string, open: boolean }>({ open: false })
|
|
10
|
+
const ProviderContext = createContext(false)
|
|
11
|
+
|
|
12
|
+
export type TooltipProviderProps = ComponentProps<typeof TooltipPrimitive.Provider>
|
|
13
|
+
|
|
14
|
+
export function TooltipProvider({ delay = 100, ...rest }: TooltipProviderProps) {
|
|
15
|
+
return (
|
|
16
|
+
<ProviderContext.Provider value>
|
|
17
|
+
<TooltipPrimitive.Provider {...rest} delay={delay} />
|
|
18
|
+
</ProviderContext.Provider>
|
|
19
|
+
)
|
|
14
20
|
}
|
|
15
21
|
|
|
16
|
-
export
|
|
22
|
+
export type TooltipProps = ComponentProps<typeof TooltipPrimitive.Root>
|
|
23
|
+
|
|
24
|
+
export function Tooltip({ open, defaultOpen = false, onOpenChange, ...rest }: TooltipProps) {
|
|
25
|
+
const id = useId()
|
|
26
|
+
const [seen, setSeen] = useState(defaultOpen)
|
|
27
|
+
const isOpen = open ?? seen
|
|
28
|
+
|
|
29
|
+
const change: TooltipProps['onOpenChange'] = (next, eventDetails) => {
|
|
30
|
+
setSeen(next)
|
|
31
|
+
onOpenChange?.(next, eventDetails)
|
|
32
|
+
}
|
|
33
|
+
|
|
17
34
|
return (
|
|
18
|
-
<
|
|
19
|
-
<TooltipPrimitive.Root
|
|
20
|
-
|
|
21
|
-
</TooltipPrimitive.Root>
|
|
22
|
-
</TooltipPrimitive.Provider>
|
|
35
|
+
<TooltipContext.Provider value={{ id, open: isOpen }}>
|
|
36
|
+
<TooltipPrimitive.Root {...rest} open={isOpen} onOpenChange={change} />
|
|
37
|
+
</TooltipContext.Provider>
|
|
23
38
|
)
|
|
24
39
|
}
|
|
25
40
|
|
|
26
|
-
export
|
|
27
|
-
|
|
41
|
+
export type TooltipTriggerProps = ComponentProps<typeof TooltipPrimitive.Trigger>
|
|
42
|
+
|
|
43
|
+
export function TooltipTrigger(props: TooltipTriggerProps) {
|
|
44
|
+
const { id, open } = useContext(TooltipContext)
|
|
45
|
+
const inProvider = useContext(ProviderContext)
|
|
46
|
+
return <TooltipPrimitive.Trigger delay={inProvider ? undefined : 100} {...props} aria-describedby={open ? id : undefined} />
|
|
28
47
|
}
|
|
29
48
|
|
|
30
|
-
export
|
|
49
|
+
export type TooltipContentProps = Omit<ComponentProps<typeof TooltipPrimitive.Popup>, 'className'> & {
|
|
31
50
|
side?: 'top' | 'right' | 'bottom' | 'left'
|
|
32
51
|
arrow?: boolean
|
|
52
|
+
className?: string
|
|
33
53
|
}
|
|
34
54
|
|
|
35
55
|
const tooltipContent = tv({
|
|
@@ -38,6 +58,14 @@ const tooltipContent = tv({
|
|
|
38
58
|
+ ' open:animate-enter closed:animate-exit',
|
|
39
59
|
})
|
|
40
60
|
|
|
61
|
+
const tooltipArrow = tv({
|
|
62
|
+
base: 'data-uncentered:invisible'
|
|
63
|
+
+ ' side-top:top-full'
|
|
64
|
+
+ ' side-bottom:bottom-full side-bottom:rotate-180'
|
|
65
|
+
+ ' side-right:left-0 side-right:origin-top-left side-right:rotate-90 side-right:-translate-y-1/2'
|
|
66
|
+
+ ' side-left:right-0 side-left:origin-top-right side-left:-rotate-90 side-left:-translate-y-1/2',
|
|
67
|
+
})
|
|
68
|
+
|
|
41
69
|
export function TooltipContent({
|
|
42
70
|
side = 'top',
|
|
43
71
|
arrow = true,
|
|
@@ -45,23 +73,26 @@ export function TooltipContent({
|
|
|
45
73
|
children,
|
|
46
74
|
...rest
|
|
47
75
|
}: TooltipContentProps) {
|
|
76
|
+
const { id } = useContext(TooltipContext)
|
|
77
|
+
|
|
48
78
|
return (
|
|
49
79
|
<TooltipPrimitive.Portal>
|
|
50
|
-
<TooltipPrimitive.
|
|
51
|
-
{...rest}
|
|
80
|
+
<TooltipPrimitive.Positioner
|
|
52
81
|
side={side}
|
|
53
|
-
sideOffset={6}
|
|
54
|
-
className=
|
|
82
|
+
sideOffset={arrow ? 11 : 6}
|
|
83
|
+
className="z-overlay min-w-max will-change-transform"
|
|
55
84
|
>
|
|
56
|
-
{
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
<
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
85
|
+
<TooltipPrimitive.Popup {...rest} id={id} role="tooltip" className={tooltipContent({ className })}>
|
|
86
|
+
{children}
|
|
87
|
+
{arrow && (
|
|
88
|
+
<TooltipPrimitive.Arrow className={tooltipArrow()}>
|
|
89
|
+
<svg width={10} height={5} viewBox="0 0 12 6" preserveAspectRatio="none" className="block fill-bg-panel -translate-y-px">
|
|
90
|
+
<path d="M0 0L4.58579 4.58579C5.36683 5.36683 6.63316 5.36684 7.41421 4.58579L12 0" className="stroke-line stroke-1" />
|
|
91
|
+
</svg>
|
|
92
|
+
</TooltipPrimitive.Arrow>
|
|
93
|
+
)}
|
|
94
|
+
</TooltipPrimitive.Popup>
|
|
95
|
+
</TooltipPrimitive.Positioner>
|
|
65
96
|
</TooltipPrimitive.Portal>
|
|
66
97
|
)
|
|
67
98
|
}
|
package/utilities.css
CHANGED
|
@@ -10,10 +10,14 @@
|
|
|
10
10
|
|
|
11
11
|
@custom-variant described (&:has([data-description]));
|
|
12
12
|
|
|
13
|
-
@custom-variant checked (&:where([data-
|
|
14
|
-
@custom-variant open (&[data-
|
|
15
|
-
@custom-variant closed (&[data-
|
|
16
|
-
@custom-variant on (&[
|
|
13
|
+
@custom-variant checked (&:where([data-checked], [data-indeterminate]));
|
|
14
|
+
@custom-variant open (&[data-open]);
|
|
15
|
+
@custom-variant closed (&[data-closed]);
|
|
16
|
+
@custom-variant on (&[aria-pressed="true"]);
|
|
17
|
+
@custom-variant side-top (&[data-side="top"]);
|
|
18
|
+
@custom-variant side-right (&[data-side="right"]);
|
|
19
|
+
@custom-variant side-bottom (&[data-side="bottom"]);
|
|
20
|
+
@custom-variant side-left (&[data-side="left"]);
|
|
17
21
|
@custom-variant selected (&[data-selected]:not([data-selected="false"]));
|
|
18
22
|
@custom-variant unset (&[data-placeholder]);
|
|
19
23
|
|
|
@@ -24,6 +28,7 @@
|
|
|
24
28
|
.dark { color-scheme: dark; }
|
|
25
29
|
|
|
26
30
|
body {
|
|
31
|
+
position: relative;
|
|
27
32
|
background-color: var(--color-bg);
|
|
28
33
|
color: var(--color-fg);
|
|
29
34
|
font-family: var(--font-sans);
|
|
@@ -104,7 +109,7 @@
|
|
|
104
109
|
}
|
|
105
110
|
|
|
106
111
|
&:active:not(:disabled):not([aria-disabled="true"])::after,
|
|
107
|
-
&[data-pressed]:not(:disabled):not([aria-disabled="true"])::after {
|
|
112
|
+
&[data-pressed]:not([data-popup-open]):not([aria-pressed]):not(:disabled):not([aria-disabled="true"])::after {
|
|
108
113
|
opacity: 1;
|
|
109
114
|
transition-duration: 0s;
|
|
110
115
|
}
|
|
@@ -112,7 +117,7 @@
|
|
|
112
117
|
|
|
113
118
|
@utility press-shrink {
|
|
114
119
|
&:active:not(:disabled):not([aria-disabled="true"]),
|
|
115
|
-
&[data-pressed]:not(:disabled):not([aria-disabled="true"]) {
|
|
120
|
+
&[data-pressed]:not([data-popup-open]):not([aria-pressed]):not(:disabled):not([aria-disabled="true"]) {
|
|
116
121
|
scale: var(--scale-press);
|
|
117
122
|
transition-duration: 0s;
|
|
118
123
|
}
|
package/accordion/AGENTS.md
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# Accordion
|
|
2
|
-
|
|
3
|
-
```
|
|
4
|
-
받기 npx @pantoia/ui add accordion
|
|
5
|
-
층 ui
|
|
6
|
-
파일 accordion/index.tsx, accordion/index.module.css, accordion/AGENTS.md
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
## 부위
|
|
10
|
-
|
|
11
|
-
| 이름 | props |
|
|
12
|
-
|---|---|
|
|
13
|
-
| `Accordion` | `AccordionProps` |
|
|
14
|
-
| `AccordionItem` | - |
|
|
15
|
-
| `AccordionTrigger` | - |
|
|
16
|
-
| `AccordionContent` | - |
|
|
17
|
-
|
|
18
|
-
### `AccordionItem`
|
|
19
|
-
|
|
20
|
-
| 이름 | 타입 | 필수 |
|
|
21
|
-
|---|---|---|
|
|
22
|
-
| `value` | `string` | 예 |
|
|
23
|
-
| `disabled` | `boolean` | |
|
|
24
|
-
| `className` | `string` | |
|
|
25
|
-
| `children` | `ReactNode` | |
|
|
26
|
-
|
|
27
|
-
### `AccordionTrigger`
|
|
28
|
-
|
|
29
|
-
| 이름 | 타입 | 필수 |
|
|
30
|
-
|---|---|---|
|
|
31
|
-
| `className` | `string` | |
|
|
32
|
-
| `children` | `ReactNode` | |
|
|
33
|
-
|
|
34
|
-
### `AccordionContent`
|
|
35
|
-
|
|
36
|
-
| 이름 | 타입 | 필수 |
|
|
37
|
-
|---|---|---|
|
|
38
|
-
| `className` | `string` | |
|
|
39
|
-
| `children` | `ReactNode` | |
|
|
40
|
-
|
|
41
|
-
## props
|
|
42
|
-
|
|
43
|
-
### `AccordionProps`
|
|
44
|
-
|
|
45
|
-
| 이름 | 타입 | 필수 |
|
|
46
|
-
|---|---|---|
|
|
47
|
-
| `value` | `string \| string[]` | |
|
|
48
|
-
| `defaultValue` | `string \| string[]` | |
|
|
49
|
-
| `onValueChange` | `(value: string & string[]) => void` | |
|
|
50
|
-
| `multiple` | `boolean` | |
|
|
51
|
-
| `className` | `string` | |
|
|
52
|
-
| `children` | `ReactNode` | |
|
|
53
|
-
|
|
54
|
-
## 함께 받는 것
|
|
55
|
-
|
|
56
|
-
`tv`
|
|
57
|
-
|
|
58
|
-
## npm 의존
|
|
59
|
-
|
|
60
|
-
`radix-ui`
|