Block Area Rules
This commit is contained in:
@@ -135,6 +135,9 @@ export class UmbBlockGridEntriesElement extends UmbFormControlMixin(UmbLitElemen
|
||||
|
||||
#context = new UmbBlockGridEntriesContext(this);
|
||||
#controlValidator?: UmbFormControlValidator;
|
||||
#typeLimitValidator?: UmbFormControlValidatorConfig;
|
||||
#rangeUnderflowValidator?: UmbFormControlValidatorConfig;
|
||||
#rangeOverflowValidator?: UmbFormControlValidatorConfig;
|
||||
|
||||
@property({ type: String, attribute: 'area-key', reflect: true })
|
||||
public set areaKey(value: string | null | undefined) {
|
||||
@@ -217,6 +220,14 @@ export class UmbBlockGridEntriesElement extends UmbFormControlMixin(UmbLitElemen
|
||||
null,
|
||||
);
|
||||
|
||||
this.observe(
|
||||
this.#context.hasTypeLimits,
|
||||
(hasTypeLimits) => {
|
||||
this.#setupBlockTypeLimitValidation(hasTypeLimits);
|
||||
},
|
||||
null,
|
||||
);
|
||||
|
||||
this.#context.getManager().then((manager) => {
|
||||
this.observe(
|
||||
manager.layoutStylesheet,
|
||||
@@ -233,8 +244,6 @@ export class UmbBlockGridEntriesElement extends UmbFormControlMixin(UmbLitElemen
|
||||
new UmbFormControlValidator(this, this /*, this.#dataPath*/);
|
||||
}
|
||||
|
||||
#rangeUnderflowValidator?: UmbFormControlValidatorConfig;
|
||||
#rangeOverflowValidator?: UmbFormControlValidatorConfig;
|
||||
async #setupRangeValidation(rangeLimit: UmbNumberRangeValueType | undefined) {
|
||||
if (this.#rangeUnderflowValidator) {
|
||||
this.removeValidator(this.#rangeUnderflowValidator);
|
||||
@@ -277,6 +286,38 @@ export class UmbBlockGridEntriesElement extends UmbFormControlMixin(UmbLitElemen
|
||||
}
|
||||
}
|
||||
|
||||
async #setupBlockTypeLimitValidation(hasTypeLimits: boolean | undefined) {
|
||||
if (this.#typeLimitValidator) {
|
||||
this.removeValidator(this.#typeLimitValidator);
|
||||
this.#typeLimitValidator = undefined;
|
||||
}
|
||||
if (hasTypeLimits) {
|
||||
console.log('hasTypeLimits');
|
||||
this.#typeLimitValidator = this.addValidator(
|
||||
'patternMismatch',
|
||||
() => {
|
||||
const invalids = this.#context.getInvalidBlockTypeLimits();
|
||||
return invalids
|
||||
.map((invalidRule) =>
|
||||
this.localize.term(
|
||||
invalidRule.amount < invalidRule.minRequirement
|
||||
? 'blockEditor_areaValidationEntriesShort'
|
||||
: 'blockEditor_areaValidationEntriesExceed',
|
||||
invalidRule.name,
|
||||
invalidRule.amount,
|
||||
invalidRule.minRequirement,
|
||||
invalidRule.maxRequirement,
|
||||
),
|
||||
)
|
||||
.join(', ');
|
||||
},
|
||||
() => {
|
||||
return !this.#context.checkBlockTypeLimitsValidity();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Missing ability to jump directly to creating a Block, when there is only one Block Type. [NL]
|
||||
override render() {
|
||||
return html`
|
||||
|
||||
@@ -8,7 +8,13 @@ import {
|
||||
import type { UmbBlockGridLayoutModel, UmbBlockGridTypeAreaType, UmbBlockGridTypeModel } from '../types.js';
|
||||
import { UMB_BLOCK_GRID_MANAGER_CONTEXT } from './block-grid-manager.context-token.js';
|
||||
import type { UmbBlockGridScalableContainerContext } from './block-grid-scale-manager/block-grid-scale-manager.controller.js';
|
||||
import { UmbArrayState, UmbNumberState, UmbObjectState, UmbStringState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import {
|
||||
UmbArrayState,
|
||||
UmbBooleanState,
|
||||
UmbNumberState,
|
||||
UmbObjectState,
|
||||
UmbStringState,
|
||||
} from '@umbraco-cms/backoffice/observable-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router';
|
||||
import { pathFolderName } from '@umbraco-cms/backoffice/utils';
|
||||
@@ -49,6 +55,9 @@ export class UmbBlockGridEntriesContext
|
||||
public readonly amountOfAllowedBlockTypes = this.#allowedBlockTypes.asObservablePart((x) => x.length);
|
||||
public readonly canCreate = this.#allowedBlockTypes.asObservablePart((x) => x.length > 0);
|
||||
|
||||
#hasTypeLimits = new UmbBooleanState(undefined);
|
||||
public readonly hasTypeLimits = this.#hasTypeLimits.asObservable();
|
||||
|
||||
firstAllowedBlockTypeName() {
|
||||
if (!this._manager) {
|
||||
throw new Error('Manager not ready');
|
||||
@@ -241,8 +250,6 @@ export class UmbBlockGridEntriesContext
|
||||
'observeThisLayouts',
|
||||
);
|
||||
|
||||
this.removeUmbControllerByAlias('observeAreaType');
|
||||
|
||||
const hostEl = this.getHostElement() as HTMLElement | undefined;
|
||||
if (hostEl) {
|
||||
hostEl.removeAttribute('data-area-alias');
|
||||
@@ -309,6 +316,7 @@ export class UmbBlockGridEntriesContext
|
||||
#setupAllowedBlockTypes() {
|
||||
if (!this._manager) return;
|
||||
this.#allowedBlockTypes.setValue(this.#retrieveAllowedElementTypes());
|
||||
this.#setupAllowedBlockTypesLimits();
|
||||
}
|
||||
#setupRangeLimits() {
|
||||
if (!this._manager) return;
|
||||
@@ -429,6 +437,100 @@ export class UmbBlockGridEntriesContext
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
#setupAllowedBlockTypesLimits() {
|
||||
if (!this._manager) return;
|
||||
|
||||
if (this.#areaKey) {
|
||||
// Area entries:
|
||||
if (!this.#areaType) return;
|
||||
|
||||
if (this.#areaType.specifiedAllowance && this.#areaType.specifiedAllowance?.length > 0) {
|
||||
this.#hasTypeLimits.setValue(true);
|
||||
}
|
||||
} else if (this.#areaKey === null) {
|
||||
// RESET
|
||||
}
|
||||
}
|
||||
|
||||
#invalidBlockTypeLimits?: Array<{
|
||||
groupKey?: string;
|
||||
key?: string;
|
||||
name: string;
|
||||
amount: number;
|
||||
minRequirement: number;
|
||||
maxRequirement: number;
|
||||
}>;
|
||||
|
||||
getInvalidBlockTypeLimits() {
|
||||
return this.#invalidBlockTypeLimits ?? [];
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
* @returns {boolean} - True if the block type limits are valid, otherwise false.
|
||||
*/
|
||||
checkBlockTypeLimitsValidity(): boolean {
|
||||
if (!this.#areaType || !this.#areaType.specifiedAllowance) return false;
|
||||
|
||||
const layoutEntries = this._layoutEntries.getValue();
|
||||
|
||||
this.#invalidBlockTypeLimits = [];
|
||||
|
||||
const hasInvalidRules = this.#areaType.specifiedAllowance.some((rule) => {
|
||||
const minAllowed = rule.minAllowed || 0;
|
||||
const maxAllowed = rule.maxAllowed || 0;
|
||||
|
||||
// For block groups:
|
||||
if (rule.groupKey) {
|
||||
const groupElementTypeKeys =
|
||||
this._manager
|
||||
?.getBlockTypes()
|
||||
.filter((blockType) => blockType.groupKey === rule.groupKey && blockType.allowInAreas === true)
|
||||
.map((x) => x.contentElementTypeKey) ?? [];
|
||||
const groupAmount = layoutEntries.filter((entry) => {
|
||||
const contentTypeKey = this._manager!.getContentTypeKeyOf(entry.contentUdi);
|
||||
return contentTypeKey ? groupElementTypeKeys.indexOf(contentTypeKey) !== -1 : false;
|
||||
}).length;
|
||||
|
||||
if (groupAmount < minAllowed || (maxAllowed > 0 && groupAmount > maxAllowed)) {
|
||||
this.#invalidBlockTypeLimits!.push({
|
||||
groupKey: rule.groupKey,
|
||||
name: this._manager!.getBlockGroupName(rule.groupKey) ?? '?',
|
||||
amount: groupAmount,
|
||||
minRequirement: minAllowed,
|
||||
maxRequirement: maxAllowed,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// For specific elementTypes:
|
||||
else if (rule.elementTypeKey) {
|
||||
const amount = layoutEntries.filter((entry) => {
|
||||
const contentTypeKey = this._manager!.getContentOf(entry.contentUdi)?.contentTypeKey;
|
||||
return contentTypeKey === rule.elementTypeKey;
|
||||
}).length;
|
||||
console.log('amount', amount);
|
||||
if (amount < minAllowed || (maxAllowed > 0 ? amount > maxAllowed : false)) {
|
||||
this.#invalidBlockTypeLimits!.push({
|
||||
key: rule.elementTypeKey,
|
||||
name: this._manager!.getContentTypeNameOf(rule.elementTypeKey) ?? '?',
|
||||
amount: amount,
|
||||
minRequirement: minAllowed,
|
||||
maxRequirement: maxAllowed,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Lets fail cause the rule was bad.
|
||||
console.error('Invalid block type limit rule.', rule);
|
||||
return false;
|
||||
});
|
||||
return hasInvalidRules === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given contentUdi is allowed in the current area.
|
||||
* @param contentUdi {string} - The contentUdi of the content to check.
|
||||
|
||||
@@ -62,6 +62,9 @@ export class UmbBlockGridManagerContext<
|
||||
getBlockGroups() {
|
||||
return this.#blockGroups.value;
|
||||
}
|
||||
getBlockGroupName(unique: string) {
|
||||
return this.#blockGroups.getValue().find((group) => group.key === unique)?.name;
|
||||
}
|
||||
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host);
|
||||
|
||||
@@ -148,6 +148,9 @@ export abstract class UmbBlockManagerContext<
|
||||
getContentTypeNameOf(contentTypeKey: string) {
|
||||
return this.#contentTypes.getValue().find((x) => x.unique === contentTypeKey)?.name;
|
||||
}
|
||||
getContentTypeKeyOf(contentTypeKey: string) {
|
||||
return this.#contentTypes.getValue().find((x) => x.unique === contentTypeKey)?.unique;
|
||||
}
|
||||
getContentTypeHasProperties(contentTypeKey: string) {
|
||||
const properties = this.#contentTypes.getValue().find((x) => x.unique === contentTypeKey)?.properties;
|
||||
return properties ? properties.length > 0 : false;
|
||||
|
||||
Reference in New Issue
Block a user