Skip to content
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
3 changes: 2 additions & 1 deletion lib/components/FilePicker/FileList.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
tr.file-picker__row {
height: var(--row-height, 50px);
// 18px vertical padding around the preview (50px at the default 32px icon size)
height: var(--row-height, calc(var(--file-picker-preview-size, 32px) + 18px));

td {
cursor: pointer;
Expand Down
6 changes: 3 additions & 3 deletions lib/components/FilePicker/FileList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,8 @@ const fileContainer = ref<HTMLDivElement>()
<style scoped lang="scss">
.file-picker {
&__header-preview {
width: 22px; // 32px - 16px padding of button + 6px padding in file list rows
height: 32px;
width: var(--file-picker-preview-size, 32px);
height: var(--file-picker-preview-size, 32px);
flex: 0 0 auto; // do not shrink or grow
}

Expand Down Expand Up @@ -283,7 +283,7 @@ const fileContainer = ref<HTMLDivElement>()
flex-direction: row-reverse;
}
:deep(.button-vue) {
padding-inline: 16px 4px;
padding-inline: 4px;
}
}
th.row-size :deep(.button-vue__wrapper) {
Expand Down
40 changes: 29 additions & 11 deletions lib/components/FilePicker/FileListIcon.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,45 @@
/**
* Icon styling of the file list row preview or fallback icon
* (leading icon on the name row and header)
*
* Thumbnail size can be overridden instance-wide via CSS:
* :root { --file-picker-preview-size: 64px; }
*/
.file-picker__file-icon {
position: relative;
width: 32px;
height: 32px;
min-width: 32px;
min-height: 32px;
width: var(--file-picker-preview-size, 32px);
height: var(--file-picker-preview-size, 32px);
min-width: var(--file-picker-preview-size, 32px);
min-height: var(--file-picker-preview-size, 32px);
background-repeat: no-repeat;
background-size: contain;
// for the fallback
display: flex;
justify-content: center;

:global(.material-design-icon),
:global(.material-design-icon svg) {
width: var(--file-picker-preview-size, 32px);
height: var(--file-picker-preview-size, 32px);
}

.file-picker__file-icon-overlay {
color: var(--color-primary-element-text);
position: absolute;
// Center overlay on folder icon with slight offset to match material design folder glyph
// ((preview - overlay) / 2) + 2px where overlay is half the preview size
inset-block-start: calc(var(--file-picker-preview-size, 32px) * 0.25 + 2px);

// Overlay is half the preview size (beats NcIconSvgWrapper scoped svg rules)
&:global(.icon-vue) :global(svg) {
width: calc(var(--file-picker-preview-size, 32px) / 2);
height: calc(var(--file-picker-preview-size, 32px) / 2);
max-width: calc(var(--file-picker-preview-size, 32px) / 2);
max-height: calc(var(--file-picker-preview-size, 32px) / 2);
}
}
}

.file-picker__file-icon--primary {
color: var(--color-primary-element);
}

.file-picker__file-icon-overlay {
color: var(--color-primary-element-text);
position: absolute;
// 32px icon size - 16px overlay size + 2px padding to align with folder icon
inset-block-start: 10px;
}
2 changes: 1 addition & 1 deletion lib/components/FilePicker/FilePreview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<div :style="previewLoaded ? { backgroundImage: `url(${previewURL})`} : undefined"
:class="fileListIconStyles['file-picker__file-icon']">
<template v-if="!previewLoaded">
<IconFile v-if="isFile" :size="32" />
<IconFile v-if="isFile" />
<template v-else>
<NcIconSvgWrapper v-if="folderDecorationIcon"
:class="fileListIconStyles['file-picker__file-icon-overlay']"
Expand Down
6 changes: 4 additions & 2 deletions lib/composables/preview.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,10 @@ describe('preview composable', () => {
it('supports options', () => {
const previewNode = new File(createData('text.txt', 'text/plain'))

expect(getPreviewURL(previewNode, { size: 16 })?.searchParams.get('x')).toBe('16')
expect(getPreviewURL(previewNode, { size: 16 })?.searchParams.get('y')).toBe('16')
// Request sizes are snapped to backend-pregenerated 64 / 256
expect(getPreviewURL(previewNode, { size: 16 })?.searchParams.get('x')).toBe('64')
expect(getPreviewURL(previewNode, { size: 16 })?.searchParams.get('y')).toBe('64')
expect(getPreviewURL(previewNode, { size: 65 })?.searchParams.get('x')).toBe('256')

expect(getPreviewURL(previewNode, { mimeFallback: false })?.searchParams.get('mimeFallback')).toBe('false')
})
Expand Down
56 changes: 55 additions & 1 deletion lib/composables/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,13 @@ interface PreviewOptions {
* @param options Preview options
*/
export function getPreviewURL(node: INode, options: PreviewOptions = {}) {
options = { size: 32, cropPreview: false, mimeFallback: true, ...options }
options = {
cropPreview: false,
mimeFallback: true,
...options,
// Only request pregenerated sizes so large folders do not flood the preview generator
size: toPregeneratedPreviewSize(options.size ?? getFilePickerPreviewRequestSize()),
}

try {
const previewUrl = node.attributes?.previewUrl
Expand Down Expand Up @@ -87,3 +93,51 @@ export const usePreviewURL = (node: MaybeRef<INode>, options?: MaybeRef<PreviewO
previewLoaded,
}
}

/**
* CSS custom property controlling FilePicker thumbnail *display* size.
* Override on `:root` (e.g. via instance theming) to change size without a public API.
*/
const FILE_PICKER_PREVIEW_SIZE_VAR = '--file-picker-preview-size'

/** Backend-pregenerated preview sizes (cheap to serve). */
const PREGENERATED_PREVIEW_SIZE_SMALL = 64
const PREGENERATED_PREVIEW_SIZE_LARGE = 256

/**
* Cached snapped preview *request* size (64 or 256).
* CSS is read once — display size does not change at runtime.
*/
let previewRequestSize: number | undefined

/**
* Map a display size to a backend-pregenerated request size.
* size ≤ 64 → 64, size > 64 → 256 (avoids generating arbitrary preview sizes).
*
* @param size - Desired display or request size in px
*/
function toPregeneratedPreviewSize(size: number): number {
return size <= PREGENERATED_PREVIEW_SIZE_SMALL
? PREGENERATED_PREVIEW_SIZE_SMALL
: PREGENERATED_PREVIEW_SIZE_LARGE
}

/**
* Resolve the preview request size from CSS display size (once), snapped to 64 or 256.
*/
function getFilePickerPreviewRequestSize(): number {
if (previewRequestSize !== undefined) {
return previewRequestSize
}

const raw = getComputedStyle(document.documentElement)
.getPropertyValue(FILE_PICKER_PREVIEW_SIZE_VAR)
.trim()
const parsed = Number.parseFloat(raw)
const displaySize = Number.isFinite(parsed) && parsed > 0
? Math.round(parsed)
: PREGENERATED_PREVIEW_SIZE_SMALL

previewRequestSize = toPregeneratedPreviewSize(displaySize)
return previewRequestSize
}
Loading