@uniai-fe/uds-primitives 0.3.59 → 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.
Files changed (83) hide show
  1. package/README.md +209 -3
  2. package/dist/styles.css +91 -76
  3. package/package.json +1 -1
  4. package/src/components/alternate/index.tsx +7 -1
  5. package/src/components/alternate/markup/Label.tsx +10 -5
  6. package/src/components/alternate/markup/empty/Data.tsx +9 -6
  7. package/src/components/alternate/markup/index.tsx +8 -0
  8. package/src/components/alternate/markup/loading/Default.tsx +10 -6
  9. package/src/components/alternate/markup/loading/Icon.tsx +11 -4
  10. package/src/components/alternate/types/index.ts +75 -2
  11. package/src/components/badge/index.tsx +4 -1
  12. package/src/components/badge/markup/Badge.tsx +10 -8
  13. package/src/components/badge/types/index.ts +26 -2
  14. package/src/components/button/index.tsx +6 -1
  15. package/src/components/button/markup/Base.tsx +20 -18
  16. package/src/components/button/markup/Rounded.tsx +7 -4
  17. package/src/components/button/markup/Text.tsx +7 -4
  18. package/src/components/calendar/index.tsx +8 -0
  19. package/src/components/calendar/markup/index.tsx +7 -7
  20. package/src/components/carousel/index.tsx +8 -0
  21. package/src/components/carousel/markup/index.tsx +9 -0
  22. package/src/components/checkbox/index.tsx +7 -0
  23. package/src/components/chip/index.tsx +7 -1
  24. package/src/components/chip/markup/index.tsx +9 -0
  25. package/src/components/divider/index.tsx +4 -0
  26. package/src/components/divider/markup/Divider.tsx +11 -7
  27. package/src/components/divider/types/index.ts +1 -0
  28. package/src/components/divider/types/props.ts +27 -0
  29. package/src/components/drawer/index.tsx +7 -0
  30. package/src/components/drawer/markup/index.tsx +6 -0
  31. package/src/components/dropdown/index.tsx +7 -0
  32. package/src/components/dropdown/markup/Template.tsx +9 -2
  33. package/src/components/dropdown/markup/foundation/Container.tsx +30 -12
  34. package/src/components/dropdown/markup/index.tsx +9 -10
  35. package/src/components/dropdown/types/base.ts +13 -0
  36. package/src/components/dropdown/types/props.ts +19 -2
  37. package/src/components/form/index.tsx +7 -0
  38. package/src/components/form/markup/index.tsx +6 -2
  39. package/src/components/info-box/index.tsx +7 -0
  40. package/src/components/info-box/markup/InfoBox.tsx +1 -1
  41. package/src/components/info-box/markup/index.ts +6 -0
  42. package/src/components/info-box/types/props.ts +2 -2
  43. package/src/components/input/index.tsx +6 -1
  44. package/src/components/input/markup/foundation/Input.tsx +2 -2
  45. package/src/components/input/styles/foundation.scss +57 -54
  46. package/src/components/input/types/foundation.ts +1 -1
  47. package/src/components/navigation/index.tsx +7 -0
  48. package/src/components/navigation/markup/index.tsx +6 -0
  49. package/src/components/pagination/index.tsx +6 -1
  50. package/src/components/pagination/markup/index.tsx +7 -0
  51. package/src/components/pop-over/index.tsx +7 -0
  52. package/src/components/pop-over/markup/index.tsx +5 -4
  53. package/src/components/radio/index.tsx +5 -1
  54. package/src/components/scrollbar/hooks/index.ts +1 -1
  55. package/src/components/scrollbar/index.tsx +1 -1
  56. package/src/components/scrollbar/markup/index.tsx +1 -1
  57. package/src/components/scrollbar/types/index.ts +1 -1
  58. package/src/components/scrollbar/utils/index.ts +1 -1
  59. package/src/components/segmented-control/index.tsx +5 -1
  60. package/src/components/segmented-control/markup/index.ts +6 -0
  61. package/src/components/select/index.tsx +6 -1
  62. package/src/components/select/markup/Default.tsx +10 -13
  63. package/src/components/select/markup/foundation/Selected.tsx +31 -26
  64. package/src/components/select/markup/index.tsx +1 -1
  65. package/src/components/select/markup/multiple/Multiple.tsx +32 -15
  66. package/src/components/select/styles/select.scss +15 -6
  67. package/src/components/select/styles/variables.scss +4 -0
  68. package/src/components/select/types/multiple.ts +19 -0
  69. package/src/components/select/types/props.ts +19 -6
  70. package/src/components/select/utils/display.tsx +41 -0
  71. package/src/components/select/utils/index.ts +1 -4
  72. package/src/components/slot/index.tsx +7 -0
  73. package/src/components/slot/markup/index.tsx +6 -0
  74. package/src/components/spinner/hooks/index.ts +1 -1
  75. package/src/components/spinner/index.tsx +1 -1
  76. package/src/components/spinner/markup/index.tsx +1 -1
  77. package/src/components/spinner/types/index.ts +1 -1
  78. package/src/components/spinner/utils/index.ts +1 -1
  79. package/src/components/tab/index.tsx +5 -1
  80. package/src/components/tab/markup/index.tsx +8 -0
  81. package/src/components/table/index.tsx +3 -0
  82. package/src/components/tooltip/index.tsx +7 -0
  83. package/src/components/tooltip/markup/index.tsx +7 -6
