options and options parsing mechanism

This commit is contained in:
Roman Hotsiy 2015-12-26 23:12:10 +02:00
parent 7bbe43e195
commit db8f9e8a96
9 changed files with 49 additions and 27 deletions

View File

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

View File

@ -5,7 +5,7 @@ import {BrowserDomAdapter} from 'angular2/platform/browser';
@Component({ @Component({
selector: 'sticky-sidebar', selector: 'sticky-sidebar',
inputs: ['scrollParent', 'scrollOffsetY'] inputs: ['scrollParent', 'scrollYOffset']
}) })
@View({ @View({
template: ` template: `
@ -37,7 +37,7 @@ export default class StickySidebar {
} }
updatePosition() { updatePosition() {
if ( this.scrollY + this.scrollOffsetY >= 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.scrollOffsetY); this.adapter.setStyle(this.element, 'top', this.scrollYOffset);
} }
unstick() { unstick() {
@ -60,7 +60,7 @@ export default class StickySidebar {
ngOnInit() { ngOnInit() {
this.redocEl = this.element.offsetParent; this.redocEl = this.element.offsetParent;
this.scrollOffsetY = parseInt(this.scrollOffsetY); this.scrollYOffset = parseInt(this.scrollYOffset);
this.bind(); this.bind();
} }

View File

@ -46,7 +46,7 @@ describe('Common components', () => {
expect(component.stick).not.toHaveBeenCalled(); expect(component.stick).not.toHaveBeenCalled();
}); });
it('should stick if scrolled more than scrollOffsetY', () => { it('should stick if scrolled more than scrollYOffset', () => {
spyOn(component, 'stick').and.callThrough(); spyOn(component, 'stick').and.callThrough();
fixture.detectChanges(); fixture.detectChanges();
window.scrollY = 40; window.scrollY = 40;
@ -65,7 +65,7 @@ describe('Common components', () => {
`<div style="padding-top: 20px"> `<div style="padding-top: 20px">
<div style="height: 20px; position: fixed; top: 0;"> </div> <div style="height: 20px; position: fixed; top: 0;"> </div>
<div style="position: relative"> <div style="position: relative">
<sticky-sidebar [scrollParent]="options.scrollParent" [scrollOffsetY]="options.scrollOffsetY"> <sticky-sidebar [scrollParent]="scrollParent" [scrollYOffset]="options.scrollYOffset">
</sticky-sidebar> </sticky-sidebar>
</div> </div>
</div>` </div>`
@ -74,7 +74,7 @@ describe('Common components', () => {
class TestApp { class TestApp {
constructor() { constructor() {
this.options = {}; this.options = {};
this.options.scrollParent = window; this.scrollParent = window;
this.options.scrollOffsetY = 20; this.options.scrollYOffset = 20;
} }
} }

View File

@ -1,5 +1,5 @@
<div class="redoc-wrap"> <div class="redoc-wrap">
<sticky-sidebar [scrollParent]="options.scrollParent" [scrollOffsetY]="options.scrollOffsetTop"> <sticky-sidebar [scrollParent]="scrollParent" [scrollYOffset]="options.scrollYOffset">
<api-logo> </api-logo> <api-logo> </api-logo>
<side-menu> </side-menu> <side-menu> </side-menu>
</sticky-sidebar> </sticky-sidebar>

View File

@ -8,12 +8,14 @@ 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 {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';
let optionNames = new Set(['scrollYOffset']);
@RedocComponent({ @RedocComponent({
selector: 'redoc', selector: 'redoc',
@ -24,20 +26,39 @@ import detectScollParent from 'scrollparent';
changeDetection: ChangeDetectionStrategy.Default changeDetection: ChangeDetectionStrategy.Default
}) })
export default class Redoc extends BaseComponent { export default class Redoc extends BaseComponent {
constructor(schemaMgr, elementRef) { constructor(schemaMgr, elementRef, dom) {
super(schemaMgr); super(schemaMgr);
this.element = elementRef.nativeElement; this.element = elementRef.nativeElement;
let DOM = new BrowserDomAdapter(); this.dom = dom;
let el = this.element; let el = this.element;
this.options = {};
//parse options (top level component doesn't support inputs) //parse options (top level component doesn't support inputs)
this.options.scrollParent = detectScollParent(el); this.scrollParent = detectScollParent(el);
this.options.scrollOffsetTop = parseInt(DOM.getAttribute(el, 'scroll-y-offset')) || 0; this.parseOptions();
this.options = Object.assign({}, defaultOptions, this.options);
}
parseOptions() {
let attributesMap = this.dom.attributeMap(this.element);
this.options = {};
Array.from(attributesMap.keys())
//camelCasify
.map(k => ({
attrName: k,
name: k.replace(/-(.)/g, (m, $1) => $1.toUpperCase())
})
)
.filter(option => optionNames.has(option.name))
.forEach(option => {
this.options[option.name] = attributesMap.get(option.attrName);
});
// post-process options
this.options.scrollYOffset = parseInt(this.options.scrollYOffset);
} }
} }
Redoc.parameters = Redoc.parameters.concat([[ElementRef]]); Redoc.parameters = Redoc.parameters.concat([[ElementRef], [BrowserDomAdapter]]);
// this doesn't work in side-menu.js because of some circular references issue // this doesn't work in side-menu.js because of some circular references issue
SideMenu.parameters = SideMenu.parameters.concat([[Redoc]]); SideMenu.parameters = SideMenu.parameters.concat([[Redoc]]);

View File

@ -49,8 +49,7 @@ describe('Redoc components', () => {
builder.createAsync(TestApp).then(fixture => { builder.createAsync(TestApp).then(fixture => {
let component = getChildDebugElement(fixture.debugElement, 'redoc').componentInstance; let component = getChildDebugElement(fixture.debugElement, 'redoc').componentInstance;
fixture.detectChanges(); fixture.detectChanges();
console.log(component.options.scrollOffsetTop); component.options.scrollYOffset.should.be.equal(50);
component.options.scrollOffsetTop.should.be.equal(50);
fixture.destroy(); fixture.destroy();
done(); done();
}, err => done.fail(err)); }, err => done.fail(err));

View File

@ -33,7 +33,7 @@ export default class SideMenu extends BaseComponent {
this.adapter = adapter; this.adapter = adapter;
this.redoc = redoc; this.redoc = redoc;
this.scrollParent = this.redoc.options.scrollParent; this.scrollParent = this.redoc.scrollParent;
// for some reason constructor is not run inside zone // for some reason constructor is not run inside zone
// as workaround running it manually // as workaround running it manually
@ -65,7 +65,7 @@ export default class SideMenu extends BaseComponent {
bindEvents() { bindEvents() {
this.prevOffsetY = this.scrollY(); this.prevOffsetY = this.scrollY();
this.scrollOffsetY = this.redoc.options.scrollOffsetTop || 0; this.scrollYOffset = this.redoc.options.scrollYOffset;
this._cancel = {}; this._cancel = {};
this._cancel.scroll = this.adapter.onAndCancel(this.scrollParent, 'scroll', () => { this.scrollHandler(); }); this._cancel.scroll = this.adapter.onAndCancel(this.scrollParent, 'scroll', () => { this.scrollHandler(); });
this._cancel.hash = this.adapter.onAndCancel(global, 'hashchange', evt => this.hashScroll(evt)); this._cancel.hash = this.adapter.onAndCancel(global, 'hashchange', evt => this.hashScroll(evt));
@ -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.scrollOffsetY + 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.scrollOffsetY) { if (Math.floor(el.getBoundingClientRect().top) > this.scrollYOffset) {
return INVIEW_POSITION.ABOVE; return INVIEW_POSITION.ABOVE;
} }
if (el.getBoundingClientRect().bottom <= this.scrollOffsetY) { 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,9 +20,9 @@ import SchemaManager from 'lib/utils/SchemaManager';
let _mockRedoc = { let _mockRedoc = {
options: { options: {
scrollOffsetTop: 0, scrollYOffset: 0
scrollParent: window },
} scrollParent: window
}; };
describe('Redoc components', () => { describe('Redoc components', () => {
describe('SideMenu Component', () => { describe('SideMenu Component', () => {

View File

@ -6,7 +6,9 @@ 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) {