gebeya-whatsapp-otp
Version:
React WhatsApp OTP verification component with Supabase integration
102 lines (101 loc) • 5.58 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import React, { useState, useRef, useEffect } from "react";
import { cn } from "../../utils/cn";
export const Select = ({ value, onValueChange, children, searchable = false, placeholder, }) => {
const [isOpen, setIsOpen] = useState(false);
const [searchValue, setSearchValue] = useState("");
const selectRef = useRef(null);
const searchInputRef = useRef(null);
useEffect(() => {
const handleClickOutside = (event) => {
if (selectRef.current &&
!selectRef.current.contains(event.target)) {
setIsOpen(false);
setSearchValue("");
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
useEffect(() => {
if (isOpen && searchable && searchInputRef.current) {
// Focus search input when dropdown opens
setTimeout(() => {
searchInputRef.current?.focus();
}, 100);
}
}, [isOpen, searchable]);
const handleToggle = () => {
setIsOpen(!isOpen);
if (!isOpen) {
setSearchValue("");
}
};
const handleSelect = (selectedValue) => {
onValueChange?.(selectedValue);
setIsOpen(false);
setSearchValue("");
};
const handleSearchChange = (newSearchValue) => {
setSearchValue(newSearchValue);
};
return (_jsx("div", { className: "relative", ref: selectRef, children: React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
if (child.type === SelectTrigger) {
return React.cloneElement(child, { onClick: handleToggle });
}
if (child.type === SelectContent) {
return React.cloneElement(child, {
isOpen,
onSelect: handleSelect,
searchable,
searchValue,
onSearchChange: handleSearchChange,
searchInputRef,
});
}
if (child.type === SelectValue) {
return React.cloneElement(child, { value, placeholder });
}
}
return child;
}) }));
};
export const SelectTrigger = ({ children, className, onClick, }) => {
return (_jsxs("div", { className: cn("flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 cursor-pointer", className), onClick: onClick, children: [children, _jsx("svg", { className: "h-4 w-4 opacity-50", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }));
};
export const SelectValue = ({ children, placeholder = "", value, }) => {
return _jsx("span", { children: children || value || placeholder });
};
export const SelectContent = ({ children, isOpen, onSelect, searchable, searchValue, onSearchChange, searchInputRef, }) => {
if (!isOpen)
return null;
// Filter children based on search value
const filteredChildren = React.Children.toArray(children).filter((child) => {
if (!searchable || !searchValue || !React.isValidElement(child)) {
return true;
}
// Extract search text from child props
const childProps = child.props;
const searchText = childProps.searchText || childProps.value || "";
return searchText.toLowerCase().includes(searchValue.toLowerCase());
});
return (_jsxs("div", { className: "absolute top-full left-0 right-0 z-50 mt-1 w-full rounded-md border bg-white shadow-lg", children: [searchable && (_jsx("div", { className: "p-2 border-b bg-white sticky top-0 z-10", children: _jsx("input", { ref: searchInputRef, type: "text", placeholder: "", value: searchValue, onChange: (e) => onSearchChange?.(e.target.value), className: "w-full px-3 py-2 text-sm border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent", onClick: (e) => e.stopPropagation() }) })), _jsx("div", { className: "overflow-y-auto", style: {
maxHeight: "200px",
scrollbarWidth: "thin",
scrollbarColor: "#cbd5e0 #f7fafc",
}, children: filteredChildren.length > 0 ? (React.Children.map(filteredChildren, (child) => {
if (React.isValidElement(child) && child.type === SelectItem) {
return React.cloneElement(child, { onSelect });
}
return child;
})) : (_jsx("div", { className: "px-3 py-2 text-sm text-gray-500 text-center", children: "No countries found" })) })] }));
};
export const SelectItem = ({ value, children, onSelect, }) => {
const handleClick = () => {
onSelect?.(value);
};
return (_jsx("div", { className: "relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-2 pr-2 text-sm outline-none hover:bg-accent hover:text-accent-foreground", onClick: handleClick, children: children }));
};