UBER-1187: AnyType field support (#4343)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2024-01-12 11:33:18 +07:00 committed by GitHub
parent f972066df6
commit 262c2dd82c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 100 additions and 39 deletions

View File

@ -14,6 +14,13 @@
//
import {
DOMAIN_BLOB,
DOMAIN_CONFIGURATION,
DOMAIN_DOC_INDEX_STATE,
DOMAIN_FULLTEXT_BLOB,
DOMAIN_MIGRATION,
DOMAIN_MODEL,
IndexKind,
type Account,
type AnyAttribute,
type ArrOf,
@ -27,21 +34,15 @@ import {
type Doc,
type DocIndexState,
type Domain,
DOMAIN_BLOB,
DOMAIN_CONFIGURATION,
DOMAIN_MIGRATION,
DOMAIN_DOC_INDEX_STATE,
DOMAIN_FULLTEXT_BLOB,
DOMAIN_MODEL,
type Enum,
type EnumOf,
type FieldIndex,
type FullTextData,
type FullTextSearchContext,
type IndexingConfiguration,
IndexKind,
type IndexStageState,
type IndexingConfiguration,
type Interface,
type MigrationState,
type Mixin,
type Obj,
type PluginConfiguration,
@ -50,8 +51,8 @@ import {
type Space,
type Timestamp,
type Type,
type Version,
type MigrationState
type TypeAny,
type Version
} from '@hcengineering/core'
import {
Hidden,
@ -243,6 +244,13 @@ export class TEnumOf extends TType implements EnumOf {
of!: Ref<Enum>
}
@UX(getEmbeddedLabel('Any'))
@Model(core.class.TypeAny, core.class.Type)
export class TTypeAny extends TType implements TypeAny {
presenter!: any
editor!: any
}
@Model(core.class.Version, core.class.Doc, DOMAIN_MODEL)
export class TVersion extends TDoc implements Version {
major!: number

View File

@ -15,13 +15,13 @@
import {
AccountRole,
systemAccountEmail,
type AttachedDoc,
type Class,
type Doc,
type DocIndexState,
type IndexingConfiguration,
type TxCollectionCUD,
systemAccountEmail
type TxCollectionCUD
} from '@hcengineering/core'
import { type Builder } from '@hcengineering/model'
import core from './component'
@ -49,6 +49,7 @@ import {
TPluginConfiguration,
TRefTo,
TType,
TTypeAny,
TTypeAttachment,
TTypeBoolean,
TTypeCollaborativeMarkup,
@ -126,6 +127,7 @@ export function createModel (builder: Builder): void {
TPluginConfiguration,
TUserStatus,
TEnum,
TTypeAny,
TBlobData,
TFulltextData,
TTypeRelatedDocument,

View File

@ -291,6 +291,16 @@ export interface EnumOf extends Type<string> {
*/
export interface TypeHyperlink extends Type<Hyperlink> {}
/**
* @public
*
* A type for some custom serialized field with a set of editors
*/
export interface TypeAny<AnyComponent = any> extends Type<any> {
presenter: AnyComponent
editor?: AnyComponent
}
/**
* @public
*/

View File

@ -45,6 +45,7 @@ import type {
Space,
Timestamp,
Type,
TypeAny,
UserStatus
} from './classes'
import { Status, StatusCategory } from './status'
@ -109,6 +110,7 @@ export default plugin(coreId, {
Enum: '' as Ref<Class<Enum>>,
EnumOf: '' as Ref<Class<EnumOf>>,
Collection: '' as Ref<Class<Collection<AttachedDoc>>>,
TypeAny: '' as Ref<Class<TypeAny>>,
Version: '' as Ref<Class<Version>>,
PluginConfiguration: '' as Ref<Class<PluginConfiguration>>,
UserStatus: '' as Ref<Class<UserStatus>>,

View File

@ -15,25 +15,22 @@
import core, {
Account,
ArrOf as TypeArrOf,
AttachedDoc,
Attribute,
Class,
Classifier,
ClassifierKind,
Collection as TypeCollection,
Data,
DateRangeMode,
Doc,
Domain,
Enum,
EnumOf,
generateId,
Hyperlink,
Mixin as IMixin,
IndexKind,
Interface,
Markup,
Mixin as IMixin,
MixinUpdate,
Obj,
PropertyType,
@ -46,7 +43,11 @@ import core, {
TxFactory,
TxProcessor,
Type,
TypeDate as TypeDateType
TypeAny as TypeAnyType,
ArrOf as TypeArrOf,
Collection as TypeCollection,
TypeDate as TypeDateType,
generateId
} from '@hcengineering/core'
import type { Asset, IntlString } from '@hcengineering/platform'
import toposort from 'toposort'
@ -464,6 +465,17 @@ export function TypeEnum (of: Ref<Enum>): EnumOf {
return { _class: core.class.EnumOf, label: core.string.Enum, of }
}
/**
* @public
*/
export function TypeAny<AnyComponent = any> (
presenter: AnyComponent,
label: IntlString,
editor?: AnyComponent
): TypeAnyType<AnyComponent> {
return { _class: core.class.TypeAny, label, presenter, editor }
}
/**
* @public
*/

View File

@ -40,7 +40,7 @@
const dispatch = createEventDispatcher()
let editor: AnySvelteComponent | undefined
function onChange (value: any) {
function onChange (value: any): void {
const doc = object as Doc
dispatch('update', { key, value })
@ -48,12 +48,14 @@
if (draft) {
;(doc as any)[attributeKey] = value
} else {
updateAttribute(client, doc, doc._class, { key: attributeKey, attr: attribute }, value)
void updateAttribute(client, doc, doc._class, { key: attributeKey, attr: attribute }, value)
}
}
function getEditor (_class: Ref<Class<Doc>>, key: KeyedAttribute | string) {
getAttributeEditor(client, _class, key).then((p) => (editor = p))
function getEditor (_class: Ref<Class<Doc>>, key: KeyedAttribute | string): void {
void getAttributeEditor(client, _class, key).then((p) => {
editor = p
})
}
$: getEditor(_class, key)

View File

@ -48,10 +48,10 @@
}
}
function onChange (value: any) {
function onChange (value: any): void {
if (!editable) return
const doc = object as Doc
updateAttribute(client, doc, _class, { key: attributeKey, attr: attribute }, value)
void updateAttribute(client, doc, _class, { key: attributeKey, attr: attribute }, value)
}
</script>

View File

@ -15,6 +15,9 @@
//
import core, {
TxOperations,
type TypeAny,
getCurrentAccount,
type AnyAttribute,
type ArrOf,
type AttachedDoc,
@ -25,28 +28,26 @@ import core, {
type DocumentQuery,
type FindOptions,
type FindResult,
getCurrentAccount,
type Hierarchy,
type Mixin,
type Obj,
type Ref,
type RefTo,
type Tx,
TxOperations,
type TxResult,
type WithLookup,
type SearchQuery,
type SearchOptions,
type SearchResult
type SearchQuery,
type SearchResult,
type Tx,
type TxResult,
type WithLookup
} from '@hcengineering/core'
import { getMetadata, getResource } from '@hcengineering/platform'
import { LiveQuery as LQ } from '@hcengineering/query'
import { 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 { deepEqual } from 'fast-equals'
import { onDestroy } from 'svelte'
import { type KeyedAttribute } from '..'
import { OptimizeQueryMiddleware, type PresentationPipeline, PresentationPipelineImpl } from './pipeline'
import { OptimizeQueryMiddleware, PresentationPipelineImpl, type PresentationPipeline } from './pipeline'
import plugin from './plugin'
let liveQuery: LQ
@ -406,6 +407,12 @@ export async function getAttributeEditor (
): Promise<AnySvelteComponent | undefined> {
const hierarchy = client.getHierarchy()
const attribute = typeof key === 'string' ? hierarchy.getAttribute(_class, key) : key.attr
if (attribute.type._class === core.class.TypeAny) {
const _type: TypeAny = attribute.type as TypeAny<AnyComponent>
return await getResource(_type.editor ?? _type.presenter)
}
const presenterClass = attribute !== undefined ? getAttributePresenterClass(hierarchy, attribute) : undefined
if (presenterClass === undefined) {

View File

@ -19,13 +19,14 @@
import { Panel } from '@hcengineering/panel'
import { getResource } from '@hcengineering/platform'
import presentation, {
createQuery,
getClient,
ActionContext,
ComponentExtensions,
contextStore,
ComponentExtensions
createQuery,
getClient
} from '@hcengineering/presentation'
import setting, { settingId } from '@hcengineering/setting'
import { taskTypeStore, typeStore } from '@hcengineering/task-resources'
import { Issue, Project } from '@hcengineering/tracker'
import {
AnyComponent,
@ -42,8 +43,8 @@
navigate,
showPopup
} from '@hcengineering/ui'
import { ContextMenu, DocNavLink, ParentsNavigator } from '@hcengineering/view-resources'
import view from '@hcengineering/view'
import { ContextMenu, DocNavLink, ParentsNavigator } from '@hcengineering/view-resources'
import { createEventDispatcher, onDestroy } from 'svelte'
import { generateIssueShortLink, getIssueId } from '../../../issues'
import tracker from '../../../plugin'
@ -161,6 +162,10 @@
$: editorFooter = getEditorFooter(issue?._class)
let content: HTMLElement
$: taskType = issue?.kind !== undefined ? $taskTypeStore.get(issue?.kind) : undefined
$: projectType = taskType?.parent !== undefined ? $typeStore.get(taskType.parent) : undefined
</script>
{#if !embedded}
@ -187,12 +192,24 @@
on:select
>
<svelte:fragment slot="title">
{#if !embedded}<ParentsNavigator element={issue} />{/if}
{#if !embedded}
<ParentsNavigator element={issue} />
{/if}
{#if embedded && issueId}
<DocNavLink noUnderline object={issue}>
<div class="title">{issueId}</div>
</DocNavLink>
{:else if issueId}<div class="title not-active">{issueId}</div>{/if}
{:else if issueId}
<div class="title not-active">{issueId}</div>
{/if}
{#if (projectType?.tasks.length ?? 0) > 1 && taskType !== undefined}
({taskType.name})
{/if}
<ComponentExtensions
extension={tracker.extensions.EditIssueTitle}
props={{ size: 'medium', value: issue, space: currentProject }}
/>
</svelte:fragment>
<svelte:fragment slot="pre-utils">
<ComponentExtensions

View File

@ -532,7 +532,8 @@ const pluginState = plugin(trackerId, {
},
extensions: {
IssueListHeader: '' as ComponentExtensionId,
EditIssueHeader: '' as ComponentExtensionId
EditIssueHeader: '' as ComponentExtensionId,
EditIssueTitle: '' as ComponentExtensionId
},
taskTypes: {
Issue: '' as Ref<TaskType>,