@sesionario/blocks 1.7.0 → 1.9.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, useCallback, useEffect, 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';
@@ -425,6 +425,68 @@ function ConfirmDestructiveDialog({
425
425
  ] })
426
426
  ] }) });
427
427
  }
428
+ var COLLAPSED_MAX_HEIGHT_REM = 11;
429
+ function ExpandableText({
430
+ children,
431
+ expandLabel,
432
+ collapseLabel,
433
+ className,
434
+ textClassName
435
+ }) {
436
+ const [expanded, setExpanded] = useState(false);
437
+ const [overflowing, setOverflowing] = useState(false);
438
+ const contentRef = useRef(null);
439
+ useEffect(() => {
440
+ setExpanded(false);
441
+ }, [children]);
442
+ useLayoutEffect(() => {
443
+ const el = contentRef.current;
444
+ if (!el) return;
445
+ const measure = () => {
446
+ const rootFontSize = Number.parseFloat(
447
+ getComputedStyle(document.documentElement).fontSize
448
+ );
449
+ const maxPx = COLLAPSED_MAX_HEIGHT_REM * (rootFontSize || 16);
450
+ setOverflowing(el.scrollHeight > maxPx + 1);
451
+ };
452
+ measure();
453
+ const observer = new ResizeObserver(measure);
454
+ observer.observe(el);
455
+ return () => observer.disconnect();
456
+ }, [children]);
457
+ const collapsed = !expanded && overflowing;
458
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-1", className), children: [
459
+ /* @__PURE__ */ jsx(
460
+ "div",
461
+ {
462
+ className: cn(
463
+ collapsed && "max-h-[11rem] overflow-hidden scroll-fade-b"
464
+ ),
465
+ children: /* @__PURE__ */ jsx(
466
+ "p",
467
+ {
468
+ ref: contentRef,
469
+ className: cn(
470
+ "whitespace-pre-wrap text-sm leading-relaxed",
471
+ textClassName
472
+ ),
473
+ children
474
+ }
475
+ )
476
+ }
477
+ ),
478
+ overflowing ? /* @__PURE__ */ jsx(
479
+ "button",
480
+ {
481
+ type: "button",
482
+ className: "text-sm font-medium text-primary underline-offset-4 hover:underline focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 rounded-sm",
483
+ "aria-expanded": expanded,
484
+ onClick: () => setExpanded((prev) => !prev),
485
+ children: expanded ? collapseLabel : expandLabel
486
+ }
487
+ ) : null
488
+ ] });
489
+ }
428
490
  var MIN_SCALE = 1;
429
491
  var MAX_SCALE = 4;
430
492
  var DOUBLE_TAP_MS = 300;
