Skip to content
Open
Show file tree
Hide file tree
Changes from 79 commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
0896d48
install rolldown & remove rollup-related dependencies
noahchoii Jul 9, 2026
00de410
migrate from rollup to rolldown
noahchoii Jul 9, 2026
d918c3e
remove get-tsconfig
noahchoii Jul 9, 2026
8b247e4
format
noahchoii Jul 9, 2026
5b361ae
remove build:rollup
noahchoii Jul 9, 2026
f60ff1d
split tsconfig according to scope
noahchoii Jul 16, 2026
0047235
edit ts-api-extractor integrations
noahchoii Jul 16, 2026
8e7efeb
Merge branch 'main' into rolldown
noahchoii Jul 16, 2026
a95fb75
Merge branch 'main' into rolldown
noahchoii Jul 27, 2026
7729185
cleanup tsconfig
noahchoii Jul 29, 2026
c674ee3
unify tsconfig
noahchoii Jul 29, 2026
94af759
disallow js in tsconfig, unify include with "."
noahchoii Jul 30, 2026
dc119dd
add .storybook
noahchoii Jul 30, 2026
d5f29ef
add hooks tsconfig exclude
noahchoii Jul 30, 2026
c53fe07
edit core tsconfig path from ts-api-extractor
noahchoii Jul 30, 2026
ee1b78a
edit ts-api-extractor constants
noahchoii Jul 30, 2026
9c85d19
add test case for default paths
noahchoii Jul 30, 2026
525240f
remove path test
noahchoii Jul 30, 2026
1258eb9
edit execution of ts-api-extractor test command
noahchoii Jul 30, 2026
fec56fc
add `@vapor-ui/composites` package
noahchoii Aug 5, 2026
48d53d5
split rolldown-config
noahchoii Aug 7, 2026
7a7e618
adjust rolldown configs to core/composites
noahchoii Aug 7, 2026
a716b09
add new `Dialog` composites
noahchoii Aug 13, 2026
94844fd
resolve storybook alias
noahchoii Aug 13, 2026
a309585
edit dialog style
noahchoii Aug 14, 2026
9a8e0e5
propagate slots props
noahchoii Aug 18, 2026
f29bcf7
add size prop & export with ts namespace
noahchoii Aug 18, 2026
193e20f
adjust rsc
noahchoii Aug 18, 2026
8bbc9b1
Merge branch 'main' into new-composites-package
noahchoii Aug 19, 2026
bc16436
fix: build dts for esm, cjs
noahchoii Aug 19, 2026
36d8651
add convention docs
noahchoii Aug 19, 2026
22edd97
adjust conventions.md for claude.md
noahchoii Aug 19, 2026
3c8edf7
add changeset
noahchoii Aug 19, 2026
83e4a76
adjust rolldown-config for core package
noahchoii Aug 19, 2026
91c48c3
format
noahchoii Aug 19, 2026
8776117
edit composites regressions port number
noahchoii Aug 19, 2026
4bef67b
update snapshots
noahchoii Aug 19, 2026
04e9e77
correct rolldown output type
noahchoii Aug 19, 2026
1989a30
notice resolve alias to bundler
noahchoii Aug 19, 2026
af65f87
replace visual into variants
noahchoii Aug 19, 2026
55fb0a4
add eslint & exec lint
noahchoii Aug 19, 2026
a89a31b
escape dot when find vapor.xxx layer
noahchoii Aug 19, 2026
e0ad52a
lint & format
noahchoii Aug 19, 2026
5cf01fc
remove footer & export assistive, action prop
noahchoii Aug 20, 2026
73079bf
infer slots prop types
noahchoii Aug 20, 2026
202d52b
ajdust design feedback
noahchoii Aug 20, 2026
2589af6
replace padding into margin
noahchoii Aug 21, 2026
0d1dbbe
early return when not exist render
noahchoii Aug 21, 2026
7172c1b
Merge branch 'main' into new-composites-package
noahchoii Aug 21, 2026
da37aa6
code-review
noahchoii Aug 24, 2026
0be04f5
code-review
noahchoii Aug 24, 2026
6843654
refactor dialog interface
noahchoii Aug 24, 2026
42b39e1
replace CONVENTIONS.md into rules
noahchoii Aug 24, 2026
3d04478
refactor action & assistive interface
noahchoii Aug 25, 2026
6f0d3f4
Merge branch 'main' into new-composites-package
noahchoii Aug 25, 2026
0840709
add manual close example to dialog stories
noahchoii Aug 25, 2026
65c2c28
apply Dialog Root naming to JSDoc
noahchoii Aug 25, 2026
0adef44
remove render prop example on actions
noahchoii Aug 25, 2026
23a7dcf
enhance create-slots early return conditions
noahchoii Aug 25, 2026
c9b7803
omit variants props from Action, Assistive
noahchoii Aug 25, 2026
008b144
Merge branch 'main' into new-composites-package
noahchoii Aug 25, 2026
74a8557
remove not found plugin
noahchoii Aug 25, 2026
bec0c26
add alert-dialog
noahchoii Aug 26, 2026
68aed96
edit alert-dialog description style
noahchoii Aug 26, 2026
f93b17d
update snapshots
noahchoii Aug 26, 2026
cb40e98
edit alert-dialog widths
noahchoii Aug 26, 2026
a0f966e
add changeset
noahchoii Aug 26, 2026
e5a82c7
add composites alert-dialog
noahchoii Aug 26, 2026
ba70f9f
add changeset
noahchoii Aug 26, 2026
8f31bfa
edit dialog type values
noahchoii Aug 27, 2026
80594d1
change some props to required
noahchoii Aug 28, 2026
231da20
change info type prop name to confirm
noahchoii Sep 1, 2026
1a2b044
apply alert-dialog style change
noahchoii Sep 1, 2026
c77214a
Merge branch 'main' into feature/vapor-258
noahchoii Sep 3, 2026
aa73303
update snapshots
noahchoii Sep 3, 2026
6cccd96
add alert-dialog header
noahchoii Sep 3, 2026
601cbce
apply core alert-dialog changes to composites
noahchoii Sep 3, 2026
4103db2
export alert-dialog
noahchoii Sep 3, 2026
6689099
update snapshots
noahchoii Sep 3, 2026
08301f0
add sha in regressions reports comment
noahchoii Sep 3, 2026
9c99818
allow number 0 for children
noahchoii Sep 3, 2026
58b75e3
edit alert-dialog header comments
noahchoii Sep 3, 2026
dd11e22
add alert-dialog code-connect
noahchoii Sep 3, 2026
88722c4
edit SHA_FULL to github.sha
noahchoii Sep 4, 2026
54265e4
Merge branch 'main' into feature/vapor-258
noahchoii Sep 4, 2026
9fcd46a
update changeset
noahchoii Sep 4, 2026
840c429
edit semantic naming
noahchoii Sep 4, 2026
61f47db
format
noahchoii Sep 4, 2026
0ee313b
Merge branch 'main' into feature/vapor-258
noahchoii Sep 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/open-poems-return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

