Card refs (#8319)
Some checks are pending
CI / build (push) Waiting to run
CI / svelte-check (push) Blocked by required conditions
CI / formatting (push) Blocked by required conditions
CI / test (push) Blocked by required conditions
CI / uitest (push) Waiting to run
CI / uitest-pg (push) Waiting to run
CI / uitest-qms (push) Waiting to run
CI / uitest-workspaces (push) Waiting to run
CI / docker-build (push) Blocked by required conditions
CI / dist-build (push) Blocked by required conditions

This commit is contained in:
Denis Bykhov 2025-03-24 07:56:55 +05:00 committed by GitHub
parent 2a735553e9
commit 34fc0024d3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 410 additions and 93 deletions

View File

@ -43,7 +43,7 @@ import presentation from '@hcengineering/model-presentation'
import setting from '@hcengineering/model-setting'
import view, { createAction } from '@hcengineering/model-view'
import workbench from '@hcengineering/model-workbench'
import { getEmbeddedLabel, type IntlString } from '@hcengineering/platform'
import { type Asset, getEmbeddedLabel, type IntlString } from '@hcengineering/platform'
import time, { type ToDo } from '@hcengineering/time'
import { type AnyComponent } from '@hcengineering/ui/src/types'
import { type BuildModelKey } from '@hcengineering/view'
@ -102,58 +102,43 @@ export class MasterTagEditorSection extends TDoc implements MasterTagEditorSecti
export * from './migration'
export function createModel (builder: Builder): void {
builder.createModel(TMasterTag, TTag, TCard, MasterTagEditorSection)
const listConfig: (BuildModelKey | string)[] = [
{ key: '', props: { showParent: true }, displayProps: { fixed: 'left', key: 'card' } },
{ key: '_class', displayProps: { fixed: 'left', key: 'type' } },
{ key: '', displayProps: { grow: true } },
{
key: '',
presenter: view.component.RolePresenter,
label: card.string.Tags,
props: { fullSize: true },
displayProps: { key: 'tags', fixed: 'right' }
},
{
key: 'modifiedOn',
displayProps: { fixed: 'right', dividerBefore: true }
}
]
export function createSystemType (
builder: Builder,
type: Ref<MasterTag>,
label: IntlString,
icon: Asset = card.icon.MasterTag
): void {
builder.createDoc(
card.class.MasterTag,
core.space.Model,
{
label: attachment.string.File,
label,
extends: card.class.Card,
icon: card.icon.File,
icon,
kind: ClassifierKind.CLASS
},
card.types.File
type
)
builder.mixin(card.types.File, card.class.MasterTag, setting.mixin.Editable, {
value: false
})
builder.createDoc(view.class.Viewlet, core.space.Model, {
attachTo: card.types.File,
descriptor: view.viewlet.Table,
configOptions: {
hiddenKeys: ['content', 'title']
},
config: [
'',
'_class',
{ key: '', presenter: view.component.RolePresenter, label: card.string.Tags, props: { fullSize: true } },
'modifiedOn'
]
})
const listConfig: (BuildModelKey | string)[] = [
{ key: '', props: { showParent: true }, displayProps: { fixed: 'left', key: 'card' } },
{ key: '_class', displayProps: { fixed: 'left', key: 'type' } },
{ key: '', displayProps: { grow: true } },
{
key: '',
presenter: view.component.RolePresenter,
label: card.string.Tags,
props: { fullSize: true },
displayProps: { key: 'tags', fixed: 'right' }
},
{
key: 'modifiedOn',
displayProps: { fixed: 'right', dividerBefore: true }
}
]
builder.createDoc(view.class.Viewlet, core.space.Model, {
attachTo: card.types.File,
attachTo: type,
descriptor: view.viewlet.List,
viewOptions: {
groupBy: ['_class', 'createdBy', 'modifiedBy'],
@ -169,6 +154,31 @@ export function createModel (builder: Builder): void {
config: listConfig
})
builder.mixin(type, card.class.MasterTag, setting.mixin.Editable, {
value: false
})
builder.createDoc(view.class.Viewlet, core.space.Model, {
attachTo: type,
descriptor: view.viewlet.Table,
configOptions: {
hiddenKeys: ['content', 'title']
},
config: [
'',
'_class',
{ key: '', presenter: view.component.RolePresenter, label: card.string.Tags, props: { fullSize: true } },
'modifiedOn'
]
})
}
export function createModel (builder: Builder): void {
builder.createModel(TMasterTag, TTag, TCard, MasterTagEditorSection)
createSystemType(builder, card.types.File, attachment.string.File, card.icon.File)
createSystemType(builder, card.types.Document, card.string.Document, card.icon.Document)
builder.createDoc(
workbench.class.Application,
core.space.Model,
@ -210,6 +220,10 @@ export function createModel (builder: Builder): void {
card.action.SetParent
)
builder.mixin(card.class.Card, core.class.Class, view.mixin.AttributeEditor, {
inlineEditor: card.component.CardEditor
})
createAction(
builder,
{
@ -304,6 +318,10 @@ export function createModel (builder: Builder): void {
presenter: card.component.CardPresenter
})
builder.mixin(card.class.Card, core.class.Class, view.mixin.AttributePresenter, {
presenter: card.component.CardRefPresenter
})
builder.mixin(card.class.Card, core.class.Class, activity.mixin.ActivityDoc, {})
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {

View File

@ -103,6 +103,7 @@
{justify}
type={attribute?.type}
{maxWidth}
{attribute}
{attributeKey}
value={getAttribute(client, object, { key: attributeKey, attr: attribute })}
space={object.space}
@ -123,6 +124,7 @@
disabled={isReadonly}
space={object.space}
{onChange}
{attribute}
{focus}
{object}
{size}

View File

@ -76,6 +76,7 @@
{focus}
{focusIndex}
{editKind}
{attribute}
/>
{/await}
{/if}

View File

@ -43,4 +43,7 @@
<symbol id="view" viewBox="0 0 32 32">
<path d="M25.8,2H6.2C3.9,2,2,3.9,2,6.2v19.5C2,28.1,3.9,30,6.2,30h19.5c2.3,0,4.2-1.9,4.2-4.2V6.2C30,3.9,28.1,2,25.8,2z M28,6.2v5.5H13.8V4h12C27,4,28,5,28,6.2z M6.2,4h5.5v7.8H4V6.2C4,5,5,4,6.2,4z M4,25.8v-12h7.8V28H6.2C5,28,4,27,4,25.8z M25.8,28h-12V13.8H28v12C28,27,27,28,25.8,28z" />
</symbol>
<symbol id="document" viewBox="0 0 32 32">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 4C7.89543 4 7 4.89543 7 6V26C7 27.1046 7.89543 28 9 28H23C24.1046 28 25 27.1046 25 26V12H21C18.7909 12 17 10.2091 17 8V4H9ZM19 4.41421V8C19 9.10457 19.8954 10 21 10H24.5858L19 4.41421ZM5 6C5 3.79086 6.79086 2 9 2H18.5858C19.1162 2 19.6249 2.21071 20 2.58579L26.4142 9C26.7893 9.37507 27 9.88378 27 10.4142V26C27 28.2091 25.2091 30 23 30H9C6.79086 30 5 28.2091 5 26V6ZM10 17C10 16.4477 10.4477 16 11 16H21C21.5523 16 22 16.4477 22 17C22 17.5523 21.5523 18 21 18H11C10.4477 18 10 17.5523 10 17ZM10 23C10 22.4477 10.4477 22 11 22H21C21.5523 22 22 22.4477 22 23C22 23.5523 21.5523 24 21 24H11C10.4477 24 10 23.5523 10 23Z" />
</symbol>
</svg>

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -27,6 +27,7 @@
"Views": "Zobrazení",
"CreateView": "Vytvořit zobrazení",
"EditView": "Upravit zobrazení",
"SelectViewType": "Vybrat typ zobrazení"
"SelectViewType": "Vybrat typ zobrazení",
"Document": "Dokument"
}
}

View File

@ -27,6 +27,7 @@
"Views": "Ansichten",
"CreateView": "Ansicht erstellen",
"EditView": "Ansicht bearbeiten",
"SelectViewType": "Ansichtstyp auswählen"
"SelectViewType": "Ansichtstyp auswählen",
"Document": "Dokument"
}
}

View File

@ -27,6 +27,7 @@
"Views": "Views",
"CreateView": "Create View",
"EditView": "Edit View",
"SelectViewType": "Select View Type"
"SelectViewType": "Select View Type",
"Document": "Document"
}
}

View File

@ -27,6 +27,7 @@
"Views": "Vistas",
"CreateView": "Crear vista",
"EditView": "Editar vista",
"SelectViewType": "Seleccionar tipo de vista"
"SelectViewType": "Seleccionar tipo de vista",
"Document": "Documento"
}
}

