Collection presenter (#1798)

Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
Denis Bykhov 2022-05-19 13:14:05 +06:00 committed by GitHub
parent d277134032
commit 6152d55eb1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 97 additions and 130 deletions

View File

@ -64,6 +64,10 @@ export function createModel (builder: Builder): void {
presenter: attachment.component.AttachmentPresenter
})
builder.mixin(attachment.class.Attachment, core.class.Class, view.mixin.CollectionPresenter, {
presenter: attachment.component.AttachmentsPresenter
})
builder.mixin(attachment.class.Attachment, core.class.Class, view.mixin.CollectionEditor, {
editor: attachment.component.Attachments
})

View File

@ -105,7 +105,7 @@ export class TCard extends TTask implements Card {
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)
comments?: number
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments)
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files)
attachments?: number
@Prop(TypeRef(contact.class.Employee), board.string.Assignee)

View File

@ -72,7 +72,7 @@ export class TEvent extends TAttachedDoc implements Event {
@Prop(TypeDate(true), calendar.string.DueTo)
dueDate!: Timestamp
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments)
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files)
attachments?: number
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)

View File

@ -79,7 +79,7 @@ export class TChunterMessage extends TAttachedDoc implements ChunterMessage {
@Index(IndexKind.FullText)
content!: string
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments)
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files)
attachments?: number
@Prop(TypeRef(core.class.Account), chunter.string.CreateBy)
@ -119,7 +119,7 @@ export class TComment extends TAttachedDoc implements Comment {
@Index(IndexKind.FullText)
message!: string
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments)
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files)
attachments?: number
}
@ -373,6 +373,10 @@ export function createModel (builder: Builder): void {
presenter: chunter.component.CommentPresenter
})
builder.mixin(chunter.class.Comment, core.class.Class, view.mixin.CollectionPresenter, {
presenter: chunter.component.CommentsPresenter
})
builder.createDoc(
activity.class.TxViewlet,
core.space.Model,

View File

@ -59,7 +59,7 @@ export class TContact extends TDoc implements Contact {
@Prop(Collection(contact.class.Channel), contact.string.ContactInfo)
channels?: number
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments)
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files)
attachments?: number
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)
@ -164,12 +164,7 @@ export function createModel (builder: Builder): void {
'',
{ key: '$lookup._class.label', label: contact.string.TypeLabel },
'city',
{
key: '',
presenter: attachment.component.AttachmentsPresenter,
label: attachment.string.Files,
sortingKey: 'attachments'
},
'attachments',
'modifiedOn',
{ key: '', presenter: view.component.RolePresenter, label: view.string.Role },
'$lookup.channels'

View File

@ -68,7 +68,7 @@ export class TMessage extends TAttachedDoc implements Message {
@Prop(TypeBoolean(), gmail.string.Incoming)
incoming!: boolean
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments)
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files)
attachments?: number
@Prop(TypeTimestamp(), core.string.Modified)
@ -99,7 +99,7 @@ export class TNewMessage extends TDoc implements NewMessage {
@Prop(ArrOf(TypeString()), gmail.string.Copy)
copy?: string[]
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments)
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files)
attachments?: number
}

View File

@ -50,7 +50,7 @@ export class TProduct extends TAttachedDoc implements Product {
@Prop(Collection(inventory.class.Variant), inventory.string.Variants)
variants?: number
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments)
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files)
attachments?: number
}

View File

@ -44,7 +44,7 @@ export class TLead extends TTask implements Lead {
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)
comments?: number
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments)
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files)
attachments?: number
@Prop(TypeRef(contact.class.Employee), lead.string.Assignee)
@ -139,18 +139,8 @@ export function createModel (builder: Builder): void {
'$lookup.attachedTo',
'$lookup.state',
'$lookup.doneState',
{
key: '',
presenter: attachment.component.AttachmentsPresenter,
label: attachment.string.Files,
sortingKey: 'attachments'
},
{
key: '',
presenter: chunter.component.CommentsPresenter,
label: chunter.string.Comments,
sortingKey: 'comments'
},
'attachments',
'comments',
'modifiedOn',
'$lookup.attachedTo.$lookup.channels'
]

