From 14be16ce744d67c763cae4413f8c0eaf36990ade Mon Sep 17 00:00:00 2001 From: Hide_D Date: Wed, 20 Apr 2022 03:34:12 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20Vue3=EB=A1=9C=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=EB=90=9C=20=EC=A7=80=EB=8F=84=20=EB=A0=8C=EB=8D=94=EB=9F=AC=20?= =?UTF-8?q?(#216)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기존의 jQuery 기반 지도 렌더러를 Vue3로 재작성. - ajax로 호출하는 부분은 외부에서 처리하고, 다시 그리는 부분만 생성 - 기능상으론 거의 동일. - 일반 렌더러의 700px에, 500px모드용 모드 추가 - 아이콘의 크기는 작아지지만, 글자 크기는 작아지지 않도록 조정 dep: vueuse, detect-it 추가 Reviewed-on: https://storage.hided.net/gitea/devsam/core/pulls/216 --- hwe/css/map.css | 10 +- hwe/scss/map.scss | 665 ++++++++++++++++++++++++++++ hwe/ts/components/MapCityBasic.vue | 137 ++++++ hwe/ts/components/MapCityDetail.vue | 154 +++++++ hwe/ts/components/MapViewer.vue | 465 +++++++++++++++++++ hwe/ts/state/is1000pxMode.ts | 3 + hwe/ts/state/mapViewer.ts | 11 + package-lock.json | 124 ++++++ package.json | 2 + 9 files changed, 1565 insertions(+), 6 deletions(-) create mode 100644 hwe/scss/map.scss create mode 100644 hwe/ts/components/MapCityBasic.vue create mode 100644 hwe/ts/components/MapCityDetail.vue create mode 100644 hwe/ts/components/MapViewer.vue create mode 100644 hwe/ts/state/is1000pxMode.ts create mode 100644 hwe/ts/state/mapViewer.ts diff --git a/hwe/css/map.css b/hwe/css/map.css index 0b3dcca8..c50898ca 100644 --- a/hwe/css/map.css +++ b/hwe/css/map.css @@ -1,5 +1,3 @@ -@charset "UTF-8"; - .world_map { width: 700px; background: black; @@ -69,11 +67,11 @@ } .map_body .map_toggle_cityname::after { - content: "끄기" + content: " 끄기" } .map_body .map_toggle_cityname.active::after { - content: "켜기" + content: " 켜기" } .map_body .map_toggle_single_tap { @@ -81,11 +79,11 @@ } .map_body .map_toggle_single_tap::after { - content: "끄기" + content: " 끄기" } .map_body .map_toggle_single_tap.active::after { - content: "켜기" + content: " 켜기" } .world_map .city_tooltip { diff --git a/hwe/scss/map.scss b/hwe/scss/map.scss new file mode 100644 index 00000000..26d500ba --- /dev/null +++ b/hwe/scss/map.scss @@ -0,0 +1,665 @@ +.map_title_tooltiptext .tooltip-inner { + max-width: 220px; + width: 220px; + text-align: left; +} + +.map_title_text { + margin: auto; + text-align: center; + width: 160px; + display: block; + line-height: 20px; + font-size: 14px; + font-weight: bold; +} + +.map_body .map_bglayer1 { + width: 100%; + height: 100%; + position: absolute; + left: 0; + top: 0; +} + +.map_body .map_bglayer2 { + width: 100%; + height: 100%; + position: absolute; + left: 0; + top: 0; +} + +.map_body .map_bgroad { + width: 100%; + height: 100%; + position: absolute; + left: 0; + top: 0; +} + +.map_body .map_button_stack { + position: absolute; + right: 0; + bottom: 0; + text-align: right; +} + +.map_body .map_toggle_cityname::after { + content: " 끄기"; +} + +.map_body .map_toggle_cityname.active::after { + content: " 켜기"; +} + +.map_body .map_toggle_single_tap::after { + content: " 끄기"; +} + +.map_body .map_toggle_single_tap.active::after { + content: " 켜기"; +} + +.world_map .city_tooltip { + position: absolute; + z-index: 16; + display: none; + min-width: 120px; + border: 0.02em gray solid; + white-space: nowrap; + font-size: 14px; +} + +.world_map .city_name { + background-color: rgb(30, 164, 255); + z-index: 6; + line-height: 15px; + height: 15px; +} + +.world_map .nation_name { + background-color: rgb(30, 164, 255); + z-index: 6; + line-height: 15px; + height: 15px; + border-top: 0.02em gray solid; + text-align: right; +} + +@keyframes blink-my-city-d1 { + 0% { + outline: solid 4px transparent; + } + + 50% { + outline: solid 4px rgba(255, 0, 0, 1); + } + + 100% { + outline: solid 4px transparent; + } +} + +@keyframes blink-my-city-d2 { + 0% { + outline: double 4px transparent; + } + + 50% { + outline: double 4px rgba(192, 192, 192, 1); + } + + 100% { + outline: double 4px transparent; + } +} + +/* Basic */ + +@keyframes blink-my-city { + 0% { + outline: dashed 5px transparent; + } + + 50% { + outline: dashed 5px white; + } + + 100% { + outline: dashed 5px transparent; + } +} + +@keyframes blink-my-city2 { + 0% { + outline: dashed 4px transparent; + } + + 50% { + outline: dashed 4px red; + } + + 100% { + outline: dashed 4px transparent; + } +} + +.btn.btn-minimum { + padding-top: 1px; + padding-bottom: 1px; +} + +.world_map.hide_cityname .city_detail_name { + display: none; +} + +.city_base .city_bg { + z-index: 1; + position: absolute; + background-position: center; +} + +.city_base div { + z-index: 2; +} + +$cityBaseWidth: 40px; +$cityBaseHeight: 30px; + +$detailMapCitySizes: ( + (1, 48px, 45px, 16px, 15px, -8px, -4px), + (2, 60px, 42px, 20px, 14px, -8px, -4px), + (3, 42px, 42px, 14px, 14px, -8px, -4px), + (4, 60px, 45px, 20px, 15px, -6px, -3px), + (5, 72px, 48px, 24px, 16px, -6px, -4px), + (6, 78px, 54px, 26px, 18px, -6px, -4px), + (7, 84px, 60px, 28px, 20px, -6px, -4px), + (8, 96px, 72px, 32px, 24px, -6px, -3px) +); + +$basicMapCitySize: ( + (1, 12px, 12px), + (2, 12px, 12px), + (3, 14px, 14px), + (4, 16px, 14px), + (5, 18px, 16px), + (6, 20px, 16px), + (7, 22px, 18px), + (8, 24px, 18px) +); +.city_img { + position: absolute; +} + +.world_map.full_width_map { + width: 700px; + background: black; + font-size: 14px; + color: white; + position: relative; + + .map_title { + width: 700px; + height: 20px; + text-align: center; + } + + .map_body { + width: 700px; + height: 500px; + position: relative; + overflow: hidden; + } + + .city_base { + position: absolute; + width: $cityBaseWidth; + height: $cityBaseHeight; + } + + .city_detail_name { + background-color: rgba(0, 0, 0, 0.5); + position: absolute; + white-space: nowrap; + left: 70%; + font-size: 10px; + bottom: -10px; + color: white; + } + + &.map_detail { + @each $cityIdx, $cityAreaWidth, $cityAreaHeight, $cityIconWidth, $cityIconHeight, $flagRight, + $flagTop in $detailMapCitySizes + { + .my_city { + border-radius: 33%; + } + + .my_city:before { + content: ""; + top: -2px; + left: -2px; + right: -2px; + bottom: -2px; + position: absolute; + border-radius: 33%; + overflow: hidden; + + animation-duration: 3.9s; + animation-name: blink-my-city-d1; + animation-iteration-count: infinite; + } + + .my_city:after { + content: ""; + top: -2px; + left: -2px; + right: -2px; + bottom: -2px; + position: absolute; + border-radius: 33%; + overflow: hidden; + + animation-duration: 3.9s; + animation-name: blink-my-city-d2; + animation-iteration-count: infinite; + } + + .city_filler { + position: absolute; + width: calc(100% + 2px); + height: calc(100% + 2px); + left: -1px; + top: -1px; + background: transparent; + } + + .city_base.city_level_#{$cityIdx} { + .city_bg { + width: $cityAreaWidth; + height: $cityAreaHeight; + background-size: $cityAreaWidth $cityAreaHeight; + left: calc(($cityBaseWidth - $cityAreaWidth) / 2); + top: calc(($cityBaseHeight - $cityAreaHeight) / 2); + } + .city_img { + width: $cityIconWidth; + height: $cityIconHeight; + background-size: $cityIconWidth $cityIconHeight; + left: calc(($cityBaseWidth - $cityIconWidth) / 2); + top: calc(($cityBaseHeight - $cityIconHeight) / 2); + + > img { + width: $cityIconWidth; + height: $cityIconHeight; + } + } + .city_flag { + right: $flagRight; + top: $flagTop; + } + } + } + + .city_base { + .city_state { + position: absolute; + top: 5px; + /*TODO:도시 레벨 별로 다르게 위치 설정해볼 것*/ + left: 0; + } + + .city_flag { + position: absolute; + width: 12px; + height: 12px; + + .city_capital { + position: absolute; + width: 10px; + height: 10px; + top: 0; + right: -1px; + + > img { + width: 10px; + height: 10px; + } + } + } + + .city_flag > img { + width: 12px; + height: 12px; + } + } + } + + &.map_basic { + .city_filler { + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + background: transparent; + } + + .my_city:before { + content: ""; + top: -2px; + left: -2px; + right: -2px; + bottom: -2px; + position: absolute; + + animation-duration: 2s; + animation-name: blink-my-city; + animation-iteration-count: infinite; + } + + .my_city:after { + content: ""; + top: -2px; + left: -2px; + right: -2px; + bottom: -2px; + position: absolute; + + animation-duration: 2s; + animation-name: blink-my-city2; + animation-iteration-count: infinite; + } + + .city_img { + background-color: white; + } + + @each $cityIdx, $cityIconWidth, $cityIconHeight in $basicMapCitySize { + .city_base.city_level_#{$cityIdx} { + .city_img { + width: $cityIconWidth; + height: $cityIconHeight; + left: calc(($cityBaseWidth - $cityIconWidth) / 2); + top: calc(($cityBaseHeight - $cityIconHeight) / 2); + } + } + } + + .city_base { + .city_capital { + position: absolute; + width: 5px; + height: 5px; + top: -2px; + right: -2px; + background-color: yellow; + } + + .city_state { + position: absolute; + width: 10px; + height: 10px; + top: -2px; + left: -4px; + background-color: white; + } + + .city_state.city_state_war { + background-color: red; + } + + .city_state.city_state_bad { + background-color: orange; + } + + .city_state.city_state_good { + background-color: blue; + } + } + } +} + +$smV: calc(500 / 700); + +.world_map.small_width_map { + width: 500px; + background: black; + font-size: 12px; + color: white; + position: relative; + + .map_body { + .map_bglayer1, + .map_bglayer2, + .map_bgroad { + background-size: calc(700px * $smV) calc(500px * $smV); + } + } + + .map_title { + width: 700px * $smV; + height: 20px; + text-align: center; + } + + .map_body { + width: 700px * $smV; + height: 500px * $smV; + position: relative; + overflow: hidden; + } + + .city_base { + position: absolute; + width: $cityBaseWidth; + height: $cityBaseHeight; + } + + .city_detail_name { + background-color: rgba(0, 0, 0, 0.5); + position: absolute; + white-space: nowrap; + left: 70%; + font-size: 10px; + bottom: -12px; + color: white; + } + + &.map_detail { + @each $cityIdx, $cityAreaWidth, $cityAreaHeight, $cityIconWidth, $cityIconHeight, $flagRight, + $flagTop in $detailMapCitySizes + { + .my_city { + border-radius: 33%; + } + + .my_city:before { + content: ""; + top: -2px; + left: -2px; + right: -2px; + bottom: -2px; + position: absolute; + border-radius: 33%; + overflow: hidden; + + animation-duration: 3.9s; + animation-name: blink-my-city-d1; + animation-iteration-count: infinite; + } + + .my_city:after { + content: ""; + top: -2px; + left: -2px; + right: -2px; + bottom: -2px; + position: absolute; + border-radius: 33%; + overflow: hidden; + + animation-duration: 3.9s; + animation-name: blink-my-city-d2; + animation-iteration-count: infinite; + } + + .city_filler { + position: absolute; + width: calc(100% + 2px); + height: calc(100% + 2px); + left: -1px; + top: -1px; + background: transparent; + } + + .city_base.city_level_#{$cityIdx} { + .city_bg { + width: $cityAreaWidth * $smV; + height: $cityAreaHeight * $smV; + background-size: calc($cityAreaWidth * $smV) calc($cityAreaHeight * $smV); + left: calc(($cityBaseWidth - $cityAreaWidth * $smV) / 2); + top: calc(($cityBaseHeight - $cityAreaHeight * $smV) / 2); + } + .city_img { + width: $cityIconWidth * $smV; + height: $cityIconHeight * $smV; + background-size: calc($cityIconWidth * $smV) calc($cityIconHeight * $smV); + left: calc(($cityBaseWidth - $cityIconWidth * $smV) / 2); + top: calc(($cityBaseHeight - $cityIconHeight * $smV) / 2); + + > img { + width: $cityIconWidth * $smV; + height: $cityIconHeight * $smV; + } + } + .city_flag { + right: $flagRight; + top: $flagTop; + } + } + } + + .city_base { + .city_state { + position: absolute; + top: 8px; + /*TODO:도시 레벨 별로 다르게 위치 설정해볼 것*/ + left: 5px; + > img { + width: 10px; + object-fit: contain; + } + } + + .city_flag { + position: absolute; + width: 12px * $smV; + height: 12px * $smV; + + > img { + width: 12px * $smV; + height: 12px * $smV; + } + + .city_capital { + position: absolute; + width: 10px * $smV; + height: 10px * $smV; + top: 0; + right: -1px; + + > img { + width: 10px * $smV; + height: 10px * $smV; + } + } + } + } + } + + &.map_basic { + .city_filler { + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + background: transparent; + } + + .my_city:before { + content: ""; + top: -2px; + left: -2px; + right: -2px; + bottom: -2px; + position: absolute; + + animation-duration: 2s; + animation-name: blink-my-city; + animation-iteration-count: infinite; + } + + .my_city:after { + content: ""; + top: -2px; + left: -2px; + right: -2px; + bottom: -2px; + position: absolute; + + animation-duration: 2s; + animation-name: blink-my-city2; + animation-iteration-count: infinite; + } + + .city_img { + background-color: white; + } + + @each $cityIdx, $cityIconWidth, $cityIconHeight in $basicMapCitySize { + .city_base.city_level_#{$cityIdx} { + .city_img { + width: $cityIconWidth * $smV; + height: $cityIconHeight * $smV; + left: calc(($cityBaseWidth - $cityIconWidth * $smV) / 2); + top: calc(($cityBaseHeight - $cityIconHeight * $smV) / 2); + } + } + } + + .city_base { + .city_capital { + position: absolute; + width: 5px; + height: 5px; + top: -2px; + right: -2px; + background-color: yellow; + } + + .city_state { + position: absolute; + width: 8px; + height: 8px; + top: -3px; + left: -5px; + background-color: white; + } + + .city_state.city_state_war { + background-color: red; + } + + .city_state.city_state_bad { + background-color: orange; + } + + .city_state.city_state_good { + background-color: blue; + } + } + } +} diff --git a/hwe/ts/components/MapCityBasic.vue b/hwe/ts/components/MapCityBasic.vue new file mode 100644 index 00000000..e7182383 --- /dev/null +++ b/hwe/ts/components/MapCityBasic.vue @@ -0,0 +1,137 @@ + + + + + diff --git a/hwe/ts/components/MapCityDetail.vue b/hwe/ts/components/MapCityDetail.vue new file mode 100644 index 00000000..42836431 --- /dev/null +++ b/hwe/ts/components/MapCityDetail.vue @@ -0,0 +1,154 @@ + + + + + diff --git a/hwe/ts/components/MapViewer.vue b/hwe/ts/components/MapViewer.vue new file mode 100644 index 00000000..c75802dc --- /dev/null +++ b/hwe/ts/components/MapViewer.vue @@ -0,0 +1,465 @@ + + + + diff --git a/hwe/ts/state/is1000pxMode.ts b/hwe/ts/state/is1000pxMode.ts new file mode 100644 index 00000000..cd062030 --- /dev/null +++ b/hwe/ts/state/is1000pxMode.ts @@ -0,0 +1,3 @@ +import { useMediaQuery } from "@vueuse/core"; + +export const is1000pxMode = useMediaQuery('(min-width:900px'); \ No newline at end of file diff --git a/hwe/ts/state/mapViewer.ts b/hwe/ts/state/mapViewer.ts new file mode 100644 index 00000000..f5a2764b --- /dev/null +++ b/hwe/ts/state/mapViewer.ts @@ -0,0 +1,11 @@ +import { watch, ref } from "vue"; + +export const hideMapCityName = ref(localStorage.getItem('sam.hideMapCityName') == 'yes'); +watch(hideMapCityName, (value) => { + localStorage.setItem('sam.hideMapCityName', value ? 'yes' : 'no'); +}); + +export const toggleSingleTap = ref(localStorage.getItem('sam.toggleSingleTap') == 'yes'); +watch(toggleSingleTap, (value) => { + localStorage.setItem('sam.toggleSingleTap', value ? 'yes' : 'no'); +}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a519dc2e..ffa99a27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,6 +37,7 @@ "@typescript-eslint/parser": "^5.14.0", "@vue/compiler-sfc": "^3.2.31", "@vue/eslint-config-typescript": "^10.0.0", + "@vueuse/core": "^8.2.6", "ag-grid-community": "^27.1.0", "ag-grid-vue3": "^27.1.0", "async-validator": "^4.0.7", @@ -56,6 +57,7 @@ "css-loader": "^6.7.1", "cssnano": "^5.1.3", "date-fns": "^2.28.0", + "detect-it": "^4.0.1", "downloadjs": "^1.4.7", "esbuild-loader": "^2.18.0", "eslint": "^8.11.0", @@ -2860,6 +2862,87 @@ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.31.tgz", "integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ==" }, + "node_modules/@vueuse/core": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-8.2.6.tgz", + "integrity": "sha512-fzlpM3B5oVe+UhCT1mXlhG1Zxdq2lq1Z2AvddSB8+RxrsSFzII7DKfsQEz8Vop7Lzc++4m8drTNbhPovYoFqHw==", + "dependencies": { + "@vueuse/metadata": "8.2.6", + "@vueuse/shared": "8.2.6", + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.1.0", + "vue": "^2.6.0 || ^3.2.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/@vueuse/core/node_modules/@vueuse/shared": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-8.2.6.tgz", + "integrity": "sha512-J/W4CMfdL8TahELuSOgtfVO4eQXTjhigp7dVWIBsLUVFCeY9d49gvHUcQN3y5xYLZ6iNP57TjTQjMMT/zhklkw==", + "dependencies": { + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.1.0", + "vue": "^2.6.0 || ^3.2.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/@vueuse/core/node_modules/vue-demi": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.12.5.tgz", + "integrity": "sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-8.2.6.tgz", + "integrity": "sha512-OBKtafCt+4RcEJlYDCjp1vl65pBCL2g4TmipEtdZ8/qphKlW6nakJbkY7XRN5grPmjqU99/ahJGtyGk5NHS2hw==", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -4248,6 +4331,11 @@ "node": ">= 0.4" } }, + "node_modules/detect-it": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/detect-it/-/detect-it-4.0.1.tgz", + "integrity": "sha512-dg5YBTJYvogK1+dA2mBUDKzOWfYZtHVba89SyZUhc4+e3i2tzgjANFg5lDRCd3UOtRcw00vUTMK8LELcMdicug==" + }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -12635,6 +12723,37 @@ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.31.tgz", "integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ==" }, + "@vueuse/core": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-8.2.6.tgz", + "integrity": "sha512-fzlpM3B5oVe+UhCT1mXlhG1Zxdq2lq1Z2AvddSB8+RxrsSFzII7DKfsQEz8Vop7Lzc++4m8drTNbhPovYoFqHw==", + "requires": { + "@vueuse/metadata": "8.2.6", + "@vueuse/shared": "8.2.6", + "vue-demi": "*" + }, + "dependencies": { + "@vueuse/shared": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-8.2.6.tgz", + "integrity": "sha512-J/W4CMfdL8TahELuSOgtfVO4eQXTjhigp7dVWIBsLUVFCeY9d49gvHUcQN3y5xYLZ6iNP57TjTQjMMT/zhklkw==", + "requires": { + "vue-demi": "*" + } + }, + "vue-demi": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.12.5.tgz", + "integrity": "sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==", + "requires": {} + } + } + }, + "@vueuse/metadata": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-8.2.6.tgz", + "integrity": "sha512-OBKtafCt+4RcEJlYDCjp1vl65pBCL2g4TmipEtdZ8/qphKlW6nakJbkY7XRN5grPmjqU99/ahJGtyGk5NHS2hw==" + }, "@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -13664,6 +13783,11 @@ "object-keys": "^1.0.12" } }, + "detect-it": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/detect-it/-/detect-it-4.0.1.tgz", + "integrity": "sha512-dg5YBTJYvogK1+dA2mBUDKzOWfYZtHVba89SyZUhc4+e3i2tzgjANFg5lDRCd3UOtRcw00vUTMK8LELcMdicug==" + }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", diff --git a/package.json b/package.json index 4bfd6640..0dcb29d7 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "@typescript-eslint/parser": "^5.14.0", "@vue/compiler-sfc": "^3.2.31", "@vue/eslint-config-typescript": "^10.0.0", + "@vueuse/core": "^8.2.6", "ag-grid-community": "^27.1.0", "ag-grid-vue3": "^27.1.0", "async-validator": "^4.0.7", @@ -69,6 +70,7 @@ "css-loader": "^6.7.1", "cssnano": "^5.1.3", "date-fns": "^2.28.0", + "detect-it": "^4.0.1", "downloadjs": "^1.4.7", "esbuild-loader": "^2.18.0", "eslint": "^8.11.0",