mirror of
				https://github.com/Redocly/redoc.git
				synced 2025-11-04 01:37:32 +03:00 
			
		
		
		
	Disable menu items until corresponding content is rendered
This commit is contained in:
		
							parent
							
								
									4f79d4950a
								
							
						
					
					
						commit
						c0e33bff61
					
				| 
						 | 
					@ -16,7 +16,7 @@ import { BaseComponent } from '../base';
 | 
				
			||||||
import * as detectScollParent from 'scrollparent';
 | 
					import * as detectScollParent from 'scrollparent';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { SpecManager } from '../../utils/spec-manager';
 | 
					import { SpecManager } from '../../utils/spec-manager';
 | 
				
			||||||
import { OptionsService, Hash, MenuService, AppStateService, SchemaHelper } from '../../services/index';
 | 
					import { OptionsService, Hash, AppStateService, SchemaHelper } from '../../services/index';
 | 
				
			||||||
import { LazyTasksService } from '../../shared/components/LazyFor/lazy-for';
 | 
					import { LazyTasksService } from '../../shared/components/LazyFor/lazy-for';
 | 
				
			||||||
import { CustomErrorHandler } from '../../utils/';
 | 
					import { CustomErrorHandler } from '../../utils/';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,7 @@
 | 
				
			||||||
  <div *ngFor="let cat of categories; let idx = index" class="menu-cat">
 | 
					  <div *ngFor="let cat of categories; let idx = index" class="menu-cat">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <label class="menu-cat-header" (click)="activateAndScroll(idx, -1)" [hidden]="cat.headless"
 | 
					    <label class="menu-cat-header" (click)="activateAndScroll(idx, -1)" [hidden]="cat.headless"
 | 
				
			||||||
    [ngClass]="{active: cat.active, disabled: !cat.ready}"> {{cat.name}}</label>
 | 
					    [ngClass]="{active: cat.active, disabled: !cat.ready && cat.methods.length}"> {{cat.name}}</label>
 | 
				
			||||||
    <ul class="menu-subitems" [@itemAnimation]="cat.active ? 'expanded' : 'collapsed'">
 | 
					    <ul class="menu-subitems" [@itemAnimation]="cat.active ? 'expanded' : 'collapsed'">
 | 
				
			||||||
      <li *ngFor="let method of cat.methods; trackBy:summary; let methIdx = index"
 | 
					      <li *ngFor="let method of cat.methods; trackBy:summary; let methIdx = index"
 | 
				
			||||||
        [ngClass]="{active: method.active, disabled: !method.ready}"
 | 
					        [ngClass]="{active: method.active, disabled: !method.ready}"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,6 +38,11 @@ $mobile-menu-compact-breakpoint: 550px;
 | 
				
			||||||
  &[hidden] {
 | 
					  &[hidden] {
 | 
				
			||||||
    display: none;
 | 
					    display: none;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &.disabled, &.disabled:hover {
 | 
				
			||||||
 | 
					    cursor: default;
 | 
				
			||||||
 | 
					    color: lighten($text-color, 60%);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.menu-subitems {
 | 
					.menu-subitems {
 | 
				
			||||||
| 
						 | 
					@ -72,6 +77,11 @@ $mobile-menu-compact-breakpoint: 550px;
 | 
				
			||||||
  & li.active {
 | 
					  & li.active {
 | 
				
			||||||
    background: darken($side-menu-active-bg-color, 6%);
 | 
					    background: darken($side-menu-active-bg-color, 6%);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &.disabled, &.disabled:hover {
 | 
				
			||||||
 | 
					    cursor: default;
 | 
				
			||||||
 | 
					    color: lighten($text-color, 60%);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,6 +38,8 @@ export class SideMenu extends BaseComponent implements OnInit {
 | 
				
			||||||
  private $resourcesNav: any;
 | 
					  private $resourcesNav: any;
 | 
				
			||||||
  private $scrollParent: any;
 | 
					  private $scrollParent: any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private firstChange = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(specMgr:SpecManager, elementRef:ElementRef,
 | 
					  constructor(specMgr:SpecManager, elementRef:ElementRef,
 | 
				
			||||||
  private scrollService:ScrollService, private menuService:MenuService, private hash:Hash,
 | 
					  private scrollService:ScrollService, private menuService:MenuService, private hash:Hash,
 | 
				
			||||||
  optionsService:OptionsService, private detectorRef:ChangeDetectorRef) {
 | 
					  optionsService:OptionsService, private detectorRef:ChangeDetectorRef) {
 | 
				
			||||||
| 
						 | 
					@ -52,20 +54,37 @@ export class SideMenu extends BaseComponent implements OnInit {
 | 
				
			||||||
    this.menuService.changed.subscribe((evt) => this.changed(evt));
 | 
					    this.menuService.changed.subscribe((evt) => this.changed(evt));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  changed({cat, item}) {
 | 
					  changed(newItem) {
 | 
				
			||||||
 | 
					    if (newItem) {
 | 
				
			||||||
 | 
					      let {cat, item} = newItem;
 | 
				
			||||||
      this.activeCatCaption = cat.name || '';
 | 
					      this.activeCatCaption = cat.name || '';
 | 
				
			||||||
      this.activeItemCaption = item && item.summary || '';
 | 
					      this.activeItemCaption = item && item.summary || '';
 | 
				
			||||||
 | 
					 | 
				
			||||||
    //safari doesn't update bindings if not run changeDetector manually :(
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    this.detectorRef.detectChanges();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  activateAndScroll(idx, methodIdx) {
 | 
					    //safari doesn't update bindings if not run changeDetector manually :(
 | 
				
			||||||
 | 
					    this.detectorRef.detectChanges();
 | 
				
			||||||
 | 
					    if (this.firstChange) {
 | 
				
			||||||
 | 
					      this.scrollActiveIntoView();
 | 
				
			||||||
 | 
					      this.firstChange = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  scrollActiveIntoView() {
 | 
				
			||||||
 | 
					    let $item = this.$element.querySelector('li.active, label.active');
 | 
				
			||||||
 | 
					    if ($item) $item.scrollIntoView();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  activateAndScroll(catIdx, methodIdx) {
 | 
				
			||||||
    if (this.mobileMode()) {
 | 
					    if (this.mobileMode()) {
 | 
				
			||||||
      this.toggleMobileNav();
 | 
					      this.toggleMobileNav();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.menuService.activate(idx, methodIdx);
 | 
					    let menu = this.categories;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!menu[catIdx].ready) return;
 | 
				
			||||||
 | 
					    if (menu[catIdx].methods && menu[catIdx].methods.length && (methodIdx >= 0) &&
 | 
				
			||||||
 | 
					    !menu[catIdx].methods[methodIdx].ready) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.menuService.activate(catIdx, methodIdx);
 | 
				
			||||||
    this.menuService.scrollToActive();
 | 
					    this.menuService.scrollToActive();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,13 +37,13 @@ export class MenuService {
 | 
				
			||||||
      this.scrollUpdate(evt.isScrolledDown);
 | 
					      this.scrollUpdate(evt.isScrolledDown);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.changeActive(CHANGE.INITIAL);
 | 
					    //this.changeActive(CHANGE.INITIAL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.hash.value.subscribe((hash) => {
 | 
					    this.hash.value.subscribe((hash) => {
 | 
				
			||||||
      if (hash == undefined) return;
 | 
					      if (hash == undefined) return;
 | 
				
			||||||
      this.setActiveByHash(hash);
 | 
					      this.setActiveByHash(hash);
 | 
				
			||||||
      if (!this.tasks.empty) {
 | 
					      if (!this.tasks.empty) {
 | 
				
			||||||
        this.tasks.start(this.activeCatIdx, this.activeMethodIdx);
 | 
					        this.tasks.start(this.activeCatIdx, this.activeMethodIdx, this);
 | 
				
			||||||
        this.scrollService.setStickElement(this.getCurrentMethodEl());
 | 
					        this.scrollService.setStickElement(this.getCurrentMethodEl());
 | 
				
			||||||
        if (hash) this.scrollToActive();
 | 
					        if (hash) this.scrollToActive();
 | 
				
			||||||
        this.appState.stopLoading();
 | 
					        this.appState.stopLoading();
 | 
				
			||||||
| 
						 | 
					@ -53,6 +53,14 @@ export class MenuService {
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  enableItem(catIdx, methodIdx) {
 | 
				
			||||||
 | 
					    let cat = this.categories[catIdx];
 | 
				
			||||||
 | 
					    cat.ready = true;
 | 
				
			||||||
 | 
					    cat.methods[methodIdx].ready = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.changed.next();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  get activeMethodPtr() {
 | 
					  get activeMethodPtr() {
 | 
				
			||||||
    let cat = this.categories[this.activeCatIdx];
 | 
					    let cat = this.categories[this.activeCatIdx];
 | 
				
			||||||
    let ptr = null;
 | 
					    let ptr = null;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,6 +15,7 @@ export interface MenuMethod {
 | 
				
			||||||
  tag: string;
 | 
					  tag: string;
 | 
				
			||||||
  pointer: string;
 | 
					  pointer: string;
 | 
				
			||||||
  operationId: string;
 | 
					  operationId: string;
 | 
				
			||||||
 | 
					  ready: boolean
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface MenuCategory {
 | 
					export interface MenuCategory {
 | 
				
			||||||
| 
						 | 
					@ -26,6 +27,7 @@ export interface MenuCategory {
 | 
				
			||||||
  description?: string;
 | 
					  description?: string;
 | 
				
			||||||
  empty?: string;
 | 
					  empty?: string;
 | 
				
			||||||
  virtual?: boolean;
 | 
					  virtual?: boolean;
 | 
				
			||||||
 | 
					  ready: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// global var for this module
 | 
					// global var for this module
 | 
				
			||||||
| 
						 | 
					@ -304,7 +306,7 @@ export class SchemaHelper {
 | 
				
			||||||
    for (let header of (<Array<string>>(schema.info && schema.info['x-redoc-markdown-headers'] || []))) {
 | 
					    for (let header of (<Array<string>>(schema.info && schema.info['x-redoc-markdown-headers'] || []))) {
 | 
				
			||||||
      let id = 'section/' + slugify(header);
 | 
					      let id = 'section/' + slugify(header);
 | 
				
			||||||
      tag2MethodMapping[id] = {
 | 
					      tag2MethodMapping[id] = {
 | 
				
			||||||
        name: header, id: id, virtual: true, methods: [], idx: catIdx
 | 
					        name: header, id: id, virtual: true, ready: true, methods: [], idx: catIdx
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
      catIdx++;
 | 
					      catIdx++;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -317,6 +319,7 @@ export class SchemaHelper {
 | 
				
			||||||
        description: tag.description,
 | 
					        description: tag.description,
 | 
				
			||||||
        headless: tag.name === '',
 | 
					        headless: tag.name === '',
 | 
				
			||||||
        empty: !!tag['x-traitTag'],
 | 
					        empty: !!tag['x-traitTag'],
 | 
				
			||||||
 | 
					        ready: !!tag['x-traitTag'],
 | 
				
			||||||
        methods: [],
 | 
					        methods: [],
 | 
				
			||||||
        idx: catIdx
 | 
					        idx: catIdx
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,6 +32,8 @@ export class LazyTasksService {
 | 
				
			||||||
  private _tasks = [];
 | 
					  private _tasks = [];
 | 
				
			||||||
  private _current: number = 0;
 | 
					  private _current: number = 0;
 | 
				
			||||||
  private _syncCount: number = 0;
 | 
					  private _syncCount: number = 0;
 | 
				
			||||||
 | 
					  private menuService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public loadProgress = new BehaviorSubject<number>(0);
 | 
					  public loadProgress = new BehaviorSubject<number>(0);
 | 
				
			||||||
  public allSync = false;
 | 
					  public allSync = false;
 | 
				
			||||||
  constructor(public optionsService: OptionsService, private zone: NgZone) {
 | 
					  constructor(public optionsService: OptionsService, private zone: NgZone) {
 | 
				
			||||||
| 
						 | 
					@ -61,6 +63,7 @@ export class LazyTasksService {
 | 
				
			||||||
    if (!task) return;
 | 
					    if (!task) return;
 | 
				
			||||||
    task._callback(task.idx, true);
 | 
					    task._callback(task.idx, true);
 | 
				
			||||||
    this._current++;
 | 
					    this._current++;
 | 
				
			||||||
 | 
					    this.menuService.enableItem(task.catIdx, task.idx);
 | 
				
			||||||
    this.loadProgress.next(this._current / this._tasks.length * 100);
 | 
					    this.loadProgress.next(this._current / this._tasks.length * 100);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -70,7 +73,7 @@ export class LazyTasksService {
 | 
				
			||||||
      if (!task) return;
 | 
					      if (!task) return;
 | 
				
			||||||
      task._callback(task.idx, false).then(() => {
 | 
					      task._callback(task.idx, false).then(() => {
 | 
				
			||||||
        this._current++;
 | 
					        this._current++;
 | 
				
			||||||
 | 
					        this.menuService.enableItem(task.catIdx, task.idx);
 | 
				
			||||||
        setTimeout(()=> this.nextTask());
 | 
					        setTimeout(()=> this.nextTask());
 | 
				
			||||||
        this.loadProgress.next(this._current / this._tasks.length * 100);
 | 
					        this.loadProgress.next(this._current / this._tasks.length * 100);
 | 
				
			||||||
      }).catch(err => console.error(err));
 | 
					      }).catch(err => console.error(err));
 | 
				
			||||||
| 
						 | 
					@ -91,7 +94,8 @@ export class LazyTasksService {
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  start(catIdx, metIdx) {
 | 
					  start(catIdx, metIdx, menuService) {
 | 
				
			||||||
 | 
					    this.menuService = menuService;
 | 
				
			||||||
    let syncCount = 5;
 | 
					    let syncCount = 5;
 | 
				
			||||||
    // I know this is bad practice to detect browsers but there is an issue on Safari only
 | 
					    // I know this is bad practice to detect browsers but there is an issue on Safari only
 | 
				
			||||||
    // http://stackoverflow.com/questions/40692365/maintaining-scroll-position-while-inserting-elements-above-glitching-only-in-sa
 | 
					    // http://stackoverflow.com/questions/40692365/maintaining-scroll-position-while-inserting-elements-above-glitching-only-in-sa
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user