added default images for card ui & fixed ui
This commit is contained in:
BIN
src/assets/default-1.jpeg
Normal file
BIN
src/assets/default-1.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 788 KiB |
BIN
src/assets/default-2.jpeg
Normal file
BIN
src/assets/default-2.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 663 KiB |
BIN
src/assets/default-3.jpeg
Normal file
BIN
src/assets/default-3.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 566 KiB |
@@ -226,7 +226,7 @@
|
|||||||
{#each ['solo','friends','family'] as t}
|
{#each ['solo','friends','family'] as t}
|
||||||
<label class="toggle-opt" class:active={tripType === t}>
|
<label class="toggle-opt" class:active={tripType === t}>
|
||||||
<input type="radio" name="nc-tripType" value={t} bind:group={tripType} />
|
<input type="radio" name="nc-tripType" value={t} bind:group={tripType} />
|
||||||
{t === 'solo' ? '🧑 Solo' : t === 'friends' ? '👥 With friends' : '👨👩👧👦 With family'}
|
{t === 'solo' ? 'Solo' : t === 'friends' ? 'With friends' : 'With family'}
|
||||||
</label>
|
</label>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
@@ -264,7 +264,7 @@
|
|||||||
|
|
||||||
{#each questions as q, i}
|
{#each questions as q, i}
|
||||||
<div class="q-card">
|
<div class="q-card">
|
||||||
<p class="q-text">{q}{country.trim() ? ` in ${country}` : ''}</p>
|
<p class="q-text">{q}</p>
|
||||||
<textarea class="q-input" rows="3" placeholder="Your answer…" bind:value={answers[i]}></textarea>
|
<textarea class="q-input" rows="3" placeholder="Your answer…" bind:value={answers[i]}></textarea>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
@@ -422,22 +422,23 @@
|
|||||||
}
|
}
|
||||||
.input:focus { border-color: var(--accent-border); }
|
.input:focus { border-color: var(--accent-border); }
|
||||||
|
|
||||||
.toggle-row { display: flex; gap: 10px; flex-wrap: wrap; }
|
.toggle-row { display: flex; gap: 8px; flex-wrap: wrap; }
|
||||||
.toggle-opt {
|
.toggle-opt {
|
||||||
display: flex; align-items: center; justify-content: center; gap: 8px;
|
display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
|
||||||
font-size: 16px; font-weight: 400; color: var(--text);
|
font-size: 14px; font-weight: 400; color: var(--text);
|
||||||
padding: 12px 14px; border-radius: 10px;
|
padding: 16px 10px; border-radius: 10px;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
cursor: pointer; transition: border-color 0.15s, background 0.15s, color 0.15s, box-shadow 0.15s;
|
cursor: pointer; transition: border-color 0.15s, background 0.15s, color 0.15s, box-shadow 0.15s;
|
||||||
background: var(--bg-subtle);
|
background: var(--bg-subtle);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
.toggle-opt input { display: none; }
|
.toggle-opt input { display: none; }
|
||||||
.toggle-opt.active { border-color: var(--accent); background: var(--accent-bg); color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
|
.toggle-opt.active { border-color: var(--accent); background: var(--accent-bg); color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
|
||||||
.toggle-opt.active img { filter: brightness(0) saturate(100%) invert(27%) sepia(98%) saturate(1169%) hue-rotate(239deg) brightness(80%) contrast(92%); }
|
.toggle-opt.active img { filter: brightness(0) saturate(100%) invert(27%) sepia(98%) saturate(1169%) hue-rotate(239deg) brightness(80%) contrast(92%); }
|
||||||
|
|
||||||
.transport-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
|
.transport-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
|
||||||
.transport-img { width: 30px; height: 30px; object-fit: contain; flex-shrink: 0; }
|
.transport-img { width: 44px; height: 44px; object-fit: contain; flex-shrink: 0; }
|
||||||
|
|
||||||
.tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
|
.tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
|
||||||
.tag {
|
.tag {
|
||||||
@@ -464,8 +465,8 @@
|
|||||||
box-shadow: 0 4px 20px rgba(0,0,0,0.06);
|
box-shadow: 0 4px 20px rgba(0,0,0,0.06);
|
||||||
}
|
}
|
||||||
.q-text {
|
.q-text {
|
||||||
font-size: 20px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 400;
|
||||||
color: var(--text-h);
|
color: var(--text-h);
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|||||||
@@ -42,10 +42,6 @@
|
|||||||
|
|
||||||
<div class="pc-header">
|
<div class="pc-header">
|
||||||
<span class="pc-brand">MAP JOURNAL</span>
|
<span class="pc-brand">MAP JOURNAL</span>
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="pc-share-icon">
|
|
||||||
<circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/>
|
|
||||||
<path d="m8.59 13.51 6.83 3.98M15.41 6.51l-6.82 3.98"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pc-hero">
|
<div class="pc-hero">
|
||||||
@@ -83,7 +79,10 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="pc-cta">Share your journey →</div>
|
<svg class="pc-share-icon-corner" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/>
|
||||||
|
<path d="m8.59 13.51 6.83 3.98M15.41 6.51l-6.82 3.98"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@@ -143,7 +142,13 @@
|
|||||||
letter-spacing: 0.2em;
|
letter-spacing: 0.2em;
|
||||||
color: #a5b4fc;
|
color: #a5b4fc;
|
||||||
}
|
}
|
||||||
.pc-share-icon { color: #a5b4fc; flex-shrink: 0; }
|
.pc-share-icon-corner {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 14px;
|
||||||
|
right: 14px;
|
||||||
|
color: #a5b4fc;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.pc-hero {
|
.pc-hero {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -231,5 +236,6 @@
|
|||||||
letter-spacing: 0.04em;
|
letter-spacing: 0.04em;
|
||||||
padding-top: 4px;
|
padding-top: 4px;
|
||||||
border-top: 1px solid rgba(255,255,255,0.08);
|
border-top: 1px solid rgba(255,255,255,0.08);
|
||||||
|
text-align: right;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,16 +1,28 @@
|
|||||||
<script>
|
<script>
|
||||||
import { flagEmoji } from '../../shared/countries.js';
|
import { flagEmoji } from '../../shared/countries.js';
|
||||||
|
import default1 from '../../../assets/default-1.jpeg';
|
||||||
|
import default2 from '../../../assets/default-2.jpeg';
|
||||||
|
import default3 from '../../../assets/default-3.jpeg';
|
||||||
|
|
||||||
/** @type {{ entry: import('../shared/types.js').JournalEntry, onClick: () => void }} */
|
/** @type {{ entry: import('../shared/types.js').JournalEntry, onClick: () => void }} */
|
||||||
let { entry, onClick } = $props();
|
let { entry, onClick } = $props();
|
||||||
|
|
||||||
|
const defaults = [default1, default2, default3];
|
||||||
|
|
||||||
function formatDate(/** @type {string} */ iso) {
|
function formatDate(/** @type {string} */ iso) {
|
||||||
return new Date(iso).toLocaleDateString('en-US', {
|
return new Date(iso).toLocaleDateString('en-US', {
|
||||||
month: 'short', day: 'numeric', year: 'numeric',
|
month: 'short', day: 'numeric', year: 'numeric',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let mainPhoto = $derived(entry.photos[0] ?? null);
|
// Pick a stable random default based on the entry id
|
||||||
|
function defaultPhoto(id) {
|
||||||
|
let hash = 0;
|
||||||
|
for (let i = 0; i < id.length; i++) hash = (hash * 31 + id.charCodeAt(i)) >>> 0;
|
||||||
|
return defaults[hash % defaults.length];
|
||||||
|
}
|
||||||
|
|
||||||
|
let mainPhoto = $derived(entry.photos[0] ?? defaultPhoto(entry.id));
|
||||||
let thumbPhotos = $derived(entry.photos.slice(1, 4));
|
let thumbPhotos = $derived(entry.photos.slice(1, 4));
|
||||||
let extraCount = $derived(entry.photos.length > 4 ? entry.photos.length - 4 : 0);
|
let extraCount = $derived(entry.photos.length > 4 ? entry.photos.length - 4 : 0);
|
||||||
|
|
||||||
@@ -29,10 +41,11 @@
|
|||||||
<div class="v-dot" aria-hidden="true"></div>
|
<div class="v-dot" aria-hidden="true"></div>
|
||||||
|
|
||||||
<div class="v-content">
|
<div class="v-content">
|
||||||
<!-- Country above card -->
|
<!-- Country + cities above card -->
|
||||||
<div class="above-card">
|
<div class="above-card">
|
||||||
<span class="flag">{flagEmoji(entry.location.country)}</span>
|
<span class="flag">{flagEmoji(entry.location.country)}</span>
|
||||||
<span class="country-name">{entry.location.country}</span>
|
<span class="country-name">{entry.location.country}</span>
|
||||||
|
<span class="city-inline">· {entry.location.cities.join(', ')}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Card -->
|
<!-- Card -->
|
||||||
@@ -40,36 +53,10 @@
|
|||||||
onclick={onClick}
|
onclick={onClick}
|
||||||
onkeydown={(e) => e.key === 'Enter' && onClick()}>
|
onkeydown={(e) => e.key === 'Enter' && onClick()}>
|
||||||
|
|
||||||
<!-- Trip badge — top-right of card, outside photo -->
|
|
||||||
<span class="trip-badge trip-badge--{entry.tripType}">
|
|
||||||
{entry.tripType === 'solo' ? 'Solo' : entry.tripType === 'family' ? 'Family' : 'Friends'}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<!-- Photos -->
|
<!-- Photos -->
|
||||||
<div class="photo-grid" class:has-thumbs={thumbPhotos.length > 0}>
|
<div class="photo-grid" class:has-thumbs={thumbPhotos.length > 0}>
|
||||||
<div class="photo-main">
|
<div class="photo-main">
|
||||||
{#if mainPhoto}
|
<img src={mainPhoto} alt="" loading="lazy" />
|
||||||
<img src={mainPhoto} alt="" loading="lazy"
|
|
||||||
onerror={(e) => {
|
|
||||||
e.currentTarget.style.display = 'none';
|
|
||||||
e.currentTarget.nextElementSibling.style.display = 'flex';
|
|
||||||
}} />
|
|
||||||
<div class="photo-fallback" style="display:none">
|
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2">
|
|
||||||
<rect x="3" y="3" width="18" height="18" rx="3"/>
|
|
||||||
<circle cx="8.5" cy="8.5" r="1.5"/>
|
|
||||||
<path d="M21 15l-5-5L5 21"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<div class="photo-fallback">
|
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2">
|
|
||||||
<rect x="3" y="3" width="18" height="18" rx="3"/>
|
|
||||||
<circle cx="8.5" cy="8.5" r="1.5"/>
|
|
||||||
<path d="M21 15l-5-5L5 21"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if thumbPhotos.length > 0}
|
{#if thumbPhotos.length > 0}
|
||||||
@@ -99,18 +86,17 @@
|
|||||||
|
|
||||||
<!-- Info bar -->
|
<!-- Info bar -->
|
||||||
<div class="card-info">
|
<div class="card-info">
|
||||||
<span class="city">{entry.location.cities.join(', ')}</span>
|
<span class="days-label">{entry.days} {entry.days === 1 ? 'day' : 'days'}</span>
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
{#if entry.transport}
|
{#if entry.transport}
|
||||||
<span class="transport-chip transport-chip--{entry.transport}">
|
<span class="transport-chip transport-chip--{entry.transport}">
|
||||||
{@html transportIcons[entry.transport] ?? ''}
|
{@html transportIcons[entry.transport] ?? ''}
|
||||||
{transportLabel}
|
{transportLabel}
|
||||||
</span>
|
</span>
|
||||||
<span class="dot-sep">·</span>
|
|
||||||
{/if}
|
{/if}
|
||||||
<span>{formatDate(entry.date)}</span>
|
<span class="trip-badge trip-badge--{entry.tripType}">
|
||||||
<span class="dot-sep">·</span>
|
{entry.tripType === 'solo' ? 'Solo' : entry.tripType === 'family' ? 'Family' : 'Friends'}
|
||||||
<span>{entry.days} {entry.days === 1 ? 'day' : 'days'}</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -172,6 +158,14 @@
|
|||||||
color: var(--text-h);
|
color: var(--text-h);
|
||||||
letter-spacing: -0.2px;
|
letter-spacing: -0.2px;
|
||||||
}
|
}
|
||||||
|
.city-inline {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 300;
|
||||||
|
color: var(--text-sub);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Card ── */
|
/* ── Card ── */
|
||||||
.entry-card {
|
.entry-card {
|
||||||
@@ -191,22 +185,18 @@
|
|||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Trip badge — absolute top-right of card ── */
|
/* ── Trip badge — inline in info bar ── */
|
||||||
.trip-badge {
|
.trip-badge {
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
right: 10px;
|
|
||||||
z-index: 2;
|
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: 300;
|
font-weight: 400;
|
||||||
padding: 3px 10px;
|
padding: 2px 8px;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
letter-spacing: 0.04em;
|
letter-spacing: 0.03em;
|
||||||
backdrop-filter: blur(6px);
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.trip-badge--solo { background: rgba(245,158,11,0.85); color: #fff; }
|
.trip-badge--solo { background: rgba(245,158,11,0.12); color: #b45309; border: 1px solid rgba(245,158,11,0.25); }
|
||||||
.trip-badge--friends { background: rgba(124,58,237,0.85); color: #fff; }
|
.trip-badge--friends { background: rgba(124,58,237,0.07); color: #7c3aed; border: 1px solid rgba(124,58,237,0.2); }
|
||||||
.trip-badge--family { background: rgba(16,185,129,0.85); color: #fff; }
|
.trip-badge--family { background: rgba(16,185,129,0.08); color: #059669; border: 1px solid rgba(16,185,129,0.2); }
|
||||||
|
|
||||||
/* ── Photo grid — fixed height, always consistent ── */
|
/* ── Photo grid — fixed height, always consistent ── */
|
||||||
.photo-grid {
|
.photo-grid {
|
||||||
@@ -294,16 +284,12 @@
|
|||||||
padding: 10px 14px;
|
padding: 10px 14px;
|
||||||
background: var(--bg);
|
background: var(--bg);
|
||||||
border-top: 1px solid var(--border);
|
border-top: 1px solid var(--border);
|
||||||
gap: 8px;
|
|
||||||
min-height: 44px;
|
min-height: 44px;
|
||||||
}
|
}
|
||||||
.city {
|
.days-label {
|
||||||
font-size: 13px;
|
font-size: 12px;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
color: var(--text);
|
color: var(--text-sub);
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
}
|
||||||
.meta {
|
.meta {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
Reference in New Issue
Block a user