@orangesk/orange-design-system 2.0.0-beta.25 → 2.0.0-beta.27

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.
@@ -134,7 +134,21 @@ export default class Modal {
134
134
  }
135
135
 
136
136
  destroy(): void {
137
- this.instance.destroy();
137
+ if (this.instance) {
138
+ // Don't call this.instance.destroy() because a11y-dialog's destroy
139
+ // does replaceWith(cloneNode(true)) which breaks React!
140
+ // Instead, just hide and let the instance be garbage collected
141
+ this.instance.hide();
142
+ // Cast to any to access private methods for cleanup
143
+ const dialog = this.instance as any;
144
+ document.removeEventListener("click", dialog.handleTriggerClicks, true);
145
+ document.body.removeEventListener("focus", dialog.maintainFocus, true);
146
+ this.element.removeEventListener("keydown", dialog.bindKeypress, true);
147
+
148
+ // Make sure scroll is unlocked when destroying
149
+ Modal.unlockBody();
150
+ }
151
+
138
152
  this.element.ODS_Modal = this;
139
153
  }
140
154
 
@@ -153,74 +167,48 @@ export default class Modal {
153
167
  }
154
168
  }
155
169
 
170
+ // Track if body is locked
171
+ private static isLocked = false;
172
+
156
173
  static lockBody(className?: string, root?: string): void {
174
+ if (Modal.isLocked) return;
175
+
157
176
  const actualClassName =
158
177
  className || (this as unknown as Modal).config?.classModalIsOpenBody;
159
- const actualRoot = root || (this as unknown as Modal).config?.root;
160
-
161
- const container = document.querySelector(actualRoot);
162
- // store current scrollTop value
163
- const scrollTop =
164
- document.documentElement.scrollTop || document.body.scrollTop;
165
- document.body.setAttribute("data-lock-scrolltop", scrollTop.toString());
166
-
167
- // add locking styles to body
168
- document.body.style.height = "100%";
169
- document.body.style.width = "100%";
170
- document.body.style.overflow = "hidden";
171
- document.body.style.position = "fixed";
172
178
 
173
- // add locking styles to scrollTop
174
- if (container) {
175
- (container as HTMLElement).style.height = "100%";
176
- (container as HTMLElement).style.width = "100%";
177
- (container as HTMLElement).style.overflow = "hidden";
178
- (container as HTMLElement).style.position = "fixed";
179
- // scroll page-container to scrollTop position
180
- (container as HTMLElement).scrollTop = scrollTop;
181
- }
179
+ // Store scroll position
180
+ const scrollY = window.scrollY;
181
+ document.body.setAttribute("data-lock-scrolltop", scrollY.toString());
182
+
183
+ // Lock body with position fixed and offset
184
+ document.body.style.position = "fixed";
185
+ document.body.style.top = `-${scrollY}px`;
186
+ document.body.style.left = "0";
187
+ document.body.style.right = "0";
182
188
 
183
189
  // add modal class
184
190
  document.body.classList.add(actualClassName);
185
-
186
- // attempt to scroll top fixed position
187
- window.requestAnimationFrame(() => {
188
- window.scrollTo(0, scrollTop);
189
- });
191
+ Modal.isLocked = true;
190
192
  }
191
193
 
192
194
  static unlockBody(className?: string, root?: string): void {
195
+ if (!Modal.isLocked) return;
196
+
193
197
  const actualClassName =
194
198
  className || (this as unknown as Modal).config?.classModalIsOpenBody;
195
- const actualRoot = root || (this as unknown as Modal).config?.root;
196
-
197
- const container = document.querySelector(actualRoot);
198
199
  const scrollTop = document.body.getAttribute("data-lock-scrolltop") || "0";
199
200
 
200
- // remove locking styles from body
201
- document.body.style.height = "";
202
- document.body.style.width = "";
203
- document.body.style.overflow = "";
201
+ // Remove lock styles
204
202
  document.body.style.position = "";
203
+ document.body.style.top = "";
204
+ document.body.style.left = "";
205
+ document.body.style.right = "";
205
206
 
206
- // add modal class
207
+ // remove modal class
207
208
  document.body.classList.remove(actualClassName);
208
209
 
209
- // remove locking styles from page-container
210
- if (container) {
211
- (container as HTMLElement).style.height = "";
212
- (container as HTMLElement).style.width = "";
213
- (container as HTMLElement).style.overflow = "";
214
- (container as HTMLElement).style.position = "";
215
- }
216
-
217
- // set scroll position back
218
- window.requestAnimationFrame(() => {
219
- window.scrollTo({
220
- left: 0,
221
- top: parseInt(scrollTop, 10),
222
- behavior: "instant" as ScrollBehavior,
223
- });
224
- });
210
+ // Restore scroll position
211
+ window.scrollTo(0, parseInt(scrollTop, 10));
212
+ Modal.isLocked = false;
225
213
  }
