@vendure-io/ui 2.0.0-beta.7 → 2.0.0-beta.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vendure-io/ui",
3
- "version": "2.0.0-beta.7",
3
+ "version": "2.0.0-beta.8",
4
4
  "description": "React component library for Vendure, built on shadcn/ui and Tailwind v4",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "homepage": "https://github.com/vendurehq/design/tree/main/packages/ui",
@@ -49,7 +49,7 @@
49
49
  "@shikijs/themes": "^3.23.0",
50
50
  "@shikijs/transformers": "^3.22.0",
51
51
  "@tanstack/react-table": "^8.21.3",
52
- "@vendure-io/design-tokens": "^2.0.0-beta.5",
52
+ "@vendure-io/design-tokens": "^2.0.0-beta.7",
53
53
  "class-variance-authority": "^0.7.1",
54
54
  "clsx": "^2.1.1",
55
55
  "cmdk": "^1.1.1",
@@ -53,9 +53,9 @@ interface AnonymizedTokenProps extends Omit<React.ComponentProps<'span'>, 'child
53
53
  copyable?: boolean;
54
54
  /** How long the check-mark feedback stays visible, in ms. */
55
55
  timeout?: CopyButtonProps['timeout'];
56
- /** Called after a successful copy. Wire your toast here. */
56
+ /** Called after a successful copy. Wire your toast here. Falls back to `CopyFeedbackProvider`. */
57
57
  onCopied?: CopyButtonProps['onCopied'];
58
- /** Called when the clipboard write fails. */
58
+ /** Called when the clipboard write fails. Falls back to `CopyFeedbackProvider`. */
59
59
  onCopyError?: CopyButtonProps['onCopyError'];
60
60
  /** Accessible label before copying. @default "Copy token" */
61
61
  copyLabel?: CopyButtonProps['copyLabel'];
