@sesionario/blocks 1.8.0 → 1.10.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,6 +1,6 @@
1
1
  "use client";
2
2
  import * as React6 from 'react';
3
- import { createContext, useContext, useState, useRef, useEffect, useLayoutEffect, useCallback, useMemo } from 'react';
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';
6
6
  import { twMerge } from 'tailwind-merge';
@@ -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 });
712
+ }
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;
656
730
  }
731
+ if (wasMoved || transformRef.current.tx !== 0) {
732
+ if (!prefersReducedMotion()) setSnapBack(true);
733
+ setTransform(IDENTITY);
734
+ }
735
+ return wasMoved;
657
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
  }
@@ -1380,7 +1465,7 @@ function LanguageSwitcher({
1380
1465
  const sizes = sizeClasses2[size];
1381
1466
  const shapeClass = shapeClasses2[shape];
1382
1467
  const reduceMotion = useReducedMotion();
1383
- const defaultLabels4 = {
1468
+ const defaultLabels5 = {
1384
1469
  language: labels.language ?? "Language"
1385
1470
  };
1386
1471
  if (variant === "mini") {
@@ -1390,7 +1475,7 @@ function LanguageSwitcher({
1390
1475
  {
1391
1476
  variant: "ghost",
1392
1477
  size: "icon",
1393
- "aria-label": defaultLabels4.language,
1478
+ "aria-label": defaultLabels5.language,
1394
1479
  className: cn(sizes.buttonMini, shapeClass, className),
1395
1480
  children: /* @__PURE__ */ jsx(Languages, { className: sizes.iconMini })
1396
1481
  }
@@ -3679,6 +3764,19 @@ var PLAYBACK_RATES = [0.75, 1, 1.25, 1.5, 2];
3679
3764
  var CONTROLS_HIDE_MS = 3e3;
3680
3765
  var LIVE_EDGE_THRESHOLD_S = 8;
3681
3766
  var SEEK_STEP_S = 5;
3767
+ function asWebkitVideo(video) {
3768
+ return video;
3769
+ }
3770
+ function hasWebkitVideoFullscreen(video) {
3771
+ const webkit = asWebkitVideo(video);
3772
+ return typeof webkit?.webkitEnterFullscreen === "function";
3773
+ }
3774
+ function hasStandardFullscreen() {
3775
+ if (typeof document === "undefined") return false;
3776
+ if (document.fullscreenEnabled) return true;
3777
+ const doc = document;
3778
+ return !!doc.webkitFullscreenEnabled;
3779
+ }
3682
3780
  function formatTime(seconds) {
3683
3781
  if (!Number.isFinite(seconds) || seconds < 0) return "0:00";
3684
3782
  const total = Math.floor(seconds);
@@ -3752,10 +3850,10 @@ function VideoPlayer({
3752
3850
  null
3753
3851
  );
3754
3852
  const [behindLive, setBehindLive] = React6.useState(false);
3853
+ const [webkitFullscreenSupported, setWebkitFullscreenSupported] = React6.useState(false);
3755
3854
  const hideTimerRef = React6.useRef(null);
3756
3855
  const pipSupported = typeof document !== "undefined" && "pictureInPictureEnabled" in document && document.pictureInPictureEnabled;
3757
- const fullscreenSupported = typeof document !== "undefined" && (!!document.fullscreenEnabled || // Safari
3758
- "webkitFullscreenEnabled" in document);
3856
+ const fullscreenSupported = hasStandardFullscreen() || webkitFullscreenSupported;
3759
3857
  React6.useEffect(() => {
3760
3858
  const video = videoRef.current;
3761
3859
  if (!video || !active) return;
@@ -3789,11 +3887,26 @@ function VideoPlayer({
3789
3887
  video.removeEventListener("ratechange", onRate);
3790
3888
  };
3791
3889
  }, [videoRef, active, mode]);
3890
+ React6.useEffect(() => {
3891
+ setWebkitFullscreenSupported(hasWebkitVideoFullscreen(videoRef.current));
3892
+ }, [videoRef, active]);
3792
3893
  React6.useEffect(() => {
3793
3894
  const onFsChange = () => setIsFullscreen(document.fullscreenElement === containerRef.current);
3794
3895
  document.addEventListener("fullscreenchange", onFsChange);
3795
3896
  return () => document.removeEventListener("fullscreenchange", onFsChange);
3796
3897
  }, []);
3898
+ React6.useEffect(() => {
3899
+ const video = asWebkitVideo(videoRef.current);
3900
+ if (!video) return;
3901
+ const onBegin = () => setIsFullscreen(true);
3902
+ const onEnd = () => setIsFullscreen(false);
3903
+ video.addEventListener("webkitbeginfullscreen", onBegin);
3904
+ video.addEventListener("webkitendfullscreen", onEnd);
3905
+ return () => {
3906
+ video.removeEventListener("webkitbeginfullscreen", onBegin);
3907
+ video.removeEventListener("webkitendfullscreen", onEnd);
3908
+ };
3909
+ }, [videoRef, active]);
3797
3910
  React6.useEffect(() => {
3798
3911
  const video = videoRef.current;
3799
3912
  if (!video) return;
@@ -3858,11 +3971,21 @@ function VideoPlayer({
3858
3971
  }
3859
3972
  function toggleFullscreen() {
3860
3973
  const el = containerRef.current;
3861
- if (!el) return;
3862
- if (document.fullscreenElement === el) {
3974
+ const video = asWebkitVideo(videoRef.current);
3975
+ if (el && document.fullscreenElement === el) {
3863
3976
  void document.exitFullscreen();
3864
- } else {
3865
- void el.requestFullscreen?.();
3977
+ return;
3978
+ }
3979
+ if (video?.webkitDisplayingFullscreen && video.webkitExitFullscreen) {
3980
+ video.webkitExitFullscreen();
3981
+ return;
3982
+ }
3983
+ if (el && hasStandardFullscreen() && typeof el.requestFullscreen === "function") {
3984
+ void el.requestFullscreen();
3985
+ return;
3986
+ }
3987
+ if (video && typeof video.webkitEnterFullscreen === "function") {
3988
+ video.webkitEnterFullscreen();
3866
3989
  }
3867
3990
  }
3868
3991
  function togglePip() {
@@ -4317,6 +4440,18 @@ function useDebouncedValueStrict(value, delayMs) {
4317
4440
  }, [value, delayMs]);
4318
4441
  return debounced;
4319
4442
  }
4443
+ function useMediaQuery2(query) {
4444
+ const [matches, setMatches] = useState(false);
4445
+ useEffect(() => {
4446
+ if (typeof window === "undefined" || !window.matchMedia) return;
4447
+ const mql = window.matchMedia(query);
4448
+ const onChange = () => setMatches(mql.matches);
4449
+ onChange();
4450
+ mql.addEventListener("change", onChange);
4451
+ return () => mql.removeEventListener("change", onChange);
4452
+ }, [query]);
4453
+ return matches;
4454
+ }
4320
4455
  function useSearchHotkey(options = {}) {
4321
4456
  const { defaultOpen = false, disabled = false } = options;
4322
4457
  const [open, setOpen] = useState(defaultOpen);
@@ -4334,6 +4469,22 @@ function useSearchHotkey(options = {}) {
4334
4469
  }, [disabled]);
4335
4470
  return { open, setOpen, toggle };
4336
4471
  }
4472
+
4473
+ // src/components/search/fuzzy.ts
4474
+ function normalize(value) {
4475
+ return value.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
4476
+ }
4477
+ function fuzzyMatch(query, target) {
4478
+ const q = normalize(query.trim());
4479
+ if (!q) return true;
4480
+ const t = normalize(target);
4481
+ if (t.includes(q)) return true;
4482
+ let qi = 0;
4483
+ for (let ti = 0; ti < t.length && qi < q.length; ti++) {
4484
+ if (t[ti] === q[qi]) qi++;
4485
+ }
4486
+ return qi === q.length;
4487
+ }
4337
4488
  function Command({
4338
4489
  className,
4339
4490
  ...props
@@ -4451,6 +4602,19 @@ function CommandGroup({
4451
4602
  }
4452
4603
  );
4453
4604
  }
4605
+ function CommandSeparator({
4606
+ className,
4607
+ ...props
4608
+ }) {
4609
+ return /* @__PURE__ */ jsx(
4610
+ Command$1.Separator,
4611
+ {
4612
+ "data-slot": "command-separator",
4613
+ className: cn("-mx-1 h-px bg-border", className),
4614
+ ...props
4615
+ }
4616
+ );
4617
+ }
4454
4618
  function CommandItem({
4455
4619
  className,
4456
4620
  ...props
@@ -4467,23 +4631,584 @@ function CommandItem({
4467
4631
  }
4468
4632
  );
4469
4633
  }
4470
-
4471
- // src/components/search/fuzzy.ts
4472
- function normalize(value) {
4473
- return value.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
4634
+ function OptionRow({
4635
+ entry,
4636
+ selected,
4637
+ onSelect,
4638
+ renderOption
4639
+ }) {
4640
+ return /* @__PURE__ */ jsxs(
4641
+ CommandItem,
4642
+ {
4643
+ value: entry.value,
4644
+ keywords: [entry.label],
4645
+ onSelect,
4646
+ "data-checked": selected || void 0,
4647
+ children: [
4648
+ renderOption ? renderOption(entry.item, { selected }) : /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: entry.label }),
4649
+ /* @__PURE__ */ jsx(
4650
+ CheckIcon,
4651
+ {
4652
+ className: cn(
4653
+ "ml-auto size-4 shrink-0",
4654
+ selected ? "opacity-100" : "opacity-0"
4655
+ )
4656
+ }
4657
+ )
4658
+ ]
4659
+ },
4660
+ entry.value
4661
+ );
4474
4662
  }
4475
- function fuzzyMatch(query, target) {
4476
- const q = normalize(query.trim());
4477
- if (!q) return true;
4478
- const t = normalize(target);
4479
- if (t.includes(q)) return true;
4480
- let qi = 0;
4481
- for (let ti = 0; ti < t.length && qi < q.length; ti++) {
4482
- if (t[ti] === q[qi]) qi++;
4483
- }
4484
- return qi === q.length;
4663
+ function SearchablePickerList({
4664
+ query,
4665
+ onQueryChange,
4666
+ labels,
4667
+ isLoading,
4668
+ hasError,
4669
+ recommended,
4670
+ recent,
4671
+ results,
4672
+ selectedValues,
4673
+ mode,
4674
+ allowEmpty,
4675
+ emptyLabel,
4676
+ onSelectEmpty,
4677
+ onSelectItem,
4678
+ renderOption,
4679
+ createOption,
4680
+ onLoadMore,
4681
+ hasMore,
4682
+ isLoadingMore,
4683
+ listClassName
4684
+ }) {
4685
+ const hasPinned = recommended.length > 0 || recent.length > 0;
4686
+ const hasResults = results.length > 0;
4687
+ const showEmpty = !isLoading && !hasError && !hasPinned && !hasResults && !(allowEmpty && mode === "single");
4688
+ return /* @__PURE__ */ jsxs(Command, { shouldFilter: false, className: "flex h-full min-h-0 flex-col", children: [
4689
+ /* @__PURE__ */ jsx(
4690
+ CommandInput,
4691
+ {
4692
+ placeholder: labels.searchPlaceholder,
4693
+ value: query,
4694
+ onValueChange: onQueryChange
4695
+ }
4696
+ ),
4697
+ /* @__PURE__ */ jsxs(CommandList, { className: cn("min-h-0 flex-1", listClassName), children: [
4698
+ isLoading ? /* @__PURE__ */ jsxs(
4699
+ "div",
4700
+ {
4701
+ className: "flex items-center justify-center gap-2 py-6 text-sm text-muted-foreground",
4702
+ role: "status",
4703
+ children: [
4704
+ /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": true }),
4705
+ labels.loading
4706
+ ]
4707
+ }
4708
+ ) : null,
4709
+ hasError && !isLoading ? /* @__PURE__ */ jsx(
4710
+ "div",
4711
+ {
4712
+ className: "py-6 text-center text-sm text-destructive",
4713
+ role: "alert",
4714
+ children: labels.error
4715
+ }
4716
+ ) : null,
4717
+ showEmpty ? /* @__PURE__ */ jsx(CommandEmpty, { children: labels.empty }) : null,
4718
+ allowEmpty && mode === "single" && !query.trim() ? /* @__PURE__ */ jsx(CommandGroup, { children: /* @__PURE__ */ jsxs(
4719
+ CommandItem,
4720
+ {
4721
+ value: "__empty__",
4722
+ onSelect: () => onSelectEmpty?.(),
4723
+ "data-checked": !selectedValues.size || void 0,
4724
+ children: [
4725
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-muted-foreground", children: emptyLabel }),
4726
+ /* @__PURE__ */ jsx(
4727
+ CheckIcon,
4728
+ {
4729
+ className: cn(
4730
+ "ml-auto size-4 shrink-0",
4731
+ selectedValues.size === 0 ? "opacity-100" : "opacity-0"
4732
+ )
4733
+ }
4734
+ )
4735
+ ]
4736
+ }
4737
+ ) }) : null,
4738
+ recommended.length > 0 ? /* @__PURE__ */ jsx(CommandGroup, { heading: labels.recommended, children: recommended.map((entry) => /* @__PURE__ */ jsx(
4739
+ OptionRow,
4740
+ {
4741
+ entry,
4742
+ selected: selectedValues.has(entry.value),
4743
+ onSelect: () => onSelectItem(entry.value),
4744
+ renderOption
4745
+ },
4746
+ `rec-${entry.value}`
4747
+ )) }) : null,
4748
+ recent.length > 0 ? /* @__PURE__ */ jsx(CommandGroup, { heading: labels.recent, children: recent.map((entry) => /* @__PURE__ */ jsx(
4749
+ OptionRow,
4750
+ {
4751
+ entry,
4752
+ selected: selectedValues.has(entry.value),
4753
+ onSelect: () => onSelectItem(entry.value),
4754
+ renderOption
4755
+ },
4756
+ `recent-${entry.value}`
4757
+ )) }) : null,
4758
+ hasPinned && hasResults ? /* @__PURE__ */ jsx(CommandSeparator, {}) : null,
4759
+ hasResults ? /* @__PURE__ */ jsx(CommandGroup, { heading: hasPinned ? labels.results : void 0, children: results.map((entry) => /* @__PURE__ */ jsx(
4760
+ OptionRow,
4761
+ {
4762
+ entry,
4763
+ selected: selectedValues.has(entry.value),
4764
+ onSelect: () => onSelectItem(entry.value),
4765
+ renderOption
4766
+ },
4767
+ `res-${entry.value}`
4768
+ )) }) : null,
4769
+ hasMore && onLoadMore ? /* @__PURE__ */ jsx("div", { className: "p-2", children: /* @__PURE__ */ jsx(
4770
+ Button,
4771
+ {
4772
+ type: "button",
4773
+ variant: "ghost",
4774
+ size: "sm",
4775
+ className: "w-full",
4776
+ disabled: isLoadingMore,
4777
+ onClick: () => void onLoadMore(),
4778
+ children: isLoadingMore ? /* @__PURE__ */ jsxs(Fragment, { children: [
4779
+ /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": true }),
4780
+ labels.loading
4781
+ ] }) : labels.loadMore
4782
+ }
4783
+ ) }) : null,
4784
+ createOption ? /* @__PURE__ */ jsxs(Fragment, { children: [
4785
+ /* @__PURE__ */ jsx(CommandSeparator, {}),
4786
+ /* @__PURE__ */ jsx(CommandGroup, { children: /* @__PURE__ */ jsx(
4787
+ CommandItem,
4788
+ {
4789
+ value: "__create__",
4790
+ onSelect: () => createOption.onSelect(),
4791
+ children: createOption.label
4792
+ }
4793
+ ) })
4794
+ ] }) : null
4795
+ ] })
4796
+ ] });
4485
4797
  }
4486
4798
  var defaultLabels3 = {
4799
+ placeholder: "Seleccionar\u2026",
4800
+ searchPlaceholder: "Buscar\u2026",
4801
+ empty: "No se encontraron resultados",
4802
+ loading: "Cargando\u2026",
4803
+ error: "No se pudo cargar la lista",
4804
+ recommended: "Recomendados",
4805
+ recent: "Recientes",
4806
+ results: "Resultados",
4807
+ done: "Listo",
4808
+ cancel: "Cancelar",
4809
+ clear: "Limpiar",
4810
+ selectedCount: (count) => count === 1 ? "1 seleccionado" : `${count} seleccionados`,
4811
+ emptyOption: "Ninguno",
4812
+ loadMore: "Cargar m\xE1s",
4813
+ dialogTitle: "Seleccionar"
4814
+ };
4815
+ function toListItems(items, getOptionValue, getOptionLabel) {
4816
+ if (!items?.length) return [];
4817
+ return items.map((item) => ({
4818
+ item,
4819
+ value: getOptionValue(item),
4820
+ label: getOptionLabel(item)
4821
+ }));
4822
+ }
4823
+ function dedupeByValue(items, exclude = /* @__PURE__ */ new Set()) {
4824
+ const seen = new Set(exclude);
4825
+ const out = [];
4826
+ for (const entry of items) {
4827
+ if (seen.has(entry.value)) continue;
4828
+ seen.add(entry.value);
4829
+ out.push(entry);
4830
+ }
4831
+ return out;
4832
+ }
4833
+ function matchesQuery(entry, query, getOptionKeywords) {
4834
+ if (!query.trim()) return true;
4835
+ if (fuzzyMatch(query, entry.label)) return true;
4836
+ return getOptionKeywords?.(entry.item)?.some(
4837
+ (keyword) => fuzzyMatch(query, keyword)
4838
+ ) ?? false;
4839
+ }
4840
+ function SearchablePicker(props) {
4841
+ const {
4842
+ options,
4843
+ fetchOptions,
4844
+ getOptionValue,
4845
+ getOptionLabel,
4846
+ getOptionKeywords,
4847
+ renderOption,
4848
+ renderValue,
4849
+ pinned,
4850
+ labels: userLabels,
4851
+ minQueryLength = 0,
4852
+ debounceMs = 200,
4853
+ disabled,
4854
+ open: openProp,
4855
+ onOpenChange,
4856
+ clearable,
4857
+ allowEmpty,
4858
+ emptyLabel,
4859
+ keepOpenOnSelect,
4860
+ maxSelected,
4861
+ createOption,
4862
+ onLoadMore,
4863
+ hasMore,
4864
+ className,
4865
+ triggerClassName: triggerClassName2,
4866
+ id,
4867
+ "aria-labelledby": ariaLabelledBy,
4868
+ "aria-label": ariaLabel
4869
+ } = props;
4870
+ const mode = props.mode ?? "single";
4871
+ const labels = { ...defaultLabels3, ...userLabels };
4872
+ const resolvedEmptyLabel = emptyLabel ?? labels.emptyOption;
4873
+ const isMobile = useMediaQuery2("(max-width: 767px)");
4874
+ const generatedId = useId();
4875
+ const triggerId = id ?? generatedId;
4876
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
4877
+ const open = openProp ?? uncontrolledOpen;
4878
+ const setOpen = useCallback(
4879
+ (next) => {
4880
+ onOpenChange?.(next);
4881
+ if (openProp === void 0) setUncontrolledOpen(next);
4882
+ },
4883
+ [onOpenChange, openProp]
4884
+ );
4885
+ const [query, setQuery] = useState("");
4886
+ const [remoteItems, setRemoteItems] = useState([]);
4887
+ const [isLoading, setIsLoading] = useState(false);
4888
+ const [hasError, setHasError] = useState(false);
4889
+ const [isLoadingMore, setIsLoadingMore] = useState(false);
4890
+ const debouncedQuery = useDebouncedValue(query, debounceMs);
4891
+ const requestIdRef = useRef(0);
4892
+ const knownItemsRef = useRef(/* @__PURE__ */ new Map());
4893
+ const rememberItems = useCallback(
4894
+ (items) => {
4895
+ for (const item of items) {
4896
+ knownItemsRef.current.set(getOptionValue(item), item);
4897
+ }
4898
+ },
4899
+ [getOptionValue]
4900
+ );
4901
+ useEffect(() => {
4902
+ if (options) rememberItems(options);
4903
+ if (pinned?.recommended) rememberItems(pinned.recommended);
4904
+ if (pinned?.recent) rememberItems(pinned.recent);
4905
+ }, [options, pinned, rememberItems]);
4906
+ useEffect(() => {
4907
+ if (!open) {
4908
+ setQuery("");
4909
+ setRemoteItems([]);
4910
+ setHasError(false);
4911
+ setIsLoading(false);
4912
+ }
4913
+ }, [open]);
4914
+ const trimmedDebounced = debouncedQuery.trim();
4915
+ const shouldFetch = Boolean(fetchOptions) && open && trimmedDebounced.length >= minQueryLength && (trimmedDebounced.length > 0 || !options);
4916
+ useEffect(() => {
4917
+ if (!shouldFetch || !fetchOptions) {
4918
+ if (!trimmedDebounced) {
4919
+ setRemoteItems([]);
4920
+ setHasError(false);
4921
+ setIsLoading(false);
4922
+ }
4923
+ return;
4924
+ }
4925
+ const requestId = ++requestIdRef.current;
4926
+ setIsLoading(true);
4927
+ setHasError(false);
4928
+ fetchOptions(trimmedDebounced).then((result) => {
4929
+ if (requestIdRef.current !== requestId) return;
4930
+ setRemoteItems(result);
4931
+ rememberItems(result);
4932
+ }).catch(() => {
4933
+ if (requestIdRef.current !== requestId) return;
4934
+ setRemoteItems([]);
4935
+ setHasError(true);
4936
+ }).finally(() => {
4937
+ if (requestIdRef.current !== requestId) return;
4938
+ setIsLoading(false);
4939
+ });
4940
+ }, [shouldFetch, fetchOptions, trimmedDebounced, rememberItems]);
4941
+ const selectedValues = useMemo(() => {
4942
+ if (mode === "multiple") {
4943
+ const multiValue = props.value ?? [];
4944
+ return new Set(multiValue);
4945
+ }
4946
+ const singleValue = props.value;
4947
+ return singleValue ? /* @__PURE__ */ new Set([singleValue]) : /* @__PURE__ */ new Set();
4948
+ }, [mode, props]);
4949
+ const selectedValueKey = mode === "multiple" ? (props.value ?? []).join("\0") : props.value ?? "";
4950
+ const selectedItems = useMemo(() => {
4951
+ const values = mode === "multiple" ? selectedValueKey ? selectedValueKey.split("\0") : [] : selectedValueKey ? [selectedValueKey] : [];
4952
+ return values.map((value) => knownItemsRef.current.get(value)).filter((item) => item !== void 0);
4953
+ }, [mode, selectedValueKey, options, pinned, remoteItems]);
4954
+ const queryForFilter = query.trim();
4955
+ const recommended = useMemo(() => {
4956
+ if (queryForFilter) return [];
4957
+ const pinnedValues = /* @__PURE__ */ new Set();
4958
+ const items = dedupeByValue(
4959
+ toListItems(pinned?.recommended, getOptionValue, getOptionLabel)
4960
+ );
4961
+ for (const entry of items) pinnedValues.add(entry.value);
4962
+ return items;
4963
+ }, [queryForFilter, pinned?.recommended, getOptionValue, getOptionLabel]);
4964
+ const recent = useMemo(() => {
4965
+ if (queryForFilter) return [];
4966
+ const exclude = new Set(recommended.map((e) => e.value));
4967
+ return dedupeByValue(
4968
+ toListItems(pinned?.recent, getOptionValue, getOptionLabel),
4969
+ exclude
4970
+ );
4971
+ }, [
4972
+ queryForFilter,
4973
+ pinned?.recent,
4974
+ recommended,
4975
+ getOptionValue,
4976
+ getOptionLabel
4977
+ ]);
4978
+ const pinnedValueSet = useMemo(() => {
4979
+ const set = /* @__PURE__ */ new Set();
4980
+ for (const entry of recommended) set.add(entry.value);
4981
+ for (const entry of recent) set.add(entry.value);
4982
+ return set;
4983
+ }, [recommended, recent]);
4984
+ const results = useMemo(() => {
4985
+ const usingRemote = Boolean(fetchOptions) && trimmedDebounced.length >= minQueryLength && (trimmedDebounced.length > 0 || !options);
4986
+ if (usingRemote) {
4987
+ return dedupeByValue(
4988
+ toListItems(remoteItems, getOptionValue, getOptionLabel),
4989
+ queryForFilter ? /* @__PURE__ */ new Set() : pinnedValueSet
4990
+ );
4991
+ }
4992
+ const staticItems = toListItems(options, getOptionValue, getOptionLabel);
4993
+ const filtered = staticItems.filter(
4994
+ (entry) => matchesQuery(entry, queryForFilter, getOptionKeywords)
4995
+ );
4996
+ return dedupeByValue(filtered, queryForFilter ? /* @__PURE__ */ new Set() : pinnedValueSet);
4997
+ }, [
4998
+ fetchOptions,
4999
+ trimmedDebounced,
5000
+ minQueryLength,
5001
+ options,
5002
+ remoteItems,
5003
+ getOptionValue,
5004
+ getOptionLabel,
5005
+ getOptionKeywords,
5006
+ queryForFilter,
5007
+ pinnedValueSet
5008
+ ]);
5009
+ const closeOnSelect = keepOpenOnSelect !== void 0 ? !keepOpenOnSelect : mode === "single";
5010
+ const handleSelectEmpty = useCallback(() => {
5011
+ if (mode === "single") {
5012
+ props.onValueChange?.(
5013
+ null
5014
+ );
5015
+ }
5016
+ if (closeOnSelect) setOpen(false);
5017
+ }, [mode, props, closeOnSelect, setOpen]);
5018
+ const handleSelectItem = useCallback(
5019
+ (value) => {
5020
+ if (mode === "multiple") {
5021
+ const current = props.value ?? [];
5022
+ const onChange = props.onValueChange;
5023
+ const exists = current.includes(value);
5024
+ if (exists) {
5025
+ onChange?.(current.filter((v) => v !== value));
5026
+ } else if (maxSelected !== void 0 && current.length >= maxSelected) {
5027
+ return;
5028
+ } else {
5029
+ onChange?.([...current, value]);
5030
+ }
5031
+ if (closeOnSelect) setOpen(false);
5032
+ return;
5033
+ }
5034
+ props.onValueChange?.(
5035
+ value
5036
+ );
5037
+ if (closeOnSelect) setOpen(false);
5038
+ },
5039
+ [mode, props, maxSelected, closeOnSelect, setOpen]
5040
+ );
5041
+ const handleClear = useCallback(
5042
+ (event) => {
5043
+ event.preventDefault();
5044
+ event.stopPropagation();
5045
+ if (mode === "multiple") {
5046
+ props.onValueChange?.(
5047
+ []
5048
+ );
5049
+ } else {
5050
+ props.onValueChange?.(null);
5051
+ }
5052
+ },
5053
+ [mode, props]
5054
+ );
5055
+ const handleLoadMore = useCallback(async () => {
5056
+ if (!onLoadMore || isLoadingMore) return;
5057
+ setIsLoadingMore(true);
5058
+ try {
5059
+ await onLoadMore();
5060
+ } finally {
5061
+ setIsLoadingMore(false);
5062
+ }
5063
+ }, [onLoadMore, isLoadingMore]);
5064
+ const triggerLabel = useMemo(() => {
5065
+ if (renderValue) {
5066
+ if (mode === "multiple") {
5067
+ return renderValue(selectedItems);
5068
+ }
5069
+ return renderValue(selectedItems[0] ?? null);
5070
+ }
5071
+ if (mode === "multiple") {
5072
+ const count = selectedValues.size;
5073
+ if (count === 0) {
5074
+ return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: labels.placeholder });
5075
+ }
5076
+ if (selectedItems.length === 1) {
5077
+ return getOptionLabel(selectedItems[0]);
5078
+ }
5079
+ return labels.selectedCount(count);
5080
+ }
5081
+ if (selectedValues.size === 0) {
5082
+ return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: allowEmpty ? resolvedEmptyLabel : labels.placeholder });
5083
+ }
5084
+ if (selectedItems[0]) {
5085
+ return getOptionLabel(selectedItems[0]);
5086
+ }
5087
+ const only = [...selectedValues][0];
5088
+ return only ?? labels.placeholder;
5089
+ }, [
5090
+ renderValue,
5091
+ mode,
5092
+ selectedItems,
5093
+ selectedValues,
5094
+ labels,
5095
+ getOptionLabel,
5096
+ allowEmpty,
5097
+ resolvedEmptyLabel
5098
+ ]);
5099
+ const showClear = clearable && !disabled && (mode === "multiple" ? selectedValues.size > 0 : selectedValues.size > 0);
5100
+ const list = /* @__PURE__ */ jsx(
5101
+ SearchablePickerList,
5102
+ {
5103
+ query,
5104
+ onQueryChange: setQuery,
5105
+ labels,
5106
+ isLoading,
5107
+ hasError,
5108
+ recommended,
5109
+ recent,
5110
+ results,
5111
+ selectedValues,
5112
+ mode,
5113
+ allowEmpty,
5114
+ emptyLabel: resolvedEmptyLabel,
5115
+ onSelectEmpty: handleSelectEmpty,
5116
+ onSelectItem: handleSelectItem,
5117
+ renderOption,
5118
+ createOption,
5119
+ onLoadMore: onLoadMore ? handleLoadMore : void 0,
5120
+ hasMore,
5121
+ isLoadingMore,
5122
+ listClassName: isMobile ? "max-h-[min(50vh,360px)]" : "max-h-72"
5123
+ }
5124
+ );
5125
+ const triggerButton = /* @__PURE__ */ jsxs(
5126
+ Button,
5127
+ {
5128
+ type: "button",
5129
+ variant: "outline",
5130
+ role: "combobox",
5131
+ id: triggerId,
5132
+ "aria-expanded": open,
5133
+ "aria-labelledby": ariaLabelledBy,
5134
+ "aria-label": ariaLabel ?? labels.placeholder,
5135
+ disabled,
5136
+ className: cn("w-full justify-between font-normal", triggerClassName2),
5137
+ children: [
5138
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: triggerLabel }),
5139
+ /* @__PURE__ */ jsx(ChevronsUpDown, { className: "ml-2 size-4 shrink-0 opacity-50", "aria-hidden": true })
5140
+ ]
5141
+ }
5142
+ );
5143
+ const clearButton = showClear ? /* @__PURE__ */ jsx(
5144
+ Button,
5145
+ {
5146
+ type: "button",
5147
+ variant: "ghost",
5148
+ size: "icon-sm",
5149
+ "aria-label": labels.clear,
5150
+ disabled,
5151
+ className: "shrink-0",
5152
+ onClick: handleClear,
5153
+ children: /* @__PURE__ */ jsx(X, { className: "size-4" })
5154
+ }
5155
+ ) : null;
5156
+ if (isMobile) {
5157
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex w-full items-center gap-1", className), children: [
5158
+ /* @__PURE__ */ jsx(
5159
+ "div",
5160
+ {
5161
+ className: "min-w-0 flex-1",
5162
+ onClick: () => {
5163
+ if (!disabled) setOpen(true);
5164
+ },
5165
+ children: triggerButton
5166
+ }
5167
+ ),
5168
+ clearButton,
5169
+ /* @__PURE__ */ jsx(Drawer, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(DrawerContent, { className: "max-h-[90vh]", children: [
5170
+ /* @__PURE__ */ jsx(DrawerHeader, { className: "text-left", children: /* @__PURE__ */ jsx(DrawerTitle, { children: labels.dialogTitle }) }),
5171
+ /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-hidden px-2 pb-2", children: list }),
5172
+ mode === "multiple" ? /* @__PURE__ */ jsxs(DrawerFooter, { className: "flex-row gap-2", children: [
5173
+ /* @__PURE__ */ jsx(
5174
+ Button,
5175
+ {
5176
+ type: "button",
5177
+ variant: "outline",
5178
+ className: "flex-1",
5179
+ onClick: () => setOpen(false),
5180
+ children: labels.cancel
5181
+ }
5182
+ ),
5183
+ /* @__PURE__ */ jsx(
5184
+ Button,
5185
+ {
5186
+ type: "button",
5187
+ className: "flex-1",
5188
+ onClick: () => setOpen(false),
5189
+ children: labels.done
5190
+ }
5191
+ )
5192
+ ] }) : null
5193
+ ] }) })
5194
+ ] });
5195
+ }
5196
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex w-full items-center gap-1", className), children: [
5197
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, modal: true, children: [
5198
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: triggerButton }),
5199
+ /* @__PURE__ */ jsx(
5200
+ PopoverContent,
5201
+ {
5202
+ className: "w-[var(--radix-popover-trigger-width)] min-w-[240px] p-0",
5203
+ align: "start",
5204
+ children: list
5205
+ }
5206
+ )
5207
+ ] }) }),
5208
+ clearButton
5209
+ ] });
5210
+ }
5211
+ var defaultLabels4 = {
4487
5212
  placeholder: "Buscar en toda la plataforma\u2026",
4488
5213
  dialogTitle: "Buscar",
4489
5214
  dialogDescription: "Busca funcionalidades, eventos, documentos y m\xE1s",
@@ -4517,7 +5242,7 @@ function SearchCommand({
4517
5242
  debounceMs = 200,
4518
5243
  className
4519
5244
  }) {
4520
- const labels = { ...defaultLabels3, ...userLabels };
5245
+ const labels = { ...defaultLabels4, ...userLabels };
4521
5246
  const [query, setQuery] = useState("");
4522
5247
  const [hits, setHits] = useState([]);
4523
5248
  const [isLoading, setIsLoading] = useState(false);
@@ -5398,18 +6123,6 @@ function ChatMessageList({
5398
6123
  /* @__PURE__ */ jsx(MessageScrollerButton, {})
5399
6124
  ] }) });
5400
6125
  }
5401
- function useMediaQuery2(query) {
5402
- const [matches, setMatches] = useState(false);
5403
- useEffect(() => {
5404
- if (typeof window === "undefined" || !window.matchMedia) return;
5405
- const mql = window.matchMedia(query);
5406
- const onChange = () => setMatches(mql.matches);
5407
- onChange();
5408
- mql.addEventListener("change", onChange);
5409
- return () => mql.removeEventListener("change", onChange);
5410
- }, [query]);
5411
- return matches;
5412
- }
5413
6126
  var RECONNECT_DELAY_MS = 2e3;
5414
6127
  function useSupportConversation({
5415
6128
  client,
@@ -6028,6 +6741,6 @@ function createSupportChatClient(basePath = "/api/chat-svc") {
6028
6741
  };
6029
6742
  }
6030
6743
 
6031
- 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, SupportMessengerProvider, ThemeSwitcher, VerifiedProfessionalBadge, VideoPlayer, avatarFallbackStyle, bubbleVariants, cn, createLiveHlsConfig, createSupportChatClient, createVodHlsConfig, defaultLanguages, describePlaybackError, fuzzyMatch, getAvatarColors, getInitials, insertAtCaret, markerVariants, normalizeEmojiQuery, shouldRetryLiveEdge404, useDebouncedValue, useDebouncedValueStrict, useHlsPlayback, useLanguageContext, usePlaybackStats, useSearchHotkey, useSupportConversation, useSupportMessenger, useSupportMessengerOptional };
6744
+ 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 };
6032
6745
  //# sourceMappingURL=index.js.map
6033
6746
  //# sourceMappingURL=index.js.map