Skip to content

Instantly share code, notes, and snippets.

@wilmoore
Created June 21, 2024 22:41
Show Gist options
  • Save wilmoore/5700db124429f92bfe8808b193044718 to your computer and use it in GitHub Desktop.
Save wilmoore/5700db124429f92bfe8808b193044718 to your computer and use it in GitHub Desktop.
Software Engineering :: Web :: Development :: API :: Web Components :: Example :: Copy To Clipboard Button

Software Engineering :: Web :: Development :: API :: Web Components :: Example :: Copy To Clipboard Button

⪼ Made with 💜 by Polyglot.

import IconCheck from '@icon/icon-check.svg'
import IconCopy from '@icon/icon-copy.svg'
import IconCopyError from '@icon/icon-copy-error.svg'

import { copyToUserClipboard } from '@core/clipboard'

const ICON_HIDE_DURATION_IN_MILLISECONDS = 2000;
const TEMPLATE = `
  <span data-state="closed">
    <button class="rounded-lg text-token-text-secondary hover:bg-token-main-surface-secondary tooltip-button" style="position: relative;">
      <span class="flex h-[30px] w-[30px] items-center justify-center">${IconCopy}</span>
      <tool-tip text="${chrome.i18n.getMessage('copy_tooltip_text')}" event-target-selector=".tooltip-button" />
    </button>
  </span>
`;

export class CopyButton extends HTMLElement {
  private readonly conversationTitle: string;

  constructor() {
    super()
    this.conversationTitle = this.getAttribute('conversationTitle') as string
  }

  connectedCallback(): void {
    this.render()
    this.addEventListeners()
  }

  disconnectedCallback(): void {
    this.removeEventListeners()
  }

  private addEventListeners(): void {
    const button = this.querySelector('button') as HTMLButtonElement
    if (!(button instanceof HTMLButtonElement)) return
    button.addEventListener('click', this.copyTitleToClipboard)
  }

  private removeEventListeners(): void {
    const button = this.querySelector('button') as HTMLButtonElement
    if (!(button instanceof HTMLButtonElement)) return
    button.removeEventListener('click', this.copyTitleToClipboard)
  }

  private setIcon = (icon: string): void => {
    const element = this.querySelector('svg')
    if (!element) return
    element.outerHTML = icon
  }

  private flashIcon = (icon: string): void => {
    this.setIcon(icon)
    setTimeout(() => this.setIcon(IconCopy), ICON_HIDE_DURATION_IN_MILLISECONDS)
  }

  private copyTitleToClipboard = (): void => {
    copyToUserClipboard({
      clipboardText: this.conversationTitle,
      onSuccess: () => this.flashIcon(IconCheck),
      onFailure: (error: Error) => {
        console.error('Failed to copy text:', error)
        this.flashIcon(IconCopyError)
      },
    })
  }

  private render(): void {
    this.innerHTML = TEMPLATE;
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment