mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-23 08:48:01 +00:00
Custom attributes (#1650)
Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
parent
342acb5bb8
commit
0be40e005e
@ -13,16 +13,11 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import type { Class, Ref, Type } from '@anticrm/core'
|
|
||||||
import core, { coreId } from '@anticrm/core'
|
import core, { coreId } from '@anticrm/core'
|
||||||
import { IntlString, mergeIds } from '@anticrm/platform'
|
import { IntlString, mergeIds } from '@anticrm/platform'
|
||||||
|
|
||||||
export default mergeIds(coreId, core, {
|
export default mergeIds(coreId, core, {
|
||||||
class: {
|
|
||||||
Type: '' as Ref<Class<Type<any>>>
|
|
||||||
},
|
|
||||||
string: {
|
string: {
|
||||||
Name: '' as IntlString,
|
|
||||||
Description: '' as IntlString,
|
Description: '' as IntlString,
|
||||||
Private: '' as IntlString,
|
Private: '' as IntlString,
|
||||||
Archived: '' as IntlString,
|
Archived: '' as IntlString,
|
||||||
|
@ -36,7 +36,7 @@ import {
|
|||||||
Type,
|
Type,
|
||||||
Version
|
Version
|
||||||
} from '@anticrm/core'
|
} from '@anticrm/core'
|
||||||
import { Index, Model, Prop, TypeIntlString, TypeRef, TypeString, TypeTimestamp } from '@anticrm/model'
|
import { Index, Model, Prop, TypeIntlString, TypeRef, TypeString, TypeTimestamp, UX } from '@anticrm/model'
|
||||||
import type { IntlString } from '@anticrm/platform'
|
import type { IntlString } from '@anticrm/platform'
|
||||||
import core from './component'
|
import core from './component'
|
||||||
|
|
||||||
@ -106,46 +106,57 @@ export class TAttribute extends TDoc implements AnyAttribute {
|
|||||||
name!: string
|
name!: string
|
||||||
type!: Type<any>
|
type!: Type<any>
|
||||||
label!: IntlString
|
label!: IntlString
|
||||||
|
isCustom?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@Model(core.class.Type, core.class.Obj)
|
@Model(core.class.Type, core.class.Obj, DOMAIN_MODEL)
|
||||||
export class TType extends TObj implements Type<any> {
|
export class TType extends TObj implements Type<any> {
|
||||||
label!: IntlString
|
label!: IntlString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UX(core.string.String)
|
||||||
@Model(core.class.TypeString, core.class.Type)
|
@Model(core.class.TypeString, core.class.Type)
|
||||||
export class TTypeString extends TType {}
|
export class TTypeString extends TType {}
|
||||||
|
|
||||||
|
@UX(core.string.IntlString)
|
||||||
@Model(core.class.TypeIntlString, core.class.Type)
|
@Model(core.class.TypeIntlString, core.class.Type)
|
||||||
export class TTypeIntlString extends TType {}
|
export class TTypeIntlString extends TType {}
|
||||||
|
|
||||||
|
@UX(core.string.Number)
|
||||||
@Model(core.class.TypeNumber, core.class.Type)
|
@Model(core.class.TypeNumber, core.class.Type)
|
||||||
export class TTypeNumber extends TType {}
|
export class TTypeNumber extends TType {}
|
||||||
|
|
||||||
|
@UX(core.string.Markup)
|
||||||
@Model(core.class.TypeMarkup, core.class.Type)
|
@Model(core.class.TypeMarkup, core.class.Type)
|
||||||
export class TTypeMarkup extends TType {}
|
export class TTypeMarkup extends TType {}
|
||||||
|
|
||||||
|
@UX(core.string.Ref)
|
||||||
@Model(core.class.RefTo, core.class.Type)
|
@Model(core.class.RefTo, core.class.Type)
|
||||||
export class TRefTo extends TType implements RefTo<Doc> {
|
export class TRefTo extends TType implements RefTo<Doc> {
|
||||||
to!: Ref<Class<Doc>>
|
to!: Ref<Class<Doc>>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UX(core.string.Collection)
|
||||||
@Model(core.class.Collection, core.class.Type)
|
@Model(core.class.Collection, core.class.Type)
|
||||||
export class TCollection extends TType implements Collection<AttachedDoc> {
|
export class TCollection extends TType implements Collection<AttachedDoc> {
|
||||||
of!: Ref<Class<Doc>>
|
of!: Ref<Class<Doc>>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UX(core.string.Array)
|
||||||
@Model(core.class.ArrOf, core.class.Type)
|
@Model(core.class.ArrOf, core.class.Type)
|
||||||
export class TArrOf extends TType implements ArrOf<Doc> {
|
export class TArrOf extends TType implements ArrOf<Doc> {
|
||||||
of!: Type<Doc>
|
of!: Type<Doc>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UX(core.string.Boolean)
|
||||||
@Model(core.class.TypeBoolean, core.class.Type)
|
@Model(core.class.TypeBoolean, core.class.Type)
|
||||||
export class TTypeBoolean extends TType {}
|
export class TTypeBoolean extends TType {}
|
||||||
|
|
||||||
|
@UX(core.string.Timestamp)
|
||||||
@Model(core.class.TypeTimestamp, core.class.Type)
|
@Model(core.class.TypeTimestamp, core.class.Type)
|
||||||
export class TTypeTimestamp extends TType {}
|
export class TTypeTimestamp extends TType {}
|
||||||
|
|
||||||
|
@UX(core.string.Date)
|
||||||
@Model(core.class.TypeDate, core.class.Type)
|
@Model(core.class.TypeDate, core.class.Type)
|
||||||
export class TTypeDate extends TType {}
|
export class TTypeDate extends TType {}
|
||||||
|
|
||||||
|
@ -236,6 +236,22 @@ export function createModel (builder: Builder): void {
|
|||||||
classPresenter(builder, core.class.TypeDate, view.component.DatePresenter, view.component.DateEditor)
|
classPresenter(builder, core.class.TypeDate, view.component.DatePresenter, view.component.DateEditor)
|
||||||
classPresenter(builder, core.class.Space, view.component.ObjectPresenter)
|
classPresenter(builder, core.class.Space, view.component.ObjectPresenter)
|
||||||
|
|
||||||
|
builder.mixin(core.class.TypeString, core.class.Class, view.mixin.ObjectEditor, {
|
||||||
|
editor: view.component.StringTypeEditor
|
||||||
|
})
|
||||||
|
|
||||||
|
builder.mixin(core.class.TypeBoolean, core.class.Class, view.mixin.ObjectEditor, {
|
||||||
|
editor: view.component.BooleanTypeEditor
|
||||||
|
})
|
||||||
|
|
||||||
|
builder.mixin(core.class.TypeDate, core.class.Class, view.mixin.ObjectEditor, {
|
||||||
|
editor: view.component.DateTypeEditor
|
||||||
|
})
|
||||||
|
|
||||||
|
builder.mixin(core.class.TypeNumber, core.class.Class, view.mixin.ObjectEditor, {
|
||||||
|
editor: view.component.NumberTypeEditor
|
||||||
|
})
|
||||||
|
|
||||||
builder.createDoc(
|
builder.createDoc(
|
||||||
view.class.ActionCategory,
|
view.class.ActionCategory,
|
||||||
core.space.Model,
|
core.space.Model,
|
||||||
|
@ -73,7 +73,11 @@ export default mergeIds(viewId, view, {
|
|||||||
TableView: '' as AnyComponent,
|
TableView: '' as AnyComponent,
|
||||||
RolePresenter: '' as AnyComponent,
|
RolePresenter: '' as AnyComponent,
|
||||||
YoutubePresenter: '' as AnyComponent,
|
YoutubePresenter: '' as AnyComponent,
|
||||||
GithubPresenter: '' as AnyComponent
|
GithubPresenter: '' as AnyComponent,
|
||||||
|
StringTypeEditor: '' as AnyComponent,
|
||||||
|
BooleanTypeEditor: '' as AnyComponent,
|
||||||
|
NumberTypeEditor: '' as AnyComponent,
|
||||||
|
DateTypeEditor: '' as AnyComponent
|
||||||
},
|
},
|
||||||
string: {
|
string: {
|
||||||
Table: '' as IntlString,
|
Table: '' as IntlString,
|
||||||
|
@ -11,6 +11,17 @@
|
|||||||
"Description": "Description",
|
"Description": "Description",
|
||||||
"Private": "Private",
|
"Private": "Private",
|
||||||
"Archived": "Archived",
|
"Archived": "Archived",
|
||||||
"ClassLabel": "Label"
|
"ClassLabel": "Label",
|
||||||
|
"String": "String",
|
||||||
|
"Markup": "Markup",
|
||||||
|
"Number": "Number",
|
||||||
|
"Boolean": "Boolean",
|
||||||
|
"Timestamp": "Timestamp",
|
||||||
|
"Date": "Date",
|
||||||
|
"IntlString": "IntlString",
|
||||||
|
"Ref": "Ref",
|
||||||
|
"Collection": "Collection",
|
||||||
|
"Array": "Array",
|
||||||
|
"Bag": "Bag"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,17 @@
|
|||||||
"Description": "Описание",
|
"Description": "Описание",
|
||||||
"Private": "Личный",
|
"Private": "Личный",
|
||||||
"Archived": "Архивный",
|
"Archived": "Архивный",
|
||||||
"ClassLabel": "Тип"
|
"ClassLabel": "Тип",
|
||||||
|
"String": "Строка",
|
||||||
|
"Markup": "Разметка",
|
||||||
|
"Number": "Число",
|
||||||
|
"Boolean": "Логическое",
|
||||||
|
"Timestamp": "Времянная отметка",
|
||||||
|
"Date": "Дата",
|
||||||
|
"IntlString": "Интернационализированная строка",
|
||||||
|
"Ref": "Ссылка",
|
||||||
|
"Collection": "Коллекция",
|
||||||
|
"Array": "Массив",
|
||||||
|
"Bag": "Bag"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -98,6 +98,7 @@ export interface Attribute<T extends PropertyType> extends Doc, UXObject {
|
|||||||
name: string
|
name: string
|
||||||
type: Type<T>
|
type: Type<T>
|
||||||
index?: IndexKind
|
index?: IndexKind
|
||||||
|
isCustom?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,6 +71,7 @@ export default plugin(coreId, {
|
|||||||
TxPutBag: '' as Ref<Class<TxPutBag<PropertyType>>>,
|
TxPutBag: '' as Ref<Class<TxPutBag<PropertyType>>>,
|
||||||
Space: '' as Ref<Class<Space>>,
|
Space: '' as Ref<Class<Space>>,
|
||||||
Account: '' as Ref<Class<Account>>,
|
Account: '' as Ref<Class<Account>>,
|
||||||
|
Type: '' as Ref<Class<Type<any>>>,
|
||||||
TypeString: '' as Ref<Class<Type<string>>>,
|
TypeString: '' as Ref<Class<Type<string>>>,
|
||||||
TypeIntlString: '' as Ref<Class<Type<IntlString>>>,
|
TypeIntlString: '' as Ref<Class<Type<IntlString>>>,
|
||||||
TypeNumber: '' as Ref<Class<Type<string>>>,
|
TypeNumber: '' as Ref<Class<Type<string>>>,
|
||||||
@ -109,6 +110,18 @@ export default plugin(coreId, {
|
|||||||
ModifiedBy: '' as IntlString,
|
ModifiedBy: '' as IntlString,
|
||||||
Class: '' as IntlString,
|
Class: '' as IntlString,
|
||||||
AttachedTo: '' as IntlString,
|
AttachedTo: '' as IntlString,
|
||||||
AttachedToClass: '' as IntlString
|
AttachedToClass: '' as IntlString,
|
||||||
|
String: '' as IntlString,
|
||||||
|
Markup: '' as IntlString,
|
||||||
|
Number: '' as IntlString,
|
||||||
|
Boolean: '' as IntlString,
|
||||||
|
Timestamp: '' as IntlString,
|
||||||
|
Date: '' as IntlString,
|
||||||
|
IntlString: '' as IntlString,
|
||||||
|
Ref: '' as IntlString,
|
||||||
|
Collection: '' as IntlString,
|
||||||
|
Array: '' as IntlString,
|
||||||
|
Bag: '' as IntlString,
|
||||||
|
Name: '' as IntlString
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -339,75 +339,75 @@ export class Builder {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export function TypeString (): Type<string> {
|
export function TypeString (): Type<string> {
|
||||||
return { _class: core.class.TypeString, label: 'TypeString' as IntlString }
|
return { _class: core.class.TypeString, label: core.string.String }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export function TypeNumber (): Type<number> {
|
export function TypeNumber (): Type<number> {
|
||||||
return { _class: core.class.TypeNumber, label: 'TypeNumber' as IntlString }
|
return { _class: core.class.TypeNumber, label: core.string.Number }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export function TypeMarkup (): Type<Markup> {
|
export function TypeMarkup (): Type<Markup> {
|
||||||
return { _class: core.class.TypeMarkup, label: 'TypeMarkup' as IntlString }
|
return { _class: core.class.TypeMarkup, label: core.string.Markup }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export function TypeIntlString (): Type<IntlString> {
|
export function TypeIntlString (): Type<IntlString> {
|
||||||
return { _class: core.class.TypeIntlString, label: 'TypeIntlString' as IntlString }
|
return { _class: core.class.TypeIntlString, label: core.string.IntlString }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export function TypeBoolean (): Type<boolean> {
|
export function TypeBoolean (): Type<boolean> {
|
||||||
return { _class: core.class.TypeBoolean, label: 'TypeBoolean' as IntlString }
|
return { _class: core.class.TypeBoolean, label: core.string.Boolean }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export function TypeTimestamp (): Type<Timestamp> {
|
export function TypeTimestamp (): Type<Timestamp> {
|
||||||
return { _class: core.class.TypeTimestamp, label: 'TypeTimestamp' as IntlString }
|
return { _class: core.class.TypeTimestamp, label: core.string.Timestamp }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export function TypeDate (withTime?: boolean): TypeDateType {
|
export function TypeDate (withTime?: boolean): TypeDateType {
|
||||||
return { _class: core.class.TypeDate, label: 'TypeDate' as IntlString, withTime }
|
return { _class: core.class.TypeDate, label: core.string.Date, withTime }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export function TypeRef (_class: Ref<Class<Doc>>): RefTo<Doc> {
|
export function TypeRef (_class: Ref<Class<Doc>>): RefTo<Doc> {
|
||||||
return { _class: core.class.RefTo, to: _class, label: 'TypeRef' as IntlString }
|
return { _class: core.class.RefTo, label: core.string.Ref, to: _class }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export function Collection<T extends AttachedDoc> (clazz: Ref<Class<T>>, itemLabel?: IntlString): TypeCollection<T> {
|
export function Collection<T extends AttachedDoc> (clazz: Ref<Class<T>>, itemLabel?: IntlString): TypeCollection<T> {
|
||||||
return { _class: core.class.Collection, of: clazz, label: 'Collection' as IntlString, itemLabel }
|
return { _class: core.class.Collection, label: core.string.Collection, of: clazz, itemLabel }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export function ArrOf<T extends PropertyType | Ref<Doc>> (type: Type<T>): TypeArrOf<T> {
|
export function ArrOf<T extends PropertyType | Ref<Doc>> (type: Type<T>): TypeArrOf<T> {
|
||||||
return { _class: core.class.ArrOf, of: type, label: 'Array' as IntlString }
|
return { _class: core.class.ArrOf, label: core.string.Array, of: type }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export function Bag (): Type<Record<string, PropertyType>> {
|
export function Bag (): Type<Record<string, PropertyType>> {
|
||||||
return { _class: core.class.Bag, label: 'Bag' as IntlString }
|
return { _class: core.class.Bag, label: core.string.Bag }
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import type { Plugin, IntlString } from './platform'
|
import type { Plugin, IntlString } from './platform'
|
||||||
import { Status, Severity, unknownError } from './status'
|
import { Status, Severity, unknownError } from './status'
|
||||||
import { _parseId } from './ident'
|
import { _IdInfo, _parseId } from './ident'
|
||||||
import { setPlatformStatus } from './event'
|
import { setPlatformStatus } from './event'
|
||||||
import { IntlMessageFormat } from 'intl-messageformat'
|
import { IntlMessageFormat } from 'intl-messageformat'
|
||||||
|
|
||||||
@ -73,9 +73,8 @@ async function loadTranslationsForComponent (plugin: Plugin, locale: string): Pr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getTranslation (message: IntlString, locale: string): Promise<IntlString | Status> {
|
async function getTranslation (id: _IdInfo, locale: string): Promise<IntlString | Status | undefined> {
|
||||||
try {
|
try {
|
||||||
const id = _parseId(message)
|
|
||||||
let messages = translations.get(id.component)
|
let messages = translations.get(id.component)
|
||||||
if (messages === undefined) {
|
if (messages === undefined) {
|
||||||
messages = await loadTranslationsForComponent(id.component, locale)
|
messages = await loadTranslationsForComponent(id.component, locale)
|
||||||
@ -84,11 +83,9 @@ async function getTranslation (message: IntlString, locale: string): Promise<Int
|
|||||||
if (messages instanceof Status) {
|
if (messages instanceof Status) {
|
||||||
return messages
|
return messages
|
||||||
}
|
}
|
||||||
return (
|
return id.kind !== undefined
|
||||||
(id.kind !== undefined
|
? (messages[id.kind] as Record<string, IntlString>)?.[id.name]
|
||||||
? (messages[id.kind] as Record<string, IntlString>)?.[id.name]
|
: (messages[id.name] as IntlString)
|
||||||
: (messages[id.name] as IntlString)) ?? message
|
|
||||||
)
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const status = unknownError(err)
|
const status = unknownError(err)
|
||||||
await setPlatformStatus(status)
|
await setPlatformStatus(status)
|
||||||
@ -111,13 +108,24 @@ export async function translate<P extends Record<string, any>> (message: IntlStr
|
|||||||
}
|
}
|
||||||
return compiled.format(params)
|
return compiled.format(params)
|
||||||
} else {
|
} else {
|
||||||
const translation = await getTranslation(message, locale)
|
try {
|
||||||
if (translation instanceof Status) {
|
const id = _parseId(message)
|
||||||
cache.set(message, translation)
|
if (id.component === 'embedded') {
|
||||||
|
return id.name
|
||||||
|
}
|
||||||
|
const translation = (await getTranslation(id, locale)) ?? message
|
||||||
|
if (translation instanceof Status) {
|
||||||
|
cache.set(message, translation)
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
const compiled = new IntlMessageFormat(translation, locale)
|
||||||
|
cache.set(message, compiled)
|
||||||
|
return compiled.format(params)
|
||||||
|
} catch (err) {
|
||||||
|
const status = unknownError(err)
|
||||||
|
await setPlatformStatus(status)
|
||||||
|
cache.set(message, status)
|
||||||
return message
|
return message
|
||||||
}
|
}
|
||||||
const compiled = new IntlMessageFormat(translation, locale)
|
|
||||||
cache.set(message, compiled)
|
|
||||||
return compiled.format(params)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,11 @@ export type Namespace = Record<string, Record<string, string>>
|
|||||||
*/
|
*/
|
||||||
export const _ID_SEPARATOR = ':'
|
export const _ID_SEPARATOR = ':'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export const _EmbeddedId = 'embedded'
|
||||||
|
|
||||||
function identify (result: Record<string, any>, prefix: string, namespace: Record<string, any>): Namespace {
|
function identify (result: Record<string, any>, prefix: string, namespace: Record<string, any>): Namespace {
|
||||||
for (const key in namespace) {
|
for (const key in namespace) {
|
||||||
const value = namespace[key]
|
const value = namespace[key]
|
||||||
@ -83,6 +88,13 @@ function identify (result: Record<string, any>, prefix: string, namespace: Recor
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function getEmbeddedLabel (str: string): IntlString {
|
||||||
|
return (_EmbeddedId + _ID_SEPARATOR + _EmbeddedId + _ID_SEPARATOR + str) as IntlString
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines plugin Ids.
|
* Defines plugin Ids.
|
||||||
*
|
*
|
||||||
|
@ -35,10 +35,10 @@
|
|||||||
let container: HTMLElement
|
let container: HTMLElement
|
||||||
let opened: boolean = false
|
let opened: boolean = false
|
||||||
|
|
||||||
let selectedItem = items.find((x) => x.id === selected)
|
|
||||||
$: selectedItem = items.find((x) => x.id === selected)
|
$: selectedItem = items.find((x) => x.id === selected)
|
||||||
$: if (selected === undefined && items[0] !== undefined) {
|
$: if (selected === undefined && items[0] !== undefined) {
|
||||||
selected = items[0].id
|
selected = items[0].id
|
||||||
|
dispatch('selected', selected)
|
||||||
}
|
}
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
@ -37,14 +37,17 @@ async function createPseudoViewlet (
|
|||||||
// Check if it is attached doc and collection have title override.
|
// Check if it is attached doc and collection have title override.
|
||||||
const presenter = await getObjectPresenter(client, doc._class, { key: 'doc-presenter' })
|
const presenter = await getObjectPresenter(client, doc._class, { key: 'doc-presenter' })
|
||||||
if (presenter !== undefined) {
|
if (presenter !== undefined) {
|
||||||
|
let collection = ''
|
||||||
|
if (dtx.collectionAttribute?.label !== undefined) {
|
||||||
|
collection = await translate(dtx.collectionAttribute.label, {})
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
display,
|
display,
|
||||||
icon: docClass.icon ?? activity.icon.Activity,
|
icon: docClass.icon ?? activity.icon.Activity,
|
||||||
label: label,
|
label: label,
|
||||||
labelParams: {
|
labelParams: {
|
||||||
_class: trLabel,
|
_class: trLabel,
|
||||||
collection:
|
collection
|
||||||
dtx.collectionAttribute?.label !== undefined ? await translate(dtx.collectionAttribute?.label, {}) : ''
|
|
||||||
},
|
},
|
||||||
component: presenter.presenter,
|
component: presenter.presenter,
|
||||||
pseudo: true
|
pseudo: true
|
||||||
|
@ -32,6 +32,10 @@
|
|||||||
"General": "General",
|
"General": "General",
|
||||||
"Navigation": "Navigation",
|
"Navigation": "Navigation",
|
||||||
"Editor": "Editor",
|
"Editor": "Editor",
|
||||||
"MarkdownFormatting": "Formatting"
|
"MarkdownFormatting": "Formatting",
|
||||||
|
"Type": "Type",
|
||||||
|
"WithTime": "WithTime",
|
||||||
|
"CreatingAttribute": "Creating an attribute",
|
||||||
|
"CreatingAttributeConfirm": "Do you want to create an attribute? It will not be possible to change or delete it."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,10 @@
|
|||||||
"General": "Общее",
|
"General": "Общее",
|
||||||
"Navigation": "Навигация",
|
"Navigation": "Навигация",
|
||||||
"Editor": "Редактор",
|
"Editor": "Редактор",
|
||||||
"MarkdownFormatting": "Форматирование"
|
"MarkdownFormatting": "Форматирование",
|
||||||
|
"Type": "Тип",
|
||||||
|
"WithTime": "Со временем",
|
||||||
|
"CreatingAttribute": "Создание атрибута",
|
||||||
|
"CreatingAttributeConfirm": "Вы хотите создать атрибут? Изменить или удалить его будет невозможно"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
"svelte": "^3.47",
|
"svelte": "^3.47",
|
||||||
"@anticrm/platform": "~0.6.6",
|
"@anticrm/platform": "~0.6.6",
|
||||||
"@anticrm/contact": "~0.6.5",
|
"@anticrm/contact": "~0.6.5",
|
||||||
|
"@anticrm/model": "~0.6.0",
|
||||||
"@anticrm/panel": "~0.6.0",
|
"@anticrm/panel": "~0.6.0",
|
||||||
"@anticrm/core": "~0.6.16",
|
"@anticrm/core": "~0.6.16",
|
||||||
"@anticrm/view": "~0.6.0",
|
"@anticrm/view": "~0.6.0",
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
<!--
|
||||||
|
// 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 { Class, Doc, Ref } from '@anticrm/core'
|
||||||
|
import presentation, { AttributesBar, getClient, KeyedAttribute } from '@anticrm/presentation'
|
||||||
|
import { ActionIcon, IconAdd, Label, showPopup } from '@anticrm/ui'
|
||||||
|
import view from '@anticrm/view'
|
||||||
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
import { collectionsFilter, getFiltredKeys } from '../utils'
|
||||||
|
|
||||||
|
export let object: Doc
|
||||||
|
export let objectClass: Class<Doc>
|
||||||
|
export let to: Ref<Class<Doc>> | undefined
|
||||||
|
export let ignoreKeys: string[] = []
|
||||||
|
export let vertical: boolean
|
||||||
|
const client = getClient()
|
||||||
|
const hierarchy = client.getHierarchy()
|
||||||
|
|
||||||
|
let keys: KeyedAttribute[] = []
|
||||||
|
|
||||||
|
function updateKeys (ignoreKeys: string[]): void {
|
||||||
|
const filtredKeys = getFiltredKeys(hierarchy, objectClass._id, ignoreKeys, to)
|
||||||
|
keys = collectionsFilter(hierarchy, filtredKeys, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
$: updateKeys(ignoreKeys)
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if vertical}
|
||||||
|
<div class="flex-between text-sm mb-4">
|
||||||
|
<Label label={objectClass.label} />
|
||||||
|
<ActionIcon
|
||||||
|
label={presentation.string.Create}
|
||||||
|
icon={IconAdd}
|
||||||
|
size="small"
|
||||||
|
action={() => {
|
||||||
|
showPopup(view.component.CreateAttribute, { _class: objectClass._id }, undefined, () => {
|
||||||
|
updateKeys(ignoreKeys)
|
||||||
|
dispatch('update')
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if keys.length || !vertical}
|
||||||
|
<AttributesBar {object} {keys} {vertical} />
|
||||||
|
{/if}
|
118
plugins/view-resources/src/components/CreateAttribute.svelte
Normal file
118
plugins/view-resources/src/components/CreateAttribute.svelte
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<!--
|
||||||
|
// 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, { Class, generateId, PropertyType, Ref, Space, Type } from '@anticrm/core'
|
||||||
|
import presentation, { getClient, MessageBox } from '@anticrm/presentation'
|
||||||
|
import { AnyComponent, EditBox, DropdownLabelsIntl, Label, Component, Button, showPopup } from '@anticrm/ui'
|
||||||
|
import { DropdownIntlItem } from '@anticrm/ui/src/types'
|
||||||
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
import view from '../plugin'
|
||||||
|
import { getEmbeddedLabel } from '@anticrm/platform'
|
||||||
|
|
||||||
|
export let _class: Ref<Class<Space>>
|
||||||
|
let name: string
|
||||||
|
let type: Type<PropertyType> | undefined
|
||||||
|
let is: AnyComponent | undefined
|
||||||
|
const client = getClient()
|
||||||
|
const hierarchy = client.getHierarchy()
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
async function save (): Promise<void> {
|
||||||
|
if (type === undefined) return
|
||||||
|
showPopup(
|
||||||
|
MessageBox,
|
||||||
|
{
|
||||||
|
label: view.string.CreatingAttribute,
|
||||||
|
message: view.string.CreatingAttributeConfirm
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
async (result) => {
|
||||||
|
if (result && type !== undefined) {
|
||||||
|
await client.createDoc(core.class.Attribute, core.space.Model, {
|
||||||
|
attributeOf: _class,
|
||||||
|
name: name + generateId(),
|
||||||
|
label: getEmbeddedLabel(name),
|
||||||
|
isCustom: true,
|
||||||
|
type
|
||||||
|
})
|
||||||
|
dispatch('close')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTypes (): DropdownIntlItem[] {
|
||||||
|
const descendants = hierarchy.getDescendants(core.class.Type)
|
||||||
|
const res: DropdownIntlItem[] = []
|
||||||
|
for (const descendant of descendants) {
|
||||||
|
const _class = hierarchy.getClass(descendant)
|
||||||
|
if (_class.label !== undefined && hierarchy.hasMixin(_class, view.mixin.ObjectEditor)) {
|
||||||
|
res.push({
|
||||||
|
label: _class.label,
|
||||||
|
id: _class._id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = getTypes()
|
||||||
|
let selectedType: Ref<Class<Type<PropertyType>>>
|
||||||
|
|
||||||
|
$: selectedType && selectType(selectedType)
|
||||||
|
|
||||||
|
function selectType (type: Ref<Class<Type<PropertyType>>>): void {
|
||||||
|
const _class = hierarchy.getClass(type)
|
||||||
|
const editor = hierarchy.as(_class, view.mixin.ObjectEditor)
|
||||||
|
if (editor.editor !== undefined) {
|
||||||
|
is = editor.editor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="antiPopup w-60 p-4 flex-col">
|
||||||
|
<div class="mb-2"><EditBox bind:value={name} placeholder={core.string.Name} maxWidth="13rem" /></div>
|
||||||
|
<div class="flex-between mb-2">
|
||||||
|
<Label label={view.string.Type} />
|
||||||
|
<DropdownLabelsIntl
|
||||||
|
label={view.string.Type}
|
||||||
|
{items}
|
||||||
|
width="8rem"
|
||||||
|
bind:selected={selectedType}
|
||||||
|
on:selected={(e) => selectType(e.detail)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{#if is}
|
||||||
|
<Component
|
||||||
|
{is}
|
||||||
|
on:change={(e) => {
|
||||||
|
type = e.detail
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
<div class="mt-2">
|
||||||
|
<Button
|
||||||
|
width="100%"
|
||||||
|
disabled={type === undefined || name === undefined || name.trim().length === 0}
|
||||||
|
label={presentation.string.Create}
|
||||||
|
on:click={() => {
|
||||||
|
save()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
</style>
|
33
plugins/view-resources/src/components/DocAttributeBar.svelte
Normal file
33
plugins/view-resources/src/components/DocAttributeBar.svelte
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<!--
|
||||||
|
// 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 { Doc, Mixin } from '@anticrm/core'
|
||||||
|
import { getClient } from '@anticrm/presentation'
|
||||||
|
import ClassAttributeBar from './ClassAttributeBar.svelte'
|
||||||
|
|
||||||
|
export let object: Doc
|
||||||
|
export let mixins: Mixin<Doc>[]
|
||||||
|
export let ignoreKeys: string[]
|
||||||
|
const client = getClient()
|
||||||
|
const hierarchy = client.getHierarchy()
|
||||||
|
|
||||||
|
$: objectClass = hierarchy.getClass(object._class)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ClassAttributeBar {objectClass} {object} {ignoreKeys} to={undefined} vertical on:update />
|
||||||
|
{#each mixins as mixin}
|
||||||
|
<div class="bottom-divider mt-4 mb-2" />
|
||||||
|
<ClassAttributeBar objectClass={mixin} {object} {ignoreKeys} to={objectClass._id} vertical on:update />
|
||||||
|
{/each}
|
@ -15,7 +15,7 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { formatName } from '@anticrm/contact'
|
import contact, { formatName } from '@anticrm/contact'
|
||||||
import core, { Class, ClassifierKind, Doc, Mixin, Obj, Ref } from '@anticrm/core'
|
import { Class, ClassifierKind, Doc, Mixin, Obj, Ref } from '@anticrm/core'
|
||||||
import notification from '@anticrm/notification'
|
import notification from '@anticrm/notification'
|
||||||
import { Panel } from '@anticrm/panel'
|
import { Panel } from '@anticrm/panel'
|
||||||
import { Asset, getResource, translate } from '@anticrm/platform'
|
import { Asset, getResource, translate } from '@anticrm/platform'
|
||||||
@ -29,8 +29,9 @@
|
|||||||
import { AnyComponent, Component } from '@anticrm/ui'
|
import { AnyComponent, Component } from '@anticrm/ui'
|
||||||
import view from '@anticrm/view'
|
import view from '@anticrm/view'
|
||||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||||
import { getCollectionCounter } from '../utils'
|
import { collectionsFilter, getCollectionCounter, getFiltredKeys } from '../utils'
|
||||||
import ActionContext from './ActionContext.svelte'
|
import ActionContext from './ActionContext.svelte'
|
||||||
|
import DocAttributeBar from './DocAttributeBar.svelte'
|
||||||
import UpDownNavigator from './UpDownNavigator.svelte'
|
import UpDownNavigator from './UpDownNavigator.svelte'
|
||||||
|
|
||||||
export let _id: Ref<Doc>
|
export let _id: Ref<Doc>
|
||||||
@ -45,7 +46,6 @@
|
|||||||
const client = getClient()
|
const client = getClient()
|
||||||
const hierarchy = client.getHierarchy()
|
const hierarchy = client.getHierarchy()
|
||||||
const notificationClient = getResource(notification.function.GetNotificationClient).then((res) => res())
|
const notificationClient = getResource(notification.function.GetNotificationClient).then((res) => res())
|
||||||
const docKeys: Set<string> = new Set<string>(hierarchy.getAllAttributes(core.class.AttachedDoc).keys())
|
|
||||||
|
|
||||||
$: read(_id)
|
$: read(_id)
|
||||||
function read (_id: Ref<Doc>) {
|
function read (_id: Ref<Doc>) {
|
||||||
@ -90,35 +90,21 @@
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterKeys (keys: KeyedAttribute[], ignoreKeys: string[]): KeyedAttribute[] {
|
|
||||||
keys = keys.filter((k) => !docKeys.has(k.key))
|
|
||||||
keys = keys.filter((k) => !ignoreKeys.includes(k.key))
|
|
||||||
return keys
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFiltredKeys (objectClass: Ref<Class<Doc>>, ignoreKeys: string[], to?: Ref<Class<Doc>>): KeyedAttribute[] {
|
|
||||||
const keys = [...hierarchy.getAllAttributes(objectClass, to).entries()]
|
|
||||||
.filter(([, value]) => value.hidden !== true)
|
|
||||||
.map(([key, attr]) => ({ key, attr }))
|
|
||||||
|
|
||||||
return filterKeys(keys, ignoreKeys)
|
|
||||||
}
|
|
||||||
|
|
||||||
let ignoreKeys: string[] = []
|
let ignoreKeys: string[] = []
|
||||||
let ignoreMixins: Set<Ref<Mixin<Doc>>> = new Set<Ref<Mixin<Doc>>>()
|
let ignoreMixins: Set<Ref<Mixin<Doc>>> = new Set<Ref<Mixin<Doc>>>()
|
||||||
|
|
||||||
async function updateKeys (): Promise<void> {
|
async function updateKeys (): Promise<void> {
|
||||||
const keysMap = new Map(getFiltredKeys(object._class, ignoreKeys).map((p) => [p.attr._id, p]))
|
const keysMap = new Map(getFiltredKeys(hierarchy, object._class, ignoreKeys).map((p) => [p.attr._id, p]))
|
||||||
for (const m of mixins) {
|
for (const m of mixins) {
|
||||||
const mkeys = getFiltredKeys(m._id, ignoreKeys)
|
const mkeys = getFiltredKeys(hierarchy, m._id, ignoreKeys)
|
||||||
for (const key of mkeys) {
|
for (const key of mkeys) {
|
||||||
keysMap.set(key.attr._id, key)
|
keysMap.set(key.attr._id, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const filtredKeys = Array.from(keysMap.values())
|
const filtredKeys = Array.from(keysMap.values())
|
||||||
keys = collectionsFilter(filtredKeys, false)
|
keys = collectionsFilter(hierarchy, filtredKeys, false)
|
||||||
|
|
||||||
const collectionKeys = collectionsFilter(filtredKeys, true)
|
const collectionKeys = collectionsFilter(hierarchy, filtredKeys, true)
|
||||||
const editors: { key: KeyedAttribute; editor: AnyComponent }[] = []
|
const editors: { key: KeyedAttribute; editor: AnyComponent }[] = []
|
||||||
for (const k of collectionKeys) {
|
for (const k of collectionKeys) {
|
||||||
const editor = await getCollectionEditor(k)
|
const editor = await getCollectionEditor(k)
|
||||||
@ -127,18 +113,6 @@
|
|||||||
collectionEditors = editors
|
collectionEditors = editors
|
||||||
}
|
}
|
||||||
|
|
||||||
function collectionsFilter (keys: KeyedAttribute[], get: boolean): KeyedAttribute[] {
|
|
||||||
const result: KeyedAttribute[] = []
|
|
||||||
for (const key of keys) {
|
|
||||||
if (isCollectionAttr(key) === get) result.push(key)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
function isCollectionAttr (key: KeyedAttribute): boolean {
|
|
||||||
return hierarchy.isDerived(key.attr.type._class, core.class.Collection)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getEditor (_class: Ref<Class<Doc>>): Promise<AnyComponent> {
|
async function getEditor (_class: Ref<Class<Doc>>): Promise<AnyComponent> {
|
||||||
const clazz = hierarchy.getClass(_class)
|
const clazz = hierarchy.getClass(_class)
|
||||||
const editorMixin = hierarchy.as(clazz, view.mixin.ObjectEditor)
|
const editorMixin = hierarchy.as(clazz, view.mixin.ObjectEditor)
|
||||||
@ -269,8 +243,10 @@
|
|||||||
{#if !headerLoading}
|
{#if !headerLoading}
|
||||||
{#if headerEditor !== undefined}
|
{#if headerEditor !== undefined}
|
||||||
<Component is={headerEditor} props={{ object, keys, vertical: dir === 'column' }} />
|
<Component is={headerEditor} props={{ object, keys, vertical: dir === 'column' }} />
|
||||||
|
{:else if dir === 'column'}
|
||||||
|
<DocAttributeBar {object} {mixins} {ignoreKeys} on:update={updateKeys} />
|
||||||
{:else}
|
{:else}
|
||||||
<AttributesBar {object} {keys} vertical={dir === 'column'} />
|
<AttributesBar {object} {keys} />
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
<!--
|
||||||
|
// 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 { TypeBoolean } from '@anticrm/model'
|
||||||
|
import { onMount } from 'svelte'
|
||||||
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
dispatch('change', TypeBoolean())
|
||||||
|
})
|
||||||
|
</script>
|
@ -0,0 +1,40 @@
|
|||||||
|
<!--
|
||||||
|
// 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 { TypeDate } from '@anticrm/model'
|
||||||
|
import { Label } from '@anticrm/ui'
|
||||||
|
import { onMount } from 'svelte'
|
||||||
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
import view from '../../plugin'
|
||||||
|
import BooleanEditor from '../BooleanEditor.svelte'
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
let withTime: boolean = false
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
dispatch('change', TypeDate(withTime))
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex-between">
|
||||||
|
<Label label={view.string.WithTime} />
|
||||||
|
<BooleanEditor
|
||||||
|
bind:value={withTime}
|
||||||
|
onChange={(e) => {
|
||||||
|
dispatch('change', TypeDate(e))
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
@ -0,0 +1,25 @@
|
|||||||
|
<!--
|
||||||
|
// 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 { TypeNumber } from '@anticrm/model'
|
||||||
|
import { onMount } from 'svelte'
|
||||||
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
dispatch('change', TypeNumber())
|
||||||
|
})
|
||||||
|
</script>
|
@ -0,0 +1,25 @@
|
|||||||
|
<!--
|
||||||
|
// 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 { TypeString } from '@anticrm/model'
|
||||||
|
import { onMount } from 'svelte'
|
||||||
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
dispatch('change', TypeString())
|
||||||
|
})
|
||||||
|
</script>
|
@ -38,6 +38,11 @@ import UpDownNavigator from './components/UpDownNavigator.svelte'
|
|||||||
import GithubPresenter from './components/linkPresenters/GithubPresenter.svelte'
|
import GithubPresenter from './components/linkPresenters/GithubPresenter.svelte'
|
||||||
import YoutubePresenter from './components/linkPresenters/YoutubePresenter.svelte'
|
import YoutubePresenter from './components/linkPresenters/YoutubePresenter.svelte'
|
||||||
import ActionsPopup from './components/ActionsPopup.svelte'
|
import ActionsPopup from './components/ActionsPopup.svelte'
|
||||||
|
import CreateAttribute from './components/CreateAttribute.svelte'
|
||||||
|
import StringTypeEditor from './components/typeEditors/StringTypeEditor.svelte'
|
||||||
|
import BooleanTypeEditor from './components/typeEditors/BooleanTypeEditor.svelte'
|
||||||
|
import DateTypeEditor from './components/typeEditors/DateTypeEditor.svelte'
|
||||||
|
import NumberTypeEditor from './components/typeEditors/NumberTypeEditor.svelte'
|
||||||
|
|
||||||
export { getActions } from './actions'
|
export { getActions } from './actions'
|
||||||
export { default as ActionContext } from './components/ActionContext.svelte'
|
export { default as ActionContext } from './components/ActionContext.svelte'
|
||||||
@ -53,6 +58,11 @@ export { Table, TableView, EditDoc, ColorsPopup, Menu, SpacePresenter, UpDownNav
|
|||||||
export default async (): Promise<Resources> => ({
|
export default async (): Promise<Resources> => ({
|
||||||
actionImpl: actionImpl,
|
actionImpl: actionImpl,
|
||||||
component: {
|
component: {
|
||||||
|
CreateAttribute,
|
||||||
|
StringTypeEditor,
|
||||||
|
BooleanTypeEditor,
|
||||||
|
NumberTypeEditor,
|
||||||
|
DateTypeEditor,
|
||||||
SpacePresenter,
|
SpacePresenter,
|
||||||
StringEditor,
|
StringEditor,
|
||||||
StringPresenter,
|
StringPresenter,
|
||||||
|
@ -35,6 +35,10 @@ export default mergeIds(viewId, view, {
|
|||||||
DeleteObjectConfirm: '' as IntlString,
|
DeleteObjectConfirm: '' as IntlString,
|
||||||
Assignees: '' as IntlString,
|
Assignees: '' as IntlString,
|
||||||
Labels: '' as IntlString,
|
Labels: '' as IntlString,
|
||||||
ActionPlaceholder: '' as IntlString
|
WithTime: '' as IntlString,
|
||||||
|
Type: '' as IntlString,
|
||||||
|
ActionPlaceholder: '' as IntlString,
|
||||||
|
CreatingAttribute: '' as IntlString,
|
||||||
|
CreatingAttributeConfirm: '' as IntlString
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -283,3 +283,35 @@ export function getCollectionCounter (hierarchy: Hierarchy, object: Doc, key: Ke
|
|||||||
}
|
}
|
||||||
return (object as any)[key.key] ?? 0
|
return (object as any)[key.key] ?? 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function filterKeys (hierarchy: Hierarchy, keys: KeyedAttribute[], ignoreKeys: string[]): KeyedAttribute[] {
|
||||||
|
const docKeys: Set<string> = new Set<string>(hierarchy.getAllAttributes(core.class.AttachedDoc).keys())
|
||||||
|
keys = keys.filter((k) => !docKeys.has(k.key))
|
||||||
|
keys = keys.filter((k) => !ignoreKeys.includes(k.key))
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFiltredKeys (
|
||||||
|
hierarchy: Hierarchy,
|
||||||
|
objectClass: Ref<Class<Doc>>,
|
||||||
|
ignoreKeys: string[],
|
||||||
|
to?: Ref<Class<Doc>>
|
||||||
|
): KeyedAttribute[] {
|
||||||
|
const keys = [...hierarchy.getAllAttributes(objectClass, to).entries()]
|
||||||
|
.filter(([, value]) => value.hidden !== true)
|
||||||
|
.map(([key, attr]) => ({ key, attr }))
|
||||||
|
|
||||||
|
return filterKeys(hierarchy, keys, ignoreKeys)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function collectionsFilter (hierarchy: Hierarchy, keys: KeyedAttribute[], get: boolean): KeyedAttribute[] {
|
||||||
|
const result: KeyedAttribute[] = []
|
||||||
|
for (const key of keys) {
|
||||||
|
if (isCollectionAttr(hierarchy, key) === get) result.push(key)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCollectionAttr (hierarchy: Hierarchy, key: KeyedAttribute): boolean {
|
||||||
|
return hierarchy.isDerived(key.attr.type._class, core.class.Collection)
|
||||||
|
}
|
||||||
|
@ -311,6 +311,7 @@ const view = plugin(viewId, {
|
|||||||
component: {
|
component: {
|
||||||
ObjectPresenter: '' as AnyComponent,
|
ObjectPresenter: '' as AnyComponent,
|
||||||
EditDoc: '' as AnyComponent,
|
EditDoc: '' as AnyComponent,
|
||||||
|
CreateAttribute: '' as AnyComponent,
|
||||||
SpacePresenter: '' as AnyComponent
|
SpacePresenter: '' as AnyComponent
|
||||||
},
|
},
|
||||||
icon: {
|
icon: {
|
||||||
|
Loading…
Reference in New Issue
Block a user