Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Entity Item Ref Extension #18265

Merged
merged 37 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
edb6a7c
wip entity-item-ref extension point
madsrasmussen Feb 6, 2025
62f1bd7
clean up
madsrasmussen Feb 6, 2025
fd06887
add ref list element
madsrasmussen Feb 6, 2025
977b047
fix styling
madsrasmussen Feb 6, 2025
0731f66
Update document-item-ref.element.ts
madsrasmussen Feb 6, 2025
5901d4a
move item repo
madsrasmussen Feb 6, 2025
5b0444f
implement for input member
madsrasmussen Feb 6, 2025
d073ab5
enable action slot
madsrasmussen Feb 7, 2025
964e86c
add null check
madsrasmussen Feb 7, 2025
e1cbf73
fix sorting again
madsrasmussen Feb 7, 2025
b986ebd
fix sorting again
madsrasmussen Feb 7, 2025
a98e3db
use member element
madsrasmussen Feb 7, 2025
3aba568
add draft styling back
madsrasmussen Feb 7, 2025
f4547b5
move item repository
madsrasmussen Feb 7, 2025
1adf436
implement for user input
madsrasmussen Feb 7, 2025
db9b9d3
pass readonly and standalone props
madsrasmussen Feb 7, 2025
1088b04
make editPath a state
madsrasmussen Feb 7, 2025
d70fb95
Update member-item-ref.element.ts
madsrasmussen Feb 7, 2025
914953b
Fix user item ref
madsrasmussen Feb 7, 2025
726d07d
remove open button
madsrasmussen Feb 7, 2025
efe75be
remove unused
madsrasmussen Feb 7, 2025
33f3759
remove unused
madsrasmussen Feb 8, 2025
95c12ec
check for section permission
madsrasmussen Feb 8, 2025
e940f6e
Merge branch 'v15/dev' into v15/feature/entity-item-ref-extension
madsrasmussen Feb 10, 2025
0afe46c
add fallback element
madsrasmussen Feb 10, 2025
fa49110
add unique to modal route registration
madsrasmussen Feb 11, 2025
a76b30a
add unique to modal router
madsrasmussen Feb 11, 2025
ed0deb6
remove unused id
madsrasmussen Feb 11, 2025
0f72728
Update member-item-ref.element.ts
madsrasmussen Feb 11, 2025
13d5fea
append unique
madsrasmussen Feb 11, 2025
494fcbe
compare with old value
madsrasmussen Feb 11, 2025
3c67f00
only recreate the controller if the entity type changes
madsrasmussen Feb 11, 2025
b521750
fix console warning
madsrasmussen Feb 11, 2025
e8ad351
Merge branch 'v15/dev' into v15/feature/entity-item-ref-extension
madsrasmussen Feb 11, 2025
fe1fbea
Merge branch 'v15/dev' into v15/feature/entity-item-ref-extension
madsrasmussen Feb 12, 2025
d43c6cb
Merge branch 'v15/dev' into v15/feature/entity-item-ref-extension
madsrasmussen Feb 12, 2025
1b04ec7
use addUniquePaths for modal registration
madsrasmussen Feb 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { UmbDefaultItemModel } from '../types.js';
import { customElement, html, nothing, property } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';

@customElement('umb-default-item-ref')
export class UmbDefaultItemRefElement extends UmbLitElement {
@property({ type: Object })
item?: UmbDefaultItemModel;

@property({ type: Boolean })
readonly = false;

@property({ type: Boolean })
standalone = false;

override render() {
if (!this.item) return nothing;

return html`
<uui-ref-node name=${this.item.name} ?readonly=${this.readonly} ?standalone=${this.standalone}>
<slot name="actions" slot="actions"></slot>
${this.#renderIcon(this.item)}
</uui-ref-node>
`;
}

#renderIcon(item: UmbDefaultItemModel) {
if (!item.icon) return;
return html`<umb-icon slot="icon" name=${item.icon}></umb-icon>`;
}
}

