diff --git a/models/view/src/index.ts b/models/view/src/index.ts index fa6067528d..f8b9a1018b 100644 --- a/models/view/src/index.ts +++ b/models/view/src/index.ts @@ -44,6 +44,7 @@ import type { ObjectTitle, ObjectValidator, PreviewPresenter, + ListItemPresenter, SpaceHeader, SpaceName, ViewAction, @@ -133,6 +134,11 @@ export class TAttributePresenter extends TClass implements AttributePresenter { presenter!: AnyComponent } +@Mixin(view.mixin.ListItemPresenter, core.class.Class) +export class TListItemPresenter extends TClass implements ListItemPresenter { + presenter!: AnyComponent +} + @Mixin(view.mixin.ObjectEditor, core.class.Class) export class TObjectEditor extends TClass implements ObjectEditor { editor!: AnyComponent @@ -269,6 +275,7 @@ export function createModel (builder: Builder): void { TAttributeFilter, TAttributeEditor, TAttributePresenter, + TListItemPresenter, TCollectionEditor, TCollectionPresenter, TObjectEditor, diff --git a/plugins/view-resources/src/components/list/SortableList.svelte b/plugins/view-resources/src/components/list/SortableList.svelte index 9704aaf6b0..36755a3196 100644 --- a/plugins/view-resources/src/components/list/SortableList.svelte +++ b/plugins/view-resources/src/components/list/SortableList.svelte @@ -17,16 +17,17 @@ import { getResource, IntlString } from '@hcengineering/platform' import presentation, { createQuery, getClient } from '@hcengineering/presentation' import { calcRank, DocWithRank } from '@hcengineering/task' - import { AnyComponent, Button, Component, IconAdd, Label, Loading } from '@hcengineering/ui' - import view, { AttributeModel, ObjectFactory } from '@hcengineering/view' + import { Button, Component, IconAdd, Label, Loading } from '@hcengineering/ui' + import view, { ObjectFactory } from '@hcengineering/view' import { flip } from 'svelte/animate' - import { getObjectPresenter } from '../../utils' + import { SvelteComponentDev } from 'svelte/internal' + import { getListItemPresenter, getObjectPresenter } from '../../utils' /* How to use: - We must add presenter for the "_class" via "AttributePresenter" mixin - or pass it through "presenter" prop to be able display the rows list. + We must add presenter for the "_class" via "ListItemPresenter" / "AttributePresenter" + mixins or render the "object" slot to be able display the rows list. To create a new items, we should add "ObjectFactory" mixin also. @@ -39,7 +40,6 @@ export let label: IntlString | undefined = undefined export let query: DocumentQuery = {} export let queryOptions: FindOptions | undefined = undefined - export let presenter: AnyComponent | undefined = undefined export let presenterProps: Record = {} export let direction: 'row' | 'column' = 'column' export let flipDuration = 200 @@ -51,11 +51,11 @@ const hierarchy = client.getHierarchy() const itemsQuery = createQuery() - let isModelLoading = false + let isPresenterLoading = false let areItemsloading = true let areItemsSorting = false - let model: AttributeModel | undefined + let presenter: typeof SvelteComponentDev | undefined let objectFactory: ObjectFactory | undefined let items: FindResult | undefined @@ -64,12 +64,22 @@ let isCreating = false - async function updateModel (modelClassRef: Ref>, props: Record) { + async function updatePresenter (classRef: Ref>) { try { - isModelLoading = true - model = await getObjectPresenter(client, modelClassRef, { key: '', props }) + isPresenterLoading = true + + const listItemPresenter = await getListItemPresenter(client, classRef) + if (listItemPresenter) { + presenter = await getResource(listItemPresenter) + return + } + + const objectModel = await getObjectPresenter(client, classRef, { key: '' }) + if (objectModel?.presenter) { + presenter = objectModel.presenter + } } finally { - isModelLoading = false + isPresenterLoading = false } } @@ -135,11 +145,11 @@ hoveringIndex = null } - $: !presenter && updateModel(_class, presenterProps) + $: !$$slots.object && updatePresenter(_class) $: updateObjectFactory(_class) $: itemsQuery.query(_class, query, updateItems, { ...queryOptions, limit: Math.max(queryOptions?.limit ?? 0, 200) }) - $: isLoading = isModelLoading || areItemsloading + $: isLoading = isPresenterLoading || areItemsloading $: isSortable = hierarchy.getAllAttributes(_class).has('rank') $: itemsCount = items?.length ?? 0 @@ -172,7 +182,7 @@ {#if isLoading} - {:else if (presenter || model) && items} + {:else if ($$slots.object ?? presenter) && items} {@const isVertical = direction === 'column'}
{#each items as item, index (item._id)} @@ -190,10 +200,10 @@ on:drop={() => handleDrop(index)} on:dragend={resetDrag} > - {#if presenter} - - {:else if model} - + {#if $$slots.object} + + {:else if presenter} + {/if}
{/each} diff --git a/plugins/view-resources/src/utils.ts b/plugins/view-resources/src/utils.ts index e798a8c854..a4a4385201 100644 --- a/plugins/view-resources/src/utils.ts +++ b/plugins/view-resources/src/utils.ts @@ -91,6 +91,20 @@ export async function getObjectPresenter ( } } +/** + * @public + */ +export async function getListItemPresenter (client: Client, _class: Ref>): Promise { + const clazz = client.getHierarchy().getClass(_class) + const presenterMixin = client.getHierarchy().as(clazz, view.mixin.ListItemPresenter) + if (presenterMixin.presenter === undefined) { + if (clazz.extends !== undefined) { + return await getListItemPresenter(client, clazz.extends) + } + } + return presenterMixin?.presenter +} + /** * @public */ diff --git a/plugins/view/src/index.ts b/plugins/view/src/index.ts index ee86c6d2c4..179e37f3fb 100644 --- a/plugins/view/src/index.ts +++ b/plugins/view/src/index.ts @@ -128,6 +128,13 @@ export interface AttributePresenter extends Class { presenter: AnyComponent } +/** + * @public + */ +export interface ListItemPresenter extends Class { + presenter: AnyComponent +} + /** * @public */ @@ -401,6 +408,7 @@ const view = plugin(viewId, { InlineAttributEditor: '' as Ref>, ArrayEditor: '' as Ref>, AttributePresenter: '' as Ref>, + ListItemPresenter: '' as Ref>, ObjectEditor: '' as Ref>, ObjectEditorHeader: '' as Ref>, ObjectValidator: '' as Ref>,