element-book 1.0.3 → 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.
- package/dist/data/element-book-entry/element-book-chapter/element-book-chapter.d.ts +3 -3
- package/dist/data/element-book-entry/element-book-chapter/element-book-chapter.js +1 -1
- package/dist/data/element-book-entry/element-book-entry.d.ts +3 -3
- package/dist/data/element-book-entry/element-book-entry.js +2 -2
- package/dist/data/element-book-entry/element-book-page/element-book-page-controls/element-book-page-control-type.d.ts +13 -0
- package/dist/data/element-book-entry/element-book-page/element-book-page-controls/element-book-page-control-type.js +7 -0
- package/dist/data/element-book-entry/element-book-page/element-book-page-controls/element-book-page-control.d.ts +13 -0
- package/dist/data/element-book-entry/element-book-page/element-book-page-controls/element-book-page-control.js +4 -0
- package/dist/data/element-book-entry/element-book-page/element-book-page-example.d.ts +13 -5
- package/dist/data/element-book-entry/element-book-page/element-book-page-example.js +19 -2
- package/dist/data/element-book-entry/element-book-page/element-book-page.d.ts +11 -5
- package/dist/data/element-book-entry/element-book-page/element-book-page.js +5 -27
- package/dist/data/element-book-entry/entry-tree/entry-tree-search.d.ts +8 -0
- package/dist/data/element-book-entry/entry-tree/entry-tree-search.js +66 -0
- package/dist/data/element-book-entry/entry-tree/entry-tree.d.ts +7 -6
- package/dist/data/element-book-entry/entry-tree/entry-tree.js +15 -5
- package/dist/data/element-book-entry/entry-tree/tree-cache.d.ts +5 -0
- package/dist/data/element-book-entry/entry-tree/tree-cache.js +8 -0
- package/dist/data/unset.d.ts +1 -0
- package/dist/data/unset.js +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/routing/create-element-book-router.js +8 -2
- package/dist/routing/element-book-routing.d.ts +3 -2
- package/dist/routing/element-book-routing.js +8 -0
- package/dist/ui/elements/element-book-app/element-book-app.element.d.ts +2 -2
- package/dist/ui/elements/element-book-app/element-book-app.element.js +16 -20
- package/dist/ui/elements/element-book-app/get-current-entry.d.ts +4 -0
- package/dist/ui/elements/element-book-app/get-current-entry.js +17 -0
- package/dist/ui/elements/element-book-nav.element.js +2 -2
- package/dist/ui/elements/entry-display/element-book-entry-display.element.js +49 -8
- package/dist/ui/elements/entry-display/element-book-example-controls.element.d.ts +2 -2
- package/dist/ui/elements/entry-display/element-book-example-controls.element.js +1 -2
- package/dist/ui/elements/entry-display/element-book-example-viewer.element.d.ts +3 -2
- package/dist/ui/elements/entry-display/element-book-example-viewer.element.js +6 -2
- package/dist/ui/elements/entry-display/element-book-page-controls.element.d.ts +11 -0
- package/dist/ui/elements/entry-display/element-book-page-controls.element.js +65 -0
- package/dist/ui/elements/entry-display/element-book-page-examples.element.d.ts +3 -1
- package/dist/ui/elements/entry-display/element-book-page-examples.element.js +55 -22
- package/dist/ui/events/change-route.event.d.ts +1 -1
- package/dist/ui/events/change-route.event.js +1 -1
- package/dist/utilities/search.d.ts +34 -0
- package/dist/utilities/search.js +54 -0
- package/package.json +1 -1
- /package/dist/{augments → utilities}/type.d.ts +0 -0
- /package/dist/{augments → utilities}/type.js +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Overwrite } from '@augment-vir/common';
|
|
2
2
|
import { ElementBookEntryTypeEnum } from '../element-book-entry-type';
|
|
3
3
|
export type ElementBookChapter = Overwrite<BaseElementBookEntry, {
|
|
4
|
-
|
|
4
|
+
entryType: ElementBookEntryTypeEnum.Chapter;
|
|
5
5
|
}>;
|
|
6
6
|
/** This is in the ElementBookChapter file so that they don't circularly depend on each other. */
|
|
7
7
|
export type BaseElementBookEntry = {
|
|
8
8
|
/** Display name for the chapter or page. This is also used to create breadcrumbs and URL paths. */
|
|
9
9
|
title: string;
|
|
10
|
-
|
|
10
|
+
entryType: ElementBookEntryTypeEnum;
|
|
11
11
|
/**
|
|
12
12
|
* The parent chapter. A value of undefined here indicates that the chapter or page should be at
|
|
13
13
|
* the top level.
|
|
@@ -19,4 +19,4 @@ export type BaseElementBookEntry = {
|
|
|
19
19
|
*/
|
|
20
20
|
descriptionParagraphs?: ReadonlyArray<string> | undefined;
|
|
21
21
|
};
|
|
22
|
-
export declare function defineElementBookChapter(chapterSetup: Omit<ElementBookChapter, '
|
|
22
|
+
export declare function defineElementBookChapter(chapterSetup: Omit<ElementBookChapter, 'entryType'>): ElementBookChapter;
|
|
@@ -9,7 +9,7 @@ export function defineElementBookChapter(chapterSetup) {
|
|
|
9
9
|
return new Error(`Cannot have an element-book chapter with an empty title.`);
|
|
10
10
|
}
|
|
11
11
|
const chapter = {
|
|
12
|
-
|
|
12
|
+
entryType: ElementBookEntryTypeEnum.Chapter,
|
|
13
13
|
...chapterSetup,
|
|
14
14
|
};
|
|
15
15
|
return chapter;
|
|
@@ -2,11 +2,11 @@ import { ElementBookChapter } from './element-book-chapter/element-book-chapter'
|
|
|
2
2
|
import { ElementBookEntryTypeEnum } from './element-book-entry-type';
|
|
3
3
|
import { ElementBookPage } from './element-book-page/element-book-page';
|
|
4
4
|
export type ElementBookRoot = {
|
|
5
|
-
|
|
5
|
+
entryType: ElementBookEntryTypeEnum.Root;
|
|
6
6
|
title: string;
|
|
7
7
|
parent: undefined;
|
|
8
8
|
};
|
|
9
9
|
export type ElementBookEntry = ElementBookChapter | ElementBookPage | ElementBookRoot;
|
|
10
|
-
export declare function isElementBookEntry<SpecificType extends ElementBookEntryTypeEnum>(entry: unknown,
|
|
11
|
-
|
|
10
|
+
export declare function isElementBookEntry<SpecificType extends ElementBookEntryTypeEnum>(entry: unknown, entryType: SpecificType): entry is Extract<ElementBookEntry, {
|
|
11
|
+
entryType: SpecificType;
|
|
12
12
|
}>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { typedHasProperty } from '@augment-vir/common';
|
|
2
|
-
export function isElementBookEntry(entry,
|
|
3
|
-
return typedHasProperty(entry, '
|
|
2
|
+
export function isElementBookEntry(entry, entryType) {
|
|
3
|
+
return typedHasProperty(entry, 'entryType') && entry.entryType === entryType;
|
|
4
4
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare enum ElementBookPageControlTypeEnum {
|
|
2
|
+
Checkbox = "checkbox",
|
|
3
|
+
Color = "color",
|
|
4
|
+
Dropdown = "dropdown",
|
|
5
|
+
Text = "text"
|
|
6
|
+
}
|
|
7
|
+
export type ElementBookPageControlValueType = {
|
|
8
|
+
[ElementBookPageControlTypeEnum.Checkbox]: boolean;
|
|
9
|
+
[ElementBookPageControlTypeEnum.Color]: string;
|
|
10
|
+
/** Value type corresponds to which option in the dropdown is selected. */
|
|
11
|
+
[ElementBookPageControlTypeEnum.Dropdown]: string;
|
|
12
|
+
[ElementBookPageControlTypeEnum.Text]: string;
|
|
13
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export var ElementBookPageControlTypeEnum;
|
|
2
|
+
(function (ElementBookPageControlTypeEnum) {
|
|
3
|
+
ElementBookPageControlTypeEnum["Checkbox"] = "checkbox";
|
|
4
|
+
ElementBookPageControlTypeEnum["Color"] = "color";
|
|
5
|
+
ElementBookPageControlTypeEnum["Dropdown"] = "dropdown";
|
|
6
|
+
ElementBookPageControlTypeEnum["Text"] = "text";
|
|
7
|
+
})(ElementBookPageControlTypeEnum || (ElementBookPageControlTypeEnum = {}));
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ElementBookPageControlTypeEnum, ElementBookPageControlValueType } from './element-book-page-control-type';
|
|
2
|
+
export type ElementBookPageControl<ControlType extends ElementBookPageControlTypeEnum> = {
|
|
3
|
+
controlType: ControlType;
|
|
4
|
+
initValue: ElementBookPageControlValueType[ControlType];
|
|
5
|
+
controlName: string;
|
|
6
|
+
};
|
|
7
|
+
export type ElementBookPageControlInit<ControlType extends ElementBookPageControlTypeEnum> = Omit<ElementBookPageControl<ControlType>, 'controlName'>;
|
|
8
|
+
/** Define a page control. This doesn't do anything fancy but it helps immensely with type inference. */
|
|
9
|
+
export declare function definePageControl<ControlType extends ElementBookPageControlTypeEnum>(controlInit: ElementBookPageControlInit<ControlType>): ElementBookPageControlInit<ControlType>;
|
|
10
|
+
export type ElementBookPageControlMap = Record<string, ElementBookPageControlInit<ElementBookPageControlTypeEnum>>;
|
|
11
|
+
export type PageControlsToValues<Controls extends ElementBookPageControlMap> = {
|
|
12
|
+
[ControlName in keyof Controls]: Controls[ControlName]['initValue'];
|
|
13
|
+
};
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
+
import { RequireNonVoid } from '@augment-vir/common';
|
|
1
2
|
import { PropertyInitMapBase, RenderParams, TypedEvent } from 'element-vir';
|
|
2
3
|
import { CSSResult } from 'lit';
|
|
3
|
-
|
|
4
|
+
import { ElementBookPage, PageControlsFromPage } from './element-book-page';
|
|
5
|
+
import { PageControlsToValues } from './element-book-page-controls/element-book-page-control';
|
|
6
|
+
export type ElementBookPageExampleRenderParams<ParentPage extends ElementBookPage<any>, StateInit extends PropertyInitMapBase> = Pick<RenderParams<any, any, StateInit, any, any, any>, 'state' | 'updateState'> & {
|
|
7
|
+
controls: PageControlsToValues<PageControlsFromPage<ParentPage>>;
|
|
8
|
+
};
|
|
9
|
+
export type ElementBookPageExampleInit<ParentPage extends ElementBookPage<any>, StateInit extends PropertyInitMapBase, RenderOutput> = {
|
|
10
|
+
/** This example's title. Each title in a page must be unique. */
|
|
4
11
|
title: string;
|
|
12
|
+
/** The page that this example belongs to. */
|
|
13
|
+
parent: ParentPage;
|
|
5
14
|
/** Initialize the state for this example. */
|
|
6
15
|
stateInit?: StateInit;
|
|
7
16
|
/** Specify which events this example should intercept (so the user can see them). */
|
|
@@ -11,9 +20,8 @@ export type ElementBookPageExample<StateInit extends PropertyInitMapBase = {}> =
|
|
|
11
20
|
* wrapper element!
|
|
12
21
|
*/
|
|
13
22
|
styles?: CSSResult;
|
|
14
|
-
/** Set to true to hide the example controls (example title and buttons). */
|
|
15
|
-
hideControls?: boolean | undefined;
|
|
16
23
|
/** Render the example. */
|
|
17
|
-
|
|
24
|
+
renderCallback: RequireNonVoid<RenderOutput, (renderParams: ElementBookPageExampleRenderParams<ParentPage, StateInit>) => RenderOutput, 'renderCallback is missing a return statement'>;
|
|
18
25
|
};
|
|
19
|
-
|
|
26
|
+
/** Inserts the defined element example into its parent page. */
|
|
27
|
+
export declare function insertElementExample<ParentPage extends ElementBookPage<any>, StateInit extends PropertyInitMapBase = {}, RenderOutput = any>(exampleInit: ElementBookPageExampleInit<ParentPage, StateInit, RenderOutput>): void;
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { combineErrors } from '@augment-vir/common';
|
|
2
|
+
/** Inserts the defined element example into its parent page. */
|
|
3
|
+
export function insertElementExample(exampleInit) {
|
|
4
|
+
const errors = [];
|
|
5
|
+
const failureMessage = `Failed to define example '${exampleInit.parent.pageBreadcrumbs
|
|
6
|
+
.concat(exampleInit.title)
|
|
7
|
+
.join(' > ')}'`;
|
|
8
|
+
if (exampleInit.parent.examples.hasOwnProperty(exampleInit.title)) {
|
|
9
|
+
errors.push(new Error(`${failureMessage}: title '${exampleInit.title}' is already being used.`));
|
|
10
|
+
}
|
|
11
|
+
else if (!exampleInit.title) {
|
|
12
|
+
errors.push(new Error(`${failureMessage}: example title is missing or empty.`));
|
|
13
|
+
}
|
|
14
|
+
if (errors.length) {
|
|
15
|
+
exampleInit.parent.examples[exampleInit.title] = combineErrors(errors);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
exampleInit.parent.examples[exampleInit.title] = exampleInit;
|
|
19
|
+
}
|
|
3
20
|
}
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { Overwrite } from '@augment-vir/common';
|
|
2
|
+
import { SetOptional } from 'type-fest';
|
|
2
3
|
import { BaseElementBookEntry } from '../element-book-chapter/element-book-chapter';
|
|
3
4
|
import { ElementBookEntryTypeEnum } from '../element-book-entry-type';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
import { ElementBookPageControlMap } from './element-book-page-controls/element-book-page-control';
|
|
6
|
+
import { ElementBookPageExampleInit } from './element-book-page-example';
|
|
7
|
+
export type ElementBookPage<Controls extends ElementBookPageControlMap = ElementBookPageControlMap> = Overwrite<BaseElementBookEntry, {
|
|
8
|
+
entryType: ElementBookEntryTypeEnum.Page;
|
|
7
9
|
}> & {
|
|
8
|
-
|
|
10
|
+
controls: Controls;
|
|
11
|
+
examples: Record<string, ElementBookPageExampleInit<any, any, any> | Error>;
|
|
12
|
+
pageBreadcrumbs: ReadonlyArray<string>;
|
|
9
13
|
};
|
|
10
|
-
export
|
|
14
|
+
export type PageControlsFromPage<Page extends ElementBookPage<any>> = Page extends ElementBookPage<infer Controls> ? Controls : never;
|
|
15
|
+
export type ElementBookPageInit<Controls extends ElementBookPageControlMap> = SetOptional<Omit<ElementBookPage<Controls>, 'entryType' | 'examples' | 'allExampleTitles' | 'pageBreadcrumbs'>, 'controls'>;
|
|
16
|
+
export declare function defineElementBookPage<Controls extends ElementBookPageControlMap = {}>(pageSetup: ElementBookPageInit<Controls>): ElementBookPage<Controls>;
|
|
@@ -1,36 +1,14 @@
|
|
|
1
|
-
import { combineErrors } from '@augment-vir/common';
|
|
2
1
|
import { ElementBookEntryTypeEnum } from '../element-book-entry-type';
|
|
3
|
-
import { listBreadcrumbs } from '../entry-tree/entry-tree';
|
|
4
2
|
export function defineElementBookPage(pageSetup) {
|
|
5
|
-
const errors = [];
|
|
6
3
|
if (!pageSetup.title) {
|
|
7
|
-
|
|
4
|
+
throw new Error(`Cannot have an element-book page with an empty title.`);
|
|
8
5
|
}
|
|
9
6
|
const page = {
|
|
10
|
-
|
|
7
|
+
entryType: ElementBookEntryTypeEnum.Page,
|
|
11
8
|
...pageSetup,
|
|
9
|
+
examples: {},
|
|
10
|
+
controls: pageSetup.controls ?? {},
|
|
11
|
+
pageBreadcrumbs: [],
|
|
12
12
|
};
|
|
13
|
-
const pageBreadcrumbs = listBreadcrumbs(page, true);
|
|
14
|
-
const exampleTitlesSet = new Set();
|
|
15
|
-
pageSetup.examples.forEach((example) => {
|
|
16
|
-
const failureMessage = `Failed to define example '${pageBreadcrumbs
|
|
17
|
-
.concat(example.title)
|
|
18
|
-
.join(' > ')}'`;
|
|
19
|
-
if (exampleTitlesSet.has(example.title)) {
|
|
20
|
-
errors.push(Error(`${failureMessage}: title '${example.title}' is already being used.`));
|
|
21
|
-
}
|
|
22
|
-
else if (!example.title) {
|
|
23
|
-
errors.push(Error(`${failureMessage}: example title is missing or empty.`));
|
|
24
|
-
}
|
|
25
|
-
exampleTitlesSet.add(example.title);
|
|
26
|
-
});
|
|
27
|
-
if (errors.length) {
|
|
28
|
-
/**
|
|
29
|
-
* We don't want the Error type to actually be a part of this function's return type, cause
|
|
30
|
-
* users shouldn't actually return errors, but we still want to pass errors to element-book
|
|
31
|
-
* so element-book can handle them.
|
|
32
|
-
*/
|
|
33
|
-
return combineErrors(errors);
|
|
34
|
-
}
|
|
35
13
|
return page;
|
|
36
14
|
}
|
|
@@ -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
|
+
}
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { ElementBookEntry } from '../element-book-entry';
|
|
2
2
|
import { ElementBookEntryTypeEnum } from '../element-book-entry-type';
|
|
3
|
-
export declare function doesNodeHaveEntryType<EntryType extends ElementBookEntryTypeEnum>(node: EntryTreeNode<any>,
|
|
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 =
|
|
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
|
-
export declare function isEntryNode<SpecificType extends ElementBookEntryTypeEnum>(input: unknown,
|
|
14
|
-
export declare function createEmptyEntryTreeRoot(title: string | undefined): EntryTreeNode
|
|
14
|
+
export declare function isEntryNode<SpecificType extends ElementBookEntryTypeEnum>(input: unknown, entryType: SpecificType): input is EntryTreeNode<SpecificType>;
|
|
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,22 +1,23 @@
|
|
|
1
1
|
import { collapseWhiteSpace, isLengthAtLeast, typedHasProperties } from '@augment-vir/common';
|
|
2
2
|
import { ElementBookEntryTypeEnum } from '../element-book-entry-type';
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import { addTreeToCache, getTreeFromCache } from './tree-cache';
|
|
4
|
+
export function doesNodeHaveEntryType(node, entryType) {
|
|
5
|
+
return node.entry.entryType === entryType;
|
|
5
6
|
}
|
|
6
7
|
const markerKeyName = 'isElementBookEntryTreeNode';
|
|
7
|
-
export function isEntryNode(input,
|
|
8
|
+
export function isEntryNode(input, entryType) {
|
|
8
9
|
return !!(typedHasProperties(input, [
|
|
9
10
|
markerKeyName,
|
|
10
11
|
'entry',
|
|
11
12
|
]) &&
|
|
12
13
|
input[markerKeyName] &&
|
|
13
|
-
input.entry.
|
|
14
|
+
input.entry.entryType === entryType);
|
|
14
15
|
}
|
|
15
16
|
export function createEmptyEntryTreeRoot(title) {
|
|
16
17
|
const rootNode = {
|
|
17
18
|
[markerKeyName]: true,
|
|
18
19
|
entry: {
|
|
19
|
-
|
|
20
|
+
entryType: ElementBookEntryTypeEnum.Root,
|
|
20
21
|
title: title || 'Everything',
|
|
21
22
|
parent: undefined,
|
|
22
23
|
},
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const unsetInternalState: unique symbol;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const unsetInternalState = Symbol('unset-internal-state');
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export * from './data/element-book-entry/element-book-chapter/element-book-chapter';
|
|
2
2
|
export * from './data/element-book-entry/element-book-entry';
|
|
3
3
|
export * from './data/element-book-entry/element-book-page/element-book-page';
|
|
4
|
+
export * from './data/element-book-entry/element-book-page/element-book-page-controls/element-book-page-control';
|
|
5
|
+
export * from './data/element-book-entry/element-book-page/element-book-page-controls/element-book-page-control-type';
|
|
4
6
|
export * from './data/element-book-entry/element-book-page/element-book-page-example';
|
|
5
7
|
export * from './ui/elements/element-book-app/element-book-app.element';
|
|
6
8
|
export * from './ui/elements/element-book-app/element-book-config';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export * from './data/element-book-entry/element-book-chapter/element-book-chapter';
|
|
2
2
|
export * from './data/element-book-entry/element-book-entry';
|
|
3
3
|
export * from './data/element-book-entry/element-book-page/element-book-page';
|
|
4
|
+
export * from './data/element-book-entry/element-book-page/element-book-page-controls/element-book-page-control';
|
|
5
|
+
export * from './data/element-book-entry/element-book-page/element-book-page-controls/element-book-page-control-type';
|
|
4
6
|
export * from './data/element-book-entry/element-book-page/element-book-page-example';
|
|
5
7
|
export * from './ui/elements/element-book-app/element-book-app.element';
|
|
6
8
|
export * from './ui/elements/element-book-app/element-book-config';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isEnumValue } from '@augment-vir/common';
|
|
2
2
|
import { createSpaRouter } from 'spa-router-vir';
|
|
3
|
-
import {
|
|
3
|
+
import { defaultElementBookFullRoute, ElementBookMainRoute, } from './element-book-routing';
|
|
4
4
|
export function createElementBookRouter(baseRoute) {
|
|
5
5
|
return createSpaRouter({
|
|
6
6
|
routeBase: baseRoute,
|
|
@@ -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 {
|
|
@@ -4,6 +4,7 @@ export declare enum ElementBookMainRoute {
|
|
|
4
4
|
Book = "book"
|
|
5
5
|
}
|
|
6
6
|
export type ValidElementBookPaths = [ElementBookMainRoute.Search, string] | [ElementBookMainRoute.Book, ...string[]];
|
|
7
|
-
export type ElementBookFullRoute = Required<Readonly<FullRoute<ValidElementBookPaths, undefined, undefined>>>;
|
|
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
|
-
export type ElementBookRouter = Readonly<SpaRouter<
|
|
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],
|
|
@@ -6,8 +6,8 @@ type ColorThemeState = {
|
|
|
6
6
|
theme: ColorTheme;
|
|
7
7
|
};
|
|
8
8
|
export declare const ElementBookApp: import("element-vir").DeclarativeElementDefinition<"element-book-app", ElementBookConfig, {
|
|
9
|
-
currentRoute: Readonly<Required<Readonly<import("spa-router-vir").FullRoute<import("../../../routing/element-book-routing").ValidElementBookPaths, undefined, undefined>>>>;
|
|
10
|
-
router: Readonly<import("spa-router-vir").SpaRouter<import("../../../routing/element-book-routing").ValidElementBookPaths, undefined, undefined>> | undefined;
|
|
9
|
+
currentRoute: Readonly<Required<Readonly<import("spa-router-vir").FullRoute<import("../../../routing/element-book-routing").ValidElementBookPaths, Record<string, string> | undefined, undefined>>>>;
|
|
10
|
+
router: Readonly<import("spa-router-vir").SpaRouter<import("../../../routing/element-book-routing").ValidElementBookPaths, Record<string, string> | undefined, undefined>> | undefined;
|
|
11
11
|
colors: ColorThemeState;
|
|
12
12
|
}, {
|
|
13
13
|
pathUpdate: import("element-vir").DefinedTypedEventNameDefinition<readonly string[]>;
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { areJsonEqual, extractErrorMessage
|
|
1
|
+
import { areJsonEqual, extractErrorMessage } from '@augment-vir/common';
|
|
2
2
|
import { assign, css, defineElement, defineElementEvent, html, listen } from 'element-vir';
|
|
3
|
-
import { entriesToTree
|
|
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 {
|
|
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
|
-
|
|
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
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
|
|
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:
|
|
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
|
+
}
|
|
@@ -86,7 +86,7 @@ function createNavigationTree({ indent, entryTreeNode, rootPath, selectedPath, r
|
|
|
86
86
|
const entryPath = entryTreeNode.breadcrumb
|
|
87
87
|
? rootPath.concat(entryTreeNode.breadcrumb)
|
|
88
88
|
: rootPath;
|
|
89
|
-
const isPage = entryTreeNode.entry.
|
|
89
|
+
const isPage = entryTreeNode.entry.entryType === ElementBookEntryTypeEnum.Page;
|
|
90
90
|
const childTemplates = Object.values(entryTreeNode.children).map((child) => {
|
|
91
91
|
return createNavigationTree({
|
|
92
92
|
indent: indent + 1,
|
|
@@ -99,7 +99,7 @@ function createNavigationTree({ indent, entryTreeNode, rootPath, selectedPath, r
|
|
|
99
99
|
return html `
|
|
100
100
|
<div class="nav-tree-entry" style="--indent: ${indent};">
|
|
101
101
|
<slot></slot>
|
|
102
|
-
<li class=${entryTreeNode.entry.
|
|
102
|
+
<li class=${entryTreeNode.entry.entryType}>
|
|
103
103
|
<${ElementBookRouteLink}
|
|
104
104
|
${assign(ElementBookRouteLink, {
|
|
105
105
|
router: router,
|
|
@@ -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
|
-
|
|
103
|
-
|
|
109
|
+
${renderIf(!!currentSearch, html `
|
|
110
|
+
|
|
111
|
+
`, html `
|
|
112
|
+
<${ElementBookBreadcrumbs}
|
|
113
|
+
${assign(ElementBookBreadcrumbs, {
|
|
104
114
|
currentRoute: inputs.currentRoute,
|
|
105
115
|
router: inputs.router,
|
|
106
116
|
})}
|
|
107
|
-
|
|
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
|
});
|
|
@@ -214,7 +255,7 @@ function createDescriptionTemplate(entry) {
|
|
|
214
255
|
`;
|
|
215
256
|
}
|
|
216
257
|
function extractNestedPages(node) {
|
|
217
|
-
if (node.entry.
|
|
258
|
+
if (node.entry.entryType === ElementBookEntryTypeEnum.Page) {
|
|
218
259
|
return [node];
|
|
219
260
|
}
|
|
220
261
|
const nestedPages = [
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ElementBookPageExampleInit } from '../../../data/element-book-entry/element-book-page/element-book-page-example';
|
|
2
2
|
export declare const ElementBookExampleControls: import("element-vir").DeclarativeElementDefinition<"element-book-example-controls", {
|
|
3
|
-
example:
|
|
3
|
+
example: ElementBookPageExampleInit<any, any, any>;
|
|
4
4
|
}, {}, {}, string, string, import("lit-html").HTMLTemplateResult>;
|
|
@@ -16,10 +16,9 @@ export const ElementBookExampleControls = defineElementBookElement()({
|
|
|
16
16
|
}
|
|
17
17
|
`,
|
|
18
18
|
renderCallback({ inputs }) {
|
|
19
|
-
const title = inputs.example.hideControls ? '' : inputs.example.title;
|
|
20
19
|
return html `
|
|
21
20
|
<span>
|
|
22
|
-
${title
|
|
21
|
+
${inputs.example.title}
|
|
23
22
|
<span></span>
|
|
24
23
|
</span>
|
|
25
24
|
`;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ElementBookPageExampleInit } from '../../../data/element-book-entry/element-book-page/element-book-page-example';
|
|
2
2
|
export declare const ElementBookExampleViewer: import("element-vir").DeclarativeElementDefinition<"element-book-example-viewer", {
|
|
3
|
-
example:
|
|
3
|
+
example: ElementBookPageExampleInit<any, any, any>;
|
|
4
4
|
breadcrumbs: ReadonlyArray<string>;
|
|
5
|
+
currentPageControls: Record<string, any>;
|
|
5
6
|
}, any, {}, string, string, string | import("lit-html").HTMLTemplateResult>;
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { extractErrorMessage } from '@augment-vir/common';
|
|
2
2
|
import { html, renderIf } from 'element-vir';
|
|
3
|
+
import { unsetInternalState } from '../../../data/unset';
|
|
3
4
|
import { defineElementBookElement } from '../define-book-element';
|
|
4
|
-
const unsetInternalState = Symbol('unset-internal-state');
|
|
5
5
|
export const ElementBookExampleViewer = defineElementBookElement()({
|
|
6
6
|
tagName: 'element-book-example-viewer',
|
|
7
7
|
stateInit: {
|
|
8
8
|
isUnset: unsetInternalState,
|
|
9
9
|
},
|
|
10
10
|
renderCallback({ state, inputs, updateState }) {
|
|
11
|
+
if (!inputs.example.renderCallback || typeof inputs.example.renderCallback === 'string') {
|
|
12
|
+
throw new Error(`Failed to render example '${inputs.example.title}': renderCallback is not a function`);
|
|
13
|
+
}
|
|
11
14
|
if (state.isUnset === unsetInternalState) {
|
|
12
15
|
updateState({
|
|
13
16
|
isUnset: undefined,
|
|
@@ -15,9 +18,10 @@ export const ElementBookExampleViewer = defineElementBookElement()({
|
|
|
15
18
|
});
|
|
16
19
|
}
|
|
17
20
|
try {
|
|
18
|
-
const output = inputs.example.
|
|
21
|
+
const output = inputs.example.renderCallback({
|
|
19
22
|
state,
|
|
20
23
|
updateState,
|
|
24
|
+
controls: inputs.currentPageControls,
|
|
21
25
|
});
|
|
22
26
|
return html `
|
|
23
27
|
${renderIf(!!inputs.example.styles, html `
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ElementBookPage } from '../../../data/element-book-entry/element-book-page/element-book-page';
|
|
2
|
+
import { ElementBookPageControlMap } from '../../../data/element-book-entry/element-book-page/element-book-page-controls/element-book-page-control';
|
|
3
|
+
export declare const ElementBookPageControls: import("element-vir").DeclarativeElementDefinition<"element-book-page-controls", {
|
|
4
|
+
config: ElementBookPage['controls'];
|
|
5
|
+
currentValues: Record<string, ElementBookPageControlMap['initValue']>;
|
|
6
|
+
}, {}, {
|
|
7
|
+
controlValueChange: import("element-vir").DefinedTypedEventNameDefinition<{
|
|
8
|
+
name: string;
|
|
9
|
+
value: unknown;
|
|
10
|
+
}>;
|
|
11
|
+
}, string, string, import("lit-html").HTMLTemplateResult[]>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { css, defineElementEvent, html, listen } from 'element-vir';
|
|
2
|
+
import { ElementBookPageControlTypeEnum } from '../../../data/element-book-entry/element-book-page/element-book-page-controls/element-book-page-control-type';
|
|
3
|
+
import { defineElementBookElement } from '../define-book-element';
|
|
4
|
+
export const ElementBookPageControls = defineElementBookElement()({
|
|
5
|
+
tagName: 'element-book-page-controls',
|
|
6
|
+
events: {
|
|
7
|
+
controlValueChange: defineElementEvent(),
|
|
8
|
+
},
|
|
9
|
+
styles: css `
|
|
10
|
+
:host {
|
|
11
|
+
display: flex;
|
|
12
|
+
flex-wrap: wrap;
|
|
13
|
+
gap: 16px;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.control-wrapper {
|
|
17
|
+
display: flex;
|
|
18
|
+
gap: 4px;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.error {
|
|
23
|
+
font-weight: bold;
|
|
24
|
+
color: red;
|
|
25
|
+
}
|
|
26
|
+
`,
|
|
27
|
+
renderCallback({ inputs, dispatch, events }) {
|
|
28
|
+
return Object.entries(inputs.config).map(([controlName, controlInit,]) => {
|
|
29
|
+
const controlInputTemplate = createControlInput(inputs.currentValues[controlName], controlInit.controlType, (newValue) => {
|
|
30
|
+
dispatch(new events.controlValueChange({
|
|
31
|
+
name: controlName,
|
|
32
|
+
value: newValue,
|
|
33
|
+
}));
|
|
34
|
+
});
|
|
35
|
+
return html `
|
|
36
|
+
<label class="control-wrapper">
|
|
37
|
+
<span>${controlName}</span>
|
|
38
|
+
${controlInputTemplate}
|
|
39
|
+
</label>
|
|
40
|
+
`;
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
function createControlInput(value, controlType, valueChange) {
|
|
45
|
+
if (controlType === ElementBookPageControlTypeEnum.Text) {
|
|
46
|
+
return html `
|
|
47
|
+
<input
|
|
48
|
+
type="text"
|
|
49
|
+
.value=${value}
|
|
50
|
+
${listen('input', (event) => {
|
|
51
|
+
const inputElement = event.currentTarget;
|
|
52
|
+
if (!(inputElement instanceof HTMLInputElement)) {
|
|
53
|
+
throw new Error("Din't get an input element from the event target.");
|
|
54
|
+
}
|
|
55
|
+
valueChange(inputElement.value);
|
|
56
|
+
})}
|
|
57
|
+
/>
|
|
58
|
+
`;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
return html `
|
|
62
|
+
<p class="error">${controlType} controls are not implemented yet.</p>
|
|
63
|
+
`;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -2,4 +2,6 @@ import { ElementBookPage } from '../../../data/element-book-entry/element-book-p
|
|
|
2
2
|
export declare const ElementBookPageExamples: import("element-vir").DeclarativeElementDefinition<"element-book-page-examples", {
|
|
3
3
|
page: ElementBookPage;
|
|
4
4
|
parentBreadcrumbs: ReadonlyArray<string>;
|
|
5
|
-
}, {
|
|
5
|
+
}, {
|
|
6
|
+
unset: symbol;
|
|
7
|
+
}, {}, string, string, import("lit-html").HTMLTemplateResult>;
|
|
@@ -1,17 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mapObjectValues } from '@augment-vir/common';
|
|
2
|
+
import { assign, css, html, listen, repeat } from 'element-vir';
|
|
3
|
+
import { unsetInternalState } from '../../../data/unset';
|
|
2
4
|
import { colorThemeCssVars } from '../../color-theme/color-theme';
|
|
3
5
|
import { defineElementBookElement } from '../define-book-element';
|
|
4
6
|
import { ElementBookExampleControls } from './element-book-example-controls.element';
|
|
5
7
|
import { ElementBookExampleViewer } from './element-book-example-viewer.element';
|
|
8
|
+
import { ElementBookPageControls } from './element-book-page-controls.element';
|
|
6
9
|
export const ElementBookPageExamples = defineElementBookElement()({
|
|
7
10
|
tagName: 'element-book-page-examples',
|
|
8
11
|
styles: css `
|
|
9
12
|
:host {
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
gap: 24px;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.examples-wrapper {
|
|
10
19
|
display: flex;
|
|
11
20
|
gap: 32px;
|
|
12
21
|
flex-wrap: wrap;
|
|
13
22
|
}
|
|
14
23
|
|
|
24
|
+
.error {
|
|
25
|
+
color: red;
|
|
26
|
+
font-weight: bold;
|
|
27
|
+
}
|
|
28
|
+
|
|
15
29
|
.individual-example-wrapper {
|
|
16
30
|
display: flex;
|
|
17
31
|
flex-direction: column;
|
|
@@ -22,46 +36,65 @@ export const ElementBookPageExamples = defineElementBookElement()({
|
|
|
22
36
|
.individual-example-wrapper:hover ${ElementBookExampleControls} {
|
|
23
37
|
color: ${colorThemeCssVars['element-book-accent-icon-color'].value};
|
|
24
38
|
}
|
|
25
|
-
|
|
26
|
-
.hidden-controls {
|
|
27
|
-
pointer-events: none;
|
|
28
|
-
visibility: hidden;
|
|
29
|
-
}
|
|
30
39
|
`,
|
|
31
|
-
|
|
40
|
+
stateInit: {
|
|
41
|
+
unset: unsetInternalState,
|
|
42
|
+
},
|
|
43
|
+
renderCallback({ inputs, state, updateState }) {
|
|
44
|
+
if (state.unset === unsetInternalState) {
|
|
45
|
+
const newState = mapObjectValues(inputs.page.controls, (key, value) => {
|
|
46
|
+
return value.initValue;
|
|
47
|
+
});
|
|
48
|
+
updateState({
|
|
49
|
+
unset: undefined,
|
|
50
|
+
...newState,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
32
53
|
const examples = inputs.page.examples;
|
|
33
|
-
const allControlsHidden = examples.every((example) => example.hideControls);
|
|
34
54
|
/**
|
|
35
55
|
* Use the repeat directive here, instead of just a map, so that lit doesn't accidentally
|
|
36
56
|
* keep state cached between element book pages.
|
|
37
57
|
*/
|
|
38
|
-
|
|
58
|
+
const examplesTemplate = repeat(Object.values(examples), (example) => inputs.parentBreadcrumbs
|
|
59
|
+
.concat(example instanceof Error ? example.message : example.title)
|
|
60
|
+
.join('>'), (example) => {
|
|
61
|
+
if (example instanceof Error) {
|
|
62
|
+
return html `
|
|
63
|
+
<p class="error">${example.message}</p>
|
|
64
|
+
`;
|
|
65
|
+
}
|
|
39
66
|
const exampleBreadcrumbs = inputs.parentBreadcrumbs.concat(example.title);
|
|
40
67
|
return html `
|
|
41
68
|
<div class="individual-example-wrapper">
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
class=${classMap({
|
|
45
|
-
/**
|
|
46
|
-
* If not all controls are hidden, we still want to render
|
|
47
|
-
* every control so that they take up space, but just hide
|
|
48
|
-
* them.
|
|
49
|
-
*/
|
|
50
|
-
'hidden-controls': !!example.hideControls,
|
|
51
|
-
})}
|
|
52
|
-
${assign(ElementBookExampleControls, {
|
|
69
|
+
<${ElementBookExampleControls}
|
|
70
|
+
${assign(ElementBookExampleControls, {
|
|
53
71
|
example,
|
|
54
72
|
})}
|
|
55
|
-
|
|
56
|
-
`)}
|
|
73
|
+
></${ElementBookExampleControls}>
|
|
57
74
|
<${ElementBookExampleViewer}
|
|
58
75
|
${assign(ElementBookExampleViewer, {
|
|
59
76
|
example,
|
|
60
77
|
breadcrumbs: exampleBreadcrumbs,
|
|
78
|
+
currentPageControls: state,
|
|
61
79
|
})}
|
|
62
80
|
></${ElementBookExampleViewer}>
|
|
63
81
|
</div>
|
|
64
82
|
`;
|
|
65
83
|
});
|
|
84
|
+
return html `
|
|
85
|
+
<${ElementBookPageControls}
|
|
86
|
+
${assign(ElementBookPageControls, {
|
|
87
|
+
config: inputs.page.controls,
|
|
88
|
+
currentValues: state,
|
|
89
|
+
})}
|
|
90
|
+
${listen(ElementBookPageControls.events.controlValueChange, (event) => {
|
|
91
|
+
updateState({ [event.detail.name]: event.detail.value });
|
|
92
|
+
})}
|
|
93
|
+
></${ElementBookPageControls}>
|
|
94
|
+
<section class="examples-wrapper">${examplesTemplate}</section>
|
|
95
|
+
`;
|
|
96
|
+
},
|
|
97
|
+
options: {
|
|
98
|
+
allowPolymorphicState: true,
|
|
66
99
|
},
|
|
67
100
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const ChangeRouteEvent: import("element-vir").DefinedTypedEvent<"change-
|
|
1
|
+
export declare const ChangeRouteEvent: import("element-vir").DefinedTypedEvent<"change-route", Partial<Required<Readonly<import("spa-router-vir").FullRoute<import("../../routing/element-book-routing").ValidElementBookPaths, Record<string, string> | undefined, undefined>>>>>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { defineTypedEvent } from 'element-vir';
|
|
2
|
-
export const ChangeRouteEvent = defineTypedEvent()('change-
|
|
2
|
+
export const ChangeRouteEvent = defineTypedEvent()('change-route');
|
|
@@ -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
|
File without changes
|
|
File without changes
|