declare global {
interface HTMLElementTagNameMap {
'umb-default-item-ref': UmbDefaultItemRefElement;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import type { UmbEntityModel } from '../types.js';
import type { ManifestEntityItemRef } from './entity-item-ref.extension.js';
import { customElement, property, type PropertyValueMap, state, css, html } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UmbExtensionsElementInitializer } from '@umbraco-cms/backoffice/extension-api';
import { UMB_MARK_ATTRIBUTE_NAME } from '@umbraco-cms/backoffice/const';
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';

import './default-item-ref.element.js';

@customElement('umb-entity-item-ref')
export class UmbEntityItemRefElement extends UmbLitElement {
#extensionsController?: UmbExtensionsElementInitializer<any>;
#item?: UmbEntityModel;

@state()
_component?: any; // TODO: Add type

@property({ type: Object, attribute: false })
public get item(): UmbEntityModel | undefined {
return this.#item;
}
public set item(value: UmbEntityModel | undefined) {
const oldValue = this.#item;
this.#item = value;

if (value === oldValue) return;
if (!value) return;

// If the component is already created and the entity type is the same, we can just update the item.
if (this._component && value.entityType === oldValue?.entityType) {
this._component.item = value;
return;
}

// If the component is already created, but the entity type is different, we need to destroy the component.
this.#createController(value.entityType);
}

#readonly = false;
@property({ type: Boolean, attribute: 'readonly' })
public get readonly() {
return this.#readonly;
}
public set readonly(value) {
this.#readonly = value;

if (this._component) {
this._component.readonly = this.#readonly;
}
}

#standalone = false;
@property({ type: Boolean, attribute: 'standalone' })
public get standalone() {
return this.#standalone;
}
public set standalone(value) {
this.#standalone = value;

if (this._component) {
this._component.standalone = this.#standalone;
}
}

protected override firstUpdated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {
super.firstUpdated(_changedProperties);
this.setAttribute(UMB_MARK_ATTRIBUTE_NAME, 'entity-item-ref');
}

#createController(entityType: string) {
if (this.#extensionsController) {
this.#extensionsController.destroy();
}

this.#extensionsController = new UmbExtensionsElementInitializer(
this,
umbExtensionsRegistry,
'entityItemRef',
(manifest: ManifestEntityItemRef) => manifest.forEntityTypes.includes(entityType),
(extensionControllers) => {
this._component?.remove();
const component = extensionControllers[0]?.component || document.createElement('umb-default-item-ref');

// assign the properties to the component
component.item = this.#item;
component.readonly = this.readonly;
component.standalone = this.standalone;

// Proxy the actions slot to the component
const slotElement = document.createElement('slot');
slotElement.name = 'actions';
slotElement.setAttribute('slot', 'actions');
component.appendChild(slotElement);

this._component = component;
},
undefined, // We can leave the alias to undefined, as we destroy this our selfs.
undefined,
{ single: true },
);
}

override render() {
return html`${this._component}`;
}

static override styles = [
css`
:host {
display: block;
position: relative;
}
`,
];
}

export { UmbEntityItemRefElement as element };

