fix: various search fixes

This commit is contained in:
Roman Hotsiy 2018-02-22 18:48:50 +02:00
parent 1ff2bd84cc
commit b797c965b2
No known key found for this signature in database
GPG Key ID: 5CB7B3ACABA57CB0
10 changed files with 43 additions and 20 deletions

View File

@ -8,3 +8,4 @@ export * from './dropdown';
export * from './mixins'; export * from './mixins';
export * from './tabs'; export * from './tabs';
export * from './samples'; export * from './samples';
export * from './perfect-scrollbar';

View File

@ -3,6 +3,7 @@ import styled, { withProps } from '../../styled-components';
export const OperationEndpointWrap = styled.div` export const OperationEndpointWrap = styled.div`
cursor: pointer; cursor: pointer;
position: relative; position: relative;
margin-bottom: 5px;
`; `;
export const ServerRelativeURL = styled.span` export const ServerRelativeURL = styled.span`

View File

@ -24,7 +24,7 @@ export class Redoc extends React.Component<RedocProps> {
}; };
componentDidMount() { componentDidMount() {
this.props.store.menu.updateOnHash(); this.props.store.onDidMount();
} }
componentWillUnmount() { componentWillUnmount() {

View File

@ -82,6 +82,8 @@ export interface SearchBoxProps {
marker: MarkerService; marker: MarkerService;
getItemById: (id: string) => IMenuItem | undefined; getItemById: (id: string) => IMenuItem | undefined;
onActivate: (item: IMenuItem) => void; onActivate: (item: IMenuItem) => void;
className?: string;
} }
export interface SearchBoxState { export interface SearchBoxState {
@ -89,6 +91,11 @@ export interface SearchBoxState {
term: string; term: string;
} }
interface SearchResult {
item: IMenuItem;
score: number;
}
export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxState> { export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxState> {
constructor(props) { constructor(props) {
super(props); super(props);
@ -145,8 +152,14 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
}; };
render() { render() {
const items: IMenuItem[] = this.state.results.map(res => this.props.getItemById(res.id)); const results: SearchResult[] = this.state.results.map(res => ({
items.sort((a, b) => (a.depth > b.depth ? 1 : a.depth < b.depth ? -1 : 0)); item: this.props.getItemById(res.id),
score: res.score,
}));
results.sort(
(a, b) =>
a.item.depth > b.item.depth ? 1 : a.item.depth < b.item.depth ? -1 : b.score - a.score,
);
return ( return (
<div> <div>
@ -158,14 +171,14 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
type="text" type="text"
onChange={this.search} onChange={this.search}
/> />
{items.length > 0 && ( {results.length > 0 && (
<SearchResultsBox> <SearchResultsBox>
{items.map(item => ( {results.map(res => (
<MenuItem <MenuItem
item={item} item={res.item}
onActivate={this.props.onActivate} onActivate={this.props.onActivate}
withoutChildren={true} withoutChildren={true}
key={item.id} key={res.item.id}
/> />
))} ))}
</SearchResultsBox> </SearchResultsBox>

View File

@ -2,6 +2,7 @@ export * from './RedocStandalone';
export * from './Redoc/Redoc'; export * from './Redoc/Redoc';
// export * from './Redoc/elements'; // export * from './Redoc/elements';
export * from './Schema/'; export * from './Schema/';
export * from './SearchBox/SearchBox';
export * from './Operation/Operation'; export * from './Operation/Operation';
export * from './RedocStandalone'; export * from './RedocStandalone';

View File

@ -59,15 +59,21 @@ export class AppStore {
this.spec = new SpecStore(spec, specUrl, this.options); this.spec = new SpecStore(spec, specUrl, this.options);
this.menu = new MenuStore(this.spec, this.scroll); this.menu = new MenuStore(this.spec, this.scroll);
this.search = new SearchStore(this.spec); this.search = new SearchStore();
this.search.indexItems(this.menu.items);
this.search.done();
this.disposer = observe(this.menu, 'activeItemIdx', change => { this.disposer = observe(this.menu, 'activeItemIdx', change => {
this.updateMarkOnMenu(change.newValue as number); this.updateMarkOnMenu(change.newValue as number);
}); });
} }
onDidMount() {
this.menu.updateOnHash();
this.updateMarkOnMenu(this.menu.activeItemIdx);
}
updateMarkOnMenu(idx: number) { updateMarkOnMenu(idx: number) {
console.log('update marker');
const start = Math.max(0, idx); const start = Math.max(0, idx);
const end = Math.min(this.menu.flatItems.length, start + 5); const end = Math.min(this.menu.flatItems.length, start + 5);

View File

@ -34,7 +34,6 @@ export class MarkerService {
} }
mark(term?: string) { mark(term?: string) {
console.log('mark', term);
if (!term && !this.prevTerm) return; if (!term && !this.prevTerm) return;
this.map.forEach(val => { this.map.forEach(val => {
val.unmark(); val.unmark();

View File

@ -16,6 +16,7 @@ export interface IMenuItem {
id: string; id: string;
absoluteIdx?: number; absoluteIdx?: number;
name: string; name: string;
description?: string;
depth: number; depth: number;
active: boolean; active: boolean;
items: IMenuItem[]; items: IMenuItem[];

View File

@ -1,21 +1,18 @@
import { SpecStore } from '../index'; import { OperationModel } from './models';
import { GroupModel, OperationModel } from './models';
import worker from './SearchWorker.worker'; import worker from './SearchWorker.worker';
import { IMenuItem } from './MenuStore';
export class SearchStore { export class SearchStore {
searchWorker = new worker(); searchWorker = new worker();
constructor(private spec: SpecStore) { constructor() {}
this.indexGroups(this.spec.operationGroups);
this.done();
}
indexGroups(groups: Array<GroupModel | OperationModel>) { indexItems(groups: Array<IMenuItem | OperationModel>) {
groups.forEach(group => { groups.forEach(group => {
if (group.type !== 'group') { if (group.type !== 'group') {
this.add(group.name, group.description || '', group.id); this.add(group.name, group.description || '', group.id);
} }
this.indexGroups(group.items); this.indexItems(group.items);
}); });
} }

View File

@ -13,6 +13,10 @@ export interface SearchDocument {
id: string; id: string;
} }
export interface SearchResult extends SearchDocument {
score: number;
}
const store: { [id: string]: SearchDocument } = {}; const store: { [id: string]: SearchDocument } = {};
let resolveIndex: (v: lunr.Index) => void; let resolveIndex: (v: lunr.Index) => void;
@ -39,7 +43,7 @@ export async function done() {
resolveIndex(builder.build()); resolveIndex(builder.build());
} }
export async function search(q: string): Promise<SearchDocument[]> { export async function search(q: string): Promise<SearchResult[]> {
if (q.trim().length === 0) { if (q.trim().length === 0) {
return []; return [];
} }
@ -54,5 +58,5 @@ export async function search(q: string): Promise<SearchDocument[]> {
t.term(exp, {}); t.term(exp, {});
}); });
}) })
.map(res => store[res.ref]); .map(res => ({ ...store[res.ref], score: res.score }));
} }