expo-native-sheet-emojis 2.0.4 → 2.1.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.
@@ -9,11 +9,14 @@ import android.view.ViewGroup
9
9
  import android.view.animation.AccelerateDecelerateInterpolator
10
10
  import android.view.WindowManager
11
11
  import android.view.inputmethod.InputMethodManager
12
+ import android.widget.Button
13
+ import android.widget.FrameLayout
14
+ import android.widget.LinearLayout
15
+ import androidx.core.view.AccessibilityDelegateCompat
12
16
  import androidx.core.view.ViewCompat
13
17
  import androidx.core.view.WindowCompat
14
18
  import androidx.core.view.WindowInsetsCompat
15
- import android.widget.FrameLayout
16
- import android.widget.LinearLayout
19
+ import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
17
20
  import com.google.android.material.bottomsheet.BottomSheetBehavior
18
21
  import com.google.android.material.bottomsheet.BottomSheetDialog
19
22
  import expo.modules.kotlin.Promise
@@ -250,19 +253,37 @@ class EmojiSheetModule : Module() {
250
253
  }
251
254
 
252
255
  // Drag handle
256
+ val handleTargetHeight = (24 * density).toInt()
253
257
  val handleBar = View(activity).apply {
254
258
  val width = (40 * density).toInt()
255
259
  val height = (4 * density).toInt()
256
- layoutParams = LinearLayout.LayoutParams(width, height).apply {
257
- gravity = Gravity.CENTER_HORIZONTAL
260
+ layoutParams = FrameLayout.LayoutParams(width, height, Gravity.TOP or Gravity.CENTER_HORIZONTAL).apply {
258
261
  topMargin = (8 * density).toInt()
259
- bottomMargin = (4 * density).toInt()
260
262
  }
261
263
  background = GradientDrawable().apply {
262
264
  setColor(handleColor)
263
265
  cornerRadius = height / 2f
264
266
  }
265
267
  }
268
+ val handleTouchTarget = FrameLayout(activity).apply {
269
+ layoutParams = LinearLayout.LayoutParams(
270
+ LinearLayout.LayoutParams.MATCH_PARENT,
271
+ handleTargetHeight
272
+ )
273
+ contentDescription = "Dismiss emoji sheet"
274
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
275
+ isClickable = true
276
+ isFocusable = true
277
+ setOnClickListener { dismissSheet(cancelled = true) }
278
+ ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
279
+ override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfoCompat) {
280
+ super.onInitializeAccessibilityNodeInfo(host, info)
281
+ info.className = Button::class.java.name
282
+ info.hintText = "Double tap to dismiss."
283
+ }
284
+ })
285
+ addView(handleBar)
286
+ }
266
287
 
267
288
  // Container
268
289
  val halfExpandedRatio = (snapPoints.firstOrNull()?.toFloat() ?: 0.5f).coerceIn(0.05f, 1f)
@@ -285,7 +306,7 @@ class EmojiSheetModule : Module() {
285
306
  }
286
307
  clipToOutline = true
287
308
  outlineProvider = android.view.ViewOutlineProvider.BACKGROUND
288
- addView(handleBar)
309
+ addView(handleTouchTarget)
289
310
  val pickerLp = LinearLayout.LayoutParams(
290
311
  LinearLayout.LayoutParams.MATCH_PARENT, 0, 1f
291
312
  )
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |s|
2
2
  s.name = 'EmojiSheetModule'
3
- s.version = '2.0.4'
3
+ s.version = '2.1.0'
4
4
  s.summary = 'Native emoji picker bottom sheet for React Native'
5
5
  s.description = 'A fully native iOS/Android emoji picker presented in a bottom sheet with search, skin tones, and theming support.'
6
6
  s.author = ''
@@ -337,6 +337,7 @@ private final class SheetViewController: UIViewController, UIGestureRecognizerDe
337
337
 
338
338
  private let backdropView = UIView()
339
339
  private let sheetContainerView = UIView()
340
+ private let grabberHitAreaView = UIControl()
340
341
  private let grabberView = UIView()
341
342
  private let contentContainerView = UIView()
342
343
  private let backdropColor: UIColor
@@ -411,6 +412,11 @@ private final class SheetViewController: UIViewController, UIGestureRecognizerDe
411
412
  applyDetentLayout(currentDetent)
412
413
  }
413
414
 
