mirror of
https://github.com/google/blockly.git
synced 2026-01-07 00:50:27 +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
@@ -1066,11 +1066,12 @@ export class Block implements IASTNodeLocation, IDeletable {
|
|||||||
* @returns List of variable ids.
|
* @returns List of variable ids.
|
||||||
*/
|
*/
|
||||||
getVars(): string[] {
|
getVars(): string[] {
|
||||||
const vars = [];
|
const vars: string[] = [];
|
||||||
for (let i = 0, input; input = this.inputList[i]; i++) {
|
for (let i = 0, input; input = this.inputList[i]; i++) {
|
||||||
for (let j = 0, field; field = input.fieldRow[j]; j++) {
|
for (let j = 0, field; field = input.fieldRow[j]; j++) {
|
||||||
if (field.referencesVariables()) {
|
if (field.referencesVariables()) {
|
||||||
vars.push(field.getValue());
|
// NOTE: This only applies to `FieldVariable`, a `Field<string>`
|
||||||
|
vars.push(field.getValue() as string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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 type {WorkspaceSvg} from './workspace_svg.js';
|
||||||
import * as Xml from './xml.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.
|
* Abstract class for an editable field.
|
||||||
*
|
*
|
||||||
* @alias Blockly.Field
|
* @alias Blockly.Field
|
||||||
|
* @typeParam T - The value stored on the field.
|
||||||
*/
|
*/
|
||||||
export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
||||||
IASTNodeLocationWithBlock,
|
IASTNodeLocationWithBlock,
|
||||||
@@ -76,6 +94,9 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
|||||||
* instead.
|
* instead.
|
||||||
*/
|
*/
|
||||||
static readonly SKIP_SETUP = new Sentinel();
|
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.
|
* 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. */
|
/** The size of the area rendered by the field. */
|
||||||
this.size_ = new Size(0, 0);
|
this.size_ = new Size(0, 0);
|
||||||
|
|
||||||
if (value === Field.SKIP_SETUP) {
|
if (Field.isSentinel(value)) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (opt_config) {
|
if (opt_config) {
|
||||||
this.configure_(opt_config);
|
this.configure_(opt_config);
|
||||||
}
|
}
|
||||||
@@ -372,7 +391,8 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
fromXml(fieldElement: Element) {
|
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
|
* @internal
|
||||||
*/
|
*/
|
||||||
toXml(fieldElement: Element): Element {
|
toXml(fieldElement: Element): Element {
|
||||||
fieldElement.textContent = this.getValue();
|
// Any because gremlins live here. No touchie!
|
||||||
|
fieldElement.textContent = this.getValue() as any;
|
||||||
return fieldElement;
|
return fieldElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -619,7 +640,7 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
|||||||
*
|
*
|
||||||
* @returns Validation function, or null.
|
* @returns Validation function, or null.
|
||||||
*/
|
*/
|
||||||
getValidator(): Function|null {
|
getValidator(): FieldValidator<T>|null {
|
||||||
return this.validator_;
|
return this.validator_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -965,41 +986,37 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let validatedValue = this.doClassValidation_(newValue);
|
const classValidation = this.doClassValidation_(newValue);
|
||||||
// Class validators might accidentally forget to return, we'll ignore that.
|
const classValue = this.processValidation_(newValue, classValidation);
|
||||||
newValue = this.processValidation_(newValue, validatedValue);
|
if (classValue instanceof Error) {
|
||||||
if (newValue instanceof Error) {
|
|
||||||
doLogging && console.log('invalid class validation, return');
|
doLogging && console.log('invalid class validation, return');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const localValidator = this.getValidator();
|
const localValidation = this.getValidator()?.call(this, classValue);
|
||||||
if (localValidator) {
|
const localValue = this.processValidation_(classValue, localValidation);
|
||||||
validatedValue = localValidator.call(this, newValue);
|
if (localValue instanceof Error) {
|
||||||
// Local validators might accidentally forget to return, we'll ignore
|
doLogging && console.log('invalid local validation, return');
|
||||||
// that.
|
return;
|
||||||
newValue = this.processValidation_(newValue, validatedValue);
|
|
||||||
if (newValue instanceof Error) {
|
|
||||||
doLogging && console.log('invalid local validation, return');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const source = this.sourceBlock_;
|
const source = this.sourceBlock_;
|
||||||
if (source && source.disposed) {
|
if (source && source.disposed) {
|
||||||
doLogging && console.log('source disposed, return');
|
doLogging && console.log('source disposed, return');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const oldValue = this.getValue();
|
const oldValue = this.getValue();
|
||||||
if (oldValue === newValue) {
|
if (oldValue === localValue) {
|
||||||
doLogging && console.log('same, doValueUpdate_, return');
|
doLogging && console.log('same, doValueUpdate_, return');
|
||||||
this.doValueUpdate_(newValue);
|
this.doValueUpdate_(localValue);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.doValueUpdate_(newValue);
|
this.doValueUpdate_(localValue);
|
||||||
if (source && eventUtils.isEnabled()) {
|
if (source && eventUtils.isEnabled()) {
|
||||||
eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CHANGE))(
|
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_) {
|
if (this.isDirty_) {
|
||||||
this.forceRerender();
|
this.forceRerender();
|
||||||
@@ -1015,8 +1032,7 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
|||||||
* @returns New value, or an Error object.
|
* @returns New value, or an Error object.
|
||||||
*/
|
*/
|
||||||
private processValidation_(
|
private processValidation_(
|
||||||
newValue: AnyDuringMigration,
|
newValue: AnyDuringMigration, validatedValue: T|null|undefined): T|Error {
|
||||||
validatedValue: AnyDuringMigration): AnyDuringMigration {
|
|
||||||
if (validatedValue === null) {
|
if (validatedValue === null) {
|
||||||
this.doValueInvalid_(newValue);
|
this.doValueInvalid_(newValue);
|
||||||
if (this.isDirty_) {
|
if (this.isDirty_) {
|
||||||
@@ -1024,10 +1040,7 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
|||||||
}
|
}
|
||||||
return Error();
|
return Error();
|
||||||
}
|
}
|
||||||
if (validatedValue !== undefined) {
|
return validatedValue === undefined ? newValue as T : validatedValue;
|
||||||
newValue = validatedValue;
|
|
||||||
}
|
|
||||||
return newValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1035,23 +1048,39 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
|||||||
*
|
*
|
||||||
* @returns Current value.
|
* @returns Current value.
|
||||||
*/
|
*/
|
||||||
getValue(): AnyDuringMigration {
|
getValue(): T|null {
|
||||||
return this.value_;
|
return this.value_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to validate a value. Returns input by default. Can be overridden by
|
* Validate the changes to a field's value before they are set. See
|
||||||
* subclasses, see FieldDropdown.
|
* **FieldDropdown** for an example of subclass implementation.
|
||||||
*
|
*
|
||||||
* @param opt_newValue The value to be validated.
|
* **NOTE:** Validation returns one option between `T`, `null`, and
|
||||||
* @returns The validated value, same as input by default.
|
* `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):
|
protected doClassValidation_(newValue: T): T|null|undefined;
|
||||||
AnyDuringMigration {
|
protected doClassValidation_(newValue?: AnyDuringMigration): T|null;
|
||||||
if (opt_newValue === null || opt_newValue === undefined) {
|
protected doClassValidation_(newValue?: T|AnyDuringMigration): T|null
|
||||||
|
|undefined {
|
||||||
|
if (newValue === null || newValue === undefined) {
|
||||||
return null;
|
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.
|
* @param newValue The value to be saved.
|
||||||
*/
|
*/
|
||||||
protected doValueUpdate_(newValue: AnyDuringMigration) {
|
protected doValueUpdate_(newValue: T) {
|
||||||
this.value_ = newValue;
|
this.value_ = newValue;
|
||||||
this.isDirty_ = true;
|
this.isDirty_ = true;
|
||||||
}
|
}
|
||||||
@@ -1086,7 +1115,7 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
|||||||
}
|
}
|
||||||
const gesture = (this.sourceBlock_.workspace as WorkspaceSvg).getGesture(e);
|
const gesture = (this.sourceBlock_.workspace as WorkspaceSvg).getGesture(e);
|
||||||
if (gesture) {
|
if (gesture) {
|
||||||
gesture.setStartField(this as Field);
|
gesture.setStartField(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -137,9 +137,7 @@ export class FieldAngle extends FieldInput<number> {
|
|||||||
opt_config?: FieldAngleConfig) {
|
opt_config?: FieldAngleConfig) {
|
||||||
super(Field.SKIP_SETUP);
|
super(Field.SKIP_SETUP);
|
||||||
|
|
||||||
if (opt_value === Field.SKIP_SETUP) {
|
if (Field.isSentinel(opt_value)) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (opt_config) {
|
if (opt_config) {
|
||||||
this.configure_(opt_config);
|
this.configure_(opt_config);
|
||||||
}
|
}
|
||||||
@@ -404,25 +402,24 @@ export class FieldAngle extends FieldInput<number> {
|
|||||||
*
|
*
|
||||||
* @param e Keyboard event.
|
* @param e Keyboard event.
|
||||||
*/
|
*/
|
||||||
protected override onHtmlInputKeyDown_(e: Event) {
|
protected override onHtmlInputKeyDown_(e: KeyboardEvent) {
|
||||||
super.onHtmlInputKeyDown_(e);
|
super.onHtmlInputKeyDown_(e);
|
||||||
const block = this.getSourceBlock();
|
const block = this.getSourceBlock();
|
||||||
if (!block) {
|
if (!block) {
|
||||||
throw new UnattachedFieldError();
|
throw new UnattachedFieldError();
|
||||||
}
|
}
|
||||||
|
|
||||||
const keyboardEvent = e as KeyboardEvent;
|
|
||||||
let multiplier;
|
let multiplier;
|
||||||
if (keyboardEvent.keyCode === KeyCodes.LEFT) {
|
if (e.keyCode === KeyCodes.LEFT) {
|
||||||
// decrement (increment in RTL)
|
// decrement (increment in RTL)
|
||||||
multiplier = block.RTL ? 1 : -1;
|
multiplier = block.RTL ? 1 : -1;
|
||||||
} else if (keyboardEvent.keyCode === KeyCodes.RIGHT) {
|
} else if (e.keyCode === KeyCodes.RIGHT) {
|
||||||
// increment (decrement in RTL)
|
// increment (decrement in RTL)
|
||||||
multiplier = block.RTL ? -1 : 1;
|
multiplier = block.RTL ? -1 : 1;
|
||||||
} else if (keyboardEvent.keyCode === KeyCodes.DOWN) {
|
} else if (e.keyCode === KeyCodes.DOWN) {
|
||||||
// decrement
|
// decrement
|
||||||
multiplier = -1;
|
multiplier = -1;
|
||||||
} else if (keyboardEvent.keyCode === KeyCodes.UP) {
|
} else if (e.keyCode === KeyCodes.UP) {
|
||||||
// increment
|
// increment
|
||||||
multiplier = 1;
|
multiplier = 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,14 +20,16 @@ import {Field, FieldConfig, FieldValidator} from './field.js';
|
|||||||
import * as fieldRegistry from './field_registry.js';
|
import * as fieldRegistry from './field_registry.js';
|
||||||
import type {Sentinel} from './utils/sentinel.js';
|
import type {Sentinel} from './utils/sentinel.js';
|
||||||
|
|
||||||
export type FieldCheckboxValidator = FieldValidator<boolean>;
|
type BoolString = 'TRUE'|'FALSE';
|
||||||
|
type CheckboxBool = BoolString|boolean;
|
||||||
|
export type FieldCheckboxValidator = FieldValidator<CheckboxBool>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class for a checkbox field.
|
* Class for a checkbox field.
|
||||||
*
|
*
|
||||||
* @alias Blockly.FieldCheckbox
|
* @alias Blockly.FieldCheckbox
|
||||||
*/
|
*/
|
||||||
export class FieldCheckbox extends Field<boolean> {
|
export class FieldCheckbox extends Field<CheckboxBool> {
|
||||||
/** Default character for the checkmark. */
|
/** Default character for the checkmark. */
|
||||||
static readonly CHECK_CHAR = '✓';
|
static readonly CHECK_CHAR = '✓';
|
||||||
private checkChar_: string;
|
private checkChar_: string;
|
||||||
@@ -42,7 +44,12 @@ export class FieldCheckbox extends Field<boolean> {
|
|||||||
* Mouse cursor style when over the hotspot that initiates editability.
|
* Mouse cursor style when over the hotspot that initiates editability.
|
||||||
*/
|
*/
|
||||||
override CURSOR = 'default';
|
override CURSOR = 'default';
|
||||||
override value_: AnyDuringMigration;
|
|
||||||
|
/**
|
||||||
|
* NOTE: The default value is set in `Field`, so maintain that value instead
|
||||||
|
* of overwriting it here or in the constructor.
|
||||||
|
*/
|
||||||
|
override value_: boolean|null = this.value_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param opt_value The initial value of the field. Should either be 'TRUE',
|
* @param opt_value The initial value of the field. Should either be 'TRUE',
|
||||||
@@ -59,8 +66,7 @@ export class FieldCheckbox extends Field<boolean> {
|
|||||||
* for a list of properties this parameter supports.
|
* for a list of properties this parameter supports.
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
opt_value?: string|boolean|Sentinel,
|
opt_value?: CheckboxBool|Sentinel, opt_validator?: FieldCheckboxValidator,
|
||||||
opt_validator?: FieldCheckboxValidator,
|
|
||||||
opt_config?: FieldCheckboxConfig) {
|
opt_config?: FieldCheckboxConfig) {
|
||||||
super(Field.SKIP_SETUP);
|
super(Field.SKIP_SETUP);
|
||||||
|
|
||||||
@@ -70,9 +76,7 @@ export class FieldCheckbox extends Field<boolean> {
|
|||||||
*/
|
*/
|
||||||
this.checkChar_ = FieldCheckbox.CHECK_CHAR;
|
this.checkChar_ = FieldCheckbox.CHECK_CHAR;
|
||||||
|
|
||||||
if (opt_value === Field.SKIP_SETUP) {
|
if (Field.isSentinel(opt_value)) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (opt_config) {
|
if (opt_config) {
|
||||||
this.configure_(opt_config);
|
this.configure_(opt_config);
|
||||||
}
|
}
|
||||||
@@ -153,7 +157,7 @@ export class FieldCheckbox extends Field<boolean> {
|
|||||||
* @returns A valid value ('TRUE' or 'FALSE), or null if invalid.
|
* @returns A valid value ('TRUE' or 'FALSE), or null if invalid.
|
||||||
*/
|
*/
|
||||||
protected override doClassValidation_(opt_newValue?: AnyDuringMigration):
|
protected override doClassValidation_(opt_newValue?: AnyDuringMigration):
|
||||||
string|null {
|
BoolString|null {
|
||||||
if (opt_newValue === true || opt_newValue === 'TRUE') {
|
if (opt_newValue === true || opt_newValue === 'TRUE') {
|
||||||
return 'TRUE';
|
return 'TRUE';
|
||||||
}
|
}
|
||||||
@@ -169,7 +173,7 @@ export class FieldCheckbox extends Field<boolean> {
|
|||||||
* @param newValue The value to be saved. The default validator guarantees
|
* @param newValue The value to be saved. The default validator guarantees
|
||||||
* that this is a either 'TRUE' or 'FALSE'.
|
* that this is a either 'TRUE' or 'FALSE'.
|
||||||
*/
|
*/
|
||||||
protected override doValueUpdate_(newValue: AnyDuringMigration) {
|
protected override doValueUpdate_(newValue: BoolString) {
|
||||||
this.value_ = this.convertValueToBool_(newValue);
|
this.value_ = this.convertValueToBool_(newValue);
|
||||||
// Update visual.
|
// Update visual.
|
||||||
if (this.textElement_) {
|
if (this.textElement_) {
|
||||||
@@ -182,7 +186,7 @@ export class FieldCheckbox extends Field<boolean> {
|
|||||||
*
|
*
|
||||||
* @returns The value of this field.
|
* @returns The value of this field.
|
||||||
*/
|
*/
|
||||||
override getValue(): string {
|
override getValue(): BoolString {
|
||||||
return this.value_ ? 'TRUE' : 'FALSE';
|
return this.value_ ? 'TRUE' : 'FALSE';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,8 +195,8 @@ export class FieldCheckbox extends Field<boolean> {
|
|||||||
*
|
*
|
||||||
* @returns The boolean value of this field.
|
* @returns The boolean value of this field.
|
||||||
*/
|
*/
|
||||||
getValueBoolean(): boolean {
|
getValueBoolean(): boolean|null {
|
||||||
return this.value_ as boolean;
|
return this.value_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -213,12 +217,9 @@ export class FieldCheckbox extends Field<boolean> {
|
|||||||
* @param value The value to convert.
|
* @param value The value to convert.
|
||||||
* @returns The converted value.
|
* @returns The converted value.
|
||||||
*/
|
*/
|
||||||
private convertValueToBool_(value: AnyDuringMigration): boolean {
|
private convertValueToBool_(value: CheckboxBool|null): boolean {
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') return value === 'TRUE';
|
||||||
return value === 'TRUE';
|
return !!value;
|
||||||
} else {
|
|
||||||
return !!value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ export class FieldColour extends Field<string> {
|
|||||||
static COLUMNS = 7;
|
static COLUMNS = 7;
|
||||||
|
|
||||||
/** The field's colour picker element. */
|
/** The field's colour picker element. */
|
||||||
private picker_: Element|null = null;
|
private picker_: HTMLElement|null = null;
|
||||||
|
|
||||||
/** Index of the currently highlighted element. */
|
/** Index of the currently highlighted element. */
|
||||||
private highlightedIndex_: number|null = null;
|
private highlightedIndex_: number|null = null;
|
||||||
@@ -134,9 +134,6 @@ export class FieldColour extends Field<string> {
|
|||||||
* setting. By default use the global constants for columns.
|
* setting. By default use the global constants for columns.
|
||||||
*/
|
*/
|
||||||
private columns_ = 0;
|
private columns_ = 0;
|
||||||
override size_: AnyDuringMigration;
|
|
||||||
override clickTarget_: AnyDuringMigration;
|
|
||||||
override value_: AnyDuringMigration;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param opt_value The initial value of the field. Should be in '#rrggbb'
|
* @param opt_value The initial value of the field. Should be in '#rrggbb'
|
||||||
@@ -157,9 +154,7 @@ export class FieldColour extends Field<string> {
|
|||||||
opt_config?: FieldColourConfig) {
|
opt_config?: FieldColourConfig) {
|
||||||
super(Field.SKIP_SETUP);
|
super(Field.SKIP_SETUP);
|
||||||
|
|
||||||
if (opt_value === Field.SKIP_SETUP) {
|
if (Field.isSentinel(opt_value)) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (opt_config) {
|
if (opt_config) {
|
||||||
this.configure_(opt_config);
|
this.configure_(opt_config);
|
||||||
}
|
}
|
||||||
@@ -230,15 +225,14 @@ export class FieldColour extends Field<string> {
|
|||||||
* @param newValue The value to be saved. The default validator guarantees
|
* @param newValue The value to be saved. The default validator guarantees
|
||||||
* that this is a colour in '#rrggbb' format.
|
* that this is a colour in '#rrggbb' format.
|
||||||
*/
|
*/
|
||||||
protected override doValueUpdate_(newValue: AnyDuringMigration) {
|
protected override doValueUpdate_(newValue: string) {
|
||||||
this.value_ = newValue;
|
this.value_ = newValue;
|
||||||
if (this.borderRect_) {
|
if (this.borderRect_) {
|
||||||
this.borderRect_.style.fill = newValue as string;
|
this.borderRect_.style.fill = newValue;
|
||||||
} else if (
|
} else if (
|
||||||
this.sourceBlock_ && this.sourceBlock_.rendered &&
|
this.sourceBlock_ && this.sourceBlock_.rendered &&
|
||||||
this.sourceBlock_ instanceof BlockSvg) {
|
this.sourceBlock_ instanceof BlockSvg) {
|
||||||
this.sourceBlock_.pathObject.svgPath.setAttribute(
|
this.sourceBlock_.pathObject.svgPath.setAttribute('fill', newValue);
|
||||||
'fill', newValue as string);
|
|
||||||
this.sourceBlock_.pathObject.svgPath.setAttribute('stroke', '#fff');
|
this.sourceBlock_.pathObject.svgPath.setAttribute('stroke', '#fff');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -289,16 +283,12 @@ export class FieldColour extends Field<string> {
|
|||||||
/** Create and show the colour field's editor. */
|
/** Create and show the colour field's editor. */
|
||||||
protected override showEditor_() {
|
protected override showEditor_() {
|
||||||
this.dropdownCreate_();
|
this.dropdownCreate_();
|
||||||
// AnyDuringMigration because: Argument of type 'Element | null' is not
|
dropDownDiv.getContentDiv().appendChild(this.picker_!);
|
||||||
// assignable to parameter of type 'Node'.
|
|
||||||
dropDownDiv.getContentDiv().appendChild(this.picker_ as AnyDuringMigration);
|
|
||||||
|
|
||||||
dropDownDiv.showPositionedByField(this, this.dropdownDispose_.bind(this));
|
dropDownDiv.showPositionedByField(this, this.dropdownDispose_.bind(this));
|
||||||
|
|
||||||
// Focus so we can start receiving keyboard events.
|
// Focus so we can start receiving keyboard events.
|
||||||
// AnyDuringMigration because: Property 'focus' does not exist on type
|
this.picker_!.focus({preventScroll: true});
|
||||||
// 'Element'.
|
|
||||||
(this.picker_ as AnyDuringMigration)!.focus({preventScroll: true});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -519,9 +509,7 @@ export class FieldColour extends Field<string> {
|
|||||||
cell.setAttribute('data-colour', colours[i]);
|
cell.setAttribute('data-colour', colours[i]);
|
||||||
cell.title = titles[i] || colours[i];
|
cell.title = titles[i] || colours[i];
|
||||||
cell.id = idGenerator.getNextUniqueId();
|
cell.id = idGenerator.getNextUniqueId();
|
||||||
// AnyDuringMigration because: Argument of type 'number' is not
|
cell.setAttribute('data-index', String(i));
|
||||||
// assignable to parameter of type 'string'.
|
|
||||||
cell.setAttribute('data-index', i as AnyDuringMigration);
|
|
||||||
aria.setRole(cell, aria.Role.GRIDCELL);
|
aria.setRole(cell, aria.Role.GRIDCELL);
|
||||||
aria.setState(cell, aria.State.LABEL, colours[i]);
|
aria.setState(cell, aria.State.LABEL, colours[i]);
|
||||||
aria.setState(cell, aria.State.SELECTED, colours[i] === selectedColour);
|
aria.setState(cell, aria.State.SELECTED, colours[i] === selectedColour);
|
||||||
@@ -584,7 +572,7 @@ export class FieldColour extends Field<string> {
|
|||||||
static fromJson(options: FieldColourFromJsonConfig): FieldColour {
|
static fromJson(options: FieldColourFromJsonConfig): FieldColour {
|
||||||
// `this` might be a subclass of FieldColour if that class doesn't override
|
// `this` might be a subclass of FieldColour if that class doesn't override
|
||||||
// the static fromJson method.
|
// the static fromJson method.
|
||||||
return new this(options['colour'], undefined, options);
|
return new this(options.colour, undefined, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ export class FieldDropdown extends Field<string> {
|
|||||||
*/
|
*/
|
||||||
override suffixField: string|null = null;
|
override suffixField: string|null = null;
|
||||||
// TODO(b/109816955): remove '!', see go/strict-prop-init-fix.
|
// TODO(b/109816955): remove '!', see go/strict-prop-init-fix.
|
||||||
private selectedOption_!: Array<string|ImageProperties>;
|
private selectedOption_!: MenuOption;
|
||||||
override clickTarget_: SVGElement|null = null;
|
override clickTarget_: SVGElement|null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -398,8 +398,7 @@ export class FieldDropdown extends Field<string> {
|
|||||||
* @param opt_newValue The input value.
|
* @param opt_newValue The input value.
|
||||||
* @returns A valid language-neutral option, or null if invalid.
|
* @returns A valid language-neutral option, or null if invalid.
|
||||||
*/
|
*/
|
||||||
protected override doClassValidation_(opt_newValue?: MenuOption[1]): string
|
protected override doClassValidation_(opt_newValue?: string): string|null {
|
||||||
|null {
|
|
||||||
const options = this.getOptions(true);
|
const options = this.getOptions(true);
|
||||||
const isValueValid = options.some((option) => option[1] === opt_newValue);
|
const isValueValid = options.some((option) => option[1] === opt_newValue);
|
||||||
|
|
||||||
@@ -421,7 +420,7 @@ export class FieldDropdown extends Field<string> {
|
|||||||
* @param newValue The value to be saved. The default validator guarantees
|
* @param newValue The value to be saved. The default validator guarantees
|
||||||
* that this is one of the valid dropdown options.
|
* that this is one of the valid dropdown options.
|
||||||
*/
|
*/
|
||||||
protected override doValueUpdate_(newValue: MenuOption[1]) {
|
protected override doValueUpdate_(newValue: string) {
|
||||||
super.doValueUpdate_(newValue);
|
super.doValueUpdate_(newValue);
|
||||||
const options = this.getOptions(true);
|
const options = this.getOptions(true);
|
||||||
for (let i = 0, option; option = options[i]; i++) {
|
for (let i = 0, option; option = options[i]; i++) {
|
||||||
@@ -465,7 +464,7 @@ export class FieldDropdown extends Field<string> {
|
|||||||
// Show correct element.
|
// Show correct element.
|
||||||
const option = this.selectedOption_ && this.selectedOption_[0];
|
const option = this.selectedOption_ && this.selectedOption_[0];
|
||||||
if (option && typeof option === 'object') {
|
if (option && typeof option === 'object') {
|
||||||
this.renderSelectedImage_((option));
|
this.renderSelectedImage_(option);
|
||||||
} else {
|
} else {
|
||||||
this.renderSelectedText_();
|
this.renderSelectedText_();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ export class FieldImage extends Field<string> {
|
|||||||
|
|
||||||
/** Alt text of this image. */
|
/** Alt text of this image. */
|
||||||
private altText_ = '';
|
private altText_ = '';
|
||||||
override value_: AnyDuringMigration;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param src The URL of the image.
|
* @param src The URL of the image.
|
||||||
@@ -179,7 +178,7 @@ export class FieldImage extends Field<string> {
|
|||||||
* @param newValue The value to be saved. The default validator guarantees
|
* @param newValue The value to be saved. The default validator guarantees
|
||||||
* that this is a string.
|
* that this is a string.
|
||||||
*/
|
*/
|
||||||
protected override doValueUpdate_(newValue: AnyDuringMigration) {
|
protected override doValueUpdate_(newValue: string) {
|
||||||
this.value_ = newValue;
|
this.value_ = newValue;
|
||||||
if (this.imageElement_) {
|
if (this.imageElement_) {
|
||||||
this.imageElement_.setAttributeNS(
|
this.imageElement_.setAttributeNS(
|
||||||
|
|||||||
@@ -32,14 +32,16 @@ import * as WidgetDiv from './widgetdiv.js';
|
|||||||
import type {WorkspaceSvg} from './workspace_svg.js';
|
import type {WorkspaceSvg} from './workspace_svg.js';
|
||||||
|
|
||||||
export type InputTypes = string|number;
|
export type InputTypes = string|number;
|
||||||
export type FieldInputValidator<T extends InputTypes> = FieldValidator<T>;
|
export type FieldInputValidator<T extends InputTypes> =
|
||||||
|
FieldValidator<string|T>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class for an editable text field.
|
* Abstract class for an editable input field.
|
||||||
*
|
*
|
||||||
* @alias Blockly.FieldInput
|
* @alias Blockly.FieldInput
|
||||||
|
* @typeParam T - The value stored on the field.
|
||||||
*/
|
*/
|
||||||
export abstract class FieldInput<T extends InputTypes> extends Field<T> {
|
export abstract class FieldInput<T extends InputTypes> extends Field<string|T> {
|
||||||
/**
|
/**
|
||||||
* Pixel size of input border radius.
|
* Pixel size of input border radius.
|
||||||
* Should match blocklyText's border-radius in CSS.
|
* Should match blocklyText's border-radius in CSS.
|
||||||
@@ -83,9 +85,6 @@ export abstract class FieldInput<T extends InputTypes> extends Field<T> {
|
|||||||
|
|
||||||
/** Mouse cursor style when over the hotspot that initiates the editor. */
|
/** Mouse cursor style when over the hotspot that initiates the editor. */
|
||||||
override CURSOR = 'text';
|
override CURSOR = 'text';
|
||||||
override clickTarget_: AnyDuringMigration;
|
|
||||||
override value_: AnyDuringMigration;
|
|
||||||
override isDirty_: AnyDuringMigration;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param opt_value The initial value of the field. Should cast to a string.
|
* @param opt_value The initial value of the field. Should cast to a string.
|
||||||
@@ -106,9 +105,7 @@ export abstract class FieldInput<T extends InputTypes> extends Field<T> {
|
|||||||
opt_config?: FieldInputConfig) {
|
opt_config?: FieldInputConfig) {
|
||||||
super(Field.SKIP_SETUP);
|
super(Field.SKIP_SETUP);
|
||||||
|
|
||||||
if (opt_value === Field.SKIP_SETUP) {
|
if (Field.isSentinel(opt_value)) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (opt_config) {
|
if (opt_config) {
|
||||||
this.configure_(opt_config);
|
this.configure_(opt_config);
|
||||||
}
|
}
|
||||||
@@ -161,20 +158,6 @@ export abstract class FieldInput<T extends InputTypes> extends Field<T> {
|
|||||||
this.createTextElement_();
|
this.createTextElement_();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure that the input value casts to a valid string.
|
|
||||||
*
|
|
||||||
* @param opt_newValue The input value.
|
|
||||||
* @returns A valid string, or null if invalid.
|
|
||||||
*/
|
|
||||||
protected override doClassValidation_(opt_newValue?: AnyDuringMigration):
|
|
||||||
AnyDuringMigration {
|
|
||||||
if (opt_newValue === null || opt_newValue === undefined) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return String(opt_newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by setValue if the text input is not valid. If the field is
|
* Called by setValue if the text input is not valid. If the field is
|
||||||
* currently being edited it reverts value of the field to the previous
|
* currently being edited it reverts value of the field to the previous
|
||||||
@@ -207,7 +190,7 @@ export abstract class FieldInput<T extends InputTypes> extends Field<T> {
|
|||||||
* @param newValue The value to be saved. The default validator guarantees
|
* @param newValue The value to be saved. The default validator guarantees
|
||||||
* that this is a string.
|
* that this is a string.
|
||||||
*/
|
*/
|
||||||
protected override doValueUpdate_(newValue: AnyDuringMigration) {
|
protected override doValueUpdate_(newValue: string|T) {
|
||||||
this.isDirty_ = true;
|
this.isDirty_ = true;
|
||||||
this.isTextValid_ = true;
|
this.isTextValid_ = true;
|
||||||
this.value_ = newValue;
|
this.value_ = newValue;
|
||||||
@@ -380,7 +363,7 @@ export abstract class FieldInput<T extends InputTypes> extends Field<T> {
|
|||||||
div!.appendChild(htmlInput);
|
div!.appendChild(htmlInput);
|
||||||
|
|
||||||
htmlInput.value = htmlInput.defaultValue = this.getEditorText_(this.value_);
|
htmlInput.value = htmlInput.defaultValue = this.getEditorText_(this.value_);
|
||||||
htmlInput.setAttribute('data-untyped-default-value', this.value_);
|
htmlInput.setAttribute('data-untyped-default-value', String(this.value_));
|
||||||
|
|
||||||
this.resizeEditor_();
|
this.resizeEditor_();
|
||||||
|
|
||||||
@@ -457,29 +440,19 @@ export abstract class FieldInput<T extends InputTypes> extends Field<T> {
|
|||||||
*
|
*
|
||||||
* @param e Keyboard event.
|
* @param e Keyboard event.
|
||||||
*/
|
*/
|
||||||
protected onHtmlInputKeyDown_(e: Event) {
|
protected onHtmlInputKeyDown_(e: KeyboardEvent) {
|
||||||
// AnyDuringMigration because: Property 'keyCode' does not exist on type
|
if (e.keyCode === KeyCodes.ENTER) {
|
||||||
// 'Event'.
|
|
||||||
if ((e as AnyDuringMigration).keyCode === KeyCodes.ENTER) {
|
|
||||||
WidgetDiv.hide();
|
WidgetDiv.hide();
|
||||||
dropDownDiv.hideWithoutAnimation();
|
dropDownDiv.hideWithoutAnimation();
|
||||||
// AnyDuringMigration because: Property 'keyCode' does not exist on type
|
} else if (e.keyCode === KeyCodes.ESC) {
|
||||||
// 'Event'.
|
|
||||||
} else if ((e as AnyDuringMigration).keyCode === KeyCodes.ESC) {
|
|
||||||
this.setValue(
|
this.setValue(
|
||||||
this.htmlInput_!.getAttribute('data-untyped-default-value'));
|
this.htmlInput_!.getAttribute('data-untyped-default-value'));
|
||||||
WidgetDiv.hide();
|
WidgetDiv.hide();
|
||||||
dropDownDiv.hideWithoutAnimation();
|
dropDownDiv.hideWithoutAnimation();
|
||||||
// AnyDuringMigration because: Property 'keyCode' does not exist on type
|
} else if (e.keyCode === KeyCodes.TAB) {
|
||||||
// 'Event'.
|
|
||||||
} else if ((e as AnyDuringMigration).keyCode === KeyCodes.TAB) {
|
|
||||||
WidgetDiv.hide();
|
WidgetDiv.hide();
|
||||||
dropDownDiv.hideWithoutAnimation();
|
dropDownDiv.hideWithoutAnimation();
|
||||||
// AnyDuringMigration because: Property 'shiftKey' does not exist on type
|
(this.sourceBlock_ as BlockSvg).tab(this, !e.shiftKey);
|
||||||
// 'Event'. AnyDuringMigration because: Argument of type 'this' is not
|
|
||||||
// assignable to parameter of type 'Field'.
|
|
||||||
(this.sourceBlock_ as BlockSvg)
|
|
||||||
.tab(this as AnyDuringMigration, !(e as AnyDuringMigration).shiftKey);
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,9 +51,7 @@ export class FieldLabel extends Field<string> {
|
|||||||
opt_config?: FieldLabelConfig) {
|
opt_config?: FieldLabelConfig) {
|
||||||
super(Field.SKIP_SETUP);
|
super(Field.SKIP_SETUP);
|
||||||
|
|
||||||
if (opt_value === Field.SKIP_SETUP) {
|
if (Field.isSentinel(opt_value)) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (opt_config) {
|
if (opt_config) {
|
||||||
this.configure_(opt_config);
|
this.configure_(opt_config);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -70,9 +70,7 @@ export class FieldMultilineInput extends FieldTextInput {
|
|||||||
opt_config?: FieldMultilineInputConfig) {
|
opt_config?: FieldMultilineInputConfig) {
|
||||||
super(Field.SKIP_SETUP);
|
super(Field.SKIP_SETUP);
|
||||||
|
|
||||||
if (opt_value === Field.SKIP_SETUP) {
|
if (Field.isSentinel(opt_value)) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (opt_config) {
|
if (opt_config) {
|
||||||
this.configure_(opt_config);
|
this.configure_(opt_config);
|
||||||
}
|
}
|
||||||
@@ -210,9 +208,11 @@ export class FieldMultilineInput extends FieldTextInput {
|
|||||||
* @param newValue The value to be saved. The default validator guarantees
|
* @param newValue The value to be saved. The default validator guarantees
|
||||||
* that this is a string.
|
* that this is a string.
|
||||||
*/
|
*/
|
||||||
protected override doValueUpdate_(newValue: AnyDuringMigration) {
|
protected override doValueUpdate_(newValue: string) {
|
||||||
super.doValueUpdate_(newValue);
|
super.doValueUpdate_(newValue);
|
||||||
this.isOverflowedY_ = this.value_.split('\n').length > this.maxLines_;
|
if (this.value_ !== null) {
|
||||||
|
this.isOverflowedY_ = this.value_.split('\n').length > this.maxLines_;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Updates the text of the textElement. */
|
/** Updates the text of the textElement. */
|
||||||
@@ -300,7 +300,7 @@ export class FieldMultilineInput extends FieldTextInput {
|
|||||||
// absolute longest line, even if it would be truncated after editing.
|
// absolute longest line, even if it would be truncated after editing.
|
||||||
// Otherwise we would get wrong editor width when there are more
|
// Otherwise we would get wrong editor width when there are more
|
||||||
// lines than this.maxLines_.
|
// lines than this.maxLines_.
|
||||||
const actualEditorLines = this.value_.split('\n');
|
const actualEditorLines = String(this.value_).split('\n');
|
||||||
const dummyTextElement = dom.createSvgElement(
|
const dummyTextElement = dom.createSvgElement(
|
||||||
Svg.TEXT, {'class': 'blocklyText blocklyMultilineText'});
|
Svg.TEXT, {'class': 'blocklyText blocklyMultilineText'});
|
||||||
|
|
||||||
@@ -385,7 +385,7 @@ export class FieldMultilineInput extends FieldTextInput {
|
|||||||
div!.appendChild(htmlInput);
|
div!.appendChild(htmlInput);
|
||||||
|
|
||||||
htmlInput.value = htmlInput.defaultValue = this.getEditorText_(this.value_);
|
htmlInput.value = htmlInput.defaultValue = this.getEditorText_(this.value_);
|
||||||
htmlInput.setAttribute('data-untyped-default-value', this.value_);
|
htmlInput.setAttribute('data-untyped-default-value', String(this.value_));
|
||||||
htmlInput.setAttribute('data-old-value', '');
|
htmlInput.setAttribute('data-old-value', '');
|
||||||
if (userAgent.GECKO) {
|
if (userAgent.GECKO) {
|
||||||
// In FF, ensure the browser reflows before resizing to avoid issue #2777.
|
// In FF, ensure the browser reflows before resizing to avoid issue #2777.
|
||||||
@@ -428,10 +428,8 @@ export class FieldMultilineInput extends FieldTextInput {
|
|||||||
*
|
*
|
||||||
* @param e Keyboard event.
|
* @param e Keyboard event.
|
||||||
*/
|
*/
|
||||||
protected override onHtmlInputKeyDown_(e: Event) {
|
protected override onHtmlInputKeyDown_(e: KeyboardEvent) {
|
||||||
// AnyDuringMigration because: Property 'keyCode' does not exist on type
|
if (e.keyCode !== KeyCodes.ENTER) {
|
||||||
// 'Event'.
|
|
||||||
if ((e as AnyDuringMigration).keyCode !== KeyCodes.ENTER) {
|
|
||||||
super.onHtmlInputKeyDown_(e);
|
super.onHtmlInputKeyDown_(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,9 +77,7 @@ export class FieldNumber extends FieldInput<number> {
|
|||||||
// Pass SENTINEL so that we can define properties before value validation.
|
// Pass SENTINEL so that we can define properties before value validation.
|
||||||
super(Field.SKIP_SETUP);
|
super(Field.SKIP_SETUP);
|
||||||
|
|
||||||
if (opt_value === Field.SKIP_SETUP) {
|
if (Field.isSentinel(opt_value)) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (opt_config) {
|
if (opt_config) {
|
||||||
this.configure_(opt_config);
|
this.configure_(opt_config);
|
||||||
} else {
|
} else {
|
||||||
@@ -260,6 +258,7 @@ export class FieldNumber extends FieldInput<number> {
|
|||||||
if (opt_newValue === null) {
|
if (opt_newValue === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up text.
|
// Clean up text.
|
||||||
let newValue = String(opt_newValue);
|
let newValue = String(opt_newValue);
|
||||||
// TODO: Handle cases like 'ten', '1.203,14', etc.
|
// TODO: Handle cases like 'ten', '1.203,14', etc.
|
||||||
|
|||||||
@@ -22,6 +22,11 @@ import type {Sentinel} from './utils/sentinel.js';
|
|||||||
|
|
||||||
export type FieldTextInputValidator = FieldInputValidator<string>;
|
export type FieldTextInputValidator = FieldInputValidator<string>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for an editable text field.
|
||||||
|
*
|
||||||
|
* @alias Blockly.FieldTextInput
|
||||||
|
*/
|
||||||
export class FieldTextInput extends FieldInput<string> {
|
export class FieldTextInput extends FieldInput<string> {
|
||||||
/**
|
/**
|
||||||
* @param opt_value The initial value of the field. Should cast to a string.
|
* @param opt_value The initial value of the field. Should cast to a string.
|
||||||
@@ -43,6 +48,20 @@ export class FieldTextInput extends FieldInput<string> {
|
|||||||
super(opt_value, opt_validator, opt_config);
|
super(opt_value, opt_validator, opt_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure that the input value casts to a valid string.
|
||||||
|
*
|
||||||
|
* @param opt_newValue The input value.
|
||||||
|
* @returns A valid string, or null if invalid.
|
||||||
|
*/
|
||||||
|
protected override doClassValidation_(opt_newValue?: AnyDuringMigration):
|
||||||
|
string|null {
|
||||||
|
if (opt_newValue === undefined) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return String(opt_newValue);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a FieldTextInput from a JSON arg object,
|
* Construct a FieldTextInput from a JSON arg object,
|
||||||
* dereferencing any string table references.
|
* dereferencing any string table references.
|
||||||
|
|||||||
@@ -306,7 +306,7 @@ export class FieldVariable extends FieldDropdown {
|
|||||||
*
|
*
|
||||||
* @returns Validation function, or null.
|
* @returns Validation function, or null.
|
||||||
*/
|
*/
|
||||||
override getValidator(): Function|null {
|
override getValidator(): FieldVariableValidator|null {
|
||||||
// Validators shouldn't operate on the initial setValue call.
|
// Validators shouldn't operate on the initial setValue call.
|
||||||
// Normally this is achieved by calling setValidator after setValue, but
|
// Normally this is achieved by calling setValidator after setValue, but
|
||||||
// this is not a possibility with variable fields.
|
// this is not a possibility with variable fields.
|
||||||
@@ -357,7 +357,7 @@ export class FieldVariable extends FieldDropdown {
|
|||||||
*
|
*
|
||||||
* @param newId The value to be saved.
|
* @param newId The value to be saved.
|
||||||
*/
|
*/
|
||||||
protected override doValueUpdate_(newId: AnyDuringMigration) {
|
protected override doValueUpdate_(newId: string) {
|
||||||
const block = this.getSourceBlock();
|
const block = this.getSourceBlock();
|
||||||
if (!block) {
|
if (!block) {
|
||||||
throw new UnattachedFieldError();
|
throw new UnattachedFieldError();
|
||||||
|
|||||||
@@ -969,14 +969,14 @@ export class Gesture {
|
|||||||
* @param field The field the gesture started on.
|
* @param field The field the gesture started on.
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
setStartField(field: Field) {
|
setStartField<T>(field: Field<T>) {
|
||||||
if (this.hasStarted_) {
|
if (this.hasStarted_) {
|
||||||
throw Error(
|
throw Error(
|
||||||
'Tried to call gesture.setStartField, ' +
|
'Tried to call gesture.setStartField, ' +
|
||||||
'but the gesture had already been started.');
|
'but the gesture had already been started.');
|
||||||
}
|
}
|
||||||
if (!this.startField_) {
|
if (!this.startField_) {
|
||||||
this.startField_ = field;
|
this.startField_ = field as Field;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,4 +18,10 @@ goog.declareModuleId('Blockly.utils.Sentinel');
|
|||||||
*
|
*
|
||||||
* @alias Blockly.utils.Sentinel
|
* @alias Blockly.utils.Sentinel
|
||||||
*/
|
*/
|
||||||
export class Sentinel {}
|
export class Sentinel {
|
||||||
|
/**
|
||||||
|
* Provide a unique key so that type guarding properly excludes values like
|
||||||
|
* string.
|
||||||
|
*/
|
||||||
|
UNIQUE_KEY?: never;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user