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: Readonly mode for umb-collection element #2247

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5014e8d
Update manifests.ts
madsrasmussen Aug 28, 2024
6039364
set readonly attribute
madsrasmussen Aug 28, 2024
b6812b3
set default state type
madsrasmussen Aug 28, 2024
683f6ee
add read only state manager to collection context
madsrasmussen Aug 28, 2024
1a6e4db
add option manager to base interface
madsrasmussen Aug 28, 2024
55cbc0b
add condition to check for a writable collection
madsrasmussen Aug 28, 2024
ea6bb57
add condition to create document workspace action
madsrasmussen Aug 28, 2024
57d5616
move into folder
madsrasmussen Aug 28, 2024
f97b2ed
set readonly state in context based on the attribute state
madsrasmussen Aug 28, 2024
bce9a3a
add specific token for a content property data set
madsrasmussen Aug 28, 2024
20d7cf9
introduce a content collection workspace
madsrasmussen Aug 30, 2024
a49ed30
add comment about duplicated code
madsrasmussen Aug 30, 2024
bc44e43
Update collection-action-bundle.element.ts
madsrasmussen Aug 30, 2024
fb11a33
add condition
madsrasmussen Aug 30, 2024
19ccb0a
Merge branch 'main' into v14/feature/readonly-collections
madsrasmussen Aug 30, 2024
73d1896
Merge branch 'main' into v14/feature/readonly-collections
madsrasmussen Sep 2, 2024
2ed94a8
Merge branch 'main' into v14/feature/readonly-collections
madsrasmussen Sep 2, 2024
999841c
Merge branch 'main' into v14/feature/readonly-collections
nielslyngsoe Sep 9, 2024
a9f6eb9
Update manifests.ts
madsrasmussen Sep 9, 2024
b9132fe
Merge branch 'main' into v14/feature/readonly-collections
madsrasmussen Sep 12, 2024
b82d2ac
Merge branch 'main' into v14/feature/readonly-collections
madsrasmussen Sep 17, 2024
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
40 changes: 40 additions & 0 deletions src/packages/core/collection/collection.element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,57 @@ export class UmbCollectionElement extends UmbExtensionElementAndApiSlotElementBa
}
#config?: UmbCollectionConfiguration;

