@trustquery/browser 0.2.9 → 0.2.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trustquery/browser",
3
- "version": "0.2.9",
3
+ "version": "0.2.10",
4
4
  "description": "Turn any textarea into an interactive trigger-based editor with inline styling",
5
5
  "type": "module",
6
6
  "main": "dist/trustquery.js",
@@ -36,7 +36,7 @@
36
36
  "license": "MIT",
37
37
  "repository": {
38
38
  "type": "git",
39
- "url": "https://github.com/RonItelman/trustquery-browser.git"
39
+ "url": "git+https://github.com/RonItelman/trustquery-browser.git"
40
40
  },
41
41
  "bugs": {
42
42
  "url": "https://github.com/RonItelman/trustquery-browser/issues"
@@ -0,0 +1,240 @@
1
+ // MobileKeyboardHandler - Handles mobile virtual keyboard behavior
2
+ // Detects keyboard appearance and adjusts layout to keep textarea visible
3
+
4
+ export default class MobileKeyboardHandler {
5
+ /**
6
+ * Create mobile keyboard handler
7
+ * @param {Object} options - Configuration options
8
+ */
9
+ constructor(options = {}) {
10
+ this.options = {
11
+ textarea: options.textarea || null,
12
+ wrapper: options.wrapper || null,
13
+ debug: options.debug || false,
14
+ ...options
15
+ };
16
+
17
+ this.isKeyboardVisible = false;
18
+ this.lastViewportHeight = window.innerHeight;
19
+ this.visualViewport = window.visualViewport;
20
+
21
+ if (this.options.debug) {
22
+ console.log('[MobileKeyboardHandler] Initialized');
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Initialize keyboard detection
28
+ */
29
+ init() {
30
+ if (!this.options.textarea) {
31
+ console.warn('[MobileKeyboardHandler] No textarea provided');
32
+ return;
33
+ }
34
+
35
+ // Use Visual Viewport API if available (preferred method)
36
+ if (this.visualViewport) {
37
+ this.visualViewport.addEventListener('resize', this.handleViewportResize);
38
+ this.visualViewport.addEventListener('scroll', this.handleViewportScroll);
39
+
40
+ if (this.options.debug) {
41
+ console.log('[MobileKeyboardHandler] Using Visual Viewport API');
42
+ }
43
+ } else {
44
+ // Fallback to window resize
45
+ window.addEventListener('resize', this.handleWindowResize);
46
+
47
+ if (this.options.debug) {
48
+ console.log('[MobileKeyboardHandler] Using window resize fallback');
49
+ }
50
+ }
51
+
52
+ // Handle focus events
53
+ this.options.textarea.addEventListener('focus', this.handleFocus);
54
+ this.options.textarea.addEventListener('blur', this.handleBlur);
55
+ }
56
+
57
+ /**
58
+ * Handle Visual Viewport resize (keyboard appearance/disappearance)
59
+ */
60
+ handleViewportResize = () => {
61
+ if (!this.visualViewport) return;
62
+
63
+ const viewportHeight = this.visualViewport.height;
64
+ const windowHeight = window.innerHeight;
65
+
66
+ if (this.options.debug) {
67
+ console.log('[MobileKeyboardHandler] Viewport resize:', {
68
+ viewportHeight,
69
+ windowHeight,
70
+ scale: this.visualViewport.scale
71
+ });
72
+ }
73
+
74
+ // Keyboard is visible if viewport height is significantly smaller than window height
75
+ const wasKeyboardVisible = this.isKeyboardVisible;
76
+ this.isKeyboardVisible = viewportHeight < windowHeight * 0.75;
77
+
78
+ if (this.isKeyboardVisible !== wasKeyboardVisible) {
79
+ if (this.isKeyboardVisible) {
80
+ this.onKeyboardShow();
81
+ } else {
82
+ this.onKeyboardHide();
83
+ }
84
+ }
85
+
86
+ // Always adjust layout when viewport changes
87
+ if (this.isKeyboardVisible) {
88
+ this.adjustLayout();
89
+ }
90
+ };
91
+
92
+ /**
93
+ * Handle Visual Viewport scroll
94
+ */
95
+ handleViewportScroll = () => {
96
+ if (this.isKeyboardVisible) {
97
+ // Ensure textarea stays in view during scroll
98
+ this.ensureTextareaVisible();
99
+ }
100
+ };
101
+
102
+ /**
103
+ * Handle window resize (fallback)
104
+ */
105
+ handleWindowResize = () => {
106
+ const currentHeight = window.innerHeight;
107
+ const heightDifference = this.lastViewportHeight - currentHeight;
108
+
109
+ if (this.options.debug) {
110
+ console.log('[MobileKeyboardHandler] Window resize:', {
111
+ lastHeight: this.lastViewportHeight,
112
+ currentHeight,
113
+ difference: heightDifference
114
+ });
115
+ }
116
+
117
+ // Significant decrease in height suggests keyboard appeared
118
+ if (heightDifference > 150) {
119
+ if (!this.isKeyboardVisible) {
120
+ this.isKeyboardVisible = true;
121
+ this.onKeyboardShow();
122
+ }
123
+ }
124
+ // Significant increase suggests keyboard hidden
125
+ else if (heightDifference < -150) {
126
+ if (this.isKeyboardVisible) {
127
+ this.isKeyboardVisible = false;
128
+ this.onKeyboardHide();
129
+ }
130
+ }
131
+
132
+ this.lastViewportHeight = currentHeight;
133
+ };
134
+
135
+ /**
136
+ * Handle textarea focus
137
+ */
138
+ handleFocus = () => {
139
+ if (this.options.debug) {
140
+ console.log('[MobileKeyboardHandler] Textarea focused');
141
+ }
142
+
143
+ // Delay to allow keyboard to appear
144
+ setTimeout(() => {
145
+ this.ensureTextareaVisible();
146
+ }, 300);
147
+ };
148
+
149
+ /**
150
+ * Handle textarea blur
151
+ */
152
+ handleBlur = () => {
153
+ if (this.options.debug) {
154
+ console.log('[MobileKeyboardHandler] Textarea blurred');
155
+ }
156
+ };
157
+
158
+ /**
159
+ * Called when keyboard appears
160
+ */
161
+ onKeyboardShow() {
162
+ if (this.options.debug) {
163
+ console.log('[MobileKeyboardHandler] Keyboard shown');
164
+ }
165
+
166
+ this.adjustLayout();
167
+ this.ensureTextareaVisible();
168
+ }
169
+
170
+ /**
171
+ * Called when keyboard hides
172
+ */
173
+ onKeyboardHide() {
174
+ if (this.options.debug) {
175
+ console.log('[MobileKeyboardHandler] Keyboard hidden');
176
+ }
177
+
178
+ // Reset wrapper height to auto
179
+ if (this.options.wrapper) {
180
+ this.options.wrapper.style.maxHeight = '';
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Adjust layout to accommodate keyboard
186
+ */
187
+ adjustLayout() {
188
+ if (!this.visualViewport || !this.options.wrapper) return;
189
+
190
+ const viewportHeight = this.visualViewport.height;
191
+
192
+ // Set wrapper max-height to visible viewport height minus some padding
193
+ const maxHeight = viewportHeight - 20; // 20px padding
194
+ this.options.wrapper.style.maxHeight = `${maxHeight}px`;
195
+ this.options.wrapper.style.overflow = 'auto';
196
+
197
+ if (this.options.debug) {
198
+ console.log('[MobileKeyboardHandler] Adjusted wrapper height:', maxHeight);
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Ensure textarea is visible above the keyboard
204
+ */
205
+ ensureTextareaVisible() {
206
+ if (!this.options.textarea) return;
207
+
208
+ // Scroll textarea into view
209
+ this.options.textarea.scrollIntoView({
210
+ behavior: 'smooth',
211
+ block: 'center',
212
+ inline: 'nearest'
213
+ });
214
+
215
+ if (this.options.debug) {
216
+ console.log('[MobileKeyboardHandler] Scrolled textarea into view');
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Cleanup event listeners
222
+ */
223
+ destroy() {
224
+ if (this.visualViewport) {
225
+ this.visualViewport.removeEventListener('resize', this.handleViewportResize);
226
+ this.visualViewport.removeEventListener('scroll', this.handleViewportScroll);
227
+ } else {
228
+ window.removeEventListener('resize', this.handleWindowResize);
229
+ }
230
+
231
+ if (this.options.textarea) {
232
+ this.options.textarea.removeEventListener('focus', this.handleFocus);
233
+ this.options.textarea.removeEventListener('blur', this.handleBlur);
234
+ }
235
+
236
+ if (this.options.debug) {
237
+ console.log('[MobileKeyboardHandler] Destroyed');
238
+ }
239
+ }
240
+ }
package/src/TrustQuery.js CHANGED
@@ -8,6 +8,7 @@ import StyleManager from './StyleManager.js';
8
8
  import CommandHandlerRegistry from './CommandHandlers.js';
9
9
  import AutoGrow from './AutoGrow.js';
10
10
  import ValidationStateManager from './ValidationStateManager.js';
11
+ import MobileKeyboardHandler from './MobileKeyboardHandler.js';
11
12
 
12
13
  export default class TrustQuery {
13
14
  // Store all instances
@@ -212,6 +213,17 @@ export default class TrustQuery {
212
213
  console.log('[TrustQuery] AutoGrow feature enabled');
213
214
  }
214
215
 
216
+ // Mobile keyboard handler (enabled by default, can be disabled via options)
217
+ if (this.options.mobileKeyboard !== false) {
218
+ this.features.mobileKeyboard = new MobileKeyboardHandler({
219
+ textarea: this.textarea,
220
+ wrapper: this.wrapper,
221
+ debug: this.options.debug
222
+ });
223
+ this.features.mobileKeyboard.init();
224
+ console.log('[TrustQuery] Mobile keyboard handler enabled');
225
+ }
226
+
215
227
  // Debug logging feature
216
228
  if (this.options.debug) {
217
229
  this.enableDebugLogging();
@@ -502,6 +514,16 @@ export default class TrustQuery {
502
514
  this.interactionHandler.destroy();
503
515
  }
504
516
 
517
+ // Cleanup mobile keyboard handler
518
+ if (this.features.mobileKeyboard) {
519
+ this.features.mobileKeyboard.destroy();
520
+ }
521
+
522
+ // Cleanup auto-grow
523
+ if (this.features.autoGrow) {
524
+ this.features.autoGrow.destroy();
525
+ }
526
+
505
527
  // Unwrap textarea
506
528
  const parent = this.wrapper.parentNode;
507
529
  parent.insertBefore(this.textarea, this.wrapper);