Implement share and export components
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
"use client";
|
||||
|
||||
/**
|
||||
* Figma: Community Rule System — "Modal / Share"
|
||||
* https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=22073-30884
|
||||
*/
|
||||
import { memo, useId, useRef } from "react";
|
||||
import { useTranslation } from "../../../contexts/MessagesContext";
|
||||
import { useCreateModalA11y } from "../Create/useCreateModalA11y";
|
||||
import { ShareView } from "./Share.view";
|
||||
import type { ShareProps } from "./Share.types";
|
||||
|
||||
const ShareContainer = memo<ShareProps>((props) => {
|
||||
const dialogRef = useRef<HTMLDivElement>(null);
|
||||
const overlayRef = useRef<HTMLDivElement>(null);
|
||||
const titleId = useId();
|
||||
const t = useTranslation("modals.share");
|
||||
|
||||
useCreateModalA11y(props.isOpen, props.onClose, dialogRef);
|
||||
|
||||
return (
|
||||
<ShareView
|
||||
{...props}
|
||||
dialogRef={dialogRef}
|
||||
overlayRef={overlayRef}
|
||||
titleId={titleId}
|
||||
title={t("title")}
|
||||
description={t("description")}
|
||||
copyLinkLabel={t("copyLink")}
|
||||
signalLabel={t("signal")}
|
||||
slackLabel={t("slack")}
|
||||
discordLabel={t("discord")}
|
||||
emailLabel={t("email")}
|
||||
doneLabel={t("done")}
|
||||
closeDialogAriaLabel={t("closeDialogAriaLabel")}
|
||||
moreOptionsAriaLabel={t("moreOptionsAriaLabel")}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
ShareContainer.displayName = "Share";
|
||||
|
||||
export default ShareContainer;
|
||||
@@ -0,0 +1,37 @@
|
||||
import type { ReactNode, RefObject } from "react";
|
||||
import type { CreateModalBackdropVariant } from "../Create/CreateModalFrame.view";
|
||||
|
||||
export type ShareProps = {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onCopyLink: () => void | Promise<void>;
|
||||
onEmailShare: () => void;
|
||||
onSignalShare: () => void | Promise<void>;
|
||||
onSlackShare: () => void | Promise<void>;
|
||||
onDiscordShare: () => void | Promise<void>;
|
||||
className?: string;
|
||||
backdropVariant?: CreateModalBackdropVariant;
|
||||
};
|
||||
|
||||
export type ShareViewProps = ShareProps & {
|
||||
dialogRef: RefObject<HTMLDivElement | null>;
|
||||
overlayRef: RefObject<HTMLDivElement | null>;
|
||||
titleId: string;
|
||||
title: string;
|
||||
description: string;
|
||||
copyLinkLabel: string;
|
||||
signalLabel: string;
|
||||
slackLabel: string;
|
||||
discordLabel: string;
|
||||
emailLabel: string;
|
||||
doneLabel: string;
|
||||
closeDialogAriaLabel: string;
|
||||
moreOptionsAriaLabel: string;
|
||||
};
|
||||
|
||||
export type ShareChannelTileProps = {
|
||||
label: string;
|
||||
onClick: () => void | Promise<void>;
|
||||
circleClassName: string;
|
||||
icon: ReactNode;
|
||||
};
|
||||
@@ -0,0 +1,165 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { memo } from "react";
|
||||
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: {
|
||||
src:
|
||||
| "/assets/Share/Discord.svg"
|
||||
| "/assets/Share/Link.svg"
|
||||
| "/assets/Share/Mail.svg"
|
||||
| "/assets/Share/Signal.svg"
|
||||
| "/assets/Share/Slack.svg";
|
||||
width: number;
|
||||
height: number;
|
||||
}) {
|
||||
const { src, width, height } = props;
|
||||
return (
|
||||
<Image
|
||||
src={src}
|
||||
alt=""
|
||||
width={width}
|
||||
height={height}
|
||||
className="shrink-0"
|
||||
unoptimized
|
||||
aria-hidden
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function ShareChannelTile({ label, onClick, circleClassName, icon }: 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)]"
|
||||
>
|
||||
<div
|
||||
className={`flex h-[60px] w-[60px] items-center justify-center rounded-full border border-solid ${circleClassName}`}
|
||||
>
|
||||
{icon}
|
||||
</div>
|
||||
<span className="max-w-[4.5rem] text-center font-inter text-[12px] font-medium leading-4 text-[var(--color-content-default-tertiary)]">
|
||||
{label}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export const ShareView = memo(function ShareView({
|
||||
isOpen,
|
||||
onClose,
|
||||
onCopyLink,
|
||||
onEmailShare,
|
||||
onSignalShare,
|
||||
onSlackShare,
|
||||
onDiscordShare,
|
||||
className = "",
|
||||
backdropVariant = "default",
|
||||
dialogRef,
|
||||
overlayRef,
|
||||
titleId,
|
||||
title,
|
||||
description,
|
||||
copyLinkLabel,
|
||||
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">
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<ShareChannelTile
|
||||
label={copyLinkLabel}
|
||||
onClick={onCopyLink}
|
||||
circleClassName="border-[#444444] bg-[#333333]"
|
||||
icon={<ShareAssetIcon src="/assets/Share/Link.svg" width={24} height={24} />}
|
||||
/>
|
||||
<ShareChannelTile
|
||||
label={signalLabel}
|
||||
onClick={onSignalShare}
|
||||
circleClassName="border-[#3a76f0] bg-[#3a76f0]"
|
||||
icon={<ShareAssetIcon src="/assets/Share/Signal.svg" width={26} height={26} />}
|
||||
/>
|
||||
<ShareChannelTile
|
||||
label={slackLabel}
|
||||
onClick={onSlackShare}
|
||||
circleClassName="border-[#4a154b] bg-[#4a154b]"
|
||||
icon={<ShareAssetIcon src="/assets/Share/Slack.svg" width={26} height={26} />}
|
||||
/>
|
||||
<ShareChannelTile
|
||||
label={discordLabel}
|
||||
onClick={onDiscordShare}
|
||||
circleClassName="border-[#5865f2] bg-[#5865f2]"
|
||||
icon={<ShareAssetIcon src="/assets/Share/Discord.svg" width={30} height={30} />}
|
||||
/>
|
||||
<ShareChannelTile
|
||||
label={emailLabel}
|
||||
onClick={onEmailShare}
|
||||
circleClassName="border-[var(--color-surface-default-brand-kiwi)] bg-[var(--color-surface-default-brand-kiwi)]"
|
||||
icon={<ShareAssetIcon src="/assets/Share/Mail.svg" width={24} height={24} />}
|
||||
/>
|
||||
</div>
|
||||
</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";
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default } from "./Share.container";
|
||||
export type { ShareProps } from "./Share.types";
|
||||
Reference in New Issue
Block a user