brew-js-react 0.4.2 → 0.4.4

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.
@@ -1,3 +1,14 @@
1
+ import React from "react";
1
2
  import ClassNameMixin from "./ClassNameMixin";
2
3
 
3
- export default class FocusStateMixin extends ClassNameMixin { }
4
+ export default class FocusStateMixin extends ClassNameMixin {
5
+ /**
6
+ * Sets focus state based on another element.
7
+ *
8
+ * If the element with this mixin gain focus, the target element will also gain focus.
9
+ * Likewise, if the target element lost focus, the element with this mixin will also lose focus.
10
+ *
11
+ * @param target A CSS selector or a ref object.
12
+ */
13
+ for(target: string | React.RefObject<Element>): this;
14
+ }
@@ -10,20 +10,41 @@ export default function FocusStateMixin() {
10
10
  }
11
11
 
12
12
  definePrototype(FocusStateMixin, StatefulMixin, {
13
+ for: function (ref) {
14
+ this.state.ref = ref;
15
+ return this;
16
+ },
13
17
  initElement: function (element, state) {
14
18
  FocusStateMixinSuper.initElement.call(this, element, state);
19
+ var checkTarget = function (callback, arg) {
20
+ var ref = state.ref;
21
+ var target = ref && (typeof ref === 'string' ? element.querySelector(ref) : ref.current);
22
+ if (target && !dom.focused(target)) {
23
+ callback(arg || target);
24
+ }
25
+ };
15
26
  this.onDispose(dom.on(element, {
16
27
  focusin: function (e) {
17
- state.focused = true;
28
+ state.focused = e.source;
18
29
  setClass(element, 'focused', e.source);
30
+ checkTarget(dom.focus);
19
31
  },
20
32
  focusout: function () {
21
33
  state.focused = false;
22
34
  setClass(element, 'focused', false);
35
+ },
36
+ focuschange: function () {
37
+ checkTarget(dom.blur, element);
23
38
  }
24
39
  }));
25
40
  },
26
41
  getClassNames: function () {
27
- return [{ focused: !!this.state.focused }];
42
+ var classes = {};
43
+ var focused = this.state.focused;
44
+ if (focused) {
45
+ classes.focused = true;
46
+ classes['focused-' + focused] = true;
47
+ }
48
+ return [classes];
28
49
  }
29
50
  });
@@ -8,6 +8,7 @@ export interface ScrollableMixinOptions {
8
8
  handle?: 'auto' | 'scrollbar' | 'content';
9
9
  paged?: 'always' | 'landscape' | 'portrait';
10
10
  pagedItemSelector?: string;
11
+ persistScroll?: boolean;
11
12
  }
12
13
 
13
14
  export default class ScrollableMixin extends ClassNameMixin implements JQueryScrollable {
@@ -31,6 +32,6 @@ export default class ScrollableMixin extends ClassNameMixin implements JQueryScr
31
32
  scrollTo(x: number, y: number, duration?: number, callback?: () => void): Promise<void>;
32
33
  scrollByPage(x: number, y: number, duration?: number, callback?: () => void): Promise<void>;
33
34
  scrollToPage(x: number, y: number, duration?: number, callback?: () => void): Promise<void>;
34
- scrollToElement(target: Element, targetOrigin: string, duration: number, callback?: () => void): Promise<void>;
35
- scrollToElement(target: Element, targetOrigin?: string, wrapperOrigin?: string, duration?: number, callback?: () => void): Promise<void>;
35
+ scrollToElement(target: Element | string, targetOrigin: string, duration: number, callback?: () => void): Promise<void>;
36
+ scrollToElement(target: Element | string, targetOrigin?: string, wrapperOrigin?: string, duration?: number, callback?: () => void): Promise<void>;
36
37
  }
@@ -28,6 +28,8 @@ definePrototype(ScrollableMixin, ClassNameMixin, {
28
28
  'scroller-snap-page': options.paged,
29
29
  'scroller-page': options.pagedItemSelector,
30
30
  'scroller-state': 'pageIndex'
31
+ }, options.persistScroll && {
32
+ 'persist-scroll': ''
31
33
  });
