make scrollYOffset option consume selector and function

This commit is contained in:
Roman Hotsiy 2015-12-27 01:02:17 +02:00
parent db8f9e8a96
commit 1c4a3ca20f
9 changed files with 94 additions and 25 deletions

View File

@ -12,7 +12,7 @@
</nav> </nav>
</nav> </nav>
<redoc scroll-y-offset="50"> <redoc scroll-y-offset="body > nav">
Loading... Loading...
</redoc> </redoc>

View File

@ -37,7 +37,7 @@ export default class StickySidebar {
} }
updatePosition() { updatePosition() {
if ( this.scrollY + this.scrollYOffset >= this.redocEl.offsetTop) { if ( this.scrollY + this.scrollYOffset() >= this.redocEl.offsetTop) {
this.stick(); this.stick();
} else { } else {
this.unstick(); this.unstick();
@ -46,7 +46,7 @@ export default class StickySidebar {
stick() { stick() {
this.adapter.setStyle(this.element, 'position', 'fixed'); this.adapter.setStyle(this.element, 'position', 'fixed');
this.adapter.setStyle(this.element, 'top', this.scrollYOffset); this.adapter.setStyle(this.element, 'top', this.scrollYOffset());
} }
unstick() { unstick() {
@ -60,7 +60,6 @@ export default class StickySidebar {
ngOnInit() { ngOnInit() {
this.redocEl = this.element.offsetParent; this.redocEl = this.element.offsetParent;
this.scrollYOffset = parseInt(this.scrollYOffset);
this.bind(); this.bind();
} }

View File

@ -75,6 +75,6 @@ class TestApp {
constructor() { constructor() {
this.options = {}; this.options = {};
this.scrollParent = window; this.scrollParent = window;
this.options.scrollYOffset = 20; this.options.scrollYOffset = () => 20;
} }
} }

View File

@ -8,13 +8,15 @@ import ApiLogo from '../ApiLogo/api-logo';
import MethodsList from '../MethodsList/methods-list'; import MethodsList from '../MethodsList/methods-list';
import SideMenu from '../SideMenu/side-menu'; import SideMenu from '../SideMenu/side-menu';
import StickySidebar from '../../common/components/StickySidebar/sticky-sidebar'; import StickySidebar from '../../common/components/StickySidebar/sticky-sidebar';
import {options as defaultOptions} from '../../index'; import {options as defaultOptions} from '../../options';
import {ChangeDetectionStrategy} from 'angular2/core'; import {ChangeDetectionStrategy} from 'angular2/core';
import {ElementRef} from 'angular2/core'; import {ElementRef} from 'angular2/core';
import {BrowserDomAdapter} from 'angular2/platform/browser'; import {BrowserDomAdapter} from 'angular2/platform/browser';
import detectScollParent from 'scrollparent'; import detectScollParent from 'scrollparent';
import {isFunction} from 'angular2/src/facade/lang';
let optionNames = new Set(['scrollYOffset']); let optionNames = new Set(['scrollYOffset']);
@RedocComponent({ @RedocComponent({
@ -37,6 +39,7 @@ export default class Redoc extends BaseComponent {
this.scrollParent = detectScollParent(el); this.scrollParent = detectScollParent(el);
this.parseOptions(); this.parseOptions();
this.options = Object.assign({}, defaultOptions, this.options); this.options = Object.assign({}, defaultOptions, this.options);
this.normalizeOptions();
} }
parseOptions() { parseOptions() {
@ -53,9 +56,28 @@ export default class Redoc extends BaseComponent {
.forEach(option => { .forEach(option => {
this.options[option.name] = attributesMap.get(option.attrName); this.options[option.name] = attributesMap.get(option.attrName);
}); });
}
// post-process options normalizeOptions() {
this.options.scrollYOffset = parseInt(this.options.scrollYOffset); // modify scrollYOffset to always be a function
if (!isFunction(this.options.scrollYOffset)) {
if (isFinite(this.options.scrollYOffset)) {
// if number specified create function that returns this value
let numberOffset = parseFloat(this.options.scrollYOffset);
this.options.scrollYOffset = () => numberOffset;
} else {
// if selector or node function that returns bottom offset of this node
let el = this.options.scrollYOffset;
if (!(el instanceof Node)) {
el = this.dom.query(el);
}
if (!el) {
this.options.scrollYOffset = () => 0;
} else {
this.options.scrollYOffset = () => el.offsetTop + el.offsetHeight;
}
}
}
} }
} }
Redoc.parameters = Redoc.parameters.concat([[ElementRef], [BrowserDomAdapter]]); Redoc.parameters = Redoc.parameters.concat([[ElementRef], [BrowserDomAdapter]]);

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
import { getChildDebugElement } from 'tests/helpers'; import { getChildDebugElement } from 'tests/helpers';
import {Component, View, provide} from 'angular2/core'; import {Component, View, ViewMetadata, provide} from 'angular2/core';
import {BrowserDomAdapter} from 'angular2/platform/browser'; import {BrowserDomAdapter} from 'angular2/platform/browser';
import { import {
@ -14,6 +14,7 @@ import {
import Redoc from 'lib/components/Redoc/redoc'; import Redoc from 'lib/components/Redoc/redoc';
import SchemaManager from 'lib/utils/SchemaManager'; import SchemaManager from 'lib/utils/SchemaManager';
import {options} from 'lib/options';
describe('Redoc components', () => { describe('Redoc components', () => {
describe('Redoc Component', () => { describe('Redoc Component', () => {
@ -45,14 +46,60 @@ describe('Redoc components', () => {
}, err => done.fail(err)); }, err => done.fail(err));
}); });
it('should parse component options from host element', (done) => { describe('Options', () => {
builder.createAsync(TestApp).then(fixture => { let component;
let component = getChildDebugElement(fixture.debugElement, 'redoc').componentInstance; let fixture;
fixture.detectChanges();
component.options.scrollYOffset.should.be.equal(50); function build(tmpl, cb) {
builder = builder.overrideView(TestApp,
new ViewMetadata({template: tmpl, directives: [Redoc]}));
builder.createAsync(TestApp).then(_fixture => {
fixture = _fixture;
component = getChildDebugElement(fixture.debugElement, 'redoc').componentInstance;
fixture.detectChanges();
cb();
}, err => cb(err));
}
afterEach(() => {
fixture.destroy(); fixture.destroy();
done(); });
}, err => done.fail(err));
it('should parse numeric scrollYOffset', (done) => {
build(`<redoc scroll-y-offset="50"></redoc>`, err => {
if (err) return done.fail(err);
component.options.scrollYOffset().should.be.equal(50);
done();
});
});
it('should parse selector scrollYOffset', (done) => {
build(`<div id="test" style="position: fixed; height: 50px; top:0"> </div>
<redoc scroll-y-offset="#test"></redoc>`, err => {
if (err) return done.fail(err);
component.options.scrollYOffset().should.be.equal(50);
done();
});
});
it('should return 0 for incorrect selector scrollYOffset', (done) => {
build(`<div id="test" style="position: fixed; height: 50px; top:0"> </div>
<redoc scroll-y-offset="#test2"></redoc>`, err => {
if (err) return done.fail(err);
component.options.scrollYOffset().should.be.equal(0);
done();
});
});
it('should handle function scrollYOffset', (done) => {
options.scrollYOffset = () => 123;
build(`<redoc></redoc>`, err => {
if (err) return done.fail(err);
component.options.scrollYOffset().should.be.equal(123);
options.scrollYOffset = 0;
done();
});
});
}); });
}); });
}); });
@ -62,7 +109,7 @@ describe('Redoc components', () => {
@View({ @View({
directives: [Redoc], directives: [Redoc],
template: template:
`<redoc scroll-y-offset="50"></redoc>` `<redoc></redoc>`
}) })
class TestApp { class TestApp {
} }

View File

@ -84,7 +84,7 @@ export default class SideMenu extends BaseComponent {
scrollTo(el) { scrollTo(el) {
// TODO: rewrite this to use offsetTop as more reliable solution // TODO: rewrite this to use offsetTop as more reliable solution
let subjRect = el.getBoundingClientRect(); let subjRect = el.getBoundingClientRect();
let offset = this.scrollY() + subjRect.top - this.scrollYOffset + 1; let offset = this.scrollY() + subjRect.top - this.scrollYOffset() + 1;
if (this.scrollParent.scrollTo) { if (this.scrollParent.scrollTo) {
this.scrollParent.scrollTo(0, offset); this.scrollParent.scrollTo(0, offset);
} else { } else {
@ -162,11 +162,11 @@ export default class SideMenu extends BaseComponent {
/* returns 1 if element if above the view, 0 if in view and -1 below the view */ /* returns 1 if element if above the view, 0 if in view and -1 below the view */
getElementInViewPos(el) { getElementInViewPos(el) {
if (Math.floor(el.getBoundingClientRect().top) > this.scrollYOffset) { if (Math.floor(el.getBoundingClientRect().top) > this.scrollYOffset()) {
return INVIEW_POSITION.ABOVE; return INVIEW_POSITION.ABOVE;
} }
if (el.getBoundingClientRect().bottom <= this.scrollYOffset) { if (el.getBoundingClientRect().bottom <= this.scrollYOffset()) {
return INVIEW_POSITION.BELLOW; return INVIEW_POSITION.BELLOW;
} }
return INVIEW_POSITION.INVIEW; return INVIEW_POSITION.INVIEW;

View File

@ -20,7 +20,7 @@ import SchemaManager from 'lib/utils/SchemaManager';
let _mockRedoc = { let _mockRedoc = {
options: { options: {
scrollYOffset: 0 scrollYOffset: () => 0
}, },
scrollParent: window scrollParent: window
}; };

View File

@ -6,10 +6,6 @@ import SchemaManager from './utils/SchemaManager';
import {redocEvents} from './events'; import {redocEvents} from './events';
export * from './components/index'; export * from './components/index';
export var options = {
scrollYOffset: 0
};
export function init(schemaUrl) { export function init(schemaUrl) {
var promise = new Promise(function(resolve, reject) { var promise = new Promise(function(resolve, reject) {

5
lib/options.js Normal file
View File

@ -0,0 +1,5 @@
'use strict';
export var options = {
scrollYOffset: 0
};