Skip to the content

Taisen Brackets対戦

Knockout brackets as SVG, from a list of ties.

Taisen (対戦, たいせん) is Japanese for a matchup: two sides facing each other. About the name

Contents

How-to

In React, Vue, Svelte and Angular

Draw a bracket in React, Vue, Svelte and Angular: the SVG string goes in as HTML.

drawBracket gives a string of SVG, so every framework uses it the same way: make the string from your data, and give it to the framework as HTML. The drawing carries its own style and no script, and every mark it is given is cleaned, so the string is safe to put in the page as it is. It also draws the same on a server, so a server render has the bracket in it.

React

Bracket.tsx; dangerouslySetInnerHTML is how React takes HTML.

import { drawBracket, type Matchup } from "@johnmorrisdotca/taisen-brackets";

const matchups: Matchup[] = [
  { sides: ["Aurora", "Harbour"], score: [2, 1] },
  { sides: ["Cascade", "Delta"], score: [0, 1] },
  { sides: ["Aurora", "Delta"], score: [3, 1] },
];

export function Bracket({ theme = "auto" }: { theme?: "auto" | "light" | "dark" }) {
  return <div id="bracket" dangerouslySetInnerHTML={{ __html: drawBracket(matchups, { theme }) }} />;
}

Mount it as usual. The component renders to a string on a server (renderToString), as the package's tests check: the bracket is in the page before the browser runs anything.

import { createRoot } from "react-dom/client";
import { Bracket } from "./Bracket";

createRoot(document.getElementById("root")!).render(<Bracket />);

Vue

v-html takes the string.

<script setup lang="ts">
import { drawBracket, type Matchup } from "@johnmorrisdotca/taisen-brackets";

const props = withDefaults(defineProps<{ theme?: "auto" | "light" | "dark" }>(), { theme: "auto" });
const matchups: Matchup[] = [
  { sides: ["Aurora", "Harbour"], score: [2, 1] },
  { sides: ["Cascade", "Delta"], score: [0, 1] },
  { sides: ["Aurora", "Delta"], score: [3, 1] },
];
</script>

<template>
  <div id="bracket" v-html="drawBracket(matchups, { theme: props.theme })"></div>
</template>
import { createApp } from "vue";
import Bracket from "./Bracket.vue";

createApp(Bracket).mount("#app");

Svelte

{@html …} takes the string.

<script lang="ts">
  import { drawBracket, type Matchup } from "@johnmorrisdotca/taisen-brackets";

  let { theme = "auto" }: { theme?: "auto" | "light" | "dark" } = $props();
  const matchups: Matchup[] = [
    { sides: ["Aurora", "Harbour"], score: [2, 1] },
    { sides: ["Cascade", "Delta"], score: [0, 1] },
    { sides: ["Aurora", "Delta"], score: [3, 1] },
  ];
</script>

<div id="bracket">{@html drawBracket(matchups, { theme })}</div>
import { mount } from "svelte";
import Bracket from "./Bracket.svelte";

mount(Bracket, { target: document.getElementById("app")! });

Angular

Angular's [innerHTML] would strip the drawing's <style>, so the string goes through bypassSecurityTrustHtml:

import { Component, inject, Input } from "@angular/core";
import { DomSanitizer, type SafeHtml } from "@angular/platform-browser";
import { drawBracket, type Matchup } from "@johnmorrisdotca/taisen-brackets";

const matchups: Matchup[] = [
  { sides: ["Aurora", "Harbour"], score: [2, 1] },
  { sides: ["Cascade", "Delta"], score: [0, 1] },
  { sides: ["Aurora", "Delta"], score: [3, 1] },
];

@Component({
  selector: "app-bracket",
  standalone: true,
  template: `<div id="bracket" [innerHTML]="svg"></div>`,
})
export class BracketComponent {
  @Input() theme: "auto" | "light" | "dark" = "auto";

  private readonly sanitizer = inject(DomSanitizer);

  // The SVG is made here from your own data, and the package cleans every mark it is given: so it is trusted as it is.
  get svg(): SafeHtml {
    return this.sanitizer.bypassSecurityTrustHtml(drawBracket(matchups, { theme: this.theme }));
  }
}
import "@angular/compiler";
import { bootstrapApplication } from "@angular/platform-browser";
import { provideZonelessChangeDetection } from "@angular/core";
import { BracketComponent } from "./bracket.component";

void bootstrapApplication(BracketComponent, { providers: [provideZonelessChangeDetection()] });

Next.js, Nuxt, SvelteKit

They render on a server first, and drawBracket draws the same there, so the components work unchanged. In Next.js's App Router a component that only draws needs no "use client".

View sourceEdit on GitHubMade from the source of @johnmorrisdotca/taisen-brackets 0.20.0.