mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-23 04:42:22 +02:00
141 lines
4.1 KiB
TypeScript
141 lines
4.1 KiB
TypeScript
import { Hierarchy, Ref, RefTo, Class, Doc, SearchResultDoc, docKey } from '@hcengineering/core'
|
|
import { getResource } from '@hcengineering/platform'
|
|
|
|
import plugin from './plugin'
|
|
import { IndexedDoc, SearchPresenter, ClassSearchConfigProps } from './types'
|
|
|
|
interface IndexedReader {
|
|
get: (attribute: string) => any
|
|
getDoc: (attribute: string) => IndexedReader | undefined
|
|
}
|
|
|
|
function createIndexedReader (
|
|
_class: Ref<Class<Doc>>,
|
|
hierarchy: Hierarchy,
|
|
doc: IndexedDoc,
|
|
refAttribute?: string
|
|
): IndexedReader {
|
|
return {
|
|
get: (attr: string) => {
|
|
const realAttr = hierarchy.findAttribute(_class, attr)
|
|
if (realAttr !== undefined) {
|
|
return doc[docKey(attr, { refAttribute, _class: realAttr.attributeOf })]
|
|
}
|
|
return undefined
|
|
},
|
|
getDoc: (attr: string) => {
|
|
const realAttr = hierarchy.findAttribute(_class, attr)
|
|
if (realAttr !== undefined) {
|
|
const refAtrr = realAttr.type as RefTo<Doc>
|
|
return createIndexedReader(refAtrr.to, hierarchy, doc, docKey(attr, { _class }))
|
|
}
|
|
return undefined
|
|
}
|
|
}
|
|
}
|
|
|
|
function readAndMapProps (reader: IndexedReader, props: ClassSearchConfigProps[]): Record<string, any> {
|
|
const res: Record<string, any> = {}
|
|
for (const prop of props) {
|
|
if (typeof prop === 'string') {
|
|
res[prop] = reader.get(prop)
|
|
} else {
|
|
for (const [propName, rest] of Object.entries(prop)) {
|
|
if (rest.length > 1) {
|
|
const val = reader.getDoc(rest[0])?.get(rest[1])
|
|
res[propName] = Array.isArray(val) ? val[0] : val
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
|
|
function findSearchPresenter (hierarchy: Hierarchy, _class: Ref<Class<Doc>>): SearchPresenter | undefined {
|
|
const ancestors = hierarchy.getAncestors(_class).reverse()
|
|
for (const _class of ancestors) {
|
|
const searchMixin = hierarchy.classHierarchyMixin(_class, plugin.mixin.SearchPresenter)
|
|
if (searchMixin !== undefined) {
|
|
return searchMixin
|
|
}
|
|
}
|
|
return undefined
|
|
}
|
|
|
|
/**
|
|
* @public
|
|
*/
|
|
export async function updateDocWithPresenter (hierarchy: Hierarchy, doc: IndexedDoc): Promise<void> {
|
|
const searchPresenter = findSearchPresenter(hierarchy, doc._class)
|
|
if (searchPresenter === undefined) {
|
|
return
|
|
}
|
|
|
|
const reader = createIndexedReader(doc._class, hierarchy, doc)
|
|
|
|
const props = [
|
|
{
|
|
name: 'searchTitle',
|
|
config: searchPresenter.searchConfig.title,
|
|
provider: searchPresenter.getSearchTitle
|
|
}
|
|
]
|
|
|
|
if (searchPresenter.searchConfig.shortTitle !== undefined) {
|
|
props.push({
|
|
name: 'searchShortTitle',
|
|
config: searchPresenter.searchConfig.shortTitle,
|
|
provider: searchPresenter.getSearchShortTitle
|
|
})
|
|
}
|
|
|
|
for (const prop of props) {
|
|
let value
|
|
if (typeof prop.config === 'string') {
|
|
value = reader.get(prop.config)
|
|
} else if (prop.config.tmpl !== undefined) {
|
|
const tmpl = prop.config.tmpl
|
|
const renderProps = readAndMapProps(reader, prop.config.props)
|
|
value = fillTemplate(tmpl, renderProps)
|
|
} else if (prop.provider !== undefined) {
|
|
const func = await getResource(prop.provider)
|
|
const renderProps = readAndMapProps(reader, prop.config.props)
|
|
value = func(hierarchy, { _class: doc._class, ...renderProps })
|
|
}
|
|
doc[prop.name] = value
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @public
|
|
*/
|
|
export function mapSearchResultDoc (hierarchy: Hierarchy, raw: IndexedDoc): SearchResultDoc {
|
|
const doc: SearchResultDoc = {
|
|
id: raw.id,
|
|
title: raw.searchTitle,
|
|
shortTitle: raw.searchShortTitle,
|
|
doc: {
|
|
_id: raw.id,
|
|
_class: raw._class
|
|
}
|
|
}
|
|
|
|
const searchPresenter = findSearchPresenter(hierarchy, doc.doc._class)
|
|
if (searchPresenter?.searchConfig.icon !== undefined) {
|
|
doc.icon = searchPresenter.searchConfig.icon
|
|
}
|
|
if (searchPresenter?.searchConfig.iconConfig !== undefined) {
|
|
doc.iconComponent = searchPresenter.searchConfig.iconConfig.component
|
|
doc.iconProps = readAndMapProps(
|
|
createIndexedReader(raw._class, hierarchy, raw),
|
|
searchPresenter.searchConfig.iconConfig.props
|
|
)
|
|
}
|
|
|
|
return doc
|
|
}
|
|
|
|
function fillTemplate (tmpl: string, props: Record<string, any>): string {
|
|
return tmpl.replace(/{(.*?)}/g, (_, key: string) => props[key])
|
|
}
|