Update marker size and restore marker font size

This commit is contained in:
unknown
2026-05-26 19:33:51 +09:00
parent f5a1e49d20
commit 4b205be0c0

View File

@@ -188,7 +188,7 @@ body { font-family: 'Segoe UI', sans-serif; display: flex; height: 100vh; color:
.num-marker { .num-marker {
background: #2b6cb0; color: #fff; border-radius: 50%; background: #2b6cb0; color: #fff; border-radius: 50%;
width: 28px; height: 28px; display: flex; align-items: center; width: 34px; height: 34px; display: flex; align-items: center;
justify-content: center; font-size: 12px; font-weight: 700; justify-content: center; font-size: 12px; font-weight: 700;
border: 3px solid #fff; box-shadow: 0 2px 6px rgba(0,0,0,0.3); border: 3px solid #fff; box-shadow: 0 2px 6px rgba(0,0,0,0.3);
cursor: pointer; cursor: pointer;
@@ -288,8 +288,9 @@ body { font-family: 'Segoe UI', sans-serif; display: flex; height: 100vh; color:
<div class="modal-overlay" id="name-edit-modal"> <div class="modal-overlay" id="name-edit-modal">
<div class="modal-box"> <div class="modal-box">
<h3>Edit Name</h3> <h3>Edit Flag</h3>
<input type="text" id="name-edit-input" /> <input type="text" id="name-edit-input" placeholder="Name" />
<input type="text" id="phrase-edit-input" placeholder="Flag phrase (optional)" />
<div class="modal-actions"> <div class="modal-actions">
<button id="name-edit-cancel">Cancel</button> <button id="name-edit-cancel">Cancel</button>
<button id="name-edit-apply" class="btn-apply">Apply</button> <button id="name-edit-apply" class="btn-apply">Apply</button>
@@ -481,7 +482,7 @@ document.getElementById('api-key-btn').addEventListener('click', () => {
function makeNumIcon(num) { function makeNumIcon(num) {
return L.divIcon({ return L.divIcon({
html: `<div class="num-marker">${num}</div>`, html: `<div class="num-marker">${num}</div>`,
className: '', iconSize: [28, 28], iconAnchor: [14, 14] className: '', iconSize: [34, 34], iconAnchor: [17, 17]
}); });
} }
@@ -494,6 +495,11 @@ function esc(s) {
({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'})[c]); ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'})[c]);
} }
function getFlagTooltipText(flag) {
if (!flag.name) return flag.phrase || '';
return flag.phrase ? `${flag.name} · ${flag.phrase}` : flag.name;
}
// ═══════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════
// ─── Photo ───────────────────────────────────────────── // ─── Photo ─────────────────────────────────────────────
// ═══════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════
@@ -559,7 +565,7 @@ function renderList() {
<span class="status-dot ${flag.placed ? 'placed' : 'unplaced'}"></span> <span class="status-dot ${flag.placed ? 'placed' : 'unplaced'}"></span>
<div class="info"> <div class="info">
<div class="name">${esc(flag.name)}</div> <div class="name">${esc(flag.name)}</div>
<div class="detail">${flag.number ? '#' + esc(flag.number) : ''}${detailText ? ' · ' + detailText : ''}</div> <div class="detail">${flag.number ? '#' + esc(flag.number) : ''}${flag.phrase ? (flag.number ? ' · ' : '') + esc(flag.phrase) : ''}${detailText ? ((flag.number || flag.phrase) ? ' · ' : '') + detailText : ''}</div>
</div> </div>
<span class="chevron ${flag.expanded ? 'open' : ''}">▾</span> <span class="chevron ${flag.expanded ? 'open' : ''}">▾</span>
<div class="actions"> <div class="actions">
@@ -585,6 +591,10 @@ function renderList() {
bHTML += `<div class="detail-row"><span class="label">Address:</span> bHTML += `<div class="detail-row"><span class="label">Address:</span>
<span>${flag.address ? esc(flag.address) : '<span class="address-loading">Loading…</span>'}</span></div>`; <span>${flag.address ? esc(flag.address) : '<span class="address-loading">Loading…</span>'}</span></div>`;
} }
if (flag.phrase) {
bHTML += `<div class="detail-row"><span class="label">Phrase:</span>
<span>${esc(flag.phrase)}</span></div>`;
}
bHTML += `<div class="photo-section"><div class="photo-label">📷 Photo</div>`; bHTML += `<div class="photo-section"><div class="photo-label">📷 Photo</div>`;
if (flag.photoPath) bHTML += `<div class="photo-preview"><img src="${flag.photoPath}" alt="Photo" /></div>`; if (flag.photoPath) bHTML += `<div class="photo-preview"><img src="${flag.photoPath}" alt="Photo" /></div>`;
bHTML += `<div class="photo-actions"> bHTML += `<div class="photo-actions">
@@ -634,8 +644,8 @@ function removeFlag(id) {
renderList(); renderList();
} }
function addFlag(name, number) { function addFlag(name, number, phrase = '') {
const flag = { id: nextId++, name, number, const flag = { id: nextId++, name, number, phrase,
placed: false, lat: null, lng: null, address: null, placed: false, lat: null, lng: null, address: null,
marker: null, expanded: false, photoPath: null }; marker: null, expanded: false, photoPath: null };
flags.push(flag); flags.push(flag);
@@ -652,18 +662,24 @@ function openNameEdit(flagId) {
if (!flag) return; if (!flag) return;
editingFlagId = flagId; editingFlagId = flagId;
document.getElementById('name-edit-input').value = flag.name; document.getElementById('name-edit-input').value = flag.name;
document.getElementById('phrase-edit-input').value = flag.phrase || '';
document.getElementById('name-edit-modal').classList.add('open'); document.getElementById('name-edit-modal').classList.add('open');
} }
document.getElementById('name-edit-apply').addEventListener('click', () => { document.getElementById('name-edit-apply').addEventListener('click', () => {
const flag = flags.find(f => f.id === editingFlagId); const flag = flags.find(f => f.id === editingFlagId);
if (!flag) return; if (!flag) return;
const val = document.getElementById('name-edit-input').value.trim(); const nameVal = document.getElementById('name-edit-input').value.trim();
if (val) { const phraseVal = document.getElementById('phrase-edit-input').value.trim();
flag.name = val; if (nameVal) {
flag.name = nameVal;
flag.phrase = phraseVal;
if (flag.marker) { if (flag.marker) {
flag.marker.unbindTooltip(); flag.marker.unbindTooltip();
flag.marker.bindTooltip(esc(flag.name), { permanent: true, direction: 'right', offset: [10, 0], className: 'flag-marker-tooltip' }); const tooltipText = esc(getFlagTooltipText(flag));
if (tooltipText) {
flag.marker.bindTooltip(tooltipText, { permanent: true, direction: 'right', offset: [10, 0], className: 'flag-marker-tooltip' });
}
} }
renderList(); renderList();
} }
@@ -712,7 +728,8 @@ map.on('click', (e) => {
const { lat, lng } = e.latlng; const { lat, lng } = e.latlng;
const displayNum = flag.number || flags.indexOf(flag) + 1; const displayNum = flag.number || flags.indexOf(flag) + 1;
const marker = L.marker([lat, lng], { icon: makeNumIcon(displayNum), draggable: true }).addTo(map); const marker = L.marker([lat, lng], { icon: makeNumIcon(displayNum), draggable: true }).addTo(map);
if (flag.name) marker.bindTooltip(esc(flag.name), { permanent: true, direction: 'right', offset: [10, 0], className: 'flag-marker-tooltip' }); const tooltipText = esc(getFlagTooltipText(flag));
if (tooltipText) marker.bindTooltip(tooltipText, { permanent: true, direction: 'right', offset: [10, 0], className: 'flag-marker-tooltip' });
marker.on('dragend', () => { marker.on('dragend', () => {
const p = marker.getLatLng(); flag.lat = p.lat; flag.lng = p.lng; const p = marker.getLatLng(); flag.lat = p.lat; flag.lng = p.lng;
scheduleGeocode(flag); renderList(); scheduleGeocode(flag); renderList();
@@ -756,8 +773,9 @@ document.getElementById('file-input').addEventListener('change', async (e) => {
for (const row of rows) { for (const row of rows) {
const name = row.Name || row.name || row.이름 || row['Flag Name'] || Object.values(row)[0] || ''; 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 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; if (!String(name).trim()) continue;
const flag = addFlag(String(name).trim(), String(number).trim()); const flag = addFlag(String(name).trim(), String(number).trim(), String(phrase).trim());
const photo = row.Photo || row.photo || ''; const photo = row.Photo || row.photo || '';
if (photo) flag.photoPath = String(photo); if (photo) flag.photoPath = String(photo);
@@ -767,7 +785,8 @@ document.getElementById('file-input').addEventListener('change', async (e) => {
if (!isNaN(lat) && !isNaN(lng)) { if (!isNaN(lat) && !isNaN(lng)) {
const displayNum = flag.number || flags.indexOf(flag) + 1; const displayNum = flag.number || flags.indexOf(flag) + 1;
const marker = L.marker([lat, lng], { icon: makeNumIcon(displayNum), draggable: true }).addTo(map); const marker = L.marker([lat, lng], { icon: makeNumIcon(displayNum), draggable: true }).addTo(map);
if (flag.name) marker.bindTooltip(esc(flag.name), { permanent: true, direction: 'right', offset: [10, 0], className: 'flag-marker-tooltip' }); const tooltipText = esc(getFlagTooltipText(flag));
if (tooltipText) marker.bindTooltip(tooltipText, { permanent: true, direction: 'right', offset: [10, 0], className: 'flag-marker-tooltip' });
marker.on('dragend', () => { marker.on('dragend', () => {
const p = marker.getLatLng(); flag.lat = p.lat; flag.lng = p.lng; const p = marker.getLatLng(); flag.lat = p.lat; flag.lng = p.lng;
scheduleGeocode(flag); renderList(); scheduleGeocode(flag); renderList();
@@ -794,23 +813,23 @@ document.getElementById('file-input').addEventListener('change', async (e) => {
document.getElementById('template-btn').addEventListener('click', () => { document.getElementById('template-btn').addEventListener('click', () => {
const wb = XLSX.utils.book_new(); const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, XLSX.utils.json_to_sheet([ XLSX.utils.book_append_sheet(wb, XLSX.utils.json_to_sheet([
{ Number: 'A001', Name: 'Flag 1' }, { Number: 'A001', Name: 'Flag 1', Phrase: 'Welcome message' },
{ Number: 'A002', Name: 'Flag 2' }, { Number: 'A002', Name: 'Flag 2', Phrase: 'Sample phrase' },
{ Number: 'A003', Name: 'Flag 3' }, { Number: 'A003', Name: 'Flag 3', Phrase: 'Another note' },
]), 'Flags'); ]), 'Flags');
XLSX.writeFile(wb, 'flag_template.xlsx'); XLSX.writeFile(wb, 'flag_template.xlsx');
}); });
document.getElementById('export-btn').addEventListener('click', () => { document.getElementById('export-btn').addEventListener('click', () => {
const rows = flags.map(f => ({ const rows = flags.map(f => ({
Number: f.number, Name: f.name, Number: f.number, Name: f.name, Phrase: f.phrase,
Status: f.placed ? 'Placed' : 'Unplaced', Status: f.placed ? 'Placed' : 'Unplaced',
Latitude: f.lat ?? '', Longitude: f.lng ?? '', Address: f.address ?? '', Latitude: f.lat ?? '', Longitude: f.lng ?? '', Address: f.address ?? '',
Photo: f.photoPath ?? '', Photo: f.photoPath ?? '',
})); }));
const wb = XLSX.utils.book_new(); const wb = XLSX.utils.book_new();
const ws = XLSX.utils.json_to_sheet(rows); const ws = XLSX.utils.json_to_sheet(rows);
ws['!cols'] = [{ wch: 12 }, { wch: 20 }, { wch: 10 }, { wch: 12 }, { wch: 12 }, { wch: 60 }, { wch: 30 }]; ws['!cols'] = [{ wch: 12 }, { wch: 20 }, { wch: 20 }, { wch: 10 }, { wch: 12 }, { wch: 12 }, { wch: 60 }, { wch: 30 }];
XLSX.utils.book_append_sheet(wb, ws, 'Flags'); XLSX.utils.book_append_sheet(wb, ws, 'Flags');
XLSX.writeFile(wb, 'flag_export.xlsx'); XLSX.writeFile(wb, 'flag_export.xlsx');
}); });