@@ -1318,7 +1380,7 @@ function LanguageSwitcher({
1318
1380
  const sizes = sizeClasses2[size];
1319
1381
  const shapeClass = shapeClasses2[shape];
1320
1382
  const reduceMotion = useReducedMotion();
1321
- const defaultLabels4 = {
1383
+ const defaultLabels5 = {
1322
1384
  language: labels.language ?? "Language"
1323
1385
  };
1324
1386
  if (variant === "mini") {
@@ -1328,7 +1390,7 @@ function LanguageSwitcher({
1328
1390
  {
1329
1391
  variant: "ghost",
1330
1392
  size: "icon",
1331
- "aria-label": defaultLabels4.language,
1393
+ "aria-label": defaultLabels5.language,
1332
1394
  className: cn(sizes.buttonMini, shapeClass, className),
1333
1395
  children: /* @__PURE__ */ jsx(Languages, { className: sizes.iconMini })
1334
1396
  }
@@ -4255,6 +4317,18 @@ function useDebouncedValueStrict(value, delayMs) {
4255
4317
  }, [value, delayMs]);
4256
4318
  return debounced;
4257
4319
  }
4320
+ function useMediaQuery2(query) {
4321
+ const [matches, setMatches] = useState(false);
4322
+ useEffect(() => {
4323
+ if (typeof window === "undefined" || !window.matchMedia) return;
4324
+ const mql = window.matchMedia(query);
4325
+ const onChange = () => setMatches(mql.matches);
4326
+ onChange();
4327
+ mql.addEventListener("change", onChange);
4328
+ return () => mql.removeEventListener("change", onChange);
4329
+ }, [query]);
4330
+ return matches;
4331
+ }
4258
4332
  function useSearchHotkey(options = {}) {
4259
4333
  const { defaultOpen = false, disabled = false } = options;
4260
4334
  const [open, setOpen] = useState(defaultOpen);
@@ -4272,6 +4346,22 @@ function useSearchHotkey(options = {}) {
4272
4346
  }, [disabled]);
4273
4347
  return { open, setOpen, toggle };
4274
4348
  }
4349
+
4350
+ // src/components/search/fuzzy.ts
4351
+ function normalize(value) {
4352
+ return value.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
4353
+ }
4354
+ function fuzzyMatch(query, target) {
4355
+ const q = normalize(query.trim());
4356
+ if (!q) return true;
4357
+ const t = normalize(target);
4358
+ if (t.includes(q)) return true;
4359
+ let qi = 0;
4360
+ for (let ti = 0; ti < t.length && qi < q.length; ti++) {
4361
+ if (t[ti] === q[qi]) qi++;
4362
+ }
4363
+ return qi === q.length;
4364
+ }
4275
4365
  function Command({
4276
4366
  className,
4277
4367
  ...props
@@ -4389,6 +4479,19 @@ function CommandGroup({
4389
4479
  }
4390
4480
  );
4391
4481
  }
4482
+ function CommandSeparator({
4483
+ className,
4484
+ ...props
4485
+ }) {
4486
+ return /* @__PURE__ */ jsx(
4487
+ Command$1.Separator,
4488
+ {
4489
+ "data-slot": "command-separator",
4490
+ className: cn("-mx-1 h-px bg-border", className),
4491
+ ...props
4492
+ }
4493
+ );
4494
+ }
4392
4495
  function CommandItem({
4393
4496
  className,
4394
4497
  ...props
@@ -4405,23 +4508,584 @@ function CommandItem({
4405
4508
  }
4406
4509
  );
4407
4510
  }
4408
-
4409
- // src/components/search/fuzzy.ts
4410
- function normalize(value) {
4411
- return value.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
4511
+ function OptionRow({
4512
+ entry,
4513
+ selected,
4514
+ onSelect,
4515
+ renderOption
4516
+ }) {
4517
+ return /* @__PURE__ */ jsxs(
4518
+ CommandItem,
4519
+ {
4520
+ value: entry.value,
4521
+ keywords: [entry.label],
4522
+ onSelect,
4523
+ "data-checked": selected || void 0,
4524
+ children: [
4525
+ renderOption ? renderOption(entry.item, { selected }) : /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: entry.label }),
4526
+ /* @__PURE__ */ jsx(
4527
+ CheckIcon,
4528
+ {
4529
+ className: cn(
4530
+ "ml-auto size-4 shrink-0",
4531
+ selected ? "opacity-100" : "opacity-0"
4532
+ )
4533
+ }
4534
+ )
4535
+ ]
4536
+ },
4537
+ entry.value
4538
+ );
4412
4539
  }
4413
- function fuzzyMatch(query, target) {
4414
- const q = normalize(query.trim());
4415
- if (!q) return true;
4416
- const t = normalize(target);
4417
- if (t.includes(q)) return true;
4418
- let qi = 0;
4419
- for (let ti = 0; ti < t.length && qi < q.length; ti++) {
4420
- if (t[ti] === q[qi]) qi++;
4421
- }
4422
- return qi === q.length;
4540
+ function SearchablePickerList({
4541
+ query,
4542
+ onQueryChange,
4543
+ labels,
4544
+ isLoading,
4545
+ hasError,
4546
+ recommended,
4547
+ recent,
4548
+ results,
4549
+ selectedValues,
4550
+ mode,
4551
+ allowEmpty,
4552
+ emptyLabel,
4553
+ onSelectEmpty,
4554
+ onSelectItem,
4555
+ renderOption,
4556
+ createOption,
4557
+ onLoadMore,
4558
+ hasMore,
4559
+ isLoadingMore,
4560
+ listClassName
4561
+ }) {
4562
+ const hasPinned = recommended.length > 0 || recent.length > 0;
4563
+ const hasResults = results.length > 0;
4564
+ const showEmpty = !isLoading && !hasError && !hasPinned && !hasResults && !(allowEmpty && mode === "single");
4565
+ return /* @__PURE__ */ jsxs(Command, { shouldFilter: false, className: "flex h-full min-h-0 flex-col", children: [
4566
+ /* @__PURE__ */ jsx(
4567
+ CommandInput,
4568
+ {
4569
+ placeholder: labels.searchPlaceholder,
4570
+ value: query,
4571
+ onValueChange: onQueryChange
4572
+ }
4573
+ ),
4574
+ /* @__PURE__ */ jsxs(CommandList, { className: cn("min-h-0 flex-1", listClassName), children: [
4575
+ isLoading ? /* @__PURE__ */ jsxs(
4576
+ "div",
4577
+ {
4578
+ className: "flex items-center justify-center gap-2 py-6 text-sm text-muted-foreground",
4579
+ role: "status",
4580
+ children: [
4581
+ /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": true }),
4582
+ labels.loading
4583
+ ]
4584
+ }
4585
+ ) : null,
4586
+ hasError && !isLoading ? /* @__PURE__ */ jsx(
4587
+ "div",
4588
+ {
4589
+ className: "py-6 text-center text-sm text-destructive",
4590
+ role: "alert",
4591
+ children: labels.error
4592
+ }
4593
+ ) : null,
4594
+ showEmpty ? /* @__PURE__ */ jsx(CommandEmpty, { children: labels.empty }) : null,
4595
+ allowEmpty && mode === "single" && !query.trim() ? /* @__PURE__ */ jsx(CommandGroup, { children: /* @__PURE__ */ jsxs(
4596
+ CommandItem,
4597
+ {
4598
+ value: "__empty__",
4599
+ onSelect: () => onSelectEmpty?.(),
4600
+ "data-checked": !selectedValues.size || void 0,
4601
+ children: [
4602
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-muted-foreground", children: emptyLabel }),
4603
+ /* @__PURE__ */ jsx(
4604
+ CheckIcon,
4605
+ {
4606
+ className: cn(
4607
+ "ml-auto size-4 shrink-0",
4608
+ selectedValues.size === 0 ? "opacity-100" : "opacity-0"
4609
+ )
4610
+ }
4611
+ )
4612
+ ]
4613
+ }
4614
+ ) }) : null,
4615
+ recommended.length > 0 ? /* @__PURE__ */ jsx(CommandGroup, { heading: labels.recommended, children: recommended.map((entry) => /* @__PURE__ */ jsx(
4616
+ OptionRow,
4617
+ {
4618
+ entry,
4619
+ selected: selectedValues.has(entry.value),
4620
+ onSelect: () => onSelectItem(entry.value),
4621
+ renderOption
4622
+ },
4623
+ `rec-${entry.value}`
4624
+ )) }) : null,
4625
+ recent.length > 0 ? /* @__PURE__ */ jsx(CommandGroup, { heading: labels.recent, children: recent.map((entry) => /* @__PURE__ */ jsx(
4626
+ OptionRow,
4627
+ {
4628
+ entry,
4629
+ selected: selectedValues.has(entry.value),
4630
+ onSelect: () => onSelectItem(entry.value),
4631
+ renderOption
4632
+ },
4633
+ `recent-${entry.value}`
4634
+ )) }) : null,
4635
+ hasPinned && hasResults ? /* @__PURE__ */ jsx(CommandSeparator, {}) : null,
4636
+ hasResults ? /* @__PURE__ */ jsx(CommandGroup, { heading: hasPinned ? labels.results : void 0, children: results.map((entry) => /* @__PURE__ */ jsx(
4637
+ OptionRow,
4638
+ {
4639
+ entry,
4640
+ selected: selectedValues.has(entry.value),
4641
+ onSelect: () => onSelectItem(entry.value),
4642
+ renderOption
4643
+ },
4644
+ `res-${entry.value}`
4645
+ )) }) : null,
4646
+ hasMore && onLoadMore ? /* @__PURE__ */ jsx("div", { className: "p-2", children: /* @__PURE__ */ jsx(
4647
+ Button,
4648
+ {
4649
+ type: "button",
4650
+ variant: "ghost",
4651
+ size: "sm",
4652
+ className: "w-full",
4653
+ disabled: isLoadingMore,
4654
+ onClick: () => void onLoadMore(),
4655
+ children: isLoadingMore ? /* @__PURE__ */ jsxs(Fragment, { children: [
4656
+ /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": true }),
4657
+ labels.loading
4658
+ ] }) : labels.loadMore
4659
+ }
4660
+ ) }) : null,
4661
+ createOption ? /* @__PURE__ */ jsxs(Fragment, { children: [
4662
+ /* @__PURE__ */ jsx(CommandSeparator, {}),
4663
+ /* @__PURE__ */ jsx(CommandGroup, { children: /* @__PURE__ */ jsx(
4664
+ CommandItem,
4665
+ {
4666
+ value: "__create__",
4667
+ onSelect: () => createOption.onSelect(),
4668
+ children: createOption.label
4669
+ }
4670
+ ) })
4671
+ ] }) : null
4672
+ ] })
4673
+ ] });
4423
4674
  }
4424
4675
  var defaultLabels3 = {
4676
+ placeholder: "Seleccionar\u2026",
4677
+ searchPlaceholder: "Buscar\u2026",
4678
+ empty: "No se encontraron resultados",
4679
+ loading: "Cargando\u2026",
4680
+ error: "No se pudo cargar la lista",
4681
+ recommended: "Recomendados",
4682
+ recent: "Recientes",
4683
+ results: "Resultados",
4684
+ done: "Listo",
4685
+ cancel: "Cancelar",
4686
+ clear: "Limpiar",
4687
+ selectedCount: (count) => count === 1 ? "1 seleccionado" : `${count} seleccionados`,
4688
+ emptyOption: "Ninguno",
4689
+ loadMore: "Cargar m\xE1s",
4690
+ dialogTitle: "Seleccionar"
4691
+ };
4692
+ function toListItems(items, getOptionValue, getOptionLabel) {
4693
+ if (!items?.length) return [];
4694
+ return items.map((item) => ({
4695
+ item,
4696
+ value: getOptionValue(item),
4697
+ label: getOptionLabel(item)
4698
+ }));
4699
+ }
4700
+ function dedupeByValue(items, exclude = /* @__PURE__ */ new Set()) {
4701
+ const seen = new Set(exclude);
4702
+ const out = [];
4703
+ for (const entry of items) {
4704
+ if (seen.has(entry.value)) continue;
4705
+ seen.add(entry.value);
4706
+ out.push(entry);
4707
+ }
4708
+ return out;
4709
+ }
4710
+ function matchesQuery(entry, query, getOptionKeywords) {
4711
+ if (!query.trim()) return true;
4712
+ if (fuzzyMatch(query, entry.label)) return true;
4713
+ return getOptionKeywords?.(entry.item)?.some(
4714
+ (keyword) => fuzzyMatch(query, keyword)
4715
+ ) ?? false;
4716
+ }
4717
+ function SearchablePicker(props) {
4718
+ const {
4719
+ options,
4720
+ fetchOptions,
4721
+ getOptionValue,
4722
+ getOptionLabel,
4723
+ getOptionKeywords,
4724
+ renderOption,
4725
+ renderValue,
4726
+ pinned,
4727
+ labels: userLabels,
4728
+ minQueryLength = 0,
4729
+ debounceMs = 200,
4730
+ disabled,
4731
+ open: openProp,
4732
+ onOpenChange,
4733
+ clearable,
4734
+ allowEmpty,
4735
+ emptyLabel,
4736
+ keepOpenOnSelect,
4737
+ maxSelected,
4738
+ createOption,
4739
+ onLoadMore,
4740
+ hasMore,
4741
+ className,
4742
+ triggerClassName: triggerClassName2,
4743
+ id,
4744
+ "aria-labelledby": ariaLabelledBy,
4745
+ "aria-label": ariaLabel
4746
+ } = props;
4747
+ const mode = props.mode ?? "single";
4748
+ const labels = { ...defaultLabels3, ...userLabels };
4749
+ const resolvedEmptyLabel = emptyLabel ?? labels.emptyOption;
4750
+ const isMobile = useMediaQuery2("(max-width: 767px)");
4751
+ const generatedId = useId();
4752
+ const triggerId = id ?? generatedId;
4753
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
4754
+ const open = openProp ?? uncontrolledOpen;
4755
+ const setOpen = useCallback(
4756
+ (next) => {
4757
+ onOpenChange?.(next);
4758
+ if (openProp === void 0) setUncontrolledOpen(next);
4759
+ },
4760
+ [onOpenChange, openProp]
4761
+ );
4762
+ const [query, setQuery] = useState("");
4763
+ const [remoteItems, setRemoteItems] = useState([]);
4764
+ const [isLoading, setIsLoading] = useState(false);
4765
+ const [hasError, setHasError] = useState(false);
4766
+ const [isLoadingMore, setIsLoadingMore] = useState(false);
4767
+ const debouncedQuery = useDebouncedValue(query, debounceMs);
4768
+ const requestIdRef = useRef(0);
4769
+ const knownItemsRef = useRef(/* @__PURE__ */ new Map());
4770
+ const rememberItems = useCallback(
4771
+ (items) => {
4772
+ for (const item of items) {
4773
+ knownItemsRef.current.set(getOptionValue(item), item);
4774
+ }
4775
+ },
4776
+ [getOptionValue]
4777
+ );
4778
+ useEffect(() => {
4779
+ if (options) rememberItems(options);
4780
+ if (pinned?.recommended) rememberItems(pinned.recommended);
4781
+ if (pinned?.recent) rememberItems(pinned.recent);
4782
+ }, [options, pinned, rememberItems]);
4783
+ useEffect(() => {
4784
+ if (!open) {
4785
+ setQuery("");
4786
+ setRemoteItems([]);
4787
+ setHasError(false);
4788
+ setIsLoading(false);
4789
+ }
4790
+ }, [open]);
4791
+ const trimmedDebounced = debouncedQuery.trim();
4792
+ const shouldFetch = Boolean(fetchOptions) && open && trimmedDebounced.length >= minQueryLength && (trimmedDebounced.length > 0 || !options);
4793
+ useEffect(() => {
4794
+ if (!shouldFetch || !fetchOptions) {
4795
+ if (!trimmedDebounced) {
4796
+ setRemoteItems([]);
4797
+ setHasError(false);
4798
+ setIsLoading(false);
4799
+ }
4800
+ return;
4801
+ }
4802
+ const requestId = ++requestIdRef.current;
4803
+ setIsLoading(true);
4804
+ setHasError(false);
4805
+ fetchOptions(trimmedDebounced).then((result) => {
4806
+ if (requestIdRef.current !== requestId) return;
4807
+ setRemoteItems(result);
4808
+ rememberItems(result);
4809
+ }).catch(() => {
4810
+ if (requestIdRef.current !== requestId) return;
4811
+ setRemoteItems([]);
4812
+ setHasError(true);
4813
+ }).finally(() => {
4814
+ if (requestIdRef.current !== requestId) return;
4815
+ setIsLoading(false);
4816
+ });
4817
+ }, [shouldFetch, fetchOptions, trimmedDebounced, rememberItems]);
4818
+ const selectedValues = useMemo(() => {
4819
+ if (mode === "multiple") {
4820
+ const multiValue = props.value ?? [];
4821
+ return new Set(multiValue);
4822
+ }
4823
+ const singleValue = props.value;
4824
+ return singleValue ? /* @__PURE__ */ new Set([singleValue]) : /* @__PURE__ */ new Set();
4825
+ }, [mode, props]);
4826
+ const selectedValueKey = mode === "multiple" ? (props.value ?? []).join("\0") : props.value ?? "";
4827
+ const selectedItems = useMemo(() => {
4828
+ const values = mode === "multiple" ? selectedValueKey ? selectedValueKey.split("\0") : [] : selectedValueKey ? [selectedValueKey] : [];
4829
+ return values.map((value) => knownItemsRef.current.get(value)).filter((item) => item !== void 0);
4830
+ }, [mode, selectedValueKey, options, pinned, remoteItems]);
4831
+ const queryForFilter = query.trim();
4832
+ const recommended = useMemo(() => {
4833
+ if (queryForFilter) return [];
4834
+ const pinnedValues = /* @__PURE__ */ new Set();
4835
+ const items = dedupeByValue(
4836
+ toListItems(pinned?.recommended, getOptionValue, getOptionLabel)
4837
+ );
4838
+ for (const entry of items) pinnedValues.add(entry.value);
4839
+ return items;
4840
+ }, [queryForFilter, pinned?.recommended, getOptionValue, getOptionLabel]);
4841
+ const recent = useMemo(() => {
4842
+ if (queryForFilter) return [];
4843
+ const exclude = new Set(recommended.map((e) => e.value));
4844
+ return dedupeByValue(
4845
+ toListItems(pinned?.recent, getOptionValue, getOptionLabel),
4846
+ exclude
4847
+ );
4848
+ }, [
4849
+ queryForFilter,
4850
+ pinned?.recent,
4851
+ recommended,
4852
+ getOptionValue,
4853
+ getOptionLabel
4854
+ ]);
4855
+ const pinnedValueSet = useMemo(() => {
4856
+ const set = /* @__PURE__ */ new Set();
4857
+ for (const entry of recommended) set.add(entry.value);
4858
+ for (const entry of recent) set.add(entry.value);
4859
+ return set;
4860
+ }, [recommended, recent]);
4861
+ const results = useMemo(() => {
4862
+ const usingRemote = Boolean(fetchOptions) && trimmedDebounced.length >= minQueryLength && (trimmedDebounced.length > 0 || !options);
4863
+ if (usingRemote) {
4864
+ return dedupeByValue(
4865
+ toListItems(remoteItems, getOptionValue, getOptionLabel),
4866
+ queryForFilter ? /* @__PURE__ */ new Set() : pinnedValueSet
4867
+ );
4868
+ }
4869
+ const staticItems = toListItems(options, getOptionValue, getOptionLabel);
4870
+ const filtered = staticItems.filter(
4871
+ (entry) => matchesQuery(entry, queryForFilter, getOptionKeywords)
4872
+ );
4873
+ return dedupeByValue(filtered, queryForFilter ? /* @__PURE__ */ new Set() : pinnedValueSet);
4874
+ }, [
4875
+ fetchOptions,
4876
+ trimmedDebounced,
4877
+ minQueryLength,
4878
+ options,
4879
+ remoteItems,
4880
+ getOptionValue,
4881
+ getOptionLabel,
4882
+ getOptionKeywords,
4883
+ queryForFilter,
4884
+ pinnedValueSet
4885
+ ]);
4886
+ const closeOnSelect = keepOpenOnSelect !== void 0 ? !keepOpenOnSelect : mode === "single";
4887
+ const handleSelectEmpty = useCallback(() => {
4888
+ if (mode === "single") {
4889
+ props.onValueChange?.(
4890
+ null
4891
+ );
4892
+ }
4893
+ if (closeOnSelect) setOpen(false);
4894
+ }, [mode, props, closeOnSelect, setOpen]);
4895
+ const handleSelectItem = useCallback(
4896
+ (value) => {
4897
+ if (mode === "multiple") {
4898
+ const current = props.value ?? [];
4899
+ const onChange = props.onValueChange;
4900
+ const exists = current.includes(value);
4901
+ if (exists) {
4902
+ onChange?.(current.filter((v) => v !== value));
4903
+ } else if (maxSelected !== void 0 && current.length >= maxSelected) {
4904
+ return;
4905
+ } else {
4906
+ onChange?.([...current, value]);
4907
+ }
4908
+ if (closeOnSelect) setOpen(false);
4909
+ return;
4910
+ }
4911
+ props.onValueChange?.(
4912
+ value
4913
+ );
4914
+ if (closeOnSelect) setOpen(false);
4915
+ },
4916
+ [mode, props, maxSelected, closeOnSelect, setOpen]
4917
+ );
4918
+ const handleClear = useCallback(
4919
+ (event) => {
4920
+ event.preventDefault();
4921
+ event.stopPropagation();
4922
+ if (mode === "multiple") {
4923
+ props.onValueChange?.(
4924
+ []
4925
+ );
4926
+ } else {
4927
+ props.onValueChange?.(null);
4928
+ }
4929
+ },
4930
+ [mode, props]
4931
+ );
4932
+ const handleLoadMore = useCallback(async () => {
4933
+ if (!onLoadMore || isLoadingMore) return;
4934
+ setIsLoadingMore(true);
4935
+ try {
4936
+ await onLoadMore();
4937
+ } finally {
4938
+ setIsLoadingMore(false);
4939
+ }
4940
+ }, [onLoadMore, isLoadingMore]);
4941
+ const triggerLabel = useMemo(() => {
4942
+ if (renderValue) {
4943
+ if (mode === "multiple") {
4944
+ return renderValue(selectedItems);
4945
+ }
4946
+ return renderValue(selectedItems[0] ?? null);
4947
+ }
4948
+ if (mode === "multiple") {
4949
+ const count = selectedValues.size;
4950
+ if (count === 0) {
4951
+ return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: labels.placeholder });
4952
+ }
4953
+ if (selectedItems.length === 1) {
4954
+ return getOptionLabel(selectedItems[0]);
4955
+ }
4956
+ return labels.selectedCount(count);
4957
+ }
4958
+ if (selectedValues.size === 0) {
4959
+ return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: allowEmpty ? resolvedEmptyLabel : labels.placeholder });
4960
+ }
4961
+ if (selectedItems[0]) {
4962
+ return getOptionLabel(selectedItems[0]);
4963
+ }
4964
+ const only = [...selectedValues][0];
4965
+ return only ?? labels.placeholder;
4966
+ }, [
4967
+ renderValue,
4968
+ mode,
4969
+ selectedItems,
4970
+ selectedValues,
4971
+ labels,
4972
+ getOptionLabel,
4973
+ allowEmpty,
4974
+ resolvedEmptyLabel
4975
+ ]);
4976
+ const showClear = clearable && !disabled && (mode === "multiple" ? selectedValues.size > 0 : selectedValues.size > 0);
4977
+ const list = /* @__PURE__ */ jsx(
4978
+ SearchablePickerList,
4979
+ {
4980
+ query,
4981
+ onQueryChange: setQuery,
4982
+ labels,
4983
+ isLoading,
4984
+ hasError,
4985
+ recommended,
4986
+ recent,
4987
+ results,
4988
+ selectedValues,
4989
+ mode,
4990
+ allowEmpty,
4991
+ emptyLabel: resolvedEmptyLabel,
4992
+ onSelectEmpty: handleSelectEmpty,
4993
+ onSelectItem: handleSelectItem,
4994
+ renderOption,
4995
+ createOption,
4996
+ onLoadMore: onLoadMore ? handleLoadMore : void 0,
4997
+ hasMore,
4998
+ isLoadingMore,
4999
+ listClassName: isMobile ? "max-h-[min(50vh,360px)]" : "max-h-72"
5000
+ }
5001
+ );
5002
+ const triggerButton = /* @__PURE__ */ jsxs(
5003
+ Button,
5004
+ {
5005
+ type: "button",
5006
+ variant: "outline",
5007
+ role: "combobox",
5008
+ id: triggerId,
5009
+ "aria-expanded": open,
5010
+ "aria-labelledby": ariaLabelledBy,
5011
+ "aria-label": ariaLabel ?? labels.placeholder,
5012
+ disabled,
5013
+ className: cn("w-full justify-between font-normal", triggerClassName2),
5014
+ children: [
5015
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: triggerLabel }),
5016
+ /* @__PURE__ */ jsx(ChevronsUpDown, { className: "ml-2 size-4 shrink-0 opacity-50", "aria-hidden": true })
5017
+ ]
5018
+ }
5019
+ );
5020
+ const clearButton = showClear ? /* @__PURE__ */ jsx(
5021
+ Button,
5022
+ {
5023
+ type: "button",
5024
+ variant: "ghost",
5025
+ size: "icon-sm",
5026
+ "aria-label": labels.clear,
5027
+ disabled,
5028
+ className: "shrink-0",
5029
+ onClick: handleClear,
5030
+ children: /* @__PURE__ */ jsx(X, { className: "size-4" })
5031
+ }
5032
+ ) : null;
5033
+ if (isMobile) {
5034
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex w-full items-center gap-1", className), children: [
5035
+ /* @__PURE__ */ jsx(
5036
+ "div",
5037
+ {
5038
+ className: "min-w-0 flex-1",
5039
+ onClick: () => {
5040
+ if (!disabled) setOpen(true);
5041
+ },
5042
+ children: triggerButton
5043
+ }
5044
+ ),
5045
+ clearButton,
5046
+ /* @__PURE__ */ jsx(Drawer, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(DrawerContent, { className: "max-h-[90vh]", children: [
5047
+ /* @__PURE__ */ jsx(DrawerHeader, { className: "text-left", children: /* @__PURE__ */ jsx(DrawerTitle, { children: labels.dialogTitle }) }),
5048
+ /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-hidden px-2 pb-2", children: list }),
5049
+ mode === "multiple" ? /* @__PURE__ */ jsxs(DrawerFooter, { className: "flex-row gap-2", children: [
5050
+ /* @__PURE__ */ jsx(
5051
+ Button,
5052
+ {
5053
+ type: "button",
5054
+ variant: "outline",
5055
+ className: "flex-1",
5056
+ onClick: () => setOpen(false),
5057
+ children: labels.cancel
5058
+ }
5059
+ ),
5060
+ /* @__PURE__ */ jsx(
5061
+ Button,
5062
+ {
5063
+ type: "button",
5064
+ className: "flex-1",
5065
+ onClick: () => setOpen(false),
5066
+ children: labels.done
5067
+ }
5068
+ )
5069
+ ] }) : null
5070
+ ] }) })
5071
+ ] });
5072
+ }
5073
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex w-full items-center gap-1", className), children: [
5074
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, modal: true, children: [
5075
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: triggerButton }),
5076
+ /* @__PURE__ */ jsx(
5077
+ PopoverContent,
5078
+ {
5079
+ className: "w-[var(--radix-popover-trigger-width)] min-w-[240px] p-0",
5080
+ align: "start",
5081
+ children: list
5082
+ }
5083
+ )
5084
+ ] }) }),
5085
+ clearButton
5086
+ ] });
5087
+ }
5088
+ var defaultLabels4 = {
4425
5089
  placeholder: "Buscar en toda la plataforma\u2026",
4426
5090
  dialogTitle: "Buscar",
4427
5091
  dialogDescription: "Busca funcionalidades, eventos, documentos y m\xE1s",
@@ -4455,7 +5119,7 @@ function SearchCommand({
4455
5119
  debounceMs = 200,
4456
5120
  className
4457
5121
  }) {
4458
- const labels = { ...defaultLabels3, ...userLabels };
5122
+ const labels = { ...defaultLabels4, ...userLabels };
4459
5123
  const [query, setQuery] = useState("");
4460
5124
  const [hits, setHits] = useState([]);
4461
5125
  const [isLoading, setIsLoading] = useState(false);
@@ -5336,18 +6000,6 @@ function ChatMessageList({
5336
6000
  /* @__PURE__ */ jsx(MessageScrollerButton, {})
5337
6001
  ] }) });
