1
0
mirror of https://github.com/webrecorder/pywb.git synced 2025-03-15 00:03:28 +01:00

vueui: add highlighting for current snapshot and its parent period to both timeline and calendar full-view/linear-view

This commit is contained in:
Ivan Velev 2021-09-28 00:09:46 -07:00
parent ed3ed1281d
commit 2ac83c61fd
8 changed files with 206 additions and 140 deletions

File diff suppressed because one or more lines are too long

View File

@ -38,7 +38,7 @@
<div>{{ config.url }}</div>
<div v-if="currentSnapshot && !showFullView">
<span v-if="config.title">{{ config.title }}</span>
{{currentSnapshot.getTimeDateFormatted()}}
Current capture: {{currentSnapshot.getTimeDateFormatted()}}
</div>
</div>
<CalendarYear v-if="showFullView"

View File

@ -17,6 +17,9 @@
background-color: #fff7ce;
border-radius: 5px;
}
.calendar-month.contains-current-snapshot {
border: solid 1px red;
}
.calendar-month > h3 {
margin: 0;
font-size: 16px;
@ -59,6 +62,10 @@
background-color: rgba(166, 205, 245, .85);
z-index: 10;
}
.calendar-month .day.contains-current-snapshot .size {
background-color: rgba(255, 100, 100, .85);
}
.calendar-month .day .day-id {
position: absolute;
top: 0;
@ -83,19 +90,21 @@
</style>
<template>
<div class="calendar-month" :class="{current: isCurrent}">
<div class="calendar-month" :class="{current: isCurrent, 'contains-current-snapshot': containsCurrentSnapshot}">
<h3>{{month.getReadableId()}} <span v-if="month.snapshotCount">({{ month.snapshotCount }})</span></h3>
<div v-if="month.snapshotCount">
<span v-for="(day) in ['S', 'M', 'T', 'W', 'H', 'F', 'S']" class="day" :style="dayStyle">{{day}}</span><br/>
<span v-for="(day,i) in days"><br v-if="i && i % 7===0"/><span class="day" :class="{empty: !day || !day.snapshotCount}" :style="dayStyle" @click="gotoDay(day, $event)"><template v-if="day"><span class="size" v-if="day.snapshotCount" :style="getDayCountCircleStyle(day.snapshotCount)"> </span><span class="day-id">{{day.id}}</span><span v-if="day.snapshotCount" class="count">{{day.snapshotCount}} capture<span v-if="day.snapshotCount!==1">s</span></span></template><template v-else v-html="'&nbsp;'"></template></span></span>
<span v-for="(day,i) in days"><br v-if="i && i % 7===0"/><span class="day" :class="{empty: !day || !day.snapshotCount, 'contains-current-snapshot':dayContainsCurrentSnapshot(day)}" :style="dayStyle" @click="gotoDay(day, $event)"><template v-if="day"><span class="size" v-if="day.snapshotCount" :style="getDayCountCircleStyle(day.snapshotCount)"> </span><span class="day-id">{{day.id}}</span><span v-if="day.snapshotCount" class="count">{{day.snapshotCount}} capture<span v-if="day.snapshotCount!==1">s</span></span></template><template v-else v-html="'&nbsp;'"></template></span></span>
</div>
<div v-else class="empty">no captures</div>
</div>
</template>
<script>
import {PywbPeriod} from "../model";
export default {
props: ["month", "year", "isCurrent", "hasCurrentSnapshot"],
props: ["month", "year", "isCurrent", "yearContainsCurrentSnapshot", "currentSnapshot"],
data: function() {
return {
maxInDay: 0,
@ -127,6 +136,10 @@ export default {
days.push(null);
}
return days;
},
containsCurrentSnapshot() {
return this.currentSnapshot &&
this.month.contains(this.currentSnapshot);
}
},
methods: {
@ -150,6 +163,9 @@ export default {
// background-color: hsl(${scaledColorHue}, 100%, 50%, .2)
return `width: ${scaledSize}px; height: ${scaledSize}px; top: ${margin}px; left: ${margin}px; border-radius: ${scaledSize/2}px;`;
},
dayContainsCurrentSnapshot(day) {
return !!day && day.snapshotCount > 0 && this.containsCurrentSnapshot && day.contains(this.currentSnapshot);
}
}
};

View File

@ -30,8 +30,8 @@
:key="month.id"
:month="month"
:year="year"
:current-snapshot="containsCurrentSnapshot ? currentSnapshot : null"
:is-current="month === currentMonth"
:has-current-snapshot="month === currentMonth"
@goto-period="$emit('goto-period', $event)"
@show-day-timeline="setCurrentTimeline"
></CalendarMonth>
@ -39,6 +39,7 @@
<Tooltip :position="currentTimelinePos" v-if="currentTimelinePeriod" ref="timelineLinearTooltip">
<TimelineLinear
:period="currentTimelinePeriod"
:current-snapshot="containsCurrentSnapshot ? currentSnapshot : null"
@goto-period="gotoPeriod"
></TimelineLinear>
</Tooltip>
@ -67,9 +68,14 @@ export default {
computed: {
year() { // the year that the timeline period is in
let year = null;
// if timeline is showing all year
if (this.period.type === PywbPeriod.Type.all) {
// if timeline is showing all year => pick the LAST YEAR
year = this.period.children[this.period.children.length-1];
// if no current snapshot => pick the LAST YEAR
if (!this.currentSnapshot) {
year = this.period.children[this.period.children.length-1];
} else {
year = this.period.findByFullId(String(this.currentSnapshot.year));
}
} else if (this.period.type === PywbPeriod.Type.year) {
year = this.period;
} else {
@ -88,6 +94,10 @@ export default {
month = this.period.getParents().filter(p => p.type === PywbPeriod.Type.month)[0];
}
return month;
},
containsCurrentSnapshot() {
return this.currentSnapshot &&
this.year.contains(this.currentSnapshot);
}
},
methods: {

View File

@ -21,14 +21,14 @@
<div v-for="subPeriod in period.children"
:key="subPeriod.id"
class="period"
:class="{empty: !subPeriod.snapshotCount, highlight: highlightPeriod === subPeriod, 'last-level': !canZoom}"
:class="{empty: !subPeriod.snapshotCount, highlight: highlightPeriod === subPeriod, 'last-level': !canZoom, 'contains-current-snapshot': containsCurrentSnapshot(subPeriod) }"
>
<div class="histo">
<div class="line"
v-for="histoPeriod in subPeriod.children"
:key="histoPeriod.id"
:style="{height: getHistoLineHeight(histoPeriod.snapshotCount)}"
:class="{'has-single-snapshot': histoPeriod.snapshotCount === 1}"
:class="{'has-single-snapshot': histoPeriod.snapshotCount === 1, 'contains-current-snapshot': containsCurrentSnapshot(histoPeriod)}"
@click="changePeriod(histoPeriod, $event)"
@mouseover="setTooltipPeriod(histoPeriod, $event)"
@mouseout="setTooltipPeriod(null, $event)"
@ -93,12 +93,18 @@ export default{
},
isTooltipPeriodDayOrHour() {
return this.tooltipPeriod.type >= PywbPeriod.Type.day;
},
iContainCurrentSnapshot() {
return this.currentSnapshot && this.period.contains(this.currentSnapshot);
}
},
updated() {
// do something on update
},
methods: {
containsCurrentSnapshot(period) {
return this.iContainCurrentSnapshot && period.contains(this.currentSnapshot);
},
addEmptySubPeriods() {
this.period.fillEmptyChildPeriods(true);
},
@ -315,6 +321,9 @@ export default{
.timeline .period:hover {
background-color: #eeeeee;
}
.timeline .period.contains-current-snapshot, .timeline .period.contains-current-snapshot:hover {
background-color: #f7def4;
}
/* empty period */
.timeline .period.empty {
@ -399,6 +408,10 @@ export default{
background-color: #f5a6eb;
}
.timeline .period .histo .line.contains-current-snapshot {
background-color: red;
}
/* Period that contains ONE snapshot only will show snapshot info*/
.timeline .period-tooltip {
position: fixed;

View File

@ -7,7 +7,8 @@
<div class="list">
<div v-for="period in snapshotPeriods">
<span class="link" @click="gotoPeriod(period)">{{period.snapshot.getTimeFormatted()}}</span>
<span class="link" @click="gotoPeriod(period)" >{{period.snapshot.getTimeFormatted()}}</span>
<span v-if="currentSnapshot.id === period.snapshot.id" class="current">current</span>
</div>
</div>
</div>
@ -16,10 +17,14 @@
<script>
export default {
name: "TimelineLinear",
props: ['period'],
props: ['period', 'currentSnapshot'],
computed: {
snapshotPeriods() {
return this.period.getSnapshotPeriodsFlat();
},
containsCurrentSnapshot() {
return this.currentSnapshot &&
this.period.contains(this.currentSnapshot);
}
},
methods: {
@ -56,4 +61,9 @@ export default {
color: lightseagreen;
cursor: pointer;
}
.timeline-linear .current {
background-color: deeppink;
color: white;
border-radius: 5px;
}
</style>

View File

@ -64,6 +64,17 @@ class CDXLoader {
app.$mount("#app");
// TODO (Ilya): make this work with in-page snapshot/capture/replay updates!
// app.$on("show-snapshot", snapshot => {
// const replayUrl = app.config.url;
// const url = location.href.replace('/'+replayUrl, '').replace(/\d+$/, '') + snapshot.id + '/' + replayUrl;
// window.history.pushState({url: replayUrl, timestamp: snapshot.id}, document.title, url);
// if (!window.onpopstate) {
// window.onpopstate = (ev) => {
// updateSnapshot(ev.state.url, ev.state.timestamp);
// };
// }
// });
if (loadCallback) {
app.$on("show-snapshot", loadCallback);
}

View File

@ -1,5 +1,5 @@
const PywbMonthLabels = {1:"Jan", 2:"Feb",3:"Mar",4:"Apr",5:"May",6:"Jun",7:"Jul",8:"Aug",9:"Sep",10:"Oct",11:"Nov",12:"Dec"};
const PywbPeriodIdDelimiter = '-';
export function PywbData(rawSnaps) {
const allTimePeriod = new PywbPeriod({type: PywbPeriod.Type.all, id: "all"});
const snapshots = [];
@ -98,6 +98,14 @@ export class PywbSnapshot {
getTimeFormatted() {
return (this.hour < 13 ? this.hour : (this.hour % 12)) + ":" + ((this.minute < 10 ? "0":"")+this.minute) + ":" + ((this.second < 10 ? "0":"")+this.second) + " " + (this.hour < 12 ? "am":"pm");
}
getParentIds() {
return [this.year, this.month, this.day, Math.ceil((this.hour + .0001) / (24/8))];
}
getFullId() {
return [this.year, this.month, this.day, Math.ceil((this.hour + .0001) / (24/8)), this.id].join(PywbPeriodIdDelimiter);
}
}
/* ---------------- PERIOD object ----------------- */
@ -318,8 +326,17 @@ PywbPeriod.prototype.getParents = function(skipAllTime=false) {
return parents;
};
PywbPeriod.prototype.contains = function(period) {
return !!period.getParents().find(p => p === this);
PywbPeriod.prototype.contains = function(periodOrSnapshot) {
if (this.type === 0) {
return true; // all-time contains everything
}
if (periodOrSnapshot instanceof PywbPeriod) {
return periodOrSnapshot.getParents(true).slice(0,this.type).join(PywbPeriodIdDelimiter) === this.fullId;
}
if (periodOrSnapshot instanceof PywbSnapshot) {
return periodOrSnapshot.getParentIds(true).slice(0,this.type).join(PywbPeriodIdDelimiter) === this.fullId;
}
return false;
};
PywbPeriod.prototype.snapshot = null;
@ -376,13 +393,13 @@ PywbPeriod.prototype.getSnapshotPeriodsFlat = function(flatArray=false) {
};
/**
* Return the "full" id, which includes all parents ID and self ID, delimited by a hyphen "-"
* Return the "full" id, which includes all parents ID and self ID, delimited by a ${PywbPeriodIdDelimiter}
* @returns {string}
*/
PywbPeriod.prototype.initFullId = function() {
const ids = this.getParents(true).map(p => p.id);
ids.push(this.id);
this.fullId = ids.join("-");
this.fullId = ids.join(PywbPeriodIdDelimiter);
};
/**
@ -395,7 +412,7 @@ PywbPeriod.prototype.findByFullId = function(fullId) {
if (this.type !== PywbPeriod.Type.all) {
parent = this.getParents()[0];
}
const ids = fullId.split('-');
const ids = fullId.split(PywbPeriodIdDelimiter);
let found = false;
for(let i=0; i<ids.length; i++) {