223 lines
6.8 KiB
JavaScript
223 lines
6.8 KiB
JavaScript
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS-IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
/**
|
|
* @fileoverview Scroll behavior that can be added onto a container.
|
|
* @author gboyer@google.com (Garry Boyer)
|
|
*/
|
|
|
|
goog.provide('goog.ui.ContainerScroller');
|
|
|
|
goog.require('goog.Disposable');
|
|
goog.require('goog.Timer');
|
|
goog.require('goog.events.EventHandler');
|
|
goog.require('goog.style');
|
|
goog.require('goog.ui.Component');
|
|
goog.require('goog.ui.Container');
|
|
|
|
|
|
|
|
/**
|
|
* Plug-on scrolling behavior for a container.
|
|
*
|
|
* Use this to style containers, such as pop-up menus, to be scrolling, and
|
|
* automatically keep the highlighted element visible.
|
|
*
|
|
* To use this, first style your container with the desired overflow
|
|
* properties and height to achieve vertical scrolling. Also, the scrolling
|
|
* div should have no vertical padding, for two reasons: it is difficult to
|
|
* compensate for, and is generally not what you want due to the strange way
|
|
* CSS handles padding on the scrolling dimension.
|
|
*
|
|
* The container must already be rendered before this may be constructed.
|
|
*
|
|
* @param {!goog.ui.Container} container The container to attach behavior to.
|
|
* @constructor
|
|
* @extends {goog.Disposable}
|
|
*/
|
|
goog.ui.ContainerScroller = function(container) {
|
|
goog.Disposable.call(this);
|
|
|
|
/**
|
|
* The container that we are bestowing scroll behavior on.
|
|
* @type {!goog.ui.Container}
|
|
* @private
|
|
*/
|
|
this.container_ = container;
|
|
|
|
/**
|
|
* Event handler for this object.
|
|
* @type {!goog.events.EventHandler}
|
|
* @private
|
|
*/
|
|
this.eventHandler_ = new goog.events.EventHandler(this);
|
|
|
|
this.eventHandler_.listen(container, goog.ui.Component.EventType.HIGHLIGHT,
|
|
this.onHighlight_);
|
|
this.eventHandler_.listen(container, goog.ui.Component.EventType.ENTER,
|
|
this.onEnter_);
|
|
this.eventHandler_.listen(container, goog.ui.Container.EventType.AFTER_SHOW,
|
|
this.onAfterShow_);
|
|
this.eventHandler_.listen(container, goog.ui.Component.EventType.HIDE,
|
|
this.onHide_);
|
|
|
|
// TODO(gboyer): Allow a ContainerScroller to be attached with a Container
|
|
// before the container is rendered.
|
|
|
|
this.doScrolling_(true);
|
|
};
|
|
goog.inherits(goog.ui.ContainerScroller, goog.Disposable);
|
|
|
|
|
|
/**
|
|
* The last target the user hovered over.
|
|
*
|
|
* @see #onEnter_
|
|
* @type {goog.ui.Component}
|
|
* @private
|
|
*/
|
|
goog.ui.ContainerScroller.prototype.lastEnterTarget_ = null;
|
|
|
|
|
|
/**
|
|
* The scrollTop of the container before it was hidden.
|
|
* Used to restore the scroll position when the container is shown again.
|
|
* @type {?number}
|
|
* @private
|
|
*/
|
|
goog.ui.ContainerScroller.prototype.scrollTopBeforeHide_ = null;
|
|
|
|
|
|
/**
|
|
* Whether we are disabling the default handler for hovering.
|
|
*
|
|
* @see #onEnter_
|
|
* @see #temporarilyDisableHover_
|
|
* @type {boolean}
|
|
* @private
|
|
*/
|
|
goog.ui.ContainerScroller.prototype.disableHover_ = false;
|
|
|
|
|
|
/**
|
|
* Handles hover events on the container's children.
|
|
*
|
|
* Helps enforce two constraints: scrolling should not cause mouse highlights,
|
|
* and mouse highlights should not cause scrolling.
|
|
*
|
|
* @param {goog.events.Event} e The container's ENTER event.
|
|
* @private
|
|
*/
|
|
goog.ui.ContainerScroller.prototype.onEnter_ = function(e) {
|
|
if (this.disableHover_) {
|
|
// The container was scrolled recently. Since the mouse may be over the
|
|
// container, stop the default action of the ENTER event from causing
|
|
// highlights.
|
|
e.preventDefault();
|
|
} else {
|
|
// The mouse is moving and causing hover events. Stop the resulting
|
|
// highlight (if it happens) from causing a scroll.
|
|
this.lastEnterTarget_ = /** @type {goog.ui.Component} */ (e.target);
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Handles highlight events on the container's children.
|
|
* @param {goog.events.Event} e The container's highlight event.
|
|
* @private
|
|
*/
|
|
goog.ui.ContainerScroller.prototype.onHighlight_ = function(e) {
|
|
this.doScrolling_();
|
|
};
|
|
|
|
|
|
/**
|
|
* Handles AFTER_SHOW events on the container. Makes the container
|
|
* scroll to the previously scrolled position (if there was one),
|
|
* then adjust it to make the highlighted element be in view (if there is one).
|
|
* If there was no previous scroll position, then center the highlighted
|
|
* element (if there is one).
|
|
* @param {goog.events.Event} e The container's AFTER_SHOW event.
|
|
* @private
|
|
*/
|
|
goog.ui.ContainerScroller.prototype.onAfterShow_ = function(e) {
|
|
if (this.scrollTopBeforeHide_ != null) {
|
|
this.container_.getElement().scrollTop = this.scrollTopBeforeHide_;
|
|
// Make sure the highlighted item is still visible, in case the list
|
|
// or its hilighted item has changed.
|
|
this.doScrolling_(false);
|
|
} else {
|
|
this.doScrolling_(true);
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Handles hide events on the container. Clears out the last enter target,
|
|
* since it is no longer applicable, and remembers the scroll position of
|
|
* the menu so that it can be restored when the menu is reopened.
|
|
* @param {goog.events.Event} e The container's hide event.
|
|
* @private
|
|
*/
|
|
goog.ui.ContainerScroller.prototype.onHide_ = function(e) {
|
|
if (e.target == this.container_) {
|
|
this.lastEnterTarget_ = null;
|
|
this.scrollTopBeforeHide_ = this.container_.getElement().scrollTop;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Centers the currently highlighted item, if this is scrollable.
|
|
* @param {boolean=} opt_center Whether to center the highlighted element
|
|
* rather than simply ensure it is in view. Useful for the first
|
|
* render.
|
|
* @private
|
|
*/
|
|
goog.ui.ContainerScroller.prototype.doScrolling_ = function(opt_center) {
|
|
var highlighted = this.container_.getHighlighted();
|
|
|
|
// Only scroll if we're visible and there is a highlighted item.
|
|
if (this.container_.isVisible() && highlighted &&
|
|
highlighted != this.lastEnterTarget_) {
|
|
var element = this.container_.getElement();
|
|
goog.style.scrollIntoContainerView(highlighted.getElement(), element,
|
|
opt_center);
|
|
this.temporarilyDisableHover_();
|
|
this.lastEnterTarget_ = null;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Temporarily disables hover events from changing highlight.
|
|
* @see #onEnter_
|
|
* @private
|
|
*/
|
|
goog.ui.ContainerScroller.prototype.temporarilyDisableHover_ = function() {
|
|
this.disableHover_ = true;
|
|
goog.Timer.callOnce(function() {
|
|
this.disableHover_ = false;
|
|
}, 0, this);
|
|
};
|
|
|
|
|
|
/** @override */
|
|
goog.ui.ContainerScroller.prototype.disposeInternal = function() {
|
|
goog.ui.ContainerScroller.superClass_.disposeInternal.call(this);
|
|
this.eventHandler_.dispose();
|
|
this.lastEnterTarget_ = null;
|
|
};
|