@nadicodeai/ui 6.0.1 → 6.0.2

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.
@@ -1,9 +1,3 @@
1
- export interface ConfirmActionGate {
2
- busy: boolean;
3
- }
4
- export declare function requestConfirmAction<T>(gate: ConfirmActionGate, value: T, setPending: (value: T | null) => void): void;
5
- export declare function cancelConfirmAction<T>(gate: ConfirmActionGate, setPending: (value: T | null) => void): void;
6
- export declare function runConfirmAction<T>(gate: ConfirmActionGate, pending: T | null, onConfirm: (value: T) => void | Promise<void>, setPending: (value: T | null) => void, setBusy: (busy: boolean) => void): Promise<void>;
7
1
  export declare function useConfirmAction<T>({ onConfirm, }: {
8
2
  onConfirm: (value: T) => void | Promise<void>;
9
3
  }): {
@@ -1 +1 @@
1
- {"version":3,"file":"use-confirm-action.d.ts","sourceRoot":"","sources":["../../src/hooks/use-confirm-action.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,OAAO,CAAC;CACf;AAID,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,IAAI,EAAE,iBAAiB,EACvB,KAAK,EAAE,CAAC,EACR,UAAU,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,GACpC,IAAI,CAKN;AAID,wBAAgB,mBAAmB,CAAC,CAAC,EACnC,IAAI,EAAE,iBAAiB,EACvB,UAAU,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,GACpC,IAAI,CAKN;AAKD,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE,CAAC,GAAG,IAAI,EACjB,SAAS,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EAC7C,UAAU,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,EACrC,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,GAC/B,OAAO,CAAC,IAAI,CAAC,CAef;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,EAClC,SAAS,GACV,EAAE;IACD,SAAS,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;8BAK2C,CAAC;;;;;;EAqB5C"}
1
+ {"version":3,"file":"use-confirm-action.d.ts","sourceRoot":"","sources":["../../src/hooks/use-confirm-action.ts"],"names":[],"mappings":"AAwDA,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,EAClC,SAAS,GACV,EAAE;IACD,SAAS,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;8BAK2C,CAAC;;;;;;EAqB5C"}
@@ -1,25 +1,18 @@
1
1
  "use client";
2
2
  import * as React from "react";
3
- // Request stages a value for confirmation; ignored while a confirm is in
4
- // flight so the pending value cannot change under a running action.
5
- export function requestConfirmAction(gate, value, setPending) {
3
+ function requestConfirmAction(gate, value, setPending) {
6
4
  if (gate.busy) {
7
5
  return;
8
6
  }
9
7
  setPending(value);
10
8
  }
11
- // Cancel clears the pending value, but is a no-op while a confirm is in flight
12
- // so the dialog cannot be dismissed mid-action.
13
- export function cancelConfirmAction(gate, setPending) {
9
+ function cancelConfirmAction(gate, setPending) {
14
10
  if (gate.busy) {
15
11
  return;
16
12
  }
17
13
  setPending(null);
18
14
  }
19
- // Confirm marks busy, awaits `onConfirm`, and clears the pending value on
20
- // success. A thrown error is swallowed so the dialog stays open (the caller
21
- // surfaces the error inside `onConfirm`); busy always resets in `finally`.
22
- export async function runConfirmAction(gate, pending, onConfirm, setPending, setBusy) {
15
+ async function confirmOrKeepOpen(gate, pending, onConfirm, setPending, setBusy) {
23
16
  if (gate.busy || pending === null) {
24
17
  return;
25
18
  }
@@ -30,7 +23,7 @@ export async function runConfirmAction(gate, pending, onConfirm, setPending, set
30
23
  setPending(null);
31
24
  }
32
25
  catch {
33
- // Keep the pending value so the dialog stays open.
26
+ return;
34
27
  }
35
28
  finally {
36
29
  gate.busy = false;
@@ -47,7 +40,7 @@ export function useConfirmAction({ onConfirm, }) {
47
40
  const cancel = React.useCallback(() => {
48
41
  cancelConfirmAction(gateRef.current, setPending);
49
42
  }, []);
50
- const confirm = React.useCallback(() => runConfirmAction(gateRef.current, pending, onConfirm, setPending, setIsBusy), [pending, onConfirm]);
43
+ const confirm = React.useCallback(() => confirmOrKeepOpen(gateRef.current, pending, onConfirm, setPending, setIsBusy), [pending, onConfirm]);
51
44
  return {
52
45
  request,
53
46
  cancel,
@@ -1,44 +1,3 @@
1
- /**
2
- * The copy affordance behind every "copy" button in the package.
3
- *
4
- * The decision half and the timer half are exported as plain functions, with the
5
- * hook a thin binding over them (the shape `use-confirm-action` established), so
6
- * both are testable in the package's node-only default suite instead of needing a
7
- * DOM render harness.
8
- */
9
- export type CopyAttempt =
10
- /** No clipboard to write to — an insecure origin, typically. */
11
- {
12
- kind: "unavailable";
13
- error: Error;
14
- }
15
- /** Already showing "copied" and the caller asked not to repeat. */
16
- | {
17
- kind: "skipped";
18
- } | {
19
- kind: "copied";
20
- } | {
21
- kind: "failed";
22
- error: Error;
23
- };
24
- /** Attempts the write and classifies the result; performs no state or timer work. */
25
- export declare function attemptCopy(input: {
26
- text: string;
27
- isCopied: boolean;
28
- skipWhileCopied: boolean;
29
- writeText: ((text: string) => Promise<void>) | undefined;
30
- }): Promise<CopyAttempt>;
31
- export type ResetTimers = {
32
- clear: (handle: number) => void;
33
- set: (onReset: () => void, timeout: number) => number;
34
- };
35
- /**
36
- * Starts the "copied" reset window, dropping whichever reset was already
37
- * pending. Without the clear, an earlier press's timer keeps its own schedule and
38
- * fires part-way through this press's window, so the feedback disappears early.
39
- * Returns the new handle for the caller to hold.
40
- */
41
- export declare function restartResetTimer(timers: ResetTimers, pendingHandle: number, onReset: () => void, timeout: number): number;
42
1
  export declare function useCopyToClipboard({ text, onCopy, onError, timeout, skipWhileCopied, }: {
43
2
  text: string;
44
3
  onCopy?: () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"use-copy-to-clipboard.d.ts","sourceRoot":"","sources":["../../src/lib/use-copy-to-clipboard.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,MAAM,MAAM,WAAW;AACrB,gEAAgE;AAC9D;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE;AACvC,mEAAmE;GACjE;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,CAAC;AAErC,qFAAqF;AACrF,wBAAsB,WAAW,CAAC,KAAK,EAAE;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,OAAO,CAAC;IACzB,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC;CAC1D,GAAG,OAAO,CAAC,WAAW,CAAC,CAavB;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,IAAI,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;CACvD,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,WAAW,EACnB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,IAAI,EACnB,OAAO,EAAE,MAAM,GACd,MAAM,CAGR;AAED,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,MAAM,EACN,OAAO,EACP,OAAO,EACP,eAAuB,GACxB,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;;;EAyCA"}
1
+ {"version":3,"file":"use-copy-to-clipboard.d.ts","sourceRoot":"","sources":["../../src/lib/use-copy-to-clipboard.ts"],"names":[],"mappings":"AA4BA,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,MAAM,EACN,OAAO,EACP,OAAO,EACP,eAAuB,GACxB,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;;;EAsCA"}
@@ -1,6 +1,5 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
- /** Attempts the write and classifies the result; performs no state or timer work. */
3
- export async function attemptCopy(input) {
2
+ async function attemptCopy(input) {
4
3
  if (!input.writeText) {
5
4
  return { kind: "unavailable", error: new Error("Clipboard API not available") };
6
5
  }
@@ -15,18 +14,9 @@ export async function attemptCopy(input) {
15
14
  return { kind: "failed", error: error };
16
15
  }
17
16
  }
18
- /**
19
- * Starts the "copied" reset window, dropping whichever reset was already
20
- * pending. Without the clear, an earlier press's timer keeps its own schedule and
21
- * fires part-way through this press's window, so the feedback disappears early.
22
- * Returns the new handle for the caller to hold.
23
- */
24
- export function restartResetTimer(timers, pendingHandle, onReset, timeout) {
25
- timers.clear(pendingHandle);
26
- return timers.set(onReset, timeout);
27
- }
28
17
  export function useCopyToClipboard({ text, onCopy, onError, timeout, skipWhileCopied = false, }) {
29
- const [isCopied, setIsCopied] = useState(false);
18
+ const [copiedText, setCopiedText] = useState(null);
19
+ const isCopied = copiedText === text;
30
20
  const timeoutRef = useRef(0);
31
21
  const copyToClipboard = useCallback(async () => {
32
22
  const attempt = await attemptCopy({
@@ -44,9 +34,10 @@ export function useCopyToClipboard({ text, onCopy, onError, timeout, skipWhileCo
44
34
  if (attempt.kind === "skipped") {
45
35
  return;
46
36
  }
47
- setIsCopied(true);
37
+ setCopiedText(text);
48
38
  onCopy?.();
49
- timeoutRef.current = restartResetTimer({ clear: window.clearTimeout.bind(window), set: window.setTimeout.bind(window) }, timeoutRef.current, () => setIsCopied(false), timeout);
39
+ window.clearTimeout(timeoutRef.current);
40
+ timeoutRef.current = window.setTimeout(() => setCopiedText(null), timeout);
50
41
  }, [isCopied, onCopy, onError, skipWhileCopied, text, timeout]);
51
42
  useEffect(() => () => {
52
43
  window.clearTimeout(timeoutRef.current);
package/docs/contract.md CHANGED
@@ -135,7 +135,7 @@ package.
135
135
 
136
136
  ## Accessibility Invariant
137
137
 
138
- **Tier-1 accessibility is tested, browser-free.** Every rendering component test uses the package `expectAccessible` helper; the coverage guard derives the applicable test set and fails omissions. The jsdom tier is structural: rendered contrast, landmark context, focus, and keyboard behavior belong to real-browser Playwright coverage. The shared library ESLint configuration is the executable authority for static JSX accessibility rules, and the repository quality doctrine owns their policy; this contract does not copy the rule inventory.
138
+ **Tier-1 accessibility is tested, browser-free.** Rendering component tests use the package `expectAccessible` helper to check the rendered DOM. The jsdom tier is structural: rendered contrast, landmark context, focus, and keyboard behavior belong to real-browser Playwright coverage. The shared library ESLint configuration is the executable authority for static JSX accessibility rules, and the repository quality doctrine owns their policy; this contract does not copy the rule inventory.
139
139
 
140
140
  ## Reusable Sections
141
141
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nadicodeai/ui",
3
- "version": "6.0.1",
3
+ "version": "6.0.2",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -70,7 +70,7 @@
70
70
  "@assistant-ui/react": "0.15.18",
71
71
  "@assistant-ui/react-markdown": "0.14.14",
72
72
  "@base-ui/react": "^1.8.0",
73
- "@nadicodeai/design-system": "6.0.1",
73
+ "@nadicodeai/design-system": "6.0.2",
74
74
  "@paper-design/shaders": "0.0.80",
75
75
  "class-variance-authority": "^0.7.1",
76
76
  "clsx": "^2.1.1",