* fix multiple text string validation * notify about messages * cherry picked fix * protection again unnecessary calls * json path cherry pick + tests * validation message change lock * cherry pick from control lifecycle * optimization * propagate errors * cherry picked sync * query umb-input-multiple-text-string * remove unused import * remove optional chain expression * use ! * outcomment the error handling * outcomment more promise rejection error * Fixed issue with multi URL picker. * remove unesecary warning --------- Co-authored-by: Andy Butland <abutland73@gmail.com>
This commit is contained in:
@@ -374,8 +374,6 @@ export default {
|
||||
fileSecurityValidationFailure: 'One or more file security validations have failed',
|
||||
moveToSameFolderFailed: 'Parent and destination folders cannot be the same',
|
||||
uploadNotAllowed: 'Upload is not allowed in this location.',
|
||||
noticeExtensionsServerOverride:
|
||||
'Regardless of the allowed file types, the following limitations apply system-wide due to the server configuration:',
|
||||
},
|
||||
member: {
|
||||
'2fa': 'Two-Factor Authentication',
|
||||
@@ -899,7 +897,6 @@ export default {
|
||||
retrieve: 'Retrieve',
|
||||
retry: 'Retry',
|
||||
rights: 'Permissions',
|
||||
serverConfiguration: 'Server Configuration',
|
||||
scheduledPublishing: 'Scheduled Publishing',
|
||||
umbracoInfo: 'Umbraco info',
|
||||
search: 'Search',
|
||||
|
||||
@@ -48,8 +48,9 @@ export class UmbContextProviderController<
|
||||
|
||||
public override destroy(): void {
|
||||
if (this.#host) {
|
||||
this.#host.removeUmbController(this);
|
||||
const host = this.#host;
|
||||
(this.#host as unknown) = undefined;
|
||||
host.removeUmbController(this);
|
||||
}
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
@@ -173,12 +173,13 @@ export class UmbPropertyEditorUIBlockListElement
|
||||
this.observe(
|
||||
this.#managerContext.layouts,
|
||||
(layouts) => {
|
||||
const validationMessagesToRemove: string[] = [];
|
||||
const contentKeys = layouts.map((x) => x.contentKey);
|
||||
this.#validationContext.messages.getMessagesOfPathAndDescendant('$.contentData').forEach((message) => {
|
||||
// get the KEY from this string: $.contentData[?(@.key == 'KEY')]
|
||||
const key = extractJsonQueryProps(message.path).key;
|
||||
if (key && contentKeys.indexOf(key) === -1) {
|
||||
this.#validationContext.messages.removeMessageByKey(message.key);
|
||||
validationMessagesToRemove.push(message.key);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -187,9 +188,12 @@ export class UmbPropertyEditorUIBlockListElement
|
||||
// get the key from this string: $.settingsData[?(@.key == 'KEY')]
|
||||
const key = extractJsonQueryProps(message.path).key;
|
||||
if (key && settingsKeys.indexOf(key) === -1) {
|
||||
this.#validationContext.messages.removeMessageByKey(message.key);
|
||||
validationMessagesToRemove.push(message.key);
|
||||
}
|
||||
});
|
||||
|
||||
// Remove the messages after the loop to prevent changing the array while iterating over it.
|
||||
this.#validationContext.messages.removeMessageByKeys(validationMessagesToRemove);
|
||||
},
|
||||
null,
|
||||
);
|
||||
|
||||
@@ -33,6 +33,26 @@ export class UmbValidationMessagesManager {
|
||||
this.messages.subscribe((x) => console.log(logName, x));
|
||||
}
|
||||
|
||||
getMessages(): Array<UmbValidationMessage> {
|
||||
return this.#messages.getValue();
|
||||
}
|
||||
|
||||
#updateLock = 0;
|
||||
initiateChange() {
|
||||
this.#updateLock++;
|
||||
this.#messages.mute();
|
||||
// TODO: When ready enable this code will enable handling a finish automatically by this implementation 'using myState.initiatePropertyValueChange()' (Relies on TS support of Using) [NL]
|
||||
/*return {
|
||||
[Symbol.dispose]: this.finishPropertyValueChange,
|
||||
};*/
|
||||
}
|
||||
finishChange() {
|
||||
this.#updateLock--;
|
||||
if (this.#updateLock === 0) {
|
||||
this.#messages.unmute();
|
||||
}
|
||||
}
|
||||
|
||||
getHasAnyMessages(): boolean {
|
||||
return this.#messages.getValue().length !== 0;
|
||||
}
|
||||
@@ -75,7 +95,9 @@ export class UmbValidationMessagesManager {
|
||||
if (this.#messages.getValue().find((x) => x.type === type && x.path === path && x.body === body)) {
|
||||
return;
|
||||
}
|
||||
this.initiateChange();
|
||||
this.#messages.appendOne({ type, key, path, body: body });
|
||||
this.finishChange();
|
||||
}
|
||||
|
||||
addMessages(type: UmbValidationMessageType, path: string, bodies: Array<string>): void {
|
||||
@@ -86,27 +108,42 @@ export class UmbValidationMessagesManager {
|
||||
const newBodies = bodies.filter(
|
||||
(message) => existingMessages.find((x) => x.type === type && x.path === path && x.body === message) === undefined,
|
||||
);
|
||||
this.initiateChange();
|
||||
this.#messages.append(newBodies.map((body) => ({ type, key: UmbId.new(), path, body })));
|
||||
this.finishChange();
|
||||
}
|
||||
|
||||
removeMessageByKey(key: string): void {
|
||||
this.initiateChange();
|
||||
this.#messages.removeOne(key);
|
||||
this.finishChange();
|
||||
}
|
||||
removeMessageByKeys(keys: Array<string>): void {
|
||||
if (keys.length === 0) return;
|
||||
this.initiateChange();
|
||||
this.#messages.filter((x) => keys.indexOf(x.key) === -1);
|
||||
this.finishChange();
|
||||
}
|
||||
removeMessagesByType(type: UmbValidationMessageType): void {
|
||||
this.initiateChange();
|
||||
this.#messages.filter((x) => x.type !== type);
|
||||
this.finishChange();
|
||||
}
|
||||
removeMessagesByPath(path: string): void {
|
||||
this.initiateChange();
|
||||
this.#messages.filter((x) => x.path !== path);
|
||||
this.finishChange();
|
||||
}
|
||||
removeMessagesAndDescendantsByPath(path: string): void {
|
||||
this.initiateChange();
|
||||
this.#messages.filter((x) => MatchPathOrDescendantPath(x.path, path));
|
||||
this.finishChange();
|
||||
}
|
||||
removeMessagesByTypeAndPath(type: UmbValidationMessageType, path: string): void {
|
||||
//path = path.toLowerCase();
|
||||
this.initiateChange();
|
||||
this.#messages.filter((x) => !(x.type === type && x.path === path));
|
||||
this.finishChange();
|
||||
}
|
||||
|
||||
#translatePath(path: string): string | undefined {
|
||||
@@ -124,6 +161,7 @@ export class UmbValidationMessagesManager {
|
||||
|
||||
#translators: Array<UmbValidationMessageTranslator> = [];
|
||||
addTranslator(translator: UmbValidationMessageTranslator): void {
|
||||
this.initiateChange();
|
||||
if (this.#translators.indexOf(translator) === -1) {
|
||||
this.#translators.push(translator);
|
||||
}
|
||||
@@ -137,6 +175,7 @@ export class UmbValidationMessagesManager {
|
||||
this.#messages.updateOne(msg.key, { path: newPath });
|
||||
}
|
||||
}
|
||||
this.finishChange();
|
||||
}
|
||||
|
||||
removeTranslator(translator: UmbValidationMessageTranslator): void {
|
||||
|
||||
@@ -35,8 +35,6 @@ export class UmbFormControlValidator extends UmbControllerBase implements UmbVal
|
||||
}
|
||||
});
|
||||
this.#control = formControl;
|
||||
this.#control.addEventListener(UmbValidationInvalidEvent.TYPE, this.#setInvalid);
|
||||
this.#control.addEventListener(UmbValidationValidEvent.TYPE, this.#setValid);
|
||||
}
|
||||
|
||||
get isValid(): boolean {
|
||||
@@ -82,12 +80,18 @@ export class UmbFormControlValidator extends UmbControllerBase implements UmbVal
|
||||
|
||||
override hostConnected(): void {
|
||||
super.hostConnected();
|
||||
this.#control.addEventListener(UmbValidationInvalidEvent.TYPE, this.#setInvalid);
|
||||
this.#control.addEventListener(UmbValidationValidEvent.TYPE, this.#setValid);
|
||||
if (this.#context) {
|
||||
this.#context.addValidator(this);
|
||||
}
|
||||
}
|
||||
override hostDisconnected(): void {
|
||||
super.hostDisconnected();
|
||||
if (this.#control) {
|
||||
this.#control.removeEventListener(UmbValidationInvalidEvent.TYPE, this.#setInvalid);
|
||||
this.#control.removeEventListener(UmbValidationValidEvent.TYPE, this.#setValid);
|
||||
}
|
||||
if (this.#context) {
|
||||
this.#context.removeValidator(this);
|
||||
// Remove any messages that this validator has added:
|
||||
@@ -99,11 +103,9 @@ export class UmbFormControlValidator extends UmbControllerBase implements UmbVal
|
||||
}
|
||||
|
||||
override destroy(): void {
|
||||
super.destroy();
|
||||
if (this.#control) {
|
||||
this.#control.removeEventListener(UmbValidationInvalidEvent.TYPE, this.#setInvalid);
|
||||
this.#control.removeEventListener(UmbValidationValidEvent.TYPE, this.#setValid);
|
||||
this.#control = undefined as any;
|
||||
}
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,11 +122,12 @@ export class UmbValidationController extends UmbControllerBase implements UmbVal
|
||||
this.observe(
|
||||
parent.messages.messagesOfPathAndDescendant(dataPath),
|
||||
(msgs) => {
|
||||
this.messages.initiateChange();
|
||||
//this.messages.appendMessages(msgs);
|
||||
if (this.#parentMessages) {
|
||||
// Remove the local messages that does not exist in the parent anymore:
|
||||
const toRemove = this.#parentMessages.filter((msg) => !msgs.find((m) => m.key === msg.key));
|
||||
this.#parent!.messages.removeMessageByKeys(toRemove.map((msg) => msg.key));
|
||||
this.messages.removeMessageByKeys(toRemove.map((msg) => msg.key));
|
||||
}
|
||||
this.#parentMessages = msgs;
|
||||
msgs.forEach((msg) => {
|
||||
@@ -139,6 +140,7 @@ export class UmbValidationController extends UmbControllerBase implements UmbVal
|
||||
// Notice, the local message uses the same key. [NL]
|
||||
this.messages.addMessage(msg.type, path, msg.body, msg.key);
|
||||
});
|
||||
this.messages.finishChange();
|
||||
},
|
||||
'observeParentMessages',
|
||||
);
|
||||
@@ -147,6 +149,9 @@ export class UmbValidationController extends UmbControllerBase implements UmbVal
|
||||
this.messages.messages,
|
||||
(msgs) => {
|
||||
if (!this.#parent) return;
|
||||
|
||||
this.#parent!.messages.initiateChange();
|
||||
|
||||
//this.messages.appendMessages(msgs);
|
||||
if (this.#localMessages) {
|
||||
// Remove the parent messages that does not exist locally anymore:
|
||||
@@ -165,6 +170,8 @@ export class UmbValidationController extends UmbControllerBase implements UmbVal
|
||||
// Notice, the parent message uses the same key. [NL]
|
||||
this.#parent!.messages.addMessage(msg.type, path, msg.body, msg.key);
|
||||
});
|
||||
|
||||
this.#parent!.messages.finishChange();
|
||||
},
|
||||
'observeLocalMessages',
|
||||
);
|
||||
@@ -172,6 +179,19 @@ export class UmbValidationController extends UmbControllerBase implements UmbVal
|
||||
// Notice skipHost ^^, this is because we do not want it to consume it self, as this would be a match for this consumption, instead we will look at the parent and above. [NL]
|
||||
}
|
||||
|
||||
override hostConnected(): void {
|
||||
super.hostConnected();
|
||||
if (this.#parent) {
|
||||
this.#parent.addValidator(this);
|
||||
}
|
||||
}
|
||||
override hostDisconnected(): void {
|
||||
super.hostDisconnected();
|
||||
if (this.#parent) {
|
||||
this.#parent.removeValidator(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if this context is valid.
|
||||
* Notice this does not verify the validity.
|
||||
@@ -229,18 +249,27 @@ export class UmbValidationController extends UmbControllerBase implements UmbVal
|
||||
() => false,
|
||||
);
|
||||
|
||||
if (!this.messages) {
|
||||
/*if (this.#validators.length === 0 && resultsStatus === false) {
|
||||
throw new Error('No validators to validate, but validation failed');
|
||||
}*/
|
||||
|
||||
if (this.messages === undefined) {
|
||||
// This Context has been destroyed while is was validating, so we should not continue.
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
const hasMessages = this.messages.getHasAnyMessages();
|
||||
|
||||
// If we have any messages then we are not valid, otherwise lets check the validation results: [NL]
|
||||
// This enables us to keep client validations though UI is not present anymore — because the client validations got defined as messages. [NL]
|
||||
const isValid = this.messages.getHasAnyMessages() ? false : resultsStatus;
|
||||
const isValid = hasMessages ? false : resultsStatus;
|
||||
|
||||
this.#isValid = isValid;
|
||||
|
||||
if (isValid === false) {
|
||||
/*if (hasMessages === false && resultsStatus === false) {
|
||||
throw new Error('Missing validation messages to represent why a child validation context is invalid.');
|
||||
}*/
|
||||
// Focus first invalid element:
|
||||
this.focusFirstInvalidElement();
|
||||
return Promise.reject();
|
||||
@@ -278,6 +307,7 @@ export class UmbValidationController extends UmbControllerBase implements UmbVal
|
||||
}
|
||||
|
||||
override destroy(): void {
|
||||
this.#providerCtrl?.destroy();
|
||||
this.#providerCtrl = undefined;
|
||||
if (this.#parent) {
|
||||
this.#parent.removeValidator(this);
|
||||
|
||||
@@ -4,8 +4,13 @@
|
||||
* @param {string} path - the JSON path to the value that should be found
|
||||
* @returns {unknown} - the found value.
|
||||
*/
|
||||
export function GetValueByJsonPath(data: unknown, path: string): unknown {
|
||||
export function GetValueByJsonPath<ReturnType = unknown>(data: unknown, path: string): ReturnType | undefined {
|
||||
if (path === '$') return data as ReturnType;
|
||||
// strip $ from the path:
|
||||
if (path.startsWith('$[')) {
|
||||
return _GetNextArrayEntryFromPath(data as Array<unknown>, path.slice(2));
|
||||
}
|
||||
|
||||
const strippedPath = path.startsWith('$.') ? path.slice(2) : path;
|
||||
// get value from the path:
|
||||
return GetNextPropertyValueFromPath(data, strippedPath);
|
||||
@@ -33,49 +38,62 @@ function GetNextPropertyValueFromPath(data: any, path: string): any {
|
||||
const value = data[key];
|
||||
// if there is no rest of the path, return the value:
|
||||
if (rest === undefined) return value;
|
||||
|
||||
// if the value is an array, get the value at the index:
|
||||
if (Array.isArray(value)) {
|
||||
// get the value until the next ']', the value can be anything in between the brackets:
|
||||
const lookupEnd = rest.match(/\]/);
|
||||
if (!lookupEnd) return undefined;
|
||||
// get everything before the match:
|
||||
const entryPointer = rest.slice(0, lookupEnd.index);
|
||||
|
||||
// check if the entryPointer is a JSON Path Filter ( starting with ?( and ending with ) ):
|
||||
if (entryPointer.startsWith('?(') && entryPointer.endsWith(')')) {
|
||||
// get the filter from the entryPointer:
|
||||
// get the filter as a function:
|
||||
const jsFilter = JsFilterFromJsonPathFilter(entryPointer);
|
||||
// find the index of the value that matches the filter:
|
||||
const index = value.findIndex(jsFilter[0]);
|
||||
// if the index is -1, return undefined:
|
||||
if (index === -1) return undefined;
|
||||
// get the value at the index:
|
||||
const data = value[index];
|
||||
// Check for safety:
|
||||
if (lookupEnd.index === undefined || lookupEnd.index + 1 >= rest.length) {
|
||||
return data;
|
||||
}
|
||||
// continue with the rest of the path:
|
||||
return GetNextPropertyValueFromPath(data, rest.slice(lookupEnd.index + 2)) ?? data;
|
||||
} else {
|
||||
// get the value at the index:
|
||||
const indexAsNumber = parseInt(entryPointer);
|
||||
if (isNaN(indexAsNumber)) return undefined;
|
||||
const data = value[indexAsNumber];
|
||||
// Check for safety:
|
||||
if (lookupEnd.index === undefined || lookupEnd.index + 1 >= rest.length) {
|
||||
return data;
|
||||
}
|
||||
// continue with the rest of the path:
|
||||
return GetNextPropertyValueFromPath(data, rest.slice(lookupEnd.index + 2)) ?? data;
|
||||
}
|
||||
return _GetNextArrayEntryFromPath(value, rest);
|
||||
} else {
|
||||
// continue with the rest of the path:
|
||||
return GetNextPropertyValueFromPath(value, rest);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {object} array - object to traverse for the value.
|
||||
* @param {string} path - the JSON path to the value that should be found, notice without the starting '['
|
||||
* @returns {unknown} - the found value.
|
||||
*/
|
||||
function _GetNextArrayEntryFromPath(array: Array<any>, path: string): any {
|
||||
if (!array) return undefined;
|
||||
|
||||
// get the value until the next ']', the value can be anything in between the brackets:
|
||||
const lookupEnd = path.match(/\]/);
|
||||
if (!lookupEnd) return undefined;
|
||||
// get everything before the match:
|
||||
const entryPointer = path.slice(0, lookupEnd.index);
|
||||
|
||||
// check if the entryPointer is a JSON Path Filter ( starting with ?( and ending with ) ):
|
||||
if (entryPointer.startsWith('?(') && entryPointer.endsWith(')')) {
|
||||
// get the filter from the entryPointer:
|
||||
// get the filter as a function:
|
||||
const jsFilter = JsFilterFromJsonPathFilter(entryPointer);
|
||||
// find the index of the value that matches the filter:
|
||||
const index = array.findIndex(jsFilter[0]);
|
||||
// if the index is -1, return undefined:
|
||||
if (index === -1) return undefined;
|
||||
// get the value at the index:
|
||||
const entryData = array[index];
|
||||
// Check for safety:
|
||||
if (lookupEnd.index === undefined || lookupEnd.index + 1 >= path.length) {
|
||||
return entryData;
|
||||
}
|
||||
// continue with the rest of the path:
|
||||
return GetNextPropertyValueFromPath(entryData, path.slice(lookupEnd.index + 2)) ?? entryData;
|
||||
} else {
|
||||
// get the value at the index:
|
||||
const indexAsNumber = parseInt(entryPointer);
|
||||
if (isNaN(indexAsNumber)) return undefined;
|
||||
const entryData = array[indexAsNumber];
|
||||
// Check for safety:
|
||||
if (lookupEnd.index === undefined || lookupEnd.index + 1 >= path.length) {
|
||||
return entryData;
|
||||
}
|
||||
// continue with the rest of the path:
|
||||
return GetNextPropertyValueFromPath(entryData, path.slice(lookupEnd.index + 2)) ?? entryData;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} filter - A JSON Query, limited to filtering features. Do not support other JSON PATH Query features.
|
||||
* @returns {Array<(queryFilter: any) => boolean>} - An array of methods that returns true if the given items property value matches the value of the query.
|
||||
|
||||
@@ -2,6 +2,14 @@ import { expect } from '@open-wc/testing';
|
||||
import { GetValueByJsonPath } from './json-path.function.js';
|
||||
|
||||
describe('UmbJsonPathFunctions', () => {
|
||||
it('retrieves root when path is root', () => {
|
||||
const data = { value: 'test' };
|
||||
const result = GetValueByJsonPath(data, '$') as any;
|
||||
|
||||
expect(result).to.eq(data);
|
||||
expect(result.value).to.eq('test');
|
||||
});
|
||||
|
||||
it('retrieve property value', () => {
|
||||
const result = GetValueByJsonPath({ value: 'test' }, '$.value');
|
||||
|
||||
@@ -34,4 +42,10 @@ describe('UmbJsonPathFunctions', () => {
|
||||
|
||||
expect(result).to.eq('test');
|
||||
});
|
||||
|
||||
it('query of array in root', () => {
|
||||
const result = GetValueByJsonPath([{ id: '123', value: 'test' }], "$[?(@.id == '123')].value");
|
||||
|
||||
expect(result).to.eq('test');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,4 +19,10 @@ describe('ReplaceStartOfPath', () => {
|
||||
|
||||
expect(result).to.eq('$');
|
||||
});
|
||||
|
||||
it('replaces the root character with root character', () => {
|
||||
const result = ReplaceStartOfPath('$.start.test', '$', '$');
|
||||
|
||||
expect(result).to.eq('$.start.test');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -74,7 +74,6 @@ export abstract class UmbSubmittableWorkspaceContextBase<WorkspaceDataModelType>
|
||||
* @returns Promise that resolves to void when the validation is complete.
|
||||
*/
|
||||
public async validate(): Promise<Array<void>> {
|
||||
//return this.validation.validate();
|
||||
return Promise.all(this.#validationContexts.map((context) => context.validate()));
|
||||
}
|
||||
|
||||
@@ -97,7 +96,15 @@ export abstract class UmbSubmittableWorkspaceContextBase<WorkspaceDataModelType>
|
||||
async () => {
|
||||
onValid().then(this.#completeSubmit, this.#rejectSubmit);
|
||||
},
|
||||
async () => {
|
||||
async (/*error*/) => {
|
||||
/*if (error) {
|
||||
throw new Error(error);
|
||||
}*/
|
||||
// TODO: Implement developer-mode logging here. [NL]
|
||||
console.warn(
|
||||
'Validation failed because of these validation messages still begin present: ',
|
||||
this.#validationContexts.flatMap((x) => x.messages.getMessages()),
|
||||
);
|
||||
onInvalid().then(this.#resolveSubmit, this.#rejectSubmit);
|
||||
},
|
||||
);
|
||||
@@ -105,7 +112,10 @@ export abstract class UmbSubmittableWorkspaceContextBase<WorkspaceDataModelType>
|
||||
return this.#submitPromise;
|
||||
}
|
||||
|
||||
#rejectSubmit = () => {
|
||||
#rejectSubmit = (/*error: any*/) => {
|
||||
/*if (error) {
|
||||
throw new Error(error);
|
||||
}*/
|
||||
if (this.#submitPromise) {
|
||||
// TODO: Capture the validation contexts messages on open, and then reset to them in this case. [NL]
|
||||
|
||||
|
||||
@@ -11,15 +11,16 @@ import type {
|
||||
import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui';
|
||||
|
||||
import '../components/input-multi-url/index.js';
|
||||
import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
|
||||
|
||||
/**
|
||||
* @element umb-property-editor-ui-multi-url-picker
|
||||
*/
|
||||
@customElement('umb-property-editor-ui-multi-url-picker')
|
||||
export class UmbPropertyEditorUIMultiUrlPickerElement extends UmbLitElement implements UmbPropertyEditorUiElement {
|
||||
@property({ type: Array })
|
||||
value: Array<UmbLinkPickerLink> = [];
|
||||
|
||||
export class UmbPropertyEditorUIMultiUrlPickerElement
|
||||
extends UmbFormControlMixin<Array<UmbLinkPickerLink>, typeof UmbLitElement, undefined>(UmbLitElement)
|
||||
implements UmbPropertyEditorUiElement
|
||||
{
|
||||
public set config(config: UmbPropertyEditorConfigCollection | undefined) {
|
||||
if (!config) return;
|
||||
|
||||
@@ -81,6 +82,7 @@ export class UmbPropertyEditorUIMultiUrlPickerElement extends UmbLitElement impl
|
||||
this,
|
||||
);
|
||||
}
|
||||
this.addFormControlElement(this.shadowRoot!.querySelector('umb-input-multi-url')!);
|
||||
}
|
||||
|
||||
#onChange(event: CustomEvent & { target: UmbInputMultiUrlElement }) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { UmbPropertyEditorUIMultipleTextStringElement } from '../multiple-text-string/property-editor-ui-multiple-text-string.element.js';
|
||||
import { css, customElement, html, nothing, state, when } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { formatBytes } from '@umbraco-cms/backoffice/utils';
|
||||
import { css, customElement, html, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbTemporaryFileConfigRepository } from '@umbraco-cms/backoffice/temporary-file';
|
||||
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/property-editor';
|
||||
import type { UmbTemporaryFileConfigurationModel } from '@umbraco-cms/backoffice/temporary-file';
|
||||
@@ -40,7 +39,8 @@ export class UmbPropertyEditorUIAcceptedUploadTypesElement
|
||||
}
|
||||
|
||||
#addValidators(config: UmbTemporaryFileConfigurationModel) {
|
||||
this._inputElement?.addValidator(
|
||||
const inputElement = this.shadowRoot?.querySelector('umb-input-multiple-text-string');
|
||||
inputElement?.addValidator(
|
||||
'badInput',
|
||||
() => {
|
||||
let message = this.localize.term('validation_invalidExtensions');
|
||||
@@ -53,7 +53,7 @@ export class UmbPropertyEditorUIAcceptedUploadTypesElement
|
||||
return message;
|
||||
},
|
||||
() => {
|
||||
const extensions = this._inputElement?.items;
|
||||
const extensions = inputElement?.items;
|
||||
if (!extensions) return false;
|
||||
if (
|
||||
config.allowedUploadedFileExtensions.length &&
|
||||
@@ -69,49 +69,8 @@ export class UmbPropertyEditorUIAcceptedUploadTypesElement
|
||||
);
|
||||
}
|
||||
|
||||
#renderAcceptedTypes() {
|
||||
if (!this._acceptedTypes.length && !this._disallowedTypes.length && !this._maxFileSize) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<uui-box id="notice" headline=${this.localize.term('general_serverConfiguration')}>
|
||||
<p><umb-localize key="media_noticeExtensionsServerOverride"></umb-localize></p>
|
||||
${when(
|
||||
this._acceptedTypes.length,
|
||||
() => html`
|
||||
<p>
|
||||
<umb-localize key="validation_allowedExtensions"></umb-localize>
|
||||
<strong>${this._acceptedTypes.join(', ')}</strong>
|
||||
</p>
|
||||
`,
|
||||
)}
|
||||
${when(
|
||||
this._disallowedTypes.length,
|
||||
() => html`
|
||||
<p>
|
||||
<umb-localize key="validation_disallowedExtensions"></umb-localize>
|
||||
<strong>${this._disallowedTypes.join(', ')}</strong>
|
||||
</p>
|
||||
`,
|
||||
)}
|
||||
${when(
|
||||
this._maxFileSize,
|
||||
() => html`
|
||||
<p>
|
||||
${this.localize.term('media_maxFileSize')}
|
||||
<strong title="${this.localize.number(this._maxFileSize!)} bytes"
|
||||
>${formatBytes(this._maxFileSize!, { decimals: 2 })}</strong
|
||||
>.
|
||||
</p>
|
||||
`,
|
||||
)}
|
||||
</uui-box>
|
||||
`;
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`${this.#renderAcceptedTypes()} ${super.render()}`;
|
||||
return html`${super.render()}`;
|
||||
}
|
||||
|
||||
static override readonly styles = [
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import { customElement, html, property, query, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { umbBindToValidation, UmbValidationContext } from '@umbraco-cms/backoffice/validation';
|
||||
import { customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { umbBindToValidation, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor';
|
||||
import { UMB_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/property';
|
||||
import {
|
||||
UMB_SUBMITTABLE_WORKSPACE_CONTEXT,
|
||||
UmbSubmittableWorkspaceContextBase,
|
||||
} from '@umbraco-cms/backoffice/workspace';
|
||||
import type { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
import type { UmbInputMultipleTextStringElement } from '@umbraco-cms/backoffice/components';
|
||||
import type {
|
||||
@@ -18,10 +14,10 @@ import type {
|
||||
* @element umb-property-editor-ui-multiple-text-string
|
||||
*/
|
||||
@customElement('umb-property-editor-ui-multiple-text-string')
|
||||
export class UmbPropertyEditorUIMultipleTextStringElement extends UmbLitElement implements UmbPropertyEditorUiElement {
|
||||
@property({ type: Array })
|
||||
value?: Array<string>;
|
||||
|
||||
export class UmbPropertyEditorUIMultipleTextStringElement
|
||||
extends UmbFormControlMixin<Array<string>, typeof UmbLitElement, undefined>(UmbLitElement)
|
||||
implements UmbPropertyEditorUiElement
|
||||
{
|
||||
public set config(config: UmbPropertyEditorConfigCollection | undefined) {
|
||||
if (!config) return;
|
||||
|
||||
@@ -65,23 +61,12 @@ export class UmbPropertyEditorUIMultipleTextStringElement extends UmbLitElement
|
||||
@state()
|
||||
private _max = Infinity;
|
||||
|
||||
@query('#input', true)
|
||||
protected _inputElement?: UmbInputMultipleTextStringElement;
|
||||
|
||||
protected _validationContext = new UmbValidationContext(this);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.consumeContext(UMB_PROPERTY_CONTEXT, (context) => {
|
||||
this._label = context.getLabel();
|
||||
});
|
||||
|
||||
this.consumeContext(UMB_SUBMITTABLE_WORKSPACE_CONTEXT, (context) => {
|
||||
if (context instanceof UmbSubmittableWorkspaceContextBase) {
|
||||
context.addValidationContext(this._validationContext);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected override firstUpdated() {
|
||||
@@ -91,6 +76,7 @@ export class UmbPropertyEditorUIMultipleTextStringElement extends UmbLitElement
|
||||
this,
|
||||
);
|
||||
}
|
||||
this.addFormControlElement(this.shadowRoot!.querySelector('umb-input-multiple-text-string')!);
|
||||
}
|
||||
|
||||
#onChange(event: UmbChangeEvent) {
|
||||
@@ -100,31 +86,18 @@ export class UmbPropertyEditorUIMultipleTextStringElement extends UmbLitElement
|
||||
this.dispatchEvent(new UmbPropertyValueChangeEvent());
|
||||
}
|
||||
|
||||
// Prevent valid events from bubbling outside the message element
|
||||
#onValid(event: Event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
// Prevent invalid events from bubbling outside the message element
|
||||
#onInvalid(event: Event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<umb-form-validation-message id="validation-message" @invalid=${this.#onInvalid} @valid=${this.#onValid}>
|
||||
<umb-input-multiple-text-string
|
||||
id="input"
|
||||
max=${this._max}
|
||||
min=${this._min}
|
||||
.items=${this.value ?? []}
|
||||
?disabled=${this.disabled}
|
||||
?readonly=${this.readonly}
|
||||
?required=${this.required}
|
||||
@change=${this.#onChange}
|
||||
${umbBindToValidation(this)}>
|
||||
</umb-input-multiple-text-string>
|
||||
</umb-form-validation-message>
|
||||
<umb-input-multiple-text-string
|
||||
max=${this._max}
|
||||
min=${this._min}
|
||||
.items=${this.value ?? []}
|
||||
?disabled=${this.disabled}
|
||||
?readonly=${this.readonly}
|
||||
?required=${this.required}
|
||||
@change=${this.#onChange}
|
||||
${umbBindToValidation(this)}>
|
||||
</umb-input-multiple-text-string>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ export class UmbPropertyEditorUiTiptapElement extends UmbPropertyEditorUiRteElem
|
||||
protected override firstUpdated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {
|
||||
super.firstUpdated(_changedProperties);
|
||||
|
||||
this.addFormControlElement(this.shadowRoot?.querySelector('umb-input-tiptap') as UmbInputTiptapElement);
|
||||
this.addFormControlElement(this.shadowRoot!.querySelector('umb-input-tiptap') as UmbInputTiptapElement);
|
||||
}
|
||||
|
||||
#onChange(event: CustomEvent & { target: UmbInputTiptapElement }) {
|
||||
|
||||
Reference in New Issue
Block a user