Guide generator (#231)

* Replace sw, sns battlesuit images

* Add gen guide page

* Make supports configurable

* Extract DifficultySelect & Improve Ex Signet Ordering style

* Extract BattlesuitSelect

* Use BattlesuitSelect for support valk

* Swap support if duplicated

* Add sigil selector

* Set initial ex signets

* Change layout

* Add sigil icon to single value

* Add equipment select

* Fix difficulty box label

* Implement SignetBox

* Add Signet form control

* Improve rendering perf of stigma and weapon select

* Implement image converter

* Fix signet select

* Remove FontHead

* Remove wrong new line

* Fix warnings

* Add custom style configurator

* Make labels of DiffucltyBox and ValkBox configurable

* Add background and improve layout

* Improve ex signet fontsize

* Style boundaries of left bottom boxes

* Apply fonts directly

* Style signet box

* Add Signet group label

* Style ValkBox

* Add support valk label

* Style layout of SignetBox

* Add shadow to difficulty box

* Add sigil label

* Adjust signet group label

* Add equipment label

* Configure colors of left bottom boxes

* Add tag, rank, signature

* Add icon to current value of battlesuit select

* Adjust form styles

* Add signet group select

* Implement import/export data

* Fix dashed border of difficulty box

* Add gen link to elysian realm home

* Prefix types
This commit is contained in:
Sakura Knoll
2022-08-29 20:08:19 +09:00
committed by GitHub
Unverified
parent 2dd48df340
commit 5c8ed4f603
28 changed files with 3064 additions and 180 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 380 KiB

After

Width:  |  Height:  |  Size: 445 KiB

+4 -8
View File
@@ -16,14 +16,12 @@
- name: Active Skill (CN Live Data) - name: Active Skill (CN Live Data)
krName: 액티브 스킬 (CN Live Data) krName: 액티브 스킬 (CN Live Data)
description: |- description: |-
[SP Cost: 0][CD: 0] Use the bow to aim and shoot (max aiming duration: [SP Cost: 0][CD: 0] Use the bow to aim and shoot (max aiming duration: 3s), dealing 675.0% ATK of Ice DMG.
3s), dealing 675.0% ATK of Ice DMG.
Herrscher of Human:Ego equip bonus: Inflicts Ego Blossom to the target, which makes the enemy to take 15.0% more Ice DMG for 10s (can be refreshed). In Herrscher of Human form, inflicting Ego Blossom deals 10 x 20.0% ATK of Ice DMG. Herrscher of Human:Ego equip bonus: Inflicts Ego Blossom to the target, which makes the enemy to take 15.0% more Ice DMG for 10s (can be refreshed). In Herrscher of Human form, inflicting Ego Blossom deals 10 x 20.0% ATK of Ice DMG.
The weapon active's CD and charging time are based on the shooting mode. The weapon active's CD and charging time are based on the shooting mode.
krDescription: |- krDescription: |-
[SP 소모: 0][재사용 대기 시간: 0초] 화살로 적을 조준 사격해(최대 3초 동안 조준), 목표에게 [SP 소모: 0][재사용 대기 시간: 0초] 화살로 적을 조준 사격해(최대 3초 동안 조준), 목표에게 675.0%의 빙결 대미지를 가한다.
675.0%의 빙결 대미지를 가한다.
진아·인류의 율자 장착 보너스: 진아 개화 효과를 목표에 발동시키며, 10초간 적이 받는 빙결 피해 15.0% 증가 (갱신 가능). 인간의 율자 형태에서, 진아 개화 발동 시, 공격력 10 x 20.0%의 빙결 대미지를 가한다. 진아·인류의 율자 장착 보너스: 진아 개화 효과를 목표에 발동시키며, 10초간 적이 받는 빙결 피해 15.0% 증가 (갱신 가능). 인간의 율자 형태에서, 진아 개화 발동 시, 공격력 10 x 20.0%의 빙결 대미지를 가한다.
무기 액티브 스킬의 재사용 대기시간과 충전 시간은 장착 캐릭터의 사격 유형에 따라 결정된다. 무기 액티브 스킬의 재사용 대기시간과 충전 시간은 장착 캐릭터의 사격 유형에 따라 결정된다.
@@ -60,14 +58,12 @@
- name: Active Skill (CN Live Data) - name: Active Skill (CN Live Data)
krName: 액티브 스킬 (CN Live Data) krName: 액티브 스킬 (CN Live Data)
description: |- description: |-
[SP Cost: 0][CD: 0] Use the bow to aim and shoot (max aiming duration: [SP Cost: 0][CD: 0] Use the bow to aim and shoot (max aiming duration: 3s), dealing 550.0% ATK of Ice DMG.
3s), dealing 550.0% ATK of Ice DMG.
Herrscher of Human:Ego equip bonus: Inflicts Ego Blossom to the target, which makes the enemy to take 12.0% more Ice DMG for 10s (can be refreshed). In Herrscher of Human form, inflicting Ego Blossom deals 10 x 15.0% ATK of Ice DMG. Herrscher of Human:Ego equip bonus: Inflicts Ego Blossom to the target, which makes the enemy to take 12.0% more Ice DMG for 10s (can be refreshed). In Herrscher of Human form, inflicting Ego Blossom deals 10 x 15.0% ATK of Ice DMG.
The weapon active's CD and charging time are based on the shooting mode. The weapon active's CD and charging time are based on the shooting mode.
krDescription: |- krDescription: |-
[SP 소모: 0][재사용 대기 시간: 0초] 화살로 적을 조준 사격해(최대 3초 동안 조준), 목표에게 [SP 소모: 0][재사용 대기 시간: 0초] 화살로 적을 조준 사격해(최대 3초 동안 조준), 목표에게 555.0%의 빙결 대미지를 가한다.
555.0%의 빙결 대미지를 가한다.
진아·인류의 율자 장착 보너스: 진아 개화 효과를 목표에 발동시키며, 10초간 적이 받는 빙결 피해 12.0% 증가 (갱신 가능). 인간의 율자 형태에서, 진아 개화 발동 시, 공격력 10 x 15.0%의 빙결 대미지를 가한다. 진아·인류의 율자 장착 보너스: 진아 개화 효과를 목표에 발동시키며, 10초간 적이 받는 빙결 피해 12.0% 증가 (갱신 가능). 인간의 율자 형태에서, 진아 개화 발동 시, 공격력 10 x 15.0%의 빙결 대미지를 가한다.
무기 액티브 스킬의 재사용 대기시간과 충전 시간은 장착 캐릭터의 사격 유형에 따라 결정된다. 무기 액티브 스킬의 재사용 대기시간과 충전 시간은 장착 캐릭터의 사격 유형에 따라 결정된다.
+452 -170
View File
@@ -9,9 +9,14 @@
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@dnd-kit/core": "^6.0.5",
"@dnd-kit/sortable": "^7.0.1",
"@dnd-kit/utilities": "^3.2.0",
"@mdi/js": "^6.5.95", "@mdi/js": "^6.5.95",
"@mdi/react": "^1.5.0", "@mdi/react": "^1.5.0",
"@types/file-saver": "^2.0.5",
"date-fns": "^2.27.0", "date-fns": "^2.27.0",
"html-to-image": "^1.10.6",
"next": "^12.1.6", "next": "^12.1.6",
"next-i18next": "^10.1.0", "next-i18next": "^10.1.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
@@ -20,6 +25,7 @@
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-indiana-drag-scroll": "^2.1.0", "react-indiana-drag-scroll": "^2.1.0",
"react-select": "^5.4.0",
"theme-ui": "^0.12.1", "theme-ui": "^0.12.1",
"yaml": "^2.1.1" "yaml": "^2.1.1"
}, },
@@ -31,6 +37,7 @@
"cheerio": "^1.0.0-rc.10", "cheerio": "^1.0.0-rc.10",
"eslint": "8.5.0", "eslint": "8.5.0",
"eslint-config-next": "12.0.7", "eslint-config-next": "12.0.7",
"file-saver": "^2.0.5",
"filenamify": "^4.3.0", "filenamify": "^4.3.0",
"http-server": "^14.1.0", "http-server": "^14.1.0",
"node-fetch": "^2.6.7", "node-fetch": "^2.6.7",
@@ -249,9 +256,9 @@
} }
}, },
"node_modules/@babel/helper-plugin-utils": { "node_modules/@babel/helper-plugin-utils": {
"version": "7.14.5", "version": "7.18.9",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz",
"integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==", "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==",
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
@@ -337,11 +344,11 @@
} }
}, },
"node_modules/@babel/plugin-syntax-jsx": { "node_modules/@babel/plugin-syntax-jsx": {
"version": "7.14.5", "version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.14.5.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz",
"integrity": "sha512-ohuFIsOMXJnbOMRfX7/w7LocdR6R7whhuRD4ax8IipLcLPlZGJKkBxgHp++U4N/vKyU16/YDQr2f5seajD3jIw==", "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==",
"dependencies": { "dependencies": {
"@babel/helper-plugin-utils": "^7.14.5" "@babel/helper-plugin-utils": "^7.18.6"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@@ -351,9 +358,9 @@
} }
}, },
"node_modules/@babel/runtime": { "node_modules/@babel/runtime": {
"version": "7.15.4", "version": "7.18.9",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz",
"integrity": "sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==", "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==",
"dependencies": { "dependencies": {
"regenerator-runtime": "^0.13.4" "regenerator-runtime": "^0.13.4"
}, },
@@ -474,18 +481,87 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/@emotion/babel-plugin": { "node_modules/@dnd-kit/accessibility": {
"version": "11.7.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.7.1.tgz", "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.0.1.tgz",
"integrity": "sha512-K3/6Y+J/sIAjplf3uIteWLhPuOyuMNnE+iyYnTF/m294vc6IL90kTHp7y8ldZYbpKlP17rpOWDKM9DvTcrOmNQ==", "integrity": "sha512-HXRrwS9YUYQO9lFRc/49uO/VICbM+O+ZRpFDe9Pd1rwVv2PCNkRiTZRdxrDgng/UkvdC3Re9r2vwPpXXrWeFzg==",
"dependencies": { "dependencies": {
"@babel/helper-module-imports": "^7.12.13", "tslib": "^2.0.0"
"@babel/plugin-syntax-jsx": "^7.12.13", },
"@babel/runtime": "^7.13.10", "peerDependencies": {
"@emotion/hash": "^0.8.0", "react": ">=16.8.0"
"@emotion/memoize": "^0.7.5", }
"@emotion/serialize": "^1.0.2", },
"babel-plugin-macros": "^2.6.1", "node_modules/@dnd-kit/accessibility/node_modules/tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
},
"node_modules/@dnd-kit/core": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.0.5.tgz",
"integrity": "sha512-3nL+Zy5cT+1XwsWdlXIvGIFvbuocMyB4NBxTN74DeBaBqeWdH9JsnKwQv7buZQgAHmAH+eIENfS1ginkvW6bCw==",
"dependencies": {
"@dnd-kit/accessibility": "^3.0.0",
"@dnd-kit/utilities": "^3.2.0",
"tslib": "^2.0.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@dnd-kit/core/node_modules/tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
},
"node_modules/@dnd-kit/sortable": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-7.0.1.tgz",
"integrity": "sha512-n77qAzJQtMMywu25sJzhz3gsHnDOUlEjTtnRl8A87rWIhnu32zuP+7zmFjwGgvqfXmRufqiHOSlH7JPC/tnJ8Q==",
"dependencies": {
"@dnd-kit/utilities": "^3.2.0",
"tslib": "^2.0.0"
},
"peerDependencies": {
"@dnd-kit/core": "^6.0.4",
"react": ">=16.8.0"
}
},
"node_modules/@dnd-kit/sortable/node_modules/tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
},
"node_modules/@dnd-kit/utilities": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.0.tgz",
"integrity": "sha512-h65/pn2IPCCIWwdlR2BMLqRkDxpTEONA+HQW3n765HBijLYGyrnTCLa2YQt8VVjjSQD6EfFlTE6aS2Q/b6nb2g==",
"dependencies": {
"tslib": "^2.0.0"
},
"peerDependencies": {
"react": ">=16.8.0"
}
},
"node_modules/@dnd-kit/utilities/node_modules/tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
},
"node_modules/@emotion/babel-plugin": {
"version": "11.10.2",
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.2.tgz",
"integrity": "sha512-xNQ57njWTFVfPAc3cjfuaPdsgLp5QOSuRsj9MA6ndEhH/AzuZM86qIQzt6rq+aGBwj3n5/TkLmU5lhAfdRmogA==",
"dependencies": {
"@babel/helper-module-imports": "^7.16.7",
"@babel/plugin-syntax-jsx": "^7.17.12",
"@babel/runtime": "^7.18.3",
"@emotion/hash": "^0.9.0",
"@emotion/memoize": "^0.8.0",
"@emotion/serialize": "^1.1.0",
"babel-plugin-macros": "^3.1.0",
"convert-source-map": "^1.5.0", "convert-source-map": "^1.5.0",
"escape-string-regexp": "^4.0.0", "escape-string-regexp": "^4.0.0",
"find-root": "^1.1.0", "find-root": "^1.1.0",
@@ -496,6 +572,11 @@
"@babel/core": "^7.0.0" "@babel/core": "^7.0.0"
} }
}, },
"node_modules/@emotion/babel-plugin/node_modules/@emotion/memoize": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz",
"integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA=="
},
"node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": { "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -521,26 +602,31 @@
"integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==" "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
}, },
"node_modules/@emotion/cache": { "node_modules/@emotion/cache": {
"version": "11.7.1", "version": "11.10.3",
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.7.1.tgz", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.10.3.tgz",
"integrity": "sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==", "integrity": "sha512-Psmp/7ovAa8appWh3g51goxu/z3iVms7JXOreq136D8Bbn6dYraPnmL6mdM8GThEx9vwSn92Fz+mGSjBzN8UPQ==",
"dependencies": { "dependencies": {
"@emotion/memoize": "^0.7.4", "@emotion/memoize": "^0.8.0",
"@emotion/sheet": "^1.1.0", "@emotion/sheet": "^1.2.0",
"@emotion/utils": "^1.0.0", "@emotion/utils": "^1.2.0",
"@emotion/weak-memoize": "^0.2.5", "@emotion/weak-memoize": "^0.3.0",
"stylis": "4.0.13" "stylis": "4.0.13"
} }
}, },
"node_modules/@emotion/cache/node_modules/@emotion/memoize": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz",
"integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA=="
},
"node_modules/@emotion/cache/node_modules/stylis": { "node_modules/@emotion/cache/node_modules/stylis": {
"version": "4.0.13", "version": "4.0.13",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
"integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==" "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
}, },
"node_modules/@emotion/hash": { "node_modules/@emotion/hash": {
"version": "0.8.0", "version": "0.9.0",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz",
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" "integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ=="
}, },
"node_modules/@emotion/is-prop-valid": { "node_modules/@emotion/is-prop-valid": {
"version": "1.1.1", "version": "1.1.1",
@@ -556,16 +642,16 @@
"integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==" "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ=="
}, },
"node_modules/@emotion/react": { "node_modules/@emotion/react": {
"version": "11.7.1", "version": "11.10.0",
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.7.1.tgz", "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.0.tgz",
"integrity": "sha512-DV2Xe3yhkF1yT4uAUoJcYL1AmrnO5SVsdfvu+fBuS7IbByDeTVx9+wFmvx9Idzv7/78+9Mgx2Hcmr7Fex3tIyw==", "integrity": "sha512-K6z9zlHxxBXwN8TcpwBKcEsBsOw4JWCCmR+BeeOWgqp8GIU1yA2Odd41bwdAAr0ssbQrbJbVnndvv7oiv1bZeQ==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.13.10", "@babel/runtime": "^7.18.3",
"@emotion/cache": "^11.7.1", "@emotion/babel-plugin": "^11.10.0",
"@emotion/serialize": "^1.0.2", "@emotion/cache": "^11.10.0",
"@emotion/sheet": "^1.1.0", "@emotion/serialize": "^1.1.0",
"@emotion/utils": "^1.0.0", "@emotion/utils": "^1.2.0",
"@emotion/weak-memoize": "^0.2.5", "@emotion/weak-memoize": "^0.3.0",
"hoist-non-react-statics": "^3.3.1" "hoist-non-react-statics": "^3.3.1"
}, },
"peerDependencies": { "peerDependencies": {
@@ -582,21 +668,26 @@
} }
}, },
"node_modules/@emotion/serialize": { "node_modules/@emotion/serialize": {
"version": "1.0.2", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz", "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.0.tgz",
"integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==", "integrity": "sha512-F1ZZZW51T/fx+wKbVlwsfchr5q97iW8brAnXmsskz4d0hVB4O3M/SiA3SaeH06x02lSNzkkQv+n3AX3kCXKSFA==",
"dependencies": { "dependencies": {
"@emotion/hash": "^0.8.0", "@emotion/hash": "^0.9.0",
"@emotion/memoize": "^0.7.4", "@emotion/memoize": "^0.8.0",
"@emotion/unitless": "^0.7.5", "@emotion/unitless": "^0.8.0",
"@emotion/utils": "^1.0.0", "@emotion/utils": "^1.2.0",
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
}, },
"node_modules/@emotion/serialize/node_modules/@emotion/memoize": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz",
"integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA=="
},
"node_modules/@emotion/sheet": { "node_modules/@emotion/sheet": {
"version": "1.1.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.0.tgz",
"integrity": "sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g==" "integrity": "sha512-OiTkRgpxescko+M51tZsMq7Puu/KP55wMT8BgpcXVG2hqXc0Vo0mfymJ/Uj24Hp0i083ji/o0aLddh08UEjq8w=="
}, },
"node_modules/@emotion/styled": { "node_modules/@emotion/styled": {
"version": "11.6.0", "version": "11.6.0",
@@ -624,19 +715,19 @@
} }
}, },
"node_modules/@emotion/unitless": { "node_modules/@emotion/unitless": {
"version": "0.7.5", "version": "0.8.0",
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz",
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw=="
}, },
"node_modules/@emotion/utils": { "node_modules/@emotion/utils": {
"version": "1.0.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.0.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.0.tgz",
"integrity": "sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA==" "integrity": "sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw=="
}, },
"node_modules/@emotion/weak-memoize": { "node_modules/@emotion/weak-memoize": {
"version": "0.2.5", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz",
"integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==" "integrity": "sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg=="
}, },
"node_modules/@eslint/eslintrc": { "node_modules/@eslint/eslintrc": {
"version": "1.0.5", "version": "1.0.5",
@@ -1293,6 +1384,11 @@
"integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==",
"dev": true "dev": true
}, },
"node_modules/@types/file-saver": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.5.tgz",
"integrity": "sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ=="
},
"node_modules/@types/hoist-non-react-statics": { "node_modules/@types/hoist-non-react-statics": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
@@ -1359,6 +1455,14 @@
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
}, },
"node_modules/@types/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz",
"integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==",
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/scheduler": { "node_modules/@types/scheduler": {
"version": "0.16.2", "version": "0.16.2",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
@@ -1733,13 +1837,17 @@
"dev": true "dev": true
}, },
"node_modules/babel-plugin-macros": { "node_modules/babel-plugin-macros": {
"version": "2.8.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
"integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.7.2", "@babel/runtime": "^7.12.5",
"cosmiconfig": "^6.0.0", "cosmiconfig": "^7.0.0",
"resolve": "^1.12.0" "resolve": "^1.19.0"
},
"engines": {
"node": ">=10",
"npm": ">=6"
} }
}, },
"node_modules/balanced-match": { "node_modules/balanced-match": {
@@ -1996,18 +2104,18 @@
} }
}, },
"node_modules/cosmiconfig": { "node_modules/cosmiconfig": {
"version": "6.0.0", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz",
"integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==",
"dependencies": { "dependencies": {
"@types/parse-json": "^4.0.0", "@types/parse-json": "^4.0.0",
"import-fresh": "^3.1.0", "import-fresh": "^3.2.1",
"parse-json": "^5.0.0", "parse-json": "^5.0.0",
"path-type": "^4.0.0", "path-type": "^4.0.0",
"yaml": "^1.7.2" "yaml": "^1.10.0"
}, },
"engines": { "engines": {
"node": ">=8" "node": ">=10"
} }
}, },
"node_modules/cosmiconfig/node_modules/yaml": { "node_modules/cosmiconfig/node_modules/yaml": {
@@ -2171,6 +2279,15 @@
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
"dependencies": {
"@babel/runtime": "^7.8.7",
"csstype": "^3.0.2"
}
},
"node_modules/dom-serializer": { "node_modules/dom-serializer": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
@@ -2663,18 +2780,6 @@
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
} }
}, },
"node_modules/eslint-plugin-jsx-a11y/node_modules/@babel/runtime": {
"version": "7.16.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.5.tgz",
"integrity": "sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA==",
"dev": true,
"dependencies": {
"regenerator-runtime": "^0.13.4"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/eslint-plugin-react": { "node_modules/eslint-plugin-react": {
"version": "7.27.1", "version": "7.27.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.27.1.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.27.1.tgz",
@@ -3050,6 +3155,12 @@
"node": "^10.12.0 || >=12.0.0" "node": "^10.12.0 || >=12.0.0"
} }
}, },
"node_modules/file-saver": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
"integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==",
"dev": true
},
"node_modules/filename-reserved-regex": { "node_modules/filename-reserved-regex": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",
@@ -3404,6 +3515,11 @@
"void-elements": "3.1.0" "void-elements": "3.1.0"
} }
}, },
"node_modules/html-to-image": {
"version": "1.10.6",
"resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.10.6.tgz",
"integrity": "sha512-+iFUWRJDmRRB92IxEAVoiuYQQXRp91yuI4Om7+lswwL3OOzoQ+H7laGLuZFK6ks2Z6Nz0wYm0UzahIO+D1Zxag=="
},
"node_modules/htmlparser2": { "node_modules/htmlparser2": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
@@ -4011,6 +4127,11 @@
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true "dev": true
}, },
"node_modules/memoize-one": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
},
"node_modules/memorystream": { "node_modules/memorystream": {
"version": "0.3.1", "version": "0.3.1",
"resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
@@ -4711,7 +4832,6 @@
"version": "15.7.2", "version": "15.7.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
"dev": true,
"dependencies": { "dependencies": {
"loose-envify": "^1.4.0", "loose-envify": "^1.4.0",
"object-assign": "^4.1.1", "object-assign": "^4.1.1",
@@ -4721,8 +4841,7 @@
"node_modules/prop-types/node_modules/react-is": { "node_modules/prop-types/node_modules/react-is": {
"version": "16.13.1", "version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
"dev": true
}, },
"node_modules/punycode": { "node_modules/punycode": {
"version": "2.1.1", "version": "2.1.1",
@@ -4838,6 +4957,39 @@
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0" "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0"
} }
}, },
"node_modules/react-select": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/react-select/-/react-select-5.4.0.tgz",
"integrity": "sha512-CjE9RFLUvChd5SdlfG4vqxZd55AZJRrLrHzkQyTYeHlpOztqcgnyftYAolJ0SGsBev6zAs6qFrjm6KU3eo2hzg==",
"dependencies": {
"@babel/runtime": "^7.12.0",
"@emotion/cache": "^11.4.0",
"@emotion/react": "^11.8.1",
"@types/react-transition-group": "^4.4.0",
"memoize-one": "^5.0.0",
"prop-types": "^15.6.0",
"react-transition-group": "^4.3.0"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
"dependencies": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2"
},
"peerDependencies": {
"react": ">=16.6.0",
"react-dom": ">=16.6.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",
@@ -5752,9 +5904,9 @@
} }
}, },
"@babel/helper-plugin-utils": { "@babel/helper-plugin-utils": {
"version": "7.14.5", "version": "7.18.9",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz",
"integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==" "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w=="
}, },
"@babel/helper-simple-access": { "@babel/helper-simple-access": {
"version": "7.18.6", "version": "7.18.6",
@@ -5813,17 +5965,17 @@
"peer": true "peer": true
}, },
"@babel/plugin-syntax-jsx": { "@babel/plugin-syntax-jsx": {
"version": "7.14.5", "version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.14.5.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz",
"integrity": "sha512-ohuFIsOMXJnbOMRfX7/w7LocdR6R7whhuRD4ax8IipLcLPlZGJKkBxgHp++U4N/vKyU16/YDQr2f5seajD3jIw==", "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==",
"requires": { "requires": {
"@babel/helper-plugin-utils": "^7.14.5" "@babel/helper-plugin-utils": "^7.18.6"
} }
}, },
"@babel/runtime": { "@babel/runtime": {
"version": "7.15.4", "version": "7.18.9",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz",
"integrity": "sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==", "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==",
"requires": { "requires": {
"regenerator-runtime": "^0.13.4" "regenerator-runtime": "^0.13.4"
} }
@@ -5914,18 +6066,81 @@
"@cspotcode/source-map-consumer": "0.8.0" "@cspotcode/source-map-consumer": "0.8.0"
} }
}, },
"@emotion/babel-plugin": { "@dnd-kit/accessibility": {
"version": "11.7.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.7.1.tgz", "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.0.1.tgz",
"integrity": "sha512-K3/6Y+J/sIAjplf3uIteWLhPuOyuMNnE+iyYnTF/m294vc6IL90kTHp7y8ldZYbpKlP17rpOWDKM9DvTcrOmNQ==", "integrity": "sha512-HXRrwS9YUYQO9lFRc/49uO/VICbM+O+ZRpFDe9Pd1rwVv2PCNkRiTZRdxrDgng/UkvdC3Re9r2vwPpXXrWeFzg==",
"requires": { "requires": {
"@babel/helper-module-imports": "^7.12.13", "tslib": "^2.0.0"
"@babel/plugin-syntax-jsx": "^7.12.13", },
"@babel/runtime": "^7.13.10", "dependencies": {
"@emotion/hash": "^0.8.0", "tslib": {
"@emotion/memoize": "^0.7.5", "version": "2.4.0",
"@emotion/serialize": "^1.0.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"babel-plugin-macros": "^2.6.1", "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
}
}
},
"@dnd-kit/core": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.0.5.tgz",
"integrity": "sha512-3nL+Zy5cT+1XwsWdlXIvGIFvbuocMyB4NBxTN74DeBaBqeWdH9JsnKwQv7buZQgAHmAH+eIENfS1ginkvW6bCw==",
"requires": {
"@dnd-kit/accessibility": "^3.0.0",
"@dnd-kit/utilities": "^3.2.0",
"tslib": "^2.0.0"
},
"dependencies": {
"tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
}
}
},
"@dnd-kit/sortable": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-7.0.1.tgz",
"integrity": "sha512-n77qAzJQtMMywu25sJzhz3gsHnDOUlEjTtnRl8A87rWIhnu32zuP+7zmFjwGgvqfXmRufqiHOSlH7JPC/tnJ8Q==",
"requires": {
"@dnd-kit/utilities": "^3.2.0",
"tslib": "^2.0.0"
},
"dependencies": {
"tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
}
}
},
"@dnd-kit/utilities": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.0.tgz",
"integrity": "sha512-h65/pn2IPCCIWwdlR2BMLqRkDxpTEONA+HQW3n765HBijLYGyrnTCLa2YQt8VVjjSQD6EfFlTE6aS2Q/b6nb2g==",
"requires": {
"tslib": "^2.0.0"
},
"dependencies": {
"tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
}
}
},
"@emotion/babel-plugin": {
"version": "11.10.2",
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.2.tgz",
"integrity": "sha512-xNQ57njWTFVfPAc3cjfuaPdsgLp5QOSuRsj9MA6ndEhH/AzuZM86qIQzt6rq+aGBwj3n5/TkLmU5lhAfdRmogA==",
"requires": {
"@babel/helper-module-imports": "^7.16.7",
"@babel/plugin-syntax-jsx": "^7.17.12",
"@babel/runtime": "^7.18.3",
"@emotion/hash": "^0.9.0",
"@emotion/memoize": "^0.8.0",
"@emotion/serialize": "^1.1.0",
"babel-plugin-macros": "^3.1.0",
"convert-source-map": "^1.5.0", "convert-source-map": "^1.5.0",
"escape-string-regexp": "^4.0.0", "escape-string-regexp": "^4.0.0",
"find-root": "^1.1.0", "find-root": "^1.1.0",
@@ -5933,6 +6148,11 @@
"stylis": "4.0.13" "stylis": "4.0.13"
}, },
"dependencies": { "dependencies": {
"@emotion/memoize": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz",
"integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA=="
},
"escape-string-regexp": { "escape-string-regexp": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -5951,17 +6171,22 @@
} }
}, },
"@emotion/cache": { "@emotion/cache": {
"version": "11.7.1", "version": "11.10.3",
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.7.1.tgz", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.10.3.tgz",
"integrity": "sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==", "integrity": "sha512-Psmp/7ovAa8appWh3g51goxu/z3iVms7JXOreq136D8Bbn6dYraPnmL6mdM8GThEx9vwSn92Fz+mGSjBzN8UPQ==",
"requires": { "requires": {
"@emotion/memoize": "^0.7.4", "@emotion/memoize": "^0.8.0",
"@emotion/sheet": "^1.1.0", "@emotion/sheet": "^1.2.0",
"@emotion/utils": "^1.0.0", "@emotion/utils": "^1.2.0",
"@emotion/weak-memoize": "^0.2.5", "@emotion/weak-memoize": "^0.3.0",
"stylis": "4.0.13" "stylis": "4.0.13"
}, },
"dependencies": { "dependencies": {
"@emotion/memoize": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz",
"integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA=="
},
"stylis": { "stylis": {
"version": "4.0.13", "version": "4.0.13",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
@@ -5970,9 +6195,9 @@
} }
}, },
"@emotion/hash": { "@emotion/hash": {
"version": "0.8.0", "version": "0.9.0",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz",
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" "integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ=="
}, },
"@emotion/is-prop-valid": { "@emotion/is-prop-valid": {
"version": "1.1.1", "version": "1.1.1",
@@ -5988,35 +6213,42 @@
"integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==" "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ=="
}, },
"@emotion/react": { "@emotion/react": {
"version": "11.7.1", "version": "11.10.0",
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.7.1.tgz", "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.0.tgz",
"integrity": "sha512-DV2Xe3yhkF1yT4uAUoJcYL1AmrnO5SVsdfvu+fBuS7IbByDeTVx9+wFmvx9Idzv7/78+9Mgx2Hcmr7Fex3tIyw==", "integrity": "sha512-K6z9zlHxxBXwN8TcpwBKcEsBsOw4JWCCmR+BeeOWgqp8GIU1yA2Odd41bwdAAr0ssbQrbJbVnndvv7oiv1bZeQ==",
"requires": { "requires": {
"@babel/runtime": "^7.13.10", "@babel/runtime": "^7.18.3",
"@emotion/cache": "^11.7.1", "@emotion/babel-plugin": "^11.10.0",
"@emotion/serialize": "^1.0.2", "@emotion/cache": "^11.10.0",
"@emotion/sheet": "^1.1.0", "@emotion/serialize": "^1.1.0",
"@emotion/utils": "^1.0.0", "@emotion/utils": "^1.2.0",
"@emotion/weak-memoize": "^0.2.5", "@emotion/weak-memoize": "^0.3.0",
"hoist-non-react-statics": "^3.3.1" "hoist-non-react-statics": "^3.3.1"
} }
}, },
"@emotion/serialize": { "@emotion/serialize": {
"version": "1.0.2", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz", "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.0.tgz",
"integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==", "integrity": "sha512-F1ZZZW51T/fx+wKbVlwsfchr5q97iW8brAnXmsskz4d0hVB4O3M/SiA3SaeH06x02lSNzkkQv+n3AX3kCXKSFA==",
"requires": { "requires": {
"@emotion/hash": "^0.8.0", "@emotion/hash": "^0.9.0",
"@emotion/memoize": "^0.7.4", "@emotion/memoize": "^0.8.0",
"@emotion/unitless": "^0.7.5", "@emotion/unitless": "^0.8.0",
"@emotion/utils": "^1.0.0", "@emotion/utils": "^1.2.0",
"csstype": "^3.0.2" "csstype": "^3.0.2"
},
"dependencies": {
"@emotion/memoize": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz",
"integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA=="
}
} }
}, },
"@emotion/sheet": { "@emotion/sheet": {
"version": "1.1.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.0.tgz",
"integrity": "sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g==" "integrity": "sha512-OiTkRgpxescko+M51tZsMq7Puu/KP55wMT8BgpcXVG2hqXc0Vo0mfymJ/Uj24Hp0i083ji/o0aLddh08UEjq8w=="
}, },
"@emotion/styled": { "@emotion/styled": {
"version": "11.6.0", "version": "11.6.0",
@@ -6031,19 +6263,19 @@
} }
}, },
"@emotion/unitless": { "@emotion/unitless": {
"version": "0.7.5", "version": "0.8.0",
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz",
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw=="
}, },
"@emotion/utils": { "@emotion/utils": {
"version": "1.0.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.0.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.0.tgz",
"integrity": "sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA==" "integrity": "sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw=="
}, },
"@emotion/weak-memoize": { "@emotion/weak-memoize": {
"version": "0.2.5", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz",
"integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==" "integrity": "sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg=="
}, },
"@eslint/eslintrc": { "@eslint/eslintrc": {
"version": "1.0.5", "version": "1.0.5",
@@ -6532,6 +6764,11 @@
"integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==",
"dev": true "dev": true
}, },
"@types/file-saver": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.5.tgz",
"integrity": "sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ=="
},
"@types/hoist-non-react-statics": { "@types/hoist-non-react-statics": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
@@ -6598,6 +6835,14 @@
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
}, },
"@types/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz",
"integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==",
"requires": {
"@types/react": "*"
}
},
"@types/scheduler": { "@types/scheduler": {
"version": "0.16.2", "version": "0.16.2",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
@@ -6855,13 +7100,13 @@
"dev": true "dev": true
}, },
"babel-plugin-macros": { "babel-plugin-macros": {
"version": "2.8.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
"integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
"requires": { "requires": {
"@babel/runtime": "^7.7.2", "@babel/runtime": "^7.12.5",
"cosmiconfig": "^6.0.0", "cosmiconfig": "^7.0.0",
"resolve": "^1.12.0" "resolve": "^1.19.0"
} }
}, },
"balanced-match": { "balanced-match": {
@@ -7056,15 +7301,15 @@
"dev": true "dev": true
}, },
"cosmiconfig": { "cosmiconfig": {
"version": "6.0.0", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz",
"integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==",
"requires": { "requires": {
"@types/parse-json": "^4.0.0", "@types/parse-json": "^4.0.0",
"import-fresh": "^3.1.0", "import-fresh": "^3.2.1",
"parse-json": "^5.0.0", "parse-json": "^5.0.0",
"path-type": "^4.0.0", "path-type": "^4.0.0",
"yaml": "^1.7.2" "yaml": "^1.10.0"
}, },
"dependencies": { "dependencies": {
"yaml": { "yaml": {
@@ -7190,6 +7435,15 @@
"esutils": "^2.0.2" "esutils": "^2.0.2"
} }
}, },
"dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
"requires": {
"@babel/runtime": "^7.8.7",
"csstype": "^3.0.2"
}
},
"dom-serializer": { "dom-serializer": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
@@ -7663,17 +7917,6 @@
"jsx-ast-utils": "^3.2.1", "jsx-ast-utils": "^3.2.1",
"language-tags": "^1.0.5", "language-tags": "^1.0.5",
"minimatch": "^3.0.4" "minimatch": "^3.0.4"
},
"dependencies": {
"@babel/runtime": {
"version": "7.16.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.5.tgz",
"integrity": "sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA==",
"dev": true,
"requires": {
"regenerator-runtime": "^0.13.4"
}
}
} }
}, },
"eslint-plugin-react": { "eslint-plugin-react": {
@@ -7863,6 +8106,12 @@
"flat-cache": "^3.0.4" "flat-cache": "^3.0.4"
} }
}, },
"file-saver": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
"integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==",
"dev": true
},
"filename-reserved-regex": { "filename-reserved-regex": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",
@@ -8122,6 +8371,11 @@
"void-elements": "3.1.0" "void-elements": "3.1.0"
} }
}, },
"html-to-image": {
"version": "1.10.6",
"resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.10.6.tgz",
"integrity": "sha512-+iFUWRJDmRRB92IxEAVoiuYQQXRp91yuI4Om7+lswwL3OOzoQ+H7laGLuZFK6ks2Z6Nz0wYm0UzahIO+D1Zxag=="
},
"htmlparser2": { "htmlparser2": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
@@ -8576,6 +8830,11 @@
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true "dev": true
}, },
"memoize-one": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
},
"memorystream": { "memorystream": {
"version": "0.3.1", "version": "0.3.1",
"resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
@@ -9083,7 +9342,6 @@
"version": "15.7.2", "version": "15.7.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
"dev": true,
"requires": { "requires": {
"loose-envify": "^1.4.0", "loose-envify": "^1.4.0",
"object-assign": "^4.1.1", "object-assign": "^4.1.1",
@@ -9093,8 +9351,7 @@
"react-is": { "react-is": {
"version": "16.13.1", "version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
"dev": true
} }
} }
}, },
@@ -9163,6 +9420,31 @@
"easy-bem": "^1.1.1" "easy-bem": "^1.1.1"
} }
}, },
"react-select": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/react-select/-/react-select-5.4.0.tgz",
"integrity": "sha512-CjE9RFLUvChd5SdlfG4vqxZd55AZJRrLrHzkQyTYeHlpOztqcgnyftYAolJ0SGsBev6zAs6qFrjm6KU3eo2hzg==",
"requires": {
"@babel/runtime": "^7.12.0",
"@emotion/cache": "^11.4.0",
"@emotion/react": "^11.8.1",
"@types/react-transition-group": "^4.4.0",
"memoize-one": "^5.0.0",
"prop-types": "^15.6.0",
"react-transition-group": "^4.3.0"
}
},
"react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
"requires": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2"
}
},
"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",
+8 -1
View File
@@ -4,9 +4,14 @@
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"dependencies": { "dependencies": {
"@dnd-kit/core": "^6.0.5",
"@dnd-kit/sortable": "^7.0.1",
"@dnd-kit/utilities": "^3.2.0",
"@mdi/js": "^6.5.95", "@mdi/js": "^6.5.95",
"@mdi/react": "^1.5.0", "@mdi/react": "^1.5.0",
"@types/file-saver": "^2.0.5",
"date-fns": "^2.27.0", "date-fns": "^2.27.0",
"html-to-image": "^1.10.6",
"next": "^12.1.6", "next": "^12.1.6",
"next-i18next": "^10.1.0", "next-i18next": "^10.1.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
@@ -15,6 +20,7 @@
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-indiana-drag-scroll": "^2.1.0", "react-indiana-drag-scroll": "^2.1.0",
"react-select": "^5.4.0",
"theme-ui": "^0.12.1", "theme-ui": "^0.12.1",
"yaml": "^2.1.1" "yaml": "^2.1.1"
}, },
@@ -26,6 +32,7 @@
"cheerio": "^1.0.0-rc.10", "cheerio": "^1.0.0-rc.10",
"eslint": "8.5.0", "eslint": "8.5.0",
"eslint-config-next": "12.0.7", "eslint-config-next": "12.0.7",
"file-saver": "^2.0.5",
"filenamify": "^4.3.0", "filenamify": "^4.3.0",
"http-server": "^14.1.0", "http-server": "^14.1.0",
"node-fetch": "^2.6.7", "node-fetch": "^2.6.7",
@@ -37,7 +44,7 @@
"scripts": { "scripts": {
"dev": "run-p dev:web dev:assets", "dev": "run-p dev:web dev:assets",
"dev:web": "next dev -p 4000", "dev:web": "next dev -p 4000",
"dev:assets": "http-server assets -p 4001 -s", "dev:assets": "http-server assets -p 4001 -s --cors",
"build": "npm run compile:novels && next build", "build": "npm run compile:novels && next build",
"start": "next start", "start": "next start",
"lint": "next lint", "lint": "next lint",
Binary file not shown.

After

Width:  |  Height:  |  Size: 811 KiB

@@ -0,0 +1,104 @@
import { useMemo } from 'react'
import ReactSelect, { components } from 'react-select'
import { Flex, Image } from 'theme-ui'
import { assetsBucketBaseUrl } from '../../../lib/consts'
import { BattlesuitData } from '../../../lib/honkai3rd/battlesuits'
interface BattlesuitSelectProps {
instanceId: string
value: string
optionIds: string[]
battlesuits: BattlesuitData[]
onChange: (newValue: string) => void
}
const BattlesuitSelect = ({
instanceId,
optionIds,
value,
battlesuits,
onChange,
}: BattlesuitSelectProps) => {
const battlesuitOptions = useMemo(() => {
return optionIds.map((battlesuitId) => {
const battlesuit = battlesuits.find((aBattlesuit) => {
return aBattlesuit.id === battlesuitId
})
if (battlesuit == null) {
return {
value: 'unknown',
label: 'unknown',
}
}
return {
value: battlesuit.id,
label: battlesuit.name,
}
})
}, [battlesuits, optionIds])
const battlesuit = battlesuits.find((battlesuit) => {
return battlesuit.id === value
})
return (
<ReactSelect
instanceId={instanceId}
value={
battlesuit != null
? {
label: battlesuit.name,
value: battlesuit.id,
}
: null
}
onChange={(option) => {
if (option == null) {
return
}
onChange(option.value)
}}
options={battlesuitOptions}
components={{
SingleValue: (props) => {
return (
<>
<components.SingleValue {...props}>
<Flex sx={{ alignItems: 'center', color: 'black' }}>
<Image
width={20}
height={20}
alt={props.data.label}
src={`${assetsBucketBaseUrl}/honkai3rd/battlesuits/portrait-${props.data.value}.png`}
sx={{ flexShrink: 0, mr: 2 }}
/>
{props.children}
</Flex>
</components.SingleValue>
</>
)
},
Option: (props) => {
return (
<>
<components.Option {...props}>
<Flex sx={{ alignItems: 'center', color: 'black' }}>
<Image
width={20}
height={20}
alt={props.data.label}
src={`${assetsBucketBaseUrl}/honkai3rd/battlesuits/portrait-${props.data.value}.png`}
sx={{ mr: 2, flexShrink: 0 }}
/>
{props.children}
</Flex>
</components.Option>
</>
)
},
}}
/>
)
}
export default BattlesuitSelect
@@ -0,0 +1,509 @@
import { Box, Button, Flex, Input, Label, Select, Textarea } from 'theme-ui'
import { ERGGData, ERGGDataUpdater, ERGGExSignetType } from './types'
import {
erVersions,
isGeneralSigil,
PopulatedSignetGroup,
RemembranceSigil,
remembranceSigilIds,
supportBattlesuitIds,
} from '../../../lib/honkai3rd/elysianRealm'
import {
DndContext,
closestCenter,
KeyboardSensor,
PointerSensor,
useSensor,
useSensors,
} from '@dnd-kit/core'
import {
arrayMove,
SortableContext,
sortableKeyboardCoordinates,
useSortable,
verticalListSortingStrategy,
} from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities'
import { BattlesuitData } from '../../../lib/honkai3rd/battlesuits'
import DifficultySelect from './DifficultySelect'
import { getExSignetLabel } from './utils'
import BattlesuitSelect from './BattlesuitSelect'
import SigilSelect from './SigilSelect'
import { WeaponData } from '../../../lib/honkai3rd/weapons'
import { StigmataData } from '../../../lib/honkai3rd/stigmata'
import { useMemo } from 'react'
import EquipmentSetControl from './EquipmentSetControl'
import SignetGroupSelect from './SignetGroupSelect'
interface DataFormProps {
updateData: ERGGDataUpdater
data: ERGGData
battlesuits: BattlesuitData[]
exSignetGroup: PopulatedSignetGroup
sigils: RemembranceSigil[]
weapons: WeaponData[]
stigmata: StigmataData[]
}
const DataForm = ({
updateData,
data,
battlesuits,
exSignetGroup,
sigils,
weapons,
stigmata,
}: DataFormProps) => {
const sensors = useSensors(
useSensor(PointerSensor),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
})
)
const { topStigmaIds, midStigmaIds, botStigmaIds } = useMemo(() => {
return stigmata.reduce(
(map, stigma) => {
switch (stigma.type) {
case 'top':
map.topStigmaIds.push(stigma.id)
break
case 'mid':
map.midStigmaIds.push(stigma.id)
break
case 'bot':
map.botStigmaIds.push(stigma.id)
break
}
return map
},
{
topStigmaIds: [] as string[],
midStigmaIds: [] as string[],
botStigmaIds: [] as string[],
}
)
}, [stigmata])
return (
<Box sx={{ maxWidth: 960 }}>
<Flex>
<Box sx={{ flex: 1, p: 2 }}>
<Box>
<Label></Label>
<Textarea
value={data.signature}
placeholder='서명...'
onChange={(event) => {
updateData('signature', event.target.value)
}}
sx={{ resize: 'vertical', minHeight: 100 }}
/>
</Box>
<Box>
<Label></Label>
<Select
value={data.rank || 'na'}
onChange={(event) => {
switch (event.target.value) {
case 'na':
updateData('rank', undefined)
break
default:
updateData('rank', event.target.value)
}
}}
>
<option value='na'>N/A</option>
<option value='s'>s</option>
<option value='s1'>s1</option>
<option value='s2'>s2</option>
<option value='s3'>s3</option>
<option value='ss'>ss</option>
<option value='ss1'>ss1</option>
<option value='ss2'>ss2</option>
<option value='ss3'>ss3</option>
<option value='sss'>sss</option>
<option value='a'>a</option>
</Select>
</Box>
<Box>
<Label></Label>
<Input
value={data.tag}
placeholder='태그...(필살기, 평타)'
onChange={(event) => {
updateData('tag', event.target.value)
}}
/>
</Box>
<Box>
<Label></Label>
<DifficultySelect
onChange={(newValue) => updateData('difficulty', newValue)}
value={data.difficulty}
/>
</Box>
<Box>
<Label></Label>
<BattlesuitSelect
instanceId='battlesuit-select'
battlesuits={battlesuits}
optionIds={erVersions.reduce<string[]>(
(battlesuitIds, version) => {
return [...battlesuitIds, ...version.battlesuits]
},
[]
)}
value={data.battlesuitId}
onChange={(newValue) => {
updateData('battlesuitId', newValue)
const exSignetSet = exSignetGroup.sets.find((signetSet) => {
return signetSet.id === `elysia-${newValue}`
})
if (exSignetSet != null) {
updateData(
'exSignets',
exSignetSet.signets.map((signet, index) => {
return {
type:
data.exSignets[index] != null
? data.exSignets[index].type
: 'na',
name: signet.name,
}
})
)
}
}}
/>
</Box>
<Box>
<Label> </Label>
<Flex>
<Box>
{data.exSignets.map((signet, index) => {
return (
<Flex
key={index}
sx={{ p: 1, height: 40, alignItems: 'center', mb: 2 }}
>
<Select
value={signet.type}
sx={{ height: 40, width: 80, mr: 1, p: 2 }}
onChange={(event) => {
const newExSignets = data.exSignets.slice()
newExSignets[index] = {
...data.exSignets[index],
type: event.target.value as ERGGExSignetType,
}
updateData('exSignets', newExSignets)
}}
>
<option value='start'></option>
<option value='1st'></option>
<option value='2nd'></option>
<option value='backup'></option>
<option value='na'></option>
</Select>
</Flex>
)
})}
</Box>
<Box sx={{ flex: 1 }}>
<DndContext
id='exSignetOrdering'
sensors={sensors}
collisionDetection={closestCenter}
onDragEnd={(event) => {
const { active, over } = event
if (over == null) {
return
}
if (active.id !== over.id) {
const nameArray = data.exSignets.map(
(signet) => signet.name
)
const oldIndex = nameArray.indexOf(active.id as string)
const newIndex = nameArray.indexOf(over.id as string)
const newNameArray = arrayMove(
nameArray,
oldIndex,
newIndex
)
const newExSignets = data.exSignets.map(
(signet, index) => {
return {
...signet,
name: newNameArray[index],
}
}
)
updateData('exSignets', newExSignets)
}
}}
>
<SortableContext
items={data.exSignets.map((signet) => ({
id: signet.name,
}))}
strategy={verticalListSortingStrategy}
>
{data.exSignets.map((signet) => (
<SortableItem key={signet.name} name={signet.name} />
))}
</SortableContext>
</DndContext>
</Box>
</Flex>
</Box>
</Box>
<Box sx={{ flex: 1, p: 2 }}>
<Box>
{data.signets.map((signet, index) => {
return (
<Box key={index}>
<Label>{index + 1} </Label>
<Flex sx={{ alignItems: 'center', p: 1 }}>
<Box sx={{ flex: 1, flexShrink: 0, mr: 1 }}>
<SignetGroupSelect
value={signet.group}
onChange={(newValue) => {
const newSignets = data.signets.slice()
newSignets[index] = {
...signet,
group: newValue as any,
}
updateData('signets', newSignets)
}}
instanceId={`signet-select-${index}`}
/>
</Box>
<Box>
<Button
onClick={() => {
const newSignets = data.signets.slice()
newSignets[index] = {
...signet,
nexus: signet.nexus === 1 ? 2 : 1,
}
updateData('signets', newSignets)
}}
sx={{ mr: 1, width: 40 }}
>
{signet.nexus === 1 ? 'I' : 'II'}
</Button>
</Box>
<Box sx={{ width: 60 }}>
<Select
value={signet.type}
onChange={(event) => {
const newSignets = data.signets.slice()
newSignets[index] = {
...signet,
type: event.target.value as any,
}
updateData('signets', newSignets)
}}
>
<option value='start'></option>
<option value='core'></option>
<option value='sub'></option>
</Select>
</Box>
</Flex>
<Box sx={{ p: 1 }}>
<Textarea
value={signet.description}
onChange={(event) => {
const newSignets = data.signets.slice()
newSignets[index] = {
...signet,
description: event.target.value,
}
updateData('signets', newSignets)
}}
placeholder='각인 설명...'
sx={{ resize: 'vertical' }}
/>
</Box>
</Box>
)
})}
</Box>
</Box>
<Box sx={{ flex: 1, p: 2 }}>
<Box>
<Label> </Label>
{data.supportSets.map((supportSet, index) => {
return (
<Box key={index}>
<Box>{supportSet.type === 'util' ? '유틸' : '딜링'}</Box>
<Flex>
<Box sx={{ flex: 1, mr: 1 }}>
<BattlesuitSelect
instanceId={`battlesuit-support-select-${index}-1`}
value={supportSet.battlesuitIds[0]}
optionIds={supportBattlesuitIds}
battlesuits={battlesuits}
onChange={(newValue) => {
const newSupportSets = data.supportSets.slice()
newSupportSets[index] = {
...newSupportSets[index],
battlesuitIds: [
...newSupportSets[index].battlesuitIds,
],
}
newSupportSets[index].battlesuitIds[0] = newValue
if (
data.supportSets[index].battlesuitIds[1] ===
newValue
) {
newSupportSets[index].battlesuitIds[1] =
data.supportSets[index].battlesuitIds[0]
}
updateData('supportSets', newSupportSets)
}}
/>
</Box>
<Box sx={{ flex: 1 }}>
<BattlesuitSelect
instanceId={`battlesuit-support-select-${index}-2`}
value={supportSet.battlesuitIds[1]}
optionIds={supportBattlesuitIds}
battlesuits={battlesuits}
onChange={(newValue) => {
const newSupportSets = data.supportSets.slice()
newSupportSets[index] = {
...newSupportSets[index],
battlesuitIds: [
...newSupportSets[index].battlesuitIds,
],
}
newSupportSets[index].battlesuitIds[1] = newValue
if (
data.supportSets[index].battlesuitIds[0] ===
newValue
) {
newSupportSets[index].battlesuitIds[0] =
data.supportSets[index].battlesuitIds[1]
}
updateData('supportSets', newSupportSets)
}}
/>
</Box>
</Flex>
</Box>
)
})}
</Box>
<Box sx={{ flex: 1 }}>
{data.sigilSets.map((sigilSet, index) => {
return (
<Box key={index}>
<Box>
{sigilSet.type === 'start'
? '초반'
: sigilSet.type === 'mid'
? '중반'
: '후반'}{' '}
</Box>
<Flex>
<Box sx={{ flex: 1, mr: 1 }}>
<SigilSelect
instanceId={`sigil-select-${index}-general`}
value={sigilSet.sigilIds[0]}
optionIds={remembranceSigilIds.filter(isGeneralSigil)}
sigils={sigils}
onChange={(newValue) => {
const newSigilSets = data.sigilSets.slice()
newSigilSets[index] = {
...newSigilSets[index],
sigilIds: [
newValue,
newSigilSets[index].sigilIds[1],
],
}
updateData('sigilSets', newSigilSets)
}}
/>
</Box>
<Box>
<SigilSelect
instanceId={`sigil-select-${index}-support`}
value={sigilSet.sigilIds[1]}
optionIds={remembranceSigilIds.filter(
(sigilId) => !isGeneralSigil(sigilId)
)}
sigils={sigils}
onChange={(newValue) => {
const newSigilSets = data.sigilSets.slice()
newSigilSets[index] = {
...newSigilSets[index],
sigilIds: [
newSigilSets[index].sigilIds[0],
newValue,
],
}
updateData('sigilSets', newSigilSets)
}}
/>
</Box>
</Flex>
</Box>
)
})}
</Box>
<Box>
{data.equipmentSets.map((equipmentSet, index) => {
return (
<EquipmentSetControl
key={index}
equipmentSet={equipmentSet}
weapons={weapons}
stigmata={stigmata}
topStigmaIds={topStigmaIds}
midStigmaIds={midStigmaIds}
botStigmaIds={botStigmaIds}
index={index}
updateData={updateData}
/>
)
})}
</Box>
</Box>
</Flex>
</Box>
)
}
export default DataForm
export function SortableItem(props: { name: string }) {
const { attributes, listeners, setNodeRef, transform, transition } =
useSortable({ id: props.name })
const style = {
transform: CSS.Transform.toString(transform),
transition,
}
return (
<div ref={setNodeRef} style={style} {...attributes} {...listeners}>
<Flex
sx={{
height: 40,
mb: 2,
cursor: 'ns-resize',
alignItems: 'center',
p: 1,
}}
>
{getExSignetLabel(props.name)}
</Flex>
</div>
)
}
@@ -0,0 +1,83 @@
import { Box } from 'theme-ui'
import { colors } from './styles'
interface DifficultyBoxProps {
difficulty: 'abstinence' | 'corruption' | 'inferno'
}
const DifficultyBox = ({ difficulty }: DifficultyBoxProps) => {
return (
<Box
sx={{
position: 'absolute',
left: 15,
width: 60,
height: 105,
backgroundColor:
difficulty === 'corruption'
? colors.corruptionDifficultyColor
: difficulty === 'abstinence'
? colors.abstinenceDifficultyColor
: colors.infernoDifficultyColor,
padding: '5px',
borderBottomLeftRadius: 10,
borderBottomRightRadius: 10,
boxShadow: '5px 5px 10px rgba(0,0,0,0.5)',
}}
className='difficultyLabel'
>
<Box
sx={{
color: colors.backgroundColor,
fontWeight: 'bold',
textAlign: 'center',
fontSize: 16,
}}
>
</Box>
<Box
sx={{
borderColor: colors.backgroundColor,
borderWidth: '1px 0 1px',
borderStyle: 'dashed',
width: '100%',
}}
/>
<Box
sx={{
color: colors.backgroundColor,
fontWeight: 'bold',
fontSize: 25,
textAlign: 'center',
}}
>
{difficulty === 'corruption'
? '침식'
: difficulty === 'abstinence'
? '제약'
: '겁화'}
</Box>
<Box
sx={{
borderColor: colors.backgroundColor,
borderWidth: '1px 0 1px',
borderStyle: 'dashed',
width: '100%',
}}
/>
<Box
sx={{
color: colors.backgroundColor,
fontWeight: 'bold',
textAlign: 'center',
fontSize: 16,
}}
>
</Box>
</Box>
)
}
export default DifficultyBox
@@ -0,0 +1,30 @@
import { Select } from 'theme-ui'
import { ERGGDifficulty } from './types'
interface DifficultySelectProps {
onChange: (newValue: ERGGDifficulty) => void
value: ERGGDifficulty
}
const DifficultySelect = ({ onChange, value }: DifficultySelectProps) => {
return (
<Select
onChange={(event) => {
switch (event.target.value) {
case 'abstinence':
case 'corruption':
case 'inferno':
onChange(event.target.value)
break
}
}}
value={value}
>
<option value='corruption'></option>
<option value='abstinence'></option>
<option value='inferno'></option>
</Select>
)
}
export default DifficultySelect
@@ -0,0 +1,444 @@
/* eslint-disable @next/next/no-page-custom-font */
import { useCallback, useEffect, useRef, useState } from 'react'
import {
Box,
Button,
Flex,
Heading,
Image,
Input,
Label,
Text,
Textarea,
} from 'theme-ui'
import { BattlesuitData } from '../../../lib/honkai3rd/battlesuits'
import { WeaponData } from '../../../lib/honkai3rd/weapons'
import DifficultyBox from './DifficultyBox'
import SignetBox from './SignetBox'
import { colors } from './styles'
import ValkBox from './ValkBox'
import {
PopulatedSignetGroup,
RemembranceSigil,
} from '../../../lib/honkai3rd/elysianRealm'
import SigilBox from './SigilBox'
import SupportBox from './SupportBox'
import DataForm from './DataForm'
import EquipmentBox from './EquipmentBox'
import { ERGGData, ERGGDataUpdater, ERGGExSignetType } from './types'
import { StigmataData } from '../../../lib/honkai3rd/stigmata'
import { saveAs } from 'file-saver'
import { toBlob } from 'html-to-image'
import { assetsBucketBaseUrl } from '../../../lib/consts'
interface ERGuideGeneratorProps {
weapons: WeaponData[]
stigmata: StigmataData[]
battlesuits: BattlesuitData[]
exSignetGroup: PopulatedSignetGroup
sigils: RemembranceSigil[]
}
const exSignetTypes: ERGGExSignetType[] = [
'start',
'1st',
'2nd',
'backup',
'na',
]
const ERGuideGenerator = ({
weapons,
stigmata,
battlesuits,
exSignetGroup,
sigils,
}: ERGuideGeneratorProps) => {
const [data, setData] = useState<ERGGData>({
tag: '',
signature:
'작성자 : XXX\nAbyss Lab에서 생성됨\nhttps://abyss-lab.app/honkai3rd',
battlesuitId: 'vill-v',
difficulty: 'corruption',
exSignets:
exSignetGroup.sets
.find((set) => set.id === 'elysia-vill-v')
?.signets.map((signet, index) => {
return {
type: exSignetTypes[index],
name: signet.name,
}
}) || [],
supportSets: [
{ type: 'util', battlesuitIds: ['le', 'ae'] },
{ type: 'dps', battlesuitIds: ['br', 'ae'] },
],
sigilSets: [
{
type: 'start',
sigilIds: ['burden', 'it-will-be-written'],
},
{
type: 'mid',
sigilIds: ['burden', 'it-will-be-written'],
},
{
type: 'end',
sigilIds: ['burden', 'it-will-be-written'],
},
],
equipmentSets: [
{
type: 'best',
weapon: 'vill-v-pri-weapon',
top: 'carlo-collodi-top',
mid: 'carlo-collodi-mid',
bot: 'carlo-collodi-bot',
},
{
type: 'alt',
weapon: 'vill-v-pri-weapon',
top: 'carlo-collodi-top',
mid: 'carlo-collodi-mid',
bot: 'carlo-collodi-bot',
},
],
signets: [
{
type: 'core',
group: 'vill-v',
nexus: 2,
description: '',
},
{
type: 'core',
group: 'vill-v',
nexus: 2,
description: '',
},
{
type: 'sub',
group: 'vill-v',
nexus: 2,
description: '',
},
{
type: 'sub',
group: 'vill-v',
nexus: 2,
description: '',
},
{
type: 'sub',
group: 'vill-v',
nexus: 2,
description: '',
},
],
})
const [fileName, setFileName] = useState('guide')
const guideRef = useRef(null)
const updateData = useCallback<ERGGDataUpdater>(
(key, value) => {
setData((previousData) => {
const newData = {
...previousData,
[key]: typeof value === 'function' ? value(previousData[key]) : value,
}
return newData
})
},
[setData]
)
const [customStyle, setCustomStyle] = useState('')
const loadedRef = useRef(false)
useEffect(() => {
if (loadedRef.current) {
localStorage.setItem('ergg:customStyle', customStyle)
} else {
setCustomStyle(localStorage.getItem('ergg:customStyle') || '')
loadedRef.current = true
}
}, [customStyle])
return (
<Box
onDragOver={(event) => {
event.preventDefault()
}}
onDrop={async (event) => {
event.preventDefault()
const result = await new Promise<string>((resolve, reject) => {
const file = event.dataTransfer.files[0]
const reader = new FileReader()
reader.addEventListener(
'load',
() => {
resolve(reader.result as string)
},
{ once: true }
)
reader.addEventListener(
'error',
(error) => {
reject(error)
},
{ once: true }
)
reader.readAsText(file)
})
const parsedResult = JSON.parse(result)
setData(parsedResult)
}}
>
<Box sx={{ width: 960 }}>
<Box
ref={guideRef}
sx={{
width: 960,
height: 660,
position: 'relative',
backgroundColor: colors.backgroundColor,
color: '#FFF',
fontFamily: `'NanumSquare', sans-serif`,
}}
>
<style
dangerouslySetInnerHTML={{
__html: `@font-face {
font-family: 'GyeonggiTitleM';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_one@1.0/GyeonggiTitleM.woff') format('woff');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'YdestreetB';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_2110@1.0/YdestreetB.woff2') format('woff2');
font-weight: normal;
font-style: normal;
}
@import url('https://cdn.rawgit.com/moonspam/NanumSquare/master/nanumsquare.css');
@font-face {
font-family: 'ghanachoco';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_20-04@1.0/ghanachoco.woff') format('woff');
font-weight: normal;
font-style: normal;
}
.exSignetLabel {
font-family: 'ghanachoco';
}
.difficultyLabel {
font-family: 'YdestreetB';
}
.signetTypeLabel {
font-family: 'YdestreetB';
}
.signetDescription {
font-family: 'GyeonggiTitleM';
}
.signetGroupLabel {
font-family: 'YdestreetB';
}
.tagLabel {
font-family: 'YdestreetB';
}
`,
}}
/>
<Box
sx={{
position: 'absolute',
top: 0,
left: 0,
width: 960,
height: 660,
opacity: 0.15,
backgroundImage: `url('/assets/erbg.png')`,
backgroundSize: 1300,
backgroundPositionY: -55,
backgroundPositionX: -30,
}}
></Box>
<style
dangerouslySetInnerHTML={{
__html: customStyle,
}}
/>
<Flex
sx={{
position: 'absolute',
top: 15,
left: 85,
}}
>
{data.rank && (
<Image
alt={data.rank}
width={40}
height={40}
src={`${assetsBucketBaseUrl}/honkai3rd/rank-icons/${data.rank}-rank.png`}
sx={{ mr: '5px' }}
/>
)}
{data.tag && (
<Box
sx={{
padding: '5px',
border: 'solid 1px gray',
boxSizing: 'border-box',
backgroundColor: '#181614',
boxShadow: '5px 5px 10px rgba(0,0,0,0.5)',
}}
className='tagLabel'
>
{data.tag}
</Box>
)}
</Flex>
<Flex
sx={{
position: 'absolute',
right: 10,
padding: '5px 10px',
boxSizing: 'border-box',
bottom: 15,
maxWidth: 460,
alignItems: 'center',
whiteSpace: 'pre-wrap',
color: '#A59A9B',
fontStyle: 'italic',
textAlign: 'right',
}}
>
<Text>{data.signature}</Text>
</Flex>
<SignetBox signets={data.signets} />
<DifficultyBox difficulty={data.difficulty} />
<ValkBox
battlesuitId={data.battlesuitId}
exSignets={data.exSignets}
/>
<Box
sx={{
position: 'absolute',
bottom: 15 + (87 + 10) * 2,
left: 15,
}}
>
<SupportBox supportSets={data.supportSets} />
</Box>
<Box
sx={{
position: 'absolute',
bottom: 15 + 87 + 10,
left: 15,
}}
>
<SigilBox sigilSets={data.sigilSets} sigils={sigils} />
</Box>
<Box
sx={{
position: 'absolute',
bottom: '15px',
left: 15,
}}
>
<EquipmentBox
equipmentSets={data.equipmentSets}
weapons={weapons}
stigmata={stigmata}
/>
</Box>
</Box>
</Box>
<Box sx={{ m: 2 }}>
<Flex sx={{ alignItems: 'center' }}>
<Box sx={{ mr: 1 }}>
<Label>File Name</Label>
</Box>
<Box sx={{ mr: 1 }}>
<Input
value={fileName}
onChange={(event) => {
setFileName(event.target.value)
}}
/>
</Box>
<Button
sx={{ mr: 1 }}
onClick={async () => {
if (guideRef.current == null) {
return
}
const blob = await toBlob(guideRef.current)
if (blob == null) {
return
}
saveAs(blob, `${fileName}.png`)
}}
>
Download Image ({fileName}.png)
</Button>
<Button
onClick={() => {
console.log(JSON.stringify(data))
saveAs(new Blob([JSON.stringify(data)]), `${fileName}.json`)
}}
>
Download JSON Data ({fileName}.json)
</Button>
</Flex>
</Box>
<DataForm
exSignetGroup={exSignetGroup}
battlesuits={battlesuits}
updateData={updateData}
data={data}
sigils={sigils}
weapons={weapons}
stigmata={stigmata}
/>
<hr />
<Box sx={{ p: 2 }}>
<Heading as='h3'> </Heading>
<Box as='ul'>
<Box as='li'>
Y이드스트릿체 https://www.yspotlight.co.kr/brand/font?tabNo=1
</Box>
<Box as='li'> https://hangeul.naver.com/2017/nanum</Box>
<Box as='li'>
https://www.gg.go.kr/contents/contents.do?ciIdx=679&menuId=2457
</Box>
<Box as='li'>
릿 hhttps://www.lotteconf.co.kr/prcenter/gana
</Box>
</Box>
<Box sx={{ display: 'none' }}>
<Label>Custom Style(Don&apos;t paste any suspicious scripts!!)</Label>
<Textarea
value={customStyle}
onChange={(event) => {
setCustomStyle(event.target.value)
}}
/>
</Box>
</Box>
</Box>
)
}
export default ERGuideGenerator
@@ -0,0 +1,136 @@
import { Box, Flex, Image } from 'theme-ui'
import { assetsBucketBaseUrl } from '../../../lib/consts'
import { StigmataData } from '../../../lib/honkai3rd/stigmata'
import { WeaponData } from '../../../lib/honkai3rd/weapons'
import { ERGGEquipmentSet } from './types'
interface EquipmentBoxProps {
equipmentSets: ERGGEquipmentSet[]
weapons: WeaponData[]
stigmata: StigmataData[]
}
const EquipmentBox = ({
equipmentSets,
weapons,
stigmata,
}: EquipmentBoxProps) => {
return (
<Box
sx={{
padding: '5px',
border: 'solid 1px gray',
boxSizing: 'border-box',
backgroundColor: '#181614',
boxShadow: '5px 5px 10px rgba(0,0,0,0.5)',
}}
>
<Flex>
<Box
sx={{
backgroundColor: '#8E5B45',
p: '5px',
textAlign: 'center',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
mr: '5px',
width: 55,
}}
>
<Box></Box>
<Box></Box>
</Box>
{equipmentSets.map((equipmentSet, index) => {
return (
<Box
key={index}
sx={{
backgroundColor: '#615559',
p: '15px 5px 0',
borderRadius: '5px',
position: 'relative',
mr: '5px',
'&:last-child': {
mr: 0,
},
}}
>
<Box
sx={{
position: 'absolute',
top: 0,
left: 0,
backgroundColor:
equipmentSet.type === 'best' ? '#EE4E2C' : '#9082BD',
borderTopLeftRadius: '5px',
borderBottomRightRadius: '5px',
p: '0 7px',
fontSize: 12,
}}
>
{equipmentSet.type === 'best' ? '베스트' : '대체'}
</Box>
<Box sx={{ height: 40 }}>
<Image
alt=''
src={`${assetsBucketBaseUrl}/honkai3rd/weapons/${equipmentSet.weapon}.png`}
width={40}
height={40}
sx={{ borderRadius: '5px', mr: '5px' }}
/>
<Image
alt=''
src={`${assetsBucketBaseUrl}/honkai3rd/stigmata/icon-${equipmentSet.top}.png`}
width={40}
height={40}
sx={{ borderRadius: '5px', mr: '5px' }}
/>
<Image
alt=''
src={`${assetsBucketBaseUrl}/honkai3rd/stigmata/icon-${equipmentSet.mid}.png`}
width={40}
height={40}
sx={{ borderRadius: '5px', mr: '5px' }}
/>
<Image
alt=''
src={`${assetsBucketBaseUrl}/honkai3rd/stigmata/icon-${equipmentSet.bot}.png`}
width={40}
height={40}
sx={{ borderRadius: '5px' }}
/>
</Box>
<Flex
sx={{
height: 20,
fontSize: 12,
alignItems: 'center',
justifyContent: 'center',
}}
>
{getWeaponName(equipmentSet.weapon)}+
{getStigmataName(equipmentSet.top)}/
{getStigmataName(equipmentSet.mid)}/
{getStigmataName(equipmentSet.bot)}
</Flex>
</Box>
)
})}
</Flex>
</Box>
)
function getWeaponName(id: string) {
return weapons
.find((weapon) => weapon.id === id)
?.name.split(' ')[0]
.slice(0, 4)
.trim()
}
function getStigmataName(id: string) {
return stigmata.find((stigma) => stigma.id === id)?.name.slice(0, 3)
}
}
export default EquipmentBox
@@ -0,0 +1,150 @@
import { useCallback, useMemo } from 'react'
import { Box, Flex, Label } from 'theme-ui'
import { StigmataData } from '../../../lib/honkai3rd/stigmata'
import { WeaponData } from '../../../lib/honkai3rd/weapons'
import StigmaSelect from './StigmaSelect'
import { ERGGDataUpdater, ERGGEquipmentSet } from './types'
import WeaponSelect from './WeaponSelect'
interface EquipmentSetControlProps {
index: number
equipmentSet: ERGGEquipmentSet
updateData: ERGGDataUpdater
weapons: WeaponData[]
stigmata: StigmataData[]
topStigmaIds: string[]
midStigmaIds: string[]
botStigmaIds: string[]
}
const EquipmentSetControl = ({
index,
equipmentSet,
updateData,
weapons,
stigmata,
topStigmaIds,
midStigmaIds,
botStigmaIds,
}: EquipmentSetControlProps) => {
const handleWeaponChange = useCallback(
(newValue: string) => {
updateData('equipmentSets', (previousEquipmentSets) => {
const newEquipmentSets = previousEquipmentSets.slice()
newEquipmentSets[index] = {
...newEquipmentSets[index],
weapon: newValue,
}
return newEquipmentSets
})
},
[index, updateData]
)
const weaponIds = useMemo(() => weapons.map((weapon) => weapon.id), [weapons])
const handleTopStigmaChange = useCallback(
(newValue: string) => {
updateData('equipmentSets', (previousEquipmentSets) => {
const newEquipmentSets = previousEquipmentSets.slice()
newEquipmentSets[index] = {
...newEquipmentSets[index],
top: newValue,
}
return newEquipmentSets
})
},
[index, updateData]
)
const handleMidStigmaChange = useCallback(
(newValue: string) => {
updateData('equipmentSets', (previousEquipmentSets) => {
const newEquipmentSets = previousEquipmentSets.slice()
newEquipmentSets[index] = {
...newEquipmentSets[index],
mid: newValue,
}
return newEquipmentSets
})
},
[index, updateData]
)
const handleBotStigmaChange = useCallback(
(newValue: string) => {
updateData('equipmentSets', (previousEquipmentSets) => {
const newEquipmentSets = previousEquipmentSets.slice()
newEquipmentSets[index] = {
...newEquipmentSets[index],
bot: newValue,
}
return newEquipmentSets
})
},
[index, updateData]
)
return (
<Box>
<Box>{equipmentSet.type === 'best' ? '베스트' : '대체'}</Box>
<Flex sx={{ alignItems: 'center' }}>
<Box sx={{ mr: 2 }}>
<Label></Label>
</Box>
<Box sx={{ flexShrink: 0, flex: 1 }}>
<WeaponSelect
instanceId={`equipment-${index}-weapon`}
value={equipmentSet.weapon}
onChange={handleWeaponChange}
weapons={weapons}
optionIds={weaponIds}
/>
</Box>
</Flex>
<Flex sx={{ alignItems: 'center' }}>
<Box sx={{ mr: 2 }}>
<Label></Label>
</Box>
<Box sx={{ flexShrink: 0, flex: 1 }}>
<StigmaSelect
instanceId={`equipment-${index}-top`}
value={equipmentSet.top}
onChange={handleTopStigmaChange}
stigmata={stigmata}
optionIds={topStigmaIds}
/>
</Box>
</Flex>
<Flex sx={{ alignItems: 'center' }}>
<Box sx={{ mr: 2 }}>
<Label></Label>
</Box>
<Box sx={{ flexShrink: 0, flex: 1 }}>
<StigmaSelect
instanceId={`equipment-${index}-mid`}
value={equipmentSet.mid}
onChange={handleMidStigmaChange}
stigmata={stigmata}
optionIds={midStigmaIds}
/>
</Box>
</Flex>
<Flex sx={{ alignItems: 'center' }}>
<Box sx={{ mr: 2 }}>
<Label></Label>
</Box>
<Box sx={{ flexShrink: 0, flex: 1 }}>
<StigmaSelect
instanceId={`equipment-${index}-bot`}
value={equipmentSet.bot}
onChange={handleBotStigmaChange}
stigmata={stigmata}
optionIds={botStigmaIds}
/>
</Box>
</Flex>
</Box>
)
}
export default EquipmentSetControl
@@ -0,0 +1,116 @@
import { memo } from 'react'
import { Box, Flex, Image } from 'theme-ui'
import { assetsBucketBaseUrl } from '../../../lib/consts'
import { RemembranceSigil } from '../../../lib/honkai3rd/elysianRealm'
import { ERGGSigilSet } from './types'
interface SigilBoxProps {
sigilSets: ERGGSigilSet[]
sigils: RemembranceSigil[]
}
const SigilBox = ({ sigilSets, sigils }: SigilBoxProps) => {
return (
<Box
sx={{
padding: '5px',
border: 'solid 1px gray',
boxSizing: 'border-box',
backgroundColor: 'rgba(0,0,0,0.5)',
boxShadow: '5px 5px 10px rgba(0,0,0,0.5)',
}}
>
<Flex>
<Box
sx={{
backgroundColor: '#8E5B45',
p: '5px',
textAlign: 'center',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
mr: '5px',
width: 55,
}}
>
<Box></Box>
<Box></Box>
</Box>
{sigilSets.map((sigilSet, index) => {
return (
<Box
key={index}
sx={{
backgroundColor: '#615559',
p: '15px 5px 0',
borderRadius: '5px',
position: 'relative',
mr: '5px',
'&:last-child': {
mr: 0,
},
}}
>
<Box
sx={{
position: 'absolute',
top: 0,
left: 0,
backgroundColor:
sigilSet.type === 'start'
? '#666480'
: sigilSet.type === 'mid'
? '#8A6A5C'
: '#C06848',
borderTopLeftRadius: '5px',
borderBottomRightRadius: '5px',
p: '0 7px',
fontSize: 12,
}}
>
{sigilSet.type === 'start'
? '초반'
: sigilSet.type === 'mid'
? '중반'
: '후반'}
</Box>
<Box sx={{ height: 40 }}>
<Image
alt=''
src={`${assetsBucketBaseUrl}/honkai3rd/elysian-realm/remembrance-sigils/${sigilSet.sigilIds[0]}.png`}
width={40}
height={40}
sx={{ mr: '5px', borderRadius: 5 }}
/>
<Image
alt=''
src={`${assetsBucketBaseUrl}/honkai3rd/elysian-realm/remembrance-sigils/${sigilSet.sigilIds[1]}.png`}
width={40}
height={40}
sx={{ borderRadius: 5 }}
/>
</Box>
<Flex
sx={{
height: 20,
fontSize: 12,
alignItems: 'center',
justifyContent: 'center',
}}
>
{getSigilName(sigilSet.sigilIds[0])}+
{getSigilName(sigilSet.sigilIds[1])}
</Flex>
</Box>
)
})}
</Flex>
</Box>
)
function getSigilName(id: string) {
return sigils.find((sigil) => sigil.id === id)?.name.slice(0, 3)
}
}
export default memo(SigilBox)
@@ -0,0 +1,108 @@
import { RemembranceSigil } from '../../../lib/honkai3rd/elysianRealm'
import ReactSelect, { components } from 'react-select'
import { Flex, Image } from 'theme-ui'
import { memo, useMemo } from 'react'
import { assetsBucketBaseUrl } from '../../../lib/consts'
interface SigilSelectProps {
instanceId: string
value: string
optionIds: string[]
sigils: RemembranceSigil[]
onChange: (newValue: string) => void
}
const SigilSelect = ({
instanceId,
optionIds,
sigils,
onChange,
value,
}: SigilSelectProps) => {
const sigilOptions = useMemo(() => {
return optionIds.map((sigilId) => {
const sigil = sigils.find((aSigil) => {
return aSigil.id === sigilId
})
if (sigil == null) {
return {
value: 'unknown',
label: 'unknown',
}
}
return {
value: sigil.id,
label: sigil.name,
}
})
}, [optionIds, sigils])
const sigil = useMemo(
() =>
sigils.find((sigil) => {
return sigil.id === value
}),
[sigils, value]
)
return (
<ReactSelect
instanceId={instanceId}
value={
sigil != null
? {
label: sigil.name,
value: sigil.id,
}
: null
}
onChange={(option) => {
if (option == null) {
return
}
onChange(option.value)
}}
options={sigilOptions}
components={{
SingleValue: (props) => {
return (
<>
<components.SingleValue {...props}>
<Flex sx={{ alignItems: 'center', color: 'black' }}>
<Image
width={20}
height={20}
alt={props.data.label}
src={`${assetsBucketBaseUrl}/honkai3rd/elysian-realm/remembrance-sigils/${props.data.value}.png`}
mr={2}
/>
{props.children}
</Flex>
</components.SingleValue>
</>
)
},
Option: (props) => {
return (
<>
<components.Option {...props}>
<Flex sx={{ alignItems: 'center', color: 'black' }}>
<Image
width={20}
height={20}
alt={props.data.label}
src={`${assetsBucketBaseUrl}/honkai3rd/elysian-realm/remembrance-sigils/${props.data.value}.png`}
mr={2}
/>
{props.children}
</Flex>
</components.Option>
</>
)
},
}}
/>
)
}
export default memo(SigilSelect)
@@ -0,0 +1,156 @@
import React from 'react'
import { Box, Flex, Image, Paragraph } from 'theme-ui'
import { assetsBucketBaseUrl } from '../../../lib/consts'
import { signetGroupMap } from '../../../lib/honkai3rd/elysianRealm'
import { ERGGSignet } from './types'
interface SignetBoxProps {
signets: ERGGSignet[]
}
const SignetBox = ({ signets }: SignetBoxProps) => {
return (
<>
{signets.map((signet, index) => {
const dia = 460
const radius = dia / 2
const margin = 30
const descriptionTop = 40 + margin + ((dia - margin * 2) / 4) * index
const top =
40 + radius + radius * Math.sin((30 / 180) * Math.PI * (index - 2))
const angle = Math.asin((radius - (top - 40)) / radius)
const left = Math.cos(angle) * radius + 100 + radius
const signetRadius = 45
return (
<React.Fragment key={index}>
<Flex
sx={{
position: 'absolute',
zIndex: 1,
top: `${top - signetRadius}px`,
left: `${left - signetRadius}px`,
backgroundColor: '#10131C',
height: signetRadius * 2,
width: signetRadius * 2,
justifyContent: 'center',
alignItems: 'center',
borderRadius: signetRadius,
border: '1px solid gray',
boxShadow: '5px 5px 10px rgba(0,0,0,0.5)',
flexDirection: 'column',
}}
>
<Image
alt=''
src={`${assetsBucketBaseUrl}/honkai3rd/elysian-realm/signets/${signet.group}.png`}
width={45}
height={45}
/>
<Box
className='signetGroupLabel'
sx={{
transform: 'translateX(-3px)',
fontSize: 18,
}}
>
{getSignetAltName(signet.group)}
</Box>
<Box
sx={{
backgroundColor: 'black',
border: '1px solid gray',
width: 20,
height: 20,
textAlign: 'center',
borderRadius: 4,
position: 'absolute',
bottom: '12px',
fontSize: 16,
lineHeight: '20px',
right: `${5}px`,
}}
>
{signet.nexus === 1 ? 'I' : 'II'}
</Box>
</Flex>
<Box
sx={{
position: 'absolute',
borderBottom: '2px dashed gray',
height: 0,
top: `${top}px`,
left: `${left}px`,
right: 340,
}}
/>
<Flex
sx={{
top: `${descriptionTop - 40}px`,
height: 80,
border: 'solid 1px gray',
boxSizing: 'border-box',
backgroundColor: 'rgba(0,0,0,0.5)',
boxShadow: '5px 5px 10px rgba(0,0,0,0.5)',
width: 330,
position: 'absolute',
right: 10,
alignItems: 'center',
}}
>
<Flex
sx={{
width: 80,
height: 60,
borderRight: 'solid 1px gray',
justifyContent: 'center',
alignItems: 'center',
mr: '5px',
flexShrink: 0,
}}
>
<Box
sx={{
fontSize: 4,
lineHeight: 1.3,
fontWeight: 'bold',
color:
signet.type === 'core'
? '#E39070'
: signet.type === 'start'
? '#FEDEC2'
: '#A59A9B',
}}
className='signetTypeLabel'
>
<Box>
<Box>
{signet.type === 'core'
? '핵심'
: signet.type === 'start'
? '과도'
: '보조'}
</Box>
<Box></Box>
</Box>
</Box>
</Flex>
<Paragraph
className='signetDescription'
sx={{ padding: '5px', whiteSpace: 'pre-wrap' }}
>
{signet.description}
</Paragraph>
</Flex>
</React.Fragment>
)
})}
</>
)
}
export default SignetBox
function getSignetAltName(id: string) {
return signetGroupMap.get(id)?.krAltName
}
@@ -0,0 +1,82 @@
import { useMemo } from 'react'
import ReactSelect, { components } from 'react-select'
import { Flex, Image } from 'theme-ui'
import { assetsBucketBaseUrl } from '../../../lib/consts'
import { signetGroups } from '../../../lib/honkai3rd/elysianRealm'
interface SignetGroupSelectProps {
instanceId: string
value: string
onChange: (newValue: string) => void
}
const SignetGroupSelect = ({
instanceId,
value,
onChange,
}: SignetGroupSelectProps) => {
const options = useMemo(() => {
return signetGroups.slice(1).map((signetGroup) => {
return {
value: signetGroup.id,
label: signetGroup.krAltName,
}
})
}, [])
const currentOption = options.find((option) => option.value === value)
return (
<ReactSelect
instanceId={instanceId}
value={currentOption}
onChange={(option) => {
if (option == null) {
return
}
onChange(option.value)
}}
options={options}
components={{
SingleValue: (props) => {
return (
<>
<components.SingleValue {...props}>
<Flex sx={{ alignItems: 'center', color: 'black' }}>
<Image
width={20}
height={20}
alt={props.data.label}
src={`${assetsBucketBaseUrl}/honkai3rd/elysian-realm/signets/${props.data.value}.png`}
sx={{ flexShrink: 0, mr: 2 }}
/>
{props.children}
</Flex>
</components.SingleValue>
</>
)
},
Option: (props) => {
return (
<>
<components.Option {...props}>
<Flex sx={{ alignItems: 'center', color: 'black' }}>
<Image
width={20}
height={20}
alt={props.data.label}
src={`${assetsBucketBaseUrl}/honkai3rd/elysian-realm/signets/${props.data.value}.png`}
sx={{ mr: 2, flexShrink: 0 }}
/>
{props.children}
</Flex>
</components.Option>
</>
)
},
}}
/>
)
}
export default SignetGroupSelect
@@ -0,0 +1,108 @@
import ReactSelect, { components } from 'react-select'
import { Flex, Image } from 'theme-ui'
import { memo, useMemo } from 'react'
import { assetsBucketBaseUrl } from '../../../lib/consts'
import { StigmataData } from '../../../lib/honkai3rd/stigmata'
interface StigmaSelectProps {
instanceId: string
value: string
optionIds: string[]
stigmata: StigmataData[]
onChange: (newValue: string) => void
}
const StigmaSelect = ({
instanceId,
optionIds,
stigmata,
onChange,
value,
}: StigmaSelectProps) => {
const stigmaOptions = useMemo(() => {
return optionIds.map((stigmaId) => {
const stigma = stigmata.find((aStigma) => {
return aStigma.id === stigmaId
})
if (stigma == null) {
return {
value: 'unknown',
label: 'unknown',
}
}
return {
value: stigma.id,
label: stigma.name,
}
})
}, [optionIds, stigmata])
const stigma = useMemo(
() =>
stigmata.find((aStigma) => {
return aStigma.id === value
}),
[value, stigmata]
)
return (
<ReactSelect
instanceId={instanceId}
value={
stigma != null
? {
label: stigma.name,
value: stigma.id,
}
: null
}
onChange={(option) => {
if (option == null) {
return
}
onChange(option.value)
}}
options={stigmaOptions}
components={{
SingleValue: (props) => {
return (
<>
<components.SingleValue {...props}>
<Flex sx={{ alignItems: 'center', color: 'black' }}>
<Image
width={20}
height={20}
alt={props.data.label}
src={`${assetsBucketBaseUrl}/honkai3rd/stigmata/icon-${props.data.value}.png`}
sx={{ mr: 2, flexShrink: 0 }}
/>
{props.children}
</Flex>
</components.SingleValue>
</>
)
},
Option: (props) => {
return (
<>
<components.Option {...props}>
<Flex sx={{ alignItems: 'center', color: 'black' }}>
<Image
width={20}
height={20}
alt={props.data.label}
src={`${assetsBucketBaseUrl}/honkai3rd/stigmata/icon-${props.data.value}.png`}
sx={{ mr: 2, flexShrink: 0 }}
/>
{props.children}
</Flex>
</components.Option>
</>
)
},
}}
/>
)
}
export default memo(StigmaSelect)
@@ -0,0 +1,132 @@
import { Box, Flex, Image } from 'theme-ui'
import { assetsBucketBaseUrl } from '../../../lib/consts'
import { ERGGSupportSet } from './types'
interface SupportBoxProps {
supportSets: ERGGSupportSet[]
}
const SupportBox = ({ supportSets }: SupportBoxProps) => {
return (
<Box
sx={{
padding: '5px',
border: 'solid 1px gray',
boxSizing: 'border-box',
backgroundColor: 'rgba(0,0,0,0.5)',
boxShadow: '5px 5px 10px rgba(0,0,0,0.5)',
}}
>
<Flex>
<Box
sx={{
backgroundColor: '#8E5B45',
p: '5px',
textAlign: 'center',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
width: 55,
mr: '5px',
}}
>
<Box></Box>
<Box></Box>
</Box>
{supportSets.map((supportSet, index) => {
return (
<Box
key={index}
sx={{
backgroundColor: '#615559',
p: '15px 5px 0',
borderRadius: '5px',
position: 'relative',
mr: '5px',
'&:last-child': {
mr: 0,
},
}}
>
<Box
sx={{
position: 'absolute',
top: 0,
left: 0,
backgroundColor:
supportSet.type === 'util' ? '#356D7E' : '#EE4D2A',
borderTopLeftRadius: '5px',
borderBottomRightRadius: '5px',
p: '0 7px',
fontSize: 12,
}}
>
{supportSet.type === 'util' ? '유틸' : '딜링'}
</Box>
<Box sx={{ height: 40 }}>
{supportSet.battlesuitIds.map((battlesuitId) => {
return (
<Image
key={battlesuitId}
alt=''
src={`${assetsBucketBaseUrl}/honkai3rd/battlesuits/portrait-${battlesuitId}.png`}
width={40}
height={40}
sx={{
borderRadius: '5px',
mr: '5px',
'&:last-child': { mr: 0 },
}}
/>
)
})}
</Box>
<Flex
sx={{
height: 20,
fontSize: 12,
alignItems: 'center',
justifyContent: 'center',
}}
>
{getSupportName(supportSet.battlesuitIds[0])}+
{getSupportName(supportSet.battlesuitIds[1])}
</Flex>
</Box>
)
})}
</Flex>
</Box>
)
}
export default SupportBox
function getSupportName(id: string) {
switch (id) {
case 'le':
return '귀메'
case 'vc':
return '삐로냐'
case 'ss':
return '빙로냐'
case 'sn':
return '쩨레'
case 'br':
return '스메코'
case 'ae':
return '선인'
case 'hb':
return '브로니'
case 'ma':
return '레이븐'
case 'dp':
return '성녀'
case 'bke':
return '월백'
case 'rc':
return '파르도'
case 'sa':
return '테갈'
}
}
@@ -0,0 +1,149 @@
import { Box, Flex, Text } from 'theme-ui'
import { assetsBucketBaseUrl } from '../../../lib/consts'
import { colors } from './styles'
import { ERGGExSignet, ERGGExSignetType } from './types'
import { getExSignetLabel } from './utils'
interface ValkBoxProps {
battlesuitId: string
exSignets: ERGGExSignet[]
}
const ValkBox = ({ battlesuitId, exSignets }: ValkBoxProps) => {
return (
<Box
sx={{
position: 'absolute',
left: 100,
top: 40,
width: 460,
height: 460,
}}
>
{exSignets.map(({ type, name }, index) => {
return (
<ValkExSignetBox key={index} index={index} type={type} name={name} />
)
})}
<Box
sx={{
borderRadius: '50%',
border: 'solid 1px gray',
boxShadow: '5px 5px 10px rgba(0,0,0,0.5)',
width: 460,
height: 460,
backgroundImage: `url('${assetsBucketBaseUrl}/honkai3rd/battlesuits/${battlesuitId}.png')`,
backgroundSize: 'cover',
backgroundPosition: getBattlesuitImagePosition(battlesuitId),
backgroundRepeat: 'no-repeat',
backgroundColor: 'rgba(0,0,0,0.2)',
}}
></Box>
</Box>
)
}
function getBattlesuitImagePosition(battlesuitId: string) {
switch (battlesuitId) {
case 'meme':
return '0 10px'
default:
return 'center'
}
}
const ValkExSignetBox = ({
index,
type,
name,
}: {
index: number
type: ERGGExSignetType
name: string
}) => {
const label = getExSignetLabel(name)
const intervalAngle = 15
const angle = -intervalAngle * (index + 1) - 24
return (
<Box
sx={{
position: 'absolute',
top: 230 - 230 * Math.cos((angle / 180) * Math.PI) - 25,
left: 230 + 230 * Math.sin((angle / 180) * Math.PI) - 25,
width: 50,
height: 50,
}}
>
<Box
sx={{
position: 'absolute',
backgroundColor:
type === 'start'
? '#C52CC5'
: type === '1st'
? '#EE4D2A'
: type === '2nd'
? '#9082BD'
: type === 'backup'
? '#C4C4C4'
: 'transparent',
borderWidth: 1,
borderStyle: 'solid',
color: type === 'backup' ? 'black' : '#fff',
borderColor: type === 'na' ? '#AA9FA3' : 'transparent',
top: '5px',
right: 25,
whiteSpace: 'nowrap',
paddingRight: '30px',
paddingLeft: '10px',
textAlign: 'right',
lineHeight: 1.3,
zIndex: 1,
borderTopLeftRadius: '4px',
borderBottomLeftRadius: '4px',
}}
>
{type === 'start'
? '시작'
: type === '1st'
? '우선'
: type === '2nd'
? '차선'
: type === 'backup'
? '땜빵'
: '미선택'}
</Box>
<Flex
sx={{
position: 'absolute',
zIndex: 2,
border: 'solid 1px gray',
borderRadius: '50%',
backgroundColor: colors.backgroundColor,
boxShadow: '5px 5px 10px rgba(0,0,0,0.5)',
alignItems: 'center',
justifyContent: 'center',
fontSize:
label.length > 3
? label.indexOf('\n') >= 0
? '14px'
: '11px'
: label.length > 2
? 16
: 20,
width: 50,
height: 50,
lineHeight: 1.2,
textAlign: 'center',
whiteSpace: 'pre',
}}
className='exSignetLabel'
>
<Text>{label}</Text>
</Flex>
</Box>
)
}
export default ValkBox
@@ -0,0 +1,105 @@
import ReactSelect, { components } from 'react-select'
import { Flex, Image } from 'theme-ui'
import { memo, useMemo } from 'react'
import { assetsBucketBaseUrl } from '../../../lib/consts'
import { WeaponData } from '../../../lib/honkai3rd/weapons'
interface WeaponSelectProps {
instanceId: string
value: string
optionIds: string[]
weapons: WeaponData[]
onChange: (newValue: string) => void
}
const WeaponSelect = ({
instanceId,
optionIds,
weapons,
onChange,
value,
}: WeaponSelectProps) => {
const weaponOptions = useMemo(() => {
return optionIds.map((weaponId) => {
const weapon = weapons.find((aWeapon) => {
return aWeapon.id === weaponId
})
if (weapon == null) {
return {
value: 'unknown',
label: 'unknown',
}
}
return {
value: weapon.id,
label: weapon.name,
}
})
}, [optionIds, weapons])
const weaponValue = useMemo(() => {
const weapon = weapons.find((aWeapon) => {
return aWeapon.id === value
})
return weapon != null
? {
label: weapon.name,
value: weapon.id,
}
: null
}, [value, weapons])
return (
<ReactSelect
instanceId={instanceId}
value={weaponValue}
onChange={(option) => {
if (option == null) {
return
}
onChange(option.value)
}}
options={weaponOptions}
components={{
SingleValue: (props) => {
return (
<>
<components.SingleValue {...props}>
<Flex sx={{ alignItems: 'center', color: 'black' }}>
<Image
width={20}
height={20}
alt={props.data.label}
src={`${assetsBucketBaseUrl}/honkai3rd/weapons/${props.data.value}.png`}
sx={{ mr: 2, flexShrink: 0 }}
/>
{props.children}
</Flex>
</components.SingleValue>
</>
)
},
Option: (props) => {
return (
<>
<components.Option {...props}>
<Flex sx={{ alignItems: 'center', color: 'black' }}>
<Image
width={20}
height={20}
alt={props.data.label}
src={`${assetsBucketBaseUrl}/honkai3rd/weapons/${props.data.value}.png`}
sx={{ mr: 2, flexShrink: 0 }}
/>
{props.children}
</Flex>
</components.Option>
</>
)
},
}}
/>
)
}
export default memo(WeaponSelect)
@@ -0,0 +1,6 @@
export const colors = {
backgroundColor: '#311E22',
abstinenceDifficultyColor: '#F09F85',
corruptionDifficultyColor: '#EE4E2C',
infernoDifficultyColor: '#9DA3D3',
}
@@ -0,0 +1,51 @@
export type ERGGExSignetType = 'start' | '1st' | '2nd' | 'backup' | 'na'
export interface ERGGExSignet {
type: ERGGExSignetType
name: string
}
export type ERGGDataUpdater = <T extends keyof ERGGData>(
key: T,
value: ERGGData[T] | ((previousData: ERGGData[T]) => ERGGData[T])
) => void
export interface ERGGSupportSet {
type: 'util' | 'dps'
battlesuitIds: [string, string]
}
export interface ERGGSigilSet {
type: 'start' | 'mid' | 'end'
sigilIds: [string, string]
}
export type ERGGDifficulty = 'abstinence' | 'corruption' | 'inferno'
export interface ERGGEquipmentSet {
type: 'best' | 'alt'
weapon: string
top: string
mid: string
bot: string
}
export interface ERGGSignet {
group: string
type: 'core' | 'start' | 'sub'
nexus: 1 | 2
description: string
}
export type ERGGData = {
rank?: string
tag: string
signature: string
battlesuitId: string
difficulty: ERGGDifficulty
exSignets: ERGGExSignet[]
supportSets: ERGGSupportSet[]
sigilSets: ERGGSigilSet[]
equipmentSets: ERGGEquipmentSet[]
signets: ERGGSignet[]
}
@@ -0,0 +1,6 @@
export function getExSignetLabel(name: string) {
return name
.replace('의 축복', '')
.replace(/\s/g, '\n')
.replace(/[\[\]]/g, '')
}
+22
View File
@@ -113,6 +113,26 @@ export const remembranceSigilIds = [
'key-to-the-deep', 'key-to-the-deep',
] ]
export function isGeneralSigil(sigilId: string) {
switch (sigilId) {
case 'it-will-be-written':
case 'dreamful-gold':
case 'an-old-pal-s-legacy':
case 'empty-like-shala':
case 'tsukimi-himiko':
case 'boundless-logos':
case 'hometown':
case 'because-of-you':
case 'boundless-feeling':
case 'falling-in-past-light':
case 'out-of-reach':
case 'the-lonely-moon':
case 'awakening':
return false
}
return true
}
export interface SignetData { export interface SignetData {
id: string id: string
name: string name: string
@@ -127,6 +147,8 @@ export interface SignetGroup {
id: string id: string
name: string name: string
krName: string krName: string
altName: string
krAltName: string
setIds: string[] setIds: string[]
} }
+8
View File
@@ -121,6 +121,11 @@ export const theme: Theme = {
lineHeight: 'heading', lineHeight: 'heading',
}, },
}, },
forms: {
checkbox: {
color: 'border',
},
},
buttons: { buttons: {
primary: { primary: {
color: 'primary', color: 'primary',
@@ -144,6 +149,9 @@ export const theme: Theme = {
'&.hidden': { '&.hidden': {
display: 'none', display: 'none',
}, },
'&:disabled': {
opacity: 0.5,
},
}, },
secondary: { secondary: {
color: 'background', color: 'background',
+7 -1
View File
@@ -4,7 +4,6 @@ import Breadcrumb from '../../../components/organisms/Breadcrumb'
import { getBattlesuitMapByIds } from '../../../server/data/honkai3rd/battlesuits' import { getBattlesuitMapByIds } from '../../../server/data/honkai3rd/battlesuits'
import { BattlesuitData } from '../../../lib/honkai3rd/battlesuits' import { BattlesuitData } from '../../../lib/honkai3rd/battlesuits'
import BattlesuitCard from '../../../components/molecules/BattlesuitCard' import BattlesuitCard from '../../../components/molecules/BattlesuitCard'
import { NextPageContext } from 'next' import { NextPageContext } from 'next'
import { getI18NProps } from '../../../server/i18n' import { getI18NProps } from '../../../server/i18n'
import { useTranslation } from '../../../lib/i18n' import { useTranslation } from '../../../lib/i18n'
@@ -19,6 +18,7 @@ import {
getRemembranceSigils as getRemembranceSigils, getRemembranceSigils as getRemembranceSigils,
getSupportBattlesuits, getSupportBattlesuits,
} from '../../../server/data/honkai3rd/elysianRealm' } from '../../../server/data/honkai3rd/elysianRealm'
import Link from 'next/link'
type BattlesuitListItemData = Pick< type BattlesuitListItemData = Pick<
BattlesuitData, BattlesuitData,
@@ -152,6 +152,11 @@ const ElysianRealmIndexPage = ({
})} })}
</Flex> </Flex>
</Box> </Box>
<Box>
<Link href='/honkai3rd/elysian-realm/utils/gen-guide'>
Guide Generator (KR Only)
</Link>
</Box>
</Box> </Box>
</Honkai3rdLayout> </Honkai3rdLayout>
) )
@@ -180,6 +185,7 @@ export async function getStaticProps({ locale }: NextPageContext) {
name, name,
} }
}) })
return { return {
props: { props: {
battlesuitMap: getBattlesuitMapByIds(erBattlesuitIds, locale), battlesuitMap: getBattlesuitMapByIds(erBattlesuitIds, locale),
@@ -0,0 +1,88 @@
/* eslint-disable @next/next/no-page-custom-font */
import { NextPageContext } from 'next'
import { useRouter } from 'next/router'
import { pick } from 'ramda'
import { Box, Button, Flex, Heading } from 'theme-ui'
import ERGuideGenerator from '../../../../components/organisms/ERGuideGenerator/ERGuideGenerator'
import { BattlesuitData } from '../../../../lib/honkai3rd/battlesuits'
import {
PopulatedSignetGroup,
RemembranceSigil,
} from '../../../../lib/honkai3rd/elysianRealm'
import { WeaponData } from '../../../../lib/honkai3rd/weapons'
import { StigmataData } from '../../../../lib/honkai3rd/stigmata'
import { listBattlesuits } from '../../../../server/data/honkai3rd/battlesuits'
import {
getRemembranceSigils,
getSignetGroupById,
} from '../../../../server/data/honkai3rd/elysianRealm'
import { listStigmata } from '../../../../server/data/honkai3rd/stigmata'
import { listWeapons } from '../../../../server/data/honkai3rd/weapons'
import { getI18NProps } from '../../../../server/i18n'
interface GenerateGuidePageProps {
weapons: WeaponData[]
stigmata: StigmataData[]
battlesuits: BattlesuitData[]
exSignetGroup: PopulatedSignetGroup
sigils: RemembranceSigil[]
}
const GenerateGuidePage = ({
weapons,
stigmata,
battlesuits,
exSignetGroup,
sigils,
}: GenerateGuidePageProps) => {
const router = useRouter()
return (
<Box>
<Flex m={2} sx={{ alignItems: 'center' }}>
<Button
onClick={() => {
router.push('/honkai3rd')
}}
mr={2}
>
Back
</Button>
<Heading>ER Guide Generator</Heading>
</Flex>
<ERGuideGenerator
weapons={weapons}
stigmata={stigmata}
battlesuits={battlesuits}
exSignetGroup={exSignetGroup}
sigils={sigils}
/>
</Box>
)
}
export default GenerateGuidePage
export async function getStaticProps({ locale }: NextPageContext) {
return {
props: {
weapons: listWeapons('ko-KR').map((weapon) => {
return pick(['name', 'id', 'rarity', 'category'], weapon)
}),
stigmata: listStigmata('ko-KR').map((stigmata) => {
return pick(['name', 'id', 'type'], stigmata)
}),
battlesuits: listBattlesuits('ko-KR').map((battlesuit) => {
return {
id: battlesuit.id,
name: battlesuit.name,
type: battlesuit.type,
valkyrie: battlesuit.valkyrie,
}
}),
exSignetGroup: getSignetGroupById('elysia', 'ko-KR'),
sigils: getRemembranceSigils('ko-KR'),
...(await getI18NProps('ko-KR')),
},
}
}