Fix case-study share chrome, hero lockup width, and copy confirmation in the share dialog.
Marketing pages omitted create-flow nav copy, so Share rendered as a translation key; the case-study banner squeezed hero text to the thumbnail width; Copy link had no hover or copied state. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -3,8 +3,9 @@
|
||||
/**
|
||||
* Figma: Community Rule System — "Modal / Share"
|
||||
* https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=22073-30884
|
||||
* Copy-link hover / Copied! : 23769:27494 / 23769:27443
|
||||
*/
|
||||
import { memo, useId, useRef } from "react";
|
||||
import { memo, useCallback, useEffect, useId, useRef, useState } from "react";
|
||||
import { useTranslation } from "../../../contexts/MessagesContext";
|
||||
import { useCreateModalA11y } from "../Create/useCreateModalA11y";
|
||||
import { ShareView } from "./Share.view";
|
||||
@@ -15,9 +16,27 @@ const ShareContainer = memo<ShareProps>((props) => {
|
||||
const overlayRef = useRef<HTMLDivElement>(null);
|
||||
const titleId = useId();
|
||||
const t = useTranslation("modals.share");
|
||||
const [linkCopied, setLinkCopied] = useState(false);
|
||||
|
||||
useCreateModalA11y(props.isOpen, props.onClose, dialogRef);
|
||||
|
||||
useEffect(() => {
|
||||
if (!props.isOpen) {
|
||||
setLinkCopied(false);
|
||||
}
|
||||
}, [props.isOpen]);
|
||||
|
||||
const onCopyLinkClick = useCallback(async () => {
|
||||
try {
|
||||
const result = await props.onCopyLink();
|
||||
if (result !== false) {
|
||||
setLinkCopied(true);
|
||||
}
|
||||
} catch {
|
||||
setLinkCopied(false);
|
||||
}
|
||||
}, [props.onCopyLink]);
|
||||
|
||||
return (
|
||||
<ShareView
|
||||
{...props}
|
||||
@@ -27,6 +46,10 @@ const ShareContainer = memo<ShareProps>((props) => {
|
||||
title={t("title")}
|
||||
description={t("description")}
|
||||
copyLinkLabel={t("copyLink")}
|
||||
copiedLabel={t("copied")}
|
||||
copiedLive={t("copiedLive")}
|
||||
linkCopied={linkCopied}
|
||||
onCopyLinkClick={onCopyLinkClick}
|
||||
signalLabel={t("signal")}
|
||||
slackLabel={t("slack")}
|
||||
discordLabel={t("discord")}
|
||||
|
||||
@@ -4,7 +4,8 @@ import type { CreateModalBackdropVariant } from "../Create/CreateModalFrame.view
|
||||
export type ShareProps = {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onCopyLink: () => void | Promise<void>;
|
||||
/** Return `false` when the clipboard write did not succeed. */
|
||||
onCopyLink: () => void | boolean | Promise<void | boolean>;
|
||||
onEmailShare: () => void;
|
||||
onSignalShare: () => void | Promise<void>;
|
||||
onSlackShare: () => void | Promise<void>;
|
||||
@@ -20,6 +21,10 @@ export type ShareViewProps = ShareProps & {
|
||||
title: string;
|
||||
description: string;
|
||||
copyLinkLabel: string;
|
||||
copiedLabel: string;
|
||||
copiedLive: string;
|
||||
linkCopied: boolean;
|
||||
onCopyLinkClick: () => void | Promise<void>;
|
||||
signalLabel: string;
|
||||
slackLabel: string;
|
||||
discordLabel: string;
|
||||
@@ -34,4 +39,5 @@ export type ShareChannelTileProps = {
|
||||
onClick: () => void | Promise<void>;
|
||||
circleClassName: string;
|
||||
icon: ReactNode;
|
||||
copied?: boolean;
|
||||
};
|
||||
|
||||
@@ -34,19 +34,32 @@ function ShareAssetIcon(props: {
|
||||
);
|
||||
}
|
||||
|
||||
function ShareChannelTile({ label, onClick, circleClassName, icon }: ShareChannelTileProps) {
|
||||
function ShareChannelTile({
|
||||
label,
|
||||
onClick,
|
||||
circleClassName,
|
||||
icon,
|
||||
copied = false,
|
||||
}: ShareChannelTileProps) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void onClick()}
|
||||
className="flex w-16 shrink-0 flex-col items-center gap-2 rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-border-invert-primary)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--color-surface-default-primary)]"
|
||||
aria-pressed={copied || undefined}
|
||||
className="group flex w-16 shrink-0 flex-col items-center gap-2 rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-border-invert-primary)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--color-surface-default-primary)]"
|
||||
>
|
||||
<div
|
||||
className={`flex h-[60px] w-[60px] items-center justify-center rounded-full border border-solid ${circleClassName}`}
|
||||
className={`flex h-[60px] w-[60px] items-center justify-center rounded-full border border-solid transition-[transform,background-color,border-color,filter] duration-150 ease-out group-active:scale-90 ${circleClassName}`}
|
||||
>
|
||||
{icon}
|
||||
</div>
|
||||
<span className="max-w-[4.5rem] text-center text-x-small-label text-[var(--color-content-default-tertiary)]">
|
||||
<span
|
||||
className={`max-w-[4.5rem] text-center text-x-small-label ${
|
||||
copied
|
||||
? "text-[var(--color-border-default-positive-primary)]"
|
||||
: "text-[var(--color-content-default-tertiary)]"
|
||||
}`}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
</button>
|
||||
@@ -56,7 +69,6 @@ function ShareChannelTile({ label, onClick, circleClassName, icon }: ShareChanne
|
||||
export const ShareView = memo(function ShareView({
|
||||
isOpen,
|
||||
onClose,
|
||||
onCopyLink,
|
||||
onEmailShare,
|
||||
onSignalShare,
|
||||
onSlackShare,
|
||||
@@ -69,6 +81,10 @@ export const ShareView = memo(function ShareView({
|
||||
title,
|
||||
description,
|
||||
copyLinkLabel,
|
||||
copiedLabel,
|
||||
copiedLive,
|
||||
linkCopied,
|
||||
onCopyLinkClick,
|
||||
signalLabel,
|
||||
slackLabel,
|
||||
discordLabel,
|
||||
@@ -109,36 +125,50 @@ export const ShareView = memo(function ShareView({
|
||||
{/* Channel circle hexes are third-party brand colors (copy/link, Signal, Slack, Discord), not DS tokens. */}
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<ShareChannelTile
|
||||
label={copyLinkLabel}
|
||||
onClick={onCopyLink}
|
||||
circleClassName="border-[#444444] bg-[#333333]"
|
||||
icon={<ShareAssetIcon name="link" width={24} height={24} />}
|
||||
label={linkCopied ? copiedLabel : copyLinkLabel}
|
||||
onClick={onCopyLinkClick}
|
||||
copied={linkCopied}
|
||||
circleClassName={
|
||||
linkCopied
|
||||
? "border-2 border-[#444444] bg-[#333333]"
|
||||
: "border-[#444444] bg-[#333333] group-hover:border-2 group-hover:border-[var(--color-border-default-positive-primary)] group-hover:bg-[#444444]"
|
||||
}
|
||||
icon={
|
||||
<ShareAssetIcon
|
||||
name={linkCopied ? "check" : "link"}
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<ShareChannelTile
|
||||
label={signalLabel}
|
||||
onClick={onSignalShare}
|
||||
circleClassName="border-[#3a76f0] bg-[#3a76f0]"
|
||||
circleClassName="border-[#3a76f0] bg-[#3a76f0] group-hover:brightness-110"
|
||||
icon={<ShareAssetIcon name="signal" width={26} height={26} />}
|
||||
/>
|
||||
<ShareChannelTile
|
||||
label={slackLabel}
|
||||
onClick={onSlackShare}
|
||||
circleClassName="border-[#4a154b] bg-[#4a154b]"
|
||||
circleClassName="border-[#4a154b] bg-[#4a154b] group-hover:brightness-110"
|
||||
icon={<ShareAssetIcon name="slack" width={26} height={26} />}
|
||||
/>
|
||||
<ShareChannelTile
|
||||
label={discordLabel}
|
||||
onClick={onDiscordShare}
|
||||
circleClassName="border-[#5865f2] bg-[#5865f2]"
|
||||
circleClassName="border-[#5865f2] bg-[#5865f2] group-hover:brightness-110"
|
||||
icon={<ShareAssetIcon name="discord" width={30} height={30} />}
|
||||
/>
|
||||
<ShareChannelTile
|
||||
label={emailLabel}
|
||||
onClick={onEmailShare}
|
||||
circleClassName="border-[var(--color-surface-default-brand-kiwi)] bg-[var(--color-surface-default-brand-kiwi)]"
|
||||
circleClassName="border-[var(--color-surface-default-brand-kiwi)] bg-[var(--color-surface-default-brand-kiwi)] group-hover:brightness-110"
|
||||
icon={<ShareAssetIcon name="mail" width={24} height={24} />}
|
||||
/>
|
||||
</div>
|
||||
<p className="sr-only" aria-live="polite">
|
||||
{linkCopied ? copiedLive : ""}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ModalFooter
|
||||
|
||||
Reference in New Issue
Block a user