Markers sidebar section
This commit is contained in:
parent
9265f8a02a
commit
91739d513a
@ -92,6 +92,12 @@
|
|||||||
chatErrorUnknown: 'Unexpected error while sending chat message',
|
chatErrorUnknown: 'Unexpected error while sending chat message',
|
||||||
chatErrorDisabled: 'Chat is not enabled',
|
chatErrorDisabled: 'Chat is not enabled',
|
||||||
serversHeading: 'Servers',
|
serversHeading: 'Servers',
|
||||||
|
markersHeading: 'Markers',
|
||||||
|
markersSearchPlaceholder: 'Search markers...',
|
||||||
|
markersSkeleton: 'No markers exist for the current world',
|
||||||
|
markersSetSkeleton: 'This marker set is empty',
|
||||||
|
markersSearchSkeleton: 'No matching markers found',
|
||||||
|
markersUnnamed: '(Unnamed marker)',
|
||||||
worldsSkeleton: 'No maps have been configured',
|
worldsSkeleton: 'No maps have been configured',
|
||||||
playersSkeleton: 'No players are currently online',
|
playersSkeleton: 'No players are currently online',
|
||||||
playersTitle: 'Click to center on player\nDouble-click to follow player',
|
playersTitle: 'Click to center on player\nDouble-click to follow player',
|
||||||
@ -142,6 +148,7 @@
|
|||||||
logoutSuccess: 'Logged out successfully',
|
logoutSuccess: 'Logged out successfully',
|
||||||
|
|
||||||
closeTitle: 'Close',
|
closeTitle: 'Close',
|
||||||
|
showMore: 'Show more'
|
||||||
},
|
},
|
||||||
|
|
||||||
ui: {
|
ui: {
|
||||||
|
@ -23,6 +23,12 @@ export const globalMessages = [
|
|||||||
'chatErrorDisabled',
|
'chatErrorDisabled',
|
||||||
'chatErrorUnknown',
|
'chatErrorUnknown',
|
||||||
'serversHeading',
|
'serversHeading',
|
||||||
|
'markersHeading',
|
||||||
|
'markersSkeleton',
|
||||||
|
'markersSetSkeleton',
|
||||||
|
'markersUnnamed',
|
||||||
|
'markersSearchPlaceholder',
|
||||||
|
'markersSearchSkeleton',
|
||||||
'worldsSkeleton',
|
'worldsSkeleton',
|
||||||
'playersSkeleton',
|
'playersSkeleton',
|
||||||
'playersTitle',
|
'playersTitle',
|
||||||
@ -67,6 +73,7 @@ export const globalMessages = [
|
|||||||
'logoutErrorUnknown',
|
'logoutErrorUnknown',
|
||||||
'logoutSuccess',
|
'logoutSuccess',
|
||||||
'closeTitle',
|
'closeTitle',
|
||||||
|
'showMore',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const serverMessages = [
|
export const serverMessages = [
|
||||||
|
42
package-lock.json
generated
42
package-lock.json
generated
@ -12,6 +12,7 @@
|
|||||||
"@kyvg/vue3-notification": "2.3.0",
|
"@kyvg/vue3-notification": "2.3.0",
|
||||||
"@soerenmartius/vue3-clipboard": "^0.1",
|
"@soerenmartius/vue3-clipboard": "^0.1",
|
||||||
"leaflet": "git+https://github.com/JLyne/leaflet.git",
|
"leaflet": "git+https://github.com/JLyne/leaflet.git",
|
||||||
|
"lodash.debounce": "^4.0.8",
|
||||||
"modern-normalize": "^1.1.0",
|
"modern-normalize": "^1.1.0",
|
||||||
"vue": "^3.2.21",
|
"vue": "^3.2.21",
|
||||||
"vuex": "^4.0"
|
"vuex": "^4.0"
|
||||||
@ -21,6 +22,7 @@
|
|||||||
"@types/jest": "^27.4.0",
|
"@types/jest": "^27.4.0",
|
||||||
"@types/jest-in-case": "^1.0.5",
|
"@types/jest-in-case": "^1.0.5",
|
||||||
"@types/leaflet": "1.7.8",
|
"@types/leaflet": "1.7.8",
|
||||||
|
"@types/lodash.debounce": "^4.0.6",
|
||||||
"@types/node": "^17.0.8",
|
"@types/node": "^17.0.8",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.9",
|
"@typescript-eslint/eslint-plugin": "^5.9",
|
||||||
"@typescript-eslint/parser": "^5.9",
|
"@typescript-eslint/parser": "^5.9",
|
||||||
@ -1611,6 +1613,21 @@
|
|||||||
"@types/geojson": "*"
|
"@types/geojson": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/lodash": {
|
||||||
|
"version": "4.14.178",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.178.tgz",
|
||||||
|
"integrity": "sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@types/lodash.debounce": {
|
||||||
|
"version": "4.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.6.tgz",
|
||||||
|
"integrity": "sha512-4WTmnnhCfDvvuLMaF3KV4Qfki93KebocUF45msxhYyjMttZDQYzHkO639ohhk8+oco2cluAFL3t5+Jn4mleylQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/lodash": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/minimatch": {
|
"node_modules/@types/minimatch": {
|
||||||
"version": "3.0.5",
|
"version": "3.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
|
||||||
@ -8257,6 +8274,11 @@
|
|||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash.debounce": {
|
||||||
|
"version": "4.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||||
|
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
|
||||||
|
},
|
||||||
"node_modules/lodash.memoize": {
|
"node_modules/lodash.memoize": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
||||||
@ -13347,6 +13369,21 @@
|
|||||||
"@types/geojson": "*"
|
"@types/geojson": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/lodash": {
|
||||||
|
"version": "4.14.178",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.178.tgz",
|
||||||
|
"integrity": "sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@types/lodash.debounce": {
|
||||||
|
"version": "4.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.6.tgz",
|
||||||
|
"integrity": "sha512-4WTmnnhCfDvvuLMaF3KV4Qfki93KebocUF45msxhYyjMttZDQYzHkO639ohhk8+oco2cluAFL3t5+Jn4mleylQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/lodash": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/minimatch": {
|
"@types/minimatch": {
|
||||||
"version": "3.0.5",
|
"version": "3.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
|
||||||
@ -18368,6 +18405,11 @@
|
|||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"lodash.debounce": {
|
||||||
|
"version": "4.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||||
|
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
|
||||||
|
},
|
||||||
"lodash.memoize": {
|
"lodash.memoize": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
"@kyvg/vue3-notification": "2.3.0",
|
"@kyvg/vue3-notification": "2.3.0",
|
||||||
"@soerenmartius/vue3-clipboard": "^0.1",
|
"@soerenmartius/vue3-clipboard": "^0.1",
|
||||||
"leaflet": "git+https://github.com/JLyne/leaflet.git",
|
"leaflet": "git+https://github.com/JLyne/leaflet.git",
|
||||||
|
"lodash.debounce": "^4.0.8",
|
||||||
"modern-normalize": "^1.1.0",
|
"modern-normalize": "^1.1.0",
|
||||||
"vue": "^3.2.21",
|
"vue": "^3.2.21",
|
||||||
"vuex": "^4.0"
|
"vuex": "^4.0"
|
||||||
@ -27,6 +28,7 @@
|
|||||||
"@types/jest": "^27.4.0",
|
"@types/jest": "^27.4.0",
|
||||||
"@types/jest-in-case": "^1.0.5",
|
"@types/jest-in-case": "^1.0.5",
|
||||||
"@types/leaflet": "1.7.8",
|
"@types/leaflet": "1.7.8",
|
||||||
|
"@types/lodash.debounce": "^4.0.6",
|
||||||
"@types/node": "^17.0.8",
|
"@types/node": "^17.0.8",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.9",
|
"@typescript-eslint/eslint-plugin": "^5.9",
|
||||||
"@typescript-eslint/parser": "^5.9",
|
"@typescript-eslint/parser": "^5.9",
|
||||||
|
@ -143,6 +143,9 @@ export default defineComponent({
|
|||||||
case 'M':
|
case 'M':
|
||||||
element = 'maps';
|
element = 'maps';
|
||||||
break;
|
break;
|
||||||
|
case 'I':
|
||||||
|
element = 'markers';
|
||||||
|
break;
|
||||||
case 'C':
|
case 'C':
|
||||||
element = 'chat';
|
element = 'chat';
|
||||||
break;
|
break;
|
||||||
|
1
src/assets/icons/marker_area.svg
Normal file
1
src/assets/icons/marker_area.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg width="286.89pt" height="286.89pt" viewBox="0 0 286.89 286.89" xmlns="http://www.w3.org/2000/svg"><path d="m80.789 0.381-78.122 110.68s84.697 175.47 87.791 173.92c3.094-1.5426 194.15-35.093 194.15-35.093l-23.978-169.3z"/></svg>
|
After Width: | Height: | Size: 233 B |
1
src/assets/icons/marker_circle.svg
Normal file
1
src/assets/icons/marker_circle.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg width="286.89pt" height="286.89pt" viewBox="0 0 286.89 286.89" xmlns="http://www.w3.org/2000/svg"><circle cx="142.68" cy="143.06" r="137.35" /></svg>
|
After Width: | Height: | Size: 155 B |
1
src/assets/icons/marker_line.svg
Normal file
1
src/assets/icons/marker_line.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg width="286.89pt" height="286.89pt" version="1.0" viewBox="0 0 286.89 286.89" xmlns="http://www.w3.org/2000/svg"><path d="m138.25 1.7383a8.5 8.5 0 0 0-6.125 2.1953l-115.74 105.05a8.5008 8.5008 0 0 0 2.084 13.98l222.54 105.04-84.184 41.338a8.5 8.5 0 0 0-3.8848 11.377 8.5 8.5 0 0 0 11.377 3.8828l100.03-49.119a8.5008 8.5008 0 0 0-0.11718-15.316l-227-107.15 106.32-96.5a8.5 8.5 0 0 0 0.58204-12.006 8.5 8.5 0 0 0-5.8828-2.7773z"/></svg>
|
After Width: | Height: | Size: 439 B |
3
src/assets/icons/marker_point.svg
Normal file
3
src/assets/icons/marker_point.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg width="286.89163pt" height="286.89163pt" version="1.0" viewBox="0 0 286.89162 286.89163" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path transform="translate(-59.399373,-0.17905527)" d="m 202.21483,0.17905527 c -49.2926,0 -89.25212,39.95956873 -89.25212,89.25210973 0,49.292555 82.37686,197.611115 89.25212,197.611115 7.99783,0 89.25211,-148.31856 89.25211,-197.611115 0,-49.292541 -39.95957,-89.25210973 -89.25211,-89.25210973 z m 0,51.39320173 c 20.91016,-0.0011 37.86223,16.948751 37.86389,37.858908 0.001,20.912125 -16.95177,37.865025 -37.86389,37.863935 -20.91214,0.001 -37.86506,-16.9518 -37.86394,-37.863935 0.002,-20.910176 16.95376,-37.86003 37.86394,-37.858908 z" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 686 B |
@ -24,6 +24,13 @@
|
|||||||
@click="handleSectionClick" @keydown="handleSectionKeydown">
|
@click="handleSectionClick" @keydown="handleSectionKeydown">
|
||||||
<SvgIcon :name="mapCount > 1 ? 'maps' : 'servers'"></SvgIcon>
|
<SvgIcon :name="mapCount > 1 ? 'maps' : 'servers'"></SvgIcon>
|
||||||
</button>
|
</button>
|
||||||
|
<button class="button--markers" data-section="markers"
|
||||||
|
:title="messageMarkers"
|
||||||
|
:aria-label="messageMarkers"
|
||||||
|
:aria-expanded="markersVisible"
|
||||||
|
@click="handleSectionClick" @keydown="handleSectionKeydown">
|
||||||
|
<SvgIcon name="marker_point"></SvgIcon>
|
||||||
|
</button>
|
||||||
<button v-if="playerMakersEnabled" class="button--players" data-section="players"
|
<button v-if="playerMakersEnabled" class="button--players" data-section="players"
|
||||||
:title="messagePlayers" :aria-label="messagePlayers" :aria-expanded="playersVisible"
|
:title="messagePlayers" :aria-label="messagePlayers" :aria-expanded="playersVisible"
|
||||||
@click="handleSectionClick" @keydown="handleSectionKeydown">
|
@click="handleSectionClick" @keydown="handleSectionKeydown">
|
||||||
@ -33,6 +40,7 @@
|
|||||||
<div class="sidebar__content" @keydown="handleSidebarKeydown">
|
<div class="sidebar__content" @keydown="handleSidebarKeydown">
|
||||||
<ServersSection v-if="serverCount > 1" :hidden="!mapsVisible"></ServersSection>
|
<ServersSection v-if="serverCount > 1" :hidden="!mapsVisible"></ServersSection>
|
||||||
<WorldsSection v-if="mapCount > 1" :hidden="!mapsVisible"></WorldsSection>
|
<WorldsSection v-if="mapCount > 1" :hidden="!mapsVisible"></WorldsSection>
|
||||||
|
<MarkersSection v-if="previouslyVisible.has('markers')" :hidden="!markersVisible"></MarkersSection>
|
||||||
<PlayersSection v-if="playerMakersEnabled && previouslyVisible.has('players')" :hidden="!playersVisible"></PlayersSection>
|
<PlayersSection v-if="playerMakersEnabled && previouslyVisible.has('players')" :hidden="!playersVisible"></PlayersSection>
|
||||||
<FollowTargetSection v-if="following" :hidden="!followVisible" :target="following"></FollowTargetSection>
|
<FollowTargetSection v-if="following" :hidden="!followVisible" :target="following"></FollowTargetSection>
|
||||||
</div>
|
</div>
|
||||||
@ -45,12 +53,14 @@ import FollowTargetSection from './sidebar/FollowTargetSection.vue';
|
|||||||
import PlayersSection from "@/components/sidebar/PlayersSection.vue";
|
import PlayersSection from "@/components/sidebar/PlayersSection.vue";
|
||||||
import ServersSection from "@/components/sidebar/ServersSection.vue";
|
import ServersSection from "@/components/sidebar/ServersSection.vue";
|
||||||
import WorldsSection from "@/components/sidebar/WorldsSection.vue";
|
import WorldsSection from "@/components/sidebar/WorldsSection.vue";
|
||||||
|
import MarkersSection from "@/components/sidebar/MarkersSection.vue";
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
import SvgIcon from "@/components/SvgIcon.vue";
|
import SvgIcon from "@/components/SvgIcon.vue";
|
||||||
import {MutationTypes} from "@/store/mutation-types";
|
import {MutationTypes} from "@/store/mutation-types";
|
||||||
import "@/assets/icons/players.svg";
|
import "@/assets/icons/players.svg";
|
||||||
import "@/assets/icons/maps.svg";
|
import "@/assets/icons/maps.svg";
|
||||||
import "@/assets/icons/servers.svg";
|
import "@/assets/icons/servers.svg";
|
||||||
|
import "@/assets/icons/marker_point.svg";
|
||||||
import {nextTick, ref, watch} from "vue";
|
import {nextTick, ref, watch} from "vue";
|
||||||
import {handleKeyboardEvent} from "@/util/events";
|
import {handleKeyboardEvent} from "@/util/events";
|
||||||
import {focus} from "@/util";
|
import {focus} from "@/util";
|
||||||
@ -58,6 +68,7 @@ import {LiveAtlasSidebarSection} from "@/index";
|
|||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
|
MarkersSection,
|
||||||
WorldsSection,
|
WorldsSection,
|
||||||
ServersSection,
|
ServersSection,
|
||||||
PlayersSection,
|
PlayersSection,
|
||||||
@ -79,12 +90,14 @@ export default defineComponent({
|
|||||||
|
|
||||||
messageWorlds = computed(() => store.state.messages.worldsHeading),
|
messageWorlds = computed(() => store.state.messages.worldsHeading),
|
||||||
messageServers = computed(() => store.state.messages.serversHeading),
|
messageServers = computed(() => store.state.messages.serversHeading),
|
||||||
|
messageMarkers = computed(() => store.state.messages.markersHeading),
|
||||||
messagePlayers = computed(() => store.getters.playersHeading),
|
messagePlayers = computed(() => store.getters.playersHeading),
|
||||||
|
|
||||||
playerMakersEnabled = computed(() => !!store.state.components.playerMarkers),
|
playerMakersEnabled = computed(() => !!store.state.components.playerMarkers),
|
||||||
|
|
||||||
playersVisible = computed(() => currentlyVisible.value.has('players')),
|
playersVisible = computed(() => currentlyVisible.value.has('players')),
|
||||||
mapsVisible = computed(() => currentlyVisible.value.has('maps')),
|
mapsVisible = computed(() => currentlyVisible.value.has('maps')),
|
||||||
|
markersVisible = computed(() => currentlyVisible.value.has('markers')),
|
||||||
followVisible = computed(() => {
|
followVisible = computed(() => {
|
||||||
//Show following alongside playerlist on small screens
|
//Show following alongside playerlist on small screens
|
||||||
return (!smallScreen.value && following.value)
|
return (!smallScreen.value && following.value)
|
||||||
@ -132,6 +145,7 @@ export default defineComponent({
|
|||||||
//Focus sidebar sections when they become visible, except on initial load
|
//Focus sidebar sections when they become visible, except on initial load
|
||||||
watch(playersVisible, newValue => newValue && !firstLoad.value && nextTick(() => focusSection('players')));
|
watch(playersVisible, newValue => newValue && !firstLoad.value && nextTick(() => focusSection('players')));
|
||||||
watch(mapsVisible, newValue => newValue && !firstLoad.value && nextTick(() => focusSection('maps')));
|
watch(mapsVisible, newValue => newValue && !firstLoad.value && nextTick(() => focusSection('maps')));
|
||||||
|
watch(markersVisible, newValue => newValue && !firstLoad.value && nextTick(() => focusSection('markers')));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sidebar,
|
sidebar,
|
||||||
@ -142,11 +156,13 @@ export default defineComponent({
|
|||||||
|
|
||||||
messageWorlds,
|
messageWorlds,
|
||||||
messageServers,
|
messageServers,
|
||||||
|
messageMarkers,
|
||||||
messagePlayers,
|
messagePlayers,
|
||||||
|
|
||||||
previouslyVisible,
|
previouslyVisible,
|
||||||
playersVisible,
|
playersVisible,
|
||||||
mapsVisible,
|
mapsVisible,
|
||||||
|
markersVisible,
|
||||||
followVisible,
|
followVisible,
|
||||||
playerMakersEnabled,
|
playerMakersEnabled,
|
||||||
|
|
||||||
|
168
src/components/list/MarkerList.vue
Normal file
168
src/components/list/MarkerList.vue
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
<!--
|
||||||
|
- Copyright 2022 James Lyne
|
||||||
|
-
|
||||||
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
- you may not use this file except in compliance with the License.
|
||||||
|
- You may obtain a copy of the License at
|
||||||
|
-
|
||||||
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
-
|
||||||
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
- See the License for the specific language governing permissions and
|
||||||
|
- limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<input ref="searchInput" v-if="search && unfilteredTotal" id="markers__search" class="section__search" type="text"
|
||||||
|
name="search" :value="searchQuery" :placeholder="messageMarkersSearchPlaceholder"
|
||||||
|
@keydown="e => e.stopImmediatePropagation()" @input="onSearchInput">
|
||||||
|
<RadioList v-if="markers.size" v-bind="$attrs" @keydown="onListKeydown">
|
||||||
|
<MarkerListItem v-for="[id, marker] in markers" :key="id" :marker="marker" :id="id"></MarkerListItem>
|
||||||
|
<button type="button" ref="showMoreButton" v-if="viewLimit < total" @click.prevent="showMore">{{ messageShowMore }}</button>
|
||||||
|
</RadioList>
|
||||||
|
<div v-else-if="searchQuery" class="section__skeleton" v-bind="$attrs">{{ messageSkeletonMarkersSearch }}</div>
|
||||||
|
<div v-else class="section__skeleton" v-bind="$attrs">{{ messageSkeletonMarkers }}</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import {defineComponent, onMounted, reactive, ref} from 'vue';
|
||||||
|
import debounce from 'lodash.debounce';
|
||||||
|
import RadioList from "@/components/util/RadioList.vue";
|
||||||
|
import {LiveAtlasMarkerSet, LiveAtlasMarker} from "@/index";
|
||||||
|
import {nonReactiveState} from "@/store/state";
|
||||||
|
import {computed, onUnmounted, watch} from "@vue/runtime-core";
|
||||||
|
import {useStore} from "@/store";
|
||||||
|
import MarkerListItem from "@/components/list/MarkerListItem.vue";
|
||||||
|
import {registerSetUpdateHandler, unregisterSetUpdateHandler} from "@/util/markers";
|
||||||
|
import {DynmapMarkerUpdate} from "@/dynmap";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'MarkerList',
|
||||||
|
components: {
|
||||||
|
MarkerListItem,
|
||||||
|
RadioList,
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
markerSet: {
|
||||||
|
type: Object as () => LiveAtlasMarkerSet,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
search: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setup(props) {
|
||||||
|
let focusFrame = 0;
|
||||||
|
|
||||||
|
const store = useStore(),
|
||||||
|
messageShowMore = computed(() => store.state.messages.showMore),
|
||||||
|
messageMarkersSearchPlaceholder = computed(() => store.state.messages.markersSearchPlaceholder),
|
||||||
|
messageSkeletonMarkersSearch = computed(() => store.state.messages.markersSearchSkeleton),
|
||||||
|
messageSkeletonMarkers = computed(() => store.state.messages.markersSetSkeleton),
|
||||||
|
setContents = nonReactiveState.markers.get(props.markerSet.id)!,
|
||||||
|
searchQuery = ref(""),
|
||||||
|
markers = ref<Map<string, LiveAtlasMarker>>(new Map()),
|
||||||
|
showMoreButton = ref<HTMLButtonElement | null>(null),
|
||||||
|
total = ref(0),
|
||||||
|
unfilteredTotal = ref(0),
|
||||||
|
viewLimit = ref(50),
|
||||||
|
searchInput = ref<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
|
const onListKeydown = (e: KeyboardEvent) => {
|
||||||
|
if(e.key === 'f' && e.ctrlKey) {
|
||||||
|
e.preventDefault();
|
||||||
|
searchInput.value!.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getMarkers = () => {
|
||||||
|
markers.value.clear();
|
||||||
|
|
||||||
|
if(!setContents) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unfilteredTotal.value = setContents.size;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
setContents.forEach((marker, id) => {
|
||||||
|
if(searchQuery.value && !marker.tooltip.toLowerCase().includes(searchQuery.value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if(count < viewLimit.value) {
|
||||||
|
markers.value.set(id, reactive(marker));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
total.value = setContents.size;
|
||||||
|
};
|
||||||
|
|
||||||
|
const showMore = () => {
|
||||||
|
const lastLabel = (showMoreButton.value as HTMLButtonElement).previousElementSibling as HTMLLabelElement;
|
||||||
|
viewLimit.value += 50;
|
||||||
|
//Focus first new list item
|
||||||
|
focusFrame = requestAnimationFrame(() => (lastLabel.nextElementSibling as HTMLInputElement).focus());
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUpdate = (update: DynmapMarkerUpdate) => {
|
||||||
|
unfilteredTotal.value = setContents.size;
|
||||||
|
|
||||||
|
if(update.removed ||
|
||||||
|
(searchQuery.value && !update.payload.tooltip.toLowerCase().includes(searchQuery.value))) {
|
||||||
|
markers.value.delete(update.id);
|
||||||
|
} else if(markers.value.has(update.id) || markers.value.size < viewLimit.value) {
|
||||||
|
markers.value.set(update.id, update.payload);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSearchInput = (e: Event) => {
|
||||||
|
searchQuery.value = (e.target as HTMLInputElement).value.toLowerCase();
|
||||||
|
};
|
||||||
|
|
||||||
|
const debouncedSearch = debounce(() => {
|
||||||
|
viewLimit.value = 50;
|
||||||
|
getMarkers();
|
||||||
|
searchInput.value!.nextElementSibling!.scrollIntoView();
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
getMarkers();
|
||||||
|
watch(viewLimit, () => getMarkers());
|
||||||
|
watch(searchQuery, () => debouncedSearch());
|
||||||
|
|
||||||
|
onMounted(() => registerSetUpdateHandler(handleUpdate, props.markerSet.id));
|
||||||
|
onUnmounted(() => {
|
||||||
|
if(focusFrame) {
|
||||||
|
cancelAnimationFrame(focusFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
debouncedSearch.cancel();
|
||||||
|
unregisterSetUpdateHandler(handleUpdate, props.markerSet.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
messageShowMore,
|
||||||
|
messageMarkersSearchPlaceholder,
|
||||||
|
messageSkeletonMarkersSearch,
|
||||||
|
messageSkeletonMarkers,
|
||||||
|
searchQuery,
|
||||||
|
markers,
|
||||||
|
showMoreButton,
|
||||||
|
viewLimit,
|
||||||
|
total,
|
||||||
|
unfilteredTotal,
|
||||||
|
searchInput,
|
||||||
|
showMore,
|
||||||
|
onListKeydown,
|
||||||
|
onSearchInput
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
120
src/components/list/MarkerListItem.vue
Normal file
120
src/components/list/MarkerListItem.vue
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<!--
|
||||||
|
- Copyright 2022 James Lyne
|
||||||
|
-
|
||||||
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
- you may not use this file except in compliance with the License.
|
||||||
|
- You may obtain a copy of the License at
|
||||||
|
-
|
||||||
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
-
|
||||||
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
- See the License for the specific language governing permissions and
|
||||||
|
- limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<input :id="`marker-${id}`" type="radio" name="marker" v-bind:value="id" @click.prevent="pan">
|
||||||
|
<label :for="`marker-${id}`" class="marker" :title="marker.tooltip" @click.prevent="pan">
|
||||||
|
<img width="16" height="16" v-if="icon" class="marker__icon" :src="icon" alt="" />
|
||||||
|
<SvgIcon v-else :name="defaultIcon" class="marker__icon"></SvgIcon>
|
||||||
|
<span class="marker__label">{{ marker.tooltip || messageUnnamed }}</span>
|
||||||
|
<span class="marker__location">X: {{ marker.location.x }}, Z: {{ marker.location.z }}</span>
|
||||||
|
</label>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import {defineComponent} from 'vue';
|
||||||
|
import {LiveAtlasMarker, LiveAtlasPathMarker, LiveAtlasPointMarker} from "@/index";
|
||||||
|
import {computed} from "@vue/runtime-core";
|
||||||
|
import SvgIcon from "@/components/SvgIcon.vue";
|
||||||
|
import "@/assets/icons/marker_point.svg";
|
||||||
|
import "@/assets/icons/marker_line.svg";
|
||||||
|
import "@/assets/icons/marker_area.svg";
|
||||||
|
import "@/assets/icons/marker_circle.svg";
|
||||||
|
import {useStore} from "vuex";
|
||||||
|
import {LiveAtlasMarkerType} from "@/util/markers";
|
||||||
|
import {MutationTypes} from "@/store/mutation-types";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'MarkerListItem',
|
||||||
|
components: {SvgIcon},
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
marker: {
|
||||||
|
type: Object as () => LiveAtlasMarker,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setup(props) {
|
||||||
|
const store = useStore(),
|
||||||
|
messageUnnamed = computed(() => store.state.messages.markersUnnamed),
|
||||||
|
icon = computed(() => {
|
||||||
|
if('icon' in props.marker) {
|
||||||
|
return store.state.currentMapProvider!.getMarkerIconUrl((props.marker as LiveAtlasPointMarker).icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}),
|
||||||
|
defaultIcon = computed(() => {
|
||||||
|
switch(props.marker.type) {
|
||||||
|
case LiveAtlasMarkerType.POINT:
|
||||||
|
return 'marker_point';
|
||||||
|
case LiveAtlasMarkerType.AREA:
|
||||||
|
return 'marker_area';
|
||||||
|
case LiveAtlasMarkerType.LINE:
|
||||||
|
return 'marker_line';
|
||||||
|
case LiveAtlasMarkerType.CIRCLE:
|
||||||
|
return 'marker_circle';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const pan = () => {
|
||||||
|
if(props.marker.type === LiveAtlasMarkerType.POINT) {
|
||||||
|
store.commit(MutationTypes.SET_VIEW_TARGET, {
|
||||||
|
location: props.marker.location,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
store.commit(MutationTypes.SET_VIEW_TARGET, {
|
||||||
|
location: (props.marker as LiveAtlasPathMarker).bounds,
|
||||||
|
options: {
|
||||||
|
padding: [10, 10]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
icon,
|
||||||
|
defaultIcon,
|
||||||
|
messageUnnamed,
|
||||||
|
pan,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
input[type=radio] + .marker {
|
||||||
|
padding-left: 3.9rem;
|
||||||
|
|
||||||
|
.marker__icon {
|
||||||
|
max-width: 1.6rem;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0.8rem;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.marker__location {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
162
src/components/list/MarkerSetList.vue
Normal file
162
src/components/list/MarkerSetList.vue
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
<!--
|
||||||
|
- Copyright 2022 James Lyne
|
||||||
|
-
|
||||||
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
- you may not use this file except in compliance with the License.
|
||||||
|
- You may obtain a copy of the License at
|
||||||
|
-
|
||||||
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
-
|
||||||
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
- See the License for the specific language governing permissions and
|
||||||
|
- limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<RadioList ref="list" v-if="!currentSet" :aria-labelledby="ariaLabelledby">
|
||||||
|
<template v-for="[id, markerSet] in markerSets" :key="id">
|
||||||
|
<input :id="`marker-set-${id}`" type="radio" name="marker-set" v-model="currentSet" v-bind:value="markerSet">
|
||||||
|
<label :for="`marker-set-${id}`">
|
||||||
|
<span>{{ markerSet.label || id }}</span>
|
||||||
|
<span>{{ markerCounts.get(markerSet) }} Marker(s)</span>
|
||||||
|
</label>
|
||||||
|
</template>
|
||||||
|
</RadioList>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
<div ref="subHeader" class="markers__header">
|
||||||
|
<button ref="backButton" class="markers__back" @click.prevent="currentSet = undefined">
|
||||||
|
<SvgIcon name="arrow"></SvgIcon>
|
||||||
|
</button>
|
||||||
|
<h3 class="markers__set">{{ currentSet.label }}</h3>
|
||||||
|
</div>
|
||||||
|
<MarkerList ref="submenu" :marker-set="currentSet" @keydown="onSubmenuKeydown"></MarkerList>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import {ComponentPublicInstance, defineComponent, nextTick, onMounted, ref} from 'vue';
|
||||||
|
import RadioList from "@/components/util/RadioList.vue";
|
||||||
|
import {LiveAtlasMarkerSet} from "@/index";
|
||||||
|
import {nonReactiveState} from "@/store/state";
|
||||||
|
import {onUnmounted, watch} from "@vue/runtime-core";
|
||||||
|
import MarkerList from "@/components/list/MarkerList.vue";
|
||||||
|
import SvgIcon from "@/components/SvgIcon.vue";
|
||||||
|
import {registerUpdateHandler, unregisterUpdateHandler} from "@/util/markers";
|
||||||
|
import {DynmapMarkerUpdate} from "@/dynmap";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'MarkerSetList',
|
||||||
|
components: {
|
||||||
|
SvgIcon,
|
||||||
|
MarkerList,
|
||||||
|
RadioList,
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
markerSets: {
|
||||||
|
type: Object as () => Map<string, LiveAtlasMarkerSet>,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
ariaLabelledby: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setup(props) {
|
||||||
|
const markerCounts = ref<Map<LiveAtlasMarkerSet, number>>(new Map()),
|
||||||
|
currentSet = ref<LiveAtlasMarkerSet | undefined>(undefined),
|
||||||
|
list = ref<ComponentPublicInstance | null>(null),
|
||||||
|
subHeader = ref<HTMLElement | null>(null),
|
||||||
|
backButton = ref<HTMLButtonElement | null>(null);
|
||||||
|
|
||||||
|
const checkSets = () => {
|
||||||
|
props.markerSets?.forEach((set) => checkSet(set));
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkSet = (set: LiveAtlasMarkerSet) => {
|
||||||
|
const markers = nonReactiveState.markers.get(set.id),
|
||||||
|
markerCount = markers ? markers.size : 0;
|
||||||
|
|
||||||
|
markerCounts.value.set(set, markerCount);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUpdate = (update: DynmapMarkerUpdate) => {
|
||||||
|
checkSet(props.markerSets.get(update.set)!);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSubmenuKeydown = (e: KeyboardEvent) => {
|
||||||
|
if(e.key === 'Backspace') {
|
||||||
|
currentSet.value = undefined;
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateFocus = (newValue?: LiveAtlasMarkerSet, oldValue?: LiveAtlasMarkerSet) => {
|
||||||
|
let focusTarget;
|
||||||
|
|
||||||
|
if(newValue) {
|
||||||
|
focusTarget = subHeader.value!.parentNode!.querySelector('.menu input') || backButton.value;
|
||||||
|
} else if(oldValue) {
|
||||||
|
focusTarget = list.value!.$el.parentNode.querySelector(`[id="marker-set-${oldValue.id}"]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(focusTarget) {
|
||||||
|
(focusTarget as HTMLElement).focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(props.markerSets, () => checkSets);
|
||||||
|
watch(currentSet, (newValue, oldValue) => nextTick(() => updateFocus(newValue, oldValue)));
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
checkSets();
|
||||||
|
registerUpdateHandler(handleUpdate);
|
||||||
|
});
|
||||||
|
onUnmounted(() => {
|
||||||
|
unregisterUpdateHandler(handleUpdate);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
markerCounts,
|
||||||
|
currentSet,
|
||||||
|
list,
|
||||||
|
subHeader,
|
||||||
|
backButton,
|
||||||
|
onSubmenuKeydown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.markers__back {
|
||||||
|
width: 3.2rem;
|
||||||
|
height: 3.2rem;
|
||||||
|
flex-grow: 0;
|
||||||
|
margin-right: 1rem;
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markers__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
position: sticky;
|
||||||
|
top: 4.8rem;
|
||||||
|
background-color: var(--background-base);
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markers__set {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
61
src/components/sidebar/MarkersSection.vue
Normal file
61
src/components/sidebar/MarkersSection.vue
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<!--
|
||||||
|
- Copyright 2022 James Lyne
|
||||||
|
-
|
||||||
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
- you may not use this file except in compliance with the License.
|
||||||
|
- You may obtain a copy of the License at
|
||||||
|
-
|
||||||
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
-
|
||||||
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
- See the License for the specific language governing permissions and
|
||||||
|
- limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<SidebarSection v-if="markerSets.size" name="markers" class="markers">
|
||||||
|
<template v-slot:heading>{{ heading }}</template>
|
||||||
|
<template v-slot:default>
|
||||||
|
<MarkerSetList :markerSets="markerSets" aria-labelledby="markers-heading"></MarkerSetList>
|
||||||
|
</template>
|
||||||
|
</SidebarSection>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import {computed, defineComponent} from 'vue';
|
||||||
|
import {useStore} from "@/store";
|
||||||
|
import SidebarSection from "@/components/sidebar/SidebarSection.vue";
|
||||||
|
import MarkerSetList from "@/components/list/MarkerSetList.vue";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'MarkersSection',
|
||||||
|
components: {
|
||||||
|
MarkerSetList,
|
||||||
|
SidebarSection,
|
||||||
|
},
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
const store = useStore(),
|
||||||
|
heading = computed(() => store.state.messages.markersHeading),
|
||||||
|
markerSets = computed(() => store.state.markerSets);
|
||||||
|
|
||||||
|
return {
|
||||||
|
heading,
|
||||||
|
markerSets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
::v-deep(.menu), ::v-deep(.menu input) {
|
||||||
|
scroll-margin-top: 14.4rem;
|
||||||
|
scroll-margin-bottom: 6.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep(.section__search) {
|
||||||
|
top: 9rem;
|
||||||
|
}
|
||||||
|
</style>
|
2
src/index.d.ts
vendored
2
src/index.d.ts
vendored
@ -126,7 +126,7 @@ interface LiveAtlasUIConfig {
|
|||||||
|
|
||||||
export type LiveAtlasUIElement = 'layers' | 'chat' | LiveAtlasSidebarSection;
|
export type LiveAtlasUIElement = 'layers' | 'chat' | LiveAtlasSidebarSection;
|
||||||
export type LiveAtlasUIModal = 'login' | 'settings';
|
export type LiveAtlasUIModal = 'login' | 'settings';
|
||||||
export type LiveAtlasSidebarSection = 'servers' | 'players' | 'maps';
|
export type LiveAtlasSidebarSection = 'servers' | 'players' | 'maps' | 'markers';
|
||||||
export type LiveAtlasDimension = 'overworld' | 'nether' | 'end';
|
export type LiveAtlasDimension = 'overworld' | 'nether' | 'end';
|
||||||
|
|
||||||
export type LiveAtlasSidebarSectionState = {
|
export type LiveAtlasSidebarSectionState = {
|
||||||
|
@ -61,6 +61,13 @@
|
|||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> span {
|
||||||
|
display: block;
|
||||||
|
text-overflow: inherit;
|
||||||
|
overflow: inherit;
|
||||||
|
white-space: inherit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin button-hovered {
|
@mixin button-hovered {
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
* players on the map
|
* players on the map
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.marker {
|
.map .marker {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
@ -262,13 +262,18 @@ input {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& > li > button, & > input[type=radio] + label {
|
& > li > button, & > input[type=radio] + label, & > button {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& > button {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//noinspection CssOverwrittenProperties
|
//noinspection CssOverwrittenProperties
|
||||||
|
@ -90,6 +90,7 @@ export const actions: ActionTree<State, State> & Actions = {
|
|||||||
//Make UI visible if configured, there's enough space to do so, and this is the first config load
|
//Make UI visible if configured, there's enough space to do so, and this is the first config load
|
||||||
if(!state.ui.visibleElements.size && state.configuration.expandUI && !state.ui.smallScreen) {
|
if(!state.ui.visibleElements.size && state.configuration.expandUI && !state.ui.smallScreen) {
|
||||||
commit(MutationTypes.SET_UI_ELEMENT_VISIBILITY, {element: 'players', state: true});
|
commit(MutationTypes.SET_UI_ELEMENT_VISIBILITY, {element: 'players', state: true});
|
||||||
|
commit(MutationTypes.SET_UI_ELEMENT_VISIBILITY, {element: 'markers', state: true});
|
||||||
commit(MutationTypes.SET_UI_ELEMENT_VISIBILITY, {element: 'maps', state: true});
|
commit(MutationTypes.SET_UI_ELEMENT_VISIBILITY, {element: 'maps', state: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,6 +215,7 @@ export const state: State = {
|
|||||||
servers: {},
|
servers: {},
|
||||||
players: {},
|
players: {},
|
||||||
maps: {},
|
maps: {},
|
||||||
|
markers: {},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user