~koehr/donatello

944fb8dc61b08b995880ea8993a1859a646b2613 — Norman Köhring 1 year, 2 months ago
initial
A  => .gitignore +24 -0
@@ 1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

A  => index.html +13 -0
@@ 1,13 @@
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Donatello - has thoughts about SVG</title>
  </head>
  <body>
    <main></main>
    <script type="module" src="/src/demo.ts"></script>
  </body>
</html>

A  => package.json +15 -0
@@ 1,15 @@
{
  "name": "donatello",
  "private": true,
  "version": "0.0.1",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview"
  },
  "devDependencies": {
    "typescript": "^5.0.0",
    "vite": "^4.2.0"
  }
}
\ No newline at end of file

A  => public/vite.svg +1 -0
@@ 1,1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
\ No newline at end of file

A  => src/demo.ts +36 -0
@@ 1,36 @@
import { Container, Circle } from './donatello'
import './style.css'

document.querySelector<HTMLDivElement>('body > main')!.innerHTML = `
    <header>
      <h1>Donatello</h1>
      <p>...has thoughts about SVG</p>
    </header>
    <div id="demo01" class="demo"></div>
    <div id="demo02" class="demo"></div>
`

const demo01 = new Container('test', 800, 600, true)
const circle = new Circle(demo01, {
  r: 30,
  fill: "#FFF",
  stroke: "#A00",
  strokeWidth: 5,
})

document.getElementById('demo01').append(demo01.getNode())

circle.animate({
  r: 200,
  cx: 400,
}, 2000)

const demo02 = new Container('test', 800, 600)
new Circle(demo02, {
  r: 30,
  fill: "#FFF",
  stroke: "#0F0",
  "stroke-width": 2,
})

document.getElementById('demo02').append(demo02.getNode())

A  => src/donatello.d.ts +15 -0
@@ 1,15 @@
declare global {
  namespace Don {
    export interface Container {
      getNode: () => SVGElement
    }

    export interface Element<T> {
      getNode (): T
      getParent (): Element<any> | Container
      new (parent: Container, attributes: any): Element<T>
    }
  }
}

export {}

A  => src/donatello.ts +7 -0
@@ 1,7 @@
/**
 * Donatello v0.0.1
 * Inspired by Raphaël with the aim to bring its ease of use to the modern web
 */

export * from './elements/container'
export * from './elements/circle'
\ No newline at end of file

A  => src/elements/circle.ts +44 -0
@@ 1,44 @@
/// <reference path="../donatello.d.ts" />
import { applyAttributes, animateAttributes } from '../util'

export interface CircleAttributes {
  r: number
  cx: number
  cy: number
  stroke: string
  strokeWidth: number
  "stroke-width": number
  strokeOpacity: number
  "stroke-opacity": number
  fill: string
  fillOpacity: number
  "fill-opacity": number
}

export class Circle implements Don.Element<SVGCircleElement> {
  private el: SVGCircleElement
  private parent: Don.Container | Don.Element<any>
  private parentEl: SVGElement

  constructor (parent: Don.Container | Don.Element<any>, attributes: Partial<CircleAttributes>) {
    this.el = document.createElementNS('http://www.w3.org/2000/svg', 'circle')
    this.parent = parent
    this.parentEl = parent.getNode()

    applyAttributes(this.el, attributes)

    this.parentEl.append(this.el)
  }

  getNode () {
    return this.el
  }

  getParent () {
    return this.parent
  }

  animate (attrs: Partial<CircleAttributes>, duration = 1000) {
    animateAttributes(this.el, attrs, duration)
  }
}

A  => src/elements/container.ts +34 -0
@@ 1,34 @@
/// <reference path="../donatello.d.ts" />

/** Containers function as the parent to drawn elements */
export class Container implements Don.Container {
  private el: SVGElement

