/** * @license * Copyright 2012 Google LLC * SPDX-License-Identifier: Apache-2.0 */ /** * Field. Used for editable titles, variables, etc. * This is an abstract class that defines the UI on the block. Actual * instances would be FieldTextInput, FieldDropdown, etc. * * @class */ import './events/events_block_change.js'; import type { Block } from './block.js'; import type { Input } from './inputs/input.js'; import type { IFocusableNode } from './interfaces/i_focusable_node.js'; import type { IFocusableTree } from './interfaces/i_focusable_tree.js'; import type { IKeyboardAccessible } from './interfaces/i_keyboard_accessible.js'; import type { IRegistrable } from './interfaces/i_registrable.js'; import { ISerializable } from './interfaces/i_serializable.js'; import type { ConstantProvider } from './renderers/common/constants.js'; import type { KeyboardShortcut } from './shortcut_registry.js'; import * as Tooltip from './tooltip.js'; import type { Coordinate } from './utils/coordinate.js'; import { Rect } from './utils/rect.js'; import { Size } from './utils/size.js'; /** * A function that is called to validate changes to the field's value before * they are set. * * @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 = (newValue: T) => T | null | undefined; /** * Abstract class for an editable field. * * @typeParam T - The value stored on the field. */ export declare abstract class Field implements IKeyboardAccessible, IRegistrable, ISerializable, IFocusableNode { /** * To overwrite the default value which is set in **Field**, directly update * the prototype. * * Example: * `FieldImage.prototype.DEFAULT_VALUE = null;` */ DEFAULT_VALUE: T | null; /** Non-breaking space. */ static readonly NBSP = "\u00A0"; /** * A value used to signal when a field's constructor should *not* set the * field's value or run configure_, and should allow a subclass to do that * instead. */ static readonly SKIP_SETUP: unique symbol; /** * Name of field. Unique within each block. * Static labels are usually unnamed. */ name?: string; protected value_: T | null; /** Validation function called when user edits an editable field. */ protected validator_: FieldValidator | null; /** * Used to cache the field's tooltip value if setTooltip is called when the * field is not yet initialized. Is *not* guaranteed to be accurate. */ private tooltip; /** This field's dimensions. */ private size; /** * Gets the size of this field. Because getSize() and updateSize() have side * effects, this acts as a shim for subclasses which wish to adjust field * bounds when setting/getting the size without triggering unwanted rendering * or other side effects. Note that subclasses must override *both* get and * set if either is overridden; the implementation may just call directly * through to super, but it must exist per the JS spec. */ protected get size_(): Size; protected set size_(newValue: Size); /** The rendered field's SVG group element. */ protected fieldGroup_: SVGGElement | null; /** The rendered field's SVG border element. */ protected borderRect_: SVGRectElement | null; /** The rendered field's SVG text element. */ protected textElement_: SVGTextElement | null; /** The rendered field's text content element. */ protected textContent_: Text | null; /** Mouse down event listener data. */ private mouseDownWrapper; /** Constants associated with the source block's renderer. */ protected constants_: ConstantProvider | null; /** * Has this field been disposed of? * * @internal */ disposed: boolean; /** Maximum characters of text to display before adding an ellipsis. */ maxDisplayLength: number; /** Block this field is attached to. Starts as null, then set in init. */ protected sourceBlock_: Block | null; /** Does this block need to be re-rendered? */ protected isDirty_: boolean; /** Is the field visible, or hidden due to the block being collapsed? */ protected visible_: boolean; /** * Can the field value be changed using the editor on an editable block? */ protected enabled_: boolean; /** The element the click handler is bound to. */ protected clickTarget_: Element | null; /** * The prefix field. * * @internal */ prefixField: string | null; /** * The suffix field. * * @internal */ suffixField: string | null; /** * Editable fields usually show some sort of UI indicating they are * editable. They will also be saved by the serializer. */ EDITABLE: boolean; /** * Serializable fields are saved by the serializer, non-serializable fields * are not. Editable fields should also be serializable. This is not the * case by default so that SERIALIZABLE is backwards compatible. */ SERIALIZABLE: boolean; /** The unique ID of this field. */ private id_; /** * @param value The initial value of the field. * Also accepts Field.SKIP_SETUP if you wish to skip setup (only used by * subclasses that want to handle configuration and setting the field value * after their own constructors have run). * @param validator A function that is called to validate changes to the * field's value. Takes in a value & returns a validated value, or null to * abort the change. * @param config A map of options used to configure the field. * Refer to the individual field's documentation for a list of properties * this parameter supports. */ constructor(value: T | typeof Field.SKIP_SETUP, validator?: FieldValidator | null, config?: FieldConfig); /** * Process the configuration map passed to the field. * * @param config A map of options used to configure the field. See the * individual field's documentation for a list of properties this * parameter supports. */ protected configure_(config: FieldConfig): void; /** * Attach this field to a block. * * @param block The block containing this field. */ setSourceBlock(block: Block): void; /** * Get the renderer constant provider. * * @returns The renderer constant provider. */ getConstants(): ConstantProvider | null; /** * Get the block this field is attached to. * * @returns The block containing this field. * @throws An error if the source block is not defined. */ getSourceBlock(): Block | null; /** * Initialize everything to render this field. Override * methods initModel and initView rather than this method. * * @sealed * @internal */ init(): void; /** * Create the block UI for this field. */ protected initView(): void; /** * Initializes the model of the field after it has been installed on a block. * No-op by default. */ initModel(): void; /** * Defines whether this field should take up the full block or not. * * Be cautious when overriding this function. It may not work as you expect / * intend because the behavior was kind of hacked in. If you are thinking * about overriding this function, post on the forum with your intended * behavior to see if there's another approach. * * @internal */ isFullBlockField(): boolean; /** * Create a field border rect element. Not to be overridden by subclasses. * Instead modify the result of the function inside initView, or create a * separate function to call. */ protected createBorderRect_(): void; /** * Create a field text element. Not to be overridden by subclasses. Instead * modify the result of the function inside initView, or create a separate * function to call. */ protected createTextElement_(): void; /** * Bind events to the field. Can be overridden by subclasses if they need to * do custom input handling. */ protected bindEvents_(): void; /** * Sets the field's value based on the given XML element. Should only be * called by Blockly.Xml. * * @param fieldElement The element containing info about the field's state. */ fromXml(fieldElement: Element): void; /** * Serializes this field's value to XML. Should only be called by Blockly.Xml. * * @param fieldElement The element to populate with info about the field's * state. * @returns The element containing info about the field's state. */ toXml(fieldElement: Element): Element; /** * Saves this fields value as something which can be serialized to JSON. * Should only be called by the serialization system. * * @param _doFullSerialization If true, this signals to the field that if it * normally just saves a reference to some state (eg variable fields) it * should instead serialize the full state of the thing being referenced. * See the * {@link https://developers.devsite.google.com/blockly/guides/create-custom-blocks/fields/customizing-fields/creating#full_serialization_and_backing_data | field serialization docs} * for more information. * @returns JSON serializable state. */ saveState(_doFullSerialization?: boolean): any; /** * Sets the field's state based on the given state value. Should only be * called by the serialization system. * * @param state The state we want to apply to the field. */ loadState(state: any): void; /** * Returns a stringified version of the XML state, if it should be used. * Otherwise this returns null, to signal the field should use its own * serialization. * * @param callingClass The class calling this method. * Used to see if `this` has overridden any relevant hooks. * @returns The stringified version of the XML state, or null. */ protected saveLegacyState(callingClass: FieldProto): string | null; /** * Loads the given state using either the old XML hooks, if they should be * used. Returns true to indicate loading has been handled, false otherwise. * * @param callingClass The class calling this method. * Used to see if `this` has overridden any relevant hooks. * @param state The state to apply to the field. * @returns Whether the state was applied or not. */ loadLegacyState(callingClass: FieldProto, state: any): boolean; /** * Dispose of all DOM objects and events belonging to this editable field. */ dispose(): void; /** Add or remove the UI indicating if this field is editable or not. */ updateEditable(): void; /** * Set whether this field's value can be changed using the editor when the * source block is editable. * * @param enabled True if enabled. */ setEnabled(enabled: boolean): void; /** * Check whether this field's value can be changed using the editor when the * source block is editable. * * @returns Whether this field is enabled. */ isEnabled(): boolean; /** * Check whether this field defines the showEditor_ function. * * @returns Whether this field is clickable. */ isClickable(): boolean; /** * Check whether the field should be clickable while the block is in a flyout. * The default is that fields are clickable in always-open flyouts such as the * simple toolbox, but not in autoclosing flyouts such as the category toolbox. * Subclasses may override this function to change this behavior. Note that * `isClickable` must also return true for this to have any effect. * * @param autoClosingFlyout true if the containing flyout is an auto-closing one. * @returns Whether the field should be clickable while the block is in a flyout. */ isClickableInFlyout(autoClosingFlyout: boolean): boolean; /** * Check whether this field is currently editable. Some fields are never * EDITABLE (e.g. text labels). Other fields may be EDITABLE but may exist on * non-editable blocks or be currently disabled. * * @returns Whether this field is currently enabled, editable and on an * editable block. */ isCurrentlyEditable(): boolean; /** * Check whether this field should be serialized by the XML renderer. * Handles the logic for backwards compatibility and incongruous states. * * @returns Whether this field should be serialized or not. */ isSerializable(): boolean; /** * Gets whether this editable field is visible or not. * * @returns True if visible. */ isVisible(): boolean; /** * Sets whether this editable field is visible or not. Should only be called * by input.setVisible. * * @param visible True if visible. * @internal */ setVisible(visible: boolean): void; /** * Sets a new validation function for editable fields, or clears a previously * set validator. * * The validator function takes in the new field value, and returns * validated value. The validated value could be the input value, a modified * version of the input value, or null to abort the change. * * If the function does not return anything (or returns undefined) the new * value is accepted as valid. This is to allow for fields using the * validated function as a field-level change event notification. * * @param handler The validator function or null to clear a previous * validator. */ setValidator(handler: FieldValidator): void; /** * Gets the validation function for editable fields, or null if not set. * * @returns Validation function, or null. */ getValidator(): FieldValidator | null; /** * Gets the group element for this editable field. * Used for measuring the size and for positioning. * * @returns The group element. */ getSvgRoot(): SVGGElement | null; /** * Gets the border rectangle element. * * @returns The border rectangle element. * @throws An error if the border rectangle element is not defined. */ protected getBorderRect(): SVGRectElement; /** * Gets the text element. * * @returns The text element. * @throws An error if the text element is not defined. */ protected getTextElement(): SVGTextElement; /** * Gets the text content. * * @returns The text content. * @throws An error if the text content is not defined. */ protected getTextContent(): Text; /** * Updates the field to match the colour/style of the block. * * Non-abstract sub-classes may wish to implement this if the colour of the * field depends on the colour of the block. It will automatically be called * at relevant times, such as when the parent block or renderer changes. * * See {@link * https://developers.google.com/blockly/guides/create-custom-blocks/fields/customizing-fields/creating#matching_block_colours * | the field documentation} for more information, or FieldDropdown for an * example. */ applyColour(): void; /** * Used by getSize() to move/resize any DOM elements, and get the new size. * * All rendering that has an effect on the size/shape of the block should be * done here, and should be triggered by getSize(). */ protected render_(): void; /** * Calls showEditor_ when the field is clicked if the field is clickable. * Do not override. * * @param e Optional mouse event that triggered the field to open, or * undefined if triggered programmatically. * @sealed * @internal */ showEditor(e?: Event): void; /** * A developer hook to create an editor for the field. This is no-op by * default, and must be overriden to create an editor. * * @param _e Optional mouse event that triggered the field to open, or * undefined if triggered programmatically. */ protected showEditor_(_e?: Event): void; /** * A developer hook to reposition the WidgetDiv during a window resize. You * need to define this hook if your field has a WidgetDiv that needs to * reposition itself when the window is resized. For example, text input * fields define this hook so that the input WidgetDiv can reposition itself * on a window resize event. This is especially important when modal inputs * have been disabled, as Android devices will fire a window resize event when * the soft keyboard opens. * * If you want the WidgetDiv to hide itself instead of repositioning, return * false. This is the default behavior. * * DropdownDivs already handle their own positioning logic, so you do not need * to override this function if your field only has a DropdownDiv. * * @returns True if the field should be repositioned, * false if the WidgetDiv should hide itself instead. */ repositionForWindowResize(): boolean; /** * Updates the size of the field based on the text. * * @param margin margin to use when positioning the text element. */ protected updateSize_(margin?: number): void; /** * Position a field's text element after a size change. This handles both LTR * and RTL positioning. * * @param xOffset x offset to use when positioning the text element. * @param contentWidth The content width. */ protected positionTextElement_(xOffset: number, contentWidth: number): void; /** Position a field's border rect after a size change. */ protected positionBorderRect_(): void; /** * Returns the height and width of the field. * * This should *in general* be the only place render_ gets called from. * * @returns Height and width. */ getSize(): Size; /** * Returns the bounding box of the rendered field, accounting for workspace * scaling. * * @returns An object with top, bottom, left, and right in pixels relative to * the top left corner of the page (window coordinates). * @internal */ getScaledBBox(): Rect; /** * Notifies the field that it has changed locations. * * @param _ The location of this field's block's top-start corner * in workspace coordinates. */ onLocationChange(_: Coordinate): void; /** * Get the text from this field to display on the block. May differ from * `getText` due to ellipsis, and other formatting. * * @returns Text to display. */ protected getDisplayText_(): string; /** * Get the text from this field. * Override getText_ to provide a different behavior than simply casting the * value to a string. * * @returns Current text. * @sealed */ getText(): string; /** * A developer hook to override the returned text of this field. * Override if the text representation of the value of this field * is not just a string cast of its value. * Return null to resort to a string cast. * * @returns Current text or null. */ protected getText_(): string | null; /** * Force a rerender of the block that this field is installed on, which will * rerender this field and adjust for any sizing changes. * Other fields on the same block will not rerender, because their sizes have * already been recorded. * * @internal */ markDirty(): void; /** * Force a rerender of the block that this field is installed on, which will * rerender this field and adjust for any sizing changes. * Other fields on the same block will not rerender, because their sizes have * already been recorded. */ forceRerender(): void; /** * Used to change the value of the field. Handles validation and events. * Subclasses should override doClassValidation_ and doValueUpdate_ rather * than this method. * * @param newValue New value. * @param fireChangeEvent Whether to fire a change event. Defaults to true. * Should usually be true unless the change will be reported some other * way, e.g. an intermediate field change event. * @sealed */ setValue(newValue: any, fireChangeEvent?: boolean): void; /** * Process the result of validation. * * @param newValue New value. * @param validatedValue Validated value. * @param fireChangeEvent Whether to fire a change event if the value changes. * @returns New value, or an Error object. */ private processValidation; /** * Get the current value of the field. * * @returns Current value. */ getValue(): T | null; /** * Validate the changes to a field's value before they are set. See * **FieldDropdown** for an example of subclass implementation. * * **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_(newValue: T): T | null | undefined; protected doClassValidation_(newValue?: any): T | null; /** * Used to update the value of a field. Can be overridden by subclasses to do * custom storage of values/updating of external things. * * @param newValue The value to be saved. */ protected doValueUpdate_(newValue: T): void; /** * Used to notify the field an invalid value was input. Can be overridden by * subclasses, see FieldTextInput. * No-op by default. * * @param _invalidValue The input value that was determined to be invalid. * @param _fireChangeEvent Whether to fire a change event if the value changes. */ protected doValueInvalid_(_invalidValue: any, _fireChangeEvent?: boolean): void; /** * Handle a pointerdown event on a field. * * @param e Pointer down event. */ protected onMouseDown_(e: PointerEvent): void; /** * Sets the tooltip for this field. * * @param newTip The text for the tooltip, a function that returns the text * for the tooltip, a parent object whose tooltip will be used, or null to * display the tooltip of the parent block. To not display a tooltip pass * the empty string. */ setTooltip(newTip: Tooltip.TipInfo | null): void; /** * Returns the tooltip text for this field. * * @returns The tooltip text for this field. */ getTooltip(): string; /** * The element to bind the click handler to. If not set explicitly, defaults * to the SVG root of the field. When this element is * clicked on an editable field, the editor will open. * * @returns Element to bind click handler to. */ protected getClickTarget_(): Element | null; /** * Return the absolute coordinates of the top-left corner of this field. * The origin (0,0) is the top-left corner of the page body. * * @returns Object with .x and .y properties. */ protected getAbsoluteXY_(): Coordinate; /** * Whether this field references any Blockly variables. If true it may need * to be handled differently during serialization and deserialization. * Subclasses may override this. * * @returns True if this field has any variable references. */ referencesVariables(): boolean; /** * Refresh the variable name referenced by this field if this field references * variables. */ refreshVariableName(): void; /** * Search through the list of inputs and their fields in order to find the * parent input of a field. * * @returns The input that the field belongs to. * @internal */ getParentInput(): Input; /** * Returns whether or not we should flip the field in RTL. * * @returns True if we should flip in RTL. */ getFlipRtl(): boolean; /** * Handles the given keyboard shortcut. * * @param _shortcut The shortcut to be handled. * @returns True if the shortcut has been handled, false otherwise. */ onShortcut(_shortcut: KeyboardShortcut): boolean; /** See IFocusableNode.getFocusableElement. */ getFocusableElement(): HTMLElement | SVGElement; /** See IFocusableNode.getFocusableTree. */ getFocusableTree(): IFocusableTree; /** See IFocusableNode.onNodeFocus. */ onNodeFocus(): void; /** See IFocusableNode.onNodeBlur. */ onNodeBlur(): void; /** See IFocusableNode.canBeFocused. */ canBeFocused(): boolean; /** * Subclasses should reimplement this method to construct their Field * subclass from a JSON arg object. * * It is an error to attempt to register a field subclass in the * FieldRegistry if that subclass has not overridden this method. * * @param _options JSON configuration object with properties needed * to configure a specific field. */ static fromJson(_options: FieldConfig): Field; } /** * Extra configuration options for the base field. */ export interface FieldConfig { tooltip?: string; } /** * Represents an object that has all the prototype properties of the `Field` * class. This is necessary because constructors can change * in descendants, though they should contain all of Field's prototype methods. * * This type should only be used in places where we directly access the prototype * of a Field class or subclass. */ type FieldProto = Pick; /** * Represents an error where the field is trying to access its block or * information about its block before it has actually been attached to said * block. */ export declare class UnattachedFieldError extends Error { /** @internal */ constructor(); } export {}; //# sourceMappingURL=field.d.ts.map