feat: 添加文件菜单
This commit is contained in:
Generated
+11
-222
@@ -13,13 +13,12 @@
|
|||||||
"@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",
|
"dom-to-image": "^2.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"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1930,19 +1929,6 @@
|
|||||||
"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",
|
||||||
@@ -5543,19 +5529,6 @@
|
|||||||
"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",
|
||||||
@@ -7464,14 +7437,6 @@
|
|||||||
"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",
|
||||||
@@ -7485,6 +7450,11 @@
|
|||||||
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
|
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dom-to-image": {
|
||||||
|
"version": "2.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dom-to-image/-/dom-to-image-2.6.0.tgz",
|
||||||
|
"integrity": "sha1-ilA2CAiMh7HCL5A0rgMuGJiVWGc="
|
||||||
|
},
|
||||||
"node_modules/domain-browser": {
|
"node_modules/domain-browser": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
|
||||||
@@ -9846,11 +9816,6 @@
|
|||||||
"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",
|
||||||
@@ -13131,12 +13096,6 @@
|
|||||||
"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",
|
||||||
@@ -15050,16 +15009,6 @@
|
|||||||
"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",
|
||||||
@@ -17411,28 +17360,6 @@
|
|||||||
"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",
|
||||||
@@ -17562,48 +17489,6 @@
|
|||||||
"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",
|
||||||
@@ -20495,11 +20380,6 @@
|
|||||||
"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",
|
||||||
@@ -24085,15 +23965,6 @@
|
|||||||
"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",
|
||||||
@@ -26850,12 +26721,6 @@
|
|||||||
"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",
|
||||||
@@ -28380,14 +28245,6 @@
|
|||||||
"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",
|
||||||
@@ -28398,6 +28255,11 @@
|
|||||||
"entities": "^2.0.0"
|
"entities": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dom-to-image": {
|
||||||
|
"version": "2.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dom-to-image/-/dom-to-image-2.6.0.tgz",
|
||||||
|
"integrity": "sha1-ilA2CAiMh7HCL5A0rgMuGJiVWGc="
|
||||||
|
},
|
||||||
"domain-browser": {
|
"domain-browser": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
|
||||||
@@ -30172,11 +30034,6 @@
|
|||||||
"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",
|
||||||
@@ -32587,12 +32444,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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",
|
||||||
@@ -34063,11 +33914,6 @@
|
|||||||
"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",
|
||||||
@@ -35914,25 +35760,6 @@
|
|||||||
"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",
|
||||||
@@ -36036,39 +35863,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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",
|
||||||
@@ -38362,11 +38156,6 @@
|
|||||||
"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",
|
||||||
|
|||||||
+52
-3
@@ -1,3 +1,4 @@
|
|||||||
|
import { Button, Menu, Upload } from "antd";
|
||||||
import {
|
import {
|
||||||
FolderOpenOutlined,
|
FolderOpenOutlined,
|
||||||
HighlightOutlined,
|
HighlightOutlined,
|
||||||
@@ -7,16 +8,32 @@ import {
|
|||||||
SaveOutlined,
|
SaveOutlined,
|
||||||
UndoOutlined,
|
UndoOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { Menu } from "antd";
|
|
||||||
import state from "../store/state";
|
import state from "../store/state";
|
||||||
import store from "../store/global";
|
import store from "../store/global";
|
||||||
import { createNotation, isNote, notations as N } from "../util/notation";
|
import { createNotation, isNote, notations as N } from "../util/notation";
|
||||||
import { createParagraph } from "../util/paragraph";
|
import { createParagraph } from "../util/paragraph";
|
||||||
|
import { exportFile, loadFile, saveFile } from "../util/file";
|
||||||
import { findParagraphAndNotation } from "../util/editor";
|
import { findParagraphAndNotation } from "../util/editor";
|
||||||
import { go, runInWrappedAction, wrappedAction } from "../store/history";
|
import {
|
||||||
|
go,
|
||||||
|
runInWrappedAction,
|
||||||
|
unwrappedAction,
|
||||||
|
wrappedAction,
|
||||||
|
} from "../store/history";
|
||||||
|
|
||||||
const { SubMenu } = Menu;
|
const { SubMenu } = Menu;
|
||||||
|
|
||||||
|
const handleOpenFile = unwrappedAction((ev) => {
|
||||||
|
// TODO: working
|
||||||
|
const file = ev.target.files[0];
|
||||||
|
loadFile(file);
|
||||||
|
});
|
||||||
|
const handleSaveFile = unwrappedAction(() => {
|
||||||
|
saveFile();
|
||||||
|
});
|
||||||
|
const handleExportFile = () => {
|
||||||
|
exportFile();
|
||||||
|
};
|
||||||
const handleEditMenu = wrappedAction(({ key }) => {
|
const handleEditMenu = wrappedAction(({ key }) => {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case "undo":
|
case "undo":
|
||||||
@@ -221,6 +238,20 @@ const handleKeyPress = wrappedAction((ev) => {
|
|||||||
case inputKey === "z" && ctrl && shift:
|
case inputKey === "z" && ctrl && shift:
|
||||||
go(1);
|
go(1);
|
||||||
break;
|
break;
|
||||||
|
case inputKey === "s" && ctrl && !shift: {
|
||||||
|
ev.preventDefault();
|
||||||
|
saveFile();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case inputKey === "o" && ctrl && !shift: {
|
||||||
|
ev.preventDefault();
|
||||||
|
const input = document.createElement("input");
|
||||||
|
input.type = "file";
|
||||||
|
input.accept = ".json";
|
||||||
|
input.onchange = handleOpenFile;
|
||||||
|
input.click();
|
||||||
|
break;
|
||||||
|
}
|
||||||
case inputKey === "?" && !ctrl: {
|
case inputKey === "?" && !ctrl: {
|
||||||
if (state.helpDialogVisible) {
|
if (state.helpDialogVisible) {
|
||||||
state.helpDialogVisible = false;
|
state.helpDialogVisible = false;
|
||||||
@@ -240,10 +271,28 @@ const fileMenu = (
|
|||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item key="open" icon={<FolderOpenOutlined />}>
|
<Menu.Item key="open" icon={<FolderOpenOutlined />}>
|
||||||
打开
|
打开
|
||||||
|
<input
|
||||||
|
onChange={handleOpenFile}
|
||||||
|
accept=".json"
|
||||||
|
type="file"
|
||||||
|
title=""
|
||||||
|
style={{
|
||||||
|
opacity: 0,
|
||||||
|
cursor: "pointer",
|
||||||
|
width: "100%",
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
height: "100%",
|
||||||
|
position: "absolute",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item key="save" icon={<SaveOutlined />}>
|
<Menu.Item key="save" icon={<SaveOutlined />} onClick={handleSaveFile}>
|
||||||
保存
|
保存
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
<Menu.Item key="export" icon={<SaveOutlined />} onClick={handleExportFile}>
|
||||||
|
导出为图片
|
||||||
|
</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
const editMenu = (
|
const editMenu = (
|
||||||
|
|||||||
@@ -52,6 +52,11 @@ function go(span) {
|
|||||||
Object.assign(globalStore, storeHistory[newCursor]);
|
Object.assign(globalStore, storeHistory[newCursor]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clearHistory() {
|
||||||
|
storeHistory.length = 0;
|
||||||
|
cursor = -1;
|
||||||
|
}
|
||||||
|
|
||||||
// 包装mobx的action以实现历史记录
|
// 包装mobx的action以实现历史记录
|
||||||
function executeAction(actionFunc, ...args) {
|
function executeAction(actionFunc, ...args) {
|
||||||
actionFunc.apply(this, args);
|
actionFunc.apply(this, args);
|
||||||
@@ -88,12 +93,14 @@ function runInWrappedAction(func) {
|
|||||||
executeAction.call(this, func);
|
executeAction.call(this, func);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 不需要记录历史记录的时候直接使用原action
|
// 不需要记录历史记录的时候直接使用原action
|
||||||
const unwrappedAction = action;
|
const unwrappedAction = action;
|
||||||
const runInUnwrappedAction = runInAction;
|
const runInUnwrappedAction = runInAction;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
go,
|
go,
|
||||||
|
clearHistory,
|
||||||
wrappedAction,
|
wrappedAction,
|
||||||
runInWrappedAction,
|
runInWrappedAction,
|
||||||
unwrappedAction,
|
unwrappedAction,
|
||||||
|
|||||||
@@ -14,6 +14,12 @@ let globalState = observable({
|
|||||||
helpDialogVisible: false,
|
helpDialogVisible: false,
|
||||||
// 配置项弹窗是否可见
|
// 配置项弹窗是否可见
|
||||||
configDialogVisible: false,
|
configDialogVisible: false,
|
||||||
|
// 持久化的文件
|
||||||
|
file: null,
|
||||||
|
// ref
|
||||||
|
refs: {
|
||||||
|
svg: null,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default globalState;
|
export default globalState;
|
||||||
|
|||||||
@@ -0,0 +1,799 @@
|
|||||||
|
// COPIED FROM: https://github.com/tsayen/dom-to-image
|
||||||
|
// 由于原仓库不支持高dpi改良了一下。上游合并和可引入库依赖
|
||||||
|
|
||||||
|
var util = newUtil();
|
||||||
|
var inliner = newInliner();
|
||||||
|
var fontFaces = newFontFaces();
|
||||||
|
var images = newImages();
|
||||||
|
var dpi = window.devicePixelRatio || 1;
|
||||||
|
|
||||||
|
// Default impl options
|
||||||
|
var defaultOptions = {
|
||||||
|
// Default is to fail on error, no placeholder
|
||||||
|
imagePlaceholder: undefined,
|
||||||
|
// Default cache bust is false, it will use the cache
|
||||||
|
cacheBust: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
var domtoimage = {
|
||||||
|
toSvg: toSvg,
|
||||||
|
toPng: toPng,
|
||||||
|
toJpeg: toJpeg,
|
||||||
|
toBlob: toBlob,
|
||||||
|
toPixelData: toPixelData,
|
||||||
|
impl: {
|
||||||
|
fontFaces: fontFaces,
|
||||||
|
images: images,
|
||||||
|
util: util,
|
||||||
|
inliner: inliner,
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Node} node - The DOM Node object to render
|
||||||
|
* @param {Object} options - Rendering options
|
||||||
|
* @param {Function} options.filter - Should return true if passed node should be included in the output
|
||||||
|
* (excluding node means excluding it's children as well). Not called on the root node.
|
||||||
|
* @param {String} options.bgcolor - color for the background, any valid CSS color value.
|
||||||
|
* @param {Number} options.width - width to be applied to node before rendering.
|
||||||
|
* @param {Number} options.height - height to be applied to node before rendering.
|
||||||
|
* @param {Object} options.style - an object whose properties to be copied to node's style before rendering.
|
||||||
|
* @param {Number} options.quality - a Number between 0 and 1 indicating image quality (applicable to JPEG only),
|
||||||
|
defaults to 1.0.
|
||||||
|
* @param {String} options.imagePlaceholder - dataURL to use as a placeholder for failed images, default behaviour is to fail fast on images we can't fetch
|
||||||
|
* @param {Boolean} options.cacheBust - set to true to cache bust by appending the time to the request url
|
||||||
|
* @return {Promise} - A promise that is fulfilled with a SVG image data URL
|
||||||
|
* */
|
||||||
|
function toSvg(node, options) {
|
||||||
|
options = options || {};
|
||||||
|
copyOptions(options);
|
||||||
|
return Promise.resolve(node)
|
||||||
|
.then(function (node) {
|
||||||
|
return cloneNode(node, options.filter, true);
|
||||||
|
})
|
||||||
|
.then(embedFonts)
|
||||||
|
.then(inlineImages)
|
||||||
|
.then(applyOptions)
|
||||||
|
.then(function (clone) {
|
||||||
|
return makeSvgDataUri(
|
||||||
|
clone,
|
||||||
|
(options.width || util.width(node)) * dpi,
|
||||||
|
options.height || util.height(node) * dpi
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
function applyOptions(clone) {
|
||||||
|
if (options.bgcolor) clone.style.backgroundColor = options.bgcolor;
|
||||||
|
|
||||||
|
if (options.width) clone.style.width = options.width + "px";
|
||||||
|
if (options.height) clone.style.height = options.height + "px";
|
||||||
|
|
||||||
|
if (options.style)
|
||||||
|
Object.keys(options.style).forEach(function (property) {
|
||||||
|
clone.style[property] = options.style[property];
|
||||||
|
});
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Node} node - The DOM Node object to render
|
||||||
|
* @param {Object} options - Rendering options, @see {@link toSvg}
|
||||||
|
* @return {Promise} - A promise that is fulfilled with a Uint8Array containing RGBA pixel data.
|
||||||
|
* */
|
||||||
|
function toPixelData(node, options) {
|
||||||
|
return draw(node, options || {}).then(function (canvas) {
|
||||||
|
return canvas
|
||||||
|
.getContext("2d")
|
||||||
|
.getImageData(0, 0, util.width(node) * dpi, util.height(node) * dpi).data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Node} node - The DOM Node object to render
|
||||||
|
* @param {Object} options - Rendering options, @see {@link toSvg}
|
||||||
|
* @return {Promise} - A promise that is fulfilled with a PNG image data URL
|
||||||
|
* */
|
||||||
|
function toPng(node, options) {
|
||||||
|
return draw(node, options || {}).then(function (canvas) {
|
||||||
|
return canvas.toDataURL();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Node} node - The DOM Node object to render
|
||||||
|
* @param {Object} options - Rendering options, @see {@link toSvg}
|
||||||
|
* @return {Promise} - A promise that is fulfilled with a JPEG image data URL
|
||||||
|
* */
|
||||||
|
function toJpeg(node, options) {
|
||||||
|
options = options || {};
|
||||||
|
return draw(node, options).then(function (canvas) {
|
||||||
|
return canvas.toDataURL("image/jpeg", options.quality || 1.0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Node} node - The DOM Node object to render
|
||||||
|
* @param {Object} options - Rendering options, @see {@link toSvg}
|
||||||
|
* @return {Promise} - A promise that is fulfilled with a PNG image blob
|
||||||
|
* */
|
||||||
|
function toBlob(node, options) {
|
||||||
|
return draw(node, options || {}).then(util.canvasToBlob);
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyOptions(options) {
|
||||||
|
// Copy options to impl options for use in impl
|
||||||
|
if (typeof options.imagePlaceholder === "undefined") {
|
||||||
|
domtoimage.impl.options.imagePlaceholder = defaultOptions.imagePlaceholder;
|
||||||
|
} else {
|
||||||
|
domtoimage.impl.options.imagePlaceholder = options.imagePlaceholder;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof options.cacheBust === "undefined") {
|
||||||
|
domtoimage.impl.options.cacheBust = defaultOptions.cacheBust;
|
||||||
|
} else {
|
||||||
|
domtoimage.impl.options.cacheBust = options.cacheBust;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw(domNode, options) {
|
||||||
|
return toSvg(domNode, options)
|
||||||
|
.then(util.makeImage)
|
||||||
|
.then(util.delay(100))
|
||||||
|
.then(function (image) {
|
||||||
|
var canvas = newCanvas(domNode);
|
||||||
|
canvas.getContext("2d").drawImage(image, 0, 0);
|
||||||
|
return canvas;
|
||||||
|
});
|
||||||
|
|
||||||
|
function newCanvas(domNode) {
|
||||||
|
var canvas = document.createElement("canvas");
|
||||||
|
var width = options.width || util.width(domNode);
|
||||||
|
var height = options.height || util.height(domNode);
|
||||||
|
canvas.width = width * dpi;
|
||||||
|
canvas.height = height * dpi;
|
||||||
|
canvas.style.width = width + "px";
|
||||||
|
canvas.style.height = height + "px";
|
||||||
|
|
||||||
|
canvas.getContext("2d").scale(dpi, dpi);
|
||||||
|
if (options.bgcolor) {
|
||||||
|
var ctx = canvas.getContext("2d");
|
||||||
|
ctx.fillStyle = options.bgcolor;
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
return canvas;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cloneNode(node, filter, root) {
|
||||||
|
if (!root && filter && !filter(node)) return Promise.resolve();
|
||||||
|
|
||||||
|
return Promise.resolve(node)
|
||||||
|
.then(makeNodeCopy)
|
||||||
|
.then(function (clone) {
|
||||||
|
return cloneChildren(node, clone, filter);
|
||||||
|
})
|
||||||
|
.then(function (clone) {
|
||||||
|
return processClone(node, clone);
|
||||||
|
});
|
||||||
|
|
||||||
|
function makeNodeCopy(node) {
|
||||||
|
if (node instanceof HTMLCanvasElement)
|
||||||
|
return util.makeImage(node.toDataURL());
|
||||||
|
return node.cloneNode(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cloneChildren(original, clone, filter) {
|
||||||
|
var children = original.childNodes;
|
||||||
|
if (children.length === 0) return Promise.resolve(clone);
|
||||||
|
|
||||||
|
return cloneChildrenInOrder(clone, util.asArray(children), filter).then(
|
||||||
|
function () {
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function cloneChildrenInOrder(parent, children, filter) {
|
||||||
|
var done = Promise.resolve();
|
||||||
|
children.forEach(function (child) {
|
||||||
|
done = done
|
||||||
|
.then(function () {
|
||||||
|
return cloneNode(child, filter);
|
||||||
|
})
|
||||||
|
.then(function (childClone) {
|
||||||
|
if (childClone) parent.appendChild(childClone);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function processClone(original, clone) {
|
||||||
|
if (!(clone instanceof Element)) return clone;
|
||||||
|
|
||||||
|
return Promise.resolve()
|
||||||
|
.then(cloneStyle)
|
||||||
|
.then(clonePseudoElements)
|
||||||
|
.then(copyUserInput)
|
||||||
|
.then(fixSvg)
|
||||||
|
.then(function () {
|
||||||
|
return clone;
|
||||||
|
});
|
||||||
|
|
||||||
|
function cloneStyle() {
|
||||||
|
copyStyle(window.getComputedStyle(original), clone.style);
|
||||||
|
|
||||||
|
function copyStyle(source, target) {
|
||||||
|
if (source.cssText) target.cssText = source.cssText;
|
||||||
|
else copyProperties(source, target);
|
||||||
|
|
||||||
|
function copyProperties(source, target) {
|
||||||
|
util.asArray(source).forEach(function (name) {
|
||||||
|
target.setProperty(
|
||||||
|
name,
|
||||||
|
source.getPropertyValue(name),
|
||||||
|
source.getPropertyPriority(name)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clonePseudoElements() {
|
||||||
|
[":before", ":after"].forEach(function (element) {
|
||||||
|
clonePseudoElement(element);
|
||||||
|
});
|
||||||
|
|
||||||
|
function clonePseudoElement(element) {
|
||||||
|
var style = window.getComputedStyle(original, element);
|
||||||
|
var content = style.getPropertyValue("content");
|
||||||
|
|
||||||
|
if (content === "" || content === "none") return;
|
||||||
|
|
||||||
|
var className = util.uid();
|
||||||
|
clone.className = clone.className + " " + className;
|
||||||
|
var styleElement = document.createElement("style");
|
||||||
|
styleElement.appendChild(
|
||||||
|
formatPseudoElementStyle(className, element, style)
|
||||||
|
);
|
||||||
|
clone.appendChild(styleElement);
|
||||||
|
|
||||||
|
function formatPseudoElementStyle(className, element, style) {
|
||||||
|
var selector = "." + className + ":" + element;
|
||||||
|
var cssText = style.cssText
|
||||||
|
? formatCssText(style)
|
||||||
|
: formatCssProperties(style);
|
||||||
|
return document.createTextNode(selector + "{" + cssText + "}");
|
||||||
|
|
||||||
|
function formatCssText(style) {
|
||||||
|
var content = style.getPropertyValue("content");
|
||||||
|
return style.cssText + " content: " + content + ";";
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatCssProperties(style) {
|
||||||
|
return util.asArray(style).map(formatProperty).join("; ") + ";";
|
||||||
|
|
||||||
|
function formatProperty(name) {
|
||||||
|
return (
|
||||||
|
name +
|
||||||
|
": " +
|
||||||
|
style.getPropertyValue(name) +
|
||||||
|
(style.getPropertyPriority(name) ? " !important" : "")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyUserInput() {
|
||||||
|
if (original instanceof HTMLTextAreaElement)
|
||||||
|
clone.innerHTML = original.value;
|
||||||
|
if (original instanceof HTMLInputElement)
|
||||||
|
clone.setAttribute("value", original.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fixSvg() {
|
||||||
|
if (!(clone instanceof SVGElement)) return;
|
||||||
|
clone.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
||||||
|
|
||||||
|
if (!(clone instanceof SVGRectElement)) return;
|
||||||
|
["width", "height"].forEach(function (attribute) {
|
||||||
|
var value = clone.getAttribute(attribute);
|
||||||
|
if (!value) return;
|
||||||
|
|
||||||
|
clone.style.setProperty(attribute, value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function embedFonts(node) {
|
||||||
|
return fontFaces.resolveAll().then(function (cssText) {
|
||||||
|
var styleNode = document.createElement("style");
|
||||||
|
node.appendChild(styleNode);
|
||||||
|
styleNode.appendChild(document.createTextNode(cssText));
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function inlineImages(node) {
|
||||||
|
return images.inlineAll(node).then(function () {
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeSvgDataUri(node, width, height) {
|
||||||
|
return Promise.resolve(node)
|
||||||
|
.then(function (node) {
|
||||||
|
node.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
|
||||||
|
return new XMLSerializer().serializeToString(node);
|
||||||
|
})
|
||||||
|
.then(util.escapeXhtml)
|
||||||
|
.then(function (xhtml) {
|
||||||
|
return (
|
||||||
|
'<foreignObject x="0" y="0" width="100%" height="100%">' +
|
||||||
|
xhtml +
|
||||||
|
"</foreignObject>"
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.then(function (foreignObject) {
|
||||||
|
return (
|
||||||
|
'<svg xmlns="http://www.w3.org/2000/svg" width="' +
|
||||||
|
width +
|
||||||
|
'" height="' +
|
||||||
|
height +
|
||||||
|
'">' +
|
||||||
|
foreignObject +
|
||||||
|
"</svg>"
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.then(function (svg) {
|
||||||
|
return "data:image/svg+xml;charset=utf-8," + svg;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function newUtil() {
|
||||||
|
return {
|
||||||
|
escape: escape,
|
||||||
|
parseExtension: parseExtension,
|
||||||
|
mimeType: mimeType,
|
||||||
|
dataAsUrl: dataAsUrl,
|
||||||
|
isDataUrl: isDataUrl,
|
||||||
|
canvasToBlob: canvasToBlob,
|
||||||
|
resolveUrl: resolveUrl,
|
||||||
|
getAndEncode: getAndEncode,
|
||||||
|
uid: uid(),
|
||||||
|
delay: delay,
|
||||||
|
asArray: asArray,
|
||||||
|
escapeXhtml: escapeXhtml,
|
||||||
|
makeImage: makeImage,
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
};
|
||||||
|
|
||||||
|
function mimes() {
|
||||||
|
/*
|
||||||
|
* Only WOFF and EOT mime types for fonts are 'real'
|
||||||
|
* see http://www.iana.org/assignments/media-types/media-types.xhtml
|
||||||
|
*/
|
||||||
|
var WOFF = "application/font-woff";
|
||||||
|
var JPEG = "image/jpeg";
|
||||||
|
|
||||||
|
return {
|
||||||
|
woff: WOFF,
|
||||||
|
woff2: WOFF,
|
||||||
|
ttf: "application/font-truetype",
|
||||||
|
eot: "application/vnd.ms-fontobject",
|
||||||
|
png: "image/png",
|
||||||
|
jpg: JPEG,
|
||||||
|
jpeg: JPEG,
|
||||||
|
gif: "image/gif",
|
||||||
|
tiff: "image/tiff",
|
||||||
|
svg: "image/svg+xml",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseExtension(url) {
|
||||||
|
var match = /\.([^\.\/]*?)$/g.exec(url);
|
||||||
|
if (match) return match[1];
|
||||||
|
else return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function mimeType(url) {
|
||||||
|
var extension = parseExtension(url).toLowerCase();
|
||||||
|
return mimes()[extension] || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDataUrl(url) {
|
||||||
|
return url.search(/^(data:)/) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toBlob(canvas) {
|
||||||
|
return new Promise(function (resolve) {
|
||||||
|
var binaryString = window.atob(canvas.toDataURL().split(",")[1]);
|
||||||
|
var length = binaryString.length;
|
||||||
|
var binaryArray = new Uint8Array(length);
|
||||||
|
|
||||||
|
for (var i = 0; i < length; i++)
|
||||||
|
binaryArray[i] = binaryString.charCodeAt(i);
|
||||||
|
|
||||||
|
resolve(
|
||||||
|
new Blob([binaryArray], {
|
||||||
|
type: "image/png",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function canvasToBlob(canvas) {
|
||||||
|
if (canvas.toBlob)
|
||||||
|
return new Promise(function (resolve) {
|
||||||
|
canvas.toBlob(resolve);
|
||||||
|
});
|
||||||
|
|
||||||
|
return toBlob(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveUrl(url, baseUrl) {
|
||||||
|
var doc = document.implementation.createHTMLDocument();
|
||||||
|
var base = doc.createElement("base");
|
||||||
|
doc.head.appendChild(base);
|
||||||
|
var a = doc.createElement("a");
|
||||||
|
doc.body.appendChild(a);
|
||||||
|
base.href = baseUrl;
|
||||||
|
a.href = url;
|
||||||
|
return a.href;
|
||||||
|
}
|
||||||
|
|
||||||
|
function uid() {
|
||||||
|
var index = 0;
|
||||||
|
|
||||||
|
return function () {
|
||||||
|
return "u" + fourRandomChars() + index++;
|
||||||
|
|
||||||
|
function fourRandomChars() {
|
||||||
|
/* see http://stackoverflow.com/a/6248722/2519373 */
|
||||||
|
return (
|
||||||
|
"0000" + ((Math.random() * Math.pow(36, 4)) << 0).toString(36)
|
||||||
|
).slice(-4);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeImage(uri) {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
var image = new Image();
|
||||||
|
image.onload = function () {
|
||||||
|
resolve(image);
|
||||||
|
};
|
||||||
|
image.onerror = reject;
|
||||||
|
image.src = uri;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAndEncode(url) {
|
||||||
|
var TIMEOUT = 30000;
|
||||||
|
if (domtoimage.impl.options.cacheBust) {
|
||||||
|
// Cache bypass so we dont have CORS issues with cached images
|
||||||
|
// Source: https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache
|
||||||
|
url += (/\?/.test(url) ? "&" : "?") + new Date().getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise(function (resolve) {
|
||||||
|
var request = new XMLHttpRequest();
|
||||||
|
|
||||||
|
request.onreadystatechange = done;
|
||||||
|
request.ontimeout = timeout;
|
||||||
|
request.responseType = "blob";
|
||||||
|
request.timeout = TIMEOUT;
|
||||||
|
request.open("GET", url, true);
|
||||||
|
request.send();
|
||||||
|
|
||||||
|
var placeholder;
|
||||||
|
if (domtoimage.impl.options.imagePlaceholder) {
|
||||||
|
var split = domtoimage.impl.options.imagePlaceholder.split(/,/);
|
||||||
|
if (split && split[1]) {
|
||||||
|
placeholder = split[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function done() {
|
||||||
|
if (request.readyState !== 4) return;
|
||||||
|
|
||||||
|
if (request.status !== 200) {
|
||||||
|
if (placeholder) {
|
||||||
|
resolve(placeholder);
|
||||||
|
} else {
|
||||||
|
fail(
|
||||||
|
"cannot fetch resource: " + url + ", status: " + request.status
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var encoder = new FileReader();
|
||||||
|
encoder.onloadend = function () {
|
||||||
|
var content = encoder.result.split(/,/)[1];
|
||||||
|
resolve(content);
|
||||||
|
};
|
||||||
|
encoder.readAsDataURL(request.response);
|
||||||
|
}
|
||||||
|
|
||||||
|
function timeout() {
|
||||||
|
if (placeholder) {
|
||||||
|
resolve(placeholder);
|
||||||
|
} else {
|
||||||
|
fail(
|
||||||
|
"timeout of " +
|
||||||
|
TIMEOUT +
|
||||||
|
"ms occured while fetching resource: " +
|
||||||
|
url
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fail(message) {
|
||||||
|
console.error(message);
|
||||||
|
resolve("");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function dataAsUrl(content, type) {
|
||||||
|
return "data:" + type + ";base64," + content;
|
||||||
|
}
|
||||||
|
|
||||||
|
function escape(string) {
|
||||||
|
return string.replace(/([.*+?^${}()|\[\]\/\\])/g, "\\$1");
|
||||||
|
}
|
||||||
|
|
||||||
|
function delay(ms) {
|
||||||
|
return function (arg) {
|
||||||
|
return new Promise(function (resolve) {
|
||||||
|
setTimeout(function () {
|
||||||
|
resolve(arg);
|
||||||
|
}, ms);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function asArray(arrayLike) {
|
||||||
|
var array = [];
|
||||||
|
var length = arrayLike.length;
|
||||||
|
for (var i = 0; i < length; i++) array.push(arrayLike[i]);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeXhtml(string) {
|
||||||
|
return string.replace(/#/g, "%23").replace(/\n/g, "%0A");
|
||||||
|
}
|
||||||
|
|
||||||
|
function width(node) {
|
||||||
|
var leftBorder = px(node, "border-left-width");
|
||||||
|
var rightBorder = px(node, "border-right-width");
|
||||||
|
return node.scrollWidth + leftBorder + rightBorder;
|
||||||
|
}
|
||||||
|
|
||||||
|
function height(node) {
|
||||||
|
var topBorder = px(node, "border-top-width");
|
||||||
|
var bottomBorder = px(node, "border-bottom-width");
|
||||||
|
return node.scrollHeight + topBorder + bottomBorder;
|
||||||
|
}
|
||||||
|
|
||||||
|
function px(node, styleProperty) {
|
||||||
|
var value = window.getComputedStyle(node).getPropertyValue(styleProperty);
|
||||||
|
return parseFloat(value.replace("px", ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function newInliner() {
|
||||||
|
var URL_REGEX = /url\(['"]?([^'"]+?)['"]?\)/g;
|
||||||
|
|
||||||
|
return {
|
||||||
|
inlineAll: inlineAll,
|
||||||
|
shouldProcess: shouldProcess,
|
||||||
|
impl: {
|
||||||
|
readUrls: readUrls,
|
||||||
|
inline: inline,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function shouldProcess(string) {
|
||||||
|
return string.search(URL_REGEX) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readUrls(string) {
|
||||||
|
var result = [];
|
||||||
|
var match;
|
||||||
|
while ((match = URL_REGEX.exec(string)) !== null) {
|
||||||
|
result.push(match[1]);
|
||||||
|
}
|
||||||
|
return result.filter(function (url) {
|
||||||
|
return !util.isDataUrl(url);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function inline(string, url, baseUrl, get) {
|
||||||
|
return Promise.resolve(url)
|
||||||
|
.then(function (url) {
|
||||||
|
return baseUrl ? util.resolveUrl(url, baseUrl) : url;
|
||||||
|
})
|
||||||
|
.then(get || util.getAndEncode)
|
||||||
|
.then(function (data) {
|
||||||
|
return util.dataAsUrl(data, util.mimeType(url));
|
||||||
|
})
|
||||||
|
.then(function (dataUrl) {
|
||||||
|
return string.replace(urlAsRegex(url), "$1" + dataUrl + "$3");
|
||||||
|
});
|
||||||
|
|
||||||
|
function urlAsRegex(url) {
|
||||||
|
return new RegExp(
|
||||||
|
"(url\\(['\"]?)(" + util.escape(url) + ")(['\"]?\\))",
|
||||||
|
"g"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function inlineAll(string, baseUrl, get) {
|
||||||
|
if (nothingToInline()) return Promise.resolve(string);
|
||||||
|
|
||||||
|
return Promise.resolve(string)
|
||||||
|
.then(readUrls)
|
||||||
|
.then(function (urls) {
|
||||||
|
var done = Promise.resolve(string);
|
||||||
|
urls.forEach(function (url) {
|
||||||
|
done = done.then(function (string) {
|
||||||
|
return inline(string, url, baseUrl, get);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return done;
|
||||||
|
});
|
||||||
|
|
||||||
|
function nothingToInline() {
|
||||||
|
return !shouldProcess(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function newFontFaces() {
|
||||||
|
return {
|
||||||
|
resolveAll: resolveAll,
|
||||||
|
impl: {
|
||||||
|
readAll: readAll,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function resolveAll() {
|
||||||
|
return readAll(document)
|
||||||
|
.then(function (webFonts) {
|
||||||
|
return Promise.all(
|
||||||
|
webFonts.map(function (webFont) {
|
||||||
|
return webFont.resolve();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.then(function (cssStrings) {
|
||||||
|
return cssStrings.join("\n");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function readAll() {
|
||||||
|
return Promise.resolve(util.asArray(document.styleSheets))
|
||||||
|
.then(getCssRules)
|
||||||
|
.then(selectWebFontRules)
|
||||||
|
.then(function (rules) {
|
||||||
|
return rules.map(newWebFont);
|
||||||
|
});
|
||||||
|
|
||||||
|
function selectWebFontRules(cssRules) {
|
||||||
|
return cssRules
|
||||||
|
.filter(function (rule) {
|
||||||
|
return rule.type === CSSRule.FONT_FACE_RULE;
|
||||||
|
})
|
||||||
|
.filter(function (rule) {
|
||||||
|
return inliner.shouldProcess(rule.style.getPropertyValue("src"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCssRules(styleSheets) {
|
||||||
|
var cssRules = [];
|
||||||
|
styleSheets.forEach(function (sheet) {
|
||||||
|
try {
|
||||||
|
util
|
||||||
|
.asArray(sheet.cssRules || [])
|
||||||
|
.forEach(cssRules.push.bind(cssRules));
|
||||||
|
} catch (e) {
|
||||||
|
console.log(
|
||||||
|
"Error while reading CSS rules from " + sheet.href,
|
||||||
|
e.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return cssRules;
|
||||||
|
}
|
||||||
|
|
||||||
|
function newWebFont(webFontRule) {
|
||||||
|
return {
|
||||||
|
resolve: function resolve() {
|
||||||
|
var baseUrl = (webFontRule.parentStyleSheet || {}).href;
|
||||||
|
return inliner.inlineAll(webFontRule.cssText, baseUrl);
|
||||||
|
},
|
||||||
|
src: function () {
|
||||||
|
return webFontRule.style.getPropertyValue("src");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function newImages() {
|
||||||
|
return {
|
||||||
|
inlineAll: inlineAll,
|
||||||
|
impl: {
|
||||||
|
newImage: newImage,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function newImage(element) {
|
||||||
|
return {
|
||||||
|
inline: inline,
|
||||||
|
};
|
||||||
|
|
||||||
|
function inline(get) {
|
||||||
|
if (util.isDataUrl(element.src)) return Promise.resolve();
|
||||||
|
|
||||||
|
return Promise.resolve(element.src)
|
||||||
|
.then(get || util.getAndEncode)
|
||||||
|
.then(function (data) {
|
||||||
|
return util.dataAsUrl(data, util.mimeType(element.src));
|
||||||
|
})
|
||||||
|
.then(function (dataUrl) {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
element.onload = resolve;
|
||||||
|
element.onerror = reject;
|
||||||
|
element.src = dataUrl;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function inlineAll(node) {
|
||||||
|
if (!(node instanceof Element)) return Promise.resolve(node);
|
||||||
|
|
||||||
|
return inlineBackground(node).then(function () {
|
||||||
|
if (node instanceof HTMLImageElement) return newImage(node).inline();
|
||||||
|
else
|
||||||
|
return Promise.all(
|
||||||
|
util.asArray(node.childNodes).map(function (child) {
|
||||||
|
return inlineAll(child);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
function inlineBackground(node) {
|
||||||
|
var background = node.style.getPropertyValue("background");
|
||||||
|
|
||||||
|
if (!background) return Promise.resolve(node);
|
||||||
|
|
||||||
|
return inliner
|
||||||
|
.inlineAll(background)
|
||||||
|
.then(function (inlined) {
|
||||||
|
node.style.setProperty(
|
||||||
|
"background",
|
||||||
|
inlined,
|
||||||
|
node.style.getPropertyPriority("background")
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default domtoimage;
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { message } from "antd";
|
||||||
|
import { remove, transaction } from "mobx";
|
||||||
|
import { toJS } from "mobx";
|
||||||
|
import store from "../store/global";
|
||||||
|
import { clearHistory } from "../store/history";
|
||||||
|
import domtoimage from "./dom-to-image";
|
||||||
|
import { VERSION } from "./version";
|
||||||
|
|
||||||
|
function read(file) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsText(file, "utf8");
|
||||||
|
reader.onloadend = () => {
|
||||||
|
resolve(JSON.parse(reader.result));
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadFile(file) {
|
||||||
|
const data = await read(file);
|
||||||
|
if (!(data.version <= VERSION)) {
|
||||||
|
message.error("不支持的文件格式,请使用最新版本");
|
||||||
|
console.log(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
clearHistory();
|
||||||
|
transaction(() => {
|
||||||
|
for (const key of Object.keys(store)) {
|
||||||
|
remove(store, key);
|
||||||
|
}
|
||||||
|
Object.assign(store, data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveFile() {
|
||||||
|
const data = new Blob([JSON.stringify(toJS(store))], {
|
||||||
|
type: "application/json",
|
||||||
|
});
|
||||||
|
const downloadLink = document.createElement("a");
|
||||||
|
downloadLink.download = (store.title || "未标题") + ".json";
|
||||||
|
downloadLink.href = URL.createObjectURL(data);
|
||||||
|
downloadLink.click();
|
||||||
|
URL.revokeObjectURL(downloadLink.href);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exportFile() {
|
||||||
|
// const canvas = document.createElement("canvas");
|
||||||
|
// const ctx = canvas.getContext("2d");
|
||||||
|
// const dpi = window.devicePixelRatio || 1;
|
||||||
|
// canvas.style.width = store.canvasWidth;
|
||||||
|
// canvas.style.height = store.canvasHeight;
|
||||||
|
// ctx.scale(dpi, dpi);
|
||||||
|
domtoimage.toPng(document.querySelector("#temp_svg")).then((dataUrl) => {
|
||||||
|
const downloadLink = document.createElement("a");
|
||||||
|
downloadLink.download = (store.title || "未标题") + ".png";
|
||||||
|
downloadLink.href = dataUrl;
|
||||||
|
downloadLink.click();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export { saveFile, loadFile, exportFile };
|
||||||
@@ -1,14 +1,27 @@
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
import state from "../../store/state";
|
||||||
import store from "../../store/global";
|
import store from "../../store/global";
|
||||||
import Styles from "./index.module.css";
|
import Styles from "./index.module.css";
|
||||||
|
|
||||||
// ENHANCE: 增加整体缩放能力
|
// ENHANCE: 增加整体缩放能力
|
||||||
|
|
||||||
function Canvas({ children, ...props }) {
|
function Canvas({ children, ...props }) {
|
||||||
|
const ref = useRef();
|
||||||
|
useEffect(() => {
|
||||||
|
state.refs.svg = ref;
|
||||||
|
return () => {
|
||||||
|
state.refs.svg = null;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
ref={ref}
|
||||||
|
id="temp_svg"
|
||||||
xmlns=" http://www.w3.org/2000/svg"
|
xmlns=" http://www.w3.org/2000/svg"
|
||||||
className={Styles.svg}
|
className={Styles.svg}
|
||||||
|
// viewBox={`0 0 ${store.canvasWidth} ${store.canvasHeight}`}
|
||||||
width={store.canvasWidth}
|
width={store.canvasWidth}
|
||||||
height={store.canvasHeight}
|
height={store.canvasHeight}
|
||||||
// TODO: do this
|
// TODO: do this
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ function LeftInfoBlock() {
|
|||||||
initialValue={store.tone}
|
initialValue={store.tone}
|
||||||
options={tones}
|
options={tones}
|
||||||
onChange={handleChangeTone}
|
onChange={handleChangeTone}
|
||||||
|
popoverProps={{ trigger: "click" }}
|
||||||
>
|
>
|
||||||
<Row
|
<Row
|
||||||
type="tone"
|
type="tone"
|
||||||
|
|||||||
Reference in New Issue
Block a user