@opsnow-mcp/opsnow-mcp-common-ui-server 1.0.27 → 1.0.29

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.
@@ -0,0 +1,45 @@
1
+ // createXxx 툴 정의 수집 (서버 index.ts와 validate-props CLI가 공용)
2
+ // 새 컴포넌트 파일 추가 시 여기에만 팩토리를 등록하면 서버/CLI 모두 반영된다.
3
+ import { createCalendarComponent } from "./opsnow-common-calendar.js";
4
+ import { createChartComponent } from "./opsnow-common-chart.js";
5
+ import { createDataGridComponent } from "./opsnow-common-grid.js";
6
+ import { createFormsComponent } from "./opsnow-common-forms.js";
7
+ import { createDataStatusComponent } from "./opsnow-common-data-status.js";
8
+ import { createProgressComponent } from "./opsnow-common-progress.js";
9
+ import { createNotificationComponent } from "./opsnow-common-notification.js";
10
+ import { createPaginationComponent } from "./opsnow-common-pagination.js";
11
+ import { createIconsComponent } from "./opsnow-common-icons.js";
12
+ import { createTabComponent } from "./opsnow-common-tab.js";
13
+ import { createGridLayoutComponent } from "./opsnow-common-layout.js";
14
+ import { createTypographyComponent } from "./opsnow-common-typography.js";
15
+ import { createTooltipComponent } from "./opsnow-common-tooltip.js";
16
+ import { createCoachMarkComponent } from "./opsnow-common-coach-mark.js";
17
+ import { createSelectComponent } from "./opsnow-common-select.js";
18
+ import { createPopupComponent } from "./opsnow-common-popup.js";
19
+ import { createStepperComponent } from "./opsnow-common-stepper.js";
20
+ import { createToastPopupComponent } from "./opsnow-common-toast-popup.js";
21
+ import { createFileUploaderComponent } from "./opsnow-common-file-upload.js";
22
+ const componentFactories = [
23
+ createCalendarComponent,
24
+ createChartComponent,
25
+ createDataGridComponent,
26
+ createFormsComponent,
27
+ createIconsComponent,
28
+ createDataStatusComponent,
29
+ createProgressComponent,
30
+ createNotificationComponent,
31
+ createPaginationComponent,
32
+ createTabComponent,
33
+ createGridLayoutComponent,
34
+ createTypographyComponent,
35
+ createTooltipComponent,
36
+ createCoachMarkComponent,
37
+ createSelectComponent,
38
+ createPopupComponent,
39
+ createStepperComponent,
40
+ createToastPopupComponent,
41
+ createFileUploaderComponent,
42
+ ];
43
+ export function collectComponentTools() {
44
+ return componentFactories.flatMap(factory => factory());
45
+ }
@@ -0,0 +1,91 @@
1
+ // CoachMark 컴포넌트 예제 데이터
2
+ export const CoachMarkExamples = [
3
+ {
4
+ title: "storageKey-title-content-placement",
5
+ code: `<OpsnowCommonCoachMark
6
+ storageKey="dashboard-filter-guide"
7
+ title="필터 사용법"
8
+ content="여기서 기간과 계정을 선택해 비용을 필터링할 수 있어요."
9
+ placement="bottom"
10
+ >
11
+ <Button>필터</Button>
12
+ </OpsnowCommonCoachMark>`,
13
+ description: "기본 가이드 말풍선 예제입니다. storageKey는 가이드마다 고유하게 지정하며(localStorage 키로 사용), X로 닫으면 localStorage에 저장되어 새로고침해도 다시 뜨지 않습니다.",
14
+ },
15
+ {
16
+ title: "content-only",
17
+ code: `<OpsnowCommonCoachMark
18
+ storageKey="content-only-guide"
19
+ content="제목 없이 본문만 표시할 수도 있어요."
20
+ placement="right"
21
+ >
22
+ <Button>본문만</Button>
23
+ </OpsnowCommonCoachMark>`,
24
+ description: "title 없이 content만 넘기면 본문만 표시되는 예제입니다.",
25
+ },
26
+ {
27
+ title: "placement-top",
28
+ code: `<OpsnowCommonCoachMark
29
+ storageKey="top-guide"
30
+ title="위쪽 말풍선"
31
+ content="대상 요소 위에 표시됩니다."
32
+ placement="top"
33
+ >
34
+ <Button>placement top</Button>
35
+ </OpsnowCommonCoachMark>`,
36
+ description: "placement로 말풍선 위치를 지정하는 예제입니다 (top/bottom/left/right).",
37
+ },
38
+ {
39
+ title: "placement-left",
40
+ code: `<OpsnowCommonCoachMark
41
+ storageKey="left-guide"
42
+ title="왼쪽 말풍선"
43
+ content="대상 요소 왼쪽에 표시됩니다."
44
+ placement="left"
45
+ >
46
+ <Button>placement left</Button>
47
+ </OpsnowCommonCoachMark>`,
48
+ description: "대상 요소 왼쪽에 표시되는 예제입니다. 공간이 부족하면 MUI가 자동으로 반대쪽으로 flip 됩니다.",
49
+ },
50
+ {
51
+ title: "jsx-content-onClose",
52
+ code: `<OpsnowCommonCoachMark
53
+ storageKey="jsx-guide"
54
+ title="새 기능 안내"
55
+ content={
56
+ <>
57
+ <div>여러 줄로 구성된 안내 문구입니다.</div>
58
+ <div><b>굵은 텍스트</b>나 링크도 넣을 수 있어요.</div>
59
+ </>
60
+ }
61
+ placement="right"
62
+ onClose={() => console.log('closed')}
63
+ >
64
+ <Button>JSX content</Button>
65
+ </OpsnowCommonCoachMark>`,
66
+ description: "content에 JSX를 넘겨 여러 줄/굵은 글씨/링크 등을 표현하고, onClose로 닫힐 때 콜백을 실행하는 예제입니다.",
67
+ },
68
+ {
69
+ title: "actions-button",
70
+ code: `<OpsnowCommonCoachMark
71
+ storageKey="actions-guide"
72
+ title="새 기능 안내"
73
+ content="아래 확인 버튼을 누르면 안내가 닫힙니다."
74
+ placement="bottom"
75
+ actions={(close) => (
76
+ <Button size="small" onClick={close}>확인</Button>
77
+ )}
78
+ >
79
+ <Button>actions 버튼</Button>
80
+ </OpsnowCommonCoachMark>`,
81
+ description: "actions에 함수를 주면 close 인자로 '닫기+영구저장' 핸들러를 받아 하단 우측에 액션 버튼을 표시하는 예제입니다.",
82
+ },
83
+ {
84
+ title: "reset-dismissed-guides",
85
+ code: `Object.keys(window.localStorage)
86
+ .filter((k) => k.startsWith('opsnow-common-coach-mark:'))
87
+ .forEach((k) => window.localStorage.removeItem(k));
88
+ window.location.reload();`,
89
+ description: "닫힘 기록을 초기화하는 예제입니다. localStorage에서 'opsnow-common-coach-mark:' prefix 키를 제거하면 닫았던 가이드가 다시 표시됩니다.",
90
+ },
91
+ ];
@@ -127,5 +127,26 @@ export const AutocompleteExamples = [
127
127
  title: 'Autocomplete - Placeholder only (no label)',
128
128
  code: `<OpsnowCommonAutocomplete items={[{ value: '1', label: 'Option 1' }, { value: '2', label: 'Option 2' }, { value: '3', label: 'Option 3' }]} multiple={false} value={value} onChange={setValue} placeholder="Search..." />`,
129
129
  description: 'label 없이 placeholder만 사용하는 오토컴플리트 예제입니다.'
130
+ },
131
+ {
132
+ title: 'Autocomplete - 긴 라벨 기본 동작 (드롭다운 자동 확장)',
133
+ code: `<FormControl sx={{ width: 200 }}>
134
+ <OpsnowCommonAutocomplete items={[{ value: '1', label: 'aws-prod-123456789012-ap-northeast-2-billing-account' }, { value: '2', label: 'gcp-organization-opsnow-cost-analytics-project' }, { value: '3', label: 'Short option' }]} multiple disableCloseOnSelect value={value} onChange={setValue} useCheckboxOption useSelectAll />
135
+ </FormControl>`,
136
+ description: '긴 라벨 기본 동작 예제입니다. 별도 prop 없이 사용하면 드롭다운이 옵션 라벨 폭에 맞게 넓어지고(최대 뷰포트 폭), 화면 오른쪽 공간이 부족하면 잘리는 대신 왼쪽으로 이동합니다. 드롭다운 너비 동작은 fitDropdownWidth, dropdownMaxWidth prop으로 제어합니다.'
137
+ },
138
+ {
139
+ title: 'Autocomplete - fitDropdownWidth (드롭다운을 입력 필드 폭에 고정)',
140
+ code: `<FormControl sx={{ width: 200 }}>
141
+ <OpsnowCommonAutocomplete items={[{ value: '1', label: 'aws-prod-123456789012-ap-northeast-2-billing-account' }, { value: '2', label: 'Short option' }]} multiple disableCloseOnSelect value={value} onChange={setValue} useCheckboxOption useSelectAll fitDropdownWidth />
142
+ </FormControl>`,
143
+ description: 'fitDropdownWidth prop을 주면 드롭다운이 입력 필드 폭에 고정되고 긴 라벨은 여러 줄로 줄바꿈됩니다. 좁은 레이아웃에서 드롭다운이 넓어지면 안 되는 곳에 사용하세요.'
144
+ },
145
+ {
146
+ title: 'Autocomplete - dropdownMaxWidth (확장 최대 너비 제한)',
147
+ code: `<FormControl sx={{ width: 200 }}>
148
+ <OpsnowCommonAutocomplete items={[{ value: '1', label: 'azure-subscription-9f8e7d6c-5b4a-3210-fedc-ba0987654321-management-group' }, { value: '2', label: 'Short option' }]} multiple disableCloseOnSelect value={value} onChange={setValue} useCheckboxOption useSelectAll dropdownMaxWidth={360} />
149
+ </FormControl>`,
150
+ description: 'dropdownMaxWidth prop으로 확장 최대 너비(px)를 지정하는 예제입니다. 드롭다운이 입력 필드보다 넓게 확장되되 지정한 너비(360px)로 제한되고, 그보다 긴 라벨은 줄바꿈됩니다.'
130
151
  }
