@sesionario/blocks 1.9.0 → 1.11.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.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import * as React6 from 'react';
2
+ import * as React7 from 'react';
3
3
  import { createContext, useContext, useState, useRef, useEffect, useLayoutEffect, useCallback, useId, useMemo } from 'react';
4
4
  import { ChevronsUpDown, CheckIcon, Smile, SearchIcon, Search, Clock, Flag, Hash, Lightbulb, Trophy, Plane, Apple, Leaf, Hand, Loader2, AlertTriangle, Inbox, X, ChevronLeft, ChevronRight, Monitor, Sun, Moon, Building2, Check, Plus, Languages, Upload, Move, ZoomOut, ZoomIn, RotateCcw, RotateCw, Grid3X3, RefreshCw, User, Pencil, Play, AlertCircle, VolumeX, Pause, Volume1, Volume2, Settings, PictureInPicture2, Minimize, Maximize, BadgeCheck, ArrowDownIcon, MessageCircleDashedIcon, MessageCircle, XIcon } from 'lucide-react';
5
5
  import { clsx } from 'clsx';
@@ -490,6 +490,9 @@ function ExpandableText({
490
490
  var MIN_SCALE = 1;
491
491
  var MAX_SCALE = 4;
492
492
  var DOUBLE_TAP_MS = 300;
493
+ var SWIPE_LOCK_PX = 8;
494
+ var SWIPE_COMMIT_PX = 56;
495
+ var SWIPE_SNAP_MS = 180;
493
496
  var IDENTITY = { scale: 1, tx: 0, ty: 0 };
494
497
  function clamp(value, min, max) {
495
498
  return Math.min(max, Math.max(min, value));
@@ -507,6 +510,10 @@ function zoomToward(prev, nextScale, originX, originY) {
507
510
  function pointerDistance(a, b) {
508
511
  return Math.hypot(b.x - a.x, b.y - a.y);
509
512
  }
513
+ function prefersReducedMotion() {
514
+ if (typeof window === "undefined" || !window.matchMedia) return false;
515
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
516
+ }
510
517
  function ImageLightbox({
511
518
  images,
512
519
  index,
@@ -522,15 +529,24 @@ function ImageLightbox({
522
529
  const [transform, setTransform] = useState(IDENTITY);
523
530
  const transformRef = useRef(transform);
524
531
  transformRef.current = transform;
532
+ const [isSwipeDragging, setIsSwipeDragging] = useState(false);
533
+ const [snapBack, setSnapBack] = useState(false);
525
534
  const stageRef = useRef(null);
526
535
  const pointersRef = useRef(/* @__PURE__ */ new Map());
527
536
  const pinchStartRef = useRef(null);
528
537
  const panRef = useRef(null);
538
+ const swipeRef = useRef(null);
529
539
  const lastTapRef = useRef(
530
540
  null
531
541
  );
542
+ const indexRef = useRef(index);
543
+ indexRef.current = index;
544
+ const imagesLengthRef = useRef(images.length);
545
+ imagesLengthRef.current = images.length;
532
546
  const resetZoom = useCallback(() => {
533
547
  setTransform(IDENTITY);
548
+ setIsSwipeDragging(false);
549
+ setSnapBack(false);
534
550
  }, []);
535
551
  useEffect(() => {
536
552
  resetZoom();
@@ -587,6 +603,10 @@ function ImageLightbox({
587
603
  stage.addEventListener("wheel", handleWheel, { passive: false });
588
604
  return () => stage.removeEventListener("wheel", handleWheel);
589
605
  }, [stageOrigin]);
606
+ function clearSwipe() {
607
+ swipeRef.current = null;
608
+ setIsSwipeDragging(false);
609
+ }
590
610
  function beginPinch() {
591
611
  const pts = [...pointersRef.current.values()];
592
612
  const [a, b] = pts;
@@ -601,13 +621,18 @@ function ImageLightbox({
601
621
  transform: { ...transformRef.current }
602
622
  };
603
623
  panRef.current = null;
624
+ clearSwipe();
625
+ setSnapBack(false);
604
626
  }
605
627
  function onPointerDown(e) {
606
628
  e.stopPropagation();
607
629
  const stage = stageRef.current;
608
630
  if (!stage) return;
609
- stage.setPointerCapture(e.pointerId);
631
+ if (typeof stage.setPointerCapture === "function") {
632
+ stage.setPointerCapture(e.pointerId);
633
+ }
610
634
  pointersRef.current.set(e.pointerId, { x: e.clientX, y: e.clientY });
635
+ setSnapBack(false);
611
636
  if (pointersRef.current.size === 2) {
612
637
  beginPinch();
613
638
  return;
@@ -621,6 +646,17 @@ function ImageLightbox({
621
646
  originTy: transformRef.current.ty,
622
647
  moved: false
623
648
  };
649
+ return;
650
+ }
651
+ if (hasMultiple) {
652
+ swipeRef.current = {
653
+ pointerId: e.pointerId,
654
+ startX: e.clientX,
655
+ startY: e.clientY,
656
+ moved: false,
657
+ axis: null
658
+ };
659
+ setIsSwipeDragging(true);
624
660
  }
625
661
  }
626
662
  function onPointerMove(e) {
@@ -645,19 +681,62 @@ function ImageLightbox({
645
681
  }
646
682
  const pan = panRef.current;
647
683
  if (pan && pan.pointerId === e.pointerId) {
648
- const dx = e.clientX - pan.startX;
649
- const dy = e.clientY - pan.startY;
650
- if (Math.abs(dx) > 2 || Math.abs(dy) > 2) pan.moved = true;
684
+ const dx2 = e.clientX - pan.startX;
685
+ const dy2 = e.clientY - pan.startY;
686
+ if (Math.abs(dx2) > 2 || Math.abs(dy2) > 2) pan.moved = true;
651
687
  setTransform((prev) => ({
652
688
  ...prev,
653
- tx: pan.originTx + dx,
654
- ty: pan.originTy + dy
689
+ tx: pan.originTx + dx2,
690
+ ty: pan.originTy + dy2
655
691
  }));
692
+ return;
693
+ }
694
+ const swipe = swipeRef.current;
695
+ if (!swipe || swipe.pointerId !== e.pointerId) return;
696
+ const dx = e.clientX - swipe.startX;
697
+ const dy = e.clientY - swipe.startY;
698
+ if (Math.abs(dx) > 2 || Math.abs(dy) > 2) swipe.moved = true;
699
+ if (swipe.axis === null) {
700
+ const absX = Math.abs(dx);
701
+ const absY = Math.abs(dy);
702
+ if (absX < SWIPE_LOCK_PX && absY < SWIPE_LOCK_PX) return;
703
+ swipe.axis = absX >= absY ? "x" : "y";
704
+ if (swipe.axis === "y") {
705
+ clearSwipe();
706
+ setTransform(IDENTITY);
707
+ return;
708
+ }
709
+ }
710
+ if (swipe.axis === "x") {
711
+ setTransform({ scale: MIN_SCALE, tx: dx, ty: 0 });
656
712
  }
657
713
  }
714
+ function finishSwipe(e) {
715
+ const swipe = swipeRef.current;
716
+ if (!swipe || swipe.pointerId !== e.pointerId) return false;
717
+ const dx = e.clientX - swipe.startX;
718
+ const wasMoved = swipe.moved;
719
+ const axis = swipe.axis;
720
+ clearSwipe();
721
+ if (axis === "x" && Math.abs(dx) >= SWIPE_COMMIT_PX) {
722
+ const length = imagesLengthRef.current;
723
+ const current = indexRef.current;
724
+ if (dx < 0) {
725
+ onIndexChange((current + 1) % length);
726
+ } else {
727
+ onIndexChange((current - 1 + length) % length);
728
+ }
729
+ return true;
730
+ }
731
+ if (wasMoved || transformRef.current.tx !== 0) {
732
+ if (!prefersReducedMotion()) setSnapBack(true);
733
+ setTransform(IDENTITY);
734
+ }
735
+ return wasMoved;
736
+ }
658
737
  function onPointerUp(e) {
659
738
  const stage = stageRef.current;
660
- if (stage?.hasPointerCapture(e.pointerId)) {
739
+ if (stage && typeof stage.hasPointerCapture === "function" && stage.hasPointerCapture(e.pointerId) && typeof stage.releasePointerCapture === "function") {
661
740
  stage.releasePointerCapture(e.pointerId);
662
741
  }
663
742
  pointersRef.current.delete(e.pointerId);
@@ -678,6 +757,8 @@ function ImageLightbox({
678
757
  };
679
758
  }
680
759
  }
760
+ const swipeHandled = finishSwipe(e);
761
+ if (swipeHandled) return;
681
762
  const pan = panRef.current;
682
763
  if (pan && pan.pointerId === e.pointerId) {
683
764
  const wasMoved = pan.moved;
@@ -699,6 +780,7 @@ function ImageLightbox({
699
780
  e.stopPropagation();
700
781
  }
701
782
  const { scale, tx, ty } = transform;
783
+ const transition = snapBack && !isSwipeDragging && !prefersReducedMotion() ? `transform ${SWIPE_SNAP_MS}ms ease-out` : void 0;
702
784
  return createPortal(
703
785
  /* @__PURE__ */ jsxs(
704
786
  "div",
@@ -738,6 +820,7 @@ function ImageLightbox({
738
820
  ref: stageRef,
739
821
  className: "relative flex max-h-full max-w-full touch-none items-center justify-center",
740
822
  "data-scale": scale,
823
+ "data-swipe-dragging": isSwipeDragging ? "true" : "false",
741
824
  onClick: stopDialogClose,
742
825
  onPointerDown,
743
826
  onPointerMove,
@@ -752,8 +835,10 @@ function ImageLightbox({
752
835
  className: "max-h-[calc(100dvh-2rem)] max-w-full select-none rounded-lg object-contain",
753
836
  style: {
754
837
  transform: `translate(${tx}px, ${ty}px) scale(${scale})`,
838
+ transition,
755
839
  cursor: scale > MIN_SCALE ? "grab" : "default"
756
- }
840
+ },
841
+ onTransitionEnd: () => setSnapBack(false)
757
842
  }
758
843
  )
759
844
  }
@@ -1489,7 +1574,7 @@ function Slider({
1489
1574
  max = 100,
1490
1575
  ...props
1491
1576
  }) {
1492
- const _values = React6.useMemo(
1577
+ const _values = React7.useMemo(
1493
1578
  () => Array.isArray(value) ? value : Array.isArray(defaultValue) ? defaultValue : [min, max],
1494
1579
  [value, defaultValue, min, max]
1495
1580
  );
@@ -2618,22 +2703,22 @@ function EmojiPicker({
2618
2703
  recentLimit = 24,
2619
2704
  storageKey = "blocks-emoji"
2620
2705
  }) {
2621
- const strings = React6.useMemo(
2706
+ const strings = React7.useMemo(
2622
2707
  () => ({ ...DEFAULT_LABELS[locale], ...labels }),
2623
2708
  [locale, labels]
2624
2709
  );
2625
- const titles = React6.useMemo(
2710
+ const titles = React7.useMemo(
2626
2711
  () => ({ ...DEFAULT_TITLES[locale], ...labels?.categoryTitles }),
2627
2712
  [locale, labels]
2628
2713
  );
2629
- const [query, setQuery] = React6.useState("");
2630
- const [skinTone, setSkinTone] = React6.useState(() => {
2714
+ const [query, setQuery] = React7.useState("");
2715
+ const [skinTone, setSkinTone] = React7.useState(() => {
2631
2716
  const raw = readStored(storageKey, "tone");
2632
2717
  const parsed = raw != null ? Number.parseInt(raw, 10) : 0;
2633
2718
  return parsed >= 0 && parsed <= 5 ? parsed : 0;
2634
2719
  });
2635
- const [toneOpen, setToneOpen] = React6.useState(false);
2636
- const [recents, setRecents] = React6.useState(() => {
2720
+ const [toneOpen, setToneOpen] = React7.useState(false);
2721
+ const [recents, setRecents] = React7.useState(() => {
2637
2722
  const raw = readStored(storageKey, "recent");
2638
2723
  if (!raw) return [];
2639
2724
  try {
@@ -2643,13 +2728,13 @@ function EmojiPicker({
2643
2728
  return [];
2644
2729
  }
2645
2730
  });
2646
- const [focusIndex, setFocusIndex] = React6.useState(-1);
2647
- const scrollRef = React6.useRef(null);
2648
- const sectionRefs = React6.useRef(
2731
+ const [focusIndex, setFocusIndex] = React7.useState(-1);
2732
+ const scrollRef = React7.useRef(null);
2733
+ const sectionRefs = React7.useRef(
2649
2734
  /* @__PURE__ */ new Map()
2650
2735
  );
2651
2736
  const normalizedQuery = normalizeEmojiQuery(query);
2652
- const sections = React6.useMemo(() => {
2737
+ const sections = React7.useMemo(() => {
2653
2738
  if (normalizedQuery) return [];
2654
2739
  const result = [];
2655
2740
  if (recents.length > 0) {
@@ -2668,7 +2753,7 @@ function EmojiPicker({
2668
2753
  }
2669
2754
  return result;
2670
2755
  }, [normalizedQuery, recents, titles]);
2671
- const searchResults = React6.useMemo(() => {
2756
+ const searchResults = React7.useMemo(() => {
2672
2757
  if (!normalizedQuery) return [];
2673
2758
  const out = [];
2674
2759
  for (const category of EMOJI_CATEGORIES) {
@@ -2678,21 +2763,21 @@ function EmojiPicker({
2678
2763
  }
2679
2764
  return out;
2680
2765
  }, [normalizedQuery]);
2681
- const flatList = React6.useMemo(() => {
2766
+ const flatList = React7.useMemo(() => {
2682
2767
  if (normalizedQuery) return searchResults;
2683
2768
  return sections.flatMap((s) => s.emojis);
2684
2769
  }, [normalizedQuery, searchResults, sections]);
2685
- React6.useEffect(() => {
2770
+ React7.useEffect(() => {
2686
2771
  setFocusIndex(-1);
2687
2772
  }, [normalizedQuery]);
2688
- React6.useEffect(() => {
2773
+ React7.useEffect(() => {
2689
2774
  if (focusIndex < 0) return;
2690
2775
  const el = scrollRef.current?.querySelector(
2691
2776
  `[data-emoji-index="${focusIndex}"]`
2692
2777
  );
2693
2778
  el?.focus();
2694
2779
  }, [focusIndex]);
2695
- const commitRecent = React6.useCallback(
2780
+ const commitRecent = React7.useCallback(
2696
2781
  (glyph) => {
2697
2782
  setRecents((prev) => {
2698
2783
  const next = [glyph, ...prev.filter((e) => e !== glyph)].slice(
@@ -2705,14 +2790,14 @@ function EmojiPicker({
2705
2790
  },
2706
2791
  [recentLimit, storageKey]
2707
2792
  );
2708
- const handleSelect = React6.useCallback(
2793
+ const handleSelect = React7.useCallback(
2709
2794
  (glyph) => {
2710
2795
  commitRecent(glyph);
2711
2796
  onSelect(glyph);
2712
2797
  },
2713
2798
  [commitRecent, onSelect]
2714
2799
  );
2715
- const chooseTone = React6.useCallback(
2800
+ const chooseTone = React7.useCallback(
2716
2801
  (tone) => {
2717
2802
  setSkinTone(tone);
2718
2803
  setToneOpen(false);
@@ -3018,17 +3103,17 @@ function EmojiPickerPopover({
3018
3103
  locale = "en",
3019
3104
  ...pickerProps
3020
3105
  }) {
3021
- const [internalOpen, setInternalOpen] = React6.useState(false);
3106
+ const [internalOpen, setInternalOpen] = React7.useState(false);
3022
3107
  const isControlled = open !== void 0;
3023
3108
  const isOpen = isControlled ? open : internalOpen;
3024
- const setOpen = React6.useCallback(
3109
+ const setOpen = React7.useCallback(
3025
3110
  (next) => {
3026
3111
  if (!isControlled) setInternalOpen(next);
3027
3112
  onOpenChange?.(next);
3028
3113
  },
3029
3114
  [isControlled, onOpenChange]
3030
3115
  );
3031
- const handleSelect = React6.useCallback(
3116
+ const handleSelect = React7.useCallback(
3032
3117
  (emoji) => {
3033
3118
  onSelect(emoji);
3034
3119
  if (!keepOpenOnSelect) setOpen(false);
@@ -3061,8 +3146,10 @@ function insertAtCaret(element, value, text) {
3061
3146
  caret: start + text.length
3062
3147
  };
3063
3148
  }
3064
- var wrapperClassName = "relative w-full rounded-md border border-input bg-transparent shadow-xs transition-[color,box-shadow] focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50 has-[[aria-invalid=true]]:border-destructive has-[[aria-invalid=true]]:ring-destructive/20 has-[:disabled]:opacity-50 dark:bg-input/30 dark:has-[[aria-invalid=true]]:ring-destructive/40";
3065
- var fieldResetClassName = "border-0 bg-transparent shadow-none focus-visible:border-transparent focus-visible:ring-0 dark:bg-transparent";
3149
+
3150
+ // src/components/emoji/emoji-field-shared.ts
3151
+ var emojiFieldWrapperClassName = "relative w-full rounded-md border border-input bg-transparent shadow-xs transition-[color,box-shadow] focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50 has-[[aria-invalid=true]]:border-destructive has-[[aria-invalid=true]]:ring-destructive/20 has-[:disabled]:opacity-50 dark:bg-input/30 dark:has-[[aria-invalid=true]]:ring-destructive/40";
3152
+ var emojiFieldResetClassName = "border-0 bg-transparent shadow-none focus-visible:border-transparent focus-visible:ring-0 dark:bg-transparent";
3066
3153
  function mergeRefs(...refs) {
3067
3154
  return (node) => {
3068
3155
  for (const ref of refs) {
@@ -3086,7 +3173,7 @@ function emitChange(element, nextValue, onChange) {
3086
3173
  });
3087
3174
  }
3088
3175
  function useEmojiInsert(fieldRef, value, onChange) {
3089
- return React6.useCallback(
3176
+ return React7.useCallback(
3090
3177
  (emoji) => {
3091
3178
  const el = fieldRef.current;
3092
3179
  const current = value !== void 0 && value !== null ? String(value) : el?.value ?? "";
@@ -3107,7 +3194,7 @@ function useEmojiInsert(fieldRef, value, onChange) {
3107
3194
  [fieldRef, value, onChange]
3108
3195
  );
3109
3196
  }
3110
- var EmojiTextarea = React6.forwardRef(
3197
+ var EmojiTextarea = React7.forwardRef(
3111
3198
  function EmojiTextarea2({
3112
3199
  className,
3113
3200
  locale = "en",
@@ -3127,10 +3214,10 @@ var EmojiTextarea = React6.forwardRef(
3127
3214
  emojiTriggerClassName,
3128
3215
  ...props
3129
3216
  }, ref) {
3130
- const fieldRef = React6.useRef(null);
3217
+ const fieldRef = React7.useRef(null);
3131
3218
  const handleEmojiSelect = useEmojiInsert(fieldRef, value, onChange);
3132
3219
  const pickerDisabled = Boolean(disabled || readOnly);
3133
- return /* @__PURE__ */ jsxs("div", { className: cn(wrapperClassName, className), children: [
3220
+ return /* @__PURE__ */ jsxs("div", { className: cn(emojiFieldWrapperClassName, className), children: [
3134
3221
  /* @__PURE__ */ jsx(
3135
3222
  "textarea",
3136
3223
  {
@@ -3142,7 +3229,7 @@ var EmojiTextarea = React6.forwardRef(
3142
3229
  onChange,
3143
3230
  className: cn(
3144
3231
  "placeholder:text-muted-foreground flex min-h-20 w-full rounded-md px-3 py-2 pb-9 pr-10 text-base outline-none transition-[color,box-shadow] disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
3145
- fieldResetClassName,
3232
+ emojiFieldResetClassName,
3146
3233
  className
3147
3234
  ),
3148
3235
  ...props
@@ -3173,7 +3260,7 @@ var EmojiTextarea = React6.forwardRef(
3173
3260
  }
3174
3261
  );
3175
3262
  EmojiTextarea.displayName = "EmojiTextarea";
3176
- var EmojiInput = React6.forwardRef(
3263
+ var EmojiInput = React7.forwardRef(
3177
3264
  function EmojiInput2({
3178
3265
  className,
3179
3266
  locale = "en",
@@ -3194,10 +3281,10 @@ var EmojiInput = React6.forwardRef(
3194
3281
  emojiTriggerClassName,
3195
3282
  ...props
3196
3283
  }, ref) {
3197
- const fieldRef = React6.useRef(null);
3284
+ const fieldRef = React7.useRef(null);
3198
3285
  const handleEmojiSelect = useEmojiInsert(fieldRef, value, onChange);
3199
3286
  const pickerDisabled = Boolean(disabled || readOnly);
3200
- return /* @__PURE__ */ jsxs("div", { className: cn(wrapperClassName, className), children: [
3287
+ return /* @__PURE__ */ jsxs("div", { className: cn(emojiFieldWrapperClassName, className), children: [
3201
3288
  /* @__PURE__ */ jsx(
3202
3289
  "input",
3203
3290
  {
@@ -3210,7 +3297,7 @@ var EmojiInput = React6.forwardRef(
3210
3297
  onChange,
3211
3298
  className: cn(
3212
3299
  "placeholder:text-muted-foreground flex h-9 w-full min-w-0 rounded-md px-3 py-1 pr-10 text-base outline-none transition-[color,box-shadow] selection:bg-primary selection:text-primary-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
3213
- fieldResetClassName,
3300
+ emojiFieldResetClassName,
3214
3301
  className
3215
3302
  ),
3216
3303
  ...props
@@ -3402,27 +3489,27 @@ function useHlsPlayback({
3402
3489
  onError,
3403
3490
  onPlayingChange
3404
3491
  }) {
3405
- const videoRef = React6.useRef(null);
3406
- const hlsRef = React6.useRef(null);
3407
- const [state, setState] = React6.useState("idle");
3408
- const [qualities, setQualities] = React6.useState([]);
3409
- const [activeLevel, setActiveLevel] = React6.useState(-1);
3410
- const [autoplayState, setAutoplayState] = React6.useState("none");
3411
- const [error, setError] = React6.useState(null);
3412
- const [reloadKey, setReloadKey] = React6.useState(0);
3413
- const onErrorRef = React6.useRef(onError);
3414
- const onPlayingChangeRef = React6.useRef(onPlayingChange);
3415
- React6.useEffect(() => {
3492
+ const videoRef = React7.useRef(null);
3493
+ const hlsRef = React7.useRef(null);
3494
+ const [state, setState] = React7.useState("idle");
3495
+ const [qualities, setQualities] = React7.useState([]);
3496
+ const [activeLevel, setActiveLevel] = React7.useState(-1);
3497
+ const [autoplayState, setAutoplayState] = React7.useState("none");
3498
+ const [error, setError] = React7.useState(null);
3499
+ const [reloadKey, setReloadKey] = React7.useState(0);
3500
+ const onErrorRef = React7.useRef(onError);
3501
+ const onPlayingChangeRef = React7.useRef(onPlayingChange);
3502
+ React7.useEffect(() => {
3416
3503
  onErrorRef.current = onError;
3417
3504
  onPlayingChangeRef.current = onPlayingChange;
3418
3505
  }, [onError, onPlayingChange]);
3419
- const retry = React6.useCallback(() => {
3506
+ const retry = React7.useCallback(() => {
3420
3507
  setError(null);
3421
3508
  setState("loading");
3422
3509
  setAutoplayState("none");
3423
3510
  setReloadKey((k) => k + 1);
3424
3511
  }, []);
3425
- React6.useEffect(() => {
3512
+ React7.useEffect(() => {
3426
3513
  const media = videoRef.current;
3427
3514
  if (!media || !src || !active) {
3428
3515
  setState("idle");
@@ -3634,7 +3721,7 @@ function useHlsPlayback({
3634
3721
  destroyHls();
3635
3722
  };
3636
3723
  }, [src, mode, active, autoPlay, muted, reloadKey]);
3637
- const setLevel = React6.useCallback((index) => {
3724
+ const setLevel = React7.useCallback((index) => {
3638
3725
  const hls = hlsRef.current;
3639
3726
  if (!hls) return;
3640
3727
  hls.nextLevel = index;
@@ -3679,6 +3766,19 @@ var PLAYBACK_RATES = [0.75, 1, 1.25, 1.5, 2];
3679
3766
  var CONTROLS_HIDE_MS = 3e3;
3680
3767
  var LIVE_EDGE_THRESHOLD_S = 8;
3681
3768
  var SEEK_STEP_S = 5;
3769
+ function asWebkitVideo(video) {
3770
+ return video;
3771
+ }
3772
+ function hasWebkitVideoFullscreen(video) {
3773
+ const webkit = asWebkitVideo(video);
3774
+ return typeof webkit?.webkitEnterFullscreen === "function";
3775
+ }
3776
+ function hasStandardFullscreen() {
3777
+ if (typeof document === "undefined") return false;
3778
+ if (document.fullscreenEnabled) return true;
3779
+ const doc = document;
3780
+ return !!doc.webkitFullscreenEnabled;
3781
+ }
3682
3782
  function formatTime(seconds) {
3683
3783
  if (!Number.isFinite(seconds) || seconds < 0) return "0:00";
3684
3784
  const total = Math.floor(seconds);
@@ -3704,12 +3804,12 @@ function VideoPlayer({
3704
3804
  className
3705
3805
  }) {
3706
3806
  const t = { ...DEFAULT_LABELS2, ...labels };
3707
- const containerRef = React6.useRef(null);
3708
- const firstPlayFiredRef = React6.useRef(false);
3709
- const [userStarted, setUserStarted] = React6.useState(false);
3807
+ const containerRef = React7.useRef(null);
3808
+ const firstPlayFiredRef = React7.useRef(false);
3809
+ const [userStarted, setUserStarted] = React7.useState(false);
3710
3810
  const active = autoPlay || !poster || userStarted;
3711
3811
  const autoplayActive = autoPlay || userStarted;
3712
- const handlePlayingChange = React6.useCallback(
3812
+ const handlePlayingChange = React7.useCallback(
3713
3813
  (playing) => {
3714
3814
  setIsPlaying(playing);
3715
3815
  if (playing && !firstPlayFiredRef.current) {
@@ -3738,25 +3838,25 @@ function VideoPlayer({
3738
3838
  onError,
3739
3839
  onPlayingChange: handlePlayingChange
3740
3840
  });
3741
- const [isPlaying, setIsPlaying] = React6.useState(false);
3742
- const [isMuted, setIsMuted] = React6.useState(muted);
3743
- const [volume, setVolume] = React6.useState(1);
3744
- const [currentTime, setCurrentTime] = React6.useState(0);
3745
- const [duration, setDuration] = React6.useState(0);
3746
- const [buffered, setBuffered] = React6.useState(0);
3747
- const [isFullscreen, setIsFullscreen] = React6.useState(false);
3748
- const [pipActive, setPipActive] = React6.useState(false);
3749
- const [controlsVisible, setControlsVisible] = React6.useState(true);
3750
- const [playbackRate, setPlaybackRate] = React6.useState(1);
3751
- const [openMenu, setOpenMenu] = React6.useState(
3841
+ const [isPlaying, setIsPlaying] = React7.useState(false);
3842
+ const [isMuted, setIsMuted] = React7.useState(muted);
3843
+ const [volume, setVolume] = React7.useState(1);
3844
+ const [currentTime, setCurrentTime] = React7.useState(0);
3845
+ const [duration, setDuration] = React7.useState(0);
3846
+ const [buffered, setBuffered] = React7.useState(0);
3847
+ const [isFullscreen, setIsFullscreen] = React7.useState(false);
3848
+ const [pipActive, setPipActive] = React7.useState(false);
3849
+ const [controlsVisible, setControlsVisible] = React7.useState(true);
3850
+ const [playbackRate, setPlaybackRate] = React7.useState(1);
3851
+ const [openMenu, setOpenMenu] = React7.useState(
3752
3852
  null
3753
3853
  );
3754
- const [behindLive, setBehindLive] = React6.useState(false);
3755
- const hideTimerRef = React6.useRef(null);
3854
+ const [behindLive, setBehindLive] = React7.useState(false);
3855
+ const [webkitFullscreenSupported, setWebkitFullscreenSupported] = React7.useState(false);
3856
+ const hideTimerRef = React7.useRef(null);
3756
3857
  const pipSupported = typeof document !== "undefined" && "pictureInPictureEnabled" in document && document.pictureInPictureEnabled;
3757
- const fullscreenSupported = typeof document !== "undefined" && (!!document.fullscreenEnabled || // Safari
3758
- "webkitFullscreenEnabled" in document);
3759
- React6.useEffect(() => {
3858
+ const fullscreenSupported = hasStandardFullscreen() || webkitFullscreenSupported;
3859
+ React7.useEffect(() => {
3760
3860
  const video = videoRef.current;
3761
3861
  if (!video || !active) return;
3762
3862
  const onTimeUpdate = () => {
@@ -3789,12 +3889,27 @@ function VideoPlayer({
3789
3889
  video.removeEventListener("ratechange", onRate);
3790
3890
  };
3791
3891
  }, [videoRef, active, mode]);
3792
- React6.useEffect(() => {
3892
+ React7.useEffect(() => {
3893
+ setWebkitFullscreenSupported(hasWebkitVideoFullscreen(videoRef.current));
3894
+ }, [videoRef, active]);
3895
+ React7.useEffect(() => {
3793
3896
  const onFsChange = () => setIsFullscreen(document.fullscreenElement === containerRef.current);
3794
3897
  document.addEventListener("fullscreenchange", onFsChange);
3795
3898
  return () => document.removeEventListener("fullscreenchange", onFsChange);
3796
3899
  }, []);
3797
- React6.useEffect(() => {
3900
+ React7.useEffect(() => {
3901
+ const video = asWebkitVideo(videoRef.current);
3902
+ if (!video) return;
3903
+ const onBegin = () => setIsFullscreen(true);
3904
+ const onEnd = () => setIsFullscreen(false);
3905
+ video.addEventListener("webkitbeginfullscreen", onBegin);
3906
+ video.addEventListener("webkitendfullscreen", onEnd);
3907
+ return () => {
3908
+ video.removeEventListener("webkitbeginfullscreen", onBegin);
3909
+ video.removeEventListener("webkitendfullscreen", onEnd);
3910
+ };
3911
+ }, [videoRef, active]);
3912
+ React7.useEffect(() => {
3798
3913
  const video = videoRef.current;
3799
3914
  if (!video) return;
3800
3915
  const onEnter = () => setPipActive(true);
@@ -3806,18 +3921,18 @@ function VideoPlayer({
3806
3921
  video.removeEventListener("leavepictureinpicture", onLeave);
3807
3922
  };
3808
3923
  }, [videoRef]);
3809
- const scheduleHide = React6.useCallback(() => {
3924
+ const scheduleHide = React7.useCallback(() => {
3810
3925
  if (hideTimerRef.current) clearTimeout(hideTimerRef.current);
3811
3926
  hideTimerRef.current = setTimeout(() => {
3812
3927
  setControlsVisible(false);
3813
3928
  setOpenMenu(null);
3814
3929
  }, CONTROLS_HIDE_MS);
3815
3930
  }, []);
3816
- const revealControls = React6.useCallback(() => {
3931
+ const revealControls = React7.useCallback(() => {
3817
3932
  setControlsVisible(true);
3818
3933
  scheduleHide();
3819
3934
  }, [scheduleHide]);
3820
- React6.useEffect(() => {
3935
+ React7.useEffect(() => {
3821
3936
  return () => {
3822
3937
  if (hideTimerRef.current) clearTimeout(hideTimerRef.current);
3823
3938
  };
@@ -3858,11 +3973,21 @@ function VideoPlayer({
3858
3973
  }
3859
3974
  function toggleFullscreen() {
3860
3975
  const el = containerRef.current;
3861
- if (!el) return;
3862
- if (document.fullscreenElement === el) {
3976
+ const video = asWebkitVideo(videoRef.current);
3977
+ if (el && document.fullscreenElement === el) {
3863
3978
  void document.exitFullscreen();
3864
- } else {
3865
- void el.requestFullscreen?.();
3979
+ return;
3980
+ }
3981
+ if (video?.webkitDisplayingFullscreen && video.webkitExitFullscreen) {
3982
+ video.webkitExitFullscreen();
3983
+ return;
3984
+ }
3985
+ if (el && hasStandardFullscreen() && typeof el.requestFullscreen === "function") {
3986
+ void el.requestFullscreen();
3987
+ return;
3988
+ }
3989
+ if (video && typeof video.webkitEnterFullscreen === "function") {
3990
+ video.webkitEnterFullscreen();
3866
3991
  }
3867
3992
  }
3868
3993
  function togglePip() {
@@ -4270,8 +4395,8 @@ function MenuItem({
4270
4395
  );
4271
4396
  }
4272
4397
  function usePlaybackStats(statsUrl, { intervalMs = 15e3, enabled = true } = {}) {
4273
- const [stats, setStats] = React6.useState({});
4274
- React6.useEffect(() => {
4398
+ const [stats, setStats] = React7.useState({});
4399
+ React7.useEffect(() => {
4275
4400
  if (!statsUrl || !enabled) return;
4276
4401
  let cancelled = false;
4277
4402
  const controller = new AbortController();
@@ -5296,7 +5421,7 @@ function ScrollBar({
5296
5421
  );
5297
5422
  }
5298
5423
  var phoneInputGroupClass = "flex h-9 w-full gap-0 overflow-hidden rounded-md border border-input bg-transparent shadow-xs transition-[color,box-shadow] [--PhoneInputCountrySelect-marginRight:0] focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50 dark:bg-input/30 [&_.PhoneInputCountry]:mr-0";
5299
- var PhoneInput = React6.forwardRef(
5424
+ var PhoneInput = React7.forwardRef(
5300
5425
  ({ className, onChange, value, defaultCountry = "MX", ...props }, ref) => {
5301
5426
  return /* @__PURE__ */ jsx(
5302
5427
  RPNInput.default,
@@ -5316,7 +5441,7 @@ var PhoneInput = React6.forwardRef(
5316
5441
  }
5317
5442
  );
5318
5443
  PhoneInput.displayName = "PhoneInput";
5319
- var InputComponent = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
5444
+ var InputComponent = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
5320
5445
  Input,
5321
5446
  {
5322
5447
  className: cn(
@@ -5334,9 +5459,9 @@ var CountrySelect = ({
5334
5459
  options: countryList,
5335
5460
  onChange
5336
5461
  }) => {
5337
- const scrollAreaRef = React6.useRef(null);
5338
- const [searchValue, setSearchValue] = React6.useState("");
5339
- const [isOpen, setIsOpen] = React6.useState(false);
5462
+ const scrollAreaRef = React7.useRef(null);
5463
+ const [searchValue, setSearchValue] = React7.useState("");
5464
+ const [isOpen, setIsOpen] = React7.useState(false);
5340
5465
  return /* @__PURE__ */ jsxs(
5341
5466
  Popover,
5342
5467
  {
@@ -5517,8 +5642,8 @@ var MEXICO_STATE_PATHS = {
5517
5642
  };
5518
5643
  function MexicoMap({ selected, onToggle, className }) {
5519
5644
  const isInteractive = typeof onToggle === "function";
5520
- const selectedSet = React6.useMemo(() => new Set(selected), [selected]);
5521
- const handleKeyDown = React6.useCallback(
5645
+ const selectedSet = React7.useMemo(() => new Set(selected), [selected]);
5646
+ const handleKeyDown = React7.useCallback(
5522
5647
  (event, id) => {
5523
5648
  if (!onToggle) return;
5524
5649
  if (event.key === "Enter" || event.key === " ") {
@@ -6000,6 +6125,121 @@ function ChatMessageList({
6000
6125
  /* @__PURE__ */ jsx(MessageScrollerButton, {})
6001
6126
  ] }) });
6002
6127
  }
6128
+ var MessageComposer = React7.forwardRef(function MessageComposer2({
6129
+ className,
6130
+ locale = "en",
6131
+ disabled,
6132
+ readOnly,
6133
+ value,
6134
+ onChange,
6135
+ onSubmit,
6136
+ onKeyDown,
6137
+ sendLabel,
6138
+ sendDisabled = false,
6139
+ sendButtonClassName,
6140
+ sendButtonSize = "sm",
6141
+ triggerLabel,
6142
+ keepOpenOnSelect,
6143
+ open,
6144
+ onOpenChange,
6145
+ side = "top",
6146
+ align = "end",
6147
+ labels,
6148
+ recentLimit,
6149
+ storageKey,
6150
+ emojiTriggerClassName,
6151
+ rows = 2,
6152
+ ...props
6153
+ }, ref) {
6154
+ const formRef = React7.useRef(null);
6155
+ const fieldRef = React7.useRef(null);
6156
+ const handleEmojiSelect = useEmojiInsert(fieldRef, value, onChange);
6157
+ const pickerDisabled = Boolean(disabled || readOnly);
6158
+ const draft = value !== void 0 && value !== null ? String(value) : "";
6159
+ const canSubmit = Boolean(draft.trim()) && !sendDisabled && !disabled && !readOnly;
6160
+ const handleSubmit = React7.useCallback(
6161
+ (event) => {
6162
+ event.preventDefault();
6163
+ if (!canSubmit) return;
6164
+ onSubmit(event);
6165
+ },
6166
+ [canSubmit, onSubmit]
6167
+ );
6168
+ const handleKeyDown = React7.useCallback(
6169
+ (event) => {
6170
+ onKeyDown?.(event);
6171
+ if (event.defaultPrevented) return;
6172
+ if (event.key !== "Enter" || event.shiftKey) return;
6173
+ if (event.nativeEvent.isComposing || event.keyCode === 229) return;
6174
+ event.preventDefault();
6175
+ if (!canSubmit) return;
6176
+ formRef.current?.requestSubmit();
6177
+ },
6178
+ [canSubmit, onKeyDown]
6179
+ );
6180
+ return /* @__PURE__ */ jsxs(
6181
+ "form",
6182
+ {
6183
+ ref: formRef,
6184
+ className: cn(emojiFieldWrapperClassName, "flex flex-col", className),
6185
+ onSubmit: handleSubmit,
6186
+ children: [
6187
+ /* @__PURE__ */ jsx(
6188
+ "textarea",
6189
+ {
6190
+ "data-slot": "textarea",
6191
+ ref: mergeRefs(fieldRef, ref),
6192
+ disabled,
6193
+ readOnly,
6194
+ value,
6195
+ onChange,
6196
+ onKeyDown: handleKeyDown,
6197
+ rows,
6198
+ className: cn(
6199
+ "placeholder:text-muted-foreground flex min-h-10 w-full resize-none rounded-md px-3 pt-2 pb-1 text-base outline-none transition-[color,box-shadow] disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
6200
+ emojiFieldResetClassName
6201
+ ),
6202
+ ...props
6203
+ }
6204
+ ),
6205
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 px-1.5 pb-1.5", children: [
6206
+ /* @__PURE__ */ jsx(
6207
+ EmojiPickerPopover,
6208
+ {
6209
+ locale,
6210
+ onSelect: handleEmojiSelect,
6211
+ disabled: pickerDisabled,
6212
+ triggerLabel,
6213
+ keepOpenOnSelect,
6214
+ open,
6215
+ onOpenChange,
6216
+ side,
6217
+ align,
6218
+ labels,
6219
+ recentLimit,
6220
+ storageKey,
6221
+ triggerClassName: cn(
6222
+ "size-7 text-muted-foreground",
6223
+ emojiTriggerClassName
6224
+ )
6225
+ }
6226
+ ),
6227
+ /* @__PURE__ */ jsx(
6228
+ Button,
6229
+ {
6230
+ type: "submit",
6231
+ size: sendButtonSize,
6232
+ disabled: !canSubmit,
6233
+ className: sendButtonClassName,
6234
+ children: sendLabel
6235
+ }
6236
+ )
6237
+ ] })
6238
+ ]
6239
+ }
6240
+ );
6241
+ });
6242
+ MessageComposer.displayName = "MessageComposer";
6003
6243
  var RECONNECT_DELAY_MS = 2e3;
6004
6244
  function useSupportConversation({
6005
6245
  client,
@@ -6220,23 +6460,22 @@ function ThreadPanel({
6220
6460
  emptyDescription: labels.emptyThread
6221
6461
  }
6222
6462
  ) }),
6223
- /* @__PURE__ */ jsxs("form", { className: "flex gap-2 border-t p-3", onSubmit, children: [
6224
- /* @__PURE__ */ jsx(
6225
- EmojiTextarea,
6226
- {
6227
- locale: emojiLocale,
6228
- value: draft,
6229
- onChange: (e) => setDraft(e.target.value.slice(0, 2e3)),
6230
- placeholder: labels.placeholder,
6231
- "aria-label": labels.placeholder,
6232
- disabled: loading || sending,
6233
- maxLength: 2e3,
6234
- rows: 2,
6235
- className: "min-h-10 flex-1"
6236
- }
6237
- ),
6238
- /* @__PURE__ */ jsx(Button, { type: "submit", disabled: !draft.trim() || sending || loading, children: labels.send })
6239
- ] })
6463
+ /* @__PURE__ */ jsx("div", { className: "border-t p-3", children: /* @__PURE__ */ jsx(
6464
+ MessageComposer,
6465
+ {
6466
+ locale: emojiLocale,
6467
+ value: draft,
6468
+ onChange: (e) => setDraft(e.target.value.slice(0, 2e3)),
6469
+ onSubmit,
6470
+ sendLabel: labels.send,
6471
+ placeholder: labels.placeholder,
6472
+ "aria-label": labels.placeholder,
6473
+ disabled: loading || sending,
6474
+ sendDisabled: loading || sending,
6475
+ maxLength: 2e3,
6476
+ rows: 2
6477
+ }
6478
+ ) })
6240
6479
  ]
6241
6480
  }
6242
6481
  );
@@ -6618,6 +6857,6 @@ function createSupportChatClient(basePath = "/api/chat-svc") {
6618
6857
  };
6619
6858
  }
6620
6859
 
6621
- export { AvatarEditor, AvatarEditorDialog, Bubble, BubbleContent, BubbleGroup, BubbleReactions, ChatMessageList, ConfirmDestructiveDialog, EmojiInput, EmojiPicker, EmojiPickerPopover, EmojiTextarea, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyState, EmptyTitle, ErrorState, ExpandableText, ImageLightbox, LanguageContext, LanguageSwitcher, LoadingState, MEXICO_STATES, MEXICO_STATE_PATHS, Marker, MarkerContent, MarkerIcon, Message, MessageAvatar, MessageContent, MessageFooter, MessageGroup, MessageHeader, MessageScroller, MessageScrollerButton, MessageScrollerContent, MessageScrollerItem, MessageScrollerProvider, MessageScrollerViewport, MexicoMap, OrgSwitcher, PhoneInput, SearchCommand, SearchablePicker, SupportMessengerProvider, ThemeSwitcher, VerifiedProfessionalBadge, VideoPlayer, avatarFallbackStyle, bubbleVariants, cn, createLiveHlsConfig, createSupportChatClient, createVodHlsConfig, defaultLanguages, describePlaybackError, fuzzyMatch, getAvatarColors, getInitials, insertAtCaret, markerVariants, normalizeEmojiQuery, shouldRetryLiveEdge404, useDebouncedValue, useDebouncedValueStrict, useHlsPlayback, useLanguageContext, useMediaQuery2 as useMediaQuery, usePlaybackStats, useSearchHotkey, useSupportConversation, useSupportMessenger, useSupportMessengerOptional };
6860
+ export { AvatarEditor, AvatarEditorDialog, Bubble, BubbleContent, BubbleGroup, BubbleReactions, ChatMessageList, ConfirmDestructiveDialog, EmojiInput, EmojiPicker, EmojiPickerPopover, EmojiTextarea, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyState, EmptyTitle, ErrorState, ExpandableText, ImageLightbox, LanguageContext, LanguageSwitcher, LoadingState, MEXICO_STATES, MEXICO_STATE_PATHS, Marker, MarkerContent, MarkerIcon, Message, MessageAvatar, MessageComposer, MessageContent, MessageFooter, MessageGroup, MessageHeader, MessageScroller, MessageScrollerButton, MessageScrollerContent, MessageScrollerItem, MessageScrollerProvider, MessageScrollerViewport, MexicoMap, OrgSwitcher, PhoneInput, SearchCommand, SearchablePicker, SupportMessengerProvider, ThemeSwitcher, VerifiedProfessionalBadge, VideoPlayer, avatarFallbackStyle, bubbleVariants, cn, createLiveHlsConfig, createSupportChatClient, createVodHlsConfig, defaultLanguages, describePlaybackError, fuzzyMatch, getAvatarColors, getInitials, insertAtCaret, markerVariants, normalizeEmojiQuery, shouldRetryLiveEdge404, useDebouncedValue, useDebouncedValueStrict, useHlsPlayback, useLanguageContext, useMediaQuery2 as useMediaQuery, usePlaybackStats, useSearchHotkey, useSupportConversation, useSupportMessenger, useSupportMessengerOptional };
6622
6861
  //# sourceMappingURL=index.js.map
6623
6862
  //# sourceMappingURL=index.js.map