@opsnow-mcp/opsnow-mcp-common-ui-server 1.0.39 → 1.0.41

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 (37) hide show
  1. package/build/components/all-component-tools.js +2 -0
  2. package/build/components/data/icon-names.generated.js +341 -0
  3. package/build/components/data/icon-names.js +32 -16
  4. package/build/components/examples/opsnow-common-chart-examples-data.js +73 -78
  5. package/build/components/examples/opsnow-common-forms-examples-data.js +1 -11
  6. package/build/components/examples/opsnow-common-grid-examples-data.js +271 -20
  7. package/build/components/examples/opsnow-common-layout-examples-data.js +12 -13
  8. package/build/components/examples/opsnow-common-popup-examples-data.js +14 -28
  9. package/build/components/examples/opsnow-common-progress-examples-data.js +0 -5
  10. package/build/components/examples/opsnow-common-stepper-examples-data.js +2 -3
  11. package/build/components/examples/opsnow-common-storage-examples-data.js +23 -4
  12. package/build/components/examples/opsnow-common-tooltip-examples-data.js +1 -21
  13. package/build/components/opsnow-common-calendar.js +36 -15
  14. package/build/components/opsnow-common-chart.js +213 -132
  15. package/build/components/opsnow-common-coach-mark.js +3 -2
  16. package/build/components/opsnow-common-data-status.js +17 -11
  17. package/build/components/opsnow-common-dropdown.js +80 -0
  18. package/build/components/opsnow-common-examples.js +4 -2
  19. package/build/components/opsnow-common-file-upload.js +4 -1
  20. package/build/components/opsnow-common-forms.js +73 -36
  21. package/build/components/opsnow-common-grid.js +72 -29
  22. package/build/components/opsnow-common-icons.js +23 -39
  23. package/build/components/opsnow-common-jsx-helper.js +36 -0
  24. package/build/components/opsnow-common-layout.js +66 -25
  25. package/build/components/opsnow-common-notification.js +3 -2
  26. package/build/components/opsnow-common-pagination.js +4 -4
  27. package/build/components/opsnow-common-popup.js +12 -11
  28. package/build/components/opsnow-common-progress.js +10 -16
  29. package/build/components/opsnow-common-select.js +26 -13
  30. package/build/components/opsnow-common-stepper.js +30 -7
  31. package/build/components/opsnow-common-tab.js +5 -3
  32. package/build/components/opsnow-common-toast-popup.js +1 -0
  33. package/build/components/opsnow-common-tooltip.js +44 -46
  34. package/build/components/opsnow-common-typography.js +11 -5
  35. package/build/components/opsnow-common-validate-props.js +0 -0
  36. package/build/index.js +2 -0
  37. package/package.json +2 -1
