capacitor-extended-dialog 0.0.11 → 0.0.12

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/README.md CHANGED
@@ -9,6 +9,13 @@ npm install capacitor-extended-dialog
9
9
  npx cap sync
10
10
  ```
11
11
 
12
+ ## Android sheet behavior
13
+
14
+ - In `mode: 'basic'`, native Android sheets open between 50% and 80% of screen height.
15
+ - Dragging up continuously increases available body space while keeping header content at the top and actions pinned to the bottom.
16
+ - If sheet content exceeds the 80% initial cap, the body area becomes internally scrollable.
17
+ - In `mode: 'fullscreen'`, the same top-content/bottom-actions layout is preserved with a scrollable body area for overflow.
18
+
12
19
  ## API
13
20
 
14
21
  <docgen-index>
@@ -21,6 +21,9 @@ import android.widget.ScrollView;
21
21
  import android.widget.TextView;
22
22
  import androidx.annotation.NonNull;
23
23
  import androidx.annotation.Nullable;
24
+ import androidx.core.graphics.Insets;
25
+ import androidx.core.view.ViewCompat;
26
+ import androidx.core.view.WindowInsetsCompat;
24
27
  import androidx.core.widget.TextViewCompat;
25
28
  import com.google.android.material.bottomsheet.BottomSheetBehavior;
26
29
  import com.google.android.material.bottomsheet.BottomSheetDialog;
@@ -51,10 +54,17 @@ public class SheetBottomDialogFragment extends BottomSheetDialogFragment {
51
54
  private ExtendedDialog.SheetCallback sheetCallback;
52
55
  private boolean dismissed = false;
53
56
  private Context themedContext;
57
+ private LinearLayout rootLayout;
54
58
  private ScrollView scrollView;
55
59
  private LinearLayout headerLayout;
56
60
  private LinearLayout bodyLayout;
57
61
  private LinearLayout buttonContainer;
62
+ private int buttonHorizontalPaddingPx;
63
+ private int buttonTopPaddingPx;
64
+ private int buttonBottomBasePaddingPx;
65
+ private int buttonBottomInsetsPx;
66
+ private int buttonExpandedExtraPaddingPx;
67
+ private boolean applyExpandedButtonOffset;
58
68
 
59
69
  public static SheetBottomDialogFragment newInstance(
60
70
  String title,
@@ -145,10 +155,13 @@ public class SheetBottomDialogFragment extends BottomSheetDialogFragment {
145
155
 
146
156
  Context ctx = getThemedContext();
147
157
  float density = getResources().getDisplayMetrics().density;
158
+ int screenHeight = getResources().getDisplayMetrics().heightPixels;
159
+ int initialRootHeight = fullscreen ? screenHeight : (int) (screenHeight * 0.5f);
148
160
 
149
161
  // Root layout
150
- LinearLayout root = new LinearLayout(ctx);
151
- root.setOrientation(LinearLayout.VERTICAL);
162
+ rootLayout = new LinearLayout(ctx);
163
+ rootLayout.setOrientation(LinearLayout.VERTICAL);
164
+ rootLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, initialRootHeight));
152
165
 
153
166
  // Apply background color
154
167
  if (styleOptions.getBackgroundColor() != null) {
@@ -161,7 +174,7 @@ public class SheetBottomDialogFragment extends BottomSheetDialogFragment {
161
174
  .build();
162
175
  MaterialShapeDrawable shapeDrawable = new MaterialShapeDrawable(shapeModel);
163
176
  shapeDrawable.setFillColor(ColorStateList.valueOf(styleOptions.getBackgroundColor()));
164
- root.setBackground(shapeDrawable);
177
+ rootLayout.setBackground(shapeDrawable);
165
178
  }
166
179
 
167
180
  // Drag handle (only shown in basic/non-fullscreen mode)
@@ -180,7 +193,7 @@ public class SheetBottomDialogFragment extends BottomSheetDialogFragment {
180
193
  handleDrawable.setCornerRadius(handleHeight / 2f);
181
194
  handleDrawable.setColor(outlineVariantColor);
182
195
  dragHandle.setBackground(handleDrawable);
183
- root.addView(dragHandle);
196
+ rootLayout.addView(dragHandle);
184
197
  }
185
198
 
186
199
  int horizontalPadding = (int) (24 * density);
@@ -247,12 +260,13 @@ public class SheetBottomDialogFragment extends BottomSheetDialogFragment {
247
260
  titleView.setLayoutParams(titleParams);
248
261
  headerLayout.addView(titleView);
249
262
  }
250
- root.addView(headerLayout);
263
+ rootLayout.addView(headerLayout);
251
264
 
252
265
  // ScrollView for body content (rows/message only)
253
266
  scrollView = new ScrollView(ctx);
254
267
  scrollView.setFillViewport(true);
255
- scrollView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
268
+ scrollView.setLayoutParams(new LinearLayout.LayoutParams(
269
+ ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
256
270
 
257
271
  bodyLayout = new LinearLayout(ctx);
258
272
  bodyLayout.setOrientation(LinearLayout.VERTICAL);
@@ -312,14 +326,35 @@ public class SheetBottomDialogFragment extends BottomSheetDialogFragment {
312
326
  }
313
327
 
314
328
  scrollView.addView(bodyLayout);
315
- root.addView(scrollView);
329
+ rootLayout.addView(scrollView);
316
330
 
317
331
  // Button container pinned at bottom
318
332
  buttonContainer = new LinearLayout(ctx);
319
333
  buttonContainer.setOrientation(LinearLayout.VERTICAL);
320
334
  float topSpacing = styleOptions.getContentButtonSpacing() != null ? styleOptions.getContentButtonSpacing() : 12f;
321
-
322
- buttonContainer.setPadding(horizontalPadding, (int) (topSpacing * density), horizontalPadding, (int) (4 * density));
335
+ int buttonTopPadding = (int) (topSpacing * density);
336
+ int baseBottomPadding = (int) ((fullscreen ? 4f : 12f) * density);
337
+ int nonFullscreenExtraBottomPadding = fullscreen ? 0 : (int) (4 * density);
338
+ int bottomPaddingWithoutInsets = baseBottomPadding + nonFullscreenExtraBottomPadding;
339
+
340
+ buttonHorizontalPaddingPx = horizontalPadding;
341
+ buttonTopPaddingPx = buttonTopPadding;
342
+ buttonBottomBasePaddingPx = bottomPaddingWithoutInsets;
343
+ buttonBottomInsetsPx = 0;
344
+ buttonExpandedExtraPaddingPx = (int) (48 * density);
345
+ applyExpandedButtonOffset = fullscreen;
346
+
347
+ applyButtonContainerPadding();
348
+
349
+ ViewCompat.setOnApplyWindowInsetsListener(buttonContainer, (viewWithInsets, windowInsets) -> {
350
+ Insets systemBarInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
351
+ Insets gestureInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures());
352
+ Insets tappableInsets = windowInsets.getInsets(WindowInsetsCompat.Type.tappableElement());
353
+ buttonBottomInsetsPx = Math.max(systemBarInsets.bottom, Math.max(gestureInsets.bottom, tappableInsets.bottom));
354
+ applyButtonContainerPadding();
355
+ return windowInsets;
356
+ });
357
+ ViewCompat.requestApplyInsets(buttonContainer);
323
358
 
324
359
  int primaryColor = MaterialColors.getColor(ctx, android.R.attr.colorPrimary, 0xFF6750A4);
325
360
 
@@ -380,9 +415,9 @@ public class SheetBottomDialogFragment extends BottomSheetDialogFragment {
380
415
  cancelBtn.setLayoutParams(cancelParams);
381
416
  buttonContainer.addView(cancelBtn);
382
417
 
383
- root.addView(buttonContainer);
418
+ rootLayout.addView(buttonContainer);
384
419
 
385
- return root;
420
+ return rootLayout;
386
421
  }
387
422
 
388
423
  @Override
@@ -413,17 +448,53 @@ public class SheetBottomDialogFragment extends BottomSheetDialogFragment {
413
448
  behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
414
449
  // Prevent dragging down in fullscreen mode
415
450
  behavior.setDraggable(false);
451
+ setExpandedButtonOffset(true);
452
+ behavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
453
+ @Override
454
+ public void onStateChanged(@NonNull View sheet, int newState) {
455
+ if (newState == BottomSheetBehavior.STATE_EXPANDED) {
456
+ setExpandedButtonOffset(true);
457
+ setRootHeight(getVisibleSheetHeight(sheet));
458
+ }
459
+ }
460
+
461
+ @Override
462
+ public void onSlide(@NonNull View sheet, float slideOffset) {
463
+ setExpandedButtonOffset(true);
464
+ setRootHeight(getVisibleSheetHeight(sheet));
465
+ }
466
+ });
467
+ bottomSheet.post(() -> setRootHeight(getVisibleSheetHeight(bottomSheet)));
416
468
  } else {
417
- // Dynamic height based on content (min 50%, max 80%), drag down to close
469
+ // Multi-stop bottom sheet: drag up to fullscreen, drag down to dismiss
418
470
  ViewGroup.LayoutParams layoutParams = bottomSheet.getLayoutParams();
419
471
  layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
420
472
  bottomSheet.setLayoutParams(layoutParams);
421
473
 
422
- behavior.setFitToContents(true);
474
+ behavior.setFitToContents(false);
423
475
  behavior.setSkipCollapsed(true);
424
476
  behavior.setHideable(true);
425
477
  behavior.setDraggable(true);
426
478
 
479
+ behavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
480
+ @Override
481
+ public void onStateChanged(@NonNull View sheet, int newState) {
482
+ if (
483
+ newState == BottomSheetBehavior.STATE_HALF_EXPANDED ||
484
+ newState == BottomSheetBehavior.STATE_EXPANDED
485
+ ) {
486
+ setExpandedButtonOffset(newState == BottomSheetBehavior.STATE_EXPANDED);
487
+ setRootHeight(getVisibleSheetHeight(sheet));
488
+ }
489
+ }
490
+
491
+ @Override
492
+ public void onSlide(@NonNull View sheet, float slideOffset) {
493
+ setExpandedButtonOffset(slideOffset >= 0.9f);
494
+ setRootHeight(getVisibleSheetHeight(sheet));
495
+ }
496
+ });
497
+
427
498
  bottomSheet.post(() -> {
428
499
  int screenHeight = getResources().getDisplayMetrics().heightPixels;
429
500
  int width = bottomSheet.getWidth() > 0
@@ -445,36 +516,90 @@ public class SheetBottomDialogFragment extends BottomSheetDialogFragment {
445
516
  bodyHeight = bodyLayout.getMeasuredHeight();
446
517
  }
447
518
 
448
- int buttonHeight = 0;
519
+ int btnHeight = 0;
449
520
  if (buttonContainer != null) {
450
521
  buttonContainer.measure(widthSpec, heightSpec);
451
- buttonHeight = buttonContainer.getMeasuredHeight();
522
+ btnHeight = buttonContainer.getMeasuredHeight();
452
523
  }
453
524
 
454
- int minHeight = (int) (screenHeight * 0.5f);
455
- int maxHeight = (int) (screenHeight * 0.8f);
456
- int desiredHeight = headerHeight + bodyHeight + buttonHeight;
457
- desiredHeight = Math.max(desiredHeight, minHeight);
458
- desiredHeight = Math.min(desiredHeight, maxHeight);
459
-
460
- if (scrollView != null) {
461
- ViewGroup.LayoutParams scrollParams = scrollView.getLayoutParams();
462
- int scrollHeight = Math.max(0, desiredHeight - headerHeight - buttonHeight);
463
- scrollParams.height = scrollHeight;
464
- scrollView.setLayoutParams(scrollParams);
465
- }
525
+ // Add drag handle height (~28dp)
526
+ float density = getResources().getDisplayMetrics().density;
527
+ int dragHandleHeight = (int) (28 * density);
466
528
 
467
- ViewGroup.LayoutParams sheetParams = bottomSheet.getLayoutParams();
468
- sheetParams.height = desiredHeight;
469
- bottomSheet.setLayoutParams(sheetParams);
529
+ int contentRequiredHeight = headerHeight + bodyHeight + btnHeight + dragHandleHeight;
530
+ int desiredHeight = clampInitialSheetHeight(screenHeight, contentRequiredHeight);
531
+ float ratio = calculateHalfExpandedRatio(screenHeight, desiredHeight);
470
532
 
533
+ setRootHeight(desiredHeight);
534
+ behavior.setHalfExpandedRatio(ratio);
471
535
  behavior.setPeekHeight(desiredHeight);
472
- behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
536
+ behavior.setState(BottomSheetBehavior.STATE_HALF_EXPANDED);
537
+ bottomSheet.post(() -> setRootHeight(getVisibleSheetHeight(bottomSheet)));
473
538
  });
474
539
  }
475
540
  });
476
541
  }
477
542
 
543
+ static int clampInitialSheetHeight(int screenHeight, int contentRequiredHeight) {
544
+ int minHeight = (int) (screenHeight * 0.5f);
545
+ int maxHeight = (int) (screenHeight * 0.8f);
546
+ int clampedToMin = Math.max(contentRequiredHeight, minHeight);
547
+ return Math.min(clampedToMin, maxHeight);
548
+ }
549
+
550
+ static float calculateHalfExpandedRatio(int screenHeight, int desiredHeight) {
551
+ if (screenHeight <= 0) {
552
+ return 0.5f;
553
+ }
554
+ float ratio = (float) desiredHeight / screenHeight;
555
+ return Math.max(0.5f, Math.min(ratio, 0.8f));
556
+ }
557
+
558
+ private int getVisibleSheetHeight(@NonNull View sheet) {
559
+ View parent = sheet.getParent() instanceof View ? (View) sheet.getParent() : null;
560
+ int parentHeight = parent != null ? parent.getHeight() : getResources().getDisplayMetrics().heightPixels;
561
+ int visibleHeight = parentHeight - sheet.getTop();
562
+ int nonNegativeHeight = Math.max(0, visibleHeight);
563
+ // Clamp to parent bounds to avoid overshoot during spring/drag transitions.
564
+ return Math.min(parentHeight, nonNegativeHeight);
565
+ }
566
+
567
+ private void setRootHeight(int targetHeight) {
568
+ if (rootLayout == null || targetHeight <= 0) {
569
+ return;
570
+ }
571
+ ViewGroup.LayoutParams params = rootLayout.getLayoutParams();
572
+ if (params == null) {
573
+ params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, targetHeight);
574
+ } else if (params.height == targetHeight) {
575
+ return;
576
+ }
577
+ params.height = targetHeight;
578
+ rootLayout.setLayoutParams(params);
579
+ }
580
+
581
+ private void setExpandedButtonOffset(boolean enabled) {
582
+ if (applyExpandedButtonOffset == enabled) {
583
+ return;
584
+ }
585
+ applyExpandedButtonOffset = enabled;
586
+ applyButtonContainerPadding();
587
+ }
588
+
589
+ private void applyButtonContainerPadding() {
590
+ if (buttonContainer == null) {
591
+ return;
592
+ }
593
+ int expandedExtraBottomPadding = applyExpandedButtonOffset ? buttonExpandedExtraPaddingPx : 0;
594
+ int resolvedBottomPadding = buttonBottomBasePaddingPx + buttonBottomInsetsPx + expandedExtraBottomPadding;
595
+ buttonContainer.setPadding(
596
+ buttonHorizontalPaddingPx,
597
+ buttonTopPaddingPx,
598
+ buttonHorizontalPaddingPx,
599
+ resolvedBottomPadding
600
+ );
601
+ }
602
+
478
603
  private LinearLayout createSheetRow(
479
604
  Context context,
480
605
  JSONObject row,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-extended-dialog",
3
- "version": "0.0.11",
3
+ "version": "0.0.12",
4
4
  "description": "Show native dialogs and sheets in Capacitor with Material 3 and Liquid Glass UI",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",