Skip to content

Commit 0ee19b8

Browse files
Jack Matthewsedoparearyee
authored andcommitted
feat(scroll-collapse): emit affix and minimise events
1 parent 00d66ae commit 0ee19b8

File tree

5 files changed

+139
-10
lines changed

5 files changed

+139
-10
lines changed

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
[![Commitizen friendly][commitizen-badge]][commitizen]
66
[![code style: prettier][prettier-badge]][prettier-badge-url]
77

8-
A simple lightweight library for [Angular][angular] that detects scroll direction and adds a `sn-scrolling-up` or `sn-scrolling-down` class to the element and emits an appropriate event. The library can also detect when the user has scrolled passed the element and apply a `sn-affix` class. Useful for make a element sticky when the user has scrolled beyond it. This library can will also apply `sn-minimise` class after the user has scrolled beyond the height of the element.
8+
A simple lightweight library for [Angular][angular] that detects scroll direction and adds a `sn-scrolling-up` or `sn-scrolling-down` class to the element. The library can also detect when the user has scrolled passed the element and apply a `sn-affix` class. Useful for make a element sticky when the user has scrolled beyond it. This library can will also apply `sn-minimise` class after the user has scrolled beyond the height of the element.
9+
10+
Appropriate events for the above classes are also emitted.
911

1012
This is a simple library for [Angular][angular], implemented in the [Angular Package Format v5.0][apfv5].
1113

@@ -90,7 +92,7 @@ In this scenario the nav element will have the class `sn-affix` added when the u
9092

9193
```html
9294
<header>...</header>
93-
<nav class="foo" snScrollCollapse>
95+
<nav class="foo" snScrollCollapse (affixChange)="affixHandler($event)">
9496
...
9597
</nav>
9698
```
@@ -118,7 +120,7 @@ A `[yOffset]` can also be applied. Here `sn-affix` will be added when the top of
118120
In this scenario the nav element will have the class `sn-minimise` added when the user scrolls 100px (the original height of the element) down the page.
119121

120122
```html
121-
<header class="foo" snScrollCollapse>
123+
<header class="foo" snScrollCollapse (minimiseChange)="minimiseHandler($event)">
122124
...
123125
</header>
124126
```

src/app/app.component.html

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
<h1>Scroll down ↓</h1>
22

3-
<nav class="nav" snScrollCollapse (scrollDirectionChange)="scrollDirectionHandler($event)">
3+
<nav
4+
class="nav"
5+
snScrollCollapse
6+
(scrollDirectionChange)="scrollDirectionHandler($event)"
7+
(affixChange)="affixHandler($event)"
8+
(minimiseChange)="minimiseHandler($event)"
9+
>
410
My nav&nbsp;
511
<span class="down">Going down</span>
612
<span class="up">Going up</span>

src/app/app.component.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ import { Direction } from '..';
88
})
99
export class AppComponent {
1010
scrollDirectionHandler(event: Direction) {
11-
console.log(event);
11+
console.log('scrollDirection: ', event);
12+
}
13+
affixHandler(event: boolean) {
14+
console.log('affix: ', event);
15+
}
16+
minimiseHandler(event: boolean) {
17+
console.log('minimise: ', event);
1218
}
1319
}

src/app/scroll-collapse/scroll-collapse.directive.spec.ts

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,24 +114,35 @@ describe('ScrollCollapseDirective', () => {
114114
]);
115115
expect(directive.isScrollingDown).toBeTruthy();
116116
});
117-
it('should emit scroll direction event on scroll', () => {
117+
it('should emit scroll direction event on scroll direction change', () => {
118118
const spy = spyOn(directive.scrollDirectionChange, 'emit');
119+
119120
directive.calculateScrollDirection([
120121
{ scrollX: 0, scrollY: 0, width: 1266, height: 768 },
121122
{ scrollX: 0, scrollY: 100, width: 1266, height: 768 }
122123
]);
123124
expect(spy).toHaveBeenCalledWith(Direction.DOWN);
125+
spy.calls.reset();
126+
124127
directive.calculateScrollDirection([
125128
{ scrollX: 0, scrollY: 100, width: 1266, height: 768 },
126129
{ scrollX: 0, scrollY: 0, width: 1266, height: 768 }
127130
]);
128131
expect(spy).toHaveBeenCalledWith(Direction.UP);
132+
spy.calls.reset();
133+
134+
directive.calculateScrollDirection([
135+
{ scrollX: 0, scrollY: 50, width: 1266, height: 768 },
136+
{ scrollX: 0, scrollY: 0, width: 1266, height: 768 }
137+
]);
138+
expect(spy).not.toHaveBeenCalled();
129139
});
130140
});
131141

