Files
community-rule/app/components/modals/Share/Share.view.tsx
T
adilalloandCursor ce581a42c8 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>
2026-09-09 22:00:19 -06:00

197 lines
6.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import Image from "next/image";
import { memo } from "react";
import {
getAssetPath,
shareIconPath,
type ShareIconName,
} from "../../../../lib/assetUtils";
import ContentLockup from "../../type/ContentLockup";
import Button from "../../buttons/Button";
import ModalHeader from "../ModalHeader";
import ModalFooter from "../ModalFooter";
import { CreateModalFrameView } from "../Create/CreateModalFrame.view";
import type { ShareChannelTileProps, ShareViewProps } from "./Share.types";
/** Decorative glyphs in `public/assets/share/` — sizes match prior inline SVGs within the 60×60 circles. */
function ShareAssetIcon(props: {
name: ShareIconName;
width: number;
height: number;
}) {
const { name, width, height } = props;
return (
<Image
src={getAssetPath(shareIconPath(name))}
alt=""
width={width}
height={height}
className="shrink-0"
unoptimized
aria-hidden
/>
);
}
function ShareChannelTile({
label,
onClick,
circleClassName,
icon,
copied = false,
}: ShareChannelTileProps) {
return (
<button
type="button"
onClick={() => void onClick()}
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 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 ${
copied
? "text-[var(--color-border-default-positive-primary)]"
: "text-[var(--color-content-default-tertiary)]"
}`}
>
{label}
</span>
</button>
);
}
export const ShareView = memo(function ShareView({
isOpen,
onClose,
onEmailShare,
onSignalShare,
onSlackShare,
onDiscordShare,
className = "",
backdropVariant = "blurredYellow",
dialogRef,
overlayRef,
titleId,
title,
description,
copyLinkLabel,
copiedLabel,
copiedLive,
linkCopied,
onCopyLinkClick,
signalLabel,
slackLabel,
discordLabel,
emailLabel,
doneLabel,
closeDialogAriaLabel,
moreOptionsAriaLabel,
}: ShareViewProps) {
return (
<CreateModalFrameView
isOpen={isOpen}
onOverlayClick={onClose}
backdropVariant={backdropVariant}
className={`max-h-[90vh] w-[min(546px,calc(100vw-32px))] max-w-[546px] min-h-0 ${className}`}
ariaLabel={title}
ariaLabelledBy={titleId}
overlayRef={overlayRef}
dialogRef={dialogRef}
>
<ModalHeader
onClose={onClose}
onMoreOptions={onClose}
closeButtonAriaLabel={closeDialogAriaLabel}
moreOptionsAriaLabel={moreOptionsAriaLabel}
/>
<div className="shrink-0 bg-[var(--color-surface-default-primary)] px-[24px] py-[12px]">
<ContentLockup
title={title}
description={description}
variant="modal"
alignment="left"
titleId={titleId}
/>
</div>
<div className="scrollbar-design flex min-h-0 flex-1 flex-col overflow-x-clip overflow-y-auto px-[24px] pb-6 pt-0">
{/* 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={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] group-hover:brightness-110"
icon={<ShareAssetIcon name="signal" width={26} height={26} />}
/>
<ShareChannelTile
label={slackLabel}
onClick={onSlackShare}
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] 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)] 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
showBackButton={false}
showNextButton={false}
stepper={false}
footerContent={
<div className="absolute right-[16px] top-[12px] flex max-w-[calc(100%-32px)] flex-wrap items-center justify-end gap-3">
<Button
buttonType="filled"
palette="default"
size="medium"
type="button"
onClick={onClose}
>
{doneLabel}
</Button>
</div>
}
/>
</CreateModalFrameView>
);
});
ShareView.displayName = "ShareView";