fix: linux display main page
This commit is contained in:
Generated
+34
-88
@@ -1097,9 +1097,6 @@
|
|||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1114,9 +1111,6 @@
|
|||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1131,9 +1125,6 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1148,9 +1139,6 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1165,9 +1153,6 @@
|
|||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1182,9 +1167,6 @@
|
|||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1199,9 +1181,6 @@
|
|||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1216,9 +1195,6 @@
|
|||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1233,9 +1209,6 @@
|
|||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1250,9 +1223,6 @@
|
|||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1267,9 +1237,6 @@
|
|||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1284,9 +1251,6 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1301,9 +1265,6 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1493,9 +1454,6 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "Apache-2.0 OR MIT",
|
"license": "Apache-2.0 OR MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1513,9 +1471,6 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "Apache-2.0 OR MIT",
|
"license": "Apache-2.0 OR MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1533,9 +1488,6 @@
|
|||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "Apache-2.0 OR MIT",
|
"license": "Apache-2.0 OR MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1553,9 +1505,6 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "Apache-2.0 OR MIT",
|
"license": "Apache-2.0 OR MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1573,9 +1522,6 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "Apache-2.0 OR MIT",
|
"license": "Apache-2.0 OR MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1809,9 +1755,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/baseline-browser-mapping": {
|
"node_modules/baseline-browser-mapping": {
|
||||||
"version": "2.10.44",
|
"version": "2.11.21",
|
||||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.44.tgz",
|
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.21.tgz",
|
||||||
"integrity": "sha512-T3ghW+sl/ZJ8w1v/yQx3qvJ9040DWoLBz8JT/CILbAKcFyG9b2MRe75v6W5uXjv6uH1lumK2Kv46y2zSkcej0Q==",
|
"integrity": "sha512-uh8vpY/1/YyFkunIDFH/12p7/7VdPKA1hejMVEbdkEaWnUz0Hesvx5EbiU6XxjyHZIOju+ZMbQJkRh+es3/spQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -1822,9 +1768,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "5.0.7",
|
"version": "5.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
|
||||||
"integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
|
"integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
@@ -1832,13 +1778,13 @@
|
|||||||
"balanced-match": "^4.0.2"
|
"balanced-match": "^4.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "18 || 20 || >=22"
|
"node": "20 || >=22"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/browserslist": {
|
"node_modules/browserslist": {
|
||||||
"version": "4.28.6",
|
"version": "4.28.9",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.6.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz",
|
||||||
"integrity": "sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==",
|
"integrity": "sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -1856,11 +1802,11 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.10.42",
|
"baseline-browser-mapping": "^2.11.20",
|
||||||
"caniuse-lite": "^1.0.30001803",
|
"caniuse-lite": "^1.0.30001810",
|
||||||
"electron-to-chromium": "^1.5.389",
|
"electron-to-chromium": "^1.5.420",
|
||||||
"node-releases": "^2.0.51",
|
"node-releases": "^2.0.54",
|
||||||
"update-browserslist-db": "^1.2.3"
|
"update-browserslist-db": "^1.3.2"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"browserslist": "cli.js"
|
"browserslist": "cli.js"
|
||||||
@@ -1870,9 +1816,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001806",
|
"version": "1.0.30001810",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz",
|
||||||
"integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==",
|
"integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -1947,9 +1893,9 @@
|
|||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.5.394",
|
"version": "1.5.422",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.394.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.422.tgz",
|
||||||
"integrity": "sha512-Wmt2Gm0o8JWBuGgmc4XZ0u9s1RaCRqhxP47phplmfg04+qypTUurpeJGP45A7Fhv7jdrrVH44PLlR9qXo37cVQ==",
|
"integrity": "sha512-UvA/32XqrLDdZSn7Jllo1AYNcWji/G0d5M0GTViE7KoGBiMunw3a34Sb2KO4ZZyrSEhqsxFoVhWWJshdyfKqJA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
@@ -2562,9 +2508,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.16",
|
"version": "3.3.18",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
|
||||||
"integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
|
"integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -2589,9 +2535,9 @@
|
|||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/node-releases": {
|
"node_modules/node-releases": {
|
||||||
"version": "2.0.51",
|
"version": "2.0.54",
|
||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz",
|
||||||
"integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==",
|
"integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -2694,9 +2640,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.21",
|
"version": "8.5.28",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.21.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz",
|
||||||
"integrity": "sha512-v4sDNP3fdNiWMfabO7OwOQdOX8TiQSztKyT1Wj0w+j7LDallJThJRBBBmzVGyYj0crMh7jlV4zepPkiNu9UwDQ==",
|
"integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -2714,7 +2660,7 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.16",
|
"nanoid": "^3.3.18",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
"source-map-js": "^1.2.1"
|
"source-map-js": "^1.2.1"
|
||||||
},
|
},
|
||||||
@@ -2962,9 +2908,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/update-browserslist-db": {
|
"node_modules/update-browserslist-db": {
|
||||||
"version": "1.2.3",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz",
|
||||||
"integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
|
"integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -22,4 +22,4 @@ tauri = { version = "2", features = [] }
|
|||||||
tauri-plugin-opener = "2"
|
tauri-plugin-opener = "2"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
dsp_thomann = { path = "../../dsp_thomann", features = ["serialization"] }
|
dsp_thomann = { path = "../../dsp-thomann", features = ["serialization"] }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ export function TriangleUpIcon(props: React.SVGProps<SVGSVGElement>) {
|
|||||||
return (
|
return (
|
||||||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||||
<path
|
<path
|
||||||
d="M8 3L13 10H3L8 3Z"
|
d="M8 5L4 10H12L8 5Z"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
strokeWidth="1.5"
|
strokeWidth="1.5"
|
||||||
fill="transparent"
|
fill="transparent"
|
||||||
@@ -16,7 +16,7 @@ export function TriangleDownIcon(props: React.SVGProps<SVGSVGElement>) {
|
|||||||
return (
|
return (
|
||||||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||||
<path
|
<path
|
||||||
d="M8 13L3 6H13L8 13Z"
|
d="M8 11L4 6H12L8 11Z"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
strokeWidth="1.5"
|
strokeWidth="1.5"
|
||||||
fill="transparent"
|
fill="transparent"
|
||||||
@@ -24,4 +24,4 @@ export function TriangleDownIcon(props: React.SVGProps<SVGSVGElement>) {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/* ChoiceSelect.css */
|
||||||
|
.choice-select {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.choice-select[data-disabled] {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.choice-select__input {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
height: var(--card-control-height);
|
||||||
|
padding: 0 var(--space-6) 0 var(--space-3);
|
||||||
|
|
||||||
|
background: var(--bg-panel);
|
||||||
|
color: var(--text-primary);
|
||||||
|
|
||||||
|
border: var(--border-width) solid var(--border-hairline);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
|
||||||
|
font-family: var(--font-ui);
|
||||||
|
font-size: var(--font-sm);
|
||||||
|
|
||||||
|
appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
transition: border-color var(--dur-base) var(--ease-standard);
|
||||||
|
}
|
||||||
|
|
||||||
|
.choice-select__input:focus {
|
||||||
|
border-color: var(--accent-brand);
|
||||||
|
}
|
||||||
|
|
||||||
|
.choice-select__input:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.choice-select__chevron {
|
||||||
|
position: absolute;
|
||||||
|
right: var(--space-2);
|
||||||
|
top: 50%;
|
||||||
|
height: 70%;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
|
||||||
|
pointer-events: none;
|
||||||
|
color: var(--accent-brand);
|
||||||
|
font-size: var(--font-sm);
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// ChoiceSelect.tsx
|
||||||
|
import { TriangleDownIcon } from '../../assets/icons/Triangle';
|
||||||
|
import './ChoiceSelect.css';
|
||||||
|
|
||||||
|
type ChoiceSelectProps<T extends string> = {
|
||||||
|
value: T;
|
||||||
|
options: readonly T[];
|
||||||
|
formatLabel?: (value: T) => string;
|
||||||
|
onChange: (value: T) => void;
|
||||||
|
id?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
function ChoiceSelect<T extends string>({
|
||||||
|
value,
|
||||||
|
options,
|
||||||
|
formatLabel = (v) => v,
|
||||||
|
onChange,
|
||||||
|
id,
|
||||||
|
disabled,
|
||||||
|
}: ChoiceSelectProps<T>) {
|
||||||
|
return (
|
||||||
|
<div className="choice-select" data-disabled={disabled || undefined}>
|
||||||
|
<select
|
||||||
|
id={id}
|
||||||
|
className="choice-select__input"
|
||||||
|
value={value}
|
||||||
|
disabled={disabled}
|
||||||
|
onChange={(e) => onChange(e.target.value as T)}
|
||||||
|
>
|
||||||
|
{options.map((option) => (
|
||||||
|
<option key={option} value={option}>
|
||||||
|
{formatLabel(option)}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<span className="choice-select__chevron" aria-hidden="true">
|
||||||
|
<TriangleDownIcon />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChoiceSelect;
|
||||||
@@ -20,14 +20,13 @@
|
|||||||
font-family: var(--font-ui);
|
font-family: var(--font-ui);
|
||||||
font-size: 3cqh;
|
font-size: 3cqh;
|
||||||
font-weight: var(--font-weight-semibold);
|
font-weight: var(--font-weight-semibold);
|
||||||
text-align: center;
|
text-align: left;
|
||||||
|
|
||||||
color: var(--accent-brand);
|
color: var(--accent-brand);
|
||||||
|
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: var(--radius-sm);
|
/* padding: 0.5ch; */
|
||||||
padding: 0.5ch;
|
|
||||||
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -35,7 +34,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.choice-stepper__input:focus {
|
.choice-stepper__input:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.choice-stepper__arrows {
|
.choice-stepper__arrows {
|
||||||
@@ -78,3 +77,11 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.choice-stepper[data-disabled] {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.choice-stepper__input:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,60 +1,97 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { JSX, useEffect, useState } from 'react';
|
||||||
import Button from '../Button/Button';
|
import Button from '../Button/Button';
|
||||||
import { TriangleUpIcon, TriangleDownIcon } from '../../assets/icons/Triangle';
|
import { TriangleUpIcon, TriangleDownIcon } from '../../assets/icons/Triangle';
|
||||||
import './ChoiceStepper.css';
|
import './ChoiceStepper.css';
|
||||||
|
|
||||||
type ChoiceStepperProps<T> = {
|
function StepperArrows({
|
||||||
|
onUp,
|
||||||
|
onDown,
|
||||||
|
canUp,
|
||||||
|
canDown,
|
||||||
|
disabled,
|
||||||
|
}: {
|
||||||
|
onUp: () => void;
|
||||||
|
onDown: () => void;
|
||||||
|
canUp: boolean;
|
||||||
|
canDown: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="choice-stepper__arrows">
|
||||||
|
<Button
|
||||||
|
variant="toggle"
|
||||||
|
className="choice-stepper__arrow-btn"
|
||||||
|
onClick={onUp}
|
||||||
|
disabled={disabled || !canUp}
|
||||||
|
>
|
||||||
|
<TriangleUpIcon />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="toggle"
|
||||||
|
className="choice-stepper__arrow-btn"
|
||||||
|
onClick={onDown}
|
||||||
|
disabled={disabled || !canDown}
|
||||||
|
>
|
||||||
|
<TriangleDownIcon />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- List variant ----
|
||||||
|
|
||||||
|
type ChoiceStepperListProps<T> = {
|
||||||
value: T;
|
value: T;
|
||||||
values: readonly T[];
|
values: readonly T[];
|
||||||
formatLabel: (value: T) => string;
|
formatLabel: (value: T) => string;
|
||||||
onChange: (value: T) => void;
|
onChange: (value: T) => void;
|
||||||
|
id?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
function ChoiceStepper<T>({ value, values, formatLabel, onChange }: ChoiceStepperProps<T>) {
|
function ChoiceStepperList<T>({
|
||||||
|
value,
|
||||||
|
values,
|
||||||
|
formatLabel,
|
||||||
|
onChange,
|
||||||
|
id,
|
||||||
|
disabled,
|
||||||
|
className,
|
||||||
|
}: ChoiceStepperListProps<T>) {
|
||||||
const index = values.indexOf(value);
|
const index = values.indexOf(value);
|
||||||
|
|
||||||
const canUp = index === -1 || index < values.length - 1;
|
const canUp = index === -1 || index < values.length - 1;
|
||||||
const canDown = index > 0;
|
const canDown = index > 0;
|
||||||
|
|
||||||
const [text, setText] = useState(formatLabel(value));
|
const [text, setText] = useState(formatLabel(value));
|
||||||
|
useEffect(() => setText(formatLabel(value)), [value, formatLabel]);
|
||||||
useEffect(() => {
|
|
||||||
setText(formatLabel(value));
|
|
||||||
}, [value, formatLabel]);
|
|
||||||
|
|
||||||
const step = (direction: 1 | -1) => {
|
const step = (direction: 1 | -1) => {
|
||||||
const currentIndex = index === -1 ? 0 : index;
|
const currentIndex = index === -1 ? 0 : index;
|
||||||
|
|
||||||
const nextIndex = Math.min(values.length - 1, Math.max(0, currentIndex + direction));
|
const nextIndex = Math.min(values.length - 1, Math.max(0, currentIndex + direction));
|
||||||
|
if (nextIndex !== currentIndex) onChange(values[nextIndex]);
|
||||||
if (nextIndex !== currentIndex) {
|
|
||||||
onChange(values[nextIndex]);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const commitText = () => {
|
const commitText = () => {
|
||||||
const match = values.find(
|
const match = values.find(
|
||||||
(v) => formatLabel(v).trim().toLowerCase() === text.trim().toLowerCase()
|
(v) => formatLabel(v).trim().toLowerCase() === text.trim().toLowerCase()
|
||||||
);
|
);
|
||||||
|
if (match !== undefined) onChange(match);
|
||||||
if (match !== undefined) {
|
else setText(formatLabel(value));
|
||||||
onChange(match);
|
|
||||||
} else {
|
|
||||||
setText(formatLabel(value));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="choice-stepper">
|
<div className={`choice-stepper ${className ?? ''}`} data-disabled={disabled || undefined}>
|
||||||
<input
|
<input
|
||||||
|
id={id}
|
||||||
className="choice-stepper__input"
|
className="choice-stepper__input"
|
||||||
value={text}
|
value={text}
|
||||||
|
disabled={disabled}
|
||||||
onChange={(e) => setText(e.target.value)}
|
onChange={(e) => setText(e.target.value)}
|
||||||
onBlur={commitText}
|
onBlur={commitText}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') e.currentTarget.blur();
|
||||||
e.currentTarget.blur();
|
else if (e.key === 'ArrowUp') {
|
||||||
} else if (e.key === 'ArrowUp') {
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
step(1);
|
step(1);
|
||||||
} else if (e.key === 'ArrowDown') {
|
} else if (e.key === 'ArrowDown') {
|
||||||
@@ -63,28 +100,99 @@ function ChoiceStepper<T>({ value, values, formatLabel, onChange }: ChoiceSteppe
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<StepperArrows
|
||||||
<div className="choice-stepper__arrows">
|
onUp={() => step(1)}
|
||||||
<Button
|
onDown={() => step(-1)}
|
||||||
variant="toggle"
|
canUp={canUp}
|
||||||
className="choice-stepper__arrow-btn"
|
canDown={canDown}
|
||||||
onClick={() => step(1)}
|
disabled={disabled}
|
||||||
disabled={!canUp}
|
/>
|
||||||
>
|
|
||||||
<TriangleUpIcon />
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
variant="toggle"
|
|
||||||
className="choice-stepper__arrow-btn"
|
|
||||||
onClick={() => step(-1)}
|
|
||||||
disabled={!canDown}
|
|
||||||
>
|
|
||||||
<TriangleDownIcon />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- Range variant (concrete, no generic) ----
|
||||||
|
|
||||||
|
type ChoiceStepperRangeProps = {
|
||||||
|
value: number;
|
||||||
|
min: number;
|
||||||
|
max: number;
|
||||||
|
step?: number;
|
||||||
|
formatLabel: (value: number) => string;
|
||||||
|
onChange: (value: number) => void;
|
||||||
|
id?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function ChoiceStepperRange({
|
||||||
|
value,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
step: stepSize = 1,
|
||||||
|
formatLabel,
|
||||||
|
onChange,
|
||||||
|
id,
|
||||||
|
disabled,
|
||||||
|
className,
|
||||||
|
}: ChoiceStepperRangeProps) {
|
||||||
|
const [text, setText] = useState(formatLabel(value));
|
||||||
|
useEffect(() => setText(formatLabel(value)), [value, formatLabel]);
|
||||||
|
|
||||||
|
const clamp = (n: number) => Math.min(max, Math.max(min, n));
|
||||||
|
|
||||||
|
const canUp = value < max;
|
||||||
|
const canDown = value > min;
|
||||||
|
|
||||||
|
const step = (direction: 1 | -1) => {
|
||||||
|
const next = clamp(value + direction * stepSize);
|
||||||
|
if (next !== value) onChange(next);
|
||||||
|
};
|
||||||
|
|
||||||
|
const commitText = () => {
|
||||||
|
const parsed = Number(text);
|
||||||
|
if (Number.isFinite(parsed)) onChange(clamp(parsed));
|
||||||
|
else setText(formatLabel(value));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`choice-stepper ${className ?? ''}`} data-disabled={disabled || undefined}>
|
||||||
|
<input
|
||||||
|
id={id}
|
||||||
|
className="choice-stepper__input"
|
||||||
|
value={text}
|
||||||
|
disabled={disabled}
|
||||||
|
onChange={(e) => setText(e.target.value)}
|
||||||
|
onBlur={commitText}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Enter') e.currentTarget.blur();
|
||||||
|
else if (e.key === 'ArrowUp') {
|
||||||
|
e.preventDefault();
|
||||||
|
step(1);
|
||||||
|
} else if (e.key === 'ArrowDown') {
|
||||||
|
e.preventDefault();
|
||||||
|
step(-1);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<StepperArrows
|
||||||
|
onUp={() => step(1)}
|
||||||
|
onDown={() => step(-1)}
|
||||||
|
canUp={canUp}
|
||||||
|
canDown={canDown}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ChoiceStepper<T>(props: ChoiceStepperListProps<T>): JSX.Element;
|
||||||
|
function ChoiceStepper(props: ChoiceStepperRangeProps): JSX.Element;
|
||||||
|
function ChoiceStepper(props: ChoiceStepperListProps<unknown> | ChoiceStepperRangeProps) {
|
||||||
|
if ('values' in props) {
|
||||||
|
return <ChoiceStepperList {...props} />;
|
||||||
|
}
|
||||||
|
return <ChoiceStepperRange {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
export default ChoiceStepper;
|
export default ChoiceStepper;
|
||||||
|
|||||||
@@ -68,8 +68,14 @@ function getPeqCrossoverPoints(
|
|||||||
getPeqCurvePoints({ freqs, bands: peqChain.bands as any }),
|
getPeqCurvePoints({ freqs, bands: peqChain.bands as any }),
|
||||||
getCrossoverCurvePoints({
|
getCrossoverCurvePoints({
|
||||||
freqs,
|
freqs,
|
||||||
highPass: { frequency: crossover.high_pass.frequency, filter_type: crossover.high_pass.slope as any },
|
highPass: {
|
||||||
lowPass: { frequency: crossover.low_pass.frequency, filter_type: crossover.low_pass.slope as any },
|
frequency: crossover.high_pass.frequency,
|
||||||
|
filter_type: crossover.high_pass.slope as any,
|
||||||
|
},
|
||||||
|
lowPass: {
|
||||||
|
frequency: crossover.low_pass.frequency,
|
||||||
|
filter_type: crossover.low_pass.slope as any,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -317,7 +323,9 @@ function GainSection({
|
|||||||
const [showOutputStages, setShowOutputStages] = useState(false);
|
const [showOutputStages, setShowOutputStages] = useState(false);
|
||||||
|
|
||||||
const [liveInputGains, setLiveInputGains] = useState<Partial<Record<InputChannel, number>>>({});
|
const [liveInputGains, setLiveInputGains] = useState<Partial<Record<InputChannel, number>>>({});
|
||||||
const [liveOutputGains, setLiveOutputGains] = useState<Partial<Record<OutputChannel, number>>>({});
|
const [liveOutputGains, setLiveOutputGains] = useState<Partial<Record<OutputChannel, number>>>(
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
const setLiveInputGain = (channel: InputChannel, value: number | null) => {
|
const setLiveInputGain = (channel: InputChannel, value: number | null) => {
|
||||||
setLiveInputGains((prev) => {
|
setLiveInputGains((prev) => {
|
||||||
@@ -381,7 +389,11 @@ function GainSection({
|
|||||||
inputColors,
|
inputColors,
|
||||||
showInputStages,
|
showInputStages,
|
||||||
(channel) =>
|
(channel) =>
|
||||||
getInputChannelCurves(dsp.state.current_config.input_states[channel], freqs, liveInputGains[channel])
|
getInputChannelCurves(
|
||||||
|
dsp.state.current_config.input_states[channel],
|
||||||
|
freqs,
|
||||||
|
liveInputGains[channel]
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const outputCurves = buildCurvesForChannels(
|
const outputCurves = buildCurvesForChannels(
|
||||||
@@ -390,7 +402,11 @@ function GainSection({
|
|||||||
outputColors,
|
outputColors,
|
||||||
showOutputStages,
|
showOutputStages,
|
||||||
(channel) =>
|
(channel) =>
|
||||||
getOutputChannelCurves(dsp.state.current_config.output_states[channel], freqs, liveOutputGains[channel])
|
getOutputChannelCurves(
|
||||||
|
dsp.state.current_config.output_states[channel],
|
||||||
|
freqs,
|
||||||
|
liveOutputGains[channel]
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const { yMin: inputYMin, yMax: inputYMax } = computeYAxisBounds(
|
const { yMin: inputYMin, yMax: inputYMax } = computeYAxisBounds(
|
||||||
@@ -535,4 +551,4 @@ function GainSection({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default GainSection;
|
export default GainSection;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
} from '../../constants';
|
} from '../../constants';
|
||||||
import { SidebarId } from '../../constants';
|
import { SidebarId } from '../../constants';
|
||||||
import SignalFlowMatrix from '../../../../components/SignalFlowMatrix/SignalFlowMatrix';
|
import SignalFlowMatrix from '../../../../components/SignalFlowMatrix/SignalFlowMatrix';
|
||||||
import './MatrixSection.css';
|
import './Matrixsection.css';
|
||||||
|
|
||||||
const GAIN_VALUES: number[] = (() => {
|
const GAIN_VALUES: number[] = (() => {
|
||||||
const values: number[] = [];
|
const values: number[] = [];
|
||||||
|
|||||||
@@ -58,9 +58,7 @@ export function sumCurvePoints(pointArrays: CurvePoint[][]): CurvePoint[] {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sumPhaseCurvePoints(
|
export function sumPhaseCurvePoints(pointArrays: CurvePoint[][]): CurvePoint[] {
|
||||||
pointArrays: CurvePoint[][]
|
|
||||||
): CurvePoint[] {
|
|
||||||
if (pointArrays.length === 0) return [];
|
if (pointArrays.length === 0) return [];
|
||||||
|
|
||||||
const length = pointArrays[0].length;
|
const length = pointArrays[0].length;
|
||||||
@@ -85,8 +83,7 @@ export function sumPhaseCurvePoints(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wrap to [-180, 180].
|
// Wrap to [-180, 180].
|
||||||
const wrapped =
|
const wrapped = ((((phase + 180) % 360) + 360) % 360) - 180;
|
||||||
((((phase + 180) % 360) + 360) % 360) - 180;
|
|
||||||
|
|
||||||
result[i] = {
|
result[i] = {
|
||||||
x: pointArrays[0][i].x,
|
x: pointArrays[0][i].x,
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
import {
|
import { CurvePoint, CurveStyle, Curve, DEFAULT_CURVE_COLORS, sumCurvePoints } from './curve';
|
||||||
CurvePoint,
|
|
||||||
CurveStyle,
|
|
||||||
Curve,
|
|
||||||
DEFAULT_CURVE_COLORS,
|
|
||||||
sumCurvePoints,
|
|
||||||
} from './curve';
|
|
||||||
|
|
||||||
export { sumCurvePoints };
|
export { sumCurvePoints };
|
||||||
|
|
||||||
|
|||||||
@@ -332,4 +332,4 @@ export function getPeqPhasePoints({
|
|||||||
x: freq,
|
x: freq,
|
||||||
y: peqTotalPhaseDeg(freq, bands, sampleRate),
|
y: peqTotalPhaseDeg(freq, bands, sampleRate),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,21 +34,26 @@
|
|||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-dsp-form__field input,
|
/* Input */
|
||||||
.add-dsp-form__field select {
|
|
||||||
|
.add-dsp-form__field input {
|
||||||
height: var(--card-control-height);
|
height: var(--card-control-height);
|
||||||
padding: 0 var(--space-3);
|
padding: 0 var(--space-3);
|
||||||
|
|
||||||
background: var(--bg-panel);
|
background: var(--bg-panel);
|
||||||
|
color: var(--text-primary);
|
||||||
|
|
||||||
border: var(--border-width) solid var(--border-hairline);
|
border: var(--border-width) solid var(--border-hairline);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-family: var(--font-ui);
|
font-family: var(--font-ui);
|
||||||
font-size: var(--font-sm);
|
font-size: var(--font-sm);
|
||||||
|
|
||||||
outline: none;
|
outline: none;
|
||||||
|
|
||||||
|
appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
|
||||||
transition: border-color var(--dur-base) var(--ease-standard);
|
transition: border-color var(--dur-base) var(--ease-standard);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,11 +61,15 @@
|
|||||||
font-family: var(--font-mono);
|
font-family: var(--font-mono);
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-dsp-form__field input:focus,
|
.add-dsp-form__field input:focus {
|
||||||
.add-dsp-form__field select:focus {
|
|
||||||
border-color: var(--accent-brand);
|
border-color: var(--accent-brand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.add-dsp-form__field input::placeholder {
|
||||||
|
color: var(--text-muted);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
.add-dsp-form__actions {
|
.add-dsp-form__actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
@@ -68,7 +77,89 @@
|
|||||||
margin-top: var(--space-2);
|
margin-top: var(--space-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.add-dsp-form__field input:disabled {
|
||||||
|
color: var(--text-muted);
|
||||||
|
background: var(--bg-panel);
|
||||||
|
border-color: var(--border-hairline);
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CheckBox */
|
||||||
|
|
||||||
.add-dsp-form__checkbox-label {
|
.add-dsp-form__checkbox-label {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-dsp-form__checkbox-label input[type='checkbox'] {
|
||||||
|
appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
flex: 0 0 auto;
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
background: var(--bg-panel);
|
||||||
|
border: var(--border-width) solid var(--border-hairline);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
transition:
|
||||||
|
border-color var(--dur-base) var(--ease-standard),
|
||||||
|
background var(--dur-base) var(--ease-standard);
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-dsp-form__checkbox-label input[type='checkbox']:checked {
|
||||||
|
background: var(--accent-brand);
|
||||||
|
border-color: var(--accent-brand);
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-dsp-form__checkbox-label input[type='checkbox']:checked::after {
|
||||||
|
content: '';
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
|
||||||
|
width: 5px;
|
||||||
|
height: 9px;
|
||||||
|
|
||||||
|
border: solid var(--bg-panel);
|
||||||
|
border-width: 0 2px 2px 0;
|
||||||
|
|
||||||
|
transform: translate(-50%, -60%) rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-dsp-form__checkbox-label input[type='checkbox']:focus-visible {
|
||||||
|
outline: 2px solid var(--accent-brand);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stepper */
|
||||||
|
|
||||||
|
.add-dsp-form__stepper {
|
||||||
|
width: auto;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
min-width: 120px;
|
||||||
|
height: var(--card-control-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-dsp-form__stepper .choice-stepper__arrows {
|
||||||
|
padding: 4px 4px 4px 0;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-dsp-form__field .choice-stepper__input {
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { useState } from 'react';
|
|||||||
import { DSP, DSP_DEFAULT_PARAMETERS, DSPTypes } from '../../types';
|
import { DSP, DSP_DEFAULT_PARAMETERS, DSPTypes } from '../../types';
|
||||||
import Button from '../../components/Button/Button';
|
import Button from '../../components/Button/Button';
|
||||||
import Card from '../../components/Card/Card';
|
import Card from '../../components/Card/Card';
|
||||||
|
import ChoiceStepper from '../../components/ChoiceStepper/ChoiceStepper';
|
||||||
|
import ChoiceSelect from '../../components/ChoiceSelect/ChoiceSelect';
|
||||||
|
|
||||||
import './AddDspPage.css';
|
import './AddDspPage.css';
|
||||||
|
|
||||||
@@ -14,6 +16,8 @@ type DSPForm = {
|
|||||||
demo: boolean;
|
demo: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DSP_TYPES = Object.keys(DSP_DEFAULT_PARAMETERS) as DSPTypes[];
|
||||||
|
|
||||||
function AddDSPForm({
|
function AddDSPForm({
|
||||||
onCancel,
|
onCancel,
|
||||||
onAdd,
|
onAdd,
|
||||||
@@ -39,9 +43,7 @@ function AddDSPForm({
|
|||||||
|
|
||||||
function submit() {
|
function submit() {
|
||||||
if (!canSubmit) return;
|
if (!canSubmit) return;
|
||||||
onAdd({
|
onAdd({ ...form });
|
||||||
...form,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -64,38 +66,24 @@ function AddDSPForm({
|
|||||||
<div className="add-dsp-form__row">
|
<div className="add-dsp-form__row">
|
||||||
<div className="add-dsp-form__field">
|
<div className="add-dsp-form__field">
|
||||||
<label htmlFor="f-type">Model</label>
|
<label htmlFor="f-type">Model</label>
|
||||||
<select
|
<ChoiceSelect
|
||||||
id="f-type"
|
id="f-type"
|
||||||
value={form.type}
|
value={form.type}
|
||||||
onChange={(e) => {
|
options={DSP_TYPES}
|
||||||
const type = e.target.value as DSPTypes;
|
onChange={(type) => setForm({ ...form, type })}
|
||||||
|
/>
|
||||||
setForm({
|
|
||||||
...form,
|
|
||||||
type,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{Object.keys(DSP_DEFAULT_PARAMETERS).map((type) => (
|
|
||||||
<option key={type} value={type}>
|
|
||||||
{type}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="add-dsp-form__field">
|
<div className="add-dsp-form__field">
|
||||||
<label htmlFor="f-deviceid">Device ID</label>
|
<label htmlFor="f-deviceid">Device ID</label>
|
||||||
<input
|
<ChoiceStepper
|
||||||
id="f-deviceid"
|
id="f-deviceid"
|
||||||
className="mono"
|
className="add-dsp-form__stepper"
|
||||||
type="number"
|
value={form.deviceId}
|
||||||
min={1}
|
min={1}
|
||||||
max={255}
|
max={255}
|
||||||
step={1}
|
formatLabel={(v) => String(v)}
|
||||||
placeholder="1"
|
onChange={(deviceId) => setForm({ ...form, deviceId })}
|
||||||
value={form.deviceId}
|
|
||||||
onChange={(e) => setForm({ ...form, deviceId: Number(e.target.value) })}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -114,13 +102,15 @@ function AddDSPForm({
|
|||||||
|
|
||||||
<div className="add-dsp-form__field">
|
<div className="add-dsp-form__field">
|
||||||
<label htmlFor="f-port">Port</label>
|
<label htmlFor="f-port">Port</label>
|
||||||
<input
|
<ChoiceStepper
|
||||||
id="f-port"
|
id="f-port"
|
||||||
className="mono"
|
className="add-dsp-form__stepper"
|
||||||
type="number"
|
|
||||||
value={form.port}
|
value={form.port}
|
||||||
|
min={1}
|
||||||
|
max={65535}
|
||||||
disabled={form.demo}
|
disabled={form.demo}
|
||||||
onChange={(e) => setForm({ ...form, port: Number(e.target.value) })}
|
formatLabel={(v) => String(v)}
|
||||||
|
onChange={(port) => setForm({ ...form, port })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -141,7 +131,6 @@ function AddDSPForm({
|
|||||||
<Button variant="ghost" onClick={onCancel}>
|
<Button variant="ghost" onClick={onCancel}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button variant="primary" onClick={submit} disabled={!canSubmit}>
|
<Button variant="primary" onClick={submit} disabled={!canSubmit}>
|
||||||
Add DSP
|
Add DSP
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Reference in New Issue
Block a user