@srcroot/ui 0.0.1 → 0.0.2
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/dist/index.js +91 -0
- package/package.json +9 -3
- package/registry/calendar.tsx +416 -142
- package/registry/combobox.tsx +174 -0
- package/registry/command.tsx +298 -0
- package/registry/context-menu.tsx +221 -0
- package/registry/date-picker.tsx +179 -0
- package/registry/drawer.tsx +217 -0
- package/registry/dropdown-menu.tsx +1 -30
- package/registry/file-upload.tsx +240 -0
- package/registry/hover-card.tsx +165 -0
- package/registry/kbd.tsx +60 -0
- package/registry/menubar.tsx +246 -0
- package/registry/native-select.tsx +49 -0
- package/registry/pagination.tsx +3 -0
- package/registry/resizable.tsx +213 -0
- package/registry/scroll-area.tsx +60 -0
- package/registry/search.tsx +2 -1
- package/registry/sheet.tsx +1 -0
- package/registry/sidebar.tsx +505 -0
- package/registry/slider.tsx +82 -18
- package/registry/toggle-group.tsx +129 -0
- package/registry/toggle.tsx +72 -0
- package/registry/tooltip.tsx +21 -3
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cn } from "@/lib/utils"
|
|
5
|
+
import { toggleVariants } from "./toggle"
|
|
6
|
+
import { type VariantProps } from "class-variance-authority"
|
|
7
|
+
|
|
8
|
+
// ToggleGroup Context
|
|
9
|
+
interface ToggleGroupContextValue {
|
|
10
|
+
type: "single" | "multiple"
|
|
11
|
+
value: string[]
|
|
12
|
+
onValueChange: (value: string) => void
|
|
13
|
+
variant?: VariantProps<typeof toggleVariants>["variant"]
|
|
14
|
+
size?: VariantProps<typeof toggleVariants>["size"]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const ToggleGroupContext = React.createContext<ToggleGroupContextValue | null>(null)
|
|
18
|
+
|
|
19
|
+
function useToggleGroup() {
|
|
20
|
+
const context = React.useContext(ToggleGroupContext)
|
|
21
|
+
if (!context) {
|
|
22
|
+
throw new Error("useToggleGroup must be used within a ToggleGroup")
|
|
23
|
+
}
|
|
24
|
+
return context
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ToggleGroup Props
|
|
28
|
+
interface ToggleGroupSingleProps {
|
|
29
|
+
type: "single"
|
|
30
|
+
value?: string
|
|
31
|
+
defaultValue?: string
|
|
32
|
+
onValueChange?: (value: string) => void
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface ToggleGroupMultipleProps {
|
|
36
|
+
type: "multiple"
|
|
37
|
+
value?: string[]
|
|
38
|
+
defaultValue?: string[]
|
|
39
|
+
onValueChange?: (value: string[]) => void
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type ToggleGroupProps = (ToggleGroupSingleProps | ToggleGroupMultipleProps) &
|
|
43
|
+
React.HTMLAttributes<HTMLDivElement> &
|
|
44
|
+
VariantProps<typeof toggleVariants>
|
|
45
|
+
|
|
46
|
+
const ToggleGroup = React.forwardRef<HTMLDivElement, ToggleGroupProps>(
|
|
47
|
+
({ className, type, value: controlledValue, defaultValue, onValueChange, variant, size, children, ...props }, ref) => {
|
|
48
|
+
// Handle both single and multiple types
|
|
49
|
+
const [uncontrolledValue, setUncontrolledValue] = React.useState<string[]>(() => {
|
|
50
|
+
if (type === "single") {
|
|
51
|
+
return defaultValue ? [defaultValue as string] : []
|
|
52
|
+
}
|
|
53
|
+
return (defaultValue as string[]) || []
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const value = controlledValue !== undefined
|
|
57
|
+
? (type === "single" ? [controlledValue as string] : controlledValue as string[])
|
|
58
|
+
: uncontrolledValue
|
|
59
|
+
|
|
60
|
+
const handleValueChange = (itemValue: string) => {
|
|
61
|
+
let newValue: string[]
|
|
62
|
+
|
|
63
|
+
if (type === "single") {
|
|
64
|
+
// Toggle off if clicking same value, otherwise set new value
|
|
65
|
+
newValue = value.includes(itemValue) ? [] : [itemValue]
|
|
66
|
+
if (controlledValue === undefined) {
|
|
67
|
+
setUncontrolledValue(newValue)
|
|
68
|
+
}
|
|
69
|
+
; (onValueChange as ((value: string) => void))?.(newValue[0] || "")
|
|
70
|
+
} else {
|
|
71
|
+
// Toggle item in array
|
|
72
|
+
newValue = value.includes(itemValue)
|
|
73
|
+
? value.filter(v => v !== itemValue)
|
|
74
|
+
: [...value, itemValue]
|
|
75
|
+
if (controlledValue === undefined) {
|
|
76
|
+
setUncontrolledValue(newValue)
|
|
77
|
+
}
|
|
78
|
+
; (onValueChange as ((value: string[]) => void))?.(newValue)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<ToggleGroupContext.Provider value={{ type, value, onValueChange: handleValueChange, variant, size }}>
|
|
84
|
+
<div
|
|
85
|
+
ref={ref}
|
|
86
|
+
role="group"
|
|
87
|
+
className={cn("flex items-center justify-center gap-1", className)}
|
|
88
|
+
{...props}
|
|
89
|
+
>
|
|
90
|
+
{children}
|
|
91
|
+
</div>
|
|
92
|
+
</ToggleGroupContext.Provider>
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
ToggleGroup.displayName = "ToggleGroup"
|
|
97
|
+
|
|
98
|
+
// ToggleGroupItem
|
|
99
|
+
interface ToggleGroupItemProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
100
|
+
value: string
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const ToggleGroupItem = React.forwardRef<HTMLButtonElement, ToggleGroupItemProps>(
|
|
104
|
+
({ className, value, children, ...props }, ref) => {
|
|
105
|
+
const context = useToggleGroup()
|
|
106
|
+
const pressed = context.value.includes(value)
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<button
|
|
110
|
+
ref={ref}
|
|
111
|
+
type="button"
|
|
112
|
+
aria-pressed={pressed}
|
|
113
|
+
data-state={pressed ? "on" : "off"}
|
|
114
|
+
className={cn(
|
|
115
|
+
toggleVariants({ variant: context.variant, size: context.size }),
|
|
116
|
+
pressed && "bg-accent text-accent-foreground",
|
|
117
|
+
className
|
|
118
|
+
)}
|
|
119
|
+
onClick={() => context.onValueChange(value)}
|
|
120
|
+
{...props}
|
|
121
|
+
>
|
|
122
|
+
{children}
|
|
123
|
+
</button>
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
)
|
|
127
|
+
ToggleGroupItem.displayName = "ToggleGroupItem"
|
|
128
|
+
|
|
129
|
+
export { ToggleGroup, ToggleGroupItem }
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
const toggleVariants = cva(
|
|
8
|
+
"inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: "bg-transparent",
|
|
13
|
+
outline: "border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
|
|
14
|
+
},
|
|
15
|
+
size: {
|
|
16
|
+
default: "h-10 px-3",
|
|
17
|
+
sm: "h-9 px-2.5",
|
|
18
|
+
lg: "h-11 px-5",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultVariants: {
|
|
22
|
+
variant: "default",
|
|
23
|
+
size: "default",
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
export interface ToggleProps
|
|
29
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
30
|
+
VariantProps<typeof toggleVariants> {
|
|
31
|
+
/** Controlled pressed state */
|
|
32
|
+
pressed?: boolean
|
|
33
|
+
/** Default pressed state for uncontrolled usage */
|
|
34
|
+
defaultPressed?: boolean
|
|
35
|
+
/** Callback when pressed state changes */
|
|
36
|
+
onPressedChange?: (pressed: boolean) => void
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const Toggle = React.forwardRef<HTMLButtonElement, ToggleProps>(
|
|
40
|
+
({ className, variant, size, pressed: controlledPressed, defaultPressed = false, onPressedChange, ...props }, ref) => {
|
|
41
|
+
const [uncontrolledPressed, setUncontrolledPressed] = React.useState(defaultPressed)
|
|
42
|
+
const pressed = controlledPressed ?? uncontrolledPressed
|
|
43
|
+
|
|
44
|
+
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
45
|
+
const newPressed = !pressed
|
|
46
|
+
if (controlledPressed === undefined) {
|
|
47
|
+
setUncontrolledPressed(newPressed)
|
|
48
|
+
}
|
|
49
|
+
onPressedChange?.(newPressed)
|
|
50
|
+
props.onClick?.(e)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<button
|
|
55
|
+
ref={ref}
|
|
56
|
+
type="button"
|
|
57
|
+
aria-pressed={pressed}
|
|
58
|
+
data-state={pressed ? "on" : "off"}
|
|
59
|
+
className={cn(
|
|
60
|
+
toggleVariants({ variant, size }),
|
|
61
|
+
pressed && "bg-accent text-accent-foreground",
|
|
62
|
+
className
|
|
63
|
+
)}
|
|
64
|
+
onClick={handleClick}
|
|
65
|
+
{...props}
|
|
66
|
+
/>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
Toggle.displayName = "Toggle"
|
|
71
|
+
|
|
72
|
+
export { Toggle, toggleVariants }
|
package/registry/tooltip.tsx
CHANGED
|
@@ -4,6 +4,8 @@ import { cn } from "@/lib/utils"
|
|
|
4
4
|
interface TooltipContextValue {
|
|
5
5
|
open: boolean
|
|
6
6
|
setOpen: (open: boolean) => void
|
|
7
|
+
mousePosition: { x: number; y: number }
|
|
8
|
+
setMousePosition: (pos: { x: number; y: number }) => void
|
|
7
9
|
}
|
|
8
10
|
|
|
9
11
|
const TooltipContext = React.createContext<TooltipContextValue | null>(null)
|
|
@@ -28,7 +30,7 @@ interface TooltipProps {
|
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
/**
|
|
31
|
-
* Tooltip component for hover hints
|
|
33
|
+
* Tooltip component for hover hints - appears at mouse cursor position
|
|
32
34
|
*
|
|
33
35
|
* @example
|
|
34
36
|
* <TooltipProvider>
|
|
@@ -40,12 +42,13 @@ interface TooltipProps {
|
|
|
40
42
|
*/
|
|
41
43
|
function Tooltip({ children, open: controlledOpen, onOpenChange, defaultOpen = false }: TooltipProps) {
|
|
42
44
|
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)
|
|
45
|
+
const [mousePosition, setMousePosition] = React.useState({ x: 0, y: 0 })
|
|
43
46
|
|
|
44
47
|
const open = controlledOpen !== undefined ? controlledOpen : uncontrolledOpen
|
|
45
48
|
const setOpen = onOpenChange || setUncontrolledOpen
|
|
46
49
|
|
|
47
50
|
return (
|
|
48
|
-
<TooltipContext.Provider value={{ open, setOpen }}>
|
|
51
|
+
<TooltipContext.Provider value={{ open, setOpen, mousePosition, setMousePosition }}>
|
|
49
52
|
<span className="relative inline-block">
|
|
50
53
|
{children}
|
|
51
54
|
</span>
|
|
@@ -64,6 +67,9 @@ const TooltipTrigger = React.forwardRef<HTMLSpanElement, TooltipTriggerProps>(
|
|
|
64
67
|
|
|
65
68
|
const handleMouseEnter = () => context.setOpen(true)
|
|
66
69
|
const handleMouseLeave = () => context.setOpen(false)
|
|
70
|
+
const handleMouseMove = (e: React.MouseEvent) => {
|
|
71
|
+
context.setMousePosition({ x: e.clientX, y: e.clientY })
|
|
72
|
+
}
|
|
67
73
|
const handleFocus = () => context.setOpen(true)
|
|
68
74
|
const handleBlur = () => context.setOpen(false)
|
|
69
75
|
|
|
@@ -71,6 +77,7 @@ const TooltipTrigger = React.forwardRef<HTMLSpanElement, TooltipTriggerProps>(
|
|
|
71
77
|
return React.cloneElement(children as React.ReactElement<any>, {
|
|
72
78
|
onMouseEnter: handleMouseEnter,
|
|
73
79
|
onMouseLeave: handleMouseLeave,
|
|
80
|
+
onMouseMove: handleMouseMove,
|
|
74
81
|
onFocus: handleFocus,
|
|
75
82
|
onBlur: handleBlur,
|
|
76
83
|
ref,
|
|
@@ -82,6 +89,7 @@ const TooltipTrigger = React.forwardRef<HTMLSpanElement, TooltipTriggerProps>(
|
|
|
82
89
|
ref={ref}
|
|
83
90
|
onMouseEnter={handleMouseEnter}
|
|
84
91
|
onMouseLeave={handleMouseLeave}
|
|
92
|
+
onMouseMove={handleMouseMove}
|
|
85
93
|
onFocus={handleFocus}
|
|
86
94
|
onBlur={handleBlur}
|
|
87
95
|
tabIndex={0}
|
|
@@ -103,12 +111,22 @@ const TooltipContent = React.forwardRef<
|
|
|
103
111
|
|
|
104
112
|
if (!context.open) return null
|
|
105
113
|
|
|
114
|
+
// Offset from cursor (slightly above and to the right)
|
|
115
|
+
const offsetX = 10
|
|
116
|
+
const offsetY = -10
|
|
117
|
+
|
|
106
118
|
return (
|
|
107
119
|
<div
|
|
108
120
|
ref={ref}
|
|
109
121
|
role="tooltip"
|
|
122
|
+
style={{
|
|
123
|
+
position: 'fixed',
|
|
124
|
+
left: context.mousePosition.x + offsetX,
|
|
125
|
+
top: context.mousePosition.y + offsetY,
|
|
126
|
+
transform: 'translateY(-100%)',
|
|
127
|
+
}}
|
|
110
128
|
className={cn(
|
|
111
|
-
"
|
|
129
|
+
"z-[9999] overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 pointer-events-none whitespace-nowrap",
|
|
112
130
|
className
|
|
113
131
|
)}
|
|
114
132
|
{...props}
|