View File

@ -27,6 +27,7 @@
"Views": "Vues",
"CreateView": "Créer une vue",
"EditView": "Modifier la vue",
"SelectViewType": "Sélectionner le type de vue"
"SelectViewType": "Sélectionner le type de vue",
"Document": "Document"
}
}

View File

@ -27,6 +27,7 @@
"Views": "Viste",
"CreateView": "Crea vista",
"EditView": "Modifica vista",
"SelectViewType": "Seleziona il tipo di vista"
"SelectViewType": "Seleziona il tipo di vista",
"Document": "Documento"
}
}

View File

@ -27,6 +27,7 @@
"Views": "Visualizações",
"CreateView": "Criar visualização",
"EditView": "Editar visualização",
"SelectViewType": "Selecionar tipo de visualização"
"SelectViewType": "Selecionar tipo de visualização",
"Document": "Documento"
}
}

View File

@ -27,6 +27,7 @@
"Views": "Представления",
"CreateView": "Создать представление",
"EditView": "Редактировать представление",
"SelectViewType": "Выбрать тип представления"
"SelectViewType": "Выбрать тип представления",
"Document": "Документ"
}
}

View File

@ -27,6 +27,7 @@
"Views": "视图",
"CreateView": "创建视图",
"EditView": "编辑视图",
"SelectViewType": "选择视图类型"
"SelectViewType": "选择视图类型",
"Document": "文档"
}
}

