feat(atlas): add searchbar
This commit is contained in:
@@ -605,6 +605,7 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
|
||||
'atlas.markVisitedHint': 'إضافة هذا البلد إلى قائمة المُزارة',
|
||||
'atlas.addToBucket': 'إضافة إلى قائمة الأمنيات',
|
||||
'atlas.addPoi': 'إضافة مكان',
|
||||
'atlas.searchCountry': 'ابحث عن دولة...',
|
||||
'atlas.bucketNamePlaceholder': 'الاسم (بلد، مدينة، مكان…)',
|
||||
'atlas.month': 'الشهر',
|
||||
'atlas.year': 'السنة',
|
||||
|
||||
@@ -601,6 +601,7 @@ const br: Record<string, string | { name: string; category: string }[]> = {
|
||||
'atlas.markVisitedHint': 'Adicionar este país à lista de visitados',
|
||||
'atlas.addToBucket': 'Adicionar à lista de desejos',
|
||||
'atlas.addPoi': 'Adicionar lugar',
|
||||
'atlas.searchCountry': 'Buscar um país...',
|
||||
'atlas.bucketNamePlaceholder': 'Nome (país, cidade, lugar…)',
|
||||
'atlas.month': 'Mês',
|
||||
'atlas.year': 'Ano',
|
||||
|
||||
@@ -605,6 +605,7 @@ const cs: Record<string, string | { name: string; category: string }[]> = {
|
||||
'atlas.markVisitedHint': 'Přidat tuto zemi do seznamu navštívených',
|
||||
'atlas.addToBucket': 'Přidat do seznamu přání (Bucket list)',
|
||||
'atlas.addPoi': 'Přidat místo',
|
||||
'atlas.searchCountry': 'Hledat zemi...',
|
||||
'atlas.bucketNamePlaceholder': 'Název (země, město, místo...)',
|
||||
'atlas.month': 'Měsíc',
|
||||
'atlas.addToBucketHint': 'Uložit jako místo, které chcete navštívit',
|
||||
|
||||
@@ -601,6 +601,7 @@ const de: Record<string, string | { name: string; category: string }[]> = {
|
||||
'atlas.markVisitedHint': 'Dieses Land zur besuchten Liste hinzufügen',
|
||||
'atlas.addToBucket': 'Zur Bucket List',
|
||||
'atlas.addPoi': 'Ort hinzufügen',
|
||||
'atlas.searchCountry': 'Land suchen...',
|
||||
'atlas.bucketNamePlaceholder': 'Name (Land, Stadt, Ort...)',
|
||||
'atlas.month': 'Monat',
|
||||
'atlas.year': 'Jahr',
|
||||
|
||||
@@ -600,6 +600,7 @@ const en: Record<string, string | { name: string; category: string }[]> = {
|
||||
'atlas.markVisitedHint': 'Add this country to your visited list',
|
||||
'atlas.addToBucket': 'Add to bucket list',
|
||||
'atlas.addPoi': 'Add place',
|
||||
'atlas.searchCountry': 'Search a country...',
|
||||
'atlas.bucketNamePlaceholder': 'Name (country, city, place...)',
|
||||
'atlas.month': 'Month',
|
||||
'atlas.year': 'Year',
|
||||
@@ -608,7 +609,6 @@ const en: Record<string, string | { name: string; category: string }[]> = {
|
||||
'atlas.statsTab': 'Stats',
|
||||
'atlas.bucketTab': 'Bucket List',
|
||||
'atlas.addBucket': 'Add to bucket list',
|
||||
'atlas.bucketNamePlaceholder': 'Place or destination...',
|
||||
'atlas.bucketNotesPlaceholder': 'Notes (optional)',
|
||||
'atlas.bucketEmpty': 'Your bucket list is empty',
|
||||
'atlas.bucketEmptyHint': 'Add places you dream of visiting',
|
||||
@@ -621,7 +621,6 @@ const en: Record<string, string | { name: string; category: string }[]> = {
|
||||
'atlas.nextTrip': 'Next trip',
|
||||
'atlas.daysLeft': 'days left',
|
||||
'atlas.streak': 'Streak',
|
||||
'atlas.year': 'year',
|
||||
'atlas.years': 'years',
|
||||
'atlas.yearInRow': 'year in a row',
|
||||
'atlas.yearsInRow': 'years in a row',
|
||||
|
||||
@@ -615,6 +615,7 @@ const es: Record<string, string> = {
|
||||
'atlas.markVisitedHint': 'Añadir este país a tu lista de visitados',
|
||||
'atlas.addToBucket': 'Añadir a lista de deseos',
|
||||
'atlas.addPoi': 'Añadir lugar',
|
||||
'atlas.searchCountry': 'Buscar un país...',
|
||||
'atlas.bucketNamePlaceholder': 'Nombre (país, ciudad, lugar…)',
|
||||
'atlas.month': 'Mes',
|
||||
'atlas.year': 'Año',
|
||||
|
||||
@@ -636,6 +636,7 @@ const fr: Record<string, string> = {
|
||||
'atlas.markVisitedHint': 'Ajouter ce pays à votre liste de visités',
|
||||
'atlas.addToBucket': 'Ajouter à la bucket list',
|
||||
'atlas.addPoi': 'Ajouter un lieu',
|
||||
'atlas.searchCountry': 'Rechercher un pays…',
|
||||
'atlas.bucketNamePlaceholder': 'Nom (pays, ville, lieu…)',
|
||||
'atlas.month': 'Mois',
|
||||
'atlas.year': 'Année',
|
||||
|
||||
@@ -601,6 +601,7 @@ const hu: Record<string, string | { name: string; category: string }[]> = {
|
||||
'atlas.markVisitedHint': 'Ország hozzáadása a meglátogatottak listájához',
|
||||
'atlas.addToBucket': 'Hozzáadás a bakancslistához',
|
||||
'atlas.addPoi': 'Hely hozzáadása',
|
||||
'atlas.searchCountry': 'Ország keresése...',
|
||||
'atlas.bucketNamePlaceholder': 'Név (ország, város, hely...)',
|
||||
'atlas.month': 'Hónap',
|
||||
'atlas.addToBucketHint': 'Mentés meglátogatni kívánt helyként',
|
||||
|
||||
@@ -601,6 +601,7 @@ const it: Record<string, string | { name: string; category: string }[]> = {
|
||||
'atlas.markVisitedHint': 'Aggiungi questo paese alla tua lista dei visitati',
|
||||
'atlas.addToBucket': 'Aggiungi alla lista desideri',
|
||||
'atlas.addPoi': 'Aggiungi luogo',
|
||||
'atlas.searchCountry': 'Cerca un paese...',
|
||||
'atlas.bucketNamePlaceholder': 'Nome (paese, città, luogo...)',
|
||||
'atlas.month': 'Mese',
|
||||
'atlas.addToBucketHint': 'Salvalo come luogo che vuoi visitare',
|
||||
@@ -608,7 +609,6 @@ const it: Record<string, string | { name: string; category: string }[]> = {
|
||||
'atlas.statsTab': 'Statistiche',
|
||||
'atlas.bucketTab': 'Lista desideri',
|
||||
'atlas.addBucket': 'Aggiungi alla lista desideri',
|
||||
'atlas.bucketNamePlaceholder': 'Luogo o destinazione...',
|
||||
'atlas.bucketNotesPlaceholder': 'Note (opzionale)',
|
||||
'atlas.bucketEmpty': 'La tua lista desideri è vuota',
|
||||
'atlas.bucketEmptyHint': 'Aggiungi luoghi che sogni di visitare',
|
||||
|
||||
@@ -636,6 +636,7 @@ const nl: Record<string, string> = {
|
||||
'atlas.markVisitedHint': 'Dit land toevoegen aan je bezochte lijst',
|
||||
'atlas.addToBucket': 'Aan bucket list toevoegen',
|
||||
'atlas.addPoi': 'Plaats toevoegen',
|
||||
'atlas.searchCountry': 'Zoek een land...',
|
||||
'atlas.bucketNamePlaceholder': 'Naam (land, stad, plek…)',
|
||||
'atlas.month': 'Maand',
|
||||
'atlas.year': 'Jaar',
|
||||
|
||||
@@ -636,6 +636,7 @@ const ru: Record<string, string> = {
|
||||
'atlas.markVisitedHint': 'Добавить эту страну в список посещённых',
|
||||
'atlas.addToBucket': 'В список желаний',
|
||||
'atlas.addPoi': 'Добавить место',
|
||||
'atlas.searchCountry': 'Поиск страны...',
|
||||
'atlas.bucketNamePlaceholder': 'Название (страна, город, место…)',
|
||||
'atlas.month': 'Месяц',
|
||||
'atlas.year': 'Год',
|
||||
|
||||
@@ -636,6 +636,7 @@ const zh: Record<string, string> = {
|
||||
'atlas.markVisitedHint': '将此国家添加到已访问列表',
|
||||
'atlas.addToBucket': '添加到心愿单',
|
||||
'atlas.addPoi': '添加地点',
|
||||
'atlas.searchCountry': '搜索国家...',
|
||||
'atlas.bucketNamePlaceholder': '名称(国家、城市、地点…)',
|
||||
'atlas.month': '月份',
|
||||
'atlas.year': '年份',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState, useRef } from 'react'
|
||||
import React, { useEffect, useMemo, useState, useRef } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { getIntlLanguage, getLocaleForLanguage, useTranslation } from '../i18n'
|
||||
import { useSettingsStore } from '../store/settingsStore'
|
||||
@@ -127,6 +127,7 @@ export default function AtlasPage(): React.ReactElement {
|
||||
const glareRef = useRef<HTMLDivElement>(null)
|
||||
const borderGlareRef = useRef<HTMLDivElement>(null)
|
||||
const panelRef = useRef<HTMLDivElement>(null)
|
||||
const country_layer_by_a2_ref = useRef<Record<string, any>>({})
|
||||
|
||||
const handlePanelMouseMove = (e: React.MouseEvent<HTMLDivElement>): void => {
|
||||
if (!panelRef.current || !glareRef.current || !borderGlareRef.current) return
|
||||
@@ -139,7 +140,7 @@ export default function AtlasPage(): React.ReactElement {
|
||||
// Border glow that follows cursor
|
||||
borderGlareRef.current.style.opacity = '1'
|
||||
borderGlareRef.current.style.maskImage = `radial-gradient(circle 150px at ${x}px ${y}px, black 0%, transparent 100%)`
|
||||
borderGlareRef.current.style.WebkitMaskImage = `radial-gradient(circle 150px at ${x}px ${y}px, black 0%, transparent 100%)`
|
||||
borderGlareRef.current.style.webkitMaskImage = `radial-gradient(circle 150px at ${x}px ${y}px, black 0%, transparent 100%)`
|
||||
}
|
||||
const handlePanelMouseLeave = () => {
|
||||
if (glareRef.current) glareRef.current.style.opacity = '0'
|
||||
@@ -170,6 +171,26 @@ export default function AtlasPage(): React.ReactElement {
|
||||
const [bucketTab, setBucketTab] = useState<'stats' | 'bucket'>('stats')
|
||||
const bucketMarkersRef = useRef<any>(null)
|
||||
|
||||
const [atlas_country_search, set_atlas_country_search] = useState('')
|
||||
const [atlas_country_results, set_atlas_country_results] = useState<{ code: string; label: string }[]>([])
|
||||
const [atlas_country_open, set_atlas_country_open] = useState(false)
|
||||
|
||||
const atlas_country_options = useMemo(() => {
|
||||
if (!geoData) return []
|
||||
const opts: { code: string; label: string }[] = []
|
||||
const seen = new Set<string>()
|
||||
for (const f of (geoData as any).features || []) {
|
||||
const a2 = f?.properties?.ISO_A2
|
||||
if (!a2 || a2 === '-99' || typeof a2 !== 'string' || a2.length !== 2) continue
|
||||
if (seen.has(a2)) continue
|
||||
seen.add(a2)
|
||||
const label = String(f?.properties?.NAME || f?.properties?.ADMIN || resolveName(a2) || a2)
|
||||
opts.push({ code: a2, label })
|
||||
}
|
||||
opts.sort((a, b) => a.label.localeCompare(b.label))
|
||||
return opts
|
||||
}, [geoData, resolveName])
|
||||
|
||||
// Load atlas data + bucket list
|
||||
useEffect(() => {
|
||||
Promise.all([
|
||||
@@ -231,8 +252,7 @@ export default function AtlasPage(): React.ReactElement {
|
||||
updateWhenIdle: false,
|
||||
tileSize: 256,
|
||||
zoomOffset: 0,
|
||||
crossOrigin: true,
|
||||
loading: true,
|
||||
crossOrigin: true
|
||||
}).addTo(map)
|
||||
|
||||
// Preload adjacent zoom level tiles
|
||||
@@ -292,6 +312,7 @@ export default function AtlasPage(): React.ReactElement {
|
||||
const a3 = feature.properties?.ADM0_A3 || feature.properties?.ISO_A3 || feature.properties?.['ISO3166-1-Alpha-3'] || feature.id
|
||||
const c = countryMap[a3]
|
||||
if (c) {
|
||||
country_layer_by_a2_ref.current[c.code] = layer
|
||||
const name = resolveName(c.code)
|
||||
const formatDate = (d) => { if (!d) return '—'; const dt = new Date(d); return dt.toLocaleDateString(getLocaleForLanguage(language), { month: 'short', year: 'numeric' }) }
|
||||
const tooltipHtml = `
|
||||
@@ -337,6 +358,7 @@ export default function AtlasPage(): React.ReactElement {
|
||||
const isoA2 = feature.properties?.ISO_A2
|
||||
const countryCode = a3ToA2Entry ? a3ToA2Entry[0] : (isoA2 && isoA2 !== '-99' ? isoA2 : null)
|
||||
if (countryCode && countryCode !== '-99') {
|
||||
country_layer_by_a2_ref.current[countryCode] = layer
|
||||
const name = feature.properties?.NAME || feature.properties?.ADMIN || resolveName(countryCode)
|
||||
layer.bindTooltip(`<div style="font-size:12px;font-weight:600">${name}</div>`, {
|
||||
sticky: false, className: 'atlas-tooltip', direction: 'top', offset: [0, -10], opacity: 1
|
||||
@@ -366,6 +388,23 @@ export default function AtlasPage(): React.ReactElement {
|
||||
setConfirmAction({ type: 'unmark', code, name: resolveName(code) })
|
||||
}
|
||||
|
||||
const select_country_from_search = (country_code: string): void => {
|
||||
const country_label = resolveName(country_code)
|
||||
set_atlas_country_search(country_label)
|
||||
set_atlas_country_open(false)
|
||||
set_atlas_country_results([])
|
||||
|
||||
const layer = country_layer_by_a2_ref.current[country_code]
|
||||
try {
|
||||
if (layer?.getBounds && mapInstance.current) {
|
||||
mapInstance.current.fitBounds(layer.getBounds(), { padding: [24, 24], animate: true, maxZoom: 6 })
|
||||
}
|
||||
} catch (e ) {
|
||||
console.error('Error fitting bounds', e)
|
||||
}
|
||||
setConfirmAction({ type: 'choose', code: country_code, name: country_label })
|
||||
}
|
||||
|
||||
const executeConfirmAction = async (): Promise<void> => {
|
||||
if (!confirmAction) return
|
||||
const { type, code } = confirmAction
|
||||
@@ -494,6 +533,129 @@ export default function AtlasPage(): React.ReactElement {
|
||||
<div style={{ position: 'fixed', top: 'var(--nav-h)', left: 0, right: 0, bottom: 0 }}>
|
||||
{/* Map */}
|
||||
<div ref={mapRef} style={{ position: 'absolute', inset: 0, zIndex: 1, background: dark ? '#1a1a2e' : '#f0f0f0' }} />
|
||||
<div
|
||||
className="absolute z-20 flex justify-center"
|
||||
style={{ top: 14, left: 0, right: 0, pointerEvents: 'none' }}
|
||||
>
|
||||
<div style={{ width: 'min(520px, calc(100vw - 28px))', pointerEvents: 'auto' }}>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 10,
|
||||
padding: '10px 12px',
|
||||
borderRadius: 16,
|
||||
border: '1px solid ' + (dark ? 'rgba(255,255,255,0.10)' : 'rgba(0,0,0,0.08)'),
|
||||
background: dark ? 'rgba(10,10,15,0.55)' : 'rgba(255,255,255,0.55)',
|
||||
backdropFilter: 'blur(18px) saturate(180%)',
|
||||
WebkitBackdropFilter: 'blur(18px) saturate(180%)',
|
||||
boxShadow: dark ? '0 8px 26px rgba(0,0,0,0.25)' : '0 8px 26px rgba(0,0,0,0.10)',
|
||||
}}>
|
||||
<Search size={16} style={{ color: 'var(--text-faint)', flexShrink: 0 }} />
|
||||
<input
|
||||
value={atlas_country_search}
|
||||
onChange={(e) => {
|
||||
const raw = e.target.value
|
||||
set_atlas_country_search(raw)
|
||||
const q = raw.trim().toLowerCase()
|
||||
if (!q) {
|
||||
set_atlas_country_results([])
|
||||
set_atlas_country_open(false)
|
||||
return
|
||||
}
|
||||
const results = atlas_country_options
|
||||
.filter(o => o.label.toLowerCase().includes(q) || o.code.toLowerCase() === q)
|
||||
.slice(0, 8)
|
||||
set_atlas_country_results(results)
|
||||
set_atlas_country_open(true)
|
||||
}}
|
||||
onFocus={() => {
|
||||
if (atlas_country_results.length > 0) set_atlas_country_open(true)
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Escape') {
|
||||
set_atlas_country_open(false)
|
||||
return
|
||||
}
|
||||
if (e.key === 'Enter') {
|
||||
const first = atlas_country_results[0]
|
||||
if (first) select_country_from_search(first.code)
|
||||
}
|
||||
}}
|
||||
placeholder={t('atlas.searchCountry')}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
style={{
|
||||
flex: 1,
|
||||
border: 'none',
|
||||
outline: 'none',
|
||||
background: 'transparent',
|
||||
fontSize: 13,
|
||||
fontFamily: 'inherit',
|
||||
color: 'var(--text-primary)',
|
||||
}}
|
||||
/>
|
||||
{atlas_country_search.trim() && (
|
||||
<button
|
||||
onClick={() => {
|
||||
set_atlas_country_search('')
|
||||
set_atlas_country_results([])
|
||||
set_atlas_country_open(false)
|
||||
}}
|
||||
style={{ border: 'none', background: 'none', cursor: 'pointer', color: 'var(--text-faint)', padding: 2, display: 'flex' }}
|
||||
aria-label="Clear"
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{atlas_country_open && atlas_country_results.length > 0 && (
|
||||
<div
|
||||
style={{
|
||||
marginTop: 8,
|
||||
borderRadius: 14,
|
||||
overflow: 'hidden',
|
||||
border: '1px solid ' + (dark ? 'rgba(255,255,255,0.10)' : 'rgba(0,0,0,0.08)'),
|
||||
background: dark ? 'rgba(10,10,15,0.75)' : 'rgba(255,255,255,0.75)',
|
||||
backdropFilter: 'blur(18px) saturate(180%)',
|
||||
WebkitBackdropFilter: 'blur(18px) saturate(180%)',
|
||||
boxShadow: dark ? '0 12px 30px rgba(0,0,0,0.35)' : '0 12px 30px rgba(0,0,0,0.12)',
|
||||
}}
|
||||
onMouseLeave={() => set_atlas_country_open(false)}
|
||||
>
|
||||
{atlas_country_results.map((r) => (
|
||||
<button
|
||||
key={r.code}
|
||||
onClick={() => select_country_from_search(r.code)}
|
||||
style={{
|
||||
width: '100%',
|
||||
border: 'none',
|
||||
background: 'transparent',
|
||||
cursor: 'pointer',
|
||||
padding: '10px 12px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
fontFamily: 'inherit',
|
||||
textAlign: 'left',
|
||||
borderBottom: '1px solid ' + (dark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.06)'),
|
||||
}}
|
||||
onMouseEnter={(e) => { (e.currentTarget as HTMLButtonElement).style.background = dark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.05)' }}
|
||||
onMouseLeave={(e) => { (e.currentTarget as HTMLButtonElement).style.background = 'transparent' }}
|
||||
>
|
||||
<span style={{ display: 'flex', alignItems: 'center', gap: 10, minWidth: 0 }}>
|
||||
<span style={{ fontSize: 16 }}>{countryCodeToFlag(r.code)}</span>
|
||||
<span style={{ fontSize: 13, fontWeight: 650, color: 'var(--text-primary)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
||||
{r.label}
|
||||
</span>
|
||||
</span>
|
||||
<ChevronRight size={16} style={{ color: 'var(--text-faint)', flexShrink: 0 }} />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile: Bottom bar */}
|
||||
<div className="md:hidden absolute bottom-3 left-0 right-0 z-10 flex justify-center" style={{ touchAction: 'manipulation' }}>
|
||||
@@ -551,7 +713,7 @@ export default function AtlasPage(): React.ReactElement {
|
||||
bucketForm={bucketForm} setBucketForm={setBucketForm}
|
||||
onAddBucket={handleAddBucketItem} onDeleteBucket={handleDeleteBucketItem}
|
||||
onSearchBucket={handleBucketPoiSearch} onSelectBucketPoi={handleSelectBucketPoi}
|
||||
bucketSearchResults={bucketSearchResults} bucketPoiMonth={bucketPoiMonth} setBucketPoiMonth={setBucketPoiMonth}
|
||||
bucketSearchResults={bucketSearchResults} setBucketSearchResults={setBucketSearchResults} bucketPoiMonth={bucketPoiMonth} setBucketPoiMonth={setBucketPoiMonth}
|
||||
bucketPoiYear={bucketPoiYear} setBucketPoiYear={setBucketPoiYear} bucketSearching={bucketSearching}
|
||||
bucketSearch={bucketSearch} setBucketSearch={setBucketSearch}
|
||||
t={t} dark={dark}
|
||||
@@ -629,24 +791,24 @@ export default function AtlasPage(): React.ReactElement {
|
||||
<div style={{ display: 'flex', gap: 8, justifyContent: 'center', marginBottom: 16 }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<CustomSelect
|
||||
value={bucketMonth}
|
||||
value={String(bucketMonth)}
|
||||
onChange={v => setBucketMonth(Number(v))}
|
||||
placeholder={t('atlas.month')}
|
||||
options={[
|
||||
{ value: 0, label: '—' },
|
||||
...Array.from({ length: 12 }, (_, i) => ({ value: i + 1, label: new Date(2000, i).toLocaleString(language, { month: 'long' }) })),
|
||||
{ value: '0', label: '—' },
|
||||
...Array.from({ length: 12 }, (_, i) => ({ value: String(i + 1), label: new Date(2000, i).toLocaleString(language, { month: 'long' }) })),
|
||||
]}
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<CustomSelect
|
||||
value={bucketYear}
|
||||
value={String(bucketYear)}
|
||||
onChange={v => setBucketYear(Number(v))}
|
||||
placeholder={t('atlas.year')}
|
||||
options={[
|
||||
{ value: 0, label: '—' },
|
||||
...Array.from({ length: 20 }, (_, i) => ({ value: new Date().getFullYear() + i, label: String(new Date().getFullYear() + i) })),
|
||||
{ value: '0', label: '—' },
|
||||
...Array.from({ length: 20 }, (_, i) => ({ value: String(new Date().getFullYear() + i), label: String(new Date().getFullYear() + i) })),
|
||||
]}
|
||||
size="sm"
|
||||
/>
|
||||
@@ -717,6 +879,7 @@ interface SidebarContentProps {
|
||||
onSearchBucket: () => Promise<void>
|
||||
onSelectBucketPoi: (result: any) => void
|
||||
bucketSearchResults: any[]
|
||||
setBucketSearchResults: (v: string[]) => void
|
||||
bucketPoiMonth: number
|
||||
setBucketPoiMonth: (v: number) => void
|
||||
bucketPoiYear: number
|
||||
@@ -728,7 +891,7 @@ interface SidebarContentProps {
|
||||
dark: boolean
|
||||
}
|
||||
|
||||
function SidebarContent({ data, stats, countries, selectedCountry, countryDetail, resolveName, onCountryClick, onTripClick, onUnmarkCountry, bucketList, bucketTab, setBucketTab, showBucketAdd, setShowBucketAdd, bucketForm, setBucketForm, onAddBucket, onDeleteBucket, onSearchBucket, onSelectBucketPoi, bucketSearchResults, bucketPoiMonth, setBucketPoiMonth, bucketPoiYear, setBucketPoiYear, bucketSearching, bucketSearch, setBucketSearch, t, dark }: SidebarContentProps): React.ReactElement {
|
||||
function SidebarContent({ data, stats, countries, selectedCountry, countryDetail, resolveName, onTripClick, onUnmarkCountry, bucketList, bucketTab, setBucketTab, showBucketAdd, setShowBucketAdd, bucketForm, setBucketForm, onAddBucket, onDeleteBucket, onSearchBucket, onSelectBucketPoi, bucketSearchResults, setBucketSearchResults, bucketPoiMonth, setBucketPoiMonth, bucketPoiYear, setBucketPoiYear, bucketSearching, bucketSearch, setBucketSearch, t, dark }: SidebarContentProps): React.ReactElement {
|
||||
const { language } = useTranslation()
|
||||
const bg = (o) => dark ? `rgba(255,255,255,${o})` : `rgba(0,0,0,${o})`
|
||||
const tp = dark ? '#f1f5f9' : '#0f172a'
|
||||
@@ -854,12 +1017,12 @@ function SidebarContent({ data, stats, countries, selectedCountry, countryDetail
|
||||
{/* Month / Year with CustomSelect */}
|
||||
<div style={{ display: 'flex', gap: 6 }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<CustomSelect value={bucketPoiMonth} onChange={v => setBucketPoiMonth(Number(v))} placeholder={t('atlas.month')} size="sm"
|
||||
options={[{ value: 0, label: '—' }, ...Array.from({ length: 12 }, (_, i) => ({ value: i + 1, label: new Date(2000, i).toLocaleString(language, { month: 'short' }) }))]} />
|
||||
<CustomSelect value={String(bucketPoiMonth)} onChange={v => setBucketPoiMonth(Number(v))} placeholder={t('atlas.month')} size="sm"
|
||||
options={[{ value: '0', label: '—' }, ...Array.from({ length: 12 }, (_, i) => ({ value: String(i + 1), label: new Date(2000, i).toLocaleString(language, { month: 'short' }) }))]} />
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<CustomSelect value={bucketPoiYear} onChange={v => setBucketPoiYear(Number(v))} placeholder={t('atlas.year')} size="sm"
|
||||
options={[{ value: 0, label: '—' }, ...Array.from({ length: 20 }, (_, i) => ({ value: new Date().getFullYear() + i, label: String(new Date().getFullYear() + i) }))]} />
|
||||
<CustomSelect value={String(bucketPoiYear)} onChange={v => setBucketPoiYear(Number(v))} placeholder={t('atlas.year')} size="sm"
|
||||
options={[{ value: '0', label: '—' }, ...Array.from({ length: 20 }, (_, i) => ({ value: String(new Date().getFullYear() + i), label: String(new Date().getFullYear() + i) }))]} />
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 6, justifyContent: 'flex-end' }}>
|
||||
|
||||
Reference in New Issue
Block a user