element-book 26.12.0 → 26.12.1
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/{src/data/book-entry/base-book-entry.ts → dist/data/book-entry/base-book-entry.d.ts} +2 -3
- package/dist/data/book-entry/base-book-entry.js +1 -0
- package/{src/data/book-entry/book-entry-type.ts → dist/data/book-entry/book-entry-type.d.ts} +5 -9
- package/dist/data/book-entry/book-entry-type.js +14 -0
- package/dist/data/book-entry/book-entry.d.ts +17 -0
- package/dist/data/book-entry/book-entry.js +9 -0
- package/dist/data/book-entry/book-page/book-page-controls.d.ts +92 -0
- package/dist/data/book-entry/book-page/book-page-controls.js +70 -0
- package/dist/data/book-entry/book-page/book-page.d.ts +63 -0
- package/dist/data/book-entry/book-page/book-page.js +2 -0
- package/dist/data/book-entry/book-page/controls-wrapper.d.ts +32 -0
- package/dist/data/book-entry/book-page/controls-wrapper.js +65 -0
- package/dist/data/book-entry/book-page/define-book-page.d.ts +58 -0
- package/dist/data/book-entry/book-page/define-book-page.js +52 -0
- package/dist/data/book-entry/book-root.d.ts +12 -0
- package/dist/data/book-entry/book-root.js +1 -0
- package/dist/data/book-entry/url-breadcrumbs.d.ts +23 -0
- package/{src/data/book-entry/url-breadcrumbs.ts → dist/data/book-entry/url-breadcrumbs.js} +8 -17
- package/dist/data/book-entry/verify-book-entry.d.ts +3 -0
- package/{src/data/book-entry/verify-book-entry.ts → dist/data/book-entry/verify-book-entry.js} +3 -9
- package/{src/data/book-tree/book-tree-node.ts → dist/data/book-tree/book-tree-node.d.ts} +9 -15
- package/dist/data/book-tree/book-tree-node.js +6 -0
- package/dist/data/book-tree/book-tree.d.ts +13 -0
- package/dist/data/book-tree/book-tree.js +136 -0
- package/dist/data/book-tree/search-nodes.d.ts +5 -0
- package/dist/data/book-tree/search-nodes.js +74 -0
- package/dist/data/book-tree/tree-cache.d.ts +4 -0
- package/dist/data/book-tree/tree-cache.js +8 -0
- package/dist/data/unset.d.ts +1 -0
- package/dist/index.js +18 -0
- package/dist/routing/book-router.d.ts +4 -0
- package/{src/routing/book-router.ts → dist/routing/book-router.js} +12 -16
- package/{src/routing/book-routing.ts → dist/routing/book-routing.d.ts} +6 -21
- package/dist/routing/book-routing.js +33 -0
- package/dist/ui/color-theme/color-theme.d.ts +57 -0
- package/dist/ui/color-theme/color-theme.js +94 -0
- package/dist/ui/color-theme/create-color-theme.d.ts +32 -0
- package/dist/ui/color-theme/create-color-theme.js +93 -0
- package/dist/ui/elements/book-breadcrumbs.element.d.ts +6 -0
- package/dist/ui/elements/book-breadcrumbs.element.js +50 -0
- package/dist/ui/elements/book-nav/book-nav-filter.d.ts +2 -0
- package/dist/ui/elements/book-nav/book-nav-filter.js +15 -0
- package/dist/ui/elements/book-nav/book-nav.element.d.ts +8 -0
- package/{src/ui/elements/book-nav/book-nav.element.ts → dist/ui/elements/book-nav/book-nav.element.js} +44 -65
- package/dist/ui/elements/common/book-error.element.d.ts +3 -0
- package/{src/ui/elements/common/book-error.element.ts → dist/ui/elements/common/book-error.element.js} +8 -12
- package/dist/ui/elements/common/book-route-link.element.d.ts +6 -0
- package/dist/ui/elements/common/book-route-link.element.js +40 -0
- package/dist/ui/elements/define-book-element.d.ts +2 -0
- package/dist/ui/elements/define-book-element.js +2 -0
- package/{src/ui/elements/element-book-app/element-book-app-slots.ts → dist/ui/elements/element-book-app/element-book-app-slots.d.ts} +3 -3
- package/dist/ui/elements/element-book-app/element-book-app-slots.js +18 -0
- package/dist/ui/elements/element-book-app/element-book-app.element.d.ts +35 -0
- package/dist/ui/elements/element-book-app/element-book-app.element.js +301 -0
- package/dist/ui/elements/element-book-app/element-book-config.d.ts +48 -0
- package/dist/ui/elements/element-book-app/element-book-config.js +1 -0
- package/dist/ui/elements/element-book-app/get-current-nodes.d.ts +3 -0
- package/dist/ui/elements/element-book-app/get-current-nodes.js +18 -0
- package/dist/ui/elements/element-book-app/global-values.js +1 -0
- package/dist/ui/elements/entry-display/book-breadcrumbs-bar.element.d.ts +7 -0
- package/dist/ui/elements/entry-display/book-breadcrumbs-bar.element.js +60 -0
- package/dist/ui/elements/entry-display/book-entry-description.element.d.ts +4 -0
- package/{src/ui/elements/entry-display/book-entry-description.element.ts → dist/ui/elements/entry-display/book-entry-description.element.js} +7 -10
- package/dist/ui/elements/entry-display/book-page/book-page-controls.element.d.ts +17 -0
- package/dist/ui/elements/entry-display/book-page/book-page-controls.element.js +172 -0
- package/dist/ui/elements/entry-display/book-page/book-page-wrapper.element.d.ts +10 -0
- package/dist/ui/elements/entry-display/book-page/book-page-wrapper.element.js +86 -0
- package/dist/ui/elements/entry-display/element-example/book-element-example-controls.element.d.ts +7 -0
- package/dist/ui/elements/entry-display/element-example/book-element-example-controls.element.js +34 -0
- package/dist/ui/elements/entry-display/element-example/book-element-example-viewer.element.d.ts +7 -0
- package/dist/ui/elements/entry-display/element-example/book-element-example-viewer.element.js +61 -0
- package/dist/ui/elements/entry-display/element-example/book-element-example-wrapper.element.d.ts +9 -0
- package/dist/ui/elements/entry-display/element-example/book-element-example-wrapper.element.js +50 -0
- package/dist/ui/elements/entry-display/entry-display/book-entry-display.element.d.ts +18 -0
- package/{src/ui/elements/entry-display/entry-display/book-entry-display.element.ts → dist/ui/elements/entry-display/entry-display/book-entry-display.element.js} +27 -49
- package/dist/ui/elements/entry-display/entry-display/create-node-templates.d.ts +13 -0
- package/dist/ui/elements/entry-display/entry-display/create-node-templates.js +113 -0
- package/dist/ui/events/change-route.event.d.ts +1 -0
- package/dist/ui/events/change-route.event.js +2 -0
- package/dist/util/fuzzy-search.d.ts +34 -0
- package/{src/util/fuzzy-search.ts → dist/util/fuzzy-search.js} +5 -13
- package/{src/util/type.ts → dist/util/type.d.ts} +4 -3
- package/dist/util/type.js +1 -0
- package/package.json +5 -5
- package/src/data/book-entry/book-entry.ts +0 -23
- package/src/data/book-entry/book-page/book-page-controls.ts +0 -159
- package/src/data/book-entry/book-page/book-page.ts +0 -110
- package/src/data/book-entry/book-page/controls-wrapper.ts +0 -119
- package/src/data/book-entry/book-page/define-book-page.ts +0 -171
- package/src/data/book-entry/book-root.ts +0 -16
- package/src/data/book-tree/book-tree.ts +0 -225
- package/src/data/book-tree/search-nodes.ts +0 -104
- package/src/data/book-tree/tree-cache.ts +0 -13
- package/src/ui/color-theme/color-theme.ts +0 -152
- package/src/ui/color-theme/create-color-theme.ts +0 -139
- package/src/ui/elements/book-breadcrumbs.element.ts +0 -60
- package/src/ui/elements/book-nav/book-nav-filter.ts +0 -30
- package/src/ui/elements/common/book-route-link.element.ts +0 -48
- package/src/ui/elements/define-book-element.ts +0 -5
- package/src/ui/elements/element-book-app/element-book-app.element.ts +0 -380
- package/src/ui/elements/element-book-app/element-book-config.ts +0 -52
- package/src/ui/elements/element-book-app/get-current-nodes.ts +0 -35
- package/src/ui/elements/entry-display/book-breadcrumbs-bar.element.ts +0 -78
- package/src/ui/elements/entry-display/book-page/book-page-controls.element.ts +0 -219
- package/src/ui/elements/entry-display/book-page/book-page-wrapper.element.ts +0 -105
- package/src/ui/elements/entry-display/element-example/book-element-example-controls.element.ts +0 -42
- package/src/ui/elements/entry-display/element-example/book-element-example-viewer.element.ts +0 -79
- package/src/ui/elements/entry-display/element-example/book-element-example-wrapper.element.ts +0 -61
- package/src/ui/elements/entry-display/entry-display/create-node-templates.ts +0 -183
- package/src/ui/events/change-route.event.ts +0 -6
- /package/{src/data/unset.ts → dist/data/unset.js} +0 -0
- /package/{src/index.ts → dist/index.d.ts} +0 -0
- /package/{src/ui/elements/element-book-app/global-values.ts → dist/ui/elements/element-book-app/global-values.d.ts} +0 -0
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { check } from '@augment-vir/assert';
|
|
2
|
+
import { extractErrorMessage, makeWritable } from '@augment-vir/common';
|
|
3
|
+
import { waitForAnimationFrame } from '@augment-vir/web';
|
|
4
|
+
import { css, defineElement, defineElementEvent, html, listen } from 'element-vir';
|
|
5
|
+
import { createNewControls, updateTreeControls, } from '../../../data/book-entry/book-page/controls-wrapper.js';
|
|
6
|
+
import { createBookTreeFromEntries } from '../../../data/book-tree/book-tree.js';
|
|
7
|
+
import { searchFlattenedNodes } from '../../../data/book-tree/search-nodes.js';
|
|
8
|
+
import { createBookRouter } from '../../../routing/book-router.js';
|
|
9
|
+
import { defaultBookFullRoute, extractSearchQuery, } from '../../../routing/book-routing.js';
|
|
10
|
+
import { colorThemeCssVars, setThemeCssVars, } from '../../color-theme/color-theme.js';
|
|
11
|
+
import { createTheme } from '../../color-theme/create-color-theme.js';
|
|
12
|
+
import { ChangeRouteEvent } from '../../events/change-route.event.js';
|
|
13
|
+
import { BookNav, scrollSelectedNavElementIntoView } from '../book-nav/book-nav.element.js';
|
|
14
|
+
import { BookError } from '../common/book-error.element.js';
|
|
15
|
+
import { BookPageControls } from '../entry-display/book-page/book-page-controls.element.js';
|
|
16
|
+
import { BookEntryDisplay } from '../entry-display/entry-display/book-entry-display.element.js';
|
|
17
|
+
import { ElementBookSlotName } from './element-book-app-slots.js';
|
|
18
|
+
import { getCurrentNodes } from './get-current-nodes.js';
|
|
19
|
+
/**
|
|
20
|
+
* The element-book app itself. Instantiate one of these where you want your element-book pages to
|
|
21
|
+
* render. Make sure to also provide an array of pages to actually render!
|
|
22
|
+
*
|
|
23
|
+
* @category Main
|
|
24
|
+
*/
|
|
25
|
+
export const ElementBookApp = defineElement()({
|
|
26
|
+
tagName: 'element-book-app',
|
|
27
|
+
state() {
|
|
28
|
+
return {
|
|
29
|
+
currentRoute: defaultBookFullRoute,
|
|
30
|
+
router: undefined,
|
|
31
|
+
loading: true,
|
|
32
|
+
colors: {
|
|
33
|
+
config: undefined,
|
|
34
|
+
theme: createTheme(undefined),
|
|
35
|
+
},
|
|
36
|
+
treeBasedControls: undefined,
|
|
37
|
+
originalWindowTitle: undefined,
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
events: {
|
|
41
|
+
pathUpdate: defineElementEvent(),
|
|
42
|
+
},
|
|
43
|
+
styles: css `
|
|
44
|
+
:host {
|
|
45
|
+
display: flex;
|
|
46
|
+
flex-direction: column;
|
|
47
|
+
height: 100%;
|
|
48
|
+
width: 100%;
|
|
49
|
+
font-family: sans-serif;
|
|
50
|
+
background-color: ${colorThemeCssVars['element-book-page-background-color'].value};
|
|
51
|
+
color: ${colorThemeCssVars['element-book-page-foreground-color'].value};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.error {
|
|
55
|
+
color: red;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.root {
|
|
59
|
+
flex-grow: 1;
|
|
60
|
+
width: 100%;
|
|
61
|
+
display: flex;
|
|
62
|
+
position: relative;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
${BookEntryDisplay} {
|
|
66
|
+
flex-grow: 1;
|
|
67
|
+
max-height: 100%;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
${BookNav} {
|
|
71
|
+
flex-shrink: 0;
|
|
72
|
+
overflow-x: hidden;
|
|
73
|
+
overflow-y: auto;
|
|
74
|
+
max-height: 100%;
|
|
75
|
+
top: 0;
|
|
76
|
+
max-width: min(400px, 40%);
|
|
77
|
+
}
|
|
78
|
+
`,
|
|
79
|
+
init({ host, state }) {
|
|
80
|
+
setTimeout(async () => {
|
|
81
|
+
await scrollNav(host, extractSearchQuery(state.currentRoute.paths), state.currentRoute);
|
|
82
|
+
}, 500);
|
|
83
|
+
},
|
|
84
|
+
cleanup({ state, updateState }) {
|
|
85
|
+
if (state.router) {
|
|
86
|
+
state.router.destroy();
|
|
87
|
+
updateState({ router: undefined });
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
render: ({ state, inputs, host, updateState, dispatch, events }) => {
|
|
91
|
+
if (inputs._debug) {
|
|
92
|
+
console.info('rendering element-book app');
|
|
93
|
+
}
|
|
94
|
+
function mergeRoutes(newRouteInput) {
|
|
95
|
+
return {
|
|
96
|
+
...state.currentRoute,
|
|
97
|
+
...newRouteInput,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function areRoutesNew(newRouteInput) {
|
|
101
|
+
const newRoute = mergeRoutes(newRouteInput);
|
|
102
|
+
return !check.jsonEquals(state.currentRoute, newRoute);
|
|
103
|
+
}
|
|
104
|
+
function updateWindowTitle(topNodeTitle) {
|
|
105
|
+
if (!inputs.preventWindowTitleChange) {
|
|
106
|
+
if (!state.originalWindowTitle) {
|
|
107
|
+
updateState({ originalWindowTitle: document.title });
|
|
108
|
+
}
|
|
109
|
+
document.title = [
|
|
110
|
+
state.originalWindowTitle,
|
|
111
|
+
topNodeTitle,
|
|
112
|
+
]
|
|
113
|
+
.filter(check.isTruthy)
|
|
114
|
+
.join(' - ');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function updateRoutes(newRouteInput) {
|
|
118
|
+
if (!areRoutesNew(newRouteInput)) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const newRoute = mergeRoutes(newRouteInput);
|
|
122
|
+
if (state.router) {
|
|
123
|
+
state.router.setRoute(newRoute);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
updateState({
|
|
127
|
+
currentRoute: {
|
|
128
|
+
...state.currentRoute,
|
|
129
|
+
...newRoute,
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
if (inputs.elementBookRoutePaths &&
|
|
134
|
+
!check.jsonEquals(inputs.elementBookRoutePaths, state.currentRoute.paths)) {
|
|
135
|
+
dispatch(new events.pathUpdate(newRoute.paths));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
if (inputs.elementBookRoutePaths &&
|
|
140
|
+
!check.jsonEquals(inputs.elementBookRoutePaths, state.currentRoute.paths)) {
|
|
141
|
+
updateRoutes({ paths: makeWritable(inputs.elementBookRoutePaths) });
|
|
142
|
+
}
|
|
143
|
+
if (inputs.internalRouterConfig?.useInternalRouter && !state.router) {
|
|
144
|
+
const router = createBookRouter(inputs.internalRouterConfig.basePath);
|
|
145
|
+
updateState({ router });
|
|
146
|
+
router.listen(true, (fullRoute) => {
|
|
147
|
+
updateState({
|
|
148
|
+
currentRoute: fullRoute,
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
else if (!inputs.internalRouterConfig?.useInternalRouter && state.router) {
|
|
153
|
+
state.router.destroy();
|
|
154
|
+
}
|
|
155
|
+
const inputThemeConfig = {
|
|
156
|
+
themeColor: inputs.themeColor,
|
|
157
|
+
};
|
|
158
|
+
if (!check.jsonEquals(inputThemeConfig, state.colors.config)) {
|
|
159
|
+
const newTheme = createTheme(inputThemeConfig);
|
|
160
|
+
updateState({
|
|
161
|
+
colors: {
|
|
162
|
+
config: inputThemeConfig,
|
|
163
|
+
theme: newTheme,
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
setThemeCssVars(host, newTheme);
|
|
167
|
+
}
|
|
168
|
+
const debug = inputs._debug ?? false;
|
|
169
|
+
const originalTree = createBookTreeFromEntries({
|
|
170
|
+
entries: inputs.pages,
|
|
171
|
+
debug,
|
|
172
|
+
});
|
|
173
|
+
if (!state.treeBasedControls ||
|
|
174
|
+
state.treeBasedControls.pages !== inputs.pages ||
|
|
175
|
+
state.treeBasedControls.lastGlobalInputs !== inputs.globalValues) {
|
|
176
|
+
if (inputs._debug) {
|
|
177
|
+
console.info('regenerating global controls');
|
|
178
|
+
}
|
|
179
|
+
updateState({
|
|
180
|
+
treeBasedControls: {
|
|
181
|
+
pages: inputs.pages,
|
|
182
|
+
lastGlobalInputs: inputs.globalValues ?? {},
|
|
183
|
+
controls: updateTreeControls(originalTree.tree, {
|
|
184
|
+
children: state.treeBasedControls?.controls.children,
|
|
185
|
+
controls: inputs.globalValues,
|
|
186
|
+
}),
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
const searchQuery = extractSearchQuery(state.currentRoute.paths);
|
|
191
|
+
const searchedNodes = searchQuery
|
|
192
|
+
? searchFlattenedNodes({
|
|
193
|
+
flattenedNodes: originalTree.flattenedNodes,
|
|
194
|
+
searchQuery,
|
|
195
|
+
})
|
|
196
|
+
: undefined;
|
|
197
|
+
const currentNodes = searchedNodes ??
|
|
198
|
+
getCurrentNodes(originalTree.flattenedNodes, state.currentRoute.paths, updateRoutes);
|
|
199
|
+
updateWindowTitle(currentNodes[0]?.entry.title);
|
|
200
|
+
const currentControls = state.treeBasedControls?.controls;
|
|
201
|
+
if (!currentControls) {
|
|
202
|
+
return html `
|
|
203
|
+
<${BookError.assign({
|
|
204
|
+
message: 'Failed to generate page controls.',
|
|
205
|
+
})}></${BookError}>
|
|
206
|
+
`;
|
|
207
|
+
}
|
|
208
|
+
if (inputs._debug) {
|
|
209
|
+
console.info({ currentControls });
|
|
210
|
+
}
|
|
211
|
+
return html `
|
|
212
|
+
<div
|
|
213
|
+
class="root"
|
|
214
|
+
${listen(ChangeRouteEvent, async (event) => {
|
|
215
|
+
const newRoute = event.detail;
|
|
216
|
+
if (!areRoutesNew(newRoute)) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
updateState({ loading: true });
|
|
220
|
+
updateRoutes(newRoute);
|
|
221
|
+
const navElement = host.shadowRoot.querySelector(BookNav.tagName);
|
|
222
|
+
if (!(navElement instanceof BookNav)) {
|
|
223
|
+
throw new TypeError(`Failed to find child '${BookNav.tagName}'`);
|
|
224
|
+
}
|
|
225
|
+
await scrollNav(host, searchQuery, state.currentRoute);
|
|
226
|
+
})}
|
|
227
|
+
${listen(BookPageControls.events.controlValueChange, (event) => {
|
|
228
|
+
if (!state.treeBasedControls) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const newControls = createNewControls(currentControls, event.detail.fullUrlBreadcrumbs, event.detail.newValues);
|
|
232
|
+
updateState({
|
|
233
|
+
treeBasedControls: {
|
|
234
|
+
...state.treeBasedControls,
|
|
235
|
+
controls: newControls,
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
})}
|
|
239
|
+
>
|
|
240
|
+
<${BookNav.assign({
|
|
241
|
+
flattenedNodes: originalTree.flattenedNodes,
|
|
242
|
+
router: state.router,
|
|
243
|
+
selectedPath: searchQuery ? undefined : state.currentRoute.paths.slice(1),
|
|
244
|
+
})}>
|
|
245
|
+
<slot
|
|
246
|
+
name=${ElementBookSlotName.NavHeader}
|
|
247
|
+
slot=${ElementBookSlotName.NavHeader}
|
|
248
|
+
></slot>
|
|
249
|
+
</${BookNav}>
|
|
250
|
+
<${BookEntryDisplay.assign({
|
|
251
|
+
controls: currentControls,
|
|
252
|
+
currentNodes,
|
|
253
|
+
currentRoute: state.currentRoute,
|
|
254
|
+
debug,
|
|
255
|
+
originalTree: originalTree.tree,
|
|
256
|
+
router: state.router,
|
|
257
|
+
showLoading: state.loading,
|
|
258
|
+
})}
|
|
259
|
+
${listen(BookEntryDisplay.events.loadingRender, async (event) => {
|
|
260
|
+
await waitForAnimationFrame();
|
|
261
|
+
const entryDisplay = host.shadowRoot.querySelector(BookEntryDisplay.tagName);
|
|
262
|
+
if (entryDisplay) {
|
|
263
|
+
entryDisplay.scroll({ top: 0, behavior: 'instant' });
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
console.error(`Failed to find '${BookEntryDisplay.tagName}' for scrolling.`);
|
|
267
|
+
}
|
|
268
|
+
await waitForAnimationFrame();
|
|
269
|
+
updateState({ loading: !event.detail });
|
|
270
|
+
})}
|
|
271
|
+
>
|
|
272
|
+
<slot
|
|
273
|
+
name=${ElementBookSlotName.Footer}
|
|
274
|
+
slot=${ElementBookSlotName.Footer}
|
|
275
|
+
></slot>
|
|
276
|
+
</${BookEntryDisplay}>
|
|
277
|
+
</div>
|
|
278
|
+
`;
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
console.error(error);
|
|
282
|
+
return html `
|
|
283
|
+
<p class="error">${extractErrorMessage(error)}</p>
|
|
284
|
+
`;
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
async function scrollNav(host, searchQuery, currentRoutes) {
|
|
289
|
+
/** If there is a search query, then there will be no selected nav to scroll to. */
|
|
290
|
+
if (searchQuery) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
if (currentRoutes.paths.length <= 1) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
const navElement = host.shadowRoot.querySelector(BookNav.tagName);
|
|
297
|
+
if (!(navElement instanceof BookNav)) {
|
|
298
|
+
throw new TypeError(`Failed to find child '${BookNav.tagName}'`);
|
|
299
|
+
}
|
|
300
|
+
await scrollSelectedNavElementIntoView(navElement);
|
|
301
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type PartialWithUndefined } from '@augment-vir/common';
|
|
2
|
+
import { type RequireExactlyOne } from 'type-fest';
|
|
3
|
+
import { type BookPage } from '../../../data/book-entry/book-page/book-page.js';
|
|
4
|
+
import { type ValidBookPaths } from '../../../routing/book-routing.js';
|
|
5
|
+
import { type GlobalValues } from './global-values.js';
|
|
6
|
+
/**
|
|
7
|
+
* Full configuration for an element-book app.
|
|
8
|
+
*
|
|
9
|
+
* @category Type
|
|
10
|
+
*/
|
|
11
|
+
export type ElementBookConfig = {
|
|
12
|
+
/** All element-book pages in order. */
|
|
13
|
+
pages: ReadonlyArray<BookPage>;
|
|
14
|
+
} & PartialWithUndefined<OptionalConfig>;
|
|
15
|
+
/**
|
|
16
|
+
* Options for {@link ElementBookConfig} that are optional.
|
|
17
|
+
*
|
|
18
|
+
* @category Internal
|
|
19
|
+
*/
|
|
20
|
+
export type OptionalConfig = {
|
|
21
|
+
/** The base theme color from which all other element-book colors will be generated from. */
|
|
22
|
+
themeColor: string;
|
|
23
|
+
_debug: boolean;
|
|
24
|
+
globalValues: GlobalValues;
|
|
25
|
+
preventWindowTitleChange: boolean;
|
|
26
|
+
} & RequireExactlyOne<Readonly<{
|
|
27
|
+
/**
|
|
28
|
+
* Set this internal router config if element-book is intended to be the current website's
|
|
29
|
+
* entire web app. Meaning, you're not embedded element-book within another website.
|
|
30
|
+
*
|
|
31
|
+
* In this case, element-book will create its own internal router for managing the URL. In
|
|
32
|
+
* other cases, when element-book is embedded in another website, use the
|
|
33
|
+
* elementBookRoutePaths input property instead.
|
|
34
|
+
*/
|
|
35
|
+
internalRouterConfig: Readonly<{
|
|
36
|
+
useInternalRouter: true;
|
|
37
|
+
/**
|
|
38
|
+
* Path to this page, used for routing. For example, if this page is hosted at
|
|
39
|
+
* www.example.org/my-page then this value should be `my-page`.
|
|
40
|
+
*/
|
|
41
|
+
basePath?: string | undefined;
|
|
42
|
+
}>;
|
|
43
|
+
/**
|
|
44
|
+
* Current route paths for element-book to handle. These are intended to come from a router
|
|
45
|
+
* that's external to the element-book app itself.
|
|
46
|
+
*/
|
|
47
|
+
elementBookRoutePaths: Readonly<ValidBookPaths>;
|
|
48
|
+
}>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type BookTreeNode } from '../../../data/book-tree/book-tree-node.js';
|
|
2
|
+
import { type BookFullRoute, type ValidBookPaths } from '../../../routing/book-routing.js';
|
|
3
|
+
export declare function getCurrentNodes(flattenedNodes: ReadonlyArray<Readonly<BookTreeNode>>, currentPaths: Readonly<ValidBookPaths>, updateRoutes: (newRoute: Partial<BookFullRoute>) => void): BookTreeNode[];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { doBreadcrumbsStartWith } from '../../../data/book-entry/url-breadcrumbs.js';
|
|
2
|
+
import { defaultBookFullRoute, } from '../../../routing/book-routing.js';
|
|
3
|
+
export function getCurrentNodes(flattenedNodes, currentPaths, updateRoutes) {
|
|
4
|
+
const filteredNodes = filterNodes(flattenedNodes, currentPaths);
|
|
5
|
+
if (filteredNodes.length) {
|
|
6
|
+
return filteredNodes;
|
|
7
|
+
}
|
|
8
|
+
else {
|
|
9
|
+
updateRoutes(defaultBookFullRoute);
|
|
10
|
+
}
|
|
11
|
+
return filterNodes(flattenedNodes, defaultBookFullRoute.paths);
|
|
12
|
+
}
|
|
13
|
+
function filterNodes(flattenedNodes, paths) {
|
|
14
|
+
return flattenedNodes.filter((node) => doBreadcrumbsStartWith({
|
|
15
|
+
searchFor: paths.slice(1),
|
|
16
|
+
searchIn: node.fullUrlBreadcrumbs,
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type BookRouter } from '../../../routing/book-router.js';
|
|
2
|
+
import { type BookFullRoute } from '../../../routing/book-routing.js';
|
|
3
|
+
export declare const BookBreadcrumbsBar: import("element-vir").DeclarativeElementDefinition<"book-breadcrumbs-bar", {
|
|
4
|
+
currentSearch: string;
|
|
5
|
+
currentRoute: Readonly<BookFullRoute>;
|
|
6
|
+
router: Readonly<BookRouter> | undefined;
|
|
7
|
+
}, {}, {}, "book-breadcrumbs-bar-", "book-breadcrumbs-bar-", readonly [], readonly []>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { wait } from '@augment-vir/common';
|
|
2
|
+
import { css, html, listen, renderIf } from 'element-vir';
|
|
3
|
+
import { BookMainRoute, defaultBookFullRoute, } from '../../../routing/book-routing.js';
|
|
4
|
+
import { colorThemeCssVars } from '../../color-theme/color-theme.js';
|
|
5
|
+
import { ChangeRouteEvent } from '../../events/change-route.event.js';
|
|
6
|
+
import { BookBreadcrumbs } from '../book-breadcrumbs.element.js';
|
|
7
|
+
import { defineBookElement } from '../define-book-element.js';
|
|
8
|
+
export const BookBreadcrumbsBar = defineBookElement()({
|
|
9
|
+
tagName: 'book-breadcrumbs-bar',
|
|
10
|
+
styles: css `
|
|
11
|
+
:host {
|
|
12
|
+
border-bottom: 1px solid
|
|
13
|
+
${colorThemeCssVars['element-book-page-foreground-faint-level-2-color'].value};
|
|
14
|
+
padding: 4px 8px;
|
|
15
|
+
background-color: ${colorThemeCssVars['element-book-page-background-color'].value};
|
|
16
|
+
display: flex;
|
|
17
|
+
gap: 16px;
|
|
18
|
+
justify-content: space-between;
|
|
19
|
+
}
|
|
20
|
+
`,
|
|
21
|
+
render({ inputs, dispatch }) {
|
|
22
|
+
return html `
|
|
23
|
+
${renderIf(!!inputs.currentSearch, html `
|
|
24
|
+
|
|
25
|
+
`, html `
|
|
26
|
+
<${BookBreadcrumbs.assign({
|
|
27
|
+
currentRoute: inputs.currentRoute,
|
|
28
|
+
router: inputs.router,
|
|
29
|
+
})}></${BookBreadcrumbs}>
|
|
30
|
+
`)}
|
|
31
|
+
<input
|
|
32
|
+
placeholder="search"
|
|
33
|
+
.value=${inputs.currentSearch}
|
|
34
|
+
${listen('input', async (event) => {
|
|
35
|
+
const inputElement = event.currentTarget;
|
|
36
|
+
if (!(inputElement instanceof HTMLInputElement)) {
|
|
37
|
+
throw new TypeError('Failed to find input element for search.');
|
|
38
|
+
}
|
|
39
|
+
const preThrottleValue = inputElement.value;
|
|
40
|
+
// throttle it a bit
|
|
41
|
+
await wait({ milliseconds: 200 });
|
|
42
|
+
if (inputElement.value !== preThrottleValue) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (inputElement.value) {
|
|
46
|
+
dispatch(new ChangeRouteEvent({
|
|
47
|
+
paths: [
|
|
48
|
+
BookMainRoute.Search,
|
|
49
|
+
encodeURIComponent(inputElement.value),
|
|
50
|
+
],
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
dispatch(new ChangeRouteEvent(defaultBookFullRoute));
|
|
55
|
+
}
|
|
56
|
+
})}
|
|
57
|
+
/>
|
|
58
|
+
`;
|
|
59
|
+
},
|
|
60
|
+
});
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type HTMLTemplateResult } from 'element-vir';
|
|
2
|
+
export declare const BookEntryDescription: import("element-vir").DeclarativeElementDefinition<"book-entry-description", {
|
|
3
|
+
descriptionParagraphs: ReadonlyArray<string | HTMLTemplateResult>;
|
|
4
|
+
}, {}, {}, "book-entry-description-", "book-entry-description-", readonly [], readonly []>;
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import {css, html
|
|
2
|
-
import {colorThemeCssVars} from '../../color-theme/color-theme.js';
|
|
3
|
-
import {defineBookElement} from '../define-book-element.js';
|
|
4
|
-
|
|
5
|
-
export const BookEntryDescription = defineBookElement<{
|
|
6
|
-
descriptionParagraphs: ReadonlyArray<string | HTMLTemplateResult>;
|
|
7
|
-
}>()({
|
|
1
|
+
import { css, html } from 'element-vir';
|
|
2
|
+
import { colorThemeCssVars } from '../../color-theme/color-theme.js';
|
|
3
|
+
import { defineBookElement } from '../define-book-element.js';
|
|
4
|
+
export const BookEntryDescription = defineBookElement()({
|
|
8
5
|
tagName: 'book-entry-description',
|
|
9
|
-
styles: css`
|
|
6
|
+
styles: css `
|
|
10
7
|
:host {
|
|
11
8
|
color: ${colorThemeCssVars['element-book-page-foreground-faint-level-1-color'].value};
|
|
12
9
|
display: inline-flex;
|
|
@@ -31,9 +28,9 @@ export const BookEntryDescription = defineBookElement<{
|
|
|
31
28
|
font-size: 1.2em;
|
|
32
29
|
}
|
|
33
30
|
`,
|
|
34
|
-
render({inputs}) {
|
|
31
|
+
render({ inputs }) {
|
|
35
32
|
return inputs.descriptionParagraphs.map((paragraph) => {
|
|
36
|
-
return html`
|
|
33
|
+
return html `
|
|
37
34
|
<p>${paragraph}</p>
|
|
38
35
|
`;
|
|
39
36
|
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type BookPageControl, type BookPageControlsValues } from '../../../../data/book-entry/book-page/book-page-controls.js';
|
|
2
|
+
import { type BookPage } from '../../../../data/book-entry/book-page/book-page.js';
|
|
3
|
+
export declare const BookPageControls: import("element-vir").DeclarativeElementDefinition<"book-page-controls", {
|
|
4
|
+
config: BookPage["controls"];
|
|
5
|
+
/**
|
|
6
|
+
* If an object (or Record) is given for this input, then each key of the object must correspond
|
|
7
|
+
* to one of the controls from the input config and the value for each key will be the
|
|
8
|
+
* breadcrumbs for that specific config.
|
|
9
|
+
*/
|
|
10
|
+
fullUrlBreadcrumbs: ReadonlyArray<string> | Record<string, ReadonlyArray<string>>;
|
|
11
|
+
currentValues: Record<string, BookPageControl["initValue"]>;
|
|
12
|
+
}, {}, {
|
|
13
|
+
controlValueChange: import("element-vir").DefineEvent<{
|
|
14
|
+
fullUrlBreadcrumbs: ReadonlyArray<string>;
|
|
15
|
+
newValues: BookPageControlsValues;
|
|
16
|
+
}>;
|
|
17
|
+
}, "book-page-controls-has-controls", "book-page-controls-", readonly [], readonly []>;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { check } from '@augment-vir/assert';
|
|
2
|
+
import { extractEventTarget } from '@augment-vir/web';
|
|
3
|
+
import { css, defineElementEvent, html, listen, renderIf } from 'element-vir';
|
|
4
|
+
import { Options24Icon, ViraIcon, ViraInput } from 'vira';
|
|
5
|
+
import { BookPageControlType, isControlInitType, } from '../../../../data/book-entry/book-page/book-page-controls.js';
|
|
6
|
+
import { colorThemeCssVars } from '../../../color-theme/color-theme.js';
|
|
7
|
+
import { defineBookElement } from '../../define-book-element.js';
|
|
8
|
+
export const BookPageControls = defineBookElement()({
|
|
9
|
+
tagName: 'book-page-controls',
|
|
10
|
+
events: {
|
|
11
|
+
controlValueChange: defineElementEvent(),
|
|
12
|
+
},
|
|
13
|
+
hostClasses: {
|
|
14
|
+
'book-page-controls-has-controls': ({ inputs }) => !!Object.keys(inputs.config).length,
|
|
15
|
+
},
|
|
16
|
+
styles: ({ hostClasses }) => css `
|
|
17
|
+
:host {
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-wrap: wrap;
|
|
20
|
+
align-items: flex-end;
|
|
21
|
+
padding-left: 36px;
|
|
22
|
+
align-content: flex-start;
|
|
23
|
+
gap: 16px;
|
|
24
|
+
row-gap: 10px;
|
|
25
|
+
color: ${colorThemeCssVars['element-book-page-foreground-faint-level-1-color'].value};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
${hostClasses['book-page-controls-has-controls'].selector} {
|
|
29
|
+
margin-top: 8px;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.control-wrapper {
|
|
33
|
+
position: relative;
|
|
34
|
+
display: flex;
|
|
35
|
+
gap: 4px;
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.error {
|
|
40
|
+
font-weight: bold;
|
|
41
|
+
color: red;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
${ViraInput} {
|
|
45
|
+
height: 24px;
|
|
46
|
+
max-width: 128px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
${ViraIcon}.options-icon {
|
|
50
|
+
position: absolute;
|
|
51
|
+
left: 0;
|
|
52
|
+
bottom: 0;
|
|
53
|
+
margin-left: -32px;
|
|
54
|
+
}
|
|
55
|
+
`,
|
|
56
|
+
render({ inputs, dispatch, events }) {
|
|
57
|
+
if (!Object.entries(inputs.config).length) {
|
|
58
|
+
return '';
|
|
59
|
+
}
|
|
60
|
+
return Object.entries(inputs.config).map(([controlName, controlInit,], index) => {
|
|
61
|
+
if (controlInit.controlType === BookPageControlType.Hidden) {
|
|
62
|
+
return '';
|
|
63
|
+
}
|
|
64
|
+
const controlInputTemplate = createControlInput(inputs.currentValues[controlName], controlInit, (newValue) => {
|
|
65
|
+
const fullUrlBreadcrumbs = check.isArray(inputs.fullUrlBreadcrumbs)
|
|
66
|
+
? inputs.fullUrlBreadcrumbs
|
|
67
|
+
: inputs.fullUrlBreadcrumbs[controlName];
|
|
68
|
+
if (!fullUrlBreadcrumbs) {
|
|
69
|
+
throw new Error(`Failed to find breadcrumbs from given control name: '${controlName}'`);
|
|
70
|
+
}
|
|
71
|
+
dispatch(new events.controlValueChange({
|
|
72
|
+
fullUrlBreadcrumbs,
|
|
73
|
+
newValues: {
|
|
74
|
+
...inputs.currentValues,
|
|
75
|
+
[controlName]: newValue,
|
|
76
|
+
},
|
|
77
|
+
}));
|
|
78
|
+
});
|
|
79
|
+
return html `
|
|
80
|
+
<div class="control-wrapper">
|
|
81
|
+
${renderIf(index === 0, html `
|
|
82
|
+
<${ViraIcon.assign({ icon: Options24Icon })}
|
|
83
|
+
class="options-icon"
|
|
84
|
+
></${ViraIcon}>
|
|
85
|
+
`)}
|
|
86
|
+
<label class="control-wrapper">
|
|
87
|
+
<span>${controlName}</span>
|
|
88
|
+
${controlInputTemplate}
|
|
89
|
+
</label>
|
|
90
|
+
</div>
|
|
91
|
+
`;
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
function createControlInput(value, controlInit, valueChange) {
|
|
96
|
+
if (isControlInitType(controlInit, BookPageControlType.Hidden)) {
|
|
97
|
+
return '';
|
|
98
|
+
}
|
|
99
|
+
else if (isControlInitType(controlInit, BookPageControlType.Checkbox)) {
|
|
100
|
+
return html `
|
|
101
|
+
<input
|
|
102
|
+
type="checkbox"
|
|
103
|
+
?checked=${value}
|
|
104
|
+
${listen('input', (event) => {
|
|
105
|
+
const inputElement = extractEventTarget(event, HTMLInputElement);
|
|
106
|
+
valueChange(inputElement.checked);
|
|
107
|
+
})}
|
|
108
|
+
/>
|
|
109
|
+
`;
|
|
110
|
+
}
|
|
111
|
+
else if (isControlInitType(controlInit, BookPageControlType.Color)) {
|
|
112
|
+
return html `
|
|
113
|
+
<input
|
|
114
|
+
type="color"
|
|
115
|
+
.value=${value}
|
|
116
|
+
${listen('input', (event) => {
|
|
117
|
+
const inputElement = extractEventTarget(event, HTMLInputElement);
|
|
118
|
+
valueChange(inputElement.value);
|
|
119
|
+
})}
|
|
120
|
+
/>
|
|
121
|
+
`;
|
|
122
|
+
}
|
|
123
|
+
else if (isControlInitType(controlInit, BookPageControlType.Text)) {
|
|
124
|
+
return html `
|
|
125
|
+
<${ViraInput.assign({
|
|
126
|
+
value,
|
|
127
|
+
showClearButton: true,
|
|
128
|
+
disableBrowserHelps: true,
|
|
129
|
+
})}
|
|
130
|
+
${listen(ViraInput.events.valueChange, (event) => {
|
|
131
|
+
valueChange(event.detail);
|
|
132
|
+
})}
|
|
133
|
+
></${ViraInput}>
|
|
134
|
+
`;
|
|
135
|
+
}
|
|
136
|
+
else if (isControlInitType(controlInit, BookPageControlType.Number)) {
|
|
137
|
+
return html `
|
|
138
|
+
<input
|
|
139
|
+
type="number"
|
|
140
|
+
.value=${value}
|
|
141
|
+
${listen('input', (event) => {
|
|
142
|
+
const inputElement = extractEventTarget(event, HTMLInputElement);
|
|
143
|
+
valueChange(inputElement.value);
|
|
144
|
+
})}
|
|
145
|
+
/>
|
|
146
|
+
`;
|
|
147
|
+
}
|
|
148
|
+
else if (isControlInitType(controlInit, BookPageControlType.Dropdown)) {
|
|
149
|
+
return html `
|
|
150
|
+
<select
|
|
151
|
+
.value=${value}
|
|
152
|
+
${listen('input', (event) => {
|
|
153
|
+
const selectElement = extractEventTarget(event, HTMLSelectElement);
|
|
154
|
+
valueChange(selectElement.value);
|
|
155
|
+
})}
|
|
156
|
+
>
|
|
157
|
+
${controlInit.options.map((optionLabel) => {
|
|
158
|
+
return html `
|
|
159
|
+
<option ?selected=${optionLabel === value} value=${optionLabel}>
|
|
160
|
+
${optionLabel}
|
|
161
|
+
</option>
|
|
162
|
+
`;
|
|
163
|
+
})}
|
|
164
|
+
</select>
|
|
165
|
+
`;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
return html `
|
|
169
|
+
<p class="error">${controlInit.controlType} controls are not implemented yet.</p>
|
|
170
|
+
`;
|
|
171
|
+
}
|
|
172
|
+
}
|