The weeb for the next gen discord boat - Wamellow
wamellow.com
bot
discord
1"use client";
2
3import { Button } from "@/components/ui/button";
4import { cn } from "@/utils/cn";
5import { composeEventHandlers } from "@radix-ui/primitive";
6import { useComposedRefs } from "@radix-ui/react-compose-refs";
7import { Primitive } from "@radix-ui/react-primitive";
8import { Slot } from "@radix-ui/react-slot";
9import * as React from "react";
10
11export type InputBaseContextProps = Pick<
12 InputBaseProps,
13 "autoFocus" | "disabled"
14> & {
15 controlRef: React.RefObject<HTMLElement | null>;
16 onFocusedChange: (focused: boolean) => void;
17};
18
19const InputBaseContext = React.createContext<InputBaseContextProps>({
20 autoFocus: false,
21 controlRef: { current: null },
22 disabled: false,
23 onFocusedChange: () => {}
24});
25
26function useInputBase() {
27 const context = React.useContext(InputBaseContext);
28 if (!context) {
29 throw new Error("useInputBase must be used within a <InputBase />.");
30 }
31
32 return context;
33}
34
35export interface InputBaseProps
36 extends React.ComponentProps<typeof Primitive.div> {
37 autoFocus?: boolean;
38 disabled?: boolean;
39 error?: boolean;
40}
41
42function InputBase({
43 autoFocus,
44 disabled,
45 className,
46 onClick,
47 error,
48 ...props
49}: InputBaseProps) {
50 const [focused, setFocused] = React.useState(false);
51 const controlRef = React.useRef<HTMLElement>(null);
52 const handleClick = React.useCallback<React.MouseEventHandler<HTMLDivElement>>((event) => {
53 onClick?.(event);
54 if (event.defaultPrevented) return;
55 if (controlRef.current && event.currentTarget === event.target) {
56 controlRef.current.focus();
57 }
58 }, [onClick]);
59
60 return (
61 <InputBaseContext.Provider
62 value={{
63 autoFocus,
64 controlRef,
65 disabled,
66 onFocusedChange: setFocused
67 }}
68 >
69 <Primitive.div
70 data-slot="input-base"
71 onClick={handleClick}
72 className={cn(
73 "border-input dark:bg-input/30 flex min-h-9 cursor-text items-center gap-2 rounded-lg border bg-transparent px-3 py-1 text-base shadow-2xs transition-[color,box-shadow] outline-hidden md:text-sm",
74 disabled && "pointer-events-none cursor-not-allowed opacity-50",
75 focused && "border-ring ring-ring/50 ring-[3px]",
76 error &&
77 "ring-destructive/20 dark:ring-destructive/40 border-destructive",
78 className
79 )}
80 {...props}
81 />
82 </InputBaseContext.Provider>
83 );
84}
85
86function InputBaseFlexWrapper({
87 className,
88 ...props
89}: React.ComponentProps<typeof Primitive.div>) {
90 return (
91 <Primitive.div
92 data-slot="input-base-flex-wrapper"
93 className={cn("flex flex-1 flex-wrap", className)}
94 {...props}
95 />
96 );
97}
98
99function InputBaseControl({
100 ref,
101 onFocus,
102 onBlur,
103 ...props
104}: React.ComponentProps<typeof Slot>) {
105 const { controlRef, autoFocus, disabled, onFocusedChange } = useInputBase();
106
107 const composedRefs = useComposedRefs(controlRef, ref);
108
109 return (
110 <Slot
111 data-slot="input-base-control"
112 ref={composedRefs}
113 autoFocus={autoFocus}
114 onFocus={composeEventHandlers(onFocus, () => onFocusedChange(true))}
115 onBlur={composeEventHandlers(onBlur, () => onFocusedChange(false))}
116 {...{ disabled }}
117 {...props}
118 />
119 );
120}
121
122export interface InputBaseAdornmentProps extends React.ComponentProps<"div"> {
123 asChild?: boolean;
124}
125
126function InputBaseAdornment({
127 className,
128 asChild,
129 children,
130 ...props
131}: InputBaseAdornmentProps) {
132 const Comp = asChild ? Slot : typeof children === "string" ? "p" : "div";
133
134 return (
135 <Comp
136 data-slot="input-base-adornment"
137 className={cn(
138 "text-muted-foreground flex items-center [&_svg:not([class*='size-'])]:size-4",
139 "[&:not(:has(button))]:pointer-events-none",
140 className
141 )}
142 {...props}
143 >
144 {children}
145 </Comp>
146 );
147}
148
149function InputBaseAdornmentButton({
150 type = "button",
151 variant = "ghost",
152 size = "icon",
153 disabled: disabledProp,
154 className,
155 ...props
156}: React.ComponentProps<typeof Button>) {
157 const { disabled } = useInputBase();
158
159 return (
160 <Button
161 data-slot="input-base-adornment-button"
162 type={type}
163 variant={variant}
164 size={size}
165 disabled={disabled || disabledProp}
166 className={cn("size-6", className)}
167 {...props}
168 />
169 );
170}
171
172function InputBaseInput({
173 className,
174 ...props
175}: React.ComponentProps<typeof Primitive.input>) {
176 return (
177 <Primitive.input
178 data-slot="input-base-input"
179 className={cn(
180 "placeholder:text-muted-foreground file:text-foreground w-full flex-1 bg-transparent file:border-0 file:bg-transparent file:text-sm file:font-medium focus:outline-hidden disabled:pointer-events-none",
181 className
182 )}
183 {...props}
184 />
185 );
186}
187
188function InputBaseTextarea({
189 className,
190 ...props
191}: React.ComponentProps<"textarea">) {
192 return (
193 <textarea
194 data-slot="input-base-textarea"
195 className={cn(
196 "placeholder:text-muted-foreground min-h-16 flex-1 bg-transparent focus:outline-hidden disabled:pointer-events-none",
197 className
198 )}
199 {...props}
200 />
201 );
202}
203
204export {
205 InputBase,
206 InputBaseAdornment,
207 InputBaseAdornmentButton,
208 InputBaseControl,
209 InputBaseFlexWrapper,
210 InputBaseInput,
211 InputBaseTextarea,
212 useInputBase
213};