/
githubmirror
/
brackets
Обзор
Документация
Войти
/
githubmirror
/
brackets
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/utils/Resizer.js
624 строки
25 KB
ficristo
Add a maxsize check for filetree resize and now it is possible to pass a percentage value
15 янв 2017, 20:46
15 янв 2017, 20:46
a82f1a4
Код
Авторство
О чём код?
/* * Copyright (c) 2012 - present Adobe Systems Incorporated. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ /** * Resizer is a Module utility to inject resizing capabilities to any element * inside Brackets. * * On initialization, Resizer discovers all nodes tagged as "vert-resizable" * and "horz-resizable" to add the resizer handler. Additionally, "top-resizer", * "bottom-resizer", "left-resizer" and "right-resizer" classes control the * position of the resizer on the element. * * An element can be made resizable at any time using the `makeResizable()` API. * Panel sizes are saved via preferences and restored when the DOM node becomes resizable * again in a subsequent launch. * * The resizable elements trigger a panelResizeStart, panelResizeUpdate and panelResizeEnd * event that can be used to create performance optimizations (such as hiding/showing elements * while resizing), custom layout logic, etc. See makeResizable() for details on the events. * * A resizable element can be collapsed/expanded using the `show`, `hide` and `toggle` APIs or * via user action. This triggers panelCollapsed/panelExpanded events - see makeResizable(). */ define(function (require, exports, module) { "use strict"; var DIRECTION_VERTICAL = "vert"; var DIRECTION_HORIZONTAL = "horz"; var POSITION_TOP = "top"; var POSITION_BOTTOM = "bottom"; var POSITION_LEFT = "left"; var POSITION_RIGHT = "right"; var PREFS_PURE_CODE = "noDistractions"; // Minimum size (height or width) for autodiscovered resizable panels var DEFAULT_MIN_SIZE = 100; // Load dependent modules var AppInit = require("utils/AppInit"), EventDispatcher = require("utils/EventDispatcher"), ViewUtils = require("utils/ViewUtils"), PreferencesManager = require("preferences/PreferencesManager"); var $mainView, $sideBar; var isResizing = false, isWindowResizing = false; /** * Shows a resizable element. * @param {DOMNode} element Html element to show if possible */ function show(element) { var showFunc = $(element).data("show"); if (showFunc) { showFunc.apply(element); } } /** * Hides a resizable element. * @param {DOMNode} element Html element to hide if possible */ function hide(element) { var hideFunc = $(element).data("hide"); if (hideFunc) { hideFunc.apply(element); } } /** * Changes the visibility state of a resizable element. The toggle * functionality is added when an element is made resizable. * @param {DOMNode} element Html element to toggle */ function toggle(element) { if ($(element).is(":visible")) { hide(element); } else { show(element); } } /** * Removes the resizability of an element if it's resizable * @param {DOMNode} element Html element in which to remove sizing */ function removeSizable(element) { var removeSizableFunc = $(element).data("removeSizable"); if (removeSizableFunc) { removeSizableFunc.apply(element); } } /** * Updates the sizing div by resyncing to the sizing edge of the element * Call this method after manually changing the size of the element * @param {DOMNode} element Html element whose sizer should be resynchronized */ function resyncSizer(element) { var resyncSizerFunc = $(element).data("resyncSizer"); if (resyncSizerFunc) { resyncSizerFunc.apply(element); } } /** * Returns the visibility state of a resizable element. * @param {DOMNode} element Html element to toggle * @return {boolean} true if element is visible, false if it is not visible */ function isVisible(element) { return $(element).is(":visible"); } function _isPercentage(value) { return !$.isNumeric(value) && value.indexOf('%') > -1; } function _percentageToPixels(value, total) { return parseFloat(value.replace('%', '')) * (total / 100); } function _sideBarMaxSize() { var siblingsWidth = 0; $sideBar.siblings().not(".content").each(function (i, elem) { var $elem = $(elem); if ($elem.css("display") !== "none") { siblingsWidth += $elem.outerWidth(); } }); return $(".main-view").width() - siblingsWidth - 1; } /** * Adds resizing and (optionally) expand/collapse capabilities to a given html element. The element's size * & visibility are automatically saved & restored as a view-state preference. * * Resizing can be configured in two directions: * - Vertical ("vert"): Resizes the height of the element * - Horizontal ("horz"): Resizes the width of the element * * Resizer handlers can be positioned on the element at: * - Top ("top") or bottom ("bottom") for vertical resizing * - Left ("left") or right ("right") for horizontal resizing * * A resizable element triggers the following events while resizing: * - panelResizeStart: When the resize starts. Passed the new size. * - panelResizeUpdate: When the resize gets updated. Passed the new size. * - panelResizeEnd: When the resize ends. Passed the final size. * - panelCollapsed: When the panel gets collapsed (or hidden). Passed the last size * before collapse. May occur without any resize events. * - panelExpanded: When the panel gets expanded (or shown). Passed the initial size. * May occur without any resize events. * * @param {!DOMNode} element DOM element which should be made resizable. Must have an id attribute, for * use as a preferences key. * @param {!string} direction Direction of the resize action: one of the DIRECTION_* constants. * @param {!string} position Which side of the element can be dragged: one of the POSITION_* constants * (TOP/BOTTOM for vertical resizing or LEFT/RIGHT for horizontal). * @param {?number} minSize Minimum size (width or height) of the element's outer dimensions, including * border & padding. Defaults to DEFAULT_MIN_SIZE. * @param {?boolean} collapsible Indicates the panel is collapsible on double click on the * resizer. Defaults to false. * @param {?string} forceLeft CSS selector indicating element whose 'left' should be locked to the * the resizable element's size (useful for siblings laid out to the right of * the element). Must lie in element's parent's subtree. * @param {?boolean} createdByWorkspaceManager For internal use only * @param {?boolean} usePercentages Maintain the size of the element as a percentage of its parent * the default is to maintain the size of the element in pixels * @param {?boolean} _attachToParent Attaches the resizer element to parent of the element rather than * to element itself. Attach the resizer to the parent *ONLY* if element has the * same offset as parent otherwise the resizer will be incorrectly positioned. * FOR INTERNAL USE ONLY */ function makeResizable(element, direction, position, minSize, collapsible, forceLeft, createdByWorkspaceManager, usePercentages, _attachToParent) { var $resizer = $('<div class="' + direction + '-resizer"></div>'), $element = $(element), $parent = $element.parent(), $resizableElement = $($element.find(".resizable-content:first")[0]), $body = $(window.document.body), elementID = $element.attr("id"), elementPrefs = PreferencesManager.getViewState(elementID) || {}, animationRequest = null, directionProperty = direction === DIRECTION_HORIZONTAL ? "clientX" : "clientY", directionIncrement = (position === POSITION_TOP || position === POSITION_LEFT) ? 1 : -1, parentSizeFunction = direction === DIRECTION_HORIZONTAL ? $parent.innerWidth : $parent.innerHeight, elementSizeFunction = function (newSize) { if (!newSize) { // calling the function as a getter if (direction === DIRECTION_HORIZONTAL) { return this.width(); } else { return this.height(); } } else if (!usePercentages) { if (direction === DIRECTION_HORIZONTAL) { return this.width(newSize); } else { return this.height(newSize); } } else { // calling the function as a setter var parentSize = parentSizeFunction.apply($parent), percentage, prop; if (direction === DIRECTION_HORIZONTAL) { prop = "width"; } else { prop = "height"; } percentage = newSize / parentSize; this.css(prop, (percentage * 100) + "%"); return this; // chainable } }, resizerCSSPosition = direction === DIRECTION_HORIZONTAL ? "left" : "top", contentSizeFunction = direction === DIRECTION_HORIZONTAL ? $resizableElement.width : $resizableElement.height; if (PreferencesManager.get(PREFS_PURE_CODE) && ($element.hasClass("bottom-panel") || $element.hasClass("sidebar"))) { elementPrefs.visible = false; } if (!elementID) { console.error("Resizable panels must have a DOM id to use as a preferences key:", element); return; } // Detect legacy cases where panels in the editor area are created without using WorkspaceManager APIs if ($parent[0] && $parent.is(".content") && !createdByWorkspaceManager) { console.error("Resizable panels within the editor area should be created via WorkspaceManager.createBottomPanel(). \nElement:", element); return; } if (minSize === undefined) { minSize = DEFAULT_MIN_SIZE; } collapsible = collapsible || false; if (_attachToParent) { $parent.prepend($resizer); } else { $element.prepend($resizer); } // Important so min/max sizes behave predictably $element.css("box-sizing", "border-box"); function adjustSibling(size) { if (forceLeft !== undefined) { $(forceLeft, $parent).css("left", size); } } function resizeElement(elementSize, contentSize) { elementSizeFunction.apply($element, [elementSize]); if ($resizableElement.length) { contentSizeFunction.apply($resizableElement, [contentSize]); } } // If the resizer is positioned right or bottom of the panel, we need to listen to // reposition it if the element size changes externally function repositionResizer(elementSize) { var resizerPosition = elementSize || 1; if (position === POSITION_RIGHT || position === POSITION_BOTTOM) { $resizer.css(resizerCSSPosition, resizerPosition); } } $element.data("removeSizable", function () { $resizer.off(".resizer"); $element.removeData("show"); $element.removeData("hide"); $element.removeData("resyncSizer"); $element.removeData("removeSizable"); $resizer.remove(); }); $element.data("resyncSizer", function () { repositionResizer(elementSizeFunction.apply($element)); }); $element.data("show", function () { var elementOffset = $element.offset(), elementSize = elementSizeFunction.apply($element) || elementPrefs.size, contentSize = contentSizeFunction.apply($resizableElement) || elementPrefs.contentSize; // Resize the element before showing it again. If the panel was collapsed by dragging // the resizer, the size of the element should be 0, so we restore size in preferences resizeElement(elementSize, contentSize); $element.show(); elementPrefs.visible = true; if (collapsible) { if (_attachToParent) { $parent.prepend($resizer); } else { $element.prepend($resizer); } if (position === POSITION_TOP) { $resizer.css(resizerCSSPosition, ""); } else if (position === POSITION_RIGHT) { $resizer.css(resizerCSSPosition, elementOffset[resizerCSSPosition] + elementSize); } } adjustSibling(elementSize); $element.trigger("panelExpanded", [elementSize]); PreferencesManager.setViewState(elementID, elementPrefs, null, isResizing); }); $element.data("hide", function () { var elementOffset = $element.offset(), elementSize = elementSizeFunction.apply($element), resizerSize = elementSizeFunction.apply($resizer); $element.hide(); elementPrefs.visible = false; if (collapsible) { $resizer.insertBefore($element); if (position === POSITION_RIGHT) { $resizer.css(resizerCSSPosition, ""); } else if (position === POSITION_TOP) { $resizer.css(resizerCSSPosition, elementOffset[resizerCSSPosition] + elementSize - resizerSize); } } adjustSibling(0); $element.trigger("panelCollapsed", [elementSize]); PreferencesManager.setViewState(elementID, elementPrefs, null, isResizing); }); $resizer.on("mousedown.resizer", function (e) { var $resizeShield = $("<div class='resizing-container " + direction + "-resizing' />"), startPosition = e[directionProperty], startSize = $element.is(":visible") ? elementSizeFunction.apply($element) : 0, newSize = startSize, previousSize = startSize, baseSize = 0, resizeStarted = false; isResizing = true; $body.append($resizeShield); if ($resizableElement.length) { $element.children().not(".horz-resizer, .vert-resizer, .resizable-content").each(function (index, child) { if (direction === DIRECTION_HORIZONTAL) { baseSize += $(child).outerWidth(); } else { baseSize += $(child).outerHeight(); } }); } function doRedraw() { // only run this if the mouse is down so we don't constantly loop even // after we're done resizing. if (!isResizing) { return; } // Check for real size changes to avoid unnecessary resizing and events if (newSize !== previousSize) { previousSize = newSize; if ($element.is(":visible")) { if (newSize < 10) { toggle($element); elementSizeFunction.apply($element, [0]); } else { // Trigger resizeStarted just before the first successful resize update if (!resizeStarted) { resizeStarted = true; $element.trigger("panelResizeStart", newSize); } // Resize the main element to the new size. If there is a content element, // its size is the new size minus the size of the non-resizable elements resizeElement(newSize, (newSize - baseSize)); adjustSibling(newSize); $element.trigger("panelResizeUpdate", [newSize]); } } else if (newSize > 10) { elementSizeFunction.apply($element, [newSize]); toggle($element); // Trigger resizeStarted after expanding the element if it was previously collapsed if (!resizeStarted) { resizeStarted = true; $element.trigger("panelResizeStart", newSize); } } } animationRequest = window.requestAnimationFrame(doRedraw); } function onMouseMove(e) { // calculate newSize adding to startSize the difference // between starting and current position, capped at minSize newSize = Math.max(startSize + directionIncrement * (startPosition - e[directionProperty]), minSize); // respect max size if one provided (e.g. by WorkspaceManager) var maxSize = $element.data("maxsize"); if (maxSize !== undefined) { // if provided as percentage size convert it to a pixel size if (_isPercentage(maxSize)) { maxSize = _percentageToPixels(maxSize, _sideBarMaxSize()); } newSize = Math.min(newSize, maxSize); } e.preventDefault(); if (animationRequest === null) { animationRequest = window.requestAnimationFrame(doRedraw); } } $(window.document).on("mousemove", onMouseMove); // If the element is marked as collapsible, check for double click // to toggle the element visibility if (collapsible) { $resizeShield.on("mousedown", function (e) { $(window.document).off("mousemove", onMouseMove); $resizeShield.off("mousedown"); $resizeShield.remove(); animationRequest = null; toggle($element); }); } function endResize(e) { if (isResizing) { var elementSize = elementSizeFunction.apply($element); if ($element.is(":visible")) { elementPrefs.size = elementSize; if ($resizableElement.length) { elementPrefs.contentSize = contentSizeFunction.apply($resizableElement); } PreferencesManager.setViewState(elementID, elementPrefs); repositionResizer(elementSize); } isResizing = false; if (resizeStarted) { $element.trigger("panelResizeEnd", [elementSize]); } // We wait 300ms to remove the resizer container to capture a mousedown // on the container that would account for double click window.setTimeout(function () { $(window.document).off("mousemove", onMouseMove); $resizeShield.off("mousedown"); $resizeShield.remove(); animationRequest = null; }, 300); } } $(window.document).one("mouseup", endResize); e.preventDefault(); }); // Panel preferences initialization if (elementPrefs) { if (elementPrefs.size !== undefined) { elementSizeFunction.apply($element, [elementPrefs.size]); } if (elementPrefs.contentSize !== undefined) { contentSizeFunction.apply($resizableElement, [elementPrefs.contentSize]); } if (elementPrefs.visible !== undefined && !elementPrefs.visible) { hide($element); } else { adjustSibling(elementSizeFunction.apply($element)); repositionResizer(elementSizeFunction.apply($element)); } } } function updateResizeLimits() { var sideBarMaxSize = _sideBarMaxSize(), maxSize = $sideBar.data("maxsize"), width = false; if (maxSize !== undefined && _isPercentage(maxSize)) { sideBarMaxSize = _percentageToPixels(maxSize, sideBarMaxSize); } if ($sideBar.width() > sideBarMaxSize) { // Adjust the sideBar's width in case it exceeds the window's width when resizing the window. $sideBar.width(sideBarMaxSize); resyncSizer($sideBar); $(".content").css("left", $sideBar.width()); $sideBar.trigger("panelResizeStart", $sideBar.width()); $sideBar.trigger("panelResizeUpdate", [$sideBar.width()]); $sideBar.trigger("panelResizeEnd", [$sideBar.width()]); } } function onWindowResize(e) { if ($sideBar.css("display") === "none") { return; } if (!isWindowResizing) { isWindowResizing = true; // We don't need any fancy debouncing here - we just need to react before the user can start // resizing any panels at the new window size. So just listen for first mousemove once the // window resize releases mouse capture. $(window.document).one("mousemove", function () { isWindowResizing = false; updateResizeLimits(); }); } } window.addEventListener("resize", onWindowResize, true); // Scan DOM for horz-resizable and vert-resizable classes and make them resizable AppInit.htmlReady(function () { var minSize = DEFAULT_MIN_SIZE; $mainView = $(".main-view"); $sideBar = $("#sidebar"); $(".vert-resizable").each(function (index, element) { if ($(element).data().minsize !== undefined) { minSize = $(element).data().minsize; } if ($(element).hasClass("top-resizer")) { makeResizable(element, DIRECTION_VERTICAL, POSITION_TOP, minSize, $(element).hasClass("collapsible")); } //if ($(element).hasClass("bottom-resizer")) { // makeResizable(element, DIRECTION_VERTICAL, POSITION_BOTTOM, DEFAULT_MIN_SIZE); //} }); $(".horz-resizable").each(function (index, element) { if ($(element).data().minsize !== undefined) { minSize = $(element).data().minsize; } //if ($(element).hasClass("left-resizer")) { // makeResizable(element, DIRECTION_HORIZONTAL, POSITION_LEFT, DEFAULT_MIN_SIZE); //} if ($(element).hasClass("right-resizer")) { makeResizable(element, DIRECTION_HORIZONTAL, POSITION_RIGHT, minSize, $(element).hasClass("collapsible"), $(element).data().forceleft); } }); // The main toolbar is only collapsible. if ($("#main-toolbar").hasClass("collapsible") && PreferencesManager.get(PREFS_PURE_CODE)) { ViewUtils.hideMainToolBar(); } }); EventDispatcher.makeEventDispatcher(exports); exports.makeResizable = makeResizable; exports.removeSizable = removeSizable; exports.resyncSizer = resyncSizer; exports.toggle = toggle; exports.show = show; exports.hide = hide; exports.isVisible = isVisible; //Resizer Constants exports.DIRECTION_VERTICAL = DIRECTION_VERTICAL; exports.DIRECTION_HORIZONTAL = DIRECTION_HORIZONTAL; exports.POSITION_TOP = POSITION_TOP; exports.POSITION_RIGHT = POSITION_RIGHT; exports.POSITION_BOTTOM = POSITION_BOTTOM; exports.POSITION_LEFT = POSITION_LEFT; });