react-native-nitro-modal 0.1.4 → 0.2.0

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.
Files changed (61) hide show
  1. package/README.md +19 -7
  2. package/android/src/main/java/com/margelo/nitro/nitromodal/BottomSheetPresenter.kt +72 -21
  3. package/android/src/main/java/com/margelo/nitro/nitromodal/HybridNitroModal.kt +17 -3
  4. package/android/src/main/java/com/margelo/nitro/nitromodal/ModalPresenter.kt +121 -12
  5. package/android/src/main/java/com/margelo/nitro/nitromodal/PopupPresenter.kt +92 -31
  6. package/lib/module/NitroModal.js +1 -2
  7. package/lib/module/NitroModal.js.map +1 -1
  8. package/lib/module/NitroModalView.js +4 -3
  9. package/lib/module/NitroModalView.js.map +1 -1
  10. package/lib/module/NitroModalView.native.js +3 -0
  11. package/lib/module/NitroModalView.native.js.map +1 -1
  12. package/lib/module/NitroModalView.web.js +129 -0
  13. package/lib/module/NitroModalView.web.js.map +1 -0
  14. package/lib/module/web/AnimatedValue.js +123 -0
  15. package/lib/module/web/AnimatedValue.js.map +1 -0
  16. package/lib/module/web/ModalConfig.js +26 -0
  17. package/lib/module/web/ModalConfig.js.map +1 -0
  18. package/lib/module/web/ModalController.js +279 -0
  19. package/lib/module/web/ModalController.js.map +1 -0
  20. package/lib/module/web/ModalPresenter.js +245 -0
  21. package/lib/module/web/ModalPresenter.js.map +1 -0
  22. package/lib/module/web/PopupPresenter.js +117 -0
  23. package/lib/module/web/PopupPresenter.js.map +1 -0
  24. package/lib/module/web/SheetPresenter.js +469 -0
  25. package/lib/module/web/SheetPresenter.js.map +1 -0
  26. package/lib/module/web/dom.js +125 -0
  27. package/lib/module/web/dom.js.map +1 -0
  28. package/lib/typescript/src/NitroModal.d.ts +2 -2
  29. package/lib/typescript/src/NitroModal.d.ts.map +1 -1
  30. package/lib/typescript/src/NitroModalView.d.ts +2 -1
  31. package/lib/typescript/src/NitroModalView.d.ts.map +1 -1
  32. package/lib/typescript/src/NitroModalView.native.d.ts +1 -0
  33. package/lib/typescript/src/NitroModalView.native.d.ts.map +1 -1
  34. package/lib/typescript/src/NitroModalView.web.d.ts +6 -0
  35. package/lib/typescript/src/NitroModalView.web.d.ts.map +1 -0
  36. package/lib/typescript/src/web/AnimatedValue.d.ts +48 -0
  37. package/lib/typescript/src/web/AnimatedValue.d.ts.map +1 -0
  38. package/lib/typescript/src/web/ModalConfig.d.ts +24 -0
  39. package/lib/typescript/src/web/ModalConfig.d.ts.map +1 -0
  40. package/lib/typescript/src/web/ModalController.d.ts +65 -0
  41. package/lib/typescript/src/web/ModalController.d.ts.map +1 -0
  42. package/lib/typescript/src/web/ModalPresenter.d.ts +98 -0
  43. package/lib/typescript/src/web/ModalPresenter.d.ts.map +1 -0
  44. package/lib/typescript/src/web/PopupPresenter.d.ts +20 -0
  45. package/lib/typescript/src/web/PopupPresenter.d.ts.map +1 -0
  46. package/lib/typescript/src/web/SheetPresenter.d.ts +65 -0
  47. package/lib/typescript/src/web/SheetPresenter.d.ts.map +1 -0
  48. package/lib/typescript/src/web/dom.d.ts +40 -0
  49. package/lib/typescript/src/web/dom.d.ts.map +1 -0
  50. package/package.json +3 -2
  51. package/src/NitroModal.tsx +3 -4
  52. package/src/NitroModalView.native.tsx +4 -0
  53. package/src/NitroModalView.tsx +9 -4
  54. package/src/NitroModalView.web.tsx +138 -0
  55. package/src/web/AnimatedValue.ts +189 -0
  56. package/src/web/ModalConfig.ts +57 -0
  57. package/src/web/ModalController.ts +305 -0
  58. package/src/web/ModalPresenter.ts +298 -0
  59. package/src/web/PopupPresenter.ts +129 -0
  60. package/src/web/SheetPresenter.ts +582 -0
  61. package/src/web/dom.ts +154 -0