  /**
   * By default, containers are 1000 by 1000 units in size and the view box is
   * set to center at 500 by 500, but it can be set to center around 0 by 0.
   *
   * @param id: string - the elements ID property
   * @param width: number [1000] - how many units wide
   * @param height: number [1000] - how many units tall
   * @param zeroIsCenter: boolean [false] - center view box around 0,0
   */ 
  constructor (id: string, width = 1000, height = 1000, zeroIsCenter = false) {
    const minX = zeroIsCenter ? width / -2 : 0
    const minY = zeroIsCenter ? height / -2 : 0
    const svgEl = document.createElementNS('http://www.w3.org/2000/svg', 'svg')

    svgEl.setAttribute('viewBox', `${minX} ${minY} ${width} ${height}`)
    svgEl.id = id

    this.el = svgEl
  }

  /**
   * get containers' the dom node
   * @return SVGElement
   */
  getNode () {
    return this.el
  }
}

A  => src/style.css +55 -0
@@ 1,55 @@
:root {
  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
  line-height: 1.5;
  font-weight: 400;

  color-scheme: light dark;
  color: #dedede;
  background-color: #242424;

  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  display: flex;
  place-items: center;
  min-width: 320px;
}

body>main {
  max-width: 800px;
  width: 100%;
  min-height: 100vh;
  margin: 0 auto;
  text-align: center;
}

body>main>header {
  margin: 2em 0;
}

body>main>header>h1 {
  line-height: 0;
}

@media (prefers-color-scheme: light) {
  :root {
    color: #213547;
    background-color: #ffffff;
  }
}

.demo {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  aspect-ratio: 3 / 2;
  margin: 3em 0;
  box-shadow: 0 0 5px #aaf;
}
\ No newline at end of file

A  => src/typescript.svg +1 -0
@@ 1,1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="32" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path fill="#007ACC" d="M0 128v128h256V0H0z"></path><path fill="#FFF" d="m56.612 128.85l-.081 10.483h33.32v94.68h23.568v-94.68h33.321v-10.28c0-5.69-.122-10.444-.284-10.566c-.122-.162-20.4-.244-44.983-.203l-44.74.122l-.121 10.443Zm149.955-10.742c6.501 1.625 11.459 4.51 16.01 9.224c2.357 2.52 5.851 7.111 6.136 8.208c.08.325-11.053 7.802-17.798 11.988c-.244.162-1.22-.894-2.317-2.52c-3.291-4.795-6.745-6.867-12.028-7.233c-7.76-.528-12.759 3.535-12.718 10.321c0 1.992.284 3.17 1.097 4.795c1.707 3.536 4.876 5.649 14.832 9.956c18.326 7.883 26.168 13.084 31.045 20.48c5.445 8.249 6.664 21.415 2.966 31.208c-4.063 10.646-14.14 17.879-28.323 20.276c-4.388.772-14.79.65-19.504-.203c-10.28-1.828-20.033-6.908-26.047-13.572c-2.357-2.6-6.949-9.387-6.664-9.874c.122-.163 1.178-.813 2.356-1.504c1.138-.65 5.446-3.129 9.509-5.485l7.355-4.267l1.544 2.276c2.154 3.29 6.867 7.801 9.712 9.305c8.167 4.307 19.383 3.698 24.909-1.26c2.357-2.153 3.332-4.388 3.332-7.68c0-2.966-.366-4.266-1.91-6.501c-1.99-2.845-6.054-5.242-17.595-10.24c-13.206-5.69-18.895-9.224-24.096-14.832c-3.007-3.25-5.852-8.452-7.03-12.8c-.975-3.617-1.22-12.678-.447-16.335c2.723-12.76 12.353-21.659 26.25-24.3c4.51-.853 14.994-.528 19.424.569Z"></path></svg>
\ No newline at end of file

A  => src/util.ts +131 -0
@@ 1,131 @@
/** reasonable attribute defaults for many attributes */
export const AttributeDefaults = {
  blur: 0,
  cursor: 'default',
  cx: 0,
  cy: 0,
  fill: '#000',
  'fill-opacity': 1,
  'fill-rule': 'nonzero',
  font: '10px sans-serif',
  'font-family': 'sans-serif',
  'font-size': '16',
  'font-style': 'normal',
  'font-weight': 400,
  gradient: 0,
  height: 0,
  href: '#',
  'letter-spacing': 'normal',
  opacity: 1,
  path: 'M0,0',
  r: 0,
  rx: 0,
  ry: 0,
  src: '',
  stroke: 'none',
  'stroke-dasharray': 'none',
  'stroke-dashoffset': 0,
  'stroke-linecap': 'butt',
  'stroke-linejoin': 'miter',
  'stroke-miterlimit': 4,
  'stroke-opacity': 1,
  'stroke-width': 1,
  target: '_blank',
  'text-anchor': 'middle',
  title: '',
  transform: 'none',
  'transform-origin': '0 0',
  width: 0,
  x: 0,
  y: 0,
}

