From 559902e0211a06b349c4c2f50b0882a8d314f8b7 Mon Sep 17 00:00:00 2001 From: Terry Truong Date: Fri, 6 Jan 2023 02:32:59 +1100 Subject: Jump to and highlight search results Use a 'searchTarget' prop of Timeline to trigger jumping to a search result. Make TimeLine prioritise search result in layout. When querying for events in App, check for a search target, and use incl= to retrieve it. On server, for the incl= query param, don't include the event if outside specified range. --- src/App.vue | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) (limited to 'src/App.vue') diff --git a/src/App.vue b/src/App.vue index 2f5051a..cbd6825 100644 --- a/src/App.vue +++ b/src/App.vue @@ -23,7 +23,7 @@ :style="{backgroundColor: store.color.bg}" ref="contentAreaRef"> @@ -54,9 +54,9 @@ import SettingsIcon from './components/icon/SettingsIcon.vue'; import PlusIcon from './components/icon/PlusIcon.vue'; import SearchIcon from './components/icon/SearchIcon.vue'; // Other -import {HistDate, HistEvent, queryServer, EventResponseJson, jsonToHistEvent, - SCALES, stepDate, TimelineState, cmpHistEvent, dateToUnit, DateRangeTree, - EventInfo, EventInfoJson, jsonToEventInfo} from './lib'; +import {HistDate, HistEvent, queryServer, + EventResponseJson, jsonToHistEvent, EventInfo, EventInfoJson, jsonToEventInfo, + SCALES, stepDate, TimelineState, cmpHistEvent, dateToUnit, DateRangeTree} from './lib'; import {useStore} from './store'; import {RBTree, rbtree_shallow_copy} from './rbtree'; @@ -75,7 +75,7 @@ function updateAreaDims(){ contentWidth.value = contentAreaEl.offsetWidth; contentHeight.value = contentAreaEl.offsetHeight; } -onMounted(updateAreaDims) +onMounted(updateAreaDims); // Timeline data const timelines: Ref = ref([]); @@ -90,9 +90,10 @@ function addTimeline(){ last.endDate, last.startOffset, last.endOffset, last.scaleIdx )); } + timelineTargets.value.push([null, false]); nextTimelineId++; } -addTimeline(); +onMounted(addTimeline); function onTimelineChg(state: TimelineState, idx: number){ timelines.value[idx] = state; } @@ -113,6 +114,7 @@ function onTimelineClose(idx: number){ return; } timelines.value.splice(idx, 1); + timelineTargets.value.splice(idx, 1); } // For storing and looking up events @@ -175,8 +177,11 @@ async function onEventDisplay( timelineId: number, eventIds: number[], firstDate: HistDate, lastDate: HistDate, scaleIdx: number){ async function handleEvent( timelineId: number, eventIds: number[], firstDate: HistDate, lastDate: HistDate, scaleIdx: number){ + let timelineIdx = timelines.value.findIndex((s : TimelineState) => s.id == timelineId); + let targetEvent = timelineTargets.value[timelineIdx][0]; // Skip if range has been queried, and enough of its events have been obtained - if (queriedRanges[scaleIdx].contains([firstDate, lastDate])){ + if (queriedRanges[scaleIdx].contains([firstDate, lastDate]) + && (targetEvent == null || idToEvent.has(targetEvent.id))){ // Get number of events in range, server-side let fullCount = 0; let date = firstDate.clone(); @@ -214,17 +219,21 @@ async function onEventDisplay( } } // Get events from server - if (lastQueriedRange != null && lastQueriedRange[0].equals(firstDate) && lastQueriedRange[1].equals(lastDate)){ + if (lastQueriedRange != null && lastQueriedRange[0].equals(firstDate) && lastQueriedRange[1].equals(lastDate) + && (targetEvent == null || idToEvent.has(targetEvent.id))){ console.log(`INFO: Skipping redundant server request from ${firstDate} to ${lastDate}`); return; } - lastQueriedRange = [firstDate, lastDate] + lastQueriedRange = [firstDate, lastDate]; let urlParams = new URLSearchParams({ type: 'events', range: `${firstDate}.${lastDate}`, scale: String(SCALES[scaleIdx]), limit: String(EVENT_REQ_LIMIT), }); + if (targetEvent != null){ + urlParams.append('incl', String(targetEvent.id)); + } let responseObj: EventResponseJson | null = await queryServer(urlParams); if (responseObj == null){ console.log('WARNING: Server gave null response to event query'); @@ -242,6 +251,12 @@ async function onEventDisplay( titleToEvent.set(event.title, event); } } + if (targetEvent != null){ + if (!idToEvent.has(targetEvent.id)){ + console.log(`WARNING: Server response did not include event matching 'incl=${targetEvent.id}'`); + } + timelineTargets.value[timelineIdx][0] = null; + } // Collect unit counts const unitCounts = responseObj.unitCounts; if (unitCounts == null){ @@ -289,9 +304,14 @@ async function onInfoClick(eventTitle: string){ // For search modal const searchOpen = ref(false); +const timelineTargets = ref([] as [HistEvent | null, boolean][]); // For communicating search results to timelines + // A boolean flag is used to trigger jumping even when the same event occurs twice function onSearch(event: HistEvent){ searchOpen.value = false; - console.log(`Need to jump to event "${event.title}" at ${event.start}`); + // Trigger jump in endmost timeline + let timelineIdx = timelineTargets.value.length - 1; + let oldFlag = timelineTargets.value[timelineIdx]; + timelineTargets.value.splice(timelineIdx, 1, [event, !oldFlag[1]]); } // For resize handling -- cgit v1.2.3