element-book 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,8 +20,6 @@ export type ElementBookPageExampleInit<ParentPage extends ElementBookPage<any>,
20
20
  * wrapper element!
21
21
  */
22
22
  styles?: CSSResult;
23
- /** Set to true to hide the example controls (example title and buttons). */
24
- hideExampleControls?: boolean | undefined;
25
23
  /** Render the example. */
26
24
  renderCallback: RequireNonVoid<RenderOutput, (renderParams: ElementBookPageExampleRenderParams<ParentPage, StateInit>) => RenderOutput, 'renderCallback is missing a return statement'>;
27
25
  };
@@ -0,0 +1,8 @@
1
+ import { ElementBookEntry } from '../element-book-entry';
2
+ import { ElementBookEntryTypeEnum } from '../element-book-entry-type';
3
+ import { EntryTreeNode } from './entry-tree';
4
+ export declare function createSearchedTree({ entries, startTree, searchQuery, }: {
5
+ entries: ReadonlyArray<ElementBookEntry>;
6
+ startTree: Readonly<EntryTreeNode<ElementBookEntryTypeEnum.Root>>;
7
+ searchQuery: string;
8
+ }): EntryTreeNode;
@@ -0,0 +1,66 @@
1
+ import { filterObject, isTruthy } from '@augment-vir/common';
2
+ import { fuzzySearch } from '../../../utilities/search';
3
+ import { ElementBookEntryTypeEnum } from '../element-book-entry-type';
4
+ import { createEmptyEntryTreeRoot } from './entry-tree';
5
+ import { addTreeToCache, getTreeFromCache } from './tree-cache';
6
+ export function createSearchedTree({ entries, startTree, searchQuery, }) {
7
+ const cachedSearchTree = getTreeFromCache(entries, searchQuery);
8
+ if (cachedSearchTree) {
9
+ return cachedSearchTree;
10
+ }
11
+ const newTree = createEmptyEntryTreeRoot('Search Results');
12
+ recursivelySearchTree(startTree, newTree, searchQuery);
13
+ addTreeToCache(entries, searchQuery, newTree);
14
+ return newTree;
15
+ }
16
+ function recursivelySearchTree(oldTreeNode, newNode, searchQuery) {
17
+ if (oldTreeNode.entry.entryType !== ElementBookEntryTypeEnum.Root &&
18
+ fuzzySearch({
19
+ searchIn: oldTreeNode.entry.title,
20
+ searchQuery: searchQuery,
21
+ })) {
22
+ newNode.children = oldTreeNode.children;
23
+ return true;
24
+ }
25
+ if ('examples' in oldTreeNode.entry) {
26
+ const filteredExamples = filterObject(oldTreeNode.entry.examples, (exampleName) => fuzzySearch({
27
+ searchIn: exampleName,
28
+ searchQuery,
29
+ }));
30
+ const newEntry = {
31
+ ...oldTreeNode.entry,
32
+ examples: filteredExamples,
33
+ };
34
+ newNode.entry = newEntry;
35
+ if (Object.values(newEntry.examples).length) {
36
+ return true;
37
+ }
38
+ else {
39
+ return false;
40
+ }
41
+ }
42
+ const searchedChildEntries = Object.entries(oldTreeNode.children)
43
+ .map(([childName, childNode,]) => {
44
+ const newChildNode = {
45
+ ...childNode,
46
+ children: {},
47
+ };
48
+ if (recursivelySearchTree(childNode, newChildNode, searchQuery)) {
49
+ return [
50
+ childName,
51
+ newChildNode,
52
+ ];
53
+ }
54
+ else {
55
+ return undefined;
56
+ }
57
+ })
58
+ .filter(isTruthy);
59
+ if (searchedChildEntries.length) {
60
+ newNode.children = Object.fromEntries(searchedChildEntries);
61
+ return true;
62
+ }
63
+ else {
64
+ return false;
65
+ }
66
+ }
@@ -2,18 +2,19 @@ import { ElementBookEntry } from '../element-book-entry';
2
2
  import { ElementBookEntryTypeEnum } from '../element-book-entry-type';