@@ -5,7 +5,8 @@ export const CoachMarkSchema = z.object({
5
5
  title: z.string().optional().describe("가이드 말풍선 제목 - 생략 시 본문만 표시"),
6
6
  content: z.string().describe("가이드 말풍선 본문 - 문자열 또는 JSX 사용 가능 (여러 줄/굵은 글씨/링크 등 표현 시 JSX)"),
7
7
  isJsxContent: z.boolean().optional().describe("content를 JSX로 렌더링할지 여부 - true면 content를 JSX 표현식으로 출력"),
8
- placement: z.enum(["top", "bottom", "left", "right"]).optional().describe("말풍선 표시 위치 (top/bottom/left/right) - 공간이 부족하면 MUI가 자동으로 반대쪽으로 flip"),
8
+ placement: z.enum(["top", "top-start", "top-end", "bottom", "bottom-start", "bottom-end", "left", "left-start", "left-end", "right", "right-start", "right-end"]).optional().describe("말풍선 표시 위치 (기본값: bottom) - 공간이 부족하면 MUI가 자동으로 반대쪽으로 flip"),
9
+ children: z.string().optional().describe("말풍선이 가리킬 대상 요소 JSX (실제 컴포넌트에선 필수 — 단일 엘리먼트, 예: <OpsnowCommonButton label=\"새 기능\" />)"),
9
10
  defaultOpen: z.boolean().optional().describe("닫힌 기록이 없을 때 첫 노출 여부 (기본 true) - false면 처음부터 표시되지 않음"),
10
11
  onClose: z.string().optional().describe("말풍선이 닫힐 때(X 또는 close 액션) 실행할 콜백 함수명"),
11
12
  actionLabel: z.string().optional().describe("하단 우측 액션 버튼 라벨 - 지정 시 actions={(close) => <Button onClick={close}>라벨</Button>} 형태로 렌더링 (close는 '닫기+영구저장' 핸들러)"),
@@ -52,7 +53,7 @@ export function createCoachMarkComponent() {
52
53
  <OpsnowCommonCoachMark
53
54
  ${props.join("\n ")}
54
55
  >
55
- // 가이드 대상 요소 필요(Mui Button or OpsnowCommonButton 등)
56
+ ${args.children ?? "{/* 가이드 대상 요소 (단일 엘리먼트, 필수) */}"}
56
57
  </OpsnowCommonCoachMark>
57
58
  `;
58
59
  return {
@@ -4,7 +4,8 @@ import { IconNamesMap, generateIconDescription } from "./data/icon-names.js";
4
4
  export const DataStatusSchema = z.object({
5
5
  type: z
6
6
  .enum(["loading", "noData", "noSearchResults", "withAction", "error"])
7
- .describe("데이터 상태 및 화면에 노출 되어야하는 데이터 상태 타입"),
7
+ .optional()
8
+ .describe("데이터 상태 및 화면에 노출 되어야하는 데이터 상태 타입 (기본값: loading)"),
8
9
  title: z
9
10
  .string()
10
11
  .optional()
@@ -14,10 +15,12 @@ export const DataStatusSchema = z.object({
14
15
  .optional()
15
16
  .describe("제목 아래 작게 보이는 자세한 설명(데이터 상태 메시지)"),
16
17
  iconName: z.string().optional().describe("아이콘 이름"),
18
+ useMuiIcon: z.boolean().optional().describe("상태 아이콘을 MUI 아이콘으로 표시 (label에 Material 아이콘 이름)"),
19
+ label: z.string().optional().describe("useMuiIcon일 때 표시할 Material 아이콘 이름"),
17
20
  buttonProps: z
18
21
  .object({
19
22
  label: z.string().describe("버튼 라벨"),
20
- disabled: z.boolean().default(true).describe("버튼 활성화 여부"),
23
+ disabled: z.boolean().optional().describe("버튼 비활성화 여부 (기본값: false)"),
21
24
  action: z
22
25
  .string()
23
26
  .optional()
@@ -32,7 +35,7 @@ export const DataStatusSchema = z.object({
32
35
  .describe(generateIconDescription({ format: "nested" }))
33
36
  .optional()
34
37
  .describe("아이콘 이름"),
35
- iconPosition: z.enum(["right", "left"]).describe("버튼 위치"),
38
+ iconPosition: z.enum(["right", "left"]).optional().describe("버튼 아이콘 위치"),
36
39
  })
37
40
  .optional()
38
41
  .describe("버튼 속성"),
@@ -44,7 +47,7 @@ export function createDataStatusComponent() {
44
47
  name: "createDataStatus",
45
48
  description: `데이터 상태 컴포넌트 - 로딩, 성공, 에러, no Data, 검색 결과 없음 상태 표시
46
49
  **style 적용 시:**
47
- div에 style 적용 보다는 mui/material의 Box, Stack, Typography 컴포넌트를 사용하여 스타일을 적용하는 것을 권장합니다.
50
+ OpsnowCommonDataStatus 자체에 준 sx·className·style은 적용되지 않습니다 — 바깥을 <Box sx={{ ... }}>로 감싸서 적용하세요.
48
51
  **버튼 액션 적용 시:**
49
52
  버튼 클릭 시 실행할 onConfirm 함수를 미리 정의한 후, buttonProps에서 참조하여 사용합니다.
50
53
  onConfirm은 콜백 함수이므로 사용자가 직접 구현해서 전달해야 합니다.
@@ -58,7 +61,8 @@ export function createDataStatusComponent() {
58
61
  handler: async (args) => {
59
62
  // 데이터 상태 컴포넌트 로직 구현
60
63
  let functionDefinitions = "";
61
- if (args.type === "withAction") {
64
+ // buttonProps.onConfirm은 필수
65
+ if (args.buttonProps) {
62
66
  functionDefinitions += `
63
67
  const onConfirm = () => {
64
68
  ${args.buttonProps?.action ? args.buttonProps.action : ""}
@@ -71,20 +75,22 @@ export function createDataStatusComponent() {
71
75
  if (args.title)
72
76
  dataStatusProps.push(`title="${args.title}"`);
73
77
  if (args.description)
74
- dataStatusProps.push(`description={${args.description}}`);
78
+ dataStatusProps.push(`description="${args.description}"`);
75
79
  if (args.iconName)
76
80
  dataStatusProps.push(`iconName="${args.iconName}"`);
81
+ if (args.useMuiIcon)
82
+ dataStatusProps.push(`useMuiIcon`);
83
+ if (args.label)
84
+ dataStatusProps.push(`label="${args.label}"`);
77
85
  if (args.buttonProps) {
78
86
  const buttonProps = [];
79
- if (args.buttonProps.action) {
80
- buttonProps.push(`onConfirm`);
81
- }
87
+ buttonProps.push(`onConfirm`);
82
88
  if (args.buttonProps.label)
83
89
  buttonProps.push(`label: "${args.buttonProps.label}"`);
84
90
  if (args.buttonProps.size)
85
91
  buttonProps.push(`size: "${args.buttonProps.size}"`);
86
- if (!args.buttonProps.disabled)
87
- buttonProps.push(`disabled: ${!args.buttonProps.disabled}`);
92
+ if (args.buttonProps.disabled)
93
+ buttonProps.push(`disabled: true`);
88
94
  if (args.buttonProps.muiIconName)
89
95
  buttonProps.push(`muiIconName: "${args.buttonProps.muiIconName}"`);
90
96
  if (args.buttonProps.iconName)
@@ -0,0 +1,80 @@
1
+ import { z } from "zod";
2
+ import { IconNamesMap } from "./data/icon-names.js";
3
+ import { jsxAttrs, jsxTag, jsxResult } from "./opsnow-common-jsx-helper.js";
4
+ // dropdown 패키지(@opsnow-common/opsnow-common-dropdown 2.x) 컴포넌트 중 Select/Autocomplete/CurrencySwitcher 외 항목
5
+ const SelectMenuItemSchema = z.object({
6
+ label: z.string().describe("메뉴 항목 텍스트 (필수)"),
7
+ iconName: z.enum(Object.keys(IconNamesMap)).optional().describe("OpsNow 아이콘 이름 — muiIconName과 둘 중 하나만"),
8
+ muiIconName: z.string().optional().describe("Material 아이콘 이름 (예: 'settings') — iconName과 둘 중 하나만"),
9
+ onClick: z.string().optional().describe("클릭 핸들러 함수명"),
10
+ link: z.string().optional().describe("이동할 링크 URL"),
11
+ hasDivider: z.boolean().optional().describe("항목 아래 구분선 표시"),
12
+ selected: z.boolean().optional().describe("선택 상태 표시"),
13
+ disabled: z.boolean().optional().describe("비활성화"),
14
+ keepOpenOnClick: z.boolean().optional().describe("클릭 후에도 메뉴를 닫지 않음 (기본 false — 토글 항목 등에만 true)"),
15
+ });
16
+ export const SelectMenuSchema = z.object({
17
+ menuItems: z.array(SelectMenuItemSchema).describe("메뉴 항목 배열 (필수)"),
18
+ button: z.string().optional().describe("메뉴를 여는 트리거 JSX (미지정 시 아이콘 버튼)"),
19
+ buttonIconName: z.enum(Object.keys(IconNamesMap)).optional().describe("기본 트리거 버튼의 아이콘 이름"),
20
+ listHeader: z.string().optional().describe("메뉴 목록 위에 표시할 JSX"),
21
+ listFooter: z.string().optional().describe("메뉴 목록 아래에 표시할 JSX"),
22
+ anchorOrigin: z.object({
23
+ vertical: z.enum(["top", "center", "bottom"]),
24
+ horizontal: z.enum(["left", "center", "right"]),
25
+ }).optional().describe("메뉴가 붙을 트리거 기준점 (MUI Menu anchorOrigin)"),
26
+ transformOrigin: z.object({
27
+ vertical: z.enum(["top", "center", "bottom"]),
28
+ horizontal: z.enum(["left", "center", "right"]),
29
+ }).optional().describe("메뉴 자신의 기준점 (MUI Menu transformOrigin)"),
30
+ });
31
+ export const CompanySelectSchema = z.object({
32
+ currentCompanyName: z.string().describe("현재 선택된 회사 이름 (필수)"),
33
+ isExtended: z.boolean().describe("확장(사이드바 펼침) 상태 여부 (필수)"),
34
+ autocompleteOptions: z.string().describe("회사 목록 변수명 — [{ value, label, group?, iconName?, disabled? }] (필수)"),
35
+ onSelect: z.string().describe("회사 선택 핸들러 함수명 ((selectedItem) => void) (필수)"),
36
+ });
37
+ export function createDropdownComponent() {
38
+ return [
39
+ {
40
+ name: "createSelectMenu",
41
+ description: `SelectMenu 컴포넌트 - 버튼을 누르면 펼쳐지는 액션 메뉴 (더보기 ⋯ 메뉴, 컨텍스트 메뉴)
42
+ 값을 고르는 드롭다운은 Select, 동작을 고르는 메뉴는 SelectMenu를 씁니다.
43
+ menuItems의 각 항목은 label이 필수이고, 아이콘은 iconName(OpsNow) 또는 muiIconName(Material) 중 하나만 지정합니다.
44
+
45
+ **import:**
46
+ \`\`\`javascript
47
+ import { useCommonComponents } from '@opsnow-common/opsnow-finops-common-ui-loader';
48
+ const { OpsnowCommonSelectMenu } = useCommonComponents();
49
+ \`\`\``,
50
+ parameters: SelectMenuSchema,
51
+ handler: async (args) => {
52
+ const items = args.menuItems.map(item => {
53
+ const fields = Object.entries(item)
54
+ .filter(([, v]) => v !== undefined)
55
+ .map(([k, v]) => (k === "onClick" ? `${k}: ${v}` : `${k}: ${JSON.stringify(v)}`));
56
+ return `{ ${fields.join(", ")} }`;
57
+ });
58
+ const { menuItems: _menuItems, ...rest } = args;
59
+ const attrs = [
60
+ `menuItems={[\n ${items.join(",\n ")}\n ]}`,
61
+ ...jsxAttrs(rest, { exprKeys: ["button", "listHeader", "listFooter"] }),
62
+ ];
63
+ return jsxResult(jsxTag("OpsnowCommonSelectMenu", attrs));
64
+ },
65
+ },
66
+ {
67
+ name: "createCompanySelect",
68
+ description: `CompanySelect 컴포넌트 - GNB/사이드바의 회사(테넌트) 선택 자동완성
69
+ 4개 prop 모두 필수입니다. 일반 페이지의 값 선택에는 Autocomplete를 쓰세요.
70
+
71
+ **import:**
72
+ \`\`\`javascript
73
+ import { useCommonComponents } from '@opsnow-common/opsnow-finops-common-ui-loader';
74
+ const { OpsnowCommonCompanySelect } = useCommonComponents();
75
+ \`\`\``,
76
+ parameters: CompanySelectSchema,
77
+ handler: async (args) => jsxResult(jsxTag("OpsnowCommonCompanySelect", jsxAttrs(args, { exprKeys: ["autocompleteOptions", "onSelect"] }))),
78
+ },
79
+ ];
80
+ }
@@ -1,5 +1,5 @@
1
1
  import { ButtonExamples, BadgeExamples, TextareaExamples, SwitchExamples, ChipExamples, TextFieldExamples, AvatarExamples, CheckboxExamples, RadioGroupExamples, CollapseExamples, SliderExamples, LinkExamples, TreeViewExamples, InsightCardExamples, InsightContentExamples, CurrencySwitcherExamples, ToggleButtonExamples } from "./examples/opsnow-common-forms-examples-data.js";
2
- import { IconExamples, MuiIconExamples } from "./examples/opsnow-common-icons-examples-data.js";
2
+ import { IconExamples, MuiIconExamples, MarkersExamples } from "./examples/opsnow-common-icons-examples-data.js";
3
3
  import { CalendarExamples } from "./examples/opsnow-common-calendar-examples-data.js";
4
4
  import { GaugeChartExamples, PieChartExamples, BarChartExamples, LineChartExamples, StackChartExamples, XyMultiChartExamples, HeatmapChartExamples, BubbleChartExamples } from "./examples/opsnow-common-chart-examples-data.js";
5
5
  import { TooltipExamples } from "./examples/opsnow-common-tooltip-examples-data.js";
@@ -14,7 +14,7 @@ import { FileUploaderExamples } from "./examples/opsnow-common-file-upload-examp
14
14
  import { NotificationExamples } from "./examples/opsnow-common-notification-examples-data.js";
15
15
  import { PaginationExamples } from "./examples/opsnow-common-pagination-examples-data.js";
16
16
  import { DataStatusExamples } from "./examples/opsnow-common-data-status-examples-data.js";
17
- import { ProgressExamples } from "./examples/opsnow-common-progress-examples-data.js";
17
+ import { ProgressExamples, LoadingExamples } from "./examples/opsnow-common-progress-examples-data.js";
18
18
  import { TabExamples } from "./examples/opsnow-common-tab-examples-data.js";
19
19
  import { GridLayoutExamples, MegaFilterExamples } from "./examples/opsnow-common-layout-examples-data.js";
20
20
  import { TypographyExamples } from "./examples/opsnow-common-typography-examples-data.js";
@@ -40,6 +40,7 @@ export const EXAMPLES_MAP = {
40
40
  ToggleButtonGroup: ToggleButtonExamples,
41
41
  Icon: IconExamples,
42
42
  MuiIcon: MuiIconExamples,
43
+ Markers: MarkersExamples,
43
44
  Calendar: CalendarExamples,
44
45
  GaugeChart: GaugeChartExamples,
45
46
  PieChart: PieChartExamples,
@@ -62,6 +63,7 @@ export const EXAMPLES_MAP = {
62
63
  Pagination: PaginationExamples,
63
64
  DataStatus: DataStatusExamples,
64
65
  Progress: ProgressExamples,
66
+ Loading: LoadingExamples,
65
67
  Tab: TabExamples,
66
68
  GridLayout: GridLayoutExamples,
67
69
  MegaFilter: MegaFilterExamples,
@@ -3,7 +3,7 @@ import { z } from "zod";
3
3
  export const FileUploaderSchema = z.object({
4
4
  accept: z.record(z.string(), z.array(z.string())).optional().describe("허용할 파일 타입 (예: { 'application/pdf': ['.pdf'] })"),
5
5
  multiple: z.boolean().optional().default(false).describe("다중 파일 업로드 가능 여부"),
6
- targetUrl: z.string().optional().describe("업로드 대상 URL"),
6
+ targetUrl: z.string().describe("업로드 대상 URL (필수)"),
7
7
  disabled: z.boolean().optional().default(false).describe("비활성화 여부"),
8
8
  helperText: z.string().optional().describe("도움말 텍스트"),
9
9
  fileNotSelectedText: z.string().optional().describe("파일 미선택 시 텍스트"),
@@ -15,6 +15,7 @@ export const FileUploaderSchema = z.object({
15
15
  placeholder: z.string().optional().describe("플레이스홀더 텍스트"),
16
16
  dropPlaceholder: z.string().optional().describe("파일을 드래그하여 업로드 영역 위로 올렸을 때 표시되는 안내 텍스트 (string). 미지정 시 라이브러리 기본 문구 사용"),
17
17
  onChange: z.string().optional().describe("onChange 핸들러 함수명"),
18
+ ref: z.string().optional().describe("useRef 변수명 — ref.current.upload(formDataKey, headers?, params?), clearFiles(), setInvalid(), clearInvalid() 호출용"),
18
19
  });
19
20
  // FileUploader MCP 툴 함수 - Stepper MCP 툴 구조 참고
20
21
  export function createFileUploaderComponent() {
@@ -34,6 +35,8 @@ export function createFileUploaderComponent() {
34
35
  const propList = Object.entries(args)
35
36
  .filter(([_, v]) => v !== undefined)
36
37
  .map(([k, v]) => {
38
+ if (k === 'onChange' || k === 'ref')
39
+ return `${k}={${v}}`;
37
40
  if (typeof v === 'string')
38
41
  return `${k}="${v}"`;
39
42
  if (typeof v === 'boolean')
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { IconNamesMap, generateIconDescription } from "./data/icon-names.js";
3
+ import { jsxAttrs, jsxTag, jsxResult } from "./opsnow-common-jsx-helper.js";
3
4
  // Button 컴포넌트 관련 스키마 정의
4
5
  export const ButtonSchema = z.object({
5
6
  label: z.string().optional().describe("버튼 라벨"),
@@ -13,12 +14,13 @@ export const ButtonSchema = z.object({
13
14
  iconOnly: z.boolean().optional().describe("아이콘만 표시"),
14
15
  disabled: z.boolean().optional().describe("비활성화 여부"),
15
16
  loading: z.boolean().optional().describe("로딩 상태"),
16
- onClick: z.string().optional().describe("클릭 이벤트 핸들러")
17
+ onClick: z.string().optional().describe("클릭 이벤트 핸들러"),
18
+ copilotId: z.string().optional().describe("Copilot 연동 ID — Copilot 매니페스트의 bind.copilotId와 매칭되어 AI가 이 컨트롤을 조작할 수 있게 함. useCommonComponents()로 받은 컴포넌트에서만 동작(로더 HOC가 소비 후 제거)"),
17
19
  });
18
20
  // Badge 컴포넌트 관련 스키마 정의
19
21
  export const BadgeSchema = z.object({
20
22
  badgeContent: z.number().optional().describe("뱃지 내용 (숫자)"),
21
- color: z.enum(["primary", "error", "info", "warning", "success"]).default("primary").describe("뱃지 색상"),
23
+ color: z.enum(["primary", "secondary", "error", "info", "warning", "success"]).default("primary").describe("뱃지 색상 (실제 컴포넌트에선 필수). badgeContent·variant·color·children·showZero 외 MUI Badge prop은 전달되지 않음"),
22
24
  variant: z.enum(["standard", "dot"]).optional().describe("뱃지 스타일"),
23
25
  showZero: z.boolean().optional().describe("0일 때도 표시 여부"),
24
26
  children: z.string().optional().describe("뱃지 내부 콘텐츠")
@@ -60,12 +62,14 @@ export const TextFieldSchema = z.object({
60
62
  placeholder: z.string().optional().describe("입력 필드에 표시될 플레이스홀더 텍스트"),
61
63
  value: z.string().optional().describe("입력 필드의 값. React 상태 변수 이름을 전달하세요 (예: 'name')."),
62
64
  onChange: z.string().optional().describe("값이 변경될 때 실행할 함수의 본문을 문자열로 전달하세요 (예: '(e) => setName(e.target.value)')."),
63
- variant: z.enum(["standard", "outlined", "filled"]).default("standard").describe("입력 필드 스타일"),
64
- type: z.enum(["text", "password", "number"]).optional().describe("입력 타입"),
65
+ variant: z.enum(["standard", "outlined", "filled"]).optional().describe("입력 필드 스타일 — 미지정 시 MUI 기본값 outlined로 렌더 (standard 원하면 명시)"),
66
+ type: z.string().optional().describe("input type (예: 'text', 'password', 'number', 'email', 'search')"),
65
67
  size: z.enum(["medium", "small"]).optional().describe("입력 필드 크기"),
66
68
  error: z.boolean().optional().describe("에러 상태 여부"),
67
69
  helperText: z.string().optional().describe("입력 필드 아래에 표시될 도움말 텍스트"),
68
70
  disabled: z.boolean().optional().describe("비활성화 여부"),
71
+ InputProps: z.string().optional().describe("InputProps 객체(JSX/문자열) — Input 컴포넌트에 전달 (예: { endAdornment: <InputAdornment position=\"end\">원</InputAdornment> }). 래퍼가 내부에서 slotProps.input으로 매핑"),
72
+ InputLabelProps: z.string().optional().describe("InputLabelProps 객체(JSX/문자열) — 라벨에 전달 (예: { shrink: true })"),
69
73
  inputProps: z.string().optional().describe(`inputProps 객체(JSX/문자열).
70
74
  - input 엘리먼트에 직접 전달할 속성 객체입니다.
71
75
  - 예: { readOnly: true }, { inputMode: 'numeric', pattern: '[0-9]*' }
@@ -122,22 +126,22 @@ export const TextFieldSchema = z.object({
122
126
  adornmentPosition: z.enum(["start", "end"]).optional().describe("장식(adornment)의 위치"),
123
127
  readOnly: z.boolean().optional().describe("읽기 전용 상태 여부 - 생성 코드에서 inputProps={{ readOnly: true }}로 변환되어 출력됨 (톱레벨 readOnly prop은 lib에 없음). inputProps를 직접 지정하는 경우 이 파라미터 대신 inputProps에 readOnly를 포함할 것"),
124
128
  adornmentLabelColor: z.string().optional().describe("장식(adornment) 라벨 텍스트의 색상 (CSS 색상 문자열, 예: '#666666', 'red')"),
125
- adornmentUnit: z.boolean().optional().describe("장식(adornment) 라벨을 단위(unit) 스타일로 표시할지 여부 (boolean, 기본값: false)"),
129
+ adornmentUnit: z.boolean().optional().describe("장식(adornment) 라벨을 단위(unit) 스타일로 표시할지 여부 (boolean, 기본값: true — 단위 스타일을 끄려면 false)"),
126
130
  adornmentIconColor: z.string().optional().describe("장식(adornment) 아이콘의 색상 (CSS 색상 문자열, 예: '#1976d2')"),
127
131
  withBtnVoice: z.boolean().optional().describe("입력 필드 우측에 음성 입력(마이크) 버튼 표시 여부 (boolean, 기본값: false)"),
128
132
  voiceOn: z.boolean().optional().describe("음성 입력 활성(녹음 중) 상태 여부 (boolean). withBtnVoice와 함께 사용하며 보통 상태 변수로 제어"),
129
133
  disabledVoice: z.boolean().optional().describe("음성 입력 버튼 비활성화 여부 (boolean, 기본값: false)"),
130
134
  voiceSupported: z.boolean().optional().describe("브라우저의 음성 인식 지원 여부 (boolean). false면 음성 버튼이 미지원 상태로 표시됨"),
131
135
  onVoice: z.string().optional().describe("음성 입력 버튼 클릭 시 호출될 함수명 (예: 'handleVoice'). () => void 형태"),
132
- withBtnIconName: z.enum(Object.keys(IconNamesMap)).optional().describe("입력 필드 우측 확인(withBtn) 버튼에 표시할 아이콘 이름"),
133
- withBtnDisabled: z.boolean().optional().describe("우측 확인(withBtn) 버튼 비활성화 여부 (boolean, 기본값: false)"),
134
- onConfirm: z.string().optional().describe("우측 확인(withBtn) 버튼 클릭 시 호출될 함수명 (예: 'handleConfirm'). () => void 형태"),
136
+ withBtnIconName: z.string().optional().describe("우측 확인 버튼의 Material 아이콘 이름 (기본값: 'send') — OpsNow IconName이 아님. withBtnVoice=true일 때만 표시"),
137
+ withBtnDisabled: z.boolean().optional().describe("우측 확인(withBtn) 버튼 비활성화 여부 (boolean, 기본값: false). withBtnVoice=true일 때만 동작"),
138
+ onConfirm: z.string().optional().describe("우측 확인(withBtn) 버튼 클릭 시 호출될 함수명 (예: 'handleConfirm'). () => void 형태. 확인 버튼은 withBtnVoice=true일 때만 나타남"),
135
139
  });
136
140
  // Avatar 컴포넌트 관련 스키마 정의
137
141
  export const AvatarSchema = z.object({
138
142
  src: z.string().optional().describe('이미지 URL'),
139
143
  alt: z.string().optional().describe('이미지 대체 텍스트'),
140
- size: z.enum(['xsmall', 'small', 'medium', 'large']).optional().describe('아바타 크기'),
144
+ size: z.union([z.enum(['xsmall', 'small', 'medium', 'large']), z.number()]).optional().describe('아바타 크기 — 프리셋 또는 px 숫자 (기본값: medium). 뱃지 기본값 badgeVariant=\'dot\', badgeOverlap=\'circular\''),
141
145
  variant: z.enum(['circular', 'rounded', 'square']).optional().describe('아바타 형태'),
142
146
  showBadge: z.boolean().optional().describe('뱃지 표시 여부'),
143
147
  sx: z.string().optional().describe('MUI sx 스타일 객체(JSX/문자열)'),
@@ -160,17 +164,18 @@ export const CheckboxSchema = z.object({
160
164
  });
161
165
  // RadioGroup 컴포넌트 관련 스키마 정의
162
166
  export const RadioGroupSchema = z.object({
163
- options: z.string().describe("라디오 옵션 배열 변수명 (예: options)"),
167
+ options: z.string().describe("라디오 옵션 배열 변수명 (예: options) — [{ label, value, disabled? }]. 개별 비활성화는 options[].disabled (RadioGroup 전체 disabled prop은 없음)"),
164
168
  color: z.enum(["primary", "secondary", "error", "success", "warning", "info", "default"]).optional().describe("라디오 색상"),
165
- name: z.string().describe("라디오 그룹 이름"),
169
+ name: z.string().optional().describe("라디오 그룹 이름"),
166
170
  defaultValue: z.string().optional().describe("기본 선택값"),
167
171
  row: z.boolean().optional().describe("가로 배치 여부"),
168
172
  size: z.enum(["small", "medium", "large"]).optional().describe("라디오 크기"),
173
+ value: z.string().optional().describe("선택된 값 (제어형) — 상태 변수명. onChange와 함께 사용"),
174
+ onChange: z.string().optional().describe("선택 변경 핸들러 함수명 ((e, value) => void)"),
169
175
  });
170
176
  // Collapse 컴포넌트 관련 스키마 정의
171
177
  export const CollapseSchema = z.object({
172
178
  label: z.string().describe("토글 라벨 텍스트"),
173
- defaultExpanded: z.boolean().optional().describe("기본 확장 여부"),
174
179
  children: z.string().describe("Collapse 내부에 들어갈 JSX/텍스트"),
175
180
  btnClass: z.string().optional().describe("토글 버튼에 적용할 커스텀 CSS 클래스명"),
176
181
  btnToggleClass: z.string().optional().describe("확장(펼침) 상태일 때 토글 버튼에 적용할 커스텀 CSS 클래스명"),
@@ -179,7 +184,7 @@ export const CollapseSchema = z.object({
179
184
  // Slider 컴포넌트 관련 스키마 정의
180
185
  export const SliderSchema = z.object({
181
186
  defaultValue: z.string().optional().describe("기본 값 (string, 단일 값: '30', 범위: '[10, 50]')"),
182
- color: z.enum(["primary", "secondary"]).optional().describe("슬라이더 색상"),
187
+ color: z.enum(["primary", "secondary", "error", "warning", "info", "success"]).optional().describe("슬라이더 색상"),
183
188
  size: z.enum(["small", "medium"]).optional().describe("슬라이더 크기"),
184
189
  step: z.number().optional().describe("슬라이더 step 값"),
185
190
  marks: z.string().optional().describe(`마크 표시 여부/마크 배열(string, 예: 'true', 'false', 또는 아래와 같은 배열 문자열)
@@ -198,31 +203,31 @@ export const SliderSchema = z.object({
198
203
  getAriaValueText: z.string().optional().describe("aria value 텍스트 함수 변수명"),
199
204
  ariaLabel: z.string().optional().describe("aria-label 속성"),
200
205
  sx: z.string().optional().describe("MUI sx 스타일 객체(JSX/문자열)"),
206
+ value: z.union([z.number(), z.array(z.number())]).optional().describe("현재 값 (제어형) — 상태 변수명. onChange와 함께 사용"),
201
207
  });
202
208
  // Link 컴포넌트 관련 스키마 정의
203
209
  export const LinkSchema = z.object({
204
210
  href: z.string().describe("링크 주소(href)"),
205
- color: z.enum(["primary", "inherit"]).optional().describe("링크 색상"),
211
+ color: z.enum(["primary", "inherit"]).default("primary").describe("링크 색상 (실제 컴포넌트에선 필수)"),
206
212
  underline: z.enum(["always", "hover", "none"]).optional().describe("밑줄 표시 방식"),
207
213
  onClick: z.string().optional().describe("클릭 이벤트 핸들러"),
208
214
  children: z.string().describe("링크 내부 텍스트/JSX"),
209
215
  });
210
216
  // ToggleButtonGroup 컴포넌트 관련 스키마 정의
211
217
  export const ToggleButtonSchema = z.object({
212
- value: z.string().describe('선택된 값'),
218
+ value: z.string().describe('선택된 값 상태 변수명 — string | string[] | null (exclusive가 아니면 배열)'),
213
219
  onChange: z.string().describe('값 변경 핸들러 함수명'),
214
220
  size: z.enum(['large', 'medium', 'small']).optional().describe('토글 버튼 크기'),
215
221
  exclusive: z.boolean().optional().describe('단일 선택 여부'),
216
222
  disabled: z.boolean().optional().describe('비활성화 여부'),
217
- theme: z.enum(['new']).optional().describe('테마'),
223
+ theme: z.enum(['us', 'new']).optional().describe("테마 (기본값: 'us')"),
218
224
  buttons: z.array(z.object({
219
225
  value: z.string().describe('버튼 값'),
220
226
  label: z.string().optional().describe('버튼 라벨'),
221
227
  iconName: z.enum(Object.keys(IconNamesMap)).optional().describe(generateIconDescription({ format: 'nested' })),
222
- iconOnly: z.boolean().optional().describe('아이콘만 표시'),
223
- iconPosition: z.enum(['left', 'right']).optional().describe('아이콘 위치'),
224
228
  disabled: z.boolean().optional().describe('비활성화 여부'),
225
229
  })).describe('토글 버튼 목록'),
230
+ copilotId: z.string().optional().describe("Copilot 연동 ID — Copilot 매니페스트의 bind.copilotId와 매칭되어 AI가 이 컨트롤을 조작할 수 있게 함. useCommonComponents()로 받은 컴포넌트에서만 동작(로더 HOC가 소비 후 제거)"),
226
231
  });
227
232
  // TreeView 컴포넌트 관련 스키마 정의
228
233
  export const TreeViewSchema = z.object({
@@ -232,6 +237,7 @@ export const TreeViewSchema = z.object({
232
237
  - id: 노드 고유 ID
233
238
  - label: 노드 라벨 텍스트
234
239
  - muiIconName: Material-UI 아이콘 이름 (예: 'settings', 'person', 'lock' 등)
240
+ - iconName: OpsNow 아이콘 이름 (IconName) — muiIconName이 있으면 muiIconName이 우선
235
241
  - children: 자식 노드 배열 (선택사항)
236
242
  - data: 노드에 추가 데이터 (선택사항, 예: { memberCount: 5 })
237
243
 
@@ -315,6 +321,23 @@ export const CurrencySwitcherSchema = z.object({
315
321
  showDetail: z.boolean().optional().describe("환율 설정(직접 입력 시뮬레이션) 섹션 노출 여부 (기본값: false). 실제 노출은 showDetail && simulationAnchorCurrency 지정 && simulationAnchorCurrency !== 선택 통화 3조건으로 판정 — 선택 통화가 기준 통화(USD)여도 앵커와 다르면 노출됨"),
316
322
  });
317
323
  // Forms 컴포넌트 함수 - 배열 반환
324
+ export const InputSchema = z.object({
325
+ value: z.string().optional().describe("입력값 상태 변수명 (예: 'keyword')"),
326
+ onChange: z.string().optional().describe("변경 핸들러 함수명 ((e) => void)"),
327
+ placeholder: z.string().optional().describe("플레이스홀더 텍스트"),
328
+ variant: z.enum(["standard", "outlined", "filled", "noLine", "emUnderline"]).optional().describe("입력 스타일 (기본값: standard)"),
329
+ helperText: z.string().optional().describe("입력 아래 도움말 텍스트"),
330
+ adornmentPosition: z.enum(["start", "end"]).optional().describe("장식(adornment) 위치 (기본값: start)"),
331
+ adornmentLabel: z.string().optional().describe("장식 라벨 텍스트 (예: '$', 'GB')"),
332
+ adornmentLabelColor: z.string().optional().describe("장식 라벨 색상 (CSS 색상)"),
333
+ adornmentUnit: z.boolean().optional().describe("장식 라벨을 단위(unit) 스타일로 표시 (기본값: true — 끄려면 false)"),
334
+ adornmentIconName: z.enum(Object.keys(IconNamesMap)).optional().describe("장식 아이콘 이름 (IconName)"),
335
+ adornmentIconColor: z.string().optional().describe("장식 아이콘 색상 (CSS 색상)"),
336
+ type: z.string().optional().describe("input type (예: 'text', 'number', 'password')"),
337
+ disabled: z.boolean().optional().describe("비활성화 여부"),
338
+ error: z.boolean().optional().describe("에러 상태"),
339
+ fullWidth: z.boolean().optional().describe("부모 너비 채움"),
340
+ });
318
341
  export function createFormsComponent() {
319
342
  return [
320
343
  {
@@ -538,14 +561,14 @@ export function createFormsComponent() {
538
561
  handler: async (args) => {
539
562
  const props = [];
540
563
  let inputPropsBlock = '';
541
- let InputPropsBlock = '';
542
564
  if (args.placeholder)
543
565
  props.push(`placeholder=\"${args.placeholder}\"`);
544
566
  if (args.value)
545
567
  props.push(`value={${args.value}}`);
546
568
  if (args.onChange)
547
569
  props.push(`onChange={${args.onChange}}`);
548
- if (args.variant && args.variant !== "standard")
570
+ // variant 미지정 시 MUI 기본 outlined로 렌더됨
571
+ if (args.variant)
549
572
  props.push(`variant=\"${args.variant}\"`);
550
573
  if (args.type)
551
574
  props.push(`type=\"${args.type}\"`);
@@ -568,8 +591,9 @@ export function createFormsComponent() {
568
591
  props.push(`inputProps={{ readOnly: true }}`);
569
592
  if (args.adornmentLabelColor)
570
593
  props.push(`adornmentLabelColor=\"${args.adornmentLabelColor}\"`);
571
- if (args.adornmentUnit)
572
- props.push(`adornmentUnit`);
594
+ // adornmentUnit은 런타임 기본값이 true → false일 때만 명시
595
+ if (args.adornmentUnit === false)
596
+ props.push(`adornmentUnit={false}`);
573
597
  if (args.adornmentIconColor)
574
598
  props.push(`adornmentIconColor=\"${args.adornmentIconColor}\"`);
575
599
  if (args.withBtnVoice)
@@ -588,6 +612,10 @@ export function createFormsComponent() {
588
612
  props.push(`withBtnDisabled`);
589
613
  if (args.onConfirm)
590
614
  props.push(`onConfirm={${args.onConfirm}}`);
615
+ if (args.InputProps)
616
+ props.push(`InputProps={${args.InputProps}}`);
617
+ if (args.InputLabelProps)
618
+ props.push(`InputLabelProps={${args.InputLabelProps}}`);
591
619
  // inputProps, InputProps 멀티라인/JSX 대응
592
620
  if (args.inputProps) {
593
621
  if (args.inputProps.trim().startsWith('{') && args.inputProps.includes('\n')) {
@@ -599,8 +627,8 @@ export function createFormsComponent() {
599
627
  }
600
628
  // 멀티라인 여부에 따라 코드 조립
601
629
  let code = '';
602
- if (inputPropsBlock || InputPropsBlock) {
603
- code = `<OpsnowCommonTextField\n ${props.join('\n ')}\n ${inputPropsBlock ? inputPropsBlock + '\n ' : ''}${InputPropsBlock ? InputPropsBlock + '\n' : ''}/>`;
630
+ if (inputPropsBlock) {
631
+ code = `<OpsnowCommonTextField\n ${props.join('\n ')}\n ${inputPropsBlock}\n/>`;
604
632
  }
605
633
  else {
606
634
  code = `<OpsnowCommonTextField ${props.join(' ')} />`;
@@ -727,6 +755,10 @@ export function createFormsComponent() {
727
755
  props.push(`defaultValue=\"${args.defaultValue}\"`);
728
756
  if (args.row)
729
757
  props.push(`row`);
758
+ if (args.value)
759
+ props.push(`value={${args.value}}`);
760
+ if (args.onChange)
761
+ props.push(`onChange={${args.onChange}}`);
730
762
  if (args.size)
731
763
  props.push(`size=\"${args.size}\"`);
732
764
  const code = `<OpsnowCommonRadioGroup ${props.join(' ')} />`;
@@ -754,8 +786,6 @@ export function createFormsComponent() {
754
786
  const props = [];
755
787
  if (args.label)
756
788
  props.push(`label=\"${args.label}\"`);
757
- if (args.defaultExpanded)
758
- props.push(`defaultExpanded`);
759
789
  if (args.btnClass)
760
790
  props.push(`btnClass=\"${args.btnClass}\"`);
761
791
  if (args.btnToggleClass)
@@ -931,14 +961,7 @@ export function createFormsComponent() {
931
961
  const buttonCodes = args.buttons.map(btn => {
932
962
  const btnProps = [];
933
963
  btnProps.push(`value=\"${btn.value}\"`);
934
- if (btn.label)
935
- btnProps.push(btn.iconName ? '' : btn.label); // label은 children으로 처리
936
- if (btn.iconName)
937
- btnProps.push(`<OpsnowCommonIcon iconName=\"${btn.iconName}\" />`);
938
- if (btn.iconOnly)
939
- btnProps.push(`iconOnly`);
940
- if (btn.iconPosition)
941
- btnProps.push(`iconPosition=\"${btn.iconPosition}\"`);
964
+ // label/icon은 children으로 처리
942
965
  if (btn.disabled)
943
966
  btnProps.push(`disabled`);
944
967
  // children 조립
@@ -946,7 +969,7 @@ export function createFormsComponent() {
946
969
  if (btn.iconName) {
947
970
  children = `<OpsnowCommonIcon iconName=\"${btn.iconName}\" />${btn.label ? ' ' + btn.label : ''}`;
948
971
  }
949
- return ` <OpsnowCommonToggleButton ${btnProps.filter(p => !p.startsWith('<')).join(' ')}>${children}</OpsnowCommonToggleButton>`;
972
+ return ` <OpsnowCommonToggleButton ${btnProps.join(' ')}>${children}</OpsnowCommonToggleButton>`;
950
973
  }).join('\n');
951
974
  const code = `<OpsnowCommonToggleButtonGroup\n ${props.join('\n ')}\n>\n${buttonCodes}\n</OpsnowCommonToggleButtonGroup>`;
952
975
  return {
@@ -1241,6 +1264,20 @@ export function createFormsComponent() {
1241
1264
  ]
1242
1265
  };
1243
1266
  }
1244
- }
1267
+ },
1268
+ {
1269
+ name: "createInput",
1270
+ description: `Input 컴포넌트 - 라벨 없이 쓰는 단일 입력 필드 (adornment 라벨/아이콘/단위 지원)
1271
+ 라벨·helperText를 갖춘 폼 필드는 TextField, 검색창·인라인 입력처럼 가벼운 입력은 Input을 씁니다.
1272
+ MUI Input props(type, disabled, error, fullWidth, inputProps 등)를 그대로 받습니다.
1273
+
1274
+ **import:**
1275
+ \`\`\`javascript
1276
+ import { useCommonComponents } from '@opsnow-common/opsnow-finops-common-ui-loader';
1277
+ const { OpsnowCommonInput } = useCommonComponents();
1278
+ \`\`\``,
1279
+ parameters: InputSchema,
1280
+ handler: async (args) => jsxResult(jsxTag("OpsnowCommonInput", jsxAttrs(args, { exprKeys: ["value", "onChange"] }))),
1281
+ },
1245
1282
  ];
1246
1283
  }