mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-23 19:44:59 +03:00
Add list item presenter (#2449)
Signed-off-by: Sergei Ogorelkov <sergei.ogorelkov@xored.com>
This commit is contained in:
parent
0718ca0812
commit
bf550d067f
@ -44,6 +44,7 @@ import type {
|
|||||||
ObjectTitle,
|
ObjectTitle,
|
||||||
ObjectValidator,
|
ObjectValidator,
|
||||||
PreviewPresenter,
|
PreviewPresenter,
|
||||||
|
ListItemPresenter,
|
||||||
SpaceHeader,
|
SpaceHeader,
|
||||||
SpaceName,
|
SpaceName,
|
||||||
ViewAction,
|
ViewAction,
|
||||||
@ -133,6 +134,11 @@ export class TAttributePresenter extends TClass implements AttributePresenter {
|
|||||||
presenter!: AnyComponent
|
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)
|
@Mixin(view.mixin.ObjectEditor, core.class.Class)
|
||||||
export class TObjectEditor extends TClass implements ObjectEditor {
|
export class TObjectEditor extends TClass implements ObjectEditor {
|
||||||
editor!: AnyComponent
|
editor!: AnyComponent
|
||||||
@ -269,6 +275,7 @@ export function createModel (builder: Builder): void {
|
|||||||
TAttributeFilter,
|
TAttributeFilter,
|
||||||
TAttributeEditor,
|
TAttributeEditor,
|
||||||
TAttributePresenter,
|
TAttributePresenter,
|
||||||
|
TListItemPresenter,
|
||||||
TCollectionEditor,
|
TCollectionEditor,
|
||||||
TCollectionPresenter,
|
TCollectionPresenter,
|
||||||
TObjectEditor,
|
TObjectEditor,
|
||||||
|
@ -17,16 +17,17 @@
|
|||||||
import { getResource, IntlString } from '@hcengineering/platform'
|
import { getResource, IntlString } from '@hcengineering/platform'
|
||||||
import presentation, { createQuery, getClient } from '@hcengineering/presentation'
|
import presentation, { createQuery, getClient } from '@hcengineering/presentation'
|
||||||
import { calcRank, DocWithRank } from '@hcengineering/task'
|
import { calcRank, DocWithRank } from '@hcengineering/task'
|
||||||
import { AnyComponent, Button, Component, IconAdd, Label, Loading } from '@hcengineering/ui'
|
import { Button, Component, IconAdd, Label, Loading } from '@hcengineering/ui'
|
||||||
import view, { AttributeModel, ObjectFactory } from '@hcengineering/view'
|
import view, { ObjectFactory } from '@hcengineering/view'
|
||||||
import { flip } from 'svelte/animate'
|
import { flip } from 'svelte/animate'
|
||||||
import { getObjectPresenter } from '../../utils'
|
import { SvelteComponentDev } from 'svelte/internal'
|
||||||
|
import { getListItemPresenter, getObjectPresenter } from '../../utils'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
How to use:
|
How to use:
|
||||||
|
|
||||||
We must add presenter for the "_class" via "AttributePresenter" mixin
|
We must add presenter for the "_class" via "ListItemPresenter" / "AttributePresenter"
|
||||||
or pass it through "presenter" prop to be able display the rows list.
|
mixins or render the "object" slot to be able display the rows list.
|
||||||
|
|
||||||
To create a new items, we should add "ObjectFactory" mixin also.
|
To create a new items, we should add "ObjectFactory" mixin also.
|
||||||
|
|
||||||
@ -39,7 +40,6 @@
|
|||||||
export let label: IntlString | undefined = undefined
|
export let label: IntlString | undefined = undefined
|
||||||
export let query: DocumentQuery<Doc> = {}
|
export let query: DocumentQuery<Doc> = {}
|
||||||
export let queryOptions: FindOptions<Doc> | undefined = undefined
|
export let queryOptions: FindOptions<Doc> | undefined = undefined
|
||||||
export let presenter: AnyComponent | undefined = undefined
|
|
||||||
export let presenterProps: Record<string, any> = {}
|
export let presenterProps: Record<string, any> = {}
|
||||||
export let direction: 'row' | 'column' = 'column'
|
export let direction: 'row' | 'column' = 'column'
|
||||||
export let flipDuration = 200
|
export let flipDuration = 200
|
||||||
@ -51,11 +51,11 @@
|
|||||||
const hierarchy = client.getHierarchy()
|
const hierarchy = client.getHierarchy()
|
||||||
const itemsQuery = createQuery()
|
const itemsQuery = createQuery()
|
||||||
|
|
||||||
let isModelLoading = false
|
let isPresenterLoading = false
|
||||||
let areItemsloading = true
|
let areItemsloading = true
|
||||||
let areItemsSorting = false
|
let areItemsSorting = false
|
||||||
|
|
||||||
let model: AttributeModel | undefined
|
let presenter: typeof SvelteComponentDev | undefined
|
||||||
let objectFactory: ObjectFactory | undefined
|
let objectFactory: ObjectFactory | undefined
|
||||||
let items: FindResult<Doc> | undefined
|
let items: FindResult<Doc> | undefined
|
||||||
|
|
||||||
@ -64,12 +64,22 @@
|
|||||||
|
|
||||||
let isCreating = false
|
let isCreating = false
|
||||||
|
|
||||||
async function updateModel (modelClassRef: Ref<Class<Doc>>, props: Record<string, any>) {
|
async function updatePresenter (classRef: Ref<Class<Doc>>) {
|
||||||
try {
|
try {
|
||||||
isModelLoading = true
|
isPresenterLoading = true
|
||||||
model = await getObjectPresenter(client, modelClassRef, { key: '', props })
|
|
||||||
|
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 {
|
} finally {
|
||||||
isModelLoading = false
|
isPresenterLoading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,11 +145,11 @@
|
|||||||
hoveringIndex = null
|
hoveringIndex = null
|
||||||
}
|
}
|
||||||
|
|
||||||
$: !presenter && updateModel(_class, presenterProps)
|
$: !$$slots.object && updatePresenter(_class)
|
||||||
$: updateObjectFactory(_class)
|
$: updateObjectFactory(_class)
|
||||||
$: itemsQuery.query(_class, query, updateItems, { ...queryOptions, limit: Math.max(queryOptions?.limit ?? 0, 200) })
|
$: 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')
|
$: isSortable = hierarchy.getAllAttributes(_class).has('rank')
|
||||||
$: itemsCount = items?.length ?? 0
|
$: itemsCount = items?.length ?? 0
|
||||||
</script>
|
</script>
|
||||||
@ -172,7 +182,7 @@
|
|||||||
|
|
||||||
{#if isLoading}
|
{#if isLoading}
|
||||||
<Loading />
|
<Loading />
|
||||||
{:else if (presenter || model) && items}
|
{:else if ($$slots.object ?? presenter) && items}
|
||||||
{@const isVertical = direction === 'column'}
|
{@const isVertical = direction === 'column'}
|
||||||
<div class="flex-gap-1" class:flex-col={isVertical} class:flex={!isVertical} class:flex-wrap={!isVertical}>
|
<div class="flex-gap-1" class:flex-col={isVertical} class:flex={!isVertical} class:flex-wrap={!isVertical}>
|
||||||
{#each items as item, index (item._id)}
|
{#each items as item, index (item._id)}
|
||||||
@ -190,10 +200,10 @@
|
|||||||
on:drop={() => handleDrop(index)}
|
on:drop={() => handleDrop(index)}
|
||||||
on:dragend={resetDrag}
|
on:dragend={resetDrag}
|
||||||
>
|
>
|
||||||
{#if presenter}
|
{#if $$slots.object}
|
||||||
<Component is={presenter} props={{ isDraggable, ...presenterProps, value: item }} />
|
<slot name="object" value={item} {isDraggable} />
|
||||||
{:else if model}
|
{:else if presenter}
|
||||||
<svelte:component this={model.presenter} {isDraggable} {...model.props ?? {}} value={item} />
|
<svelte:component this={presenter} {isDraggable} {...presenterProps} value={item} />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -91,6 +91,20 @@ export async function getObjectPresenter (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export async function getListItemPresenter (client: Client, _class: Ref<Class<Obj>>): Promise<AnyComponent | undefined> {
|
||||||
|
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
|
* @public
|
||||||
*/
|
*/
|
||||||
|
@ -128,6 +128,13 @@ export interface AttributePresenter extends Class<Doc> {
|
|||||||
presenter: AnyComponent
|
presenter: AnyComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface ListItemPresenter extends Class<Doc> {
|
||||||
|
presenter: AnyComponent
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
@ -401,6 +408,7 @@ const view = plugin(viewId, {
|
|||||||
InlineAttributEditor: '' as Ref<Mixin<InlineAttributEditor>>,
|
InlineAttributEditor: '' as Ref<Mixin<InlineAttributEditor>>,
|
||||||
ArrayEditor: '' as Ref<Mixin<ArrayEditor>>,
|
ArrayEditor: '' as Ref<Mixin<ArrayEditor>>,
|
||||||
AttributePresenter: '' as Ref<Mixin<AttributePresenter>>,
|
AttributePresenter: '' as Ref<Mixin<AttributePresenter>>,
|
||||||
|
ListItemPresenter: '' as Ref<Mixin<ListItemPresenter>>,
|
||||||
ObjectEditor: '' as Ref<Mixin<ObjectEditor>>,
|
ObjectEditor: '' as Ref<Mixin<ObjectEditor>>,
|
||||||
ObjectEditorHeader: '' as Ref<Mixin<ObjectEditorHeader>>,
|
ObjectEditorHeader: '' as Ref<Mixin<ObjectEditorHeader>>,
|
||||||
ObjectValidator: '' as Ref<Mixin<ObjectValidator>>,
|
ObjectValidator: '' as Ref<Mixin<ObjectValidator>>,
|
||||||
|
Loading…
Reference in New Issue
Block a user