element-book 1.0.2 → 2.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.
Files changed (34) hide show
  1. package/dist/data/element-book-entry/element-book-chapter/element-book-chapter.d.ts +3 -3
  2. package/dist/data/element-book-entry/element-book-chapter/element-book-chapter.js +1 -1
  3. package/dist/data/element-book-entry/element-book-entry.d.ts +3 -3
  4. package/dist/data/element-book-entry/element-book-entry.js +2 -2
  5. package/dist/data/element-book-entry/element-book-page/element-book-page-controls/element-book-page-control-type.d.ts +13 -0
  6. package/dist/data/element-book-entry/element-book-page/element-book-page-controls/element-book-page-control-type.js +7 -0
  7. package/dist/data/element-book-entry/element-book-page/element-book-page-controls/element-book-page-control.d.ts +13 -0
  8. package/dist/data/element-book-entry/element-book-page/element-book-page-controls/element-book-page-control.js +4 -0
  9. package/dist/data/element-book-entry/element-book-page/element-book-page-example.d.ts +15 -8
  10. package/dist/data/element-book-entry/element-book-page/element-book-page-example.js +19 -2
  11. package/dist/data/element-book-entry/element-book-page/element-book-page.d.ts +11 -5
  12. package/dist/data/element-book-entry/element-book-page/element-book-page.js +5 -27
  13. package/dist/data/element-book-entry/entry-tree/entry-tree.d.ts +3 -3
  14. package/dist/data/element-book-entry/entry-tree/entry-tree.js +5 -5
  15. package/dist/data/unset.d.ts +1 -0
  16. package/dist/data/unset.js +1 -0
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.js +2 -0
  19. package/dist/routing/create-element-book-router.js +1 -1
  20. package/dist/routing/element-book-routing.d.ts +2 -2
  21. package/dist/ui/elements/element-book-app/element-book-app.element.d.ts +2 -2
  22. package/dist/ui/elements/element-book-nav.element.js +2 -2
  23. package/dist/ui/elements/entry-display/element-book-entry-display.element.js +1 -1
  24. package/dist/ui/elements/entry-display/element-book-example-controls.element.d.ts +2 -2
  25. package/dist/ui/elements/entry-display/element-book-example-controls.element.js +1 -1
  26. package/dist/ui/elements/entry-display/element-book-example-viewer.element.d.ts +4 -5
  27. package/dist/ui/elements/entry-display/element-book-example-viewer.element.js +17 -14
  28. package/dist/ui/elements/entry-display/element-book-page-controls.element.d.ts +11 -0
  29. package/dist/ui/elements/entry-display/element-book-page-controls.element.js +65 -0
  30. package/dist/ui/elements/entry-display/element-book-page-examples.element.d.ts +3 -1
  31. package/dist/ui/elements/entry-display/element-book-page-examples.element.js +54 -5
  32. package/dist/ui/events/change-route.event.d.ts +1 -1
  33. package/dist/ui/events/change-route.event.js +1 -1
  34. package/package.json +5 -5
@@ -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
- type: ElementBookEntryTypeEnum.Chapter;
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
- type: ElementBookEntryTypeEnum;
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, 'type'>): 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
- type: ElementBookEntryTypeEnum.Chapter,
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
- type: ElementBookEntryTypeEnum.Root;
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, type: SpecificType): entry is Extract<ElementBookEntry, {
11
- type: SpecificType;
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, type) {
3
- return typedHasProperty(entry, 'type') && entry.type === type;
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
+ };
@@ -0,0 +1,4 @@
1
+ /** Define a page control. This doesn't do anything fancy but it helps immensely with type inference. */
2
+ export function definePageControl(controlInit) {
3
+ return controlInit;
4
+ }
@@ -1,7 +1,16 @@
1
- import { PropertyInitMapBase, TypedEvent } from 'element-vir';
1
+ import { RequireNonVoid } from '@augment-vir/common';
2
+ import { PropertyInitMapBase, RenderParams, TypedEvent } from 'element-vir';
2
3
  import { CSSResult } from 'lit';
3
- export type ElementBookPageExample<StateInit extends PropertyInitMapBase = {}> = {
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). */
@@ -12,11 +21,9 @@ export type ElementBookPageExample<StateInit extends PropertyInitMapBase = {}> =
12
21
  */
13
22
  styles?: CSSResult;
14
23
  /** Set to true to hide the example controls (example title and buttons). */
15
- hideControls?: boolean | undefined;
24
+ hideExampleControls?: boolean | undefined;
16
25
  /** Render the example. */
17
- render: (renderParams: {
18
- state: StateInit;
19
- updateState: (newState: Partial<StateInit>) => void;
20
- }) => unknown;
26
+ renderCallback: RequireNonVoid<RenderOutput, (renderParams: ElementBookPageExampleRenderParams<ParentPage, StateInit>) => RenderOutput, 'renderCallback is missing a return statement'>;
21
27
  };
