brew-js-react 0.5.4 → 0.5.6
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/brew.d.ts +9 -0
- package/dist/brew-js-react.js +267 -194
- package/dist/brew-js-react.js.map +1 -1
- package/dist/brew-js-react.min.js +2 -2
- package/dist/brew-js-react.min.js.map +1 -1
- package/entry.js +7 -4
- package/hooks.d.ts +22 -0
- package/hooks.js +63 -19
- package/mixin.js +9 -9
- package/mixins/FlyoutMixin.js +13 -15
- package/package.json +3 -3
- package/view.d.ts +43 -0
- package/view.js +73 -50
package/hooks.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { createElement, useEffect, useRef, useState } from "react";
|
|
2
|
-
import { ViewStateProvider, useObservableProperty, useUpdateTrigger } from "zeta-dom-react";
|
|
3
|
-
import { definePrototype, delay, each, extend, kv,
|
|
1
|
+
import { createElement, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { ViewStateProvider, useMemoizedFunction, useObservableProperty, useUpdateTrigger } from "zeta-dom-react";
|
|
3
|
+
import { catchAsync, definePrototype, delay, each, equal, extend, freeze, isFunction, isPlainObject, keys, kv, mapObject, throwNotFunction } from "zeta-dom/util";
|
|
4
4
|
import { ZetaEventContainer } from "zeta-dom/events";
|
|
5
|
+
import { getQueryParam, setQueryParam } from "brew-js/util/common";
|
|
6
|
+
import { parsePath } from "brew-js/util/path";
|
|
5
7
|
import { app } from "./app.js";
|
|
6
8
|
import { useViewContext } from "./view.js";
|
|
7
9
|
|
|
@@ -81,34 +83,23 @@ export function useAppReadyState() {
|
|
|
81
83
|
export function useRouteParam(name, defaultValue) {
|
|
82
84
|
const container = useViewContext();
|
|
83
85
|
const params = container.page.params;
|
|
84
|
-
const route = app.route;
|
|
85
86
|
const value = params[name] || '';
|
|
86
87
|
const ref = useRef(value);
|
|
87
88
|
const forceUpdate = useUpdateTrigger();
|
|
88
89
|
useEffect(function () {
|
|
89
90
|
var setValue = function () {
|
|
90
|
-
var current =
|
|
91
|
+
var current = container.page.params[name] || '';
|
|
91
92
|
if (current !== ref.current) {
|
|
92
|
-
|
|
93
|
-
ref.current = current;
|
|
94
|
-
forceUpdate();
|
|
95
|
-
} else {
|
|
96
|
-
watch(container, 'active', setValue);
|
|
97
|
-
}
|
|
93
|
+
forceUpdate();
|
|
98
94
|
}
|
|
99
95
|
};
|
|
100
96
|
// route parameter might be changed after state initialization and before useEffect hook is called
|
|
101
97
|
setValue();
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
setImmediateOnce(setValue);
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
console.error('Route parameter ' + name + ' does not exist');
|
|
108
|
-
}, [name, defaultValue]);
|
|
98
|
+
return container.on('pagechange', setValue);
|
|
99
|
+
}, [name]);
|
|
109
100
|
ref.current = value;
|
|
110
101
|
if (defaultValue && container.active && (!value || (name === 'remainingSegments' && value === '/'))) {
|
|
111
|
-
app.navigate(route.getPath(extend({}, params, kv(name, defaultValue))), true);
|
|
102
|
+
app.navigate(app.route.getPath(extend({}, params, kv(name, defaultValue))), true);
|
|
112
103
|
}
|
|
113
104
|
return value;
|
|
114
105
|
}
|
|
@@ -130,6 +121,59 @@ export function useRouteState(key, defaultValue, snapshotOnUpdate) {
|
|
|
130
121
|
return state;
|
|
131
122
|
}
|
|
132
123
|
|
|
124
|
+
export function useQueryParam(key, value, snapshotOnUpdate) {
|
|
125
|
+
if (isPlainObject(key)) {
|
|
126
|
+
snapshotOnUpdate = value;
|
|
127
|
+
value = key;
|
|
128
|
+
key = false;
|
|
129
|
+
}
|
|
130
|
+
var container = useViewContext();
|
|
131
|
+
var getParams = function () {
|
|
132
|
+
return mapObject(key === false ? value : kv(key, value), function (v, i) {
|
|
133
|
+
return getQueryParam(i, app.path) || v || '';
|
|
134
|
+
});
|
|
135
|
+
};
|
|
136
|
+
var state = useState([]);
|
|
137
|
+
useMemo(function () {
|
|
138
|
+
state[0].splice(0, 2, getParams());
|
|
139
|
+
}, [key]);
|
|
140
|
+
var current = state[0][0];
|
|
141
|
+
var trackChanges = function (values) {
|
|
142
|
+
if (!equal(values, current)) {
|
|
143
|
+
extend(current, values);
|
|
144
|
+
state[1]([current]);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
var setParams = useMemoizedFunction(function (values) {
|
|
148
|
+
if (key !== false) {
|
|
149
|
+
values = kv(key, isFunction(values) ? values(current[key]) : values);
|
|
150
|
+
} else if (isFunction(values)) {
|
|
151
|
+
values = values(extend({}, current));
|
|
152
|
+
}
|
|
153
|
+
if (container.active) {
|
|
154
|
+
var url = parsePath(app.path);
|
|
155
|
+
var search = keys(values).reduce(function (v, i) {
|
|
156
|
+
return values[i] !== current[i] ? setQueryParam(i, values[i] || null, v) : v;
|
|
157
|
+
}, url.search);
|
|
158
|
+
if (search !== url.search) {
|
|
159
|
+
if (snapshotOnUpdate) {
|
|
160
|
+
app.snapshot();
|
|
161
|
+
}
|
|
162
|
+
catchAsync(app.navigate(search + url.hash, true));
|
|
163
|
+
trackChanges(getParams());
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
useEffect(function () {
|
|
168
|
+
return app.watch('path', function () {
|
|
169
|
+
if (container.active) {
|
|
170
|
+
trackChanges(getParams());
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}, [key]);
|
|
174
|
+
return [key !== false ? current[key] : (state[0][1] || (state[0][1] = freeze(extend({}, current)))), setParams];
|
|
175
|
+
}
|
|
176
|
+
|
|
133
177
|
export function ViewStateContainer(props) {
|
|
134
178
|
const cache = useState({})[0];
|
|
135
179
|
const container = useViewContextWithEffect(function (cur) {
|
package/mixin.js
CHANGED
|
@@ -27,15 +27,15 @@ function createUseFunction(ctor) {
|
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
export const useAnimateMixin = createUseFunction(AnimateMixin);
|
|
31
|
-
export const useAnimateSequenceMixin = createUseFunction(AnimateSequenceMixin);
|
|
32
|
-
export const useClassNameToggleMixin = createUseFunction(ClassNameToggleMixin);
|
|
33
|
-
export const useFlyoutMixin = createUseFunction(FlyoutMixin);
|
|
34
|
-
export const useFocusStateMixin = createUseFunction(FocusStateMixin);
|
|
35
|
-
export const useLoadingStateMixin = createUseFunction(LoadingStateMixin);
|
|
36
|
-
export const useScrollableMixin = createUseFunction(ScrollableMixin);
|
|
37
|
-
export const useScrollIntoViewMixin = createUseFunction(ScrollIntoViewMixin);
|
|
38
|
-
export const useUnmanagedClassNameMixin = createUseFunction(UnmanagedClassNameMixin);
|
|
30
|
+
export const useAnimateMixin = /*#__PURE__*/ createUseFunction(AnimateMixin);
|
|
31
|
+
export const useAnimateSequenceMixin = /*#__PURE__*/ createUseFunction(AnimateSequenceMixin);
|
|
32
|
+
export const useClassNameToggleMixin = /*#__PURE__*/ createUseFunction(ClassNameToggleMixin);
|
|
33
|
+
export const useFlyoutMixin = /*#__PURE__*/ createUseFunction(FlyoutMixin);
|
|
34
|
+
export const useFocusStateMixin = /*#__PURE__*/ createUseFunction(FocusStateMixin);
|
|
35
|
+
export const useLoadingStateMixin = /*#__PURE__*/ createUseFunction(LoadingStateMixin);
|
|
36
|
+
export const useScrollableMixin = /*#__PURE__*/ createUseFunction(ScrollableMixin);
|
|
37
|
+
export const useScrollIntoViewMixin = /*#__PURE__*/ createUseFunction(ScrollIntoViewMixin);
|
|
38
|
+
export const useUnmanagedClassNameMixin = /*#__PURE__*/ createUseFunction(UnmanagedClassNameMixin);
|
|
39
39
|
|
|
40
40
|
export function useMixin(ctor) {
|
|
41
41
|
return useSingleton(function () {
|
package/mixins/FlyoutMixin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { definePrototype, extend, makeArray, pick } from "zeta-dom/util";
|
|
2
|
-
import { closeFlyout,
|
|
2
|
+
import { closeFlyout, openFlyout } from "brew-js/domAction";
|
|
3
3
|
import { app } from "../app.js";
|
|
4
4
|
import ClassNameMixin from "./ClassNameMixin.js";
|
|
5
5
|
import FlyoutToggleMixin from "./FlyoutToggleMixin.js";
|
|
@@ -57,11 +57,7 @@ definePrototype(FlyoutMixin, ClassNameMixin, {
|
|
|
57
57
|
});
|
|
58
58
|
},
|
|
59
59
|
open: function (value, source) {
|
|
60
|
-
|
|
61
|
-
if (!isFlyoutOpen(element)) {
|
|
62
|
-
valueMap.set(element, value);
|
|
63
|
-
}
|
|
64
|
-
return openFlyout(element, source, this.getOptions());
|
|
60
|
+
return openFlyout(this.elements()[0], value, source, this.getOptions());
|
|
65
61
|
},
|
|
66
62
|
close: function (value) {
|
|
67
63
|
return closeFlyout(this.elements()[0], value);
|
|
@@ -84,6 +80,17 @@ definePrototype(FlyoutMixin, ClassNameMixin, {
|
|
|
84
80
|
var self = this;
|
|
85
81
|
FlyoutMixinSuper.initElement.call(self, element, state);
|
|
86
82
|
self.onDispose(app.on(element, {
|
|
83
|
+
flyoutshow: function (e) {
|
|
84
|
+
valueMap.set(element, e.data);
|
|
85
|
+
self.isFlyoutOpened = true;
|
|
86
|
+
self.visible = true;
|
|
87
|
+
},
|
|
88
|
+
flyoutclose: function () {
|
|
89
|
+
self.isFlyoutOpened = false;
|
|
90
|
+
},
|
|
91
|
+
flyouthide: function () {
|
|
92
|
+
self.visible = false;
|
|
93
|
+
},
|
|
87
94
|
animationstart: function () {
|
|
88
95
|
self.animating = true;
|
|
89
96
|
},
|
|
@@ -91,14 +98,5 @@ definePrototype(FlyoutMixin, ClassNameMixin, {
|
|
|
91
98
|
self.animating = false;
|
|
92
99
|
},
|
|
93
100
|
}, true));
|
|
94
|
-
},
|
|
95
|
-
onClassNameUpdated: function (element, prevState, state) {
|
|
96
|
-
var self = this;
|
|
97
|
-
var isFlyoutOpened = isFlyoutOpen(element);
|
|
98
|
-
if (!isFlyoutOpened) {
|
|
99
|
-
valueMap.delete(element);
|
|
100
|
-
}
|
|
101
|
-
self.visible = state.open;
|
|
102
|
-
self.isFlyoutOpened = isFlyoutOpened;
|
|
103
101
|
}
|
|
104
102
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brew-js-react",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"repository": "github:misonou/brew-js-react",
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@misonou/react-dom-client": "^1.1.1",
|
|
16
|
-
"brew-js": ">=0.6.
|
|
16
|
+
"brew-js": ">=0.6.5",
|
|
17
17
|
"waterpipe": "^2.5.0",
|
|
18
18
|
"zeta-dom": ">=0.4.10",
|
|
19
19
|
"zeta-dom-react": ">=0.4.14"
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"@babel/preset-env": "^7.16.11",
|
|
28
28
|
"@babel/preset-react": "^7.16.7",
|
|
29
29
|
"@jest/globals": "^26.6.2",
|
|
30
|
-
"@misonou/build-utils": "^1.1.
|
|
30
|
+
"@misonou/build-utils": "^1.1.3",
|
|
31
31
|
"@misonou/test-utils": "^1.0.3",
|
|
32
32
|
"@testing-library/dom": "^8.11.3",
|
|
33
33
|
"@testing-library/react": "^12.1.2",
|
package/view.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/// <reference path="./brew.d.ts" />
|
|
2
|
+
|
|
1
3
|
import { useRouteState } from "./hooks";
|
|
2
4
|
|
|
3
5
|
export type ViewComponentRootProps = React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
|
|
@@ -16,14 +18,35 @@ export interface ViewContextEventMap {
|
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
export interface ViewContext extends Zeta.ZetaEventDispatcher<ViewContextEventMap, ViewContext> {
|
|
21
|
+
/**
|
|
22
|
+
* Gets the parent view context, or `null` if there is no parent context.
|
|
23
|
+
*/
|
|
24
|
+
readonly parent: ViewContext | null;
|
|
25
|
+
/**
|
|
26
|
+
* Gets the HTML element associated with this view.
|
|
27
|
+
*/
|
|
19
28
|
readonly container: HTMLElement;
|
|
29
|
+
/**
|
|
30
|
+
* Gets the rendered view component.
|
|
31
|
+
*/
|
|
20
32
|
readonly view: ViewComponent<any>;
|
|
33
|
+
/**
|
|
34
|
+
* Gets whether the view is active.
|
|
35
|
+
*
|
|
36
|
+
* A rendered view becomes inactive if the view will be replaced by another view, or parent container will be unmounted.
|
|
37
|
+
* It may also be temporarily inactive during navigation.
|
|
38
|
+
*/
|
|
21
39
|
readonly active: boolean;
|
|
22
40
|
/**
|
|
23
41
|
* Gets information of current page that rendered the view.
|
|
24
42
|
* Unlike {@link Brew.WithRouter.page}, it will not change when user has navigated away and the current view is going to unmount.
|
|
25
43
|
*/
|
|
26
44
|
readonly page: Brew.PageInfo;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Gets view contexts rendered under this view container.
|
|
48
|
+
*/
|
|
49
|
+
getChildren(): ViewContext[];
|
|
27
50
|
}
|
|
28
51
|
|
|
29
52
|
export interface ViewProps<S = {}> {
|
|
@@ -97,6 +120,12 @@ export function registerErrorView(component: React.ComponentType<ErrorViewProps>
|
|
|
97
120
|
*/
|
|
98
121
|
export function isViewMatched(view: ViewComponent<any>): boolean;
|
|
99
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Returns whether a registered view component is currently being rendered.
|
|
125
|
+
* @param view A view component returned from {@link registerView}.
|
|
126
|
+
*/
|
|
127
|
+
export function isViewRendered(view: ViewComponent<any>): boolean;
|
|
128
|
+
|
|
100
129
|
/**
|
|
101
130
|
* Gets the view component that matches the current path within all registered views.
|
|
102
131
|
* However it does not imply if the view is in fact being rendered.
|
|
@@ -110,6 +139,13 @@ export function matchView(): ViewComponent<any> | undefined;
|
|
|
110
139
|
*/
|
|
111
140
|
export function matchView(views: ViewComponent<any>[]): ViewComponent<any> | undefined;
|
|
112
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Gets the view component that matches the current path within the given set of views.
|
|
144
|
+
* However it does not imply if the view is in fact being rendered.
|
|
145
|
+
* @param views A list of view components.
|
|
146
|
+
*/
|
|
147
|
+
export function matchView(...views: ViewComponent<any>[]): ViewComponent<any> | undefined;
|
|
148
|
+
|
|
113
149
|
/**
|
|
114
150
|
* Gets the view component that matches the specified path.
|
|
115
151
|
* @param path A valid path that could navigate to.
|
|
@@ -123,6 +159,13 @@ export function matchView(path: string): ViewComponent<any> | undefined;
|
|
|
123
159
|
*/
|
|
124
160
|
export function matchView(path: string, views: ViewComponent<any>[]): ViewComponent<any> | undefined;
|
|
125
161
|
|
|
162
|
+
/**
|
|
163
|
+
* Gets the view component that matches the specified path, within the given set of views.
|
|
164
|
+
* @param path A valid path that could navigate to.
|
|
165
|
+
* @param views A list of view components.
|
|
166
|
+
*/
|
|
167
|
+
export function matchView(path: string, ...views: ViewComponent<any>[]): ViewComponent<any> | undefined;
|
|
168
|
+
|
|
126
169
|
/**
|
|
127
170
|
* Renders view by matching current route state against registered route parameters of each supplied views.
|
|
128
171
|
* @param args A list of view components created by {@link registerView}.
|
package/view.js
CHANGED
|
@@ -3,7 +3,7 @@ import { useAsync } from "zeta-dom-react";
|
|
|
3
3
|
import dom from "zeta-dom/dom";
|
|
4
4
|
import { notifyAsync } from "zeta-dom/domLock";
|
|
5
5
|
import { ZetaEventContainer } from "zeta-dom/events";
|
|
6
|
-
import { any, arrRemove, catchAsync,
|
|
6
|
+
import { any, arrRemove, catchAsync, createPrivateStore, defineObservableProperty, defineOwnProperty, definePrototype, each, exclude, executeOnce, extend, freeze, grep, isArray, isFunction, isThenable, isUndefinedOrNull, keys, makeArray, map, noop, pick, randomId, resolveAll, setImmediate, single, throwNotFunction, watch } from "zeta-dom/util";
|
|
7
7
|
import { animateIn, animateOut } from "brew-js/anim";
|
|
8
8
|
import { removeQueryAndHash } from "brew-js/util/path";
|
|
9
9
|
import { app, onAppInit } from "./app.js";
|
|
@@ -15,7 +15,7 @@ const routeMap = new Map();
|
|
|
15
15
|
const usedParams = {};
|
|
16
16
|
const sortedViews = [];
|
|
17
17
|
const emitter = new ZetaEventContainer();
|
|
18
|
-
const rootContext = freeze(extend(new ViewContext(
|
|
18
|
+
const rootContext = freeze(extend(new ViewContext(), { container: root }));
|
|
19
19
|
const rootState = _(rootContext);
|
|
20
20
|
const StateContext = React.createContext(rootContext);
|
|
21
21
|
|
|
@@ -48,13 +48,13 @@ onAppInit(function () {
|
|
|
48
48
|
});
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
-
function ViewContext(view, page) {
|
|
51
|
+
function ViewContext(view, page, parent) {
|
|
52
52
|
var self = this;
|
|
53
|
-
|
|
54
|
-
defineOwnProperty(self, '
|
|
53
|
+
defineOwnProperty(self, 'view', view || null, true);
|
|
54
|
+
defineOwnProperty(self, 'parent', parent || null, true);
|
|
55
55
|
_(self, {
|
|
56
56
|
children: [],
|
|
57
|
-
setPage: defineObservableProperty(self, 'page', page, true),
|
|
57
|
+
setPage: defineObservableProperty(self, 'page', page || null, true),
|
|
58
58
|
setActive: defineObservableProperty(self, 'active', !!page, true)
|
|
59
59
|
});
|
|
60
60
|
watch(self, 'page', function (page, previousPage) {
|
|
@@ -63,6 +63,11 @@ function ViewContext(view, page) {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
definePrototype(ViewContext, {
|
|
66
|
+
getChildren: function () {
|
|
67
|
+
return map(_(this).children, function (v) {
|
|
68
|
+
return v.currentState;
|
|
69
|
+
});
|
|
70
|
+
},
|
|
66
71
|
on: function (event, handler) {
|
|
67
72
|
return emitter.add(this, event, handler);
|
|
68
73
|
}
|
|
@@ -85,6 +90,8 @@ definePrototype(ErrorBoundary, React.Component, {
|
|
|
85
90
|
setImmediate(function () {
|
|
86
91
|
dom.emit('error', self.context.container, { error }, true);
|
|
87
92
|
});
|
|
93
|
+
// ensure promise sent to beforepageload event is resolved
|
|
94
|
+
self.props.onComponentLoaded();
|
|
88
95
|
}
|
|
89
96
|
},
|
|
90
97
|
render: function () {
|
|
@@ -111,18 +118,25 @@ function ViewContainer() {
|
|
|
111
118
|
ViewContainer.contextType = StateContext;
|
|
112
119
|
|
|
113
120
|
definePrototype(ViewContainer, React.Component, {
|
|
121
|
+
setActive: noop,
|
|
114
122
|
componentDidMount: function () {
|
|
115
123
|
var self = this;
|
|
116
124
|
var parent = _(self.context).children;
|
|
125
|
+
var unwatch = watch(app.route, function () {
|
|
126
|
+
self.setActive(self.getViewComponent() === self.currentViewComponent);
|
|
127
|
+
});
|
|
128
|
+
self.componentWillUnmount = function () {
|
|
129
|
+
self.setActive(false);
|
|
130
|
+
arrRemove(parent, self);
|
|
131
|
+
unwatch();
|
|
132
|
+
setImmediate(function () {
|
|
133
|
+
if (self.unmountView && !self.currentState.active) {
|
|
134
|
+
self.unmountView();
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
};
|
|
117
138
|
parent.push(self);
|
|
118
|
-
self.
|
|
119
|
-
watch(app.route, function () {
|
|
120
|
-
(self.setActive || noop)(self.getViewComponent() === self.currentViewComponent);
|
|
121
|
-
}),
|
|
122
|
-
function () {
|
|
123
|
-
arrRemove(parent, self);
|
|
124
|
-
}
|
|
125
|
-
);
|
|
139
|
+
self.setActive(true);
|
|
126
140
|
},
|
|
127
141
|
render: function () {
|
|
128
142
|
/** @type {any} */
|
|
@@ -140,40 +154,39 @@ definePrototype(ViewContainer, React.Component, {
|
|
|
140
154
|
if (V && (viewChanged || !(self.children || '')[0])) {
|
|
141
155
|
// ensure the current path actually corresponds to the matched view
|
|
142
156
|
// when some views are not included in the list of allowed views
|
|
143
|
-
var targetPath = resolvePath(V,
|
|
157
|
+
var targetPath = resolvePath(V, app.route);
|
|
144
158
|
if (targetPath !== removeQueryAndHash(app.path)) {
|
|
145
159
|
app.navigate(targetPath, true);
|
|
146
160
|
return;
|
|
147
161
|
}
|
|
148
162
|
}
|
|
149
163
|
if (V && viewChanged) {
|
|
150
|
-
|
|
151
|
-
if (prevElement) {
|
|
152
|
-
self.setActive(false);
|
|
153
|
-
self.prevView = self.currentView;
|
|
154
|
-
self.currentElement = undefined;
|
|
155
|
-
app.emit('pageleave', prevElement, { pathname: self.currentPath }, true);
|
|
156
|
-
animateOut(prevElement, 'show').then(function () {
|
|
157
|
-
self.prevView = undefined;
|
|
158
|
-
self.forceUpdate();
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
(self.cancelPrevious || noop)();
|
|
164
|
+
(self.unmountView || noop)(true);
|
|
162
165
|
|
|
166
|
+
var state = new ViewContext(V, app.page, self.context);
|
|
163
167
|
var onComponentLoaded;
|
|
164
168
|
var promise = new Promise(function (resolve) {
|
|
165
169
|
onComponentLoaded = resolve;
|
|
166
170
|
});
|
|
171
|
+
var unmountView = onComponentLoaded;
|
|
167
172
|
var initElement = executeOnce(function (element) {
|
|
168
|
-
self.currentElement = element;
|
|
169
173
|
state.container = element;
|
|
170
174
|
promise.then(function () {
|
|
171
|
-
|
|
172
|
-
|
|
175
|
+
if (self.currentState === state) {
|
|
176
|
+
unmountView = function () {
|
|
177
|
+
self.prevView = self.currentView;
|
|
178
|
+
app.emit('pageleave', element, { pathname: state.page.path, view: V }, true);
|
|
179
|
+
animateOut(element, 'show').then(function () {
|
|
180
|
+
self.prevView = undefined;
|
|
181
|
+
self.forceUpdate();
|
|
182
|
+
});
|
|
183
|
+
};
|
|
184
|
+
animateIn(element, 'show', '[brew-view]', true);
|
|
185
|
+
app.emit('pageenter', element, { pathname: state.page.path, view: V }, true);
|
|
186
|
+
}
|
|
173
187
|
});
|
|
174
188
|
notifyAsync(element, promise);
|
|
175
189
|
});
|
|
176
|
-
var state = new ViewContext(V, app.page);
|
|
177
190
|
var viewProps = freeze({
|
|
178
191
|
navigationType: event.navigationType,
|
|
179
192
|
viewContext: state,
|
|
@@ -184,11 +197,16 @@ definePrototype(ViewContainer, React.Component, {
|
|
|
184
197
|
React.createElement('div', extend({}, self.props.rootProps, { ref: initElement, 'brew-view': '' }),
|
|
185
198
|
React.createElement(ErrorBoundary, { onComponentLoaded, viewProps }))));
|
|
186
199
|
extend(self, _(state), {
|
|
187
|
-
|
|
188
|
-
currentPath: app.path,
|
|
200
|
+
currentState: state,
|
|
189
201
|
currentView: view,
|
|
190
|
-
currentViewComponent: V
|
|
202
|
+
currentViewComponent: V,
|
|
203
|
+
unmountView: executeOnce(function () {
|
|
204
|
+
self.setActive(false);
|
|
205
|
+
routeMap.get(V).rendered--;
|
|
206
|
+
unmountView();
|
|
207
|
+
})
|
|
191
208
|
});
|
|
209
|
+
routeMap.get(V).rendered++;
|
|
192
210
|
(event.waitFor || noop)(promise);
|
|
193
211
|
}
|
|
194
212
|
(self.setPage || noop)(app.page);
|
|
@@ -199,7 +217,7 @@ definePrototype(ViewContainer, React.Component, {
|
|
|
199
217
|
}
|
|
200
218
|
});
|
|
201
219
|
|
|
202
|
-
function getCurrentParams(view,
|
|
220
|
+
function getCurrentParams(view, params) {
|
|
203
221
|
var state = routeMap.get(view);
|
|
204
222
|
if (!state.maxParams) {
|
|
205
223
|
var matchers = exclude(state.matchers, ['remainingSegments']);
|
|
@@ -228,7 +246,7 @@ function getCurrentParams(view, includeAll, params) {
|
|
|
228
246
|
});
|
|
229
247
|
}
|
|
230
248
|
}
|
|
231
|
-
return pick(
|
|
249
|
+
return extend(pick(app.route, state.minParams), params && pick(params, state.maxParams), state.params);
|
|
232
250
|
}
|
|
233
251
|
|
|
234
252
|
function sortViews(a, b) {
|
|
@@ -262,11 +280,14 @@ function createViewComponent(factory) {
|
|
|
262
280
|
return React.createElement(s.default, viewProps);
|
|
263
281
|
});
|
|
264
282
|
}, !!promise)[1];
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
if (
|
|
268
|
-
|
|
283
|
+
var loaded = !promise || !state.loading;
|
|
284
|
+
React.useEffect(function () {
|
|
285
|
+
if (loaded) {
|
|
286
|
+
setImmediate(props.onComponentLoaded);
|
|
269
287
|
}
|
|
288
|
+
}, [loaded]);
|
|
289
|
+
if (state.error) {
|
|
290
|
+
throw state.error;
|
|
270
291
|
}
|
|
271
292
|
return children || state.value || React.createElement(React.Fragment);
|
|
272
293
|
};
|
|
@@ -280,14 +301,17 @@ export function isViewMatched(view) {
|
|
|
280
301
|
return matchViewParams(view, app.route);
|
|
281
302
|
}
|
|
282
303
|
|
|
283
|
-
export function
|
|
304
|
+
export function isViewRendered(view) {
|
|
305
|
+
return !!(routeMap.get(view) || '').rendered;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export function matchView() {
|
|
309
|
+
var views = makeArray(arguments);
|
|
284
310
|
var route = app.route;
|
|
285
|
-
if (typeof
|
|
286
|
-
route = route.parse(
|
|
287
|
-
} else {
|
|
288
|
-
views = path;
|
|
311
|
+
if (typeof views[0] === 'string') {
|
|
312
|
+
route = route.parse(views.shift());
|
|
289
313
|
}
|
|
290
|
-
views = views ? makeArray(views).sort(sortViews) : sortedViews;
|
|
314
|
+
views = views[0] ? (isArray(views[0]) ? makeArray(views[0]) : views).sort(sortViews) : sortedViews;
|
|
291
315
|
return any(views, function (v) {
|
|
292
316
|
return matchViewParams(v, route);
|
|
293
317
|
}) || undefined;
|
|
@@ -304,6 +328,7 @@ export function registerView(factory, routeParams) {
|
|
|
304
328
|
});
|
|
305
329
|
routeMap.set(Component, {
|
|
306
330
|
id: randomId(),
|
|
331
|
+
rendered: 0,
|
|
307
332
|
matchCount: keys(routeParams).length,
|
|
308
333
|
matchers: routeParams,
|
|
309
334
|
params: pick(routeParams, function (v) {
|
|
@@ -333,12 +358,10 @@ export function renderView() {
|
|
|
333
358
|
}
|
|
334
359
|
|
|
335
360
|
export function resolvePath(view, params) {
|
|
336
|
-
|
|
337
|
-
if (!state) {
|
|
361
|
+
if (!routeMap.has(view)) {
|
|
338
362
|
return '/';
|
|
339
363
|
}
|
|
340
|
-
|
|
341
|
-
return app.route.getPath(newParams);
|
|
364
|
+
return app.route.getPath(getCurrentParams(view, params));
|
|
342
365
|
}
|
|
343
366
|
|
|
344
367
|
export function linkTo(view, params) {
|