outlookemailmicrosoft-teamsdiscordmessengercustom-servicesmacoslinuxwindowsinboxwhatsappicloudtweetdeckhipchattelegramhangoutsslackgmailskypefacebook-workplace
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.
516 lines
16 KiB
516 lines
16 KiB
/** |
|
* 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', { |
|
* target: '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', |
|
overCls: Ext.baseCSSPrefix + 'resizable-handle-over', |
|
pinnedCls: Ext.baseCSSPrefix + 'resizable-pinned', |
|
wrapCls: Ext.baseCSSPrefix + 'resizable-wrap', |
|
wrappedCls: Ext.baseCSSPrefix + 'resizable-wrapped', |
|
delimiterRe: /(?:\s*[,;]\s*)|\s+/, |
|
|
|
/** |
|
* @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 |
|
*/ |
|
height : null, |
|
|
|
/** |
|
* @cfg {Number} width |
|
* Optional. The width to set the target to in pixels |
|
*/ |
|
width : null, |
|
|
|
/** |
|
* @cfg {Number} heightIncrement |
|
* The increment to snap the height resize in pixels. |
|
*/ |
|
heightIncrement : 0, |
|
|
|
/** |
|
* @cfg {Number} widthIncrement |
|
* The increment to snap the width resize in pixels. |
|
*/ |
|
widthIncrement : 0, |
|
|
|
/** |
|
* @cfg {Number} minHeight |
|
* The minimum height for the element |
|
*/ |
|
minHeight : 20, |
|
|
|
/** |
|
* @cfg {Number} minWidth |
|
* The minimum width for the element |
|
*/ |
|
minWidth : 20, |
|
|
|
/** |
|
* @cfg {Number} maxHeight |
|
* The maximum height for the element |
|
*/ |
|
maxHeight : 10000, |
|
|
|
/** |
|
* @cfg {Number} maxWidth |
|
* The maximum width for the element |
|
*/ |
|
maxWidth : 10000, |
|
|
|
/** |
|
* @cfg {Boolean} pinned |
|
* True to ensure that the resize handles are always visible, false indicates resizing by cursor changes only |
|
*/ |
|
pinned: false, |
|
|
|
/** |
|
* @cfg {Boolean} preserveRatio |
|
* True to preserve the original ratio between height and width during resize |
|
*/ |
|
preserveRatio: false, |
|
|
|
/** |
|
* @cfg {Boolean} transparent |
|
* True for transparent handles. This is only applied at config time. |
|
*/ |
|
transparent: false, |
|
|
|
/** |
|
* @cfg {Ext.dom.Element/Ext.util.Region} constrainTo |
|
* An element, or a {@link Ext.util.Region 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 {Ext.dom.Element/Ext.Component} target |
|
* The Element or Component to resize. |
|
*/ |
|
|
|
/** |
|
* @property {Ext.dom.Element} el |
|
* Outer element for resizing behavior. |
|
*/ |
|
|
|
ariaRole: 'presentation', |
|
|
|
/** |
|
* @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.event.Event} e The mousedown event |
|
*/ |
|
|
|
/** |
|
* @event resizedrag |
|
* Fires during resizing. |
|
* @param {Ext.resizer.Resizer} this |
|
* @param {Number} width The new width |
|
* @param {Number} height The new height |
|
* @param {Ext.event.Event} e The mousedown event |
|
*/ |
|
|
|
/** |
|
* @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.event.Event} e The mouseup event |
|
*/ |
|
|
|
constructor: function(config) { |
|
var me = this, |
|
handles = me.handles, |
|
unselectableCls = Ext.dom.Element.unselectableCls, |
|
handleEls = [], |
|
resizeTarget, handleCls, possibles, tag, |
|
len, i, pos, el, box, |
|
wrapTarget, positioning, targetBaseCls; |
|
|
|
|
|
if (Ext.isString(config) || Ext.isElement(config) || config.dom) { |
|
resizeTarget = config; |
|
config = arguments[1] || {}; |
|
config.target = resizeTarget; |
|
} |
|
// 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. |
|
resizeTarget = me.target; |
|
if (resizeTarget) { |
|
if (resizeTarget.isComponent) { |
|
|
|
// Resizable Components get a new UI class on them which makes them overflow:visible |
|
// if the border width is non-zero and therefore the SASS has embedded the handles |
|
// in the borders using -ve position. |
|
resizeTarget.addClsWithUI('resizable'); |
|
|
|
if (resizeTarget.minWidth) { |
|
me.minWidth = resizeTarget.minWidth; |
|
} |
|
if (resizeTarget.minHeight) { |
|
me.minHeight = resizeTarget.minHeight; |
|
} |
|
if (resizeTarget.maxWidth) { |
|
me.maxWidth = resizeTarget.maxWidth; |
|
} |
|
if (resizeTarget.maxHeight) { |
|
me.maxHeight = resizeTarget.maxHeight; |
|
} |
|
if (resizeTarget.floating) { |
|
if (!me.hasOwnProperty('handles')) { |
|
me.handles = 'n ne e se s sw w nw'; |
|
} |
|
} |
|
me.el = resizeTarget.getEl(); |
|
} else { |
|
resizeTarget = me.el = me.target = Ext.get(resizeTarget); |
|
} |
|
} |
|
// Backwards compatibility with Ext3.x's Resizable which used el as a config. |
|
else { |
|
resizeTarget = me.target = me.el = Ext.get(me.el); |
|
} |
|
|
|
// Locally enforce border box model. |
|
// https://sencha.jira.com/browse/EXTJSIV-11511 |
|
me.el.addCls(Ext.Component.prototype.borderBoxCls); |
|
|
|
// 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 target. |
|
if (me.width !== null || me.height !== null) { |
|
me.target.setSize(me.width, me.height); |
|
} |
|
|
|
// Tags like textarea and img cannot |
|
// have children and therefore must |
|
// be wrapped |
|
tag = me.el.dom.tagName.toUpperCase(); |
|
if (tag === 'TEXTAREA' || tag === 'IMG' || tag === 'TABLE') { |
|
/** |
|
* @property {Ext.dom.Element/Ext.Component} originalTarget |
|
* Reference to the original resize target if the element of the original resize target was a |
|
* {@link Ext.form.field.Field Field}, or an IMG or a TEXTAREA which must be wrapped in a DIV. |
|
*/ |
|
me.originalTarget = me.target; |
|
|
|
wrapTarget = resizeTarget.isComponent ? resizeTarget.getEl() : resizeTarget; |
|
|
|
// Tag the wrapped element with a class so thaht we can force it to use border box sizing model |
|
me.el.addCls(me.wrappedCls); |
|
|
|
me.target = me.el = me.el.wrap({ |
|
role: 'presentation', |
|
cls: me.wrapCls, |
|
id: me.el.id + '-rzwrap', |
|
style: wrapTarget.getStyle(['margin-top', 'margin-bottom']) |
|
}); |
|
|
|
positioning = wrapTarget.getPositioning(); |
|
|
|
// Transfer originalTarget's positioning+sizing+margins |
|
me.el.setPositioning(positioning); |
|
|
|
wrapTarget.clearPositioning(); |
|
|
|
box = wrapTarget.getBox(); |
|
|
|
if (positioning.position !== 'absolute'){ |
|
//reset coordinates |
|
box.x = 0; |
|
box.y = 0; |
|
} |
|
|
|
me.el.setBox(box); |
|
|
|
// Position the wrapped element absolute so that it does not stretch the wrapper |
|
wrapTarget.setStyle('position', 'absolute'); |
|
|
|
me.isTargetWrapped = true; |
|
} |
|
|
|
// 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); |
|
} |
|
|
|
/** |
|
* @property {Ext.resizer.ResizeTracker} resizeTracker |
|
*/ |
|
me.resizeTracker = new Ext.resizer.ResizeTracker({ |
|
disabled: me.disabled, |
|
target: resizeTarget, |
|
el: me.el, |
|
constrainTo: me.constrainTo, |
|
handleCls: me.handleCls, |
|
overCls: me.overCls, |
|
throttle: me.throttle, |
|
|
|
// If we have wrapped something, instruct the ResizerTracker to use that wrapper as a proxy |
|
// and we should resize the wrapped target dynamically. |
|
proxy: me.originalTarget ? me.el : null, |
|
dynamic: me.originalTarget ? true : me.dynamic, |
|
|
|
originalTarget: me.originalTarget, |
|
delegate: '.' + me.handleCls, |
|
preserveRatio: me.preserveRatio, |
|
heightIncrement: me.heightIncrement, |
|
widthIncrement: me.widthIncrement, |
|
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, |
|
drag: me.onResize, |
|
dragend: me.onResizeEnd, |
|
scope: me |
|
}); |
|
|
|
if (me.handles === 'all') { |
|
me.handles = 'n s e w ne nw se sw'; |
|
} |
|
|
|
handles = me.handles = me.handles.split(me.delimiterRe); |
|
possibles = me.possiblePositions; |
|
len = handles.length; |
|
|
|
handleCls = me.handleCls + ' ' + me.handleCls + '-{0}'; |
|
if (me.target.isComponent) { |
|
targetBaseCls = me.target.baseCls; |
|
handleCls += ' ' + targetBaseCls + '-handle ' + targetBaseCls + '-handle-{0}'; |
|
if (Ext.supports.CSS3BorderRadius) { |
|
handleCls += ' ' + targetBaseCls + '-handle-{0}-br'; |
|
} |
|
} |
|
|
|
for (i = 0; i < len; i++){ |
|
// if specified and possible, create |
|
if (handles[i] && possibles[handles[i]]) { |
|
pos = possibles[handles[i]]; |
|
|
|
handleEls.push( |
|
'<div id="', me.el.id, '-', pos, '-handle" class="', Ext.String.format(handleCls, pos), ' ', unselectableCls, |
|
'" unselectable="on" role="presentation"', |
|
'></div>' |
|
); |
|
} |
|
} |
|
Ext.DomHelper.append(me.el, handleEls.join('')); |
|
|
|
// Let's reuse the handleEls stack to collect the actual els. |
|
handleEls.length = 0; |
|
|
|
// store a reference to each handle element in this.east, this.west, etc |
|
for (i = 0; i < len; i++){ |
|
// if specified and possible, create |
|
if (handles[i] && possibles[handles[i]]) { |
|
pos = possibles[handles[i]]; |
|
el = me[pos] = me.el.getById(me.el.id + '-' + pos + '-handle'); |
|
handleEls.push(el); |
|
el.region = pos; |
|
|
|
if (me.transparent) { |
|
el.setOpacity(0); |
|
} |
|
} |
|
} |
|
|
|
me.resizeTracker.handleEls = handleEls; |
|
}, |
|
|
|
disable: function() { |
|
this.resizeTracker.disable(); |
|
}, |
|
|
|
enable: function() { |
|
this.resizeTracker.enable(); |
|
}, |
|
|
|
/** |
|
* @private |
|
* Relay the Tracker's mousedown event as beforeresize |
|
* @param {Ext.resizer.ResizeTracker} tracker The tracker |
|
* @param {Ext.event.Event} event The event |
|
*/ |
|
onBeforeResize: function(tracker, e) { |
|
return this.fireResizeEvent('beforeresize', tracker, e); |
|
}, |
|
|
|
/** |
|
* @private |
|
* Relay the Tracker's drag event as resizedrag |
|
* @param {Ext.resizer.ResizeTracker} tracker The tracker |
|
* @param {Ext.event.Event} event The event |
|
*/ |
|
onResize: function(tracker, e) { |
|
return this.fireResizeEvent('resizedrag', tracker, e); |
|
}, |
|
|
|
/** |
|
* @private |
|
* Relay the Tracker's dragend event as resize |
|
* @param {Ext.resizer.ResizeTracker} tracker The tracker |
|
* @param {Ext.event.Event} event The event |
|
*/ |
|
onResizeEnd: function(tracker, e) { |
|
return this.fireResizeEvent('resize', tracker, e); |
|
}, |
|
|
|
/** |
|
* @private |
|
* Fire a resize event, checking if we have listeners before firing. |
|
* @param {String} name The name of the event |
|
* @param {Ext.resizer.ResizeTracker} tracker The tracker |
|
* @param {Ext.event.Event} event The event |
|
*/ |
|
fireResizeEvent: function(name, tracker, e) { |
|
var me = this, |
|
box; |
|
|
|
if (me.hasListeners[name]) { |
|
box = me.el.getBox(); |
|
return me.fireEvent(name, me, box.width, box.height, e); |
|
} |
|
}, |
|
|
|
/** |
|
* Perform a manual resize and fires the 'resize' event. |
|
* @param {Number} width |
|
* @param {Number} height |
|
*/ |
|
resizeTo : function(width, height) { |
|
var me = this; |
|
me.target.setSize(width, height); |
|
me.fireEvent('resize', me, 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 {Ext.dom.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 {Ext.dom.Element/Ext.Component} |
|
*/ |
|
getTarget: function() { |
|
return this.target; |
|
}, |
|
|
|
destroy: function() { |
|
var me = this, |
|
i, |
|
handles = me.handles, |
|
len = handles.length, |
|
positions = me.possiblePositions, |
|
handle; |
|
|
|
me.resizeTracker.destroy(); |
|
|
|
// The target is redefined as an element when it's wrapped so we must destroy it. |
|
if (me.isTargetWrapped) { |
|
me.target.destroy(); |
|
} |
|
|
|
for (i = 0; i < len; i++) { |
|
if ((handle = me[positions[handles[i]]])) { |
|
handle.destroy(); |
|
} |
|
} |
|
} |
|
});
|
|
|