Revert "Replace gr-fixed-panel with position:sticky"
This reverts commit 368641eb4f.
Reason for revert: This introduced a bug which causes horizontal
scrolling on pages with a vertical scroll bar, due to 100vw hot taking
into account the width of the scroll bar. This is not an easy fix, so
reverting this change, at least for now.
Change-Id: Iff02ec60e02c85593dafdb3f9c5f99cf6cfac2ab
This commit is contained in:
@@ -110,11 +110,6 @@ limitations under the License.
|
||||
.changeStatus {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
.container {
|
||||
/* Needed to restrict the change view from allowing horizontal
|
||||
scrolling */
|
||||
width: 100vw;
|
||||
}
|
||||
/* Strong specificity here is needed due to
|
||||
https://github.com/Polymer/polymer/issues/2531 */
|
||||
.container section.changeInfo {
|
||||
|
||||
@@ -25,6 +25,7 @@ limitations under the License.
|
||||
<link rel="import" href="../../shared/gr-button/gr-button.html">
|
||||
<link rel="import" href="../../shared/gr-count-string-formatter/gr-count-string-formatter.html">
|
||||
<link rel="import" href="../../shared/gr-dropdown-list/gr-dropdown-list.html">
|
||||
<link rel="import" href="../../shared/gr-fixed-panel/gr-fixed-panel.html">
|
||||
<link rel="import" href="../../shared/gr-icons/gr-icons.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">
|
||||
@@ -54,6 +55,11 @@ limitations under the License.
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
}
|
||||
gr-fixed-panel {
|
||||
background-color: #fff;
|
||||
border-bottom: 1px #eee solid;
|
||||
z-index: 1;
|
||||
}
|
||||
header,
|
||||
.subHeader {
|
||||
align-items: center;
|
||||
@@ -141,15 +147,6 @@ limitations under the License.
|
||||
.separator.hide {
|
||||
display: none;
|
||||
}
|
||||
.sticky {
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #eee;
|
||||
left: 0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
width: 100vw;
|
||||
z-index: 1;
|
||||
}
|
||||
gr-dropdown-list {
|
||||
--trigger-style: {
|
||||
text-transform: none;
|
||||
@@ -208,136 +205,138 @@ limitations under the License.
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div>
|
||||
<div class="sticky">
|
||||
<header class$="[[_computeContainerClass(_editMode)]]">
|
||||
<h3>
|
||||
<a href$="[[_computeChangePath(_change, _patchRange.*, _change.revisions)]]">
|
||||
[[_changeNum]]</a><span>:</span>
|
||||
<span>[[_change.subject]]</span>
|
||||
<span class="dash">—</span>
|
||||
<input id="reviewed"
|
||||
class="reviewed hideOnEdit"
|
||||
type="checkbox"
|
||||
on-change="_handleReviewedChange"
|
||||
hidden$="[[!_loggedIn]]" hidden>
|
||||
<div class="jumpToFileContainer">
|
||||
<gr-dropdown-list
|
||||
id="dropdown"
|
||||
value="[[_path]]"
|
||||
on-value-change="_handleFileChange"
|
||||
items="[[_formattedFiles]]"
|
||||
initial-count="75">
|
||||
</gr-dropdown-list>
|
||||
</div>
|
||||
</h3>
|
||||
<div class="navLinks desktop">
|
||||
<span class$="fileNum [[_computeFileNumClass(_fileNum, _formattedFiles)]]">
|
||||
File [[_fileNum]] of [[_formattedFiles.length]]
|
||||
<span class="separator"></span>
|
||||
</span>
|
||||
<a class="navLink"
|
||||
href$="[[_computeNavLinkURL(_change, _path, _fileList, -1, 1)]]">
|
||||
Prev</a>
|
||||
<span class="separator"></span>
|
||||
<a class="navLink"
|
||||
href$="[[_computeChangePath(_change, _patchRange.*, _change.revisions)]]">
|
||||
Up</a>
|
||||
<span class="separator"></span>
|
||||
<a class="navLink"
|
||||
href$="[[_computeNavLinkURL(_change, _path, _fileList, 1, 1)]]">
|
||||
Next</a>
|
||||
<gr-fixed-panel
|
||||
class$="[[_computeContainerClass(_editMode)]]"
|
||||
floating-disabled="[[_panelFloatingDisabled]]"
|
||||
keep-on-scroll
|
||||
ready-for-measure="[[!_loading]]">
|
||||
<header>
|
||||
<h3>
|
||||
<a href$="[[_computeChangePath(_change, _patchRange.*, _change.revisions)]]">
|
||||
[[_changeNum]]</a><span>:</span>
|
||||
<span>[[_change.subject]]</span>
|
||||
<span class="dash">—</span>
|
||||
<input id="reviewed"
|
||||
class="reviewed hideOnEdit"
|
||||
type="checkbox"
|
||||
on-change="_handleReviewedChange"
|
||||
hidden$="[[!_loggedIn]]" hidden>
|
||||
<div class="jumpToFileContainer">
|
||||
<gr-dropdown-list
|
||||
id="dropdown"
|
||||
value="[[_path]]"
|
||||
on-value-change="_handleFileChange"
|
||||
items="[[_formattedFiles]]"
|
||||
initial-count="75">
|
||||
</gr-dropdown-list>
|
||||
</div>
|
||||
</header>
|
||||
<div class="subHeader">
|
||||
<div class="patchRangeLeft">
|
||||
<gr-patch-range-select
|
||||
id="rangeSelect"
|
||||
change-num="[[_changeNum]]"
|
||||
change-comments="[[_changeComments]]"
|
||||
patch-num="[[_patchRange.patchNum]]"
|
||||
base-patch-num="[[_patchRange.basePatchNum]]"
|
||||
files-weblinks="[[_filesWeblinks]]"
|
||||
available-patches="[[_allPatchSets]]"
|
||||
revisions="[[_change.revisions]]"
|
||||
revision-info="[[_revisionInfo]]"
|
||||
on-patch-range-change="_handlePatchChange">
|
||||
</gr-patch-range-select>
|
||||
<span class="download desktop">
|
||||
<span class="separator"></span>
|
||||
<a
|
||||
class="downloadLink"
|
||||
download
|
||||
href$="[[_computeDownloadLink(_changeNum, _patchRange, _path)]]">
|
||||
Download
|
||||
</a>
|
||||
</span>
|
||||
</h3>
|
||||
<div class="navLinks desktop">
|
||||
<span class$="fileNum [[_computeFileNumClass(_fileNum, _formattedFiles)]]">
|
||||
File [[_fileNum]] of [[_formattedFiles.length]]
|
||||
<span class="separator"></span>
|
||||
</span>
|
||||
<a class="navLink"
|
||||
href$="[[_computeNavLinkURL(_change, _path, _fileList, -1, 1)]]">
|
||||
Prev</a>
|
||||
<span class="separator"></span>
|
||||
<a class="navLink"
|
||||
href$="[[_computeChangePath(_change, _patchRange.*, _change.revisions)]]">
|
||||
Up</a>
|
||||
<span class="separator"></span>
|
||||
<a class="navLink"
|
||||
href$="[[_computeNavLinkURL(_change, _path, _fileList, 1, 1)]]">
|
||||
Next</a>
|
||||
</div>
|
||||
</header>
|
||||
<div class="subHeader">
|
||||
<div class="patchRangeLeft">
|
||||
<gr-patch-range-select
|
||||
id="rangeSelect"
|
||||
change-num="[[_changeNum]]"
|
||||
change-comments="[[_changeComments]]"
|
||||
patch-num="[[_patchRange.patchNum]]"
|
||||
base-patch-num="[[_patchRange.basePatchNum]]"
|
||||
files-weblinks="[[_filesWeblinks]]"
|
||||
available-patches="[[_allPatchSets]]"
|
||||
revisions="[[_change.revisions]]"
|
||||
revision-info="[[_revisionInfo]]"
|
||||
on-patch-range-change="_handlePatchChange">
|
||||
</gr-patch-range-select>
|
||||
<span class="download desktop">
|
||||
<span class="separator"></span>
|
||||
<a
|
||||
class="downloadLink"
|
||||
download
|
||||
href$="[[_computeDownloadLink(_changeNum, _patchRange, _path)]]">
|
||||
Download
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="rightControls">
|
||||
<span class$="blameLoader [[_computeBlameLoaderClass(_isImageDiff, _isBlameSupported)]]">
|
||||
<gr-button
|
||||
link
|
||||
disabled="[[_isBlameLoading]]"
|
||||
on-tap="_toggleBlame">[[_computeBlameToggleLabel(_isBlameLoaded, _isBlameLoading)]]</gr-button>
|
||||
<span class="separator"></span>
|
||||
</span>
|
||||
<div class$="diffModeSelector [[_computeModeSelectHideClass(_isImageDiff)]]">
|
||||
<span>Diff view:</span>
|
||||
<gr-diff-mode-selector
|
||||
id="modeSelect"
|
||||
save-on-change="[[_loggedIn]]"
|
||||
mode="{{changeViewState.diffMode}}"></gr-diff-mode-selector>
|
||||
</div>
|
||||
<div class="rightControls">
|
||||
<span class$="blameLoader [[_computeBlameLoaderClass(_isImageDiff, _isBlameSupported)]]">
|
||||
<span id="diffPrefsContainer"
|
||||
hidden$="[[_computePrefsButtonHidden(_prefs, _loggedIn)]]" hidden>
|
||||
<span class="preferences desktop">
|
||||
<gr-button
|
||||
link
|
||||
disabled="[[_isBlameLoading]]"
|
||||
on-tap="_toggleBlame">[[_computeBlameToggleLabel(_isBlameLoaded, _isBlameLoading)]]</gr-button>
|
||||
<span class="separator"></span>
|
||||
class="prefsButton"
|
||||
has-tooltip
|
||||
title="Diff preferences"
|
||||
on-tap="_handlePrefsTap"><iron-icon icon="gr-icons:settings"></iron-icon></gr-button>
|
||||
</span>
|
||||
<div class$="diffModeSelector [[_computeModeSelectHideClass(_isImageDiff)]]">
|
||||
<span>Diff view:</span>
|
||||
<gr-diff-mode-selector
|
||||
id="modeSelect"
|
||||
save-on-change="[[_loggedIn]]"
|
||||
mode="{{changeViewState.diffMode}}"></gr-diff-mode-selector>
|
||||
</div>
|
||||
<span id="diffPrefsContainer"
|
||||
hidden$="[[_computePrefsButtonHidden(_prefs, _loggedIn)]]" hidden>
|
||||
<span class="preferences desktop">
|
||||
<gr-button
|
||||
link
|
||||
class="prefsButton"
|
||||
has-tooltip
|
||||
title="Diff preferences"
|
||||
on-tap="_handlePrefsTap"><iron-icon icon="gr-icons:settings"></iron-icon></gr-button>
|
||||
</span>
|
||||
</span>
|
||||
<gr-endpoint-decorator name="annotation-toggler">
|
||||
<span hidden id="annotation-span">
|
||||
<label for="annotation-checkbox" id="annotation-label"></label>
|
||||
<input is="iron-input" type="checkbox" id="annotation-checkbox" disabled>
|
||||
</span>
|
||||
<gr-endpoint-decorator name="annotation-toggler">
|
||||
<span hidden id="annotation-span">
|
||||
<label for="annotation-checkbox" id="annotation-label"></label>
|
||||
<input is="iron-input" type="checkbox" id="annotation-checkbox" disabled>
|
||||
</span>
|
||||
</gr-endpoint-decorator>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fileNav mobile">
|
||||
<a class="mobileNavLink"
|
||||
href$="[[_computeNavLinkURL(_change, _path, _fileList, -1, 1)]]">
|
||||
<</a>
|
||||
<div class="fullFileName mobile">[[computeDisplayPath(_path)]]
|
||||
</div>
|
||||
<a class="mobileNavLink"
|
||||
href$="[[_computeNavLinkURL(_change, _path, _fileList, 1, 1)]]">
|
||||
></a>
|
||||
</gr-endpoint-decorator>
|
||||
</div>
|
||||
</div>
|
||||
<div class="loading" hidden$="[[!_loading]]">Loading...</div>
|
||||
<gr-diff
|
||||
id="diff"
|
||||
hidden
|
||||
hidden$="[[_loading]]"
|
||||
class$="[[_computeDiffClass(_panelFloatingDisabled)]]"
|
||||
is-image-diff="{{_isImageDiff}}"
|
||||
files-weblinks="{{_filesWeblinks}}"
|
||||
change-num="[[_changeNum]]"
|
||||
commit-range="[[_commitRange]]"
|
||||
patch-range="[[_patchRange]]"
|
||||
path="[[_path]]"
|
||||
prefs="[[_prefs]]"
|
||||
project-config="[[_projectConfig]]"
|
||||
project-name="[[_change.project]]"
|
||||
view-mode="[[_diffMode]]"
|
||||
is-blame-loaded="{{_isBlameLoaded}}"
|
||||
on-line-selected="_onLineSelected">
|
||||
</gr-diff>
|
||||
</div>
|
||||
<div class="fileNav mobile">
|
||||
<a class="mobileNavLink"
|
||||
href$="[[_computeNavLinkURL(_change, _path, _fileList, -1, 1)]]">
|
||||
<</a>
|
||||
<div class="fullFileName mobile">[[computeDisplayPath(_path)]]
|
||||
</div>
|
||||
<a class="mobileNavLink"
|
||||
href$="[[_computeNavLinkURL(_change, _path, _fileList, 1, 1)]]">
|
||||
></a>
|
||||
</div>
|
||||
</gr-fixed-panel>
|
||||
<div class="loading" hidden$="[[!_loading]]">Loading...</div>
|
||||
<gr-diff
|
||||
id="diff"
|
||||
hidden
|
||||
hidden$="[[_loading]]"
|
||||
class$="[[_computeDiffClass(_panelFloatingDisabled)]]"
|
||||
is-image-diff="{{_isImageDiff}}"
|
||||
files-weblinks="{{_filesWeblinks}}"
|
||||
change-num="[[_changeNum]]"
|
||||
commit-range="[[_commitRange]]"
|
||||
patch-range="[[_patchRange]]"
|
||||
path="[[_path]]"
|
||||
prefs="[[_prefs]]"
|
||||
project-config="[[_projectConfig]]"
|
||||
project-name="[[_change.project]]"
|
||||
view-mode="[[_diffMode]]"
|
||||
is-blame-loaded="{{_isBlameLoaded}}"
|
||||
on-line-selected="_onLineSelected">
|
||||
</gr-diff>
|
||||
<gr-diff-preferences
|
||||
id="diffPreferences"
|
||||
prefs="{{_prefs}}"
|
||||
|
||||
@@ -48,7 +48,6 @@ limitations under the License.
|
||||
display: none;
|
||||
}
|
||||
.diffContainer {
|
||||
border-bottom: 1px solid #eee;
|
||||
display: flex;
|
||||
font: var(--font-size-small) var(--monospace-font-family);
|
||||
@apply --diff-container-styles;
|
||||
|
||||
@@ -24,6 +24,7 @@ limitations under the License.
|
||||
<link rel="import" href="../../plugins/gr-endpoint-param/gr-endpoint-param.html">
|
||||
<link rel="import" href="../../shared/gr-button/gr-button.html">
|
||||
<link rel="import" href="../../shared/gr-editable-label/gr-editable-label.html">
|
||||
<link rel="import" href="../../shared/gr-fixed-panel/gr-fixed-panel.html">
|
||||
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||
<link rel="import" href="../gr-default-editor/gr-default-editor.html">
|
||||
<link rel="import" href="../../../styles/shared-styles.html">
|
||||
@@ -34,13 +35,9 @@ limitations under the License.
|
||||
:host {
|
||||
background-color: var(--view-background-color);
|
||||
}
|
||||
.sticky {
|
||||
gr-fixed-panel {
|
||||
background-color: #ebf5fb;
|
||||
border-bottom: 1px #ddd solid;
|
||||
position: -webkit-sticky;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
width: 100vw;
|
||||
z-index: 1;
|
||||
}
|
||||
header,
|
||||
@@ -88,7 +85,7 @@ limitations under the License.
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div class="sticky">
|
||||
<gr-fixed-panel keep-on-scroll>
|
||||
<header>
|
||||
<span class="controlGroup">
|
||||
<span>Edit mode</span>
|
||||
@@ -112,7 +109,7 @@ limitations under the License.
|
||||
on-tap="_saveEdit">Save</gr-button>
|
||||
</span>
|
||||
</header>
|
||||
</div>
|
||||
</gr-fixed-panel>
|
||||
<div class="textareaWrapper">
|
||||
<gr-endpoint-decorator id="editorEndpoint" name="editor">
|
||||
<gr-endpoint-param name="fileContent" value="[[_newContent]]"></gr-endpoint-param>
|
||||
|
||||
@@ -54,6 +54,7 @@ limitations under the License.
|
||||
<link rel="import" href="./settings/gr-cla-view/gr-cla-view.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="./shared/gr-fixed-panel/gr-fixed-panel.html">
|
||||
<link rel="import" href="./shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||
|
||||
<script src="../scripts/util.js"></script>
|
||||
@@ -62,45 +63,35 @@ limitations under the License.
|
||||
<template>
|
||||
<style include="shared-styles">
|
||||
:host {
|
||||
/* This needs to be inline-flex so that the app container inherits the
|
||||
width of its children. From here, can use position: sticky to display
|
||||
items that we want to be 'floating' like the header and footer, with
|
||||
a combination of: position: sticky, width=100vw, and left: 0. */
|
||||
display: inline-flex;
|
||||
display: flex;
|
||||
min-height: 100%;
|
||||
flex-direction: column;
|
||||
}
|
||||
gr-fixed-panel {
|
||||
/**
|
||||
* This one should be greater that the z-index in gr-diff-view
|
||||
* because gr-main-header contains overlay.
|
||||
*/
|
||||
z-index: 10;
|
||||
}
|
||||
gr-main-header,
|
||||
footer {
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
gr-main-header {
|
||||
background-color: var(--header-background-color);
|
||||
padding: 0 var(--default-horizontal-margin);
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
gr-main-header.shadow {
|
||||
/* Make it obvious for shadow dom testing */
|
||||
border-bottom: 1px solid pink;
|
||||
}
|
||||
gr-main-header {
|
||||
background-color: var(--header-background-color);
|
||||
border-bottom: 1px solid #ddd;
|
||||
/* The combination of left, width, position allow this to be
|
||||
floating. */
|
||||
left: 0;
|
||||
padding: 0 var(--default-horizontal-margin);
|
||||
position: sticky;
|
||||
width: 100vw;
|
||||
/* Needed for the menu dropdowns to display on top of the sticky
|
||||
headers with z-index of 1.*/
|
||||
z-index: 2;
|
||||
}
|
||||
footer {
|
||||
/* The combination of left, width, position allow this to be
|
||||
floating. */
|
||||
background-color: var(--footer-background-color);
|
||||
display: flex;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
justify-content: space-between;
|
||||
padding: .5rem var(--default-horizontal-margin);
|
||||
position: sticky;
|
||||
z-index: 100;
|
||||
}
|
||||
main {
|
||||
@@ -139,11 +130,13 @@ limitations under the License.
|
||||
color: #b71c1c;
|
||||
}
|
||||
</style>
|
||||
<gr-main-header
|
||||
id="mainHeader"
|
||||
search-query="{{params.query}}"
|
||||
class$="[[_computeShadowClass(_isShadowDom)]]">
|
||||
</gr-main-header>
|
||||
<gr-fixed-panel id="header">
|
||||
<gr-main-header
|
||||
id="mainHeader"
|
||||
search-query="{{params.query}}"
|
||||
class$="[[_computeShadowClass(_isShadowDom)]]">
|
||||
</gr-main-header>
|
||||
</gr-fixed-panel>
|
||||
<main>
|
||||
<template is="dom-if" if="[[_showChangeListView]]" restamp="true">
|
||||
<gr-change-list-view
|
||||
|
||||
@@ -182,6 +182,7 @@
|
||||
if (this.params.justRegistered) {
|
||||
this.$.registration.open();
|
||||
}
|
||||
this.$.header.unfloat();
|
||||
},
|
||||
|
||||
_computeShowGwtUiLink(config) {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<!--
|
||||
Copyright (C) 2017 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">
|
||||
<link rel="import" href="../../../styles/shared-styles.html">
|
||||
|
||||
<dom-module id="gr-fixed-panel">
|
||||
<template>
|
||||
<style include="shared-styles">
|
||||
:host {
|
||||
display: block;
|
||||
min-height: var(--header-height);
|
||||
position: relative;
|
||||
}
|
||||
header {
|
||||
background: inherit;
|
||||
border: inherit;
|
||||
display: inline;
|
||||
height: inherit;
|
||||
}
|
||||
.floating {
|
||||
left: 0;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
will-change: top;
|
||||
}
|
||||
.fixedAtTop {
|
||||
border-bottom: 1px solid #a4a4a4;
|
||||
box-shadow: 0 4px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
</style>
|
||||
<header id="header" class$="[[_computeHeaderClass(_headerFloating, _topLast)]]">
|
||||
<slot></slot>
|
||||
</header>
|
||||
</template>
|
||||
<script src="gr-fixed-panel.js"></script>
|
||||
</dom-module>
|
||||
@@ -0,0 +1,194 @@
|
||||
// Copyright (C) 2017 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-fixed-panel',
|
||||
|
||||
properties: {
|
||||
floatingDisabled: Boolean,
|
||||
readyForMeasure: {
|
||||
type: Boolean,
|
||||
observer: '_readyForMeasureObserver',
|
||||
},
|
||||
keepOnScroll: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
_isMeasured: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
|
||||
/**
|
||||
* Initial offset from the top of the document, in pixels.
|
||||
*/
|
||||
_topInitial: Number,
|
||||
|
||||
/**
|
||||
* Current offset from the top of the window, in pixels.
|
||||
*/
|
||||
_topLast: Number,
|
||||
|
||||
_headerHeight: Number,
|
||||
_headerFloating: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
_observer: {
|
||||
type: Object,
|
||||
value: null,
|
||||
},
|
||||
_webComponentsReady: Boolean,
|
||||
},
|
||||
|
||||
attached() {
|
||||
if (this.floatingDisabled) {
|
||||
return;
|
||||
}
|
||||
// Enable content measure unless blocked by param.
|
||||
if (this.readyForMeasure !== false) {
|
||||
this.readyForMeasure = true;
|
||||
}
|
||||
this.listen(window, 'resize', 'update');
|
||||
this.listen(window, 'scroll', '_updateOnScroll');
|
||||
this._observer = new MutationObserver(this.update.bind(this));
|
||||
this._observer.observe(this.$.header, {childList: true, subtree: true});
|
||||
},
|
||||
|
||||
detached() {
|
||||
this.unlisten(window, 'scroll', '_updateOnScroll');
|
||||
this.unlisten(window, 'resize', 'update');
|
||||
if (this._observer) {
|
||||
this._observer.disconnect();
|
||||
}
|
||||
},
|
||||
|
||||
_readyForMeasureObserver(readyForMeasure) {
|
||||
if (readyForMeasure) {
|
||||
this.update();
|
||||
}
|
||||
},
|
||||
|
||||
_computeHeaderClass(headerFloating, topLast) {
|
||||
const fixedAtTop = this.keepOnScroll && topLast === 0;
|
||||
return [
|
||||
headerFloating ? 'floating' : '',
|
||||
fixedAtTop ? 'fixedAtTop' : '',
|
||||
].join(' ');
|
||||
},
|
||||
|
||||
_getScrollY() {
|
||||
return window.scrollY;
|
||||
},
|
||||
|
||||
unfloat() {
|
||||
if (this.floatingDisabled) {
|
||||
return;
|
||||
}
|
||||
this.$.header.style.top = '';
|
||||
this._headerFloating = false;
|
||||
this.updateStyles({'--header-height': ''});
|
||||
},
|
||||
|
||||
update() {
|
||||
this.debounce('update', () => {
|
||||
this._updateDebounced();
|
||||
}, 100);
|
||||
},
|
||||
|
||||
_updateOnScroll() {
|
||||
this.debounce('update', () => {
|
||||
this._updateDebounced();
|
||||
});
|
||||
},
|
||||
|
||||
_updateDebounced() {
|
||||
if (this.floatingDisabled) {
|
||||
return;
|
||||
}
|
||||
this._isMeasured = false;
|
||||
this._maybeFloatHeader();
|
||||
this._reposition();
|
||||
},
|
||||
|
||||
_reposition() {
|
||||
if (!this._headerFloating) {
|
||||
return;
|
||||
}
|
||||
const header = this.$.header;
|
||||
const scrollY = this._topInitial - this._getScrollY();
|
||||
let 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;
|
||||
}
|
||||
},
|
||||
|
||||
_measure() {
|
||||
if (this._isMeasured) {
|
||||
return; // Already measured.
|
||||
}
|
||||
const rect = this.$.header.getBoundingClientRect();
|
||||
if (rect.height === 0 && rect.width === 0) {
|
||||
return; // Not ready for measurement yet.
|
||||
}
|
||||
const top = document.body.scrollTop + rect.top;
|
||||
this._topLast = top;
|
||||
this._headerHeight = rect.height;
|
||||
this._topInitial =
|
||||
this.getBoundingClientRect().top + document.body.scrollTop;
|
||||
this._isMeasured = true;
|
||||
},
|
||||
|
||||
_isFloatingNeeded() {
|
||||
return this.keepOnScroll ||
|
||||
document.body.scrollWidth > document.body.clientWidth;
|
||||
},
|
||||
|
||||
_maybeFloatHeader() {
|
||||
if (!this._isFloatingNeeded()) {
|
||||
return;
|
||||
}
|
||||
this._measure();
|
||||
if (this._isMeasured) {
|
||||
this._floatHeader();
|
||||
}
|
||||
},
|
||||
|
||||
_floatHeader() {
|
||||
this.updateStyles({'--header-height': this._headerHeight + 'px'});
|
||||
this._headerFloating = true;
|
||||
},
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,111 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright (C) 2017 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-fixed-panel</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="../../../test/common-test-setup.html"/>
|
||||
<link rel="import" href="gr-fixed-panel.html">
|
||||
|
||||
<script>void(0);</script>
|
||||
|
||||
<test-fixture id="basic">
|
||||
<template>
|
||||
<gr-fixed-panel>
|
||||
<div style="height: 100px"></div>
|
||||
</gr-fixed-panel>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<script>
|
||||
suite('gr-fixed-panel', () => {
|
||||
let element;
|
||||
let sandbox;
|
||||
|
||||
setup(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
element = fixture('basic');
|
||||
element.readyForMeasure = true;
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
test('can be disabled with floatingDisabled', () => {
|
||||
element.floatingDisabled = true;
|
||||
sandbox.stub(element, '_reposition');
|
||||
window.dispatchEvent(new CustomEvent('resize'));
|
||||
element.flushDebouncer('update');
|
||||
assert.isFalse(element._reposition.called);
|
||||
});
|
||||
|
||||
test('header is the height of the content', () => {
|
||||
assert.equal(element.getBoundingClientRect().height, 100);
|
||||
});
|
||||
|
||||
test('scroll triggers _reposition', () => {
|
||||
sandbox.stub(element, '_reposition');
|
||||
window.dispatchEvent(new CustomEvent('scroll'));
|
||||
element.flushDebouncer('update');
|
||||
assert.isTrue(element._reposition.called);
|
||||
});
|
||||
|
||||
suite('_reposition', () => {
|
||||
const getHeaderTop = function() {
|
||||
return element.$.header.style.top;
|
||||
};
|
||||
|
||||
const emulateScrollY = function(distance) {
|
||||
element._getScrollY.returns(distance);
|
||||
element._updateDebounced();
|
||||
element.flushDebouncer('scroll');
|
||||
};
|
||||
|
||||
setup(() => {
|
||||
element._headerTopInitial = 10;
|
||||
sandbox.stub(element, '_getScrollY').returns(0);
|
||||
});
|
||||
|
||||
test('scrolls header along with document', () => {
|
||||
emulateScrollY(20);
|
||||
assert.equal(getHeaderTop(), '-12px');
|
||||
});
|
||||
|
||||
test('does not stick to the top by default', () => {
|
||||
emulateScrollY(150);
|
||||
assert.equal(getHeaderTop(), '-100px');
|
||||
});
|
||||
|
||||
test('sticks to the top if enabled', () => {
|
||||
element.keepOnScroll = true;
|
||||
emulateScrollY(120);
|
||||
assert.equal(getHeaderTop(), '0px');
|
||||
});
|
||||
|
||||
test('drops a shadow when fixed to the top', () => {
|
||||
element.keepOnScroll = true;
|
||||
emulateScrollY(5);
|
||||
assert.isFalse(element.$.header.classList.contains('fixedAtTop'));
|
||||
emulateScrollY(120);
|
||||
assert.isTrue(element.$.header.classList.contains('fixedAtTop'));
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user