devextreme-planit-treegrid-react 0.2.2 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/DxPlanitTreeGrid.js +610 -0
- package/dist/index.js +10 -0
- package/dist/type.js +5 -0
- package/package.json +19 -12
- package/dist/DxPlanitTreeGrid.tsx +0 -580
- package/dist/index.tsx +0 -3
- package/dist/type.ts +0 -32
@@ -0,0 +1,610 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
5
|
+
value: true
|
6
|
+
});
|
7
|
+
exports.default = void 0;
|
8
|
+
var _react = require("react");
|
9
|
+
var _loadPanel = require("devextreme-react/load-panel");
|
10
|
+
var _pivotGrid = _interopRequireWildcard(require("devextreme-react/pivot-grid"));
|
11
|
+
var _dataGrid = require("devextreme-react/data-grid");
|
12
|
+
var _excel_exporter = require("devextreme/excel_exporter");
|
13
|
+
var _exceljs = require("exceljs");
|
14
|
+
var _fileSaver = _interopRequireDefault(require("file-saver"));
|
15
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
16
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
17
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
18
|
+
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
19
|
+
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
20
|
+
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
|
21
|
+
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
22
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
23
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
24
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
25
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
|
26
|
+
function _iterableToArrayLimit(arr, i) { var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"]; if (null != _i) { var _s, _e, _x, _r, _arr = [], _n = !0, _d = !1; try { if (_x = (_i = _i.call(arr)).next, 0 === i) { if (Object(_i) !== _i) return; _n = !1; } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0) { ; } } catch (err) { _d = !0, _e = err; } finally { try { if (!_n && null != _i.return && (_r = _i.return(), Object(_r) !== _r)) return; } finally { if (_d) throw _e; } } return _arr; } }
|
27
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
28
|
+
/**
|
29
|
+
* devextreme pivotgrid Configrations 중 사용 불가 항목 : id, width, height, showColumnGrandTotals, showColumnTotals, showRowGrandTotals, FieldChooser
|
30
|
+
* devextreme pivotgrid Configrations 중 사용 방법 변경 항목 : stateStoring, Export
|
31
|
+
* onExported, onFileSaving 이벤트 사용하지 않음.
|
32
|
+
*/
|
33
|
+
/**
|
34
|
+
* todoList:
|
35
|
+
* 2) columIndex 초기화 기능이 있어야 함(column 개수 변할 때)
|
36
|
+
* 3) 헤더에 테이블 삽입되면서 그리드 크기가 늘어남. height에 그리드 크기 늘어난 만큼 반영되어야 함.
|
37
|
+
*/
|
38
|
+
|
39
|
+
var grandTotalCssNm = 'data-grand-total';
|
40
|
+
var DxPlanitTreeGrid = /*#__PURE__*/(0, _react.forwardRef)(function (props, ref) {
|
41
|
+
var _props$id = props.id,
|
42
|
+
id = _props$id === void 0 ? 'dx-planit-vera-pivotgrid-id' : _props$id,
|
43
|
+
groupField = props.groupField,
|
44
|
+
dataColor = props.dataColor,
|
45
|
+
_props$convertNullToH = props.convertNullToHipen,
|
46
|
+
convertNullToHipen = _props$convertNullToH === void 0 ? true : _props$convertNullToH,
|
47
|
+
_props$convertZeroToH = props.convertZeroToHipen,
|
48
|
+
convertZeroToHipen = _props$convertZeroToH === void 0 ? true : _props$convertZeroToH,
|
49
|
+
_props$stateStoringKe = props.stateStoringKey,
|
50
|
+
stateStoringKey = _props$stateStoringKe === void 0 ? '' : _props$stateStoringKe,
|
51
|
+
_props$allowExpandAll = props.allowExpandAll,
|
52
|
+
allowExpandAll = _props$allowExpandAll === void 0 ? false : _props$allowExpandAll,
|
53
|
+
_props$allowFiltering = props.allowFiltering,
|
54
|
+
allowFiltering = _props$allowFiltering === void 0 ? false : _props$allowFiltering,
|
55
|
+
_props$allowSorting = props.allowSorting,
|
56
|
+
allowSorting = _props$allowSorting === void 0 ? false : _props$allowSorting,
|
57
|
+
_props$allowSortingBy = props.allowSortingBySummary,
|
58
|
+
allowSortingBySummary = _props$allowSortingBy === void 0 ? false : _props$allowSortingBy,
|
59
|
+
_props$dataFieldArea = props.dataFieldArea,
|
60
|
+
dataFieldArea = _props$dataFieldArea === void 0 ? 'column' : _props$dataFieldArea,
|
61
|
+
dataSource = props.dataSource,
|
62
|
+
_props$disabled = props.disabled,
|
63
|
+
disabled = _props$disabled === void 0 ? false : _props$disabled,
|
64
|
+
elementAttr = props.elementAttr,
|
65
|
+
encodeHtml = props.encodeHtml,
|
66
|
+
_props$hideEmptySumma = props.hideEmptySummaryCells,
|
67
|
+
hideEmptySummaryCells = _props$hideEmptySumma === void 0 ? false : _props$hideEmptySumma,
|
68
|
+
hint = props.hint,
|
69
|
+
_props$rowHeaderLayou = props.rowHeaderLayout,
|
70
|
+
rowHeaderLayout = _props$rowHeaderLayou === void 0 ? 'standard' : _props$rowHeaderLayou,
|
71
|
+
_props$rtlEnabled = props.rtlEnabled,
|
72
|
+
rtlEnabled = _props$rtlEnabled === void 0 ? false : _props$rtlEnabled,
|
73
|
+
_props$showBorders = props.showBorders,
|
74
|
+
showBorders = _props$showBorders === void 0 ? true : _props$showBorders,
|
75
|
+
_props$showRowTotals = props.showRowTotals,
|
76
|
+
showRowTotals = _props$showRowTotals === void 0 ? true : _props$showRowTotals,
|
77
|
+
_props$showTotalsPrio = props.showTotalsPrior,
|
78
|
+
showTotalsPrior = _props$showTotalsPrio === void 0 ? 'none' : _props$showTotalsPrio,
|
79
|
+
_props$tabIndex = props.tabIndex,
|
80
|
+
tabIndex = _props$tabIndex === void 0 ? 0 : _props$tabIndex,
|
81
|
+
_props$visible = props.visible,
|
82
|
+
visible = _props$visible === void 0 ? true : _props$visible,
|
83
|
+
_props$wordWrapEnable = props.wordWrapEnabled,
|
84
|
+
wordWrapEnabled = _props$wordWrapEnable === void 0 ? false : _props$wordWrapEnable,
|
85
|
+
_props$customExcelBut = props.customExcelButton,
|
86
|
+
customExcelButton = _props$customExcelBut === void 0 ? false : _props$customExcelBut,
|
87
|
+
onCellClick = props.onCellClick,
|
88
|
+
onCellPrepared = props.onCellPrepared,
|
89
|
+
onContentReady = props.onContentReady,
|
90
|
+
onContextMenuPreparing = props.onContextMenuPreparing,
|
91
|
+
onDisposing = props.onDisposing,
|
92
|
+
onExporting = props.onExporting,
|
93
|
+
onInitialized = props.onInitialized,
|
94
|
+
onOptionChanged = props.onOptionChanged;
|
95
|
+
var _useState = (0, _react.useState)(0),
|
96
|
+
_useState2 = _slicedToArray(_useState, 2),
|
97
|
+
width = _useState2[0],
|
98
|
+
setWidth = _useState2[1];
|
99
|
+
var _useState3 = (0, _react.useState)(0),
|
100
|
+
_useState4 = _slicedToArray(_useState3, 2),
|
101
|
+
height = _useState4[0],
|
102
|
+
setHeight = _useState4[1];
|
103
|
+
var _useState5 = (0, _react.useState)(0),
|
104
|
+
_useState6 = _slicedToArray(_useState5, 2),
|
105
|
+
columnIndex = _useState6[0],
|
106
|
+
setColumnIndex = _useState6[1];
|
107
|
+
var _useState7 = (0, _react.useState)(dataSource),
|
108
|
+
_useState8 = _slicedToArray(_useState7, 2),
|
109
|
+
gridDataSource = _useState8[0],
|
110
|
+
setGridDataSource = _useState8[1];
|
111
|
+
var $tableRef = (0, _react.useRef)(null);
|
112
|
+
var excelBorder = {
|
113
|
+
style: 'thin',
|
114
|
+
color: {
|
115
|
+
argb: 'FF7E7E7E'
|
116
|
+
}
|
117
|
+
};
|
118
|
+
(0, _react.useImperativeHandle)(ref, function () {
|
119
|
+
return {
|
120
|
+
exportToExcel: exportToExcel
|
121
|
+
};
|
122
|
+
});
|
123
|
+
|
124
|
+
/**
|
125
|
+
* 그리드 사이즈 재조정
|
126
|
+
* @returns 그리드 사이즈
|
127
|
+
*/
|
128
|
+
var getGridSize = function getGridSize() {
|
129
|
+
var _wrapper$clientWidth;
|
130
|
+
var wrapper = document.querySelector('.diag-table-wrapper');
|
131
|
+
var gap = 10;
|
132
|
+
setWidth((_wrapper$clientWidth = wrapper === null || wrapper === void 0 ? void 0 : wrapper.clientWidth) !== null && _wrapper$clientWidth !== void 0 ? _wrapper$clientWidth : 0);
|
133
|
+
setHeight(wrapper ? wrapper.clientHeight - gap : 0);
|
134
|
+
window.addEventListener('resize', function () {
|
135
|
+
var _wrapper$clientWidth2;
|
136
|
+
setWidth((_wrapper$clientWidth2 = wrapper === null || wrapper === void 0 ? void 0 : wrapper.clientWidth) !== null && _wrapper$clientWidth2 !== void 0 ? _wrapper$clientWidth2 : 0);
|
137
|
+
setHeight(wrapper ? wrapper.clientHeight - gap : 0);
|
138
|
+
});
|
139
|
+
return {
|
140
|
+
width: width,
|
141
|
+
height: height
|
142
|
+
};
|
143
|
+
};
|
144
|
+
|
145
|
+
/**
|
146
|
+
* 'Total' 을 한글로 변경
|
147
|
+
* @param e devextreme CellPreparedEvent
|
148
|
+
*/
|
149
|
+
var changeTotalText = function changeTotalText(e) {
|
150
|
+
var _e$cell;
|
151
|
+
if (!e.cellElement) {
|
152
|
+
return;
|
153
|
+
}
|
154
|
+
if (((_e$cell = e.cell) === null || _e$cell === void 0 ? void 0 : _e$cell.type) === 'T') {
|
155
|
+
var _e$cell$text;
|
156
|
+
var text = (_e$cell$text = e.cell.text) === null || _e$cell$text === void 0 ? void 0 : _e$cell$text.replace('Total', '합계');
|
157
|
+
e.cellElement.innerHTML = "<span>".concat(text, "</span>");
|
158
|
+
}
|
159
|
+
};
|
160
|
+
|
161
|
+
/**
|
162
|
+
* null값을 하이픈으로 모두 변경
|
163
|
+
* @param e devextreme CellPreparedEvent
|
164
|
+
*/
|
165
|
+
var changeNullToHipen = function changeNullToHipen(e) {
|
166
|
+
var _e$cell2;
|
167
|
+
if (!convertNullToHipen) {
|
168
|
+
return;
|
169
|
+
}
|
170
|
+
if (e.area === 'data' && ((_e$cell2 = e.cell) === null || _e$cell2 === void 0 ? void 0 : _e$cell2.text) === null && e.cellElement) {
|
171
|
+
e.cellElement.innerHTML = '<span class="text-color">-</span>';
|
172
|
+
}
|
173
|
+
};
|
174
|
+
|
175
|
+
/**
|
176
|
+
* '0', '0.0%' 를 하이픈으로 모두 변경
|
177
|
+
* @param e devextreme CellPreparedEvent
|
178
|
+
*/
|
179
|
+
var changeZeroToHipen = function changeZeroToHipen(e) {
|
180
|
+
var _e$cell3, _e$cell4, _e$cell5;
|
181
|
+
if (!convertZeroToHipen) {
|
182
|
+
return;
|
183
|
+
}
|
184
|
+
if (e.area === 'data' && (((_e$cell3 = e.cell) === null || _e$cell3 === void 0 ? void 0 : _e$cell3.text) === '0' || ((_e$cell4 = e.cell) === null || _e$cell4 === void 0 ? void 0 : _e$cell4.text) === '0.0%' || ((_e$cell5 = e.cell) === null || _e$cell5 === void 0 ? void 0 : _e$cell5.text) === '') && e.cellElement) {
|
185
|
+
e.cellElement.innerHTML = '<span class="text-color">-</span>';
|
186
|
+
}
|
187
|
+
};
|
188
|
+
|
189
|
+
/**
|
190
|
+
* 테이블 헤더에 colspan, rowspan 한 HTMLElement 정보 반환
|
191
|
+
* @param groupField 사용자가 작성한 그룹 정보
|
192
|
+
* @return
|
193
|
+
*/
|
194
|
+
var makeColspan = function makeColspan(group, index, isLast) {
|
195
|
+
var td = document.createElement('td');
|
196
|
+
var text = group.groupCaption;
|
197
|
+
if (group.depth === 1) {
|
198
|
+
text = "".concat(group.groupCaption);
|
199
|
+
}
|
200
|
+
td.setAttribute('colspan', group.colspan.toString());
|
201
|
+
td.setAttribute('class', 'dx-row-total dx-grand-total dx-planit-colspan');
|
202
|
+
if (isLast && index === 0) {
|
203
|
+
td.setAttribute('style', 'border-bottom: 0; border-right: 0');
|
204
|
+
} else if (isLast && index !== 0) {
|
205
|
+
td.setAttribute('style', 'border-right: 0');
|
206
|
+
} else if (!isLast && index === 0) {
|
207
|
+
td.setAttribute('style', 'border-bottom: 0');
|
208
|
+
}
|
209
|
+
td.innerHTML = "<div>".concat(text, "</div>");
|
210
|
+
return td;
|
211
|
+
};
|
212
|
+
|
213
|
+
/**
|
214
|
+
* 그룹 필드 데이터 유효성 검증용 데이터 생성
|
215
|
+
* @param groupField
|
216
|
+
* @returns
|
217
|
+
*/
|
218
|
+
var makeCheckGroupData = function makeCheckGroupData(groupField) {
|
219
|
+
var data = {};
|
220
|
+
groupField === null || groupField === void 0 ? void 0 : groupField.forEach(function (group) {
|
221
|
+
if (data[group.depth]) {
|
222
|
+
data[group.depth] += group.colspan;
|
223
|
+
} else {
|
224
|
+
data[group.depth] = group.colspan;
|
225
|
+
}
|
226
|
+
});
|
227
|
+
return data;
|
228
|
+
};
|
229
|
+
|
230
|
+
/**
|
231
|
+
* GroupField 데이터 검증
|
232
|
+
* @param 사용자가 설정한 그룹 필드 정보
|
233
|
+
* @returns 데이터 검증 결과
|
234
|
+
*/
|
235
|
+
var isCheckGroupField = function isCheckGroupField(groupField) {
|
236
|
+
var map = makeCheckGroupData(groupField);
|
237
|
+
for (var _i2 = 0, _Object$keys = Object.keys(map); _i2 < _Object$keys.length; _i2++) {
|
238
|
+
var depth = _Object$keys[_i2];
|
239
|
+
if (map[depth] !== columnIndex + 1) {
|
240
|
+
console.error('그룹 데이터의 children 숫자가 columnIndex와 맞지 않습니다. 다시 한 번 확인 바랍니다.');
|
241
|
+
}
|
242
|
+
}
|
243
|
+
return true;
|
244
|
+
};
|
245
|
+
|
246
|
+
/**
|
247
|
+
* Grand Total 셀 정보 저장
|
248
|
+
* @param e
|
249
|
+
*/
|
250
|
+
var setTotalElementInfo = function setTotalElementInfo(e) {
|
251
|
+
var _e$cell6, _e$cell7, _e$cellElement;
|
252
|
+
if (!(groupField !== null && groupField !== void 0 && groupField.length) || ((_e$cell6 = e.cell) === null || _e$cell6 === void 0 ? void 0 : _e$cell6.type) !== 'GT' || ((_e$cell7 = e.cell) === null || _e$cell7 === void 0 ? void 0 : _e$cell7.text) !== 'Grand Total') {
|
253
|
+
return;
|
254
|
+
}
|
255
|
+
(_e$cellElement = e.cellElement) === null || _e$cellElement === void 0 ? void 0 : _e$cellElement.classList.add(grandTotalCssNm);
|
256
|
+
};
|
257
|
+
|
258
|
+
/**
|
259
|
+
* cell의 columnIndex 최대값 저장
|
260
|
+
* @param e
|
261
|
+
*/
|
262
|
+
var setMaxColumIndex = function setMaxColumIndex(e) {
|
263
|
+
if (!e.columnIndex) {
|
264
|
+
return;
|
265
|
+
}
|
266
|
+
if (e.columnIndex > columnIndex) {
|
267
|
+
setColumnIndex(e.columnIndex);
|
268
|
+
}
|
269
|
+
};
|
270
|
+
|
271
|
+
/**
|
272
|
+
* groupField depth의 유니크한 배열 구하기
|
273
|
+
* @param group
|
274
|
+
* @param arr
|
275
|
+
* @returns
|
276
|
+
*/
|
277
|
+
var getGroupDepth = function getGroupDepth(group, arr) {
|
278
|
+
var groupData = group.slice();
|
279
|
+
var set = new Set(groupData.map(function (group) {
|
280
|
+
return group.depth;
|
281
|
+
}));
|
282
|
+
return Array.from(set).sort(function compare(a, b) {
|
283
|
+
if (a > b) {
|
284
|
+
return arr === 'asc' ? -1 : 1;
|
285
|
+
}
|
286
|
+
if (a < b) {
|
287
|
+
return arr === 'asc' ? 1 : -1;
|
288
|
+
}
|
289
|
+
return 0;
|
290
|
+
});
|
291
|
+
};
|
292
|
+
|
293
|
+
/**
|
294
|
+
* 현재 depth에 맞는 그룹 필드 정보 반환
|
295
|
+
* @param group
|
296
|
+
* @param depth
|
297
|
+
* @returns
|
298
|
+
*/
|
299
|
+
var getCurrentGroup = function getCurrentGroup(group, depth) {
|
300
|
+
return group.filter(function (gr) {
|
301
|
+
return gr.depth === depth;
|
302
|
+
});
|
303
|
+
};
|
304
|
+
|
305
|
+
/**
|
306
|
+
* 테이블 헤더(DOM)에 colspan 적용된 테이블 삽입
|
307
|
+
*/
|
308
|
+
var insertRowHeaderGroup = function insertRowHeaderGroup() {
|
309
|
+
var _thead$previousSiblin;
|
310
|
+
if (!(groupField !== null && groupField !== void 0 && groupField.length)) {
|
311
|
+
return;
|
312
|
+
}
|
313
|
+
isCheckGroupField(groupField);
|
314
|
+
var totalElement = document.querySelector('.' + grandTotalCssNm);
|
315
|
+
var targetElement = totalElement === null || totalElement === void 0 ? void 0 : totalElement.parentNode;
|
316
|
+
var thead = targetElement === null || targetElement === void 0 ? void 0 : targetElement.parentNode;
|
317
|
+
if (!targetElement || !thead) {
|
318
|
+
return;
|
319
|
+
}
|
320
|
+
var firstChild = thead === null || thead === void 0 ? void 0 : thead.firstChild;
|
321
|
+
if (!firstChild) {
|
322
|
+
return;
|
323
|
+
}
|
324
|
+
totalElement.innerHTML = '';
|
325
|
+
totalElement.setAttribute('style', 'padding: 0; border: 0');
|
326
|
+
var colgroup = (_thead$previousSiblin = thead.previousSibling) === null || _thead$previousSiblin === void 0 ? void 0 : _thead$previousSiblin.cloneNode(true);
|
327
|
+
var groupData = groupField.slice();
|
328
|
+
var depth = getGroupDepth(groupData, 'asc');
|
329
|
+
var table = document.createElement('table');
|
330
|
+
depth.forEach(function (dep, index) {
|
331
|
+
var groupInfo = getCurrentGroup(groupData, dep);
|
332
|
+
var tr = document.createElement('tr');
|
333
|
+
groupInfo.forEach(function (group, cellIndex) {
|
334
|
+
var isLast = cellIndex === groupInfo.length - 1 ? true : false;
|
335
|
+
tr.appendChild(makeColspan(group, index, isLast));
|
336
|
+
});
|
337
|
+
table.prepend(tr);
|
338
|
+
});
|
339
|
+
table.prepend(colgroup);
|
340
|
+
totalElement.appendChild(table);
|
341
|
+
};
|
342
|
+
|
343
|
+
/**
|
344
|
+
* Devextreme의 dateController columnInfo에 그룹 정보 삽입
|
345
|
+
* @param group
|
346
|
+
* @returns
|
347
|
+
*/
|
348
|
+
var makeDataControllerColumnGroup = function makeDataControllerColumnGroup(group) {
|
349
|
+
var groupData = group.slice();
|
350
|
+
var depth = getGroupDepth(groupData, 'desc');
|
351
|
+
return depth.map(function (dep) {
|
352
|
+
var groupInfo = getCurrentGroup(groupData, dep);
|
353
|
+
return groupInfo.map(function (group) {
|
354
|
+
return {
|
355
|
+
colspan: group.colspan,
|
356
|
+
text: group.groupCaption,
|
357
|
+
type: 'GT'
|
358
|
+
};
|
359
|
+
});
|
360
|
+
});
|
361
|
+
};
|
362
|
+
|
363
|
+
/**
|
364
|
+
* 사용자가 입력한 컬러 조건을 { standard: string; condition: string } 형식으로 변경 반환
|
365
|
+
* @param condition 사용자 입력 컬러 조건식 ex) '>= 100'
|
366
|
+
* @returns
|
367
|
+
*/
|
368
|
+
var makeSplitCondtion = function makeSplitCondtion(condition) {
|
369
|
+
var newCondition = {
|
370
|
+
standard: '',
|
371
|
+
condition: ''
|
372
|
+
};
|
373
|
+
_toConsumableArray(condition).forEach(function (cond) {
|
374
|
+
if (Number.isNaN(parseFloat(cond))) {
|
375
|
+
newCondition.condition += cond;
|
376
|
+
} else {
|
377
|
+
newCondition.standard += cond;
|
378
|
+
}
|
379
|
+
});
|
380
|
+
return newCondition;
|
381
|
+
};
|
382
|
+
|
383
|
+
/**
|
384
|
+
* 데이터에 색상 적용
|
385
|
+
* @param e onCellPrepared 이벤트
|
386
|
+
* @returns
|
387
|
+
*/
|
388
|
+
var makeColorAtPercent = function makeColorAtPercent(e) {
|
389
|
+
if (!dataColor || !e.cellElement) {
|
390
|
+
return;
|
391
|
+
}
|
392
|
+
dataColor.forEach(function (color) {
|
393
|
+
var _e$cell8, _e$cell8$format;
|
394
|
+
if (e.cell.value === null) {
|
395
|
+
return;
|
396
|
+
}
|
397
|
+
if (((_e$cell8 = e.cell) === null || _e$cell8 === void 0 ? void 0 : (_e$cell8$format = _e$cell8.format) === null || _e$cell8$format === void 0 ? void 0 : _e$cell8$format.type) === color.format && !Number.isNaN(e.cell.value)) {
|
398
|
+
var standardData = makeSplitCondtion(color.condition.replace(/(\s*)/g, ''));
|
399
|
+
var rate = color.format === 'percent' ? 0.01 : 1;
|
400
|
+
var condition = false;
|
401
|
+
switch (standardData.condition) {
|
402
|
+
case '>':
|
403
|
+
condition = e.cell.value > parseFloat(standardData.standard) * rate;
|
404
|
+
break;
|
405
|
+
case '>=':
|
406
|
+
condition = e.cell.value >= parseFloat(standardData.standard) * rate;
|
407
|
+
break;
|
408
|
+
case '<':
|
409
|
+
condition = e.cell.value < parseFloat(standardData.standard) * rate;
|
410
|
+
break;
|
411
|
+
case '<=':
|
412
|
+
condition = e.cell.value <= parseFloat(standardData.standard) * rate;
|
413
|
+
break;
|
414
|
+
}
|
415
|
+
if (condition && !(e.cell.value === 0 && convertZeroToHipen)) {
|
416
|
+
e.cellElement.style.color = color.color;
|
417
|
+
}
|
418
|
+
}
|
419
|
+
});
|
420
|
+
};
|
421
|
+
|
422
|
+
/**
|
423
|
+
* 그리드 데이터 정합성 체크. 데이터 잘못되어 있으면 에러 발생
|
424
|
+
* @param dataSource
|
425
|
+
*/
|
426
|
+
var checkDataSource = function checkDataSource(dataSource) {
|
427
|
+
var isColumns = dataSource._fields.findIndex(function (field) {
|
428
|
+
return field.area === 'column';
|
429
|
+
});
|
430
|
+
var isRows = dataSource._fields.findIndex(function (field) {
|
431
|
+
return field.area === 'row';
|
432
|
+
});
|
433
|
+
var isDatas = dataSource._fields.findIndex(function (field) {
|
434
|
+
return field.area === 'data';
|
435
|
+
});
|
436
|
+
if (isColumns > -1) {
|
437
|
+
throw Error('DxPlanitTreeGrid는 column이 존재하는 형식의 pivot grid에는 사용할 수 없습니다.');
|
438
|
+
}
|
439
|
+
if (isRows === -1 || isDatas === -1) {
|
440
|
+
throw Error('DxPlanitTreeGrid 데이터는 row와 data가 반드시 존재해야 합니다.');
|
441
|
+
}
|
442
|
+
};
|
443
|
+
|
444
|
+
/**
|
445
|
+
* 그리드 펼침 정보 세션스토리지 리셋
|
446
|
+
*/
|
447
|
+
var resetSession = function resetSession() {
|
448
|
+
sessionStorage.removeItem('dx-vera-pivotgrid-storing');
|
449
|
+
};
|
450
|
+
|
451
|
+
/**
|
452
|
+
* 엑셀 export 명령
|
453
|
+
* @param fileName 저장하고자 하는 엑셀파일명
|
454
|
+
*/
|
455
|
+
var exportToExcel = function exportToExcel(fileName) {
|
456
|
+
setTimeout(function () {
|
457
|
+
var _$tableRef$current;
|
458
|
+
return exportToExcelAction((_$tableRef$current = $tableRef.current) === null || _$tableRef$current === void 0 ? void 0 : _$tableRef$current.instance, fileName);
|
459
|
+
});
|
460
|
+
};
|
461
|
+
|
462
|
+
/**
|
463
|
+
* devextreme component 정보의 dataController의 columnInfo에 사용자가 설정한 groupFIled 정보 병합
|
464
|
+
* @param component devextreme component
|
465
|
+
* @returns devextreme component
|
466
|
+
*/
|
467
|
+
var convertDataControllerColumnsInfo = function convertDataControllerColumnsInfo(component) {
|
468
|
+
var arr = [];
|
469
|
+
var columnInfo = component._dataController._columnsInfo.forEach(function (column) {
|
470
|
+
var newColumn = column.slice();
|
471
|
+
if (groupField && newColumn.length === 1 && newColumn[0].type === 'GT' && newColumn[0].text === 'Grand Total') {
|
472
|
+
arr.push.apply(arr, _toConsumableArray(makeDataControllerColumnGroup(groupField)));
|
473
|
+
} else {
|
474
|
+
arr.push(newColumn);
|
475
|
+
}
|
476
|
+
});
|
477
|
+
component._dataController._columnsInfo = arr;
|
478
|
+
return component;
|
479
|
+
};
|
480
|
+
|
481
|
+
/**
|
482
|
+
* 엑셀 export
|
483
|
+
* @param e
|
484
|
+
*/
|
485
|
+
var exportToExcelAction = function exportToExcelAction(e, fileName) {
|
486
|
+
var newComponent = convertDataControllerColumnsInfo(e);
|
487
|
+
var workbook = new _exceljs.Workbook();
|
488
|
+
var worksheet = workbook.addWorksheet(fileName);
|
489
|
+
(0, _excel_exporter.exportPivotGrid)({
|
490
|
+
component: newComponent,
|
491
|
+
worksheet: worksheet,
|
492
|
+
customizeCell: function customizeCell(_ref) {
|
493
|
+
var excelCell = _ref.excelCell;
|
494
|
+
var borderStyle = excelBorder;
|
495
|
+
excelCell.border = {
|
496
|
+
bottom: borderStyle,
|
497
|
+
left: borderStyle,
|
498
|
+
right: borderStyle,
|
499
|
+
top: borderStyle
|
500
|
+
};
|
501
|
+
}
|
502
|
+
}).then(function () {
|
503
|
+
workbook.xlsx.writeBuffer().then(function (buffer) {
|
504
|
+
(0, _fileSaver.default)(new Blob([buffer], {
|
505
|
+
type: 'application/octet-stream'
|
506
|
+
}), fileName + '.xlsx');
|
507
|
+
});
|
508
|
+
});
|
509
|
+
e.cancel = true;
|
510
|
+
};
|
511
|
+
|
512
|
+
/**
|
513
|
+
* devextreme CellPreparedEvent 이벤트 실행
|
514
|
+
* @param e
|
515
|
+
*/
|
516
|
+
var onCellPreparedChild = function onCellPreparedChild(e) {
|
517
|
+
makeColorAtPercent(e);
|
518
|
+
setTotalElementInfo(e);
|
519
|
+
setMaxColumIndex(e);
|
520
|
+
changeTotalText(e);
|
521
|
+
changeNullToHipen(e);
|
522
|
+
changeZeroToHipen(e);
|
523
|
+
return onCellPrepared ? onCellPrepared(e) : undefined;
|
524
|
+
};
|
525
|
+
|
526
|
+
/**
|
527
|
+
* devextreme Raise Event
|
528
|
+
*/
|
529
|
+
var onContentReadyChild = function onContentReadyChild(e) {
|
530
|
+
setTimeout(function () {
|
531
|
+
return insertRowHeaderGroup();
|
532
|
+
}, 0);
|
533
|
+
getGridSize();
|
534
|
+
return onContentReady ? onContentReady(e) : undefined;
|
535
|
+
};
|
536
|
+
var onCellClickChild = function onCellClickChild(e) {
|
537
|
+
return onCellClick ? onCellClick(e) : undefined;
|
538
|
+
};
|
539
|
+
var onContextMenuPreparingChild = function onContextMenuPreparingChild(e) {
|
540
|
+
return onContextMenuPreparing ? onContextMenuPreparing(e) : undefined;
|
541
|
+
};
|
542
|
+
var onDisposingChild = function onDisposingChild(e) {
|
543
|
+
return onDisposing ? onDisposing(e) : undefined;
|
544
|
+
};
|
545
|
+
var onExportingChild = function onExportingChild(e) {
|
546
|
+
return onExporting ? onExporting(e) : undefined;
|
547
|
+
};
|
548
|
+
var onInitializedChild = function onInitializedChild(e) {
|
549
|
+
return onInitialized ? onInitialized(e) : undefined;
|
550
|
+
};
|
551
|
+
var onOptionChangedChild = function onOptionChangedChild(e) {
|
552
|
+
return onOptionChanged ? onOptionChanged(e) : undefined;
|
553
|
+
};
|
554
|
+
(0, _react.useEffect)(function () {
|
555
|
+
if (customExcelButton) {}
|
556
|
+
}, [customExcelButton]);
|
557
|
+
(0, _react.useEffect)(function () {
|
558
|
+
setGridDataSource(dataSource);
|
559
|
+
checkDataSource(dataSource);
|
560
|
+
resetSession();
|
561
|
+
}, [dataSource]);
|
562
|
+
return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(_loadPanel.LoadPanel, {
|
563
|
+
position: {
|
564
|
+
of: id
|
565
|
+
}
|
566
|
+
}), /*#__PURE__*/React.createElement(_pivotGrid.default, {
|
567
|
+
id: id,
|
568
|
+
ref: $tableRef,
|
569
|
+
dataSource: gridDataSource,
|
570
|
+
showColumnTotals: false,
|
571
|
+
showColumnGrandTotals: true,
|
572
|
+
showRowGrandTotals: false,
|
573
|
+
width: width,
|
574
|
+
height: height,
|
575
|
+
allowExpandAll: allowExpandAll,
|
576
|
+
allowFiltering: allowFiltering,
|
577
|
+
allowSorting: allowSorting,
|
578
|
+
allowSortingBySummary: allowSortingBySummary,
|
579
|
+
dataFieldArea: dataFieldArea,
|
580
|
+
disabled: disabled,
|
581
|
+
elementAttr: elementAttr,
|
582
|
+
encodeHtml: encodeHtml,
|
583
|
+
hideEmptySummaryCells: hideEmptySummaryCells,
|
584
|
+
hint: hint,
|
585
|
+
rowHeaderLayout: rowHeaderLayout,
|
586
|
+
rtlEnabled: rtlEnabled,
|
587
|
+
showBorders: showBorders,
|
588
|
+
showRowTotals: showRowTotals,
|
589
|
+
showTotalsPrior: showTotalsPrior,
|
590
|
+
tabIndex: tabIndex,
|
591
|
+
visible: visible,
|
592
|
+
wordWrapEnabled: wordWrapEnabled,
|
593
|
+
onCellClick: onCellClickChild,
|
594
|
+
onContentReady: onContentReadyChild,
|
595
|
+
onCellPrepared: onCellPreparedChild,
|
596
|
+
onContextMenuPreparing: onContextMenuPreparingChild,
|
597
|
+
onDisposing: onDisposingChild,
|
598
|
+
onExporting: onExportingChild,
|
599
|
+
onInitialized: onInitializedChild,
|
600
|
+
onOptionChanged: onOptionChangedChild
|
601
|
+
}, /*#__PURE__*/React.createElement(_dataGrid.StateStoring, {
|
602
|
+
enabled: stateStoringKey === null || stateStoringKey === void 0 ? void 0 : stateStoringKey.length,
|
603
|
+
type: "sessionStorage",
|
604
|
+
storageKey: stateStoringKey
|
605
|
+
}), /*#__PURE__*/React.createElement(_pivotGrid.FieldChooser, {
|
606
|
+
enabled: false
|
607
|
+
})));
|
608
|
+
});
|
609
|
+
var _default = DxPlanitTreeGrid;
|
610
|
+
exports.default = _default;
|
package/dist/index.js
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
4
|
+
value: true
|
5
|
+
});
|
6
|
+
exports.default = void 0;
|
7
|
+
var _DxPlanitTreeGrid = _interopRequireDefault(require("./DxPlanitTreeGrid"));
|
8
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
9
|
+
var _default = _DxPlanitTreeGrid.default;
|
10
|
+
exports.default = _default;
|
package/dist/type.js
ADDED
package/package.json
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
{
|
2
2
|
"name": "devextreme-planit-treegrid-react",
|
3
|
-
"version": "0.2.
|
3
|
+
"version": "0.2.4",
|
4
4
|
"description": "Devextreme의 DxPivotGrid를 Tree Grid처럼 보여주는 Wrapper입니다.",
|
5
|
-
"main": "dist/index.
|
6
|
-
"module": "dist/index.
|
5
|
+
"main": "dist/index.js",
|
6
|
+
"module": "dist/index.js",
|
7
7
|
"files": [
|
8
8
|
"dist",
|
9
9
|
"README.md"
|
@@ -20,7 +20,7 @@
|
|
20
20
|
"test": "react-scripts test",
|
21
21
|
"eject": "react-scripts eject",
|
22
22
|
"clean": "rimraf dist",
|
23
|
-
"compile": "
|
23
|
+
"compile": "npm run clean && cross-env NODE_ENV=production babel src/lib --out-dir dist --copy-files --extensions .ts,.tsx"
|
24
24
|
},
|
25
25
|
"babel": {
|
26
26
|
"presets": [
|
@@ -39,25 +39,30 @@
|
|
39
39
|
"not op_mini all"
|
40
40
|
],
|
41
41
|
"dependencies": {
|
42
|
-
"@babel/polyfill": "^7.12.1",
|
43
42
|
"devextreme": "^22.1.6",
|
44
|
-
"devextreme-planit-treegrid-react": "^0.2.
|
43
|
+
"devextreme-planit-treegrid-react": "^0.2.3",
|
45
44
|
"devextreme-react": "^22.1.6",
|
46
45
|
"exceljs": "^4.3.0",
|
47
46
|
"file-saver": "^2.0.5",
|
48
|
-
"react": "^18.2.0",
|
49
|
-
"react-dom": "^18.2.0",
|
50
|
-
"react-router-dom": "^6.4.3",
|
51
|
-
"react-scripts": "^5.0.1",
|
52
47
|
"typescript": "^4.9.3",
|
53
48
|
"uuid": "^9.0.0",
|
54
49
|
"web-vitals": "^2.1.4"
|
55
50
|
},
|
51
|
+
"peerDependencies": {
|
52
|
+
"react": "^18.2.0",
|
53
|
+
"react-dom": "^18.2.0",
|
54
|
+
"react-router-dom": "^6.4.3",
|
55
|
+
"react-scripts": "^5.0.1"
|
56
|
+
},
|
56
57
|
"devDependencies": {
|
57
|
-
"@babel/cli": "^7.
|
58
|
+
"@babel/cli": "^7.0.0",
|
58
59
|
"@babel/core": "^7.20.5",
|
60
|
+
"@babel/node": "^7.20.5",
|
61
|
+
"@babel/polyfill": "^7.12.1",
|
59
62
|
"@babel/preset-env": "^7.20.2",
|
60
63
|
"@babel/preset-react": "^7.18.6",
|
64
|
+
"@babel/preset-typescript": "^7.18.6",
|
65
|
+
"@babel/register": "^7.18.9",
|
61
66
|
"@testing-library/jest-dom": "^5.16.5",
|
62
67
|
"@testing-library/react": "^13.4.0",
|
63
68
|
"@testing-library/user-event": "^13.5.0",
|
@@ -65,7 +70,9 @@
|
|
65
70
|
"@types/react": "^18.0.25",
|
66
71
|
"@types/react-dom": "^18.0.9",
|
67
72
|
"@types/uuid": "^8.3.4",
|
68
|
-
"babel-
|
73
|
+
"babel-core": "^7.0.0-bridge.0",
|
74
|
+
"babel-loader": "^8.0.0-beta.6",
|
75
|
+
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
69
76
|
"cross-env": "^7.0.3",
|
70
77
|
"css-loader": "^6.7.2",
|
71
78
|
"eslint": "^8.27.0",
|
@@ -1,580 +0,0 @@
|
|
1
|
-
import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
|
2
|
-
|
3
|
-
import { LoadPanel } from 'devextreme-react/load-panel';
|
4
|
-
|
5
|
-
import PivotGrid, { FieldChooser } from 'devextreme-react/pivot-grid';
|
6
|
-
import { StateStoring } from 'devextreme-react/data-grid';
|
7
|
-
import DevExpress from 'devextreme';
|
8
|
-
import { ColumnField, IColorInfo, IGroupField, Props } from './type';
|
9
|
-
import { exportPivotGrid } from 'devextreme/excel_exporter';
|
10
|
-
import { Workbook } from 'exceljs';
|
11
|
-
import saveAs from 'file-saver';
|
12
|
-
import PivotGridDataSource from 'devextreme/ui/pivot_grid/data_source';
|
13
|
-
|
14
|
-
/**
|
15
|
-
* devextreme pivotgrid Configrations 중 사용 불가 항목 : id, width, height, showColumnGrandTotals, showColumnTotals, showRowGrandTotals, FieldChooser
|
16
|
-
* devextreme pivotgrid Configrations 중 사용 방법 변경 항목 : stateStoring, Export
|
17
|
-
* onExported, onFileSaving 이벤트 사용하지 않음.
|
18
|
-
*/
|
19
|
-
/**
|
20
|
-
* todoList:
|
21
|
-
* 2) columIndex 초기화 기능이 있어야 함(column 개수 변할 때)
|
22
|
-
* 3) 헤더에 테이블 삽입되면서 그리드 크기가 늘어남. height에 그리드 크기 늘어난 만큼 반영되어야 함.
|
23
|
-
*/
|
24
|
-
|
25
|
-
const grandTotalCssNm = 'data-grand-total';
|
26
|
-
|
27
|
-
const DxPlanitTreeGrid = forwardRef(
|
28
|
-
(props: Props, ref: any): JSX.Element => {
|
29
|
-
const {
|
30
|
-
id = 'dx-planit-vera-pivotgrid-id',
|
31
|
-
groupField,
|
32
|
-
dataColor,
|
33
|
-
convertNullToHipen = true,
|
34
|
-
convertZeroToHipen = true,
|
35
|
-
stateStoringKey = '',
|
36
|
-
allowExpandAll = false,
|
37
|
-
allowFiltering = false,
|
38
|
-
allowSorting = false,
|
39
|
-
allowSortingBySummary = false,
|
40
|
-
dataFieldArea = 'column',
|
41
|
-
dataSource,
|
42
|
-
disabled = false,
|
43
|
-
elementAttr,
|
44
|
-
encodeHtml,
|
45
|
-
hideEmptySummaryCells = false,
|
46
|
-
hint,
|
47
|
-
rowHeaderLayout = 'standard',
|
48
|
-
rtlEnabled = false,
|
49
|
-
showBorders = true,
|
50
|
-
showRowTotals = true,
|
51
|
-
showTotalsPrior = 'none',
|
52
|
-
tabIndex = 0,
|
53
|
-
visible = true,
|
54
|
-
wordWrapEnabled = false,
|
55
|
-
customExcelButton = false,
|
56
|
-
onCellClick,
|
57
|
-
onCellPrepared,
|
58
|
-
onContentReady,
|
59
|
-
onContextMenuPreparing,
|
60
|
-
onDisposing,
|
61
|
-
onExporting,
|
62
|
-
onInitialized,
|
63
|
-
onOptionChanged,
|
64
|
-
} = props;
|
65
|
-
|
66
|
-
const [width, setWidth] = useState(0);
|
67
|
-
const [height, setHeight] = useState(0);
|
68
|
-
const [columnIndex, setColumnIndex] = useState(0);
|
69
|
-
const [gridDataSource, setGridDataSource] = useState<PivotGridDataSource>(dataSource);
|
70
|
-
|
71
|
-
const $tableRef = useRef<PivotGrid>(null);
|
72
|
-
const excelBorder = { style: 'thin', color: { argb: 'FF7E7E7E' } };
|
73
|
-
|
74
|
-
useImperativeHandle(ref, () => ({
|
75
|
-
exportToExcel,
|
76
|
-
}));
|
77
|
-
|
78
|
-
/**
|
79
|
-
* 그리드 사이즈 재조정
|
80
|
-
* @returns 그리드 사이즈
|
81
|
-
*/
|
82
|
-
const getGridSize = (): { width: number; height: number } => {
|
83
|
-
const wrapper = document.querySelector('.diag-table-wrapper');
|
84
|
-
const gap = 10;
|
85
|
-
setWidth(wrapper?.clientWidth ?? 0);
|
86
|
-
setHeight(wrapper ? wrapper.clientHeight - gap : 0);
|
87
|
-
|
88
|
-
window.addEventListener('resize', () => {
|
89
|
-
setWidth(wrapper?.clientWidth ?? 0);
|
90
|
-
setHeight(wrapper ? wrapper.clientHeight - gap : 0);
|
91
|
-
});
|
92
|
-
return { width, height };
|
93
|
-
};
|
94
|
-
|
95
|
-
/**
|
96
|
-
* 'Total' 을 한글로 변경
|
97
|
-
* @param e devextreme CellPreparedEvent
|
98
|
-
*/
|
99
|
-
const changeTotalText = (e: DevExpress.ui.dxPivotGrid.CellPreparedEvent): void => {
|
100
|
-
if (!e.cellElement) {
|
101
|
-
return;
|
102
|
-
}
|
103
|
-
if (e.cell?.type === 'T') {
|
104
|
-
const text = e.cell.text?.replace('Total', '합계');
|
105
|
-
e.cellElement.innerHTML = `<span>${text}</span>`;
|
106
|
-
}
|
107
|
-
};
|
108
|
-
|
109
|
-
/**
|
110
|
-
* null값을 하이픈으로 모두 변경
|
111
|
-
* @param e devextreme CellPreparedEvent
|
112
|
-
*/
|
113
|
-
const changeNullToHipen = (e: DevExpress.ui.dxPivotGrid.CellPreparedEvent): void => {
|
114
|
-
if (!convertNullToHipen) {
|
115
|
-
return;
|
116
|
-
}
|
117
|
-
if (e.area === 'data' && e.cell?.text === null && e.cellElement) {
|
118
|
-
e.cellElement.innerHTML = '<span class="text-color">-</span>';
|
119
|
-
}
|
120
|
-
};
|
121
|
-
|
122
|
-
/**
|
123
|
-
* '0', '0.0%' 를 하이픈으로 모두 변경
|
124
|
-
* @param e devextreme CellPreparedEvent
|
125
|
-
*/
|
126
|
-
const changeZeroToHipen = (e: DevExpress.ui.dxPivotGrid.CellPreparedEvent): void => {
|
127
|
-
if (!convertZeroToHipen) {
|
128
|
-
return;
|
129
|
-
}
|
130
|
-
if (e.area === 'data' && (e.cell?.text === '0' || e.cell?.text === '0.0%' || e.cell?.text === '') && e.cellElement) {
|
131
|
-
e.cellElement.innerHTML = '<span class="text-color">-</span>';
|
132
|
-
}
|
133
|
-
};
|
134
|
-
|
135
|
-
/**
|
136
|
-
* 테이블 헤더에 colspan, rowspan 한 HTMLElement 정보 반환
|
137
|
-
* @param groupField 사용자가 작성한 그룹 정보
|
138
|
-
* @return
|
139
|
-
*/
|
140
|
-
const makeColspan = (group: IGroupField, index: number, isLast: boolean): HTMLElement => {
|
141
|
-
const td = document.createElement('td');
|
142
|
-
let text = group.groupCaption;
|
143
|
-
|
144
|
-
if (group.depth === 1) {
|
145
|
-
text = `${group.groupCaption}`;
|
146
|
-
}
|
147
|
-
|
148
|
-
td.setAttribute('colspan', group.colspan.toString());
|
149
|
-
td.setAttribute('class', 'dx-row-total dx-grand-total dx-planit-colspan');
|
150
|
-
|
151
|
-
if (isLast && index === 0) {
|
152
|
-
td.setAttribute('style', 'border-bottom: 0; border-right: 0');
|
153
|
-
} else if (isLast && index !== 0) {
|
154
|
-
td.setAttribute('style', 'border-right: 0');
|
155
|
-
} else if (!isLast && index === 0) {
|
156
|
-
td.setAttribute('style', 'border-bottom: 0');
|
157
|
-
}
|
158
|
-
td.innerHTML = `<div>${text}</div>`;
|
159
|
-
|
160
|
-
return td;
|
161
|
-
};
|
162
|
-
|
163
|
-
/**
|
164
|
-
* 그룹 필드 데이터 유효성 검증용 데이터 생성
|
165
|
-
* @param groupField
|
166
|
-
* @returns
|
167
|
-
*/
|
168
|
-
const makeCheckGroupData = (groupField: IGroupField[]): any => {
|
169
|
-
const data: any = {};
|
170
|
-
|
171
|
-
groupField?.forEach((group: IGroupField) => {
|
172
|
-
if (data[group.depth]) {
|
173
|
-
data[group.depth] += group.colspan;
|
174
|
-
} else {
|
175
|
-
data[group.depth] = group.colspan;
|
176
|
-
}
|
177
|
-
});
|
178
|
-
|
179
|
-
return data;
|
180
|
-
};
|
181
|
-
|
182
|
-
/**
|
183
|
-
* GroupField 데이터 검증
|
184
|
-
* @param 사용자가 설정한 그룹 필드 정보
|
185
|
-
* @returns 데이터 검증 결과
|
186
|
-
*/
|
187
|
-
const isCheckGroupField = (groupField: IGroupField[]): boolean => {
|
188
|
-
const map = makeCheckGroupData(groupField);
|
189
|
-
|
190
|
-
for (const depth of Object.keys(map)) {
|
191
|
-
if (map[depth] !== columnIndex + 1) {
|
192
|
-
console.error('그룹 데이터의 children 숫자가 columnIndex와 맞지 않습니다. 다시 한 번 확인 바랍니다.');
|
193
|
-
}
|
194
|
-
}
|
195
|
-
|
196
|
-
return true;
|
197
|
-
};
|
198
|
-
|
199
|
-
/**
|
200
|
-
* Grand Total 셀 정보 저장
|
201
|
-
* @param e
|
202
|
-
*/
|
203
|
-
const setTotalElementInfo = (e: DevExpress.ui.dxPivotGrid.CellPreparedEvent): void => {
|
204
|
-
if (!groupField?.length || e.cell?.type !== 'GT' || e.cell?.text !== 'Grand Total') {
|
205
|
-
return;
|
206
|
-
}
|
207
|
-
|
208
|
-
e.cellElement?.classList.add(grandTotalCssNm);
|
209
|
-
};
|
210
|
-
|
211
|
-
/**
|
212
|
-
* cell의 columnIndex 최대값 저장
|
213
|
-
* @param e
|
214
|
-
*/
|
215
|
-
const setMaxColumIndex = (e: DevExpress.ui.dxPivotGrid.CellPreparedEvent): void => {
|
216
|
-
if (!e.columnIndex) {
|
217
|
-
return;
|
218
|
-
}
|
219
|
-
if (e.columnIndex > columnIndex) {
|
220
|
-
setColumnIndex(e.columnIndex);
|
221
|
-
}
|
222
|
-
};
|
223
|
-
|
224
|
-
/**
|
225
|
-
* groupField depth의 유니크한 배열 구하기
|
226
|
-
* @param group
|
227
|
-
* @param arr
|
228
|
-
* @returns
|
229
|
-
*/
|
230
|
-
const getGroupDepth = (group: IGroupField[], arr: 'asc' | 'desc'): number[] => {
|
231
|
-
const groupData = group.slice();
|
232
|
-
const set = new Set(groupData.map((group: IGroupField) => group.depth));
|
233
|
-
return Array.from(set).sort(function compare(a: number, b: number) {
|
234
|
-
if (a > b) {
|
235
|
-
return arr === 'asc' ? -1 : 1;
|
236
|
-
}
|
237
|
-
if (a < b) {
|
238
|
-
return arr === 'asc' ? 1 : -1;
|
239
|
-
}
|
240
|
-
return 0;
|
241
|
-
});
|
242
|
-
};
|
243
|
-
|
244
|
-
/**
|
245
|
-
* 현재 depth에 맞는 그룹 필드 정보 반환
|
246
|
-
* @param group
|
247
|
-
* @param depth
|
248
|
-
* @returns
|
249
|
-
*/
|
250
|
-
const getCurrentGroup = (group: IGroupField[], depth: number): IGroupField[] => {
|
251
|
-
return group.filter((gr: IGroupField) => gr.depth === depth);
|
252
|
-
};
|
253
|
-
|
254
|
-
/**
|
255
|
-
* 테이블 헤더(DOM)에 colspan 적용된 테이블 삽입
|
256
|
-
*/
|
257
|
-
const insertRowHeaderGroup = (): void => {
|
258
|
-
if (!groupField?.length) {
|
259
|
-
return;
|
260
|
-
}
|
261
|
-
|
262
|
-
isCheckGroupField(groupField);
|
263
|
-
|
264
|
-
const totalElement = document.querySelector('.' + grandTotalCssNm);
|
265
|
-
const targetElement = totalElement?.parentNode;
|
266
|
-
const thead = targetElement?.parentNode;
|
267
|
-
|
268
|
-
if (!targetElement || !thead) {
|
269
|
-
return;
|
270
|
-
}
|
271
|
-
|
272
|
-
const firstChild = thead?.firstChild;
|
273
|
-
if (!firstChild) {
|
274
|
-
return;
|
275
|
-
}
|
276
|
-
totalElement.innerHTML = '';
|
277
|
-
totalElement.setAttribute('style', 'padding: 0; border: 0');
|
278
|
-
const colgroup = thead.previousSibling?.cloneNode(true);
|
279
|
-
|
280
|
-
const groupData = groupField.slice();
|
281
|
-
const depth = getGroupDepth(groupData, 'asc');
|
282
|
-
|
283
|
-
const table = document.createElement('table');
|
284
|
-
|
285
|
-
depth.forEach((dep: number, index: number) => {
|
286
|
-
const groupInfo = getCurrentGroup(groupData, dep);
|
287
|
-
|
288
|
-
const tr = document.createElement('tr');
|
289
|
-
|
290
|
-
groupInfo.forEach((group: IGroupField, cellIndex: number) => {
|
291
|
-
const isLast = cellIndex === groupInfo.length - 1 ? true : false;
|
292
|
-
tr.appendChild(makeColspan(group, index, isLast));
|
293
|
-
});
|
294
|
-
(table as HTMLElement).prepend(tr);
|
295
|
-
});
|
296
|
-
|
297
|
-
table.prepend(colgroup as Node);
|
298
|
-
totalElement.appendChild(table);
|
299
|
-
};
|
300
|
-
|
301
|
-
/**
|
302
|
-
* Devextreme의 dateController columnInfo에 그룹 정보 삽입
|
303
|
-
* @param group
|
304
|
-
* @returns
|
305
|
-
*/
|
306
|
-
const makeDataControllerColumnGroup = (group: IGroupField[]): ColumnField[][] => {
|
307
|
-
const groupData = group.slice();
|
308
|
-
const depth = getGroupDepth(groupData, 'desc');
|
309
|
-
|
310
|
-
return depth.map((dep: number) => {
|
311
|
-
const groupInfo = getCurrentGroup(groupData, dep);
|
312
|
-
return groupInfo.map((group: IGroupField) => ({ colspan: group.colspan, text: group.groupCaption, type: 'GT' }));
|
313
|
-
});
|
314
|
-
};
|
315
|
-
|
316
|
-
/**
|
317
|
-
* 사용자가 입력한 컬러 조건을 { standard: string; condition: string } 형식으로 변경 반환
|
318
|
-
* @param condition 사용자 입력 컬러 조건식 ex) '>= 100'
|
319
|
-
* @returns
|
320
|
-
*/
|
321
|
-
const makeSplitCondtion = (condition: string): { standard: string; condition: string } => {
|
322
|
-
const newCondition = { standard: '', condition: '' };
|
323
|
-
[...condition].forEach((cond: string) => {
|
324
|
-
if (Number.isNaN(parseFloat(cond))) {
|
325
|
-
newCondition.condition += cond;
|
326
|
-
} else {
|
327
|
-
newCondition.standard += cond;
|
328
|
-
}
|
329
|
-
});
|
330
|
-
|
331
|
-
return newCondition;
|
332
|
-
};
|
333
|
-
|
334
|
-
/**
|
335
|
-
* 데이터에 색상 적용
|
336
|
-
* @param e onCellPrepared 이벤트
|
337
|
-
* @returns
|
338
|
-
*/
|
339
|
-
const makeColorAtPercent = (e: any): void => {
|
340
|
-
if (!dataColor || !e.cellElement) {
|
341
|
-
return;
|
342
|
-
}
|
343
|
-
|
344
|
-
dataColor.forEach((color: IColorInfo) => {
|
345
|
-
if (e.cell.value === null) {
|
346
|
-
return;
|
347
|
-
}
|
348
|
-
if (e.cell?.format?.type === color.format && !Number.isNaN(e.cell.value)) {
|
349
|
-
const standardData = makeSplitCondtion(color.condition.replace(/(\s*)/g, ''));
|
350
|
-
const rate = color.format === 'percent' ? 0.01 : 1;
|
351
|
-
let condition = false;
|
352
|
-
|
353
|
-
switch (standardData.condition) {
|
354
|
-
case '>':
|
355
|
-
condition = e.cell.value > parseFloat(standardData.standard) * rate;
|
356
|
-
break;
|
357
|
-
case '>=':
|
358
|
-
condition = e.cell.value >= parseFloat(standardData.standard) * rate;
|
359
|
-
break;
|
360
|
-
case '<':
|
361
|
-
condition = e.cell.value < parseFloat(standardData.standard) * rate;
|
362
|
-
break;
|
363
|
-
case '<=':
|
364
|
-
condition = e.cell.value <= parseFloat(standardData.standard) * rate;
|
365
|
-
break;
|
366
|
-
}
|
367
|
-
|
368
|
-
if (condition && !(e.cell.value === 0 && convertZeroToHipen)) {
|
369
|
-
e.cellElement.style.color = color.color;
|
370
|
-
}
|
371
|
-
}
|
372
|
-
});
|
373
|
-
};
|
374
|
-
|
375
|
-
/**
|
376
|
-
* 그리드 데이터 정합성 체크. 데이터 잘못되어 있으면 에러 발생
|
377
|
-
* @param dataSource
|
378
|
-
*/
|
379
|
-
const checkDataSource = (dataSource: any): void => {
|
380
|
-
const isColumns = dataSource._fields.findIndex((field: any) => field.area === 'column');
|
381
|
-
const isRows = dataSource._fields.findIndex((field: any) => field.area === 'row');
|
382
|
-
const isDatas = dataSource._fields.findIndex((field: any) => field.area === 'data');
|
383
|
-
|
384
|
-
if (isColumns > -1) {
|
385
|
-
throw Error('DxPlanitTreeGrid는 column이 존재하는 형식의 pivot grid에는 사용할 수 없습니다.');
|
386
|
-
}
|
387
|
-
|
388
|
-
if (isRows === -1 || isDatas === -1) {
|
389
|
-
throw Error('DxPlanitTreeGrid 데이터는 row와 data가 반드시 존재해야 합니다.');
|
390
|
-
}
|
391
|
-
};
|
392
|
-
|
393
|
-
/**
|
394
|
-
* 그리드 펼침 정보 세션스토리지 리셋
|
395
|
-
*/
|
396
|
-
const resetSession = (): void => {
|
397
|
-
sessionStorage.removeItem('dx-vera-pivotgrid-storing');
|
398
|
-
};
|
399
|
-
|
400
|
-
/**
|
401
|
-
* 엑셀 export 명령
|
402
|
-
* @param fileName 저장하고자 하는 엑셀파일명
|
403
|
-
*/
|
404
|
-
const exportToExcel = (fileName: string): void => {
|
405
|
-
setTimeout(() => exportToExcelAction($tableRef.current?.instance, fileName));
|
406
|
-
};
|
407
|
-
|
408
|
-
/**
|
409
|
-
* devextreme component 정보의 dataController의 columnInfo에 사용자가 설정한 groupFIled 정보 병합
|
410
|
-
* @param component devextreme component
|
411
|
-
* @returns devextreme component
|
412
|
-
*/
|
413
|
-
const convertDataControllerColumnsInfo = (component: any): any => {
|
414
|
-
let arr: ColumnField[][] = [];
|
415
|
-
const columnInfo = component._dataController._columnsInfo.forEach((column: ColumnField[]) => {
|
416
|
-
let newColumn = column.slice();
|
417
|
-
if (groupField && newColumn.length === 1 && newColumn[0].type === 'GT' && newColumn[0].text === 'Grand Total') {
|
418
|
-
arr.push(...makeDataControllerColumnGroup(groupField));
|
419
|
-
} else {
|
420
|
-
arr.push(newColumn);
|
421
|
-
}
|
422
|
-
});
|
423
|
-
component._dataController._columnsInfo = arr;
|
424
|
-
return component;
|
425
|
-
};
|
426
|
-
|
427
|
-
/**
|
428
|
-
* 엑셀 export
|
429
|
-
* @param e
|
430
|
-
*/
|
431
|
-
const exportToExcelAction = (e: any, fileName: string): void => {
|
432
|
-
const newComponent = convertDataControllerColumnsInfo(e);
|
433
|
-
|
434
|
-
const workbook = new Workbook();
|
435
|
-
const worksheet = workbook.addWorksheet(fileName);
|
436
|
-
|
437
|
-
exportPivotGrid({
|
438
|
-
component: newComponent,
|
439
|
-
worksheet,
|
440
|
-
customizeCell: ({ excelCell }) => {
|
441
|
-
const borderStyle = excelBorder;
|
442
|
-
excelCell.border = {
|
443
|
-
bottom: borderStyle,
|
444
|
-
left: borderStyle,
|
445
|
-
right: borderStyle,
|
446
|
-
top: borderStyle,
|
447
|
-
};
|
448
|
-
},
|
449
|
-
}).then(() => {
|
450
|
-
workbook.xlsx.writeBuffer().then(buffer => {
|
451
|
-
saveAs(new Blob([buffer], { type: 'application/octet-stream' }), fileName + '.xlsx');
|
452
|
-
});
|
453
|
-
});
|
454
|
-
e.cancel = true;
|
455
|
-
};
|
456
|
-
|
457
|
-
/**
|
458
|
-
* devextreme CellPreparedEvent 이벤트 실행
|
459
|
-
* @param e
|
460
|
-
*/
|
461
|
-
const onCellPreparedChild = (
|
462
|
-
e: DevExpress.ui.dxPivotGrid.CellPreparedEvent
|
463
|
-
): ((e: DevExpress.ui.dxPivotGrid.CellPreparedEvent) => void) | void => {
|
464
|
-
makeColorAtPercent(e);
|
465
|
-
setTotalElementInfo(e);
|
466
|
-
setMaxColumIndex(e);
|
467
|
-
changeTotalText(e);
|
468
|
-
changeNullToHipen(e);
|
469
|
-
changeZeroToHipen(e);
|
470
|
-
|
471
|
-
return onCellPrepared ? onCellPrepared(e) : undefined;
|
472
|
-
};
|
473
|
-
|
474
|
-
/**
|
475
|
-
* devextreme Raise Event
|
476
|
-
*/
|
477
|
-
const onContentReadyChild = (
|
478
|
-
e: DevExpress.ui.dxPivotGrid.ContentReadyEvent
|
479
|
-
): ((e: DevExpress.ui.dxPivotGrid.ContentReadyEvent) => void) | void => {
|
480
|
-
setTimeout(() => insertRowHeaderGroup(), 0);
|
481
|
-
getGridSize();
|
482
|
-
|
483
|
-
return onContentReady ? onContentReady(e) : undefined;
|
484
|
-
};
|
485
|
-
|
486
|
-
const onCellClickChild = (
|
487
|
-
e: DevExpress.ui.dxPivotGrid.CellClickEvent
|
488
|
-
): ((e: DevExpress.ui.dxPivotGrid.CellClickEvent) => void) | void => {
|
489
|
-
return onCellClick ? onCellClick(e) : undefined;
|
490
|
-
};
|
491
|
-
|
492
|
-
const onContextMenuPreparingChild = (
|
493
|
-
e: DevExpress.ui.dxPivotGrid.ContextMenuPreparingEvent
|
494
|
-
): ((e: DevExpress.ui.dxPivotGrid.ContextMenuPreparingEvent) => void) | void => {
|
495
|
-
return onContextMenuPreparing ? onContextMenuPreparing(e) : undefined;
|
496
|
-
};
|
497
|
-
|
498
|
-
const onDisposingChild = (
|
499
|
-
e: DevExpress.ui.dxPivotGrid.DisposingEvent
|
500
|
-
): ((e: DevExpress.ui.dxPivotGrid.DisposingEvent) => void) | void => {
|
501
|
-
return onDisposing ? onDisposing(e) : undefined;
|
502
|
-
};
|
503
|
-
|
504
|
-
const onExportingChild = (
|
505
|
-
e: DevExpress.ui.dxPivotGrid.ExportingEvent
|
506
|
-
): ((e: DevExpress.ui.dxPivotGrid.ExportingEvent) => void) | void => {
|
507
|
-
return onExporting ? onExporting(e) : undefined;
|
508
|
-
};
|
509
|
-
|
510
|
-
const onInitializedChild = (
|
511
|
-
e: DevExpress.ui.dxPivotGrid.InitializedEvent
|
512
|
-
): ((e: DevExpress.ui.dxPivotGrid.InitializedEvent) => void) | void => {
|
513
|
-
return onInitialized ? onInitialized(e) : undefined;
|
514
|
-
};
|
515
|
-
|
516
|
-
const onOptionChangedChild = (
|
517
|
-
e: DevExpress.ui.dxPivotGrid.OptionChangedEvent
|
518
|
-
): ((e: DevExpress.ui.dxPivotGrid.OptionChangedEvent) => void) | void => {
|
519
|
-
return onOptionChanged ? onOptionChanged(e) : undefined;
|
520
|
-
};
|
521
|
-
|
522
|
-
useEffect(() => {
|
523
|
-
if (customExcelButton) {
|
524
|
-
}
|
525
|
-
}, [customExcelButton]);
|
526
|
-
|
527
|
-
useEffect(() => {
|
528
|
-
setGridDataSource(dataSource);
|
529
|
-
checkDataSource(dataSource);
|
530
|
-
resetSession();
|
531
|
-
}, [dataSource]);
|
532
|
-
|
533
|
-
return (
|
534
|
-
<div>
|
535
|
-
<LoadPanel position={{ of: id }} />
|
536
|
-
<PivotGrid
|
537
|
-
id={id}
|
538
|
-
ref={$tableRef}
|
539
|
-
dataSource={gridDataSource}
|
540
|
-
showColumnTotals={false}
|
541
|
-
showColumnGrandTotals={true}
|
542
|
-
showRowGrandTotals={false}
|
543
|
-
width={width}
|
544
|
-
height={height}
|
545
|
-
allowExpandAll={allowExpandAll}
|
546
|
-
allowFiltering={allowFiltering}
|
547
|
-
allowSorting={allowSorting}
|
548
|
-
allowSortingBySummary={allowSortingBySummary}
|
549
|
-
dataFieldArea={dataFieldArea}
|
550
|
-
disabled={disabled}
|
551
|
-
elementAttr={elementAttr}
|
552
|
-
encodeHtml={encodeHtml}
|
553
|
-
hideEmptySummaryCells={hideEmptySummaryCells}
|
554
|
-
hint={hint}
|
555
|
-
rowHeaderLayout={rowHeaderLayout}
|
556
|
-
rtlEnabled={rtlEnabled}
|
557
|
-
showBorders={showBorders}
|
558
|
-
showRowTotals={showRowTotals}
|
559
|
-
showTotalsPrior={showTotalsPrior}
|
560
|
-
tabIndex={tabIndex}
|
561
|
-
visible={visible}
|
562
|
-
wordWrapEnabled={wordWrapEnabled}
|
563
|
-
onCellClick={onCellClickChild}
|
564
|
-
onContentReady={onContentReadyChild}
|
565
|
-
onCellPrepared={onCellPreparedChild}
|
566
|
-
onContextMenuPreparing={onContextMenuPreparingChild}
|
567
|
-
onDisposing={onDisposingChild}
|
568
|
-
onExporting={onExportingChild}
|
569
|
-
onInitialized={onInitializedChild}
|
570
|
-
onOptionChanged={onOptionChangedChild}
|
571
|
-
>
|
572
|
-
<StateStoring enabled={stateStoringKey?.length} type="sessionStorage" storageKey={stateStoringKey} />
|
573
|
-
<FieldChooser enabled={false} />
|
574
|
-
</PivotGrid>
|
575
|
-
</div>
|
576
|
-
);
|
577
|
-
}
|
578
|
-
);
|
579
|
-
|
580
|
-
export default DxPlanitTreeGrid;
|
package/dist/index.tsx
DELETED
package/dist/type.ts
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
import DevExpress from 'devextreme';
|
2
|
-
import { Format } from 'devextreme/localization';
|
3
|
-
|
4
|
-
export interface IGroupField {
|
5
|
-
groupCaption: string;
|
6
|
-
groupName?: string;
|
7
|
-
depth: number;
|
8
|
-
colspan: number;
|
9
|
-
}
|
10
|
-
|
11
|
-
export interface IColorInfo {
|
12
|
-
format: Format;
|
13
|
-
color: string;
|
14
|
-
condition: string;
|
15
|
-
}
|
16
|
-
|
17
|
-
export interface ColumnField {
|
18
|
-
colspan: number;
|
19
|
-
text: string;
|
20
|
-
type: string;
|
21
|
-
}
|
22
|
-
|
23
|
-
export interface Props extends DevExpress.ui.dxPivotGrid.Properties {
|
24
|
-
id?: string;
|
25
|
-
dataSource?: any;
|
26
|
-
groupField?: IGroupField[];
|
27
|
-
dataColor?: IColorInfo[];
|
28
|
-
convertNullToHipen?: boolean;
|
29
|
-
convertZeroToHipen?: boolean;
|
30
|
-
stateStoringKey?: string;
|
31
|
-
customExcelButton?: boolean;
|
32
|
-
}
|