View File

@ -22,5 +22,6 @@ loadMetadata(card.icon, {
Tag: `${icons}#tag`,
Card: `${icons}#card`,
File: `${icons}#file`,
View: `${icons}#view`
View: `${icons}#view`,
Document: `${icons}#document`
})

View File

@ -0,0 +1,105 @@
<!--
// Copyright © 2025 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 { Card, MasterTag } from '@hcengineering/card'
import { AnyAttribute, Ref, RefTo } from '@hcengineering/core'
import { IntlString } from '@hcengineering/platform'
import { createQuery, getClient } from '@hcengineering/presentation'
import {
Button,
ButtonKind,
ButtonSize,
eventToHTMLElement,
IconWithEmoji,
Label,
showPopup
} from '@hcengineering/ui'
import view from '@hcengineering/view'
import { createEventDispatcher } from 'svelte'
import card from '../plugin'
import CardsPopup from './CardsPopup.svelte'
export let value: Ref<Card>
export let readonly: boolean = false
export let label: IntlString = card.string.Card
export let onChange: (value: any) => void
export let attribute: AnyAttribute
export let focusIndex: number | undefined = undefined
export let shouldShowLabel: boolean = true
export let kind: ButtonKind = 'no-border'
export let size: ButtonSize = 'small'
export let justify: 'left' | 'center' = 'left'
export let width: string | undefined = 'min-content'
const dispatch = createEventDispatcher()
const client = getClient()
const hierarchy = client.getHierarchy()
const handleOpen = (event: MouseEvent): void => {
event.stopPropagation()
const _class = (attribute.type as RefTo<Card>).to
if (readonly) {
return
}
showPopup(CardsPopup, { selected: value, _class }, eventToHTMLElement(event), change)
}
const change = (val: Card | undefined): void => {
if (readonly || val == null || value === val._id) {
return
}
value = val._id
dispatch('change', value)
onChange(value)
}
let doc: Card | undefined
const query = createQuery()
$: query.query(card.class.Card, { _id: value }, (res) => {
doc = res[0]
})
$: _classRef = doc?._class ?? (attribute?.type as RefTo<Card>)?.to
$: _class = _classRef !== undefined ? (hierarchy.findClass(_classRef) as MasterTag) : undefined
$: icon = _class?.icon === view.ids.IconWithEmoji ? IconWithEmoji : _class?.icon
$: iconProps = _class?.icon === view.ids.IconWithEmoji ? { icon: _class?.color } : {}
</script>
<Button
showTooltip={!readonly ? { label } : undefined}
{justify}
{focusIndex}
{width}
{size}
{icon}
{iconProps}
{kind}
disabled={readonly}
on:click={handleOpen}
>
<div slot="content" class="overflow-label">
{#if doc}
{doc.title}
{:else}
<Label {label} />
{/if}
</div>
</Button>

View File

@ -0,0 +1,43 @@
<!--
// Copyright © 2025 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 { Card } from '@hcengineering/card'
import { Ref } from '@hcengineering/core'
import { Asset } from '@hcengineering/platform'
import { createQuery } from '@hcengineering/presentation'
import { AnySvelteComponent } from '@hcengineering/ui'
import { ObjectPresenterType } from '@hcengineering/view'
import card from '../plugin'
import CardPresenter from './CardPresenter.svelte'
export let value: Ref<Card> | undefined
export let kind: 'list' | undefined = undefined
export let type: ObjectPresenterType = 'link'
export let icon: Asset | AnySvelteComponent | undefined = undefined
let doc: Card | undefined
const query = createQuery()
$: value &&
query.query(
card.class.Card,
{ _id: value },
(res) => {
;[doc] = res
},
{ limit: 1 }
)
</script>
<CardPresenter value={doc} {kind} {type} {icon} />

View File

@ -0,0 +1,64 @@
<!--
// Copyright © 2025 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 { getClient, ObjectPopup } from '@hcengineering/presentation'
import { Card } from '@hcengineering/card'
import { Class, Ref } from '@hcengineering/core'
import { IntlString } from '@hcengineering/platform'
import { Label } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'
export let _class: Ref<Class<Card>>
export let selected: Ref<Card> | undefined
export let multiSelect: boolean = false
export let allowDeselect: boolean = true
export let titleDeselect: IntlString | undefined = undefined
export let readonly = false
const dispatch = createEventDispatcher()
const client = getClient()
const hierarchy = client.getHierarchy()
</script>
<ObjectPopup
{_class}
{selected}
{multiSelect}
{allowDeselect}
{titleDeselect}
type={'object'}
groupBy={'_class'}
on:update
on:close
on:changeContent
on:created={(doc) => dispatch('close', doc.detail)}
{readonly}
>
<svelte:fragment slot="item" let:item={it}>
<div class="flex-row-center flex-grow">
{it.title}
</div>
</svelte:fragment>
<svelte:fragment slot="category" let:item={it}>
{@const cl = hierarchy.getClass(it._class)}
<div class="menu-group__header">
<span class="overflow-label">
<Label label={cl.label} />
</span>
</div>
</svelte:fragment>
</ObjectPopup>

View File

@ -39,10 +39,10 @@
icon: isMasterTag ? card.icon.MasterTag : card.icon.Tag
}
await client.createDoc(_class, core.space.Model, data)
const id = await client.createDoc(_class, core.space.Model, data)
Analytics.handleEvent(isMasterTag ? CardEvents.TypeCreated : CardEvents.TagCreated)
dispatch('close')
dispatch('close', id)
}
</script>

