mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-31 12:19:47 +02:00
193 lines
5.7 KiB
Svelte
193 lines
5.7 KiB
Svelte
<script lang="ts">
|
|
import core, { Association, Class, Data, Doc, Ref } from '@hcengineering/core'
|
|
import { IntlString, translate } from '@hcengineering/platform'
|
|
import { createQuery, getClient, MessageBox } from '@hcengineering/presentation'
|
|
import {
|
|
Breadcrumb,
|
|
Button,
|
|
defineSeparators,
|
|
Header,
|
|
IconDelete,
|
|
Separator,
|
|
showPopup,
|
|
twoPanelsSeparators
|
|
} from '@hcengineering/ui'
|
|
import view from '@hcengineering/view-resources/src/plugin'
|
|
import settings from '../plugin'
|
|
import card from '@hcengineering/card'
|
|
import AssociationEditor from './AssociationEditor.svelte'
|
|
|
|
export let _classes: Ref<Class<Doc>>[] = [core.class.Doc]
|
|
export let exclude: Ref<Class<Doc>>[] = [card.class.Card]
|
|
|
|
const query = createQuery()
|
|
const client = getClient()
|
|
const hierarchy = client.getHierarchy()
|
|
|
|
let selected: Association | Data<Association> | undefined
|
|
|
|
let associations: Association[] = []
|
|
|
|
query.query(core.class.Association, {}, (res) => {
|
|
associations = res
|
|
})
|
|
|
|
$: filtered = filterAssociations(associations, _classes, exclude)
|
|
|
|
function filterAssociations (
|
|
associations: Association[],
|
|
_classes: Ref<Class<Doc>>[],
|
|
exclude: Ref<Class<Doc>>[]
|
|
): Association[] {
|
|
_classes = _classes ?? [core.class.Doc]
|
|
exclude = exclude ?? [card.class.Card]
|
|
const res: Association[] = []
|
|
const descendants = new Set(_classes.map((p) => hierarchy.getDescendants(p)).reduce((a, b) => a.concat(b)))
|
|
const excluded = new Set()
|
|
for (const _class of exclude) {
|
|
const desc = hierarchy.getDescendants(_class)
|
|
for (const _id of desc) {
|
|
excluded.add(_id)
|
|
}
|
|
}
|
|
for (const association of associations) {
|
|
if (
|
|
descendants.has(association.classA) &&
|
|
descendants.has(association.classB) &&
|
|
!excluded.has(association.classA) &&
|
|
!excluded.has(association.classB)
|
|
) {
|
|
res.push(association)
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
|
|
function createRelation (): void {
|
|
selected = {
|
|
classA: '' as Ref<Class<Doc>>,
|
|
classB: '' as Ref<Class<Doc>>,
|
|
nameA: '',
|
|
nameB: '',
|
|
type: '1:1'
|
|
}
|
|
}
|
|
|
|
function isAssociation (data: Data<Association> | Association | undefined): data is Association {
|
|
return (data as Association)?._id !== undefined
|
|
}
|
|
|
|
function getClassLabel (_class: Ref<Class<Doc>>): IntlString | undefined {
|
|
const _classLabel = client.getModel().findObject(_class)
|
|
return _classLabel?.label
|
|
}
|
|
|
|
async function getLabel (association: Association): Promise<string> {
|
|
const aLabel = getClassLabel(association.classA)
|
|
const bLabel = getClassLabel(association.classB)
|
|
const aClass = aLabel !== undefined ? await translate(aLabel, {}) : undefined
|
|
const bClass = bLabel !== undefined ? await translate(bLabel, {}) : undefined
|
|
return `${association.nameA} ${aClass !== undefined ? '(' + aClass + ')' : ''} - ${association.nameB} ${bClass !== undefined ? '(' + bClass + ')' : ''}`
|
|
}
|
|
|
|
defineSeparators('workspaceSettings', twoPanelsSeparators)
|
|
|
|
async function remove (val: Association | Data<Association> | undefined): Promise<void> {
|
|
if (isAssociation(val)) {
|
|
showPopup(MessageBox, {
|
|
label: view.string.DeleteObject,
|
|
message: view.string.DeleteObjectConfirm,
|
|
params: { count: 1 },
|
|
dangerous: true,
|
|
action: async () => {
|
|
selected = undefined
|
|
await client.remove(val)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<div class="hulyComponent">
|
|
<Header adaptive={'disabled'}>
|
|
<Breadcrumb icon={settings.icon.Relations} label={core.string.Relations} size={'large'} isCurrent />
|
|
|
|
<svelte:fragment slot="actions">
|
|
{#if isAssociation(selected)}
|
|
<Button icon={IconDelete} label={view.string.Delete} kind={'dangerous'} on:click={() => remove(selected)} />
|
|
{/if}
|
|
</svelte:fragment>
|
|
</Header>
|
|
|
|
<div class="hulyComponent-content__container columns">
|
|
<div class="hulyComponent-content__column">
|
|
<div class="ulyComponent-content__navHeader flex-between trans-title flex-no-shrink bottom-divider p-3">
|
|
<Button
|
|
icon={view.icon.Add}
|
|
label={core.string.AddRelation}
|
|
justify={'left'}
|
|
width={'100%'}
|
|
on:click={createRelation}
|
|
/>
|
|
</div>
|
|
|
|
<div class="flex-col overflow-y-auto">
|
|
{#each filtered as association (association._id)}
|
|
<button
|
|
class="list-item"
|
|
class:selected={selected === association}
|
|
on:click={() => {
|
|
selected = association
|
|
}}
|
|
>
|
|
{#await getLabel(association) then label}
|
|
<span class="font-regular-14 overflow-label">{label}</span>
|
|
{/await}
|
|
</button>
|
|
{/each}
|
|
</div>
|
|
</div>
|
|
<Separator name={'workspaceSettings'} index={0} color={'var(--theme-divider-color)'} />
|
|
{#if selected !== undefined}
|
|
<AssociationEditor
|
|
{exclude}
|
|
{_classes}
|
|
association={selected}
|
|
on:create
|
|
on:close={(res) => {
|
|
selected = undefined
|
|
}}
|
|
/>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
|
|
<style lang="scss">
|
|
.list-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin: 0 var(--spacing-1_5);
|
|
padding: var(--spacing-1) var(--spacing-1_25);
|
|
text-align: left;
|
|
border: none;
|
|
border-radius: var(--small-BorderRadius);
|
|
outline: none;
|
|
|
|
& :global(button.type-button-icon) {
|
|
visibility: hidden;
|
|
}
|
|
&:hover {
|
|
background-color: var(--theme-button-hovered);
|
|
|
|
& :global(button.type-button-icon) {
|
|
visibility: visible;
|
|
}
|
|
}
|
|
&.selected {
|
|
background-color: var(--theme-button-default);
|
|
cursor: default;
|
|
}
|
|
}
|
|
</style>
|