Make horizontal scrolling visible on diff page
Diff header scrolls with content and sticks to the top. Implemented custom gr-header wrapper to accomodate this. Bug: Issue 4491 Change-Id: I451ecd4f6c454fd9ab3085ad8f9c5bdd27cb9269
This commit is contained in:
@@ -21,6 +21,7 @@ limitations under the License.
|
|||||||
<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
|
<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
|
||||||
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||||
<link rel="import" href="../../shared/gr-select/gr-select.html">
|
<link rel="import" href="../../shared/gr-select/gr-select.html">
|
||||||
|
<link rel="import" href="../../shared/gr-header/gr-header.html">
|
||||||
<link rel="import" href="../gr-diff/gr-diff.html">
|
<link rel="import" href="../gr-diff/gr-diff.html">
|
||||||
<link rel="import" href="../gr-diff-cursor/gr-diff-cursor.html">
|
<link rel="import" href="../gr-diff-cursor/gr-diff-cursor.html">
|
||||||
<link rel="import" href="../gr-diff-preferences/gr-diff-preferences.html">
|
<link rel="import" href="../gr-diff-preferences/gr-diff-preferences.html">
|
||||||
@@ -31,17 +32,23 @@ limitations under the License.
|
|||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
background-color: var(--view-background-color);
|
background-color: var(--view-background-color);
|
||||||
display: block;
|
|
||||||
}
|
}
|
||||||
header,
|
gr-header {
|
||||||
.subHeader {
|
background-color: #fff;
|
||||||
|
border-bottom: 1px #eee solid;
|
||||||
|
}
|
||||||
|
.titleHeader,
|
||||||
|
.patchRangeHeader {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
header {
|
.titleHeader {
|
||||||
padding: .75em var(--default-horizontal-margin);
|
padding: .75em var(--default-horizontal-margin);
|
||||||
}
|
}
|
||||||
|
.patchRangeHeader {
|
||||||
|
margin: 0 var(--default-horizontal-margin) .75em;
|
||||||
|
}
|
||||||
.navLink:not([href]) {
|
.navLink:not([href]) {
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
@@ -106,9 +113,6 @@ limitations under the License.
|
|||||||
padding: 0 var(--default-horizontal-margin) 1em;
|
padding: 0 var(--default-horizontal-margin) 1em;
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
.subHeader {
|
|
||||||
margin: 0 var(--default-horizontal-margin) .75em;
|
|
||||||
}
|
|
||||||
.prefsButton {
|
.prefsButton {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
@@ -134,17 +138,20 @@ limitations under the License.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<header>
|
<gr-header keep-on-scroll>
|
||||||
|
<div class="titleHeader">
|
||||||
<h3>
|
<h3>
|
||||||
<a href$="[[_computeChangePath(_changeNum, _patchRange.*, _change.revisions)]]">
|
<a href$="[[_computeChangePath(_changeNum, _patchRange.*, _change.revisions)]]">
|
||||||
[[_changeNum]]</a><span>:</span>
|
[[_changeNum]]</a><span>:</span>
|
||||||
<span>[[_change.subject]]</span>
|
<span>[[_change.subject]]</span>
|
||||||
<span class="dash">—</span>
|
<span class="dash">—</span>
|
||||||
<input id="reviewed"
|
<input
|
||||||
|
id="reviewed"
|
||||||
class="reviewed"
|
class="reviewed"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
on-change="_handleReviewedChange"
|
on-change="_handleReviewedChange"
|
||||||
hidden$="[[!_loggedIn]]" hidden>
|
hidden$="[[!_loggedIn]]"
|
||||||
|
hidden>
|
||||||
<div class="jumpToFileContainer">
|
<div class="jumpToFileContainer">
|
||||||
<gr-button link class="dropdown-trigger" id="trigger" on-tap="_showDropdownTapHandler">
|
<gr-button link class="dropdown-trigger" id="trigger" on-tap="_showDropdownTapHandler">
|
||||||
<span>[[_computeFileDisplayName(_path)]]</span>
|
<span>[[_computeFileDisplayName(_path)]]</span>
|
||||||
@@ -184,10 +191,9 @@ limitations under the License.
|
|||||||
<a class="navLink"
|
<a class="navLink"
|
||||||
href$="[[_computeNavLinkURL(_path, _fileList, 1, 1)]]">Next</a>
|
href$="[[_computeNavLinkURL(_path, _fileList, 1, 1)]]">Next</a>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</div>
|
||||||
<div class="loading" hidden$="[[!_loading]]">Loading...</div>
|
<div class="loading" hidden$="[[!_loading]]">Loading...</div>
|
||||||
<div hidden$="[[_loading]]" hidden>
|
<div class="patchRangeHeader" hidden hidden$="[[_loading]]">
|
||||||
<div class="subHeader">
|
|
||||||
<gr-patch-range-select
|
<gr-patch-range-select
|
||||||
path="[[_path]]"
|
path="[[_path]]"
|
||||||
change-num="[[_changeNum]]"
|
change-num="[[_changeNum]]"
|
||||||
@@ -208,12 +214,12 @@ limitations under the License.
|
|||||||
<span hidden$="[[_computePrefsButtonHidden(_prefs, _loggedIn)]]">
|
<span hidden$="[[_computePrefsButtonHidden(_prefs, _loggedIn)]]">
|
||||||
<span
|
<span
|
||||||
hidden$="[[_computeModeSelectHidden(_isImageDiff)]]">/</span>
|
hidden$="[[_computeModeSelectHidden(_isImageDiff)]]">/</span>
|
||||||
<gr-button link
|
<gr-button
|
||||||
|
link
|
||||||
class="prefsButton"
|
class="prefsButton"
|
||||||
on-tap="_handlePrefsTap">Preferences</gr-button>
|
on-tap="_handlePrefsTap">Preferences</gr-button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<gr-overlay id="prefsOverlay" with-backdrop>
|
<gr-overlay id="prefsOverlay" with-backdrop>
|
||||||
<gr-diff-preferences
|
<gr-diff-preferences
|
||||||
id="diffPreferences"
|
id="diffPreferences"
|
||||||
@@ -222,8 +228,12 @@ limitations under the License.
|
|||||||
on-save="_handlePrefsSave"
|
on-save="_handlePrefsSave"
|
||||||
on-cancel="_handlePrefsCancel"></gr-diff-preferences>
|
on-cancel="_handlePrefsCancel"></gr-diff-preferences>
|
||||||
</gr-overlay>
|
</gr-overlay>
|
||||||
|
</div>
|
||||||
|
</gr-header>
|
||||||
<gr-diff
|
<gr-diff
|
||||||
id="diff"
|
id="diff"
|
||||||
|
hidden
|
||||||
|
hidden$="[[_loading]]"
|
||||||
project="[[_change.project]]"
|
project="[[_change.project]]"
|
||||||
commit="[[_change.current_revision]]"
|
commit="[[_change.current_revision]]"
|
||||||
is-image-diff="{{_isImageDiff}}"
|
is-image-diff="{{_isImageDiff}}"
|
||||||
@@ -236,7 +246,6 @@ limitations under the License.
|
|||||||
view-mode="[[_diffMode]]"
|
view-mode="[[_diffMode]]"
|
||||||
on-line-selected="_onLineSelected">
|
on-line-selected="_onLineSelected">
|
||||||
</gr-diff>
|
</gr-diff>
|
||||||
</div>
|
|
||||||
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
|
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
|
||||||
<gr-storage id="storage"></gr-storage>
|
<gr-storage id="storage"></gr-storage>
|
||||||
<gr-diff-cursor id="cursor"></gr-diff-cursor>
|
<gr-diff-cursor id="cursor"></gr-diff-cursor>
|
||||||
|
@@ -44,7 +44,6 @@ limitations under the License.
|
|||||||
border-top: 1px solid #eee;
|
border-top: 1px solid #eee;
|
||||||
display: flex;
|
display: flex;
|
||||||
font: 12px var(--monospace-font-family);
|
font: 12px var(--monospace-font-family);
|
||||||
overflow-x: auto;
|
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
}
|
}
|
||||||
table {
|
table {
|
||||||
|
@@ -31,6 +31,7 @@ limitations under the License.
|
|||||||
<link rel="import" href="./settings/gr-registration-dialog/gr-registration-dialog.html">
|
<link rel="import" href="./settings/gr-registration-dialog/gr-registration-dialog.html">
|
||||||
<link rel="import" href="./settings/gr-settings-view/gr-settings-view.html">
|
<link rel="import" href="./settings/gr-settings-view/gr-settings-view.html">
|
||||||
|
|
||||||
|
<link rel="import" href="./shared/gr-header/gr-header.html">
|
||||||
<link rel="import" href="./shared/gr-overlay/gr-overlay.html">
|
<link rel="import" href="./shared/gr-overlay/gr-overlay.html">
|
||||||
<link rel="import" href="./shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
<link rel="import" href="./shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||||
|
|
||||||
@@ -87,8 +88,10 @@ limitations under the License.
|
|||||||
color: #b71c1c;
|
color: #b71c1c;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<gr-header>
|
||||||
<gr-main-header id="mainHeader" search-query="{{params.query}}">
|
<gr-main-header id="mainHeader" search-query="{{params.query}}">
|
||||||
</gr-main-header>
|
</gr-main-header>
|
||||||
|
</gr-header>
|
||||||
<main>
|
<main>
|
||||||
<template is="dom-if" if="[[_showChangeListView]]" restamp="true">
|
<template is="dom-if" if="[[_showChangeListView]]" restamp="true">
|
||||||
<gr-change-list-view
|
<gr-change-list-view
|
||||||
|
46
polygerrit-ui/app/elements/shared/gr-header/gr-header.html
Normal file
46
polygerrit-ui/app/elements/shared/gr-header/gr-header.html
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<!--
|
||||||
|
Copyright (C) 2016 The Android Open Source Project
|
||||||
|
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
||||||
|
|
||||||
|
<dom-module id="gr-header">
|
||||||
|
<template>
|
||||||
|
<style>
|
||||||
|
:host {
|
||||||
|
height: var(--header-height);
|
||||||
|
}
|
||||||
|
header {
|
||||||
|
background: inherit;
|
||||||
|
border: inherit;
|
||||||
|
left: 0;
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
will-change: top;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.spacer {
|
||||||
|
height: var(--header-height);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<header id="header">
|
||||||
|
<div class="contentWrapper">
|
||||||
|
<content></content>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div class="spacer"></div>
|
||||||
|
</template>
|
||||||
|
<script src="gr-header.js"></script>
|
||||||
|
</dom-module>
|
79
polygerrit-ui/app/elements/shared/gr-header/gr-header.js
Normal file
79
polygerrit-ui/app/elements/shared/gr-header/gr-header.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
// Copyright (C) 2016 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
Polymer({
|
||||||
|
is: 'gr-header',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
keepOnScroll: {
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
|
_topInitial: Number,
|
||||||
|
_topLast: Number,
|
||||||
|
_headerHeight: Number,
|
||||||
|
},
|
||||||
|
|
||||||
|
attached: function() {
|
||||||
|
var rect = this.$.header.getBoundingClientRect();
|
||||||
|
this._topInitial = rect.top;
|
||||||
|
this._topLast = rect.top;
|
||||||
|
this._headerHeight = rect.height;
|
||||||
|
this.customStyle['--header-height'] = rect.height + 'px';
|
||||||
|
this.updateStyles();
|
||||||
|
this.listen(window, 'scroll', '_handleScroll');
|
||||||
|
},
|
||||||
|
|
||||||
|
detached: function() {
|
||||||
|
this.unlisten(window, 'scroll', '_handleScroll');
|
||||||
|
},
|
||||||
|
|
||||||
|
_getScrollY: function() {
|
||||||
|
return window.scrollY;
|
||||||
|
},
|
||||||
|
|
||||||
|
_handleScrollDebounced: function() {
|
||||||
|
var header = this.$.header;
|
||||||
|
var scrollY = this._topInitial - this._getScrollY();
|
||||||
|
var newTop;
|
||||||
|
if (this.keepOnScroll) {
|
||||||
|
if (scrollY > 0) {
|
||||||
|
// Reposition to imitate natural scrolling.
|
||||||
|
newTop = scrollY;
|
||||||
|
} else {
|
||||||
|
newTop = 0;
|
||||||
|
}
|
||||||
|
} else if (scrollY > -this._headerHeight ||
|
||||||
|
this._topLast < -this._headerHeight) {
|
||||||
|
// Allow to scroll away, but ignore when far behind the edge.
|
||||||
|
newTop = scrollY;
|
||||||
|
} else {
|
||||||
|
newTop = -this._headerHeight;
|
||||||
|
}
|
||||||
|
if (this._topLast !== newTop) {
|
||||||
|
if (newTop === undefined) {
|
||||||
|
header.style.top = '';
|
||||||
|
} else {
|
||||||
|
header.style.top = newTop + 'px';
|
||||||
|
}
|
||||||
|
this._topLast = newTop;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_handleScroll: function() {
|
||||||
|
this.debounce('scroll', this._handleScrollDebounced);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
@@ -0,0 +1,98 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2016 The Android Open Source Project
|
||||||
|
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
|
||||||
|
<title>gr-header</title>
|
||||||
|
|
||||||
|
<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
|
||||||
|
<script src="../../../bower_components/web-component-tester/browser.js"></script>
|
||||||
|
|
||||||
|
<link rel="import" href="gr-header.html">
|
||||||
|
|
||||||
|
<test-fixture id="basic">
|
||||||
|
<template>
|
||||||
|
<gr-header>
|
||||||
|
<div style="height: 100px"></div>
|
||||||
|
</gr-header>
|
||||||
|
</template>
|
||||||
|
</test-fixture>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
suite('gr-header', function() {
|
||||||
|
var element;
|
||||||
|
var sandbox;
|
||||||
|
|
||||||
|
setup(function() {
|
||||||
|
element = fixture('basic');
|
||||||
|
sandbox = sinon.sandbox.create();
|
||||||
|
});
|
||||||
|
|
||||||
|
teardown(function() {
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('header is the height of the content', function() {
|
||||||
|
assert.equal(element.getBoundingClientRect().height, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('scroll triggers _handleScroll', function() {
|
||||||
|
sandbox.stub(element, '_handleScroll');
|
||||||
|
window.dispatchEvent(new CustomEvent('scroll'));
|
||||||
|
assert.isTrue(element._handleScroll.called);
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('_handleScroll', function() {
|
||||||
|
|
||||||
|
var getHeaderTop = function() {
|
||||||
|
return element.$.header.style.top;
|
||||||
|
};
|
||||||
|
|
||||||
|
var emulateScrollY = function(distance) {
|
||||||
|
element._getScrollY.returns(distance);
|
||||||
|
element._handleScroll();
|
||||||
|
element.flushDebouncer('scroll');
|
||||||
|
};
|
||||||
|
|
||||||
|
setup(function() {
|
||||||
|
element._headerTopInitial = 10;
|
||||||
|
sandbox.stub(element, '_getScrollY').returns(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('is debounced', function() {
|
||||||
|
sandbox.stub(element, 'debounce');
|
||||||
|
element._handleScroll();
|
||||||
|
assert.isTrue(element.debounce.called);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('scrolls header along with document', function() {
|
||||||
|
emulateScrollY(20);
|
||||||
|
assert.equal(getHeaderTop(), '-10px');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('does not stick to the top by default', function() {
|
||||||
|
emulateScrollY(150);
|
||||||
|
assert.equal(getHeaderTop(), '-140px');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('sticks to the top if enabled', function() {
|
||||||
|
element.keepOnScroll = true;
|
||||||
|
emulateScrollY(120);
|
||||||
|
assert.equal(getHeaderTop(), '0px');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
Reference in New Issue
Block a user