132142
describe('minimise mode', () => {
133143
it('should calculate minimise mode', () => {
134144
directive.originalHeight = 100;
145+
135146
directive.calculateMinimiseMode({
136147
scrollX: 0,
137148
scrollY: 0,
@@ -157,6 +168,46 @@ describe('ScrollCollapseDirective', () => {
157168
expect(directive.minimiseMode).toBeFalsy();
158169
});
159170

171+
it('should emit minimise event on minimise mode change', () => {
172+
const spy = spyOn(directive.minimiseChange, 'emit');
173+
directive.originalHeight = 100;
174+
175+
directive.calculateMinimiseMode({
176+
scrollX: 0,
177+
scrollY: 200,
178+
width: 1366,
179+
height: 768
180+
});
181+
expect(spy).toHaveBeenCalledWith(true);
182+
spy.calls.reset();
183+
184+
directive.calculateMinimiseMode({
185+
scrollX: 0,
186+
scrollY: 99,
187+
width: 1366,
188+
height: 768
189+
});
190+
expect(spy).toHaveBeenCalledWith(false);
191+
spy.calls.reset();
192+
193+
directive.calculateMinimiseMode({
194+
scrollX: 0,
195+
scrollY: 200,
196+
width: 1366,
197+
height: 768
198+
});
199+
expect(spy).toHaveBeenCalledWith(true);
200+
spy.calls.reset();
201+
202+
directive.calculateMinimiseMode({
203+
scrollX: 0,
204+
scrollY: 250,
205+
width: 1366,
206+
height: 768
207+
});
208+
expect(spy).not.toHaveBeenCalled();
209+
});
210+
160211
it('should factor in element offsetTop when calculating minimise mode', () => {
161212
directive.originalHeight = 100;
162213
directive.originalTop = 100;
@@ -189,6 +240,7 @@ describe('ScrollCollapseDirective', () => {
189240
describe('affix mode', () => {
190241
it('should calculate affix mode', () => {
191242
directive.originalTop = 100;
243+
192244
directive.calculateAffixMode({
193245
scrollX: 0,
194246
scrollY: 0,
@@ -214,6 +266,46 @@ describe('ScrollCollapseDirective', () => {
214266
expect(directive.affixMode).toBeFalsy();
215267
});
216268

269+
it('should emit affix event on affix mode change', () => {
270+
const spy = spyOn(directive.affixChange, 'emit');
271+
directive.originalTop = 100;
272+
273+
directive.calculateAffixMode({
274+
scrollX: 0,
275+
scrollY: 200,
276+
width: 1366,
277+
height: 768
278+
});
279+
expect(spy).toHaveBeenCalledWith(true);
280+
spy.calls.reset();
281+
282+
directive.calculateAffixMode({
283+
scrollX: 0,
284+
scrollY: 99,
285+
width: 1366,
286+
height: 768
287+
});
288+
expect(spy).toHaveBeenCalledWith(false);
289+
spy.calls.reset();
290+
291+
directive.calculateAffixMode({
292+
scrollX: 0,
293+
scrollY: 200,
294+
width: 1366,
295+
height: 768
296+
});
297+
expect(spy).toHaveBeenCalledWith(true);
298+
spy.calls.reset();
299+
300+
directive.calculateAffixMode({
301+
scrollX: 0,
302+
scrollY: 250,
303+
width: 1366,
304+
height: 768
305+
});
306+
expect(spy).not.toHaveBeenCalled();
307+
});
308+
217309
it('should factor in yOffset property when calculating affix mode', () => {
218310
directive.originalHeight = 100;
219311
directive.originalTop = 500;

src/app/scroll-collapse/scroll-collapse.directive.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,25 @@ export class ScrollCollapseDirective implements AfterViewInit, OnDestroy {
120120
* @memberof ScrollCollapseDirective
121121
*/
122122
@HostBinding(classes.affixClass) public affixMode = false;
123+
/**
124+
* Emits affix boolean on scroll or window resize.
125+
*
126+
* @memberof ScrollCollapseDirective
127+
*/
128+
@Output() affixChange = new EventEmitter<Boolean>();
123129
/**
124130
* Returns true if the user has scrolled pass the origin height of
125131
* the element assuming the element is fixed at the top of the page
126132
*
127133
* @memberof ScrollCollapseDirective
128134
*/
129135
@HostBinding(classes.minimiseClass) public minimiseMode = false;
136+
/**
137+
* Emits affix boolean on scroll or window resize.
138+
*
139+
* @memberof ScrollCollapseDirective
140+
*/
141+
@Output() minimiseChange = new EventEmitter<Boolean>();
130142
/**
131143
* Creates an instance of ScrollCollapseDirective.
132144
* @memberof ScrollCollapseDirective
@@ -198,9 +210,12 @@ export class ScrollCollapseDirective implements AfterViewInit, OnDestroy {
198210
if (noScrollChange) {
199211
return;
200212
}
201-
this.scrollDirection =
213+
const newScrollDirection =
202214
pastEvent.scrollY > currentEvent.scrollY ? Direction.UP : Direction.DOWN;
203-
this.scrollDirectionChange.emit(this.scrollDirection);
215+
if (this.scrollDirection !== newScrollDirection) {
216+
this.scrollDirection = newScrollDirection;
217+
this.scrollDirectionChange.emit(this.scrollDirection);
218+
}
204219
}
205220
/**
206221
* Calculate if the user has scrolled pass the origin height of
@@ -209,8 +224,12 @@ export class ScrollCollapseDirective implements AfterViewInit, OnDestroy {
209224
* @memberof ScrollCollapseDirective
210225
*/
211226
public calculateMinimiseMode(viewport: Viewport): void {
212-
this.minimiseMode =
227+
const newMinimiseMode =
213228
viewport.scrollY >= this.originalHeight + this.originalTop;
229+
if (this.minimiseMode !== newMinimiseMode) {
230+
this.minimiseMode = newMinimiseMode;
231+
this.minimiseChange.emit(this.minimiseMode);
232+
}
214233
}
215234
/**
216235
* Calculate if the user has scrolled pass the origin height of
@@ -219,7 +238,11 @@ export class ScrollCollapseDirective implements AfterViewInit, OnDestroy {
219238
* @memberof ScrollCollapseDirective
220239
*/
221240
public calculateAffixMode(viewport: Viewport): void {
222-
this.affixMode = viewport.scrollY >= this.originalTop - this.yOffset;
241+
const newAffixMode = viewport.scrollY >= this.originalTop - this.yOffset;
242+
if (this.affixMode !== newAffixMode) {
243+
this.affixMode = newAffixMode;
244+
this.affixChange.emit(this.affixMode);
245+
}
223246
}
224247
/**
225248
* Return current viewport values

0 commit comments

Comments
 (0)