microsoft-teamsdiscordmessengercustom-servicesmacoslinuxwindowsinboxwhatsappicloudtweetdeckhipchattelegramhangoutsslackgmailskypefacebook-workplaceoutlookemail
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.
264 lines
10 KiB
264 lines
10 KiB
/** |
|
* This plugin can be applied to any `Component` (although almost always to a `Container`) |
|
* to make it fill the browser viewport. This plugin is used internally by the more familiar |
|
* `Ext.container.Viewport` class. |
|
* |
|
* The `Viewport` container is commonly used but it can be an issue if you need to fill the |
|
* viewport with a container that derives from another class (e.g., `Ext.tab.Panel`). Prior |
|
* to this plugin, you would have to do this: |
|
* |
|
* Ext.create('Ext.container.Viewport', { |
|
* layout: 'fit', // full the viewport with the tab panel |
|
* |
|
* items: [{ |
|
* xtype: 'tabpanel', |
|
* items: [{ |
|
* ... |
|
* }] |
|
* }] |
|
* }); |
|
* |
|
* With this plugin you can create the `tabpanel` as the viewport: |
|
* |
|
* Ext.create('Ext.tab.Panel', { |
|
* plugins: 'viewport', |
|
* |
|
* items: [{ |
|
* ... |
|
* }] |
|
* }); |
|
* |
|
* More importantly perhaps is that as a plugin, the view class can be reused in other |
|
* contexts such as the content of a `{@link Ext.window.Window window}`. |
|
* |
|
* The Viewport renders itself to the document body, and automatically sizes itself to the size of |
|
* the browser viewport and manages window resizing. There may only be one Viewport created |
|
* in a page. |
|
* |
|
* ## Responsive Design |
|
* |
|
* This plugin enables {@link Ext.mixin.Responsive#responsiveConfig} for the target component. |
|
* |
|
* @since 5.0.0 |
|
*/ |
|
Ext.define('Ext.plugin.Viewport', { |
|
extend: 'Ext.plugin.Responsive', |
|
|
|
alias: 'plugin.viewport', |
|
|
|
setCmp: function (cmp) { |
|
this.cmp = cmp; |
|
|
|
if (cmp && !cmp.isViewport) { |
|
this.decorate(cmp); |
|
if (cmp.renderConfigs) { |
|
cmp.flushRenderConfigs(); |
|
} |
|
cmp.setupViewport(); |
|
} |
|
}, |
|
|
|
statics: { |
|
decorate: function (target) { |
|
Ext.applyIf(target.prototype || target, { |
|
ariaRole: 'application', |
|
|
|
viewportCls: Ext.baseCSSPrefix + 'viewport' |
|
}); |
|
|
|
Ext.override(target, { |
|
isViewport: true, |
|
|
|
preserveElOnDestroy: true, |
|
|
|
initComponent : function() { |
|
this.callParent(); |
|
this.setupViewport(); |
|
}, |
|
|
|
// Because we don't stamp the size until onRender, our size model |
|
// won't return correctly. As we're always going to be configured, |
|
// just return the value here |
|
getSizeModel: function() { |
|
var configured = Ext.layout.SizeModel.configured; |
|
return configured.pairsByHeightOrdinal[configured.ordinal]; |
|
}, |
|
|
|
handleViewportResize: function () { |
|
var me = this, |
|
Element = Ext.dom.Element, |
|
width = Element.getViewportWidth(), |
|
height = Element.getViewportHeight(); |
|
|
|
if (width !== me.width || height !== me.height) { |
|
me.setSize(width, height); |
|
} |
|
}, |
|
|
|
setupViewport : function() { |
|
var me = this, |
|
el = document.body; |
|
|
|
// Here in the (perhaps unlikely) case that the body dom el doesn't yet have an id, |
|
// we want to give it the same id as the viewport component so getCmp lookups will |
|
// be able to find the owner component. |
|
// |
|
// Note that nothing says that components that use configured elements have to have |
|
// matching ids (they probably won't), but this is at least making the attempt so that |
|
// getCmp *may* be able to find the component. However, in these cases, it's really |
|
// better to use Component#fromElement to find the owner component. |
|
if (!el.id) { |
|
el.id = me.id; |
|
} |
|
|
|
// In addition, let's stamp on the componentIdAttribute so lookups using Component's |
|
// fromElement will work. |
|
el.setAttribute(Ext.Component.componentIdAttribute, me.id); |
|
|
|
el = me.el = Ext.getBody(); |
|
|
|
Ext.fly(document.documentElement).addCls(me.viewportCls); |
|
el.setHeight = el.setWidth = Ext.emptyFn; |
|
el.dom.scroll = 'no'; |
|
me.allowDomMove = false; |
|
me.renderTo = el; |
|
|
|
if (Ext.supports.Touch) { |
|
me.addMeta('apple-mobile-web-app-capable', 'yes'); |
|
} |
|
|
|
// Get the DOM disruption over with before the Viewport renders and begins a layout |
|
Ext.getScrollbarSize(); |
|
|
|
// Clear any dimensions, we will size later on in onRender |
|
me.width = me.height = undefined; |
|
|
|
// ... but take the measurements now because doing that in onRender |
|
// will cause a costly reflow which we just forced with getScrollbarSize() |
|
me.initialViewportHeight = Ext.Element.getViewportHeight(); |
|
me.initialViewportWidth = Ext.Element.getViewportWidth(); |
|
}, |
|
|
|
afterLayout: function(layout) { |
|
if (Ext.supports.Touch) { |
|
document.body.scrollTop = 0; |
|
} |
|
this.callParent([layout]); |
|
}, |
|
|
|
onRender: function() { |
|
var me = this; |
|
|
|
me.callParent(arguments); |
|
|
|
// Important to start life as the proper size (to avoid extra layouts) |
|
// But after render so that the size is not stamped into the body, |
|
// although measurement has to take place before render to avoid |
|
// causing a reflow. |
|
me.width = me.initialViewportWidth; |
|
me.height = me.initialViewportHeight; |
|
|
|
me.initialViewportWidth = me.initialViewportHeight = null; |
|
|
|
// prevent touchmove from panning the viewport in mobile safari |
|
if (Ext.supports.TouchEvents) { |
|
me.mon(Ext.getDoc(), { |
|
touchmove: function(e) { |
|
e.preventDefault(); |
|
}, |
|
translate: false, |
|
delegated: false |
|
}); |
|
} |
|
}, |
|
|
|
initInheritedState: function (inheritedState, inheritedStateInner) { |
|
var me = this, |
|
root = Ext.rootInheritedState; |
|
|
|
if (inheritedState !== root) { |
|
// We need to go at this again but with the rootInheritedState object. Let |
|
// any derived class poke on the proper object! |
|
me.initInheritedState(me.inheritedState = root, |
|
me.inheritedStateInner = Ext.Object.chain(root)); |
|
} else { |
|
me.callParent([inheritedState, inheritedStateInner]); |
|
} |
|
}, |
|
|
|
beforeDestroy: function(){ |
|
var me = this, |
|
root = Ext.rootInheritedState, |
|
key; |
|
|
|
// Clear any properties from the inheritedState so we don't pollute the |
|
// global namespace. If we have a rtl flag set, leave it alone because it's |
|
// likely we didn't write it |
|
for (key in root) { |
|
if (key !== 'rtl') { |
|
delete root[key]; |
|
} |
|
} |
|
|
|
me.removeUIFromElement(); |
|
me.el.removeCls(me.baseCls); |
|
Ext.fly(document.body.parentNode).removeCls(me.viewportCls); |
|
me.callParent(); |
|
}, |
|
|
|
addMeta: function(name, content) { |
|
var meta = document.createElement('meta'); |
|
|
|
meta.setAttribute('name', name); |
|
meta.setAttribute('content', content); |
|
Ext.getHead().appendChild(meta); |
|
}, |
|
|
|
privates: { |
|
// override here to prevent an extraneous warning |
|
applyTargetCls: function (targetCls) { |
|
this.el.addCls(targetCls); |
|
}, |
|
|
|
// Override here to prevent tabIndex set/reset on the body |
|
disableTabbing: function() { |
|
var el = this.el; |
|
|
|
if (el) { |
|
el.saveChildrenTabbableState(); |
|
} |
|
}, |
|
|
|
enableTabbing: function() { |
|
var el = this.el; |
|
|
|
if (el) { |
|
el.restoreChildrenTabbableState(); |
|
} |
|
}, |
|
|
|
getOverflowEl: function() { |
|
return Ext.get(document.documentElement); |
|
} |
|
} |
|
}); |
|
} |
|
}, |
|
|
|
privates: { |
|
updateResponsiveState: function () { |
|
// By providing this method we are in sync with the layout suspend/resume as |
|
// well as other changes to configs that need to happen during this pulse of |
|
// size change. |
|
|
|
// This plugin instance is response, but the cmp is what needs to be handling |
|
// the resize: |
|
this.cmp.handleViewportResize(); |
|
|
|
this.callParent(); |
|
} |
|
} |
|
}, |
|
function (Viewport) { |
|
Viewport.prototype.decorate = Viewport.decorate; |
|
});
|
|
|