5338
6002
  }
5339
- function useMediaQuery2(query) {
5340
- const [matches, setMatches] = useState(false);
5341
- useEffect(() => {
5342
- if (typeof window === "undefined" || !window.matchMedia) return;
5343
- const mql = window.matchMedia(query);
5344
- const onChange = () => setMatches(mql.matches);
5345
- onChange();
5346
- mql.addEventListener("change", onChange);
5347
- return () => mql.removeEventListener("change", onChange);
5348
- }, [query]);
5349
- return matches;
5350
- }
5351
6003
  var RECONNECT_DELAY_MS = 2e3;
5352
6004
  function useSupportConversation({
5353
6005
  client,
@@ -5966,6 +6618,6 @@ function createSupportChatClient(basePath = "/api/chat-svc") {
5966
6618
  };
5967
6619
  }
5968
6620
 
5969
- export { AvatarEditor, AvatarEditorDialog, Bubble, BubbleContent, BubbleGroup, BubbleReactions, ChatMessageList, ConfirmDestructiveDialog, EmojiInput, EmojiPicker, EmojiPickerPopover, EmojiTextarea, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyState, EmptyTitle, ErrorState, 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 };
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 };
5970
6622
  //# sourceMappingURL=index.js.map
5971
6623
  //# sourceMappingURL=index.js.map