@doscientos/ui 0.1.33 → 0.1.34

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.d.cts CHANGED
@@ -30,7 +30,7 @@ type UseAutosaveOptions<T> = {
30
30
  enabled?: boolean;
31
31
  serialize?: (data: T) => string;
32
32
  };
33
- /** Debounced autosave with stale-value protection and an explicit `saveNow`. */
33
+ /** Debounced, serialized writes. saveNow flushes the debounce; errors remain in state. */
34
34
  declare function useAutosave<T>({ data, onSave, debounceMs, enabled, serialize, }: UseAutosaveOptions<T>): {
35
35
  status: AutosaveStatus;
36
36
  error: Error | null;
@@ -856,7 +856,7 @@ type PaginationProps = {
856
856
  ariaLabel?: string;
857
857
  /** Optional description of the slice of results currently visible. */
858
858
  summary?: React$1.ReactNode;
859
- /** Number of neighbouring pages shown around the current page. */
859
+ /** Number of neighbouring pages shown around the current page (normalized to 0–10). */
860
860
  siblingCount?: number;
861
861
  className?: string;
862
862
  };
package/dist/index.d.ts CHANGED
@@ -30,7 +30,7 @@ type UseAutosaveOptions<T> = {
30
30
  enabled?: boolean;
31
31
  serialize?: (data: T) => string;
32
32
  };
33
- /** Debounced autosave with stale-value protection and an explicit `saveNow`. */
33
+ /** Debounced, serialized writes. saveNow flushes the debounce; errors remain in state. */
34
34
  declare function useAutosave<T>({ data, onSave, debounceMs, enabled, serialize, }: UseAutosaveOptions<T>): {
35
35
  status: AutosaveStatus;
36
36
  error: Error | null;
@@ -856,7 +856,7 @@ type PaginationProps = {
856
856
  ariaLabel?: string;
857
857
  /** Optional description of the slice of results currently visible. */
858
858
  summary?: React$1.ReactNode;
859
- /** Number of neighbouring pages shown around the current page. */
859
+ /** Number of neighbouring pages shown around the current page (normalized to 0–10). */
860
860
  siblingCount?: number;
861
861
  className?: string;
862
862
  };
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ function useAsyncAction(action) {
13
13
  pendingRef.current = true;
14
14
  setStatus("pending");
15
15
  setError(null);
16
+ setData(null);
16
17
  try {
17
18
  const result = await action(...args);
18
19
  setData(result);
@@ -29,6 +30,7 @@ function useAsyncAction(action) {
29
30
  [action]
30
31
  );
31
32
  const reset = useCallback(() => {
33
+ if (pendingRef.current) return;
32
34
  setData(null);
33
35
  setError(null);
34
36
  setStatus("idle");
@@ -51,24 +53,45 @@ function useAutosave({
51
53
  const saveRef = useRef2(onSave);
52
54
  const serializeRef = useRef2(serialize);
53
55
  const latestSaveId = useRef2(0);
56
+ const queue = useRef2(Promise.resolve());
57
+ const queuedCount = useRef2(0);
58
+ const timeoutRef = useRef2(void 0);
59
+ const mounted = useRef2(true);
60
+ useEffect(() => {
61
+ mounted.current = true;
62
+ return () => {
63
+ mounted.current = false;
64
+ clearTimeout(timeoutRef.current);
65
+ };
66
+ }, []);
54
67
  useEffect(() => {
55
68
  saveRef.current = onSave;
56
69
  serializeRef.current = serialize;
57
70
  }, [onSave, serialize]);
58
- const save = useCallback2(async (value) => {
71
+ const save = useCallback2((value, force = false) => {
72
+ clearTimeout(timeoutRef.current);
59
73
  const saveId = ++latestSaveId.current;
74
+ const snapshot = serializeRef.current(value);
75
+ const write = saveRef.current;
76
+ queuedCount.current += 1;
60
77
  setStatus("saving");
61
78
  setError(null);
62
- try {
63
- await saveRef.current(value);
64
- if (saveId !== latestSaveId.current) return;
65
- lastSaved.current = serializeRef.current(value);
66
- setStatus("saved");
67
- } catch (cause) {
68
- if (saveId !== latestSaveId.current) return;
69
- setError(cause instanceof Error ? cause : new Error("No se pudo guardar."));
70
- setStatus("error");
71
- }
79
+ const pending = queue.current.then(async () => {
80
+ try {
81
+ if (!mounted.current) return;
82
+ if (force || lastSaved.current !== snapshot) await write(value);
83
+ lastSaved.current = snapshot;
84
+ if (mounted.current && saveId === latestSaveId.current) setStatus("saved");
85
+ } catch (cause) {
86
+ if (!mounted.current || saveId !== latestSaveId.current) return;
87
+ setError(cause instanceof Error ? cause : new Error("No se pudo guardar."));
88
+ setStatus("error");
89
+ } finally {
90
+ queuedCount.current -= 1;
91
+ }
92
+ });
93
+ queue.current = pending;
94
+ return pending;
72
95
  }, []);
73
96
  useEffect(() => {
74
97
  if (!enabled) return;
@@ -77,11 +100,11 @@ function useAutosave({
77
100
  lastSaved.current = snapshot;
78
101
  return;
79
102
  }
80
- if (snapshot === lastSaved.current) return;
81
- const timeout = window.setTimeout(() => void save(data), debounceMs);
82
- return () => window.clearTimeout(timeout);
103
+ if (snapshot === lastSaved.current && queuedCount.current === 0) return;
104
+ timeoutRef.current = setTimeout(() => void save(data), debounceMs);
105
+ return () => clearTimeout(timeoutRef.current);
83
106
  }, [data, debounceMs, enabled, save]);
84
- return { status, error, saveNow: () => save(data) };
107
+ return { status, error, saveNow: () => save(data, true) };
85
108
  }
86
109
 
87
110
  // src/hooks/use-clipboard.ts
@@ -1513,10 +1536,10 @@ function ComboboxContent({ className, ...props }) {
1513
1536
  Popover,
1514
1537
  {
1515
1538
  "data-slot": "combobox-content",
1516
- className: cn(
1539
+ className: (state) => cn(
1517
1540
  floatingSurfaceClassName,
1518
1541
  "max-h-72 w-(--trigger-width) overflow-hidden rounded-xl border border-border bg-background p-1.5 text-foreground",
1519
- className
1542
+ typeof className === "function" ? className(state) : className
1520
1543
  ),
1521
1544
  ...props
1522
1545
  }
@@ -1531,7 +1554,10 @@ function ComboboxList({
1531
1554
  ListBox,
1532
1555
  {
1533
1556
  "data-slot": "combobox-list",
1534
- className: cn("max-h-64 overflow-y-auto", className),
1557
+ className: (state) => cn(
1558
+ "max-h-64 overflow-y-auto",
1559
+ typeof className === "function" ? className(state) : className
1560
+ ),
1535
1561
  renderEmptyState: emptyState ? () => emptyState : void 0,
1536
1562
  ...props
1537
1563
  }
@@ -1546,9 +1572,9 @@ function ComboboxItem({
1546
1572
  ListBoxItem,
1547
1573
  {
1548
1574
  "data-slot": "combobox-item",
1549
- className: cn(
1575
+ className: (state) => cn(
1550
1576
  "flex w-full cursor-default items-center justify-between rounded-md px-2 py-2 text-sm outline-none transition-colors data-focused:bg-muted data-focused:text-foreground data-hovered:bg-muted data-disabled:pointer-events-none data-disabled:opacity-50",
1551
- className
1577
+ typeof className === "function" ? className(state) : className
1552
1578
  ),
1553
1579
  ...props,
1554
1580
  children
@@ -1623,7 +1649,10 @@ function AutocompleteCombobox({
1623
1649
  AriaComboBox,
1624
1650
  {
1625
1651
  ...props,
1626
- className: cn("group/combobox flex w-full flex-col gap-1.5", className),
1652
+ className: (state) => cn(
1653
+ "group/combobox flex w-full flex-col gap-1.5",
1654
+ typeof className === "function" ? className(state) : className
1655
+ ),
1627
1656
  items: filteredItems,
1628
1657
  selectedKey,
1629
1658
  inputValue,
@@ -2479,16 +2508,19 @@ function ErrorStateActions({ className, ...props }) {
2479
2508
 
2480
2509
  // src/ui/error-boundary/error-boundary.tsx
2481
2510
  import { jsx as jsx28, jsxs as jsxs12 } from "react/jsx-runtime";
2511
+ function normalizeError(cause) {
2512
+ return cause instanceof Error ? cause : new Error("No se pudo renderizar este contenido.", { cause });
2513
+ }
2482
2514
  function changedResetKeys(previous = [], next = []) {
2483
2515
  return previous.length !== next.length || previous.some((value, index) => !Object.is(value, next[index]));
2484
2516
  }
2485
2517
  var Boundary = class extends Component {
2486
2518
  state = { error: null };
2487
2519
  static getDerivedStateFromError(error) {
2488
- return { error };
2520
+ return { error: normalizeError(error) };
2489
2521
  }
2490
2522
  componentDidCatch(error, info) {
2491
- this.props.onError?.(error, info);
2523
+ this.props.onError?.(this.state.error ?? normalizeError(error), info);
2492
2524
  }
2493
2525
  componentDidUpdate(previousProps) {
2494
2526
  if (this.state.error && changedResetKeys(previousProps.resetKeys, this.props.resetKeys))
@@ -2500,7 +2532,7 @@ var Boundary = class extends Component {
2500
2532
  if (!this.state.error) return children;
2501
2533
  if (typeof fallback === "function")
2502
2534
  return fallback({ error: this.state.error, reset: this.reset });
2503
- if (fallback) return fallback;
2535
+ if (fallback !== void 0) return fallback;
2504
2536
  return /* @__PURE__ */ jsxs12(ErrorState, { children: [
2505
2537
  /* @__PURE__ */ jsx28(ErrorStateIcon, {}),
2506
2538
  /* @__PURE__ */ jsx28(ErrorStateTitle, { children: "No se pudo cargar este contenido" }),
@@ -3582,16 +3614,17 @@ import { ChevronLeft, ChevronRight } from "lucide-react";
3582
3614
  import * as React6 from "react";
3583
3615
  import { jsx as jsx47, jsxs as jsxs21 } from "react/jsx-runtime";
3584
3616
  function visiblePages(page, pageCount, siblingCount) {
3617
+ const siblings = Number.isFinite(siblingCount) ? Math.min(10, Math.max(0, Math.floor(siblingCount))) : 1;
3585
3618
  return [
3586
3619
  .../* @__PURE__ */ new Set([
3587
3620
  1,
3588
- ...Array.from({ length: siblingCount * 2 + 1 }, (_, index) => page - siblingCount + index),
3621
+ ...Array.from({ length: siblings * 2 + 1 }, (_, index) => page - siblings + index),
3589
3622
  pageCount
3590
3623
  ])
3591
3624
  ].filter((item) => item >= 1 && item <= pageCount).sort((left, right) => left - right);
3592
3625
  }
3593
3626
  function normalizedPageCount(pageCount) {
3594
- return Number.isFinite(pageCount) ? Math.max(0, Math.floor(pageCount)) : 0;
3627
+ return Number.isFinite(pageCount) ? Math.min(Number.MAX_SAFE_INTEGER, Math.max(0, Math.floor(pageCount))) : 0;
3595
3628
  }
3596
3629
  function normalizedPage(page, pageCount) {
3597
3630
  if (!Number.isFinite(page)) return 1;
@@ -3640,7 +3673,7 @@ function Pagination({
3640
3673
  "aria-label": `P\xE1gina ${item}`,
3641
3674
  onPress: () => onPageChange(item),
3642
3675
  size: "icon",
3643
- variant: item === page ? "secondary" : "ghost",
3676
+ variant: item === currentPage ? "secondary" : "ghost",
3644
3677
  children: item
3645
3678
  }
3646
3679
  )