mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-26 18:29:51 +00:00
Objects sort (#672)
Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
parent
9a8d9f8804
commit
81000b50f6
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
import type { Domain, Type, Ref } from '@anticrm/core'
|
import type { Domain, Type, Ref } from '@anticrm/core'
|
||||||
import { DOMAIN_MODEL, IndexKind } from '@anticrm/core'
|
import { DOMAIN_MODEL, IndexKind } from '@anticrm/core'
|
||||||
import { Builder, Model, Prop, TypeString, UX, Index, Collection } from '@anticrm/model'
|
import { Builder, Model, Prop, TypeString, UX, Index, Collection, ArrOf } from '@anticrm/model'
|
||||||
import type { IntlString, Asset } from '@anticrm/platform'
|
import type { IntlString, Asset } from '@anticrm/platform'
|
||||||
import chunter from '@anticrm/model-chunter'
|
import chunter from '@anticrm/model-chunter'
|
||||||
import core, { TAccount, TDoc, TSpace, TType } from '@anticrm/model-core'
|
import core, { TAccount, TDoc, TSpace, TType } from '@anticrm/model-core'
|
||||||
@ -44,14 +44,14 @@ export class TChannelProvider extends TDoc implements ChannelProvider {
|
|||||||
placeholder!: string
|
placeholder!: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@Model(contact.class.TypeChannels, core.class.Type)
|
@Model(contact.class.TypeChannel, core.class.Type)
|
||||||
export class TTypeChannels extends TType {}
|
export class TTypeChannels extends TType {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export function TypeChannels (): Type<Channel[]> {
|
export function TypeChannel (): Type<Channel> {
|
||||||
return { _class: contact.class.TypeChannels, label: 'TypeChannels' as IntlString }
|
return { _class: contact.class.TypeChannel, label: 'Channel' as IntlString }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Model(contact.class.Contact, core.class.Doc, DOMAIN_CONTACT)
|
@Model(contact.class.Contact, core.class.Doc, DOMAIN_CONTACT)
|
||||||
@ -62,7 +62,7 @@ export class TContact extends TDoc implements Contact {
|
|||||||
|
|
||||||
avatar?: string
|
avatar?: string
|
||||||
|
|
||||||
@Prop(TypeChannels(), 'Contact Info' as IntlString)
|
@Prop(ArrOf(TypeChannel()), 'Contact Info' as IntlString)
|
||||||
channels!: Channel[]
|
channels!: Channel[]
|
||||||
|
|
||||||
@Prop(Collection(attachment.class.Attachment), 'Attachments' as IntlString)
|
@Prop(Collection(attachment.class.Attachment), 'Attachments' as IntlString)
|
||||||
@ -73,14 +73,14 @@ export class TContact extends TDoc implements Contact {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Model(contact.class.Person, contact.class.Contact)
|
@Model(contact.class.Person, contact.class.Contact)
|
||||||
@UX('Person' as IntlString, contact.icon.Person)
|
@UX('Person' as IntlString, contact.icon.Person, undefined, 'name')
|
||||||
export class TPerson extends TContact implements Person {
|
export class TPerson extends TContact implements Person {
|
||||||
@Prop(TypeString(), 'City' as IntlString)
|
@Prop(TypeString(), 'City' as IntlString)
|
||||||
city!: string
|
city!: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@Model(contact.class.Organization, contact.class.Contact)
|
@Model(contact.class.Organization, contact.class.Contact)
|
||||||
@UX('Organization' as IntlString, contact.icon.Company)
|
@UX('Organization' as IntlString, contact.icon.Company, undefined, 'name')
|
||||||
export class TOrganization extends TContact implements Organization {}
|
export class TOrganization extends TContact implements Organization {}
|
||||||
|
|
||||||
@Model(contact.class.Employee, contact.class.Person)
|
@Model(contact.class.Employee, contact.class.Person)
|
||||||
@ -158,7 +158,7 @@ export function createModel (builder: Builder): void {
|
|||||||
config: [
|
config: [
|
||||||
'',
|
'',
|
||||||
'city',
|
'city',
|
||||||
{ presenter: attachment.component.AttachmentsPresenter, label: 'Files' },
|
{ presenter: attachment.component.AttachmentsPresenter, label: 'Files', sortingKey: 'attachments' },
|
||||||
'modifiedOn',
|
'modifiedOn',
|
||||||
'channels'
|
'channels'
|
||||||
]
|
]
|
||||||
@ -170,7 +170,7 @@ export function createModel (builder: Builder): void {
|
|||||||
open: contact.component.EditContact,
|
open: contact.component.EditContact,
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
options: {},
|
options: {},
|
||||||
config: ['', { presenter: attachment.component.AttachmentsPresenter, label: 'Files' }, 'modifiedOn', 'channels']
|
config: ['', { presenter: attachment.component.AttachmentsPresenter, label: 'Files', sortingKey: 'attachments' }, 'modifiedOn', 'channels']
|
||||||
})
|
})
|
||||||
|
|
||||||
builder.mixin(contact.class.Person, core.class.Class, view.mixin.ObjectEditor, {
|
builder.mixin(contact.class.Person, core.class.Class, view.mixin.ObjectEditor, {
|
||||||
@ -185,7 +185,7 @@ export function createModel (builder: Builder): void {
|
|||||||
editor: contact.component.EditOrganization
|
editor: contact.component.EditOrganization
|
||||||
})
|
})
|
||||||
|
|
||||||
builder.mixin(contact.class.TypeChannels, core.class.Class, view.mixin.AttributePresenter, {
|
builder.mixin(contact.class.TypeChannel, core.class.Class, view.mixin.AttributePresenter, {
|
||||||
presenter: contact.component.ChannelsPresenter
|
presenter: contact.component.ChannelsPresenter
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -44,6 +44,6 @@ export const ids = mergeIds(contactId, contact, {
|
|||||||
CreateOrganizations: '' as IntlString
|
CreateOrganizations: '' as IntlString
|
||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
TypeChannels: '' as Ref<Class<Type<Channel[]>>>
|
TypeChannel: '' as Ref<Class<Type<Channel>>>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -36,7 +36,7 @@ import lead from './plugin'
|
|||||||
export class TFunnel extends TSpaceWithStates implements Funnel {}
|
export class TFunnel extends TSpaceWithStates implements Funnel {}
|
||||||
|
|
||||||
@Model(lead.class.Lead, task.class.Task)
|
@Model(lead.class.Lead, task.class.Task)
|
||||||
@UX('Lead' as IntlString, lead.icon.Lead)
|
@UX('Lead' as IntlString, lead.icon.Lead, undefined, 'title')
|
||||||
export class TLead extends TTask implements Lead {
|
export class TLead extends TTask implements Lead {
|
||||||
@Prop(TypeString(), 'Title' as IntlString)
|
@Prop(TypeString(), 'Title' as IntlString)
|
||||||
title!: string
|
title!: string
|
||||||
@ -108,8 +108,8 @@ export function createModel (builder: Builder): void {
|
|||||||
'',
|
'',
|
||||||
'$lookup.customer',
|
'$lookup.customer',
|
||||||
'$lookup.state',
|
'$lookup.state',
|
||||||
{ presenter: attachment.component.AttachmentsPresenter, label: 'Files' },
|
{ presenter: attachment.component.AttachmentsPresenter, label: 'Files', sortingKey: 'attachments' },
|
||||||
{ presenter: chunter.component.CommentsPresenter, label: 'Comments' },
|
{ presenter: chunter.component.CommentsPresenter, label: 'Comments', sortingKey: 'comments' },
|
||||||
'modifiedOn',
|
'modifiedOn',
|
||||||
'$lookup.customer.channels'
|
'$lookup.customer.channels'
|
||||||
]
|
]
|
||||||
|
@ -70,7 +70,7 @@ export class TCandidate extends TPerson implements Candidate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Model(recruit.class.Applicant, task.class.Task)
|
@Model(recruit.class.Applicant, task.class.Task)
|
||||||
@UX('Application' as IntlString, recruit.icon.Application, 'APP' as IntlString)
|
@UX('Application' as IntlString, recruit.icon.Application, 'APP' as IntlString, 'number')
|
||||||
export class TApplicant extends TTask implements Applicant {
|
export class TApplicant extends TTask implements Applicant {
|
||||||
// We need to declare, to provide property with label
|
// We need to declare, to provide property with label
|
||||||
@Prop(TypeRef(recruit.class.Candidate), 'Candidate' as IntlString)
|
@Prop(TypeRef(recruit.class.Candidate), 'Candidate' as IntlString)
|
||||||
@ -160,9 +160,9 @@ export function createModel (builder: Builder): void {
|
|||||||
'',
|
'',
|
||||||
'title',
|
'title',
|
||||||
'city',
|
'city',
|
||||||
{ presenter: recruit.component.ApplicationsPresenter, label: 'Apps' },
|
{ presenter: recruit.component.ApplicationsPresenter, label: 'Apps', sortingKey: 'applications' },
|
||||||
{ presenter: attachment.component.AttachmentsPresenter, label: 'Files' },
|
{ presenter: attachment.component.AttachmentsPresenter, label: 'Files', sortingKey: 'attachments' },
|
||||||
{ presenter: chunter.component.CommentsPresenter, label: 'Comments' },
|
{ presenter: chunter.component.CommentsPresenter, label: 'Comments', sortingKey: 'comments' },
|
||||||
'modifiedOn',
|
'modifiedOn',
|
||||||
'channels'
|
'channels'
|
||||||
]
|
]
|
||||||
@ -187,9 +187,8 @@ export function createModel (builder: Builder): void {
|
|||||||
'$lookup.assignee',
|
'$lookup.assignee',
|
||||||
'$lookup.state',
|
'$lookup.state',
|
||||||
'$lookup.doneState',
|
'$lookup.doneState',
|
||||||
// '$lookup.attachedTo.city',
|
{ presenter: attachment.component.AttachmentsPresenter, label: 'Files', sortingKey: 'attachments' },
|
||||||
{ presenter: attachment.component.AttachmentsPresenter, label: 'Files' },
|
{ presenter: chunter.component.CommentsPresenter, label: 'Comments', sortingKey: 'comments' },
|
||||||
{ presenter: chunter.component.CommentsPresenter, label: 'Comments' },
|
|
||||||
'modifiedOn',
|
'modifiedOn',
|
||||||
'$lookup.attachedTo.channels'
|
'$lookup.attachedTo.channels'
|
||||||
]
|
]
|
||||||
|
@ -54,6 +54,7 @@ export const DOMAIN_TASK = 'task' as Domain
|
|||||||
export const DOMAIN_STATE = 'state' as Domain
|
export const DOMAIN_STATE = 'state' as Domain
|
||||||
export const DOMAIN_KANBAN = 'kanban' as Domain
|
export const DOMAIN_KANBAN = 'kanban' as Domain
|
||||||
@Model(task.class.State, core.class.Doc, DOMAIN_STATE, [core.interface.DocWithRank])
|
@Model(task.class.State, core.class.Doc, DOMAIN_STATE, [core.interface.DocWithRank])
|
||||||
|
@UX('State' as IntlString, undefined, undefined, 'title')
|
||||||
export class TState extends TDoc implements State {
|
export class TState extends TDoc implements State {
|
||||||
@Prop(TypeString(), 'Title' as IntlString)
|
@Prop(TypeString(), 'Title' as IntlString)
|
||||||
title!: string
|
title!: string
|
||||||
@ -64,6 +65,7 @@ export class TState extends TDoc implements State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Model(task.class.DoneState, core.class.Doc, DOMAIN_STATE, [core.interface.DocWithRank])
|
@Model(task.class.DoneState, core.class.Doc, DOMAIN_STATE, [core.interface.DocWithRank])
|
||||||
|
@UX('Done' as IntlString, undefined, undefined, 'title')
|
||||||
export class TDoneState extends TDoc implements DoneState {
|
export class TDoneState extends TDoc implements DoneState {
|
||||||
@Prop(TypeString(), 'Title' as IntlString)
|
@Prop(TypeString(), 'Title' as IntlString)
|
||||||
title!: string
|
title!: string
|
||||||
@ -107,7 +109,7 @@ export class TSpaceWithStates extends TSpace {}
|
|||||||
export class TProject extends TSpaceWithStates implements Project {}
|
export class TProject extends TSpaceWithStates implements Project {}
|
||||||
|
|
||||||
@Model(task.class.Issue, task.class.Task, DOMAIN_TASK)
|
@Model(task.class.Issue, task.class.Task, DOMAIN_TASK)
|
||||||
@UX('Task' as IntlString, task.icon.Task, 'Task' as IntlString)
|
@UX('Task' as IntlString, task.icon.Task, 'Task' as IntlString, 'number')
|
||||||
export class TIssue extends TTask implements Issue {
|
export class TIssue extends TTask implements Issue {
|
||||||
// We need to declare, to provide property with label
|
// We need to declare, to provide property with label
|
||||||
@Prop(TypeRef(core.class.Doc), 'Parent' as IntlString)
|
@Prop(TypeRef(core.class.Doc), 'Parent' as IntlString)
|
||||||
@ -254,8 +256,8 @@ export function createModel (builder: Builder): void {
|
|||||||
'',
|
'',
|
||||||
'name',
|
'name',
|
||||||
'$lookup.assignee',
|
'$lookup.assignee',
|
||||||
{ presenter: attachment.component.AttachmentsPresenter, label: 'Files' },
|
{ presenter: attachment.component.AttachmentsPresenter, label: 'Files', sortingKey: 'attachments' },
|
||||||
{ presenter: chunter.component.CommentsPresenter, label: 'Comments' },
|
{ presenter: chunter.component.CommentsPresenter, label: 'Comments', sortingKey: 'comments' },
|
||||||
'modifiedOn'
|
'modifiedOn'
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
@ -15,17 +15,21 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import type { IntlString } from '@anticrm/platform'
|
import type { IntlString } from '@anticrm/platform'
|
||||||
import { Builder, Model, TypeString, TypeBoolean, Prop, Collection } from '@anticrm/model'
|
import { Builder, Model, TypeString, TypeBoolean, Prop, ArrOf } from '@anticrm/model'
|
||||||
import core, { TAttachedDoc, TDoc } from '@anticrm/model-core'
|
import core, { TAttachedDoc, TDoc } from '@anticrm/model-core'
|
||||||
import contact from '@anticrm/model-contact'
|
import contact from '@anticrm/model-contact'
|
||||||
import telegram from './plugin'
|
import telegram from './plugin'
|
||||||
import type { TelegramMessage, SharedTelegramMessage, SharedTelegramMessages } from '@anticrm/telegram'
|
import type { TelegramMessage, SharedTelegramMessage, SharedTelegramMessages } from '@anticrm/telegram'
|
||||||
import type { Domain } from '@anticrm/core'
|
import type { Domain, Type } from '@anticrm/core'
|
||||||
import setting from '@anticrm/setting'
|
import setting from '@anticrm/setting'
|
||||||
import activity from '@anticrm/activity'
|
import activity from '@anticrm/activity'
|
||||||
|
|
||||||
export const DOMAIN_TELEGRAM = 'telegram' as Domain
|
export const DOMAIN_TELEGRAM = 'telegram' as Domain
|
||||||
|
|
||||||
|
function TypeSharedMessage (): Type<SharedTelegramMessage> {
|
||||||
|
return { _class: telegram.class.SharedMessage, label: 'Shared message' as IntlString }
|
||||||
|
}
|
||||||
|
|
||||||
@Model(telegram.class.Message, core.class.Doc, DOMAIN_TELEGRAM)
|
@Model(telegram.class.Message, core.class.Doc, DOMAIN_TELEGRAM)
|
||||||
export class TTelegramMessage extends TDoc implements TelegramMessage {
|
export class TTelegramMessage extends TDoc implements TelegramMessage {
|
||||||
@Prop(TypeString(), 'Content' as IntlString)
|
@Prop(TypeString(), 'Content' as IntlString)
|
||||||
@ -43,7 +47,7 @@ export class TTelegramMessage extends TDoc implements TelegramMessage {
|
|||||||
|
|
||||||
@Model(telegram.class.SharedMessages, core.class.AttachedDoc, DOMAIN_TELEGRAM)
|
@Model(telegram.class.SharedMessages, core.class.AttachedDoc, DOMAIN_TELEGRAM)
|
||||||
export class TSharedTelegramMessages extends TAttachedDoc implements SharedTelegramMessages {
|
export class TSharedTelegramMessages extends TAttachedDoc implements SharedTelegramMessages {
|
||||||
@Prop(Collection(telegram.class.SharedMessage), 'Messages' as IntlString)
|
@Prop(ArrOf(TypeSharedMessage()), 'Messages' as IntlString)
|
||||||
messages!: SharedTelegramMessage[]
|
messages!: SharedTelegramMessage[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,6 +136,7 @@ export interface Class<T extends Obj> extends Classifier {
|
|||||||
implements?: Ref<Interface<Doc>>[]
|
implements?: Ref<Interface<Doc>>[]
|
||||||
domain?: Domain
|
domain?: Domain
|
||||||
shortLabel?: IntlString
|
shortLabel?: IntlString
|
||||||
|
sortingKey?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
//
|
//
|
||||||
import type { Plugin, StatusCode } from '@anticrm/platform'
|
import type { Plugin, StatusCode } from '@anticrm/platform'
|
||||||
import { plugin } from '@anticrm/platform'
|
import { plugin } from '@anticrm/platform'
|
||||||
import type { Account, AnyAttribute, AttachedDoc, DocWithRank, Class, Doc, Interface, Obj, PropertyType, Ref, Space, Timestamp, Type, Collection, RefTo } from './classes'
|
import type { Account, ArrOf, AnyAttribute, AttachedDoc, DocWithRank, Class, Doc, Interface, Obj, PropertyType, Ref, Space, Timestamp, Type, Collection, RefTo } from './classes'
|
||||||
import type { Tx, TxBulkWrite, TxCollectionCUD, TxCreateDoc, TxCUD, TxMixin, TxPutBag, TxRemoveDoc, TxUpdateDoc } from './tx'
|
import type { Tx, TxBulkWrite, TxCollectionCUD, TxCreateDoc, TxCUD, TxMixin, TxPutBag, TxRemoveDoc, TxUpdateDoc } from './tx'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,6 +46,7 @@ export default plugin(coreId, {
|
|||||||
TypeTimestamp: '' as Ref<Class<Type<Timestamp>>>,
|
TypeTimestamp: '' as Ref<Class<Type<Timestamp>>>,
|
||||||
TypeDate: '' as Ref<Class<Type<Timestamp | Date>>>,
|
TypeDate: '' as Ref<Class<Type<Timestamp | Date>>>,
|
||||||
RefTo: '' as Ref<Class<RefTo<Doc>>>,
|
RefTo: '' as Ref<Class<RefTo<Doc>>>,
|
||||||
|
ArrOf: '' as Ref<Class<ArrOf<Doc>>>,
|
||||||
Collection: '' as Ref<Class<Collection<AttachedDoc>>>,
|
Collection: '' as Ref<Class<Collection<AttachedDoc>>>,
|
||||||
Bag: '' as Ref<Class<Type<Record<string, PropertyType>>>>
|
Bag: '' as Ref<Class<Type<Record<string, PropertyType>>>>
|
||||||
},
|
},
|
||||||
|
@ -75,18 +75,30 @@ function arrayOrValue (vv: any): any[] {
|
|||||||
export function resultSort<T extends Doc> (result: T[], sortOptions: SortingQuery<T>): void {
|
export function resultSort<T extends Doc> (result: T[], sortOptions: SortingQuery<T>): void {
|
||||||
const sortFunc = (a: any, b: any): number => {
|
const sortFunc = (a: any, b: any): number => {
|
||||||
for (const key in sortOptions) {
|
for (const key in sortOptions) {
|
||||||
let aValue = getNestedValue(key, a)
|
const aValue = getValue(key, a)
|
||||||
if (typeof aValue === 'object') {
|
const bValue = getValue(key, b)
|
||||||
aValue = JSON.stringify(aValue)
|
const result = getSortingResult(aValue, bValue)
|
||||||
}
|
|
||||||
let bValue = getNestedValue(key, b)
|
|
||||||
if (typeof bValue === 'object') {
|
|
||||||
bValue = JSON.stringify(bValue)
|
|
||||||
}
|
|
||||||
const result = typeof aValue === 'string' ? aValue.localeCompare(bValue) : aValue - bValue
|
|
||||||
if (result !== 0) return result * (sortOptions[key] as number)
|
if (result !== 0) return result * (sortOptions[key] as number)
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
result.sort(sortFunc)
|
result.sort(sortFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSortingResult (aValue: any, bValue: any): number {
|
||||||
|
if (typeof aValue === 'undefined') {
|
||||||
|
return typeof bValue === 'undefined' ? 0 : -1
|
||||||
|
}
|
||||||
|
if (typeof bValue === 'undefined') {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return typeof aValue === 'string' ? aValue.localeCompare(bValue) : (aValue - bValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getValue (key: string, obj: any): any {
|
||||||
|
let value = getNestedValue(key, obj)
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
value = JSON.stringify(value)
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import core, {
|
import core, {
|
||||||
|
ArrOf as TypeArrOf,
|
||||||
Account,
|
Account,
|
||||||
AttachedDoc, Collection as TypeCollection, RefTo,
|
AttachedDoc, Collection as TypeCollection, RefTo,
|
||||||
Attribute, Class, Classifier, ClassifierKind, Data, Doc, Domain, ExtendedAttributes, generateId, IndexKind, Interface, Mixin as IMixin, Obj, PropertyType, Ref, Space, Tx, TxCreateDoc, TxFactory, TxProcessor, Type
|
Attribute, Class, Classifier, ClassifierKind, Data, Doc, Domain, ExtendedAttributes, generateId, IndexKind, Interface, Mixin as IMixin, Obj, PropertyType, Ref, Space, Tx, TxCreateDoc, TxFactory, TxProcessor, Type
|
||||||
@ -48,6 +49,7 @@ interface ClassTxes {
|
|||||||
txes: Array<NoIDs<Tx>>
|
txes: Array<NoIDs<Tx>>
|
||||||
kind: ClassifierKind
|
kind: ClassifierKind
|
||||||
shortLabel?: IntlString
|
shortLabel?: IntlString
|
||||||
|
sortingKey?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const transactions = new Map<any, ClassTxes>()
|
const transactions = new Map<any, ClassTxes>()
|
||||||
@ -159,13 +161,15 @@ export function Mixin<T extends Obj> (
|
|||||||
export function UX<T extends Obj> (
|
export function UX<T extends Obj> (
|
||||||
label: IntlString,
|
label: IntlString,
|
||||||
icon?: Asset,
|
icon?: Asset,
|
||||||
shortLabel?: IntlString
|
shortLabel?: IntlString,
|
||||||
|
sortingKey?: string
|
||||||
) {
|
) {
|
||||||
return function classDecorator<C extends new () => T> (constructor: C): void {
|
return function classDecorator<C extends new () => T> (constructor: C): void {
|
||||||
const txes = getTxes(constructor.prototype)
|
const txes = getTxes(constructor.prototype)
|
||||||
txes.label = label
|
txes.label = label
|
||||||
txes.icon = icon
|
txes.icon = icon
|
||||||
txes.shortLabel = shortLabel
|
txes.shortLabel = shortLabel
|
||||||
|
txes.sortingKey = sortingKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,7 +198,8 @@ function _generateTx (tx: ClassTxes): Tx[] {
|
|||||||
...(tx.kind === ClassifierKind.INTERFACE ? { extends: tx.implements } : { extends: tx.extends, implements: tx.implements }),
|
...(tx.kind === ClassifierKind.INTERFACE ? { extends: tx.implements } : { extends: tx.extends, implements: tx.implements }),
|
||||||
label: tx.label,
|
label: tx.label,
|
||||||
icon: tx.icon,
|
icon: tx.icon,
|
||||||
shortLabel: tx.shortLabel
|
shortLabel: tx.shortLabel,
|
||||||
|
sortingKey: tx.sortingKey
|
||||||
},
|
},
|
||||||
objectId
|
objectId
|
||||||
)
|
)
|
||||||
@ -320,6 +325,13 @@ export function Collection<T extends AttachedDoc> (clazz: Ref<Class<T>>): TypeCo
|
|||||||
return { _class: core.class.Collection, of: clazz, label: 'Collection' as IntlString }
|
return { _class: core.class.Collection, of: clazz, label: 'Collection' as IntlString }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function ArrOf<T extends PropertyType> (type: Type<T>): TypeArrOf<T> {
|
||||||
|
return { _class: core.class.ArrOf, of: type, label: 'Array' as IntlString }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
|
@ -32,7 +32,8 @@ import core, {
|
|||||||
AnyAttribute,
|
AnyAttribute,
|
||||||
RefTo,
|
RefTo,
|
||||||
Collection,
|
Collection,
|
||||||
AttachedDoc
|
AttachedDoc,
|
||||||
|
ArrOf
|
||||||
} from '@anticrm/core'
|
} from '@anticrm/core'
|
||||||
import { LiveQuery as LQ } from '@anticrm/query'
|
import { LiveQuery as LQ } from '@anticrm/query'
|
||||||
import { getMetadata } from '@anticrm/platform'
|
import { getMetadata } from '@anticrm/platform'
|
||||||
@ -120,5 +121,8 @@ export function getAttributePresenterClass (attribute: AnyAttribute): Ref<Class<
|
|||||||
if (attrClass === core.class.Collection) {
|
if (attrClass === core.class.Collection) {
|
||||||
attrClass = (attribute.type as Collection<AttachedDoc>).of
|
attrClass = (attribute.type as Collection<AttachedDoc>).of
|
||||||
}
|
}
|
||||||
|
if (attrClass === core.class.ArrOf) {
|
||||||
|
attrClass = (attribute.type as ArrOf<AttachedDoc>).of._class
|
||||||
|
}
|
||||||
return attrClass
|
return attrClass
|
||||||
}
|
}
|
||||||
|
@ -76,10 +76,10 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr class="tr-head">
|
<tr class="tr-head">
|
||||||
{#each model as attribute}
|
{#each model as attribute}
|
||||||
<th class:sortable={attribute.key} class:sorted={attribute.key === sortKey} on:click={() => changeSorting(attribute.key)}>
|
<th class:sortable={attribute.sortingKey} class:sorted={attribute.sortingKey === sortKey} on:click={() => changeSorting(attribute.sortingKey)}>
|
||||||
<div class="flex-row-center whitespace-nowrap">
|
<div class="flex-row-center whitespace-nowrap">
|
||||||
<Label label = {attribute.label}/>
|
<Label label = {attribute.label}/>
|
||||||
{#if attribute.key === sortKey}
|
{#if attribute.sortingKey === sortKey}
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
{#if sortOrder === SortingOrder.Ascending}
|
{#if sortOrder === SortingOrder.Ascending}
|
||||||
<IconUp size={'small'} />
|
<IconUp size={'small'} />
|
||||||
|
@ -115,13 +115,13 @@
|
|||||||
</th>
|
</th>
|
||||||
{/if}
|
{/if}
|
||||||
<th
|
<th
|
||||||
class:sortable={attribute.key}
|
class:sortable={attribute.sortingKey}
|
||||||
class:sorted={attribute.key === sortKey}
|
class:sorted={attribute.sortingKey === sortKey}
|
||||||
on:click={() => changeSorting(attribute.key)}
|
on:click={() => changeSorting(attribute.sortingKey)}
|
||||||
>
|
>
|
||||||
<div class="flex-row-center whitespace-nowrap">
|
<div class="flex-row-center whitespace-nowrap">
|
||||||
<Label label={attribute.label} />
|
<Label label={attribute.label} />
|
||||||
{#if attribute.key === sortKey}
|
{#if attribute.sortingKey === sortKey}
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
{#if sortOrder === SortingOrder.Ascending}
|
{#if sortOrder === SortingOrder.Ascending}
|
||||||
<IconUp size={'small'} />
|
<IconUp size={'small'} />
|
||||||
|
@ -36,11 +36,16 @@ export async function getObjectPresenter (client: Client, _class: Ref<Class<Obj>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const presenter = await getResource(presenterMixin.presenter)
|
const presenter = await getResource(presenterMixin.presenter)
|
||||||
|
const key = typeof preserveKey === 'string' ? preserveKey : ''
|
||||||
|
const sortingKey = clazz.sortingKey ?
|
||||||
|
(key.length > 0 ? key + '.' + clazz.sortingKey : clazz.sortingKey)
|
||||||
|
: key
|
||||||
return {
|
return {
|
||||||
key: typeof preserveKey === 'string' ? preserveKey : '',
|
key,
|
||||||
_class,
|
_class,
|
||||||
label: clazz.label,
|
label: clazz.label,
|
||||||
presenter
|
presenter,
|
||||||
|
sortingKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,9 +64,12 @@ async function getAttributePresenter (client: Client, _class: Ref<Class<Obj>>, k
|
|||||||
if (presenterMixin.presenter === undefined) {
|
if (presenterMixin.presenter === undefined) {
|
||||||
throw new Error('attribute presenter not found for ' + JSON.stringify(preserveKey))
|
throw new Error('attribute presenter not found for ' + JSON.stringify(preserveKey))
|
||||||
}
|
}
|
||||||
|
const resultKey = typeof preserveKey === 'string' ? preserveKey : ''
|
||||||
|
const sortingKey = attribute.type._class === core.class.ArrOf ? resultKey + '.length' : resultKey
|
||||||
const presenter = await getResource(presenterMixin.presenter)
|
const presenter = await getResource(presenterMixin.presenter)
|
||||||
return {
|
return {
|
||||||
key: typeof preserveKey === 'string' ? preserveKey : '',
|
key: resultKey,
|
||||||
|
sortingKey,
|
||||||
_class: attrClass,
|
_class: attrClass,
|
||||||
label: attribute.label,
|
label: attribute.label,
|
||||||
presenter
|
presenter
|
||||||
@ -70,9 +78,10 @@ async function getAttributePresenter (client: Client, _class: Ref<Class<Obj>>, k
|
|||||||
|
|
||||||
async function getPresenter (client: Client, _class: Ref<Class<Obj>>, key: BuildModelKey, preserveKey: BuildModelKey, options?: FindOptions<Doc>): Promise<AttributeModel> {
|
async function getPresenter (client: Client, _class: Ref<Class<Obj>>, key: BuildModelKey, preserveKey: BuildModelKey, options?: FindOptions<Doc>): Promise<AttributeModel> {
|
||||||
if (typeof key === 'object') {
|
if (typeof key === 'object') {
|
||||||
const { presenter, label } = key
|
const { presenter, label, sortingKey } = key
|
||||||
return {
|
return {
|
||||||
key: '',
|
key: '',
|
||||||
|
sortingKey: sortingKey ?? '',
|
||||||
_class,
|
_class,
|
||||||
label: label as IntlString,
|
label: label as IntlString,
|
||||||
presenter: await getResource(presenter)
|
presenter: await getResource(presenter)
|
||||||
@ -116,6 +125,7 @@ export async function buildModel (options: BuildModelOptions): Promise<Attribute
|
|||||||
console.error('Failed to find presenter for', key, err)
|
console.error('Failed to find presenter for', key, err)
|
||||||
const errorPresenter: AttributeModel = {
|
const errorPresenter: AttributeModel = {
|
||||||
key: '',
|
key: '',
|
||||||
|
sortingKey: '',
|
||||||
presenter: ErrorPresenter,
|
presenter: ErrorPresenter,
|
||||||
label: stringKey as IntlString,
|
label: stringKey as IntlString,
|
||||||
_class: core.class.TypeString,
|
_class: core.class.TypeString,
|
||||||
|
@ -84,6 +84,7 @@ export const viewId = 'view' as Plugin
|
|||||||
export type BuildModelKey = string | {
|
export type BuildModelKey = string | {
|
||||||
presenter: AnyComponent
|
presenter: AnyComponent
|
||||||
label: string
|
label: string
|
||||||
|
sortingKey?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -96,6 +97,7 @@ export interface AttributeModel {
|
|||||||
presenter: AnySvelteComponent
|
presenter: AnySvelteComponent
|
||||||
// Extra properties for component
|
// Extra properties for component
|
||||||
props?: Record<string, any>
|
props?: Record<string, any>
|
||||||
|
sortingKey: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user