├── src ├── vite-env.d.ts ├── counter.ts ├── utils.ts ├── main.ts ├── typescript.svg ├── style.css └── gradient.ts ├── vite.config.ts ├── package.json ├── README.md ├── .gitignore ├── tsconfig.json ├── .github └── workflows │ └── main.yml ├── public └── vite.svg ├── index.html └── pnpm-lock.yaml /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | 3 | export default defineConfig({ 4 | base: '/gradient-picker/', 5 | }) -------------------------------------------------------------------------------- /src/counter.ts: -------------------------------------------------------------------------------- 1 | export function setupCounter(element: HTMLButtonElement) { 2 | let counter = 0 3 | const setCounter = (count: number) => { 4 | counter = count 5 | element.innerHTML = `count is ${counter}` 6 | } 7 | element.addEventListener('click', () => setCounter(counter + 1)) 8 | setCounter(0) 9 | } 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gradient-picker", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "typescript": "^4.9.3", 13 | "vite": "^4.0.0" 14 | } 15 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

Gradient Picker

3 | 4 |

A website that allows you to create your own gradient

5 | 6 |

Open website

7 | 8 | ![image](https://user-images.githubusercontent.com/45036724/212474185-215a71b8-6bdd-4930-8598-b80ac22ecd07.png) 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | 2 | export function createElement(tagName: K, attrs?: Record, styles?: Record): HTMLElementTagNameMap[K] { 3 | const el = document.createElement(tagName) 4 | 5 | for(const attr in attrs) { 6 | el.setAttribute(attr, attrs[attr]) 7 | } 8 | 9 | for(const style in styles) { 10 | el.style.setProperty(style, styles[style]) 11 | } 12 | 13 | return el 14 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ESNext", "DOM"], 7 | "moduleResolution": "Node", 8 | "strict": true, 9 | "resolveJsonModule": true, 10 | "isolatedModules": true, 11 | "esModuleInterop": true, 12 | "noEmit": true, 13 | "noUnusedLocals": true, 14 | "noUnusedParameters": true, 15 | "noImplicitReturns": true, 16 | "skipLibCheck": true 17 | }, 18 | "include": ["src"] 19 | } 20 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build and deploy 2 | on: 3 | push: 4 | branches: [ master ] 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: actions/setup-node@v2 12 | 13 | - uses: pnpm/action-setup@v2.2.2 14 | with: 15 | version: 7.13.3 16 | run_install: true 17 | 18 | - name: Install and Build 🔧 19 | run: | 20 | pnpm run build 21 | 22 | - name: Deploy 🚀 23 | uses: JamesIves/github-pages-deploy-action@v4.2.5 24 | with: 25 | branch: gh-pages 26 | folder: ./dist 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { GradientPicker } from './gradient' 2 | import './style.css' 3 | 4 | const app = document.getElementById('app')! 5 | const preview = document.getElementById('preview')! 6 | const colorHandlers = document.createElement('div') 7 | colorHandlers.classList.add('color__handlers') 8 | 9 | // TODO: publish UI library 10 | new GradientPicker({ 11 | el: app, 12 | previewEl: preview, 13 | colorHandlersEl: colorHandlers, 14 | }) 15 | 16 | const copyCssButton = document.getElementById('copy-css') 17 | copyCssButton?.addEventListener('click', () => { 18 | let textarea = document.querySelector("#css") as HTMLTextAreaElement; 19 | navigator.clipboard.writeText(textarea.value); 20 | 21 | // Show "Copied!" text 22 | const copiedEl = document.getElementById('copied') 23 | copiedEl?.classList.add('show') 24 | setTimeout(() => copiedEl?.classList.remove('show'), 500); 25 | }) -------------------------------------------------------------------------------- /src/typescript.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Gradient Picker 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |

GradientPicker

17 |
18 |
19 | 20 |
21 | 25 | 32 |
33 | 34 |
35 |
36 |
CSS Value
37 |
38 | Copied! 39 | 40 |
41 |
42 | 43 |
44 |
45 | 46 | 47 | 48 |
49 |
50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | margin: 0; 4 | padding: 0; 5 | } 6 | :root { 7 | font-family: Inter, Avenir, Helvetica, Arial, sans-serif; 8 | font-size: 16px; 9 | line-height: 24px; 10 | font-weight: 400; 11 | 12 | background-color: #242424; 13 | 14 | font-synthesis: none; 15 | text-rendering: optimizeLegibility; 16 | -webkit-font-smoothing: antialiased; 17 | -moz-osx-font-smoothing: grayscale; 18 | -webkit-text-size-adjust: 100%; 19 | } 20 | 21 | .center-both { 22 | display: flex; 23 | justify-content: center; 24 | align-items: center; 25 | } 26 | 27 | a { 28 | font-weight: 500; 29 | color: #646cff; 30 | text-decoration: inherit; 31 | } 32 | a:hover { 33 | color: #535bf2; 34 | } 35 | 36 | body { 37 | margin: 0; 38 | min-width: 320px; 39 | min-height: 100vh; 40 | font-family: 'Noto Sans Tangsa'; 41 | } 42 | 43 | .container { 44 | width: min(500px, 100%); 45 | } 46 | 47 | h1 { 48 | font-size: clamp(3em, 5vw, 4em); 49 | font-family: 'Kaushan Script', 'arial'; 50 | color: white; 51 | line-height: 1.1; 52 | margin-bottom: 2rem; 53 | text-align: center; 54 | text-shadow: 3px 2px 5px #311e1e; 55 | } 56 | 57 | a { 58 | color: rgb(241, 241, 241); 59 | margin-top: 2rem; 60 | display: block; 61 | filter: drop-shadow(1px 3px 2px rgb(83, 83, 83)); 62 | } 63 | a:hover { 64 | color: white; 65 | } 66 | #app { 67 | height: 100vh; 68 | width: 100%; 69 | margin: 0 auto; 70 | padding: 2rem; 71 | text-align: center; 72 | } 73 | 74 | .card { 75 | background-color: white; 76 | padding: 2rem 1rem 1rem; 77 | border-radius: 1rem; 78 | } 79 | 80 | .color-preview { 81 | height: 3rem; 82 | border: 1px solid rgb(188, 188, 188); 83 | border-radius: 50rem; 84 | box-shadow: 1px 1px 3px rgb(140, 138, 138); 85 | cursor: crosshair; 86 | position: relative; 87 | } 88 | .color__handlers { 89 | height: 100%; 90 | border-radius: 50rem; 91 | overflow: hidden; 92 | position: relative; 93 | z-index: 3; 94 | } 95 | .color__handler { 96 | height: 100%; 97 | position: absolute; 98 | left: var(--handler-position); 99 | width: 5px; 100 | transform: translateX(-2.5px); 101 | background-color: rgba(31, 30, 30, 0.623); 102 | } 103 | 104 | .color__handler:hover { 105 | cursor: col-resize; 106 | } 107 | 108 | .color__handler-remover { 109 | width: 1.2rem; 110 | border: 1px solid #555; 111 | height: 1.2rem; 112 | cursor: pointer; 113 | background-color: white; 114 | box-shadow: 1px 1px 2px #ccc;; 115 | border-radius: 50%; 116 | position: absolute; 117 | top: -1.5rem; 118 | left: var(--handler-position); 119 | transform: translateX(-50%) 120 | } 121 | .color__handler-remover:after { 122 | content: 'x'; 123 | color: #555; 124 | position: absolute; 125 | left: 4px; 126 | top: -5px; 127 | } 128 | .color__handler-remover:before { 129 | content: ''; 130 | top: 100%; 131 | /* background-color: rgb(72, 72, 72); */ 132 | position: absolute; 133 | width: 1px; 134 | height: 20px; 135 | z-index: 1; 136 | } 137 | .color__handler-remover:hover { 138 | border-color: rgb(229, 64, 64); 139 | } 140 | .color__handler-remover:hover:after { 141 | color: rgb(229, 64, 64); 142 | } 143 | .color__input { 144 | border: none; 145 | border-radius: 50%; 146 | transform: translate(-2px,-2px); 147 | } 148 | .color__input-wrapper { 149 | width: 1.3rem; 150 | height: 1.3rem; 151 | border: 1px solid white; 152 | border-radius: 50%; 153 | overflow: hidden; 154 | border: .1rem solid white; 155 | outline: 1px solid #bbb; 156 | position: absolute; 157 | left: var(--handler-position); 158 | cursor: pointer; 159 | top: 110%; 160 | transform: translateX(-50%); 161 | } 162 | .options { 163 | display: flex; 164 | gap: 1rem; 165 | } 166 | select, textarea { 167 | padding: .5rem; 168 | width: 100% 169 | } 170 | select { 171 | background-color: white; 172 | border: 1px solid #928b8b; 173 | border-radius: .5rem; 174 | margin-top: 2rem; 175 | } 176 | .css-box__header { 177 | border: 1px solid #928b8b; 178 | display: flex; 179 | justify-content: space-between; 180 | padding: .5rem; 181 | margin-top: 1rem;; 182 | border-top-left-radius: .5rem; 183 | border-top-right-radius: .5rem; 184 | } 185 | .css-box__header h6 { 186 | margin: 0; 187 | } 188 | textarea { 189 | border: none; 190 | border-bottom-left-radius: .5rem; 191 | border: 1px solid #928b8b; 192 | border-top: none; 193 | border-bottom-right-radius: .5rem; 194 | } 195 | #copied { 196 | display: none; 197 | color: green; 198 | margin-right: .5rem; 199 | } 200 | .show { 201 | display: inline-block !important; 202 | } 203 | #copy-css { 204 | color: white; 205 | background-color: #535bf2; 206 | padding: .3rem .5rem; 207 | border-radius: .4rem; 208 | cursor: pointer; 209 | border: none; 210 | } 211 | #copy-css:hover { 212 | background-color: #6169ff; 213 | } -------------------------------------------------------------------------------- /src/gradient.ts: -------------------------------------------------------------------------------- 1 | import { createElement } from "./utils" 2 | 3 | interface Props { 4 | el: HTMLElement 5 | previewEl: HTMLElement 6 | colorHandlersEl: HTMLElement 7 | } 8 | 9 | type GradientDirection = "top" | "left" | "center" | "bottom" | "right" 10 | type GradientType = "linear" | "radial" 11 | type GradientStop = { 12 | color: string 13 | position: number 14 | } 15 | 16 | export class GradientPicker { 17 | direction: GradientDirection = "right" 18 | el: Props['el'] 19 | previewEl: Props['previewEl'] 20 | colorHandlersEl: Props['colorHandlersEl'] 21 | type: GradientType = "linear" 22 | stops: GradientStop[] = [] 23 | isDragging = false 24 | 25 | constructor({ el, previewEl, colorHandlersEl }: Props) { 26 | this.el = el 27 | this.previewEl = previewEl 28 | this.colorHandlersEl = colorHandlersEl 29 | this.previewEl.append(this.colorHandlersEl) 30 | this.addColorStop("#3494E6", .5) 31 | this.addColorStop("#EC6EAD", 99) 32 | 33 | this.listener() 34 | } 35 | 36 | /** 37 | * Add color to the gradient 38 | * @param color The color string (HEX/RGB/any supported css color format) 39 | * @param position The position of the stop (0-100) 40 | */ 41 | addColorStop(color: string, position: number) { 42 | this.stops.push({ color, position }) 43 | this.createStopHandler(this.stops.length-1) 44 | this.updateElementBackground() 45 | } 46 | 47 | changeGradientType(type: GradientType) { 48 | this.type = type 49 | this.updateElementBackground() 50 | } 51 | 52 | getGradientString(type: GradientType = this.type, direction: GradientDirection = this.direction) { 53 | const round = (num: number) => Math.round(num * 100) / 100 54 | const colorConcat = [...this.stops] 55 | .sort((a,b) => a.position - b.position) 56 | .map(stop => ` ${stop.color} ${round(stop.position)}%`).join(',') 57 | 58 | if(type === 'radial') { 59 | const radialPositions: Record = { 60 | "bottom": "at center bottom", 61 | "center": "", 62 | "left": "at left center", 63 | "right": "at center right", 64 | "top": "at center top", 65 | } 66 | return `radial-gradient(circle ${radialPositions[direction]}, ${colorConcat})` 67 | } 68 | 69 | return `linear-gradient(to ${direction},${colorConcat})` 70 | } 71 | 72 | private updateElementBackground() { 73 | const gradientString = this.getGradientString() 74 | this.el.style.backgroundImage = gradientString 75 | this.previewEl.style.backgroundImage = this.getGradientString('linear', 'right') 76 | let cssTextbox = document.getElementById('css')! 77 | cssTextbox.textContent = gradientString 78 | } 79 | 80 | private createStopHandler(stopIndex: number) { 81 | const colorStop = this.stops[stopIndex] 82 | 83 | // Handler bar 84 | const handler = createElement('div', { class: 'color__handler', 'data-index': stopIndex.toString() }, { '--handler-position': `${colorStop.position}%` }) 85 | 86 | // Handler remover 87 | const handlerButtons = createElement('div', { class: 'color__handler-buttons', 'data-index': stopIndex.toString() }, { '--handler-position': `${colorStop.position}%` }) 88 | const handlerRemover = createElement('div', { class: 'color__handler-remover' }) 89 | 90 | // Color picker 91 | const inputColorWrapper = createElement('div', { 92 | type: 'color', 93 | class: 'color__input-wrapper', 94 | }) 95 | const inputColor = createElement('input', { 96 | type: 'color', 97 | class: 'color__input', 98 | value: colorStop.color 99 | }) 100 | inputColorWrapper.append(inputColor) 101 | 102 | inputColor.addEventListener('input', e => this.onColorChange(e as InputEvent, stopIndex)) 103 | handler.addEventListener('mousedown', e => this.onHandlerMouseDown(e)) 104 | handler.addEventListener('mouseup', e => this.onHandlerMouseUp(e)) 105 | this.previewEl.addEventListener('mousemove', e => this.onHandlerMouseMove(e)) 106 | handlerRemover.addEventListener('click', () => { 107 | this.stops.splice(stopIndex, 1) 108 | handler.remove() 109 | handlerButtons.remove() 110 | this.updateElementBackground() 111 | }) 112 | 113 | handlerButtons.append(handlerRemover, inputColorWrapper) 114 | this.colorHandlersEl.append(handler) 115 | this.previewEl.append(handlerButtons) 116 | } 117 | 118 | onHandlerMouseDown(event: MouseEvent) { 119 | let handlerEl = event.target as HTMLElement 120 | handlerEl.classList.add('active') 121 | this.isDragging = true 122 | } 123 | 124 | onHandlerMouseMove(event: MouseEvent) { 125 | if(!this.isDragging) return 126 | 127 | let handlerEl = document.querySelector('.color__handler.active') 128 | if(!handlerEl?.classList.contains('active')) return 129 | const stopIndex = ~~(handlerEl.getAttribute('data-index') || 0) 130 | 131 | const newStopPosition = this.getPercentage(event.clientX) 132 | 133 | if(newStopPosition < 0.5 || newStopPosition > 99.5) return 134 | 135 | this.changePosition(stopIndex, newStopPosition) 136 | this.updateElementBackground() 137 | } 138 | 139 | onHandlerMouseUp(event: MouseEvent) { 140 | let handlerEl = event.target as HTMLElement 141 | handlerEl.classList.remove('active') 142 | this.isDragging = false 143 | } 144 | 145 | changeColor(stopIndex: number, color: string) { 146 | this.stops[stopIndex].color = color 147 | } 148 | 149 | changePosition(stopIndex: number, position: number) { 150 | this.stops[stopIndex].position = position 151 | this.previewEl.querySelectorAll(`div[data-index='${stopIndex}']`).forEach((el) => (el as HTMLElement).style.setProperty('--handler-position', position+'%')) 152 | } 153 | 154 | onColorChange(event: InputEvent, index: number) { 155 | this.changeColor(index, (event.target as HTMLInputElement).value) 156 | this.updateElementBackground() 157 | } 158 | 159 | getPercentage(mouseX: number) { 160 | const rect = this.previewEl.getBoundingClientRect() 161 | const clickPosition = mouseX - rect.x 162 | const elementWidth = getComputedStyle(this.previewEl).width.slice(0, -2) 163 | const newStopPosition = clickPosition/~~elementWidth * 100 164 | 165 | return newStopPosition 166 | } 167 | 168 | listener() { 169 | this.previewEl.addEventListener('click', e => { 170 | if((e.target as HTMLElement).classList.contains('color__handler') || this.isDragging) return 171 | if(!this.colorHandlersEl.contains(e.target as HTMLElement)) return 172 | 173 | const newStopPosition = this.getPercentage(e.clientX) 174 | 175 | this.addColorStop("#333333", newStopPosition) 176 | }) 177 | const directionInput = document.getElementById('direction') as HTMLInputElement 178 | const typeInput = document.getElementById('type') as HTMLInputElement 179 | 180 | directionInput?.addEventListener('input', () => { 181 | this.direction = directionInput.value as GradientDirection 182 | this.updateElementBackground() 183 | }) 184 | typeInput?.addEventListener('input', () => { 185 | this.type = typeInput.value as GradientType 186 | 187 | this.updateElementBackground() 188 | }) 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: 5.4 2 | 3 | specifiers: 4 | typescript: ^4.9.3 5 | vite: ^4.0.0 6 | 7 | devDependencies: 8 | typescript: 4.9.4 9 | vite: 4.0.4 10 | 11 | packages: 12 | 13 | /@esbuild/android-arm/0.16.17: 14 | resolution: {integrity: sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==} 15 | engines: {node: '>=12'} 16 | cpu: [arm] 17 | os: [android] 18 | requiresBuild: true 19 | dev: true 20 | optional: true 21 | 22 | /@esbuild/android-arm64/0.16.17: 23 | resolution: {integrity: sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==} 24 | engines: {node: '>=12'} 25 | cpu: [arm64] 26 | os: [android] 27 | requiresBuild: true 28 | dev: true 29 | optional: true 30 | 31 | /@esbuild/android-x64/0.16.17: 32 | resolution: {integrity: sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==} 33 | engines: {node: '>=12'} 34 | cpu: [x64] 35 | os: [android] 36 | requiresBuild: true 37 | dev: true 38 | optional: true 39 | 40 | /@esbuild/darwin-arm64/0.16.17: 41 | resolution: {integrity: sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==} 42 | engines: {node: '>=12'} 43 | cpu: [arm64] 44 | os: [darwin] 45 | requiresBuild: true 46 | dev: true 47 | optional: true 48 | 49 | /@esbuild/darwin-x64/0.16.17: 50 | resolution: {integrity: sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==} 51 | engines: {node: '>=12'} 52 | cpu: [x64] 53 | os: [darwin] 54 | requiresBuild: true 55 | dev: true 56 | optional: true 57 | 58 | /@esbuild/freebsd-arm64/0.16.17: 59 | resolution: {integrity: sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==} 60 | engines: {node: '>=12'} 61 | cpu: [arm64] 62 | os: [freebsd] 63 | requiresBuild: true 64 | dev: true 65 | optional: true 66 | 67 | /@esbuild/freebsd-x64/0.16.17: 68 | resolution: {integrity: sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==} 69 | engines: {node: '>=12'} 70 | cpu: [x64] 71 | os: [freebsd] 72 | requiresBuild: true 73 | dev: true 74 | optional: true 75 | 76 | /@esbuild/linux-arm/0.16.17: 77 | resolution: {integrity: sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==} 78 | engines: {node: '>=12'} 79 | cpu: [arm] 80 | os: [linux] 81 | requiresBuild: true 82 | dev: true 83 | optional: true 84 | 85 | /@esbuild/linux-arm64/0.16.17: 86 | resolution: {integrity: sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==} 87 | engines: {node: '>=12'} 88 | cpu: [arm64] 89 | os: [linux] 90 | requiresBuild: true 91 | dev: true 92 | optional: true 93 | 94 | /@esbuild/linux-ia32/0.16.17: 95 | resolution: {integrity: sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==} 96 | engines: {node: '>=12'} 97 | cpu: [ia32] 98 | os: [linux] 99 | requiresBuild: true 100 | dev: true 101 | optional: true 102 | 103 | /@esbuild/linux-loong64/0.16.17: 104 | resolution: {integrity: sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==} 105 | engines: {node: '>=12'} 106 | cpu: [loong64] 107 | os: [linux] 108 | requiresBuild: true 109 | dev: true 110 | optional: true 111 | 112 | /@esbuild/linux-mips64el/0.16.17: 113 | resolution: {integrity: sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==} 114 | engines: {node: '>=12'} 115 | cpu: [mips64el] 116 | os: [linux] 117 | requiresBuild: true 118 | dev: true 119 | optional: true 120 | 121 | /@esbuild/linux-ppc64/0.16.17: 122 | resolution: {integrity: sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==} 123 | engines: {node: '>=12'} 124 | cpu: [ppc64] 125 | os: [linux] 126 | requiresBuild: true 127 | dev: true 128 | optional: true 129 | 130 | /@esbuild/linux-riscv64/0.16.17: 131 | resolution: {integrity: sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==} 132 | engines: {node: '>=12'} 133 | cpu: [riscv64] 134 | os: [linux] 135 | requiresBuild: true 136 | dev: true 137 | optional: true 138 | 139 | /@esbuild/linux-s390x/0.16.17: 140 | resolution: {integrity: sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==} 141 | engines: {node: '>=12'} 142 | cpu: [s390x] 143 | os: [linux] 144 | requiresBuild: true 145 | dev: true 146 | optional: true 147 | 148 | /@esbuild/linux-x64/0.16.17: 149 | resolution: {integrity: sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==} 150 | engines: {node: '>=12'} 151 | cpu: [x64] 152 | os: [linux] 153 | requiresBuild: true 154 | dev: true 155 | optional: true 156 | 157 | /@esbuild/netbsd-x64/0.16.17: 158 | resolution: {integrity: sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==} 159 | engines: {node: '>=12'} 160 | cpu: [x64] 161 | os: [netbsd] 162 | requiresBuild: true 163 | dev: true 164 | optional: true 165 | 166 | /@esbuild/openbsd-x64/0.16.17: 167 | resolution: {integrity: sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==} 168 | engines: {node: '>=12'} 169 | cpu: [x64] 170 | os: [openbsd] 171 | requiresBuild: true 172 | dev: true 173 | optional: true 174 | 175 | /@esbuild/sunos-x64/0.16.17: 176 | resolution: {integrity: sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==} 177 | engines: {node: '>=12'} 178 | cpu: [x64] 179 | os: [sunos] 180 | requiresBuild: true 181 | dev: true 182 | optional: true 183 | 184 | /@esbuild/win32-arm64/0.16.17: 185 | resolution: {integrity: sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==} 186 | engines: {node: '>=12'} 187 | cpu: [arm64] 188 | os: [win32] 189 | requiresBuild: true 190 | dev: true 191 | optional: true 192 | 193 | /@esbuild/win32-ia32/0.16.17: 194 | resolution: {integrity: sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==} 195 | engines: {node: '>=12'} 196 | cpu: [ia32] 197 | os: [win32] 198 | requiresBuild: true 199 | dev: true 200 | optional: true 201 | 202 | /@esbuild/win32-x64/0.16.17: 203 | resolution: {integrity: sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==} 204 | engines: {node: '>=12'} 205 | cpu: [x64] 206 | os: [win32] 207 | requiresBuild: true 208 | dev: true 209 | optional: true 210 | 211 | /esbuild/0.16.17: 212 | resolution: {integrity: sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==} 213 | engines: {node: '>=12'} 214 | hasBin: true 215 | requiresBuild: true 216 | optionalDependencies: 217 | '@esbuild/android-arm': 0.16.17 218 | '@esbuild/android-arm64': 0.16.17 219 | '@esbuild/android-x64': 0.16.17 220 | '@esbuild/darwin-arm64': 0.16.17 221 | '@esbuild/darwin-x64': 0.16.17 222 | '@esbuild/freebsd-arm64': 0.16.17 223 | '@esbuild/freebsd-x64': 0.16.17 224 | '@esbuild/linux-arm': 0.16.17 225 | '@esbuild/linux-arm64': 0.16.17 226 | '@esbuild/linux-ia32': 0.16.17 227 | '@esbuild/linux-loong64': 0.16.17 228 | '@esbuild/linux-mips64el': 0.16.17 229 | '@esbuild/linux-ppc64': 0.16.17 230 | '@esbuild/linux-riscv64': 0.16.17 231 | '@esbuild/linux-s390x': 0.16.17 232 | '@esbuild/linux-x64': 0.16.17 233 | '@esbuild/netbsd-x64': 0.16.17 234 | '@esbuild/openbsd-x64': 0.16.17 235 | '@esbuild/sunos-x64': 0.16.17 236 | '@esbuild/win32-arm64': 0.16.17 237 | '@esbuild/win32-ia32': 0.16.17 238 | '@esbuild/win32-x64': 0.16.17 239 | dev: true 240 | 241 | /fsevents/2.3.2: 242 | resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} 243 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 244 | os: [darwin] 245 | requiresBuild: true 246 | dev: true 247 | optional: true 248 | 249 | /function-bind/1.1.1: 250 | resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} 251 | dev: true 252 | 253 | /has/1.0.3: 254 | resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} 255 | engines: {node: '>= 0.4.0'} 256 | dependencies: 257 | function-bind: 1.1.1 258 | dev: true 259 | 260 | /is-core-module/2.11.0: 261 | resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} 262 | dependencies: 263 | has: 1.0.3 264 | dev: true 265 | 266 | /nanoid/3.3.4: 267 | resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} 268 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 269 | hasBin: true 270 | dev: true 271 | 272 | /path-parse/1.0.7: 273 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} 274 | dev: true 275 | 276 | /picocolors/1.0.0: 277 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 278 | dev: true 279 | 280 | /postcss/8.4.21: 281 | resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==} 282 | engines: {node: ^10 || ^12 || >=14} 283 | dependencies: 284 | nanoid: 3.3.4 285 | picocolors: 1.0.0 286 | source-map-js: 1.0.2 287 | dev: true 288 | 289 | /resolve/1.22.1: 290 | resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} 291 | hasBin: true 292 | dependencies: 293 | is-core-module: 2.11.0 294 | path-parse: 1.0.7 295 | supports-preserve-symlinks-flag: 1.0.0 296 | dev: true 297 | 298 | /rollup/3.10.0: 299 | resolution: {integrity: sha512-JmRYz44NjC1MjVF2VKxc0M1a97vn+cDxeqWmnwyAF4FvpjK8YFdHpaqvQB+3IxCvX05vJxKZkoMDU8TShhmJVA==} 300 | engines: {node: '>=14.18.0', npm: '>=8.0.0'} 301 | hasBin: true 302 | optionalDependencies: 303 | fsevents: 2.3.2 304 | dev: true 305 | 306 | /source-map-js/1.0.2: 307 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} 308 | engines: {node: '>=0.10.0'} 309 | dev: true 310 | 311 | /supports-preserve-symlinks-flag/1.0.0: 312 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 313 | engines: {node: '>= 0.4'} 314 | dev: true 315 | 316 | /typescript/4.9.4: 317 | resolution: {integrity: sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==} 318 | engines: {node: '>=4.2.0'} 319 | hasBin: true 320 | dev: true 321 | 322 | /vite/4.0.4: 323 | resolution: {integrity: sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==} 324 | engines: {node: ^14.18.0 || >=16.0.0} 325 | hasBin: true 326 | peerDependencies: 327 | '@types/node': '>= 14' 328 | less: '*' 329 | sass: '*' 330 | stylus: '*' 331 | sugarss: '*' 332 | terser: ^5.4.0 333 | peerDependenciesMeta: 334 | '@types/node': 335 | optional: true 336 | less: 337 | optional: true 338 | sass: 339 | optional: true 340 | stylus: 341 | optional: true 342 | sugarss: 343 | optional: true 344 | terser: 345 | optional: true 346 | dependencies: 347 | esbuild: 0.16.17 348 | postcss: 8.4.21 349 | resolve: 1.22.1 350 | rollup: 3.10.0 351 | optionalDependencies: 352 | fsevents: 2.3.2 353 | dev: true 354 | --------------------------------------------------------------------------------