226
214
  }
@@ -7,8 +7,6 @@ import { useStatic } from "../../utils/hooks";
7
7
  import ConditionalWrapper from "../../utils/ConditionalWrapper";
8
8
  import { Button } from "../Button";
9
9
  import { Buttons } from "../Buttons";
10
-
11
- // Use JavaScript version for now, then convert to TypeScript later
12
10
  import ModalStatic from "./Modal.static";
13
11
  import { ModalCloseButton } from "./ModalCloseButton";
14
12
  import { ModalTitle } from "./ModalTitle";
@@ -31,8 +29,8 @@ interface ModalProps extends React.HTMLAttributes<HTMLDivElement> {
31
29
  id: string;
32
30
  /** isActive controls if modal is opened or closed */
33
31
  isActive?: boolean;
34
- /** Disable plugin initialization. */
35
- noInit?: boolean;
32
+ /** Callback fired when modal is closed */
33
+ onHide?: () => void;
36
34
  /** Custom header renderer. Receives id as function param. Returned element(s) must contain a header with close button. */
37
35
  renderHeader?: (id: string) => React.ReactNode;
38
36
  /** Custom body renderer. Receives title as function param. Returned element(s) must contain a title. */
@@ -61,7 +59,6 @@ const defaultProps = {
61
59
  Zatvoriť
62
60
  </Button>,
63
61
  ],
64
- noInit: false,
65
62
  };
66
63
 
67
64
  const Modal: React.FC<ModalProps> = ({
@@ -70,8 +67,8 @@ const Modal: React.FC<ModalProps> = ({
70
67
  children,
71
68
  hasStickyFooter,
72
69
  id,
73
- noInit = defaultProps.noInit,
74
70
  isActive,
71
+ onHide,
75
72
  size,
76
73
  title,
77
74
  renderHeader,
@@ -86,8 +83,26 @@ const Modal: React.FC<ModalProps> = ({
86
83
  const [modalRef, instance] = useStatic(ModalStatic);
87
84
 
88
85
  useEffect(() => {
89
- if (isActive && instance && (instance as any).current) {
90
- (instance as any).current.show();
86
+ if (!instance.current || !onHide) return;
87
+
88
+ const handleHide = () => {
89
+ onHide();
90
+ };
91
+
92
+ instance.current.instance.on("hide", handleHide);
93
+
94
+ return () => {
95
+ instance.current?.instance.off("hide", handleHide);
96
+ };
97
+ }, [instance, onHide]);
98
+
99
+ useEffect(() => {
100
+ if (!instance.current) return;
101
+
102
+ if (isActive) {
103
+ instance.current.show();
104
+ } else if (isActive === false) {
105
+ instance.current.hide();
91
106
  }
92
107
  }, [instance, isActive]);
93
108
 
@@ -113,7 +128,7 @@ const Modal: React.FC<ModalProps> = ({
113
128
  return (
114
129
  <div
115
130
  id={id}
116
- data-modal={!noInit}
131
+ data-modal
117
132
  className={CLASS_ROOT}
118
133
  ref={modalRef}
119
134
  role="dialog"
@@ -123,7 +138,6 @@ const Modal: React.FC<ModalProps> = ({
123
138
  >
124
139
  <div className="modal__overlay" tabIndex={-1} data-a11y-dialog-hide />
125
140
  <div className={dialogClasses} role="document" {...other}>
126
- {}
127
141
  <div
128
142
  className={cx(`${CLASS_ROOT}__header`, {
129
143
  [`${CLASS_ROOT}__header--no-spacing`]: disableHeaderSpacing,