Menu

Change language

GitHub repo

Code — Multi-Language Tabs

blockmarketing

Code sample switcher with a language tab row, a copy control reporting its own success state, and a caption line naming the endpoint the sample calls.

Installation

$npx uipkge-ng@latest add code-tabs-multi-language
Manifest:https://uipkge.dev/r/angular/code-tabs-multi-language.jsonInstalls to:src/app/components/blocks/code-tabs-multi-language/

Variants

Loading interactive previews…

Schema

Type aliases exported from this item's source. Use these to shape the data you pass in.

CodeSample
interface CodeSample {
  id: string
  label: string
  code: string
}

npm dependencies

Files installed (2)

  • src/app/components/blocks/code-tabs-multi-language/code-tabs-multi-language.component.ts5.2 kB
    // Code sample switcher with a language tab row, a copy control reporting its own
    // success state, and a caption line naming the endpoint the sample calls.
    // Port of the React block 1:1.
    import { ChangeDetectionStrategy, Component, DestroyRef, inject, Input, signal } from '@angular/core'
    import { Check, Copy, LucideAngularModule } from 'lucide-angular'
    import { cn } from '@/lib/utils'
    import { UiBadgeComponent } from '@/ui/badge/badge.component'
    import { UiButtonComponent } from '@/ui/button/button.component'
    import { UiCardComponent, UiCardContentComponent } from '@/ui/card/card.component'
    import {
      UiTabsComponent,
      UiTabsContentComponent,
      UiTabsListComponent,
      UiTabsTriggerComponent,
    } from '@/ui/tabs/tabs.component'
    
    export interface CodeSample {
      id: string
      label: string
      code: string
    }
    
    // Samples are plain strings, so a highlighter can be layered on later without
    // touching this component.
    const samples: CodeSample[] = [
      {
        id: 'curl',
        label: 'cURL',
        code: `curl https://api.northwind.dev/v1/metrics/revenue_net \\
      -H "Authorization: Bearer $NORTHWIND_TOKEN" \\
      -d 'window=trailing_28d' \\
      -d 'group_by=region'`,
      },
      {
        id: 'ts',
        label: 'TypeScript',
        code: `import { Northwind } from '@northwind/metrics'
    
    const client = new Northwind(process.env.NORTHWIND_TOKEN)
    
    const revenue = await client.metrics.get('revenue_net', {
      window: 'trailing_28d',
      groupBy: ['region'],
    })`,
      },
      {
        id: 'python',
        label: 'Python',
        code: `from northwind import Northwind
    
    client = Northwind(os.environ["NORTHWIND_TOKEN"])
    
    revenue = client.metrics.get(
        "revenue_net",
        window="trailing_28d",
        group_by=["region"],
    )`,
      },
    ]
    
    @Component({
      changeDetection: ChangeDetectionStrategy.Eager,
      selector: 'ui-code-tabs-multi-language, [ui-code-tabs-multi-language]',
      standalone: true,
      // React renders the root <section> itself: the host stays out of layout and `class` goes to the root.
      host: { '[attr.class]': '"contents"' },
      imports: [
        LucideAngularModule,
        UiBadgeComponent,
        UiButtonComponent,
        UiCardComponent,
        UiCardContentComponent,
        UiTabsComponent,
        UiTabsContentComponent,
        UiTabsListComponent,
        UiTabsTriggerComponent,
      ],
      template: `
        <section data-slot="code-tabs-multi-language" [class]="rootClass">
          <div class="mx-auto max-w-3xl px-6 py-20">
            <span ui-badge variant="secondary">API</span>
            <h2 class="mt-4 text-2xl font-semibold tracking-tight">Read a certified metric in three lines</h2>
            <p class="text-muted-foreground mt-2">
              The same definition the dashboards use. Scope is applied from the caller's token, not the query.
            </p>
    
            <ui-tabs defaultValue="curl" class="mt-6">
              <ui-tabs-list variant="segmented" class="flex-nowrap overflow-x-auto">
                @for (sample of samplesList; track sample.id) {
                  <button ui-tabs-trigger [value]="sample.id" variant="segmented">
                    {{ sample.label }}
                  </button>
                }
              </ui-tabs-list>
    
              @for (sample of samplesList; track sample.id) {
                <ui-tabs-content [value]="sample.id" class="mt-4">
                  <div ui-card>
                    <div ui-card-content class="p-0">
                      <div class="border-border flex items-center gap-3 border-b px-4 py-2">
                        <span class="text-muted-foreground font-mono text-xs">GET /v1/metrics/revenue_net</span>
                        <button
                          ui-button
                          variant="ghost"
                          size="sm"
                          class="ml-auto h-7 gap-1.5 text-xs"
                          [attr.aria-label]="'Copy the ' + sample.label + ' sample'"
                          (click)="copy(sample.code, sample.id)"
                        >
                          @if (copied() === sample.id) {
                            <lucide-icon [img]="Check" class="text-success size-3.5" aria-hidden="true" />
                          } @else {
                            <lucide-icon [img]="Copy" class="size-3.5" aria-hidden="true" />
                          }
                          {{ copied() === sample.id ? 'Copied' : 'Copy' }}
                        </button>
                      </div>
                      <pre
                        class="overflow-x-auto p-4 font-mono text-xs leading-relaxed"
                      ><code>{{ sample.code }}</code></pre>
                    </div>
                  </div>
                </ui-tabs-content>
              }
            </ui-tabs>
          </div>
        </section>
      `,
    })
    export class UiCodeTabsMultiLanguageComponent {
      protected readonly Check = Check
      protected readonly Copy = Copy
    
      protected readonly samplesList = samples
    
      @Input('class') className?: string
    
      readonly copied = signal('')
      private copyTimer: ReturnType<typeof setTimeout> | undefined
    
      constructor() {
        inject(DestroyRef).onDestroy(() => clearTimeout(this.copyTimer))
      }
    
      get rootClass(): string {
        return cn('bg-background', this.className)
      }
    
      async copy(code: string, id: string): Promise<void> {
        try {
          await navigator.clipboard.writeText(code)
          this.copied.set(id)
          clearTimeout(this.copyTimer)
          this.copyTimer = setTimeout(() => this.copied.set(''), 1600)
        } catch {
          // Clipboard blocked: the sample stays selectable by hand.
        }
      }
    }
    
  • src/app/components/blocks/code-tabs-multi-language/index.ts0.1 kB

Raw manifest:https://uipkge.dev/r/angular/code-tabs-multi-language.json