mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 11:01:54 +03:00
EditDoc (#834)
Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
parent
3d21b2de1e
commit
b56f3bba7c
@ -19,7 +19,6 @@ import type { Ref, Doc, Class, Domain } from '@anticrm/core'
|
||||
import { IndexKind } from '@anticrm/core'
|
||||
import core, { TSpace, TDoc, TAttachedDoc } from '@anticrm/model-core'
|
||||
import type { Backlink, Channel, Message, Comment } from '@anticrm/chunter'
|
||||
import type { AnyComponent } from '@anticrm/ui'
|
||||
import activity from '@anticrm/activity'
|
||||
|
||||
import workbench from '@anticrm/model-workbench'
|
||||
@ -72,7 +71,6 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: chunter.class.Message,
|
||||
descriptor: chunter.viewlet.Chat,
|
||||
open: 'X' as AnyComponent,
|
||||
config: {}
|
||||
})
|
||||
|
||||
|
@ -78,8 +78,7 @@ export class TContact extends TDoc implements Contact {
|
||||
|
||||
@Model(contact.class.Person, contact.class.Contact)
|
||||
@UX('Person' as IntlString, contact.icon.Person, undefined, 'name')
|
||||
export class TPerson extends TContact implements Person {
|
||||
}
|
||||
export class TPerson extends TContact implements Person {}
|
||||
|
||||
@Model(contact.class.Organization, contact.class.Contact)
|
||||
@UX('Organization' as IntlString, contact.icon.Company, undefined, 'name')
|
||||
@ -124,17 +123,21 @@ export function createModel (builder: Builder): void {
|
||||
component: contact.component.CreateOrganization
|
||||
})
|
||||
|
||||
builder.createDoc(workbench.class.Application, core.space.Model, {
|
||||
builder.createDoc(
|
||||
workbench.class.Application,
|
||||
core.space.Model,
|
||||
{
|
||||
label: contact.string.Contacts,
|
||||
icon: contact.icon.Person,
|
||||
hidden: false,
|
||||
component: contact.component.Contacts
|
||||
}, contact.app.Contacts)
|
||||
},
|
||||
contact.app.Contacts
|
||||
)
|
||||
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: contact.class.Contact,
|
||||
descriptor: view.viewlet.Table,
|
||||
open: contact.component.EditContact,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {},
|
||||
config: [
|
||||
@ -142,7 +145,7 @@ export function createModel (builder: Builder): void {
|
||||
'city',
|
||||
{ presenter: attachment.component.AttachmentsPresenter, label: 'Files', sortingKey: 'attachments' },
|
||||
'modifiedOn',
|
||||
{ presenter: contact.component.RolePresenter, label: 'Role' },
|
||||
{ presenter: view.component.RolePresenter, label: 'Role' },
|
||||
'channels'
|
||||
]
|
||||
})
|
||||
|
@ -31,14 +31,12 @@ export const ids = mergeIds(contactId, contact, {
|
||||
ChannelsPresenter: '' as AnyComponent,
|
||||
CreatePerson: '' as AnyComponent,
|
||||
EditPerson: '' as AnyComponent,
|
||||
EditContact: '' as AnyComponent,
|
||||
EditOrganization: '' as AnyComponent,
|
||||
CreateOrganization: '' as AnyComponent,
|
||||
CreatePersons: '' as AnyComponent,
|
||||
CreateOrganizations: '' as AnyComponent,
|
||||
OrganizationPresenter: '' as AnyComponent,
|
||||
Contacts: '' as AnyComponent,
|
||||
RolePresenter: '' as AnyComponent
|
||||
Contacts: '' as AnyComponent
|
||||
},
|
||||
string: {
|
||||
Organizations: '' as IntlString,
|
||||
|
@ -87,10 +87,13 @@ export function createModel (builder: Builder): void {
|
||||
editor: inventory.component.Variants
|
||||
})
|
||||
|
||||
builder.mixin(inventory.class.Product, core.class.Class, view.mixin.ObjectEditor, {
|
||||
editor: inventory.component.EditProduct
|
||||
})
|
||||
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: inventory.class.Product,
|
||||
descriptor: view.viewlet.Table,
|
||||
open: inventory.component.EditProduct,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {
|
||||
lookup: {
|
||||
|
@ -27,7 +27,7 @@ import task, { TSpaceWithStates, TTask } from '@anticrm/model-task'
|
||||
import view from '@anticrm/model-view'
|
||||
import workbench from '@anticrm/model-workbench'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import type { } from '@anticrm/view'
|
||||
import type {} from '@anticrm/view'
|
||||
import lead from './plugin'
|
||||
|
||||
@Model(lead.class.Funnel, task.class.SpaceWithStates)
|
||||
@ -118,7 +118,6 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: lead.class.Lead,
|
||||
descriptor: view.viewlet.Table,
|
||||
open: contact.component.EditContact,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {
|
||||
lookup: {
|
||||
@ -140,7 +139,6 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: lead.class.Lead,
|
||||
descriptor: task.viewlet.Kanban,
|
||||
open: contact.component.EditContact,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {
|
||||
lookup: {
|
||||
|
@ -150,7 +150,6 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: recruit.mixin.Candidate,
|
||||
descriptor: view.viewlet.Table,
|
||||
open: contact.component.EditContact,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {
|
||||
// lookup: {
|
||||
@ -172,7 +171,6 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: recruit.class.Applicant,
|
||||
descriptor: view.viewlet.Table,
|
||||
open: contact.component.EditContact,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {
|
||||
lookup: {
|
||||
@ -198,7 +196,6 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: recruit.class.Applicant,
|
||||
descriptor: task.viewlet.Kanban,
|
||||
open: contact.component.EditContact,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {
|
||||
lookup: {
|
||||
@ -212,7 +209,6 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: recruit.class.Applicant,
|
||||
descriptor: task.viewlet.StatusTable,
|
||||
open: contact.component.EditContact,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {
|
||||
lookup: {
|
||||
|
@ -20,7 +20,20 @@ import attachment from '@anticrm/model-attachment'
|
||||
import type { Employee } from '@anticrm/contact'
|
||||
import contact from '@anticrm/contact'
|
||||
import { Arr, Class, Doc, Domain, DOMAIN_MODEL, FindOptions, Ref, Space, Timestamp } from '@anticrm/core'
|
||||
import { Builder, Collection, Implements, Mixin, Model, Prop, Hidden, TypeBoolean, TypeDate, TypeRef, TypeString, UX } from '@anticrm/model'
|
||||
import {
|
||||
Builder,
|
||||
Collection,
|
||||
Implements,
|
||||
Mixin,
|
||||
Model,
|
||||
Prop,
|
||||
Hidden,
|
||||
TypeBoolean,
|
||||
TypeDate,
|
||||
TypeRef,
|
||||
TypeString,
|
||||
UX
|
||||
} from '@anticrm/model'
|
||||
import chunter from '@anticrm/model-chunter'
|
||||
import core, { TAttachedDoc, TClass, TDoc, TSpace } from '@anticrm/model-core'
|
||||
import view from '@anticrm/model-view'
|
||||
@ -281,7 +294,6 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: task.class.Issue,
|
||||
descriptor: view.viewlet.Table,
|
||||
open: task.component.EditTask,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {
|
||||
lookup: {
|
||||
@ -306,10 +318,13 @@ export function createModel (builder: Builder): void {
|
||||
editor: task.component.EditIssue
|
||||
})
|
||||
|
||||
builder.mixin(task.class.Task, core.class.Class, view.mixin.ObjectEditorHeader, {
|
||||
editor: task.component.TaskHeader
|
||||
})
|
||||
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: task.class.Issue,
|
||||
descriptor: task.viewlet.Kanban,
|
||||
open: task.component.EditTask,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {
|
||||
lookup: {
|
||||
|
@ -46,7 +46,6 @@ export default mergeIds(taskId, task, {
|
||||
ProjectView: '' as AnyComponent,
|
||||
CreateProject: '' as AnyComponent,
|
||||
CreateTask: '' as AnyComponent,
|
||||
EditTask: '' as AnyComponent,
|
||||
EditIssue: '' as AnyComponent,
|
||||
TaskPresenter: '' as AnyComponent,
|
||||
KanbanCard: '' as AnyComponent,
|
||||
@ -57,7 +56,8 @@ export default mergeIds(taskId, task, {
|
||||
KanbanView: '' as AnyComponent,
|
||||
Todos: '' as AnyComponent,
|
||||
TodoItemPresenter: '' as AnyComponent,
|
||||
StatusTableView: '' as AnyComponent
|
||||
StatusTableView: '' as AnyComponent,
|
||||
TaskHeader: '' as AnyComponent
|
||||
},
|
||||
string: {
|
||||
Task: '' as IntlString,
|
||||
|
@ -19,7 +19,18 @@ import { Builder, Mixin, Model } from '@anticrm/model'
|
||||
import core, { TClass, TDoc } from '@anticrm/model-core'
|
||||
import type { Asset, IntlString, Resource, Status } from '@anticrm/platform'
|
||||
import type { AnyComponent } from '@anticrm/ui'
|
||||
import type { Action, ActionTarget, AttributeEditor, AttributePresenter, ObjectEditor, ObjectFactory, ObjectValidator, Viewlet, ViewletDescriptor } from '@anticrm/view'
|
||||
import type {
|
||||
Action,
|
||||
ActionTarget,
|
||||
AttributeEditor,
|
||||
AttributePresenter,
|
||||
ObjectEditor,
|
||||
ObjectEditorHeader,
|
||||
ObjectFactory,
|
||||
ObjectValidator,
|
||||
Viewlet,
|
||||
ViewletDescriptor
|
||||
} from '@anticrm/view'
|
||||
import view from './plugin'
|
||||
|
||||
@Mixin(view.mixin.AttributeEditor, core.class.Class)
|
||||
@ -37,9 +48,14 @@ export class TObjectEditor extends TClass implements ObjectEditor {
|
||||
editor!: AnyComponent
|
||||
}
|
||||
|
||||
@Mixin(view.mixin.ObjectEditorHeader, core.class.Class)
|
||||
export class TObjectEditorHeader extends TClass implements ObjectEditorHeader {
|
||||
editor!: AnyComponent
|
||||
}
|
||||
|
||||
@Mixin(view.mixin.ObjectValidator, core.class.Class)
|
||||
export class TObjectValidator extends TClass implements ObjectValidator {
|
||||
validator!: Resource<(<T extends Doc>(doc: T, client: Client) => Promise<Status<{}>>)>
|
||||
validator!: Resource<<T extends Doc>(doc: T, client: Client) => Promise<Status<{}>>>
|
||||
}
|
||||
|
||||
@Mixin(view.mixin.ObjectFactory, core.class.Class)
|
||||
@ -75,7 +91,18 @@ export class TActionTarget extends TDoc implements ActionTarget {
|
||||
}
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(TAttributeEditor, TAttributePresenter, TObjectEditor, TViewletDescriptor, TViewlet, TAction, TActionTarget, TObjectValidator, TObjectFactory)
|
||||
builder.createModel(
|
||||
TAttributeEditor,
|
||||
TAttributePresenter,
|
||||
TObjectEditor,
|
||||
TViewletDescriptor,
|
||||
TViewlet,
|
||||
TAction,
|
||||
TActionTarget,
|
||||
TObjectValidator,
|
||||
TObjectFactory,
|
||||
TObjectEditorHeader
|
||||
)
|
||||
|
||||
builder.mixin(core.class.TypeString, core.class.Class, view.mixin.AttributeEditor, {
|
||||
editor: view.component.StringEditor
|
||||
@ -105,28 +132,43 @@ export function createModel (builder: Builder): void {
|
||||
editor: view.component.DateEditor
|
||||
})
|
||||
|
||||
builder.createDoc(view.class.ViewletDescriptor, core.space.Model, {
|
||||
builder.createDoc(
|
||||
view.class.ViewletDescriptor,
|
||||
core.space.Model,
|
||||
{
|
||||
label: 'Table' as IntlString,
|
||||
icon: view.icon.Table,
|
||||
component: view.component.TableView
|
||||
}, view.viewlet.Table)
|
||||
},
|
||||
view.viewlet.Table
|
||||
)
|
||||
|
||||
builder.createDoc(view.class.Action, core.space.Model, {
|
||||
builder.createDoc(
|
||||
view.class.Action,
|
||||
core.space.Model,
|
||||
{
|
||||
label: 'Delete' as IntlString,
|
||||
icon: view.icon.Delete,
|
||||
action: view.actionImpl.Delete
|
||||
}, view.action.Delete)
|
||||
},
|
||||
view.action.Delete
|
||||
)
|
||||
|
||||
builder.createDoc(view.class.ActionTarget, core.space.Model, {
|
||||
target: core.class.Doc,
|
||||
action: view.action.Delete
|
||||
})
|
||||
|
||||
builder.createDoc(view.class.Action, core.space.Model, {
|
||||
builder.createDoc(
|
||||
view.class.Action,
|
||||
core.space.Model,
|
||||
{
|
||||
label: 'Move' as IntlString,
|
||||
icon: view.icon.Move,
|
||||
action: view.actionImpl.Move
|
||||
}, view.action.Move)
|
||||
},
|
||||
view.action.Move
|
||||
)
|
||||
|
||||
builder.createDoc(view.class.ActionTarget, core.space.Model, {
|
||||
target: core.class.Doc,
|
||||
|
@ -36,6 +36,7 @@ export default mergeIds(viewId, view, {
|
||||
TimestampPresenter: '' as AnyComponent,
|
||||
DateEditor: '' as AnyComponent,
|
||||
DatePresenter: '' as AnyComponent,
|
||||
TableView: '' as AnyComponent
|
||||
TableView: '' as AnyComponent,
|
||||
RolePresenter: '' as AnyComponent
|
||||
}
|
||||
})
|
||||
|
@ -1,247 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 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 { Contact, formatName } from '@anticrm/contact'
|
||||
import core, { Class, ClassifierKind, Doc, Mixin, Ref } from '@anticrm/core'
|
||||
import { Panel } from '@anticrm/panel'
|
||||
import { Asset } from '@anticrm/platform'
|
||||
import {
|
||||
AttributesBar,
|
||||
createQuery,
|
||||
getAttributePresenterClass,
|
||||
getClient,
|
||||
KeyedAttribute
|
||||
} from '@anticrm/presentation'
|
||||
import { AnyComponent, Component, Label } from '@anticrm/ui'
|
||||
import view from '@anticrm/view'
|
||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||
import contact from '../plugin'
|
||||
import { getMixinStyle } from '../utils'
|
||||
|
||||
export let _id: Ref<Contact>
|
||||
let object: Contact
|
||||
let objectClass: Class<Doc>
|
||||
let rightSection: AnyComponent | undefined
|
||||
let fullSize: boolean = true
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const docKeys: Set<string> = new Set<string>(hierarchy.getAllAttributes(core.class.AttachedDoc).keys())
|
||||
|
||||
const query = createQuery()
|
||||
$: _id &&
|
||||
query.query(contact.class.Contact, { _id }, (result) => {
|
||||
object = result[0]
|
||||
})
|
||||
|
||||
$: if (object) objectClass = client.getHierarchy().getClass(object._class)
|
||||
|
||||
let selectedClass: Ref<Class<Doc>> | undefined
|
||||
let prevSelected = selectedClass
|
||||
|
||||
let keys: KeyedAttribute[] = []
|
||||
let collectionKeys: KeyedAttribute[] = []
|
||||
|
||||
let mixins: Mixin<Doc>[] = []
|
||||
|
||||
let selectedMixin: Mixin<Doc> | undefined
|
||||
|
||||
$: if (object && prevSelected !== object._class) {
|
||||
prevSelected = object._class
|
||||
selectedClass = objectClass._id
|
||||
selectedMixin = undefined
|
||||
const h = client.getHierarchy()
|
||||
mixins = h
|
||||
.getDescendants(contact.class.Contact)
|
||||
.filter((m) => h.getClass(m).kind === ClassifierKind.MIXIN && h.hasMixin(object, m))
|
||||
.map((m) => h.getClass(m) as Mixin<Doc>)
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
function filterKeys (keys: KeyedAttribute[], ignoreKeys: string[]): KeyedAttribute[] {
|
||||
keys = keys.filter((k) => !docKeys.has(k.key))
|
||||
keys = keys.filter((k) => !ignoreKeys.includes(k.key))
|
||||
return keys
|
||||
}
|
||||
|
||||
function getFiltredKeys (objectClass: Ref<Class<Doc>>, ignoreKeys: string[], to?: Ref<Class<Doc>>): KeyedAttribute[] {
|
||||
const keys = [...hierarchy.getAllAttributes(objectClass, to).entries()]
|
||||
.filter(([, value]) => value.hidden !== true)
|
||||
.map(([key, attr]) => ({ key, attr }))
|
||||
|
||||
return filterKeys(keys, ignoreKeys)
|
||||
}
|
||||
|
||||
function updateKeys (ignoreKeys: string[]): void {
|
||||
const filtredKeys = getFiltredKeys(selectedClass ?? object._class, ignoreKeys, selectedClass !== objectClass._id ? objectClass._id : undefined)
|
||||
keys = collectionsFilter(filtredKeys, false)
|
||||
collectionKeys = collectionsFilter(filtredKeys, true)
|
||||
}
|
||||
|
||||
function collectionsFilter (keys: KeyedAttribute[], get: boolean): KeyedAttribute[] {
|
||||
const result: KeyedAttribute[] = []
|
||||
for (const key of keys) {
|
||||
if (isCollectionAttr(key) === get) result.push(key)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function isCollectionAttr (key: KeyedAttribute): boolean {
|
||||
return hierarchy.isDerived(key.attr.type._class, core.class.Collection)
|
||||
}
|
||||
|
||||
async function getEditor (_class: Ref<Class<Doc>>): Promise<AnyComponent> {
|
||||
const clazz = hierarchy.getClass(_class)
|
||||
const editorMixin = hierarchy.as(clazz, view.mixin.ObjectEditor)
|
||||
if (editorMixin?.editor == null && clazz.extends != null) return getEditor(clazz.extends)
|
||||
return editorMixin.editor
|
||||
}
|
||||
|
||||
async function getEditorOrDefault (
|
||||
_class: Ref<Class<Doc>> | undefined,
|
||||
defaultClass: Ref<Class<Doc>>
|
||||
): Promise<AnyComponent> {
|
||||
const editor = _class !== undefined ? await getEditor(_class) : undefined
|
||||
if (editor !== undefined) {
|
||||
return editor
|
||||
}
|
||||
return getEditor(defaultClass)
|
||||
}
|
||||
|
||||
async function getCollectionEditor (key: KeyedAttribute): Promise<AnyComponent> {
|
||||
const attrClass = getAttributePresenterClass(key.attr)
|
||||
const clazz = client.getHierarchy().getClass(attrClass)
|
||||
const editorMixin = client.getHierarchy().as(clazz, view.mixin.AttributeEditor)
|
||||
return editorMixin.editor
|
||||
}
|
||||
|
||||
$: icon = (objectClass?.icon ?? contact.class.Person) as Asset
|
||||
|
||||
let mainEditor: HTMLElement
|
||||
let prevEditor: HTMLElement
|
||||
let maxHeight = 0
|
||||
const observer = new ResizeObserver(() => {
|
||||
const curHeight = mainEditor.clientHeight
|
||||
maxHeight = Math.max(maxHeight, curHeight)
|
||||
})
|
||||
|
||||
$: if (mainEditor != null) {
|
||||
if (prevEditor != null) {
|
||||
observer.unobserve(prevEditor)
|
||||
}
|
||||
prevEditor = mainEditor
|
||||
observer.observe(mainEditor)
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
observer.disconnect()
|
||||
})
|
||||
|
||||
function getCollectionCounter (object: Doc, key: KeyedAttribute): number {
|
||||
if (client.getHierarchy().isMixin(key.attr.attributeOf)) {
|
||||
return (client.getHierarchy().as(object, key.attr.attributeOf) as any)[key.key]
|
||||
}
|
||||
return (object as any)[key.key] ?? 0
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if object !== undefined}
|
||||
<Panel
|
||||
{icon}
|
||||
title={formatName(object.name)}
|
||||
{rightSection}
|
||||
{fullSize}
|
||||
{object}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<div slot="subtitle">
|
||||
{#if keys}
|
||||
<AttributesBar {object} {keys} />
|
||||
{/if}
|
||||
</div>
|
||||
<div class='main-editor' bind:this={mainEditor} style={`min-height: ${maxHeight}px;`}>
|
||||
{#await getEditorOrDefault(selectedClass, object._class) then is}
|
||||
<Component
|
||||
{is}
|
||||
props={{ object }}
|
||||
on:open={(ev) => {
|
||||
updateKeys(ev.detail.ignoreKeys)
|
||||
}}
|
||||
on:click={(ev) => {
|
||||
fullSize = true
|
||||
rightSection = ev.detail.presenter
|
||||
}}
|
||||
/>
|
||||
{/await}
|
||||
</div>
|
||||
{#if mixins.length > 0}
|
||||
<div class="mixin-container">
|
||||
<div class="mixin-selector"
|
||||
style={getMixinStyle(objectClass._id, selectedClass === objectClass._id)}
|
||||
on:click={() => { selectedClass = objectClass._id; selectedMixin = undefined }}>
|
||||
<Label label={objectClass.label} />
|
||||
</div>
|
||||
{#each mixins as mixin}
|
||||
<div class="mixin-selector"
|
||||
style={getMixinStyle(mixin._id, selectedClass === mixin._id)}
|
||||
on:click={() => { selectedClass = mixin._id; selectedMixin = mixin }}>
|
||||
<Label label={mixin.label} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#each collectionKeys as collection}
|
||||
<div class="mt-14">
|
||||
{#await getCollectionEditor(collection) then is}
|
||||
<Component {is} props={{ objectId: object._id, _class: object._class, space: object.space, [collection.key]: getCollectionCounter(object, collection) }} />
|
||||
{/await}
|
||||
</div>
|
||||
{/each}
|
||||
</Panel>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.main-editor {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
.mixin-container {
|
||||
margin-top: 2rem;
|
||||
display: flex;
|
||||
.mixin-selector {
|
||||
margin-left: .5rem;
|
||||
cursor: pointer;
|
||||
height: 1.5rem;
|
||||
min-width: 5.25rem;
|
||||
|
||||
border-radius: .5rem;
|
||||
|
||||
font-weight: 500;
|
||||
font-size: .625rem;
|
||||
|
||||
text-transform: uppercase;
|
||||
color: #FFFFFF;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -14,15 +14,15 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Person } from '@anticrm/contact'
|
||||
import { Organization } from '@anticrm/contact'
|
||||
import { showPopup } from '@anticrm/ui'
|
||||
import Company from './icons/Company.svelte'
|
||||
import EditContact from './EditContact.svelte'
|
||||
import { EditDoc } from '@anticrm/view-resources'
|
||||
|
||||
export let value: Person
|
||||
export let value: Organization
|
||||
|
||||
async function onClick () {
|
||||
showPopup(EditContact, { _id: value._id }, 'full')
|
||||
showPopup(EditDoc, { _id: value._id, _class: value._class }, 'full')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -17,12 +17,12 @@
|
||||
import { formatName, Person } from '@anticrm/contact'
|
||||
import { Avatar } from '@anticrm/presentation'
|
||||
import { showPopup } from '@anticrm/ui'
|
||||
import EditContact from './EditContact.svelte'
|
||||
import { EditDoc } from '@anticrm/view-resources'
|
||||
|
||||
export let value: Person
|
||||
|
||||
async function onClick () {
|
||||
showPopup(EditContact, { _id: value._id }, 'full')
|
||||
showPopup(EditDoc, { _id: value._id, _class: value._class }, 'full')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -20,7 +20,6 @@ import OrganizationPresenter from './components/OrganizationPresenter.svelte'
|
||||
import ChannelsPresenter from './components/ChannelsPresenter.svelte'
|
||||
import CreatePerson from './components/CreatePerson.svelte'
|
||||
import CreateOrganization from './components/CreateOrganization.svelte'
|
||||
import EditContact from './components/EditContact.svelte'
|
||||
import EditPerson from './components/EditPerson.svelte'
|
||||
import EditOrganization from './components/EditOrganization.svelte'
|
||||
import CreatePersons from './components/CreatePersons.svelte'
|
||||
@ -28,9 +27,8 @@ import CreateOrganizations from './components/CreateOrganizations.svelte'
|
||||
import SocialEditor from './components/SocialEditor.svelte'
|
||||
import Contacts from './components/Contacts.svelte'
|
||||
import { Resources } from '@anticrm/platform'
|
||||
import RolePresenter from './components/RolePresenter.svelte'
|
||||
|
||||
export { ContactPresenter, EditContact }
|
||||
export { ContactPresenter }
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
component: {
|
||||
@ -45,7 +43,6 @@ export default async (): Promise<Resources> => ({
|
||||
CreatePersons,
|
||||
CreateOrganizations,
|
||||
SocialEditor,
|
||||
Contacts,
|
||||
RolePresenter
|
||||
Contacts
|
||||
}
|
||||
})
|
||||
|
@ -1,10 +0,0 @@
|
||||
import { Class, Doc, Ref } from '@anticrm/core'
|
||||
import { getPlatformColorForText } from '@anticrm/ui'
|
||||
|
||||
export function getMixinStyle (id: Ref<Class<Doc>>, selected: boolean): string {
|
||||
const color = getPlatformColorForText(id as string)
|
||||
return `
|
||||
background: ${color + (selected ? 'ff' : '33')};
|
||||
border: 1px solid ${color + (selected ? '0f' : '66')};
|
||||
`
|
||||
}
|
@ -14,139 +14,11 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import core, { Doc, Ref } from '@anticrm/core'
|
||||
import { Category, Product } from '@anticrm/inventory'
|
||||
import { Panel } from '@anticrm/panel'
|
||||
import {
|
||||
AttributesBar,
|
||||
createQuery,
|
||||
getAttributePresenterClass,
|
||||
getClient,
|
||||
KeyedAttribute
|
||||
} from '@anticrm/presentation'
|
||||
import { AnyComponent, Component } from '@anticrm/ui'
|
||||
import view from '@anticrm/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import inventory from '../plugin'
|
||||
|
||||
export let _id: Ref<Product>
|
||||
let object: Product
|
||||
let rightSection: AnyComponent | undefined
|
||||
const fullSize: boolean = true
|
||||
let category: Category | undefined
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const docKeys: Set<string> = new Set<string>(hierarchy.getAllAttributes(core.class.AttachedDoc).keys())
|
||||
|
||||
const query = createQuery()
|
||||
$: _id && update(_id)
|
||||
|
||||
function update (id: Ref<Product>) {
|
||||
query.query(inventory.class.Product, { _id: id }, (result) => {
|
||||
object = result[0]
|
||||
client.findOne(inventory.class.Category, { _id: object.attachedTo as Ref<Category> }).then((res) => {
|
||||
category = res
|
||||
})
|
||||
updateKeys(['comments'])
|
||||
})
|
||||
}
|
||||
|
||||
let keys: KeyedAttribute[] = []
|
||||
let collectionKeys: KeyedAttribute[] = []
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
function filterKeys (keys: KeyedAttribute[], ignoreKeys: string[]): KeyedAttribute[] {
|
||||
keys = keys.filter((k) => !docKeys.has(k.key))
|
||||
keys = keys.filter((k) => !ignoreKeys.includes(k.key))
|
||||
return keys
|
||||
}
|
||||
|
||||
function getFiltredKeys (ignoreKeys: string[]): KeyedAttribute[] {
|
||||
const keys = [...hierarchy.getAllAttributes(object._class).entries()]
|
||||
.filter(([, value]) => value.hidden !== true)
|
||||
.map(([key, attr]) => ({ key, attr }))
|
||||
|
||||
return filterKeys(keys, ignoreKeys)
|
||||
}
|
||||
|
||||
function updateKeys (ignoreKeys: string[]): void {
|
||||
const filtredKeys = getFiltredKeys(ignoreKeys)
|
||||
keys = collectionsFilter(filtredKeys, false)
|
||||
collectionKeys = collectionsFilter(filtredKeys, true)
|
||||
}
|
||||
|
||||
function collectionsFilter (keys: KeyedAttribute[], get: boolean): KeyedAttribute[] {
|
||||
const result: KeyedAttribute[] = []
|
||||
for (const key of keys) {
|
||||
if (isCollectionAttr(key) === get) result.push(key)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function isCollectionAttr (key: KeyedAttribute): boolean {
|
||||
return hierarchy.isDerived(key.attr.type._class, core.class.Collection)
|
||||
}
|
||||
|
||||
async function getCollectionEditor (key: KeyedAttribute): Promise<AnyComponent> {
|
||||
const attrClass = getAttributePresenterClass(key.attr)
|
||||
const clazz = client.getHierarchy().getClass(attrClass)
|
||||
const editorMixin = client.getHierarchy().as(clazz, view.mixin.AttributeEditor)
|
||||
return editorMixin.editor
|
||||
}
|
||||
|
||||
function getCollectionCounter (object: Doc, key: KeyedAttribute): number {
|
||||
if (client.getHierarchy().isMixin(key.attr.attributeOf)) {
|
||||
return (client.getHierarchy().as(object, key.attr.attributeOf) as any)[key.key]
|
||||
}
|
||||
return (object as any)[key.key] ?? 0
|
||||
}
|
||||
onMount(() => {
|
||||
dispatch('open', { ignoreKeys: ['comments', 'number', 'title', 'customer'] })
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if object !== undefined}
|
||||
<Panel
|
||||
icon={inventory.icon.Products}
|
||||
title={object.name}
|
||||
subtitle={category?.name}
|
||||
{rightSection}
|
||||
{fullSize}
|
||||
{object}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<div slot="subtitle">
|
||||
{#if keys}
|
||||
<AttributesBar {object} {keys} />
|
||||
{/if}
|
||||
</div>
|
||||
{#each collectionKeys as collection}
|
||||
<div class="collection">
|
||||
{#await getCollectionEditor(collection) then is}
|
||||
<Component
|
||||
{is}
|
||||
props={{
|
||||
objectId: object._id,
|
||||
_class: object._class,
|
||||
space: object.space,
|
||||
[collection.key]: getCollectionCounter(object, collection)
|
||||
}}
|
||||
/>
|
||||
{/await}
|
||||
</div>
|
||||
{/each}
|
||||
</Panel>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.main-editor {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.collection + .collection {
|
||||
margin-top: 3.5rem;
|
||||
}
|
||||
</style>
|
||||
|
@ -16,14 +16,14 @@
|
||||
<script lang="ts">
|
||||
import { Product } from '@anticrm/inventory'
|
||||
import { closeTooltip, Icon, showPopup } from '@anticrm/ui'
|
||||
import EditProduct from './EditProduct.svelte'
|
||||
import inventory from '../plugin'
|
||||
import { EditDoc } from '@anticrm/view-resources'
|
||||
|
||||
export let value: Product
|
||||
|
||||
function show () {
|
||||
closeTooltip()
|
||||
showPopup(EditProduct, { _id: value._id }, 'full')
|
||||
showPopup(EditDoc, { _id: value._id, _class: value._class }, 'full')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -50,9 +50,9 @@
|
||||
_class={contact.class.Contact}
|
||||
title={lead.string.Customer}
|
||||
caption={lead.string.SelectCustomer}
|
||||
bind:value={object.customer}
|
||||
bind:value={object.attachedTo}
|
||||
on:change={() => {
|
||||
change('customer', object.customer)
|
||||
change('attachedTo', object.attachedTo)
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
|
@ -20,9 +20,8 @@
|
||||
import type { WithLookup } from '@anticrm/core'
|
||||
import type { Lead } from '@anticrm/lead'
|
||||
import { ActionIcon, IconMoreH, showPopup } from '@anticrm/ui'
|
||||
import { ContextMenu } from '@anticrm/view-resources'
|
||||
import { ContextMenu, EditDoc } from '@anticrm/view-resources'
|
||||
import lead from '../plugin'
|
||||
import { EditTask } from '@anticrm/task-resources'
|
||||
|
||||
export let object: WithLookup<Lead>
|
||||
export let draggable: boolean
|
||||
@ -32,7 +31,7 @@
|
||||
}
|
||||
|
||||
function showLead () {
|
||||
showPopup(EditTask, { _id: object._id }, 'full')
|
||||
showPopup(EditDoc, { _id: object._id, _class: object._class }, 'full')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -17,13 +17,13 @@
|
||||
import type { Lead } from '@anticrm/lead'
|
||||
import { closeTooltip, Icon, showPopup } from '@anticrm/ui'
|
||||
import lead from '../plugin'
|
||||
import { EditTask } from '@anticrm/task-resources'
|
||||
import { EditDoc } from '@anticrm/view-resources'
|
||||
|
||||
export let value: Lead
|
||||
|
||||
function show () {
|
||||
closeTooltip()
|
||||
showPopup(EditTask, { _id: value._id }, 'full')
|
||||
showPopup(EditDoc, { _id: value._id, _class: value._class }, 'full')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -114,7 +114,7 @@ export async function createWorkspace (workspace: string): Promise<[Status, Logi
|
||||
}
|
||||
|
||||
const overrideToken = getMetadata(login.metadata.OverrideLoginToken)
|
||||
const email = getMetadata(login.metadata.LoginEmail) ?? ''
|
||||
const email = fetchMetadataLocalStorage(login.metadata.LoginEmail) ?? ''
|
||||
if (overrideToken !== undefined) {
|
||||
const endpoint = getMetadata(login.metadata.OverrideEndpoint)
|
||||
if (endpoint !== undefined) {
|
||||
@ -213,7 +213,7 @@ export async function selectWorkspace (workspace: string): Promise<[Status, Logi
|
||||
}
|
||||
|
||||
const overrideToken = getMetadata(login.metadata.OverrideLoginToken)
|
||||
const email = getMetadata(login.metadata.LoginEmail) ?? ''
|
||||
const email = fetchMetadataLocalStorage(login.metadata.LoginEmail) ?? ''
|
||||
if (overrideToken !== undefined) {
|
||||
const endpoint = getMetadata(login.metadata.OverrideEndpoint)
|
||||
if (endpoint !== undefined) {
|
||||
|
@ -17,7 +17,7 @@
|
||||
import type { Applicant } from '@anticrm/recruit'
|
||||
import { closeTooltip, Icon, showPopup } from '@anticrm/ui'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { EditTask } from '@anticrm/task-resources'
|
||||
import { EditDoc } from '@anticrm/view-resources'
|
||||
import recruit from '@anticrm/recruit'
|
||||
|
||||
export let value: Applicant
|
||||
@ -27,7 +27,7 @@
|
||||
|
||||
function show () {
|
||||
closeTooltip()
|
||||
showPopup(EditTask, { _id: value._id }, 'full')
|
||||
showPopup(EditDoc, { _id: value._id, _class: value._class }, 'full')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -12,7 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { Avatar } from '@anticrm/presentation'
|
||||
import { showPopup, ActionIcon, IconMoreH } from '@anticrm/ui'
|
||||
@ -23,13 +22,13 @@
|
||||
import { AttachmentsPresenter } from '@anticrm/attachment-resources'
|
||||
import { formatName } from '@anticrm/contact'
|
||||
import ApplicationPresenter from './ApplicationPresenter.svelte'
|
||||
import { EditContact } from '@anticrm/contact-resources'
|
||||
import { EditDoc } from '@anticrm/view-resources'
|
||||
|
||||
export let object: WithLookup<Applicant>
|
||||
export let draggable: boolean
|
||||
|
||||
function showCandidate () {
|
||||
showPopup(EditContact, { _id: object.attachedTo }, 'full')
|
||||
showPopup(EditDoc, { _id: object.attachedTo, _class: object.attachedToClass }, 'full')
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -37,7 +36,9 @@
|
||||
<div class="flex-between mb-3">
|
||||
<Avatar avatar={object.$lookup?.attachedTo?.avatar} size={'medium'} />
|
||||
<div class="flex-grow flex-col min-w-0 ml-2">
|
||||
<div class="fs-title over-underline lines-limit-2" on:click={showCandidate}>{formatName(object.$lookup?.attachedTo?.name)}</div>
|
||||
<div class="fs-title over-underline lines-limit-2" on:click={showCandidate}>
|
||||
{formatName(object.$lookup?.attachedTo?.name)}
|
||||
</div>
|
||||
<div class="small-text lines-limit-2">{object.$lookup?.attachedTo?.title ?? ''}</div>
|
||||
</div>
|
||||
<div class="tool"><ActionIcon label={undefined} icon={IconMoreH} size={'small'} /></div>
|
||||
@ -47,10 +48,10 @@
|
||||
<div class="sm-tool-icon step-lr75">
|
||||
<ApplicationPresenter value={object} />
|
||||
</div>
|
||||
{#if object.attachments ?? 0 > 0}
|
||||
{#if (object.attachments ?? 0) > 0}
|
||||
<div class="step-lr75"><AttachmentsPresenter value={object} /></div>
|
||||
{/if}
|
||||
{#if object.comments ?? 0 > 0}
|
||||
{#if (object.comments ?? 0) > 0}
|
||||
<div class="step-lr75"><CommentsPresenter value={object} /></div>
|
||||
{/if}
|
||||
</div>
|
||||
@ -63,11 +64,15 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1rem 1.25rem;
|
||||
background-color: rgba(222, 222, 240, .06);
|
||||
border-radius: .75rem;
|
||||
background-color: rgba(222, 222, 240, 0.06);
|
||||
border-radius: 0.75rem;
|
||||
user-select: none;
|
||||
|
||||
&.draggable { cursor: grab; }
|
||||
&.draggable {
|
||||
cursor: grab;
|
||||
}
|
||||
}
|
||||
.tool {
|
||||
align-self: start;
|
||||
}
|
||||
.tool { align-self: start; }
|
||||
</style>
|
||||
|
@ -1,120 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 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 core, { Class, Doc, Ref } from '@anticrm/core'
|
||||
import { Panel } from '@anticrm/panel'
|
||||
import { createQuery, getAttributePresenterClass, getClient } from '@anticrm/presentation'
|
||||
import type { Task } from '@anticrm/task'
|
||||
import { AnyComponent, Component } from '@anticrm/ui'
|
||||
import view from '@anticrm/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import task from '../plugin'
|
||||
import TaskHeader from './TaskHeader.svelte'
|
||||
import { Asset } from '@anticrm/platform'
|
||||
|
||||
export let _id: Ref<Task>
|
||||
let object: Task
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const docKeys: Set<string> = new Set<string>(hierarchy.getAllAttributes(core.class.AttachedDoc).keys())
|
||||
|
||||
let keys: string[] = []
|
||||
let collectionKeys: string[] = []
|
||||
|
||||
const query = createQuery()
|
||||
$: _id &&
|
||||
query.query(task.class.Task, { _id }, (result) => {
|
||||
object = result[0]
|
||||
})
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
function getFiltredKeys (ignoreKeys: string[]): string[] {
|
||||
let keys = [...hierarchy.getAllAttributes(object._class).entries()]
|
||||
.filter(([, value]) => value.hidden !== true)
|
||||
.map(([key]) => key)
|
||||
keys = keys.filter((k) => !docKeys.has(k))
|
||||
keys = keys.filter((k) => !ignoreKeys.includes(k))
|
||||
return keys
|
||||
}
|
||||
|
||||
function getKeys (ignoreKeys: string[]): void {
|
||||
const filtredKeys = getFiltredKeys(ignoreKeys)
|
||||
keys = collectionsFilter(filtredKeys, false)
|
||||
collectionKeys = collectionsFilter(filtredKeys, true)
|
||||
}
|
||||
|
||||
function collectionsFilter (keys: string[], get: boolean): string[] {
|
||||
const result: string[] = []
|
||||
for (const key of keys) {
|
||||
if (isCollectionAttr(key) === get) result.push(key)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function isCollectionAttr (key: string): boolean {
|
||||
const attribute = hierarchy.getAttribute(object._class, key)
|
||||
return hierarchy.isDerived(attribute.type._class, core.class.Collection)
|
||||
}
|
||||
|
||||
async function getEditor (_class: Ref<Class<Doc>>): Promise<AnyComponent> {
|
||||
const clazz = hierarchy.getClass(_class)
|
||||
const editorMixin = hierarchy.as(clazz, view.mixin.ObjectEditor)
|
||||
if (editorMixin?.editor == null && clazz.extends != null) return getEditor(clazz.extends)
|
||||
return editorMixin.editor
|
||||
}
|
||||
|
||||
async function getCollectionEditor (key: string): Promise<AnyComponent> {
|
||||
const attribute = hierarchy.getAttribute(object._class, key)
|
||||
const attrClass = getAttributePresenterClass(attribute)
|
||||
const clazz = client.getHierarchy().getClass(attrClass)
|
||||
const editorMixin = client.getHierarchy().as(clazz, view.mixin.AttributeEditor)
|
||||
return editorMixin.editor
|
||||
}
|
||||
|
||||
$: icon = object && (hierarchy.getClass(object._class).icon as Asset)
|
||||
$: title = object && hierarchy.getClass(object._class).label
|
||||
</script>
|
||||
|
||||
{#if object !== undefined}
|
||||
<Panel
|
||||
{icon}
|
||||
{title}
|
||||
{object}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<TaskHeader {object} {keys} slot="subtitle" />
|
||||
|
||||
{#await getEditor(object._class) then is}
|
||||
<Component
|
||||
{is}
|
||||
props={{ object }}
|
||||
on:open={(ev) => {
|
||||
getKeys(ev.detail.ignoreKeys)
|
||||
}}
|
||||
/>
|
||||
{/await}
|
||||
{#each collectionKeys as collection}
|
||||
<div class="mt-14">
|
||||
{#await getCollectionEditor(collection) then is}
|
||||
<Component {is} props={{ objectId: object._id, _class: object._class, space: object.space }} />
|
||||
{/await}
|
||||
</div>
|
||||
{/each}
|
||||
</Panel>
|
||||
{/if}
|
@ -16,12 +16,12 @@
|
||||
<script lang="ts">
|
||||
import contact from '@anticrm/contact'
|
||||
import core, { Class, Doc, Ref, RefTo } from '@anticrm/core'
|
||||
import { AttributeBarEditor, AttributesBar, getClient, UserBox } from '@anticrm/presentation'
|
||||
import { AttributeBarEditor, AttributesBar, getClient, KeyedAttribute, UserBox } from '@anticrm/presentation'
|
||||
import { Task } from '@anticrm/task'
|
||||
import task from '../plugin'
|
||||
|
||||
export let object: Task
|
||||
export let keys: string[]
|
||||
export let keys: KeyedAttribute[]
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
@ -48,7 +48,7 @@
|
||||
return contact.class.Employee
|
||||
}
|
||||
|
||||
$: filtredKeys = keys.filter((p) => p !== 'state' && p !== 'assignee' && p !== 'doneState') // todo
|
||||
$: filtredKeys = keys.filter((p) => p.key !== 'state' && p.key !== 'assignee' && p.key !== 'doneState') // todo
|
||||
</script>
|
||||
|
||||
<div class="flex-between header">
|
||||
|
@ -16,9 +16,9 @@
|
||||
<script lang="ts">
|
||||
import type { Issue } from '@anticrm/task'
|
||||
import { closeTooltip, Icon, showPopup } from '@anticrm/ui'
|
||||
import EditTask from './EditTask.svelte'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import task from '../plugin'
|
||||
import { EditDoc } from '@anticrm/view-resources'
|
||||
|
||||
export let value: Issue
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
|
||||
function show () {
|
||||
closeTooltip()
|
||||
showPopup(EditTask, { _id: value._id }, 'full')
|
||||
showPopup(EditDoc, { _id: value._id, _class: value._class }, 'full')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -30,7 +30,6 @@
|
||||
|
||||
export let _class: Ref<Class<Item>>
|
||||
export let space: Ref<SpaceWithStates>
|
||||
export let open: AnyComponent
|
||||
export let search: string
|
||||
export let options: FindOptions<Item> | undefined
|
||||
export let config: string[]
|
||||
|
@ -13,31 +13,30 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import type { State } from '@anticrm/task'
|
||||
import { getPlatformColor } from '@anticrm/ui'
|
||||
|
||||
export let value: State
|
||||
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<div class="overflow-label state-container" style="background-color: {value.color}">
|
||||
<div class="overflow-label state-container" style="background-color: {getPlatformColor(value.color)}">
|
||||
{value.title}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.state-container {
|
||||
padding: .25rem .5rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
width: 6.25rem;
|
||||
max-width: 6.25rem;
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
letter-spacing: .5px;
|
||||
font-size: .625rem;
|
||||
letter-spacing: 0.5px;
|
||||
font-size: 0.625rem;
|
||||
color: #fff;
|
||||
border: 1px solid rgba(0, 0, 0, .1);
|
||||
border-radius: .25rem;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
</style>
|
||||
|
@ -17,6 +17,7 @@
|
||||
import { Ref, SortingOrder } from '@anticrm/core'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import task, { SpaceWithStates, State } from '@anticrm/task'
|
||||
import { getPlatformColor } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let space: Ref<SpaceWithStates>
|
||||
@ -45,7 +46,7 @@
|
||||
dispatch('close', state)
|
||||
}}
|
||||
>
|
||||
<div class="color" style="background-color: {state.color}" />
|
||||
<div class="color" style="background-color: {getPlatformColor(state.color)}" />
|
||||
{state.title}
|
||||
</div>
|
||||
{/each}
|
||||
|
@ -36,12 +36,12 @@ import Todos from './components/todos/Todos.svelte'
|
||||
import TodoItemPresenter from './components/todos/TodoItemPresenter.svelte'
|
||||
import TodoStatePresenter from './components/todos/TodoStatePresenter.svelte'
|
||||
import StatusTableView from './components/StatusTableView.svelte'
|
||||
import TaskHeader from './components/TaskHeader.svelte'
|
||||
|
||||
export { default as KanbanTemplateEditor } from './components/kanban/KanbanTemplateEditor.svelte'
|
||||
export { default as KanbanTemplateSelector } from './components/kanban/KanbanTemplateSelector.svelte'
|
||||
|
||||
export { default as Tasks } from './components/Tasks.svelte'
|
||||
export { default as EditTask } from './components/EditTask.svelte'
|
||||
|
||||
async function createTask (object: Doc): Promise<void> {
|
||||
showPopup(CreateTask, { parent: object._id, space: object.space })
|
||||
@ -108,7 +108,8 @@ export default async (): Promise<Resources> => ({
|
||||
Todos,
|
||||
TodoItemPresenter,
|
||||
TodoStatePresenter,
|
||||
StatusTableView
|
||||
StatusTableView,
|
||||
TaskHeader
|
||||
},
|
||||
actionImpl: {
|
||||
CreateTask: createTask,
|
||||
|
@ -32,6 +32,8 @@
|
||||
"dependencies": {
|
||||
"svelte": "^3.37.0",
|
||||
"@anticrm/platform": "~0.6.5",
|
||||
"@anticrm/contact": "~0.6.2",
|
||||
"@anticrm/panel": "~0.6.0",
|
||||
"@anticrm/core": "~0.6.11",
|
||||
"@anticrm/view": "~0.6.0",
|
||||
"@anticrm/ui": "~0.6.0",
|
||||
|
308
plugins/view-resources/src/components/EditDoc.svelte
Normal file
308
plugins/view-resources/src/components/EditDoc.svelte
Normal file
@ -0,0 +1,308 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021, 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact, { formatName } from '@anticrm/contact'
|
||||
import core, { Class, ClassifierKind, Doc, Mixin, Obj, Ref } from '@anticrm/core'
|
||||
import { Panel } from '@anticrm/panel'
|
||||
import { Asset, translate } from '@anticrm/platform'
|
||||
import {
|
||||
AttributesBar,
|
||||
createQuery,
|
||||
getAttributePresenterClass,
|
||||
getClient,
|
||||
KeyedAttribute
|
||||
} from '@anticrm/presentation'
|
||||
import { AnyComponent, Component, Label } from '@anticrm/ui'
|
||||
import view from '@anticrm/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { getMixinStyle } from '../utils'
|
||||
|
||||
export let _id: Ref<Doc>
|
||||
export let _class: Ref<Class<Doc>>
|
||||
let object: Doc
|
||||
let objectClass: Class<Doc>
|
||||
let parentClass: Ref<Class<Doc>>
|
||||
let rightSection: AnyComponent | undefined
|
||||
let fullSize: boolean = true
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const docKeys: Set<string> = new Set<string>(hierarchy.getAllAttributes(core.class.AttachedDoc).keys())
|
||||
|
||||
const query = createQuery()
|
||||
$: _id &&
|
||||
_class &&
|
||||
query.query(_class, { _id }, (result) => {
|
||||
object = result[0]
|
||||
})
|
||||
|
||||
$: if (object) objectClass = hierarchy.getClass(object._class)
|
||||
|
||||
let selectedClass: Ref<Class<Doc>> | undefined
|
||||
let prevSelected = selectedClass
|
||||
|
||||
let keys: KeyedAttribute[] = []
|
||||
let collectionKeys: KeyedAttribute[] = []
|
||||
|
||||
let mixins: Mixin<Doc>[] = []
|
||||
|
||||
let selectedMixin: Mixin<Doc> | undefined
|
||||
|
||||
$: if (object && prevSelected !== object._class) {
|
||||
prevSelected = object._class
|
||||
selectedClass = objectClass._id
|
||||
selectedMixin = undefined
|
||||
parentClass = getParentClass(object._class)
|
||||
mixins = getMixins()
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
function getMixins (): Mixin<Doc>[] {
|
||||
const descendants = hierarchy.getDescendants(parentClass)
|
||||
const mixins = descendants.filter(
|
||||
(m) => hierarchy.getClass(m).kind === ClassifierKind.MIXIN && hierarchy.hasMixin(object, m)
|
||||
)
|
||||
return mixins.map((m) => hierarchy.getClass(m) as Mixin<Doc>)
|
||||
}
|
||||
|
||||
function filterKeys (keys: KeyedAttribute[], ignoreKeys: string[]): KeyedAttribute[] {
|
||||
keys = keys.filter((k) => !docKeys.has(k.key))
|
||||
keys = keys.filter((k) => !ignoreKeys.includes(k.key))
|
||||
return keys
|
||||
}
|
||||
|
||||
function getFiltredKeys (objectClass: Ref<Class<Doc>>, ignoreKeys: string[], to?: Ref<Class<Doc>>): KeyedAttribute[] {
|
||||
const keys = [...hierarchy.getAllAttributes(objectClass, to).entries()]
|
||||
.filter(([, value]) => value.hidden !== true)
|
||||
.map(([key, attr]) => ({ key, attr }))
|
||||
|
||||
return filterKeys(keys, ignoreKeys)
|
||||
}
|
||||
|
||||
function updateKeys (ignoreKeys: string[]): void {
|
||||
const filtredKeys = getFiltredKeys(
|
||||
selectedClass ?? object._class,
|
||||
ignoreKeys,
|
||||
selectedClass !== objectClass._id ? objectClass._id : undefined
|
||||
)
|
||||
keys = collectionsFilter(filtredKeys, false)
|
||||
collectionKeys = collectionsFilter(filtredKeys, true)
|
||||
}
|
||||
|
||||
function collectionsFilter (keys: KeyedAttribute[], get: boolean): KeyedAttribute[] {
|
||||
const result: KeyedAttribute[] = []
|
||||
for (const key of keys) {
|
||||
if (isCollectionAttr(key) === get) result.push(key)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function isCollectionAttr (key: KeyedAttribute): boolean {
|
||||
return hierarchy.isDerived(key.attr.type._class, core.class.Collection)
|
||||
}
|
||||
|
||||
async function getEditor (_class: Ref<Class<Doc>>): Promise<AnyComponent> {
|
||||
const clazz = hierarchy.getClass(_class)
|
||||
const editorMixin = hierarchy.as(clazz, view.mixin.ObjectEditor)
|
||||
if (editorMixin?.editor == null && clazz.extends != null) return getEditor(clazz.extends)
|
||||
return editorMixin.editor
|
||||
}
|
||||
|
||||
async function getEditorOrDefault (
|
||||
_class: Ref<Class<Doc>> | undefined,
|
||||
defaultClass: Ref<Class<Doc>>
|
||||
): Promise<AnyComponent> {
|
||||
const editor = _class !== undefined ? await getEditor(_class) : undefined
|
||||
if (editor !== undefined) {
|
||||
return editor
|
||||
}
|
||||
return getEditor(defaultClass)
|
||||
}
|
||||
|
||||
async function getCollectionEditor (key: KeyedAttribute): Promise<AnyComponent> {
|
||||
const attrClass = getAttributePresenterClass(key.attr)
|
||||
const clazz = client.getHierarchy().getClass(attrClass)
|
||||
const editorMixin = client.getHierarchy().as(clazz, view.mixin.AttributeEditor)
|
||||
return editorMixin.editor
|
||||
}
|
||||
|
||||
function getIcon (_class: Ref<Class<Obj>>): Asset {
|
||||
let clazz = hierarchy.getClass(_class)
|
||||
if (clazz.icon !== undefined) return clazz.icon
|
||||
while (clazz.extends !== undefined) {
|
||||
clazz = hierarchy.getClass(clazz.extends)
|
||||
if (clazz.icon !== undefined) {
|
||||
return clazz.icon
|
||||
}
|
||||
}
|
||||
throw new Error(`Icon not found for ${_class}`)
|
||||
}
|
||||
|
||||
$: icon = object && getIcon(object._class)
|
||||
|
||||
function getCollectionCounter (object: Doc, key: KeyedAttribute): number {
|
||||
if (hierarchy.isMixin(key.attr.attributeOf)) {
|
||||
return (hierarchy.as(object, key.attr.attributeOf) as any)[key.key]
|
||||
}
|
||||
return (object as any)[key.key] ?? 0
|
||||
}
|
||||
|
||||
function getParentClass (_class: Ref<Class<Doc>>): Ref<Class<Doc>> {
|
||||
const baseDomain = hierarchy.getDomain(_class)
|
||||
const ancestors = hierarchy.getAncestors(_class)
|
||||
let result: Ref<Class<Doc>> = _class
|
||||
for (const ancestor of ancestors) {
|
||||
try {
|
||||
const domain = hierarchy.getClass(ancestor).domain
|
||||
if (domain === baseDomain) {
|
||||
result = ancestor
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
async function getTitle (object: Doc): Promise<string> {
|
||||
const name = (object as any).name
|
||||
if (name !== undefined) {
|
||||
if (hierarchy.isDerived(object._class, contact.class.Person)) {
|
||||
return formatName(name)
|
||||
}
|
||||
return name
|
||||
}
|
||||
const label = hierarchy.getClass(object._class).label
|
||||
return await translate(label, {})
|
||||
}
|
||||
|
||||
async function getHeaderEditor (_class: Ref<Class<Doc>>): Promise<AnyComponent | undefined> {
|
||||
const clazz = hierarchy.getClass(_class)
|
||||
const editorMixin = hierarchy.as(clazz, view.mixin.ObjectEditorHeader)
|
||||
if (editorMixin.editor != null) return editorMixin.editor
|
||||
if (clazz.extends != null) return getHeaderEditor(clazz.extends)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if object !== undefined}
|
||||
{#await getTitle(object) then title}
|
||||
<Panel
|
||||
{icon}
|
||||
{title}
|
||||
{rightSection}
|
||||
{fullSize}
|
||||
{object}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<div class="w-full" slot="subtitle">
|
||||
{#await getHeaderEditor(object._class) then is}
|
||||
{#if is}
|
||||
<Component {is} props={{ object, keys }} />
|
||||
{:else}
|
||||
<AttributesBar {object} {keys} />
|
||||
{/if}
|
||||
{/await}
|
||||
</div>
|
||||
<div class="main-editor">
|
||||
{#await getEditorOrDefault(selectedClass, object._class) then is}
|
||||
<Component
|
||||
{is}
|
||||
props={{ object }}
|
||||
on:open={(ev) => {
|
||||
updateKeys(ev.detail.ignoreKeys)
|
||||
}}
|
||||
on:click={(ev) => {
|
||||
fullSize = true
|
||||
rightSection = ev.detail.presenter
|
||||
}}
|
||||
/>
|
||||
{/await}
|
||||
</div>
|
||||
{#if mixins.length > 0}
|
||||
<div class="mixin-container">
|
||||
<div
|
||||
class="mixin-selector"
|
||||
style={getMixinStyle(objectClass._id, selectedClass === objectClass._id)}
|
||||
on:click={() => {
|
||||
selectedClass = objectClass._id
|
||||
selectedMixin = undefined
|
||||
}}
|
||||
>
|
||||
<Label label={objectClass.label} />
|
||||
</div>
|
||||
{#each mixins as mixin}
|
||||
<div
|
||||
class="mixin-selector"
|
||||
style={getMixinStyle(mixin._id, selectedClass === mixin._id)}
|
||||
on:click={() => {
|
||||
selectedClass = mixin._id
|
||||
selectedMixin = mixin
|
||||
}}
|
||||
>
|
||||
<Label label={mixin.label} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#each collectionKeys as collection}
|
||||
<div class="mt-14">
|
||||
{#await getCollectionEditor(collection) then is}
|
||||
<Component
|
||||
{is}
|
||||
props={{
|
||||
objectId: object._id,
|
||||
_class: object._class,
|
||||
space: object.space,
|
||||
[collection.key]: getCollectionCounter(object, collection)
|
||||
}}
|
||||
/>
|
||||
{/await}
|
||||
</div>
|
||||
{/each}
|
||||
</Panel>
|
||||
{/await}
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.main-editor {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
.mixin-container {
|
||||
margin-top: 2rem;
|
||||
display: flex;
|
||||
.mixin-selector {
|
||||
margin-left: 0.5rem;
|
||||
cursor: pointer;
|
||||
height: 1.5rem;
|
||||
min-width: 5.25rem;
|
||||
|
||||
border-radius: 0.5rem;
|
||||
|
||||
font-weight: 500;
|
||||
font-size: 0.625rem;
|
||||
|
||||
text-transform: uppercase;
|
||||
color: #ffffff;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -14,16 +14,14 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Contact } from '@anticrm/contact'
|
||||
import { ClassifierKind, Doc, Mixin } from '@anticrm/core'
|
||||
import { Class, ClassifierKind, Doc, Mixin, Ref } from '@anticrm/core'
|
||||
import {
|
||||
getClient
|
||||
} from '@anticrm/presentation'
|
||||
import { Label } from '@anticrm/ui'
|
||||
import contact from '../plugin'
|
||||
import { getMixinStyle } from '../utils'
|
||||
|
||||
export let value: Contact
|
||||
export let value: Doc
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
@ -31,8 +29,20 @@
|
||||
let mixins: Mixin<Doc>[] = []
|
||||
|
||||
$: if (value !== undefined) {
|
||||
const baseDomain = hierarchy.getDomain(value._class)
|
||||
const ancestors = hierarchy.getAncestors(value._class)
|
||||
let parentClass: Ref<Class<Doc>> = value._class
|
||||
for (const ancestor of ancestors) {
|
||||
try {
|
||||
const domain = hierarchy.getClass(ancestor).domain
|
||||
if (domain === baseDomain) {
|
||||
parentClass = ancestor
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
mixins = hierarchy
|
||||
.getDescendants(contact.class.Contact)
|
||||
.getDescendants(parentClass)
|
||||
.filter((m) => hierarchy.getClass(m).kind === ClassifierKind.MIXIN && hierarchy.hasMixin(value, m))
|
||||
.map((m) => hierarchy.getClass(m) as Mixin<Doc>)
|
||||
}
|
@ -28,10 +28,12 @@ import TableView from './components/TableView.svelte'
|
||||
import TimestampPresenter from './components/TimestampPresenter.svelte'
|
||||
import { deleteObject } from './utils'
|
||||
import MoveView from './components/Move.svelte'
|
||||
import EditDoc from './components/EditDoc.svelte'
|
||||
import RolePresenter from './components/RolePresenter.svelte'
|
||||
|
||||
export { default as ContextMenu } from './components/Menu.svelte'
|
||||
export { buildModel, getActions, getObjectPresenter, LoadingProps } from './utils'
|
||||
export { Table, TableView }
|
||||
export { Table, TableView, EditDoc }
|
||||
|
||||
function Delete (object: Doc): void {
|
||||
showPopup(
|
||||
@ -66,6 +68,7 @@ export default async (): Promise<Resources> => ({
|
||||
TableView,
|
||||
TimestampPresenter,
|
||||
DateEditor,
|
||||
DatePresenter
|
||||
DatePresenter,
|
||||
RolePresenter
|
||||
}
|
||||
})
|
||||
|
@ -14,13 +14,25 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import core, { AttachedDoc, Class, Client, Collection, Doc, FindOptions, FindResult, Obj, Ref, TxOperations, matchQuery } from '@anticrm/core'
|
||||
import core, {
|
||||
AttachedDoc,
|
||||
Class,
|
||||
Client,
|
||||
Collection,
|
||||
Doc,
|
||||
FindOptions,
|
||||
FindResult,
|
||||
Obj,
|
||||
Ref,
|
||||
TxOperations,
|
||||
matchQuery
|
||||
} from '@anticrm/core'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import { getAttributePresenterClass } from '@anticrm/presentation'
|
||||
import type { Action, ActionTarget, BuildModelOptions } from '@anticrm/view'
|
||||
import view, { AttributeModel, BuildModelKey } from '@anticrm/view'
|
||||
import { ErrorPresenter } from '@anticrm/ui'
|
||||
import { ErrorPresenter, getPlatformColorForText } from '@anticrm/ui'
|
||||
|
||||
/**
|
||||
* Define some properties to be used to show component until data is properly loaded.
|
||||
@ -32,7 +44,11 @@ export interface LoadingProps {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function getObjectPresenter (client: Client, _class: Ref<Class<Obj>>, preserveKey: BuildModelKey): Promise<AttributeModel> {
|
||||
export async function getObjectPresenter (
|
||||
client: Client,
|
||||
_class: Ref<Class<Obj>>,
|
||||
preserveKey: BuildModelKey
|
||||
): Promise<AttributeModel> {
|
||||
const clazz = client.getHierarchy().getClass(_class)
|
||||
const presenterMixin = client.getHierarchy().as(clazz, view.mixin.AttributePresenter)
|
||||
if (presenterMixin.presenter === undefined) {
|
||||
@ -44,9 +60,8 @@ export async function getObjectPresenter (client: Client, _class: Ref<Class<Obj>
|
||||
}
|
||||
const presenter = await getResource(presenterMixin.presenter)
|
||||
const key = preserveKey.sortingKey ?? preserveKey.key
|
||||
const sortingKey = clazz.sortingKey !== undefined
|
||||
? (key.length > 0 ? key + '.' + clazz.sortingKey : clazz.sortingKey)
|
||||
: key
|
||||
const sortingKey =
|
||||
clazz.sortingKey !== undefined ? (key.length > 0 ? key + '.' + clazz.sortingKey : clazz.sortingKey) : key
|
||||
return {
|
||||
key: preserveKey.key,
|
||||
_class,
|
||||
@ -56,7 +71,12 @@ export async function getObjectPresenter (client: Client, _class: Ref<Class<Obj>
|
||||
}
|
||||
}
|
||||
|
||||
async function getAttributePresenter (client: Client, _class: Ref<Class<Obj>>, key: string, preserveKey: BuildModelKey): Promise<AttributeModel> {
|
||||
async function getAttributePresenter (
|
||||
client: Client,
|
||||
_class: Ref<Class<Obj>>,
|
||||
key: string,
|
||||
preserveKey: BuildModelKey
|
||||
): Promise<AttributeModel> {
|
||||
const attribute = client.getHierarchy().getAttribute(_class, key)
|
||||
let attrClass = getAttributePresenterClass(attribute)
|
||||
const clazz = client.getHierarchy().getClass(attrClass)
|
||||
@ -84,7 +104,13 @@ 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 (key.presenter !== undefined) {
|
||||
const { presenter, label, sortingKey } = key
|
||||
return {
|
||||
@ -122,11 +148,13 @@ async function getPresenter (client: Client, _class: Ref<Class<Obj>>, key: Build
|
||||
export async function buildModel (options: BuildModelOptions): Promise<AttributeModel[]> {
|
||||
console.log('building table model for', options)
|
||||
// eslint-disable-next-line array-callback-return
|
||||
const model = options.keys.map(key => typeof key === 'string' ? { key: key } : key).map(async key => {
|
||||
const model = options.keys
|
||||
.map((key) => (typeof key === 'string' ? { key: key } : key))
|
||||
.map(async (key) => {
|
||||
try {
|
||||
return await getPresenter(options.client, options._class, key, key, options.options)
|
||||
} catch (err: any) {
|
||||
if ((options.ignoreMissing ?? false)) {
|
||||
if (options.ignoreMissing ?? false) {
|
||||
return undefined
|
||||
}
|
||||
const stringKey = key.label ?? key.key
|
||||
@ -142,10 +170,15 @@ export async function buildModel (options: BuildModelOptions): Promise<Attribute
|
||||
return errorPresenter
|
||||
}
|
||||
})
|
||||
return (await Promise.all(model)).filter(a => a !== undefined) as AttributeModel[]
|
||||
return (await Promise.all(model)).filter((a) => a !== undefined) as AttributeModel[]
|
||||
}
|
||||
|
||||
function filterActions (client: Client, doc: Doc, targets: ActionTarget[], derived: Ref<Class<Doc>> = core.class.Doc): Array<Ref<Action>> {
|
||||
function filterActions (
|
||||
client: Client,
|
||||
doc: Doc,
|
||||
targets: ActionTarget[],
|
||||
derived: Ref<Class<Doc>> = core.class.Doc
|
||||
): Array<Ref<Action>> {
|
||||
const result: Array<Ref<Action>> = []
|
||||
const hierarchy = client.getHierarchy()
|
||||
for (const target of targets) {
|
||||
@ -170,7 +203,11 @@ function filterActions (client: Client, doc: Doc, targets: ActionTarget[], deriv
|
||||
* So if we have contribution for Doc, Space and we ask for SpaceWithStates and derivedFrom=Space,
|
||||
* we won't recieve Doc contribution but recieve Space ones.
|
||||
*/
|
||||
export async function getActions (client: Client, doc: Doc, derived: Ref<Class<Doc>> = core.class.Doc): Promise<FindResult<Action>> {
|
||||
export async function getActions (
|
||||
client: Client,
|
||||
doc: Doc,
|
||||
derived: Ref<Class<Doc>> = core.class.Doc
|
||||
): Promise<FindResult<Action>> {
|
||||
const targets = await client.findAll(view.class.ActionTarget, {})
|
||||
return await client.findAll(view.class.Action, { _id: { $in: filterActions(client, doc, targets, derived) } })
|
||||
}
|
||||
@ -183,14 +220,24 @@ export async function deleteObject (client: TxOperations, object: Doc): Promise<
|
||||
const collection = attribute.type as Collection<AttachedDoc>
|
||||
const allAttached = await client.findAll(collection.of, { attachedTo: object._id })
|
||||
for (const attached of allAttached) {
|
||||
deleteObject(client, attached).catch(err => console.log('failed to delete', name, err))
|
||||
deleteObject(client, attached).catch((err) => console.log('failed to delete', name, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (client.getHierarchy().isDerived(object._class, core.class.AttachedDoc)) {
|
||||
const adoc = object as AttachedDoc
|
||||
client.removeCollection(object._class, object.space, adoc._id, adoc.attachedTo, adoc.attachedToClass, adoc.collection).catch(err => console.error(err))
|
||||
client
|
||||
.removeCollection(object._class, object.space, adoc._id, adoc.attachedTo, adoc.attachedToClass, adoc.collection)
|
||||
.catch((err) => console.error(err))
|
||||
} else {
|
||||
client.removeDoc(object._class, object.space, object._id).catch(err => console.error(err))
|
||||
client.removeDoc(object._class, object.space, object._id).catch((err) => console.error(err))
|
||||
}
|
||||
}
|
||||
|
||||
export function getMixinStyle (id: Ref<Class<Doc>>, selected: boolean): string {
|
||||
const color = getPlatformColorForText(id as string)
|
||||
return `
|
||||
background: ${color + (selected ? 'ff' : '33')};
|
||||
border: 1px solid ${color + (selected ? '0f' : '66')};
|
||||
`
|
||||
}
|
||||
|
@ -40,6 +40,13 @@ export interface ObjectEditor extends Class<Doc> {
|
||||
editor: AnyComponent
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ObjectEditorHeader extends Class<Doc> {
|
||||
editor: AnyComponent
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -60,7 +67,6 @@ export interface ViewletDescriptor extends Doc, UXObject {
|
||||
export interface Viewlet extends Doc {
|
||||
attachTo: Ref<Class<Space>>
|
||||
descriptor: Ref<ViewletDescriptor>
|
||||
open: AnyComponent
|
||||
options?: FindOptions<Doc>
|
||||
config: any
|
||||
}
|
||||
@ -75,7 +81,7 @@ export interface Action extends Doc, UXObject {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ActionTarget<T extends Doc=Doc> extends Doc {
|
||||
export interface ActionTarget<T extends Doc = Doc> extends Doc {
|
||||
target: Ref<Class<T>>
|
||||
action: Ref<Action>
|
||||
|
||||
@ -141,6 +147,7 @@ const view = plugin(viewId, {
|
||||
AttributeEditor: '' as Ref<Mixin<AttributeEditor>>,
|
||||
AttributePresenter: '' as Ref<Mixin<AttributePresenter>>,
|
||||
ObjectEditor: '' as Ref<Mixin<ObjectEditor>>,
|
||||
ObjectEditorHeader: '' as Ref<Mixin<ObjectEditorHeader>>,
|
||||
ObjectValidator: '' as Ref<Mixin<ObjectValidator>>,
|
||||
ObjectFactory: '' as Ref<Mixin<ObjectFactory>>
|
||||
},
|
||||
|
@ -31,7 +31,6 @@
|
||||
<Component is={viewlet.$lookup?.descriptor?.component} props={ {
|
||||
_class,
|
||||
space,
|
||||
open: viewlet.open,
|
||||
options: viewlet.options,
|
||||
config: viewlet.config,
|
||||
search
|
||||
|
Loading…
Reference in New Issue
Block a user