aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/App.vue12
-rw-r--r--src/components/TimeLine.vue37
-rw-r--r--src/lib.ts44
3 files changed, 59 insertions, 34 deletions
diff --git a/src/App.vue b/src/App.vue
index 9ac8e3f..abbe2b4 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -19,7 +19,7 @@
<div class="grow min-h-0 flex" :class="{'flex-col': !vert}"
:style="{backgroundColor: store.color.bg}" ref="contentAreaRef">
<time-line v-for="(state, idx) in timelines" :key="state.id"
- :vert="vert" :initialState="state" :eventMap="eventMap"
+ :vert="vert" :initialState="state" :eventTree="eventTree"
class="grow basis-full min-h-0 outline outline-1"
@remove="onTimelineRemove(idx)" @state-chg="onTimelineChg($event, idx)" @event-req="onEventReq"/>
<base-line :vert="vert" :timelines="timelines"/>
@@ -38,8 +38,9 @@ import PlusIcon from './components/icon/PlusIcon.vue';
import SettingsIcon from './components/icon/SettingsIcon.vue';
import HelpIcon from './components/icon/HelpIcon.vue';
// Other
-import {HistDate, TimelineState, HistEvent, queryServer, HistEventJson, jsonToHistEvent} from './lib';
+import {HistDate, TimelineState, HistEvent, queryServer, HistEventJson, jsonToHistEvent, cmpHistEvent} from './lib';
import {useStore} from './store';
+import {RBTree} from './rbtree';
// Refs
const contentAreaRef = ref(null as HTMLElement | null);
@@ -99,7 +100,7 @@ function onTimelineRemove(idx: number){
}
// Event data
-const eventMap: Ref<Map<number, HistEvent>> = ref(new Map()); // Maps event IDs to HistEvents
+const eventTree: Ref<RBTree<HistEvent>> = ref(new RBTree(cmpHistEvent)); // Used to look up events by date range
async function onEventReq(startDate: HistDate, endDate: HistDate){
// Get events from server
let urlParams = new URLSearchParams({type: 'events', range: `${startDate}.${endDate}`, limit: '10'});
@@ -109,9 +110,8 @@ async function onEventReq(startDate: HistDate, endDate: HistDate){
}
// Add to map
for (let eventObj of responseObj){
- if (!eventMap.value.has(eventObj.id)){
- eventMap.value.set(eventObj.id, jsonToHistEvent(eventObj));
- }
+ let event = jsonToHistEvent(eventObj);
+ eventTree.value.insert(event);
}
}
diff --git a/src/components/TimeLine.vue b/src/components/TimeLine.vue
index da2c571..3a5de5b 100644
--- a/src/components/TimeLine.vue
+++ b/src/components/TimeLine.vue
@@ -28,10 +28,10 @@
</text>
</svg>
<!-- Events -->
- <div v-for="id in eventIdToPos.keys()" :key="id"
+ <div v-for="id in idToEvent.keys()" :key="id"
class="absolute bg-black text-white border border-white rounded animate-fadein"
:style="eventStyles(id)">
- {{eventMap.get(id)!.title}}
+ {{idToEvent.get(id)!.event.title}}
</div>
<!-- Buttons -->
<icon-button :size="30" class="absolute top-2 right-2"
@@ -53,6 +53,7 @@ import {WRITING_MODE_HORZ, MIN_DATE, MAX_DATE, MONTH_SCALE, DAY_SCALE, SCALES, M
HistDate, stepDate, inDateScale, getScaleRatio, getUnitDiff, getDaysInMonth, moduloPositive, TimelineState,
HistEvent, getImagePath} from '../lib';
import {useStore} from '../store';
+import {RBTree} from '../rbtree';
// Refs
const rootRef: Ref<HTMLElement | null> = ref(null);
@@ -64,7 +65,7 @@ const store = useStore();
const props = defineProps({
vert: {type: Boolean, required: true},
initialState: {type: Object as PropType<TimelineState>, required: true},
- eventMap: {type: Object as PropType<Map<number, HistEvent>>, required: true},
+ eventTree: {type: Object as PropType<RBTree<HistEvent>>, required: true},
});
const emit = defineEmits(['remove', 'state-chg', 'event-req']);
@@ -253,20 +254,23 @@ const ticks = computed((): Ticks => {
});
// For displayed events
-const eventIdToPos = computed(() => { // Maps visible event IDs to x-pos, y-pos, width, and height
- let idToPos: Map<number, [number, number, number, number]> = new Map();
- // Find events to display, and do basic layouting
+const idToEvent = computed(() => { // Maps visible event IDs to HistEvent, x-pos, y-pos, width, and height
+ let idToPos: Map<number, {event: HistEvent, x: number, y: number, w: number, h: number}> = new Map();
let numUnits = getNumVisibleUnits();
let minorAxisStep = 0;
- for (let event of props.eventMap.values()){
- if (event.start.isEarlier(startDate.value) || endDate.value.isEarlier(event.start)){
- continue;
+ // Find events to display, and do basic layouting
+ let iter = props.eventTree.lowerBound(new HistEvent(0, '', startDate.value));
+ while (iter.data() != null){
+ let event = iter.data();
+ iter.next();
+ if (endDate.value.isEarlier(event.start)){
+ break;
}
let unitOffset = getUnitDiff(event.start, startDate.value, scale.value) + startOffset.value;
let posFrac = unitOffset / numUnits;
let posX = props.vert ? minorAxisStep : availLen.value * posFrac;
let posY = props.vert ? availLen.value * posFrac : minorAxisStep;
- idToPos.set(event.id, [posX, posY, 100, 100]);
+ idToPos.set(event.id, {event, x: posX, y: posY, w: 100, h: 100});
minorAxisStep += 10;
}
// If more events could be displayed, notify parent
@@ -725,14 +729,13 @@ function tickLabelStyles(idx: number){
}
}
function eventStyles(eventId: number){
- const [x, y, w, h] = eventIdToPos.value.get(eventId)!;
- let event = props.eventMap.get(eventId)!;
+ const evt = idToEvent.value.get(eventId)!;
return {
- left: x + 'px',
- top: y + 'px',
- width: w + 'px',
- height: h + 'px',
- backgroundImage: `url(${getImagePath(event.imgId)})`,
+ left: evt.x + 'px',
+ top: evt.y + 'px',
+ width: evt.w + 'px',
+ height: evt.h + 'px',
+ backgroundImage: `url(${getImagePath(evt.event.imgId)})`,
backgroundSize: 'cover',
transitionProperty: skipTransition.value ? 'none' : 'all',
transitionDuration,
diff --git a/src/lib.ts b/src/lib.ts
index 0751fa5..b635d28 100644
--- a/src/lib.ts
+++ b/src/lib.ts
@@ -299,17 +299,39 @@ export class TimelineState {
}
}
-export type HistEvent = {
- id: number,
- title: string,
- start: HistDate,
- startUpper: HistDate | null,
- end: HistDate | null,
- endUpper: HistDate | null,
- ctg: string,
- imgId: number,
- pop: number,
-};
+export class HistEvent {
+ id: number;
+ title: string;
+ start: HistDate;
+ startUpper: HistDate | null;
+ end: HistDate | null;
+ endUpper: HistDate | null;
+ ctg: string;
+ imgId: number;
+ pop: number;
+ constructor(
+ id: number, title: string, start: HistDate, startUpper: HistDate | null = null,
+ end: HistDate | null = null, endUpper: HistDate | null = null, ctg='', imgId=0, pop=0){
+ this.id = id;
+ this.title = title;
+ this.start = start;
+ this.startUpper = startUpper;
+ this.end = end;
+ this.endUpper = endUpper;
+ this.ctg = ctg;
+ this.imgId = imgId;
+ this.pop = pop;
+ }
+}
+export function cmpHistEvent(event: HistEvent, event2: HistEvent){
+ if (event.start.isEarlier(event2.start)){
+ return -1;
+ } else if (!event.start.equals(event2.start)){
+ return 1;
+ } else {
+ return event.id - event2.id;
+ }
+}
// For server requests
const SERVER_DATA_URL = (new URL(window.location.href)).origin + '/data/'