X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/0494b8d9b9bb03ab6c22b34dae81261e3cd7e3e6..7a654f8d43fdb43d78b63d90528bed6e86b608cc:/src/resizer/Resizer.js diff --git a/src/resizer/Resizer.js b/src/resizer/Resizer.js new file mode 100644 index 00000000..943c3b9e --- /dev/null +++ b/src/resizer/Resizer.js @@ -0,0 +1,434 @@ +/** + * @class Ext.resizer.Resizer + *

Applies drag handles to an element or component to make it resizable. The + * drag handles are inserted into the element (or component's element) and + * positioned absolute.

+ * + *

Textarea and img elements will be wrapped with an additional div because + * these elements do not support child nodes. The original element can be accessed + * through the originalTarget property.

+ * + *

Here is the list of valid resize handles:

+ *
+Value   Description
+------  -------------------
+ 'n'     north
+ 's'     south
+ 'e'     east
+ 'w'     west
+ 'nw'    northwest
+ 'sw'    southwest
+ 'se'    southeast
+ 'ne'    northeast
+ 'all'   all
+
+ * {@img Ext.resizer.Resizer/Ext.resizer.Resizer.png Ext.resizer.Resizer component} + *

Here's an example showing the creation of a typical Resizer:

+ *

+    
+ + Ext.create('Ext.resizer.Resizer', { + el: 'elToResize', + handles: 'all', + minWidth: 200, + minHeight: 100, + maxWidth: 500, + maxHeight: 400, + pinned: true + }); +
+*/ +Ext.define('Ext.resizer.Resizer', { + mixins: { + observable: 'Ext.util.Observable' + }, + uses: ['Ext.resizer.ResizeTracker', 'Ext.Component'], + + alternateClassName: 'Ext.Resizable', + + handleCls: Ext.baseCSSPrefix + 'resizable-handle', + pinnedCls: Ext.baseCSSPrefix + 'resizable-pinned', + overCls: Ext.baseCSSPrefix + 'resizable-over', + proxyCls: Ext.baseCSSPrefix + 'resizable-proxy', + wrapCls: Ext.baseCSSPrefix + 'resizable-wrap', + + /** + * @cfg {Boolean} dynamic + *

Specify as true to update the {@link #target} (Element or {@link Ext.Component Component}) dynamically during dragging. + * This is true by default, but the {@link Ext.Component Component} class passes false when it + * is configured as {@link Ext.Component#resizable}.

+ *

If specified as false, a proxy element is displayed during the resize operation, and the {@link #target} + * is updated on mouseup.

+ */ + dynamic: true, + + /** + * @cfg {String} handles String consisting of the resize handles to display. Defaults to 's e se' for + * Elements and fixed position Components. Defaults to 8 point resizing for floating Components (such as Windows). + * Specify either 'all' or any of 'n s e w ne nw se sw'. + */ + handles: 's e se', + + /** + * @cfg {Number} height Optional. The height to set target to in pixels (defaults to null) + */ + height : null, + + /** + * @cfg {Number} width Optional. The width to set the target to in pixels (defaults to null) + */ + width : null, + + /** + * @cfg {Number} minHeight The minimum height for the element (defaults to 20) + */ + minHeight : 20, + + /** + * @cfg {Number} minWidth The minimum width for the element (defaults to 20) + */ + minWidth : 20, + + /** + * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000) + */ + maxHeight : 10000, + + /** + * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000) + */ + maxWidth : 10000, + + /** + * @cfg {Boolean} pinned True to ensure that the resize handles are always + * visible, false indicates resizing by cursor changes only (defaults to false) + */ + pinned: false, + + /** + * @cfg {Boolean} preserveRatio True to preserve the original ratio between height + * and width during resize (defaults to false) + */ + preserveRatio: false, + + /** + * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false) + */ + transparent: false, + + /** + * @cfg {Mixed} constrainTo Optional. An element, or a {@link Ext.util.Region} into which the resize operation + * must be constrained. + */ + + possiblePositions: { + n: 'north', + s: 'south', + e: 'east', + w: 'west', + se: 'southeast', + sw: 'southwest', + nw: 'northwest', + ne: 'northeast' + }, + + /** + * @cfg {Mixed} target The Element or Component to resize. + */ + + /** + * Outer element for resizing behavior. + * @type Ext.core.Element + * @property el + */ + + constructor: function(config) { + var me = this, + target, + tag, + handles = me.handles, + handleCls, + possibles, + len, + i = 0, + pos; + + this.addEvents( + /** + * @event beforeresize + * Fired before resize is allowed. Return false to cancel resize. + * @param {Ext.resizer.Resizer} this + * @param {Number} width The start width + * @param {Number} height The start height + * @param {Ext.EventObject} e The mousedown event + */ + 'beforeresize', + /** + * @event resizedrag + * Fires during resizing. Return false to cancel resize. + * @param {Ext.resizer.Resizer} this + * @param {Number} width The new width + * @param {Number} height The new height + * @param {Ext.EventObject} e The mousedown event + */ + 'resizedrag', + /** + * @event resize + * Fired after a resize. + * @param {Ext.resizer.Resizer} this + * @param {Number} width The new width + * @param {Number} height The new height + * @param {Ext.EventObject} e The mouseup event + */ + 'resize' + ); + + if (Ext.isString(config) || Ext.isElement(config) || config.dom) { + target = config; + config = arguments[1] || {}; + config.target = target; + } + // will apply config to this + me.mixins.observable.constructor.call(me, config); + + // If target is a Component, ensure that we pull the element out. + // Resizer must examine the underlying Element. + target = me.target; + if (target) { + if (target.isComponent) { + me.el = target.getEl(); + if (target.minWidth) { + me.minWidth = target.minWidth; + } + if (target.minHeight) { + me.minHeight = target.minHeight; + } + if (target.maxWidth) { + me.maxWidth = target.maxWidth; + } + if (target.maxHeight) { + me.maxHeight = target.maxHeight; + } + if (target.floating) { + if (!this.hasOwnProperty('handles')) { + this.handles = 'n ne e se s sw w nw'; + } + } + } else { + me.el = me.target = Ext.get(target); + } + } + // Backwards compatibility with Ext3.x's Resizable which used el as a config. + else { + me.target = me.el = Ext.get(me.el); + } + + // Tags like textarea and img cannot + // have children and therefore must + // be wrapped + tag = me.el.dom.tagName; + if (tag == 'TEXTAREA' || tag == 'IMG') { + /** + * Reference to the original resize target if the element of the original + * resize target was an IMG or a TEXTAREA which must be wrapped in a DIV. + * @type Mixed + * @property originalTarget + */ + me.originalTarget = me.target; + me.target = me.el = me.el.wrap({ + cls: me.wrapCls, + id: me.el.id + '-rzwrap' + }); + + // Transfer originalTarget's positioning/sizing + me.el.setPositioning(me.originalTarget.getPositioning()); + me.originalTarget.clearPositioning(); + var box = me.originalTarget.getBox(); + me.el.setBox(box); + } + + // Position the element, this enables us to absolute position + // the handles within this.el + me.el.position(); + if (me.pinned) { + me.el.addCls(me.pinnedCls); + } + + /** + * @type Ext.resizer.ResizeTracker + * @property resizeTracker + */ + me.resizeTracker = Ext.create('Ext.resizer.ResizeTracker', { + disabled: me.disabled, + target: me.target, + constrainTo: me.constrainTo, + overCls: me.overCls, + throttle: me.throttle, + originalTarget: me.originalTarget, + delegate: '.' + me.handleCls, + dynamic: me.dynamic, + preserveRatio: me.preserveRatio, + minHeight: me.minHeight, + maxHeight: me.maxHeight, + minWidth: me.minWidth, + maxWidth: me.maxWidth + }); + + // Relay the ResizeTracker's superclass events as our own resize events + me.resizeTracker.on('mousedown', me.onBeforeResize, me); + me.resizeTracker.on('drag', me.onResize, me); + me.resizeTracker.on('dragend', me.onResizeEnd, me); + + if (me.handles == 'all') { + me.handles = 'n s e w ne nw se sw'; + } + + handles = me.handles = me.handles.split(/ |\s*?[,;]\s*?/); + possibles = me.possiblePositions; + len = handles.length; + handleCls = me.handleCls + ' ' + (this.target.isComponent ? (me.target.baseCls + '-handle ') : '') + me.handleCls + '-'; + + for(; i < len; i++){ + // if specified and possible, create + if (handles[i] && possibles[handles[i]]) { + pos = possibles[handles[i]]; + // store a reference in this.east, this.west, etc + + me[pos] = Ext.create('Ext.Component', { + owner: this, + region: pos, + cls: handleCls + pos, + renderTo: me.el + }); + me[pos].el.unselectable(); + if (me.transparent) { + me[pos].el.setOpacity(0); + } + } + } + + // Constrain within configured maxima + if (Ext.isNumber(me.width)) { + me.width = Ext.Number.constrain(me.width, me.minWidth, me.maxWidth); + } + if (Ext.isNumber(me.height)) { + me.height = Ext.Number.constrain(me.height, me.minHeight, me.maxHeight); + } + + // Size the element + if (me.width != null || me.height != null) { + if (me.originalTarget) { + me.originalTarget.setWidth(me.width); + me.originalTarget.setHeight(me.height); + } + me.resizeTo(me.width, me.height); + } + + me.forceHandlesHeight(); + }, + + disable: function() { + this.resizeTracker.disable(); + }, + + enable: function() { + this.resizeTracker.enable(); + }, + + /** + * @private Relay the Tracker's mousedown event as beforeresize + * @param tracker The Resizer + * @param e The Event + */ + onBeforeResize: function(tracker, e) { + var b = this.target.getBox(); + return this.fireEvent('beforeresize', this, b.width, b.height, e); + }, + + /** + * @private Relay the Tracker's drag event as resizedrag + * @param tracker The Resizer + * @param e The Event + */ + onResize: function(tracker, e) { + var me = this, + b = me.target.getBox(); + me.forceHandlesHeight(); + return me.fireEvent('resizedrag', me, b.width, b.height, e); + }, + + /** + * @private Relay the Tracker's dragend event as resize + * @param tracker The Resizer + * @param e The Event + */ + onResizeEnd: function(tracker, e) { + var me = this, + b = me.target.getBox(); + me.forceHandlesHeight(); + return me.fireEvent('resize', me, b.width, b.height, e); + }, + + /** + * Perform a manual resize and fires the 'resize' event. + * @param {Number} width + * @param {Number} height + */ + resizeTo : function(width, height){ + this.target.setSize(width, height); + this.fireEvent('resize', this, width, height, null); + }, + + /** + *

Returns the element that was configured with the el or target config property. + * If a component was configured with the target property then this will return the + * element of this component.

+ *

Textarea and img elements will be wrapped with an additional div because + * these elements do not support child nodes. The original element can be accessed + * through the originalTarget property.

+ * @return {Element} element + */ + getEl : function() { + return this.el; + }, + + /** + *

Returns the element or component that was configured with the target config property.

+ *

Textarea and img elements will be wrapped with an additional div because + * these elements do not support child nodes. The original element can be accessed + * through the originalTarget property.

+ * @return {Element/Component} + */ + getTarget: function() { + return this.target; + }, + + destroy: function() { + var h; + for (var i = 0, l = this.handles.length; i < l; i++) { + h = this[this.possiblePositions[this.handles[i]]]; + delete h.owner; + Ext.destroy(h); + } + }, + + /** + * @private + * Fix IE6 handle height issue. + */ + forceHandlesHeight : function() { + var me = this, + handle; + if (Ext.isIE6) { + handle = me.east; + if (handle) { + handle.setHeight(me.el.getHeight()); + } + handle = me.west; + if (handle) { + handle.setHeight(me.el.getHeight()); + } + me.el.repaint(); + } + } +});