mirror of
https://github.com/Redocly/redoc.git
synced 2025-01-31 10:04:08 +03:00
Lazy search highlight + basic search
This commit is contained in:
parent
fed2f378df
commit
46f6b29547
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
|
||||
import { Component, ChangeDetectionStrategy, OnInit, ElementRef } from '@angular/core';
|
||||
import { SpecManager, BaseComponent } from '../base';
|
||||
import { OptionsService } from '../../services/index';
|
||||
import { OptionsService, Marker } from '../../services/index';
|
||||
|
||||
@Component({
|
||||
selector: 'api-info',
|
||||
|
@ -12,8 +12,13 @@ import { OptionsService } from '../../services/index';
|
|||
export class ApiInfo extends BaseComponent implements OnInit {
|
||||
info: any = {};
|
||||
specUrl: String;
|
||||
constructor(specMgr: SpecManager, private optionsService: OptionsService) {
|
||||
constructor(specMgr: SpecManager,
|
||||
private optionsService: OptionsService,
|
||||
elRef: ElementRef,
|
||||
marker: Marker
|
||||
) {
|
||||
super(specMgr);
|
||||
marker.addElement(elRef.nativeElement);
|
||||
}
|
||||
|
||||
init() {
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
</div>
|
||||
<div class="menu-content" sticky-sidebar [scrollParent]="options.$scrollParent" [scrollYOffset]="options.scrollYOffset">
|
||||
<api-logo> </api-logo>
|
||||
<redoc-search> </redoc-search>
|
||||
<side-menu> </side-menu>
|
||||
</div>
|
||||
<div class="api-content">
|
||||
|
|
1
lib/components/Search/redoc-search.html
Normal file
1
lib/components/Search/redoc-search.html
Normal file
|
@ -0,0 +1 @@
|
|||
<input #search (keyup)="update(search.value)">
|
3
lib/components/Search/redoc-search.scss
Normal file
3
lib/components/Search/redoc-search.scss
Normal file
|
@ -0,0 +1,3 @@
|
|||
:host {
|
||||
display: block;
|
||||
}
|
28
lib/components/Search/redoc-search.ts
Normal file
28
lib/components/Search/redoc-search.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
'use strict';
|
||||
import { Component, ChangeDetectionStrategy, OnInit, HostBinding } from '@angular/core';
|
||||
import { Marker } from '../../services/';
|
||||
|
||||
@Component({
|
||||
selector: 'redoc-search',
|
||||
styleUrls: ['./redoc-search.css'],
|
||||
templateUrl: './redoc-search.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class RedocSearch implements OnInit {
|
||||
logo:any = {};
|
||||
|
||||
constructor(private marker: Marker) {
|
||||
}
|
||||
|
||||
init() {
|
||||
|
||||
}
|
||||
|
||||
update(val) {
|
||||
this.marker.mark(val);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ import { Component, EventEmitter, Input, Output, ElementRef, ChangeDetectorRef,
|
|||
//import { global } from '@angular/core/src/facade/lang';
|
||||
import { trigger, state, animate, transition, style } from '@angular/core';
|
||||
import { BaseComponent, SpecManager } from '../base';
|
||||
import { ScrollService, MenuService, OptionsService, MenuItem } from '../../services/';
|
||||
import { ScrollService, MenuService, OptionsService, MenuItem, Marker} from '../../services/';
|
||||
import { BrowserDomAdapter as DOM } from '../../utils/browser-adapter';
|
||||
|
||||
const global = window;
|
||||
|
@ -55,7 +55,7 @@ export class SideMenu extends BaseComponent implements OnInit, OnDestroy {
|
|||
|
||||
constructor(specMgr:SpecManager, elementRef:ElementRef,
|
||||
private scrollService:ScrollService, private menuService:MenuService,
|
||||
optionsService:OptionsService, private detectorRef:ChangeDetectorRef) {
|
||||
optionsService:OptionsService, private detectorRef:ChangeDetectorRef, private marker:Marker) {
|
||||
super(specMgr);
|
||||
this.$element = elementRef.nativeElement;
|
||||
|
||||
|
@ -64,7 +64,8 @@ export class SideMenu extends BaseComponent implements OnInit, OnDestroy {
|
|||
|
||||
this.options = optionsService.options;
|
||||
|
||||
this.menuService.changed.subscribe((evt) => this.changed(evt));
|
||||
this.menuService.changedActiveItem.subscribe((evt) => this.changed(evt));
|
||||
this.menuService.changed.subscribe((evt) => this.detectorRef.detectChanges());
|
||||
}
|
||||
|
||||
changed(item) {
|
||||
|
@ -147,4 +148,7 @@ export class SideMenu extends BaseComponent implements OnInit, OnDestroy {
|
|||
ngOnInit() {
|
||||
this.preinit();
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,15 +15,16 @@ import { Method } from './Method/method';
|
|||
import { Warnings } from './Warnings/warnings';
|
||||
import { SecurityDefinitions } from './SecurityDefinitions/security-definitions';
|
||||
import { LoadingBar } from './LoadingBar/loading-bar';
|
||||
import { RedocSearch } from './Search/redoc-search';
|
||||
|
||||
import { Redoc } from './Redoc/redoc';
|
||||
|
||||
export const REDOC_DIRECTIVES = [
|
||||
ApiInfo, ApiLogo, JsonSchema, JsonSchemaLazy, ParamsList, RequestSamples, ResponsesList,
|
||||
ResponsesSamples, SchemaSample, SideMenu, MethodsList, Method, Warnings, Redoc, SecurityDefinitions,
|
||||
LoadingBar, SideMenuItems
|
||||
LoadingBar, SideMenuItems, RedocSearch
|
||||
];
|
||||
|
||||
export { ApiInfo, ApiLogo, JsonSchema, JsonSchemaLazy, ParamsList, RequestSamples, ResponsesList,
|
||||
ResponsesSamples, SchemaSample, SideMenu, MethodsList, Method, Warnings, Redoc, SecurityDefinitions,
|
||||
LoadingBar, SideMenuItems }
|
||||
LoadingBar, SideMenuItems, RedocSearch }
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
AppStateService,
|
||||
ComponentParser,
|
||||
ContentProjector,
|
||||
Marker,
|
||||
COMPONENT_PARSER_ALLOWED } from './services/';
|
||||
import { SpecManager } from './utils/spec-manager';
|
||||
|
||||
|
@ -35,6 +36,7 @@ import { SpecManager } from './utils/spec-manager';
|
|||
ComponentParser,
|
||||
ContentProjector,
|
||||
LazyTasksService,
|
||||
Marker,
|
||||
{ provide: APP_ID, useValue: 'redoc' },
|
||||
{ provide: ErrorHandler, useClass: CustomErrorHandler },
|
||||
{ provide: COMPONENT_PARSER_ALLOWED, useValue: { 'security-definitions': SecurityDefinitions} }
|
||||
|
|
|
@ -11,3 +11,4 @@ export * from './warnings.service';
|
|||
|
||||
export * from './component-parser.service';
|
||||
export * from './content-projector.service';
|
||||
export * from './marker.service';
|
||||
|
|
90
lib/services/marker.service.ts
Normal file
90
lib/services/marker.service.ts
Normal file
|
@ -0,0 +1,90 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import * as Mark from 'mark.js';
|
||||
import { MenuService } from './menu.service';
|
||||
|
||||
const ROLL_LEN = 5;
|
||||
@Injectable()
|
||||
export class Marker {
|
||||
permInstances = [];
|
||||
rolledInstances = new Array(ROLL_LEN);
|
||||
term: string;
|
||||
|
||||
currIdx = -1;
|
||||
|
||||
constructor(private menu: MenuService) {
|
||||
menu.changedActiveItem.subscribe(() => {
|
||||
this.roll();
|
||||
});
|
||||
}
|
||||
|
||||
addElement(el: Element) {
|
||||
this.permInstances.push(new Mark(el));
|
||||
}
|
||||
|
||||
newMarkerAtMenuItem(idx:number) {
|
||||
let context = this.menu.getEl(idx);
|
||||
|
||||
if (this.menu.isTagItem(idx)) {
|
||||
context = this.menu.getTagInfoEl(idx);
|
||||
}
|
||||
let newInst = context && new Mark(context);
|
||||
if (newInst && this.term) {
|
||||
newInst.mark(this.term);
|
||||
}
|
||||
return newInst;
|
||||
}
|
||||
|
||||
roll() {
|
||||
let newIdx = this.menu.activeIdx;
|
||||
let diff = newIdx - this.currIdx;
|
||||
this.currIdx = newIdx;
|
||||
if (diff < 0) {
|
||||
diff = - diff;
|
||||
for (let i=0; i < Math.min(diff, ROLL_LEN); i++) {
|
||||
let prevInst = this.rolledInstances.pop();
|
||||
if(prevInst) prevInst.unmark();
|
||||
|
||||
let idx = newIdx - Math.floor(ROLL_LEN/2) + i;
|
||||
let newMark = this.newMarkerAtMenuItem(idx);
|
||||
this.rolledInstances.unshift(newMark);
|
||||
}
|
||||
} else {
|
||||
for (let i=0; i < Math.min(diff, ROLL_LEN); i++) {
|
||||
let oldInst = this.rolledInstances.shift();
|
||||
oldInst && oldInst.unmark();
|
||||
|
||||
let idx = newIdx + Math.floor(ROLL_LEN/2) - i;
|
||||
let newMark = this.newMarkerAtMenuItem(idx);
|
||||
this.rolledInstances.push(newMark);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mark(term: string) {
|
||||
this.term = term || null;
|
||||
this.remark();
|
||||
}
|
||||
|
||||
remark() {
|
||||
for (let marker of this.permInstances) {
|
||||
if (marker) {
|
||||
marker.unmark();
|
||||
if (this.term) marker.mark(this.term);
|
||||
}
|
||||
}
|
||||
for (let marker of this.rolledInstances) {
|
||||
if (marker) {
|
||||
marker.unmark();
|
||||
if (this.term) marker.mark(this.term);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unmark() {
|
||||
this.term = null;
|
||||
this.remark();
|
||||
}
|
||||
|
||||
updateMark() {
|
||||
}
|
||||
}
|
|
@ -44,6 +44,7 @@ export interface MenuItem {
|
|||
@Injectable()
|
||||
export class MenuService {
|
||||
changed: EventEmitter<any> = new EventEmitter();
|
||||
changedActiveItem: EventEmitter<any> = new EventEmitter();
|
||||
|
||||
items: MenuItem[];
|
||||
activeIdx: number = -1;
|
||||
|
@ -135,6 +136,7 @@ export class MenuService {
|
|||
|
||||
getEl(flatIdx:number):Element {
|
||||
if (flatIdx < 0) return null;
|
||||
if (flatIdx > this.flatItems.length - 1) return null;
|
||||
let currentItem = this.flatItems[flatIdx];
|
||||
if (!currentItem) return;
|
||||
if (currentItem.isGroup) currentItem = this.flatItems[flatIdx + 1];
|
||||
|
@ -156,6 +158,18 @@ export class MenuService {
|
|||
return selector ? document.querySelector(selector) : null;
|
||||
}
|
||||
|
||||
isTagItem(flatIdx: number):boolean {
|
||||
let item = this.flatItems[flatIdx];
|
||||
return item && item.metadata && item.metadata.type === 'tag';
|
||||
}
|
||||
|
||||
getTagInfoEl(flatIdx: number):Element {
|
||||
if (!this.isTagItem(flatIdx)) return null;
|
||||
|
||||
let el = this.getEl(flatIdx);
|
||||
return el && el.querySelector('.tag-info');
|
||||
}
|
||||
|
||||
getCurrentEl():Element {
|
||||
return this.getEl(this.activeIdx);
|
||||
}
|
||||
|
@ -186,7 +200,7 @@ export class MenuService {
|
|||
cItem.parent.active = true;
|
||||
cItem = cItem.parent;
|
||||
}
|
||||
this.changed.next(item);
|
||||
this.changedActiveItem.next(item);
|
||||
}
|
||||
|
||||
changeActive(offset = 1):boolean {
|
||||
|
|
1
manual-types/index.d.ts
vendored
1
manual-types/index.d.ts
vendored
|
@ -6,6 +6,7 @@ declare module "scrollparent"
|
|||
declare module "slugify"
|
||||
declare module "url"
|
||||
declare module "json-pointer";
|
||||
declare module "mark.js";
|
||||
|
||||
declare module "*.css" {
|
||||
const content: string;
|
||||
|
|
|
@ -111,6 +111,7 @@
|
|||
"hint.css": "^2.3.2",
|
||||
"json-pointer": "^0.6.0",
|
||||
"json-schema-ref-parser": "^3.1.2",
|
||||
"mark.js": "github:julmot/mark.js",
|
||||
"openapi-sampler": "^0.3.3",
|
||||
"prismjs": "^1.5.1",
|
||||
"remarkable": "^1.6.2",
|
||||
|
|
Loading…
Reference in New Issue
Block a user