@@ -10,6 +10,7 @@ import {
10
10
  import { Button } from '@vendure-io/ui/components/atoms/button';
11
11
  import { ScrollArea, ScrollBar } from '@vendure-io/ui/components/atoms/scroll-area';
12
12
  import { Tooltip, TooltipContent, TooltipTrigger } from '@vendure-io/ui/components/atoms/tooltip';
13
+ import { useCopyFeedback } from '@vendure-io/ui/components/molecules/copy-feedback-provider';
13
14
  import { useCopy } from '@vendure-io/ui/hooks/use-copy';
14
15
  import { cn } from '@vendure-io/ui/lib/utils';
15
16
  import {
@@ -56,9 +57,14 @@ type CodeBlockProps = PropsWithChildren<ComponentProps<'div'>> & {
56
57
  packageManagerSwitcher?: boolean;
57
58
  /** Extra toolbar actions rendered before the built-in copy button. Compose with CodeBlockAction. */
58
59
  actions?: ReactNode;
59
- /** Called after a successful copy. Wire your toast here. The DS never toasts. */
60
+ /**
61
+ * Called after a successful copy. Wire your toast here. The DS never toasts.
62
+ * Function props can't cross an RSC boundary — when rendering from a server
63
+ * component, omit this and mount `CopyFeedbackProvider` instead; it is the
64
+ * fallback when no prop is passed.
65
+ */
60
66
  onCopied?: () => void;
61
- /** Called when the clipboard write fails. */
67
+ /** Called when the clipboard write fails. Falls back to `CopyFeedbackProvider`. */
62
68
  onCopyError?: (error: Error) => void;
63
69
  };
64
70
 
@@ -975,7 +981,9 @@ function SyntaxHighlightedContent({ code, language }: SyntaxHighlightedContentPr
975
981
  * long snippets, and an optional npm→pnpm/yarn/bun package-manager switcher.
976
982
  * Highlighting is lazy (a shared highlighter singleton + per-snippet cache), and
977
983
  * line/diff/focus/word decorations use Shiki's `[!code ...]` notations. The DS
978
- * never toasts. Wire `onCopied`/`onCopyError` to your own feedback.
984
+ * never toasts. Wire `onCopied`/`onCopyError` to your own feedback — or, when
985
+ * rendering from server components (where function props can't be passed),
986
+ * mount `CopyFeedbackProvider` once and leave the props off.
979
987
  */
980
988
  export function CodeBlock({
981
989
  className,
@@ -990,6 +998,7 @@ export function CodeBlock({
990
998
  ...props
991
999
  }: CodeBlockProps) {
992
1000
  const { copied, copy } = useCopy();
1001
+ const copyFeedback = useCopyFeedback();
993
1002
  const [activePackageManager, setActivePackageManager] = usePackageManager();
994
1003
  const [isExpanded, setIsExpanded] = useState(false);
995
1004
  const containerRef = useRef<HTMLDivElement>(null);
@@ -1035,8 +1044,8 @@ export function CodeBlock({
1035
1044
 
1036
1045
  const handleCopy = async () => {
1037
1046
  const ok = await copy(displayCode);
1038
- if (ok) onCopied?.();
1039
- else onCopyError?.(new Error('Failed to copy to the clipboard'));
1047
+ if (ok) (onCopied ?? copyFeedback.onCopied)?.();
1048
+ else (onCopyError ?? copyFeedback.onCopyError)?.(new Error('Failed to copy to the clipboard'));
1040
1049
  };
1041
1050
 
1042
1051
  const toolbar = <CodeBlockToolbar onCopy={handleCopy} isCopied={copied} actions={actions} />;
@@ -0,0 +1,33 @@
1
+ 'use client';
2
+
3
+ import { createContext, type ReactNode, useContext, useMemo } from 'react';
4
+
5
+ // Function props can't cross a server→client boundary, so a copy surface
6
+ // rendered from RSC (e.g. MDX docs) could never receive an `onCopied` callback
7
+ // for toast wiring. This context is the RSC-safe alternative: mount
8
+ // CopyFeedbackProvider once in a client component (wire your toast there — the
9
+ // DS never toasts), and copy surfaces resolve their feedback in a fixed order —
10
+ // explicit prop → this context → nothing beyond the built-in copied icon.
11
+ interface CopyFeedbackContextValue {
12
+ /** Called after a successful copy. Wire your toast here — the DS never toasts. */
13
+ onCopied?: () => void;
14
+ /** Called when the clipboard write fails. */
15
+ onCopyError?: (error: Error) => void;
16
+ }
17
+
18
+ const CopyFeedbackContext = createContext<CopyFeedbackContextValue>({});
19
+
20
+ function CopyFeedbackProvider({
21
+ children,
22
+ onCopied,
23
+ onCopyError,
24
+ }: CopyFeedbackContextValue & { children: ReactNode }) {
25
+ const value = useMemo(() => ({ onCopied, onCopyError }), [onCopied, onCopyError]);
26
+ return <CopyFeedbackContext.Provider value={value}>{children}</CopyFeedbackContext.Provider>;
27
+ }
28
+
29
+ function useCopyFeedback(): CopyFeedbackContextValue {
30
+ return useContext(CopyFeedbackContext);
31
+ }
32
+
33
+ export { CopyFeedbackProvider, useCopyFeedback, type CopyFeedbackContextValue };
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { Button } from '@vendure-io/ui/components/atoms/button';
4
+ import { useCopyFeedback } from '@vendure-io/ui/components/molecules/copy-feedback-provider';
4
5
  import { useCopy } from '@vendure-io/ui/hooks/use-copy';
5
6
  import { cn } from '@vendure-io/ui/lib/utils';
6
7
  import { CheckIcon, CopyIcon } from 'lucide-react';
@@ -11,9 +12,16 @@ interface CopyButtonProps extends Omit<React.ComponentProps<typeof Button>, 'val
11
12
  value: string;
12
13
  /** How long the check-mark feedback stays visible, in ms. @default 2000 */
13
14
  timeout?: number;
14
- /** Called after a successful copy. Wire your toast here — the DS never toasts. */
15
+ /**
16
+ * Called after a successful copy. Wire your toast here — the DS never toasts.
17
+ * Falls back to `CopyFeedbackProvider` when omitted (the RSC-safe path, since
18
+ * function props can't be passed from server components).
19
+ */
15
20
  onCopied?: () => void;
16
- /** Called when the clipboard write fails (e.g. permissions, insecure context). */
21
+ /**
22
+ * Called when the clipboard write fails (e.g. permissions, insecure context).
23
+ * Falls back to `CopyFeedbackProvider` when omitted.
24
+ */
17
25
  onCopyError?: (error: Error) => void;
18
26
  /** Accessible label before copying. @default "Copy" */
19
27
  copyLabel?: string;
@@ -40,6 +48,7 @@ function CopyButton({
40
48
  ...props
41
49
  }: CopyButtonProps) {
42
50
  const { copied, copy } = useCopy({ timeout });
51
+ const copyFeedback = useCopyFeedback();
43
52
 
44
53
  return (
45
54
  <Button
@@ -53,8 +62,9 @@ function CopyButton({
53
62
  onClick?.(event);
54
63
  if (event.defaultPrevented) return;
55
64
  const ok = await copy(value);
56
- if (ok) onCopied?.();
57
- else onCopyError?.(new Error('Failed to copy to the clipboard'));
65
+ if (ok) (onCopied ?? copyFeedback.onCopied)?.();
66
+ else
67
+ (onCopyError ?? copyFeedback.onCopyError)?.(new Error('Failed to copy to the clipboard'));
58
68
  }}
59
69
  {...props}
60
70
  >
@@ -71,9 +81,9 @@ interface CopyableTextProps {
71
81
  className?: string;
72
82
  /** How long the check-mark feedback stays visible, in ms. @default 2000 */
73
83
  timeout?: number;
74
- /** Called after a successful copy. Wire your toast here — the DS never toasts. */
84
+ /** Called after a successful copy. Wire your toast here — the DS never toasts. Falls back to `CopyFeedbackProvider`. */
75
85
  onCopied?: () => void;
76
- /** Called when the clipboard write fails. */
86
+ /** Called when the clipboard write fails. Falls back to `CopyFeedbackProvider`. */
77
87
  onCopyError?: (error: Error) => void;
78
88
  }
79
89
 
@@ -40,7 +40,7 @@ interface IdChipProps {
40
40
  /** Render the copy affordance. @default true */
41
41
  copyable?: boolean;
42
42
  className?: string;
43
- /** Called after a successful copy. Wire your toast here — the DS never toasts. */
43
+ /** Called after a successful copy. Wire your toast here — the DS never toasts. Falls back to `CopyFeedbackProvider`. */
44
44
  onCopied?: () => void;
45
45
  }
46
46