|
| 1 | +import {toLatLng} from './position-formats'; |
| 2 | +import { |
| 3 | + AttributeKey, |
| 4 | + attributeKeys, |
| 5 | + Attributes, |
| 6 | + Position |
| 7 | +} from './marker-attributes'; |
| 8 | + |
| 9 | +import type {Marker} from './marker'; |
| 10 | +import type {StaticAttributes} from './marker-attributes'; |
| 11 | + |
| 12 | +/** |
| 13 | + * ComputedMarkerAttributes resolves all attributes based on dynamic and static |
| 14 | + * values and makes them behave as if there were only static attributes. |
| 15 | + */ |
| 16 | +export class ComputedMarkerAttributes<T = unknown> |
| 17 | + implements Partial<StaticAttributes> |
| 18 | +{ |
| 19 | + private marker_: Marker<T>; |
| 20 | + private callbackDepth_: number = 0; |
| 21 | + |
| 22 | + // Attributes are declaration-only, the implementataion uses dynamic |
| 23 | + // getters/setters created in the static initializer |
| 24 | + |
| 25 | + // note: internally, the position-attribute uses the google.maps.LatLng |
| 26 | + // type instead of the generic Position type. |
| 27 | + declare readonly position?: google.maps.LatLng; |
| 28 | + declare readonly draggable?: StaticAttributes['draggable']; |
| 29 | + declare readonly collisionBehavior?: StaticAttributes['collisionBehavior']; |
| 30 | + declare readonly title?: StaticAttributes['title']; |
| 31 | + declare readonly zIndex?: StaticAttributes['zIndex']; |
| 32 | + |
| 33 | + declare readonly glyph?: StaticAttributes['glyph']; |
| 34 | + declare readonly scale?: StaticAttributes['scale']; |
| 35 | + declare readonly color?: StaticAttributes['color']; |
| 36 | + declare readonly backgroundColor?: StaticAttributes['backgroundColor']; |
| 37 | + declare readonly borderColor?: StaticAttributes['borderColor']; |
| 38 | + declare readonly glyphColor?: StaticAttributes['glyphColor']; |
| 39 | + declare readonly icon?: StaticAttributes['icon']; |
| 40 | + |
| 41 | + constructor(marker: Marker<T>) { |
| 42 | + this.marker_ = marker; |
| 43 | + } |
| 44 | + |
| 45 | + private getComputedAttributeValue<TKey extends AttributeKey>( |
| 46 | + key: TKey |
| 47 | + ): StaticAttributes[TKey] | undefined { |
| 48 | + const value = this.marker_[key]; |
| 49 | + if (typeof value !== 'function') { |
| 50 | + return this.marker_[key] as StaticAttributes[TKey]; |
| 51 | + } |
| 52 | + |
| 53 | + this.callbackDepth_++; |
| 54 | + if (this.callbackDepth_ > 10) { |
| 55 | + throw new Error( |
| 56 | + 'maximum recursion depth reached. ' + |
| 57 | + 'This is probably caused by a cyclic dependency in dynamic attributes.' |
| 58 | + ); |
| 59 | + } |
| 60 | + |
| 61 | + const {map, data, marker} = this.marker_.getDynamicAttributeState(); |
| 62 | + const res = value({data, map, marker, attr: this}); |
| 63 | + this.callbackDepth_--; |
| 64 | + |
| 65 | + return res as StaticAttributes[TKey]; |
| 66 | + } |
| 67 | + |
| 68 | + /** |
| 69 | + * The static initializer sets up the implementation for the properties of the |
| 70 | + * ComputedMarkerAttributes class. |
| 71 | + */ |
| 72 | + static { |
| 73 | + Object.defineProperty(this.prototype, 'position', { |
| 74 | + get(this: ComputedMarkerAttributes) { |
| 75 | + const userValue = this.getComputedAttributeValue('position'); |
| 76 | + |
| 77 | + if (userValue) return toLatLng(userValue); |
| 78 | + } |
| 79 | + }); |
| 80 | + |
| 81 | + // position is treated separately, so it is skipped here |
| 82 | + for (const key of attributeKeys) { |
| 83 | + if (key === 'position') { |
| 84 | + continue; |
| 85 | + } |
| 86 | + |
| 87 | + Object.defineProperty(this.prototype, key, { |
| 88 | + get(this: ComputedMarkerAttributes) { |
| 89 | + return this.getComputedAttributeValue(key); |
| 90 | + } |
| 91 | + }); |
| 92 | + } |
| 93 | + } |
| 94 | +} |
0 commit comments