mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-23 11:31:57 +03:00
Chunter: FileBrowser - add grid view (#1571)
Signed-off-by: Ruslan Izhitsky <ruslan.izhitskiy@xored.com>
This commit is contained in:
parent
ed3d59044d
commit
e9a0225dd7
@ -17,6 +17,8 @@
|
|||||||
"Name": "Name",
|
"Name": "Name",
|
||||||
"FileBrowser": "File browser",
|
"FileBrowser": "File browser",
|
||||||
"FileBrowserFileCounter": "{results, plural, =1 {# result} other {# results}}",
|
"FileBrowserFileCounter": "{results, plural, =1 {# result} other {# results}}",
|
||||||
|
"FileBrowserListView": "View as list",
|
||||||
|
"FileBrowserGridView": "View as grid",
|
||||||
"FileBrowserFilterFrom": "From",
|
"FileBrowserFilterFrom": "From",
|
||||||
"FileBrowserFilterIn": "In",
|
"FileBrowserFilterIn": "In",
|
||||||
"FileBrowserFilterDate": "Date",
|
"FileBrowserFilterDate": "Date",
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
"Name": "Название",
|
"Name": "Название",
|
||||||
"FileBrowser": "Браузер файлов",
|
"FileBrowser": "Браузер файлов",
|
||||||
"FileBrowserFileCounter": "{results, plural, =1 {# результат} =2 {# результата} =3 {# результата} =4 {# результата} other {# результатов}}",
|
"FileBrowserFileCounter": "{results, plural, =1 {# результат} =2 {# результата} =3 {# результата} =4 {# результата} other {# результатов}}",
|
||||||
|
"FileBrowserListView": "Показать в виде списка",
|
||||||
|
"FileBrowserGridView": "Показать в виде таблицы",
|
||||||
"FileBrowserFilterFrom": "От",
|
"FileBrowserFilterFrom": "От",
|
||||||
"FileBrowserFilterIn": "В",
|
"FileBrowserFilterIn": "В",
|
||||||
"FileBrowserFilterDate": "Дата",
|
"FileBrowserFilterDate": "Дата",
|
||||||
|
@ -0,0 +1,200 @@
|
|||||||
|
<!--
|
||||||
|
// Copyright © 2022 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 { Attachment } from '@anticrm/attachment'
|
||||||
|
import { showPopup, closeTooltip } from '@anticrm/ui'
|
||||||
|
import { PDFViewer, getFileUrl } from '@anticrm/presentation'
|
||||||
|
import filesize from 'filesize'
|
||||||
|
|
||||||
|
export let value: Attachment
|
||||||
|
|
||||||
|
const maxLength: number = 18
|
||||||
|
const trimFilename = (fname: string): string =>
|
||||||
|
fname.length > maxLength ? fname.substr(0, (maxLength - 1) / 2) + '...' + fname.substr(-(maxLength - 1) / 2) : fname
|
||||||
|
|
||||||
|
function extensionIconLabel (name: string): string {
|
||||||
|
const parts = name.split('.')
|
||||||
|
const ext = parts[parts.length - 1]
|
||||||
|
return ext.substring(0, 4).toUpperCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPDF (contentType: string) {
|
||||||
|
return contentType.includes('application/pdf')
|
||||||
|
}
|
||||||
|
function isImage (contentType: string) {
|
||||||
|
return contentType.startsWith('image/')
|
||||||
|
}
|
||||||
|
function isEmbedded (contentType: string) {
|
||||||
|
return isPDF(contentType) || isImage(contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
function openAttachment () {
|
||||||
|
closeTooltip()
|
||||||
|
showPopup(PDFViewer, { file: value.file, name: value.name, contentType: value.type }, 'right')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="gridCellOverlay">
|
||||||
|
<div class="gridCell">
|
||||||
|
{#if isImage(value.type)}
|
||||||
|
<div class="cellImagePreview" on:click={openAttachment}>
|
||||||
|
<img class={'img-fit'} src={getFileUrl(value.file)} alt={value.name} />
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="cellMiscPreview">
|
||||||
|
{#if isPDF(value.type)}
|
||||||
|
<div class="flex-center extensionIcon" on:click={openAttachment}>
|
||||||
|
{extensionIconLabel(value.name)}
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<a class="no-line" href={getFileUrl(value.file)} download={value.name}>
|
||||||
|
<div class="flex-center extensionIcon">{extensionIconLabel(value.name)}</div>
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="cellInfo">
|
||||||
|
{#if isEmbedded(value.type)}
|
||||||
|
<div class="flex-center extensionIcon" on:click={openAttachment}>
|
||||||
|
{extensionIconLabel(value.name)}
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<a class="no-line" href={getFileUrl(value.file)} download={value.name}>
|
||||||
|
<div class="flex-center extensionIcon">{extensionIconLabel(value.name)}</div>
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
|
<div class="eCellInfoData">
|
||||||
|
{#if isEmbedded(value.type)}
|
||||||
|
<div class="eCellInfoFilename" on:click={openAttachment}>
|
||||||
|
{trimFilename(value.name)}
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="eCellInfoFilename">
|
||||||
|
<a href={getFileUrl(value.file)} download={value.name}>{trimFilename(value.name)}</a>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<div class="eCellInfoFilesize">{filesize(value.size)}</div>
|
||||||
|
</div>
|
||||||
|
<div class="eCellInfoMenu"><slot name="rowMenu" /></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.gridCellOverlay {
|
||||||
|
position: relative;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gridCell {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border-radius: 12px;
|
||||||
|
justify-content: space-between;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 0 0 1px var(--theme-bg-focused-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cellImagePreview {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
height: 160px;
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 0 1.5rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background-color: var(--theme-menu-color);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cellMiscPreview {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.img-fit {
|
||||||
|
object-fit: cover;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cellInfo {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
padding: 12px;
|
||||||
|
min-height: 36px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.eCellInfoData {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eCellInfoMenu {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eCellInfoFilename {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--theme-content-accent-color);
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eCellInfoFilesize {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--theme-content-dark-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.extensionIcon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.625rem;
|
||||||
|
color: var(--primary-button-color);
|
||||||
|
background-color: var(--primary-button-enabled);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eCellInfoFilename:hover,
|
||||||
|
.extensionIcon:hover + .eCellInfoData > .eCellInfoFilename, // embedded on extension hover
|
||||||
|
.no-line:hover + .eCellInfoData > .eCellInfoFilename a, // not embedded on extension hover
|
||||||
|
.cellImagePreview:hover + .cellInfo .eCellInfoFilename, // image on preview hover
|
||||||
|
.cellMiscPreview:hover + .cellInfo .eCellInfoFilename, // PDF on preview hover
|
||||||
|
.cellMiscPreview:hover + .cellInfo .eCellInfoFilename a // not embedded on preview hover
|
||||||
|
{
|
||||||
|
text-decoration: underline;
|
||||||
|
color: var(--theme-caption-color);
|
||||||
|
}
|
||||||
|
.eCellInfoFilename:active,
|
||||||
|
.extensionIcon:active + .eCellInfoData > .eCellInfoFilename, // embedded on extension hover
|
||||||
|
.no-line:active + .eCellInfoData > .eCellInfoFilename a, // not embedded on extension hover
|
||||||
|
.cellImagePreview:active + .cellInfo .eCellInfoFilename, // image on preview hover
|
||||||
|
.cellMiscPreview:active + .cellInfo .eCellInfoFilename, // PDF on preview hover
|
||||||
|
.cellMiscPreview:active + .cellInfo .eCellInfoFilename a // not embedded on preview hover
|
||||||
|
{
|
||||||
|
text-decoration: underline;
|
||||||
|
color: var(--theme-content-accent-color);
|
||||||
|
}
|
||||||
|
</style>
|
@ -13,7 +13,6 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Attachment } from '@anticrm/attachment'
|
import type { Attachment } from '@anticrm/attachment'
|
||||||
import { showPopup, closeTooltip } from '@anticrm/ui'
|
import { showPopup, closeTooltip } from '@anticrm/ui'
|
||||||
@ -23,9 +22,8 @@
|
|||||||
export let value: Attachment
|
export let value: Attachment
|
||||||
|
|
||||||
const maxLenght: number = 16
|
const maxLenght: number = 16
|
||||||
const trimFilename = (fname: string): string => (fname.length > maxLenght)
|
const trimFilename = (fname: string): string =>
|
||||||
? fname.substr(0, (maxLenght - 1) / 2) + '...' + fname.substr(-(maxLenght - 1) / 2)
|
fname.length > maxLenght ? fname.substr(0, (maxLenght - 1) / 2) + '...' + fname.substr(-(maxLenght - 1) / 2) : fname
|
||||||
: fname
|
|
||||||
|
|
||||||
function iconLabel (name: string): string {
|
function iconLabel (name: string): string {
|
||||||
const parts = name.split('.')
|
const parts = name.split('.')
|
||||||
@ -40,18 +38,33 @@
|
|||||||
|
|
||||||
<div class="flex-row-center">
|
<div class="flex-row-center">
|
||||||
{#if openEmbedded(value.type)}
|
{#if openEmbedded(value.type)}
|
||||||
<div class="flex-center icon" on:click={() => {
|
<div
|
||||||
closeTooltip()
|
class="flex-center icon"
|
||||||
showPopup(PDFViewer, { file: value.file, name: value.name, contentType: value.type }, 'right')
|
on:click={() => {
|
||||||
}}>{iconLabel(value.name)}</div>
|
closeTooltip()
|
||||||
|
showPopup(PDFViewer, { file: value.file, name: value.name, contentType: value.type }, 'right')
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{iconLabel(value.name)}
|
||||||
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<a class="no-line" href={getFileUrl(value.file)} download={value.name}><div class="flex-center icon">{iconLabel(value.name)}</div></a>
|
<a class="no-line" href={getFileUrl(value.file)} download={value.name}
|
||||||
|
><div class="flex-center icon">{iconLabel(value.name)}</div></a
|
||||||
|
>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="flex-col info">
|
<div class="flex-col info">
|
||||||
{#if openEmbedded(value.type)}
|
{#if openEmbedded(value.type)}
|
||||||
<div class="name" on:click={() => { closeTooltip(); showPopup(PDFViewer, { file: value.file, name: value.name, contentType: value.type }, 'right') }}>{trimFilename(value.name)}</div>
|
<div
|
||||||
|
class="name"
|
||||||
|
on:click={() => {
|
||||||
|
closeTooltip()
|
||||||
|
showPopup(PDFViewer, { file: value.file, name: value.name, contentType: value.type }, 'right')
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{trimFilename(value.name)}
|
||||||
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="name"><a href={getFileUrl(value.file)} download={value.name}>{trimFilename(value.name)}</a></div>
|
<div class="name"><a href={getFileUrl(value.file)} download={value.name}>{trimFilename(value.name)}</a></div>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="type">{filesize(value.size)}</div>
|
<div class="type">{filesize(value.size)}</div>
|
||||||
</div>
|
</div>
|
||||||
@ -64,11 +77,11 @@
|
|||||||
width: 2rem;
|
width: 2rem;
|
||||||
height: 2rem;
|
height: 2rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: .625rem;
|
font-size: 0.625rem;
|
||||||
color: var(--primary-button-color);
|
color: var(--primary-button-color);
|
||||||
background-color: var(--primary-button-enabled);
|
background-color: var(--primary-button-enabled);
|
||||||
border: 1px solid rgba(0, 0, 0, .1);
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
border-radius: .5rem;
|
border-radius: 0.5rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,15 +93,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.type {
|
.type {
|
||||||
font-size: .75rem;
|
font-size: 0.75rem;
|
||||||
color: var(--theme-content-dark-color);
|
color: var(--theme-content-dark-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.name:hover, .icon:hover + .info > .name, .no-line:hover + .info > .name a {
|
.name:hover,
|
||||||
|
.icon:hover + .info > .name,
|
||||||
|
.no-line:hover + .info > .name a {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
color: var(--theme-caption-color);
|
color: var(--theme-caption-color);
|
||||||
}
|
}
|
||||||
.name:active, .icon:active + .info > .name, .no-line:active + .info > .name a {
|
.name:active,
|
||||||
|
.icon:active + .info > .name,
|
||||||
|
.no-line:active + .info > .name a {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
color: var(--theme-content-accent-color);
|
color: var(--theme-content-accent-color);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<!--
|
<!--
|
||||||
// Copyright © 2022 Hardcore Engineering Inc.
|
// Copyright © 2022 Hardcore Engineering Inc.
|
||||||
//
|
//
|
||||||
@ -44,15 +43,20 @@
|
|||||||
const query = createQuery()
|
const query = createQuery()
|
||||||
let attachments: Map<Ref<Attachment>, Attachment> = new Map<Ref<Attachment>, Attachment>()
|
let attachments: Map<Ref<Attachment>, Attachment> = new Map<Ref<Attachment>, Attachment>()
|
||||||
let originalAttachments: Set<Ref<Attachment>> = new Set<Ref<Attachment>>()
|
let originalAttachments: Set<Ref<Attachment>> = new Set<Ref<Attachment>>()
|
||||||
let newAttachments: Set<Ref<Attachment>> = new Set<Ref<Attachment>>()
|
const newAttachments: Set<Ref<Attachment>> = new Set<Ref<Attachment>>()
|
||||||
let removedAttachments: Set<Attachment> = new Set<Attachment>()
|
const removedAttachments: Set<Attachment> = new Set<Attachment>()
|
||||||
|
|
||||||
$: objectId && query.query(attachment.class.Attachment, {
|
$: objectId &&
|
||||||
attachedTo: objectId
|
query.query(
|
||||||
}, (res) => {
|
attachment.class.Attachment,
|
||||||
originalAttachments = new Set(res.map((p) => p._id))
|
{
|
||||||
attachments = new Map(res.map((p) => [p._id, p]))
|
attachedTo: objectId
|
||||||
})
|
},
|
||||||
|
(res) => {
|
||||||
|
originalAttachments = new Set(res.map((p) => p._id))
|
||||||
|
attachments = new Map(res.map((p) => [p._id, p]))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
async function createAttachment (file: File) {
|
async function createAttachment (file: File) {
|
||||||
try {
|
try {
|
||||||
@ -82,7 +86,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function saveAttachment (doc: Attachment) {
|
async function saveAttachment (doc: Attachment) {
|
||||||
const res = await client.addCollection(attachment.class.Attachment, space, objectId, _class, 'attachments', doc, doc._id)
|
const res = await client.addCollection(
|
||||||
|
attachment.class.Attachment,
|
||||||
|
space,
|
||||||
|
objectId,
|
||||||
|
_class,
|
||||||
|
'attachments',
|
||||||
|
doc,
|
||||||
|
doc._id
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function fileSelected () {
|
function fileSelected () {
|
||||||
@ -112,7 +124,14 @@
|
|||||||
|
|
||||||
async function deleteAttachment (attachment: Attachment): Promise<void> {
|
async function deleteAttachment (attachment: Attachment): Promise<void> {
|
||||||
if (originalAttachments.has(attachment._id)) {
|
if (originalAttachments.has(attachment._id)) {
|
||||||
await client.removeCollection(attachment._class, attachment.space, attachment._id, attachment.attachedTo, attachment.attachedToClass, 'attachments')
|
await client.removeCollection(
|
||||||
|
attachment._class,
|
||||||
|
attachment.space,
|
||||||
|
attachment._id,
|
||||||
|
attachment.attachedTo,
|
||||||
|
attachment.attachedToClass,
|
||||||
|
'attachments'
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
await deleteFile(attachment.file)
|
await deleteFile(attachment.file)
|
||||||
}
|
}
|
||||||
@ -144,7 +163,6 @@
|
|||||||
await Promise.all(promises)
|
await Promise.all(promises)
|
||||||
dispatch('message', { message: event.detail, attachments: attachments.size })
|
dispatch('message', { message: event.detail, attachments: attachments.size })
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
@ -155,25 +173,41 @@
|
|||||||
id="file"
|
id="file"
|
||||||
style="display: none"
|
style="display: none"
|
||||||
on:change={fileSelected}
|
on:change={fileSelected}
|
||||||
/>
|
/>
|
||||||
<div class="container"
|
<div
|
||||||
|
class="container"
|
||||||
on:dragover|preventDefault={() => {}}
|
on:dragover|preventDefault={() => {}}
|
||||||
on:dragleave={() => {}}
|
on:dragleave={() => {}}
|
||||||
on:drop|preventDefault|stopPropagation={fileDrop}
|
on:drop|preventDefault|stopPropagation={fileDrop}
|
||||||
>
|
>
|
||||||
{#if attachments.size}
|
{#if attachments.size}
|
||||||
<div class='flex-row-center list'>
|
<div class="flex-row-center list">
|
||||||
{#each Array.from(attachments.values()) as attachment}
|
{#each Array.from(attachments.values()) as attachment}
|
||||||
<div class='item flex'>
|
<div class="item flex">
|
||||||
<AttachmentPresenter value={attachment} />
|
<AttachmentPresenter value={attachment} />
|
||||||
<div class='remove'>
|
<div class="remove">
|
||||||
<ActionIcon icon={IconClose} action={() => { removeAttachment(attachment) }} size='small' />
|
<ActionIcon
|
||||||
|
icon={IconClose}
|
||||||
|
action={() => {
|
||||||
|
removeAttachment(attachment)
|
||||||
|
}}
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<ReferenceInput bind:this={refInput} {content} {showSend} on:message={onMessage} withoutTopBorder={attachments.size > 0} on:attach={() => { inputFile.click() }} />
|
<ReferenceInput
|
||||||
|
bind:this={refInput}
|
||||||
|
{content}
|
||||||
|
{showSend}
|
||||||
|
on:message={onMessage}
|
||||||
|
withoutTopBorder={attachments.size > 0}
|
||||||
|
on:attach={() => {
|
||||||
|
inputFile.click()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@ -183,7 +217,7 @@
|
|||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
background-color: var(--theme-bg-accent-color);
|
background-color: var(--theme-bg-accent-color);
|
||||||
border: 1px solid var(--theme-bg-accent-color);
|
border: 1px solid var(--theme-bg-accent-color);
|
||||||
border-radius: .75rem;
|
border-radius: 0.75rem;
|
||||||
border-bottom-left-radius: 0;
|
border-bottom-left-radius: 0;
|
||||||
border-bottom-right-radius: 0;
|
border-bottom-right-radius: 0;
|
||||||
|
|
||||||
|
@ -0,0 +1,98 @@
|
|||||||
|
<!--
|
||||||
|
// Copyright © 2022 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 { Attachment } from '@anticrm/attachment'
|
||||||
|
import { Doc } from '@anticrm/core'
|
||||||
|
import { getFileUrl } from '@anticrm/presentation'
|
||||||
|
import { Icon, IconMoreV, showPopup } from '@anticrm/ui'
|
||||||
|
import { Menu } from '@anticrm/view-resources'
|
||||||
|
import FileDownload from './icons/FileDownload.svelte'
|
||||||
|
import { AttachmentGalleryPresenter } from '..'
|
||||||
|
|
||||||
|
export let attachments: Attachment[]
|
||||||
|
let selectedFileNumber: number | undefined
|
||||||
|
|
||||||
|
const showFileMenu = async (ev: MouseEvent, object: Doc, fileNumber: number): Promise<void> => {
|
||||||
|
selectedFileNumber = fileNumber
|
||||||
|
showPopup(Menu, { object }, ev.target as HTMLElement, () => {
|
||||||
|
selectedFileNumber = undefined
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="galleryGrid">
|
||||||
|
{#each attachments as attachment, i}
|
||||||
|
<div class="attachmentCell" class:fixed={i === selectedFileNumber}>
|
||||||
|
<AttachmentGalleryPresenter value={attachment}>
|
||||||
|
<svelte:fragment slot="rowMenu">
|
||||||
|
<div class="eAttachmentCellActions" class:fixed={i === selectedFileNumber}>
|
||||||
|
<a href={getFileUrl(attachment.file)} download={attachment.name}>
|
||||||
|
<Icon icon={FileDownload} size={'small'} />
|
||||||
|
</a>
|
||||||
|
<div class="eAttachmentCellMenu" on:click={(event) => showFileMenu(event, attachment, i)}>
|
||||||
|
<IconMoreV size={'small'} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</svelte:fragment>
|
||||||
|
</AttachmentGalleryPresenter>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.galleryGrid {
|
||||||
|
display: grid;
|
||||||
|
margin: 0 1.5rem;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachmentCell {
|
||||||
|
.eAttachmentCellActions {
|
||||||
|
display: flex;
|
||||||
|
visibility: hidden;
|
||||||
|
border: 1px solid var(--theme-bg-focused-border);
|
||||||
|
padding: 0.2rem;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eAttachmentCellMenu {
|
||||||
|
visibility: hidden;
|
||||||
|
margin-left: 0.2rem;
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.eAttachmentCellActions {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
.eAttachmentCellMenu {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.fixed {
|
||||||
|
.eAttachmentCellActions {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
.eAttachmentCellMenu {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,96 @@
|
|||||||
|
<!--
|
||||||
|
// Copyright © 2022 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 { Attachment } from '@anticrm/attachment'
|
||||||
|
import { Doc } from '@anticrm/core'
|
||||||
|
import { getFileUrl } from '@anticrm/presentation'
|
||||||
|
import { Icon, IconMoreV, showPopup } from '@anticrm/ui'
|
||||||
|
import { Menu } from '@anticrm/view-resources'
|
||||||
|
import FileDownload from './icons/FileDownload.svelte'
|
||||||
|
import { AttachmentPresenter } from '..'
|
||||||
|
|
||||||
|
export let attachments: Attachment[]
|
||||||
|
let selectedFileNumber: number | undefined
|
||||||
|
|
||||||
|
const showFileMenu = async (ev: MouseEvent, object: Doc, fileNumber: number): Promise<void> => {
|
||||||
|
selectedFileNumber = fileNumber
|
||||||
|
showPopup(Menu, { object }, ev.target as HTMLElement, () => {
|
||||||
|
selectedFileNumber = undefined
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex-col">
|
||||||
|
{#each attachments as attachment, i}
|
||||||
|
<div class="flex-between attachmentRow" class:fixed={i === selectedFileNumber}>
|
||||||
|
<div class="item flex">
|
||||||
|
<AttachmentPresenter value={attachment} />
|
||||||
|
</div>
|
||||||
|
<div class="eAttachmentRowActions" class:fixed={i === selectedFileNumber}>
|
||||||
|
<a href={getFileUrl(attachment.file)} download={attachment.name}>
|
||||||
|
<Icon icon={FileDownload} size={'small'} />
|
||||||
|
</a>
|
||||||
|
<div class="eAttachmentRowMenu" on:click={(event) => showFileMenu(event, attachment, i)}>
|
||||||
|
<IconMoreV size={'small'} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.attachmentRow {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 0 1.5rem;
|
||||||
|
padding: 0.25rem 0;
|
||||||
|
|
||||||
|
.eAttachmentRowActions {
|
||||||
|
display: flex;
|
||||||
|
visibility: hidden;
|
||||||
|
border: 1px solid var(--theme-bg-focused-border);
|
||||||
|
padding: 0.2rem;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eAttachmentRowMenu {
|
||||||
|
visibility: hidden;
|
||||||
|
margin-left: 0.2rem;
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.eAttachmentRowActions {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
.eAttachmentRowMenu {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.fixed {
|
||||||
|
.eAttachmentRowActions {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
.eAttachmentRowMenu {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -16,31 +16,24 @@
|
|||||||
import { Attachment } from '@anticrm/attachment'
|
import { Attachment } from '@anticrm/attachment'
|
||||||
import contact, { Employee } from '@anticrm/contact'
|
import contact, { Employee } from '@anticrm/contact'
|
||||||
import { EmployeeAccount } from '@anticrm/contact'
|
import { EmployeeAccount } from '@anticrm/contact'
|
||||||
import core, { Class, Doc, getCurrentAccount, Ref, Space } from '@anticrm/core'
|
import core, { Class, getCurrentAccount, Ref, Space } from '@anticrm/core'
|
||||||
import { getClient, getFileUrl } from '@anticrm/presentation'
|
import { getClient } from '@anticrm/presentation'
|
||||||
import ui, {
|
import ui, {
|
||||||
getCurrentLocation,
|
getCurrentLocation,
|
||||||
location,
|
location,
|
||||||
IconMoreV,
|
|
||||||
IconSearch,
|
IconSearch,
|
||||||
Label,
|
Label,
|
||||||
showPopup,
|
|
||||||
navigate,
|
navigate,
|
||||||
EditWithIcon,
|
EditWithIcon,
|
||||||
Spinner,
|
Spinner,
|
||||||
|
Tooltip,
|
||||||
Icon
|
Icon
|
||||||
} from '@anticrm/ui'
|
} from '@anticrm/ui'
|
||||||
import { Menu } from '@anticrm/view-resources'
|
|
||||||
import { onDestroy } from 'svelte'
|
import { onDestroy } from 'svelte'
|
||||||
import {
|
import { FileBrowserSortMode, dateFileBrowserFilters, fileTypeFileBrowserFilters, sortModeToOptionObject } from '..'
|
||||||
AttachmentPresenter,
|
|
||||||
FileBrowserSortMode,
|
|
||||||
dateFileBrowserFilters,
|
|
||||||
fileTypeFileBrowserFilters,
|
|
||||||
sortModeToOptionObject
|
|
||||||
} from '..'
|
|
||||||
import attachment from '../plugin'
|
import attachment from '../plugin'
|
||||||
import FileDownload from './icons/FileDownload.svelte'
|
import AttachmentsGalleryView from './AttachmentsGalleryView.svelte'
|
||||||
|
import AttachmentsListView from './AttachmentsListView.svelte'
|
||||||
import FileBrowserFilters from './FileBrowserFilters.svelte'
|
import FileBrowserFilters from './FileBrowserFilters.svelte'
|
||||||
import FileBrowserSortMenu from './FileBrowserSortMenu.svelte'
|
import FileBrowserSortMenu from './FileBrowserSortMenu.svelte'
|
||||||
|
|
||||||
@ -55,22 +48,15 @@
|
|||||||
let isLoading = false
|
let isLoading = false
|
||||||
|
|
||||||
let attachments: Attachment[] = []
|
let attachments: Attachment[] = []
|
||||||
let selectedFileNumber: number | undefined
|
|
||||||
|
|
||||||
let selectedSort: FileBrowserSortMode = FileBrowserSortMode.NewestFile
|
let selectedSort: FileBrowserSortMode = FileBrowserSortMode.NewestFile
|
||||||
let selectedDateId = 'dateAny'
|
let selectedDateId = 'dateAny'
|
||||||
let selectedFileTypeId = 'typeAny'
|
let selectedFileTypeId = 'typeAny'
|
||||||
|
let isListDisplayMode = true
|
||||||
const showFileMenu = async (ev: MouseEvent, object: Doc, fileNumber: number): Promise<void> => {
|
|
||||||
selectedFileNumber = fileNumber
|
|
||||||
showPopup(Menu, { object }, ev.target as HTMLElement, () => {
|
|
||||||
selectedFileNumber = undefined
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
$: fetch(searchQuery, selectedSort, selectedFileTypeId, selectedDateId, selectedParticipants, selectedSpaces)
|
$: fetch(searchQuery, selectedSort, selectedFileTypeId, selectedDateId, selectedParticipants, selectedSpaces)
|
||||||
|
|
||||||
async function fetch(
|
async function fetch (
|
||||||
searchQuery_: string,
|
searchQuery_: string,
|
||||||
selectedSort_: FileBrowserSortMode,
|
selectedSort_: FileBrowserSortMode,
|
||||||
selectedFileTypeId_: string,
|
selectedFileTypeId_: string,
|
||||||
@ -131,14 +117,40 @@
|
|||||||
</div>
|
</div>
|
||||||
<EditWithIcon icon={IconSearch} bind:value={searchQuery} placeholder={ui.string.SearchDots} />
|
<EditWithIcon icon={IconSearch} bind:value={searchQuery} placeholder={ui.string.SearchDots} />
|
||||||
</div>
|
</div>
|
||||||
<FileBrowserFilters
|
<div class="ac-header full">
|
||||||
{requestedSpaceClasses}
|
<FileBrowserFilters
|
||||||
{spaceId}
|
{requestedSpaceClasses}
|
||||||
bind:selectedParticipants
|
{spaceId}
|
||||||
bind:selectedSpaces
|
bind:selectedParticipants
|
||||||
bind:selectedDateId
|
bind:selectedSpaces
|
||||||
bind:selectedFileTypeId
|
bind:selectedDateId
|
||||||
/>
|
bind:selectedFileTypeId
|
||||||
|
/>
|
||||||
|
<div class="flex">
|
||||||
|
<Tooltip label={attachment.string.FileBrowserListView} direction={'bottom'}>
|
||||||
|
<button
|
||||||
|
class="ac-header__icon-button"
|
||||||
|
class:selected={isListDisplayMode}
|
||||||
|
on:click={() => {
|
||||||
|
isListDisplayMode = true
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon={contact.icon.Person} size={'small'} />
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip label={attachment.string.FileBrowserGridView} direction={'bottom'}>
|
||||||
|
<button
|
||||||
|
class="ac-header__icon-button"
|
||||||
|
class:selected={!isListDisplayMode}
|
||||||
|
on:click={() => {
|
||||||
|
isListDisplayMode = false
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon={contact.icon.Edit} size={'small'} />
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="group">
|
<div class="group">
|
||||||
<div class="groupHeader">
|
<div class="groupHeader">
|
||||||
<div class="eGroupHeaderCount">
|
<div class="eGroupHeaderCount">
|
||||||
@ -151,23 +163,11 @@
|
|||||||
<Spinner />
|
<Spinner />
|
||||||
</div>
|
</div>
|
||||||
{:else if attachments?.length}
|
{:else if attachments?.length}
|
||||||
<div class="flex-col">
|
{#if isListDisplayMode}
|
||||||
{#each attachments as attachment, i}
|
<AttachmentsListView {attachments} />
|
||||||
<div class="flex-between attachmentRow" class:fixed={i === selectedFileNumber}>
|
{:else}
|
||||||
<div class="item flex">
|
<AttachmentsGalleryView {attachments} />
|
||||||
<AttachmentPresenter value={attachment} />
|
{/if}
|
||||||
</div>
|
|
||||||
<div class="eAttachmentRowActions" class:fixed={i === selectedFileNumber}>
|
|
||||||
<a href={getFileUrl(attachment.file)} download={attachment.name}>
|
|
||||||
<Icon icon={FileDownload} size={'small'} />
|
|
||||||
</a>
|
|
||||||
<div id="context-menu" class="eAttachmentRowMenu" on:click={(event) => showFileMenu(event, attachment, i)}>
|
|
||||||
<IconMoreV size={'small'} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex-between attachmentRow">
|
<div class="flex-between attachmentRow">
|
||||||
<Label label={attachment.string.NoFiles} />
|
<Label label={attachment.string.NoFiles} />
|
||||||
@ -191,48 +191,4 @@
|
|||||||
color: var(--theme-caption-color);
|
color: var(--theme-caption-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.attachmentRow {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding-right: 1rem;
|
|
||||||
margin: 0 1.5rem;
|
|
||||||
padding: 0.25rem 0;
|
|
||||||
|
|
||||||
.eAttachmentRowActions {
|
|
||||||
display: flex;
|
|
||||||
visibility: hidden;
|
|
||||||
border: 1px solid var(--theme-bg-focused-border);
|
|
||||||
padding: 0.2rem;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eAttachmentRowMenu {
|
|
||||||
margin-left: 0.2rem;
|
|
||||||
visibility: hidden;
|
|
||||||
opacity: 0.6;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
.eAttachmentRowActions {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
.eAttachmentRowMenu {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.fixed {
|
|
||||||
.eAttachmentRowActions {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
.eAttachmentRowMenu {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -17,6 +17,7 @@ import AddAttachment from './components/AddAttachment.svelte'
|
|||||||
import AttachmentDroppable from './components/AttachmentDroppable.svelte'
|
import AttachmentDroppable from './components/AttachmentDroppable.svelte'
|
||||||
import AttachmentsPresenter from './components/AttachmentsPresenter.svelte'
|
import AttachmentsPresenter from './components/AttachmentsPresenter.svelte'
|
||||||
import AttachmentPresenter from './components/AttachmentPresenter.svelte'
|
import AttachmentPresenter from './components/AttachmentPresenter.svelte'
|
||||||
|
import AttachmentGalleryPresenter from './components/AttachmentGalleryPresenter.svelte'
|
||||||
import AttachmentDocList from './components/AttachmentDocList.svelte'
|
import AttachmentDocList from './components/AttachmentDocList.svelte'
|
||||||
import AttachmentList from './components/AttachmentList.svelte'
|
import AttachmentList from './components/AttachmentList.svelte'
|
||||||
import AttachmentRefInput from './components/AttachmentRefInput.svelte'
|
import AttachmentRefInput from './components/AttachmentRefInput.svelte'
|
||||||
@ -27,7 +28,7 @@ import Photos from './components/Photos.svelte'
|
|||||||
import FileDownload from './components/icons/FileDownload.svelte'
|
import FileDownload from './components/icons/FileDownload.svelte'
|
||||||
import { uploadFile, deleteFile } from './utils'
|
import { uploadFile, deleteFile } from './utils'
|
||||||
import attachment, { Attachment } from '@anticrm/attachment'
|
import attachment, { Attachment } from '@anticrm/attachment'
|
||||||
import { SortingOrder, SortingQuery } from '@anticrm/core'
|
import { ObjQueryType, SortingOrder, SortingQuery } from '@anticrm/core'
|
||||||
import { IntlString, Resources } from '@anticrm/platform'
|
import { IntlString, Resources } from '@anticrm/platform'
|
||||||
import preference from '@anticrm/preference'
|
import preference from '@anticrm/preference'
|
||||||
import { getClient } from '@anticrm/presentation'
|
import { getClient } from '@anticrm/presentation'
|
||||||
@ -38,6 +39,7 @@ export {
|
|||||||
Attachments,
|
Attachments,
|
||||||
AttachmentsPresenter,
|
AttachmentsPresenter,
|
||||||
AttachmentPresenter,
|
AttachmentPresenter,
|
||||||
|
AttachmentGalleryPresenter,
|
||||||
AttachmentRefInput,
|
AttachmentRefInput,
|
||||||
AttachmentList,
|
AttachmentList,
|
||||||
AttachmentDocList,
|
AttachmentDocList,
|
||||||
@ -72,18 +74,27 @@ export const sortModeToOptionObject = (sortMode: FileBrowserSortMode): SortingQu
|
|||||||
|
|
||||||
const msInDay = 24 * 60 * 60 * 1000
|
const msInDay = 24 * 60 * 60 * 1000
|
||||||
const getBeginningOfDate = (customDate?: Date) => {
|
const getBeginningOfDate = (customDate?: Date) => {
|
||||||
if (!customDate) {
|
if (customDate == null) {
|
||||||
customDate = new Date()
|
customDate = new Date()
|
||||||
}
|
}
|
||||||
customDate.setUTCHours(0, 0, 0, 0)
|
customDate.setUTCHours(0, 0, 0, 0)
|
||||||
return customDate.getTime()
|
return customDate.getTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const dateFileBrowserFilters: {
|
interface Filter {
|
||||||
id: string
|
id: string
|
||||||
label: IntlString<{}>
|
label: IntlString
|
||||||
getDate: () => any
|
}
|
||||||
}[] = [
|
|
||||||
|
interface DateFilter extends Filter {
|
||||||
|
getDate: () => ObjQueryType<number> | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TypeFilter extends Filter {
|
||||||
|
getType: () => ObjQueryType<string> | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export const dateFileBrowserFilters: DateFilter[] = [
|
||||||
{
|
{
|
||||||
id: 'dateAny',
|
id: 'dateAny',
|
||||||
label: attachment.string.FileBrowserDateFilterAny,
|
label: attachment.string.FileBrowserDateFilterAny,
|
||||||
@ -139,11 +150,7 @@ export const dateFileBrowserFilters: {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
export const fileTypeFileBrowserFilters: {
|
export const fileTypeFileBrowserFilters: TypeFilter[] = [
|
||||||
id: string
|
|
||||||
label: IntlString<{}>
|
|
||||||
getType: () => any
|
|
||||||
}[] = [
|
|
||||||
{
|
{
|
||||||
id: 'typeAny',
|
id: 'typeAny',
|
||||||
label: attachment.string.FileBrowserTypeFilterAny,
|
label: attachment.string.FileBrowserTypeFilterAny,
|
||||||
@ -181,7 +188,7 @@ export const fileTypeFileBrowserFilters: {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
export async function AddAttachmentToSaved(attach: Attachment): Promise<void> {
|
export async function AddAttachmentToSaved (attach: Attachment): Promise<void> {
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
|
|
||||||
await client.createDoc(attachment.class.SavedAttachments, preference.space.Preference, {
|
await client.createDoc(attachment.class.SavedAttachments, preference.space.Preference, {
|
||||||
@ -189,7 +196,7 @@ export async function AddAttachmentToSaved(attach: Attachment): Promise<void> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function DeleteAttachmentFromSaved(attach: Attachment): Promise<void> {
|
export async function DeleteAttachmentFromSaved (attach: Attachment): Promise<void> {
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
|
|
||||||
const current = await client.findOne(attachment.class.SavedAttachments, { attachedTo: attach._id })
|
const current = await client.findOne(attachment.class.SavedAttachments, { attachedTo: attach._id })
|
||||||
@ -202,6 +209,7 @@ export default async (): Promise<Resources> => ({
|
|||||||
component: {
|
component: {
|
||||||
AttachmentsPresenter,
|
AttachmentsPresenter,
|
||||||
AttachmentPresenter,
|
AttachmentPresenter,
|
||||||
|
AttachmentGalleryPresenter,
|
||||||
Attachments,
|
Attachments,
|
||||||
FileBrowser,
|
FileBrowser,
|
||||||
Photos
|
Photos
|
||||||
|
@ -28,6 +28,8 @@ export default mergeIds(attachmentId, attachment, {
|
|||||||
Photos: '' as IntlString,
|
Photos: '' as IntlString,
|
||||||
FileBrowser: '' as IntlString,
|
FileBrowser: '' as IntlString,
|
||||||
FileBrowserFileCounter: '' as IntlString,
|
FileBrowserFileCounter: '' as IntlString,
|
||||||
|
FileBrowserListView: '' as IntlString,
|
||||||
|
FileBrowserGridView: '' as IntlString,
|
||||||
FileBrowserFilterFrom: '' as IntlString,
|
FileBrowserFilterFrom: '' as IntlString,
|
||||||
FileBrowserFilterIn: '' as IntlString,
|
FileBrowserFilterIn: '' as IntlString,
|
||||||
FileBrowserFilterDate: '' as IntlString,
|
FileBrowserFilterDate: '' as IntlString,
|
||||||
|
@ -229,7 +229,7 @@
|
|||||||
.container {
|
.container {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: .5rem 2rem;
|
padding: 0.5rem 2rem;
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
min-width: 2.25rem;
|
min-width: 2.25rem;
|
||||||
|
Loading…
Reference in New Issue
Block a user