@emkodev/emroute 1.6.2 → 1.6.4
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/README.md +6 -6
- package/dist/runtime/abstract.runtime.d.ts +94 -0
- package/dist/runtime/abstract.runtime.js +339 -0
- package/dist/runtime/abstract.runtime.js.map +1 -0
- package/dist/runtime/bun/esbuild-runtime-loader.plugin.d.ts +25 -0
- package/dist/runtime/bun/esbuild-runtime-loader.plugin.js +72 -0
- package/dist/runtime/bun/esbuild-runtime-loader.plugin.js.map +1 -0
- package/dist/runtime/bun/fs/bun-fs.runtime.d.ts +21 -0
- package/dist/runtime/bun/fs/bun-fs.runtime.js +205 -0
- package/dist/runtime/bun/fs/bun-fs.runtime.js.map +1 -0
- package/dist/runtime/bun/sqlite/bun-sqlite.runtime.d.ts +27 -0
- package/dist/runtime/bun/sqlite/bun-sqlite.runtime.js +234 -0
- package/dist/runtime/bun/sqlite/bun-sqlite.runtime.js.map +1 -0
- package/dist/runtime/sitemap.generator.d.ts +58 -0
- package/dist/runtime/sitemap.generator.js +107 -0
- package/dist/runtime/sitemap.generator.js.map +1 -0
- package/dist/runtime/universal/fs/universal-fs.runtime.d.ts +29 -0
- package/dist/runtime/universal/fs/universal-fs.runtime.js +213 -0
- package/dist/runtime/universal/fs/universal-fs.runtime.js.map +1 -0
- package/dist/server/codegen.util.d.ts +16 -0
- package/dist/server/codegen.util.js +46 -0
- package/dist/server/codegen.util.js.map +1 -0
- package/dist/server/emroute.server.d.ts +37 -0
- package/dist/server/emroute.server.js +314 -0
- package/dist/server/emroute.server.js.map +1 -0
- package/dist/server/esbuild-manifest.plugin.d.ts +26 -0
- package/dist/server/esbuild-manifest.plugin.js +187 -0
- package/dist/server/esbuild-manifest.plugin.js.map +1 -0
- package/dist/server/scanner.util.d.ts +22 -0
- package/dist/server/scanner.util.js +194 -0
- package/dist/server/scanner.util.js.map +1 -0
- package/dist/server/server-api.type.d.ts +71 -0
- package/dist/server/server-api.type.js +9 -0
- package/dist/server/server-api.type.js.map +1 -0
- package/dist/src/component/abstract.component.d.ts +197 -0
- package/dist/src/component/abstract.component.js +84 -0
- package/dist/src/component/abstract.component.js.map +1 -0
- package/dist/src/component/page.component.d.ts +74 -0
- package/dist/src/component/page.component.js +107 -0
- package/dist/src/component/page.component.js.map +1 -0
- package/dist/src/component/widget.component.d.ts +47 -0
- package/dist/src/component/widget.component.js +69 -0
- package/dist/src/component/widget.component.js.map +1 -0
- package/dist/src/element/component.element.d.ts +79 -0
- package/dist/src/element/component.element.js +293 -0
- package/dist/src/element/component.element.js.map +1 -0
- package/dist/src/element/markdown.element.d.ts +36 -0
- package/dist/src/element/markdown.element.js +93 -0
- package/dist/src/element/markdown.element.js.map +1 -0
- package/dist/src/element/slot.element.d.ts +30 -0
- package/dist/src/element/slot.element.js +31 -0
- package/dist/src/element/slot.element.js.map +1 -0
- package/dist/src/index.d.ts +23 -0
- package/dist/src/index.js +24 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/overlay/mod.d.ts +9 -0
- package/dist/src/overlay/mod.js +9 -0
- package/dist/src/overlay/mod.js.map +1 -0
- package/dist/src/overlay/overlay.css.d.ts +8 -0
- package/dist/src/overlay/overlay.css.js +170 -0
- package/dist/src/overlay/overlay.css.js.map +1 -0
- package/dist/src/overlay/overlay.service.d.ts +14 -0
- package/dist/src/overlay/overlay.service.js +307 -0
- package/dist/src/overlay/overlay.service.js.map +1 -0
- package/dist/src/overlay/overlay.type.d.ts +33 -0
- package/dist/src/overlay/overlay.type.js +11 -0
- package/dist/src/overlay/overlay.type.js.map +1 -0
- package/dist/src/renderer/spa/base.renderer.d.ts +39 -0
- package/dist/src/renderer/spa/base.renderer.js +149 -0
- package/dist/src/renderer/spa/base.renderer.js.map +1 -0
- package/dist/src/renderer/spa/hash.renderer.d.ts +78 -0
- package/dist/src/renderer/spa/hash.renderer.js +162 -0
- package/dist/src/renderer/spa/hash.renderer.js.map +1 -0
- package/dist/src/renderer/spa/html.renderer.d.ts +81 -0
- package/dist/src/renderer/spa/html.renderer.js +304 -0
- package/dist/src/renderer/spa/html.renderer.js.map +1 -0
- package/dist/src/renderer/spa/mod.d.ts +30 -0
- package/dist/src/renderer/spa/mod.js +35 -0
- package/dist/src/renderer/spa/mod.js.map +1 -0
- package/dist/src/renderer/ssr/html.renderer.d.ts +49 -0
- package/dist/src/renderer/ssr/html.renderer.js +108 -0
- package/dist/src/renderer/ssr/html.renderer.js.map +1 -0
- package/dist/src/renderer/ssr/md.renderer.d.ts +40 -0
- package/dist/src/renderer/ssr/md.renderer.js +100 -0
- package/dist/src/renderer/ssr/md.renderer.js.map +1 -0
- package/dist/src/renderer/ssr/ssr.renderer.d.ts +74 -0
- package/dist/src/renderer/ssr/ssr.renderer.js +185 -0
- package/dist/src/renderer/ssr/ssr.renderer.js.map +1 -0
- package/dist/src/route/route.core.d.ts +129 -0
- package/dist/src/route/route.core.js +255 -0
- package/dist/src/route/route.core.js.map +1 -0
- package/dist/src/route/route.matcher.d.ts +86 -0
- package/dist/src/route/route.matcher.js +214 -0
- package/dist/src/route/route.matcher.js.map +1 -0
- package/dist/src/type/logger.type.d.ts +17 -0
- package/dist/src/type/logger.type.js +9 -0
- package/dist/src/type/logger.type.js.map +1 -0
- package/dist/src/type/markdown.type.d.ts +20 -0
- package/dist/src/type/markdown.type.js +2 -0
- package/dist/src/type/markdown.type.js.map +1 -0
- package/dist/src/type/route.type.d.ts +112 -0
- package/dist/src/type/route.type.js +8 -0
- package/dist/src/type/route.type.js.map +1 -0
- package/dist/src/type/widget.type.d.ts +55 -0
- package/dist/src/type/widget.type.js +10 -0
- package/dist/src/type/widget.type.js.map +1 -0
- package/dist/src/util/html.util.d.ts +29 -0
- package/dist/src/util/html.util.js +158 -0
- package/dist/src/util/html.util.js.map +1 -0
- package/dist/src/util/logger.util.d.ts +26 -0
- package/dist/src/util/logger.util.js +80 -0
- package/dist/src/util/logger.util.js.map +1 -0
- package/dist/src/util/widget-resolve.util.d.ts +52 -0
- package/dist/src/util/widget-resolve.util.js +149 -0
- package/dist/src/util/widget-resolve.util.js.map +1 -0
- package/dist/src/widget/breadcrumb.widget.d.ts +48 -0
- package/dist/src/widget/breadcrumb.widget.js +72 -0
- package/dist/src/widget/breadcrumb.widget.js.map +1 -0
- package/dist/src/widget/page-title.widget.d.ts +33 -0
- package/dist/src/widget/page-title.widget.js +33 -0
- package/dist/src/widget/page-title.widget.js.map +1 -0
- package/dist/src/widget/widget.parser.d.ts +26 -0
- package/dist/src/widget/widget.parser.js +76 -0
- package/dist/src/widget/widget.parser.js.map +1 -0
- package/dist/src/widget/widget.registry.d.ts +23 -0
- package/dist/src/widget/widget.registry.js +42 -0
- package/dist/src/widget/widget.registry.js.map +1 -0
- package/package.json +63 -13
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Overlay Service
|
|
3
|
+
*
|
|
4
|
+
* Programmatic API for modals, toasts, and popovers. For simple trigger
|
|
5
|
+
* patterns, use declarative HTML (commandfor/command + popover/dialog)
|
|
6
|
+
* with zero JS. This service covers dynamic content, programmatic
|
|
7
|
+
* triggers, and complex workflows.
|
|
8
|
+
*
|
|
9
|
+
* dismissAll() is DOM-aware: it closes both programmatic overlays
|
|
10
|
+
* managed by this service AND declarative popovers/dialogs found
|
|
11
|
+
* via DOM queries.
|
|
12
|
+
*/
|
|
13
|
+
import { overlayCSS } from "./overlay.css.js";
|
|
14
|
+
const ANIMATION_SAFETY_TIMEOUT = 300;
|
|
15
|
+
/**
|
|
16
|
+
* Animate an element out by setting `data-dismissing`, waiting for
|
|
17
|
+
* `transitionend`, then calling the provided callback. Includes a
|
|
18
|
+
* safety timeout in case the transition event never fires.
|
|
19
|
+
*/
|
|
20
|
+
function animateDismiss(el, onDone) {
|
|
21
|
+
el.setAttribute('data-dismissing', '');
|
|
22
|
+
let done = false;
|
|
23
|
+
const finish = () => {
|
|
24
|
+
if (done)
|
|
25
|
+
return;
|
|
26
|
+
done = true;
|
|
27
|
+
onDone();
|
|
28
|
+
};
|
|
29
|
+
el.addEventListener('transitionend', finish, { once: true });
|
|
30
|
+
setTimeout(finish, ANIMATION_SAFETY_TIMEOUT);
|
|
31
|
+
}
|
|
32
|
+
export function createOverlayService() {
|
|
33
|
+
let styleInjected = false;
|
|
34
|
+
// Modal state
|
|
35
|
+
let dialog = null;
|
|
36
|
+
// Uses `any` because modalResolve is reassigned across multiple modal() calls
|
|
37
|
+
// with different type parameters T. Each call creates a Promise.withResolvers<T>(),
|
|
38
|
+
// so the resolver function signature changes (accepts T | PromiseLike<T | undefined>).
|
|
39
|
+
// Type safety is maintained by closeModal<T>(value?: T) which ensures only valid
|
|
40
|
+
// types are passed to the resolver.
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
42
|
+
let modalResolve = null;
|
|
43
|
+
let modalOnClose;
|
|
44
|
+
// Toast state
|
|
45
|
+
let toastContainer = null;
|
|
46
|
+
// Popover state
|
|
47
|
+
let popoverEl = null;
|
|
48
|
+
let popoverAnchorObserver = null;
|
|
49
|
+
const supportsAnchor = typeof CSS !== 'undefined' &&
|
|
50
|
+
CSS.supports('anchor-name', '--a');
|
|
51
|
+
function injectCSS() {
|
|
52
|
+
if (styleInjected)
|
|
53
|
+
return;
|
|
54
|
+
styleInjected = true;
|
|
55
|
+
const style = document.createElement('style');
|
|
56
|
+
style.textContent = overlayCSS;
|
|
57
|
+
document.head.appendChild(style);
|
|
58
|
+
}
|
|
59
|
+
function ensureDialog() {
|
|
60
|
+
if (dialog)
|
|
61
|
+
return dialog;
|
|
62
|
+
injectCSS();
|
|
63
|
+
dialog = document.createElement('dialog');
|
|
64
|
+
dialog.setAttribute('data-overlay-modal', '');
|
|
65
|
+
document.body.appendChild(dialog);
|
|
66
|
+
dialog.addEventListener('click', (e) => {
|
|
67
|
+
if (e.target === dialog) {
|
|
68
|
+
closeModal(undefined);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return dialog;
|
|
72
|
+
}
|
|
73
|
+
function ensureToastContainer() {
|
|
74
|
+
if (toastContainer)
|
|
75
|
+
return toastContainer;
|
|
76
|
+
injectCSS();
|
|
77
|
+
toastContainer = document.createElement('div');
|
|
78
|
+
toastContainer.setAttribute('data-overlay-toast-container', '');
|
|
79
|
+
document.body.appendChild(toastContainer);
|
|
80
|
+
return toastContainer;
|
|
81
|
+
}
|
|
82
|
+
function ensurePopover() {
|
|
83
|
+
if (popoverEl)
|
|
84
|
+
return popoverEl;
|
|
85
|
+
injectCSS();
|
|
86
|
+
popoverEl = document.createElement('div');
|
|
87
|
+
popoverEl.setAttribute('data-overlay-popover', '');
|
|
88
|
+
popoverEl.setAttribute('popover', '');
|
|
89
|
+
document.body.appendChild(popoverEl);
|
|
90
|
+
return popoverEl;
|
|
91
|
+
}
|
|
92
|
+
// --- Modal ---
|
|
93
|
+
function modal(options) {
|
|
94
|
+
const d = ensureDialog();
|
|
95
|
+
// Clean up any lingering dismiss state from a previous close
|
|
96
|
+
d.removeAttribute('data-dismissing');
|
|
97
|
+
// Immediately dismiss popover — modal takes over the top layer
|
|
98
|
+
hidePopoverImmediate();
|
|
99
|
+
// Last wins: close current modal if open
|
|
100
|
+
if (d.open) {
|
|
101
|
+
d.close();
|
|
102
|
+
if (modalResolve) {
|
|
103
|
+
modalResolve(undefined);
|
|
104
|
+
modalResolve = null;
|
|
105
|
+
}
|
|
106
|
+
if (modalOnClose) {
|
|
107
|
+
modalOnClose();
|
|
108
|
+
modalOnClose = undefined;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
d.innerHTML = '';
|
|
112
|
+
options.render(d);
|
|
113
|
+
modalOnClose = options.onClose;
|
|
114
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
115
|
+
modalResolve = resolve;
|
|
116
|
+
d.showModal();
|
|
117
|
+
return promise;
|
|
118
|
+
}
|
|
119
|
+
function closeModal(value) {
|
|
120
|
+
if (!dialog || !dialog.open)
|
|
121
|
+
return;
|
|
122
|
+
const resolve = modalResolve;
|
|
123
|
+
const onClose = modalOnClose;
|
|
124
|
+
const dialogRef = dialog;
|
|
125
|
+
modalResolve = null;
|
|
126
|
+
modalOnClose = undefined;
|
|
127
|
+
animateDismiss(dialogRef, () => {
|
|
128
|
+
if (dialogRef && dialogRef.open) {
|
|
129
|
+
dialogRef.close();
|
|
130
|
+
if (resolve)
|
|
131
|
+
resolve(value);
|
|
132
|
+
if (onClose)
|
|
133
|
+
onClose();
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
// --- Toast ---
|
|
138
|
+
/** Remove dead toasts (dismissed or animation-finished) from container. */
|
|
139
|
+
function clearDeadToasts(container) {
|
|
140
|
+
for (const child of [...container.children]) {
|
|
141
|
+
const el = child;
|
|
142
|
+
if (el.hasAttribute('data-dismissing')) {
|
|
143
|
+
el.remove();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function toast(options) {
|
|
148
|
+
const container = ensureToastContainer();
|
|
149
|
+
// Clean up dead toasts before adding a new one
|
|
150
|
+
clearDeadToasts(container);
|
|
151
|
+
const el = document.createElement('div');
|
|
152
|
+
el.setAttribute('data-overlay-toast', '');
|
|
153
|
+
const timeout = options.timeout ?? 0;
|
|
154
|
+
if (timeout === 0) {
|
|
155
|
+
el.setAttribute('data-toast-manual', '');
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
el.style.setProperty('--overlay-toast-duration', `${timeout}ms`);
|
|
159
|
+
}
|
|
160
|
+
options.render(el);
|
|
161
|
+
container.appendChild(el);
|
|
162
|
+
let dismissed = false;
|
|
163
|
+
const dismiss = () => {
|
|
164
|
+
if (dismissed)
|
|
165
|
+
return;
|
|
166
|
+
dismissed = true;
|
|
167
|
+
el.setAttribute('data-dismissing', '');
|
|
168
|
+
};
|
|
169
|
+
return { dismiss };
|
|
170
|
+
}
|
|
171
|
+
// --- Popover ---
|
|
172
|
+
function popover(options) {
|
|
173
|
+
const el = ensurePopover();
|
|
174
|
+
// Last wins: hide current popover if showing
|
|
175
|
+
cleanupPopoverAnchorObserver();
|
|
176
|
+
try {
|
|
177
|
+
el.hidePopover();
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
// Not shown — ignore
|
|
181
|
+
}
|
|
182
|
+
el.removeAttribute('data-dismissing');
|
|
183
|
+
el.innerHTML = '';
|
|
184
|
+
options.render(el);
|
|
185
|
+
// Anchor positioning
|
|
186
|
+
if (supportsAnchor) {
|
|
187
|
+
const anchorName = '--overlay-anchor';
|
|
188
|
+
options.anchor.style.setProperty('anchor-name', anchorName);
|
|
189
|
+
el.style.setProperty('position-anchor', anchorName);
|
|
190
|
+
el.style.removeProperty('top');
|
|
191
|
+
el.style.removeProperty('left');
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
const rect = options.anchor.getBoundingClientRect();
|
|
195
|
+
el.style.top = `${rect.bottom + globalThis.scrollY}px`;
|
|
196
|
+
el.style.left = `${rect.left + globalThis.scrollX}px`;
|
|
197
|
+
el.style.position = 'absolute';
|
|
198
|
+
}
|
|
199
|
+
el.showPopover();
|
|
200
|
+
// Watch for anchor disconnect
|
|
201
|
+
watchAnchorDisconnect(options.anchor);
|
|
202
|
+
}
|
|
203
|
+
function watchAnchorDisconnect(anchor) {
|
|
204
|
+
cleanupPopoverAnchorObserver();
|
|
205
|
+
const parent = anchor.parentNode;
|
|
206
|
+
if (!parent) {
|
|
207
|
+
closePopover();
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
popoverAnchorObserver = new MutationObserver(() => {
|
|
211
|
+
if (!document.contains(anchor)) {
|
|
212
|
+
closePopover();
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
popoverAnchorObserver.observe(parent, { childList: true });
|
|
216
|
+
}
|
|
217
|
+
/** Hide popover instantly without dismiss animation. */
|
|
218
|
+
function hidePopoverImmediate() {
|
|
219
|
+
cleanupPopoverAnchorObserver();
|
|
220
|
+
if (!popoverEl)
|
|
221
|
+
return;
|
|
222
|
+
try {
|
|
223
|
+
popoverEl.hidePopover();
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
// Not shown — ignore
|
|
227
|
+
}
|
|
228
|
+
popoverEl.removeAttribute('data-dismissing');
|
|
229
|
+
}
|
|
230
|
+
function cleanupPopoverAnchorObserver() {
|
|
231
|
+
if (popoverAnchorObserver) {
|
|
232
|
+
popoverAnchorObserver.disconnect();
|
|
233
|
+
popoverAnchorObserver = null;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function closePopover() {
|
|
237
|
+
cleanupPopoverAnchorObserver();
|
|
238
|
+
if (!popoverEl)
|
|
239
|
+
return;
|
|
240
|
+
// Check if popover is showing via matches(':popover-open')
|
|
241
|
+
let isOpen;
|
|
242
|
+
try {
|
|
243
|
+
isOpen = popoverEl.matches(':popover-open');
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
// :popover-open may not be supported — fall back
|
|
247
|
+
isOpen = popoverEl.hasAttribute('popover') && popoverEl.style.display !== 'none';
|
|
248
|
+
}
|
|
249
|
+
if (!isOpen)
|
|
250
|
+
return;
|
|
251
|
+
animateDismiss(popoverEl, () => {
|
|
252
|
+
try {
|
|
253
|
+
popoverEl.hidePopover();
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
// Already hidden
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
// --- Dismiss all ---
|
|
261
|
+
function dismissAll() {
|
|
262
|
+
// Close programmatic modal
|
|
263
|
+
if (dialog && dialog.open) {
|
|
264
|
+
const resolve = modalResolve;
|
|
265
|
+
const onClose = modalOnClose;
|
|
266
|
+
modalResolve = null;
|
|
267
|
+
modalOnClose = undefined;
|
|
268
|
+
dialog.removeAttribute('data-dismissing');
|
|
269
|
+
dialog.close();
|
|
270
|
+
if (resolve)
|
|
271
|
+
resolve(undefined);
|
|
272
|
+
if (onClose)
|
|
273
|
+
onClose();
|
|
274
|
+
}
|
|
275
|
+
// Hide programmatic popover
|
|
276
|
+
hidePopoverImmediate();
|
|
277
|
+
// Dismiss all toasts via CSS
|
|
278
|
+
if (toastContainer) {
|
|
279
|
+
for (const child of toastContainer.children) {
|
|
280
|
+
child.setAttribute('data-dismissing', '');
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
// Close declarative popovers found in the DOM
|
|
284
|
+
try {
|
|
285
|
+
for (const el of document.querySelectorAll(':popover-open')) {
|
|
286
|
+
el.hidePopover();
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
// :popover-open not supported
|
|
291
|
+
}
|
|
292
|
+
// Close declarative dialogs found in the DOM (skip our own)
|
|
293
|
+
for (const el of document.querySelectorAll('dialog[open]')) {
|
|
294
|
+
if (el !== dialog)
|
|
295
|
+
el.close();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return {
|
|
299
|
+
modal,
|
|
300
|
+
closeModal,
|
|
301
|
+
toast,
|
|
302
|
+
popover,
|
|
303
|
+
closePopover,
|
|
304
|
+
dismissAll,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
//# sourceMappingURL=overlay.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay.service.js","sourceRoot":"","sources":["../../../src/overlay/overlay.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Overlay Service Types
|
|
3
|
+
*
|
|
4
|
+
* Programmatic API for overlays. For simple cases, use declarative HTML
|
|
5
|
+
* attributes (commandfor/command + popover/dialog) — zero JS required.
|
|
6
|
+
* This service provides the imperative path for dynamic content,
|
|
7
|
+
* programmatic triggers, and complex workflows. dismissAll() is
|
|
8
|
+
* DOM-aware and closes both programmatic and declarative overlays.
|
|
9
|
+
*/
|
|
10
|
+
export interface OverlayService {
|
|
11
|
+
modal<T = undefined>(options: ModalOptions<T>): Promise<T | undefined>;
|
|
12
|
+
closeModal<T>(value?: T): void;
|
|
13
|
+
popover(options: PopoverOptions): void;
|
|
14
|
+
closePopover(): void;
|
|
15
|
+
toast(options: ToastOptions): {
|
|
16
|
+
dismiss(): void;
|
|
17
|
+
};
|
|
18
|
+
/** Close all open overlays — programmatic and declarative — and toasts. */
|
|
19
|
+
dismissAll(): void;
|
|
20
|
+
}
|
|
21
|
+
export interface ModalOptions<T = undefined> {
|
|
22
|
+
render(dialog: HTMLDialogElement): void;
|
|
23
|
+
onClose?(): void;
|
|
24
|
+
}
|
|
25
|
+
export interface PopoverOptions {
|
|
26
|
+
anchor: HTMLElement;
|
|
27
|
+
render(el: HTMLDivElement): void;
|
|
28
|
+
}
|
|
29
|
+
export interface ToastOptions {
|
|
30
|
+
render(el: HTMLDivElement): void;
|
|
31
|
+
/** Auto-dismiss timeout in ms. Default 0 (manual dismiss only). Set to a positive ms value for auto-dismiss via CSS animation. */
|
|
32
|
+
timeout?: number;
|
|
33
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Overlay Service Types
|
|
3
|
+
*
|
|
4
|
+
* Programmatic API for overlays. For simple cases, use declarative HTML
|
|
5
|
+
* attributes (commandfor/command + popover/dialog) — zero JS required.
|
|
6
|
+
* This service provides the imperative path for dynamic content,
|
|
7
|
+
* programmatic triggers, and complex workflows. dismissAll() is
|
|
8
|
+
* DOM-aware and closes both programmatic and declarative overlays.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=overlay.type.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay.type.js","sourceRoot":"","sources":["../../../src/overlay/overlay.type.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Renderer
|
|
3
|
+
*
|
|
4
|
+
* Shared rendering logic for SPA and Hash routers:
|
|
5
|
+
* - Route hierarchy traversal with nested slot rendering
|
|
6
|
+
* - Component loading, data fetching, and HTML rendering
|
|
7
|
+
* - Markdown render waiting
|
|
8
|
+
* - Document title updates
|
|
9
|
+
*/
|
|
10
|
+
import type { MatchedRoute, RouteConfig, RouteInfo } from '../../type/route.type.ts';
|
|
11
|
+
import { RouteCore } from '../../route/route.core.ts';
|
|
12
|
+
/**
|
|
13
|
+
* Abstract base for renderers that share the page rendering pipeline.
|
|
14
|
+
* Subclasses provide navigation mechanics (Navigation API, hashchange, etc.).
|
|
15
|
+
*/
|
|
16
|
+
export declare abstract class BaseRenderer {
|
|
17
|
+
protected core: RouteCore;
|
|
18
|
+
protected slot: Element | null;
|
|
19
|
+
constructor(core: RouteCore);
|
|
20
|
+
/**
|
|
21
|
+
* Render a matched page route with nested route support.
|
|
22
|
+
*/
|
|
23
|
+
protected renderPage(routeInfo: RouteInfo, matched: MatchedRoute, signal: AbortSignal): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Render a single route's content.
|
|
26
|
+
*/
|
|
27
|
+
protected renderRouteContent(routeInfo: RouteInfo, route: RouteConfig, signal: AbortSignal, isLeaf?: boolean): Promise<{
|
|
28
|
+
html: string;
|
|
29
|
+
title?: string;
|
|
30
|
+
}>;
|
|
31
|
+
/**
|
|
32
|
+
* Wait for a <mark-down> element to finish rendering.
|
|
33
|
+
*/
|
|
34
|
+
protected waitForMarkdownRender(element: HTMLElement): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Update document.title from getTitle() result.
|
|
37
|
+
*/
|
|
38
|
+
protected updateTitle(pageTitle?: string): void;
|
|
39
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Renderer
|
|
3
|
+
*
|
|
4
|
+
* Shared rendering logic for SPA and Hash routers:
|
|
5
|
+
* - Route hierarchy traversal with nested slot rendering
|
|
6
|
+
* - Component loading, data fetching, and HTML rendering
|
|
7
|
+
* - Markdown render waiting
|
|
8
|
+
* - Document title updates
|
|
9
|
+
*/
|
|
10
|
+
import defaultPageComponent from "../../component/page.component.js";
|
|
11
|
+
import { DEFAULT_ROOT_ROUTE } from "../../route/route.core.js";
|
|
12
|
+
import { logger } from "../../util/logger.util.js";
|
|
13
|
+
const MARKDOWN_RENDER_TIMEOUT = 5000;
|
|
14
|
+
/**
|
|
15
|
+
* Abstract base for renderers that share the page rendering pipeline.
|
|
16
|
+
* Subclasses provide navigation mechanics (Navigation API, hashchange, etc.).
|
|
17
|
+
*/
|
|
18
|
+
export class BaseRenderer {
|
|
19
|
+
core;
|
|
20
|
+
slot = null;
|
|
21
|
+
constructor(core) {
|
|
22
|
+
this.core = core;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Render a matched page route with nested route support.
|
|
26
|
+
*/
|
|
27
|
+
async renderPage(routeInfo, matched, signal) {
|
|
28
|
+
if (!this.slot)
|
|
29
|
+
return;
|
|
30
|
+
try {
|
|
31
|
+
const hierarchy = this.core.buildRouteHierarchy(routeInfo.pattern);
|
|
32
|
+
logger.render('page', routeInfo.pattern, `hierarchy: ${hierarchy.join(' > ')}`);
|
|
33
|
+
let currentSlot = this.slot;
|
|
34
|
+
let pageTitle;
|
|
35
|
+
for (let i = 0; i < hierarchy.length; i++) {
|
|
36
|
+
if (signal.aborted)
|
|
37
|
+
return;
|
|
38
|
+
const routePattern = hierarchy[i];
|
|
39
|
+
const isLeaf = i === hierarchy.length - 1;
|
|
40
|
+
let route = this.core.matcher.findRoute(routePattern);
|
|
41
|
+
if (!route && routePattern === this.core.root) {
|
|
42
|
+
route = { ...DEFAULT_ROOT_ROUTE, pattern: this.core.root };
|
|
43
|
+
}
|
|
44
|
+
if (!route) {
|
|
45
|
+
logger.render('skip', routePattern, 'route not found');
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const routeType = isLeaf ? 'leaf' : 'layout';
|
|
49
|
+
logger.render(routeType, routePattern, `${route.files?.ts ?? 'default'} → slot`);
|
|
50
|
+
// Skip wildcard route appearing as its own parent (prevents double-render)
|
|
51
|
+
if (route === matched.route && routePattern !== matched.route.pattern) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const { html, title } = await this.renderRouteContent(routeInfo, route, signal, isLeaf);
|
|
55
|
+
if (signal.aborted)
|
|
56
|
+
return;
|
|
57
|
+
currentSlot.setHTMLUnsafe(html);
|
|
58
|
+
if (title) {
|
|
59
|
+
pageTitle = title;
|
|
60
|
+
}
|
|
61
|
+
// Wait for <mark-down> to finish rendering its content
|
|
62
|
+
// (must happen before attributing slots — router-slot may be inside markdown)
|
|
63
|
+
const markDown = currentSlot.querySelector('mark-down');
|
|
64
|
+
if (markDown) {
|
|
65
|
+
await this.waitForMarkdownRender(markDown);
|
|
66
|
+
if (signal.aborted)
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// Attribute bare <router-slot> tags with this route's pattern
|
|
70
|
+
for (const slot of currentSlot.querySelectorAll('router-slot:not([pattern])')) {
|
|
71
|
+
slot.setAttribute('pattern', routePattern);
|
|
72
|
+
}
|
|
73
|
+
if (!isLeaf) {
|
|
74
|
+
const nestedSlot = currentSlot.querySelector(`router-slot[pattern="${CSS.escape(routePattern)}"]`);
|
|
75
|
+
if (nestedSlot) {
|
|
76
|
+
currentSlot = nestedSlot;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
logger.warn(`Route "${routePattern}" has no <router-slot> ` +
|
|
80
|
+
`for child routes to render into. ` +
|
|
81
|
+
`Add <router-slot></router-slot> to the parent template.`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (signal.aborted)
|
|
86
|
+
return;
|
|
87
|
+
this.updateTitle(pageTitle);
|
|
88
|
+
this.core.emit({
|
|
89
|
+
type: 'load',
|
|
90
|
+
pathname: routeInfo.pattern,
|
|
91
|
+
params: routeInfo.params,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
if (signal.aborted)
|
|
96
|
+
return;
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Render a single route's content.
|
|
102
|
+
*/
|
|
103
|
+
async renderRouteContent(routeInfo, route, signal, isLeaf) {
|
|
104
|
+
if (route.modulePath === DEFAULT_ROOT_ROUTE.modulePath) {
|
|
105
|
+
return { html: `<router-slot pattern="${route.pattern}"></router-slot>` };
|
|
106
|
+
}
|
|
107
|
+
const files = route.files ?? {};
|
|
108
|
+
const component = files.ts
|
|
109
|
+
? (await this.core.loadModule(files.ts)).default
|
|
110
|
+
: defaultPageComponent;
|
|
111
|
+
const context = await this.core.buildComponentContext(routeInfo, route, signal, isLeaf);
|
|
112
|
+
const data = await component.getData({ params: routeInfo.params, signal, context });
|
|
113
|
+
const html = component.renderHTML({ data, params: routeInfo.params, context });
|
|
114
|
+
const title = component.getTitle({ data, params: routeInfo.params, context });
|
|
115
|
+
return { html, title };
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Wait for a <mark-down> element to finish rendering.
|
|
119
|
+
*/
|
|
120
|
+
waitForMarkdownRender(element) {
|
|
121
|
+
return new Promise((resolve) => {
|
|
122
|
+
if (element.children.length > 0) {
|
|
123
|
+
resolve();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const timeout = setTimeout(() => {
|
|
127
|
+
observer.disconnect();
|
|
128
|
+
resolve();
|
|
129
|
+
}, MARKDOWN_RENDER_TIMEOUT);
|
|
130
|
+
const observer = new MutationObserver(() => {
|
|
131
|
+
if (element.children.length > 0) {
|
|
132
|
+
clearTimeout(timeout);
|
|
133
|
+
observer.disconnect();
|
|
134
|
+
resolve();
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
observer.observe(element, { childList: true });
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Update document.title from getTitle() result.
|
|
142
|
+
*/
|
|
143
|
+
updateTitle(pageTitle) {
|
|
144
|
+
if (pageTitle) {
|
|
145
|
+
document.title = pageTitle;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=base.renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.renderer.js","sourceRoot":"","sources":["../../../../src/renderer/spa/base.renderer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hash Router
|
|
3
|
+
*
|
|
4
|
+
* Lightweight client-side router for leaf mode mini-apps.
|
|
5
|
+
* Uses hashchange events + RouteCore pattern matching.
|
|
6
|
+
*
|
|
7
|
+
* Routes are defined inline by the consumer, not from the main manifest.
|
|
8
|
+
* Coexists with SpaHtmlRouter in root mode (SPA router skips hash changes).
|
|
9
|
+
*/
|
|
10
|
+
import type { RoutesManifest } from '../../type/route.type.ts';
|
|
11
|
+
import type { ContextProvider } from '../../component/abstract.component.ts';
|
|
12
|
+
import { RouteCore } from '../../route/route.core.ts';
|
|
13
|
+
import { BaseRenderer } from './base.renderer.ts';
|
|
14
|
+
/**
|
|
15
|
+
* A single hash route definition with a lazy module loader.
|
|
16
|
+
* @experimental
|
|
17
|
+
*/
|
|
18
|
+
export interface HashRouteConfig {
|
|
19
|
+
/** URLPattern pathname pattern (e.g. '/settings', '/users/:id'). */
|
|
20
|
+
pattern: string;
|
|
21
|
+
/** Lazy loader returning a module with a default PageComponent export. */
|
|
22
|
+
loader: () => Promise<unknown>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Options for creating a HashRouter.
|
|
26
|
+
* @experimental
|
|
27
|
+
*/
|
|
28
|
+
export interface HashRouterOptions {
|
|
29
|
+
/** Inline route definitions. */
|
|
30
|
+
routes: HashRouteConfig[];
|
|
31
|
+
/** CSS selector or element to render into. Defaults to 'hash-slot'. */
|
|
32
|
+
slot?: string | Element;
|
|
33
|
+
/** Enriches every ComponentContext with app-level services. */
|
|
34
|
+
extendContext?: ContextProvider;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Hash-based mini-app router for leaf mode pages.
|
|
38
|
+
*
|
|
39
|
+
* Listens to `hashchange`, maps `#/path` → pattern match via RouteCore,
|
|
40
|
+
* renders matched PageComponent into a slot element.
|
|
41
|
+
*
|
|
42
|
+
* @experimental
|
|
43
|
+
*/
|
|
44
|
+
export declare class HashRouter extends BaseRenderer {
|
|
45
|
+
private boundHandler;
|
|
46
|
+
constructor(manifest: RoutesManifest, options?: {
|
|
47
|
+
extendContext?: ContextProvider;
|
|
48
|
+
});
|
|
49
|
+
/**
|
|
50
|
+
* Initialize: find slot, attach hashchange listener, render initial hash.
|
|
51
|
+
*/
|
|
52
|
+
initialize(slot?: string | Element): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Navigate to a hash path. Triggers hashchange → render.
|
|
55
|
+
*/
|
|
56
|
+
navigate(hash: string): void;
|
|
57
|
+
/**
|
|
58
|
+
* Add event listener for router events.
|
|
59
|
+
*/
|
|
60
|
+
addEventListener(listener: Parameters<RouteCore['addEventListener']>[0]): () => void;
|
|
61
|
+
/**
|
|
62
|
+
* Remove event listeners and release references.
|
|
63
|
+
*/
|
|
64
|
+
dispose(): void;
|
|
65
|
+
/**
|
|
66
|
+
* Handle a hashchange event: parse hash, match, render.
|
|
67
|
+
*/
|
|
68
|
+
private handleHashChange;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Create and initialize a hash router for a leaf-mode mini-app.
|
|
72
|
+
*
|
|
73
|
+
* The router instance is stored on `globalThis.__emroute_hash_router` for
|
|
74
|
+
* programmatic access. Calling twice returns the existing router with a warning.
|
|
75
|
+
*
|
|
76
|
+
* @experimental
|
|
77
|
+
*/
|
|
78
|
+
export declare function createHashRouter(options: HashRouterOptions): Promise<HashRouter>;
|