415
+ override func accessibilityPerformEscape() -> Bool {
416
+ requestDismiss()
417
+ return true
418
+ }
419
+
414
420
  func embedPickerView(_ embeddedView: UIView) {
415
421
  embeddedView.translatesAutoresizingMaskIntoConstraints = false
416
422
  contentContainerView.addSubview(embeddedView)
@@ -574,9 +580,7 @@ private final class SheetViewController: UIViewController, UIGestureRecognizerDe
574
580
  let dismissalThreshold = max(sheetContainerView.bounds.height * 0.5, 1)
575
581
  let shouldDismiss = distance >= dismissalThreshold || velocity >= 1400
576
582
  if shouldDismiss {
577
- dismissSheet { [weak self] in
578
- self?.onDismiss?()
579
- }
583
+ requestDismiss()
580
584
  return
581
585
  }
582
586
 
@@ -632,17 +636,28 @@ private final class SheetViewController: UIViewController, UIGestureRecognizerDe
632
636
  sheetContainerView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
633
637
  sheetContainerView.layer.masksToBounds = true
634
638
 
639
+ grabberHitAreaView.translatesAutoresizingMaskIntoConstraints = false
640
+ grabberHitAreaView.backgroundColor = .clear
641
+ grabberHitAreaView.isAccessibilityElement = true
642
+ grabberHitAreaView.accessibilityLabel = "Dismiss emoji sheet"
643
+ grabberHitAreaView.accessibilityHint = "Double-tap to dismiss."
644
+ grabberHitAreaView.accessibilityTraits = .button
645
+ grabberHitAreaView.addTarget(self, action: #selector(handleGrabberTap), for: .touchUpInside)
646
+
635
647
  grabberView.translatesAutoresizingMaskIntoConstraints = false
636
648
  grabberView.backgroundColor = theme.handleColor
637
649
  grabberView.layer.cornerRadius = Layout.grabberHeight / 2
650
+ grabberView.isUserInteractionEnabled = false
638
651
 
639
652
  contentContainerView.translatesAutoresizingMaskIntoConstraints = false
640
653
  contentContainerView.backgroundColor = .clear
641
654
 
655
+ sheetContainerView.accessibilityViewIsModal = true
642
656
  view.addSubview(backdropView)
643
657
  view.addSubview(sheetContainerView)
644
658
  sheetContainerView.addSubview(contentContainerView)
645
- sheetContainerView.addSubview(grabberView)
659
+ sheetContainerView.addSubview(grabberHitAreaView)
660
+ grabberHitAreaView.addSubview(grabberView)
646
661
 
647
662
  if gestureEnabled {
648
663
  let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handleSheetPan(_:)))
@@ -668,11 +683,16 @@ private final class SheetViewController: UIViewController, UIGestureRecognizerDe
668
683
  sheetContainerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
669
684
  sheetBottomConstraint,
670
685
 
686
+ grabberHitAreaView.topAnchor.constraint(equalTo: sheetContainerView.topAnchor),
687
+ grabberHitAreaView.centerXAnchor.constraint(equalTo: sheetContainerView.centerXAnchor),
688
+ grabberHitAreaView.widthAnchor.constraint(equalToConstant: 96),
689
+ grabberHitAreaView.heightAnchor.constraint(equalToConstant: 24),
690
+
671
691
  grabberView.topAnchor.constraint(
672
- equalTo: sheetContainerView.topAnchor,
692
+ equalTo: grabberHitAreaView.topAnchor,
673
693
  constant: Layout.grabberTopInset
674
694
  ),
675
- grabberView.centerXAnchor.constraint(equalTo: sheetContainerView.centerXAnchor),
695
+ grabberView.centerXAnchor.constraint(equalTo: grabberHitAreaView.centerXAnchor),
676
696
  grabberView.widthAnchor.constraint(equalToConstant: Layout.grabberWidth),
677
697
  grabberView.heightAnchor.constraint(equalToConstant: Layout.grabberHeight),
678
698
 
@@ -684,6 +704,15 @@ private final class SheetViewController: UIViewController, UIGestureRecognizerDe
684
704
  }
685
705
 
686
706
  @objc private func handleBackdropTap() {
707
+ requestDismiss()
708
+ }
709
+
710
+ @objc private func handleGrabberTap() {
711
+ requestDismiss()
712
+ }
713
+
714
+ private func requestDismiss() {
715
+ guard !isAnimatingDismissal else { return }
687
716
  dismissSheet { [weak self] in
688
717
  self?.onDismiss?()
689
718
  }
@@ -713,9 +742,7 @@ private final class SheetViewController: UIViewController, UIGestureRecognizerDe
713
742
  velocityY > Layout.dismissVelocityThreshold
714
743
 
715
744
  if shouldDismiss {
716
- dismissSheet { [weak self] in
717
- self?.onDismiss?()
718
- }
745
+ requestDismiss()
719
746
  } else {
720
747
  let targetDetent: Detent
721
748
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-native-sheet-emojis",
3
- "version": "2.0.4",
3
+ "version": "2.1.0",
4
4
  "description": "A fully native emoji picker bottom sheet for React Native. Built with Swift and Kotlin for maximum performance. Features search with multilingual keywords, skin tones, frequently used tracking, theming, and configurable layout.",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",