화면 상단에서 현재 화면의 제목과 탐색 액션을 보여주는 내비게이션 바 컴포넌트입니다.
import "./styles" ;
import IconBellLine from "@karrotmarket/lynx-monochrome-icon/IconBellLine" ;
import {
AppBar,
AppBarBackButton,
AppBarIconButton,
AppBarLeft,
AppBarMain,
AppBarRight,
} from "@/components/ui/app-bar" ;
export default function Example () {
return (
< view className = "app-bar-preview" >
< AppBar theme = "cupertino" >
< AppBarLeft >
< AppBarBackButton />
</ AppBarLeft >
< AppBarMain title = "동네생활" />
< AppBarRight >
< AppBarIconButton accessibility-label = "알림" icon = {< IconBellLine />} />
</ AppBarRight >
</ AppBar >
< view className = "app-bar-preview__content" >
< text className = "app-bar-preview__status" >화면 콘텐츠</ text >
</ view >
</ view >
);
}
npx @seed-design/cli add ui:app-bar pnpm dlx @seed-design/cli add ui:app-bar yarn dlx @seed-design/cli add ui:app-bar bun x @seed-design/cli add ui:app-bar
의존성 설치
npm install @karrotmarket/lynx-monochrome-icon @seed-design/lynx-react yarn add @karrotmarket/lynx-monochrome-icon @seed-design/lynx-react pnpm add @karrotmarket/lynx-monochrome-icon @seed-design/lynx-react bun add @karrotmarket/lynx-monochrome-icon @seed-design/lynx-react 아래 코드를 복사 후 붙여넣고 사용하세요 /**
* @file ui:app-bar
* @requires @seed-design/lynx-react@^1.0.0
* @requires @seed-design/lynx-css@^1.0.0
**/
import IconChevronLeftLine from "@karrotmarket/lynx-monochrome-icon/IconChevronLeftLine" ;
import IconXmarkLine from "@karrotmarket/lynx-monochrome-icon/IconXmarkLine" ;
import { forwardRef, type ReactNode } from "@lynx-js/react" ;
import { AppBar as SeedAppBar } from "@seed-design/lynx-react" ;
export interface AppBarProps extends SeedAppBar . RootProps {}
/**
* @see https://seed-design.io/lynx/components/app-bar
*/
export const AppBar = SeedAppBar.Root;
export interface AppBarLeftProps extends SeedAppBar . LeftProps {}
export const AppBarLeft = SeedAppBar.Left;
export interface AppBarRightProps extends SeedAppBar . RightProps {}
export const AppBarRight = SeedAppBar.Right;
export interface AppBarSlotProps extends SeedAppBar . SlotProps {}
export const AppBarSlot = SeedAppBar.Slot;
export interface AppBarMainProps extends SeedAppBar . MainProps {
/**
* The title of the app bar.
* If custom children are provided, this prop will be ignored.
*/
title ?: ReactNode ;
/**
* The subtitle of the app bar.
* If custom children are provided, this prop will be ignored.
*/
subtitle ?: ReactNode ;
}
function shouldRenderCustomMain ( children : ReactNode ) {
return (
children != null &&
typeof children !== "string" &&
typeof children !== "number" &&
typeof children !== "boolean"
);
}
export const AppBarMain = forwardRef < unknown , AppBarMainProps >(
({ title , subtitle , children , ... otherProps }, ref ) => {
if ( shouldRenderCustomMain (children)) {
return (
< SeedAppBar.Main ref = {ref} { ... otherProps}>
{children}
</ SeedAppBar.Main >
);
}
return (
< SeedAppBar.Main ref = {ref} layout = {subtitle ? "withSubtitle" : "titleOnly" } { ... otherProps}>
< SeedAppBar.Title >{children ?? title}</ SeedAppBar.Title >
{subtitle ? < SeedAppBar.Subtitle >{subtitle}</ SeedAppBar.Subtitle > : null }
</ SeedAppBar.Main >
);
},
);
AppBarMain.displayName = "AppBarMain" ;
export interface AppBarIconButtonProps extends SeedAppBar . IconButtonProps {}
export const AppBarIconButton = SeedAppBar.IconButton;
export interface AppBarBackButtonProps extends Omit < AppBarIconButtonProps , "icon" > {
icon ?: AppBarIconButtonProps [ "icon" ];
}
export const AppBarBackButton = forwardRef < unknown , AppBarBackButtonProps >(
(
{
icon = < IconChevronLeftLine />,
"accessibility-label" : accessibilityLabel = "뒤로" ,
... otherProps
},
ref,
) => {
return (
< AppBarIconButton
ref = {ref}
accessibility-label = {accessibilityLabel}
icon = {icon}
{ ... otherProps}
/>
);
},
);
AppBarBackButton.displayName = "AppBarBackButton" ;
export interface AppBarCloseButtonProps extends Omit < AppBarIconButtonProps , "icon" > {
icon ?: AppBarIconButtonProps [ "icon" ];
}
export const AppBarCloseButton = forwardRef < unknown , AppBarCloseButtonProps >(
(
{ icon = < IconXmarkLine />, "accessibility-label" : accessibilityLabel = "닫기" , ... otherProps },
ref,
) => {
return (
< AppBarIconButton
ref = {ref}
accessibility-label = {accessibilityLabel}
icon = {icon}
{ ... otherProps}
/>
);
},
);
AppBarCloseButton.displayName = "AppBarCloseButton" ;
/**
* 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.
*/
고유 prop이 없습니다. className, style, children, bindlayoutchange를 받습니다.
layout?"titleOnly" | "withSubtitle" | undefined
theme?"cupertino" | "android" | undefined
transitionStyle?"slideFromRightIOS" | "fadeFromBottomAndroid" | "fadeIn" | undefined
tone?"layer" | "transparent" | undefined
고유 prop이 없습니다. className, style, children, bindlayoutchange를 받습니다.
고유 prop이 없습니다. className, style, children을 받습니다.
icon?ReactElement < LynxIconElementProps, string | JSXElementConstructor < any >> | undefined
icon?ReactElement < LynxIconElementProps, string | JSXElementConstructor < any >> | undefined
icon?ReactElement < LynxIconElementProps, string | JSXElementConstructor < any >> | undefined
AppBarBackButton과 AppBarCloseButton에는 이동 동작이 없습니다. bindtap으로 앱의 탐색 함수를 연결하세요.
import {
AppBar,
AppBarBackButton,
AppBarLeft,
AppBarMain,
AppBarRight,
} from "@/components/ui/app-bar" ;
export function Header ({ goBack } : { goBack : () => void }) {
return (
< AppBar >
< AppBarLeft >
< AppBarBackButton bindtap = {goBack} />
</ AppBarLeft >
< AppBarMain title = "동네생활" />
< AppBarRight />
</ AppBar >
);
}
AppBarMain에 subtitle을 전달하면 layout="withSubtitle"가 자동으로 적용됩니다.
import "./styles" ;
import { AppBar, AppBarMain } from "@/components/ui/app-bar" ;
export default function Example () {
return (
< view className = "app-bar-preview" >
< AppBar theme = "cupertino" >
< AppBarMain title = "관심 목록" subtitle = "3개의 새 소식" />
</ AppBar >
< view className = "app-bar-preview__content" >
< text className = "app-bar-preview__status" >제목과 부제목을 함께 표시한 AppBar</ text >
</ view >
</ view >
);
}
왼쪽과 오른쪽에는 아이콘 버튼이나 직접 만든 슬롯을 배치할 수 있습니다. Cupertino theme에서는 좌우 슬롯 너비를 기준으로 제목이 가운데에 오도록 여백을 보정합니다.
각 슬롯의 가장자리 아이콘 버튼에만 보정이 적용됩니다. 직접 만든 레이아웃 박스 안의 버튼은 edge="leading", "trailing", "both" 중 하나를 지정하세요.
import "./styles" ;
import { useState } from "@lynx-js/react" ;
import {
AppBar,
AppBarBackButton,
AppBarCloseButton,
AppBarLeft,
AppBarMain,
AppBarRight,
AppBarSlot,
} from "@/components/ui/app-bar" ;
export default function Example () {
const [ lastAction , setLastAction ] = useState ( "없음" );
function handleBack () {
"background only" ;
setLastAction ( "뒤로" );
}
function handleClose () {
"background only" ;
setLastAction ( "닫기" );
}
return (
< view className = "app-bar-preview" >
< AppBar theme = "cupertino" >
< AppBarLeft >
< AppBarBackButton bindtap = {handleBack} />
</ AppBarLeft >
< AppBarMain title = "작성하기" />
< AppBarRight >
< AppBarSlot >
< text >완료</ text >
</ AppBarSlot >
< AppBarCloseButton bindtap = {handleClose} />
</ AppBarRight >
</ AppBar >
< view className = "app-bar-preview__content" >
< text className = "app-bar-preview__status" >마지막 액션: {lastAction}</ text >
</ view >
</ view >
);
}
다른 아이콘을 쓰려면 AppBarIconButton의 icon prop에 Lynx 아이콘을 전달합니다.
import IconBellLine from "@karrotmarket/lynx-monochrome-icon/IconBellLine" ;
import {
AppBar,
AppBarBackButton,
AppBarIconButton,
AppBarLeft,
AppBarMain,
AppBarRight,
} from "@/components/ui/app-bar" ;
export function Header ({ goBack } : { goBack : () => void }) {
return (
< AppBar >
< AppBarLeft >
< AppBarBackButton bindtap = {goBack} />
</ AppBarLeft >
< AppBarMain title = "동네생활" />
< AppBarRight >
< AppBarIconButton accessibility-label = "알림" icon = {< IconBellLine />} />
</ AppBarRight >
</ AppBar >
);
}
여러 버튼 중 가장자리에 있는 버튼만 여백을 보정합니다. 조건부 렌더링으로 버튼 수가 바뀌어도 제목은 가운데에 유지됩니다.
import "./styles" ;
import IconBellLine from "@karrotmarket/lynx-monochrome-icon/IconBellLine" ;
import IconMagnifyingglassLine from "@karrotmarket/lynx-monochrome-icon/IconMagnifyingglassLine" ;
import { useState } from "@lynx-js/react" ;
import { ActionButton } from "@seed-design/lynx-react" ;
import {
AppBar,
AppBarBackButton,
AppBarCloseButton,
AppBarIconButton,
AppBarLeft,
AppBarMain,
AppBarRight,
} from "@/components/ui/app-bar" ;
export default function Example () {
const [ showExtraActions , setShowExtraActions ] = useState ( true );
const [ lastAction , setLastAction ] = useState ( "없음" );
function toggleExtraActions () {
"background only" ;
setShowExtraActions (( value ) => ! value);
}
function recordAction ( action : string ) {
"background only" ;
setLastAction (action);
}
return (
< view className = "app-bar-preview" >
< AppBar theme = "cupertino" >
< AppBarLeft >
< AppBarBackButton bindtap = {() => recordAction ( "뒤로" )} />
</ AppBarLeft >
< AppBarMain title = "동네생활" />
< AppBarRight >
< AppBarIconButton
accessibility-label = "알림"
icon = {< IconBellLine />}
bindtap = {() => recordAction ( "알림" )}
/>
{showExtraActions && (
<>
< AppBarIconButton
accessibility-label = "검색"
icon = {< IconMagnifyingglassLine />}
bindtap = {() => recordAction ( "검색" )}
/>
< AppBarCloseButton bindtap = {() => recordAction ( "닫기" )} />
</>
)}
</ AppBarRight >
</ AppBar >
< view className = "app-bar-preview__content app-bar-preview__controls" >
< ActionButton size = "small" variant = "neutralWeak" bindtap = {toggleExtraActions}>
{showExtraActions ? "검색·닫기 숨기기" : "검색·닫기 표시" }
</ ActionButton >
< text className = "app-bar-preview__status" >마지막 액션: {lastAction}</ text >
</ view >
</ view >
);
}
직접 만든 레이아웃 박스나 AppBarSlot 안의 버튼에는 edge를 지정하세요. 일반적인 AppBarLeft와 AppBarRight의 직계 아이콘 버튼에는 필요하지 않습니다.
< AppBarRight >
< AppBarSlot >
< AppBarCloseButton edge = "trailing" bindtap = {close} />
</ AppBarSlot >
</ AppBarRight >
기본값인 tone="layer"는 레이어 배경을 표시합니다. tone="transparent"는 배경을 투명하게 하고 제목과 아이콘에 밝은 색상을 적용하므로 사진이나 어두운 배경 위에서 사용하세요. 배경과 스크롤에 따른 tone 전환은 호출부에서 관리합니다.
import "./styles" ;
import { useState } from "@lynx-js/react" ;
import {
AppBar,
AppBarBackButton,
AppBarCloseButton,
AppBarLeft,
AppBarMain,
AppBarRight,
} from "@/components/ui/app-bar" ;
export default function Example () {
const [ lastAction , setLastAction ] = useState ( "배경 위에 겹쳐지는 AppBar" );
function handleBack () {
"background only" ;
setLastAction ( "뒤로 버튼을 눌렀습니다" );
}
function handleClose () {
"background only" ;
setLastAction ( "닫기 버튼을 눌렀습니다" );
}
return (
< view className = "app-bar-preview app-bar-preview--transparent" >
< AppBar theme = "cupertino" tone = "transparent" >
< AppBarLeft >
< AppBarBackButton bindtap = {handleBack} />
</ AppBarLeft >
< AppBarMain title = "사진 보기" />
< AppBarRight >
< AppBarCloseButton bindtap = {handleClose} />
</ AppBarRight >
</ AppBar >
< view className = "app-bar-preview__content" >
< text className = "app-bar-preview__overlay-text" >{lastAction}</ text >
</ view >
</ view >
);
}
Lynx에서는 HTML ARIA 속성 대신 Lynx native 접근성 속성을 사용합니다. AppBarIconButton은 기본적으로 accessibility-element={true}와 accessibility-traits="button"을 적용하며, 아이콘만으로 의미를 알 수 없는 버튼에는 accessibility-label을 전달해야 합니다.
import IconBellLine from "@karrotmarket/lynx-monochrome-icon/IconBellLine" ;
< AppBarIconButton accessibility-label = "알림" icon = {< IconBellLine />} />
AppBarBackButton, AppBarCloseButton은 각각 "뒤로", "닫기" 라벨을 기본값으로 제공합니다.
theme은 "cupertino"와 "android"를 지원합니다. 지정하지 않으면 플랫폼에 맞는 값을 사용합니다.
< AppBar >
< AppBarMain title = "자동 theme" />
</ AppBar >
import "./styles" ;
import {
AppBar,
AppBarBackButton,
AppBarCloseButton,
AppBarLeft,
AppBarMain,
AppBarRight,
} from "@/components/ui/app-bar" ;
export default function Example () {
return (
< view className = "app-bar-preview__platforms" >
< view className = "app-bar-preview__platform" >
< text className = "app-bar-preview__label" >Cupertino</ text >
< view className = "app-bar-preview" >
< AppBar theme = "cupertino" >
< AppBarLeft >
< AppBarBackButton />
</ AppBarLeft >
< AppBarMain title = "화면 제목" subtitle = "보조 제목" />
< AppBarRight >
< AppBarCloseButton />
</ AppBarRight >
</ AppBar >
</ view >
</ view >
< view className = "app-bar-preview__platform" >
< text className = "app-bar-preview__label" >Android</ text >
< view className = "app-bar-preview" >
< AppBar theme = "android" >
< AppBarLeft >
< AppBarBackButton />
</ AppBarLeft >
< AppBarMain title = "화면 제목" subtitle = "보조 제목" />
< AppBarRight >
< AppBarCloseButton />
</ AppBarRight >
</ AppBar >
</ view >
</ view >
</ view >
);
}
클릭 이벤트는 onClick 대신 bindtap으로 받습니다.
아이콘 버튼의 접근성 이름은 aria-label 대신 accessibility-label로 지정합니다.
theme과 tone으로 플랫폼별 디자인과 배경을 지정합니다.
뒤로 가기와 닫기 버튼에는 앱의 탐색 함수를 직접 연결해야 합니다.
Stackflow activity와 자동으로 연동하지 않습니다.
swipe-back 전환 애니메이션은 화면 탐색 계층에서 처리해야 합니다.