Converted to typescript

This commit is contained in:
2026-03-11 15:58:07 +01:00 Verified
parent d82228d5e5
commit cff173f212
17 changed files with 883 additions and 354 deletions
+3 -9
View File
@@ -2,28 +2,22 @@ import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{js,jsx}'],
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
parserOptions: {
ecmaVersion: 'latest',
ecmaFeatures: { jsx: true },
sourceType: 'module',
},
},
rules: {
'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
},
},
])
+403 -37
View File
@@ -17,6 +17,7 @@
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/node": "^24.10.1",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1",
@@ -24,6 +25,8 @@
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.48.0",
"vite": "^7.3.1"
}
},
@@ -768,15 +771,15 @@
}
},
"node_modules/@eslint/config-array": {
"version": "0.21.1",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
"integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
"version": "0.21.2",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
"integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/object-schema": "^2.1.7",
"debug": "^4.3.1",
"minimatch": "^3.1.2"
"minimatch": "^3.1.5"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -809,9 +812,9 @@
}
},
"node_modules/@eslint/eslintrc": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.4.tgz",
"integrity": "sha512-4h4MVF8pmBsncB60r0wSJiIeUKTSD4m7FmTFThG8RHlsg9ajqckLm9OraguFGZE4vVdpiI1Q4+hFnisopmG6gQ==",
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz",
"integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -822,7 +825,7 @@
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
"js-yaml": "^4.1.1",
"minimatch": "^3.1.3",
"minimatch": "^3.1.5",
"strip-json-comments": "^3.1.1"
},
"engines": {
@@ -846,9 +849,9 @@
}
},
"node_modules/@eslint/js": {
"version": "9.39.3",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.3.tgz",
"integrity": "sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==",
"version": "9.39.4",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
"integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1626,6 +1629,16 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "24.12.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz",
"integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.16.0"
}
},
"node_modules/@types/react": {
"version": "19.2.14",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
@@ -1646,6 +1659,301 @@
"@types/react": "^19.2.0"
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.57.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.0.tgz",
"integrity": "sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
"@typescript-eslint/scope-manager": "8.57.0",
"@typescript-eslint/type-utils": "8.57.0",
"@typescript-eslint/utils": "8.57.0",
"@typescript-eslint/visitor-keys": "8.57.0",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.57.0",
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.57.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.0.tgz",
"integrity": "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.57.0",
"@typescript-eslint/types": "8.57.0",
"@typescript-eslint/typescript-estree": "8.57.0",
"@typescript-eslint/visitor-keys": "8.57.0",
"debug": "^4.4.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.57.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.0.tgz",
"integrity": "sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.57.0",
"@typescript-eslint/types": "^8.57.0",
"debug": "^4.4.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.57.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.0.tgz",
"integrity": "sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.57.0",
"@typescript-eslint/visitor-keys": "8.57.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.57.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.0.tgz",
"integrity": "sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.57.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.0.tgz",
"integrity": "sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.57.0",
"@typescript-eslint/typescript-estree": "8.57.0",
"@typescript-eslint/utils": "8.57.0",
"debug": "^4.4.3",
"ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.57.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.0.tgz",
"integrity": "sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.57.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.0.tgz",
"integrity": "sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.57.0",
"@typescript-eslint/tsconfig-utils": "8.57.0",
"@typescript-eslint/types": "8.57.0",
"@typescript-eslint/visitor-keys": "8.57.0",
"debug": "^4.4.3",
"minimatch": "^10.2.2",
"semver": "^7.7.3",
"tinyglobby": "^0.2.15",
"ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
"integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^4.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
"version": "10.2.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
"integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"brace-expansion": "^5.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.57.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.0.tgz",
"integrity": "sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@typescript-eslint/scope-manager": "8.57.0",
"@typescript-eslint/types": "8.57.0",
"@typescript-eslint/typescript-estree": "8.57.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.57.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.0.tgz",
"integrity": "sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.57.0",
"eslint-visitor-keys": "^5.0.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
"integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@vitejs/plugin-react": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.4.tgz",
@@ -1806,9 +2114,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001775",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001775.tgz",
"integrity": "sha512-s3Qv7Lht9zbVKE9XoTyRG6wVDCKdtOFIjBGg3+Yhn6JaytuNKPIjBMTMIY1AnOH3seL5mvF+x33oGAyK3hVt3A==",
"version": "1.0.30001777",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001777.tgz",
"integrity": "sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ==",
"dev": true,
"funding": [
{
@@ -1947,9 +2255,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.5.302",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz",
"integrity": "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==",
"version": "1.5.307",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.307.tgz",
"integrity": "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg==",
"dev": true,
"license": "ISC"
},
@@ -2031,25 +2339,25 @@
}
},
"node_modules/eslint": {
"version": "9.39.3",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.3.tgz",
"integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==",
"version": "9.39.4",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz",
"integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.21.1",
"@eslint/config-array": "^0.21.2",
"@eslint/config-helpers": "^0.4.2",
"@eslint/core": "^0.17.0",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "9.39.3",
"@eslint/eslintrc": "^3.3.5",
"@eslint/js": "9.39.4",
"@eslint/plugin-kit": "^0.4.1",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2",
"@types/estree": "^1.0.6",
"ajv": "^6.12.4",
"ajv": "^6.14.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.6",
"debug": "^4.3.2",
@@ -2068,7 +2376,7 @@
"is-glob": "^4.0.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"lodash.merge": "^4.6.2",
"minimatch": "^3.1.2",
"minimatch": "^3.1.5",
"natural-compare": "^1.4.0",
"optionator": "^0.9.3"
},
@@ -2297,9 +2605,9 @@
}
},
"node_modules/flatted": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz",
"integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==",
"dev": true,
"license": "ISC"
},
@@ -2890,9 +3198,9 @@
"license": "MIT"
},
"node_modules/node-releases": {
"version": "2.0.27",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
"integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
"version": "2.0.36",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz",
"integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==",
"dev": true,
"license": "MIT"
},
@@ -2998,9 +3306,9 @@
}
},
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"version": "8.5.8",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
"integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
"funding": [
{
"type": "opencollective",
@@ -3067,9 +3375,9 @@
}
},
"node_modules/react-icons": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz",
"integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==",
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.6.0.tgz",
"integrity": "sha512-RH93p5ki6LfOiIt0UtDyNg/cee+HLVR6cHHtW3wALfo+eOHTp8RnU2kRkI6E+H19zMIs03DyxUG/GfZMOGvmiA==",
"license": "MIT",
"peerDependencies": {
"react": "*"
@@ -3292,6 +3600,19 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/ts-api-utils": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz",
"integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18.12"
},
"peerDependencies": {
"typescript": ">=4.8.4"
}
},
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -3305,6 +3626,51 @@
"node": ">= 0.8.0"
}
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/typescript-eslint": {
"version": "8.57.0",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.0.tgz",
"integrity": "sha512-W8GcigEMEeB07xEZol8oJ26rigm3+bfPHxHvwbYUlu1fUDsGuQ7Hiskx5xGW/xM4USc9Ephe3jtv7ZYPQntHeA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/eslint-plugin": "8.57.0",
"@typescript-eslint/parser": "8.57.0",
"@typescript-eslint/typescript-estree": "8.57.0",
"@typescript-eslint/utils": "8.57.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/undici-types": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"devOptional": true,
"license": "MIT"
},
"node_modules/update-browserslist-db": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+4 -1
View File
@@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
@@ -19,6 +19,7 @@
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/node": "^24.10.1",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1",
@@ -26,6 +27,8 @@
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.48.0",
"vite": "^7.3.1"
}
}
+4 -3
View File
@@ -1,11 +1,12 @@
// src/App.jsx
// src/App.tsx
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { Route, BrowserRouter as Router, Routes } from 'react-router-dom';
import Effects from './pages/Effects';
import Home from './pages/Home';
import Rules from './pages/Rules';
import Effects from './pages/Effects';
import Scorepad from './pages/Scorepad';
function App() {
return (
<Router>
@@ -1,6 +1,14 @@
// src/components/EffectRow.jsx
// src/components/EffectComponents.tsx
export function SideEffect({ imageSrc, children, banding = true }) {
import React from 'react';
export interface SideEffectProps {
imageSrc: string;
children: React.ReactNode;
banding?: boolean;
}
export function SideEffect({ imageSrc, children, banding = true }: SideEffectProps) {
return (
<div className={`flex flex-col sm:flex-row text-left items-center px-[min(40px,5vw)] ${banding && "odd:bg-sev-light"}`}>
<div className="min-h-10 min-w-max max-w-62.5 w-[32vw] flex justify-center items-center shrink-0 py-2.5">
@@ -13,17 +21,21 @@ export function SideEffect({ imageSrc, children, banding = true }) {
);
}
export function TopEffect({ images, visuals, children, banding = true }) {
export interface TopEffectProps {
images?: string[];
visuals?: React.ReactNode;
children: React.ReactNode;
banding?: boolean;
}
export function TopEffect({ images, visuals, children, banding = true }: TopEffectProps) {
return (
<div className={`flex flex-col text-center p-2.5 px-[min(40px,5vw)] ${banding && "odd:bg-sev-light"}`}>
{/* Renders custom complex grids if provided */}
{visuals && (
<div className="py-2.5 w-full flex flex-col gap-4">
{visuals}
</div>
)}
{/* Renders the standard single row if a simple array is provided */}
{images && images.length > 0 && (
<div className="grid gap-[min(2vw,50px)] grid-flow-col justify-center items-center py-2.5 min-h-10">
{images.map((src, index) => (
@@ -31,7 +43,6 @@ export function TopEffect({ images, visuals, children, banding = true }) {
))}
</div>
)}
<div className="px-[min(40px,10vw)] my-5 mx-0 font-condensed text-sev-dark text-lg">
{children}
</div>
@@ -39,7 +50,13 @@ export function TopEffect({ images, visuals, children, banding = true }) {
);
}
export function InfoBox({ title, children, className = "self-center" }) {
export interface InfoBoxProps {
title?: string;
children: React.ReactNode;
className?: string;
}
export function InfoBox({ title, children, className = "self-center" }: InfoBoxProps) {
return (
<div className={`border-2 border-sev-brown p-[min(20px,4vw)] sm:p-7.5 my-5 mx-2.5 flex flex-col bg-white ${className}`}>
{title && <h3 className="text-center font-condensed font-semibold text-sev-dark text-[min(35px,7vw)] mt-0">{title}</h3>}
@@ -48,7 +65,12 @@ export function InfoBox({ title, children, className = "self-center" }) {
);
}
export function SpecialDescription({ title, children }) {
export interface SpecialDescriptionProps {
title?: string;
children: React.ReactNode;
}
export function SpecialDescription({ title, children }: SpecialDescriptionProps) {
return (
<div className="bg-linear-to-t from-white to-sev-light flex flex-col p-2.5 text-center">
{title && <h3 className="text-center font-condensed font-semibold text-sev-dark text-[min(35px,7vw)] mt-0 mb-4">{title}</h3>}
@@ -57,7 +79,12 @@ export function SpecialDescription({ title, children }) {
);
}
export function SectionHeader({ logoSrc, title }) {
export interface SectionHeaderProps {
logoSrc: string;
title: string;
}
export function SectionHeader({ logoSrc, title }: SectionHeaderProps) {
return (
<>
<h2 className="text-center mt-12 mb-0">
@@ -79,7 +106,12 @@ export function SectionDivider() {
)
}
export function SectionContainer({ id, children }) {
export interface SectionContainerProps {
id: string;
children: React.ReactNode;
}
export function SectionContainer({ id, children }: SectionContainerProps) {
return (
<section id={id} className="bg-white max-w-350 shadow-[0_0_10px_10px_rgba(49,49,49,0.49)] rounded-[min(50px,10vw)] mb-12 overflow-hidden shrink-0 w-full flex flex-col">
{children}
@@ -87,7 +119,12 @@ export function SectionContainer({ id, children }) {
);
}
export function Clarification({ type = "Clarification", children }) {
export interface AnnotationProps {
type?: string;
children: React.ReactNode;
}
export function Clarification({ type = "Clarification", children }: AnnotationProps) {
return (
<p className="text-[#005f7d] italic text-[17px] sm:ml-[12ch] sm:-indent-[12ch] mt-2">
<strong className="underline font-bold">{type}:</strong> {children}
@@ -95,7 +132,7 @@ export function Clarification({ type = "Clarification", children }) {
);
}
export function Note({ type = "Note", children }) {
export function Note({ type = "Note", children }: AnnotationProps) {
return (
<p className="text-[#005f7d] italic text-[17px] sm:ml-[12ch] sm:-indent-[12ch] mt-2">
<strong className="underline font-bold">{type}:</strong> {children}
@@ -1,8 +1,13 @@
// src/components/InsetButton.jsx
// src/components/InsetButton.tsx
import { Link } from 'react-router-dom';
export default function InsetButton({ to, title }) {
interface InsetButtonProps {
to: string;
title: string;
}
export default function InsetButton({ to, title }: InsetButtonProps) {
return (
<Link
to={to}
@@ -1,10 +1,14 @@
// src/components/Navbar.jsx
// src/components/Navbar.tsx
import { useState } from 'react';
import { Link } from 'react-router-dom';
export default function Navbar({ showSections = true }) {
const [isOpen, setIsOpen] = useState(false);
interface NavbarProps {
showSections?: boolean;
}
export default function Navbar({ showSections = true }: NavbarProps) {
const [isOpen, setIsOpen] = useState<boolean>(false);
return (
<div className="fixed top-5 left-5 z-50">
@@ -1,7 +1,12 @@
// src/components/RulesComponents.jsx
// src/components/RulesComponents.tsx
// Reusable Section Header (e.g., "OVERVIEW OF THE GAME")
export function RuleHeader({ title }) {
import React from 'react';
export interface RuleHeaderProps {
title: string;
}
export function RuleHeader({ title }: RuleHeaderProps) {
return (
<>
<div className="flex flex-col text-center mt-5 mb-2">
@@ -12,8 +17,12 @@ export function RuleHeader({ title }) {
);
}
// Standard text wrapper
export function DescBox({ children, className = "" }) {
export interface DescBoxProps {
children: React.ReactNode;
className?: string;
}
export function DescBox({ children, className = "" }: DescBoxProps) {
return (
<div className={`flex flex-col px-[min(40px,5vw)] py-2.5 text-left text-lg font-condensed text-sev-dark ${className}`}>
{children}
@@ -21,8 +30,12 @@ export function DescBox({ children, className = "" }) {
);
}
// Quote formatter
export function Quote({ text, author }) {
export interface QuoteProps {
text: string;
author: string;
}
export function Quote({ text, author }: QuoteProps) {
return (
<h4 className="text-[min(20px,3vw)] text-center font-condensed font-medium text-sev-brown mt-0 mb-8 italic px-2.5">
{text} ({author})
@@ -30,8 +43,11 @@ export function Quote({ text, author }) {
);
}
// Custom Numbered Setup List
export function FancierListItem({ children }) {
export interface FancierListItemProps {
children: React.ReactNode;
}
export function FancierListItem({ children }: FancierListItemProps) {
return (
<li className="relative ml-7.5 my-1.5 font-condensed text-lg before:content-[counter(mycounter)] before:counter-increment-[mycounter] before:absolute before:-left-8.75 before:w-7.5 before:h-7.5 before:leading-7.5 before:rounded-full before:text-white before:bg-sev-brown before:text-center before:italic before:text-[20px]">
{children}
@@ -39,8 +55,11 @@ export function FancierListItem({ children }) {
);
}
// End Game Step Icon
export function StepIcon({ num }) {
export interface StepIconProps {
num: string | number;
}
export function StepIcon({ num }: StepIconProps) {
return (
<span className="mr-1.5 inline-block w-7.5 h-7.5 leading-6.5 rounded-full text-white bg-sev-brown text-center italic text-[22px] align-middle font-medium">
{num}
@@ -48,8 +67,15 @@ export function StepIcon({ num }) {
);
}
// Military Conflict Example Column
export function MilitaryChild({ title, img, strength, children, isEven }) {
export interface MilitaryChildProps {
title: string;
img: string;
strength?: string;
children?: React.ReactNode;
isEven?: boolean;
}
export function MilitaryChild({ title, img, strength, children, isEven }: MilitaryChildProps) {
return (
<div className={`flex flex-col items-center py-2.5 px-7.5 rounded-t-[min(40px,5vw)] flex-[1_1_175px] max-w-75 min-w-43.75 ${isEven ? 'bg-linear-to-t from-white via-sev-light to-sev-light' : ''}`}>
<p className="italic py-1.5 text-center font-condensed text-lg">{title}</p>
@@ -60,8 +86,12 @@ export function MilitaryChild({ title, img, strength, children, isEven }) {
);
}
// Helper for the left-text, right-image split rows
export function FlexRow({ children, className = "" }) {
export interface FlexRowProps {
children: React.ReactNode;
className?: string;
}
export function FlexRow({ children, className = "" }: FlexRowProps) {
return (
<div className={`flex flex-col md:flex-row justify-between gap-5 my-5 ${className}`}>
{children}
@@ -69,11 +99,17 @@ export function FlexRow({ children, className = "" }) {
);
}
// Helper for the Armada Shipyard Naval Tracks
export function NavalTrack({ img, colorClass, title, children }) {
export interface NavalTrackProps {
img: string;
colorClass: string;
title: string;
children: React.ReactNode;
}
export function NavalTrack({ img, colorClass, title, children }: NavalTrackProps) {
return (
<div className="flex flex-row bg-gradient-to-r from-[#f0ebe5] via-[#f0ebe5] to-white p-2.5 pl-5 gap-2.5 my-5">
<img src={img} alt="" className="max-h-[60px] min-w-[38px] drop-shadow-sm object-contain" loading="lazy" />
<div className="flex flex-row bg-linear-to-r from-sev-light via-sev-light to-white p-2.5 pl-5 gap-2.5 my-5">
<img src={img} alt="" className="max-h-15 min-w-9.5 drop-shadow-sm object-contain" loading="lazy" />
<div>
<h6 className={`text-[min(25px,8vw)] font-condensed font-bold m-0 ${colorClass}`}>{title}</h6>
{children}
@@ -82,17 +118,25 @@ export function NavalTrack({ img, colorClass, title, children }) {
);
}
// Helper for filled definition/restriction boxes
export function FilledInfoBox({ children, className = "" }) {
export interface FilledInfoBoxProps {
children: React.ReactNode;
className?: string;
}
export function FilledInfoBox({ children, className = "" }: FilledInfoBoxProps) {
return (
<div className={`bg-[#f0ebe5] p-[15px] my-2.5 mx-1.5 flex flex-col self-center border-2 border-sev-brown shadow-sm w-full max-w-[800px] ${className}`}>
<div className={`bg-sev-light p-3.75 my-2.5 mx-1.5 flex flex-col self-center border-2 border-sev-brown shadow-sm w-full max-w-200 ${className}`}>
{children}
</div>
);
}
// Updated Science Table to support Armada's extended 9-column layout
export function ScienceTable({ limit = 6, showAsterisk = false }) {
export interface ScienceTableProps {
limit?: number;
showAsterisk?: boolean;
}
export function ScienceTable({ limit = 6, showAsterisk = false }: ScienceTableProps) {
const counts = Array.from({ length: limit }, (_, i) => i + 1);
return (
<div className="overflow-x-auto block border-none min-w-max my-2.5">
@@ -100,13 +144,13 @@ export function ScienceTable({ limit = 6, showAsterisk = false }) {
<tbody>
<tr className="border-2 border-sev-brown">
<th className="text-right p-2.5 text-sev-brown italic font-normal">Number of identical symbols</th>
{counts.map(c => <td key={`count-${c}`} className="text-center p-2.5 text-sev-brown w-[30px] font-bold even:bg-[#ebe2da]">{c}</td>)}
{showAsterisk && <td className="text-center p-2.5 text-sev-brown w-[30px] font-bold even:bg-[#ebe2da]">...</td>}
{counts.map(c => <td key={`count-${c}`} className="text-center p-2.5 text-sev-brown w-7.5 font-bold even:bg-[#ebe2da]">{c}</td>)}
{showAsterisk && <td className="text-center p-2.5 text-sev-brown w-7.5 font-bold even:bg-[#ebe2da]">...</td>}
</tr>
<tr className="border-2 border-sev-brown">
<th className="text-right p-2.5 text-sev-brown italic font-normal">Victory points</th>
{counts.map(c => <td key={`vp-${c}`} className="text-center p-2.5 text-sev-brown w-[30px] font-bold even:bg-[#ebe2da]">{c * c}</td>)}
{showAsterisk && <td className="text-center p-2.5 text-black w-[30px] font-bold even:bg-[#ebe2da]">*</td>}
{counts.map(c => <td key={`vp-${c}`} className="text-center p-2.5 text-sev-brown w-7.5 font-bold even:bg-[#ebe2da]">{c * c}</td>)}
{showAsterisk && <td className="text-center p-2.5 text-black w-7.5 font-bold even:bg-[#ebe2da]">*</td>}
</tr>
</tbody>
</table>
+2 -2
View File
@@ -1,9 +1,9 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'
import App from './App.tsx'
createRoot(document.getElementById('root')).render(
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
@@ -1,7 +1,7 @@
// src/pages/Effects.jsx
// src/pages/Effects.tsx
import { Clarification, InfoBox, Note, SectionContainer, SectionDivider, SectionHeader, SideEffect, SpecialDescription, TopEffect } from '../components/EffectComponents';
import Navbar from '../components/Navbar';
import { SideEffect, TopEffect, InfoBox, SpecialDescription, SectionHeader, SectionDivider, SectionContainer, Clarification, Note } from '../components/EffectComponents';
export default function Effects() {
return (
File diff suppressed because it is too large Load Diff
@@ -1,10 +1,16 @@
// src/pages/Scorepad.jsx
import { useState, useEffect } from 'react';
import React, { useState, useEffect } from 'react';
import { FaMinus, FaPlus } from "react-icons/fa";
import Navbar from '../components/Navbar';
const CATEGORIES = [
interface ScoreCategory {
id: string;
icon: string;
color: string;
}
const CATEGORIES: ScoreCategory[] = [
{ id: 'wonder', icon: '/assets/scorepad/wonder.svg', color: '' },
{ id: 'money', icon: '/assets/scorepad/money.svg', color: '' },
{ id: 'military', icon: '/assets/scorepad/military.svg', color: '' },
@@ -18,24 +24,26 @@ const CATEGORIES = [
{ id: 'armada-island', icon: '/assets/scorepad/armada-island.svg', color: '' },
];
const generateInitialScores = (count) => {
return Array(count).fill().map(() =>
CATEGORIES.reduce((acc, cat) => ({ ...acc, [cat.id]: '' }), {})
type PlayerScores = Record<string, string>;
const generateInitialScores = (count: number): PlayerScores[] => {
return Array(count).fill(null).map(() =>
CATEGORIES.reduce((acc, cat) => ({ ...acc, [cat.id]: '' }), {} as PlayerScores)
);
};
export default function Scorepad() {
const [playerCount, setPlayerCount] = useState(() => {
const [playerCount, setPlayerCount] = useState < number > (() => {
const saved = localStorage.getItem('sev_playerCount');
return saved ? JSON.parse(saved) : 7;
});
const [scores, setScores] = useState(() => {
const [scores, setScores] = useState < PlayerScores[] > (() => {
const saved = localStorage.getItem('sev_scores');
return saved ? JSON.parse(saved) : generateInitialScores(7);
});
const [playerNames, setPlayerNames] = useState(() => {
const [playerNames, setPlayerNames] = useState < string[] > (() => {
const saved = localStorage.getItem('sev_playerNames');
return saved ? JSON.parse(saved) : Array(8).fill('');
});
@@ -46,28 +54,28 @@ export default function Scorepad() {
localStorage.setItem('sev_playerNames', JSON.stringify(playerNames));
}, [playerCount, scores, playerNames]);
const handleScoreChange = (playerIndex, category, value) => {
const handleScoreChange = (playerIndex: number, category: string, value: string) => {
const newScores = [...scores];
newScores[playerIndex][category] = value;
setScores(newScores);
};
const handleNameChange = (playerIndex, value) => {
const handleNameChange = (playerIndex: number, value: string) => {
const newNames = [...playerNames];
newNames[playerIndex] = value;
setPlayerNames(newNames);
};
const calculatePlayerTotal = (playerIndex) => {
return Object.values(scores[playerIndex]).reduce((sum, val) => {
const calculatePlayerTotal = (playerIndex: number): number => {
return Object.values(scores[playerIndex] || {}).reduce((sum: number, val: string) => {
const num = parseInt(val, 10);
return sum + (isNaN(num) ? 0 : num);
}, 0);
};
const calculateTeamTotal = (p1, p2) => calculatePlayerTotal(p1) + calculatePlayerTotal(p2);
const calculateTeamTotal = (p1: number, p2: number): number => calculatePlayerTotal(p1) + calculatePlayerTotal(p2);
const handleReset = (e) => {
const handleReset = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
if (window.confirm('Do you really want to reset the scorepad?')) {
setScores(generateInitialScores(playerCount));
@@ -75,18 +83,18 @@ export default function Scorepad() {
}
};
const addPlayer = (e) => {
const addPlayer = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
if (playerCount < 8) {
setPlayerCount(prev => prev + 1);
setScores(prev => [
...prev,
CATEGORIES.reduce((acc, cat) => ({ ...acc, [cat.id]: '' }), {})
CATEGORIES.reduce((acc, cat) => ({ ...acc, [cat.id]: '' }), {} as PlayerScores)
]);
}
};
const removePlayer = (e) => {
const removePlayer = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
if (playerCount > 2) {
if (window.confirm('Remove the last player? Their score will be lost.')) {
@@ -161,7 +169,7 @@ export default function Scorepad() {
<td key={pIdx} className="border border-[#595959] h-7.5 sm:h-10 even:bg-[#efebe7]">
<input
type="number"
value={scores[pIdx][cat.id]}
value={scores[pIdx]?.[cat.id] || ''}
onChange={(e) => handleScoreChange(pIdx, cat.id, e.target.value)}
className="w-full h-full text-center text-xl font-bold bg-transparent outline-none no-spinners text-sev-dark"
placeholder="0"
@@ -173,7 +181,7 @@ export default function Scorepad() {
</tbody>
<tfoot className="border-t-[3px] border-[#595959]">
<tr>
<th rowSpan="2" className="border border-[#595959] border-t-[3px] bg-white">
<th rowSpan={2} className="border border-[#595959] border-t-[3px] bg-white">
<img src="/assets/scorepad/summary.svg" alt="" className="h-6.25 mx-auto" />
</th>
{Array.from({ length: playerCount }).map((_, i) => (
@@ -184,7 +192,7 @@ export default function Scorepad() {
</tr>
<tr>
{Array.from({ length: Math.floor(playerCount / 2) }).map((_, i) => (
<td key={`team-${i}`} colSpan="2" className="border border-[#595959] bg-white font-bold text-lg text-sev-dark text-center">
<td key={`team-${i}`} colSpan={2} className="border border-[#595959] bg-white font-bold text-lg text-sev-dark text-center">
{calculateTeamTotal(i * 2, i * 2 + 1)}
</td>
))}
+28
View File
@@ -0,0 +1,28 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2022",
"useDefineForClassFields": true,
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"types": ["vite/client"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}
+7
View File
@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}
+26
View File
@@ -0,0 +1,26 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2023",
"lib": ["ES2023"],
"module": "ESNext",
"types": ["node"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}
+2 -4
View File
@@ -1,9 +1,7 @@
// vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
// https://vite.dev/config/
export default defineConfig({
plugins: [react(), tailwindcss()],
plugins: [react()],
})