@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.
- package/build/components/all-component-tools.js +2 -0
- package/build/components/data/icon-names.generated.js +341 -0
- package/build/components/data/icon-names.js +32 -16
- package/build/components/examples/opsnow-common-chart-examples-data.js +73 -78
- package/build/components/examples/opsnow-common-forms-examples-data.js +1 -11
- package/build/components/examples/opsnow-common-grid-examples-data.js +271 -20
- package/build/components/examples/opsnow-common-layout-examples-data.js +12 -13
- package/build/components/examples/opsnow-common-popup-examples-data.js +14 -28
- package/build/components/examples/opsnow-common-progress-examples-data.js +0 -5
- package/build/components/examples/opsnow-common-stepper-examples-data.js +2 -3
- package/build/components/examples/opsnow-common-storage-examples-data.js +23 -4
- package/build/components/examples/opsnow-common-tooltip-examples-data.js +1 -21
- package/build/components/opsnow-common-calendar.js +36 -15
- package/build/components/opsnow-common-chart.js +213 -132
- package/build/components/opsnow-common-coach-mark.js +3 -2
- package/build/components/opsnow-common-data-status.js +17 -11
- package/build/components/opsnow-common-dropdown.js +80 -0
- package/build/components/opsnow-common-examples.js +4 -2
- package/build/components/opsnow-common-file-upload.js +4 -1
- package/build/components/opsnow-common-forms.js +73 -36
- package/build/components/opsnow-common-grid.js +72 -29
- package/build/components/opsnow-common-icons.js +23 -39
- package/build/components/opsnow-common-jsx-helper.js +36 -0
- package/build/components/opsnow-common-layout.js +66 -25
- package/build/components/opsnow-common-notification.js +3 -2
- package/build/components/opsnow-common-pagination.js +4 -4
- package/build/components/opsnow-common-popup.js +12 -11
- package/build/components/opsnow-common-progress.js +10 -16
- package/build/components/opsnow-common-select.js +26 -13
- package/build/components/opsnow-common-stepper.js +30 -7
- package/build/components/opsnow-common-tab.js +5 -3
- package/build/components/opsnow-common-toast-popup.js +1 -0
- package/build/components/opsnow-common-tooltip.js +44 -46
- package/build/components/opsnow-common-typography.js +11 -5
- package/build/components/opsnow-common-validate-props.js +0 -0
- package/build/index.js +2 -0
- package/package.json +2 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { jsxAttrs, jsxTag, jsxResult } from "./opsnow-common-jsx-helper.js";
|
|
2
3
|
const gridOptionsSchema = z.object({
|
|
3
4
|
animateRows: z.boolean().optional().describe('행 애니메이션 사용 여부'),
|
|
4
5
|
rowSelection: z.object({
|
|
@@ -9,7 +10,7 @@ const gridOptionsSchema = z.object({
|
|
|
9
10
|
enableSelectionWithoutKeys: z.boolean().optional().describe('키 없이 선택 허용'),
|
|
10
11
|
}).optional().describe('행 선택 옵션'),
|
|
11
12
|
tooltipShowDelay: z.number().optional().describe('툴팁 표시 지연 시간'),
|
|
12
|
-
domLayout: z.enum(['
|
|
13
|
+
domLayout: z.enum(['autoHeight', 'print']).optional().describe("그리드 레이아웃 방식 — 'autoHeight' 권장(최대 maxGridHeight까지 콘텐츠 맞춤). 고정 높이는 이 키를 빼고 gridHeight. ⚠️ 'normal'은 쓰지 말 것: 컨테이너 높이가 비워져 그리드가 0px로 사라짐(부모 높이와 무관, grid 2.0.7 브라우저 실측)"),
|
|
13
14
|
suppressCellFocus: z.boolean().optional().describe('셀 포커스 비활성화 (셀 클릭 이벤트 사용 시 필수: true)'),
|
|
14
15
|
suppressPaginationPanel: z.boolean().optional().describe('페이지네이션 패널 숨김'),
|
|
15
16
|
masterDetail: z.boolean().optional().describe('마스터/디테일 모드 사용'),
|
|
@@ -30,6 +31,7 @@ const gridOptionsSchema = z.object({
|
|
|
30
31
|
serverSideSortAllLevels: z.boolean().optional().describe('서버사이드 전체 레벨 정렬'),
|
|
31
32
|
getRowId: z.string().optional().describe('행 ID 반환 함수(stringified)'),
|
|
32
33
|
suppressMenuHide: z.boolean().optional().describe('메뉴 숨김 억제'),
|
|
34
|
+
rowClassRules: z.record(z.string()).optional().describe('행 클래스 규칙 { 클래스명: (params) => boolean (stringified) }. redrawRows({ rowNodes }) 호출 시 재평가됨 — 행 단위 로딩(dim/클릭 차단) 표현에 사용. \'셀·행 단위 로딩\' 예제 참고'),
|
|
33
35
|
});
|
|
34
36
|
const columnDefsSchema = z.lazy(() => z.object({
|
|
35
37
|
headerName: z.string().optional().describe('컬럼 헤더명'),
|
|
@@ -37,18 +39,20 @@ const columnDefsSchema = z.lazy(() => z.object({
|
|
|
37
39
|
colId: z.string().optional().describe('컬럼 고유 ID'),
|
|
38
40
|
groupId: z.string().optional().describe('컬럼 그룹 고유 ID'),
|
|
39
41
|
width: z.number().optional().describe('컬럼 너비'),
|
|
40
|
-
minWidth: z.number().describe('컬럼 최소 너비'),
|
|
42
|
+
minWidth: z.number().optional().describe('컬럼 최소 너비'),
|
|
41
43
|
maxWidth: z.number().optional().describe('컬럼 최대 너비'),
|
|
42
44
|
pinned: z.enum(['left', 'right']).optional().describe('컬럼 고정 위치'),
|
|
43
45
|
type: z.array(z.enum([
|
|
46
|
+
'default',
|
|
44
47
|
'number',
|
|
48
|
+
'integer',
|
|
45
49
|
'cost',
|
|
46
50
|
'ratio',
|
|
47
|
-
'mixFormat',
|
|
48
51
|
'numberTooltip',
|
|
52
|
+
'mixFormat',
|
|
49
53
|
'ratioBar',
|
|
50
54
|
'dataBar',
|
|
51
|
-
'
|
|
55
|
+
'date'
|
|
52
56
|
])).optional().describe(`컬럼 타입.
|
|
53
57
|
- ratioBar: 여러 색상/세그먼트로 구성 비율을 보여주는 스택 차트 스타일 바. 여러 필드의 비율을 시각화할 때 사용 (예: Cost Composition by Type). 'Ratio Bar' 예제 참고.
|
|
54
58
|
- dataBar: 단일 색상으로 셀 배경을 값에 비례하여 채우는 Excel 스타일 바. 단일 값의 크기를 시각화할 때 사용 (예: 월별 비용 트렌드). 'Data Bar' 예제 참고.`),
|
|
@@ -59,6 +63,8 @@ const columnDefsSchema = z.lazy(() => z.object({
|
|
|
59
63
|
digits: z.number().optional().describe('소수점 자리수'),
|
|
60
64
|
iconRight: z.boolean().optional().describe('오른쪽 아이콘 표시'),
|
|
61
65
|
isPercent: z.boolean().optional().describe('퍼센트 표시 여부'),
|
|
66
|
+
vendor: z.string().optional().describe('cost 타입: 벤더별 통화·포맷 (예: \'aws\')'),
|
|
67
|
+
isNegative: z.boolean().optional().describe('cost 타입: 음수 표시 처리'),
|
|
62
68
|
fields: z.array(z.string()).optional().describe('비율바 등에서 여러 필드'),
|
|
63
69
|
label: z.object({
|
|
64
70
|
postfix: z.string().optional().describe('라벨 접미사'),
|
|
@@ -122,15 +128,14 @@ const defaultColDefSchema = z.object({
|
|
|
122
128
|
});
|
|
123
129
|
// ag-grid 기반 OpsnowCommonDataGrid 스키마 정의
|
|
124
130
|
export const DataGridSchema = z.object({
|
|
125
|
-
totalPage: z.number().optional().describe('전체 페이지 수'),
|
|
126
|
-
initPageNum: z.number().optional().describe('초기 페이지 번호'),
|
|
127
|
-
dropdownSearchDefaultValue: z.string().optional().describe('드롭다운 검색 기본값'),
|
|
128
131
|
gridOptions: gridOptionsSchema.optional().describe('ag-grid 옵션'),
|
|
129
132
|
columnDefs: z.array(columnDefsSchema).describe("ag-grid 컬럼 정의").min(1),
|
|
130
133
|
defaultColDef: defaultColDefSchema.optional().describe('ag-grid 기본 컬럼 옵션'),
|
|
131
134
|
rowData: z.array(z.record(z.any())).optional().describe('그리드에 표시할 데이터'),
|
|
132
135
|
status: z.number().describe(`그리드 상태값: GRID.STATUS.LOADING(0), GRID.STATUS.DATA_EXISTS(1), GRID.STATUS.ERROR(9) 중 하나를 사용해야 합니다.
|
|
133
136
|
|
|
137
|
+
⚠️ LOADING은 그리드 **전체**를 가리는 로딩입니다. "이 셀만/이 행만 갱신 중" 같은 부분 로딩은 status로 표현할 수 없습니다 — status는 DATA_EXISTS로 두고, ref API(refreshCells/redrawRows) + 로딩 키 Set(useRef) 조합으로 구현합니다. '셀 단위 로딩' / '행 단위 로딩' 예제 참고.
|
|
138
|
+
|
|
134
139
|
**필수 구현 패턴:**
|
|
135
140
|
반드시 useState로 상태를 관리하고, useEffect 등을 사용하여 데이터 로딩 로직을 구현해야 합니다.
|
|
136
141
|
사용자는 이 상태 제어 코드 안에 실제 비즈니스 로직(API 호출, 데이터 처리 등)을 추가할 수 있습니다.
|
|
@@ -150,11 +155,12 @@ export const DataGridSchema = z.object({
|
|
|
150
155
|
setStatus(CommonConst.GRID_STATUS.ERROR)
|
|
151
156
|
})
|
|
152
157
|
}, [])`),
|
|
153
|
-
gridHeight: z.number().optional().describe(
|
|
158
|
+
gridHeight: z.number().optional().describe("고정 높이(px, 기본 483). gridOptions.domLayout을 지정하지 않았을 때만 적용 — domLayout에 값이 있으면 무시됨 ('normal'이면 그리드가 0px로 사라짐)"),
|
|
159
|
+
maxGridHeight: z.number().optional().describe("domLayout:'autoHeight'일 때 최대 높이(px, 헤더 42px 포함, 기본 600). 넘치면 본문 세로 스크롤. grid ≥2.0.6"),
|
|
160
|
+
unlimitedHeight: z.boolean().optional().describe("true면 autoHeight 높이 제한 해제(모든 행 펼침). 기본 false. 짧은 고정 목록에만 사용"),
|
|
154
161
|
setClass: z.string().optional().describe('그리드 클래스명'),
|
|
155
|
-
localRowClass: z.string().optional().describe('로컬 행 클래스명'),
|
|
156
162
|
pagination: z.boolean().optional().describe('페이지네이션 사용 여부 (true로 설정 시 내부적으로 opsnow-common-pagination 컴포넌트 사용)'),
|
|
157
|
-
paginationPageSize: z.number().optional().describe('페이지당 행 수'),
|
|
163
|
+
paginationPageSize: z.number().optional().describe('페이지당 행 수 (기본값: 10)'),
|
|
158
164
|
textNoData: z.string().optional().describe('데이터 없음 텍스트'),
|
|
159
165
|
noDataDescription: z.string().optional().describe('데이터 없음 설명'),
|
|
160
166
|
textLoading: z.string().optional().describe('로딩 중 텍스트'),
|
|
@@ -163,25 +169,24 @@ export const DataGridSchema = z.object({
|
|
|
163
169
|
filterSearchText: z.string().optional().describe('필터 검색 텍스트'),
|
|
164
170
|
searchTextNoData: z.string().optional().describe('검색 결과 없음 텍스트'),
|
|
165
171
|
searchTextNoDataDescription: z.string().optional().describe('검색 결과 없음 설명'),
|
|
166
|
-
pagingFromServer: z.boolean().optional().describe('서버 페이징 사용 여부'),
|
|
167
172
|
pageSizeList: z.array(z.object({
|
|
168
173
|
label: z.string().describe('페이지 사이즈 라벨'),
|
|
169
174
|
value: z.number().describe('페이지 사이즈 값')
|
|
170
|
-
})).optional().describe('페이지 사이즈 리스트 (예: [{ label: "5", value: 5 }, { label: "10", value: 10 }])'),
|
|
175
|
+
})).optional().describe('페이지 사이즈 리스트 (기본값: 10/20/50, 예: [{ label: "5", value: 5 }, { label: "10", value: 10 }])'),
|
|
171
176
|
placeholderText: z.string().optional().describe('플레이스홀더 텍스트'),
|
|
172
|
-
usedRowClass: z.boolean().optional().describe('행
|
|
177
|
+
usedRowClass: z.boolean().optional().describe('클릭 가능한 행 스타일(hover·커서) 적용 여부 — onCellClick이 있으면 자동 적용되므로 보통 지정 불필요'),
|
|
173
178
|
usedSearch: z.boolean().optional().describe('검색창 사용 여부'),
|
|
174
179
|
usedSearchAndDropdown: z.boolean().optional().describe('검색+드롭다운 동시 사용'),
|
|
175
180
|
useOpsnowUs: z.boolean().optional().describe('Opsnow US 스타일 사용'),
|
|
176
181
|
inTile: z.boolean().optional().describe('타일 내 그리드 여부'),
|
|
177
182
|
noDataCustomComponent: z.any().optional().describe('커스텀 No Data 컴포넌트'),
|
|
178
|
-
rowModelType: z.
|
|
183
|
+
rowModelType: z.enum(['clientSide', 'serverSide']).optional().describe("행 모델 타입 (기본값: 'clientSide')"),
|
|
179
184
|
serverSideDatasource: z.record(z.any()).optional().describe('서버사이드 데이터소스'),
|
|
180
185
|
cacheBlockSize: z.number().optional().describe('서버사이드 캐시 블록 크기'),
|
|
181
186
|
usedMenuItem: z.boolean().optional().describe('메뉴 아이템 사용 여부'),
|
|
182
187
|
showSelectedRowCount: z.boolean().optional().describe('선택 행 개수 표시'),
|
|
183
|
-
isAutoSizeAllColumns: z.boolean().optional().describe('모든 컬럼 자동 크기
|
|
184
|
-
langCd: z.string().optional().describe(
|
|
188
|
+
isAutoSizeAllColumns: z.boolean().optional().describe('모든 컬럼 자동 크기 조정. 모바일(≤767.95px)에서는 자동으로 true — 컬럼이 내용 폭에 맞춰지고, 컨테이너보다 넓으면 가로 스크롤이 생김 (flex/sizeColumnsToFit 전제 레이아웃은 모바일에서 달라질 수 있음)'),
|
|
189
|
+
langCd: z.string().optional().describe("언어 코드 (기본값: 'ko') — 보통 i18n.getLocale()"),
|
|
185
190
|
useTreeDataDragDrop: z.boolean().optional().describe('트리 데이터 드래그 사용'),
|
|
186
191
|
getDropTargetNode: z.string().optional().describe('드롭 타겟 노드 반환 함수(stringified)'),
|
|
187
192
|
emitQuickFilterChange: z.boolean().optional().describe('빠른 필터 변경 이벤트 발생'),
|
|
@@ -190,7 +195,7 @@ export const DataGridSchema = z.object({
|
|
|
190
195
|
onGridReady: z.string().optional().describe('그리드 준비 이벤트 핸들러(stringified)'),
|
|
191
196
|
onFirstDataRendered: z.string().optional().describe('첫 데이터 렌더링 이벤트 핸들러(stringified)'),
|
|
192
197
|
onGridSizeChanged: z.string().optional().describe('그리드 크기 변경 이벤트 핸들러(stringified)'),
|
|
193
|
-
onSelectionChanged: z.string().optional().describe('선택 변경 이벤트 핸들러(stringified)'),
|
|
198
|
+
onSelectionChanged: z.string().optional().describe('선택 변경 이벤트 핸들러(stringified). 반드시 gridOptions가 아니라 이 top-level prop으로 넘겨야 호출됨 (래퍼가 AG Grid 이벤트를 내부에서 잡고 이 prop을 부름). 인자는 이벤트 객체가 아니라 선택된 노드 배열: (selectedNodes) => setCount(selectedNodes.length)'),
|
|
194
199
|
onCellClick: z.string().optional().describe('셀 클릭 이벤트 핸들러(stringified)'),
|
|
195
200
|
onFilterChanged: z.string().optional().describe('필터 변경 이벤트 핸들러(stringified)'),
|
|
196
201
|
onUpdateFilter: z.string().optional().describe('필터 업데이트 이벤트 핸들러(stringified)'),
|
|
@@ -198,6 +203,12 @@ export const DataGridSchema = z.object({
|
|
|
198
203
|
onSetPage: z.string().optional().describe('페이지 변경 이벤트 핸들러(stringified)'),
|
|
199
204
|
serverSideTotalPageSize: z.number().optional().describe('서버사이드 전체 페이지 수')
|
|
200
205
|
});
|
|
206
|
+
export const HeaderFilterSchema = z.object({
|
|
207
|
+
gridData: z.string().describe("AG Grid가 headerComponent에 넘기는 header params 변수명 (column, displayName, enableSorting, setSort, context) (필수)"),
|
|
208
|
+
textRight: z.boolean().optional().describe("헤더 텍스트 우측 정렬 (숫자 컬럼)"),
|
|
209
|
+
isUsedMenuFilter: z.boolean().optional().describe("헤더 필터 메뉴 사용 여부"),
|
|
210
|
+
children: z.string().optional().describe("헤더 텍스트 옆에 넣을 JSX (예: 툴팁 아이콘)"),
|
|
211
|
+
});
|
|
201
212
|
// DataGrid 컴포넌트 함수 - 배열 반환
|
|
202
213
|
export function createDataGridComponent() {
|
|
203
214
|
return [
|
|
@@ -239,18 +250,32 @@ export function createDataGridComponent() {
|
|
|
239
250
|
|
|
240
251
|
⚠️ defaultColDef에 wrapText, autoHeight가 없으면 텍스트가 넘칩니다!
|
|
241
252
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
253
|
+
**그리드 높이 정책 (grid ≥2.0.6) — 행 수로 분기하지 말 것**
|
|
254
|
+
- 기본: gridOptions.domLayout: 'autoHeight' 하나만. 행이 적으면 콘텐츠 높이에 맞추고,
|
|
255
|
+
maxGridHeight(기본 600px, 헤더 42px 포함)를 넘으면 본문에 세로 스크롤이 자동으로 생긴다.
|
|
256
|
+
- 상한 변경: maxGridHeight={800} / 상한 해제(모든 행 펼침): unlimitedHeight (짧은 고정 목록에만)
|
|
257
|
+
- 고정 높이: gridOptions에서 domLayout 키를 아예 빼고 gridHeight={N} (기본 483)
|
|
258
|
+
- ⚠️ domLayout: 'normal'을 명시하면 컨테이너 높이가 비워져 그리드가 0px로 사라진다 (gridHeight·부모 높이와 무관 — 브라우저 실측).
|
|
259
|
+
isScroll ? 'normal' : 'autoHeight' 같은 행 수 분기 패턴 금지.
|
|
260
|
+
- 상한은 헤더 1줄(42px) 기준이라 헤더가 여러 줄이면 그만큼 커지고, 페이지네이션 영역은 상한 밖이다.
|
|
261
|
+
- 모바일(≤767.95px)에서는 컬럼을 autoSizeAllColumns로 맞추므로 가로 스크롤이 생길 수 있다.
|
|
245
262
|
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
|
|
263
|
+
const gridOptions = { /* ... */ domLayout: 'autoHeight' };
|
|
264
|
+
<OpsnowCommonDataGrid gridOptions={gridOptions} /> // 최대 600px, 넘치면 스크롤
|
|
265
|
+
<OpsnowCommonDataGrid gridOptions={gridOptions} maxGridHeight={800} /> // 상한 변경
|
|
266
|
+
|
|
267
|
+
**셀·행 단위 부분 로딩 (Partial Loading) — status로 하지 말 것**
|
|
268
|
+
status={GRID_STATUS.LOADING}은 그리드 전체를 가립니다. "이 셀만 재계산 중", "선택한 행만 동기화 중" 같은 부분 로딩은
|
|
269
|
+
status는 DATA_EXISTS로 유지하고 아래 조합으로 앱에서 구현합니다 ('셀 단위 로딩' / '행 단위 로딩' 예제 전문 참고):
|
|
270
|
+
1. 로딩 중인 키는 useState가 아니라 useRef(new Set()) 에 담는다 — state로 두면 그리드 전체 리렌더로 스크롤이 튄다
|
|
271
|
+
2. 셀 단위: gridRef.current.refreshCells({ rowNodes, columns, force: true }) — 값이 그대로라 force: true 필수. rowNodes 생략 시 컬럼 전체
|
|
272
|
+
3. 행 단위: gridOptions.rowClassRules + gridRef.current.redrawRows({ rowNodes }) — redrawRows 시 rowClassRules 재평가. 여러 행은 rowNodes 배열로 묶어 한 번만 호출
|
|
273
|
+
4. 인디케이터로 셀 콘텐츠를 치환하지 말고, 원래 콘텐츠를 visibility: hidden 으로 남기고 인디케이터를 position: absolute 로 겹친다 (autoHeight라 치환하면 행 높이가 바뀜)
|
|
274
|
+
5. columnDefs/셀 렌더러는 useMemo/useCallback 으로 고정 — 매 렌더마다 새로 만들면 그리드가 통째로 다시 그려진다
|
|
275
|
+
6. 선택 노드 수집: forEachNode + node.isSelected() (getSelectedNodes는 ref API에 없음). onSelectionChanged는 top-level prop (인자 = 선택 노드 배열)
|
|
276
|
+
ref API (gridRef.current.*, 26종): getColumnState, applyColumnState, setColumnDefs, setAutoGroupColumnDef, sizeColumnsToFit / setRowData, applyTransaction, getGridData, getDisplayedRowAtIndex, forEachNode, forEachNodeAfterFilter, refreshCells, redrawRows / setNodesSelected, checkBoxSelectAll, saveSelectionState, loadSelectionState, setSelectionState, clearSelectionState / updateFilter / downloadExcel, downloadCsv / getRowDropZoneParams, addRowDropZone / setServerSideDatasource, setServerSideFilterList (노드 값 변경은 node.setDataValue(field, value))
|
|
277
|
+
⚠️ DataGrid는 AG Grid 옵션을 최상위 prop으로 받지 않습니다 (rowHeight, getRowId, context, onRowClicked 등) — 모두 gridOptions 안에 넣으세요.
|
|
278
|
+
rowModelType: 'serverSide' 그리드는 AG Grid 내장 loadingCellRenderer를 쓰는 것이 맞습니다.
|
|
254
279
|
|
|
255
280
|
**import:**
|
|
256
281
|
\`\`\`javascript
|
|
@@ -360,6 +385,24 @@ export function createDataGridComponent() {
|
|
|
360
385
|
]
|
|
361
386
|
};
|
|
362
387
|
}
|
|
363
|
-
}
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
name: "createHeaderFilter",
|
|
391
|
+
description: `HeaderFilter 컴포넌트 - DataGrid 컬럼 헤더에 정렬·필터 메뉴를 붙이는 커스텀 헤더
|
|
392
|
+
JSX로 직접 배치하지 않고 columnDefs의 headerComponent로 등록합니다. gridData에는 AG Grid가 넘기는 header params를 그대로 전달합니다.
|
|
393
|
+
예: { field: 'cost', headerComponent: (params) => <OpsnowCommonHeaderFilter gridData={params} textRight /> }
|
|
394
|
+
|
|
395
|
+
**import:**
|
|
396
|
+
\`\`\`javascript
|
|
397
|
+
import { useCommonComponents } from '@opsnow-common/opsnow-finops-common-ui-loader';
|
|
398
|
+
const { OpsnowCommonHeaderFilter } = useCommonComponents();
|
|
399
|
+
\`\`\``,
|
|
400
|
+
parameters: HeaderFilterSchema,
|
|
401
|
+
handler: async (args) => {
|
|
402
|
+
const { children, ...rest } = args;
|
|
403
|
+
const tag = jsxTag("OpsnowCommonHeaderFilter", jsxAttrs(rest, { exprKeys: ["gridData"] }), children);
|
|
404
|
+
return jsxResult(`const headerFilterColumn = {\n field: 'fieldName',\n headerComponent: (${args.gridData}) => (\n${tag}\n ),\n}\n// columnDefs={[ ..., headerFilterColumn ]}`);
|
|
405
|
+
},
|
|
406
|
+
},
|
|
364
407
|
];
|
|
365
408
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { jsxAttrs, jsxTag, jsxResult } from "./opsnow-common-jsx-helper.js";
|
|
2
3
|
import { IconNamesMap, generateIconDescription } from "./data/icon-names.js";
|
|
3
4
|
export const IconSchema = z.object({
|
|
4
5
|
iconName: z.enum(Object.keys(IconNamesMap)).describe(generateIconDescription({ format: 'nested' })),
|
|
@@ -8,21 +9,20 @@ export const IconSchema = z.object({
|
|
|
8
9
|
onClick: z.string().optional().describe("클릭 이벤트 핸들러 함수명 (HTMLAttributes 상속으로 span에 적용됨)"),
|
|
9
10
|
});
|
|
10
11
|
export const MuiIconSchema = z.object({
|
|
11
|
-
theme: z.
|
|
12
|
+
theme: z.enum(["mixed-circle-line-legend", "mixed-stack-line-legend", "diagonal-legend"]).optional().describe("레전드 마커 모양 (범례용일 때만) — 아이콘 이름은 theme이 아니라 label"),
|
|
12
13
|
hexColor: z.string().optional().describe("HEX 색상"),
|
|
13
14
|
size: z.number().optional().default(24).describe("아이콘 크기(px)"),
|
|
14
15
|
noCircle: z.boolean().optional().describe("원형 배경 없음"),
|
|
15
16
|
alpha: z.number().optional().describe("투명도"),
|
|
16
|
-
color: z.
|
|
17
|
-
label: z.string().optional().describe("MUI 아이콘 이름")
|
|
17
|
+
color: z.enum(["primary", "secondary", "success", "error", "info", "warning", "inherit"]).optional().describe("MUI 색상"),
|
|
18
|
+
label: z.string().optional().describe("MUI(Material) 아이콘 이름 (예: 'close', 'menu') — 기본값 add_icon")
|
|
19
|
+
});
|
|
20
|
+
export const MarkersSchema = z.object({
|
|
21
|
+
theme: z.enum(["square", "line-graph", "line-trend", "line-dashed", "mixed-line-stack"]).optional().describe("마커 모양 — square(사각), line-graph/line-trend/line-dashed(선), mixed-line-stack(선+막대)"),
|
|
22
|
+
hexColor: z.string().optional().describe("마커 색상 HEX (미지정 시 primary.main)"),
|
|
23
|
+
lineAlpha: z.number().optional().describe("선 부분 투명도 (0~1)"),
|
|
24
|
+
shapesAlpha: z.number().optional().describe("도형 부분 투명도 (0~1). mixed-line-stack이면 0.3으로 고정"),
|
|
18
25
|
});
|
|
19
|
-
// export const MarkersSchema = z.object({
|
|
20
|
-
// theme: z.string().describe("마커 테마"),
|
|
21
|
-
// hexColor: z.string().optional().describe("HEX 색상"),
|
|
22
|
-
// lineAlpha: z.number().optional().describe("선 투명도"),
|
|
23
|
-
// size: z.number().optional().default(24).describe("마커 크기(px)")
|
|
24
|
-
// });
|
|
25
|
-
// export type MarkersProps = z.infer<typeof MarkersSchema>;
|
|
26
26
|
export function createIconsComponent() {
|
|
27
27
|
return [
|
|
28
28
|
{
|
|
@@ -103,34 +103,18 @@ export function createIconsComponent() {
|
|
|
103
103
|
};
|
|
104
104
|
}
|
|
105
105
|
},
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
// const props: string[] = [];
|
|
120
|
-
// props.push(`theme="${args.theme}"`);
|
|
121
|
-
// if (args.hexColor) props.push(`hexColor="${args.hexColor}"`);
|
|
122
|
-
// if (args.lineAlpha) props.push(`lineAlpha={${args.lineAlpha}}`);
|
|
123
|
-
// if (args.size) props.push(`size={${args.size}}`);
|
|
124
|
-
// const code = `<OpsnowCommonMarkers ${props.join(" ")} />`;
|
|
125
|
-
// return {
|
|
126
|
-
// content: [
|
|
127
|
-
// {
|
|
128
|
-
// type: "text" as const,
|
|
129
|
-
// text: `\`\`\`jsx\n${code}\n\`\`\``
|
|
130
|
-
// }
|
|
131
|
-
// ]
|
|
132
|
-
// };
|
|
133
|
-
// }
|
|
134
|
-
// }
|
|
106
|
+
{
|
|
107
|
+
name: "createMarkers",
|
|
108
|
+
description: `Markers 컴포넌트 - 차트 범례·커스텀 레전드에 쓰는 시리즈 마커 (선/도형 조합)
|
|
109
|
+
크기는 고정이라 size prop이 없습니다.
|
|
110
|
+
|
|
111
|
+
**import:**
|
|
112
|
+
\`\`\`javascript
|
|
113
|
+
import { useOpsnowCommonIcons } from '@opsnow-common/opsnow-finops-common-ui-loader';
|
|
114
|
+
const { OpsnowCommonMarkers } = useOpsnowCommonIcons();
|
|
115
|
+
\`\`\``,
|
|
116
|
+
parameters: MarkersSchema,
|
|
117
|
+
handler: async (args) => jsxResult(jsxTag("OpsnowCommonMarkers", jsxAttrs(args))),
|
|
118
|
+
},
|
|
135
119
|
];
|
|
136
120
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// 생성기 공용: 툴 입력(args)을 JSX 속성 문자열로 변환
|
|
2
|
+
// - exprKeys에 든 키(상태 변수명·함수명·JSX)는 {값}으로, 그 외 문자열은 "값"으로 출력
|
|
3
|
+
// - true → 속성만, false → {false}, 숫자 → {n}, 객체/배열 → {JSON}
|
|
4
|
+
// - skipKeys는 출력하지 않음 (생성기 입력 전용 키 등)
|
|
5
|
+
export function jsxAttrs(args, opts = {}) {
|
|
6
|
+
const exprKeys = new Set(opts.exprKeys ?? []);
|
|
7
|
+
const skipKeys = new Set(opts.skipKeys ?? []);
|
|
8
|
+
const out = [];
|
|
9
|
+
for (const [k, v] of Object.entries(args)) {
|
|
10
|
+
if (v === undefined || v === null || skipKeys.has(k))
|
|
11
|
+
continue;
|
|
12
|
+
if (typeof v === "string")
|
|
13
|
+
out.push(exprKeys.has(k) ? `${k}={${v}}` : `${k}="${v}"`);
|
|
14
|
+
else if (typeof v === "boolean")
|
|
15
|
+
out.push(v ? k : `${k}={false}`);
|
|
16
|
+
else if (typeof v === "number")
|
|
17
|
+
out.push(`${k}={${v}}`);
|
|
18
|
+
else
|
|
19
|
+
out.push(`${k}={${JSON.stringify(v)}}`);
|
|
20
|
+
}
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
// 여러 줄 JSX 태그 조립 (children 없으면 self-closing)
|
|
24
|
+
export function jsxTag(tag, attrs, children) {
|
|
25
|
+
const open = attrs.length ? `<${tag}\n ${attrs.join("\n ")}\n` : `<${tag}`;
|
|
26
|
+
if (!children)
|
|
27
|
+
return attrs.length ? `${open}/>` : `${open} />`;
|
|
28
|
+
return `${attrs.length ? open : open + " "}>\n ${children}\n</${tag}>`.replace(`<${tag} >`, `<${tag}>`);
|
|
29
|
+
}
|
|
30
|
+
// JSX 표현식 값: 문자열은 변수명·함수·JSX로 보고 그대로, 그 외(객체·배열·숫자·불리언)는 JSON 리터럴로
|
|
31
|
+
export function jsxExpr(value) {
|
|
32
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
33
|
+
}
|
|
34
|
+
export function jsxResult(code) {
|
|
35
|
+
return { content: [{ type: "text", text: `\`\`\`jsx\n${code}\n\`\`\`` }] };
|
|
36
|
+
}
|
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { jsxAttrs, jsxTag, jsxResult } from "./opsnow-common-jsx-helper.js";
|
|
3
|
+
// MUI 9 Grid size 값: 컬럼 수(1~12) | 'auto'(콘텐츠 폭) | 'grow'(남은 폭 채움)
|
|
4
|
+
const GridSizeValue = z.union([z.number(), z.enum(["auto", "grow"])]);
|
|
5
|
+
const GridBreakpointSize = z.object({
|
|
6
|
+
xs: GridSizeValue.optional(),
|
|
7
|
+
sm: GridSizeValue.optional(),
|
|
8
|
+
md: GridSizeValue.optional(),
|
|
9
|
+
lg: GridSizeValue.optional(),
|
|
10
|
+
xl: GridSizeValue.optional(),
|
|
11
|
+
});
|
|
2
12
|
const GridLayoutSchema = z.object({
|
|
3
13
|
items: z.array(z.string()).describe("렌더링할 JSX 문자열 배열 (각 항목은 컴포넌트 호출 코드)"),
|
|
4
|
-
layout: z.array(
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
md: z.number().optional(),
|
|
8
|
-
lg: z.number().optional(),
|
|
9
|
-
xl: z.number().optional(),
|
|
10
|
-
}).partial()).optional().describe("각 항목별 브레이크포인트 레이아웃 배열"),
|
|
11
|
-
defaultLayout: z.object({
|
|
12
|
-
xs: z.number().optional(),
|
|
13
|
-
sm: z.number().optional(),
|
|
14
|
-
md: z.number().optional(),
|
|
15
|
-
lg: z.number().optional(),
|
|
16
|
-
xl: z.number().optional(),
|
|
17
|
-
}).optional().describe("기본 브레이크포인트 레이아웃"),
|
|
18
|
-
spacing: z.number().optional().describe("Grid 간격 (MUI spacing 단위)"),
|
|
14
|
+
layout: z.array(GridBreakpointSize.partial()).optional().describe("각 항목별 브레이크포인트 size 배열 (MUI 9 Grid size prop으로 출력, 예: { xs: 12, md: 6 })"),
|
|
15
|
+
defaultLayout: GridBreakpointSize.optional().describe("기본 브레이크포인트 size (기본 { xs: 'grow' })"),
|
|
16
|
+
spacing: z.number().optional().describe("Grid 간격 (MUI spacing 단위). 미지정 시 섹션 간격 토큰(sectionGap 24px) 사용"),
|
|
19
17
|
tileProps: z.record(z.any()).optional().describe("OpsnowCommonTile에 전달할 추가 props"),
|
|
20
18
|
});
|
|
21
19
|
const MegaFilterSchema = z.object({
|
|
@@ -23,10 +21,12 @@ const MegaFilterSchema = z.object({
|
|
|
23
21
|
vendors: z.string().optional().describe("벤더 옵션 배열 변수명"),
|
|
24
22
|
selectedVendor: z.string().optional().describe("선택된 벤더 상태 변수명"),
|
|
25
23
|
onVendorChange: z.string().optional().describe("벤더 변경 이벤트 핸들러"),
|
|
26
|
-
onFilterChange: z.string().
|
|
24
|
+
onFilterChange: z.string().describe("필터 변경 이벤트 핸들러 (필수)"),
|
|
27
25
|
selectedDateRange: z.string().optional().describe("선택된 날짜 범위 상태 변수명"),
|
|
28
26
|
onDateRangeChange: z.string().optional().describe("날짜 범위 변경 이벤트 핸들러"),
|
|
29
|
-
locale: z.enum(["en", "ko"]).optional().describe("로케일 설정"),
|
|
27
|
+
locale: z.enum(["en", "ko", "ja", "zh"]).optional().describe("로케일 설정"),
|
|
28
|
+
chipStateOptions: z.string().optional().describe("필터 칩 상태 옵션 배열 변수명"),
|
|
29
|
+
filterCountText: z.string().optional().describe("선택 개수 텍스트 함수명 ((selected, total) => string)"),
|
|
30
30
|
title: z.string().optional().describe("필터 제목"),
|
|
31
31
|
clearAllText: z.string().optional().describe("전체 초기화 버튼 텍스트"),
|
|
32
32
|
clearText: z.string().optional().describe("개별 초기화 버튼 텍스트"),
|
|
@@ -36,6 +36,17 @@ const MegaFilterSchema = z.object({
|
|
|
36
36
|
noItemsText: z.string().optional().describe("검색 결과 없음 텍스트"),
|
|
37
37
|
addText: z.string().optional().describe("개별 필터 추가 버튼 텍스트"),
|
|
38
38
|
});
|
|
39
|
+
export const TileSchema = z.object({
|
|
40
|
+
size: z.union([z.number(), z.enum(["auto", "grow"])]).optional().describe("flex 크기 — 'grow'(남은 폭 채움, 기본값) | 'auto'(콘텐츠 폭) | 숫자(flex 비율)"),
|
|
41
|
+
theme: z.enum(["selectable", "gray", "white", "blue", "dash", "special"]).optional().describe("타일 테마 (dash = 점선 테두리)"),
|
|
42
|
+
borderColor: z.string().optional().describe("테두리 색상"),
|
|
43
|
+
borderStyle: z.string().optional().describe("테두리 스타일 (기본값: solid)"),
|
|
44
|
+
height: z.string().optional().describe("높이 (CSS 값, 예: '240px')"),
|
|
45
|
+
className: z.string().optional().describe("클래스명 — Tile은 sx를 받지 않으므로 스타일은 className으로"),
|
|
46
|
+
onClick: z.string().optional().describe("클릭 핸들러 함수명 (selectable 테마와 함께)"),
|
|
47
|
+
header: z.string().optional().describe("Tile.Header 안에 넣을 JSX (생성기 입력 — <OpsnowCommonTile.Header>로 출력)"),
|
|
48
|
+
content: z.string().optional().describe("Tile.Content 안에 넣을 JSX (생성기 입력 — <OpsnowCommonTile.Content>로 출력)"),
|
|
49
|
+
});
|
|
39
50
|
export function createGridLayoutComponent() {
|
|
40
51
|
return [
|
|
41
52
|
{
|
|
@@ -44,7 +55,8 @@ export function createGridLayoutComponent() {
|
|
|
44
55
|
|
|
45
56
|
**간격 규칙 (px 하드코딩 금지, 색상 토큰처럼 theme.commonSpacing 참조):**
|
|
46
57
|
- 페이지/그리드 컨테이너 패딩: theme.commonSpacing.pagePadding (24px)
|
|
47
|
-
- 섹션(카드 그룹) 사이 간격:
|
|
58
|
+
- 섹션(카드 그룹) 사이 간격: sectionGap (24px) — Grid container의 spacing="var(--opsnow-spacing-section-gap)" (sx gap 금지: size 폭 계산에 안 들어가 줄바꿈이 깨짐)
|
|
59
|
+
- MUI 9 Grid: <Grid size={{ xs: 12, md: 6 }}> (item/xs/md prop 금지)
|
|
48
60
|
- 카드(Box) 내부 패딩: theme.commonSpacing.cardPadding ('16px 20px')
|
|
49
61
|
- 카드 내부 요소 간 간격: theme.commonSpacing.cardGap (16px)
|
|
50
62
|
- 값이 여러 개이므로 sx는 콜백 형태로: sx={theme => ({ padding: theme.commonSpacing.pagePadding, gap: theme.commonSpacing.sectionGap })}
|
|
@@ -56,13 +68,13 @@ export function createGridLayoutComponent() {
|
|
|
56
68
|
`,
|
|
57
69
|
parameters: GridLayoutSchema,
|
|
58
70
|
handler: async (args) => {
|
|
59
|
-
const { items, layout = [], defaultLayout = { xs:
|
|
71
|
+
const { items, layout = [], defaultLayout = { xs: 'grow' }, spacing, } = args;
|
|
60
72
|
const gridItems = items.map((item, i) => {
|
|
61
73
|
const bp = layout[i] || defaultLayout;
|
|
62
|
-
const
|
|
63
|
-
.map(([k, v]) => `${k}
|
|
64
|
-
.join(" ");
|
|
65
|
-
return ` <Grid
|
|
74
|
+
const sizeObj = Object.entries(bp)
|
|
75
|
+
.map(([k, v]) => `${k}: ${typeof v === "string" ? `'${v}'` : v}`)
|
|
76
|
+
.join(", ");
|
|
77
|
+
return ` <Grid size={{ ${sizeObj} }} key={${i}}>` +
|
|
66
78
|
`\n <Box sx={theme => ({` +
|
|
67
79
|
`\n border: '1px solid #ddd',` +
|
|
68
80
|
`\n borderRadius: '12px',` +
|
|
@@ -77,7 +89,8 @@ export function createGridLayoutComponent() {
|
|
|
77
89
|
`\n </Grid>`;
|
|
78
90
|
}).join("\n");
|
|
79
91
|
const code = [
|
|
80
|
-
|
|
92
|
+
// 간격은 sx gap이 아니라 spacing으로 (gap은 size 폭 계산에 안 들어감)
|
|
93
|
+
`<Grid container spacing={${spacing !== undefined ? spacing : '"var(--opsnow-spacing-section-gap)"'}} sx={theme => ({ padding: theme.commonSpacing.pagePadding })}>`,
|
|
81
94
|
gridItems,
|
|
82
95
|
`</Grid>`,
|
|
83
96
|
].join("\n");
|
|
@@ -112,6 +125,10 @@ export function createGridLayoutComponent() {
|
|
|
112
125
|
props.push(`onVendorChange={${args.onVendorChange}}`);
|
|
113
126
|
if (args.onFilterChange)
|
|
114
127
|
props.push(`onFilterChange={${args.onFilterChange}}`);
|
|
128
|
+
if (args.chipStateOptions)
|
|
129
|
+
props.push(`chipStateOptions={${args.chipStateOptions}}`);
|
|
130
|
+
if (args.filterCountText)
|
|
131
|
+
props.push(`filterCountText={${args.filterCountText}}`);
|
|
115
132
|
if (args.selectedDateRange)
|
|
116
133
|
props.push(`selectedDateRange={${args.selectedDateRange}}`);
|
|
117
134
|
if (args.onDateRangeChange)
|
|
@@ -144,6 +161,30 @@ export function createGridLayoutComponent() {
|
|
|
144
161
|
]
|
|
145
162
|
};
|
|
146
163
|
},
|
|
147
|
-
}
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: "createTile",
|
|
167
|
+
description: `Tile 컴포넌트 - 카드형 컨테이너 (헤더 + 본문)
|
|
168
|
+
<OpsnowCommonTile>
|
|
169
|
+
<OpsnowCommonTile.Header>제목</OpsnowCommonTile.Header> // props: paddingX, paddingY (기본 16), borderColor
|
|
170
|
+
<OpsnowCommonTile.Content>본문</OpsnowCommonTile.Content> // props: paddingX, paddingY (기본 16)
|
|
171
|
+
</OpsnowCommonTile>
|
|
172
|
+
⚠️ Tile은 sx를 받지 않습니다 (rest props 미전달) — 스타일은 className 또는 감싸는 Box의 sx로.
|
|
173
|
+
⚠️ layout 패키지는 로더 의존성이 아니라 앱 package.json에 @opsnow-common/opsnow-common-layout@^2 를 직접 설치해야 합니다.
|
|
174
|
+
|
|
175
|
+
**import:**
|
|
176
|
+
\`\`\`javascript
|
|
177
|
+
import { OpsnowCommonTile } from '@opsnow-common/opsnow-common-layout';
|
|
178
|
+
\`\`\``,
|
|
179
|
+
parameters: TileSchema,
|
|
180
|
+
handler: async (args) => {
|
|
181
|
+
const { header, content, ...rest } = args;
|
|
182
|
+
const children = [
|
|
183
|
+
header ? `<OpsnowCommonTile.Header>${header}</OpsnowCommonTile.Header>` : "",
|
|
184
|
+
`<OpsnowCommonTile.Content>${content ?? ""}</OpsnowCommonTile.Content>`,
|
|
185
|
+
].filter(Boolean).join("\n ");
|
|
186
|
+
return jsxResult(jsxTag("OpsnowCommonTile", jsxAttrs(rest, { exprKeys: ["onClick"] }), children));
|
|
187
|
+
},
|
|
188
|
+
},
|
|
148
189
|
];
|
|
149
190
|
}
|
|
@@ -11,6 +11,7 @@ export const NotificationSchema = z.object({
|
|
|
11
11
|
"warning",
|
|
12
12
|
"info",
|
|
13
13
|
])
|
|
14
|
+
.optional()
|
|
14
15
|
.describe(`알림 타입(색상/스타일):
|
|
15
16
|
- "info": 회색/ 흰색 배경 + 배경과 동일한 색상의 아이콘 + 컬러 라인 없음
|
|
16
17
|
- "infoSuccess": 흰/회색 배경 + 왼쪽 초록 컬러 라인 + 초록 아이콘(placement: page 전용)
|
|
@@ -32,7 +33,7 @@ export const NotificationSchema = z.object({
|
|
|
32
33
|
- "card": 배경색 자체가 타입(success/ error/ warning/ info)에 따라 컬러풀하게 채워짐.
|
|
33
34
|
※ infoSuccess/infoWarning/infoError는 "page"에서만 사용 가능(배경 흰색, 컬러 라인 색상만 변함).
|
|
34
35
|
※ "card"는 배경색이 타입에 따라 바뀌며, infoSuccess/infoWarning/infoError는 지원하지 않음.`),
|
|
35
|
-
closable: z.boolean().optional().describe("수동 닫기 가능 여부"),
|
|
36
|
+
closable: z.boolean().optional().describe("수동 닫기 가능 여부 — 생성기 입력 (실제 prop 아님, 생성 코드에서 onClose + 상태로 변환)"),
|
|
36
37
|
bgWhite: z
|
|
37
38
|
.boolean()
|
|
38
39
|
.default(false)
|
|
@@ -42,7 +43,7 @@ export const NotificationSchema = z.object({
|
|
|
42
43
|
isCustom: z
|
|
43
44
|
.boolean()
|
|
44
45
|
.default(false)
|
|
45
|
-
.describe("커스텀 action 버튼 사용 여부 - action랑 같이
|
|
46
|
+
.describe("커스텀 action 버튼 사용 여부 - action랑 같이 사용 — 생성기 입력 (실제 prop 아님, 생성 코드에서 customActionBtn으로 변환)"),
|
|
46
47
|
}).describe("알림 컴포넌트 - 박스 형태로 알림을 제공하는 컴포넌트. 알림 컴포넌트 내에 버튼 사용 시 (action, isCustom 옵션 사용 시) 반드시 버튼 컴포넌트 동시 호출 하여 action에 OpsnowCommonButton 컴포넌트를 string으로 전달");
|
|
47
48
|
// Notification 컴포넌트 함수 - 배열 반환
|
|
48
49
|
export function createNotificationComponent() {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
// Pagination 컴포넌트 관련 스키마 정의
|
|
3
3
|
export const PaginationSchema = z.object({
|
|
4
|
-
page: z.number().describe("현재 페이지 번호"),
|
|
4
|
+
page: z.number().optional().describe("현재 페이지 번호 (기본값: 1)"),
|
|
5
5
|
rowsPerPage: z.number().optional().default(10).describe("페이지당 항목 수"),
|
|
6
6
|
size: z
|
|
7
7
|
.enum(["medium", "small", "large"])
|
|
@@ -18,7 +18,7 @@ export const PaginationSchema = z.object({
|
|
|
18
18
|
.string()
|
|
19
19
|
.optional()
|
|
20
20
|
.describe("rowsPerPage가 변경될 때 호출되는 핸들러 함수명 - 함수명만 필요함"),
|
|
21
|
-
isLocale: z.boolean().default(true).describe("locale(언어) 설정 여부"),
|
|
21
|
+
isLocale: z.boolean().default(true).describe("locale(언어) 설정 여부 — 생성기 입력 (실제 prop 아님, true면 locale={i18n.getLocale()}로 출력. 실제 prop locale 기본값은 'en')"),
|
|
22
22
|
onChangePageNum: z
|
|
23
23
|
.string()
|
|
24
24
|
.optional()
|
|
@@ -26,7 +26,7 @@ export const PaginationSchema = z.object({
|
|
|
26
26
|
onChangePageSize: z
|
|
27
27
|
.string()
|
|
28
28
|
.optional()
|
|
29
|
-
.describe("페이지당 항목수 변경(pageSize) 및 현재 페이지(pageNum) 변경되었을때 핸들러 함수. 함수 내부 코드(문자열)로 전달하세요. 예시: console.log('페이지당 항목 수가',pageSize,'로 변경되고, 현재 페이지는',pageNum,'입니다.')"),
|
|
29
|
+
.describe("페이지당 항목수 변경(pageSize) 및 현재 페이지(pageNum) 변경되었을때 핸들러 함수. 콜백 인자 순서는 (pageNum, pageSize). 함수 내부 코드(문자열)로 전달하세요. 예시: console.log('페이지당 항목 수가',pageSize,'로 변경되고, 현재 페이지는',pageNum,'입니다.')"),
|
|
30
30
|
onNext: z
|
|
31
31
|
.string()
|
|
32
32
|
.optional()
|
|
@@ -130,7 +130,7 @@ export function createPaginationComponent() {
|
|
|
130
130
|
}}`);
|
|
131
131
|
}
|
|
132
132
|
if (args.onChangePageSize) {
|
|
133
|
-
props.push(`onChangePageSize={(
|
|
133
|
+
props.push(`onChangePageSize={(pageNum, pageSize) => {
|
|
134
134
|
${args.onChangePageSize}
|
|
135
135
|
}}`);
|
|
136
136
|
}
|
|
@@ -2,19 +2,20 @@ import { z } from "zod";
|
|
|
2
2
|
// Popup 컴포넌트 관련 스키마 정의
|
|
3
3
|
export const PopupSchema = z.object({
|
|
4
4
|
title: z.string().describe("팝업 제목"),
|
|
5
|
-
visible: z.boolean().optional().default(false).describe("팝업 표시 여부"),
|
|
5
|
+
visible: z.boolean().optional().default(false).describe("팝업 표시 여부 — 생성기 입력. 실제 prop은 open (생성 코드에서 open={...}으로 출력)"),
|
|
6
6
|
closable: z.boolean().optional().default(true).describe("닫기 버튼 표시 여부 - false 시 생성 코드에서 hideHeaderClose로 변환 출력됨 (lib에 closable prop 없음). 크기 조절은 width/height 대신 size(sm/md/lg) 사용"),
|
|
7
|
-
disableEscapeKeyDown: z.boolean().optional().default(false).describe('ESC 키 입력으로 닫기 방지 여부'),
|
|
8
7
|
type: z.enum(['wing', 'standard', 'bottom']).optional().default('standard').describe("팝업 타입"),
|
|
9
|
-
size: z.enum(['sm', 'md', 'lg']).optional().describe(
|
|
8
|
+
size: z.enum(['sm', 'md', 'lg']).optional().describe("팝업 크기 (sm, md, lg) — 기본값: wing 'md', standard 'sm'"),
|
|
10
9
|
confirmButton: z.object({
|
|
11
|
-
|
|
12
|
-
variant: z.
|
|
13
|
-
|
|
10
|
+
text: z.string().optional().describe('확인 버튼 텍스트'),
|
|
11
|
+
variant: z.enum(['text', 'outlined', 'contained']).optional().describe('확인 버튼 변형'),
|
|
12
|
+
disabled: z.boolean().optional().describe('확인 버튼 비활성화 여부'),
|
|
13
|
+
color: z.string().optional().describe('확인 버튼 색상'),
|
|
14
|
+
}).optional().describe('확인 버튼 설정 객체 - { text, variant, disabled, color }'),
|
|
14
15
|
onConfirm: z.string().optional().describe('확인(Confirm) 버튼 클릭 시 실행할 함수명 (() => void 타입) - 예시: handleConfirm'),
|
|
15
16
|
onAction: z.string().optional().describe('액션(Action) 버튼 클릭 시 실행할 함수명 (() => void 타입) - 예시: handleAction'),
|
|
16
17
|
useSpecialTitle: z.boolean().optional().describe('특수 타이틀 스타일 사용 여부 (boolean, 기본값 없음)'),
|
|
17
|
-
useFooterCancelBtn: z.boolean().optional().describe('푸터에 취소 버튼 표시 여부 (boolean)'),
|
|
18
|
+
useFooterCancelBtn: z.boolean().optional().describe('푸터에 취소 버튼 표시 여부 (boolean, 기본값: true — 숨기려면 false)'),
|
|
18
19
|
useFooterActionBtn: z.boolean().optional().describe('푸터에 액션 버튼 표시 여부 (boolean)'),
|
|
19
20
|
cancelButton: z.object({
|
|
20
21
|
text: z.string().optional().describe('취소 버튼 텍스트'),
|
|
@@ -50,20 +51,20 @@ export function createPopupComponent() {
|
|
|
50
51
|
parameters: PopupSchema,
|
|
51
52
|
handler: async (args) => {
|
|
52
53
|
// <OpsnowCommonPopup ...> 코드만 동적으로 생성
|
|
53
|
-
const { title = 'Example Popup', visible = false, closable = true,
|
|
54
|
+
const { title = 'Example Popup', visible = false, closable = true, size, confirmButton, onConfirm, onAction, useSpecialTitle, useFooterCancelBtn, useFooterActionBtn, cancelButton, actionButton, hideHeaderClose, hideFooter, useCustomHeader = false, useCustomFooter = false, headerContent, footerContent, type = 'standard', } = args;
|
|
54
55
|
// prop 문자열 생성
|
|
55
56
|
const propStr = [
|
|
56
57
|
`title=\"${title}\"`,
|
|
57
58
|
`open={${visible}}`,
|
|
58
59
|
`onClose={handleClose}`,
|
|
59
|
-
disableEscapeKeyDown ? 'disableEscapeKeyDown' : '',
|
|
60
60
|
`type=\"${type}\"`,
|
|
61
|
-
`size=\"${size}\"
|
|
61
|
+
size ? `size=\"${size}\"` : '',
|
|
62
62
|
confirmButton ? `confirmButton={${JSON.stringify(confirmButton)}}` : '',
|
|
63
63
|
onConfirm ? `onConfirm={${onConfirm}}` : '',
|
|
64
64
|
onAction ? `onAction={${onAction}}` : '',
|
|
65
65
|
useSpecialTitle ? 'useSpecialTitle' : '',
|
|
66
|
-
useFooterCancelBtn
|
|
66
|
+
// useFooterCancelBtn은 런타임 기본값이 true → false일 때만 명시
|
|
67
|
+
useFooterCancelBtn === false ? 'useFooterCancelBtn={false}' : '',
|
|
67
68
|
useFooterActionBtn ? 'useFooterActionBtn' : '',
|
|
68
69
|
cancelButton ? `cancelButton={${JSON.stringify(cancelButton)}}` : '',
|
|
69
70
|
actionButton ? `actionButton={${JSON.stringify(actionButton)}}` : '',
|