9 * @class Roo.bootstrap.Popover
10 * @extends Roo.bootstrap.Component
12 * Bootstrap Popover class
13 * @cfg {String} html contents of the popover (or false to use children..)
14 * @cfg {String} title of popover (or false to hide)
15 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
16 * @cfg {String} trigger click || hover (or false to trigger manually)
17 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
18 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19 * - if false and it has a 'parent' then it will be automatically added to that element
20 * - if string - Roo.get will be called
21 * @cfg {Number} delay - delay before showing
24 * Create a new Popover
25 * @param {Object} config The config object
28 Roo.bootstrap.Popover = function(config){
29 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
35 * After the popover show
37 * @param {Roo.bootstrap.Popover} this
42 * After the popover hide
44 * @param {Roo.bootstrap.Popover} this
50 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
56 trigger : 'hover', // hover
62 can_build_overlaid : false,
64 maskEl : false, // the mask element
67 alignEl : false, // when show is called with an element - this get's stored.
69 getChildContainer : function()
71 return this.contentEl;
74 getPopoverHeader : function()
76 this.title = true; // flag not to hide it..
77 this.headerEl.addClass('p-0');
82 getAutoCreate : function(){
85 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
86 style: 'display:block',
92 cls : 'popover-inner ',
96 cls: 'popover-title popover-header',
97 html : this.title === false ? '' : this.title
100 cls : 'popover-content popover-body ' + (this.cls || ''),
101 html : this.html || ''
112 * @param {string} the title
114 setTitle: function(str)
118 this.headerEl.dom.innerHTML = str;
123 * @param {string} the body content
125 setContent: function(str)
128 if (this.contentEl) {
129 this.contentEl.dom.innerHTML = str;
133 // as it get's added to the bottom of the page.
134 onRender : function(ct, position)
136 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
141 var cfg = Roo.apply({}, this.getAutoCreate());
145 cfg.cls += ' ' + this.cls;
148 cfg.style = this.style;
150 //Roo.log("adding to ");
151 this.el = Roo.get(document.body).createChild(cfg, position);
155 this.contentEl = this.el.select('.popover-content',true).first();
156 this.headerEl = this.el.select('.popover-title',true).first();
159 if(typeof(this.items) != 'undefined'){
160 var items = this.items;
163 for(var i =0;i < items.length;i++) {
164 nitems.push(this.addxtype(Roo.apply({}, items[i])));
170 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
171 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
178 resizeMask : function()
181 Roo.lib.Dom.getViewWidth(true),
182 Roo.lib.Dom.getViewHeight(true)
186 initEvents : function()
190 Roo.bootstrap.Popover.register(this);
193 this.arrowEl = this.el.select('.arrow',true).first();
194 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
195 this.el.enableDisplayMode('block');
199 if (this.over === false && !this.parent()) {
202 if (this.triggers === false) {
207 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
208 var triggers = this.trigger ? this.trigger.split(' ') : [];
209 Roo.each(triggers, function(trigger) {
211 if (trigger == 'click') {
212 on_el.on('click', this.toggle, this);
213 } else if (trigger != 'manual') {
214 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
215 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
217 on_el.on(eventIn ,this.enter, this);
218 on_el.on(eventOut, this.leave, this);
228 toggle : function () {
229 this.hoverState == 'in' ? this.leave() : this.enter();
232 enter : function () {
234 clearTimeout(this.timeout);
236 this.hoverState = 'in';
238 if (!this.delay || !this.delay.show) {
243 this.timeout = setTimeout(function () {
244 if (_t.hoverState == 'in') {
251 clearTimeout(this.timeout);
253 this.hoverState = 'out';
255 if (!this.delay || !this.delay.hide) {
260 this.timeout = setTimeout(function () {
261 if (_t.hoverState == 'out') {
268 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
269 * @param {string} (left|right|top|bottom) position
271 show : function (on_el, placement)
273 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
274 on_el = on_el || false; // default to false
277 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
278 on_el = this.parent().el;
279 } else if (this.over) {
280 on_el = Roo.get(this.over);
285 this.alignEl = Roo.get( on_el );
288 this.render(document.body);
294 if (this.title === false) {
295 this.headerEl.hide();
300 this.el.dom.style.display = 'block';
304 this.updatePosition(this.placement, true);
307 // this is usually just done by the builder = to show the popoup in the middle of the scren.
308 var es = this.el.getSize();
309 var x = Roo.lib.Dom.getViewWidth()/2;
310 var y = Roo.lib.Dom.getViewHeight()/2;
311 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
316 //var arrow = this.el.select('.arrow',true).first();
317 //arrow.set(align[2],
319 this.el.addClass('in');
323 this.hoverState = 'in';
326 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
327 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
328 this.maskEl.dom.style.display = 'block';
329 this.maskEl.addClass('show');
331 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
333 this.fireEvent('show', this);
337 * fire this manually after loading a grid in the table for example
338 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
339 * @param {Boolean} try and move it if we cant get right position.
341 updatePosition : function(placement, try_move)
343 // allow for calling with no parameters
344 placement = placement ? placement : this.placement;
345 try_move = typeof(try_move) == 'undefined' ? true : try_move;
347 this.el.removeClass([
348 'fade','top','bottom', 'left', 'right','in',
349 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
351 this.el.addClass(placement + ' bs-popover-' + placement);
353 if (!this.alignEl ) {
359 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
360 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
361 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
362 //normal display... or moved up/down.
363 this.el.setXY(offset);
364 var xy = this.alignEl.getAnchorXY('tr', false);
366 this.arrowEl.setXY(xy);
369 // continue through...
370 return this.updatePosition('left', false);
374 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
375 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
376 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
377 //normal display... or moved up/down.
378 this.el.setXY(offset);
379 var xy = this.alignEl.getAnchorXY('tl', false);
380 xy[0]-=10;xy[1]+=5; // << fix me
381 this.arrowEl.setXY(xy);
385 return this.updatePosition('right', false);
388 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
389 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
390 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
391 //normal display... or moved up/down.
392 this.el.setXY(offset);
393 var xy = this.alignEl.getAnchorXY('t', false);
394 xy[1]-=10; // << fix me
395 this.arrowEl.setXY(xy);
399 return this.updatePosition('bottom', false);
402 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
403 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
404 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
405 //normal display... or moved up/down.
406 this.el.setXY(offset);
407 var xy = this.alignEl.getAnchorXY('b', false);
408 xy[1]+=2; // << fix me
409 this.arrowEl.setXY(xy);
413 return this.updatePosition('top', false);
424 this.el.setXY([0,0]);
425 this.el.removeClass('in');
427 this.hoverState = null;
428 this.maskEl.hide(); // always..
429 this.fireEvent('hide', this);
435 Roo.apply(Roo.bootstrap.Popover, {
438 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
439 'right' : ['l-br', [10,0], 'right bs-popover-right'],
440 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
441 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
450 onMouseDown : function(e)
452 if (this.popups.length && !e.getTarget(".roo-popover")) {
453 /// what is nothing is showing..
462 register : function(popup)
464 if (!Roo.bootstrap.Popover.clickHandler) {
465 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
467 // hide other popups.
468 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
469 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
470 this.hideAll(); //<< why?
471 //this.popups.push(popup);
475 this.popups.forEach(function(p) {
479 onShow : function() {
480 Roo.bootstrap.Popover.popups.push(this);
482 onHide : function() {
483 Roo.bootstrap.Popover.popups.remove(this);