export type Attributes = Record<string, number | string>
export type NumberAttributes = Record<string, number>
export type AttributeName = keyof typeof AttributeDefaults
export type AttributeType = 'number' | 'color' | 'path' | 'transform'

/**
 * Attributes that Donatello can animate. Other attributes are supported, but
 * without transition. Donatello sets them in the middle of the animation.
 */
export const AnimatedAttrs: Partial<Record<AttributeName, AttributeType>> = {
  blur: 'number',
  cx: 'number',
  cy: 'number',
  fill: 'color',
  "fill-opacity": 'number',
  "font-size": 'number',
  height: 'number',
  opacity: 'number',
  path: "path",
  r: 'number',
  rx: 'number',
  ry: 'number',
  stroke: 'color',
  'stroke-opacity': 'number',
  'stroke-width': 'number',
  transform: 'transform',
  width: 'number',
  x: 'number',
  y: 'number',
}

/**
 * transforms a string from eg fooBar to foo-bar
 * so that attributes can be written in convenient, js-typical snakeCase
 */
export function ensureKebabCase (s: string): string {
  return s.replace(/([A-Z])/g, (m, _, i) => `${i ? '-' : ''}${m.toLowerCase()}`)
}

/** applies attributes, transforming from snakeCase to kebab-case as necessary */ 
export function applyAttributes(el: Element, attrs: Attributes) {
  for (const attr in attrs) {
    const kebabAttr = ensureKebabCase(attr)
    const value = `${attrs[attr]}`
    el.setAttribute(kebabAttr, value)
  }
}

export function animateNumberAttr(el: Element, toAttrs: NumberAttributes, duration: number) {
  const intermediate = {} as NumberAttributes // start attributes
  const singleStep = {} as NumberAttributes   // attributes set each animation step

  for (const attr in toAttrs) {
    const fromAttr = Number(el.getAttribute(attr)) ?? AttributeDefaults[attr]
    intermediate[attr] = fromAttr
    singleStep[attr] = (toAttrs[attr] - fromAttr) / duration
  }

  let start: number
  let lastTimestamp: number

  function step(timestamp: number) {
    if (start === undefined) start = timestamp
    if (lastTimestamp === undefined) lastTimestamp = timestamp
    const elapsed = timestamp - start
    const tickLength = timestamp - lastTimestamp

    for (const attr in intermediate) {
      intermediate[attr] += singleStep[attr] * tickLength
      if (intermediate[attr] > toAttrs[attr]) intermediate[attr] = toAttrs[attr]
    }
    applyAttributes(el, intermediate)

    if (elapsed > duration) return
    requestAnimationFrame(step)
    lastTimestamp = timestamp
  }
  requestAnimationFrame(step)
}
export function animateAttributes(el: Element, toAttrs: Attributes, duration: number) {
  // TODO: support more attribute types
  const numberAttrs = {} as NumberAttributes
  for (const attr in toAttrs) {
    const attrType = AnimatedAttrs[attr]
    if (attrType === 'number') numberAttrs[attr] = Number(toAttrs[attr])
  }

  animateNumberAttr(el, numberAttrs, duration)
}

A  => src/vite-env.d.ts +2 -0
@@ 1,2 @@
/// <reference types="vite/client" />
/// <reference path="./donatello.d.ts" />

A  => tsconfig.json +19 -0
@@ 1,19 @@
{
  "compilerOptions": {
    "target": "ESNext",
    "useDefineForClassFields": true,
    "module": "ESNext",
    "lib": ["ESNext", "DOM"],
    "moduleResolution": "Node",
    "strict": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "esModuleInterop": true,
    "noEmit": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "skipLibCheck": true
  },
  "include": ["src"]
}

