mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 11:01:54 +03:00
Sidebar attachments preview (#6797)
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
parent
e09a24cbc1
commit
d3a7759ef7
@ -40,6 +40,7 @@
|
||||
"@hcengineering/ui": "^0.6.15",
|
||||
"@hcengineering/view": "^0.6.13",
|
||||
"@hcengineering/model-presentation": "^0.6.0",
|
||||
"@hcengineering/model-uploader": "^0.6.0"
|
||||
"@hcengineering/model-uploader": "^0.6.0",
|
||||
"@hcengineering/workbench": "^0.6.16"
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ import {
|
||||
import core, { TAttachedDoc } from '@hcengineering/model-core'
|
||||
import preference, { TPreference } from '@hcengineering/model-preference'
|
||||
import view, { createAction } from '@hcengineering/model-view'
|
||||
import workbench, { WidgetType } from '@hcengineering/workbench'
|
||||
import presentation from '@hcengineering/model-presentation'
|
||||
|
||||
import attachment from './plugin'
|
||||
|
||||
@ -97,6 +99,24 @@ export function createModel (builder: Builder): void {
|
||||
editor: attachment.component.Photos
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
workbench.class.Widget,
|
||||
core.space.Model,
|
||||
{
|
||||
label: attachment.string.Files,
|
||||
type: WidgetType.Flexible,
|
||||
icon: attachment.icon.Attachment,
|
||||
component: attachment.component.PreviewWidget,
|
||||
closeIfNoTabs: true
|
||||
},
|
||||
attachment.ids.PreviewWidget
|
||||
)
|
||||
|
||||
builder.createDoc(presentation.class.ComponentPointExtension, core.space.Model, {
|
||||
extension: presentation.extension.FilePreviewPopupActions,
|
||||
component: attachment.component.PreviewPopupActions
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.DocUpdateMessageViewlet,
|
||||
core.space.Model,
|
||||
|
@ -24,7 +24,9 @@ import type { ActionCategory } from '@hcengineering/view'
|
||||
|
||||
export default mergeIds(attachmentId, attachment, {
|
||||
component: {
|
||||
AttachmentPresenter: '' as AnyComponent
|
||||
AttachmentPresenter: '' as AnyComponent,
|
||||
PreviewWidget: '' as AnyComponent,
|
||||
PreviewPopupActions: '' as AnyComponent
|
||||
},
|
||||
string: {
|
||||
AddAttachment: '' as IntlString,
|
||||
|
@ -0,0 +1,43 @@
|
||||
<!--
|
||||
// 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 type { Ref, Blob } from '@hcengineering/core'
|
||||
import { Button } from '@hcengineering/ui'
|
||||
|
||||
import { getFileUrl } from '../file'
|
||||
import Download from './icons/Download.svelte'
|
||||
import presentation from '../plugin'
|
||||
|
||||
export let file: Ref<Blob> | undefined
|
||||
export let name: string
|
||||
|
||||
let download: HTMLAnchorElement
|
||||
$: srcRef = file !== undefined ? getFileUrl(file, name) : undefined
|
||||
</script>
|
||||
|
||||
{#await srcRef then src}
|
||||
{#if src !== ''}
|
||||
<a class="no-line" href={src} download={name} bind:this={download}>
|
||||
<Button
|
||||
icon={Download}
|
||||
kind={'icon'}
|
||||
on:click={() => {
|
||||
download.click()
|
||||
}}
|
||||
showTooltip={{ label: presentation.string.Download }}
|
||||
/>
|
||||
</a>
|
||||
{/if}
|
||||
{/await}
|
@ -15,17 +15,17 @@
|
||||
<script lang="ts">
|
||||
import { type Blob, type Ref } from '@hcengineering/core'
|
||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||
import { Button, Dialog, tooltip } from '@hcengineering/ui'
|
||||
import { Dialog, tooltip } from '@hcengineering/ui'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
|
||||
import presentation from '../plugin'
|
||||
|
||||
import { getFileUrl } from '../file'
|
||||
import { BlobMetadata } from '../types'
|
||||
|
||||
import ActionContext from './ActionContext.svelte'
|
||||
import FilePreview from './FilePreview.svelte'
|
||||
import Download from './icons/Download.svelte'
|
||||
import DownloadFileButton from './DownloadFileButton.svelte'
|
||||
import { ComponentExtensions } from '../index'
|
||||
import presentation from '../plugin'
|
||||
import FileTypeIcon from './FileTypeIcon.svelte'
|
||||
|
||||
export let file: Ref<Blob> | undefined
|
||||
export let name: string
|
||||
@ -38,21 +38,11 @@
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let download: HTMLAnchorElement
|
||||
|
||||
onMount(() => {
|
||||
if (fullSize) {
|
||||
dispatch('fullsize')
|
||||
}
|
||||
})
|
||||
|
||||
function iconLabel (name: string): string {
|
||||
const parts = `${name}`.split('.')
|
||||
const ext = parts[parts.length - 1]
|
||||
return ext.substring(0, 4).toUpperCase()
|
||||
}
|
||||
|
||||
$: srcRef = file !== undefined ? getFileUrl(file, name) : undefined
|
||||
</script>
|
||||
|
||||
<ActionContext context={{ mode: 'browser' }} />
|
||||
@ -67,9 +57,7 @@
|
||||
<div class="antiTitle icon-wrapper">
|
||||
{#if showIcon}
|
||||
<div class="wrapped-icon">
|
||||
<div class="flex-center icon">
|
||||
{iconLabel(name)}
|
||||
</div>
|
||||
<FileTypeIcon {name} />
|
||||
</div>
|
||||
{/if}
|
||||
<span class="wrapped-title" use:tooltip={{ label: getEmbeddedLabel(name) }}>{name}</span>
|
||||
@ -77,39 +65,19 @@
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="utils">
|
||||
{#await srcRef then src}
|
||||
{#if src !== ''}
|
||||
<a class="no-line" href={src} download={name} bind:this={download}>
|
||||
<Button
|
||||
icon={Download}
|
||||
kind={'ghost'}
|
||||
on:click={() => {
|
||||
download.click()
|
||||
}}
|
||||
showTooltip={{ label: presentation.string.Download }}
|
||||
/>
|
||||
</a>
|
||||
{/if}
|
||||
{/await}
|
||||
<DownloadFileButton {name} {file} />
|
||||
<ComponentExtensions
|
||||
extension={presentation.extension.FilePreviewPopupActions}
|
||||
props={{
|
||||
file,
|
||||
name,
|
||||
contentType,
|
||||
metadata
|
||||
}}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
|
||||
{#if file}
|
||||
<FilePreview {file} {contentType} {name} {metadata} {props} fit />
|
||||
{/if}
|
||||
</Dialog>
|
||||
|
||||
<style lang="scss">
|
||||
.icon {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
font-weight: 500;
|
||||
font-size: 0.625rem;
|
||||
color: var(--primary-button-color);
|
||||
background-color: var(--primary-button-default);
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 0.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
42
packages/presentation/src/components/FileTypeIcon.svelte
Normal file
42
packages/presentation/src/components/FileTypeIcon.svelte
Normal file
@ -0,0 +1,42 @@
|
||||
<!--
|
||||
// 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">
|
||||
export let name: string
|
||||
|
||||
function iconLabel (name: string): string {
|
||||
const parts = `${name}`.split('.')
|
||||
const ext = parts[parts.length - 1]
|
||||
return ext.substring(0, 4).toUpperCase()
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex-center icon">
|
||||
{iconLabel(name)}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.icon {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
font-weight: 500;
|
||||
font-size: 0.625rem;
|
||||
color: var(--primary-button-color);
|
||||
background-color: var(--primary-button-default);
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
</style>
|
@ -47,6 +47,8 @@ export { default as BreadcrumbsElement } from './components/breadcrumbs/Breadcru
|
||||
export { default as ComponentExtensions } from './components/extensions/ComponentExtensions.svelte'
|
||||
export { default as DocCreateExtComponent } from './components/extensions/DocCreateExtComponent.svelte'
|
||||
export { default as SearchResult } from './components/SearchResult.svelte'
|
||||
export { default as DownloadFileButton } from './components/DownloadFileButton.svelte'
|
||||
export { default as FileTypeIcon } from './components/FileTypeIcon.svelte'
|
||||
export { default } from './plugin'
|
||||
export * from './types'
|
||||
export * from './utils'
|
||||
|
@ -123,7 +123,8 @@ export default plugin(presentationId, {
|
||||
ContentTypeNotSupported: '' as IntlString
|
||||
},
|
||||
extension: {
|
||||
FilePreviewExtension: '' as ComponentExtensionId
|
||||
FilePreviewExtension: '' as ComponentExtensionId,
|
||||
FilePreviewPopupActions: '' as ComponentExtensionId
|
||||
},
|
||||
metadata: {
|
||||
ModelVersion: '' as Metadata<string>,
|
||||
|
@ -22,6 +22,7 @@
|
||||
export let icon: Asset | AnySvelteComponent | ComponentType | undefined = undefined
|
||||
export let iconProps: any | undefined = undefined
|
||||
export let iconWidth: string | undefined = undefined
|
||||
export let iconMargin: string | undefined = undefined
|
||||
export let withoutIconBackground = false
|
||||
export let label: IntlString | undefined = undefined
|
||||
export let title: string | undefined = undefined
|
||||
@ -31,7 +32,12 @@
|
||||
|
||||
<button class="hulyBreadcrumb-container {size}" class:current={isCurrent} on:click>
|
||||
{#if size === 'large' && icon}
|
||||
<div class="hulyBreadcrumb-avatar" style:width={iconWidth ?? null} class:withoutIconBackground>
|
||||
<div
|
||||
class="hulyBreadcrumb-avatar"
|
||||
style:width={iconWidth ?? null}
|
||||
style:margin={iconMargin}
|
||||
class:withoutIconBackground
|
||||
>
|
||||
<Icon {icon} size={'small'} {iconProps} />
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -130,6 +130,7 @@ export interface BreadcrumbItem {
|
||||
icon?: Asset | AnySvelteComponent | ComponentType
|
||||
iconProps?: any
|
||||
iconWidth?: string
|
||||
iconMargin?: string
|
||||
withoutIconBackground?: boolean
|
||||
label?: IntlString
|
||||
title?: string
|
||||
|
@ -52,6 +52,7 @@
|
||||
"UnPinAttachment": "Mark less important",
|
||||
"FilterAttachments": "Attachments",
|
||||
"RemovedAttachment": "Removed attachment",
|
||||
"ContentType": "Content type"
|
||||
"ContentType": "Content type",
|
||||
"OpenInWindow": "Open in window"
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +52,7 @@
|
||||
"UnPinAttachment": "Marcar como menos importante",
|
||||
"FilterAttachments": "Adjuntos",
|
||||
"RemovedAttachment": "Adjunto eliminado",
|
||||
"ContentType": "Tipo de contenido"
|
||||
"ContentType": "Tipo de contenido",
|
||||
"OpenInWindow": "Abrir en ventana"
|
||||
}
|
||||
}
|
@ -52,6 +52,7 @@
|
||||
"UnPinAttachment": "Marquer comme moins important",
|
||||
"FilterAttachments": "Pièces jointes",
|
||||
"RemovedAttachment": "Pièce jointe supprimée",
|
||||
"ContentType": "Type de contenu"
|
||||
"ContentType": "Type de contenu",
|
||||
"OpenInWindow": "Ouvrir dans une fenêtre"
|
||||
}
|
||||
}
|
@ -52,6 +52,7 @@
|
||||
"UnPinAttachment": "Marcar como menos importante",
|
||||
"FilterAttachments": "Anexos",
|
||||
"RemovedAttachment": "Anexo removido",
|
||||
"ContentType": "Tipo de conteúdo"
|
||||
"ContentType": "Tipo de conteúdo",
|
||||
"OpenInWindow": "Abrir numa janela"
|
||||
}
|
||||
}
|
@ -52,6 +52,7 @@
|
||||
"UnPinAttachment": "Убрать пометку важное",
|
||||
"FilterAttachments": "Вложения",
|
||||
"RemovedAttachment": "Удалил(а) вложение",
|
||||
"ContentType": "Тип контента"
|
||||
"ContentType": "Тип контента",
|
||||
"OpenInWindow": "Открыть в окне"
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +52,7 @@
|
||||
"UnPinAttachment": "标记为不重要",
|
||||
"FilterAttachments": "附件",
|
||||
"RemovedAttachment": "移除附件",
|
||||
"ContentType": "内容类型"
|
||||
"ContentType": "内容类型",
|
||||
"OpenInWindow": "在新窗口中打开"
|
||||
}
|
||||
}
|
||||
|
@ -38,8 +38,8 @@
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/analytics": "^0.6.0",
|
||||
"@hcengineering/activity": "^0.6.0",
|
||||
"@hcengineering/analytics": "^0.6.0",
|
||||
"@hcengineering/attachment": "^0.6.14",
|
||||
"@hcengineering/contact": "^0.6.24",
|
||||
"@hcengineering/core": "^0.6.32",
|
||||
@ -55,6 +55,7 @@
|
||||
"@hcengineering/uploader": "^0.6.0",
|
||||
"@hcengineering/view": "^0.6.13",
|
||||
"@hcengineering/view-resources": "^0.6.0",
|
||||
"@hcengineering/workbench": "^0.6.16",
|
||||
"filesize": "^8.0.3",
|
||||
"svelte": "^4.2.12"
|
||||
},
|
||||
|
@ -25,10 +25,12 @@
|
||||
} from '@hcengineering/presentation'
|
||||
import { IconMoreH, Menu, Action as UIAction, closeTooltip, showPopup, tooltip } from '@hcengineering/ui'
|
||||
import view, { Action } from '@hcengineering/view'
|
||||
import workbench from '@hcengineering/workbench'
|
||||
|
||||
import AttachmentAction from './AttachmentAction.svelte'
|
||||
import FileDownload from './icons/FileDownload.svelte'
|
||||
import attachmentPlugin from '../plugin'
|
||||
import { openAttachmentInSidebar } from '../utils'
|
||||
|
||||
export let attachment: WithLookup<Attachment>
|
||||
export let isSaved = false
|
||||
@ -95,6 +97,13 @@
|
||||
if (canPreview) {
|
||||
actions.push(openAction)
|
||||
}
|
||||
actions.push({
|
||||
icon: view.icon.DetailsFilled,
|
||||
label: workbench.string.OpenInSidebar,
|
||||
action: async () => {
|
||||
await openAttachmentInSidebar(attachment)
|
||||
}
|
||||
})
|
||||
actions.push({
|
||||
label: saveAttachmentAction.label,
|
||||
icon: saveAttachmentAction.icon,
|
||||
|
@ -17,11 +17,10 @@
|
||||
import type { Attachment } from '@hcengineering/attachment'
|
||||
import core, { type WithLookup } from '@hcengineering/core'
|
||||
import presentation, {
|
||||
FilePreviewPopup,
|
||||
canPreviewFile,
|
||||
FilePreviewPopup,
|
||||
getBlobRef,
|
||||
getFileUrl,
|
||||
getPreviewAlignment,
|
||||
previewTypes,
|
||||
sizeToWidth
|
||||
} from '@hcengineering/presentation'
|
||||
@ -29,7 +28,7 @@
|
||||
import { permissionsStore } from '@hcengineering/view-resources'
|
||||
import filesize from 'filesize'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { getType } from '../utils'
|
||||
import { getType, openAttachmentInSidebar } from '../utils'
|
||||
|
||||
import AttachmentName from './AttachmentName.svelte'
|
||||
|
||||
@ -70,7 +69,7 @@
|
||||
canPreview = false
|
||||
}
|
||||
|
||||
function clickHandler (e: MouseEvent): void {
|
||||
async function clickHandler (e: MouseEvent): Promise<void> {
|
||||
if (value === undefined || !canPreview) return
|
||||
|
||||
e.preventDefault()
|
||||
@ -80,16 +79,20 @@
|
||||
return
|
||||
}
|
||||
closeTooltip()
|
||||
showPopup(
|
||||
FilePreviewPopup,
|
||||
{
|
||||
file: value.file,
|
||||
contentType: value.type,
|
||||
name: value.name,
|
||||
metadata: value.metadata
|
||||
},
|
||||
getPreviewAlignment(value.type)
|
||||
)
|
||||
if (value.type.startsWith('image/') || value.type.startsWith('video/') || value.type.startsWith('audio/')) {
|
||||
showPopup(
|
||||
FilePreviewPopup,
|
||||
{
|
||||
file: value.file,
|
||||
contentType: value.type,
|
||||
name: value.name,
|
||||
metadata: value.metadata
|
||||
},
|
||||
'centered'
|
||||
)
|
||||
} else {
|
||||
await openAttachmentInSidebar(value)
|
||||
}
|
||||
}
|
||||
|
||||
function middleClickHandler (e: MouseEvent): void {
|
||||
|
@ -14,13 +14,13 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Attachment } from '@hcengineering/attachment'
|
||||
import { Attachment } from '@hcengineering/attachment'
|
||||
import { FilePreviewPopup } from '@hcengineering/presentation'
|
||||
import { closeTooltip, showPopup } from '@hcengineering/ui'
|
||||
import { ListSelectionProvider } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { WithLookup } from '@hcengineering/core'
|
||||
|
||||
import type { WithLookup } from '@hcengineering/core'
|
||||
import { AttachmentImageSize } from '../types'
|
||||
import { getType } from '../utils'
|
||||
import AttachmentActions from './AttachmentActions.svelte'
|
||||
|
@ -0,0 +1,41 @@
|
||||
<!--
|
||||
// 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 type { Blob, Ref } from '@hcengineering/core'
|
||||
import { BlobMetadata } from '@hcengineering/presentation'
|
||||
import { Button, closePopup, closeTooltip, IconDetailsFilled } from '@hcengineering/ui'
|
||||
import workbench from '@hcengineering/workbench'
|
||||
|
||||
import { openFilePreviewInSidebar } from '../utils'
|
||||
|
||||
export let file: Ref<Blob> | undefined
|
||||
export let name: string
|
||||
export let contentType: string
|
||||
export let metadata: BlobMetadata | undefined
|
||||
</script>
|
||||
|
||||
{#if file}
|
||||
<Button
|
||||
icon={IconDetailsFilled}
|
||||
kind="icon"
|
||||
on:click={() => {
|
||||
if (file === undefined) return
|
||||
closeTooltip()
|
||||
closePopup()
|
||||
void openFilePreviewInSidebar(file, name, contentType, metadata)
|
||||
}}
|
||||
showTooltip={{ label: workbench.string.OpenInSidebar }}
|
||||
/>
|
||||
{/if}
|
@ -0,0 +1,68 @@
|
||||
<!--
|
||||
// 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 workbench, { Widget, WidgetTab } from '@hcengineering/workbench'
|
||||
import { FilePreview, DownloadFileButton, FilePreviewPopup, FileTypeIcon } from '@hcengineering/presentation'
|
||||
import { Breadcrumbs, Button, closeTooltip, Header, IconOpen, showPopup } from '@hcengineering/ui'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import view from '@hcengineering/view'
|
||||
|
||||
import attachment from '../plugin'
|
||||
|
||||
export let widget: Widget
|
||||
export let tab: WidgetTab
|
||||
|
||||
$: file = tab.data?.file
|
||||
$: fileName = tab.data?.name ?? ''
|
||||
$: contentType = tab.data?.contentType
|
||||
$: metadata = tab.data?.metadata
|
||||
|
||||
async function closeTab (): Promise<void> {
|
||||
const fn = await getResource(workbench.function.CloseWidgetTab)
|
||||
await fn(widget, tab.id)
|
||||
}
|
||||
</script>
|
||||
|
||||
<Header
|
||||
allowFullsize={false}
|
||||
type="type-aside"
|
||||
hideBefore={true}
|
||||
hideActions={false}
|
||||
hideDescription={true}
|
||||
adaptive="disabled"
|
||||
closeOnEscape={false}
|
||||
on:close={closeTab}
|
||||
>
|
||||
<Breadcrumbs
|
||||
items={[{ title: fileName, icon: FileTypeIcon, iconProps: { name: fileName }, iconMargin: '0 0.5rem 0 0' }]}
|
||||
currentOnly
|
||||
/>
|
||||
<svelte:fragment slot="actions">
|
||||
<DownloadFileButton name={fileName} {file} />
|
||||
<Button
|
||||
icon={view.icon.Open}
|
||||
kind="icon"
|
||||
showTooltip={{ label: attachment.string.OpenInWindow }}
|
||||
on:click={() => {
|
||||
closeTooltip()
|
||||
showPopup(FilePreviewPopup, { file, name: fileName, contentType, metadata }, 'centered')
|
||||
}}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</Header>
|
||||
|
||||
{#if file}
|
||||
<FilePreview {file} {contentType} name={fileName} {metadata} />
|
||||
{/if}
|
@ -40,6 +40,8 @@ import AttachmentsUpdatedMessage from './components/activity/AttachmentsUpdatedM
|
||||
import IconAttachment from './components/icons/Attachment.svelte'
|
||||
import FileDownload from './components/icons/FileDownload.svelte'
|
||||
import IconUploadDuo from './components/icons/UploadDuo.svelte'
|
||||
import PreviewWidget from './components/PreviewWidget.svelte'
|
||||
import PreviewPopupActions from './components/PreviewPopupActions.svelte'
|
||||
|
||||
export * from './types'
|
||||
|
||||
@ -256,7 +258,9 @@ export default async (): Promise<Resources> => ({
|
||||
Attachments,
|
||||
FileBrowser,
|
||||
Photos,
|
||||
PDFViewer
|
||||
PDFViewer,
|
||||
PreviewWidget,
|
||||
PreviewPopupActions
|
||||
},
|
||||
activity: {
|
||||
AttachmentsUpdatedMessage
|
||||
|
@ -14,7 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { type Attachment } from '@hcengineering/attachment'
|
||||
import { type BlobMetadata, type Attachment } from '@hcengineering/attachment'
|
||||
import {
|
||||
type Blob,
|
||||
type Class,
|
||||
@ -24,8 +24,10 @@ import {
|
||||
type Ref,
|
||||
type Space
|
||||
} from '@hcengineering/core'
|
||||
import { setPlatformStatus, unknownError } from '@hcengineering/platform'
|
||||
import { type FileOrBlob, getFileMetadata, uploadFile } from '@hcengineering/presentation'
|
||||
import { getResource, setPlatformStatus, unknownError } from '@hcengineering/platform'
|
||||
import { type FileOrBlob, getClient, getFileMetadata, uploadFile } from '@hcengineering/presentation'
|
||||
import workbench, { type WidgetTab } from '@hcengineering/workbench'
|
||||
import view from '@hcengineering/view'
|
||||
|
||||
import attachment from './plugin'
|
||||
|
||||
@ -98,3 +100,38 @@ export function getType (type: string): 'image' | 'text' | 'json' | 'video' | 'a
|
||||
|
||||
return 'other'
|
||||
}
|
||||
|
||||
export async function openAttachmentInSidebar (value: Attachment): Promise<void> {
|
||||
await openFilePreviewInSidebar(value.file, value.name, value.type, value.metadata)
|
||||
}
|
||||
|
||||
export async function openFilePreviewInSidebar (
|
||||
file: Ref<Blob>,
|
||||
name: string,
|
||||
contentType: string,
|
||||
metadata?: BlobMetadata
|
||||
): Promise<void> {
|
||||
const client = getClient()
|
||||
const widget = client.getModel().findAllSync(workbench.class.Widget, { _id: attachment.ids.PreviewWidget })[0]
|
||||
const createFn = await getResource(workbench.function.CreateWidgetTab)
|
||||
let icon = attachment.icon.Attachment
|
||||
|
||||
if (contentType.startsWith('image/')) {
|
||||
icon = view.icon.Image
|
||||
} else if (contentType.startsWith('video/')) {
|
||||
icon = view.icon.Video
|
||||
} else if (contentType.startsWith('audio/')) {
|
||||
icon = view.icon.Audio
|
||||
} else {
|
||||
icon = view.icon.File
|
||||
}
|
||||
|
||||
const tab: WidgetTab = {
|
||||
id: file,
|
||||
icon,
|
||||
name,
|
||||
widget: attachment.ids.PreviewWidget,
|
||||
data: { file, name, contentType, metadata }
|
||||
}
|
||||
await createFn(widget, tab, true)
|
||||
}
|
||||
|
@ -40,6 +40,7 @@
|
||||
"@hcengineering/platform": "^0.6.11",
|
||||
"@hcengineering/ui": "^0.6.15",
|
||||
"@hcengineering/core": "^0.6.32",
|
||||
"@hcengineering/workbench": "^0.6.16",
|
||||
"@hcengineering/preference": "^0.6.13"
|
||||
},
|
||||
"repository": "https://github.com/hcengineering/platform",
|
||||
|
@ -19,6 +19,7 @@ import type { Asset, Plugin } from '@hcengineering/platform'
|
||||
import { IntlString, plugin, Resource } from '@hcengineering/platform'
|
||||
import type { Preference } from '@hcengineering/preference'
|
||||
import { AnyComponent } from '@hcengineering/ui'
|
||||
import { Widget } from '@hcengineering/workbench'
|
||||
|
||||
export * from './analytics'
|
||||
|
||||
@ -87,6 +88,9 @@ export default plugin(attachmentId, {
|
||||
UploadFile: '' as Resource<(file: File) => Promise<Ref<Blob>>>,
|
||||
DeleteFile: '' as Resource<(id: string) => Promise<void>>
|
||||
},
|
||||
ids: {
|
||||
PreviewWidget: '' as Ref<Widget>
|
||||
},
|
||||
string: {
|
||||
Files: '' as IntlString,
|
||||
NoFiles: '' as IntlString,
|
||||
@ -106,6 +110,7 @@ export default plugin(attachmentId, {
|
||||
FileBrowserTypeFilterPDFs: '' as IntlString,
|
||||
DeleteFile: '' as IntlString,
|
||||
Attachments: '' as IntlString,
|
||||
FileBrowser: '' as IntlString
|
||||
FileBrowser: '' as IntlString,
|
||||
OpenInWindow: '' as IntlString
|
||||
}
|
||||
})
|
||||
|
@ -196,4 +196,23 @@
|
||||
<symbol id="undo" viewBox="0 0 32 32">
|
||||
<path d="M20 10H7.8149L11.4023 6.4141L10 5L4 11L10 17L11.4023 15.5854L7.8179 12H20C21.5913 12 23.1174 12.6321 24.2426 13.7574C25.3679 14.8826 26 16.4087 26 18C26 19.5913 25.3679 21.1174 24.2426 22.2426C23.1174 23.3679 21.5913 24 20 24H13C12.4477 24 12 24.4477 12 25C12 25.5523 12.4477 26 13 26H20C22.1217 26 24.1566 25.1571 25.6569 23.6569C27.1571 22.1566 28 20.1217 28 18C28 15.8783 27.1571 13.8434 25.6569 12.3431C24.1566 10.8429 22.1217 10 20 10Z"/>
|
||||
</symbol>
|
||||
<symbol id="video" viewBox="0 0 32 32">
|
||||
<path d="M13 15V23L20 19L13 15Z" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M9 4C7.89543 4 7 4.89543 7 6V26C7 27.1046 7.89543 28 9 28H23C24.1046 28 25 27.1046 25 26V12H21C18.7909 12 17 10.2091 17 8V4H9ZM19 4.41421V8C19 9.10457 19.8954 10 21 10H24.5858L19 4.41421ZM5 6C5 3.79086 6.79086 2 9 2H18.5858C19.1162 2 19.6249 2.21071 20 2.58579L26.4142 9C26.7893 9.37507 27 9.88378 27 10.4142V26C27 28.2091 25.2091 30 23 30H9C6.79086 30 5 28.2091 5 26V6Z"
|
||||
/>
|
||||
</symbol>
|
||||
<symbol id="audio" viewBox="0 0 32 32">
|
||||
<path d="M13 15V23L20 19L13 15Z" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M9 4C7.89543 4 7 4.89543 7 6V26C7 27.1046 7.89543 28 9 28H23C24.1046 28 25 27.1046 25 26V12H21C18.7909 12 17 10.2091 17 8V4H9ZM19 4.41421V8C19 9.10457 19.8954 10 21 10H24.5858L19 4.41421ZM5 6C5 3.79086 6.79086 2 9 2H18.5858C19.1162 2 19.6249 2.21071 20 2.58579L26.4142 9C26.7893 9.37507 27 9.88378 27 10.4142V26C27 28.2091 25.2091 30 23 30H9C6.79086 30 5 28.2091 5 26V6Z"
|
||||
/>
|
||||
</symbol>
|
||||
<symbol id="file" viewBox="0 0 32 32">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 4C7.89543 4 7 4.89543 7 6V26C7 27.1046 7.89543 28 9 28H23C24.1046 28 25 27.1046 25 26V12H21C18.7909 12 17 10.2091 17 8V4H9ZM19 4.41421V8C19 9.10457 19.8954 10 21 10H24.5858L19 4.41421ZM5 6C5 3.79086 6.79086 2 9 2H18.5858C19.1162 2 19.6249 2.21071 20 2.58579L26.4142 9C26.7893 9.37507 27 9.88378 27 10.4142V26C27 28.2091 25.2091 30 23 30H9C6.79086 30 5 28.2091 5 26V6ZM10 17C10 16.4477 10.4477 16 11 16H21C21.5523 16 22 16.4477 22 17C22 17.5523 21.5523 18 21 18H11C10.4477 18 10 17.5523 10 17ZM10 23C10 22.4477 10.4477 22 11 22H21C21.5523 22 22 22.4477 22 23C22 23.5523 21.5523 24 21 24H11C10.4477 24 10 23.5523 10 23Z" />
|
||||
</symbol>
|
||||
</svg>
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 46 KiB |
@ -58,5 +58,8 @@ loadMetadata(view.icon, {
|
||||
Copy: `${icons}#copy`,
|
||||
DetailsFilled: `${icons}#details-filled`,
|
||||
Translate: `${icons}#translate`,
|
||||
Undo: `${icons}#undo`
|
||||
Undo: `${icons}#undo`,
|
||||
Video: `${icons}#video`,
|
||||
Audio: `${icons}#audio`,
|
||||
File: `${icons}#file`
|
||||
})
|
||||
|
@ -254,7 +254,10 @@ const view = plugin(viewId, {
|
||||
TodoList: '' as Asset,
|
||||
DetailsFilled: '' as Asset,
|
||||
Translate: '' as Asset,
|
||||
Undo: '' as Asset
|
||||
Undo: '' as Asset,
|
||||
Video: '' as Asset,
|
||||
Audio: '' as Asset,
|
||||
File: '' as Asset
|
||||
},
|
||||
category: {
|
||||
General: '' as Ref<ActionCategory>,
|
||||
|
@ -26,6 +26,7 @@ import ServerManager from './components/ServerManager.svelte'
|
||||
import WorkbenchTabs from './components/WorkbenchTabs.svelte'
|
||||
import { isAdminUser } from '@hcengineering/presentation'
|
||||
import { canCloseTab, closeTab, pinTab, unpinTab } from './workbench'
|
||||
import { closeWidgetTab, createWidgetTab } from './sidebar'
|
||||
|
||||
async function hasArchiveSpaces (spaces: Space[]): Promise<boolean> {
|
||||
return spaces.find((sp) => sp.archived) !== undefined
|
||||
@ -54,7 +55,9 @@ export default async (): Promise<Resources> => ({
|
||||
function: {
|
||||
HasArchiveSpaces: hasArchiveSpaces,
|
||||
IsOwner: async (docs: Space[]) => getCurrentAccount().role === AccountRole.Owner || isAdminUser(),
|
||||
CanCloseTab: canCloseTab
|
||||
CanCloseTab: canCloseTab,
|
||||
CreateWidgetTab: createWidgetTab,
|
||||
CloseWidgetTab: closeWidgetTab
|
||||
},
|
||||
actionImpl: {
|
||||
Navigate: doNavigate,
|
||||
|
@ -257,6 +257,10 @@ export default plugin(workbenchId, {
|
||||
WorkbenchExtensions: '' as ComponentExtensionId,
|
||||
WorkbenchTabExtensions: '' as ComponentExtensionId
|
||||
},
|
||||
function: {
|
||||
CreateWidgetTab: '' as Resource<(widget: Widget, tab: WidgetTab, newTab: boolean) => Promise<void>>,
|
||||
CloseWidgetTab: '' as Resource<(widget: Widget, tab: string) => Promise<void>>
|
||||
},
|
||||
actionImpl: {
|
||||
Navigate: '' as ViewAction<{
|
||||
mode: 'app' | 'special' | 'space'
|
||||
|
Loading…
Reference in New Issue
Block a user