Skip to content

Commit

Permalink
DOCSTOOLS-190: highlight search words
Browse files Browse the repository at this point in the history
  • Loading branch information
yndx-birman committed Jun 16, 2022
1 parent 4f98e73 commit a79f9bf
Show file tree
Hide file tree
Showing 22 changed files with 1,025 additions and 42 deletions.
1 change: 1 addition & 0 deletions assets/icons/search-bar-arrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 7 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,14 @@
"pre-commit": "lint-staged"
},
"dependencies": {
"@popperjs/core": "2.11.2",
"bem-cn-lite": "4.1.0",
"i18next": "19.9.2",
"lodash": "4.17.21",
"@popperjs/core": "^2.11.2",
"i18next": "^19.9.2",
"lodash": "^4.17.21",
"mark.ts": "^1.0.5",
"react-hotkeys-hook": "^3.3.1",
"react-popper": "^2.2.5",
"bem-cn-lite": "4.1.0", ,
"react-i18next": "11.15.6",
"react-popper": "2.2.5",
"scroll-into-view-if-needed": "2.2.29"
},
"peerDependencies": {
Expand Down
18 changes: 18 additions & 0 deletions src/components/DocPage/DocPage.scss
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,24 @@
margin-bottom: 20px;
}

&__search-bar {
background: var(--yc-color-base);
width: 100%;
height: 40px;
position: sticky;
top: calc(var(--dc-header-height, #{$headerHeight}) + 6px);
z-index: 101;
padding: 0 36px;

@media (max-width: map-get($screenBreakpoints, 'md') - 1) {
padding: 0 10px;
}
}

&__search-bar + &__breadcrumbs {
margin-top: 12px;
}

&_full-screen {
--dc-header-height: 0px;

Expand Down
105 changes: 96 additions & 9 deletions src/components/DocPage/DocPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,18 @@ import {Breadcrumbs} from '../Breadcrumbs';
import {TocNavPanel} from '../TocNavPanel';
import {Controls} from '../Controls';
import {EditButton} from '../EditButton';
import {SearchBar} from '../SearchBar';
import {Feedback, FeedbackView} from '../Feedback';
import Contributors from '../Contributors/Contributors';

import {callSafe, createElementFromHTML, getHeaderTag, getStateKey, InnerProps} from '../../utils';
import {
callSafe,
createElementFromHTML,
getHeaderTag,
getStateKey,
InnerProps,
getRandomKey,
} from '../../utils';
import {DEFAULT_SETTINGS} from '../../constants';

import LinkIcon from '../../../assets/icons/link.svg';
Expand All @@ -34,13 +42,22 @@ import './DocPage.scss';

const b = block('dc-doc-page');

export interface DocPageProps extends DocPageData, Partial<DocSettings> {
export interface DocPageProps extends DocPageData, DocSettings {
lang: Lang;
router: Router;
headerHeight?: number;
tocTitleIcon?: React.ReactNode;
hideTocHeader?: boolean;
hideToc?: boolean;

showSearchBar?: boolean;
searchQuery?: string;
onClickPrevSearch?: () => void;
onClickNextSearch?: () => void;
onCloseSearchBar?: () => void;
searchCurrentIndex?: number;
searchCountResults?: number;

hideControls?: boolean;
hideEditControl?: boolean;
hideFeedbackControls?: boolean;
Expand All @@ -57,35 +74,47 @@ export interface DocPageProps extends DocPageData, Partial<DocSettings> {
onChangeTheme?: (theme: Theme) => void;
onChangeTextSize?: (textSize: TextSizes) => void;
onSendFeedback?: (data: FeedbackSendData) => void;
onContentMutation?: () => void;
onContentLoaded?: () => void;
}

type DocPageInnerProps = InnerProps<DocPageProps, DocSettings>;
type DocPageState = {
loading: boolean;
keyDOM: number;
};

class DocPage extends React.Component<DocPageInnerProps, DocPageState> {
static defaultProps: DocSettings = DEFAULT_SETTINGS;

state: DocPageState = {
loading: false,
loading: true,
keyDOM: getRandomKey(),
};

bodyRef: HTMLDivElement | null = null;
bodyObserver: MutationObserver | null = null;

componentDidMount(): void {
if (this.props.singlePage) {
const {singlePage} = this.props;

if (singlePage) {
this.addLinksToOriginalArticle();
}

this.setState({loading: false});

this.addBodyObserver();
}

componentDidUpdate(prevProps: DocPageInnerProps): void {
if (prevProps.singlePage !== this.props.singlePage) {
this.setState({loading: true});
}

if (prevProps.html !== this.props.html) {
this.setState({keyDOM: getRandomKey()});
}
}

componentWillUnmount(): void {
Expand Down Expand Up @@ -135,6 +164,7 @@ class DocPage extends React.Component<DocPageInnerProps, DocPageState> {
footer={footer}
>
<DocLayout.Center>
{this.renderSearchBar()}
{this.renderBreadcrumbs()}
{this.renderControls()}
<div className={b('main')}>
Expand Down Expand Up @@ -162,17 +192,40 @@ class DocPage extends React.Component<DocPageInnerProps, DocPageState> {
);
}

private handleBodyMutation = () => {
this.setState({loading: false});
private handleBodyMutation = (mutationsList: MutationRecord[]) => {
const {onContentMutation, onContentLoaded} = this.props;

if (this.props.singlePage) {
this.bodyObserver!.disconnect();
if (this.props.singlePage && this.bodyObserver && this.bodyRef) {
this.bodyObserver.disconnect();
this.addLinksToOriginalArticle();
this.bodyObserver!.observe(this.bodyRef!, {
this.bodyObserver.observe(this.bodyRef, {
childList: true,
subtree: true,
});
}

if (onContentMutation) {
setTimeout(onContentMutation, 0);
}

this.setState({loading: false});

if (!onContentLoaded) {
return;
}

setTimeout(() => {
mutationsList
.filter(({type}) => type === 'childList')
.forEach((mutation) => {
const yfmRoot = mutation.target as HTMLElement;
const yfmImages = [...yfmRoot.querySelectorAll('img')];

yfmImages.forEach((img) => {
img.addEventListener('load', onContentLoaded, false);
});
});
}, 0);
};

private addBodyObserver() {
Expand Down Expand Up @@ -396,6 +449,7 @@ class DocPage extends React.Component<DocPageInnerProps, DocPageState> {

private renderAsideMiniToc() {
const {headings, router, headerHeight, lang, toc} = this.props;
const {keyDOM} = this.state;

const emptyHeaderOrSinglePage = headings.length === 0 || toc.singlePage;
const soloHeaderWithChildren =
Expand All @@ -412,6 +466,7 @@ class DocPage extends React.Component<DocPageInnerProps, DocPageState> {
router={router}
headerHeight={headerHeight}
lang={lang}
key={keyDOM}
/>
</div>
);
Expand Down Expand Up @@ -485,6 +540,38 @@ class DocPage extends React.Component<DocPageInnerProps, DocPageState> {
return !this.showMiniToc || fullScreen;
};

private renderSearchBar = () => {
const {
showSearchBar,
searchQuery,
searchCurrentIndex,
searchCountResults,
onClickPrevSearch,
onClickNextSearch,
onCloseSearchBar,
lang,
singlePage,
} = this.props;

if (!showSearchBar || singlePage) {
return null;
}

return (
<div className={b('search-bar')}>
<SearchBar
lang={lang}
searchCurrentIndex={searchCurrentIndex}
searchCountResults={searchCountResults}
onClickPrevSearch={onClickPrevSearch}
onClickNextSearch={onClickNextSearch}
onCloseSearchBar={onCloseSearchBar}
searchQuery={searchQuery}
/>
</div>
);
};

private renderControls() {
const {
lang,
Expand Down
75 changes: 75 additions & 0 deletions src/components/SearchBar/SearchBar.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
@import '../../../styles/variables';
@import '../../../styles/mixins';

.dc-search-bar {
width: 100%;
height: 100%;
border-radius: 5px;
box-shadow: 0px 3px 10px var(--yc-color-sfx-shadow);
padding: 11px;
display: flex;
align-items: center;
justify-content: space-between;

font-size: var(--yc-text-body-1-short-font-size);
line-height: var(--yc-text-body-1-short-line-height);

&__search-query-label {
color: var(--yc-color-text-secondary);
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;

@media (max-width: map-get($screenBreakpoints, 'md') + 1) {
display: none;
}
}

&__search-query {
max-width: 400px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;

@media (max-width: map-get($screenBreakpoints, 'md') + 1) {
display: none;
}
}

&__left {
width: 90%;
display: flex;
align-items: center;
}

&__navigation {
display: flex;
align-items: center;
margin-right: 11px;
}

&__next-arrow {
transform: rotate(-180deg);
}

&__counter {
margin: 0 4px;
}
}

$hl-class: '.dc-search-highlighted';

#{$hl-class} {
background: var(--dc-text-highlight);

&_selected {
background: var(--dc-text-highlight-selected);
}
}

.yc-root_theme_dark {
& #{$hl-class},
& #{$hl-class}_selected {
color: var(--yc-color-text-inverted-primary);
}
}
Loading

0 comments on commit a79f9bf

Please sign in to comment.