brew-js-react 0.5.5 → 0.5.7
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 +196 -84
- 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/hooks.d.ts +22 -0
- package/hooks.js +58 -3
- package/mixins/FlyoutMixin.d.ts +6 -0
- package/mixins/FlyoutMixin.js +16 -15
- package/mixins/FlyoutToggleMixin.d.ts +17 -0
- package/mixins/FlyoutToggleMixin.js +23 -3
- package/mixins/StatefulMixin.js +1 -0
- package/package.json +3 -3
- package/view.d.ts +20 -0
- package/view.js +41 -33
package/mixins/FlyoutMixin.d.ts
CHANGED
|
@@ -99,4 +99,10 @@ export default class FlyoutMixin extends ClassNameMixin {
|
|
|
99
99
|
* @returns A promise that resolves after closing animation completes.
|
|
100
100
|
*/
|
|
101
101
|
close(state?: any): Promise<void>;
|
|
102
|
+
/**
|
|
103
|
+
* Toggles the flyout.
|
|
104
|
+
* @param source Source element which triggered the flyout.
|
|
105
|
+
* @returns A promise that resolves when the flyout is being closed.
|
|
106
|
+
*/
|
|
107
|
+
toggleSelf(source?: Element): Promise<any>;
|
|
102
108
|
}
|
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, toggleFlyout } 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,15 +57,14 @@ 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);
|
|
68
64
|
},
|
|
65
|
+
toggleSelf: function (source) {
|
|
66
|
+
return toggleFlyout(this.elements()[0], source, this.getOptions());
|
|
67
|
+
},
|
|
69
68
|
onOpen: function (callback) {
|
|
70
69
|
var element = this.elements()[0];
|
|
71
70
|
return this.onToggleState(function (opened) {
|
|
@@ -84,6 +83,17 @@ definePrototype(FlyoutMixin, ClassNameMixin, {
|
|
|
84
83
|
var self = this;
|
|
85
84
|
FlyoutMixinSuper.initElement.call(self, element, state);
|
|
86
85
|
self.onDispose(app.on(element, {
|
|
86
|
+
flyoutshow: function (e) {
|
|
87
|
+
valueMap.set(element, e.data);
|
|
88
|
+
self.isFlyoutOpened = true;
|
|
89
|
+
self.visible = true;
|
|
90
|
+
},
|
|
91
|
+
flyoutclose: function () {
|
|
92
|
+
self.isFlyoutOpened = false;
|
|
93
|
+
},
|
|
94
|
+
flyouthide: function () {
|
|
95
|
+
self.visible = false;
|
|
96
|
+
},
|
|
87
97
|
animationstart: function () {
|
|
88
98
|
self.animating = true;
|
|
89
99
|
},
|
|
@@ -91,14 +101,5 @@ definePrototype(FlyoutMixin, ClassNameMixin, {
|
|
|
91
101
|
self.animating = false;
|
|
92
102
|
},
|
|
93
103
|
}, 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
104
|
}
|
|
104
105
|
});
|
|
@@ -7,6 +7,17 @@ import FlyoutMixin from "./FlyoutMixin";
|
|
|
7
7
|
* Instances of this mixin is exposed by {@link FlyoutMixin.toggle}.
|
|
8
8
|
*/
|
|
9
9
|
export default class FlyoutToggleMixin extends ClassNameMixin {
|
|
10
|
+
/**
|
|
11
|
+
* Specifies the condition on when the flyout should toggle. It can be either:
|
|
12
|
+
*
|
|
13
|
+
* - `click`: flyout is toggled when applied element is clicked;
|
|
14
|
+
* - `focus`: flyout is opened or closed when applied element has gained or lost focus.
|
|
15
|
+
*
|
|
16
|
+
* Default behavior is `click`.
|
|
17
|
+
*
|
|
18
|
+
* @param trigger Condition of trigger.
|
|
19
|
+
*/
|
|
20
|
+
on(trigger: 'focus' | 'click'): this;
|
|
10
21
|
/**
|
|
11
22
|
* Opens the associated flyout.
|
|
12
23
|
* @param state Value to be sent to listener added by {@link FlyoutMixin.onOpen}.
|
|
@@ -20,4 +31,10 @@ export default class FlyoutToggleMixin extends ClassNameMixin {
|
|
|
20
31
|
* @returns A promise that resolves after closing animation completes.
|
|
21
32
|
*/
|
|
22
33
|
close(state?: any): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Toggles the associated flyout.
|
|
36
|
+
* @param source Source element which triggered the flyout.
|
|
37
|
+
* @returns A promise that resolves when the flyout is being closed.
|
|
38
|
+
*/
|
|
39
|
+
toggle(source?: Element): Promise<any>;
|
|
23
40
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import dom from "zeta-dom/dom";
|
|
2
2
|
import { definePrototype } from "zeta-dom/util";
|
|
3
|
-
import { toggleFlyout } from "brew-js/domAction";
|
|
4
3
|
import ClassNameMixin from "./ClassNameMixin.js";
|
|
5
4
|
|
|
6
5
|
const FlyoutToggleMixinSuper = ClassNameMixin.prototype;
|
|
@@ -10,18 +9,39 @@ export default function FlyoutToggleMixin(mixin) {
|
|
|
10
9
|
this.flyoutMixin = mixin;
|
|
11
10
|
}
|
|
12
11
|
|
|
12
|
+
function triggerFlyoutAction(self, state, trigger, action, args) {
|
|
13
|
+
if ((state.trigger || 'click') === trigger) {
|
|
14
|
+
action.apply(self, args);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
13
18
|
definePrototype(FlyoutToggleMixin, ClassNameMixin, {
|
|
19
|
+
on: function (trigger) {
|
|
20
|
+
this.state.trigger = trigger;
|
|
21
|
+
return this;
|
|
22
|
+
},
|
|
14
23
|
open: function (value, source) {
|
|
15
24
|
return this.flyoutMixin.open(value, source);
|
|
16
25
|
},
|
|
17
26
|
close: function (value) {
|
|
18
27
|
return this.flyoutMixin.close(value);
|
|
19
28
|
},
|
|
29
|
+
toggle: function (source) {
|
|
30
|
+
return this.flyoutMixin.toggleSelf(source);
|
|
31
|
+
},
|
|
20
32
|
initElement: function (element, state) {
|
|
21
33
|
var self = this;
|
|
22
34
|
FlyoutToggleMixinSuper.initElement.call(self, element, state);
|
|
23
|
-
self.onDispose(dom.on(element,
|
|
24
|
-
|
|
35
|
+
self.onDispose(dom.on(element, {
|
|
36
|
+
focusin: function () {
|
|
37
|
+
triggerFlyoutAction(self, state, 'focus', self.open, [null, dom.activeElement]);
|
|
38
|
+
},
|
|
39
|
+
focusout: function () {
|
|
40
|
+
triggerFlyoutAction(self, state, 'focus', self.close, []);
|
|
41
|
+
},
|
|
42
|
+
click: function () {
|
|
43
|
+
triggerFlyoutAction(self, state, 'click', self.toggle, [element]);
|
|
44
|
+
}
|
|
25
45
|
}));
|
|
26
46
|
}
|
|
27
47
|
});
|
package/mixins/StatefulMixin.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brew-js-react",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.7",
|
|
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.3.1",
|
|
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>;
|
|
@@ -8,7 +10,25 @@ export type ViewComponent<P> = React.FC<ViewProps<P>>;
|
|
|
8
10
|
export type ViewContainerState = ViewContext;
|
|
9
11
|
|
|
10
12
|
export interface PageChangeEvent extends Zeta.ZetaEventBase {
|
|
13
|
+
/**
|
|
14
|
+
* Gets information of the current page.
|
|
15
|
+
*/
|
|
16
|
+
readonly page: Brew.PageInfo;
|
|
17
|
+
/**
|
|
18
|
+
* Gets information of the previous page.
|
|
19
|
+
*/
|
|
11
20
|
readonly previousPage: Brew.PageInfo;
|
|
21
|
+
/**
|
|
22
|
+
* Gets how user has triggered navigation.
|
|
23
|
+
* @see {Brew.RouterEvent.navigationType}
|
|
24
|
+
*/
|
|
25
|
+
readonly navigationType: Brew.NavigationType;
|
|
26
|
+
/**
|
|
27
|
+
* Defers completion of navigation.
|
|
28
|
+
* @param args One or more promises.
|
|
29
|
+
* @returns Whether the supplied promises will be awaited.
|
|
30
|
+
*/
|
|
31
|
+
waitFor(...args: Promise<any>[]): boolean;
|
|
12
32
|
}
|
|
13
33
|
|
|
14
34
|
export interface ViewContextEventMap {
|
package/view.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Component, Fragment, createContext, createElement, useContext, useEffect } from "react";
|
|
2
2
|
import { useAsync } from "zeta-dom-react";
|
|
3
3
|
import dom from "zeta-dom/dom";
|
|
4
4
|
import { notifyAsync } from "zeta-dom/domLock";
|
|
@@ -17,7 +17,7 @@ const sortedViews = [];
|
|
|
17
17
|
const emitter = new ZetaEventContainer();
|
|
18
18
|
const rootContext = freeze(extend(new ViewContext(), { container: root }));
|
|
19
19
|
const rootState = _(rootContext);
|
|
20
|
-
const StateContext =
|
|
20
|
+
const StateContext = createContext(rootContext);
|
|
21
21
|
|
|
22
22
|
var errorView;
|
|
23
23
|
/** @type {Partial<Zeta.ZetaEventType<"beforepageload", Brew.RouterEventMap, Element>>} */
|
|
@@ -58,7 +58,12 @@ function ViewContext(view, page, parent) {
|
|
|
58
58
|
setActive: defineObservableProperty(self, 'active', !!page, true)
|
|
59
59
|
});
|
|
60
60
|
watch(self, 'page', function (page, previousPage) {
|
|
61
|
-
emitter.emit('pagechange', self, {
|
|
61
|
+
emitter.emit('pagechange', self, {
|
|
62
|
+
page: page,
|
|
63
|
+
previousPage: previousPage,
|
|
64
|
+
navigationType: event.navigationType,
|
|
65
|
+
waitFor: event.waitFor
|
|
66
|
+
});
|
|
62
67
|
});
|
|
63
68
|
}
|
|
64
69
|
|
|
@@ -74,12 +79,12 @@ definePrototype(ViewContext, {
|
|
|
74
79
|
});
|
|
75
80
|
|
|
76
81
|
function ErrorBoundary() {
|
|
77
|
-
|
|
82
|
+
Component.apply(this, arguments);
|
|
78
83
|
this.state = {};
|
|
79
84
|
}
|
|
80
85
|
ErrorBoundary.contextType = StateContext;
|
|
81
86
|
|
|
82
|
-
definePrototype(ErrorBoundary,
|
|
87
|
+
definePrototype(ErrorBoundary, Component, {
|
|
83
88
|
componentDidCatch: function (error) {
|
|
84
89
|
var self = this;
|
|
85
90
|
if (errorView && !self.state.error) {
|
|
@@ -90,6 +95,8 @@ definePrototype(ErrorBoundary, React.Component, {
|
|
|
90
95
|
setImmediate(function () {
|
|
91
96
|
dom.emit('error', self.context.container, { error }, true);
|
|
92
97
|
});
|
|
98
|
+
// ensure promise sent to beforepageload event is resolved
|
|
99
|
+
self.props.onComponentLoaded();
|
|
93
100
|
}
|
|
94
101
|
},
|
|
95
102
|
render: function () {
|
|
@@ -101,9 +108,9 @@ definePrototype(ErrorBoundary, React.Component, {
|
|
|
101
108
|
};
|
|
102
109
|
var onComponentLoaded = self.props.onComponentLoaded;
|
|
103
110
|
if (props.error) {
|
|
104
|
-
return
|
|
111
|
+
return createElement(errorView, { onComponentLoaded, viewProps: props });
|
|
105
112
|
}
|
|
106
|
-
return
|
|
113
|
+
return createElement(props.view, { onComponentLoaded, viewProps: self.props.viewProps });
|
|
107
114
|
},
|
|
108
115
|
reset: function () {
|
|
109
116
|
this.setState({ error: null });
|
|
@@ -111,11 +118,11 @@ definePrototype(ErrorBoundary, React.Component, {
|
|
|
111
118
|
});
|
|
112
119
|
|
|
113
120
|
function ViewContainer() {
|
|
114
|
-
|
|
121
|
+
Component.apply(this, arguments);
|
|
115
122
|
}
|
|
116
123
|
ViewContainer.contextType = StateContext;
|
|
117
124
|
|
|
118
|
-
definePrototype(ViewContainer,
|
|
125
|
+
definePrototype(ViewContainer, Component, {
|
|
119
126
|
setActive: noop,
|
|
120
127
|
componentDidMount: function () {
|
|
121
128
|
var self = this;
|
|
@@ -143,7 +150,7 @@ definePrototype(ViewContainer, React.Component, {
|
|
|
143
150
|
self.updateView();
|
|
144
151
|
}
|
|
145
152
|
(self.onRender || noop)();
|
|
146
|
-
return
|
|
153
|
+
return createElement(Fragment, null, self.prevView, self.currentView);
|
|
147
154
|
},
|
|
148
155
|
updateView: function () {
|
|
149
156
|
var self = this;
|
|
@@ -152,7 +159,7 @@ definePrototype(ViewContainer, React.Component, {
|
|
|
152
159
|
if (V && (viewChanged || !(self.children || '')[0])) {
|
|
153
160
|
// ensure the current path actually corresponds to the matched view
|
|
154
161
|
// when some views are not included in the list of allowed views
|
|
155
|
-
var targetPath = resolvePath(V,
|
|
162
|
+
var targetPath = resolvePath(V, app.route);
|
|
156
163
|
if (targetPath !== removeQueryAndHash(app.path)) {
|
|
157
164
|
app.navigate(targetPath, true);
|
|
158
165
|
return;
|
|
@@ -173,14 +180,14 @@ definePrototype(ViewContainer, React.Component, {
|
|
|
173
180
|
if (self.currentState === state) {
|
|
174
181
|
unmountView = function () {
|
|
175
182
|
self.prevView = self.currentView;
|
|
176
|
-
app.emit('pageleave', element, { pathname: state.page.path }, true);
|
|
183
|
+
app.emit('pageleave', element, { pathname: state.page.path, view: V }, true);
|
|
177
184
|
animateOut(element, 'show').then(function () {
|
|
178
185
|
self.prevView = undefined;
|
|
179
186
|
self.forceUpdate();
|
|
180
187
|
});
|
|
181
188
|
};
|
|
182
189
|
animateIn(element, 'show', '[brew-view]', true);
|
|
183
|
-
app.emit('pageenter', element, { pathname: state.page.path }, true);
|
|
190
|
+
app.emit('pageenter', element, { pathname: state.page.path, view: V }, true);
|
|
184
191
|
}
|
|
185
192
|
});
|
|
186
193
|
notifyAsync(element, promise);
|
|
@@ -190,10 +197,10 @@ definePrototype(ViewContainer, React.Component, {
|
|
|
190
197
|
viewContext: state,
|
|
191
198
|
viewData: event.data || {}
|
|
192
199
|
});
|
|
193
|
-
var view =
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
200
|
+
var view = createElement(StateContext.Provider, { key: routeMap.get(V).id, value: state },
|
|
201
|
+
createElement(ViewStateContainer, null,
|
|
202
|
+
createElement('div', extend({}, self.props.rootProps, { ref: initElement, 'brew-view': '' }),
|
|
203
|
+
createElement(ErrorBoundary, { onComponentLoaded, viewProps }))));
|
|
197
204
|
extend(self, _(state), {
|
|
198
205
|
currentState: state,
|
|
199
206
|
currentView: view,
|
|
@@ -215,7 +222,7 @@ definePrototype(ViewContainer, React.Component, {
|
|
|
215
222
|
}
|
|
216
223
|
});
|
|
217
224
|
|
|
218
|
-
function getCurrentParams(view,
|
|
225
|
+
function getCurrentParams(view, params) {
|
|
219
226
|
var state = routeMap.get(view);
|
|
220
227
|
if (!state.maxParams) {
|
|
221
228
|
var matchers = exclude(state.matchers, ['remainingSegments']);
|
|
@@ -244,7 +251,7 @@ function getCurrentParams(view, includeAll, params) {
|
|
|
244
251
|
});
|
|
245
252
|
}
|
|
246
253
|
}
|
|
247
|
-
return pick(
|
|
254
|
+
return extend(pick(app.route, state.minParams), params && pick(params, state.maxParams), state.params);
|
|
248
255
|
}
|
|
249
256
|
|
|
250
257
|
function sortViews(a, b) {
|
|
@@ -262,8 +269,8 @@ function matchViewParams(view, route) {
|
|
|
262
269
|
function createViewComponent(factory) {
|
|
263
270
|
var promise;
|
|
264
271
|
throwNotFunction(factory);
|
|
265
|
-
if (factory.prototype instanceof
|
|
266
|
-
factory =
|
|
272
|
+
if (factory.prototype instanceof Component) {
|
|
273
|
+
factory = createElement.bind(null, factory);
|
|
267
274
|
}
|
|
268
275
|
return function fn(props) {
|
|
269
276
|
var viewProps = props.viewProps;
|
|
@@ -275,21 +282,24 @@ function createViewComponent(factory) {
|
|
|
275
282
|
}
|
|
276
283
|
var state = useAsync(function () {
|
|
277
284
|
return promise.then(function (s) {
|
|
278
|
-
return
|
|
285
|
+
return createElement(s.default, viewProps);
|
|
279
286
|
});
|
|
280
287
|
}, !!promise)[1];
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
if (
|
|
284
|
-
|
|
288
|
+
var loaded = !promise || !state.loading;
|
|
289
|
+
useEffect(function () {
|
|
290
|
+
if (loaded) {
|
|
291
|
+
setImmediate(props.onComponentLoaded);
|
|
285
292
|
}
|
|
293
|
+
}, [loaded]);
|
|
294
|
+
if (state.error) {
|
|
295
|
+
throw state.error;
|
|
286
296
|
}
|
|
287
|
-
return children || state.value ||
|
|
297
|
+
return children || state.value || createElement(Fragment);
|
|
288
298
|
};
|
|
289
299
|
}
|
|
290
300
|
|
|
291
301
|
export function useViewContext() {
|
|
292
|
-
return
|
|
302
|
+
return useContext(StateContext);
|
|
293
303
|
}
|
|
294
304
|
|
|
295
305
|
export function isViewMatched(view) {
|
|
@@ -349,16 +359,14 @@ export function renderView() {
|
|
|
349
359
|
}
|
|
350
360
|
});
|
|
351
361
|
views.sort(sortViews);
|
|
352
|
-
return
|
|
362
|
+
return createElement(ViewContainer, { rootProps, views, defaultView });
|
|
353
363
|
}
|
|
354
364
|
|
|
355
365
|
export function resolvePath(view, params) {
|
|
356
|
-
|
|
357
|
-
if (!state) {
|
|
366
|
+
if (!routeMap.has(view)) {
|
|
358
367
|
return '/';
|
|
359
368
|
}
|
|
360
|
-
|
|
361
|
-
return app.route.getPath(newParams);
|
|
369
|
+
return app.route.getPath(getCurrentParams(view, params));
|
|
362
370
|
}
|
|
363
371
|
|
|
364
372
|
export function linkTo(view, params) {
|