brew-js-react 0.2.2 → 0.2.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/view.js CHANGED
@@ -2,14 +2,16 @@ import React, { useRef } from "react";
2
2
  import { useAsync } from "zeta-dom-react";
3
3
  import dom from "./include/zeta-dom/dom.js";
4
4
  import { notifyAsync } from "./include/zeta-dom/domLock.js";
5
- import { any, defineGetterProperty, definePrototype, each, extend, isFunction, keys, makeArray, noop, pick, randomId, setImmediate } from "./include/zeta-dom/util.js";
5
+ import { any, defineGetterProperty, definePrototype, each, exclude, extend, grep, isFunction, keys, makeArray, map, noop, pick, randomId, setImmediate, single } from "./include/zeta-dom/util.js";
6
6
  import { animateIn, animateOut } from "./include/brew-js/anim.js";
7
+ import { removeQueryAndHash } from "./include/brew-js/util/path.js";
7
8
  import { app } from "./app.js";
8
9
  import { ViewStateContainer } from "./hooks.js";
9
10
 
10
11
  const root = dom.root;
11
12
  const routeMap = new Map();
12
13
  const usedParams = {};
14
+ const sortedViews = [];
13
15
  const StateContext = React.createContext(Object.freeze({ active: true }));
14
16
 
15
17
  let stateId;
@@ -27,6 +29,7 @@ function ViewContainer() {
27
29
  }
28
30
  self.componentWillUnmount = app.on('navigate', function () {
29
31
  if (self.mounted && self.getViewComponent()) {
32
+ self.isForceUpdate = true;
30
33
  self.forceUpdate();
31
34
  }
32
35
  });