A  => yarn.lock +232 -0
@@ 1,232 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


"@esbuild/android-arm64@0.17.12":
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.12.tgz#15a8e2b407d03989b899e325151dc2e96d19c620"
  integrity sha512-WQ9p5oiXXYJ33F2EkE3r0FRDFVpEdcDiwNX3u7Xaibxfx6vQE0Sb8ytrfQsA5WO6kDn6mDfKLh6KrPBjvkk7xA==

"@esbuild/android-arm@0.17.12":
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.12.tgz#677a09297e1f4f37aba7b4fc4f31088b00484985"
  integrity sha512-E/sgkvwoIfj4aMAPL2e35VnUJspzVYl7+M1B2cqeubdBhADV4uPon0KCc8p2G+LqSJ6i8ocYPCqY3A4GGq0zkQ==

"@esbuild/android-x64@0.17.12":
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.12.tgz#b292729eef4e0060ae1941f6a021c4d2542a3521"
  integrity sha512-m4OsaCr5gT+se25rFPHKQXARMyAehHTQAz4XX1Vk3d27VtqiX0ALMBPoXZsGaB6JYryCLfgGwUslMqTfqeLU0w==

"@esbuild/darwin-arm64@0.17.12":
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.12.tgz#efa35318df931da05825894e1787b976d55adbe3"
  integrity sha512-O3GCZghRIx+RAN0NDPhyyhRgwa19MoKlzGonIb5hgTj78krqp9XZbYCvFr9N1eUxg0ZQEpiiZ4QvsOQwBpP+lg==

"@esbuild/darwin-x64@0.17.12":
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.12.tgz#e7b54bb3f6dc81aadfd0485cd1623c648157e64d"
  integrity sha512-5D48jM3tW27h1qjaD9UNRuN+4v0zvksqZSPZqeSWggfMlsVdAhH3pwSfQIFJwcs9QJ9BRibPS4ViZgs3d2wsCA==

"@esbuild/freebsd-arm64@0.17.12":
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.12.tgz#99a18a8579d6299c449566fe91d9b6a54cf2a591"
  integrity sha512-OWvHzmLNTdF1erSvrfoEBGlN94IE6vCEaGEkEH29uo/VoONqPnoDFfShi41Ew+yKimx4vrmmAJEGNoyyP+OgOQ==

"@esbuild/freebsd-x64@0.17.12":
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.12.tgz#0e090190fede307fb4022f671791a50dd5121abd"
  integrity sha512-A0Xg5CZv8MU9xh4a+7NUpi5VHBKh1RaGJKqjxe4KG87X+mTjDE6ZvlJqpWoeJxgfXHT7IMP9tDFu7IZ03OtJAw==

"@esbuild/linux-arm64@0.17.12":
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.12.tgz#7fe2a69f8a1a7153fa2b0f44aabcadb59475c7e0"
  integrity sha512-cK3AjkEc+8v8YG02hYLQIQlOznW+v9N+OI9BAFuyqkfQFR+DnDLhEM5N8QRxAUz99cJTo1rLNXqRrvY15gbQUg==

"@esbuild/linux-arm@0.17.12":
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.12.tgz#b87c76ebf1fe03e01fd6bb5cfc2f3c5becd5ee93"
  integrity sha512-WsHyJ7b7vzHdJ1fv67Yf++2dz3D726oO3QCu8iNYik4fb5YuuReOI9OtA+n7Mk0xyQivNTPbl181s+5oZ38gyA==

"@esbuild/linux-ia32@0.17.12":
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.12.tgz#9e9357090254524d32e6708883a47328f3037858"
  integrity sha512-jdOBXJqcgHlah/nYHnj3Hrnl9l63RjtQ4vn9+bohjQPI2QafASB5MtHAoEv0JQHVb/xYQTFOeuHnNYE1zF7tYw==

"@esbuild/linux-loong64@0.17.12":
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.12.tgz#9deb605f9e2c82f59412ddfefb4b6b96d54b5b5b"
  integrity sha512-GTOEtj8h9qPKXCyiBBnHconSCV9LwFyx/gv3Phw0pa25qPYjVuuGZ4Dk14bGCfGX3qKF0+ceeQvwmtI+aYBbVA==