core component alert dialog header component part가 추가된 것 같습니다.! 이 부분도 추가하면 어떨까요? 그리고 composite도 alert component가 추가되었으니 minor로 가면 어떨까요?

파일을 그럼 분리해서 두 개 추가해야 할 듯 합니다.!

'@vapor-ui/composites': patch
---

add composite `AlertDialog` component
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import { Button } from '@vapor-ui/core';

import { AlertDialog } from '.';

export default {
title: 'Composites/AlertDialog',
component: AlertDialog.Root,
argTypes: {
type: { control: 'inline-radio', options: ['critical', 'confirm'] },
title: { control: 'text' },
description: { control: 'text' },
},
} satisfies Meta<typeof AlertDialog.Root>;

type Story = StoryObj<typeof AlertDialog.Root>;

export const Default: Story = {
args: {
type: 'critical',
title: '이 항목을 삭제할까요?',
description: '삭제하면 되돌릴 수 없습니다. 연결된 기록도 함께 사라집니다.',
},
render: ({
action = <AlertDialog.Action>Remove</AlertDialog.Action>,
cancel = <AlertDialog.Cancel>Cancel</AlertDialog.Cancel>,
...args
}) => {
return (
<AlertDialog.Root
trigger={<Button>트리거</Button>}
cancel={cancel}
action={action}
{...args}
/>
);
},
};
307 changes: 307 additions & 0 deletions packages/composites/src/components/alert-dialog/alert-dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
import { createContext, useCallback, useContext, useMemo, useRef } from 'react';
import type { ReactNode } from 'react';