@@ -62,7 +65,7 @@ definePrototype(ViewContainer, React.Component, {
62
65
  }
63
66
  var providerProps = {
64
67
  key: routeMap.get(V).id,
65
- value: {}
68
+ value: { view: V }
66
69
  };
67
70
  var view = React.createElement(StateContext.Provider, providerProps,
68
71
  React.createElement(ViewStateContainer, null,
@@ -84,7 +87,10 @@ definePrototype(ViewContainer, React.Component, {
84
87
  self.currentPath = app.path;
85
88
  self.currentView = view;
86
89
  } else {
87
- app.emit('pageenter', self.currentElement, { pathname: app.path }, true);
90
+ if (self.isForceUpdate) {
91
+ self.isForceUpdate = false;
92
+ app.emit('pageenter', self.currentElement, { pathname: app.path }, true);
93
+ }
88
94
  resolve();
89
95
  }
90
96
  notifyAsync(self.parentElement || root, promise);
@@ -92,22 +98,84 @@ definePrototype(ViewContainer, React.Component, {
92
98
  },
93
99
  getViewComponent: function () {
94
100
  var props = this.props;
95
- return any(props.views, isViewMatched) || (history.state === stateId && void redirectTo(props.defaultView));
101
+ var matched = any(props.views, isViewMatched) || props.defaultView;
102
+ if (history.state === stateId) {
103
+ // ensure the current path actually corresponds to the matched view
104
+ // when some views are not included in the list of allowed views
105
+ var targetPath = linkTo(matched, getCurrentParams(matched, true));
106
+ if (targetPath !== removeQueryAndHash(app.path)) {
107
+ app.navigate(targetPath, true);
108
+ }
109
+ }
110
+ return matched;
96
111
  }
97
112
  });
98
113
 
99
- export function useViewContainerState() {
100
- return React.useContext(StateContext);
114
+ function getCurrentParams(view, includeAll, params) {
115
+ var state = routeMap.get(view);
116
+ if (!state.maxParams) {
117
+ var matchers = exclude(state.matchers, ['remainingSegments']);
118
+ var matched = map(app.routes, function (v) {
119
+ var route = app.parseRoute(v);
120
+ var matched = route.length && !any(matchers, function (v, i) {
121
+ var pos = route.params[i];
122
+ return (v ? !(pos >= 0) : pos < route.minLength) || (!isFunction(v) && !route.match(i, v));
123
+ });
124
+ return matched ? route : null;
125
+ });
126
+ if (matched[1]) {
127
+ matched = grep(matched, function (v) {
128
+ return !single(v.params, function (v, i) {
129
+ return usedParams[i] && !matchers[i];
130
+ });
131
+ });
132
+ }
133
+ if (matched[0]) {
134
+ var last = matched.slice(-1)[0];
135
+ state.maxParams = keys(extend.apply(0, [{}].concat(matched.map(function (v) {
136
+ return v.params;
137
+ }))));
138
+ state.minParams = map(last.params, function (v, i) {
139
+ return state.params[i] || v >= last.minLength ? null : i;
140
+ });
141
+ }
142
+ }
143
+ return pick(params || app.route, includeAll ? state.maxParams : state.minParams);
101
144
  }
102
145
 
103
- export function isViewMatched(view) {
146
+ function sortViews(a, b) {
147
+ return (routeMap.get(b) || {}).matchCount - (routeMap.get(a) || {}).matchCount;
148
+ }
149
+
150
+ function matchViewParams(view, route) {
104
151
  var params = routeMap.get(view);
105
- return !!params && false === any(params.matchers, function (v, i) {
106
- var value = app.route[i] || '';
152
+ return !!params && !single(params.matchers, function (v, i) {
153
+ var value = route[i] || '';
107
154
  return isFunction(v) ? !v(value) : (v || '') !== value;
108
155
  });
109
156
  }
110
157
 
158
+ export function useViewContainerState() {
159
+ return React.useContext(StateContext);
160
+ }
161
+
162
+ export function isViewMatched(view) {
163
+ return matchViewParams(view, app.route);
164
+ }
165
+
166
+ export function matchView(path, views) {
167
+ var route = app.route;
168
+ if (typeof path === 'string') {
169
+ route = route.parse(path);
170
+ } else {
171
+ views = path;
172
+ }
173
+ views = views ? makeArray(views).sort(sortViews) : sortedViews;
174
+ return any(views, function (v) {
175
+ return matchViewParams(v, route);
176
+ }) || undefined;
177
+ }
178
+
111
179
  export function registerView(factory, routeParams) {
112
180
  var Component = function (props) {
113
181
  var state = useAsync(factory);
@@ -135,6 +203,8 @@ export function registerView(factory, routeParams) {
135
203
  return typeof v === 'string';
136
204
  })
137
205
  });
206
+ sortedViews.push(Component);
207
+ sortedViews.sort(sortViews);
138
208
  return Component;
139
209
  }
140
210
 
@@ -142,24 +212,16 @@ export function renderView() {
142
212
  var views = makeArray(arguments);
143
213
  var rootProps = isFunction(views[0]) ? {} : views.shift();
144
214
  var defaultView = views[0];
145
- views.sort(function (a, b) {
146
- return (routeMap.get(b) || {}).matchCount - (routeMap.get(a) || {}).matchCount;
147
- });
215
+ views.sort(sortViews);
148
216
  return React.createElement(ViewContainer, { rootProps, views, defaultView });
149
217
  }
150
218
 
151
219
  export function linkTo(view, params) {
152
- var viewParams = (routeMap.get(view) || {}).params;
153
- var newParams = {};
154
- for (var i in app.route) {
155
- if (viewParams && i in viewParams) {
156
- newParams[i] = viewParams[i];
157
- } else if (params && i in params) {
158
- newParams[i] = params[i];
159
- } else if (!usedParams[i]) {
160
- newParams[i] = app.route[i];
161
- }
220
+ var state = routeMap.get(view);
221
+ if (!state) {
222
+ return '/';
162
223
  }
224
+ var newParams = extend(getCurrentParams(view), getCurrentParams(view, true, params), state.params);
163
225
  return app.route.getPath(newParams);
164
226
  }
165
227