View File

@ -53,7 +53,7 @@ export class TVacancy extends TSpaceWithStates implements Vacancy {
@Index(IndexKind.FullText)
fullDescription?: string
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments)
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files)
attachments?: number
@Prop(TypeDate(), recruit.string.Due, recruit.icon.Calendar)
@ -81,7 +81,7 @@ export class TCandidate extends TPerson implements Candidate {
@Index(IndexKind.FullText)
title?: string
@Prop(Collection(recruit.class.Applicant), recruit.string.Applications)
@Prop(Collection(recruit.class.Applicant), recruit.string.Applications, undefined, recruit.string.ApplicationsShort)
applications?: number
@Prop(TypeBoolean(), recruit.string.Onsite)
@ -112,7 +112,7 @@ export class TApplicant extends TTask implements Applicant {
@Prop(TypeRef(recruit.class.Vacancy), recruit.string.Vacancy)
declare space: Ref<Vacancy>
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments)
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files)
attachments?: number
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)
@ -240,24 +240,9 @@ export function createModel (builder: Builder): void {
'',
'title',
'city',
{
key: '',
presenter: recruit.component.ApplicationsPresenter,
label: recruit.string.ApplicationsShort,
sortingKey: 'applications'
},
{
key: '',
presenter: attachment.component.AttachmentsPresenter,
label: attachment.string.Files,
sortingKey: 'attachments'
},
{
key: '',
presenter: chunter.component.CommentsPresenter,
label: chunter.string.Comments,
sortingKey: 'comments'
},
'applications',
'attachments',
'comments',
{
// key: '$lookup.skills', // Required, since presenter require list of tag references or '' and TagsPopupPresenter
key: '',
@ -284,18 +269,8 @@ export function createModel (builder: Builder): void {
'$lookup.assignee',
'$lookup.state',
'$lookup.doneState',
{
key: '',
presenter: attachment.component.AttachmentsPresenter,
label: attachment.string.Files,
sortingKey: 'attachments'
},
{
key: '',
presenter: chunter.component.CommentsPresenter,
label: chunter.string.Comments,
sortingKey: 'comments'
},
'attachments',
'comments',
'modifiedOn',
'$lookup.attachedTo.$lookup.channels'
]
@ -339,6 +314,10 @@ export function createModel (builder: Builder): void {
presenter: recruit.component.ApplicationPresenter
})
builder.mixin(recruit.class.Applicant, core.class.Class, view.mixin.CollectionPresenter, {
presenter: recruit.component.ApplicationsPresenter
})
builder.mixin(recruit.class.Vacancy, core.class.Class, view.mixin.AttributePresenter, {
presenter: recruit.component.VacancyPresenter
})

View File

@ -41,7 +41,7 @@ export class TOpinion extends TAttachedDoc implements Opinion {
@Prop(TypeRef(recruit.class.Review), recruit.string.Review)
declare attachedTo: Ref<Review>
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments)
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files)
attachments?: number
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)

View File

@ -164,7 +164,7 @@ export class TIssue extends TTask implements Issue {
@Prop(Collection(chunter.class.Comment), task.string.TaskComments)
comments!: number
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments)
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files)
attachments!: number
@Prop(TypeString(), task.string.TaskLabels)
@ -341,18 +341,8 @@ export function createModel (builder: Builder): void {
'$lookup.assignee',
'$lookup.state',
'$lookup.doneState',
{
key: '',
presenter: attachment.component.AttachmentsPresenter,
label: attachment.string.Files,
sortingKey: 'attachments'
},
{
key: '',
presenter: chunter.component.CommentsPresenter,
label: chunter.string.Comments,
sortingKey: 'comments'
},
'attachments',
'comments',
'modifiedOn'
]
})

View File