import { AlertDialog as AlertDialogPrimitives, Button, VStack } from '@vapor-ui/core';

import type { SlotProps } from '~/utils/create-slots';
import { createSlots } from '~/utils/create-slots';

export interface AlertDialogContext {
close: () => void;
type: AlertDialogRoot.Props['type'];
}

export const AlertDialogContext = createContext<AlertDialogContext | undefined>(undefined);

export function useAlertDialogContext() {
const context = useContext<AlertDialogContext | undefined>(AlertDialogContext);
if (context === undefined) {
throw new Error(
'AlertDialogContext is missing. AlertDialog parts must be placed within <AlertDialog.Root>.',
);
}
return context;
}

/* -----------------------------------------------------------------------------------------------*/

const slots = createSlots({
title: AlertDialogPrimitives.Title,
trigger: AlertDialogPrimitives.Trigger,
description: AlertDialogPrimitives.Description,
cancel: Button,
action: Button,
});

export const AlertDialogRoot = ({
// functions
open,
onOpenChange,
defaultOpen,
actionsRef: actionsRefProp,
container,
keepMounted,

// variants
type,

// slots
trigger,

title,
description,
cancel,
action,
children,
}: AlertDialogRoot.Props) => {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
const actionsRef = useRef<AlertDialogPrimitives.Root.Actions>(null);
const mergedRef = actionsRefProp ?? actionsRef;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기 변수명을 수정해 보면 어떨까요??

base-ui 내부에서 mergeProps, mergedRef와 같이 두 개 이상의 값을 합쳐주는 merge 로직이 있습니다. 이에 학습이 mergeXXX는 두 값을 합쳐서 하나의 props에 전달하는 컨벤션으로 익숙해져 있습니다. 다만 여기는 외부에서 들어온 값이 없으면 내부 값을 할당한다는 의미가 강해서 동일한 이름에 서로 다른 동작을 하고 있는 것 같습니다.!

이 부분 resolvedActionsRef와 같은 네이밍은 어떠신지요??

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋습니다~! 다른 속성들과 컨벤션을 맞추면 좋을 것 같아서 actionsRef라는 이름을 소비할 수 있도록, 기본 ref를 fallbackActionsRef로 만들어뒀습니다!

const fallbackActionsRef = useRef<AlertDialogPrimitives.Root.Actions>(null);
const actionsRef = actionsRefProp ?? fallbackActionsRef;

const close = useCallback(() => mergedRef.current?.close(), [mergedRef]);

const context = useMemo<AlertDialogContext>(() => ({ close, type }), [close, type]);

return (
<AlertDialogContext.Provider value={context}>
<AlertDialogPrimitives.Root
open={open}
onOpenChange={onOpenChange}
defaultOpen={defaultOpen}
actionsRef={mergedRef}
>
<slots.trigger render={trigger} />

<AlertDialogPrimitives.PortalPrimitive
container={container}
keepMounted={keepMounted}
>
<AlertDialogPrimitives.OverlayPrimitive />
<AlertDialogPrimitives.PopupPrimitive>
<Header title={title} description={description} />
<Body>{children}</Body>
<Footer action={action} cancel={cancel} />
</AlertDialogPrimitives.PopupPrimitive>
</AlertDialogPrimitives.PortalPrimitive>
</AlertDialogPrimitives.Root>
</AlertDialogContext.Provider>
);
};

