route-tabs.tsx 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. "use client";
  2. import { useCallback, useState, type KeyboardEvent } from "react";
  3. interface AddressBarProps {
  4. route: string;
  5. onNavigate: (route: string) => void;
  6. }
  7. export function AddressBar({ route, onNavigate }: AddressBarProps) {
  8. const [value, setValue] = useState(route);
  9. const [focused, setFocused] = useState(false);
  10. const commit = useCallback(() => {
  11. let normalized = value.trim();
  12. if (!normalized.startsWith("/")) normalized = "/" + normalized;
  13. onNavigate(normalized);
  14. setValue(normalized);
  15. }, [value, onNavigate]);
  16. const handleKeyDown = useCallback(
  17. (e: KeyboardEvent<HTMLInputElement>) => {
  18. if (e.key === "Enter") {
  19. commit();
  20. (e.target as HTMLInputElement).blur();
  21. }
  22. },
  23. [commit],
  24. );
  25. const displayValue = focused ? value : route;
  26. return (
  27. <div className="flex items-center h-10 px-2 border-b border-border bg-muted/30">
  28. <div className="flex items-center flex-1 h-7 rounded-md border border-border bg-background px-2 gap-1.5">
  29. <svg
  30. width="14"
  31. height="14"
  32. viewBox="0 0 24 24"
  33. fill="none"
  34. stroke="currentColor"
  35. strokeWidth="2"
  36. strokeLinecap="round"
  37. strokeLinejoin="round"
  38. className="text-muted-foreground shrink-0"
  39. >
  40. <circle cx="12" cy="12" r="10" />
  41. <path d="M2 12h20" />
  42. <path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
  43. </svg>
  44. <input
  45. type="text"
  46. value={displayValue}
  47. onChange={(e) => setValue(e.target.value)}
  48. onFocus={() => {
  49. setFocused(true);
  50. setValue(route);
  51. }}
  52. onBlur={() => {
  53. setFocused(false);
  54. commit();
  55. }}
  56. onKeyDown={handleKeyDown}
  57. spellCheck={false}
  58. className="flex-1 bg-transparent text-xs font-mono text-foreground outline-none placeholder:text-muted-foreground"
  59. placeholder="/"
  60. />
  61. </div>
  62. </div>
  63. );
  64. }