mirror of
https://github.com/hcengineering/platform.git
synced 2025-05-29 19:56:18 +00:00
Security improvments (#5595)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
parent
b60067cc92
commit
2d92b9aa46
@ -66,7 +66,7 @@ import core, { TAccount, TAttachedDoc, TDoc } from '@hcengineering/model-core'
|
|||||||
import { createPublicLinkAction } from '@hcengineering/model-guest'
|
import { createPublicLinkAction } from '@hcengineering/model-guest'
|
||||||
import { generateClassNotificationTypes } from '@hcengineering/model-notification'
|
import { generateClassNotificationTypes } from '@hcengineering/model-notification'
|
||||||
import presentation from '@hcengineering/model-presentation'
|
import presentation from '@hcengineering/model-presentation'
|
||||||
import view, { createAction, type Viewlet } from '@hcengineering/model-view'
|
import view, { createAction, createAttributePresenter, type Viewlet } from '@hcengineering/model-view'
|
||||||
import workbench from '@hcengineering/model-workbench'
|
import workbench from '@hcengineering/model-workbench'
|
||||||
import notification from '@hcengineering/notification'
|
import notification from '@hcengineering/notification'
|
||||||
import type { Asset, IntlString, Resource } from '@hcengineering/platform'
|
import type { Asset, IntlString, Resource } from '@hcengineering/platform'
|
||||||
@ -1142,4 +1142,6 @@ export function createModel (builder: Builder): void {
|
|||||||
domain: DOMAIN_CONTACT,
|
domain: DOMAIN_CONTACT,
|
||||||
disabled: [{ attachedToClass: 1 }, { modifiedBy: 1 }, { createdBy: 1 }, { createdOn: -1 }, { attachedTo: 1 }]
|
disabled: [{ attachedToClass: 1 }, { modifiedBy: 1 }, { createdBy: 1 }, { createdOn: -1 }, { attachedTo: 1 }]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
createAttributePresenter(builder, contact.component.SpaceMembersEditor, core.class.Space, 'members', 'array')
|
||||||
}
|
}
|
||||||
|
@ -211,6 +211,10 @@ export function createModel (builder: Builder): void {
|
|||||||
inlineEditor: hr.component.DepartmentEditor
|
inlineEditor: hr.component.DepartmentEditor
|
||||||
})
|
})
|
||||||
|
|
||||||
|
builder.mixin(hr.class.Department, core.class.Class, view.mixin.AttributePresenter, {
|
||||||
|
presenter: hr.component.DepartmentRefPresenter
|
||||||
|
})
|
||||||
|
|
||||||
builder.mixin(hr.class.Department, core.class.Class, view.mixin.ObjectEditor, {
|
builder.mixin(hr.class.Department, core.class.Class, view.mixin.ObjectEditor, {
|
||||||
editor: hr.component.EditDepartment
|
editor: hr.component.EditDepartment
|
||||||
})
|
})
|
||||||
|
@ -50,7 +50,8 @@ export default mergeIds(hrId, hr, {
|
|||||||
TzDatePresenter: '' as AnyComponent,
|
TzDatePresenter: '' as AnyComponent,
|
||||||
TzDateEditor: '' as AnyComponent,
|
TzDateEditor: '' as AnyComponent,
|
||||||
RequestPresenter: '' as AnyComponent,
|
RequestPresenter: '' as AnyComponent,
|
||||||
DepartmentPresenter: '' as AnyComponent
|
DepartmentPresenter: '' as AnyComponent,
|
||||||
|
DepartmentRefPresenter: '' as AnyComponent
|
||||||
},
|
},
|
||||||
category: {
|
category: {
|
||||||
HR: '' as Ref<ActionCategory>
|
HR: '' as Ref<ActionCategory>
|
||||||
|
@ -18,9 +18,7 @@ import activity, { type ActivityMessage } from '@hcengineering/activity'
|
|||||||
import chunter from '@hcengineering/chunter'
|
import chunter from '@hcengineering/chunter'
|
||||||
import {
|
import {
|
||||||
DOMAIN_MODEL,
|
DOMAIN_MODEL,
|
||||||
Hierarchy,
|
|
||||||
IndexKind,
|
IndexKind,
|
||||||
type Space,
|
|
||||||
type Account,
|
type Account,
|
||||||
type AttachedDoc,
|
type AttachedDoc,
|
||||||
type Class,
|
type Class,
|
||||||
@ -31,6 +29,7 @@ import {
|
|||||||
type Domain,
|
type Domain,
|
||||||
type Markup,
|
type Markup,
|
||||||
type Ref,
|
type Ref,
|
||||||
|
type Space,
|
||||||
type Timestamp,
|
type Timestamp,
|
||||||
type Tx
|
type Tx
|
||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
@ -700,11 +699,7 @@ export function generateClassNotificationTypes (
|
|||||||
ignoreKeys: string[] = [],
|
ignoreKeys: string[] = [],
|
||||||
defaultEnabled: string[] = []
|
defaultEnabled: string[] = []
|
||||||
): void {
|
): void {
|
||||||
const txes = builder.getTxes()
|
const hierarchy = builder.hierarchy
|
||||||
const hierarchy = new Hierarchy()
|
|
||||||
for (const tx of txes) {
|
|
||||||
hierarchy.tx(tx)
|
|
||||||
}
|
|
||||||
const attributes = hierarchy.getAllAttributes(
|
const attributes = hierarchy.getAllAttributes(
|
||||||
_class,
|
_class,
|
||||||
hierarchy.isDerived(_class, core.class.AttachedDoc) ? core.class.AttachedDoc : core.class.Doc
|
hierarchy.isDerived(_class, core.class.AttachedDoc) ? core.class.AttachedDoc : core.class.Doc
|
||||||
|
@ -23,7 +23,8 @@ import {
|
|||||||
type DocumentQuery,
|
type DocumentQuery,
|
||||||
type Domain,
|
type Domain,
|
||||||
type Ref,
|
type Ref,
|
||||||
type Space
|
type Space,
|
||||||
|
type AnyAttribute
|
||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
import { type Builder, Mixin, Model, UX } from '@hcengineering/model'
|
import { type Builder, Mixin, Model, UX } from '@hcengineering/model'
|
||||||
import core, { TClass, TDoc } from '@hcengineering/model-core'
|
import core, { TClass, TDoc } from '@hcengineering/model-core'
|
||||||
@ -87,10 +88,13 @@ import {
|
|||||||
type ViewletPreference,
|
type ViewletPreference,
|
||||||
type ObjectIdentifier,
|
type ObjectIdentifier,
|
||||||
type ObjectIcon,
|
type ObjectIcon,
|
||||||
type ObjectTooltip
|
type ObjectTooltip,
|
||||||
|
type AttrPresenter,
|
||||||
|
type AttributeCategory
|
||||||
} from '@hcengineering/view'
|
} from '@hcengineering/view'
|
||||||
|
|
||||||
import view from './plugin'
|
import view from './plugin'
|
||||||
|
import { classPresenter, createAction } from './utils'
|
||||||
|
|
||||||
export { viewId } from '@hcengineering/view'
|
export { viewId } from '@hcengineering/view'
|
||||||
export { viewOperation } from './migration'
|
export { viewOperation } from './migration'
|
||||||
@ -98,38 +102,7 @@ export type { ViewAction, Viewlet }
|
|||||||
|
|
||||||
export const DOMAIN_VIEW = 'view' as Domain
|
export const DOMAIN_VIEW = 'view' as Domain
|
||||||
|
|
||||||
export function createAction<T extends Doc = Doc, P = Record<string, any>> (
|
export * from './utils'
|
||||||
builder: Builder,
|
|
||||||
data: Data<Action<T, P>>,
|
|
||||||
id?: Ref<Action<T, P>>
|
|
||||||
): void {
|
|
||||||
const { label, ...adata } = data
|
|
||||||
builder.createDoc<Action<T, P>>(view.class.Action, core.space.Model, { label, ...adata }, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function classPresenter (
|
|
||||||
builder: Builder,
|
|
||||||
_class: Ref<Class<Doc>>,
|
|
||||||
presenter: AnyComponent,
|
|
||||||
editor?: AnyComponent,
|
|
||||||
popup?: AnyComponent,
|
|
||||||
activity?: AnyComponent
|
|
||||||
): void {
|
|
||||||
builder.mixin(_class, core.class.Class, view.mixin.AttributePresenter, {
|
|
||||||
presenter
|
|
||||||
})
|
|
||||||
if (editor !== undefined) {
|
|
||||||
builder.mixin(_class, core.class.Class, view.mixin.AttributeEditor, {
|
|
||||||
inlineEditor: editor,
|
|
||||||
popup
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (activity !== undefined) {
|
|
||||||
builder.mixin(_class, core.class.Class, view.mixin.ActivityAttributePresenter, {
|
|
||||||
presenter: activity
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Model(view.class.FilteredView, core.class.Doc, DOMAIN_VIEW)
|
@Model(view.class.FilteredView, core.class.Doc, DOMAIN_VIEW)
|
||||||
@UX(view.string.FilteredViews)
|
@UX(view.string.FilteredViews)
|
||||||
@ -383,6 +356,14 @@ export class TObjectPanel extends TClass implements ObjectPanel {
|
|||||||
component!: AnyComponent
|
component!: AnyComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Model(view.class.AttrPresenter, core.class.Doc, DOMAIN_MODEL)
|
||||||
|
export class TAttrPresenter extends TDoc implements AttrPresenter {
|
||||||
|
category!: AttributeCategory
|
||||||
|
objectClass!: Ref<Class<Doc<Space>>>
|
||||||
|
attribute!: Ref<AnyAttribute>
|
||||||
|
component!: AnyComponent
|
||||||
|
}
|
||||||
|
|
||||||
export type ActionTemplate = Partial<Data<Action>>
|
export type ActionTemplate = Partial<Data<Action>>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -467,7 +448,8 @@ export function createModel (builder: Builder): void {
|
|||||||
TGroupping,
|
TGroupping,
|
||||||
TObjectIdentifier,
|
TObjectIdentifier,
|
||||||
TObjectTooltip,
|
TObjectTooltip,
|
||||||
TObjectIcon
|
TObjectIcon,
|
||||||
|
TAttrPresenter
|
||||||
)
|
)
|
||||||
|
|
||||||
classPresenter(
|
classPresenter(
|
||||||
|
70
models/view/src/utils.ts
Normal file
70
models/view/src/utils.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
//
|
||||||
|
// Copyright © 2024 Hardcore Engineering Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License. You may
|
||||||
|
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
//
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import { type Class, type Data, type Doc, type Ref } from '@hcengineering/core'
|
||||||
|
import { type Builder } from '@hcengineering/model'
|
||||||
|
import core from '@hcengineering/model-core'
|
||||||
|
import { type AnyComponent } from '@hcengineering/ui'
|
||||||
|
import { type Action, type AttributeCategory } from '@hcengineering/view'
|
||||||
|
import view from '.'
|
||||||
|
|
||||||
|
export function createAction<T extends Doc = Doc, P = Record<string, any>> (
|
||||||
|
builder: Builder,
|
||||||
|
data: Data<Action<T, P>>,
|
||||||
|
id?: Ref<Action<T, P>>
|
||||||
|
): void {
|
||||||
|
const { label, ...adata } = data
|
||||||
|
builder.createDoc<Action<T, P>>(view.class.Action, core.space.Model, { label, ...adata }, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function classPresenter (
|
||||||
|
builder: Builder,
|
||||||
|
_class: Ref<Class<Doc>>,
|
||||||
|
presenter: AnyComponent,
|
||||||
|
editor?: AnyComponent,
|
||||||
|
popup?: AnyComponent,
|
||||||
|
activity?: AnyComponent
|
||||||
|
): void {
|
||||||
|
builder.mixin(_class, core.class.Class, view.mixin.AttributePresenter, {
|
||||||
|
presenter
|
||||||
|
})
|
||||||
|
if (editor !== undefined) {
|
||||||
|
builder.mixin(_class, core.class.Class, view.mixin.AttributeEditor, {
|
||||||
|
inlineEditor: editor,
|
||||||
|
popup
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (activity !== undefined) {
|
||||||
|
builder.mixin(_class, core.class.Class, view.mixin.ActivityAttributePresenter, {
|
||||||
|
presenter: activity
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createAttributePresenter<T extends Doc> (
|
||||||
|
builder: Builder,
|
||||||
|
component: AnyComponent,
|
||||||
|
_class: Ref<Class<T>>,
|
||||||
|
key: keyof T,
|
||||||
|
category: AttributeCategory
|
||||||
|
): void {
|
||||||
|
const attr = builder.hierarchy.getAttribute(_class, key as string)
|
||||||
|
builder.createDoc(view.class.AttrPresenter, core.space.Model, {
|
||||||
|
component,
|
||||||
|
attribute: attr._id,
|
||||||
|
objectClass: _class,
|
||||||
|
category
|
||||||
|
})
|
||||||
|
}
|
@ -27,6 +27,7 @@ import core, {
|
|||||||
Domain,
|
Domain,
|
||||||
Enum,
|
Enum,
|
||||||
EnumOf,
|
EnumOf,
|
||||||
|
Hierarchy,
|
||||||
Hyperlink,
|
Hyperlink,
|
||||||
Mixin as IMixin,
|
Mixin as IMixin,
|
||||||
IndexKind,
|
IndexKind,
|
||||||
@ -290,6 +291,7 @@ function _generateTx (tx: ClassTxes): Tx[] {
|
|||||||
*/
|
*/
|
||||||
export class Builder {
|
export class Builder {
|
||||||
private readonly txes: Tx[] = []
|
private readonly txes: Tx[] = []
|
||||||
|
readonly hierarchy = new Hierarchy()
|
||||||
|
|
||||||
onTx?: (tx: Tx) => void
|
onTx?: (tx: Tx) => void
|
||||||
|
|
||||||
@ -322,7 +324,7 @@ export class Builder {
|
|||||||
for (const tx of generated) {
|
for (const tx of generated) {
|
||||||
this.txes.push(tx)
|
this.txes.push(tx)
|
||||||
this.onTx?.(tx)
|
this.onTx?.(tx)
|
||||||
// this.hierarchy.tx(tx)
|
this.hierarchy.tx(tx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,6 +354,7 @@ export class Builder {
|
|||||||
}
|
}
|
||||||
this.txes.push(tx)
|
this.txes.push(tx)
|
||||||
this.onTx?.(tx)
|
this.onTx?.(tx)
|
||||||
|
this.hierarchy.tx(tx)
|
||||||
return TxProcessor.createDoc2Doc(tx)
|
return TxProcessor.createDoc2Doc(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,6 +367,7 @@ export class Builder {
|
|||||||
const tx = txFactory.createTxMixin(objectId, objectClass, core.space.Model, mixin, attributes)
|
const tx = txFactory.createTxMixin(objectId, objectClass, core.space.Model, mixin, attributes)
|
||||||
this.txes.push(tx)
|
this.txes.push(tx)
|
||||||
this.onTx?.(tx)
|
this.onTx?.(tx)
|
||||||
|
this.hierarchy.tx(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
getTxes (): Tx[] {
|
getTxes (): Tx[] {
|
||||||
|
@ -61,6 +61,12 @@
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export let sort: <T extends Doc>(a: T, b: T) => number = (a, b) => {
|
||||||
|
const aval: string = `${getObjectValue(groupBy, a as any)}`
|
||||||
|
const bval: string = `${getObjectValue(groupBy, b as any)}`
|
||||||
|
return aval.localeCompare(bval)
|
||||||
|
}
|
||||||
|
|
||||||
const created: Doc[] = []
|
const created: Doc[] = []
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
@ -84,11 +90,7 @@
|
|||||||
_id: { $nin: ignoreObjects, ..._idExtra }
|
_id: { $nin: ignoreObjects, ..._idExtra }
|
||||||
},
|
},
|
||||||
(result) => {
|
(result) => {
|
||||||
result.sort((a, b) => {
|
result.sort(sort)
|
||||||
const aval: string = `${getObjectValue(groupBy, a as any)}`
|
|
||||||
const bval: string = `${getObjectValue(groupBy, b as any)}`
|
|
||||||
return aval.localeCompare(bval)
|
|
||||||
})
|
|
||||||
if (created.length > 0) {
|
if (created.length > 0) {
|
||||||
const cmap = new Set(created.map((it) => it._id))
|
const cmap = new Set(created.map((it) => it._id))
|
||||||
objects = [...created, ...result.filter((d) => !cmap.has(d._id))].filter(filter)
|
objects = [...created, ...result.filter((d) => !cmap.has(d._id))].filter(filter)
|
||||||
|
@ -48,7 +48,7 @@ import core, {
|
|||||||
import { getMetadata, getResource } from '@hcengineering/platform'
|
import { getMetadata, getResource } from '@hcengineering/platform'
|
||||||
import { LiveQuery as LQ } from '@hcengineering/query'
|
import { LiveQuery as LQ } from '@hcengineering/query'
|
||||||
import { type AnyComponent, type AnySvelteComponent, type IconSize } from '@hcengineering/ui'
|
import { type AnyComponent, type AnySvelteComponent, type IconSize } from '@hcengineering/ui'
|
||||||
import view, { type AttributeEditor } from '@hcengineering/view'
|
import view, { type AttributeCategory, type AttributeEditor } from '@hcengineering/view'
|
||||||
import { deepEqual } from 'fast-equals'
|
import { deepEqual } from 'fast-equals'
|
||||||
import { onDestroy } from 'svelte'
|
import { onDestroy } from 'svelte'
|
||||||
import { type KeyedAttribute } from '..'
|
import { type KeyedAttribute } from '..'
|
||||||
@ -407,16 +407,6 @@ export async function copyTextToClipboard (text: string): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export type AttributeCategory = 'object' | 'attribute' | 'inplace' | 'collection' | 'array'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export const AttributeCategoryOrder = { attribute: 0, inplace: 1, collection: 2, array: 2, object: 3 }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Employee, PersonAccount } from '@hcengineering/contact'
|
import { Contact, Employee, PersonAccount, getName } from '@hcengineering/contact'
|
||||||
import core, { Account, Ref } from '@hcengineering/core'
|
import core, { Account, Ref, getCurrentAccount } from '@hcengineering/core'
|
||||||
import { IntlString } from '@hcengineering/platform'
|
import { IntlString } from '@hcengineering/platform'
|
||||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||||
import { ButtonKind, ButtonSize } from '@hcengineering/ui'
|
import { ButtonKind, ButtonSize } from '@hcengineering/ui'
|
||||||
@ -33,7 +33,7 @@
|
|||||||
export let includeItems: Ref<Account>[] | undefined = undefined
|
export let includeItems: Ref<Account>[] | undefined = undefined
|
||||||
export let excludeItems: Ref<Account>[] | undefined = undefined
|
export let excludeItems: Ref<Account>[] | undefined = undefined
|
||||||
export let emptyLabel: IntlString | undefined = undefined
|
export let emptyLabel: IntlString | undefined = undefined
|
||||||
export let allowGuests: boolean = true
|
export let allowGuests: boolean = false
|
||||||
|
|
||||||
let timer: any = null
|
let timer: any = null
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
@ -103,6 +103,29 @@
|
|||||||
: {})
|
: {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hierarchy = client.getHierarchy()
|
||||||
|
const me = getCurrentAccount() as PersonAccount
|
||||||
|
|
||||||
|
function sort (a: Contact, b: Contact): number {
|
||||||
|
if (me.person === a._id) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if (me.person === b._id) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
const aIncludes = employees.includes(a._id as Ref<Employee>)
|
||||||
|
const bIncludes = employees.includes(b._id as Ref<Employee>)
|
||||||
|
if (aIncludes && !bIncludes) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if (!aIncludes && bIncludes) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
const aName = getName(hierarchy, a)
|
||||||
|
const bName = getName(hierarchy, b)
|
||||||
|
return aName.localeCompare(bName)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<UserBoxList
|
<UserBoxList
|
||||||
@ -114,6 +137,7 @@
|
|||||||
{docQuery}
|
{docQuery}
|
||||||
on:update={onUpdate}
|
on:update={onUpdate}
|
||||||
{size}
|
{size}
|
||||||
|
{sort}
|
||||||
justify={'left'}
|
justify={'left'}
|
||||||
width={width ?? 'min-content'}
|
width={width ?? 'min-content'}
|
||||||
{kind}
|
{kind}
|
||||||
|
@ -4,14 +4,9 @@
|
|||||||
import notification from '@hcengineering/notification'
|
import notification from '@hcengineering/notification'
|
||||||
import { Panel } from '@hcengineering/panel'
|
import { Panel } from '@hcengineering/panel'
|
||||||
import { getResource } from '@hcengineering/platform'
|
import { getResource } from '@hcengineering/platform'
|
||||||
import presentation, {
|
import presentation, { createQuery, getClient, type KeyedAttribute } from '@hcengineering/presentation'
|
||||||
type AttributeCategory,
|
|
||||||
createQuery,
|
|
||||||
getClient,
|
|
||||||
type KeyedAttribute
|
|
||||||
} from '@hcengineering/presentation'
|
|
||||||
import { type AnyComponent, Button, Component, IconMixin, IconMoreH, Label } from '@hcengineering/ui'
|
import { type AnyComponent, Button, Component, IconMixin, IconMoreH, Label } from '@hcengineering/ui'
|
||||||
import view from '@hcengineering/view'
|
import view, { AttributeCategory } from '@hcengineering/view'
|
||||||
import {
|
import {
|
||||||
DocAttributeBar,
|
DocAttributeBar,
|
||||||
DocNavLink,
|
DocNavLink,
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
<!--
|
||||||
|
// Copyright © 2024 Hardcore Engineering Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License. You may
|
||||||
|
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
//
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
-->
|
||||||
|
<script lang="ts">
|
||||||
|
import { Account, Ref, getCurrentAccount } from '@hcengineering/core'
|
||||||
|
import { IntlString } from '@hcengineering/platform'
|
||||||
|
import { Button, ButtonKind, ButtonSize } from '@hcengineering/ui'
|
||||||
|
import view from '@hcengineering/view'
|
||||||
|
import AccountArrayEditor from './AccountArrayEditor.svelte'
|
||||||
|
|
||||||
|
export let label: IntlString
|
||||||
|
export let value: Ref<Account>[]
|
||||||
|
export let onChange: ((refs: Ref<Account>[]) => void) | undefined
|
||||||
|
export let readonly = false
|
||||||
|
export let kind: ButtonKind = 'link'
|
||||||
|
export let size: ButtonSize = 'large'
|
||||||
|
export let width: string | undefined = undefined
|
||||||
|
|
||||||
|
const me = getCurrentAccount()._id
|
||||||
|
|
||||||
|
$: joined = value.includes(me)
|
||||||
|
|
||||||
|
function join (): void {
|
||||||
|
if (value.includes(me)) return
|
||||||
|
if (onChange === undefined) return
|
||||||
|
onChange([...value, me])
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if !joined && onChange !== undefined}
|
||||||
|
<Button label={view.string.Join} {size} {width} kind={'primary'} on:click={join} />
|
||||||
|
{:else}
|
||||||
|
<AccountArrayEditor {label} {value} {onChange} {readonly} {kind} {size} {width} allowGuests />
|
||||||
|
{/if}
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { Employee, Person } from '@hcengineering/contact'
|
import contact, { Contact, Employee, Person } from '@hcengineering/contact'
|
||||||
import type { Class, Doc, DocumentQuery, Ref } from '@hcengineering/core'
|
import type { Class, Doc, DocumentQuery, Ref } from '@hcengineering/core'
|
||||||
import type { IntlString } from '@hcengineering/platform'
|
import type { IntlString } from '@hcengineering/platform'
|
||||||
import { ObjectCreate, getClient } from '@hcengineering/presentation'
|
import { ObjectCreate, getClient } from '@hcengineering/presentation'
|
||||||
@ -27,9 +27,9 @@
|
|||||||
import UsersPopup from './UsersPopup.svelte'
|
import UsersPopup from './UsersPopup.svelte'
|
||||||
import Members from './icons/Members.svelte'
|
import Members from './icons/Members.svelte'
|
||||||
|
|
||||||
export let items: Ref<Employee>[] = []
|
export let items: Ref<Person>[] = []
|
||||||
export let _class: Ref<Class<Employee>> = contact.mixin.Employee
|
export let _class: Ref<Class<Person>> = contact.mixin.Employee
|
||||||
export let docQuery: DocumentQuery<Employee> | undefined = {}
|
export let docQuery: DocumentQuery<Person> | undefined = {}
|
||||||
|
|
||||||
export let label: IntlString | undefined = undefined
|
export let label: IntlString | undefined = undefined
|
||||||
export let kind: ButtonKind = 'no-border'
|
export let kind: ButtonKind = 'no-border'
|
||||||
@ -41,16 +41,18 @@
|
|||||||
export let readonly: boolean = false
|
export let readonly: boolean = false
|
||||||
export let create: ObjectCreate | undefined = undefined
|
export let create: ObjectCreate | undefined = undefined
|
||||||
|
|
||||||
function filter (items: Ref<Employee>[]): Ref<Employee>[] {
|
export let sort: ((a: Person, b: Person) => number) | undefined = undefined
|
||||||
|
|
||||||
|
function filter (items: Ref<Person>[]): Ref<Person>[] {
|
||||||
return items.filter((it, idx, arr) => arr.indexOf(it) === idx)
|
return items.filter((it, idx, arr) => arr.indexOf(it) === idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
let persons: Person[] = filter(items)
|
let persons: Person[] = filter(items)
|
||||||
.map((p) => $personByIdStore.get(p))
|
.map((p) => $personByIdStore.get(p))
|
||||||
.filter((p) => p !== undefined) as Employee[]
|
.filter((p) => p !== undefined) as Person[]
|
||||||
$: persons = filter(items)
|
$: persons = filter(items)
|
||||||
.map((p) => $personByIdStore.get(p))
|
.map((p) => $personByIdStore.get(p))
|
||||||
.filter((p) => p !== undefined) as Employee[]
|
.filter((p) => p !== undefined) as Person[]
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
@ -62,36 +64,34 @@
|
|||||||
.findAllSync(contact.class.PersonAccount, {})
|
.findAllSync(contact.class.PersonAccount, {})
|
||||||
.map((p) => p.person)
|
.map((p) => p.person)
|
||||||
)
|
)
|
||||||
showPopup(
|
const popupProps: any = {
|
||||||
UsersPopup,
|
_class,
|
||||||
{
|
label,
|
||||||
_class,
|
docQuery,
|
||||||
label,
|
multiSelect: true,
|
||||||
docQuery,
|
allowDeselect: false,
|
||||||
multiSelect: true,
|
selectedUsers: filter(items),
|
||||||
allowDeselect: false,
|
filter: (it: Doc) => {
|
||||||
selectedUsers: filter(items),
|
const h = client.getHierarchy()
|
||||||
filter: (it: Doc) => {
|
if (h.hasMixin(it, contact.mixin.Employee)) {
|
||||||
const h = client.getHierarchy()
|
const isActive = h.as(it, contact.mixin.Employee).active
|
||||||
if (h.hasMixin(it, contact.mixin.Employee)) {
|
const isSelected = items.some((selectedItem) => selectedItem === it._id)
|
||||||
const isActive = h.as(it, contact.mixin.Employee).active
|
return isActive || isSelected
|
||||||
const isSelected = items.some((selectedItem) => selectedItem === it._id)
|
|
||||||
return isActive || isSelected
|
|
||||||
}
|
|
||||||
return accounts.has(it._id as Ref<Person>)
|
|
||||||
},
|
|
||||||
readonly,
|
|
||||||
create
|
|
||||||
},
|
|
||||||
evt.target as HTMLElement,
|
|
||||||
undefined,
|
|
||||||
(result) => {
|
|
||||||
if (result != null) {
|
|
||||||
items = filter(result)
|
|
||||||
dispatch('update', items)
|
|
||||||
}
|
}
|
||||||
|
return accounts.has(it._id as Ref<Person>)
|
||||||
|
},
|
||||||
|
readonly,
|
||||||
|
create
|
||||||
|
}
|
||||||
|
if (sort !== undefined) {
|
||||||
|
popupProps.sort = sort
|
||||||
|
}
|
||||||
|
showPopup(UsersPopup, popupProps, evt.target as HTMLElement, undefined, (result) => {
|
||||||
|
if (result != null) {
|
||||||
|
items = filter(result)
|
||||||
|
dispatch('update', items)
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { Contact, getFirstName, getLastName, Person } from '@hcengineering/contact'
|
import contact, { Contact, getFirstName, getLastName, getName, Person } from '@hcengineering/contact'
|
||||||
import type { Class, Doc, DocumentQuery, FindOptions, Ref } from '@hcengineering/core'
|
import type { Class, Doc, DocumentQuery, FindOptions, Ref } from '@hcengineering/core'
|
||||||
import type { Asset, IntlString } from '@hcengineering/platform'
|
import type { Asset, IntlString } from '@hcengineering/platform'
|
||||||
import presentation, { getClient, ObjectCreate, ObjectPopup } from '@hcengineering/presentation'
|
import presentation, { getClient, ObjectCreate, ObjectPopup } from '@hcengineering/presentation'
|
||||||
@ -35,6 +35,14 @@
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hierarchy = getClient().getHierarchy()
|
||||||
|
|
||||||
|
export let sort: (a: Doc, b: Doc) => number = (a, b) => {
|
||||||
|
const aName = getName(hierarchy, a as Contact)
|
||||||
|
const bName = getName(hierarchy, b as Contact)
|
||||||
|
return aName.localeCompare(bName)
|
||||||
|
}
|
||||||
|
|
||||||
export let multiSelect: boolean = false
|
export let multiSelect: boolean = false
|
||||||
export let allowDeselect: boolean = false
|
export let allowDeselect: boolean = false
|
||||||
export let titleDeselect: IntlString | undefined = undefined
|
export let titleDeselect: IntlString | undefined = undefined
|
||||||
@ -46,7 +54,6 @@
|
|||||||
export let create: ObjectCreate | undefined = undefined
|
export let create: ObjectCreate | undefined = undefined
|
||||||
export let readonly = false
|
export let readonly = false
|
||||||
|
|
||||||
const hierarchy = getClient().getHierarchy()
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
$: _create =
|
$: _create =
|
||||||
@ -72,6 +79,7 @@
|
|||||||
type={'object'}
|
type={'object'}
|
||||||
docQuery={readonly ? { ...docQuery, _id: { $in: selectedUsers } } : docQuery}
|
docQuery={readonly ? { ...docQuery, _id: { $in: selectedUsers } } : docQuery}
|
||||||
{filter}
|
{filter}
|
||||||
|
{sort}
|
||||||
groupBy={'_class'}
|
groupBy={'_class'}
|
||||||
bind:selectedObjects={selectedUsers}
|
bind:selectedObjects={selectedUsers}
|
||||||
bind:ignoreObjects={ignoreUsers}
|
bind:ignoreObjects={ignoreUsers}
|
||||||
|
@ -107,6 +107,7 @@ import UserDetails from './components/UserDetails.svelte'
|
|||||||
import EditOrganizationPanel from './components/EditOrganizationPanel.svelte'
|
import EditOrganizationPanel from './components/EditOrganizationPanel.svelte'
|
||||||
import ChannelIcon from './components/ChannelIcon.svelte'
|
import ChannelIcon from './components/ChannelIcon.svelte'
|
||||||
import CreateGuest from './components/CreateGuest.svelte'
|
import CreateGuest from './components/CreateGuest.svelte'
|
||||||
|
import SpaceMembersEditor from './components/SpaceMembersEditor.svelte'
|
||||||
|
|
||||||
import contact from './plugin'
|
import contact from './plugin'
|
||||||
import {
|
import {
|
||||||
@ -342,7 +343,8 @@ export default async (): Promise<Resources> => ({
|
|||||||
PersonAccountRefPresenter,
|
PersonAccountRefPresenter,
|
||||||
PersonIcon,
|
PersonIcon,
|
||||||
EditOrganizationPanel,
|
EditOrganizationPanel,
|
||||||
ChannelIcon
|
ChannelIcon,
|
||||||
|
SpaceMembersEditor
|
||||||
},
|
},
|
||||||
completion: {
|
completion: {
|
||||||
EmployeeQuery: async (
|
EmployeeQuery: async (
|
||||||
|
@ -188,7 +188,8 @@ export const contactPlugin = plugin(contactId, {
|
|||||||
PersonIcon: '' as AnyComponent,
|
PersonIcon: '' as AnyComponent,
|
||||||
EditOrganizationPanel: '' as AnyComponent,
|
EditOrganizationPanel: '' as AnyComponent,
|
||||||
CollaborationUserAvatar: '' as AnyComponent,
|
CollaborationUserAvatar: '' as AnyComponent,
|
||||||
CreateGuest: '' as AnyComponent
|
CreateGuest: '' as AnyComponent,
|
||||||
|
SpaceMembersEditor: '' as AnyComponent
|
||||||
},
|
},
|
||||||
channelProvider: {
|
channelProvider: {
|
||||||
Email: '' as Ref<ChannelProvider>,
|
Email: '' as Ref<ChannelProvider>,
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
<!--
|
||||||
|
// Copyright © 2024 Hardcore Engineering Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License. You may
|
||||||
|
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
//
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
-->
|
||||||
|
<script lang="ts">
|
||||||
|
import { Ref } from '@hcengineering/core'
|
||||||
|
import hr, { Department } from '@hcengineering/hr'
|
||||||
|
import DepartmentPresenter from './DepartmentPresenter.svelte'
|
||||||
|
import { createQuery } from '@hcengineering/presentation'
|
||||||
|
|
||||||
|
export let value: Ref<Department>
|
||||||
|
|
||||||
|
let department: Department | undefined
|
||||||
|
|
||||||
|
const query = createQuery()
|
||||||
|
query.query(hr.class.Department, { _id: value }, (result) => {
|
||||||
|
department = result[0]
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if department}
|
||||||
|
<DepartmentPresenter value={department} />
|
||||||
|
{/if}
|
@ -28,6 +28,7 @@ import RequestPresenter from './components/RequestPresenter.svelte'
|
|||||||
import { showPopup } from '@hcengineering/ui'
|
import { showPopup } from '@hcengineering/ui'
|
||||||
import { type Request } from '@hcengineering/hr'
|
import { type Request } from '@hcengineering/hr'
|
||||||
import EditRequestType from './components/EditRequestType.svelte'
|
import EditRequestType from './components/EditRequestType.svelte'
|
||||||
|
import DepartmentRefPresenter from './components/DepartmentRefPresenter.svelte'
|
||||||
|
|
||||||
async function editRequestType (object: Request): Promise<void> {
|
async function editRequestType (object: Request): Promise<void> {
|
||||||
showPopup(EditRequestType, { object })
|
showPopup(EditRequestType, { object })
|
||||||
@ -46,7 +47,8 @@ export default async (): Promise<Resources> => ({
|
|||||||
TzDateEditor,
|
TzDateEditor,
|
||||||
RequestPresenter,
|
RequestPresenter,
|
||||||
EditRequestType,
|
EditRequestType,
|
||||||
DepartmentPresenter
|
DepartmentPresenter,
|
||||||
|
DepartmentRefPresenter
|
||||||
},
|
},
|
||||||
actionImpl: {
|
actionImpl: {
|
||||||
EditRequestType: editRequestType
|
EditRequestType: editRequestType
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
import { getResource } from '@hcengineering/platform'
|
import { getResource } from '@hcengineering/platform'
|
||||||
import {
|
import {
|
||||||
ActionContext,
|
ActionContext,
|
||||||
AttributeCategory,
|
|
||||||
AttributesBar,
|
AttributesBar,
|
||||||
KeyedAttribute,
|
KeyedAttribute,
|
||||||
createQuery,
|
createQuery,
|
||||||
@ -29,7 +28,7 @@
|
|||||||
reduceCalls
|
reduceCalls
|
||||||
} from '@hcengineering/presentation'
|
} from '@hcengineering/presentation'
|
||||||
import { AnyComponent, Button, Component, IconMixin, IconMoreH } from '@hcengineering/ui'
|
import { AnyComponent, Button, Component, IconMixin, IconMoreH } from '@hcengineering/ui'
|
||||||
import view from '@hcengineering/view'
|
import view, { AttributeCategory } from '@hcengineering/view'
|
||||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||||
|
|
||||||
import { DocNavLink, ParentsNavigator, getDocAttrsInfo, getDocLabel, getDocMixins, showMenu } from '..'
|
import { DocNavLink, ParentsNavigator, getDocAttrsInfo, getDocLabel, getDocMixins, showMenu } from '..'
|
||||||
|
@ -152,9 +152,9 @@
|
|||||||
if (hierarchy.isDerived(type._class, core.class.RefTo)) {
|
if (hierarchy.isDerived(type._class, core.class.RefTo)) {
|
||||||
return '$lookup.' + name
|
return '$lookup.' + name
|
||||||
}
|
}
|
||||||
if (hierarchy.isDerived(type._class, core.class.ArrOf)) {
|
// if (hierarchy.isDerived(type._class, core.class.ArrOf)) {
|
||||||
return getValue(name, (type as ArrOf<any>).of)
|
// return getValue(name, (type as ArrOf<any>).of)
|
||||||
}
|
// }
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const key = { key: filter.key.key }
|
const key = { key: filter.key.key }
|
||||||
const promise = getPresenter(client, filter.key._class, key, key)
|
const promise = getPresenter(client, filter.key._class, key, key, undefined, false, 'attribute')
|
||||||
|
|
||||||
let values = new Set<any>()
|
let values = new Set<any>()
|
||||||
let selectedValues: Set<any> = new Set<any>(filter.value.map((p) => p[0]))
|
let selectedValues: Set<any> = new Set<any>(filter.value.map((p) => p[0]))
|
||||||
|
@ -36,6 +36,7 @@ import core, {
|
|||||||
type Lookup,
|
type Lookup,
|
||||||
type Mixin,
|
type Mixin,
|
||||||
type Obj,
|
type Obj,
|
||||||
|
type Permission,
|
||||||
type Ref,
|
type Ref,
|
||||||
type RefTo,
|
type RefTo,
|
||||||
type ReverseLookup,
|
type ReverseLookup,
|
||||||
@ -48,22 +49,19 @@ import core, {
|
|||||||
type TxOperations,
|
type TxOperations,
|
||||||
type TxUpdateDoc,
|
type TxUpdateDoc,
|
||||||
type TypeAny,
|
type TypeAny,
|
||||||
type TypedSpace,
|
type TypedSpace
|
||||||
type Permission
|
|
||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
import { type Restrictions } from '@hcengineering/guest'
|
import { type Restrictions } from '@hcengineering/guest'
|
||||||
import type { Asset, IntlString } from '@hcengineering/platform'
|
import type { Asset, IntlString } from '@hcengineering/platform'
|
||||||
import { getResource, translate } from '@hcengineering/platform'
|
import { getResource, translate } from '@hcengineering/platform'
|
||||||
import {
|
import {
|
||||||
type AttributeCategory,
|
createQuery,
|
||||||
AttributeCategoryOrder,
|
|
||||||
getAttributePresenterClass,
|
getAttributePresenterClass,
|
||||||
getClient,
|
getClient,
|
||||||
hasResource,
|
|
||||||
type KeyedAttribute,
|
|
||||||
getFiltredKeys,
|
getFiltredKeys,
|
||||||
|
hasResource,
|
||||||
isAdminUser,
|
isAdminUser,
|
||||||
createQuery
|
type KeyedAttribute
|
||||||
} from '@hcengineering/presentation'
|
} from '@hcengineering/presentation'
|
||||||
import { type CollaborationUser } from '@hcengineering/text-editor'
|
import { type CollaborationUser } from '@hcengineering/text-editor'
|
||||||
import {
|
import {
|
||||||
@ -81,8 +79,10 @@ import {
|
|||||||
type Location
|
type Location
|
||||||
} from '@hcengineering/ui'
|
} from '@hcengineering/ui'
|
||||||
import view, {
|
import view, {
|
||||||
type AttributePresenter,
|
AttributeCategoryOrder,
|
||||||
|
type AttributeCategory,
|
||||||
type AttributeModel,
|
type AttributeModel,
|
||||||
|
type AttributePresenter,
|
||||||
type BuildModelKey,
|
type BuildModelKey,
|
||||||
type BuildModelOptions,
|
type BuildModelOptions,
|
||||||
type CollectionPresenter,
|
type CollectionPresenter,
|
||||||
@ -179,37 +179,58 @@ export async function getAttributePresenter (
|
|||||||
_class: Ref<Class<Obj>>,
|
_class: Ref<Class<Obj>>,
|
||||||
key: string,
|
key: string,
|
||||||
preserveKey: BuildModelKey,
|
preserveKey: BuildModelKey,
|
||||||
mixinClass?: Ref<Mixin<CollectionPresenter>>
|
mixinClass?: Ref<Mixin<CollectionPresenter>>,
|
||||||
|
_category?: AttributeCategory
|
||||||
): Promise<AttributeModel> {
|
): Promise<AttributeModel> {
|
||||||
const actualMixinClass = mixinClass ?? view.mixin.AttributePresenter
|
const actualMixinClass = mixinClass ?? view.mixin.AttributePresenter
|
||||||
|
|
||||||
const hierarchy = client.getHierarchy()
|
const hierarchy = client.getHierarchy()
|
||||||
const attribute = hierarchy.getAttribute(_class, key)
|
const attribute = hierarchy.getAttribute(_class, key)
|
||||||
|
let { attrClass, category } = getAttributePresenterClass(hierarchy, attribute)
|
||||||
|
if (_category !== undefined) {
|
||||||
|
category = _category
|
||||||
|
}
|
||||||
|
|
||||||
const presenterClass = getAttributePresenterClass(hierarchy, attribute)
|
let overridedPresenter = await client
|
||||||
const isCollectionAttr = presenterClass.category === 'collection'
|
.getModel()
|
||||||
|
.findOne(view.class.AttrPresenter, { objectClass: _class, attribute: attribute._id, category })
|
||||||
|
if (overridedPresenter === undefined) {
|
||||||
|
overridedPresenter = await client
|
||||||
|
.getModel()
|
||||||
|
.findOne(view.class.AttrPresenter, { attribute: attribute._id, category })
|
||||||
|
}
|
||||||
|
|
||||||
|
const isCollectionAttr = category === 'collection'
|
||||||
const mixin = isCollectionAttr ? view.mixin.CollectionPresenter : actualMixinClass
|
const mixin = isCollectionAttr ? view.mixin.CollectionPresenter : actualMixinClass
|
||||||
|
|
||||||
let presenterMixin: AttributePresenter | CollectionPresenter | undefined = hierarchy.classHierarchyMixin(
|
let presenterMixin: AttributePresenter | CollectionPresenter | undefined = hierarchy.classHierarchyMixin(
|
||||||
presenterClass.attrClass,
|
attrClass,
|
||||||
mixin
|
mixin
|
||||||
)
|
)
|
||||||
|
|
||||||
if (presenterMixin?.presenter === undefined && mixinClass != null && mixin === mixinClass) {
|
if (presenterMixin?.presenter === undefined && mixinClass != null && mixin === mixinClass) {
|
||||||
presenterMixin = hierarchy.classHierarchyMixin(presenterClass.attrClass, view.mixin.AttributePresenter)
|
presenterMixin = hierarchy.classHierarchyMixin(attrClass, view.mixin.AttributePresenter)
|
||||||
}
|
}
|
||||||
|
|
||||||
let presenter: AnySvelteComponent
|
let presenter: AnySvelteComponent | undefined
|
||||||
|
|
||||||
const attributePresenter = presenterMixin as AttributePresenter
|
if (overridedPresenter !== undefined) {
|
||||||
if (presenterClass.category === 'array' && attributePresenter.arrayPresenter !== undefined) {
|
presenter = await getResource(overridedPresenter.component)
|
||||||
presenter = await getResource(attributePresenter.arrayPresenter)
|
}
|
||||||
} else if (presenterMixin?.presenter !== undefined) {
|
|
||||||
presenter = await getResource(presenterMixin.presenter)
|
if (presenter === undefined) {
|
||||||
} else if (presenterClass.attrClass === core.class.TypeAny) {
|
const attributePresenter = presenterMixin as AttributePresenter
|
||||||
const typeAny = attribute.type as TypeAny
|
if (category === 'array' && attributePresenter.arrayPresenter !== undefined) {
|
||||||
presenter = await getResource(typeAny.presenter)
|
presenter = await getResource(attributePresenter.arrayPresenter)
|
||||||
} else {
|
} else if (presenterMixin?.presenter !== undefined) {
|
||||||
|
presenter = await getResource(presenterMixin.presenter)
|
||||||
|
} else if (attrClass === core.class.TypeAny) {
|
||||||
|
const typeAny = attribute.type as TypeAny
|
||||||
|
presenter = await getResource(typeAny.presenter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (presenter === undefined) {
|
||||||
throw new Error('attribute presenter not found for ' + JSON.stringify(preserveKey))
|
throw new Error('attribute presenter not found for ' + JSON.stringify(preserveKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,7 +244,7 @@ export async function getAttributePresenter (
|
|||||||
return {
|
return {
|
||||||
key: preserveKey.key,
|
key: preserveKey.key,
|
||||||
sortingKey,
|
sortingKey,
|
||||||
_class: presenterClass.attrClass,
|
_class: attrClass,
|
||||||
label: preserveKey.label ?? attribute.shortLabel ?? attribute.label,
|
label: preserveKey.label ?? attribute.shortLabel ?? attribute.label,
|
||||||
presenter,
|
presenter,
|
||||||
props: preserveKey.props,
|
props: preserveKey.props,
|
||||||
@ -265,7 +286,8 @@ export async function getPresenter<T extends Doc> (
|
|||||||
key: BuildModelKey,
|
key: BuildModelKey,
|
||||||
preserveKey: BuildModelKey,
|
preserveKey: BuildModelKey,
|
||||||
lookup?: Lookup<T>,
|
lookup?: Lookup<T>,
|
||||||
isCollectionAttr: boolean = false
|
isCollectionAttr: boolean = false,
|
||||||
|
_category?: AttributeCategory
|
||||||
): Promise<AttributeModel> {
|
): Promise<AttributeModel> {
|
||||||
if (key.presenter !== undefined) {
|
if (key.presenter !== undefined) {
|
||||||
const { presenter, label, sortingKey } = key
|
const { presenter, label, sortingKey } = key
|
||||||
@ -294,7 +316,7 @@ export async function getPresenter<T extends Doc> (
|
|||||||
}
|
}
|
||||||
return await getLookupPresenter(client, _class, key, preserveKey, lookup)
|
return await getLookupPresenter(client, _class, key, preserveKey, lookup)
|
||||||
}
|
}
|
||||||
return await getAttributePresenter(client, _class, key.key, preserveKey)
|
return await getAttributePresenter(client, _class, key.key, preserveKey, undefined, _category)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@ import {
|
|||||||
ObjectTitle,
|
ObjectTitle,
|
||||||
ObjectTooltip,
|
ObjectTooltip,
|
||||||
ObjectValidator,
|
ObjectValidator,
|
||||||
|
AttrPresenter,
|
||||||
PreviewPresenter,
|
PreviewPresenter,
|
||||||
SpaceHeader,
|
SpaceHeader,
|
||||||
SpaceName,
|
SpaceName,
|
||||||
@ -116,7 +117,8 @@ const view = plugin(viewId, {
|
|||||||
ActionCategory: '' as Ref<Class<ActionCategory>>,
|
ActionCategory: '' as Ref<Class<ActionCategory>>,
|
||||||
LinkPresenter: '' as Ref<Class<LinkPresenter>>,
|
LinkPresenter: '' as Ref<Class<LinkPresenter>>,
|
||||||
FilterMode: '' as Ref<Class<FilterMode>>,
|
FilterMode: '' as Ref<Class<FilterMode>>,
|
||||||
FilteredView: '' as Ref<Class<FilteredView>>
|
FilteredView: '' as Ref<Class<FilteredView>>,
|
||||||
|
AttrPresenter: '' as Ref<Class<AttrPresenter>>
|
||||||
},
|
},
|
||||||
action: {
|
action: {
|
||||||
Delete: '' as Ref<Action>,
|
Delete: '' as Ref<Action>,
|
||||||
|
@ -780,4 +780,21 @@ export interface IconProps {
|
|||||||
color?: number
|
color?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type AttributeCategory = 'attribute' | 'inplace' | 'collection' | 'array' | 'object'
|
||||||
|
|
||||||
|
export const AttributeCategoryOrder: Record<AttributeCategory, number> = {
|
||||||
|
attribute: 0,
|
||||||
|
inplace: 1,
|
||||||
|
collection: 2,
|
||||||
|
array: 3,
|
||||||
|
object: 4
|
||||||
|
}
|
||||||
|
|
||||||
export type ObjectPresenterType = 'link' | 'text'
|
export type ObjectPresenterType = 'link' | 'text'
|
||||||
|
|
||||||
|
export interface AttrPresenter extends Doc {
|
||||||
|
attribute: Ref<AnyAttribute>
|
||||||
|
category: AttributeCategory
|
||||||
|
objectClass: Ref<Class<Doc>>
|
||||||
|
component: AnyComponent
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user