element-book 9.0.0 → 10.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.
@@ -4,11 +4,9 @@ import { BookTree, BookTreeNode } from './book-tree-node';
4
4
  export declare function doesNodeHaveEntryType<const EntryType extends BookEntryTypeEnum>(node: BookTreeNode<any>, entryType: EntryType): node is BookTreeNode<EntryType>;
5
5
  export declare function isBookTreeNode<const SpecificType extends BookEntryTypeEnum>(input: unknown, entryType: SpecificType): input is BookTreeNode<SpecificType>;
6
6
  export declare function isAnyBookTreeNode(input: unknown): input is BookTreeNode<BookEntryTypeEnum>;
7
- export declare function createEmptyBookTreeRoot(title: string | undefined, descriptionParagraphs: ReadonlyArray<string>): BookTreeNode<BookEntryTypeEnum.Root>;
8
- export declare function createBookTreeFromEntries({ entries, everythingTitle, everythingDescriptionParagraphs, debug, }: {
7
+ export declare function createEmptyBookTreeRoot(): BookTreeNode<BookEntryTypeEnum.Root>;
8
+ export declare function createBookTreeFromEntries({ entries, debug, }: {
9
9
  entries: ReadonlyArray<BookEntry>;
10
- everythingTitle: string | undefined;
11
- everythingDescriptionParagraphs: ReadonlyArray<string>;
12
10
  debug: boolean;
13
11
  }): BookTree;
14
12
  export declare function traverseToImmediateParent(entryOrNode: Readonly<BookEntry> | BookTreeNode, currentTree: Readonly<BookTreeNode>): BookTreeNode | undefined;
@@ -1,4 +1,4 @@
1
- import { makeWritable, typedHasProperties } from '@augment-vir/common';
1
+ import { typedHasProperties } from '@augment-vir/common';
2
2
  import { isBookEntry } from '../book-entry/book-entry';
3
3
  import { BookEntryTypeEnum } from '../book-entry/book-entry-type';
4
4
  import { listUrlBreadcrumbs, titleToUrlBreadcrumb } from '../book-entry/url-breadcrumbs';
@@ -17,15 +17,15 @@ export function isAnyBookTreeNode(input) {
17
17
  'entry',
18
18
  ]) && input[isBookTreeNodeMarker]);
19
19
  }
