|
| 1 | +"use client"; |
| 2 | + |
| 3 | +import Link from "next/link"; |
| 4 | +import { useRef, useState } from "react"; |
| 5 | +import CommandSnippet from "./CommandSnippet"; |
| 6 | + |
| 7 | +type QuickStartTabsProps = { |
| 8 | + dockerCommand: string; |
| 9 | + vscodeDeepLinkUrl: string; |
| 10 | + dockerDocsUrl: string; |
| 11 | + vscodeDocsUrl: string; |
| 12 | +}; |
| 13 | + |
| 14 | +const TABS = [ |
| 15 | + { id: "command", label: "Docker command", description: "Run it yourself" }, |
| 16 | + { id: "guided", label: "Guided setup", description: "VS Code extension" }, |
| 17 | +] as const; |
| 18 | + |
| 19 | +type TabId = (typeof TABS)[number]["id"]; |
| 20 | + |
| 21 | +export default function QuickStartTabs({ |
| 22 | + dockerCommand, |
| 23 | + vscodeDeepLinkUrl, |
| 24 | + dockerDocsUrl, |
| 25 | + vscodeDocsUrl, |
| 26 | +}: QuickStartTabsProps) { |
| 27 | + const [activeTab, setActiveTab] = useState<TabId>("command"); |
| 28 | + const tabRefs = useRef<Partial<Record<TabId, HTMLButtonElement | null>>>({}); |
| 29 | + |
| 30 | + const selectTab = (id: TabId) => { |
| 31 | + setActiveTab(id); |
| 32 | + tabRefs.current[id]?.focus(); |
| 33 | + }; |
| 34 | + |
| 35 | + const onTabKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => { |
| 36 | + const currentIndex = TABS.findIndex((tab) => tab.id === activeTab); |
| 37 | + |
| 38 | + switch (event.key) { |
| 39 | + case "ArrowRight": |
| 40 | + case "ArrowLeft": { |
| 41 | + event.preventDefault(); |
| 42 | + const delta = event.key === "ArrowRight" ? 1 : -1; |
| 43 | + selectTab(TABS[(currentIndex + delta + TABS.length) % TABS.length].id); |
| 44 | + break; |
| 45 | + } |
| 46 | + case "Home": |
| 47 | + event.preventDefault(); |
| 48 | + selectTab(TABS[0].id); |
| 49 | + break; |
| 50 | + case "End": |
| 51 | + event.preventDefault(); |
| 52 | + selectTab(TABS[TABS.length - 1].id); |
| 53 | + break; |
| 54 | + default: |
| 55 | + break; |
| 56 | + } |
| 57 | + }; |
| 58 | + |
| 59 | + return ( |
| 60 | + <div> |
| 61 | + <div |
| 62 | + role="tablist" |
| 63 | + aria-label="Ways to set up DocumentDB locally" |
| 64 | + className="mb-4 grid grid-cols-2 gap-1 rounded-2xl border border-neutral-700 bg-neutral-900/80 p-1" |
| 65 | + > |
| 66 | + {TABS.map((tab) => { |
| 67 | + const isActive = tab.id === activeTab; |
| 68 | + |
| 69 | + return ( |
| 70 | + <button |
| 71 | + key={tab.id} |
| 72 | + ref={(element) => { |
| 73 | + tabRefs.current[tab.id] = element; |
| 74 | + }} |
| 75 | + type="button" |
| 76 | + role="tab" |
| 77 | + id={`quickstart-tab-${tab.id}`} |
| 78 | + aria-selected={isActive} |
| 79 | + aria-controls={`quickstart-panel-${tab.id}`} |
| 80 | + tabIndex={isActive ? 0 : -1} |
| 81 | + onClick={() => selectTab(tab.id)} |
| 82 | + onKeyDown={onTabKeyDown} |
| 83 | + className={`min-h-14 min-w-0 rounded-xl px-3 py-3 text-left text-sm font-semibold transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-300 ${ |
| 84 | + isActive |
| 85 | + ? "bg-neutral-700 text-white" |
| 86 | + : "text-gray-300 hover:text-white" |
| 87 | + }`} |
| 88 | + > |
| 89 | + {tab.label} |
| 90 | + <span |
| 91 | + className={`mt-1 block text-xs font-normal ${ |
| 92 | + isActive ? "text-gray-300" : "text-gray-400" |
| 93 | + }`} |
| 94 | + > |
| 95 | + {tab.description} |
| 96 | + </span> |
| 97 | + </button> |
| 98 | + ); |
| 99 | + })} |
| 100 | + </div> |
| 101 | + |
| 102 | + <div |
| 103 | + role="tabpanel" |
| 104 | + id="quickstart-panel-command" |
| 105 | + aria-labelledby="quickstart-tab-command" |
| 106 | + hidden={activeTab !== "command"} |
| 107 | + > |
| 108 | + <p className="mb-4 text-sm leading-6 text-gray-300"> |
| 109 | + Replace the username and password, then run the command. |
| 110 | + </p> |
| 111 | + <CommandSnippet command={dockerCommand} label="bash" /> |
| 112 | + <p className="mt-4 text-sm leading-6 text-gray-300"> |
| 113 | + Then connect with your preferred client. |
| 114 | + </p> |
| 115 | + <div className="mt-3 text-sm"> |
| 116 | + <Link |
| 117 | + href={dockerDocsUrl} |
| 118 | + className="font-semibold text-blue-300 transition-colors hover:text-blue-200" |
| 119 | + > |
| 120 | + Docker setup guide |
| 121 | + </Link> |
| 122 | + </div> |
| 123 | + </div> |
| 124 | + |
| 125 | + <div |
| 126 | + role="tabpanel" |
| 127 | + id="quickstart-panel-guided" |
| 128 | + aria-labelledby="quickstart-tab-guided" |
| 129 | + hidden={activeTab !== "guided"} |
| 130 | + > |
| 131 | + <p className="mb-4 text-sm leading-6 text-gray-300"> |
| 132 | + The VS Code extension creates your local database, generates |
| 133 | + credentials, and saves a connection. |
| 134 | + </p> |
| 135 | + <a |
| 136 | + href={vscodeDeepLinkUrl} |
| 137 | + aria-describedby="quickstart-vscode-setup-caption" |
| 138 | + className="inline-flex w-full items-center justify-center rounded-md bg-blue-500 px-6 py-3 text-sm font-semibold text-white transition-colors hover:bg-blue-400 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-300 sm:w-auto" |
| 139 | + > |
| 140 | + Set up in VS Code |
| 141 | + </a> |
| 142 | + <p |
| 143 | + id="quickstart-vscode-setup-caption" |
| 144 | + className="mt-3 text-sm leading-6 text-gray-400" |
| 145 | + > |
| 146 | + Don't have VS Code?{" "} |
| 147 | + <Link |
| 148 | + href="https://code.visualstudio.com/" |
| 149 | + target="_blank" |
| 150 | + rel="noopener noreferrer" |
| 151 | + className="font-semibold text-blue-300 transition-colors hover:text-blue-200" |
| 152 | + > |
| 153 | + Download it |
| 154 | + </Link>{" "} |
| 155 | + first. |
| 156 | + </p> |
| 157 | + <p className="mt-4 text-sm leading-6 text-gray-300"> |
| 158 | + When setup finishes, select Open Connection. |
| 159 | + </p> |
| 160 | + <div className="mt-4 text-sm"> |
| 161 | + <Link |
| 162 | + href={vscodeDocsUrl} |
| 163 | + className="font-semibold text-blue-300 transition-colors hover:text-blue-200" |
| 164 | + > |
| 165 | + VS Code setup guide |
| 166 | + </Link> |
| 167 | + </div> |
| 168 | + </div> |
| 169 | + </div> |
| 170 | + ); |
| 171 | +} |
0 commit comments