Projekt-Tagebuch Blog, Tag 32


coden
Rustikaler, tropischer Arbeitsplatz mit einem Laptop auf einem Holztisch, daneben eine Hängematte.
Daniel Kaser|13. Juni 2024
3 min.
Donnerstag, 13. Juni 2024

Wochen-Ziele:

  • Blog-Projekt-Tagebuch (Mai) in den Blog uploaden

Tages-Ziele:


Gestern hab ich die automatischen id’s für Headings in Markdown eingerichtet. Heute sollte dann die Auto-Copy-to-Clipboard-Funktion her.

Auto-Copy-to-Clipboard

Wenn man auf eine Überschrift klickt, soll ein fertiger Link, inklusive Anchor direkt zu der geklickten Überschrift in die Zwischenablage kopiert werden. Außerdem soll beim Hovern über die Überschrift ein Link-Symbol daneben erscheinen.

Dafür habe ich eine clientseitige CopyableHeading-Komponente erstellt.

// CopyableHeading.tsx
export function CopyableHeading({
  id,
  className,
  children,
  as: Tag,
}: CopyableHeadingProps) {
  const [confirmation, setConfirmation] = useState("");
  const pathname = usePathname();
  id = id || createId(children);

  function handleCopy() {
    const link = `${window.location.origin}${pathname}#${id}`;
    navigator.clipboard.writeText(link).then(() => {
      setConfirmation("Link kopiert!");
      setTimeout(() => setConfirmation(""), 1000);
    });
  }

  return (
    <div
      className="group cursor-pointer"
      onClick={handleCopy}
      title="Link kopieren"
    >
      <Tag
        id={id}
        className={`flex items-center space-x-[0.5em] hover:text-foreground/75 ${className}`}
      >
        <span>{children}</span>
        <Link2Icon className="h-[1em] w-[1em] text-foreground/65 opacity-0 transition-opacity duration-500 hover:text-danger-600 group-hover:opacity-100" />
        <CopyConfirmation confirmation={confirmation} />
      </Tag>
    </div>
  );
}

Bei der Gelegenheit hab ich die createId()-Funktion, die ich gestern erstellt und in die CustomStyledMDX-Komponente eingebaut bzw. gemappt habe, in die CopyableHeading-Kompnente mit eingebaut (id = id || createId(children);).

D.h., sie konnte aus der CustomStyledMDX-Komponente wieder raus. Dafür hab ich die Standard-HTML-Heading-Tags mit meinen Heading-Komponenten ersetzt:

// src/components/CustomStyledMDX.tsx (alt)
// ...
export const customComponents: MDXComponents = {
  Image,
  h2: ({ children }) => <h2 id={createId(children)}>{children}</h2>,
  h3: ({ children }) => <h3 id={createId(children)}>{children}</h3>,
  h4: ({ children }) => <h4 id={createId(children)}>{children}</h4>,
  // ...
};
// components/CustomStyledMDX.tsx (neu)
// ...
export const customComponents: MDXComponents = {
  Image,
  h2: (props) => <H2 {...props}>{props.children}</H2>,
  h3: (props) => <H3 {...props}>{props.children}</H3>,
  h4: (props) => <H4 {...props}>{props.children}</H4>,
  // ...
};

Als nächstes musste ich dann meine bestehenden Heading-Komponenten (H2-H6) anpassen. Da man die h1-Überschrift nur 1x pro Seite verwenden sollte und das am besten ganz oben, hab ich die Kopier-Funktion hier weggelassen. Hier als Beispiel meine H2-Komponente:

// Headings.tsx
// ...
export function H2({ id, className = "", children }: HeadingProps) {
  return (
    <CopyableHeading
      as="h2"
      id={id}
      className={`mb-6 mt-0 text-2xl sm:text-3xl lg:text-4xl ${className}`}
    >
      {children}
    </CopyableHeading>
  );
}
// ...

Jetzt kann man einfach auf jede Überschrift im Blog (außer h1) klicken und bekommt den Link dorthin in die Zwischenablage kopiert. Und das Beste ist: Diese Funktion, inklusive der dazugehörigen id’s wird vollautomatisch generiert. Ohne dass ich für jede Überschrift eine id setzen muss.

Geil! 💪

Feedback

Schreib mir!