@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
package/select/index.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { Select as SelectPrimitive } from '
|
|
3
|
+
import { Select as SelectPrimitive } from '@base-ui/react/select'
|
|
4
|
+
import { Children, isValidElement, useEffect, useRef, useState } from 'react'
|
|
4
5
|
import type { ComponentProps, ReactNode } from 'react'
|
|
5
6
|
|
|
6
7
|
import { tv } from '../tv'
|
|
@@ -29,20 +30,53 @@ export const selectTrigger = tv({
|
|
|
29
30
|
defaultVariants: { surface: 'bare', size: 'md' },
|
|
30
31
|
})
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
33
|
+
const selectArrow = tv({
|
|
34
|
+
base: 'data-uncentered:invisible'
|
|
35
|
+
+ ' side-top:top-full'
|
|
36
|
+
+ ' side-bottom:bottom-full side-bottom:rotate-180'
|
|
37
|
+
+ ' side-right:left-0 side-right:origin-top-left side-right:rotate-90 side-right:-translate-y-1/2'
|
|
38
|
+
+ ' side-left:right-0 side-left:origin-top-right side-left:-rotate-90 side-left:-translate-y-1/2',
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
type ItemElementProps = { value?: string, children?: ReactNode }
|
|
42
|
+
|
|
43
|
+
function findItemText(children: ReactNode): { label: ReactNode } | undefined {
|
|
44
|
+
let found: { label: ReactNode } | undefined
|
|
45
|
+
Children.forEach(children, child => {
|
|
46
|
+
if (found || !isValidElement<ItemElementProps>(child)) return
|
|
47
|
+
found = child.type === SelectItemText
|
|
48
|
+
? { label: child.props.children }
|
|
49
|
+
: findItemText(child.props.children)
|
|
50
|
+
})
|
|
51
|
+
return found
|
|
44
52
|
}
|
|
45
53
|
|
|
54
|
+
function collectItems(children: ReactNode, items: Record<string, ReactNode>) {
|
|
55
|
+
Children.forEach(children, child => {
|
|
56
|
+
if (!isValidElement<ItemElementProps>(child)) return
|
|
57
|
+
if (child.type === SelectItem && child.props.value !== undefined) {
|
|
58
|
+
const text = findItemText(child.props.children)
|
|
59
|
+
if (text) items[child.props.value] = text.label
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
collectItems(child.props.children, items)
|
|
63
|
+
})
|
|
64
|
+
return items
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export type SelectProps = Omit<ComponentProps<typeof SelectPrimitive.Trigger>, 'className' | 'value' | 'defaultValue'>
|
|
68
|
+
& Pick<SelectPrimitive.Root.Props<string>, 'open' | 'defaultOpen' | 'onOpenChange' | 'items' | 'name' | 'required' | 'readOnly' | 'modal' | 'form'>
|
|
69
|
+
& {
|
|
70
|
+
value?: string
|
|
71
|
+
defaultValue?: string
|
|
72
|
+
onValueChange?: (value: string, eventDetails: SelectPrimitive.Root.ChangeEventDetails) => void
|
|
73
|
+
placeholder?: string
|
|
74
|
+
surface?: 'bare' | 'well'
|
|
75
|
+
size?: 'sm' | 'md' | 'lg'
|
|
76
|
+
arrow?: boolean
|
|
77
|
+
className?: string
|
|
78
|
+
}
|
|
79
|
+
|
|
46
80
|
export function Select({
|
|
47
81
|
value,
|
|
48
82
|
defaultValue,
|
|
@@ -50,6 +84,12 @@ export function Select({
|
|
|
50
84
|
open,
|
|
51
85
|
defaultOpen,
|
|
52
86
|
onOpenChange,
|
|
87
|
+
items,
|
|
88
|
+
name,
|
|
89
|
+
required,
|
|
90
|
+
readOnly,
|
|
91
|
+
modal,
|
|
92
|
+
form,
|
|
53
93
|
placeholder,
|
|
54
94
|
surface,
|
|
55
95
|
size,
|
|
@@ -58,59 +98,91 @@ export function Select({
|
|
|
58
98
|
children,
|
|
59
99
|
...rest
|
|
60
100
|
}: SelectProps) {
|
|
101
|
+
const [shown, setShown] = useState(defaultOpen ?? false)
|
|
102
|
+
const popup = useRef<HTMLDivElement>(null)
|
|
103
|
+
const isOpen = open ?? shown
|
|
104
|
+
|
|
105
|
+
useEffect(() => {
|
|
106
|
+
if (!isOpen) return
|
|
107
|
+
|
|
108
|
+
const frame = requestAnimationFrame(() => {
|
|
109
|
+
const list = popup.current
|
|
110
|
+
if (!list || list.querySelector('[role="option"][aria-selected="true"]')) return
|
|
111
|
+
list.querySelector<HTMLElement>('[role="option"]:not([data-disabled])')?.focus({ preventScroll: true })
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
return () => cancelAnimationFrame(frame)
|
|
115
|
+
}, [isOpen])
|
|
116
|
+
|
|
61
117
|
return (
|
|
62
|
-
<SelectPrimitive.Root
|
|
118
|
+
<SelectPrimitive.Root<string>
|
|
63
119
|
value={value}
|
|
64
120
|
defaultValue={defaultValue}
|
|
65
|
-
onValueChange={onValueChange}
|
|
121
|
+
onValueChange={(next, eventDetails) => onValueChange?.(next ?? '', eventDetails)}
|
|
66
122
|
open={open}
|
|
67
123
|
defaultOpen={defaultOpen}
|
|
68
|
-
onOpenChange={
|
|
124
|
+
onOpenChange={(next, eventDetails) => {
|
|
125
|
+
setShown(next)
|
|
126
|
+
onOpenChange?.(next, eventDetails)
|
|
127
|
+
}}
|
|
128
|
+
items={items ?? collectItems(children, {})}
|
|
129
|
+
name={name}
|
|
130
|
+
required={required}
|
|
131
|
+
readOnly={readOnly}
|
|
132
|
+
modal={modal}
|
|
133
|
+
form={form}
|
|
69
134
|
>
|
|
70
135
|
<SelectPrimitive.Trigger {...rest} className={selectTrigger({ surface, size, className })}>
|
|
71
136
|
<span className="min-w-0 truncate">
|
|
72
137
|
<SelectPrimitive.Value placeholder={placeholder} />
|
|
73
138
|
</span>
|
|
74
139
|
<SelectPrimitive.Icon className="text-fg-subtle">
|
|
75
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className="size-4">
|
|
140
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className="size-4 transition-transform in-data-popup-open:rotate-180 motion-reduce:transition-none">
|
|
76
141
|
<path d="m6 9 6 6 6-6" />
|
|
77
142
|
</svg>
|
|
78
143
|
</SelectPrimitive.Icon>
|
|
79
144
|
</SelectPrimitive.Trigger>
|
|
80
145
|
|
|
81
146
|
<SelectPrimitive.Portal>
|
|
82
|
-
<SelectPrimitive.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
position="popper"
|
|
87
|
-
sideOffset={8}
|
|
147
|
+
<SelectPrimitive.Positioner
|
|
148
|
+
alignItemWithTrigger={false}
|
|
149
|
+
align="start"
|
|
150
|
+
sideOffset={arrow ? 14 : 8}
|
|
88
151
|
arrowPadding={16}
|
|
152
|
+
className="z-overlay min-w-max will-change-transform"
|
|
89
153
|
>
|
|
90
|
-
<SelectPrimitive.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
<SelectPrimitive.ScrollDownButton className="text-fg-subtle flex h-6 cursor-default items-center justify-center">
|
|
101
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className="size-4">
|
|
102
|
-
<path d="m6 9 6 6 6-6" />
|
|
103
|
-
</svg>
|
|
104
|
-
</SelectPrimitive.ScrollDownButton>
|
|
105
|
-
|
|
106
|
-
{arrow && (
|
|
107
|
-
<SelectPrimitive.Arrow asChild width={12} height={6}>
|
|
108
|
-
<svg viewBox="0 0 12 6" preserveAspectRatio="none" className="fill-bg-panel -translate-y-px">
|
|
109
|
-
<path d="M0 0L4.58579 4.58579C5.36683 5.36683 6.63316 5.36684 7.41421 4.58579L12 0" className="stroke-line stroke-1" />
|
|
154
|
+
<SelectPrimitive.Popup
|
|
155
|
+
ref={popup}
|
|
156
|
+
className="float text-fg rounded-r12 z-overlay
|
|
157
|
+
flex flex-col outline-none
|
|
158
|
+
min-w-(--anchor-width) py-1
|
|
159
|
+
open:animate-enter closed:animate-exit"
|
|
160
|
+
>
|
|
161
|
+
<SelectPrimitive.ScrollUpArrow className="text-fg-subtle flex h-6 cursor-default items-center justify-center">
|
|
162
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className="size-4">
|
|
163
|
+
<path d="m18 15-6-6-6 6" />
|
|
110
164
|
</svg>
|
|
111
|
-
</SelectPrimitive.
|
|
112
|
-
|
|
113
|
-
|
|
165
|
+
</SelectPrimitive.ScrollUpArrow>
|
|
166
|
+
|
|
167
|
+
<SelectPrimitive.List className="relative flex flex-1 flex-col gap-px overflow-x-hidden overflow-y-auto px-1">
|
|
168
|
+
{children}
|
|
169
|
+
</SelectPrimitive.List>
|
|
170
|
+
|
|
171
|
+
<SelectPrimitive.ScrollDownArrow className="text-fg-subtle flex h-6 cursor-default items-center justify-center">
|
|
172
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className="size-4">
|
|
173
|
+
<path d="m6 9 6 6 6-6" />
|
|
174
|
+
</svg>
|
|
175
|
+
</SelectPrimitive.ScrollDownArrow>
|
|
176
|
+
|
|
177
|
+
{arrow && (
|
|
178
|
+
<SelectPrimitive.Arrow className={selectArrow()}>
|
|
179
|
+
<svg width={12} height={6} viewBox="0 0 12 6" preserveAspectRatio="none" className="block fill-bg-panel -translate-y-px">
|
|
180
|
+
<path d="M0 0L4.58579 4.58579C5.36683 5.36683 6.63316 5.36684 7.41421 4.58579L12 0" className="stroke-line stroke-1" />
|
|
181
|
+
</svg>
|
|
182
|
+
</SelectPrimitive.Arrow>
|
|
183
|
+
)}
|
|
184
|
+
</SelectPrimitive.Popup>
|
|
185
|
+
</SelectPrimitive.Positioner>
|
|
114
186
|
</SelectPrimitive.Portal>
|
|
115
187
|
</SelectPrimitive.Root>
|
|
116
188
|
)
|
|
@@ -118,17 +190,18 @@ export function Select({
|
|
|
118
190
|
|
|
119
191
|
const selectGroup = tv({ base: 'flex flex-col gap-px' })
|
|
120
192
|
|
|
121
|
-
export
|
|
193
|
+
export type SelectGroupProps = Omit<ComponentProps<typeof SelectPrimitive.Group>, 'className'> & {
|
|
122
194
|
label?: string
|
|
123
195
|
className?: string
|
|
124
|
-
|
|
125
|
-
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function SelectGroup({ label, className, children, ...rest }: SelectGroupProps) {
|
|
126
199
|
return (
|
|
127
|
-
<SelectPrimitive.Group className={selectGroup({ className })}>
|
|
200
|
+
<SelectPrimitive.Group {...rest} className={selectGroup({ className })}>
|
|
128
201
|
{label && (
|
|
129
|
-
<SelectPrimitive.
|
|
202
|
+
<SelectPrimitive.GroupLabel className="text-caption text-fg-subtle px-2 pb-1 pt-2">
|
|
130
203
|
{label}
|
|
131
|
-
</SelectPrimitive.
|
|
204
|
+
</SelectPrimitive.GroupLabel>
|
|
132
205
|
)}
|
|
133
206
|
{children}
|
|
134
207
|
</SelectPrimitive.Group>
|
|
@@ -142,18 +215,14 @@ const selectItem = tv({
|
|
|
142
215
|
+ ' data-disabled:text-fg-disabled data-disabled:pointer-events-none',
|
|
143
216
|
})
|
|
144
217
|
|
|
145
|
-
export
|
|
218
|
+
export type SelectItemProps = Omit<ComponentProps<typeof SelectPrimitive.Item>, 'className' | 'value'> & {
|
|
146
219
|
value: string
|
|
147
|
-
disabled?: boolean
|
|
148
220
|
className?: string
|
|
149
|
-
|
|
150
|
-
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function SelectItem({ className, children, ...rest }: SelectItemProps) {
|
|
151
224
|
return (
|
|
152
|
-
<SelectPrimitive.Item
|
|
153
|
-
value={value}
|
|
154
|
-
disabled={disabled}
|
|
155
|
-
className={selectItem({ className })}
|
|
156
|
-
>
|
|
225
|
+
<SelectPrimitive.Item {...rest} className={selectItem({ className })}>
|
|
157
226
|
<span className="flex min-w-0 flex-col">
|
|
158
227
|
{children}
|
|
159
228
|
</span>
|
|
@@ -167,14 +236,12 @@ export function SelectItem({ value, disabled, className, children }: {
|
|
|
167
236
|
)
|
|
168
237
|
}
|
|
169
238
|
|
|
170
|
-
|
|
239
|
+
export type SelectItemTextProps = Omit<ComponentProps<typeof SelectPrimitive.ItemText>, 'className'> & {
|
|
240
|
+
className?: string
|
|
241
|
+
}
|
|
171
242
|
|
|
172
|
-
export function SelectItemText({ className,
|
|
173
|
-
return
|
|
174
|
-
<SelectPrimitive.ItemText className={selectItemText({ className })}>
|
|
175
|
-
{children}
|
|
176
|
-
</SelectPrimitive.ItemText>
|
|
177
|
-
)
|
|
243
|
+
export function SelectItemText({ className: _className, render = <span />, ...rest }: SelectItemTextProps) {
|
|
244
|
+
return <SelectPrimitive.ItemText {...rest} render={render} />
|
|
178
245
|
}
|
|
179
246
|
|
|
180
247
|
const selectItemDescription = tv({ base: 'text-caption text-fg-subtle' })
|
|
@@ -189,6 +256,10 @@ export function SelectItemDescription({ className, children }: { className?: str
|
|
|
189
256
|
|
|
190
257
|
const selectSeparator = tv({ base: 'bg-line -mx-1 my-1 h-px' })
|
|
191
258
|
|
|
192
|
-
export
|
|
193
|
-
|
|
259
|
+
export type SelectSeparatorProps = Omit<ComponentProps<typeof SelectPrimitive.Separator>, 'className'> & {
|
|
260
|
+
className?: string
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export function SelectSeparator({ className, ...rest }: SelectSeparatorProps) {
|
|
264
|
+
return <SelectPrimitive.Separator {...rest} className={selectSeparator({ className })} />
|
|
194
265
|
}
|
package/switch/index.tsx
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { Switch as SwitchPrimitive } from '
|
|
3
|
+
import { Switch as SwitchPrimitive } from '@base-ui/react/switch'
|
|
4
4
|
import type { ComponentProps } from 'react'
|
|
5
5
|
|
|
6
6
|
import { tv } from '../tv'
|
|
7
7
|
|
|
8
8
|
export const uiSwitch = tv({
|
|
9
9
|
base: 'h-6 w-10 shrink-0 rounded-full p-0.5 transition-state press press-shrink'
|
|
10
|
-
+ ' bg-bg-
|
|
10
|
+
+ ' bg-bg-neutral-track ring-1 ring-inset ring-line-strong'
|
|
11
11
|
+ ' invalid:ring-2 invalid:ring-line-danger'
|
|
12
12
|
+ ' focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-line-focus-ring'
|
|
13
13
|
+ ' disabled:bg-bg-neutral-disabled disabled:cursor-not-allowed'
|
|
@@ -26,15 +26,22 @@ export const uiSwitch = tv({
|
|
|
26
26
|
defaultVariants: { color: 'neutral' },
|
|
27
27
|
})
|
|
28
28
|
|
|
29
|
-
export
|
|
29
|
+
export type SwitchProps = Omit<ComponentProps<typeof SwitchPrimitive.Root>, 'className'> & {
|
|
30
30
|
color?: 'neutral' | 'info' | 'success' | 'warning' | 'danger'
|
|
31
|
+
className?: string
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
export function Switch({
|
|
34
|
+
export function Switch({
|
|
35
|
+
color,
|
|
36
|
+
className,
|
|
37
|
+
nativeButton = true,
|
|
38
|
+
render = <button type="button" />,
|
|
39
|
+
...rest
|
|
40
|
+
}: SwitchProps) {
|
|
34
41
|
return (
|
|
35
|
-
<SwitchPrimitive.Root {...rest} className={uiSwitch({ color, className })}>
|
|
42
|
+
<SwitchPrimitive.Root {...rest} nativeButton={nativeButton} render={render} className={uiSwitch({ color, className })}>
|
|
36
43
|
<SwitchPrimitive.Thumb
|
|
37
|
-
className="bg-bg-neutral-knob block size-5 rounded-full checked:translate-x-4
|
|
44
|
+
className="bg-bg-neutral-knob-off checked:bg-bg-neutral-knob block size-5 rounded-full checked:translate-x-4
|
|
38
45
|
transition-transform ease-state motion-reduce:transition-none"
|
|
39
46
|
/>
|
|
40
47
|
</SwitchPrimitive.Root>
|
package/table/index.tsx
CHANGED
|
@@ -89,8 +89,7 @@ export function TableBody({ className, children }: { className?: string, childre
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
const tableRow = tv({
|
|
92
|
-
base: 'border-b border-line last:border-b-0
|
|
93
|
-
+ ' hover:bg-bg-neutral-hover',
|
|
92
|
+
base: 'border-b border-line last:border-b-0',
|
|
94
93
|
})
|
|
95
94
|
|
|
96
95
|
export function TableRow({ className, children }: { className?: string, children?: ReactNode }) {
|
package/tabs/index.module.css
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
bottom: 0;
|
|
4
4
|
height: 2px;
|
|
5
5
|
background: var(--color-fg);
|
|
6
|
-
width: var(--
|
|
7
|
-
translate: var(--
|
|
6
|
+
width: var(--active-tab-width);
|
|
7
|
+
translate: var(--active-tab-left) 0;
|
|
8
8
|
transition: translate var(--dur-state) var(--ease-state),
|
|
9
9
|
width var(--dur-state) var(--ease-state);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
.content[data-
|
|
12
|
+
.content[data-hidden] { display: none; }
|
|
13
13
|
|
|
14
14
|
@media (prefers-reduced-motion: reduce) {
|
|
15
15
|
.underline { transition: none; }
|
package/tabs/index.tsx
CHANGED
|
@@ -1,73 +1,31 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { Tabs as TabsPrimitive } from '
|
|
4
|
-
import {
|
|
3
|
+
import { Tabs as TabsPrimitive } from '@base-ui/react/tabs'
|
|
4
|
+
import type { ComponentProps } from 'react'
|
|
5
5
|
|
|
6
6
|
import { tv } from '../tv'
|
|
7
7
|
|
|
8
8
|
import s from './index.module.css'
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
export interface TabsProps {
|
|
13
|
-
value?: string
|
|
14
|
-
defaultValue?: string
|
|
15
|
-
onValueChange?: (value: string) => void
|
|
16
|
-
className?: string
|
|
17
|
-
children?: ReactNode
|
|
18
|
-
}
|
|
10
|
+
export type TabsProps = Omit<ComponentProps<typeof TabsPrimitive.Root>, 'className'> & { className?: string }
|
|
19
11
|
|
|
20
12
|
const tabs = tv({ base: 'flex flex-col gap-stack' })
|
|
21
13
|
|
|
22
|
-
export function Tabs({
|
|
23
|
-
return (
|
|
24
|
-
<TabsPrimitive.Root
|
|
25
|
-
value={value}
|
|
26
|
-
defaultValue={defaultValue}
|
|
27
|
-
onValueChange={onValueChange}
|
|
28
|
-
className={tabs({ className })}
|
|
29
|
-
>
|
|
30
|
-
{children}
|
|
31
|
-
</TabsPrimitive.Root>
|
|
32
|
-
)
|
|
14
|
+
export function Tabs({ className, ...rest }: TabsProps) {
|
|
15
|
+
return <TabsPrimitive.Root {...rest} className={tabs({ className })} />
|
|
33
16
|
}
|
|
34
17
|
|
|
35
18
|
const tabsList = tv({ base: 'border-line scrollbar-thin scrollbar-thumb-line-strong scrollbar-track-transparent overflow-x-auto border-b' })
|
|
36
19
|
|
|
37
|
-
export
|
|
38
|
-
const list = useRef<HTMLDivElement>(null)
|
|
39
|
-
const bar = useRef<HTMLSpanElement>(null)
|
|
40
|
-
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
const root = list.current
|
|
43
|
-
const mark = bar.current
|
|
44
|
-
if (!root || !mark) return
|
|
45
|
-
|
|
46
|
-
const measure = () => {
|
|
47
|
-
const active = root.querySelector<HTMLElement>(ACTIVE)
|
|
48
|
-
mark.style.setProperty('--tabs-indicator-size', `${active?.offsetWidth ?? 0}px`)
|
|
49
|
-
mark.style.setProperty('--tabs-indicator-position', `${active?.offsetLeft ?? 0}px`)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
measure()
|
|
53
|
-
|
|
54
|
-
const attrs = new MutationObserver(measure)
|
|
55
|
-
attrs.observe(root, { attributes: true, subtree: true, attributeFilter: ['data-state'] })
|
|
56
|
-
|
|
57
|
-
const box = new ResizeObserver(measure)
|
|
58
|
-
box.observe(root)
|
|
59
|
-
|
|
60
|
-
return () => { attrs.disconnect(); box.disconnect() }
|
|
61
|
-
}, [])
|
|
20
|
+
export type TabsListProps = Omit<ComponentProps<typeof TabsPrimitive.List>, 'className'> & { className?: string }
|
|
62
21
|
|
|
22
|
+
export function TabsList({ className, children, ...rest }: TabsListProps) {
|
|
63
23
|
return (
|
|
64
|
-
<div
|
|
65
|
-
|
|
66
|
-
>
|
|
67
|
-
<TabsPrimitive.List ref={list} className="relative flex w-max min-w-full">
|
|
24
|
+
<div className={tabsList({ className })}>
|
|
25
|
+
<TabsPrimitive.List {...rest} className="relative flex w-max min-w-full">
|
|
68
26
|
{children}
|
|
69
27
|
|
|
70
|
-
<
|
|
28
|
+
<TabsPrimitive.Indicator className={s.underline} />
|
|
71
29
|
</TabsPrimitive.List>
|
|
72
30
|
</div>
|
|
73
31
|
)
|
|
@@ -76,41 +34,29 @@ export function TabsList({ className, children }: { className?: string, children
|
|
|
76
34
|
const tabsTrigger = tv({
|
|
77
35
|
base: 'text-label text-fg-muted inline-flex h-9 items-center gap-tight px-3 transition-state'
|
|
78
36
|
+ ' [&_svg]:size-4'
|
|
79
|
-
+ ' press-shrink
|
|
80
|
-
+ ' disabled:text-fg-disabled disabled:cursor-not-allowed'
|
|
37
|
+
+ ' press-shrink not-data-disabled:cursor-pointer not-data-disabled:hover:text-fg'
|
|
38
|
+
+ ' data-disabled:text-fg-disabled data-disabled:cursor-not-allowed'
|
|
81
39
|
+ ' aria-selected:text-fg'
|
|
82
40
|
+ ' focus-visible:outline-2 focus-visible:-outline-offset-2'
|
|
83
41
|
+ ' focus-visible:outline-line-focus-ring',
|
|
84
42
|
})
|
|
85
43
|
|
|
86
|
-
export
|
|
44
|
+
export type TabsTriggerProps = Omit<ComponentProps<typeof TabsPrimitive.Tab>, 'className' | 'value'> & {
|
|
87
45
|
value: string
|
|
88
|
-
disabled?: boolean
|
|
89
46
|
className?: string
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
value={value}
|
|
95
|
-
disabled={disabled}
|
|
96
|
-
className={tabsTrigger({ className })}
|
|
97
|
-
>
|
|
98
|
-
{children}
|
|
99
|
-
</TabsPrimitive.Trigger>
|
|
100
|
-
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function TabsTrigger({ className, ...rest }: TabsTriggerProps) {
|
|
50
|
+
return <TabsPrimitive.Tab {...rest} className={tabsTrigger({ className })} />
|
|
101
51
|
}
|
|
102
52
|
|
|
103
53
|
const tabsContent = tv({ base: s.content })
|
|
104
54
|
|
|
105
|
-
export
|
|
55
|
+
export type TabsContentProps = Omit<ComponentProps<typeof TabsPrimitive.Panel>, 'className' | 'value'> & {
|
|
106
56
|
value: string
|
|
107
|
-
forceMount?: boolean
|
|
108
57
|
className?: string
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
{children}
|
|
114
|
-
</TabsPrimitive.Content>
|
|
115
|
-
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function TabsContent({ className, ...rest }: TabsContentProps) {
|
|
61
|
+
return <TabsPrimitive.Panel {...rest} className={tabsContent({ className })} />
|
|
116
62
|
}
|
package/textarea/index.tsx
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Field as FieldPrimitive } from '@base-ui/react/field'
|
|
4
|
+
import type { ComponentProps } from 'react'
|
|
2
5
|
|
|
3
6
|
import { tv } from '../tv'
|
|
4
7
|
|
|
@@ -27,11 +30,20 @@ export const textarea = tv({
|
|
|
27
30
|
defaultVariants: { surface: 'bare', size: 'md' },
|
|
28
31
|
})
|
|
29
32
|
|
|
30
|
-
export
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
export type TextareaProps = Omit<ComponentProps<'textarea'>, 'className' | 'value' | 'defaultValue'>
|
|
34
|
+
& Pick<FieldPrimitive.Control.Props, 'value' | 'defaultValue' | 'onValueChange'>
|
|
35
|
+
& {
|
|
36
|
+
surface?: 'bare' | 'well'
|
|
37
|
+
size?: 'sm' | 'md' | 'lg'
|
|
38
|
+
className?: string
|
|
39
|
+
}
|
|
34
40
|
|
|
35
|
-
export function Textarea({ surface, size, rows = 3, className, ...rest }: TextareaProps) {
|
|
36
|
-
return
|
|
41
|
+
export function Textarea({ surface, size, rows = 3, cols, wrap, className, ...rest }: TextareaProps) {
|
|
42
|
+
return (
|
|
43
|
+
<FieldPrimitive.Control
|
|
44
|
+
{...(rest as FieldPrimitive.Control.Props)}
|
|
45
|
+
render={<textarea rows={rows} cols={cols} wrap={wrap} />}
|
|
46
|
+
className={textarea({ surface, size, className })}
|
|
47
|
+
/>
|
|
48
|
+
)
|
|
37
49
|
}
|