brew-js-react 0.5.1 → 0.5.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/dialog.d.ts CHANGED
@@ -1,35 +1,116 @@
1
1
  export type DialogCloseCallback<T> = (value?: T) => Promise<void>;
2
2
 
3
3
  export interface DialogState<T> {
4
+ /**
5
+ * Gets the root element of the dialog.
6
+ */
4
7
  readonly root: HTMLElement;
8
+ /**
9
+ * Opens the dialog.
10
+ *
11
+ * It will receive result from {@link DialogState.close} or {@link DialogRenderComponentProps.closeDialog} when the dialog is properly closed.
12
+ * Otherwise in cases such as being closed due to lost of focus, result value will always be `undefined`.
13
+ */
5
14
  open: () => Promise<T | undefined>;
6
- close: DialogCloseCallback<T>;
15
+ /**
16
+ * Closes the dialog, with optional result value.
17
+ *
18
+ * This method is different from {@link DialogRenderComponentProps.closeDialog} that {@link DialogBaseProps.onCommit} will not be called.
19
+ */
20
+ close: (value?: T) => Promise<void>;
7
21
  }
8
22
 
9
- export interface DialogBaseProps<T> {
23
+ export interface DialogBaseProps<T, V = T> {
24
+ /**
25
+ * Specifies dialog title.
26
+ * This property is intended to be handled by {@link DialogOptions.onRender} or {@link DialogOptions.wrapper}.
27
+ */
10
28
  title?: string;
29
+ /**
30
+ * CSS class names to be added to root element.
31
+ */
11
32
  className?: string;
33
+ /**
34
+ * Whether to show close button in the dialog.
35
+ * This property is intended to be handled by {@link DialogOptions.onRender} or {@link DialogOptions.wrapper}.
36
+ */
12
37
  showCloseButton?: boolean;
38
+ /**
39
+ * Whether confirmation should be prompted when user leaves the page.
40
+ */
13
41
  preventLeave?: boolean;
42
+ /**
43
+ * Whether navigation within single-paged app should be prevented.
44
+ */
14
45
  preventNavigation?: boolean;
46
+ /**
47
+ * Whether the dialog is modal.
48
+ */
15
49
  modal?: boolean;
16
- onCommit?: (value: T | undefined) => void;
50
+ /**
51
+ * Whether flyout content will be initially focused.
52
+ * Default is `true` if source element is not an text input element.
53
+ *
54
+ * If a CSS selector is given, the first matched element will be focused; otherwise
55
+ * the first focusable element will be focused.
56
+ */
57
+ focus?: boolean | string;
58
+ /**
59
+ * Callback to perform asynchronous action when user confirms the dialog.
60
+ *
61
+ * The callback will receive value sent from {@link DialogRenderComponentProps.closeDialog},
62
+ * and will send resolved value to promise returned by {@link DialogState.open}.
63
+ *
64
+ * The dialog will be held open until the returned promise resolves.
65
+ * When the promise rejects, the dialog will remain open.
66
+ */
67
+ onCommit?: (value: V | undefined) => T | Promise<T> | void;
68
+ /**
69
+ * Callback to be invoked when dialog has opened.
70
+ */
17
71
  onOpen?: (root: HTMLElement) => void;
72
+ /**
73
+ * Callback to be invoked when dialog has closed, after outro animation and before React DOM is unmounted.
74
+ */
18
75
  onClose?: (root: HTMLElement) => void;
19
76
  }
20
77
 
21
- export interface DialogRenderComponentProps<T, P = {}> extends DialogBaseProps<T>, P {
22
- closeDialog: DialogCloseCallback<T>;
78
+ export interface DialogRenderComponentProps<T, V = T> extends DialogBaseProps<T, V> {
79
+ /**
80
+ * Commits the dialog, with optional result value.
81
+ *
82
+ * When {@link DialogBaseProps.onCommit} is specified, it is invoked with the value passed in.
83
+ * Dialog will be closed when async operation is completed, or remain open if the operation failed.
84
+ */
85
+ closeDialog: (value?: V) => Promise<void>;
23
86
  }
24
87
 
25
- export interface DialogOptions<T, P = {}> extends DialogBaseProps<T> {
26
- onRender: React.FC<DialogRenderComponentProps<T, P>>;
88
+ export interface DialogOptions<T, V = T> extends DialogBaseProps<T, V> {
89
+ /**
90
+ * A callback that render dialog contents.
91
+ */
92
+ onRender: React.FC<DialogRenderComponentProps<T, V> & this>;
93
+ /**
94
+ * Specifies wrapper component that envelops content from {@link DialogOptions.onRender},
95
+ * which is useful for reusable layout for dialogs.
96
+ */
97
+ wrapper?: React.FC<React.PropsWithChildren<DialogRenderComponentProps<T, V> & this>>;
27
98
  }
28
99
 
29
- export interface DialogProps<T> extends React.PropsWithChildren<DialogBaseProps<T>> {
100
+ export interface DialogProps<T, V = T> extends React.PropsWithChildren<DialogBaseProps<T, V>> {
101
+ /**
102
+ * A boolean flag controlling whether dialog is open.
103
+ */
30
104
  isOpen: boolean;
31
105
  }
32
106
 
33
- export function createDialog<T = any, P = {}>(props: DialogOptions<T, P> & { wrapper?: React.FC<React.PropsWithChildren<DialogRenderComponentProps<T, P>>> }): DialogState<T>;
107
+ /**
108
+ * Creates a controller to render dialog.
109
+ * @param props A dictionary containing options.
110
+ */
111
+ export function createDialog<T = any, V = T>(props: DialogOptions<T, V>): DialogState<T>;
34
112
 
35
- export function Dialog<T>(props: DialogProps<T>): JSX.Element;
113
+ /**
114
+ * Renders a dialog declaratively.
115
+ */
116
+ export function Dialog<T, V = T>(props: DialogProps<T, V>): JSX.Element;
package/dialog.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { createElement, useEffect, useState } from "react";
2
2
  import ReactDOM from "react-dom";
3
3
  import ReactDOMClient from "./include/external/react-dom-client.js";
4
- import { always, either, extend, makeAsync, noop, pipe, setImmediate } from "./include/zeta-dom/util.js";
5
- import { containsOrEquals, removeNode, setClass } from "./include/zeta-dom/domUtil.js";
4
+ import { either, extend, makeAsync, noop, pick, pipe, resolve } from "./include/zeta-dom/util.js";
5
+ import { containsOrEquals, removeNode } from "./include/zeta-dom/domUtil.js";
6
6
  import dom from "./include/zeta-dom/dom.js";
7
7
  import { lock, notifyAsync, preventLeave, subscribeAsync } from "./include/zeta-dom/domLock.js";
8
8
  import { closeFlyout, openFlyout } from "./include/brew-js/domAction.js";
@@ -17,21 +17,20 @@ export function createDialog(props) {
17
17
  var promise;
18
18
 
19
19
  dom.on(root, {
20
+ flyoutshow: function () {
21
+ (props.onOpen || noop)(root);
22
+ },
20
23
  flyouthide: function () {
24
+ promise = null;
21
25
  removeNode(root);
22
26
  (props.onClose || noop)(root);
23
27
  if (props.onRender) {
24
28
  reactRoot.unmount();
25
29
  }
26
- },
27
- asyncStart: function () {
28
- setClass(root, 'loading', true);
29
- },
30
- asyncEnd: function () {
31
- setClass(root, 'loading', false);
32
30
  }
33
31
  });
34
- subscribeAsync(root);
32
+ root.setAttribute('loading-class', '');
33
+ subscribeAsync(root, true);
35
34
 
36
35
  return {
37
36
  root: root,
@@ -45,7 +44,6 @@ export function createDialog(props) {
45
44
  dom.retainFocus(dom.activeElement, root);
46
45
  if (props.modal) {
47
46
  root.setAttribute('is-modal', '');
48
- dom.setModal(root);
49
47
  }
50
48
  if (props.onRender) {
51
49
  var dialogProps = extend({}, props, {
@@ -60,20 +58,15 @@ export function createDialog(props) {
60
58
  content = createElement(props.wrapper, dialogProps, content);
61
59
  }
62
60
  reactRoot.render(content);
63
- setImmediate(function () {
64
- dom.focus(root);
65
- });
66
61
  }
67
- promise = openFlyout(root);
62
+ promise = resolve().then(function () {
63
+ return openFlyout(root, null, pick(props, ['focus']));
64
+ });
68
65
  if (props.preventLeave) {
69
66
  preventLeave(root, promise);
70
67
  } else if (props.preventNavigation) {
71
68
  lock(root, promise);
72
69
  }
73
- always(promise, function () {
74
- promise = null;
75
- });
76
- (props.onOpen || noop)(root);
77
70
  return promise;
78
71
  }
79
72
  };
@@ -1,4 +1,4 @@
1
- /*! brew-js-react v0.5.1 | (c) misonou | https://misonou.github.io */
1
+ /*! brew-js-react v0.5.3 | (c) misonou | https://misonou.github.io */
2
2
  (function webpackUniversalModuleDefinition(root, factory) {
3
3
  if(typeof exports === 'object' && typeof module === 'object')
4
4
  module.exports = factory(require("brew-js"), require("react"), require("react-dom"), require("zeta-dom"), require("zeta-dom-react"), require("waterpipe"), require("jquery"));
@@ -355,6 +355,7 @@ var domUtil_lib$util = external_commonjs_zeta_dom_commonjs2_zeta_dom_amd_zeta_do
355
355
  createNodeIterator = domUtil_lib$util.createNodeIterator,
356
356
  createTreeWalker = domUtil_lib$util.createTreeWalker,
357
357
  bind = domUtil_lib$util.bind,
358
+ bindOnce = domUtil_lib$util.bindOnce,
358
359
  bindUntil = domUtil_lib$util.bindUntil,
359
360
  dispatchDOMMouseEvent = domUtil_lib$util.dispatchDOMMouseEvent,
360
361
  removeNode = domUtil_lib$util.removeNode,
@@ -412,6 +413,7 @@ var domLock_lib$dom = external_commonjs_zeta_dom_commonjs2_zeta_dom_amd_zeta_dom
412
413
  cancelLock = domLock_lib$dom.cancelLock,
413
414
  subscribeAsync = domLock_lib$dom.subscribeAsync,
414
415
  notifyAsync = domLock_lib$dom.notifyAsync,
416
+ runAsync = domLock_lib$dom.runAsync,
415
417
  preventLeave = domLock_lib$dom.preventLeave;
416
418
 
417
419
  ;// CONCATENATED MODULE: ./tmp/brew-js/domAction.js
@@ -443,22 +445,21 @@ function createDialog(props) {
443
445
 
444
446
  var promise;
445
447
  zeta_dom_dom.on(root, {
448
+ flyoutshow: function flyoutshow() {
449
+ (props.onOpen || noop)(root);
450
+ },
446
451
  flyouthide: function flyouthide() {
452
+ promise = null;
447
453
  removeNode(root);
448
454
  (props.onClose || noop)(root);
449
455
 
450
456
  if (props.onRender) {
451
457
  reactRoot.unmount();
452
458
  }
453
- },
454
- asyncStart: function asyncStart() {
455
- setClass(root, 'loading', true);
456
- },
457
- asyncEnd: function asyncEnd() {
458
- setClass(root, 'loading', false);
459
459
  }
460
460
  });
461
- subscribeAsync(root);
461
+ root.setAttribute('loading-class', '');
462
+ subscribeAsync(root, true);
462
463
  return {
463
464
  root: root,
464
465
  close: _closeDialog,
@@ -473,7 +474,6 @@ function createDialog(props) {
473
474
 
474
475
  if (props.modal) {
475
476
  root.setAttribute('is-modal', '');
476
- zeta_dom_dom.setModal(root);
477
477
  }
478
478
 
479
479
  if (props.onRender) {
@@ -491,12 +491,11 @@ function createDialog(props) {
491
491
  }
492
492
 
493
493
  reactRoot.render(content);
494
- setImmediate(function () {
495
- zeta_dom_dom.focus(root);
496
- });
497
494
  }
498
495
 
499
- promise = openFlyout(root);
496
+ promise = resolve().then(function () {
497
+ return openFlyout(root, null, pick(props, ['focus']));
498
+ });
500
499
 
501
500
  if (props.preventLeave) {
502
501
  preventLeave(root, promise);
@@ -504,10 +503,6 @@ function createDialog(props) {
504
503
  lock(root, promise);
505
504
  }
506
505
 
507
- always(promise, function () {
508
- promise = null;
509
- });
510
- (props.onOpen || noop)(root);
511
506
  return promise;
512
507
  }
513
508
  };
@@ -1273,9 +1268,9 @@ function makeTranslation(resources, defaultLang) {
1273
1268
  ;// CONCATENATED MODULE: ./src/mixins/StaticAttributeMixin.js
1274
1269
 
1275
1270
 
1276
- function StaticAttributeMixin(attributes) {
1271
+ function StaticAttributeMixin(name, value) {
1277
1272
  Mixin.call(this);
1278
- this.attributes = attributes || {};
1273
+ this.attributes = isPlainObject(name) || kv(name, value || '');
1279
1274
  }
1280
1275
  definePrototype(StaticAttributeMixin, Mixin, {
1281
1276
  getCustomAttributes: function getCustomAttributes() {
@@ -1306,9 +1301,11 @@ definePrototype(Mixin, {
1306
1301
  watchable(Mixin.prototype);
1307
1302
  util_define(Mixin, {
1308
1303
  get scrollableTarget() {
1309
- return new StaticAttributeMixin({
1310
- 'scrollable-target': ''
1311
- });
1304
+ return new StaticAttributeMixin('scrollable-target');
1305
+ },
1306
+
1307
+ get tabRoot() {
1308
+ return new StaticAttributeMixin('tab-root');
1312
1309
  },
1313
1310
 
1314
1311
  use: function use() {
@@ -1425,8 +1422,10 @@ definePrototype(StatefulMixin, Mixin, {
1425
1422
 
1426
1423
  self.onLayoutEffect(current, state);
1427
1424
  obj.elements.add(current);
1428
- } else {
1429
- obj.elements["delete"](state.element);
1425
+ } else if (state) {
1426
+ var prev = state.element;
1427
+ self.onBeforeUpdate(prev, state);
1428
+ obj.elements["delete"](prev);
1430
1429
  }
1431
1430
  };
1432
1431
  },
@@ -1446,6 +1445,7 @@ definePrototype(StatefulMixin, Mixin, {
1446
1445
  extend(state, newState);
1447
1446
  },
1448
1447
  onLayoutEffect: function onLayoutEffect(element, state) {},
1448
+ onBeforeUpdate: function onBeforeUpdate(element, state) {},
1449
1449
  clone: function clone() {
1450
1450
  return this;
1451
1451
  },
@@ -1471,7 +1471,8 @@ function checkState(self, element, state, fireEvent) {
1471
1471
  classNames[i] = element.classList.contains(i);
1472
1472
  });
1473
1473
 
1474
- if (fireEvent && !equal(prev, classNames)) {
1474
+ if (fireEvent && !equal(state.prev || prev, classNames)) {
1475
+ state.prev = prev;
1475
1476
  self.onClassNameUpdated(element, prev, extend({}, classNames));
1476
1477
  }
1477
1478
  }
@@ -1497,6 +1498,9 @@ definePrototype(ClassNameMixin, StatefulMixin, {
1497
1498
  onLayoutEffect: function onLayoutEffect(element, state) {
1498
1499
  setClass(element, state.classNames);
1499
1500
  },
1501
+ onBeforeUpdate: function onBeforeUpdate(element, state) {
1502
+ checkState(this, element, state);
1503
+ },
1500
1504
  onClassNameUpdated: function onClassNameUpdated(element, prevState, state) {}
1501
1505
  });
1502
1506
  ;// CONCATENATED MODULE: ./src/mixins/AnimateMixin.js
@@ -1525,7 +1529,7 @@ definePrototype(AnimateMixin, ClassNameMixin, {
1525
1529
  },
1526
1530
  getCustomAttributes: function getCustomAttributes() {
1527
1531
  var self = this;
1528
- return extend({}, AnimateMixinSuper.getCustomAttributes.call(self), {
1532
+ return extend(AnimateMixinSuper.getCustomAttributes.call(self), {
1529
1533
  'animate-in': (self.effects || []).join(' '),
1530
1534
  'animate-on': self.trigger || 'show'
1531
1535
  });
@@ -1541,7 +1545,7 @@ function AnimateSequenceItemMixin(className) {
1541
1545
  }
1542
1546
  definePrototype(AnimateSequenceItemMixin, ClassNameMixin, {
1543
1547
  getCustomAttributes: function getCustomAttributes() {
1544
- return extend({}, AnimateSequenceItemMixinSuper.getCustomAttributes.call(this), {
1548
+ return extend(AnimateSequenceItemMixinSuper.getCustomAttributes.call(this), {
1545
1549
  'is-animate-sequence': ''
1546
1550
  });
1547
1551
  },
@@ -1576,7 +1580,7 @@ definePrototype(AnimateSequenceMixin, AnimateMixin, {
1576
1580
  },
1577
1581
  getCustomAttributes: function getCustomAttributes() {
1578
1582
  var self = this;
1579
- return extend({}, AnimateSequenceMixinSuper.getCustomAttributes.call(self), {
1583
+ return extend(AnimateSequenceMixinSuper.getCustomAttributes.call(self), {
1580
1584
  'animate-in': null,
1581
1585
  'animate-sequence-type': (self.effects || []).join(' '),
1582
1586
  'animate-sequence': self.selector || '.' + self.className
@@ -1636,7 +1640,7 @@ definePrototype(FlyoutToggleMixin, ClassNameMixin, {
1636
1640
  var self = this;
1637
1641
  FlyoutToggleMixinSuper.initElement.call(self, element, state);
1638
1642
  self.onDispose(zeta_dom_dom.on(element, 'click', function () {
1639
- toggleFlyout(self.flyoutMixin.elements()[0], element);
1643
+ toggleFlyout(self.flyoutMixin.elements()[0], element, self.flyoutMixin.flyoutOptions);
1640
1644
  }));
1641
1645
  }
1642
1646
  });
@@ -1656,6 +1660,7 @@ function FlyoutMixin() {
1656
1660
  self.isFlyoutOpened = false;
1657
1661
  self.animating = false;
1658
1662
  self.visible = false;
1663
+ self.initialFocus = undefined;
1659
1664
  self.toggle = new FlyoutToggleMixin(self);
1660
1665
  self.onDispose(function () {
1661
1666
  self.isFlyoutOpened = false;
@@ -1663,6 +1668,16 @@ function FlyoutMixin() {
1663
1668
  });
1664
1669
  }
1665
1670
  definePrototype(FlyoutMixin, ClassNameMixin, {
1671
+ get flyoutOptions() {
1672
+ var options = {};
1673
+
1674
+ if (this.initialFocus !== undefined) {
1675
+ options.focus = this.initialFocus;
1676
+ }
1677
+
1678
+ return options;
1679
+ },
1680
+
1666
1681
  next: function next() {
1667
1682
  this.effects = undefined;
1668
1683
  return FlyoutMixinSuper.next.call(this);
@@ -1673,7 +1688,7 @@ definePrototype(FlyoutMixin, ClassNameMixin, {
1673
1688
  },
1674
1689
  getCustomAttributes: function getCustomAttributes() {
1675
1690
  var self = this;
1676
- return extend({}, FlyoutMixinSuper.getCustomAttributes.call(self), {
1691
+ return extend(FlyoutMixinSuper.getCustomAttributes.call(self), {
1677
1692
  'is-flyout': '',
1678
1693
  'swipe-dismiss': self.swipeToDismiss
1679
1694
  }, self.modal && {
@@ -1689,7 +1704,7 @@ definePrototype(FlyoutMixin, ClassNameMixin, {
1689
1704
  open: function open(value) {
1690
1705
  var element = this.elements()[0];
1691
1706
  valueMap.set(element, value);
1692
- return openFlyout(element);
1707
+ return openFlyout(element, null, this.flyoutOptions);
1693
1708
  },
1694
1709
  close: function close(value) {
1695
1710
  return closeFlyout(this.elements()[0], value);
@@ -1771,32 +1786,35 @@ definePrototype(FocusStateMixin, StatefulMixin, {
1771
1786
  setClass(element, 'focused', state.focused);
1772
1787
  }
1773
1788
  });
1789
+ ;// CONCATENATED MODULE: ./tmp/brew-js/directive.js
1790
+
1791
+ var getDirectiveComponent = external_commonjs_brew_js_commonjs2_brew_js_amd_brew_js_root_brew_.getDirectiveComponent,
1792
+ registerSimpleDirective = external_commonjs_brew_js_commonjs2_brew_js_amd_brew_js_root_brew_.registerSimpleDirective,
1793
+ registerDirective = external_commonjs_brew_js_commonjs2_brew_js_amd_brew_js_root_brew_.registerDirective;
1794
+
1774
1795
  ;// CONCATENATED MODULE: ./src/mixins/LoadingStateMixin.js
1775
1796
 
1776
1797
 
1777
1798
 
1778
1799
 
1779
- var LoadingStateMixinSuper = StatefulMixin.prototype;
1800
+
1801
+ var LoadingStateMixinSuper = ClassNameMixin.prototype;
1780
1802
  function LoadingStateMixin() {
1781
- StatefulMixin.call(this);
1803
+ ClassNameMixin.call(this, ['loading', 'loading-complete']);
1804
+ this.loading = false;
1782
1805
  }
1783
- definePrototype(LoadingStateMixin, StatefulMixin, {
1806
+ definePrototype(LoadingStateMixin, ClassNameMixin, {
1784
1807
  initElement: function initElement(element, state) {
1785
- LoadingStateMixinSuper.initElement.call(this, element, state);
1786
- this.onDispose(subscribeAsync(element, function (loading) {
1787
- state.loading = loading;
1788
- setClass(element, 'loading', loading);
1808
+ var self = this;
1809
+ LoadingStateMixinSuper.initElement.call(self, element, state);
1810
+ getDirectiveComponent(element).enableLoadingClass = true;
1811
+ self.onDispose(subscribeAsync(element, function (loading) {
1812
+ self.loading = loading || !!any(self.elements(), function (v) {
1813
+ return v !== element && getClass(v, 'loading') === true;
1814
+ });
1789
1815
  }));
1790
- },
1791
- onLayoutEffect: function onLayoutEffect(element, state) {
1792
- setClass(element, 'loading', state.loading);
1793
1816
  }
1794
1817
  });
1795
- ;// CONCATENATED MODULE: ./tmp/brew-js/directive.js
1796
-
1797
- var getDirectiveComponent = external_commonjs_brew_js_commonjs2_brew_js_amd_brew_js_root_brew_.getDirectiveComponent,
1798
- registerDirective = external_commonjs_brew_js_commonjs2_brew_js_amd_brew_js_root_brew_.registerDirective;
1799
-
1800
1818
  ;// CONCATENATED MODULE: ./src/mixins/ScrollableMixin.js
1801
1819
 
1802
1820
 
@@ -1818,7 +1836,7 @@ definePrototype(ScrollableMixin, ClassNameMixin, {
1818
1836
  },
1819
1837
  getCustomAttributes: function getCustomAttributes() {
1820
1838
  var options = this.options || {};
1821
- return extend({}, ScrollableMixinSuper.getCustomAttributes.call(this), {
1839
+ return extend(ScrollableMixinSuper.getCustomAttributes.call(this), {
1822
1840
  'scrollable': [options.direction || 'both', options.handle || 'auto'].join(' ')
1823
1841
  }, options.pagedItemSelector && {
1824
1842
  'scroller-snap-page': options.paged,