brew-js-react 0.6.3 → 0.6.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/dialog.d.ts +57 -21
- package/dialog.js +10 -4
- package/dist/brew-js-react.js +172 -134
- 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/i18n.d.ts +34 -47
- package/mixins/ClassNameMixin.d.ts +1 -1
- package/mixins/ScrollableMixin.d.ts +3 -1
- package/mixins/StaticAttributeMixin.d.ts +1 -1
- package/package.json +6 -4
- package/view.d.ts +72 -13
- package/view.js +127 -104
package/view.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { Component,
|
|
2
|
-
import { useAsync } from "zeta-dom-react";
|
|
1
|
+
import { Component, createContext, createElement, useContext, useLayoutEffect } from "react";
|
|
2
|
+
import { createAsyncScope, useAsync } from "zeta-dom-react";
|
|
3
3
|
import dom, { reportError } from "zeta-dom/dom";
|
|
4
4
|
import { ZetaEventContainer } from "zeta-dom/events";
|
|
5
|
-
import { any, arrRemove, catchAsync, createPrivateStore, defineObservableProperty, defineOwnProperty, definePrototype, each, exclude, executeOnce, extend, freeze, grep, isArray, isFunction, isThenable, isUndefinedOrNull, keys, makeArray, map, noop, pick, randomId, setImmediate, single, throwNotFunction, watch } from "zeta-dom/util";
|
|
5
|
+
import { always, any, arrRemove, catchAsync, createPrivateStore, defineObservableProperty, defineOwnProperty, definePrototype, delay, each, exclude, executeOnce, extend, fill, freeze, grep, isArray, isFunction, isPlainObject, isThenable, isUndefinedOrNull, keys, makeArray, map, noop, pick, randomId, setImmediate, single, throwNotFunction, watch } from "zeta-dom/util";
|
|
6
6
|
import { animateIn, animateOut } from "brew-js/anim";
|
|
7
|
+
import { toQueryString } from "brew-js/util/common";
|
|
7
8
|
import { removeQueryAndHash } from "brew-js/util/path";
|
|
8
9
|
import { app, onAppInit } from "./app.js";
|
|
9
10
|
import { ViewStateContainer } from "./hooks.js";
|
|
@@ -20,7 +21,7 @@ const StateContext = createContext(rootContext);
|
|
|
20
21
|
|
|
21
22
|
var errorView;
|
|
22
23
|
/** @type {Partial<Zeta.ZetaEventType<"beforepageload", Brew.RouterEventMap, Element>>} */
|
|
23
|
-
var event = {};
|
|
24
|
+
var event = { waitFor: noop };
|
|
24
25
|
|
|
25
26
|
onAppInit(function () {
|
|
26
27
|
app.on('beforepageload', function (e) {
|
|
@@ -41,7 +42,7 @@ onAppInit(function () {
|
|
|
41
42
|
rootState.setPage(app.page);
|
|
42
43
|
});
|
|
43
44
|
|
|
44
|
-
function ViewContext(view, page, parent) {
|
|
45
|
+
export function ViewContext(view, page, parent) {
|
|
45
46
|
var self = this;
|
|
46
47
|
defineOwnProperty(self, 'view', view || null, true);
|
|
47
48
|
defineOwnProperty(self, 'parent', parent || null, true);
|
|
@@ -59,6 +60,7 @@ function ViewContext(view, page, parent) {
|
|
|
59
60
|
});
|
|
60
61
|
});
|
|
61
62
|
}
|
|
63
|
+
defineOwnProperty(ViewContext, 'root', rootContext, true);
|
|
62
64
|
|
|
63
65
|
definePrototype(ViewContext, {
|
|
64
66
|
getChildren: function () {
|
|
@@ -66,72 +68,93 @@ definePrototype(ViewContext, {
|
|
|
66
68
|
return v.currentContext;
|
|
67
69
|
});
|
|
68
70
|
},
|
|
71
|
+
setErrorView: function (errorView, error) {
|
|
72
|
+
var wrapper = _(this).wrapper;
|
|
73
|
+
return wrapper && errorView && !wrapper.setState({ error, errorView });
|
|
74
|
+
},
|
|
69
75
|
on: function (event, handler) {
|
|
70
76
|
return emitter.add(this, event, handler);
|
|
71
77
|
}
|
|
72
78
|
});
|
|
73
79
|
|
|
74
|
-
function ErrorBoundary() {
|
|
75
|
-
Component.
|
|
80
|
+
function ErrorBoundary(props) {
|
|
81
|
+
Component.call(this, props);
|
|
76
82
|
this.state = {};
|
|
83
|
+
_(props.context).wrapper = this;
|
|
77
84
|
}
|
|
78
|
-
ErrorBoundary.contextType = StateContext;
|
|
79
85
|
|
|
80
86
|
definePrototype(ErrorBoundary, Component, {
|
|
87
|
+
componentDidMount: function () {
|
|
88
|
+
var self = this;
|
|
89
|
+
self.componentWillUnmount = watch(self.props.context, 'page', function () {
|
|
90
|
+
self.state.errorView = null;
|
|
91
|
+
self.forceUpdate();
|
|
92
|
+
});
|
|
93
|
+
},
|
|
81
94
|
componentDidCatch: function (error) {
|
|
82
95
|
var self = this;
|
|
83
|
-
if (errorView &&
|
|
84
|
-
self.setState({ error });
|
|
96
|
+
if (errorView && self.state.errorView !== errorView) {
|
|
97
|
+
self.setState({ error, errorView });
|
|
85
98
|
} else {
|
|
86
|
-
|
|
87
|
-
self.props.
|
|
88
|
-
reportError(error, self.context.container);
|
|
99
|
+
self.props.onLoad();
|
|
100
|
+
reportError(error, self.props.context.container);
|
|
89
101
|
}
|
|
90
102
|
},
|
|
91
103
|
render: function () {
|
|
92
104
|
var self = this;
|
|
93
|
-
|
|
105
|
+
var context = self.props.context;
|
|
106
|
+
if (!context.container) {
|
|
94
107
|
setImmediate(function () {
|
|
108
|
+
extend(self, createAsyncScope(context.container));
|
|
109
|
+
dom.on(context.container, 'error', function (e) {
|
|
110
|
+
return emitter.emit(e, context, { error: e.error });
|
|
111
|
+
});
|
|
95
112
|
self.forceUpdate();
|
|
96
113
|
});
|
|
97
114
|
return null;
|
|
98
115
|
}
|
|
99
|
-
var
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return createElement(errorView, { onComponentLoaded, onError, viewProps: props });
|
|
116
|
+
var errorView = self.state.errorView;
|
|
117
|
+
if (errorView) {
|
|
118
|
+
self.props.onLoad();
|
|
119
|
+
return createElement(self.Provider, null, createElement(self.state.errorView, {
|
|
120
|
+
view: context.view,
|
|
121
|
+
error: self.state.error,
|
|
122
|
+
reset: self.reset.bind(self)
|
|
123
|
+
}));
|
|
108
124
|
}
|
|
109
|
-
|
|
125
|
+
var onError = self.componentDidCatch.bind(self);
|
|
126
|
+
var viewProps = {
|
|
127
|
+
errorHandler: self.errorHandler,
|
|
128
|
+
navigationType: event.navigationType,
|
|
129
|
+
viewContext: context,
|
|
130
|
+
viewData: context.page.data || {}
|
|
131
|
+
};
|
|
132
|
+
return createElement(self.Provider, null, createElement(context.view, extend({ viewProps, onError }, self.props)));
|
|
110
133
|
},
|
|
111
134
|
reset: function () {
|
|
112
|
-
this.setState({
|
|
135
|
+
this.setState({ errorView: null });
|
|
113
136
|
}
|
|
114
137
|
});
|
|
115
138
|
|
|
116
139
|
function ViewContainer() {
|
|
117
140
|
Component.apply(this, arguments);
|
|
141
|
+
this.views = [];
|
|
118
142
|
}
|
|
119
143
|
ViewContainer.contextType = StateContext;
|
|
120
144
|
|
|
121
145
|
definePrototype(ViewContainer, Component, {
|
|
122
|
-
setActive: noop,
|
|
123
146
|
componentDidMount: function () {
|
|
124
147
|
var self = this;
|
|
125
148
|
var parent = _(self.context).children;
|
|
126
149
|
var unwatch = watch(app.route, function () {
|
|
127
|
-
self.setActive(self.getViewComponent() === self.
|
|
150
|
+
self.setActive(self.getViewComponent() === (self.currentContext || '').view);
|
|
128
151
|
});
|
|
129
152
|
self.componentWillUnmount = function () {
|
|
130
153
|
self.setActive(false);
|
|
131
154
|
arrRemove(parent, self);
|
|
132
155
|
unwatch();
|
|
133
156
|
setImmediate(function () {
|
|
134
|
-
if (self.
|
|
157
|
+
if (self.currentContext && !self.currentContext.active) {
|
|
135
158
|
self.unmountView();
|
|
136
159
|
}
|
|
137
160
|
});
|
|
@@ -145,13 +168,13 @@ definePrototype(ViewContainer, Component, {
|
|
|
145
168
|
if (self.context.active) {
|
|
146
169
|
self.updateView();
|
|
147
170
|
}
|
|
148
|
-
|
|
149
|
-
return
|
|
171
|
+
self.onRender();
|
|
172
|
+
return self.views;
|
|
150
173
|
},
|
|
151
174
|
updateView: function () {
|
|
152
175
|
var self = this;
|
|
153
176
|
var V = self.getViewComponent();
|
|
154
|
-
var viewChanged = V !== self.
|
|
177
|
+
var viewChanged = V !== (self.currentContext || '').view;
|
|
155
178
|
if (V && (viewChanged || !(self.children || '')[0])) {
|
|
156
179
|
// ensure the current path actually corresponds to the matched view
|
|
157
180
|
// when some views are not included in the list of allowed views
|
|
@@ -161,64 +184,68 @@ definePrototype(ViewContainer, Component, {
|
|
|
161
184
|
return;
|
|
162
185
|
}
|
|
163
186
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
187
|
+
var state = routeMap.get(V) || {};
|
|
188
|
+
if ((self.views[2] || '').key === state.id) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
self.views[2] = null;
|
|
192
|
+
self.abort();
|
|
193
|
+
if (!V || !viewChanged) {
|
|
194
|
+
self.setActive(true);
|
|
195
|
+
self.setPage(app.page);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
event.waitFor(new Promise(function (resolve) {
|
|
167
199
|
var context = new ViewContext(V, app.page, self.context);
|
|
168
|
-
var
|
|
169
|
-
var onComponentLoaded;
|
|
170
|
-
var promise = new Promise(function (resolve) {
|
|
171
|
-
onComponentLoaded = resolve;
|
|
172
|
-
});
|
|
173
|
-
var unmountView = onComponentLoaded;
|
|
200
|
+
var rootProps = self.props.rootProps;
|
|
174
201
|
var initElement = executeOnce(function (element) {
|
|
175
|
-
context
|
|
176
|
-
|
|
177
|
-
if (self.currentContext === context) {
|
|
178
|
-
unmountView = function () {
|
|
179
|
-
self.prevView = self.currentView;
|
|
180
|
-
app.emit('pageleave', element, { pathname: context.page.path, view: V }, true);
|
|
181
|
-
animateOut(element, 'show').then(function () {
|
|
182
|
-
self.prevView = undefined;
|
|
183
|
-
self.forceUpdate();
|
|
184
|
-
});
|
|
185
|
-
};
|
|
186
|
-
animateIn(element, 'show', '[brew-view]', true);
|
|
187
|
-
app.emit('pageenter', element, { pathname: context.page.path, view: V }, true);
|
|
188
|
-
}
|
|
189
|
-
});
|
|
202
|
+
defineOwnProperty(context, 'container', element, true);
|
|
203
|
+
self.currentContext = self.currentContext || context;
|
|
190
204
|
});
|
|
191
|
-
var
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
viewData: context.page.data || {}
|
|
196
|
-
});
|
|
197
|
-
};
|
|
198
|
-
var view = createElement(StateContext.Provider, { key: state.id, value: context },
|
|
199
|
-
createElement(ViewStateContainer, null,
|
|
200
|
-
createElement('div', extend({}, self.props.rootProps, { ref: initElement, 'brew-view': '' }),
|
|
201
|
-
createElement(ErrorBoundary, { onComponentLoaded, viewProps }))));
|
|
202
|
-
extend(self, _(context), {
|
|
203
|
-
currentContext: context,
|
|
204
|
-
currentView: view,
|
|
205
|
-
currentViewComponent: V,
|
|
206
|
-
unmountView: executeOnce(function () {
|
|
207
|
-
self.setActive(false);
|
|
205
|
+
var onLoad = executeOnce(function () {
|
|
206
|
+
var element = context.container;
|
|
207
|
+
var promise = self.unmountView();
|
|
208
|
+
self.unmountView = executeOnce(function () {
|
|
208
209
|
state.rendered--;
|
|
209
|
-
|
|
210
|
-
|
|
210
|
+
self.setActive(false);
|
|
211
|
+
app.emit('pageleave', element, { pathname: context.page.path, view: V }, true);
|
|
212
|
+
return animateOut(element, 'show').then(function () {
|
|
213
|
+
self.views[0] = null;
|
|
214
|
+
self.forceUpdate();
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
always(promise, delay).then(function () {
|
|
218
|
+
app.emit('pageenter', element, { pathname: context.page.path, view: V }, true);
|
|
219
|
+
});
|
|
220
|
+
self.views.shift();
|
|
221
|
+
self.currentContext = context;
|
|
222
|
+
extend(self, _(context));
|
|
223
|
+
state.rendered++;
|
|
224
|
+
animateIn(element, 'show', '[brew-view]', true);
|
|
225
|
+
resolve();
|
|
211
226
|
});
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
227
|
+
context.on('error', function () {
|
|
228
|
+
return (rootProps.onError || noop).apply(this, arguments);
|
|
229
|
+
});
|
|
230
|
+
self.abort = resolve;
|
|
231
|
+
self.views[2] = createElement(StateContext.Provider, { key: state.id, value: context },
|
|
232
|
+
createElement(ViewStateContainer, null,
|
|
233
|
+
createElement('div', extend(exclude(rootProps, ['loader', 'onError']), { ref: initElement, 'brew-view': '' }),
|
|
234
|
+
createElement(ErrorBoundary, { onLoad, context, self, loader: rootProps.loader }))));
|
|
235
|
+
}));
|
|
216
236
|
},
|
|
217
237
|
getViewComponent: function () {
|
|
218
238
|
var props = this.props;
|
|
219
|
-
return any(props.views,
|
|
239
|
+
return any(props.views, function (v) {
|
|
240
|
+
return matchViewParams(v, app.route);
|
|
241
|
+
}) || props.defaultView;
|
|
220
242
|
}
|
|
221
243
|
});
|
|
244
|
+
fill(ViewContainer.prototype, 'abort onRender setActive setPage unmountView', noop);
|
|
245
|
+
|
|
246
|
+
function normalizePart(value, part) {
|
|
247
|
+
return isUndefinedOrNull(value) || value === '' || value === part ? '' : value[0] === part ? value : part + value;
|
|
248
|
+
}
|
|
222
249
|
|
|
223
250
|
function getCurrentParams(view, params) {
|
|
224
251
|
var state = routeMap.get(view);
|
|
@@ -271,31 +298,24 @@ function createViewComponent(factory) {
|
|
|
271
298
|
factory = createElement.bind(null, factory);
|
|
272
299
|
}
|
|
273
300
|
return function fn(props) {
|
|
274
|
-
var
|
|
275
|
-
var viewProps = useState(props.viewProps);
|
|
276
|
-
var children = !promise && factory(viewProps[0]);
|
|
301
|
+
var children = promise || factory(props.viewProps);
|
|
277
302
|
if (isThenable(children)) {
|
|
278
303
|
promise = children;
|
|
279
|
-
children = null;
|
|
280
304
|
catchAsync(promise);
|
|
305
|
+
} else {
|
|
306
|
+
useLayoutEffect(props.onLoad, []);
|
|
307
|
+
return children;
|
|
281
308
|
}
|
|
282
|
-
var
|
|
283
|
-
return promise.then(null,
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
useEffect(function () {
|
|
287
|
-
state.elementRef(viewContext.container);
|
|
288
|
-
// listen to property directly so that it is invoked after pagechange event handlers in actual component
|
|
289
|
-
return watch(viewContext, 'page', function () {
|
|
290
|
-
viewProps[1](props.viewProps);
|
|
309
|
+
var component = useAsync(function () {
|
|
310
|
+
return promise.then(null, function (error) {
|
|
311
|
+
promise = null;
|
|
312
|
+
props.onError(error);
|
|
291
313
|
});
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
}, [loaded]);
|
|
298
|
-
return children || (state.value ? createElement(state.value.default, viewProps[0]) : null);
|
|
314
|
+
})[0];
|
|
315
|
+
if (component) {
|
|
316
|
+
props.onLoad();
|
|
317
|
+
}
|
|
318
|
+
return component ? createElement(component.default, props.viewProps) : (props.self.currentContext === props.context && props.loader) || null;
|
|
299
319
|
};
|
|
300
320
|
}
|
|
301
321
|
|
|
@@ -304,7 +324,8 @@ export function useViewContext() {
|
|
|
304
324
|
}
|
|
305
325
|
|
|
306
326
|
export function isViewMatched(view) {
|
|
307
|
-
|
|
327
|
+
var route = app.route;
|
|
328
|
+
return matchViewParams(view, route) && resolvePath(view, route) === route.toString();
|
|
308
329
|
}
|
|
309
330
|
|
|
310
331
|
export function isViewRendered(view) {
|
|
@@ -347,7 +368,7 @@ export function registerView(factory, routeParams) {
|
|
|
347
368
|
}
|
|
348
369
|
|
|
349
370
|
export function registerErrorView(factory) {
|
|
350
|
-
errorView =
|
|
371
|
+
errorView = throwNotFunction(factory);
|
|
351
372
|
}
|
|
352
373
|
|
|
353
374
|
export function renderView() {
|
|
@@ -364,10 +385,12 @@ export function renderView() {
|
|
|
364
385
|
}
|
|
365
386
|
|
|
366
387
|
export function resolvePath(view, params) {
|
|
367
|
-
|
|
368
|
-
|
|
388
|
+
var suffix = '';
|
|
389
|
+
if (isArray(params)) {
|
|
390
|
+
suffix = normalizePart(isPlainObject(params[1]) ? toQueryString(params[1]) : params[1], '?') + normalizePart(params[2], '#');
|
|
391
|
+
params = params[0];
|
|
369
392
|
}
|
|
370
|
-
return app.route.getPath(getCurrentParams(view, params));
|
|
393
|
+
return (routeMap.has(view) ? app.route.getPath(getCurrentParams(view, params)) : '/') + suffix;
|
|
371
394
|
}
|
|
372
395
|
|
|
373
396
|
export function linkTo(view, params) {
|