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>
197 lines
6.0 KiB
TypeScript
197 lines
6.0 KiB
TypeScript
"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";
|