Lynx
Callout
사용자에게 중요한 정보나 팁을 시각적으로 강조하여 전달하는 메시지 컴포넌트입니다.
사용 가능 버전@seed-design/[email protected], @seed-design/[email protected]
Lynx 예제를 불러오는 중입니다.
import "./styles";
import { ActionableCallout, Callout, DismissibleCallout } from "@/components/ui/callout";
export default function Example() {
return (
<view className="callout-preview">
<Callout description="Aute nulla proident tempor minim eiusmod. In nostrud officia irure laborum." />
<ActionableCallout description="Aute nulla proident tempor minim eiusmod. In nostrud officia irure laborum." />
<DismissibleCallout description="Aute nulla proident tempor minim eiusmod. In nostrud officia irure laborum." />
</view>
);
}설치
npx @seed-design/cli@latest add ui:calloutpnpm dlx @seed-design/cli@latest add ui:calloutyarn dlx @seed-design/cli@latest add ui:calloutbun x @seed-design/cli@latest add ui:callout의존성 설치
npm install @karrotmarket/lynx-monochrome-icon @seed-design/lynx-reactyarn add @karrotmarket/lynx-monochrome-icon @seed-design/lynx-reactpnpm add @karrotmarket/lynx-monochrome-icon @seed-design/lynx-reactbun add @karrotmarket/lynx-monochrome-icon @seed-design/lynx-react아래 코드를 복사 후 붙여넣고 사용하세요
/**
* @file ui:callout
* @requires @seed-design/lynx-react@^1.0.0
* @requires @seed-design/lynx-css@^1.0.0
**/
import IconChevronRightLine from "@karrotmarket/lynx-monochrome-icon/IconChevronRightLine";
import IconXmarkLine from "@karrotmarket/lynx-monochrome-icon/IconXmarkLine";
import * as React from "@lynx-js/react";
import {
Callout as SeedCallout,
PrefixIcon,
SuffixIcon,
type PrefixIconProps,
} from "@seed-design/lynx-react";
export interface CalloutProps
extends Omit<SeedCallout.RootProps, "children" | "open" | "defaultOpen" | "onDismiss"> {
prefixIcon?: PrefixIconProps["icon"];
title?: React.ReactNode;
description: React.ReactNode;
linkProps?: SeedCallout.LinkProps;
}
/**
* @see https://seed-design.io/lynx/components/callout
*/
export const Callout = React.forwardRef<unknown, CalloutProps>(
({ prefixIcon, title, description, linkProps, ...otherProps }, ref) => {
return (
<SeedCallout.Root ref={ref} {...otherProps}>
{prefixIcon ? <PrefixIcon icon={prefixIcon} /> : null}
<SeedCallout.Content>
{title ? <SeedCallout.Title>{title}</SeedCallout.Title> : null}
<SeedCallout.Description>{description}</SeedCallout.Description>
{linkProps ? <SeedCallout.Link {...linkProps} /> : null}
</SeedCallout.Content>
</SeedCallout.Root>
);
},
);
Callout.displayName = "Callout";
export interface ActionableCalloutProps
extends Omit<SeedCallout.RootProps, "children" | "open" | "defaultOpen" | "onDismiss"> {
prefixIcon?: PrefixIconProps["icon"];
title?: React.ReactNode;
description: React.ReactNode;
}
// Lynx `Callout.Root`는 tap handler가 있어야 눌림 상태·Scale Feedback·button 접근성을 연결합니다.
// React의 `<button>`처럼 handler 없이도 탭할 수 있는 Callout로 표시합니다.
const handleEmptyTap: NonNullable<SeedCallout.RootProps["bindtap"]> = () => {};
/**
* 전체 영역을 탭할 수 있는 Callout입니다. `bindtap`이 없어도 눌림 상태와 Scale Feedback을 표시합니다.
*
* @see https://seed-design.io/lynx/components/callout
*/
export const ActionableCallout = React.forwardRef<unknown, ActionableCalloutProps>(
({ prefixIcon, title, description, bindtap = handleEmptyTap, ...otherProps }, ref) => {
return (
<SeedCallout.Root ref={ref} bindtap={bindtap} {...otherProps}>
{prefixIcon ? <PrefixIcon icon={prefixIcon} /> : null}
<SeedCallout.Content>
{title ? <SeedCallout.Title>{title}</SeedCallout.Title> : null}
<SeedCallout.Description>{description}</SeedCallout.Description>
</SeedCallout.Content>
<SuffixIcon icon={<IconChevronRightLine />} />
</SeedCallout.Root>
);
},
);
ActionableCallout.displayName = "ActionableCallout";
export interface DismissibleCalloutProps extends Omit<SeedCallout.RootProps, "children"> {
prefixIcon?: PrefixIconProps["icon"];
title?: React.ReactNode;
description: React.ReactNode;
linkProps?: SeedCallout.LinkProps;
dismissLabel?: string;
}
/**
* @see https://seed-design.io/lynx/components/callout
*/
export const DismissibleCallout = React.forwardRef<unknown, DismissibleCalloutProps>(
({ prefixIcon, title, description, linkProps, dismissLabel = "닫기", ...otherProps }, ref) => {
return (
<SeedCallout.Root ref={ref} {...otherProps}>
{prefixIcon ? <PrefixIcon icon={prefixIcon} /> : null}
<SeedCallout.Content>
{title ? <SeedCallout.Title>{title}</SeedCallout.Title> : null}
<SeedCallout.Description>{description}</SeedCallout.Description>
{linkProps ? <SeedCallout.Link {...linkProps} /> : null}
</SeedCallout.Content>
<SeedCallout.CloseButton accessibility-label={dismissLabel}>
<SuffixIcon icon={<IconXmarkLine />} />
</SeedCallout.CloseButton>
</SeedCallout.Root>
);
},
);
DismissibleCallout.displayName = "DismissibleCallout";
/**
* This file is a snippet from SEED Design, helping you get started quickly with @seed-design/* packages.
* You can extend this snippet however you want.
*/
속성
Callout
Prop
Type
prefixIcon?React.ReactElement<LynxIconElementProps, string | React.JSXElementConstructor<any>> | undefinedtitle?React.ReactNodedescriptionReact.ReactNodelinkProps?SeedCallout.LinkProps | undefinedActionableCallout
Prop
Type
prefixIcon?React.ReactElement<LynxIconElementProps, string | React.JSXElementConstructor<any>> | undefinedtitle?React.ReactNodedescriptionReact.ReactNodeDismissibleCallout
Prop
Type
prefixIcon?React.ReactElement<LynxIconElementProps, string | React.JSXElementConstructor<any>> | undefinedtitle?React.ReactNodedescriptionReact.ReactNodelinkProps?SeedCallout.LinkProps | undefineddismissLabel?string | undefined사용법
import { Callout } from "@/components/ui/callout";
export function App() {
return <Callout title="알림" description="새로운 소식을 확인해 보세요." />;
}예시
콘텐츠 구성
텍스트만
Lynx 예제를 불러오는 중입니다.
import "./styles";
import { ActionableCallout, Callout, DismissibleCallout } from "@/components/ui/callout";
const description =
"기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요.";
export default function Example() {
return (
<view className="callout-preview">
<Callout description={description} />
<ActionableCallout description={description} />
<DismissibleCallout description={description} />
</view>
);
}아이콘 포함
Lynx 예제를 불러오는 중입니다.
import "./styles";
import IconCalendarFill from "@karrotmarket/lynx-monochrome-icon/IconCalendarFill";
import { ActionableCallout, Callout, DismissibleCallout } from "@/components/ui/callout";
const description =
"기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요.";
export default function Example() {
return (
<view className="callout-preview">
<Callout prefixIcon={<IconCalendarFill />} description={description} />
<ActionableCallout prefixIcon={<IconCalendarFill />} description={description} />
<DismissibleCallout prefixIcon={<IconCalendarFill />} description={description} />
</view>
);
}제목 텍스트 포함
Lynx 예제를 불러오는 중입니다.
import "./styles";
import { ActionableCallout, Callout, DismissibleCallout } from "@/components/ui/callout";
const description =
"기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요.";
export default function Example() {
return (
<view className="callout-preview">
<Callout title="타이틀" description={description} />
<ActionableCallout title="타이틀" description={description} />
<DismissibleCallout title="타이틀" description={description} />
</view>
);
}링크 레이블 포함
ActionableCallout에서는 linkProps를 제공하지 않습니다.
Lynx 예제를 불러오는 중입니다.
import "./styles";
import { Callout, DismissibleCallout } from "@/components/ui/callout";
const description =
"기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요.";
export default function Example() {
return (
<view className="callout-preview">
<Callout description={description} linkProps={{ children: "시도해 보기" }} />
<DismissibleCallout description={description} linkProps={{ children: "시도해 보기" }} />
</view>
);
}모든 요소 포함
Lynx 예제를 불러오는 중입니다.
import "./styles";
import IconCalendarFill from "@karrotmarket/lynx-monochrome-icon/IconCalendarFill";
import { Callout, DismissibleCallout } from "@/components/ui/callout";
const description =
"기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요.";
export default function Example() {
return (
<view className="callout-preview">
<Callout
title="타이틀"
prefixIcon={<IconCalendarFill />}
description={description}
linkProps={{ children: "시도해 보기" }}
/>
<DismissibleCallout
title="타이틀"
prefixIcon={<IconCalendarFill />}
description={description}
linkProps={{ children: "시도해 보기" }}
/>
</view>
);
}톤
tone은 neutral, informative, positive, warning, critical, magic을 지원합니다. 기본값은 neutral입니다.
중립 (기본값)
Lynx 예제를 불러오는 중입니다.
import "./styles";
import IconCalendarFill from "@karrotmarket/lynx-monochrome-icon/IconCalendarFill";
import { ActionableCallout, Callout, DismissibleCallout } from "@/components/ui/callout";
const description =
"기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요.";
export default function Example() {
return (
<view className="callout-preview">
<Callout tone="neutral" prefixIcon={<IconCalendarFill />} description={description} />
<ActionableCallout
tone="neutral"
prefixIcon={<IconCalendarFill />}
description={description}
/>
<DismissibleCallout
tone="neutral"
prefixIcon={<IconCalendarFill />}
description={description}
/>
</view>
);
}정보
Lynx 예제를 불러오는 중입니다.
import "./styles";
import IconCalendarFill from "@karrotmarket/lynx-monochrome-icon/IconCalendarFill";
import { ActionableCallout, Callout, DismissibleCallout } from "@/components/ui/callout";
const description =
"기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요.";
export default function Example() {
return (
<view className="callout-preview">
<Callout tone="informative" prefixIcon={<IconCalendarFill />} description={description} />
<ActionableCallout
tone="informative"
prefixIcon={<IconCalendarFill />}
description={description}
/>
<DismissibleCallout
tone="informative"
prefixIcon={<IconCalendarFill />}
description={description}
/>
</view>
);
}긍정
Lynx 예제를 불러오는 중입니다.
import "./styles";
import IconCalendarFill from "@karrotmarket/lynx-monochrome-icon/IconCalendarFill";
import { ActionableCallout, Callout, DismissibleCallout } from "@/components/ui/callout";
const description =
"기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요.";
export default function Example() {
return (
<view className="callout-preview">
<Callout tone="positive" prefixIcon={<IconCalendarFill />} description={description} />
<ActionableCallout
tone="positive"
prefixIcon={<IconCalendarFill />}
description={description}
/>
<DismissibleCallout
tone="positive"
prefixIcon={<IconCalendarFill />}
description={description}
/>
</view>
);
}경고
Lynx 예제를 불러오는 중입니다.
import "./styles";
import IconCalendarFill from "@karrotmarket/lynx-monochrome-icon/IconCalendarFill";
import { ActionableCallout, Callout, DismissibleCallout } from "@/components/ui/callout";
const description =
"기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요.";
export default function Example() {
return (
<view className="callout-preview">
<Callout tone="warning" prefixIcon={<IconCalendarFill />} description={description} />
<ActionableCallout
tone="warning"
prefixIcon={<IconCalendarFill />}
description={description}
/>
<DismissibleCallout
tone="warning"
prefixIcon={<IconCalendarFill />}
description={description}
/>
</view>
);
}위험
Lynx 예제를 불러오는 중입니다.
import "./styles";
import IconCalendarFill from "@karrotmarket/lynx-monochrome-icon/IconCalendarFill";
import { ActionableCallout, Callout, DismissibleCallout } from "@/components/ui/callout";
const description =
"기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요.";
export default function Example() {
return (
<view className="callout-preview">
<Callout tone="critical" prefixIcon={<IconCalendarFill />} description={description} />
<ActionableCallout
tone="critical"
prefixIcon={<IconCalendarFill />}
description={description}
/>
<DismissibleCallout
tone="critical"
prefixIcon={<IconCalendarFill />}
description={description}
/>
</view>
);
}매직
Lynx 예제를 불러오는 중입니다.
import "./styles";
import IconSparkle2 from "@karrotmarket/lynx-multicolor-icon/IconSparkle2";
import { ActionableCallout, Callout, DismissibleCallout } from "@/components/ui/callout";
const description =
"기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요.";
export default function Example() {
return (
<view className="callout-preview">
<Callout tone="magic" prefixIcon={<IconSparkle2 />} description={description} />
<ActionableCallout tone="magic" prefixIcon={<IconSparkle2 />} description={description} />
<DismissibleCallout tone="magic" prefixIcon={<IconSparkle2 />} description={description} />
</view>
);
}웹 버전과의 차이
- 탭 이벤트는
onClick대신bindtap또는main-thread:bindtap으로 받습니다. - 접근성은 HTML 의미 요소와 ARIA 대신 Lynx의
accessibility-*속성을 사용합니다. ActionableCallout은bindtap이 없어도 눌림 피드백과button역할을 적용합니다.
Lynx 미지원 기능
asChild를 지원하지 않습니다.- 링크 문구는
linkProps.children, 탭 동작은linkProps.bindtap으로 전달하세요.
Last updated on