type Slots = SlotProps<typeof slots, 'title'>;
type RootProps = AlertDialogPrimitives.Root.Props;
type PortalProps = AlertDialogPrimitives.PortalPrimitive.Props;

export interface AlertDialogProps {
/**
* 다이얼로그 열림 상태(제어). 사용자가 결정을 내려야 하는 시점을 외부 상태로 동기화할 때 사용한다.
* 상태의 변경을 추적할 필요가 없다면 defaultOpen을 사용한다.
*/
open?: RootProps['open'];

/**
* 마운트 시 초기 열림 여부(비제어).
* @default false
*/
defaultOpen?: RootProps['defaultOpen'];

/**
* 열림 상태 변경 콜백. 트리거·오버레이·ESC 등 모든 닫힘 경로에서 호출된다.
* @example
* <AlertDialog.Root onOpenChange={(open) => setOpen(open)} />
*/
onOpenChange?: RootProps['onOpenChange'];

/**
* 다이얼로그를 조작하기 위한 ref를 지정한다.
* @example
* const actionsRef = useRef<AlertDialog.Actions>(null);
* const handleSave = async (event: MouseEvent<HTMLButtonElement>) => {
* event.preventDefault();
*
* await save();
* actionsRef.current?.close();
* };
* <AlertDialog.Root actionsRef={actionsRef} action={<AlertDialog.Action onClick={handleSave} />} />
*/
actionsRef?: RootProps['actionsRef'];

/**
* Portal 대상 컨테이너. SSR·shadow DOM·특정 스택 컨텍스트에서 오버레이 위치 제어가 필요할 때만 지정한다.
* @default document.body
*/
container?: PortalProps['container'];

/**
* 닫힘 시에도 DOM에 유지할지 여부. 애니메이션/폼 상태 보존이 필요한 경우 true.
* @default false
*/
keepMounted?: PortalProps['keepMounted'];

/**
* 다이얼로그의 타입을 결정한다.
* @default "critical"
*/
type: 'critical' | 'confirm';

/**
* 다이얼로그의 목적을 한 문장으로 전달한다.
*/
title: Slots['title'];

/**
* 결정에 필요한 부가 설명.
*/
description: Slots['description'];

/**
* 다이얼로그를 여는 진입 요소.
* @example
* <AlertDialog.Root trigger={<Button>열기</Button>} />
*/
trigger?: Slots['trigger'];

/**
* 다이얼로그의 보조 액션 요소.
* @example
* <AlertDialog.Root cancel={<AlertDialog.Cancel>취소</Dialog.Cancel>} />
*/
cancel: Slots['cancel'];

/**
* 다이얼로그의 주요 액션 요소.
* @example
* <AlertDialog.Root action={<AlertDialog.Action>취소</Dialog.Action>} />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사용자가 jsdocs를 볼 때 action 버튼의 의도를 혼동해서 쓸 수 있을 듯 합니다. 취소 대신 stories 예시처럼 삭제로 추가하는 것이 어떨까요?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분도 반영해두었습니다~!

*/
action: Slots['action'];

/**
* 다이얼로그의 본문에 해당한다.
*/
children?: ReactNode;
}

export namespace AlertDialogRoot {
export type ChangeEventDetails = AlertDialogPrimitives.Root.ChangeEventDetails;
export type Actions = AlertDialogPrimitives.Root.Actions;
export type Props = AlertDialogProps;
}

/* -----------------------------------------------------------------------------------------------*/

interface HeaderProps extends Pick<AlertDialogRoot.Props, 'title' | 'description'> {}

const Header = ({ title, description }: HeaderProps) => {
return (
<AlertDialogPrimitives.Header render={<VStack />} $css={{ gap: '$200' }}>
<VStack $css={{ gap: '$075' }}>
<slots.title render={title} />
<slots.description render={description} />
</VStack>
</AlertDialogPrimitives.Header>
);
};