22
- export declare function createExample<StateInit extends PropertyInitMapBase>(example: ElementBookPageExample<StateInit>): ElementBookPageExample<StateInit>;
28
+ /** Inserts the defined element example into its parent page. */
29
+ export declare function insertElementExample<ParentPage extends ElementBookPage<any>, StateInit extends PropertyInitMapBase = {}, RenderOutput = any>(exampleInit: ElementBookPageExampleInit<ParentPage, StateInit, RenderOutput>): void;
@@ -1,3 +1,20 @@
1
- export function createExample(example) {
2
- return example;
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 { ElementBookPageExample } from './element-book-page-example';
5
- export type ElementBookPage = Overwrite<BaseElementBookEntry, {
6
- type: ElementBookEntryTypeEnum.Page;
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
- examples: ReadonlyArray<ElementBookPageExample<any>>;
10
+ controls: Controls;
11
+ examples: Record<string, ElementBookPageExampleInit<any, any, any> | Error>;
12
+ pageBreadcrumbs: ReadonlyArray<string>;
9
13
  };
10
- export declare function defineElementBookPage(pageSetup: Omit<ElementBookPage, 'type'>): ElementBookPage;
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
- errors.push(new Error(`Cannot have an element-book page with an empty title.`));
4
+ throw new Error(`Cannot have an element-book page with an empty title.`);
8
5
  }
9
6
  const page = {
10
- type: ElementBookEntryTypeEnum.Page,
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
  }
@@ -1,16 +1,16 @@
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>, type: EntryType): node is EntryTreeNode<EntryType>;
3
+ export declare function doesNodeHaveEntryType<EntryType extends ElementBookEntryTypeEnum>(node: EntryTreeNode<any>, entryType: EntryType): node is EntryTreeNode<EntryType>;
4
4
  declare const markerKeyName = "isElementBookEntryTreeNode";
5
5
  export type EntryTreeNode<EntryType extends ElementBookEntryTypeEnum = ElementBookEntryTypeEnum> = {
6
6
  [markerKeyName]: true;
7
7
  entry: Extract<ElementBookEntry, {
8
- type: EntryType;
8
+ entryType: EntryType;
9
9
  }>;
10
10
  breadcrumb: string;
11
11
  children: Record<string, EntryTreeNode>;
12
12
  };
13
- export declare function isEntryNode<SpecificType extends ElementBookEntryTypeEnum>(input: unknown, type: SpecificType): input is EntryTreeNode<SpecificType>;
13
+ export declare function isEntryNode<SpecificType extends ElementBookEntryTypeEnum>(input: unknown, entryType: SpecificType): input is EntryTreeNode<SpecificType>;
14
14
  export declare function createEmptyEntryTreeRoot(title: string | undefined): EntryTreeNode;
15
15
  export declare function titleToBreadcrumb(title: string): string;
16
16
  export declare function entriesToTree(entries: ReadonlyArray<ElementBookEntry>, everythingTitle: string | undefined): EntryTreeNode<ElementBookEntryTypeEnum>;
@@ -1,22 +1,22 @@
1
1
  import { collapseWhiteSpace, isLengthAtLeast, typedHasProperties } from '@augment-vir/common';
2
2
  import { ElementBookEntryTypeEnum } from '../element-book-entry-type';