@ -48,7 +48,7 @@ export class TTelegramMessage extends TAttachedDoc implements TelegramMessage {
@Prop(TypeBoolean(), telegram.string.Incoming)
incoming!: boolean
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments)
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files)
attachments?: number
@Prop(TypeTimestamp(), core.string.Modified)
@ -64,7 +64,7 @@ export class TNewTelegramMessage extends TAttachedDoc implements NewTelegramMess
@Prop(TypeString(), telegram.string.Status)
status!: 'new' | 'sent'
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments)
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files)
attachments?: number
}

View File

@ -233,7 +233,7 @@ export class TProject extends TDoc implements Project {
@Prop(Collection(tracker.class.Document), tracker.string.Document)
documents!: number
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments)
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files)
attachments?: number
@Prop(TypeDate(true), tracker.string.Project)

View File

@ -28,6 +28,7 @@ import type {
BuildModelKey,
ClassFilters,
CollectionEditor,
CollectionPresenter,
HTMLPresenter,
IgnoreActions,
KeyBinding,
@ -94,6 +95,11 @@ export class TAttributeEditor extends TClass implements AttributeEditor {
editor!: AnyComponent
}
@Mixin(view.mixin.CollectionPresenter, core.class.Class)
export class TCollectionPresenter extends TClass implements CollectionPresenter {
presenter!: AnyComponent
}
@Mixin(view.mixin.CollectionEditor, core.class.Class)
export class TCollectionEditor extends TClass implements CollectionEditor {
editor!: AnyComponent
@ -242,6 +248,7 @@ export function createModel (builder: Builder): void {
TAttributeEditor,
TAttributePresenter,
TCollectionEditor,
TCollectionPresenter,
TObjectEditor,
TViewletPreference,
TViewletDescriptor,

View File

@ -98,6 +98,7 @@ export interface Attribute<T extends PropertyType> extends Doc, UXObject {
name: string
type: Type<T>
index?: IndexKind
shortLabel?: IntlString
isCustom?: boolean
}

View File

@ -119,7 +119,7 @@ function getAttrs (target: any, prop: string): Record<string, any> {
* @param icon -
* @returns
*/
export function Prop (type: Type<PropertyType>, label: IntlString, icon?: Asset) {
export function Prop (type: Type<PropertyType>, label: IntlString, icon?: Asset, shortLabel?: IntlString) {
return function (target: any, propertyKey: string): void {
const txes = getTxes(target)
const tx: NoIDs<TxCreateDoc<Attribute<PropertyType>>> = {
@ -135,6 +135,7 @@ export function Prop (type: Type<PropertyType>, label: IntlString, icon?: Asset)
type,
label,
icon,
shortLabel,
attributeOf: txes._id, // undefined, need to fix later
...getAttrs(target, propertyKey)
}

View File

@ -19,21 +19,22 @@
import AttachmentPopup from './AttachmentPopup.svelte'
import attachment from '../plugin'
export let value: Doc & { attachments?: number }
export let value: number | undefined
export let object: Doc
export let size: 'small' | 'medium' | 'large' = 'small'
export let showCounter = true
</script>
{#if value && value.attachments && value.attachments > 0}
{#if value && value > 0}
<Tooltip
label={attachment.string.Attachments}
component={AttachmentPopup}
props={{ objectId: value._id, attachments: value.attachments }}
props={{ objectId: object._id, attachments: value }}
>
<div class="sm-tool-icon ml-1 mr-1">
<span class="icon"><IconAttachment {size} /></span>
{#if showCounter}
&nbsp;{value.attachments}
&nbsp;{value}
{/if}
</div>
</Tooltip>

View File

@ -179,12 +179,12 @@
{/if}
{#if (object.attachments ?? 0) > 0}
<div class="float-left">
<AttachmentsPresenter value={object} size="small" />
<AttachmentsPresenter value={object.attachments} {object} size="small" />
</div>
{/if}
{#if (object.comments ?? 0) > 0}
<div class="float-left">
<CommentsPresenter value={object} />
<CommentsPresenter value={object.comments} {object} />
</div>
{/if}
{#if (object.todoItems ?? 0) > 0}

View File

@ -19,17 +19,18 @@
import CommentPopup from './CommentPopup.svelte'
import chunter from '@anticrm/chunter'
export let value: Doc & { comments?: number }
export let value: number | undefined
export let object: Doc
export let size: 'small' | 'medium' | 'large' = 'small'
export let showCounter = true
</script>
{#if value && value.comments && value.comments > 0}
<Tooltip label={chunter.string.Comments} component={CommentPopup} props={{ objectId: value._id }}>
{#if value && value > 0}
<Tooltip label={chunter.string.Comments} component={CommentPopup} props={{ objectId: object._id }}>
<div class="sm-tool-icon ml-1 mr-1">
<span class="icon"><IconThread {size} /></span>
{#if showCounter}
&nbsp;{value.comments}
&nbsp;{value}
{/if}
</div>
</Tooltip>

View File

@ -41,7 +41,7 @@
<span class="content-accent-color">{message.sender}</span>
</div>
<div class="dark-color flex">
<AttachmentsPresenter value={message} />
<AttachmentsPresenter value={message.attachments} object={message} />
<span class="content-accent-color">{getTime(message.sendOn)}</span>
</div>
</div>

View File

@ -62,12 +62,12 @@
<div class="flex-row-center">
{#if (object.attachments ?? 0) > 0}
<div class="step-lr75">
<AttachmentsPresenter value={object} />
<AttachmentsPresenter value={object.attachments} {object} />
</div>
{/if}
{#if (object.comments ?? 0) > 0}
<div class="step-lr75">
<CommentsPresenter value={object} />
<CommentsPresenter value={object.comments} {object} />
</div>
{/if}
</div>

View File

@ -13,8 +13,6 @@
// limitations under the License.
-->
<script lang="ts">
import attachment from '@anticrm/attachment'
import chunter from '@anticrm/chunter'
import type { Doc, Ref } from '@anticrm/core'
import { CircleButton, IconAdd, Label, showPopup } from '@anticrm/ui'
import { BuildModelKey } from '@anticrm/view'
@ -35,13 +33,8 @@
const config: (BuildModelKey | string)[] = [
'',
'$lookup.space.name',
{ key: '', presenter: chunter.component.CommentsPresenter, label: chunter.string.Comments, sortingKey: 'comments' },
{
key: '',
presenter: attachment.component.AttachmentsPresenter,
label: attachment.string.Files,
sortingKey: 'attachments'
},
'comments',
'attachments',
'$lookup.state',
'$lookup.doneState'
]

View File

@ -19,13 +19,14 @@
import ApplicationsPopup from './ApplicationsPopup.svelte'
import recruit from '../plugin'
export let value: Candidate
export let value: number
export let object: Candidate
</script>
{#if value.applications && value.applications > 0}
<Tooltip label={recruit.string.Applications} component={ApplicationsPopup} props={{ value }}>
{#if value && value > 0}
<Tooltip label={recruit.string.Applications} component={ApplicationsPopup} props={{ value: object }}>
<div class="sm-tool-icon">
<span class="icon"><Icon icon={recruit.icon.Application} size={'small'} /></span>&nbsp;{value.applications}
<span class="icon"><Icon icon={recruit.icon.Application} size={'small'} /></span>&nbsp;{value}
</div>
</Tooltip>
{/if}

View File

@ -58,11 +58,11 @@
<div class="flex-center flex-wrap">
<Component
is={chunter.component.CommentsPresenter}
props={{ value: candidate, size: 'medium', showCounter: true }}
props={{ value: candidate.comments, object: candidate, size: 'medium', showCounter: true }}
/>
<Component
is={attachment.component.AttachmentsPresenter}
props={{ value: candidate, size: 'medium', showCounter: true }}
props={{ value: candidate.attachments, object: candidate, size: 'medium', showCounter: true }}
/>
</div>
{#if channels[0]}

View File

@ -67,12 +67,12 @@
</div>
{#if (object.attachments ?? 0) > 0}
<div class="step-lr75">
<AttachmentsPresenter value={object} />
<AttachmentsPresenter value={object.attachments} {object} />
</div>
{/if}
{#if (object.comments ?? 0) > 0}
<div class="step-lr75">
<CommentsPresenter value={object} />
<CommentsPresenter value={object.comments} {object} />
</div>
{/if}
</div>

View File

@ -13,8 +13,6 @@
// limitations under the License.
-->
<script lang="ts">
import attachment from '@anticrm/attachment'
import chunter from '@anticrm/chunter'
import { EmployeeAccount } from '@anticrm/contact'
import { Class, DocumentQuery, getCurrentAccount, Ref } from '@anticrm/core'
import { createQuery, getClient } from '@anticrm/presentation'
@ -99,18 +97,8 @@
'$lookup.assignee',
'$lookup.state',
'$lookup.doneState',
{
key: '',
presenter: attachment.component.AttachmentsPresenter,
label: attachment.string.Files,
sortingKey: 'attachments'
},
{
key: '',
presenter: chunter.component.CommentsPresenter,
label: chunter.string.Comments,
sortingKey: 'comments'
},
'attachments',
'comments',
'modifiedOn'
]}
query={resultQuery}

View File

@ -64,12 +64,12 @@
<div class="flex-row-center">
{#if (object.attachments ?? 0) > 0}
<div class="step-lr75">
<AttachmentsPresenter value={object} />
<AttachmentsPresenter value={object.attachments} {object} />
</div>
{/if}
{#if (object.comments ?? 0) > 0}
<div class="step-lr75">
<CommentsPresenter value={object} />
<CommentsPresenter value={object.comments} {object} />
</div>
{/if}
</div>

View File

@ -93,15 +93,19 @@ async function getAttributePresenter (
key: string,
preserveKey: BuildModelKey
): Promise<AttributeModel> {
const attribute = client.getHierarchy().getAttribute(_class, key)
const hierarchy = client.getHierarchy()
const attribute = hierarchy.getAttribute(_class, key)
let attrClass = getAttributePresenterClass(attribute)
const clazz = client.getHierarchy().getClass(attrClass)
let presenterMixin = client.getHierarchy().as(clazz, view.mixin.AttributePresenter)
const mixin = hierarchy.isDerived(attribute.type._class, core.class.Collection)
? view.mixin.CollectionPresenter
: view.mixin.AttributePresenter
const clazz = hierarchy.getClass(attrClass)
let presenterMixin = hierarchy.as(clazz, mixin)
let parent = clazz.extends
while (presenterMixin.presenter === undefined && parent !== undefined) {
const pclazz = client.getHierarchy().getClass(parent)
const pclazz = hierarchy.getClass(parent)
attrClass = parent
presenterMixin = client.getHierarchy().as(pclazz, view.mixin.AttributePresenter)
presenterMixin = hierarchy.as(pclazz, mixin)
parent = pclazz.extends
}
if (presenterMixin.presenter === undefined) {
@ -115,7 +119,7 @@ async function getAttributePresenter (
key: preserveKey.key,
sortingKey,
_class: attrClass,
label: preserveKey.label ?? attribute.label,
label: preserveKey.label ?? attribute.shortLabel ?? attribute.label,
presenter,
props: {},
icon: presenterMixin.icon,

View File

@ -93,6 +93,13 @@ export interface CollectionEditor extends Class<Doc> {
editor: AnyComponent
}
/**
* @public
*/
export interface CollectionPresenter extends Class<Doc> {
presenter: AnyComponent
}
/**
* @public
*/
@ -351,6 +358,7 @@ const view = plugin(viewId, {
ClassFilters: '' as Ref<Mixin<ClassFilters>>,
AttributeFilter: '' as Ref<Mixin<AttributeFilter>>,
AttributeEditor: '' as Ref<Mixin<AttributeEditor>>,
CollectionPresenter: '' as Ref<Mixin<CollectionPresenter>>,
CollectionEditor: '' as Ref<Mixin<CollectionEditor>>,
AttributePresenter: '' as Ref<Mixin<AttributePresenter>>,
ObjectEditor: '' as Ref<Mixin<ObjectEditor>>,