Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,26 @@
"@visual-framework/vf-box": "^2.4.0",
"@visual-framework/vf-button": "3.0.2",
"@visual-framework/vf-content": "1.6.16",
"@visual-framework/vf-core": "2.2.47",
"@visual-framework/vf-extensions-react": "^0.0.8",
"@visual-framework/vf-font-plex-mono": "^1.1.3",
"@visual-framework/vf-font-plex-sans": "^1.1.3",
"@visual-framework/vf-footer": "^1.2.1",
"@visual-framework/vf-global-header": "^3.1.6",
"@visual-framework/vf-grid": "^1.4.1",
"@visual-framework/vf-hero": "^4.0.3",
"@visual-framework/vf-link": "^2.0.2",
"@visual-framework/vf-mega-menu": "^1.0.2",
"@visual-framework/vf-profile": "^1.4.0",
"@visual-framework/vf-sass-config": "2.6.1",
"@visual-framework/vf-sass-starter": "^0.1.30",
"@visual-framework/vf-search": "^1.0.0",
"@visual-framework/vf-search-client-side": "^2.0.2",
"@visual-framework/vf-stack": "^3.0.0",
"@visual-framework/vf-tabs": "^2.1.5",
"@visual-framework/vf-text": "^1.1.1",
"@visual-framework/vf-u-fullbleed": "^1.2.2",
"@visual-framework/vf-utility-classes": "2.0.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.13.0"
Expand Down
2 changes: 2 additions & 0 deletions src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import { VfHeroComponent } from './components/vf-hero/vf-hero.component';
import { VfTabsComponent } from './components/vf-tabs/vf-tabs.component';
import { VfBackToTopComponent } from './components/vf-back-to-top/vf-back-to-top.component';
import { HomeComponent } from './components/home/home.component';
import { SearchComponent } from './components/search/search.component';

