whatsappicloudtweetdeckhipchattelegramhangoutsslackgmailskypefacebook-workplaceoutlookemailmicrosoft-teamsdiscordmessengercustom-servicesmacoslinuxwindowsinbox
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
466 lines
15 KiB
466 lines
15 KiB
/** |
|
* A specialized tooltip class for tooltips that can be specified in markup and automatically managed |
|
* by the global {@link Ext.tip.QuickTipManager} instance. See the QuickTipManager documentation for |
|
* additional usage details and examples. |
|
* |
|
* @example |
|
* Ext.tip.QuickTipManager.init(); // Instantiate the QuickTipManager |
|
* |
|
* Ext.create('Ext.Button', { |
|
* |
|
* renderTo: Ext.getBody(), |
|
* text: 'My Button', |
|
* listeners: { |
|
* |
|
* afterrender: function(me) { |
|
* |
|
* // Register the new tip with an element's ID |
|
* Ext.tip.QuickTipManager.register({ |
|
* target: me.getId(), // Target button's ID |
|
* title : 'My Tooltip', // QuickTip Header |
|
* text : 'My Button has a QuickTip' // Tip content |
|
* }); |
|
* |
|
* } |
|
* } |
|
* }); |
|
* |
|
*/ |
|
Ext.define('Ext.tip.QuickTip', { |
|
extend: 'Ext.tip.ToolTip', |
|
alias: 'widget.quicktip', |
|
alternateClassName: 'Ext.QuickTip', |
|
|
|
/** |
|
* @cfg {String/HTMLElement/Ext.dom.Element} target |
|
* The target HTMLElement, {@link Ext.dom.Element} or id to associate with this Quicktip. |
|
* |
|
* Defaults to the document. |
|
*/ |
|
|
|
/** |
|
* @cfg {Boolean} interceptTitles |
|
* `true` to automatically use the element's DOM title value if available. |
|
*/ |
|
interceptTitles : false, |
|
|
|
/** |
|
* @cfg {String/Ext.panel.Title} title |
|
* The title text to be used to display in the Tip header. May be a string |
|
* (including HTML tags) or an {@link Ext.panel.Title} config object. |
|
*/ |
|
title: ' ', |
|
|
|
/** |
|
* @private |
|
*/ |
|
tagConfig : { |
|
namespace : 'data-', |
|
attribute : 'qtip', |
|
width : 'qwidth', |
|
target : 'target', |
|
title : 'qtitle', |
|
hide : 'hide', |
|
cls : 'qclass', |
|
align : 'qalign', |
|
anchor : 'anchor', |
|
showDelay: 'qshowDelay', |
|
hideAction: 'hideAction', |
|
anchorTarget: 'anchorTarget' |
|
}, |
|
|
|
shrinkWrapDock: true, |
|
|
|
initComponent : function(){ |
|
var me = this; |
|
|
|
me.target = me.target || Ext.getDoc(); |
|
me.targets = me.targets || {}; |
|
me.callParent(); |
|
}, |
|
|
|
setTagConfig: function(cfg) { |
|
this.tagConfig = Ext.apply({}, cfg); |
|
// Let attr get recomputed |
|
delete this.tagConfig.attr; |
|
}, |
|
|
|
/** |
|
* @cfg text |
|
* @inheritdoc Ext.tip.ToolTip#cfg-html |
|
*/ |
|
text: null, |
|
|
|
/** |
|
* @cfg html |
|
* @hide |
|
* -- hidden for Ext.tip.QuickTip - see #cfg-text |
|
*/ |
|
|
|
/** |
|
* Configures a new quick tip instance and assigns it to a target element. |
|
* |
|
* For example usage, see the {@link Ext.tip.QuickTipManager} class header. |
|
* |
|
* @param {Object} config The config object with the following properties: |
|
* @param config.target (required) The target HTMLElement, {@link Ext.dom.Element} or |
|
* id to associate with this Quicktip. See {@link #target}. |
|
* @param config.text Tip body content. See {@link #text}. |
|
* @param config.title Tip header. See {@link #title}. |
|
* @param config.autoHide False to prevent the tip from automatically hiding on |
|
* mouseleave. See {@link #autoHide}. |
|
* @param config.cls An optional extra CSS class that will be added to the tip. See |
|
* {@link #cls}. |
|
* @param config.dismissDelay Delay in milliseconds before the tooltip automatically |
|
* hides (overrides singleton value). See {@link #dismissDelay}. |
|
* @param config.width Tip width in pixels. See {@link #width}. |
|
*/ |
|
register : function(config){ |
|
var configs = Ext.isArray(config) ? config : arguments, |
|
i = 0, |
|
len = configs.length, |
|
target, j, targetLen; |
|
|
|
for (; i < len; i++) { |
|
config = configs[i]; |
|
target = config.target; |
|
if (target) { |
|
if (Ext.isArray(target)) { |
|
for (j = 0, targetLen = target.length; j < targetLen; j++) { |
|
this.targets[Ext.id(target[j])] = config; |
|
} |
|
} else{ |
|
this.targets[Ext.id(target)] = config; |
|
} |
|
} |
|
} |
|
}, |
|
|
|
/** |
|
* Removes this quick tip from its element and destroys it. |
|
* @param {String/HTMLElement/Ext.dom.Element} el The element from which the quick tip |
|
* is to be removed or ID of the element. |
|
*/ |
|
unregister : function(el){ |
|
delete this.targets[Ext.id(el)]; |
|
}, |
|
|
|
/** |
|
* Hides a visible tip or cancels an impending show for a particular element. |
|
* @param {String/HTMLElement/Ext.dom.Element} el The element that is the target of |
|
* the tip or ID of the element. |
|
*/ |
|
cancelShow: function(el){ |
|
var me = this, |
|
activeTarget = me.activeTarget; |
|
|
|
el = Ext.get(el).dom; |
|
if (me.isVisible()) { |
|
if (activeTarget && activeTarget.el === el) { |
|
me.hide(); |
|
} |
|
} else if (activeTarget && activeTarget.el === el) { |
|
me.clearTimer('show'); |
|
} |
|
}, |
|
|
|
/** |
|
* @private |
|
* Reads the tip text from the closest node to the event target which contains the |
|
* attribute we are configured to look for. Returns an object containing the text |
|
* from the attribute, and the target element from which the text was read. |
|
*/ |
|
getTipCfg: function (target, event) { |
|
var titleText = target.title, |
|
cfg = this.tagConfig, |
|
attr = cfg.attr || (cfg.attr = cfg.namespace + cfg.attribute), |
|
text; |
|
|
|
if (this.interceptTitles && titleText && Ext.isString(titleText)) { |
|
target.setAttribute(attr, titleText); |
|
target.removeAttribute('title'); |
|
|
|
return { |
|
text: titleText |
|
}; |
|
} |
|
else { |
|
target = Ext.fly(target).findParent(function(dom) { |
|
// Want to test the truthiness of the attribute and save it. |
|
return (text = dom.getAttribute(attr)); |
|
}); |
|
if (target) { |
|
return { |
|
target: target, |
|
text: text |
|
}; |
|
} |
|
} |
|
}, |
|
|
|
onTargetOver: function (event) { |
|
// This is a separate method so that tooltip activation can be done by calling |
|
// doTargetOver directly. |
|
this.doTargetOver(event.getTarget(this.delegate), null, event); |
|
}, |
|
|
|
/** |
|
* @private |
|
*/ |
|
doTargetOver : function (target, xy, event) { |
|
var me = this, |
|
hasShowDelay, hideAction, delay, elTarget, cfg, ns, tipConfig, autoHide, |
|
targets, targetEl, value, key; |
|
|
|
if (me.disabled) { |
|
return; |
|
} |
|
|
|
if (typeof target === 'string') { |
|
target = Ext.getDom(target); |
|
} |
|
|
|
me.targetXY = xy || (event ? event.getXY() : Ext.fly(target).getXY()); |
|
|
|
// If the over target was filtered out by the delegate selector, or is not an HTMLElement, or is the <html> or the <body>, then return |
|
if(!target || target.nodeType !== 1 || target === document.documentElement || target === document.body){ |
|
return; |
|
} |
|
|
|
if (me.activeTarget && ((target === me.activeTarget.el) || Ext.fly(me.activeTarget.el).contains(target))) { |
|
// We may have started a delayed show where we have an active target. |
|
// If the timer is yet to be fired, but the mouse moves, it will try to |
|
// show it immediately. If the attribute has been removed from the element, |
|
// we want to cancel the show. |
|
if (me.targetTextEmpty()) { |
|
me.onShowVeto(); |
|
delete me.activeTarget; |
|
} else { |
|
me.clearTimer('hide'); |
|
me.show(); |
|
} |
|
return; |
|
} |
|
|
|
if (target) { |
|
targets = me.targets; |
|
|
|
for (key in targets) { |
|
if (targets.hasOwnProperty(key)) { |
|
value = targets[key]; |
|
|
|
targetEl = Ext.fly(value.target); |
|
if (targetEl && (targetEl.dom === target || targetEl.contains(target))) { |
|
elTarget = targetEl.dom; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
if (elTarget) { |
|
me.activeTarget = me.targets[elTarget.id]; |
|
me.activeTarget.el = target; |
|
me.anchor = me.activeTarget.anchor; |
|
if (me.anchor) { |
|
me.anchorTarget = target; |
|
} |
|
hasShowDelay = parseInt(me.activeTarget.showDelay, 10); |
|
if (hasShowDelay) { |
|
delay = me.showDelay; |
|
me.showDelay = hasShowDelay; |
|
} |
|
me.delayShow(); |
|
if (hasShowDelay) { |
|
me.showDelay = delay; |
|
} |
|
if (!(hideAction = me.activeTarget.hideAction)) { |
|
delete me.hideAction; |
|
} else { |
|
me.hideAction = hideAction; |
|
} |
|
return; |
|
} |
|
} |
|
|
|
// Should be a fly. |
|
elTarget = Ext.fly(target, '_quicktip-target'); |
|
cfg = me.tagConfig; |
|
ns = cfg.namespace; |
|
tipConfig = me.getTipCfg(target, event); |
|
|
|
if (tipConfig) { |
|
|
|
// getTipCfg may look up the parentNode axis for a tip text attribute and will return the new target node. |
|
// Change our target element to match that from which the tip text attribute was read. |
|
if (tipConfig.target) { |
|
target = tipConfig.target; |
|
elTarget = Ext.fly(target, '_quicktip-target'); |
|
} |
|
autoHide = elTarget.getAttribute(ns + cfg.hide); |
|
|
|
me.activeTarget = { |
|
el: target, |
|
text: tipConfig.text, |
|
width: +elTarget.getAttribute(ns + cfg.width) || null, |
|
autoHide: autoHide !== "user" && autoHide !== 'false', |
|
title: elTarget.getAttribute(ns + cfg.title), |
|
cls: elTarget.getAttribute(ns + cfg.cls), |
|
align: elTarget.getAttribute(ns + cfg.align), |
|
showDelay: parseInt(elTarget.getAttribute(ns + cfg.showDelay)||0, 10), |
|
hideAction: elTarget.getAttribute(ns + cfg.hideAction), |
|
anchorTarget: elTarget.getAttribute(ns + cfg.anchorTarget) |
|
}; |
|
|
|
// If we were not configured with an anchor, allow it to be set by the target's properties |
|
if (!me.initialConfig.hasOwnProperty('anchor')) { |
|
me.anchor = elTarget.getAttribute(ns + cfg.anchor); |
|
} |
|
|
|
// If we are anchored, and not configured with an anchorTarget, anchor to the target element, or whatever its 'data-anchortarget' points to |
|
if (me.anchor && !me.initialConfig.hasOwnProperty('anchorTarget')) { |
|
me.anchorTarget = me.activeTarget.anchorTarget || target; |
|
} |
|
hasShowDelay = parseInt(me.activeTarget.showDelay, 10); |
|
if (hasShowDelay) { |
|
delay = me.showDelay; |
|
me.showDelay = hasShowDelay; |
|
} |
|
me.delayShow(); |
|
if (hasShowDelay) { |
|
me.showDelay = delay; |
|
} |
|
} |
|
}, |
|
|
|
/** |
|
* @private |
|
*/ |
|
onTargetOut : function(e){ |
|
var me = this, |
|
active = me.activeTarget, |
|
hasHideDelay, |
|
delay; |
|
|
|
// If moving within the current target, and it does not have a new tip, ignore |
|
// the mouseout. e.within is the only correct way to determine this. |
|
if (active && e.within(me.activeTarget.el) && !me.getTipCfg(e.getTarget(), e)) { |
|
return; |
|
} |
|
|
|
me.clearTimer('show'); |
|
delete me.activeTarget; |
|
if (me.autoHide !== false) { |
|
hasHideDelay = active && parseInt(active.hideDelay, 10); |
|
if (hasHideDelay) { |
|
delay = me.hideDelay; |
|
me.hideDelay = hasHideDelay; |
|
} |
|
me.delayHide(); |
|
if (hasHideDelay) { |
|
me.hideDelay = delay; |
|
} |
|
} |
|
}, |
|
|
|
targetTextEmpty: function(){ |
|
var me = this, |
|
target = me.activeTarget, |
|
cfg = me.tagConfig, |
|
el, text; |
|
|
|
if (target) { |
|
el = target.el; |
|
if (el) { |
|
text = el.getAttribute(cfg.namespace + cfg.attribute); |
|
// Note that the quicktip could also have been registered with the QuickTipManager. |
|
// If this was the case, then we don't want to veto showing it. |
|
// Simply do a lookup in the registered targets collection. |
|
if (!text && !me.targets[Ext.id(target.target)]) { |
|
return true; |
|
} |
|
} |
|
} |
|
return false; |
|
}, |
|
|
|
show: function(){ |
|
var me = this, |
|
fromDelay = me.fromDelayShow; |
|
|
|
// We're coming from a delayed show, so check whether |
|
// the attribute has been removed before we show it |
|
if (fromDelay && me.targetTextEmpty()) { |
|
me.onShowVeto(); |
|
delete me.activeTarget; |
|
return; |
|
} |
|
me.callParent(arguments); |
|
}, |
|
|
|
/** |
|
* @inheritdoc Ext.tip.Tip#method-showAt |
|
*/ |
|
showAt : function(xy){ |
|
var me = this, |
|
target = me.activeTarget, |
|
header = me.header, |
|
dismiss, cls; |
|
|
|
if (target) { |
|
if (!me.rendered) { |
|
me.render(Ext.getBody()); |
|
me.activeTarget = target; |
|
} |
|
me.suspendLayouts(); |
|
if (target.title) { |
|
me.setTitle(target.title); |
|
header.show(); |
|
} else if (header) { |
|
header.hide(); |
|
} |
|
me.update(target.text); |
|
me.autoHide = target.autoHide; |
|
dismiss = target.dismissDelay; |
|
|
|
me.dismissDelay = Ext.isNumber(dismiss) ? dismiss : me.dismissDelay; |
|
if (target.mouseOffset) { |
|
xy[0] += target.mouseOffset[0]; |
|
xy[1] += target.mouseOffset[1]; |
|
} |
|
|
|
cls = me.lastCls; |
|
if (cls) { |
|
me.removeCls(cls); |
|
delete me.lastCls; |
|
} |
|
|
|
cls = target.cls; |
|
if (cls) { |
|
me.addCls(cls); |
|
me.lastCls = cls; |
|
} |
|
|
|
me.setWidth(target.width); |
|
|
|
if (me.anchor) { |
|
me.constrainPosition = false; |
|
} else if (target.align) { // TODO: this doesn't seem to work consistently |
|
xy = me.getAlignToXY(target.el, target.align); |
|
me.constrainPosition = false; |
|
}else{ |
|
me.constrainPosition = true; |
|
} |
|
me.resumeLayouts(true); |
|
} |
|
me.callParent([xy]); |
|
}, |
|
|
|
/** |
|
* @method hide |
|
* @inheritdoc |
|
*/ |
|
hide: function(){ |
|
delete this.activeTarget; |
|
this.callParent(); |
|
} |
|
});
|
|
|