@reykjavik/hanna-react 0.10.167 → 0.10.169

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/CHANGELOG.md CHANGED
@@ -4,6 +4,25 @@
4
4
 
5
5
  - ... <!-- Add new lines here. -->
6
6
 
7
+ ## 0.10.169
8
+
9
+ _2026-02-24_
10
+
11
+ - `Modal`:
12
+ - fix: Togling `open` prop works unreliably in modern React versions
13
+ - `Multiselect`:
14
+ - fix: Stop `'Escape'` key events bubbling and causing side-effects (e.g.
15
+ closing a parent `Modal`)
16
+
17
+ ## 0.10.168
18
+
19
+ _2026-02-23_
20
+
21
+ - `Modal`:
22
+ - fix: `children` render function not invoked correctly
23
+ - fix: Drop redundant `visible` flag for the `children` render function —
24
+ (It was always `true`)
25
+
7
26
  ## 0.10.167
8
27
 
9
28
  _2026-02-17_
package/Modal.js CHANGED
@@ -6,16 +6,19 @@ const react_1 = tslib_1.__importDefault(require("react"));
6
6
  const _AbstractModal_js_1 = require("./_abstract/_AbstractModal.js");
7
7
  exports.defaultModalTexts = _AbstractModal_js_1.defaultAbstractModalTexts;
8
8
  const Modal = (props) => {
9
- const { bling, render, children } = props;
10
- return (react_1.default.createElement(_AbstractModal_js_1.AbstractModal, Object.assign({}, props, { bem: "Modal", render: (renderProps) => {
11
- const _children = render ? render(renderProps) : children;
12
- return bling ? (react_1.default.createElement(react_1.default.Fragment, null,
13
- _children,
14
- react_1.default.createElement("div", { className: "Modal__blings" },
15
- react_1.default.createElement("div", { className: "Modal__blings__inner" }, bling)))) : (_children);
16
- },
17
- // Required since props might contain children
18
- children: undefined })));
9
+ const { bling, render, // eslint-disable-line deprecation/deprecation
10
+ children, } = props;
11
+ return (react_1.default.createElement(_AbstractModal_js_1.AbstractModal, Object.assign({}, props, { bem: "Modal", render: undefined }), (renderProps) => {
12
+ const _children = render
13
+ ? render(renderProps)
14
+ : typeof children === 'function'
15
+ ? children(renderProps)
16
+ : children;
17
+ return bling ? (react_1.default.createElement(react_1.default.Fragment, null,
18
+ _children,
19
+ react_1.default.createElement("div", { className: "Modal__blings" },
20
+ react_1.default.createElement("div", { className: "Modal__blings__inner" }, bling)))) : (_children);
21
+ }));
19
22
  };
20
23
  exports.Modal = Modal;
21
24
  exports.default = exports.Modal;
package/Multiselect.js CHANGED
@@ -152,6 +152,7 @@ const Multiselect = (props) => {
152
152
  }
153
153
  else if (e.key === 'Escape') {
154
154
  e.preventDefault();
155
+ e.stopPropagation();
155
156
  inputElm.blur();
156
157
  inputElm.focus();
157
158
  toggleOpen(false);
@@ -98,12 +98,6 @@ export type AbstractModalProps = {
98
98
  children: ReactNode | ((props: {
99
99
  /** Action dispacher that initiates modal closing action */
100
100
  closeModal(): void;
101
- /**
102
- * Whether the modal is visible or not. The `onOpen` and `onClosed`
103
- * callbacks are triggered once the modal has become fully visible or
104
- * fully hidden.
105
- */
106
- visible: boolean;
107
101
  }) => ReactNode);
108
102
  }> & WrapperElmProps<'div', 'hidden' | 'role'> & SSRSupportProps;
109
103
  type AbstractModalProps_private = AbstractModalProps & BemProps<true>;
@@ -82,17 +82,6 @@ const AbstractModal = (props) => {
82
82
  }, closeDelay);
83
83
  }
84
84
  };
85
- // ---
86
- // Update open state when props.open changes. Icky but simple.
87
- const lastPropsOpen = (0, react_1.useRef)(openProp);
88
- if (openProp !== lastPropsOpen.current && openProp !== open) {
89
- lastPropsOpen.current = openProp;
90
- // these update state during render, which aborts the current render
91
- // and triggers an immediate rerender.
92
- openProp ? openModal() : closeModal();
93
- }
94
- lastPropsOpen.current = openProp;
95
- // ---
96
85
  const closeOnCurtainClick = isFickle &&
97
86
  ((e) => {
98
87
  if (e.target === e.currentTarget) {
@@ -118,6 +107,13 @@ const AbstractModal = (props) => {
118
107
  return () => removeFromModalStack(privateDomId);
119
108
  }, [] // eslint-disable-line react-hooks/exhaustive-deps
120
109
  );
110
+ (0, react_1.useEffect)(() => {
111
+ if (openProp === open) {
112
+ return;
113
+ }
114
+ openProp ? openModal() : closeModal();
115
+ }, [openProp] // eslint-disable-line react-hooks/exhaustive-deps
116
+ );
121
117
  const PortalOrFragment = props.portal !== false ? _Portal_js_1.Portal : react_1.Fragment;
122
118
  const closeButtonLabel = txt.closeButtonLabel || txt.closeButton;
123
119
  const { onClick, className } = wrapperProps;
@@ -132,7 +128,7 @@ const AbstractModal = (props) => {
132
128
  render
133
129
  ? render({ closeModal })
134
130
  : typeof children === 'function'
135
- ? children({ closeModal, visible })
131
+ ? children({ closeModal })
136
132
  : children,
137
133
  isBrowser && !props.noCloseButton && (react_1.default.createElement("button", { className: `${bem}__closebutton`, type: "button", onClick: closeModal, "aria-label": closeButtonLabel, "aria-controls": domid, title: closeButtonLabel }, txt.closeButton))),
138
134
  isBrowser && react_1.default.createElement(FocusTrap_js_1.FocusTrap, null)))));
