@seojaeho/app-launcher
v0.1.11
Published
Unified app launcher menu component for CROSS ecosystem services
Downloads
1,890
Readme
@seojaeho/app-launcher
CROSS 생태계 서비스 전체에서 사용하는 통합 앱 런처 메뉴 컴포넌트.
S3에 호스팅된 글로벌 메뉴 메타데이터를 fetch하여 일관된 UI로 렌더링한다. Trigger는 headless(소비자가 자유롭게 구성), Content는 패키지가 디자인을 통제한다. Desktop에서는 Popover, Mobile에서는 Drawer로 자동 전환된다.
Install
npm install @seojaeho/app-launcherPeer Dependencies
소비자 프로젝트에 아래 패키지가 이미 설치되어 있어야 한다.
react>= 18react-dom>= 18@tanstack/react-query>= 5
Quick Start
import {
AppLauncher,
AppLauncherTrigger,
AppLauncherContent,
} from "@seojaeho/app-launcher";
function MyNav() {
return (
<AppLauncher env="production" theme="dark">
<AppLauncherTrigger asChild>
<button>Menu</button>
</AppLauncherTrigger>
<AppLauncherContent />
</AppLauncher>
);
}별도의 CSS import 없이 스타일이 자동 적용된다.
Components
<AppLauncher>
Root 컴포넌트. Context Provider + Popover/Drawer Root 역할.
| Prop | Type | Default | Description |
| ------------------ | ---------------------------------- | ---------- | ---------------------------------------------- |
| env | 'dev' \| 'stage' \| 'production' | (필수) | 환경에 따라 메뉴 아이템의 URL이 결정된다 |
| theme | 'dark' \| 'light' | 'dark' | 팝오버/드로어의 다크/라이트 테마 |
| mobileBreakpoint | number | 768 | 이 px 이하에서 Drawer로 전환 (반응형 기준점) |
<AppLauncherTrigger>
Headless trigger. 소비자가 제공하는 엘리먼트를 trigger로 사용한다.
| Prop | Type | Default | Description |
| --------- | --------- | ------- | ------------------------------------------------------ |
| asChild | boolean | true | true이면 자식 엘리먼트를 trigger로 사용 (Radix 패턴) |
<AppLauncherContent>
Styled content. 패키지가 디자인을 통제한다. 2열 그리드 레이아웃.
| Prop | Type | Default | Description |
| ------------ | ------------------------------ | ------- | ------------------------------- |
| align | 'start' \| 'center' \| 'end' | 'end' | 팝오버 정렬 위치 (Desktop only) |
| sideOffset | number | 12 | trigger와의 간격 px (Desktop) |
| className | string | - | 추가 CSS 클래스 |
Responsive
- Desktop (
> mobileBreakpoint): Popover로 렌더링 - Mobile (
<= mobileBreakpoint): 하단 Drawer(Bottom Sheet)로 렌더링
소비자는 별도의 분기 처리 없이 패키지가 자동으로 전환한다.
<AppLauncher env="production" mobileBreakpoint={1024}>
{/* 1024px 이하에서 Drawer로 전환 */}
</AppLauncher>Usage Example
import {
AppLauncher,
AppLauncherTrigger,
AppLauncherContent,
} from "@seojaeho/app-launcher";
function NavBar() {
return (
<AppLauncher env="production" theme="dark">
<AppLauncherTrigger asChild>
<button>
<DotsGridIcon />
</button>
</AppLauncherTrigger>
<AppLauncherContent align="end" sideOffset={8} />
</AppLauncher>
);
}Data Source
메뉴 데이터는 S3에서 자동으로 fetch된다.
https://contents.crosstoken.io/global-menu/global-menu.json@tanstack/react-query의useQuery사용- 캐시 전략:
no-cache(항상 최신 데이터) - 메뉴 아이템은
order필드 기준으로 정렬
useGlobalMenu Hook
패키지에서 useGlobalMenu hook도 export한다. 메뉴 데이터를 직접 사용해야 할 때:
import { useGlobalMenu } from "@seojaeho/app-launcher";
function MyComponent() {
const { data, isLoading } = useGlobalMenu();
// data.items: GlobalMenuItem[]
}Styling
- Vanilla CSS + CSS Custom Properties 사용
- 클래스명은
al-접두사로 네임스페이스 충돌 방지 injectStyle로 CSS가 JS에 포함되어 별도 CSS import 불필요- 테마는
data-theme속성으로 전환
CSS Custom Properties Override
필요 시 CSS 변수를 override하여 커스텀 가능:
[data-theme="dark"] {
--al-bg: #222;
--al-bg-hover: #333;
}| Variable | Description |
| -------------------- | ---------------- |
| --al-bg | 배경색 |
| --al-bg-hover | hover 배경색 |
| --al-text | 기본 텍스트 색 |
| --al-text-secondary| 보조 텍스트 색 |
| --al-border | 테두리 색 |
| --al-shadow | 박스 그림자 |
| --al-badge-bg | 뱃지 배경색 |
| --al-badge-text | 뱃지 텍스트 색 |
| --al-badge-new-bg | NEW 뱃지 배경색 |
| --al-badge-new-text| NEW 뱃지 텍스트 색|
| --al-drawer-overlay| Drawer 오버레이 색|
Types
type Environment = "dev" | "stage" | "production";
interface GlobalMenuItem {
id: string;
label: string;
description: string;
url: { dev: string; stage: string; production: string };
iconUrl: string;
order: number;
type: string;
badge: string | null;
isNew: boolean;
}
interface GlobalMenu {
version: string;
items: GlobalMenuItem[];
}Architecture
AppLauncher (Provider + Popover.Root / Drawer.Root)
├── AppLauncherTrigger (headless - 소비자 제공)
└── AppLauncherContent (styled - 패키지 통제)
├── Desktop → Popover.Content (2열 그리드)
└── Mobile → Drawer.Content (2열 그리드)내부적으로 useGlobalMenu hook이 S3에서 메뉴 데이터를 fetch하고, useMediaQuery로 반응형을 감지하여 Popover/Drawer를 자동 전환한다.
