evui 3.4.207 → 3.4.209

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/README.md +18 -33
  2. package/dist/404.html +44 -0
  3. package/dist/favicon.ico +0 -0
  4. package/dist/index.js +22738 -0
  5. package/dist/index.umd.cjs +28 -0
  6. package/dist/style.css +1 -0
  7. package/package.json +46 -43
  8. package/dist/evui.common.js +0 -63681
  9. package/dist/evui.common.js.map +0 -1
  10. package/dist/evui.umd.js +0 -63691
  11. package/dist/evui.umd.js.map +0 -1
  12. package/dist/evui.umd.min.js +0 -2
  13. package/dist/evui.umd.min.js.map +0 -1
  14. package/dist/img/EVUI.b82ee81a.svg +0 -293
  15. package/src/assets/logo.png +0 -0
  16. package/src/common/emitter.js +0 -20
  17. package/src/common/utils.bignumber.js +0 -67
  18. package/src/common/utils.debounce.js +0 -223
  19. package/src/common/utils.js +0 -151
  20. package/src/common/utils.table.js +0 -78
  21. package/src/common/utils.throttle.js +0 -83
  22. package/src/common/utils.tree.js +0 -18
  23. package/src/components/button/Button.vue +0 -195
  24. package/src/components/button/index.js +0 -7
  25. package/src/components/buttonGroup/ButtonGroup.vue +0 -11
  26. package/src/components/buttonGroup/index.js +0 -7
  27. package/src/components/calendar/Calendar.vue +0 -725
  28. package/src/components/calendar/index.js +0 -7
  29. package/src/components/calendar/uses.js +0 -1410
  30. package/src/components/chart/Chart.vue +0 -363
  31. package/src/components/chart/ChartToolbar.vue +0 -52
  32. package/src/components/chart/chart.core.js +0 -1170
  33. package/src/components/chart/chartZoom.core.js +0 -540
  34. package/src/components/chart/element/element.bar.js +0 -672
  35. package/src/components/chart/element/element.bar.time.js +0 -166
  36. package/src/components/chart/element/element.heatmap.js +0 -743
  37. package/src/components/chart/element/element.line.js +0 -611
  38. package/src/components/chart/element/element.pie.js +0 -197
  39. package/src/components/chart/element/element.scatter.js +0 -320
  40. package/src/components/chart/element/element.tip.js +0 -717
  41. package/src/components/chart/helpers/helpers.canvas.js +0 -265
  42. package/src/components/chart/helpers/helpers.constant.js +0 -235
  43. package/src/components/chart/helpers/helpers.util.js +0 -400
  44. package/src/components/chart/index.js +0 -9
  45. package/src/components/chart/model/index.js +0 -50
  46. package/src/components/chart/model/model.series.js +0 -125
  47. package/src/components/chart/model/model.store.js +0 -1427
  48. package/src/components/chart/plugins/plugins.interaction.js +0 -1659
  49. package/src/components/chart/plugins/plugins.legend.gradient.js +0 -606
  50. package/src/components/chart/plugins/plugins.legend.js +0 -1543
  51. package/src/components/chart/plugins/plugins.pie.js +0 -254
  52. package/src/components/chart/plugins/plugins.scrollbar.js +0 -732
  53. package/src/components/chart/plugins/plugins.title.js +0 -61
  54. package/src/components/chart/plugins/plugins.tooltip.js +0 -1041
  55. package/src/components/chart/scale/scale.js +0 -951
  56. package/src/components/chart/scale/scale.linear.js +0 -268
  57. package/src/components/chart/scale/scale.logarithmic.js +0 -135
  58. package/src/components/chart/scale/scale.step.js +0 -430
  59. package/src/components/chart/scale/scale.time.category.js +0 -338
  60. package/src/components/chart/scale/scale.time.js +0 -49
  61. package/src/components/chart/style/chart.scss +0 -405
  62. package/src/components/chart/uses.js +0 -721
  63. package/src/components/chartBrush/ChartBrush.vue +0 -323
  64. package/src/components/chartBrush/chartBrush.core.js +0 -691
  65. package/src/components/chartBrush/index.js +0 -9
  66. package/src/components/chartBrush/uses.js +0 -23
  67. package/src/components/chartGroup/ChartGroup.vue +0 -144
  68. package/src/components/chartGroup/index.js +0 -9
  69. package/src/components/chartGroup/style/chartGroup.scss +0 -5
  70. package/src/components/chartGroup/uses.js +0 -53
  71. package/src/components/checkbox/Checkbox.vue +0 -229
  72. package/src/components/checkbox/index.js +0 -7
  73. package/src/components/checkboxGroup/CheckboxGroup.vue +0 -44
  74. package/src/components/checkboxGroup/index.js +0 -7
  75. package/src/components/contextMenu/ContextMenu.vue +0 -95
  76. package/src/components/contextMenu/MenuList.vue +0 -182
  77. package/src/components/contextMenu/index.js +0 -7
  78. package/src/components/contextMenu/uses.js +0 -223
  79. package/src/components/datePicker/DatePicker.vue +0 -504
  80. package/src/components/datePicker/index.js +0 -7
  81. package/src/components/datePicker/uses.js +0 -460
  82. package/src/components/grid/Grid.vue +0 -1535
  83. package/src/components/grid/GridColumnSetting.vue +0 -358
  84. package/src/components/grid/GridFilterSetting.vue +0 -323
  85. package/src/components/grid/GridPagination.vue +0 -75
  86. package/src/components/grid/GridSummary.vue +0 -314
  87. package/src/components/grid/GridToolbar.vue +0 -35
  88. package/src/components/grid/icon/icon-option-button.vue +0 -17
  89. package/src/components/grid/icon/icon-sort-button.vue +0 -67
  90. package/src/components/grid/index.js +0 -11
  91. package/src/components/grid/style/grid.scss +0 -417
  92. package/src/components/grid/uses.js +0 -1629
  93. package/src/components/icon/Icon.vue +0 -53
  94. package/src/components/icon/index.js +0 -8
  95. package/src/components/inputNumber/InputNumber.vue +0 -212
  96. package/src/components/inputNumber/index.js +0 -7
  97. package/src/components/inputNumber/uses.js +0 -217
  98. package/src/components/loading/Loading.vue +0 -125
  99. package/src/components/loading/index.js +0 -7
  100. package/src/components/menu/Menu.vue +0 -79
  101. package/src/components/menu/MenuItem.vue +0 -201
  102. package/src/components/menu/index.js +0 -7
  103. package/src/components/message/Message.vue +0 -229
  104. package/src/components/message/index.js +0 -34
  105. package/src/components/messageBox/MessageBox.vue +0 -358
  106. package/src/components/messageBox/index.js +0 -22
  107. package/src/components/notification/Notification.vue +0 -316
  108. package/src/components/notification/index.js +0 -49
  109. package/src/components/pagination/Pagination.vue +0 -317
  110. package/src/components/pagination/index.js +0 -7
  111. package/src/components/pagination/pageButton.vue +0 -31
  112. package/src/components/progress/Progress.vue +0 -139
  113. package/src/components/progress/index.js +0 -7
  114. package/src/components/radio/Radio.vue +0 -159
  115. package/src/components/radio/index.js +0 -7
  116. package/src/components/radioGroup/RadioGroup.vue +0 -41
  117. package/src/components/radioGroup/index.js +0 -7
  118. package/src/components/scheduler/Scheduler.vue +0 -149
  119. package/src/components/scheduler/index.js +0 -7
  120. package/src/components/scheduler/uses.js +0 -183
  121. package/src/components/select/Select.vue +0 -556
  122. package/src/components/select/index.js +0 -7
  123. package/src/components/select/uses.js +0 -379
  124. package/src/components/slider/Slider.vue +0 -505
  125. package/src/components/slider/index.js +0 -7
  126. package/src/components/slider/uses.js +0 -391
  127. package/src/components/tabPanel/TabPanel.vue +0 -74
  128. package/src/components/tabPanel/index.js +0 -7
  129. package/src/components/tabs/Tabs.vue +0 -517
  130. package/src/components/tabs/index.js +0 -7
  131. package/src/components/textField/TextField.vue +0 -399
  132. package/src/components/textField/index.js +0 -7
  133. package/src/components/timePicker/TimePicker.vue +0 -364
  134. package/src/components/timePicker/index.js +0 -7
  135. package/src/components/toggle/Toggle.vue +0 -115
  136. package/src/components/toggle/index.js +0 -7
  137. package/src/components/tree/Tree.vue +0 -338
  138. package/src/components/tree/TreeNode.vue +0 -293
  139. package/src/components/tree/index.js +0 -7
  140. package/src/components/treeGrid/TreeGrid.vue +0 -1074
  141. package/src/components/treeGrid/TreeGridNode.vue +0 -349
  142. package/src/components/treeGrid/TreeGridToolbar.vue +0 -35
  143. package/src/components/treeGrid/icon/icon-tree.png +0 -0
  144. package/src/components/treeGrid/index.js +0 -9
  145. package/src/components/treeGrid/style/treeGrid.scss +0 -277
  146. package/src/components/treeGrid/uses.js +0 -1178
  147. package/src/components/window/Window.vue +0 -329
  148. package/src/components/window/index.js +0 -7
  149. package/src/components/window/uses.js +0 -908
  150. package/src/directives/clickoutside.js +0 -90
  151. package/src/main.js +0 -120
  152. package/src/style/components/input.scss +0 -108
  153. package/src/style/functions.scss +0 -3
  154. package/src/style/index.scss +0 -6
  155. package/src/style/lib/fonts/EVUI.eot +0 -0
  156. package/src/style/lib/fonts/EVUI.svg +0 -293
  157. package/src/style/lib/fonts/EVUI.ttf +0 -0
  158. package/src/style/lib/fonts/EVUI.woff +0 -0
  159. package/src/style/lib/icon.css +0 -888
  160. package/src/style/mixins.scss +0 -94
  161. package/src/style/themes.scss +0 -69
  162. package/src/style/variables.scss +0 -22
