diff --git a/package-lock.json b/package-lock.json index 54398b7a..ab8cdd8e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "axios": "^1.6.2", "dexie": "^3.2.4", "dexie-react-hooks": "^1.1.7", + "exceljs": "^4.4.0", "file-saver": "^2.0.5", "framer-motion": "^10.18.0", "html-to-image": "^1.11.11", @@ -1116,6 +1117,43 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@fast-csv/format": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz", + "integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==", + "dependencies": { + "@types/node": "^14.0.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isboolean": "^3.0.3", + "lodash.isequal": "^4.5.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0" + } + }, + "node_modules/@fast-csv/format/node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==" + }, + "node_modules/@fast-csv/parse": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz", + "integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==", + "dependencies": { + "@types/node": "^14.0.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.groupby": "^4.6.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0", + "lodash.isundefined": "^3.0.1", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/@fast-csv/parse/node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.13", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", @@ -1718,6 +1756,17 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/node": { + "version": "20.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", + "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, "node_modules/@types/pegjs": { "version": "0.10.6", "resolved": "https://registry.npmjs.org/@types/pegjs/-/pegjs-0.10.6.tgz", @@ -1971,6 +2020,75 @@ "node": ">= 8" } }, + "node_modules/archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dependencies": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/archiver-utils/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/archiver-utils/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/archiver-utils/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -2085,6 +2203,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + }, "node_modules/async-validator": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-3.5.2.tgz", @@ -2188,6 +2311,25 @@ "node": ">= 0.6.0" } }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/bezier-easing": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/bezier-easing/-/bezier-easing-2.1.0.tgz", @@ -2201,6 +2343,18 @@ "node": ">=0.6" } }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -2210,6 +2364,21 @@ "node": ">=8" } }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2274,6 +2443,53 @@ "node": ">= 0.4.0" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "engines": { + "node": ">=0.2.0" + } + }, "node_modules/call-bind": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", @@ -2350,6 +2566,17 @@ "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "optional": true }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -2465,6 +2692,20 @@ "node": ">= 6" } }, + "node_modules/compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "dependencies": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/compute-scroll-into-view": { "version": "1.0.20", "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz", @@ -2503,6 +2744,34 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/crelt": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", @@ -2571,6 +2840,11 @@ "date-fns": ">=2.0.0" } }, + "node_modules/dayjs": { + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz", + "integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -2680,12 +2954,60 @@ "integrity": "sha512-kxxKlPEDa6Nc5WJi+qRgPbOAbgTpSULL+vI3NUXsZMlkJxTqYI9wg5ZTay2sFrdZRWHPWNi+EdAhcJf81WtoMQ==", "optional": true }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/duplexer2/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexer2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.614", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.614.tgz", "integrity": "sha512-X4ze/9Sc3QWs6h92yerwqv7aB/uU8vCjZcrMjA8N9R1pjMFRe44dLsck5FzLilOYvcXuDn93B+bpGYyufc70gQ==", "dev": true }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/es-abstract": { "version": "1.22.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", @@ -3170,6 +3492,37 @@ "node": ">=0.10.0" } }, + "node_modules/exceljs": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.4.0.tgz", + "integrity": "sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==", + "dependencies": { + "archiver": "^5.0.0", + "dayjs": "^1.8.34", + "fast-csv": "^4.3.1", + "jszip": "^3.10.1", + "readable-stream": "^3.6.0", + "saxes": "^5.0.1", + "tmp": "^0.2.0", + "unzipper": "^0.10.11", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/fast-csv": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz", + "integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==", + "dependencies": { + "@fast-csv/format": "4.3.5", + "@fast-csv/parse": "4.3.6" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3372,6 +3725,11 @@ } } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3391,6 +3749,31 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/fstream/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -3531,6 +3914,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -3670,6 +4058,25 @@ "@babel/runtime": "^7.23.2" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", @@ -3679,6 +4086,11 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -4201,6 +4613,49 @@ "node": ">=4.0" } }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -4210,6 +4665,49 @@ "json-buffer": "3.0.1" } }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -4248,6 +4746,14 @@ "url": "https://github.com/sponsors/dmonad" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -4263,6 +4769,11 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4283,12 +4794,77 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==" + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" + }, + "node_modules/lodash.groupby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, + "node_modules/lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==" + }, + "node_modules/lodash.isnil": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", + "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isundefined": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", + "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -4366,6 +4942,25 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -4429,7 +5024,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -4611,6 +5205,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4892,6 +5491,11 @@ "node": ">=6" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5121,6 +5725,46 @@ "pify": "^2.3.0" } }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -5310,6 +5954,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/safe-regex-test": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", @@ -5324,6 +5987,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -5382,6 +6056,11 @@ "node": ">= 0.4" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5434,6 +6113,14 @@ "node": ">=0.1.14" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string.prototype.matchall": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", @@ -5657,6 +6344,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/text-segmentation": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", @@ -5693,6 +6395,14 @@ "node": ">=0.8" } }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "engines": { + "node": ">=14.14" + } + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -5714,6 +6424,14 @@ "node": ">=8.0" } }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "engines": { + "node": "*" + } + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -5829,6 +6547,63 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/unzipper": { + "version": "0.10.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", + "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "node_modules/unzipper/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/unzipper/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/unzipper/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/unzipper/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", @@ -5885,8 +6660,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/utility-types": { "version": "3.10.0", @@ -5905,6 +6679,14 @@ "base64-arraybuffer": "^1.0.2" } }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/vite": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.1.tgz", @@ -6069,6 +6851,11 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -6112,6 +6899,39 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "dependencies": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "dependencies": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } } } } diff --git a/package.json b/package.json index cff6fa59..f1a03bc5 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "axios": "^1.6.2", "dexie": "^3.2.4", "dexie-react-hooks": "^1.1.7", + "exceljs": "^4.4.0", "file-saver": "^2.0.5", "framer-motion": "^10.18.0", "html-to-image": "^1.11.11", diff --git a/src/components/EditorHeader/ControlPanel.jsx b/src/components/EditorHeader/ControlPanel.jsx index 1e067918..66991423 100644 --- a/src/components/EditorHeader/ControlPanel.jsx +++ b/src/components/EditorHeader/ControlPanel.jsx @@ -21,15 +21,7 @@ import { Toast, Popconfirm, } from "@douyinfe/semi-ui"; -import { toPng, toJpeg, toSvg } from "html-to-image"; -import { saveAs } from "file-saver"; -import { - jsonToMySQL, - jsonToPostgreSQL, - jsonToSQLite, - jsonToMariaDB, - jsonToSQLServer, -} from "../../utils/toSQL"; +import { toPng } from "html-to-image"; import { ObjectType, Action, @@ -38,7 +30,6 @@ import { MODAL, SIDESHEET, } from "../../data/constants"; -import jsPDF from "jspdf"; import { useHotkeys } from "react-hotkeys-hook"; import { Validator } from "jsonschema"; import { areaSchema, noteSchema, tableSchema } from "../../data/schemas"; @@ -60,8 +51,8 @@ import useSaveState from "../../hooks/useSaveState"; import { IconAddArea, IconAddNote, IconAddTable } from "../../icons"; import LayoutDropdown from "./LayoutDropdown"; import Sidesheet from "./SideSheet/Sidesheet"; -import Modal from "./Modal/Modal"; import { useTranslation } from "react-i18next"; +import ModalManager from "./ModalManager"; export default function ControlPanel({ diagramId, @@ -72,13 +63,7 @@ export default function ControlPanel({ }) { const [modal, setModal] = useState(MODAL.NONE); const [sidesheet, setSidesheet] = useState(SIDESHEET.NONE); - const [prevTitle, setPrevTitle] = useState(title); const [showEditName, setShowEditName] = useState(false); - const [exportData, setExportData] = useState({ - data: null, - filename: `${title}_${new Date().toISOString()}`, - extension: "", - }); const { saveState, setSaveState } = useSaveState(); const { layout, setLayout } = useLayout(); const { settings, setSettings } = useSettings(); @@ -452,7 +437,7 @@ export default function ControlPanel({ } }; - const fileImport = () => setModal(MODAL.IMPORT); + const importDiagram = () => setModal(MODAL.IMPORT_DIAGRAM); const viewGrid = () => setSettings((prev) => ({ ...prev, showGrid: !prev.showGrid })); const zoomIn = () => @@ -704,7 +689,6 @@ export default function ControlPanel({ rename: { function: () => { setModal(MODAL.RENAME); - setPrevTitle(title); }, }, delete_diagram: { @@ -730,7 +714,7 @@ export default function ControlPanel({ }, }, import_diagram: { - function: fileImport, + function: importDiagram, shortcut: "Ctrl+I", }, import_from_source: { @@ -739,188 +723,33 @@ export default function ControlPanel({ export_as: { children: [ { - PNG: () => { - toPng(document.getElementById("canvas")).then(function (dataUrl) { - setExportData((prev) => ({ - ...prev, - data: dataUrl, - extension: "png", - })); - }); - setModal(MODAL.IMG); - }, - }, - { - JPEG: () => { - toJpeg(document.getElementById("canvas"), { quality: 0.95 }).then( - function (dataUrl) { - setExportData((prev) => ({ - ...prev, - data: dataUrl, - extension: "jpeg", - })); - }, - ); - setModal(MODAL.IMG); - }, - }, - { - JSON: () => { - setModal(MODAL.CODE); - const result = JSON.stringify( - { - tables: tables, - relationships: relationships, - notes: notes, - subjectAreas: areas, - types: types, - title: title, - }, - null, - 2, - ); - setExportData((prev) => ({ - ...prev, - data: result, - extension: "json", - })); - }, - }, - { - SVG: () => { - const filter = (node) => node.tagName !== "i"; - toSvg(document.getElementById("canvas"), { filter: filter }).then( - function (dataUrl) { - setExportData((prev) => ({ - ...prev, - data: dataUrl, - extension: "svg", - })); - }, - ); - setModal(MODAL.IMG); + IMAGE: () => { + setModal(MODAL.EXPORT_IMG); }, }, { PDF: () => { - const canvas = document.getElementById("canvas"); - toJpeg(canvas).then(function (dataUrl) { - const doc = new jsPDF("l", "px", [ - canvas.offsetWidth, - canvas.offsetHeight, - ]); - doc.addImage( - dataUrl, - "jpeg", - 0, - 0, - canvas.offsetWidth, - canvas.offsetHeight, - ); - doc.save(`${exportData.filename}.pdf`); - }); + setModal(MODAL.EXPORT_PDF); }, }, { - DRAWDB: () => { - const result = JSON.stringify( - { - author: "Unnamed", - title: title, - date: new Date().toISOString(), - tables: tables, - relationships: relationships, - notes: notes, - subjectAreas: areas, - types: types, - }, - null, - 2, - ); - const blob = new Blob([result], { - type: "text/plain;charset=utf-8", - }); - saveAs(blob, `${exportData.filename}.ddb`); + SQL: () => { + setModal(MODAL.EXPORT_SQL); }, }, - ], - function: () => {}, - }, - export_source: { - children: [ { - MySQL: () => { - setModal(MODAL.CODE); - const src = jsonToMySQL({ - tables: tables, - references: relationships, - types: types, - }); - setExportData((prev) => ({ - ...prev, - data: src, - extension: "sql", - })); + EXCEL: async () => { + setModal(MODAL.EXPORT_EXCEL); }, }, { - PostgreSQL: () => { - setModal(MODAL.CODE); - const src = jsonToPostgreSQL({ - tables: tables, - references: relationships, - types: types, - }); - setExportData((prev) => ({ - ...prev, - data: src, - extension: "sql", - })); - }, - }, - { - SQLite: () => { - setModal(MODAL.CODE); - const src = jsonToSQLite({ - tables: tables, - references: relationships, - types: types, - }); - setExportData((prev) => ({ - ...prev, - data: src, - extension: "sql", - })); - }, - }, - { - MariaDB: () => { - setModal(MODAL.CODE); - const src = jsonToMariaDB({ - tables: tables, - references: relationships, - types: types, - }); - setExportData((prev) => ({ - ...prev, - data: src, - extension: "sql", - })); + JSON: () => { + setModal(MODAL.EXPORT_JSON); }, }, { - MSSQL: () => { - setModal(MODAL.CODE); - const src = jsonToSQLServer({ - tables: tables, - references: relationships, - types: types, - }); - setExportData((prev) => ({ - ...prev, - data: src, - extension: "sql", - })); + DRAWDB: () => { + setModal(MODAL.EXPORT_DIAGRAM); }, }, ], @@ -1166,7 +995,7 @@ export default function ControlPanel({ }, }; - useHotkeys("ctrl+i, meta+i", fileImport, { preventDefault: true }); + useHotkeys("ctrl+i, meta+i", importDiagram, { preventDefault: true }); useHotkeys("ctrl+z, meta+z", undo, { preventDefault: true }); useHotkeys("ctrl+y, meta+y", redo, { preventDefault: true }); useHotkeys("ctrl+s, meta+s", save, { preventDefault: true }); @@ -1196,20 +1025,21 @@ export default function ControlPanel({ }); useHotkeys("ctrl+alt+w, meta+alt+w", fitWindow, { preventDefault: true }); + const hideModal = () => { + setModal(MODAL.NONE); + }; + return ( <> {layout.header && header()} {layout.toolbar && toolbar()} - + {children} + + ); +}; diff --git a/src/components/EditorHeader/Modal/ExportDrawDB.jsx b/src/components/EditorHeader/Modal/ExportDrawDB.jsx new file mode 100644 index 00000000..73f25355 --- /dev/null +++ b/src/components/EditorHeader/Modal/ExportDrawDB.jsx @@ -0,0 +1,49 @@ +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { + useAreas, + useNotes, + useTables, + useTypes, +} from "../../../hooks"; +import ExportModal from "./ExportModal"; + +export default function ExportDrawDB({ title, hideModal }) { + const { t } = useTranslation(); + const { tables, relationships } = useTables(); + const { notes } = useNotes(); + const { areas } = useAreas(); + const { types } = useTypes(); + + const rawData = JSON.stringify( + { + author: "Unnamed", + title: title, + date: new Date().toISOString(), + tables: tables, + relationships: relationships, + notes: notes, + subjectAreas: areas, + types: types, + }, + null, + 2, + ); + const [exportData, setExportData] = useState({ + data: new Blob( + [rawData], + { type: "text/plain;charset=utf-8" } + ), + filename: `${title}_${new Date().toISOString()}`, + extension: "ddb", + }); + + return ( + + ) +} diff --git a/src/components/EditorHeader/Modal/ExportExcel.jsx b/src/components/EditorHeader/Modal/ExportExcel.jsx new file mode 100644 index 00000000..0a857e97 --- /dev/null +++ b/src/components/EditorHeader/Modal/ExportExcel.jsx @@ -0,0 +1,91 @@ +import { Checkbox, Select } from "@douyinfe/semi-ui"; +import { useCallback, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { DATABASE_TYPES } from "../../../data/constants"; +import { jsonToExcel } from "../../../utils/toDocument"; +import { useTables } from "../../../hooks"; +import ExportModal from "./ExportModal"; + +export default function ExportExcel({ title, hideModal }) { + const { t } = useTranslation(); + const { tables, relationships } = useTables(); + + const [options, setOptions] = useState({ + format: DATABASE_TYPES[0], + hasRelationship: true, + hasDiagram: true, + }); + + const [exportData, setExportData] = useState({ + data: null, + filename: `${title}_${new Date().toISOString()}`, + extension: "xlsx", + }); + + const changeOptions = (options) => { + setOptions((prev) => ({ + ...prev, + ...options + })); + } + + const getData = useCallback(async (options) => { + const content = await jsonToExcel({ + tables: tables, + dbms: options.format.toLowerCase(), + relationships: options.hasRelationship ? relationships: [], + hasDiagram: options.hasDiagram, + }); + + return new Blob([content], { + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + }); + }, [tables, relationships]); + + useEffect(() => { + getData(options).then( + (data) => setExportData((prev) => ({ + ...prev, + data: data, + })) + ) + }, [options, getData]); + + return ( + +
{t("format")}:
+ ({ + label: type.toUpperCase(), + value: type, + }))} + value={exportData.extension} + onChange={changeType} + /> +
+ {exportData.data + ? Diagram + : + } +
+
+ ) +} diff --git a/src/components/EditorHeader/Modal/ExportJson.jsx b/src/components/EditorHeader/Modal/ExportJson.jsx new file mode 100644 index 00000000..a57375e2 --- /dev/null +++ b/src/components/EditorHeader/Modal/ExportJson.jsx @@ -0,0 +1,68 @@ +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import CodeMirror from "@uiw/react-codemirror"; +import { sql } from "@codemirror/lang-sql"; +import { vscodeDark } from "@uiw/codemirror-theme-vscode"; +import { json } from "@codemirror/lang-json"; +import { githubLight } from "@uiw/codemirror-theme-github"; +import { + useAreas, + useNotes, + useTables, + useTypes, + useSettings +} from "../../../hooks"; +import ExportModal from "./ExportModal"; + +const languageExtension = { + sql: [sql()], + json: [json()], +}; + +export default function ExportJson({ title, hideModal }) { + const { t } = useTranslation(); + const { settings } = useSettings(); + const { tables, relationships } = useTables(); + const { notes } = useNotes(); + const { areas } = useAreas(); + const { types } = useTypes(); + + const rawData = JSON.stringify( + { + tables: tables, + relationships: relationships, + notes: notes, + subjectAreas: areas, + types: types, + title: title, + }, + null, + 2, + ); + const [exportData, setExportData] = useState({ + data: new Blob( + [rawData], + { type: "application/json" } + ), + filename: `${title}_${new Date().toISOString()}`, + extension: "json", + }); + + return ( + + {}} + editable={false} + theme={settings.mode === "dark" ? vscodeDark : githubLight} + /> + + ) +} diff --git a/src/components/EditorHeader/Modal/ExportModal.jsx b/src/components/EditorHeader/Modal/ExportModal.jsx new file mode 100644 index 00000000..ccd8918e --- /dev/null +++ b/src/components/EditorHeader/Modal/ExportModal.jsx @@ -0,0 +1,35 @@ +import { Input } from "@douyinfe/semi-ui"; +import { saveAs } from "file-saver"; +import { useTranslation } from "react-i18next"; +import BaseModal from "./BaseModal"; + +export default function ExportModal({children, modalTitle, exportData, setExportData, onCancel}) { + const { t } = useTranslation(); + + const onOk = () => { + saveAs(exportData.data, `${exportData.filename}.${exportData.extension}`) + } + + return ( + +
{t("filename")}:
+ {`.${exportData.extension}`}} + onChange={(value) => + setExportData((prev) => ({ ...prev, filename: value })) + } + field="filename" + /> + {children} +
+ ) +} diff --git a/src/components/EditorHeader/Modal/ExportPdf.jsx b/src/components/EditorHeader/Modal/ExportPdf.jsx new file mode 100644 index 00000000..80127427 --- /dev/null +++ b/src/components/EditorHeader/Modal/ExportPdf.jsx @@ -0,0 +1,50 @@ +import { useEffect, useState } from "react"; +import { toJpeg } from "html-to-image"; +import jsPDF from "jspdf"; +import { useTranslation } from "react-i18next"; +import ExportModal from "./ExportModal"; + +export default function ExportPdf({ title, hideModal }) { + const { t } = useTranslation(); + const [exportData, setExportData] = useState({ + data: null, + filename: `${title}_${new Date().toISOString()}`, + extension: "pdf", + }); + + const getData = async () => { + const canvas = document.getElementById("canvas"); + const dataUrl = await toJpeg(canvas); + const doc = new jsPDF("l", "px", [ + canvas.offsetWidth, + canvas.offsetHeight, + ]); + doc.addImage( + dataUrl, + "jpeg", + 0, + 0, + canvas.offsetWidth, + canvas.offsetHeight, + ); + return doc.output('blob'); + } + + useEffect(() => { + getData().then( + (data) => setExportData((prev) => ({ + ...prev, + data: data, + })) + ) + }, []); + + return ( + + ) +} diff --git a/src/components/EditorHeader/Modal/ExportSql.jsx b/src/components/EditorHeader/Modal/ExportSql.jsx new file mode 100644 index 00000000..d59766db --- /dev/null +++ b/src/components/EditorHeader/Modal/ExportSql.jsx @@ -0,0 +1,104 @@ +import { useCallback, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import CodeMirror from "@uiw/react-codemirror"; +import { sql } from "@codemirror/lang-sql"; +import { vscodeDark } from "@uiw/codemirror-theme-vscode"; +import { json } from "@codemirror/lang-json"; +import { githubLight } from "@uiw/codemirror-theme-github"; +import { Checkbox, Select } from "@douyinfe/semi-ui"; +import { DATABASE_TYPES } from "../../../data/constants"; +import toSQL from "../../../utils/toSQL"; +import { + useTables, + useSettings, + useTypes, +} from "../../../hooks"; +import ExportModal from "./ExportModal"; + +const languageExtension = { + sql: [sql()], + json: [json()], +}; + +export default function ExportSql({ title, hideModal }) { + const { t } = useTranslation(); + const { settings } = useSettings(); + const { tables, relationships } = useTables(); + const { types } = useTypes(); + + const [options, setOptions] = useState({ + format: DATABASE_TYPES[0], + hasRelationship: true, + }); + + const [exportData, setExportData] = useState({ + data: null, + rawData: '', + filename: `${title}_${new Date().toISOString()}`, + extension: "sql", + }); + + const changeOptions = (options) => { + setOptions((prev) => ({ + ...prev, + ...options + })); + } + + const changeData = useCallback((options) => { + const rawData = toSQL[`jsonTo${options.format}`]({ + tables: tables, + references: options.hasRelationship ? relationships : [], + types: types, + }); + + setExportData((prev) => ({ + ...prev, + data: new Blob([rawData], { + type: "application/json", + }), + rawData: rawData + })) + }, [tables, relationships, types]) + + useEffect(() => { + changeData(options) + }, [options, changeData]); + + return ( + +
{t("format")}:
+ setSaveAsTitle(v)} - /> - ); - case MODAL.CODE: - case MODAL.IMG: - if (exportData.data !== "" || exportData.data) { - return ( - <> - {modal === MODAL.IMG ? ( - Diagram - ) : ( - {}} - editable={false} - theme={settings.mode === "dark" ? vscodeDark : githubLight} - /> - )} -
{t("filename")}:
- {`.${exportData.extension}`}} - onChange={(value) => - setExportData((prev) => ({ ...prev, filename: value })) - } - field="filename" - /> - - ); - } else { - return ( -
- -
- ); - } - case MODAL.TABLE_WIDTH: - return ; - case MODAL.LANGUAGE: - return ; - default: - return <>; - } - }; - - return ( - { - setExportData(() => ({ - data: "", - extension: "", - filename: `${title}_${new Date().toISOString()}`, - })); - setError({ - type: STATUS.NONE, - message: "", - }); - setImportData(null); - setImportSource({ - src: "", - overwrite: true, - dbms: "MySQL", - }); - }} - onCancel={() => { - if (modal === MODAL.RENAME) setTitle(prevTitle); - setModal(MODAL.NONE); - }} - centered - closeOnEsc={true} - okText={getOkText(modal)} - okButtonProps={{ - disabled: - (error && error?.type === STATUS.ERROR) || - (modal === MODAL.IMPORT && - (error.type === STATUS.ERROR || !importData)) || - (modal === MODAL.RENAME && title === "") || - ((modal === MODAL.IMG || modal === MODAL.CODE) && !exportData.data) || - (modal === MODAL.SAVEAS && saveAsTitle === "") || - (modal === MODAL.IMPORT_SRC && importSource.src === ""), - }} - cancelText={t("cancel")} - width={modal === MODAL.NEW ? 740 : 600} - bodyStyle={{ maxHeight: window.innerHeight - 280, overflow: "auto" }} - > - {getModalBody()} - - ); -} diff --git a/src/components/EditorHeader/Modal/New.jsx b/src/components/EditorHeader/Modal/New.jsx index 81ffcdb2..e5919338 100644 --- a/src/components/EditorHeader/Modal/New.jsx +++ b/src/components/EditorHeader/Modal/New.jsx @@ -1,45 +1,67 @@ -import { db } from "../../../data/db"; -import { useSettings } from "../../../hooks"; import { useLiveQuery } from "dexie-react-hooks"; -import Thumbnail from "../../Thumbnail"; +import { useState } from "react"; import { useTranslation } from "react-i18next"; +import { db } from "../../../data/db"; +import Thumbnail from "../../Thumbnail"; +import { useSettings } from "../../../hooks"; +import BaseModal from "./BaseModal"; -export default function New({ selectedTemplateId, setSelectedTemplateId }) { - const { settings } = useSettings(); +export default function New({ hideModal }) { const { t } = useTranslation(); + const { settings } = useSettings(); + const [selectedTemplateId, setSelectedTemplateId] = useState(-1); + const templates = useLiveQuery(() => db.templates.toArray()); + const createNewDiagram = (id) => { + const newWindow = window.open("/editor"); + newWindow.name = "lt " + id; + }; + + const onOk = () => { + hideModal(); + createNewDiagram(selectedTemplateId); + } + return ( -
-
setSelectedTemplateId(0)}> -
- -
-
{t("blank")}
-
- {templates?.map((temp, i) => ( -
setSelectedTemplateId(temp.id)}> + +
+
setSelectedTemplateId(0)}>
- +
-
{temp.title}
+
{t("blank")}
- ))} -
+ {templates?.map((temp, i) => ( +
setSelectedTemplateId(temp.id)}> +
+ +
+
{temp.title}
+
+ ))} +
+ ); } diff --git a/src/components/EditorHeader/Modal/Open.jsx b/src/components/EditorHeader/Modal/Open.jsx index cf7a5266..d106641d 100644 --- a/src/components/EditorHeader/Modal/Open.jsx +++ b/src/components/EditorHeader/Modal/Open.jsx @@ -1,11 +1,29 @@ -import { db } from "../../../data/db"; -import { Banner } from "@douyinfe/semi-ui"; +import { Banner, Toast } from "@douyinfe/semi-ui"; import { useLiveQuery } from "dexie-react-hooks"; +import { useState } from "react"; import { useTranslation } from "react-i18next"; +import { db } from "../../../data/db"; +import { + useAreas, + useNotes, + useTables, + useTransform, + useTypes, + useUndoRedo, +} from "../../../hooks"; +import BaseModal from "./BaseModal"; -export default function Open({ selectedDiagramId, setSelectedDiagramId }) { - const diagrams = useLiveQuery(() => db.diagrams.toArray()); +export default function Open({ hideModal, setDiagramId, setTitle}) { const { t } = useTranslation(); + const { setTables, setRelationships } = useTables(); + const { setNotes } = useNotes(); + const { setAreas } = useAreas(); + const { setTypes } = useTypes(); + const { setTransform } = useTransform(); + const { setUndoStack, setRedoStack } = useUndoRedo(); + + const diagrams = useLiveQuery(() => db.diagrams.toArray()); + const [selectedDiagramId, setSelectedDiagramId] = useState(0); const getDiagramSize = (d) => { const size = JSON.stringify(d).length; @@ -18,8 +36,48 @@ export default function Open({ selectedDiagramId, setSelectedDiagramId }) { return sizeStr; }; + + const loadDiagram = async (id) => { + await db.diagrams + .get(id) + .then((diagram) => { + if (diagram) { + setDiagramId(diagram.id); + setTitle(diagram.name); + setTables(diagram.tables); + setTypes(diagram.types); + setRelationships(diagram.references); + setAreas(diagram.areas); + setNotes(diagram.notes); + setTransform({ + pan: diagram.pan, + zoom: diagram.zoom, + }); + setUndoStack([]); + setRedoStack([]); + window.name = `d ${diagram.id}`; + } else { + Toast.error("Oops! Something went wrong."); + } + }) + .catch(() => { + Toast.error("Oops! Couldn't load diagram."); + }); + }; + + const onOk = () => { + if (selectedDiagramId === 0) return; + loadDiagram(selectedDiagramId); + hideModal(); + }; + return ( -
+ {diagrams?.length === 0 ? (
)} -
+ ); } diff --git a/src/components/EditorHeader/Modal/Rename.jsx b/src/components/EditorHeader/Modal/Rename.jsx index d8767468..915e60c8 100644 --- a/src/components/EditorHeader/Modal/Rename.jsx +++ b/src/components/EditorHeader/Modal/Rename.jsx @@ -1,14 +1,34 @@ import { Input } from "@douyinfe/semi-ui"; +import { useRef } from "react"; import { useTranslation } from "react-i18next"; +import BaseModal from "./BaseModal" -export default function Rename({ title, setTitle }) { +export default function Rename({ hideModal, title, setTitle }) { const { t } = useTranslation(); + const originalTitle = useRef(title) + + const onCancel = () => { + setTitle(originalTitle.current); + hideModal(); + }; + + const onOk = () => { + hideModal(); + }; return ( - setTitle(v)} - /> + + setTitle(v)} + /> + ); } diff --git a/src/components/EditorHeader/Modal/SaveAs.jsx b/src/components/EditorHeader/Modal/SaveAs.jsx new file mode 100644 index 00000000..ea80c545 --- /dev/null +++ b/src/components/EditorHeader/Modal/SaveAs.jsx @@ -0,0 +1,30 @@ +import { Input } from "@douyinfe/semi-ui"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import BaseModal from "./BaseModal" + +export default function SaveAs({ hideModal, title, setTitle }) { + const { t } = useTranslation(); + const [saveAsTitle, setSaveAsTitle] = useState(title); + + const onOk = () => { + setTitle(saveAsTitle); + hideModal(); + }; + + return ( + + setSaveAsTitle(v)} + /> + + ); +} diff --git a/src/components/EditorHeader/Modal/SetTableWidth.jsx b/src/components/EditorHeader/Modal/SetTableWidth.jsx index 93482e68..2658882b 100644 --- a/src/components/EditorHeader/Modal/SetTableWidth.jsx +++ b/src/components/EditorHeader/Modal/SetTableWidth.jsx @@ -1,17 +1,26 @@ import { InputNumber } from "@douyinfe/semi-ui"; +import { useTranslation } from "react-i18next"; import { useSettings } from "../../../hooks"; +import BaseModal from "./BaseModal" -export default function SetTableWidth() { +export default function SetTableWidth({ hideModal }) { + const { t } = useTranslation(); const { settings, setSettings } = useSettings(); return ( - { - if (c < 180) return; - setSettings((prev) => ({ ...prev, tableWidth: c })); - }} - /> + + { + if (c < 180) return; + setSettings((prev) => ({ ...prev, tableWidth: c })); + }} + /> + ); } diff --git a/src/components/EditorHeader/ModalManager.jsx b/src/components/EditorHeader/ModalManager.jsx new file mode 100644 index 00000000..f5585711 --- /dev/null +++ b/src/components/EditorHeader/ModalManager.jsx @@ -0,0 +1,56 @@ +import { MODAL } from "../../data/constants"; +import Rename from "./Modal/Rename"; +import Open from "./Modal/Open"; +import New from "./Modal/New"; +import ImportDiagram from "./Modal/ImportDiagram"; +import ImportSource from "./Modal/ImportSource"; +import SetTableWidth from "./Modal/SetTableWidth"; +import Language from "./Modal/Language"; +import ExportExcel from "./Modal/ExportExcel"; +import ExportImage from "./Modal/ExportImage"; +import ExportJson from "./Modal/ExportJson"; +import ExportDrawDB from "./Modal/ExportDrawDB"; +import ExportSql from "./Modal/ExportSql"; +import ExportPdf from "./Modal/ExportPdf"; +import SaveAs from "./Modal/SaveAs"; + +export default function ModalManager({ + modal, + hideModal, + title, + setTitle, + setDiagramId, +}) { + switch (modal) { + case MODAL.EXPORT_IMG: + return ; + case MODAL.EXPORT_PDF: + return ; + case MODAL.EXPORT_SQL: + return ; + case MODAL.EXPORT_EXCEL: + return ; + case MODAL.EXPORT_JSON: + return ; + case MODAL.EXPORT_DIAGRAM: + return ; + case MODAL.IMPORT_DIAGRAM: + return ; + case MODAL.IMPORT_SRC: + return ; + case MODAL.NEW: + return ; + case MODAL.OPEN: + return ; + case MODAL.SAVEAS: + return ; + case MODAL.RENAME: + return ; + case MODAL.TABLE_WIDTH: + return ; + case MODAL.LANGUAGE: + return ; + default: + return <>; + } +} diff --git a/src/data/constants.js b/src/data/constants.js index 480b1ba4..0d4fd453 100644 --- a/src/data/constants.js +++ b/src/data/constants.js @@ -105,16 +105,20 @@ export const State = { export const MODAL = { NONE: 0, - IMG: 1, - CODE: 2, - IMPORT: 3, + NEW: 1, + OPEN: 2, + SAVEAS: 3, RENAME: 4, - OPEN: 5, - SAVEAS: 6, - NEW: 7, - IMPORT_SRC: 8, - TABLE_WIDTH: 9, - LANGUAGE: 10, + IMPORT_SRC: 5, + IMPORT_DIAGRAM: 6, + TABLE_WIDTH: 7, + LANGUAGE: 8, + EXPORT_EXCEL: 9, + EXPORT_IMG: 10, + EXPORT_JSON: 11, + EXPORT_DIAGRAM: 12, + EXPORT_SQL: 13, + EXPORT_PDF: 14, }; export const STATUS = { @@ -129,3 +133,13 @@ export const SIDESHEET = { TODO: 1, TIMELINE: 2, }; + +export const DATABASE_TYPES = [ + "MySQL", + "MariaDB", + "PostgreSQL", + "SQLite", + "MSSQL", +]; + +export const IMAGE_TYPES = ["png", "jpeg", "svg"]; diff --git a/src/i18n/locales/da.js b/src/i18n/locales/da.js index baca4f5b..3f775dcb 100644 --- a/src/i18n/locales/da.js +++ b/src/i18n/locales/da.js @@ -165,7 +165,7 @@ const da = { no_values_for_field: "'{{fieldName}}' felt fra tabellen '{{tableName}}' er af type `{{type}}` men ingen værdi er blevet specificeret", default_doesnt_match_type: - "Standardværdien for feltet '{{fieldName}}' i tabellen '{{table.name}}' stemmer ikke overens med dens type", + "Standardværdien for feltet '{{fieldName}}' i tabellen '{{tableName}}' stemmer ikke overens med dens type", not_null_is_null: "'{{fieldName}}' felt fra tabellen '{{tableName}}' er IKKE NUL, men har standardværdien NUL", duplicate_fields: diff --git a/src/i18n/locales/de.js b/src/i18n/locales/de.js index a1c2d776..973507f2 100644 --- a/src/i18n/locales/de.js +++ b/src/i18n/locales/de.js @@ -168,7 +168,7 @@ const de = { no_values_for_field: "Das Feld '{{fieldName}}' der Tabelle '{{tableName}}' ist vom Typ `{{type}}`, aber es wurden keine Werte angegeben", default_doesnt_match_type: - "Der Standardwert für das Feld '{{fieldName}}' in der Tabelle '{{table.name}}' entspricht nicht seinem Typ", + "Der Standardwert für das Feld '{{fieldName}}' in der Tabelle '{{tableName}}' entspricht nicht seinem Typ", not_null_is_null: "Das Feld '{{fieldName}}' der Tabelle '{{tableName}}' ist NOT NULL, hat aber standardmäßig NULL", duplicate_fields: diff --git a/src/i18n/locales/en.js b/src/i18n/locales/en.js index 2bce8950..65cf62f8 100644 --- a/src/i18n/locales/en.js +++ b/src/i18n/locales/en.js @@ -165,7 +165,7 @@ const en = { no_values_for_field: "'{{fieldName}}' field of table '{{tableName}}' is of type `{{type}}` but no values have been specified", default_doesnt_match_type: - "Default value for field '{{fieldName}}' in table '{{table.name}}' does not match its type", + "Default value for field '{{fieldName}}' in table '{{tableName}}' does not match its type", not_null_is_null: "'{{fieldName}}' field of table '{{tableName}}' is NOT NULL but has default NULL", duplicate_fields: @@ -212,6 +212,21 @@ const en = { edit_relationship: "{{extra}} Edit relationship {{refName}}", delete_relationship: "Delete relationship {{refName}}", not_found: "Not found", + export_pdf: "Export pdf", + export_json: "Export json", + export_diagram: "Export diagram", + export_excel: "Export excel", + format: "Format", + fields: "Fields", + source_table: "Source table", + source_field: "Source field", + foreign_table: "Foreign table", + foreign_field: "Foreign field", + update_constraint: "Update constraint", + delete_constraint: "Delete constraint", + yes: "Yes", + diagram: "Diagram", + add_diagram: "Add diagram" }, }; diff --git a/src/i18n/locales/es.js b/src/i18n/locales/es.js index 39c00c68..173b2975 100644 --- a/src/i18n/locales/es.js +++ b/src/i18n/locales/es.js @@ -165,7 +165,7 @@ const es = { no_values_for_field: "El campo '{{fieldName}}' de la tabla '{{tableName}}' es de tipo `{{type}}` pero no se han especificado valores", default_doesnt_match_type: - "El valor predeterminado para el campo '{{fieldName}}' en la tabla '{{table.name}}' no coincide con su tipo", + "El valor predeterminado para el campo '{{fieldName}}' en la tabla '{{tableName}}' no coincide con su tipo", not_null_is_null: "El campo '{{fieldName}}' de la tabla '{{tableName}}' es NOT NULL pero tiene NULL por defecto", duplicate_fields: diff --git a/src/i18n/locales/fa.js b/src/i18n/locales/fa.js index 4e4c4414..b7214458 100644 --- a/src/i18n/locales/fa.js +++ b/src/i18n/locales/fa.js @@ -156,7 +156,7 @@ const fa = { empty_field_name: "فیلد خالی name در جدول '{{tableName}}'", empty_field_type: "فیلد خالی type در جدول '{{tableName}}'", no_values_for_field: "فیلد '{{fieldName}}' جدول '{{tableName}}' از نوع {{type}} است اما هیچ مقداری مشخص نشده است", - default_doesnt_match_type: "مقدار پیش‌فرض برای فیلد '{{fieldName}}' در جدول '{{table.name}}' با نوع آن مطابقت ندارد", + default_doesnt_match_type: "مقدار پیش‌فرض برای فیلد '{{fieldName}}' در جدول '{{tableName}}' با نوع آن مطابقت ندارد", not_null_is_null: "فیلد '{{fieldName}}' جدول '{{tableName}}' غیر خالی است اما پیش‌فرض آن خالی است", duplicate_fields: "فیلدهای تکراری جدول به نام '{{fieldName}}' در جدول '{{tableName}}'", duplicate_index: "شاخص تکراری به نام '{{indexName}}' در جدول '{{tableName}}'", @@ -198,9 +198,8 @@ const fa = { edit_relationship: "{{extra}} ویرایش ارتباط {{refName}}", delete_relationship: "حذف ارتباط {{refName}}", not_found: "یافت نشد" - + }, }; - + export { fa,persian }; - \ No newline at end of file diff --git a/src/i18n/locales/pt-br.js b/src/i18n/locales/pt-br.js index 9368201a..008bdff8 100644 --- a/src/i18n/locales/pt-br.js +++ b/src/i18n/locales/pt-br.js @@ -165,7 +165,7 @@ const pt = { no_values_for_field: "O campo '{{fieldName}}' da tabela '{{tableName}}' é do tipo `{{type}}`, mas nenhum valor foi especificado", default_doesnt_match_type: - "O valor padrão para o campo '{{fieldName}}' na tabela '{{table.name}}' não corresponde ao seu tipo", + "O valor padrão para o campo '{{fieldName}}' na tabela '{{tableName}}' não corresponde ao seu tipo", not_null_is_null: "O campo '{{fieldName}}' da tabela '{{tableName}}' é NOT NULL mas tem o valor padrão NULL", duplicate_fields: diff --git a/src/i18n/locales/vi.js b/src/i18n/locales/vi.js index 42662e47..bcfef787 100644 --- a/src/i18n/locales/vi.js +++ b/src/i18n/locales/vi.js @@ -112,7 +112,7 @@ const vi = { size: "Kích cỡ", precision: "Độ chính xác", set_precision: "Đặt độ chính xác: (kích thước, chữ số)", - use_for_batch_input: "Sử dụng , để nhập hàng loạt", + use_for_batch_input: "Sử dụng để nhập hàng loạt", indices: "Chỉ số", add_index: "Thêm chỉ mục", select_fields: "Chọn các trường", @@ -123,9 +123,9 @@ const vi = { on_update: "Khi cập nhật", on_delete: "Khi xóa", swap: "Tráo đổi", - one_to_one: "Một đối một", - one_to_many: "Một đến nhiều", - many_to_one: "Nhiều thành một", + one_to_one: "Một với một", + one_to_many: "Một với nhiều", + many_to_one: "Nhiều với một", content: "Nội dung", types_info: "Tính năng này dành cho các DBMS quan hệ đối tượng như PostgreSQL.\nNếu được sử dụng cho MySQL hoặc MariaDB, loại JSON sẽ được tạo bằng kiểm tra xác thực json tương ứng.\nNếu được sử dụng cho SQLite, nó sẽ được dịch sang BLOB.\nNếu được sử dụng cho MSSQL một bí danh loại cho trường đầu tiên sẽ được tạo.", table_deleted: "Đã xóa bảng", @@ -156,7 +156,7 @@ const vi = { empty_field_name: "Trường trống `name` trong bảng '{{tableName}}'", empty_field_type: "Trường trống `loại` trong bảng '{{tableName}}'", no_values_for_field: "Trường '{{fieldName}}' của bảng '{{tableName}}' thuộc loại `{{type}}` nhưng không có giá trị nào được chỉ định", - default_doesnt_match_type: "Giá trị mặc định cho trường '{{fieldName}}' trong bảng '{{table.name}}' không khớp với loại của nó", + default_doesnt_match_type: "Giá trị mặc định cho trường '{{fieldName}}' trong bảng '{{tableName}}' không khớp với loại của nó", not_null_is_null: "Trường '{{fieldName}}' của bảng '{{tableName}}' là NOT NULL nhưng đang là NULL", duplicate_fields: "Các trường bảng trùng lặp theo tên '{{fieldName}}' trong bảng '{{tableName}}'", duplicate_index: "Chỉ mục trùng lặp theo tên '{{indexName}}' trong bảng '{{tableName}}'", @@ -198,6 +198,21 @@ const vi = { edit_relationship: "{{extra}} Chỉnh sửa quan hệ {{refName}}", delete_relationship: "Xóa quan hệ {{refName}}", not_found: "Không tìm thấy", + export_pdf: "Xuất pdf", + export_json: "Xuất json", + export_diagram: "Xuất sơ đồ", + export_excel: "Xuất excel", + format: "Định dạng", + fields: "Trường", + source_table: "Bảng gốc", + source_field: "Trường bảng gốc", + foreign_table: "Bảng ngoại", + foreign_field: "Trường bảng ngoại", + update_constraint: "Hạn chế cập nhật", + delete_constraint: "Hạn chế xoá", + yes: "Có", + diagram: "Sơ đồ", + add_diagram: "Thêm sơ đồ" }, }; diff --git a/src/utils/modalTitles.js b/src/utils/modalTitles.js deleted file mode 100644 index 6501aa08..00000000 --- a/src/utils/modalTitles.js +++ /dev/null @@ -1,49 +0,0 @@ -import { MODAL } from "../data/constants"; -import i18n from "../i18n/i18n"; - -export const getModalTitle = (modal) => { - switch (modal) { - case MODAL.IMPORT: - case MODAL.IMPORT_SRC: - return i18n.t("import_diagram"); - case MODAL.CODE: - return i18n.t("export_source"); - case MODAL.IMG: - return i18n.t("export_image"); - case MODAL.RENAME: - return i18n.t("rename_diagram"); - case MODAL.OPEN: - return i18n.t("open_diagram"); - case MODAL.SAVEAS: - return i18n.t("save_as"); - case MODAL.NEW: - return i18n.t("create_new_diagram"); - case MODAL.TABLE_WIDTH: - return i18n.t("table_width"); - case MODAL.LANGUAGE: - return i18n.t("language"); - default: - return ""; - } -}; - -export const getOkText = (modal) => { - switch (modal) { - case MODAL.IMPORT: - case MODAL.IMPORT_SRC: - return i18n.t("import"); - case MODAL.CODE: - case MODAL.IMG: - return i18n.t("export"); - case MODAL.RENAME: - return i18n.t("rename"); - case MODAL.OPEN: - return i18n.t("open"); - case MODAL.SAVEAS: - return i18n.t("save_as"); - case MODAL.NEW: - return i18n.t("create"); - default: - return i18n.t("confirm"); - } -}; diff --git a/src/utils/toDocument.js b/src/utils/toDocument.js new file mode 100644 index 00000000..4e8c1874 --- /dev/null +++ b/src/utils/toDocument.js @@ -0,0 +1,176 @@ +import Excel from "exceljs"; +import { getTypeString } from "./toSQL"; +import { toPng } from "html-to-image"; +import i18n from "../i18n/i18n"; + +const excelTableTheme = "TableStyleLight9"; + +export async function jsonToExcel(data) { + const labels = { + tables: i18n.t("tables"), + relationships: i18n.t("relationships"), + diagram: i18n.t("diagram"), + fields: i18n.t("fields"), + indices: i18n.t("indices"), + name: i18n.t("name"), + cardinality: i18n.t("cardinality"), + type: i18n.t("type"), + not_null: i18n.t("not_null"), + primary: i18n.t("primary"), + default_value: i18n.t("default_value"), + unique: i18n.t("unique"), + autoincrement: i18n.t("autoincrement"), + comment: i18n.t("comment"), + yes: i18n.t("yes"), + source_table: i18n.t("source_table"), + source_field: i18n.t("source_field"), + foreign_table: i18n.t("foreign_table"), + foreign_field: i18n.t("foreign_field"), + update_constraint: i18n.t("update_constraint"), + delete_constraint: i18n.t("delete_constraint"), + }; + + const workbook = new Excel.Workbook(); + + // create sheet for list tables and relationships + const listTableSheet = workbook.addWorksheet(labels.tables); + listTableSheet.addTable({ + name: "Tables", + ref: "A1", + style: { + theme: excelTableTheme, + showRowStripes: true, + }, + columns: [{ name: "#" }, { name: labels.name }, { name: labels.comment }], + rows: data.tables.map((table, index) => [ + index + 1, + table.name, + table.comment, + ]), + }); + + if (data.relationships.length) { + const relationSheet = workbook.addWorksheet(labels.relationships); + relationSheet.addTable({ + name: "Relations", + ref: "A1", + style: { + theme: excelTableTheme, + showRowStripes: true, + }, + columns: [ + { name: "#" }, + { name: labels.name }, + { name: labels.cardinality }, + { name: labels.source_table }, + { name: labels.source_field }, + { name: labels.foreign_table }, + { name: labels.foreign_field }, + { name: labels.update_constraint }, + { name: labels.delete_constraint }, + ], + rows: data.relationships.map((r, index) => [ + index + 1, + r.name, + i18n.t(r.cardinality.toLowerCase().replaceAll(" ", "_")), + data.tables[r.startTableId].name, + data.tables[r.startTableId].fields[r.startFieldId].name, + data.tables[r.endTableId].name, + data.tables[r.endTableId].fields[r.endFieldId].name, + r.updateConstraint.toUpperCase(), + r.deleteConstraint.toUpperCase(), + ]), + }); + } + + // create ER diagram sheet + if (data.hasDiagram) { + const canvasElm = document.getElementById("canvas"); + const canvasBoundary = canvasElm.getBoundingClientRect(); + const diagramImageId = workbook.addImage({ + base64: await toPng(canvasElm), + extension: 'png', + }); + workbook.addWorksheet(labels.diagram).addImage(diagramImageId, { + tl: { + col: 0, + row: 0 + }, + ext: { + width: parseInt(canvasBoundary.width), + height: parseInt(canvasBoundary.height) + } + }); + } + // create table sheet + data.tables.map((table, index) => { + const tableSheet = workbook.addWorksheet(table.name); + + tableSheet.getRow(1).getCell(1).value = labels.fields; + tableSheet.getRow(1).getCell(1).style = { font: { bold: true } }; + + tableSheet.addTable({ + name: `FieldTable${index}`, + ref: "A2", + style: { + theme: excelTableTheme, + showRowStripes: true, + }, + columns: [ + { name: "#" }, + { name: labels.name }, + { name: labels.type }, + { name: labels.not_null }, + { name: labels.default_value }, + { name: labels.primary }, + { name: labels.unique }, + { name: labels.autoincrement }, + { name: labels.comment }, + ], + rows: table.fields.map((field, index) => [ + index + 1, + field.name, + getTypeString(field, data.dbms), + field.notNull ? labels.yes : "", + field.default, + field.primary ? labels.yes : "", + field.unique ? labels.yes : "", + field.increment ? labels.yes : "", + field.comment, + ]), + }); + + if (table.indices.length) { + // add 1 empty line to separate tables + const lastRowIdx = tableSheet.rowCount + 1; + + tableSheet.getRow(lastRowIdx + 1).getCell(1).value = labels.indices; + tableSheet.getRow(lastRowIdx + 1).getCell(1).style = { + font: { bold: true }, + }; + + tableSheet.addTable({ + name: `IndexTable${index}`, + ref: `A${lastRowIdx + 2}`, + style: { + theme: excelTableTheme, + showRowStripes: true, + }, + columns: [ + { name: "#" }, + { name: labels.name }, + { name: labels.fields }, + { name: labels.unique }, + ], + rows: table.indices.map((indexData, index) => [ + index + 1, + indexData.name, + indexData.fields.join(", "), + indexData.unique ? labels.yes : "", + ]), + }); + } + }); + + return await workbook.xlsx.writeBuffer(); +} diff --git a/src/utils/toSQL.js b/src/utils/toSQL.js index 2ad49616..edae5bc3 100644 --- a/src/utils/toSQL.js +++ b/src/utils/toSQL.js @@ -39,58 +39,61 @@ export function generateSchema(type) { )}\n\t\t\t},\n\t\t\t"additionalProperties": false\n\t\t}`; } -export function getTypeString(field, dbms = "mysql", baseType = false) { - if (dbms === "mysql") { - if (field.type === "UUID") { - return `VARCHAR(36)`; - } - if (hasPrecision(field.type) || isSized(field.type)) { - return `${field.type}${field.size ? `(${field.size})` : ""}`; - } - if (field.type === "SET" || field.type === "ENUM") { - return `${field.type}(${field.values.map((v) => `"${v}"`).join(", ")})`; - } - if (!sqlDataTypes.includes(field.type)) { - return "JSON"; - } - return field.type; - } else if (dbms === "postgres") { - if (field.type === "SMALLINT" && field.increment) { - return "smallserial"; - } - if (field.type === "INT" && field.increment) { - return "serial"; - } - if (field.type === "BIGINT" && field.increment) { - return "bigserial"; - } - if (field.type === "ENUM") { - return `${field.name}_t`; - } - if (field.type === "SET") { - return `${field.name}_t[]`; - } - if (field.type === "TIMESTAMP") { - return "TIMESTAMPTZ"; - } - if (field.type === "DATETIME") { - return `timestamp`; - } - if (isSized(field.type)) { - const type = - field.type === "BINARY" - ? "bit" - : field.type === "VARBINARY" - ? "bit varying" - : field.type.toLowerCase(); - return `${type}(${field.size})`; - } - if (hasPrecision(field.type) && field.size !== "") { - return `${field.type}${field.size}`; - } - return field.type.toLowerCase(); - } else if (dbms === "mssql") { - let type = field.type; +export function getMysqlType(field) { + if (field.type === "UUID") { + return `VARCHAR(36)`; + } + if (hasPrecision(field.type) || isSized(field.type)) { + return `${field.type}${field.size ? `(${field.size})` : ""}`; + } + if (field.type === "SET" || field.type === "ENUM") { + return `${field.type}(${field.values.map((v) => `"${v}"`).join(", ")})`; + } + if (!sqlDataTypes.includes(field.type)) { + return "JSON"; + } + return field.type; +} + +export function getPostgreSQLType(field) { + if (field.type === "SMALLINT" && field.increment) { + return "smallserial"; + } + if (field.type === "INT" && field.increment) { + return "serial"; + } + if (field.type === "BIGINT" && field.increment) { + return "bigserial"; + } + if (field.type === "ENUM") { + return `${field.name}_t`; + } + if (field.type === "SET") { + return `${field.name}_t[]`; + } + if (field.type === "TIMESTAMP") { + return "TIMESTAMPTZ"; + } + if (field.type === "DATETIME") { + return `timestamp`; + } + if (isSized(field.type)) { + const type = + field.type === "BINARY" + ? "bit" + : field.type === "VARBINARY" + ? "bit varying" + : field.type.toLowerCase(); + return `${type}(${field.size})`; + } + if (hasPrecision(field.type) && field.size !== "") { + return `${field.type}${field.size}`; + } + return field.type.toLowerCase(); +} + +export function getMSSQLType(field, baseType = false) { + let type = field.type; switch (field.type) { case "ENUM": return baseType @@ -126,9 +129,55 @@ export function getTypeString(field, dbms = "mysql", baseType = false) { } return type; +} + +export function getSQLiteType(field) { + switch (field.type) { + case "INT": + case "SMALLINT": + case "BIGINT": + case "BOOLEAN": + return "INTEGER"; + case "DECIMAL": + case "NUMERIC": + case "FLOAT": + case "DOUBLE": + case "REAL": + return "REAL"; + case "CHAR": + case "VARCHAR": + case "UUID": + case "TEXT": + case "DATE": + case "TIME": + case "TIMESTAMP": + case "DATETIME": + case "BINARY": + case "VARBINARY": + return "TEXT"; + case "ENUM": + return `TEXT CHECK("${field.name}" in (${field.values + .map((v) => `'${v}'`) + .join(", ")}))`; + default: + return "BLOB"; } } +export function getTypeString(field, dbms = "mysql", baseType = false) { + if (dbms === "mysql" || dbms === "mariadb") { + return getMysqlType(field); + } else if (dbms === "postgressql") { + return getPostgreSQLType(field); + } else if (dbms === "mssql") { + return getMSSQLType(field, baseType) + } else if (dbms === "sqlite") { + return getSQLiteType(field); + } + + return field.type; +} + export function hasQuotes(type) { return [ "CHAR", @@ -233,14 +282,14 @@ export function jsonToPostgreSQL(obj) { `${ type.comment === "" ? "" : `/**\n${type.comment}\n*/\n` }CREATE TYPE ${type.name} AS (\n${type.fields - .map((f) => `\t${f.name} ${getTypeString(f, "postgres")}`) + .map((f) => `\t${f.name} ${getTypeString(f, "postgressql")}`) .join("\n")}\n);` ); } else { return `${ type.comment === "" ? "" : `/**\n${type.comment}\n*/\n` }CREATE TYPE ${type.name} AS (\n${type.fields - .map((f) => `\t${f.name} ${getTypeString(f, "postgres")}`) + .map((f) => `\t${f.name} ${getTypeString(f, "postgressql")}`) .join("\n")}\n);`; } })}\n${obj.tables @@ -263,7 +312,7 @@ export function jsonToPostgreSQL(obj) { (field) => `${field.comment === "" ? "" : `\t-- ${field.comment}\n`}\t"${ field.name - }" ${getTypeString(field, "postgres")}${ + }" ${getTypeString(field, "postgressql")}${ field.notNull ? " NOT NULL" : "" }${ field.default !== "" ? ` DEFAULT ${parseDefault(field)}` : "" @@ -305,39 +354,6 @@ export function jsonToPostgreSQL(obj) { .join("\n")}`; } -export function getSQLiteType(field) { - switch (field.type) { - case "INT": - case "SMALLINT": - case "BIGINT": - case "BOOLEAN": - return "INTEGER"; - case "DECIMAL": - case "NUMERIC": - case "FLOAT": - case "DOUBLE": - case "REAL": - return "REAL"; - case "CHAR": - case "VARCHAR": - case "UUID": - case "TEXT": - case "DATE": - case "TIME": - case "TIMESTAMP": - case "DATETIME": - case "BINARY": - case "VARBINARY": - return "TEXT"; - case "ENUM": - return `TEXT CHECK("${field.name}" in (${field.values - .map((v) => `'${v}'`) - .join(", ")}))`; - default: - return "BLOB"; - } -} - export function getInlineFK(table, obj) { let fk = ""; obj.references.forEach((r) => { @@ -364,7 +380,7 @@ export function jsonToSQLite(obj) { (field) => `${field.comment === "" ? "" : `\t-- ${field.comment}\n`}\t"${ field.name - }" ${getSQLiteType(field)}${field.notNull ? " NOT NULL" : ""}${ + }" ${getTypeString(field, "sqlite")}${field.notNull ? " NOT NULL" : ""}${ field.unique ? " UNIQUE" : "" }${field.default !== "" ? ` DEFAULT ${parseDefault(field)}` : ""}${ field.check === "" || !hasCheck(field.type) @@ -458,7 +474,7 @@ export function jsonToMariaDB(obj) { .join("\n")}`; } -export function jsonToSQLServer(obj) { +export function jsonToMSSQL(obj) { return `${obj.types .map((type) => { return `${ @@ -560,3 +576,11 @@ export function getSize(type) { return ""; } } + +export default { + jsonToMySQL, + jsonToMariaDB, + jsonToPostgreSQL, + jsonToSQLite, + jsonToMSSQL, +}