"@esbuild/linux-mips64el@0.17.12":
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.12.tgz#6ef170b974ddf5e6acdfa5b05f22b6e9dfd2b003"
  integrity sha512-o8CIhfBwKcxmEENOH9RwmUejs5jFiNoDw7YgS0EJTF6kgPgcqLFjgoc5kDey5cMHRVCIWc6kK2ShUePOcc7RbA==

"@esbuild/linux-ppc64@0.17.12":
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.12.tgz#1638d3d4acf1d34aaf37cf8908c2e1cefed16204"
  integrity sha512-biMLH6NR/GR4z+ap0oJYb877LdBpGac8KfZoEnDiBKd7MD/xt8eaw1SFfYRUeMVx519kVkAOL2GExdFmYnZx3A==

"@esbuild/linux-riscv64@0.17.12":
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.12.tgz#135b6e9270a8e2de2b9094bb21a287517df520ef"
  integrity sha512-jkphYUiO38wZGeWlfIBMB72auOllNA2sLfiZPGDtOBb1ELN8lmqBrlMiucgL8awBw1zBXN69PmZM6g4yTX84TA==

"@esbuild/linux-s390x@0.17.12":
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.12.tgz#21e40830770c5d08368e300842bde382ce97d615"
  integrity sha512-j3ucLdeY9HBcvODhCY4b+Ds3hWGO8t+SAidtmWu/ukfLLG/oYDMaA+dnugTVAg5fnUOGNbIYL9TOjhWgQB8W5g==

"@esbuild/linux-x64@0.17.12":
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.12.tgz#76c1c199871d48e1aaa47a762fb9e0dca52e1f7a"
  integrity sha512-uo5JL3cgaEGotaqSaJdRfFNSCUJOIliKLnDGWaVCgIKkHxwhYMm95pfMbWZ9l7GeW9kDg0tSxcy9NYdEtjwwmA==

"@esbuild/netbsd-x64@0.17.12":
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.12.tgz#c7c3b3017a4b938c76c35f66af529baf62eac527"
  integrity sha512-DNdoRg8JX+gGsbqt2gPgkgb00mqOgOO27KnrWZtdABl6yWTST30aibGJ6geBq3WM2TIeW6COs5AScnC7GwtGPg==

"@esbuild/openbsd-x64@0.17.12":
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.12.tgz#05d04217d980e049001afdbeacbb58d31bb5cefb"
  integrity sha512-aVsENlr7B64w8I1lhHShND5o8cW6sB9n9MUtLumFlPhG3elhNWtE7M1TFpj3m7lT3sKQUMkGFjTQBrvDDO1YWA==

"@esbuild/sunos-x64@0.17.12":
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.12.tgz#cf3862521600e4eb6c440ec3bad31ed40fb87ef3"
  integrity sha512-qbHGVQdKSwi0JQJuZznS4SyY27tYXYF0mrgthbxXrZI3AHKuRvU+Eqbg/F0rmLDpW/jkIZBlCO1XfHUBMNJ1pg==

"@esbuild/win32-arm64@0.17.12":
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.12.tgz#43dd7fb5be77bf12a1550355ab2b123efd60868e"
  integrity sha512-zsCp8Ql+96xXTVTmm6ffvoTSZSV2B/LzzkUXAY33F/76EajNw1m+jZ9zPfNJlJ3Rh4EzOszNDHsmG/fZOhtqDg==

"@esbuild/win32-ia32@0.17.12":
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.12.tgz#9940963d0bff4ea3035a84e2b4c6e41c5e6296eb"
  integrity sha512-FfrFjR4id7wcFYOdqbDfDET3tjxCozUgbqdkOABsSFzoZGFC92UK7mg4JKRc/B3NNEf1s2WHxJ7VfTdVDPN3ng==

"@esbuild/win32-x64@0.17.12":
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.12.tgz#3a11d13e9a5b0c05db88991b234d8baba1f96487"
  integrity sha512-JOOxw49BVZx2/5tW3FqkdjSD/5gXYeVGPDcB0lvap0gLQshkh1Nyel1QazC+wNxus3xPlsYAgqU1BUmrmCvWtw==

