Skip to content

Commit

Permalink
feat: add marker
Browse files Browse the repository at this point in the history
  • Loading branch information
RomanHotsiy committed Mar 5, 2018
1 parent ecf33d2 commit 1ff2bd8
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 16 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"react-dom": "^16.0.0"
},
"dependencies": {
"@types/mark.js": "^8.11.0",
"@types/marked": "^0.3.0",
"classnames": "^2.2.5",
"decko": "^1.2.0",
Expand All @@ -88,6 +89,7 @@
"json-pointer": "^0.6.0",
"json-schema-ref-parser": "^4.0.4",
"lunr": "^2.1.5",
"mark.js": "^8.11.1",
"marked": "^0.3.12",
"mobx": "^3.3.0",
"mobx-react": "^4.3.3",
Expand Down
3 changes: 2 additions & 1 deletion src/components/Redoc/Redoc.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export class Redoc extends React.Component<RedocProps> {
}

render() {
const { store: { spec, menu, options, search } } = this.props;
const { store: { spec, menu, options, search, marker } } = this.props;
const store = this.props.store;
return (
<ThemeProvider theme={options.theme}>
Expand All @@ -42,6 +42,7 @@ export class Redoc extends React.Component<RedocProps> {
<ApiLogo info={spec.info} />
<SearchBox
search={search}
marker={marker}
getItemById={menu.getItemById}
onActivate={menu.activateAndScroll}
/>
Expand Down
50 changes: 36 additions & 14 deletions src/components/SearchBox/SearchBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { IMenuItem } from '../../services/MenuStore';
import { SearchStore } from '../../services/SearchStore';
import { MenuItem } from '../SideMenu/MenuItem';
import { MenuItemLabel } from '../SideMenu/styled.elements';
import { MarkerService } from '../../services/MarkerService';
import { SearchDocument } from '../../services/SearchWorker.worker';

const SearchInput = styled.input.attrs({
className: 'search-input',
Expand Down Expand Up @@ -77,6 +79,7 @@ const SearchResultsBox = styled.div.attrs({

export interface SearchBoxProps {
search: SearchStore;
marker: MarkerService;
getItemById: (id: string) => IMenuItem | undefined;
onActivate: (item: IMenuItem) => void;
}
Expand All @@ -95,33 +98,52 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
};
}

clearResults(term: string) {
this.setState({
results: [],
term,
});
this.props.marker.unmark();
}

clear() {
this.setState({
results: [],
term: '',
});
this.props.marker.unmark();
}

clearIfEsq = event => {
if (event && event.keyCode === 27) {
this.clear();
}
};

setResults(results: SearchDocument[], term: string) {
this.setState({
results,
term,
});
this.props.marker.mark(term);
}

search = (event: React.ChangeEvent<HTMLInputElement>) => {
const q = event.target.value;
if (q.length < 3) {
this.setState({
term: q,
results: [],
});
this.clearResults(q);
return;
}

this.setState({
term: q,
});

this.props.search.search(event.target.value).then(res => {
this.setState({
results: res,
});
this.setResults(res, q);
});
};

clearIfEsq = event => {
if (event && event.keyCode === 27) {
// escape
this.setState({ term: '', results: [] });
}
};

render() {
const items: IMenuItem[] = this.state.results.map(res => this.props.getItemById(res.id));
items.sort((a, b) => (a.depth > b.depth ? 1 : a.depth < b.depth ? -1 : 0));
Expand Down
29 changes: 29 additions & 0 deletions src/services/AppStore.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { observe } from 'mobx';

import { OpenAPISpec } from '../types';
import { loadAndBundleSpec } from '../utils/loadAndBundleSpec';
import { MenuStore } from './MenuStore';
import { SpecStore } from './models';
import { RedocNormalizedOptions, RedocRawOptions } from './RedocNormalizedOptions';
import { ScrollService } from './ScrollService';
import { SearchStore } from './SearchStore';
import { MarkerService } from './MarkerService';

interface StoreData {
menu: {
Expand Down Expand Up @@ -44,8 +47,10 @@ export class AppStore {
rawOptions: RedocRawOptions;
options: RedocNormalizedOptions;
search: SearchStore;
marker = new MarkerService();

private scroll: ScrollService;
private disposer;

constructor(spec: OpenAPISpec, specUrl?: string, options: RedocRawOptions = {}) {
this.rawOptions = options;
Expand All @@ -55,11 +60,35 @@ export class AppStore {
this.menu = new MenuStore(this.spec, this.scroll);

this.search = new SearchStore(this.spec);

this.disposer = observe(this.menu, 'activeItemIdx', change => {
this.updateMarkOnMenu(change.newValue as number);
});
}

updateMarkOnMenu(idx: number) {
console.log('update marker');
const start = Math.max(0, idx);
const end = Math.min(this.menu.flatItems.length, start + 5);

const elements: Element[] = [];
for (let i = start; i < end; i++) {
let elem = this.menu.getElementAt(i);
if (!elem) continue;
if (this.menu.flatItems[i].type === 'section') {
elem = elem.parentElement!.parentElement;
}
if (elem) elements.push(elem);
}

this.marker.addOnly(elements);
this.marker.mark();
}

dispose() {
this.scroll.dispose();
this.menu.dispose();
this.disposer();
}

/**
Expand Down
50 changes: 50 additions & 0 deletions src/services/MarkerService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import * as Mark from 'mark.js';

export class MarkerService {
map: Map<Element, Mark> = new Map();

private prevTerm: string = '';

add(el: HTMLElement) {
this.map.set(el, new Mark(el));
}

delete(el: Element) {
this.map.delete(el);
}

addOnly(elements: Element[]) {
this.map.forEach((inst, elem) => {
if (elements.indexOf(elem) === -1) {
inst.unmark();
this.map.delete(elem);
}
});

for (let el of elements) {
if (!this.map.has(el)) {
this.map.set(el, new Mark(el as HTMLElement));
}
}
}

clearAll() {
this.unmark();
this.map.clear();
}

mark(term?: string) {
console.log('mark', term);
if (!term && !this.prevTerm) return;
this.map.forEach(val => {
val.unmark();
val.mark(term || this.prevTerm);
});
this.prevTerm = term || this.prevTerm || '';
}

unmark() {
this.map.forEach(val => val.unmark());
this.prevTerm = '';
}
}
2 changes: 1 addition & 1 deletion src/services/MenuStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class MenuStore {
/**
* active item absolute index (when flattened). -1 means nothing is selected
*/
activeItemIdx: number = -1;
@observable activeItemIdx: number = -1;

/**
* whether sidebar with menu is opened or not
Expand Down
4 changes: 4 additions & 0 deletions src/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ export * from './SpecStore';
export * from './ClipboardService';
export * from './HistoryService';
export * from './models';
export * from './RedocNormalizedOptions';
export * from './MenuBuilder';
export * from './SearchStore';
export * from './MarkerService';
10 changes: 10 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@
version "2.1.5"
resolved "https://registry.yarnpkg.com/@types/lunr/-/lunr-2.1.5.tgz#afb90226a6d2eb472eb1732cef7493a02b0177fd"

"@types/mark.js@^8.11.0":
version "8.11.0"
resolved "https://registry.yarnpkg.com/@types/mark.js/-/mark.js-8.11.0.tgz#1d507352c30f020a35213f80b5131d8ffba194d7"
dependencies:
"@types/jquery" "*"

"@types/marked@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@types/marked/-/marked-0.3.0.tgz#583c223dd33385a1dda01aaf77b0cd0411c4b524"
Expand Down Expand Up @@ -5098,6 +5104,10 @@ map-visit@^1.0.0:
dependencies:
object-visit "^1.0.0"

mark.js@^8.11.1:
version "8.11.1"
resolved "https://registry.yarnpkg.com/mark.js/-/mark.js-8.11.1.tgz#180f1f9ebef8b0e638e4166ad52db879beb2ffc5"

marked@^0.3.12:
version "0.3.17"
resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.17.tgz#607f06668b3c6b1246b28f13da76116ac1aa2d2b"
Expand Down

0 comments on commit 1ff2bd8

Please sign in to comment.