/* -----------------------------------------------------------------------------------------------*/

interface BodyProps extends Pick<AlertDialogRoot.Props, 'children'> {}

const Body = ({ children }: BodyProps) => {
if (!children) return null;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

return <AlertDialogPrimitives.Body>{children}</AlertDialogPrimitives.Body>;
};

/* -----------------------------------------------------------------------------------------------*/

interface FooterProps extends Pick<AlertDialogRoot.Props, 'action' | 'cancel'> {}

const Footer = ({ action, cancel }: FooterProps) => {
return (
<AlertDialogPrimitives.Footer
$css={{
display: 'grid',
gap: '$100',
gridTemplateAreas: '"cancel action"',
}}
>
<slots.cancel render={cancel} $css={{ gridArea: 'cancel' }} />
<slots.action render={action} $css={{ gridArea: 'action' }} />
</AlertDialogPrimitives.Footer>
);
};

/* -----------------------------------------------------------------------------------------------*/

export const AlertDialogAction = ({
closeOnClick = true,
onClick,
children,
...props
}: AlertDialogAction.Props) => {
const { close, type } = useAlertDialogContext();
const handleClick = (event: Parameters<NonNullable<Button.Props['onClick']>>[0]) => {
onClick?.(event);

if (event.defaultPrevented) return;
if (!closeOnClick) return;
close();
};

return (
<Button
size="lg"
colorPalette={type === 'critical' ? 'danger' : 'primary'}
onClick={handleClick}
{...props}
>
{children}
</Button>
);
};

interface AlertDialogActionProps extends Omit<Button.Props, 'size' | 'colorPalette' | 'variant'> {
/**
* 클릭 시 다이얼로그를 자동으로 닫을지 여부.
* @default true
*/
closeOnClick?: boolean;
}

export namespace AlertDialogAction {
export type Props = AlertDialogActionProps;
}

/* -----------------------------------------------------------------------------------------------*/

export const AlertDialogCancel = ({
closeOnClick = true,
onClick,
children,
...props
}: AlertDialogCancel.Props) => {
const { close } = useAlertDialogContext();
const handleClick = (event: Parameters<NonNullable<Button.Props['onClick']>>[0]) => {
onClick?.(event);

if (event.defaultPrevented) return;
if (!closeOnClick) return;
close();
};

return (
<Button size="lg" colorPalette="secondary" onClick={handleClick} {...props}>
{children}
</Button>
);
};

interface AlertDialogCancelProps extends Omit<Button.Props, 'size' | 'colorPalette' | 'variant'> {
/**
* 클릭 시 다이얼로그를 자동으로 닫을지 여부.
* @default true
*/
closeOnClick?: boolean;
}

export namespace AlertDialogCancel {
export type Props = AlertDialogCancelProps;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export {
AlertDialogRoot as Root,
AlertDialogAction as Action,
AlertDialogCancel as Cancel,
} from './alert-dialog';
1 change: 1 addition & 0 deletions packages/composites/src/components/alert-dialog/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * as AlertDialog from './index.parts';
Comment thread
coderabbitai[bot] marked this conversation as resolved.
1 change: 1 addition & 0 deletions packages/composites/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './components/alert-dialog';
export * from './components/dialog';
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 10 additions & 3 deletions packages/core/src/components/alert-dialog/alert-dialog.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,21 @@ export const description = componentStyle([
foregrounds({ color: 'hint' }),
]);

export const body = componentStyle({
paddingTop: vars.size.space[400],
paddingBottom: 0,
export const header = componentStyle({
paddingTop: vars.size.space['300'],
paddingBottom: vars.size.space['000'],
paddingInline: vars.size.space['300'],
width: '100%',
overflowY: 'auto',
});

export const body = componentStyle({
padding: vars.size.space['300'],
paddingBottom: vars.size.space['000'],
width: '100%',
overflowY: 'auto',
});

export const footer = componentStyle({
display: 'flex',
alignItems: 'center',
Expand Down
Loading
Loading