@@ -1,611 +0,0 @@
1
- import { defaultsDeep, isNil, isUndefined } from 'lodash-es';
2
- import { COLOR, LINE_OPTION } from '../helpers/helpers.constant';
3
- import Util from '../helpers/helpers.util';
4
- import Canvas from '../helpers/helpers.canvas';
5
-
6
- class Line {
7
- constructor(sId, opt, sIdx) {
8
- const merged = defaultsDeep({}, opt, LINE_OPTION);
9
- Object.keys(merged).forEach((key) => {
10
- this[key] = merged[key];
11
- });
12
-
13
- if (this.name === undefined) {
14
- this.name = `series-${sIdx}`;
15
- }
16
-
17
- ['color', 'pointFill', 'fillColor'].forEach((colorProp) => {
18
- if (this[colorProp] === undefined) {
19
- this[colorProp] = colorProp === 'pointFill' ? this.color : COLOR[(sIdx) % COLOR.length];
20
- }
21
- });
22
- this.type = 'line';
23
- this.sId = sId;
24
- this.extent = {
25
- downplay: { opacity: 0.1, lineWidth: 1 },
26
- normal: { opacity: 1, lineWidth: 1 },
27
- highlight: { opacity: 1, lineWidth: 2 },
28
- };
29
- /** @type {import('../model/index').ChartSeriesDataPoint[]} */
30
- this.data = [];
31
- this.beforeMouseXp = 0;
32
- this.beforeMouseYp = 0;
33
- this.beforeFindItemIndex = -1;
34
- this.size = {
35
- comboOffset: 0,
36
- };
37
- }
38
-
39
- useLinearInterpolation() {
40
- return this.interpolation === 'linear' || (this.interpolation === 'none' && !!this.passingValue && this.hasPassingValueInData);
41
- }
42
-
43
- /**
44
- * @typedef {Object} LineDrawParam
45
- * @property {CanvasRenderingContext2D} ctx - 캔버스 렌더링 컨텍스트
46
- * @property {object} chartRect - 차트 영역 정보
47
- * @property {object} labelOffset - 라벨 오프셋 정보
48
- * @property {object} axesSteps - 축 스텝 정보
49
- * @property {object} [selectLabel] - 선택된 라벨 정보
50
- * @property {object} [selectSeries] - 선택된 시리즈 정보
51
- * @property {object} [legendHitInfo] - 범례 히트 정보
52
- * @property {boolean} [isBrush] - 브러시 사용 여부
53
- */
54
- /**
55
- * Draw series data
56
- * @param {LineDrawParam} param object for drawing series data
57
- *
58
- * @returns {undefined}
59
- */
60
- draw(param) {
61
- if (!this.show) {
62
- return;
63
- }
64
-
65
- const {
66
- ctx, chartRect,
67
- labelOffset, axesSteps,
68
- selectLabel, selectSeries, legendHitInfo,
69
- isBrush,
70
- } = param;
71
-
72
- // about selectLabel
73
- const selectLabelOption = selectLabel?.option;
74
- const useSelectLabel = selectLabelOption?.use && selectLabelOption?.useSeriesOpacity;
75
- const selectedLabelIndexList = selectLabel?.selected?.dataIndex ?? [];
76
-
77
- // set Style
78
- let extent;
79
- if (legendHitInfo) {
80
- extent = this.extent[legendHitInfo?.sId === this.sId ? 'highlight' : 'downplay'];
81
- } else if (selectSeries?.option?.use && selectSeries?.selected?.seriesId?.length) {
82
- const isSelectedSeries = selectSeries?.selected?.seriesId?.includes(this.sId);
83
- extent = this.extent[isSelectedSeries ? 'highlight' : 'downplay'];
84
- } else if (useSelectLabel && selectedLabelIndexList.length) {
85
- extent = this.extent.downplay;
86
- } else {
87
- extent = this.extent.normal;
88
- }
89
-
90
- const getOpacity = colorStr => (colorStr?.includes('rgba') ? Util.getOpacity(colorStr) : extent.opacity);
91
- const mainColor = this.color;
92
- const mainColorOpacity = getOpacity(mainColor);
93
- const pointFillColor = this.pointFill;
94
- const pointFillColorOpacity = getOpacity(pointFillColor);
95
- const fillOpacity = this.fillOpacity;
96
- const lineWidth = this.lineWidth * extent.lineWidth;
97
-
98
- ctx.beginPath();
99
- ctx.save();
100
- ctx.lineJoin = 'round';
101
- ctx.lineWidth = lineWidth;
102
- ctx.strokeStyle = Util.colorStringToRgba(mainColor, mainColorOpacity);
103
- if (this.segments) {
104
- ctx.setLineDash(this.segments);
105
- }
106
-
107
- const isLinearInterpolation = this.useLinearInterpolation();
108
-
109
- let barAreaByCombo = 0;
110
-
111
- const minmaxX = axesSteps.x[this.xAxisIndex];
112
- const minmaxY = axesSteps.y[this.yAxisIndex];
113
-
114
- let xArea = chartRect.chartWidth - (labelOffset.left + labelOffset.right);
115
- const yArea = chartRect.chartHeight - (labelOffset.top + labelOffset.bottom);
116
-
117
- if (this.combo) {
118
- barAreaByCombo = xArea / (this.data.length || 1);
119
- xArea -= barAreaByCombo;
120
- this.size.comboOffset = barAreaByCombo;
121
- }
122
-
123
- const xsp = chartRect.x1 + labelOffset.left + (barAreaByCombo / 2);
124
- const ysp = chartRect.y2 - labelOffset.bottom;
125
-
126
- const getXPos = val => Canvas.calculateX(val, minmaxX.graphMin, minmaxX.graphMax, xArea, xsp);
127
- const getYPos = val => Canvas.calculateY(val, minmaxY.graphMin, minmaxY.graphMax, yArea, ysp);
128
- const includeNegativeValue = this.data.some(data => data.o < 0);
129
- const endPoint = includeNegativeValue ? getYPos(0) : chartRect.y2 - labelOffset.bottom;
130
-
131
- // draw line
132
- let prevValid;
133
- this.data.forEach((curr) => {
134
- let x = getXPos(curr.x);
135
- let y = getYPos(curr.y);
136
-
137
- if (this.isExistGrp && isLinearInterpolation && curr.o === null) {
138
- y = getYPos(curr.b ?? 0);
139
- }
140
-
141
- if (x !== null) {
142
- x += Util.aliasPixel(x);
143
- }
144
-
145
- curr.xp = x;
146
- curr.yp = y;
147
-
148
- if (isLinearInterpolation && curr.o === null) {
149
- return;
150
- } else if ((isNil(prevValid?.y) && !this.isExistGrp)
151
- || (!isLinearInterpolation && (isNil(prevValid?.y) || isNil(curr.o)))) {
152
- ctx.moveTo(x, y);
153
- } else {
154
- ctx.lineTo(x, y);
155
- }
156
-
157
- prevValid = curr;
158
- });
159
-
160
- ctx.stroke();
161
- if (this.segments) {
162
- ctx.setLineDash([]);
163
- }
164
-
165
-
166
- // draw fill
167
- if (this.fill && this.data.length) {
168
- ctx.beginPath();
169
-
170
- const fillColor = Util.colorStringToRgba(this.fillColor || mainColor, fillOpacity);
171
- if (this.fill?.gradient) {
172
- let maxValueYPos = this.data[0].yp;
173
- let minValueYBottomPos = this.data[0].y;
174
- this.data.forEach((data) => {
175
- if (data.yp && data.yp <= maxValueYPos) {
176
- maxValueYPos = data.yp;
177
- } else if (data.y && data.y >= minValueYBottomPos) {
178
- minValueYBottomPos = data.y;
179
- }
180
- });
181
- const gradient = ctx.createLinearGradient(0, chartRect.y2, 0, maxValueYPos);
182
- const mainGradientColor = extent.opacity < 1 ? fillColor : mainColor;
183
- gradient.addColorStop(0, includeNegativeValue ? mainGradientColor : fillColor);
184
- gradient.addColorStop(0.5, fillColor);
185
- gradient.addColorStop(1, mainGradientColor);
186
-
187
- ctx.fillStyle = gradient;
188
- } else {
189
- ctx.fillStyle = fillColor;
190
- }
191
-
192
- // Set dataIndex List for filling
193
- // ex) [10, passing, null, 10, 10, passing, 10] -> [[0, 1], [3, 6]]
194
- let start = null;
195
- let end = null;
196
- const valueArray = this.data.map(item => (item?.o));
197
- /** @type {Array<[number, number]>} */
198
- const needFillDataIndexList = [];
199
- for (let i = 0; i < valueArray.length + 1; i++) {
200
- if ((isLinearInterpolation && isUndefined(valueArray[i]))
201
- || (!isLinearInterpolation && isNil(valueArray[i]))) {
202
- if (start !== null && end !== null) {
203
- const temp = valueArray.slice(start, i);
204
- const lastNormalValueIndex = temp.findLastIndex(
205
- item => !isNil(item) && item !== null);
206
- needFillDataIndexList.push([start, start + lastNormalValueIndex]);
207
- start = null;
208
- end = null;
209
- }
210
- } else if (isLinearInterpolation && valueArray[i] === null) {
211
- end = i;
212
- } else {
213
- start = start === null ? i : start;
214
- end = i;
215
- }
216
- }
217
-
218
- // Draw rect for filling
219
- needFillDataIndexList.forEach(([startIndex, endIndex]) => {
220
- if (startIndex === endIndex) {
221
- const singleData = this.data[startIndex];
222
- ctx.moveTo(singleData.xp - lineWidth, singleData.yp);
223
- ctx.lineTo(singleData.xp + lineWidth, singleData.yp);
224
- ctx.lineTo(singleData.xp + lineWidth, getYPos(singleData.b) ?? endPoint);
225
- ctx.closePath();
226
- return;
227
- }
228
-
229
- for (let ix = startIndex; ix <= endIndex; ix++) {
230
- const currData = this.data[ix];
231
-
232
- if (ix === startIndex) {
233
- ctx.moveTo(currData.xp, currData.yp);
234
- } else if (currData.o !== null) {
235
- ctx.lineTo(currData.xp, currData.yp);
236
- }
237
-
238
- if (ix === endIndex) {
239
- for (let jx = endIndex; jx >= startIndex; jx--) {
240
- const nextData = this.data[jx];
241
- const xp = getXPos(nextData.x);
242
- const bp = nextData.o === null ? getYPos(0) : getYPos(nextData.b) ?? getYPos(0);
243
- ctx.lineTo(xp, bp);
244
- }
245
-
246
- ctx.closePath();
247
- }
248
- }
249
- });
250
-
251
- ctx.fill();
252
- }
253
-
254
- // Draw points
255
- if (!isBrush) {
256
- ctx.strokeStyle = Util.colorStringToRgba(mainColor, mainColorOpacity);
257
- const focusStyle = Util.colorStringToRgba(pointFillColor, 1);
258
- const blurStyle = Util.colorStringToRgba(pointFillColor, pointFillColorOpacity);
259
- const isLinearSingle = this.interpolation === 'linear' && this.data.filter(item => item.o !== null).length === 1;
260
-
261
- this.data.forEach((curr, ix) => {
262
- if (curr.xp === null || curr.yp === null || curr.o === null) {
263
- return;
264
- }
265
-
266
- const prevData = this.data[ix - 1]?.o;
267
- const nextData = this.data[ix + 1]?.o;
268
-
269
- const isSingle = (!isLinearInterpolation && isNil(prevData) && isNil(nextData))
270
- || isLinearSingle;
271
- const isSelectedLabel = selectedLabelIndexList.includes(ix);
272
- if (this.point || isSingle || isSelectedLabel) {
273
- ctx.fillStyle = isSelectedLabel && !legendHitInfo ? focusStyle : blurStyle;
274
- Canvas.drawPoint(ctx, this.pointStyle, this.pointSize, curr.xp, curr.yp);
275
- }
276
- });
277
- }
278
-
279
- ctx.restore();
280
- }
281
-
282
- /**
283
- * Draw item highlight
284
- * @param {object} item object for drawing series data
285
- * @param {object} context canvas context
286
- * @param {boolean} isMax determines if this series has max value
287
- *
288
- * @returns {undefined}
289
- */
290
- itemHighlight(item, context) {
291
- const gdata = item.data;
292
- const ctx = context;
293
-
294
- const { xp, yp, o } = gdata;
295
-
296
- ctx.save();
297
- if (xp !== null && yp !== null && o !== null && this.pointHighlight) {
298
- ctx.strokeStyle = Util.colorStringToRgba(this.color, 0);
299
- ctx.fillStyle = Util.colorStringToRgba(this.color, this.highlight.maxShadowOpacity);
300
- Canvas.drawPoint(ctx, this.pointStyle, this.highlight.maxShadowSize, xp, yp);
301
-
302
- ctx.fillStyle = this.color;
303
- Canvas.drawPoint(ctx, this.pointStyle, this.highlight.maxSize, xp, yp);
304
-
305
- ctx.fillStyle = '#fff';
306
- Canvas.drawPoint(ctx, this.pointStyle, this.highlight.defaultSize, xp, yp);
307
- }
308
-
309
- ctx.restore();
310
- }
311
-
312
- /**
313
- * Find graph item
314
- * @param {array} offset mouse position
315
- * @param {boolean} isHorizontal
316
- * @param {number} dataIndex selected label data index
317
- * @param {boolean} useSelectLabelOrItem used to display select label/item at tooltip location
318
- *
319
- * @returns {object} graph item
320
- */
321
- findGraphData(offset, isHorizontal, dataIndex, useSelectLabelOrItem) {
322
- const xp = offset[0];
323
- const yp = offset[1];
324
- const item = { data: null, hit: false, color: this.color };
325
- const gdata = this.data.filter(data => !Util.isNullOrUndefined(data.x));
326
- const isLinearInterpolation = this.useLinearInterpolation();
327
-
328
- if (gdata?.length) {
329
- if (typeof dataIndex === 'number' && this.show) {
330
- item.data = gdata[dataIndex];
331
- item.index = dataIndex;
332
- if (item.data) {
333
- const point = gdata[dataIndex];
334
- const yDist = Math.abs(yp - point.yp);
335
- const directHitThreshold = 15; // 직접 히트 임계값
336
-
337
- if (yDist <= directHitThreshold) {
338
- item.hit = true;
339
- }
340
- }
341
- } else if (typeof this.beforeFindItemIndex === 'number' && this.beforeFindItemIndex !== -1 && this.show && useSelectLabelOrItem) {
342
- item.data = gdata[this.beforeFindItemIndex];
343
- item.index = this.beforeFindItemIndex;
344
- } else {
345
- // Axis 트리거 방식: X축 위치에서 가장 가까운 데이터 포인트 찾기
346
- let closestXDistance = Infinity;
347
- let closestIndex = -1;
348
-
349
- // null이 아닌 유효한 데이터만 필터링
350
- const validData = [];
351
- gdata.forEach((point, idx) => {
352
- if (point.xp !== null && point.yp !== null && point.o !== null) {
353
- validData.push({ ...point, originalIndex: idx });
354
- }
355
- });
356
-
357
- if (validData.length === 0) {
358
- gdata.forEach((point, idx) => {
359
- validData.push({ ...point, originalIndex: idx });
360
- });
361
- }
362
-
363
- // 이진 탐색으로 가장 가까운 포인트 찾기
364
- let left = 0;
365
- let right = validData.length - 1;
366
-
367
- while (left <= right) {
368
- const mid = Math.floor((left + right) / 2);
369
- const point = validData[mid];
370
- const xDistance = Math.abs(xp - point.xp);
371
-
372
- if (xDistance < closestXDistance) {
373
- closestXDistance = xDistance;
374
- closestIndex = point.originalIndex;
375
- }
376
-
377
- if (point.xp < xp) {
378
- left = mid + 1;
379
- // 다음 포인트도 확인
380
- if (left < validData.length) {
381
- const nextDistance = Math.abs(xp - validData[left].xp);
382
- if (nextDistance < closestXDistance) {
383
- closestXDistance = nextDistance;
384
- closestIndex = validData[left].originalIndex;
385
- }
386
- }
387
- } else if (point.xp > xp) {
388
- right = mid - 1;
389
- // 이전 포인트도 확인
390
- if (right >= 0) {
391
- const prevDistance = Math.abs(xp - validData[right].xp);
392
- if (prevDistance < closestXDistance) {
393
- closestXDistance = prevDistance;
394
- closestIndex = validData[right].originalIndex;
395
- }
396
- }
397
- } else {
398
- // 정확히 일치하는 경우
399
- break;
400
- }
401
- }
402
-
403
- // 이진 탐색 후 주변 포인트 추가 확인 (정확도 향상)
404
- const foundIdx = validData.findIndex(p => p.originalIndex === closestIndex);
405
- if (foundIdx !== -1) {
406
- // 앞뒤 2개씩 추가 확인
407
- for (let i = Math.max(0, foundIdx - 2);
408
- i <= Math.min(validData.length - 1, foundIdx + 2);
409
- i++) {
410
- const point = validData[i];
411
- const xDistance = Math.abs(xp - point.xp);
412
- if (xDistance < closestXDistance) {
413
- closestXDistance = xDistance;
414
- closestIndex = point.originalIndex;
415
- }
416
- }
417
- }
418
-
419
- // 가장 가까운 포인트 설정
420
- if (closestIndex !== -1) {
421
- // 데이터 간격 계산 - 모든 데이터(null 포함)의 평균 간격 사용
422
- let avgInterval = 50;
423
- if (gdata.length > 1) {
424
- const intervals = [];
425
- for (let i = 1; i < gdata.length; i++) {
426
- if (gdata[i].xp !== null && gdata[i - 1].xp !== null) {
427
- intervals.push(Math.abs(gdata[i].xp - gdata[i - 1].xp));
428
- }
429
- }
430
- if (intervals.length > 0) {
431
- avgInterval = intervals.reduce((a, b) => a + b, 0) / intervals.length;
432
- }
433
- }
434
-
435
- // 두 가지 임계값 설정
436
- const threshold = Math.max(avgInterval, 1);
437
-
438
- // 1. 먼저 엄격한 임계값으로 정확한 매치 확인
439
- if (closestXDistance <= threshold) {
440
- // 정확히 일치하거나 매우 가까운 데이터가 있음
441
- item.data = gdata[closestIndex];
442
- item.index = closestIndex;
443
- } else {
444
- // 2. 정확한 매치가 없을 때, 현재 X 위치 근처에 다른 데이터가 있는지 확인
445
- let hasNearbyAnyData = false;
446
- let closestDistance = isLinearInterpolation ? Infinity : threshold;
447
- const dataSet = isLinearInterpolation ? validData : gdata;
448
- for (let i = 0; i < dataSet.length; i++) {
449
- const xDist = Math.abs(xp - dataSet[i].xp);
450
- if (xDist <= closestDistance) {
451
- hasNearbyAnyData = true;
452
- closestDistance = xDist;
453
- closestIndex = isLinearInterpolation ? dataSet[i].originalIndex : i;
454
- }
455
- }
456
-
457
- if (hasNearbyAnyData) {
458
- item.data = gdata[closestIndex];
459
- item.index = closestIndex;
460
- }
461
- }
462
-
463
- // Y축 거리를 확인하여 직접 히트 판정
464
- if (item.data) {
465
- const point = gdata[closestIndex];
466
- const yDist = Math.abs(yp - point.yp);
467
- const directHitThreshold = 15; // 직접 히트 임계값
468
-
469
- if (yDist <= directHitThreshold) {
470
- item.hit = true;
471
- }
472
- }
473
- }
474
- }
475
- }
476
-
477
- if (!useSelectLabelOrItem) {
478
- this.beforeMouseXp = xp;
479
- this.beforeMouseYp = yp;
480
-
481
- if (typeof item.index === 'number') {
482
- this.beforeFindItemIndex = item.index;
483
- }
484
- }
485
-
486
- if (isLinearInterpolation && item?.data?.o === null) {
487
- item.data = null;
488
- }
489
-
490
- return item;
491
- }
492
-
493
- /**
494
- * Find approximate graph item
495
- * @param {array} offset mouse position
496
- *
497
- * @returns {object} graph item
498
- */
499
- findApproximateData(offset) {
500
- const xp = offset[0];
501
- const yp = offset[1];
502
- const item = { data: null, hit: false, color: this.color };
503
- const gdata = this.data.filter(data => !Util.isNullOrUndefined(data.x));
504
-
505
- if (!gdata.length) {
506
- return item;
507
- }
508
-
509
- // 동적 감지 범위 계산
510
- const gap = gdata.length > 1 ? Math.abs(gdata[1]?.xp - gdata[0]?.xp) : 50;
511
- const xpInterval = Math.max(gap * 0.4, 10); // 데이터 간격의 40% 또는 최소 10px
512
-
513
- let s = 0;
514
- let e = gdata.length - 1;
515
- let closestIndex = -1;
516
- let closestDistance = Infinity;
517
-
518
- // 이진 탐색으로 근처 데이터 찾기
519
- while (s <= e) {
520
- const m = Math.floor((s + e) / 2);
521
- const x = gdata[m].xp;
522
-
523
- // X 좌표가 감지 범위 내에 있는 경우
524
- if ((x - xpInterval <= xp) && (xp <= x + xpInterval)) {
525
- // 중간점 주변 데이터들과 거리 비교
526
- const checkStart = Math.max(0, m - 2);
527
- const checkEnd = Math.min(gdata.length - 1, m + 2);
528
-
529
- for (let i = checkStart; i <= checkEnd; i++) {
530
- if (gdata[i].xp !== null && gdata[i].yp !== null) {
531
- const distance = Math.sqrt(
532
- ((xp - gdata[i].xp) ** 2)
533
- + ((yp - gdata[i].yp) ** 2),
534
- );
535
-
536
- if (distance < closestDistance) {
537
- closestDistance = distance;
538
- closestIndex = i;
539
- }
540
- }
541
- }
542
-
543
- if (closestIndex !== -1) {
544
- item.data = gdata[closestIndex];
545
- item.index = closestIndex;
546
-
547
- // 매우 가까운 경우 hit으로 표시
548
- if (closestDistance < 5) {
549
- item.hit = true;
550
- }
551
- }
552
-
553
- return item;
554
- } else if (x + xpInterval < xp) {
555
- // 마우스가 오른쪽에 있는 경우
556
- if (m < e && xp < gdata[m + 1].xp) {
557
- const curr = Math.abs(gdata[m].xp - xp);
558
- const next = Math.abs(gdata[m + 1].xp - xp);
559
-
560
- item.data = curr > next ? gdata[m + 1] : gdata[m];
561
- item.index = curr > next ? m + 1 : m;
562
-
563
- // Y 거리도 확인하여 hit 판정
564
- const selectedPoint = item.data;
565
- const yDist = Math.abs(yp - selectedPoint.yp);
566
- if (yDist < 10) {
567
- item.hit = true;
568
- }
569
-
570
- return item;
571
- }
572
- s = m + 1;
573
- } else {
574
- // 마우스가 왼쪽에 있는 경우
575
- if (m > 0 && xp > gdata[m - 1].xp) {
576
- const prev = Math.abs(gdata[m - 1].xp - xp);
577
- const curr = Math.abs(gdata[m].xp - xp);
578
-
579
- item.data = prev > curr ? gdata[m] : gdata[m - 1];
580
- item.index = prev > curr ? m : m - 1;
581
-
582
- // Y 거리도 확인하여 hit 판정
583
- const selectedPoint = item.data;
584
- const yDist = Math.abs(yp - selectedPoint.yp);
585
- if (yDist < 10) {
586
- item.hit = true;
587
- }
588
-
589
- return item;
590
- }
591
- e = m - 1;
592
- }
593
- }
594
-
595
- return item;
596
- }
597
-
598
- /**
599
- * Returns items in range
600
- * @param {object} params range values
601
- *
602
- * @returns {array}
603
- */
604
- findItems({ xsp, width }) {
605
- const xep = xsp + width;
606
-
607
- return this.data.filter(seriesData => (xsp - 1 <= seriesData.xp) && (seriesData.xp <= xep + 1));
608
- }
609
- }
610
-
611
- export default Line;