@@ -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 {...containerProps} size={size} width={width}>
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 resolvedMinWidth = useMemo(() => {
51
- if (shouldMatchTriggerWidth) {
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 width === "number") {
56
- return `${width}px`;
60
+ if (typeof value === "number") {
61
+ return `${value}px`;
57
62
  }
58
63
 
59
- if (typeof width === "string") {
60
- if (width === "fit-content") {
64
+ if (typeof value === "string") {
65
+ if (value === "fit-content") {
61
66
  return "fit-content";
62
67
  }
63
- if (width === "max-content") {
68
+ if (value === "max-content") {
64
69
  return "max-content";
65
70
  }
66
- if (width.trim().length > 0 && width !== "match") {
67
- return width;
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
- }, [shouldMatchTriggerWidth, width]);
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
- * - Provider
7
- * - Root
8
- * - Container
9
- * - Menu
10
- * - Item
11
- * - List
12
- * - Panel
13
- * - Trigger
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 { DropdownPanelWidth, DropdownSize } from "./base";
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<DropdownContainerProps, "children" | "size" | "width">;
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 namespace 조립 전용 파일.
6
- * - index.tsx 배럴 파일에서 로직/객체 생성을 분리한다.
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,
@@ -1,3 +1,10 @@
1
+ /**
2
+ * InfoBox; 안내/상태 메시지 카테고리 배럴
3
+ * @desc
4
+ * - `InfoBox`: heading/body를 렌더링하는 루트 컴포넌트다.
5
+ * - `InfoBoxIcon`: state별 아이콘 맵이다.
6
+ * - `InfoBoxProps`, `InfoBoxState`: public contract 타입이다.
7
+ */
1
8
  import "./index.scss";
2
9
 
3
10
  export * from "./markup";
@@ -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"] 현재는 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.
@@ -1,2 +1,8 @@
1
+ /**
2
+ * InfoBox; 안내/상태 메시지 markup export 배럴
3
+ * @desc
4
+ * - `InfoBox`: heading/body를 렌더링하는 루트 컴포넌트다.
5
+ * - `InfoBoxIcon`: state별 아이콘 맵이다.
6
+ */
1
7
  export { default as InfoBox } from "./InfoBox";
2
8
  export * from "./Icon";
@@ -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] state 값. 현재는 info만 허용한다.
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
- * state 값. 현재는 info만 허용한다.
18
+ * info/caution/success/error 상태 축이다.
19
19
  * - info, caution, success, error
20
20
  */
21
21
  state?: InfoBoxState;
@@ -1,5 +1,10 @@
1
1
  /**
2
- * input 카테고리 배럴 placeholder: 실제 구현은 markup/ 하위에 추가한다.
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 {FormFieldWidth} [props.width] width preset
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
  (