@uniai-fe/uds-primitives 0.3.60 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +209 -3
- package/dist/styles.css +20 -6
- package/package.json +1 -1
- package/src/components/alternate/index.tsx +7 -1
- package/src/components/alternate/markup/Label.tsx +10 -5
- package/src/components/alternate/markup/empty/Data.tsx +9 -6
- package/src/components/alternate/markup/index.tsx +8 -0
- package/src/components/alternate/markup/loading/Default.tsx +10 -6
- package/src/components/alternate/markup/loading/Icon.tsx +11 -4
- package/src/components/alternate/types/index.ts +75 -2
- package/src/components/badge/index.tsx +4 -1
- package/src/components/badge/markup/Badge.tsx +10 -8
- package/src/components/badge/types/index.ts +26 -2
- package/src/components/button/index.tsx +6 -1
- package/src/components/button/markup/Base.tsx +20 -18
- package/src/components/button/markup/Rounded.tsx +7 -4
- package/src/components/button/markup/Text.tsx +7 -4
- package/src/components/calendar/index.tsx +8 -0
- package/src/components/calendar/markup/index.tsx +7 -7
- package/src/components/carousel/index.tsx +8 -0
- package/src/components/carousel/markup/index.tsx +9 -0
- package/src/components/checkbox/index.tsx +7 -0
- package/src/components/chip/index.tsx +7 -1
- package/src/components/chip/markup/index.tsx +9 -0
- package/src/components/divider/index.tsx +4 -0
- package/src/components/divider/markup/Divider.tsx +11 -7
- package/src/components/divider/types/index.ts +1 -0
- package/src/components/divider/types/props.ts +27 -0
- package/src/components/drawer/index.tsx +7 -0
- package/src/components/drawer/markup/index.tsx +6 -0
- package/src/components/dropdown/index.tsx +7 -0
- package/src/components/dropdown/markup/Template.tsx +9 -2
- package/src/components/dropdown/markup/foundation/Container.tsx +30 -12
- package/src/components/dropdown/markup/index.tsx +9 -10
- package/src/components/dropdown/types/base.ts +13 -0
- package/src/components/dropdown/types/props.ts +19 -2
- package/src/components/form/index.tsx +7 -0
- package/src/components/form/markup/index.tsx +6 -2
- package/src/components/info-box/index.tsx +7 -0
- package/src/components/info-box/markup/InfoBox.tsx +1 -1
- package/src/components/info-box/markup/index.ts +6 -0
- package/src/components/info-box/types/props.ts +2 -2
- package/src/components/input/index.tsx +6 -1
- package/src/components/input/markup/foundation/Input.tsx +2 -2
- package/src/components/input/types/foundation.ts +1 -1
- package/src/components/navigation/index.tsx +7 -0
- package/src/components/navigation/markup/index.tsx +6 -0
- package/src/components/pagination/index.tsx +6 -1
- package/src/components/pagination/markup/index.tsx +7 -0
- package/src/components/pop-over/index.tsx +7 -0
- package/src/components/pop-over/markup/index.tsx +5 -4
- package/src/components/radio/index.tsx +5 -1
- package/src/components/scrollbar/hooks/index.ts +1 -1
- package/src/components/scrollbar/index.tsx +1 -1
- package/src/components/scrollbar/markup/index.tsx +1 -1
- package/src/components/scrollbar/types/index.ts +1 -1
- package/src/components/scrollbar/utils/index.ts +1 -1
- package/src/components/segmented-control/index.tsx +5 -1
- package/src/components/segmented-control/markup/index.ts +6 -0
- package/src/components/select/index.tsx +6 -1
- package/src/components/select/markup/Default.tsx +10 -13
- package/src/components/select/markup/foundation/Selected.tsx +31 -26
- package/src/components/select/markup/index.tsx +1 -1
- package/src/components/select/markup/multiple/Multiple.tsx +32 -15
- package/src/components/select/styles/select.scss +15 -6
- package/src/components/select/styles/variables.scss +4 -0
- package/src/components/select/types/multiple.ts +19 -0
- package/src/components/select/types/props.ts +19 -6
- package/src/components/select/utils/display.tsx +41 -0
- package/src/components/select/utils/index.ts +1 -4
- package/src/components/slot/index.tsx +7 -0
- package/src/components/slot/markup/index.tsx +6 -0
- package/src/components/spinner/hooks/index.ts +1 -1
- package/src/components/spinner/index.tsx +1 -1
- package/src/components/spinner/markup/index.tsx +1 -1
- package/src/components/spinner/types/index.ts +1 -1
- package/src/components/spinner/utils/index.ts +1 -1
- package/src/components/tab/index.tsx +5 -1
- package/src/components/tab/markup/index.tsx +8 -0
- package/src/components/table/index.tsx +3 -0
- package/src/components/tooltip/index.tsx +7 -0
- package/src/components/tooltip/markup/index.tsx +7 -6
|
@@ -11,27 +11,29 @@ import {
|
|
|
11
11
|
} from "../../form/utils/form-field";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
14
|
+
* Button Component; Default action button
|
|
15
15
|
* @component
|
|
16
16
|
* @param {ButtonProps} props
|
|
17
|
-
* @param {ElementType} [props.as="button"]
|
|
18
|
-
* @param {React.ReactNode} [props.children]
|
|
19
|
-
* @param {
|
|
20
|
-
* @param {
|
|
21
|
-
* @param {
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* } [props.
|
|
25
|
-
* @param {"
|
|
26
|
-
* @param {
|
|
27
|
-
* @param {
|
|
28
|
-
* @param {
|
|
29
|
-
* @param {boolean} [props.block=false] true면 width 100%.
|
|
30
|
-
* @param {boolean} [props.loading=false] true면 readonly 처리 + aria-busy.
|
|
17
|
+
* @param {ElementType} [props.as="button"] 렌더링할 요소.
|
|
18
|
+
* @param {React.ReactNode} [props.children] 문자열/숫자는 `.button-label`로 래핑하고, 그 외 ReactNode는 그대로 렌더링한다.
|
|
19
|
+
* @param {"solid-xlarge" | "solid-large" | "solid-medium" | "solid-small" | "outlined-xlarge" | "outlined-large" | "outlined-medium" | "outlined-small"} [props.scale] legacy fill/size fallback 식별자.
|
|
20
|
+
* @param {"solid" | "outlined"} props.fill 채움 스타일.
|
|
21
|
+
* @param {"xlarge" | "large" | "medium" | "small" | "table"} props.size 버튼 크기. `table`은 Table 셀 액션용 compact 규격이다.
|
|
22
|
+
* @param {"primary" | "secondary" | "tertiary"} props.priority semantic color priority.
|
|
23
|
+
* @param {"default" | "readonly" | "disabled"} [props.state="default"] 내부 상태. `disabled`/`loading`과 병합된다.
|
|
24
|
+
* @param {boolean} [props.block=false] true면 폭을 100%로 확장한다.
|
|
25
|
+
* @param {"full" | "fit" | "fill" | "auto" | number | string} [props.width] Form.Field width preset 또는 custom width.
|
|
26
|
+
* @param {boolean} [props.loading=false] true면 readonly 상태와 `aria-busy`를 강제한다.
|
|
27
|
+
* @param {React.ReactNode} [props.left] 라벨 왼쪽 콘텐츠.
|
|
28
|
+
* @param {React.ReactNode} [props.right] 라벨 오른쪽 콘텐츠.
|
|
31
29
|
* @param {string} [props.className] 추가 className.
|
|
32
|
-
* @param {"button" | "submit" | "reset"} [props.type="button"] button
|
|
33
|
-
* @param {boolean} [props.disabled] native disabled. state
|
|
34
|
-
* @param {"hover" | "pressed"} [props
|
|
30
|
+
* @param {"button" | "submit" | "reset"} [props.type="button"] `button` 태그일 때 native type.
|
|
31
|
+
* @param {boolean} [props.disabled] native disabled. `state`보다 우선한다.
|
|
32
|
+
* @param {"hover" | "pressed"} [props["data-user-action"]] Storybook 시각 상태 고정용 data attribute.
|
|
33
|
+
* @example
|
|
34
|
+
* <Button.Default fill="solid" size="medium" priority="primary">
|
|
35
|
+
* 확인
|
|
36
|
+
* </Button.Default>
|
|
35
37
|
*/
|
|
36
38
|
const ButtonDefault = forwardRef<HTMLElement, ButtonProps>(
|
|
37
39
|
(
|
|
@@ -4,12 +4,15 @@ import type { RoundButtonProps } from "../types";
|
|
|
4
4
|
import { ButtonDefault } from "./Base";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Button Component; Rounded action button
|
|
8
8
|
* @component
|
|
9
9
|
* @param {RoundButtonProps} props
|
|
10
|
-
* @param {"small" | "medium" | "large"} props.size
|
|
11
|
-
* @param {"primary" | "secondary" | "tertiary"} props.priority semantic color.
|
|
12
|
-
* @
|
|
10
|
+
* @param {"small" | "medium" | "large"} props.size 라운드 버튼 크기 축.
|
|
11
|
+
* @param {"primary" | "secondary" | "tertiary"} props.priority semantic color priority.
|
|
12
|
+
* @example
|
|
13
|
+
* <Button.Rounded size="small" priority="primary" aria-label="추가">
|
|
14
|
+
* <span aria-hidden="true">+</span>
|
|
15
|
+
* </Button.Rounded>
|
|
13
16
|
*/
|
|
14
17
|
const ButtonRounded = forwardRef<HTMLElement, RoundButtonProps>(
|
|
15
18
|
// 라운드 전용 템플릿도 fill/size/priority를 직접 주입해 공통 규칙을 따른다.
|
|
@@ -4,12 +4,15 @@ import type { TextButtonProps } from "../types";
|
|
|
4
4
|
import { ButtonDefault } from "./Base";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Button Component; Text action button
|
|
8
8
|
* @component
|
|
9
9
|
* @param {TextButtonProps} props
|
|
10
|
-
* @param {"small" | "medium" | "large"} props.size
|
|
11
|
-
* @param {"secondary" | "tertiary"} props.priority
|
|
12
|
-
* @
|
|
10
|
+
* @param {"small" | "medium" | "large"} props.size 텍스트 버튼 크기 축.
|
|
11
|
+
* @param {"secondary" | "tertiary"} props.priority 텍스트 버튼 priority 축.
|
|
12
|
+
* @example
|
|
13
|
+
* <Button.Text size="medium" priority="secondary">
|
|
14
|
+
* 자세히 보기
|
|
15
|
+
* </Button.Text>
|
|
13
16
|
*/
|
|
14
17
|
const ButtonText = forwardRef<HTMLElement, TextButtonProps>(
|
|
15
18
|
// 템플릿 레벨에서 fill/size/priority를 강제해 ButtonDefault 정책과 동기화한다.
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calendar; date picker 조합 카테고리 배럴
|
|
3
|
+
* @desc
|
|
4
|
+
* - `Calendar.Root`: trigger + popover + core 조합 템플릿이다.
|
|
5
|
+
* - `Calendar.Container`: Header/Body/Footer 레이아웃 컨테이너다.
|
|
6
|
+
* - `Calendar.Core`: Mantine DatePicker SOT 렌더러다.
|
|
7
|
+
* - `CalendarHooks`, `CalendarUtils`: 관련 hook/util namespace다.
|
|
8
|
+
*/
|
|
1
9
|
import "./index.scss";
|
|
2
10
|
|
|
3
11
|
export * from "./markup";
|
|
@@ -7,13 +7,13 @@ import CalendarRoot from "./Root";
|
|
|
7
7
|
import { CalendarIcon } from "./Icon";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Calendar namespace
|
|
11
|
-
*
|
|
12
|
-
* -
|
|
13
|
-
* - Header
|
|
14
|
-
* - Body
|
|
15
|
-
* -
|
|
16
|
-
* -
|
|
10
|
+
* Calendar; date picker namespace
|
|
11
|
+
* @desc
|
|
12
|
+
* - `Calendar.Root`: trigger + popover + core 조합 템플릿이다.
|
|
13
|
+
* - `Calendar.Container`: Header/Body/Footer 레이아웃 컨테이너다.
|
|
14
|
+
* - `Calendar.Header`, `Calendar.Body`, `Calendar.Footer`: depth 고정 레이아웃 섹션이다.
|
|
15
|
+
* - `Calendar.Core`: Mantine DatePicker SOT 렌더러다.
|
|
16
|
+
* - `Calendar.Icon`: 입력/네비게이션 아이콘 namespace다.
|
|
17
17
|
*/
|
|
18
18
|
export const Calendar = {
|
|
19
19
|
Root: CalendarRoot,
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Carousel; viewport scroll carousel 카테고리 배럴
|
|
3
|
+
* @desc
|
|
4
|
+
* - `Carousel.Provider`: viewport/track 상태 공유 루트다.
|
|
5
|
+
* - `Carousel.Track`: viewport + track 렌더러다.
|
|
6
|
+
* - `Carousel.Control`, `Carousel.Button.*`: 이동 제어 도구다.
|
|
7
|
+
* - `useCarousel`: provider context 접근 hook이다.
|
|
8
|
+
*/
|
|
1
9
|
import "./index.scss";
|
|
2
10
|
|
|
3
11
|
export * from "./markup";
|
|
@@ -6,6 +6,15 @@ import { CarouselPrevButton } from "./button/Prev";
|
|
|
6
6
|
import { CarouselProvider } from "./Provider";
|
|
7
7
|
import { CarouselTrack } from "./Track";
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Carousel; viewport scroll carousel namespace
|
|
11
|
+
* @desc
|
|
12
|
+
* - `Carousel.Provider`: viewport/track 상태 공유 루트다.
|
|
13
|
+
* - `Carousel.Container`: Provider + Control 프리셋이다.
|
|
14
|
+
* - `Carousel.Control`: Prev/Track/Next 조합 컨트롤이다.
|
|
15
|
+
* - `Carousel.Track`: viewport + track 렌더러다.
|
|
16
|
+
* - `Carousel.Button.Base|Prev|Next`: 이동 제어 leaf 버튼이다.
|
|
17
|
+
*/
|
|
9
18
|
export const Carousel = {
|
|
10
19
|
Provider: CarouselProvider,
|
|
11
20
|
Container: CarouselContainer,
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Chip; filter/assist/input chip 카테고리 배럴
|
|
3
|
+
* @desc
|
|
4
|
+
* - `Chip.Default`: style 분기 루트 컴포넌트다.
|
|
5
|
+
* - `Chip.ClickableStyle`: filter/assist button 렌더 전용 leaf다.
|
|
6
|
+
* - `Chip.InputStyle`: input figure 렌더 전용 leaf다.
|
|
7
|
+
* - `Chip.List`: chip 목록 템플릿이다.
|
|
8
|
+
* - `ChipProps`, `ChipListRootProps`, `ChipListItemData`: public contract 타입이다.
|
|
3
9
|
*/
|
|
4
10
|
import "./index.scss";
|
|
5
11
|
|
|
@@ -4,6 +4,15 @@ import ChipInputStyle from "./InputStyle";
|
|
|
4
4
|
import ChipLabel from "./Label";
|
|
5
5
|
import { ChipListRoot } from "./ListRoot";
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Chip; filter/assist/input namespace
|
|
9
|
+
* @desc
|
|
10
|
+
* - `Chip.Default`: style 분기 루트 컴포넌트다.
|
|
11
|
+
* - `Chip.ClickableStyle`: filter/assist button 렌더 전용 leaf다.
|
|
12
|
+
* - `Chip.InputStyle`: input figure 렌더 전용 leaf다.
|
|
13
|
+
* - `Chip.Label`: chip 텍스트 슬롯이다.
|
|
14
|
+
* - `Chip.List`: chip 목록 템플릿이다.
|
|
15
|
+
*/
|
|
7
16
|
export const Chip = {
|
|
8
17
|
Default: ChipDefault,
|
|
9
18
|
ClickableStyle: ChipClickableStyle,
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import clsx from "clsx";
|
|
2
|
+
import type { DividerProps } from "../types";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Divider; 수평/수직 구분선 마크업
|
|
6
|
+
* @component
|
|
7
|
+
* @param {DividerProps} props
|
|
8
|
+
* @param {string} [props.className] divider 루트 className
|
|
9
|
+
* @param {"horizontal" | "vertical"} [props.direction] 구분선 방향
|
|
10
|
+
* @example
|
|
11
|
+
* <Divider direction="horizontal" />
|
|
12
|
+
*/
|
|
13
|
+
export function Divider({ className, direction = "vertical" }: DividerProps) {
|
|
10
14
|
return <div className={clsx("divider", direction, className)}></div>;
|
|
11
15
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { DividerDirection, DividerProps } from "./props";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ComponentPropsWithoutRef } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* DividerDirection; divider 방향 축
|
|
5
|
+
* @typedef {"horizontal" | "vertical"} DividerDirection
|
|
6
|
+
*/
|
|
7
|
+
export type DividerDirection = "horizontal" | "vertical";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Divider Props; divider 루트 속성
|
|
11
|
+
* @property {string} [className] divider 루트 className
|
|
12
|
+
* @property {"horizontal" | "vertical"} [direction] 구분선 방향
|
|
13
|
+
* @see React.ComponentPropsWithoutRef<"div">
|
|
14
|
+
*/
|
|
15
|
+
export interface DividerProps extends Omit<
|
|
16
|
+
ComponentPropsWithoutRef<"div">,
|
|
17
|
+
"children"
|
|
18
|
+
> {
|
|
19
|
+
/**
|
|
20
|
+
* divider 루트 className
|
|
21
|
+
*/
|
|
22
|
+
className?: string;
|
|
23
|
+
/**
|
|
24
|
+
* 구분선 방향
|
|
25
|
+
*/
|
|
26
|
+
direction?: DividerDirection;
|
|
27
|
+
}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drawer; bottom sheet 카테고리 배럴
|
|
3
|
+
* @desc
|
|
4
|
+
* - `DrawerRoot`, `DrawerTrigger`, `DrawerPortal`, `DrawerOverlay`, `DrawerContent`: 루트/레이어 조합 축이다.
|
|
5
|
+
* - `DrawerHeader`, `DrawerBody`, `DrawerFooter`, `DrawerTitle`, `DrawerDescription`, `DrawerClose`: anatomy 도구다.
|
|
6
|
+
* - `useDrawerDrag`: handle drag 제어 hook이다.
|
|
7
|
+
*/
|
|
1
8
|
import "./index.scss";
|
|
2
9
|
|
|
3
10
|
export * from "./markup";
|
|
@@ -1,3 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Drawer; bottom sheet markup export 배럴
|
|
5
|
+
* @desc
|
|
6
|
+
* - `DrawerRoot`, `DrawerTrigger`, `DrawerPortal`, `DrawerOverlay`, `DrawerContent`: 루트/레이어 조합 축이다.
|
|
7
|
+
* - `DrawerHeader`, `DrawerBody`, `DrawerFooter`, `DrawerTitle`, `DrawerDescription`, `DrawerClose`: anatomy 도구다.
|
|
8
|
+
*/
|
|
3
9
|
export * from "./Drawer";
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dropdown; namespace 배럴 export
|
|
3
|
+
* @desc
|
|
4
|
+
* - `Dropdown.Root`, `Dropdown.Trigger`, `Dropdown.Container`: foundation 레이어
|
|
5
|
+
* - `Dropdown.Menu.*`: list/item 조합 레이어
|
|
6
|
+
* - `Dropdown.Template`: items[].selected + onChange(payload) preset
|
|
7
|
+
*/
|
|
1
8
|
import "./index.scss";
|
|
2
9
|
|
|
3
10
|
export * from "./markup";
|
|
@@ -27,8 +27,9 @@ import {
|
|
|
27
27
|
* @param {ReactNode} props.trigger trigger 요소
|
|
28
28
|
* @param {DropdownTemplateItem[]} props.items 렌더링할 menu item 리스트
|
|
29
29
|
* @param {(payload: DropdownTemplateChangePayload) => void} [props.onChange] 선택 결과 변경 콜백
|
|
30
|
-
* @param {"small" | "medium" | "large"} [props.size="medium"] menu size scale
|
|
30
|
+
* @param {"xsmall" | "small" | "medium" | "large"} [props.size="medium"] menu size scale
|
|
31
31
|
* @param {"match" | "fit-content" | "max-content" | string | number} [props.width="match"] panel width 옵션
|
|
32
|
+
* @param {"match" | "fit-content" | "max-content" | string | number} [props.minWidth] panel 최소 너비 옵션
|
|
32
33
|
* @param {DropdownMenuProps} [props.rootProps] Dropdown.Root 전달 props
|
|
33
34
|
* @param {DropdownContainerProps} [props.containerProps] Dropdown.Container 전달 props
|
|
34
35
|
* @param {DropdownMenuListProps} [props.menuListProps] Dropdown.Menu.List 전달 props
|
|
@@ -40,6 +41,7 @@ const DropdownTemplate = ({
|
|
|
40
41
|
onChange,
|
|
41
42
|
size = "medium",
|
|
42
43
|
width = "match",
|
|
44
|
+
minWidth,
|
|
43
45
|
rootProps,
|
|
44
46
|
containerProps,
|
|
45
47
|
menuListProps,
|
|
@@ -199,7 +201,12 @@ const DropdownTemplate = ({
|
|
|
199
201
|
return (
|
|
200
202
|
<DropdownRoot {...rootProps}>
|
|
201
203
|
<DropdownTrigger asChild>{trigger}</DropdownTrigger>
|
|
202
|
-
<DropdownContainer
|
|
204
|
+
<DropdownContainer
|
|
205
|
+
{...containerProps}
|
|
206
|
+
size={size}
|
|
207
|
+
width={width}
|
|
208
|
+
minWidth={minWidth}
|
|
209
|
+
>
|
|
203
210
|
<DropdownMenuList {...menuListProps}>
|
|
204
211
|
{items.length > 0 ? (
|
|
205
212
|
<>
|
|
@@ -15,6 +15,7 @@ import { useDropdownContext } from "./Provider";
|
|
|
15
15
|
* @param {string} [props.className] panel className
|
|
16
16
|
* @param {DropdownSize} [props.size="medium"] option height scale
|
|
17
17
|
* @param {DropdownPanelWidth} [props.width="match"] panel width 옵션
|
|
18
|
+
* @param {DropdownPanelMinWidth} [props.minWidth] panel 최소 너비 옵션
|
|
18
19
|
* @param {HTMLElement | null} [props.portalContainer] portal 컨테이너
|
|
19
20
|
* @param {"start" | "center" | "end"} [props.align="start"] 정렬 기준
|
|
20
21
|
* @param {"top" | "right" | "bottom" | "left"} [props.side="bottom"] 패널 위치
|
|
@@ -33,6 +34,7 @@ const DropdownContainer = forwardRef<HTMLDivElement, DropdownContainerProps>(
|
|
|
33
34
|
className,
|
|
34
35
|
size = "medium",
|
|
35
36
|
width = "match",
|
|
37
|
+
minWidth,
|
|
36
38
|
portalContainer,
|
|
37
39
|
align = "start",
|
|
38
40
|
side = "bottom",
|
|
@@ -47,29 +49,46 @@ const DropdownContainer = forwardRef<HTMLDivElement, DropdownContainerProps>(
|
|
|
47
49
|
const [panelWidth, setPanelWidth] = useState<number>();
|
|
48
50
|
const shouldMatchTriggerWidth = width === "match";
|
|
49
51
|
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
+
const resolvePanelSizeValue = (
|
|
53
|
+
value?: string | number,
|
|
54
|
+
fallbackToMatch?: boolean,
|
|
55
|
+
): string | undefined => {
|
|
56
|
+
if (value === undefined) {
|
|
52
57
|
return undefined;
|
|
53
58
|
}
|
|
54
59
|
|
|
55
|
-
if (typeof
|
|
56
|
-
return `${
|
|
60
|
+
if (typeof value === "number") {
|
|
61
|
+
return `${value}px`;
|
|
57
62
|
}
|
|
58
63
|
|
|
59
|
-
if (typeof
|
|
60
|
-
if (
|
|
64
|
+
if (typeof value === "string") {
|
|
65
|
+
if (value === "fit-content") {
|
|
61
66
|
return "fit-content";
|
|
62
67
|
}
|
|
63
|
-
if (
|
|
68
|
+
if (value === "max-content") {
|
|
64
69
|
return "max-content";
|
|
65
70
|
}
|
|
66
|
-
if (
|
|
67
|
-
return
|
|
71
|
+
if (fallbackToMatch && value === "match" && panelWidth) {
|
|
72
|
+
return `${panelWidth}px`;
|
|
73
|
+
}
|
|
74
|
+
if (value.trim().length > 0 && value !== "match") {
|
|
75
|
+
return value;
|
|
68
76
|
}
|
|
69
77
|
}
|
|
70
78
|
|
|
71
79
|
return undefined;
|
|
72
|
-
}
|
|
80
|
+
};
|
|
81
|
+
const resolvedWidth = useMemo(() => {
|
|
82
|
+
if (shouldMatchTriggerWidth) {
|
|
83
|
+
return panelWidth ? `${panelWidth}px` : undefined;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return resolvePanelSizeValue(width);
|
|
87
|
+
}, [panelWidth, shouldMatchTriggerWidth, width]);
|
|
88
|
+
const resolvedMinWidth = useMemo(() => {
|
|
89
|
+
// 변경 설명: width 계약은 기준 폭, minWidth 계약은 최소 보장 폭으로 분리한다.
|
|
90
|
+
return resolvePanelSizeValue(minWidth, true);
|
|
91
|
+
}, [minWidth, panelWidth]);
|
|
73
92
|
|
|
74
93
|
useEffect(() => {
|
|
75
94
|
if (!shouldMatchTriggerWidth) {
|
|
@@ -110,8 +129,7 @@ const DropdownContainer = forwardRef<HTMLDivElement, DropdownContainerProps>(
|
|
|
110
129
|
className={clsx("dropdown-panel", `dropdown-panel-${size}`, className)}
|
|
111
130
|
style={{
|
|
112
131
|
...style,
|
|
113
|
-
width:
|
|
114
|
-
shouldMatchTriggerWidth && panelWidth ? panelWidth : style?.width,
|
|
132
|
+
width: resolvedWidth ?? style?.width,
|
|
115
133
|
minWidth:
|
|
116
134
|
resolvedMinWidth !== undefined ? resolvedMinWidth : style?.minWidth,
|
|
117
135
|
}}
|
|
@@ -2,16 +2,15 @@ import { DropdownFoundation } from "./foundation";
|
|
|
2
2
|
import DropdownTemplate from "./Template";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Dropdown
|
|
6
|
-
*
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
9
|
-
* -
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* -
|
|
13
|
-
* -
|
|
14
|
-
* - Template
|
|
5
|
+
* Dropdown; 컴포넌트 모듈
|
|
6
|
+
* @namespace Dropdown
|
|
7
|
+
* - `Dropdown.Provider`
|
|
8
|
+
* - `Dropdown.Root`
|
|
9
|
+
* - `Dropdown.Trigger`
|
|
10
|
+
* - `Dropdown.Container`
|
|
11
|
+
* - `Dropdown.Menu.Item`
|
|
12
|
+
* - `Dropdown.Menu.List`
|
|
13
|
+
* - `Dropdown.Template`
|
|
15
14
|
*/
|
|
16
15
|
export const Dropdown = {
|
|
17
16
|
...DropdownFoundation,
|
|
@@ -16,3 +16,16 @@ export type DropdownPanelWidth =
|
|
|
16
16
|
| "max-content"
|
|
17
17
|
| string
|
|
18
18
|
| number;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Dropdown panel min-width 옵션
|
|
22
|
+
* - "match": trigger width를 최소 폭으로 보장
|
|
23
|
+
* - "fit-content" | "max-content": 콘텐츠 기준 최소 폭
|
|
24
|
+
* - string/number: 커스텀 최소 폭
|
|
25
|
+
*/
|
|
26
|
+
export type DropdownPanelMinWidth =
|
|
27
|
+
| "match"
|
|
28
|
+
| "fit-content"
|
|
29
|
+
| "max-content"
|
|
30
|
+
| string
|
|
31
|
+
| number;
|
|
@@ -6,7 +6,11 @@ import type {
|
|
|
6
6
|
import type { HTMLAttributes, ReactNode, RefObject } from "react";
|
|
7
7
|
|
|
8
8
|
import type { CheckboxProps } from "../../checkbox/types";
|
|
9
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
DropdownPanelMinWidth,
|
|
11
|
+
DropdownPanelWidth,
|
|
12
|
+
DropdownSize,
|
|
13
|
+
} from "./base";
|
|
10
14
|
|
|
11
15
|
/**
|
|
12
16
|
* Dropdown template value 타입
|
|
@@ -17,6 +21,7 @@ export type DropdownTemplateValue = string | number;
|
|
|
17
21
|
* Dropdown Container props
|
|
18
22
|
* @property {DropdownSize} [size="medium"] option 높이 스케일
|
|
19
23
|
* @property {DropdownPanelWidth} [width="match"] dropdown panel width 옵션
|
|
24
|
+
* @property {DropdownPanelMinWidth} [minWidth] dropdown panel 최소 너비 옵션
|
|
20
25
|
* @property {HTMLElement | null} [portalContainer] portal을 렌더링할 DOM 컨테이너
|
|
21
26
|
*/
|
|
22
27
|
export interface DropdownContainerProps extends DropdownMenuContentProps {
|
|
@@ -28,6 +33,10 @@ export interface DropdownContainerProps extends DropdownMenuContentProps {
|
|
|
28
33
|
* trigger 너비에 맞춰 dropdown 너비를 맞출지 여부
|
|
29
34
|
*/
|
|
30
35
|
width?: DropdownPanelWidth;
|
|
36
|
+
/**
|
|
37
|
+
* dropdown 최소 너비 제약
|
|
38
|
+
*/
|
|
39
|
+
minWidth?: DropdownPanelMinWidth;
|
|
31
40
|
/**
|
|
32
41
|
* portal을 렌더링할 DOM 컨테이너
|
|
33
42
|
*/
|
|
@@ -191,6 +200,7 @@ export interface DropdownTemplateChangePayload {
|
|
|
191
200
|
* @property {(payload: DropdownTemplateChangePayload) => void} [onChange] 선택 결과 변경 콜백
|
|
192
201
|
* @property {DropdownSize} [size="medium"] surface height scale
|
|
193
202
|
* @property {DropdownPanelWidth} [width="match"] panel width 옵션
|
|
203
|
+
* @property {DropdownPanelMinWidth} [minWidth] panel 최소 너비 옵션
|
|
194
204
|
* @property {DropdownMenuProps} [rootProps] Root 에 전달할 props
|
|
195
205
|
* @property {DropdownContainerProps} [containerProps] Container 에 전달할 props
|
|
196
206
|
* @property {DropdownMenuListProps} [menuListProps] MenuList 에 전달할 props
|
|
@@ -205,6 +215,10 @@ export interface DropdownTemplateProps {
|
|
|
205
215
|
onChange?: (payload: DropdownTemplateChangePayload) => void;
|
|
206
216
|
size?: DropdownSize;
|
|
207
217
|
width?: DropdownPanelWidth;
|
|
218
|
+
/**
|
|
219
|
+
* panel 최소 너비 옵션
|
|
220
|
+
*/
|
|
221
|
+
minWidth?: DropdownPanelMinWidth;
|
|
208
222
|
/**
|
|
209
223
|
* Root 에 전달할 props
|
|
210
224
|
* - 타입 출처를 명확히 하기 위해 Radix 원본 타입을 직접 사용한다.
|
|
@@ -213,7 +227,10 @@ export interface DropdownTemplateProps {
|
|
|
213
227
|
/**
|
|
214
228
|
* Container 에 전달할 props
|
|
215
229
|
*/
|
|
216
|
-
containerProps?: Omit<
|
|
230
|
+
containerProps?: Omit<
|
|
231
|
+
DropdownContainerProps,
|
|
232
|
+
"children" | "size" | "width" | "minWidth"
|
|
233
|
+
>;
|
|
217
234
|
/**
|
|
218
235
|
* MenuList 에 전달할 props
|
|
219
236
|
*/
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Form; provider + field layout 카테고리 배럴
|
|
3
|
+
* @desc
|
|
4
|
+
* - `Form.Provider`: react-hook-form provider 래퍼다.
|
|
5
|
+
* - `Form.Field.*`: Container/Header/Body/Footer/Template 레이아웃 도구다.
|
|
6
|
+
* - `getFormFieldWidthAttr`, `getFormFieldWidthValue`: width 계산 util이다.
|
|
7
|
+
*/
|
|
1
8
|
import "./index.scss";
|
|
2
9
|
|
|
3
10
|
export { Form } from "./markup";
|
|
@@ -2,8 +2,12 @@ import FormProvider from "./Provider";
|
|
|
2
2
|
import { FormField } from "./form-field";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Form
|
|
6
|
-
*
|
|
5
|
+
* Form; provider + field layout namespace
|
|
6
|
+
* @desc
|
|
7
|
+
* - `Form.Provider`: react-hook-form provider 래퍼다.
|
|
8
|
+
* - `Form.Field.Container`: field 루트 wrapper다.
|
|
9
|
+
* - `Form.Field.Header`, `Form.Field.Body`, `Form.Field.Footer`: depth 고정 섹션이다.
|
|
10
|
+
* - `Form.Field.Template`: 반복 조합용 필드 템플릿이다.
|
|
7
11
|
*/
|
|
8
12
|
export const Form = {
|
|
9
13
|
Provider: FormProvider,
|
|
@@ -7,7 +7,7 @@ import { InfoBoxIcon } from "./Icon";
|
|
|
7
7
|
* InfoBox Component; 안내 메시지 전용 정보 박스
|
|
8
8
|
* @component
|
|
9
9
|
* @param {InfoBoxProps} props
|
|
10
|
-
* @param {"info"} [props.state="info"]
|
|
10
|
+
* @param {"info" | "caution" | "success" | "error"} [props.state="info"] 안내/상태 메시지 시각 축이다.
|
|
11
11
|
* @param {React.ReactNode} [props.heading] 상단 제목 영역.
|
|
12
12
|
* @param {React.ReactNode} [props.children] 본문 콘텐츠.
|
|
13
13
|
* @param {string} [props.className] 루트 section className.
|
|
@@ -7,7 +7,7 @@ type NativeSectionProps = Omit<ComponentPropsWithoutRef<"section">, "title">;
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* InfoBox Props; info-box 루트 속성
|
|
10
|
-
* @property {"info" | "caution" | "success" | "error"} [state]
|
|
10
|
+
* @property {"info" | "caution" | "success" | "error"} [state] info/caution/success/error 상태 축이다.
|
|
11
11
|
* @property {ReactNode} [heading] 상단 제목 콘텐츠
|
|
12
12
|
* @property {ReactNode} [children] 본문 콘텐츠
|
|
13
13
|
* @property {string} [className] 루트 section className
|
|
@@ -15,7 +15,7 @@ type NativeSectionProps = Omit<ComponentPropsWithoutRef<"section">, "title">;
|
|
|
15
15
|
*/
|
|
16
16
|
export interface InfoBoxProps extends Omit<NativeSectionProps, "children"> {
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
18
|
+
* info/caution/success/error 상태 축이다.
|
|
19
19
|
* - info, caution, success, error
|
|
20
20
|
*/
|
|
21
21
|
state?: InfoBoxState;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Input; namespace 배럴 export
|
|
3
|
+
* @desc
|
|
4
|
+
* - `Input.Base`, `Input.TextArea`: foundation control
|
|
5
|
+
* - `Input.Text.*`: text scenario wrappers
|
|
6
|
+
* - `Input.Date`, `Input.Address`, `Input.File`: category presets
|
|
7
|
+
* - hooks/utils/types는 별도 배럴로 함께 export한다
|
|
3
8
|
*/
|
|
4
9
|
import "./index.scss";
|
|
5
10
|
|
|
@@ -34,7 +34,7 @@ import InputBaseUtil from "./Utility";
|
|
|
34
34
|
* @param {"default" | "active" | "focused" | "success" | "error" | "disabled" | "loading"} [props.state="default"] 시각 상태(tertiary는 disabled 외 feedback color 미적용)
|
|
35
35
|
* @param {boolean} [props.block=false] true면 width 100% (`priority="table"`은 width 미지정 시 full 기본)
|
|
36
36
|
* @param {React.ReactNode} [props.inlineLabel] tertiary border 내부 상단 라벨
|
|
37
|
-
* @param {
|
|
37
|
+
* @param {"full" | "fit" | "fill" | "auto" | number | string} [props.width] width preset 또는 custom width
|
|
38
38
|
* @param {React.ReactNode} [props.left] 입력 왼쪽 슬롯(아이콘/텍스트)
|
|
39
39
|
* @param {React.ReactNode} [props.right] 입력 오른쪽 슬롯
|
|
40
40
|
* @param {React.ReactNode} [props.clear] 입력값 초기화 아이콘. 지정하지 않으면 기본 Reset 아이콘
|
|
@@ -54,7 +54,7 @@ import InputBaseUtil from "./Utility";
|
|
|
54
54
|
* @param {string | number | readonly string[]} [props.value] 제어형 값
|
|
55
55
|
* @param {string | number | readonly string[]} [props.defaultValue] 비제어 초기값
|
|
56
56
|
* @param {string} [props.type="text"] native input type
|
|
57
|
-
* @param {boolean} [props.readOnly] native readOnly
|
|
57
|
+
* @param {boolean} [props.readOnly] native readOnly. value가 없으면 placeholder는 alternative 토큰으로 그대로 노출된다.
|
|
58
58
|
*/
|
|
59
59
|
const InputBase = forwardRef<HTMLInputElement, InputProps>(
|
|
60
60
|
(
|