(function() {
/**
* @namespace hotspot
* @memberof Help4.control2
*/
Help4.control2.hotspot = {};
/**
* @enum {string}
*/
Help4.control2.hotspot.ANIMATION_TYPES = {
none: 'none',
fadeinout: 'fadeinout',
pulsingripples: 'pulsingripples',
shake: 'shake',
updown: 'updown'
}
const CSS_VARS = {
spotlightOffset: {unit: 'px'},
spotlightBlur: {unit: 'px'},
spotlightOpacity: {}
};
/**
* @typedef {Help4.control2.Control.Params} Help4.control2.hotspot.Hotspot.Params
* @property {number} [borderSize = 0] - size of border in px
* @property {boolean} [spotlight = false] - whether to activate spotlight
* @property {number} [spotlightOffset = 0] - offset for spotlight in px
* @property {number} [spotlightBlur = 0] - blur for spotlight in px
* @property {number} [spotlightOpacity = 0] - opacity for spotlight
* @property {boolean} [callout = false] - whether if it is a callout hotspot
* @property {boolean} [instantHelp = false] - whether if it is an instant help hotspot
*/
/**
* Hotspot base class.
* @abstract
* @augments Help4.control2.Control
* @property {number} borderSize - size of border in px
* @property {boolean} spotlight - whether to activate spotlight
* @property {number} spotlightOffset - offset for spotlight in px
* @property {number} spotlightBlur - blur for spotlight in px
* @property {number} spotlightOpacity - opacity for spotlight
* @property {boolean} callout - whether if it is a callout hotspot
* @property {boolean} instantHelp - whether if it is an instant help hotspot
*/
Help4.control2.hotspot.Hotspot = class extends Help4.control2.Control {
/**
* @override
* @param {Help4.control2.hotspot.Hotspot.Params} [params]
* @param {Help4.jscore.ControlBase.Params} [derived]
*/
constructor(params, derived) {
const {ControlBase} = Help4.jscore;
const T = ControlBase.TYPES;
const TT = ControlBase.TEXT_TYPES;
super(params, {
params: {
role: {init: 'button'}, // needs to be in sync with Connected.js
tabIndex: {init: 1}, // needs to be in sync with Connected.js
borderSize: {type: T.number, readonly: true},
spotlight: {type: T.boolean},
spotlightOffset: {type: T.number},
spotlightBlur: {type: T.number},
spotlightOpacity: {type: T.number},
callout: {type: T.boolean},
instantHelp: {type: T.boolean},
autoEvent: {init: true}
},
statics: {
_content: {destroy: false}
},
config: {
css: 'control-hotspot'
},
texts: {
text: TT.innerText
},
derived
});
}
/**
* @param {string} text
* @returns {Help4.control2.hotspot.Hotspot}
*/
setText(text) {
const {_content} = this;
if (_content) {
_content.innerHTML = '';
if (text) {
const span = this._createElement('span', {css: 'text', dom: _content, text: text});
this._setTextAttribute(span, 'text');
}
}
return this;
}
/**
* @override
* @returns {Help4.control2.hotspot.Hotspot}
*/
focus() {
!this.mobile && Help4.Element.execAfterTransition(this.getDom(), () => !this.isDestroyed() && super.focus());
return this;
}
/**
* gets the position (excluding offsets, including positions like top-left, bottom-right)
* @returns {Help4.control2.PositionXY}
*/
getDragStartPosition() {
// see XRAY-4790; drag enabled hotspots have their own implementation
// this one is a placeholder which returns actual DOM position
return this.getPosition();
}
/**
* @override
*/
getConnectionPoints(params) {
const p = super.getConnectionPoints(params);
// due to CSS layout the connection point t & b appear to be to much left; correct
const b = this.borderSize >> 1;
p.t.x += b;
p.b.x += b;
return p;
}
/**
* returns the meeting point from a line starting in "point" to the hotspot
* @param {Help4.control2.PositionXY} point
* @returns {Help4.control2.PositionXY}
* @abstract
*/
calcMeetingPoint(point) {
return point;
}
/**
* @override
* @param {HTMLElement} dom - control DOM
*/
_onDomCreated(dom) {
dom.setAttribute('aria-haspopup', 'dialog');
// on mobile, we need to add an onclick handler to a div to be able to click it
this.mobile && dom.addEventListener('click', Help4.noop, false);
}
/**
* @override
* @param {Help4.jscore.ControlBase.PropertyChangeEvent} event - the change event
*/
_applyPropertyToDom({name, value, oldValue}) {
switch (name) {
case 'active':
this.getDom().setAttribute('aria-pressed', value);
break;
case 'spotlight':
value ? this.addCss(name) : this.removeCss(name);
_setCSSVars.call(this);
break;
case 'spotlightOffset':
case 'spotlightBlur':
case 'spotlightOpacity':
this.spotlight && _setCSSVars.call(this, name);
break;
case 'callout':
value && !this.instantHelp ? this.addCss(name) : this.removeCss(name);
break;
}
super._applyPropertyToDom({name, value, oldValue});
}
}
/**
* @memberof Help4.control2.hotspot.Hotspot#
* @param {string} [name]
* @private
*/
function _setCSSVars(name) {
const {spotlight} = this;
const apply = (name, {unit = ''}) => {
spotlight
? Help4.setCssVariable(`hotspot-${name}`, this[name] + unit)
: Help4.removeCssVariable(`hotspot-${name}`);
}
if (typeof name === 'string') {
apply(name, CSS_VARS[name]);
} else {
for (const [name, value] of Object.entries(CSS_VARS)) {
apply(name, value);
}
}
}
})();