32
34
  },
33
35
  onPageIndexChanged: function (callback) {
@@ -16,7 +16,7 @@ definePrototype(MixinRefImpl, {
16
16
  export default function StatefulMixin() {
17
17
  Mixin.call(this);
18
18
  _(this, {
19
- elements: new Set(),
19
+ elements: new WeakSet(),
20
20
  flush: watch(this, false),
21
21
  dispose: [],
22
22
  states: {},
@@ -84,7 +84,7 @@ definePrototype(StatefulMixin, Mixin, {
84
84
  var states = state.states;
85
85
  combineFn(state.dispose.splice(0))();
86
86
  state.flush();
87
- state.elements.clear();
87
+ state.elements = new WeakSet();
88
88
  each(states, function (i, v) {
89
89
  delete states[i];
90
90
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brew-js-react",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/view.js CHANGED
@@ -2,20 +2,21 @@ import React 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 { bind } from "./include/zeta-dom/domUtil.js";
6
5
  import { ZetaEventContainer } from "./include/zeta-dom/events.js";
7
- import { any, combineFn, defineObservableProperty, defineOwnProperty, definePrototype, each, exclude, executeOnce, extend, freeze, grep, isFunction, isThenable, isUndefinedOrNull, keys, makeArray, map, noop, pick, randomId, setImmediate, single, throwNotFunction, watch } from "./include/zeta-dom/util.js";
6
+ import { any, catchAsync, combineFn, createPrivateStore, defineObservableProperty, defineOwnProperty, definePrototype, each, exclude, executeOnce, extend, freeze, grep, isFunction, isThenable, isUndefinedOrNull, keys, makeArray, map, noop, pick, randomId, setImmediate, single, throwNotFunction, watch } from "./include/zeta-dom/util.js";
8
7
  import { animateIn, animateOut } from "./include/brew-js/anim.js";
9
8
  import { removeQueryAndHash } from "./include/brew-js/util/path.js";
10
9
  import { app, onAppInit } from "./app.js";
11
10
  import { ViewStateContainer } from "./hooks.js";
12
11
 
12
+ const _ = createPrivateStore();
13
13
  const root = dom.root;
14
14
  const routeMap = new Map();
15
15
  const usedParams = {};
16
16
  const sortedViews = [];
17
17
  const emitter = new ZetaEventContainer();
18
- const StateContext = React.createContext(Object.freeze({ container: root, active: true }));
18
+ const rootContext = freeze(extend(new ViewContext(null, null), { container: root }));
19
+ const StateContext = React.createContext(rootContext);
19
20
 
20
21
  var errorView;
21
22
  /** @type {Partial<Zeta.ZetaEventType<"beforepageload", Brew.RouterEventMap, Element>>} */
@@ -24,11 +25,20 @@ var event = {};
24
25
  onAppInit(function () {
25
26
  app.on('beforepageload', function (e) {
26
27
  event = e;
28
+ _(rootContext).setPage(app.page);
27
29
  });
28
30
  });
29
31
 
30
- function ViewContext(view) {
31
- defineOwnProperty(this, 'view', view, true);
32
+ function ViewContext(view, page) {
33
+ var self = this;
34
+ defineOwnProperty(self, 'view', view, true);
35
+ _(self, {
36
+ setPage: defineObservableProperty(self, 'page', page, true),
37
+ setActive: defineObservableProperty(self, 'active', true, true)
38
+ });
39
+ watch(self, 'page', function (page, previousPage) {
40
+ emitter.emit('pagechange', self, { previousPage });
41
+ });
32
42
  }
33
43
 
34
44
  definePrototype(ViewContext, {
@@ -78,6 +88,7 @@ function ViewContainer() {
78
88
  React.Component.apply(this, arguments);
79
89
  this.stateId = history.state;
80
90
  }
91
+ ViewContainer.contextType = StateContext;
81
92
 
82
93
  definePrototype(ViewContainer, React.Component, {
83
94
  componentDidMount: function () {
@@ -85,32 +96,41 @@ definePrototype(ViewContainer, React.Component, {
85
96
  var self = this;
86
97
  self.componentWillUnmount = combineFn(
87
98
  watch(app.route, function () {
88
- self.setActive(self.getViewComponent() === self.currentViewComponent);
99
+ (self.setActive || noop)(self.getViewComponent() === self.currentViewComponent);
89
100
  }),
90
101
  app.on('beforepageload', function () {
91
102
  self.stateId = history.state;
92
- self.updateView();
93
- self.forceUpdate();
103
+ if (self.context === rootContext || self.updateOnNext) {
104
+ event.waitFor(new Promise(function (resolve) {
105
+ self.onRender = resolve;
106
+ }));
107
+ self.updateView();
108
+ self.forceUpdate();
109
+ }
94
110
  })
95
111
  );
96
112
  },
97
113
  render: function () {
98
114
  /** @type {any} */
99
115
  var self = this;
100
- if (history.state === self.stateId) {
116
+ if (history.state === self.stateId && self.context.active) {
101
117
  self.updateView();
102
118
  }
119
+ (self.onRender || noop)();
103
120
  return React.createElement(React.Fragment, null, self.prevView, self.currentView);
104
121
  },
105
122
  updateView: function () {
106
123
  var self = this;
107
124
  var V = self.getViewComponent();
125
+ self.updateOnNext = false;
108
126
  if (V) {
109
127
  // ensure the current path actually corresponds to the matched view
110
128
  // when some views are not included in the list of allowed views
111
129
  var targetPath = resolvePath(V, getCurrentParams(V, true));
112
130
  if (targetPath !== removeQueryAndHash(app.path)) {
113
131
  app.navigate(targetPath, true);
132
+ self.updateOnNext = true;
133
+ return;
114
134
  }
115
135
  }
116
136
  if (V && V !== self.currentViewComponent) {
@@ -125,6 +145,8 @@ definePrototype(ViewContainer, React.Component, {
125
145
  self.forceUpdate();
126
146
  });
127
147
  }
148
+ (self.cancelPrevious || noop)();
149
+
128
150
  var onComponentLoaded;
129
151
  var promise = new Promise(function (resolve) {
130
152
  onComponentLoaded = resolve;
@@ -138,7 +160,7 @@ definePrototype(ViewContainer, React.Component, {
138
160
  });
139
161
  notifyAsync(element, promise);
140
162
  });
141
- var state = new ViewContext(V);
163
+ var state = new ViewContext(V, app.page);
142
164
  var viewProps = freeze({
143
165
  navigationType: event.navigationType,
144
166
  viewContext: state,
@@ -148,15 +170,11 @@ definePrototype(ViewContainer, React.Component, {
148
170
  React.createElement(ViewStateContainer, null,
149
171
  React.createElement('div', extend({}, self.props.rootProps, { ref: initElement }),
150
172
  React.createElement(ErrorBoundary, { onComponentLoaded, viewProps }))));
151
- extend(self, {
173
+ extend(self, _(state), {
174
+ cancelPrevious: onComponentLoaded,
152
175
  currentPath: app.path,
153
176
  currentView: view,
154
- currentViewComponent: V,
155
- setPage: defineObservableProperty(state, 'page', app.page, true),
156
- setActive: defineObservableProperty(state, 'active', true, true)
157
- });
158
- watch(state, 'page', function (page, previousPage) {
159
- emitter.emit('pagechange', state, { previousPage });
177
+ currentViewComponent: V
160
178
  });
161
179
  (event.waitFor || noop)(promise);
162
180
  }
@@ -224,6 +242,7 @@ function createViewComponent(factory) {
224
242
  if (isThenable(children)) {
225
243
  promise = children;
226
244
  children = null;
245
+ catchAsync(promise);
227
246
  }
228
247
  var state = useAsync(function () {
229
248
  return promise.then(function (s) {