131
152
  ];
@@ -5,12 +5,8 @@ export const CalendarSchema = z.object({
5
5
  type: z
6
6
  .enum(["date", "dateRange", "dateTime", "dateTimeRange"])
7
7
  .describe("단일 날짜, 날짜 범위, 날짜 + 시간 타입 중 선택"),
8
- date: z
9
- .enum(["month", "week", "day"])
10
- .optional()
11
- .describe("날짜 (YYYY-MM-DD 형식)"),
12
8
  views: z
13
- .array(z.enum(["month", "year", "week", "day", "hours", "minutes", "seconds"]))
9
+ .array(z.enum(["month", "year", "day", "hours", "minutes", "seconds"]))
14
10
  .optional()
15
11
  .describe("캘린더 뷰 타입 (여러개 선택 가능)"),
16
12
  activate: z
@@ -26,7 +22,7 @@ export const CalendarSchema = z.object({
26
22
  useShortcuts: z
27
23
  .boolean()
28
24
  .optional()
29
- .describe("쇼트컷 - 달력에서 자주 사용하는 날짜 범위를 빠르게 선택할 수 있는 원클릭 버튼들 표시 여부 (예: 오늘, 어제, 지난 7일, 이번 달 등)"),
25
+ .describe("쇼트컷 - 달력에서 자주 사용하는 날짜 범위를 빠르게 선택할 수 있는 원클릭 버튼들 표시 여부 (예: 오늘, 어제, 지난 7일, 이번 달 등). ⚠️ type이 'dateRange'일 때만 동작 (다른 타입에서는 무시됨)"),
30
26
  customShortcuts: z
31
27
  .array(z
32
28
  .object({
@@ -58,7 +54,9 @@ export const CalendarSchema = z.object({
58
54
  message: "Reset일 때는 num, direction 없이 사용하고, Months/Weeks/Days 때는 num과 direction을 모두 설정하세요.",
59
55
  }))
60
56
  .optional()
61
- .describe("커스텀 쇼트컷 - 기본 쇼트컷 대신 사용자가 원하는 날짜 범위 버튼들을 직접 설정 (예: {num: 2, date: 'Months', direction: 'Last'}, {date: 'Reset'})"),
57
+ .describe("커스텀 쇼트컷 - 기본 쇼트컷 대신 사용자가 원하는 날짜 범위 버튼들을 직접 설정 (예: {num: 2, date: 'Months', direction: 'Last'}, {date: 'Reset'}). ⚠️ type이 'dateRange'일 때만 동작 (다른 타입에서는 무시됨)"),
58
+ locale: z.enum(["ko", "en", "ja", "zh"]).optional().describe("달력 표시 언어 로케일 ('ko' | 'en' | 'ja' | 'zh'). 생성 코드는 항상 i18n.getLocale()과 연동된 locale 상태를 자동 전달하므로 보통 지정 불필요. 특정 언어로 고정하려면 생성된 코드의 locale 값을 직접 수정"),
59
+ onChange: z.string().optional().describe("날짜 선택(변경) 시 호출되는 콜백 - 라이브러리 prop으로 (newValue, context) 인자를 받음. 생성 코드에 setSelectedDate 기반 onChange 핸들러가 항상 자동 포함되므로 보통 지정 불필요. 추가 로직은 생성된 onChange 내부에 덧붙이면 됨"),
62
60
  });
63
61
  // Calendar 컴포넌트 함수 - 배열 반환
64
62
  export function createCalendarComponent() {
@@ -116,8 +114,9 @@ export function createCalendarComponent() {
116
114
  return disabledDates.includes(date.format('YYYY-MM-DD'))
117
115
  }\n`;
118
116
  }
119
- // 커스텀 쇼트컷 생성
120
- if (args.customShortcuts && args.customShortcuts.length > 0) {
117
+ // 커스텀 쇼트컷 생성 (useShortcuts/customShortcuts는 lib에서 dateRange 타입 전용)
118
+ const isDateRangeType = args.type === "dateRange";
119
+ if (isDateRangeType && args.customShortcuts && args.customShortcuts.length > 0) {
121
120
  functionDefinitions += `const customShortcuts = [\n`;
122
121
  args.customShortcuts.forEach((shortcut) => {
123
122
  if (shortcut.date === "Reset") {
@@ -178,11 +177,11 @@ export function createCalendarComponent() {
178
177
  props.push(`minDate={dayjs("${args.minDate}")}`);
179
178
  if (args.maxDate)
180
179
  props.push(`maxDate={dayjs("${args.maxDate}")}`);
181
- // customShortcuts가 있으면 useShortcuts는 무조건 true
182
- if (args.useShortcuts || args.customShortcuts) {
180
+ // customShortcuts가 있으면 useShortcuts는 무조건 true (dateRange 타입 전용)
181
+ if (isDateRangeType && (args.useShortcuts || args.customShortcuts)) {
183
182
  props.push("useShortcuts");
184
183
  }
185
- if (args.customShortcuts)
184
+ if (isDateRangeType && args.customShortcuts)
186
185
  props.push(`customShortcuts={customShortcuts}`);
187
186
  props.push(`locale={locale}`);
188
187
  if (args.views)