20
- export function createEmptyBookTreeRoot(title, descriptionParagraphs) {
20
+ export function createEmptyBookTreeRoot() {
21
21
  const rootNode = {
22
22
  [isBookTreeNodeMarker]: true,
23
23
  entry: {
24
24
  entryType: BookEntryTypeEnum.Root,
25
- title: title || 'Everything',
25
+ title: '',
26
26
  parent: undefined,
27
27
  errors: [],
28
- descriptionParagraphs: makeWritable(descriptionParagraphs),
28
+ descriptionParagraphs: [],
29
29
  },
30
30
  urlBreadcrumb: '',
31
31
  fullUrlBreadcrumbs: [],
@@ -34,12 +34,12 @@ export function createEmptyBookTreeRoot(title, descriptionParagraphs) {
34
34
  };
35
35
  return rootNode;
36
36
  }
37
- export function createBookTreeFromEntries({ entries, everythingTitle, everythingDescriptionParagraphs, debug, }) {
37
+ export function createBookTreeFromEntries({ entries, debug, }) {
38
38
  const cachedTree = getTreeFromCache(entries);
39
39
  if (cachedTree) {
40
40
  return cachedTree;
41
41
  }
42
- const tree = createEmptyBookTreeRoot(everythingTitle, everythingDescriptionParagraphs);
42
+ const tree = createEmptyBookTreeRoot();
43
43
  entries.forEach((newEntry) => addEntryToTree({ tree, newEntry, debug, manuallyAdded: true }));
44
44
  const flattenedNodes = flattenTree(tree);
45
45
  const bookTree = {
@@ -25,8 +25,16 @@ function isSearchingForErrors(searchQuery) {
25
25
  }
26
26
  export function searchFlattenedNodes({ flattenedNodes, searchQuery, }) {
27
27
  const includeInSearchResults = {};
28
+ function addChildren(treeNode) {
29
+ const childrenKeys = Object.values(treeNode.children).map((child) => {
30
+ addChildren(child);
31
+ return createBreadcrumbsSearchKey(child.fullUrlBreadcrumbs);
32
+ });
33
+ childrenKeys.forEach((keyToInclude) => (includeInSearchResults[keyToInclude] = true));
34
+ }
28
35
  flattenedNodes.forEach((treeNode) => {
29
36
  const matchesErrors = treeNode.entry.errors.length && isSearchingForErrors(searchQuery);
37
+ const currentNodeSearchKey = createBreadcrumbsSearchKey(treeNode.fullUrlBreadcrumbs);
30
38
  const shouldInclude = fuzzySearch({
31
39
  searchIn: [
32
40
  treeNode.entry.title,
@@ -35,14 +43,16 @@ export function searchFlattenedNodes({ flattenedNodes, searchQuery, }) {
35
43
  .join(' ')
36
44
  .toLowerCase(),
37
45
  searchQuery: searchQuery.toLowerCase(),
38
- }) || matchesErrors;
46
+ }) ||
47
+ matchesErrors ||
48
+ includeInSearchResults[currentNodeSearchKey];
39
49
  if (shouldInclude) {
40
50
  const keysToInclude = getFullTreeKeysToInclude(treeNode.fullUrlBreadcrumbs);
51
+ addChildren(treeNode);
41
52
  keysToInclude.forEach((keyToInclude) => (includeInSearchResults[keyToInclude] = true));
42
53
  }
43
54
  else {
44
- const currentNodeKey = createBreadcrumbsSearchKey(treeNode.fullUrlBreadcrumbs);
45
- includeInSearchResults[currentNodeKey] = false;
55
+ includeInSearchResults[currentNodeSearchKey] = false;
46
56
  }
47
57
  });
48
58
  return flattenedNodes.filter((treeNode) => {
@@ -0,0 +1,2 @@
1
+ import { BookTreeNode } from '../../../data/book-tree/book-tree-node';
2
+ export declare function shouldShowTreeNodeInNav(currentNode: Readonly<BookTreeNode>, selectedPath: undefined | ReadonlyArray<string>): boolean;
@@ -0,0 +1,19 @@
1
+ import { areJsonEqual } from '@augment-vir/common';
2
+ import { BookEntryTypeEnum } from '../../../data/book-entry/book-entry-type';
3
+ export function shouldShowTreeNodeInNav(currentNode, selectedPath) {
4
+ if (currentNode.entry.entryType === BookEntryTypeEnum.Root) {
5
+ return false;
6
+ }
7
+ if (currentNode.entry.entryType === BookEntryTypeEnum.Page) {
8
+ return true;
9
+ }
10
+ const isParentSelected = areJsonEqual(selectedPath, currentNode.fullUrlBreadcrumbs.slice(0, -1));
11
+ if (isParentSelected) {
12
+ return true;
13
+ }
14
+ const isSiblingSelected = areJsonEqual(selectedPath?.slice(0, -1), currentNode.fullUrlBreadcrumbs.slice(0, -1));
15
+ if (isSiblingSelected) {
16
+ return true;
17
+ }
18
+ return false;
19
+ }
@@ -1,5 +1,5 @@
1
- import { BookTreeNode } from '../../data/book-tree/book-tree-node';
2
- import { BookRouter } from '../../routing/book-routing';
1
+ import { BookTreeNode } from '../../../data/book-tree/book-tree-node';
2
+ import { BookRouter } from '../../../routing/book-routing';
3
3
  export declare const BookNav: import("element-vir").DeclarativeElementDefinition<"book-nav", {
4
4
  flattenedNodes: ReadonlyArray<Readonly<BookTreeNode>>;
5
5
  selectedPath: ReadonlyArray<string> | undefined;
@@ -2,13 +2,14 @@ import { checkIfEntirelyInScrollView, waitForAnimationFrame } from '@augment-vir
2
2
  import { areJsonEqual } from '@augment-vir/common';
3
3
  import { classMap, css, html, renderIf } from 'element-vir';
4
4
  import { Element16Icon, ViraIcon } from 'vira';
5
- import { BookEntryTypeEnum } from '../../data/book-entry/book-entry-type';
6
- import { isBookTreeNode } from '../../data/book-tree/book-tree';
7
- import { BookMainRoute } from '../../routing/book-routing';
8
- import { colorThemeCssVars } from '../color-theme/color-theme';
9
- import { BookRouteLink } from './common/book-route-link.element';
10
- import { defineBookElement } from './define-book-element';
11
- import { ElementBookSlotName } from './element-book-app/element-book-app-slots';
5
+ import { BookEntryTypeEnum } from '../../../data/book-entry/book-entry-type';
6
+ import { isBookTreeNode } from '../../../data/book-tree/book-tree';
7
+ import { BookMainRoute, defaultBookFullRoute } from '../../../routing/book-routing';
8
+ import { colorThemeCssVars } from '../../color-theme/color-theme';
9
+ import { BookRouteLink } from '../common/book-route-link.element';
10
+ import { defineBookElement } from '../define-book-element';
11
+ import { ElementBookSlotName } from '../element-book-app/element-book-app-slots';
12
+ import { shouldShowTreeNodeInNav } from './book-nav-filter';
12
13
  export const BookNav = defineBookElement()({
13
14
  tagName: 'book-nav',
14
15
  cssVars: {
@@ -71,8 +72,11 @@ export const BookNav = defineBookElement()({
71
72
  `,
72
73
  renderCallback({ inputs }) {
73
74
  const navTreeTemplates = inputs.flattenedNodes.map((treeNode) => {
75
+ if (!shouldShowTreeNodeInNav(treeNode, inputs.selectedPath)) {
76
+ return;
77
+ }
74
78
  const liStyle = css `
75
- --book-nav-internal-indent: ${treeNode.fullUrlBreadcrumbs.length};
79
+ --book-nav-internal-indent: ${treeNode.fullUrlBreadcrumbs.length - 1};
76
80
  `;
77
81
  return html `
78
82
  <li style=${liStyle}>
@@ -103,7 +107,12 @@ export const BookNav = defineBookElement()({
103
107
  `;
104
108
  });
105
109
  return html `
106
- <slot name=${ElementBookSlotName.NavHeader}></slot>
110
+ <${BookRouteLink.assign({
111
+ route: defaultBookFullRoute,
112
+ router: inputs.router,
113
+ })}>
114
+ <slot name=${ElementBookSlotName.NavHeader}>Book</slot>
115
+ </${BookRouteLink}>
107
116
  <ul>
108
117
  ${navTreeTemplates}
109
118
  </ul>
@@ -9,6 +9,7 @@ type ColorThemeState = {
9
9
  export declare const ElementBookApp: import("element-vir").DeclarativeElementDefinition<"element-book-app", ElementBookConfig, {
10
10
  currentRoute: Readonly<Required<Readonly<import("spa-router-vir").FullRoute<import("../../../routing/book-routing").ValidBookPaths, Record<string, string> | undefined, undefined>>>>;
11
11
  router: Readonly<import("spa-router-vir").SpaRouter<import("../../../routing/book-routing").ValidBookPaths, Record<string, string> | undefined, undefined>> | undefined;
12
+ loading: boolean;
12
13
  colors: ColorThemeState;
13
14
  treeBasedCurrentControls: {
14
15
  entries: ElementBookConfig['entries'];
@@ -1,3 +1,4 @@
1
+ import { waitForAnimationFrame } from '@augment-vir/browser';
1
2
  import { areJsonEqual, extractErrorMessage } from '@augment-vir/common';
2
3
  import { css, defineElement, defineElementEvent, html, listen } from 'element-vir';
3
4
  import { createControlsFromTree, createNewCurrentControls, } from '../../../data/book-entry/book-page/current-controls';
@@ -8,7 +9,7 @@ import { createBookRouter } from '../../../routing/create-book-router';
8
9
  import { colorThemeCssVars, setThemeCssVars } from '../../color-theme/color-theme';
9
10
  import { createTheme } from '../../color-theme/create-color-theme';
10
11
  import { ChangeRouteEvent } from '../../events/change-route.event';
11
- import { BookNav, scrollSelectedNavElementIntoView } from '../book-nav.element';
12
+ import { BookNav, scrollSelectedNavElementIntoView } from '../book-nav/book-nav.element';
12
13
  import { BookError } from '../common/book-error.element';
13
14
  import { BookEntryDisplay } from '../entry-display/book-entry-display.element';
14
15
  import { BookPageControls } from '../entry-display/book-page/book-page-controls.element';
@@ -22,6 +23,7 @@ export const ElementBookApp = defineElement()({
22
23
  stateInitStatic: {
23
24
  currentRoute: defaultBookFullRoute,
24
25
  router: undefined,
26
+ loading: false,
25
27
  colors: {
26
28
  config: undefined,
27
29
  theme: createTheme(undefined),
@@ -63,11 +65,16 @@ export const ElementBookApp = defineElement()({
63
65
  overflow-y: auto;
64
66
  max-height: 100%;
65
67
  top: 0;
68
+ max-width: min(400px, 40%);
69
+ }
70
+
71
+ .loading {
72
+ padding: 64px;
66
73
  }
67
74
  `,
68
- initCallback({ host }) {
75
+ initCallback({ host, state }) {
69
76
  setTimeout(() => {
70
- scrollNav(host);
77
+ scrollNav(host, extractSearchQuery(state.currentRoute.paths), state.currentRoute);
71
78
  }, 1000);
72
79
  },
73
80
  cleanupCallback({ state, updateState }) {
@@ -124,11 +131,9 @@ export const ElementBookApp = defineElement()({
124
131
  });
125
132
  setThemeCssVars(host, newTheme);
126
133
  }
127
- const debug = inputs.debug ?? false;
134
+ const debug = inputs._debug ?? false;
128
135
  const originalTree = createBookTreeFromEntries({
129
136
  entries: inputs.entries,
130
- everythingTitle: inputs.everythingTitle,
131
- everythingDescriptionParagraphs: inputs.everythingDescriptionParagraphs ?? [],
132
137
  debug,
133
138
  });
134
139
  if (!state.treeBasedCurrentControls ||
@@ -159,14 +164,21 @@ export const ElementBookApp = defineElement()({
159
164
  })}></${BookError}>
160
165
  `;
161
166
  }
162
- if (inputs.debug) {
167
+ if (inputs._debug) {
163
168
  console.info({ currentControls });
164
169
  }
165
170
  return html `
166
171
  <div
167
172
  class="root"
168
- ${listen(ChangeRouteEvent, (event) => {
173
+ ${listen(ChangeRouteEvent, async (event) => {
169
174
  const entryDisplay = host.shadowRoot.querySelector(BookEntryDisplay.tagName);
175
+ updateState({ loading: true });
176
+ /** Wait for the loading div to show up. */
177
+ while (!host.shadowRoot.querySelector('.loading')) {
178
+ console.log('waiting');
179
+ await waitForAnimationFrame();
180
+ }
181
+ await waitForAnimationFrame();
170
182
  if (entryDisplay) {
171
183
  entryDisplay.scroll({ top: 0, behavior: 'smooth' });
172
184
  }
@@ -178,7 +190,8 @@ export const ElementBookApp = defineElement()({
178
190
  if (!(navElement instanceof BookNav)) {
179
191
  throw new Error(`Failed to find child '${BookNav.tagName}'`);
180
192
  }
181
- scrollNav(host);
193
+ updateState({ loading: false });
194
+ scrollNav(host, searchQuery, state.currentRoute);
182
195
  })}
183
196
  ${listen(BookPageControls.events.controlValueChange, (event) => {
184
197
  if (!state.treeBasedCurrentControls) {
@@ -203,19 +216,25 @@ export const ElementBookApp = defineElement()({
203
216
  slot=${ElementBookSlotName.NavHeader}
204
217
  ></slot>
205
218
  </${BookNav}>
206
- <${BookEntryDisplay.assign({
207
- currentRoute: state.currentRoute,
208
- currentNodes,
209
- router: state.router,
210
- debug,
211
- currentControls,
212
- originalTree: originalTree.tree,
213
- })}>
214
- <slot
215
- name=${ElementBookSlotName.Footer}
216
- slot=${ElementBookSlotName.Footer}
217
- ></slot>
218
- </${BookEntryDisplay}>
219
+ ${state.loading
220
+ ? html `
221
+ <div class="loading">Loading...</div>
222
+ `
223
+ : html `
224
+ <${BookEntryDisplay.assign({
225
+ currentRoute: state.currentRoute,
226
+ currentNodes,
227
+ router: state.router,
228
+ debug,
229
+ currentControls,
230
+ originalTree: originalTree.tree,
231
+ })}>
232
+ <slot
233
+ name=${ElementBookSlotName.Footer}
234
+ slot=${ElementBookSlotName.Footer}
235
+ ></slot>
236
+ </${BookEntryDisplay}>
237
+ `}
219
238
  </div>
220
239
  `;
221
240
  }
@@ -227,7 +246,14 @@ export const ElementBookApp = defineElement()({
227
246
  }
228
247
  },
229
248
  });
230
- async function scrollNav(host) {
249
+ async function scrollNav(host, searchQuery, currentRoutes) {
250
+ /** If there is a search query, then there will be no selected nav to scroll to. */
251
+ if (searchQuery) {
252
+ return;
253
+ }
254
+ if (currentRoutes.paths.length <= 1) {
255
+ return;
256
+ }
231
257
  const navElement = host.shadowRoot.querySelector(BookNav.tagName);
232
258
  if (!(navElement instanceof BookNav)) {
233
259
  throw new Error(`Failed to find child '${BookNav.tagName}'`);
@@ -9,10 +9,7 @@ export type GlobalValues = Readonly<Record<string, unknown>>;
9
9
  type OptionalConfig = {
10
10
  /** The base theme color from which all other element-book colors will be generated from. */
11
11
  themeColor: string;
12
- /** The title to use for the "Everything" nav link. */
13
- everythingTitle: string;
14
- everythingDescriptionParagraphs: ReadonlyArray<string>;
15
- debug: boolean;
12
+ _debug: boolean;
16
13
  globalValues: GlobalValues;
17
14
  } & RequireExactlyOne<{
18
15
  /**
@@ -110,7 +110,9 @@ function createNodeTemplates({ currentNodes, isTopLevel, router, isSearching, cu
110
110
  const hiddenAncestorControls = isLengthAtLeast(currentNodes, 1)
111
111
  ? getFlattenedControlsFromHiddenParents(currentNodes, currentControls, currentNodes[0], originalTree)
112
112
  : undefined;
113
- const hiddenAncestorControlsTemplate = hiddenAncestorControls && isLengthAtLeast(currentNodes, 1)
113
+ const hiddenAncestorControlsTemplate = hiddenAncestorControls &&
114
+ Object.values(hiddenAncestorControls.config).length &&
115
+ isLengthAtLeast(currentNodes, 1)
114
116
  ? html `
115
117
  <${BookPageControls.assign({
116
118
  config: hiddenAncestorControls.config,
@@ -145,9 +147,7 @@ function createNodeTemplates({ currentNodes, isTopLevel, router, isSearching, cu
145
147
  `;
146
148
  }
147
149
  else if (isBookTreeNode(currentNode, BookEntryTypeEnum.Root)) {
148
- return html `
149
- <h1>${currentNode.entry.title}</h1>
150
- `;
150
+ return html ``;
151
151
  }
152
152
  else {
153
153
  return html `
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "element-book",
3
- "version": "9.0.0",
3
+ "version": "10.0.0",
4
4
  "keywords": [
5
5
  "book",
6
6
  "design system",
@@ -39,18 +39,18 @@
39
39
  "test:watch": "web-test-runner --color --watch --config configs/web-test-runner.config.mjs"
40
40
  },
41
41
  "dependencies": {
42
- "@augment-vir/browser": "^15.4.0",
43
- "@augment-vir/common": "^15.4.0",
42
+ "@augment-vir/browser": "^15.5.0",
43
+ "@augment-vir/common": "^15.5.0",
44
44
  "colorjs.io": "0.4.5",
45
- "element-vir": "^14.2.1",
45
+ "element-vir": "^16.0.0",
46
46
  "lit-css-vars": "^2.0.3",
47
- "object-shape-tester": "^0.3.0",
47
+ "object-shape-tester": "^0.4.0",
48
48
  "spa-router-vir": "^2.1.1",
49
49
  "typed-event-target": "^1.3.1",
50
- "vira": "^0.6.1"
50
+ "vira": "^0.6.4"
51
51
  },
52
52
  "devDependencies": {
53
- "@augment-vir/browser-testing": "^15.4.0",
53
+ "@augment-vir/browser-testing": "^15.5.0",
54
54
  "@open-wc/testing": "^3.2.0",
55
55
  "@types/chai": "^4.3.5",
56
56
  "@types/mocha": "^10.0.1",