@lwrjs/router 0.10.0-alpha.1 → 0.10.0-alpha.10
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/build/bundle/prod/lwr/navigation/es/modules/lwr/currentView/currentView.d.ts +28 -0
- package/build/bundle/prod/lwr/navigation/navigation.js +1 -1
- package/build/bundle/prod/lwr/router/es/modules/lwr/currentView/currentView.d.ts +28 -0
- package/build/bundle/prod/lwr/router/router.js +1 -1
- package/build/bundle/prod/lwr/routerContainer/es/modules/lwr/currentView/currentView.d.ts +28 -0
- package/build/bundle/prod/lwr/routerContainer/routerContainer.js +1 -1
- package/build/cjs/modules/lwr/outlet/outlet.cjs +4 -1
- package/build/es/modules/lwr/contextProvider/contextProvider.js +30 -0
- package/build/es/modules/lwr/contextUtils/contextInfo.js +93 -0
- package/build/es/modules/lwr/contextUtils/contextUtils.js +77 -0
- package/build/es/modules/lwr/contextUtils/navigationApiStore.js +46 -0
- package/build/{modules → es/modules}/lwr/currentPageReference/currentPageReference.d.ts +1 -1
- package/build/es/modules/lwr/currentPageReference/currentPageReference.js +14 -0
- package/build/{modules → es/modules}/lwr/currentView/currentView.d.ts +2 -2
- package/build/es/modules/lwr/currentView/currentView.js +62 -0
- package/build/{modules → es/modules}/lwr/domRouter/domRouter.d.ts +2 -2
- package/build/es/modules/lwr/domRouter/domRouter.js +441 -0
- package/build/es/modules/lwr/domRouterUtils/domRouterUtils.js +3 -0
- package/build/es/modules/lwr/domRouterUtils/historyUtils.js +30 -0
- package/build/{modules → es/modules}/lwr/domRouterUtils/types.d.ts +1 -1
- package/build/es/modules/lwr/domRouterUtils/types.js +2 -0
- package/build/es/modules/lwr/domRouterUtils/uriUtils.js +69 -0
- package/build/es/modules/lwr/historyRouter/historyRouter.js +88 -0
- package/build/es/modules/lwr/navigation/navigation.js +20 -0
- package/build/es/modules/lwr/navigation/navigationApi.js +27 -0
- package/build/es/modules/lwr/navigation/navigationMixin.js +76 -0
- package/build/{modules → es/modules}/lwr/navigationContext/navigationContext.d.ts +2 -2
- package/build/es/modules/lwr/navigationContext/navigationContext.js +10 -0
- package/build/es/modules/lwr/navigationMixinHacks/navigationMixinHacks.d.ts +7 -0
- package/build/es/modules/lwr/navigationMixinHacks/navigationMixinHacks.js +5 -0
- package/build/es/modules/lwr/observable/observable.js +71 -0
- package/build/{modules → es/modules}/lwr/outlet/outlet.d.ts +1 -0
- package/build/es/modules/lwr/outlet/outlet.js +69 -0
- package/build/es/modules/lwr/router/router.js +201 -0
- package/build/es/modules/lwr/routerBridge/routerBridge.js +85 -0
- package/build/es/modules/lwr/routerContainer/routerContainer.js +116 -0
- package/build/es/modules/lwr/routerContainer/utils.js +83 -0
- package/build/es/modules/lwr/routerErrors/routerErrors.js +154 -0
- package/build/es/modules/lwr/routerUtils/domUtils.js +3 -0
- package/build/{modules → es/modules}/lwr/routerUtils/filterUtils.d.ts +1 -1
- package/build/es/modules/lwr/routerUtils/filterUtils.js +74 -0
- package/build/es/modules/lwr/routerUtils/parseUtils.js +182 -0
- package/build/{modules → es/modules}/lwr/routerUtils/pathToRegexp.d.ts +5 -5
- package/build/es/modules/lwr/routerUtils/pathToRegexp.js +415 -0
- package/build/es/modules/lwr/routerUtils/routeDefUtils.js +204 -0
- package/build/es/modules/lwr/routerUtils/routeUtils.js +239 -0
- package/build/es/modules/lwr/routerUtils/routerUtils.js +19 -0
- package/build/es/modules/lwr/routerUtils/typeUtils.js +112 -0
- package/build/{modules → es/modules}/lwr/routerUtils/types.d.ts +23 -23
- package/build/es/modules/lwr/routerUtils/types.js +2 -0
- package/build/es/modules/lwr/routerUtils/uriUtils.js +134 -0
- package/build/modules/lwr/contextUtils/contextUtils.js +5 -0
- package/build/modules/lwr/contextUtils/navigationApiStore.js +7 -0
- package/build/modules/lwr/domRouter/domRouter.js +6 -0
- package/build/modules/lwr/domRouterUtils/historyUtils.js +0 -1
- package/build/modules/lwr/outlet/outlet.css +1 -1
- package/build/modules/lwr/outlet/outlet.html +2 -2
- package/build/modules/lwr/outlet/outlet.js +3 -4
- package/build/modules/lwr/router/router.js +3 -0
- package/build/modules/lwr/routerContainer/utils.js +1 -3
- package/build/modules/lwr/routerUtils/pathToRegexp.js +17 -0
- package/build/modules/lwr/routerUtils/typeUtils.js +0 -1
- package/package.json +17 -9
- package/pageObjects/outlet.cjs +21 -4
- package/pageObjects/outlet.d.ts +10 -2
- package/pageObjects/outlet.js +22 -5
- package/build/modules/lwr/navigationMixinHacks/navigationMixinHacks.d.ts +0 -7
- /package/build/{modules → es/modules}/lwr/contextProvider/contextProvider.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/contextUtils/contextInfo.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/contextUtils/contextUtils.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/contextUtils/navigationApiStore.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/domRouterUtils/domRouterUtils.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/domRouterUtils/historyUtils.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/domRouterUtils/uriUtils.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/historyRouter/historyRouter.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/navigation/navigation.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/navigation/navigationApi.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/navigation/navigationMixin.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/observable/observable.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/router/router.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/routerBridge/routerBridge.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/routerContainer/routerContainer.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/routerContainer/utils.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/routerErrors/routerErrors.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/routerUtils/domUtils.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/routerUtils/parseUtils.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/routerUtils/routeDefUtils.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/routerUtils/routeUtils.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/routerUtils/routerUtils.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/routerUtils/typeUtils.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/routerUtils/uriUtils.d.ts +0 -0
- /package/build/{services → es/services}/index.d.ts +0 -0
- /package/build/{services → es/services}/index.js +0 -0
- /package/build/{services → es/services}/module-provider/index.d.ts +0 -0
- /package/build/{services → es/services}/module-provider/index.js +0 -0
- /package/build/{services → es/services}/module-provider/utils.d.ts +0 -0
- /package/build/{services → es/services}/module-provider/utils.js +0 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2019, salesforce.com, inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
6
|
+
*/
|
|
7
|
+
import { DomRouterImpl } from 'lwr/domRouter';
|
|
8
|
+
import { getRelativeUrl, replace, set } from 'lwr/domRouterUtils';
|
|
9
|
+
import { hasDocument } from 'lwr/routerUtils';
|
|
10
|
+
/*
|
|
11
|
+
* Provides a Router rooted to the window, which controls the browser history by default.
|
|
12
|
+
*/
|
|
13
|
+
export class HistoryRouter extends DomRouterImpl {
|
|
14
|
+
constructor() {
|
|
15
|
+
super(...arguments);
|
|
16
|
+
this.historyDisabled = false;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Override.
|
|
20
|
+
* Initialize with the current route and listen to the popstate event for future changes.
|
|
21
|
+
*/
|
|
22
|
+
connect() {
|
|
23
|
+
super.connect();
|
|
24
|
+
if (!this.historyDisabled) {
|
|
25
|
+
// Subscribe to the Window.popstate event to listen for URL changes.
|
|
26
|
+
hasDocument && window.addEventListener('popstate', this.onpopstate.bind(this));
|
|
27
|
+
// Initialize using the current URL state
|
|
28
|
+
this.onpopstate();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
onpopstate() {
|
|
32
|
+
// Only the root should update the url since it has the full context
|
|
33
|
+
if (!this.parent && hasDocument) {
|
|
34
|
+
this.catchBrowserUpdate(getRelativeUrl(document.location.href));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
disconnect() {
|
|
38
|
+
super.disconnect();
|
|
39
|
+
hasDocument && window.removeEventListener('popstate', this.onpopstate);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Override.
|
|
43
|
+
* Update the browser history if the preNavigate hooks.
|
|
44
|
+
*
|
|
45
|
+
* @param {string} url - The URL to go to
|
|
46
|
+
* @param {boolean} shouldReplace - True if the current history state should be replaced
|
|
47
|
+
* @param {boolean} updateHistory - True if the browser history should be updated with the new URL
|
|
48
|
+
*
|
|
49
|
+
* @returns {boolean} - True if the processing was NOT blocked by a preNavigate listener
|
|
50
|
+
*/
|
|
51
|
+
async process(url, shouldReplace, updateHistory = true) {
|
|
52
|
+
// Run the preNavigate hooks to check if this event should be processed.
|
|
53
|
+
const canContinue = await super.process(url);
|
|
54
|
+
// Update the window location if this router is connected and is the root router
|
|
55
|
+
if (canContinue && !this.historyDisabled && updateHistory && this.connected && !this.parent) {
|
|
56
|
+
// Update the window history.
|
|
57
|
+
if (shouldReplace) {
|
|
58
|
+
replace(url);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
set(url);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return canContinue;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Update the root route, and trickle down the router tree.
|
|
68
|
+
* Redirect to use the base path, if it is missing.
|
|
69
|
+
*
|
|
70
|
+
* @param {string} url - The URL to go to
|
|
71
|
+
*/
|
|
72
|
+
catchBrowserUpdate(url) {
|
|
73
|
+
this.process(url, false, false);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Create a new root Router, attach to the Window.
|
|
78
|
+
* This is the public, programmitic API for root router creation.
|
|
79
|
+
* An application can only have ONE root router.
|
|
80
|
+
*
|
|
81
|
+
* @param {object} config - The router config object
|
|
82
|
+
*
|
|
83
|
+
* @returns {object} - { addPreNavigate, addPostNavigate, addErrorNavigate, connect, disconnect }
|
|
84
|
+
*/
|
|
85
|
+
export function createHistoryRouter(config, router, target) {
|
|
86
|
+
return new HistoryRouter(config, router, target);
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=historyRouter.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2019, salesforce.com, inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
6
|
+
*/
|
|
7
|
+
// Wires and their return types
|
|
8
|
+
export { NavigationContext } from 'lwr/navigationContext'; // provides ContextId
|
|
9
|
+
export { CurrentPageReference } from 'lwr/currentPageReference'; // provides PageReference
|
|
10
|
+
export { CurrentView } from 'lwr/currentView'; // Provides Constructable
|
|
11
|
+
// The LWR Navigation context singleton (i.e. CACHE in lwr/contextUtils#navigationApiStore)
|
|
12
|
+
// These functions and classes must only be included in an app ONCE:
|
|
13
|
+
// - 'lwr/navigation' must be excluded from bundling
|
|
14
|
+
// - ALWAYS import these from 'lwr/navigation', even internally
|
|
15
|
+
export { ContextInfo, getNavigationHelm, registerNavigationHelm, generateContextualWireAdapter, } from 'lwr/contextUtils';
|
|
16
|
+
// NavigationMixin has a dependency on navigate and generateUrl
|
|
17
|
+
// They need to be sibling exports in order to avoid a circular dependency
|
|
18
|
+
export { navigate, generateUrl } from './navigationApi';
|
|
19
|
+
export { NavigationMixin } from './navigationMixin';
|
|
20
|
+
//# sourceMappingURL=navigation.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { getNavigationHelm } from 'lwr/contextUtils';
|
|
2
|
+
/**
|
|
3
|
+
* Navigate programmatically.
|
|
4
|
+
* The Promise used within is deliberately not returned.
|
|
5
|
+
*
|
|
6
|
+
* @param {HTMLElement} context - The navigation context
|
|
7
|
+
* @param {object | string} loc - A route or URL for navigating
|
|
8
|
+
* @param {*} options - Usually a boolean; when true the previous browser history
|
|
9
|
+
* entry should be replaced by this one
|
|
10
|
+
*/
|
|
11
|
+
export function navigate(context, pageReference, replace) {
|
|
12
|
+
const api = getNavigationHelm(context);
|
|
13
|
+
api.navigate(pageReference, replace);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Generate a URL for the given route.
|
|
17
|
+
*
|
|
18
|
+
* @param {HTMLElement} context - The navigation context
|
|
19
|
+
* @param {object} route - A route
|
|
20
|
+
*
|
|
21
|
+
* @returns {Promise<string>}
|
|
22
|
+
*/
|
|
23
|
+
export function generateUrl(context, pageReference) {
|
|
24
|
+
const api = getNavigationHelm(context);
|
|
25
|
+
return api.generateUrl(pageReference);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=navigationApi.js.map
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2019, salesforce.com, inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
6
|
+
*/
|
|
7
|
+
// TODO: The way NavigationMixin is currently set up, we need to use unique
|
|
8
|
+
// symbols as indexes, which typescript does not like, necessitating 'any'
|
|
9
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
10
|
+
import { invariant, generateMessage, messages } from 'lwr/routerErrors';
|
|
11
|
+
import { generateUrl, navigate } from './navigationApi';
|
|
12
|
+
import { CONTEXT_ID_BACKDOOR } from 'lwr/navigationMixinHacks';
|
|
13
|
+
/*
|
|
14
|
+
* Exports the NavigationMixin and CurrentPageReference wire adapter.
|
|
15
|
+
* Uses the NavigationContext wire to retrieve the current navigation context/node.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Provides navigate() and generateUrl() functionality.
|
|
19
|
+
* Here, navigate() can take either a route OR a string URL.
|
|
20
|
+
* Functionality pulled in from the current navigation context.
|
|
21
|
+
* Used by components as a Mixin to extend their own functionality.
|
|
22
|
+
*
|
|
23
|
+
* @param {HTMLElement} Base - A class instance
|
|
24
|
+
*/
|
|
25
|
+
const Navigate = Symbol('Navigate');
|
|
26
|
+
const GenerateUrl = Symbol('GenerateUrl');
|
|
27
|
+
const NavContext = Symbol('NavContext');
|
|
28
|
+
const GetContext = Symbol('NavContext');
|
|
29
|
+
function NavigationMixin(Base) {
|
|
30
|
+
invariant(typeof Base.prototype.dispatchEvent === 'function', messages.INVALID_MIXIN_CMP, [
|
|
31
|
+
Base.toString(),
|
|
32
|
+
]);
|
|
33
|
+
class Mixin extends Base {
|
|
34
|
+
[GetContext]() {
|
|
35
|
+
/*
|
|
36
|
+
* NavigationMixin is a special snowflake that needs to use the DOM to lookup its parent ContextId,
|
|
37
|
+
* but is not allowed to use the traditional means: @wire(NavigationContext) to do so, because it's
|
|
38
|
+
* part of the navigation API, which must be importable into the priming environment, and thus cannot
|
|
39
|
+
* import anything from LWC. Best solution we have so far is a CustomEvent that allows the module to
|
|
40
|
+
* be imported, but cannot be RAN in the priming environment. This is acceptable because it's generally
|
|
41
|
+
* a component that extends LightningElement that's using the NavigationMixin, rather than any priming code.
|
|
42
|
+
*/
|
|
43
|
+
if (!this[NavContext]) {
|
|
44
|
+
this.dispatchEvent(
|
|
45
|
+
// eslint exception reason: this code is safe to be imported into the priming environment, but is not runnable, and not expected to be runnable
|
|
46
|
+
// eslint-disable-next-line no-undef
|
|
47
|
+
new CustomEvent(CONTEXT_ID_BACKDOOR, {
|
|
48
|
+
bubbles: true,
|
|
49
|
+
composed: true,
|
|
50
|
+
detail: {
|
|
51
|
+
callback: (contextId) => {
|
|
52
|
+
this[NavContext] = contextId;
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
}));
|
|
56
|
+
if (!this[NavContext]) {
|
|
57
|
+
throw new Error(generateMessage(messages.MISSING_CONTEXT));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
[Navigate](pageRef, replace) {
|
|
62
|
+
this[GetContext]();
|
|
63
|
+
navigate(this[NavContext], pageRef, replace);
|
|
64
|
+
}
|
|
65
|
+
async [GenerateUrl](pageRef) {
|
|
66
|
+
this[GetContext]();
|
|
67
|
+
return generateUrl(this[NavContext], pageRef);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return Mixin;
|
|
71
|
+
}
|
|
72
|
+
NavigationMixin.Navigate = Navigate;
|
|
73
|
+
NavigationMixin.GenerateUrl = GenerateUrl;
|
|
74
|
+
NavigationMixin.NavContext = NavContext;
|
|
75
|
+
export { NavigationMixin };
|
|
76
|
+
//# sourceMappingURL=navigationMixin.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
1
|
+
export type ContextId = () => void;
|
|
2
|
+
type NavigationContextType = ContextId | undefined;
|
|
3
3
|
export declare const NavigationContext: import("lwr/contextUtils").ContextualWireAdapter<NavigationContextType, unknown, NavigationContextType>;
|
|
4
4
|
export {};
|
|
5
5
|
//# sourceMappingURL=navigationContext.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2019, salesforce.com, inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
6
|
+
*/
|
|
7
|
+
import { ContextInfo, generateContextualWireAdapter } from 'lwr/contextUtils';
|
|
8
|
+
const NAVIGATION_CONTEXT = new ContextInfo(undefined);
|
|
9
|
+
export const NavigationContext = generateContextualWireAdapter(NAVIGATION_CONTEXT);
|
|
10
|
+
//# sourceMappingURL=navigationContext.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const CONTEXT_ID_BACKDOOR: string;
|
|
2
|
+
export type NavigationContextBackdoorEvent = globalThis.CustomEvent<NavigationContextBackdoorEventPayload>;
|
|
3
|
+
import type { ContextId } from 'lwr/navigation';
|
|
4
|
+
export type NavigationContextBackdoorEventPayload = {
|
|
5
|
+
callback: (contextId: ContextId) => void;
|
|
6
|
+
};
|
|
7
|
+
//# sourceMappingURL=navigationMixinHacks.d.ts.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// This is claimed to be a portable module, but it really isn't. It can be imported, but will not RUN in the portable environment
|
|
2
|
+
import { guid } from 'lwr/routerUtils';
|
|
3
|
+
// A hack to get NavigationMixin working without requiring lwc
|
|
4
|
+
export const CONTEXT_ID_BACKDOOR = `universalcontainergetnavigationcontext${guid()}`;
|
|
5
|
+
//# sourceMappingURL=navigationMixinHacks.js.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a simple observable object, which can have any number of observers.
|
|
3
|
+
* @returns {object}
|
|
4
|
+
*/
|
|
5
|
+
export function createObservable() {
|
|
6
|
+
// Keep track of the current value and error.
|
|
7
|
+
let currentValue = undefined;
|
|
8
|
+
let currentError = undefined;
|
|
9
|
+
// Observer list with functions to add and remove members safely.
|
|
10
|
+
let observers = [];
|
|
11
|
+
const addObserver = (obs) => {
|
|
12
|
+
observers.push(obs);
|
|
13
|
+
};
|
|
14
|
+
const removeObserver = (obsIndex) => {
|
|
15
|
+
// copy on delete prevents issues when an observer unsubscribes while iterating over them
|
|
16
|
+
observers = [...observers.slice(0, obsIndex), ...observers.slice(obsIndex + 1)];
|
|
17
|
+
};
|
|
18
|
+
// On next, broadcast the value to all observers.
|
|
19
|
+
// Clear out current error.
|
|
20
|
+
const next = (value) => {
|
|
21
|
+
observers
|
|
22
|
+
.filter((obs) => obs !== null)
|
|
23
|
+
.forEach((obs) => obs.next && obs.next(value));
|
|
24
|
+
currentValue = value;
|
|
25
|
+
currentError = undefined;
|
|
26
|
+
};
|
|
27
|
+
// On error, broadcast the error to all observers.
|
|
28
|
+
// Clear out current value.
|
|
29
|
+
const error = (err) => {
|
|
30
|
+
observers
|
|
31
|
+
.filter((obs) => obs !== null)
|
|
32
|
+
.forEach((obs) => obs.error && obs.error(err));
|
|
33
|
+
currentValue = undefined;
|
|
34
|
+
currentError = err;
|
|
35
|
+
};
|
|
36
|
+
// On complete, call complete on all observers.
|
|
37
|
+
// Clear out all observers + current value and error.
|
|
38
|
+
const complete = () => {
|
|
39
|
+
observers
|
|
40
|
+
.filter((obs) => obs !== null)
|
|
41
|
+
.forEach((obs) => obs.complete && obs.complete());
|
|
42
|
+
observers = [];
|
|
43
|
+
currentValue = undefined;
|
|
44
|
+
currentError = undefined;
|
|
45
|
+
};
|
|
46
|
+
// Observable can be subscribed and unsubscribed, by multiple observers.
|
|
47
|
+
// When replay is true, the currentValue will be emitted synchronously.
|
|
48
|
+
const subscribe = (obs, replay = true) => {
|
|
49
|
+
addObserver(obs);
|
|
50
|
+
// Push the current value and error, if they exist.
|
|
51
|
+
if (currentValue && replay) {
|
|
52
|
+
obs.next(currentValue);
|
|
53
|
+
}
|
|
54
|
+
if (currentError) {
|
|
55
|
+
error(currentError);
|
|
56
|
+
}
|
|
57
|
+
// On unsubscribe, the observer is nulled out.
|
|
58
|
+
const obsIndex = observers.length - 1;
|
|
59
|
+
return {
|
|
60
|
+
unsubscribe: () => removeObserver(obsIndex),
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
// Return the observation methods + the associated observable.
|
|
64
|
+
return {
|
|
65
|
+
next,
|
|
66
|
+
error,
|
|
67
|
+
complete,
|
|
68
|
+
subscribe,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=observable.js.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { LightningElement, api, wire } from 'lwc';
|
|
8
|
+
import { CurrentView } from 'lwr/navigation';
|
|
9
|
+
export default class Outlet extends LightningElement {
|
|
10
|
+
constructor() {
|
|
11
|
+
super(...arguments);
|
|
12
|
+
this.refocusOff = false;
|
|
13
|
+
this.viewName = 'default';
|
|
14
|
+
this.viewCtor = undefined;
|
|
15
|
+
this.previousViewCtor = undefined;
|
|
16
|
+
this.hasError = false;
|
|
17
|
+
}
|
|
18
|
+
// Get a reference to the current view
|
|
19
|
+
setView(currentView) {
|
|
20
|
+
this.hasError = false;
|
|
21
|
+
this.viewCtor = currentView;
|
|
22
|
+
this.refocus();
|
|
23
|
+
}
|
|
24
|
+
renderedCallback() {
|
|
25
|
+
if (this.viewCtor !== this.previousViewCtor) {
|
|
26
|
+
this.previousViewCtor = this.viewCtor;
|
|
27
|
+
const viewChangedEvent = new CustomEvent('viewchange', { detail: this.viewCtor });
|
|
28
|
+
this.dispatchEvent(viewChangedEvent);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
errorCallback(e, stack) {
|
|
32
|
+
// when hosted view has unhandled error thrown:
|
|
33
|
+
// - display the error slot
|
|
34
|
+
// - dispatch the "viewchange" event
|
|
35
|
+
// - call the outletErrorCallback()
|
|
36
|
+
this.hasError = true;
|
|
37
|
+
const viewErrorEvent = new CustomEvent('viewerror', { detail: { error: e, stack } });
|
|
38
|
+
this.dispatchEvent(viewErrorEvent);
|
|
39
|
+
if (this.outletErrorCallback) {
|
|
40
|
+
this.outletErrorCallback(e, stack);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
console.error(e);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
refocus() {
|
|
47
|
+
// If the feature is not turned off, put the browser focus onto the dynamic content.
|
|
48
|
+
// This is done after a route change for accessibility.
|
|
49
|
+
if (!this.refocusOff) {
|
|
50
|
+
const region = this.querySelector(`div[role='region'].outlet`);
|
|
51
|
+
if (region)
|
|
52
|
+
region.focus();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
Outlet.renderMode = 'light';
|
|
57
|
+
__decorate([
|
|
58
|
+
api
|
|
59
|
+
], Outlet.prototype, "refocusOff", void 0);
|
|
60
|
+
__decorate([
|
|
61
|
+
api
|
|
62
|
+
], Outlet.prototype, "outletErrorCallback", void 0);
|
|
63
|
+
__decorate([
|
|
64
|
+
api
|
|
65
|
+
], Outlet.prototype, "viewName", void 0);
|
|
66
|
+
__decorate([
|
|
67
|
+
wire(CurrentView, { viewName: '$viewName' })
|
|
68
|
+
], Outlet.prototype, "setView", null);
|
|
69
|
+
//# sourceMappingURL=outlet.js.map
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2019, salesforce.com, inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
6
|
+
*/
|
|
7
|
+
import { freeze, getUrlFromPageReference, getPageReferenceFromUrl, matchRouteByUrl, getUrlFromPageReferenceAndRouteDef, parseRoutes, } from 'lwr/routerUtils';
|
|
8
|
+
import { generateMessage, messages } from 'lwr/routerErrors';
|
|
9
|
+
import { createObservable } from 'lwr/observable';
|
|
10
|
+
class RouterImpl {
|
|
11
|
+
/**
|
|
12
|
+
* Create and configure the Router.
|
|
13
|
+
*
|
|
14
|
+
* @param {object} config - The router config object, all properties are optional
|
|
15
|
+
*/
|
|
16
|
+
constructor(config) {
|
|
17
|
+
this.deprecatedConfig = {};
|
|
18
|
+
this.routeHandlerId = 0;
|
|
19
|
+
this.compiledRoutes = [];
|
|
20
|
+
this.routeObservable = createObservable();
|
|
21
|
+
this.config = {
|
|
22
|
+
basePath: config.basePath || '',
|
|
23
|
+
caseSensitive: Boolean(config.caseSensitive),
|
|
24
|
+
routes: config.routes || [],
|
|
25
|
+
generateUrl: (address) => getUrlFromPageReference(address, this.compiledRoutes, this.config.basePath),
|
|
26
|
+
parseUrl: (url) => getPageReferenceFromUrl(url, this.compiledRoutes, this.config.basePath),
|
|
27
|
+
};
|
|
28
|
+
const { DEPRECATED_getRouteFromUrl, DEPRECATED_getUrlFromRoute } = config;
|
|
29
|
+
if (DEPRECATED_getRouteFromUrl) {
|
|
30
|
+
this.deprecatedConfig.DEPRECATED_getRouteFromUrl = DEPRECATED_getRouteFromUrl;
|
|
31
|
+
}
|
|
32
|
+
if (DEPRECATED_getUrlFromRoute) {
|
|
33
|
+
this.deprecatedConfig.DEPRECATED_getUrlFromRoute = DEPRECATED_getUrlFromRoute;
|
|
34
|
+
}
|
|
35
|
+
this.compiledRoutes = parseRoutes(this.config);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Convert address to url string.
|
|
39
|
+
*
|
|
40
|
+
* @param address
|
|
41
|
+
*/
|
|
42
|
+
generateUrl(address) {
|
|
43
|
+
const { DEPRECATED_getUrlFromRoute: getUrlFromRoute } = this.deprecatedConfig;
|
|
44
|
+
if (getUrlFromRoute) {
|
|
45
|
+
return getUrlFromRoute(address, this.config.generateUrl);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
return this.config.generateUrl(address);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Convert url string to address.
|
|
53
|
+
*
|
|
54
|
+
* @param url
|
|
55
|
+
*/
|
|
56
|
+
parseUrl(url) {
|
|
57
|
+
const { DEPRECATED_getRouteFromUrl: getRouteFromUrl } = this.deprecatedConfig;
|
|
58
|
+
if (getRouteFromUrl) {
|
|
59
|
+
return getRouteFromUrl(url, this.config.parseUrl);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
return this.config.parseUrl(url);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Match the given url against the internal route definitions and return info
|
|
67
|
+
* on the match.
|
|
68
|
+
*
|
|
69
|
+
* @param address - The address to match
|
|
70
|
+
*/
|
|
71
|
+
matchRoute(address) {
|
|
72
|
+
const url = typeof address === 'string' ? address : this.generateUrl(address);
|
|
73
|
+
if (url === null) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const match = matchRouteByUrl(url, this.compiledRoutes, this.config.basePath);
|
|
77
|
+
const pathMatch = match &&
|
|
78
|
+
getUrlFromPageReferenceAndRouteDef(match.route.pageReference, match.routeDefinition, this.config.basePath);
|
|
79
|
+
if (!match || !pathMatch) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
pathMatch,
|
|
84
|
+
route: match.route,
|
|
85
|
+
routeDefinition: match.routeDefinition.original,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Given an address, return the associated viewset by calling the route handler.
|
|
90
|
+
*
|
|
91
|
+
* @param address - The address to match to a viewset
|
|
92
|
+
*/
|
|
93
|
+
async resolveView(address) {
|
|
94
|
+
return new Promise((resolve, reject) => {
|
|
95
|
+
// get the RoutingMatch: { pathMatch, route, routeDefinition }
|
|
96
|
+
const routingMatch = this.matchRoute(address);
|
|
97
|
+
if (!routingMatch) {
|
|
98
|
+
return reject(generateMessage(messages.NO_ROUTE_MATCH, [JSON.stringify(address)]));
|
|
99
|
+
}
|
|
100
|
+
// retrieve the route handler and use it to get the RouteDestination
|
|
101
|
+
// handler ~= import('my/handlerModule');
|
|
102
|
+
return routingMatch.routeDefinition.handler().then((handlerModule) => {
|
|
103
|
+
// the route handler class is the module's default export
|
|
104
|
+
const routeHandlerClass = handlerModule.default;
|
|
105
|
+
if (!routeHandlerClass) {
|
|
106
|
+
return reject(generateMessage(messages.INVALID_ROUTE_HANDLER, [routingMatch.routeDefinition.id]));
|
|
107
|
+
}
|
|
108
|
+
// instantiate an instance of the route handler class
|
|
109
|
+
// and pass in resolve() as the RouteHandlerCallback
|
|
110
|
+
const routeHandler = new routeHandlerClass(resolve);
|
|
111
|
+
// update() will invoke the resolve callback with the matching RouteDestination/viewset
|
|
112
|
+
if (routeHandler) {
|
|
113
|
+
routeHandler.update(routingMatch.route);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Process the given URL by matching it to an existing route and looking up
|
|
120
|
+
* the associated view
|
|
121
|
+
*
|
|
122
|
+
* @param {object} address
|
|
123
|
+
*/
|
|
124
|
+
navigate(address) {
|
|
125
|
+
const routingMatch = this.matchRoute(address);
|
|
126
|
+
if (!routingMatch) {
|
|
127
|
+
throw new Error(generateMessage(messages.MISSING_ROUTE, [JSON.stringify(address)]));
|
|
128
|
+
}
|
|
129
|
+
this.pendingRoute = { ...routingMatch };
|
|
130
|
+
// map the matched route to a view and notify subscribers
|
|
131
|
+
this._mapView(this.pendingRoute);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* lightning/navigation
|
|
135
|
+
* Subscribe a callback to the Observable on the current route of this router.
|
|
136
|
+
*
|
|
137
|
+
* @param {function} callback - A callback function invoked when the navigation state changes
|
|
138
|
+
* callback(route, routeDef)
|
|
139
|
+
* @param {boolean} replay - Flag to determine if callback should be called with current route and data immediately
|
|
140
|
+
*/
|
|
141
|
+
subscribe(callback, replay) {
|
|
142
|
+
return this.routeObservable.subscribe({
|
|
143
|
+
next: callback,
|
|
144
|
+
error: () => {
|
|
145
|
+
// ignore
|
|
146
|
+
},
|
|
147
|
+
complete: () => {
|
|
148
|
+
// nothing to clean up
|
|
149
|
+
},
|
|
150
|
+
}, Boolean(replay));
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Map the given route to a view set that can be emitted to subscribers. After the correct handler
|
|
154
|
+
* is determined (picked from the routers routes in the config), view mapping is delegated to the
|
|
155
|
+
* handler with a callback to finish processing and emit the final result.
|
|
156
|
+
*
|
|
157
|
+
* @param {*} route
|
|
158
|
+
* @param {*} routeInfo
|
|
159
|
+
*/
|
|
160
|
+
async _mapView(routingMatch) {
|
|
161
|
+
// set an id in case _mapView is called again.
|
|
162
|
+
const routeHandlerId = Math.random();
|
|
163
|
+
this.routeHandlerId = routeHandlerId;
|
|
164
|
+
const handlerModule = await routingMatch.routeDefinition.handler();
|
|
165
|
+
const routeHandlerClass = handlerModule.default;
|
|
166
|
+
if (!routeHandlerClass) {
|
|
167
|
+
throw new Error(generateMessage(messages.INVALID_ROUTE_HANDLER, [routingMatch.routeDefinition.id]));
|
|
168
|
+
}
|
|
169
|
+
this.routeHandler = new routeHandlerClass((routeDestination) => {
|
|
170
|
+
// If the id in the callback no longer matches the latest id from the router, it indicates _mapView
|
|
171
|
+
// was called again after this handler began loading but before the callback completed.
|
|
172
|
+
this._updateView(routeHandlerId, routeDestination);
|
|
173
|
+
});
|
|
174
|
+
if (this.routeHandler) {
|
|
175
|
+
this.routeHandler.update(routingMatch.route);
|
|
176
|
+
}
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Set internal state, notify observable with frozen data, and propagate to child router
|
|
181
|
+
*
|
|
182
|
+
* @param {*} viewDestination
|
|
183
|
+
*/
|
|
184
|
+
_updateView(viewHandlerId, routeDestination) {
|
|
185
|
+
if (!routeDestination || viewHandlerId !== this.routeHandlerId) {
|
|
186
|
+
// no-op, a status handler stopped the action
|
|
187
|
+
// or this callback does not belong to the most recent view handler.
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (!this.pendingRoute) {
|
|
191
|
+
throw new Error('Trying to commit route state without a route');
|
|
192
|
+
}
|
|
193
|
+
const viewset = freeze(routeDestination.viewset);
|
|
194
|
+
this.routeObservable.next({ ...this.pendingRoute, ...routeDestination, viewset });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// export the function only instead of the class, may give us more flexibility in the future
|
|
198
|
+
export function createRouter(config = {}) {
|
|
199
|
+
return new RouterImpl(config);
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=router.js.map
|