mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-22 03:14:40 +03:00
Signed-off-by: Ruslan Izhitsky <ruslan.izhitskiy@xored.com>
This commit is contained in:
parent
1bfce26be4
commit
53821621c6
@ -23,7 +23,8 @@ import type { TxViewlet } from '@anticrm/activity'
|
||||
|
||||
export default mergeIds(attachmentId, attachment, {
|
||||
component: {
|
||||
AttachmentPresenter: '' as AnyComponent
|
||||
AttachmentPresenter: '' as AnyComponent,
|
||||
FileBrowser: '' as AnyComponent
|
||||
},
|
||||
string: {
|
||||
AddAttachment: '' as IntlString,
|
||||
|
@ -329,6 +329,12 @@ export function createModel (builder: Builder): void {
|
||||
label: chunter.string.SavedMessages,
|
||||
icon: chunter.icon.Bookmark,
|
||||
component: chunter.component.SavedMessages
|
||||
},
|
||||
{
|
||||
id: 'fileBrowser',
|
||||
label: attachment.string.FileBrowser,
|
||||
icon: attachment.icon.FileBrowser,
|
||||
component: attachment.component.FileBrowser
|
||||
}
|
||||
],
|
||||
spaces: [
|
||||
|
75
packages/ui/src/components/DropdownLabelsIntl.svelte
Normal file
75
packages/ui/src/components/DropdownLabelsIntl.svelte
Normal file
@ -0,0 +1,75 @@
|
||||
<!--
|
||||
// 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 { IntlString, Asset } from '@anticrm/platform'
|
||||
import DropdownLabelsPopupIntl from './DropdownLabelsPopupIntl.svelte';
|
||||
import type { AnySvelteComponent, TooltipAlignment, ButtonKind, ButtonSize, DropdownIntlItem } from '../types'
|
||||
import { showPopup, Tooltip, Button, Label } from '..'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import ui from '../plugin'
|
||||
|
||||
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
||||
export let label: IntlString
|
||||
export let placeholder: IntlString | undefined = ui.string.SearchDots
|
||||
export let items: DropdownIntlItem[]
|
||||
export let selected: DropdownIntlItem['id'] | undefined = undefined
|
||||
|
||||
export let kind: ButtonKind = 'no-border'
|
||||
export let size: ButtonSize = 'small'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
export let width: string | undefined = undefined
|
||||
export let labelDirection: TooltipAlignment | undefined = undefined
|
||||
|
||||
let container: HTMLElement
|
||||
let opened: boolean = false
|
||||
let isDisabled = false
|
||||
$: isDisabled = items.length === 0
|
||||
|
||||
let selectedItem = items.find((x) => x.id === selected)
|
||||
$: selectedItem = items.find((x) => x.id === selected)
|
||||
$: if (selected === undefined && items[0] !== undefined) {
|
||||
selected = items[0].id
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const none = ui.string.None
|
||||
</script>
|
||||
|
||||
<div bind:this={container} class="min-w-0">
|
||||
<Tooltip label={label} fill={width === '100%'} direction={labelDirection}>
|
||||
<Button
|
||||
{icon}
|
||||
width={width ?? 'min-content'}
|
||||
{size} {kind} {justify}
|
||||
on:click={() => {
|
||||
if (!opened) {
|
||||
opened = true
|
||||
showPopup(DropdownLabelsPopupIntl, { placeholder, items, selected }, container, (result) => {
|
||||
if (result) {
|
||||
selected = result
|
||||
dispatch('selected', result)
|
||||
}
|
||||
opened = false
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span slot="content" style="overflow: hidden">
|
||||
{#if selectedItem} <Label label={selectedItem.label} /> {:else} <Label label={label ?? ui.string.NotSelected} />{/if}
|
||||
</span>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
71
packages/ui/src/components/DropdownLabelsPopupIntl.svelte
Normal file
71
packages/ui/src/components/DropdownLabelsPopupIntl.svelte
Normal file
@ -0,0 +1,71 @@
|
||||
<!--
|
||||
// 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 { IntlString } from '@anticrm/platform'
|
||||
import { translate } from '@anticrm/platform'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import CheckBox from './CheckBox.svelte'
|
||||
import type { DropdownIntlItem } from '../types'
|
||||
import plugin from '../plugin'
|
||||
import { Label } from '..';
|
||||
|
||||
export let placeholder: IntlString = plugin.string.SearchDots
|
||||
export let items: DropdownIntlItem[]
|
||||
export let selected: DropdownIntlItem['id'] | undefined = undefined
|
||||
|
||||
let search: string = ''
|
||||
let phTraslate: string = ''
|
||||
$: translate(placeholder, {}).then(res => { phTraslate = res })
|
||||
const dispatch = createEventDispatcher()
|
||||
let btns: HTMLButtonElement[] = []
|
||||
let searchInput: HTMLInputElement
|
||||
|
||||
const keyDown = (ev: KeyboardEvent, n: number): void => {
|
||||
if (ev.key === 'ArrowDown') {
|
||||
if (n === btns.length - 1) btns[0].focus()
|
||||
else btns[n + 1].focus()
|
||||
} else if (ev.key === 'ArrowUp') {
|
||||
if (n === 0) btns[btns.length - 1].focus()
|
||||
else btns[n - 1].focus()
|
||||
} else searchInput.focus()
|
||||
}
|
||||
|
||||
onMount(() => { if (searchInput) searchInput.focus() })
|
||||
</script>
|
||||
|
||||
<div class="selectPopup">
|
||||
<div class="header">
|
||||
<input bind:this={searchInput} type='text' bind:value={search} placeholder={phTraslate} on:input={(ev) => { }} on:change/>
|
||||
</div>
|
||||
<div class="scroll">
|
||||
<div class="box">
|
||||
{#each items.filter((x) => x.label.toLowerCase().includes(search.toLowerCase())) as item, i}
|
||||
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
|
||||
<button
|
||||
class="menu-item flex-between"
|
||||
on:mouseover={(ev) => ev.currentTarget.focus()}
|
||||
on:keydown={(ev) => keyDown(ev, i)}
|
||||
on:click={() => { dispatch('close', item.id) }}
|
||||
>
|
||||
<div class="flex-grow caption-color lines-limit-2"> <Label label={item.label} /> </div>
|
||||
{#if item.id === selected}
|
||||
<div class="check-right"><CheckBox checked primary /></div>
|
||||
{/if}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -71,6 +71,7 @@ export { default as TimeSince } from './components/TimeSince.svelte'
|
||||
export { default as Dropdown } from './components/Dropdown.svelte'
|
||||
export { default as DropdownPopup } from './components/DropdownPopup.svelte'
|
||||
export { default as DropdownLabels } from './components/DropdownLabels.svelte'
|
||||
export { default as DropdownLabelsIntl } from './components/DropdownLabelsIntl.svelte'
|
||||
export { default as ShowMore } from './components/ShowMore.svelte'
|
||||
export { default as Menu } from './components/Menu.svelte'
|
||||
export { default as TimeShiftPicker } from './components/TimeShiftPicker.svelte'
|
||||
|
@ -97,3 +97,8 @@ export interface DropdownTextItem {
|
||||
id: string
|
||||
label: string
|
||||
}
|
||||
|
||||
export interface DropdownIntlItem {
|
||||
id: string
|
||||
label: IntlString
|
||||
}
|
||||
|
@ -9,4 +9,7 @@
|
||||
<symbol id="lock" viewBox="0 0 16 16">
|
||||
<path d="M12,7.1h-0.7V5.4c0-1.8-1.5-3.3-3.3-3.3c-1.8,0-3.3,1.5-3.3,3.3v1.7H4c-0.8,0-1.5,0.7-1.5,1.5v4.1c0,0.8,0.7,1.5,1.5,1.5h8 c0.8,0,1.5-0.7,1.5-1.5V8.6C13.5,7.8,12.8,7.1,12,7.1z M5.7,5.4c0-1.2,1-2.3,2.3-2.3s2.3,1,2.3,2.3v1.7H5.7V5.4z M12.5,12.7 c0,0.3-0.2,0.5-0.5,0.5H4c-0.3,0-0.5-0.2-0.5-0.5V8.6c0-0.3,0.2-0.5,0.5-0.5h8c0.3,0,0.5,0.2,0.5,0.5V12.7z"/>
|
||||
</symbol>
|
||||
<symbol id="fileBrowser" viewBox="0 0 16 16">
|
||||
<path d="M12.6541 10.7952L14.7544 11.6213C14.8576 11.6618 14.9394 11.7434 14.9801 11.8466C15.0511 12.0264 14.9828 12.2268 14.827 12.3284L14.755 12.3656L8.35645 14.8924C8.15935 14.9703 7.94372 14.9831 7.74052 14.9309L7.62035 14.8918L1.25259 12.3653C1.1499 12.3246 1.06864 12.2432 1.02806 12.1404C0.957068 11.9607 1.02536 11.7603 1.1812 11.6587L1.25319 11.6215L3.34307 10.7962L7.06917 12.2751C7.65895 12.5091 8.31525 12.5097 8.9054 12.2766L12.6541 10.7952ZM12.6541 6.77688L14.7544 7.60289C14.8576 7.64346 14.9394 7.72508 14.9801 7.82824C15.0511 8.00803 14.9828 8.20839 14.827 8.31004L14.755 8.3472L10.6001 9.98825L9.619 10.375L8.35645 10.8741L8.317 10.886L8.23566 10.9132C8.20301 10.9215 8.17004 10.9282 8.13688 10.9331C8.12585 10.9346 8.11547 10.936 8.10507 10.9372C8.02541 10.9468 7.94422 10.9464 7.86397 10.9363L7.74052 10.9126L7.62035 10.8735L6.391 10.385L5.38907 9.98825L1.25259 8.34697C1.1499 8.30623 1.06864 8.22483 1.02806 8.12208C0.957068 7.94229 1.02536 7.74192 1.1812 7.64029L1.25319 7.60312L3.34307 6.77788L7.06917 8.25677C7.65895 8.49078 8.31525 8.4913 8.9054 8.25824L12.6541 6.77688ZM7.62186 1.06989C7.85734 0.976906 8.11932 0.976697 8.35494 1.06931L14.7544 3.58452C14.8576 3.62509 14.9394 3.70671 14.9801 3.80987C15.0612 4.01534 14.9605 4.24769 14.755 4.32884L10.6001 5.96988L8.35565 6.856L8.27468 6.88396C8.25405 6.8901 8.23326 6.89557 8.21236 6.90036C8.09824 6.92674 7.98013 6.93258 7.86397 6.91788L7.74052 6.89419L7.62035 6.8551L1.25259 4.3286C1.1499 4.28786 1.06864 4.20646 1.02806 4.10371C0.946925 3.89823 1.04772 3.66589 1.25319 3.58475L7.62186 1.06989Z"/>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 3.4 KiB |
@ -2,6 +2,7 @@
|
||||
"string": {
|
||||
"UploadDropFilesHere": "Upload or drop files here",
|
||||
"NoAttachments": "There are no attachments for this",
|
||||
"NoParticipants": "No participants added",
|
||||
"AddAttachment": "uploaded an attachment",
|
||||
"Attachments": "Attachments",
|
||||
"Photos": "Photos",
|
||||
@ -13,6 +14,28 @@
|
||||
"Size": "Size",
|
||||
"Photo": "Photo",
|
||||
"Date": "Date",
|
||||
"Name": "Name"
|
||||
"Name": "Name",
|
||||
"FileBrowser": "File browser",
|
||||
"FileBrowserFileCounter": "{results, plural, =1 {# result} other {# results}}",
|
||||
"FileBrowserFilterFrom": "From",
|
||||
"FileBrowserFilterIn": "In",
|
||||
"FileBrowserFilterDate": "Date",
|
||||
"FileBrowserFilterFileType": "File type",
|
||||
"FileBrowserSortNewest": "Newest file",
|
||||
"FileBrowserSortOldest": "Oldest file",
|
||||
"FileBrowserSortAZ": "A to Z",
|
||||
"FileBrowserSortZA": "Z to A",
|
||||
"FileBrowserDateFilterAny": "Any time",
|
||||
"FileBrowserDateFilterToday": "Today",
|
||||
"FileBrowserDateFilterYesterday": "Yesterday",
|
||||
"FileBrowserDateFilter7Days": "Last 7 days",
|
||||
"FileBrowserDateFilter30Days": "Last 30 days",
|
||||
"FileBrowserDateFilter3Months": "Last 3 months",
|
||||
"FileBrowserDateFilter12Months": "Last 12 months",
|
||||
"FileBrowserTypeFilterAny": "Any file type",
|
||||
"FileBrowserTypeFilterImages": "Images",
|
||||
"FileBrowserTypeFilterAudio": "Audio",
|
||||
"FileBrowserTypeFilterVideos": "Videos",
|
||||
"FileBrowserTypeFilterPDFs": "PDFs"
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
"string": {
|
||||
"UploadDropFilesHere": "Загрузите или перетащите файлы сюда",
|
||||
"NoAttachments": "Нет вложений",
|
||||
"NoParticipants": "Участники не добавлены",
|
||||
"AddAttachment": "загрузил вложение",
|
||||
"Attachments": "Вложения",
|
||||
"Photos": "Фотографии",
|
||||
@ -13,6 +14,28 @@
|
||||
"Size": "Размер",
|
||||
"Photo": "Фотография",
|
||||
"Date": "Дата",
|
||||
"Name": "Название"
|
||||
"Name": "Название",
|
||||
"FileBrowser": "Браузер файлов",
|
||||
"FileBrowserFileCounter": "{results, plural, =1 {# результат} =2 {# результата} =3 {# результата} =4 {# результата} other {# результатов}}",
|
||||
"FileBrowserFilterFrom": "От",
|
||||
"FileBrowserFilterIn": "В",
|
||||
"FileBrowserFilterDate": "Дата",
|
||||
"FileBrowserFilterFileType": "Тип файла",
|
||||
"FileBrowserSortNewest": "Самый новый файл",
|
||||
"FileBrowserSortOldest": "Самый старый файл",
|
||||
"FileBrowserSortAZ": "От А до Я",
|
||||
"FileBrowserSortZA": "От Я до А",
|
||||
"FileBrowserDateFilterAny": "В любое время",
|
||||
"FileBrowserDateFilterToday": "Сегодня",
|
||||
"FileBrowserDateFilterYesterday": "Вчера",
|
||||
"FileBrowserDateFilter7Days": "Последние 7 дней",
|
||||
"FileBrowserDateFilter30Days": "Последние 30 дней",
|
||||
"FileBrowserDateFilter3Months": "Последние 3 месяца",
|
||||
"FileBrowserDateFilter12Months": "Последние 12 месяцев",
|
||||
"FileBrowserTypeFilterAny": "Любой тип файла",
|
||||
"FileBrowserTypeFilterImages": "Изображения",
|
||||
"FileBrowserTypeFilterAudio": "Звук",
|
||||
"FileBrowserTypeFilterVideos": "Видео",
|
||||
"FileBrowserTypeFilterPDFs": "PDF-файлы"
|
||||
}
|
||||
}
|
@ -18,7 +18,8 @@ import attachment, { attachmentId } from '@anticrm/attachment'
|
||||
|
||||
const icons = require('../assets/icons.svg') as string // eslint-disable-line
|
||||
loadMetadata(attachment.icon, {
|
||||
Attachment: `${icons}#chunter`
|
||||
Attachment: `${icons}#chunter`,
|
||||
FileBrowser: `${icons}#fileBrowser`
|
||||
})
|
||||
|
||||
addStringsLoader(attachmentId, async (lang: string) => await import(`../lang/${lang}.json`))
|
||||
|
@ -36,6 +36,7 @@
|
||||
"@anticrm/ui": "~0.6.0",
|
||||
"@anticrm/presentation": "~0.6.2",
|
||||
"@anticrm/attachment": "~0.6.1",
|
||||
"@anticrm/contact": "~0.6.5",
|
||||
"@anticrm/core": "~0.6.16",
|
||||
"@anticrm/view": "~0.6.0",
|
||||
"@anticrm/view-resources": "~0.6.0",
|
||||
|
@ -29,7 +29,7 @@
|
||||
const client = getClient()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
async function fileSelected() {
|
||||
async function fileSelected () {
|
||||
const list = inputFile.files
|
||||
if (list === null || list.length === 0) return
|
||||
|
||||
@ -43,7 +43,7 @@
|
||||
dispatch('attached')
|
||||
}
|
||||
|
||||
function openFile() {
|
||||
function openFile () {
|
||||
inputFile.click()
|
||||
}
|
||||
</script>
|
||||
|
@ -12,7 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { Attachment } from '@anticrm/attachment'
|
||||
import type { Doc } from '@anticrm/core'
|
||||
@ -29,9 +28,13 @@
|
||||
|
||||
function updateQuery (value: Doc & { attachments?: number }): void {
|
||||
if (value && value.attachments && value.attachments > 0) {
|
||||
query.query(attachment.class.Attachment, {
|
||||
attachedTo: value._id
|
||||
}, (res) => attachments = res)
|
||||
query.query(
|
||||
attachment.class.Attachment,
|
||||
{
|
||||
attachedTo: value._id
|
||||
},
|
||||
(res) => (attachments = res)
|
||||
)
|
||||
} else {
|
||||
attachments = []
|
||||
}
|
||||
|
@ -28,7 +28,7 @@
|
||||
|
||||
const client = getClient()
|
||||
|
||||
async function fileDrop(e: DragEvent) {
|
||||
async function fileDrop (e: DragEvent) {
|
||||
dragover = false
|
||||
|
||||
if (canDrop && !canDrop(e)) {
|
||||
|
@ -12,7 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { Attachment } from '@anticrm/attachment'
|
||||
import AttachmentPreview from './AttachmentPreview.svelte'
|
||||
@ -21,9 +20,9 @@
|
||||
</script>
|
||||
|
||||
{#if attachments.length}
|
||||
<div class='container'>
|
||||
<div class="container">
|
||||
{#each attachments as attachment}
|
||||
<div class='item'>
|
||||
<div class="item">
|
||||
<AttachmentPreview value={attachment} />
|
||||
</div>
|
||||
{/each}
|
||||
|
@ -13,7 +13,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import type { Ref, Doc } from '@anticrm/core'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
@ -22,13 +21,12 @@
|
||||
|
||||
export let objectId: Ref<Doc>
|
||||
export let attachments: number
|
||||
|
||||
</script>
|
||||
|
||||
<Table
|
||||
<Table
|
||||
_class={attachment.class.Attachment}
|
||||
config={['', 'lastModified']}
|
||||
options={ {} }
|
||||
query={ { attachedTo: objectId } }
|
||||
options={{}}
|
||||
query={{ attachedTo: objectId }}
|
||||
loadingProps={{ length: attachments }}
|
||||
/>
|
||||
|
@ -33,7 +33,6 @@
|
||||
let inputFile: HTMLInputElement
|
||||
let loading = 0
|
||||
let dragover = false
|
||||
|
||||
</script>
|
||||
|
||||
<div class="attachments-container">
|
||||
@ -66,7 +65,8 @@
|
||||
config={['', 'lastModified']}
|
||||
options={{}}
|
||||
query={{ attachedTo: objectId }}
|
||||
loadingProps={{ length: attachments ?? 0 }} />
|
||||
loadingProps={{ length: attachments ?? 0 }}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@ -93,5 +93,4 @@
|
||||
border-style: solid;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@ -13,7 +13,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import type { Doc } from '@anticrm/core'
|
||||
import { Tooltip, IconAttachment } from '@anticrm/ui'
|
||||
@ -26,9 +25,13 @@
|
||||
</script>
|
||||
|
||||
{#if value && value.attachments && value.attachments > 0}
|
||||
<Tooltip label={attachment.string.Attachments} component={AttachmentPopup} props={{ objectId: value._id, attachments: value.attachments }}>
|
||||
<Tooltip
|
||||
label={attachment.string.Attachments}
|
||||
component={AttachmentPopup}
|
||||
props={{ objectId: value._id, attachments: value.attachments }}
|
||||
>
|
||||
<div class="sm-tool-icon ml-1 mr-1">
|
||||
<span class="icon"><IconAttachment {size}/></span>
|
||||
<span class="icon"><IconAttachment {size} /></span>
|
||||
{#if showCounter}
|
||||
{value.attachments}
|
||||
{/if}
|
||||
|
@ -12,7 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import type { Attachment } from '@anticrm/attachment'
|
||||
import { getFileUrl } from '@anticrm/presentation'
|
||||
@ -23,8 +22,8 @@
|
||||
export let value: Attachment
|
||||
|
||||
let time = 0
|
||||
let duration = Number.POSITIVE_INFINITY
|
||||
let paused = true
|
||||
let duration = Number.POSITIVE_INFINITY
|
||||
let paused = true
|
||||
|
||||
function buttonClick () {
|
||||
paused = !paused
|
||||
@ -33,16 +32,16 @@
|
||||
$: icon = !paused ? Pause : Play
|
||||
</script>
|
||||
|
||||
<div class='container flex-between'>
|
||||
<div class="container flex-between">
|
||||
<div>
|
||||
<CircleButton size='x-large' on:click={buttonClick} {icon} />
|
||||
<CircleButton size="x-large" on:click={buttonClick} {icon} />
|
||||
</div>
|
||||
<div class='w-full ml-4'>
|
||||
<div class="w-full ml-4">
|
||||
<Progress bind:value={time} max={Number.isFinite(duration) ? duration : 100} editable />
|
||||
</div>
|
||||
</div>
|
||||
<audio bind:duration bind:currentTime={time} bind:paused>
|
||||
<source src={getFileUrl(value.file)} type={value.type}>
|
||||
<source src={getFileUrl(value.file)} type={value.type} />
|
||||
</audio>
|
||||
|
||||
<style lang="scss">
|
||||
@ -53,4 +52,4 @@
|
||||
width: 20rem;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
375
plugins/attachment-resources/src/components/FileBrowser.svelte
Normal file
375
plugins/attachment-resources/src/components/FileBrowser.svelte
Normal file
@ -0,0 +1,375 @@
|
||||
<!--
|
||||
// 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 contact, { Employee } from '@anticrm/contact'
|
||||
import { EmployeeAccount } from '@anticrm/contact'
|
||||
import { Doc, getCurrentAccount, Ref, SortingOrder, SortingQuery, Space } from '@anticrm/core'
|
||||
import { IntlString } from '@anticrm/platform'
|
||||
import { getClient, UserBoxList } from '@anticrm/presentation'
|
||||
import { DropdownLabelsIntl, IconMoreV, Label, Menu as UIMenu, showPopup } from '@anticrm/ui'
|
||||
import { Menu } from '@anticrm/view-resources'
|
||||
import { AttachmentPresenter } from '..'
|
||||
import attachment from '../plugin'
|
||||
|
||||
enum SortMode {
|
||||
NewestFile,
|
||||
OldestFile,
|
||||
AscendingAlphabetical,
|
||||
DescendingAlphabetical
|
||||
}
|
||||
|
||||
const msInDay = 24 * 60 * 60 * 1000
|
||||
const getBeginningOfToday = () => {
|
||||
const date = new Date()
|
||||
date.setUTCHours(0, 0, 0, 0)
|
||||
return date.getTime()
|
||||
}
|
||||
const dateObjects = [
|
||||
{
|
||||
id: 'dateAny',
|
||||
label: attachment.string.FileBrowserDateFilterAny,
|
||||
getDate: () => {
|
||||
return undefined
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'dateToday',
|
||||
label: attachment.string.FileBrowserDateFilterToday,
|
||||
getDate: () => {
|
||||
return { $gte: getBeginningOfToday() }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'dateYesterday',
|
||||
label: attachment.string.FileBrowserDateFilterYesterday,
|
||||
getDate: () => {
|
||||
return { $gte: getBeginningOfToday() - msInDay, $lt: getBeginningOfToday() }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'date7Days',
|
||||
label: attachment.string.FileBrowserDateFilter7Days,
|
||||
getDate: () => {
|
||||
return { $gte: getBeginningOfToday() - msInDay * 6 }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'date30Days',
|
||||
label: attachment.string.FileBrowserDateFilter30Days,
|
||||
getDate: () => {
|
||||
return { $gte: getBeginningOfToday() - msInDay * 29 }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'date3Months',
|
||||
label: attachment.string.FileBrowserDateFilter3Months,
|
||||
getDate: () => {
|
||||
return { $gte: getBeginningOfToday() - msInDay * 90 }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'date12Months',
|
||||
label: attachment.string.FileBrowserDateFilter12Months,
|
||||
getDate: () => {
|
||||
return { $gte: getBeginningOfToday() - msInDay * 364 }
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const fileTypeObjects = [
|
||||
{
|
||||
id: 'typeAny',
|
||||
label: attachment.string.FileBrowserTypeFilterAny,
|
||||
getType: () => {
|
||||
return undefined
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'typeImage',
|
||||
label: attachment.string.FileBrowserTypeFilterImages,
|
||||
getType: () => {
|
||||
return { $like: '%image/%' }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'typeAudio',
|
||||
label: attachment.string.FileBrowserTypeFilterAudio,
|
||||
getType: () => {
|
||||
return { $like: '%audio/%' }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'typeVideo',
|
||||
label: attachment.string.FileBrowserTypeFilterVideos,
|
||||
getType: () => {
|
||||
return { $like: '%video/%' }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'typePDF',
|
||||
label: attachment.string.FileBrowserTypeFilterPDFs,
|
||||
getType: () => {
|
||||
return 'application/pdf'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const client = getClient()
|
||||
export let space: Space | undefined
|
||||
const currentUser = getCurrentAccount() as EmployeeAccount
|
||||
let participants: Ref<Employee>[] = [currentUser.employee]
|
||||
const assignee: Ref<Employee> | null = null
|
||||
|
||||
let attachments: Attachment[] = []
|
||||
let selectedFileNumber: number | undefined
|
||||
|
||||
let selectedSort: SortMode = SortMode.NewestFile
|
||||
let selectedDateId = 'dateAny'
|
||||
let selectedFileTypeId = 'typeAny'
|
||||
|
||||
const showFileMenu = async (ev: MouseEvent, object: Doc, fileNumber: number): Promise<void> => {
|
||||
selectedFileNumber = fileNumber
|
||||
showPopup(Menu, { object }, ev.target as HTMLElement, () => {
|
||||
selectedFileNumber = undefined
|
||||
})
|
||||
}
|
||||
|
||||
const showSortMenu = async (ev: Event): Promise<void> => {
|
||||
showPopup(
|
||||
UIMenu,
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
label: sortModeToString(SortMode.NewestFile),
|
||||
action: () => {
|
||||
selectedSort = SortMode.NewestFile
|
||||
}
|
||||
},
|
||||
{
|
||||
label: sortModeToString(SortMode.OldestFile),
|
||||
action: () => {
|
||||
selectedSort = SortMode.OldestFile
|
||||
}
|
||||
},
|
||||
{
|
||||
label: sortModeToString(SortMode.AscendingAlphabetical),
|
||||
action: () => {
|
||||
selectedSort = SortMode.AscendingAlphabetical
|
||||
}
|
||||
},
|
||||
{
|
||||
label: sortModeToString(SortMode.DescendingAlphabetical),
|
||||
action: () => {
|
||||
selectedSort = SortMode.DescendingAlphabetical
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
ev.target as HTMLElement
|
||||
)
|
||||
}
|
||||
|
||||
const sortModeToString = (sortMode: SortMode): IntlString<{}> => {
|
||||
switch (sortMode) {
|
||||
case SortMode.NewestFile:
|
||||
return attachment.string.FileBrowserSortNewest
|
||||
case SortMode.OldestFile:
|
||||
return attachment.string.FileBrowserSortOldest
|
||||
case SortMode.AscendingAlphabetical:
|
||||
return attachment.string.FileBrowserSortAZ
|
||||
case SortMode.DescendingAlphabetical:
|
||||
return attachment.string.FileBrowserSortZA
|
||||
}
|
||||
}
|
||||
|
||||
const sortModeToOptionObject = (sortMode: SortMode): SortingQuery<Attachment> => {
|
||||
switch (sortMode) {
|
||||
case SortMode.NewestFile:
|
||||
return { modifiedOn: SortingOrder.Descending }
|
||||
case SortMode.OldestFile:
|
||||
return { modifiedOn: SortingOrder.Ascending }
|
||||
case SortMode.AscendingAlphabetical:
|
||||
return { name: SortingOrder.Ascending }
|
||||
case SortMode.DescendingAlphabetical:
|
||||
return { name: SortingOrder.Descending }
|
||||
}
|
||||
}
|
||||
|
||||
$: fetch(selectedSort, selectedFileTypeId, selectedDateId)
|
||||
|
||||
async function fetch (selectedSort_: SortMode, selectedFileTypeId_: string, selectedDateId_: string) {
|
||||
const spaceQuery = space && { space: space._id }
|
||||
const fileType = fileTypeObjects.find((o) => o.id === selectedFileTypeId_)?.getType()
|
||||
const typeQuery = fileType && { type: fileType }
|
||||
const date = dateObjects.find((o) => o.id === selectedDateId_)?.getDate()
|
||||
const dateQuery = date && { modifiedOn: date }
|
||||
|
||||
attachments = await client.findAll(
|
||||
attachment.class.Attachment,
|
||||
{ ...spaceQuery, ...typeQuery, ...dateQuery },
|
||||
{
|
||||
sort: sortModeToOptionObject(selectedSort_)
|
||||
}
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="ac-header full divide">
|
||||
<div class="ac-header__wrap-title">
|
||||
<span class="ac-header__title"><Label label={attachment.string.FileBrowser} /></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filterBlockContainer">
|
||||
<div class="simpleFilterButton">
|
||||
<UserBoxList
|
||||
_class={contact.class.Employee}
|
||||
items={participants}
|
||||
label={attachment.string.FileBrowserFilterFrom}
|
||||
on:update={(evt) => {
|
||||
participants = evt.detail
|
||||
}}
|
||||
noItems={attachment.string.NoParticipants}
|
||||
/>
|
||||
</div>
|
||||
<!-- TODO: wait for In filter -->
|
||||
<!-- <div class="simpleFilterButton">
|
||||
<UserBox _class={contact.class.Employee} label={attachment.string.FileBrowserFilterIn} bind:value={assignee} />
|
||||
</div> -->
|
||||
<div class="simpleFilterButton">
|
||||
<DropdownLabelsIntl
|
||||
items={dateObjects}
|
||||
placeholder={attachment.string.FileBrowserFilterDate}
|
||||
label={attachment.string.FileBrowserFilterDate}
|
||||
bind:selected={selectedDateId}
|
||||
/>
|
||||
</div>
|
||||
<div class="simpleFilterButton">
|
||||
<DropdownLabelsIntl
|
||||
items={fileTypeObjects}
|
||||
placeholder={attachment.string.FileBrowserFilterFileType}
|
||||
label={attachment.string.FileBrowserFilterFileType}
|
||||
bind:selected={selectedFileTypeId}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="group">
|
||||
<div class="groupHeader">
|
||||
<div class="eGroupHeaderCount">
|
||||
<Label label={attachment.string.FileBrowserFileCounter} params={{ results: attachments?.length ?? 0 }} />
|
||||
</div>
|
||||
<div class="eGroupHeaderSortMenu" on:click={(event) => showSortMenu(event)}>
|
||||
{'Sort: '}
|
||||
<Label label={sortModeToString(selectedSort)} />
|
||||
</div>
|
||||
</div>
|
||||
{#if attachments?.length}
|
||||
<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}>
|
||||
<div id="context-menu" class="eAttachmentRowMenu" on:click={(event) => showFileMenu(event, attachment, i)}>
|
||||
<IconMoreV size={'small'} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex-between attachmentRow">
|
||||
<Label label={attachment.string.NoFiles} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.group {
|
||||
border: 1px solid var(--theme-bg-focused-border);
|
||||
border-radius: 1rem;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.groupHeader {
|
||||
margin: 0 1.5rem 0.75rem 1.5rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.eGroupHeaderCount {
|
||||
font-size: 0.75rem;
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
|
||||
.eGroupHeaderSortMenu {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.attachmentRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-right: 1rem;
|
||||
margin: 0 1.5rem;
|
||||
padding: 0.25rem 0;
|
||||
|
||||
.eAttachmentRowActions {
|
||||
visibility: hidden;
|
||||
border: 1px solid var(--theme-bg-focused-border);
|
||||
padding: 0.2rem;
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
.eAttachmentRowMenu {
|
||||
visibility: hidden;
|
||||
opacity: 0.6;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.eAttachmentRowActions {
|
||||
visibility: visible;
|
||||
}
|
||||
.eAttachmentRowMenu {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
&.fixed {
|
||||
.eAttachmentRowActions {
|
||||
visibility: visible;
|
||||
}
|
||||
.eAttachmentRowMenu {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filterBlockContainer {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.simpleFilterButton {
|
||||
min-width: 4rem;
|
||||
max-width: 12rem;
|
||||
margin-left: 0.75rem;
|
||||
}
|
||||
</style>
|
@ -12,14 +12,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import type { Attachment } from "@anticrm/attachment"
|
||||
import type { TxCreateDoc } from "@anticrm/core"
|
||||
import type { Attachment } from '@anticrm/attachment'
|
||||
import type { TxCreateDoc } from '@anticrm/core'
|
||||
import { TxProcessor } from '@anticrm/core'
|
||||
import AttachmentPresenter from "../AttachmentPresenter.svelte"
|
||||
import AttachmentPresenter from '../AttachmentPresenter.svelte'
|
||||
|
||||
export let tx: TxCreateDoc<Attachment>
|
||||
</script>
|
||||
|
||||
<AttachmentPresenter value={TxProcessor.createDoc2Doc(tx)}/>
|
||||
<AttachmentPresenter value={TxProcessor.createDoc2Doc(tx)} />
|
||||
|
@ -22,17 +22,28 @@ import AttachmentList from './components/AttachmentList.svelte'
|
||||
import AttachmentRefInput from './components/AttachmentRefInput.svelte'
|
||||
import TxAttachmentCreate from './components/activity/TxAttachmentCreate.svelte'
|
||||
import Attachments from './components/Attachments.svelte'
|
||||
import FileBrowser from './components/FileBrowser.svelte'
|
||||
import Photos from './components/Photos.svelte'
|
||||
import { Resources } from '@anticrm/platform'
|
||||
import { uploadFile, deleteFile } from './utils'
|
||||
|
||||
export { AddAttachment, AttachmentDroppable, Attachments, AttachmentsPresenter, AttachmentPresenter, AttachmentRefInput, AttachmentList, AttachmentDocList }
|
||||
export {
|
||||
AddAttachment,
|
||||
AttachmentDroppable,
|
||||
Attachments,
|
||||
AttachmentsPresenter,
|
||||
AttachmentPresenter,
|
||||
AttachmentRefInput,
|
||||
AttachmentList,
|
||||
AttachmentDocList
|
||||
}
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
component: {
|
||||
AttachmentsPresenter,
|
||||
AttachmentPresenter,
|
||||
Attachments,
|
||||
FileBrowser,
|
||||
Photos
|
||||
},
|
||||
activity: {
|
||||
|
@ -24,6 +24,28 @@ export default mergeIds(attachmentId, attachment, {
|
||||
NoAttachments: '' as IntlString,
|
||||
UploadDropFilesHere: '' as IntlString,
|
||||
Attachments: '' as IntlString,
|
||||
Photos: '' as IntlString
|
||||
Photos: '' as IntlString,
|
||||
FileBrowser: '' as IntlString,
|
||||
FileBrowserFileCounter: '' as IntlString,
|
||||
FileBrowserFilterFrom: '' as IntlString,
|
||||
FileBrowserFilterIn: '' as IntlString,
|
||||
FileBrowserFilterDate: '' as IntlString,
|
||||
FileBrowserFilterFileType: '' as IntlString,
|
||||
FileBrowserSortNewest: '' as IntlString,
|
||||
FileBrowserSortOldest: '' as IntlString,
|
||||
FileBrowserSortAZ: '' as IntlString,
|
||||
FileBrowserSortZA: '' as IntlString,
|
||||
FileBrowserDateFilterAny: '' as IntlString,
|
||||
FileBrowserDateFilterToday: '' as IntlString,
|
||||
FileBrowserDateFilterYesterday: '' as IntlString,
|
||||
FileBrowserDateFilter7Days: '' as IntlString,
|
||||
FileBrowserDateFilter30Days: '' as IntlString,
|
||||
FileBrowserDateFilter3Months: '' as IntlString,
|
||||
FileBrowserDateFilter12Months: '' as IntlString,
|
||||
FileBrowserTypeFilterAny: '' as IntlString,
|
||||
FileBrowserTypeFilterImages: '' as IntlString,
|
||||
FileBrowserTypeFilterAudio: '' as IntlString,
|
||||
FileBrowserTypeFilterVideos: '' as IntlString,
|
||||
FileBrowserTypeFilterPDFs: '' as IntlString
|
||||
}
|
||||
})
|
||||
|
@ -20,7 +20,7 @@ import { getMetadata, setPlatformStatus, unknownError } from '@anticrm/platform'
|
||||
|
||||
import attachment from './plugin'
|
||||
|
||||
export async function uploadFile(file: File, opts?: { space: Ref<Space>; attachedTo: Ref<Doc> }): Promise<string> {
|
||||
export async function uploadFile (file: File, opts?: { space: Ref<Space>, attachedTo: Ref<Doc> }): Promise<string> {
|
||||
const uploadUrl = getMetadata(login.metadata.UploadUrl)
|
||||
|
||||
if (uploadUrl === undefined) {
|
||||
@ -58,7 +58,7 @@ export async function uploadFile(file: File, opts?: { space: Ref<Space>; attache
|
||||
return await resp.text()
|
||||
}
|
||||
|
||||
export async function deleteFile(id: string): Promise<void> {
|
||||
export async function deleteFile (id: string): Promise<void> {
|
||||
const uploadUrl = getMetadata(login.metadata.UploadUrl)
|
||||
|
||||
const url = `${uploadUrl as string}?file=${id}`
|
||||
@ -74,10 +74,10 @@ export async function deleteFile(id: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
export async function createAttachments(
|
||||
export async function createAttachments (
|
||||
client: Client,
|
||||
list: FileList,
|
||||
attachTo: { objectClass: Ref<Class<Doc>>; space: Ref<Space>; objectId: Ref<Doc> }
|
||||
attachTo: { objectClass: Ref<Class<Doc>>, space: Ref<Space>, objectId: Ref<Doc> }
|
||||
) {
|
||||
const { objectClass, objectId, space } = attachTo
|
||||
try {
|
||||
@ -100,7 +100,7 @@ export async function createAttachments(
|
||||
}
|
||||
}
|
||||
|
||||
export function getType(type: string): 'image' | 'video' | 'audio' | 'pdf' | 'other' {
|
||||
export function getType (type: string): 'image' | 'video' | 'audio' | 'pdf' | 'other' {
|
||||
if (type.startsWith('image/')) {
|
||||
return 'image'
|
||||
}
|
||||
|
@ -47,7 +47,8 @@ export default plugin(attachmentId, {
|
||||
AttachmentsPresenter: '' as AnyComponent
|
||||
},
|
||||
icon: {
|
||||
Attachment: '' as Asset
|
||||
Attachment: '' as Asset,
|
||||
FileBrowser: '' as Asset
|
||||
},
|
||||
class: {
|
||||
Attachment: '' as Ref<Class<Attachment>>,
|
||||
@ -60,6 +61,7 @@ export default plugin(attachmentId, {
|
||||
string: {
|
||||
Files: '' as IntlString,
|
||||
NoFiles: '' as IntlString,
|
||||
NoParticipants: '' as IntlString,
|
||||
ShowMoreAttachments: '' as IntlString
|
||||
}
|
||||
})
|
||||
|
@ -20,14 +20,14 @@
|
||||
import { Doc, SortingOrder } from '@anticrm/core'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import { Menu } from '@anticrm/view-resources'
|
||||
import { showPopup, IconMoreV, Label } from '@anticrm/ui'
|
||||
import { getCurrentLocation, showPopup, IconMoreV, Label, navigate } from '@anticrm/ui'
|
||||
|
||||
export let channel: ChunterSpace | undefined
|
||||
|
||||
const query = createQuery()
|
||||
let visibleAttachments: Attachment[] | undefined
|
||||
let totalAttachments = 0
|
||||
let attachmentsLimit: number | undefined = 5
|
||||
const ATTACHEMNTS_LIMIT = 5
|
||||
let selectedRowNumber: number | undefined
|
||||
const sort = { modifiedOn: SortingOrder.Descending }
|
||||
|
||||
@ -48,14 +48,10 @@
|
||||
visibleAttachments = res
|
||||
totalAttachments = res.total
|
||||
},
|
||||
attachmentsLimit
|
||||
? {
|
||||
limit: attachmentsLimit,
|
||||
sort: sort
|
||||
}
|
||||
: {
|
||||
sort: sort
|
||||
}
|
||||
{
|
||||
limit: ATTACHEMNTS_LIMIT,
|
||||
sort: sort
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
@ -75,12 +71,13 @@
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{#if attachmentsLimit && visibleAttachments.length < totalAttachments}
|
||||
{#if visibleAttachments.length < totalAttachments}
|
||||
<div
|
||||
class="showMoreAttachmentsButton"
|
||||
on:click={() => {
|
||||
// TODO: replace this with an external attachments page
|
||||
attachmentsLimit = undefined
|
||||
const loc = getCurrentLocation()
|
||||
loc.path[2] = 'fileBrowser'
|
||||
navigate(loc)
|
||||
}}
|
||||
>
|
||||
<Label label={attachment.string.ShowMoreAttachments} />
|
||||
|
@ -55,30 +55,30 @@
|
||||
{#if channel}
|
||||
<div class="flex-col flex-gap-3">
|
||||
{#if isCommonChannel(channel)}
|
||||
<EditBox
|
||||
label={chunter.string.Topic}
|
||||
bind:value={channel.topic}
|
||||
placeholder={chunter.string.Topic}
|
||||
maxWidth="39rem"
|
||||
focus
|
||||
on:change={onTopicChange}
|
||||
/>
|
||||
<EditBox
|
||||
label={chunter.string.ChannelDescription}
|
||||
bind:value={channel.description}
|
||||
placeholder={chunter.string.ChannelDescription}
|
||||
maxWidth="39rem"
|
||||
focus
|
||||
on:change={onDescriptionChange}
|
||||
/>
|
||||
<Button
|
||||
label={chunter.string.LeaveChannel}
|
||||
justify={'left'}
|
||||
size={'x-large'}
|
||||
on:click={() => {
|
||||
leaveChannel()
|
||||
}}
|
||||
/>
|
||||
<EditBox
|
||||
label={chunter.string.Topic}
|
||||
bind:value={channel.topic}
|
||||
placeholder={chunter.string.Topic}
|
||||
maxWidth="39rem"
|
||||
focus
|
||||
on:change={onTopicChange}
|
||||
/>
|
||||
<EditBox
|
||||
label={chunter.string.ChannelDescription}
|
||||
bind:value={channel.description}
|
||||
placeholder={chunter.string.ChannelDescription}
|
||||
maxWidth="39rem"
|
||||
focus
|
||||
on:change={onDescriptionChange}
|
||||
/>
|
||||
<Button
|
||||
label={chunter.string.LeaveChannel}
|
||||
justify={'left'}
|
||||
size={'x-large'}
|
||||
on:click={() => {
|
||||
leaveChannel()
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<EditChannelDescriptionAttachments {channel} />
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user