linuxwindowsinboxwhatsappicloudtweetdeckhipchattelegramhangoutsslackgmailskypefacebook-workplaceoutlookemailmicrosoft-teamsdiscordmessengercustom-servicesmacos
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.
467 lines
15 KiB
467 lines
15 KiB
9 years ago
|
/**
|
||
|
* 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();
|
||
|
}
|
||
|
});
|