3
- export function doesNodeHaveEntryType(node, type) {
4
- return node.entry.type === type;
3
+ export function doesNodeHaveEntryType(node, entryType) {
4
+ return node.entry.entryType === entryType;
5
5
  }
6
6
  const markerKeyName = 'isElementBookEntryTreeNode';
7
- export function isEntryNode(input, type) {
7
+ export function isEntryNode(input, entryType) {
8
8
  return !!(typedHasProperties(input, [
9
9
  markerKeyName,
10
10
  'entry',
11
11
  ]) &&
12
12
  input[markerKeyName] &&
13
- input.entry.type === type);
13
+ input.entry.entryType === entryType);
14
14
  }
15
15
  export function createEmptyEntryTreeRoot(title) {
16
16
  const rootNode = {
17
17
  [markerKeyName]: true,
18
18
  entry: {
19
- type: ElementBookEntryTypeEnum.Root,
19
+ entryType: ElementBookEntryTypeEnum.Root,
20
20
  title: title || 'Everything',
21
21
  parent: undefined,
22
22
  },
@@ -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 { ElementBookMainRoute, defaultElementBookFullRoute, } from './element-book-routing';
3
+ import { defaultElementBookFullRoute, ElementBookMainRoute, } from './element-book-routing';
4
4
  export function createElementBookRouter(baseRoute) {
5
5
  return createSpaRouter({
6
6
  routeBase: baseRoute,
@@ -4,6 +4,6 @@ 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
8
  export declare const defaultElementBookFullRoute: Readonly<ElementBookFullRoute>;
9
- export type ElementBookRouter = Readonly<SpaRouter<ValidElementBookPaths, undefined, undefined>>;
9
+ export type ElementBookRouter = ElementBookFullRoute extends FullRoute<infer Paths, infer Search, infer Hash> ? Readonly<SpaRouter<Paths, Search, Hash>> : never;
@@ -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[]>;
@@ -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.type === ElementBookEntryTypeEnum.Page;
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.type}>
102
+ <li class=${entryTreeNode.entry.entryType}>
103
103
  <${ElementBookRouteLink}
104
104
  ${assign(ElementBookRouteLink, {
105
105
  router: router,
@@ -214,7 +214,7 @@ function createDescriptionTemplate(entry) {
214
214
  `;
215
215
  }
216
216
  function extractNestedPages(node) {
217
- if (node.entry.type === ElementBookEntryTypeEnum.Page) {
217
+ if (node.entry.entryType === ElementBookEntryTypeEnum.Page) {
218
218
  return [node];
219
219
  }
220
220
  const nestedPages = [
@@ -1,4 +1,4 @@
1
- import { ElementBookPageExample } from '../../../data/element-book-entry/element-book-page/element-book-page-example';
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: ElementBookPageExample;
3
+ example: ElementBookPageExampleInit<any, any, any>;
4
4
  }, {}, {}, string, string, import("lit-html").HTMLTemplateResult>;
@@ -16,7 +16,7 @@ export const ElementBookExampleControls = defineElementBookElement()({
16
16
  }
17
17
  `,
18
18
  renderCallback({ inputs }) {
19
- const title = inputs.example.hideControls ? '' : inputs.example.title;
19
+ const title = inputs.example.hideExampleControls ? '' : inputs.example.title;
20
20
  return html `
21
21
  <span>
22
22
  ${title || defaultTitle}
@@ -1,7 +1,6 @@
1
- import { ElementBookPageExample } from '../../../data/element-book-entry/element-book-page/element-book-page-example';
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: ElementBookPageExample;
3
+ example: ElementBookPageExampleInit<any, any, any>;
4
4
  breadcrumbs: ReadonlyArray<string>;
5
- }, {
6
- internalState: any;
7
- }, {}, string, string, string | import("lit-html").HTMLTemplateResult>;
5
+ currentPageControls: Record<string, any>;
6
+ }, any, {}, string, string, string | import("lit-html").HTMLTemplateResult>;
@@ -1,27 +1,27 @@
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
- internalState: unsetInternalState,
8
+ isUnset: unsetInternalState,
9
9
  },
10
10
  renderCallback({ state, inputs, updateState }) {
11
- if (state.internalState === unsetInternalState) {
12
- updateState({ internalState: inputs.example.stateInit });
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
+ }
14
+ if (state.isUnset === unsetInternalState) {
15
+ updateState({
16
+ isUnset: undefined,
17
+ ...inputs.example.stateInit,
18
+ });
13
19
  }
14
20
  try {
15
- const output = inputs.example.render({
16
- state: state.internalState,
17
- updateState: (newState) => {
18
- updateState({
19
- internalState: {
20
- ...state.internalState,
21
- ...newState,
22
- },
23
- });
24
- },
21
+ const output = inputs.example.renderCallback({
22
+ state,
23
+ updateState,
24
+ controls: inputs.currentPageControls,
25
25
  });
26
26
  return html `
27
27
  ${renderIf(!!inputs.example.styles, html `
@@ -37,4 +37,7 @@ export const ElementBookExampleViewer = defineElementBookElement()({
37
37
  return `${inputs.breadcrumbs.join(' > ')} failed: ${extractErrorMessage(error)}`;
38
38
  }
39
39
  },
40
+ options: {
41
+ allowPolymorphicState: true,
42
+ },
40
43
  });
@@ -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
- }, {}, {}, string, string, unknown>;
5
+ }, {
6
+ unset: symbol;
7
+ }, {}, string, string, import("lit-html").HTMLTemplateResult>;
@@ -1,17 +1,31 @@
1
- import { assign, classMap, css, html, renderIf, repeat } from 'element-vir';
1
+ import { mapObjectValues } from '@augment-vir/common';
2
+ import { assign, classMap, css, html, listen, renderIf, 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;
@@ -28,14 +42,33 @@ export const ElementBookPageExamples = defineElementBookElement()({
28
42
  visibility: hidden;
29
43
  }
30
44
  `,
31
- renderCallback({ inputs }) {
45
+ stateInit: {
46
+ unset: unsetInternalState,
47
+ },
48
+ renderCallback({ inputs, state, updateState }) {
49
+ if (state.unset === unsetInternalState) {
50
+ const newState = mapObjectValues(inputs.page.controls, (key, value) => {
51
+ return value.initValue;
52
+ });
53
+ updateState({
54
+ unset: undefined,
55
+ ...newState,
56
+ });
57
+ }
32
58
  const examples = inputs.page.examples;
33
- const allControlsHidden = examples.every((example) => example.hideControls);
59
+ const allControlsHidden = Object.values(examples).every((example) => 'hideExampleControls' in example && example.hideExampleControls);
34
60
  /**
35
61
  * Use the repeat directive here, instead of just a map, so that lit doesn't accidentally
36
62
  * keep state cached between element book pages.
37
63
  */
38
- return repeat(examples, (example) => inputs.parentBreadcrumbs.concat(example.title).join('>'), (example) => {
64
+ const examplesTemplate = repeat(Object.values(examples), (example) => inputs.parentBreadcrumbs
65
+ .concat(example instanceof Error ? example.message : example.title)
66
+ .join('>'), (example) => {
67
+ if (example instanceof Error) {
68
+ return html `
69
+ <p class="error">${example.message}</p>
70
+ `;
71
+ }
39
72
  const exampleBreadcrumbs = inputs.parentBreadcrumbs.concat(example.title);
40
73
  return html `
41
74
  <div class="individual-example-wrapper">
@@ -47,7 +80,7 @@ export const ElementBookPageExamples = defineElementBookElement()({
47
80
  * every control so that they take up space, but just hide
48
81
  * them.
49
82
  */
50
- 'hidden-controls': !!example.hideControls,
83
+ 'hidden-controls': !!example.hideExampleControls,
51
84
  })}
52
85
  ${assign(ElementBookExampleControls, {
53
86
  example,
@@ -58,10 +91,26 @@ export const ElementBookPageExamples = defineElementBookElement()({
58
91
  ${assign(ElementBookExampleViewer, {
59
92
  example,
60
93
  breadcrumbs: exampleBreadcrumbs,
94
+ currentPageControls: state,
61
95
  })}
62
96
  ></${ElementBookExampleViewer}>
63
97
  </div>
64
98
  `;
65
99
  });
100
+ return html `
101
+ <${ElementBookPageControls}
102
+ ${assign(ElementBookPageControls, {
103
+ config: inputs.page.controls,
104
+ currentValues: state,
105
+ })}
106
+ ${listen(ElementBookPageControls.events.controlValueChange, (event) => {
107
+ updateState({ [event.detail.name]: event.detail.value });
108
+ })}
109
+ ></${ElementBookPageControls}>
110
+ <section class="examples-wrapper">${examplesTemplate}</section>
111
+ `;
112
+ },
113
+ options: {
114
+ allowPolymorphicState: true,
66
115
  },
67
116
  });
@@ -1 +1 @@
1
- export declare const ChangeRouteEvent: import("element-vir").DefinedTypedEvent<"change-page", Partial<Required<Readonly<import("spa-router-vir").FullRoute<import("../../routing/element-book-routing").ValidElementBookPaths, undefined, undefined>>>>>;
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-page');
2
+ export const ChangeRouteEvent = defineTypedEvent()('change-route');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "element-book",
3
- "version": "1.0.2",
3
+ "version": "2.0.0",
4
4
  "keywords": [
5
5
  "book",
6
6
  "design system",
@@ -38,16 +38,16 @@
38
38
  "test:types": "tsc --noEmit"
39
39
  },
40
40
  "dependencies": {
41
- "@augment-vir/common": "^13.4.0",
41
+ "@augment-vir/common": "^14.2.0",
42
42
  "@electrovir/icon-element": "^0.0.2",
43
43
  "colorjs.io": "^0.4.3",
44
- "element-vir": "^12.4.6",
44
+ "element-vir": "^12.5.2",
45
45
  "lit-css-vars": "^2.0.2",
46
46
  "spa-router-vir": "^2.1.0",
47
47
  "typed-event-target": "^1.2.0"
48
48
  },
49
49
  "devDependencies": {
50
- "@augment-vir/browser-testing": "^13.4.0",
50
+ "@augment-vir/browser-testing": "^14.2.0",
51
51
  "@open-wc/testing": "^3.1.8",
52
52
  "@types/mocha": "^10.0.1",
53
53
  "@web/dev-server-esbuild": "^0.4.1",
@@ -56,7 +56,7 @@
56
56
  "@web/test-runner-playwright": "^0.10.0",
57
57
  "@web/test-runner-visual-regression": "^0.8.0",
58
58
  "istanbul-smart-text-reporter": "^1.1.1",
59
- "type-fest": "^3.10.0",
59
+ "type-fest": "^3.11.0",
60
60
  "typescript": "^5.0.4"
61
61
  }
62
62
  }