Update marker size and restore marker font size
This commit is contained in:
57
index.html
57
index.html
@@ -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) {
|
|||||||
({'&':'&','<':'<','>':'>','"':'"'})[c]);
|
({'&':'&','<':'<','>':'>','"':'"'})[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');
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user