feat: 添加配置面板

This commit is contained in:
2021-10-05 23:45:52 +08:00
parent 615ba6ffba
commit 44b4ef1a44
13 changed files with 524 additions and 124 deletions
+222
View File
@@ -13,11 +13,13 @@
"@testing-library/react": "^11.2.7", "@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^12.8.3", "@testing-library/user-event": "^12.8.3",
"antd": "^4.16.13", "antd": "^4.16.13",
"bootstrap": "^4.6.0",
"mobx": "^6.3.3", "mobx": "^6.3.3",
"mobx-react-lite": "^3.2.1", "mobx-react-lite": "^3.2.1",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-scripts": "4.0.3", "react-scripts": "4.0.3",
"reactstrap": "^8.10.0",
"web-vitals": "^1.1.2" "web-vitals": "^1.1.2"
} }
}, },
@@ -1928,6 +1930,19 @@
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz",
"integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==" "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w=="
}, },
"node_modules/@hypnosphi/create-react-context": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@hypnosphi/create-react-context/-/create-react-context-0.3.1.tgz",
"integrity": "sha512-V1klUed202XahrWJLLOT3EXNeCpFHCcJntdFGI15ntCwau+jfT386w7OFTMaCqOgXUH1fa0w/I1oZs+i/Rfr0A==",
"dependencies": {
"gud": "^1.0.0",
"warning": "^4.0.3"
},
"peerDependencies": {
"prop-types": "^15.0.0",
"react": ">=0.14.0"
}
},
"node_modules/@istanbuljs/load-nyc-config": { "node_modules/@istanbuljs/load-nyc-config": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@@ -5528,6 +5543,19 @@
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
}, },
"node_modules/bootstrap": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.0.tgz",
"integrity": "sha512-Io55IuQY3kydzHtbGvQya3H+KorS/M9rSNyfCGCg9WZ4pyT/lCxIlpJgG1GXW/PswzC84Tr2fBYi+7+jFVQQBw==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/bootstrap"
},
"peerDependencies": {
"jquery": "1.9.1 - 3",
"popper.js": "^1.16.1"
}
},
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -7436,6 +7464,14 @@
"utila": "~0.4" "utila": "~0.4"
} }
}, },
"node_modules/dom-helpers": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz",
"integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==",
"dependencies": {
"@babel/runtime": "^7.1.2"
}
},
"node_modules/dom-serializer": { "node_modules/dom-serializer": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
@@ -9810,6 +9846,11 @@
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=",
"optional": true "optional": true
}, },
"node_modules/gud": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
"integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw=="
},
"node_modules/gzip-size": { "node_modules/gzip-size": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz",
@@ -13090,6 +13131,12 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/jquery": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
"integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==",
"peer": true
},
"node_modules/js-tokens": { "node_modules/js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -15003,6 +15050,16 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/popper.js": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
"integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
"deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/portfinder": { "node_modules/portfinder": {
"version": "1.0.28", "version": "1.0.28",
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",
@@ -17354,6 +17411,28 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
}, },
"node_modules/react-lifecycles-compat": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
"node_modules/react-popper": {
"version": "1.3.11",
"resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.11.tgz",
"integrity": "sha512-VSA/bS+pSndSF2fiasHK/PTEEAyOpX60+H5EPAjoArr8JGm+oihu4UbrqcEBpQibJxBVCpYyjAX7abJ+7DoYVg==",
"dependencies": {
"@babel/runtime": "^7.1.2",
"@hypnosphi/create-react-context": "^0.3.1",
"deep-equal": "^1.1.1",
"popper.js": "^1.14.4",
"prop-types": "^15.6.1",
"typed-styles": "^0.0.7",
"warning": "^4.0.2"
},
"peerDependencies": {
"react": "0.14.x || ^15.0.0 || ^16.0.0 || ^17.0.0"
}
},
"node_modules/react-refresh": { "node_modules/react-refresh": {
"version": "0.8.3", "version": "0.8.3",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz",
@@ -17483,6 +17562,48 @@
"semver": "bin/semver" "semver": "bin/semver"
} }
}, },
"node_modules/react-transition-group": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz",
"integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==",
"dependencies": {
"dom-helpers": "^3.4.0",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2",
"react-lifecycles-compat": "^3.0.4"
},
"peerDependencies": {
"react": ">=15.0.0",
"react-dom": ">=15.0.0"
}
},
"node_modules/reactstrap": {
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-8.10.0.tgz",
"integrity": "sha512-MsFUB/fRZj6Orf8Mxc93iYuAs+9ngnFmy2cfYlzkmc4vi5oM4u6ziY/DsO71lDG3cotxHRyS3Flr51cuYv+IEQ==",
"dependencies": {
"@babel/runtime": "^7.12.5",
"classnames": "^2.2.3",
"prop-types": "^15.5.8",
"react-popper": "^1.3.6",
"react-transition-group": "^2.3.1"
},
"peerDependencies": {
"react": ">=16.3.0",
"react-dom": ">=16.3.0"
}
},
"node_modules/reactstrap/node_modules/@babel/runtime": {
"version": "7.15.4",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz",
"integrity": "sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==",
"dependencies": {
"regenerator-runtime": "^0.13.4"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/read-pkg": { "node_modules/read-pkg": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
@@ -20374,6 +20495,11 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/typed-styles": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz",
"integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q=="
},
"node_modules/typedarray": { "node_modules/typedarray": {
"version": "0.0.6", "version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
@@ -23959,6 +24085,15 @@
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz",
"integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==" "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w=="
}, },
"@hypnosphi/create-react-context": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@hypnosphi/create-react-context/-/create-react-context-0.3.1.tgz",
"integrity": "sha512-V1klUed202XahrWJLLOT3EXNeCpFHCcJntdFGI15ntCwau+jfT386w7OFTMaCqOgXUH1fa0w/I1oZs+i/Rfr0A==",
"requires": {
"gud": "^1.0.0",
"warning": "^4.0.3"
}
},
"@istanbuljs/load-nyc-config": { "@istanbuljs/load-nyc-config": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@@ -26715,6 +26850,12 @@
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
}, },
"bootstrap": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.0.tgz",
"integrity": "sha512-Io55IuQY3kydzHtbGvQya3H+KorS/M9rSNyfCGCg9WZ4pyT/lCxIlpJgG1GXW/PswzC84Tr2fBYi+7+jFVQQBw==",
"requires": {}
},
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -28239,6 +28380,14 @@
"utila": "~0.4" "utila": "~0.4"
} }
}, },
"dom-helpers": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz",
"integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==",
"requires": {
"@babel/runtime": "^7.1.2"
}
},
"dom-serializer": { "dom-serializer": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
@@ -30023,6 +30172,11 @@
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=",
"optional": true "optional": true
}, },
"gud": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
"integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw=="
},
"gzip-size": { "gzip-size": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz",
@@ -32433,6 +32587,12 @@
} }
} }
}, },
"jquery": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
"integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==",
"peer": true
},
"js-tokens": { "js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -33903,6 +34063,11 @@
"ts-pnp": "^1.1.6" "ts-pnp": "^1.1.6"
} }
}, },
"popper.js": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
"integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ=="
},
"portfinder": { "portfinder": {
"version": "1.0.28", "version": "1.0.28",
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",
@@ -35749,6 +35914,25 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
}, },
"react-lifecycles-compat": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
"react-popper": {
"version": "1.3.11",
"resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.11.tgz",
"integrity": "sha512-VSA/bS+pSndSF2fiasHK/PTEEAyOpX60+H5EPAjoArr8JGm+oihu4UbrqcEBpQibJxBVCpYyjAX7abJ+7DoYVg==",
"requires": {
"@babel/runtime": "^7.1.2",
"@hypnosphi/create-react-context": "^0.3.1",
"deep-equal": "^1.1.1",
"popper.js": "^1.14.4",
"prop-types": "^15.6.1",
"typed-styles": "^0.0.7",
"warning": "^4.0.2"
}
},
"react-refresh": { "react-refresh": {
"version": "0.8.3", "version": "0.8.3",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz",
@@ -35852,6 +36036,39 @@
} }
} }
}, },
"react-transition-group": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz",
"integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==",
"requires": {
"dom-helpers": "^3.4.0",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2",
"react-lifecycles-compat": "^3.0.4"
}
},
"reactstrap": {
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-8.10.0.tgz",
"integrity": "sha512-MsFUB/fRZj6Orf8Mxc93iYuAs+9ngnFmy2cfYlzkmc4vi5oM4u6ziY/DsO71lDG3cotxHRyS3Flr51cuYv+IEQ==",
"requires": {
"@babel/runtime": "^7.12.5",
"classnames": "^2.2.3",
"prop-types": "^15.5.8",
"react-popper": "^1.3.6",
"react-transition-group": "^2.3.1"
},
"dependencies": {
"@babel/runtime": {
"version": "7.15.4",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz",
"integrity": "sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==",
"requires": {
"regenerator-runtime": "^0.13.4"
}
}
}
},
"read-pkg": { "read-pkg": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
@@ -38145,6 +38362,11 @@
"mime-types": "~2.1.24" "mime-types": "~2.1.24"
} }
}, },
"typed-styles": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz",
"integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q=="
},
"typedarray": { "typedarray": {
"version": "0.0.6", "version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+2
View File
@@ -1,5 +1,7 @@
import Editor from "./view/Editor"; import Editor from "./view/Editor";
import globalStore, { GlobalContext } from "./store/global"; import globalStore, { GlobalContext } from "./store/global";
// ENHANCE: 替换掉antd组件库。。一堆莫名其妙的毛病
function App() { function App() {
return ( return (
<GlobalContext.Provider value={globalStore}> <GlobalContext.Provider value={globalStore}>
+18 -1
View File
@@ -4,7 +4,18 @@ import PopoverOnSvg from "../PopoverOnSvg";
import Styles from "./index.module.css"; import Styles from "./index.module.css";
const EditableContent = function ( const EditableContent = function (
{ children, title, initialValue, inputType, options, onChange, popoverProps }, {
children,
title,
initialValue,
inputType,
// ENHANCE: 去除这个prop,使用overlay代替
options,
// 选择模式要渲染的菜单,如果没有提供则根据options生成
overlay,
onChange,
popoverProps,
},
ref ref
) { ) {
const [inputValue, setInputValue] = useState(initialValue); const [inputValue, setInputValue] = useState(initialValue);
@@ -37,6 +48,9 @@ const EditableContent = function (
}, },
[inputValue, onChange] [inputValue, onChange]
); );
const handleHideContextMenu = useCallback(() => {
setPopoverVisible(false);
}, []);
const handleCancel = useCallback( const handleCancel = useCallback(
function () { function () {
setInputValue(initialValue); setInputValue(initialValue);
@@ -68,6 +82,9 @@ const EditableContent = function (
[inputValue, handleInput, handleConfirm, handleCancel] [inputValue, handleInput, handleConfirm, handleCancel]
); );
const renderSelectionPopover = useCallback(() => { const renderSelectionPopover = useCallback(() => {
if (overlay) {
return <div onClick={handleHideContextMenu}>{overlay}</div>;
}
return ( return (
<Menu className={Styles.menu}> <Menu className={Styles.menu}>
{(options || []) {(options || [])
+83 -63
View File
@@ -1,7 +1,11 @@
import { import {
FolderOpenOutlined, FolderOpenOutlined,
HighlightOutlined,
MenuUnfoldOutlined,
PlusOutlined, PlusOutlined,
RedoOutlined,
SaveOutlined, SaveOutlined,
UndoOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { Menu } from "antd"; import { Menu } from "antd";
import { action } from "mobx"; import { action } from "mobx";
@@ -13,69 +17,8 @@ import { findParagraphAndNotation } from "../util/editor";
import { go, runInWrappedAction, wrappedAction } from "../store/history"; import { go, runInWrappedAction, wrappedAction } from "../store/history";
const { SubMenu } = Menu; const { SubMenu } = Menu;
const fileMenu = (
<Menu>
<Menu.Item key="create" icon={<PlusOutlined />}>
新建
</Menu.Item>
<Menu.Item key="open" icon={<FolderOpenOutlined />}>
打开
</Menu.Item>
<Menu.Item key="save" icon={<SaveOutlined />}>
保存
</Menu.Item>
</Menu>
);
const editMenu = (
<Menu onClick={handleMenu}>
<Menu.Item key="undo">撤销</Menu.Item>
<Menu.Item key="redo">重做</Menu.Item>
<Menu.Item key="reset-title">添加段落</Menu.Item>
<Menu.Item key="reset-title">重置歌曲名称</Menu.Item>
<Menu.Item key="reset-authors">重置作者信息</Menu.Item>
</Menu>
);
const convertMenu = ( const handleEditMenu = wrappedAction(({ key }) => {
<Menu key="tone">
<SubMenu key="convert-to" title="转到...">
<Menu.Item key="convert-to-C">1 = C</Menu.Item>
<Menu.Item key="convert-to-D">1 = D</Menu.Item>
<Menu.Item key="convert-to-E">1 = E</Menu.Item>
<Menu.Item key="convert-to-F">1 = F</Menu.Item>
<Menu.Item key="convert-to-G">1 = G</Menu.Item>
<Menu.Item key="convert-to-A">1 = A</Menu.Item>
<Menu.Item key="convert-to-B">1 = B</Menu.Item>
<Menu.Item key="convert-to-bC">
1 = <sup></sup>C
</Menu.Item>
<Menu.Item key="convert-to-bD">
1 = <sup></sup>D
</Menu.Item>
<Menu.Item key="convert-to-bE">
1 = <sup></sup>E
</Menu.Item>
<Menu.Item key="convert-to-bF">
1 = <sup></sup>F
</Menu.Item>
<Menu.Item key="convert-to-bG">
1 = <sup></sup>G
</Menu.Item>
<Menu.Item key="convert-to-bA">
1 = <sup></sup>A
</Menu.Item>
<Menu.Item key="convert-to-bB">
1 = <sup></sup>B
</Menu.Item>
</SubMenu>
<Menu.Item key="convert-up">升高一个音</Menu.Item>
<Menu.Item key="convert-down">降低一个音</Menu.Item>
<Menu.Item key="convert-up8">升高一个八度</Menu.Item>
<Menu.Item key="convert-down8">降低一个八度</Menu.Item>
</Menu>
);
function handleMenu({ key }) {
switch (key) { switch (key) {
case "undo": case "undo":
go(-1); go(-1);
@@ -96,7 +39,11 @@ function handleMenu({ key }) {
]; ];
break; break;
} }
} });
const handleConvertMenu = wrappedAction(() => {
console.log(111);
});
const handleClick = wrappedAction((ev) => { const handleClick = wrappedAction((ev) => {
if (state.shouldNotationBlurAfterClick) { if (state.shouldNotationBlurAfterClick) {
@@ -217,4 +164,77 @@ const handleKeyPress = wrappedAction((ev) => {
} }
}); });
const fileMenu = (
<Menu>
<Menu.Item key="create" icon={<PlusOutlined />}>
新建
</Menu.Item>
<Menu.Item key="open" icon={<FolderOpenOutlined />}>
打开
</Menu.Item>
<Menu.Item key="save" icon={<SaveOutlined />}>
保存
</Menu.Item>
</Menu>
);
const editMenu = (
<Menu onClick={handleEditMenu}>
<Menu.Item key="undo" icon={<UndoOutlined />}>
撤销
</Menu.Item>
<Menu.Item key="redo" icon={<RedoOutlined />}>
重做
</Menu.Item>
<Menu.Item key="add-paragraph" icon={<MenuUnfoldOutlined />}>
添加段落
</Menu.Item>
<Menu.Item key="reset-title" icon={<HighlightOutlined />}>
重置歌曲名称
</Menu.Item>
<Menu.Item key="reset-authors" icon={<HighlightOutlined />}>
重置作者信息
</Menu.Item>
</Menu>
);
const convertMenu = (
<Menu key="tone" onClick={handleConvertMenu}>
<SubMenu key="convert-to" title="转到...">
<Menu.Item key="convert-to-C">1 = C</Menu.Item>
<Menu.Item key="convert-to-D">1 = D</Menu.Item>
<Menu.Item key="convert-to-E">1 = E</Menu.Item>
<Menu.Item key="convert-to-F">1 = F</Menu.Item>
<Menu.Item key="convert-to-G">1 = G</Menu.Item>
<Menu.Item key="convert-to-A">1 = A</Menu.Item>
<Menu.Item key="convert-to-B">1 = B</Menu.Item>
<Menu.Item key="convert-to-bC">
1 = <sup></sup>C
</Menu.Item>
<Menu.Item key="convert-to-bD">
1 = <sup></sup>D
</Menu.Item>
<Menu.Item key="convert-to-bE">
1 = <sup></sup>E
</Menu.Item>
<Menu.Item key="convert-to-bF">
1 = <sup></sup>F
</Menu.Item>
<Menu.Item key="convert-to-bG">
1 = <sup></sup>G
</Menu.Item>
<Menu.Item key="convert-to-bA">
1 = <sup></sup>A
</Menu.Item>
<Menu.Item key="convert-to-bB">
1 = <sup></sup>B
</Menu.Item>
</SubMenu>
<Menu.Item key="convert-up">升高一个音</Menu.Item>
<Menu.Item key="convert-down">降低一个音</Menu.Item>
<Menu.Item key="convert-up8">升高一个八度</Menu.Item>
<Menu.Item key="convert-down8">降低一个八度</Menu.Item>
</Menu>
);
export { fileMenu, editMenu, convertMenu, handleKeyPress, handleClick }; export { fileMenu, editMenu, convertMenu, handleKeyPress, handleClick };
+78 -24
View File
@@ -1,33 +1,36 @@
import { message } from "antd"; import {
ArrowDownOutlined,
ArrowUpOutlined,
FontColorsOutlined,
MinusOutlined,
PlusOutlined,
RadiusSettingOutlined,
StopOutlined,
} from "@ant-design/icons";
import { Menu, message } from "antd";
import state from "../store/state"; import state from "../store/state";
import { findParagraphAndNotation } from "../util/editor"; import { findParagraphAndNotation } from "../util/editor";
import { isNote } from "../util/notation"; import { isNote } from "../util/notation";
import { runInWrappedAction, wrappedAction } from "../store/history"; import { runInWrappedAction, wrappedAction } from "../store/history";
const getNotationContextItems = (notation, paragraph) => { const getNotationContextMenu = (notation, paragraph) => {
const hasTie = notation.tieTo; const hasTie = notation.tieTo;
return [ const handleMenu = wrappedAction(function ({ key }) {
{ switch (key) {
key: "octaveUp", case "octave-up":
text: "升高一个八度",
visible: isNote(notation),
onClick: wrappedAction(() => {
notation.octave += 1; notation.octave += 1;
}), break;
}, case "octave-down":
{
key: "octaveDown",
text: "降低一个八度",
visible: isNote(notation),
onClick: wrappedAction(() => {
notation.octave -= 1; notation.octave -= 1;
}), break;
}, case "underline-add":
{ notation.underline += 1;
key: "tie", break;
text: notation.tieTo ? "删除连音线" : "从此处添加连音线到...", case "underline-remove":
visible: isNote(notation), notation.underline -= 1;
onClick: wrappedAction(() => { notation.underline = Math.max(0, notation.underline);
break;
case "tie":
if (!hasTie) { if (!hasTie) {
state.tieSourceKey = notation.key; state.tieSourceKey = notation.key;
return; return;
@@ -37,8 +40,59 @@ const getNotationContextItems = (notation, paragraph) => {
tieDesc.tieTo = null; tieDesc.tieTo = null;
} }
notation.tieTo = null; notation.tieTo = null;
break;
case "break-underline":
notation.breakUnderline = !notation.breakUnderline;
break;
}
});
return (
<Menu onClick={handleMenu}>
<Menu.Item key="octave-up" icon={<ArrowUpOutlined />}>
升高一个八度8
</Menu.Item>
<Menu.Item key="octave-down" icon={<ArrowDownOutlined />}>
降低一个八度Shift + 8
</Menu.Item>
<Menu.Item key="underline-add" icon={<PlusOutlined />}>
添加增减时线U
</Menu.Item>
<Menu.Item key="underline-remove" icon={<MinusOutlined />}>
减少增减时线Shift + U
</Menu.Item>
<Menu.Item key="break-underline" icon={<StopOutlined />}>
{notation.breakUnderline ? "在此处延续增减时线" : "在此处打断增减时线"}
</Menu.Item>
<Menu.Item key="tie" icon={<RadiusSettingOutlined />}>
{notation.tieTo ? "删除连音线" : "从此处添加连音线到..."}
</Menu.Item>
</Menu>
);
return [
{
key: "octaveUp",
text: "升高一个八度(8",
visible: isNote(notation),
onClick: wrappedAction(() => {
notation.octave += 1;
}), }),
}, },
{
key: "octaveDown",
text: "降低一个八度(Shift + 8",
visible: isNote(notation),
onClick: wrappedAction(() => {
notation.octave -= 1;
}),
},
{
key: "tie",
text: notation.tieTo ? "删除连音线" : "从此处添加连音线到...",
visible: isNote(notation),
onClick: wrappedAction(() => {}),
},
{ {
key: "break-underline", key: "break-underline",
text: notation.breakUnderline text: notation.breakUnderline
@@ -64,7 +118,7 @@ const getNotationContextItems = (notation, paragraph) => {
]; ];
}; };
// ENHANCE: 改变<EditableContent>组件这个反人类的回调执行 // ENHANCE: 使用EditableContent组件的overlay属性传入菜单
// 执行选项回调。。 // 执行选项回调。。
function handleNotationContext(options, key) { function handleNotationContext(options, key) {
runInWrappedAction(() => (state.shouldNotationBlurAfterClick = false)); runInWrappedAction(() => (state.shouldNotationBlurAfterClick = false));
@@ -72,4 +126,4 @@ function handleNotationContext(options, key) {
callback && callback(); callback && callback();
} }
export { getNotationContextItems, handleNotationContext }; export { getNotationContextMenu, handleNotationContext };
+2 -10
View File
@@ -11,8 +11,8 @@ import store from "../store/global";
import { go, runInWrappedAction, wrappedAction } from "../store/history"; import { go, runInWrappedAction, wrappedAction } from "../store/history";
function getParagraphMenuOptions(paragraph) { function getParagraphMenuOptions(paragraph) {
return [];
const handleMenu = wrappedAction(({ key }) => { const handleMenu = wrappedAction(({ key }) => {
console.log(1111111111, key);
switch (key) { switch (key) {
case "justify-auto": case "justify-auto":
paragraph.alignJustify = null; paragraph.alignJustify = null;
@@ -46,12 +46,4 @@ function getParagraphMenuOptions(paragraph) {
); );
} }
// ENHANCE: 同notation的菜单项处理函数,改改改 export { getParagraphMenuOptions };
// TODO: working 现在就改!然后EditableContent应该直接传入antd的菜单项
_function handleSelectParagraphMenu(options, key) {
runInWrappedAction(() => (state.shouldNotationBlurAfterClick = false));
const callback = options.find((ops) => ops.key === key)?.onClick;
callback && callback();
}
export { getParagraphMenuOptions, handleSelectParagraphMenu };
+10 -1
View File
@@ -88,5 +88,14 @@ function runInWrappedAction(func) {
executeAction.call(this, func); executeAction.call(this, func);
}); });
} }
// 不需要记录历史记录的时候直接使用原action
const unwrappedAction = action;
const runInUnwrappedAction = runInAction;
export { go, wrappedAction, runInWrappedAction }; export {
go,
wrappedAction,
runInWrappedAction,
unwrappedAction,
runInUnwrappedAction,
};
+72 -3
View File
@@ -1,6 +1,75 @@
function ConfigModal({ visible, onVisibleChange }) { import { Button, Form, Input, InputNumber, Modal } from "antd";
return null; import { observer } from "mobx-react-lite";
import { useCallback } from "react";
import store from "../../store/global";
import { unwrappedAction, wrappedAction } from "../../store/history";
const ImmediateNumberConfigInput = observer(({ propertyName, ...props }) => {
const handleChange = unwrappedAction((ev) => {
const v = Number(ev.target.value) | 0;
store[propertyName] = v;
});
return (
<Input value={store[propertyName]} onChange={handleChange} {...props} />
);
});
function ImmediateNumberConfigItem({ propertyName, label, ...props }) {
return (
<Form.Item label={label} {...props}>
<ImmediateNumberConfigInput propertyName={propertyName} />
</Form.Item>
);
}
function ConfigModal({ visible, onVisibleChange }) {
const handleClose = useCallback(() => {
onVisibleChange?.call(null, false);
}, [onVisibleChange]);
return (
<Modal
visible={visible}
title="配置"
onCancel={handleClose}
footer={
<Button key="ok" type="primary" onClick={handleClose}>
关闭
</Button>
}
>
<Form labelAlign="right" labelCol={{ span: 4 }}>
<Form.Item label="画布大小">
<Input.Group>
<ImmediateNumberConfigInput
propertyName="canvasWidth"
style={{ width: "45%", textAlign: "center" }}
/>
<Input
className="site-input-split"
style={{
width: "10%",
borderLeft: 0,
borderRight: 0,
textAlign: "center",
pointerEvents: "none",
}}
placeholder="x"
disabled
/>
<ImmediateNumberConfigInput
propertyName="canvasHeight"
style={{ width: "45%", textAlign: "center" }}
/>
</Input.Group>
</Form.Item>
<ImmediateNumberConfigItem
label="左右边距"
propertyName="marginHorizontal"
/>
</Form>
</Modal>
);
} }
// TODO: do this
export default ConfigModal; export default ConfigModal;
+24 -6
View File
@@ -2,12 +2,12 @@ import { Button, Dropdown } from "antd";
import { import {
EditOutlined, EditOutlined,
FileTextOutlined, FileTextOutlined,
QuestionOutlined,
RetweetOutlined, RetweetOutlined,
SettingOutlined, SettingOutlined,
SyncOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import store from "../../store/global"; import store from "../../store/global";
import P, { import P, {
calcParagraphAboveOffset, calcParagraphAboveOffset,
@@ -28,7 +28,8 @@ import Row from "../Row";
import Styles from "./index.module.css"; import Styles from "./index.module.css";
function Editor() { function Editor() {
const [isModalVisible, setIsModalVisible] = useState(false); const [isConfigModalVisible, setIsConfigModalVisible] = useState(false);
const [isHelpModalVisible, setIsHelpModalVisible] = useState(false);
const heightCache = []; const heightCache = [];
function accumulate(index) { function accumulate(index) {
const paragraphs = store.paragraphs || []; const paragraphs = store.paragraphs || [];
@@ -42,6 +43,12 @@ function Editor() {
height += calcParagraphAboveOffset(paragraphs[index]); height += calcParagraphAboveOffset(paragraphs[index]);
return height; return height;
} }
const handleShowConfigDialog = useCallback(() => {
setIsConfigModalVisible(true);
}, []);
const handleShowHelpDialog = useCallback(() => {
setIsHelpModalVisible(true);
}, []);
useEffect(() => { useEffect(() => {
document.addEventListener("keydown", handleKeyPress); document.addEventListener("keydown", handleKeyPress);
@@ -77,9 +84,20 @@ function Editor() {
转调 转调
</Button> </Button>
</Dropdown> </Dropdown>
<Button icon={<SettingOutlined />} type="text"> <Button
icon={<SettingOutlined />}
type="text"
onClick={handleShowConfigDialog}
>
配置 配置
</Button> </Button>
<Button
icon={<QuestionOutlined />}
type="text"
onClick={handleShowHelpDialog}
>
帮助
</Button>
</div> </div>
</div> </div>
<Canvas> <Canvas>
@@ -102,8 +120,8 @@ function Editor() {
</Row> </Row>
</Canvas> </Canvas>
<ConfigModal <ConfigModal
visible={isModalVisible} visible={isConfigModalVisible}
onVisibleChange={setIsModalVisible} onVisibleChange={setIsConfigModalVisible}
></ConfigModal> ></ConfigModal>
</div> </div>
); );
+3 -3
View File
@@ -13,7 +13,7 @@ const authorContextMenu = [
{ {
key: "create", key: "create",
text: "添加", text: "添加",
icon: <PlusOutlined style={{ color: "grey" }} />, icon: <PlusOutlined />,
onClick: () => { onClick: () => {
store.authors.push("作者信息"); store.authors.push("作者信息");
}, },
@@ -21,7 +21,7 @@ const authorContextMenu = [
{ {
key: "delete", key: "delete",
text: "删除", text: "删除",
icon: <DeleteOutlined style={{ color: "grey" }} />, icon: <DeleteOutlined />,
onClick: (index) => { onClick: (index) => {
store.authors.splice(index, 1); store.authors.splice(index, 1);
}, },
@@ -31,7 +31,7 @@ const blockContextMenu = [
{ {
key: "create", key: "create",
text: "添加作者信息", text: "添加作者信息",
icon: <PlusOutlined style={{ color: "grey" }} />, icon: <PlusOutlined />,
onClick: () => { onClick: () => {
store.authors.push("【记谱者】 记谱"); store.authors.push("【记谱者】 记谱");
}, },
+6
View File
@@ -0,0 +1,6 @@
function HelpModal({ visible, onVisibleChange }) {
return null;
}
// TODO: do this
export default HelpModal;
+3 -11
View File
@@ -4,15 +4,9 @@ import { observer } from "mobx-react-lite";
import EditableContent from "../../component/EditableContent"; import EditableContent from "../../component/EditableContent";
import state from "../../store/state"; import state from "../../store/state";
import store from "../../store/global"; import store from "../../store/global";
import P, { import P, { calcSubTextWidth } from "../../util/placement";
calcNotationPrefixOffset,
calcSubTextWidth,
} from "../../util/placement";
import { findParagraphAndNotation } from "../../util/editor"; import { findParagraphAndNotation } from "../../util/editor";
import { import { getNotationContextMenu } from "../../menu/notation";
getNotationContextItems,
handleNotationContext,
} from "../../menu/notation";
import { notations } from "../../util/notation"; import { notations } from "../../util/notation";
import { wrappedAction } from "../../store/history"; import { wrappedAction } from "../../store/history";
import Row from "../Row"; import Row from "../Row";
@@ -128,13 +122,11 @@ function Notation({ offsetX, notation, paragraph }) {
)); ));
}; };
const options = getNotationContextItems(notation, paragraph);
return ( return (
<EditableContent <EditableContent
inputType="select" inputType="select"
options={options}
popoverProps={{ trigger: "context" }} popoverProps={{ trigger: "context" }}
onChange={handleNotationContext.bind(null, options)} overlay={getNotationContextMenu(notation, paragraph)}
> >
<Row <Row
editable editable
+1 -2
View File
@@ -166,8 +166,7 @@ function Paragraph({ paragraph, offsetY, alignJustify }) {
return ( return (
<EditableContent <EditableContent
inputType="select" inputType="select"
options={getParagraphMenuOptions(paragraph)} overlay={getParagraphMenuOptions(paragraph)}
onChange={handleSelectParagraphMenu}
> >
<rect <rect
x={-P.xWidth / 2} x={-P.xWidth / 2}