aumera-on-screen-widget 0.0.7 → 0.0.9

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 (2) hide show
  1. package/df-btn.js +159 -13
  2. package/package.json +1 -1
package/df-btn.js CHANGED
@@ -34,7 +34,10 @@ if (!config.orgId) {
34
34
  background-color: ${config.background || "#FEFFFF"};
35
35
  border-radius: 24px;
36
36
  cursor: pointer;
37
- transition: all .45s cubic-bezier(.4, 0, .2, 1);
37
+ transition: none;
38
+ transform-origin: ${
39
+ config.position === "left" ? "bottom left" : "bottom right"
40
+ };
38
41
  position: fixed;
39
42
  bottom: 0px;
40
43
  ${config.position === "left" ? "left: 0px;" : "right: 0px;"}
@@ -130,7 +133,10 @@ if (!config.orgId) {
130
133
  max-height: 75vh;
131
134
  width: ${config.width || "414px"};
132
135
  max-width: 414px;
133
- transition: all .45s cubic-bezier(.4, 0, .2, 1);
136
+ transition: none;
137
+ transform-origin: ${
138
+ config.position === "left" ? "bottom left" : "bottom right"
139
+ };
134
140
  ${config.position === "left" ? "float: left;" : "float: right;"}
135
141
  opacity: 1;
136
142
  border-radius: 0 0 16px 16px;
@@ -160,20 +166,27 @@ if (!config.orgId) {
160
166
  .df-btn.df-maximized {
161
167
  width: 100vw !important;
162
168
  height: 100vh !important;
169
+ height: 100dvh !important; /* Dynamic viewport height for mobile */
163
170
  top: 0 !important;
164
- left: 0 !important;
165
- right: 0 !important;
171
+ ${config.position === "left" ? "left: 0 !important;" : ""}
172
+ ${config.position === "right" ? "right: 0 !important;" : ""}
166
173
  bottom: 0 !important;
167
174
  margin: 0 !important;
168
175
  border-radius: 0 !important;
169
- transition: all .45s cubic-bezier(.4, 0, .2, 1) !important;
176
+ /* Handle safe areas on mobile devices */
177
+ padding-bottom: env(safe-area-inset-bottom, 0);
170
178
  }
171
179
  .df-btn.df-maximized .df-btn-content {
172
180
  width: 100vw !important;
181
+ max-width: 100vw !important;
173
182
  height: calc(100vh - 56px) !important;
183
+ height: calc(100dvh - env(safe-area-inset-bottom, 0px)) !important; /* Account for safe area */
184
+ max-height: calc(100vh - 56px) !important;
185
+ max-height: calc(100dvh - env(safe-area-inset-bottom, 0px)) !important;
174
186
  opacity: 1 !important;
187
+ float: none !important;
175
188
  display: block !important;
176
- transition: all .45s cubic-bezier(.4, 0, .2, 1) !important;
189
+ border-radius: 0 !important;
177
190
  }
178
191
  .df-btn.df-closed .df-btn-content {
179
192
  width: 0 !important;
@@ -190,6 +203,17 @@ if (!config.orgId) {
190
203
  justify-content: center;
191
204
  }
192
205
 
206
+ /* Animation classes for FLIP */
207
+ .df-animating {
208
+ transition: transform .45s cubic-bezier(.4, 0, .2, 1);
209
+ will-change: transform;
210
+ }
211
+
212
+ .df-content-animating {
213
+ transition: opacity .45s cubic-bezier(.4, 0, .2, 1);
214
+ will-change: opacity;
215
+ }
216
+
193
217
  @media screen and (max-width: 720px){
194
218
  .df-btn {
195
219
  border-radius: 28px;
@@ -197,13 +221,16 @@ if (!config.orgId) {
197
221
 
198
222
  .df-btn:not(.df-closed) {
199
223
  margin: 0px;
200
- border-radius: 0px
224
+ border-radius: 0px;
225
+ padding-bottom: env(safe-area-inset-bottom, 0);
201
226
  }
202
227
 
203
228
  .df-btn:not(.df-closed) > .df-btn-content {
204
229
  width: 100vw;
205
230
  max-height: 100vh;
231
+ max-height: 100dvh;
206
232
  height: calc(100vh - 56px);
233
+ height: calc(100dvh - 56px - env(safe-area-inset-bottom, 0px));
207
234
  padding-bottom: 0px
208
235
  }
209
236
 
@@ -272,6 +299,8 @@ if (!config.orgId) {
272
299
  // Get the detected language
273
300
  const detectedLang = getLanguage();
274
301
 
302
+ // Check if mobile device - removed unused variable
303
+
275
304
  document.write(`
276
305
  <button class="df-btn df-closed" onclick="dfToggle()">
277
306
  ${
@@ -282,7 +311,7 @@ if (!config.orgId) {
282
311
  <div class="df-btn-header">
283
312
  <div class="df-btn-text">${config.openText || "Chat"}</div>
284
313
  </div>
285
- <iframe class="df-btn-content" src="https://chat.dev.aumera.ai/${detectedLang}/?orgId=${
314
+ <iframe class="df-btn-content" src="https://chat.aumera.ai/${detectedLang}/?orgId=${
286
315
  config.orgId
287
316
  }" allow="microphone *"></iframe>
288
317
  </button>
@@ -291,7 +320,6 @@ if (!config.orgId) {
291
320
  let dfToggled = false;
292
321
  let inactivityTimer = null;
293
322
  let shakeInterval = null;
294
- let maximized = false;
295
323
 
296
324
  const startInactivityTimer = () => {
297
325
  // Only start inactivity timer if button is closed
@@ -352,6 +380,9 @@ if (!config.orgId) {
352
380
  if (btnText) btnText.innerText = "";
353
381
  // remove right padding from df-btn-text
354
382
  btnText.style.paddingRight = "0";
383
+
384
+ // Only add maximize/minimize button on desktop
385
+
355
386
  // Add maximize/minimize button (right)
356
387
  const maxBtn = document.createElement("div");
357
388
  maxBtn.className = "maximize-minimize-btn";
@@ -369,6 +400,7 @@ if (!config.orgId) {
369
400
  e.stopPropagation();
370
401
  maximizeMinimize();
371
402
  });
403
+
372
404
  // Add close button (left)
373
405
  const closeBtn = document.createElement("div");
374
406
  closeBtn.className = "close-btn";
@@ -393,8 +425,56 @@ if (!config.orgId) {
393
425
  function maximizeMinimize() {
394
426
  const btn = document.querySelector(".df-btn");
395
427
  if (!btn) return;
428
+ const content = btn.querySelector(".df-btn-content");
429
+ if (!content) return;
430
+
431
+ // FIRST: Capture current positions
432
+ const firstBtn = btn.getBoundingClientRect();
433
+
434
+ // Toggle the maximized state (instant layout change)
396
435
  btn.classList.toggle("df-maximized");
397
- updateHeaderButtons();
436
+
437
+ // LAST: Capture new positions
438
+ const lastBtn = btn.getBoundingClientRect();
439
+
440
+ // INVERT: Calculate the delta and apply transform to invert to original position
441
+ const btnDeltaX = firstBtn.left - lastBtn.left;
442
+ const btnDeltaY = firstBtn.top - lastBtn.top;
443
+ const btnScaleX = firstBtn.width / Math.max(lastBtn.width, 0.0001);
444
+ const btnScaleY = firstBtn.height / Math.max(lastBtn.height, 0.0001);
445
+
446
+ // Apply inverted transform
447
+ btn.style.transform = `translate(${btnDeltaX}px, ${btnDeltaY}px) scale(${btnScaleX}, ${btnScaleY})`;
448
+
449
+ // Optional: Soften iframe reflow with opacity
450
+ content.classList.add("df-content-animating");
451
+ content.style.opacity = "0.95";
452
+
453
+ // Force reflow to ensure the inverted state is applied
454
+ btn.getBoundingClientRect();
455
+
456
+ // PLAY: Add animation class and clear transform to animate to identity
457
+ btn.classList.add("df-animating");
458
+ btn.style.transform = "";
459
+
460
+ // Cleanup after animation ends
461
+ const onTransitionEnd = (e) => {
462
+ if (e.target !== btn) return;
463
+ btn.classList.remove("df-animating");
464
+ content.classList.remove("df-content-animating");
465
+ content.style.opacity = "";
466
+ updateHeaderButtons();
467
+ };
468
+
469
+ btn.addEventListener("transitionend", onTransitionEnd, { once: true });
470
+
471
+ // Fallback cleanup in case transition doesn't fire
472
+ setTimeout(() => {
473
+ btn.classList.remove("df-animating");
474
+ content.classList.remove("df-content-animating");
475
+ content.style.opacity = "";
476
+ updateHeaderButtons();
477
+ }, 500);
398
478
  }
399
479
 
400
480
  function dfToggle() {
@@ -418,14 +498,80 @@ if (!config.orgId) {
418
498
  // Remove onclick from button, handle open/close in JS
419
499
  const btn = document.querySelector(".df-btn");
420
500
  btn.onclick = null;
421
- btn.addEventListener("click", (e) => {
501
+
502
+ // Check if mobile for later use
503
+ const isMobile = window.innerWidth <= 768;
504
+
505
+ btn.addEventListener("click", () => {
422
506
  // Only toggle if chat is closed (popover mode)
423
507
  if (btn.classList.contains("df-closed")) {
424
- dfToggle();
508
+ const isMobile = window.innerWidth <= 768;
509
+
510
+ if (isMobile) {
511
+ // On mobile, apply FLIP animation for open+maximize
512
+ const content = btn.querySelector(".df-btn-content");
513
+
514
+ // FIRST: Capture current (closed) position
515
+ const firstBtn = btn.getBoundingClientRect();
516
+
517
+ // Open and maximize instantly
518
+ btn.classList.remove("df-closed");
519
+ btn.classList.add("df-maximized");
520
+
521
+ // LAST: Capture final position
522
+ const lastBtn = btn.getBoundingClientRect();
523
+
524
+ // INVERT
525
+ const btnDeltaX = firstBtn.left - lastBtn.left;
526
+ const btnDeltaY = firstBtn.top - lastBtn.top;
527
+ const btnScaleX = firstBtn.width / Math.max(lastBtn.width, 0.0001);
528
+ const btnScaleY = firstBtn.height / Math.max(lastBtn.height, 0.0001);
529
+
530
+ btn.style.transform = `translate(${btnDeltaX}px, ${btnDeltaY}px) scale(${btnScaleX}, ${btnScaleY})`;
531
+
532
+ if (content) {
533
+ content.classList.add("df-content-animating");
534
+ content.style.opacity = "0.95";
535
+ }
536
+
537
+ // Force reflow
538
+ btn.getBoundingClientRect();
539
+
540
+ // PLAY
541
+ btn.classList.add("df-animating");
542
+ btn.style.transform = "";
543
+
544
+ // Cleanup
545
+ const onTransitionEnd = () => {
546
+ btn.classList.remove("df-animating");
547
+ if (content) {
548
+ content.classList.remove("df-content-animating");
549
+ content.style.opacity = "";
550
+ }
551
+ updateHeaderButtons();
552
+ };
553
+
554
+ btn.addEventListener("transitionend", onTransitionEnd, { once: true });
555
+
556
+ setTimeout(() => {
557
+ btn.classList.remove("df-animating");
558
+ if (content) {
559
+ content.classList.remove("df-content-animating");
560
+ content.style.opacity = "";
561
+ }
562
+ updateHeaderButtons();
563
+ }, 500);
564
+ } else {
565
+ // Desktop: just toggle open
566
+ dfToggle();
567
+ }
425
568
  }
426
569
  // If open, do nothing (let header buttons handle maximize/minimize/close)
427
570
  });
428
571
 
429
572
  // Start the inactivity timer when the page loads (button is initially closed)
430
- startInactivityTimer();
573
+ // But not on mobile since it starts open
574
+ if (!isMobile) {
575
+ startInactivityTimer();
576
+ }
431
577
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aumera-on-screen-widget",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "A lightweight, customizable chat widget for websites",
5
5
  "main": "df-btn.js",
6
6
  "scripts": {