View File

@ -70,7 +70,7 @@
size={'medium'}
showTooltip={{ label: setting.string.AddAttribute }}
on:click={(ev) => {
showPopup(setting.component.CreateAttributePopup, { _class: value._class }, 'top')
showPopup(setting.component.CreateAttributePopup, { _class: value._class, isCard: true }, 'top')
}}
/>
<Button

View File

@ -69,7 +69,7 @@
size={'medium'}
showTooltip={{ label: setting.string.AddAttribute }}
on:click={(ev) => {
showPopup(setting.component.CreateAttributePopup, { _class: tag._id }, 'top')
showPopup(setting.component.CreateAttributePopup, { _class: tag._id, isCard: true }, 'top')
}}
/>
<Button

View File

@ -16,7 +16,6 @@
import { MasterTag } from '@hcengineering/card'
import { Class, Doc, Ref } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import { ClassHierarchy } from '@hcengineering/setting-resources'
import { ButtonIcon, getCurrentLocation, Icon, IconAdd, Label, navigate, showPopup } from '@hcengineering/ui'
import card from '../../plugin'
import CreateTag from '../CreateTag.svelte'

View File

@ -13,15 +13,30 @@
// limitations under the License.
-->
<script lang="ts">
import { ButtonIcon, IconAdd, showPopup } from '@hcengineering/ui'
import { ButtonIcon, getCurrentResolvedLocation, IconAdd, navigate, showPopup } from '@hcengineering/ui'
import CreateTag from '../CreateTag.svelte'
import card from '../../plugin'
import { clearSettingsStore } from '@hcengineering/setting-resources'
function handleAdd (): void {
showPopup(CreateTag, {
parent: undefined,
_class: card.class.MasterTag
})
showPopup(
CreateTag,
{
parent: undefined,
_class: card.class.MasterTag
},
undefined,
(res) => {
if (res != null) {
clearSettingsStore()
const loc = getCurrentResolvedLocation()
loc.path[3] = 'types'
loc.path[4] = res
loc.path.length = 5
navigate(loc)
}
}
)
}
</script>