esbuild@^0.17.5:
  version "0.17.12"
  resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.12.tgz#2ad7523bf1bc01881e9d904bc04e693bd3bdcf2f"
  integrity sha512-bX/zHl7Gn2CpQwcMtRogTTBf9l1nl+H6R8nUbjk+RuKqAE3+8FDulLA+pHvX7aA7Xe07Iwa+CWvy9I8Y2qqPKQ==
  optionalDependencies:
    "@esbuild/android-arm" "0.17.12"
    "@esbuild/android-arm64" "0.17.12"
    "@esbuild/android-x64" "0.17.12"
    "@esbuild/darwin-arm64" "0.17.12"
    "@esbuild/darwin-x64" "0.17.12"
    "@esbuild/freebsd-arm64" "0.17.12"
    "@esbuild/freebsd-x64" "0.17.12"
    "@esbuild/linux-arm" "0.17.12"
    "@esbuild/linux-arm64" "0.17.12"
    "@esbuild/linux-ia32" "0.17.12"
    "@esbuild/linux-loong64" "0.17.12"
    "@esbuild/linux-mips64el" "0.17.12"
    "@esbuild/linux-ppc64" "0.17.12"
    "@esbuild/linux-riscv64" "0.17.12"
    "@esbuild/linux-s390x" "0.17.12"
    "@esbuild/linux-x64" "0.17.12"
    "@esbuild/netbsd-x64" "0.17.12"
    "@esbuild/openbsd-x64" "0.17.12"
    "@esbuild/sunos-x64" "0.17.12"
    "@esbuild/win32-arm64" "0.17.12"
    "@esbuild/win32-ia32" "0.17.12"
    "@esbuild/win32-x64" "0.17.12"

fsevents@~2.3.2:
  version "2.3.2"
  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
  integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==

function-bind@^1.1.1:
  version "1.1.1"
  resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
  integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==

has@^1.0.3:
  version "1.0.3"
  resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
  integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
  dependencies:
    function-bind "^1.1.1"

is-core-module@^2.9.0:
  version "2.11.0"
  resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144"
  integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==
  dependencies:
    has "^1.0.3"

nanoid@^3.3.4:
  version "3.3.4"
  resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
  integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==

path-parse@^1.0.7:
  version "1.0.7"
  resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
  integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==

picocolors@^1.0.0:
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
  integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==

postcss@^8.4.21:
  version "8.4.21"
  resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4"
  integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==
  dependencies:
    nanoid "^3.3.4"
    picocolors "^1.0.0"
    source-map-js "^1.0.2"

resolve@^1.22.1:
  version "1.22.1"
  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
  integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
  dependencies:
    is-core-module "^2.9.0"
    path-parse "^1.0.7"
    supports-preserve-symlinks-flag "^1.0.0"

rollup@^3.18.0:
  version "3.20.0"
  resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.20.0.tgz#ce7bd88449a776b9f75bf4e35959e25fbd3f51b1"
  integrity sha512-YsIfrk80NqUDrxrjWPXUa7PWvAfegZEXHuPsEZg58fGCdjL1I9C1i/NaG+L+27kxxwkrG/QEDEQc8s/ynXWWGQ==
  optionalDependencies:
    fsevents "~2.3.2"

source-map-js@^1.0.2:
  version "1.0.2"
  resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
  integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==

supports-preserve-symlinks-flag@^1.0.0:
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
  integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==

typescript@^5.0.0:
  version "5.0.2"
  resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.2.tgz#891e1a90c5189d8506af64b9ef929fca99ba1ee5"
  integrity sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==

vite@^4.2.0:
  version "4.2.1"
  resolved "https://registry.yarnpkg.com/vite/-/vite-4.2.1.tgz#6c2eb337b0dfd80a9ded5922163b94949d7fc254"
  integrity sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==
  dependencies:
    esbuild "^0.17.5"
    postcss "^8.4.21"
    resolve "^1.22.1"
    rollup "^3.18.0"
  optionalDependencies:
    fsevents "~2.3.2"