package/esm/Modal.js CHANGED
@@ -2,15 +2,18 @@ import React from 'react';
2
2
  import { AbstractModal, defaultAbstractModalTexts, } from './_abstract/_AbstractModal.js';
3
3
  export const defaultModalTexts = defaultAbstractModalTexts;
4
4
  export const Modal = (props) => {
5
- const { bling, render, children } = props;
6
- return (React.createElement(AbstractModal, Object.assign({}, props, { bem: "Modal", render: (renderProps) => {
7
- const _children = render ? render(renderProps) : children;
8
- return bling ? (React.createElement(React.Fragment, null,
9
- _children,
10
- React.createElement("div", { className: "Modal__blings" },
11
- React.createElement("div", { className: "Modal__blings__inner" }, bling)))) : (_children);
12
- },
13
- // Required since props might contain children
14
- children: undefined })));
5
+ const { bling, render, // eslint-disable-line deprecation/deprecation
6
+ children, } = props;
7
+ return (React.createElement(AbstractModal, Object.assign({}, props, { bem: "Modal", render: undefined }), (renderProps) => {
8
+ const _children = render
9
+ ? render(renderProps)
10
+ : typeof children === 'function'
11
+ ? children(renderProps)
12
+ : children;
13
+ return bling ? (React.createElement(React.Fragment, null,
14
+ _children,
15
+ React.createElement("div", { className: "Modal__blings" },
16
+ React.createElement("div", { className: "Modal__blings__inner" }, bling)))) : (_children);
17
+ }));
15
18
  };
16
19
  export default Modal;
@@ -148,6 +148,7 @@ export const Multiselect = (props) => {
148
148
  }
149
149
  else if (e.key === 'Escape') {
150
150
  e.preventDefault();
151
+ e.stopPropagation();
151
152
  inputElm.blur();
152
153
  inputElm.focus();
153
154
  toggleOpen(false);
@@ -98,12 +98,6 @@ export type AbstractModalProps = {
98
98
  children: ReactNode | ((props: {
99
99
  /** Action dispacher that initiates modal closing action */
100
100
  closeModal(): void;
101
- /**
102
- * Whether the modal is visible or not. The `onOpen` and `onClosed`
103
- * callbacks are triggered once the modal has become fully visible or
104
- * fully hidden.
105
- */
106
- visible: boolean;
107
101
  }) => ReactNode);
108
102
  }> & WrapperElmProps<'div', 'hidden' | 'role'> & SSRSupportProps;
109
103
  type AbstractModalProps_private = AbstractModalProps & BemProps<true>;
@@ -78,17 +78,6 @@ export const AbstractModal = (props) => {
78
78
  }, closeDelay);
79
79
  }
80
80
  };
81
- // ---
82
- // Update open state when props.open changes. Icky but simple.
83
- const lastPropsOpen = useRef(openProp);
84
- if (openProp !== lastPropsOpen.current && openProp !== open) {
85
- lastPropsOpen.current = openProp;
86
- // these update state during render, which aborts the current render
87
- // and triggers an immediate rerender.
88
- openProp ? openModal() : closeModal();
89
- }
90
- lastPropsOpen.current = openProp;
91
- // ---
92
81
  const closeOnCurtainClick = isFickle &&
93
82
  ((e) => {
94
83
  if (e.target === e.currentTarget) {
@@ -114,6 +103,13 @@ export const AbstractModal = (props) => {
114
103
  return () => removeFromModalStack(privateDomId);
115
104
  }, [] // eslint-disable-line react-hooks/exhaustive-deps
116
105
  );
106
+ useEffect(() => {
107
+ if (openProp === open) {
108
+ return;
109
+ }
110
+ openProp ? openModal() : closeModal();
111
+ }, [openProp] // eslint-disable-line react-hooks/exhaustive-deps
112
+ );
117
113
  const PortalOrFragment = props.portal !== false ? Portal : Fragment;
118
114
  const closeButtonLabel = txt.closeButtonLabel || txt.closeButton;
119
115
  const { onClick, className } = wrapperProps;
@@ -128,7 +124,7 @@ export const AbstractModal = (props) => {
128
124
  render
129
125
  ? render({ closeModal })
130
126
  : typeof children === 'function'
131
- ? children({ closeModal, visible })
127
+ ? children({ closeModal })
132
128
  : children,
133
129
  isBrowser && !props.noCloseButton && (React.createElement("button", { className: `${bem}__closebutton`, type: "button", onClick: closeModal, "aria-label": closeButtonLabel, "aria-controls": domid, title: closeButtonLabel }, txt.closeButton))),
134
130
  isBrowser && React.createElement(FocusTrap, null)))));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reykjavik/hanna-react",
3
- "version": "0.10.167",
3
+ "version": "0.10.169",
4
4
  "author": "Reykjavík (http://www.reykjavik.is)",
5
5
  "contributors": [
6
6
  "Hugsmiðjan ehf (http://www.hugsmidjan.is)",