feat: 添加配置面板
This commit is contained in:
Generated
+222
@@ -13,11 +13,13 @@
|
||||
"@testing-library/react": "^11.2.7",
|
||||
"@testing-library/user-event": "^12.8.3",
|
||||
"antd": "^4.16.13",
|
||||
"bootstrap": "^4.6.0",
|
||||
"mobx": "^6.3.3",
|
||||
"mobx-react-lite": "^3.2.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-scripts": "4.0.3",
|
||||
"reactstrap": "^8.10.0",
|
||||
"web-vitals": "^1.1.2"
|
||||
}
|
||||
},
|
||||
@@ -1928,6 +1930,19 @@
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz",
|
||||
"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": {
|
||||
"version": "1.1.0",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
@@ -7436,6 +7464,14 @@
|
||||
"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": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
|
||||
@@ -9810,6 +9846,11 @@
|
||||
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=",
|
||||
"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": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz",
|
||||
@@ -13090,6 +13131,12 @@
|
||||
"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": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
@@ -15003,6 +15050,16 @@
|
||||
"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": {
|
||||
"version": "1.0.28",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz",
|
||||
@@ -17483,6 +17562,48 @@
|
||||
"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": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
|
||||
@@ -20374,6 +20495,11 @@
|
||||
"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": {
|
||||
"version": "0.0.6",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "1.1.0",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
@@ -28239,6 +28380,14 @@
|
||||
"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": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
|
||||
@@ -30023,6 +30172,11 @@
|
||||
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=",
|
||||
"optional": true
|
||||
},
|
||||
"gud": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
|
||||
"integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw=="
|
||||
},
|
||||
"gzip-size": {
|
||||
"version": "5.1.1",
|
||||
"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": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
@@ -33903,6 +34063,11 @@
|
||||
"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": {
|
||||
"version": "1.0.28",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "0.8.3",
|
||||
"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": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
|
||||
@@ -38145,6 +38362,11 @@
|
||||
"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": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import Editor from "./view/Editor";
|
||||
import globalStore, { GlobalContext } from "./store/global";
|
||||
|
||||
// ENHANCE: 替换掉antd组件库。。一堆莫名其妙的毛病
|
||||
function App() {
|
||||
return (
|
||||
<GlobalContext.Provider value={globalStore}>
|
||||
|
||||
@@ -4,7 +4,18 @@ import PopoverOnSvg from "../PopoverOnSvg";
|
||||
import Styles from "./index.module.css";
|
||||
|
||||
const EditableContent = function (
|
||||
{ children, title, initialValue, inputType, options, onChange, popoverProps },
|
||||
{
|
||||
children,
|
||||
title,
|
||||
initialValue,
|
||||
inputType,
|
||||
// ENHANCE: 去除这个prop,使用overlay代替
|
||||
options,
|
||||
// 选择模式要渲染的菜单,如果没有提供则根据options生成
|
||||
overlay,
|
||||
onChange,
|
||||
popoverProps,
|
||||
},
|
||||
ref
|
||||
) {
|
||||
const [inputValue, setInputValue] = useState(initialValue);
|
||||
@@ -37,6 +48,9 @@ const EditableContent = function (
|
||||
},
|
||||
[inputValue, onChange]
|
||||
);
|
||||
const handleHideContextMenu = useCallback(() => {
|
||||
setPopoverVisible(false);
|
||||
}, []);
|
||||
const handleCancel = useCallback(
|
||||
function () {
|
||||
setInputValue(initialValue);
|
||||
@@ -68,6 +82,9 @@ const EditableContent = function (
|
||||
[inputValue, handleInput, handleConfirm, handleCancel]
|
||||
);
|
||||
const renderSelectionPopover = useCallback(() => {
|
||||
if (overlay) {
|
||||
return <div onClick={handleHideContextMenu}>{overlay}</div>;
|
||||
}
|
||||
return (
|
||||
<Menu className={Styles.menu}>
|
||||
{(options || [])
|
||||
|
||||
+83
-63
@@ -1,7 +1,11 @@
|
||||
import {
|
||||
FolderOpenOutlined,
|
||||
HighlightOutlined,
|
||||
MenuUnfoldOutlined,
|
||||
PlusOutlined,
|
||||
RedoOutlined,
|
||||
SaveOutlined,
|
||||
UndoOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { Menu } from "antd";
|
||||
import { action } from "mobx";
|
||||
@@ -13,69 +17,8 @@ import { findParagraphAndNotation } from "../util/editor";
|
||||
import { go, runInWrappedAction, wrappedAction } from "../store/history";
|
||||
|
||||
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 = (
|
||||
<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 }) {
|
||||
const handleEditMenu = wrappedAction(({ key }) => {
|
||||
switch (key) {
|
||||
case "undo":
|
||||
go(-1);
|
||||
@@ -96,7 +39,11 @@ function handleMenu({ key }) {
|
||||
];
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
const handleConvertMenu = wrappedAction(() => {
|
||||
|
||||
console.log(111);
|
||||
});
|
||||
|
||||
const handleClick = wrappedAction((ev) => {
|
||||
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 };
|
||||
|
||||
+78
-24
@@ -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 { findParagraphAndNotation } from "../util/editor";
|
||||
import { isNote } from "../util/notation";
|
||||
import { runInWrappedAction, wrappedAction } from "../store/history";
|
||||
|
||||
const getNotationContextItems = (notation, paragraph) => {
|
||||
const getNotationContextMenu = (notation, paragraph) => {
|
||||
const hasTie = notation.tieTo;
|
||||
return [
|
||||
{
|
||||
key: "octaveUp",
|
||||
text: "升高一个八度",
|
||||
visible: isNote(notation),
|
||||
onClick: wrappedAction(() => {
|
||||
const handleMenu = wrappedAction(function ({ key }) {
|
||||
switch (key) {
|
||||
case "octave-up":
|
||||
notation.octave += 1;
|
||||
}),
|
||||
},
|
||||
{
|
||||
key: "octaveDown",
|
||||
text: "降低一个八度",
|
||||
visible: isNote(notation),
|
||||
onClick: wrappedAction(() => {
|
||||
break;
|
||||
case "octave-down":
|
||||
notation.octave -= 1;
|
||||
}),
|
||||
},
|
||||
{
|
||||
key: "tie",
|
||||
text: notation.tieTo ? "删除连音线" : "从此处添加连音线到...",
|
||||
visible: isNote(notation),
|
||||
onClick: wrappedAction(() => {
|
||||
break;
|
||||
case "underline-add":
|
||||
notation.underline += 1;
|
||||
break;
|
||||
case "underline-remove":
|
||||
notation.underline -= 1;
|
||||
notation.underline = Math.max(0, notation.underline);
|
||||
break;
|
||||
case "tie":
|
||||
if (!hasTie) {
|
||||
state.tieSourceKey = notation.key;
|
||||
return;
|
||||
@@ -37,8 +40,59 @@ const getNotationContextItems = (notation, paragraph) => {
|
||||
tieDesc.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",
|
||||
text: notation.breakUnderline
|
||||
@@ -64,7 +118,7 @@ const getNotationContextItems = (notation, paragraph) => {
|
||||
];
|
||||
};
|
||||
|
||||
// ENHANCE: 改变<EditableContent>组件这个反人类的回调执行
|
||||
// ENHANCE: 使用EditableContent组件的overlay属性传入菜单
|
||||
// 执行选项回调。。
|
||||
function handleNotationContext(options, key) {
|
||||
runInWrappedAction(() => (state.shouldNotationBlurAfterClick = false));
|
||||
@@ -72,4 +126,4 @@ function handleNotationContext(options, key) {
|
||||
callback && callback();
|
||||
}
|
||||
|
||||
export { getNotationContextItems, handleNotationContext };
|
||||
export { getNotationContextMenu, handleNotationContext };
|
||||
|
||||
+2
-10
@@ -11,8 +11,8 @@ import store from "../store/global";
|
||||
import { go, runInWrappedAction, wrappedAction } from "../store/history";
|
||||
|
||||
function getParagraphMenuOptions(paragraph) {
|
||||
return [];
|
||||
const handleMenu = wrappedAction(({ key }) => {
|
||||
console.log(1111111111, key);
|
||||
switch (key) {
|
||||
case "justify-auto":
|
||||
paragraph.alignJustify = null;
|
||||
@@ -46,12 +46,4 @@ function getParagraphMenuOptions(paragraph) {
|
||||
);
|
||||
}
|
||||
|
||||
// ENHANCE: 同notation的菜单项处理函数,改改改
|
||||
// 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 };
|
||||
export { getParagraphMenuOptions };
|
||||
|
||||
+10
-1
@@ -88,5 +88,14 @@ function runInWrappedAction(func) {
|
||||
executeAction.call(this, func);
|
||||
});
|
||||
}
|
||||
// 不需要记录历史记录的时候直接使用原action
|
||||
const unwrappedAction = action;
|
||||
const runInUnwrappedAction = runInAction;
|
||||
|
||||
export { go, wrappedAction, runInWrappedAction };
|
||||
export {
|
||||
go,
|
||||
wrappedAction,
|
||||
runInWrappedAction,
|
||||
unwrappedAction,
|
||||
runInUnwrappedAction,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,75 @@
|
||||
function ConfigModal({ visible, onVisibleChange }) {
|
||||
return null;
|
||||
import { Button, Form, Input, InputNumber, Modal } from "antd";
|
||||
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;
|
||||
|
||||
@@ -2,12 +2,12 @@ import { Button, Dropdown } from "antd";
|
||||
import {
|
||||
EditOutlined,
|
||||
FileTextOutlined,
|
||||
QuestionOutlined,
|
||||
RetweetOutlined,
|
||||
SettingOutlined,
|
||||
SyncOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import store from "../../store/global";
|
||||
import P, {
|
||||
calcParagraphAboveOffset,
|
||||
@@ -28,7 +28,8 @@ import Row from "../Row";
|
||||
import Styles from "./index.module.css";
|
||||
|
||||
function Editor() {
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const [isConfigModalVisible, setIsConfigModalVisible] = useState(false);
|
||||
const [isHelpModalVisible, setIsHelpModalVisible] = useState(false);
|
||||
const heightCache = [];
|
||||
function accumulate(index) {
|
||||
const paragraphs = store.paragraphs || [];
|
||||
@@ -42,6 +43,12 @@ function Editor() {
|
||||
height += calcParagraphAboveOffset(paragraphs[index]);
|
||||
return height;
|
||||
}
|
||||
const handleShowConfigDialog = useCallback(() => {
|
||||
setIsConfigModalVisible(true);
|
||||
}, []);
|
||||
const handleShowHelpDialog = useCallback(() => {
|
||||
setIsHelpModalVisible(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener("keydown", handleKeyPress);
|
||||
@@ -77,9 +84,20 @@ function Editor() {
|
||||
转调
|
||||
</Button>
|
||||
</Dropdown>
|
||||
<Button icon={<SettingOutlined />} type="text">
|
||||
<Button
|
||||
icon={<SettingOutlined />}
|
||||
type="text"
|
||||
onClick={handleShowConfigDialog}
|
||||
>
|
||||
配置
|
||||
</Button>
|
||||
<Button
|
||||
icon={<QuestionOutlined />}
|
||||
type="text"
|
||||
onClick={handleShowHelpDialog}
|
||||
>
|
||||
帮助
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Canvas>
|
||||
@@ -102,8 +120,8 @@ function Editor() {
|
||||
</Row>
|
||||
</Canvas>
|
||||
<ConfigModal
|
||||
visible={isModalVisible}
|
||||
onVisibleChange={setIsModalVisible}
|
||||
visible={isConfigModalVisible}
|
||||
onVisibleChange={setIsConfigModalVisible}
|
||||
></ConfigModal>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -13,7 +13,7 @@ const authorContextMenu = [
|
||||
{
|
||||
key: "create",
|
||||
text: "添加",
|
||||
icon: <PlusOutlined style={{ color: "grey" }} />,
|
||||
icon: <PlusOutlined />,
|
||||
onClick: () => {
|
||||
store.authors.push("作者信息");
|
||||
},
|
||||
@@ -21,7 +21,7 @@ const authorContextMenu = [
|
||||
{
|
||||
key: "delete",
|
||||
text: "删除",
|
||||
icon: <DeleteOutlined style={{ color: "grey" }} />,
|
||||
icon: <DeleteOutlined />,
|
||||
onClick: (index) => {
|
||||
store.authors.splice(index, 1);
|
||||
},
|
||||
@@ -31,7 +31,7 @@ const blockContextMenu = [
|
||||
{
|
||||
key: "create",
|
||||
text: "添加作者信息",
|
||||
icon: <PlusOutlined style={{ color: "grey" }} />,
|
||||
icon: <PlusOutlined />,
|
||||
onClick: () => {
|
||||
store.authors.push("【记谱者】 记谱");
|
||||
},
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
function HelpModal({ visible, onVisibleChange }) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: do this
|
||||
export default HelpModal;
|
||||
@@ -4,15 +4,9 @@ import { observer } from "mobx-react-lite";
|
||||
import EditableContent from "../../component/EditableContent";
|
||||
import state from "../../store/state";
|
||||
import store from "../../store/global";
|
||||
import P, {
|
||||
calcNotationPrefixOffset,
|
||||
calcSubTextWidth,
|
||||
} from "../../util/placement";
|
||||
import P, { calcSubTextWidth } from "../../util/placement";
|
||||
import { findParagraphAndNotation } from "../../util/editor";
|
||||
import {
|
||||
getNotationContextItems,
|
||||
handleNotationContext,
|
||||
} from "../../menu/notation";
|
||||
import { getNotationContextMenu } from "../../menu/notation";
|
||||
import { notations } from "../../util/notation";
|
||||
import { wrappedAction } from "../../store/history";
|
||||
import Row from "../Row";
|
||||
@@ -128,13 +122,11 @@ function Notation({ offsetX, notation, paragraph }) {
|
||||
));
|
||||
};
|
||||
|
||||
const options = getNotationContextItems(notation, paragraph);
|
||||
return (
|
||||
<EditableContent
|
||||
inputType="select"
|
||||
options={options}
|
||||
popoverProps={{ trigger: "context" }}
|
||||
onChange={handleNotationContext.bind(null, options)}
|
||||
overlay={getNotationContextMenu(notation, paragraph)}
|
||||
>
|
||||
<Row
|
||||
editable
|
||||
|
||||
@@ -166,8 +166,7 @@ function Paragraph({ paragraph, offsetY, alignJustify }) {
|
||||
return (
|
||||
<EditableContent
|
||||
inputType="select"
|
||||
options={getParagraphMenuOptions(paragraph)}
|
||||
onChange={handleSelectParagraphMenu}
|
||||
overlay={getParagraphMenuOptions(paragraph)}
|
||||
>
|
||||
<rect
|
||||
x={-P.xWidth / 2}
|
||||
|
||||
Reference in New Issue
Block a user