implement range validation for areas

This commit is contained in:
Niels Lyngsø
2024-05-24 13:35:53 +02:00
parent 9e3e4b64b7
commit 703e919069
3 changed files with 86 additions and 53 deletions

View File

@@ -138,7 +138,6 @@ export class UmbBlockGridEntriesElement extends UmbFormControlMixin(UmbLitElemen
public set areaKey(value: string | null | undefined) {
this._areaKey = value;
this.#context.setAreaKey(value ?? null);
this.#setupValidation();
}
public get areaKey(): string | null | undefined {
return this._areaKey;
@@ -169,27 +168,43 @@ export class UmbBlockGridEntriesElement extends UmbFormControlMixin(UmbLitElemen
constructor() {
super();
this.observe(this.#context.layoutEntries, (layoutEntries) => {
//const oldValue = this._layoutEntries;
this.#sorter.setModel(layoutEntries);
this._layoutEntries = layoutEntries;
//this.requestUpdate('layoutEntries', oldValue);
});
this.observe(
this.#context.layoutEntries,
(layoutEntries) => {
//const oldValue = this._layoutEntries;
this.#sorter.setModel(layoutEntries);
this._layoutEntries = layoutEntries;
//this.requestUpdate('layoutEntries', oldValue);
},
null,
);
this.observe(this.#context.amountOfAllowedBlockTypes, (length) => {
this._canCreate = length > 0;
if (length === 1) {
this.observe(
this.#context.firstAllowedBlockTypeName(),
(firstAllowedName) => {
this._singleBlockTypeName = firstAllowedName;
},
'observeSingleBlockTypeName',
);
} else {
this.removeUmbControllerByAlias('observeSingleBlockTypeName');
}
});
this.observe(
this.#context.amountOfAllowedBlockTypes,
(length) => {
this._canCreate = length > 0;
if (length === 1) {
this.observe(
this.#context.firstAllowedBlockTypeName(),
(firstAllowedName) => {
this._singleBlockTypeName = firstAllowedName;
},
'observeSingleBlockTypeName',
);
} else {
this.removeUmbControllerByAlias('observeSingleBlockTypeName');
}
},
null,
);
this.observe(
this.#context.rangeLimits,
(rangeLimits) => {
this.#setupRangeValidation(rangeLimits);
},
null,
);
this.#context.getManager().then((manager) => {
this.observe(
@@ -207,42 +222,25 @@ export class UmbBlockGridEntriesElement extends UmbFormControlMixin(UmbLitElemen
this.#controlValidator = new UmbFormControlValidator(this, this /*, this.#dataPath*/);
}
async #getLimitValidation() {
if (this._areaKey === null) {
// This validation setup is not be as configurable as it should be, but it is a start. Alternatively we should consume the manager and observe the configuration. [NL]
const manager = await this.#context.getManager();
const config = manager.getEditorConfiguration();
const min = config?.getValueByAlias<UmbNumberRangeValueType>('validationLimit')?.min ?? 0;
const max = config?.getValueByAlias<UmbNumberRangeValueType>('validationLimit')?.max ?? Infinity;
return { min, max };
} else {
return { min: 3, max: 4 };
}
}
#rangeUnderflowValidator?: UmbFormControlValidatorConfig;
#rangeOverflowValidator?: UmbFormControlValidatorConfig;
async #setupValidation() {
const rangeLimit = await this.#getLimitValidation();
console.log('setup validation', this);
async #setupRangeValidation(rangeLimit: UmbNumberRangeValueType | undefined) {
if (this.#rangeUnderflowValidator) {
this.removeValidator(this.#rangeUnderflowValidator);
this.#rangeUnderflowValidator = undefined;
}
if (rangeLimit.min !== 0) {
if (rangeLimit?.min !== 0) {
this.#rangeUnderflowValidator = this.addValidator(
'rangeUnderflow',
() => {
return this.localize.term(
'validation_entriesShort',
rangeLimit.min,
rangeLimit.min - (this._layoutEntries.length ?? 0),
rangeLimit!.min,
(rangeLimit!.min ?? 0) - this._layoutEntries.length,
);
},
() => {
return (this._layoutEntries.length ?? 0) < rangeLimit.min;
return this._layoutEntries.length < (rangeLimit?.min ?? 0);
},
);
}
@@ -251,18 +249,18 @@ export class UmbBlockGridEntriesElement extends UmbFormControlMixin(UmbLitElemen
this.removeValidator(this.#rangeOverflowValidator);
this.#rangeOverflowValidator = undefined;
}
if (rangeLimit.max !== Infinity) {
if (rangeLimit?.max !== Infinity) {
this.#rangeOverflowValidator = this.addValidator(
'rangeOverflow',
() => {
return this.localize.term(
'validation_entriesExceed',
rangeLimit.max,
(this._layoutEntries.length ?? 0) - rangeLimit.max,
rangeLimit!.max,
this._layoutEntries.length - (rangeLimit!.max ?? this._layoutEntries.length),
);
},
() => {
return (this._layoutEntries.length ?? 0) > rangeLimit.max;
return (this._layoutEntries.length ?? 0) > (rangeLimit?.max ?? Infinity);
},
);
}

View File

@@ -4,10 +4,11 @@ import { UMB_BLOCK_GRID_ENTRY_CONTEXT, type UmbBlockGridWorkspaceData } from '..
import type { UmbBlockGridLayoutModel, UmbBlockGridTypeAreaType, UmbBlockGridTypeModel } from '../types.js';
import { UMB_BLOCK_GRID_MANAGER_CONTEXT } from './block-grid-manager.context.js';
import type { UmbBlockGridScalableContainerContext } from './block-grid-scale-manager/block-grid-scale-manager.controller.js';
import { UmbArrayState, UmbNumberState, UmbStringState } from '@umbraco-cms/backoffice/observable-api';
import { UmbArrayState, UmbNumberState, UmbObjectState, UmbStringState } from '@umbraco-cms/backoffice/observable-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal';
import { pathFolderName } from '@umbraco-cms/backoffice/utils';
import type { UmbNumberRangeValueType } from '@umbraco-cms/backoffice/models';
export class UmbBlockGridEntriesContext
extends UmbBlockEntriesContext<
@@ -31,6 +32,9 @@ export class UmbBlockGridEntriesContext
#parentUnique?: string | null;
#areaKey?: string | null;
#rangeLimits = new UmbObjectState<UmbNumberRangeValueType | undefined>(undefined);
readonly rangeLimits = this.#rangeLimits.asObservable();
#allowedBlockTypes = new UmbArrayState<UmbBlockGridTypeModel>([], (x) => x.contentElementTypeKey);
public readonly allowedBlockTypes = this.#allowedBlockTypes.asObservable();
public readonly amountOfAllowedBlockTypes = this.#allowedBlockTypes.asObservablePart((x) => x.length);
@@ -121,6 +125,7 @@ export class UmbBlockGridEntriesContext
if (!this._manager) return;
this.#getAllowedBlockTypes();
this.#getRangeLimits();
this.observe(
this._manager.propertyAlias,
@@ -185,6 +190,7 @@ export class UmbBlockGridEntriesContext
this.removeUmbControllerByAlias('observeAreaType');
this.#getAllowedBlockTypes();
this.#getRangeLimits();
} else {
if (!this.#parentEntry) return;
@@ -228,6 +234,7 @@ export class UmbBlockGridEntriesContext
hostEl.style.setProperty('--umb-block-grid--area-column-span', areaType?.columnSpan?.toString() ?? '');
hostEl.style.setProperty('--umb-block-grid--area-row-span', areaType?.rowSpan?.toString() ?? '');
this.#getAllowedBlockTypes();
this.#getRangeLimits();
},
'observeAreaType',
);
@@ -235,10 +242,14 @@ export class UmbBlockGridEntriesContext
}
#getAllowedBlockTypes() {
if (this.#areaKey === undefined || !this._manager) return;
if (!this._manager) return;
this.#allowedBlockTypes.setValue(this.#retrieveAllowedElementTypes());
}
#getRangeLimits() {
if (!this._manager) return;
const range = this.#retrieveRangeLimits();
this.#rangeLimits.setValue(range);
}
getPathForCreateBlock(index: number) {
return this._catalogueRouteBuilderState.getValue()?.({ view: 'create', index: index });
@@ -324,10 +335,34 @@ export class UmbBlockGridEntriesContext
// No specific permissions setup, so we will fallback to items allowed in areas:
return this._manager.getBlockTypes().filter((x) => x.allowInAreas);
} else if (this.#areaKey === null) {
// If AreaKey is null, then we are in the root, looking for items allowed as root:
return this._manager.getBlockTypes().filter((x) => x.allowAtRoot);
}
// If no AreaKey, then we are in the root, looking for items allowed as root:
return this._manager.getBlockTypes().filter((x) => x.allowAtRoot);
return [];
}
/**
* @internal
* @returns an NumberRange of the min and max allowed items in the current area. Or undefined if not ready jet.
*/
#retrieveRangeLimits(): UmbNumberRangeValueType | undefined {
if (this.#areaKey != null) {
// Area entries:
if (!this.#areaType) return undefined;
return { min: this.#areaType.minAllowed ?? 0, max: this.#areaType.maxAllowed ?? Infinity };
} else if (this.#areaKey === null) {
if (!this._manager) return undefined;
const config = this._manager.getEditorConfiguration();
const min = config?.getValueByAlias<UmbNumberRangeValueType>('validationLimit')?.min ?? 0;
const max = config?.getValueByAlias<UmbNumberRangeValueType>('validationLimit')?.max ?? Infinity;
return { min, max };
}
return undefined;
}
/**

View File

@@ -27,7 +27,7 @@ type FlagTypes =
| 'valid';
// Acceptable as an internal interface/type, BUT if exposed externally this should be turned into a public interface in a separate file.
interface UmbFormControlValidatorConfig {
export interface UmbFormControlValidatorConfig {
flagKey: FlagTypes;
getMessageMethod: () => string;
checkMethod: () => boolean;