@hybridly/core 0.4.5 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +32 -6
- package/dist/index.d.cts +7 -7
- package/dist/index.d.mts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.mjs +32 -6
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -644,6 +644,27 @@ async function closeDialog(options) {
|
|
|
644
644
|
});
|
|
645
645
|
}
|
|
646
646
|
|
|
647
|
+
function isDownloadResponse(response) {
|
|
648
|
+
return response.status === 200 && !!response.headers["content-disposition"];
|
|
649
|
+
}
|
|
650
|
+
async function handleDownloadResponse(response) {
|
|
651
|
+
const blob = new Blob([response.data], { type: "application/octet-stream" });
|
|
652
|
+
const urlObject = window.webkitURL || window.URL;
|
|
653
|
+
const link = document.createElement("a");
|
|
654
|
+
link.style.display = "none";
|
|
655
|
+
link.href = urlObject.createObjectURL(blob);
|
|
656
|
+
link.download = getFileNameFromContentDispositionHeader(response.headers["content-disposition"]);
|
|
657
|
+
link.click();
|
|
658
|
+
setTimeout(() => {
|
|
659
|
+
urlObject.revokeObjectURL(link.href);
|
|
660
|
+
link.remove();
|
|
661
|
+
}, 0);
|
|
662
|
+
}
|
|
663
|
+
function getFileNameFromContentDispositionHeader(header) {
|
|
664
|
+
const result = header.split(";")[1]?.trim().split("=")[1];
|
|
665
|
+
return result?.replace(/^"(.*)"$/, "$1") ?? "";
|
|
666
|
+
}
|
|
667
|
+
|
|
647
668
|
function isPreloaded(targetUrl) {
|
|
648
669
|
const context = getInternalRouterContext();
|
|
649
670
|
return context.preloadCache.has(targetUrl.toString()) ?? false;
|
|
@@ -765,10 +786,7 @@ async function performHybridNavigation(options) {
|
|
|
765
786
|
context.pendingNavigation?.controller?.abort();
|
|
766
787
|
}
|
|
767
788
|
saveScrollPositions();
|
|
768
|
-
|
|
769
|
-
options.url = makeUrl(options.url, options.transformUrl);
|
|
770
|
-
}
|
|
771
|
-
const targetUrl = makeUrl(options.url ?? context.url);
|
|
789
|
+
const targetUrl = makeUrl(options.url ?? context.url, options.transformUrl);
|
|
772
790
|
const abortController = new AbortController();
|
|
773
791
|
setContext({
|
|
774
792
|
pendingNavigation: {
|
|
@@ -782,7 +800,10 @@ async function performHybridNavigation(options) {
|
|
|
782
800
|
await runHooks("start", options.hooks, context);
|
|
783
801
|
utils.debug.router("Making request with axios.");
|
|
784
802
|
const response = await performHybridRequest(targetUrl, options, abortController);
|
|
785
|
-
await runHooks("data", options.hooks, response, context);
|
|
803
|
+
const result = await runHooks("data", options.hooks, response, context);
|
|
804
|
+
if (result === false) {
|
|
805
|
+
return { response };
|
|
806
|
+
}
|
|
786
807
|
if (isExternalResponse(response)) {
|
|
787
808
|
utils.debug.router("The response is explicitely external.");
|
|
788
809
|
await performExternalNavigation({
|
|
@@ -791,6 +812,11 @@ async function performHybridNavigation(options) {
|
|
|
791
812
|
});
|
|
792
813
|
return { response };
|
|
793
814
|
}
|
|
815
|
+
if (isDownloadResponse(response)) {
|
|
816
|
+
utils.debug.router("The response returns a file to download.");
|
|
817
|
+
await handleDownloadResponse(response);
|
|
818
|
+
return { response };
|
|
819
|
+
}
|
|
794
820
|
if (!isHybridResponse(response)) {
|
|
795
821
|
throw new NotAHybridResponseError(response);
|
|
796
822
|
}
|
|
@@ -995,7 +1021,7 @@ async function initializeRouter() {
|
|
|
995
1021
|
} else if (isExternalNavigation()) {
|
|
996
1022
|
handleExternalNavigation();
|
|
997
1023
|
} else {
|
|
998
|
-
utils.debug.router("Handling the initial
|
|
1024
|
+
utils.debug.router("Handling the initial navigation.");
|
|
999
1025
|
setContext({
|
|
1000
1026
|
url: makeUrl(context.url, { hash: window.location.hash }).toString()
|
|
1001
1027
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -55,7 +55,7 @@ interface Hooks extends RequestHooks {
|
|
|
55
55
|
*/
|
|
56
56
|
initialized: (context: InternalRouterContext) => MaybePromise<any>;
|
|
57
57
|
/**
|
|
58
|
-
* Called after Hybridly's initial
|
|
58
|
+
* Called after Hybridly's initial load.
|
|
59
59
|
*/
|
|
60
60
|
ready: (context: InternalRouterContext) => MaybePromise<any>;
|
|
61
61
|
/**
|
|
@@ -134,7 +134,7 @@ interface ComponentNavigationOptions {
|
|
|
134
134
|
replace?: ConditionalNavigationOption<boolean>;
|
|
135
135
|
/** Whether to preserve the current scrollbar position. */
|
|
136
136
|
preserveScroll?: ConditionalNavigationOption<boolean>;
|
|
137
|
-
/** Whether to preserve the current
|
|
137
|
+
/** Whether to preserve the current view component state. */
|
|
138
138
|
preserveState?: ConditionalNavigationOption<boolean>;
|
|
139
139
|
}
|
|
140
140
|
interface NavigationOptions {
|
|
@@ -145,9 +145,9 @@ interface NavigationOptions {
|
|
|
145
145
|
* one. This affects the browser's "back" and "forward" features.
|
|
146
146
|
*/
|
|
147
147
|
replace?: ConditionalNavigationOption<boolean>;
|
|
148
|
-
/** Whether to preserve the scrollbars positions on the
|
|
148
|
+
/** Whether to preserve the scrollbars positions on the view. */
|
|
149
149
|
preserveScroll?: ConditionalNavigationOption<boolean>;
|
|
150
|
-
/** Whether to preserve the current
|
|
150
|
+
/** Whether to preserve the current view component's state. */
|
|
151
151
|
preserveState?: ConditionalNavigationOption<boolean>;
|
|
152
152
|
/** Whether to preserve the current URL. */
|
|
153
153
|
preserveUrl?: ConditionalNavigationOption<boolean>;
|
|
@@ -170,7 +170,7 @@ interface NavigationOptions {
|
|
|
170
170
|
interface InternalNavigationOptions extends NavigationOptions {
|
|
171
171
|
/**
|
|
172
172
|
* Defines the kind of navigation being performed.
|
|
173
|
-
* - initial: the initial
|
|
173
|
+
* - initial: the initial load's navigation
|
|
174
174
|
* - server: a navigation initiated by a server round-trip
|
|
175
175
|
* - local: a navigation initiated by `router.local`
|
|
176
176
|
* - back-forward: a navigation initiated by the browser's `popstate` event
|
|
@@ -278,7 +278,7 @@ interface PendingNavigation {
|
|
|
278
278
|
/** Current status. */
|
|
279
279
|
status: 'pending' | 'success' | 'error';
|
|
280
280
|
}
|
|
281
|
-
/** A
|
|
281
|
+
/** A view or dialog component. */
|
|
282
282
|
interface View {
|
|
283
283
|
/** Name of the component to use. */
|
|
284
284
|
component?: string;
|
|
@@ -288,7 +288,7 @@ interface View {
|
|
|
288
288
|
deferred: string[];
|
|
289
289
|
}
|
|
290
290
|
interface Dialog extends Required<View> {
|
|
291
|
-
/** URL that is the base background
|
|
291
|
+
/** URL that is the base background view when navigating to the dialog directly. */
|
|
292
292
|
baseUrl: string;
|
|
293
293
|
/** URL to which the dialog should redirect when closed. */
|
|
294
294
|
redirectUrl: string;
|
package/dist/index.d.mts
CHANGED
|
@@ -55,7 +55,7 @@ interface Hooks extends RequestHooks {
|
|
|
55
55
|
*/
|
|
56
56
|
initialized: (context: InternalRouterContext) => MaybePromise<any>;
|
|
57
57
|
/**
|
|
58
|
-
* Called after Hybridly's initial
|
|
58
|
+
* Called after Hybridly's initial load.
|
|
59
59
|
*/
|
|
60
60
|
ready: (context: InternalRouterContext) => MaybePromise<any>;
|
|
61
61
|
/**
|
|
@@ -134,7 +134,7 @@ interface ComponentNavigationOptions {
|
|
|
134
134
|
replace?: ConditionalNavigationOption<boolean>;
|
|
135
135
|
/** Whether to preserve the current scrollbar position. */
|
|
136
136
|
preserveScroll?: ConditionalNavigationOption<boolean>;
|
|
137
|
-
/** Whether to preserve the current
|
|
137
|
+
/** Whether to preserve the current view component state. */
|
|
138
138
|
preserveState?: ConditionalNavigationOption<boolean>;
|
|
139
139
|
}
|
|
140
140
|
interface NavigationOptions {
|
|
@@ -145,9 +145,9 @@ interface NavigationOptions {
|
|
|
145
145
|
* one. This affects the browser's "back" and "forward" features.
|
|
146
146
|
*/
|
|
147
147
|
replace?: ConditionalNavigationOption<boolean>;
|
|
148
|
-
/** Whether to preserve the scrollbars positions on the
|
|
148
|
+
/** Whether to preserve the scrollbars positions on the view. */
|
|
149
149
|
preserveScroll?: ConditionalNavigationOption<boolean>;
|
|
150
|
-
/** Whether to preserve the current
|
|
150
|
+
/** Whether to preserve the current view component's state. */
|
|
151
151
|
preserveState?: ConditionalNavigationOption<boolean>;
|
|
152
152
|
/** Whether to preserve the current URL. */
|
|
153
153
|
preserveUrl?: ConditionalNavigationOption<boolean>;
|
|
@@ -170,7 +170,7 @@ interface NavigationOptions {
|
|
|
170
170
|
interface InternalNavigationOptions extends NavigationOptions {
|
|
171
171
|
/**
|
|
172
172
|
* Defines the kind of navigation being performed.
|
|
173
|
-
* - initial: the initial
|
|
173
|
+
* - initial: the initial load's navigation
|
|
174
174
|
* - server: a navigation initiated by a server round-trip
|
|
175
175
|
* - local: a navigation initiated by `router.local`
|
|
176
176
|
* - back-forward: a navigation initiated by the browser's `popstate` event
|
|
@@ -278,7 +278,7 @@ interface PendingNavigation {
|
|
|
278
278
|
/** Current status. */
|
|
279
279
|
status: 'pending' | 'success' | 'error';
|
|
280
280
|
}
|
|
281
|
-
/** A
|
|
281
|
+
/** A view or dialog component. */
|
|
282
282
|
interface View {
|
|
283
283
|
/** Name of the component to use. */
|
|
284
284
|
component?: string;
|
|
@@ -288,7 +288,7 @@ interface View {
|
|
|
288
288
|
deferred: string[];
|
|
289
289
|
}
|
|
290
290
|
interface Dialog extends Required<View> {
|
|
291
|
-
/** URL that is the base background
|
|
291
|
+
/** URL that is the base background view when navigating to the dialog directly. */
|
|
292
292
|
baseUrl: string;
|
|
293
293
|
/** URL to which the dialog should redirect when closed. */
|
|
294
294
|
redirectUrl: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -55,7 +55,7 @@ interface Hooks extends RequestHooks {
|
|
|
55
55
|
*/
|
|
56
56
|
initialized: (context: InternalRouterContext) => MaybePromise<any>;
|
|
57
57
|
/**
|
|
58
|
-
* Called after Hybridly's initial
|
|
58
|
+
* Called after Hybridly's initial load.
|
|
59
59
|
*/
|
|
60
60
|
ready: (context: InternalRouterContext) => MaybePromise<any>;
|
|
61
61
|
/**
|
|
@@ -134,7 +134,7 @@ interface ComponentNavigationOptions {
|
|
|
134
134
|
replace?: ConditionalNavigationOption<boolean>;
|
|
135
135
|
/** Whether to preserve the current scrollbar position. */
|
|
136
136
|
preserveScroll?: ConditionalNavigationOption<boolean>;
|
|
137
|
-
/** Whether to preserve the current
|
|
137
|
+
/** Whether to preserve the current view component state. */
|
|
138
138
|
preserveState?: ConditionalNavigationOption<boolean>;
|
|
139
139
|
}
|
|
140
140
|
interface NavigationOptions {
|
|
@@ -145,9 +145,9 @@ interface NavigationOptions {
|
|
|
145
145
|
* one. This affects the browser's "back" and "forward" features.
|
|
146
146
|
*/
|
|
147
147
|
replace?: ConditionalNavigationOption<boolean>;
|
|
148
|
-
/** Whether to preserve the scrollbars positions on the
|
|
148
|
+
/** Whether to preserve the scrollbars positions on the view. */
|
|
149
149
|
preserveScroll?: ConditionalNavigationOption<boolean>;
|
|
150
|
-
/** Whether to preserve the current
|
|
150
|
+
/** Whether to preserve the current view component's state. */
|
|
151
151
|
preserveState?: ConditionalNavigationOption<boolean>;
|
|
152
152
|
/** Whether to preserve the current URL. */
|
|
153
153
|
preserveUrl?: ConditionalNavigationOption<boolean>;
|
|
@@ -170,7 +170,7 @@ interface NavigationOptions {
|
|
|
170
170
|
interface InternalNavigationOptions extends NavigationOptions {
|
|
171
171
|
/**
|
|
172
172
|
* Defines the kind of navigation being performed.
|
|
173
|
-
* - initial: the initial
|
|
173
|
+
* - initial: the initial load's navigation
|
|
174
174
|
* - server: a navigation initiated by a server round-trip
|
|
175
175
|
* - local: a navigation initiated by `router.local`
|
|
176
176
|
* - back-forward: a navigation initiated by the browser's `popstate` event
|
|
@@ -278,7 +278,7 @@ interface PendingNavigation {
|
|
|
278
278
|
/** Current status. */
|
|
279
279
|
status: 'pending' | 'success' | 'error';
|
|
280
280
|
}
|
|
281
|
-
/** A
|
|
281
|
+
/** A view or dialog component. */
|
|
282
282
|
interface View {
|
|
283
283
|
/** Name of the component to use. */
|
|
284
284
|
component?: string;
|
|
@@ -288,7 +288,7 @@ interface View {
|
|
|
288
288
|
deferred: string[];
|
|
289
289
|
}
|
|
290
290
|
interface Dialog extends Required<View> {
|
|
291
|
-
/** URL that is the base background
|
|
291
|
+
/** URL that is the base background view when navigating to the dialog directly. */
|
|
292
292
|
baseUrl: string;
|
|
293
293
|
/** URL to which the dialog should redirect when closed. */
|
|
294
294
|
redirectUrl: string;
|
package/dist/index.mjs
CHANGED
|
@@ -637,6 +637,27 @@ async function closeDialog(options) {
|
|
|
637
637
|
});
|
|
638
638
|
}
|
|
639
639
|
|
|
640
|
+
function isDownloadResponse(response) {
|
|
641
|
+
return response.status === 200 && !!response.headers["content-disposition"];
|
|
642
|
+
}
|
|
643
|
+
async function handleDownloadResponse(response) {
|
|
644
|
+
const blob = new Blob([response.data], { type: "application/octet-stream" });
|
|
645
|
+
const urlObject = window.webkitURL || window.URL;
|
|
646
|
+
const link = document.createElement("a");
|
|
647
|
+
link.style.display = "none";
|
|
648
|
+
link.href = urlObject.createObjectURL(blob);
|
|
649
|
+
link.download = getFileNameFromContentDispositionHeader(response.headers["content-disposition"]);
|
|
650
|
+
link.click();
|
|
651
|
+
setTimeout(() => {
|
|
652
|
+
urlObject.revokeObjectURL(link.href);
|
|
653
|
+
link.remove();
|
|
654
|
+
}, 0);
|
|
655
|
+
}
|
|
656
|
+
function getFileNameFromContentDispositionHeader(header) {
|
|
657
|
+
const result = header.split(";")[1]?.trim().split("=")[1];
|
|
658
|
+
return result?.replace(/^"(.*)"$/, "$1") ?? "";
|
|
659
|
+
}
|
|
660
|
+
|
|
640
661
|
function isPreloaded(targetUrl) {
|
|
641
662
|
const context = getInternalRouterContext();
|
|
642
663
|
return context.preloadCache.has(targetUrl.toString()) ?? false;
|
|
@@ -758,10 +779,7 @@ async function performHybridNavigation(options) {
|
|
|
758
779
|
context.pendingNavigation?.controller?.abort();
|
|
759
780
|
}
|
|
760
781
|
saveScrollPositions();
|
|
761
|
-
|
|
762
|
-
options.url = makeUrl(options.url, options.transformUrl);
|
|
763
|
-
}
|
|
764
|
-
const targetUrl = makeUrl(options.url ?? context.url);
|
|
782
|
+
const targetUrl = makeUrl(options.url ?? context.url, options.transformUrl);
|
|
765
783
|
const abortController = new AbortController();
|
|
766
784
|
setContext({
|
|
767
785
|
pendingNavigation: {
|
|
@@ -775,7 +793,10 @@ async function performHybridNavigation(options) {
|
|
|
775
793
|
await runHooks("start", options.hooks, context);
|
|
776
794
|
debug.router("Making request with axios.");
|
|
777
795
|
const response = await performHybridRequest(targetUrl, options, abortController);
|
|
778
|
-
await runHooks("data", options.hooks, response, context);
|
|
796
|
+
const result = await runHooks("data", options.hooks, response, context);
|
|
797
|
+
if (result === false) {
|
|
798
|
+
return { response };
|
|
799
|
+
}
|
|
779
800
|
if (isExternalResponse(response)) {
|
|
780
801
|
debug.router("The response is explicitely external.");
|
|
781
802
|
await performExternalNavigation({
|
|
@@ -784,6 +805,11 @@ async function performHybridNavigation(options) {
|
|
|
784
805
|
});
|
|
785
806
|
return { response };
|
|
786
807
|
}
|
|
808
|
+
if (isDownloadResponse(response)) {
|
|
809
|
+
debug.router("The response returns a file to download.");
|
|
810
|
+
await handleDownloadResponse(response);
|
|
811
|
+
return { response };
|
|
812
|
+
}
|
|
787
813
|
if (!isHybridResponse(response)) {
|
|
788
814
|
throw new NotAHybridResponseError(response);
|
|
789
815
|
}
|
|
@@ -988,7 +1014,7 @@ async function initializeRouter() {
|
|
|
988
1014
|
} else if (isExternalNavigation()) {
|
|
989
1015
|
handleExternalNavigation();
|
|
990
1016
|
} else {
|
|
991
|
-
debug.router("Handling the initial
|
|
1017
|
+
debug.router("Handling the initial navigation.");
|
|
992
1018
|
setContext({
|
|
993
1019
|
url: makeUrl(context.url, { hash: window.location.hash }).toString()
|
|
994
1020
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hybridly/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
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.
|
|
41
|
+
"@hybridly/utils": "0.5.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"defu": "^6.1.2"
|