brew-js-react 0.1.0-beta → 0.1.3

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/mixin.js CHANGED
@@ -4,46 +4,30 @@ import AnimateMixin from "./mixins/AnimateMixin.js";
4
4
  import AnimateSequenceItemMixin from "./mixins/AnimateSequenceItemMixin.js";
5
5
  import AnimateSequenceMixin from "./mixins/AnimateSequenceMixin.js";
6
6
  import ClassNameMixin from "./mixins/ClassNameMixin.js";
7
+ import ErrorHandlerMixin from "./mixins/ErrorHandlerMixin.js";
7
8
  import FlyoutMixin from "./mixins/FlyoutMixin.js";
8
9
  import FocusStateMixin from "./mixins/FocusStateMixin.js";
9
10
  import LoadingStateMixin from "./mixins/LoadingStateMixin.js";
10
11
  import StatefulMixin from "./mixins/StatefulMixin.js";
11
12
  import ScrollableMixin from "./mixins/ScrollableMixin.js";
12
13
 
13
- export function useScrollableMixin(options) {
14
- return useState(function () {
15
- return new ScrollableMixin();
16
- })[0].reset().withOptions(options);
14
+ function createUseFunction(ctor) {
15
+ return function () {
16
+ return useState(function () {
17
+ return new ctor();
18
+ })[0].reset();
19
+ };
17
20
  }
18
21
 
19
- export function useFlyoutMixin() {
20
- return useState(function () {
21
- return new FlyoutMixin();
22
- })[0].reset();
23
- }
22
+ export const useAnimateMixin = createUseFunction(AnimateMixin);
23
+ export const useAnimateSequenceMixin = createUseFunction(AnimateSequenceMixin);
24
+ export const useErrorHandlerMixin = createUseFunction(ErrorHandlerMixin);
25
+ export const useFlyoutMixin = createUseFunction(FlyoutMixin);
26
+ export const useFocusStateMixin = createUseFunction(FocusStateMixin);
27
+ export const useLoadingStateMixin = createUseFunction(LoadingStateMixin);
24
28
 
25
- export function useAnimateMixin() {
26
- return useState(function () {
27
- return new AnimateMixin();
28
- })[0].reset();
29
- }
30
-
31
- export function useAnimateSequenceMixin() {
32
- return useState(function () {
33
- return new AnimateSequenceMixin();
34
- })[0].reset();
35
- }
36
-
37
- export function useFocusStateMixin() {
38
- return useState(function () {
39
- return new FocusStateMixin();
40
- })[0].reset();
41
- }
42
-
43
- export function useLoadingStateMixin() {
44
- return useState(() => {
45
- return new LoadingStateMixin();
46
- })[0].reset();
29
+ export function useScrollableMixin(options) {
30
+ return createUseFunction(ScrollableMixin)().withOptions(options);
47
31
  }
48
32
 
49
33
  export function useMixinRef(mixin) {
@@ -56,6 +40,7 @@ export {
56
40
  AnimateSequenceItemMixin,
57
41
  AnimateSequenceMixin,
58
42
  ClassNameMixin,
43
+ ErrorHandlerMixin,
59
44
  FlyoutMixin,
60
45
  FocusStateMixin,
61
46
  LoadingStateMixin,
@@ -0,0 +1,34 @@
1
+ import dom from "../include/zeta-dom/dom";
2
+ import StatefulMixin from "./StatefulMixin";
3
+
4
+ export default class ErrorHandlerMixin extends StatefulMixin {
5
+ /**
6
+ * Catches errors from promises registered to descandant elements by {@link dom.lock}.
7
+ * Unfiltered handlers are called after filtered handlers, registered by other overloads, regardless of order.
8
+ *
9
+ * If handler returns a value other than `undefined`, including promises resolving to whatever values,
10
+ * the error is marked as handled and no further handlers are called nor will be propagated up the DOM tree.
11
+ * @param handler Callback to be invoked.
12
+ */
13
+ catch(handler: (e: any) => any): Zeta.UnregisterCallback;
14
+
15
+ /**
16
+ * Catches errors with property `code` matching the specified code, from promises registered to descandant elements by {@link dom.lock}.
17
+ *
18
+ * If handler returns a value other than `undefined`, including promises resolving to whatever values,
19
+ * the error is marked as handled and no further handlers are called nor will be propagated up the DOM tree.
20
+ * @param code Value to be matched against.
21
+ * @param handler Callback to be invoked when the criteria matches.
22
+ */
23
+ catch(code: string, handler: (e: Error) => any): Zeta.UnregisterCallback;
24
+
25
+ /**
26
+ * Catches errors that are instances of the specified error type, from promises registered to descandant elements by {@link dom.lock}.
27
+ *
28
+ * If handler returns a value other than `undefined`, including promises resolving to whatever values,
29
+ * the error is marked as handled and no further handlers are called nor will be propagated up the DOM tree.
30
+ * @param type Constructor of a specific error type.
31
+ * @param handler Callback to be invoked when the criteria matches.
32
+ */
33
+ catch<T extends Error>(type: typeof T, handler: (e: T) => any): Zeta.UnregisterCallback;
34
+ }
@@ -0,0 +1,40 @@
1
+ import dom from "../include/zeta-dom/dom.js";
2
+ import { ZetaEventContainer } from "../include/zeta-dom/events.js";
3
+ import { definePrototype, is, isFunction } from "../include/zeta-dom/util.js";
4
+ import StatefulMixin from "./StatefulMixin.js";
5
+
6
+ const ErrorHandlerMixinSuper = StatefulMixin.prototype;
7
+ const emitter = new ZetaEventContainer();
8
+
9
+ function isErrorMatched(filter, error) {
10
+ if (isFunction(filter)) {
11
+ return is(error, filter);
12
+ }
13
+ return error && error.code === filter;
14
+ }
15
+
16
+ export default function ErrorHandlerMixin() {
17
+ StatefulMixin.call(this);
18
+ }
19
+
20
+ definePrototype(ErrorHandlerMixin, StatefulMixin, {
21
+ catch: function (filter, callback) {
22
+ if (!callback) {
23
+ callback = filter;
24
+ filter = null;
25
+ }
26
+ return emitter.add(this, filter ? 'error' : 'default', function (e) {
27
+ if (!filter || isErrorMatched(filter, e.error)) {
28
+ return callback(e.error);
29
+ }
30
+ });
31
+ },
32
+ initElement: function (element, state) {
33
+ var self = this;
34
+ ErrorHandlerMixinSuper.initElement.call(self, element, state);
35
+ dom.on(element, 'error', function (e) {
36
+ var data = { error: e.error };
37
+ return emitter.emit('error', self, data) || emitter.emit('default', self, data);
38
+ });
39
+ }
40
+ });
@@ -1,24 +1,29 @@
1
1
  import { definePrototype } from "../include/zeta-dom/util.js";
2
2
  import { setClass } from "../include/zeta-dom/domUtil.js";
3
3
  import dom from "../include/zeta-dom/dom.js";
4
- import ClassNameMixin from "./ClassNameMixin.js";
4
+ import StatefulMixin from "./StatefulMixin.js";
5
5
 
6
- const FocusStateMixinSuper = ClassNameMixin.prototype;
6
+ const FocusStateMixinSuper = StatefulMixin.prototype;
7
7
 
8
8
  export default function FocusStateMixin() {
9
- ClassNameMixin.call(this, ['focused']);
9
+ StatefulMixin.call(this);
10
10
  }
11
11
 
12
- definePrototype(FocusStateMixin, ClassNameMixin, {
12
+ definePrototype(FocusStateMixin, StatefulMixin, {
13
13
  initElement: function (element, state) {
14
14
  FocusStateMixinSuper.initElement.call(this, element, state);
15
15
  dom.on(element, {
16
16
  focusin: function () {
17
+ state.focused = true;
17
18
  setClass(element, 'focused', true);
18
19
  },
19
20
  focusout: function () {
21
+ state.focused = false;
20
22
  setClass(element, 'focused', false);
21
23
  }
22
24
  });
25
+ },
26
+ getClassNames: function () {
27
+ return [{ focused: !!this.state.focused }];
23
28
  }
24
29
  });
@@ -1,27 +1,33 @@
1
1
  import { definePrototype } from "../include/zeta-dom/util.js";
2
2
  import { setClass } from "../include/zeta-dom/domUtil.js";
3
3
  import dom from "../include/zeta-dom/dom.js";
4
- import ClassNameMixin from "./ClassNameMixin.js";
4
+ import StatefulMixin from "./StatefulMixin.js";
5
5
 
6
- const LoadingStateMixinSuper = ClassNameMixin.prototype;
6
+ const LoadingStateMixinSuper = StatefulMixin.prototype;
7
7
 
8
8
  export default function LoadingStateMixin() {
9
- ClassNameMixin.call(this, ['loading']);
9
+ StatefulMixin.call(this);
10
10
  }
11
11
 
12
- definePrototype(LoadingStateMixin, ClassNameMixin, {
12
+ definePrototype(LoadingStateMixin, StatefulMixin, {
13
13
  initElement: function (element, state) {
14
14
  LoadingStateMixinSuper.initElement.call(this, element, state);
15
15
  dom.on(element, {
16
16
  asyncStart: function () {
17
+ state.loading = true;
17
18
  setClass(element, 'loading', true);
18
19
  },
19
20
  asyncEnd: function () {
21
+ state.loading = false;
20
22
  setClass(element, 'loading', false);
21
23
  },
22
24
  cancelled: function () {
25
+ state.loading = false;
23
26
  setClass(element, 'loading', false);
24
27
  }
25
28
  });
29
+ },
30
+ getClassNames: function () {
31
+ return [{ loading: !!this.state.loading }];
26
32
  }
27
33
  });
@@ -41,11 +41,9 @@ definePrototype(StatefulMixin, Mixin, {
41
41
  const self = this;
42
42
  const state = self.state;
43
43
  return function (current) {
44
- if (current !== state.element) {
44
+ if (current && current !== state.element) {
45
45
  state.element = current;
46
- if (current) {
47
- self.initElement(current, state);
48
- }
46
+ self.initElement(current, state);
49
47
  }
50
48
  };
51
49
  },
package/package.json CHANGED
@@ -1,35 +1,47 @@
1
1
  {
2
2
  "name": "brew-js-react",
3
- "version": "0.1.0-beta",
3
+ "version": "0.1.3",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "types": "index.d.ts",
8
8
  "scripts": {
9
9
  "build": "webpack",
10
+ "test": "cross-env NODE_OPTIONS=--experimental-vm-modules npx jest",
11
+ "snapshot": "npm run test -- -u",
10
12
  "version": "npm run build && git add -A dist",
11
13
  "release": "node ./npm-publish.cjs"
12
14
  },
13
15
  "author": "misonou",
14
16
  "license": "ISC",
15
- "repository": "github:misonou/brew-react",
17
+ "homepage": "https://hackmd.io/@misonou/brew-js-react",
18
+ "repository": "github:misonou/brew-js-react",
16
19
  "dependencies": {
17
- "brew-js": "^0.2.1",
18
- "react": "^16.14.0",
19
- "react-dom": "^16.14.0",
20
- "zeta-dom": "^0.1.13",
21
- "zeta-dom-react": "^0.1.0-beta"
20
+ "brew-js": ">=0.2.1",
21
+ "waterpipe": "^2.4.2",
22
+ "zeta-dom": ">=0.1.13",
23
+ "zeta-dom-react": ">=0.1.1"
24
+ },
25
+ "peerDependencies": {
26
+ "react": ">=16.8.0",
27
+ "react-dom": ">=16.8.0"
22
28
  },
23
29
  "devDependencies": {
24
30
  "@babel/core": "^7.12.3",
25
- "@babel/preset-env": "^7.12.1",
31
+ "@babel/preset-env": "^7.16.11",
32
+ "@babel/preset-react": "^7.16.7",
26
33
  "@jest/globals": "^26.6.2",
34
+ "@testing-library/dom": "^8.11.3",
35
+ "@testing-library/react": "^12.1.2",
36
+ "@testing-library/react-hooks": "^7.0.2",
27
37
  "@types/jest": "^26.0.15",
28
38
  "babel-loader": "^8.2.1",
29
39
  "clean-webpack-plugin": "^4.0.0",
30
40
  "cross-env": "^7.0.2",
41
+ "glob": "^7.2.0",
31
42
  "jest": "^27.0.6",
32
43
  "ncp": "^2.0.0",
44
+ "regenerator-runtime": "^0.13.9",
33
45
  "webpack": "^5.3.0",
34
46
  "webpack-cli": "^4.1.0"
35
47
  },
package/view.d.ts CHANGED
@@ -9,6 +9,13 @@ export type ViewComponent<P> = React.FC<P>;
9
9
  */
10
10
  export function registerView<P>(factory: () => Promise<{ default: React.ComponentType<P> }>, params: Zeta.Dictionary<string>): ViewComponent<P>;
11
11
 
12
+ /**
13
+ * Determines whether a registered view component matches current route state.
14
+ * However it does not imply if the view is in fact being rendered.
15
+ * @param view A view component returned from {@link registerView}.
16
+ */
17
+ export function isViewMatched(view: ViewComponent<any>): boolean;
18
+
12
19
  /**
13
20
  * Renders view by matching current route state against registered route parameters of each supplied views.
14
21
  * @param args A list of view components created by {@link registerView}.
@@ -31,3 +38,19 @@ export function renderView(props: ViewComponentRootProps, ...args: ViewComponent
31
38
  * @param params Extra route parameters that supplements or overrides current route parameters.
32
39
  */
33
40
  export function linkTo(view: ViewComponent<any>, params?: Zeta.Dictionary<string>): string;
41
+
42
+ /**
43
+ * Navigates to path that will render the specified view.
44
+ * @param view A view component created by {@link registerView}.
45
+ * @param params Extra route parameters that supplements or overrides current route parameters.
46
+ * @see {@link linkTo}.
47
+ */
48
+ export function navigateTo(view: ViewComponent<any>, params?: Zeta.Dictionary<string>): Promise<Brew.NavigateResult>;
49
+
50
+ /**
51
+ * Navigates to path that will render the specified view, replacing current state in browser history.
52
+ * @param view A view component created by {@link registerView}.
53
+ * @param params Extra route parameters that supplements or overrides current route parameters.
54
+ * @see {@link linkTo}.
55
+ */
56
+ export function redirectTo(view: ViewComponent<any>, params?: Zeta.Dictionary<string>): Promise<Brew.NavigateResult>;
package/view.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
- import brew from "brew-js";
3
2
  import { useAsync } from "zeta-dom-react";
4
3
  import { any, definePrototype, equal, exclude, extend, keys, makeArray, noop, pick, setImmediate } from "./include/zeta-dom/util.js";
4
+ import { animateIn, animateOut } from "./include/brew-js/anim.js";
5
5
  import { app } from "./app.js";
6
6
 
7
7
  const routeMap = new Map();
@@ -32,7 +32,7 @@ definePrototype(ViewContainer, React.Component, {
32
32
  self.prevView = self.currentView;
33
33
  self.prevElement = self.currentElement;
34
34
  self.currentElement = undefined;
35
- brew.animateOut(self.prevElement, 'show').then(function () {
35
+ animateOut(self.prevElement, 'show').then(function () {
36
36
  self.prevElement = undefined;
37
37
  self.prevView = undefined;
38
38
  self.forceUpdate();
@@ -44,7 +44,7 @@ definePrototype(ViewContainer, React.Component, {
44
44
  onComponentLoaded: function (element) {
45
45
  self.currentElement = element;
46
46
  setImmediate(function () {
47
- return brew.animateIn(element, 'show');
47
+ return animateIn(element, 'show');
48
48
  });
49
49
  }
50
50
  });
@@ -53,14 +53,15 @@ definePrototype(ViewContainer, React.Component, {
53
53
  },
54
54
  getViewComponent: function () {
55
55
  var views = this.props.views;
56
- var V = any(views, function (V) {
57
- var params = routeMap.get(V);
58
- return params && equal(params, pick(app.route, keys(params)));
59
- });
60
- return V || void app.navigate(linkTo(views[0]), true);
56
+ return any(views, isViewMatched) || void redirectTo(views[0]);
61
57
  }
62
58
  });
63
59
 
60
+ export function isViewMatched(view) {
61
+ var params = routeMap.get(view);
62
+ return !!params && equal(params, pick(app.route, keys(params)));
63
+ }
64
+
64
65
  export function registerView(factory, routeParams) {
65
66
  var Component = function (props) {
66
67
  var childProps = exclude(props, ['rootProps', 'onComponentLoaded']);
@@ -90,3 +91,11 @@ export function renderView() {
90
91
  export function linkTo(view, params) {
91
92
  return app.route.getPath(extend({}, app.route, params, routeMap.get(view)));
92
93
  }
94
+
95
+ export function navigateTo(view, params) {
96
+ return app.navigate(linkTo(view, params));
97
+ }
98
+
99
+ export function redirectTo(view, params) {
100
+ return app.navigate(linkTo(view, params), true);
101
+ }