mirror of
				https://github.com/Redocly/redoc.git
				synced 2025-11-04 17:57:30 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			199 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			199 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
'use strict';
 | 
						|
 | 
						|
import { Component,
 | 
						|
  Input,
 | 
						|
  Renderer,
 | 
						|
  ElementRef,
 | 
						|
  OnInit,
 | 
						|
  ChangeDetectionStrategy,
 | 
						|
  ChangeDetectorRef
 | 
						|
} from '@angular/core';
 | 
						|
 | 
						|
import { BaseSearchableComponent, SpecManager } from '../base';
 | 
						|
import { SchemaNormalizer, SchemaHelper, AppStateService } from '../../services/';
 | 
						|
import { JsonPointer, DescendantInfo } from '../../utils/';
 | 
						|
import { Zippy } from '../../shared/components';
 | 
						|
import { JsonSchemaLazy } from './json-schema-lazy';
 | 
						|
 | 
						|
@Component({
 | 
						|
  selector: 'json-schema',
 | 
						|
  templateUrl: './json-schema.html',
 | 
						|
  styleUrls: ['./json-schema.css'],
 | 
						|
  changeDetection: ChangeDetectionStrategy.OnPush
 | 
						|
})
 | 
						|
export class JsonSchema extends BaseSearchableComponent implements OnInit {
 | 
						|
  @Input() pointer: string;
 | 
						|
  @Input() absolutePointer: string;
 | 
						|
  @Input() final: boolean = false;
 | 
						|
  @Input() nestOdd: boolean;
 | 
						|
  @Input() childFor: string;
 | 
						|
  @Input() isRequestSchema: boolean;
 | 
						|
 | 
						|
  schema: any = {};
 | 
						|
  activeDescendant:any = {};
 | 
						|
  discriminator: string = null;
 | 
						|
  _hasSubSchemas: boolean = false;
 | 
						|
  properties: any;
 | 
						|
  _isArray: boolean;
 | 
						|
  normalizer: SchemaNormalizer;
 | 
						|
  descendants: DescendantInfo[];
 | 
						|
 | 
						|
  constructor(
 | 
						|
    specMgr:SpecManager,
 | 
						|
    app: AppStateService,
 | 
						|
    private _renderer: Renderer,
 | 
						|
    private cdr: ChangeDetectorRef,
 | 
						|
    private _elementRef: ElementRef) {
 | 
						|
    super(specMgr, app);
 | 
						|
    this.normalizer = new SchemaNormalizer(specMgr);
 | 
						|
  }
 | 
						|
 | 
						|
  get normPointer() {
 | 
						|
    return this.schema._pointer || this.pointer;
 | 
						|
  }
 | 
						|
 | 
						|
  selectDescendantByIdx(idx) {
 | 
						|
    this.selectDescendant(this.descendants[idx]);
 | 
						|
  }
 | 
						|
 | 
						|
  selectDescendant(activeDescendant: DescendantInfo) {
 | 
						|
    if (!activeDescendant || activeDescendant.active) return;
 | 
						|
    this.descendants.forEach(d => {
 | 
						|
      d.active = false;
 | 
						|
    });
 | 
						|
    activeDescendant.active = true;
 | 
						|
 | 
						|
    this.schema = this.specMgr.getDescendant(activeDescendant, this.componentSchema);
 | 
						|
    this.pointer = this.schema._pointer || activeDescendant.$ref;
 | 
						|
    this.normalizer.reset();
 | 
						|
    this.schema = this.normalizer.normalize(this.schema, this.normPointer,
 | 
						|
      {resolved: true});
 | 
						|
    this.preprocessSchema();
 | 
						|
    this.activeDescendant = activeDescendant;
 | 
						|
  }
 | 
						|
 | 
						|
  initDescendants() {
 | 
						|
    this.descendants = this.specMgr.findDerivedDefinitions(this.normPointer, this.schema);
 | 
						|
    if (!this.descendants.length) return;
 | 
						|
    let discriminator = this.discriminator = this.schema.discriminator || this.schema['x-extendedDiscriminator'];
 | 
						|
    let discrProperty = this.schema.properties &&
 | 
						|
      this.schema.properties[discriminator];
 | 
						|
    if (discrProperty && discrProperty.enum) {
 | 
						|
      let enumOrder = {};
 | 
						|
      discrProperty.enum.forEach((enumItem, idx) => {
 | 
						|
        enumOrder[enumItem] = idx;
 | 
						|
      });
 | 
						|
 | 
						|
      this.descendants = this.descendants
 | 
						|
      .filter(a => {
 | 
						|
        return enumOrder[a.name] != undefined;
 | 
						|
      }).sort((a, b) => {
 | 
						|
        return enumOrder[a.name] > enumOrder[b.name] ? 1 : -1;
 | 
						|
      });
 | 
						|
      this.descendants.forEach((d, idx) => d.idx = idx);
 | 
						|
    }
 | 
						|
    this.selectDescendantByIdx(0);
 | 
						|
  }
 | 
						|
 | 
						|
  init() {
 | 
						|
    if (!this.pointer) return;
 | 
						|
    if (!this.absolutePointer) this.absolutePointer = this.pointer;
 | 
						|
 | 
						|
    this.schema = this.componentSchema;
 | 
						|
    if (!this.schema) {
 | 
						|
      throw new Error(`Can't load component schema at ${this.pointer}`);
 | 
						|
    }
 | 
						|
 | 
						|
    this.applyStyling();
 | 
						|
 | 
						|
    this.schema = this.normalizer.normalize(this.schema, this.normPointer, {resolved: true});
 | 
						|
    this.schema = SchemaHelper.unwrapArray(this.schema, this.normPointer);
 | 
						|
    this._isArray = this.schema._isArray;
 | 
						|
    this.absolutePointer += (this._isArray ? '/items' : '');
 | 
						|
    this.initDescendants();
 | 
						|
    this.preprocessSchema();
 | 
						|
  }
 | 
						|
 | 
						|
  preprocessSchema() {
 | 
						|
    SchemaHelper.preprocess(this.schema, this.normPointer, this.pointer);
 | 
						|
 | 
						|
    if (!this.schema.isTrivial) {
 | 
						|
      SchemaHelper.preprocessProperties(this.schema, this.normPointer, {
 | 
						|
        childFor: this.childFor,
 | 
						|
        discriminator: this.discriminator
 | 
						|
      });
 | 
						|
    }
 | 
						|
 | 
						|
    this.properties = this.schema._properties || [];
 | 
						|
    if (this.isRequestSchema) {
 | 
						|
      this.properties = this.properties && this.properties.filter(prop => !prop.readOnly);
 | 
						|
    }
 | 
						|
 | 
						|
    this._hasSubSchemas = this.properties && this.properties.some(
 | 
						|
      propSchema => {
 | 
						|
        if (propSchema.type === 'array') {
 | 
						|
          propSchema = propSchema.items;
 | 
						|
        }
 | 
						|
        return (propSchema && propSchema.type === 'object' && propSchema._pointer);
 | 
						|
      });
 | 
						|
 | 
						|
    if (this.properties.length === 1) {
 | 
						|
      this.properties[0].expanded = true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  applyStyling() {
 | 
						|
    if (this.nestOdd) {
 | 
						|
      this._renderer.setElementAttribute(this._elementRef.nativeElement, 'nestodd', 'true');
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  trackByName(_: number, item: any): number {
 | 
						|
    return item.name + (item._pointer || '');
 | 
						|
  }
 | 
						|
 | 
						|
  trackByIdx(idx: number, _: any): number {
 | 
						|
    return idx;
 | 
						|
  }
 | 
						|
 | 
						|
  findDescendantWithField(fieldName: string): DescendantInfo {
 | 
						|
    let res: DescendantInfo;
 | 
						|
    for (let descendantInfo of this.descendants) {
 | 
						|
      let schema = this.specMgr.getDescendant(descendantInfo, this.schema);
 | 
						|
      this.normalizer.reset();
 | 
						|
      schema = this.normalizer.normalize(schema, this.normPointer,
 | 
						|
        {resolved: true});
 | 
						|
      if (schema.properties && schema.properties[fieldName]) {
 | 
						|
        res = descendantInfo;
 | 
						|
        break;
 | 
						|
      };
 | 
						|
    };
 | 
						|
    return res;
 | 
						|
  }
 | 
						|
 | 
						|
  ensureSearchIsShown(ptr: string) {
 | 
						|
    if (ptr.startsWith(this.absolutePointer)) {
 | 
						|
      let props = this.properties;
 | 
						|
      if (!props) return;
 | 
						|
      let relative = JsonPointer.relative(this.absolutePointer, ptr);
 | 
						|
      let propName;
 | 
						|
      if (relative.length > 1 && relative[0] === 'properties') {
 | 
						|
        propName = relative[1];
 | 
						|
      }
 | 
						|
      let prop = props.find(p => p.name === propName);
 | 
						|
      if (!prop) {
 | 
						|
        let d = this.findDescendantWithField(propName);
 | 
						|
        this.selectDescendant(d);
 | 
						|
        prop = this.properties.find(p => p.name === propName);
 | 
						|
      }
 | 
						|
      if (prop && !prop.isTrivial) prop.expanded = true;
 | 
						|
      this.cdr.markForCheck();
 | 
						|
      this.cdr.detectChanges();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  ngOnInit() {
 | 
						|
    this.preinit();
 | 
						|
  }
 | 
						|
}
 |