Compare commits

...
19 changed files with 522 additions and 92 deletions
@@ -5,7 +5,7 @@ import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
@customElement('example-dashboard-with-tree')
export class ExampleDashboardWithTree extends UmbElementMixin(LitElement) {
override render() {
return html`<uui-box><umb-tree alias=${EXAMPLE_TREE_ALIAS}></umb-tree></uui-box>`;
return html`<uui-box><umb-tree alias=${EXAMPLE_TREE_ALIAS} .props=${{ hideTreeRoot: true }}></umb-tree></uui-box>`;
}
static override styles = [
@@ -9,7 +9,8 @@ import type { UmbTreeExpansionModel } from '../expansion-manager/types.js';
import type { UmbTreeItemModel, UmbTreeRootModel, UmbTreeStartNode } from '../types.js';
import type { UmbTreeRepository } from '../data/index.js';
import type { UmbTreeRootItemsRequestArgs } from '../data/types.js';
import { UmbBooleanState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
import { UmbTreeViewManager } from '../view/tree-view.manager.js';
import { UmbBooleanState, UmbObjectState, UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
import { UmbDeprecation, UmbSelectionManager, debounce } from '@umbraco-cms/backoffice/utils';
import { UmbExtensionApiInitializer } from '@umbraco-cms/backoffice/extension-api';
@@ -38,6 +39,10 @@ export class UmbDefaultTreeContext<
#expandTreeRoot = new UmbBooleanState(undefined);
public readonly expandTreeRoot = this.#expandTreeRoot.asObservable();
#treeViewManager = new UmbTreeViewManager(this);
public readonly views = this.#treeViewManager.views;
public readonly currentView = this.#treeViewManager.currentView;
#treeItemChildrenManager = new UmbTreeItemChildrenManager<TreeItemType, TreeRootType, RequestArgsType>(this);
public readonly rootItems = this.#treeItemChildrenManager.children;
public readonly hasChildren = this.#treeItemChildrenManager.hasChildren;
@@ -56,6 +61,9 @@ export class UmbDefaultTreeContext<
readonly activeManager = new UmbTreeItemActiveManager(this);
#items = new UmbArrayState<TreeItemType | TreeRootType>([], (x) => x.unique);
public readonly items = this.#items.asObservable();
#manifest?: ManifestTree;
#repository?: UmbTreeRepository<TreeItemType, TreeRootType>;
@@ -176,6 +184,7 @@ export class UmbDefaultTreeContext<
if (data) {
this.#treeRoot.setValue(data);
this.#items.setValue([data]);
this.#treeItemChildrenManager.setTreeItem(data);
this.#treeItemExpansionManager.setTreeItem(data);
this.pagination.setTotalItems(1);
@@ -188,6 +197,14 @@ export class UmbDefaultTreeContext<
}
}
setCurrentView(viewUnique: string) {
this.#treeViewManager.setCurrentView(viewUnique);
}
getCurrentView() {
return this.#treeViewManager.getCurrentView();
}
/**
* Sets the hideTreeRoot config
* @param {boolean} hideTreeRoot - Whether to hide the tree root
@@ -198,6 +215,12 @@ export class UmbDefaultTreeContext<
// we need to reset the tree if this config changes
this.#clearTree();
this.loadTree();
if (hideTreeRoot) {
this.observe(this.#treeItemChildrenManager.children, (children) => {
this.#items.setValue(children);
});
}
}
/**
@@ -6,10 +6,13 @@ import type {
UmbTreeStartNode,
} from '../types.js';
import type { UmbTreeExpansionModel } from '../expansion-manager/types.js';
import type { UmbTreeViewItemModel } from '../view/types.js';
import type { ManifestTreeView } from '../view/extension/tree-view.extension.js';
import type { UmbDefaultTreeContext } from './default-tree.context.js';
import { css, customElement, html, nothing, property, repeat, state } from '@umbraco-cms/backoffice/external/lit';
import { customElement, html, nothing, property, repeat, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit';
import { getItemFallbackIcon } from '@umbraco-cms/backoffice/entity-item';
@customElement('umb-default-tree')
export class UmbDefaultTreeElement extends UmbLitElement {
@@ -60,41 +63,14 @@ export class UmbDefaultTreeElement extends UmbLitElement {
expansion: UmbTreeExpansionModel = [];
@state()
private _rootItems: UmbTreeItemModel[] = [];
private _views?: Array<UmbTreeViewItemModel>;
@state()
private _treeRoot?: UmbTreeRootModel;
@state()
private _currentPage = 1;
@state()
private _hasPreviousItems = false;
@state()
private _hasNextItems = false;
@state()
private _isLoadingPrevChildren = false;
@state()
private _isLoadingNextChildren = false;
private _currentView?: UmbTreeViewItemModel;
#observeData() {
this.observe(this._api?.treeRoot, (treeRoot) => (this._treeRoot = treeRoot));
this.observe(this._api?.rootItems, (rootItems) => (this._rootItems = rootItems ?? []));
this.observe(this._api?.pagination.currentPage, (value) => (this._currentPage = value ?? 1));
this.observe(this._api?.isLoadingPrevChildren, (value) => (this._isLoadingPrevChildren = value ?? false));
this.observe(this._api?.isLoadingNextChildren, (value) => (this._isLoadingNextChildren = value ?? false));
this.observe(
this._api?.targetPagination?.totalPrevItems,
(value) => (this._hasPreviousItems = value ? value > 0 : false),
);
this.observe(
this._api?.targetPagination?.totalNextItems,
(value) => (this._hasNextItems = value ? value > 0 : false),
);
this.observe(this._api?.views, (views) => (this._views = views));
this.observe(this._api?.currentView, (currentView) => (this._currentView = currentView));
}
protected override async updated(
@@ -148,74 +124,46 @@ export class UmbDefaultTreeElement extends UmbLitElement {
return this._api?.expansion.getExpansion();
}
#onLoadPrev(event: any) {
event.stopPropagation();
this._api?.loadPrevItems?.();
}
#onLoadNext(event: any) {
event.stopPropagation();
const next = (this._currentPage = this._currentPage + 1);
this._api?.pagination.setCurrentPageNumber(next);
#onViewClick(view: UmbTreeViewItemModel) {
this._api?.setCurrentView(view.unique);
}
override render() {
return html` ${this.#renderTreeRoot()} ${this.#renderRootItems()}`;
}
#renderTreeRoot() {
if (this.hideTreeRoot || this._treeRoot === undefined) return nothing;
return html`
<umb-tree-item
.entityType=${this._treeRoot.entityType}
.props=${{
hideActions: this.hideTreeItemActions,
item: this._treeRoot,
isMenu: this.isMenu,
}}></umb-tree-item>
<div>${this.#renderViewsBundle()}</div>
<umb-extension-slot
type="treeView"
.filter=${(manifest: ManifestTreeView) => manifest.alias === this._currentView?.unique}></umb-extension-slot>
`;
}
#renderRootItems() {
// only show the root items directly if the tree root is hidden
if (this.hideTreeRoot === true) {
return html`
${this.#renderLoadPrevButton()}
${repeat(
this._rootItems,
(item, index) => item.name + '___' + index,
(item) => html`
<umb-tree-item
.entityType=${item.entityType}
.props=${{ hideActions: this.hideTreeItemActions, item, isMenu: this.isMenu }}></umb-tree-item>
`,
)}
${this.#renderLoadNextButton()}
`;
} else {
return nothing;
}
#renderViewsBundle() {
if (!this._views || this._views.length === 0 || !this._currentView) return nothing;
return html`<uui-button compact popovertarget="tree-view-popover" label="status">
<umb-icon name=${this._currentView.icon ?? getItemFallbackIcon()}></umb-icon>
</uui-button>
<uui-popover-container id="tree-view-popover" placement="bottom-end">
<umb-popover-layout>
<div class="filter-dropdown">
${repeat(
this._views,
(view) => view.unique,
(view) => this.#renderViewItem(view),
)}
</div>
</umb-popover-layout>
</uui-popover-container>`;
}
#renderLoadPrevButton() {
if (!this._hasPreviousItems) return nothing;
return html`<umb-tree-load-prev-button
@click=${this.#onLoadPrev}
.loading=${this._isLoadingPrevChildren}></umb-tree-load-prev-button>`;
#renderViewItem(view: UmbTreeViewItemModel) {
return html`
<uui-menu-item @click-label=${() => this.#onViewClick(view)} ?active=${view.unique === this._currentView?.unique}>
<umb-icon slot="icon" name=${view.icon ?? getItemFallbackIcon()}></umb-icon>
</uui-menu-item>
`;
}
#renderLoadNextButton() {
if (!this._hasNextItems) return nothing;
return html`<umb-tree-load-more-button
@click=${this.#onLoadNext}
.loading=${this._isLoadingNextChildren}></umb-tree-load-more-button> `;
}
static override styles = css`
#load-more {
width: 100%;
}
`;
}
export default UmbDefaultTreeElement;
@@ -4,6 +4,7 @@ import { manifests as entityActionManifests } from './entity-actions/manifests.j
import { manifests as folderManifests } from './folder/manifests.js';
import { manifests as treeMenuItemManifests } from './tree-menu-item/manifests.js';
import { manifests as treePickerManifests } from './tree-picker-modal/manifests.js';
import { manifests as viewManifests } from './view/manifests.js';
import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry';
export const manifests: Array<UmbExtensionManifest | UmbExtensionManifestKind> = [
@@ -13,4 +14,5 @@ export const manifests: Array<UmbExtensionManifest | UmbExtensionManifestKind> =
...folderManifests,
...treeMenuItemManifests,
...treePickerManifests,
...viewManifests,
];
@@ -0,0 +1,17 @@
import type { ManifestElement, ManifestWithDynamicConditions } from '@umbraco-cms/backoffice/extension-api';
export interface ManifestTreeView extends ManifestElement, ManifestWithDynamicConditions<UmbExtensionConditionConfig> {
type: 'treeView';
meta: MetaTreeView;
}
export interface MetaTreeView {
label: string;
icon: string;
}
declare global {
interface UmbExtensionManifestMap {
UmbTreeView: ManifestTreeView;
}
}
@@ -0,0 +1 @@
export type * from './tree-view.extension.js';
@@ -0,0 +1,22 @@
export const manifests: Array<UmbExtensionManifest> = [
{
type: 'treeView',
alias: 'Umb.TreeView.Menu',
name: 'Menu Tree View',
element: () => import('./tree-menu-view/tree-menu-view.element.js'),
meta: {
label: 'Menu',
icon: 'icon-blockquote',
},
},
{
type: 'treeView',
alias: 'Umb.TreeView.Card',
name: 'Card Tree View',
element: () => import('./tree-card-view/tree-card-view.element.js'),
meta: {
label: 'Card',
icon: 'icon-grid-2',
},
},
];
@@ -0,0 +1,71 @@
import { UMB_TREE_CONTEXT } from '../../constants.js';
import type { UmbTreeItemModel, UmbTreeRootModel } from '../../types.js';
import { css, customElement, html, ifDefined, nothing, repeat, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
@customElement('umb-tree-card-view')
export class UmbTreeCardViewElement extends UmbLitElement {
@state()
private _items: Array<UmbTreeItemModel | UmbTreeRootModel> | undefined;
#treeContext?: typeof UMB_TREE_CONTEXT.TYPE;
constructor() {
super();
this.consumeContext(UMB_TREE_CONTEXT, (context) => {
this.#treeContext = context;
this.#observeData();
});
}
#observeData() {
this.observe(this.#treeContext?.items, (items) => (this._items = items));
}
#expand() {
console.log('expand');
}
override render() {
return html`${this.#renderItems()}`;
}
#renderItems() {
if (!this._items) return nothing;
console.log(this._items);
return html`
<div id="tree-card-grid">
${repeat(
this._items,
(item, index) => item.name + '___' + index,
(item) =>
html` <uui-card
><umb-icon name=${ifDefined(item.icon)}></umb-icon>
<h3>${item.name}</h3>
${item.hasChildren ? html`<uui-button @click=${this.#expand}>Expand</uui-button>` : nothing}
</uui-card>`,
)}
</div>
`;
}
static override styles = css`
#tree-card-grid {
display: grid;
gap: var(--uui-size-space-5);
grid-template-columns: repeat(auto-fill, minmax(var(--umb-card-medium-min-width), 1fr));
grid-auto-rows: var(--umb-card-medium-min-width);
padding-bottom: 5px;
}
`;
}
export { UmbTreeCardViewElement as element };
declare global {
interface HTMLElementTagNameMap {
'umb-tree-card-view': UmbTreeCardViewElement;
}
}
@@ -0,0 +1,115 @@
import { UMB_TREE_CONTEXT } from '../../tree.context.token.js';
import type { UmbTreeItemModel, UmbTreeRootModel } from '../../types.js';
import { customElement, html, repeat, state, nothing, css } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
@customElement('umb-tree-menu-view')
export class UmbTreeMenuViewElement extends UmbLitElement {
@state()
private _items: Array<UmbTreeItemModel | UmbTreeRootModel> | undefined;
@state()
private _currentPage = 1;
@state()
private _hasPreviousItems = false;
@state()
private _hasNextItems = false;
@state()
private _isLoadingPrevChildren = false;
@state()
private _isLoadingNextChildren = false;
#treeContext?: typeof UMB_TREE_CONTEXT.TYPE;
constructor() {
super();
this.consumeContext(UMB_TREE_CONTEXT, (context) => {
this.#treeContext = context;
this.#observeData();
});
}
#observeData() {
this.observe(this.#treeContext?.items, (items) => (this._items = items));
this.observe(this.#treeContext?.pagination.currentPage, (value) => (this._currentPage = value ?? 1));
this.observe(this.#treeContext?.isLoadingPrevChildren, (value) => (this._isLoadingPrevChildren = value ?? false));
this.observe(this.#treeContext?.isLoadingNextChildren, (value) => (this._isLoadingNextChildren = value ?? false));
this.observe(
this.#treeContext?.targetPagination?.totalPrevItems,
(value) => (this._hasPreviousItems = value ? value > 0 : false),
);
this.observe(
this.#treeContext?.targetPagination?.totalNextItems,
(value) => (this._hasNextItems = value ? value > 0 : false),
);
}
#onLoadPrev(event: any) {
event.stopPropagation();
this.#treeContext?.loadPrevItems?.();
}
#onLoadNext(event: any) {
event.stopPropagation();
const next = (this._currentPage = this._currentPage + 1);
this.#treeContext?.pagination.setCurrentPageNumber(next);
}
override render() {
return html`${this.#renderItems()}`;
}
#renderItems() {
if (!this._items) return nothing;
return html`
${this.#renderLoadPrevButton()}
${repeat(
this._items,
(item, index) => item.name + '___' + index,
(item) => html`
<umb-tree-item
.entityType=${item.entityType}
.props=${{ hideActions: false, item, isMenu: false }}></umb-tree-item>
`,
)}
${this.#renderLoadNextButton()}
`;
}
// .props=${{ hideActions: this.hideTreeItemActions, item, isMenu: this.isMenu }}
#renderLoadPrevButton() {
if (!this._hasPreviousItems) return nothing;
return html`<umb-tree-load-prev-button
@click=${this.#onLoadPrev}
.loading=${this._isLoadingPrevChildren}></umb-tree-load-prev-button>`;
}
#renderLoadNextButton() {
if (!this._hasNextItems) return nothing;
return html`<umb-tree-load-more-button
@click=${this.#onLoadNext}
.loading=${this._isLoadingNextChildren}></umb-tree-load-more-button> `;
}
static override styles = css`
#load-more {
width: 100%;
}
`;
}
export { UmbTreeMenuViewElement as element };
declare global {
interface HTMLElementTagNameMap {
'umb-tree-menu-view': UmbTreeMenuViewElement;
}
}
@@ -0,0 +1,46 @@
import type { UmbTreeViewItemModel } from './types.js';
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import { UmbExtensionsManifestInitializer } from '@umbraco-cms/backoffice/extension-api';
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import { UmbArrayState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
export class UmbTreeViewManager extends UmbControllerBase {
#views = new UmbArrayState<UmbTreeViewItemModel>([], (x) => x.unique);
public readonly views = this.#views.asObservable();
#currentView = new UmbObjectState<UmbTreeViewItemModel | undefined>(undefined);
public readonly currentView = this.#currentView.asObservable();
constructor(host: UmbControllerHost) {
super(host);
this.#observeViews();
}
public setCurrentView(unique: string) {
const view = this.#views.getValue().find((view) => view.unique === unique);
this.#currentView.setValue(view);
}
public getCurrentView() {
return this.#currentView.getValue();
}
#observeViews() {
return new UmbExtensionsManifestInitializer(this, umbExtensionsRegistry, 'treeView', null, (views) => {
const manifests = views.map((view) => view.manifest);
const items = manifests.map((view) => ({
unique: view.alias,
entityType: 'tree-view',
name: view.meta.label,
icon: view.meta.icon,
}));
this.#views.setValue(items);
// TODO: change this
this.setCurrentView(items[0]?.unique ?? '');
});
}
}
@@ -0,0 +1,5 @@
import type { UmbItemModel } from '@umbraco-cms/backoffice/entity-item';
export interface UmbTreeViewItemModel extends UmbItemModel {
unique: string;
}
@@ -0,0 +1,18 @@
import type { ManifestElement } from '@umbraco-cms/backoffice/extension-api';
export interface ManifestEntityDataPickerDisplayMode extends ManifestElement<any> {
type: 'entityDataPickerDisplayMode';
meta?: MetaEntityDataPickerDisplayMode;
}
export interface MetaEntityDataPickerDisplayMode {
label: string;
description?: string;
icon?: string;
}
declare global {
interface UmbExtensionManifestMap {
UmbEntityDataPickerDisplayMode: ManifestEntityDataPickerDisplayMode;
}
}
@@ -0,0 +1 @@
export type * from './entity-data-picker-display-mode.extension.js';
@@ -0,0 +1,3 @@
import { manifests as propertyEditorUiManifests } from './property-editor/manifests.js';
export const manifests: Array<UmbExtensionManifest> = [...propertyEditorUiManifests];
@@ -0,0 +1,99 @@
import type { ManifestEntityDataPickerDisplayMode } from '../extension/types.js';
import type { UmbEntityDataPickerDisplayModePropertyEditorValue } from './types.js';
import { customElement, html, nothing, property, repeat, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/property-editor';
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
import type { UUIComboboxElement } from '@umbraco-cms/backoffice/external/uui';
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
@customElement('umb-entity-data-picker-display-mode-property-editor-ui')
export class UmbEntityDataPickerDisplayModePropertyEditorUIElement
extends UmbFormControlMixin<UmbEntityDataPickerDisplayModePropertyEditorValue | undefined, typeof UmbLitElement>(
UmbLitElement,
undefined,
)
implements UmbPropertyEditorUiElement
{
/**
* Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content.
* @type {boolean}
* @attr
* @default false
*/
@property({ type: Boolean, reflect: true })
readonly = false;
@state()
private _displayModes: Array<ManifestEntityDataPickerDisplayMode> = [];
constructor() {
super();
this.#observeDisplayModes();
}
#observeDisplayModes() {
this.observe(
umbExtensionsRegistry.byType('entityDataPickerDisplayMode'),
(extensions) => {
this._displayModes = extensions;
},
'observeDisplayModes',
);
}
override focus() {
return this.shadowRoot?.querySelector<UUIComboboxElement>('uui-combobox')?.focus();
}
#onChange(event: CustomEvent & { target: UUIComboboxElement }) {
const selected = event.target.value;
// Ensure the value is of the correct type before setting it.
if (typeof selected === 'string') {
this.value = { ids: [selected] };
this.dispatchEvent(new UmbChangeEvent());
} else {
throw new Error('Selected is not of type string. Cannot set property value.');
}
}
override render() {
return html` <uui-combobox value=${this.value?.ids[0] || ''} @change=${this.#onChange}>
<uui-combobox-list
>${repeat(
this._displayModes ?? [],
(property) => property.alias,
(property) => this.#renderOption(property),
)}</uui-combobox-list
>
</uui-combobox>`;
}
#renderOption(item: ManifestEntityDataPickerDisplayMode) {
const label = item.meta?.label || item.name;
const description = item.meta?.description;
return html`
<uui-combobox-list-option
.displayValue=${label}
style="display: flex; gap: 9px; align-items: center; padding: var(--uui-size-3)"
.value=${item.alias}>
<uui-icon name="icon-wrench"></uui-icon>
<div style="display: flex; flex-direction: column">
<b>${label}</b>
${description ? html`<small>${description}</small>` : nothing}
</div>
</uui-combobox-list-option>
`;
}
}
export { UmbEntityDataPickerDisplayModePropertyEditorUIElement as element };
declare global {
interface HTMLElementTagNameMap {
'umb-entity-data-picker-display-mode-property-editor-ui': UmbEntityDataPickerDisplayModePropertyEditorUIElement;
}
}
@@ -0,0 +1,14 @@
export const manifests: Array<UmbExtensionManifest> = [
{
type: 'propertyEditorUi',
alias: 'Umb.PropertyEditorUi.EntityDataPicker.DisplayMode',
name: 'Entity Data Picker Display Mode Property Editor UI',
element: () => import('./entity-data-picker-display-mode-property-editor-ui.element.js'),
meta: {
label: 'Entity Data Picker Display Mode',
icon: 'icon-page-add',
group: 'pickers',
supportsReadOnly: true,
},
},
];
@@ -0,0 +1,3 @@
export type UmbEntityDataPickerDisplayModePropertyEditorValue = {
ids: Array<string>;
};
@@ -3,6 +3,7 @@ import { manifests as pickerItemManifests } from './picker-item/manifests.js';
import { manifests as pickerSearchManifests } from './picker-search/manifests.js';
import { manifests as pickerTreeManifests } from './picker-tree/manifests.js';
import { manifests as propertyEditorManifests } from './property-editor/manifests.js';
import { manifests as displayModeManifests } from './display-mode/manifests.js';
export const manifests: Array<UmbExtensionManifest> = [
...pickerCollectionMenuManifests,
@@ -10,4 +11,5 @@ export const manifests: Array<UmbExtensionManifest> = [
...pickerSearchManifests,
...pickerTreeManifests,
...propertyEditorManifests,
...displayModeManifests,
];
@@ -17,7 +17,47 @@ export const manifests: Array<UmbExtensionManifest> = [
enabled: true,
forDataSourceTypes: [UMB_PICKER_DATA_SOURCE_TYPE],
},
settings: {
properties: [
{
alias: 'displayMode',
label: 'Display Mode',
propertyEditorUiAlias: 'Umb.PropertyEditorUi.EntityDataPicker.DisplayMode',
weight: 100,
},
],
},
},
},
...schemaManifests,
{
type: 'entityDataPickerDisplayMode',
alias: 'Umb.EntityDataPickerDisplayMode.MenuItem',
name: 'Menu Item Entity Data Picker Display Mode',
weight: 900,
meta: {
label: 'Menu Item',
description: 'Description of Menu Item display mode.',
},
},
{
type: 'entityDataPickerDisplayMode',
alias: 'Umb.EntityDataPickerDisplayMode.RefItem',
name: 'Ref Item Entity Data Picker Display Mode',
weight: 800,
meta: {
label: 'Reference Item',
description: 'Description of Reference Item display mode.',
},
},
{
type: 'entityDataPickerDisplayMode',
alias: 'Umb.EntityDataPickerDisplayMode.Card',
name: 'Card Entity Data Picker Display Mode',
weight: 700,
meta: {
label: 'Card',
description: 'Description of Card display mode.',
},
},
];