fix: UI world map
This commit is contained in:
BIN
public/logo.png
BIN
public/logo.png
Binary file not shown.
|
Before Width: | Height: | Size: 963 KiB After Width: | Height: | Size: 102 KiB |
BIN
src/assets/airplane-animation.png
Normal file
BIN
src/assets/airplane-animation.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@@ -7,18 +7,23 @@
|
|||||||
|
|
||||||
let fileInput;
|
let fileInput;
|
||||||
let uploading = $state(false);
|
let uploading = $state(false);
|
||||||
|
let uploadError = $state('');
|
||||||
|
|
||||||
function remove(index) {
|
function remove(index) {
|
||||||
onchange(photos.filter((_, i) => i !== index));
|
onchange(photos.filter((_, i) => i !== index));
|
||||||
|
uploadError = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addFiles(e) {
|
async function addFiles(e) {
|
||||||
const files = Array.from(e.currentTarget.files ?? []);
|
const files = Array.from(e.currentTarget.files ?? []);
|
||||||
if (!files.length) return;
|
if (!files.length) return;
|
||||||
uploading = true;
|
uploading = true;
|
||||||
|
uploadError = '';
|
||||||
try {
|
try {
|
||||||
const urls = await Promise.all(files.map(uploadPhoto));
|
const urls = await Promise.all(files.map(uploadPhoto));
|
||||||
onchange([...photos, ...urls]);
|
onchange([...photos, ...urls]);
|
||||||
|
} catch (err) {
|
||||||
|
uploadError = err?.message ?? 'Upload failed. Check Firebase Storage rules.';
|
||||||
} finally {
|
} finally {
|
||||||
uploading = false;
|
uploading = false;
|
||||||
e.currentTarget.value = '';
|
e.currentTarget.value = '';
|
||||||
@@ -68,6 +73,10 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#if uploadError}
|
||||||
|
<div class="upload-error">{uploadError}</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -183,4 +192,15 @@
|
|||||||
transition: border-color 0.15s, color 0.15s;
|
transition: border-color 0.15s, color 0.15s;
|
||||||
}
|
}
|
||||||
.add-cell:hover { border-color: var(--accent-border); color: var(--accent); }
|
.add-cell:hover { border-color: var(--accent-border); color: var(--accent); }
|
||||||
|
|
||||||
|
.upload-error {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #ef4444;
|
||||||
|
background: #fef2f2;
|
||||||
|
border: 1px solid #fecaca;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
line-height: 1.4;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
import worldData from 'world-atlas/countries-50m.json';
|
import worldData from 'world-atlas/countries-50m.json';
|
||||||
import { get } from 'svelte/store';
|
import { get } from 'svelte/store';
|
||||||
import { journals } from '../stores/journalStore.js';
|
import { journals } from '../stores/journalStore.js';
|
||||||
import airplaneImg from '../../assets/airplane.png';
|
import airplaneImg from '../../assets/airplane-animation.png';
|
||||||
|
|
||||||
let { onclose, onprogress, mode = 'map', onmodechange } = $props();
|
let { onclose, onprogress, mode = 'map', onmodechange } = $props();
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
import { getSelected, setTotalCount, getFlashing } from '../layout/selection.svelte.js';
|
import { getSelected, setTotalCount, getFlashing } from '../layout/selection.svelte.js';
|
||||||
import { getUserProfile } from '../auth/userStore.svelte.js';
|
import { getUserProfile } from '../auth/userStore.svelte.js';
|
||||||
import { nameToId } from '../shared/countries.js';
|
import { nameToId } from '../shared/countries.js';
|
||||||
|
import homeIconUrl from '../../assets/home.png';
|
||||||
import crayonCursorUrl from '../../assets/logo-cursor.png';
|
import crayonCursorUrl from '../../assets/logo-cursor.png';
|
||||||
|
|
||||||
let { onCountryClick = (_name) => {} } = $props();
|
let { onCountryClick = (_name) => {} } = $props();
|
||||||
@@ -103,6 +104,28 @@
|
|||||||
|
|
||||||
$effect(updateAllFills);
|
$effect(updateAllFills);
|
||||||
|
|
||||||
|
function placeHomeMarker() {
|
||||||
|
if (!_g || !_pathFn || !_countries) return;
|
||||||
|
_g.selectAll('.home-marker').remove();
|
||||||
|
const name = getUserProfile()?.homeCountry;
|
||||||
|
if (!name) return;
|
||||||
|
const found = _countries.find(f => f.properties.name === name);
|
||||||
|
if (!found) return;
|
||||||
|
const [cx, cy] = _pathFn.centroid(found);
|
||||||
|
if (isNaN(cx) || isNaN(cy)) return;
|
||||||
|
const SIZE = 14;
|
||||||
|
_g.append('image')
|
||||||
|
.attr('class', 'home-marker')
|
||||||
|
.attr('href', homeIconUrl)
|
||||||
|
.attr('x', cx - SIZE / 2)
|
||||||
|
.attr('y', cy - SIZE / 2)
|
||||||
|
.attr('width', SIZE)
|
||||||
|
.attr('height', SIZE)
|
||||||
|
.style('pointer-events', 'none');
|
||||||
|
}
|
||||||
|
|
||||||
|
$effect(placeHomeMarker);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
const flashSet = getFlashing();
|
const flashSet = getFlashing();
|
||||||
const paths = _paths; // reactive read so effect re-runs when _paths is set
|
const paths = _paths; // reactive read so effect re-runs when _paths is set
|
||||||
@@ -166,7 +189,7 @@
|
|||||||
})
|
})
|
||||||
.on('mousemove', (event) => {
|
.on('mousemove', (event) => {
|
||||||
const [x, y] = d3.pointer(event, frameEl);
|
const [x, y] = d3.pointer(event, frameEl);
|
||||||
tooltip.style('left', (x + 10) + 'px').style('top', (y - 28) + 'px');
|
tooltip.style('left', (x + 22) + 'px').style('top', (y - 28) + 'px');
|
||||||
})
|
})
|
||||||
.on('mouseleave', (event, d) => {
|
.on('mouseleave', (event, d) => {
|
||||||
const s = getSelected();
|
const s = getSelected();
|
||||||
@@ -207,6 +230,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderMicrostates();
|
renderMicrostates();
|
||||||
|
placeHomeMarker();
|
||||||
|
|
||||||
const zoom = d3.zoom()
|
const zoom = d3.zoom()
|
||||||
.scaleExtent([1, 32])
|
.scaleExtent([1, 32])
|
||||||
@@ -230,8 +254,9 @@
|
|||||||
fitProjection(projection, width, height);
|
fitProjection(projection, width, height);
|
||||||
const countryPaths = _g.selectAll('path');
|
const countryPaths = _g.selectAll('path');
|
||||||
countryPaths.attr('d', path);
|
countryPaths.attr('d', path);
|
||||||
updateFill(countryPaths);
|
updateAllFills();
|
||||||
renderMicrostates();
|
renderMicrostates();
|
||||||
|
placeHomeMarker();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user