/** * Return type of the {@link Bindable} watch function, used to unregister a watch. */ interface WatchHandle { /** Unregister the watch handle */ remove(): void; } type WatchCallback = () => void; /** * The interface that must be implemented by the left and right side objects of a Binding. * Methods of this interface enable the synchronization of watches by making properties reactive: * when a property changes, watchers of that property (that is, the binding) will be notified. */ interface BindableInterface { /** * Sets the property `propName` to `value`. This function is called * by Bindings to propagate property changes from one object to the other. * * When a property called `propName` on the "source" Bindable of a binding changes, * the "target" Bindable will eventually receive the updated value via this function. * * @param propName the name of the property * @param value the new value of the property */ set?(propName: PropName, value: any): void; /** * Returns the value of the property `propName`. This function will be called, * for example, when the Binding is notified about an update (via watch). * * @param propName the name of the property * @returns the current value of the property */ get?(propName: PropName): any; /** * Registers a watch callback for the property `propName` on this Bindable. * * This function will be called by a Binding when it is configured to synchronize * changes of this property to another object. The callback must be invoked by this Bindable when * the property is updated in order to notify the Binding about the change. * * @param propName the name of the property * @param callback a callback function that takes no arguments * @returns an object implementing {@link WatchHandle} that allows the Binding to unregister the watch. */ watch?(propName: PropName, callback: WatchCallback): WatchHandle; } /** * All methods on the bindable interface are optional, you can also use plain objects. * * The "Record" part is a workaround to allow arbitrary plain data objects. * If BindableInterface is used directly, the compiler complains because of "no common properties": * https://stackoverflow.com/questions/46449237/type-x-has-no-properties-in-common-with-type-y */ type Bindable = BindableInterface & Record; /** * Conversion callback * * @param newValue provided as an array if multiple values * @param context see {@link Context} * @example * ``` * Binding.for(left, right).syncToRight(["heading", "tilt"], "camera", ([heading, tilt], context) => { * const camera = context.targetValue(); * if(camera.heading === heading && camera.tilt === tilt) { * return context.ignore(); * } * let newCamera = camera.clone(); * newCamera.heading = heading; * newCamera.tilt = tilt; * return newCamera; * }); * ``` */ type ConvertFunction = (newValue: any, context: Context) => any; /** * The Context is available in conversion functions. */ interface Context { /** The source bindable. */ readonly source: Bindable; /** The target bindable. */ readonly target: Bindable; /** The property name on the source side (for single source property). */ readonly sourceName: PropertyName; /** The property names on the source side (for multiple source properties). */ readonly sourceNames: readonly PropertyName[]; /** The property name on the target side (for single source property). */ readonly targetName: PropertyName; /** The property names on the target side (for multiple source properties). */ readonly targetNames: readonly PropertyName[]; /** The old property value. Provided if single source property. */ oldValue(): any; /** The new property value. Provided if single source property. */ newValue(): any; /** The current property value on the target side. Provided if single source property. */ targetValue(): any; /** The old property values. Provided if multiple source properties. */ oldValues(): any[]; /** The new property values. Provided if multiple source properties. */ newValues(): any[]; /** The current property values on the target side. Provided if multiple source properties. */ targetValues(): any; /** execute and return for avoiding mutation */ ignore(): void; /** * Wraps a promise to signal that it should be handled as plain value. * @param candidate a promise. * @returns a wrapper for the promise. */ promiseValue(candidate: PromiseLike): any; } type PropertyName = string | symbol; /** * Factory for initializing binding instances */ interface Factory { /** * Creates binding. * Equivalent to `Binding.create().bindTo(left, right)` * @param left * @param right * @returns a new Binding */ for(left: Bindable, right: Bindable): Binding; /** * Creates empty binding. * @returns a new binding */ create(): Binding; /** * Produces a value which may be returned by converter functions to signal that no further processing is required. * @returns ignore symbol. */ ignore(): typeof IGNORE; /** * Wraps a promise to signal that it should be handled as plain value. * @param candidate a promise. * @returns a wrapper for the promise. */ promiseValue(candidate: PromiseLike): any; } declare const IGNORE: unique symbol; /** * Class for two way binding between bindables. */ interface Binding { readonly left: Bindable | undefined; readonly right: Bindable | undefined; readonly enabled: boolean; readonly bound: boolean; /** * Bind two bindables to each other * @param left * @param right * @example * ``` * Binding.create().bindTo(esriAccessor, vueComponent); * ``` */ bindTo(left: Bindable, right: Bindable): this; /** * Unbind two models from each other */ unbind(): this; /** * Semantically equivalent to unbind. It is provided to allow simpler registration of cleanups as event handles. * @example * ``` * let dijitWidget= ... * let binding = Binding.create(); * ... * // clean up binding if widget is destroyed * dijitWidget.own(binding); * ``` */ remove(): this; /** * Define properties to be synchronized and converted between two bindables. * Represents a combination of `syncToRight`/`syncToLeft` * * @param leftProperty * @param rightProperty * @param convertLeftToRight if omitted, updated value will not be converted * @param convertRightToLeft if omitted, updated value will not be converted * @example * ``` * Binding.for(esriAccessor, vueComponent).sync('textField', 'numberField', Number, String); * ``` */ sync(leftProperty: LeftPropName | LeftPropName[], rightProperty: RightPropName | RightPropName[], convertLeftToRight?: ConvertFunction, convertRightToLeft?: ConvertFunction): this; sync(leftProperty: LeftPropName | (LeftPropName | RightPropName)[], convertLeftToRight?: ConvertFunction, convertRightToLeft?: ConvertFunction): this; /** * Define properties to be synchronized and converted from left bindable to right bindable * * @param leftProperty * @param rightProperty * @param convertValue if omitted, updated value will not be converted * @example * ``` * Binding.for(esriAccessor, vueComponent).syncToRight('textField', 'numberField', Number); * ``` */ syncToRight(leftProperty: LeftPropName | LeftPropName[], rightProperty: RightPropName | RightPropName[], convertValue?: ConvertFunction): this; syncToRight(leftProperty: LeftPropName | LeftPropName[], convertValue?: ConvertFunction): this; /** * Define properties to be synchronized and converted from right bindable to left bindable * * @param rightProperty * @param leftProperty * @param convertValue if omitted, updated value will not be converted * @example * ``` * Binding.for(esriAccessor, vueComponent).syncToLeft('numberField', 'textField', String); * ``` */ syncToLeft(rightProperty: RightPropName | RightPropName[], leftProperty: LeftPropName | LeftPropName[], convertValue?: ConvertFunction): this; syncToLeft(rightProperty: RightPropName | RightPropName[], convertValue?: ConvertFunction): this; /** * Define properties to be synchronized between two bindables. * * @param properties * @example * ``` * // synchronize between properties with same names * Binding.for(esriAccessor, vueComponent).syncAll('textField', 'otherTextField'); * * // synchronize between properties with different names * Binding.for(esriAccessor, vueComponent).syncAll({ * 'latitude: 'x', * 'longitude: 'y' * }); * ``` */ syncAll(properties: Partial>): this; syncAll(...properties: (LeftPropName | RightPropName | ConvertFunction | undefined)[]): this; /** * Define properties to be synchronized between from left to right bindables. * * @param properties * @example * ``` * // synchronize between properties with same names * Binding.for(esriAccessor, vueComponent).syncAllToRight('textField', 'otherTextField'); * * // synchronize between properties with different names * Binding.for(esriAccessor, vueComponent).syncAllToRight({ * 'latitude: 'x', * 'longitude: 'y' * }); * ``` */ syncAllToRight(properties: Partial>): this; syncAllToRight(...properties: (LeftPropName | ConvertFunction | undefined)[]): this; /** * Define properties to be synchronized between from left to right bindables. * * @param properties * @example * ``` * // synchronize between properties with same names * Binding.for(esriAccessor, vueComponent).syncAllToLeft('textField', 'otherTextField'); * * // synchronize between properties with different names * Binding.for(esriAccessor, vueComponent).syncAllToLeft({ * 'latitude: 'x', * 'longitude: 'y' * }); * ``` */ syncAllToLeft(properties: Partial>): this; syncAllToLeft(...properties: (RightPropName | ConvertFunction | undefined)[]): this; /** * Trigger the synchronization of the properties of left to their pedants on right. */ syncToRightNow(): this; /** * Trigger the synchronization of the properties of right to their pedants on left. */ syncToLeftNow(): this; /** * Start watching for changes on bindables * * @example * ``` * Binding.for(esriAccessor, vueComponent).enable(); * ``` */ enable(): this; /** * Stop watching for changes on bindables * * @example * ``` * Binding.for(esriAccessor, vueComponent).enable().disable(); * ``` */ disable(): this; } /** * Factory for the construction of bindings. * * @example * ``` * import { Binding } from "apprt-binding/Binding"; * const binding = Binding.for(left, right); * ``` */ declare const Binding: Factory; export { Binding, Binding as default }; export type { Bindable, BindableInterface, Context, ConvertFunction, Factory, PropertyName, WatchCallback, WatchHandle };