|
3 | 3 | import React, { useState, useEffect } from "react";
|
4 | 4 | import { CodeBlock } from "@/components/ui/code-block";
|
5 | 5 | import { InstallationTabs } from "@/components/docs/installation-tabs";
|
| 6 | +import { Button } from "@/components/ui/button"; |
| 7 | +import { |
| 8 | + DropdownMenu, |
| 9 | + DropdownMenuContent, |
| 10 | + DropdownMenuItem, |
| 11 | + DropdownMenuTrigger, |
| 12 | +} from "@/components/ui/dropdown-menu"; |
6 | 13 |
|
7 | 14 | export interface ComponentExample {
|
8 | 15 | title: string;
|
@@ -102,6 +109,48 @@ export function ComponentPreview({
|
102 | 109 | dependencies: false,
|
103 | 110 | });
|
104 | 111 |
|
| 112 | + // Fonctions helper pour LLM et navigation |
| 113 | + const copyPageContent = async () => { |
| 114 | + const content = `# ${name}\n\n${description}\n\n## Installation\n\n\`\`\`bash\nnpx @nativeui/cli add ${registryName}\n\`\`\`\n\n## Code\n\n\`\`\`tsx\n${componentCode}\n\`\`\`\n\n## Usage\n\n\`\`\`tsx\n${previewCode}\n\`\`\``; |
| 115 | + await navigator.clipboard.writeText(content); |
| 116 | + }; |
| 117 | + |
| 118 | + const openInLLM = (llm: string) => { |
| 119 | + const prompt = `I'm looking at this NativeUI documentation: https://nativeui.io/docs/components/${registryName}.md |
| 120 | +Help me understand how to use it. Be ready to explain concepts, give examples, or help debug based on it.`; |
| 121 | + |
| 122 | + const urls = { |
| 123 | + chatgpt: `https://chat.openai.com/?q=${encodeURIComponent(prompt)}`, |
| 124 | + claude: `https://claude.ai/new?q=${encodeURIComponent(prompt)}` |
| 125 | + }; |
| 126 | + |
| 127 | + window.open(urls[llm as keyof typeof urls], '_blank'); |
| 128 | + }; |
| 129 | + |
| 130 | + const viewAsMarkdown = () => { |
| 131 | + window.open(`/docs/components/${registryName}.md`, '_blank'); |
| 132 | + }; |
| 133 | + |
| 134 | + const navigateToComponent = (direction: 'next' | 'prev') => { |
| 135 | + // Liste des composants (à adapter selon votre structure) |
| 136 | + const components = ['accordion', 'alert', 'alert-dialog', 'avatar', 'badge', 'breadcrumb', 'button', 'calendar', 'card', 'carousel', 'checkbox', 'collapsible', 'combobox', 'date-time-picker', 'dialog', 'drawer', 'dropdown', 'input', 'input-otp', 'label', 'pagination', 'popover', 'progress', 'radio-group', 'select', 'separator', 'sheet', 'skeleton', 'slider', 'switch', 'table', 'tabs', 'textarea', 'toggle', 'toggle-group', 'tooltip']; |
| 137 | + |
| 138 | + const currentIndex = components.indexOf(registryName); |
| 139 | + if (currentIndex === -1) return; |
| 140 | + |
| 141 | + let targetIndex; |
| 142 | + if (direction === 'next') { |
| 143 | + targetIndex = currentIndex + 1; |
| 144 | + if (targetIndex >= components.length) targetIndex = 0; |
| 145 | + } else { |
| 146 | + targetIndex = currentIndex - 1; |
| 147 | + if (targetIndex < 0) targetIndex = components.length - 1; |
| 148 | + } |
| 149 | + |
| 150 | + const targetComponent = components[targetIndex]; |
| 151 | + window.location.href = `/docs/components/${targetComponent}`; |
| 152 | + }; |
| 153 | + |
105 | 154 | useEffect(() => {
|
106 | 155 | const loadAllResources = async () => {
|
107 | 156 | setIsLoading(true);
|
@@ -162,7 +211,67 @@ export function ComponentPreview({
|
162 | 211 | return (
|
163 | 212 | <div className="container max-w-3xl py-10">
|
164 | 213 | <div className="space-y-4">
|
165 |
| - <h1 className="text-3xl font-bold tracking-tight">{name}</h1> |
| 214 | + <div className="flex items-center justify-between"> |
| 215 | + <h1 className="text-3xl font-bold tracking-tight">{name}</h1> |
| 216 | + <div className="flex items-center gap-2"> |
| 217 | + <Button |
| 218 | + variant="ghost" |
| 219 | + size="sm" |
| 220 | + onClick={() => navigateToComponent('prev')} |
| 221 | + className="text-muted-foreground hover:text-foreground" |
| 222 | + > |
| 223 | + <svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 224 | + <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" /> |
| 225 | + </svg> |
| 226 | + </Button> |
| 227 | + <Button |
| 228 | + variant="ghost" |
| 229 | + size="sm" |
| 230 | + onClick={() => navigateToComponent('next')} |
| 231 | + className="text-muted-foreground hover:text-foreground" |
| 232 | + > |
| 233 | + <svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 234 | + <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" /> |
| 235 | + </svg> |
| 236 | + </Button> |
| 237 | + <DropdownMenu> |
| 238 | + <DropdownMenuTrigger asChild> |
| 239 | + <Button variant="outline" size="sm" className="gap-2"> |
| 240 | + <svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 241 | + <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" /> |
| 242 | + </svg> |
| 243 | + Copy Page |
| 244 | + <svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 245 | + <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" /> |
| 246 | + </svg> |
| 247 | + </Button> |
| 248 | + </DropdownMenuTrigger> |
| 249 | + <DropdownMenuContent align="end" className="w-48"> |
| 250 | + <DropdownMenuItem onClick={copyPageContent}> |
| 251 | + <svg className="h-4 w-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 252 | + <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" /> |
| 253 | + </svg> |
| 254 | + Copy as Markdown |
| 255 | + </DropdownMenuItem> |
| 256 | + <DropdownMenuItem onClick={viewAsMarkdown}> |
| 257 | + <svg className="h-4 w-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 258 | + <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /> |
| 259 | + <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" /> |
| 260 | + </svg> |
| 261 | + View as Markdown |
| 262 | + </DropdownMenuItem> |
| 263 | + <DropdownMenuItem onClick={() => openInLLM('chatgpt')}> |
| 264 | + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M22.282 9.821a5.985 5.985 0 0 0-.516-4.91 6.046 6.046 0 0 0-6.51-2.9A6.065 6.065 0 0 0 4.981 4.18a5.985 5.985 0 0 0-3.998 2.9 6.046 6.046 0 0 0 .743 7.097 5.98 5.98 0 0 0 .51 4.911 6.051 6.051 0 0 0 6.515 2.9A5.985 5.985 0 0 0 13.26 24a6.056 6.056 0 0 0 5.772-4.206 5.99 5.99 0 0 0 3.997-2.9 6.056 6.056 0 0 0-.747-7.073zM13.26 22.43a4.476 4.476 0 0 1-2.876-1.04l.141-.081 4.779-2.758a.795.795 0 0 0 .392-.681v-6.737l2.02 1.168a.071.071 0 0 1 .038.052v5.583a4.504 4.504 0 0 1-4.494 4.494zM3.6 18.304a4.47 4.47 0 0 1-.535-3.014l.142.085 4.783 2.759a.771.771 0 0 0 .78 0l5.843-3.369v2.332a.08.08 0 0 1-.033.062L9.74 19.95a4.5 4.5 0 0 1-6.14-1.646zM2.34 7.896a4.485 4.485 0 0 1 2.366-1.973V11.6a.766.766 0 0 0 .388.676l5.815 3.355-2.02 1.168a.076.076 0 0 1-.071 0l-4.83-2.786A4.504 4.504 0 0 1 2.34 7.872zm16.597 3.855-5.833-3.387L15.119 7.2a.076.076 0 0 1 .071 0l4.83 2.791a4.494 4.494 0 0 1-.676 8.105v-5.678a.79.79 0 0 0-.407-.667zm2.01-3.023-.141-.085-4.774-2.782a.776.776 0 0 0-.785 0L9.409 9.23V6.897a.066.066 0 0 1 .028-.061l4.83-2.787a4.5 4.5 0 0 1 6.68 4.66zm-12.64 4.135-2.02-1.164a.08.08 0 0 1-.038-.057V6.075a4.5 4.5 0 0 1 7.375-3.453l-.142.08-4.778 2.758a.795.795 0 0 0-.393.681zm1.097-2.365 2.602-1.5 2.607 1.5v2.999l-2.597 1.5-2.607-1.5Z" fill="currentColor"></path></svg> |
| 265 | + Open in ChatGPT |
| 266 | + </DropdownMenuItem> |
| 267 | + <DropdownMenuItem onClick={() => openInLLM('claude')}> |
| 268 | + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m4.714 15.956 4.718-2.648.079-.23-.08-.128h-.23l-.79-.048-2.695-.073-2.337-.097-2.265-.122-.57-.121-.535-.704.055-.353.48-.321.685.06 1.518.104 2.277.157 1.651.098 2.447.255h.389l.054-.158-.133-.097-.103-.098-2.356-1.596-2.55-1.688-1.336-.972-.722-.491L2 6.223l-.158-1.008.655-.722.88.06.225.061.893.686 1.906 1.476 2.49 1.833.364.304.146-.104.018-.072-.164-.274-1.354-2.446-1.445-2.49-.644-1.032-.17-.619a2.972 2.972 0 0 1-.103-.729L6.287.133 6.7 0l.995.134.42.364.619 1.415L9.735 4.14l1.555 3.03.455.898.243.832.09.255h.159V9.01l.127-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.583.28.48.685-.067.444-.286 1.851-.558 2.903-.365 1.942h.213l.243-.242.983-1.306 1.652-2.064.728-.82.85-.904.547-.431h1.032l.759 1.129-.34 1.166-1.063 1.347-.88 1.142-1.263 1.7-.79 1.36.074.11.188-.02 2.853-.606 1.542-.28 1.84-.315.832.388.09.395-.327.807-1.967.486-2.307.462-3.436.813-.043.03.049.061 1.548.146.662.036h1.62l3.018.225.79.522.473.638-.08.485-1.213.62-1.64-.389-3.825-.91-1.31-.329h-.183v.11l1.093 1.068 2.003 1.81 2.508 2.33.127.578-.321.455-.34-.049-2.204-1.657-.85-.747-1.925-1.62h-.127v.17l.443.649 2.343 3.521.122 1.08-.17.353-.607.213-.668-.122-1.372-1.924-1.415-2.168-1.141-1.943-.14.08-.674 7.254-.316.37-.728.28-.607-.461-.322-.747.322-1.476.388-1.924.316-1.53.285-1.9.17-.632-.012-.042-.14.018-1.432 1.967-2.18 2.945-1.724 1.845-.413.164-.716-.37.066-.662.401-.589 2.386-3.036 1.439-1.882.929-1.086-.006-.158h-.055L4.138 18.56l-1.13.146-.485-.456.06-.746.231-.243 1.907-1.312Z" fill="currentColor"></path></svg> |
| 269 | + Open in Claude |
| 270 | + </DropdownMenuItem> |
| 271 | + </DropdownMenuContent> |
| 272 | + </DropdownMenu> |
| 273 | + </div> |
| 274 | + </div> |
166 | 275 | <p className="text-muted-foreground text-lg">{description}</p>
|
167 | 276 | </div>
|
168 | 277 |
|
@@ -279,17 +388,17 @@ export function ComponentPreview({
|
279 | 388 | <button
|
280 | 389 | onClick={() => setActiveInstallTab("cli")}
|
281 | 390 | className={`px-4 py-2 text-sm font-medium ${activeInstallTab === "cli"
|
282 |
| - ? "border-b-2 border-primary text-primary" |
283 |
| - : "text-muted-foreground hover:text-foreground" |
| 391 | + ? "border-b-2 border-primary text-primary" |
| 392 | + : "text-muted-foreground hover:text-foreground" |
284 | 393 | }`}
|
285 | 394 | >
|
286 | 395 | CLI
|
287 | 396 | </button>
|
288 | 397 | <button
|
289 | 398 | onClick={() => setActiveInstallTab("manual")}
|
290 | 399 | className={`px-4 py-2 text-sm font-medium ${activeInstallTab === "manual"
|
291 |
| - ? "border-b-2 border-primary text-primary" |
292 |
| - : "text-muted-foreground hover:text-foreground" |
| 400 | + ? "border-b-2 border-primary text-primary" |
| 401 | + : "text-muted-foreground hover:text-foreground" |
293 | 402 | }`}
|
294 | 403 | >
|
295 | 404 | Manual
|
|
0 commit comments