3
3
  export declare function doesNodeHaveEntryType<EntryType extends ElementBookEntryTypeEnum>(node: EntryTreeNode<any>, entryType: EntryType): node is EntryTreeNode<EntryType>;
4
4
  declare const markerKeyName = "isElementBookEntryTreeNode";
5
- export type EntryTreeNode<EntryType extends ElementBookEntryTypeEnum = ElementBookEntryTypeEnum> = {
5
+ export type EntryTreeNode<EntryType extends ElementBookEntryTypeEnum = any> = {
6
6
  [markerKeyName]: true;
7
7
  entry: Extract<ElementBookEntry, {
8
8
  entryType: EntryType;
9
9
  }>;
10
+ /** Breadcrumb is different from entry.title because it's modified to support URLs. */
10
11
  breadcrumb: string;
11
12
  children: Record<string, EntryTreeNode>;
12
13
  };
13
14
  export declare function isEntryNode<SpecificType extends ElementBookEntryTypeEnum>(input: unknown, entryType: SpecificType): input is EntryTreeNode<SpecificType>;
14
- export declare function createEmptyEntryTreeRoot(title: string | undefined): EntryTreeNode;
15
+ export declare function createEmptyEntryTreeRoot(title: string | undefined): EntryTreeNode<ElementBookEntryTypeEnum.Root>;
15
16
  export declare function titleToBreadcrumb(title: string): string;
16
- export declare function entriesToTree(entries: ReadonlyArray<ElementBookEntry>, everythingTitle: string | undefined): EntryTreeNode<ElementBookEntryTypeEnum>;
17
+ export declare function entriesToTree(entries: ReadonlyArray<ElementBookEntry>, everythingTitle: string | undefined): EntryTreeNode<ElementBookEntryTypeEnum.Root>;
17
18
  export declare function listBreadcrumbs(entry: ElementBookEntry, includeSelf?: boolean): string[];
18
19
  export declare function findEntryByBreadcrumbs(breadcrumbs: ReadonlyArray<string>, tree: Readonly<EntryTreeNode>): Readonly<EntryTreeNode> | undefined;
19
20
  export {};
@@ -1,5 +1,6 @@
1
1
  import { collapseWhiteSpace, isLengthAtLeast, typedHasProperties } from '@augment-vir/common';
2
2
  import { ElementBookEntryTypeEnum } from '../element-book-entry-type';
3
+ import { addTreeToCache, getTreeFromCache } from './tree-cache';
3
4
  export function doesNodeHaveEntryType(node, entryType) {
4
5
  return node.entry.entryType === entryType;
5
6
  }
@@ -29,6 +30,14 @@ export function titleToBreadcrumb(title) {
29
30
  return collapseWhiteSpace(title).toLowerCase().replaceAll(/\s/g, '-');
30
31
  }
31
32
  export function entriesToTree(entries, everythingTitle) {
33
+ const baseTree = createBaseTree(entries, everythingTitle);
34
+ return baseTree;
35
+ }
36
+ function createBaseTree(entries, everythingTitle) {
37
+ const cachedTree = getTreeFromCache(entries, '');
38
+ if (cachedTree) {
39
+ return cachedTree;
40
+ }
32
41
  const tree = createEmptyEntryTreeRoot(everythingTitle);
33
42
  entries.forEach((newEntry) => {
34
43
  /**
@@ -51,6 +60,7 @@ export function entriesToTree(entries, everythingTitle) {
51
60
  };
52
61
  immediateParent.children[breadcrumb] = newNode;
53
62
  });
63
+ addTreeToCache(entries, '', tree);
54
64
  return tree;
55
65
  }
56
66
  function traverseToImmediateParent(entry, currentTree) {
@@ -0,0 +1,5 @@
1
+ import { ElementBookEntry } from '../element-book-entry';
2
+ import { ElementBookEntryTypeEnum } from '../element-book-entry-type';
3
+ import { EntryTreeNode } from './entry-tree';
4
+ export declare function getTreeFromCache(entries: ReadonlyArray<ElementBookEntry>, searchString: string): EntryTreeNode<ElementBookEntryTypeEnum.Root> | undefined;
5
+ export declare function addTreeToCache(entries: ReadonlyArray<ElementBookEntry>, searchString: string, tree: EntryTreeNode<ElementBookEntryTypeEnum.Root>): void;
@@ -0,0 +1,8 @@
1
+ import { getOrSetFromMap } from '@augment-vir/common';
2
+ const treeCache = new Map();
3
+ export function getTreeFromCache(entries, searchString) {
4
+ return treeCache.get(entries)?.get(searchString);
5
+ }
6
+ export function addTreeToCache(entries, searchString, tree) {
7
+ getOrSetFromMap(treeCache, entries, () => new Map()).set(searchString, tree);
8
+ }
@@ -26,9 +26,15 @@ function sanitizePaths(paths) {
26
26
  ];
27
27
  }
28
28
  else if (firstPath === ElementBookMainRoute.Search) {
29
+ if (!paths[1]) {
30
+ return [
31
+ ElementBookMainRoute.Book,
32
+ ...paths.slice(1),
33
+ ];
34
+ }
29
35
  return [
30
36
  firstPath,
31
- paths[1] ?? '',
37
+ paths[1],
32
38
  ];
33
39
  }
34
40
  else {
@@ -5,5 +5,6 @@ export declare enum ElementBookMainRoute {
5
5
  }
6
6
  export type ValidElementBookPaths = [ElementBookMainRoute.Search, string] | [ElementBookMainRoute.Book, ...string[]];
7
7
  export type ElementBookFullRoute = Required<Readonly<FullRoute<ValidElementBookPaths, undefined | Record<string, string>, undefined>>>;
8
+ export declare function extractSearchQuery(paths: Readonly<ValidElementBookPaths>): string;
8
9
  export declare const defaultElementBookFullRoute: Readonly<ElementBookFullRoute>;
9
10
  export type ElementBookRouter = ElementBookFullRoute extends FullRoute<infer Paths, infer Search, infer Hash> ? Readonly<SpaRouter<Paths, Search, Hash>> : never;
@@ -3,6 +3,14 @@ export var ElementBookMainRoute;
3
3
  ElementBookMainRoute["Search"] = "search";
4
4
  ElementBookMainRoute["Book"] = "book";
5
5
  })(ElementBookMainRoute || (ElementBookMainRoute = {}));
6
+ export function extractSearchQuery(paths) {
7
+ if (paths[0] === ElementBookMainRoute.Book) {
8
+ return '';
9
+ }
10
+ else {
11
+ return decodeURIComponent(paths[1]);
12
+ }
13
+ }
6
14
  export const defaultElementBookFullRoute = {
7
15
  hash: undefined,
8
16
  paths: [ElementBookMainRoute.Book],
@@ -1,14 +1,15 @@
1
- import { areJsonEqual, extractErrorMessage, getOrSetFromMap } from '@augment-vir/common';
1
+ import { areJsonEqual, extractErrorMessage } from '@augment-vir/common';
2
2
  import { assign, css, defineElement, defineElementEvent, html, listen } from 'element-vir';
3
- import { entriesToTree, findEntryByBreadcrumbs, } from '../../../data/element-book-entry/entry-tree/entry-tree';
3
+ import { entriesToTree } from '../../../data/element-book-entry/entry-tree/entry-tree';
4
+ import { createSearchedTree } from '../../../data/element-book-entry/entry-tree/entry-tree-search';
4
5
  import { createElementBookRouter } from '../../../routing/create-element-book-router';
5
- import { ElementBookMainRoute, defaultElementBookFullRoute, } from '../../../routing/element-book-routing';
6
+ import { defaultElementBookFullRoute, extractSearchQuery, } from '../../../routing/element-book-routing';
6
7
  import { colorThemeCssVars, setThemeCssVars } from '../../color-theme/color-theme';
7
8
  import { createTheme } from '../../color-theme/create-color-theme';
8
9
  import { ChangeRouteEvent } from '../../events/change-route.event';
9
10
  import { ElementBookNav } from '../element-book-nav.element';
10
11
  import { ElementBookEntryDisplay } from '../entry-display/element-book-entry-display.element';
11
- const treeCache = new Map();
12
+ import { getCurrentTreeEntry } from './get-current-entry';
12
13
  export const ElementBookApp = defineElement()({
13
14
  tagName: 'element-book-app',
14
15
  events: {
@@ -113,21 +114,16 @@ export const ElementBookApp = defineElement()({
113
114
  });
114
115
  setThemeCssVars(host, newTheme);
115
116
  }
116
- const entriesTree = getOrSetFromMap(treeCache, inputs.entries, () => entriesToTree(inputs.entries, inputs.everythingTitle));
117
- const entryTreeNodeByInitialPath = findEntryByBreadcrumbs(state.currentRoute.paths.slice(1), entriesTree);
118
- if (!entryTreeNodeByInitialPath) {
119
- const newRoute = {
120
- paths: [
121
- ElementBookMainRoute.Book,
122
- ],
123
- };
124
- updateRoutes(newRoute);
125
- }
126
- const currentEntryTreeNode = findEntryByBreadcrumbs(state.currentRoute.paths.slice(1), entriesTree);
127
- if (!currentEntryTreeNode) {
128
- throw new Error(`Tried to self-correct for invalid path ${state.currentRoute.paths.join('/')}
129
- but failed to do so.`);
130
- }
117
+ const originalTree = entriesToTree(inputs.entries, inputs.everythingTitle);
118
+ const searchQuery = extractSearchQuery(state.currentRoute.paths);
119
+ const searchedTree = searchQuery
120
+ ? createSearchedTree({
121
+ entries: inputs.entries,
122
+ searchQuery,
123
+ startTree: originalTree,
124
+ })
125
+ : originalTree;
126
+ const currentEntryTreeNode = getCurrentTreeEntry(searchedTree, state.currentRoute.paths, updateRoutes);
131
127
  return html `
132
128
  <div
133
129
  class="root"
@@ -144,7 +140,7 @@ export const ElementBookApp = defineElement()({
144
140
  >
145
141
  <${ElementBookNav}
146
142
  ${assign(ElementBookNav, {
147
- tree: entriesTree,
143
+ tree: originalTree,
148
144
  router: state.router,
149
145
  selectedPath: state.currentRoute.paths,
150
146
  })}
@@ -0,0 +1,4 @@
1
+ import { ElementBookEntryTypeEnum } from '../../../data/element-book-entry/element-book-entry-type';
2
+ import { EntryTreeNode } from '../../../data/element-book-entry/entry-tree/entry-tree';
3
+ import { ElementBookFullRoute, ValidElementBookPaths } from '../../../routing/element-book-routing';
4
+ export declare function getCurrentTreeEntry(entriesTree: EntryTreeNode<ElementBookEntryTypeEnum.Root>, paths: Readonly<ValidElementBookPaths>, updateRoutes: (newRoute: Partial<ElementBookFullRoute>) => void): EntryTreeNode;
@@ -0,0 +1,17 @@
1
+ import { findEntryByBreadcrumbs, } from '../../../data/element-book-entry/entry-tree/entry-tree';
2
+ import { ElementBookMainRoute, defaultElementBookFullRoute, } from '../../../routing/element-book-routing';
3
+ export function getCurrentTreeEntry(entriesTree, paths, updateRoutes) {
4
+ if (paths[0] === ElementBookMainRoute.Search) {
5
+ return entriesTree;
6
+ }
7
+ const entryTreeNodeByInitialPath = findEntryByBreadcrumbs(paths.slice(1), entriesTree);
8
+ if (!entryTreeNodeByInitialPath) {
9
+ updateRoutes(defaultElementBookFullRoute);
10
+ }
11
+ const currentEntryTreeNode = findEntryByBreadcrumbs(paths.slice(1), entriesTree);
12
+ if (!currentEntryTreeNode) {
13
+ throw new Error(`Tried to self-correct for invalid path ${paths.join('/')}
14
+ but failed to do so.`);
15
+ }
16
+ return currentEntryTreeNode;
17
+ }
@@ -1,10 +1,12 @@
1
+ import { wait } from '@augment-vir/common';
1
2
  import { VirIcon } from '@electrovir/icon-element';
2
- import { assign, css, html } from 'element-vir';
3
+ import { assign, css, html, listen, renderIf } from 'element-vir';
3
4
  import { isElementBookEntry } from '../../../data/element-book-entry/element-book-entry';
4
5
  import { ElementBookEntryTypeEnum } from '../../../data/element-book-entry/element-book-entry-type';
5
6
  import { isEntryNode, listBreadcrumbs, } from '../../../data/element-book-entry/entry-tree/entry-tree';
6
- import { ElementBookMainRoute, } from '../../../routing/element-book-routing';
7
+ import { ElementBookMainRoute, extractSearchQuery, } from '../../../routing/element-book-routing';
7
8
  import { colorThemeCssVars } from '../../color-theme/color-theme';
9
+ import { ChangeRouteEvent } from '../../events/change-route.event';
8
10
  import { Element24Icon } from '../../icons/element-24.icon';
9
11
  import { ElementBookRouteLink } from '../common/element-book-route-link.element';
10
12
  import { defineElementBookElement } from '../define-book-element';
@@ -26,6 +28,9 @@ export const ElementBookEntryDisplay = defineElementBookElement()({
26
28
  padding: 4px 8px;
27
29
  background-color: ${colorThemeCssVars['element-book-page-background-color'].value};
28
30
  z-index: 9999999999;
31
+ display: flex;
32
+ gap: 16px;
33
+ justify-content: space-between;
29
34
  }
30
35
 
31
36
  .all-examples-wrapper {
@@ -88,29 +93,64 @@ export const ElementBookEntryDisplay = defineElementBookElement()({
88
93
  margin-top: 8px;
89
94
  }
90
95
  `,
91
- renderCallback: ({ inputs }) => {
96
+ renderCallback: ({ inputs, dispatch }) => {
92
97
  const nestedPages = extractNestedPages(inputs.currentNode);
98
+ const currentSearch = extractSearchQuery(inputs.currentRoute.paths);
93
99
  const entryBreadcrumbs = listBreadcrumbs(inputs.currentNode.entry, false).reverse();
94
100
  const exampleTemplates = createNestedPagesTemplates({
95
101
  nestedPages,
96
102
  parentBreadcrumbs: entryBreadcrumbs,
97
103
  isTopLevel: true,
98
104
  router: inputs.router,
105
+ isSearching: !!currentSearch,
99
106
  });
100
107
  return html `
101
108
  <div class="title-bar">
102
- <${ElementBookBreadcrumbs}
103
- ${assign(ElementBookBreadcrumbs, {
109
+ ${renderIf(!!currentSearch, html `
110
+ &nbsp;
111
+ `, html `
112
+ <${ElementBookBreadcrumbs}
113
+ ${assign(ElementBookBreadcrumbs, {
104
114
  currentRoute: inputs.currentRoute,
105
115
  router: inputs.router,
106
116
  })}
107
- ></${ElementBookBreadcrumbs}>
117
+ ></${ElementBookBreadcrumbs}>
118
+ `)}
119
+ <input
120
+ placeholder="search"
121
+ .value=${currentSearch}
122
+ ${listen('input', async (event) => {
123
+ const inputElement = event.currentTarget;
124
+ if (!(inputElement instanceof HTMLInputElement)) {
125
+ throw new Error('Failed to find input element for search.');
126
+ }
127
+ const preThrottleValue = inputElement.value;
128
+ // throttle it a bit
129
+ await wait(500);
130
+ if (inputElement.value !== preThrottleValue) {
131
+ return;
132
+ }
133
+ dispatch(new ChangeRouteEvent({
134
+ paths: [
135
+ ElementBookMainRoute.Search,
136
+ encodeURIComponent(inputElement.value),
137
+ ],
138
+ }));
139
+ })}
140
+ />
108
141
  </div>
109
142
  <div class="all-examples-wrapper">${exampleTemplates}</div>
110
143
  `;
111
144
  },
112
145
  });
113
- function createNestedPagesTemplates({ nestedPages, parentBreadcrumbs, isTopLevel, router, }) {
146
+ function createNestedPagesTemplates({ nestedPages, parentBreadcrumbs, isTopLevel, router, isSearching, }) {
147
+ if (!nestedPages.length && isSearching) {
148
+ return [
149
+ html `
150
+ No results
151
+ `,
152
+ ];
153
+ }
114
154
  return nestedPages
115
155
  .map((nestingNode) => {
116
156
  if (isEntryNode(nestingNode, ElementBookEntryTypeEnum.Page)) {
@@ -196,6 +236,7 @@ function createNestedPagesTemplates({ nestedPages, parentBreadcrumbs, isTopLevel
196
236
  : parentBreadcrumbs,
197
237
  isTopLevel: false,
198
238
  router,
239
+ isSearching,
199
240
  })}
200
241
  `;
201
242
  });
@@ -16,10 +16,9 @@ export const ElementBookExampleControls = defineElementBookElement()({
16
16
  }
17
17
  `,
18
18
  renderCallback({ inputs }) {
19
- const title = inputs.example.hideExampleControls ? '' : inputs.example.title;
20
19
  return html `
21
20
  <span>
22
- ${title || defaultTitle}
21
+ ${inputs.example.title}
23
22
  <span></span>
24
23
  </span>
25
24
  `;
@@ -1,5 +1,5 @@
1
1
  import { mapObjectValues } from '@augment-vir/common';
2
- import { assign, classMap, css, html, listen, renderIf, repeat } from 'element-vir';
2
+ import { assign, css, html, listen, repeat } from 'element-vir';
3
3
  import { unsetInternalState } from '../../../data/unset';
4
4
  import { colorThemeCssVars } from '../../color-theme/color-theme';
5
5
  import { defineElementBookElement } from '../define-book-element';
@@ -36,11 +36,6 @@ export const ElementBookPageExamples = defineElementBookElement()({
36
36
  .individual-example-wrapper:hover ${ElementBookExampleControls} {
37
37
  color: ${colorThemeCssVars['element-book-accent-icon-color'].value};
38
38
  }
39
-
40
- .hidden-controls {
41
- pointer-events: none;
42
- visibility: hidden;
43
- }
44
39
  `,
45
40
  stateInit: {
46
41
  unset: unsetInternalState,
@@ -56,7 +51,6 @@ export const ElementBookPageExamples = defineElementBookElement()({
56
51
  });
57
52
  }
58
53
  const examples = inputs.page.examples;
59
- const allControlsHidden = Object.values(examples).every((example) => 'hideExampleControls' in example && example.hideExampleControls);
60
54
  /**
61
55
  * Use the repeat directive here, instead of just a map, so that lit doesn't accidentally
62
56
  * keep state cached between element book pages.
@@ -72,21 +66,11 @@ export const ElementBookPageExamples = defineElementBookElement()({
72
66
  const exampleBreadcrumbs = inputs.parentBreadcrumbs.concat(example.title);
73
67
  return html `
74
68
  <div class="individual-example-wrapper">
75
- ${renderIf(!allControlsHidden, html `
76
- <${ElementBookExampleControls}
77
- class=${classMap({
78
- /**
79
- * If not all controls are hidden, we still want to render
80
- * every control so that they take up space, but just hide
81
- * them.
82
- */
83
- 'hidden-controls': !!example.hideExampleControls,
84
- })}
85
- ${assign(ElementBookExampleControls, {
69
+ <${ElementBookExampleControls}
70
+ ${assign(ElementBookExampleControls, {
86
71
  example,
87
72
  })}
88
- ></${ElementBookExampleControls}>
89
- `)}
73
+ ></${ElementBookExampleControls}>
90
74
  <${ElementBookExampleViewer}
91
75
  ${assign(ElementBookExampleViewer, {
92
76
  example,
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Code adapted from
3
+ * https://github.com/bevacqua/fuzzysearch/blob/9873ea0546a47f5d96e037ff02b78b871c809d62/index.js
4
+ *
5
+ * That code has the following license:
6
+ */
7
+ /**
8
+ * The MIT License (MIT)
9
+ *
10
+ * Copyright © 2015 Nicolas Bevacqua
11
+ *
12
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
13
+ * associated documentation files (the "Software"), to deal in the Software without restriction,
14
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute,
15
+ * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
16
+ * furnished to do so, subject to the following conditions:
17
+ *
18
+ * The above copyright notice and this permission notice shall be included in all copies or
19
+ * substantial portions of the Software.
20
+ *
21
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
22
+ * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
24
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
+ */
27
+ export declare function fuzzySearch({
28
+ /** What you want to search for; the user search input. */
29
+ searchQuery,
30
+ /** What we're searching in. See if the searchQuery is found within this. */
31
+ searchIn, }: {
32
+ searchQuery: string;
33
+ searchIn: string;
34
+ }): boolean;
@@ -0,0 +1,54 @@
1
+ // cspell:disable
2
+ /**
3
+ * Code adapted from
4
+ * https://github.com/bevacqua/fuzzysearch/blob/9873ea0546a47f5d96e037ff02b78b871c809d62/index.js
5
+ *
6
+ * That code has the following license:
7
+ */
8
+ /**
9
+ * The MIT License (MIT)
10
+ *
11
+ * Copyright © 2015 Nicolas Bevacqua
12
+ *
13
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
14
+ * associated documentation files (the "Software"), to deal in the Software without restriction,
15
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute,
16
+ * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
17
+ * furnished to do so, subject to the following conditions:
18
+ *
19
+ * The above copyright notice and this permission notice shall be included in all copies or
20
+ * substantial portions of the Software.
21
+ *
22
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
23
+ * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
25
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
+ */
28
+ // cspell:enable
29
+ export function fuzzySearch({
30
+ /** What you want to search for; the user search input. */
31
+ searchQuery,
32
+ /** What we're searching in. See if the searchQuery is found within this. */
33
+ searchIn, }) {
34
+ const searchInLength = searchIn.length;
35
+ const searchQueryLength = searchQuery.length;
36
+ if (searchQueryLength > searchInLength) {
37
+ return false;
38
+ }
39
+ if (searchQueryLength === searchInLength) {
40
+ return searchQuery === searchIn;
41
+ }
42
+ const lowercaseSearchIn = searchIn.toLowerCase();
43
+ const lowercaseSearchQuery = searchQuery.toLowerCase();
44
+ outer: for (let i = 0, j = 0; i < searchQueryLength; i++) {
45
+ const charCode = lowercaseSearchQuery.charCodeAt(i);
46
+ while (j < searchInLength) {
47
+ if (lowercaseSearchIn.charCodeAt(j++) === charCode) {
48
+ continue outer;
49
+ }
50
+ }
51
+ return false;
52
+ }
53
+ return true;
54
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "element-book",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "keywords": [
5
5
  "book",
6
6
  "design system",
File without changes
File without changes