mirror of https://github.com/nextcloud/photos
Split sections in independent lists
Signed-off-by: Louis Chemineau <louis@chmn.me>
This commit is contained in:
parent
d7da7987eb
commit
5d111a18cb
|
@ -21,36 +21,55 @@
|
|||
-->
|
||||
<template>
|
||||
<div class="files-list-viewer">
|
||||
<NcEmptyContent v-if="emptyMessage !== '' && items.length === 0 && !loading"
|
||||
<NcEmptyContent v-if="emptyMessage !== '' && itemsBySections.length === 1 && itemsBySections[0].items.length === 0 && !loading"
|
||||
key="emptycontent"
|
||||
:title="emptyMessage">
|
||||
<PackageVariant slot="icon" />
|
||||
</NcEmptyContent>
|
||||
|
||||
<TiledLayout :base-height="baseHeight" :items="items">
|
||||
<VirtualScrolling slot-scope="{rows}"
|
||||
<TiledLayout :base-height="baseHeight" :sections="itemsBySections">
|
||||
<VirtualScrolling slot-scope="{tiledSections}"
|
||||
:use-window="useWindow"
|
||||
:container-element="containerElement"
|
||||
:rows="rows"
|
||||
:sections="tiledSections"
|
||||
:scroll-to-key="scrollToSection"
|
||||
:header-height="sectionHeaderHeight"
|
||||
@need-content="needContent">
|
||||
<ul slot-scope="{renderedRows}">
|
||||
<template v-for="row of renderedRows">
|
||||
<!--
|
||||
We are subtracting 1 from flex-basis to compensate for rounding issues.
|
||||
The flex algo will then compensate with flex-grow.
|
||||
-->
|
||||
<li v-for="item of row.items"
|
||||
:key="item.id"
|
||||
:class="{'files-list-viewer__section-header': item.sectionHeader}"
|
||||
:style="{ 'flex-basis': item.ratio ? `${row.height * item.ratio - 1}px` : '100%', height: `${row.height}px`}">
|
||||
<template slot-scope="{visibleSections}">
|
||||
<div v-for="section of visibleSections" :key="section.id">
|
||||
<template v-if="section.id !== ''">
|
||||
<!-- Placeholder when initial loading -->
|
||||
<div v-if="showPlaceholders" class="files-list-viewer__placeholder" />
|
||||
<div v-if="showPlaceholders"
|
||||
class="files-list-viewer__placeholder"
|
||||
:style="{ 'flex-basis': '100%', height: `${sectionHeaderHeight}px`}" />
|
||||
<!-- Real file. -->
|
||||
<slot v-else :file="item" :distance="row.distance" />
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
<slot v-else
|
||||
:file="{id: section.id}"
|
||||
:is-header="true"
|
||||
class="files-list-viewer__section-header"
|
||||
:style="{ 'flex-basis': '100%', height: `${sectionHeaderHeight}px`}" />
|
||||
</template>
|
||||
|
||||
<ul>
|
||||
<template v-for="(row, rowIndex) of section.rows">
|
||||
<!--
|
||||
We are subtracting 1 from flex-basis to compensate for rounding issues.
|
||||
The flex algo will then compensate with flex-grow.
|
||||
'last-tiled-row' prevents the last row's items from growing.
|
||||
-->
|
||||
<li v-for="item of row.items"
|
||||
:key="item.id"
|
||||
:class="{ 'last-tiled-rows': rowIndex === section.rows.length - 1 }"
|
||||
:style="{ 'flex-basis': `${item.width - 1}px`, height: `${item.height}px`}">
|
||||
<!-- Placeholder when initial loading -->
|
||||
<div v-if="showPlaceholders" class="files-list-viewer__placeholder" />
|
||||
<!-- Real file. -->
|
||||
<slot v-else :file="item" :distance="row.distance" />
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
<NcLoadingIcon v-if="loading && !showPlaceholders" slot="loader" class="files-list-viewer__loader" />
|
||||
</VirtualScrolling>
|
||||
</TiledLayout>
|
||||
|
@ -158,37 +177,36 @@ export default {
|
|||
]),
|
||||
|
||||
/**
|
||||
* @return {object[]} The list of items to pass to TiledLayout.
|
||||
* @return {{id: string, items: import('../services/TiledLayout.js').TiledItem[][]}[]} The list of items to pass to TiledLayout.
|
||||
*/
|
||||
fileIdsToItems() {
|
||||
if (this.fileIds === undefined) {
|
||||
return []
|
||||
}
|
||||
|
||||
return this.fileIds
|
||||
.filter(fileId => this.files[fileId])
|
||||
.map(this.mapFileToItem)
|
||||
return [{
|
||||
id: '',
|
||||
items: this.fileIds
|
||||
.filter(fileId => this.files[fileId])
|
||||
.map(this.mapFileToItem),
|
||||
}]
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {object[]} The list of items separated by sections to pass to TiledLayout.
|
||||
* @return {{id: string, items: import('../services/TiledLayout.js').TiledItem[][]}[]} The list of items separated by sections to pass to TiledLayout.
|
||||
*/
|
||||
sectionsToItems() {
|
||||
if (this.sections === undefined) {
|
||||
return []
|
||||
}
|
||||
|
||||
return this.sections.flatMap((sectionId) => {
|
||||
return [
|
||||
{
|
||||
id: sectionId,
|
||||
sectionHeader: true,
|
||||
height: this.sectionHeaderHeight,
|
||||
},
|
||||
...this.fileIdsBySection[sectionId]
|
||||
return this.sections.map((sectionId) => {
|
||||
return {
|
||||
id: sectionId,
|
||||
items: this.fileIdsBySection[sectionId]
|
||||
.filter(fileId => this.files[fileId])
|
||||
.map(this.mapFileToItem),
|
||||
]
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
|
@ -200,13 +218,12 @@ export default {
|
|||
},
|
||||
|
||||
/**
|
||||
* @return {object[]} The list of items to pass to TiledLayout.
|
||||
* @return {{id: string, items: import('../services/TiledLayout.js').TiledItem[][]}[]} The list of items to pass to TiledLayout.
|
||||
*/
|
||||
items() {
|
||||
|
||||
itemsBySections() {
|
||||
if (this.fileIds !== undefined) {
|
||||
if (this.showPlaceholders) {
|
||||
return this.placeholderFiles
|
||||
return [{ id: '', items: this.placeholderFiles }]
|
||||
}
|
||||
|
||||
return this.fileIdsToItems
|
||||
|
@ -214,7 +231,7 @@ export default {
|
|||
|
||||
if (this.sections !== undefined) {
|
||||
if (this.showPlaceholders) {
|
||||
return [{ height: 75, sectionHeader: true }, ...this.placeholderFiles]
|
||||
return [{ id: 'placeholder', items: this.placeholderFiles }]
|
||||
}
|
||||
|
||||
return this.sectionsToItems
|
||||
|
@ -223,6 +240,7 @@ export default {
|
|||
return []
|
||||
},
|
||||
|
||||
/** @return {boolean} The list of items to pass to TiledLayout. */
|
||||
showLoader() {
|
||||
return this.loading && (this.fileIds?.length !== 0 || this.sections?.length !== 0)
|
||||
},
|
||||
|
@ -246,6 +264,10 @@ export default {
|
|||
this.$emit('need-content')
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} fileId
|
||||
* @return {import('../services/TiledLayout.js').TiledItem[]}
|
||||
*/
|
||||
mapFileToItem(fileId) {
|
||||
const file = this.files[fileId]
|
||||
return {
|
||||
|
@ -287,7 +309,7 @@ export default {
|
|||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
li {
|
||||
li:not(.last-tiled-rows) {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,8 +43,8 @@
|
|||
:section-header-height="50"
|
||||
:scroll-to-section="targetMonth"
|
||||
@need-content="getFiles">
|
||||
<template slot-scope="{file, height, distance}">
|
||||
<h3 v-if="file.sectionHeader"
|
||||
<template slot-scope="{file, height, isHeader, distance}">
|
||||
<h3 v-if="isHeader"
|
||||
:id="`file-picker-section-header-${file.id}`"
|
||||
:style="{ height: `${height}px`}"
|
||||
class="section-header">
|
||||
|
|
|
@ -24,9 +24,9 @@
|
|||
class="tiled-container">
|
||||
<!-- Slot to allow changing the rows before passing them to TiledRows -->
|
||||
<!-- Useful for partially rendering rows like with VirtualScrolling -->
|
||||
<slot :rows="rows">
|
||||
<slot :tiled-sections="tiledSections">
|
||||
<!-- Default rendering -->
|
||||
<TiledRows :rows="rows" />
|
||||
<TiledRows :rows="tiledSections" />
|
||||
</slot>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -44,7 +44,8 @@ export default {
|
|||
},
|
||||
|
||||
props: {
|
||||
items: {
|
||||
/** @type {import('vue').PropType<import('../VirtualScrolling.vue').Section[]>} */
|
||||
sections: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
|
@ -63,11 +64,19 @@ export default {
|
|||
},
|
||||
|
||||
computed: {
|
||||
/** @return {import('../services/TiledLayout.js').TiledRow[]} */
|
||||
rows() {
|
||||
logger.debug('[TiledLayout] Computing rows', { items: this.items })
|
||||
/** @return {import('../../services/TiledLayout.js').TiledSection[]} */
|
||||
tiledSections() {
|
||||
logger.debug('[TiledLayout] Computing rows', { items: this.sections })
|
||||
|
||||
return splitItemsInRows(this.items, this.containerWidth, this.baseHeight)
|
||||
return this.sections.map(section => {
|
||||
const rows = splitItemsInRows(section.items, this.containerWidth, this.baseHeight)
|
||||
return {
|
||||
...section,
|
||||
key: section.id,
|
||||
rows: rows.map(row => ({ ...row, sectionKey: section.id })),
|
||||
height: rows.reduce((totalHeight, row) => totalHeight + row.height, 0),
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<div ref="rowsContainer"
|
||||
class="vs-rows-container"
|
||||
:style="rowsContainerStyle">
|
||||
<slot :rendered-rows="visibleRows" />
|
||||
<slot :visible-sections="visibleSections" />
|
||||
<slot name="loader" />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -32,7 +32,7 @@
|
|||
ref="rowsContainer"
|
||||
class="vs-rows-container"
|
||||
:style="rowsContainerStyle">
|
||||
<slot :rendered-rows="visibleRows" />
|
||||
<slot :visible-sections="visibleSections" />
|
||||
<slot name="loader" />
|
||||
</div>
|
||||
</template>
|
||||
|
@ -41,9 +41,24 @@
|
|||
import { debounce } from 'debounce'
|
||||
|
||||
import logger from '../services/logger.js'
|
||||
|
||||
/**
|
||||
* @typedef {object} Section
|
||||
* @property {string} key - Unique key for the section.
|
||||
* @property {Row[]} rows - The height of the row.
|
||||
* @property {number} height - Height of the section, excluding the header.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Section} VisibleSection
|
||||
* @property {VisibleRow[]} rows - The height of the row.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} Row
|
||||
* @property {string} key - Unique key for the row.
|
||||
* @property {number} height - The height of the row.
|
||||
* @property {string} sectionKey - Unique key for the row.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -55,7 +70,8 @@ export default {
|
|||
name: 'VirtualScrolling',
|
||||
|
||||
props: {
|
||||
rows: {
|
||||
/** @type {import('vue').PropType<Section[]}>} */
|
||||
sections: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
|
@ -70,6 +86,10 @@ export default {
|
|||
default: false,
|
||||
},
|
||||
|
||||
headerHeight: {
|
||||
type: Number,
|
||||
default: 75,
|
||||
},
|
||||
renderDistance: {
|
||||
type: Number,
|
||||
default: 10,
|
||||
|
@ -95,11 +115,9 @@ export default {
|
|||
},
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* @return {VisibleRow[]}
|
||||
*/
|
||||
visibleRows() {
|
||||
logger.debug('[VirtualScrolling] Computing visible rows', this.rows)
|
||||
/** @return {VisibleSection[]} */
|
||||
visibleSections() {
|
||||
logger.debug('[VirtualScrolling] Computing visible section', { sections: this.sections })
|
||||
|
||||
// Optimisation: get those computed properties once to not go through vue's internal every time we need them.
|
||||
const containerHeight = this.containerHeight
|
||||
|
@ -111,31 +129,39 @@ export default {
|
|||
|
||||
// Compute whether a row should be included in the DOM (shouldRender)
|
||||
// And how visible the row is.
|
||||
return this.rows
|
||||
.reduce((visibleRows, row) => {
|
||||
currentRowTop = currentRowBottom
|
||||
currentRowBottom += row.height
|
||||
return this.sections
|
||||
.map(section => {
|
||||
currentRowBottom += this.headerHeight
|
||||
|
||||
let distance = 0
|
||||
return {
|
||||
...section,
|
||||
rows: section.rows.reduce((visibleRows, row) => {
|
||||
currentRowTop = currentRowBottom
|
||||
currentRowBottom += row.height
|
||||
|
||||
if (currentRowBottom < containerTop) {
|
||||
distance = (containerTop - currentRowBottom) / containerHeight
|
||||
} else if (currentRowTop > containerBottom) {
|
||||
distance = (currentRowTop - containerBottom) / containerHeight
|
||||
let distance = 0
|
||||
|
||||
if (currentRowBottom < containerTop) {
|
||||
distance = (containerTop - currentRowBottom) / containerHeight
|
||||
} else if (currentRowTop > containerBottom) {
|
||||
distance = (currentRowTop - containerBottom) / containerHeight
|
||||
}
|
||||
|
||||
if (distance > this.renderDistance) {
|
||||
return visibleRows
|
||||
}
|
||||
|
||||
return [
|
||||
...visibleRows,
|
||||
{
|
||||
...row,
|
||||
distance,
|
||||
},
|
||||
]
|
||||
}, []),
|
||||
}
|
||||
|
||||
if (distance > this.renderDistance) {
|
||||
return visibleRows
|
||||
}
|
||||
|
||||
return [
|
||||
...visibleRows,
|
||||
{
|
||||
...row,
|
||||
distance,
|
||||
},
|
||||
]
|
||||
}, [])
|
||||
})
|
||||
.filter(section => section.rows.length > 0)
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -143,28 +169,42 @@ export default {
|
|||
*
|
||||
* @return {number}
|
||||
*/
|
||||
rowsHeight() {
|
||||
totalHeight() {
|
||||
const loaderHeight = 200
|
||||
|
||||
return this.rows
|
||||
.map(row => row.height)
|
||||
.reduce((totalHeight, rowHeight) => totalHeight + rowHeight, 0) + loaderHeight
|
||||
return this.sections
|
||||
.map(section => this.headerHeight + section.height)
|
||||
.reduce((totalHeight, sectionHeight) => totalHeight + sectionHeight, 0) + loaderHeight
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {number}
|
||||
*/
|
||||
paddingTop() {
|
||||
if (this.visibleRows.length === 0) {
|
||||
if (this.visibleSections.length === 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
const firstVisibleRowIndex = this.rows.findIndex(row => row.items === this.visibleRows[0].items)
|
||||
let paddingTop = 0
|
||||
|
||||
return this.rows
|
||||
.map(row => row.height)
|
||||
.slice(0, firstVisibleRowIndex)
|
||||
.reduce((totalHeight, rowHeight) => totalHeight + rowHeight, 0)
|
||||
for (const section of this.sections) {
|
||||
if (section.key !== this.visibleSections[0].rows[0].sectionKey) {
|
||||
paddingTop += this.headerHeight + section.height
|
||||
continue
|
||||
}
|
||||
|
||||
for (const row of section.rows) {
|
||||
if (row.key === this.visibleSections[0].rows[0].key) {
|
||||
return paddingTop
|
||||
}
|
||||
|
||||
paddingTop += row.height
|
||||
}
|
||||
|
||||
paddingTop += this.headerHeight
|
||||
}
|
||||
|
||||
return paddingTop
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -174,7 +214,7 @@ export default {
|
|||
*/
|
||||
rowsContainerStyle() {
|
||||
return {
|
||||
height: `${this.rowsHeight}px`,
|
||||
height: `${this.totalHeight}px`,
|
||||
paddingTop: `${this.paddingTop}px`,
|
||||
}
|
||||
},
|
||||
|
@ -187,7 +227,7 @@ export default {
|
|||
*/
|
||||
isNearBottom() {
|
||||
const buffer = this.containerHeight * this.bottomBufferRatio
|
||||
return this.scrollPosition + this.containerHeight >= this.rowsHeight - buffer
|
||||
return this.scrollPosition + this.containerHeight >= this.totalHeight - buffer
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -207,12 +247,13 @@ export default {
|
|||
|
||||
watch: {
|
||||
isNearBottom(value) {
|
||||
logger.debug('[VirtualScrolling] isNearBottom changed', { value })
|
||||
if (value) {
|
||||
this.$emit('need-content')
|
||||
}
|
||||
},
|
||||
|
||||
rows() {
|
||||
visibleSections() {
|
||||
// Re-emit need-content when rows is updated and isNearBottom is still true.
|
||||
// If the height of added rows is under `bottomBufferRatio`, `isNearBottom` will still be true so we need more content.
|
||||
if (this.isNearBottom) {
|
||||
|
@ -222,14 +263,18 @@ export default {
|
|||
|
||||
scrollToKey(key) {
|
||||
let currentRowTopDistanceFromTop = 0
|
||||
for (const row of this.rows) {
|
||||
if (row.key === key) {
|
||||
this.$refs.container.scrollTo({ top: currentRowTopDistanceFromTop, behavior: 'smooth' })
|
||||
return
|
||||
|
||||
for (const section of this.sections) {
|
||||
if (section.key !== key) {
|
||||
currentRowTopDistanceFromTop += this.headerHeight + section.height
|
||||
continue
|
||||
}
|
||||
|
||||
currentRowTopDistanceFromTop += row.height
|
||||
break
|
||||
}
|
||||
|
||||
logger.debug('[VirtualScrolling] Scrolling to', { currentRowTopDistanceFromTop })
|
||||
this.$refs.container.scrollTo({ top: currentRowTopDistanceFromTop, behavior: 'smooth' })
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ export default {
|
|||
|
||||
const fileIds = fetchedFiles
|
||||
.map(file => file.fileid)
|
||||
.filter(fileId => !this.fetchedFileIds.includes(fileId)) // Filter to prevent duplicate fileIds.
|
||||
.filter(fileId => !this.fetchedFileIds.includes(fileId.toString())) // Filter to prevent duplicate fileIds.
|
||||
|
||||
this.fetchedFileIds.push(
|
||||
...fileIds
|
||||
|
|
|
@ -22,18 +22,30 @@
|
|||
|
||||
/**
|
||||
* @typedef {object} TiledItem
|
||||
* @property {string} id
|
||||
* @property {number} [width] Real width of the item.
|
||||
* @property {string} id - Unique id for the item.
|
||||
* @property {number} width Real width of the item.
|
||||
* @property {number} height Real height of the item.
|
||||
* @property {number} [ratio] The aspect ratio of the item.
|
||||
* @property {boolean} [sectionHeader] Whether this row is a section header.
|
||||
* @property {number} ratio The aspect ratio of the item.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} Section
|
||||
* @property {string} id - Unique id for the section.
|
||||
* @property {TiledItem[]} items Real width of the item.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} TiledRow
|
||||
* @property {TiledItem[]} items -
|
||||
* @property {number} height -
|
||||
* @property {string} key -
|
||||
* @property {TiledItem[]} items - List of item in the row.
|
||||
* @property {number} height - Height of the row.
|
||||
* @property {string} key - Unique key for the row.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Section} TiledSection
|
||||
* @property {string} key - Unique key for the section.
|
||||
* @property {TiledRow[]} rows Real width of the item.
|
||||
* @property {number} height - Height of the section.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -64,19 +76,20 @@ export function splitItemsInRows(items, containerWidth, baseHeight = 200) {
|
|||
rowItems.push(items[currentItem++])
|
||||
} while (
|
||||
currentItem < items.length
|
||||
&& !items[currentItem - 1].sectionHeader && !items[currentItem].sectionHeader
|
||||
&& computeRowWidth([...rowItems, items[currentItem]], baseHeight) <= containerWidth
|
||||
)
|
||||
|
||||
const rowHeight = computeRowHeight(
|
||||
rowItems,
|
||||
containerWidth,
|
||||
items.length === currentItem,
|
||||
baseHeight
|
||||
)
|
||||
|
||||
rows[rowNumber] = {
|
||||
items: rowItems,
|
||||
height: computeRowHeight(
|
||||
rowItems,
|
||||
containerWidth,
|
||||
items.length === currentItem || items[currentItem].sectionHeader === true,
|
||||
baseHeight
|
||||
),
|
||||
items: rowItems.map(item => ({ ...item, width: rowHeight * item.ratio, height: rowHeight })),
|
||||
// Key to help vue to keep track of the row in VirtualScrolling.
|
||||
height: rowHeight,
|
||||
key: rowItems.map(item => item.id).join('-'),
|
||||
}
|
||||
|
||||
|
@ -123,11 +136,6 @@ function computeRowWidth(items, baseHeight) {
|
|||
* @return {number} The height of the row
|
||||
*/
|
||||
function computeRowHeight(items, containerWidth, isLastRow, baseHeight) {
|
||||
// Exception 1: there is only one item and its width it is a sectionHeader, meaning take the full width.
|
||||
if (items.length === 1 && items[0].sectionHeader) {
|
||||
return items[0].height
|
||||
}
|
||||
|
||||
const sumOfItemsRatio = items
|
||||
.map(item => item.ratio)
|
||||
.reduce((sum, itemRatio) => sum + itemRatio
|
||||
|
@ -135,14 +143,14 @@ function computeRowHeight(items, containerWidth, isLastRow, baseHeight) {
|
|||
|
||||
let rowHeight = containerWidth / sumOfItemsRatio
|
||||
|
||||
// Exception 2: there is only one item which is larger than containerWidth.
|
||||
// Exception 1: there is only one item which is larger than containerWidth.
|
||||
// Limit its height so that itemWidth === containerWidth
|
||||
if (items.length === 1 && items[0].width > containerWidth) {
|
||||
rowHeight = containerWidth / items[0].ratio
|
||||
}
|
||||
|
||||
// Exception 3: we reached the last row.
|
||||
// Force the items width to match containerWidth, and limit their heigh to baseHeight + 20.
|
||||
// Exception 2: we reached the last row.
|
||||
// Force the items width to match containerWidth, and limit their height to baseHeight + 20.
|
||||
if (isLastRow) {
|
||||
rowHeight = Math.min(baseHeight + 20, rowHeight)
|
||||
}
|
||||
|
|
|
@ -122,7 +122,6 @@
|
|||
<FilesPicker v-if="album !== undefined"
|
||||
:destination="album.basename"
|
||||
:blacklist-ids="albumFileIds"
|
||||
:loading="loadingAddFilesToAlbum"
|
||||
@files-picked="handleFilesPicked" />
|
||||
</NcModal>
|
||||
|
||||
|
|
|
@ -89,13 +89,13 @@
|
|||
:base-height="isMobile ? 120 : 200"
|
||||
:empty-message="t('photos', 'No photos or videos in here')"
|
||||
@need-content="getContent">
|
||||
<template slot-scope="{file, distance}">
|
||||
<h3 v-if="file.sectionHeader"
|
||||
<template slot-scope="{file, isHeader, distance}">
|
||||
<h2 v-if="isHeader"
|
||||
:id="`file-picker-section-header-${file.id}`"
|
||||
class="section-header">
|
||||
<b>{{ file.id | dateMonth }}</b>
|
||||
{{ file.id | dateYear }}
|
||||
</h3>
|
||||
</h2>
|
||||
<File v-else
|
||||
:file="files[file.id]"
|
||||
:allow-selection="true"
|
||||
|
|
Loading…
Reference in New Issue