| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import {createElementWithClassName} from 'chrome://resources/ash/common/util.js'; |
| |
| /** |
| * Create by |LineChart.LineChart|. |
| * Create a dummy scrollbar to show the position of the line chart and to scroll |
| * the line chart, so we can draw the visible part of the line chart only |
| * instead of drawing the whole chart. |
| * @const |
| */ |
| export class Scrollbar { |
| constructor(/** function(): undefined */ callback) { |
| /** @const {function(): undefined} - Handle the scrolling event. */ |
| this.callback_ = callback; |
| |
| /** @type {number} - The range the scrollbar can scroll. */ |
| this.range_ = 0; |
| |
| /** @type {number} - The current position of the scrollbar. */ |
| this.position_ = 0; |
| |
| /** @type {number} - The real width of this scrollbar, in pixels. */ |
| this.width_ = 0; |
| |
| /** @type {Element} - The outer div to show the scrollbar. */ |
| this.outerDiv_ = |
| createElementWithClassName('div', 'horizontal-scrollbar-outer'); |
| this.outerDiv_.addEventListener('scroll', this.onScroll_.bind(this)); |
| |
| /** @type {Element} - The inner div to make outer div scrollable. */ |
| this.innerDiv_ = |
| createElementWithClassName('div', 'horizontal-scrollbar-inner'); |
| this.outerDiv_.appendChild(this.innerDiv_); |
| } |
| |
| /** |
| * Scrolling event handler. |
| */ |
| onScroll_() { |
| const /** number */ newPosition = this.outerDiv_.scrollLeft; |
| if (newPosition == this.position_) { |
| return; |
| } |
| this.position_ = newPosition; |
| this.callback_(); |
| } |
| |
| /** @return {Element} */ |
| getRootDiv() { |
| return this.outerDiv_; |
| } |
| |
| /** |
| * Return the height of scrollbar element. |
| * @return {number} |
| */ |
| getHeight() { |
| return this.outerDiv_.offsetHeight; |
| } |
| |
| /** @return {number} */ |
| getRange() { |
| return this.range_; |
| } |
| |
| /** |
| * Position may be float point number because |document.scrollLeft| may be |
| * float point number. |
| * @return {number} |
| */ |
| getPosition() { |
| return Math.round(this.position_); |
| } |
| |
| /** |
| * Change the size of the outer div and update the scrollbar position. |
| * @param {number} width |
| */ |
| resize(width) { |
| if (this.width_ == width) { |
| return; |
| } |
| this.width_ = width; |
| this.updateOuterDivWidth_(); |
| } |
| |
| updateOuterDivWidth_() { |
| this.constructor.setNodeWidth(this.outerDiv_, this.width_); |
| } |
| |
| /** |
| * Set the scrollable range to |range|. Use the inner div's width to control |
| * the scrollable range. If position go out of range after range update, set |
| * it to the boundary value. |
| * @param {number} range |
| */ |
| setRange(range) { |
| this.range_ = range; |
| this.updateInnerDivWidth_(); |
| if (range < this.position_) { |
| this.position_ = range; |
| this.updateScrollbarPosition_(); |
| } |
| } |
| |
| updateInnerDivWidth_() { |
| const width = this.outerDiv_.clientWidth; |
| this.constructor.setNodeWidth(this.innerDiv_, width + this.range_); |
| } |
| |
| /** |
| * @param {Element} node |
| * @param {number} width |
| */ |
| static setNodeWidth(node, width) { |
| node.style.width = width + 'px'; |
| } |
| |
| /** |
| * Set the scrollbar position to |position|. If the new position go out of |
| * range, set it to the boundary value. |
| * @param {number} position |
| */ |
| setPosition(position) { |
| const /** number */ newPosition = |
| Math.max(0, Math.min(position, this.range_)); |
| this.position_ = newPosition; |
| this.updateScrollbarPosition_(); |
| } |
| |
| /** |
| * Update the scrollbar position via Javascript scrollbar api. Position may |
| * not be the same value as what we assigned even if the value is in the |
| * range. See crbug.com/760425. |
| */ |
| updateScrollbarPosition_() { |
| if (this.outerDiv_.scrollLeft == this.position_) { |
| return; |
| } |
| this.outerDiv_.scrollLeft = this.position_; |
| } |
| |
| /** |
| * Return true if scrollbar is at the right edge of the chart. |
| * @return {boolean} |
| */ |
| isScrolledToRightEdge() { |
| /* |scrollLeft| may become a float point number even if we set it to some |
| * integer value. If the distance to the right edge less than 2 pixels, we |
| * consider that it is scrolled to the right edge. |
| */ |
| const scrollLeftErrorAmount = 2; |
| return this.position_ + scrollLeftErrorAmount > this.range_; |
| } |
| |
| /** |
| * Scroll the scrollbar to the right edge. |
| */ |
| scrollToRightEdge() { |
| this.setPosition(this.range_); |
| } |
| } |