mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-15 12:55:59 +00:00
Editable presenters (#2594)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
parent
61fbb4b78b
commit
0bee06e8a7
@ -713,7 +713,7 @@ export function createModel (builder: Builder): void {
|
||||
view.filter.FilterNestedDontMatch
|
||||
)
|
||||
|
||||
classPresenter(builder, core.class.EnumOf, view.component.StringPresenter, view.component.EnumEditor)
|
||||
classPresenter(builder, core.class.EnumOf, view.component.EnumPresenter, view.component.EnumEditor)
|
||||
|
||||
createAction(builder, {
|
||||
action: view.actionImpl.ShowPopup,
|
||||
|
@ -66,7 +66,8 @@ export default mergeIds(viewId, view, {
|
||||
MarkupEditorPopup: '' as AnyComponent,
|
||||
ListView: '' as AnyComponent,
|
||||
IndexedDocumentPreview: '' as AnyComponent,
|
||||
SpaceRefPresenter: '' as AnyComponent
|
||||
SpaceRefPresenter: '' as AnyComponent,
|
||||
EnumPresenter: '' as AnyComponent
|
||||
},
|
||||
string: {
|
||||
Table: '' as IntlString,
|
||||
|
@ -24,7 +24,7 @@
|
||||
import Label from './Label.svelte'
|
||||
|
||||
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
||||
export let label: IntlString
|
||||
export let label: IntlString | undefined = undefined
|
||||
export let placeholder: IntlString | undefined = ui.string.SearchDots
|
||||
export let items: DropdownTextItem[]
|
||||
export let multiselect = false
|
||||
|
@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { Card } from '@hcengineering/board'
|
||||
import { DateRangeMode } from '@hcengineering/core'
|
||||
import { DatePresenter } from '@hcengineering/ui'
|
||||
|
||||
export let value: Card
|
||||
@ -19,7 +20,7 @@
|
||||
{#if value.dueDate}
|
||||
<DatePresenter
|
||||
bind:value={value.dueDate}
|
||||
withTime={true}
|
||||
mode={DateRangeMode.DATETIME}
|
||||
icon={isOverdue ? 'overdue' : undefined}
|
||||
{size}
|
||||
kind="transparent"
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Employee } from '@hcengineering/contact'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { AssigneeBox, createQuery } from '@hcengineering/presentation'
|
||||
import { ButtonKind } from '@hcengineering/ui'
|
||||
import { PersonLabelTooltip } from '..'
|
||||
import contact from '../plugin'
|
||||
@ -10,6 +10,7 @@
|
||||
export let value: Ref<Employee> | null | undefined
|
||||
export let kind: ButtonKind = 'link'
|
||||
export let tooltipLabels: PersonLabelTooltip | undefined = undefined
|
||||
export let onChange: ((value: Ref<Employee>) => void) | undefined = undefined
|
||||
|
||||
let employee: Employee | undefined
|
||||
const query = createQuery()
|
||||
@ -26,14 +27,26 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<EmployeePresenter
|
||||
value={getValue(employee, value)}
|
||||
{tooltipLabels}
|
||||
isInteractive={false}
|
||||
shouldShowAvatar
|
||||
shouldShowPlaceholder
|
||||
defaultName={contact.string.NotSpecified}
|
||||
shouldShowName={kind !== 'list'}
|
||||
avatarSize={kind === 'list-header' ? 'small' : 'x-small'}
|
||||
disableClick
|
||||
/>
|
||||
{#if onChange !== undefined}
|
||||
<AssigneeBox
|
||||
label={contact.string.Employee}
|
||||
{value}
|
||||
size={'medium'}
|
||||
kind={'link'}
|
||||
showNavigate={false}
|
||||
justify={'left'}
|
||||
on:change={({ detail }) => onChange?.(detail)}
|
||||
/>
|
||||
{:else}
|
||||
<EmployeePresenter
|
||||
value={getValue(employee, value)}
|
||||
{tooltipLabels}
|
||||
isInteractive={false}
|
||||
shouldShowAvatar
|
||||
shouldShowPlaceholder
|
||||
defaultName={contact.string.NotSpecified}
|
||||
shouldShowName={kind !== 'list'}
|
||||
avatarSize={kind === 'list-header' ? 'small' : 'x-small'}
|
||||
disableClick
|
||||
/>
|
||||
{/if}
|
||||
|
@ -20,10 +20,10 @@
|
||||
import Won from '../icons/Won.svelte'
|
||||
import Lost from '../icons/Lost.svelte'
|
||||
|
||||
export let value: DoneState
|
||||
export let value: DoneState | null | undefined
|
||||
export let showTitle: boolean = true
|
||||
|
||||
$: color = value._class === task.class.WonState ? getPlatformColor(0) : getPlatformColor(11)
|
||||
$: color = value?._class === task.class.WonState ? getPlatformColor(0) : getPlatformColor(11)
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
|
@ -20,12 +20,12 @@
|
||||
import task from '@hcengineering/task'
|
||||
import DoneStatePresenter from './DoneStatePresenter.svelte'
|
||||
|
||||
export let value: Ref<DoneState>
|
||||
export let value: Ref<DoneState> | null | undefined
|
||||
export let showTitle: boolean = true
|
||||
|
||||
let state: DoneState | undefined
|
||||
const query = createQuery()
|
||||
$: query.query(task.class.DoneState, { _id: value }, (res) => ([state] = res), { limit: 1 })
|
||||
$: value && query.query(task.class.DoneState, { _id: value }, (res) => ([state] = res), { limit: 1 })
|
||||
</script>
|
||||
|
||||
{#if state}
|
||||
|
@ -17,9 +17,11 @@
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import task, { State } from '@hcengineering/task'
|
||||
import StateEditor from './StateEditor.svelte'
|
||||
import StatePresenter from './StatePresenter.svelte'
|
||||
|
||||
export let value: Ref<State>
|
||||
export let onChange: ((value: Ref<State>) => void) | undefined = undefined
|
||||
|
||||
let state: State | undefined
|
||||
const query = createQuery()
|
||||
@ -27,5 +29,9 @@
|
||||
</script>
|
||||
|
||||
{#if state}
|
||||
<StatePresenter value={state} />
|
||||
{#if onChange !== undefined}
|
||||
<StateEditor {value} space={state.space} {onChange} kind="link" size="medium" />
|
||||
{:else}
|
||||
<StatePresenter value={state} />
|
||||
{/if}
|
||||
{/if}
|
||||
|
@ -18,7 +18,12 @@
|
||||
import { DateRangePresenter } from '@hcengineering/ui'
|
||||
|
||||
export let value: number | null | undefined
|
||||
export let onChange: ((value: number | null) => void) | undefined = undefined
|
||||
export let noShift: boolean = false
|
||||
</script>
|
||||
|
||||
<DateRangePresenter {value} {noShift} />
|
||||
{#if onChange !== undefined}
|
||||
<DateRangePresenter {value} {noShift} editable on:change={(e) => onChange?.(e.detail)} />
|
||||
{:else}
|
||||
<DateRangePresenter {value} {noShift} />
|
||||
{/if}
|
||||
|
60
plugins/view-resources/src/components/EnumPresenter.svelte
Normal file
60
plugins/view-resources/src/components/EnumPresenter.svelte
Normal file
@ -0,0 +1,60 @@
|
||||
<!--
|
||||
// Copyright © 2022 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 core, { EnumOf } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { DropdownLabels, DropdownTextItem } from '@hcengineering/ui'
|
||||
import StringPresenter from './StringPresenter.svelte'
|
||||
|
||||
export let value: string
|
||||
export let type: EnumOf | undefined = undefined
|
||||
export let onChange: ((value: string) => void) | undefined = undefined
|
||||
|
||||
let items: DropdownTextItem[] = []
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
$: type &&
|
||||
query.query(
|
||||
core.class.Enum,
|
||||
{
|
||||
_id: type.of
|
||||
},
|
||||
(res) => {
|
||||
items = res[0]?.enumValues?.map((p) => {
|
||||
return { id: p, label: p }
|
||||
})
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
</script>
|
||||
|
||||
{#if onChange !== undefined && type !== undefined}
|
||||
<DropdownLabels
|
||||
bind:selected={value}
|
||||
{items}
|
||||
useFlexGrow={true}
|
||||
justify={'left'}
|
||||
size={'large'}
|
||||
kind={'link'}
|
||||
width={'100%'}
|
||||
autoSelect={false}
|
||||
on:selected={(e) => {
|
||||
onChange?.(e.detail)
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<StringPresenter {value} />
|
||||
{/if}
|
@ -14,10 +14,10 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Class, Doc, DocumentQuery, FindOptions, Lookup, Ref } from '@hcengineering/core'
|
||||
import core, { AnyAttribute, Class, Doc, DocumentQuery, FindOptions, Lookup, Ref } from '@hcengineering/core'
|
||||
import { getObjectValue, SortingOrder } from '@hcengineering/core'
|
||||
import notification from '@hcengineering/notification'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { createQuery, getClient, updateAttribute } from '@hcengineering/presentation'
|
||||
import {
|
||||
CheckBox,
|
||||
Component,
|
||||
@ -31,9 +31,9 @@
|
||||
} from '@hcengineering/ui'
|
||||
import { AttributeModel, BuildModelKey } from '@hcengineering/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import view from '../plugin'
|
||||
import { buildConfigLookup, buildModel, LoadingProps } from '../utils'
|
||||
import Menu from './Menu.svelte'
|
||||
import view from '../plugin'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let query: DocumentQuery<Doc>
|
||||
@ -180,11 +180,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
const joinProps = (collectionAttr: boolean, object: Doc, props: any) => {
|
||||
if (collectionAttr) {
|
||||
return { object, ...props }
|
||||
const joinProps = (attribute: AttributeModel, object: Doc) => {
|
||||
if (attribute.collectionAttr) {
|
||||
return { object, ...attribute.props }
|
||||
}
|
||||
return props
|
||||
if (attribute.attribute?.type._class === core.class.EnumOf) {
|
||||
return { ...attribute.props, type: attribute.attribute.type }
|
||||
}
|
||||
return attribute.props
|
||||
}
|
||||
function getValue (attribute: AttributeModel, object: Doc): any {
|
||||
if (attribute.castRequest) {
|
||||
@ -195,6 +198,19 @@
|
||||
}
|
||||
return getObjectValue(attribute.key, object)
|
||||
}
|
||||
|
||||
function onChange (value: any, doc: Doc, key: string, attribute: AnyAttribute) {
|
||||
updateAttribute(client, doc, _class, { key, attr: attribute }, value)
|
||||
}
|
||||
|
||||
function getOnChange (doc: Doc, attribute: AttributeModel) {
|
||||
const attr = attribute.attribute
|
||||
if (attr === undefined) return
|
||||
if (attribute.collectionAttr) return
|
||||
if (attribute.isLookup) return
|
||||
const key = attribute.castRequest ? attribute.key.substring(attribute.castRequest.length + 1) : attribute.key
|
||||
return (value: any) => onChange(value, doc, key, attr)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#await buildModel({ client, _class, keys: config, lookup })}
|
||||
@ -295,10 +311,12 @@
|
||||
{#each model as attribute, cell}
|
||||
<td>
|
||||
<div class:antiTable-cells__firstCell={!cell}>
|
||||
<!-- {getOnChange(object, attribute) !== undefined} -->
|
||||
<svelte:component
|
||||
this={attribute.presenter}
|
||||
value={getValue(attribute, object)}
|
||||
{...joinProps(attribute.collectionAttr, object, attribute.props)}
|
||||
onChange={getOnChange(object, attribute)}
|
||||
{...joinProps(attribute, object)}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
|
@ -121,6 +121,7 @@
|
||||
if (viewlet.hiddenKeys?.includes(attribute.name)) return
|
||||
if (hierarchy.isDerived(attribute.type._class, core.class.Collection)) return
|
||||
const value = getValue(attribute.name, attribute.type)
|
||||
if (result.findIndex((p) => p.value === attribute.name) !== -1) return
|
||||
if (result.findIndex((p) => p.value === value) !== -1) return
|
||||
const { attrClass, category } = getAttributePresenterClass(hierarchy, attribute)
|
||||
const typeClass = hierarchy.getClass(attrClass)
|
||||
|
@ -13,8 +13,9 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Doc, getObjectValue, Ref } from '@hcengineering/core'
|
||||
import { AnyAttribute, Doc, getObjectValue, Ref } from '@hcengineering/core'
|
||||
import notification from '@hcengineering/notification'
|
||||
import { getClient, updateAttribute } from '@hcengineering/presentation'
|
||||
import { CheckBox, Component, deviceOptionsStore as deviceInfo, tooltip } from '@hcengineering/ui'
|
||||
import { AttributeModel } from '@hcengineering/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@ -50,6 +51,20 @@
|
||||
$: elem && elementByIndex.set(index, elem)
|
||||
$: indexById.set(docObject._id, index)
|
||||
$: docByIndex.set(index, docObject)
|
||||
|
||||
const client = getClient()
|
||||
|
||||
function onChange (value: any, doc: Doc, key: string, attribute: AnyAttribute) {
|
||||
updateAttribute(client, doc, doc._class, { key, attr: attribute }, value)
|
||||
}
|
||||
|
||||
function getOnChange (docObject: Doc, attribute: AttributeModel) {
|
||||
const attr = attribute.attribute
|
||||
if (attr === undefined) return
|
||||
if (attribute.collectionAttr) return
|
||||
if (attribute.isLookup) return
|
||||
return (value: any) => onChange(value, docObject, attribute.key, attr)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
@ -90,6 +105,7 @@
|
||||
{...props}
|
||||
value={getObjectValue(attributeModel.key, docObject) ?? ''}
|
||||
object={docObject}
|
||||
onChange={getOnChange(docObject, attributeModel)}
|
||||
kind={'list'}
|
||||
{...attributeModel.props}
|
||||
/>
|
||||
@ -100,6 +116,7 @@
|
||||
{...props}
|
||||
value={getObjectValue(attributeModel.key, docObject) ?? ''}
|
||||
object={docObject}
|
||||
onChange={getOnChange(docObject, attributeModel)}
|
||||
kind={'list'}
|
||||
{...attributeModel.props}
|
||||
/>
|
||||
@ -127,6 +144,7 @@
|
||||
{...props}
|
||||
value={value ?? ''}
|
||||
objectId={docObject._id}
|
||||
onChange={getOnChange(docObject, attributeModel)}
|
||||
groupBy={groupByKey}
|
||||
{...attributeModel.props}
|
||||
/>
|
||||
|
@ -64,6 +64,7 @@ import ValueSelector from './components/ValueSelector.svelte'
|
||||
import ViewletSettingButton from './components/ViewletSettingButton.svelte'
|
||||
import SpaceRefPresenter from './components/SpaceRefPresenter.svelte'
|
||||
import EnumArrayEditor from './components/EnumArrayEditor.svelte'
|
||||
import EnumPresenter from './components/EnumPresenter.svelte'
|
||||
|
||||
import {
|
||||
afterResult,
|
||||
@ -176,7 +177,8 @@ export default async (): Promise<Resources> => ({
|
||||
GrowPresenter,
|
||||
IndexedDocumentPreview,
|
||||
SpaceRefPresenter,
|
||||
EnumArrayEditor
|
||||
EnumArrayEditor,
|
||||
EnumPresenter
|
||||
},
|
||||
popup: {
|
||||
PositionElementAlignment
|
||||
|
@ -92,7 +92,8 @@ export async function getObjectPresenter (
|
||||
presenter,
|
||||
props: preserveKey.props,
|
||||
sortingKey,
|
||||
collectionAttr: isCollectionAttr
|
||||
collectionAttr: isCollectionAttr,
|
||||
isLookup: false
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,7 +165,8 @@ async function getAttributePresenter (
|
||||
props: preserveKey.props,
|
||||
icon: presenterMixin.icon,
|
||||
attribute,
|
||||
collectionAttr: isCollectionAttr
|
||||
collectionAttr: isCollectionAttr,
|
||||
isLookup: false
|
||||
}
|
||||
}
|
||||
|
||||
@ -185,7 +187,8 @@ export async function getPresenter<T extends Doc> (
|
||||
label: label as IntlString,
|
||||
presenter: typeof presenter === 'string' ? await getResource(presenter) : presenter,
|
||||
props: preserveKey.props,
|
||||
collectionAttr: isCollectionAttr
|
||||
collectionAttr: isCollectionAttr,
|
||||
isLookup: false
|
||||
}
|
||||
}
|
||||
if (key.key.length === 0) {
|
||||
@ -293,7 +296,8 @@ export async function buildModel (options: BuildModelOptions): Promise<Attribute
|
||||
label: stringKey as IntlString,
|
||||
_class: core.class.TypeString,
|
||||
props: { error: err },
|
||||
collectionAttr: false
|
||||
collectionAttr: false,
|
||||
isLookup: false
|
||||
}
|
||||
return errorPresenter
|
||||
}
|
||||
@ -348,6 +352,7 @@ async function getLookupPresenter<T extends Doc> (
|
||||
const lookupKey = { ...key, key: lookupProperty[0] }
|
||||
const model = await getPresenter(client, lookupClass[0], lookupKey, preserveKey, undefined, lookupClass[2])
|
||||
model.label = getLookupLabel(client, lookupClass[1], lookupClass[0], lookupKey, lookupProperty[1])
|
||||
model.isLookup = true
|
||||
return model
|
||||
}
|
||||
|
||||
|
@ -416,6 +416,7 @@ export interface AttributeModel {
|
||||
|
||||
attribute?: AnyAttribute
|
||||
collectionAttr: boolean
|
||||
isLookup: boolean
|
||||
|
||||
castRequest?: Ref<Mixin<Doc>>
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user