diff --git a/.changeset/mcp-copy-confirmation-dropdown.md b/.changeset/mcp-copy-confirmation-dropdown.md new file mode 100644 index 000000000..ad5fdbef0 --- /dev/null +++ b/.changeset/mcp-copy-confirmation-dropdown.md @@ -0,0 +1,5 @@ +--- +"gitbook": patch +--- + +Fix the page-actions dropdown closing before the "Copied" confirmation could be shown when copying the MCP server URL, an MCP install command, or the page as Markdown. diff --git a/packages/gitbook/src/components/PageActions/PageActions.tsx b/packages/gitbook/src/components/PageActions/PageActions.tsx index 37eaa02ea..7e9ff1648 100644 --- a/packages/gitbook/src/components/PageActions/PageActions.tsx +++ b/packages/gitbook/src/components/PageActions/PageActions.tsx @@ -98,9 +98,14 @@ const createCopiedStateStore = () => { set({ copied: true }); timeoutRef = setTimeout(() => { - set({ copied: false }); onSuccess?.(); - timeoutRef = null; + + // Delay resetting the label past the dropdown's closing animation (`scaleOut`, + // 200ms) so the "Copied" label doesn't flip back while still visible mid-fade. + timeoutRef = setTimeout(() => { + set({ copied: false }); + timeoutRef = null; + }, 200); }, 1500); }, })); @@ -153,13 +158,7 @@ export function ActionCopyMarkdown(props: { return result; }; - const onClick = async (e: React.MouseEvent) => { - // Prevent default behavior for non-default actions to avoid closing the dropdown. - // This allows showing transient UI (e.g., a "copied" state) inside the menu item. - if (!isDefaultAction) { - e.preventDefault(); - } - + const onClick = async () => { copy(markdownCache.get(markdownPageURL) || (await fetchMarkdown()), { onSuccess: () => { // We close the dropdown menu if the action is a dropdown menu item and not the default action. @@ -179,6 +178,7 @@ export function ActionCopyMarkdown(props: { description={tString(language, 'copy_page_markdown')} onClick={onClick} loading={loading} + closeOnClick={false} /> ); } @@ -421,9 +421,7 @@ export function CopyToClipboard(props: { icon={copied ? 'check' : icon} label={copied ? tString(language, 'code_copied') : label} description={description} - onClick={(e) => { - e.preventDefault(); - + onClick={() => { copy(data, { onSuccess: () => { if (type === 'dropdown-menu-item') { @@ -432,6 +430,7 @@ export function CopyToClipboard(props: { }, }); }} + closeOnClick={false} /> ); } @@ -453,6 +452,7 @@ function PageActionWrapper(props: { target?: React.HTMLAttributeAnchorTarget; disabled?: boolean; loading?: boolean; + closeOnClick?: boolean; }) { const { type, @@ -465,6 +465,7 @@ function PageActionWrapper(props: { description, disabled, loading, + closeOnClick, } = props; if (type === 'button') { @@ -498,6 +499,7 @@ function PageActionWrapper(props: { target={target} onClick={onClick} disabled={disabled || loading} + closeOnClick={closeOnClick} >
{loading ? (