/**
* 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 })
public get readonly() {
return this.#readonly;
}
public set readonly(value) {
this.#readonly = value;
this.#setReadonly();
}
#readonly = false;

protected override apiChanged(api: UmbApi | undefined): void {
super.apiChanged(api);
this.#setConfig();
this.#setReadonly();
}

#setConfig() {
if (!this.#config || !this._api) return;
// TODO: add type
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
this._api.setConfig(this.#config);
}

#setReadonly() {
if (!this._api) return;

const unique = 'UMB_READ_ONLY_ATTRIBUTE';

if (this.#readonly) {
const state = {
unique,
message: '',
};
// TODO: add type
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
this._api.readOnlyState.addState(state);
} else {
// TODO: add type
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
this._api.readOnlyState.removeState(unique);
}
}
}

declare global {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
import { html, customElement } from '@umbraco-cms/backoffice/external/lit';
import type { CSSResultGroup } from '@umbraco-cms/backoffice/external/lit';
import { html, customElement, css } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';

@customElement('umb-collection-action-bundle')
export class UmbCollectionActionBundleElement extends UmbLitElement {
override render() {
return html`<umb-extension-slot type="collectionAction"></umb-extension-slot>`;
}

static override styles: CSSResultGroup = [
css`
:host {
display: contents;
}
`,
];
}

declare global {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const UMB_WRITABLE_COLLECTION_CONDITION_ALIAS = 'Umb.Condition.Collection.Writable';
1 change: 1 addition & 0 deletions src/packages/core/collection/conditions/writable/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './constants.js';
11 changes: 11 additions & 0 deletions src/packages/core/collection/conditions/writable/manifests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { UMB_WRITABLE_COLLECTION_CONDITION_ALIAS } from './constants.js';
import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';

export const manifests: Array<ManifestTypes> = [
{
type: 'condition',
name: 'Writable Collection Condition',
alias: UMB_WRITABLE_COLLECTION_CONDITION_ALIAS,
api: () => import('./writable-collection.condition.js'),
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { UMB_COLLECTION_CONTEXT } from '../../default/collection-default.context-token.js';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import type {
UmbConditionConfigBase,
UmbConditionControllerArguments,
UmbExtensionCondition,
} from '@umbraco-cms/backoffice/extension-api';
import { UmbConditionBase } from '@umbraco-cms/backoffice/extension-registry';

export class UmbWritableCollectionCondition
extends UmbConditionBase<UmbConditionConfigBase>
implements UmbExtensionCondition
{
constructor(host: UmbControllerHost, args: UmbConditionControllerArguments<UmbConditionConfigBase>) {
super(host, args);

this.consumeContext(UMB_COLLECTION_CONTEXT, (context) => {
this.observe(context.readOnlyState.isOn, (value) => {
this.permitted = value !== true;
});
});
}
}

export { UmbWritableCollectionCondition as api };
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { UmbArrayState, UmbNumberState, UmbObjectState } from '@umbraco-cms/back
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
import { UmbExtensionApiInitializer } from '@umbraco-cms/backoffice/extension-api';
import { UmbSelectionManager, UmbPaginationManager } from '@umbraco-cms/backoffice/utils';
import { UmbSelectionManager, UmbPaginationManager, UmbReadOnlyStateManager } from '@umbraco-cms/backoffice/utils';
import type { ManifestCollection, ManifestRepository } from '@umbraco-cms/backoffice/extension-registry';
import type { UmbApi } from '@umbraco-cms/backoffice/extension-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
Expand Down Expand Up @@ -61,6 +61,8 @@ export class UmbDefaultCollectionContext<
public readonly selection = new UmbSelectionManager(this);
public readonly view = new UmbCollectionViewManager(this);

public readOnlyState = new UmbReadOnlyStateManager(this);

#defaultViewAlias: string;
#defaultFilter: Partial<FilterModelType>;

Expand Down
5 changes: 3 additions & 2 deletions src/packages/core/collection/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import './default/collection-default.element.js';
import './collection.element.js';
import './components/index.js';

export * from './default/collection-default.element.js';
export * from './collection-item-picker-modal/index.js';
export * from './collection.element.js';
export * from './components/index.js';
export * from './collection-item-picker-modal/index.js';
export * from './conditions/writable/index.js';
export * from './default/collection-default.element.js';

export * from './default/collection-default.context.js';
export * from './default/collection-default.context-token.js';
Expand Down
7 changes: 6 additions & 1 deletion src/packages/core/collection/manifests.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { manifest as collectionAliasCondition } from './collection-alias.manifest.js';
import { manifest as collectionBulkActionPermissionCondition } from './collection-bulk-action-permission.manifest.js';
import { manifests as conditionManifests } from './conditions/writable/manifests.js';
import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';

export const manifests: Array<ManifestTypes> = [collectionAliasCondition, collectionBulkActionPermissionCondition];
export const manifests: Array<ManifestTypes> = [
collectionAliasCondition,
collectionBulkActionPermissionCondition,
...conditionManifests,
];
3 changes: 2 additions & 1 deletion src/packages/core/collection/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
import type { ManifestCollection } from '@umbraco-cms/backoffice/extension-registry';
import type { UmbPaginationManager } from '@umbraco-cms/backoffice/utils';
import type { UmbPaginationManager, UmbReadOnlyStateManager } from '@umbraco-cms/backoffice/utils';

export interface UmbCollectionBulkActionPermissions {
allowBulkCopy: boolean;
Expand Down Expand Up @@ -46,4 +46,5 @@ export interface UmbCollectionContext {
pagination: UmbPaginationManager;
items: Observable<any[]>;
totalItems: Observable<number>;
readOnlyState?: UmbReadOnlyStateManager;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import type { UmbCollectionBulkActionPermissions, UmbCollectionConfiguration } from '../../collection/types.js';
import { UMB_CONTENT_PROPERTY_DATASET_CONTEXT } from '../property-dataset-context/content-property-dataset.context.token.js';
import type { UmbContentPropertyDatasetContext } from '../property-dataset-context/index.js';
import { UMB_CONTENT_COLLECTION_WORKSPACE_CONTEXT } from './content-collection-workspace.context.token.js';
import { customElement, html, nothing, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UmbDataTypeDetailRepository } from '@umbraco-cms/backoffice/data-type';
import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
import type { UmbDataTypeDetailModel } from '@umbraco-cms/backoffice/data-type';
import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/extension-registry';

const elementName = 'umb-content-collection-workspace-view';
@customElement(elementName)
export class UmbContentCollectionWorkspaceViewElement extends UmbLitElement implements UmbWorkspaceViewElement {
@state()
private _loading = true;

@state()
private _config?: UmbCollectionConfiguration;

@state()
private _collectionAlias?: string;

@state()
private _documentUnique?: string;

@state()
_readOnly = false;

#dataTypeDetailRepository = new UmbDataTypeDetailRepository(this);
#datasetContext?: UmbContentPropertyDatasetContext;

constructor() {
super();
this.#observeConfig();

this.consumeContext(UMB_CONTENT_PROPERTY_DATASET_CONTEXT, (context) => {
this.#datasetContext = context;
this.#observeReadOnlyDataset();
});
}

async #observeConfig() {
this.consumeContext(UMB_CONTENT_COLLECTION_WORKSPACE_CONTEXT, (workspaceContext) => {
this._collectionAlias = workspaceContext.getCollectionAlias();
this._documentUnique = workspaceContext.getUnique() ?? '';

this.observe(
workspaceContext.structure.ownerContentType,
async (contentType) => {
if (!contentType || !contentType.collection) return;

const dataTypeUnique = contentType.collection.unique;

if (dataTypeUnique) {
await this.#dataTypeDetailRepository.requestByUnique(dataTypeUnique);
this.observe(
await this.#dataTypeDetailRepository.byUnique(dataTypeUnique),
(dataType) => {
if (!dataType) return;
this._config = this.#mapDataTypeConfigToCollectionConfig(dataType);
this._loading = false;
},
'_observeConfigDataType',
);
}
},
'_observeConfigContentType',
);
});
}

#mapDataTypeConfigToCollectionConfig(dataType: UmbDataTypeDetailModel): UmbCollectionConfiguration {
const config = new UmbPropertyEditorConfigCollection(dataType.values);
const pageSize = Number(config.getValueByAlias('pageSize'));
return {
unique: this._documentUnique,
allowedEntityBulkActions: config?.getValueByAlias<UmbCollectionBulkActionPermissions>('bulkActionPermissions'),
layouts: config?.getValueByAlias('layouts'),
orderBy: config?.getValueByAlias('orderBy') ?? 'updateDate',
orderDirection: config?.getValueByAlias('orderDirection') ?? 'asc',
pageSize: isNaN(pageSize) ? 50 : pageSize,
userDefinedProperties: config?.getValueByAlias('includeProperties'),
};
}

#observeReadOnlyDataset() {
if (!this.#datasetContext) return;
this.observe(
this.#datasetContext.currentVariantCultureIsReadOnly,
(isReadOnly) => {
this._readOnly = isReadOnly;
},
'umbIObserveReadOnlyDataset',
);
}

override render() {
if (this._loading) return nothing;
return html`<umb-collection
.alias=${this._collectionAlias}
.config=${this._config}
?readonly=${this._readOnly}></umb-collection>`;
}
}

export { UmbContentCollectionWorkspaceViewElement as element };

declare global {
interface HTMLElementTagNameMap {
[elementName]: UmbContentCollectionWorkspaceViewElement;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { UmbContentCollectionWorkspaceContext } from './types.js';
import type { UmbContentTypeModel } from '@umbraco-cms/backoffice/content-type';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import type { UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace';

export const UMB_CONTENT_COLLECTION_WORKSPACE_CONTEXT = new UmbContextToken<
UmbWorkspaceContext,
UmbContentCollectionWorkspaceContext<UmbContentTypeModel>
>(
'UmbWorkspaceContext',
undefined,
(context): context is UmbContentCollectionWorkspaceContext<UmbContentTypeModel> =>
(context as UmbContentCollectionWorkspaceContext<UmbContentTypeModel>).contentTypeHasCollection !== undefined,
);
21 changes: 21 additions & 0 deletions src/packages/core/content/collection/manifests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { UmbBackofficeManifestKind } from '@umbraco-cms/backoffice/extension-registry';

export const manifests: Array<UmbBackofficeManifestKind> = [
{
type: 'kind',
alias: 'Umb.Kind.WorkspaceView.ContentCollection',
matchKind: 'contentCollection',
matchType: 'workspaceView',
manifest: {
type: 'workspaceView',
kind: 'contentCollection',
element: () => import('./content-collection-workspace-view.element.js'),
weight: 300,
meta: {
label: 'Collection',
pathname: 'collection',
icon: 'icon-grid',
},
},
},
];
8 changes: 8 additions & 0 deletions src/packages/core/content/collection/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { UmbContentTypeModel } from '@umbraco-cms/backoffice/content-type';
import type { UmbReadOnlyVariantStateManager } from '@umbraco-cms/backoffice/utils';
import type { UmbCollectionWorkspaceContext } from '@umbraco-cms/backoffice/workspace';

export interface UmbContentCollectionWorkspaceContext<ContentType extends UmbContentTypeModel>
extends UmbCollectionWorkspaceContext<ContentType> {
readOnlyState: UmbReadOnlyVariantStateManager;
}
2 changes: 2 additions & 0 deletions src/packages/core/content/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ export { UMB_CONTENT_PROPERTY_CONTEXT } from './content-property.context-token.j
export { UmbContentPropertyContext } from './content-property.context.js';
export * from './property-dataset-context/content-property-dataset.context.js';
export * from './workspace/index.js';

export * from './collection/types.js';
3 changes: 2 additions & 1 deletion src/packages/core/content/manifests.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { manifests as workspaceManifests } from './workspace/manifests.js';
import { manifests as collectionManifests } from './collection/manifests.js';

export const manifests = [...workspaceManifests];
export const manifests = [...workspaceManifests, ...collectionManifests];
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { UmbContentPropertyDatasetContext } from './content-property-dataset.context.js';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';

export const UMB_CONTENT_PROPERTY_DATASET_CONTEXT = new UmbContextToken<UmbContentPropertyDatasetContext>(
'UmbPropertyDatasetContext',
);
2 changes: 2 additions & 0 deletions src/packages/core/content/property-dataset-context/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './content-property-dataset.context.js';
export * from './content-property-dataset.context.token.js';
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { UmbState } from './state.manager.js';
import { UmbStateManager } from './state.manager.js';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';

export class UmbReadOnlyStateManager<StateType extends UmbState> extends UmbStateManager<StateType> {
export class UmbReadOnlyStateManager<StateType extends UmbState = UmbState> extends UmbStateManager<StateType> {
constructor(host: UmbControllerHost) {
super(host);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { UMB_COLLECTION_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/worksp
import type { UmbDataTypeDetailModel } from '@umbraco-cms/backoffice/data-type';
import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/extension-registry';

// TODO: clean up this code and make it more generic
/* Be aware this code has been duplicated in the UmbContentCollectionWorkspaceViewElement as is should only be specific for collections based on a content type.
This element is currently no longer in use in the code base but we will need it in the future with a different implementation without knowledge of content and data types */
@customElement('umb-workspace-view-collection')
export class UmbWorkspaceViewCollectionElement extends UmbLitElement implements UmbWorkspaceViewElement {
@state()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { UMB_COLLECTION_ALIAS_CONDITION } from '@umbraco-cms/backoffice/collection';
import {
UMB_COLLECTION_ALIAS_CONDITION,
UMB_WRITABLE_COLLECTION_CONDITION_ALIAS,
} from '@umbraco-cms/backoffice/collection';
import type { ManifestCollectionAction, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';

export const createManifest: ManifestCollectionAction = {
Expand All @@ -16,6 +19,9 @@ export const createManifest: ManifestCollectionAction = {
alias: UMB_COLLECTION_ALIAS_CONDITION,
match: 'Umb.Collection.Document',
},
{
alias: UMB_WRITABLE_COLLECTION_CONDITION_ALIAS,
},
],
};

Expand Down
Loading
Loading