Select with head (fix #27)

pull/62/head
Varun Patil 2022-09-15 10:49:51 -07:00
parent f522f7c87b
commit e058316f66
2 changed files with 119 additions and 33 deletions

View File

@ -14,10 +14,20 @@
@update="scrollChange"
@resize="handleResizeWithDelay"
>
<h1 v-if="item.type === 0" class="head-row"
:class="{ 'first': item.id === 1 }">
<div v-if="item.type === 0" class="head-row"
:class="{
'first': item.id === 1,
'selected': item.selected,
}"
>
<div class="icon-checkmark select"
@click="selectHead(item)">
</div>
<span class="name"
@click="selectHead(item)">
{{ item.name || getHeadName(item) }}
</h1>
</span>
</div>
<div v-else
class="photo-row"
@ -98,7 +108,7 @@
<script lang="ts">
import { Component, Watch, Mixins } from 'vue-property-decorator';
import { IDay, IPhoto, IRow, IRowType, ITick } from "../types";
import { IDay, IHeadRow, IPhoto, IRow, IRowType, ITick } from "../types";
import { NcActions, NcActionButton, NcButton } from '@nextcloud/vue';
import { generateUrl } from '@nextcloud/router'
import GlobalMixin from '../mixins/GlobalMixin';
@ -144,7 +154,7 @@ export default class Timeline extends Mixins(GlobalMixin) {
/** Computed number of columns */
private numCols = 5;
/** Header rows for dayId key */
private heads: { [dayid: number]: IRow } = {};
private heads: { [dayid: number]: IHeadRow } = {};
/** Original days response */
private days: IDay[] = [];
@ -440,8 +450,8 @@ export default class Timeline extends Mixins(GlobalMixin) {
/** Process the data for days call including folders */
async processDays(data: IDay[]) {
const list: IRow[] = [];
const heads: {[dayId: number]: IRow} = {};
const list: typeof this.list = [];
const heads: typeof this.heads = {};
// Store the preloads in a separate map.
// This is required since otherwise the inner detail objects
@ -472,10 +482,11 @@ export default class Timeline extends Mixins(GlobalMixin) {
}
// Add header to list
const head = {
const head: IHeadRow = {
id: ++this.numRows,
size: 40,
type: IRowType.HEAD,
selected: false,
dayId: day.dayid,
day: day,
};
@ -840,8 +851,8 @@ export default class Timeline extends Mixins(GlobalMixin) {
}
/** Add a photo to selection list */
selectPhoto(photo: IPhoto) {
const nval = !this.selection.has(photo);
selectPhoto(photo: IPhoto, val?: boolean, noUpdate?: boolean) {
const nval = val ?? !this.selection.has(photo);
if (nval) {
photo.flag |= this.c.FLAG_SELECTED;
this.selection.add(photo);
@ -849,23 +860,60 @@ export default class Timeline extends Mixins(GlobalMixin) {
photo.flag &= ~this.c.FLAG_SELECTED;
this.selection.delete(photo);
}
if (!noUpdate) {
this.updateHeadSelected(this.heads[photo.d.dayid]);
this.$forceUpdate();
}
}
/** Clear all selected photos */
clearSelection() {
const heads = new Set<IHeadRow>();
for (const photo of this.selection) {
photo.flag &= ~this.c.FLAG_SELECTED;
heads.add(this.heads[photo.d.dayid]);
}
this.selection.clear();
heads.forEach(this.updateHeadSelected);
this.$forceUpdate();
}
/** Select or deselect all photos in a head */
selectHead(head: IHeadRow) {
head.selected = !head.selected;
for (const row of head.day.rows) {
for (const photo of row.photos) {
this.selectPhoto(photo, head.selected, true);
}
}
this.$forceUpdate();
}
/** Check if the day for a photo is selected entirely */
updateHeadSelected(head: IHeadRow) {
let selected = true;
// Check if all photos are selected
for (const row of head.day.rows) {
for (const photo of row.photos) {
if (!(photo.flag & this.c.FLAG_SELECTED)) {
selected = false;
break;
}
}
}
// Update head
head.selected = selected;
}
/**
* Download the currently selected files
*/
async downloadSelection() {
if (this.selection.size >= 5) {
if (this.selection.size >= 100) {
if (!confirm(this.t("memories", "You are about to download a large number of files. Are you sure?"))) {
return;
}
@ -989,8 +1037,9 @@ export default class Timeline extends Mixins(GlobalMixin) {
.recycler {
height: 300px;
width: calc(100% + 20px);
}
.photo-row .photo {
.photo-row > .photo {
display: inline-block;
position: relative;
cursor: pointer;
@ -1010,6 +1059,37 @@ export default class Timeline extends Mixins(GlobalMixin) {
padding-top: 12px;
}
}
> .select {
position: absolute;
left: 5px; top: 50%;
display: none;
opacity: 0;
transform: translateY(-30%);
transition: opacity 0.2s ease;
background-color: var(--color-background-darker);
border-radius: 50%;
height: 12px; width: 12px;
background-size: 70%;
padding: 5px;
cursor: pointer;
}
> .name {
transition: margin 0.2s ease;
cursor: pointer;
}
.hover &, &.selected {
> .select {
display: inline-block;
opacity: 1;
}
> .name {
margin-left: 25px;
}
}
&.selected > .select {
filter: invert(1);
}
}
@ -1028,7 +1108,7 @@ export default class Timeline extends Mixins(GlobalMixin) {
opacity: 1;
}
.tick {
> .tick {
pointer-events: none;
position: absolute;
font-size: 0.75em;
@ -1055,7 +1135,7 @@ export default class Timeline extends Mixins(GlobalMixin) {
}
}
.cursor {
> .cursor {
position: absolute;
pointer-events: none;
right: 0;
@ -1080,7 +1160,7 @@ export default class Timeline extends Mixins(GlobalMixin) {
font-weight: 600;
}
}
&:hover .cursor.st {
&:hover > .cursor.st {
opacity: 1;
}
}
@ -1099,7 +1179,7 @@ export default class Timeline extends Mixins(GlobalMixin) {
display: flex;
vertical-align: middle;
.text {
> .text {
flex-grow: 1;
line-height: 40px;
padding-left: 8px;

View File

@ -58,6 +58,8 @@ export type IRow = {
type: IRowType;
/** [Head only] Title of the header */
name?: string;
/** [Head only] Boolean if the entire day is selected */
selected?: boolean;
/** Main list of photo items */
photos?: IPhoto[];
/** Height in px of the row */
@ -65,6 +67,10 @@ export type IRow = {
/** Count of placeholders to create */
pct?: number;
}
export type IHeadRow = IRow & {
type: IRowType.HEAD;
selected: boolean;
}
export enum IRowType {
HEAD = 0,
PHOTOS = 1,