declare global {
interface HTMLElementTagNameMap {
'umb-entity-item-ref': UmbEntityItemRefElement;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { ManifestElement, ManifestWithDynamicConditions } from '@umbraco-cms/backoffice/extension-api';

export interface ManifestEntityItemRef<MetaType extends MetaEntityItemRef = MetaEntityItemRef>
extends ManifestElement<any>,
ManifestWithDynamicConditions<UmbExtensionConditionConfig> {
type: 'entityItemRef';
meta: MetaType;
forEntityTypes: Array<string>;
}

// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface MetaEntityItemRef {}

declare global {
interface UmbExtensionManifestMap {
umbManifestEntityItemRef: ManifestEntityItemRef;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import './entity-item-ref.element.js';

export * from './entity-item-ref.element.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type * from './types.js';
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { UMB_ENTITY_CONTEXT } from './entity.context-token.js';
export { UmbEntityContext } from './entity.context.js';
export * from './entity-item-ref/index.js';
export type * from './types.js';
4 changes: 4 additions & 0 deletions src/Umbraco.Web.UI.Client/src/packages/core/entity/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ export interface UmbEntityModel {
export interface UmbNamedEntityModel extends UmbEntityModel {
name: string;
}

export interface UmbDefaultItemModel extends UmbNamedEntityModel {
icon?: string;
}
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './picker-input.context.js';
export * from './picker-input.context-token.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import type { UmbPickerInputContext } from './picker-input.context.js';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';

export const UMB_PICKER_INPUT_CONTEXT = new UmbContextToken<UmbPickerInputContext>('UmbPickerInputContext');
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { UMB_PICKER_INPUT_CONTEXT } from './picker-input.context-token.js';
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
import { UmbRepositoryItemsManager } from '@umbraco-cms/backoffice/repository';
import { UMB_MODAL_MANAGER_CONTEXT, umbConfirmModal } from '@umbraco-cms/backoffice/modal';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
Expand All @@ -13,7 +14,7 @@ export class UmbPickerInputContext<
PickerItemType extends PickerItemBaseType = PickedItemType,
PickerModalConfigType extends UmbPickerModalData<PickerItemType> = UmbPickerModalData<PickerItemType>,
PickerModalValueType extends UmbPickerModalValue = UmbPickerModalValue,
> extends UmbControllerBase {
> extends UmbContextBase<UmbPickerInputContext> {
modalAlias: string | UmbModalToken<UmbPickerModalData<PickerItemType>, PickerModalValueType>;
repository?: UmbItemRepository<PickedItemType>;
#getUnique: (entry: PickedItemType) => string | undefined;
Expand Down Expand Up @@ -59,9 +60,8 @@ export class UmbPickerInputContext<
modalAlias: string | UmbModalToken<UmbPickerModalData<PickerItemType>, PickerModalValueType>,
getUniqueMethod?: (entry: PickedItemType) => string | undefined,
) {
super(host);
super(host, UMB_PICKER_INPUT_CONTEXT);
this.modalAlias = modalAlias;
this.#getUnique = getUniqueMethod || ((entry) => entry.unique);

this.#getUnique = getUniqueMethod
? (entry: PickedItemType) => {
Expand All @@ -74,7 +74,7 @@ export class UmbPickerInputContext<
}
: (entry) => entry.unique;

this.#itemManager = new UmbRepositoryItemsManager<PickedItemType>(this, repositoryAlias, this.#getUnique);
this.#itemManager = new UmbRepositoryItemsManager<PickedItemType>(this, repositoryAlias, getUniqueMethod);

this.selection = this.#itemManager.uniques;
this.selectedItems = this.#itemManager.items;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import type { UmbDocumentPickerModalData, UmbDocumentPickerModalValue } from '../../modals/types.js';
import {
UMB_DOCUMENT_PICKER_MODAL,
UMB_DOCUMENT_ITEM_REPOSITORY_ALIAS,
UMB_DOCUMENT_SEARCH_PROVIDER_ALIAS,
} from '../../constants.js';
import type { UmbDocumentItemModel } from '../../repository/index.js';
import { UMB_DOCUMENT_PICKER_MODAL, UMB_DOCUMENT_SEARCH_PROVIDER_ALIAS } from '../../constants.js';
import type { UmbDocumentItemModel } from '../../item/types.js';
import { UMB_DOCUMENT_ITEM_REPOSITORY_ALIAS } from '../../item/constants.js';
import type { UmbDocumentTreeItemModel } from '../../tree/types.js';
import { UmbPickerInputContext } from '@umbraco-cms/backoffice/picker-input';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
Expand Down
Loading
Loading