mirror of
https://github.com/google/blockly.git
synced 2026-01-05 08:00:09 +01:00
fix: replace 'AnyDuringMigration' for core/field.ts value functions (#6639)
* chore: replace `AnyDuringMigration` for field value functions * chore: removed unnecessary `KeyboardEvent` comments and `AnyDuringMigration` casts * chore: cleaned up `doValueUpdate_` and `doClassValidation_` in `Field` subclasses * fix: updated `FieldValidator` to allow returning `undefined` and restructured `setValue` * fix: implemented initial `core/field_checkbox.ts` feedback * fix: updated `Field` to accept `U` and `undefined` and reverted subclass constructor handling of the input value * fix: reverted `getVars` to returning `string[]` and added related comment * chore: removed unnecessary comment * fix: updated `processValidation_` to no longer allow returning `undefined` * fix: removed `Un` type alias for `undefined` * chore: removed unnecessary string cast in `core/field_colour.ts` * fix: updated `doClassValidation_` not to expect `null` since it will never come up in `setValue` * fix: updated `doClassValidation_` to only allow `undefined` when the new value exists * Updated `FieldValidator` type to expect `newValue` to exist * cleanup: updated `picker` from type `Element` to type `HTMLElement` * fix: updated `doValueInvalid_` type info in `core/field_input.ts` to handle `string` and `undefined` * fix: reverted `getValue` in `core/field_checkbox.ts` to previous logic * fix: updated the `Field` constructor to allow `value` to be optional * chore: consolidated `Validation` with `FieldValidator` and `doClassValidation_` * fix: reverted generic param `U` while handling diverging user input is being discussed * fix: updated `doClassValidation_` return comment to work for TSDoc * fix: misc keyboard event function tweaks
This commit is contained in:
committed by
GitHub
parent
25d9acb418
commit
0fb64a6772
113
core/field.ts
113
core/field.ts
@@ -45,12 +45,30 @@ import * as WidgetDiv from './widgetdiv.js';
|
||||
import type {WorkspaceSvg} from './workspace_svg.js';
|
||||
import * as Xml from './xml.js';
|
||||
|
||||
export type FieldValidator<T = any> = (value?: T) => T|null|undefined;
|
||||
/**
|
||||
* A function that is called to validate changes to the field's value before
|
||||
* they are set.
|
||||
*
|
||||
* **NOTE:** Validation returns one option between `T`, `null`, and `undefined`.
|
||||
*
|
||||
* @see {@link https://developers.google.com/blockly/guides/create-custom-blocks/fields/validators#return_values}
|
||||
* @param newValue The value to be validated.
|
||||
* @returns One of three instructions for setting the new value: `T`, `null`,
|
||||
* or `undefined`.
|
||||
*
|
||||
* - `T` to set this function's returned value instead of `newValue`.
|
||||
*
|
||||
* - `null` to invoke `doValueInvalid_` and not set a value.
|
||||
*
|
||||
* - `undefined` to set `newValue` as is.
|
||||
*/
|
||||
export type FieldValidator<T = any> = (newValue: T) => T|null|undefined;
|
||||
|
||||
/**
|
||||
* Abstract class for an editable field.
|
||||
*
|
||||
* @alias Blockly.Field
|
||||
* @typeParam T - The value stored on the field.
|
||||
*/
|
||||
export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
||||
IASTNodeLocationWithBlock,
|
||||
@@ -76,6 +94,9 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
||||
* instead.
|
||||
*/
|
||||
static readonly SKIP_SETUP = new Sentinel();
|
||||
static isSentinel<T>(value: T|Sentinel): value is Sentinel {
|
||||
return value === Field.SKIP_SETUP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Name of field. Unique within each block.
|
||||
@@ -207,9 +228,7 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
||||
/** The size of the area rendered by the field. */
|
||||
this.size_ = new Size(0, 0);
|
||||
|
||||
if (value === Field.SKIP_SETUP) {
|
||||
return;
|
||||
}
|
||||
if (Field.isSentinel(value)) return;
|
||||
if (opt_config) {
|
||||
this.configure_(opt_config);
|
||||
}
|
||||
@@ -372,7 +391,8 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
||||
* @internal
|
||||
*/
|
||||
fromXml(fieldElement: Element) {
|
||||
this.setValue(fieldElement.textContent);
|
||||
// Any because gremlins live here. No touchie!
|
||||
this.setValue(fieldElement.textContent as any);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -384,7 +404,8 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
||||
* @internal
|
||||
*/
|
||||
toXml(fieldElement: Element): Element {
|
||||
fieldElement.textContent = this.getValue();
|
||||
// Any because gremlins live here. No touchie!
|
||||
fieldElement.textContent = this.getValue() as any;
|
||||
return fieldElement;
|
||||
}
|
||||
|
||||
@@ -619,7 +640,7 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
||||
*
|
||||
* @returns Validation function, or null.
|
||||
*/
|
||||
getValidator(): Function|null {
|
||||
getValidator(): FieldValidator<T>|null {
|
||||
return this.validator_;
|
||||
}
|
||||
|
||||
@@ -965,41 +986,37 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
||||
return;
|
||||
}
|
||||
|
||||
let validatedValue = this.doClassValidation_(newValue);
|
||||
// Class validators might accidentally forget to return, we'll ignore that.
|
||||
newValue = this.processValidation_(newValue, validatedValue);
|
||||
if (newValue instanceof Error) {
|
||||
const classValidation = this.doClassValidation_(newValue);
|
||||
const classValue = this.processValidation_(newValue, classValidation);
|
||||
if (classValue instanceof Error) {
|
||||
doLogging && console.log('invalid class validation, return');
|
||||
return;
|
||||
}
|
||||
|
||||
const localValidator = this.getValidator();
|
||||
if (localValidator) {
|
||||
validatedValue = localValidator.call(this, newValue);
|
||||
// Local validators might accidentally forget to return, we'll ignore
|
||||
// that.
|
||||
newValue = this.processValidation_(newValue, validatedValue);
|
||||
if (newValue instanceof Error) {
|
||||
doLogging && console.log('invalid local validation, return');
|
||||
return;
|
||||
}
|
||||
const localValidation = this.getValidator()?.call(this, classValue);
|
||||
const localValue = this.processValidation_(classValue, localValidation);
|
||||
if (localValue instanceof Error) {
|
||||
doLogging && console.log('invalid local validation, return');
|
||||
return;
|
||||
}
|
||||
|
||||
const source = this.sourceBlock_;
|
||||
if (source && source.disposed) {
|
||||
doLogging && console.log('source disposed, return');
|
||||
return;
|
||||
}
|
||||
|
||||
const oldValue = this.getValue();
|
||||
if (oldValue === newValue) {
|
||||
if (oldValue === localValue) {
|
||||
doLogging && console.log('same, doValueUpdate_, return');
|
||||
this.doValueUpdate_(newValue);
|
||||
this.doValueUpdate_(localValue);
|
||||
return;
|
||||
}
|
||||
|
||||
this.doValueUpdate_(newValue);
|
||||
this.doValueUpdate_(localValue);
|
||||
if (source && eventUtils.isEnabled()) {
|
||||
eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CHANGE))(
|
||||
source, 'field', this.name || null, oldValue, newValue));
|
||||
source, 'field', this.name || null, oldValue, localValue));
|
||||
}
|
||||
if (this.isDirty_) {
|
||||
this.forceRerender();
|
||||
@@ -1015,8 +1032,7 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
||||
* @returns New value, or an Error object.
|
||||
*/
|
||||
private processValidation_(
|
||||
newValue: AnyDuringMigration,
|
||||
validatedValue: AnyDuringMigration): AnyDuringMigration {
|
||||
newValue: AnyDuringMigration, validatedValue: T|null|undefined): T|Error {
|
||||
if (validatedValue === null) {
|
||||
this.doValueInvalid_(newValue);
|
||||
if (this.isDirty_) {
|
||||
@@ -1024,10 +1040,7 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
||||
}
|
||||
return Error();
|
||||
}
|
||||
if (validatedValue !== undefined) {
|
||||
newValue = validatedValue;
|
||||
}
|
||||
return newValue;
|
||||
return validatedValue === undefined ? newValue as T : validatedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1035,23 +1048,39 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
||||
*
|
||||
* @returns Current value.
|
||||
*/
|
||||
getValue(): AnyDuringMigration {
|
||||
getValue(): T|null {
|
||||
return this.value_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to validate a value. Returns input by default. Can be overridden by
|
||||
* subclasses, see FieldDropdown.
|
||||
* Validate the changes to a field's value before they are set. See
|
||||
* **FieldDropdown** for an example of subclass implementation.
|
||||
*
|
||||
* @param opt_newValue The value to be validated.
|
||||
* @returns The validated value, same as input by default.
|
||||
* **NOTE:** Validation returns one option between `T`, `null`, and
|
||||
* `undefined`. **Field**'s implementation will never return `undefined`, but
|
||||
* it is valid for a subclass to return `undefined` if the new value is
|
||||
* compatible with `T`.
|
||||
*
|
||||
* @see {@link https://developers.google.com/blockly/guides/create-custom-blocks/fields/validators#return_values}
|
||||
* @param newValue - The value to be validated.
|
||||
* @returns One of three instructions for setting the new value: `T`, `null`,
|
||||
* or `undefined`.
|
||||
*
|
||||
* - `T` to set this function's returned value instead of `newValue`.
|
||||
*
|
||||
* - `null` to invoke `doValueInvalid_` and not set a value.
|
||||
*
|
||||
* - `undefined` to set `newValue` as is.
|
||||
*/
|
||||
protected doClassValidation_(opt_newValue?: AnyDuringMigration):
|
||||
AnyDuringMigration {
|
||||
if (opt_newValue === null || opt_newValue === undefined) {
|
||||
protected doClassValidation_(newValue: T): T|null|undefined;
|
||||
protected doClassValidation_(newValue?: AnyDuringMigration): T|null;
|
||||
protected doClassValidation_(newValue?: T|AnyDuringMigration): T|null
|
||||
|undefined {
|
||||
if (newValue === null || newValue === undefined) {
|
||||
return null;
|
||||
}
|
||||
return opt_newValue;
|
||||
|
||||
return newValue as T;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1060,7 +1089,7 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
||||
*
|
||||
* @param newValue The value to be saved.
|
||||
*/
|
||||
protected doValueUpdate_(newValue: AnyDuringMigration) {
|
||||
protected doValueUpdate_(newValue: T) {
|
||||
this.value_ = newValue;
|
||||
this.isDirty_ = true;
|
||||
}
|
||||
@@ -1086,7 +1115,7 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
||||
}
|
||||
const gesture = (this.sourceBlock_.workspace as WorkspaceSvg).getGesture(e);
|
||||
if (gesture) {
|
||||
gesture.setStartField(this as Field);
|
||||
gesture.setStartField(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user