View File

@ -20,4 +20,11 @@
export let masterTag: MasterTag
</script>
<ClassAttributes ofClass={card.class.Card} _class={masterTag._id} showHierarchy showHeader={false} disabled={false} />
<ClassAttributes
ofClass={card.class.Card}
_class={masterTag._id}
showHierarchy
showHeader={false}
disabled={false}
isCard
/>

View File

@ -13,10 +13,11 @@
// limitations under the License.
-->
<script lang="ts">
import cardPlugin from '@hcengineering/card'
import cardPlugin, { MasterTag } from '@hcengineering/card'
import core, { Class, ClassifierKind, Doc, Ref } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import { Icon, Label } from '@hcengineering/ui'
import { Icon, IconWithEmoji, Label } from '@hcengineering/ui'
import view from '@hcengineering/view'
import { createEventDispatcher } from 'svelte'
export let classes: Ref<Class<Doc>>[] = []
@ -54,10 +55,14 @@
})
$: fillDescendants(classes)
function getMasterTag (cl: Ref<Class<Doc>>): MasterTag {
return client.getHierarchy().getClass(cl) as MasterTag
}
</script>
{#each classes as cl}
{@const clazz = client.getHierarchy().getClass(cl)}
{@const clazz = getMasterTag(cl)}
<button
class="hulyTableAttr-content__row justify-start cursor-pointer"
on:click={() => {
@ -68,7 +73,11 @@
class="hulyTableAttr-content__row-label font-medium-14 flex flex-gap-2"
style:margin-left={`${level * 1.25}rem`}
>
<Icon icon={clazz.icon ?? cardPlugin.icon.Tag} size="small" />
<Icon
icon={clazz.icon === view.ids.IconWithEmoji ? IconWithEmoji : clazz.icon ?? cardPlugin.icon.Tag}
iconProps={clazz.icon === view.ids.IconWithEmoji ? { icon: clazz.color, size: 'small' } : {}}
size="small"
/>
<Label label={clazz.label} />
</div>
</button>

View File

@ -38,6 +38,8 @@ import SetParentActionPopup from './components/SetParentActionPopup.svelte'
import RelationSetting from './components/settings/RelationSetting.svelte'
import ViewsSection from './components/settings/view/ViewsSection.svelte'
import EditView from './components/settings/view/EditView.svelte'
import CardEditor from './components/CardEditor.svelte'
import CardRefPresenter from './components/CardRefPresenter.svelte'
export default async (): Promise<Resources> => ({
component: {
@ -57,7 +59,9 @@ export default async (): Promise<Resources> => ({
SetParentActionPopup,
RelationSetting,
ViewsSection,
EditView
EditView,
CardEditor,
CardRefPresenter
},
completion: {
CardQuery: queryCard

View File

@ -38,7 +38,9 @@ export default mergeIds(cardId, card, {
SetParentActionPopup: '' as AnyComponent,
RelationSetting: '' as AnyComponent,
ViewsSection: '' as AnyComponent,
EditView: '' as AnyComponent
EditView: '' as AnyComponent,
CardEditor: '' as AnyComponent,
CardRefPresenter: '' as AnyComponent
},
completion: {
CardQuery: '' as Resource<ObjectSearchFactory>,
@ -71,6 +73,7 @@ export default mergeIds(cardId, card, {
Children: '' as IntlString,
CreateView: '' as IntlString,
EditView: '' as IntlString,
SelectViewType: '' as IntlString
SelectViewType: '' as IntlString,
Document: '' as IntlString
}
})

View File

@ -67,7 +67,8 @@ const cardPlugin = plugin(cardId, {
MasterTagEditorSection: '' as Ref<Class<MasterTagEditorSection>>
},
types: {
File: '' as Ref<MasterTag>
File: '' as Ref<MasterTag>,
Document: '' as Ref<MasterTag>
},
icon: {
MasterTags: '' as Asset,
@ -76,7 +77,8 @@ const cardPlugin = plugin(cardId, {
Tags: '' as Asset,
Card: '' as Asset,
File: '' as Asset,
View: '' as Asset
View: '' as Asset,
Document: '' as Asset
},
extensions: {
EditCardExtension: '' as ComponentExtensionId

View File

@ -46,6 +46,7 @@
export let showTitle: boolean = !showHierarchy
export let showHeader: boolean = true
export let disabled: boolean = true
export let isCard: boolean = false
export let attributeMapper:
| {
component: AnySvelteComponent
@ -90,7 +91,9 @@
}
showPopup(TypesPopup, { _class }, getEventPositionElement(ev), (_id) => {
if (_id !== undefined) $settingsStore = { component: CreateAttribute, props: { selectedType: _id, _class } }
if (_id !== undefined) {
$settingsStore = { component: CreateAttribute, props: { selectedType: _id, _class, isCard } }
}
})
}

View File

@ -46,6 +46,7 @@
export let _id: Ref<Class<Type<PropertyType>>> | undefined = undefined
export let _class: Ref<Class<Doc>>
export let isCard: boolean = false
let name: string
let icon: Asset | undefined
@ -93,19 +94,20 @@
const items = getTypes()
export let selectedType: Ref<Class<Type<PropertyType>>> | undefined = undefined
$: selectedType && selectType(selectedType)
$: selectType(selectedType)
function selectType (type: Ref<Class<Type<PropertyType>>>): void {
function selectType (type: Ref<Class<Type<PropertyType>>> | undefined): void {
if (type === undefined) return
const _class = hierarchy.getClass(type)
const editor = hierarchy.as(_class, view.mixin.ObjectEditor)
if (editor.editor !== undefined) {
is = editor.editor
}
}
const handleSelection = (e: { detail: Ref<Class<Type<any>>> }) => {
const handleSelection = (e: { detail: Ref<Class<Type<any>>> }): void => {
selectType(e.detail)
}
const handleChange = (e: any) => {
const handleChange = (e: any): void => {
type = e.detail?.type
index = e.detail?.index
defaultValue = e.detail?.defaultValue
@ -169,6 +171,7 @@
props={{
type,
defaultValue,
isCard,
kind: 'regular',
size: 'large'
}}

View File

@ -42,6 +42,8 @@
import { IconPicker } from '@hcengineering/view-resources'
export let _class: Ref<Class<Doc>>
export let isCard: boolean = false
let selectedType: Ref<Class<Type<PropertyType>>> | undefined = undefined
let name: string
@ -159,6 +161,7 @@
props={{
type,
defaultValue,
isCard,
kind: 'regular',
size: 'large'
}}

View File

@ -26,6 +26,7 @@
export let editable: boolean = true
export let kind: ButtonKind = 'regular'
export let size: ButtonSize = 'medium'
export let isCard: boolean = false
const dispatch = createEventDispatcher()
const client = getClient()
@ -49,7 +50,7 @@
$: selected = types.find((p) => p._id === refClass)
const handleChange = (e: any) => {
const handleChange = (e: any): void => {
const type = e.detail?.type
const res = { type: createArrOf(type) }
dispatch('change', res)
@ -86,6 +87,7 @@
props={{
type: type?.of,
nested: true,
isCard,
editable,
kind,
size

View File

@ -18,37 +18,58 @@
import { getClient } from '@hcengineering/presentation'
import { DropdownLabelsIntl, Label } from '@hcengineering/ui'
import view from '@hcengineering/view-resources/src/plugin'
import card from '@hcengineering/card'
import { createEventDispatcher } from 'svelte'
import type { ButtonKind, ButtonSize } from '@hcengineering/ui'
import type { ButtonKind, ButtonSize, DropdownIntlItem } from '@hcengineering/ui'
import contactPlugin from '@hcengineering/contact'
export let type: RefTo<Doc> | undefined
export let editable: boolean = true
export let kind: ButtonKind = 'regular'
export let size: ButtonSize = 'medium'
export let isCard: boolean = false
const _classes = isCard ? [card.class.Card, contactPlugin.class.Contact] : [core.class.Doc]
const exclude = !isCard ? [card.class.Card] : []
const dispatch = createEventDispatcher()
const client = getClient()
const hierarchy = client.getHierarchy()
const descendants = hierarchy.getDescendants(core.class.Doc)
const classes = descendants
.map((p) => hierarchy.getClass(p))
.filter((p) => {
return (
hierarchy.hasMixin(p, view.mixin.AttributeEditor) &&
p.label !== undefined &&
hierarchy.getDomain(p._id) !== DOMAIN_STATUS
)
})
.map((p) => {
return { id: p._id, label: p.label }
})
const classes = fillClasses(_classes, exclude)
function fillClasses (classes: Ref<Class<Doc>>[], exclude: Ref<Class<Doc>>[]): DropdownIntlItem[] {
const res: DropdownIntlItem[] = []
const descendants = new Set(
classes
.map((p) => hierarchy.getDescendants(p))
.reduce((a, b) => a.concat(b))
.filter((p) => p !== card.class.Card)
)
const excluded = new Set()
for (const _class of exclude) {
const desc = hierarchy.getDescendants(_class)
for (const _id of desc) {
excluded.add(_id)
}
}
for (const desc of descendants) {
if (excluded.has(desc)) continue
const domain = hierarchy.findDomain(desc)
if (domain === DOMAIN_STATUS || domain === undefined) continue
if (hierarchy.classHierarchyMixin(desc, view.mixin.AttributeEditor) === undefined) continue
const _class = hierarchy.getClass(desc)
if (_class.label === undefined) continue
res.push({ id: _class._id, label: _class.label })
}
return res
}
let refClass: Ref<Class<Doc>> | undefined = type?.to
$: selected = classes.find((p) => p.id === refClass)
$: refClass && dispatch('change', { type: TypeRef(refClass) })
$: refClass !== undefined && dispatch('change', { type: TypeRef(refClass) })
</script>
<div class="hulyModal-content__settingsSet-line">