mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-25 19:15:03 +02:00
UBERF-5595: set up attachments sizes (#4746)
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
@@ -19,9 +19,11 @@
|
||||
|
||||
import attachment from '../plugin'
|
||||
import AttachmentList from './AttachmentList.svelte'
|
||||
import { AttachmentImageSize } from '../types'
|
||||
|
||||
export let value: Doc & { attachments?: number }
|
||||
export let attachments: Attachment[] | undefined = undefined
|
||||
export let imageSize: AttachmentImageSize = 'auto'
|
||||
|
||||
const query = createQuery()
|
||||
const savedAttachmentsQuery = createQuery()
|
||||
@@ -57,4 +59,4 @@
|
||||
})
|
||||
</script>
|
||||
|
||||
<AttachmentList attachments={resAttachments} {savedAttachmentsIds} />
|
||||
<AttachmentList attachments={resAttachments} {savedAttachmentsIds} {imageSize} />
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { getIconSize2x, IconSize } from '@hcengineering/ui'
|
||||
import { getFileUrl } from '@hcengineering/presentation'
|
||||
import type { Attachment } from '@hcengineering/attachment'
|
||||
|
||||
import { AttachmentImageSize } from '../types'
|
||||
|
||||
export let value: Attachment
|
||||
export let size: AttachmentImageSize = 'auto'
|
||||
|
||||
interface Dimensions {
|
||||
width: 'auto' | number
|
||||
height: 'auto' | number
|
||||
}
|
||||
|
||||
const minSizeRem = 4
|
||||
const maxSizeRem = 20
|
||||
|
||||
const preferredWidthMap = {
|
||||
'x-large': 300
|
||||
} as const
|
||||
|
||||
let dimensions: Dimensions
|
||||
let urlSize: IconSize
|
||||
|
||||
$: dimensions = getDimensions(value, size)
|
||||
$: urlSize = getUrlSize(size)
|
||||
|
||||
function getDimensions (value: Attachment, size: AttachmentImageSize): Dimensions {
|
||||
if (size === 'auto') {
|
||||
return {
|
||||
width: 'auto',
|
||||
height: 'auto'
|
||||
}
|
||||
}
|
||||
|
||||
const preferredWidth = preferredWidthMap[size]
|
||||
const { metadata } = value
|
||||
|
||||
if (!metadata) {
|
||||
return {
|
||||
width: preferredWidth,
|
||||
height: preferredWidth
|
||||
}
|
||||
}
|
||||
|
||||
const { originalWidth, originalHeight } = metadata
|
||||
const maxSize = maxSizeRem * parseFloat(getComputedStyle(document.documentElement).fontSize)
|
||||
|
||||
const width = Math.min(originalWidth, preferredWidth)
|
||||
const ratio = originalHeight / originalWidth
|
||||
const height = width * ratio
|
||||
|
||||
if (height > maxSize) {
|
||||
return {
|
||||
width: maxSize / ratio,
|
||||
height: maxSize
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
width,
|
||||
height
|
||||
}
|
||||
}
|
||||
|
||||
function getObjectFit (size: Dimensions): 'contain' | 'cover' {
|
||||
if (size.width === 'auto' || size.height === 'auto') {
|
||||
return 'contain'
|
||||
}
|
||||
|
||||
const minSize = minSizeRem * parseFloat(getComputedStyle(document.documentElement).fontSize)
|
||||
|
||||
if (size.width < minSize || size.height < minSize) {
|
||||
return 'cover'
|
||||
}
|
||||
|
||||
return 'contain'
|
||||
}
|
||||
|
||||
function getUrlSize (size: AttachmentImageSize): IconSize {
|
||||
if (size === 'auto') {
|
||||
return 'large'
|
||||
}
|
||||
|
||||
return 'x-large'
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<img
|
||||
src={getFileUrl(value.file, urlSize)}
|
||||
style:object-fit={getObjectFit(dimensions)}
|
||||
width={dimensions.width}
|
||||
height={dimensions.height}
|
||||
srcset={`${getFileUrl(value.file, urlSize, value.name)} 1x, ${getFileUrl(
|
||||
value.file,
|
||||
getIconSize2x(urlSize),
|
||||
value.name
|
||||
)} 2x`}
|
||||
alt={value.name}
|
||||
/>
|
||||
|
||||
<style lang="scss">
|
||||
img {
|
||||
max-width: 20rem;
|
||||
max-height: 20rem;
|
||||
border-radius: 0.75rem;
|
||||
object-fit: contain;
|
||||
min-height: 4rem;
|
||||
min-width: 4rem;
|
||||
}
|
||||
</style>
|
||||
@@ -16,16 +16,23 @@
|
||||
import { Attachment } from '@hcengineering/attachment'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { Scroller } from '@hcengineering/ui'
|
||||
|
||||
import AttachmentPreview from './AttachmentPreview.svelte'
|
||||
import { AttachmentImageSize } from '../types'
|
||||
|
||||
export let attachments: Attachment[] = []
|
||||
export let savedAttachmentsIds: Ref<Attachment>[] = []
|
||||
export let imageSize: AttachmentImageSize | undefined = undefined
|
||||
</script>
|
||||
|
||||
{#if attachments.length}
|
||||
<Scroller contentDirection={'horizontal'} horizontal gap={'gap-3'}>
|
||||
{#each attachments as attachment}
|
||||
<AttachmentPreview value={attachment} isSaved={savedAttachmentsIds?.includes(attachment._id) ?? false} />
|
||||
<AttachmentPreview
|
||||
value={attachment}
|
||||
isSaved={savedAttachmentsIds?.includes(attachment._id) ?? false}
|
||||
{imageSize}
|
||||
/>
|
||||
{/each}
|
||||
</Scroller>
|
||||
{/if}
|
||||
|
||||
@@ -18,9 +18,11 @@
|
||||
import { Attachment } from '@hcengineering/attachment'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { ActionIcon, IconAdd, Label, Loading } from '@hcengineering/ui'
|
||||
import { setPlatformStatus, unknownError } from '@hcengineering/platform'
|
||||
|
||||
import { AttachmentPresenter } from '..'
|
||||
import attachment from '../plugin'
|
||||
import { uploadFile } from '../utils'
|
||||
import { getAttachmentMetadata, uploadFile } from '../utils'
|
||||
|
||||
// export let attachments: number
|
||||
export let object: Doc
|
||||
@@ -51,14 +53,21 @@
|
||||
}
|
||||
|
||||
async function createAttachment (file: File) {
|
||||
const uuid = await uploadFile(file)
|
||||
await client.addCollection(attachment.class.Attachment, object.space, object._id, object._class, 'attachments', {
|
||||
name: file.name,
|
||||
file: uuid,
|
||||
type: file.type,
|
||||
size: file.size,
|
||||
lastModified: file.lastModified
|
||||
})
|
||||
try {
|
||||
const uuid = await uploadFile(file)
|
||||
const metadata = await getAttachmentMetadata(file, uuid)
|
||||
|
||||
await client.addCollection(attachment.class.Attachment, object.space, object._id, object._class, 'attachments', {
|
||||
name: file.name,
|
||||
file: uuid,
|
||||
type: file.type,
|
||||
size: file.size,
|
||||
lastModified: file.lastModified,
|
||||
metadata
|
||||
})
|
||||
} catch (e) {
|
||||
void setPlatformStatus(unknownError(e))
|
||||
}
|
||||
}
|
||||
|
||||
async function fileSelected (): Promise<void> {
|
||||
|
||||
@@ -15,19 +15,25 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Attachment } from '@hcengineering/attachment'
|
||||
import { getFileUrl, PDFViewer } from '@hcengineering/presentation'
|
||||
import { showPopup, closeTooltip, getIconSize2x } from '@hcengineering/ui'
|
||||
import { PDFViewer } from '@hcengineering/presentation'
|
||||
import { showPopup, closeTooltip } from '@hcengineering/ui'
|
||||
import { ListSelectionProvider } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
import { getType } from '../utils'
|
||||
import AttachmentPresenter from './AttachmentPresenter.svelte'
|
||||
import AttachmentActions from './AttachmentActions.svelte'
|
||||
import AudioPlayer from './AudioPlayer.svelte'
|
||||
import { ListSelectionProvider } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { AttachmentImageSize } from '../types'
|
||||
import AttachmentImagePreview from './AttachmentImagePreview.svelte'
|
||||
import AttachmentVideoPreview from './AttachmentVideoPreview.svelte'
|
||||
|
||||
export let value: Attachment
|
||||
export let isSaved: boolean = false
|
||||
export let listProvider: ListSelectionProvider | undefined = undefined
|
||||
export let imageSize: AttachmentImageSize = 'auto'
|
||||
export let removable: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
$: type = getType(value.type)
|
||||
@@ -49,15 +55,7 @@
|
||||
dispatch('open', popupInfo.id)
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={getFileUrl(value.file, 'large')}
|
||||
srcset={`${getFileUrl(value.file, 'large', value.name)} 1x, ${getFileUrl(
|
||||
value.file,
|
||||
getIconSize2x('large'),
|
||||
value.name
|
||||
)} 2x`}
|
||||
alt={value.name}
|
||||
/>
|
||||
<AttachmentImagePreview {value} size={imageSize} />
|
||||
<div class="actions conner">
|
||||
<AttachmentActions attachment={value} {isSaved} {removable} />
|
||||
</div>
|
||||
@@ -71,13 +69,7 @@
|
||||
</div>
|
||||
{:else if type === 'video'}
|
||||
<div class="content buttonContainer flex-center">
|
||||
<video controls>
|
||||
<source src={getFileUrl(value.file, 'full', value.name)} />
|
||||
<track kind="captions" label={value.name} />
|
||||
<div class="container">
|
||||
<AttachmentPresenter {value} />
|
||||
</div>
|
||||
</video>
|
||||
<AttachmentVideoPreview {value} />
|
||||
<div class="actions conner">
|
||||
<AttachmentActions attachment={value} {isSaved} {removable} />
|
||||
</div>
|
||||
@@ -129,17 +121,5 @@
|
||||
.content {
|
||||
max-width: 20rem;
|
||||
max-height: 20rem;
|
||||
|
||||
img,
|
||||
video {
|
||||
max-width: 20rem;
|
||||
max-height: 20rem;
|
||||
border-radius: 0.75rem;
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
img {
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -17,10 +17,17 @@
|
||||
import { Attachment } from '@hcengineering/attachment'
|
||||
import { Account, Class, Doc, generateId, IdMap, Ref, Space, toIdMap } from '@hcengineering/core'
|
||||
import { IntlString, setPlatformStatus, unknownError, Asset } from '@hcengineering/platform'
|
||||
import { createQuery, DraftController, draftsStore, getClient } from '@hcengineering/presentation'
|
||||
import {
|
||||
createQuery,
|
||||
DraftController,
|
||||
draftsStore,
|
||||
getClient,
|
||||
getFileUrl,
|
||||
getImageSize
|
||||
} from '@hcengineering/presentation'
|
||||
import textEditor, { AttachIcon, type RefAction, ReferenceInput } from '@hcengineering/text-editor'
|
||||
import { Loading, type AnySvelteComponent } from '@hcengineering/ui'
|
||||
import { deleteFile, uploadFile } from '../utils'
|
||||
import { deleteFile, getAttachmentMetadata, uploadFile } from '../utils'
|
||||
import attachment from '../plugin'
|
||||
import AttachmentPresenter from './AttachmentPresenter.svelte'
|
||||
|
||||
@@ -103,6 +110,8 @@
|
||||
async function createAttachment (file: File) {
|
||||
try {
|
||||
const uuid = await uploadFile(file)
|
||||
const metadata = await getAttachmentMetadata(file, uuid)
|
||||
|
||||
const _id: Ref<Attachment> = generateId()
|
||||
attachments.set(_id, {
|
||||
_id,
|
||||
@@ -117,7 +126,8 @@
|
||||
file: uuid,
|
||||
type: file.type,
|
||||
size: file.size,
|
||||
lastModified: file.lastModified
|
||||
lastModified: file.lastModified,
|
||||
metadata
|
||||
})
|
||||
newAttachments.add(_id)
|
||||
attachments = attachments
|
||||
|
||||
@@ -20,8 +20,9 @@
|
||||
import { createQuery, DraftController, draftsStore, getClient } from '@hcengineering/presentation'
|
||||
import textEditor, { AttachIcon, type RefAction, StyledTextBox } from '@hcengineering/text-editor'
|
||||
import { ButtonSize } from '@hcengineering/ui'
|
||||
|
||||
import attachment from '../plugin'
|
||||
import { deleteFile, uploadFile } from '../utils'
|
||||
import { deleteFile, getAttachmentMetadata, uploadFile } from '../utils'
|
||||
import AttachmentsGrid from './AttachmentsGrid.svelte'
|
||||
|
||||
export let objectId: Ref<Doc> | undefined = undefined
|
||||
@@ -133,7 +134,9 @@
|
||||
if (space === undefined || objectId === undefined || _class === undefined) return
|
||||
try {
|
||||
const uuid = await uploadFile(file)
|
||||
const metadata = await getAttachmentMetadata(file, uuid)
|
||||
const _id: Ref<Attachment> = generateId()
|
||||
|
||||
attachments.set(_id, {
|
||||
_id,
|
||||
_class: attachment.class.Attachment,
|
||||
@@ -147,7 +150,8 @@
|
||||
file: uuid,
|
||||
type: file.type,
|
||||
size: file.size,
|
||||
lastModified: file.lastModified
|
||||
lastModified: file.lastModified,
|
||||
metadata
|
||||
})
|
||||
newAttachments.add(_id)
|
||||
attachments = attachments
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { getFileUrl } from '@hcengineering/presentation'
|
||||
import type { Attachment } from '@hcengineering/attachment'
|
||||
|
||||
import AttachmentPresenter from './AttachmentPresenter.svelte'
|
||||
|
||||
export let value: Attachment
|
||||
|
||||
const maxSizeRem = 20
|
||||
const baseSizeRem = 12
|
||||
const minSizeRem = 4
|
||||
|
||||
$: dimensions = getDimensions(value)
|
||||
|
||||
function getDimensions (value: Attachment): { width: number, height: number } {
|
||||
const fontSize = parseFloat(getComputedStyle(document.documentElement).fontSize)
|
||||
|
||||
if (!value.metadata) {
|
||||
const baseSize = baseSizeRem * fontSize
|
||||
return { width: baseSize, height: baseSize }
|
||||
}
|
||||
|
||||
const { originalWidth, originalHeight } = value.metadata
|
||||
const maxSize = maxSizeRem * fontSize
|
||||
|
||||
// For mp4 audio files, we don't have originalWidth, originalHeight
|
||||
if (originalWidth === 0 || originalHeight === 0) {
|
||||
return { width: maxSize, height: minSizeRem * fontSize }
|
||||
}
|
||||
|
||||
const ratio = originalHeight / originalWidth
|
||||
|
||||
const width = Math.min(maxSize, originalWidth)
|
||||
const height = width * ratio
|
||||
|
||||
if (height > maxSize) {
|
||||
return { width: maxSize / ratio, height: maxSize }
|
||||
}
|
||||
|
||||
return { width, height }
|
||||
}
|
||||
</script>
|
||||
|
||||
<video controls width={dimensions.width} height={dimensions.height}>
|
||||
<source src={getFileUrl(value.file, 'full', value.name)} />
|
||||
<track kind="captions" label={value.name} />
|
||||
<div class="container">
|
||||
<AttachmentPresenter {value} />
|
||||
</div>
|
||||
</video>
|
||||
|
||||
<style lang="scss">
|
||||
video {
|
||||
max-width: 20rem;
|
||||
max-height: 20rem;
|
||||
min-width: 4rem;
|
||||
min-height: 4rem;
|
||||
border-radius: 0.75rem;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user