@hybridly/core 0.4.3 → 0.4.5
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/dist/index.cjs +44 -20
- package/dist/index.d.cts +30 -11
- package/dist/index.d.mts +30 -11
- package/dist/index.d.ts +30 -11
- package/dist/index.mjs +44 -20
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -18,14 +18,12 @@ const ONLY_DATA_HEADER = `${HYBRIDLY_HEADER}-only-data`;
|
|
|
18
18
|
const DIALOG_KEY_HEADER = `${HYBRIDLY_HEADER}-dialog-key`;
|
|
19
19
|
const DIALOG_REDIRECT_HEADER = `${HYBRIDLY_HEADER}-dialog-redirect`;
|
|
20
20
|
const EXCEPT_DATA_HEADER = `${HYBRIDLY_HEADER}-except-data`;
|
|
21
|
-
const CONTEXT_HEADER = `${HYBRIDLY_HEADER}-context`;
|
|
22
21
|
const VERSION_HEADER = `${HYBRIDLY_HEADER}-version`;
|
|
23
22
|
const ERROR_BAG_HEADER = `${HYBRIDLY_HEADER}-error-bag`;
|
|
24
23
|
const SCROLL_REGION_ATTRIBUTE = "scroll-region";
|
|
25
24
|
|
|
26
25
|
const constants = {
|
|
27
26
|
__proto__: null,
|
|
28
|
-
CONTEXT_HEADER: CONTEXT_HEADER,
|
|
29
27
|
DIALOG_KEY_HEADER: DIALOG_KEY_HEADER,
|
|
30
28
|
DIALOG_REDIRECT_HEADER: DIALOG_REDIRECT_HEADER,
|
|
31
29
|
ERROR_BAG_HEADER: ERROR_BAG_HEADER,
|
|
@@ -273,6 +271,7 @@ async function registerEventListeners() {
|
|
|
273
271
|
if (!state) {
|
|
274
272
|
utils.debug.history("There is no state. Adding hash if any and restoring scroll positions.");
|
|
275
273
|
return await navigate({
|
|
274
|
+
type: "initial",
|
|
276
275
|
payload: {
|
|
277
276
|
...context,
|
|
278
277
|
url: makeUrl(context.url, { hash: window.location.hash }).toString()
|
|
@@ -283,11 +282,11 @@ async function registerEventListeners() {
|
|
|
283
282
|
});
|
|
284
283
|
}
|
|
285
284
|
await navigate({
|
|
285
|
+
type: "back-forward",
|
|
286
286
|
payload: state,
|
|
287
287
|
preserveScroll: true,
|
|
288
288
|
preserveState: !!getInternalRouterContext().dialog || !!state.dialog,
|
|
289
|
-
updateHistoryState: false
|
|
290
|
-
isBackForward: true
|
|
289
|
+
updateHistoryState: false
|
|
291
290
|
});
|
|
292
291
|
});
|
|
293
292
|
window?.addEventListener("scroll", (event) => utils.debounce(() => {
|
|
@@ -310,14 +309,14 @@ async function handleBackForwardNavigation() {
|
|
|
310
309
|
throw new Error("Tried to handling a back/forward navigation, but there was no state in the history. This should not happen.");
|
|
311
310
|
}
|
|
312
311
|
await navigate({
|
|
312
|
+
type: "back-forward",
|
|
313
313
|
payload: {
|
|
314
314
|
...state,
|
|
315
315
|
version: context.version
|
|
316
316
|
},
|
|
317
317
|
preserveScroll: true,
|
|
318
318
|
preserveState: false,
|
|
319
|
-
updateHistoryState: false
|
|
320
|
-
isBackForward: true
|
|
319
|
+
updateHistoryState: false
|
|
321
320
|
});
|
|
322
321
|
}
|
|
323
322
|
function remember(key, value) {
|
|
@@ -607,8 +606,9 @@ async function handleExternalNavigation() {
|
|
|
607
606
|
url: makeUrl(getRouterContext().url, { hash: window.location.hash }).toString()
|
|
608
607
|
});
|
|
609
608
|
await navigate({
|
|
610
|
-
|
|
611
|
-
preserveState: true
|
|
609
|
+
type: "initial",
|
|
610
|
+
preserveState: true,
|
|
611
|
+
preserveScroll: options.preserveScroll
|
|
612
612
|
});
|
|
613
613
|
}
|
|
614
614
|
function isExternalNavigation() {
|
|
@@ -802,6 +802,7 @@ async function performHybridNavigation(options) {
|
|
|
802
802
|
utils.debug.router("Merged properties:", payload.view.properties);
|
|
803
803
|
}
|
|
804
804
|
await navigate({
|
|
805
|
+
type: "server",
|
|
805
806
|
payload: {
|
|
806
807
|
...payload,
|
|
807
808
|
url: fillHash(targetUrl, payload.url)
|
|
@@ -885,6 +886,7 @@ function isHybridResponse(response) {
|
|
|
885
886
|
}
|
|
886
887
|
async function navigate(options) {
|
|
887
888
|
const context = getRouterContext();
|
|
889
|
+
options.hasDialog ?? (options.hasDialog = !!options.payload?.dialog);
|
|
888
890
|
utils.debug.router("Making an internal navigation:", { context, options });
|
|
889
891
|
await runHooks("navigating", {}, options, context);
|
|
890
892
|
options.payload ?? (options.payload = payloadFromContext());
|
|
@@ -895,6 +897,7 @@ async function navigate(options) {
|
|
|
895
897
|
const shouldPreserveScroll = evaluateConditionalOption(options.preserveScroll);
|
|
896
898
|
const shouldReplaceHistory = evaluateConditionalOption(options.replace);
|
|
897
899
|
const shouldReplaceUrl = evaluateConditionalOption(options.preserveUrl);
|
|
900
|
+
const shouldPreserveView = !options.payload.view.component;
|
|
898
901
|
if (shouldPreserveState && getHistoryMemo() && options.payload.view.component === context.view.component) {
|
|
899
902
|
utils.debug.history("Setting the memo from this history entry into the current context.");
|
|
900
903
|
setContext({ memo: getHistoryMemo() });
|
|
@@ -903,31 +906,49 @@ async function navigate(options) {
|
|
|
903
906
|
utils.debug.router(`Preserving the current URL (${context.url}) instead of navigating to ${options.payload.url}`);
|
|
904
907
|
options.payload.url = context.url;
|
|
905
908
|
}
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
909
|
+
const payload = shouldPreserveView ? {
|
|
910
|
+
view: {
|
|
911
|
+
component: context.view.component,
|
|
912
|
+
properties: utils.merge(context.view.properties, options.payload.view.properties),
|
|
913
|
+
deferred: context.view.deferred
|
|
914
|
+
},
|
|
915
|
+
url: context.url,
|
|
916
|
+
version: options.payload.version,
|
|
917
|
+
dialog: context.dialog
|
|
918
|
+
} : options.payload;
|
|
919
|
+
setContext({ ...payload, memo: {} });
|
|
910
920
|
if (options.updateHistoryState !== false) {
|
|
911
921
|
utils.debug.router(`Target URL is ${context.url}, current window URL is ${window.location.href}.`, { shouldReplaceHistory });
|
|
912
922
|
setHistoryState({ replace: shouldReplaceHistory });
|
|
913
923
|
}
|
|
914
|
-
|
|
915
|
-
|
|
924
|
+
if (context.view.deferred?.length) {
|
|
925
|
+
utils.debug.router("Request has deferred properties, queueing a partial reload:", context.view.deferred);
|
|
926
|
+
context.adapter.executeOnMounted(async () => {
|
|
927
|
+
await performHybridNavigation({
|
|
928
|
+
preserveScroll: true,
|
|
929
|
+
preserveState: true,
|
|
930
|
+
replace: true,
|
|
931
|
+
only: context.view.deferred
|
|
932
|
+
});
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
const viewComponent = !shouldPreserveView ? await context.adapter.resolveComponent(context.view.component) : void 0;
|
|
936
|
+
if (viewComponent) {
|
|
937
|
+
utils.debug.router(`Component [${context.view.component}] resolved to:`, viewComponent);
|
|
938
|
+
}
|
|
916
939
|
await context.adapter.onViewSwap({
|
|
917
940
|
component: viewComponent,
|
|
918
941
|
dialog: context.dialog,
|
|
919
942
|
properties: options.payload?.view?.properties,
|
|
920
|
-
preserveState: shouldPreserveState
|
|
943
|
+
preserveState: shouldPreserveState,
|
|
944
|
+
onMounted: (hookOptions) => runHooks("mounted", {}, { ...options, ...hookOptions }, context)
|
|
921
945
|
});
|
|
922
|
-
if (options.
|
|
946
|
+
if (options.type === "back-forward") {
|
|
923
947
|
restoreScrollPositions();
|
|
924
948
|
} else if (!shouldPreserveScroll) {
|
|
925
949
|
resetScrollPositions();
|
|
926
950
|
}
|
|
927
951
|
await runHooks("navigated", {}, options, context);
|
|
928
|
-
context.adapter.executeOnMounted(() => {
|
|
929
|
-
runHooks("mounted", {}, context);
|
|
930
|
-
});
|
|
931
952
|
}
|
|
932
953
|
async function performHybridRequest(targetUrl, options, abortController) {
|
|
933
954
|
const context = getInternalRouterContext();
|
|
@@ -979,6 +1000,7 @@ async function initializeRouter() {
|
|
|
979
1000
|
url: makeUrl(context.url, { hash: window.location.hash }).toString()
|
|
980
1001
|
});
|
|
981
1002
|
await navigate({
|
|
1003
|
+
type: "initial",
|
|
982
1004
|
preserveState: true,
|
|
983
1005
|
replace: sameUrls(context.url, window.location.href)
|
|
984
1006
|
});
|
|
@@ -992,13 +1014,15 @@ async function performLocalNavigation(targetUrl, options) {
|
|
|
992
1014
|
const url = normalizeUrl(targetUrl);
|
|
993
1015
|
return await navigate({
|
|
994
1016
|
...options,
|
|
1017
|
+
type: "local",
|
|
995
1018
|
payload: {
|
|
996
1019
|
version: context.version,
|
|
997
1020
|
dialog: options?.dialog === false ? void 0 : options?.dialog ?? context.dialog,
|
|
998
1021
|
url,
|
|
999
1022
|
view: {
|
|
1000
1023
|
component: options?.component ?? context.view.component,
|
|
1001
|
-
properties: options?.properties ?? {}
|
|
1024
|
+
properties: options?.properties ?? {},
|
|
1025
|
+
deferred: []
|
|
1002
1026
|
}
|
|
1003
1027
|
}
|
|
1004
1028
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -65,15 +65,21 @@ interface Hooks extends RequestHooks {
|
|
|
65
65
|
/**
|
|
66
66
|
* Called when a component navigation is being made.
|
|
67
67
|
*/
|
|
68
|
-
navigating: (options:
|
|
68
|
+
navigating: (options: InternalNavigationOptions, context: InternalRouterContext) => MaybePromise<any>;
|
|
69
69
|
/**
|
|
70
70
|
* Called when a component has been navigated to.
|
|
71
71
|
*/
|
|
72
|
-
navigated: (options:
|
|
72
|
+
navigated: (options: InternalNavigationOptions, context: InternalRouterContext) => MaybePromise<any>;
|
|
73
73
|
/**
|
|
74
74
|
* Called when a component has been navigated to and was mounted by the adapter.
|
|
75
75
|
*/
|
|
76
|
-
mounted: (context: InternalRouterContext) => MaybePromise<any>;
|
|
76
|
+
mounted: (options: InternalNavigationOptions & MountedHookOptions, context: InternalRouterContext) => MaybePromise<any>;
|
|
77
|
+
}
|
|
78
|
+
interface MountedHookOptions {
|
|
79
|
+
/**
|
|
80
|
+
* Whether the component being mounted is a dialog.
|
|
81
|
+
*/
|
|
82
|
+
isDialog: boolean;
|
|
77
83
|
}
|
|
78
84
|
interface HookOptions {
|
|
79
85
|
/** Executes the hook only once. */
|
|
@@ -160,11 +166,22 @@ interface NavigationOptions {
|
|
|
160
166
|
* @internal This is an advanced property meant to be used internally.
|
|
161
167
|
*/
|
|
162
168
|
updateHistoryState?: boolean;
|
|
169
|
+
}
|
|
170
|
+
interface InternalNavigationOptions extends NavigationOptions {
|
|
163
171
|
/**
|
|
164
|
-
* Defines
|
|
165
|
-
*
|
|
172
|
+
* Defines the kind of navigation being performed.
|
|
173
|
+
* - initial: the initial page load's navigation
|
|
174
|
+
* - server: a navigation initiated by a server round-trip
|
|
175
|
+
* - local: a navigation initiated by `router.local`
|
|
176
|
+
* - back-forward: a navigation initiated by the browser's `popstate` event
|
|
177
|
+
* @internal
|
|
178
|
+
*/
|
|
179
|
+
type: 'initial' | 'local' | 'back-forward' | 'server';
|
|
180
|
+
/**
|
|
181
|
+
* Defines whether this navigation opens a dialog.
|
|
182
|
+
* @internal
|
|
166
183
|
*/
|
|
167
|
-
|
|
184
|
+
hasDialog?: boolean;
|
|
168
185
|
}
|
|
169
186
|
type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
170
187
|
interface HybridRequestOptions extends Omit<NavigationOptions, 'payload'> {
|
|
@@ -264,11 +281,13 @@ interface PendingNavigation {
|
|
|
264
281
|
/** A page or dialog component. */
|
|
265
282
|
interface View {
|
|
266
283
|
/** Name of the component to use. */
|
|
267
|
-
component
|
|
284
|
+
component?: string;
|
|
268
285
|
/** Properties to apply to the component. */
|
|
269
286
|
properties: Properties;
|
|
287
|
+
/** Deferred properties for this view. */
|
|
288
|
+
deferred: string[];
|
|
270
289
|
}
|
|
271
|
-
interface Dialog extends View {
|
|
290
|
+
interface Dialog extends Required<View> {
|
|
272
291
|
/** URL that is the base background page when navigating to the dialog directly. */
|
|
273
292
|
baseUrl: string;
|
|
274
293
|
/** URL to which the dialog should redirect when closed. */
|
|
@@ -289,6 +308,8 @@ interface SwapOptions<T> {
|
|
|
289
308
|
preserveState?: boolean;
|
|
290
309
|
/** Current dialog. */
|
|
291
310
|
dialog?: Dialog;
|
|
311
|
+
/** On mounted callback. */
|
|
312
|
+
onMounted?: (options: MountedHookOptions) => void;
|
|
292
313
|
}
|
|
293
314
|
type ViewComponent = any;
|
|
294
315
|
type ResolveComponent = (name: string) => Promise<ViewComponent>;
|
|
@@ -466,12 +487,10 @@ declare const ONLY_DATA_HEADER = "x-hybrid-only-data";
|
|
|
466
487
|
declare const DIALOG_KEY_HEADER = "x-hybrid-dialog-key";
|
|
467
488
|
declare const DIALOG_REDIRECT_HEADER = "x-hybrid-dialog-redirect";
|
|
468
489
|
declare const EXCEPT_DATA_HEADER = "x-hybrid-except-data";
|
|
469
|
-
declare const CONTEXT_HEADER = "x-hybrid-context";
|
|
470
490
|
declare const VERSION_HEADER = "x-hybrid-version";
|
|
471
491
|
declare const ERROR_BAG_HEADER = "x-hybrid-error-bag";
|
|
472
492
|
declare const SCROLL_REGION_ATTRIBUTE = "scroll-region";
|
|
473
493
|
|
|
474
|
-
declare const constants_CONTEXT_HEADER: typeof CONTEXT_HEADER;
|
|
475
494
|
declare const constants_DIALOG_KEY_HEADER: typeof DIALOG_KEY_HEADER;
|
|
476
495
|
declare const constants_DIALOG_REDIRECT_HEADER: typeof DIALOG_REDIRECT_HEADER;
|
|
477
496
|
declare const constants_ERROR_BAG_HEADER: typeof ERROR_BAG_HEADER;
|
|
@@ -484,7 +503,7 @@ declare const constants_SCROLL_REGION_ATTRIBUTE: typeof SCROLL_REGION_ATTRIBUTE;
|
|
|
484
503
|
declare const constants_STORAGE_EXTERNAL_KEY: typeof STORAGE_EXTERNAL_KEY;
|
|
485
504
|
declare const constants_VERSION_HEADER: typeof VERSION_HEADER;
|
|
486
505
|
declare namespace constants {
|
|
487
|
-
export {
|
|
506
|
+
export { constants_DIALOG_KEY_HEADER as DIALOG_KEY_HEADER, constants_DIALOG_REDIRECT_HEADER as DIALOG_REDIRECT_HEADER, constants_ERROR_BAG_HEADER as ERROR_BAG_HEADER, constants_EXCEPT_DATA_HEADER as EXCEPT_DATA_HEADER, constants_EXTERNAL_NAVIGATION_HEADER as EXTERNAL_NAVIGATION_HEADER, constants_HYBRIDLY_HEADER as HYBRIDLY_HEADER, constants_ONLY_DATA_HEADER as ONLY_DATA_HEADER, constants_PARTIAL_COMPONENT_HEADER as PARTIAL_COMPONENT_HEADER, constants_SCROLL_REGION_ATTRIBUTE as SCROLL_REGION_ATTRIBUTE, constants_STORAGE_EXTERNAL_KEY as STORAGE_EXTERNAL_KEY, constants_VERSION_HEADER as VERSION_HEADER };
|
|
488
507
|
}
|
|
489
508
|
|
|
490
509
|
export { type Authorizable, type DynamicConfiguration, type GlobalRouteCollection, type HybridPayload, type HybridRequestOptions, type MaybePromise, type Method, type NavigationResponse, type Plugin, type Progress, type ResolveComponent, type RouteDefinition, type RouteName, type RouteParameters, type Router, type RouterContext, type RouterContextOptions, type RoutingConfiguration, type UrlResolvable, can, constants, createRouter, definePlugin, getRouterContext, makeUrl, registerHook, route, router, sameUrls };
|
package/dist/index.d.mts
CHANGED
|
@@ -65,15 +65,21 @@ interface Hooks extends RequestHooks {
|
|
|
65
65
|
/**
|
|
66
66
|
* Called when a component navigation is being made.
|
|
67
67
|
*/
|
|
68
|
-
navigating: (options:
|
|
68
|
+
navigating: (options: InternalNavigationOptions, context: InternalRouterContext) => MaybePromise<any>;
|
|
69
69
|
/**
|
|
70
70
|
* Called when a component has been navigated to.
|
|
71
71
|
*/
|
|
72
|
-
navigated: (options:
|
|
72
|
+
navigated: (options: InternalNavigationOptions, context: InternalRouterContext) => MaybePromise<any>;
|
|
73
73
|
/**
|
|
74
74
|
* Called when a component has been navigated to and was mounted by the adapter.
|
|
75
75
|
*/
|
|
76
|
-
mounted: (context: InternalRouterContext) => MaybePromise<any>;
|
|
76
|
+
mounted: (options: InternalNavigationOptions & MountedHookOptions, context: InternalRouterContext) => MaybePromise<any>;
|
|
77
|
+
}
|
|
78
|
+
interface MountedHookOptions {
|
|
79
|
+
/**
|
|
80
|
+
* Whether the component being mounted is a dialog.
|
|
81
|
+
*/
|
|
82
|
+
isDialog: boolean;
|
|
77
83
|
}
|
|
78
84
|
interface HookOptions {
|
|
79
85
|
/** Executes the hook only once. */
|
|
@@ -160,11 +166,22 @@ interface NavigationOptions {
|
|
|
160
166
|
* @internal This is an advanced property meant to be used internally.
|
|
161
167
|
*/
|
|
162
168
|
updateHistoryState?: boolean;
|
|
169
|
+
}
|
|
170
|
+
interface InternalNavigationOptions extends NavigationOptions {
|
|
163
171
|
/**
|
|
164
|
-
* Defines
|
|
165
|
-
*
|
|
172
|
+
* Defines the kind of navigation being performed.
|
|
173
|
+
* - initial: the initial page load's navigation
|
|
174
|
+
* - server: a navigation initiated by a server round-trip
|
|
175
|
+
* - local: a navigation initiated by `router.local`
|
|
176
|
+
* - back-forward: a navigation initiated by the browser's `popstate` event
|
|
177
|
+
* @internal
|
|
178
|
+
*/
|
|
179
|
+
type: 'initial' | 'local' | 'back-forward' | 'server';
|
|
180
|
+
/**
|
|
181
|
+
* Defines whether this navigation opens a dialog.
|
|
182
|
+
* @internal
|
|
166
183
|
*/
|
|
167
|
-
|
|
184
|
+
hasDialog?: boolean;
|
|
168
185
|
}
|
|
169
186
|
type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
170
187
|
interface HybridRequestOptions extends Omit<NavigationOptions, 'payload'> {
|
|
@@ -264,11 +281,13 @@ interface PendingNavigation {
|
|
|
264
281
|
/** A page or dialog component. */
|
|
265
282
|
interface View {
|
|
266
283
|
/** Name of the component to use. */
|
|
267
|
-
component
|
|
284
|
+
component?: string;
|
|
268
285
|
/** Properties to apply to the component. */
|
|
269
286
|
properties: Properties;
|
|
287
|
+
/** Deferred properties for this view. */
|
|
288
|
+
deferred: string[];
|
|
270
289
|
}
|
|
271
|
-
interface Dialog extends View {
|
|
290
|
+
interface Dialog extends Required<View> {
|
|
272
291
|
/** URL that is the base background page when navigating to the dialog directly. */
|
|
273
292
|
baseUrl: string;
|
|
274
293
|
/** URL to which the dialog should redirect when closed. */
|
|
@@ -289,6 +308,8 @@ interface SwapOptions<T> {
|
|
|
289
308
|
preserveState?: boolean;
|
|
290
309
|
/** Current dialog. */
|
|
291
310
|
dialog?: Dialog;
|
|
311
|
+
/** On mounted callback. */
|
|
312
|
+
onMounted?: (options: MountedHookOptions) => void;
|
|
292
313
|
}
|
|
293
314
|
type ViewComponent = any;
|
|
294
315
|
type ResolveComponent = (name: string) => Promise<ViewComponent>;
|
|
@@ -466,12 +487,10 @@ declare const ONLY_DATA_HEADER = "x-hybrid-only-data";
|
|
|
466
487
|
declare const DIALOG_KEY_HEADER = "x-hybrid-dialog-key";
|
|
467
488
|
declare const DIALOG_REDIRECT_HEADER = "x-hybrid-dialog-redirect";
|
|
468
489
|
declare const EXCEPT_DATA_HEADER = "x-hybrid-except-data";
|
|
469
|
-
declare const CONTEXT_HEADER = "x-hybrid-context";
|
|
470
490
|
declare const VERSION_HEADER = "x-hybrid-version";
|
|
471
491
|
declare const ERROR_BAG_HEADER = "x-hybrid-error-bag";
|
|
472
492
|
declare const SCROLL_REGION_ATTRIBUTE = "scroll-region";
|
|
473
493
|
|
|
474
|
-
declare const constants_CONTEXT_HEADER: typeof CONTEXT_HEADER;
|
|
475
494
|
declare const constants_DIALOG_KEY_HEADER: typeof DIALOG_KEY_HEADER;
|
|
476
495
|
declare const constants_DIALOG_REDIRECT_HEADER: typeof DIALOG_REDIRECT_HEADER;
|
|
477
496
|
declare const constants_ERROR_BAG_HEADER: typeof ERROR_BAG_HEADER;
|
|
@@ -484,7 +503,7 @@ declare const constants_SCROLL_REGION_ATTRIBUTE: typeof SCROLL_REGION_ATTRIBUTE;
|
|
|
484
503
|
declare const constants_STORAGE_EXTERNAL_KEY: typeof STORAGE_EXTERNAL_KEY;
|
|
485
504
|
declare const constants_VERSION_HEADER: typeof VERSION_HEADER;
|
|
486
505
|
declare namespace constants {
|
|
487
|
-
export {
|
|
506
|
+
export { constants_DIALOG_KEY_HEADER as DIALOG_KEY_HEADER, constants_DIALOG_REDIRECT_HEADER as DIALOG_REDIRECT_HEADER, constants_ERROR_BAG_HEADER as ERROR_BAG_HEADER, constants_EXCEPT_DATA_HEADER as EXCEPT_DATA_HEADER, constants_EXTERNAL_NAVIGATION_HEADER as EXTERNAL_NAVIGATION_HEADER, constants_HYBRIDLY_HEADER as HYBRIDLY_HEADER, constants_ONLY_DATA_HEADER as ONLY_DATA_HEADER, constants_PARTIAL_COMPONENT_HEADER as PARTIAL_COMPONENT_HEADER, constants_SCROLL_REGION_ATTRIBUTE as SCROLL_REGION_ATTRIBUTE, constants_STORAGE_EXTERNAL_KEY as STORAGE_EXTERNAL_KEY, constants_VERSION_HEADER as VERSION_HEADER };
|
|
488
507
|
}
|
|
489
508
|
|
|
490
509
|
export { type Authorizable, type DynamicConfiguration, type GlobalRouteCollection, type HybridPayload, type HybridRequestOptions, type MaybePromise, type Method, type NavigationResponse, type Plugin, type Progress, type ResolveComponent, type RouteDefinition, type RouteName, type RouteParameters, type Router, type RouterContext, type RouterContextOptions, type RoutingConfiguration, type UrlResolvable, can, constants, createRouter, definePlugin, getRouterContext, makeUrl, registerHook, route, router, sameUrls };
|
package/dist/index.d.ts
CHANGED
|
@@ -65,15 +65,21 @@ interface Hooks extends RequestHooks {
|
|
|
65
65
|
/**
|
|
66
66
|
* Called when a component navigation is being made.
|
|
67
67
|
*/
|
|
68
|
-
navigating: (options:
|
|
68
|
+
navigating: (options: InternalNavigationOptions, context: InternalRouterContext) => MaybePromise<any>;
|
|
69
69
|
/**
|
|
70
70
|
* Called when a component has been navigated to.
|
|
71
71
|
*/
|
|
72
|
-
navigated: (options:
|
|
72
|
+
navigated: (options: InternalNavigationOptions, context: InternalRouterContext) => MaybePromise<any>;
|
|
73
73
|
/**
|
|
74
74
|
* Called when a component has been navigated to and was mounted by the adapter.
|
|
75
75
|
*/
|
|
76
|
-
mounted: (context: InternalRouterContext) => MaybePromise<any>;
|
|
76
|
+
mounted: (options: InternalNavigationOptions & MountedHookOptions, context: InternalRouterContext) => MaybePromise<any>;
|
|
77
|
+
}
|
|
78
|
+
interface MountedHookOptions {
|
|
79
|
+
/**
|
|
80
|
+
* Whether the component being mounted is a dialog.
|
|
81
|
+
*/
|
|
82
|
+
isDialog: boolean;
|
|
77
83
|
}
|
|
78
84
|
interface HookOptions {
|
|
79
85
|
/** Executes the hook only once. */
|
|
@@ -160,11 +166,22 @@ interface NavigationOptions {
|
|
|
160
166
|
* @internal This is an advanced property meant to be used internally.
|
|
161
167
|
*/
|
|
162
168
|
updateHistoryState?: boolean;
|
|
169
|
+
}
|
|
170
|
+
interface InternalNavigationOptions extends NavigationOptions {
|
|
163
171
|
/**
|
|
164
|
-
* Defines
|
|
165
|
-
*
|
|
172
|
+
* Defines the kind of navigation being performed.
|
|
173
|
+
* - initial: the initial page load's navigation
|
|
174
|
+
* - server: a navigation initiated by a server round-trip
|
|
175
|
+
* - local: a navigation initiated by `router.local`
|
|
176
|
+
* - back-forward: a navigation initiated by the browser's `popstate` event
|
|
177
|
+
* @internal
|
|
178
|
+
*/
|
|
179
|
+
type: 'initial' | 'local' | 'back-forward' | 'server';
|
|
180
|
+
/**
|
|
181
|
+
* Defines whether this navigation opens a dialog.
|
|
182
|
+
* @internal
|
|
166
183
|
*/
|
|
167
|
-
|
|
184
|
+
hasDialog?: boolean;
|
|
168
185
|
}
|
|
169
186
|
type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
170
187
|
interface HybridRequestOptions extends Omit<NavigationOptions, 'payload'> {
|
|
@@ -264,11 +281,13 @@ interface PendingNavigation {
|
|
|
264
281
|
/** A page or dialog component. */
|
|
265
282
|
interface View {
|
|
266
283
|
/** Name of the component to use. */
|
|
267
|
-
component
|
|
284
|
+
component?: string;
|
|
268
285
|
/** Properties to apply to the component. */
|
|
269
286
|
properties: Properties;
|
|
287
|
+
/** Deferred properties for this view. */
|
|
288
|
+
deferred: string[];
|
|
270
289
|
}
|
|
271
|
-
interface Dialog extends View {
|
|
290
|
+
interface Dialog extends Required<View> {
|
|
272
291
|
/** URL that is the base background page when navigating to the dialog directly. */
|
|
273
292
|
baseUrl: string;
|
|
274
293
|
/** URL to which the dialog should redirect when closed. */
|
|
@@ -289,6 +308,8 @@ interface SwapOptions<T> {
|
|
|
289
308
|
preserveState?: boolean;
|
|
290
309
|
/** Current dialog. */
|
|
291
310
|
dialog?: Dialog;
|
|
311
|
+
/** On mounted callback. */
|
|
312
|
+
onMounted?: (options: MountedHookOptions) => void;
|
|
292
313
|
}
|
|
293
314
|
type ViewComponent = any;
|
|
294
315
|
type ResolveComponent = (name: string) => Promise<ViewComponent>;
|
|
@@ -466,12 +487,10 @@ declare const ONLY_DATA_HEADER = "x-hybrid-only-data";
|
|
|
466
487
|
declare const DIALOG_KEY_HEADER = "x-hybrid-dialog-key";
|
|
467
488
|
declare const DIALOG_REDIRECT_HEADER = "x-hybrid-dialog-redirect";
|
|
468
489
|
declare const EXCEPT_DATA_HEADER = "x-hybrid-except-data";
|
|
469
|
-
declare const CONTEXT_HEADER = "x-hybrid-context";
|
|
470
490
|
declare const VERSION_HEADER = "x-hybrid-version";
|
|
471
491
|
declare const ERROR_BAG_HEADER = "x-hybrid-error-bag";
|
|
472
492
|
declare const SCROLL_REGION_ATTRIBUTE = "scroll-region";
|
|
473
493
|
|
|
474
|
-
declare const constants_CONTEXT_HEADER: typeof CONTEXT_HEADER;
|
|
475
494
|
declare const constants_DIALOG_KEY_HEADER: typeof DIALOG_KEY_HEADER;
|
|
476
495
|
declare const constants_DIALOG_REDIRECT_HEADER: typeof DIALOG_REDIRECT_HEADER;
|
|
477
496
|
declare const constants_ERROR_BAG_HEADER: typeof ERROR_BAG_HEADER;
|
|
@@ -484,7 +503,7 @@ declare const constants_SCROLL_REGION_ATTRIBUTE: typeof SCROLL_REGION_ATTRIBUTE;
|
|
|
484
503
|
declare const constants_STORAGE_EXTERNAL_KEY: typeof STORAGE_EXTERNAL_KEY;
|
|
485
504
|
declare const constants_VERSION_HEADER: typeof VERSION_HEADER;
|
|
486
505
|
declare namespace constants {
|
|
487
|
-
export {
|
|
506
|
+
export { constants_DIALOG_KEY_HEADER as DIALOG_KEY_HEADER, constants_DIALOG_REDIRECT_HEADER as DIALOG_REDIRECT_HEADER, constants_ERROR_BAG_HEADER as ERROR_BAG_HEADER, constants_EXCEPT_DATA_HEADER as EXCEPT_DATA_HEADER, constants_EXTERNAL_NAVIGATION_HEADER as EXTERNAL_NAVIGATION_HEADER, constants_HYBRIDLY_HEADER as HYBRIDLY_HEADER, constants_ONLY_DATA_HEADER as ONLY_DATA_HEADER, constants_PARTIAL_COMPONENT_HEADER as PARTIAL_COMPONENT_HEADER, constants_SCROLL_REGION_ATTRIBUTE as SCROLL_REGION_ATTRIBUTE, constants_STORAGE_EXTERNAL_KEY as STORAGE_EXTERNAL_KEY, constants_VERSION_HEADER as VERSION_HEADER };
|
|
488
507
|
}
|
|
489
508
|
|
|
490
509
|
export { type Authorizable, type DynamicConfiguration, type GlobalRouteCollection, type HybridPayload, type HybridRequestOptions, type MaybePromise, type Method, type NavigationResponse, type Plugin, type Progress, type ResolveComponent, type RouteDefinition, type RouteName, type RouteParameters, type Router, type RouterContext, type RouterContextOptions, type RoutingConfiguration, type UrlResolvable, can, constants, createRouter, definePlugin, getRouterContext, makeUrl, registerHook, route, router, sameUrls };
|
package/dist/index.mjs
CHANGED
|
@@ -11,14 +11,12 @@ const ONLY_DATA_HEADER = `${HYBRIDLY_HEADER}-only-data`;
|
|
|
11
11
|
const DIALOG_KEY_HEADER = `${HYBRIDLY_HEADER}-dialog-key`;
|
|
12
12
|
const DIALOG_REDIRECT_HEADER = `${HYBRIDLY_HEADER}-dialog-redirect`;
|
|
13
13
|
const EXCEPT_DATA_HEADER = `${HYBRIDLY_HEADER}-except-data`;
|
|
14
|
-
const CONTEXT_HEADER = `${HYBRIDLY_HEADER}-context`;
|
|
15
14
|
const VERSION_HEADER = `${HYBRIDLY_HEADER}-version`;
|
|
16
15
|
const ERROR_BAG_HEADER = `${HYBRIDLY_HEADER}-error-bag`;
|
|
17
16
|
const SCROLL_REGION_ATTRIBUTE = "scroll-region";
|
|
18
17
|
|
|
19
18
|
const constants = {
|
|
20
19
|
__proto__: null,
|
|
21
|
-
CONTEXT_HEADER: CONTEXT_HEADER,
|
|
22
20
|
DIALOG_KEY_HEADER: DIALOG_KEY_HEADER,
|
|
23
21
|
DIALOG_REDIRECT_HEADER: DIALOG_REDIRECT_HEADER,
|
|
24
22
|
ERROR_BAG_HEADER: ERROR_BAG_HEADER,
|
|
@@ -266,6 +264,7 @@ async function registerEventListeners() {
|
|
|
266
264
|
if (!state) {
|
|
267
265
|
debug.history("There is no state. Adding hash if any and restoring scroll positions.");
|
|
268
266
|
return await navigate({
|
|
267
|
+
type: "initial",
|
|
269
268
|
payload: {
|
|
270
269
|
...context,
|
|
271
270
|
url: makeUrl(context.url, { hash: window.location.hash }).toString()
|
|
@@ -276,11 +275,11 @@ async function registerEventListeners() {
|
|
|
276
275
|
});
|
|
277
276
|
}
|
|
278
277
|
await navigate({
|
|
278
|
+
type: "back-forward",
|
|
279
279
|
payload: state,
|
|
280
280
|
preserveScroll: true,
|
|
281
281
|
preserveState: !!getInternalRouterContext().dialog || !!state.dialog,
|
|
282
|
-
updateHistoryState: false
|
|
283
|
-
isBackForward: true
|
|
282
|
+
updateHistoryState: false
|
|
284
283
|
});
|
|
285
284
|
});
|
|
286
285
|
window?.addEventListener("scroll", (event) => debounce(() => {
|
|
@@ -303,14 +302,14 @@ async function handleBackForwardNavigation() {
|
|
|
303
302
|
throw new Error("Tried to handling a back/forward navigation, but there was no state in the history. This should not happen.");
|
|
304
303
|
}
|
|
305
304
|
await navigate({
|
|
305
|
+
type: "back-forward",
|
|
306
306
|
payload: {
|
|
307
307
|
...state,
|
|
308
308
|
version: context.version
|
|
309
309
|
},
|
|
310
310
|
preserveScroll: true,
|
|
311
311
|
preserveState: false,
|
|
312
|
-
updateHistoryState: false
|
|
313
|
-
isBackForward: true
|
|
312
|
+
updateHistoryState: false
|
|
314
313
|
});
|
|
315
314
|
}
|
|
316
315
|
function remember(key, value) {
|
|
@@ -600,8 +599,9 @@ async function handleExternalNavigation() {
|
|
|
600
599
|
url: makeUrl(getRouterContext().url, { hash: window.location.hash }).toString()
|
|
601
600
|
});
|
|
602
601
|
await navigate({
|
|
603
|
-
|
|
604
|
-
preserveState: true
|
|
602
|
+
type: "initial",
|
|
603
|
+
preserveState: true,
|
|
604
|
+
preserveScroll: options.preserveScroll
|
|
605
605
|
});
|
|
606
606
|
}
|
|
607
607
|
function isExternalNavigation() {
|
|
@@ -795,6 +795,7 @@ async function performHybridNavigation(options) {
|
|
|
795
795
|
debug.router("Merged properties:", payload.view.properties);
|
|
796
796
|
}
|
|
797
797
|
await navigate({
|
|
798
|
+
type: "server",
|
|
798
799
|
payload: {
|
|
799
800
|
...payload,
|
|
800
801
|
url: fillHash(targetUrl, payload.url)
|
|
@@ -878,6 +879,7 @@ function isHybridResponse(response) {
|
|
|
878
879
|
}
|
|
879
880
|
async function navigate(options) {
|
|
880
881
|
const context = getRouterContext();
|
|
882
|
+
options.hasDialog ?? (options.hasDialog = !!options.payload?.dialog);
|
|
881
883
|
debug.router("Making an internal navigation:", { context, options });
|
|
882
884
|
await runHooks("navigating", {}, options, context);
|
|
883
885
|
options.payload ?? (options.payload = payloadFromContext());
|
|
@@ -888,6 +890,7 @@ async function navigate(options) {
|
|
|
888
890
|
const shouldPreserveScroll = evaluateConditionalOption(options.preserveScroll);
|
|
889
891
|
const shouldReplaceHistory = evaluateConditionalOption(options.replace);
|
|
890
892
|
const shouldReplaceUrl = evaluateConditionalOption(options.preserveUrl);
|
|
893
|
+
const shouldPreserveView = !options.payload.view.component;
|
|
891
894
|
if (shouldPreserveState && getHistoryMemo() && options.payload.view.component === context.view.component) {
|
|
892
895
|
debug.history("Setting the memo from this history entry into the current context.");
|
|
893
896
|
setContext({ memo: getHistoryMemo() });
|
|
@@ -896,31 +899,49 @@ async function navigate(options) {
|
|
|
896
899
|
debug.router(`Preserving the current URL (${context.url}) instead of navigating to ${options.payload.url}`);
|
|
897
900
|
options.payload.url = context.url;
|
|
898
901
|
}
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
902
|
+
const payload = shouldPreserveView ? {
|
|
903
|
+
view: {
|
|
904
|
+
component: context.view.component,
|
|
905
|
+
properties: merge(context.view.properties, options.payload.view.properties),
|
|
906
|
+
deferred: context.view.deferred
|
|
907
|
+
},
|
|
908
|
+
url: context.url,
|
|
909
|
+
version: options.payload.version,
|
|
910
|
+
dialog: context.dialog
|
|
911
|
+
} : options.payload;
|
|
912
|
+
setContext({ ...payload, memo: {} });
|
|
903
913
|
if (options.updateHistoryState !== false) {
|
|
904
914
|
debug.router(`Target URL is ${context.url}, current window URL is ${window.location.href}.`, { shouldReplaceHistory });
|
|
905
915
|
setHistoryState({ replace: shouldReplaceHistory });
|
|
906
916
|
}
|
|
907
|
-
|
|
908
|
-
|
|
917
|
+
if (context.view.deferred?.length) {
|
|
918
|
+
debug.router("Request has deferred properties, queueing a partial reload:", context.view.deferred);
|
|
919
|
+
context.adapter.executeOnMounted(async () => {
|
|
920
|
+
await performHybridNavigation({
|
|
921
|
+
preserveScroll: true,
|
|
922
|
+
preserveState: true,
|
|
923
|
+
replace: true,
|
|
924
|
+
only: context.view.deferred
|
|
925
|
+
});
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
const viewComponent = !shouldPreserveView ? await context.adapter.resolveComponent(context.view.component) : void 0;
|
|
929
|
+
if (viewComponent) {
|
|
930
|
+
debug.router(`Component [${context.view.component}] resolved to:`, viewComponent);
|
|
931
|
+
}
|
|
909
932
|
await context.adapter.onViewSwap({
|
|
910
933
|
component: viewComponent,
|
|
911
934
|
dialog: context.dialog,
|
|
912
935
|
properties: options.payload?.view?.properties,
|
|
913
|
-
preserveState: shouldPreserveState
|
|
936
|
+
preserveState: shouldPreserveState,
|
|
937
|
+
onMounted: (hookOptions) => runHooks("mounted", {}, { ...options, ...hookOptions }, context)
|
|
914
938
|
});
|
|
915
|
-
if (options.
|
|
939
|
+
if (options.type === "back-forward") {
|
|
916
940
|
restoreScrollPositions();
|
|
917
941
|
} else if (!shouldPreserveScroll) {
|
|
918
942
|
resetScrollPositions();
|
|
919
943
|
}
|
|
920
944
|
await runHooks("navigated", {}, options, context);
|
|
921
|
-
context.adapter.executeOnMounted(() => {
|
|
922
|
-
runHooks("mounted", {}, context);
|
|
923
|
-
});
|
|
924
945
|
}
|
|
925
946
|
async function performHybridRequest(targetUrl, options, abortController) {
|
|
926
947
|
const context = getInternalRouterContext();
|
|
@@ -972,6 +993,7 @@ async function initializeRouter() {
|
|
|
972
993
|
url: makeUrl(context.url, { hash: window.location.hash }).toString()
|
|
973
994
|
});
|
|
974
995
|
await navigate({
|
|
996
|
+
type: "initial",
|
|
975
997
|
preserveState: true,
|
|
976
998
|
replace: sameUrls(context.url, window.location.href)
|
|
977
999
|
});
|
|
@@ -985,13 +1007,15 @@ async function performLocalNavigation(targetUrl, options) {
|
|
|
985
1007
|
const url = normalizeUrl(targetUrl);
|
|
986
1008
|
return await navigate({
|
|
987
1009
|
...options,
|
|
1010
|
+
type: "local",
|
|
988
1011
|
payload: {
|
|
989
1012
|
version: context.version,
|
|
990
1013
|
dialog: options?.dialog === false ? void 0 : options?.dialog ?? context.dialog,
|
|
991
1014
|
url,
|
|
992
1015
|
view: {
|
|
993
1016
|
component: options?.component ?? context.view.component,
|
|
994
|
-
properties: options?.properties ?? {}
|
|
1017
|
+
properties: options?.properties ?? {},
|
|
1018
|
+
deferred: []
|
|
995
1019
|
}
|
|
996
1020
|
}
|
|
997
1021
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hybridly/core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.5",
|
|
4
4
|
"description": "Core functionality of Hybridly",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"hybridly",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"qs": "^6.11.2",
|
|
40
40
|
"superjson": "^1.13.1",
|
|
41
|
-
"@hybridly/utils": "0.4.
|
|
41
|
+
"@hybridly/utils": "0.4.5"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"defu": "^6.1.2"
|