/** * サイズ変更を可能にするために、ドラッグハンドルと要素またはコンポーネントに適用します。ドラッグハンドルは要素(またはコンポーネントの要素)に挿入され、絶対位置で配置されます。 * * テキストエリアとimgエレメントは子ノードをサポートしていないため、divタグ中に生成されます。元のエレメントへはoriginalTargetプロパティによりアクセスできます。 * * 有効なサイズ変更可能なハンドルのリストです。 * * 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} * * 通常のリサイザー作成を示す例: * * 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 * trueと設定すると、要素や{@link Ext.Component コンポーネント}などをドラッグしている間にその{@link #target}は、動的に更新されます。デフォルトは`true`ですが、{@link Ext.Component#resizable}が設定されている場合は、{@link Ext.Component Component}クラスは`false`を渡します。 * * `false`を指定している場合、プロキシエレメントがリサイズ操作中に表示され、{@link #target}は、mouseup時に更新されます。 */ dynamic: true, /** * @cfg {String} handles * 表示するリサイズハンドルで構成される文字列。要素および固定位置コンポーネントでデフォルトは's e se'です。フローティングコンポーネント(Windowsなど)ではデフォルトは8ポイントリサイズ。`'all'`または、`'n s e w ne nw se sw'`のいずれかを指定します。 */ handles: 's e se', /** * @cfg {Number} height * オプション。ターゲットの高さピクセル単位で設定します。 */ height : null, /** * @cfg {Number} width * オプション。ターゲットの幅をピクセル単位で設定します。 */ width : null, /** * @cfg {Number} heightIncrement * リサイズする高さのインクリメントをピクセル単位で指定します。 */ heightIncrement : 0, /** * @cfg {Number} widthIncrement * リサイズする幅のインクリメントをピクセル単位で指定します。 */ widthIncrement : 0, /** * @cfg {Number} minHeight * エレメントの最小高さを設定します。 */ minHeight : 20, /** * @cfg {Number} minWidth * エレメントの最小幅を設定します。 */ minWidth : 20, /** * @cfg {Number} maxHeight * エレメントの最大高さを設定します。 */ maxHeight : 10000, /** * @cfg {Number} maxWidth * エレメントの最大幅を設定します。 */ maxWidth : 10000, /** * @cfg {Boolean} pinned * trueに設定すると、リサイズハンドルが常に表示されます。 falseに設定した場合は、カーソルをエレメントの端に乗せたときにのみ表示されます。 */ pinned: false, /** * @cfg {Boolean} preserveRatio * trueに設定すると、リサイズの間に元の縦横比を維持したままリサイズします。 */ preserveRatio: false, /** * @cfg {Boolean} transparent * Trueに設定すると、ハンドルを透明にします。これはコンフィグ時のみ適用されます。 */ transparent: false, /** * @cfg {Ext.dom.Element/Ext.util.Region} constrainTo * 要素または、リサイズ操作の制約範囲の{@link Ext.util.Region 領域}。 */ possiblePositions: { n: 'north', s: 'south', e: 'east', w: 'west', se: 'southeast', sw: 'southwest', nw: 'northwest', ne: 'northeast' }, /** * @cfg {Ext.dom.Element/Ext.Component} target * リサイズするエレメントまたは、コンポーネントです。 */ /** * @property {Ext.dom.Element} el * リサイズの設定をする外部のエレメントを指定します。 */ ariaRole: 'presentation', /** * @event beforeresize * リサイズがされる前に発火します。リサイズがキャンセルされた場合、falseを返します。 * @param {Ext.resizer.Resizer} this * @param {Number} width 始めの幅 * @param {Number} height 始めの高さ * @param {Ext.event.Event} e mousedownイベント */ /** * @event resizedrag * リサイズの最中に発火します。 * @param {Ext.resizer.Resizer} this * @param {Number} width 新しい幅 * @param {Number} height 新しい高さ * @param {Ext.event.Event} e mousedownイベント */ /** * @event resize * リサイズの後に発火します。 * @param {Ext.resizer.Resizer} this * @param {Number} width 新しい幅 * @param {Number} height 新しい高さ * @param {Ext.event.Event} e mouseupイベント */ 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 * {@link Ext.form.field.Field Field}または、リサイズする元のターゲットの要素がDIV内に作成するべきIMG(イメージ)、TEXTAREA(テキストエリア)ならば、リサイズする元のターゲットへの参照を設定します。 */ 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 * トラッカーのmousedownイベントをbeforeresizeイベントとしてリレーします。 * @param {Ext.resizer.ResizeTracker} The トラッカー * @param {Ext.event.Event} The イベント。 */ onBeforeResize: function(tracker, e) { return this.fireResizeEvent('beforeresize', tracker, e); }, /** * @private * トラッカーのdragイベントをresizedragイベントとしてリレーします。 * @param {Ext.resizer.ResizeTracker} The トラッカー * @param {Ext.event.Event} The イベント。 */ onResize: function(tracker, e) { return this.fireResizeEvent('resizedrag', tracker, e); }, /** * @private * トラッカーのdragendイベントをresizeイベントとしてリレーします。 * @param {Ext.resizer.ResizeTracker} The トラッカー * @param {Ext.event.Event} The イベント。 */ onResizeEnd: function(tracker, e) { return this.fireResizeEvent('resize', tracker, e); }, /** * @private * リスナがあるかどうかを発火前に確認して、リサイズイベントを発火します。 * @param {String} name イベント名 * @param {Ext.resizer.ResizeTracker} The トラッカー * @param {Ext.event.Event} The イベント。 */ 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); } }, /** * リサイズを実行し、'resize'イベントを発火させます。 * @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); }, /** * elまたはtargetコンフィグプロパティで設定したエレメントを返します。targetプロパティを設定しているコンポーネントの場合は、そのコンポーネントのエレメントを返します。 * * テキストエリアとimgエレメントは子ノードをサポートしていないため、divタグ中に生成されます。元のエレメントへはoriginalTargetプロパティによりアクセスできます。 * @return {Ext.dom.Element} 要素 */ getEl : function() { return this.el; }, /** * targetコンフィグプロパティを設定したエレメントまたは、コンポーネントを返します。 * * テキストエリアとimgエレメントは子ノードをサポートしていないため、divタグ中に生成されます。元のエレメントへはoriginalTargetプロパティによりアクセスできます。 * @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(); } } } });