Collection menu item extension point (#20506)

* add extension option for collection menu item

* Add collection menu module export

* remove unused css

* register user collection menu item

* register user collection menu

* use collection modal for user picker

* Delete user-picker-modal.element.ts

* Update manifests.ts

* explicit exports to avoid name collision

* hack to avoid circular dependency

* fix lint errors

* fix missing const export

* Update collection-menu-item.element.ts
This commit is contained in:
Mads Rasmussen
2025-10-16 13:57:15 +02:00
committed by GitHub
parent 498754e170
commit 8beb7f2acc
26 changed files with 512 additions and 120 deletions

View File

@@ -1,15 +1,22 @@
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import type { UmbCollectionFilterModel, UmbCollectionItemModel } from '@umbraco-cms/backoffice/collection';
import type { UmbItemModel } from '@umbraco-cms/backoffice/entity-item';
import type {
UmbPickerCollectionDataSource,
UmbPickerSearchableDataSource,
} from '@umbraco-cms/backoffice/picker-data-source';
import type { UmbSearchRequestArgs } from '@umbraco-cms/backoffice/search';
interface ExampleCollectionItemModel extends UmbCollectionItemModel {
isPickable: boolean;
}
export class ExampleCustomPickerCollectionPropertyEditorDataSource
extends UmbControllerBase
implements UmbPickerCollectionDataSource, UmbPickerSearchableDataSource
implements UmbPickerCollectionDataSource<ExampleCollectionItemModel>, UmbPickerSearchableDataSource
{
collectionPickableFilter = (item: ExampleCollectionItemModel) => item.isPickable;
async requestCollection(args: UmbCollectionFilterModel) {
// TODO: use args to filter/paginate etc
console.log(args);
@@ -41,35 +48,40 @@ export class ExampleCustomPickerCollectionPropertyEditorDataSource
export { ExampleCustomPickerCollectionPropertyEditorDataSource as api };
const customItems: Array<UmbCollectionItemModel> = [
const customItems: Array<ExampleCollectionItemModel> = [
{
unique: '1',
entityType: 'example',
name: 'Example 1',
icon: 'icon-shape-triangle',
isPickable: true,
},
{
unique: '2',
entityType: 'example',
name: 'Example 2',
icon: 'icon-shape-triangle',
isPickable: true,
},
{
unique: '3',
entityType: 'example',
name: 'Example 3',
icon: 'icon-shape-triangle',
isPickable: true,
},
{
unique: '4',
entityType: 'example',
name: 'Example 4',
icon: 'icon-shape-triangle',
isPickable: false,
},
{
unique: '5',
entityType: 'example',
name: 'Example 5',
icon: 'icon-shape-triangle',
isPickable: true,
},
];

View File

@@ -9,6 +9,7 @@ export * from './conditions/index.js';
export * from './constants.js';
export * from './default/collection-default.element.js';
export * from './global-components.js';
export * from './menu/index.js';
export * from './workspace-view/index.js';
export * from './default/collection-default.context.js';

View File

@@ -1 +1,2 @@
export { UMB_COLLECTION_MENU_CONTEXT } from './default/default-collection-menu.context.token.js';
export * from './menu-item/constants.js';

View File

@@ -1,7 +1,6 @@
import type { UmbCollectionItemModel } from '../../item/types.js';
import type { UmbCollectionSelectionConfiguration } from '../../types.js';
import type { UmbDefaultCollectionMenuContext } from './default-collection-menu.context.js';
import { getItemFallbackIcon, getItemFallbackName } from '@umbraco-cms/backoffice/entity-item';
import {
html,
customElement,
@@ -14,6 +13,8 @@ import {
} from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import '../menu-item/collection-menu-item.element.js';
@customElement('umb-default-collection-menu')
export class UmbDefaultCollectionMenuElement extends UmbLitElement {
private _api: UmbDefaultCollectionMenuContext | undefined;
@@ -115,16 +116,11 @@ export class UmbDefaultCollectionMenuElement extends UmbLitElement {
#renderItem(item: UmbCollectionItemModel) {
return html`
<uui-menu-item
label=${item.name ?? getItemFallbackName(item)}
selectable
@selected=${() => this._api?.selection.select(item.unique)}
@deselected=${() => this._api?.selection.deselect(item.unique)}
?selected=${this._api?.selection.isSelected(item.unique)}>
${item.icon
? html`<uui-icon slot="icon" name=${item.icon}></uui-icon>`
: html`<uui-icon slot="icon" name=${getItemFallbackIcon()}></uui-icon>`}
</uui-menu-item>
<umb-collection-menu-item
entityType=${item.entityType}
.props=${{
item: item,
}}></umb-collection-menu-item>
`;
}

View File

@@ -0,0 +1 @@
export * from './menu-item/index.js';

View File

@@ -0,0 +1,17 @@
import type { UmbCollectionItemModel } from '../../item/types.js';
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
import type { UmbApi } from '@umbraco-cms/backoffice/extension-api';
import type { UmbContextMinimal } from '@umbraco-cms/backoffice/context-api';
export interface UmbCollectionMenuItemContext<
CollectionMenuItemType extends UmbCollectionItemModel = UmbCollectionItemModel,
> extends UmbApi,
UmbContextMinimal {
item: Observable<CollectionMenuItemType | undefined>;
isSelectable: Observable<boolean>;
isSelected: Observable<boolean>;
getItem(): CollectionMenuItemType | undefined;
setItem(item: CollectionMenuItemType | undefined): void;
select(): void;
deselect(): void;
}

View File

@@ -0,0 +1,6 @@
import type { UmbCollectionMenuItemContext } from './collection-menu-item-context.interface.js';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
export const UMB_COLLECTION_MENU_ITEM_CONTEXT = new UmbContextToken<UmbCollectionMenuItemContext>(
'UmbCollectionMenuItemContext',
);

View File

@@ -0,0 +1,96 @@
import { UmbDefaultCollectionMenuItemContext } from './default/index.js';
import type { ManifestCollectionMenuItem } from './extension/types.js';
import { customElement, property } from '@umbraco-cms/backoffice/external/lit';
import {
UmbExtensionElementAndApiSlotElementBase,
umbExtensionsRegistry,
} from '@umbraco-cms/backoffice/extension-registry';
import { createObservablePart } from '@umbraco-cms/backoffice/observable-api';
@customElement('umb-collection-menu-item')
export class UmbCollectionMenuItemElement extends UmbExtensionElementAndApiSlotElementBase<ManifestCollectionMenuItem> {
@property({ type: String, reflect: true })
get entityType() {
return this.#entityType;
}
set entityType(newVal) {
this.#entityType = newVal;
this.#observeEntityType();
}
#entityType?: string;
@property({ type: Object, attribute: false })
override set props(newVal: Record<string, unknown> | undefined) {
super.props = newVal;
this.#assignProps();
}
override get props() {
return super.props;
}
#observeEntityType() {
if (!this.#entityType) return;
const filterByEntityType = (manifest: ManifestCollectionMenuItem) => {
if (!this.#entityType) return false;
return manifest.forEntityTypes.includes(this.#entityType);
};
// Check if we can find a matching collection menu item for the current entity type.
// If we can, we will use that one, if not we will render a fallback collection menu item.
this.observe(
// TODO: what should we do if there are multiple collection menu items for an entity type?
// This method gets all extensions based on a type, then filters them based on the entity type. and then we get the alias of the first one [NL]
createObservablePart(
umbExtensionsRegistry.byTypeAndFilter(this.getExtensionType(), filterByEntityType),
(x) => x[0]?.alias,
),
(alias) => {
this.alias = alias;
// If we don't find any registered collection menu items for this specific entity type, we will render a fallback collection menu item.
// This is on purpose not done with the extension initializer since we don't want to spin up a real extension unless we have to.
if (!alias) {
this.#renderFallbackItem();
}
},
'umbObserveAlias',
);
}
#renderFallbackItem() {
// TODO: make creating of elements with apis a shared function.
const element = document.createElement('umb-default-collection-menu-item');
const api = new UmbDefaultCollectionMenuItemContext(element);
element.api = api;
this._element = element;
this.#assignProps();
this.requestUpdate('_element');
}
getExtensionType() {
return 'collectionMenuItem';
}
getDefaultElementName() {
return 'umb-default-collection-menu-item';
}
#assignProps() {
if (!this._element || !this.props) return;
Object.keys(this.props).forEach((key) => {
(this._element as any)[key] = this.props![key];
});
}
override getDefaultApiConstructor() {
return UmbDefaultCollectionMenuItemContext;
}
}
declare global {
interface HTMLElementTagNameMap {
'umb-collection-menu-item': UmbCollectionMenuItemElement;
}
}

View File

@@ -0,0 +1 @@
export * from './default/constants.js';

View File

@@ -0,0 +1 @@
export { UMB_COLLECTION_MENU_ITEM_DEFAULT_KIND_MANIFEST } from './manifests.js';

View File

@@ -0,0 +1,114 @@
import type { UmbCollectionMenuItemContext } from '../collection-menu-item-context.interface.js';
import { UMB_COLLECTION_MENU_ITEM_CONTEXT } from '../collection-menu-item.context.token.js';
import type { UmbCollectionItemModel } from '../../../types.js';
import type { ManifestCollectionMenuItem } from '../extension/types.js';
import { UMB_COLLECTION_MENU_CONTEXT } from '../../default/default-collection-menu.context.token.js';
import { UmbBooleanState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { map } from '@umbraco-cms/backoffice/external/rxjs';
export class UmbDefaultCollectionMenuItemContext<
CollectionMenuItemType extends UmbCollectionItemModel = UmbCollectionItemModel,
>
extends UmbContextBase
implements UmbCollectionMenuItemContext<CollectionMenuItemType>
{
#manifest?: ManifestCollectionMenuItem;
protected readonly _item = new UmbObjectState<CollectionMenuItemType | undefined>(undefined);
readonly item = this._item.asObservable();
#isSelectable = new UmbBooleanState(false);
readonly isSelectable = this.#isSelectable.asObservable();
#isSelectableContext = new UmbBooleanState(false);
readonly isSelectableContext = this.#isSelectableContext.asObservable();
#isSelected = new UmbBooleanState(false);
readonly isSelected = this.#isSelected.asObservable();
#collectionMenuContext?: typeof UMB_COLLECTION_MENU_CONTEXT.TYPE;
constructor(host: UmbControllerHost) {
super(host, UMB_COLLECTION_MENU_ITEM_CONTEXT);
this.#consumeContexts();
}
async #consumeContexts() {
this.consumeContext(UMB_COLLECTION_MENU_CONTEXT, (context) => {
this.#collectionMenuContext = context;
this.#observeIsSelectable();
this.#observeIsSelected();
});
}
public set manifest(manifest: ManifestCollectionMenuItem | undefined) {
if (this.#manifest === manifest) return;
this.#manifest = manifest;
}
public get manifest() {
return this.#manifest;
}
public setItem(item: CollectionMenuItemType | undefined) {
this._item.setValue(item);
if (item) {
this.#observeIsSelectable();
this.#observeIsSelected();
}
}
public select() {
const unique = this.getItem()?.unique;
if (!unique) throw new Error('Could not select. Unique is missing');
this.#collectionMenuContext?.selection.select(unique);
}
public deselect() {
const unique = this.getItem()?.unique;
if (!unique) throw new Error('Could not deselect. Unique is missing');
this.#collectionMenuContext?.selection.deselect(unique);
}
getItem() {
return this._item.getValue();
}
#observeIsSelectable() {
if (!this.#collectionMenuContext) return;
const item = this.getItem();
if (!item) return;
this.observe(
this.#collectionMenuContext.selection.selectable,
(value) => {
this.#isSelectableContext.setValue(value);
// If the collection menu is selectable, check if this item is selectable
if (value === true) {
const isSelectable = this.#collectionMenuContext?.selectableFilter?.(item) ?? true;
this.#isSelectable.setValue(isSelectable);
}
},
'observeIsSelectable',
);
}
#observeIsSelected() {
if (!this.#collectionMenuContext) return;
const unique = this.getItem()?.unique;
if (!unique) return;
this.observe(
this.#collectionMenuContext.selection.selection.pipe(map((selection) => selection.includes(unique))),
(isSelected) => {
this.#isSelected.setValue(isSelected);
},
'observeIsSelected',
);
}
}
export { UmbDefaultCollectionMenuItemContext as api };

View File

@@ -0,0 +1,80 @@
import type { UmbCollectionItemModel } from '../../../item/types.js';
import type { UmbCollectionMenuItemContext } from '../collection-menu-item-context.interface.js';
import { html, state, property, customElement, nothing } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { getItemFallbackIcon, getItemFallbackName } from '@umbraco-cms/backoffice/entity-item';
@customElement('umb-default-collection-menu-item')
export class UmbDefaultCollectionMenuItemElement extends UmbLitElement {
@property({ type: Object, attribute: false })
set item(newVal: UmbCollectionItemModel) {
this._item = newVal;
if (this._item) {
this.#initItem();
}
}
get item(): UmbCollectionItemModel | undefined {
return this._item;
}
protected _item?: UmbCollectionItemModel;
@property({ type: Object, attribute: false })
public set api(value: UmbCollectionMenuItemContext | undefined) {
this.#api = value;
if (this.#api) {
this.observe(this.#api.isSelectable, (value) => (this._isSelectable = value));
this.observe(this.#api.isSelected, (value) => (this._isSelected = value));
this.#initItem();
}
}
public get api(): UmbCollectionMenuItemContext | undefined {
return this.#api;
}
#api: UmbCollectionMenuItemContext | undefined;
@state()
protected _isActive = false;
@state()
protected _isSelected = false;
@state()
protected _isSelectable = false;
#initItem() {
if (!this.#api) return;
if (!this._item) return;
this.#api.setItem(this._item);
}
override render() {
const item = this._item;
if (!item) return nothing;
return html`
<uui-menu-item
label=${item?.name ?? getItemFallbackName(item)}
?selectable=${this._isSelectable}
?selected=${this._isSelected}
@selected=${() => this.#api?.select()}
@deselected=${() => this.#api?.deselect()}>
${item.icon
? html`<uui-icon slot="icon" name=${item.icon}></uui-icon>`
: html`<uui-icon slot="icon" name=${getItemFallbackIcon()}></uui-icon>`}
</uui-menu-item>
`;
}
static override styles = [UmbTextStyles];
}
export { UmbDefaultCollectionMenuItemElement as element };
declare global {
interface HTMLElementTagNameMap {
'umb-default-collection-menu-item': UmbDefaultCollectionMenuItemElement;
}
}

View File

@@ -0,0 +1,2 @@
export { UmbDefaultCollectionMenuItemContext } from './default-collection-menu-item.context.js';
export { UmbDefaultCollectionMenuItemElement } from './default-collection-menu-item.element.js';

View File

@@ -0,0 +1,17 @@
import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry';
export const UMB_COLLECTION_MENU_ITEM_DEFAULT_KIND_MANIFEST: UmbExtensionManifestKind = {
type: 'kind',
alias: 'Umb.Kind.CollectionMenuItem.Default',
matchKind: 'default',
matchType: 'collectionMenuItem',
manifest: {
type: 'collectionMenuItem',
api: () => import('./default-collection-menu-item.context.js'),
element: () => import('./default-collection-menu-item.element.js'),
},
};
export const manifests: Array<UmbExtensionManifest | UmbExtensionManifestKind> = [
UMB_COLLECTION_MENU_ITEM_DEFAULT_KIND_MANIFEST,
];

View File

@@ -0,0 +1,12 @@
import type { ManifestElementAndApi } from '@umbraco-cms/backoffice/extension-api';
export interface ManifestCollectionMenuItem extends ManifestElementAndApi<any, any> {
type: 'collectionMenuItem';
forEntityTypes: Array<string>;
}
declare global {
interface UmbExtensionManifestMap {
UmbCollectionMenuItem: ManifestCollectionMenuItem;
}
}

View File

@@ -0,0 +1 @@
export type * from './collection-menu-item.extension.js';

View File

@@ -0,0 +1,4 @@
export * from './default/index.js';
export * from './collection-menu-item.context.token.js';
export * from './collection-menu-item.element.js';
export type * from './collection-menu-item-context.interface.js';

View File

@@ -0,0 +1,4 @@
import { manifests as defaultManifests } from './default/manifests.js';
import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry';
export const manifests: Array<UmbExtensionManifest | UmbExtensionManifestKind> = [...defaultManifests];

View File

@@ -1,3 +1,4 @@
export * from './views/constants.js';
export const UMB_USER_COLLECTION_ALIAS = 'Umb.Collection.User';
export { UMB_USER_COLLECTION_CONTEXT } from './user-collection.context-token.js';
export * from './menu/constants.js';

View File

@@ -1,7 +1,8 @@
import { UMB_USER_COLLECTION_REPOSITORY_ALIAS } from './repository/index.js';
import { manifests as collectionActionManifests } from './action/manifests.js';
import { manifests as collectionMenuManifests } from './menu/manifests.js';
import { manifests as collectionRepositoryManifests } from './repository/manifests.js';
import { manifests as collectionViewManifests } from './views/manifests.js';
import { manifests as collectionActionManifests } from './action/manifests.js';
import { UMB_USER_COLLECTION_ALIAS } from './constants.js';
export const manifests: Array<UmbExtensionManifest> = [
@@ -15,7 +16,8 @@ export const manifests: Array<UmbExtensionManifest> = [
repositoryAlias: UMB_USER_COLLECTION_REPOSITORY_ALIAS,
},
},
...collectionActionManifests,
...collectionMenuManifests,
...collectionRepositoryManifests,
...collectionViewManifests,
...collectionActionManifests,
];

View File

@@ -0,0 +1 @@
export const UMB_USER_COLLECTION_MENU_ALIAS = 'Umb.CollectionMenu.User';

View File

@@ -0,0 +1,23 @@
import { UMB_USER_ENTITY_TYPE } from '../../entity.js';
import { UMB_USER_COLLECTION_REPOSITORY_ALIAS } from '../repository/constants.js';
import { UMB_USER_COLLECTION_MENU_ALIAS } from './constants.js';
export const manifests: Array<UmbExtensionManifest> = [
{
type: 'collectionMenu',
kind: 'default',
alias: UMB_USER_COLLECTION_MENU_ALIAS,
name: 'User Collection Menu',
meta: {
collectionRepositoryAlias: UMB_USER_COLLECTION_REPOSITORY_ALIAS,
},
},
{
type: 'collectionMenuItem',
kind: 'default',
alias: 'Umb.CollectionMenuItem.User',
name: 'User Collection Menu Item',
element: () => import('./user-collection-menu-item.element.js'),
forEntityTypes: [UMB_USER_ENTITY_TYPE],
},
];

View File

@@ -0,0 +1,88 @@
import type { UmbUserDetailModel } from '../../types.js';
import { html, customElement, css, state, property, nothing } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import type { UmbCollectionMenuItemContext } from '@umbraco-cms/backoffice/collection';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
@customElement('umb-user-collection-menu-item')
export class UmbUserCollectionMenuItemElement extends UmbLitElement {
@property({ type: Object, attribute: false })
set item(newVal: UmbUserDetailModel) {
this._item = newVal;
if (this._item) {
this.#initItem();
}
}
get item(): UmbUserDetailModel | undefined {
return this._item;
}
protected _item?: UmbUserDetailModel;
@property({ type: Object, attribute: false })
public set api(value: UmbCollectionMenuItemContext | undefined) {
this.#api = value;
if (this.#api) {
this.observe(this.#api.isSelectable, (value) => (this._isSelectable = value));
this.observe(this.#api.isSelected, (value) => (this._isSelected = value));
this.#initItem();
}
}
public get api(): UmbCollectionMenuItemContext | undefined {
return this.#api;
}
#api: UmbCollectionMenuItemContext | undefined;
@state()
protected _isActive = false;
@state()
protected _isSelected = false;
@state()
protected _isSelectable = false;
#initItem() {
if (!this.#api) return;
if (!this._item) return;
this.#api.setItem(this._item);
}
override render() {
const item = this._item;
if (!item) return nothing;
return html`
<uui-menu-item
label=${item?.name}
?selectable=${this._isSelectable}
?selected=${this._isSelected}
@selected=${() => this.#api?.select()}
@deselected=${() => this.#api?.deselect()}>
<umb-user-avatar
slot="icon"
.name=${item.name}
.kind=${item.kind}
.imgUrls=${item.avatarUrls}></umb-user-avatar>
</uui-menu-item>
`;
}
static override styles = [
UmbTextStyles,
css`
umb-user-avatar {
font-size: 10px;
}
`,
];
}
export { UmbUserCollectionMenuItemElement as element };
declare global {
interface HTMLElementTagNameMap {
'umb-user-collection-menu-item': UmbUserCollectionMenuItemElement;
}
}

View File

@@ -3,7 +3,6 @@ export const manifests: Array<UmbExtensionManifest> = [
type: 'modal',
alias: 'Umb.Modal.User.Picker',
name: 'User Picker Modal',
js: () => import('./user-picker/user-picker-modal.element.js'),
},
{
type: 'modal',

View File

@@ -1,98 +0,0 @@
import { UmbUserCollectionRepository } from '../../collection/repository/user-collection.repository.js';
import type { UmbUserItemModel } from '../../repository/item/index.js';
import type { UmbUserPickerModalData, UmbUserPickerModalValue } from './user-picker-modal.token.js';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit';
import { css, html, customElement, state, ifDefined } from '@umbraco-cms/backoffice/external/lit';
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
import { UmbSelectionManager } from '@umbraco-cms/backoffice/utils';
@customElement('umb-user-picker-modal')
export class UmbUserPickerModalElement extends UmbModalBaseElement<UmbUserPickerModalData, UmbUserPickerModalValue> {
@state()
private _users: Array<UmbUserItemModel> = [];
#selectionManager = new UmbSelectionManager(this);
#userCollectionRepository = new UmbUserCollectionRepository(this);
override connectedCallback(): void {
super.connectedCallback();
// TODO: in theory this config could change during the lifetime of the modal, so we could observe it
this.#selectionManager.setMultiple(this.data?.multiple ?? false);
this.#selectionManager.setSelection(this.value?.selection ?? []);
}
protected override firstUpdated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {
super.firstUpdated(_changedProperties);
this.#requestUsers();
}
async #requestUsers() {
if (!this.#userCollectionRepository) return;
const { data } = await this.#userCollectionRepository.requestCollection();
if (data) {
this._users = data.items;
}
}
#submit() {
this.value = { selection: this.#selectionManager.getSelection() };
this.modalContext?.submit();
}
#close() {
this.modalContext?.reject();
}
override render() {
return html`
<umb-body-layout headline=${this.localize.term('defaultdialogs_chooseUsers')}>
<uui-box>
${this._users.map(
(user) => html`
<uui-menu-item
label=${ifDefined(user.name)}
selectable
@selected=${() => this.#selectionManager.select(user.unique)}
@deselected=${() => this.#selectionManager.deselect(user.unique)}
?selected=${this.#selectionManager.isSelected(user.unique)}>
<umb-user-avatar
slot="icon"
.name=${user.name}
.kind=${user.kind}
.imgUrls=${user.avatarUrls}></umb-user-avatar>
</uui-menu-item>
`,
)}
</uui-box>
<div slot="actions">
<uui-button label=${this.localize.term('general_close')} @click=${this.#close}></uui-button>
<uui-button
label=${this.localize.term('general_choose')}
look="primary"
color="positive"
@click=${this.#submit}></uui-button>
</div>
</umb-body-layout>
`;
}
static override styles = [
UmbTextStyles,
css`
umb-user-avatar {
font-size: 12px;
}
`,
];
}
export default UmbUserPickerModalElement;
declare global {
interface HTMLElementTagNameMap {
'umb-user-picker-modal': UmbUserPickerModalElement;
}
}

View File

@@ -1,19 +1,29 @@
import type { UmbUserDetailModel } from '../../types.js';
import type { UmbPickerModalData } from '@umbraco-cms/backoffice/modal';
import { UMB_USER_COLLECTION_MENU_ALIAS } from '../../collection/constants.js';
import type {
UmbCollectionItemPickerModalData,
UmbCollectionItemPickerModalValue,
} from '@umbraco-cms/backoffice/collection';
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
export type UmbUserPickerModalData = UmbPickerModalData<UmbUserDetailModel>;
export type UmbUserPickerModalData = UmbCollectionItemPickerModalData<UmbUserDetailModel>;
export interface UmbUserPickerModalValue {
selection: Array<string | null>;
}
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface UmbUserPickerModalValue extends UmbCollectionItemPickerModalValue {}
export const UMB_USER_PICKER_MODAL = new UmbModalToken<UmbUserPickerModalData, UmbUserPickerModalValue>(
'Umb.Modal.User.Picker',
/* TODO: use constant. We had to use the string directly here to avoid a circular dependency.
When we have removed the dataType (dependency on content) from the picker context we update this */
'Umb.Modal.CollectionItemPicker',
{
modal: {
type: 'sidebar',
size: 'small',
},
data: {
collection: {
menuAlias: UMB_USER_COLLECTION_MENU_ALIAS,
},
},
},
);