mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-13 19:58:09 +00:00
parent
1abcee26bf
commit
9018fc143e
@ -20,6 +20,7 @@
|
||||
// import Icon from './Icon.svelte'
|
||||
import Loading from './Loading.svelte'
|
||||
import ErrorBoundary from './internal/ErrorBoundary'
|
||||
import ErrorPresenter from './ErrorPresenter.svelte';
|
||||
|
||||
export let is: AnyComponent
|
||||
export let props = {}
|
||||
@ -36,8 +37,8 @@
|
||||
</Ctor>
|
||||
</ErrorBoundary>
|
||||
{:catch err}
|
||||
ERROR: {console.log(err, JSON.stringify(component))}
|
||||
{props}
|
||||
{err}
|
||||
<pre style='max-height: 140px; overflow: auto;'>
|
||||
<ErrorPresenter error={err}/>
|
||||
</pre>
|
||||
<!-- <Icon icon={ui.icon.Error} size="32" /> -->
|
||||
{/await}
|
||||
|
28
packages/ui/src/components/ErrorPopup.svelte
Normal file
28
packages/ui/src/components/ErrorPopup.svelte
Normal file
@ -0,0 +1,28 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 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">
|
||||
|
||||
export let error: any
|
||||
|
||||
</script>
|
||||
|
||||
{error.message}
|
||||
<pre>
|
||||
{#if error.status.params}
|
||||
{JSON.stringify(error.status.params, undefined, 2)}
|
||||
{/if}
|
||||
</pre>
|
32
packages/ui/src/components/ErrorPresenter.svelte
Normal file
32
packages/ui/src/components/ErrorPresenter.svelte
Normal file
@ -0,0 +1,32 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 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 Tooltip from './Tooltip.svelte'
|
||||
import ErrorPopup from './ErrorPopup.svelte'
|
||||
export let error: any
|
||||
</script>
|
||||
|
||||
<Tooltip component={ErrorPopup} props={{ error: error }}>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="10" cy="10" r="7.275" stroke="#EE7A7A" stroke-width="1.2" />
|
||||
<path
|
||||
d="M9.51371 11.6902L9.51636 11.7875H9.61367H10.4137H10.511L10.5136 11.6902L10.6886 5.27772L10.6914 5.17499H10.5887H9.43867H9.33591L9.33871 5.27772L9.51371 11.6902ZM10.0012 14.375C10.4929 14.375 10.9012 13.9812 10.9012 13.475C10.9012 12.9687 10.4929 12.575 10.0012 12.575C9.50947 12.575 9.10117 12.9687 9.10117 13.475C9.10117 13.9812 9.50947 14.375 10.0012 14.375Z"
|
||||
fill="#EE7A7A"
|
||||
stroke="#EE7A7A"
|
||||
stroke-width="0.2"
|
||||
/>
|
||||
</svg>
|
||||
</Tooltip>
|
@ -15,9 +15,9 @@
|
||||
|
||||
import { SvelteComponent } from 'svelte'
|
||||
import type { AnySvelteComponent, AnyComponent, PopupAlignment, LabelAndProps, TooltipAligment } from './types'
|
||||
import { getResource, IntlString } from '@anticrm/platform'
|
||||
import { addStringsLoader } from '@anticrm/platform'
|
||||
import { uiId } from './plugin'
|
||||
import { getResource, IntlString, addStringsLoader } from '@anticrm/platform'
|
||||
import { uiId } from './plugin'
|
||||
import { writable, readable } from 'svelte/store'
|
||||
|
||||
import Root from './components/internal/Root.svelte'
|
||||
|
||||
@ -79,11 +79,10 @@ export { default as IconDelete } from './components/icons/Delete.svelte'
|
||||
export { default as IconEdit } from './components/icons/Edit.svelte'
|
||||
export { default as IconInfo } from './components/icons/Info.svelte'
|
||||
export { default as Menu } from './components/Menu.svelte'
|
||||
export { default as ErrorPresenter } from './components/ErrorPresenter.svelte'
|
||||
|
||||
export * from './utils'
|
||||
|
||||
import { writable, readable } from 'svelte/store'
|
||||
|
||||
export function createApp (target: HTMLElement): SvelteComponent {
|
||||
return new Root({ target })
|
||||
}
|
||||
|
@ -15,16 +15,14 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import type { Ref, Class, Doc, Space, FindOptions, DocumentQuery } from '@anticrm/core'
|
||||
import type { Class, Doc, DocumentQuery, FindOptions, Ref } from '@anticrm/core'
|
||||
import { SortingOrder } from '@anticrm/core'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { IconDown, IconUp, Label, Loading, showPopup } from '@anticrm/ui'
|
||||
import { buildModel } from '../utils'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Label, showPopup, Loading, CheckBox, IconDown, IconUp } from '@anticrm/ui'
|
||||
import MoreV from './icons/MoreV.svelte'
|
||||
import Menu from './Menu.svelte'
|
||||
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let query: DocumentQuery<Doc>
|
||||
export let options: FindOptions<Doc> | undefined
|
||||
@ -39,13 +37,14 @@
|
||||
const q = createQuery()
|
||||
$: q.query(_class, query, result => { objects = result }, { sort: { [sortKey]: sortOrder }, ...options })
|
||||
|
||||
function getValue(doc: Doc, key: string): any {
|
||||
if (key.length === 0)
|
||||
function getValue (doc: Doc, key: string): any {
|
||||
if (key.length === 0) {
|
||||
return doc
|
||||
}
|
||||
const path = key.split('.')
|
||||
const len = path.length
|
||||
let obj = doc as any
|
||||
for (let i=0; i<len; i++){
|
||||
for (let i = 0; i < len; i++) {
|
||||
obj = obj?.[path[i]]
|
||||
}
|
||||
return obj ?? ''
|
||||
@ -55,12 +54,13 @@
|
||||
|
||||
const showMenu = (ev: MouseEvent, object: Doc, row: number): void => {
|
||||
selectRow = row
|
||||
showPopup(Menu, { object }, ev.target as HTMLElement, (() => { selectRow = undefined }))
|
||||
showPopup(Menu, { object }, ev.target as HTMLElement, () => { selectRow = undefined })
|
||||
}
|
||||
|
||||
function changeSorting(key: string) {
|
||||
if (key === '')
|
||||
function changeSorting (key: string): void {
|
||||
if (key === '') {
|
||||
return
|
||||
}
|
||||
if (key !== sortKey) {
|
||||
sortKey = key
|
||||
sortOrder = SortingOrder.Ascending
|
||||
@ -69,8 +69,7 @@
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#await buildModel({client, _class, keys: config, options})}
|
||||
{#await buildModel({ client, _class, keys: config, options })}
|
||||
<Loading/>
|
||||
{:then model}
|
||||
<table class="table-body">
|
||||
@ -100,12 +99,12 @@
|
||||
<tr class="tr-body" class:fixed={row === selectRow}>
|
||||
{#each model as attribute, cell}
|
||||
{#if !cell}
|
||||
<td><div class="firstCell">
|
||||
<svelte:component this={attribute.presenter} value={getValue(object, attribute.key)}/>
|
||||
<td><div class="firstCell">
|
||||
<svelte:component this={attribute.presenter} value={getValue(object, attribute.key)} {...attribute.props}/>
|
||||
<div class="menuRow" on:click={(ev) => showMenu(ev, object, row)}><MoreV size={'small'} /></div>
|
||||
</div></td>
|
||||
{:else}
|
||||
<td><svelte:component this={attribute.presenter} value={getValue(object, attribute.key)}/></td>
|
||||
<td><svelte:component this={attribute.presenter} value={getValue(object, attribute.key)} {...attribute.props}/></td>
|
||||
{/if}
|
||||
{/each}
|
||||
</tr>
|
||||
|
@ -13,19 +13,15 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import type { Ref, Class, Doc, Space, FindOptions } from '@anticrm/core'
|
||||
import type { Class, Doc, FindOptions, Ref, Space } from '@anticrm/core'
|
||||
import { SortingOrder } from '@anticrm/core'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { CheckBox, IconDown, IconUp, Label, Loading, ScrollBox, showPopup } from '@anticrm/ui'
|
||||
import { buildModel } from '../utils'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Label, showPopup, Loading, ScrollBox, CheckBox, IconDown, IconUp } from '@anticrm/ui'
|
||||
import MoreV from './icons/MoreV.svelte'
|
||||
import Menu from './Menu.svelte'
|
||||
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let space: Ref<Space>
|
||||
export let options: FindOptions<Doc> | undefined
|
||||
@ -39,15 +35,23 @@
|
||||
let objects: Doc[]
|
||||
|
||||
const query = createQuery()
|
||||
$: query.query(_class, search === '' ? { space } : { $search: search }, result => { objects = result }, { sort: { [sortKey]: sortOrder }, ...options })
|
||||
$: query.query(
|
||||
_class,
|
||||
search === '' ? { space } : { $search: search },
|
||||
(result) => {
|
||||
objects = result
|
||||
},
|
||||
{ sort: { [sortKey]: sortOrder }, ...options }
|
||||
)
|
||||
|
||||
function getValue(doc: Doc, key: string): any {
|
||||
if (key.length === 0)
|
||||
function getValue (doc: Doc, key: string): any {
|
||||
if (key.length === 0) {
|
||||
return doc
|
||||
}
|
||||
const path = key.split('.')
|
||||
const len = path.length
|
||||
let obj = doc as any
|
||||
for (let i=0; i<len; i++){
|
||||
for (let i = 0; i < len; i++) {
|
||||
obj = obj?.[path[i]]
|
||||
}
|
||||
return obj ?? ''
|
||||
@ -58,76 +62,96 @@
|
||||
|
||||
const showMenu = (ev: MouseEvent, object: Doc, row: number): void => {
|
||||
selectRow = row
|
||||
showPopup(Menu, { object }, ev.target as HTMLElement, (() => { selectRow = undefined }))
|
||||
showPopup(Menu, { object }, ev.target as HTMLElement, () => {
|
||||
selectRow = undefined
|
||||
})
|
||||
}
|
||||
|
||||
function changeSorting(key: string) {
|
||||
if (key === '')
|
||||
function changeSorting (key: string): void {
|
||||
if (key === '') {
|
||||
return
|
||||
}
|
||||
if (key !== sortKey) {
|
||||
sortKey = key
|
||||
sortOrder = SortingOrder.Ascending
|
||||
} else {
|
||||
sortOrder = (sortOrder === SortingOrder.Ascending) ? SortingOrder.Descending : SortingOrder.Ascending
|
||||
sortOrder = sortOrder === SortingOrder.Ascending ? SortingOrder.Descending : SortingOrder.Ascending
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#await buildModel({client, _class, keys: config, options})}
|
||||
<Loading/>
|
||||
{#await buildModel({ client, _class, keys: config, options })}
|
||||
<Loading />
|
||||
{:then model}
|
||||
<div class="container">
|
||||
<ScrollBox vertical stretch noShift>
|
||||
<table class="table-body">
|
||||
<thead>
|
||||
<tr class="tr-head">
|
||||
{#each model as attribute, cellHead}
|
||||
{#if !cellHead}
|
||||
<th>
|
||||
<div class="checkCell" class:checkall={checking}>
|
||||
<CheckBox symbol={'minus'} />
|
||||
<div class="container">
|
||||
<ScrollBox vertical stretch noShift>
|
||||
<table class="table-body">
|
||||
<thead>
|
||||
<tr class="tr-head">
|
||||
{#each model as attribute, cellHead}
|
||||
{#if !cellHead}
|
||||
<th>
|
||||
<div class="checkCell" class:checkall={checking}>
|
||||
<CheckBox symbol={'minus'} />
|
||||
</div>
|
||||
</th>
|
||||
{/if}
|
||||
|
||||
<th
|
||||
class:sortable={attribute.key}
|
||||
class:sorted={attribute.key === sortKey}
|
||||
on:click={() => changeSorting(attribute.key)}
|
||||
>
|
||||
<div class="flex-row-center">
|
||||
<Label label={attribute.label} />
|
||||
{#if attribute.key === sortKey}
|
||||
<div class="icon">
|
||||
{#if sortOrder === SortingOrder.Ascending}
|
||||
<IconUp size={'small'} />
|
||||
{:else}
|
||||
<IconDown size={'small'} />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</th>
|
||||
{/if}
|
||||
<th class:sortable={attribute.key} class:sorted={attribute.key === sortKey} on:click={() => changeSorting(attribute.key)}>
|
||||
<div class="flex-row-center">
|
||||
<Label label = {attribute.label}/>
|
||||
{#if attribute.key === sortKey}
|
||||
<div class="icon">
|
||||
{#if sortOrder === SortingOrder.Ascending}
|
||||
<IconUp size={'small'} />
|
||||
{:else}
|
||||
<IconDown size={'small'} />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</th>
|
||||
{/each}
|
||||
</tr>
|
||||
</thead>
|
||||
{#if objects}
|
||||
<tbody>
|
||||
{#each objects as object, row (object._id)}
|
||||
<tr class="tr-body" class:checking class:fixed={row === selectRow}>
|
||||
{#each model as attribute, cell}
|
||||
{#if !cell}
|
||||
<td><div class="checkCell"><CheckBox bind:checked={checking} /></div></td>
|
||||
<td><div class="firstCell">
|
||||
<svelte:component this={attribute.presenter} value={getValue(object, attribute.key)}/>
|
||||
<div class="menuRow" on:click={(ev) => showMenu(ev, object, row)}><MoreV size={'small'} /></div>
|
||||
</div></td>
|
||||
{:else}
|
||||
<td><svelte:component this={attribute.presenter} value={getValue(object, attribute.key)}/></td>
|
||||
{/if}
|
||||
{/each}
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
{/if}
|
||||
</table>
|
||||
</ScrollBox>
|
||||
</div>
|
||||
{/each}
|
||||
</tr>
|
||||
</thead>
|
||||
{#if objects}
|
||||
<tbody>
|
||||
{#each objects as object, row (object._id)}
|
||||
<tr class="tr-body" class:checking class:fixed={row === selectRow}>
|
||||
{#each model as attribute, cell}
|
||||
{#if !cell}
|
||||
<td><div class="checkCell"><CheckBox bind:checked={checking} /></div></td>
|
||||
<td
|
||||
><div class="firstCell">
|
||||
<svelte:component
|
||||
this={attribute.presenter}
|
||||
value={getValue(object, attribute.key)}
|
||||
{...attribute.props}
|
||||
/>
|
||||
<div class="menuRow" on:click={(ev) => showMenu(ev, object, row)}><MoreV size={'small'} /></div>
|
||||
</div></td
|
||||
>
|
||||
{:else}
|
||||
<td
|
||||
><svelte:component
|
||||
this={attribute.presenter}
|
||||
value={getValue(object, attribute.key)}
|
||||
{...attribute.props}
|
||||
/></td
|
||||
>
|
||||
{/if}
|
||||
{/each}
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
{/if}
|
||||
</table>
|
||||
</ScrollBox>
|
||||
</div>
|
||||
{/await}
|
||||
|
||||
<style lang="scss">
|
||||
@ -138,7 +162,9 @@
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.table-body { width: 100%; }
|
||||
.table-body {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.firstCell {
|
||||
display: flex;
|
||||
@ -146,10 +172,12 @@
|
||||
align-items: center;
|
||||
.menuRow {
|
||||
visibility: hidden;
|
||||
margin-left: .5rem;
|
||||
opacity: .6;
|
||||
margin-left: 0.5rem;
|
||||
opacity: 0.6;
|
||||
cursor: pointer;
|
||||
&:hover { opacity: 1; }
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.checkCell {
|
||||
@ -158,11 +186,12 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: .5rem 1.5rem;
|
||||
th,
|
||||
td {
|
||||
padding: 0.5rem 1.5rem;
|
||||
text-align: left;
|
||||
&:first-child {
|
||||
padding: 0 .75rem;
|
||||
padding: 0 0.75rem;
|
||||
width: 2.5rem;
|
||||
}
|
||||
&:nth-child(2) {
|
||||
@ -176,36 +205,51 @@
|
||||
top: 0;
|
||||
height: 2.5rem;
|
||||
font-weight: 500;
|
||||
font-size: .75rem;
|
||||
font-size: 0.75rem;
|
||||
color: var(--theme-content-dark-color);
|
||||
background-color: var(--theme-bg-color);
|
||||
box-shadow: inset 0 -1px 0 0 var(--theme-bg-focused-color);
|
||||
user-select: none;
|
||||
z-index: 5;
|
||||
|
||||
&.sortable { cursor: pointer; }
|
||||
&.sorted .icon {
|
||||
margin-left: .25rem;
|
||||
opacity: .6;
|
||||
&.sortable {
|
||||
cursor: pointer;
|
||||
}
|
||||
&.sorted .icon {
|
||||
margin-left: 0.25rem;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.checkall {
|
||||
visibility: visible;
|
||||
}
|
||||
.checkall { visibility: visible; }
|
||||
}
|
||||
|
||||
.tr-body {
|
||||
height: 3.25rem;
|
||||
color: var(--theme-caption-color);
|
||||
border-bottom: 1px solid var(--theme-button-border-hovered);
|
||||
&:hover, &.checking {
|
||||
&:hover,
|
||||
&.checking {
|
||||
background-color: var(--theme-table-bg-hover);
|
||||
.checkCell { visibility: visible; }
|
||||
.checkCell {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
&:hover .firstCell .menuRow {
|
||||
visibility: visible;
|
||||
}
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
&:hover .firstCell .menuRow { visibility: visible; }
|
||||
&:last-child { border-bottom: none; }
|
||||
}
|
||||
|
||||
.fixed {
|
||||
background-color: var(--theme-table-bg-hover);
|
||||
.checkCell { visibility: visible; }
|
||||
.menuRow { visibility: visible; }
|
||||
.checkCell {
|
||||
visibility: visible;
|
||||
}
|
||||
.menuRow {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -14,37 +14,37 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import core, { Class, Client, Doc, FindOptions, FindResult, Obj, Ref, AttachedDoc, TxOperations, Collection } from '@anticrm/core'
|
||||
import core, { AttachedDoc, Class, Client, Collection, Doc, FindOptions, FindResult, Obj, Ref, TxOperations } from '@anticrm/core'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import { getAttributePresenterClass } from '@anticrm/presentation'
|
||||
import type { AnyComponent } from '@anticrm/ui'
|
||||
import type { Action, ActionTarget, BuildModelOptions } from '@anticrm/view'
|
||||
import view, { AttributeModel } from '@anticrm/view'
|
||||
import view, { AttributeModel, BuildModelKey } from '@anticrm/view'
|
||||
import { ErrorPresenter } from '@anticrm/ui'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function getObjectPresenter (client: Client, _class: Ref<Class<Obj>>, preserveKey: string): Promise<AttributeModel> {
|
||||
export async function getObjectPresenter (client: Client, _class: Ref<Class<Obj>>, preserveKey: BuildModelKey): Promise<AttributeModel> {
|
||||
const clazz = client.getHierarchy().getClass(_class)
|
||||
const presenterMixin = client.getHierarchy().as(clazz, view.mixin.AttributePresenter)
|
||||
if (presenterMixin.presenter === undefined) {
|
||||
if (clazz.extends !== undefined) {
|
||||
return await getObjectPresenter(client, clazz.extends, preserveKey)
|
||||
} else {
|
||||
throw new Error('object presenter not found for ' + preserveKey)
|
||||
throw new Error('object presenter not found for ' + JSON.stringify(preserveKey))
|
||||
}
|
||||
}
|
||||
const presenter = await getResource(presenterMixin.presenter)
|
||||
return {
|
||||
key: preserveKey,
|
||||
key: typeof preserveKey === 'string' ? preserveKey : '',
|
||||
_class,
|
||||
label: clazz.label,
|
||||
presenter
|
||||
}
|
||||
}
|
||||
|
||||
async function getAttributePresenter (client: Client, _class: Ref<Class<Obj>>, key: string, preserveKey: string): Promise<AttributeModel> {
|
||||
async function getAttributePresenter (client: Client, _class: Ref<Class<Obj>>, key: string, preserveKey: BuildModelKey): Promise<AttributeModel> {
|
||||
const attribute = client.getHierarchy().getAttribute(_class, key)
|
||||
let attrClass = getAttributePresenterClass(attribute)
|
||||
const clazz = client.getHierarchy().getClass(attrClass)
|
||||
@ -57,25 +57,25 @@ async function getAttributePresenter (client: Client, _class: Ref<Class<Obj>>, k
|
||||
parent = pclazz.extends
|
||||
}
|
||||
if (presenterMixin.presenter === undefined) {
|
||||
throw new Error('attribute presenter not found for ' + preserveKey)
|
||||
throw new Error('attribute presenter not found for ' + JSON.stringify(preserveKey))
|
||||
}
|
||||
const presenter = await getResource(presenterMixin.presenter)
|
||||
return {
|
||||
key: preserveKey,
|
||||
key: key,
|
||||
_class: attrClass,
|
||||
label: attribute.label,
|
||||
presenter
|
||||
}
|
||||
}
|
||||
|
||||
async function getPresenter (client: Client, _class: Ref<Class<Obj>>, key: string, preserveKey: string, options?: FindOptions<Doc>): Promise<AttributeModel> {
|
||||
async function getPresenter (client: Client, _class: Ref<Class<Obj>>, key: BuildModelKey, preserveKey: BuildModelKey, options?: FindOptions<Doc>): Promise<AttributeModel> {
|
||||
if (typeof key === 'object') {
|
||||
const { presenter, label } = key
|
||||
return {
|
||||
key: '',
|
||||
_class,
|
||||
label: label as IntlString,
|
||||
presenter: await getResource(presenter as AnyComponent)
|
||||
presenter: await getResource(presenter)
|
||||
}
|
||||
}
|
||||
if (key.length === 0) {
|
||||
@ -103,16 +103,25 @@ async function getPresenter (client: Client, _class: Ref<Class<Obj>>, key: strin
|
||||
}
|
||||
|
||||
export async function buildModel (options: BuildModelOptions): Promise<AttributeModel[]> {
|
||||
console.log('building table model for', options._class)
|
||||
console.log('building table model for', options)
|
||||
// eslint-disable-next-line array-callback-return
|
||||
const model = options.keys.map(key => {
|
||||
const model = options.keys.map(async key => {
|
||||
try {
|
||||
const result = getPresenter(options.client, options._class, key, key, options.options)
|
||||
return result
|
||||
return await getPresenter(options.client, options._class, key, key, options.options)
|
||||
} catch (err: any) {
|
||||
if (!(options.ignoreMissing ?? false)) {
|
||||
throw err
|
||||
if ((options.ignoreMissing ?? false)) {
|
||||
return undefined
|
||||
}
|
||||
const stringKey = (typeof key === 'string') ? key : key.label
|
||||
console.error('Failed to find presenter for', key, err)
|
||||
const errorPresenter: AttributeModel = {
|
||||
key: '',
|
||||
presenter: ErrorPresenter,
|
||||
label: stringKey as IntlString,
|
||||
_class: core.class.TypeString,
|
||||
props: { error: err }
|
||||
}
|
||||
return errorPresenter
|
||||
}
|
||||
})
|
||||
console.log(model)
|
||||
@ -134,7 +143,7 @@ export async function getActions (client: Client, _class: Ref<Class<Obj>>): Prom
|
||||
return await client.findAll(view.class.Action, { _id: { $in: filterActions(client, _class, targets) } })
|
||||
}
|
||||
|
||||
export async function deleteObject (client: Client & TxOperations, object: Doc) {
|
||||
export async function deleteObject (client: Client & TxOperations, object: Doc): Promise<void> {
|
||||
const hierarchy = client.getHierarchy()
|
||||
const attributes = hierarchy.getAllAttributes(object._class)
|
||||
for (const [name, attribute] of attributes) {
|
||||
@ -142,7 +151,7 @@ export async function deleteObject (client: Client & TxOperations, object: Doc)
|
||||
const collection = attribute.type as Collection<AttachedDoc>
|
||||
const allAttached = await client.findAll(collection.of, { attachedTo: object._id })
|
||||
for (const attached of allAttached) {
|
||||
deleteObject(client, attached)
|
||||
deleteObject(client, attached).catch(err => console.log('failed to delete', name, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -103,6 +103,14 @@ export interface Sequence extends Doc {
|
||||
*/
|
||||
export const viewId = 'view' as Plugin
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type BuildModelKey = string | {
|
||||
presenter: AnyComponent
|
||||
label: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -111,6 +119,8 @@ export interface AttributeModel {
|
||||
label: IntlString
|
||||
_class: Ref<Class<Doc>>
|
||||
presenter: AnySvelteComponent
|
||||
// Extra properties for component
|
||||
props?: Record<string, any>
|
||||
}
|
||||
|
||||
/**
|
||||
@ -119,7 +129,7 @@ export interface AttributeModel {
|
||||
export interface BuildModelOptions {
|
||||
client: Client
|
||||
_class: Ref<Class<Obj>>
|
||||
keys: string[]
|
||||
keys: BuildModelKey[]
|
||||
options?: FindOptions<Doc>
|
||||
ignoreMissing?: boolean
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user