@emkodev/emroute 1.8.0 → 1.8.2-beta.2
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 +1 -1
- package/core/renderer/html.renderer.ts +1 -1
- package/core/renderer/md.renderer.ts +1 -1
- package/core/renderer/ssr.renderer.ts +21 -1
- package/core/server/emroute.server.ts +45 -52
- package/core/util/widget-resolve.util.ts +2 -2
- package/core/widget/widget.registry.ts +29 -2
- package/dist/core/renderer/html.renderer.js +1 -1
- package/dist/core/renderer/html.renderer.js.map +1 -1
- package/dist/core/renderer/md.renderer.js +1 -1
- package/dist/core/renderer/md.renderer.js.map +1 -1
- package/dist/core/renderer/ssr.renderer.d.ts +4 -1
- package/dist/core/renderer/ssr.renderer.js +20 -0
- package/dist/core/renderer/ssr.renderer.js.map +1 -1
- package/dist/core/server/emroute.server.d.ts +0 -2
- package/dist/core/server/emroute.server.js +36 -47
- package/dist/core/server/emroute.server.js.map +1 -1
- package/dist/core/util/widget-resolve.util.d.ts +1 -3
- package/dist/core/util/widget-resolve.util.js +2 -2
- package/dist/core/util/widget-resolve.util.js.map +1 -1
- package/dist/core/widget/widget.registry.d.ts +8 -0
- package/dist/core/widget/widget.registry.js +28 -2
- package/dist/core/widget/widget.registry.js.map +1 -1
- package/dist/emroute.js +227 -66
- package/dist/emroute.js.map +13 -13
- package/dist/runtime/abstract.runtime.js +7 -3
- package/dist/runtime/abstract.runtime.js.map +1 -1
- package/dist/runtime/fetch.runtime.js +7 -1
- package/dist/runtime/fetch.runtime.js.map +1 -1
- package/dist/server/build.util.d.ts +1 -1
- package/dist/server/build.util.js +10 -28
- package/dist/server/build.util.js.map +1 -1
- package/dist/src/element/component.element.d.ts +4 -1
- package/dist/src/element/component.element.js +4 -1
- package/dist/src/element/component.element.js.map +1 -1
- package/dist/src/overlay/mod.d.ts +1 -1
- package/dist/src/overlay/overlay.service.js +136 -6
- package/dist/src/overlay/overlay.service.js.map +1 -1
- package/dist/src/overlay/overlay.type.d.ts +28 -4
- package/dist/src/renderer/spa/emroute.app.d.ts +3 -0
- package/dist/src/renderer/spa/emroute.app.js +18 -3
- package/dist/src/renderer/spa/emroute.app.js.map +1 -1
- package/package.json +2 -2
- package/runtime/abstract.runtime.ts +11 -5
- package/runtime/fetch.runtime.ts +6 -1
- package/server/build.util.ts +10 -30
- package/src/element/component.element.ts +4 -1
- package/src/overlay/mod.ts +1 -1
- package/src/overlay/overlay.service.ts +158 -8
- package/src/overlay/overlay.type.ts +27 -2
- package/src/renderer/spa/emroute.app.ts +20 -3
|
@@ -7,14 +7,29 @@
|
|
|
7
7
|
* programmatic triggers, and complex workflows. dismissAll() is
|
|
8
8
|
* DOM-aware and closes both programmatic and declarative overlays.
|
|
9
9
|
*/
|
|
10
|
+
export interface ToastHandle {
|
|
11
|
+
id: number;
|
|
12
|
+
dismiss(): void;
|
|
13
|
+
update(opts: {
|
|
14
|
+
message?: string;
|
|
15
|
+
type?: string;
|
|
16
|
+
timeout?: number;
|
|
17
|
+
}): void;
|
|
18
|
+
}
|
|
19
|
+
export type ToastType = 'success' | 'error' | 'warning' | 'info';
|
|
20
|
+
export interface ToastFunction {
|
|
21
|
+
(options: ToastOptions): ToastHandle;
|
|
22
|
+
success(message: string, timeout?: number): ToastHandle;
|
|
23
|
+
error(message: string, timeout?: number): ToastHandle;
|
|
24
|
+
warning(message: string, timeout?: number): ToastHandle;
|
|
25
|
+
info(message: string, timeout?: number): ToastHandle;
|
|
26
|
+
}
|
|
10
27
|
export interface OverlayService {
|
|
11
28
|
modal<T = undefined>(options: ModalOptions<T>): Promise<T | undefined>;
|
|
12
29
|
closeModal<T>(value?: T): void;
|
|
13
30
|
popover(options: PopoverOptions): void;
|
|
14
31
|
closePopover(): void;
|
|
15
|
-
toast
|
|
16
|
-
dismiss(): void;
|
|
17
|
-
};
|
|
32
|
+
toast: ToastFunction;
|
|
18
33
|
/** Close all open overlays — programmatic and declarative — and toasts. */
|
|
19
34
|
dismissAll(): void;
|
|
20
35
|
}
|
|
@@ -27,7 +42,16 @@ export interface PopoverOptions {
|
|
|
27
42
|
render(el: HTMLDivElement): void;
|
|
28
43
|
}
|
|
29
44
|
export interface ToastOptions {
|
|
30
|
-
render
|
|
45
|
+
/** Custom render function — escape hatch for full control. */
|
|
46
|
+
render?(el: HTMLDivElement): void;
|
|
47
|
+
/** Text content (alternative to render). */
|
|
48
|
+
message?: string;
|
|
49
|
+
/** Toast type — sets `data-toast-type` attribute. */
|
|
50
|
+
type?: ToastType;
|
|
51
|
+
/** Confirm button label. Shows button, makes toast manual, returns PromiseLike<boolean>. */
|
|
52
|
+
confirm?: string;
|
|
53
|
+
/** Reject button label. */
|
|
54
|
+
reject?: string;
|
|
31
55
|
/** Auto-dismiss timeout in ms. Default 0 (manual dismiss only). Set to a positive ms value for auto-dismiss via CSS animation. */
|
|
32
56
|
timeout?: number;
|
|
33
57
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { Emroute } from '../../../core/server/emroute.server.ts';
|
|
9
9
|
import type { NavigateOptions } from '../../../core/type/route.type.ts';
|
|
10
|
+
import type { ContextProvider } from '../../../core/type/component.type.ts';
|
|
10
11
|
import { type BasePath } from '../../../core/server/emroute.server.ts';
|
|
11
12
|
/** Options for `createEmrouteApp`. */
|
|
12
13
|
export interface EmrouteAppOptions {
|
|
@@ -36,6 +37,8 @@ export declare function createEmrouteApp(server: Emroute, options?: EmrouteAppOp
|
|
|
36
37
|
export interface BootOptions extends EmrouteAppOptions {
|
|
37
38
|
/** Override the server origin (defaults to `location.origin`). */
|
|
38
39
|
origin?: string;
|
|
40
|
+
/** Context provider — enriches every ComponentContext (pages and widgets). */
|
|
41
|
+
extendContext?: ContextProvider;
|
|
39
42
|
}
|
|
40
43
|
/**
|
|
41
44
|
* Boot the browser app from runtime manifests.
|
|
@@ -21,9 +21,17 @@ export class EmrouteApp {
|
|
|
21
21
|
slot = null;
|
|
22
22
|
abortController = null;
|
|
23
23
|
constructor(server, options) {
|
|
24
|
-
const bp = options?.basePath ?? DEFAULT_BASE_PATH;
|
|
25
24
|
this.server = server;
|
|
26
|
-
|
|
25
|
+
if (options?.basePath) {
|
|
26
|
+
this.appBase = options.basePath.app;
|
|
27
|
+
}
|
|
28
|
+
else if (typeof document !== 'undefined') {
|
|
29
|
+
const base = document.querySelector('base')?.getAttribute('href');
|
|
30
|
+
this.appBase = base ? base.replace(/\/$/, '') : DEFAULT_BASE_PATH.app;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
this.appBase = DEFAULT_BASE_PATH.app;
|
|
34
|
+
}
|
|
27
35
|
}
|
|
28
36
|
async initialize(slotSelector = 'router-slot') {
|
|
29
37
|
this.slot = document.querySelector(slotSelector);
|
|
@@ -179,9 +187,11 @@ export async function bootEmrouteApp(options) {
|
|
|
179
187
|
: [];
|
|
180
188
|
// Build lazy module loaders for all route + widget + element modules
|
|
181
189
|
const moduleLoaders = buildLazyLoaders(routeTree, widgetEntries, elementEntries, runtime);
|
|
182
|
-
// Register widgets
|
|
190
|
+
// Register widgets: lazy in registry (loaded on demand during render),
|
|
191
|
+
// lazy in DOM (module loads on connectedCallback for hydration).
|
|
183
192
|
const widgets = new WidgetRegistry();
|
|
184
193
|
for (const entry of widgetEntries) {
|
|
194
|
+
widgets.addLazy(entry.name, entry.modulePath);
|
|
185
195
|
ComponentElement.registerLazy(entry.name, moduleLoaders[entry.modulePath]);
|
|
186
196
|
}
|
|
187
197
|
// Register custom elements — import all modules, define when loaded
|
|
@@ -198,6 +208,10 @@ export async function bootEmrouteApp(options) {
|
|
|
198
208
|
});
|
|
199
209
|
}
|
|
200
210
|
}
|
|
211
|
+
// Wire context provider for both pages (Pipeline) and widgets (ComponentElement)
|
|
212
|
+
if (options?.extendContext) {
|
|
213
|
+
ComponentElement.setContextProvider(options.extendContext);
|
|
214
|
+
}
|
|
201
215
|
// Create Emroute instance (same class as SSR)
|
|
202
216
|
const mdRenderer = MarkdownElement.getConfiguredRenderer();
|
|
203
217
|
const server = await Emroute.create({
|
|
@@ -205,6 +219,7 @@ export async function bootEmrouteApp(options) {
|
|
|
205
219
|
widgets,
|
|
206
220
|
moduleLoaders,
|
|
207
221
|
...(mdRenderer ? { markdownRenderer: mdRenderer } : {}),
|
|
222
|
+
...(options?.extendContext ? { extendContext: options.extendContext } : {}),
|
|
208
223
|
}, runtime);
|
|
209
224
|
return createEmrouteApp(server, options);
|
|
210
225
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emroute.app.js","sourceRoot":"","sources":["../../../../src/renderer/spa/emroute.app.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,wCAAwC,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;
|
|
1
|
+
{"version":3,"file":"emroute.app.js","sourceRoot":"","sources":["../../../../src/renderer/spa/emroute.app.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emkodev/emroute",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.2-beta.2",
|
|
4
4
|
"description": "File-based (but storage-agnostic) router with triple rendering (SPA, SSR HTML, SSR Markdown). Zero dependencies.",
|
|
5
5
|
"license": "BSD-3-Clause",
|
|
6
6
|
"author": "emko.dev",
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
}
|
|
100
100
|
},
|
|
101
101
|
"devDependencies": {
|
|
102
|
-
"@emkodev/emkoma": "^0.1.
|
|
102
|
+
"@emkodev/emkoma": "^0.1.2",
|
|
103
103
|
"@eslint/js": "^10.0.1",
|
|
104
104
|
"@types/bun": "^1.3.9",
|
|
105
105
|
"eslint": "^10.0.2",
|
|
@@ -769,11 +769,17 @@ export abstract class Runtime {
|
|
|
769
769
|
|
|
770
770
|
const files: { html?: string; md?: string; css?: string } = {};
|
|
771
771
|
let hasFiles = false;
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
772
|
+
const companionResults = await Promise.all(
|
|
773
|
+
COMPANION_EXTENSIONS.map(async (ext) => {
|
|
774
|
+
const companionFile = `${name}.widget.${ext}`;
|
|
775
|
+
const companionPath = `${trailingDir}${name}/${companionFile}`;
|
|
776
|
+
const exists = (await this.query(companionPath)).status !== 404;
|
|
777
|
+
return { ext, exists, path: `${prefix}${name}/${companionFile}` };
|
|
778
|
+
}),
|
|
779
|
+
);
|
|
780
|
+
for (const { ext, exists, path } of companionResults) {
|
|
781
|
+
if (exists) {
|
|
782
|
+
files[ext] = path;
|
|
777
783
|
hasFiles = true;
|
|
778
784
|
}
|
|
779
785
|
}
|
package/runtime/fetch.runtime.ts
CHANGED
|
@@ -59,7 +59,12 @@ export class FetchRuntime extends Runtime {
|
|
|
59
59
|
const response = await fetch(url);
|
|
60
60
|
const js = await response.text();
|
|
61
61
|
const blob = new Blob([js], { type: 'application/javascript' });
|
|
62
|
-
|
|
62
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
63
|
+
try {
|
|
64
|
+
return await import(objectUrl);
|
|
65
|
+
} finally {
|
|
66
|
+
URL.revokeObjectURL(objectUrl);
|
|
67
|
+
}
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
private toUrl(resource: FetchParams[0]): string {
|
package/server/build.util.ts
CHANGED
|
@@ -60,17 +60,17 @@ const DEFAULT_BUNDLE_PATHS = { emroute: '/emroute.js', app: '/app.js' };
|
|
|
60
60
|
* - Updated manifests — route tree and widget manifest reference .js paths
|
|
61
61
|
* - emroute.js — pre-built from dist/ (copied into runtime)
|
|
62
62
|
* - app.js — consumer entry point (transpiled from .ts, no bundler)
|
|
63
|
-
* -
|
|
63
|
+
* - importmap.json — merged import map (emroute externals + consumer entries)
|
|
64
64
|
*/
|
|
65
65
|
export async function buildClientBundles(options: BuildOptions): Promise<void> {
|
|
66
66
|
const { runtime, root, spa, entryPoint } = options;
|
|
67
|
-
if (spa === 'none') return;
|
|
68
|
-
|
|
69
67
|
const paths = options.bundlePaths ?? DEFAULT_BUNDLE_PATHS;
|
|
70
68
|
|
|
71
69
|
// Merge .ts modules → .js with inlined companions, update manifests
|
|
72
70
|
await mergeModules(runtime);
|
|
73
71
|
|
|
72
|
+
if (spa === 'none') return;
|
|
73
|
+
|
|
74
74
|
// Copy pre-built emroute.js from the package dist/
|
|
75
75
|
const consumerRequire = createRequire(root + '/');
|
|
76
76
|
const emrouteJsPath = resolvePrebuiltBundle(consumerRequire);
|
|
@@ -100,8 +100,8 @@ export async function buildClientBundles(options: BuildOptions): Promise<void> {
|
|
|
100
100
|
} catch { /* no main.css on disk — fine */ }
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
// Write
|
|
104
|
-
await
|
|
103
|
+
// Write merged import map — server reads this when generating the shell
|
|
104
|
+
await writeImportMap(runtime, paths);
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
/**
|
|
@@ -122,15 +122,12 @@ function resolvePrebuiltBundle(require: NodeRequire): string {
|
|
|
122
122
|
return resolve(process.cwd(), 'dist/emroute.js');
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
// ──
|
|
125
|
+
// ── Import map ───────────────────────────────────────────────────────
|
|
126
126
|
|
|
127
|
-
async function
|
|
127
|
+
async function writeImportMap(
|
|
128
128
|
runtime: Runtime,
|
|
129
129
|
paths: { emroute: string; app: string },
|
|
130
130
|
): Promise<void> {
|
|
131
|
-
if ((await runtime.query('/index.html')).status !== 404) return;
|
|
132
|
-
|
|
133
|
-
// Base emroute imports
|
|
134
131
|
const imports: Record<string, string> = {};
|
|
135
132
|
for (const pkg of EMROUTE_EXTERNALS) {
|
|
136
133
|
imports[pkg] = paths.emroute;
|
|
@@ -147,26 +144,9 @@ async function writeShell(
|
|
|
147
144
|
}
|
|
148
145
|
}
|
|
149
146
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
<html>
|
|
154
|
-
<head>
|
|
155
|
-
<meta charset="utf-8">
|
|
156
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
157
|
-
<title>emroute</title>
|
|
158
|
-
<style>@view-transition { navigation: auto; } router-slot { display: contents; }</style>
|
|
159
|
-
</head>
|
|
160
|
-
<body>
|
|
161
|
-
<router-slot></router-slot>
|
|
162
|
-
<script type="importmap">
|
|
163
|
-
${importMap}
|
|
164
|
-
</script>
|
|
165
|
-
<script type="module" src="${paths.app}"></script>
|
|
166
|
-
</body>
|
|
167
|
-
</html>`;
|
|
168
|
-
|
|
169
|
-
await runtime.command('/index.html', { body: html });
|
|
147
|
+
await runtime.command('/importmap.json', {
|
|
148
|
+
body: JSON.stringify({ imports }, null, 2),
|
|
149
|
+
});
|
|
170
150
|
}
|
|
171
151
|
|
|
172
152
|
// ── Module merging ───────────────────────────────────────────────────
|
|
@@ -39,7 +39,10 @@ export class ComponentElement<TParams, TData> extends HTMLElementBase {
|
|
|
39
39
|
/** App-level context provider set once during router initialization. */
|
|
40
40
|
private static extendContext: ContextProvider | undefined;
|
|
41
41
|
|
|
42
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* Register (or clear) the context provider that enriches every widget's ComponentContext.
|
|
44
|
+
* @deprecated Use `bootEmrouteApp({ extendContext })` instead — it wires both pages and widgets.
|
|
45
|
+
*/
|
|
43
46
|
static setContextProvider(provider: ContextProvider | undefined): void {
|
|
44
47
|
ComponentElement.extendContext = provider;
|
|
45
48
|
}
|
package/src/overlay/mod.ts
CHANGED
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
* dismissAll() closes both programmatic and declarative overlays.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
export type { ModalOptions, OverlayService, PopoverOptions, ToastOptions } from './overlay.type.ts';
|
|
9
|
+
export type { ModalOptions, OverlayService, PopoverOptions, ToastHandle, ToastOptions } from './overlay.type.ts';
|
|
10
10
|
export { createOverlayService } from './overlay.service.ts';
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* via DOM queries.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import type { ModalOptions, OverlayService, PopoverOptions, ToastOptions } from './overlay.type.ts';
|
|
14
|
+
import type { ModalOptions, OverlayService, PopoverOptions, ToastFunction, ToastHandle, ToastOptions } from './overlay.type.ts';
|
|
15
15
|
import { overlayCSS } from './overlay.css.ts';
|
|
16
16
|
|
|
17
17
|
const ANIMATION_SAFETY_TIMEOUT = 300;
|
|
@@ -35,6 +35,86 @@ function animateDismiss(el: HTMLElement, onDone: () => void): void {
|
|
|
35
35
|
setTimeout(finish, ANIMATION_SAFETY_TIMEOUT);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Populate a toast element from a cloned template.
|
|
40
|
+
* Sets `[data-toast-message]` textContent, `data-toast-type` attribute,
|
|
41
|
+
* and unhides confirm/reject buttons when labels are provided.
|
|
42
|
+
*/
|
|
43
|
+
function fillTemplate(
|
|
44
|
+
el: HTMLElement,
|
|
45
|
+
options: ToastOptions,
|
|
46
|
+
onConfirm?: () => void,
|
|
47
|
+
onReject?: () => void,
|
|
48
|
+
): void {
|
|
49
|
+
const msgEl = el.querySelector('[data-toast-message]');
|
|
50
|
+
if (msgEl && options.message) {
|
|
51
|
+
msgEl.textContent = options.message;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (options.type) {
|
|
55
|
+
el.setAttribute('data-toast-type', options.type);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const confirmBtn = el.querySelector('[data-toast-confirm]') as HTMLElement | null;
|
|
59
|
+
if (confirmBtn) {
|
|
60
|
+
if (options.confirm) {
|
|
61
|
+
confirmBtn.textContent = options.confirm;
|
|
62
|
+
confirmBtn.hidden = false;
|
|
63
|
+
if (onConfirm) confirmBtn.addEventListener('click', onConfirm, { once: true });
|
|
64
|
+
} else {
|
|
65
|
+
confirmBtn.hidden = true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const rejectBtn = el.querySelector('[data-toast-reject]') as HTMLElement | null;
|
|
70
|
+
if (rejectBtn) {
|
|
71
|
+
if (options.reject) {
|
|
72
|
+
rejectBtn.textContent = options.reject;
|
|
73
|
+
rejectBtn.hidden = false;
|
|
74
|
+
if (onReject) rejectBtn.addEventListener('click', onReject, { once: true });
|
|
75
|
+
} else {
|
|
76
|
+
rejectBtn.hidden = true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Build toast inner content when no `<template id="overlay-toast">` exists.
|
|
83
|
+
* Creates a `<span>` with the message text, plus confirm/reject buttons
|
|
84
|
+
* if labels are provided.
|
|
85
|
+
*/
|
|
86
|
+
function buildFallback(
|
|
87
|
+
el: HTMLElement,
|
|
88
|
+
options: ToastOptions,
|
|
89
|
+
onConfirm?: () => void,
|
|
90
|
+
onReject?: () => void,
|
|
91
|
+
): void {
|
|
92
|
+
if (options.type) {
|
|
93
|
+
el.setAttribute('data-toast-type', options.type);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const span = document.createElement('span');
|
|
97
|
+
span.setAttribute('data-toast-message', '');
|
|
98
|
+
span.textContent = options.message ?? '';
|
|
99
|
+
el.appendChild(span);
|
|
100
|
+
|
|
101
|
+
if (options.confirm) {
|
|
102
|
+
const btn = document.createElement('button');
|
|
103
|
+
btn.setAttribute('data-toast-confirm', '');
|
|
104
|
+
btn.textContent = options.confirm;
|
|
105
|
+
if (onConfirm) btn.addEventListener('click', onConfirm, { once: true });
|
|
106
|
+
el.appendChild(btn);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (options.reject) {
|
|
110
|
+
const btn = document.createElement('button');
|
|
111
|
+
btn.setAttribute('data-toast-reject', '');
|
|
112
|
+
btn.textContent = options.reject;
|
|
113
|
+
if (onReject) btn.addEventListener('click', onReject, { once: true });
|
|
114
|
+
el.appendChild(btn);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
38
118
|
export function createOverlayService(): OverlayService {
|
|
39
119
|
let styleInjected = false;
|
|
40
120
|
|
|
@@ -167,7 +247,7 @@ export function createOverlayService(): OverlayService {
|
|
|
167
247
|
}
|
|
168
248
|
}
|
|
169
249
|
|
|
170
|
-
function toast(options: ToastOptions):
|
|
250
|
+
function toast(options: ToastOptions): ToastHandle {
|
|
171
251
|
const container = ensureToastContainer();
|
|
172
252
|
|
|
173
253
|
// Clean up dead toasts before adding a new one
|
|
@@ -176,26 +256,96 @@ export function createOverlayService(): OverlayService {
|
|
|
176
256
|
const el = document.createElement('div');
|
|
177
257
|
el.setAttribute('data-overlay-toast', '');
|
|
178
258
|
|
|
179
|
-
const
|
|
259
|
+
const isConfirmation = !!(options.confirm || options.reject);
|
|
260
|
+
const timeout = isConfirmation ? 0 : (options.timeout ?? 0);
|
|
180
261
|
if (timeout === 0) {
|
|
181
262
|
el.setAttribute('data-toast-manual', '');
|
|
182
263
|
} else {
|
|
183
264
|
el.style.setProperty('--overlay-toast-duration', `${timeout}ms`);
|
|
184
265
|
}
|
|
185
266
|
|
|
186
|
-
|
|
267
|
+
// Confirmation promise plumbing
|
|
268
|
+
let confirmResolve: ((value: boolean) => void) | undefined;
|
|
269
|
+
let confirmPromise: Promise<boolean> | undefined;
|
|
270
|
+
if (isConfirmation) {
|
|
271
|
+
const resolvers = Promise.withResolvers<boolean>();
|
|
272
|
+
confirmResolve = resolvers.resolve;
|
|
273
|
+
confirmPromise = resolvers.promise;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const onConfirm = confirmResolve
|
|
277
|
+
? () => { confirmResolve!(true); dismiss(); }
|
|
278
|
+
: undefined;
|
|
279
|
+
const onReject = confirmResolve
|
|
280
|
+
? () => { confirmResolve!(false); dismiss(); }
|
|
281
|
+
: undefined;
|
|
282
|
+
|
|
283
|
+
// Render content
|
|
284
|
+
if (options.render) {
|
|
285
|
+
// Escape hatch: caller takes full control
|
|
286
|
+
options.render(el);
|
|
287
|
+
} else {
|
|
288
|
+
// Template-based or fallback
|
|
289
|
+
const template = document.querySelector<HTMLTemplateElement>('#overlay-toast');
|
|
290
|
+
if (template) {
|
|
291
|
+
const clone = template.content.cloneNode(true) as DocumentFragment;
|
|
292
|
+
el.appendChild(clone);
|
|
293
|
+
fillTemplate(el, options, onConfirm, onReject);
|
|
294
|
+
} else {
|
|
295
|
+
buildFallback(el, options, onConfirm, onReject);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
187
299
|
container.appendChild(el);
|
|
188
300
|
|
|
301
|
+
const id = performance.now();
|
|
189
302
|
let dismissed = false;
|
|
190
|
-
|
|
303
|
+
|
|
304
|
+
function dismiss(): void {
|
|
191
305
|
if (dismissed) return;
|
|
192
306
|
dismissed = true;
|
|
193
307
|
el.setAttribute('data-dismissing', '');
|
|
194
|
-
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function update(opts: { message?: string; type?: string; timeout?: number }): void {
|
|
311
|
+
if (opts.message !== undefined) {
|
|
312
|
+
const msgEl = el.querySelector('[data-toast-message]');
|
|
313
|
+
if (msgEl) msgEl.textContent = opts.message;
|
|
314
|
+
}
|
|
315
|
+
if (opts.type !== undefined) {
|
|
316
|
+
el.setAttribute('data-toast-type', opts.type);
|
|
317
|
+
}
|
|
318
|
+
if (opts.timeout !== undefined) {
|
|
319
|
+
el.style.setProperty('--overlay-toast-duration', `${opts.timeout}ms`);
|
|
320
|
+
// Restart animation by removing and re-adding the manual flag
|
|
321
|
+
if (opts.timeout === 0) {
|
|
322
|
+
el.setAttribute('data-toast-manual', '');
|
|
323
|
+
} else {
|
|
324
|
+
el.removeAttribute('data-toast-manual');
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const handle: ToastHandle = { id, dismiss, update };
|
|
195
330
|
|
|
196
|
-
return
|
|
331
|
+
// For confirmation toasts, return a handle that is also PromiseLike<boolean>
|
|
332
|
+
if (confirmPromise) {
|
|
333
|
+
(handle as ToastHandle & PromiseLike<boolean>).then = confirmPromise.then.bind(confirmPromise);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return handle;
|
|
197
337
|
}
|
|
198
338
|
|
|
339
|
+
// Attach convenience methods
|
|
340
|
+
toast.success = (message: string, timeout?: number): ToastHandle =>
|
|
341
|
+
toast({ message, type: 'success', timeout: timeout ?? 5000 });
|
|
342
|
+
toast.error = (message: string, timeout?: number): ToastHandle =>
|
|
343
|
+
toast({ message, type: 'error', timeout: timeout ?? 5000 });
|
|
344
|
+
toast.warning = (message: string, timeout?: number): ToastHandle =>
|
|
345
|
+
toast({ message, type: 'warning', timeout: timeout ?? 5000 });
|
|
346
|
+
toast.info = (message: string, timeout?: number): ToastHandle =>
|
|
347
|
+
toast({ message, type: 'info', timeout: timeout ?? 5000 });
|
|
348
|
+
|
|
199
349
|
// --- Popover ---
|
|
200
350
|
|
|
201
351
|
function popover(options: PopoverOptions): void {
|
|
@@ -340,7 +490,7 @@ export function createOverlayService(): OverlayService {
|
|
|
340
490
|
return {
|
|
341
491
|
modal,
|
|
342
492
|
closeModal,
|
|
343
|
-
toast,
|
|
493
|
+
toast: toast as ToastFunction,
|
|
344
494
|
popover,
|
|
345
495
|
closePopover,
|
|
346
496
|
dismissAll,
|
|
@@ -8,6 +8,22 @@
|
|
|
8
8
|
* DOM-aware and closes both programmatic and declarative overlays.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
export interface ToastHandle {
|
|
12
|
+
id: number;
|
|
13
|
+
dismiss(): void;
|
|
14
|
+
update(opts: { message?: string; type?: string; timeout?: number }): void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type ToastType = 'success' | 'error' | 'warning' | 'info';
|
|
18
|
+
|
|
19
|
+
export interface ToastFunction {
|
|
20
|
+
(options: ToastOptions): ToastHandle;
|
|
21
|
+
success(message: string, timeout?: number): ToastHandle;
|
|
22
|
+
error(message: string, timeout?: number): ToastHandle;
|
|
23
|
+
warning(message: string, timeout?: number): ToastHandle;
|
|
24
|
+
info(message: string, timeout?: number): ToastHandle;
|
|
25
|
+
}
|
|
26
|
+
|
|
11
27
|
export interface OverlayService {
|
|
12
28
|
modal<T = undefined>(options: ModalOptions<T>): Promise<T | undefined>;
|
|
13
29
|
closeModal<T>(value?: T): void;
|
|
@@ -15,7 +31,7 @@ export interface OverlayService {
|
|
|
15
31
|
popover(options: PopoverOptions): void;
|
|
16
32
|
closePopover(): void;
|
|
17
33
|
|
|
18
|
-
toast
|
|
34
|
+
toast: ToastFunction;
|
|
19
35
|
|
|
20
36
|
/** Close all open overlays — programmatic and declarative — and toasts. */
|
|
21
37
|
dismissAll(): void;
|
|
@@ -32,7 +48,16 @@ export interface PopoverOptions {
|
|
|
32
48
|
}
|
|
33
49
|
|
|
34
50
|
export interface ToastOptions {
|
|
35
|
-
render
|
|
51
|
+
/** Custom render function — escape hatch for full control. */
|
|
52
|
+
render?(el: HTMLDivElement): void;
|
|
53
|
+
/** Text content (alternative to render). */
|
|
54
|
+
message?: string;
|
|
55
|
+
/** Toast type — sets `data-toast-type` attribute. */
|
|
56
|
+
type?: ToastType;
|
|
57
|
+
/** Confirm button label. Shows button, makes toast manual, returns PromiseLike<boolean>. */
|
|
58
|
+
confirm?: string;
|
|
59
|
+
/** Reject button label. */
|
|
60
|
+
reject?: string;
|
|
36
61
|
/** Auto-dismiss timeout in ms. Default 0 (manual dismiss only). Set to a positive ms value for auto-dismiss via CSS animation. */
|
|
37
62
|
timeout?: number;
|
|
38
63
|
}
|
|
@@ -15,6 +15,7 @@ import type { RouteNode } from '../../../core/type/route-tree.type.ts';
|
|
|
15
15
|
import type { NavigateOptions } from '../../../core/type/route.type.ts';
|
|
16
16
|
import type { WidgetManifestEntry } from '../../../core/type/widget.type.ts';
|
|
17
17
|
import type { ElementManifestEntry } from '../../../core/type/element.type.ts';
|
|
18
|
+
import type { ContextProvider } from '../../../core/type/component.type.ts';
|
|
18
19
|
import { type BasePath, DEFAULT_BASE_PATH } from '../../../core/server/emroute.server.ts';
|
|
19
20
|
import { assertSafeRedirect, escapeHtml } from '../../../core/util/html.util.ts';
|
|
20
21
|
import { ComponentElement } from '../../element/component.element.ts';
|
|
@@ -34,9 +35,15 @@ export class EmrouteApp {
|
|
|
34
35
|
private abortController: AbortController | null = null;
|
|
35
36
|
|
|
36
37
|
constructor(server: Emroute, options?: EmrouteAppOptions) {
|
|
37
|
-
const bp = options?.basePath ?? DEFAULT_BASE_PATH;
|
|
38
38
|
this.server = server;
|
|
39
|
-
|
|
39
|
+
if (options?.basePath) {
|
|
40
|
+
this.appBase = options.basePath.app;
|
|
41
|
+
} else if (typeof document !== 'undefined') {
|
|
42
|
+
const base = document.querySelector('base')?.getAttribute('href');
|
|
43
|
+
this.appBase = base ? base.replace(/\/$/, '') : DEFAULT_BASE_PATH.app;
|
|
44
|
+
} else {
|
|
45
|
+
this.appBase = DEFAULT_BASE_PATH.app;
|
|
46
|
+
}
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
async initialize(slotSelector = 'router-slot'): Promise<void> {
|
|
@@ -178,6 +185,8 @@ export async function createEmrouteApp(
|
|
|
178
185
|
export interface BootOptions extends EmrouteAppOptions {
|
|
179
186
|
/** Override the server origin (defaults to `location.origin`). */
|
|
180
187
|
origin?: string;
|
|
188
|
+
/** Context provider — enriches every ComponentContext (pages and widgets). */
|
|
189
|
+
extendContext?: ContextProvider;
|
|
181
190
|
}
|
|
182
191
|
|
|
183
192
|
/**
|
|
@@ -216,9 +225,11 @@ export async function bootEmrouteApp(options?: BootOptions): Promise<EmrouteApp>
|
|
|
216
225
|
// Build lazy module loaders for all route + widget + element modules
|
|
217
226
|
const moduleLoaders = buildLazyLoaders(routeTree, widgetEntries, elementEntries, runtime);
|
|
218
227
|
|
|
219
|
-
// Register widgets
|
|
228
|
+
// Register widgets: lazy in registry (loaded on demand during render),
|
|
229
|
+
// lazy in DOM (module loads on connectedCallback for hydration).
|
|
220
230
|
const widgets = new WidgetRegistry();
|
|
221
231
|
for (const entry of widgetEntries) {
|
|
232
|
+
widgets.addLazy(entry.name, entry.modulePath);
|
|
222
233
|
ComponentElement.registerLazy(entry.name, moduleLoaders[entry.modulePath]!);
|
|
223
234
|
}
|
|
224
235
|
|
|
@@ -237,6 +248,11 @@ export async function bootEmrouteApp(options?: BootOptions): Promise<EmrouteApp>
|
|
|
237
248
|
}
|
|
238
249
|
}
|
|
239
250
|
|
|
251
|
+
// Wire context provider for both pages (Pipeline) and widgets (ComponentElement)
|
|
252
|
+
if (options?.extendContext) {
|
|
253
|
+
ComponentElement.setContextProvider(options.extendContext);
|
|
254
|
+
}
|
|
255
|
+
|
|
240
256
|
// Create Emroute instance (same class as SSR)
|
|
241
257
|
const mdRenderer = MarkdownElement.getConfiguredRenderer();
|
|
242
258
|
const server = await Emroute.create({
|
|
@@ -244,6 +260,7 @@ export async function bootEmrouteApp(options?: BootOptions): Promise<EmrouteApp>
|
|
|
244
260
|
widgets,
|
|
245
261
|
moduleLoaders,
|
|
246
262
|
...(mdRenderer ? { markdownRenderer: mdRenderer } : {}),
|
|
263
|
+
...(options?.extendContext ? { extendContext: options.extendContext } : {}),
|
|
247
264
|
}, runtime);
|
|
248
265
|
|
|
249
266
|
return createEmrouteApp(server, options);
|