@thewhateverapp/tile-sdk 0.11.0 → 0.11.1
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.
|
@@ -49,8 +49,37 @@ export declare class TileBridge {
|
|
|
49
49
|
private currentToken;
|
|
50
50
|
private tokenExpiresAt;
|
|
51
51
|
private keyboardState;
|
|
52
|
+
private baselineViewportHeight;
|
|
53
|
+
private keyboardDetectionEnabled;
|
|
54
|
+
private lastReportedKeyboardState;
|
|
55
|
+
private viewportDebounceTimer;
|
|
56
|
+
private readonly KEYBOARD_THRESHOLD;
|
|
57
|
+
private readonly DEBOUNCE_MS;
|
|
58
|
+
private inputFocused;
|
|
59
|
+
private focusedElement;
|
|
52
60
|
constructor(expectedOrigin?: string, router?: NextRouter);
|
|
53
61
|
private initialize;
|
|
62
|
+
/**
|
|
63
|
+
* Initialize keyboard detection using VisualViewport API and input focus tracking
|
|
64
|
+
* This runs inside the WebView to detect when the soft keyboard appears
|
|
65
|
+
*/
|
|
66
|
+
private initializeKeyboardDetection;
|
|
67
|
+
/**
|
|
68
|
+
* Process viewport change and determine keyboard state
|
|
69
|
+
*/
|
|
70
|
+
private processViewportChange;
|
|
71
|
+
/**
|
|
72
|
+
* Handle focusin events - input element gained focus
|
|
73
|
+
*/
|
|
74
|
+
private handleFocusIn;
|
|
75
|
+
/**
|
|
76
|
+
* Handle focusout events - input element lost focus
|
|
77
|
+
*/
|
|
78
|
+
private handleFocusOut;
|
|
79
|
+
/**
|
|
80
|
+
* Check if an element is an input-like element that would trigger keyboard
|
|
81
|
+
*/
|
|
82
|
+
private isInputElement;
|
|
54
83
|
private handleMessage;
|
|
55
84
|
private handleConfig;
|
|
56
85
|
private handleResponse;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TileBridge.d.ts","sourceRoot":"","sources":["../../src/bridge/TileBridge.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAGD,KAAK,UAAU,GAAG;IAChB,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB,CAAC;AAEF,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,gBAAgB,CAAmD;IAC3E,OAAO,CAAC,aAAa,CAAoD;IACzE,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,MAAM,CAA2B;IAGzC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,cAAc,CAAqB;IAG3C,OAAO,CAAC,aAAa,CAAgD;
|
|
1
|
+
{"version":3,"file":"TileBridge.d.ts","sourceRoot":"","sources":["../../src/bridge/TileBridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,6 +15,16 @@ export class TileBridge {
|
|
|
15
15
|
this.tokenExpiresAt = null;
|
|
16
16
|
// Keyboard state from parent (mobile app)
|
|
17
17
|
this.keyboardState = { visible: false, height: 0 };
|
|
18
|
+
// VisualViewport tracking for keyboard detection (Plan A)
|
|
19
|
+
this.baselineViewportHeight = 0;
|
|
20
|
+
this.keyboardDetectionEnabled = false;
|
|
21
|
+
this.lastReportedKeyboardState = { visible: false, height: 0 };
|
|
22
|
+
this.viewportDebounceTimer = null;
|
|
23
|
+
this.KEYBOARD_THRESHOLD = 150; // Minimum height change to consider keyboard visible
|
|
24
|
+
this.DEBOUNCE_MS = 100; // Debounce viewport events
|
|
25
|
+
// Input focus tracking (Plan B)
|
|
26
|
+
this.inputFocused = false;
|
|
27
|
+
this.focusedElement = null;
|
|
18
28
|
this.parentOrigin = expectedOrigin;
|
|
19
29
|
this.router = router || null;
|
|
20
30
|
// Auto-detect parent origin based on environment
|
|
@@ -66,6 +76,182 @@ export class TileBridge {
|
|
|
66
76
|
this.ready = true;
|
|
67
77
|
// Send ready signal to parent
|
|
68
78
|
this.sendToParent({ type: 'tile:ready' });
|
|
79
|
+
// Initialize keyboard detection (Plan A: VisualViewport + Plan B: Input Focus)
|
|
80
|
+
this.initializeKeyboardDetection();
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Initialize keyboard detection using VisualViewport API and input focus tracking
|
|
84
|
+
* This runs inside the WebView to detect when the soft keyboard appears
|
|
85
|
+
*/
|
|
86
|
+
initializeKeyboardDetection() {
|
|
87
|
+
if (typeof window === 'undefined')
|
|
88
|
+
return;
|
|
89
|
+
// Only enable in iframe context (when running in mobile WebView)
|
|
90
|
+
if (!this.isInIframe()) {
|
|
91
|
+
if (this.isDevelopment()) {
|
|
92
|
+
console.log('[TileBridge] Keyboard detection skipped - not in iframe');
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
this.keyboardDetectionEnabled = true;
|
|
97
|
+
// ==================== PLAN A: VisualViewport Detection ====================
|
|
98
|
+
// Use VisualViewport API to detect keyboard by monitoring viewport height changes
|
|
99
|
+
const vv = window.visualViewport;
|
|
100
|
+
if (vv) {
|
|
101
|
+
// Set baseline height (full viewport without keyboard)
|
|
102
|
+
this.baselineViewportHeight = vv.height;
|
|
103
|
+
if (this.isDevelopment() || this.isPreview()) {
|
|
104
|
+
console.log('[TileBridge] ⌨️ Keyboard detection initialized', {
|
|
105
|
+
baselineHeight: this.baselineViewportHeight,
|
|
106
|
+
hasVisualViewport: true,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
// Handler for viewport resize events
|
|
110
|
+
const handleViewportChange = () => {
|
|
111
|
+
if (!this.keyboardDetectionEnabled)
|
|
112
|
+
return;
|
|
113
|
+
// Debounce rapid events
|
|
114
|
+
if (this.viewportDebounceTimer) {
|
|
115
|
+
clearTimeout(this.viewportDebounceTimer);
|
|
116
|
+
}
|
|
117
|
+
this.viewportDebounceTimer = setTimeout(() => {
|
|
118
|
+
this.processViewportChange();
|
|
119
|
+
}, this.DEBOUNCE_MS);
|
|
120
|
+
};
|
|
121
|
+
// Subscribe to resize and scroll events on visualViewport
|
|
122
|
+
vv.addEventListener('resize', handleViewportChange);
|
|
123
|
+
vv.addEventListener('scroll', handleViewportChange);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
if (this.isDevelopment() || this.isPreview()) {
|
|
127
|
+
console.log('[TileBridge] ⌨️ VisualViewport not available, using focus-only detection');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// ==================== PLAN B: Input Focus Detection ====================
|
|
131
|
+
// Track when input elements are focused/blurred
|
|
132
|
+
document.addEventListener('focusin', this.handleFocusIn.bind(this));
|
|
133
|
+
document.addEventListener('focusout', this.handleFocusOut.bind(this));
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Process viewport change and determine keyboard state
|
|
137
|
+
*/
|
|
138
|
+
processViewportChange() {
|
|
139
|
+
const vv = window.visualViewport;
|
|
140
|
+
if (!vv)
|
|
141
|
+
return;
|
|
142
|
+
const currentHeight = vv.height;
|
|
143
|
+
const heightDiff = this.baselineViewportHeight - currentHeight;
|
|
144
|
+
const keyboardVisible = heightDiff > this.KEYBOARD_THRESHOLD;
|
|
145
|
+
const keyboardHeight = keyboardVisible ? Math.round(heightDiff) : 0;
|
|
146
|
+
// Only report if state changed
|
|
147
|
+
if (this.lastReportedKeyboardState.visible !== keyboardVisible ||
|
|
148
|
+
Math.abs(this.lastReportedKeyboardState.height - keyboardHeight) > 10) {
|
|
149
|
+
this.lastReportedKeyboardState = { visible: keyboardVisible, height: keyboardHeight };
|
|
150
|
+
if (this.isDevelopment() || this.isPreview()) {
|
|
151
|
+
console.log('[TileBridge] ⌨️ Keyboard state changed (viewport)', {
|
|
152
|
+
visible: keyboardVisible,
|
|
153
|
+
height: keyboardHeight,
|
|
154
|
+
baselineHeight: this.baselineViewportHeight,
|
|
155
|
+
currentHeight,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
// Send keyboard state to parent (React Native)
|
|
159
|
+
this.sendToParent({
|
|
160
|
+
type: 'tile:keyboard',
|
|
161
|
+
payload: {
|
|
162
|
+
visible: keyboardVisible,
|
|
163
|
+
height: keyboardHeight,
|
|
164
|
+
source: 'viewport',
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
// Update baseline when keyboard is hidden (to handle orientation changes)
|
|
169
|
+
if (!keyboardVisible && currentHeight > this.baselineViewportHeight) {
|
|
170
|
+
this.baselineViewportHeight = currentHeight;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Handle focusin events - input element gained focus
|
|
175
|
+
*/
|
|
176
|
+
handleFocusIn(event) {
|
|
177
|
+
const target = event.target;
|
|
178
|
+
if (!target)
|
|
179
|
+
return;
|
|
180
|
+
// Check if the focused element is an input-like element
|
|
181
|
+
const isInputLike = this.isInputElement(target);
|
|
182
|
+
if (isInputLike && !this.inputFocused) {
|
|
183
|
+
this.inputFocused = true;
|
|
184
|
+
this.focusedElement = target;
|
|
185
|
+
if (this.isDevelopment() || this.isPreview()) {
|
|
186
|
+
console.log('[TileBridge] ⌨️ Input focused', {
|
|
187
|
+
tagName: target.tagName,
|
|
188
|
+
type: target.type,
|
|
189
|
+
id: target.id,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
// Send focus event to parent
|
|
193
|
+
this.sendToParent({
|
|
194
|
+
type: 'tile:input-focused',
|
|
195
|
+
payload: {
|
|
196
|
+
tagName: target.tagName.toLowerCase(),
|
|
197
|
+
type: target.type || null,
|
|
198
|
+
id: target.id || null,
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Handle focusout events - input element lost focus
|
|
205
|
+
*/
|
|
206
|
+
handleFocusOut(event) {
|
|
207
|
+
const target = event.target;
|
|
208
|
+
if (!target)
|
|
209
|
+
return;
|
|
210
|
+
// Check if the blurred element is the one we're tracking
|
|
211
|
+
const isInputLike = this.isInputElement(target);
|
|
212
|
+
if (isInputLike && this.inputFocused) {
|
|
213
|
+
// Use setTimeout to check if focus moved to another input
|
|
214
|
+
// If relatedTarget is another input, we stay in "focused" state
|
|
215
|
+
setTimeout(() => {
|
|
216
|
+
const activeElement = document.activeElement;
|
|
217
|
+
const stillFocused = activeElement && this.isInputElement(activeElement);
|
|
218
|
+
if (!stillFocused) {
|
|
219
|
+
this.inputFocused = false;
|
|
220
|
+
this.focusedElement = null;
|
|
221
|
+
if (this.isDevelopment() || this.isPreview()) {
|
|
222
|
+
console.log('[TileBridge] ⌨️ Input blurred');
|
|
223
|
+
}
|
|
224
|
+
// Send blur event to parent
|
|
225
|
+
this.sendToParent({
|
|
226
|
+
type: 'tile:input-blurred',
|
|
227
|
+
payload: {},
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}, 10);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Check if an element is an input-like element that would trigger keyboard
|
|
235
|
+
*/
|
|
236
|
+
isInputElement(element) {
|
|
237
|
+
const tagName = element.tagName.toLowerCase();
|
|
238
|
+
// Standard input elements
|
|
239
|
+
if (tagName === 'textarea')
|
|
240
|
+
return true;
|
|
241
|
+
if (tagName === 'select')
|
|
242
|
+
return false; // Select doesn't trigger keyboard
|
|
243
|
+
if (tagName === 'input') {
|
|
244
|
+
const inputType = element.type?.toLowerCase();
|
|
245
|
+
// These input types trigger the keyboard
|
|
246
|
+
const keyboardTypes = ['text', 'email', 'password', 'search', 'tel', 'url', 'number'];
|
|
247
|
+
return !inputType || keyboardTypes.includes(inputType);
|
|
248
|
+
}
|
|
249
|
+
// Contenteditable elements
|
|
250
|
+
if (element.hasAttribute('contenteditable')) {
|
|
251
|
+
const value = element.getAttribute('contenteditable');
|
|
252
|
+
return value === 'true' || value === '';
|
|
253
|
+
}
|
|
254
|
+
return false;
|
|
69
255
|
}
|
|
70
256
|
handleMessage(event) {
|
|
71
257
|
// Validate origin (skip validation in dev and preview)
|