Add license; make corrections; cleanup
This commit is contained in:
@@ -1,105 +0,0 @@
|
||||
import { Dispatch, KeyboardEvent as ReactKeyboardEvent, MutableRefObject, SetStateAction } from 'react';
|
||||
import Result from './fuzzy-result';
|
||||
import style from '../styles/fuzzy.module.css';
|
||||
|
||||
export type FuzzyConstr = {
|
||||
pages: Page[],
|
||||
searchField: MutableRefObject<null>,
|
||||
searchValue: string,
|
||||
resultsValue: JSX.Element,
|
||||
setResultsValue: Dispatch<SetStateAction<JSX.Element>>,
|
||||
maxResults?: number
|
||||
};
|
||||
|
||||
export type Page = {
|
||||
title: string;
|
||||
link: string;
|
||||
};
|
||||
|
||||
export default class Fuzzy {
|
||||
searchValue: string = '';
|
||||
searchField: HTMLInputElement | null;
|
||||
pages: Page[];
|
||||
setResultsValue: Dispatch<SetStateAction<JSX.Element>>;
|
||||
resultsValue: JSX.Element;
|
||||
maxResults: number;
|
||||
|
||||
constructor(obj: FuzzyConstr) {
|
||||
this.searchField = obj.searchField.current;
|
||||
this.resultsValue = obj.resultsValue; // not yet implemented. have to look into lazy eval in js
|
||||
this.setResultsValue = obj.setResultsValue;
|
||||
this.searchValue = obj.searchValue || '';
|
||||
|
||||
this.pages = obj.pages
|
||||
this.maxResults = obj.maxResults || this.pages.length;
|
||||
}
|
||||
|
||||
searchKeyUpListener(e: ReactKeyboardEvent) {
|
||||
this.showSearchResults();
|
||||
}
|
||||
|
||||
showSearchResults(): void {
|
||||
let searchValue: string = this.searchValue.toLowerCase();
|
||||
searchValue = searchValue.trimStart().trimEnd();
|
||||
if (this.maxResults !== undefined && searchValue === ''){
|
||||
this.setResultsValue(
|
||||
<>
|
||||
<h2>Search PaulW.XYZ</h2>
|
||||
<div>Enter a page or directory name in the search bar above.</div>
|
||||
</>
|
||||
)
|
||||
return;
|
||||
}
|
||||
|
||||
let results = [];
|
||||
for (const [idx, page] of this.pages.entries()) {
|
||||
const ret = this.fuzzySearch(page.title, searchValue);
|
||||
if (ret === null)
|
||||
continue;
|
||||
results.push({ formatted: ret.formatted, link: page.link, score: ret.score });
|
||||
}
|
||||
|
||||
if (results.length <= 0)
|
||||
return this.setResultsValue(<>Unknown command or no matching pages found.</>);
|
||||
|
||||
results.sort((x, y) => { return x.score - y.score });
|
||||
|
||||
this.setResultsValue(
|
||||
<>
|
||||
{results.map((res: { formatted: JSX.Element[], link: string, score: number }, idx) => {
|
||||
return (<Result
|
||||
link={res.link}
|
||||
formatted={res.formatted}
|
||||
idx={idx}
|
||||
key={`res-${idx}`} />);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
fuzzySearch(findIn: string, find: string) {
|
||||
let search = find.replace(/\s/g, '');
|
||||
search = search.toLowerCase();
|
||||
let tokens: string[] = findIn.split('');
|
||||
let elements: JSX.Element[] = new Array(tokens.length);
|
||||
let pc = 0;
|
||||
let score = 0;
|
||||
|
||||
for (const [i, ch] of tokens.entries()) {
|
||||
if (ch.toLowerCase() === search[pc]) {
|
||||
score += i - pc;
|
||||
elements[i] = (<span className={style.highlight}>{ch}</span>);
|
||||
pc++;
|
||||
if (search.length < pc)
|
||||
return null;
|
||||
continue;
|
||||
}
|
||||
elements[i] = (<>{ch}</>); // not very nice :(
|
||||
}
|
||||
|
||||
if (search.length === pc)
|
||||
return { formatted: elements, score: (score / search.length) };
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
import { ChangeEvent, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import Fuzzy from './_fuzzy';
|
||||
import pages from '../public/pages.json';
|
||||
import style from '../styles/fuzzy.module.css';
|
||||
// @ts-ignore
|
||||
import posts from '../posts/meta.json' // I do not like this
|
||||
|
||||
function FuzzyBar(): JSX.Element {
|
||||
const searchField = useRef<any>(null);
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
const [resultsValue, setResultsValue] = useState(<></>);
|
||||
let [metaKey, setMetaKey] = useState('Ctrl');
|
||||
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
let fuzz: Fuzzy | null = null;
|
||||
|
||||
let entries = [...pages];
|
||||
|
||||
for (const [k,v] of posts.entries()) {
|
||||
const item = v as any;
|
||||
if (item.title && item.slug)
|
||||
entries.push({title: item.title, link: `posts/${item.slug}`});
|
||||
}
|
||||
|
||||
try {
|
||||
fuzz = new Fuzzy({
|
||||
pages: entries,
|
||||
searchField: searchField,
|
||||
searchValue: searchValue,
|
||||
resultsValue: resultsValue,
|
||||
setResultsValue: setResultsValue,
|
||||
maxResults: 5
|
||||
});
|
||||
} catch (e: any) {
|
||||
console.error(e.message);
|
||||
}
|
||||
|
||||
function searchInput(e: ChangeEvent<HTMLInputElement>) {
|
||||
setSearchValue(e.target.value);
|
||||
}
|
||||
|
||||
const toggleSearch = useCallback(() => {
|
||||
setShow(!show);
|
||||
fuzz?.showSearchResults();
|
||||
searchField.current?.focus();
|
||||
}, [fuzz, show]);
|
||||
|
||||
useEffect(() => {
|
||||
if (window.navigator.userAgent.match(/mac[\s]?os/i))
|
||||
setMetaKey('⌘ Cmd');
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const event = (e: KeyboardEvent) => {
|
||||
if (e.code === 'KeyK' && (e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
toggleSearch();
|
||||
}
|
||||
|
||||
if (e.code === 'KeyP' && (e.ctrlKey || e.metaKey) && e.shiftKey) {
|
||||
e.preventDefault();
|
||||
alert('not really sure what to do here lol');
|
||||
}
|
||||
|
||||
if (show)
|
||||
searchField.current?.focus();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', event);
|
||||
|
||||
return () => { window.removeEventListener('keydown', event); }
|
||||
}, [fuzz, show, toggleSearch]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{
|
||||
show ?
|
||||
<div className={`fuzzynav ${style.container}`}>
|
||||
<input type='text'
|
||||
className={style.search}
|
||||
value={searchValue}
|
||||
placeholder='Go to ...'
|
||||
ref={searchField}
|
||||
onChange={searchInput}
|
||||
onKeyUp={(e) => { fuzz?.searchKeyUpListener(e) }} />
|
||||
<div
|
||||
id='results'
|
||||
className={style.results}>{resultsValue}</div>
|
||||
</div>
|
||||
: <></>
|
||||
}
|
||||
|
||||
|
||||
<a className={style.searchBar} onClick={toggleSearch}>
|
||||
<span className={style.searchTerm}>Search</span>
|
||||
<div className={style.keybind}>
|
||||
<span className={style.key}>{metaKey}</span> <span className={style.key}>K</span>
|
||||
</div>
|
||||
</a>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default FuzzyBar;
|
||||
@@ -1,20 +0,0 @@
|
||||
import style from '../styles/fuzzy.module.css';
|
||||
import Link from 'next/link';
|
||||
|
||||
function Result(props: { formatted: JSX.Element[], key: string, link: string, idx: number }) {
|
||||
|
||||
return (
|
||||
<Link href={props.link}>
|
||||
<a className={style.hyperlink}>
|
||||
<div className={style['hyperlink-name']}>
|
||||
{props.formatted}
|
||||
</div>
|
||||
<div className={style['hyperlink-url']}>
|
||||
{props.link}
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export default Result;
|
||||
@@ -1,6 +1,5 @@
|
||||
import Meta from './meta';
|
||||
import Title from './title';
|
||||
// import FuzzyBar from './fuzzy-bar';
|
||||
|
||||
type ChildrenType = JSX.Element | Array<ChildrenType>;
|
||||
|
||||
@@ -16,7 +15,6 @@ function Layout({ name, title, children, ancestors }: LayoutProps) {
|
||||
<>
|
||||
<Meta name={name} ancestors={ancestors} />
|
||||
<Title title={title} name={name} ancestors={ancestors} />
|
||||
{/* <FuzzyBar /> */}
|
||||
<div className='container'>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@ function Meta({name, ancestors}
|
||||
return name;
|
||||
|
||||
let path = '';
|
||||
ancestors.map((obj) => {
|
||||
ancestors.forEach((obj) => {
|
||||
path = `${path}${obj.name} /`;
|
||||
});
|
||||
|
||||
|
||||
@@ -2,19 +2,21 @@ import Link from 'next/link';
|
||||
import Pages from '../public/pages.json';
|
||||
|
||||
function QuickLinks() {
|
||||
return (<>
|
||||
<div className='h2'>Quick Links</div>
|
||||
{
|
||||
Pages.map((obj, i) => {
|
||||
const extern = obj.link.match(/^http/) && `blue extern` || '';
|
||||
return (
|
||||
<Link key={i} href={obj.link}>
|
||||
<a className={`${extern} link button`}>{obj.title}</a>
|
||||
</Link>
|
||||
);
|
||||
})
|
||||
}
|
||||
</>);
|
||||
return (
|
||||
<div className='block'>
|
||||
<div className='h2'>Quick Links</div>
|
||||
{
|
||||
Object.entries(Pages).map(([title, link], i) => {
|
||||
const extern = link.match(/^http/) && `blue extern` || '';
|
||||
return (
|
||||
<Link key={i} href={link}>
|
||||
<a className={`${extern} link button`}>{title}</a>
|
||||
</Link>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default QuickLinks;
|
||||
@@ -2,24 +2,25 @@ import Link from "next/link";
|
||||
import { NoteMeta } from "../util/slug";
|
||||
|
||||
function RecentNotes({ notesMeta }: { notesMeta: NoteMeta[] }) {
|
||||
return (<>
|
||||
<div className='h2'>Recent Notes</div>
|
||||
{notesMeta?.slice(0, 10)
|
||||
.map((note: any) => {
|
||||
return <Link key={note.slug} href={`/notes/${note.slug}`}>
|
||||
return (
|
||||
<div className='block'>
|
||||
<div className='h2'>Recent Notes</div>
|
||||
{notesMeta?.slice(0, 10)
|
||||
.map((note: any) => {
|
||||
return <Link key={note.slug} href={`/notes/${note.slug}`}>
|
||||
<a className={`button link`}>{note.title}</a>
|
||||
</Link>
|
||||
})
|
||||
}
|
||||
{
|
||||
notesMeta.length > 10 &&
|
||||
<div className={''}>
|
||||
<Link href='/notes'>
|
||||
<a className='h5'>More...</a>
|
||||
</Link>
|
||||
</div>
|
||||
}
|
||||
</>
|
||||
})
|
||||
}
|
||||
{
|
||||
notesMeta.length > 10 &&
|
||||
<div className={''}>
|
||||
<Link href='/notes'>
|
||||
<a className='h5'>More...</a>
|
||||
</Link>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,30 +4,31 @@ import { PostMeta } from "../util/slug";
|
||||
import style from '../styles/recent-posts.module.css';
|
||||
|
||||
function RecentPosts({ postsMeta }: { postsMeta: PostMeta[] }) {
|
||||
return (<>
|
||||
<div className='h2'>Recent Posts</div>
|
||||
<div className={style.container}>
|
||||
{postsMeta?.slice(0, 10)
|
||||
.map((post: any) => {
|
||||
return <div className={style.block} key={post.slug}>
|
||||
<Link href={`/posts/${post.slug}`}>
|
||||
<a className={`${style.postTitle} h5`}>{post.title}</a>
|
||||
</Link>
|
||||
<span className={style.postDate}>
|
||||
{date.prettyPrint(new Date(post.created_at))}
|
||||
</span>
|
||||
</div>
|
||||
})}
|
||||
</div>
|
||||
{
|
||||
postsMeta.length > 10 &&
|
||||
<div className={style.more}>
|
||||
<Link href='/posts'>
|
||||
<a className='h5'>More...</a>
|
||||
</Link>
|
||||
return (
|
||||
<div className='block'>
|
||||
<div className='h2'>Recent Posts</div>
|
||||
<div className={style.container}>
|
||||
{postsMeta?.slice(0, 10)
|
||||
.map((post: any) => {
|
||||
return <div className={style.block} key={post.slug}>
|
||||
<Link href={`/posts/${post.slug}`}>
|
||||
<a className={`${style.postTitle} h5`}>{post.title}</a>
|
||||
</Link>
|
||||
<span className={style.postDate}>
|
||||
{date.prettyPrint(new Date(post.created_at))}
|
||||
</span>
|
||||
</div>
|
||||
})}
|
||||
</div>
|
||||
}
|
||||
</>
|
||||
{
|
||||
postsMeta.length > 10 &&
|
||||
<div className={style.more}>
|
||||
<Link href='/posts'>
|
||||
<a className='h5'>More...</a>
|
||||
</Link>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,11 +9,11 @@ type propsObj = {
|
||||
|
||||
function createPathElements(ancestors: Array<{ name: string, path: string }>) {
|
||||
let currentPath = '';
|
||||
return ancestors.map((ancestor) => {
|
||||
return ancestors.map((ancestor, id) => {
|
||||
currentPath += `/${ancestor.path}`
|
||||
return (
|
||||
<>
|
||||
<Link href={currentPath}>
|
||||
<Link key={id + 1} href={currentPath}>
|
||||
<a>{ancestor.name}</a>
|
||||
</Link>
|
||||
<> / </>
|
||||
|
||||
Reference in New Issue
Block a user