Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flag for the ScrollSpy component that ensures the last active element remains visible #3

Open
wants to merge 8 commits into
base: v2-dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dist/css/materialize.min.css.map

Large diffs are not rendered by default.

23 changes: 19 additions & 4 deletions dist/js/materialize.cjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -4615,13 +4615,23 @@ class Pushpin extends Component {
}
}

const SCROLL_SPY_DEFAULT_BEHAVIOR = 'smooth';
let _defaults$9 = {
behavior: SCROLL_SPY_DEFAULT_BEHAVIOR,
throttle: 100,
scrollOffset: 200, // offset - 200 allows elements near bottom of page to scroll
activeClass: 'active',
getActiveElement: (id) => { return 'a[href="#' + id + '"]'; }
};
function mapScrollSpyOptions(options) {
const result = { ...options };
if (result.behavior && result.behavior !== 'instant' && result.behavior !== 'smooth') {
result.behavior = SCROLL_SPY_DEFAULT_BEHAVIOR;
}
return result;
}
class ScrollSpy extends Component {
static DEFAULT_BEHAVIOR = SCROLL_SPY_DEFAULT_BEHAVIOR;
static _elements;
static _count;
static _increment;
Expand All @@ -4631,7 +4641,7 @@ class ScrollSpy extends Component {
static _visibleElements;
static _ticks;
constructor(el, options) {
super(el, options, ScrollSpy);
super(el, (options = mapScrollSpyOptions(options)), ScrollSpy);
this.el.M_ScrollSpy = this;
this.options = {
...ScrollSpy.defaults,
Expand All @@ -4642,6 +4652,7 @@ class ScrollSpy extends Component {
ScrollSpy._increment++;
this.tickId = -1;
this.id = ScrollSpy._increment;
this._handleTriggerClick = this._handleTriggerClick.bind(this);
this._setupEventHandlers();
this._handleWindowScroll();
}
Expand Down Expand Up @@ -4674,28 +4685,32 @@ class ScrollSpy extends Component {
window.addEventListener('scroll', this._handleWindowScroll);
window.addEventListener('resize', this._handleThrottledResize);
document.body.addEventListener('click', this._handleTriggerClick);
console.log('Add event listener:', 'click', this._handleTriggerClick);
}
}
_removeEventHandlers() {
// console.log(`destroy, count: ${ScrollSpy._count}, ${ScrollSpy._count === 0}`)
if (ScrollSpy._count === 0) {
window.removeEventListener('scroll', this._handleWindowScroll);
window.removeEventListener('resize', this._handleThrottledResize);
document.body.removeEventListener('click', this._handleTriggerClick);
console.log('Remove event listener:', 'click', this._handleTriggerClick);
}
}
_handleThrottledResize = Utils.throttle(function () { this._handleWindowScroll(); }, 200).bind(this);
_handleTriggerClick = (e) => {
_handleTriggerClick(e) {
const trigger = e.target;
for (let i = ScrollSpy._elements.length - 1; i >= 0; i--) {
const scrollspy = ScrollSpy._elements[i];
const x = document.querySelector('a[href="#' + scrollspy.el.id + '"]');
if (trigger === x) {
e.preventDefault();
scrollspy.el.scrollIntoView({ behavior: 'smooth' });
console.log(this.options);
scrollspy.el.scrollIntoView({ behavior: this.options.behavior });
break;
}
}
};
}
_handleWindowScroll = () => {
// unique tick id
ScrollSpy._ticks++;
Expand Down
9 changes: 8 additions & 1 deletion dist/js/materialize.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1358,7 +1358,13 @@ declare class Pushpin extends Component<PushpinOptions> {
_removePinClasses(): void;
}

type ScrollSpyBehavior = 'instant' | 'smooth';
interface ScrollSpyOptions extends BaseOptions {
/**
* Scrollspy behavior.
* @default 'smooth'
*/
behavior: ScrollSpyBehavior;
/**
* Throttle of scroll handler.
* @default 100
Expand All @@ -1381,6 +1387,7 @@ interface ScrollSpyOptions extends BaseOptions {
getActiveElement: (id: string) => string;
}
declare class ScrollSpy extends Component<ScrollSpyOptions> {
static readonly DEFAULT_BEHAVIOR: ScrollSpyBehavior;
static _elements: ScrollSpy[];
static _count: number;
static _increment: number;
Expand Down Expand Up @@ -1408,7 +1415,7 @@ declare class ScrollSpy extends Component<ScrollSpyOptions> {
_setupEventHandlers(): void;
_removeEventHandlers(): void;
_handleThrottledResize: () => void;
_handleTriggerClick: (e: MouseEvent) => void;
_handleTriggerClick(e: MouseEvent): void;
_handleWindowScroll: () => void;
static _offset(el: any): {
top: number;
Expand Down
23 changes: 19 additions & 4 deletions dist/js/materialize.js
Original file line number Diff line number Diff line change
Expand Up @@ -4616,13 +4616,23 @@ var M = (function (exports) {
}
}

const SCROLL_SPY_DEFAULT_BEHAVIOR = 'smooth';
let _defaults$9 = {
behavior: SCROLL_SPY_DEFAULT_BEHAVIOR,
throttle: 100,
scrollOffset: 200, // offset - 200 allows elements near bottom of page to scroll
activeClass: 'active',
getActiveElement: (id) => { return 'a[href="#' + id + '"]'; }
};
function mapScrollSpyOptions(options) {
const result = { ...options };
if (result.behavior && result.behavior !== 'instant' && result.behavior !== 'smooth') {
result.behavior = SCROLL_SPY_DEFAULT_BEHAVIOR;
}
return result;
}
class ScrollSpy extends Component {
static DEFAULT_BEHAVIOR = SCROLL_SPY_DEFAULT_BEHAVIOR;
static _elements;
static _count;
static _increment;
Expand All @@ -4632,7 +4642,7 @@ var M = (function (exports) {
static _visibleElements;
static _ticks;
constructor(el, options) {
super(el, options, ScrollSpy);
super(el, (options = mapScrollSpyOptions(options)), ScrollSpy);
this.el.M_ScrollSpy = this;
this.options = {
...ScrollSpy.defaults,
Expand All @@ -4643,6 +4653,7 @@ var M = (function (exports) {
ScrollSpy._increment++;
this.tickId = -1;
this.id = ScrollSpy._increment;
this._handleTriggerClick = this._handleTriggerClick.bind(this);
this._setupEventHandlers();
this._handleWindowScroll();
}
Expand Down Expand Up @@ -4675,28 +4686,32 @@ var M = (function (exports) {
window.addEventListener('scroll', this._handleWindowScroll);
window.addEventListener('resize', this._handleThrottledResize);
document.body.addEventListener('click', this._handleTriggerClick);
console.log('Add event listener:', 'click', this._handleTriggerClick);
}
}
_removeEventHandlers() {
// console.log(`destroy, count: ${ScrollSpy._count}, ${ScrollSpy._count === 0}`)
if (ScrollSpy._count === 0) {
window.removeEventListener('scroll', this._handleWindowScroll);
window.removeEventListener('resize', this._handleThrottledResize);
document.body.removeEventListener('click', this._handleTriggerClick);
console.log('Remove event listener:', 'click', this._handleTriggerClick);
}
}
_handleThrottledResize = Utils.throttle(function () { this._handleWindowScroll(); }, 200).bind(this);
_handleTriggerClick = (e) => {
_handleTriggerClick(e) {
const trigger = e.target;
for (let i = ScrollSpy._elements.length - 1; i >= 0; i--) {
const scrollspy = ScrollSpy._elements[i];
const x = document.querySelector('a[href="#' + scrollspy.el.id + '"]');
if (trigger === x) {
e.preventDefault();
scrollspy.el.scrollIntoView({ behavior: 'smooth' });
console.log(this.options);
scrollspy.el.scrollIntoView({ behavior: this.options.behavior });
break;
}
}
};
}
_handleWindowScroll = () => {
// unique tick id
ScrollSpy._ticks++;
Expand Down
2 changes: 1 addition & 1 deletion dist/js/materialize.min.js

Large diffs are not rendered by default.

23 changes: 19 additions & 4 deletions dist/js/materialize.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4613,13 +4613,23 @@ class Pushpin extends Component {
}
}

const SCROLL_SPY_DEFAULT_BEHAVIOR = 'smooth';
let _defaults$9 = {
behavior: SCROLL_SPY_DEFAULT_BEHAVIOR,
throttle: 100,
scrollOffset: 200, // offset - 200 allows elements near bottom of page to scroll
activeClass: 'active',
getActiveElement: (id) => { return 'a[href="#' + id + '"]'; }
};
function mapScrollSpyOptions(options) {
const result = { ...options };
if (result.behavior && result.behavior !== 'instant' && result.behavior !== 'smooth') {
result.behavior = SCROLL_SPY_DEFAULT_BEHAVIOR;
}
return result;
}
class ScrollSpy extends Component {
static DEFAULT_BEHAVIOR = SCROLL_SPY_DEFAULT_BEHAVIOR;
static _elements;
static _count;
static _increment;
Expand All @@ -4629,7 +4639,7 @@ class ScrollSpy extends Component {
static _visibleElements;
static _ticks;
constructor(el, options) {
super(el, options, ScrollSpy);
super(el, (options = mapScrollSpyOptions(options)), ScrollSpy);
this.el.M_ScrollSpy = this;
this.options = {
...ScrollSpy.defaults,
Expand All @@ -4640,6 +4650,7 @@ class ScrollSpy extends Component {
ScrollSpy._increment++;
this.tickId = -1;
this.id = ScrollSpy._increment;
this._handleTriggerClick = this._handleTriggerClick.bind(this);
this._setupEventHandlers();
this._handleWindowScroll();
}
Expand Down Expand Up @@ -4672,28 +4683,32 @@ class ScrollSpy extends Component {
window.addEventListener('scroll', this._handleWindowScroll);
window.addEventListener('resize', this._handleThrottledResize);
document.body.addEventListener('click', this._handleTriggerClick);
console.log('Add event listener:', 'click', this._handleTriggerClick);
}
}
_removeEventHandlers() {
// console.log(`destroy, count: ${ScrollSpy._count}, ${ScrollSpy._count === 0}`)
if (ScrollSpy._count === 0) {
window.removeEventListener('scroll', this._handleWindowScroll);
window.removeEventListener('resize', this._handleThrottledResize);
document.body.removeEventListener('click', this._handleTriggerClick);
console.log('Remove event listener:', 'click', this._handleTriggerClick);
}
}
_handleThrottledResize = Utils.throttle(function () { this._handleWindowScroll(); }, 200).bind(this);
_handleTriggerClick = (e) => {
_handleTriggerClick(e) {
const trigger = e.target;
for (let i = ScrollSpy._elements.length - 1; i >= 0; i--) {
const scrollspy = ScrollSpy._elements[i];
const x = document.querySelector('a[href="#' + scrollspy.el.id + '"]');
if (trigger === x) {
e.preventDefault();
scrollspy.el.scrollIntoView({ behavior: 'smooth' });
console.log(this.options);
scrollspy.el.scrollIntoView({ behavior: this.options.behavior });
break;
}
}
};
}
_handleWindowScroll = () => {
// unique tick id
ScrollSpy._ticks++;
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
}
],
"scripts": {
"test": "npx jasmine-browser-runner runSpecs",
"test": "jasmine-browser-runner serve",
"originalTest": "jasmine-browser-runner runSpecs",
"build": "rollup --config rollup.config.ts --configPlugin @rollup/plugin-typescript",
"release": " npm run build && node ci/compress.js",
"preversion": "npm test",
Expand Down
16 changes: 14 additions & 2 deletions spec/helpers/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,23 @@ const KEYMAP = {
'105': '9'
};

function XloadHtml(html) {
function XloadHtml(html, options) {
options = options ? options : {};
const defaultOptions = { insertionType: 'append' };
options = {
...defaultOptions,
...options
};

const div = document.createElement('div');
div.classList.add('please-delete-me');
div.innerHTML = html;
document.body.appendChild(div);

if (options.insertionType === 'append') {
document.body.appendChild(div);
} else if (options.insertionType === 'prepend') {
document.body.prepend(div);
}
}

function XunloadFixtures() {
Expand Down
2 changes: 1 addition & 1 deletion spec/support/jasmine-browser.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export default {
srcDir: 'dist',
srcFiles: ['**/materialize.js'],
specDir: 'spec',
specFiles: ['tests/**/*[sS]pec.js'],
specFiles: ['tests1/**/*[sS]pec.js'],
helpers: ['helpers/**/*.js'],

cssFiles: ['**/materialize.css'],
Expand Down
40 changes: 40 additions & 0 deletions spec/tests1/scrollspy/a.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@


<div class="container">
<header id="header" class="row" style="height: 100vh; margin: 0; padding: 0;"></header>
<div class="row">
<div class="col m7">
<div id="introduction" class="section scrollspy"
style="height: 100vh; margin: 0; padding: 0; background-color: red;">
introduction
</div>
<div id="initialization" class="section scrollspy"
style="height: 100vh; margin: 0; padding: 0; background-color: green;">
initialization
</div>
<div id="options" class="section scrollspy"
style="height: 100vh; margin: 0; padding: 0; background-color: yellow;">
options
</div>
</div>

<div class="col hide-on-small-only m5">
<div class="toc-wrapper pinned" style="top: 0px;">
<div style="height: 1px">
<ul class="section table-of-contents">
<li>
<a href="#introduction" class="">Introduction</a>
</li>
<li>
<a href="#initialization" class="">Initialization</a>
</li>
<li>
<a href="#options" class="">Options</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<footer id="footer" class="row" style="height: 100vh; margin: 0; padding: 0;"></footer>
</div>
Loading
Loading