diff --git a/index.html b/index.html
index d3702e7..27d0848 100644
--- a/index.html
+++ b/index.html
@@ -253,6 +253,7 @@ body { font-family: 'Segoe UI', sans-serif; display: flex; height: 100vh; color:
0 / 0 placed
+
@@ -458,6 +459,37 @@ let flags = [];
let nextId = 1;
let selectedFlagId = null;
let filterMode = 'all';
+let showAllTooltips = true;
+let activeTooltipId = null;
+
+function refreshMarkerTooltip(flag) {
+ if (!flag.marker) return;
+ flag.marker.unbindTooltip();
+ const text = getFlagTooltipHtml(flag);
+ if (text) {
+ const isPermanent = showAllTooltips || activeTooltipId === flag.id;
+ flag.marker.bindTooltip(text, {
+ permanent: isPermanent,
+ direction: 'top',
+ offset: [0, -17],
+ className: 'flag-marker-tooltip'
+ });
+ }
+}
+
+function setActiveTooltip(flagId) {
+ if (showAllTooltips) return;
+ const prevId = activeTooltipId;
+ activeTooltipId = flagId;
+ if (prevId) {
+ const prevFlag = flags.find(f => f.id === prevId);
+ if (prevFlag) refreshMarkerTooltip(prevFlag);
+ }
+ if (flagId) {
+ const flag = flags.find(f => f.id === flagId);
+ if (flag) refreshMarkerTooltip(flag);
+ }
+}
// ═══════════════════════════════════════════════════════════
// ─── API Key UI ──────────────────────────────────────────
@@ -495,9 +527,13 @@ function esc(s) {
({'&':'&','<':'<','>':'>','"':'"'})[c]);
}
-function getFlagTooltipText(flag) {
- if (!flag.name) return flag.phrase || '';
- return flag.phrase ? `${flag.name} · ${flag.phrase}` : flag.name;
+function getFlagTooltipHtml(flag) {
+ const n = esc(flag.name || '');
+ const p = esc(flag.phrase || '');
+ if (!n && !p) return '';
+ if (!n) return `
${p}
`;
+ if (!p) return `
${n}
`;
+ return `
${n}
${p}
`;
}
// ═══════════════════════════════════════════════════════════
@@ -578,6 +614,7 @@ function renderList() {
header.addEventListener('click', (e) => {
if (e.target.closest('.actions')) return;
flag.expanded = !flag.expanded;
+ if (flag.expanded && flag.marker) setActiveTooltip(flag.id);
renderList();
});
li.appendChild(header);
@@ -616,7 +653,10 @@ function renderList() {
header.querySelector('.idx')?.addEventListener('click', (e) => {
e.stopPropagation();
- if (flag.marker) map.flyTo(flag.marker.getLatLng(), 17);
+ if (flag.marker) {
+ setActiveTooltip(flag.id);
+ map.flyTo(flag.marker.getLatLng(), 17);
+ }
});
header.querySelector('[data-action="edit-name"]')?.addEventListener('click', (e) => {
e.stopPropagation(); openNameEdit(flag.id);
@@ -638,6 +678,7 @@ function removeFlag(id) {
const flag = flags.find(f => f.id === id);
if (!flag || !flag.placed) return;
if (flag.marker) map.removeLayer(flag.marker);
+ if (activeTooltipId === id) setActiveTooltip(null);
flag.placed = false; flag.lat = null; flag.lng = null;
flag.address = null; flag.marker = null;
if (selectedFlagId === id) selectedFlagId = null;
@@ -674,13 +715,7 @@ document.getElementById('name-edit-apply').addEventListener('click', () => {
if (nameVal) {
flag.name = nameVal;
flag.phrase = phraseVal;
- if (flag.marker) {
- flag.marker.unbindTooltip();
- const tooltipText = esc(getFlagTooltipText(flag));
- if (tooltipText) {
- flag.marker.bindTooltip(tooltipText, { permanent: true, direction: 'right', offset: [10, 0], className: 'flag-marker-tooltip' });
- }
- }
+ if (flag.marker) refreshMarkerTooltip(flag);
renderList();
}
document.getElementById('name-edit-modal').classList.remove('open');
@@ -721,38 +756,70 @@ document.getElementById('photo-overlay').addEventListener('click', () => {
// ─── Map Click ──────────────────────────────────────────
// ═══════════════════════════════════════════════════════════
map.on('click', (e) => {
- if (!selectedFlagId) { alert('Select a flag from the list first.'); return; }
+ let clearedTooltip = false;
+ if (!showAllTooltips && activeTooltipId) {
+ setActiveTooltip(null);
+ clearedTooltip = true;
+ }
+
+ if (!selectedFlagId) {
+ if (!clearedTooltip && flags.length > 0) alert('Select a flag from the list first.');
+ return;
+ }
const flag = flags.find(f => f.id === selectedFlagId);
if (!flag || flag.placed) return;
const { lat, lng } = e.latlng;
const displayNum = flag.number || flags.indexOf(flag) + 1;
const marker = L.marker([lat, lng], { icon: makeNumIcon(displayNum), draggable: true }).addTo(map);
- const tooltipText = esc(getFlagTooltipText(flag));
- if (tooltipText) marker.bindTooltip(tooltipText, { permanent: true, direction: 'right', offset: [10, 0], className: 'flag-marker-tooltip' });
+
marker.on('dragend', () => {
const p = marker.getLatLng(); flag.lat = p.lat; flag.lng = p.lng;
scheduleGeocode(flag); renderList();
});
marker.on('contextmenu', () => { removeFlag(flag.id); });
+ marker.on('click', () => { setActiveTooltip(flag.id); });
+
flag.placed = true; flag.lat = lat; flag.lng = lng; flag.marker = marker;
+ refreshMarkerTooltip(flag);
+
selectedFlagId = null;
renderList();
scheduleGeocode(flag);
});
// ═══════════════════════════════════════════════════════════
-// ─── Filters ────────────────────────────────────────────
+// ─── Filters & Tooltip Toggle ───────────────────────────
// ═══════════════════════════════════════════════════════════
+document.getElementById('toggle-tooltips').addEventListener('click', (e) => {
+ showAllTooltips = !showAllTooltips;
+ const btn = e.target;
+ if (showAllTooltips) {
+ btn.classList.add('active');
+ btn.textContent = '💬 켜짐';
+ activeTooltipId = null;
+ } else {
+ btn.classList.remove('active');
+ btn.textContent = '💬 꺼짐';
+ }
+ flags.forEach(f => {
+ if (f.placed) refreshMarkerTooltip(f);
+ });
+});
+
document.getElementById('filter-all').addEventListener('click', () => {
filterMode = 'all';
- document.querySelectorAll('.filter-btns button').forEach(b => b.classList.remove('active'));
+ document.querySelectorAll('.filter-btns button').forEach(b => {
+ if (b.id !== 'toggle-tooltips') b.classList.remove('active');
+ });
document.getElementById('filter-all').classList.add('active');
renderList();
});
document.getElementById('filter-unplaced').addEventListener('click', () => {
filterMode = 'unplaced';
- document.querySelectorAll('.filter-btns button').forEach(b => b.classList.remove('active'));
+ document.querySelectorAll('.filter-btns button').forEach(b => {
+ if (b.id !== 'toggle-tooltips') b.classList.remove('active');
+ });
document.getElementById('filter-unplaced').classList.add('active');
renderList();
});
@@ -764,36 +831,55 @@ document.getElementById('file-input').addEventListener('change', async (e) => {
const file = e.target.files[0];
if (!file) return;
for (const f of flags) { if (f.marker) map.removeLayer(f.marker); }
- flags = []; nextId = 1; selectedFlagId = null;
+ flags = []; nextId = 1; selectedFlagId = null; activeTooltipId = null;
const data = await file.arrayBuffer();
const wb = XLSX.read(data, { type: 'array' });
const rows = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], { defval: '' });
for (const row of rows) {
- const name = row.Name || row.name || row.이름 || row['Flag Name'] || Object.values(row)[0] || '';
- const number = row.Number || row.number || row.숫자 || row['Flag Number'] || Object.values(row)[1] || '';
- const phrase = row.Phrase || row.phrase || row.문구 || row['Flag Phrase'] || row['Flag Text'] || row.Text || '';
- if (!String(name).trim()) continue;
- const flag = addFlag(String(name).trim(), String(number).trim(), String(phrase).trim());
+ let name = row.Name ?? row.name ?? row.이름 ?? row['Flag Name'];
+ let number = row.Number ?? row.number ?? row.숫자 ?? row['Flag Number'];
+ let phrase = row.Phrase ?? row.phrase ?? row.문구 ?? row['Flag Phrase'] ?? row['Flag Text'] ?? row.Text;
- const photo = row.Photo || row.photo || '';
+ if (name === undefined && number === undefined) {
+ const vals = Object.values(row);
+ name = vals[0];
+ number = vals[1];
+ if (phrase === undefined) phrase = vals[2];
+ }
+
+ name = String(name ?? '').trim();
+ number = String(number ?? '').trim();
+ phrase = String(phrase ?? '').trim();
+
+ if (!name) continue;
+ const flag = addFlag(name, number, phrase);
+
+ const photo = row.Photo ?? row.photo ?? row.사진;
if (photo) flag.photoPath = String(photo);
- let lat = parseFloat(row.Latitude || row.latitude || row.위도);
- let lng = parseFloat(row.Longitude || row.longitude || row.경도);
+ let latRaw = row.Latitude ?? row.latitude ?? row.위도;
+ let lngRaw = row.Longitude ?? row.longitude ?? row.경도;
+ let lat = parseFloat(latRaw);
+ let lng = parseFloat(lngRaw);
+
if (!isNaN(lat) && !isNaN(lng)) {
const displayNum = flag.number || flags.indexOf(flag) + 1;
const marker = L.marker([lat, lng], { icon: makeNumIcon(displayNum), draggable: true }).addTo(map);
- const tooltipText = esc(getFlagTooltipText(flag));
- if (tooltipText) marker.bindTooltip(tooltipText, { permanent: true, direction: 'right', offset: [10, 0], className: 'flag-marker-tooltip' });
+
marker.on('dragend', () => {
const p = marker.getLatLng(); flag.lat = p.lat; flag.lng = p.lng;
scheduleGeocode(flag); renderList();
});
marker.on('contextmenu', () => { removeFlag(flag.id); });
+ marker.on('click', () => { setActiveTooltip(flag.id); });
+
flag.placed = true; flag.lat = lat; flag.lng = lng; flag.marker = marker;
- if (row.Address || row.address || row.주소) flag.address = String(row.Address || row.address || row.주소);
+ refreshMarkerTooltip(flag);
+
+ let addr = row.Address ?? row.address ?? row.주소;
+ if (addr) flag.address = String(addr);
else scheduleGeocode(flag);
}
}
@@ -1090,4 +1176,4 @@ document.getElementById('boundary-toggle').addEventListener('click', () => {
});