const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'search', component: SearchComponent },
{ path: 'components/vf-back-to-top', component: VfBackToTopComponent },
{ path: 'components/vf-badge', component: VfBadgeComponent },
{ path: 'components/vf-banner', component: VfBannerComponent },
Expand Down
2 changes: 2 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { AppRoutingModule } from './app-routing.module';
import { AppHeaderModule } from './components/header/header.module';
import { AppFooterModule } from './components/footer/footer.module';
import { VfComponentsModule } from './components/vf-components.module';
import { SearchModule } from './components/search/search.module';
@NgModule({
declarations: [
AppComponent
Expand All @@ -20,6 +21,7 @@ import { VfComponentsModule } from './components/vf-components.module';
AppHeaderModule,
AppFooterModule,
VfComponentsModule,
SearchModule,
],
bootstrap: [AppComponent],
schemas: [NO_ERRORS_SCHEMA]
Expand Down
3 changes: 0 additions & 3 deletions src/app/components/footer/footer.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,6 @@ <h4 class="vf-links__heading">
<li class="vf-list__item">
<a class="vf-list__link" href="https://github.com/visual-framework/vf-core">GitHub</a>
</li>
<li class="vf-list__item">
<a class="vf-list__link" href="https://join.slack.com/t/visual-framework/shared_invite/enQtNDAxNzY0NDg4NTY0LWFhMjEwNGY3ZTk3NWYxNWVjOWQ1ZWE4YjViZmY1YjBkMDQxMTNlNjQ0N2ZiMTQ1ZTZiMGM4NjU5Y2E0MjM3ZGQ">Slack</a>
</li>
</ul>
</div>
<div class="vf-links"></div>
Expand Down
3 changes: 0 additions & 3 deletions src/app/components/header/header.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@
<li class="vf-navigation__item">
<a href="https://github.com/visual-framework/vf-core" class="vf-navigation__link vf-mega-menu__link">GitHub</a>
</li>
<li class="vf-navigation__item">
<a href="https://join.slack.com/t/visual-framework/shared_invite/enQtNDAxNzY0NDg4NTY0LWFhMjEwNGY3ZTk3NWYxNWVjOWQ1ZWE4YjViZmY1YjBkMDQxMTNlNjQ0N2ZiMTQ1ZTZiMGM4NjU5Y2E0MjM3ZGQ" class="vf-navigation__link vf-mega-menu__link">Slack</a>
</li>
<li class="vf-navigation__item">
<a class="vf-navigation__link vf-mega-menu__link" data-vf-js-mega-menu-section-id="search-content-section" href="/search">
Search
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/home/home.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ <h1 class="vf-hero__heading">
<h2 class="vf-section-header__heading" id="search"> Search</h2>
</div>

<form action="/search/" method="GET" class="vf-form vf-form--search vf-form--search--responsive | vf-sidebar vf-sidebar--end">
<form action="/search/" method="GET" class="vf-form vf-form--search vf-form--search--responsive | vf-sidebar vf-sidebar--end" ngNoForm>
<div class="vf-sidebar__inner">
<div class="vf-form__item | vf-search__item">
<label class="vf-form__label vf-u-sr-only | vf-search__label" for="search_query"></label>
Expand Down
51 changes: 51 additions & 0 deletions src/app/components/search/search-index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
export interface SearchPage {
id: string;
title: string;
text: string;
url: string;
}

export const COMPONENT_SEARCH_INDEX: SearchPage[] = [
{
id: '0',
title: 'vf-banner',
text: 'vf-banner',
url: '/components/vf-banner'
},
{
id: '1',
title: 'vf-hero',
text: 'vf-hero',
url: '/components/vf-hero'
},
{
id: '2',
title: 'vf-back-to-top',
text: 'vf-back-to-top',
url: '/components/vf-back-to-top'
},
{
id: '3',
title: 'vf-tabs',
text: 'vf-tabs',
url: '/components/vf-tabs'
},
{
id: '4',
title: 'vf-badge',
text: 'vf-badge',
url: '/components/vf-badge'
},
{
id: '5',
title: 'vf-blockquote',
text: 'vf-blockquote',
url: '/components/vf-blockquote'
},
{
id: '6',
title: 'vf-button',
text: 'vf-button',
url: '/components/vf-button'
}
];
54 changes: 54 additions & 0 deletions src/app/components/search/search.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<div>
<section class="vf-intro" id="an-id-for-anchor">
<div class="vf-stack">
<h1 class="vf-intro__heading ">Search</h1>
<p class="vf-lede">Search documentation and guidance.</p>

<p class="vf-intro__text"></p>
</div>
</section>
<div class="embl-grid embl-grid--has-centered-content">
<div></div>
<div class="vf-stack vf-stack--400">
<form action="search" method="GET" class="vf-form vf-form--search vf-form--search--responsive | vf-sidebar vf-sidebar--end">
<div class="vf-sidebar__inner">
<div class="vf-form__item | vf-search__item">
<label class="vf-form__label vf-u-sr-only | vf-search__label" for="search_query_results">Search</label>
<input
#searchInput
type="search"
id="search_query_results"
placeholder="Search components and documentation"
name="search_query"
[value]="searchTerm"
(input)="onSearchInput(searchInput.value)"
class="vf-search__input | vf-form__input"
data-vf-search-client-side-input
data-vf-search-client-side-destination-prefix=""
>
</div>

<button type="submit" class="vf-search__button | vf-button vf-button--primary">
<span class="vf-button__text">Search</span>
</button>
</div>
</form>

<section class="vf-stack vf-stack--400 vf-u-margin__bottom--400" data-vf-search-client-side-results>
<p *ngIf="!searchTerm" class="snippet">Enter a search query.</p>
<p *ngIf="searchTerm && results.length === 0" class="snippet">No results found</p>

<article *ngFor="let result of results" class="vf-box">
<h3 class="vf-text vf-text-heading--3">
<a [routerLink]="result.url" [queryParams]="{ q: searchTerm }">{{ result.title }}</a>
</h3>
<p class="snippet">{{ result.text }}</p>
<p>
<code>{{ result.url }}</code>
</p>
</article>
</section>
</div>
<div></div>
</div>
</div>
11 changes: 11 additions & 0 deletions src/app/components/search/search.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.snippet {
margin: 0;
}

[data-vf-search-client-side-results] {
width: min(100%, 70rem);
}

[data-vf-search-client-side-results] article {
margin-top: 1rem;
}
99 changes: 99 additions & 0 deletions src/app/components/search/search.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs';
import { COMPONENT_SEARCH_INDEX, SearchPage } from './search-index';

@Component({
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.scss']
})
export class SearchComponent implements OnInit, OnDestroy {
searchTerm = '';
results: SearchPage[] = [];

private querySubscription?: Subscription;
private searchInputTimeout?: ReturnType<typeof setTimeout>;

constructor(private route: ActivatedRoute) {}

ngOnInit(): void {
this.querySubscription = this.route.queryParamMap.subscribe((params) => {
this.searchTerm = this.sanitizeQuery(params.get('search_query') || '');
this.renderResults();
});
}

ngOnDestroy(): void {
this.querySubscription?.unsubscribe();

if (this.searchInputTimeout) {
clearTimeout(this.searchInputTimeout);
}
}

onSearchInput(searchTerm: string): void {
if (this.searchInputTimeout) {
clearTimeout(this.searchInputTimeout);
}

this.searchInputTimeout = setTimeout(() => {
const sanitizedSearchTerm = this.sanitizeQuery(searchTerm);
this.searchTerm = sanitizedSearchTerm;
this.renderResults();
}, 600);
}

private renderResults(): void {
const normalizedTerm = this.normalizeSearchTerm(this.searchTerm);

if (!normalizedTerm) {
this.results = [];
return;
}

this.results = [...COMPONENT_SEARCH_INDEX]
.map((page) => ({
page,
score: this.getScore(page, normalizedTerm)
}))
.filter((item) => item.score > 0)
.sort((a, b) => b.score - a.score)
.map((item) => item.page);
}

private getScore(page: SearchPage, normalizedTerm: string): number {
const searchableText = this.normalizeSearchTerm(
`${page.title} ${page.text} ${page.url}`
);

if (!searchableText.includes(normalizedTerm)) {
return 0;
}

let score = 10;

if (this.normalizeSearchTerm(page.title).startsWith(normalizedTerm)) {
score += 100;
}

if (this.normalizeSearchTerm(page.url).includes(normalizedTerm)) {
score += 20;
}

return score;
}

private normalizeSearchTerm(value: string): string {
return value
.toLowerCase()
.replace(/vf-/g, '')
.replace(/-/g, ' ')
.replace(/\s+/g, ' ')
.trim();
}

private sanitizeQuery(value: string): string {
return value.replace(/(<([^>]+)>)/gi, '').trim();
}
}
14 changes: 14 additions & 0 deletions src/app/components/search/search.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { SearchComponent } from './search.component';

@NgModule({
declarations: [SearchComponent],
imports: [
CommonModule,
RouterModule
],
exports: [SearchComponent]
})
export class SearchModule { }
2 changes: 1 addition & 1 deletion src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<base href="/">
<link rel="stylesheet" media="all" href="https://dev.assets.emblstatic.net/vf/develop/css/styles.css"/>
<link rel="stylesheet" media="all" href="https://assets.emblstatic.net/vf/v2.5.28/css/styles.css"/>
<!--
Want to build a site with the Visual Framework?
---
Expand Down
Loading