Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(files): Provide useFileListWidth composable #49305

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions apps/files/src/components/BreadCrumbs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
v-bind="section"
dir="auto"
:to="section.to"
:force-icon-text="index === 0 && filesListWidth >= 486"
:force-icon-text="index === 0 && fileListWidth >= 486"
:title="titleForSection(index, section)"
:aria-description="ariaForSection(section)"
@click.native="onClick(section.to)"
Expand Down Expand Up @@ -46,15 +46,15 @@ import NcBreadcrumb from '@nextcloud/vue/dist/Components/NcBreadcrumb.js'
import NcBreadcrumbs from '@nextcloud/vue/dist/Components/NcBreadcrumbs.js'
import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js'

import { useNavigation } from '../composables/useNavigation'
import { onDropInternalFiles, dataTransferToFileTree, onDropExternalFiles } from '../services/DropService'
import { useNavigation } from '../composables/useNavigation.ts'
import { onDropInternalFiles, dataTransferToFileTree, onDropExternalFiles } from '../services/DropService.ts'
import { useFileListWidth } from '../composables/useFileListWidth.ts'
import { showError } from '@nextcloud/dialogs'
import { useDragAndDropStore } from '../store/dragging.ts'
import { useFilesStore } from '../store/files.ts'
import { usePathsStore } from '../store/paths.ts'
import { useSelectionStore } from '../store/selection.ts'
import { useUploaderStore } from '../store/uploader.ts'
import filesListWidthMixin from '../mixins/filesListWidth.ts'
import logger from '../logger'

