mirror of
https://github.com/ccmdi/obsidian-map-plus.git
synced 2026-07-22 06:43:14 +00:00
feat: ext base view for timeline
This commit is contained in:
parent
abe91b400d
commit
e02267f54e
3 changed files with 79 additions and 36 deletions
|
|
@ -26,16 +26,16 @@ export class MapBasesView extends BasesView {
|
|||
mapEl: HTMLElement;
|
||||
plugin: MapPlugin;
|
||||
|
||||
private deck: Deck<MapViewType[]> | null = null;
|
||||
protected deck: Deck<MapViewType[]> | null = null;
|
||||
private coordinatesProp: BasesPropertyId | null = null;
|
||||
private coverProp: BasesPropertyId | null = null;
|
||||
private mapHeight: number = DEFAULT_MAP_HEIGHT;
|
||||
private defaultZoom: number = DEFAULT_MAP_ZOOM;
|
||||
private center: [number, number] = [0, 0];
|
||||
private markerType: 'pins' | 'dots' = 'pins';
|
||||
protected defaultZoom: number = DEFAULT_MAP_ZOOM;
|
||||
protected center: [number, number] = [0, 0];
|
||||
protected markerType: 'pins' | 'dots' = 'pins';
|
||||
private tileLayer: string = 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png';
|
||||
private savedViewState: { latitude: number; longitude: number; zoom: number } | null = null;
|
||||
private lastPoints: MapPoint[] = [];
|
||||
protected lastPoints: MapPoint[] = [];
|
||||
|
||||
constructor(controller: QueryController, scrollEl: HTMLElement, plugin: MapPlugin) {
|
||||
super(controller);
|
||||
|
|
@ -102,7 +102,7 @@ export class MapBasesView extends BasesView {
|
|||
}
|
||||
}
|
||||
|
||||
private loadConfig(): void {
|
||||
protected loadConfig(): void {
|
||||
this.coordinatesProp = this.config.getAsPropertyId('coordinates');
|
||||
this.coverProp = this.config.getAsPropertyId('coverImage');
|
||||
|
||||
|
|
@ -147,7 +147,7 @@ export class MapBasesView extends BasesView {
|
|||
}
|
||||
}
|
||||
|
||||
private renderMap(): void {
|
||||
protected renderMap(): void {
|
||||
if (!this.data) {
|
||||
this.containerEl.removeClass('is-loading');
|
||||
return;
|
||||
|
|
@ -358,7 +358,7 @@ export class MapBasesView extends BasesView {
|
|||
return points;
|
||||
}
|
||||
|
||||
private extractCoordinates(entry: BasesEntry): [number, number] | null {
|
||||
protected extractCoordinates(entry: BasesEntry): [number, number] | null {
|
||||
if (this.coordinatesProp) {
|
||||
try {
|
||||
const value = entry.getValue(this.coordinatesProp);
|
||||
|
|
|
|||
|
|
@ -4,11 +4,10 @@ import {
|
|||
QueryController,
|
||||
ViewOption,
|
||||
} from 'obsidian';
|
||||
import { Deck } from '@deck.gl/core';
|
||||
import { MapView as MapViewType } from '@deck.gl/core';
|
||||
import { MapPoint } from '../map-renderer';
|
||||
import MapPlugin from '../main';
|
||||
import { MapBasesView } from './map-bases-view';
|
||||
import { updateMapPoints } from '../map-renderer';
|
||||
|
||||
export const MapTimelineBasesViewType = 'map-timeline';
|
||||
|
||||
|
|
@ -40,7 +39,6 @@ export class MapTimelineBasesView extends MapBasesView {
|
|||
|
||||
onload(): void {
|
||||
super.onload();
|
||||
this.createSlider();
|
||||
}
|
||||
|
||||
onunload() {
|
||||
|
|
@ -49,7 +47,22 @@ export class MapTimelineBasesView extends MapBasesView {
|
|||
}
|
||||
|
||||
private createSlider(): void {
|
||||
const sliderContainer = this.containerEl.createDiv({ cls: 'bases-timeline-slider' });
|
||||
const sliderContainer = this.mapEl.createDiv({ cls: 'bases-timeline-slider' });
|
||||
|
||||
const dateInputEl = sliderContainer.createEl('input', {
|
||||
type: 'date',
|
||||
cls: 'timeline-date-input',
|
||||
});
|
||||
|
||||
dateInputEl.addEventListener('change', () => {
|
||||
const selectedDate = new Date(dateInputEl.value);
|
||||
if (!isNaN(selectedDate.getTime())) {
|
||||
this.dateRangeEnd = selectedDate.getTime();
|
||||
this.updateSliderFromDate();
|
||||
this.updateDateDisplay();
|
||||
this.applyTimelineFilter();
|
||||
}
|
||||
});
|
||||
|
||||
this.dateDisplayEl = sliderContainer.createDiv({ cls: 'timeline-date-display' });
|
||||
this.dateDisplayEl.textContent = this.getDateText();
|
||||
|
|
@ -65,8 +78,34 @@ export class MapTimelineBasesView extends MapBasesView {
|
|||
this.sliderEl.addEventListener('input', () => {
|
||||
this.updateDateRange();
|
||||
this.updateDateDisplay();
|
||||
this.updateDateInput(dateInputEl);
|
||||
this.applyTimelineFilter();
|
||||
});
|
||||
|
||||
this.updateDateInput(dateInputEl);
|
||||
}
|
||||
|
||||
private updateSliderFromDate(): void {
|
||||
if (!this.sliderEl || this.allTimelineEntries.length === 0) return;
|
||||
|
||||
const minDate = Math.min(...this.allTimelineEntries.map(e => e.date));
|
||||
const maxDate = Math.max(...this.allTimelineEntries.map(e => e.date));
|
||||
const range = maxDate - minDate;
|
||||
|
||||
if (range === 0) {
|
||||
this.sliderEl.value = '100';
|
||||
} else {
|
||||
const percentage = ((this.dateRangeEnd - minDate) / range) * 100;
|
||||
this.sliderEl.value = Math.min(100, Math.max(0, percentage)).toString();
|
||||
}
|
||||
}
|
||||
|
||||
private updateDateInput(dateInputEl: HTMLInputElement): void {
|
||||
const date = new Date(this.dateRangeEnd);
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
dateInputEl.value = `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
private getDateText(): string {
|
||||
|
|
@ -96,22 +135,24 @@ export class MapTimelineBasesView extends MapBasesView {
|
|||
|
||||
if (this.dateProperty) {
|
||||
this.updateTimelineData();
|
||||
// Don't call super - we'll handle rendering ourselves
|
||||
|
||||
if (this.deck) {
|
||||
this.applyTimelineFilter();
|
||||
} else {
|
||||
// First render - need to call parent's loadConfig and renderMap
|
||||
super['loadConfig']();
|
||||
super['renderMap']();
|
||||
super.loadConfig();
|
||||
super.renderMap();
|
||||
|
||||
this.createSlider();
|
||||
this.applyTimelineFilter();
|
||||
}
|
||||
} else {
|
||||
// No date property set, just render normally
|
||||
super.onDataUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
private loadConfig(): void {
|
||||
protected loadConfig(): void {
|
||||
super.loadConfig();
|
||||
|
||||
this.dateProperty = this.config.getAsPropertyId('dateProperty');
|
||||
this.uniquenessProperty = this.config.getAsPropertyId('uniquenessProperty');
|
||||
|
||||
|
|
@ -178,7 +219,6 @@ export class MapTimelineBasesView extends MapBasesView {
|
|||
const date = new Date(stringValue);
|
||||
|
||||
if (isNaN(date.getTime())) {
|
||||
// Try parsing as timestamp
|
||||
const timestamp = parseInt(stringValue);
|
||||
if (!isNaN(timestamp)) {
|
||||
return timestamp;
|
||||
|
|
@ -227,12 +267,10 @@ export class MapTimelineBasesView extends MapBasesView {
|
|||
return;
|
||||
}
|
||||
|
||||
// Filter by date range
|
||||
let filteredEntries = this.allTimelineEntries.filter(
|
||||
entry => entry.date >= this.dateRangeStart && entry.date <= this.dateRangeEnd
|
||||
);
|
||||
|
||||
// Apply uniqueness constraint
|
||||
if (this.uniquenessProperty && this.uniquenessMode !== 'all') {
|
||||
const grouped = new Map<string, TimelineEntry[]>();
|
||||
|
||||
|
|
@ -248,10 +286,8 @@ export class MapTimelineBasesView extends MapBasesView {
|
|||
for (const group of grouped.values()) {
|
||||
if (group.length === 0) continue;
|
||||
|
||||
// Sort by date
|
||||
group.sort((a, b) => a.date - b.date);
|
||||
|
||||
// Pick based on mode
|
||||
if (this.uniquenessMode === 'most-recent') {
|
||||
filteredEntries.push(group[group.length - 1]);
|
||||
} else if (this.uniquenessMode === 'least-recent') {
|
||||
|
|
@ -260,16 +296,11 @@ export class MapTimelineBasesView extends MapBasesView {
|
|||
}
|
||||
}
|
||||
|
||||
// Update the view with filtered points
|
||||
this.updateMapWithFilteredPoints(filteredEntries.map(e => e.point));
|
||||
}
|
||||
|
||||
private updateMapWithFilteredPoints(points: MapPoint[]): void {
|
||||
if (!this.deck || !this.data) return;
|
||||
|
||||
// Directly update using updateMapPoints
|
||||
const { updateMapPoints } = require('../map-renderer');
|
||||
|
||||
const hasConfiguredCenter = this.center[0] !== 0 || this.center[1] !== 0;
|
||||
|
||||
updateMapPoints(this.deck, points, {
|
||||
|
|
|
|||
28
styles.css
28
styles.css
|
|
@ -362,21 +362,33 @@
|
|||
|
||||
.bases-timeline-slider {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
min-width: 280px;
|
||||
padding: 12px 16px;
|
||||
background: var(--background-secondary);
|
||||
border-bottom: 1px solid var(--background-modifier-border);
|
||||
background: var(--background-primary);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.timeline-date-input {
|
||||
width: 100%;
|
||||
padding: 6px 8px;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: 4px;
|
||||
background: var(--background-secondary);
|
||||
color: var(--text-normal);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.timeline-date-display {
|
||||
font-size: 13px;
|
||||
color: var(--text-normal);
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue