merge: 최신 main을 발령 도시 우선 브랜치에 통합

This commit is contained in:
2026-08-21 01:04:02 +00:00
7 changed files with 240 additions and 50 deletions
@@ -28,6 +28,8 @@ const props = defineProps<{
const emit = defineEmits<{
(event: 'hover', cityId: number): void;
(event: 'leave'): void;
(event: 'touch', cityId: number, touchEvent: TouchEvent): void;
(event: 'touchleave'): void;
(event: 'select', cityId: number): void;
}>();
@@ -37,6 +39,25 @@ const stateOffset = computed(() => -6 * props.mapScale);
const selectCity = () => {
if (!props.readonly) emit('select', props.city.id);
};
let touchOnTrack = false;
const touchstart = () => {
touchOnTrack = true;
};
const touchmove = () => {
touchOnTrack = false;
};
const touchend = (event: TouchEvent) => {
if (touchOnTrack) {
event.stopPropagation();
emit('touch', props.city.id, event);
return;
}
emit('touchleave');
};
</script>
<template>
@@ -59,6 +80,9 @@ const selectCity = () => {
:style="{ left: `${props.city.x}px`, top: `${props.city.y}px` }"
@mouseenter="emit('hover', props.city.id)"
@mouseleave="emit('leave')"
@touchstart="touchstart"
@touchmove="touchmove"
@touchend="touchend"
@click.stop="selectCity"
>
<div class="city-dot" :style="{ backgroundColor: props.city.color, width: `${size}px`, height: `${size}px` }">
@@ -54,6 +54,8 @@ const props = defineProps<{
const emit = defineEmits<{
(event: 'hover', cityId: number): void;
(event: 'leave'): void;
(event: 'touch', cityId: number, touchEvent: TouchEvent): void;
(event: 'touchleave'): void;
(event: 'select', cityId: number): void;
}>();
@@ -148,6 +150,25 @@ const selectCity = () => {
if (!props.readonly) emit('select', props.city.id);
};
let touchOnTrack = false;
const touchstart = () => {
touchOnTrack = true;
};
const touchmove = () => {
touchOnTrack = false;
};
const touchend = (event: TouchEvent) => {
if (touchOnTrack) {
event.stopPropagation();
emit('touch', props.city.id, event);
return;
}
emit('touchleave');
};
const cityStateStyle = computed(() => ({
width: `${12 * props.mapScale}px`,
height: `${12 * props.mapScale}px`,
@@ -174,6 +195,9 @@ const cityStateStyle = computed(() => ({
:style="cityBaseStyle"
@mouseenter="emit('hover', props.city.id)"
@mouseleave="emit('leave')"
@touchstart="touchstart"
@touchmove="touchmove"
@touchend="touchend"
@click.stop="selectCity"
>
<div v-if="cityBgStyle" class="city-bg" :style="[cityBgWrapperStyle, cityBgStyle]" />
@@ -94,9 +94,11 @@ const mapStore = useMapViewerStore();
const {
showCityName,
detailMode: storeDetailMode,
singleTapNavigation,
hoveredCityId,
selectedCityId: storeSelectedCityId,
} = storeToRefs(mapStore);
const hasTouchInput = useMediaQuery('(any-pointer: coarse)');
const mapArea = ref<HTMLElement | null>(null);
const mapBody = ref<HTMLElement | null>(null);
@@ -404,6 +406,28 @@ const setHoveredCity = (cityId: number | null) => {
mapStore.setHoveredCity(cityId);
};
const touchPreviewCityId = ref<number | null>(null);
const clearTouchPreview = () => {
touchPreviewCityId.value = null;
setHoveredCity(null);
};
const touchCity = (cityId: number, event: TouchEvent) => {
if (touchPreviewCityId.value !== cityId) {
touchPreviewCityId.value = cityId;
setHoveredCity(cityId);
if (!singleTapNavigation.value) {
event.preventDefault();
}
}
};
const toggleSingleTapNavigation = () => {
clearTouchPreview();
mapStore.toggleSingleTapNavigation();
};
const selectCity = (cityId: number) => {
if (props.readonly) return;
emit('select-city', cityId);
@@ -433,6 +457,7 @@ const selectCity = (cityId: number) => {
class="map-area"
:class="[mapThemeClass, mapSeasonClass]"
:style="{ width: mapWidth, height: mapHeight }"
@click="clearTouchPreview"
>
<div class="map-layer map-bglayer1" :style="mapBackgroundStyle" />
<div class="map-layer map-bglayer2" />
@@ -449,6 +474,8 @@ const selectCity = (cityId: number) => {
v-bind="detailProps"
@hover="setHoveredCity"
@leave="setHoveredCity(null)"
@touch="touchCity"
@touchleave="clearTouchPreview"
@select="selectCity"
/>
<div
@@ -466,9 +493,18 @@ const selectCity = (cityId: number) => {
<div class="tooltip-body">{{ hoveredCity.nationId > 0 ? hoveredCity.nationName : '' }}</div>
</div>
<div class="map-controls">
<button class="map-toggle" :class="{ active: showCityName }" @click="mapStore.toggleCityName">
<button class="map-toggle" :class="{ active: showCityName }" @click.stop="mapStore.toggleCityName">
도시명 표기 {{ showCityName ? '끄기' : '켜기' }}
</button>
<button
v-if="hasTouchInput"
class="map-toggle map-toggle-single-tap"
:class="{ active: singleTapNavigation }"
:aria-pressed="singleTapNavigation"
@click.stop="toggleSingleTapNavigation"
>
두번 도시 이동 {{ singleTapNavigation ? '켜기' : '끄기' }}
</button>
</div>
</div>
</div>
@@ -555,6 +591,8 @@ const selectCity = (cityId: number) => {
right: 4px;
bottom: 4px;
display: flex;
flex-direction: column;
align-items: flex-end;
}
.map-toggle {