export default defineComponent({
Expand All @@ -66,10 +66,6 @@ export default defineComponent({
NcIconSvgWrapper,
},

mixins: [
filesListWidthMixin,
],

props: {
path: {
type: String,
Expand All @@ -83,6 +79,7 @@ export default defineComponent({
const pathsStore = usePathsStore()
const selectionStore = useSelectionStore()
const uploaderStore = useUploaderStore()
const fileListWidth = useFileListWidth()
const { currentView, views } = useNavigation()

return {
Expand All @@ -93,6 +90,7 @@ export default defineComponent({
uploaderStore,

currentView,
fileListWidth,
views,
}
},
Expand Down Expand Up @@ -129,7 +127,7 @@ export default defineComponent({
wrapUploadProgressBar(): boolean {
// if an upload is ongoing, and on small screens / mobile, then
// show the progress bar for the upload below breadcrumbs
return this.isUploadInProgress && this.filesListWidth < 512
return this.isUploadInProgress && this.fileListWidth < 512
},

// used to show the views icon for the first breadcrumb
Expand Down
14 changes: 6 additions & 8 deletions apps/files/src/components/FilesListTableHeaderActions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'

import { useRouteParameters } from '../composables/useRouteParameters.ts'
import { useFileListWidth } from '../composables/useFileListWidth.ts'
import { useActionsMenuStore } from '../store/actionsmenu.ts'
import { useFilesStore } from '../store/files.ts'
import { useSelectionStore } from '../store/selection.ts'
import filesListWidthMixin from '../mixins/filesListWidth.ts'
import logger from '../logger.ts'

// The registered actions list
Expand All @@ -62,10 +62,6 @@ export default defineComponent({
NcLoadingIcon,
},

mixins: [
filesListWidthMixin,
],

props: {
currentView: {
type: Object as PropType<View>,
Expand All @@ -81,10 +77,12 @@ export default defineComponent({
const actionsMenuStore = useActionsMenuStore()
const filesStore = useFilesStore()
const selectionStore = useSelectionStore()
const fileListWidth = useFileListWidth()
const { directory } = useRouteParameters()

return {
directory,
fileListWidth,

actionsMenuStore,
filesStore,
Expand Down Expand Up @@ -126,13 +124,13 @@ export default defineComponent({
},

inlineActions() {
if (this.filesListWidth < 512) {
if (this.fileListWidth < 512) {
return 0
}
if (this.filesListWidth < 768) {
if (this.fileListWidth < 768) {
return 1
}
if (this.filesListWidth < 1024) {
if (this.fileListWidth < 1024) {
return 2
}
return 3
Expand Down
18 changes: 8 additions & 10 deletions apps/files/src/components/FilesListVirtual.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
isMtimeAvailable,
isSizeAvailable,
nodes,
filesListWidth,
fileListWidth,
}"
:scroll-to-index="scrollToIndex"
:caption="caption">
Expand All @@ -39,7 +39,7 @@
<template #header>
<!-- Table header and sort buttons -->
<FilesListTableHeader ref="thead"
:files-list-width="filesListWidth"
:files-list-width="fileListWidth"
:is-mtime-available="isMtimeAvailable"
:is-size-available="isSizeAvailable"
:nodes="nodes" />
Expand All @@ -48,7 +48,7 @@
<!-- Tfoot-->
<template #footer>
<FilesListTableFooter :current-view="currentView"
:files-list-width="filesListWidth"
:files-list-width="fileListWidth"
:is-mtime-available="isMtimeAvailable"
:is-size-available="isSizeAvailable"
:nodes="nodes"
Expand All @@ -69,6 +69,7 @@ import { subscribe, unsubscribe } from '@nextcloud/event-bus'
import { defineComponent } from 'vue'

import { action as sidebarAction } from '../actions/sidebarAction.ts'
import { useFileListWidth } from '../composables/useFileListWidth.ts'
import { useRouteParameters } from '../composables/useRouteParameters.ts'
import { getSummaryFor } from '../utils/fileUtils'
import { useSelectionStore } from '../store/selection.js'
Expand All @@ -79,7 +80,6 @@ import FileEntryGrid from './FileEntryGrid.vue'
import FilesListHeader from './FilesListHeader.vue'
import FilesListTableFooter from './FilesListTableFooter.vue'
import FilesListTableHeader from './FilesListTableHeader.vue'
import filesListWidthMixin from '../mixins/filesListWidth.ts'
import VirtualList from './VirtualList.vue'
import logger from '../logger.ts'
import FilesListTableHeaderActions from './FilesListTableHeaderActions.vue'
Expand All @@ -97,10 +97,6 @@ export default defineComponent({
FilesListTableHeaderActions,
},

mixins: [
filesListWidthMixin,
],

props: {
currentView: {
type: View,
Expand All @@ -119,10 +115,12 @@ export default defineComponent({
setup() {
const userConfigStore = useUserConfigStore()
const selectionStore = useSelectionStore()
const fileListWidth = useFileListWidth()
const { fileId, openFile } = useRouteParameters()

return {
fileId,
fileListWidth,
openFile,

userConfigStore,
Expand Down Expand Up @@ -151,14 +149,14 @@ export default defineComponent({

isMtimeAvailable() {
// Hide mtime column on narrow screens
if (this.filesListWidth < 768) {
if (this.fileListWidth < 768) {
return false
}
return this.nodes.some(node => node.mtime !== undefined)
},
isSizeAvailable() {
// Hide size column on narrow screens
if (this.filesListWidth < 768) {
if (this.fileListWidth < 768) {
return false
}
return this.nodes.some(node => node.size !== undefined)
Expand Down
21 changes: 13 additions & 8 deletions apps/files/src/components/VirtualList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,9 @@
import type { File, Folder, Node } from '@nextcloud/files'
import type { PropType } from 'vue'

import { useFileListWidth } from '../composables/useFileListWidth.ts'
import { defineComponent } from 'vue'
import debounce from 'debounce'
import Vue from 'vue'

import filesListWidthMixin from '../mixins/filesListWidth.ts'
import logger from '../logger.ts'

interface RecycledPoolItem {
Expand All @@ -70,11 +69,9 @@ type DataSource = File | Folder

type DataSourceKey = keyof DataSource

export default Vue.extend({
export default defineComponent({
name: 'VirtualList',

mixins: [filesListWidthMixin],

props: {
dataComponent: {
type: [Object, Function],
Expand All @@ -101,14 +98,22 @@ export default Vue.extend({
default: false,
},
/**
* Visually hidden caption for the table accesibility
* Visually hidden caption for the table accessibility
*/
caption: {
type: String,
default: '',
},
},

setup() {
const fileListWidth = useFileListWidth()

return {
fileListWidth,
}
},

data() {
return {
index: this.scrollToIndex,
Expand Down Expand Up @@ -151,7 +156,7 @@ export default Vue.extend({
if (!this.gridMode) {
return 1
}
return Math.floor(this.filesListWidth / this.itemWidth)
return Math.floor(this.fileListWidth / this.itemWidth)
},

/**
Expand Down
56 changes: 56 additions & 0 deletions apps/files/src/composables/useFileListWidth.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*!
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import { defineComponent } from 'vue'
import { useFileListWidth } from './useFileListWidth.ts'

const ComponentMock = defineComponent({
template: '<div id="test-component" style="width: 100%;background: white;">{{ fileListWidth }}</div>',
setup() {
return {
fileListWidth: useFileListWidth(),
}
},
})
const FileListMock = defineComponent({
template: '<main id="app-content-vue" style="width: 100%;"><component-mock /></main>',
components: {
ComponentMock,
},
})

describe('composable: fileListWidth', () => {

it('Has initial value', () => {
cy.viewport(600, 400)

cy.mount(FileListMock, {})
cy.get('#app-content-vue')
.should('be.visible')
.and('contain.text', '600')
})

it('Is reactive to size change', () => {
cy.viewport(600, 400)
cy.mount(FileListMock)
cy.get('#app-content-vue').should('contain.text', '600')

cy.viewport(800, 400)
cy.screenshot()
cy.get('#app-content-vue').should('contain.text', '800')
})

it('Is reactive to style changes', () => {
cy.viewport(600, 400)
cy.mount(FileListMock)
cy.get('#app-content-vue')
.should('be.visible')
.and('contain.text', '600')
.invoke('attr', 'style', 'width: 100px')

cy.get('#app-content-vue')
.should('contain.text', '100')
})
})
50 changes: 50 additions & 0 deletions apps/files/src/composables/useFileListWidth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*!
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { Ref } from 'vue'
import { onMounted, readonly, ref } from 'vue'

/** The element we observe */
let element: HTMLElement | undefined

/** The current width of the element */
const width = ref(0)

const observer = new ResizeObserver((elements) => {
if (elements[0].contentBoxSize) {
// use the newer `contentBoxSize` property if available
width.value = elements[0].contentBoxSize[0].inlineSize
} else {
// fall back to `contentRect`
width.value = elements[0].contentRect.width
}
})

/**
* Update the observed element if needed and reconfigure the observer
*/
function updateObserver() {
const el = document.querySelector<HTMLElement>('#app-content-vue') ?? document.body
if (el !== element) {
// if already observing: stop observing the old element
if (element) {
observer.unobserve(element)
}
// observe the new element if needed
observer.observe(el)
element = el
}
}

/**
* Get the reactive width of the file list
*/
export function useFileListWidth(): Readonly<Ref<number>> {
// Update the observer when the component is mounted (e.g. because this is the files app)
onMounted(updateObserver)
// Update the observer also in setup context, so we already have an initial value
updateObserver()

return readonly(width)
}
Loading
Loading