@@ -0,0 +1,279 @@
1
+ "use strict";
2
+
3
+ /// <reference lib="dom" />
4
+
5
+ import { canUseDOM, estimateViewport, readSafeAreaInsets } from "./dom.js";
6
+ import { isSameConfig, makeConfig } from "./ModalConfig.js";
7
+ import { popupContentArea, PopupPresenter } from "./PopupPresenter.js";
8
+ import { sheetContentArea, SheetPresenter } from "./SheetPresenter.js";
9
+ /**
10
+ * The web counterpart of the native `HybridNitroModal`: receives the host
11
+ * view's props and drives one presenter per presentation.
12
+ *
13
+ * Presentation is a small state machine so that rapid `isOpen` toggles, user
14
+ * dismissals and unmounts always settle on a consistent state, and every
15
+ * accepted present request ends with exactly one `onDismiss`. Transitions are
16
+ * interrupted rather than queued: closing mid-enter turns the enter around,
17
+ * and opening mid-exit turns the exit around (that session carries on, so it
18
+ * gets no `onDismiss` and at most one `onPresent`).
19
+ */
20
+ export class ModalController {
21
+ methods = {
22
+ present: () => this.requestPresent(),
23
+ dismiss: () => this.requestDismiss('programmatic'),
24
+ snapToDetent: index => {
25
+ if (Number.isFinite(index)) {
26
+ this.presenter?.snapToDetent(Math.trunc(index));
27
+ }
28
+ }
29
+ };
30
+ props = null;
31
+ config = null;
32
+ isOpen = false;
33
+ phase = 'idle';
34
+ /** What React (or an imperative call) asked for. */
35
+ wantsOpen = false;
36
+ /** A present request was accepted and its `onDismiss` is still owed. */
37
+ sessionActive = false;
38
+ /** `onPresent` was sent for the current session. */
39
+ presentSent = false;
40
+ dismissReason = 'programmatic';
41
+ reconcileScheduled = false;
42
+ isAttached = false;
43
+ presenter = null;
44
+ elements = null;
45
+ lastContentArea = null;
46
+
47
+ /** `setMounted` renders (or removes) the modal's elements. */
48
+ constructor(setMounted) {
49
+ this.setMounted = setMounted;
50
+ }
51
+
52
+ // Host view lifecycle
53
+
54
+ attach() {
55
+ this.isAttached = true;
56
+ window.addEventListener('resize', this.handleWindowResize);
57
+ }
58
+
59
+ /** The host view unmounted. Also runs for React's strict-mode remount, so `attach` must work again. */
60
+ detach() {
61
+ this.isAttached = false;
62
+ window.removeEventListener('resize', this.handleWindowResize);
63
+ this.presenter?.destroy();
64
+ this.presenter = null;
65
+ this.phase = 'idle';
66
+ this.wantsOpen = false;
67
+ this.sessionActive = false;
68
+ this.presentSent = false;
69
+ this.dismissReason = 'programmatic';
70
+ // Applied again from the props on the next `update`.
71
+ this.isOpen = false;
72
+ this.config = null;
73
+ this.lastContentArea = null;
74
+ this.setMounted(false);
75
+ }
76
+
77
+ /** The host view rendered with new props. */
78
+ update(props) {
79
+ this.props = props;
80
+ const next = makeConfig(props);
81
+ if (!this.config || !isSameConfig(next, this.config)) {
82
+ this.config = next;
83
+ if (this.presenter) {
84
+ this.presenter.update(next);
85
+ } else {
86
+ this.reportEstimatedContentArea();
87
+ }
88
+ }
89
+ if (props.isOpen !== this.isOpen) {
90
+ this.isOpen = props.isOpen;
91
+ if (props.isOpen) {
92
+ this.requestPresent();
93
+ } else {
94
+ this.requestDismiss('programmatic');
95
+ }
96
+ }
97
+ }
98
+ elementsDidMount(elements) {
99
+ this.elements = elements;
100
+ if (this.presenter && !this.presenter.isShown) {
101
+ this.presenter.show(elements);
102
+ }
103
+ }
104
+ elementsWillUnmount() {
105
+ this.elements = null;
106
+ }
107
+ contentDidRender() {
108
+ this.presenter?.contentDidRender();
109
+ }
110
+
111
+ /** Escape key, routed by React Native Web's `Modal` to the topmost modal. */
112
+ handleBackPress = () => {
113
+ // Already on its way out: the press is spent.
114
+ if (!this.isVisible) return;
115
+ this.props?.onBackButtonPress?.();
116
+ if (!this.config?.dismissOnBackButton) return;
117
+ this.requestDismiss('backButton');
118
+ this.reconcile();
119
+ };
120
+
121
+ // State machine
122
+
123
+ get isVisible() {
124
+ return this.phase === 'presenting' || this.phase === 'presented';
125
+ }
126
+ requestPresent() {
127
+ if (!this.isAttached) return;
128
+ this.wantsOpen = true;
129
+ this.sessionActive = true;
130
+ this.scheduleReconcile();
131
+ }
132
+ requestDismiss(reason) {
133
+ if (this.wantsOpen) {
134
+ this.dismissReason = reason;
135
+ }
136
+ this.wantsOpen = false;
137
+ this.scheduleReconcile();
138
+ }
139
+
140
+ /** Coalesces the requests made in one task, e.g. an open and a close in the same handler. */
141
+ scheduleReconcile() {
142
+ if (this.reconcileScheduled) return;
143
+ this.reconcileScheduled = true;
144
+ queueMicrotask(() => {
145
+ this.reconcileScheduled = false;
146
+ if (this.isAttached) {
147
+ this.reconcile();
148
+ }
149
+ });
150
+ }
151
+ reconcile() {
152
+ switch (this.phase) {
153
+ case 'presenting':
154
+ case 'presented':
155
+ if (!this.wantsOpen) {
156
+ this.startDismiss();
157
+ }
158
+ break;
159
+ case 'dismissing':
160
+ if (this.wantsOpen && this.presenter?.cancelDismiss()) {
161
+ this.phase = 'presenting';
162
+ this.dismissReason = 'programmatic';
163
+ }
164
+ break;
165
+ case 'idle':
166
+ if (this.wantsOpen) {
167
+ this.startPresent();
168
+ } else if (this.sessionActive) {
169
+ this.endSession(); // Cancelled before anything appeared.
170
+ }
171
+ break;
172
+ }
173
+ }
174
+ startPresent() {
175
+ const {
176
+ config
177
+ } = this;
178
+ if (!canUseDOM || !config) return;
179
+ const presenter = config.mode === 'popup' ? new PopupPresenter(config, this) : new SheetPresenter(config, this);
180
+ this.presenter = presenter;
181
+ this.phase = 'presenting';
182
+ this.setMounted(true);
183
+ // Still mounted from a presentation that just ended.
184
+ if (this.elements) {
185
+ presenter.show(this.elements);
186
+ }
187
+ }
188
+ startDismiss() {
189
+ const {
190
+ presenter
191
+ } = this;
192
+ if (!presenter?.isShown) {
193
+ // Its elements never mounted.
194
+ presenter?.destroy();
195
+ this.finishDismiss();
196
+ return;
197
+ }
198
+ this.phase = 'dismissing';
199
+ presenter.dismiss();
200
+ }
201
+ finishDismiss() {
202
+ this.phase = 'idle';
203
+ this.presenter = null;
204
+ this.setMounted(false);
205
+ this.endSession();
206
+ this.reconcile();
207
+ }
208
+ endSession() {
209
+ if (!this.sessionActive) return;
210
+ const reason = this.dismissReason;
211
+ this.dismissReason = 'programmatic';
212
+ this.presentSent = false;
213
+ // A present requested while we were dismissing starts a new session.
214
+ this.sessionActive = this.wantsOpen;
215
+ if (this.isAttached) {
216
+ this.props?.onDismiss?.(reason);
217
+ }
218
+ }
219
+
220
+ // PresenterListener
221
+
222
+ onPresented() {
223
+ if (this.phase !== 'presenting') return;
224
+ this.phase = 'presented';
225
+ if (!this.presentSent) {
226
+ this.presentSent = true;
227
+ this.props?.onPresent?.();
228
+ }
229
+ this.reconcile();
230
+ }
231
+ onDismissed() {
232
+ this.finishDismiss();
233
+ }
234
+ onBackdropPress() {
235
+ this.props?.onBackdropPress?.();
236
+ if (!this.config?.dismissOnBackdropPress || !this.isVisible) return;
237
+ this.requestDismiss('backdrop');
238
+ this.reconcile();
239
+ }
240
+ onSwipeDismiss() {
241
+ if (!this.config?.dismissOnSwipe || !this.isVisible) return;
242
+ this.requestDismiss('swipe');
243
+ this.reconcile();
244
+ }
245
+ onDetentChange(index) {
246
+ this.props?.onDetentChange?.(index);
247
+ }
248
+ onContentAreaChange(width, height) {
249
+ const area = {
250
+ width: Math.floor(width),
251
+ height: Math.floor(height)
252
+ };
253
+ const last = this.lastContentArea;
254
+ if (area.width <= 0 || area.height <= 0) return;
255
+ if (last?.width === area.width && last.height === area.height) return;
256
+ this.lastContentArea = area;
257
+ this.props?.onContentAreaChange?.(area);
258
+ }
259
+
260
+ // Helpers
261
+
262
+ /** Lets React size the content before the first presentation. */
263
+ reportEstimatedContentArea() {
264
+ const {
265
+ config
266
+ } = this;
267
+ if (!canUseDOM || !this.isAttached || !config) return;
268
+ const viewport = estimateViewport();
269
+ const insets = readSafeAreaInsets();
270
+ const area = config.mode === 'popup' ? popupContentArea(viewport, insets) : sheetContentArea(config.detents, viewport, insets);
271
+ this.onContentAreaChange(area.width, area.height);
272
+ }
273
+ handleWindowResize = () => {
274
+ if (!this.presenter) {
275
+ this.reportEstimatedContentArea();
276
+ }
277
+ };
278
+ }
279
+ //# sourceMappingURL=ModalController.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["canUseDOM","estimateViewport","readSafeAreaInsets","isSameConfig","makeConfig","popupContentArea","PopupPresenter","sheetContentArea","SheetPresenter","ModalController","methods","present","requestPresent","dismiss","requestDismiss","snapToDetent","index","Number","isFinite","presenter","Math","trunc","props","config","isOpen","phase","wantsOpen","sessionActive","presentSent","dismissReason","reconcileScheduled","isAttached","elements","lastContentArea","constructor","setMounted","attach","window","addEventListener","handleWindowResize","detach","removeEventListener","destroy","update","next","reportEstimatedContentArea","elementsDidMount","isShown","show","elementsWillUnmount","contentDidRender","handleBackPress","isVisible","onBackButtonPress","dismissOnBackButton","reconcile","scheduleReconcile","reason","queueMicrotask","startDismiss","cancelDismiss","startPresent","endSession","mode","finishDismiss","onDismiss","onPresented","onPresent","onDismissed","onBackdropPress","dismissOnBackdropPress","onSwipeDismiss","dismissOnSwipe","onDetentChange","onContentAreaChange","width","height","area","floor","last","viewport","insets","detents"],"sourceRoot":"../../../src","sources":["web/ModalController.ts"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,245 @@
1
+ "use strict";
2
+
3
+ /// <reference lib="dom" />
4
+
5
+ import { canScrollWithin, clipOverflow, readKeyboardHeight, readSafeAreaInsets, toCSSColor, ZERO_INSETS } from "./dom.js";
6
+
7
+ /** DOM rendered by the web host view for one presentation. */
8
+
9
+ /**
10
+ * Owns one presentation from show to dismiss. Subclasses lay out the surface
11
+ * and drive the enter/exit animations; this class tracks the viewport, the
12
+ * on-screen keyboard and the content size, and handles the backdrop and
13
+ * teardown.
14
+ *
15
+ * Transitions are interruptible: dismissing mid-enter turns the enter around,
16
+ * and `cancelDismiss` turns an exit back around. Once an exit starts the modal
17
+ * stops taking input.
18
+ */
19
+ export class ModalPresenter {
20
+ elements = null;
21
+ viewport = {
22
+ width: 0,
23
+ height: 0
24
+ };
25
+ insets = ZERO_INSETS;
26
+ /** Part of the viewport covered by the on-screen keyboard, from the bottom. */
27
+ keyboardHeight = 0;
28
+ /** Measured size of the React content. */
29
+ contentSize = {
30
+ width: 0,
31
+ height: 0
32
+ };
33
+ entering = false;
34
+ isDismissing = false;
35
+ finished = false;
36
+ observedContent = null;
37
+ resizeObserver = null;
38
+ constructor(config, listener) {
39
+ this.config = config;
40
+ this.listener = listener;
41
+ }
42
+ get isShown() {
43
+ return this.elements != null;
44
+ }
45
+
46
+ /** Takes over the rendered elements and starts the enter transition. */
47
+ show(elements) {
48
+ if (this.elements || this.finished) return;
49
+ this.elements = elements;
50
+ // The elements can outlive a previous presentation (an immediate re-present).
51
+ elements.overlay.style.pointerEvents = '';
52
+ elements.surface.style.overflow = clipOverflow();
53
+ elements.surface.style.opacity = '';
54
+ elements.surface.style.transform = '';
55
+ window.addEventListener('resize', this.handleResize);
56
+ window.visualViewport?.addEventListener('resize', this.handleKeyboard);
57
+ window.visualViewport?.addEventListener('scroll', this.handleKeyboard);
58
+ elements.backdrop.addEventListener('click', this.handleBackdropClick);
59
+ elements.overlay.addEventListener('wheel', this.handleWheel, {
60
+ passive: false
61
+ });
62
+ if (typeof ResizeObserver !== 'undefined') {
63
+ this.resizeObserver = new ResizeObserver(this.handleContentResize);
64
+ }
65
+ this.measureViewport();
66
+ this.observeContent();
67
+ this.applyBackdrop();
68
+ this.didShow();
69
+ this.entering = true;
70
+ this.animateIn();
71
+ }
72
+ update(config) {
73
+ const previous = this.config;
74
+ this.config = config;
75
+ if (!this.elements) return;
76
+ this.applyBackdrop();
77
+ this.configDidChange(previous);
78
+ }
79
+
80
+ /** The React content re-rendered; its root element may have changed. */
81
+ contentDidRender() {
82
+ if (this.elements && this.observeContent()) {
83
+ this.contentSizeDidChange();
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Animated dismissal, also from mid-enter. `onDismissed` follows when it
89
+ * completes, unless `cancelDismiss` turns it around first.
90
+ */
91
+ dismiss() {
92
+ const elements = this.elements;
93
+ if (!elements || this.isDismissing || this.finished) return;
94
+ this.isDismissing = true;
95
+ this.entering = false;
96
+ elements.overlay.style.pointerEvents = 'none';
97
+ // Also hides the on-screen keyboard.
98
+ const focused = document.activeElement;
99
+ if (focused instanceof HTMLElement && elements.overlay.contains(focused)) {
100
+ focused.blur();
101
+ }
102
+ this.animateOut(() => this.finish());
103
+ }
104
+
105
+ /**
106
+ * Reverses a running exit from wherever it is. `onPresented` follows once
107
+ * it settles. Returns false when there is no exit to reverse.
108
+ */
109
+ cancelDismiss() {
110
+ if (!this.elements || !this.isDismissing || this.finished) return false;
111
+ this.isDismissing = false;
112
+ this.elements.overlay.style.pointerEvents = '';
113
+ this.entering = true;
114
+ this.animateIn();
115
+ return true;
116
+ }
117
+
118
+ /** Tears down without animating or notifying the listener. */
119
+ destroy() {
120
+ if (this.finished) return;
121
+ this.finished = true;
122
+ this.teardown();
123
+ }
124
+ snapToDetent(_index) {}
125
+
126
+ /** The elements are in place; lay out the hidden state. */
127
+
128
+ /** Animates from the current state to presented, then calls `notifyPresented`. */
129
+
130
+ /** Animates from the current state (possibly mid-enter) to gone, then calls `onEnd`. */
131
+
132
+ /** The viewport size, safe area or keyboard height changed. */
133
+
134
+ /** For subclasses: the running enter animation settled. */
135
+ notifyPresented() {
136
+ if (!this.entering || this.isDismissing || this.finished) return;
137
+ this.entering = false;
138
+ this.listener.onPresented();
139
+ }
140
+ reportContentArea(width, height) {
141
+ if (width > 0 && height > 0) {
142
+ this.listener.onContentAreaChange(width, height);
143
+ }
144
+ }
145
+ teardown() {
146
+ this.stopAnimations();
147
+ window.removeEventListener('resize', this.handleResize);
148
+ window.visualViewport?.removeEventListener('resize', this.handleKeyboard);
149
+ window.visualViewport?.removeEventListener('scroll', this.handleKeyboard);
150
+ this.elements?.backdrop.removeEventListener('click', this.handleBackdropClick);
151
+ this.elements?.overlay.removeEventListener('wheel', this.handleWheel);
152
+ this.resizeObserver?.disconnect();
153
+ this.resizeObserver = null;
154
+ this.observedContent = null;
155
+ }
156
+ finish() {
157
+ if (this.finished) return;
158
+ this.finished = true;
159
+ this.teardown();
160
+ this.listener.onDismissed();
161
+ }
162
+ applyBackdrop() {
163
+ const {
164
+ backdrop,
165
+ surface
166
+ } = this.elements;
167
+ const {
168
+ config
169
+ } = this;
170
+ backdrop.style.backgroundColor = toCSSColor(config.backdropColor, config.backdropOpacity);
171
+ const blur = config.backdropBlurRadius > 0 ? `blur(${config.backdropBlurRadius}px)` : '';
172
+ backdrop.style.setProperty('backdrop-filter', blur);
173
+ backdrop.style.setProperty('-webkit-backdrop-filter', blur);
174
+ // `Canvas` follows the page's `color-scheme`, like a system background.
175
+ surface.style.backgroundColor = config.contentBackgroundColor != null ? toCSSColor(config.contentBackgroundColor) : 'Canvas';
176
+ }
177
+ measureViewport() {
178
+ const {
179
+ overlay
180
+ } = this.elements;
181
+ this.viewport = {
182
+ width: overlay.clientWidth,
183
+ height: overlay.clientHeight
184
+ };
185
+ this.insets = readSafeAreaInsets();
186
+ this.keyboardHeight = readKeyboardHeight(this.viewport.height);
187
+ }
188
+
189
+ /** Re-targets the observer at the content's root element. Returns whether its size changed. */
190
+ observeContent() {
191
+ const root = this.elements?.content.firstElementChild;
192
+ const element = root instanceof HTMLElement ? root : null;
193
+ if (element !== this.observedContent) {
194
+ if (this.observedContent) {
195
+ this.resizeObserver?.unobserve(this.observedContent);
196
+ }
197
+ this.observedContent = element;
198
+ if (element) {
199
+ this.resizeObserver?.observe(element);
200
+ }
201
+ }
202
+ return this.measureContent();
203
+ }
204
+ measureContent() {
205
+ // Layout sizes, unaffected by the transition's transforms.
206
+ const width = this.observedContent?.offsetWidth ?? 0;
207
+ const height = this.observedContent?.offsetHeight ?? 0;
208
+ if (width === this.contentSize.width && height === this.contentSize.height) return false;
209
+ this.contentSize = {
210
+ width,
211
+ height
212
+ };
213
+ return true;
214
+ }
215
+ handleContentResize = () => {
216
+ if (this.measureContent()) {
217
+ this.contentSizeDidChange();
218
+ }
219
+ };
220
+ handleResize = () => {
221
+ if (!this.elements) return;
222
+ this.measureViewport();
223
+ this.viewportDidChange();
224
+ };
225
+ handleKeyboard = () => {
226
+ const height = readKeyboardHeight(this.viewport.height);
227
+ if (height === this.keyboardHeight) return;
228
+ this.keyboardHeight = height;
229
+ this.viewportDidChange();
230
+ };
231
+ handleBackdropClick = () => {
232
+ if (!this.isDismissing && !this.finished) {
233
+ this.listener.onBackdropPress();
234
+ }
235
+ };
236
+
237
+ /** Keeps the page behind the modal from scrolling; scrollable content still scrolls. */
238
+ handleWheel = event => {
239
+ if (event.ctrlKey || Math.abs(event.deltaX) > Math.abs(event.deltaY)) return;
240
+ if (!canScrollWithin(event.target, this.elements.overlay, event.deltaY)) {
241
+ event.preventDefault();
242
+ }
243
+ };
244
+ }
245
+ //# sourceMappingURL=ModalPresenter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["canScrollWithin","clipOverflow","readKeyboardHeight","readSafeAreaInsets","toCSSColor","ZERO_INSETS","ModalPresenter","elements","viewport","width","height","insets","keyboardHeight","contentSize","entering","isDismissing","finished","observedContent","resizeObserver","constructor","config","listener","isShown","show","overlay","style","pointerEvents","surface","overflow","opacity","transform","window","addEventListener","handleResize","visualViewport","handleKeyboard","backdrop","handleBackdropClick","handleWheel","passive","ResizeObserver","handleContentResize","measureViewport","observeContent","applyBackdrop","didShow","animateIn","update","previous","configDidChange","contentDidRender","contentSizeDidChange","dismiss","focused","document","activeElement","HTMLElement","contains","blur","animateOut","finish","cancelDismiss","destroy","teardown","snapToDetent","_index","notifyPresented","onPresented","reportContentArea","onContentAreaChange","stopAnimations","removeEventListener","disconnect","onDismissed","backgroundColor","backdropColor","backdropOpacity","backdropBlurRadius","setProperty","contentBackgroundColor","clientWidth","clientHeight","root","content","firstElementChild","element","unobserve","observe","measureContent","offsetWidth","offsetHeight","viewportDidChange","onBackdropPress","event","ctrlKey","Math","abs","deltaX","deltaY","target","preventDefault"],"sourceRoot":"../../../src","sources":["web/ModalPresenter.ts"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+
3
+ /// <reference lib="dom" />
4
+
5
+ import { AnimatedValue, INSTANT } from "./AnimatedValue.js";
6
+ import { prefersReducedMotion } from "./dom.js";
7
+ import { ModalPresenter } from "./ModalPresenter.js";
8
+ const MARGIN = 16;
9
+ const DEFAULT_CORNER_RADIUS = 16;
10
+ const ENTER_SCALE = 0.9;
11
+ const ENTER = {
12
+ type: 'spring',
13
+ response: 0.3,
14
+ dampingRatio: 0.82
15
+ };
16
+ const EXIT = {
17
+ type: 'timing',
18
+ duration: 200,
19
+ easing: t => t * t
20
+ };
21
+ /** Area a card may occupy, clear of the safe area and the part of the keyboard above it. */
22
+ function availableRect(viewport, insets, keyboardHeight) {
23
+ const covered = Math.max(0, keyboardHeight - insets.bottom);
24
+ return {
25
+ x: insets.left + MARGIN,
26
+ y: insets.top + MARGIN,
27
+ width: Math.max(0, viewport.width - insets.left - insets.right - 2 * MARGIN),
28
+ height: Math.max(0, viewport.height - insets.top - insets.bottom - 2 * MARGIN - covered)
29
+ };
30
+ }
31
+
32
+ /** Best guess of the content area before the popup is on screen. */
33
+ export function popupContentArea(viewport, insets) {
34
+ const {
35
+ width,
36
+ height
37
+ } = availableRect(viewport, insets, 0);
38
+ return {
39
+ width,
40
+ height
41
+ };
42
+ }
43
+
44
+ /** `popup` mode: a centered card over the backdrop with a fade/scale transition. */
45
+ export class PopupPresenter extends ModalPresenter {
46
+ /** 0 hidden, 1 presented. */
47
+ progress = new AnimatedValue(0, 0.001, value => this.render(value));
48
+ didShow() {
49
+ this.layout();
50
+ this.progress.set(0);
51
+ }
52
+ animateIn() {
53
+ this.progress.animateTo(1, this.isAnimated ? ENTER : INSTANT, {
54
+ onEnd: () => this.notifyPresented()
55
+ });
56
+ }
57
+ animateOut(onEnd) {
58
+ this.progress.animateTo(0, this.isAnimated ? EXIT : INSTANT, {
59
+ onEnd
60
+ });
61
+ }
62
+ configDidChange() {
63
+ this.layout();
64
+ this.render(this.progress.value);
65
+ }
66
+ viewportDidChange() {
67
+ this.layout();
68
+ }
69
+ contentSizeDidChange() {
70
+ this.layout();
71
+ }
72
+ stopAnimations() {
73
+ this.progress.stop();
74
+ }
75
+ get isAnimated() {
76
+ return this.config.popupAnimation !== 'none' && !prefersReducedMotion();
77
+ }
78
+ layout() {
79
+ const {
80
+ surface,
81
+ content,
82
+ grabber
83
+ } = this.elements;
84
+ const behavior = this.config.keyboardBehavior;
85
+ const available = availableRect(this.viewport, this.insets, behavior === 'none' ? 0 : this.keyboardHeight);
86
+ const width = Math.min(this.contentSize.width, available.width);
87
+ const height = Math.min(this.contentSize.height, available.height);
88
+ Object.assign(surface.style, {
89
+ left: `${available.x + (available.width - width) / 2}px`,
90
+ top: `${available.y + (available.height - height) / 2}px`,
91
+ width: `${width}px`,
92
+ height: `${height}px`,
93
+ borderRadius: `${this.config.cornerRadius ?? DEFAULT_CORNER_RADIUS}px`
94
+ });
95
+ grabber.style.display = 'none';
96
+
97
+ // The content host also bounds the content's own (shrink-to-fit) width.
98
+ const area = behavior === 'resize' ? available : availableRect(this.viewport, this.insets, 0);
99
+ content.style.width = `${area.width}px`;
100
+ content.style.height = `${area.height}px`;
101
+ this.reportContentArea(area.width, area.height);
102
+ }
103
+ render(progress) {
104
+ if (!this.elements) return;
105
+ const {
106
+ surface,
107
+ backdrop
108
+ } = this.elements;
109
+ const visible = Math.min(Math.max(progress, 0), 1);
110
+ const animation = this.config.popupAnimation;
111
+ backdrop.style.opacity = String(visible);
112
+ surface.style.opacity = animation === 'none' ? '1' : String(visible);
113
+ const scale = animation === 'scale' ? ENTER_SCALE + (1 - ENTER_SCALE) * progress : 1;
114
+ surface.style.transform = `scale(${scale})`;
115
+ }
116
+ }
117
+ //# sourceMappingURL=PopupPresenter.js.map