Compare commits

14 Commits

Author SHA1 Message Date
50c9726444 fix 2025-06-15 13:27:42 +02:00
Daniel Ledda
a1d59d21a7 update 2024-12-01 20:04:41 +01:00
Daniel Ledda
efb6523622 update 2024-12-01 19:11:11 +01:00
Daniel Ledda
3bf20cae48 update 2024-12-01 17:43:48 +01:00
Daniel Ledda
a3b428a226 update 2024-12-01 17:42:58 +01:00
91958489c0 save light mode 2024-12-01 14:59:31 +01:00
e64f45bd21 save light mode 2024-12-01 14:54:36 +01:00
139bbbcf32 Merge pull request 'light mode' (#4) from light_mode into master
Reviewed-on: http://git.djledda.de/djledda/arne-drums/pulls/4
2024-12-01 14:24:54 +01:00
Daniel Ledda
78d724a2bf update red 2024-12-01 14:18:45 +01:00
Daniel Ledda
63695399bf update 2024-12-01 14:17:37 +01:00
Daniel Ledda
b1d72015de update 2024-12-01 14:16:30 +01:00
Daniel Ledda
ddf277b1f5 update 2024-12-01 14:15:05 +01:00
Daniel Ledda
911c751aa0 update 2024-12-01 14:06:20 +01:00
Daniel Ledda
3aa1932634 update 2024-12-01 14:02:10 +01:00
20 changed files with 329 additions and 352 deletions

View File

@@ -1,7 +0,0 @@
export default {
extends: [
// add more generic rulesets here, such as:
'eslint:recommended',
'plugin:vue/vue3-recommended',
],
};

246
package-lock.json generated
View File

@@ -12,7 +12,7 @@
"@vitejs/plugin-vue": "^5.0.4", "@vitejs/plugin-vue": "^5.0.4",
"@vue/tsconfig": "^0.1.3", "@vue/tsconfig": "^0.1.3",
"sass": "^1.58.3", "sass": "^1.58.3",
"vue": "^3.4.21", "vue": "^3.5.16",
"vue-draggable-next": "^2.2.1", "vue-draggable-next": "^2.2.1",
"vuedraggable": "^4.1.0", "vuedraggable": "^4.1.0",
"zod": "^3.21.4" "zod": "^3.21.4"
@@ -291,17 +291,19 @@
} }
}, },
"node_modules/@babel/helper-string-parser": { "node_modules/@babel/helper-string-parser": {
"version": "7.25.9", "version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
"license": "MIT",
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/helper-validator-identifier": { "node_modules/@babel/helper-validator-identifier": {
"version": "7.25.9", "version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
"integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
"license": "MIT",
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
@@ -329,11 +331,12 @@
} }
}, },
"node_modules/@babel/parser": { "node_modules/@babel/parser": {
"version": "7.26.2", "version": "7.27.5",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz",
"integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==",
"license": "MIT",
"dependencies": { "dependencies": {
"@babel/types": "^7.26.0" "@babel/types": "^7.27.3"
}, },
"bin": { "bin": {
"parser": "bin/babel-parser.js" "parser": "bin/babel-parser.js"
@@ -492,12 +495,13 @@
} }
}, },
"node_modules/@babel/types": { "node_modules/@babel/types": {
"version": "7.26.0", "version": "7.27.6",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz",
"integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==",
"license": "MIT",
"dependencies": { "dependencies": {
"@babel/helper-string-parser": "^7.25.9", "@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.25.9" "@babel/helper-validator-identifier": "^7.27.1"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@@ -1560,106 +1564,54 @@
"@babel/core": "^7.0.0-0" "@babel/core": "^7.0.0-0"
} }
}, },
"node_modules/@vue/babel-plugin-resolve-type/node_modules/@vue/compiler-core": {
"version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz",
"integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.25.3",
"@vue/shared": "3.5.13",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.2.0"
}
},
"node_modules/@vue/babel-plugin-resolve-type/node_modules/@vue/compiler-dom": {
"version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz",
"integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==",
"dev": true,
"dependencies": {
"@vue/compiler-core": "3.5.13",
"@vue/shared": "3.5.13"
}
},
"node_modules/@vue/babel-plugin-resolve-type/node_modules/@vue/compiler-sfc": {
"version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz",
"integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.25.3",
"@vue/compiler-core": "3.5.13",
"@vue/compiler-dom": "3.5.13",
"@vue/compiler-ssr": "3.5.13",
"@vue/shared": "3.5.13",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.11",
"postcss": "^8.4.48",
"source-map-js": "^1.2.0"
}
},
"node_modules/@vue/babel-plugin-resolve-type/node_modules/@vue/compiler-ssr": {
"version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz",
"integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==",
"dev": true,
"dependencies": {
"@vue/compiler-dom": "3.5.13",
"@vue/shared": "3.5.13"
}
},
"node_modules/@vue/babel-plugin-resolve-type/node_modules/@vue/shared": {
"version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz",
"integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==",
"dev": true
},
"node_modules/@vue/compiler-core": { "node_modules/@vue/compiler-core": {
"version": "3.4.27", "version": "3.5.16",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.27.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.16.tgz",
"integrity": "sha512-E+RyqY24KnyDXsCuQrI+mlcdW3ALND6U7Gqa/+bVwbcpcR3BRRIckFoz7Qyd4TTlnugtwuI7YgjbvsLmxb+yvg==", "integrity": "sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"@babel/parser": "^7.24.4", "@babel/parser": "^7.27.2",
"@vue/shared": "3.4.27", "@vue/shared": "3.5.16",
"entities": "^4.5.0", "entities": "^4.5.0",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"source-map-js": "^1.2.0" "source-map-js": "^1.2.1"
} }
}, },
"node_modules/@vue/compiler-dom": { "node_modules/@vue/compiler-dom": {
"version": "3.4.27", "version": "3.5.16",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.27.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.16.tgz",
"integrity": "sha512-kUTvochG/oVgE1w5ViSr3KUBh9X7CWirebA3bezTbB5ZKBQZwR2Mwj9uoSKRMFcz4gSMzzLXBPD6KpCLb9nvWw==", "integrity": "sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"@vue/compiler-core": "3.4.27", "@vue/compiler-core": "3.5.16",
"@vue/shared": "3.4.27" "@vue/shared": "3.5.16"
} }
}, },
"node_modules/@vue/compiler-sfc": { "node_modules/@vue/compiler-sfc": {
"version": "3.4.27", "version": "3.5.16",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.27.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.16.tgz",
"integrity": "sha512-nDwntUEADssW8e0rrmE0+OrONwmRlegDA1pD6QhVeXxjIytV03yDqTey9SBDiALsvAd5U4ZrEKbMyVXhX6mCGA==", "integrity": "sha512-rQR6VSFNpiinDy/DVUE0vHoIDUF++6p910cgcZoaAUm3POxgNOOdS/xgoll3rNdKYTYPnnbARDCZOyZ+QSe6Pw==",
"license": "MIT",
"dependencies": { "dependencies": {
"@babel/parser": "^7.24.4", "@babel/parser": "^7.27.2",
"@vue/compiler-core": "3.4.27", "@vue/compiler-core": "3.5.16",
"@vue/compiler-dom": "3.4.27", "@vue/compiler-dom": "3.5.16",
"@vue/compiler-ssr": "3.4.27", "@vue/compiler-ssr": "3.5.16",
"@vue/shared": "3.4.27", "@vue/shared": "3.5.16",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"magic-string": "^0.30.10", "magic-string": "^0.30.17",
"postcss": "^8.4.38", "postcss": "^8.5.3",
"source-map-js": "^1.2.0" "source-map-js": "^1.2.1"
} }
}, },
"node_modules/@vue/compiler-ssr": { "node_modules/@vue/compiler-ssr": {
"version": "3.4.27", "version": "3.5.16",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.27.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.16.tgz",
"integrity": "sha512-CVRzSJIltzMG5FcidsW0jKNQnNRYC8bT21VegyMMtHmhW3UOI7knmUehzswXLrExDLE6lQCZdrhD4ogI7c+vuw==", "integrity": "sha512-d2V7kfxbdsjrDSGlJE7my1ZzCXViEcqN6w14DOsDrUCHEA6vbnVCpRFfrc4ryCP/lCKzX2eS1YtnLE/BuC9f/A==",
"license": "MIT",
"dependencies": { "dependencies": {
"@vue/compiler-dom": "3.4.27", "@vue/compiler-dom": "3.5.16",
"@vue/shared": "3.4.27" "@vue/shared": "3.5.16"
} }
}, },
"node_modules/@vue/devtools-core": { "node_modules/@vue/devtools-core": {
@@ -1722,48 +1674,54 @@
} }
}, },
"node_modules/@vue/reactivity": { "node_modules/@vue/reactivity": {
"version": "3.4.27", "version": "3.5.16",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.27.tgz", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.16.tgz",
"integrity": "sha512-kK0g4NknW6JX2yySLpsm2jlunZJl2/RJGZ0H9ddHdfBVHcNzxmQ0sS0b09ipmBoQpY8JM2KmUw+a6sO8Zo+zIA==", "integrity": "sha512-FG5Q5ee/kxhIm1p2bykPpPwqiUBV3kFySsHEQha5BJvjXdZTUfmya7wP7zC39dFuZAcf/PD5S4Lni55vGLMhvA==",
"license": "MIT",
"dependencies": { "dependencies": {
"@vue/shared": "3.4.27" "@vue/shared": "3.5.16"
} }
}, },
"node_modules/@vue/runtime-core": { "node_modules/@vue/runtime-core": {
"version": "3.4.27", "version": "3.5.16",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.27.tgz", "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.16.tgz",
"integrity": "sha512-7aYA9GEbOOdviqVvcuweTLe5Za4qBZkUY7SvET6vE8kyypxVgaT1ixHLg4urtOlrApdgcdgHoTZCUuTGap/5WA==", "integrity": "sha512-bw5Ykq6+JFHYxrQa7Tjr+VSzw7Dj4ldR/udyBZbq73fCdJmyy5MPIFR9IX/M5Qs+TtTjuyUTCnmK3lWWwpAcFQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"@vue/reactivity": "3.4.27", "@vue/reactivity": "3.5.16",
"@vue/shared": "3.4.27" "@vue/shared": "3.5.16"
} }
}, },
"node_modules/@vue/runtime-dom": { "node_modules/@vue/runtime-dom": {
"version": "3.4.27", "version": "3.5.16",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.27.tgz", "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.16.tgz",
"integrity": "sha512-ScOmP70/3NPM+TW9hvVAz6VWWtZJqkbdf7w6ySsws+EsqtHvkhxaWLecrTorFxsawelM5Ys9FnDEMt6BPBDS0Q==", "integrity": "sha512-T1qqYJsG2xMGhImRUV9y/RseB9d0eCYZQ4CWca9ztCuiPj/XWNNN+lkNBuzVbia5z4/cgxdL28NoQCvC0Xcfww==",
"license": "MIT",
"dependencies": { "dependencies": {
"@vue/runtime-core": "3.4.27", "@vue/reactivity": "3.5.16",
"@vue/shared": "3.4.27", "@vue/runtime-core": "3.5.16",
"@vue/shared": "3.5.16",
"csstype": "^3.1.3" "csstype": "^3.1.3"
} }
}, },
"node_modules/@vue/server-renderer": { "node_modules/@vue/server-renderer": {
"version": "3.4.27", "version": "3.5.16",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.27.tgz", "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.16.tgz",
"integrity": "sha512-dlAMEuvmeA3rJsOMJ2J1kXU7o7pOxgsNHVr9K8hB3ImIkSuBrIdy0vF66h8gf8Tuinf1TK3mPAz2+2sqyf3KzA==", "integrity": "sha512-BrX0qLiv/WugguGsnQUJiYOE0Fe5mZTwi6b7X/ybGB0vfrPH9z0gD/Y6WOR1sGCgX4gc25L1RYS5eYQKDMoNIg==",
"license": "MIT",
"dependencies": { "dependencies": {
"@vue/compiler-ssr": "3.4.27", "@vue/compiler-ssr": "3.5.16",
"@vue/shared": "3.4.27" "@vue/shared": "3.5.16"
}, },
"peerDependencies": { "peerDependencies": {
"vue": "3.4.27" "vue": "3.5.16"
} }
}, },
"node_modules/@vue/shared": { "node_modules/@vue/shared": {
"version": "3.4.27", "version": "3.5.16",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.16.tgz",
"integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==" "integrity": "sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg==",
"license": "MIT"
}, },
"node_modules/@vue/tsconfig": { "node_modules/@vue/tsconfig": {
"version": "0.1.3", "version": "0.1.3",
@@ -2151,7 +2109,8 @@
"node_modules/csstype": { "node_modules/csstype": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"license": "MIT"
}, },
"node_modules/debug": { "node_modules/debug": {
"version": "4.3.7", "version": "4.3.7",
@@ -2250,6 +2209,7 @@
"version": "4.5.0", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"license": "BSD-2-Clause",
"engines": { "engines": {
"node": ">=0.12" "node": ">=0.12"
}, },
@@ -3204,9 +3164,10 @@
} }
}, },
"node_modules/magic-string": { "node_modules/magic-string": {
"version": "0.30.14", "version": "0.30.17",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.14.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
"integrity": "sha512-5c99P1WKTed11ZC0HMJOj6CDIue6F8ySu+bJL+85q1zBEIY8IklrJ1eiKC2NDRh3Ct3FcvmJPyQHb9erXMTJNw==", "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
"license": "MIT",
"dependencies": { "dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0" "@jridgewell/sourcemap-codec": "^1.5.0"
} }
@@ -3267,15 +3228,16 @@
"dev": true "dev": true
}, },
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "3.3.7", "version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ai" "url": "https://github.com/sponsors/ai"
} }
], ],
"license": "MIT",
"bin": { "bin": {
"nanoid": "bin/nanoid.cjs" "nanoid": "bin/nanoid.cjs"
}, },
@@ -3512,9 +3474,9 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.4.49", "version": "8.5.5",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.5.tgz",
"integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "integrity": "sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@@ -3529,8 +3491,9 @@
"url": "https://github.com/sponsors/ai" "url": "https://github.com/sponsors/ai"
} }
], ],
"license": "MIT",
"dependencies": { "dependencies": {
"nanoid": "^3.3.7", "nanoid": "^3.3.11",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
"source-map-js": "^1.2.1" "source-map-js": "^1.2.1"
}, },
@@ -4232,15 +4195,16 @@
} }
}, },
"node_modules/vue": { "node_modules/vue": {
"version": "3.4.27", "version": "3.5.16",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.27.tgz", "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.16.tgz",
"integrity": "sha512-8s/56uK6r01r1icG/aEOHqyMVxd1bkYcSe9j8HcKtr/xTOFWvnzIVTehNW+5Yt89f+DLBe4A569pnZLS5HzAMA==", "integrity": "sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w==",
"license": "MIT",
"dependencies": { "dependencies": {
"@vue/compiler-dom": "3.4.27", "@vue/compiler-dom": "3.5.16",
"@vue/compiler-sfc": "3.4.27", "@vue/compiler-sfc": "3.5.16",
"@vue/runtime-dom": "3.4.27", "@vue/runtime-dom": "3.5.16",
"@vue/server-renderer": "3.4.27", "@vue/server-renderer": "3.5.16",
"@vue/shared": "3.4.27" "@vue/shared": "3.5.16"
}, },
"peerDependencies": { "peerDependencies": {
"typescript": "*" "typescript": "*"

View File

@@ -19,7 +19,7 @@
"@vitejs/plugin-vue": "^5.0.4", "@vitejs/plugin-vue": "^5.0.4",
"@vue/tsconfig": "^0.1.3", "@vue/tsconfig": "^0.1.3",
"sass": "^1.58.3", "sass": "^1.58.3",
"vue": "^3.4.21", "vue": "^3.5.16",
"vue-draggable-next": "^2.2.1", "vue-draggable-next": "^2.2.1",
"vuedraggable": "^4.1.0", "vuedraggable": "^4.1.0",
"zod": "^3.21.4" "zod": "^3.21.4"

View File

@@ -1,4 +1,4 @@
import { inject, type InjectionKey, ref, getCurrentInstance } from "vue"; import { inject, type InjectionKey, ref, getCurrentInstance, watch, computed } from "vue";
import { Bound } from "./utils"; import { Bound } from "./utils";
export type UITool = export type UITool =
@@ -6,12 +6,51 @@ export type UITool =
| "sticking"; | "sticking";
class AppStateStore extends Bound { class AppStateStore extends Bound {
private osThemeIsDark = ref(false);
private isDarkUser = ref<boolean | null>(null);
selectedTool = ref<UITool>("track-unit-type"); selectedTool = ref<UITool>("track-unit-type");
activeTrackUnitType = ref<number | null>(0); activeTrackUnitType = ref<number | null>(0);
activeStickingType = ref<number | null>(1); activeStickingType = ref<number | null>(1);
unitMouseStart = ref<string | null>(null); unitMouseStart = ref<string | null>(null);
selectingUnits = ref(false); selectingUnits = ref(false);
deselectingUnits = ref(false); deselectingUnits = ref(false);
isDark = computed<boolean>({
get: () => this.isDarkUser.value === null ? this.osThemeIsDark.value : this.isDarkUser.value,
set: (value) => {
this.isDarkUser.value = value;
},
});
constructor() {
super();
const isDarkUserInitial = localStorage?.getItem("drum-slayer-theme");
if (isDarkUserInitial === "dark") {
this.isDarkUser.value = true;
} else if (isDarkUserInitial === "light") {
this.isDarkUser.value = false;
}
const mediaQueryList = window.matchMedia("(prefers-color-scheme: dark)");
const onMediaChange = (event: MediaQueryListEvent | MediaQueryList) => {
this.osThemeIsDark.value = event.matches;
};
mediaQueryList.onchange = onMediaChange;
onMediaChange(mediaQueryList);
watch(this.isDarkUser, (newIsDarkUser) => {
if (typeof newIsDarkUser === "boolean") {
localStorage?.setItem("drum-slayer-theme", newIsDarkUser ? "dark" : "light");
}
}, { immediate: true });
watch(this.isDark, (newIsDark) => {
if (newIsDark) {
document.body.classList.add("dark");
} else {
document.body.classList.remove("dark");
}
}, { immediate: true });
}
} }
const AppStateStoreKey = Symbol("AppStateStore") as InjectionKey<AppStateStore>; const AppStateStoreKey = Symbol("AppStateStore") as InjectionKey<AppStateStore>;

View File

@@ -31,11 +31,13 @@ export class BeatStore extends Bound {
activeBeatIndex = ref(0); activeBeatIndex = ref(0);
activeBeat = computed<Beat | null>(() => this.beats.value[this.activeBeatIndex.value] ?? null); activeBeat = computed<Beat | null>(() => this.beats.value[this.activeBeatIndex.value] ?? null);
orientation = ref<"horizontal" | "vertical">("horizontal"); orientation = ref<"horizontal" | "vertical">("horizontal");
trackSettingsOpen = ref<number | null>(null);
constructor() { constructor() {
super(); super();
watch([this.activeBeatIndex, this.orientation, this.beats], () => { watch([this.activeBeatIndex, this.orientation, this.beats], () => {
this.saveDirtyGlobal.value = true; this.saveDirtyGlobal.value = true;
this.trackSettingsOpen.value = null;
}); });
const saveInterval = setInterval(() => this.saveDirtyGlobal.value && this.save("localStorage"), 5 * 60 * 1000); const saveInterval = setInterval(() => this.saveDirtyGlobal.value && this.save("localStorage"), 5 * 60 * 1000);

View File

@@ -2,7 +2,7 @@ import { isPosInt, EffectScoped } from "@/utils";
import { ref, shallowRef, computed, watch, reactive, triggerRef, type Ref, type ShallowRef } from "vue"; import { ref, shallowRef, computed, watch, reactive, triggerRef, type Ref, type ShallowRef } from "vue";
import type { BeatManager } from "./Beat"; import type { BeatManager } from "./Beat";
import { z } from "zod"; import { z } from "zod";
import { useAppStateStore, type UITool } from "./AppState"; import { useAppStateStore } from "./AppState";
export type TrackInitOptions = { export type TrackInitOptions = {
visible?: boolean, visible?: boolean,
@@ -34,7 +34,7 @@ const TrackSerialSchema = z.object({
}); });
export type TrackSerial = z.infer<typeof TrackSerialSchema>; export type TrackSerial = z.infer<typeof TrackSerialSchema>;
export const TrackUnitTypeList = [ "Normal", "GhostNote", "Accent", "GhostNoteAccent" ] as const; export const TrackUnitTypeList = [ "Normal", "GhostNote", "Accent" ] as const;
export type TrackUnitType = typeof TrackUnitTypeList[number]; export type TrackUnitType = typeof TrackUnitTypeList[number];
export const TrackUnitStickingTypeList = [ "none", "lh", "rh", "lf", "rf" ] as const; export const TrackUnitStickingTypeList = [ "none", "lh", "rh", "lf", "rf" ] as const;

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-down"><polyline points="6 9 12 15 18 9"></polyline></svg>

After

Width:  |  Height:  |  Size: 269 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-left"><polyline points="15 18 9 12 15 6"></polyline></svg>

After

Width:  |  Height:  |  Size: 270 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-right"><polyline points="9 18 15 12 9 6"></polyline></svg>

After

Width:  |  Height:  |  Size: 270 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-up"><polyline points="18 15 12 9 6 15"></polyline></svg>

After

Width:  |  Height:  |  Size: 268 B

1
src/assets/svgs/moon.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-moon"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>

After

Width:  |  Height:  |  Size: 281 B

1
src/assets/svgs/sun.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-sun"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>

After

Width:  |  Height:  |  Size: 650 B

View File

@@ -1,5 +1,5 @@
<template> <template>
<div v-if="beat" class="beat" :class="{ vertical: false }"> <div v-if="beat" class="beat">
<editable-text-field node-type="h3" class="beat-title" v-model="beat.name.value" /> <editable-text-field node-type="h3" class="beat-title" v-model="beat.name.value" />
<div class="beat-main-container"> <div class="beat-main-container">
<div class="beat-track-container" :class="{ dragging }"> <div class="beat-track-container" :class="{ dragging }">
@@ -12,39 +12,51 @@
:sticking-type="type + 1" /> :sticking-type="type + 1" />
</div> </div>
</template> </template>
<VueDraggableNext v-else <div v-else class="beat-wrap-row">
@start="dragging = true" <VueDraggableNext
@end="dragging = false" @start="dragging = true"
animation="150" @end="dragging = false"
ghost-class="ghost" animation="150"
handle=".handle" ghost-class="ghost"
:list="tracks" handle=".handle"
@change="onMove" :list="tracks"
item-key="index"> @change="onMove"
<transition-group name="fade"> item-key="index">
<template v-for="element in tracks"> <transition-group name="fade">
<div v-if="element.track.visible.value" class="beat-line" :key="element.index"> <template v-for="element in tracks">
<div class="track-actions"> <div v-if="element.track.visible.value" :key="element.index">
<span class="track-action"> <div class="beat-line">
<icon color="var(--color-ui-neutral-dark)" <div class="track-actions">
@click="element.track.visible.value = !element.track.visible.value" <span @click="toggleTrackSettings(element.index)" class="track-action">
:icon-name="element.track.visible.value ? 'eye' : 'eyeOff'" /> <icon color="var(--color-ui-neutral-dark)" :icon-name="trackSettingsOpen === element.index ? 'up' : 'down'" />
</span> </span>
<span class="track-action" <span class="handle track-action">
@click="applyToolToTrack(element.track)"> <icon color="var(--color-ui-neutral-dark)" icon-name="list" />
<icon color="var(--color-ui-neutral-dark)" icon-name="paintBucket" /> </span>
</span> </div>
<span class="handle track-action"> <editable-text-field class="track-name" v-model="element.track.name.value" />
<icon color="var(--color-ui-neutral-dark)" icon-name="list" /> <track-view :beat-index="beatIndex"
</span> :track-index="element.index" />
</div>
<div class="track-settings-row" v-show="trackSettingsOpen === element.index">
<track-settings-view :beat-index="beatIndex" :track-index="element.index" />
</div>
</div> </div>
<editable-text-field class="track-name" v-model="element.track.name.value" /> </template>
<track-view :beat-index="beatIndex" </transition-group>
:track-index="element.index" /> </VueDraggableNext>
</div> </div>
</template> <!--
</transition-group> <div v-for="i in beat.barCount.value - 1" class="beat-wrap-row">
</VueDraggableNext> <template v-for="(element, j) in tracks" :key="j">
<div v-if="element.track.visible.value" class="beat-line" :key="element.index">
<track-view :beat-index="beatIndex"
:bars="{ start: i, count: 1 }"
:track-index="element.index" />
</div>
</template>
</div>
-->
</div> </div>
</div> </div>
</div> </div>
@@ -60,13 +72,14 @@
import { TrackUnitStickingTypeList, type Track } from "@/Track"; import { TrackUnitStickingTypeList, type Track } from "@/Track";
import Icon from "@/ui/Widgets/Icon/Icon.vue"; import Icon from "@/ui/Widgets/Icon/Icon.vue";
import { StickingTypeIconMap } from "../TrackUnit/trackUnit"; import { StickingTypeIconMap } from "../TrackUnit/trackUnit";
import TrackSettingsView from "@/ui/TrackSettings/TrackSettings.vue";
const props = defineProps<{ const props = defineProps<{
beatIndex: number, beatIndex: number,
orientation?: "horizontal" | "vertical", orientation?: "horizontal" | "vertical",
}>(); }>();
const { beats } = useBeatStore(); const { beats, trackSettingsOpen } = useBeatStore();
const beat = computed(() => beats.value[props.beatIndex] ?? null); const beat = computed(() => beats.value[props.beatIndex] ?? null);
const tracks = computed(() => { const tracks = computed(() => {
@@ -87,6 +100,14 @@
const dragging = ref(false); const dragging = ref(false);
function toggleTrackSettings(trackIndex: number) {
if (trackSettingsOpen.value === trackIndex) {
trackSettingsOpen.value = null;
} else {
trackSettingsOpen.value = trackIndex;
}
}
function onMove(evt: { moved: { oldIndex: number, newIndex: number }}) { function onMove(evt: { moved: { oldIndex: number, newIndex: number }}) {
if (props.orientation !== 'horizontal') { if (props.orientation !== 'horizontal') {
beat.value?.insertAt(tracks.value.length - 1 - evt.moved.oldIndex, tracks.value.length - 1 - evt.moved.newIndex); beat.value?.insertAt(tracks.value.length - 1 - evt.moved.oldIndex, tracks.value.length - 1 - evt.moved.newIndex);
@@ -110,6 +131,11 @@
width: 100%; width: 100%;
} }
.beat-wrap-row {
display: inline-block;
margin-bottom: 30px;
}
.beat-title { .beat-title {
color: var(--color-title-light); color: var(--color-title-light);
text-align: center; text-align: center;
@@ -124,7 +150,7 @@
.beat-main-container { .beat-main-container {
display: flex; display: flex;
white-space: nowrap; //white-space: nowrap;
} }
.track-name { .track-name {
@@ -185,6 +211,12 @@
opacity: 0; opacity: 0;
} }
.track-settings-row {
background-color: var(--color-bg-medium);
width: 100%;
margin-bottom: 0.5em;
}
.vertical-mode { .vertical-mode {
.beat-title { .beat-title {
color: var(--color-title-light); color: var(--color-title-light);

View File

@@ -45,18 +45,6 @@
@click="sidebarActive = !sidebarActive"> @click="sidebarActive = !sidebarActive">
<icon icon-name="list" color="var(--color-ui-neutral-dark)" /> <icon icon-name="list" color="var(--color-ui-neutral-dark)" />
</div> </div>
<div
class="quick-access-button"
title="Change orientation"
@click="toggleOrientation">
<icon icon-name="arrowClockwise" color="var(--color-ui-neutral-dark)" />
</div>
<div
class="quick-access-button"
title="Bake all tracks"
@click="bakeAll">
<icon icon-name="snowflake" color="var(--color-ui-neutral-dark)" />
</div>
<div <div
class="quick-access-button" class="quick-access-button"
title="Reset all" title="Reset all"
@@ -86,7 +74,7 @@
class="quick-access-button" class="quick-access-button"
title="Switch theme" title="Switch theme"
@click="toggleTheme"> @click="toggleTheme">
<icon icon-name="list" color="var(--color-ui-neutral-dark)" /> <icon :icon-name="appStateStore.isDark.value ? 'moon' : 'sun'" color="var(--color-ui-neutral-dark)" />
</div> </div>
</div> </div>
<toolbox class="toolbox" /> <toolbox class="toolbox" />
@@ -128,11 +116,12 @@
const currentOrientation = ref<'horizontal' | 'vertical'>('horizontal'); const currentOrientation = ref<'horizontal' | 'vertical'>('horizontal');
const sidebarActive = ref(false); const sidebarActive = ref(false);
const appStateStore = useAppStateStore();
function toggleTheme() { function toggleTheme() {
document.body.classList.toggle("dark"); appStateStore.isDark.value = !appStateStore.isDark.value;
} }
const appStateStore = useAppStateStore();
const beatStore = useBeatStore(); const beatStore = useBeatStore();
window.drumslayer = { window.drumslayer = {
@@ -224,13 +213,6 @@
} }
}); });
const mediaQueryList = window.matchMedia("screen and (max-width: 900px)");
function onMediaChange(event: MediaQueryListEvent | MediaQueryList) {
sidebarActive.value = event.matches;
}
mediaQueryList.addEventListener('change', onMediaChange);
onMediaChange(mediaQueryList);
function windowMouseUp() { function windowMouseUp() {
appStateStore.selectingUnits.value = false; appStateStore.selectingUnits.value = false;
appStateStore.deselectingUnits.value = false; appStateStore.deselectingUnits.value = false;
@@ -244,14 +226,6 @@
onBeforeUnmount(() => { onBeforeUnmount(() => {
window.removeEventListener('mouseup', windowMouseUp); window.removeEventListener('mouseup', windowMouseUp);
}); });
function toggleOrientation(): void {
if (currentOrientation.value === "vertical") {
currentOrientation.value = "horizontal";
} else {
currentOrientation.value = "vertical";
}
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -38,6 +38,7 @@
beatIndex: number, beatIndex: number,
locked?: boolean, locked?: boolean,
trackIndex: number, trackIndex: number,
bars?: { start: number, count: number },
}>(); }>();
const { const {
@@ -52,8 +53,16 @@
const trackUnits = computed(() => { const trackUnits = computed(() => {
const units = []; const units = [];
if (track.value) { let start = 0;
for (let i = 0; i < track.value.unitCount(); i++) { let end = 0;
if (track.value && beat.value) {
if (props.bars) {
start = beat.value.timeSigUp.value * props.bars.start;
end = start + beat.value.timeSigUp.value * props.bars.count;
} else {
end = track.value.unitCount();
}
for (let i = start; i < end; i++) {
const unit = track.value.getUnitByIndex(i); const unit = track.value.getUnitByIndex(i);
if (unit) { if (unit) {
units.push(unit); units.push(unit);

View File

@@ -1,30 +1,16 @@
<template> <template>
<div class="track-settings" v-if="track && beat"> <div class="track-settings" v-if="track && beat">
<div class="track-settings-title-container"> <bool-box label="Loop:" v-model="track.looping.value" />
<editable-text-field v-model="track.name.value" /> <number-input v-model="track.loopLength.value" />
</div> <action-button v-show="track.looping.value" icon-name="snowflake" type="secondary" alt="Bake Loops" @click="track.bakeLoops()" />
<div class="track-settings-lower"> <bool-box label="Auto beat length:" v-model="beat.useAutoBeatLength.value" />
<action-button icon-name="snowflake" type="secondary" alt="Bake Loops" :disabled="!track.looping.value" @click="track.bakeLoops()" />
<action-button icon-name="trash" type="secondary" alt="Delete Track" @click="beat.removeTrack(trackIndex)" />
<div class="loop-settings">
<bool-box label="Loop:" v-model="track.looping.value" />
</div>
<div class="loop-settings-option" :class="{ hide: !track.looping.value }">
<number-input v-model="track.loopLength.value" />
</div>
<div class="visibility">
<icon color="var(--color-ui-neutral-dark)" :icon-name="track.visible.value ? 'eye' : 'eyeOff'" @click="track.visible.value = !track.visible.value"/>
</div>
</div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import NumberInput from "@/ui/Widgets/NumberInput/NumberInput.vue"; import NumberInput from "@/ui/Widgets/NumberInput/NumberInput.vue";
import Icon from "@/ui/Widgets/Icon/Icon.vue";
import BoolBox from "@/ui/Widgets/BoolBox/BoolBox.vue"; import BoolBox from "@/ui/Widgets/BoolBox/BoolBox.vue";
import ActionButton from "@/ui/Widgets/ActionButton/ActionButton.vue"; import ActionButton from "@/ui/Widgets/ActionButton/ActionButton.vue";
import EditableTextField from "@/ui/Widgets/EditableTextField/EditableTextField.vue";
import { useBeatStore } from "@/BeatStore"; import { useBeatStore } from "@/BeatStore";
import { computed } from "vue"; import { computed } from "vue";
@@ -39,54 +25,22 @@
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.track-settings-title-container input { .track-settings {
min-width: 100%; height: 3.5em;
height: 2em; display: inline-flex;
} text-align: center;
align-items: center;
.track-settings-title-container > div { .loop-settings {
width: 100%;
font-weight: bold;
padding: 0.5em;
transition: background-color 200ms;
cursor: pointer;
}
.track-settings-title-container > div:hover {
background-color: var(--color-ui-neutral-dark-hover);
}
.track-settings-lower {
height: 3.5em;
display: flex;
text-align: center;
align-items: center;
justify-content: space-between;
margin-bottom: 0.5em;
}
.track-settings-lower > * {
margin-right: 0.2em;
}
.track-settings-lower:last-child {
margin-right: 0;
}
.track-settings .loop-settings {
text-align: left; text-align: left;
flex: auto;
} }
.track-settings .loop-settings-option.hide { > * {
display: none; margin-right: 0.2em;
}
.track-settings .loop-settings-option {
flex: auto;
padding-right: 1em;
} }
.visibility { .visibility {
cursor: pointer; cursor: pointer;
} }
}
</style> </style>

View File

@@ -42,12 +42,30 @@
let blockNextMouseUp = false; let blockNextMouseUp = false;
let mouseHeld = false; let mouseHeld = false;
let movement = 0; let movement = 0;
let maybeSelecting = false;
function handleTouchStart(ev: TouchEvent): void {
rotationTimeout ??= setTimeout(() => {
emit('rotateType');
rotationTimeout = null;
blockNextMouseUp = true;
}, 400);
}
function handleTouchEnd(ev: TouchEvent): void {
if (rotationTimeout) {
clearTimeout(rotationTimeout);
rotationTimeout = null;
}
}
function handleMouseDown(ev: MouseEvent): void { function handleMouseDown(ev: MouseEvent): void {
blockNextMouseUp = false; blockNextMouseUp = false;
maybeSelecting = false;
if (ev.button === 0) { if (ev.button === 0) {
handleTouchStart();
unitMouseStart.value = props.id; unitMouseStart.value = props.id;
selectingUnits.value = true; maybeSelecting = true;
} else if (ev.button === 2) { } else if (ev.button === 2) {
unitMouseStart.value = props.id; unitMouseStart.value = props.id;
deselectingUnits.value = true; deselectingUnits.value = true;
@@ -57,6 +75,7 @@
} }
function handleMouseUp(ev: MouseEvent): void { function handleMouseUp(ev: MouseEvent): void {
handleTouchEnd();
if (!blockNextMouseUp && unitMouseStart.value === props.id) { if (!blockNextMouseUp && unitMouseStart.value === props.id) {
if (ev.button === 0) { if (ev.button === 0) {
emit('toggle'); emit('toggle');
@@ -67,6 +86,9 @@
} }
function handleMouseMove(ev: MouseEvent): void { function handleMouseMove(ev: MouseEvent): void {
if (maybeSelecting === true) {
selectingUnits.value = true;
}
if (selectingUnits.value || deselectingUnits.value) { if (selectingUnits.value || deselectingUnits.value) {
movement += ev.movementX**2 + ev.movementY**2; movement += ev.movementX**2 + ev.movementY**2;
} }
@@ -92,19 +114,6 @@
} }
} }
function handleTouchStart(ev: TouchEvent): void {
rotationTimeout ??= setTimeout(() => {
emit('rotateType');
rotationTimeout = null;
}, 400);
}
function handleTouchEnd(ev: TouchEvent): void {
if (rotationTimeout) {
clearTimeout(rotationTimeout);
rotationTimeout = null;
}
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -1,100 +1,78 @@
<template> <template>
<component <component
v-if="!editing"
ref="textField"
:is="nodeType" :is="nodeType"
class="static editable-text-field" class="static editable-text-field"
@click="onClick">
{{ model }}
</component>
<input
v-else
class="editable-text-field"
ref="inputField" ref="inputField"
@keydown.enter.prevent="onEnter"
@input="onInput"
@blur="onBlur" @blur="onBlur"
@keyup="onKeyUp" @keyup="onKeyUp"
@click="onClick"> v-model="model"
{{ modelValue }} @keydown.enter.prevent="onEnter"
</component> type="text" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref, nextTick, watch } from 'vue';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
nodeType?: string, nodeType?: string,
modelValue: string,
noEmpty?: boolean, noEmpty?: boolean,
}>(), { }>(), {
nodeType: 'div', nodeType: 'div',
noEmpty: true, noEmpty: true,
}); });
const emit = defineEmits<{ const editing = ref(false);
(e: 'update:modelValue', value: string): true,
}>(); const model = defineModel<string>({ default: '' });
let lastNonEmptyInput = props.modelValue ?? "";
const inputField = ref<HTMLElement | null>(null); const inputField = ref<HTMLElement | null>(null);
const textField = ref<HTMLElement | null>(null);
function onClick() { async function onClick() {
editing.value = true;
await nextTick();
if (inputField.value) { if (inputField.value) {
inputField.value.contentEditable = "true";
inputField.value.focus(); inputField.value.focus();
const selection = window.getSelection(); inputField.value.select();
const firstChild = inputField.value.firstChild;
if (selection && firstChild) {
const range = document.createRange();
range.setStart(firstChild, 0);
range.setEnd(firstChild, inputField.value.textContent?.length ?? 1);
selection.removeAllRanges();
selection.addRange(range);
}
} }
} }
function onInput(event: Event) { const width = ref('30px');
const input = (event.target as HTMLElement).textContent ?? "";
const inputToSet = props.noEmpty && input === "" ? lastNonEmptyInput : input; watch(editing, (newEditing) => {
emit('update:modelValue', inputToSet); if (!newEditing && textField.value) {
} width.value = `${ textField.value.getBoundingClientRect().width }px`;
}
}, { flush: 'pre' });
function onBlur(event: Event) { function onBlur(event: Event) {
if ((event.target as HTMLElement).textContent === "") { editing.value = false;
emit('update:modelValue', lastNonEmptyInput);
}
if (inputField.value) {
inputField.value.contentEditable = "false";
window.getSelection()?.removeAllRanges();
inputField.value.textContent = props.modelValue;
}
}
function onKeyUp(event: KeyboardEvent) {
if (event.key === "Enter") {
(event.target as HTMLElement).blur();
}
} }
function onEnter(e: KeyboardEvent) { function onEnter(e: KeyboardEvent) {
onBlur(e); editing.value = false;
} }
</script> </script>
<style scoped lang="scss"> <style scoped>
.editable-text-field { .editable-text-field {
padding: 0.2em; padding: 0.2em;
} }
input.input { input {
height: 100px; width: v-bind(width);
color: black;
}
.vertical-mode {
input.input {
height: 100px;
color: black;
}
} }
.static { .static {
transition: background-color 200ms; transition: background-color 200ms;
width: fit-content;
cursor: pointer; cursor: pointer;
&:hover { &:hover {

View File

@@ -13,7 +13,13 @@ import Floppy from "@/assets/svgs/floppy2-fill.svg";
import Delete from "@/assets/svgs/delete.svg"; import Delete from "@/assets/svgs/delete.svg";
import Eye from "@/assets/svgs/eye.svg"; import Eye from "@/assets/svgs/eye.svg";
import EyeOff from "@/assets/svgs/eye-off.svg"; import EyeOff from "@/assets/svgs/eye-off.svg";
import PaintBucket from '@/assets/svgs/paint-bucket.svg'; import PaintBucket from "@/assets/svgs/paint-bucket.svg";
import Sun from "@/assets/svgs/sun.svg";
import Moon from "@/assets/svgs/moon.svg";
import Up from "@/assets/svgs/chevron-up.svg";
import Down from "@/assets/svgs/chevron-down.svg";
import Left from "@/assets/svgs/chevron-left.svg";
import Right from "@/assets/svgs/chevron-right.svg";
export const IconUrlMap = { export const IconUrlMap = {
arrowClockwise: ArrowClockwise, arrowClockwise: ArrowClockwise,
@@ -32,6 +38,12 @@ export const IconUrlMap = {
eye: Eye, eye: Eye,
eyeOff: EyeOff, eyeOff: EyeOff,
paintBucket: PaintBucket, paintBucket: PaintBucket,
sun: Sun,
moon: Moon,
up: Up,
down: Down,
left: Left,
right: Right,
} as const; } as const;
export type IconName = keyof typeof IconUrlMap; export type IconName = keyof typeof IconUrlMap;

View File

@@ -13,7 +13,7 @@ html, body {
--color-ui-neutral-dark-hover: #a1a1a1; --color-ui-neutral-dark-hover: #a1a1a1;
--color-ui-neutral-dark-active: #c1c1c1; --color-ui-neutral-dark-active: #c1c1c1;
--color-bg-light: #e0e0e0; --color-bg-light: #e0e0e0;
--color-bg-medium: #fff; --color-bg-medium: #eee;
--color-bg-dark: #fff; --color-bg-dark: #fff;
--color-p-light: #282828; --color-p-light: #282828;
--color-p-light-hover: #fafafa; --color-p-light-hover: #fafafa;
@@ -23,13 +23,12 @@ html, body {
--color-p-dark-active: #464646; --color-p-dark-active: #464646;
--color-title-light: #282828; --color-title-light: #282828;
--color-title-dark: #282828; --color-title-dark: #282828;
--color-box-dark: #464646;
--color-box-dark-border: #464646;
--color-box-hover-dark: #5f5f5f;
--color-box-hover-dark-border: #5f5f5f;
--color-box: #e0e0e0; --color-box: #e0e0e0;
--color-box-hover: #f1f1f1; --color-box-hover: #f1f1f1;
}
* {
transition: color,background-color ease-out 300ms;
} }
body.dark { body.dark {
@@ -54,6 +53,12 @@ body.dark {
--color-box-hover: #5f5f5f; --color-box-hover: #5f5f5f;
} }
html, body {
position: relative;
width: 100%;
height: 100%;
}
#dropdowns { #dropdowns {
top: 0; top: 0;
left: 0; left: 0;