brew-js-react 0.5.1 → 0.5.2

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,7 +1,7 @@
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";
4
+ import { either, extend, makeAsync, noop, pick, pipe, resolve } from "./include/zeta-dom/util.js";
5
5
  import { containsOrEquals, removeNode, setClass } 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";
@@ -17,7 +17,11 @@ 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) {
@@ -31,7 +35,7 @@ export function createDialog(props) {
31
35
  setClass(root, 'loading', false);
32
36
  }
33
37
  });
34
- subscribeAsync(root);
38
+ subscribeAsync(root, true);
35
39
 
36
40
  return {
37
41
  root: root,
@@ -45,7 +49,6 @@ export function createDialog(props) {
45
49
  dom.retainFocus(dom.activeElement, root);
46
50
  if (props.modal) {
47
51
  root.setAttribute('is-modal', '');
48
- dom.setModal(root);
49
52
  }
50
53
  if (props.onRender) {
51
54
  var dialogProps = extend({}, props, {
@@ -60,20 +63,15 @@ export function createDialog(props) {
60
63
  content = createElement(props.wrapper, dialogProps, content);
61
64
  }
62
65
  reactRoot.render(content);
63
- setImmediate(function () {
64
- dom.focus(root);
65
- });
66
66
  }
67
- promise = openFlyout(root);
67
+ promise = resolve().then(function () {
68
+ return openFlyout(root, null, pick(props, ['focus']));
69
+ });
68
70
  if (props.preventLeave) {
69
71
  preventLeave(root, promise);
70
72
  } else if (props.preventNavigation) {
71
73
  lock(root, promise);
72
74
  }
73
- always(promise, function () {
74
- promise = null;
75
- });
76
- (props.onOpen || noop)(root);
77
75
  return promise;
78
76
  }
79
77
  };
@@ -1,4 +1,4 @@
1
- /*! brew-js-react v0.5.1 | (c) misonou | https://misonou.github.io */
1
+ /*! brew-js-react v0.5.2 | (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,
@@ -443,7 +444,11 @@ function createDialog(props) {
443
444
 
444
445
  var promise;
445
446
  zeta_dom_dom.on(root, {
447
+ flyoutshow: function flyoutshow() {
448
+ (props.onOpen || noop)(root);
449
+ },
446
450
  flyouthide: function flyouthide() {
451
+ promise = null;
447
452
  removeNode(root);
448
453
  (props.onClose || noop)(root);
449
454
 
@@ -458,7 +463,7 @@ function createDialog(props) {
458
463
  setClass(root, 'loading', false);
459
464
  }
460
465
  });
461
- subscribeAsync(root);
466
+ subscribeAsync(root, true);
462
467
  return {
463
468
  root: root,
464
469
  close: _closeDialog,
@@ -473,7 +478,6 @@ function createDialog(props) {
473
478
 
474
479
  if (props.modal) {
475
480
  root.setAttribute('is-modal', '');
476
- zeta_dom_dom.setModal(root);
477
481
  }
478
482
 
479
483
  if (props.onRender) {
@@ -491,12 +495,11 @@ function createDialog(props) {
491
495
  }
492
496
 
493
497
  reactRoot.render(content);
494
- setImmediate(function () {
495
- zeta_dom_dom.focus(root);
496
- });
497
498
  }
498
499
 
499
- promise = openFlyout(root);
500
+ promise = resolve().then(function () {
501
+ return openFlyout(root, null, pick(props, ['focus']));
502
+ });
500
503
 
501
504
  if (props.preventLeave) {
502
505
  preventLeave(root, promise);
@@ -504,10 +507,6 @@ function createDialog(props) {
504
507
  lock(root, promise);
505
508
  }
506
509
 
507
- always(promise, function () {
508
- promise = null;
509
- });
510
- (props.onOpen || noop)(root);
511
510
  return promise;
512
511
  }
513
512
  };
@@ -1636,7 +1635,7 @@ definePrototype(FlyoutToggleMixin, ClassNameMixin, {
1636
1635
  var self = this;
1637
1636
  FlyoutToggleMixinSuper.initElement.call(self, element, state);
1638
1637
  self.onDispose(zeta_dom_dom.on(element, 'click', function () {
1639
- toggleFlyout(self.flyoutMixin.elements()[0], element);
1638
+ toggleFlyout(self.flyoutMixin.elements()[0], element, self.flyoutMixin.flyoutOptions);
1640
1639
  }));
1641
1640
  }
1642
1641
  });
@@ -1656,6 +1655,7 @@ function FlyoutMixin() {
1656
1655
  self.isFlyoutOpened = false;
1657
1656
  self.animating = false;
1658
1657
  self.visible = false;
1658
+ self.initialFocus = undefined;
1659
1659
  self.toggle = new FlyoutToggleMixin(self);
1660
1660
  self.onDispose(function () {
1661
1661
  self.isFlyoutOpened = false;
@@ -1663,6 +1663,16 @@ function FlyoutMixin() {
1663
1663
  });
1664
1664
  }
1665
1665
  definePrototype(FlyoutMixin, ClassNameMixin, {
1666
+ get flyoutOptions() {
1667
+ var options = {};
1668
+
1669
+ if (this.initialFocus !== undefined) {
1670
+ options.focus = this.initialFocus;
1671
+ }
1672
+
1673
+ return options;
1674
+ },
1675
+
1666
1676
  next: function next() {
1667
1677
  this.effects = undefined;
1668
1678
  return FlyoutMixinSuper.next.call(this);
@@ -1689,7 +1699,7 @@ definePrototype(FlyoutMixin, ClassNameMixin, {
1689
1699
  open: function open(value) {
1690
1700
  var element = this.elements()[0];
1691
1701
  valueMap.set(element, value);
1692
- return openFlyout(element);
1702
+ return openFlyout(element, null, this.flyoutOptions);
1693
1703
  },
1694
1704
  close: function close(value) {
1695
1705
  return closeFlyout(this.elements()[0], value);